All of lore.kernel.org
 help / color / mirror / Atom feed
From: NeilBrown <neilb@suse.de>
To: Al Viro <viro@zeniv.linux.org.uk>,
	Linus Torvalds <torvalds@linux-foundation.org>,
	Daire Byrne <daire@dneg.com>,
	Trond Myklebust <trond.myklebust@hammerspace.com>,
	Chuck Lever <chuck.lever@oracle.com>
Cc: Linux NFS Mailing List <linux-nfs@vger.kernel.org>,
	linux-fsdevel@vger.kernel.org,
	LKML <linux-kernel@vger.kernel.org>
Subject: [PATCH 09/10] VFS: add LOOKUP_SILLY_RENAME
Date: Fri, 26 Aug 2022 12:10:43 +1000	[thread overview]
Message-ID: <166147984377.25420.5747334898411663007.stgit@noble.brown> (raw)
In-Reply-To: <166147828344.25420.13834885828450967910.stgit@noble.brown>

When performing a "silly rename" to avoid removing a file that is still
open, we need to perform a lookup in a directory that is already locked.

In order to allow common functions to be used for this lookup, introduce
LOOKUP_SILLY_RENAME which affirms that the directory is already locked
and that the vfsmnt is already writable.

When LOOKUP_SILLY_RENAME is set, path->mnt can be NULL.  As
i_op->rename() doesn't make the vfsmnt available, this is unavoidable.
So we ensure that a NULL ->mnt isn't fatal.

Signed-off-by: NeilBrown <neilb@suse.de>
---
 fs/dcache.c           |    3 +-
 fs/namei.c            |   88 +++++++++++++++++++++++++++++--------------------
 include/linux/namei.h |    9 +++--
 3 files changed, 59 insertions(+), 41 deletions(-)

diff --git a/fs/dcache.c b/fs/dcache.c
index d6bfa49b143b..9bf346a9de52 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -3297,7 +3297,8 @@ EXPORT_SYMBOL(d_tmpfile);
  * If the parent directory is locked with I_MUTEX_NORMAL, use I_MUTEX_NORMAL.
  * If the parent is locked with I_MUTEX_PARENT, I_MUTEX_PARENT2 or
  * I_MUTEX_CHILD, use I_MUTEX_PARENT or, for the second in a rename,
- * I_MUTEX_PARENT2.
+ * I_MUTEX_PARENT2.  When a third name is needed, as with "silly-rename"
+ * I_MUTEX_CHILD is used.
  */
 bool d_lock_update_nested(struct dentry *dentry,
 			  struct dentry *base, const struct qstr *name,
diff --git a/fs/namei.c b/fs/namei.c
index ef994239fa7c..c9bbff120bf9 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -1650,12 +1650,14 @@ static struct dentry *__lookup_hash(const struct qstr *name,
 }
 
 /*
- * Parent directory (base) is not locked.  We take either an exclusive
- * or shared lock depending on the fs preference, then do a lookup,
- * and then set the DCACHE_PAR_UPDATE bit on the child if a shared lock
- * was taken on the parent.
+ * Without LOOKUP_SILLY_RENAME parent directory (base) is not locked.
+ * We take either an exclusive or shared lock depending on the fs
+ * preference, then do a lookup, and then set the DCACHE_PAR_UPDATE bit
+ * on the child if a shared lock was taken on the parent.
  * If LOOKUP_EXCL, name should not already exist, else -EEXIST
  * If not LOOKUP_CREATE, name should already exist, else -ENOENT
+ * If LOOKUP_SILLY_RENAME, don't require write access.  In this case
+ * path->mnt may be NULL.
  */
 static struct dentry *lookup_hash_update(
 	const struct qstr *name,
@@ -1665,21 +1667,26 @@ static struct dentry *lookup_hash_update(
 	struct dentry *dentry;
 	struct dentry *base = path->dentry;
 	struct inode *dir = base->d_inode;
-	int err, err2;
-
-	/* For create, don't fail immediately if it's r/o,
-	 * at least try to report other errors.
-	 * For unlink/rmdir where LOOKUP_REVAl is the only
-	 * flag, fail immediately if r/o.
-	 */
-	err2 = mnt_want_write(path->mnt);
-	if (err2 && (flags & ~LOOKUP_REVAL) == 0)
-		return ERR_PTR(err2);
+	int err, err2 = 0;
+	int class;
+
+	if (!(flags & LOOKUP_SILLY_RENAME)) {
+		/* For create, don't fail immediately if it's r/o,
+		 * at least try to report other errors.
+		 * For unlink/rmdir where LOOKUP_REVAl is the only
+		 * flag, fail immediately if r/o.
+		 */
+		err2 = mnt_want_write(path->mnt);
+		if (err2 && (flags & ~LOOKUP_REVAL) == 0)
+			return ERR_PTR(err2);
 
-	if (wq && IS_PAR_UPDATE(dir))
-		inode_lock_shared_nested(dir, I_MUTEX_PARENT);
-	else
-		inode_lock_nested(dir, I_MUTEX_PARENT);
+		if (wq && IS_PAR_UPDATE(dir))
+			inode_lock_shared_nested(dir, I_MUTEX_PARENT);
+		else
+			inode_lock_nested(dir, I_MUTEX_PARENT);
+		class = I_MUTEX_PARENT;
+	} else
+		class = I_MUTEX_CHILD;
 
 retry:
 	dentry = __lookup_hash(name, base, flags, wq);
@@ -1687,7 +1694,7 @@ static struct dentry *lookup_hash_update(
 		err = PTR_ERR(dentry);
 		goto out_err;
 	}
-	if (!d_lock_update_nested(dentry, base, name, I_MUTEX_PARENT)) {
+	if (!d_lock_update_nested(dentry, base, name, class)) {
 		/*
 		 * Failed to get lock due to race with unlink or rename
 		 * - try again
@@ -1724,12 +1731,14 @@ static struct dentry *lookup_hash_update(
 	return dentry;
 
 out_err:
-	if (wq && IS_PAR_UPDATE(dir))
-		inode_unlock_shared(dir);
-	else
-		inode_unlock(dir);
-	if (!err2)
-		mnt_drop_write(path->mnt);
+	if (!(flags & LOOKUP_SILLY_RENAME)) {
+		if (wq && IS_PAR_UPDATE(dir))
+			inode_unlock_shared(dir);
+		else
+			inode_unlock(dir);
+		if (!err2)
+			mnt_drop_write(path->mnt);
+	}
 	return ERR_PTR(err);
 }
 
@@ -1751,18 +1760,22 @@ struct dentry *lookup_hash_update_len(const char *name, int nlen,
 EXPORT_SYMBOL(lookup_hash_update_len);
 
 void __done_path_update(struct path *path, struct dentry *dentry,
-			bool with_wq)
+			bool with_wq, unsigned int flags)
 {
 	struct inode *dir = path->dentry->d_inode;
 
 	d_lookup_done(dentry);
 	d_unlock_update(dentry);
-	if (IS_PAR_UPDATE(dir) && with_wq)
-		inode_unlock_shared(dir);
-	else
-		inode_unlock(dir);
-	dput(dentry);
-	mnt_drop_write(path->mnt);
+	if (flags & LOOKUP_SILLY_RENAME) {
+		dput(dentry);
+	} else {
+		if (IS_PAR_UPDATE(dir) && with_wq)
+			inode_unlock_shared(dir);
+		else
+			inode_unlock(dir);
+		dput(dentry);
+		mnt_drop_write(path->mnt);
+	}
 }
 EXPORT_SYMBOL(__done_path_update);
 
@@ -4122,7 +4135,8 @@ static struct dentry *filename_create_one(struct qstr *last, struct path *path,
 					  wait_queue_head_t *wq)
 {
 	bool want_dir = lookup_flags & LOOKUP_DIRECTORY;
-	unsigned int reval_flag = lookup_flags & LOOKUP_REVAL;
+	unsigned int flags = lookup_flags & (LOOKUP_REVAL |
+					     LOOKUP_SILLY_RENAME);
 	unsigned int create_flag = LOOKUP_CREATE;
 
 	/*
@@ -4136,7 +4150,7 @@ static struct dentry *filename_create_one(struct qstr *last, struct path *path,
 		 */
 		create_flag = 0;
 	return lookup_hash_update(last, path,
-				  reval_flag | create_flag | LOOKUP_EXCL,
+				  flags | create_flag | LOOKUP_EXCL,
 				  wq);
 }
 
@@ -4146,10 +4160,12 @@ struct dentry *filename_create_one_len(const char *name, int nlen,
 				       wait_queue_head_t *wq)
 {
 	struct qstr this;
+	struct user_namespace *uns = &init_user_ns;
 	int err;
 
-	err = lookup_one_common(mnt_user_ns(path->mnt), name,
-				path->dentry, nlen, &this);
+	if (path->mnt)
+		uns = mnt_user_ns(path->mnt);
+	err = lookup_one_common(uns, name, path->dentry, nlen, &this);
 	if (err)
 		return ERR_PTR(err);
 	return filename_create_one(&this, path, lookup_flags, wq);
diff --git a/include/linux/namei.h b/include/linux/namei.h
index 29756921f69b..92a62b04a83d 100644
--- a/include/linux/namei.h
+++ b/include/linux/namei.h
@@ -21,6 +21,7 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT};
 #define LOOKUP_FOLLOW		0x0001	/* follow links at the end */
 #define LOOKUP_DIRECTORY	0x0002	/* require a directory */
 #define LOOKUP_AUTOMOUNT	0x0004  /* force terminal automount */
+#define LOOKUP_SILLY_RENAME	0x0008	/* Directory already locked, don't lock again */
 #define LOOKUP_EMPTY		0x4000	/* accept empty path [user_... only] */
 #define LOOKUP_DOWN		0x8000	/* follow mounts in the starting point */
 #define LOOKUP_MOUNTPOINT	0x0080	/* follow mounts in the end */
@@ -64,20 +65,20 @@ extern struct dentry *user_path_create(int, const char __user *, struct path *,
 extern struct dentry *lookup_hash_update_len(const char *name, int nlen,
 					     struct path *path, unsigned int flags,
 					     wait_queue_head_t *wq);
-extern void __done_path_update(struct path *, struct dentry *, bool);
+extern void __done_path_update(struct path *, struct dentry *, bool, unsigned int);
 static inline void done_path_update(struct path *path, struct dentry *dentry)
 {
-	__done_path_update(path, dentry, true);
+	__done_path_update(path, dentry, true, 0);
 }
 static inline void done_path_create(struct path *path, struct dentry *dentry)
 {
-	__done_path_update(path, dentry, false);
+	__done_path_update(path, dentry, false, 0);
 	path_put(path);
 }
 static inline void done_path_create_wq(struct path *path, struct dentry *dentry,
 				       bool with_wq)
 {
-	__done_path_update(path, dentry, with_wq);
+	__done_path_update(path, dentry, with_wq, 0);
 	path_put(path);
 }
 



  reply	other threads:[~2022-08-26  2:19 UTC|newest]

Thread overview: 37+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-08-26  2:10 [PATCH/RFC 00/10 v5] Improve scalability of directory operations NeilBrown
2022-08-26  2:10 ` NeilBrown [this message]
2022-08-27  1:21   ` [PATCH 09/10] VFS: add LOOKUP_SILLY_RENAME Al Viro
2022-08-29  3:15     ` NeilBrown
2022-08-26  2:10 ` [PATCH 01/10] VFS: support parallel updates in the one directory NeilBrown
2022-08-26 19:06   ` Linus Torvalds
2022-08-26 23:06     ` NeilBrown
2022-08-27  0:13       ` Linus Torvalds
2022-08-27  0:23         ` Al Viro
2022-08-27 21:14         ` Al Viro
2022-08-27  0:17     ` Al Viro
2022-09-01  0:31       ` NeilBrown
2022-09-01  3:44         ` Al Viro
2022-08-27  3:43   ` Al Viro
2022-08-29  1:59     ` NeilBrown
2022-09-03  0:06       ` Al Viro
2022-09-03  1:40         ` NeilBrown
2022-09-03  2:12           ` Al Viro
2022-09-03 17:52             ` Al Viro
2022-09-04 23:33               ` NeilBrown
2022-08-26  2:10 ` [PATCH 08/10] NFSD: allow parallel creates from nfsd NeilBrown
2022-08-27  4:37   ` Al Viro
2022-08-29  3:12     ` NeilBrown
2022-08-26  2:10 ` [PATCH 05/10] VFS: export done_path_update() NeilBrown
2022-08-26  2:10 ` [PATCH 02/10] VFS: move EEXIST and ENOENT tests into lookup_hash_update() NeilBrown
2022-08-26  2:10 ` [PATCH 06/10] VFS: support concurrent renames NeilBrown
2022-08-27  4:12   ` Al Viro
2022-08-29  3:08     ` NeilBrown
2022-08-26  2:10 ` [PATCH 10/10] NFS: support parallel updates in the one directory NeilBrown
2022-08-26 15:31   ` John Stoffel
2022-08-26 23:13     ` NeilBrown
2022-08-26  2:10 ` [PATCH 03/10] VFS: move want_write checks into lookup_hash_update() NeilBrown
2022-08-27  3:48   ` Al Viro
2022-08-26  2:10 ` [PATCH 04/10] VFS: move dput() and mnt_drop_write() into done_path_update() NeilBrown
2022-08-26  2:10 ` [PATCH 07/10] VFS: hold DCACHE_PAR_UPDATE lock across d_revalidate() NeilBrown
2022-08-26 14:42 ` [PATCH/RFC 00/10 v5] Improve scalability of directory operations John Stoffel
2022-08-26 23:30   ` 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=166147984377.25420.5747334898411663007.stgit@noble.brown \
    --to=neilb@suse.de \
    --cc=chuck.lever@oracle.com \
    --cc=daire@dneg.com \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-nfs@vger.kernel.org \
    --cc=torvalds@linux-foundation.org \
    --cc=trond.myklebust@hammerspace.com \
    --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.