All of lore.kernel.org
 help / color / mirror / Atom feed
From: Jeff Layton <jlayton@kernel.org>
To: NeilBrown <neil@brown.name>,
	Alexander Viro <viro@zeniv.linux.org.uk>,
	 Christian Brauner <brauner@kernel.org>,
	Amir Goldstein <amir73il@gmail.com>
Cc: Jan Kara <jack@suse.cz>, linux-fsdevel@vger.kernel.org
Subject: Re: [PATCH 06/11] VFS: introduce start_removing_dentry()
Date: Thu, 02 Oct 2025 13:19:38 -0400	[thread overview]
Message-ID: <71d455e2d378a7500b37101cb8c16576aea90198.camel@kernel.org> (raw)
In-Reply-To: <20250926025015.1747294-7-neilb@ownmail.net>

On Fri, 2025-09-26 at 12:49 +1000, NeilBrown wrote:
> From: NeilBrown <neil@brown.name>
> 
> start_removing_dentry() is similar to start_removing() but instead of
> providing a name for lookup, the target dentry is given.
> 
> start_removing_dentry() checks that the dentry is still hashed and in
> the parent, and if so it locks and increases the refcount so that
> end_removing() can be used to finish the operation.
> 
> This is used in cachefiles, overlayfs, smb/server, and apparmor.
> 
> There will be other users including ecryptfs.
> 
> As start_removing_dentry() takes an extra reference to the dentry (to be
> put by end_removing()), there is no need to explicitly take an extra
> reference to stop d_delete() from using dentry_unlink_inode() to negate
> the dentry - as in cachefiles_delete_object(), and ksmbd_vfs_unlink().
> 
> Signed-off-by: NeilBrown <neil@brown.name>
> ---
>  fs/cachefiles/interface.c      | 14 +++++++++-----
>  fs/cachefiles/namei.c          | 22 ++++++++++++----------
>  fs/cachefiles/volume.c         | 10 +++++++---
>  fs/namei.c                     | 29 +++++++++++++++++++++++++++++
>  fs/overlayfs/dir.c             | 10 ++++------
>  fs/overlayfs/readdir.c         |  8 ++++----
>  fs/smb/server/vfs.c            | 27 ++++-----------------------
>  include/linux/namei.h          |  2 ++
>  security/apparmor/apparmorfs.c |  8 ++++----
>  9 files changed, 75 insertions(+), 55 deletions(-)
> 
> diff --git a/fs/cachefiles/interface.c b/fs/cachefiles/interface.c
> index 3e63cfe15874..3f8a6f1a8fc3 100644
> --- a/fs/cachefiles/interface.c
> +++ b/fs/cachefiles/interface.c
> @@ -9,6 +9,7 @@
>  #include <linux/mount.h>
>  #include <linux/xattr.h>
>  #include <linux/file.h>
> +#include <linux/namei.h>
>  #include <linux/falloc.h>
>  #include <trace/events/fscache.h>
>  #include "internal.h"
> @@ -428,11 +429,14 @@ static bool cachefiles_invalidate_cookie(struct fscache_cookie *cookie)
>  		if (!old_tmpfile) {
>  			struct cachefiles_volume *volume = object->volume;
>  			struct dentry *fan = volume->fanout[(u8)cookie->key_hash];
> -
> -			inode_lock_nested(d_inode(fan), I_MUTEX_PARENT);
> -			cachefiles_bury_object(volume->cache, object, fan,
> -					       old_file->f_path.dentry,
> -					       FSCACHE_OBJECT_INVALIDATED);
> +			struct dentry *obj;
> +
> +			obj = start_removing_dentry(fan, old_file->f_path.dentry);
> +			if (!IS_ERR(obj))
> +				cachefiles_bury_object(volume->cache, object,
> +						       fan, obj,
> +						       FSCACHE_OBJECT_INVALIDATED);
> +			end_removing(obj);
>  		}
>  		fput(old_file);
>  	}
> diff --git a/fs/cachefiles/namei.c b/fs/cachefiles/namei.c
> index 3064d439807b..80a3055d8ae5 100644
> --- a/fs/cachefiles/namei.c
> +++ b/fs/cachefiles/namei.c
> @@ -424,13 +424,12 @@ int cachefiles_delete_object(struct cachefiles_object *object,
>  
>  	_enter(",OBJ%x{%pD}", object->debug_id, object->file);
>  
> -	/* Stop the dentry being negated if it's only pinned by a file struct. */
> -	dget(dentry);
> -
> -	inode_lock_nested(d_backing_inode(fan), I_MUTEX_PARENT);
> -	ret = cachefiles_unlink(volume->cache, object, fan, dentry, why);
> -	inode_unlock(d_backing_inode(fan));
> -	dput(dentry);
> +	dentry = start_removing_dentry(fan, dentry);
> +	if (IS_ERR(dentry))
> +		ret = PTR_ERR(dentry);
> +	else
> +		ret = cachefiles_unlink(volume->cache, object, fan, dentry, why);
> +	end_removing(dentry);
>  	return ret;
>  }
>  
> @@ -643,9 +642,12 @@ bool cachefiles_look_up_object(struct cachefiles_object *object)
>  
>  	if (!d_is_reg(dentry)) {
>  		pr_err("%pd is not a file\n", dentry);
> -		inode_lock_nested(d_inode(fan), I_MUTEX_PARENT);
> -		ret = cachefiles_bury_object(volume->cache, object, fan, dentry,
> -					     FSCACHE_OBJECT_IS_WEIRD);
> +		struct dentry *de = start_removing_dentry(fan, dentry);
> +		if (!IS_ERR(de))
> +			ret = cachefiles_bury_object(volume->cache, object,
> +						     fan, de,
> +						     FSCACHE_OBJECT_IS_WEIRD);
> +		end_removing(de);
>  		dput(dentry);
>  		if (ret < 0)
>  			return false;
> diff --git a/fs/cachefiles/volume.c b/fs/cachefiles/volume.c
> index 781aac4ef274..ddf95ff5daf0 100644
> --- a/fs/cachefiles/volume.c
> +++ b/fs/cachefiles/volume.c
> @@ -7,6 +7,7 @@
>  
>  #include <linux/fs.h>
>  #include <linux/slab.h>
> +#include <linux/namei.h>
>  #include "internal.h"
>  #include <trace/events/fscache.h>
>  
> @@ -58,9 +59,12 @@ void cachefiles_acquire_volume(struct fscache_volume *vcookie)
>  		if (ret < 0) {
>  			if (ret != -ESTALE)
>  				goto error_dir;
> -			inode_lock_nested(d_inode(cache->store), I_MUTEX_PARENT);
> -			cachefiles_bury_object(cache, NULL, cache->store, vdentry,
> -					       FSCACHE_VOLUME_IS_WEIRD);
> +			vdentry = start_removing_dentry(cache->store, vdentry);
> +			if (!IS_ERR(vdentry))
> +				cachefiles_bury_object(cache, NULL, cache->store,
> +						       vdentry,
> +						       FSCACHE_VOLUME_IS_WEIRD);
> +			end_removing(vdentry);
>  			cachefiles_put_directory(volume->dentry);
>  			cond_resched();
>  			goto retry;
> diff --git a/fs/namei.c b/fs/namei.c
> index bd5c45801756..cb4d40af12ae 100644
> --- a/fs/namei.c
> +++ b/fs/namei.c
> @@ -3344,6 +3344,35 @@ struct dentry *start_removing_noperm(struct dentry *parent,
>  }
>  EXPORT_SYMBOL(start_removing_noperm);
>  
> +/**
> + * start_removing_dentry - prepare to remove a given dentry
> + * @parent - directory from which dentry should be removed
> + * @child - the dentry to be removed
> + *
> + * A lock is taken to protect the dentry again other dirops and
> + * the validity of the dentry is checked: correct parent and still hashed.
> + *
> + * If the dentry is valid a reference is taken and returned.  If not
> + * an error is returned.
> + *
> + * end_removing() should be called when removal is complete, or aborted.
> + *
> + * Returns: the valid dentry, or an error.
> + */
> +struct dentry *start_removing_dentry(struct dentry *parent,
> +				     struct dentry *child)
> +{
> +	inode_lock_nested(parent->d_inode, I_MUTEX_PARENT);
> +	if (unlikely(IS_DEADDIR(parent->d_inode) ||
> +		     child->d_parent != parent ||
> +		     d_unhashed(child))) {
> +		inode_unlock(parent->d_inode);
> +		return ERR_PTR(-EINVAL);
> +	}
> +	return dget(child);
> +}
> +EXPORT_SYMBOL(start_removing_dentry);
> +
>  #ifdef CONFIG_UNIX98_PTYS
>  int path_pts(struct path *path)
>  {
> diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c
> index c4057b4a050d..74b1ef5860a4 100644
> --- a/fs/overlayfs/dir.c
> +++ b/fs/overlayfs/dir.c
> @@ -47,14 +47,12 @@ static int ovl_cleanup_locked(struct ovl_fs *ofs, struct inode *wdir,
>  int ovl_cleanup(struct ovl_fs *ofs, struct dentry *workdir,
>  		struct dentry *wdentry)
>  {
> -	int err;
> -
> -	err = ovl_parent_lock(workdir, wdentry);
> -	if (err)
> -		return err;
> +	wdentry = start_removing_dentry(workdir, wdentry);
> +	if (IS_ERR(wdentry))
> +		return PTR_ERR(wdentry);
>  
>  	ovl_cleanup_locked(ofs, workdir->d_inode, wdentry);
> -	ovl_parent_unlock(workdir);
> +	end_removing(wdentry);
>  
>  	return 0;
>  }
> diff --git a/fs/overlayfs/readdir.c b/fs/overlayfs/readdir.c
> index 15cb06fa0c9a..213ff42556e7 100644
> --- a/fs/overlayfs/readdir.c
> +++ b/fs/overlayfs/readdir.c
> @@ -1158,11 +1158,11 @@ int ovl_workdir_cleanup(struct ovl_fs *ofs, struct dentry *parent,
>  	if (!d_is_dir(dentry) || level > 1)
>  		return ovl_cleanup(ofs, parent, dentry);
>  
> -	err = ovl_parent_lock(parent, dentry);
> -	if (err)
> -		return err;
> +	dentry = start_removing_dentry(parent, dentry);
> +	if (IS_ERR(dentry))
> +		return PTR_ERR(dentry);
>  	err = ovl_do_rmdir(ofs, parent->d_inode, dentry);
> -	ovl_parent_unlock(parent);
> +	end_removing(dentry);
>  	if (err) {
>  		struct path path = { .mnt = mnt, .dentry = dentry };
>  
> diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c
> index 1cfa688904b2..56b755a05c4e 100644
> --- a/fs/smb/server/vfs.c
> +++ b/fs/smb/server/vfs.c
> @@ -48,24 +48,6 @@ static void ksmbd_vfs_inherit_owner(struct ksmbd_work *work,
>  	i_uid_write(inode, i_uid_read(parent_inode));
>  }
>  
> -/**
> - * ksmbd_vfs_lock_parent() - lock parent dentry if it is stable
> - * @parent: parent dentry
> - * @child: child dentry
> - *
> - * Returns: %0 on success, %-ENOENT if the parent dentry is not stable
> - */
> -int ksmbd_vfs_lock_parent(struct dentry *parent, struct dentry *child)
> -{
> -	inode_lock_nested(d_inode(parent), I_MUTEX_PARENT);
> -	if (child->d_parent != parent) {
> -		inode_unlock(d_inode(parent));
> -		return -ENOENT;
> -	}
> -
> -	return 0;
> -}
> -
>  static int ksmbd_vfs_path_lookup(struct ksmbd_share_config *share_conf,
>  				 char *pathname, unsigned int flags,
>  				 struct path *path, bool do_lock)
> @@ -1083,18 +1065,17 @@ int ksmbd_vfs_unlink(struct file *filp)
>  		return err;
>  
>  	dir = dget_parent(dentry);
> -	err = ksmbd_vfs_lock_parent(dir, dentry);
> -	if (err)
> +	dentry = start_removing_dentry(dir, dentry);
> +	err = PTR_ERR(dentry);
> +	if (IS_ERR(dentry))
>  		goto out;
> -	dget(dentry);
>  
>  	if (S_ISDIR(d_inode(dentry)->i_mode))
>  		err = vfs_rmdir(idmap, d_inode(dir), dentry);
>  	else
>  		err = vfs_unlink(idmap, d_inode(dir), dentry, NULL);
>  
> -	dput(dentry);
> -	inode_unlock(d_inode(dir));
> +	end_removing(dentry);
>  	if (err)
>  		ksmbd_debug(VFS, "failed to delete, err %d\n", err);
>  out:
> diff --git a/include/linux/namei.h b/include/linux/namei.h
> index 20a88a46fe92..32a007f1043e 100644
> --- a/include/linux/namei.h
> +++ b/include/linux/namei.h
> @@ -94,6 +94,8 @@ struct dentry *start_removing(struct mnt_idmap *idmap, struct dentry *parent,
>  			      struct qstr *name);
>  struct dentry *start_creating_noperm(struct dentry *parent, struct qstr *name);
>  struct dentry *start_removing_noperm(struct dentry *parent, struct qstr *name);
> +struct dentry *start_removing_dentry(struct dentry *parent,
> +				     struct dentry *child);
>  
>  /* end_creating - finish action started with start_creating
>   * @child - dentry returned by start_creating()
> diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
> index 391a586d0557..9d08d103f142 100644
> --- a/security/apparmor/apparmorfs.c
> +++ b/security/apparmor/apparmorfs.c
> @@ -355,17 +355,17 @@ static void aafs_remove(struct dentry *dentry)
>  	if (!dentry || IS_ERR(dentry))
>  		return;
>  
> +	/* ->d_parent is stable as rename is not supported */
>  	dir = d_inode(dentry->d_parent);
> -	inode_lock(dir);
> -	if (simple_positive(dentry)) {
> +	dentry = start_removing_dentry(dentry->d_parent, dentry);
> +	if (!IS_ERR(dentry) && simple_positive(dentry)) {
>  		if (d_is_dir(dentry))
>  			simple_rmdir(dir, dentry);
>  		else
>  			simple_unlink(dir, dentry);
>  		d_delete(dentry);
> -		dput(dentry);
>  	}
> -	inode_unlock(dir);
> +	end_removing(dentry);
>  	simple_release_fs(&aafs_mnt, &aafs_count);
>  }
>  

Reviewed-by: Jeff Layton <jlayton@kernel.org>

  parent reply	other threads:[~2025-10-02 17:19 UTC|newest]

Thread overview: 49+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-09-26  2:49 [PATCH 00/11] Create APIs to centralise locking for directory ops NeilBrown
2025-09-26  2:49 ` [PATCH 01/11] debugfs: rename end_creating() to debugfs_end_creating() NeilBrown
2025-09-27  9:13   ` Amir Goldstein
2025-09-27 11:29   ` Jeff Layton
2025-09-26  2:49 ` [PATCH 02/11] VFS: introduce start_dirop() and end_dirop() NeilBrown
2025-09-26 16:41   ` Amir Goldstein
2025-09-27 11:32     ` NeilBrown
2025-09-26  2:49 ` [PATCH 03/11] VFS/nfsd/cachefiles/ovl: add start_creating() and end_creating() NeilBrown
2025-09-29 12:37   ` Jeff Layton
2025-09-30  5:37     ` NeilBrown
2025-09-30 10:19       ` Jeff Layton
2025-09-30  8:54   ` Amir Goldstein
2025-10-01  3:15     ` NeilBrown
2025-10-02 10:52       ` Amir Goldstein
2025-09-26  2:49 ` [PATCH 04/11] VFS/nfsd/cachefiles/ovl: introduce start_removing() and end_removing() NeilBrown
2025-09-27  9:12   ` Amir Goldstein
2025-10-02 17:02   ` Jeff Layton
2025-09-26  2:49 ` [PATCH 05/11] VFS: introduce start_creating_noperm() and start_removing_noperm() NeilBrown
2025-09-28 12:26   ` Amir Goldstein
2025-10-02 17:13   ` Jeff Layton
2025-09-26  2:49 ` [PATCH 06/11] VFS: introduce start_removing_dentry() NeilBrown
2025-09-27  9:32   ` Amir Goldstein
2025-09-27 11:55     ` NeilBrown
2025-10-02 17:19   ` Jeff Layton [this message]
2025-09-26  2:49 ` [PATCH 07/11] VFS: add start_creating_killable() and start_removing_killable() NeilBrown
2025-09-28 12:05   ` Amir Goldstein
2025-09-29  1:44     ` NeilBrown
2025-09-26  2:49 ` [PATCH 08/11] VFS/nfsd/ovl: introduce start_renaming() and end_renaming() NeilBrown
2025-09-29 11:23   ` Amir Goldstein
2025-09-26  2:49 ` [PATCH 09/11] VFS/ovl/smb: introduce start_renaming_dentry() NeilBrown
2025-09-26 15:43   ` kernel test robot
2025-09-26 17:17   ` kernel test robot
2025-09-30  7:08   ` Amir Goldstein
2025-10-01  1:45     ` NeilBrown
2025-10-02 10:56       ` Amir Goldstein
2025-10-01  4:35     ` NeilBrown
2025-09-26  2:49 ` [PATCH 10/11] Add start_renaming_two_dentrys() NeilBrown
2025-09-30  7:46   ` Amir Goldstein
2025-10-01  4:14     ` NeilBrown
2025-09-26  2:49 ` [PATCH 11/11] ecryptfs: use new start_creaing/start_removing APIs NeilBrown
2025-09-26 16:03   ` kernel test robot
2025-09-28 12:50   ` Amir Goldstein
2025-09-29  5:26     ` NeilBrown
2025-09-29  7:53       ` Amir Goldstein
2025-10-01  1:31         ` NeilBrown
2025-10-02 10:25           ` Amir Goldstein
2025-09-26 15:47 ` [PATCH 00/11] Create APIs to centralise locking for directory ops Amir Goldstein
2025-09-27 11:20   ` NeilBrown
2025-10-01  5:04     ` NeilBrown

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=71d455e2d378a7500b37101cb8c16576aea90198.camel@kernel.org \
    --to=jlayton@kernel.org \
    --cc=amir73il@gmail.com \
    --cc=brauner@kernel.org \
    --cc=jack@suse.cz \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=neil@brown.name \
    --cc=viro@zeniv.linux.org.uk \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.