Unverified Commit 7bb1eb45 authored by NeilBrown's avatar NeilBrown Committed by Christian Brauner
Browse files

VFS: introduce start_removing_dentry()



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().

cachefiles_bury_object() now gets an extra ref to the victim, which is
drops.  As it includes the needed end_removing() calls, the caller
doesn't need them.

Reviewed-by: default avatarAmir Goldstein <amir73il@gmail.com>
Reviewed-by: default avatarNamjae Jeon <linkinjeon@kernel.org>
Reviewed-by: default avatarJeff Layton <jlayton@kernel.org>
Signed-off-by: default avatarNeilBrown <neil@brown.name>
Link: https://patch.msgid.link/20251113002050.676694-9-neilb@ownmail.net


Signed-off-by: default avatarChristian Brauner <brauner@kernel.org>
parent 1ead2213
Loading
Loading
Loading
Loading
+7 −4
Original line number Diff line number Diff line
@@ -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,10 +429,12 @@ 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];
			struct dentry *obj;

			inode_lock_nested(d_inode(fan), I_MUTEX_PARENT);
			cachefiles_bury_object(volume->cache, object, fan,
					       old_file->f_path.dentry,
			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);
		}
		fput(old_file);
+14 −16
Original line number Diff line number Diff line
@@ -261,6 +261,7 @@ static int cachefiles_unlink(struct cachefiles_cache *cache,
 * - Directory backed objects are stuffed into the graveyard for userspace to
 *   delete
 * On entry dir must be locked.  It will be unlocked on exit.
 * On entry there must be at least 2 refs on rep, one will be dropped on exit.
 */
int cachefiles_bury_object(struct cachefiles_cache *cache,
			   struct cachefiles_object *object,
@@ -275,12 +276,6 @@ int cachefiles_bury_object(struct cachefiles_cache *cache,

	_enter(",'%pd','%pd'", dir, rep);

	/* end_removing() will dput() @rep but we need to keep
	 * a ref, so take one now.  This also stops the dentry
	 * being negated when unlinked which we need.
	 */
	dget(rep);

	if (rep->d_parent != dir) {
		end_removing(rep);
		_leave(" = -ESTALE");
@@ -425,13 +420,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);
	dentry = start_removing_dentry(fan, dentry);
	if (IS_ERR(dentry))
		ret = PTR_ERR(dentry);
	else
		ret = cachefiles_unlink(volume->cache, object, fan, dentry, why);
	inode_unlock(d_backing_inode(fan));
	dput(dentry);
	end_removing(dentry);
	return ret;
}

@@ -644,8 +638,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,
		struct dentry *de = start_removing_dentry(fan, dentry);
		if (IS_ERR(de))
			ret = PTR_ERR(de);
		else
			ret = cachefiles_bury_object(volume->cache, object,
						     fan, de,
						     FSCACHE_OBJECT_IS_WEIRD);
		dput(dentry);
		if (ret < 0)
+6 −3
Original line number Diff line number Diff line
@@ -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,8 +59,10 @@ 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,
			vdentry = start_removing_dentry(cache->store, vdentry);
			if (!IS_ERR(vdentry))
				cachefiles_bury_object(cache, NULL, cache->store,
						       vdentry,
						       FSCACHE_VOLUME_IS_WEIRD);
			cachefiles_put_directory(volume->dentry);
			cond_resched();
+33 −0
Original line number Diff line number Diff line
@@ -3323,6 +3323,39 @@ 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 and positive, 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);
	}
	if (d_is_negative(child)) {
		inode_unlock(parent->d_inode);
		return ERR_PTR(-ENOENT);
	}
	return dget(child);
}
EXPORT_SYMBOL(start_removing_dentry);

#ifdef CONFIG_UNIX98_PTYS
int path_pts(struct path *path)
{
+4 −6
Original line number Diff line number Diff line
@@ -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;
}
Loading