diff mbox series

[RFC,03/24] vfs: add try_break_deleg calls for parents to vfs_{link,rename,unlink}

Message ID 20240315-dir-deleg-v1-3-a1d6209a3654@kernel.org (mailing list archive)
State RFC
Headers show
Series vfs, nfsd, nfs: implement directory delegations | expand

Checks

Context Check Description
netdev/tree_selection success Not a local patch

Commit Message

Jeff Layton March 15, 2024, 4:52 p.m. UTC
In order to add directory delegation support, we need to break
delegations on the parent whenever there is going to be a change in the
directory.

vfs_link, vfs_unlink, and vfs_rename all have existing delegation break
handling for the children in the rename. Add the necessary calls for
breaking delegations in the parent(s) as well.

Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
 fs/namei.c | 20 +++++++++++++++++---
 1 file changed, 17 insertions(+), 3 deletions(-)

Comments

Al Viro March 16, 2024, 11:57 p.m. UTC | #1
On Fri, Mar 15, 2024 at 12:52:54PM -0400, Jeff Layton wrote:
> @@ -4603,9 +4606,12 @@ int vfs_link(struct dentry *old_dentry, struct mnt_idmap *idmap,
>  	else if (max_links && inode->i_nlink >= max_links)
>  		error = -EMLINK;
>  	else {
> -		error = try_break_deleg(inode, delegated_inode);
> -		if (!error)
> -			error = dir->i_op->link(old_dentry, dir, new_dentry);
> +		error = try_break_deleg(dir, delegated_inode);
> +		if (!error) {
> +			error = try_break_deleg(inode, delegated_inode);
> +			if (!error)
> +				error = dir->i_op->link(old_dentry, dir, new_dentry);
> +		}

A minor nit: that might be easier to follow as
		error = try_break_deleg(dir, delegated_inode);
		if (!error)
			error = try_break_deleg(inode, delegated_inode);
		if (!error)
			error = dir->i_op->link(old_dentry, dir, new_dentry);

and let the compiler deal with optimizing it - any C compiler is going to be
able to figure out that one out.  vfs_link() is a mix of those styles anyway -
we have
        if (!error && (inode->i_state & I_LINKABLE)) {
                spin_lock(&inode->i_lock);
                inode->i_state &= ~I_LINKABLE;
                spin_unlock(&inode->i_lock);
        }
immediately afterwards; might as well make that consistent, especially since
you are getting more shallow nesting that way.
Jeff Layton March 17, 2024, 2:09 p.m. UTC | #2
On Sat, 2024-03-16 at 23:57 +0000, Al Viro wrote:
> On Fri, Mar 15, 2024 at 12:52:54PM -0400, Jeff Layton wrote:
> > @@ -4603,9 +4606,12 @@ int vfs_link(struct dentry *old_dentry, struct mnt_idmap *idmap,
> >  	else if (max_links && inode->i_nlink >= max_links)
> >  		error = -EMLINK;
> >  	else {
> > -		error = try_break_deleg(inode, delegated_inode);
> > -		if (!error)
> > -			error = dir->i_op->link(old_dentry, dir, new_dentry);
> > +		error = try_break_deleg(dir, delegated_inode);
> > +		if (!error) {
> > +			error = try_break_deleg(inode, delegated_inode);
> > +			if (!error)
> > +				error = dir->i_op->link(old_dentry, dir, new_dentry);
> > +		}
> 
> A minor nit: that might be easier to follow as
> 		error = try_break_deleg(dir, delegated_inode);
> 		if (!error)
> 			error = try_break_deleg(inode, delegated_inode);
> 		if (!error)
> 			error = dir->i_op->link(old_dentry, dir, new_dentry);
> 
> and let the compiler deal with optimizing it - any C compiler is going to be
> able to figure out that one out.  vfs_link() is a mix of those styles anyway -
> we have
>         if (!error && (inode->i_state & I_LINKABLE)) {
>                 spin_lock(&inode->i_lock);
>                 inode->i_state &= ~I_LINKABLE;
>                 spin_unlock(&inode->i_lock);
>         }
> immediately afterwards; might as well make that consistent, especially since
> you are getting more shallow nesting that way.

Sounds good. Fixed in my tree.

Thanks for the review so far!
diff mbox series

Patch

diff --git a/fs/namei.c b/fs/namei.c
index 9342fa6a38c2..56d3ebed7bac 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -4332,6 +4332,9 @@  int vfs_unlink(struct mnt_idmap *idmap, struct inode *dir,
 	else {
 		error = security_inode_unlink(dir, dentry);
 		if (!error) {
+			error = try_break_deleg(dir, delegated_inode);
+			if (error)
+				goto out;
 			error = try_break_deleg(target, delegated_inode);
 			if (error)
 				goto out;
@@ -4603,9 +4606,12 @@  int vfs_link(struct dentry *old_dentry, struct mnt_idmap *idmap,
 	else if (max_links && inode->i_nlink >= max_links)
 		error = -EMLINK;
 	else {
-		error = try_break_deleg(inode, delegated_inode);
-		if (!error)
-			error = dir->i_op->link(old_dentry, dir, new_dentry);
+		error = try_break_deleg(dir, delegated_inode);
+		if (!error) {
+			error = try_break_deleg(inode, delegated_inode);
+			if (!error)
+				error = dir->i_op->link(old_dentry, dir, new_dentry);
+		}
 	}
 
 	if (!error && (inode->i_state & I_LINKABLE)) {
@@ -4870,6 +4876,14 @@  int vfs_rename(struct renamedata *rd)
 		    old_dir->i_nlink >= max_links)
 			goto out;
 	}
+	error = try_break_deleg(old_dir, delegated_inode);
+	if (error)
+		goto out;
+	if (new_dir != old_dir) {
+		error = try_break_deleg(new_dir, delegated_inode);
+		if (error)
+			goto out;
+	}
 	if (!is_dir) {
 		error = try_break_deleg(source, delegated_inode);
 		if (error)