From: Sasha Levin Date: Fri, 23 Jun 2023 12:45:13 +0000 (-0400) Subject: Fixes for 6.3 X-Git-Tag: v4.14.320~40 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=874f3918587aafeb785e7faa2c55071e9842f914;p=thirdparty%2Fkernel%2Fstable-queue.git Fixes for 6.3 Signed-off-by: Sasha Levin --- 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 index 00000000000..926aa472323 --- /dev/null +++ b/queue-6.3/fs-introduce-lock_rename_child-helper.patch @@ -0,0 +1,132 @@ +From fa040cabc969977f1f775d154ea8bb3217face13 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 16 Mar 2023 07:34:34 +0900 +Subject: fs: introduce lock_rename_child() helper + +From: Al Viro + +[ 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 +Signed-off-by: Namjae Jeon +Signed-off-by: Al Viro +Stable-dep-of: 40b268d384a2 ("ksmbd: add mnt_want_write to ksmbd vfs functions") +Signed-off-by: Sasha Levin +--- + 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 index 00000000000..e3ef2965778 --- /dev/null +++ b/queue-6.3/ksmbd-add-mnt_want_write-to-ksmbd-vfs-functions.patch @@ -0,0 +1,612 @@ +From bfb95b2b430cc3a4e9cac2b511dac80ba96e948f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 15 Jun 2023 15:56:32 +0900 +Subject: ksmbd: add mnt_want_write to ksmbd vfs functions + +From: Namjae Jeon + +[ 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 +Signed-off-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + 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 index 00000000000..8febc3f6287 --- /dev/null +++ b/queue-6.3/ksmbd-fix-racy-issue-from-using-d_parent-and-d_name.patch @@ -0,0 +1,1194 @@ +From a4db2bbcd3a9961d43bf9a00537988de8082f5fd Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 21 Apr 2023 16:09:01 +0900 +Subject: ksmbd: fix racy issue from using ->d_parent and ->d_name + +From: Namjae Jeon + +[ 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 +Signed-off-by: Steve French +Stable-dep-of: 40b268d384a2 ("ksmbd: add mnt_want_write to ksmbd vfs functions") +Signed-off-by: Sasha Levin +--- + 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 + #include + #include ++#include + + #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 index 00000000000..6d5144cea4d --- /dev/null +++ b/queue-6.3/ksmbd-remove-internal.h-include.patch @@ -0,0 +1,66 @@ +From cd1195f6fc003e400a84ac460b4230e796467628 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 16 Mar 2023 07:34:33 +0900 +Subject: ksmbd: remove internal.h include + +From: Namjae Jeon + +[ 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 +Reviewed-by: Christian Brauner +Signed-off-by: Namjae Jeon +Signed-off-by: Al Viro +Stable-dep-of: 40b268d384a2 ("ksmbd: add mnt_want_write to ksmbd vfs functions") +Signed-off-by: Sasha Levin +--- + 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 + #include + +-#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 index 00000000000..c05b54258e2 --- /dev/null +++ b/queue-6.3/regmap-spi-avmm-fix-regmap_bus-max_raw_write.patch @@ -0,0 +1,52 @@ +From 584118ede3e56de62c5c735052034e897d8ebfa0 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 20 Jun 2023 13:28:24 -0700 +Subject: regmap: spi-avmm: Fix regmap_bus max_raw_write + +From: Russ Weight + +[ 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 +Signed-off-by: Russ Weight +Link: https://lore.kernel.org/r/20230620202824.380313-1-russell.h.weight@intel.com +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + 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 index 00000000000..5335421f47f --- /dev/null +++ b/queue-6.3/regulator-pca9450-fix-ldo3out-and-ldo4out-mask.patch @@ -0,0 +1,43 @@ +From df16c168d9f5a73ecfa19dee632c51c1fbbd7594 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 14 Jun 2023 14:52:40 +0200 +Subject: regulator: pca9450: Fix LDO3OUT and LDO4OUT MASK + +From: Teresa Remmet + +[ 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 +Reviewed-by: Frieder Schrempf +Link: https://lore.kernel.org/r/20230614125240.3946519-1-t.remmet@phytec.de +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + 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 + diff --git a/queue-6.3/series b/queue-6.3/series index 46ab58e384e..6bd52126487 100644 --- a/queue-6.3/series +++ b/queue-6.3/series @@ -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 index 00000000000..224e5b2a308 --- /dev/null +++ b/queue-6.3/spi-spi-geni-qcom-correctly-handle-eprobe_defer-from.patch @@ -0,0 +1,40 @@ +From e4b9bf35a1ac36280afd79b1a25b5ff1c65a29b0 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +[ 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 +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 +Signed-off-by: Sasha Levin +--- + 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 +