]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
Fixes for 6.3
authorSasha Levin <sashal@kernel.org>
Fri, 23 Jun 2023 12:45:13 +0000 (08:45 -0400)
committerSasha Levin <sashal@kernel.org>
Fri, 23 Jun 2023 12:45:13 +0000 (08:45 -0400)
Signed-off-by: Sasha Levin <sashal@kernel.org>
queue-6.3/fs-introduce-lock_rename_child-helper.patch [new file with mode: 0644]
queue-6.3/ksmbd-add-mnt_want_write-to-ksmbd-vfs-functions.patch [new file with mode: 0644]
queue-6.3/ksmbd-fix-racy-issue-from-using-d_parent-and-d_name.patch [new file with mode: 0644]
queue-6.3/ksmbd-remove-internal.h-include.patch [new file with mode: 0644]
queue-6.3/regmap-spi-avmm-fix-regmap_bus-max_raw_write.patch [new file with mode: 0644]
queue-6.3/regulator-pca9450-fix-ldo3out-and-ldo4out-mask.patch [new file with mode: 0644]
queue-6.3/series
queue-6.3/spi-spi-geni-qcom-correctly-handle-eprobe_defer-from.patch [new file with mode: 0644]

diff --git a/queue-6.3/fs-introduce-lock_rename_child-helper.patch b/queue-6.3/fs-introduce-lock_rename_child-helper.patch
new file mode 100644 (file)
index 0000000..926aa47
--- /dev/null
@@ -0,0 +1,132 @@
+From fa040cabc969977f1f775d154ea8bb3217face13 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 16 Mar 2023 07:34:34 +0900
+Subject: fs: introduce lock_rename_child() helper
+
+From: Al Viro <viro@zeniv.linux.org.uk>
+
+[ Upstream commit 9bc37e04823b5280dd0f22b6680fc23fe81ca325 ]
+
+Pass the dentry of a source file and the dentry of a destination directory
+to lock parent inodes for rename. As soon as this function returns,
+->d_parent of the source file dentry is stable and inodes are properly
+locked for calling vfs-rename. This helper is needed for ksmbd server.
+rename request of SMB protocol has to rename an opened file, no matter
+which directory it's in.
+
+Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
+Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
+Stable-dep-of: 40b268d384a2 ("ksmbd: add mnt_want_write to ksmbd vfs functions")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/namei.c            | 68 ++++++++++++++++++++++++++++++++++++-------
+ include/linux/namei.h |  1 +
+ 2 files changed, 58 insertions(+), 11 deletions(-)
+
+diff --git a/fs/namei.c b/fs/namei.c
+index edfedfbccaef4..6bc1964e2214e 100644
+--- a/fs/namei.c
++++ b/fs/namei.c
+@@ -2980,20 +2980,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 +3002,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);
+diff --git a/include/linux/namei.h b/include/linux/namei.h
+index ba9b32b4d1b07..5864e4d82e567 100644
+--- a/include/linux/namei.h
++++ b/include/linux/namei.h
+@@ -83,6 +83,7 @@ extern int follow_down(struct path *path, unsigned int flags);
+ extern int follow_up(struct path *);
+ extern struct dentry *lock_rename(struct dentry *, struct dentry *);
++extern struct dentry *lock_rename_child(struct dentry *, struct dentry *);
+ extern void unlock_rename(struct dentry *, struct dentry *);
+ extern int __must_check nd_jump_link(const struct path *path);
+-- 
+2.39.2
+
diff --git a/queue-6.3/ksmbd-add-mnt_want_write-to-ksmbd-vfs-functions.patch b/queue-6.3/ksmbd-add-mnt_want_write-to-ksmbd-vfs-functions.patch
new file mode 100644 (file)
index 0000000..e3ef296
--- /dev/null
@@ -0,0 +1,612 @@
+From bfb95b2b430cc3a4e9cac2b511dac80ba96e948f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 15 Jun 2023 15:56:32 +0900
+Subject: ksmbd: add mnt_want_write to ksmbd vfs functions
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit 40b268d384a22276dca1450549f53eed60e21deb ]
+
+ksmbd is doing write access using vfs helpers. There are the cases that
+mnt_want_write() is not called in vfs helper. This patch add missing
+mnt_want_write() to ksmbd vfs functions.
+
+Cc: stable@vger.kernel.org
+Cc: Amir Goldstein <amir73il@gmail.com>
+Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/ksmbd/smb2pdu.c   |  26 ++++------
+ fs/ksmbd/smbacl.c    |  10 ++--
+ fs/ksmbd/vfs.c       | 117 ++++++++++++++++++++++++++++++++++---------
+ fs/ksmbd/vfs.h       |  17 +++----
+ fs/ksmbd/vfs_cache.c |   2 +-
+ 5 files changed, 117 insertions(+), 55 deletions(-)
+
+diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c
+index 06a335356e373..1d365eb79c3ef 100644
+--- a/fs/ksmbd/smb2pdu.c
++++ b/fs/ksmbd/smb2pdu.c
+@@ -2311,7 +2311,7 @@ static int smb2_set_ea(struct smb2_ea_info *eabuf, unsigned int buf_len,
+                       /* delete the EA only when it exits */
+                       if (rc > 0) {
+                               rc = ksmbd_vfs_remove_xattr(idmap,
+-                                                          path->dentry,
++                                                          path,
+                                                           attr_name);
+                               if (rc < 0) {
+@@ -2325,8 +2325,7 @@ static int smb2_set_ea(struct smb2_ea_info *eabuf, unsigned int buf_len,
+                       /* if the EA doesn't exist, just do nothing. */
+                       rc = 0;
+               } else {
+-                      rc = ksmbd_vfs_setxattr(idmap,
+-                                              path->dentry, attr_name, value,
++                      rc = ksmbd_vfs_setxattr(idmap, path, attr_name, value,
+                                               le16_to_cpu(eabuf->EaValueLength), 0);
+                       if (rc < 0) {
+                               ksmbd_debug(SMB,
+@@ -2383,8 +2382,7 @@ static noinline int smb2_set_stream_name_xattr(const struct path *path,
+               return -EBADF;
+       }
+-      rc = ksmbd_vfs_setxattr(idmap, path->dentry,
+-                              xattr_stream_name, NULL, 0, 0);
++      rc = ksmbd_vfs_setxattr(idmap, path, xattr_stream_name, NULL, 0, 0);
+       if (rc < 0)
+               pr_err("Failed to store XATTR stream name :%d\n", rc);
+       return 0;
+@@ -2412,7 +2410,7 @@ static int smb2_remove_smb_xattrs(const struct path *path)
+               if (!strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN) &&
+                   !strncmp(&name[XATTR_USER_PREFIX_LEN], STREAM_PREFIX,
+                            STREAM_PREFIX_LEN)) {
+-                      err = ksmbd_vfs_remove_xattr(idmap, path->dentry,
++                      err = ksmbd_vfs_remove_xattr(idmap, path,
+                                                    name);
+                       if (err)
+                               ksmbd_debug(SMB, "remove xattr failed : %s\n",
+@@ -2459,8 +2457,7 @@ static void smb2_new_xattrs(struct ksmbd_tree_connect *tcon, const struct path *
+       da.flags = XATTR_DOSINFO_ATTRIB | XATTR_DOSINFO_CREATE_TIME |
+               XATTR_DOSINFO_ITIME;
+-      rc = ksmbd_vfs_set_dos_attrib_xattr(mnt_idmap(path->mnt),
+-                                          path->dentry, &da);
++      rc = ksmbd_vfs_set_dos_attrib_xattr(mnt_idmap(path->mnt), path, &da);
+       if (rc)
+               ksmbd_debug(SMB, "failed to store file attribute into xattr\n");
+ }
+@@ -3034,7 +3031,7 @@ int smb2_open(struct ksmbd_work *work)
+               struct inode *inode = d_inode(path.dentry);
+               posix_acl_rc = ksmbd_vfs_inherit_posix_acl(idmap,
+-                                                         path.dentry,
++                                                         &path,
+                                                          d_inode(path.dentry->d_parent));
+               if (posix_acl_rc)
+                       ksmbd_debug(SMB, "inherit posix acl failed : %d\n", posix_acl_rc);
+@@ -3050,7 +3047,7 @@ int smb2_open(struct ksmbd_work *work)
+                       if (rc) {
+                               if (posix_acl_rc)
+                                       ksmbd_vfs_set_init_posix_acl(idmap,
+-                                                                   path.dentry);
++                                                                   &path);
+                               if (test_share_config_flag(work->tcon->share_conf,
+                                                          KSMBD_SHARE_FLAG_ACL_XATTR)) {
+@@ -3090,7 +3087,7 @@ int smb2_open(struct ksmbd_work *work)
+                                       rc = ksmbd_vfs_set_sd_xattr(conn,
+                                                                   idmap,
+-                                                                  path.dentry,
++                                                                  &path,
+                                                                   pntsd,
+                                                                   pntsd_size);
+                                       kfree(pntsd);
+@@ -5526,7 +5523,7 @@ static int smb2_rename(struct ksmbd_work *work,
+                       goto out;
+               rc = ksmbd_vfs_setxattr(file_mnt_idmap(fp->filp),
+-                                      fp->filp->f_path.dentry,
++                                      &fp->filp->f_path,
+                                       xattr_stream_name,
+                                       NULL, 0, 0);
+               if (rc < 0) {
+@@ -5691,8 +5688,7 @@ static int set_file_basic_info(struct ksmbd_file *fp,
+               da.flags = XATTR_DOSINFO_ATTRIB | XATTR_DOSINFO_CREATE_TIME |
+                       XATTR_DOSINFO_ITIME;
+-              rc = ksmbd_vfs_set_dos_attrib_xattr(idmap,
+-                                                  filp->f_path.dentry, &da);
++              rc = ksmbd_vfs_set_dos_attrib_xattr(idmap, &filp->f_path, &da);
+               if (rc)
+                       ksmbd_debug(SMB,
+                                   "failed to restore file attribute in EA\n");
+@@ -7547,7 +7543,7 @@ static inline int fsctl_set_sparse(struct ksmbd_work *work, u64 id,
+               da.attr = le32_to_cpu(fp->f_ci->m_fattr);
+               ret = ksmbd_vfs_set_dos_attrib_xattr(idmap,
+-                                                   fp->filp->f_path.dentry, &da);
++                                                   &fp->filp->f_path, &da);
+               if (ret)
+                       fp->f_ci->m_fattr = old_fattr;
+       }
+diff --git a/fs/ksmbd/smbacl.c b/fs/ksmbd/smbacl.c
+index 0a5862a61c773..ad919a4239d0a 100644
+--- a/fs/ksmbd/smbacl.c
++++ b/fs/ksmbd/smbacl.c
+@@ -1162,8 +1162,7 @@ int smb_inherit_dacl(struct ksmbd_conn *conn,
+                       pntsd_size += sizeof(struct smb_acl) + nt_size;
+               }
+-              ksmbd_vfs_set_sd_xattr(conn, idmap,
+-                                     path->dentry, pntsd, pntsd_size);
++              ksmbd_vfs_set_sd_xattr(conn, idmap, path, pntsd, pntsd_size);
+               kfree(pntsd);
+       }
+@@ -1383,7 +1382,7 @@ int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon,
+       newattrs.ia_valid |= ATTR_MODE;
+       newattrs.ia_mode = (inode->i_mode & ~0777) | (fattr.cf_mode & 0777);
+-      ksmbd_vfs_remove_acl_xattrs(idmap, path->dentry);
++      ksmbd_vfs_remove_acl_xattrs(idmap, path);
+       /* Update posix acls */
+       if (IS_ENABLED(CONFIG_FS_POSIX_ACL) && fattr.cf_dacls) {
+               rc = set_posix_acl(idmap, path->dentry,
+@@ -1414,9 +1413,8 @@ int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon,
+       if (test_share_config_flag(tcon->share_conf, KSMBD_SHARE_FLAG_ACL_XATTR)) {
+               /* Update WinACL in xattr */
+-              ksmbd_vfs_remove_sd_xattrs(idmap, path->dentry);
+-              ksmbd_vfs_set_sd_xattr(conn, idmap,
+-                                     path->dentry, pntsd, ntsd_len);
++              ksmbd_vfs_remove_sd_xattrs(idmap, path);
++              ksmbd_vfs_set_sd_xattr(conn, idmap, path, pntsd, ntsd_len);
+       }
+ out:
+diff --git a/fs/ksmbd/vfs.c b/fs/ksmbd/vfs.c
+index 448c442ad3f49..fb19deba58cd9 100644
+--- a/fs/ksmbd/vfs.c
++++ b/fs/ksmbd/vfs.c
+@@ -166,6 +166,10 @@ int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode)
+               return err;
+       }
++      err = mnt_want_write(path.mnt);
++      if (err)
++              goto out_err;
++
+       mode |= S_IFREG;
+       err = vfs_create(mnt_idmap(path.mnt), d_inode(path.dentry),
+                        dentry, mode, true);
+@@ -175,6 +179,9 @@ int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode)
+       } else {
+               pr_err("File(%s): creation failed (err:%d)\n", name, err);
+       }
++      mnt_drop_write(path.mnt);
++
++out_err:
+       done_path_create(&path, dentry);
+       return err;
+ }
+@@ -205,30 +212,35 @@ int ksmbd_vfs_mkdir(struct ksmbd_work *work, const char *name, umode_t mode)
+               return err;
+       }
++      err = mnt_want_write(path.mnt);
++      if (err)
++              goto out_err2;
++
+       idmap = mnt_idmap(path.mnt);
+       mode |= S_IFDIR;
+       err = vfs_mkdir(idmap, d_inode(path.dentry), dentry, mode);
+-      if (err) {
+-              goto out;
+-      } else if (d_unhashed(dentry)) {
++      if (!err && d_unhashed(dentry)) {
+               struct dentry *d;
+               d = lookup_one(idmap, dentry->d_name.name, dentry->d_parent,
+                              dentry->d_name.len);
+               if (IS_ERR(d)) {
+                       err = PTR_ERR(d);
+-                      goto out;
++                      goto out_err1;
+               }
+               if (unlikely(d_is_negative(d))) {
+                       dput(d);
+                       err = -ENOENT;
+-                      goto out;
++                      goto out_err1;
+               }
+               ksmbd_vfs_inherit_owner(work, d_inode(path.dentry), d_inode(d));
+               dput(d);
+       }
+-out:
++
++out_err1:
++      mnt_drop_write(path.mnt);
++out_err2:
+       done_path_create(&path, dentry);
+       if (err)
+               pr_err("mkdir(%s): creation failed (err:%d)\n", name, err);
+@@ -439,7 +451,7 @@ static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos,
+       memcpy(&stream_buf[*pos], buf, count);
+       err = ksmbd_vfs_setxattr(idmap,
+-                               fp->filp->f_path.dentry,
++                               &fp->filp->f_path,
+                                fp->stream.name,
+                                (void *)stream_buf,
+                                size,
+@@ -585,6 +597,10 @@ int ksmbd_vfs_remove_file(struct ksmbd_work *work, const struct path *path)
+               goto out_err;
+       }
++      err = mnt_want_write(path->mnt);
++      if (err)
++              goto out_err;
++
+       idmap = mnt_idmap(path->mnt);
+       if (S_ISDIR(d_inode(path->dentry)->i_mode)) {
+               err = vfs_rmdir(idmap, d_inode(parent), path->dentry);
+@@ -595,6 +611,7 @@ int ksmbd_vfs_remove_file(struct ksmbd_work *work, const struct path *path)
+               if (err)
+                       ksmbd_debug(VFS, "unlink failed, err %d\n", err);
+       }
++      mnt_drop_write(path->mnt);
+ out_err:
+       ksmbd_revert_fsids(work);
+@@ -640,11 +657,16 @@ int ksmbd_vfs_link(struct ksmbd_work *work, const char *oldname,
+               goto out3;
+       }
++      err = mnt_want_write(newpath.mnt);
++      if (err)
++              goto out3;
++
+       err = vfs_link(oldpath.dentry, mnt_idmap(newpath.mnt),
+                      d_inode(newpath.dentry),
+                      dentry, NULL);
+       if (err)
+               ksmbd_debug(VFS, "vfs_link failed err %d\n", err);
++      mnt_drop_write(newpath.mnt);
+ out3:
+       done_path_create(&newpath, dentry);
+@@ -690,6 +712,10 @@ int ksmbd_vfs_rename(struct ksmbd_work *work, const struct path *old_path,
+               goto out2;
+       }
++      err = mnt_want_write(old_path->mnt);
++      if (err)
++              goto out2;
++
+       trap = lock_rename_child(old_child, new_path.dentry);
+       old_parent = dget(old_child->d_parent);
+@@ -752,6 +778,7 @@ int ksmbd_vfs_rename(struct ksmbd_work *work, const struct path *old_path,
+ out3:
+       dput(old_parent);
+       unlock_rename(old_parent, new_path.dentry);
++      mnt_drop_write(old_path->mnt);
+ out2:
+       path_put(&new_path);
+@@ -892,19 +919,24 @@ ssize_t ksmbd_vfs_getxattr(struct mnt_idmap *idmap,
+  * Return:    0 on success, otherwise error
+  */
+ int ksmbd_vfs_setxattr(struct mnt_idmap *idmap,
+-                     struct dentry *dentry, const char *attr_name,
++                     const struct path *path, const char *attr_name,
+                      void *attr_value, size_t attr_size, int flags)
+ {
+       int err;
++      err = mnt_want_write(path->mnt);
++      if (err)
++              return err;
++
+       err = vfs_setxattr(idmap,
+-                         dentry,
++                         path->dentry,
+                          attr_name,
+                          attr_value,
+                          attr_size,
+                          flags);
+       if (err)
+               ksmbd_debug(VFS, "setxattr failed, err %d\n", err);
++      mnt_drop_write(path->mnt);
+       return err;
+ }
+@@ -1008,9 +1040,18 @@ int ksmbd_vfs_fqar_lseek(struct ksmbd_file *fp, loff_t start, loff_t length,
+ }
+ int ksmbd_vfs_remove_xattr(struct mnt_idmap *idmap,
+-                         struct dentry *dentry, char *attr_name)
++                         const struct path *path, char *attr_name)
+ {
+-      return vfs_removexattr(idmap, dentry, attr_name);
++      int err;
++
++      err = mnt_want_write(path->mnt);
++      if (err)
++              return err;
++
++      err = vfs_removexattr(idmap, path->dentry, attr_name);
++      mnt_drop_write(path->mnt);
++
++      return err;
+ }
+ int ksmbd_vfs_unlink(struct file *filp)
+@@ -1019,6 +1060,10 @@ int ksmbd_vfs_unlink(struct file *filp)
+       struct dentry *dir, *dentry = filp->f_path.dentry;
+       struct mnt_idmap *idmap = file_mnt_idmap(filp);
++      err = mnt_want_write(filp->f_path.mnt);
++      if (err)
++              return err;
++
+       dir = dget_parent(dentry);
+       err = ksmbd_vfs_lock_parent(dir, dentry);
+       if (err)
+@@ -1036,6 +1081,7 @@ int ksmbd_vfs_unlink(struct file *filp)
+               ksmbd_debug(VFS, "failed to delete, err %d\n", err);
+ out:
+       dput(dir);
++      mnt_drop_write(filp->f_path.mnt);
+       return err;
+ }
+@@ -1239,13 +1285,13 @@ struct dentry *ksmbd_vfs_kern_path_create(struct ksmbd_work *work,
+ }
+ int ksmbd_vfs_remove_acl_xattrs(struct mnt_idmap *idmap,
+-                              struct dentry *dentry)
++                              const struct path *path)
+ {
+       char *name, *xattr_list = NULL;
+       ssize_t xattr_list_len;
+       int err = 0;
+-      xattr_list_len = ksmbd_vfs_listxattr(dentry, &xattr_list);
++      xattr_list_len = ksmbd_vfs_listxattr(path->dentry, &xattr_list);
+       if (xattr_list_len < 0) {
+               goto out;
+       } else if (!xattr_list_len) {
+@@ -1253,6 +1299,10 @@ int ksmbd_vfs_remove_acl_xattrs(struct mnt_idmap *idmap,
+               goto out;
+       }
++      err = mnt_want_write(path->mnt);
++      if (err)
++              goto out;
++
+       for (name = xattr_list; name - xattr_list < xattr_list_len;
+            name += strlen(name) + 1) {
+               ksmbd_debug(SMB, "%s, len %zd\n", name, strlen(name));
+@@ -1261,25 +1311,26 @@ int ksmbd_vfs_remove_acl_xattrs(struct mnt_idmap *idmap,
+                            sizeof(XATTR_NAME_POSIX_ACL_ACCESS) - 1) ||
+                   !strncmp(name, XATTR_NAME_POSIX_ACL_DEFAULT,
+                            sizeof(XATTR_NAME_POSIX_ACL_DEFAULT) - 1)) {
+-                      err = vfs_remove_acl(idmap, dentry, name);
++                      err = vfs_remove_acl(idmap, path->dentry, name);
+                       if (err)
+                               ksmbd_debug(SMB,
+                                           "remove acl xattr failed : %s\n", name);
+               }
+       }
++      mnt_drop_write(path->mnt);
++
+ out:
+       kvfree(xattr_list);
+       return err;
+ }
+-int ksmbd_vfs_remove_sd_xattrs(struct mnt_idmap *idmap,
+-                             struct dentry *dentry)
++int ksmbd_vfs_remove_sd_xattrs(struct mnt_idmap *idmap, const struct path *path)
+ {
+       char *name, *xattr_list = NULL;
+       ssize_t xattr_list_len;
+       int err = 0;
+-      xattr_list_len = ksmbd_vfs_listxattr(dentry, &xattr_list);
++      xattr_list_len = ksmbd_vfs_listxattr(path->dentry, &xattr_list);
+       if (xattr_list_len < 0) {
+               goto out;
+       } else if (!xattr_list_len) {
+@@ -1292,7 +1343,7 @@ int ksmbd_vfs_remove_sd_xattrs(struct mnt_idmap *idmap,
+               ksmbd_debug(SMB, "%s, len %zd\n", name, strlen(name));
+               if (!strncmp(name, XATTR_NAME_SD, XATTR_NAME_SD_LEN)) {
+-                      err = ksmbd_vfs_remove_xattr(idmap, dentry, name);
++                      err = ksmbd_vfs_remove_xattr(idmap, path, name);
+                       if (err)
+                               ksmbd_debug(SMB, "remove xattr failed : %s\n", name);
+               }
+@@ -1369,13 +1420,14 @@ static struct xattr_smb_acl *ksmbd_vfs_make_xattr_posix_acl(struct mnt_idmap *id
+ int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn,
+                          struct mnt_idmap *idmap,
+-                         struct dentry *dentry,
++                         const struct path *path,
+                          struct smb_ntsd *pntsd, int len)
+ {
+       int rc;
+       struct ndr sd_ndr = {0}, acl_ndr = {0};
+       struct xattr_ntacl acl = {0};
+       struct xattr_smb_acl *smb_acl, *def_smb_acl = NULL;
++      struct dentry *dentry = path->dentry;
+       struct inode *inode = d_inode(dentry);
+       acl.version = 4;
+@@ -1427,7 +1479,7 @@ int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn,
+               goto out;
+       }
+-      rc = ksmbd_vfs_setxattr(idmap, dentry,
++      rc = ksmbd_vfs_setxattr(idmap, path,
+                               XATTR_NAME_SD, sd_ndr.data,
+                               sd_ndr.offset, 0);
+       if (rc < 0)
+@@ -1517,7 +1569,7 @@ int ksmbd_vfs_get_sd_xattr(struct ksmbd_conn *conn,
+ }
+ int ksmbd_vfs_set_dos_attrib_xattr(struct mnt_idmap *idmap,
+-                                 struct dentry *dentry,
++                                 const struct path *path,
+                                  struct xattr_dos_attrib *da)
+ {
+       struct ndr n;
+@@ -1527,7 +1579,7 @@ int ksmbd_vfs_set_dos_attrib_xattr(struct mnt_idmap *idmap,
+       if (err)
+               return err;
+-      err = ksmbd_vfs_setxattr(idmap, dentry, XATTR_NAME_DOS_ATTRIBUTE,
++      err = ksmbd_vfs_setxattr(idmap, path, XATTR_NAME_DOS_ATTRIBUTE,
+                                (void *)n.data, n.offset, 0);
+       if (err)
+               ksmbd_debug(SMB, "failed to store dos attribute in xattr\n");
+@@ -1764,10 +1816,11 @@ void ksmbd_vfs_posix_lock_unblock(struct file_lock *flock)
+ }
+ int ksmbd_vfs_set_init_posix_acl(struct mnt_idmap *idmap,
+-                               struct dentry *dentry)
++                               struct path *path)
+ {
+       struct posix_acl_state acl_state;
+       struct posix_acl *acls;
++      struct dentry *dentry = path->dentry;
+       struct inode *inode = d_inode(dentry);
+       int rc;
+@@ -1797,6 +1850,11 @@ int ksmbd_vfs_set_init_posix_acl(struct mnt_idmap *idmap,
+               return -ENOMEM;
+       }
+       posix_state_to_acl(&acl_state, acls->a_entries);
++
++      rc = mnt_want_write(path->mnt);
++      if (rc)
++              goto out_err;
++
+       rc = set_posix_acl(idmap, dentry, ACL_TYPE_ACCESS, acls);
+       if (rc < 0)
+               ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_ACCESS) failed, rc : %d\n",
+@@ -1808,16 +1866,20 @@ int ksmbd_vfs_set_init_posix_acl(struct mnt_idmap *idmap,
+                       ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_DEFAULT) failed, rc : %d\n",
+                                   rc);
+       }
++      mnt_drop_write(path->mnt);
++
++out_err:
+       free_acl_state(&acl_state);
+       posix_acl_release(acls);
+       return rc;
+ }
+ int ksmbd_vfs_inherit_posix_acl(struct mnt_idmap *idmap,
+-                              struct dentry *dentry, struct inode *parent_inode)
++                              struct path *path, struct inode *parent_inode)
+ {
+       struct posix_acl *acls;
+       struct posix_acl_entry *pace;
++      struct dentry *dentry = path->dentry;
+       struct inode *inode = d_inode(dentry);
+       int rc, i;
+@@ -1836,6 +1898,10 @@ int ksmbd_vfs_inherit_posix_acl(struct mnt_idmap *idmap,
+               }
+       }
++      rc = mnt_want_write(path->mnt);
++      if (rc)
++              goto out_err;
++
+       rc = set_posix_acl(idmap, dentry, ACL_TYPE_ACCESS, acls);
+       if (rc < 0)
+               ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_ACCESS) failed, rc : %d\n",
+@@ -1847,6 +1913,9 @@ int ksmbd_vfs_inherit_posix_acl(struct mnt_idmap *idmap,
+                       ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_DEFAULT) failed, rc : %d\n",
+                                   rc);
+       }
++      mnt_drop_write(path->mnt);
++
++out_err:
+       posix_acl_release(acls);
+       return rc;
+ }
+diff --git a/fs/ksmbd/vfs.h b/fs/ksmbd/vfs.h
+index a4ae89f3230de..8c0931d4d5310 100644
+--- a/fs/ksmbd/vfs.h
++++ b/fs/ksmbd/vfs.h
+@@ -108,12 +108,12 @@ ssize_t ksmbd_vfs_casexattr_len(struct mnt_idmap *idmap,
+                               struct dentry *dentry, char *attr_name,
+                               int attr_name_len);
+ int ksmbd_vfs_setxattr(struct mnt_idmap *idmap,
+-                     struct dentry *dentry, const char *attr_name,
++                     const struct path *path, const char *attr_name,
+                      void *attr_value, size_t attr_size, int flags);
+ int ksmbd_vfs_xattr_stream_name(char *stream_name, char **xattr_stream_name,
+                               size_t *xattr_stream_name_size, int s_type);
+ int ksmbd_vfs_remove_xattr(struct mnt_idmap *idmap,
+-                         struct dentry *dentry, char *attr_name);
++                         const struct path *path, char *attr_name);
+ int ksmbd_vfs_kern_path_locked(struct ksmbd_work *work, char *name,
+                              unsigned int flags, struct path *path,
+                              bool caseless);
+@@ -139,26 +139,25 @@ void ksmbd_vfs_posix_lock_wait(struct file_lock *flock);
+ int ksmbd_vfs_posix_lock_wait_timeout(struct file_lock *flock, long timeout);
+ void ksmbd_vfs_posix_lock_unblock(struct file_lock *flock);
+ int ksmbd_vfs_remove_acl_xattrs(struct mnt_idmap *idmap,
+-                              struct dentry *dentry);
+-int ksmbd_vfs_remove_sd_xattrs(struct mnt_idmap *idmap,
+-                             struct dentry *dentry);
++                              const struct path *path);
++int ksmbd_vfs_remove_sd_xattrs(struct mnt_idmap *idmap, const struct path *path);
+ int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn,
+                          struct mnt_idmap *idmap,
+-                         struct dentry *dentry,
++                         const struct path *path,
+                          struct smb_ntsd *pntsd, int len);
+ int ksmbd_vfs_get_sd_xattr(struct ksmbd_conn *conn,
+                          struct mnt_idmap *idmap,
+                          struct dentry *dentry,
+                          struct smb_ntsd **pntsd);
+ int ksmbd_vfs_set_dos_attrib_xattr(struct mnt_idmap *idmap,
+-                                 struct dentry *dentry,
++                                 const struct path *path,
+                                  struct xattr_dos_attrib *da);
+ int ksmbd_vfs_get_dos_attrib_xattr(struct mnt_idmap *idmap,
+                                  struct dentry *dentry,
+                                  struct xattr_dos_attrib *da);
+ int ksmbd_vfs_set_init_posix_acl(struct mnt_idmap *idmap,
+-                               struct dentry *dentry);
++                               struct path *path);
+ int ksmbd_vfs_inherit_posix_acl(struct mnt_idmap *idmap,
+-                              struct dentry *dentry,
++                              struct path *path,
+                               struct inode *parent_inode);
+ #endif /* __KSMBD_VFS_H__ */
+diff --git a/fs/ksmbd/vfs_cache.c b/fs/ksmbd/vfs_cache.c
+index 2d0138e72d783..f41f8d6108ce9 100644
+--- a/fs/ksmbd/vfs_cache.c
++++ b/fs/ksmbd/vfs_cache.c
+@@ -252,7 +252,7 @@ static void __ksmbd_inode_close(struct ksmbd_file *fp)
+       if (ksmbd_stream_fd(fp) && (ci->m_flags & S_DEL_ON_CLS_STREAM)) {
+               ci->m_flags &= ~S_DEL_ON_CLS_STREAM;
+               err = ksmbd_vfs_remove_xattr(file_mnt_idmap(filp),
+-                                           filp->f_path.dentry,
++                                           &filp->f_path,
+                                            fp->stream.name);
+               if (err)
+                       pr_err("remove xattr failed : %s\n",
+-- 
+2.39.2
+
diff --git a/queue-6.3/ksmbd-fix-racy-issue-from-using-d_parent-and-d_name.patch b/queue-6.3/ksmbd-fix-racy-issue-from-using-d_parent-and-d_name.patch
new file mode 100644 (file)
index 0000000..8febc3f
--- /dev/null
@@ -0,0 +1,1194 @@
+From a4db2bbcd3a9961d43bf9a00537988de8082f5fd Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 21 Apr 2023 16:09:01 +0900
+Subject: ksmbd: fix racy issue from using ->d_parent and ->d_name
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit 74d7970febf7e9005375aeda0df821d2edffc9f7 ]
+
+Al pointed out that ksmbd has racy issue from using ->d_parent and ->d_name
+in ksmbd_vfs_unlink and smb2_vfs_rename(). and use new lock_rename_child()
+to lock stable parent while underlying rename racy.
+Introduce vfs_path_parent_lookup helper to avoid out of share access and
+export vfs functions like the following ones to use
+vfs_path_parent_lookup().
+ - rename __lookup_hash() to lookup_one_qstr_excl().
+ - export lookup_one_qstr_excl().
+ - export getname_kernel() and putname().
+
+vfs_path_parent_lookup() is used for parent lookup of destination file
+using absolute pathname given from FILE_RENAME_INFORMATION request.
+
+Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Stable-dep-of: 40b268d384a2 ("ksmbd: add mnt_want_write to ksmbd vfs functions")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/ksmbd/smb2pdu.c    | 147 ++++----------
+ fs/ksmbd/vfs.c        | 435 ++++++++++++++++++------------------------
+ fs/ksmbd/vfs.h        |  19 +-
+ fs/ksmbd/vfs_cache.c  |   5 +-
+ fs/namei.c            |  57 ++++--
+ include/linux/namei.h |   6 +
+ 6 files changed, 283 insertions(+), 386 deletions(-)
+
+diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c
+index 36843bef05c5f..06a335356e373 100644
+--- a/fs/ksmbd/smb2pdu.c
++++ b/fs/ksmbd/smb2pdu.c
+@@ -2515,7 +2515,7 @@ static int smb2_creat(struct ksmbd_work *work, struct path *path, char *name,
+                       return rc;
+       }
+-      rc = ksmbd_vfs_kern_path(work, name, 0, path, 0);
++      rc = ksmbd_vfs_kern_path_locked(work, name, 0, path, 0);
+       if (rc) {
+               pr_err("cannot get linux path (%s), err = %d\n",
+                      name, rc);
+@@ -2806,8 +2806,10 @@ int smb2_open(struct ksmbd_work *work)
+               goto err_out1;
+       }
+-      rc = ksmbd_vfs_kern_path(work, name, LOOKUP_NO_SYMLINKS, &path, 1);
++      rc = ksmbd_vfs_kern_path_locked(work, name, LOOKUP_NO_SYMLINKS, &path, 1);
+       if (!rc) {
++              file_present = true;
++
+               if (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE) {
+                       /*
+                        * If file exists with under flags, return access
+@@ -2816,7 +2818,6 @@ int smb2_open(struct ksmbd_work *work)
+                       if (req->CreateDisposition == FILE_OVERWRITE_IF_LE ||
+                           req->CreateDisposition == FILE_OPEN_IF_LE) {
+                               rc = -EACCES;
+-                              path_put(&path);
+                               goto err_out;
+                       }
+@@ -2824,26 +2825,23 @@ int smb2_open(struct ksmbd_work *work)
+                               ksmbd_debug(SMB,
+                                           "User does not have write permission\n");
+                               rc = -EACCES;
+-                              path_put(&path);
+                               goto err_out;
+                       }
+               } else if (d_is_symlink(path.dentry)) {
+                       rc = -EACCES;
+-                      path_put(&path);
+                       goto err_out;
+               }
+-      }
+-      if (rc) {
++              file_present = true;
++              idmap = mnt_idmap(path.mnt);
++      } else {
+               if (rc != -ENOENT)
+                       goto err_out;
+               ksmbd_debug(SMB, "can not get linux path for %s, rc = %d\n",
+                           name, rc);
+               rc = 0;
+-      } else {
+-              file_present = true;
+-              idmap = mnt_idmap(path.mnt);
+       }
++
+       if (stream_name) {
+               if (req->CreateOptions & FILE_DIRECTORY_FILE_LE) {
+                       if (s_type == DATA_STREAM) {
+@@ -2971,8 +2969,9 @@ int smb2_open(struct ksmbd_work *work)
+                       if ((daccess & FILE_DELETE_LE) ||
+                           (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE)) {
+-                              rc = ksmbd_vfs_may_delete(idmap,
+-                                                        path.dentry);
++                              rc = inode_permission(idmap,
++                                                    d_inode(path.dentry->d_parent),
++                                                    MAY_EXEC | MAY_WRITE);
+                               if (rc)
+                                       goto err_out;
+                       }
+@@ -3343,10 +3342,13 @@ int smb2_open(struct ksmbd_work *work)
+       }
+ err_out:
+-      if (file_present || created)
+-              path_put(&path);
++      if (file_present || created) {
++              inode_unlock(d_inode(path.dentry->d_parent));
++              dput(path.dentry);
++      }
+       ksmbd_revert_fsids(work);
+ err_out1:
++
+       if (rc) {
+               if (rc == -EINVAL)
+                       rsp->hdr.Status = STATUS_INVALID_PARAMETER;
+@@ -5485,44 +5487,19 @@ int smb2_echo(struct ksmbd_work *work)
+ static int smb2_rename(struct ksmbd_work *work,
+                      struct ksmbd_file *fp,
+-                     struct mnt_idmap *idmap,
+                      struct smb2_file_rename_info *file_info,
+                      struct nls_table *local_nls)
+ {
+       struct ksmbd_share_config *share = fp->tcon->share_conf;
+-      char *new_name = NULL, *abs_oldname = NULL, *old_name = NULL;
+-      char *pathname = NULL;
+-      struct path path;
+-      bool file_present = true;
+-      int rc;
++      char *new_name = NULL;
++      int rc, flags = 0;
+       ksmbd_debug(SMB, "setting FILE_RENAME_INFO\n");
+-      pathname = kmalloc(PATH_MAX, GFP_KERNEL);
+-      if (!pathname)
+-              return -ENOMEM;
+-
+-      abs_oldname = file_path(fp->filp, pathname, PATH_MAX);
+-      if (IS_ERR(abs_oldname)) {
+-              rc = -EINVAL;
+-              goto out;
+-      }
+-      old_name = strrchr(abs_oldname, '/');
+-      if (old_name && old_name[1] != '\0') {
+-              old_name++;
+-      } else {
+-              ksmbd_debug(SMB, "can't get last component in path %s\n",
+-                          abs_oldname);
+-              rc = -ENOENT;
+-              goto out;
+-      }
+-
+       new_name = smb2_get_name(file_info->FileName,
+                                le32_to_cpu(file_info->FileNameLength),
+                                local_nls);
+-      if (IS_ERR(new_name)) {
+-              rc = PTR_ERR(new_name);
+-              goto out;
+-      }
++      if (IS_ERR(new_name))
++              return PTR_ERR(new_name);
+       if (strchr(new_name, ':')) {
+               int s_type;
+@@ -5548,7 +5525,7 @@ static int smb2_rename(struct ksmbd_work *work,
+               if (rc)
+                       goto out;
+-              rc = ksmbd_vfs_setxattr(idmap,
++              rc = ksmbd_vfs_setxattr(file_mnt_idmap(fp->filp),
+                                       fp->filp->f_path.dentry,
+                                       xattr_stream_name,
+                                       NULL, 0, 0);
+@@ -5563,47 +5540,18 @@ static int smb2_rename(struct ksmbd_work *work,
+       }
+       ksmbd_debug(SMB, "new name %s\n", new_name);
+-      rc = ksmbd_vfs_kern_path(work, new_name, LOOKUP_NO_SYMLINKS, &path, 1);
+-      if (rc) {
+-              if (rc != -ENOENT)
+-                      goto out;
+-              file_present = false;
+-      } else {
+-              path_put(&path);
+-      }
+-
+       if (ksmbd_share_veto_filename(share, new_name)) {
+               rc = -ENOENT;
+               ksmbd_debug(SMB, "Can't rename vetoed file: %s\n", new_name);
+               goto out;
+       }
+-      if (file_info->ReplaceIfExists) {
+-              if (file_present) {
+-                      rc = ksmbd_vfs_remove_file(work, new_name);
+-                      if (rc) {
+-                              if (rc != -ENOTEMPTY)
+-                                      rc = -EINVAL;
+-                              ksmbd_debug(SMB, "cannot delete %s, rc %d\n",
+-                                          new_name, rc);
+-                              goto out;
+-                      }
+-              }
+-      } else {
+-              if (file_present &&
+-                  strncmp(old_name, path.dentry->d_name.name, strlen(old_name))) {
+-                      rc = -EEXIST;
+-                      ksmbd_debug(SMB,
+-                                  "cannot rename already existing file\n");
+-                      goto out;
+-              }
+-      }
++      if (!file_info->ReplaceIfExists)
++              flags = RENAME_NOREPLACE;
+-      rc = ksmbd_vfs_fp_rename(work, fp, new_name);
++      rc = ksmbd_vfs_rename(work, &fp->filp->f_path, new_name, flags);
+ out:
+-      kfree(pathname);
+-      if (!IS_ERR(new_name))
+-              kfree(new_name);
++      kfree(new_name);
+       return rc;
+ }
+@@ -5643,18 +5591,17 @@ static int smb2_create_link(struct ksmbd_work *work,
+       }
+       ksmbd_debug(SMB, "target name is %s\n", target_name);
+-      rc = ksmbd_vfs_kern_path(work, link_name, LOOKUP_NO_SYMLINKS, &path, 0);
++      rc = ksmbd_vfs_kern_path_locked(work, link_name, LOOKUP_NO_SYMLINKS,
++                                      &path, 0);
+       if (rc) {
+               if (rc != -ENOENT)
+                       goto out;
+               file_present = false;
+-      } else {
+-              path_put(&path);
+       }
+       if (file_info->ReplaceIfExists) {
+               if (file_present) {
+-                      rc = ksmbd_vfs_remove_file(work, link_name);
++                      rc = ksmbd_vfs_remove_file(work, &path);
+                       if (rc) {
+                               rc = -EINVAL;
+                               ksmbd_debug(SMB, "cannot delete %s\n",
+@@ -5674,6 +5621,10 @@ static int smb2_create_link(struct ksmbd_work *work,
+       if (rc)
+               rc = -EINVAL;
+ out:
++      if (file_present) {
++              inode_unlock(d_inode(path.dentry->d_parent));
++              path_put(&path);
++      }
+       if (!IS_ERR(link_name))
+               kfree(link_name);
+       kfree(pathname);
+@@ -5851,12 +5802,6 @@ static int set_rename_info(struct ksmbd_work *work, struct ksmbd_file *fp,
+                          struct smb2_file_rename_info *rename_info,
+                          unsigned int buf_len)
+ {
+-      struct mnt_idmap *idmap;
+-      struct ksmbd_file *parent_fp;
+-      struct dentry *parent;
+-      struct dentry *dentry = fp->filp->f_path.dentry;
+-      int ret;
+-
+       if (!(fp->daccess & FILE_DELETE_LE)) {
+               pr_err("no right to delete : 0x%x\n", fp->daccess);
+               return -EACCES;
+@@ -5866,32 +5811,10 @@ static int set_rename_info(struct ksmbd_work *work, struct ksmbd_file *fp,
+                       le32_to_cpu(rename_info->FileNameLength))
+               return -EINVAL;
+-      idmap = file_mnt_idmap(fp->filp);
+-      if (ksmbd_stream_fd(fp))
+-              goto next;
+-
+-      parent = dget_parent(dentry);
+-      ret = ksmbd_vfs_lock_parent(idmap, parent, dentry);
+-      if (ret) {
+-              dput(parent);
+-              return ret;
+-      }
+-
+-      parent_fp = ksmbd_lookup_fd_inode(d_inode(parent));
+-      inode_unlock(d_inode(parent));
+-      dput(parent);
++      if (!le32_to_cpu(rename_info->FileNameLength))
++              return -EINVAL;
+-      if (parent_fp) {
+-              if (parent_fp->daccess & FILE_DELETE_LE) {
+-                      pr_err("parent dir is opened with delete access\n");
+-                      ksmbd_fd_put(work, parent_fp);
+-                      return -ESHARE;
+-              }
+-              ksmbd_fd_put(work, parent_fp);
+-      }
+-next:
+-      return smb2_rename(work, fp, idmap, rename_info,
+-                         work->conn->local_nls);
++      return smb2_rename(work, fp, rename_info, work->conn->local_nls);
+ }
+ static int set_file_disposition_info(struct ksmbd_file *fp,
+diff --git a/fs/ksmbd/vfs.c b/fs/ksmbd/vfs.c
+index 1ad97df1dfb6f..448c442ad3f49 100644
+--- a/fs/ksmbd/vfs.c
++++ b/fs/ksmbd/vfs.c
+@@ -18,6 +18,7 @@
+ #include <linux/vmalloc.h>
+ #include <linux/sched/xacct.h>
+ #include <linux/crc32c.h>
++#include <linux/namei.h>
+ #include "glob.h"
+ #include "oplock.h"
+@@ -35,19 +36,6 @@
+ #include "mgmt/user_session.h"
+ #include "mgmt/user_config.h"
+-static char *extract_last_component(char *path)
+-{
+-      char *p = strrchr(path, '/');
+-
+-      if (p && p[1] != '\0') {
+-              *p = '\0';
+-              p++;
+-      } else {
+-              p = NULL;
+-      }
+-      return p;
+-}
+-
+ static void ksmbd_vfs_inherit_owner(struct ksmbd_work *work,
+                                   struct inode *parent_inode,
+                                   struct inode *inode)
+@@ -61,65 +49,77 @@ static void ksmbd_vfs_inherit_owner(struct ksmbd_work *work,
+ /**
+  * ksmbd_vfs_lock_parent() - lock parent dentry if it is stable
+- *
+- * the parent dentry got by dget_parent or @parent could be
+- * unstable, we try to lock a parent inode and lookup the
+- * child dentry again.
+- *
+- * the reference count of @parent isn't incremented.
+  */
+-int ksmbd_vfs_lock_parent(struct mnt_idmap *idmap, struct dentry *parent,
+-                        struct dentry *child)
++int ksmbd_vfs_lock_parent(struct dentry *parent, struct dentry *child)
+ {
+-      struct dentry *dentry;
+-      int ret = 0;
+-
+       inode_lock_nested(d_inode(parent), I_MUTEX_PARENT);
+-      dentry = lookup_one(idmap, child->d_name.name, parent,
+-                          child->d_name.len);
+-      if (IS_ERR(dentry)) {
+-              ret = PTR_ERR(dentry);
+-              goto out_err;
+-      }
+-
+-      if (dentry != child) {
+-              ret = -ESTALE;
+-              dput(dentry);
+-              goto out_err;
++      if (child->d_parent != parent) {
++              inode_unlock(d_inode(parent));
++              return -ENOENT;
+       }
+-      dput(dentry);
+       return 0;
+-out_err:
+-      inode_unlock(d_inode(parent));
+-      return ret;
+ }
+-int ksmbd_vfs_may_delete(struct mnt_idmap *idmap,
+-                       struct dentry *dentry)
++static int ksmbd_vfs_path_lookup_locked(struct ksmbd_share_config *share_conf,
++                                      char *pathname, unsigned int flags,
++                                      struct path *path)
+ {
+-      struct dentry *parent;
+-      int ret;
++      struct qstr last;
++      struct filename *filename;
++      struct path *root_share_path = &share_conf->vfs_path;
++      int err, type;
++      struct path parent_path;
++      struct dentry *d;
++
++      if (pathname[0] == '\0') {
++              pathname = share_conf->path;
++              root_share_path = NULL;
++      } else {
++              flags |= LOOKUP_BENEATH;
++      }
+-      parent = dget_parent(dentry);
+-      ret = ksmbd_vfs_lock_parent(idmap, parent, dentry);
+-      if (ret) {
+-              dput(parent);
+-              return ret;
++      filename = getname_kernel(pathname);
++      if (IS_ERR(filename))
++              return PTR_ERR(filename);
++
++      err = vfs_path_parent_lookup(filename, flags,
++                                   &parent_path, &last, &type,
++                                   root_share_path);
++      putname(filename);
++      if (err)
++              return err;
++
++      if (unlikely(type != LAST_NORM)) {
++              path_put(&parent_path);
++              return -ENOENT;
+       }
+-      ret = inode_permission(idmap, d_inode(parent),
+-                             MAY_EXEC | MAY_WRITE);
++      inode_lock_nested(parent_path.dentry->d_inode, I_MUTEX_PARENT);
++      d = lookup_one_qstr_excl(&last, parent_path.dentry, 0);
++      if (IS_ERR(d))
++              goto err_out;
+-      inode_unlock(d_inode(parent));
+-      dput(parent);
+-      return ret;
++      if (d_is_negative(d)) {
++              dput(d);
++              goto err_out;
++      }
++
++      path->dentry = d;
++      path->mnt = share_conf->vfs_path.mnt;
++      path_put(&parent_path);
++
++      return 0;
++
++err_out:
++      inode_unlock(parent_path.dentry->d_inode);
++      path_put(&parent_path);
++      return -ENOENT;
+ }
+ int ksmbd_vfs_query_maximal_access(struct mnt_idmap *idmap,
+                                  struct dentry *dentry, __le32 *daccess)
+ {
+-      struct dentry *parent;
+       int ret = 0;
+       *daccess = cpu_to_le32(FILE_READ_ATTRIBUTES | READ_CONTROL);
+@@ -136,18 +136,9 @@ int ksmbd_vfs_query_maximal_access(struct mnt_idmap *idmap,
+       if (!inode_permission(idmap, d_inode(dentry), MAY_OPEN | MAY_EXEC))
+               *daccess |= FILE_EXECUTE_LE;
+-      parent = dget_parent(dentry);
+-      ret = ksmbd_vfs_lock_parent(idmap, parent, dentry);
+-      if (ret) {
+-              dput(parent);
+-              return ret;
+-      }
+-
+-      if (!inode_permission(idmap, d_inode(parent), MAY_EXEC | MAY_WRITE))
++      if (!inode_permission(idmap, d_inode(dentry->d_parent), MAY_EXEC | MAY_WRITE))
+               *daccess |= FILE_DELETE_LE;
+-      inode_unlock(d_inode(parent));
+-      dput(parent);
+       return ret;
+ }
+@@ -580,54 +571,32 @@ int ksmbd_vfs_fsync(struct ksmbd_work *work, u64 fid, u64 p_id)
+  *
+  * Return:    0 on success, otherwise error
+  */
+-int ksmbd_vfs_remove_file(struct ksmbd_work *work, char *name)
++int ksmbd_vfs_remove_file(struct ksmbd_work *work, const struct path *path)
+ {
+       struct mnt_idmap *idmap;
+-      struct path path;
+-      struct dentry *parent;
++      struct dentry *parent = path->dentry->d_parent;
+       int err;
+       if (ksmbd_override_fsids(work))
+               return -ENOMEM;
+-      err = ksmbd_vfs_kern_path(work, name, LOOKUP_NO_SYMLINKS, &path, false);
+-      if (err) {
+-              ksmbd_debug(VFS, "can't get %s, err %d\n", name, err);
+-              ksmbd_revert_fsids(work);
+-              return err;
+-      }
+-
+-      idmap = mnt_idmap(path.mnt);
+-      parent = dget_parent(path.dentry);
+-      err = ksmbd_vfs_lock_parent(idmap, parent, path.dentry);
+-      if (err) {
+-              dput(parent);
+-              path_put(&path);
+-              ksmbd_revert_fsids(work);
+-              return err;
+-      }
+-
+-      if (!d_inode(path.dentry)->i_nlink) {
++      if (!d_inode(path->dentry)->i_nlink) {
+               err = -ENOENT;
+               goto out_err;
+       }
+-      if (S_ISDIR(d_inode(path.dentry)->i_mode)) {
+-              err = vfs_rmdir(idmap, d_inode(parent), path.dentry);
++      idmap = mnt_idmap(path->mnt);
++      if (S_ISDIR(d_inode(path->dentry)->i_mode)) {
++              err = vfs_rmdir(idmap, d_inode(parent), path->dentry);
+               if (err && err != -ENOTEMPTY)
+-                      ksmbd_debug(VFS, "%s: rmdir failed, err %d\n", name,
+-                                  err);
++                      ksmbd_debug(VFS, "rmdir failed, err %d\n", err);
+       } else {
+-              err = vfs_unlink(idmap, d_inode(parent), path.dentry, NULL);
++              err = vfs_unlink(idmap, d_inode(parent), path->dentry, NULL);
+               if (err)
+-                      ksmbd_debug(VFS, "%s: unlink failed, err %d\n", name,
+-                                  err);
++                      ksmbd_debug(VFS, "unlink failed, err %d\n", err);
+       }
+ out_err:
+-      inode_unlock(d_inode(parent));
+-      dput(parent);
+-      path_put(&path);
+       ksmbd_revert_fsids(work);
+       return err;
+ }
+@@ -686,149 +655,114 @@ int ksmbd_vfs_link(struct ksmbd_work *work, const char *oldname,
+       return err;
+ }
+-static int ksmbd_validate_entry_in_use(struct dentry *src_dent)
++int ksmbd_vfs_rename(struct ksmbd_work *work, const struct path *old_path,
++                   char *newname, int flags)
+ {
+-      struct dentry *dst_dent;
+-
+-      spin_lock(&src_dent->d_lock);
+-      list_for_each_entry(dst_dent, &src_dent->d_subdirs, d_child) {
+-              struct ksmbd_file *child_fp;
++      struct dentry *old_parent, *new_dentry, *trap;
++      struct dentry *old_child = old_path->dentry;
++      struct path new_path;
++      struct qstr new_last;
++      struct renamedata rd;
++      struct filename *to;
++      struct ksmbd_share_config *share_conf = work->tcon->share_conf;
++      struct ksmbd_file *parent_fp;
++      int new_type;
++      int err, lookup_flags = LOOKUP_NO_SYMLINKS;
+-              if (d_really_is_negative(dst_dent))
+-                      continue;
++      if (ksmbd_override_fsids(work))
++              return -ENOMEM;
+-              child_fp = ksmbd_lookup_fd_inode(d_inode(dst_dent));
+-              if (child_fp) {
+-                      spin_unlock(&src_dent->d_lock);
+-                      ksmbd_debug(VFS, "Forbid rename, sub file/dir is in use\n");
+-                      return -EACCES;
+-              }
++      to = getname_kernel(newname);
++      if (IS_ERR(to)) {
++              err = PTR_ERR(to);
++              goto revert_fsids;
+       }
+-      spin_unlock(&src_dent->d_lock);
+-      return 0;
+-}
++retry:
++      err = vfs_path_parent_lookup(to, lookup_flags | LOOKUP_BENEATH,
++                                   &new_path, &new_last, &new_type,
++                                   &share_conf->vfs_path);
++      if (err)
++              goto out1;
+-static int __ksmbd_vfs_rename(struct ksmbd_work *work,
+-                            struct mnt_idmap *src_idmap,
+-                            struct dentry *src_dent_parent,
+-                            struct dentry *src_dent,
+-                            struct mnt_idmap *dst_idmap,
+-                            struct dentry *dst_dent_parent,
+-                            struct dentry *trap_dent,
+-                            char *dst_name)
+-{
+-      struct dentry *dst_dent;
+-      int err;
++      if (old_path->mnt != new_path.mnt) {
++              err = -EXDEV;
++              goto out2;
++      }
+-      if (!work->tcon->posix_extensions) {
+-              err = ksmbd_validate_entry_in_use(src_dent);
+-              if (err)
+-                      return err;
++      trap = lock_rename_child(old_child, new_path.dentry);
++
++      old_parent = dget(old_child->d_parent);
++      if (d_unhashed(old_child)) {
++              err = -EINVAL;
++              goto out3;
+       }
+-      if (d_really_is_negative(src_dent_parent))
+-              return -ENOENT;
+-      if (d_really_is_negative(dst_dent_parent))
+-              return -ENOENT;
+-      if (d_really_is_negative(src_dent))
+-              return -ENOENT;
+-      if (src_dent == trap_dent)
+-              return -EINVAL;
++      parent_fp = ksmbd_lookup_fd_inode(d_inode(old_child->d_parent));
++      if (parent_fp) {
++              if (parent_fp->daccess & FILE_DELETE_LE) {
++                      pr_err("parent dir is opened with delete access\n");
++                      err = -ESHARE;
++                      ksmbd_fd_put(work, parent_fp);
++                      goto out3;
++              }
++              ksmbd_fd_put(work, parent_fp);
++      }
+-      if (ksmbd_override_fsids(work))
+-              return -ENOMEM;
++      new_dentry = lookup_one_qstr_excl(&new_last, new_path.dentry,
++                                        lookup_flags | LOOKUP_RENAME_TARGET);
++      if (IS_ERR(new_dentry)) {
++              err = PTR_ERR(new_dentry);
++              goto out3;
++      }
+-      dst_dent = lookup_one(dst_idmap, dst_name,
+-                            dst_dent_parent, strlen(dst_name));
+-      err = PTR_ERR(dst_dent);
+-      if (IS_ERR(dst_dent)) {
+-              pr_err("lookup failed %s [%d]\n", dst_name, err);
+-              goto out;
++      if (d_is_symlink(new_dentry)) {
++              err = -EACCES;
++              goto out4;
+       }
+-      err = -ENOTEMPTY;
+-      if (dst_dent != trap_dent && !d_really_is_positive(dst_dent)) {
+-              struct renamedata rd = {
+-                      .old_mnt_idmap  = src_idmap,
+-                      .old_dir        = d_inode(src_dent_parent),
+-                      .old_dentry     = src_dent,
+-                      .new_mnt_idmap  = dst_idmap,
+-                      .new_dir        = d_inode(dst_dent_parent),
+-                      .new_dentry     = dst_dent,
+-              };
+-              err = vfs_rename(&rd);
++      if ((flags & RENAME_NOREPLACE) && d_is_positive(new_dentry)) {
++              err = -EEXIST;
++              goto out4;
+       }
+-      if (err)
+-              pr_err("vfs_rename failed err %d\n", err);
+-      if (dst_dent)
+-              dput(dst_dent);
+-out:
+-      ksmbd_revert_fsids(work);
+-      return err;
+-}
+-int ksmbd_vfs_fp_rename(struct ksmbd_work *work, struct ksmbd_file *fp,
+-                      char *newname)
+-{
+-      struct mnt_idmap *idmap;
+-      struct path dst_path;
+-      struct dentry *src_dent_parent, *dst_dent_parent;
+-      struct dentry *src_dent, *trap_dent, *src_child;
+-      char *dst_name;
+-      int err;
++      if (old_child == trap) {
++              err = -EINVAL;
++              goto out4;
++      }
+-      dst_name = extract_last_component(newname);
+-      if (!dst_name) {
+-              dst_name = newname;
+-              newname = "";
++      if (new_dentry == trap) {
++              err = -ENOTEMPTY;
++              goto out4;
+       }
+-      src_dent_parent = dget_parent(fp->filp->f_path.dentry);
+-      src_dent = fp->filp->f_path.dentry;
++      rd.old_mnt_idmap        = mnt_idmap(old_path->mnt),
++      rd.old_dir              = d_inode(old_parent),
++      rd.old_dentry           = old_child,
++      rd.new_mnt_idmap        = mnt_idmap(new_path.mnt),
++      rd.new_dir              = new_path.dentry->d_inode,
++      rd.new_dentry           = new_dentry,
++      rd.flags                = flags,
++      err = vfs_rename(&rd);
++      if (err)
++              ksmbd_debug(VFS, "vfs_rename failed err %d\n", err);
+-      err = ksmbd_vfs_kern_path(work, newname,
+-                                LOOKUP_NO_SYMLINKS | LOOKUP_DIRECTORY,
+-                                &dst_path, false);
+-      if (err) {
+-              ksmbd_debug(VFS, "Cannot get path for %s [%d]\n", newname, err);
+-              goto out;
++out4:
++      dput(new_dentry);
++out3:
++      dput(old_parent);
++      unlock_rename(old_parent, new_path.dentry);
++out2:
++      path_put(&new_path);
++
++      if (retry_estale(err, lookup_flags)) {
++              lookup_flags |= LOOKUP_REVAL;
++              goto retry;
+       }
+-      dst_dent_parent = dst_path.dentry;
+-
+-      trap_dent = lock_rename(src_dent_parent, dst_dent_parent);
+-      dget(src_dent);
+-      dget(dst_dent_parent);
+-      idmap = file_mnt_idmap(fp->filp);
+-      src_child = lookup_one(idmap, src_dent->d_name.name, src_dent_parent,
+-                             src_dent->d_name.len);
+-      if (IS_ERR(src_child)) {
+-              err = PTR_ERR(src_child);
+-              goto out_lock;
+-      }
+-
+-      if (src_child != src_dent) {
+-              err = -ESTALE;
+-              dput(src_child);
+-              goto out_lock;
+-      }
+-      dput(src_child);
+-
+-      err = __ksmbd_vfs_rename(work,
+-                               idmap,
+-                               src_dent_parent,
+-                               src_dent,
+-                               mnt_idmap(dst_path.mnt),
+-                               dst_dent_parent,
+-                               trap_dent,
+-                               dst_name);
+-out_lock:
+-      dput(src_dent);
+-      dput(dst_dent_parent);
+-      unlock_rename(src_dent_parent, dst_dent_parent);
+-      path_put(&dst_path);
+-out:
+-      dput(src_dent_parent);
++out1:
++      putname(to);
++revert_fsids:
++      ksmbd_revert_fsids(work);
+       return err;
+ }
+@@ -1079,14 +1013,16 @@ int ksmbd_vfs_remove_xattr(struct mnt_idmap *idmap,
+       return vfs_removexattr(idmap, dentry, attr_name);
+ }
+-int ksmbd_vfs_unlink(struct mnt_idmap *idmap,
+-                   struct dentry *dir, struct dentry *dentry)
++int ksmbd_vfs_unlink(struct file *filp)
+ {
+       int err = 0;
++      struct dentry *dir, *dentry = filp->f_path.dentry;
++      struct mnt_idmap *idmap = file_mnt_idmap(filp);
+-      err = ksmbd_vfs_lock_parent(idmap, dir, dentry);
++      dir = dget_parent(dentry);
++      err = ksmbd_vfs_lock_parent(dir, dentry);
+       if (err)
+-              return err;
++              goto out;
+       dget(dentry);
+       if (S_ISDIR(d_inode(dentry)->i_mode))
+@@ -1098,6 +1034,8 @@ int ksmbd_vfs_unlink(struct mnt_idmap *idmap,
+       inode_unlock(d_inode(dir));
+       if (err)
+               ksmbd_debug(VFS, "failed to delete, err %d\n", err);
++out:
++      dput(dir);
+       return err;
+ }
+@@ -1200,7 +1138,7 @@ static int ksmbd_vfs_lookup_in_dir(const struct path *dir, char *name,
+ }
+ /**
+- * ksmbd_vfs_kern_path() - lookup a file and get path info
++ * ksmbd_vfs_kern_path_locked() - lookup a file and get path info
+  * @name:     file path that is relative to share
+  * @flags:    lookup flags
+  * @path:     if lookup succeed, return path info
+@@ -1208,24 +1146,20 @@ static int ksmbd_vfs_lookup_in_dir(const struct path *dir, char *name,
+  *
+  * Return:    0 on success, otherwise error
+  */
+-int ksmbd_vfs_kern_path(struct ksmbd_work *work, char *name,
+-                      unsigned int flags, struct path *path, bool caseless)
++int ksmbd_vfs_kern_path_locked(struct ksmbd_work *work, char *name,
++                             unsigned int flags, struct path *path,
++                             bool caseless)
+ {
+       struct ksmbd_share_config *share_conf = work->tcon->share_conf;
+       int err;
++      struct path parent_path;
+-      flags |= LOOKUP_BENEATH;
+-      err = vfs_path_lookup(share_conf->vfs_path.dentry,
+-                            share_conf->vfs_path.mnt,
+-                            name,
+-                            flags,
+-                            path);
++      err = ksmbd_vfs_path_lookup_locked(share_conf, name, flags, path);
+       if (!err)
+-              return 0;
++              return err;
+       if (caseless) {
+               char *filepath;
+-              struct path parent;
+               size_t path_len, remain_len;
+               filepath = kstrdup(name, GFP_KERNEL);
+@@ -1235,10 +1169,10 @@ int ksmbd_vfs_kern_path(struct ksmbd_work *work, char *name,
+               path_len = strlen(filepath);
+               remain_len = path_len;
+-              parent = share_conf->vfs_path;
+-              path_get(&parent);
++              parent_path = share_conf->vfs_path;
++              path_get(&parent_path);
+-              while (d_can_lookup(parent.dentry)) {
++              while (d_can_lookup(parent_path.dentry)) {
+                       char *filename = filepath + path_len - remain_len;
+                       char *next = strchrnul(filename, '/');
+                       size_t filename_len = next - filename;
+@@ -1247,12 +1181,11 @@ int ksmbd_vfs_kern_path(struct ksmbd_work *work, char *name,
+                       if (filename_len == 0)
+                               break;
+-                      err = ksmbd_vfs_lookup_in_dir(&parent, filename,
++                      err = ksmbd_vfs_lookup_in_dir(&parent_path, filename,
+                                                     filename_len,
+                                                     work->conn->um);
+-                      path_put(&parent);
+                       if (err)
+-                              goto out;
++                              goto out2;
+                       next[0] = '\0';
+@@ -1260,23 +1193,31 @@ int ksmbd_vfs_kern_path(struct ksmbd_work *work, char *name,
+                                             share_conf->vfs_path.mnt,
+                                             filepath,
+                                             flags,
+-                                            &parent);
++                                            path);
+                       if (err)
+-                              goto out;
+-                      else if (is_last) {
+-                              *path = parent;
+-                              goto out;
+-                      }
++                              goto out2;
++                      else if (is_last)
++                              goto out1;
++                      path_put(&parent_path);
++                      parent_path = *path;
+                       next[0] = '/';
+                       remain_len -= filename_len + 1;
+               }
+-              path_put(&parent);
+               err = -EINVAL;
+-out:
++out2:
++              path_put(&parent_path);
++out1:
+               kfree(filepath);
+       }
++
++      if (!err) {
++              err = ksmbd_vfs_lock_parent(parent_path.dentry, path->dentry);
++              if (err)
++                      dput(path->dentry);
++              path_put(&parent_path);
++      }
+       return err;
+ }
+diff --git a/fs/ksmbd/vfs.h b/fs/ksmbd/vfs.h
+index 9d676ab0cd25b..a4ae89f3230de 100644
+--- a/fs/ksmbd/vfs.h
++++ b/fs/ksmbd/vfs.h
+@@ -71,9 +71,7 @@ struct ksmbd_kstat {
+       __le32                  file_attributes;
+ };
+-int ksmbd_vfs_lock_parent(struct mnt_idmap *idmap, struct dentry *parent,
+-                        struct dentry *child);
+-int ksmbd_vfs_may_delete(struct mnt_idmap *idmap, struct dentry *dentry);
++int ksmbd_vfs_lock_parent(struct dentry *parent, struct dentry *child);
+ int ksmbd_vfs_query_maximal_access(struct mnt_idmap *idmap,
+                                  struct dentry *dentry, __le32 *daccess);
+ int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode);
+@@ -84,12 +82,12 @@ int ksmbd_vfs_write(struct ksmbd_work *work, struct ksmbd_file *fp,
+                   char *buf, size_t count, loff_t *pos, bool sync,
+                   ssize_t *written);
+ int ksmbd_vfs_fsync(struct ksmbd_work *work, u64 fid, u64 p_id);
+-int ksmbd_vfs_remove_file(struct ksmbd_work *work, char *name);
++int ksmbd_vfs_remove_file(struct ksmbd_work *work, const struct path *path);
+ int ksmbd_vfs_link(struct ksmbd_work *work,
+                  const char *oldname, const char *newname);
+ int ksmbd_vfs_getattr(const struct path *path, struct kstat *stat);
+-int ksmbd_vfs_fp_rename(struct ksmbd_work *work, struct ksmbd_file *fp,
+-                      char *newname);
++int ksmbd_vfs_rename(struct ksmbd_work *work, const struct path *old_path,
++                   char *newname, int flags);
+ int ksmbd_vfs_truncate(struct ksmbd_work *work,
+                      struct ksmbd_file *fp, loff_t size);
+ struct srv_copychunk;
+@@ -116,9 +114,9 @@ int ksmbd_vfs_xattr_stream_name(char *stream_name, char **xattr_stream_name,
+                               size_t *xattr_stream_name_size, int s_type);
+ int ksmbd_vfs_remove_xattr(struct mnt_idmap *idmap,
+                          struct dentry *dentry, char *attr_name);
+-int ksmbd_vfs_kern_path(struct ksmbd_work *work,
+-                      char *name, unsigned int flags, struct path *path,
+-                      bool caseless);
++int ksmbd_vfs_kern_path_locked(struct ksmbd_work *work, char *name,
++                             unsigned int flags, struct path *path,
++                             bool caseless);
+ struct dentry *ksmbd_vfs_kern_path_create(struct ksmbd_work *work,
+                                         const char *name,
+                                         unsigned int flags,
+@@ -131,8 +129,7 @@ struct file_allocated_range_buffer;
+ int ksmbd_vfs_fqar_lseek(struct ksmbd_file *fp, loff_t start, loff_t length,
+                        struct file_allocated_range_buffer *ranges,
+                        unsigned int in_count, unsigned int *out_count);
+-int ksmbd_vfs_unlink(struct mnt_idmap *idmap, struct dentry *dir,
+-                   struct dentry *dentry);
++int ksmbd_vfs_unlink(struct file *filp);
+ void *ksmbd_vfs_init_kstat(char **p, struct ksmbd_kstat *ksmbd_kstat);
+ int ksmbd_vfs_fill_dentry_attrs(struct ksmbd_work *work,
+                               struct mnt_idmap *idmap,
+diff --git a/fs/ksmbd/vfs_cache.c b/fs/ksmbd/vfs_cache.c
+index 054a7d2e0f489..2d0138e72d783 100644
+--- a/fs/ksmbd/vfs_cache.c
++++ b/fs/ksmbd/vfs_cache.c
+@@ -244,7 +244,6 @@ void ksmbd_release_inode_hash(void)
+ static void __ksmbd_inode_close(struct ksmbd_file *fp)
+ {
+-      struct dentry *dir, *dentry;
+       struct ksmbd_inode *ci = fp->f_ci;
+       int err;
+       struct file *filp;
+@@ -263,11 +262,9 @@ static void __ksmbd_inode_close(struct ksmbd_file *fp)
+       if (atomic_dec_and_test(&ci->m_count)) {
+               write_lock(&ci->m_lock);
+               if (ci->m_flags & (S_DEL_ON_CLS | S_DEL_PENDING)) {
+-                      dentry = filp->f_path.dentry;
+-                      dir = dentry->d_parent;
+                       ci->m_flags &= ~(S_DEL_ON_CLS | S_DEL_PENDING);
+                       write_unlock(&ci->m_lock);
+-                      ksmbd_vfs_unlink(file_mnt_idmap(filp), dir, dentry);
++                      ksmbd_vfs_unlink(filp);
+                       write_lock(&ci->m_lock);
+               }
+               write_unlock(&ci->m_lock);
+diff --git a/fs/namei.c b/fs/namei.c
+index 6bc1964e2214e..c1d59ae5af83e 100644
+--- a/fs/namei.c
++++ b/fs/namei.c
+@@ -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
+@@ -3852,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;
+@@ -4212,7 +4243,7 @@ int do_rmdir(int dfd, struct filename *name)
+               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;
+@@ -4345,7 +4376,7 @@ int do_unlinkat(int dfd, struct filename *name)
+               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)) {
+@@ -4909,7 +4940,8 @@ int do_renameat2(int olddfd, struct filename *from, int newdfd,
+ 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;
+@@ -4917,7 +4949,8 @@ int do_renameat2(int olddfd, struct filename *from, int newdfd,
+       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;
+diff --git a/include/linux/namei.h b/include/linux/namei.h
+index 5864e4d82e567..1463cbda48886 100644
+--- a/include/linux/namei.h
++++ b/include/linux/namei.h
+@@ -57,12 +57,18 @@ static inline int user_path_at(int dfd, const char __user *name, unsigned flags,
+       return user_path_at_empty(dfd, name, flags, path, NULL);
+ }
++struct dentry *lookup_one_qstr_excl(const struct qstr *name,
++                                  struct dentry *base,
++                                  unsigned int flags);
+ extern int kern_path(const char *, unsigned, struct path *);
+ extern struct dentry *kern_path_create(int, const char *, struct path *, unsigned int);
+ extern struct dentry *user_path_create(int, const char __user *, struct path *, unsigned int);
+ extern void done_path_create(struct path *, struct dentry *);
+ extern struct dentry *kern_path_locked(const char *, struct path *);
++int vfs_path_parent_lookup(struct filename *filename, unsigned int flags,
++                         struct path *parent, struct qstr *last, int *type,
++                         const struct path *root);
+ int vfs_path_lookup(struct dentry *, struct vfsmount *, const char *,
+                   unsigned int, struct path *);
+-- 
+2.39.2
+
diff --git a/queue-6.3/ksmbd-remove-internal.h-include.patch b/queue-6.3/ksmbd-remove-internal.h-include.patch
new file mode 100644 (file)
index 0000000..6d5144c
--- /dev/null
@@ -0,0 +1,66 @@
+From cd1195f6fc003e400a84ac460b4230e796467628 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 16 Mar 2023 07:34:33 +0900
+Subject: ksmbd: remove internal.h include
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit 211db0ac9e3dc6c46f2dd53395b34d76af929faf ]
+
+Since vfs_path_lookup is exported, It should not be internal.
+Move vfs_path_lookup prototype in internal.h to linux/namei.h.
+
+Suggested-by: Al Viro <viro@zeniv.linux.org.uk>
+Reviewed-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
+Stable-dep-of: 40b268d384a2 ("ksmbd: add mnt_want_write to ksmbd vfs functions")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/internal.h         | 2 --
+ fs/ksmbd/vfs.c        | 2 --
+ include/linux/namei.h | 2 ++
+ 3 files changed, 2 insertions(+), 4 deletions(-)
+
+diff --git a/fs/internal.h b/fs/internal.h
+index dc4eb91a577a8..071a7517f1a74 100644
+--- a/fs/internal.h
++++ b/fs/internal.h
+@@ -59,8 +59,6 @@ extern int finish_clean_context(struct fs_context *fc);
+  */
+ 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);
+diff --git a/fs/ksmbd/vfs.c b/fs/ksmbd/vfs.c
+index f6a8b26593090..1ad97df1dfb6f 100644
+--- a/fs/ksmbd/vfs.c
++++ b/fs/ksmbd/vfs.c
+@@ -19,8 +19,6 @@
+ #include <linux/sched/xacct.h>
+ #include <linux/crc32c.h>
+-#include "../internal.h"      /* for vfs_path_lookup */
+-
+ #include "glob.h"
+ #include "oplock.h"
+ #include "connection.h"
+diff --git a/include/linux/namei.h b/include/linux/namei.h
+index 0d797f3367cad..ba9b32b4d1b07 100644
+--- a/include/linux/namei.h
++++ b/include/linux/namei.h
+@@ -63,6 +63,8 @@ extern struct dentry *kern_path_create(int, const char *, struct path *, unsigne
+ extern struct dentry *user_path_create(int, const char __user *, struct path *, unsigned int);
+ extern void done_path_create(struct path *, struct dentry *);
+ extern struct dentry *kern_path_locked(const char *, struct path *);
++int vfs_path_lookup(struct dentry *, struct vfsmount *, const char *,
++                  unsigned int, struct path *);
+ extern struct dentry *try_lookup_one_len(const char *, struct dentry *, int);
+ extern struct dentry *lookup_one_len(const char *, struct dentry *, int);
+-- 
+2.39.2
+
diff --git a/queue-6.3/regmap-spi-avmm-fix-regmap_bus-max_raw_write.patch b/queue-6.3/regmap-spi-avmm-fix-regmap_bus-max_raw_write.patch
new file mode 100644 (file)
index 0000000..c05b542
--- /dev/null
@@ -0,0 +1,52 @@
+From 584118ede3e56de62c5c735052034e897d8ebfa0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 20 Jun 2023 13:28:24 -0700
+Subject: regmap: spi-avmm: Fix regmap_bus max_raw_write
+
+From: Russ Weight <russell.h.weight@intel.com>
+
+[ Upstream commit c8e796895e2310b6130e7577248da1d771431a77 ]
+
+The max_raw_write member of the regmap_spi_avmm_bus structure is defined
+as:
+       .max_raw_write = SPI_AVMM_VAL_SIZE * MAX_WRITE_CNT
+
+SPI_AVMM_VAL_SIZE == 4 and MAX_WRITE_CNT == 1 so this results in a
+maximum write transfer size of 4 bytes which provides only enough space to
+transfer the address of the target register. It provides no space for the
+value to be transferred. This bug became an issue (divide-by-zero in
+_regmap_raw_write()) after the following was accepted into mainline:
+
+commit 3981514180c9 ("regmap: Account for register length when chunking")
+
+Change max_raw_write to include space (4 additional bytes) for both the
+register address and value:
+
+       .max_raw_write = SPI_AVMM_REG_SIZE + SPI_AVMM_VAL_SIZE * MAX_WRITE_CNT
+
+Fixes: 7f9fb67358a2 ("regmap: add Intel SPI Slave to AVMM Bus Bridge support")
+Reviewed-by: Matthew Gerlach <matthew.gerlach@linux.intel.com>
+Signed-off-by: Russ Weight <russell.h.weight@intel.com>
+Link: https://lore.kernel.org/r/20230620202824.380313-1-russell.h.weight@intel.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/base/regmap/regmap-spi-avmm.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/base/regmap/regmap-spi-avmm.c b/drivers/base/regmap/regmap-spi-avmm.c
+index 4c2b94b3e30be..6af692844c196 100644
+--- a/drivers/base/regmap/regmap-spi-avmm.c
++++ b/drivers/base/regmap/regmap-spi-avmm.c
+@@ -660,7 +660,7 @@ static const struct regmap_bus regmap_spi_avmm_bus = {
+       .reg_format_endian_default = REGMAP_ENDIAN_NATIVE,
+       .val_format_endian_default = REGMAP_ENDIAN_NATIVE,
+       .max_raw_read = SPI_AVMM_VAL_SIZE * MAX_READ_CNT,
+-      .max_raw_write = SPI_AVMM_VAL_SIZE * MAX_WRITE_CNT,
++      .max_raw_write = SPI_AVMM_REG_SIZE + SPI_AVMM_VAL_SIZE * MAX_WRITE_CNT,
+       .free_context = spi_avmm_bridge_ctx_free,
+ };
+-- 
+2.39.2
+
diff --git a/queue-6.3/regulator-pca9450-fix-ldo3out-and-ldo4out-mask.patch b/queue-6.3/regulator-pca9450-fix-ldo3out-and-ldo4out-mask.patch
new file mode 100644 (file)
index 0000000..5335421
--- /dev/null
@@ -0,0 +1,43 @@
+From df16c168d9f5a73ecfa19dee632c51c1fbbd7594 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 14 Jun 2023 14:52:40 +0200
+Subject: regulator: pca9450: Fix LDO3OUT and LDO4OUT MASK
+
+From: Teresa Remmet <t.remmet@phytec.de>
+
+[ Upstream commit 7257d930aadcd62d1c7971ab14f3b1126356abdc ]
+
+L3_OUT and L4_OUT Bit fields range from Bit 0:4 and thus the
+mask should be 0x1F instead of 0x0F.
+
+Fixes: 0935ff5f1f0a ("regulator: pca9450: add pca9450 pmic driver")
+Signed-off-by: Teresa Remmet <t.remmet@phytec.de>
+Reviewed-by: Frieder Schrempf <frieder.schrempf@kontron.de>
+Link: https://lore.kernel.org/r/20230614125240.3946519-1-t.remmet@phytec.de
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/linux/regulator/pca9450.h | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/include/linux/regulator/pca9450.h b/include/linux/regulator/pca9450.h
+index 3c01c2bf84f53..505c908dbb817 100644
+--- a/include/linux/regulator/pca9450.h
++++ b/include/linux/regulator/pca9450.h
+@@ -196,11 +196,11 @@ enum {
+ /* PCA9450_REG_LDO3_VOLT bits */
+ #define LDO3_EN_MASK                  0xC0
+-#define LDO3OUT_MASK                  0x0F
++#define LDO3OUT_MASK                  0x1F
+ /* PCA9450_REG_LDO4_VOLT bits */
+ #define LDO4_EN_MASK                  0xC0
+-#define LDO4OUT_MASK                  0x0F
++#define LDO4OUT_MASK                  0x1F
+ /* PCA9450_REG_LDO5_VOLT bits */
+ #define LDO5L_EN_MASK                 0xC0
+-- 
+2.39.2
+
index 46ab58e384ede5611ba8efb26e27106659a36bf2..6bd52126487860aca91c17e41064387f25f49546 100644 (file)
@@ -85,3 +85,10 @@ mmc-sunxi-fix-deferred-probing.patch
 mmc-meson-gx-fix-deferred-probing.patch
 bpf-ensure-main-program-has-an-extable.patch
 wifi-iwlwifi-pcie-handle-so-f-device-for-pci-id-0x7af0.patch
+spi-spi-geni-qcom-correctly-handle-eprobe_defer-from.patch
+regulator-pca9450-fix-ldo3out-and-ldo4out-mask.patch
+regmap-spi-avmm-fix-regmap_bus-max_raw_write.patch
+ksmbd-remove-internal.h-include.patch
+fs-introduce-lock_rename_child-helper.patch
+ksmbd-fix-racy-issue-from-using-d_parent-and-d_name.patch
+ksmbd-add-mnt_want_write-to-ksmbd-vfs-functions.patch
diff --git a/queue-6.3/spi-spi-geni-qcom-correctly-handle-eprobe_defer-from.patch b/queue-6.3/spi-spi-geni-qcom-correctly-handle-eprobe_defer-from.patch
new file mode 100644 (file)
index 0000000..224e5b2
--- /dev/null
@@ -0,0 +1,40 @@
+From e4b9bf35a1ac36280afd79b1a25b5ff1c65a29b0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 15 Jun 2023 14:51:45 +0200
+Subject: spi: spi-geni-qcom: correctly handle -EPROBE_DEFER from
+ dma_request_chan()
+
+From: Neil Armstrong <neil.armstrong@linaro.org>
+
+[ Upstream commit 9d7054fb3ac2e8d252aae1268f20623f244e644f ]
+
+Now spi_geni_grab_gpi_chan() errors are correctly reported, the
+-EPROBE_DEFER error should be returned from probe in case the
+GPI dma driver is built as module and/or not probed yet.
+
+Fixes: b59c122484ec ("spi: spi-geni-qcom: Add support for GPI dma")
+Fixes: 6532582c353f ("spi: spi-geni-qcom: fix error handling in spi_geni_grab_gpi_chan()")
+Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
+Link: https://lore.kernel.org/r/20230615-topic-sm8550-upstream-fix-spi-geni-qcom-probe-v2-1-670c3d9e8c9c@linaro.org
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/spi/spi-geni-qcom.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/drivers/spi/spi-geni-qcom.c b/drivers/spi/spi-geni-qcom.c
+index b106faf21a723..baf477383682d 100644
+--- a/drivers/spi/spi-geni-qcom.c
++++ b/drivers/spi/spi-geni-qcom.c
+@@ -646,6 +646,8 @@ static int spi_geni_init(struct spi_geni_master *mas)
+                       geni_se_select_mode(se, GENI_GPI_DMA);
+                       dev_dbg(mas->dev, "Using GPI DMA mode for SPI\n");
+                       break;
++              } else if (ret == -EPROBE_DEFER) {
++                      goto out_pm;
+               }
+               /*
+                * in case of failure to get gpi dma channel, we can still do the
+-- 
+2.39.2
+