diff mbox

[NOMERGE,02/13] vfs: Propagate LOOKUP_CASEFOLD after doing parent lookups

Message ID 20180522203818.14666-3-krisman@collabora.co.uk (mailing list archive)
State New, archived
Headers show

Commit Message

Gabriel Krisman Bertazi May 22, 2018, 8:38 p.m. UTC
filename_parentat() is executed in order to quickly access a child
dentry for creation, unlinking or rename, outside the path_walk path.
Although, if the parent was accessed via a CI mount, we need to make
sure the child will also be searched in a CI manner, so the nameidata
flag from the first search needs to be propageted to the following child
__lookup_hash call.

Signed-off-by: Gabriel Krisman Bertazi <krisman@collabora.co.uk>
---
 fs/namei.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 54 insertions(+), 7 deletions(-)
diff mbox

Patch

diff --git a/fs/namei.c b/fs/namei.c
index ddd5c9e9ab3c..135a9a4e676b 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2369,7 +2369,7 @@  static int path_parentat(struct nameidata *nd, unsigned flags,
 
 static struct filename *filename_parentat(int dfd, struct filename *name,
 				unsigned int flags, struct path *parent,
-				struct qstr *last, int *type)
+				struct qstr *last, int *type, bool *foldtree)
 {
 	int retval;
 	struct nameidata nd;
@@ -2385,6 +2385,7 @@  static struct filename *filename_parentat(int dfd, struct filename *name,
 	if (likely(!retval)) {
 		*last = nd.last;
 		*type = nd.last_type;
+		*foldtree = !!(nd.flags & LOOKUP_CASEFOLD);
 		audit_inode(name, parent->dentry, LOOKUP_PARENT);
 	} else {
 		putname(name);
@@ -2401,9 +2402,14 @@  struct dentry *kern_path_locked(const char *name, struct path *path)
 	struct dentry *d;
 	struct qstr last;
 	int type;
+	bool foldtree = false;
 
 	filename = filename_parentat(AT_FDCWD, getname_kernel(name), 0, path,
-				    &last, &type);
+				     &last, &type, &foldtree);
+
+	/* kernel path don't care about foldtree.  Root cannot be folded. */
+	WARN_ON(foldtree);
+
 	if (IS_ERR(filename))
 		return ERR_CAST(filename);
 	if (unlikely(type != LAST_NORM)) {
@@ -3620,6 +3626,7 @@  static struct dentry *filename_create(int dfd, struct filename *name,
 	int err2;
 	int error;
 	bool is_dir = (lookup_flags & LOOKUP_DIRECTORY);
+	bool foldtree = false;
 
 	/*
 	 * Note that only LOOKUP_REVAL and LOOKUP_DIRECTORY matter here. Any
@@ -3627,7 +3634,8 @@  static struct dentry *filename_create(int dfd, struct filename *name,
 	 */
 	lookup_flags &= LOOKUP_REVAL;
 
-	name = filename_parentat(dfd, name, lookup_flags, path, &last, &type);
+	name = filename_parentat(dfd, name, lookup_flags, path, &last,
+				 &type, &foldtree);
 	if (IS_ERR(name))
 		return ERR_CAST(name);
 
@@ -3644,6 +3652,16 @@  static struct dentry *filename_create(int dfd, struct filename *name,
 	 * Do the final lookup.
 	 */
 	lookup_flags |= LOOKUP_CREATE | LOOKUP_EXCL;
+
+	/* If parent is a casefold mountpoint, search for an existing
+	 * dentry with LOOKUP_CASEFOLD.
+	 * LOOKUP_CASEFOLD needs to be explicitly cleared in case it is
+	 * doing retry. */
+	if (foldtree)
+		lookup_flags |= LOOKUP_CASEFOLD;
+	else
+		lookup_flags &= ~LOOKUP_CASEFOLD;
+
 	inode_lock_nested(path->dentry->d_inode, I_MUTEX_PARENT);
 	dentry = __lookup_hash(&last, path->dentry, lookup_flags);
 	if (IS_ERR(dentry))
@@ -3904,9 +3922,10 @@  static long do_rmdir(int dfd, const char __user *pathname)
 	struct qstr last;
 	int type;
 	unsigned int lookup_flags = 0;
+	bool foldtree;
 retry:
 	name = filename_parentat(dfd, getname(pathname), lookup_flags,
-				&path, &last, &type);
+				 &path, &last, &type, &foldtree);
 	if (IS_ERR(name))
 		return PTR_ERR(name);
 
@@ -3926,6 +3945,13 @@  static long do_rmdir(int dfd, const char __user *pathname)
 	if (error)
 		goto exit1;
 
+	/* LOOKUP_CASEFOLD needs to be explicitly cleared in case it is
+	 * doing retry. */
+	if (foldtree)
+		lookup_flags |= LOOKUP_CASEFOLD;
+	else
+		lookup_flags &= ~LOOKUP_CASEFOLD;
+
 	inode_lock_nested(path.dentry->d_inode, I_MUTEX_PARENT);
 	dentry = __lookup_hash(&last, path.dentry, lookup_flags);
 	error = PTR_ERR(dentry);
@@ -4033,8 +4059,10 @@  long do_unlinkat(int dfd, struct filename *name)
 	struct inode *inode = NULL;
 	struct inode *delegated_inode = NULL;
 	unsigned int lookup_flags = 0;
+	bool foldtree = false;
 retry:
-	name = filename_parentat(dfd, name, lookup_flags, &path, &last, &type);
+	name = filename_parentat(dfd, name, lookup_flags, &path, &last, &type,
+				 &foldtree);
 	if (IS_ERR(name))
 		return PTR_ERR(name);
 
@@ -4045,6 +4073,13 @@  long do_unlinkat(int dfd, struct filename *name)
 	error = mnt_want_write(path.mnt);
 	if (error)
 		goto exit1;
+
+	/* LOOKUP_CASEFOLD needs to be explicitly cleared in case it is
+	 * doing retry. */
+	if (foldtree)
+		lookup_flags |= LOOKUP_CASEFOLD;
+	else
+		lookup_flags &= ~LOOKUP_CASEFOLD;
 retry_deleg:
 	inode_lock_nested(path.dentry->d_inode, I_MUTEX_PARENT);
 	dentry = __lookup_hash(&last, path.dentry, lookup_flags);
@@ -4514,6 +4549,7 @@  SYSCALL_DEFINE5(renameat2, int, olddfd, const char __user *, oldname,
 	struct filename *to;
 	unsigned int lookup_flags = 0, target_flags = LOOKUP_RENAME_TARGET;
 	bool should_retry = false;
+	bool foldtree = false;
 	int error;
 
 	if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE | RENAME_WHITEOUT))
@@ -4531,14 +4567,14 @@  SYSCALL_DEFINE5(renameat2, int, olddfd, const char __user *, oldname,
 
 retry:
 	from = filename_parentat(olddfd, getname(oldname), lookup_flags,
-				&old_path, &old_last, &old_type);
+				 &old_path, &old_last, &old_type, &foldtree);
 	if (IS_ERR(from)) {
 		error = PTR_ERR(from);
 		goto exit;
 	}
 
 	to = filename_parentat(newdfd, getname(newname), lookup_flags,
-				&new_path, &new_last, &new_type);
+			       &new_path, &new_last, &new_type, &foldtree);
 	if (IS_ERR(to)) {
 		error = PTR_ERR(to);
 		goto exit1;
@@ -4561,6 +4597,17 @@  SYSCALL_DEFINE5(renameat2, int, olddfd, const char __user *, oldname,
 	if (error)
 		goto exit2;
 
+	/* Since rename(2) doesn't work across mountpoints, we are sure
+	 * that foldtree for TO and FROM are the same.
+	 *
+	 * LOOKUP_CASEFOLD needs to be explicitly cleared in case it is
+	 * doing retry. */
+	if (foldtree)
+		lookup_flags |= LOOKUP_CASEFOLD;
+	else
+		lookup_flags &= ~LOOKUP_CASEFOLD;
+
+
 retry_deleg:
 	trap = lock_rename(new_path.dentry, old_path.dentry);