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

Jeff Layton posted 24 patches 1 year, 11 months ago
There is a newer version of this series
[PATCH RFC 03/24] vfs: add try_break_deleg calls for parents to vfs_{link,rename,unlink}
Posted by Jeff Layton 1 year, 11 months ago
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(-)

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)

-- 
2.44.0
Re: [PATCH RFC 03/24] vfs: add try_break_deleg calls for parents to vfs_{link,rename,unlink}
Posted by Al Viro 1 year, 10 months ago
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.
Re: [PATCH RFC 03/24] vfs: add try_break_deleg calls for parents to vfs_{link,rename,unlink}
Posted by Jeff Layton 1 year, 10 months ago
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!
-- 
Jeff Layton <jlayton@kernel.org>