Merge tag 'locking-core-2023-05-05' of git://git.kernel.org/pub/scm/linux/kernel...
[linux-block.git] / fs / namei.c
index f04f7be589331992c794fe96296db96dbfba2e18..e4fe0879ae5539d3e95dc3b7eec988b16db2a25f 100644 (file)
@@ -254,6 +254,7 @@ getname_kernel(const char * filename)
 
        return result;
 }
+EXPORT_SYMBOL(getname_kernel);
 
 void putname(struct filename *name)
 {
@@ -271,6 +272,7 @@ void putname(struct filename *name)
        } else
                __putname(name);
 }
+EXPORT_SYMBOL(putname);
 
 /**
  * check_acl - perform ACL permission checking
@@ -1581,8 +1583,9 @@ static struct dentry *lookup_dcache(const struct qstr *name,
  * when directory is guaranteed to have no in-lookup children
  * at all.
  */
-static struct dentry *__lookup_hash(const struct qstr *name,
-               struct dentry *base, unsigned int flags)
+struct dentry *lookup_one_qstr_excl(const struct qstr *name,
+                                   struct dentry *base,
+                                   unsigned int flags)
 {
        struct dentry *dentry = lookup_dcache(name, base, flags);
        struct dentry *old;
@@ -1606,6 +1609,7 @@ static struct dentry *__lookup_hash(const struct qstr *name,
        }
        return dentry;
 }
+EXPORT_SYMBOL(lookup_one_qstr_excl);
 
 static struct dentry *lookup_fast(struct nameidata *nd)
 {
@@ -2532,16 +2536,17 @@ static int path_parentat(struct nameidata *nd, unsigned flags,
 }
 
 /* Note: this does not consume "name" */
-static int filename_parentat(int dfd, struct filename *name,
-                            unsigned int flags, struct path *parent,
-                            struct qstr *last, int *type)
+static int __filename_parentat(int dfd, struct filename *name,
+                              unsigned int flags, struct path *parent,
+                              struct qstr *last, int *type,
+                              const struct path *root)
 {
        int retval;
        struct nameidata nd;
 
        if (IS_ERR(name))
                return PTR_ERR(name);
-       set_nameidata(&nd, dfd, name, NULL);
+       set_nameidata(&nd, dfd, name, root);
        retval = path_parentat(&nd, flags | LOOKUP_RCU, parent);
        if (unlikely(retval == -ECHILD))
                retval = path_parentat(&nd, flags, parent);
@@ -2556,6 +2561,13 @@ static int filename_parentat(int dfd, struct filename *name,
        return retval;
 }
 
+static int filename_parentat(int dfd, struct filename *name,
+                            unsigned int flags, struct path *parent,
+                            struct qstr *last, int *type)
+{
+       return __filename_parentat(dfd, name, flags, parent, last, type, NULL);
+}
+
 /* does lookup, returns the object with parent locked */
 static struct dentry *__kern_path_locked(struct filename *name, struct path *path)
 {
@@ -2571,7 +2583,7 @@ static struct dentry *__kern_path_locked(struct filename *name, struct path *pat
                return ERR_PTR(-EINVAL);
        }
        inode_lock_nested(path->dentry->d_inode, I_MUTEX_PARENT);
-       d = __lookup_hash(&last, path->dentry, 0);
+       d = lookup_one_qstr_excl(&last, path->dentry, 0);
        if (IS_ERR(d)) {
                inode_unlock(path->dentry->d_inode);
                path_put(path);
@@ -2599,6 +2611,24 @@ int kern_path(const char *name, unsigned int flags, struct path *path)
 }
 EXPORT_SYMBOL(kern_path);
 
+/**
+ * vfs_path_parent_lookup - lookup a parent path relative to a dentry-vfsmount pair
+ * @filename: filename structure
+ * @flags: lookup flags
+ * @parent: pointer to struct path to fill
+ * @last: last component
+ * @type: type of the last component
+ * @root: pointer to struct path of the base directory
+ */
+int vfs_path_parent_lookup(struct filename *filename, unsigned int flags,
+                          struct path *parent, struct qstr *last, int *type,
+                          const struct path *root)
+{
+       return  __filename_parentat(AT_FDCWD, filename, flags, parent, last,
+                                   type, root);
+}
+EXPORT_SYMBOL(vfs_path_parent_lookup);
+
 /**
  * vfs_path_lookup - lookup a file path relative to a dentry-vfsmount pair
  * @dentry:  pointer to dentry of the base directory
@@ -2980,20 +3010,10 @@ static inline int may_create(struct mnt_idmap *idmap,
        return inode_permission(idmap, dir, MAY_WRITE | MAY_EXEC);
 }
 
-/*
- * p1 and p2 should be directories on the same fs.
- */
-struct dentry *lock_rename(struct dentry *p1, struct dentry *p2)
+static struct dentry *lock_two_directories(struct dentry *p1, struct dentry *p2)
 {
        struct dentry *p;
 
-       if (p1 == p2) {
-               inode_lock_nested(p1->d_inode, I_MUTEX_PARENT);
-               return NULL;
-       }
-
-       mutex_lock(&p1->d_sb->s_vfs_rename_mutex);
-
        p = d_ancestor(p2, p1);
        if (p) {
                inode_lock_nested(p2->d_inode, I_MUTEX_PARENT);
@@ -3012,8 +3032,64 @@ struct dentry *lock_rename(struct dentry *p1, struct dentry *p2)
        inode_lock_nested(p2->d_inode, I_MUTEX_PARENT2);
        return NULL;
 }
+
+/*
+ * p1 and p2 should be directories on the same fs.
+ */
+struct dentry *lock_rename(struct dentry *p1, struct dentry *p2)
+{
+       if (p1 == p2) {
+               inode_lock_nested(p1->d_inode, I_MUTEX_PARENT);
+               return NULL;
+       }
+
+       mutex_lock(&p1->d_sb->s_vfs_rename_mutex);
+       return lock_two_directories(p1, p2);
+}
 EXPORT_SYMBOL(lock_rename);
 
+/*
+ * c1 and p2 should be on the same fs.
+ */
+struct dentry *lock_rename_child(struct dentry *c1, struct dentry *p2)
+{
+       if (READ_ONCE(c1->d_parent) == p2) {
+               /*
+                * hopefully won't need to touch ->s_vfs_rename_mutex at all.
+                */
+               inode_lock_nested(p2->d_inode, I_MUTEX_PARENT);
+               /*
+                * now that p2 is locked, nobody can move in or out of it,
+                * so the test below is safe.
+                */
+               if (likely(c1->d_parent == p2))
+                       return NULL;
+
+               /*
+                * c1 got moved out of p2 while we'd been taking locks;
+                * unlock and fall back to slow case.
+                */
+               inode_unlock(p2->d_inode);
+       }
+
+       mutex_lock(&c1->d_sb->s_vfs_rename_mutex);
+       /*
+        * nobody can move out of any directories on this fs.
+        */
+       if (likely(c1->d_parent != p2))
+               return lock_two_directories(c1->d_parent, p2);
+
+       /*
+        * c1 got moved into p2 while we were taking locks;
+        * we need p2 locked and ->s_vfs_rename_mutex unlocked,
+        * for consistency with lock_rename().
+        */
+       inode_lock_nested(p2->d_inode, I_MUTEX_PARENT);
+       mutex_unlock(&c1->d_sb->s_vfs_rename_mutex);
+       return NULL;
+}
+EXPORT_SYMBOL(lock_rename_child);
+
 void unlock_rename(struct dentry *p1, struct dentry *p2)
 {
        inode_unlock(p1->d_inode);
@@ -3806,7 +3882,8 @@ static struct dentry *filename_create(int dfd, struct filename *name,
        if (last.name[last.len] && !want_dir)
                create_flags = 0;
        inode_lock_nested(path->dentry->d_inode, I_MUTEX_PARENT);
-       dentry = __lookup_hash(&last, path->dentry, reval_flag | create_flags);
+       dentry = lookup_one_qstr_excl(&last, path->dentry,
+                                     reval_flag | create_flags);
        if (IS_ERR(dentry))
                goto unlock;
 
@@ -4166,7 +4243,7 @@ retry:
                goto exit2;
 
        inode_lock_nested(path.dentry->d_inode, I_MUTEX_PARENT);
-       dentry = __lookup_hash(&last, path.dentry, lookup_flags);
+       dentry = lookup_one_qstr_excl(&last, path.dentry, lookup_flags);
        error = PTR_ERR(dentry);
        if (IS_ERR(dentry))
                goto exit3;
@@ -4299,7 +4376,7 @@ retry:
                goto exit2;
 retry_deleg:
        inode_lock_nested(path.dentry->d_inode, I_MUTEX_PARENT);
-       dentry = __lookup_hash(&last, path.dentry, lookup_flags);
+       dentry = lookup_one_qstr_excl(&last, path.dentry, lookup_flags);
        error = PTR_ERR(dentry);
        if (!IS_ERR(dentry)) {
 
@@ -4863,7 +4940,8 @@ retry:
 retry_deleg:
        trap = lock_rename(new_path.dentry, old_path.dentry);
 
-       old_dentry = __lookup_hash(&old_last, old_path.dentry, lookup_flags);
+       old_dentry = lookup_one_qstr_excl(&old_last, old_path.dentry,
+                                         lookup_flags);
        error = PTR_ERR(old_dentry);
        if (IS_ERR(old_dentry))
                goto exit3;
@@ -4871,7 +4949,8 @@ retry_deleg:
        error = -ENOENT;
        if (d_is_negative(old_dentry))
                goto exit4;
-       new_dentry = __lookup_hash(&new_last, new_path.dentry, lookup_flags | target_flags);
+       new_dentry = lookup_one_qstr_excl(&new_last, new_path.dentry,
+                                         lookup_flags | target_flags);
        error = PTR_ERR(new_dentry);
        if (IS_ERR(new_dentry))
                goto exit4;