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@xxxxxxxxxxxxxxx> --- fs/namei.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 54 insertions(+), 7 deletions(-) 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); -- 2.17.0