@@ -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);
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(-)