Merge tag '6.4-rc-ksmbd-server-fixes' of git://git.samba.org/ksmbd
authorLinus Torvalds <torvalds@linux-foundation.org>
Sat, 29 Apr 2023 18:10:39 +0000 (11:10 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sat, 29 Apr 2023 18:10:39 +0000 (11:10 -0700)
Pull ksmbd server updates from Steve French:

 - SMB3.1.1 negotiate context fixes and cleanup

 - new lock_rename_child VFS helper

 - ksmbd fix to avoid unlink race and to use the new VFS helper to avoid
   rename race

* tag '6.4-rc-ksmbd-server-fixes' of git://git.samba.org/ksmbd:
  ksmbd: fix racy issue from using ->d_parent and ->d_name
  ksmbd: remove unused compression negotiate ctx packing
  ksmbd: avoid duplicate negotiate ctx offset increments
  ksmbd: set NegotiateContextCount once instead of every inc
  fs: introduce lock_rename_child() helper
  ksmbd: remove internal.h include

1  2 
fs/internal.h
fs/namei.c

diff --combined fs/internal.h
@@@ -59,8 -59,6 +59,6 @@@ extern int finish_clean_context(struct 
   */
  extern int filename_lookup(int dfd, struct filename *name, unsigned flags,
                           struct path *path, struct path *root);
- extern int vfs_path_lookup(struct dentry *, struct vfsmount *,
-                          const char *, unsigned int, struct path *);
  int do_rmdir(int dfd, struct filename *name);
  int do_unlinkat(int dfd, struct filename *name);
  int may_linkat(struct mnt_idmap *idmap, const struct path *link);
@@@ -259,6 -257,8 +257,6 @@@ ssize_t __kernel_write_iter(struct fil
  /*
   * fs/attr.c
   */
 -int setattr_should_drop_sgid(struct mnt_idmap *idmap,
 -                           const struct inode *inode);
  struct mnt_idmap *alloc_mnt_idmap(struct user_namespace *mnt_userns);
  struct mnt_idmap *mnt_idmap_get(struct mnt_idmap *idmap);
  void mnt_idmap_put(struct mnt_idmap *idmap);
diff --combined fs/namei.c
@@@ -254,6 -254,7 +254,7 @@@ getname_kernel(const char * filename
  
        return result;
  }
+ EXPORT_SYMBOL(getname_kernel);
  
  void putname(struct filename *name)
  {
        } else
                __putname(name);
  }
+ EXPORT_SYMBOL(putname);
  
  /**
   * check_acl - perform ACL permission checking
@@@ -1581,8 -1583,9 +1583,9 @@@ static struct dentry *lookup_dcache(con
   * 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;
        }
        return dentry;
  }
+ EXPORT_SYMBOL(lookup_one_qstr_excl);
  
  static struct dentry *lookup_fast(struct nameidata *nd)
  {
@@@ -2532,16 -2536,17 +2536,17 @@@ static int path_parentat(struct nameida
  }
  
  /* 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);
        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)
  {
                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);
@@@ -2600,6 -2612,24 +2612,24 @@@ int kern_path(const char *name, unsigne
  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
   * @mnt: pointer to vfs mount of the base directory
@@@ -2980,20 -3010,10 +3010,10 @@@ static inline int may_create(struct mnt
        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);
        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);
@@@ -3574,9 -3650,9 +3650,9 @@@ static int do_open(struct nameidata *nd
  /**
   * vfs_tmpfile - create tmpfile
   * @idmap:    idmap of the mount the inode was found from
 - * @dentry:   pointer to dentry of the base directory
 + * @parentpath:       pointer to the path of the base directory
 + * @file:     file descriptor of the new tmpfile
   * @mode:     mode of the new tmpfile
 - * @open_flag:        flags
   *
   * Create a temporary file.
   *
@@@ -3806,7 -3882,8 +3882,8 @@@ static struct dentry *filename_create(i
        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 +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 +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 +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;
        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;