]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
Fixes for 6.1
authorSasha Levin <sashal@kernel.org>
Mon, 1 Jan 2024 18:11:04 +0000 (13:11 -0500)
committerSasha Levin <sashal@kernel.org>
Mon, 1 Jan 2024 18:11:04 +0000 (13:11 -0500)
Signed-off-by: Sasha Levin <sashal@kernel.org>
85 files changed:
queue-6.1/arm-dts-fix-occasional-boot-hang-for-am3-usb.patch [new file with mode: 0644]
queue-6.1/block-renumber-queue_flag_hw_wc.patch [new file with mode: 0644]
queue-6.1/fs-introduce-lock_rename_child-helper.patch [new file with mode: 0644]
queue-6.1/iio-imu-adis16475-add-spi_device_id-table.patch [new file with mode: 0644]
queue-6.1/ksmbd-add-missing-calling-smb2_set_err_rsp-on-error.patch [new file with mode: 0644]
queue-6.1/ksmbd-add-mnt_want_write-to-ksmbd-vfs-functions.patch [new file with mode: 0644]
queue-6.1/ksmbd-add-support-for-read-compound.patch [new file with mode: 0644]
queue-6.1/ksmbd-add-support-for-surrogate-pair-conversion.patch [new file with mode: 0644]
queue-6.1/ksmbd-avoid-duplicate-negotiate-ctx-offset-increment.patch [new file with mode: 0644]
queue-6.1/ksmbd-avoid-duplicate-opinfo_put-call-on-error-of-sm.patch [new file with mode: 0644]
queue-6.1/ksmbd-call-putname-after-using-the-last-component.patch [new file with mode: 0644]
queue-6.1/ksmbd-change-the-return-value-of-ksmbd_vfs_query_max.patch [new file with mode: 0644]
queue-6.1/ksmbd-check-if-a-mount-point-is-crossed-during-path-.patch [new file with mode: 0644]
queue-6.1/ksmbd-check-iov-vector-index-in-ksmbd_conn_write.patch [new file with mode: 0644]
queue-6.1/ksmbd-convert-to-use-sysfs_emit-sysfs_emit_at-apis.patch [new file with mode: 0644]
queue-6.1/ksmbd-delete-asynchronous-work-from-list.patch [new file with mode: 0644]
queue-6.1/ksmbd-don-t-update-op_state-as-oplock_state_none-on-.patch [new file with mode: 0644]
queue-6.1/ksmbd-downgrade-rwh-lease-caching-state-to-rh-for-di.patch [new file with mode: 0644]
queue-6.1/ksmbd-fix-force-create-mode-and-force-directory-mode.patch [new file with mode: 0644]
queue-6.1/ksmbd-fix-kernel-doc-comment-of-ksmbd_vfs_kern_path_.patch [new file with mode: 0644]
queue-6.1/ksmbd-fix-kernel-doc-comment-of-ksmbd_vfs_setxattr.patch [new file with mode: 0644]
queue-6.1/ksmbd-fix-missing-rdma-capable-flag-for-ipoib-device.patch [new file with mode: 0644]
queue-6.1/ksmbd-fix-null-pointer-dereferences-in-ksmbd_update_.patch [new file with mode: 0644]
queue-6.1/ksmbd-fix-one-kernel-doc-comment.patch [new file with mode: 0644]
queue-6.1/ksmbd-fix-parameter-name-and-comment-mismatch.patch [new file with mode: 0644]
queue-6.1/ksmbd-fix-passing-freed-memory-aux_payload_buf.patch [new file with mode: 0644]
queue-6.1/ksmbd-fix-posix_acls-and-acls-dereferencing-possible.patch [new file with mode: 0644]
queue-6.1/ksmbd-fix-possible-deadlock-in-smb2_open.patch [new file with mode: 0644]
queue-6.1/ksmbd-fix-potential-double-free-on-smb2_read_pipe-er.patch [new file with mode: 0644]
queue-6.1/ksmbd-fix-race-condition-between-tree-conn-lookup-an.patch [new file with mode: 0644]
queue-6.1/ksmbd-fix-race-condition-from-parallel-smb2-lock-req.patch [new file with mode: 0644]
queue-6.1/ksmbd-fix-race-condition-from-parallel-smb2-logoff-r.patch [new file with mode: 0644]
queue-6.1/ksmbd-fix-race-condition-with-fp.patch [new file with mode: 0644]
queue-6.1/ksmbd-fix-racy-issue-from-using-d_parent-and-d_name.patch [new file with mode: 0644]
queue-6.1/ksmbd-fix-recursive-locking-in-vfs-helpers.patch [new file with mode: 0644]
queue-6.1/ksmbd-fix-resource-leak-in-smb2_lock.patch [new file with mode: 0644]
queue-6.1/ksmbd-fix-spelling-mistake-excceed-exceeded.patch [new file with mode: 0644]
queue-6.1/ksmbd-fix-typo-syncronous-synchronous.patch [new file with mode: 0644]
queue-6.1/ksmbd-fix-uninitialized-pointer-read-in-ksmbd_vfs_re.patch [new file with mode: 0644]
queue-6.1/ksmbd-fix-uninitialized-pointer-read-in-smb2_create_.patch [new file with mode: 0644]
queue-6.1/ksmbd-fix-unsigned-expression-compared-with-zero.patch [new file with mode: 0644]
queue-6.1/ksmbd-fix-wrong-allocation-size-update-in-smb2_open.patch [new file with mode: 0644]
queue-6.1/ksmbd-fix-wrong-error-response-status-by-using-set_s.patch [new file with mode: 0644]
queue-6.1/ksmbd-fix-wrong-interim-response-on-compound.patch [new file with mode: 0644]
queue-6.1/ksmbd-implements-sess-rpc_handle_list-as-xarray.patch [new file with mode: 0644]
queue-6.1/ksmbd-lazy-v2-lease-break-on-smb2_write.patch [new file with mode: 0644]
queue-6.1/ksmbd-move-oplock-handling-after-unlock-parent-dir.patch [new file with mode: 0644]
queue-6.1/ksmbd-move-setting-smb2_flags_async_command-and-asyn.patch [new file with mode: 0644]
queue-6.1/ksmbd-no-need-to-wait-for-binded-connection-terminat.patch [new file with mode: 0644]
queue-6.1/ksmbd-prevent-memory-leak-on-error-return.patch [new file with mode: 0644]
queue-6.1/ksmbd-release-interim-response-after-sending-status-.patch [new file with mode: 0644]
queue-6.1/ksmbd-remove-duplicated-codes.patch [new file with mode: 0644]
queue-6.1/ksmbd-remove-experimental-warning.patch [new file with mode: 0644]
queue-6.1/ksmbd-remove-unneeded-mark_inode_dirty-in-set_info_s.patch [new file with mode: 0644]
queue-6.1/ksmbd-remove-unused-compression-negotiate-ctx-packin.patch [new file with mode: 0644]
queue-6.1/ksmbd-remove-unused-field-in-ksmbd_user-struct.patch [new file with mode: 0644]
queue-6.1/ksmbd-remove-unused-is_char_allowed-function.patch [new file with mode: 0644]
queue-6.1/ksmbd-remove-unused-ksmbd_tree_conn_share-function.patch [new file with mode: 0644]
queue-6.1/ksmbd-reorganize-ksmbd_iov_pin_rsp.patch [new file with mode: 0644]
queue-6.1/ksmbd-replace-one-element-array-with-flexible-array-.patch [new file with mode: 0644]
queue-6.1/ksmbd-replace-one-element-arrays-with-flexible-array.patch [new file with mode: 0644]
queue-6.1/ksmbd-replace-the-ternary-conditional-operator-with-.patch [new file with mode: 0644]
queue-6.1/ksmbd-return-a-literal-instead-of-err-in-ksmbd_vfs_k.patch [new file with mode: 0644]
queue-6.1/ksmbd-return-invalid-parameter-error-response-if-smb.patch [new file with mode: 0644]
queue-6.1/ksmbd-send-v2-lease-break-notification-for-directory.patch [new file with mode: 0644]
queue-6.1/ksmbd-separately-allocate-ci-per-dentry.patch [new file with mode: 0644]
queue-6.1/ksmbd-set-epoch-in-create-context-v2-lease.patch [new file with mode: 0644]
queue-6.1/ksmbd-set-negotiatecontextcount-once-instead-of-ever.patch [new file with mode: 0644]
queue-6.1/ksmbd-set-smb2_session_flag_encrypt_data-when-enforc.patch [new file with mode: 0644]
queue-6.1/ksmbd-set-v2-lease-capability.patch [new file with mode: 0644]
queue-6.1/ksmbd-switch-to-use-kmemdup_nul-helper.patch [new file with mode: 0644]
queue-6.1/ksmbd-update-kconfig-to-note-kerberos-support-and-fi.patch [new file with mode: 0644]
queue-6.1/ksmbd-use-f_setlk-when-unlocking-a-file.patch [new file with mode: 0644]
queue-6.1/ksmbd-use-kvzalloc-instead-of-kvmalloc.patch [new file with mode: 0644]
queue-6.1/ksmbd-use-kzalloc-instead-of-__gfp_zero.patch [new file with mode: 0644]
queue-6.1/ksmbd-use-struct_size-helper-in-ksmbd_negotiate_smb_.patch [new file with mode: 0644]
queue-6.1/linux-export-ensure-natural-alignment-of-kcrctab-arr.patch [new file with mode: 0644]
queue-6.1/nfsd-call-nfsd_last_thread-before-final-nfsd_put.patch [new file with mode: 0644]
queue-6.1/nfsd-separate-nfsd_last_thread-from-nfsd_put.patch [new file with mode: 0644]
queue-6.1/series [new file with mode: 0644]
queue-6.1/spi-add-apis-in-spi-core-to-set-get-spi-chip_select-.patch [new file with mode: 0644]
queue-6.1/spi-atmel-fix-clock-issue-when-using-devices-with-di.patch [new file with mode: 0644]
queue-6.1/spi-introduce-spi_get_device_match_data-helper.patch [new file with mode: 0644]
queue-6.1/spi-reintroduce-spi_set_cs_timing.patch [new file with mode: 0644]
queue-6.1/usb-fotg210-hcd-delete-an-incorrect-bounds-test.patch [new file with mode: 0644]

diff --git a/queue-6.1/arm-dts-fix-occasional-boot-hang-for-am3-usb.patch b/queue-6.1/arm-dts-fix-occasional-boot-hang-for-am3-usb.patch
new file mode 100644 (file)
index 0000000..d0f8501
--- /dev/null
@@ -0,0 +1,40 @@
+From cb680f84a33e0bd3cda8cfe94452f3c15f0e3927 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 12 Dec 2023 15:50:35 +0200
+Subject: ARM: dts: Fix occasional boot hang for am3 usb
+
+From: Tony Lindgren <tony@atomide.com>
+
+[ Upstream commit 9b6a51aab5f5f9f71d2fa16e8b4d530e1643dfcb ]
+
+With subtle timings changes, we can now sometimes get an external abort on
+non-linefetch error booting am3 devices at sysc_reset(). This is because
+of a missing reset delay needed for the usb target module.
+
+Looks like we never enabled the delay earlier for am3, although a similar
+issue was seen earlier with a similar usb setup for dm814x as described in
+commit ebf244148092 ("ARM: OMAP2+: Use srst_udelay for USB on dm814x").
+
+Cc: stable@vger.kernel.org
+Fixes: 0782e8572ce4 ("ARM: dts: Probe am335x musb with ti-sysc")
+Signed-off-by: Tony Lindgren <tony@atomide.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/arm/boot/dts/am33xx.dtsi | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/arch/arm/boot/dts/am33xx.dtsi b/arch/arm/boot/dts/am33xx.dtsi
+index 32d397b3950b9..b2e7f6a710740 100644
+--- a/arch/arm/boot/dts/am33xx.dtsi
++++ b/arch/arm/boot/dts/am33xx.dtsi
+@@ -349,6 +349,7 @@ usb: target-module@47400000 {
+                                       <SYSC_IDLE_NO>,
+                                       <SYSC_IDLE_SMART>,
+                                       <SYSC_IDLE_SMART_WKUP>;
++                      ti,sysc-delay-us = <2>;
+                       clocks = <&l3s_clkctrl AM3_L3S_USB_OTG_HS_CLKCTRL 0>;
+                       clock-names = "fck";
+                       #address-cells = <1>;
+-- 
+2.43.0
+
diff --git a/queue-6.1/block-renumber-queue_flag_hw_wc.patch b/queue-6.1/block-renumber-queue_flag_hw_wc.patch
new file mode 100644 (file)
index 0000000..a9ee3b5
--- /dev/null
@@ -0,0 +1,37 @@
+From 41a9ccbc475d613f073f31681da464dfdaf616eb Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 26 Dec 2023 08:15:24 +0000
+Subject: block: renumber QUEUE_FLAG_HW_WC
+
+From: Christoph Hellwig <hch@lst.de>
+
+[ Upstream commit 02d374f3418df577c850f0cd45c3da9245ead547 ]
+
+For the QUEUE_FLAG_HW_WC to actually work, it needs to have a separate
+number from QUEUE_FLAG_FUA, doh.
+
+Fixes: 43c9835b144c ("block: don't allow enabling a cache on devices that don't support it")
+Signed-off-by: Christoph Hellwig <hch@lst.de>
+Link: https://lore.kernel.org/r/20231226081524.180289-1-hch@lst.de
+Signed-off-by: Jens Axboe <axboe@kernel.dk>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/linux/blkdev.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
+index 57674b3c58774..07a7eeef47d39 100644
+--- a/include/linux/blkdev.h
++++ b/include/linux/blkdev.h
+@@ -565,7 +565,7 @@ struct request_queue {
+ #define QUEUE_FLAG_NOXMERGES  9       /* No extended merges */
+ #define QUEUE_FLAG_ADD_RANDOM 10      /* Contributes to random pool */
+ #define QUEUE_FLAG_SAME_FORCE 12      /* force complete on same CPU */
+-#define QUEUE_FLAG_HW_WC      18      /* Write back caching supported */
++#define QUEUE_FLAG_HW_WC      13      /* Write back caching supported */
+ #define QUEUE_FLAG_INIT_DONE  14      /* queue is initialized */
+ #define QUEUE_FLAG_STABLE_WRITES 15   /* don't modify blks until WB is done */
+ #define QUEUE_FLAG_POLL               16      /* IO polling enabled if set */
+-- 
+2.43.0
+
diff --git a/queue-6.1/fs-introduce-lock_rename_child-helper.patch b/queue-6.1/fs-introduce-lock_rename_child-helper.patch
new file mode 100644 (file)
index 0000000..7275a6a
--- /dev/null
@@ -0,0 +1,131 @@
+From 1713573bb43a2ac28f0643e58afa5730feba14b0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:12:36 +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>
+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 5e1c2ab2ae709..6daaf84567195 100644
+--- a/fs/namei.c
++++ b/fs/namei.c
+@@ -2980,20 +2980,10 @@ static inline int may_create(struct user_namespace *mnt_userns,
+       return inode_permission(mnt_userns, 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)
+                       I_MUTEX_PARENT, 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 00fee52df8423..2b66021c740dd 100644
+--- a/include/linux/namei.h
++++ b/include/linux/namei.h
+@@ -81,6 +81,7 @@ extern int follow_down(struct path *);
+ 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.43.0
+
diff --git a/queue-6.1/iio-imu-adis16475-add-spi_device_id-table.patch b/queue-6.1/iio-imu-adis16475-add-spi_device_id-table.patch
new file mode 100644 (file)
index 0000000..52365ca
--- /dev/null
@@ -0,0 +1,176 @@
+From de2a87fa19335534b473187947b74a300733dd0f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 2 Nov 2023 13:52:58 +0100
+Subject: iio: imu: adis16475: add spi_device_id table
+
+From: Nuno Sa <nuno.sa@analog.com>
+
+[ Upstream commit ee4d79055aeea27f1b8c42233cc0c90d0a8b5355 ]
+
+This prevents the warning message "SPI driver has no spi_device_id for..."
+when registering the driver. More importantly, it makes sure that
+module autoloading works as spi relies on spi: modaliases and not of.
+
+While at it, move the of_device_id table to it's natural place.
+
+Fixes: fff7352bf7a3c ("iio: imu: Add support for adis16475")
+Signed-off-by: Nuno Sa <nuno.sa@analog.com>
+Link: https://lore.kernel.org/r/20231102125258.3284830-1-nuno.sa@analog.com
+Cc: <Stable@vger.kernel.org>
+Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/iio/imu/adis16475.c | 117 ++++++++++++++++++++++--------------
+ 1 file changed, 72 insertions(+), 45 deletions(-)
+
+diff --git a/drivers/iio/imu/adis16475.c b/drivers/iio/imu/adis16475.c
+index aec55f7e1f260..2d939773445d7 100644
+--- a/drivers/iio/imu/adis16475.c
++++ b/drivers/iio/imu/adis16475.c
+@@ -1243,50 +1243,6 @@ static int adis16475_config_irq_pin(struct adis16475 *st)
+       return 0;
+ }
+-static const struct of_device_id adis16475_of_match[] = {
+-      { .compatible = "adi,adis16470",
+-              .data = &adis16475_chip_info[ADIS16470] },
+-      { .compatible = "adi,adis16475-1",
+-              .data = &adis16475_chip_info[ADIS16475_1] },
+-      { .compatible = "adi,adis16475-2",
+-              .data = &adis16475_chip_info[ADIS16475_2] },
+-      { .compatible = "adi,adis16475-3",
+-              .data = &adis16475_chip_info[ADIS16475_3] },
+-      { .compatible = "adi,adis16477-1",
+-              .data = &adis16475_chip_info[ADIS16477_1] },
+-      { .compatible = "adi,adis16477-2",
+-              .data = &adis16475_chip_info[ADIS16477_2] },
+-      { .compatible = "adi,adis16477-3",
+-              .data = &adis16475_chip_info[ADIS16477_3] },
+-      { .compatible = "adi,adis16465-1",
+-              .data = &adis16475_chip_info[ADIS16465_1] },
+-      { .compatible = "adi,adis16465-2",
+-              .data = &adis16475_chip_info[ADIS16465_2] },
+-      { .compatible = "adi,adis16465-3",
+-              .data = &adis16475_chip_info[ADIS16465_3] },
+-      { .compatible = "adi,adis16467-1",
+-              .data = &adis16475_chip_info[ADIS16467_1] },
+-      { .compatible = "adi,adis16467-2",
+-              .data = &adis16475_chip_info[ADIS16467_2] },
+-      { .compatible = "adi,adis16467-3",
+-              .data = &adis16475_chip_info[ADIS16467_3] },
+-      { .compatible = "adi,adis16500",
+-              .data = &adis16475_chip_info[ADIS16500] },
+-      { .compatible = "adi,adis16505-1",
+-              .data = &adis16475_chip_info[ADIS16505_1] },
+-      { .compatible = "adi,adis16505-2",
+-              .data = &adis16475_chip_info[ADIS16505_2] },
+-      { .compatible = "adi,adis16505-3",
+-              .data = &adis16475_chip_info[ADIS16505_3] },
+-      { .compatible = "adi,adis16507-1",
+-              .data = &adis16475_chip_info[ADIS16507_1] },
+-      { .compatible = "adi,adis16507-2",
+-              .data = &adis16475_chip_info[ADIS16507_2] },
+-      { .compatible = "adi,adis16507-3",
+-              .data = &adis16475_chip_info[ADIS16507_3] },
+-      { },
+-};
+-MODULE_DEVICE_TABLE(of, adis16475_of_match);
+ static int adis16475_probe(struct spi_device *spi)
+ {
+@@ -1300,7 +1256,7 @@ static int adis16475_probe(struct spi_device *spi)
+       st = iio_priv(indio_dev);
+-      st->info = device_get_match_data(&spi->dev);
++      st->info = spi_get_device_match_data(spi);
+       if (!st->info)
+               return -EINVAL;
+@@ -1340,12 +1296,83 @@ static int adis16475_probe(struct spi_device *spi)
+       return 0;
+ }
++static const struct of_device_id adis16475_of_match[] = {
++      { .compatible = "adi,adis16470",
++              .data = &adis16475_chip_info[ADIS16470] },
++      { .compatible = "adi,adis16475-1",
++              .data = &adis16475_chip_info[ADIS16475_1] },
++      { .compatible = "adi,adis16475-2",
++              .data = &adis16475_chip_info[ADIS16475_2] },
++      { .compatible = "adi,adis16475-3",
++              .data = &adis16475_chip_info[ADIS16475_3] },
++      { .compatible = "adi,adis16477-1",
++              .data = &adis16475_chip_info[ADIS16477_1] },
++      { .compatible = "adi,adis16477-2",
++              .data = &adis16475_chip_info[ADIS16477_2] },
++      { .compatible = "adi,adis16477-3",
++              .data = &adis16475_chip_info[ADIS16477_3] },
++      { .compatible = "adi,adis16465-1",
++              .data = &adis16475_chip_info[ADIS16465_1] },
++      { .compatible = "adi,adis16465-2",
++              .data = &adis16475_chip_info[ADIS16465_2] },
++      { .compatible = "adi,adis16465-3",
++              .data = &adis16475_chip_info[ADIS16465_3] },
++      { .compatible = "adi,adis16467-1",
++              .data = &adis16475_chip_info[ADIS16467_1] },
++      { .compatible = "adi,adis16467-2",
++              .data = &adis16475_chip_info[ADIS16467_2] },
++      { .compatible = "adi,adis16467-3",
++              .data = &adis16475_chip_info[ADIS16467_3] },
++      { .compatible = "adi,adis16500",
++              .data = &adis16475_chip_info[ADIS16500] },
++      { .compatible = "adi,adis16505-1",
++              .data = &adis16475_chip_info[ADIS16505_1] },
++      { .compatible = "adi,adis16505-2",
++              .data = &adis16475_chip_info[ADIS16505_2] },
++      { .compatible = "adi,adis16505-3",
++              .data = &adis16475_chip_info[ADIS16505_3] },
++      { .compatible = "adi,adis16507-1",
++              .data = &adis16475_chip_info[ADIS16507_1] },
++      { .compatible = "adi,adis16507-2",
++              .data = &adis16475_chip_info[ADIS16507_2] },
++      { .compatible = "adi,adis16507-3",
++              .data = &adis16475_chip_info[ADIS16507_3] },
++      { },
++};
++MODULE_DEVICE_TABLE(of, adis16475_of_match);
++
++static const struct spi_device_id adis16475_ids[] = {
++      { "adis16470", (kernel_ulong_t)&adis16475_chip_info[ADIS16470] },
++      { "adis16475-1", (kernel_ulong_t)&adis16475_chip_info[ADIS16475_1] },
++      { "adis16475-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16475_2] },
++      { "adis16475-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16475_3] },
++      { "adis16477-1", (kernel_ulong_t)&adis16475_chip_info[ADIS16477_1] },
++      { "adis16477-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16477_2] },
++      { "adis16477-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16477_3] },
++      { "adis16465-1", (kernel_ulong_t)&adis16475_chip_info[ADIS16465_1] },
++      { "adis16465-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16465_2] },
++      { "adis16465-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16465_3] },
++      { "adis16467-1", (kernel_ulong_t)&adis16475_chip_info[ADIS16467_1] },
++      { "adis16467-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16467_2] },
++      { "adis16467-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16467_3] },
++      { "adis16500", (kernel_ulong_t)&adis16475_chip_info[ADIS16500] },
++      { "adis16505-1", (kernel_ulong_t)&adis16475_chip_info[ADIS16505_1] },
++      { "adis16505-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16505_2] },
++      { "adis16505-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16505_3] },
++      { "adis16507-1", (kernel_ulong_t)&adis16475_chip_info[ADIS16507_1] },
++      { "adis16507-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16507_2] },
++      { "adis16507-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16507_3] },
++      { }
++};
++MODULE_DEVICE_TABLE(spi, adis16475_ids);
++
+ static struct spi_driver adis16475_driver = {
+       .driver = {
+               .name = "adis16475",
+               .of_match_table = adis16475_of_match,
+       },
+       .probe = adis16475_probe,
++      .id_table = adis16475_ids,
+ };
+ module_spi_driver(adis16475_driver);
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-add-missing-calling-smb2_set_err_rsp-on-error.patch b/queue-6.1/ksmbd-add-missing-calling-smb2_set_err_rsp-on-error.patch
new file mode 100644 (file)
index 0000000..45c47f9
--- /dev/null
@@ -0,0 +1,35 @@
+From adce1cb106c7824bd0f5afc904ec4aff9a6c3026 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:12:58 +0900
+Subject: ksmbd: add missing calling smb2_set_err_rsp() on error
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit 0e2378eaa2b3a663726cf740d4aaa8a801e2cb31 ]
+
+If some error happen on smb2_sess_setup(), Need to call
+smb2_set_err_rsp() to set error response.
+This patch add missing calling smb2_set_err_rsp() on error.
+
+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/smb/server/smb2pdu.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
+index 0fed613956f7a..b81a38803b40d 100644
+--- a/fs/smb/server/smb2pdu.c
++++ b/fs/smb/server/smb2pdu.c
+@@ -1904,6 +1904,7 @@ int smb2_sess_setup(struct ksmbd_work *work)
+                               ksmbd_conn_set_need_negotiate(conn);
+                       }
+               }
++              smb2_set_err_rsp(work);
+       } else {
+               unsigned int iov_len;
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-add-mnt_want_write-to-ksmbd-vfs-functions.patch b/queue-6.1/ksmbd-add-mnt_want_write-to-ksmbd-vfs-functions.patch
new file mode 100644 (file)
index 0000000..81c9630
--- /dev/null
@@ -0,0 +1,600 @@
+From 6ec47ed9ec9d4885dec6ef55dbddc216fba3f737 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:12:42 +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/smb/server/smb2pdu.c   |  26 ++++-----
+ fs/smb/server/smbacl.c    |  10 ++--
+ fs/smb/server/vfs.c       | 112 ++++++++++++++++++++++++++++++--------
+ fs/smb/server/vfs.h       |  17 +++---
+ fs/smb/server/vfs_cache.c |   2 +-
+ 5 files changed, 112 insertions(+), 55 deletions(-)
+
+diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
+index 028b1d1055b57..f35e06ae25b3b 100644
+--- a/fs/smb/server/smb2pdu.c
++++ b/fs/smb/server/smb2pdu.c
+@@ -2295,7 +2295,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(user_ns,
+-                                                          path->dentry,
++                                                          path,
+                                                           attr_name);
+                               if (rc < 0) {
+@@ -2309,8 +2309,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(user_ns,
+-                                              path->dentry, attr_name, value,
++                      rc = ksmbd_vfs_setxattr(user_ns, path, attr_name, value,
+                                               le16_to_cpu(eabuf->EaValueLength), 0);
+                       if (rc < 0) {
+                               ksmbd_debug(SMB,
+@@ -2374,8 +2373,7 @@ static noinline int smb2_set_stream_name_xattr(const struct path *path,
+               return -EBADF;
+       }
+-      rc = ksmbd_vfs_setxattr(user_ns, path->dentry,
+-                              xattr_stream_name, NULL, 0, 0);
++      rc = ksmbd_vfs_setxattr(user_ns, path, xattr_stream_name, NULL, 0, 0);
+       if (rc < 0)
+               pr_err("Failed to store XATTR stream name :%d\n", rc);
+       return 0;
+@@ -2403,7 +2401,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(user_ns, path->dentry,
++                      err = ksmbd_vfs_remove_xattr(user_ns, path,
+                                                    name);
+                       if (err)
+                               ksmbd_debug(SMB, "remove xattr failed : %s\n",
+@@ -2450,8 +2448,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_user_ns(path->mnt),
+-                                          path->dentry, &da);
++      rc = ksmbd_vfs_set_dos_attrib_xattr(mnt_user_ns(path->mnt), path, &da);
+       if (rc)
+               ksmbd_debug(SMB, "failed to store file attribute into xattr\n");
+ }
+@@ -3025,7 +3022,7 @@ int smb2_open(struct ksmbd_work *work)
+               struct inode *inode = d_inode(path.dentry);
+               posix_acl_rc = ksmbd_vfs_inherit_posix_acl(user_ns,
+-                                                         inode,
++                                                         &path,
+                                                          d_inode(path.dentry->d_parent));
+               if (posix_acl_rc)
+                       ksmbd_debug(SMB, "inherit posix acl failed : %d\n", posix_acl_rc);
+@@ -3041,7 +3038,7 @@ int smb2_open(struct ksmbd_work *work)
+                       if (rc) {
+                               if (posix_acl_rc)
+                                       ksmbd_vfs_set_init_posix_acl(user_ns,
+-                                                                   inode);
++                                                                   &path);
+                               if (test_share_config_flag(work->tcon->share_conf,
+                                                          KSMBD_SHARE_FLAG_ACL_XATTR)) {
+@@ -3081,7 +3078,7 @@ int smb2_open(struct ksmbd_work *work)
+                                       rc = ksmbd_vfs_set_sd_xattr(conn,
+                                                                   user_ns,
+-                                                                  path.dentry,
++                                                                  &path,
+                                                                   pntsd,
+                                                                   pntsd_size);
+                                       kfree(pntsd);
+@@ -5522,7 +5519,7 @@ static int smb2_rename(struct ksmbd_work *work,
+                       goto out;
+               rc = ksmbd_vfs_setxattr(file_mnt_user_ns(fp->filp),
+-                                      fp->filp->f_path.dentry,
++                                      &fp->filp->f_path,
+                                       xattr_stream_name,
+                                       NULL, 0, 0);
+               if (rc < 0) {
+@@ -5687,8 +5684,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(user_ns,
+-                                                  filp->f_path.dentry, &da);
++              rc = ksmbd_vfs_set_dos_attrib_xattr(user_ns, &filp->f_path, &da);
+               if (rc)
+                       ksmbd_debug(SMB,
+                                   "failed to restore file attribute in EA\n");
+@@ -7558,7 +7554,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(user_ns,
+-                                                   fp->filp->f_path.dentry, &da);
++                                                   &fp->filp->f_path, &da);
+               if (ret)
+                       fp->f_ci->m_fattr = old_fattr;
+       }
+diff --git a/fs/smb/server/smbacl.c b/fs/smb/server/smbacl.c
+index 8c041e71cf156..8fe2592c5525f 100644
+--- a/fs/smb/server/smbacl.c
++++ b/fs/smb/server/smbacl.c
+@@ -1185,8 +1185,7 @@ int smb_inherit_dacl(struct ksmbd_conn *conn,
+                       pntsd_size += sizeof(struct smb_acl) + nt_size;
+               }
+-              ksmbd_vfs_set_sd_xattr(conn, user_ns,
+-                                     path->dentry, pntsd, pntsd_size);
++              ksmbd_vfs_set_sd_xattr(conn, user_ns, path, pntsd, pntsd_size);
+               kfree(pntsd);
+       }
+@@ -1406,7 +1405,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(user_ns, path->dentry);
++      ksmbd_vfs_remove_acl_xattrs(user_ns, path);
+       /* Update posix acls */
+       if (IS_ENABLED(CONFIG_FS_POSIX_ACL) && fattr.cf_dacls) {
+               rc = set_posix_acl(user_ns, inode,
+@@ -1437,9 +1436,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(user_ns, path->dentry);
+-              ksmbd_vfs_set_sd_xattr(conn, user_ns,
+-                                     path->dentry, pntsd, ntsd_len);
++              ksmbd_vfs_remove_sd_xattrs(user_ns, path);
++              ksmbd_vfs_set_sd_xattr(conn, user_ns, path, pntsd, ntsd_len);
+       }
+ out:
+diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c
+index 6d171f2757f15..e6218c687fa0b 100644
+--- a/fs/smb/server/vfs.c
++++ b/fs/smb/server/vfs.c
+@@ -171,6 +171,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_user_ns(path.mnt), d_inode(path.dentry),
+                        dentry, mode, true);
+@@ -180,6 +184,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;
+ }
+@@ -210,30 +217,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;
++
+       user_ns = mnt_user_ns(path.mnt);
+       mode |= S_IFDIR;
+       err = vfs_mkdir(user_ns, 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(user_ns, 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);
+@@ -444,7 +456,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(user_ns,
+-                               fp->filp->f_path.dentry,
++                               &fp->filp->f_path,
+                                fp->stream.name,
+                                (void *)stream_buf,
+                                size,
+@@ -590,6 +602,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;
++
+       user_ns = mnt_user_ns(path->mnt);
+       if (S_ISDIR(d_inode(path->dentry)->i_mode)) {
+               err = vfs_rmdir(user_ns, d_inode(parent), path->dentry);
+@@ -600,6 +616,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);
+@@ -645,11 +662,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_user_ns(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);
+@@ -695,6 +717,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);
+@@ -758,6 +784,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);
+@@ -898,19 +925,24 @@ ssize_t ksmbd_vfs_getxattr(struct user_namespace *user_ns,
+  * Return:    0 on success, otherwise error
+  */
+ int ksmbd_vfs_setxattr(struct user_namespace *user_ns,
+-                     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(user_ns,
+-                         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;
+ }
+@@ -1014,9 +1046,18 @@ int ksmbd_vfs_fqar_lseek(struct ksmbd_file *fp, loff_t start, loff_t length,
+ }
+ int ksmbd_vfs_remove_xattr(struct user_namespace *user_ns,
+-                         struct dentry *dentry, char *attr_name)
++                         const struct path *path, char *attr_name)
+ {
+-      return vfs_removexattr(user_ns, dentry, attr_name);
++      int err;
++
++      err = mnt_want_write(path->mnt);
++      if (err)
++              return err;
++
++      err = vfs_removexattr(user_ns, path->dentry, attr_name);
++      mnt_drop_write(path->mnt);
++
++      return err;
+ }
+ int ksmbd_vfs_unlink(struct file *filp)
+@@ -1025,6 +1066,10 @@ int ksmbd_vfs_unlink(struct file *filp)
+       struct dentry *dir, *dentry = filp->f_path.dentry;
+       struct user_namespace *user_ns = file_mnt_user_ns(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)
+@@ -1042,6 +1087,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;
+ }
+@@ -1245,13 +1291,13 @@ struct dentry *ksmbd_vfs_kern_path_create(struct ksmbd_work *work,
+ }
+ int ksmbd_vfs_remove_acl_xattrs(struct user_namespace *user_ns,
+-                              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) {
+@@ -1267,25 +1313,25 @@ int ksmbd_vfs_remove_acl_xattrs(struct user_namespace *user_ns,
+                            sizeof(XATTR_NAME_POSIX_ACL_ACCESS) - 1) ||
+                   !strncmp(name, XATTR_NAME_POSIX_ACL_DEFAULT,
+                            sizeof(XATTR_NAME_POSIX_ACL_DEFAULT) - 1)) {
+-                      err = ksmbd_vfs_remove_xattr(user_ns, dentry, name);
++                      err = ksmbd_vfs_remove_xattr(user_ns, path, name);
+                       if (err)
+                               ksmbd_debug(SMB,
+                                           "remove acl xattr failed : %s\n", name);
+               }
+       }
++
+ out:
+       kvfree(xattr_list);
+       return err;
+ }
+-int ksmbd_vfs_remove_sd_xattrs(struct user_namespace *user_ns,
+-                             struct dentry *dentry)
++int ksmbd_vfs_remove_sd_xattrs(struct user_namespace *user_ns, 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) {
+@@ -1298,7 +1344,7 @@ int ksmbd_vfs_remove_sd_xattrs(struct user_namespace *user_ns,
+               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(user_ns, dentry, name);
++                      err = ksmbd_vfs_remove_xattr(user_ns, path, name);
+                       if (err)
+                               ksmbd_debug(SMB, "remove xattr failed : %s\n", name);
+               }
+@@ -1375,13 +1421,14 @@ static struct xattr_smb_acl *ksmbd_vfs_make_xattr_posix_acl(struct user_namespac
+ int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn,
+                          struct user_namespace *user_ns,
+-                         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;
+@@ -1433,7 +1480,7 @@ int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn,
+               goto out;
+       }
+-      rc = ksmbd_vfs_setxattr(user_ns, dentry,
++      rc = ksmbd_vfs_setxattr(user_ns, path,
+                               XATTR_NAME_SD, sd_ndr.data,
+                               sd_ndr.offset, 0);
+       if (rc < 0)
+@@ -1523,7 +1570,7 @@ int ksmbd_vfs_get_sd_xattr(struct ksmbd_conn *conn,
+ }
+ int ksmbd_vfs_set_dos_attrib_xattr(struct user_namespace *user_ns,
+-                                 struct dentry *dentry,
++                                 const struct path *path,
+                                  struct xattr_dos_attrib *da)
+ {
+       struct ndr n;
+@@ -1533,7 +1580,7 @@ int ksmbd_vfs_set_dos_attrib_xattr(struct user_namespace *user_ns,
+       if (err)
+               return err;
+-      err = ksmbd_vfs_setxattr(user_ns, dentry, XATTR_NAME_DOS_ATTRIBUTE,
++      err = ksmbd_vfs_setxattr(user_ns, path, XATTR_NAME_DOS_ATTRIBUTE,
+                                (void *)n.data, n.offset, 0);
+       if (err)
+               ksmbd_debug(SMB, "failed to store dos attribute in xattr\n");
+@@ -1770,10 +1817,11 @@ void ksmbd_vfs_posix_lock_unblock(struct file_lock *flock)
+ }
+ int ksmbd_vfs_set_init_posix_acl(struct user_namespace *user_ns,
+-                               struct inode *inode)
++                               struct path *path)
+ {
+       struct posix_acl_state acl_state;
+       struct posix_acl *acls;
++      struct inode *inode = d_inode(path->dentry);
+       int rc;
+       if (!IS_ENABLED(CONFIG_FS_POSIX_ACL))
+@@ -1802,6 +1850,11 @@ int ksmbd_vfs_set_init_posix_acl(struct user_namespace *user_ns,
+               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(user_ns, inode, ACL_TYPE_ACCESS, acls);
+       if (rc < 0)
+               ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_ACCESS) failed, rc : %d\n",
+@@ -1814,16 +1867,20 @@ int ksmbd_vfs_set_init_posix_acl(struct user_namespace *user_ns,
+                       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 user_namespace *user_ns,
+-                              struct inode *inode, struct inode *parent_inode)
++                              struct path *path, struct inode *parent_inode)
+ {
+       struct posix_acl *acls;
+       struct posix_acl_entry *pace;
++      struct inode *inode = d_inode(path->dentry);
+       int rc, i;
+       if (!IS_ENABLED(CONFIG_FS_POSIX_ACL))
+@@ -1841,6 +1898,10 @@ int ksmbd_vfs_inherit_posix_acl(struct user_namespace *user_ns,
+               }
+       }
++      rc = mnt_want_write(path->mnt);
++      if (rc)
++              goto out_err;
++
+       rc = set_posix_acl(user_ns, inode, ACL_TYPE_ACCESS, acls);
+       if (rc < 0)
+               ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_ACCESS) failed, rc : %d\n",
+@@ -1852,6 +1913,9 @@ int ksmbd_vfs_inherit_posix_acl(struct user_namespace *user_ns,
+                       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/smb/server/vfs.h b/fs/smb/server/vfs.h
+index 7660565ac2770..0a4eb1e1a79a9 100644
+--- a/fs/smb/server/vfs.h
++++ b/fs/smb/server/vfs.h
+@@ -108,12 +108,12 @@ ssize_t ksmbd_vfs_casexattr_len(struct user_namespace *user_ns,
+                               struct dentry *dentry, char *attr_name,
+                               int attr_name_len);
+ int ksmbd_vfs_setxattr(struct user_namespace *user_ns,
+-                     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 user_namespace *user_ns,
+-                         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 user_namespace *user_ns,
+-                              struct dentry *dentry);
+-int ksmbd_vfs_remove_sd_xattrs(struct user_namespace *user_ns,
+-                             struct dentry *dentry);
++                              const struct path *path);
++int ksmbd_vfs_remove_sd_xattrs(struct user_namespace *user_ns, const struct path *path);
+ int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn,
+                          struct user_namespace *user_ns,
+-                         struct dentry *dentry,
++                         const struct path *path,
+                          struct smb_ntsd *pntsd, int len);
+ int ksmbd_vfs_get_sd_xattr(struct ksmbd_conn *conn,
+                          struct user_namespace *user_ns,
+                          struct dentry *dentry,
+                          struct smb_ntsd **pntsd);
+ int ksmbd_vfs_set_dos_attrib_xattr(struct user_namespace *user_ns,
+-                                 struct dentry *dentry,
++                                 const struct path *path,
+                                  struct xattr_dos_attrib *da);
+ int ksmbd_vfs_get_dos_attrib_xattr(struct user_namespace *user_ns,
+                                  struct dentry *dentry,
+                                  struct xattr_dos_attrib *da);
+ int ksmbd_vfs_set_init_posix_acl(struct user_namespace *user_ns,
+-                               struct inode *inode);
++                               struct path *path);
+ int ksmbd_vfs_inherit_posix_acl(struct user_namespace *user_ns,
+-                              struct inode *inode,
++                              struct path *path,
+                               struct inode *parent_inode);
+ #endif /* __KSMBD_VFS_H__ */
+diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c
+index bafb34d4b5bbb..94ad8fa07b46e 100644
+--- a/fs/smb/server/vfs_cache.c
++++ b/fs/smb/server/vfs_cache.c
+@@ -251,7 +251,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_user_ns(filp),
+-                                           filp->f_path.dentry,
++                                           &filp->f_path,
+                                            fp->stream.name);
+               if (err)
+                       pr_err("remove xattr failed : %s\n",
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-add-support-for-read-compound.patch b/queue-6.1/ksmbd-add-support-for-read-compound.patch
new file mode 100644 (file)
index 0000000..7216f25
--- /dev/null
@@ -0,0 +1,1945 @@
+From d1ef2a1ad19ba71f9e08268c0be9c49fa26da808 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:12:54 +0900
+Subject: ksmbd: add support for read compound
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit e2b76ab8b5c9327ab2dae6da05d0752eb2f4771d ]
+
+MacOS sends a compound request including read to the server
+(e.g. open-read-close). So far, ksmbd has not handled read as
+a compound request. For compatibility between ksmbd and an OS that
+supports SMB, This patch provides compound support for read requests.
+
+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/smb/server/auth.c           |  11 +-
+ fs/smb/server/connection.c     |  55 +---
+ fs/smb/server/connection.h     |   2 +-
+ fs/smb/server/ksmbd_work.c     |  91 +++++-
+ fs/smb/server/ksmbd_work.h     |  34 ++-
+ fs/smb/server/oplock.c         |  17 +-
+ fs/smb/server/server.c         |   8 +-
+ fs/smb/server/smb2pdu.c        | 510 ++++++++++++++-------------------
+ fs/smb/server/smb_common.c     |  13 +-
+ fs/smb/server/transport_rdma.c |   4 +-
+ fs/smb/server/vfs.c            |   4 +-
+ fs/smb/server/vfs.h            |   4 +-
+ 12 files changed, 381 insertions(+), 372 deletions(-)
+
+diff --git a/fs/smb/server/auth.c b/fs/smb/server/auth.c
+index 15e5684e328c1..229a6527870d0 100644
+--- a/fs/smb/server/auth.c
++++ b/fs/smb/server/auth.c
+@@ -1032,11 +1032,15 @@ static struct scatterlist *ksmbd_init_sg(struct kvec *iov, unsigned int nvec,
+ {
+       struct scatterlist *sg;
+       unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 20;
+-      int i, nr_entries[3] = {0}, total_entries = 0, sg_idx = 0;
++      int i, *nr_entries, total_entries = 0, sg_idx = 0;
+       if (!nvec)
+               return NULL;
++      nr_entries = kcalloc(nvec, sizeof(int), GFP_KERNEL);
++      if (!nr_entries)
++              return NULL;
++
+       for (i = 0; i < nvec - 1; i++) {
+               unsigned long kaddr = (unsigned long)iov[i + 1].iov_base;
+@@ -1054,8 +1058,10 @@ static struct scatterlist *ksmbd_init_sg(struct kvec *iov, unsigned int nvec,
+       total_entries += 2;
+       sg = kmalloc_array(total_entries, sizeof(struct scatterlist), GFP_KERNEL);
+-      if (!sg)
++      if (!sg) {
++              kfree(nr_entries);
+               return NULL;
++      }
+       sg_init_table(sg, total_entries);
+       smb2_sg_set_buf(&sg[sg_idx++], iov[0].iov_base + 24, assoc_data_len);
+@@ -1089,6 +1095,7 @@ static struct scatterlist *ksmbd_init_sg(struct kvec *iov, unsigned int nvec,
+               }
+       }
+       smb2_sg_set_buf(&sg[sg_idx], sign, SMB2_SIGNATURE_SIZE);
++      kfree(nr_entries);
+       return sg;
+ }
+diff --git a/fs/smb/server/connection.c b/fs/smb/server/connection.c
+index 00a87b4acd5c5..d1f4ed18a227f 100644
+--- a/fs/smb/server/connection.c
++++ b/fs/smb/server/connection.c
+@@ -125,28 +125,22 @@ void ksmbd_conn_enqueue_request(struct ksmbd_work *work)
+       }
+ }
+-int ksmbd_conn_try_dequeue_request(struct ksmbd_work *work)
++void ksmbd_conn_try_dequeue_request(struct ksmbd_work *work)
+ {
+       struct ksmbd_conn *conn = work->conn;
+-      int ret = 1;
+       if (list_empty(&work->request_entry) &&
+           list_empty(&work->async_request_entry))
+-              return 0;
++              return;
+-      if (!work->multiRsp)
+-              atomic_dec(&conn->req_running);
+-      if (!work->multiRsp) {
+-              spin_lock(&conn->request_lock);
+-              list_del_init(&work->request_entry);
+-              spin_unlock(&conn->request_lock);
+-              if (work->asynchronous)
+-                      release_async_work(work);
+-              ret = 0;
+-      }
++      atomic_dec(&conn->req_running);
++      spin_lock(&conn->request_lock);
++      list_del_init(&work->request_entry);
++      spin_unlock(&conn->request_lock);
++      if (work->asynchronous)
++              release_async_work(work);
+       wake_up_all(&conn->req_running_q);
+-      return ret;
+ }
+ void ksmbd_conn_lock(struct ksmbd_conn *conn)
+@@ -195,41 +189,22 @@ void ksmbd_conn_wait_idle(struct ksmbd_conn *conn, u64 sess_id)
+ int ksmbd_conn_write(struct ksmbd_work *work)
+ {
+       struct ksmbd_conn *conn = work->conn;
+-      size_t len = 0;
+       int sent;
+-      struct kvec iov[3];
+-      int iov_idx = 0;
+       if (!work->response_buf) {
+               pr_err("NULL response header\n");
+               return -EINVAL;
+       }
+-      if (work->tr_buf) {
+-              iov[iov_idx] = (struct kvec) { work->tr_buf,
+-                              sizeof(struct smb2_transform_hdr) + 4 };
+-              len += iov[iov_idx++].iov_len;
+-      }
+-
+-      if (work->aux_payload_sz) {
+-              iov[iov_idx] = (struct kvec) { work->response_buf, work->resp_hdr_sz };
+-              len += iov[iov_idx++].iov_len;
+-              iov[iov_idx] = (struct kvec) { work->aux_payload_buf, work->aux_payload_sz };
+-              len += iov[iov_idx++].iov_len;
+-      } else {
+-              if (work->tr_buf)
+-                      iov[iov_idx].iov_len = work->resp_hdr_sz;
+-              else
+-                      iov[iov_idx].iov_len = get_rfc1002_len(work->response_buf) + 4;
+-              iov[iov_idx].iov_base = work->response_buf;
+-              len += iov[iov_idx++].iov_len;
+-      }
++      if (work->send_no_response)
++              return 0;
+       ksmbd_conn_lock(conn);
+-      sent = conn->transport->ops->writev(conn->transport, &iov[0],
+-                                      iov_idx, len,
+-                                      work->need_invalidate_rkey,
+-                                      work->remote_key);
++      sent = conn->transport->ops->writev(conn->transport, work->iov,
++                      work->iov_cnt,
++                      get_rfc1002_len(work->iov[0].iov_base) + 4,
++                      work->need_invalidate_rkey,
++                      work->remote_key);
+       ksmbd_conn_unlock(conn);
+       if (sent < 0) {
+diff --git a/fs/smb/server/connection.h b/fs/smb/server/connection.h
+index 335fdd714d595..3c005246a32e8 100644
+--- a/fs/smb/server/connection.h
++++ b/fs/smb/server/connection.h
+@@ -159,7 +159,7 @@ int ksmbd_conn_rdma_write(struct ksmbd_conn *conn,
+                         struct smb2_buffer_desc_v1 *desc,
+                         unsigned int desc_len);
+ void ksmbd_conn_enqueue_request(struct ksmbd_work *work);
+-int ksmbd_conn_try_dequeue_request(struct ksmbd_work *work);
++void ksmbd_conn_try_dequeue_request(struct ksmbd_work *work);
+ void ksmbd_conn_init_server_callbacks(struct ksmbd_conn_ops *ops);
+ int ksmbd_conn_handler_loop(void *p);
+ int ksmbd_conn_transport_init(void);
+diff --git a/fs/smb/server/ksmbd_work.c b/fs/smb/server/ksmbd_work.c
+index 14b9caebf7a4f..f49c2e01ea9fc 100644
+--- a/fs/smb/server/ksmbd_work.c
++++ b/fs/smb/server/ksmbd_work.c
+@@ -27,18 +27,35 @@ struct ksmbd_work *ksmbd_alloc_work_struct(void)
+               INIT_LIST_HEAD(&work->async_request_entry);
+               INIT_LIST_HEAD(&work->fp_entry);
+               INIT_LIST_HEAD(&work->interim_entry);
++              INIT_LIST_HEAD(&work->aux_read_list);
++              work->iov_alloc_cnt = 4;
++              work->iov = kcalloc(work->iov_alloc_cnt, sizeof(struct kvec),
++                                  GFP_KERNEL);
++              if (!work->iov) {
++                      kmem_cache_free(work_cache, work);
++                      work = NULL;
++              }
+       }
+       return work;
+ }
+ void ksmbd_free_work_struct(struct ksmbd_work *work)
+ {
++      struct aux_read *ar, *tmp;
++
+       WARN_ON(work->saved_cred != NULL);
+       kvfree(work->response_buf);
+-      kvfree(work->aux_payload_buf);
++
++      list_for_each_entry_safe(ar, tmp, &work->aux_read_list, entry) {
++              kvfree(ar->buf);
++              list_del(&ar->entry);
++              kfree(ar);
++      }
++
+       kfree(work->tr_buf);
+       kvfree(work->request_buf);
++      kfree(work->iov);
+       if (work->async_id)
+               ksmbd_release_id(&work->conn->async_ida, work->async_id);
+       kmem_cache_free(work_cache, work);
+@@ -77,3 +94,75 @@ bool ksmbd_queue_work(struct ksmbd_work *work)
+ {
+       return queue_work(ksmbd_wq, &work->work);
+ }
++
++static int ksmbd_realloc_iov_pin(struct ksmbd_work *work, void *ib,
++                               unsigned int ib_len)
++{
++
++      if (work->iov_alloc_cnt <= work->iov_cnt) {
++              struct kvec *new;
++
++              work->iov_alloc_cnt += 4;
++              new = krealloc(work->iov,
++                             sizeof(struct kvec) * work->iov_alloc_cnt,
++                             GFP_KERNEL | __GFP_ZERO);
++              if (!new)
++                      return -ENOMEM;
++              work->iov = new;
++      }
++
++      work->iov[++work->iov_idx].iov_base = ib;
++      work->iov[work->iov_idx].iov_len = ib_len;
++      work->iov_cnt++;
++
++      return 0;
++}
++
++static int __ksmbd_iov_pin_rsp(struct ksmbd_work *work, void *ib, int len,
++                             void *aux_buf, unsigned int aux_size)
++{
++      /* Plus rfc_length size on first iov */
++      if (!work->iov_idx) {
++              work->iov[work->iov_idx].iov_base = work->response_buf;
++              *(__be32 *)work->iov[0].iov_base = 0;
++              work->iov[work->iov_idx].iov_len = 4;
++              work->iov_cnt++;
++      }
++
++      ksmbd_realloc_iov_pin(work, ib, len);
++      inc_rfc1001_len(work->iov[0].iov_base, len);
++
++      if (aux_size) {
++              struct aux_read *ar;
++
++              ksmbd_realloc_iov_pin(work, aux_buf, aux_size);
++              inc_rfc1001_len(work->iov[0].iov_base, aux_size);
++
++              ar = kmalloc(sizeof(struct aux_read), GFP_KERNEL);
++              if (!ar)
++                      return -ENOMEM;
++
++              ar->buf = aux_buf;
++              list_add(&ar->entry, &work->aux_read_list);
++      }
++
++      return 0;
++}
++
++int ksmbd_iov_pin_rsp(struct ksmbd_work *work, void *ib, int len)
++{
++      return __ksmbd_iov_pin_rsp(work, ib, len, NULL, 0);
++}
++
++int ksmbd_iov_pin_rsp_read(struct ksmbd_work *work, void *ib, int len,
++                         void *aux_buf, unsigned int aux_size)
++{
++      return __ksmbd_iov_pin_rsp(work, ib, len, aux_buf, aux_size);
++}
++
++void ksmbd_iov_reset(struct ksmbd_work *work)
++{
++      work->iov_idx = 0;
++      work->iov_cnt = 0;
++      *(__be32 *)work->iov[0].iov_base = 0;
++}
+diff --git a/fs/smb/server/ksmbd_work.h b/fs/smb/server/ksmbd_work.h
+index f8ae6144c0aea..255157eb26dc4 100644
+--- a/fs/smb/server/ksmbd_work.h
++++ b/fs/smb/server/ksmbd_work.h
+@@ -19,6 +19,11 @@ enum {
+       KSMBD_WORK_CLOSED,
+ };
++struct aux_read {
++      void *buf;
++      struct list_head entry;
++};
++
+ /* one of these for every pending CIFS request at the connection */
+ struct ksmbd_work {
+       /* Server corresponding to this mid */
+@@ -31,13 +36,19 @@ struct ksmbd_work {
+       /* Response buffer */
+       void                            *response_buf;
+-      /* Read data buffer */
+-      void                            *aux_payload_buf;
++      struct list_head                aux_read_list;
++
++      struct kvec                     *iov;
++      int                             iov_alloc_cnt;
++      int                             iov_cnt;
++      int                             iov_idx;
+       /* Next cmd hdr in compound req buf*/
+       int                             next_smb2_rcv_hdr_off;
+       /* Next cmd hdr in compound rsp buf*/
+       int                             next_smb2_rsp_hdr_off;
++      /* Current cmd hdr in compound rsp buf*/
++      int                             curr_smb2_rsp_hdr_off;
+       /*
+        * Current Local FID assigned compound response if SMB2 CREATE
+@@ -53,16 +64,11 @@ struct ksmbd_work {
+       unsigned int                    credits_granted;
+       /* response smb header size */
+-      unsigned int                    resp_hdr_sz;
+       unsigned int                    response_sz;
+-      /* Read data count */
+-      unsigned int                    aux_payload_sz;
+       void                            *tr_buf;
+       unsigned char                   state;
+-      /* Multiple responses for one request e.g. SMB ECHO */
+-      bool                            multiRsp:1;
+       /* No response for cancelled request */
+       bool                            send_no_response:1;
+       /* Request is encrypted */
+@@ -95,6 +101,15 @@ static inline void *ksmbd_resp_buf_next(struct ksmbd_work *work)
+       return work->response_buf + work->next_smb2_rsp_hdr_off + 4;
+ }
++/**
++ * ksmbd_resp_buf_curr - Get current buffer on compound response.
++ * @work: smb work containing response buffer
++ */
++static inline void *ksmbd_resp_buf_curr(struct ksmbd_work *work)
++{
++      return work->response_buf + work->curr_smb2_rsp_hdr_off + 4;
++}
++
+ /**
+  * ksmbd_req_buf_next - Get next buffer on compound request.
+  * @work: smb work containing response buffer
+@@ -113,5 +128,8 @@ int ksmbd_work_pool_init(void);
+ int ksmbd_workqueue_init(void);
+ void ksmbd_workqueue_destroy(void);
+ bool ksmbd_queue_work(struct ksmbd_work *work);
+-
++int ksmbd_iov_pin_rsp_read(struct ksmbd_work *work, void *ib, int len,
++                         void *aux_buf, unsigned int aux_size);
++int ksmbd_iov_pin_rsp(struct ksmbd_work *work, void *ib, int len);
++void ksmbd_iov_reset(struct ksmbd_work *work);
+ #endif /* __KSMBD_WORK_H__ */
+diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c
+index c81aee9ce7ec4..4e12e3031bc53 100644
+--- a/fs/smb/server/oplock.c
++++ b/fs/smb/server/oplock.c
+@@ -639,7 +639,6 @@ static void __smb2_oplock_break_noti(struct work_struct *wk)
+ {
+       struct smb2_oplock_break *rsp = NULL;
+       struct ksmbd_work *work = container_of(wk, struct ksmbd_work, work);
+-      struct ksmbd_conn *conn = work->conn;
+       struct oplock_break_info *br_info = work->request_buf;
+       struct smb2_hdr *rsp_hdr;
+       struct ksmbd_file *fp;
+@@ -656,8 +655,6 @@ static void __smb2_oplock_break_noti(struct work_struct *wk)
+       rsp_hdr = smb2_get_msg(work->response_buf);
+       memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2);
+-      *(__be32 *)work->response_buf =
+-              cpu_to_be32(conn->vals->header_size);
+       rsp_hdr->ProtocolId = SMB2_PROTO_NUMBER;
+       rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE;
+       rsp_hdr->CreditRequest = cpu_to_le16(0);
+@@ -684,13 +681,15 @@ static void __smb2_oplock_break_noti(struct work_struct *wk)
+       rsp->PersistentFid = fp->persistent_id;
+       rsp->VolatileFid = fp->volatile_id;
+-      inc_rfc1001_len(work->response_buf, 24);
++      ksmbd_fd_put(work, fp);
++      if (ksmbd_iov_pin_rsp(work, (void *)rsp,
++                            sizeof(struct smb2_oplock_break)))
++              goto out;
+       ksmbd_debug(OPLOCK,
+                   "sending oplock break v_id %llu p_id = %llu lock level = %d\n",
+                   rsp->VolatileFid, rsp->PersistentFid, rsp->OplockLevel);
+-      ksmbd_fd_put(work, fp);
+       ksmbd_conn_write(work);
+ out:
+@@ -751,7 +750,6 @@ static void __smb2_lease_break_noti(struct work_struct *wk)
+       struct smb2_lease_break *rsp = NULL;
+       struct ksmbd_work *work = container_of(wk, struct ksmbd_work, work);
+       struct lease_break_info *br_info = work->request_buf;
+-      struct ksmbd_conn *conn = work->conn;
+       struct smb2_hdr *rsp_hdr;
+       if (allocate_oplock_break_buf(work)) {
+@@ -761,8 +759,6 @@ static void __smb2_lease_break_noti(struct work_struct *wk)
+       rsp_hdr = smb2_get_msg(work->response_buf);
+       memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2);
+-      *(__be32 *)work->response_buf =
+-              cpu_to_be32(conn->vals->header_size);
+       rsp_hdr->ProtocolId = SMB2_PROTO_NUMBER;
+       rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE;
+       rsp_hdr->CreditRequest = cpu_to_le16(0);
+@@ -791,7 +787,9 @@ static void __smb2_lease_break_noti(struct work_struct *wk)
+       rsp->AccessMaskHint = 0;
+       rsp->ShareMaskHint = 0;
+-      inc_rfc1001_len(work->response_buf, 44);
++      if (ksmbd_iov_pin_rsp(work, (void *)rsp,
++                            sizeof(struct smb2_lease_break)))
++              goto out;
+       ksmbd_conn_write(work);
+@@ -845,6 +843,7 @@ static int smb2_lease_break_noti(struct oplock_info *opinfo)
+                       setup_async_work(in_work, NULL, NULL);
+                       smb2_send_interim_resp(in_work, STATUS_PENDING);
+                       list_del(&in_work->interim_entry);
++                      ksmbd_iov_reset(in_work);
+               }
+               INIT_WORK(&work->work, __smb2_lease_break_noti);
+               ksmbd_queue_work(work);
+diff --git a/fs/smb/server/server.c b/fs/smb/server/server.c
+index 0c0db2e614ef6..ff1514c79f162 100644
+--- a/fs/smb/server/server.c
++++ b/fs/smb/server/server.c
+@@ -163,6 +163,7 @@ static void __handle_ksmbd_work(struct ksmbd_work *work,
+ {
+       u16 command = 0;
+       int rc;
++      bool is_chained = false;
+       if (conn->ops->allocate_rsp_buf(work))
+               return;
+@@ -229,14 +230,13 @@ static void __handle_ksmbd_work(struct ksmbd_work *work,
+                       }
+               }
++              is_chained = is_chained_smb2_message(work);
++
+               if (work->sess &&
+                   (work->sess->sign || smb3_11_final_sess_setup_resp(work) ||
+                    conn->ops->is_sign_req(work, command)))
+                       conn->ops->set_sign_rsp(work);
+-      } while (is_chained_smb2_message(work));
+-
+-      if (work->send_no_response)
+-              return;
++      } while (is_chained == true);
+ send:
+       smb3_preauth_hash_rsp(work);
+diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
+index 687e59cb0c8c8..0a40b793cedf4 100644
+--- a/fs/smb/server/smb2pdu.c
++++ b/fs/smb/server/smb2pdu.c
+@@ -144,12 +144,18 @@ void smb2_set_err_rsp(struct ksmbd_work *work)
+               err_rsp = smb2_get_msg(work->response_buf);
+       if (err_rsp->hdr.Status != STATUS_STOPPED_ON_SYMLINK) {
++              int err;
++
+               err_rsp->StructureSize = SMB2_ERROR_STRUCTURE_SIZE2_LE;
+               err_rsp->ErrorContextCount = 0;
+               err_rsp->Reserved = 0;
+               err_rsp->ByteCount = 0;
+               err_rsp->ErrorData[0] = 0;
+-              inc_rfc1001_len(work->response_buf, SMB2_ERROR_STRUCTURE_SIZE2);
++              err = ksmbd_iov_pin_rsp(work, (void *)err_rsp,
++                                work->conn->vals->header_size +
++                                SMB2_ERROR_STRUCTURE_SIZE2);
++              if (err)
++                      work->send_no_response = 1;
+       }
+ }
+@@ -244,9 +250,7 @@ int init_smb2_neg_rsp(struct ksmbd_work *work)
+       struct smb2_hdr *rsp_hdr;
+       struct smb2_negotiate_rsp *rsp;
+       struct ksmbd_conn *conn = work->conn;
+-
+-      *(__be32 *)work->response_buf =
+-              cpu_to_be32(conn->vals->header_size);
++      int err;
+       rsp_hdr = smb2_get_msg(work->response_buf);
+       memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2);
+@@ -285,13 +289,14 @@ int init_smb2_neg_rsp(struct ksmbd_work *work)
+       rsp->SecurityBufferLength = cpu_to_le16(AUTH_GSS_LENGTH);
+       ksmbd_copy_gss_neg_header((char *)(&rsp->hdr) +
+               le16_to_cpu(rsp->SecurityBufferOffset));
+-      inc_rfc1001_len(work->response_buf,
+-                      sizeof(struct smb2_negotiate_rsp) -
+-                      sizeof(struct smb2_hdr) - sizeof(rsp->Buffer) +
+-                      AUTH_GSS_LENGTH);
+       rsp->SecurityMode = SMB2_NEGOTIATE_SIGNING_ENABLED_LE;
+       if (server_conf.signing == KSMBD_CONFIG_OPT_MANDATORY)
+               rsp->SecurityMode |= SMB2_NEGOTIATE_SIGNING_REQUIRED_LE;
++      err = ksmbd_iov_pin_rsp(work, rsp,
++                              sizeof(struct smb2_negotiate_rsp) -
++                              sizeof(rsp->Buffer) + AUTH_GSS_LENGTH);
++      if (err)
++              return err;
+       conn->use_spnego = true;
+       ksmbd_conn_set_need_negotiate(conn);
+@@ -390,11 +395,12 @@ static void init_chained_smb2_rsp(struct ksmbd_work *work)
+       next_hdr_offset = le32_to_cpu(req->NextCommand);
+       new_len = ALIGN(len, 8);
+-      inc_rfc1001_len(work->response_buf,
+-                      sizeof(struct smb2_hdr) + new_len - len);
++      work->iov[work->iov_idx].iov_len += (new_len - len);
++      inc_rfc1001_len(work->response_buf, new_len - len);
+       rsp->NextCommand = cpu_to_le32(new_len);
+       work->next_smb2_rcv_hdr_off += next_hdr_offset;
++      work->curr_smb2_rsp_hdr_off = work->next_smb2_rsp_hdr_off;
+       work->next_smb2_rsp_hdr_off += new_len;
+       ksmbd_debug(SMB,
+                   "Compound req new_len = %d rcv off = %d rsp off = %d\n",
+@@ -470,10 +476,10 @@ bool is_chained_smb2_message(struct ksmbd_work *work)
+               len = len - get_rfc1002_len(work->response_buf);
+               if (len) {
+                       ksmbd_debug(SMB, "padding len %u\n", len);
++                      work->iov[work->iov_idx].iov_len += len;
+                       inc_rfc1001_len(work->response_buf, len);
+-                      if (work->aux_payload_sz)
+-                              work->aux_payload_sz += len;
+               }
++              work->curr_smb2_rsp_hdr_off = work->next_smb2_rsp_hdr_off;
+       }
+       return false;
+ }
+@@ -488,11 +494,8 @@ int init_smb2_rsp_hdr(struct ksmbd_work *work)
+ {
+       struct smb2_hdr *rsp_hdr = smb2_get_msg(work->response_buf);
+       struct smb2_hdr *rcv_hdr = smb2_get_msg(work->request_buf);
+-      struct ksmbd_conn *conn = work->conn;
+       memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2);
+-      *(__be32 *)work->response_buf =
+-              cpu_to_be32(conn->vals->header_size);
+       rsp_hdr->ProtocolId = rcv_hdr->ProtocolId;
+       rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE;
+       rsp_hdr->Command = rcv_hdr->Command;
+@@ -657,7 +660,7 @@ int setup_async_work(struct ksmbd_work *work, void (*fn)(void **), void **arg)
+       struct ksmbd_conn *conn = work->conn;
+       int id;
+-      rsp_hdr = smb2_get_msg(work->response_buf);
++      rsp_hdr = ksmbd_resp_buf_next(work);
+       rsp_hdr->Flags |= SMB2_FLAGS_ASYNC_COMMAND;
+       id = ksmbd_acquire_async_msg_id(&conn->async_ida);
+@@ -707,14 +710,12 @@ void smb2_send_interim_resp(struct ksmbd_work *work, __le32 status)
+ {
+       struct smb2_hdr *rsp_hdr;
+-      rsp_hdr = smb2_get_msg(work->response_buf);
++      rsp_hdr = ksmbd_resp_buf_next(work);
+       smb2_set_err_rsp(work);
+       rsp_hdr->Status = status;
+-      work->multiRsp = 1;
+       ksmbd_conn_write(work);
+       rsp_hdr->Status = 0;
+-      work->multiRsp = 0;
+ }
+ static __le32 smb2_get_reparse_tag_special_file(umode_t mode)
+@@ -821,9 +822,8 @@ static void build_posix_ctxt(struct smb2_posix_neg_context *pneg_ctxt)
+       pneg_ctxt->Name[15] = 0x7C;
+ }
+-static void assemble_neg_contexts(struct ksmbd_conn *conn,
+-                                struct smb2_negotiate_rsp *rsp,
+-                                void *smb2_buf_len)
++static unsigned int assemble_neg_contexts(struct ksmbd_conn *conn,
++                                struct smb2_negotiate_rsp *rsp)
+ {
+       char * const pneg_ctxt = (char *)rsp +
+                       le32_to_cpu(rsp->NegotiateContextOffset);
+@@ -834,7 +834,6 @@ static void assemble_neg_contexts(struct ksmbd_conn *conn,
+                   "assemble SMB2_PREAUTH_INTEGRITY_CAPABILITIES context\n");
+       build_preauth_ctxt((struct smb2_preauth_neg_context *)pneg_ctxt,
+                          conn->preauth_info->Preauth_HashId);
+-      inc_rfc1001_len(smb2_buf_len, AUTH_GSS_PADDING);
+       ctxt_size = sizeof(struct smb2_preauth_neg_context);
+       if (conn->cipher_type) {
+@@ -874,7 +873,7 @@ static void assemble_neg_contexts(struct ksmbd_conn *conn,
+       }
+       rsp->NegotiateContextCount = cpu_to_le16(neg_ctxt_cnt);
+-      inc_rfc1001_len(smb2_buf_len, ctxt_size);
++      return ctxt_size + AUTH_GSS_PADDING;
+ }
+ static __le32 decode_preauth_ctxt(struct ksmbd_conn *conn,
+@@ -1090,7 +1089,7 @@ int smb2_handle_negotiate(struct ksmbd_work *work)
+       struct smb2_negotiate_req *req = smb2_get_msg(work->request_buf);
+       struct smb2_negotiate_rsp *rsp = smb2_get_msg(work->response_buf);
+       int rc = 0;
+-      unsigned int smb2_buf_len, smb2_neg_size;
++      unsigned int smb2_buf_len, smb2_neg_size, neg_ctxt_len = 0;
+       __le32 status;
+       ksmbd_debug(SMB, "Received negotiate request\n");
+@@ -1183,7 +1182,7 @@ int smb2_handle_negotiate(struct ksmbd_work *work)
+                                                conn->preauth_info->Preauth_HashValue);
+               rsp->NegotiateContextOffset =
+                               cpu_to_le32(OFFSET_OF_NEG_CONTEXT);
+-              assemble_neg_contexts(conn, rsp, work->response_buf);
++              neg_ctxt_len = assemble_neg_contexts(conn, rsp);
+               break;
+       case SMB302_PROT_ID:
+               init_smb3_02_server(conn);
+@@ -1233,9 +1232,6 @@ int smb2_handle_negotiate(struct ksmbd_work *work)
+       rsp->SecurityBufferLength = cpu_to_le16(AUTH_GSS_LENGTH);
+       ksmbd_copy_gss_neg_header((char *)(&rsp->hdr) +
+                                 le16_to_cpu(rsp->SecurityBufferOffset));
+-      inc_rfc1001_len(work->response_buf, sizeof(struct smb2_negotiate_rsp) -
+-                      sizeof(struct smb2_hdr) - sizeof(rsp->Buffer) +
+-                       AUTH_GSS_LENGTH);
+       rsp->SecurityMode = SMB2_NEGOTIATE_SIGNING_ENABLED_LE;
+       conn->use_spnego = true;
+@@ -1253,9 +1249,16 @@ int smb2_handle_negotiate(struct ksmbd_work *work)
+       ksmbd_conn_set_need_negotiate(conn);
+ err_out:
++      if (rc)
++              rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES;
++
++      if (!rc)
++              rc = ksmbd_iov_pin_rsp(work, rsp,
++                                     sizeof(struct smb2_negotiate_rsp) -
++                                      sizeof(rsp->Buffer) +
++                                      AUTH_GSS_LENGTH + neg_ctxt_len);
+       if (rc < 0)
+               smb2_set_err_rsp(work);
+-
+       return rc;
+ }
+@@ -1455,7 +1458,6 @@ static int ntlm_authenticate(struct ksmbd_work *work,
+               memcpy((char *)&rsp->hdr.ProtocolId + sz, spnego_blob, spnego_blob_len);
+               rsp->SecurityBufferLength = cpu_to_le16(spnego_blob_len);
+               kfree(spnego_blob);
+-              inc_rfc1001_len(work->response_buf, spnego_blob_len - 1);
+       }
+       user = session_user(conn, req);
+@@ -1601,7 +1603,6 @@ static int krb5_authenticate(struct ksmbd_work *work,
+               return -EINVAL;
+       }
+       rsp->SecurityBufferLength = cpu_to_le16(out_len);
+-      inc_rfc1001_len(work->response_buf, out_len - 1);
+       if ((conn->sign || server_conf.enforced_signing) ||
+           (req->SecurityMode & SMB2_NEGOTIATE_SIGNING_REQUIRED))
+@@ -1673,7 +1674,6 @@ int smb2_sess_setup(struct ksmbd_work *work)
+       rsp->SessionFlags = 0;
+       rsp->SecurityBufferOffset = cpu_to_le16(72);
+       rsp->SecurityBufferLength = 0;
+-      inc_rfc1001_len(work->response_buf, 9);
+       ksmbd_conn_lock(conn);
+       if (!req->hdr.SessionId) {
+@@ -1809,13 +1809,6 @@ int smb2_sess_setup(struct ksmbd_work *work)
+                                       goto out_err;
+                               rsp->hdr.Status =
+                                       STATUS_MORE_PROCESSING_REQUIRED;
+-                              /*
+-                               * Note: here total size -1 is done as an
+-                               * adjustment for 0 size blob
+-                               */
+-                              inc_rfc1001_len(work->response_buf,
+-                                              le16_to_cpu(rsp->SecurityBufferLength) - 1);
+-
+                       } else if (negblob->MessageType == NtLmAuthenticate) {
+                               rc = ntlm_authenticate(work, req, rsp);
+                               if (rc)
+@@ -1900,6 +1893,17 @@ int smb2_sess_setup(struct ksmbd_work *work)
+                               ksmbd_conn_set_need_negotiate(conn);
+                       }
+               }
++      } else {
++              unsigned int iov_len;
++
++              if (rsp->SecurityBufferLength)
++                      iov_len = offsetof(struct smb2_sess_setup_rsp, Buffer) +
++                              le16_to_cpu(rsp->SecurityBufferLength);
++              else
++                      iov_len = sizeof(struct smb2_sess_setup_rsp);
++              rc = ksmbd_iov_pin_rsp(work, rsp, iov_len);
++              if (rc)
++                      rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES;
+       }
+       ksmbd_conn_unlock(conn);
+@@ -1978,13 +1982,16 @@ int smb2_tree_connect(struct ksmbd_work *work)
+               status.tree_conn->posix_extensions = true;
+       rsp->StructureSize = cpu_to_le16(16);
+-      inc_rfc1001_len(work->response_buf, 16);
+ out_err1:
+       rsp->Capabilities = 0;
+       rsp->Reserved = 0;
+       /* default manual caching */
+       rsp->ShareFlags = SMB2_SHAREFLAG_MANUAL_CACHING;
++      rc = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_tree_connect_rsp));
++      if (rc)
++              status.ret = KSMBD_TREE_CONN_STATUS_NOMEM;
++
+       if (!IS_ERR(treename))
+               kfree(treename);
+       if (!IS_ERR(name))
+@@ -2097,20 +2104,27 @@ int smb2_tree_disconnect(struct ksmbd_work *work)
+       struct smb2_tree_disconnect_req *req;
+       struct ksmbd_session *sess = work->sess;
+       struct ksmbd_tree_connect *tcon = work->tcon;
++      int err;
+       WORK_BUFFERS(work, req, rsp);
+-      rsp->StructureSize = cpu_to_le16(4);
+-      inc_rfc1001_len(work->response_buf, 4);
+-
+       ksmbd_debug(SMB, "request\n");
++      rsp->StructureSize = cpu_to_le16(4);
++      err = ksmbd_iov_pin_rsp(work, rsp,
++                              sizeof(struct smb2_tree_disconnect_rsp));
++      if (err) {
++              rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES;
++              smb2_set_err_rsp(work);
++              return err;
++      }
++
+       if (!tcon || test_and_set_bit(TREE_CONN_EXPIRE, &tcon->status)) {
+               ksmbd_debug(SMB, "Invalid tid %d\n", req->hdr.Id.SyncId.TreeId);
+               rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED;
+               smb2_set_err_rsp(work);
+-              return 0;
++              return -ENOENT;
+       }
+       ksmbd_close_tree_conn_fds(work);
+@@ -2132,15 +2146,21 @@ int smb2_session_logoff(struct ksmbd_work *work)
+       struct smb2_logoff_rsp *rsp;
+       struct ksmbd_session *sess;
+       u64 sess_id;
++      int err;
+       WORK_BUFFERS(work, req, rsp);
++      ksmbd_debug(SMB, "request\n");
++
+       sess_id = le64_to_cpu(req->hdr.SessionId);
+       rsp->StructureSize = cpu_to_le16(4);
+-      inc_rfc1001_len(work->response_buf, 4);
+-
+-      ksmbd_debug(SMB, "request\n");
++      err = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_logoff_rsp));
++      if (err) {
++              rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES;
++              smb2_set_err_rsp(work);
++              return err;
++      }
+       ksmbd_all_conn_set_status(sess_id, KSMBD_SESS_NEED_RECONNECT);
+       ksmbd_close_session_fds(work);
+@@ -2155,7 +2175,7 @@ int smb2_session_logoff(struct ksmbd_work *work)
+               ksmbd_debug(SMB, "Invalid tid %d\n", req->hdr.Id.SyncId.TreeId);
+               rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED;
+               smb2_set_err_rsp(work);
+-              return 0;
++              return -ENOENT;
+       }
+       ksmbd_destroy_file_table(&sess->file_table);
+@@ -2216,7 +2236,10 @@ static noinline int create_smb2_pipe(struct ksmbd_work *work)
+       rsp->CreateContextsOffset = 0;
+       rsp->CreateContextsLength = 0;
+-      inc_rfc1001_len(work->response_buf, 88); /* StructureSize - 1*/
++      err = ksmbd_iov_pin_rsp(work, rsp, offsetof(struct smb2_create_rsp, Buffer));
++      if (err)
++              goto out;
++
+       kfree(name);
+       return 0;
+@@ -2598,6 +2621,7 @@ int smb2_open(struct ksmbd_work *work)
+       u64 time;
+       umode_t posix_mode = 0;
+       __le32 daccess, maximal_access = 0;
++      int iov_len = 0;
+       WORK_BUFFERS(work, req, rsp);
+@@ -3249,7 +3273,7 @@ int smb2_open(struct ksmbd_work *work)
+       rsp->CreateContextsOffset = 0;
+       rsp->CreateContextsLength = 0;
+-      inc_rfc1001_len(work->response_buf, 88); /* StructureSize - 1*/
++      iov_len = offsetof(struct smb2_create_rsp, Buffer);
+       /* If lease is request send lease context response */
+       if (opinfo && opinfo->is_lease) {
+@@ -3264,8 +3288,7 @@ int smb2_open(struct ksmbd_work *work)
+               create_lease_buf(rsp->Buffer, opinfo->o_lease);
+               le32_add_cpu(&rsp->CreateContextsLength,
+                            conn->vals->create_lease_size);
+-              inc_rfc1001_len(work->response_buf,
+-                              conn->vals->create_lease_size);
++              iov_len += conn->vals->create_lease_size;
+               next_ptr = &lease_ccontext->Next;
+               next_off = conn->vals->create_lease_size;
+       }
+@@ -3285,8 +3308,7 @@ int smb2_open(struct ksmbd_work *work)
+                               le32_to_cpu(maximal_access));
+               le32_add_cpu(&rsp->CreateContextsLength,
+                            conn->vals->create_mxac_size);
+-              inc_rfc1001_len(work->response_buf,
+-                              conn->vals->create_mxac_size);
++              iov_len += conn->vals->create_mxac_size;
+               if (next_ptr)
+                       *next_ptr = cpu_to_le32(next_off);
+               next_ptr = &mxac_ccontext->Next;
+@@ -3304,8 +3326,7 @@ int smb2_open(struct ksmbd_work *work)
+                               stat.ino, tcon->id);
+               le32_add_cpu(&rsp->CreateContextsLength,
+                            conn->vals->create_disk_id_size);
+-              inc_rfc1001_len(work->response_buf,
+-                              conn->vals->create_disk_id_size);
++              iov_len += conn->vals->create_disk_id_size;
+               if (next_ptr)
+                       *next_ptr = cpu_to_le32(next_off);
+               next_ptr = &disk_id_ccontext->Next;
+@@ -3319,8 +3340,7 @@ int smb2_open(struct ksmbd_work *work)
+                               fp);
+               le32_add_cpu(&rsp->CreateContextsLength,
+                            conn->vals->create_posix_size);
+-              inc_rfc1001_len(work->response_buf,
+-                              conn->vals->create_posix_size);
++              iov_len += conn->vals->create_posix_size;
+               if (next_ptr)
+                       *next_ptr = cpu_to_le32(next_off);
+       }
+@@ -3338,7 +3358,8 @@ int smb2_open(struct ksmbd_work *work)
+       }
+       ksmbd_revert_fsids(work);
+ err_out1:
+-
++      if (!rc)
++              rc = ksmbd_iov_pin_rsp(work, (void *)rsp, iov_len);
+       if (rc) {
+               if (rc == -EINVAL)
+                       rsp->hdr.Status = STATUS_INVALID_PARAMETER;
+@@ -4064,7 +4085,10 @@ int smb2_query_dir(struct ksmbd_work *work)
+               rsp->OutputBufferOffset = cpu_to_le16(0);
+               rsp->OutputBufferLength = cpu_to_le32(0);
+               rsp->Buffer[0] = 0;
+-              inc_rfc1001_len(work->response_buf, 9);
++              rc = ksmbd_iov_pin_rsp(work, (void *)rsp,
++                                     sizeof(struct smb2_query_directory_rsp));
++              if (rc)
++                      goto err_out;
+       } else {
+ no_buf_len:
+               ((struct file_directory_info *)
+@@ -4076,7 +4100,11 @@ int smb2_query_dir(struct ksmbd_work *work)
+               rsp->StructureSize = cpu_to_le16(9);
+               rsp->OutputBufferOffset = cpu_to_le16(72);
+               rsp->OutputBufferLength = cpu_to_le32(d_info.data_count);
+-              inc_rfc1001_len(work->response_buf, 8 + d_info.data_count);
++              rc = ksmbd_iov_pin_rsp(work, (void *)rsp,
++                                     offsetof(struct smb2_query_directory_rsp, Buffer) +
++                                     d_info.data_count);
++              if (rc)
++                      goto err_out;
+       }
+       kfree(srch_ptr);
+@@ -4123,21 +4151,13 @@ int smb2_query_dir(struct ksmbd_work *work)
+  */
+ static int buffer_check_err(int reqOutputBufferLength,
+                           struct smb2_query_info_rsp *rsp,
+-                          void *rsp_org, int infoclass_size)
++                          void *rsp_org)
+ {
+       if (reqOutputBufferLength < le32_to_cpu(rsp->OutputBufferLength)) {
+-              if (reqOutputBufferLength < infoclass_size) {
+-                      pr_err("Invalid Buffer Size Requested\n");
+-                      rsp->hdr.Status = STATUS_INFO_LENGTH_MISMATCH;
+-                      *(__be32 *)rsp_org = cpu_to_be32(sizeof(struct smb2_hdr));
+-                      return -EINVAL;
+-              }
+-
+-              ksmbd_debug(SMB, "Buffer Overflow\n");
+-              rsp->hdr.Status = STATUS_BUFFER_OVERFLOW;
+-              *(__be32 *)rsp_org = cpu_to_be32(sizeof(struct smb2_hdr) +
+-                              reqOutputBufferLength);
+-              rsp->OutputBufferLength = cpu_to_le32(reqOutputBufferLength);
++              pr_err("Invalid Buffer Size Requested\n");
++              rsp->hdr.Status = STATUS_INFO_LENGTH_MISMATCH;
++              *(__be32 *)rsp_org = cpu_to_be32(sizeof(struct smb2_hdr));
++              return -EINVAL;
+       }
+       return 0;
+ }
+@@ -4156,7 +4176,6 @@ static void get_standard_info_pipe(struct smb2_query_info_rsp *rsp,
+       sinfo->Directory = 0;
+       rsp->OutputBufferLength =
+               cpu_to_le32(sizeof(struct smb2_file_standard_info));
+-      inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_standard_info));
+ }
+ static void get_internal_info_pipe(struct smb2_query_info_rsp *rsp, u64 num,
+@@ -4170,7 +4189,6 @@ static void get_internal_info_pipe(struct smb2_query_info_rsp *rsp, u64 num,
+       file_info->IndexNumber = cpu_to_le64(num | (1ULL << 63));
+       rsp->OutputBufferLength =
+               cpu_to_le32(sizeof(struct smb2_file_internal_info));
+-      inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_internal_info));
+ }
+ static int smb2_get_info_file_pipe(struct ksmbd_session *sess,
+@@ -4196,14 +4214,12 @@ static int smb2_get_info_file_pipe(struct ksmbd_session *sess,
+       case FILE_STANDARD_INFORMATION:
+               get_standard_info_pipe(rsp, rsp_org);
+               rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength),
+-                                    rsp, rsp_org,
+-                                    FILE_STANDARD_INFORMATION_SIZE);
++                                    rsp, rsp_org);
+               break;
+       case FILE_INTERNAL_INFORMATION:
+               get_internal_info_pipe(rsp, id, rsp_org);
+               rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength),
+-                                    rsp, rsp_org,
+-                                    FILE_INTERNAL_INFORMATION_SIZE);
++                                    rsp, rsp_org);
+               break;
+       default:
+               ksmbd_debug(SMB, "smb2_info_file_pipe for %u not supported\n",
+@@ -4371,7 +4387,6 @@ static int smb2_get_ea(struct ksmbd_work *work, struct ksmbd_file *fp,
+       if (rsp_data_cnt == 0)
+               rsp->hdr.Status = STATUS_NO_EAS_ON_FILE;
+       rsp->OutputBufferLength = cpu_to_le32(rsp_data_cnt);
+-      inc_rfc1001_len(rsp_org, rsp_data_cnt);
+ out:
+       kvfree(xattr_list);
+       return rc;
+@@ -4386,7 +4401,6 @@ static void get_file_access_info(struct smb2_query_info_rsp *rsp,
+       file_info->AccessFlags = fp->daccess;
+       rsp->OutputBufferLength =
+               cpu_to_le32(sizeof(struct smb2_file_access_info));
+-      inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_access_info));
+ }
+ static int get_file_basic_info(struct smb2_query_info_rsp *rsp,
+@@ -4416,7 +4430,6 @@ static int get_file_basic_info(struct smb2_query_info_rsp *rsp,
+       basic_info->Pad1 = 0;
+       rsp->OutputBufferLength =
+               cpu_to_le32(sizeof(struct smb2_file_basic_info));
+-      inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_basic_info));
+       return 0;
+ }
+@@ -4441,8 +4454,6 @@ static void get_file_standard_info(struct smb2_query_info_rsp *rsp,
+       sinfo->Directory = S_ISDIR(stat.mode) ? 1 : 0;
+       rsp->OutputBufferLength =
+               cpu_to_le32(sizeof(struct smb2_file_standard_info));
+-      inc_rfc1001_len(rsp_org,
+-                      sizeof(struct smb2_file_standard_info));
+ }
+ static void get_file_alignment_info(struct smb2_query_info_rsp *rsp,
+@@ -4454,8 +4465,6 @@ static void get_file_alignment_info(struct smb2_query_info_rsp *rsp,
+       file_info->AlignmentRequirement = 0;
+       rsp->OutputBufferLength =
+               cpu_to_le32(sizeof(struct smb2_file_alignment_info));
+-      inc_rfc1001_len(rsp_org,
+-                      sizeof(struct smb2_file_alignment_info));
+ }
+ static int get_file_all_info(struct ksmbd_work *work,
+@@ -4519,7 +4528,6 @@ static int get_file_all_info(struct ksmbd_work *work,
+       rsp->OutputBufferLength =
+               cpu_to_le32(sizeof(struct smb2_file_all_info) + conv_len - 1);
+       kfree(filename);
+-      inc_rfc1001_len(rsp_org, le32_to_cpu(rsp->OutputBufferLength));
+       return 0;
+ }
+@@ -4542,7 +4550,6 @@ static void get_file_alternate_info(struct ksmbd_work *work,
+       file_info->FileNameLength = cpu_to_le32(conv_len);
+       rsp->OutputBufferLength =
+               cpu_to_le32(sizeof(struct smb2_file_alt_name_info) + conv_len);
+-      inc_rfc1001_len(rsp_org, le32_to_cpu(rsp->OutputBufferLength));
+ }
+ static void get_file_stream_info(struct ksmbd_work *work,
+@@ -4642,7 +4649,6 @@ static void get_file_stream_info(struct ksmbd_work *work,
+       kvfree(xattr_list);
+       rsp->OutputBufferLength = cpu_to_le32(nbytes);
+-      inc_rfc1001_len(rsp_org, nbytes);
+ }
+ static void get_file_internal_info(struct smb2_query_info_rsp *rsp,
+@@ -4657,7 +4663,6 @@ static void get_file_internal_info(struct smb2_query_info_rsp *rsp,
+       file_info->IndexNumber = cpu_to_le64(stat.ino);
+       rsp->OutputBufferLength =
+               cpu_to_le32(sizeof(struct smb2_file_internal_info));
+-      inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_internal_info));
+ }
+ static int get_file_network_open_info(struct smb2_query_info_rsp *rsp,
+@@ -4693,7 +4698,6 @@ static int get_file_network_open_info(struct smb2_query_info_rsp *rsp,
+       file_info->Reserved = cpu_to_le32(0);
+       rsp->OutputBufferLength =
+               cpu_to_le32(sizeof(struct smb2_file_ntwrk_info));
+-      inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_ntwrk_info));
+       return 0;
+ }
+@@ -4705,7 +4709,6 @@ static void get_file_ea_info(struct smb2_query_info_rsp *rsp, void *rsp_org)
+       file_info->EASize = 0;
+       rsp->OutputBufferLength =
+               cpu_to_le32(sizeof(struct smb2_file_ea_info));
+-      inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_ea_info));
+ }
+ static void get_file_position_info(struct smb2_query_info_rsp *rsp,
+@@ -4717,7 +4720,6 @@ static void get_file_position_info(struct smb2_query_info_rsp *rsp,
+       file_info->CurrentByteOffset = cpu_to_le64(fp->filp->f_pos);
+       rsp->OutputBufferLength =
+               cpu_to_le32(sizeof(struct smb2_file_pos_info));
+-      inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_pos_info));
+ }
+ static void get_file_mode_info(struct smb2_query_info_rsp *rsp,
+@@ -4729,7 +4731,6 @@ static void get_file_mode_info(struct smb2_query_info_rsp *rsp,
+       file_info->Mode = fp->coption & FILE_MODE_INFO_MASK;
+       rsp->OutputBufferLength =
+               cpu_to_le32(sizeof(struct smb2_file_mode_info));
+-      inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_mode_info));
+ }
+ static void get_file_compression_info(struct smb2_query_info_rsp *rsp,
+@@ -4751,7 +4752,6 @@ static void get_file_compression_info(struct smb2_query_info_rsp *rsp,
+       rsp->OutputBufferLength =
+               cpu_to_le32(sizeof(struct smb2_file_comp_info));
+-      inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_comp_info));
+ }
+ static int get_file_attribute_tag_info(struct smb2_query_info_rsp *rsp,
+@@ -4770,11 +4770,10 @@ static int get_file_attribute_tag_info(struct smb2_query_info_rsp *rsp,
+       file_info->ReparseTag = 0;
+       rsp->OutputBufferLength =
+               cpu_to_le32(sizeof(struct smb2_file_attr_tag_info));
+-      inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_attr_tag_info));
+       return 0;
+ }
+-static int find_file_posix_info(struct smb2_query_info_rsp *rsp,
++static void find_file_posix_info(struct smb2_query_info_rsp *rsp,
+                               struct ksmbd_file *fp, void *rsp_org)
+ {
+       struct smb311_posix_qinfo *file_info;
+@@ -4812,8 +4811,6 @@ static int find_file_posix_info(struct smb2_query_info_rsp *rsp,
+                 SIDUNIX_GROUP, (struct smb_sid *)&file_info->Sids[16]);
+       rsp->OutputBufferLength = cpu_to_le32(out_buf_len);
+-      inc_rfc1001_len(rsp_org, out_buf_len);
+-      return out_buf_len;
+ }
+ static int smb2_get_info_file(struct ksmbd_work *work,
+@@ -4823,7 +4820,6 @@ static int smb2_get_info_file(struct ksmbd_work *work,
+       struct ksmbd_file *fp;
+       int fileinfoclass = 0;
+       int rc = 0;
+-      int file_infoclass_size;
+       unsigned int id = KSMBD_NO_FID, pid = KSMBD_NO_FID;
+       if (test_share_config_flag(work->tcon->share_conf,
+@@ -4856,85 +4852,69 @@ static int smb2_get_info_file(struct ksmbd_work *work,
+       switch (fileinfoclass) {
+       case FILE_ACCESS_INFORMATION:
+               get_file_access_info(rsp, fp, work->response_buf);
+-              file_infoclass_size = FILE_ACCESS_INFORMATION_SIZE;
+               break;
+       case FILE_BASIC_INFORMATION:
+               rc = get_file_basic_info(rsp, fp, work->response_buf);
+-              file_infoclass_size = FILE_BASIC_INFORMATION_SIZE;
+               break;
+       case FILE_STANDARD_INFORMATION:
+               get_file_standard_info(rsp, fp, work->response_buf);
+-              file_infoclass_size = FILE_STANDARD_INFORMATION_SIZE;
+               break;
+       case FILE_ALIGNMENT_INFORMATION:
+               get_file_alignment_info(rsp, work->response_buf);
+-              file_infoclass_size = FILE_ALIGNMENT_INFORMATION_SIZE;
+               break;
+       case FILE_ALL_INFORMATION:
+               rc = get_file_all_info(work, rsp, fp, work->response_buf);
+-              file_infoclass_size = FILE_ALL_INFORMATION_SIZE;
+               break;
+       case FILE_ALTERNATE_NAME_INFORMATION:
+               get_file_alternate_info(work, rsp, fp, work->response_buf);
+-              file_infoclass_size = FILE_ALTERNATE_NAME_INFORMATION_SIZE;
+               break;
+       case FILE_STREAM_INFORMATION:
+               get_file_stream_info(work, rsp, fp, work->response_buf);
+-              file_infoclass_size = FILE_STREAM_INFORMATION_SIZE;
+               break;
+       case FILE_INTERNAL_INFORMATION:
+               get_file_internal_info(rsp, fp, work->response_buf);
+-              file_infoclass_size = FILE_INTERNAL_INFORMATION_SIZE;
+               break;
+       case FILE_NETWORK_OPEN_INFORMATION:
+               rc = get_file_network_open_info(rsp, fp, work->response_buf);
+-              file_infoclass_size = FILE_NETWORK_OPEN_INFORMATION_SIZE;
+               break;
+       case FILE_EA_INFORMATION:
+               get_file_ea_info(rsp, work->response_buf);
+-              file_infoclass_size = FILE_EA_INFORMATION_SIZE;
+               break;
+       case FILE_FULL_EA_INFORMATION:
+               rc = smb2_get_ea(work, fp, req, rsp, work->response_buf);
+-              file_infoclass_size = FILE_FULL_EA_INFORMATION_SIZE;
+               break;
+       case FILE_POSITION_INFORMATION:
+               get_file_position_info(rsp, fp, work->response_buf);
+-              file_infoclass_size = FILE_POSITION_INFORMATION_SIZE;
+               break;
+       case FILE_MODE_INFORMATION:
+               get_file_mode_info(rsp, fp, work->response_buf);
+-              file_infoclass_size = FILE_MODE_INFORMATION_SIZE;
+               break;
+       case FILE_COMPRESSION_INFORMATION:
+               get_file_compression_info(rsp, fp, work->response_buf);
+-              file_infoclass_size = FILE_COMPRESSION_INFORMATION_SIZE;
+               break;
+       case FILE_ATTRIBUTE_TAG_INFORMATION:
+               rc = get_file_attribute_tag_info(rsp, fp, work->response_buf);
+-              file_infoclass_size = FILE_ATTRIBUTE_TAG_INFORMATION_SIZE;
+               break;
+       case SMB_FIND_FILE_POSIX_INFO:
+               if (!work->tcon->posix_extensions) {
+                       pr_err("client doesn't negotiate with SMB3.1.1 POSIX Extensions\n");
+                       rc = -EOPNOTSUPP;
+               } else {
+-                      file_infoclass_size = find_file_posix_info(rsp, fp,
+-                                      work->response_buf);
++                      find_file_posix_info(rsp, fp, work->response_buf);
+               }
+               break;
+       default:
+@@ -4944,8 +4924,7 @@ static int smb2_get_info_file(struct ksmbd_work *work,
+       }
+       if (!rc)
+               rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength),
+-                                    rsp, work->response_buf,
+-                                    file_infoclass_size);
++                                    rsp, work->response_buf);
+       ksmbd_fd_put(work, fp);
+       return rc;
+ }
+@@ -4961,7 +4940,6 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
+       struct kstatfs stfs;
+       struct path path;
+       int rc = 0, len;
+-      int fs_infoclass_size = 0;
+       if (!share->path)
+               return -EIO;
+@@ -4991,8 +4969,6 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
+               info->DeviceType = cpu_to_le32(stfs.f_type);
+               info->DeviceCharacteristics = cpu_to_le32(0x00000020);
+               rsp->OutputBufferLength = cpu_to_le32(8);
+-              inc_rfc1001_len(work->response_buf, 8);
+-              fs_infoclass_size = FS_DEVICE_INFORMATION_SIZE;
+               break;
+       }
+       case FS_ATTRIBUTE_INFORMATION:
+@@ -5021,8 +4997,6 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
+               info->FileSystemNameLen = cpu_to_le32(len);
+               sz = sizeof(struct filesystem_attribute_info) - 2 + len;
+               rsp->OutputBufferLength = cpu_to_le32(sz);
+-              inc_rfc1001_len(work->response_buf, sz);
+-              fs_infoclass_size = FS_ATTRIBUTE_INFORMATION_SIZE;
+               break;
+       }
+       case FS_VOLUME_INFORMATION:
+@@ -5049,8 +5023,6 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
+               info->Reserved = 0;
+               sz = sizeof(struct filesystem_vol_info) - 2 + len;
+               rsp->OutputBufferLength = cpu_to_le32(sz);
+-              inc_rfc1001_len(work->response_buf, sz);
+-              fs_infoclass_size = FS_VOLUME_INFORMATION_SIZE;
+               break;
+       }
+       case FS_SIZE_INFORMATION:
+@@ -5063,8 +5035,6 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
+               info->SectorsPerAllocationUnit = cpu_to_le32(1);
+               info->BytesPerSector = cpu_to_le32(stfs.f_bsize);
+               rsp->OutputBufferLength = cpu_to_le32(24);
+-              inc_rfc1001_len(work->response_buf, 24);
+-              fs_infoclass_size = FS_SIZE_INFORMATION_SIZE;
+               break;
+       }
+       case FS_FULL_SIZE_INFORMATION:
+@@ -5080,8 +5050,6 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
+               info->SectorsPerAllocationUnit = cpu_to_le32(1);
+               info->BytesPerSector = cpu_to_le32(stfs.f_bsize);
+               rsp->OutputBufferLength = cpu_to_le32(32);
+-              inc_rfc1001_len(work->response_buf, 32);
+-              fs_infoclass_size = FS_FULL_SIZE_INFORMATION_SIZE;
+               break;
+       }
+       case FS_OBJECT_ID_INFORMATION:
+@@ -5101,8 +5069,6 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
+               info->extended_info.rel_date = 0;
+               memcpy(info->extended_info.version_string, "1.1.0", strlen("1.1.0"));
+               rsp->OutputBufferLength = cpu_to_le32(64);
+-              inc_rfc1001_len(work->response_buf, 64);
+-              fs_infoclass_size = FS_OBJECT_ID_INFORMATION_SIZE;
+               break;
+       }
+       case FS_SECTOR_SIZE_INFORMATION:
+@@ -5124,8 +5090,6 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
+               info->ByteOffsetForSectorAlignment = 0;
+               info->ByteOffsetForPartitionAlignment = 0;
+               rsp->OutputBufferLength = cpu_to_le32(28);
+-              inc_rfc1001_len(work->response_buf, 28);
+-              fs_infoclass_size = FS_SECTOR_SIZE_INFORMATION_SIZE;
+               break;
+       }
+       case FS_CONTROL_INFORMATION:
+@@ -5146,8 +5110,6 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
+               info->DefaultQuotaLimit = cpu_to_le64(SMB2_NO_FID);
+               info->Padding = 0;
+               rsp->OutputBufferLength = cpu_to_le32(48);
+-              inc_rfc1001_len(work->response_buf, 48);
+-              fs_infoclass_size = FS_CONTROL_INFORMATION_SIZE;
+               break;
+       }
+       case FS_POSIX_INFORMATION:
+@@ -5167,8 +5129,6 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
+                       info->TotalFileNodes = cpu_to_le64(stfs.f_files);
+                       info->FreeFileNodes = cpu_to_le64(stfs.f_ffree);
+                       rsp->OutputBufferLength = cpu_to_le32(56);
+-                      inc_rfc1001_len(work->response_buf, 56);
+-                      fs_infoclass_size = FS_POSIX_INFORMATION_SIZE;
+               }
+               break;
+       }
+@@ -5177,8 +5137,7 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
+               return -EOPNOTSUPP;
+       }
+       rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength),
+-                            rsp, work->response_buf,
+-                            fs_infoclass_size);
++                            rsp, work->response_buf);
+       path_put(&path);
+       return rc;
+ }
+@@ -5212,7 +5171,6 @@ static int smb2_get_info_sec(struct ksmbd_work *work,
+               secdesclen = sizeof(struct smb_ntsd);
+               rsp->OutputBufferLength = cpu_to_le32(secdesclen);
+-              inc_rfc1001_len(work->response_buf, secdesclen);
+               return 0;
+       }
+@@ -5257,7 +5215,6 @@ static int smb2_get_info_sec(struct ksmbd_work *work,
+               return rc;
+       rsp->OutputBufferLength = cpu_to_le32(secdesclen);
+-      inc_rfc1001_len(work->response_buf, secdesclen);
+       return 0;
+ }
+@@ -5296,6 +5253,14 @@ int smb2_query_info(struct ksmbd_work *work)
+               rc = -EOPNOTSUPP;
+       }
++      if (!rc) {
++              rsp->StructureSize = cpu_to_le16(9);
++              rsp->OutputBufferOffset = cpu_to_le16(72);
++              rc = ksmbd_iov_pin_rsp(work, (void *)rsp,
++                                     offsetof(struct smb2_query_info_rsp, Buffer) +
++                                      le32_to_cpu(rsp->OutputBufferLength));
++      }
++
+       if (rc < 0) {
+               if (rc == -EACCES)
+                       rsp->hdr.Status = STATUS_ACCESS_DENIED;
+@@ -5303,6 +5268,8 @@ int smb2_query_info(struct ksmbd_work *work)
+                       rsp->hdr.Status = STATUS_FILE_CLOSED;
+               else if (rc == -EIO)
+                       rsp->hdr.Status = STATUS_UNEXPECTED_IO_ERROR;
++              else if (rc == -ENOMEM)
++                      rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES;
+               else if (rc == -EOPNOTSUPP || rsp->hdr.Status == 0)
+                       rsp->hdr.Status = STATUS_INVALID_INFO_CLASS;
+               smb2_set_err_rsp(work);
+@@ -5311,9 +5278,6 @@ int smb2_query_info(struct ksmbd_work *work)
+                           rc);
+               return rc;
+       }
+-      rsp->StructureSize = cpu_to_le16(9);
+-      rsp->OutputBufferOffset = cpu_to_le16(72);
+-      inc_rfc1001_len(work->response_buf, 8);
+       return 0;
+ }
+@@ -5344,8 +5308,9 @@ static noinline int smb2_close_pipe(struct ksmbd_work *work)
+       rsp->AllocationSize = 0;
+       rsp->EndOfFile = 0;
+       rsp->Attributes = 0;
+-      inc_rfc1001_len(work->response_buf, 60);
+-      return 0;
++
++      return ksmbd_iov_pin_rsp(work, (void *)rsp,
++                               sizeof(struct smb2_close_rsp));
+ }
+ /**
+@@ -5450,15 +5415,17 @@ int smb2_close(struct ksmbd_work *work)
+       err = ksmbd_close_fd(work, volatile_id);
+ out:
++      if (!err)
++              err = ksmbd_iov_pin_rsp(work, (void *)rsp,
++                                      sizeof(struct smb2_close_rsp));
++
+       if (err) {
+               if (rsp->hdr.Status == 0)
+                       rsp->hdr.Status = STATUS_FILE_CLOSED;
+               smb2_set_err_rsp(work);
+-      } else {
+-              inc_rfc1001_len(work->response_buf, 60);
+       }
+-      return 0;
++      return err;
+ }
+ /**
+@@ -5476,8 +5443,7 @@ int smb2_echo(struct ksmbd_work *work)
+       rsp->StructureSize = cpu_to_le16(4);
+       rsp->Reserved = 0;
+-      inc_rfc1001_len(work->response_buf, 4);
+-      return 0;
++      return ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_echo_rsp));
+ }
+ static int smb2_rename(struct ksmbd_work *work,
+@@ -6069,7 +6035,10 @@ int smb2_set_info(struct ksmbd_work *work)
+               goto err_out;
+       rsp->StructureSize = cpu_to_le16(2);
+-      inc_rfc1001_len(work->response_buf, 2);
++      rc = ksmbd_iov_pin_rsp(work, (void *)rsp,
++                             sizeof(struct smb2_set_info_rsp));
++      if (rc)
++              goto err_out;
+       ksmbd_fd_put(work, fp);
+       return 0;
+@@ -6116,28 +6085,36 @@ static noinline int smb2_read_pipe(struct ksmbd_work *work)
+       id = req->VolatileFileId;
+-      inc_rfc1001_len(work->response_buf, 16);
+       rpc_resp = ksmbd_rpc_read(work->sess, id);
+       if (rpc_resp) {
++              void *aux_payload_buf;
++
+               if (rpc_resp->flags != KSMBD_RPC_OK) {
+                       err = -EINVAL;
+                       goto out;
+               }
+-              work->aux_payload_buf =
++              aux_payload_buf =
+                       kvmalloc(rpc_resp->payload_sz, GFP_KERNEL);
+-              if (!work->aux_payload_buf) {
++              if (!aux_payload_buf) {
+                       err = -ENOMEM;
+                       goto out;
+               }
+-              memcpy(work->aux_payload_buf, rpc_resp->payload,
+-                     rpc_resp->payload_sz);
++              memcpy(aux_payload_buf, rpc_resp->payload, rpc_resp->payload_sz);
+               nbytes = rpc_resp->payload_sz;
+-              work->resp_hdr_sz = get_rfc1002_len(work->response_buf) + 4;
+-              work->aux_payload_sz = nbytes;
+               kvfree(rpc_resp);
++              err = ksmbd_iov_pin_rsp_read(work, (void *)rsp,
++                                           offsetof(struct smb2_read_rsp, Buffer),
++                                           aux_payload_buf, nbytes);
++              if (err)
++                      goto out;
++      } else {
++              err = ksmbd_iov_pin_rsp(work, (void *)rsp,
++                                      offsetof(struct smb2_read_rsp, Buffer));
++              if (err)
++                      goto out;
+       }
+       rsp->StructureSize = cpu_to_le16(17);
+@@ -6146,7 +6123,6 @@ static noinline int smb2_read_pipe(struct ksmbd_work *work)
+       rsp->DataLength = cpu_to_le32(nbytes);
+       rsp->DataRemaining = 0;
+       rsp->Flags = 0;
+-      inc_rfc1001_len(work->response_buf, nbytes);
+       return 0;
+ out:
+@@ -6220,13 +6196,8 @@ int smb2_read(struct ksmbd_work *work)
+       int err = 0;
+       bool is_rdma_channel = false;
+       unsigned int max_read_size = conn->vals->max_read_size;
+-
+-      WORK_BUFFERS(work, req, rsp);
+-      if (work->next_smb2_rcv_hdr_off) {
+-              work->send_no_response = 1;
+-              err = -EOPNOTSUPP;
+-              goto out;
+-      }
++      unsigned int id = KSMBD_NO_FID, pid = KSMBD_NO_FID;
++      void *aux_payload_buf;
+       if (test_share_config_flag(work->tcon->share_conf,
+                                  KSMBD_SHARE_FLAG_PIPE)) {
+@@ -6234,6 +6205,25 @@ int smb2_read(struct ksmbd_work *work)
+               return smb2_read_pipe(work);
+       }
++      if (work->next_smb2_rcv_hdr_off) {
++              req = ksmbd_req_buf_next(work);
++              rsp = ksmbd_resp_buf_next(work);
++              if (!has_file_id(req->VolatileFileId)) {
++                      ksmbd_debug(SMB, "Compound request set FID = %llu\n",
++                                      work->compound_fid);
++                      id = work->compound_fid;
++                      pid = work->compound_pfid;
++              }
++      } else {
++              req = smb2_get_msg(work->request_buf);
++              rsp = smb2_get_msg(work->response_buf);
++      }
++
++      if (!has_file_id(id)) {
++              id = req->VolatileFileId;
++              pid = req->PersistentFileId;
++      }
++
+       if (req->Channel == SMB2_CHANNEL_RDMA_V1_INVALIDATE ||
+           req->Channel == SMB2_CHANNEL_RDMA_V1) {
+               is_rdma_channel = true;
+@@ -6256,7 +6246,7 @@ int smb2_read(struct ksmbd_work *work)
+                       goto out;
+       }
+-      fp = ksmbd_lookup_fd_slow(work, req->VolatileFileId, req->PersistentFileId);
++      fp = ksmbd_lookup_fd_slow(work, id, pid);
+       if (!fp) {
+               err = -ENOENT;
+               goto out;
+@@ -6282,21 +6272,20 @@ int smb2_read(struct ksmbd_work *work)
+       ksmbd_debug(SMB, "filename %pD, offset %lld, len %zu\n",
+                   fp->filp, offset, length);
+-      work->aux_payload_buf = kvzalloc(length, GFP_KERNEL);
+-      if (!work->aux_payload_buf) {
++      aux_payload_buf = kvzalloc(length, GFP_KERNEL);
++      if (!aux_payload_buf) {
+               err = -ENOMEM;
+               goto out;
+       }
+-      nbytes = ksmbd_vfs_read(work, fp, length, &offset);
++      nbytes = ksmbd_vfs_read(work, fp, length, &offset, aux_payload_buf);
+       if (nbytes < 0) {
+               err = nbytes;
+               goto out;
+       }
+       if ((nbytes == 0 && length != 0) || nbytes < mincount) {
+-              kvfree(work->aux_payload_buf);
+-              work->aux_payload_buf = NULL;
++              kvfree(aux_payload_buf);
+               rsp->hdr.Status = STATUS_END_OF_FILE;
+               smb2_set_err_rsp(work);
+               ksmbd_fd_put(work, fp);
+@@ -6309,10 +6298,9 @@ int smb2_read(struct ksmbd_work *work)
+       if (is_rdma_channel == true) {
+               /* write data to the client using rdma channel */
+               remain_bytes = smb2_read_rdma_channel(work, req,
+-                                                    work->aux_payload_buf,
++                                                    aux_payload_buf,
+                                                     nbytes);
+-              kvfree(work->aux_payload_buf);
+-              work->aux_payload_buf = NULL;
++              kvfree(aux_payload_buf);
+               nbytes = 0;
+               if (remain_bytes < 0) {
+@@ -6327,10 +6315,11 @@ int smb2_read(struct ksmbd_work *work)
+       rsp->DataLength = cpu_to_le32(nbytes);
+       rsp->DataRemaining = cpu_to_le32(remain_bytes);
+       rsp->Flags = 0;
+-      inc_rfc1001_len(work->response_buf, 16);
+-      work->resp_hdr_sz = get_rfc1002_len(work->response_buf) + 4;
+-      work->aux_payload_sz = nbytes;
+-      inc_rfc1001_len(work->response_buf, nbytes);
++      err = ksmbd_iov_pin_rsp_read(work, (void *)rsp,
++                                   offsetof(struct smb2_read_rsp, Buffer),
++                                   aux_payload_buf, nbytes);
++      if (err)
++              goto out;
+       ksmbd_fd_put(work, fp);
+       return 0;
+@@ -6413,8 +6402,8 @@ static noinline int smb2_write_pipe(struct ksmbd_work *work)
+       rsp->DataLength = cpu_to_le32(length);
+       rsp->DataRemaining = 0;
+       rsp->Reserved2 = 0;
+-      inc_rfc1001_len(work->response_buf, 16);
+-      return 0;
++      err = ksmbd_iov_pin_rsp(work, (void *)rsp,
++                              offsetof(struct smb2_write_rsp, Buffer));
+ out:
+       if (err) {
+               rsp->hdr.Status = STATUS_INVALID_HANDLE;
+@@ -6570,7 +6559,9 @@ int smb2_write(struct ksmbd_work *work)
+       rsp->DataLength = cpu_to_le32(nbytes);
+       rsp->DataRemaining = 0;
+       rsp->Reserved2 = 0;
+-      inc_rfc1001_len(work->response_buf, 16);
++      err = ksmbd_iov_pin_rsp(work, rsp, offsetof(struct smb2_write_rsp, Buffer));
++      if (err)
++              goto out;
+       ksmbd_fd_put(work, fp);
+       return 0;
+@@ -6617,15 +6608,11 @@ int smb2_flush(struct ksmbd_work *work)
+       rsp->StructureSize = cpu_to_le16(4);
+       rsp->Reserved = 0;
+-      inc_rfc1001_len(work->response_buf, 4);
+-      return 0;
++      return ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_flush_rsp));
+ out:
+-      if (err) {
+-              rsp->hdr.Status = STATUS_INVALID_HANDLE;
+-              smb2_set_err_rsp(work);
+-      }
+-
++      rsp->hdr.Status = STATUS_INVALID_HANDLE;
++      smb2_set_err_rsp(work);
+       return err;
+ }
+@@ -7063,6 +7050,8 @@ int smb2_lock(struct ksmbd_work *work)
+                               list_del(&work->fp_entry);
+                               spin_unlock(&fp->f_lock);
++                              ksmbd_iov_reset(work);
++
+                               if (work->state != KSMBD_WORK_ACTIVE) {
+                                       list_del(&smb_lock->llist);
+                                       spin_lock(&work->conn->llist_lock);
+@@ -7081,7 +7070,6 @@ int smb2_lock(struct ksmbd_work *work)
+                                       }
+                                       init_smb2_rsp_hdr(work);
+-                                      smb2_set_err_rsp(work);
+                                       rsp->hdr.Status =
+                                               STATUS_RANGE_NOT_LOCKED;
+                                       kfree(smb_lock);
+@@ -7116,7 +7104,10 @@ int smb2_lock(struct ksmbd_work *work)
+       ksmbd_debug(SMB, "successful in taking lock\n");
+       rsp->hdr.Status = STATUS_SUCCESS;
+       rsp->Reserved = 0;
+-      inc_rfc1001_len(work->response_buf, 4);
++      err = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_lock_rsp));
++      if (err)
++              goto out;
++
+       ksmbd_fd_put(work, fp);
+       return 0;
+@@ -7912,9 +7903,9 @@ int smb2_ioctl(struct ksmbd_work *work)
+       rsp->Reserved = cpu_to_le16(0);
+       rsp->Flags = cpu_to_le32(0);
+       rsp->Reserved2 = cpu_to_le32(0);
+-      inc_rfc1001_len(work->response_buf, 48 + nbytes);
+-
+-      return 0;
++      ret = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_ioctl_rsp) + nbytes);
++      if (!ret)
++              return ret;
+ out:
+       if (ret == -EACCES)
+@@ -8049,8 +8040,9 @@ static void smb20_oplock_break_ack(struct ksmbd_work *work)
+       rsp->Reserved2 = 0;
+       rsp->VolatileFid = volatile_id;
+       rsp->PersistentFid = persistent_id;
+-      inc_rfc1001_len(work->response_buf, 24);
+-      return;
++      ret = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_oplock_break));
++      if (!ret)
++              return;
+ err_out:
+       opinfo->op_state = OPLOCK_STATE_NONE;
+@@ -8200,8 +8192,9 @@ static void smb21_lease_break_ack(struct ksmbd_work *work)
+       memcpy(rsp->LeaseKey, req->LeaseKey, 16);
+       rsp->LeaseState = lease_state;
+       rsp->LeaseDuration = 0;
+-      inc_rfc1001_len(work->response_buf, 36);
+-      return;
++      ret = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_lease_ack));
++      if (!ret)
++              return;
+ err_out:
+       opinfo->op_state = OPLOCK_STATE_NONE;
+@@ -8339,43 +8332,19 @@ int smb2_check_sign_req(struct ksmbd_work *work)
+ void smb2_set_sign_rsp(struct ksmbd_work *work)
+ {
+       struct smb2_hdr *hdr;
+-      struct smb2_hdr *req_hdr;
+       char signature[SMB2_HMACSHA256_SIZE];
+-      struct kvec iov[2];
+-      size_t len;
++      struct kvec *iov;
+       int n_vec = 1;
+-      hdr = smb2_get_msg(work->response_buf);
+-      if (work->next_smb2_rsp_hdr_off)
+-              hdr = ksmbd_resp_buf_next(work);
+-
+-      req_hdr = ksmbd_req_buf_next(work);
+-
+-      if (!work->next_smb2_rsp_hdr_off) {
+-              len = get_rfc1002_len(work->response_buf);
+-              if (req_hdr->NextCommand)
+-                      len = ALIGN(len, 8);
+-      } else {
+-              len = get_rfc1002_len(work->response_buf) -
+-                      work->next_smb2_rsp_hdr_off;
+-              len = ALIGN(len, 8);
+-      }
+-
+-      if (req_hdr->NextCommand)
+-              hdr->NextCommand = cpu_to_le32(len);
+-
++      hdr = ksmbd_resp_buf_curr(work);
+       hdr->Flags |= SMB2_FLAGS_SIGNED;
+       memset(hdr->Signature, 0, SMB2_SIGNATURE_SIZE);
+-      iov[0].iov_base = (char *)&hdr->ProtocolId;
+-      iov[0].iov_len = len;
+-
+-      if (work->aux_payload_sz) {
+-              iov[0].iov_len -= work->aux_payload_sz;
+-
+-              iov[1].iov_base = work->aux_payload_buf;
+-              iov[1].iov_len = work->aux_payload_sz;
++      if (hdr->Command == SMB2_READ) {
++              iov = &work->iov[work->iov_idx - 1];
+               n_vec++;
++      } else {
++              iov = &work->iov[work->iov_idx];
+       }
+       if (!ksmbd_sign_smb2_pdu(work->conn, work->sess->sess_key, iov, n_vec,
+@@ -8451,29 +8420,14 @@ int smb3_check_sign_req(struct ksmbd_work *work)
+ void smb3_set_sign_rsp(struct ksmbd_work *work)
+ {
+       struct ksmbd_conn *conn = work->conn;
+-      struct smb2_hdr *req_hdr, *hdr;
++      struct smb2_hdr *hdr;
+       struct channel *chann;
+       char signature[SMB2_CMACAES_SIZE];
+-      struct kvec iov[2];
++      struct kvec *iov;
+       int n_vec = 1;
+-      size_t len;
+       char *signing_key;
+-      hdr = smb2_get_msg(work->response_buf);
+-      if (work->next_smb2_rsp_hdr_off)
+-              hdr = ksmbd_resp_buf_next(work);
+-
+-      req_hdr = ksmbd_req_buf_next(work);
+-
+-      if (!work->next_smb2_rsp_hdr_off) {
+-              len = get_rfc1002_len(work->response_buf);
+-              if (req_hdr->NextCommand)
+-                      len = ALIGN(len, 8);
+-      } else {
+-              len = get_rfc1002_len(work->response_buf) -
+-                      work->next_smb2_rsp_hdr_off;
+-              len = ALIGN(len, 8);
+-      }
++      hdr = ksmbd_resp_buf_curr(work);
+       if (conn->binding == false &&
+           le16_to_cpu(hdr->Command) == SMB2_SESSION_SETUP_HE) {
+@@ -8489,21 +8443,18 @@ void smb3_set_sign_rsp(struct ksmbd_work *work)
+       if (!signing_key)
+               return;
+-      if (req_hdr->NextCommand)
+-              hdr->NextCommand = cpu_to_le32(len);
+-
+       hdr->Flags |= SMB2_FLAGS_SIGNED;
+       memset(hdr->Signature, 0, SMB2_SIGNATURE_SIZE);
+-      iov[0].iov_base = (char *)&hdr->ProtocolId;
+-      iov[0].iov_len = len;
+-      if (work->aux_payload_sz) {
+-              iov[0].iov_len -= work->aux_payload_sz;
+-              iov[1].iov_base = work->aux_payload_buf;
+-              iov[1].iov_len = work->aux_payload_sz;
++
++      if (hdr->Command == SMB2_READ) {
++              iov = &work->iov[work->iov_idx - 1];
+               n_vec++;
++      } else {
++              iov = &work->iov[work->iov_idx];
+       }
+-      if (!ksmbd_sign_smb3_pdu(conn, signing_key, iov, n_vec, signature))
++      if (!ksmbd_sign_smb3_pdu(conn, signing_key, iov, n_vec,
++                               signature))
+               memcpy(hdr->Signature, signature, SMB2_SIGNATURE_SIZE);
+ }
+@@ -8570,45 +8521,22 @@ static void fill_transform_hdr(void *tr_buf, char *old_buf, __le16 cipher_type)
+ int smb3_encrypt_resp(struct ksmbd_work *work)
+ {
+-      char *buf = work->response_buf;
+-      struct kvec iov[3];
++      struct kvec *iov = work->iov;
+       int rc = -ENOMEM;
+-      int buf_size = 0, rq_nvec = 2 + (work->aux_payload_sz ? 1 : 0);
++      void *tr_buf;
+-      if (ARRAY_SIZE(iov) < rq_nvec)
+-              return -ENOMEM;
+-
+-      work->tr_buf = kzalloc(sizeof(struct smb2_transform_hdr) + 4, GFP_KERNEL);
+-      if (!work->tr_buf)
++      tr_buf = kzalloc(sizeof(struct smb2_transform_hdr) + 4, GFP_KERNEL);
++      if (!tr_buf)
+               return rc;
+       /* fill transform header */
+-      fill_transform_hdr(work->tr_buf, buf, work->conn->cipher_type);
++      fill_transform_hdr(tr_buf, work->response_buf, work->conn->cipher_type);
+-      iov[0].iov_base = work->tr_buf;
++      iov[0].iov_base = tr_buf;
+       iov[0].iov_len = sizeof(struct smb2_transform_hdr) + 4;
+-      buf_size += iov[0].iov_len - 4;
+-
+-      iov[1].iov_base = buf + 4;
+-      iov[1].iov_len = get_rfc1002_len(buf);
+-      if (work->aux_payload_sz) {
+-              iov[1].iov_len = work->resp_hdr_sz - 4;
+-
+-              iov[2].iov_base = work->aux_payload_buf;
+-              iov[2].iov_len = work->aux_payload_sz;
+-              buf_size += iov[2].iov_len;
+-      }
+-      buf_size += iov[1].iov_len;
+-      work->resp_hdr_sz = iov[1].iov_len;
++      work->tr_buf = tr_buf;
+-      rc = ksmbd_crypt_message(work, iov, rq_nvec, 1);
+-      if (rc)
+-              return rc;
+-
+-      memmove(buf, iov[1].iov_base, iov[1].iov_len);
+-      *(__be32 *)work->tr_buf = cpu_to_be32(buf_size);
+-
+-      return rc;
++      return ksmbd_crypt_message(work, iov, work->iov_idx + 1, 1);
+ }
+ bool smb3_is_transform_hdr(void *buf)
+diff --git a/fs/smb/server/smb_common.c b/fs/smb/server/smb_common.c
+index b6f414a2404b1..d160363c09ebc 100644
+--- a/fs/smb/server/smb_common.c
++++ b/fs/smb/server/smb_common.c
+@@ -319,12 +319,6 @@ static int init_smb1_rsp_hdr(struct ksmbd_work *work)
+       struct smb_hdr *rsp_hdr = (struct smb_hdr *)work->response_buf;
+       struct smb_hdr *rcv_hdr = (struct smb_hdr *)work->request_buf;
+-      /*
+-       * Remove 4 byte direct TCP header.
+-       */
+-      *(__be32 *)work->response_buf =
+-              cpu_to_be32(sizeof(struct smb_hdr) - 4);
+-
+       rsp_hdr->Command = SMB_COM_NEGOTIATE;
+       *(__le32 *)rsp_hdr->Protocol = SMB1_PROTO_NUMBER;
+       rsp_hdr->Flags = SMBFLG_RESPONSE;
+@@ -571,10 +565,11 @@ static int smb_handle_negotiate(struct ksmbd_work *work)
+       ksmbd_debug(SMB, "Unsupported SMB1 protocol\n");
+-      /* Add 2 byte bcc and 2 byte DialectIndex. */
+-      inc_rfc1001_len(work->response_buf, 4);
+-      neg_rsp->hdr.Status.CifsError = STATUS_SUCCESS;
++      if (ksmbd_iov_pin_rsp(work, (void *)neg_rsp,
++                            sizeof(struct smb_negotiate_rsp) - 4))
++              return -ENOMEM;
++      neg_rsp->hdr.Status.CifsError = STATUS_SUCCESS;
+       neg_rsp->hdr.WordCount = 1;
+       neg_rsp->DialectIndex = cpu_to_le16(work->conn->dialect);
+       neg_rsp->ByteCount = 0;
+diff --git a/fs/smb/server/transport_rdma.c b/fs/smb/server/transport_rdma.c
+index 7578200f63b1d..3b269e1f523a1 100644
+--- a/fs/smb/server/transport_rdma.c
++++ b/fs/smb/server/transport_rdma.c
+@@ -1241,14 +1241,12 @@ static int smb_direct_writev(struct ksmbd_transport *t,
+       //FIXME: skip RFC1002 header..
+       buflen -= 4;
+-      iov[0].iov_base += 4;
+-      iov[0].iov_len -= 4;
+       remaining_data_length = buflen;
+       ksmbd_debug(RDMA, "Sending smb (RDMA): smb_len=%u\n", buflen);
+       smb_direct_send_ctx_init(st, &send_ctx, need_invalidate, remote_key);
+-      start = i = 0;
++      start = i = 1;
+       buflen = 0;
+       while (true) {
+               buflen += iov[i].iov_len;
+diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c
+index 1752a6c10bcc9..6f54ea1df0c58 100644
+--- a/fs/smb/server/vfs.c
++++ b/fs/smb/server/vfs.c
+@@ -368,15 +368,15 @@ static int check_lock_range(struct file *filp, loff_t start, loff_t end,
+  * @fid:      file id of open file
+  * @count:    read byte count
+  * @pos:      file pos
++ * @rbuf:     read data buffer
+  *
+  * Return:    number of read bytes on success, otherwise error
+  */
+ int ksmbd_vfs_read(struct ksmbd_work *work, struct ksmbd_file *fp, size_t count,
+-                 loff_t *pos)
++                 loff_t *pos, char *rbuf)
+ {
+       struct file *filp = fp->filp;
+       ssize_t nbytes = 0;
+-      char *rbuf = work->aux_payload_buf;
+       struct inode *inode = file_inode(filp);
+       if (S_ISDIR(inode->i_mode))
+diff --git a/fs/smb/server/vfs.h b/fs/smb/server/vfs.h
+index a7cc0aad6d576..93799ca4cc345 100644
+--- a/fs/smb/server/vfs.h
++++ b/fs/smb/server/vfs.h
+@@ -76,8 +76,8 @@ void ksmbd_vfs_query_maximal_access(struct user_namespace *user_ns,
+                                  struct dentry *dentry, __le32 *daccess);
+ int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode);
+ int ksmbd_vfs_mkdir(struct ksmbd_work *work, const char *name, umode_t mode);
+-int ksmbd_vfs_read(struct ksmbd_work *work, struct ksmbd_file *fp,
+-                 size_t count, loff_t *pos);
++int ksmbd_vfs_read(struct ksmbd_work *work, struct ksmbd_file *fp, size_t count,
++                 loff_t *pos, char *rbuf);
+ 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);
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-add-support-for-surrogate-pair-conversion.patch b/queue-6.1/ksmbd-add-support-for-surrogate-pair-conversion.patch
new file mode 100644 (file)
index 0000000..0ddbbbd
--- /dev/null
@@ -0,0 +1,297 @@
+From 1470f82a7a7b46ab6829e55071e1cad278e5b6cd Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:13:16 +0900
+Subject: ksmbd: add support for surrogate pair conversion
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit 0c180317c654a494fe429adbf7bc9b0793caf9e2 ]
+
+ksmbd is missing supporting to convert filename included surrogate pair
+characters. It triggers a "file or folder does not exist" error in
+Windows client.
+
+[Steps to Reproduce for bug]
+1. Create surrogate pair file
+ touch $(echo -e '\xf0\x9d\x9f\xa3')
+ touch $(echo -e '\xf0\x9d\x9f\xa4')
+
+2. Try to open these files in ksmbd share through Windows client.
+
+This patch update unicode functions not to consider about surrogate pair
+(and IVS).
+
+Reviewed-by: Marios Makassikis <mmakassikis@freebox.fr>
+Tested-by: Marios Makassikis <mmakassikis@freebox.fr>
+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/smb/server/unicode.c | 187 +++++++++++++++++++++++++++++-----------
+ 1 file changed, 138 insertions(+), 49 deletions(-)
+
+diff --git a/fs/smb/server/unicode.c b/fs/smb/server/unicode.c
+index 9ae676906ed39..33fc6d45c0f38 100644
+--- a/fs/smb/server/unicode.c
++++ b/fs/smb/server/unicode.c
+@@ -14,46 +14,10 @@
+ #include "uniupr.h"
+ #include "smb_common.h"
+-/*
+- * smb_utf16_bytes() - how long will a string be after conversion?
+- * @from:     pointer to input string
+- * @maxbytes: don't go past this many bytes of input string
+- * @codepage: destination codepage
+- *
+- * Walk a utf16le string and return the number of bytes that the string will
+- * be after being converted to the given charset, not including any null
+- * termination required. Don't walk past maxbytes in the source buffer.
+- *
+- * Return:    string length after conversion
+- */
+-static int smb_utf16_bytes(const __le16 *from, int maxbytes,
+-                         const struct nls_table *codepage)
+-{
+-      int i;
+-      int charlen, outlen = 0;
+-      int maxwords = maxbytes / 2;
+-      char tmp[NLS_MAX_CHARSET_SIZE];
+-      __u16 ftmp;
+-
+-      for (i = 0; i < maxwords; i++) {
+-              ftmp = get_unaligned_le16(&from[i]);
+-              if (ftmp == 0)
+-                      break;
+-
+-              charlen = codepage->uni2char(ftmp, tmp, NLS_MAX_CHARSET_SIZE);
+-              if (charlen > 0)
+-                      outlen += charlen;
+-              else
+-                      outlen++;
+-      }
+-
+-      return outlen;
+-}
+-
+ /*
+  * cifs_mapchar() - convert a host-endian char to proper char in codepage
+  * @target:   where converted character should be copied
+- * @src_char: 2 byte host-endian source character
++ * @from:     host-endian source string
+  * @cp:               codepage to which character should be converted
+  * @mapchar:  should character be mapped according to mapchars mount option?
+  *
+@@ -64,10 +28,13 @@ static int smb_utf16_bytes(const __le16 *from, int maxbytes,
+  * Return:    string length after conversion
+  */
+ static int
+-cifs_mapchar(char *target, const __u16 src_char, const struct nls_table *cp,
++cifs_mapchar(char *target, const __u16 *from, const struct nls_table *cp,
+            bool mapchar)
+ {
+       int len = 1;
++      __u16 src_char;
++
++      src_char = *from;
+       if (!mapchar)
+               goto cp_convert;
+@@ -105,12 +72,66 @@ cifs_mapchar(char *target, const __u16 src_char, const struct nls_table *cp,
+ cp_convert:
+       len = cp->uni2char(src_char, target, NLS_MAX_CHARSET_SIZE);
+-      if (len <= 0) {
+-              *target = '?';
+-              len = 1;
+-      }
++      if (len <= 0)
++              goto surrogate_pair;
+       goto out;
++
++surrogate_pair:
++      /* convert SURROGATE_PAIR and IVS */
++      if (strcmp(cp->charset, "utf8"))
++              goto unknown;
++      len = utf16s_to_utf8s(from, 3, UTF16_LITTLE_ENDIAN, target, 6);
++      if (len <= 0)
++              goto unknown;
++      return len;
++
++unknown:
++      *target = '?';
++      len = 1;
++      goto out;
++}
++
++/*
++ * smb_utf16_bytes() - compute converted string length
++ * @from:     pointer to input string
++ * @maxbytes: input string length
++ * @codepage: destination codepage
++ *
++ * Walk a utf16le string and return the number of bytes that the string will
++ * be after being converted to the given charset, not including any null
++ * termination required. Don't walk past maxbytes in the source buffer.
++ *
++ * Return:    string length after conversion
++ */
++static int smb_utf16_bytes(const __le16 *from, int maxbytes,
++                         const struct nls_table *codepage)
++{
++      int i, j;
++      int charlen, outlen = 0;
++      int maxwords = maxbytes / 2;
++      char tmp[NLS_MAX_CHARSET_SIZE];
++      __u16 ftmp[3];
++
++      for (i = 0; i < maxwords; i++) {
++              ftmp[0] = get_unaligned_le16(&from[i]);
++              if (ftmp[0] == 0)
++                      break;
++              for (j = 1; j <= 2; j++) {
++                      if (i + j < maxwords)
++                              ftmp[j] = get_unaligned_le16(&from[i + j]);
++                      else
++                              ftmp[j] = 0;
++              }
++
++              charlen = cifs_mapchar(tmp, ftmp, codepage, 0);
++              if (charlen > 0)
++                      outlen += charlen;
++              else
++                      outlen++;
++      }
++
++      return outlen;
+ }
+ /*
+@@ -140,12 +161,12 @@ cifs_mapchar(char *target, const __u16 src_char, const struct nls_table *cp,
+ static int smb_from_utf16(char *to, const __le16 *from, int tolen, int fromlen,
+                         const struct nls_table *codepage, bool mapchar)
+ {
+-      int i, charlen, safelen;
++      int i, j, charlen, safelen;
+       int outlen = 0;
+       int nullsize = nls_nullsize(codepage);
+       int fromwords = fromlen / 2;
+       char tmp[NLS_MAX_CHARSET_SIZE];
+-      __u16 ftmp;
++      __u16 ftmp[3];  /* ftmp[3] = 3array x 2bytes = 6bytes UTF-16 */
+       /*
+        * because the chars can be of varying widths, we need to take care
+@@ -156,9 +177,15 @@ static int smb_from_utf16(char *to, const __le16 *from, int tolen, int fromlen,
+       safelen = tolen - (NLS_MAX_CHARSET_SIZE + nullsize);
+       for (i = 0; i < fromwords; i++) {
+-              ftmp = get_unaligned_le16(&from[i]);
+-              if (ftmp == 0)
++              ftmp[0] = get_unaligned_le16(&from[i]);
++              if (ftmp[0] == 0)
+                       break;
++              for (j = 1; j <= 2; j++) {
++                      if (i + j < fromwords)
++                              ftmp[j] = get_unaligned_le16(&from[i + j]);
++                      else
++                              ftmp[j] = 0;
++              }
+               /*
+                * check to see if converting this character might make the
+@@ -173,6 +200,19 @@ static int smb_from_utf16(char *to, const __le16 *from, int tolen, int fromlen,
+               /* put converted char into 'to' buffer */
+               charlen = cifs_mapchar(&to[outlen], ftmp, codepage, mapchar);
+               outlen += charlen;
++
++              /*
++               * charlen (=bytes of UTF-8 for 1 character)
++               * 4bytes UTF-8(surrogate pair) is charlen=4
++               * (4bytes UTF-16 code)
++               * 7-8bytes UTF-8(IVS) is charlen=3+4 or 4+4
++               * (2 UTF-8 pairs divided to 2 UTF-16 pairs)
++               */
++              if (charlen == 4)
++                      i++;
++              else if (charlen >= 5)
++                      /* 5-6bytes UTF-8 */
++                      i += 2;
+       }
+       /* properly null-terminate string */
+@@ -307,6 +347,9 @@ int smbConvertToUTF16(__le16 *target, const char *source, int srclen,
+       char src_char;
+       __le16 dst_char;
+       wchar_t tmp;
++      wchar_t wchar_to[6];    /* UTF-16 */
++      int ret;
++      unicode_t u;
+       if (!mapchars)
+               return smb_strtoUTF16(target, source, srclen, cp);
+@@ -349,11 +392,57 @@ int smbConvertToUTF16(__le16 *target, const char *source, int srclen,
+                        * if no match, use question mark, which at least in
+                        * some cases serves as wild card
+                        */
+-                      if (charlen < 1) {
+-                              dst_char = cpu_to_le16(0x003f);
+-                              charlen = 1;
++                      if (charlen > 0)
++                              goto ctoUTF16;
++
++                      /* convert SURROGATE_PAIR */
++                      if (strcmp(cp->charset, "utf8"))
++                              goto unknown;
++                      if (*(source + i) & 0x80) {
++                              charlen = utf8_to_utf32(source + i, 6, &u);
++                              if (charlen < 0)
++                                      goto unknown;
++                      } else
++                              goto unknown;
++                      ret  = utf8s_to_utf16s(source + i, charlen,
++                                      UTF16_LITTLE_ENDIAN,
++                                      wchar_to, 6);
++                      if (ret < 0)
++                              goto unknown;
++
++                      i += charlen;
++                      dst_char = cpu_to_le16(*wchar_to);
++                      if (charlen <= 3)
++                              /* 1-3bytes UTF-8 to 2bytes UTF-16 */
++                              put_unaligned(dst_char, &target[j]);
++                      else if (charlen == 4) {
++                              /*
++                               * 4bytes UTF-8(surrogate pair) to 4bytes UTF-16
++                               * 7-8bytes UTF-8(IVS) divided to 2 UTF-16
++                               * (charlen=3+4 or 4+4)
++                               */
++                              put_unaligned(dst_char, &target[j]);
++                              dst_char = cpu_to_le16(*(wchar_to + 1));
++                              j++;
++                              put_unaligned(dst_char, &target[j]);
++                      } else if (charlen >= 5) {
++                              /* 5-6bytes UTF-8 to 6bytes UTF-16 */
++                              put_unaligned(dst_char, &target[j]);
++                              dst_char = cpu_to_le16(*(wchar_to + 1));
++                              j++;
++                              put_unaligned(dst_char, &target[j]);
++                              dst_char = cpu_to_le16(*(wchar_to + 2));
++                              j++;
++                              put_unaligned(dst_char, &target[j]);
+                       }
++                      continue;
++
++unknown:
++                      dst_char = cpu_to_le16(0x003f);
++                      charlen = 1;
+               }
++
++ctoUTF16:
+               /*
+                * character may take more than one byte in the source string,
+                * but will take exactly two bytes in the target string
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-avoid-duplicate-negotiate-ctx-offset-increment.patch b/queue-6.1/ksmbd-avoid-duplicate-negotiate-ctx-offset-increment.patch
new file mode 100644 (file)
index 0000000..addae6c
--- /dev/null
@@ -0,0 +1,110 @@
+From 4924fc112ba07a4b6bda253325d9fcae7447c09b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:12:34 +0900
+Subject: ksmbd: avoid duplicate negotiate ctx offset increments
+
+From: David Disseldorp <ddiss@suse.de>
+
+[ Upstream commit a12a07a85aff72e19520328f78b1c64d2281a1ec ]
+
+Both pneg_ctxt and ctxt_size change in unison, with each adding the
+length of the previously added context, rounded up to an eight byte
+boundary.
+Drop pneg_ctxt increments and instead use the ctxt_size offset when
+passing output pointers to per-context helper functions. This slightly
+simplifies offset tracking and shaves off a few text bytes.
+Before (x86-64 gcc 7.5):
+   text    data     bss     dec     hex filename
+ 213234    8677     672  222583   36577 ksmbd.ko
+
+After:
+   text    data     bss     dec     hex filename
+ 213218    8677     672  222567   36567 ksmbd.ko
+
+Signed-off-by: David Disseldorp <ddiss@suse.de>
+Acked-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/server/smb2pdu.c | 26 ++++++++++----------------
+ 1 file changed, 10 insertions(+), 16 deletions(-)
+
+diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
+index c7d43c83d2335..92e6570972437 100644
+--- a/fs/smb/server/smb2pdu.c
++++ b/fs/smb/server/smb2pdu.c
+@@ -838,7 +838,7 @@ static void assemble_neg_contexts(struct ksmbd_conn *conn,
+                                 struct smb2_negotiate_rsp *rsp,
+                                 void *smb2_buf_len)
+ {
+-      char *pneg_ctxt = (char *)rsp +
++      char * const pneg_ctxt = (char *)rsp +
+                       le32_to_cpu(rsp->NegotiateContextOffset);
+       int neg_ctxt_cnt = 1;
+       int ctxt_size;
+@@ -849,21 +849,17 @@ static void assemble_neg_contexts(struct ksmbd_conn *conn,
+                          conn->preauth_info->Preauth_HashId);
+       inc_rfc1001_len(smb2_buf_len, AUTH_GSS_PADDING);
+       ctxt_size = sizeof(struct smb2_preauth_neg_context);
+-      /* Round to 8 byte boundary */
+-      pneg_ctxt += round_up(sizeof(struct smb2_preauth_neg_context), 8);
+       if (conn->cipher_type) {
++              /* Round to 8 byte boundary */
+               ctxt_size = round_up(ctxt_size, 8);
+               ksmbd_debug(SMB,
+                           "assemble SMB2_ENCRYPTION_CAPABILITIES context\n");
+-              build_encrypt_ctxt((struct smb2_encryption_neg_context *)pneg_ctxt,
++              build_encrypt_ctxt((struct smb2_encryption_neg_context *)
++                                 (pneg_ctxt + ctxt_size),
+                                  conn->cipher_type);
+               neg_ctxt_cnt++;
+               ctxt_size += sizeof(struct smb2_encryption_neg_context) + 2;
+-              /* Round to 8 byte boundary */
+-              pneg_ctxt +=
+-                      round_up(sizeof(struct smb2_encryption_neg_context) + 2,
+-                               8);
+       }
+       if (conn->compress_algorithm) {
+@@ -871,31 +867,29 @@ static void assemble_neg_contexts(struct ksmbd_conn *conn,
+               ksmbd_debug(SMB,
+                           "assemble SMB2_COMPRESSION_CAPABILITIES context\n");
+               /* Temporarily set to SMB3_COMPRESS_NONE */
+-              build_compression_ctxt((struct smb2_compression_capabilities_context *)pneg_ctxt,
++              build_compression_ctxt((struct smb2_compression_capabilities_context *)
++                                     (pneg_ctxt + ctxt_size),
+                                      conn->compress_algorithm);
+               neg_ctxt_cnt++;
+               ctxt_size += sizeof(struct smb2_compression_capabilities_context) + 2;
+-              /* Round to 8 byte boundary */
+-              pneg_ctxt += round_up(sizeof(struct smb2_compression_capabilities_context) + 2,
+-                                    8);
+       }
+       if (conn->posix_ext_supported) {
+               ctxt_size = round_up(ctxt_size, 8);
+               ksmbd_debug(SMB,
+                           "assemble SMB2_POSIX_EXTENSIONS_AVAILABLE context\n");
+-              build_posix_ctxt((struct smb2_posix_neg_context *)pneg_ctxt);
++              build_posix_ctxt((struct smb2_posix_neg_context *)
++                               (pneg_ctxt + ctxt_size));
+               neg_ctxt_cnt++;
+               ctxt_size += sizeof(struct smb2_posix_neg_context);
+-              /* Round to 8 byte boundary */
+-              pneg_ctxt += round_up(sizeof(struct smb2_posix_neg_context), 8);
+       }
+       if (conn->signing_negotiated) {
+               ctxt_size = round_up(ctxt_size, 8);
+               ksmbd_debug(SMB,
+                           "assemble SMB2_SIGNING_CAPABILITIES context\n");
+-              build_sign_cap_ctxt((struct smb2_signing_capabilities *)pneg_ctxt,
++              build_sign_cap_ctxt((struct smb2_signing_capabilities *)
++                                  (pneg_ctxt + ctxt_size),
+                                   conn->signing_algorithm);
+               neg_ctxt_cnt++;
+               ctxt_size += sizeof(struct smb2_signing_capabilities) + 2;
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-avoid-duplicate-opinfo_put-call-on-error-of-sm.patch b/queue-6.1/ksmbd-avoid-duplicate-opinfo_put-call-on-error-of-sm.patch
new file mode 100644 (file)
index 0000000..2b2a55d
--- /dev/null
@@ -0,0 +1,51 @@
+From d50f7b118fea7c3a07bd14b10b93b18408baa4f2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:13:31 +0900
+Subject: ksmbd: avoid duplicate opinfo_put() call on error of
+ smb21_lease_break_ack()
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit 658609d9a618d8881bf549b5893c0ba8fcff4526 ]
+
+opinfo_put() could be called twice on error of smb21_lease_break_ack().
+It will cause UAF issue if opinfo is referenced on other places.
+
+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/smb/server/smb2pdu.c | 10 +++++-----
+ 1 file changed, 5 insertions(+), 5 deletions(-)
+
+diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
+index 23e2c031cf2e5..2eea811fc8cb0 100644
+--- a/fs/smb/server/smb2pdu.c
++++ b/fs/smb/server/smb2pdu.c
+@@ -8219,6 +8219,11 @@ static void smb21_lease_break_ack(struct ksmbd_work *work)
+                           le32_to_cpu(req->LeaseState));
+       }
++      if (ret < 0) {
++              rsp->hdr.Status = err;
++              goto err_out;
++      }
++
+       lease_state = lease->state;
+       opinfo->op_state = OPLOCK_STATE_NONE;
+       wake_up_interruptible_all(&opinfo->oplock_q);
+@@ -8226,11 +8231,6 @@ static void smb21_lease_break_ack(struct ksmbd_work *work)
+       wake_up_interruptible_all(&opinfo->oplock_brk);
+       opinfo_put(opinfo);
+-      if (ret < 0) {
+-              rsp->hdr.Status = err;
+-              goto err_out;
+-      }
+-
+       rsp->StructureSize = cpu_to_le16(36);
+       rsp->Reserved = 0;
+       rsp->Flags = 0;
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-call-putname-after-using-the-last-component.patch b/queue-6.1/ksmbd-call-putname-after-using-the-last-component.patch
new file mode 100644 (file)
index 0000000..34d6b23
--- /dev/null
@@ -0,0 +1,62 @@
+From 0eeb3a5731237857b6fc5caa1ccdbeb9e8930c98 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:12:40 +0900
+Subject: ksmbd: call putname after using the last component
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit 6fe55c2799bc29624770c26f98ba7b06214f43e0 ]
+
+last component point filename struct. Currently putname is called after
+vfs_path_parent_lookup(). And then last component is used for
+lookup_one_qstr_excl(). name in last component is freed by previous
+calling putname(). And It cause file lookup failure when testing
+generic/464 test of xfstest.
+
+Fixes: 74d7970febf7 ("ksmbd: fix racy issue from using ->d_parent and ->d_name")
+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/smb/server/vfs.c | 8 ++++++--
+ 1 file changed, 6 insertions(+), 2 deletions(-)
+
+diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c
+index 5d2bb58d77e80..ebcd5a312f10d 100644
+--- a/fs/smb/server/vfs.c
++++ b/fs/smb/server/vfs.c
+@@ -87,12 +87,14 @@ static int ksmbd_vfs_path_lookup_locked(struct ksmbd_share_config *share_conf,
+       err = vfs_path_parent_lookup(filename, flags,
+                                    &parent_path, &last, &type,
+                                    root_share_path);
+-      putname(filename);
+-      if (err)
++      if (err) {
++              putname(filename);
+               return err;
++      }
+       if (unlikely(type != LAST_NORM)) {
+               path_put(&parent_path);
++              putname(filename);
+               return -ENOENT;
+       }
+@@ -109,12 +111,14 @@ static int ksmbd_vfs_path_lookup_locked(struct ksmbd_share_config *share_conf,
+       path->dentry = d;
+       path->mnt = share_conf->vfs_path.mnt;
+       path_put(&parent_path);
++      putname(filename);
+       return 0;
+ err_out:
+       inode_unlock(parent_path.dentry->d_inode);
+       path_put(&parent_path);
++      putname(filename);
+       return -ENOENT;
+ }
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-change-the-return-value-of-ksmbd_vfs_query_max.patch b/queue-6.1/ksmbd-change-the-return-value-of-ksmbd_vfs_query_max.patch
new file mode 100644 (file)
index 0000000..3fb90a0
--- /dev/null
@@ -0,0 +1,82 @@
+From ef0e9f4605537b84e4fc36e8b95da35e4a409a74 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:12:46 +0900
+Subject: ksmbd: Change the return value of ksmbd_vfs_query_maximal_access to
+ void
+
+From: Lu Hongfei <luhongfei@vivo.com>
+
+[ Upstream commit ccb5889af97c03c67a83fcd649602034578c0d61 ]
+
+The return value of ksmbd_vfs_query_maximal_access is meaningless,
+it is better to modify it to void.
+
+Signed-off-by: Lu Hongfei <luhongfei@vivo.com>
+Acked-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/server/smb2pdu.c | 4 +---
+ fs/smb/server/vfs.c     | 6 +-----
+ fs/smb/server/vfs.h     | 2 +-
+ 3 files changed, 3 insertions(+), 9 deletions(-)
+
+diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
+index f35e06ae25b3b..e8d2c6fc3f37c 100644
+--- a/fs/smb/server/smb2pdu.c
++++ b/fs/smb/server/smb2pdu.c
+@@ -2891,11 +2891,9 @@ int smb2_open(struct ksmbd_work *work)
+               if (!file_present) {
+                       daccess = cpu_to_le32(GENERIC_ALL_FLAGS);
+               } else {
+-                      rc = ksmbd_vfs_query_maximal_access(user_ns,
++                      ksmbd_vfs_query_maximal_access(user_ns,
+                                                           path.dentry,
+                                                           &daccess);
+-                      if (rc)
+-                              goto err_out;
+                       already_permitted = true;
+               }
+               maximal_access = daccess;
+diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c
+index d0a85774a496a..178bcd4d0b209 100644
+--- a/fs/smb/server/vfs.c
++++ b/fs/smb/server/vfs.c
+@@ -122,11 +122,9 @@ static int ksmbd_vfs_path_lookup_locked(struct ksmbd_share_config *share_conf,
+       return -ENOENT;
+ }
+-int ksmbd_vfs_query_maximal_access(struct user_namespace *user_ns,
++void ksmbd_vfs_query_maximal_access(struct user_namespace *user_ns,
+                                  struct dentry *dentry, __le32 *daccess)
+ {
+-      int ret = 0;
+-
+       *daccess = cpu_to_le32(FILE_READ_ATTRIBUTES | READ_CONTROL);
+       if (!inode_permission(user_ns, d_inode(dentry), MAY_OPEN | MAY_WRITE))
+@@ -143,8 +141,6 @@ int ksmbd_vfs_query_maximal_access(struct user_namespace *user_ns,
+       if (!inode_permission(user_ns, d_inode(dentry->d_parent), MAY_EXEC | MAY_WRITE))
+               *daccess |= FILE_DELETE_LE;
+-
+-      return ret;
+ }
+ /**
+diff --git a/fs/smb/server/vfs.h b/fs/smb/server/vfs.h
+index 0a4eb1e1a79a9..3e3c92d22e3eb 100644
+--- a/fs/smb/server/vfs.h
++++ b/fs/smb/server/vfs.h
+@@ -72,7 +72,7 @@ struct ksmbd_kstat {
+ };
+ int ksmbd_vfs_lock_parent(struct dentry *parent, struct dentry *child);
+-int ksmbd_vfs_query_maximal_access(struct user_namespace *user_ns,
++void ksmbd_vfs_query_maximal_access(struct user_namespace *user_ns,
+                                  struct dentry *dentry, __le32 *daccess);
+ int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode);
+ int ksmbd_vfs_mkdir(struct ksmbd_work *work, const char *name, umode_t mode);
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-check-if-a-mount-point-is-crossed-during-path-.patch b/queue-6.1/ksmbd-check-if-a-mount-point-is-crossed-during-path-.patch
new file mode 100644 (file)
index 0000000..85c7679
--- /dev/null
@@ -0,0 +1,303 @@
+From 9b66485417639519d8b34feb4ac3344deed11d67 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:12:52 +0900
+Subject: ksmbd: check if a mount point is crossed during path lookup
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit 2b57a4322b1b14348940744fdc02f9a86cbbdbeb ]
+
+Since commit 74d7970febf7 ("ksmbd: fix racy issue from using ->d_parent and
+->d_name"), ksmbd can not lookup cross mount points. If last component is
+a cross mount point during path lookup, check if it is crossed to follow it
+down. And allow path lookup to cross a mount point when a crossmnt
+parameter is set to 'yes' in smb.conf.
+
+Cc: stable@vger.kernel.org
+Fixes: 74d7970febf7 ("ksmbd: fix racy issue from using ->d_parent and ->d_name")
+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/smb/server/ksmbd_netlink.h |  3 +-
+ fs/smb/server/smb2pdu.c       | 27 +++++++++-------
+ fs/smb/server/vfs.c           | 58 ++++++++++++++++++++---------------
+ fs/smb/server/vfs.h           |  4 +--
+ 4 files changed, 53 insertions(+), 39 deletions(-)
+
+diff --git a/fs/smb/server/ksmbd_netlink.h b/fs/smb/server/ksmbd_netlink.h
+index fb8b2d566efb6..b7521e41402e0 100644
+--- a/fs/smb/server/ksmbd_netlink.h
++++ b/fs/smb/server/ksmbd_netlink.h
+@@ -352,7 +352,8 @@ enum KSMBD_TREE_CONN_STATUS {
+ #define KSMBD_SHARE_FLAG_STREAMS              BIT(11)
+ #define KSMBD_SHARE_FLAG_FOLLOW_SYMLINKS      BIT(12)
+ #define KSMBD_SHARE_FLAG_ACL_XATTR            BIT(13)
+-#define KSMBD_SHARE_FLAG_UPDATE               BIT(14)
++#define KSMBD_SHARE_FLAG_UPDATE                       BIT(14)
++#define KSMBD_SHARE_FLAG_CROSSMNT             BIT(15)
+ /*
+  * Tree connect request flags.
+diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
+index 10d51256858ff..687e59cb0c8c8 100644
+--- a/fs/smb/server/smb2pdu.c
++++ b/fs/smb/server/smb2pdu.c
+@@ -2475,8 +2475,9 @@ static void smb2_update_xattrs(struct ksmbd_tree_connect *tcon,
+       }
+ }
+-static int smb2_creat(struct ksmbd_work *work, struct path *path, char *name,
+-                    int open_flags, umode_t posix_mode, bool is_dir)
++static int smb2_creat(struct ksmbd_work *work, struct path *parent_path,
++                    struct path *path, char *name, int open_flags,
++                    umode_t posix_mode, bool is_dir)
+ {
+       struct ksmbd_tree_connect *tcon = work->tcon;
+       struct ksmbd_share_config *share = tcon->share_conf;
+@@ -2503,7 +2504,7 @@ static int smb2_creat(struct ksmbd_work *work, struct path *path, char *name,
+                       return rc;
+       }
+-      rc = ksmbd_vfs_kern_path_locked(work, name, 0, path, 0);
++      rc = ksmbd_vfs_kern_path_locked(work, name, 0, parent_path, path, 0);
+       if (rc) {
+               pr_err("cannot get linux path (%s), err = %d\n",
+                      name, rc);
+@@ -2573,7 +2574,7 @@ int smb2_open(struct ksmbd_work *work)
+       struct ksmbd_tree_connect *tcon = work->tcon;
+       struct smb2_create_req *req;
+       struct smb2_create_rsp *rsp;
+-      struct path path;
++      struct path path, parent_path;
+       struct ksmbd_share_config *share = tcon->share_conf;
+       struct ksmbd_file *fp = NULL;
+       struct file *filp = NULL;
+@@ -2794,7 +2795,8 @@ int smb2_open(struct ksmbd_work *work)
+               goto err_out1;
+       }
+-      rc = ksmbd_vfs_kern_path_locked(work, name, LOOKUP_NO_SYMLINKS, &path, 1);
++      rc = ksmbd_vfs_kern_path_locked(work, name, LOOKUP_NO_SYMLINKS,
++                                      &parent_path, &path, 1);
+       if (!rc) {
+               file_present = true;
+@@ -2914,7 +2916,8 @@ int smb2_open(struct ksmbd_work *work)
+       /*create file if not present */
+       if (!file_present) {
+-              rc = smb2_creat(work, &path, name, open_flags, posix_mode,
++              rc = smb2_creat(work, &parent_path, &path, name, open_flags,
++                              posix_mode,
+                               req->CreateOptions & FILE_DIRECTORY_FILE_LE);
+               if (rc) {
+                       if (rc == -ENOENT) {
+@@ -3329,8 +3332,9 @@ int smb2_open(struct ksmbd_work *work)
+ err_out:
+       if (file_present || created) {
+-              inode_unlock(d_inode(path.dentry->d_parent));
+-              dput(path.dentry);
++              inode_unlock(d_inode(parent_path.dentry));
++              path_put(&path);
++              path_put(&parent_path);
+       }
+       ksmbd_revert_fsids(work);
+ err_out1:
+@@ -5553,7 +5557,7 @@ static int smb2_create_link(struct ksmbd_work *work,
+                           struct nls_table *local_nls)
+ {
+       char *link_name = NULL, *target_name = NULL, *pathname = NULL;
+-      struct path path;
++      struct path path, parent_path;
+       bool file_present = false;
+       int rc;
+@@ -5583,7 +5587,7 @@ static int smb2_create_link(struct ksmbd_work *work,
+       ksmbd_debug(SMB, "target name is %s\n", target_name);
+       rc = ksmbd_vfs_kern_path_locked(work, link_name, LOOKUP_NO_SYMLINKS,
+-                                      &path, 0);
++                                      &parent_path, &path, 0);
+       if (rc) {
+               if (rc != -ENOENT)
+                       goto out;
+@@ -5613,8 +5617,9 @@ static int smb2_create_link(struct ksmbd_work *work,
+               rc = -EINVAL;
+ out:
+       if (file_present) {
+-              inode_unlock(d_inode(path.dentry->d_parent));
++              inode_unlock(d_inode(parent_path.dentry));
+               path_put(&path);
++              path_put(&parent_path);
+       }
+       if (!IS_ERR(link_name))
+               kfree(link_name);
+diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c
+index 73ce3fb6e4054..1752a6c10bcc9 100644
+--- a/fs/smb/server/vfs.c
++++ b/fs/smb/server/vfs.c
+@@ -64,13 +64,13 @@ int ksmbd_vfs_lock_parent(struct dentry *parent, struct dentry *child)
+ static int ksmbd_vfs_path_lookup_locked(struct ksmbd_share_config *share_conf,
+                                       char *pathname, unsigned int flags,
++                                      struct path *parent_path,
+                                       struct path *path)
+ {
+       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') {
+@@ -85,7 +85,7 @@ static int ksmbd_vfs_path_lookup_locked(struct ksmbd_share_config *share_conf,
+               return PTR_ERR(filename);
+       err = vfs_path_parent_lookup(filename, flags,
+-                                   &parent_path, &last, &type,
++                                   parent_path, &last, &type,
+                                    root_share_path);
+       if (err) {
+               putname(filename);
+@@ -93,13 +93,13 @@ static int ksmbd_vfs_path_lookup_locked(struct ksmbd_share_config *share_conf,
+       }
+       if (unlikely(type != LAST_NORM)) {
+-              path_put(&parent_path);
++              path_put(parent_path);
+               putname(filename);
+               return -ENOENT;
+       }
+-      inode_lock_nested(parent_path.dentry->d_inode, I_MUTEX_PARENT);
+-      d = lookup_one_qstr_excl(&last, parent_path.dentry, 0);
++      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;
+@@ -109,15 +109,22 @@ static int ksmbd_vfs_path_lookup_locked(struct ksmbd_share_config *share_conf,
+       }
+       path->dentry = d;
+-      path->mnt = share_conf->vfs_path.mnt;
+-      path_put(&parent_path);
+-      putname(filename);
++      path->mnt = mntget(parent_path->mnt);
++
++      if (test_share_config_flag(share_conf, KSMBD_SHARE_FLAG_CROSSMNT)) {
++              err = follow_down(path);
++              if (err < 0) {
++                      path_put(path);
++                      goto err_out;
++              }
++      }
++      putname(filename);
+       return 0;
+ err_out:
+-      inode_unlock(parent_path.dentry->d_inode);
+-      path_put(&parent_path);
++      inode_unlock(d_inode(parent_path->dentry));
++      path_put(parent_path);
+       putname(filename);
+       return -ENOENT;
+ }
+@@ -1196,14 +1203,14 @@ static int ksmbd_vfs_lookup_in_dir(const struct path *dir, char *name,
+  * Return:    0 on success, otherwise error
+  */
+ int ksmbd_vfs_kern_path_locked(struct ksmbd_work *work, char *name,
+-                             unsigned int flags, struct path *path,
+-                             bool caseless)
++                             unsigned int flags, struct path *parent_path,
++                             struct path *path, bool caseless)
+ {
+       struct ksmbd_share_config *share_conf = work->tcon->share_conf;
+       int err;
+-      struct path parent_path;
+-      err = ksmbd_vfs_path_lookup_locked(share_conf, name, flags, path);
++      err = ksmbd_vfs_path_lookup_locked(share_conf, name, flags, parent_path,
++                                         path);
+       if (!err)
+               return 0;
+@@ -1218,10 +1225,10 @@ int ksmbd_vfs_kern_path_locked(struct ksmbd_work *work, char *name,
+               path_len = strlen(filepath);
+               remain_len = path_len;
+-              parent_path = share_conf->vfs_path;
+-              path_get(&parent_path);
++              *parent_path = share_conf->vfs_path;
++              path_get(parent_path);
+-              while (d_can_lookup(parent_path.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;
+@@ -1230,7 +1237,7 @@ int ksmbd_vfs_kern_path_locked(struct ksmbd_work *work, char *name,
+                       if (filename_len == 0)
+                               break;
+-                      err = ksmbd_vfs_lookup_in_dir(&parent_path, filename,
++                      err = ksmbd_vfs_lookup_in_dir(parent_path, filename,
+                                                     filename_len,
+                                                     work->conn->um);
+                       if (err)
+@@ -1247,8 +1254,8 @@ int ksmbd_vfs_kern_path_locked(struct ksmbd_work *work, char *name,
+                               goto out2;
+                       else if (is_last)
+                               goto out1;
+-                      path_put(&parent_path);
+-                      parent_path = *path;
++                      path_put(parent_path);
++                      *parent_path = *path;
+                       next[0] = '/';
+                       remain_len -= filename_len + 1;
+@@ -1256,16 +1263,17 @@ int ksmbd_vfs_kern_path_locked(struct ksmbd_work *work, char *name,
+               err = -EINVAL;
+ out2:
+-              path_put(&parent_path);
++              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);
++              err = ksmbd_vfs_lock_parent(parent_path->dentry, path->dentry);
++              if (err) {
++                      path_put(path);
++                      path_put(parent_path);
++              }
+       }
+       return err;
+ }
+diff --git a/fs/smb/server/vfs.h b/fs/smb/server/vfs.h
+index 3e3c92d22e3eb..a7cc0aad6d576 100644
+--- a/fs/smb/server/vfs.h
++++ b/fs/smb/server/vfs.h
+@@ -115,8 +115,8 @@ int ksmbd_vfs_xattr_stream_name(char *stream_name, char **xattr_stream_name,
+ int ksmbd_vfs_remove_xattr(struct user_namespace *user_ns,
+                          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);
++                             unsigned int flags, struct path *parent_path,
++                             struct path *path, bool caseless);
+ struct dentry *ksmbd_vfs_kern_path_create(struct ksmbd_work *work,
+                                         const char *name,
+                                         unsigned int flags,
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-check-iov-vector-index-in-ksmbd_conn_write.patch b/queue-6.1/ksmbd-check-iov-vector-index-in-ksmbd_conn_write.patch
new file mode 100644 (file)
index 0000000..0026107
--- /dev/null
@@ -0,0 +1,38 @@
+From 85ea5bd41f9df019d6e25cefc8a9c69670d6ee9c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:13:03 +0900
+Subject: ksmbd: check iov vector index in ksmbd_conn_write()
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit 73f949ea87c7d697210653501ca21efe57295327 ]
+
+If ->iov_idx is zero, This means that the iov vector for the response
+was not added during the request process. In other words, it means that
+there is a problem in generating a response, So this patch return as
+an error to avoid NULL pointer dereferencing problem.
+
+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/smb/server/connection.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/fs/smb/server/connection.c b/fs/smb/server/connection.c
+index d1f4ed18a227f..4b38c3a285f60 100644
+--- a/fs/smb/server/connection.c
++++ b/fs/smb/server/connection.c
+@@ -199,6 +199,9 @@ int ksmbd_conn_write(struct ksmbd_work *work)
+       if (work->send_no_response)
+               return 0;
++      if (!work->iov_idx)
++              return -EINVAL;
++
+       ksmbd_conn_lock(conn);
+       sent = conn->transport->ops->writev(conn->transport, work->iov,
+                       work->iov_cnt,
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-convert-to-use-sysfs_emit-sysfs_emit_at-apis.patch b/queue-6.1/ksmbd-convert-to-use-sysfs_emit-sysfs_emit_at-apis.patch
new file mode 100644 (file)
index 0000000..26e7fe4
--- /dev/null
@@ -0,0 +1,67 @@
+From 343c46a61f2feaf71ed606c915931fa11c370a7b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:12:24 +0900
+Subject: ksmbd: Convert to use sysfs_emit()/sysfs_emit_at() APIs
+
+From: ye xingchen <ye.xingchen@zte.com.cn>
+
+[ Upstream commit 72ee45fd46d0d3578c4e6046f66fae3218543ce3 ]
+
+Follow the advice of the Documentation/filesystems/sysfs.rst and show()
+should only use sysfs_emit() or sysfs_emit_at() when formatting the
+value to be returned to user space.
+
+Signed-off-by: ye xingchen <ye.xingchen@zte.com.cn>
+Reviewed-by: Sergey Senozhatsky <senozhatsky@chromium.org>
+Acked-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/server/server.c | 20 ++++++--------------
+ 1 file changed, 6 insertions(+), 14 deletions(-)
+
+diff --git a/fs/smb/server/server.c b/fs/smb/server/server.c
+index 9804cabe72a84..0c0db2e614ef6 100644
+--- a/fs/smb/server/server.c
++++ b/fs/smb/server/server.c
+@@ -442,11 +442,9 @@ static ssize_t stats_show(struct class *class, struct class_attribute *attr,
+               "reset",
+               "shutdown"
+       };
+-
+-      ssize_t sz = scnprintf(buf, PAGE_SIZE, "%d %s %d %lu\n", stats_version,
+-                             state[server_conf.state], server_conf.tcp_port,
+-                             server_conf.ipc_last_active / HZ);
+-      return sz;
++      return sysfs_emit(buf, "%d %s %d %lu\n", stats_version,
++                        state[server_conf.state], server_conf.tcp_port,
++                        server_conf.ipc_last_active / HZ);
+ }
+ static ssize_t kill_server_store(struct class *class,
+@@ -478,19 +476,13 @@ static ssize_t debug_show(struct class *class, struct class_attribute *attr,
+       for (i = 0; i < ARRAY_SIZE(debug_type_strings); i++) {
+               if ((ksmbd_debug_types >> i) & 1) {
+-                      pos = scnprintf(buf + sz,
+-                                      PAGE_SIZE - sz,
+-                                      "[%s] ",
+-                                      debug_type_strings[i]);
++                      pos = sysfs_emit_at(buf, sz, "[%s] ", debug_type_strings[i]);
+               } else {
+-                      pos = scnprintf(buf + sz,
+-                                      PAGE_SIZE - sz,
+-                                      "%s ",
+-                                      debug_type_strings[i]);
++                      pos = sysfs_emit_at(buf, sz, "%s ", debug_type_strings[i]);
+               }
+               sz += pos;
+       }
+-      sz += scnprintf(buf + sz, PAGE_SIZE - sz, "\n");
++      sz += sysfs_emit_at(buf, sz, "\n");
+       return sz;
+ }
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-delete-asynchronous-work-from-list.patch b/queue-6.1/ksmbd-delete-asynchronous-work-from-list.patch
new file mode 100644 (file)
index 0000000..983a301
--- /dev/null
@@ -0,0 +1,172 @@
+From c818867348319fc17476d7cf610f4ea3e3bfd84e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:12:32 +0900
+Subject: ksmbd: delete asynchronous work from list
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit 3a9b557f44ea8f216aab515a7db20e23f0eb51b9 ]
+
+When smb2_lock request is canceled by smb2_cancel or smb2_close(),
+ksmbd is missing deleting async_request_entry async_requests list.
+Because calling init_smb2_rsp_hdr() in smb2_lock() mark ->synchronous
+as true and then it will not be deleted in
+ksmbd_conn_try_dequeue_request(). This patch add release_async_work() to
+release the ones allocated for async work.
+
+Cc: stable@vger.kernel.org
+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/smb/server/connection.c | 12 +++++-------
+ fs/smb/server/ksmbd_work.h |  2 +-
+ fs/smb/server/smb2pdu.c    | 33 +++++++++++++++++++++------------
+ fs/smb/server/smb2pdu.h    |  1 +
+ 4 files changed, 28 insertions(+), 20 deletions(-)
+
+diff --git a/fs/smb/server/connection.c b/fs/smb/server/connection.c
+index ffbf14d024191..00a87b4acd5c5 100644
+--- a/fs/smb/server/connection.c
++++ b/fs/smb/server/connection.c
+@@ -114,10 +114,8 @@ void ksmbd_conn_enqueue_request(struct ksmbd_work *work)
+       struct ksmbd_conn *conn = work->conn;
+       struct list_head *requests_queue = NULL;
+-      if (conn->ops->get_cmd_val(work) != SMB2_CANCEL_HE) {
++      if (conn->ops->get_cmd_val(work) != SMB2_CANCEL_HE)
+               requests_queue = &conn->requests;
+-              work->synchronous = true;
+-      }
+       if (requests_queue) {
+               atomic_inc(&conn->req_running);
+@@ -138,14 +136,14 @@ int ksmbd_conn_try_dequeue_request(struct ksmbd_work *work)
+       if (!work->multiRsp)
+               atomic_dec(&conn->req_running);
+-      spin_lock(&conn->request_lock);
+       if (!work->multiRsp) {
++              spin_lock(&conn->request_lock);
+               list_del_init(&work->request_entry);
+-              if (!work->synchronous)
+-                      list_del_init(&work->async_request_entry);
++              spin_unlock(&conn->request_lock);
++              if (work->asynchronous)
++                      release_async_work(work);
+               ret = 0;
+       }
+-      spin_unlock(&conn->request_lock);
+       wake_up_all(&conn->req_running_q);
+       return ret;
+diff --git a/fs/smb/server/ksmbd_work.h b/fs/smb/server/ksmbd_work.h
+index 3234f2cf6327c..f8ae6144c0aea 100644
+--- a/fs/smb/server/ksmbd_work.h
++++ b/fs/smb/server/ksmbd_work.h
+@@ -68,7 +68,7 @@ struct ksmbd_work {
+       /* Request is encrypted */
+       bool                            encrypted:1;
+       /* Is this SYNC or ASYNC ksmbd_work */
+-      bool                            synchronous:1;
++      bool                            asynchronous:1;
+       bool                            need_invalidate_rkey:1;
+       unsigned int                    remote_key;
+diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
+index d3939fd481497..ee128f5d38c35 100644
+--- a/fs/smb/server/smb2pdu.c
++++ b/fs/smb/server/smb2pdu.c
+@@ -508,12 +508,6 @@ int init_smb2_rsp_hdr(struct ksmbd_work *work)
+       rsp_hdr->SessionId = rcv_hdr->SessionId;
+       memcpy(rsp_hdr->Signature, rcv_hdr->Signature, 16);
+-      work->synchronous = true;
+-      if (work->async_id) {
+-              ksmbd_release_id(&conn->async_ida, work->async_id);
+-              work->async_id = 0;
+-      }
+-
+       return 0;
+ }
+@@ -671,7 +665,7 @@ int setup_async_work(struct ksmbd_work *work, void (*fn)(void **), void **arg)
+               pr_err("Failed to alloc async message id\n");
+               return id;
+       }
+-      work->synchronous = false;
++      work->asynchronous = true;
+       work->async_id = id;
+       rsp_hdr->Id.AsyncId = cpu_to_le64(id);
+@@ -691,6 +685,24 @@ int setup_async_work(struct ksmbd_work *work, void (*fn)(void **), void **arg)
+       return 0;
+ }
++void release_async_work(struct ksmbd_work *work)
++{
++      struct ksmbd_conn *conn = work->conn;
++
++      spin_lock(&conn->request_lock);
++      list_del_init(&work->async_request_entry);
++      spin_unlock(&conn->request_lock);
++
++      work->asynchronous = 0;
++      work->cancel_fn = NULL;
++      kfree(work->cancel_argv);
++      work->cancel_argv = NULL;
++      if (work->async_id) {
++              ksmbd_release_id(&conn->async_ida, work->async_id);
++              work->async_id = 0;
++      }
++}
++
+ void smb2_send_interim_resp(struct ksmbd_work *work, __le32 status)
+ {
+       struct smb2_hdr *rsp_hdr;
+@@ -7153,13 +7165,9 @@ int smb2_lock(struct ksmbd_work *work)
+                               ksmbd_vfs_posix_lock_wait(flock);
+-                              spin_lock(&work->conn->request_lock);
+                               spin_lock(&fp->f_lock);
+                               list_del(&work->fp_entry);
+-                              work->cancel_fn = NULL;
+-                              kfree(argv);
+                               spin_unlock(&fp->f_lock);
+-                              spin_unlock(&work->conn->request_lock);
+                               if (work->state != KSMBD_WORK_ACTIVE) {
+                                       list_del(&smb_lock->llist);
+@@ -7177,6 +7185,7 @@ int smb2_lock(struct ksmbd_work *work)
+                                               work->send_no_response = 1;
+                                               goto out;
+                                       }
++
+                                       init_smb2_rsp_hdr(work);
+                                       smb2_set_err_rsp(work);
+                                       rsp->hdr.Status =
+@@ -7189,7 +7198,7 @@ int smb2_lock(struct ksmbd_work *work)
+                               spin_lock(&work->conn->llist_lock);
+                               list_del(&smb_lock->clist);
+                               spin_unlock(&work->conn->llist_lock);
+-
++                              release_async_work(work);
+                               goto retry;
+                       } else if (!rc) {
+                               spin_lock(&work->conn->llist_lock);
+diff --git a/fs/smb/server/smb2pdu.h b/fs/smb/server/smb2pdu.h
+index f13bd65993ccc..59e3de95961c1 100644
+--- a/fs/smb/server/smb2pdu.h
++++ b/fs/smb/server/smb2pdu.h
+@@ -488,6 +488,7 @@ int find_matching_smb2_dialect(int start_index, __le16 *cli_dialects,
+ struct file_lock *smb_flock_init(struct file *f);
+ int setup_async_work(struct ksmbd_work *work, void (*fn)(void **),
+                    void **arg);
++void release_async_work(struct ksmbd_work *work);
+ void smb2_send_interim_resp(struct ksmbd_work *work, __le32 status);
+ struct channel *lookup_chann_list(struct ksmbd_session *sess,
+                                 struct ksmbd_conn *conn);
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-don-t-update-op_state-as-oplock_state_none-on-.patch b/queue-6.1/ksmbd-don-t-update-op_state-as-oplock_state_none-on-.patch
new file mode 100644 (file)
index 0000000..bfab860
--- /dev/null
@@ -0,0 +1,35 @@
+From d136a842f4b76e9488834b12514f75c38df74606 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:13:25 +0900
+Subject: ksmbd: don't update ->op_state as OPLOCK_STATE_NONE on error
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit cd80ce7e68f1624ac29cd0a6b057789d1236641e ]
+
+ksmbd set ->op_state as OPLOCK_STATE_NONE on lease break ack error.
+op_state of lease should not be updated because client can send lease
+break ack again. This patch fix smb2.lease.breaking2 test failure.
+
+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/smb/server/smb2pdu.c | 1 -
+ 1 file changed, 1 deletion(-)
+
+diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
+index 2ba5e685dd3fe..6a698a6036bb2 100644
+--- a/fs/smb/server/smb2pdu.c
++++ b/fs/smb/server/smb2pdu.c
+@@ -8235,7 +8235,6 @@ static void smb21_lease_break_ack(struct ksmbd_work *work)
+               return;
+ err_out:
+-      opinfo->op_state = OPLOCK_STATE_NONE;
+       wake_up_interruptible_all(&opinfo->oplock_q);
+       atomic_dec(&opinfo->breaking_cnt);
+       wake_up_interruptible_all(&opinfo->oplock_brk);
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-downgrade-rwh-lease-caching-state-to-rh-for-di.patch b/queue-6.1/ksmbd-downgrade-rwh-lease-caching-state-to-rh-for-di.patch
new file mode 100644 (file)
index 0000000..b7dfc83
--- /dev/null
@@ -0,0 +1,94 @@
+From 60c2dfbc1c3e2325b8c8605ac0349453ff22411f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:13:28 +0900
+Subject: ksmbd: downgrade RWH lease caching state to RH for directory
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit eb547407f3572d2110cb1194ecd8865b3371a7a4 ]
+
+RWH(Read + Write + Handle) caching state is not supported for directory.
+ksmbd downgrade it to RH for directory if client send RWH caching lease
+state.
+
+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/smb/server/oplock.c  | 9 +++++++--
+ fs/smb/server/oplock.h  | 2 +-
+ fs/smb/server/smb2pdu.c | 8 ++++----
+ 3 files changed, 12 insertions(+), 7 deletions(-)
+
+diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c
+index 7346cbfbff6b0..f8ac539b2164b 100644
+--- a/fs/smb/server/oplock.c
++++ b/fs/smb/server/oplock.c
+@@ -1398,10 +1398,11 @@ void create_lease_buf(u8 *rbuf, struct lease *lease)
+ /**
+  * parse_lease_state() - parse lease context containted in file open request
+  * @open_req: buffer containing smb2 file open(create) request
++ * @is_dir:   whether leasing file is directory
+  *
+  * Return:  oplock state, -ENOENT if create lease context not found
+  */
+-struct lease_ctx_info *parse_lease_state(void *open_req)
++struct lease_ctx_info *parse_lease_state(void *open_req, bool is_dir)
+ {
+       struct create_context *cc;
+       struct smb2_create_req *req = (struct smb2_create_req *)open_req;
+@@ -1419,7 +1420,11 @@ struct lease_ctx_info *parse_lease_state(void *open_req)
+               struct create_lease_v2 *lc = (struct create_lease_v2 *)cc;
+               memcpy(lreq->lease_key, lc->lcontext.LeaseKey, SMB2_LEASE_KEY_SIZE);
+-              lreq->req_state = lc->lcontext.LeaseState;
++              if (is_dir)
++                      lreq->req_state = lc->lcontext.LeaseState &
++                              ~SMB2_LEASE_WRITE_CACHING_LE;
++              else
++                      lreq->req_state = lc->lcontext.LeaseState;
+               lreq->flags = lc->lcontext.LeaseFlags;
+               lreq->epoch = lc->lcontext.Epoch;
+               lreq->duration = lc->lcontext.LeaseDuration;
+diff --git a/fs/smb/server/oplock.h b/fs/smb/server/oplock.h
+index ad31439c61fef..672127318c750 100644
+--- a/fs/smb/server/oplock.h
++++ b/fs/smb/server/oplock.h
+@@ -109,7 +109,7 @@ void opinfo_put(struct oplock_info *opinfo);
+ /* Lease related functions */
+ void create_lease_buf(u8 *rbuf, struct lease *lease);
+-struct lease_ctx_info *parse_lease_state(void *open_req);
++struct lease_ctx_info *parse_lease_state(void *open_req, bool is_dir);
+ __u8 smb2_map_lease_to_oplock(__le32 lease_state);
+ int lease_read_to_write(struct oplock_info *opinfo);
+diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
+index 6a698a6036bb2..d7235c5e2e4d8 100644
+--- a/fs/smb/server/smb2pdu.c
++++ b/fs/smb/server/smb2pdu.c
+@@ -2732,10 +2732,6 @@ int smb2_open(struct ksmbd_work *work)
+               }
+       }
+-      req_op_level = req->RequestedOplockLevel;
+-      if (req_op_level == SMB2_OPLOCK_LEVEL_LEASE)
+-              lc = parse_lease_state(req);
+-
+       if (le32_to_cpu(req->ImpersonationLevel) > le32_to_cpu(IL_DELEGATE)) {
+               pr_err("Invalid impersonationlevel : 0x%x\n",
+                      le32_to_cpu(req->ImpersonationLevel));
+@@ -3215,6 +3211,10 @@ int smb2_open(struct ksmbd_work *work)
+               need_truncate = 1;
+       }
++      req_op_level = req->RequestedOplockLevel;
++      if (req_op_level == SMB2_OPLOCK_LEVEL_LEASE)
++              lc = parse_lease_state(req, S_ISDIR(file_inode(filp)->i_mode));
++
+       share_ret = ksmbd_smb_check_shared_mode(fp->filp, fp);
+       if (!test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_OPLOCKS) ||
+           (req_op_level == SMB2_OPLOCK_LEVEL_LEASE &&
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-fix-force-create-mode-and-force-directory-mode.patch b/queue-6.1/ksmbd-fix-force-create-mode-and-force-directory-mode.patch
new file mode 100644 (file)
index 0000000..132fd3a
--- /dev/null
@@ -0,0 +1,72 @@
+From 07e92566adb80f5ca96e2e6266a58d5f7dd475ad Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:12:56 +0900
+Subject: ksmbd: fix `force create mode' and `force directory mode'
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Atte Heikkilä <atteh.mailbox@gmail.com>
+
+[ Upstream commit 65656f5242e500dcfeffa6a0a1519eae14724f86 ]
+
+`force create mode' and `force directory mode' should be bitwise ORed
+with the perms after `create mask' and `directory mask' have been
+applied, respectively.
+
+Signed-off-by: Atte Heikkilä <atteh.mailbox@gmail.com>
+Acked-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/server/mgmt/share_config.h | 29 +++++++++++------------------
+ 1 file changed, 11 insertions(+), 18 deletions(-)
+
+diff --git a/fs/smb/server/mgmt/share_config.h b/fs/smb/server/mgmt/share_config.h
+index 3fd3382939421..5f591751b9236 100644
+--- a/fs/smb/server/mgmt/share_config.h
++++ b/fs/smb/server/mgmt/share_config.h
+@@ -34,29 +34,22 @@ struct ksmbd_share_config {
+ #define KSMBD_SHARE_INVALID_UID       ((__u16)-1)
+ #define KSMBD_SHARE_INVALID_GID       ((__u16)-1)
+-static inline int share_config_create_mode(struct ksmbd_share_config *share,
+-                                         umode_t posix_mode)
++static inline umode_t
++share_config_create_mode(struct ksmbd_share_config *share,
++                       umode_t posix_mode)
+ {
+-      if (!share->force_create_mode) {
+-              if (!posix_mode)
+-                      return share->create_mask;
+-              else
+-                      return posix_mode & share->create_mask;
+-      }
+-      return share->force_create_mode & share->create_mask;
++      umode_t mode = (posix_mode ?: (umode_t)-1) & share->create_mask;
++
++      return mode | share->force_create_mode;
+ }
+-static inline int share_config_directory_mode(struct ksmbd_share_config *share,
+-                                            umode_t posix_mode)
++static inline umode_t
++share_config_directory_mode(struct ksmbd_share_config *share,
++                          umode_t posix_mode)
+ {
+-      if (!share->force_directory_mode) {
+-              if (!posix_mode)
+-                      return share->directory_mask;
+-              else
+-                      return posix_mode & share->directory_mask;
+-      }
++      umode_t mode = (posix_mode ?: (umode_t)-1) & share->directory_mask;
+-      return share->force_directory_mode & share->directory_mask;
++      return mode | share->force_directory_mode;
+ }
+ static inline int test_share_config_flag(struct ksmbd_share_config *share,
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-fix-kernel-doc-comment-of-ksmbd_vfs_kern_path_.patch b/queue-6.1/ksmbd-fix-kernel-doc-comment-of-ksmbd_vfs_kern_path_.patch
new file mode 100644 (file)
index 0000000..ab0eff1
--- /dev/null
@@ -0,0 +1,44 @@
+From 1545888153b0930dd1e9fa78d6bdc1b1ba06d61a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:13:18 +0900
+Subject: ksmbd: fix kernel-doc comment of ksmbd_vfs_kern_path_locked()
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit f6049712e520287ad695e9d4f1572ab76807fa0c ]
+
+Fix argument list that the kdoc format and script verified in
+ksmbd_vfs_kern_path_locked().
+
+fs/smb/server/vfs.c:1207: warning: Function parameter or member 'parent_path'
+not described in 'ksmbd_vfs_kern_path_locked'
+
+Reported-by: kernel test robot <lkp@intel.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/smb/server/vfs.c | 7 ++++---
+ 1 file changed, 4 insertions(+), 3 deletions(-)
+
+diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c
+index e2e454eba4095..d4298a751d4a2 100644
+--- a/fs/smb/server/vfs.c
++++ b/fs/smb/server/vfs.c
+@@ -1178,9 +1178,10 @@ static int ksmbd_vfs_lookup_in_dir(const struct path *dir, char *name,
+ /**
+  * 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
++ * @name:             file path that is relative to share
++ * @flags:            lookup flags
++ * @parent_path:      if lookup succeed, return parent_path info
++ * @path:             if lookup succeed, return path info
+  * @caseless: caseless filename lookup
+  *
+  * Return:    0 on success, otherwise error
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-fix-kernel-doc-comment-of-ksmbd_vfs_setxattr.patch b/queue-6.1/ksmbd-fix-kernel-doc-comment-of-ksmbd_vfs_setxattr.patch
new file mode 100644 (file)
index 0000000..fc44511
--- /dev/null
@@ -0,0 +1,39 @@
+From 30497f8971c380596dadb0733b268a0c7bbb495f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:13:13 +0900
+Subject: ksmbd: fix kernel-doc comment of ksmbd_vfs_setxattr()
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit 3354db668808d5b6d7c5e0cb19ff4c9da4bb5e58 ]
+
+Fix argument list that the kdoc format and script verified in
+ksmbd_vfs_setxattr().
+
+fs/smb/server/vfs.c:929: warning: Function parameter or member 'path'
+not described in 'ksmbd_vfs_setxattr'
+
+Reported-by: kernel test robot <lkp@intel.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/smb/server/vfs.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c
+index 6f54ea1df0c58..071c344dd0333 100644
+--- a/fs/smb/server/vfs.c
++++ b/fs/smb/server/vfs.c
+@@ -920,7 +920,7 @@ ssize_t ksmbd_vfs_getxattr(struct user_namespace *user_ns,
+ /**
+  * ksmbd_vfs_setxattr() - vfs helper for smb set extended attributes value
+  * @user_ns:  user namespace
+- * @dentry:   dentry to set XATTR at
++ * @path:     path of dentry to set XATTR at
+  * @attr_name:        xattr name for setxattr
+  * @attr_value:       xattr value to set
+  * @attr_size:        size of xattr value
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-fix-missing-rdma-capable-flag-for-ipoib-device.patch b/queue-6.1/ksmbd-fix-missing-rdma-capable-flag-for-ipoib-device.patch
new file mode 100644 (file)
index 0000000..f941aaa
--- /dev/null
@@ -0,0 +1,89 @@
+From c879a4a24fa9a5f7d1fcc9e48ff9bfc83fcabd2d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:13:15 +0900
+Subject: ksmbd: fix missing RDMA-capable flag for IPoIB device in
+ ksmbd_rdma_capable_netdev()
+
+From: Kangjing Huang <huangkangjing@gmail.com>
+
+[ Upstream commit ecce70cf17d91c3dd87a0c4ea00b2d1387729701 ]
+
+Physical ib_device does not have an underlying net_device, thus its
+association with IPoIB net_device cannot be retrieved via
+ops.get_netdev() or ib_device_get_by_netdev(). ksmbd reads physical
+ib_device port GUID from the lower 16 bytes of the hardware addresses on
+IPoIB net_device and match its underlying ib_device using ib_find_gid()
+
+Signed-off-by: Kangjing Huang <huangkangjing@gmail.com>
+Acked-by: Namjae Jeon <linkinjeon@kernel.org>
+Reviewed-by: Tom Talpey <tom@talpey.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/server/transport_rdma.c | 40 +++++++++++++++++++++++++---------
+ 1 file changed, 30 insertions(+), 10 deletions(-)
+
+diff --git a/fs/smb/server/transport_rdma.c b/fs/smb/server/transport_rdma.c
+index 3b269e1f523a1..c5629a68c8b73 100644
+--- a/fs/smb/server/transport_rdma.c
++++ b/fs/smb/server/transport_rdma.c
+@@ -2140,8 +2140,7 @@ static int smb_direct_ib_client_add(struct ib_device *ib_dev)
+       if (ib_dev->node_type != RDMA_NODE_IB_CA)
+               smb_direct_port = SMB_DIRECT_PORT_IWARP;
+-      if (!ib_dev->ops.get_netdev ||
+-          !rdma_frwr_is_supported(&ib_dev->attrs))
++      if (!rdma_frwr_is_supported(&ib_dev->attrs))
+               return 0;
+       smb_dev = kzalloc(sizeof(*smb_dev), GFP_KERNEL);
+@@ -2241,17 +2240,38 @@ bool ksmbd_rdma_capable_netdev(struct net_device *netdev)
+               for (i = 0; i < smb_dev->ib_dev->phys_port_cnt; i++) {
+                       struct net_device *ndev;
+-                      ndev = smb_dev->ib_dev->ops.get_netdev(smb_dev->ib_dev,
+-                                                             i + 1);
+-                      if (!ndev)
+-                              continue;
++                      if (smb_dev->ib_dev->ops.get_netdev) {
++                              ndev = smb_dev->ib_dev->ops.get_netdev(
++                                      smb_dev->ib_dev, i + 1);
++                              if (!ndev)
++                                      continue;
+-                      if (ndev == netdev) {
++                              if (ndev == netdev) {
++                                      dev_put(ndev);
++                                      rdma_capable = true;
++                                      goto out;
++                              }
+                               dev_put(ndev);
+-                              rdma_capable = true;
+-                              goto out;
++                      /* if ib_dev does not implement ops.get_netdev
++                       * check for matching infiniband GUID in hw_addr
++                       */
++                      } else if (netdev->type == ARPHRD_INFINIBAND) {
++                              struct netdev_hw_addr *ha;
++                              union ib_gid gid;
++                              u32 port_num;
++                              int ret;
++
++                              netdev_hw_addr_list_for_each(
++                                      ha, &netdev->dev_addrs) {
++                                      memcpy(&gid, ha->addr + 4, sizeof(gid));
++                                      ret = ib_find_gid(smb_dev->ib_dev, &gid,
++                                                        &port_num, NULL);
++                                      if (!ret) {
++                                              rdma_capable = true;
++                                              goto out;
++                                      }
++                              }
+                       }
+-                      dev_put(ndev);
+               }
+       }
+ out:
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-fix-null-pointer-dereferences-in-ksmbd_update_.patch b/queue-6.1/ksmbd-fix-null-pointer-dereferences-in-ksmbd_update_.patch
new file mode 100644 (file)
index 0000000..11d7a46
--- /dev/null
@@ -0,0 +1,56 @@
+From 402b062ba5000a9682a8d3c575f2284158a9c37f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:13:09 +0900
+Subject: ksmbd: fix Null pointer dereferences in ksmbd_update_fstate()
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit 414849040fcf11d45025b8ae26c9fd91da1465da ]
+
+Coverity Scan report the following one. This report is a false alarm.
+Because fp is never NULL when rc is zero. This patch add null check for fp
+in ksmbd_update_fstate to make alarm silence.
+
+*** CID 1568583:  Null pointer dereferences  (FORWARD_NULL)
+/fs/smb/server/smb2pdu.c: 3408 in smb2_open()
+3402                    path_put(&path);
+3403                    path_put(&parent_path);
+3404            }
+3405            ksmbd_revert_fsids(work);
+3406     err_out1:
+3407            if (!rc) {
+>>>     CID 1568583:  Null pointer dereferences  (FORWARD_NULL)
+>>>     Passing null pointer "fp" to "ksmbd_update_fstate", which dereferences it.
+3408                    ksmbd_update_fstate(&work->sess->file_table, fp, FP_INITED);
+3409                    rc = ksmbd_iov_pin_rsp(work, (void *)rsp, iov_len);
+3410            }
+3411            if (rc) {
+3412                    if (rc == -EINVAL)
+3413                            rsp->hdr.Status = STATUS_INVALID_PARAMETER;
+
+Fixes: e2b76ab8b5c9 ("ksmbd: add support for read compound")
+Reported-by: Coverity Scan <scan-admin@coverity.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/smb/server/vfs_cache.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c
+index f600279b0a9ee..38f414e803adb 100644
+--- a/fs/smb/server/vfs_cache.c
++++ b/fs/smb/server/vfs_cache.c
+@@ -602,6 +602,9 @@ struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp)
+ void ksmbd_update_fstate(struct ksmbd_file_table *ft, struct ksmbd_file *fp,
+                        unsigned int state)
+ {
++      if (!fp)
++              return;
++
+       write_lock(&ft->lock);
+       fp->f_state = state;
+       write_unlock(&ft->lock);
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-fix-one-kernel-doc-comment.patch b/queue-6.1/ksmbd-fix-one-kernel-doc-comment.patch
new file mode 100644 (file)
index 0000000..e4e4fb6
--- /dev/null
@@ -0,0 +1,35 @@
+From 86227b9f68b1e6b4fe5bf8ccfe5bb8f41a3f17a2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:12:57 +0900
+Subject: ksmbd: Fix one kernel-doc comment
+
+From: Yang Li <yang.lee@linux.alibaba.com>
+
+[ Upstream commit bf26f1b4e0918f017775edfeacf6d867204b680b ]
+
+Fix one kernel-doc comment to silence the warning:
+fs/smb/server/smb2pdu.c:4160: warning: Excess function parameter 'infoclass_size' description in 'buffer_check_err'
+
+Signed-off-by: Yang Li <yang.lee@linux.alibaba.com>
+Acked-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/server/smb2pdu.c | 1 -
+ 1 file changed, 1 deletion(-)
+
+diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
+index dfb4fd4cb42f6..0fed613956f7a 100644
+--- a/fs/smb/server/smb2pdu.c
++++ b/fs/smb/server/smb2pdu.c
+@@ -4156,7 +4156,6 @@ int smb2_query_dir(struct ksmbd_work *work)
+  * @reqOutputBufferLength:    max buffer length expected in command response
+  * @rsp:              query info response buffer contains output buffer length
+  * @rsp_org:          base response buffer pointer in case of chained response
+- * @infoclass_size:   query info class response buffer size
+  *
+  * Return:    0 on success, otherwise error
+  */
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-fix-parameter-name-and-comment-mismatch.patch b/queue-6.1/ksmbd-fix-parameter-name-and-comment-mismatch.patch
new file mode 100644 (file)
index 0000000..1617800
--- /dev/null
@@ -0,0 +1,42 @@
+From e67aecfb9b539c1f4db68ad2cd47fd29c618e5d7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:12:30 +0900
+Subject: ksmbd: Fix parameter name and comment mismatch
+
+From: Jiapeng Chong <jiapeng.chong@linux.alibaba.com>
+
+[ Upstream commit 63f09a9986eb58578ed6ad0e27a6e2c54e49f797 ]
+
+fs/ksmbd/vfs.c:965: warning: Function parameter or member 'attr_value' not described in 'ksmbd_vfs_setxattr'.
+
+Reported-by: Abaci Robot <abaci@linux.alibaba.com>
+Link: https://bugzilla.openanolis.cn/show_bug.cgi?id=3946
+Signed-off-by: Jiapeng Chong <jiapeng.chong@linux.alibaba.com>
+Acked-by: Namjae Jeon <linkinjeon@kernel.org>
+Reviewed-by: Sergey Senozhatsky <senozhatsky@chromium.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/server/vfs.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c
+index 36914db8b6616..187d31d984946 100644
+--- a/fs/smb/server/vfs.c
++++ b/fs/smb/server/vfs.c
+@@ -951,9 +951,9 @@ ssize_t ksmbd_vfs_getxattr(struct user_namespace *user_ns,
+  * ksmbd_vfs_setxattr() - vfs helper for smb set extended attributes value
+  * @user_ns:  user namespace
+  * @dentry:   dentry to set XATTR at
+- * @name:     xattr name for setxattr
+- * @value:    xattr value to set
+- * @size:     size of xattr value
++ * @attr_name:        xattr name for setxattr
++ * @attr_value:       xattr value to set
++ * @attr_size:        size of xattr value
+  * @flags:    destination buffer length
+  *
+  * Return:    0 on success, otherwise error
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-fix-passing-freed-memory-aux_payload_buf.patch b/queue-6.1/ksmbd-fix-passing-freed-memory-aux_payload_buf.patch
new file mode 100644 (file)
index 0000000..5003585
--- /dev/null
@@ -0,0 +1,43 @@
+From af50bb43ac3da04dad814a1f4b9c5348a59938d5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:13:01 +0900
+Subject: ksmbd: fix passing freed memory 'aux_payload_buf'
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit 59d8d24f4610333560cf2e8fe3f44cafe30322eb ]
+
+The patch e2b76ab8b5c9: "ksmbd: add support for read compound" leads
+to the following Smatch static checker warning:
+
+  fs/smb/server/smb2pdu.c:6329 smb2_read()
+        warn: passing freed memory 'aux_payload_buf'
+
+It doesn't matter that we're passing a freed variable because nbytes is
+zero. This patch set "aux_payload_buf = NULL" to make smatch silence.
+
+Fixes: e2b76ab8b5c9 ("ksmbd: add support for read compound")
+Reported-by: Dan Carpenter <dan.carpenter@linaro.org>
+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/smb/server/smb2pdu.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
+index b81a38803b40d..42697ea86d47b 100644
+--- a/fs/smb/server/smb2pdu.c
++++ b/fs/smb/server/smb2pdu.c
+@@ -6312,7 +6312,7 @@ int smb2_read(struct ksmbd_work *work)
+                                                     aux_payload_buf,
+                                                     nbytes);
+               kvfree(aux_payload_buf);
+-
++              aux_payload_buf = NULL;
+               nbytes = 0;
+               if (remain_bytes < 0) {
+                       err = (int)remain_bytes;
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-fix-posix_acls-and-acls-dereferencing-possible.patch b/queue-6.1/ksmbd-fix-posix_acls-and-acls-dereferencing-possible.patch
new file mode 100644 (file)
index 0000000..e7e4a73
--- /dev/null
@@ -0,0 +1,79 @@
+From e7b95998e08d4ba7a04c83e7aee93aa1c699ddbc Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:12:41 +0900
+Subject: ksmbd: fix posix_acls and acls dereferencing possible ERR_PTR()
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit 25933573ef48f3586f559c2cac6c436c62dcf63f ]
+
+Dan reported the following error message:
+
+fs/smb/server/smbacl.c:1296 smb_check_perm_dacl()
+    error: 'posix_acls' dereferencing possible ERR_PTR()
+fs/smb/server/vfs.c:1323 ksmbd_vfs_make_xattr_posix_acl()
+    error: 'posix_acls' dereferencing possible ERR_PTR()
+fs/smb/server/vfs.c:1830 ksmbd_vfs_inherit_posix_acl()
+    error: 'acls' dereferencing possible ERR_PTR()
+
+__get_acl() returns a mix of error pointers and NULL. This change it
+with IS_ERR_OR_NULL().
+
+Fixes: e2f34481b24d ("cifsd: add server-side procedures for SMB3")
+Cc: stable@vger.kernel.org
+Reported-by: Dan Carpenter <dan.carpenter@linaro.org>
+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/smb/server/smbacl.c | 4 ++--
+ fs/smb/server/vfs.c    | 4 ++--
+ 2 files changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/fs/smb/server/smbacl.c b/fs/smb/server/smbacl.c
+index c24df86eb112b..8c041e71cf156 100644
+--- a/fs/smb/server/smbacl.c
++++ b/fs/smb/server/smbacl.c
+@@ -1313,7 +1313,7 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path,
+       if (IS_ENABLED(CONFIG_FS_POSIX_ACL)) {
+               posix_acls = get_acl(d_inode(path->dentry), ACL_TYPE_ACCESS);
+-              if (posix_acls && !found) {
++              if (!IS_ERR_OR_NULL(posix_acls) && !found) {
+                       unsigned int id = -1;
+                       pa_entry = posix_acls->a_entries;
+@@ -1337,7 +1337,7 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path,
+                               }
+                       }
+               }
+-              if (posix_acls)
++              if (!IS_ERR_OR_NULL(posix_acls))
+                       posix_acl_release(posix_acls);
+       }
+diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c
+index ebcd5a312f10d..6d171f2757f15 100644
+--- a/fs/smb/server/vfs.c
++++ b/fs/smb/server/vfs.c
+@@ -1322,7 +1322,7 @@ static struct xattr_smb_acl *ksmbd_vfs_make_xattr_posix_acl(struct user_namespac
+               return NULL;
+       posix_acls = get_acl(inode, acl_type);
+-      if (!posix_acls)
++      if (IS_ERR_OR_NULL(posix_acls))
+               return NULL;
+       smb_acl = kzalloc(sizeof(struct xattr_smb_acl) +
+@@ -1830,7 +1830,7 @@ int ksmbd_vfs_inherit_posix_acl(struct user_namespace *user_ns,
+               return -EOPNOTSUPP;
+       acls = get_acl(parent_inode, ACL_TYPE_DEFAULT);
+-      if (!acls)
++      if (IS_ERR_OR_NULL(acls))
+               return -ENOENT;
+       pace = acls->a_entries;
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-fix-possible-deadlock-in-smb2_open.patch b/queue-6.1/ksmbd-fix-possible-deadlock-in-smb2_open.patch
new file mode 100644 (file)
index 0000000..6035751
--- /dev/null
@@ -0,0 +1,488 @@
+From 7539c7c5112a3946d637236fe20993d4cbf2cb99 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:13:20 +0900
+Subject: ksmbd: fix possible deadlock in smb2_open
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit 864fb5d3716303a045c3ffb397f651bfd37bfb36 ]
+
+[ 8743.393379] ======================================================
+[ 8743.393385] WARNING: possible circular locking dependency detected
+[ 8743.393391] 6.4.0-rc1+ #11 Tainted: G           OE
+[ 8743.393397] ------------------------------------------------------
+[ 8743.393402] kworker/0:2/12921 is trying to acquire lock:
+[ 8743.393408] ffff888127a14460 (sb_writers#8){.+.+}-{0:0}, at: ksmbd_vfs_setxattr+0x3d/0xd0 [ksmbd]
+[ 8743.393510]
+               but task is already holding lock:
+[ 8743.393515] ffff8880360d97f0 (&type->i_mutex_dir_key#6/1){+.+.}-{3:3}, at: ksmbd_vfs_kern_path_locked+0x181/0x670 [ksmbd]
+[ 8743.393618]
+               which lock already depends on the new lock.
+
+[ 8743.393623]
+               the existing dependency chain (in reverse order) is:
+[ 8743.393628]
+               -> #1 (&type->i_mutex_dir_key#6/1){+.+.}-{3:3}:
+[ 8743.393648]        down_write_nested+0x9a/0x1b0
+[ 8743.393660]        filename_create+0x128/0x270
+[ 8743.393670]        do_mkdirat+0xab/0x1f0
+[ 8743.393680]        __x64_sys_mkdir+0x47/0x60
+[ 8743.393690]        do_syscall_64+0x5d/0x90
+[ 8743.393701]        entry_SYSCALL_64_after_hwframe+0x72/0xdc
+[ 8743.393711]
+               -> #0 (sb_writers#8){.+.+}-{0:0}:
+[ 8743.393728]        __lock_acquire+0x2201/0x3b80
+[ 8743.393737]        lock_acquire+0x18f/0x440
+[ 8743.393746]        mnt_want_write+0x5f/0x240
+[ 8743.393755]        ksmbd_vfs_setxattr+0x3d/0xd0 [ksmbd]
+[ 8743.393839]        ksmbd_vfs_set_dos_attrib_xattr+0xcc/0x110 [ksmbd]
+[ 8743.393924]        compat_ksmbd_vfs_set_dos_attrib_xattr+0x39/0x50 [ksmbd]
+[ 8743.394010]        smb2_open+0x3432/0x3cc0 [ksmbd]
+[ 8743.394099]        handle_ksmbd_work+0x2c9/0x7b0 [ksmbd]
+[ 8743.394187]        process_one_work+0x65a/0xb30
+[ 8743.394198]        worker_thread+0x2cf/0x700
+[ 8743.394209]        kthread+0x1ad/0x1f0
+[ 8743.394218]        ret_from_fork+0x29/0x50
+
+This patch add mnt_want_write() above parent inode lock and remove
+nested mnt_want_write calls in smb2_open().
+
+Fixes: 40b268d384a2 ("ksmbd: add mnt_want_write to ksmbd vfs functions")
+Cc: stable@vger.kernel.org
+Reported-by: Marios Makassikis <mmakassikis@freebox.fr>
+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/smb/server/smb2pdu.c | 47 +++++++++++++---------------
+ fs/smb/server/smbacl.c  |  7 +++--
+ fs/smb/server/smbacl.h  |  2 +-
+ fs/smb/server/vfs.c     | 68 +++++++++++++++++++++++++----------------
+ fs/smb/server/vfs.h     | 10 ++++--
+ 5 files changed, 75 insertions(+), 59 deletions(-)
+
+diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
+index a89a69d752a3b..b8340f1382bef 100644
+--- a/fs/smb/server/smb2pdu.c
++++ b/fs/smb/server/smb2pdu.c
+@@ -2380,7 +2380,8 @@ static int smb2_set_ea(struct smb2_ea_info *eabuf, unsigned int buf_len,
+                       rc = 0;
+               } else {
+                       rc = ksmbd_vfs_setxattr(user_ns, path, attr_name, value,
+-                                              le16_to_cpu(eabuf->EaValueLength), 0);
++                                              le16_to_cpu(eabuf->EaValueLength),
++                                              0, true);
+                       if (rc < 0) {
+                               ksmbd_debug(SMB,
+                                           "ksmbd_vfs_setxattr is failed(%d)\n",
+@@ -2443,7 +2444,7 @@ static noinline int smb2_set_stream_name_xattr(const struct path *path,
+               return -EBADF;
+       }
+-      rc = ksmbd_vfs_setxattr(user_ns, path, xattr_stream_name, NULL, 0, 0);
++      rc = ksmbd_vfs_setxattr(user_ns, path, xattr_stream_name, NULL, 0, 0, false);
+       if (rc < 0)
+               pr_err("Failed to store XATTR stream name :%d\n", rc);
+       return 0;
+@@ -2518,7 +2519,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_user_ns(path->mnt), path, &da);
++      rc = ksmbd_vfs_set_dos_attrib_xattr(mnt_user_ns(path->mnt), path, &da, false);
+       if (rc)
+               ksmbd_debug(SMB, "failed to store file attribute into xattr\n");
+ }
+@@ -2608,7 +2609,7 @@ static int smb2_create_sd_buffer(struct ksmbd_work *work,
+           sizeof(struct create_sd_buf_req))
+               return -EINVAL;
+       return set_info_sec(work->conn, work->tcon, path, &sd_buf->ntsd,
+-                          le32_to_cpu(sd_buf->ccontext.DataLength), true);
++                          le32_to_cpu(sd_buf->ccontext.DataLength), true, false);
+ }
+ static void ksmbd_acls_fattr(struct smb_fattr *fattr,
+@@ -3152,7 +3153,8 @@ int smb2_open(struct ksmbd_work *work)
+                                                                   user_ns,
+                                                                   &path,
+                                                                   pntsd,
+-                                                                  pntsd_size);
++                                                                  pntsd_size,
++                                                                  false);
+                                       kfree(pntsd);
+                                       if (rc)
+                                               pr_err("failed to store ntacl in xattr : %d\n",
+@@ -3228,12 +3230,6 @@ int smb2_open(struct ksmbd_work *work)
+       if (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE)
+               ksmbd_fd_set_delete_on_close(fp, file_info);
+-      if (need_truncate) {
+-              rc = smb2_create_truncate(&path);
+-              if (rc)
+-                      goto err_out;
+-      }
+-
+       if (req->CreateContextsOffset) {
+               struct create_alloc_size_req *az_req;
+@@ -3398,11 +3394,12 @@ int smb2_open(struct ksmbd_work *work)
+       }
+ err_out:
+-      if (file_present || created) {
+-              inode_unlock(d_inode(parent_path.dentry));
+-              path_put(&path);
+-              path_put(&parent_path);
+-      }
++      if (file_present || created)
++              ksmbd_vfs_kern_path_unlock(&parent_path, &path);
++
++      if (fp && need_truncate)
++              rc = smb2_create_truncate(&fp->filp->f_path);
++
+       ksmbd_revert_fsids(work);
+ err_out1:
+       if (!rc) {
+@@ -5537,7 +5534,7 @@ static int smb2_rename(struct ksmbd_work *work,
+               rc = ksmbd_vfs_setxattr(file_mnt_user_ns(fp->filp),
+                                       &fp->filp->f_path,
+                                       xattr_stream_name,
+-                                      NULL, 0, 0);
++                                      NULL, 0, 0, true);
+               if (rc < 0) {
+                       pr_err("failed to store stream name in xattr: %d\n",
+                              rc);
+@@ -5630,11 +5627,9 @@ static int smb2_create_link(struct ksmbd_work *work,
+       if (rc)
+               rc = -EINVAL;
+ out:
+-      if (file_present) {
+-              inode_unlock(d_inode(parent_path.dentry));
+-              path_put(&path);
+-              path_put(&parent_path);
+-      }
++      if (file_present)
++              ksmbd_vfs_kern_path_unlock(&parent_path, &path);
++
+       if (!IS_ERR(link_name))
+               kfree(link_name);
+       kfree(pathname);
+@@ -5701,7 +5696,8 @@ 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(user_ns, &filp->f_path, &da);
++              rc = ksmbd_vfs_set_dos_attrib_xattr(user_ns, &filp->f_path, &da,
++                              true);
+               if (rc)
+                       ksmbd_debug(SMB,
+                                   "failed to restore file attribute in EA\n");
+@@ -6013,7 +6009,7 @@ static int smb2_set_info_sec(struct ksmbd_file *fp, int addition_info,
+       fp->saccess |= FILE_SHARE_DELETE_LE;
+       return set_info_sec(fp->conn, fp->tcon, &fp->filp->f_path, pntsd,
+-                      buf_len, false);
++                      buf_len, false, true);
+ }
+ /**
+@@ -7583,7 +7579,8 @@ 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(user_ns,
+-                                                   &fp->filp->f_path, &da);
++                                                   &fp->filp->f_path,
++                                                   &da, true);
+               if (ret)
+                       fp->f_ci->m_fattr = old_fattr;
+       }
+diff --git a/fs/smb/server/smbacl.c b/fs/smb/server/smbacl.c
+index 7a42728d8047c..d9bbd2eb89c35 100644
+--- a/fs/smb/server/smbacl.c
++++ b/fs/smb/server/smbacl.c
+@@ -1185,7 +1185,7 @@ int smb_inherit_dacl(struct ksmbd_conn *conn,
+                       pntsd_size += sizeof(struct smb_acl) + nt_size;
+               }
+-              ksmbd_vfs_set_sd_xattr(conn, user_ns, path, pntsd, pntsd_size);
++              ksmbd_vfs_set_sd_xattr(conn, user_ns, path, pntsd, pntsd_size, false);
+               kfree(pntsd);
+       }
+@@ -1377,7 +1377,7 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path,
+ int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon,
+                const struct path *path, struct smb_ntsd *pntsd, int ntsd_len,
+-               bool type_check)
++               bool type_check, bool get_write)
+ {
+       int rc;
+       struct smb_fattr fattr = {{0}};
+@@ -1437,7 +1437,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(user_ns, path);
+-              ksmbd_vfs_set_sd_xattr(conn, user_ns, path, pntsd, ntsd_len);
++              ksmbd_vfs_set_sd_xattr(conn, user_ns, path, pntsd, ntsd_len,
++                              get_write);
+       }
+ out:
+diff --git a/fs/smb/server/smbacl.h b/fs/smb/server/smbacl.h
+index 618f2e0236b31..9651a25518881 100644
+--- a/fs/smb/server/smbacl.h
++++ b/fs/smb/server/smbacl.h
+@@ -207,7 +207,7 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path,
+                       __le32 *pdaccess, int uid);
+ int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon,
+                const struct path *path, struct smb_ntsd *pntsd, int ntsd_len,
+-               bool type_check);
++               bool type_check, bool get_write);
+ void id_to_sid(unsigned int cid, uint sidtype, struct smb_sid *ssid);
+ void ksmbd_init_domain(u32 *sub_auth);
+diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c
+index d4298a751d4a2..08f3f66e4b38e 100644
+--- a/fs/smb/server/vfs.c
++++ b/fs/smb/server/vfs.c
+@@ -98,6 +98,13 @@ static int ksmbd_vfs_path_lookup_locked(struct ksmbd_share_config *share_conf,
+               return -ENOENT;
+       }
++      err = mnt_want_write(parent_path->mnt);
++      if (err) {
++              path_put(parent_path);
++              putname(filename);
++              return -ENOENT;
++      }
++
+       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))
+@@ -124,6 +131,7 @@ static int ksmbd_vfs_path_lookup_locked(struct ksmbd_share_config *share_conf,
+ err_out:
+       inode_unlock(d_inode(parent_path->dentry));
++      mnt_drop_write(parent_path->mnt);
+       path_put(parent_path);
+       putname(filename);
+       return -ENOENT;
+@@ -452,7 +460,8 @@ static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos,
+                                fp->stream.name,
+                                (void *)stream_buf,
+                                size,
+-                               0);
++                               0,
++                               true);
+       if (err < 0)
+               goto out;
+@@ -594,10 +603,6 @@ 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;
+-
+       user_ns = mnt_user_ns(path->mnt);
+       if (S_ISDIR(d_inode(path->dentry)->i_mode)) {
+               err = vfs_rmdir(user_ns, d_inode(parent), path->dentry);
+@@ -608,7 +613,6 @@ 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);
+@@ -908,18 +912,22 @@ ssize_t ksmbd_vfs_getxattr(struct user_namespace *user_ns,
+  * @attr_value:       xattr value to set
+  * @attr_size:        size of xattr value
+  * @flags:    destination buffer length
++ * @get_write:        get write access to a mount
+  *
+  * Return:    0 on success, otherwise error
+  */
+ int ksmbd_vfs_setxattr(struct user_namespace *user_ns,
+                      const struct path *path, const char *attr_name,
+-                     void *attr_value, size_t attr_size, int flags)
++                     void *attr_value, size_t attr_size, int flags,
++                     bool get_write)
+ {
+       int err;
+-      err = mnt_want_write(path->mnt);
+-      if (err)
+-              return err;
++      if (get_write == true) {
++              err = mnt_want_write(path->mnt);
++              if (err)
++                      return err;
++      }
+       err = vfs_setxattr(user_ns,
+                          path->dentry,
+@@ -929,7 +937,8 @@ int ksmbd_vfs_setxattr(struct user_namespace *user_ns,
+                          flags);
+       if (err)
+               ksmbd_debug(VFS, "setxattr failed, err %d\n", err);
+-      mnt_drop_write(path->mnt);
++      if (get_write == true)
++              mnt_drop_write(path->mnt);
+       return err;
+ }
+@@ -1253,6 +1262,13 @@ int ksmbd_vfs_kern_path_locked(struct ksmbd_work *work, char *name,
+       }
+       if (!err) {
++              err = mnt_want_write(parent_path->mnt);
++              if (err) {
++                      path_put(path);
++                      path_put(parent_path);
++                      return err;
++              }
++
+               err = ksmbd_vfs_lock_parent(parent_path->dentry, path->dentry);
+               if (err) {
+                       path_put(path);
+@@ -1262,6 +1278,14 @@ int ksmbd_vfs_kern_path_locked(struct ksmbd_work *work, char *name,
+       return err;
+ }
++void ksmbd_vfs_kern_path_unlock(struct path *parent_path, struct path *path)
++{
++      inode_unlock(d_inode(parent_path->dentry));
++      mnt_drop_write(parent_path->mnt);
++      path_put(path);
++      path_put(parent_path);
++}
++
+ struct dentry *ksmbd_vfs_kern_path_create(struct ksmbd_work *work,
+                                         const char *name,
+                                         unsigned int flags,
+@@ -1411,7 +1435,8 @@ static struct xattr_smb_acl *ksmbd_vfs_make_xattr_posix_acl(struct user_namespac
+ int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn,
+                          struct user_namespace *user_ns,
+                          const struct path *path,
+-                         struct smb_ntsd *pntsd, int len)
++                         struct smb_ntsd *pntsd, int len,
++                         bool get_write)
+ {
+       int rc;
+       struct ndr sd_ndr = {0}, acl_ndr = {0};
+@@ -1471,7 +1496,7 @@ int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn,
+       rc = ksmbd_vfs_setxattr(user_ns, path,
+                               XATTR_NAME_SD, sd_ndr.data,
+-                              sd_ndr.offset, 0);
++                              sd_ndr.offset, 0, get_write);
+       if (rc < 0)
+               pr_err("Failed to store XATTR ntacl :%d\n", rc);
+@@ -1560,7 +1585,8 @@ int ksmbd_vfs_get_sd_xattr(struct ksmbd_conn *conn,
+ int ksmbd_vfs_set_dos_attrib_xattr(struct user_namespace *user_ns,
+                                  const struct path *path,
+-                                 struct xattr_dos_attrib *da)
++                                 struct xattr_dos_attrib *da,
++                                 bool get_write)
+ {
+       struct ndr n;
+       int err;
+@@ -1570,7 +1596,7 @@ int ksmbd_vfs_set_dos_attrib_xattr(struct user_namespace *user_ns,
+               return err;
+       err = ksmbd_vfs_setxattr(user_ns, path, XATTR_NAME_DOS_ATTRIBUTE,
+-                               (void *)n.data, n.offset, 0);
++                               (void *)n.data, n.offset, 0, get_write);
+       if (err)
+               ksmbd_debug(SMB, "failed to store dos attribute in xattr\n");
+       kfree(n.data);
+@@ -1840,10 +1866,6 @@ int ksmbd_vfs_set_init_posix_acl(struct user_namespace *user_ns,
+       }
+       posix_state_to_acl(&acl_state, acls->a_entries);
+-      rc = mnt_want_write(path->mnt);
+-      if (rc)
+-              goto out_err;
+-
+       rc = set_posix_acl(user_ns, inode, ACL_TYPE_ACCESS, acls);
+       if (rc < 0)
+               ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_ACCESS) failed, rc : %d\n",
+@@ -1856,9 +1878,7 @@ int ksmbd_vfs_set_init_posix_acl(struct user_namespace *user_ns,
+                       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;
+@@ -1887,10 +1907,6 @@ int ksmbd_vfs_inherit_posix_acl(struct user_namespace *user_ns,
+               }
+       }
+-      rc = mnt_want_write(path->mnt);
+-      if (rc)
+-              goto out_err;
+-
+       rc = set_posix_acl(user_ns, inode, ACL_TYPE_ACCESS, acls);
+       if (rc < 0)
+               ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_ACCESS) failed, rc : %d\n",
+@@ -1902,9 +1918,7 @@ int ksmbd_vfs_inherit_posix_acl(struct user_namespace *user_ns,
+                       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/smb/server/vfs.h b/fs/smb/server/vfs.h
+index 93799ca4cc345..e761dde2443e2 100644
+--- a/fs/smb/server/vfs.h
++++ b/fs/smb/server/vfs.h
+@@ -109,7 +109,8 @@ ssize_t ksmbd_vfs_casexattr_len(struct user_namespace *user_ns,
+                               int attr_name_len);
+ int ksmbd_vfs_setxattr(struct user_namespace *user_ns,
+                      const struct path *path, const char *attr_name,
+-                     void *attr_value, size_t attr_size, int flags);
++                     void *attr_value, size_t attr_size, int flags,
++                     bool get_write);
+ 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 user_namespace *user_ns,
+@@ -117,6 +118,7 @@ int ksmbd_vfs_remove_xattr(struct user_namespace *user_ns,
+ int ksmbd_vfs_kern_path_locked(struct ksmbd_work *work, char *name,
+                              unsigned int flags, struct path *parent_path,
+                              struct path *path, bool caseless);
++void ksmbd_vfs_kern_path_unlock(struct path *parent_path, struct path *path);
+ struct dentry *ksmbd_vfs_kern_path_create(struct ksmbd_work *work,
+                                         const char *name,
+                                         unsigned int flags,
+@@ -144,14 +146,16 @@ int ksmbd_vfs_remove_sd_xattrs(struct user_namespace *user_ns, const struct path
+ int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn,
+                          struct user_namespace *user_ns,
+                          const struct path *path,
+-                         struct smb_ntsd *pntsd, int len);
++                         struct smb_ntsd *pntsd, int len,
++                         bool get_write);
+ int ksmbd_vfs_get_sd_xattr(struct ksmbd_conn *conn,
+                          struct user_namespace *user_ns,
+                          struct dentry *dentry,
+                          struct smb_ntsd **pntsd);
+ int ksmbd_vfs_set_dos_attrib_xattr(struct user_namespace *user_ns,
+                                  const struct path *path,
+-                                 struct xattr_dos_attrib *da);
++                                 struct xattr_dos_attrib *da,
++                                 bool get_write);
+ int ksmbd_vfs_get_dos_attrib_xattr(struct user_namespace *user_ns,
+                                  struct dentry *dentry,
+                                  struct xattr_dos_attrib *da);
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-fix-potential-double-free-on-smb2_read_pipe-er.patch b/queue-6.1/ksmbd-fix-potential-double-free-on-smb2_read_pipe-er.patch
new file mode 100644 (file)
index 0000000..e884e7a
--- /dev/null
@@ -0,0 +1,43 @@
+From 095cce1d119cb8e983f458775c02fa4083b9603b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:13:10 +0900
+Subject: ksmbd: fix potential double free on smb2_read_pipe() error path
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit 1903e6d0578118e9aab1ee23f4a9de55737d1d05 ]
+
+Fix new smatch warnings:
+fs/smb/server/smb2pdu.c:6131 smb2_read_pipe() error: double free of 'rpc_resp'
+
+Fixes: e2b76ab8b5c9 ("ksmbd: add support for read compound")
+Reported-by: kernel test robot <lkp@intel.com>
+Reported-by: Dan Carpenter <dan.carpenter@linaro.org>
+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/smb/server/smb2pdu.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
+index c773272cd3ac2..a89a69d752a3b 100644
+--- a/fs/smb/server/smb2pdu.c
++++ b/fs/smb/server/smb2pdu.c
+@@ -6152,12 +6152,12 @@ static noinline int smb2_read_pipe(struct ksmbd_work *work)
+               memcpy(aux_payload_buf, rpc_resp->payload, rpc_resp->payload_sz);
+               nbytes = rpc_resp->payload_sz;
+-              kvfree(rpc_resp);
+               err = ksmbd_iov_pin_rsp_read(work, (void *)rsp,
+                                            offsetof(struct smb2_read_rsp, Buffer),
+                                            aux_payload_buf, nbytes);
+               if (err)
+                       goto out;
++              kvfree(rpc_resp);
+       } else {
+               err = ksmbd_iov_pin_rsp(work, (void *)rsp,
+                                       offsetof(struct smb2_read_rsp, Buffer));
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-fix-race-condition-between-tree-conn-lookup-an.patch b/queue-6.1/ksmbd-fix-race-condition-between-tree-conn-lookup-an.patch
new file mode 100644 (file)
index 0000000..ac8f94e
--- /dev/null
@@ -0,0 +1,280 @@
+From 67710341be33231c229f95062e6fc7dc194e81cb Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:13:07 +0900
+Subject: ksmbd: fix race condition between tree conn lookup and disconnect
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit 33b235a6e6ebe0f05f3586a71e8d281d00f71e2e ]
+
+if thread A in smb2_write is using work-tcon, other thread B use
+smb2_tree_disconnect free the tcon, then thread A will use free'd tcon.
+
+                            Time
+                             +
+ Thread A                    | Thread A
+ smb2_write                  | smb2_tree_disconnect
+                             |
+                             |
+                             |   kfree(tree_conn)
+                             |
+  // UAF!                    |
+  work->tcon->share_conf     |
+                             +
+
+This patch add state, reference count and lock for tree conn to fix race
+condition issue.
+
+Reported-by: luosili <rootlab@huawei.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/smb/server/mgmt/tree_connect.c | 42 ++++++++++++++++++++++++--
+ fs/smb/server/mgmt/tree_connect.h | 11 +++++--
+ fs/smb/server/mgmt/user_session.c |  1 +
+ fs/smb/server/mgmt/user_session.h |  1 +
+ fs/smb/server/server.c            |  2 ++
+ fs/smb/server/smb2pdu.c           | 50 +++++++++++++++++++++++--------
+ 6 files changed, 90 insertions(+), 17 deletions(-)
+
+diff --git a/fs/smb/server/mgmt/tree_connect.c b/fs/smb/server/mgmt/tree_connect.c
+index 408cddf2f094a..d2c81a8a11dda 100644
+--- a/fs/smb/server/mgmt/tree_connect.c
++++ b/fs/smb/server/mgmt/tree_connect.c
+@@ -73,7 +73,10 @@ ksmbd_tree_conn_connect(struct ksmbd_conn *conn, struct ksmbd_session *sess,
+       tree_conn->user = sess->user;
+       tree_conn->share_conf = sc;
++      tree_conn->t_state = TREE_NEW;
+       status.tree_conn = tree_conn;
++      atomic_set(&tree_conn->refcount, 1);
++      init_waitqueue_head(&tree_conn->refcount_q);
+       ret = xa_err(xa_store(&sess->tree_conns, tree_conn->id, tree_conn,
+                             GFP_KERNEL));
+@@ -93,14 +96,33 @@ ksmbd_tree_conn_connect(struct ksmbd_conn *conn, struct ksmbd_session *sess,
+       return status;
+ }
++void ksmbd_tree_connect_put(struct ksmbd_tree_connect *tcon)
++{
++      /*
++       * Checking waitqueue to releasing tree connect on
++       * tree disconnect. waitqueue_active is safe because it
++       * uses atomic operation for condition.
++       */
++      if (!atomic_dec_return(&tcon->refcount) &&
++          waitqueue_active(&tcon->refcount_q))
++              wake_up(&tcon->refcount_q);
++}
++
+ int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess,
+                              struct ksmbd_tree_connect *tree_conn)
+ {
+       int ret;
++      write_lock(&sess->tree_conns_lock);
++      xa_erase(&sess->tree_conns, tree_conn->id);
++      write_unlock(&sess->tree_conns_lock);
++
++      if (!atomic_dec_and_test(&tree_conn->refcount))
++              wait_event(tree_conn->refcount_q,
++                         atomic_read(&tree_conn->refcount) == 0);
++
+       ret = ksmbd_ipc_tree_disconnect_request(sess->id, tree_conn->id);
+       ksmbd_release_tree_conn_id(sess, tree_conn->id);
+-      xa_erase(&sess->tree_conns, tree_conn->id);
+       ksmbd_share_config_put(tree_conn->share_conf);
+       kfree(tree_conn);
+       return ret;
+@@ -111,11 +133,15 @@ struct ksmbd_tree_connect *ksmbd_tree_conn_lookup(struct ksmbd_session *sess,
+ {
+       struct ksmbd_tree_connect *tcon;
++      read_lock(&sess->tree_conns_lock);
+       tcon = xa_load(&sess->tree_conns, id);
+       if (tcon) {
+-              if (test_bit(TREE_CONN_EXPIRE, &tcon->status))
++              if (tcon->t_state != TREE_CONNECTED)
++                      tcon = NULL;
++              else if (!atomic_inc_not_zero(&tcon->refcount))
+                       tcon = NULL;
+       }
++      read_unlock(&sess->tree_conns_lock);
+       return tcon;
+ }
+@@ -129,8 +155,18 @@ int ksmbd_tree_conn_session_logoff(struct ksmbd_session *sess)
+       if (!sess)
+               return -EINVAL;
+-      xa_for_each(&sess->tree_conns, id, tc)
++      xa_for_each(&sess->tree_conns, id, tc) {
++              write_lock(&sess->tree_conns_lock);
++              if (tc->t_state == TREE_DISCONNECTED) {
++                      write_unlock(&sess->tree_conns_lock);
++                      ret = -ENOENT;
++                      continue;
++              }
++              tc->t_state = TREE_DISCONNECTED;
++              write_unlock(&sess->tree_conns_lock);
++
+               ret |= ksmbd_tree_conn_disconnect(sess, tc);
++      }
+       xa_destroy(&sess->tree_conns);
+       return ret;
+ }
+diff --git a/fs/smb/server/mgmt/tree_connect.h b/fs/smb/server/mgmt/tree_connect.h
+index 562d647ad9fad..6377a70b811c8 100644
+--- a/fs/smb/server/mgmt/tree_connect.h
++++ b/fs/smb/server/mgmt/tree_connect.h
+@@ -14,7 +14,11 @@ struct ksmbd_share_config;
+ struct ksmbd_user;
+ struct ksmbd_conn;
+-#define TREE_CONN_EXPIRE              1
++enum {
++      TREE_NEW = 0,
++      TREE_CONNECTED,
++      TREE_DISCONNECTED
++};
+ struct ksmbd_tree_connect {
+       int                             id;
+@@ -27,7 +31,9 @@ struct ksmbd_tree_connect {
+       int                             maximal_access;
+       bool                            posix_extensions;
+-      unsigned long                   status;
++      atomic_t                        refcount;
++      wait_queue_head_t               refcount_q;
++      unsigned int                    t_state;
+ };
+ struct ksmbd_tree_conn_status {
+@@ -46,6 +52,7 @@ struct ksmbd_session;
+ struct ksmbd_tree_conn_status
+ ksmbd_tree_conn_connect(struct ksmbd_conn *conn, struct ksmbd_session *sess,
+                       const char *share_name);
++void ksmbd_tree_connect_put(struct ksmbd_tree_connect *tcon);
+ int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess,
+                              struct ksmbd_tree_connect *tree_conn);
+diff --git a/fs/smb/server/mgmt/user_session.c b/fs/smb/server/mgmt/user_session.c
+index b8be14a96cf66..15f68ee050894 100644
+--- a/fs/smb/server/mgmt/user_session.c
++++ b/fs/smb/server/mgmt/user_session.c
+@@ -355,6 +355,7 @@ static struct ksmbd_session *__session_create(int protocol)
+       xa_init(&sess->ksmbd_chann_list);
+       xa_init(&sess->rpc_handle_list);
+       sess->sequence_number = 1;
++      rwlock_init(&sess->tree_conns_lock);
+       ret = __init_smb2_session(sess);
+       if (ret)
+diff --git a/fs/smb/server/mgmt/user_session.h b/fs/smb/server/mgmt/user_session.h
+index f99d475b28db4..63cb08fffde84 100644
+--- a/fs/smb/server/mgmt/user_session.h
++++ b/fs/smb/server/mgmt/user_session.h
+@@ -60,6 +60,7 @@ struct ksmbd_session {
+       struct ksmbd_file_table         file_table;
+       unsigned long                   last_active;
++      rwlock_t                        tree_conns_lock;
+ };
+ static inline int test_session_flag(struct ksmbd_session *sess, int bit)
+diff --git a/fs/smb/server/server.c b/fs/smb/server/server.c
+index 633383e55723c..11b201e6ee44b 100644
+--- a/fs/smb/server/server.c
++++ b/fs/smb/server/server.c
+@@ -241,6 +241,8 @@ static void __handle_ksmbd_work(struct ksmbd_work *work,
+       } while (is_chained == true);
+ send:
++      if (work->tcon)
++              ksmbd_tree_connect_put(work->tcon);
+       smb3_preauth_hash_rsp(work);
+       if (work->sess && work->sess->enc && work->encrypted &&
+           conn->ops->encrypt_resp) {
+diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
+index a76529512acfb..aad08866746c8 100644
+--- a/fs/smb/server/smb2pdu.c
++++ b/fs/smb/server/smb2pdu.c
+@@ -1993,6 +1993,9 @@ int smb2_tree_connect(struct ksmbd_work *work)
+       if (conn->posix_ext_supported)
+               status.tree_conn->posix_extensions = true;
++      write_lock(&sess->tree_conns_lock);
++      status.tree_conn->t_state = TREE_CONNECTED;
++      write_unlock(&sess->tree_conns_lock);
+       rsp->StructureSize = cpu_to_le16(16);
+ out_err1:
+       rsp->Capabilities = 0;
+@@ -2122,27 +2125,50 @@ int smb2_tree_disconnect(struct ksmbd_work *work)
+       ksmbd_debug(SMB, "request\n");
++      if (!tcon) {
++              ksmbd_debug(SMB, "Invalid tid %d\n", req->hdr.Id.SyncId.TreeId);
++
++              rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED;
++              err = -ENOENT;
++              goto err_out;
++      }
++
++      ksmbd_close_tree_conn_fds(work);
++
++      write_lock(&sess->tree_conns_lock);
++      if (tcon->t_state == TREE_DISCONNECTED) {
++              write_unlock(&sess->tree_conns_lock);
++              rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED;
++              err = -ENOENT;
++              goto err_out;
++      }
++
++      WARN_ON_ONCE(atomic_dec_and_test(&tcon->refcount));
++      tcon->t_state = TREE_DISCONNECTED;
++      write_unlock(&sess->tree_conns_lock);
++
++      err = ksmbd_tree_conn_disconnect(sess, tcon);
++      if (err) {
++              rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED;
++              goto err_out;
++      }
++
++      work->tcon = NULL;
++
+       rsp->StructureSize = cpu_to_le16(4);
+       err = ksmbd_iov_pin_rsp(work, rsp,
+                               sizeof(struct smb2_tree_disconnect_rsp));
+       if (err) {
+               rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES;
+-              smb2_set_err_rsp(work);
+-              return err;
++              goto err_out;
+       }
+-      if (!tcon || test_and_set_bit(TREE_CONN_EXPIRE, &tcon->status)) {
+-              ksmbd_debug(SMB, "Invalid tid %d\n", req->hdr.Id.SyncId.TreeId);
++      return 0;
+-              rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED;
+-              smb2_set_err_rsp(work);
+-              return -ENOENT;
+-      }
++err_out:
++      smb2_set_err_rsp(work);
++      return err;
+-      ksmbd_close_tree_conn_fds(work);
+-      ksmbd_tree_conn_disconnect(sess, tcon);
+-      work->tcon = NULL;
+-      return 0;
+ }
+ /**
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-fix-race-condition-from-parallel-smb2-lock-req.patch b/queue-6.1/ksmbd-fix-race-condition-from-parallel-smb2-lock-req.patch
new file mode 100644 (file)
index 0000000..8da6cbb
--- /dev/null
@@ -0,0 +1,85 @@
+From f27d88cd8c7a4cd964f92f4bd2d661a92ef5a153 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:13:06 +0900
+Subject: ksmbd: fix race condition from parallel smb2 lock requests
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit 75ac9a3dd65f7eab4d12b0a0f744234b5300a491 ]
+
+There is a race condition issue between parallel smb2 lock request.
+
+                                            Time
+                                             +
+Thread A                                     | Thread A
+smb2_lock                                    | smb2_lock
+                                             |
+ insert smb_lock to lock_list                |
+ spin_unlock(&work->conn->llist_lock)        |
+                                             |
+                                             |   spin_lock(&conn->llist_lock);
+                                             |   kfree(cmp_lock);
+                                             |
+ // UAF!                                     |
+ list_add(&smb_lock->llist, &rollback_list)  +
+
+This patch swaps the line for adding the smb lock to the rollback list and
+adding the lock list of connection to fix the race issue.
+
+Reported-by: luosili <rootlab@huawei.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/smb/server/smb2pdu.c | 12 +-----------
+ 1 file changed, 1 insertion(+), 11 deletions(-)
+
+diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
+index e8c779fa354ca..a76529512acfb 100644
+--- a/fs/smb/server/smb2pdu.c
++++ b/fs/smb/server/smb2pdu.c
+@@ -7038,10 +7038,6 @@ int smb2_lock(struct ksmbd_work *work)
+                               ksmbd_debug(SMB,
+                                           "would have to wait for getting lock\n");
+-                              spin_lock(&work->conn->llist_lock);
+-                              list_add_tail(&smb_lock->clist,
+-                                            &work->conn->lock_list);
+-                              spin_unlock(&work->conn->llist_lock);
+                               list_add(&smb_lock->llist, &rollback_list);
+                               argv = kmalloc(sizeof(void *), GFP_KERNEL);
+@@ -7073,9 +7069,6 @@ int smb2_lock(struct ksmbd_work *work)
+                               if (work->state != KSMBD_WORK_ACTIVE) {
+                                       list_del(&smb_lock->llist);
+-                                      spin_lock(&work->conn->llist_lock);
+-                                      list_del(&smb_lock->clist);
+-                                      spin_unlock(&work->conn->llist_lock);
+                                       locks_free_lock(flock);
+                                       if (work->state == KSMBD_WORK_CANCELLED) {
+@@ -7095,19 +7088,16 @@ int smb2_lock(struct ksmbd_work *work)
+                               }
+                               list_del(&smb_lock->llist);
+-                              spin_lock(&work->conn->llist_lock);
+-                              list_del(&smb_lock->clist);
+-                              spin_unlock(&work->conn->llist_lock);
+                               release_async_work(work);
+                               goto retry;
+                       } else if (!rc) {
++                              list_add(&smb_lock->llist, &rollback_list);
+                               spin_lock(&work->conn->llist_lock);
+                               list_add_tail(&smb_lock->clist,
+                                             &work->conn->lock_list);
+                               list_add_tail(&smb_lock->flist,
+                                             &fp->lock_list);
+                               spin_unlock(&work->conn->llist_lock);
+-                              list_add(&smb_lock->llist, &rollback_list);
+                               ksmbd_debug(SMB, "successful in taking lock\n");
+                       } else {
+                               goto out;
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-fix-race-condition-from-parallel-smb2-logoff-r.patch b/queue-6.1/ksmbd-fix-race-condition-from-parallel-smb2-logoff-r.patch
new file mode 100644 (file)
index 0000000..5f70098
--- /dev/null
@@ -0,0 +1,71 @@
+From 5547b324bb52d3478a4a7ad73d054861ed035aca Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:13:05 +0900
+Subject: ksmbd: fix race condition from parallel smb2 logoff requests
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit 7ca9da7d873ee8024e9548d3366101c2b6843eab ]
+
+If parallel smb2 logoff requests come in before closing door, running
+request count becomes more than 1 even though connection status is set to
+KSMBD_SESS_NEED_RECONNECT. It can't get condition true, and sleep forever.
+This patch fix race condition problem by returning error if connection
+status was already set to KSMBD_SESS_NEED_RECONNECT.
+
+Reported-by: luosili <rootlab@huawei.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/smb/server/smb2pdu.c | 24 ++++++++++++++++--------
+ 1 file changed, 16 insertions(+), 8 deletions(-)
+
+diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
+index d5bf1f480700a..e8c779fa354ca 100644
+--- a/fs/smb/server/smb2pdu.c
++++ b/fs/smb/server/smb2pdu.c
+@@ -2164,17 +2164,17 @@ int smb2_session_logoff(struct ksmbd_work *work)
+       ksmbd_debug(SMB, "request\n");
+-      sess_id = le64_to_cpu(req->hdr.SessionId);
+-
+-      rsp->StructureSize = cpu_to_le16(4);
+-      err = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_logoff_rsp));
+-      if (err) {
+-              rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES;
++      ksmbd_conn_lock(conn);
++      if (!ksmbd_conn_good(conn)) {
++              ksmbd_conn_unlock(conn);
++              rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED;
+               smb2_set_err_rsp(work);
+-              return err;
++              return -ENOENT;
+       }
+-
++      sess_id = le64_to_cpu(req->hdr.SessionId);
+       ksmbd_all_conn_set_status(sess_id, KSMBD_SESS_NEED_RECONNECT);
++      ksmbd_conn_unlock(conn);
++
+       ksmbd_close_session_fds(work);
+       ksmbd_conn_wait_idle(conn, sess_id);
+@@ -2196,6 +2196,14 @@ int smb2_session_logoff(struct ksmbd_work *work)
+       ksmbd_free_user(sess->user);
+       sess->user = NULL;
+       ksmbd_all_conn_set_status(sess_id, KSMBD_SESS_NEED_NEGOTIATE);
++
++      rsp->StructureSize = cpu_to_le16(4);
++      err = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_logoff_rsp));
++      if (err) {
++              rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES;
++              smb2_set_err_rsp(work);
++              return err;
++      }
+       return 0;
+ }
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-fix-race-condition-with-fp.patch b/queue-6.1/ksmbd-fix-race-condition-with-fp.patch
new file mode 100644 (file)
index 0000000..8f24d02
--- /dev/null
@@ -0,0 +1,152 @@
+From b57845f40f0bba8a4b103274bc0d12a861038b48 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:13:04 +0900
+Subject: ksmbd: fix race condition with fp
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit 5a7ee91d1154f35418367a6eaae74046fd06ed89 ]
+
+fp can used in each command. If smb2_close command is coming at the
+same time, UAF issue can happen by race condition.
+
+                           Time
+                            +
+Thread A                    | Thread B1 B2 .... B5
+smb2_open                   | smb2_close
+                            |
+ __open_id                  |
+   insert fp to file_table  |
+                            |
+                            |   atomic_dec_and_test(&fp->refcount)
+                            |   if fp->refcount == 0, free fp by kfree.
+ // UAF!                    |
+ use fp                     |
+                            +
+This patch add f_state not to use freed fp is used and not to free fp in
+use.
+
+Reported-by: luosili <rootlab@huawei.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/smb/server/smb2pdu.c   |  4 +++-
+ fs/smb/server/vfs_cache.c | 23 ++++++++++++++++++++---
+ fs/smb/server/vfs_cache.h |  9 +++++++++
+ 3 files changed, 32 insertions(+), 4 deletions(-)
+
+diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
+index 42697ea86d47b..d5bf1f480700a 100644
+--- a/fs/smb/server/smb2pdu.c
++++ b/fs/smb/server/smb2pdu.c
+@@ -3370,8 +3370,10 @@ int smb2_open(struct ksmbd_work *work)
+       }
+       ksmbd_revert_fsids(work);
+ err_out1:
+-      if (!rc)
++      if (!rc) {
++              ksmbd_update_fstate(&work->sess->file_table, fp, FP_INITED);
+               rc = ksmbd_iov_pin_rsp(work, (void *)rsp, iov_len);
++      }
+       if (rc) {
+               if (rc == -EINVAL)
+                       rsp->hdr.Status = STATUS_INVALID_PARAMETER;
+diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c
+index 94ad8fa07b46e..f600279b0a9ee 100644
+--- a/fs/smb/server/vfs_cache.c
++++ b/fs/smb/server/vfs_cache.c
+@@ -332,6 +332,9 @@ static void __ksmbd_close_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp)
+ static struct ksmbd_file *ksmbd_fp_get(struct ksmbd_file *fp)
+ {
++      if (fp->f_state != FP_INITED)
++              return NULL;
++
+       if (!atomic_inc_not_zero(&fp->refcount))
+               return NULL;
+       return fp;
+@@ -381,15 +384,20 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id)
+               return 0;
+       ft = &work->sess->file_table;
+-      read_lock(&ft->lock);
++      write_lock(&ft->lock);
+       fp = idr_find(ft->idr, id);
+       if (fp) {
+               set_close_state_blocked_works(fp);
+-              if (!atomic_dec_and_test(&fp->refcount))
++              if (fp->f_state != FP_INITED)
+                       fp = NULL;
++              else {
++                      fp->f_state = FP_CLOSED;
++                      if (!atomic_dec_and_test(&fp->refcount))
++                              fp = NULL;
++              }
+       }
+-      read_unlock(&ft->lock);
++      write_unlock(&ft->lock);
+       if (!fp)
+               return -EINVAL;
+@@ -569,6 +577,7 @@ struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp)
+       fp->tcon                = work->tcon;
+       fp->volatile_id         = KSMBD_NO_FID;
+       fp->persistent_id       = KSMBD_NO_FID;
++      fp->f_state             = FP_NEW;
+       fp->f_ci                = ksmbd_inode_get(fp);
+       if (!fp->f_ci) {
+@@ -590,6 +599,14 @@ struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp)
+       return ERR_PTR(ret);
+ }
++void ksmbd_update_fstate(struct ksmbd_file_table *ft, struct ksmbd_file *fp,
++                       unsigned int state)
++{
++      write_lock(&ft->lock);
++      fp->f_state = state;
++      write_unlock(&ft->lock);
++}
++
+ static int
+ __close_file_table_ids(struct ksmbd_file_table *ft,
+                      struct ksmbd_tree_connect *tcon,
+diff --git a/fs/smb/server/vfs_cache.h b/fs/smb/server/vfs_cache.h
+index fcb13413fa8d9..03d0bf941216f 100644
+--- a/fs/smb/server/vfs_cache.h
++++ b/fs/smb/server/vfs_cache.h
+@@ -60,6 +60,12 @@ struct ksmbd_inode {
+       __le32                          m_fattr;
+ };
++enum {
++      FP_NEW = 0,
++      FP_INITED,
++      FP_CLOSED
++};
++
+ struct ksmbd_file {
+       struct file                     *filp;
+       u64                             persistent_id;
+@@ -98,6 +104,7 @@ struct ksmbd_file {
+       /* if ls is happening on directory, below is valid*/
+       struct ksmbd_readdir_data       readdir_data;
+       int                             dot_dotdot[2];
++      unsigned int                    f_state;
+ };
+ static inline void set_ctx_actor(struct dir_context *ctx,
+@@ -142,6 +149,8 @@ int ksmbd_close_inode_fds(struct ksmbd_work *work, struct inode *inode);
+ int ksmbd_init_global_file_table(void);
+ void ksmbd_free_global_file_table(void);
+ void ksmbd_set_fd_limit(unsigned long limit);
++void ksmbd_update_fstate(struct ksmbd_file_table *ft, struct ksmbd_file *fp,
++                       unsigned int state);
+ /*
+  * INODE hash
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-fix-racy-issue-from-using-d_parent-and-d_name.patch b/queue-6.1/ksmbd-fix-racy-issue-from-using-d_parent-and-d_name.patch
new file mode 100644 (file)
index 0000000..4367d71
--- /dev/null
@@ -0,0 +1,1193 @@
+From 998937d36cb39e921a2def28ee189b78bb236fad Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:12:37 +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>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/namei.c                |  57 +++--
+ fs/smb/server/smb2pdu.c   | 147 +++----------
+ fs/smb/server/vfs.c       | 435 ++++++++++++++++----------------------
+ fs/smb/server/vfs.h       |  19 +-
+ fs/smb/server/vfs_cache.c |   5 +-
+ include/linux/namei.h     |   6 +
+ 6 files changed, 283 insertions(+), 386 deletions(-)
+
+diff --git a/fs/namei.c b/fs/namei.c
+index 6daaf84567195..b5578f4ce5d6e 100644
+--- a/fs/namei.c
++++ b/fs/namei.c
+@@ -253,6 +253,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;
+@@ -4214,7 +4245,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;
+@@ -4348,7 +4379,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)) {
+               struct user_namespace *mnt_userns;
+@@ -4922,7 +4953,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;
+@@ -4930,7 +4962,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/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
+index ca57e85abf915..fe10c75f6f2b9 100644
+--- a/fs/smb/server/smb2pdu.c
++++ b/fs/smb/server/smb2pdu.c
+@@ -2506,7 +2506,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);
+@@ -2797,8 +2797,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
+@@ -2807,7 +2809,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;
+                       }
+@@ -2815,26 +2816,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;
++              user_ns = mnt_user_ns(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;
+-              user_ns = mnt_user_ns(path.mnt);
+       }
++
+       if (stream_name) {
+               if (req->CreateOptions & FILE_DIRECTORY_FILE_LE) {
+                       if (s_type == DATA_STREAM) {
+@@ -2962,8 +2960,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(user_ns,
+-                                                        path.dentry);
++                              rc = inode_permission(user_ns,
++                                                    d_inode(path.dentry->d_parent),
++                                                    MAY_EXEC | MAY_WRITE);
+                               if (rc)
+                                       goto err_out;
+                       }
+@@ -3334,10 +3333,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;
+@@ -5481,44 +5483,19 @@ int smb2_echo(struct ksmbd_work *work)
+ static int smb2_rename(struct ksmbd_work *work,
+                      struct ksmbd_file *fp,
+-                     struct user_namespace *user_ns,
+                      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;
+@@ -5544,7 +5521,7 @@ static int smb2_rename(struct ksmbd_work *work,
+               if (rc)
+                       goto out;
+-              rc = ksmbd_vfs_setxattr(user_ns,
++              rc = ksmbd_vfs_setxattr(file_mnt_user_ns(fp->filp),
+                                       fp->filp->f_path.dentry,
+                                       xattr_stream_name,
+                                       NULL, 0, 0);
+@@ -5559,47 +5536,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;
+ }
+@@ -5639,18 +5587,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",
+@@ -5670,6 +5617,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);
+@@ -5847,12 +5798,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 user_namespace *user_ns;
+-      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;
+@@ -5862,32 +5807,10 @@ static int set_rename_info(struct ksmbd_work *work, struct ksmbd_file *fp,
+                       le32_to_cpu(rename_info->FileNameLength))
+               return -EINVAL;
+-      user_ns = file_mnt_user_ns(fp->filp);
+-      if (ksmbd_stream_fd(fp))
+-              goto next;
+-
+-      parent = dget_parent(dentry);
+-      ret = ksmbd_vfs_lock_parent(user_ns, 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, user_ns, 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/smb/server/vfs.c b/fs/smb/server/vfs.c
+index 187d31d984946..58a6665f1c3ab 100644
+--- a/fs/smb/server/vfs.c
++++ b/fs/smb/server/vfs.c
+@@ -17,6 +17,7 @@
+ #include <linux/vmalloc.h>
+ #include <linux/sched/xacct.h>
+ #include <linux/crc32c.h>
++#include <linux/namei.h>
+ #include "../../internal.h"   /* for vfs_path_lookup */
+@@ -36,19 +37,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)
+@@ -62,65 +50,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 user_namespace *user_ns, 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(user_ns, 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 user_namespace *user_ns,
+-                       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(user_ns, 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(user_ns, 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 user_namespace *user_ns,
+                                  struct dentry *dentry, __le32 *daccess)
+ {
+-      struct dentry *parent;
+       int ret = 0;
+       *daccess = cpu_to_le32(FILE_READ_ATTRIBUTES | READ_CONTROL);
+@@ -137,18 +137,9 @@ int ksmbd_vfs_query_maximal_access(struct user_namespace *user_ns,
+       if (!inode_permission(user_ns, d_inode(dentry), MAY_OPEN | MAY_EXEC))
+               *daccess |= FILE_EXECUTE_LE;
+-      parent = dget_parent(dentry);
+-      ret = ksmbd_vfs_lock_parent(user_ns, parent, dentry);
+-      if (ret) {
+-              dput(parent);
+-              return ret;
+-      }
+-
+-      if (!inode_permission(user_ns, d_inode(parent), MAY_EXEC | MAY_WRITE))
++      if (!inode_permission(user_ns, d_inode(dentry->d_parent), MAY_EXEC | MAY_WRITE))
+               *daccess |= FILE_DELETE_LE;
+-      inode_unlock(d_inode(parent));
+-      dput(parent);
+       return ret;
+ }
+@@ -581,54 +572,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 user_namespace *user_ns;
+-      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;
+-      }
+-
+-      user_ns = mnt_user_ns(path.mnt);
+-      parent = dget_parent(path.dentry);
+-      err = ksmbd_vfs_lock_parent(user_ns, 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(user_ns, d_inode(parent), path.dentry);
++      user_ns = mnt_user_ns(path->mnt);
++      if (S_ISDIR(d_inode(path->dentry)->i_mode)) {
++              err = vfs_rmdir(user_ns, 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(user_ns, d_inode(parent), path.dentry, NULL);
++              err = vfs_unlink(user_ns, 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;
+ }
+@@ -687,149 +656,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 user_namespace *src_user_ns,
+-                            struct dentry *src_dent_parent,
+-                            struct dentry *src_dent,
+-                            struct user_namespace *dst_user_ns,
+-                            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_user_ns, 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_userns = src_user_ns,
+-                      .old_dir        = d_inode(src_dent_parent),
+-                      .old_dentry     = src_dent,
+-                      .new_mnt_userns = dst_user_ns,
+-                      .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 user_namespace *user_ns;
+-      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_userns       = mnt_user_ns(old_path->mnt),
++      rd.old_dir              = d_inode(old_parent),
++      rd.old_dentry           = old_child,
++      rd.new_mnt_userns       = mnt_user_ns(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);
+-      user_ns = file_mnt_user_ns(fp->filp);
+-      src_child = lookup_one(user_ns, 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,
+-                               user_ns,
+-                               src_dent_parent,
+-                               src_dent,
+-                               mnt_user_ns(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;
+ }
+@@ -1080,14 +1014,16 @@ int ksmbd_vfs_remove_xattr(struct user_namespace *user_ns,
+       return vfs_removexattr(user_ns, dentry, attr_name);
+ }
+-int ksmbd_vfs_unlink(struct user_namespace *user_ns,
+-                   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 user_namespace *user_ns = file_mnt_user_ns(filp);
+-      err = ksmbd_vfs_lock_parent(user_ns, 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))
+@@ -1099,6 +1035,8 @@ int ksmbd_vfs_unlink(struct user_namespace *user_ns,
+       inode_unlock(d_inode(dir));
+       if (err)
+               ksmbd_debug(VFS, "failed to delete, err %d\n", err);
++out:
++      dput(dir);
+       return err;
+ }
+@@ -1201,7 +1139,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
+@@ -1209,24 +1147,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);
+@@ -1236,10 +1170,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;
+@@ -1248,12 +1182,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';
+@@ -1261,23 +1194,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/smb/server/vfs.h b/fs/smb/server/vfs.h
+index 593059ca85112..7660565ac2770 100644
+--- a/fs/smb/server/vfs.h
++++ b/fs/smb/server/vfs.h
+@@ -71,9 +71,7 @@ struct ksmbd_kstat {
+       __le32                  file_attributes;
+ };
+-int ksmbd_vfs_lock_parent(struct user_namespace *user_ns, struct dentry *parent,
+-                        struct dentry *child);
+-int ksmbd_vfs_may_delete(struct user_namespace *user_ns, struct dentry *dentry);
++int ksmbd_vfs_lock_parent(struct dentry *parent, struct dentry *child);
+ int ksmbd_vfs_query_maximal_access(struct user_namespace *user_ns,
+                                  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 user_namespace *user_ns,
+                          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 user_namespace *user_ns,
+-                   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 user_namespace *user_ns,
+diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c
+index 6ec6c129465d3..bafb34d4b5bbb 100644
+--- a/fs/smb/server/vfs_cache.c
++++ b/fs/smb/server/vfs_cache.c
+@@ -243,7 +243,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;
+@@ -262,11 +261,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_user_ns(filp), dir, dentry);
++                      ksmbd_vfs_unlink(filp);
+                       write_lock(&ci->m_lock);
+               }
+               write_unlock(&ci->m_lock);
+diff --git a/include/linux/namei.h b/include/linux/namei.h
+index 2b66021c740dd..5c0149603dc3d 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);
+ extern struct dentry *try_lookup_one_len(const char *, struct dentry *, int);
+ extern struct dentry *lookup_one_len(const char *, struct dentry *, int);
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-fix-recursive-locking-in-vfs-helpers.patch b/queue-6.1/ksmbd-fix-recursive-locking-in-vfs-helpers.patch
new file mode 100644 (file)
index 0000000..d2efb53
--- /dev/null
@@ -0,0 +1,145 @@
+From 92a6fffdf64337ca6e0860aac8b64eedafa8bfc5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:13:14 +0900
+Subject: ksmbd: fix recursive locking in vfs helpers
+
+From: Marios Makassikis <mmakassikis@freebox.fr>
+
+[ Upstream commit 807252f028c59b9a3bac4d62ad84761548c10f11 ]
+
+Running smb2.rename test from Samba smbtorture suite against a kernel built
+with lockdep triggers a "possible recursive locking detected" warning.
+
+This is because mnt_want_write() is called twice with no mnt_drop_write()
+in between:
+  -> ksmbd_vfs_mkdir()
+    -> ksmbd_vfs_kern_path_create()
+       -> kern_path_create()
+          -> filename_create()
+            -> mnt_want_write()
+       -> mnt_want_write()
+
+Fix this by removing the mnt_want_write/mnt_drop_write calls from vfs
+helpers that call kern_path_create().
+
+Full lockdep trace below:
+
+============================================
+WARNING: possible recursive locking detected
+6.6.0-rc5 #775 Not tainted
+--------------------------------------------
+kworker/1:1/32 is trying to acquire lock:
+ffff888005ac83f8 (sb_writers#5){.+.+}-{0:0}, at: ksmbd_vfs_mkdir+0xe1/0x410
+
+but task is already holding lock:
+ffff888005ac83f8 (sb_writers#5){.+.+}-{0:0}, at: filename_create+0xb6/0x260
+
+other info that might help us debug this:
+ Possible unsafe locking scenario:
+
+       CPU0
+       ----
+  lock(sb_writers#5);
+  lock(sb_writers#5);
+
+ *** DEADLOCK ***
+
+ May be due to missing lock nesting notation
+
+4 locks held by kworker/1:1/32:
+ #0: ffff8880064e4138 ((wq_completion)ksmbd-io){+.+.}-{0:0}, at: process_one_work+0x40e/0x980
+ #1: ffff888005b0fdd0 ((work_completion)(&work->work)){+.+.}-{0:0}, at: process_one_work+0x40e/0x980
+ #2: ffff888005ac83f8 (sb_writers#5){.+.+}-{0:0}, at: filename_create+0xb6/0x260
+ #3: ffff8880057ce760 (&type->i_mutex_dir_key#3/1){+.+.}-{3:3}, at: filename_create+0x123/0x260
+
+Cc: stable@vger.kernel.org
+Fixes: 40b268d384a2 ("ksmbd: add mnt_want_write to ksmbd vfs functions")
+Signed-off-by: Marios Makassikis <mmakassikis@freebox.fr>
+Acked-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/server/vfs.c | 23 +++--------------------
+ 1 file changed, 3 insertions(+), 20 deletions(-)
+
+diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c
+index 071c344dd0333..e2e454eba4095 100644
+--- a/fs/smb/server/vfs.c
++++ b/fs/smb/server/vfs.c
+@@ -174,10 +174,6 @@ 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_user_ns(path.mnt), d_inode(path.dentry),
+                        dentry, mode, true);
+@@ -187,9 +183,7 @@ 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;
+ }
+@@ -220,10 +214,6 @@ 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;
+-
+       user_ns = mnt_user_ns(path.mnt);
+       mode |= S_IFDIR;
+       err = vfs_mkdir(user_ns, d_inode(path.dentry), dentry, mode);
+@@ -234,21 +224,19 @@ int ksmbd_vfs_mkdir(struct ksmbd_work *work, const char *name, umode_t mode)
+                              dentry->d_name.len);
+               if (IS_ERR(d)) {
+                       err = PTR_ERR(d);
+-                      goto out_err1;
++                      goto out_err;
+               }
+               if (unlikely(d_is_negative(d))) {
+                       dput(d);
+                       err = -ENOENT;
+-                      goto out_err1;
++                      goto out_err;
+               }
+               ksmbd_vfs_inherit_owner(work, d_inode(path.dentry), d_inode(d));
+               dput(d);
+       }
+-out_err1:
+-      mnt_drop_write(path.mnt);
+-out_err2:
++out_err:
+       done_path_create(&path, dentry);
+       if (err)
+               pr_err("mkdir(%s): creation failed (err:%d)\n", name, err);
+@@ -666,16 +654,11 @@ 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_user_ns(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);
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-fix-resource-leak-in-smb2_lock.patch b/queue-6.1/ksmbd-fix-resource-leak-in-smb2_lock.patch
new file mode 100644 (file)
index 0000000..1d59847
--- /dev/null
@@ -0,0 +1,59 @@
+From 0d51a9c716ba0144b2ec5bb72e19beba8ed0f3b4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:12:23 +0900
+Subject: ksmbd: Fix resource leak in smb2_lock()
+
+From: Marios Makassikis <mmakassikis@freebox.fr>
+
+[ Upstream commit 01f6c61bae3d658058ee6322af77acea26a5ee3a ]
+
+"flock" is leaked if an error happens before smb2_lock_init(), as the
+lock is not added to the lock_list to be cleaned up.
+
+Signed-off-by: Marios Makassikis <mmakassikis@freebox.fr>
+Acked-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/server/smb2pdu.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
+index 554214fca5b78..21d0416f11012 100644
+--- a/fs/smb/server/smb2pdu.c
++++ b/fs/smb/server/smb2pdu.c
+@@ -6951,6 +6951,7 @@ int smb2_lock(struct ksmbd_work *work)
+               if (lock_start > U64_MAX - lock_length) {
+                       pr_err("Invalid lock range requested\n");
+                       rsp->hdr.Status = STATUS_INVALID_LOCK_RANGE;
++                      locks_free_lock(flock);
+                       goto out;
+               }
+@@ -6970,6 +6971,7 @@ int smb2_lock(struct ksmbd_work *work)
+                                   "the end offset(%llx) is smaller than the start offset(%llx)\n",
+                                   flock->fl_end, flock->fl_start);
+                       rsp->hdr.Status = STATUS_INVALID_LOCK_RANGE;
++                      locks_free_lock(flock);
+                       goto out;
+               }
+@@ -6981,6 +6983,7 @@ int smb2_lock(struct ksmbd_work *work)
+                                   flock->fl_type != F_UNLCK) {
+                                       pr_err("conflict two locks in one request\n");
+                                       err = -EINVAL;
++                                      locks_free_lock(flock);
+                                       goto out;
+                               }
+                       }
+@@ -6989,6 +6992,7 @@ int smb2_lock(struct ksmbd_work *work)
+               smb_lock = smb2_lock_init(flock, cmd, flags, &lock_list);
+               if (!smb_lock) {
+                       err = -EINVAL;
++                      locks_free_lock(flock);
+                       goto out;
+               }
+       }
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-fix-spelling-mistake-excceed-exceeded.patch b/queue-6.1/ksmbd-fix-spelling-mistake-excceed-exceeded.patch
new file mode 100644 (file)
index 0000000..636d7d0
--- /dev/null
@@ -0,0 +1,35 @@
+From 375933363fd663716b9e4e37444780b3b504da95 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:12:29 +0900
+Subject: ksmbd: Fix spelling mistake "excceed" -> "exceeded"
+
+From: Colin Ian King <colin.i.king@gmail.com>
+
+[ Upstream commit 7a17c61ee3b2683c40090179c273f4701fca9677 ]
+
+There is a spelling mistake in an error message. Fix it.
+
+Signed-off-by: Colin Ian King <colin.i.king@gmail.com>
+Acked-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/server/connection.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/fs/smb/server/connection.c b/fs/smb/server/connection.c
+index e885e0eb0dc35..ffbf14d024191 100644
+--- a/fs/smb/server/connection.c
++++ b/fs/smb/server/connection.c
+@@ -345,7 +345,7 @@ int ksmbd_conn_handler_loop(void *p)
+                       max_allowed_pdu_size = SMB3_MAX_MSGSIZE;
+               if (pdu_size > max_allowed_pdu_size) {
+-                      pr_err_ratelimited("PDU length(%u) excceed maximum allowed pdu size(%u) on connection(%d)\n",
++                      pr_err_ratelimited("PDU length(%u) exceeded maximum allowed pdu size(%u) on connection(%d)\n",
+                                       pdu_size, max_allowed_pdu_size,
+                                       READ_ONCE(conn->status));
+                       break;
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-fix-typo-syncronous-synchronous.patch b/queue-6.1/ksmbd-fix-typo-syncronous-synchronous.patch
new file mode 100644 (file)
index 0000000..3e8ba40
--- /dev/null
@@ -0,0 +1,82 @@
+From 5a79311e5a7449d9097f927d811aaeb3598513ee Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:12:26 +0900
+Subject: ksmbd: fix typo, syncronous->synchronous
+
+From: Dawei Li <set_pte_at@outlook.com>
+
+[ Upstream commit f8d6e7442aa716a233c7eba99dec628f8885e00b ]
+
+syncronous->synchronous
+
+Signed-off-by: Dawei Li <set_pte_at@outlook.com>
+Acked-by: Namjae Jeon <linkinjeon@kernel.org>
+Reviewed-by: Sergey Senozhatsky <senozhatsky@chromium.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/server/connection.c | 4 ++--
+ fs/smb/server/ksmbd_work.h | 2 +-
+ fs/smb/server/smb2pdu.c    | 4 ++--
+ 3 files changed, 5 insertions(+), 5 deletions(-)
+
+diff --git a/fs/smb/server/connection.c b/fs/smb/server/connection.c
+index ff97cad8d5b45..e885e0eb0dc35 100644
+--- a/fs/smb/server/connection.c
++++ b/fs/smb/server/connection.c
+@@ -116,7 +116,7 @@ void ksmbd_conn_enqueue_request(struct ksmbd_work *work)
+       if (conn->ops->get_cmd_val(work) != SMB2_CANCEL_HE) {
+               requests_queue = &conn->requests;
+-              work->syncronous = true;
++              work->synchronous = true;
+       }
+       if (requests_queue) {
+@@ -141,7 +141,7 @@ int ksmbd_conn_try_dequeue_request(struct ksmbd_work *work)
+       spin_lock(&conn->request_lock);
+       if (!work->multiRsp) {
+               list_del_init(&work->request_entry);
+-              if (work->syncronous == false)
++              if (!work->synchronous)
+                       list_del_init(&work->async_request_entry);
+               ret = 0;
+       }
+diff --git a/fs/smb/server/ksmbd_work.h b/fs/smb/server/ksmbd_work.h
+index 5ece58e40c979..3234f2cf6327c 100644
+--- a/fs/smb/server/ksmbd_work.h
++++ b/fs/smb/server/ksmbd_work.h
+@@ -68,7 +68,7 @@ struct ksmbd_work {
+       /* Request is encrypted */
+       bool                            encrypted:1;
+       /* Is this SYNC or ASYNC ksmbd_work */
+-      bool                            syncronous:1;
++      bool                            synchronous:1;
+       bool                            need_invalidate_rkey:1;
+       unsigned int                    remote_key;
+diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
+index 21d0416f11012..d3939fd481497 100644
+--- a/fs/smb/server/smb2pdu.c
++++ b/fs/smb/server/smb2pdu.c
+@@ -508,7 +508,7 @@ int init_smb2_rsp_hdr(struct ksmbd_work *work)
+       rsp_hdr->SessionId = rcv_hdr->SessionId;
+       memcpy(rsp_hdr->Signature, rcv_hdr->Signature, 16);
+-      work->syncronous = true;
++      work->synchronous = true;
+       if (work->async_id) {
+               ksmbd_release_id(&conn->async_ida, work->async_id);
+               work->async_id = 0;
+@@ -671,7 +671,7 @@ int setup_async_work(struct ksmbd_work *work, void (*fn)(void **), void **arg)
+               pr_err("Failed to alloc async message id\n");
+               return id;
+       }
+-      work->syncronous = false;
++      work->synchronous = false;
+       work->async_id = id;
+       rsp_hdr->Id.AsyncId = cpu_to_le64(id);
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-fix-uninitialized-pointer-read-in-ksmbd_vfs_re.patch b/queue-6.1/ksmbd-fix-uninitialized-pointer-read-in-ksmbd_vfs_re.patch
new file mode 100644 (file)
index 0000000..e09c41e
--- /dev/null
@@ -0,0 +1,37 @@
+From 748bcda2d871dff1709fc4b62b4aed517aee25e1 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:12:38 +0900
+Subject: ksmbd: fix uninitialized pointer read in ksmbd_vfs_rename()
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit 48b47f0caaa8a9f05ed803cb4f335fa3a7bfc622 ]
+
+Uninitialized rd.delegated_inode can be used in vfs_rename().
+Fix this by setting rd.delegated_inode to NULL to avoid the uninitialized
+read.
+
+Fixes: 74d7970febf7 ("ksmbd: fix racy issue from using ->d_parent and ->d_name")
+Reported-by: Coverity Scan <scan-admin@coverity.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/smb/server/vfs.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c
+index 58a6665f1c3ab..5d2bb58d77e80 100644
+--- a/fs/smb/server/vfs.c
++++ b/fs/smb/server/vfs.c
+@@ -744,6 +744,7 @@ int ksmbd_vfs_rename(struct ksmbd_work *work, const struct path *old_path,
+       rd.new_dir              = new_path.dentry->d_inode,
+       rd.new_dentry           = new_dentry,
+       rd.flags                = flags,
++      rd.delegated_inode      = NULL,
+       err = vfs_rename(&rd);
+       if (err)
+               ksmbd_debug(VFS, "vfs_rename failed err %d\n", err);
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-fix-uninitialized-pointer-read-in-smb2_create_.patch b/queue-6.1/ksmbd-fix-uninitialized-pointer-read-in-smb2_create_.patch
new file mode 100644 (file)
index 0000000..da18a33
--- /dev/null
@@ -0,0 +1,49 @@
+From 5337945c1c1c20b0a848d6cf07ec45ee874e30b5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:12:39 +0900
+Subject: ksmbd: fix uninitialized pointer read in smb2_create_link()
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit df14afeed2e6c1bbadef7d2f9c46887bbd6d8d94 ]
+
+There is a case that file_present is true and path is uninitialized.
+This patch change file_present is set to false by default and set to
+true when patch is initialized.
+
+Fixes: 74d7970febf7 ("ksmbd: fix racy issue from using ->d_parent and ->d_name")
+Reported-by: Coverity Scan <scan-admin@coverity.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/smb/server/smb2pdu.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
+index fe10c75f6f2b9..028b1d1055b57 100644
+--- a/fs/smb/server/smb2pdu.c
++++ b/fs/smb/server/smb2pdu.c
+@@ -5559,7 +5559,7 @@ static int smb2_create_link(struct ksmbd_work *work,
+ {
+       char *link_name = NULL, *target_name = NULL, *pathname = NULL;
+       struct path path;
+-      bool file_present = true;
++      bool file_present = false;
+       int rc;
+       if (buf_len < (u64)sizeof(struct smb2_file_link_info) +
+@@ -5592,8 +5592,8 @@ static int smb2_create_link(struct ksmbd_work *work,
+       if (rc) {
+               if (rc != -ENOENT)
+                       goto out;
+-              file_present = false;
+-      }
++      } else
++              file_present = true;
+       if (file_info->ReplaceIfExists) {
+               if (file_present) {
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-fix-unsigned-expression-compared-with-zero.patch b/queue-6.1/ksmbd-fix-unsigned-expression-compared-with-zero.patch
new file mode 100644 (file)
index 0000000..4107083
--- /dev/null
@@ -0,0 +1,52 @@
+From 819f47cefce3f9a56997ba9f6c05882744bb281b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:12:51 +0900
+Subject: ksmbd: Fix unsigned expression compared with zero
+
+From: Wang Ming <machel@vivo.com>
+
+[ Upstream commit 0266a2f791294e0b4ba36f4a1d89b8615ea3cac0 ]
+
+The return value of the ksmbd_vfs_getcasexattr() is signed.
+However, the return value is being assigned to an unsigned
+variable and subsequently recasted, causing warnings. Use
+a signed type.
+
+Signed-off-by: Wang Ming <machel@vivo.com>
+Acked-by: Tom Talpey <tom@talpey.com>
+Acked-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/server/vfs.c | 7 ++++---
+ 1 file changed, 4 insertions(+), 3 deletions(-)
+
+diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c
+index d05d2d1274b04..73ce3fb6e4054 100644
+--- a/fs/smb/server/vfs.c
++++ b/fs/smb/server/vfs.c
+@@ -413,7 +413,8 @@ static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos,
+ {
+       char *stream_buf = NULL, *wbuf;
+       struct user_namespace *user_ns = file_mnt_user_ns(fp->filp);
+-      size_t size, v_len;
++      size_t size;
++      ssize_t v_len;
+       int err = 0;
+       ksmbd_debug(VFS, "write stream data pos : %llu, count : %zd\n",
+@@ -430,9 +431,9 @@ static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos,
+                                      fp->stream.name,
+                                      fp->stream.size,
+                                      &stream_buf);
+-      if ((int)v_len < 0) {
++      if (v_len < 0) {
+               pr_err("not found stream in xattr : %zd\n", v_len);
+-              err = (int)v_len;
++              err = v_len;
+               goto out;
+       }
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-fix-wrong-allocation-size-update-in-smb2_open.patch b/queue-6.1/ksmbd-fix-wrong-allocation-size-update-in-smb2_open.patch
new file mode 100644 (file)
index 0000000..a94b3f2
--- /dev/null
@@ -0,0 +1,84 @@
+From 052fe9f3cfc02eafa35479e5fae9d50579c0b8c8 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:13:32 +0900
+Subject: ksmbd: fix wrong allocation size update in smb2_open()
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit a9f106c765c12d2f58aa33431bd8ce8e9d8a404a ]
+
+When client send SMB2_CREATE_ALLOCATION_SIZE create context, ksmbd update
+old size to ->AllocationSize in smb2 create response. ksmbd_vfs_getattr()
+should be called after it to get updated stat result.
+
+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/smb/server/smb2pdu.c | 36 ++++++++++++++++++------------------
+ 1 file changed, 18 insertions(+), 18 deletions(-)
+
+diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
+index 2eea811fc8cb0..ea48dd06d4da3 100644
+--- a/fs/smb/server/smb2pdu.c
++++ b/fs/smb/server/smb2pdu.c
+@@ -2516,7 +2516,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_user_ns(path->mnt), path, &da, false);
++      rc = ksmbd_vfs_set_dos_attrib_xattr(mnt_user_ns(path->mnt), path, &da, true);
+       if (rc)
+               ksmbd_debug(SMB, "failed to store file attribute into xattr\n");
+ }
+@@ -3185,23 +3185,6 @@ int smb2_open(struct ksmbd_work *work)
+               goto err_out;
+       }
+-      rc = ksmbd_vfs_getattr(&path, &stat);
+-      if (rc)
+-              goto err_out;
+-
+-      if (stat.result_mask & STATX_BTIME)
+-              fp->create_time = ksmbd_UnixTimeToNT(stat.btime);
+-      else
+-              fp->create_time = ksmbd_UnixTimeToNT(stat.ctime);
+-      if (req->FileAttributes || fp->f_ci->m_fattr == 0)
+-              fp->f_ci->m_fattr =
+-                      cpu_to_le32(smb2_get_dos_mode(&stat, le32_to_cpu(req->FileAttributes)));
+-
+-      if (!created)
+-              smb2_update_xattrs(tcon, &path, fp);
+-      else
+-              smb2_new_xattrs(tcon, &path, fp);
+-
+       if (file_present || created)
+               ksmbd_vfs_kern_path_unlock(&parent_path, &path);
+@@ -3302,6 +3285,23 @@ int smb2_open(struct ksmbd_work *work)
+               }
+       }
++      rc = ksmbd_vfs_getattr(&path, &stat);
++      if (rc)
++              goto err_out1;
++
++      if (stat.result_mask & STATX_BTIME)
++              fp->create_time = ksmbd_UnixTimeToNT(stat.btime);
++      else
++              fp->create_time = ksmbd_UnixTimeToNT(stat.ctime);
++      if (req->FileAttributes || fp->f_ci->m_fattr == 0)
++              fp->f_ci->m_fattr =
++                      cpu_to_le32(smb2_get_dos_mode(&stat, le32_to_cpu(req->FileAttributes)));
++
++      if (!created)
++              smb2_update_xattrs(tcon, &path, fp);
++      else
++              smb2_new_xattrs(tcon, &path, fp);
++
+       memcpy(fp->client_guid, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE);
+       rsp->StructureSize = cpu_to_le16(89);
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-fix-wrong-error-response-status-by-using-set_s.patch b/queue-6.1/ksmbd-fix-wrong-error-response-status-by-using-set_s.patch
new file mode 100644 (file)
index 0000000..367f963
--- /dev/null
@@ -0,0 +1,45 @@
+From 36f0753185071ab62a7d2fc061c9020f746e9ffe Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:13:08 +0900
+Subject: ksmbd: fix wrong error response status by using set_smb2_rsp_status()
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit be0f89d4419dc5413a1cf06db3671c9949be0d52 ]
+
+set_smb2_rsp_status() after __process_request() sets the wrong error
+status. This patch resets all iov vectors and sets the error status
+on clean one.
+
+Fixes: e2b76ab8b5c9 ("ksmbd: add support for read compound")
+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/smb/server/smb2pdu.c | 9 +++++----
+ 1 file changed, 5 insertions(+), 4 deletions(-)
+
+diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
+index aad08866746c8..c773272cd3ac2 100644
+--- a/fs/smb/server/smb2pdu.c
++++ b/fs/smb/server/smb2pdu.c
+@@ -230,11 +230,12 @@ void set_smb2_rsp_status(struct ksmbd_work *work, __le32 err)
+ {
+       struct smb2_hdr *rsp_hdr;
+-      if (work->next_smb2_rcv_hdr_off)
+-              rsp_hdr = ksmbd_resp_buf_next(work);
+-      else
+-              rsp_hdr = smb2_get_msg(work->response_buf);
++      rsp_hdr = smb2_get_msg(work->response_buf);
+       rsp_hdr->Status = err;
++
++      work->iov_idx = 0;
++      work->iov_cnt = 0;
++      work->next_smb2_rcv_hdr_off = 0;
+       smb2_set_err_rsp(work);
+ }
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-fix-wrong-interim-response-on-compound.patch b/queue-6.1/ksmbd-fix-wrong-interim-response-on-compound.patch
new file mode 100644 (file)
index 0000000..b3e08fb
--- /dev/null
@@ -0,0 +1,164 @@
+From 421ed7f8cb1bb0c39afe592b314be35df3c8d833 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:12:55 +0900
+Subject: ksmbd: fix wrong interim response on compound
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit 041bba4414cda37d00063952c9bff9c3d5812a19 ]
+
+If smb2_lock or smb2_open request is compound, ksmbd could send wrong
+interim response to client. ksmbd allocate new interim buffer instead of
+using resonse buffer to support compound request.
+
+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/smb/server/ksmbd_work.c | 10 ++++++----
+ fs/smb/server/ksmbd_work.h |  2 +-
+ fs/smb/server/oplock.c     | 14 ++------------
+ fs/smb/server/smb2pdu.c    | 26 +++++++++++++++++---------
+ 4 files changed, 26 insertions(+), 26 deletions(-)
+
+diff --git a/fs/smb/server/ksmbd_work.c b/fs/smb/server/ksmbd_work.c
+index f49c2e01ea9fc..51def3ca74c01 100644
+--- a/fs/smb/server/ksmbd_work.c
++++ b/fs/smb/server/ksmbd_work.c
+@@ -160,9 +160,11 @@ int ksmbd_iov_pin_rsp_read(struct ksmbd_work *work, void *ib, int len,
+       return __ksmbd_iov_pin_rsp(work, ib, len, aux_buf, aux_size);
+ }
+-void ksmbd_iov_reset(struct ksmbd_work *work)
++int allocate_interim_rsp_buf(struct ksmbd_work *work)
+ {
+-      work->iov_idx = 0;
+-      work->iov_cnt = 0;
+-      *(__be32 *)work->iov[0].iov_base = 0;
++      work->response_buf = kzalloc(MAX_CIFS_SMALL_BUFFER_SIZE, GFP_KERNEL);
++      if (!work->response_buf)
++              return -ENOMEM;
++      work->response_sz = MAX_CIFS_SMALL_BUFFER_SIZE;
++      return 0;
+ }
+diff --git a/fs/smb/server/ksmbd_work.h b/fs/smb/server/ksmbd_work.h
+index 255157eb26dc4..8ca2c813246e6 100644
+--- a/fs/smb/server/ksmbd_work.h
++++ b/fs/smb/server/ksmbd_work.h
+@@ -131,5 +131,5 @@ bool ksmbd_queue_work(struct ksmbd_work *work);
+ int ksmbd_iov_pin_rsp_read(struct ksmbd_work *work, void *ib, int len,
+                          void *aux_buf, unsigned int aux_size);
+ int ksmbd_iov_pin_rsp(struct ksmbd_work *work, void *ib, int len);
+-void ksmbd_iov_reset(struct ksmbd_work *work);
++int allocate_interim_rsp_buf(struct ksmbd_work *work);
+ #endif /* __KSMBD_WORK_H__ */
+diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c
+index 4e12e3031bc53..90a035c27130f 100644
+--- a/fs/smb/server/oplock.c
++++ b/fs/smb/server/oplock.c
+@@ -616,15 +616,6 @@ static int oplock_break_pending(struct oplock_info *opinfo, int req_op_level)
+       return 0;
+ }
+-static inline int allocate_oplock_break_buf(struct ksmbd_work *work)
+-{
+-      work->response_buf = kzalloc(MAX_CIFS_SMALL_BUFFER_SIZE, GFP_KERNEL);
+-      if (!work->response_buf)
+-              return -ENOMEM;
+-      work->response_sz = MAX_CIFS_SMALL_BUFFER_SIZE;
+-      return 0;
+-}
+-
+ /**
+  * __smb2_oplock_break_noti() - send smb2 oplock break cmd from conn
+  * to client
+@@ -647,7 +638,7 @@ static void __smb2_oplock_break_noti(struct work_struct *wk)
+       if (!fp)
+               goto out;
+-      if (allocate_oplock_break_buf(work)) {
++      if (allocate_interim_rsp_buf(work)) {
+               pr_err("smb2_allocate_rsp_buf failed! ");
+               ksmbd_fd_put(work, fp);
+               goto out;
+@@ -752,7 +743,7 @@ static void __smb2_lease_break_noti(struct work_struct *wk)
+       struct lease_break_info *br_info = work->request_buf;
+       struct smb2_hdr *rsp_hdr;
+-      if (allocate_oplock_break_buf(work)) {
++      if (allocate_interim_rsp_buf(work)) {
+               ksmbd_debug(OPLOCK, "smb2_allocate_rsp_buf failed! ");
+               goto out;
+       }
+@@ -843,7 +834,6 @@ static int smb2_lease_break_noti(struct oplock_info *opinfo)
+                       setup_async_work(in_work, NULL, NULL);
+                       smb2_send_interim_resp(in_work, STATUS_PENDING);
+                       list_del(&in_work->interim_entry);
+-                      ksmbd_iov_reset(in_work);
+               }
+               INIT_WORK(&work->work, __smb2_lease_break_noti);
+               ksmbd_queue_work(work);
+diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
+index 0a40b793cedf4..dfb4fd4cb42f6 100644
+--- a/fs/smb/server/smb2pdu.c
++++ b/fs/smb/server/smb2pdu.c
+@@ -152,8 +152,8 @@ void smb2_set_err_rsp(struct ksmbd_work *work)
+               err_rsp->ByteCount = 0;
+               err_rsp->ErrorData[0] = 0;
+               err = ksmbd_iov_pin_rsp(work, (void *)err_rsp,
+-                                work->conn->vals->header_size +
+-                                SMB2_ERROR_STRUCTURE_SIZE2);
++                                      __SMB2_HEADER_STRUCTURE_SIZE +
++                                              SMB2_ERROR_STRUCTURE_SIZE2);
+               if (err)
+                       work->send_no_response = 1;
+       }
+@@ -709,13 +709,24 @@ void release_async_work(struct ksmbd_work *work)
+ void smb2_send_interim_resp(struct ksmbd_work *work, __le32 status)
+ {
+       struct smb2_hdr *rsp_hdr;
++      struct ksmbd_work *in_work = ksmbd_alloc_work_struct();
+-      rsp_hdr = ksmbd_resp_buf_next(work);
+-      smb2_set_err_rsp(work);
++      if (allocate_interim_rsp_buf(in_work)) {
++              pr_err("smb_allocate_rsp_buf failed!\n");
++              ksmbd_free_work_struct(in_work);
++              return;
++      }
++
++      in_work->conn = work->conn;
++      memcpy(smb2_get_msg(in_work->response_buf), ksmbd_resp_buf_next(work),
++             __SMB2_HEADER_STRUCTURE_SIZE);
++
++      rsp_hdr = smb2_get_msg(in_work->response_buf);
++      smb2_set_err_rsp(in_work);
+       rsp_hdr->Status = status;
+-      ksmbd_conn_write(work);
+-      rsp_hdr->Status = 0;
++      ksmbd_conn_write(in_work);
++      ksmbd_free_work_struct(in_work);
+ }
+ static __le32 smb2_get_reparse_tag_special_file(umode_t mode)
+@@ -7050,8 +7061,6 @@ int smb2_lock(struct ksmbd_work *work)
+                               list_del(&work->fp_entry);
+                               spin_unlock(&fp->f_lock);
+-                              ksmbd_iov_reset(work);
+-
+                               if (work->state != KSMBD_WORK_ACTIVE) {
+                                       list_del(&smb_lock->llist);
+                                       spin_lock(&work->conn->llist_lock);
+@@ -7069,7 +7078,6 @@ int smb2_lock(struct ksmbd_work *work)
+                                               goto out;
+                                       }
+-                                      init_smb2_rsp_hdr(work);
+                                       rsp->hdr.Status =
+                                               STATUS_RANGE_NOT_LOCKED;
+                                       kfree(smb_lock);
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-implements-sess-rpc_handle_list-as-xarray.patch b/queue-6.1/ksmbd-implements-sess-rpc_handle_list-as-xarray.patch
new file mode 100644 (file)
index 0000000..da2b087
--- /dev/null
@@ -0,0 +1,142 @@
+From 4ccf30b4bd0fc93d2a68ae3ee2111d3e9445c7ef Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:12:25 +0900
+Subject: ksmbd: Implements sess->rpc_handle_list as xarray
+
+From: Dawei Li <set_pte_at@outlook.com>
+
+[ Upstream commit b685757c7b08d5073046fb379be965fd6c06aafc ]
+
+For some ops on rpc handle:
+1. ksmbd_session_rpc_method(), possibly on high frequency.
+2. ksmbd_session_rpc_close().
+
+id is used as indexing key to lookup channel, in that case,
+linear search based on list may suffer a bit for performance.
+
+Implements sess->rpc_handle_list as xarray.
+
+Signed-off-by: Dawei Li <set_pte_at@outlook.com>
+Acked-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/server/mgmt/user_session.c | 37 ++++++++++++-------------------
+ fs/smb/server/mgmt/user_session.h |  2 +-
+ 2 files changed, 15 insertions(+), 24 deletions(-)
+
+diff --git a/fs/smb/server/mgmt/user_session.c b/fs/smb/server/mgmt/user_session.c
+index cf6621e21ba36..b8be14a96cf66 100644
+--- a/fs/smb/server/mgmt/user_session.c
++++ b/fs/smb/server/mgmt/user_session.c
+@@ -25,7 +25,6 @@ static DECLARE_RWSEM(sessions_table_lock);
+ struct ksmbd_session_rpc {
+       int                     id;
+       unsigned int            method;
+-      struct list_head        list;
+ };
+ static void free_channel_list(struct ksmbd_session *sess)
+@@ -58,15 +57,14 @@ static void __session_rpc_close(struct ksmbd_session *sess,
+ static void ksmbd_session_rpc_clear_list(struct ksmbd_session *sess)
+ {
+       struct ksmbd_session_rpc *entry;
++      long index;
+-      while (!list_empty(&sess->rpc_handle_list)) {
+-              entry = list_entry(sess->rpc_handle_list.next,
+-                                 struct ksmbd_session_rpc,
+-                                 list);
+-
+-              list_del(&entry->list);
++      xa_for_each(&sess->rpc_handle_list, index, entry) {
++              xa_erase(&sess->rpc_handle_list, index);
+               __session_rpc_close(sess, entry);
+       }
++
++      xa_destroy(&sess->rpc_handle_list);
+ }
+ static int __rpc_method(char *rpc_name)
+@@ -102,13 +100,13 @@ int ksmbd_session_rpc_open(struct ksmbd_session *sess, char *rpc_name)
+       entry = kzalloc(sizeof(struct ksmbd_session_rpc), GFP_KERNEL);
+       if (!entry)
+-              return -EINVAL;
++              return -ENOMEM;
+-      list_add(&entry->list, &sess->rpc_handle_list);
+       entry->method = method;
+       entry->id = ksmbd_ipc_id_alloc();
+       if (entry->id < 0)
+               goto free_entry;
++      xa_store(&sess->rpc_handle_list, entry->id, entry, GFP_KERNEL);
+       resp = ksmbd_rpc_open(sess, entry->id);
+       if (!resp)
+@@ -117,9 +115,9 @@ int ksmbd_session_rpc_open(struct ksmbd_session *sess, char *rpc_name)
+       kvfree(resp);
+       return entry->id;
+ free_id:
++      xa_erase(&sess->rpc_handle_list, entry->id);
+       ksmbd_rpc_id_free(entry->id);
+ free_entry:
+-      list_del(&entry->list);
+       kfree(entry);
+       return -EINVAL;
+ }
+@@ -128,24 +126,17 @@ void ksmbd_session_rpc_close(struct ksmbd_session *sess, int id)
+ {
+       struct ksmbd_session_rpc *entry;
+-      list_for_each_entry(entry, &sess->rpc_handle_list, list) {
+-              if (entry->id == id) {
+-                      list_del(&entry->list);
+-                      __session_rpc_close(sess, entry);
+-                      break;
+-              }
+-      }
++      entry = xa_erase(&sess->rpc_handle_list, id);
++      if (entry)
++              __session_rpc_close(sess, entry);
+ }
+ int ksmbd_session_rpc_method(struct ksmbd_session *sess, int id)
+ {
+       struct ksmbd_session_rpc *entry;
+-      list_for_each_entry(entry, &sess->rpc_handle_list, list) {
+-              if (entry->id == id)
+-                      return entry->method;
+-      }
+-      return 0;
++      entry = xa_load(&sess->rpc_handle_list, id);
++      return entry ? entry->method : 0;
+ }
+ void ksmbd_session_destroy(struct ksmbd_session *sess)
+@@ -362,7 +353,7 @@ static struct ksmbd_session *__session_create(int protocol)
+       set_session_flag(sess, protocol);
+       xa_init(&sess->tree_conns);
+       xa_init(&sess->ksmbd_chann_list);
+-      INIT_LIST_HEAD(&sess->rpc_handle_list);
++      xa_init(&sess->rpc_handle_list);
+       sess->sequence_number = 1;
+       ret = __init_smb2_session(sess);
+diff --git a/fs/smb/server/mgmt/user_session.h b/fs/smb/server/mgmt/user_session.h
+index 51f38e5b61abb..f99d475b28db4 100644
+--- a/fs/smb/server/mgmt/user_session.h
++++ b/fs/smb/server/mgmt/user_session.h
+@@ -52,7 +52,7 @@ struct ksmbd_session {
+       struct xarray                   ksmbd_chann_list;
+       struct xarray                   tree_conns;
+       struct ida                      tree_conn_ida;
+-      struct list_head                rpc_handle_list;
++      struct xarray                   rpc_handle_list;
+       __u8                            smb3encryptionkey[SMB3_ENC_DEC_KEY_SIZE];
+       __u8                            smb3decryptionkey[SMB3_ENC_DEC_KEY_SIZE];
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-lazy-v2-lease-break-on-smb2_write.patch b/queue-6.1/ksmbd-lazy-v2-lease-break-on-smb2_write.patch
new file mode 100644 (file)
index 0000000..24e9f51
--- /dev/null
@@ -0,0 +1,124 @@
+From c9626e2457663b84804fd5d00bf57452cd96ad2e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:13:30 +0900
+Subject: ksmbd: lazy v2 lease break on smb2_write()
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit c2a721eead71202a0d8ddd9b56ec8dce652c71d1 ]
+
+Don't immediately send directory lease break notification on smb2_write().
+Instead, It postpones it until smb2_close().
+
+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/smb/server/oplock.c    | 45 +++++++++++++++++++++++++++++++++++++--
+ fs/smb/server/oplock.h    |  1 +
+ fs/smb/server/vfs.c       |  3 +++
+ fs/smb/server/vfs_cache.h |  1 +
+ 4 files changed, 48 insertions(+), 2 deletions(-)
+
+diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c
+index 4a8745b3e8304..af0f6914eca45 100644
+--- a/fs/smb/server/oplock.c
++++ b/fs/smb/server/oplock.c
+@@ -396,8 +396,8 @@ void close_id_del_oplock(struct ksmbd_file *fp)
+ {
+       struct oplock_info *opinfo;
+-      if (S_ISDIR(file_inode(fp->filp)->i_mode))
+-              return;
++      if (fp->reserve_lease_break)
++              smb_lazy_parent_lease_break_close(fp);
+       opinfo = opinfo_get(fp);
+       if (!opinfo)
+@@ -1127,6 +1127,47 @@ void smb_send_parent_lease_break_noti(struct ksmbd_file *fp,
+       ksmbd_inode_put(p_ci);
+ }
++void smb_lazy_parent_lease_break_close(struct ksmbd_file *fp)
++{
++      struct oplock_info *opinfo;
++      struct ksmbd_inode *p_ci = NULL;
++
++      rcu_read_lock();
++      opinfo = rcu_dereference(fp->f_opinfo);
++      rcu_read_unlock();
++
++      if (!opinfo->is_lease || opinfo->o_lease->version != 2)
++              return;
++
++      p_ci = ksmbd_inode_lookup_lock(fp->filp->f_path.dentry->d_parent);
++      if (!p_ci)
++              return;
++
++      read_lock(&p_ci->m_lock);
++      list_for_each_entry(opinfo, &p_ci->m_op_list, op_entry) {
++              if (!opinfo->is_lease)
++                      continue;
++
++              if (opinfo->o_lease->state != SMB2_OPLOCK_LEVEL_NONE) {
++                      if (!atomic_inc_not_zero(&opinfo->refcount))
++                              continue;
++
++                      atomic_inc(&opinfo->conn->r_count);
++                      if (ksmbd_conn_releasing(opinfo->conn)) {
++                              atomic_dec(&opinfo->conn->r_count);
++                              continue;
++                      }
++                      read_unlock(&p_ci->m_lock);
++                      oplock_break(opinfo, SMB2_OPLOCK_LEVEL_NONE);
++                      opinfo_conn_put(opinfo);
++                      read_lock(&p_ci->m_lock);
++              }
++      }
++      read_unlock(&p_ci->m_lock);
++
++      ksmbd_inode_put(p_ci);
++}
++
+ /**
+  * smb_grant_oplock() - handle oplock/lease request on file open
+  * @work:             smb work
+diff --git a/fs/smb/server/oplock.h b/fs/smb/server/oplock.h
+index b64d1536882a1..5b93ea9196c01 100644
+--- a/fs/smb/server/oplock.h
++++ b/fs/smb/server/oplock.h
+@@ -129,4 +129,5 @@ int find_same_lease_key(struct ksmbd_session *sess, struct ksmbd_inode *ci,
+ void destroy_lease_table(struct ksmbd_conn *conn);
+ void smb_send_parent_lease_break_noti(struct ksmbd_file *fp,
+                                     struct lease_ctx_info *lctx);
++void smb_lazy_parent_lease_break_close(struct ksmbd_file *fp);
+ #endif /* __KSMBD_OPLOCK_H */
+diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c
+index 42f270ee399c7..fe2c80ea2e47e 100644
+--- a/fs/smb/server/vfs.c
++++ b/fs/smb/server/vfs.c
+@@ -518,6 +518,9 @@ int ksmbd_vfs_write(struct ksmbd_work *work, struct ksmbd_file *fp,
+               }
+       }
++      /* Reserve lease break for parent dir at closing time */
++      fp->reserve_lease_break = true;
++
+       /* Do we need to break any of a levelII oplock? */
+       smb_break_all_levII_oplock(work, fp, 1);
+diff --git a/fs/smb/server/vfs_cache.h b/fs/smb/server/vfs_cache.h
+index 4d4938d6029b6..a528f0cc775ae 100644
+--- a/fs/smb/server/vfs_cache.h
++++ b/fs/smb/server/vfs_cache.h
+@@ -105,6 +105,7 @@ struct ksmbd_file {
+       struct ksmbd_readdir_data       readdir_data;
+       int                             dot_dotdot[2];
+       unsigned int                    f_state;
++      bool                            reserve_lease_break;
+ };
+ static inline void set_ctx_actor(struct dir_context *ctx,
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-move-oplock-handling-after-unlock-parent-dir.patch b/queue-6.1/ksmbd-move-oplock-handling-after-unlock-parent-dir.patch
new file mode 100644 (file)
index 0000000..b0d2bf1
--- /dev/null
@@ -0,0 +1,363 @@
+From 66128cdefdbca0874d099a9f97ddb8fd57537576 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:13:22 +0900
+Subject: ksmbd: move oplock handling after unlock parent dir
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit 2e450920d58b4991a436c8cecf3484bcacd8e535 ]
+
+ksmbd should process secound parallel smb2 create request during waiting
+oplock break ack. parent lock range that is too large in smb2_open() causes
+smb2_open() to be serialized. Move the oplock handling to the bottom of
+smb2_open() and make it called after parent unlock. This fixes the failure
+of smb2.lease.breaking1 testcase.
+
+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/smb/server/smb2pdu.c | 121 +++++++++++++++++++++-------------------
+ 1 file changed, 65 insertions(+), 56 deletions(-)
+
+diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
+index ff4cc39b85c72..3d965be412751 100644
+--- a/fs/smb/server/smb2pdu.c
++++ b/fs/smb/server/smb2pdu.c
+@@ -2691,7 +2691,7 @@ int smb2_open(struct ksmbd_work *work)
+                   *(char *)req->Buffer == '\\') {
+                       pr_err("not allow directory name included leading slash\n");
+                       rc = -EINVAL;
+-                      goto err_out1;
++                      goto err_out2;
+               }
+               name = smb2_get_name(req->Buffer,
+@@ -2702,7 +2702,7 @@ int smb2_open(struct ksmbd_work *work)
+                       if (rc != -ENOMEM)
+                               rc = -ENOENT;
+                       name = NULL;
+-                      goto err_out1;
++                      goto err_out2;
+               }
+               ksmbd_debug(SMB, "converted name = %s\n", name);
+@@ -2710,28 +2710,28 @@ int smb2_open(struct ksmbd_work *work)
+                       if (!test_share_config_flag(work->tcon->share_conf,
+                                                   KSMBD_SHARE_FLAG_STREAMS)) {
+                               rc = -EBADF;
+-                              goto err_out1;
++                              goto err_out2;
+                       }
+                       rc = parse_stream_name(name, &stream_name, &s_type);
+                       if (rc < 0)
+-                              goto err_out1;
++                              goto err_out2;
+               }
+               rc = ksmbd_validate_filename(name);
+               if (rc < 0)
+-                      goto err_out1;
++                      goto err_out2;
+               if (ksmbd_share_veto_filename(share, name)) {
+                       rc = -ENOENT;
+                       ksmbd_debug(SMB, "Reject open(), vetoed file: %s\n",
+                                   name);
+-                      goto err_out1;
++                      goto err_out2;
+               }
+       } else {
+               name = kstrdup("", GFP_KERNEL);
+               if (!name) {
+                       rc = -ENOMEM;
+-                      goto err_out1;
++                      goto err_out2;
+               }
+       }
+@@ -2744,14 +2744,14 @@ int smb2_open(struct ksmbd_work *work)
+                      le32_to_cpu(req->ImpersonationLevel));
+               rc = -EIO;
+               rsp->hdr.Status = STATUS_BAD_IMPERSONATION_LEVEL;
+-              goto err_out1;
++              goto err_out2;
+       }
+       if (req->CreateOptions && !(req->CreateOptions & CREATE_OPTIONS_MASK_LE)) {
+               pr_err("Invalid create options : 0x%x\n",
+                      le32_to_cpu(req->CreateOptions));
+               rc = -EINVAL;
+-              goto err_out1;
++              goto err_out2;
+       } else {
+               if (req->CreateOptions & FILE_SEQUENTIAL_ONLY_LE &&
+                   req->CreateOptions & FILE_RANDOM_ACCESS_LE)
+@@ -2761,13 +2761,13 @@ int smb2_open(struct ksmbd_work *work)
+                   (FILE_OPEN_BY_FILE_ID_LE | CREATE_TREE_CONNECTION |
+                    FILE_RESERVE_OPFILTER_LE)) {
+                       rc = -EOPNOTSUPP;
+-                      goto err_out1;
++                      goto err_out2;
+               }
+               if (req->CreateOptions & FILE_DIRECTORY_FILE_LE) {
+                       if (req->CreateOptions & FILE_NON_DIRECTORY_FILE_LE) {
+                               rc = -EINVAL;
+-                              goto err_out1;
++                              goto err_out2;
+                       } else if (req->CreateOptions & FILE_NO_COMPRESSION_LE) {
+                               req->CreateOptions = ~(FILE_NO_COMPRESSION_LE);
+                       }
+@@ -2779,21 +2779,21 @@ int smb2_open(struct ksmbd_work *work)
+               pr_err("Invalid create disposition : 0x%x\n",
+                      le32_to_cpu(req->CreateDisposition));
+               rc = -EINVAL;
+-              goto err_out1;
++              goto err_out2;
+       }
+       if (!(req->DesiredAccess & DESIRED_ACCESS_MASK)) {
+               pr_err("Invalid desired access : 0x%x\n",
+                      le32_to_cpu(req->DesiredAccess));
+               rc = -EACCES;
+-              goto err_out1;
++              goto err_out2;
+       }
+       if (req->FileAttributes && !(req->FileAttributes & FILE_ATTRIBUTE_MASK_LE)) {
+               pr_err("Invalid file attribute : 0x%x\n",
+                      le32_to_cpu(req->FileAttributes));
+               rc = -EINVAL;
+-              goto err_out1;
++              goto err_out2;
+       }
+       if (req->CreateContextsOffset) {
+@@ -2801,19 +2801,19 @@ int smb2_open(struct ksmbd_work *work)
+               context = smb2_find_context_vals(req, SMB2_CREATE_EA_BUFFER, 4);
+               if (IS_ERR(context)) {
+                       rc = PTR_ERR(context);
+-                      goto err_out1;
++                      goto err_out2;
+               } else if (context) {
+                       ea_buf = (struct create_ea_buf_req *)context;
+                       if (le16_to_cpu(context->DataOffset) +
+                           le32_to_cpu(context->DataLength) <
+                           sizeof(struct create_ea_buf_req)) {
+                               rc = -EINVAL;
+-                              goto err_out1;
++                              goto err_out2;
+                       }
+                       if (req->CreateOptions & FILE_NO_EA_KNOWLEDGE_LE) {
+                               rsp->hdr.Status = STATUS_ACCESS_DENIED;
+                               rc = -EACCES;
+-                              goto err_out1;
++                              goto err_out2;
+                       }
+               }
+@@ -2821,7 +2821,7 @@ int smb2_open(struct ksmbd_work *work)
+                                                SMB2_CREATE_QUERY_MAXIMAL_ACCESS_REQUEST, 4);
+               if (IS_ERR(context)) {
+                       rc = PTR_ERR(context);
+-                      goto err_out1;
++                      goto err_out2;
+               } else if (context) {
+                       ksmbd_debug(SMB,
+                                   "get query maximal access context\n");
+@@ -2832,11 +2832,11 @@ int smb2_open(struct ksmbd_work *work)
+                                                SMB2_CREATE_TIMEWARP_REQUEST, 4);
+               if (IS_ERR(context)) {
+                       rc = PTR_ERR(context);
+-                      goto err_out1;
++                      goto err_out2;
+               } else if (context) {
+                       ksmbd_debug(SMB, "get timewarp context\n");
+                       rc = -EBADF;
+-                      goto err_out1;
++                      goto err_out2;
+               }
+               if (tcon->posix_extensions) {
+@@ -2844,7 +2844,7 @@ int smb2_open(struct ksmbd_work *work)
+                                                        SMB2_CREATE_TAG_POSIX, 16);
+                       if (IS_ERR(context)) {
+                               rc = PTR_ERR(context);
+-                              goto err_out1;
++                              goto err_out2;
+                       } else if (context) {
+                               struct create_posix *posix =
+                                       (struct create_posix *)context;
+@@ -2852,7 +2852,7 @@ int smb2_open(struct ksmbd_work *work)
+                                   le32_to_cpu(context->DataLength) <
+                                   sizeof(struct create_posix) - 4) {
+                                       rc = -EINVAL;
+-                                      goto err_out1;
++                                      goto err_out2;
+                               }
+                               ksmbd_debug(SMB, "get posix context\n");
+@@ -2864,7 +2864,7 @@ int smb2_open(struct ksmbd_work *work)
+       if (ksmbd_override_fsids(work)) {
+               rc = -ENOMEM;
+-              goto err_out1;
++              goto err_out2;
+       }
+       rc = ksmbd_vfs_kern_path_locked(work, name, LOOKUP_NO_SYMLINKS,
+@@ -3177,11 +3177,6 @@ int smb2_open(struct ksmbd_work *work)
+       fp->attrib_only = !(req->DesiredAccess & ~(FILE_READ_ATTRIBUTES_LE |
+                       FILE_WRITE_ATTRIBUTES_LE | FILE_SYNCHRONIZE_LE));
+-      if (!S_ISDIR(file_inode(filp)->i_mode) && open_flags & O_TRUNC &&
+-          !fp->attrib_only && !stream_name) {
+-              smb_break_all_oplock(work, fp);
+-              need_truncate = 1;
+-      }
+       /* fp should be searchable through ksmbd_inode.m_fp_list
+        * after daccess, saccess, attrib_only, and stream are
+@@ -3197,13 +3192,39 @@ int smb2_open(struct ksmbd_work *work)
+               goto err_out;
+       }
++      rc = ksmbd_vfs_getattr(&path, &stat);
++      if (rc)
++              goto err_out;
++
++      if (stat.result_mask & STATX_BTIME)
++              fp->create_time = ksmbd_UnixTimeToNT(stat.btime);
++      else
++              fp->create_time = ksmbd_UnixTimeToNT(stat.ctime);
++      if (req->FileAttributes || fp->f_ci->m_fattr == 0)
++              fp->f_ci->m_fattr =
++                      cpu_to_le32(smb2_get_dos_mode(&stat, le32_to_cpu(req->FileAttributes)));
++
++      if (!created)
++              smb2_update_xattrs(tcon, &path, fp);
++      else
++              smb2_new_xattrs(tcon, &path, fp);
++
++      if (file_present || created)
++              ksmbd_vfs_kern_path_unlock(&parent_path, &path);
++
++      if (!S_ISDIR(file_inode(filp)->i_mode) && open_flags & O_TRUNC &&
++          !fp->attrib_only && !stream_name) {
++              smb_break_all_oplock(work, fp);
++              need_truncate = 1;
++      }
++
+       share_ret = ksmbd_smb_check_shared_mode(fp->filp, fp);
+       if (!test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_OPLOCKS) ||
+           (req_op_level == SMB2_OPLOCK_LEVEL_LEASE &&
+            !(conn->vals->capabilities & SMB2_GLOBAL_CAP_LEASING))) {
+               if (share_ret < 0 && !S_ISDIR(file_inode(fp->filp)->i_mode)) {
+                       rc = share_ret;
+-                      goto err_out;
++                      goto err_out1;
+               }
+       } else {
+               if (req_op_level == SMB2_OPLOCK_LEVEL_LEASE) {
+@@ -3213,7 +3234,7 @@ int smb2_open(struct ksmbd_work *work)
+                                   name, req_op_level, lc->req_state);
+                       rc = find_same_lease_key(sess, fp->f_ci, lc);
+                       if (rc)
+-                              goto err_out;
++                              goto err_out1;
+               } else if (open_flags == O_RDONLY &&
+                          (req_op_level == SMB2_OPLOCK_LEVEL_BATCH ||
+                           req_op_level == SMB2_OPLOCK_LEVEL_EXCLUSIVE))
+@@ -3224,12 +3245,18 @@ int smb2_open(struct ksmbd_work *work)
+                                     le32_to_cpu(req->hdr.Id.SyncId.TreeId),
+                                     lc, share_ret);
+               if (rc < 0)
+-                      goto err_out;
++                      goto err_out1;
+       }
+       if (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE)
+               ksmbd_fd_set_delete_on_close(fp, file_info);
++      if (need_truncate) {
++              rc = smb2_create_truncate(&fp->filp->f_path);
++              if (rc)
++                      goto err_out1;
++      }
++
+       if (req->CreateContextsOffset) {
+               struct create_alloc_size_req *az_req;
+@@ -3237,7 +3264,7 @@ int smb2_open(struct ksmbd_work *work)
+                                       SMB2_CREATE_ALLOCATION_SIZE, 4);
+               if (IS_ERR(az_req)) {
+                       rc = PTR_ERR(az_req);
+-                      goto err_out;
++                      goto err_out1;
+               } else if (az_req) {
+                       loff_t alloc_size;
+                       int err;
+@@ -3246,7 +3273,7 @@ int smb2_open(struct ksmbd_work *work)
+                           le32_to_cpu(az_req->ccontext.DataLength) <
+                           sizeof(struct create_alloc_size_req)) {
+                               rc = -EINVAL;
+-                              goto err_out;
++                              goto err_out1;
+                       }
+                       alloc_size = le64_to_cpu(az_req->AllocationSize);
+                       ksmbd_debug(SMB,
+@@ -3264,30 +3291,13 @@ int smb2_open(struct ksmbd_work *work)
+               context = smb2_find_context_vals(req, SMB2_CREATE_QUERY_ON_DISK_ID, 4);
+               if (IS_ERR(context)) {
+                       rc = PTR_ERR(context);
+-                      goto err_out;
++                      goto err_out1;
+               } else if (context) {
+                       ksmbd_debug(SMB, "get query on disk id context\n");
+                       query_disk_id = 1;
+               }
+       }
+-      rc = ksmbd_vfs_getattr(&path, &stat);
+-      if (rc)
+-              goto err_out;
+-
+-      if (stat.result_mask & STATX_BTIME)
+-              fp->create_time = ksmbd_UnixTimeToNT(stat.btime);
+-      else
+-              fp->create_time = ksmbd_UnixTimeToNT(stat.ctime);
+-      if (req->FileAttributes || fp->f_ci->m_fattr == 0)
+-              fp->f_ci->m_fattr =
+-                      cpu_to_le32(smb2_get_dos_mode(&stat, le32_to_cpu(req->FileAttributes)));
+-
+-      if (!created)
+-              smb2_update_xattrs(tcon, &path, fp);
+-      else
+-              smb2_new_xattrs(tcon, &path, fp);
+-
+       memcpy(fp->client_guid, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE);
+       rsp->StructureSize = cpu_to_le16(89);
+@@ -3394,14 +3404,13 @@ int smb2_open(struct ksmbd_work *work)
+       }
+ err_out:
+-      if (file_present || created)
++      if (rc && (file_present || created))
+               ksmbd_vfs_kern_path_unlock(&parent_path, &path);
+-      if (fp && need_truncate)
+-              rc = smb2_create_truncate(&fp->filp->f_path);
+-
+-      ksmbd_revert_fsids(work);
+ err_out1:
++      ksmbd_revert_fsids(work);
++
++err_out2:
+       if (!rc) {
+               ksmbd_update_fstate(&work->sess->file_table, fp, FP_INITED);
+               rc = ksmbd_iov_pin_rsp(work, (void *)rsp, iov_len);
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-move-setting-smb2_flags_async_command-and-asyn.patch b/queue-6.1/ksmbd-move-setting-smb2_flags_async_command-and-asyn.patch
new file mode 100644 (file)
index 0000000..5debc70
--- /dev/null
@@ -0,0 +1,57 @@
+From df39b2661a4d2e0ef3bea3e02d74abeb3b934abf Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:13:24 +0900
+Subject: ksmbd: move setting SMB2_FLAGS_ASYNC_COMMAND and AsyncId
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit 9ac45ac7cf65b0623ceeab9b28b307a08efa22dc ]
+
+Directly set SMB2_FLAGS_ASYNC_COMMAND flags and AsyncId in smb2 header of
+interim response instead of current response header.
+
+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/smb/server/smb2pdu.c | 7 ++-----
+ 1 file changed, 2 insertions(+), 5 deletions(-)
+
+diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
+index 3d965be412751..2ba5e685dd3fe 100644
+--- a/fs/smb/server/smb2pdu.c
++++ b/fs/smb/server/smb2pdu.c
+@@ -657,13 +657,9 @@ smb2_get_name(const char *src, const int maxlen, struct nls_table *local_nls)
+ int setup_async_work(struct ksmbd_work *work, void (*fn)(void **), void **arg)
+ {
+-      struct smb2_hdr *rsp_hdr;
+       struct ksmbd_conn *conn = work->conn;
+       int id;
+-      rsp_hdr = ksmbd_resp_buf_next(work);
+-      rsp_hdr->Flags |= SMB2_FLAGS_ASYNC_COMMAND;
+-
+       id = ksmbd_acquire_async_msg_id(&conn->async_ida);
+       if (id < 0) {
+               pr_err("Failed to alloc async message id\n");
+@@ -671,7 +667,6 @@ int setup_async_work(struct ksmbd_work *work, void (*fn)(void **), void **arg)
+       }
+       work->asynchronous = true;
+       work->async_id = id;
+-      rsp_hdr->Id.AsyncId = cpu_to_le64(id);
+       ksmbd_debug(SMB,
+                   "Send interim Response to inform async request id : %d\n",
+@@ -723,6 +718,8 @@ void smb2_send_interim_resp(struct ksmbd_work *work, __le32 status)
+              __SMB2_HEADER_STRUCTURE_SIZE);
+       rsp_hdr = smb2_get_msg(in_work->response_buf);
++      rsp_hdr->Flags |= SMB2_FLAGS_ASYNC_COMMAND;
++      rsp_hdr->Id.AsyncId = cpu_to_le64(work->async_id);
+       smb2_set_err_rsp(in_work);
+       rsp_hdr->Status = status;
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-no-need-to-wait-for-binded-connection-terminat.patch b/queue-6.1/ksmbd-no-need-to-wait-for-binded-connection-terminat.patch
new file mode 100644 (file)
index 0000000..4c77b58
--- /dev/null
@@ -0,0 +1,51 @@
+From 20dc27d41bf97e602b86ab48845254ca5bc47636 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:13:17 +0900
+Subject: ksmbd: no need to wait for binded connection termination at logoff
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit 67797da8a4b82446d42c52b6ee1419a3100d78ff ]
+
+The connection could be binded to the existing session for Multichannel.
+session will be destroyed when binded connections are released.
+So no need to wait for that's connection at logoff.
+
+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/smb/server/connection.c | 16 ----------------
+ 1 file changed, 16 deletions(-)
+
+diff --git a/fs/smb/server/connection.c b/fs/smb/server/connection.c
+index 4b38c3a285f60..b6fa1e285c401 100644
+--- a/fs/smb/server/connection.c
++++ b/fs/smb/server/connection.c
+@@ -167,23 +167,7 @@ void ksmbd_all_conn_set_status(u64 sess_id, u32 status)
+ void ksmbd_conn_wait_idle(struct ksmbd_conn *conn, u64 sess_id)
+ {
+-      struct ksmbd_conn *bind_conn;
+-
+       wait_event(conn->req_running_q, atomic_read(&conn->req_running) < 2);
+-
+-      down_read(&conn_list_lock);
+-      list_for_each_entry(bind_conn, &conn_list, conns_list) {
+-              if (bind_conn == conn)
+-                      continue;
+-
+-              if ((bind_conn->binding || xa_load(&bind_conn->sessions, sess_id)) &&
+-                  !ksmbd_conn_releasing(bind_conn) &&
+-                  atomic_read(&bind_conn->req_running)) {
+-                      wait_event(bind_conn->req_running_q,
+-                              atomic_read(&bind_conn->req_running) == 0);
+-              }
+-      }
+-      up_read(&conn_list_lock);
+ }
+ int ksmbd_conn_write(struct ksmbd_work *work)
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-prevent-memory-leak-on-error-return.patch b/queue-6.1/ksmbd-prevent-memory-leak-on-error-return.patch
new file mode 100644 (file)
index 0000000..f38b6fe
--- /dev/null
@@ -0,0 +1,53 @@
+From 4d4fef19e7b78ea56780f540e72fe63eb01e8e39 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:13:19 +0900
+Subject: ksmbd: prevent memory leak on error return
+
+From: Zongmin Zhou <zhouzongmin@kylinos.cn>
+
+[ Upstream commit 90044481e7cca6cb3125b3906544954a25f1309f ]
+
+When allocated memory for 'new' failed,just return
+will cause memory leak of 'ar'.
+
+Fixes: 1819a9042999 ("ksmbd: reorganize ksmbd_iov_pin_rsp()")
+Reported-by: kernel test robot <lkp@intel.com>
+Reported-by: Dan Carpenter <error27@gmail.com>
+Closes: https://lore.kernel.org/r/202311031837.H3yo7JVl-lkp@intel.com/
+Signed-off-by: Zongmin Zhou<zhouzongmin@kylinos.cn>
+Acked-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/server/ksmbd_work.c | 7 +++++--
+ 1 file changed, 5 insertions(+), 2 deletions(-)
+
+diff --git a/fs/smb/server/ksmbd_work.c b/fs/smb/server/ksmbd_work.c
+index a2ed441e837ae..2510b9f3c8c14 100644
+--- a/fs/smb/server/ksmbd_work.c
++++ b/fs/smb/server/ksmbd_work.c
+@@ -106,7 +106,7 @@ static inline void __ksmbd_iov_pin(struct ksmbd_work *work, void *ib,
+ static int __ksmbd_iov_pin_rsp(struct ksmbd_work *work, void *ib, int len,
+                              void *aux_buf, unsigned int aux_size)
+ {
+-      struct aux_read *ar;
++      struct aux_read *ar = NULL;
+       int need_iov_cnt = 1;
+       if (aux_size) {
+@@ -123,8 +123,11 @@ static int __ksmbd_iov_pin_rsp(struct ksmbd_work *work, void *ib, int len,
+               new = krealloc(work->iov,
+                              sizeof(struct kvec) * work->iov_alloc_cnt,
+                              GFP_KERNEL | __GFP_ZERO);
+-              if (!new)
++              if (!new) {
++                      kfree(ar);
++                      work->iov_alloc_cnt -= 4;
+                       return -ENOMEM;
++              }
+               work->iov = new;
+       }
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-release-interim-response-after-sending-status-.patch b/queue-6.1/ksmbd-release-interim-response-after-sending-status-.patch
new file mode 100644 (file)
index 0000000..eb31a72
--- /dev/null
@@ -0,0 +1,51 @@
+From d1d9117edcf8001b839d58cbf03b220bd0a2055b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:13:23 +0900
+Subject: ksmbd: release interim response after sending status pending response
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit 2a3f7857ec742e212d6cee7fbbf7b0e2ae7f5161 ]
+
+Add missing release async id and delete interim response entry after
+sending status pending response. This only cause when smb2 lease is enable.
+
+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/smb/server/ksmbd_work.c | 3 +++
+ fs/smb/server/oplock.c     | 3 ++-
+ 2 files changed, 5 insertions(+), 1 deletion(-)
+
+diff --git a/fs/smb/server/ksmbd_work.c b/fs/smb/server/ksmbd_work.c
+index 2510b9f3c8c14..d7c676c151e20 100644
+--- a/fs/smb/server/ksmbd_work.c
++++ b/fs/smb/server/ksmbd_work.c
+@@ -56,6 +56,9 @@ void ksmbd_free_work_struct(struct ksmbd_work *work)
+       kfree(work->tr_buf);
+       kvfree(work->request_buf);
+       kfree(work->iov);
++      if (!list_empty(&work->interim_entry))
++              list_del(&work->interim_entry);
++
+       if (work->async_id)
+               ksmbd_release_id(&work->conn->async_ida, work->async_id);
+       kmem_cache_free(work_cache, work);
+diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c
+index 90a035c27130f..4c74e8ea9649a 100644
+--- a/fs/smb/server/oplock.c
++++ b/fs/smb/server/oplock.c
+@@ -833,7 +833,8 @@ static int smb2_lease_break_noti(struct oplock_info *opinfo)
+                                            interim_entry);
+                       setup_async_work(in_work, NULL, NULL);
+                       smb2_send_interim_resp(in_work, STATUS_PENDING);
+-                      list_del(&in_work->interim_entry);
++                      list_del_init(&in_work->interim_entry);
++                      release_async_work(in_work);
+               }
+               INIT_WORK(&work->work, __smb2_lease_break_noti);
+               ksmbd_queue_work(work);
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-remove-duplicated-codes.patch b/queue-6.1/ksmbd-remove-duplicated-codes.patch
new file mode 100644 (file)
index 0000000..3fafc46
--- /dev/null
@@ -0,0 +1,66 @@
+From 816c5c3bfab4296c64a13822757bc2905ef378ad Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:12:27 +0900
+Subject: ksmbd: Remove duplicated codes
+
+From: Dawei Li <set_pte_at@outlook.com>
+
+[ Upstream commit 7010357004096e54c884813e702d71147dc081f8 ]
+
+ksmbd_neg_token_init_mech_token() and ksmbd_neg_token_targ_resp_token()
+share same implementation, unify them.
+
+Signed-off-by: Dawei Li <set_pte_at@outlook.com>
+Acked-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/server/asn1.c | 23 +++++++++++------------
+ 1 file changed, 11 insertions(+), 12 deletions(-)
+
+diff --git a/fs/smb/server/asn1.c b/fs/smb/server/asn1.c
+index c03eba0903682..cc6384f796759 100644
+--- a/fs/smb/server/asn1.c
++++ b/fs/smb/server/asn1.c
+@@ -208,9 +208,9 @@ int ksmbd_neg_token_init_mech_type(void *context, size_t hdrlen,
+       return 0;
+ }
+-int ksmbd_neg_token_init_mech_token(void *context, size_t hdrlen,
+-                                  unsigned char tag, const void *value,
+-                                  size_t vlen)
++static int ksmbd_neg_token_alloc(void *context, size_t hdrlen,
++                               unsigned char tag, const void *value,
++                               size_t vlen)
+ {
+       struct ksmbd_conn *conn = context;
+@@ -223,17 +223,16 @@ int ksmbd_neg_token_init_mech_token(void *context, size_t hdrlen,
+       return 0;
+ }
+-int ksmbd_neg_token_targ_resp_token(void *context, size_t hdrlen,
++int ksmbd_neg_token_init_mech_token(void *context, size_t hdrlen,
+                                   unsigned char tag, const void *value,
+                                   size_t vlen)
+ {
+-      struct ksmbd_conn *conn = context;
+-
+-      conn->mechToken = kmalloc(vlen + 1, GFP_KERNEL);
+-      if (!conn->mechToken)
+-              return -ENOMEM;
++      return ksmbd_neg_token_alloc(context, hdrlen, tag, value, vlen);
++}
+-      memcpy(conn->mechToken, value, vlen);
+-      conn->mechToken[vlen] = '\0';
+-      return 0;
++int ksmbd_neg_token_targ_resp_token(void *context, size_t hdrlen,
++                                  unsigned char tag, const void *value,
++                                  size_t vlen)
++{
++      return ksmbd_neg_token_alloc(context, hdrlen, tag, value, vlen);
+ }
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-remove-experimental-warning.patch b/queue-6.1/ksmbd-remove-experimental-warning.patch
new file mode 100644 (file)
index 0000000..cad3f23
--- /dev/null
@@ -0,0 +1,48 @@
+From 2fc81539421dde8e7b4cd9a20f0d9263736bb600 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:12:59 +0900
+Subject: ksmbd: remove experimental warning
+
+From: Steve French <stfrench@microsoft.com>
+
+[ Upstream commit f5069159f32c8c943e047f22731317463c8e9b84 ]
+
+ksmbd has made significant improvements over the past two
+years and is regularly tested and used.  Remove the experimental
+warning.
+
+Acked-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/server/Kconfig  | 2 +-
+ fs/smb/server/server.c | 2 --
+ 2 files changed, 1 insertion(+), 3 deletions(-)
+
+diff --git a/fs/smb/server/Kconfig b/fs/smb/server/Kconfig
+index 7055cb5d28800..d036ab80fec35 100644
+--- a/fs/smb/server/Kconfig
++++ b/fs/smb/server/Kconfig
+@@ -1,5 +1,5 @@
+ config SMB_SERVER
+-      tristate "SMB3 server support (EXPERIMENTAL)"
++      tristate "SMB3 server support"
+       depends on INET
+       depends on MULTIUSER
+       depends on FILE_LOCKING
+diff --git a/fs/smb/server/server.c b/fs/smb/server/server.c
+index ff1514c79f162..f5d8e405cf6fd 100644
+--- a/fs/smb/server/server.c
++++ b/fs/smb/server/server.c
+@@ -591,8 +591,6 @@ static int __init ksmbd_server_init(void)
+       if (ret)
+               goto err_crypto_destroy;
+-      pr_warn_once("The ksmbd server is experimental\n");
+-
+       return 0;
+ err_crypto_destroy:
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-remove-unneeded-mark_inode_dirty-in-set_info_s.patch b/queue-6.1/ksmbd-remove-unneeded-mark_inode_dirty-in-set_info_s.patch
new file mode 100644 (file)
index 0000000..4f76f30
--- /dev/null
@@ -0,0 +1,34 @@
+From de09696f841811974662d4f4c783d7a84cf78027 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:13:00 +0900
+Subject: ksmbd: remove unneeded mark_inode_dirty in set_info_sec()
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit e4e14095cc68a2efefba6f77d95efe1137e751d4 ]
+
+mark_inode_dirty will be called in notify_change().
+This patch remove unneeded mark_inode_dirty in set_info_sec().
+
+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/smb/server/smbacl.c | 1 -
+ 1 file changed, 1 deletion(-)
+
+diff --git a/fs/smb/server/smbacl.c b/fs/smb/server/smbacl.c
+index 03f19d3de2a17..7a42728d8047c 100644
+--- a/fs/smb/server/smbacl.c
++++ b/fs/smb/server/smbacl.c
+@@ -1443,7 +1443,6 @@ int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon,
+ out:
+       posix_acl_release(fattr.cf_acls);
+       posix_acl_release(fattr.cf_dacls);
+-      mark_inode_dirty(inode);
+       return rc;
+ }
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-remove-unused-compression-negotiate-ctx-packin.patch b/queue-6.1/ksmbd-remove-unused-compression-negotiate-ctx-packin.patch
new file mode 100644 (file)
index 0000000..e4a5b32
--- /dev/null
@@ -0,0 +1,80 @@
+From 795025229aa428ad34e549dda0e7365b5aabd641 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:12:35 +0900
+Subject: ksmbd: remove unused compression negotiate ctx packing
+
+From: David Disseldorp <ddiss@suse.de>
+
+[ Upstream commit af36c51e0e111de4e908328d49cba49de758f66e ]
+
+build_compression_ctxt() is currently unreachable due to
+conn.compress_algorithm remaining zero (SMB3_COMPRESS_NONE).
+
+It appears to have been broken in a couple of subtle ways over the
+years:
+- prior to d6c9ad23b421 ("ksmbd: use the common definitions for
+  NEGOTIATE_PROTOCOL") smb2_compression_ctx.DataLength was set to 8,
+  which didn't account for the single CompressionAlgorithms flexible
+  array member.
+- post d6c9ad23b421 smb2_compression_capabilities_context
+  CompressionAlgorithms is a three member array, while
+  CompressionAlgorithmCount is set to indicate only one member.
+  assemble_neg_contexts() ctxt_size is also incorrectly incremented by
+  sizeof(struct smb2_compression_capabilities_context) + 2, which
+  assumes one flexible array member.
+
+Signed-off-by: David Disseldorp <ddiss@suse.de>
+Acked-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/server/smb2pdu.c | 26 ++------------------------
+ 1 file changed, 2 insertions(+), 24 deletions(-)
+
+diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
+index 92e6570972437..ca57e85abf915 100644
+--- a/fs/smb/server/smb2pdu.c
++++ b/fs/smb/server/smb2pdu.c
+@@ -786,19 +786,6 @@ static void build_encrypt_ctxt(struct smb2_encryption_neg_context *pneg_ctxt,
+       pneg_ctxt->Ciphers[0] = cipher_type;
+ }
+-static void build_compression_ctxt(struct smb2_compression_capabilities_context *pneg_ctxt,
+-                                 __le16 comp_algo)
+-{
+-      pneg_ctxt->ContextType = SMB2_COMPRESSION_CAPABILITIES;
+-      pneg_ctxt->DataLength =
+-              cpu_to_le16(sizeof(struct smb2_compression_capabilities_context)
+-                      - sizeof(struct smb2_neg_context));
+-      pneg_ctxt->Reserved = cpu_to_le32(0);
+-      pneg_ctxt->CompressionAlgorithmCount = cpu_to_le16(1);
+-      pneg_ctxt->Flags = cpu_to_le32(0);
+-      pneg_ctxt->CompressionAlgorithms[0] = comp_algo;
+-}
+-
+ static void build_sign_cap_ctxt(struct smb2_signing_capabilities *pneg_ctxt,
+                               __le16 sign_algo)
+ {
+@@ -862,17 +849,8 @@ static void assemble_neg_contexts(struct ksmbd_conn *conn,
+               ctxt_size += sizeof(struct smb2_encryption_neg_context) + 2;
+       }
+-      if (conn->compress_algorithm) {
+-              ctxt_size = round_up(ctxt_size, 8);
+-              ksmbd_debug(SMB,
+-                          "assemble SMB2_COMPRESSION_CAPABILITIES context\n");
+-              /* Temporarily set to SMB3_COMPRESS_NONE */
+-              build_compression_ctxt((struct smb2_compression_capabilities_context *)
+-                                     (pneg_ctxt + ctxt_size),
+-                                     conn->compress_algorithm);
+-              neg_ctxt_cnt++;
+-              ctxt_size += sizeof(struct smb2_compression_capabilities_context) + 2;
+-      }
++      /* compression context not yet supported */
++      WARN_ON(conn->compress_algorithm != SMB3_COMPRESS_NONE);
+       if (conn->posix_ext_supported) {
+               ctxt_size = round_up(ctxt_size, 8);
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-remove-unused-field-in-ksmbd_user-struct.patch b/queue-6.1/ksmbd-remove-unused-field-in-ksmbd_user-struct.patch
new file mode 100644 (file)
index 0000000..f7efcad
--- /dev/null
@@ -0,0 +1,34 @@
+From 04fd95999012d5bf530ef2744ddd065a5aa1e180 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:13:11 +0900
+Subject: ksmbd: Remove unused field in ksmbd_user struct
+
+From: Cheng-Han Wu <hank20010209@gmail.com>
+
+[ Upstream commit eacc655e18d1dec9b50660d16a1ddeeb4d6c48f2 ]
+
+fs/smb/server/mgmt/user_config.h:21: Remove the unused field 'failed_login_count' from the ksmbd_user struct.
+
+Signed-off-by: Cheng-Han Wu <hank20010209@gmail.com>
+Acked-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/server/mgmt/user_config.h | 1 -
+ 1 file changed, 1 deletion(-)
+
+diff --git a/fs/smb/server/mgmt/user_config.h b/fs/smb/server/mgmt/user_config.h
+index 6a44109617f14..e068a19fd9049 100644
+--- a/fs/smb/server/mgmt/user_config.h
++++ b/fs/smb/server/mgmt/user_config.h
+@@ -18,7 +18,6 @@ struct ksmbd_user {
+       size_t                  passkey_sz;
+       char                    *passkey;
+-      unsigned int            failed_login_count;
+ };
+ static inline bool user_guest(struct ksmbd_user *user)
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-remove-unused-is_char_allowed-function.patch b/queue-6.1/ksmbd-remove-unused-is_char_allowed-function.patch
new file mode 100644 (file)
index 0000000..865ac27
--- /dev/null
@@ -0,0 +1,57 @@
+From b618eadd3d5203cd53b8b49cb0b03b4d62be8e6c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:12:31 +0900
+Subject: ksmbd: remove unused is_char_allowed function
+
+From: Tom Rix <trix@redhat.com>
+
+[ Upstream commit 2824861773eb512b37547516d81ef78108032cb2 ]
+
+clang with W=1 reports
+fs/ksmbd/unicode.c:122:19: error: unused function
+  'is_char_allowed' [-Werror,-Wunused-function]
+static inline int is_char_allowed(char *ch)
+                  ^
+This function is not used so remove it.
+
+Signed-off-by: Tom Rix <trix@redhat.com>
+Reviewed-by: Sergey Senozhatsky <senozhatsky@chromium.org>
+Acked-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/server/unicode.c | 18 ------------------
+ 1 file changed, 18 deletions(-)
+
+diff --git a/fs/smb/server/unicode.c b/fs/smb/server/unicode.c
+index a0db699ddafda..9ae676906ed39 100644
+--- a/fs/smb/server/unicode.c
++++ b/fs/smb/server/unicode.c
+@@ -113,24 +113,6 @@ cifs_mapchar(char *target, const __u16 src_char, const struct nls_table *cp,
+       goto out;
+ }
+-/*
+- * is_char_allowed() - check for valid character
+- * @ch:               input character to be checked
+- *
+- * Return:    1 if char is allowed, otherwise 0
+- */
+-static inline int is_char_allowed(char *ch)
+-{
+-      /* check for control chars, wildcards etc. */
+-      if (!(*ch & 0x80) &&
+-          (*ch <= 0x1f ||
+-           *ch == '?' || *ch == '"' || *ch == '<' ||
+-           *ch == '>' || *ch == '|'))
+-              return 0;
+-
+-      return 1;
+-}
+-
+ /*
+  * smb_from_utf16() - convert utf16le string to local charset
+  * @to:               destination buffer
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-remove-unused-ksmbd_tree_conn_share-function.patch b/queue-6.1/ksmbd-remove-unused-ksmbd_tree_conn_share-function.patch
new file mode 100644 (file)
index 0000000..5c7fe8a
--- /dev/null
@@ -0,0 +1,59 @@
+From ecf9c38c087394c798e945c1fc796e5d100e2774 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:12:43 +0900
+Subject: ksmbd: remove unused ksmbd_tree_conn_share function
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit 7bd9f0876fdef00f4e155be35e6b304981a53f80 ]
+
+Remove unused ksmbd_tree_conn_share function.
+
+Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
+Reviewed-by: Sergey Senozhatsky <senozhatsky@chromium.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/server/mgmt/tree_connect.c | 11 -----------
+ fs/smb/server/mgmt/tree_connect.h |  3 ---
+ 2 files changed, 14 deletions(-)
+
+diff --git a/fs/smb/server/mgmt/tree_connect.c b/fs/smb/server/mgmt/tree_connect.c
+index f07a05f376513..408cddf2f094a 100644
+--- a/fs/smb/server/mgmt/tree_connect.c
++++ b/fs/smb/server/mgmt/tree_connect.c
+@@ -120,17 +120,6 @@ struct ksmbd_tree_connect *ksmbd_tree_conn_lookup(struct ksmbd_session *sess,
+       return tcon;
+ }
+-struct ksmbd_share_config *ksmbd_tree_conn_share(struct ksmbd_session *sess,
+-                                               unsigned int id)
+-{
+-      struct ksmbd_tree_connect *tc;
+-
+-      tc = ksmbd_tree_conn_lookup(sess, id);
+-      if (tc)
+-              return tc->share_conf;
+-      return NULL;
+-}
+-
+ int ksmbd_tree_conn_session_logoff(struct ksmbd_session *sess)
+ {
+       int ret = 0;
+diff --git a/fs/smb/server/mgmt/tree_connect.h b/fs/smb/server/mgmt/tree_connect.h
+index 700df36cf3e30..562d647ad9fad 100644
+--- a/fs/smb/server/mgmt/tree_connect.h
++++ b/fs/smb/server/mgmt/tree_connect.h
+@@ -53,9 +53,6 @@ int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess,
+ struct ksmbd_tree_connect *ksmbd_tree_conn_lookup(struct ksmbd_session *sess,
+                                                 unsigned int id);
+-struct ksmbd_share_config *ksmbd_tree_conn_share(struct ksmbd_session *sess,
+-                                               unsigned int id);
+-
+ int ksmbd_tree_conn_session_logoff(struct ksmbd_session *sess);
+ #endif /* __TREE_CONNECT_MANAGEMENT_H__ */
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-reorganize-ksmbd_iov_pin_rsp.patch b/queue-6.1/ksmbd-reorganize-ksmbd_iov_pin_rsp.patch
new file mode 100644 (file)
index 0000000..6e55c3e
--- /dev/null
@@ -0,0 +1,99 @@
+From c0f0dd47e274b5a231b783cf7f3eef3e4e0e1c07 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:13:12 +0900
+Subject: ksmbd: reorganize ksmbd_iov_pin_rsp()
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit 1819a904299942b309f687cc0f08b123500aa178 ]
+
+If ksmbd_iov_pin_rsp fail, io vertor should be rollback.
+This patch moves memory allocations to before setting the io vector
+to avoid rollbacks.
+
+Fixes: e2b76ab8b5c9 ("ksmbd: add support for read compound")
+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/smb/server/ksmbd_work.c | 43 +++++++++++++++++++-------------------
+ 1 file changed, 22 insertions(+), 21 deletions(-)
+
+diff --git a/fs/smb/server/ksmbd_work.c b/fs/smb/server/ksmbd_work.c
+index 51def3ca74c01..a2ed441e837ae 100644
+--- a/fs/smb/server/ksmbd_work.c
++++ b/fs/smb/server/ksmbd_work.c
+@@ -95,11 +95,28 @@ bool ksmbd_queue_work(struct ksmbd_work *work)
+       return queue_work(ksmbd_wq, &work->work);
+ }
+-static int ksmbd_realloc_iov_pin(struct ksmbd_work *work, void *ib,
+-                               unsigned int ib_len)
++static inline void __ksmbd_iov_pin(struct ksmbd_work *work, void *ib,
++                                 unsigned int ib_len)
+ {
++      work->iov[++work->iov_idx].iov_base = ib;
++      work->iov[work->iov_idx].iov_len = ib_len;
++      work->iov_cnt++;
++}
++
++static int __ksmbd_iov_pin_rsp(struct ksmbd_work *work, void *ib, int len,
++                             void *aux_buf, unsigned int aux_size)
++{
++      struct aux_read *ar;
++      int need_iov_cnt = 1;
+-      if (work->iov_alloc_cnt <= work->iov_cnt) {
++      if (aux_size) {
++              need_iov_cnt++;
++              ar = kmalloc(sizeof(struct aux_read), GFP_KERNEL);
++              if (!ar)
++                      return -ENOMEM;
++      }
++
++      if (work->iov_alloc_cnt < work->iov_cnt + need_iov_cnt) {
+               struct kvec *new;
+               work->iov_alloc_cnt += 4;
+@@ -111,16 +128,6 @@ static int ksmbd_realloc_iov_pin(struct ksmbd_work *work, void *ib,
+               work->iov = new;
+       }
+-      work->iov[++work->iov_idx].iov_base = ib;
+-      work->iov[work->iov_idx].iov_len = ib_len;
+-      work->iov_cnt++;
+-
+-      return 0;
+-}
+-
+-static int __ksmbd_iov_pin_rsp(struct ksmbd_work *work, void *ib, int len,
+-                             void *aux_buf, unsigned int aux_size)
+-{
+       /* Plus rfc_length size on first iov */
+       if (!work->iov_idx) {
+               work->iov[work->iov_idx].iov_base = work->response_buf;
+@@ -129,19 +136,13 @@ static int __ksmbd_iov_pin_rsp(struct ksmbd_work *work, void *ib, int len,
+               work->iov_cnt++;
+       }
+-      ksmbd_realloc_iov_pin(work, ib, len);
++      __ksmbd_iov_pin(work, ib, len);
+       inc_rfc1001_len(work->iov[0].iov_base, len);
+       if (aux_size) {
+-              struct aux_read *ar;
+-
+-              ksmbd_realloc_iov_pin(work, aux_buf, aux_size);
++              __ksmbd_iov_pin(work, aux_buf, aux_size);
+               inc_rfc1001_len(work->iov[0].iov_base, aux_size);
+-              ar = kmalloc(sizeof(struct aux_read), GFP_KERNEL);
+-              if (!ar)
+-                      return -ENOMEM;
+-
+               ar->buf = aux_buf;
+               list_add(&ar->entry, &work->aux_read_list);
+       }
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-replace-one-element-array-with-flexible-array-.patch b/queue-6.1/ksmbd-replace-one-element-array-with-flexible-array-.patch
new file mode 100644 (file)
index 0000000..082364d
--- /dev/null
@@ -0,0 +1,43 @@
+From 2a99a4d488fd9df0dbaf78bf2ed3bf77e583f6b5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:12:50 +0900
+Subject: ksmbd: Replace one-element array with flexible-array member
+
+From: Gustavo A. R. Silva <gustavoars@kernel.org>
+
+[ Upstream commit 11d5e2061e973a8d4ff2b95a114b4b8ef8652633 ]
+
+One-element arrays are deprecated, and we are replacing them with flexible
+array members instead. So, replace one-element array with flexible-array
+member in struct smb_negotiate_req.
+
+This results in no differences in binary output.
+
+Link: https://github.com/KSPP/linux/issues/79
+Link: https://github.com/KSPP/linux/issues/317
+Signed-off-by: Gustavo A. R. Silva <gustavoars@kernel.org>
+Reviewed-by: Kees Cook <keescook@chromium.org>
+Acked-by: Namjae Jeon <linkinjeon@kernel.org>
+Reviewed-by: Sergey Senozhatsky <senozhatsky@chromium.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/server/smb_common.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/fs/smb/server/smb_common.h b/fs/smb/server/smb_common.h
+index f0134d16067fb..f1092519c0c28 100644
+--- a/fs/smb/server/smb_common.h
++++ b/fs/smb/server/smb_common.h
+@@ -200,7 +200,7 @@ struct smb_hdr {
+ struct smb_negotiate_req {
+       struct smb_hdr hdr;     /* wct = 0 */
+       __le16 ByteCount;
+-      unsigned char DialectsArray[1];
++      unsigned char DialectsArray[];
+ } __packed;
+ struct smb_negotiate_rsp {
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-replace-one-element-arrays-with-flexible-array.patch b/queue-6.1/ksmbd-replace-one-element-arrays-with-flexible-array.patch
new file mode 100644 (file)
index 0000000..e2b0601
--- /dev/null
@@ -0,0 +1,132 @@
+From e5d9c15451d11295687fcfa387bc90c3305f76af Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:12:20 +0900
+Subject: ksmbd: replace one-element arrays with flexible-array members
+
+From: Gustavo A. R. Silva <gustavoars@kernel.org>
+
+[ Upstream commit d272e01fa0a2f15c5c331a37cd99c6875c7b7186 ]
+
+One-element arrays are deprecated, and we are replacing them with flexible
+array members instead. So, replace one-element arrays with flexible-array
+members in multiple structs in fs/ksmbd/smb_common.h and one in
+fs/ksmbd/smb2pdu.h.
+
+Important to mention is that doing a build before/after this patch results
+in no binary output differences.
+
+This helps with the ongoing efforts to tighten the FORTIFY_SOURCE routines
+on memcpy() and help us make progress towards globally enabling
+-fstrict-flex-arrays=3 [1].
+
+Link: https://github.com/KSPP/linux/issues/242
+Link: https://github.com/KSPP/linux/issues/79
+Link: https://gcc.gnu.org/pipermail/gcc-patches/2022-October/602902.html [1]
+Signed-off-by: Gustavo A. R. Silva <gustavoars@kernel.org>
+Reviewed-by: Sergey Senozhatsky <senozhatsky@chromium.org>
+Acked-by: Namjae Jeon <linkinjeon@kernel.org>
+Reviewed-by: Kees Cook <keescook@chromium.org>
+Signed-off-by: Kees Cook <keescook@chromium.org>
+Link: https://lore.kernel.org/r/Y3OxronfaPYv9qGP@work
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/server/smb2pdu.c    |  4 ++--
+ fs/smb/server/smb2pdu.h    |  2 +-
+ fs/smb/server/smb_common.h | 12 ++++++------
+ 3 files changed, 9 insertions(+), 9 deletions(-)
+
+diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
+index 1598ad6155fef..3f4f6b0385655 100644
+--- a/fs/smb/server/smb2pdu.c
++++ b/fs/smb/server/smb2pdu.c
+@@ -3525,7 +3525,7 @@ static int smb2_populate_readdir_entry(struct ksmbd_conn *conn, int info_level,
+               goto free_conv_name;
+       }
+-      struct_sz = readdir_info_level_struct_sz(info_level) - 1 + conv_len;
++      struct_sz = readdir_info_level_struct_sz(info_level) + conv_len;
+       next_entry_offset = ALIGN(struct_sz, KSMBD_DIR_INFO_ALIGNMENT);
+       d_info->last_entry_off_align = next_entry_offset - struct_sz;
+@@ -3777,7 +3777,7 @@ static int reserve_populate_dentry(struct ksmbd_dir_info *d_info,
+               return -EOPNOTSUPP;
+       conv_len = (d_info->name_len + 1) * 2;
+-      next_entry_offset = ALIGN(struct_sz - 1 + conv_len,
++      next_entry_offset = ALIGN(struct_sz + conv_len,
+                                 KSMBD_DIR_INFO_ALIGNMENT);
+       if (next_entry_offset > d_info->out_buf_len) {
+diff --git a/fs/smb/server/smb2pdu.h b/fs/smb/server/smb2pdu.h
+index 665a837378540..f13bd65993ccc 100644
+--- a/fs/smb/server/smb2pdu.h
++++ b/fs/smb/server/smb2pdu.h
+@@ -446,7 +446,7 @@ struct smb2_posix_info {
+       /* SidBuffer contain two sids (UNIX user sid(16), UNIX group sid(16)) */
+       u8 SidBuffer[32];
+       __le32 name_len;
+-      u8 name[1];
++      u8 name[];
+       /*
+        * var sized owner SID
+        * var sized group SID
+diff --git a/fs/smb/server/smb_common.h b/fs/smb/server/smb_common.h
+index 1cbb492cdefec..f0134d16067fb 100644
+--- a/fs/smb/server/smb_common.h
++++ b/fs/smb/server/smb_common.h
+@@ -263,14 +263,14 @@ struct file_directory_info {
+       __le64 AllocationSize;
+       __le32 ExtFileAttributes;
+       __le32 FileNameLength;
+-      char FileName[1];
++      char FileName[];
+ } __packed;   /* level 0x101 FF resp data */
+ struct file_names_info {
+       __le32 NextEntryOffset;
+       __u32 FileIndex;
+       __le32 FileNameLength;
+-      char FileName[1];
++      char FileName[];
+ } __packed;   /* level 0xc FF resp data */
+ struct file_full_directory_info {
+@@ -285,7 +285,7 @@ struct file_full_directory_info {
+       __le32 ExtFileAttributes;
+       __le32 FileNameLength;
+       __le32 EaSize;
+-      char FileName[1];
++      char FileName[];
+ } __packed; /* level 0x102 FF resp */
+ struct file_both_directory_info {
+@@ -303,7 +303,7 @@ struct file_both_directory_info {
+       __u8   ShortNameLength;
+       __u8   Reserved;
+       __u8   ShortName[24];
+-      char FileName[1];
++      char FileName[];
+ } __packed; /* level 0x104 FFrsp data */
+ struct file_id_both_directory_info {
+@@ -323,7 +323,7 @@ struct file_id_both_directory_info {
+       __u8   ShortName[24];
+       __le16 Reserved2;
+       __le64 UniqueId;
+-      char FileName[1];
++      char FileName[];
+ } __packed;
+ struct file_id_full_dir_info {
+@@ -340,7 +340,7 @@ struct file_id_full_dir_info {
+       __le32 EaSize; /* EA size */
+       __le32 Reserved;
+       __le64 UniqueId; /* inode num - le since Samba puts ino in low 32 bit*/
+-      char FileName[1];
++      char FileName[];
+ } __packed; /* level 0x105 FF rsp data */
+ struct smb_version_values {
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-replace-the-ternary-conditional-operator-with-.patch b/queue-6.1/ksmbd-replace-the-ternary-conditional-operator-with-.patch
new file mode 100644 (file)
index 0000000..1f79bdf
--- /dev/null
@@ -0,0 +1,36 @@
+From c88a7d9cf464e00cfea6380e905e5c4e7f75d313 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:12:48 +0900
+Subject: ksmbd: Replace the ternary conditional operator with min()
+
+From: Lu Hongfei <luhongfei@vivo.com>
+
+[ Upstream commit f65fadb0422537d73f9a6472861852dc2f7a6a5b ]
+
+It would be better to replace the traditional ternary conditional
+operator with min() in compare_sids.
+
+Signed-off-by: Lu Hongfei <luhongfei@vivo.com>
+Acked-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/server/smbacl.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/fs/smb/server/smbacl.c b/fs/smb/server/smbacl.c
+index 8fe2592c5525f..03f19d3de2a17 100644
+--- a/fs/smb/server/smbacl.c
++++ b/fs/smb/server/smbacl.c
+@@ -97,7 +97,7 @@ int compare_sids(const struct smb_sid *ctsid, const struct smb_sid *cwsid)
+       /* compare all of the subauth values if any */
+       num_sat = ctsid->num_subauth;
+       num_saw = cwsid->num_subauth;
+-      num_subauth = num_sat < num_saw ? num_sat : num_saw;
++      num_subauth = min(num_sat, num_saw);
+       if (num_subauth) {
+               for (i = 0; i < num_subauth; ++i) {
+                       if (ctsid->sub_auth[i] != cwsid->sub_auth[i]) {
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-return-a-literal-instead-of-err-in-ksmbd_vfs_k.patch b/queue-6.1/ksmbd-return-a-literal-instead-of-err-in-ksmbd_vfs_k.patch
new file mode 100644 (file)
index 0000000..18d77fc
--- /dev/null
@@ -0,0 +1,36 @@
+From 76d96abe4c6f3273699618028e277420ecdad103 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:12:45 +0900
+Subject: ksmbd: return a literal instead of 'err' in
+ ksmbd_vfs_kern_path_locked()
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit cf5e7f734f445588a30350591360bca2f6bf016f ]
+
+Return a literal instead of 'err' in ksmbd_vfs_kern_path_locked().
+
+Reported-by: Dan Carpenter <dan.carpenter@linaro.org>
+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/smb/server/vfs.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c
+index e6218c687fa0b..d0a85774a496a 100644
+--- a/fs/smb/server/vfs.c
++++ b/fs/smb/server/vfs.c
+@@ -1208,7 +1208,7 @@ int ksmbd_vfs_kern_path_locked(struct ksmbd_work *work, char *name,
+       err = ksmbd_vfs_path_lookup_locked(share_conf, name, flags, path);
+       if (!err)
+-              return err;
++              return 0;
+       if (caseless) {
+               char *filepath;
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-return-invalid-parameter-error-response-if-smb.patch b/queue-6.1/ksmbd-return-invalid-parameter-error-response-if-smb.patch
new file mode 100644 (file)
index 0000000..1dd23f1
--- /dev/null
@@ -0,0 +1,103 @@
+From 19d93b0f235c5b18469c13a09b8d78cdc62b92ee Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:13:02 +0900
+Subject: ksmbd: return invalid parameter error response if smb2 request is
+ invalid
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit f2f11fca5d7112e2f91c4854cddd68a059fdaa4a ]
+
+If smb2 request from client is invalid, The following kernel oops could
+happen. The patch e2b76ab8b5c9: "ksmbd: add support for read compound"
+leads this issue. When request is invalid, It doesn't set anything in
+the response buffer. This patch add missing set invalid parameter error
+response.
+
+[  673.085542] ksmbd: cli req too short, len 184 not 142. cmd:5 mid:109
+[  673.085580] BUG: kernel NULL pointer dereference, address: 0000000000000000
+[  673.085591] #PF: supervisor read access in kernel mode
+[  673.085600] #PF: error_code(0x0000) - not-present page
+[  673.085608] PGD 0 P4D 0
+[  673.085620] Oops: 0000 [#1] PREEMPT SMP NOPTI
+[  673.085631] CPU: 3 PID: 1039 Comm: kworker/3:0 Not tainted 6.6.0-rc2-tmt #16
+[  673.085643] Hardware name: AZW U59/U59, BIOS JTKT001 05/05/2022
+[  673.085651] Workqueue: ksmbd-io handle_ksmbd_work [ksmbd]
+[  673.085719] RIP: 0010:ksmbd_conn_write+0x68/0xc0 [ksmbd]
+[  673.085808] RAX: 0000000000000000 RBX: ffff88811ade4f00 RCX: 0000000000000000
+[  673.085817] RDX: 0000000000000000 RSI: ffff88810c2a9780 RDI: ffff88810c2a9ac0
+[  673.085826] RBP: ffffc900005e3e00 R08: 0000000000000000 R09: 0000000000000000
+[  673.085834] R10: ffffffffa3168160 R11: 63203a64626d736b R12: ffff8881057c8800
+[  673.085842] R13: ffff8881057c8820 R14: ffff8882781b2380 R15: ffff8881057c8800
+[  673.085852] FS:  0000000000000000(0000) GS:ffff888278180000(0000) knlGS:0000000000000000
+[  673.085864] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
+[  673.085872] CR2: 0000000000000000 CR3: 000000015b63c000 CR4: 0000000000350ee0
+[  673.085883] Call Trace:
+[  673.085890]  <TASK>
+[  673.085900]  ? show_regs+0x6a/0x80
+[  673.085916]  ? __die+0x25/0x70
+[  673.085926]  ? page_fault_oops+0x154/0x4b0
+[  673.085938]  ? tick_nohz_tick_stopped+0x18/0x50
+[  673.085954]  ? __irq_work_queue_local+0xba/0x140
+[  673.085967]  ? do_user_addr_fault+0x30f/0x6c0
+[  673.085979]  ? exc_page_fault+0x79/0x180
+[  673.085992]  ? asm_exc_page_fault+0x27/0x30
+[  673.086009]  ? ksmbd_conn_write+0x68/0xc0 [ksmbd]
+[  673.086067]  ? ksmbd_conn_write+0x46/0xc0 [ksmbd]
+[  673.086123]  handle_ksmbd_work+0x28d/0x4b0 [ksmbd]
+[  673.086177]  process_one_work+0x178/0x350
+[  673.086193]  ? __pfx_worker_thread+0x10/0x10
+[  673.086202]  worker_thread+0x2f3/0x420
+[  673.086210]  ? _raw_spin_unlock_irqrestore+0x27/0x50
+[  673.086222]  ? __pfx_worker_thread+0x10/0x10
+[  673.086230]  kthread+0x103/0x140
+[  673.086242]  ? __pfx_kthread+0x10/0x10
+[  673.086253]  ret_from_fork+0x39/0x60
+[  673.086263]  ? __pfx_kthread+0x10/0x10
+[  673.086274]  ret_from_fork_asm+0x1b/0x30
+
+Fixes: e2b76ab8b5c9 ("ksmbd: add support for read compound")
+Reported-by: Tom Talpey <tom@talpey.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/smb/server/server.c   | 4 +++-
+ fs/smb/server/smb2misc.c | 4 +---
+ 2 files changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/fs/smb/server/server.c b/fs/smb/server/server.c
+index f5d8e405cf6fd..633383e55723c 100644
+--- a/fs/smb/server/server.c
++++ b/fs/smb/server/server.c
+@@ -115,8 +115,10 @@ static int __process_request(struct ksmbd_work *work, struct ksmbd_conn *conn,
+       if (check_conn_state(work))
+               return SERVER_HANDLER_CONTINUE;
+-      if (ksmbd_verify_smb_message(work))
++      if (ksmbd_verify_smb_message(work)) {
++              conn->ops->set_rsp_status(work, STATUS_INVALID_PARAMETER);
+               return SERVER_HANDLER_ABORT;
++      }
+       command = conn->ops->get_cmd_val(work);
+       *cmd = command;
+diff --git a/fs/smb/server/smb2misc.c b/fs/smb/server/smb2misc.c
+index e881df1d10cbd..23bd3d1209dfa 100644
+--- a/fs/smb/server/smb2misc.c
++++ b/fs/smb/server/smb2misc.c
+@@ -440,10 +440,8 @@ int ksmbd_smb2_check_message(struct ksmbd_work *work)
+ validate_credit:
+       if ((work->conn->vals->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU) &&
+-          smb2_validate_credit_charge(work->conn, hdr)) {
+-              work->conn->ops->set_rsp_status(work, STATUS_INVALID_PARAMETER);
++          smb2_validate_credit_charge(work->conn, hdr))
+               return 1;
+-      }
+       return 0;
+ }
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-send-v2-lease-break-notification-for-directory.patch b/queue-6.1/ksmbd-send-v2-lease-break-notification-for-directory.patch
new file mode 100644 (file)
index 0000000..8167aae
--- /dev/null
@@ -0,0 +1,230 @@
+From 8097f1559f0fd3800bf60d72bee5091714a47224 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:13:29 +0900
+Subject: ksmbd: send v2 lease break notification for directory
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit d47d9886aeef79feba7adac701a510d65f3682b5 ]
+
+If client send different parent key, different client guid, or there is
+no parent lease key flags in create context v2 lease, ksmbd send lease
+break to client.
+
+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/smb/common/smb2pdu.h   |  1 +
+ fs/smb/server/oplock.c    | 56 +++++++++++++++++++++++++++++++++++----
+ fs/smb/server/oplock.h    |  4 +++
+ fs/smb/server/smb2pdu.c   |  7 +++++
+ fs/smb/server/vfs_cache.c | 13 ++++++++-
+ fs/smb/server/vfs_cache.h |  2 ++
+ 6 files changed, 77 insertions(+), 6 deletions(-)
+
+diff --git a/fs/smb/common/smb2pdu.h b/fs/smb/common/smb2pdu.h
+index c8a4014f9d395..07549957b3099 100644
+--- a/fs/smb/common/smb2pdu.h
++++ b/fs/smb/common/smb2pdu.h
+@@ -1196,6 +1196,7 @@ struct create_posix {
+ #define SMB2_LEASE_WRITE_CACHING_LE           cpu_to_le32(0x04)
+ #define SMB2_LEASE_FLAG_BREAK_IN_PROGRESS_LE  cpu_to_le32(0x02)
++#define SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET_LE       cpu_to_le32(0x04)
+ #define SMB2_LEASE_KEY_SIZE                   16
+diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c
+index f8ac539b2164b..4a8745b3e8304 100644
+--- a/fs/smb/server/oplock.c
++++ b/fs/smb/server/oplock.c
+@@ -102,6 +102,7 @@ static int alloc_lease(struct oplock_info *opinfo, struct lease_ctx_info *lctx)
+       lease->new_state = 0;
+       lease->flags = lctx->flags;
+       lease->duration = lctx->duration;
++      lease->is_dir = lctx->is_dir;
+       memcpy(lease->parent_lease_key, lctx->parent_lease_key, SMB2_LEASE_KEY_SIZE);
+       lease->version = lctx->version;
+       lease->epoch = le16_to_cpu(lctx->epoch);
+@@ -543,12 +544,13 @@ static struct oplock_info *same_client_has_lease(struct ksmbd_inode *ci,
+                       /* upgrading lease */
+                       if ((atomic_read(&ci->op_count) +
+                            atomic_read(&ci->sop_count)) == 1) {
+-                              if (lease->state ==
+-                                  (lctx->req_state & lease->state)) {
++                              if (lease->state != SMB2_LEASE_NONE_LE &&
++                                  lease->state == (lctx->req_state & lease->state)) {
+                                       lease->state |= lctx->req_state;
+                                       if (lctx->req_state &
+                                               SMB2_LEASE_WRITE_CACHING_LE)
+                                               lease_read_to_write(opinfo);
++
+                               }
+                       } else if ((atomic_read(&ci->op_count) +
+                                   atomic_read(&ci->sop_count)) > 1) {
+@@ -900,7 +902,8 @@ static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level)
+                                       lease->new_state =
+                                               SMB2_LEASE_READ_CACHING_LE;
+                       } else {
+-                              if (lease->state & SMB2_LEASE_HANDLE_CACHING_LE)
++                              if (lease->state & SMB2_LEASE_HANDLE_CACHING_LE &&
++                                              !lease->is_dir)
+                                       lease->new_state =
+                                               SMB2_LEASE_READ_CACHING_LE;
+                               else
+@@ -1082,6 +1085,48 @@ static void set_oplock_level(struct oplock_info *opinfo, int level,
+       }
+ }
++void smb_send_parent_lease_break_noti(struct ksmbd_file *fp,
++                                    struct lease_ctx_info *lctx)
++{
++      struct oplock_info *opinfo;
++      struct ksmbd_inode *p_ci = NULL;
++
++      if (lctx->version != 2)
++              return;
++
++      p_ci = ksmbd_inode_lookup_lock(fp->filp->f_path.dentry->d_parent);
++      if (!p_ci)
++              return;
++
++      read_lock(&p_ci->m_lock);
++      list_for_each_entry(opinfo, &p_ci->m_op_list, op_entry) {
++              if (!opinfo->is_lease)
++                      continue;
++
++              if (opinfo->o_lease->state != SMB2_OPLOCK_LEVEL_NONE &&
++                  (!(lctx->flags & SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET_LE) ||
++                   !compare_guid_key(opinfo, fp->conn->ClientGUID,
++                                    lctx->parent_lease_key))) {
++                      if (!atomic_inc_not_zero(&opinfo->refcount))
++                              continue;
++
++                      atomic_inc(&opinfo->conn->r_count);
++                      if (ksmbd_conn_releasing(opinfo->conn)) {
++                              atomic_dec(&opinfo->conn->r_count);
++                              continue;
++                      }
++
++                      read_unlock(&p_ci->m_lock);
++                      oplock_break(opinfo, SMB2_OPLOCK_LEVEL_NONE);
++                      opinfo_conn_put(opinfo);
++                      read_lock(&p_ci->m_lock);
++              }
++      }
++      read_unlock(&p_ci->m_lock);
++
++      ksmbd_inode_put(p_ci);
++}
++
+ /**
+  * smb_grant_oplock() - handle oplock/lease request on file open
+  * @work:             smb work
+@@ -1420,10 +1465,11 @@ struct lease_ctx_info *parse_lease_state(void *open_req, bool is_dir)
+               struct create_lease_v2 *lc = (struct create_lease_v2 *)cc;
+               memcpy(lreq->lease_key, lc->lcontext.LeaseKey, SMB2_LEASE_KEY_SIZE);
+-              if (is_dir)
++              if (is_dir) {
+                       lreq->req_state = lc->lcontext.LeaseState &
+                               ~SMB2_LEASE_WRITE_CACHING_LE;
+-              else
++                      lreq->is_dir = true;
++              } else
+                       lreq->req_state = lc->lcontext.LeaseState;
+               lreq->flags = lc->lcontext.LeaseFlags;
+               lreq->epoch = lc->lcontext.Epoch;
+diff --git a/fs/smb/server/oplock.h b/fs/smb/server/oplock.h
+index 672127318c750..b64d1536882a1 100644
+--- a/fs/smb/server/oplock.h
++++ b/fs/smb/server/oplock.h
+@@ -36,6 +36,7 @@ struct lease_ctx_info {
+       __u8                    parent_lease_key[SMB2_LEASE_KEY_SIZE];
+       __le16                  epoch;
+       int                     version;
++      bool                    is_dir;
+ };
+ struct lease_table {
+@@ -54,6 +55,7 @@ struct lease {
+       __u8                    parent_lease_key[SMB2_LEASE_KEY_SIZE];
+       int                     version;
+       unsigned short          epoch;
++      bool                    is_dir;
+       struct lease_table      *l_lb;
+ };
+@@ -125,4 +127,6 @@ struct oplock_info *lookup_lease_in_table(struct ksmbd_conn *conn,
+ int find_same_lease_key(struct ksmbd_session *sess, struct ksmbd_inode *ci,
+                       struct lease_ctx_info *lctx);
+ void destroy_lease_table(struct ksmbd_conn *conn);
++void smb_send_parent_lease_break_noti(struct ksmbd_file *fp,
++                                    struct lease_ctx_info *lctx);
+ #endif /* __KSMBD_OPLOCK_H */
+diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
+index d7235c5e2e4d8..23e2c031cf2e5 100644
+--- a/fs/smb/server/smb2pdu.c
++++ b/fs/smb/server/smb2pdu.c
+@@ -3225,6 +3225,13 @@ int smb2_open(struct ksmbd_work *work)
+               }
+       } else {
+               if (req_op_level == SMB2_OPLOCK_LEVEL_LEASE) {
++                      /*
++                       * Compare parent lease using parent key. If there is no
++                       * a lease that has same parent key, Send lease break
++                       * notification.
++                       */
++                      smb_send_parent_lease_break_noti(fp, lc);
++
+                       req_op_level = smb2_map_lease_to_oplock(lc->req_state);
+                       ksmbd_debug(SMB,
+                                   "lease req for(%s) req oplock state 0x%x, lease state 0x%x\n",
+diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c
+index 774a387fccced..2528ce8aeebbe 100644
+--- a/fs/smb/server/vfs_cache.c
++++ b/fs/smb/server/vfs_cache.c
+@@ -86,6 +86,17 @@ static struct ksmbd_inode *ksmbd_inode_lookup(struct ksmbd_file *fp)
+       return __ksmbd_inode_lookup(fp->filp->f_path.dentry);
+ }
++struct ksmbd_inode *ksmbd_inode_lookup_lock(struct dentry *d)
++{
++      struct ksmbd_inode *ci;
++
++      read_lock(&inode_hash_lock);
++      ci = __ksmbd_inode_lookup(d);
++      read_unlock(&inode_hash_lock);
++
++      return ci;
++}
++
+ int ksmbd_query_inode_status(struct dentry *dentry)
+ {
+       struct ksmbd_inode *ci;
+@@ -198,7 +209,7 @@ static void ksmbd_inode_free(struct ksmbd_inode *ci)
+       kfree(ci);
+ }
+-static void ksmbd_inode_put(struct ksmbd_inode *ci)
++void ksmbd_inode_put(struct ksmbd_inode *ci)
+ {
+       if (atomic_dec_and_test(&ci->m_count))
+               ksmbd_inode_free(ci);
+diff --git a/fs/smb/server/vfs_cache.h b/fs/smb/server/vfs_cache.h
+index 8325cf4527c46..4d4938d6029b6 100644
+--- a/fs/smb/server/vfs_cache.h
++++ b/fs/smb/server/vfs_cache.h
+@@ -138,6 +138,8 @@ struct ksmbd_file *ksmbd_lookup_foreign_fd(struct ksmbd_work *work, u64 id);
+ struct ksmbd_file *ksmbd_lookup_fd_slow(struct ksmbd_work *work, u64 id,
+                                       u64 pid);
+ void ksmbd_fd_put(struct ksmbd_work *work, struct ksmbd_file *fp);
++struct ksmbd_inode *ksmbd_inode_lookup_lock(struct dentry *d);
++void ksmbd_inode_put(struct ksmbd_inode *ci);
+ struct ksmbd_file *ksmbd_lookup_durable_fd(unsigned long long id);
+ struct ksmbd_file *ksmbd_lookup_fd_cguid(char *cguid);
+ struct ksmbd_file *ksmbd_lookup_fd_inode(struct dentry *dentry);
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-separately-allocate-ci-per-dentry.patch b/queue-6.1/ksmbd-separately-allocate-ci-per-dentry.patch
new file mode 100644 (file)
index 0000000..fa367d0
--- /dev/null
@@ -0,0 +1,181 @@
+From 9bfc222a581bac716680de0c035f8099d06449f0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:13:21 +0900
+Subject: ksmbd: separately allocate ci per dentry
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit 4274a9dc6aeb9fea66bffba15697a35ae8983b6a ]
+
+xfstests generic/002 test fail when enabling smb2 leases feature.
+This test create hard link file, but removeal failed.
+ci has a file open count to count file open through the smb client,
+but in the case of hard link files, The allocation of ci per inode
+cause incorrectly open count for file deletion. This patch allocate
+ci per dentry to counts open counts for hard link.
+
+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/smb/server/smb2pdu.c   |  2 +-
+ fs/smb/server/vfs.c       |  2 +-
+ fs/smb/server/vfs_cache.c | 33 +++++++++++++--------------------
+ fs/smb/server/vfs_cache.h |  6 +++---
+ 4 files changed, 18 insertions(+), 25 deletions(-)
+
+diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
+index b8340f1382bef..ff4cc39b85c72 100644
+--- a/fs/smb/server/smb2pdu.c
++++ b/fs/smb/server/smb2pdu.c
+@@ -3039,7 +3039,7 @@ int smb2_open(struct ksmbd_work *work)
+               }
+       }
+-      rc = ksmbd_query_inode_status(d_inode(path.dentry->d_parent));
++      rc = ksmbd_query_inode_status(path.dentry->d_parent);
+       if (rc == KSMBD_INODE_STATUS_PENDING_DELETE) {
+               rc = -EBUSY;
+               goto err_out;
+diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c
+index 08f3f66e4b38e..42f270ee399c7 100644
+--- a/fs/smb/server/vfs.c
++++ b/fs/smb/server/vfs.c
+@@ -720,7 +720,7 @@ int ksmbd_vfs_rename(struct ksmbd_work *work, const struct path *old_path,
+               goto out3;
+       }
+-      parent_fp = ksmbd_lookup_fd_inode(d_inode(old_child->d_parent));
++      parent_fp = ksmbd_lookup_fd_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");
+diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c
+index 38f414e803adb..774a387fccced 100644
+--- a/fs/smb/server/vfs_cache.c
++++ b/fs/smb/server/vfs_cache.c
+@@ -65,14 +65,14 @@ static unsigned long inode_hash(struct super_block *sb, unsigned long hashval)
+       return tmp & inode_hash_mask;
+ }
+-static struct ksmbd_inode *__ksmbd_inode_lookup(struct inode *inode)
++static struct ksmbd_inode *__ksmbd_inode_lookup(struct dentry *de)
+ {
+       struct hlist_head *head = inode_hashtable +
+-              inode_hash(inode->i_sb, inode->i_ino);
++              inode_hash(d_inode(de)->i_sb, (unsigned long)de);
+       struct ksmbd_inode *ci = NULL, *ret_ci = NULL;
+       hlist_for_each_entry(ci, head, m_hash) {
+-              if (ci->m_inode == inode) {
++              if (ci->m_de == de) {
+                       if (atomic_inc_not_zero(&ci->m_count))
+                               ret_ci = ci;
+                       break;
+@@ -83,26 +83,16 @@ static struct ksmbd_inode *__ksmbd_inode_lookup(struct inode *inode)
+ static struct ksmbd_inode *ksmbd_inode_lookup(struct ksmbd_file *fp)
+ {
+-      return __ksmbd_inode_lookup(file_inode(fp->filp));
++      return __ksmbd_inode_lookup(fp->filp->f_path.dentry);
+ }
+-static struct ksmbd_inode *ksmbd_inode_lookup_by_vfsinode(struct inode *inode)
+-{
+-      struct ksmbd_inode *ci;
+-
+-      read_lock(&inode_hash_lock);
+-      ci = __ksmbd_inode_lookup(inode);
+-      read_unlock(&inode_hash_lock);
+-      return ci;
+-}
+-
+-int ksmbd_query_inode_status(struct inode *inode)
++int ksmbd_query_inode_status(struct dentry *dentry)
+ {
+       struct ksmbd_inode *ci;
+       int ret = KSMBD_INODE_STATUS_UNKNOWN;
+       read_lock(&inode_hash_lock);
+-      ci = __ksmbd_inode_lookup(inode);
++      ci = __ksmbd_inode_lookup(dentry);
+       if (ci) {
+               ret = KSMBD_INODE_STATUS_OK;
+               if (ci->m_flags & (S_DEL_PENDING | S_DEL_ON_CLS))
+@@ -142,7 +132,7 @@ void ksmbd_fd_set_delete_on_close(struct ksmbd_file *fp,
+ static void ksmbd_inode_hash(struct ksmbd_inode *ci)
+ {
+       struct hlist_head *b = inode_hashtable +
+-              inode_hash(ci->m_inode->i_sb, ci->m_inode->i_ino);
++              inode_hash(d_inode(ci->m_de)->i_sb, (unsigned long)ci->m_de);
+       hlist_add_head(&ci->m_hash, b);
+ }
+@@ -156,7 +146,6 @@ static void ksmbd_inode_unhash(struct ksmbd_inode *ci)
+ static int ksmbd_inode_init(struct ksmbd_inode *ci, struct ksmbd_file *fp)
+ {
+-      ci->m_inode = file_inode(fp->filp);
+       atomic_set(&ci->m_count, 1);
+       atomic_set(&ci->op_count, 0);
+       atomic_set(&ci->sop_count, 0);
+@@ -165,6 +154,7 @@ static int ksmbd_inode_init(struct ksmbd_inode *ci, struct ksmbd_file *fp)
+       INIT_LIST_HEAD(&ci->m_fp_list);
+       INIT_LIST_HEAD(&ci->m_op_list);
+       rwlock_init(&ci->m_lock);
++      ci->m_de = fp->filp->f_path.dentry;
+       return 0;
+ }
+@@ -487,12 +477,15 @@ struct ksmbd_file *ksmbd_lookup_fd_cguid(char *cguid)
+       return fp;
+ }
+-struct ksmbd_file *ksmbd_lookup_fd_inode(struct inode *inode)
++struct ksmbd_file *ksmbd_lookup_fd_inode(struct dentry *dentry)
+ {
+       struct ksmbd_file       *lfp;
+       struct ksmbd_inode      *ci;
++      struct inode            *inode = d_inode(dentry);
+-      ci = ksmbd_inode_lookup_by_vfsinode(inode);
++      read_lock(&inode_hash_lock);
++      ci = __ksmbd_inode_lookup(dentry);
++      read_unlock(&inode_hash_lock);
+       if (!ci)
+               return NULL;
+diff --git a/fs/smb/server/vfs_cache.h b/fs/smb/server/vfs_cache.h
+index 03d0bf941216f..8325cf4527c46 100644
+--- a/fs/smb/server/vfs_cache.h
++++ b/fs/smb/server/vfs_cache.h
+@@ -51,7 +51,7 @@ struct ksmbd_inode {
+       atomic_t                        op_count;
+       /* opinfo count for streams */
+       atomic_t                        sop_count;
+-      struct inode                    *m_inode;
++      struct dentry                   *m_de;
+       unsigned int                    m_flags;
+       struct hlist_node               m_hash;
+       struct list_head                m_fp_list;
+@@ -140,7 +140,7 @@ struct ksmbd_file *ksmbd_lookup_fd_slow(struct ksmbd_work *work, u64 id,
+ void ksmbd_fd_put(struct ksmbd_work *work, struct ksmbd_file *fp);
+ struct ksmbd_file *ksmbd_lookup_durable_fd(unsigned long long id);
+ struct ksmbd_file *ksmbd_lookup_fd_cguid(char *cguid);
+-struct ksmbd_file *ksmbd_lookup_fd_inode(struct inode *inode);
++struct ksmbd_file *ksmbd_lookup_fd_inode(struct dentry *dentry);
+ unsigned int ksmbd_open_durable_fd(struct ksmbd_file *fp);
+ struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp);
+ void ksmbd_close_tree_conn_fds(struct ksmbd_work *work);
+@@ -164,7 +164,7 @@ enum KSMBD_INODE_STATUS {
+       KSMBD_INODE_STATUS_PENDING_DELETE,
+ };
+-int ksmbd_query_inode_status(struct inode *inode);
++int ksmbd_query_inode_status(struct dentry *dentry);
+ bool ksmbd_inode_pending_delete(struct ksmbd_file *fp);
+ void ksmbd_set_inode_pending_delete(struct ksmbd_file *fp);
+ void ksmbd_clear_inode_pending_delete(struct ksmbd_file *fp);
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-set-epoch-in-create-context-v2-lease.patch b/queue-6.1/ksmbd-set-epoch-in-create-context-v2-lease.patch
new file mode 100644 (file)
index 0000000..16af7ee
--- /dev/null
@@ -0,0 +1,72 @@
+From e7a5c4b228eee1bfd4f893d7dbe116d62f83007f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:13:26 +0900
+Subject: ksmbd: set epoch in create context v2 lease
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit d045850b628aaf931fc776c90feaf824dca5a1cf ]
+
+To support v2 lease(directory lease), ksmbd set epoch in create context
+v2 lease response.
+
+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/smb/server/oplock.c | 5 ++++-
+ fs/smb/server/oplock.h | 1 +
+ 2 files changed, 5 insertions(+), 1 deletion(-)
+
+diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c
+index 4c74e8ea9649a..037316c78506e 100644
+--- a/fs/smb/server/oplock.c
++++ b/fs/smb/server/oplock.c
+@@ -104,7 +104,7 @@ static int alloc_lease(struct oplock_info *opinfo, struct lease_ctx_info *lctx)
+       lease->duration = lctx->duration;
+       memcpy(lease->parent_lease_key, lctx->parent_lease_key, SMB2_LEASE_KEY_SIZE);
+       lease->version = lctx->version;
+-      lease->epoch = 0;
++      lease->epoch = le16_to_cpu(lctx->epoch);
+       INIT_LIST_HEAD(&opinfo->lease_entry);
+       opinfo->o_lease = lease;
+@@ -1032,6 +1032,7 @@ static void copy_lease(struct oplock_info *op1, struct oplock_info *op2)
+              SMB2_LEASE_KEY_SIZE);
+       lease2->duration = lease1->duration;
+       lease2->flags = lease1->flags;
++      lease2->epoch = lease1->epoch++;
+ }
+ static int add_lease_global_list(struct oplock_info *opinfo)
+@@ -1364,6 +1365,7 @@ void create_lease_buf(u8 *rbuf, struct lease *lease)
+               memcpy(buf->lcontext.LeaseKey, lease->lease_key,
+                      SMB2_LEASE_KEY_SIZE);
+               buf->lcontext.LeaseFlags = lease->flags;
++              buf->lcontext.Epoch = cpu_to_le16(++lease->epoch);
+               buf->lcontext.LeaseState = lease->state;
+               memcpy(buf->lcontext.ParentLeaseKey, lease->parent_lease_key,
+                      SMB2_LEASE_KEY_SIZE);
+@@ -1423,6 +1425,7 @@ struct lease_ctx_info *parse_lease_state(void *open_req)
+               memcpy(lreq->lease_key, lc->lcontext.LeaseKey, SMB2_LEASE_KEY_SIZE);
+               lreq->req_state = lc->lcontext.LeaseState;
+               lreq->flags = lc->lcontext.LeaseFlags;
++              lreq->epoch = lc->lcontext.Epoch;
+               lreq->duration = lc->lcontext.LeaseDuration;
+               memcpy(lreq->parent_lease_key, lc->lcontext.ParentLeaseKey,
+                               SMB2_LEASE_KEY_SIZE);
+diff --git a/fs/smb/server/oplock.h b/fs/smb/server/oplock.h
+index 4b0fe6da76940..ad31439c61fef 100644
+--- a/fs/smb/server/oplock.h
++++ b/fs/smb/server/oplock.h
+@@ -34,6 +34,7 @@ struct lease_ctx_info {
+       __le32                  flags;
+       __le64                  duration;
+       __u8                    parent_lease_key[SMB2_LEASE_KEY_SIZE];
++      __le16                  epoch;
+       int                     version;
+ };
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-set-negotiatecontextcount-once-instead-of-ever.patch b/queue-6.1/ksmbd-set-negotiatecontextcount-once-instead-of-ever.patch
new file mode 100644 (file)
index 0000000..cf1420f
--- /dev/null
@@ -0,0 +1,75 @@
+From b1d31d12751738d75ce2c554aaad1ed4d00d7227 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:12:33 +0900
+Subject: ksmbd: set NegotiateContextCount once instead of every inc
+
+From: David Disseldorp <ddiss@suse.de>
+
+[ Upstream commit 34e8ccf9ce24b6b2e275bbe35cd392e18fbbd369 ]
+
+There are no early returns, so marshalling the incremented
+NegotiateContextCount with every context is unnecessary.
+
+Signed-off-by: David Disseldorp <ddiss@suse.de>
+Acked-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/server/smb2pdu.c | 10 +++++-----
+ 1 file changed, 5 insertions(+), 5 deletions(-)
+
+diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
+index ee128f5d38c35..c7d43c83d2335 100644
+--- a/fs/smb/server/smb2pdu.c
++++ b/fs/smb/server/smb2pdu.c
+@@ -847,7 +847,6 @@ static void assemble_neg_contexts(struct ksmbd_conn *conn,
+                   "assemble SMB2_PREAUTH_INTEGRITY_CAPABILITIES context\n");
+       build_preauth_ctxt((struct smb2_preauth_neg_context *)pneg_ctxt,
+                          conn->preauth_info->Preauth_HashId);
+-      rsp->NegotiateContextCount = cpu_to_le16(neg_ctxt_cnt);
+       inc_rfc1001_len(smb2_buf_len, AUTH_GSS_PADDING);
+       ctxt_size = sizeof(struct smb2_preauth_neg_context);
+       /* Round to 8 byte boundary */
+@@ -859,7 +858,7 @@ static void assemble_neg_contexts(struct ksmbd_conn *conn,
+                           "assemble SMB2_ENCRYPTION_CAPABILITIES context\n");
+               build_encrypt_ctxt((struct smb2_encryption_neg_context *)pneg_ctxt,
+                                  conn->cipher_type);
+-              rsp->NegotiateContextCount = cpu_to_le16(++neg_ctxt_cnt);
++              neg_ctxt_cnt++;
+               ctxt_size += sizeof(struct smb2_encryption_neg_context) + 2;
+               /* Round to 8 byte boundary */
+               pneg_ctxt +=
+@@ -874,7 +873,7 @@ static void assemble_neg_contexts(struct ksmbd_conn *conn,
+               /* Temporarily set to SMB3_COMPRESS_NONE */
+               build_compression_ctxt((struct smb2_compression_capabilities_context *)pneg_ctxt,
+                                      conn->compress_algorithm);
+-              rsp->NegotiateContextCount = cpu_to_le16(++neg_ctxt_cnt);
++              neg_ctxt_cnt++;
+               ctxt_size += sizeof(struct smb2_compression_capabilities_context) + 2;
+               /* Round to 8 byte boundary */
+               pneg_ctxt += round_up(sizeof(struct smb2_compression_capabilities_context) + 2,
+@@ -886,7 +885,7 @@ static void assemble_neg_contexts(struct ksmbd_conn *conn,
+               ksmbd_debug(SMB,
+                           "assemble SMB2_POSIX_EXTENSIONS_AVAILABLE context\n");
+               build_posix_ctxt((struct smb2_posix_neg_context *)pneg_ctxt);
+-              rsp->NegotiateContextCount = cpu_to_le16(++neg_ctxt_cnt);
++              neg_ctxt_cnt++;
+               ctxt_size += sizeof(struct smb2_posix_neg_context);
+               /* Round to 8 byte boundary */
+               pneg_ctxt += round_up(sizeof(struct smb2_posix_neg_context), 8);
+@@ -898,10 +897,11 @@ static void assemble_neg_contexts(struct ksmbd_conn *conn,
+                           "assemble SMB2_SIGNING_CAPABILITIES context\n");
+               build_sign_cap_ctxt((struct smb2_signing_capabilities *)pneg_ctxt,
+                                   conn->signing_algorithm);
+-              rsp->NegotiateContextCount = cpu_to_le16(++neg_ctxt_cnt);
++              neg_ctxt_cnt++;
+               ctxt_size += sizeof(struct smb2_signing_capabilities) + 2;
+       }
++      rsp->NegotiateContextCount = cpu_to_le16(neg_ctxt_cnt);
+       inc_rfc1001_len(smb2_buf_len, ctxt_size);
+ }
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-set-smb2_session_flag_encrypt_data-when-enforc.patch b/queue-6.1/ksmbd-set-smb2_session_flag_encrypt_data-when-enforc.patch
new file mode 100644 (file)
index 0000000..7c04f6f
--- /dev/null
@@ -0,0 +1,103 @@
+From 5a01e0a08c272f0d6e9802e4a9aade5f18bef742 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:12:21 +0900
+Subject: ksmbd: set SMB2_SESSION_FLAG_ENCRYPT_DATA when enforcing data
+ encryption for this share
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit 37ba7b005a7a4454046bd8659c7a9c5330552396 ]
+
+Currently, SMB2_SESSION_FLAG_ENCRYPT_DATA is always set session setup
+response. Since this forces data encryption from the client, there is a
+problem that data is always encrypted regardless of the use of the cifs
+seal mount option. SMB2_SESSION_FLAG_ENCRYPT_DATA should be set according
+to KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION flags, and in case of
+KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION_OFF, encryption mode is turned off for
+all connections.
+
+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/smb/server/ksmbd_netlink.h |  1 +
+ fs/smb/server/smb2ops.c       | 10 ++++++++--
+ fs/smb/server/smb2pdu.c       |  8 +++++---
+ 3 files changed, 14 insertions(+), 5 deletions(-)
+
+diff --git a/fs/smb/server/ksmbd_netlink.h b/fs/smb/server/ksmbd_netlink.h
+index ce866ff159bfe..fb8b2d566efb6 100644
+--- a/fs/smb/server/ksmbd_netlink.h
++++ b/fs/smb/server/ksmbd_netlink.h
+@@ -74,6 +74,7 @@ struct ksmbd_heartbeat {
+ #define KSMBD_GLOBAL_FLAG_SMB2_LEASES         BIT(0)
+ #define KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION     BIT(1)
+ #define KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL   BIT(2)
++#define KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION_OFF BIT(3)
+ /*
+  * IPC request for ksmbd server startup
+diff --git a/fs/smb/server/smb2ops.c b/fs/smb/server/smb2ops.c
+index ab23da2120b94..e401302478c36 100644
+--- a/fs/smb/server/smb2ops.c
++++ b/fs/smb/server/smb2ops.c
+@@ -247,8 +247,9 @@ void init_smb3_02_server(struct ksmbd_conn *conn)
+       if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES)
+               conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING;
+-      if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION &&
+-          conn->cli_cap & SMB2_GLOBAL_CAP_ENCRYPTION)
++      if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION ||
++          (!(server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION_OFF) &&
++           conn->cli_cap & SMB2_GLOBAL_CAP_ENCRYPTION))
+               conn->vals->capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION;
+       if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL)
+@@ -271,6 +272,11 @@ int init_smb3_11_server(struct ksmbd_conn *conn)
+       if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES)
+               conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING;
++      if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION ||
++          (!(server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION_OFF) &&
++           conn->cli_cap & SMB2_GLOBAL_CAP_ENCRYPTION))
++              conn->vals->capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION;
++
+       if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL)
+               conn->vals->capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL;
+diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
+index 3f4f6b0385655..f5a46b6831636 100644
+--- a/fs/smb/server/smb2pdu.c
++++ b/fs/smb/server/smb2pdu.c
+@@ -935,7 +935,7 @@ static void decode_encrypt_ctxt(struct ksmbd_conn *conn,
+               return;
+       }
+-      if (!(server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION))
++      if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION_OFF)
+               return;
+       for (i = 0; i < cph_cnt; i++) {
+@@ -1544,7 +1544,8 @@ static int ntlm_authenticate(struct ksmbd_work *work,
+                       return -EINVAL;
+               }
+               sess->enc = true;
+-              rsp->SessionFlags = SMB2_SESSION_FLAG_ENCRYPT_DATA_LE;
++              if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION)
++                      rsp->SessionFlags = SMB2_SESSION_FLAG_ENCRYPT_DATA_LE;
+               /*
+                * signing is disable if encryption is enable
+                * on this session
+@@ -1630,7 +1631,8 @@ static int krb5_authenticate(struct ksmbd_work *work,
+                       return -EINVAL;
+               }
+               sess->enc = true;
+-              rsp->SessionFlags = SMB2_SESSION_FLAG_ENCRYPT_DATA_LE;
++              if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION)
++                      rsp->SessionFlags = SMB2_SESSION_FLAG_ENCRYPT_DATA_LE;
+               sess->sign = false;
+       }
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-set-v2-lease-capability.patch b/queue-6.1/ksmbd-set-v2-lease-capability.patch
new file mode 100644 (file)
index 0000000..c6757a4
--- /dev/null
@@ -0,0 +1,72 @@
+From 8e531b27cdcfd6b2fd690d5a6fc684b84386320f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:13:27 +0900
+Subject: ksmbd: set v2 lease capability
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit 18dd1c367c31d0a060f737d48345747662369b64 ]
+
+Set SMB2_GLOBAL_CAP_DIRECTORY_LEASING to ->capabilities to inform server
+support directory lease to client.
+
+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/smb/server/oplock.c  | 4 ----
+ fs/smb/server/smb2ops.c | 9 ++++++---
+ 2 files changed, 6 insertions(+), 7 deletions(-)
+
+diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c
+index 037316c78506e..7346cbfbff6b0 100644
+--- a/fs/smb/server/oplock.c
++++ b/fs/smb/server/oplock.c
+@@ -1105,10 +1105,6 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid,
+       bool prev_op_has_lease;
+       __le32 prev_op_state = 0;
+-      /* not support directory lease */
+-      if (S_ISDIR(file_inode(fp->filp)->i_mode))
+-              return 0;
+-
+       opinfo = alloc_opinfo(work, pid, tid);
+       if (!opinfo)
+               return -ENOMEM;
+diff --git a/fs/smb/server/smb2ops.c b/fs/smb/server/smb2ops.c
+index e401302478c36..535402629655e 100644
+--- a/fs/smb/server/smb2ops.c
++++ b/fs/smb/server/smb2ops.c
+@@ -221,7 +221,8 @@ void init_smb3_0_server(struct ksmbd_conn *conn)
+       conn->signing_algorithm = SIGNING_ALG_AES_CMAC_LE;
+       if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES)
+-              conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING;
++              conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING |
++                      SMB2_GLOBAL_CAP_DIRECTORY_LEASING;
+       if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION &&
+           conn->cli_cap & SMB2_GLOBAL_CAP_ENCRYPTION)
+@@ -245,7 +246,8 @@ void init_smb3_02_server(struct ksmbd_conn *conn)
+       conn->signing_algorithm = SIGNING_ALG_AES_CMAC_LE;
+       if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES)
+-              conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING;
++              conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING |
++                      SMB2_GLOBAL_CAP_DIRECTORY_LEASING;
+       if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION ||
+           (!(server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION_OFF) &&
+@@ -270,7 +272,8 @@ int init_smb3_11_server(struct ksmbd_conn *conn)
+       conn->signing_algorithm = SIGNING_ALG_AES_CMAC_LE;
+       if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES)
+-              conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING;
++              conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING |
++                      SMB2_GLOBAL_CAP_DIRECTORY_LEASING;
+       if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION ||
+           (!(server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION_OFF) &&
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-switch-to-use-kmemdup_nul-helper.patch b/queue-6.1/ksmbd-switch-to-use-kmemdup_nul-helper.patch
new file mode 100644 (file)
index 0000000..7823acf
--- /dev/null
@@ -0,0 +1,41 @@
+From 61a8df80b1a0ba0280d148df81a0403dba06f67e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:12:53 +0900
+Subject: ksmbd: switch to use kmemdup_nul() helper
+
+From: Yang Yingliang <yangyingliang@huawei.com>
+
+[ Upstream commit 084ba46fc41c21ba827fd92e61f78def7a6e52ea ]
+
+Use kmemdup_nul() helper instead of open-coding to
+simplify the code.
+
+Acked-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Yang Yingliang <yangyingliang@huawei.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/server/asn1.c | 4 +---
+ 1 file changed, 1 insertion(+), 3 deletions(-)
+
+diff --git a/fs/smb/server/asn1.c b/fs/smb/server/asn1.c
+index cc6384f796759..4a4b2b03ff33d 100644
+--- a/fs/smb/server/asn1.c
++++ b/fs/smb/server/asn1.c
+@@ -214,12 +214,10 @@ static int ksmbd_neg_token_alloc(void *context, size_t hdrlen,
+ {
+       struct ksmbd_conn *conn = context;
+-      conn->mechToken = kmalloc(vlen + 1, GFP_KERNEL);
++      conn->mechToken = kmemdup_nul(value, vlen, GFP_KERNEL);
+       if (!conn->mechToken)
+               return -ENOMEM;
+-      memcpy(conn->mechToken, value, vlen);
+-      conn->mechToken[vlen] = '\0';
+       return 0;
+ }
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-update-kconfig-to-note-kerberos-support-and-fi.patch b/queue-6.1/ksmbd-update-kconfig-to-note-kerberos-support-and-fi.patch
new file mode 100644 (file)
index 0000000..8da305f
--- /dev/null
@@ -0,0 +1,58 @@
+From a2cca2e2827e921994f32e7c939f2cc57a7a458a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:12:28 +0900
+Subject: ksmbd: update Kconfig to note Kerberos support and fix indentation
+
+From: Steve French <stfrench@microsoft.com>
+
+[ Upstream commit d280a958f8b2b62610c280ecdf35d780e7922620 ]
+
+Fix indentation of server config options, and also since
+support for very old, less secure, NTLM authentication was removed
+(and quite a while ago), remove the mention of that in Kconfig, but
+do note Kerberos (not just NTLMv2) which are supported and much
+more secure.
+
+Acked-by: Namjae Jeon <linkinjeon@kernel.org>
+Acked-by: David Howells <dhowells@redhat.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/server/Kconfig | 8 ++++++--
+ 1 file changed, 6 insertions(+), 2 deletions(-)
+
+diff --git a/fs/smb/server/Kconfig b/fs/smb/server/Kconfig
+index e1fe17747ed69..7055cb5d28800 100644
+--- a/fs/smb/server/Kconfig
++++ b/fs/smb/server/Kconfig
+@@ -33,14 +33,16 @@ config SMB_SERVER
+         in ksmbd-tools, available from
+         https://github.com/cifsd-team/ksmbd-tools.
+         More detail about how to run the ksmbd kernel server is
+-        available via README file
++        available via the README file
+         (https://github.com/cifsd-team/ksmbd-tools/blob/master/README).
+         ksmbd kernel server includes support for auto-negotiation,
+         Secure negotiate, Pre-authentication integrity, oplock/lease,
+         compound requests, multi-credit, packet signing, RDMA(smbdirect),
+         smb3 encryption, copy-offload, secure per-user session
+-        establishment via NTLM or NTLMv2.
++        establishment via Kerberos or NTLMv2.
++
++if SMB_SERVER
+ config SMB_SERVER_SMBDIRECT
+       bool "Support for SMB Direct protocol"
+@@ -54,6 +56,8 @@ config SMB_SERVER_SMBDIRECT
+         SMB Direct allows transferring SMB packets over RDMA. If unsure,
+         say N.
++endif
++
+ config SMB_SERVER_CHECK_CAP_NET_ADMIN
+       bool "Enable check network administration capability"
+       depends on SMB_SERVER
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-use-f_setlk-when-unlocking-a-file.patch b/queue-6.1/ksmbd-use-f_setlk-when-unlocking-a-file.patch
new file mode 100644 (file)
index 0000000..04723e7
--- /dev/null
@@ -0,0 +1,50 @@
+From 86edb53671ff99396167aea58c33fcc3f2519fb9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:12:22 +0900
+Subject: ksmbd: use F_SETLK when unlocking a file
+
+From: Jeff Layton <jlayton@kernel.org>
+
+[ Upstream commit 7ecbe92696bb7fe32c80b6cf64736a0d157717a9 ]
+
+ksmbd seems to be trying to use a cmd value of 0 when unlocking a file.
+That activity requires a type of F_UNLCK with a cmd of F_SETLK. For
+local POSIX locking, it doesn't matter much since vfs_lock_file ignores
+@cmd, but filesystems that define their own ->lock operation expect to
+see it set sanely.
+
+Cc: David Howells <dhowells@redhat.com>
+Signed-off-by: Jeff Layton <jlayton@kernel.org>
+Reviewed-by: David Howells <dhowells@redhat.com>
+Acked-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/server/smb2pdu.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
+index f5a46b6831636..554214fca5b78 100644
+--- a/fs/smb/server/smb2pdu.c
++++ b/fs/smb/server/smb2pdu.c
+@@ -6845,7 +6845,7 @@ static int smb2_set_flock_flags(struct file_lock *flock, int flags)
+       case SMB2_LOCKFLAG_UNLOCK:
+               ksmbd_debug(SMB, "received unlock request\n");
+               flock->fl_type = F_UNLCK;
+-              cmd = 0;
++              cmd = F_SETLK;
+               break;
+       }
+@@ -7228,7 +7228,7 @@ int smb2_lock(struct ksmbd_work *work)
+               rlock->fl_start = smb_lock->start;
+               rlock->fl_end = smb_lock->end;
+-              rc = vfs_lock_file(filp, 0, rlock, NULL);
++              rc = vfs_lock_file(filp, F_SETLK, rlock, NULL);
+               if (rc)
+                       pr_err("rollback unlock fail : %d\n", rc);
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-use-kvzalloc-instead-of-kvmalloc.patch b/queue-6.1/ksmbd-use-kvzalloc-instead-of-kvmalloc.patch
new file mode 100644 (file)
index 0000000..0660137
--- /dev/null
@@ -0,0 +1,108 @@
+From ee0517dd0376763ba9e0b6432ac94fa691904670 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:12:47 +0900
+Subject: ksmbd: use kvzalloc instead of kvmalloc
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit 81a94b27847f7d2e499415db14dd9dc7c22b19b0 ]
+
+Use kvzalloc instead of kvmalloc.
+
+Reported-by: kernel test robot <lkp@intel.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/smb/server/smb2pdu.c       | 8 ++++----
+ fs/smb/server/transport_ipc.c | 4 ++--
+ fs/smb/server/vfs.c           | 4 ++--
+ 3 files changed, 8 insertions(+), 8 deletions(-)
+
+diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
+index e8d2c6fc3f37c..10d51256858ff 100644
+--- a/fs/smb/server/smb2pdu.c
++++ b/fs/smb/server/smb2pdu.c
+@@ -543,7 +543,7 @@ int smb2_allocate_rsp_buf(struct ksmbd_work *work)
+       if (le32_to_cpu(hdr->NextCommand) > 0)
+               sz = large_sz;
+-      work->response_buf = kvmalloc(sz, GFP_KERNEL | __GFP_ZERO);
++      work->response_buf = kvzalloc(sz, GFP_KERNEL);
+       if (!work->response_buf)
+               return -ENOMEM;
+@@ -6120,7 +6120,7 @@ static noinline int smb2_read_pipe(struct ksmbd_work *work)
+               }
+               work->aux_payload_buf =
+-                      kvmalloc(rpc_resp->payload_sz, GFP_KERNEL | __GFP_ZERO);
++                      kvmalloc(rpc_resp->payload_sz, GFP_KERNEL);
+               if (!work->aux_payload_buf) {
+                       err = -ENOMEM;
+                       goto out;
+@@ -6277,7 +6277,7 @@ int smb2_read(struct ksmbd_work *work)
+       ksmbd_debug(SMB, "filename %pD, offset %lld, len %zu\n",
+                   fp->filp, offset, length);
+-      work->aux_payload_buf = kvmalloc(length, GFP_KERNEL | __GFP_ZERO);
++      work->aux_payload_buf = kvzalloc(length, GFP_KERNEL);
+       if (!work->aux_payload_buf) {
+               err = -ENOMEM;
+               goto out;
+@@ -6428,7 +6428,7 @@ static ssize_t smb2_write_rdma_channel(struct ksmbd_work *work,
+       int ret;
+       ssize_t nbytes;
+-      data_buf = kvmalloc(length, GFP_KERNEL | __GFP_ZERO);
++      data_buf = kvzalloc(length, GFP_KERNEL);
+       if (!data_buf)
+               return -ENOMEM;
+diff --git a/fs/smb/server/transport_ipc.c b/fs/smb/server/transport_ipc.c
+index 40c721f9227e4..b49d47bdafc94 100644
+--- a/fs/smb/server/transport_ipc.c
++++ b/fs/smb/server/transport_ipc.c
+@@ -229,7 +229,7 @@ static struct ksmbd_ipc_msg *ipc_msg_alloc(size_t sz)
+       struct ksmbd_ipc_msg *msg;
+       size_t msg_sz = sz + sizeof(struct ksmbd_ipc_msg);
+-      msg = kvmalloc(msg_sz, GFP_KERNEL | __GFP_ZERO);
++      msg = kvzalloc(msg_sz, GFP_KERNEL);
+       if (msg)
+               msg->sz = sz;
+       return msg;
+@@ -268,7 +268,7 @@ static int handle_response(int type, void *payload, size_t sz)
+                              entry->type + 1, type);
+               }
+-              entry->response = kvmalloc(sz, GFP_KERNEL | __GFP_ZERO);
++              entry->response = kvzalloc(sz, GFP_KERNEL);
+               if (!entry->response) {
+                       ret = -ENOMEM;
+                       break;
+diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c
+index 178bcd4d0b209..d05d2d1274b04 100644
+--- a/fs/smb/server/vfs.c
++++ b/fs/smb/server/vfs.c
+@@ -437,7 +437,7 @@ static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos,
+       }
+       if (v_len < size) {
+-              wbuf = kvmalloc(size, GFP_KERNEL | __GFP_ZERO);
++              wbuf = kvzalloc(size, GFP_KERNEL);
+               if (!wbuf) {
+                       err = -ENOMEM;
+                       goto out;
+@@ -854,7 +854,7 @@ ssize_t ksmbd_vfs_listxattr(struct dentry *dentry, char **list)
+       if (size <= 0)
+               return size;
+-      vlist = kvmalloc(size, GFP_KERNEL | __GFP_ZERO);
++      vlist = kvzalloc(size, GFP_KERNEL);
+       if (!vlist)
+               return -ENOMEM;
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-use-kzalloc-instead-of-__gfp_zero.patch b/queue-6.1/ksmbd-use-kzalloc-instead-of-__gfp_zero.patch
new file mode 100644 (file)
index 0000000..45f450b
--- /dev/null
@@ -0,0 +1,38 @@
+From c97f5127fb0e6a274a550e2be9ce17a1ba1938dc Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:12:44 +0900
+Subject: ksmbd: use kzalloc() instead of __GFP_ZERO
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit f87d4f85f43f0d4b12ef64b015478d8053e1a33e ]
+
+Use kzalloc() instead of __GFP_ZERO.
+
+Reported-by: Dan Carpenter <error27@gmail.com>
+Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
+Reviewed-by: Sergey Senozhatsky <senozhatsky@chromium.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/server/smb_common.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/fs/smb/server/smb_common.c b/fs/smb/server/smb_common.c
+index adc41b57b84c6..62c33d3357fe1 100644
+--- a/fs/smb/server/smb_common.c
++++ b/fs/smb/server/smb_common.c
+@@ -359,8 +359,8 @@ static int smb1_check_user_session(struct ksmbd_work *work)
+  */
+ static int smb1_allocate_rsp_buf(struct ksmbd_work *work)
+ {
+-      work->response_buf = kmalloc(MAX_CIFS_SMALL_BUFFER_SIZE,
+-                      GFP_KERNEL | __GFP_ZERO);
++      work->response_buf = kzalloc(MAX_CIFS_SMALL_BUFFER_SIZE,
++                      GFP_KERNEL);
+       work->response_sz = MAX_CIFS_SMALL_BUFFER_SIZE;
+       if (!work->response_buf) {
+-- 
+2.43.0
+
diff --git a/queue-6.1/ksmbd-use-struct_size-helper-in-ksmbd_negotiate_smb_.patch b/queue-6.1/ksmbd-use-struct_size-helper-in-ksmbd_negotiate_smb_.patch
new file mode 100644 (file)
index 0000000..5656e2d
--- /dev/null
@@ -0,0 +1,38 @@
+From 9de1541d1c548ff46e8285362f587e02c4afc3ea Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 31 Dec 2023 16:12:49 +0900
+Subject: ksmbd: Use struct_size() helper in ksmbd_negotiate_smb_dialect()
+
+From: Gustavo A. R. Silva <gustavoars@kernel.org>
+
+[ Upstream commit 5211cc8727ed9701b04976ab47602955e5641bda ]
+
+Prefer struct_size() over open-coded versions.
+
+Link: https://github.com/KSPP/linux/issues/160
+Signed-off-by: Gustavo A. R. Silva <gustavoars@kernel.org>
+Reviewed-by: Kees Cook <keescook@chromium.org>
+Acked-by: Namjae Jeon <linkinjeon@kernel.org>
+Reviewed-by: Sergey Senozhatsky <senozhatsky@chromium.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/server/smb_common.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/fs/smb/server/smb_common.c b/fs/smb/server/smb_common.c
+index 62c33d3357fe1..b6f414a2404b1 100644
+--- a/fs/smb/server/smb_common.c
++++ b/fs/smb/server/smb_common.c
+@@ -266,7 +266,7 @@ static int ksmbd_negotiate_smb_dialect(void *buf)
+               if (smb2_neg_size > smb_buf_length)
+                       goto err_out;
+-              if (smb2_neg_size + le16_to_cpu(req->DialectCount) * sizeof(__le16) >
++              if (struct_size(req, Dialects, le16_to_cpu(req->DialectCount)) >
+                   smb_buf_length)
+                       goto err_out;
+-- 
+2.43.0
+
diff --git a/queue-6.1/linux-export-ensure-natural-alignment-of-kcrctab-arr.patch b/queue-6.1/linux-export-ensure-natural-alignment-of-kcrctab-arr.patch
new file mode 100644 (file)
index 0000000..617ac8c
--- /dev/null
@@ -0,0 +1,35 @@
+From f5606fbe0adf2adc4a9c9edec8e29d4e0e17f7ff Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 28 Dec 2023 11:36:03 +0100
+Subject: linux/export: Ensure natural alignment of kcrctab array
+
+From: Helge Deller <deller@gmx.de>
+
+[ Upstream commit 753547de0daecbdbd1af3618987ddade325d9aaa ]
+
+The ___kcrctab section holds an array of 32-bit CRC values.
+Add a .balign 4 to tell the linker the correct memory alignment.
+
+Fixes: f3304ecd7f06 ("linux/export: use inline assembler to populate symbol CRCs")
+Signed-off-by: Helge Deller <deller@gmx.de>
+Signed-off-by: Masahiro Yamada <masahiroy@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/linux/export-internal.h | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/include/linux/export-internal.h b/include/linux/export-internal.h
+index fe7e6ba918f10..29de29af9546c 100644
+--- a/include/linux/export-internal.h
++++ b/include/linux/export-internal.h
+@@ -12,6 +12,7 @@
+ #define SYMBOL_CRC(sym, crc, sec)   \
+       asm(".section \"___kcrctab" sec "+" #sym "\",\"a\""     "\n" \
++          ".balign 4"                                         "\n" \
+           "__crc_" #sym ":"                                   "\n" \
+           ".long " #crc                                       "\n" \
+           ".previous"                                         "\n")
+-- 
+2.43.0
+
diff --git a/queue-6.1/nfsd-call-nfsd_last_thread-before-final-nfsd_put.patch b/queue-6.1/nfsd-call-nfsd_last_thread-before-final-nfsd_put.patch
new file mode 100644 (file)
index 0000000..32fde61
--- /dev/null
@@ -0,0 +1,83 @@
+From 1ef488e4b54c2f2168f1219e1319bd08e7bb5e9f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 15 Dec 2023 11:56:31 +1100
+Subject: nfsd: call nfsd_last_thread() before final nfsd_put()
+
+From: NeilBrown <neilb@suse.de>
+
+[ Upstream commit 2a501f55cd641eb4d3c16a2eab0d678693fac663 ]
+
+If write_ports_addfd or write_ports_addxprt fail, they call nfsd_put()
+without calling nfsd_last_thread().  This leaves nn->nfsd_serv pointing
+to a structure that has been freed.
+
+So remove 'static' from nfsd_last_thread() and call it when the
+nfsd_serv is about to be destroyed.
+
+Fixes: ec52361df99b ("SUNRPC: stop using ->sv_nrthreads as a refcount")
+Signed-off-by: NeilBrown <neilb@suse.de>
+Reviewed-by: Jeff Layton <jlayton@kernel.org>
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/nfsd/nfsctl.c | 9 +++++++--
+ fs/nfsd/nfsd.h   | 1 +
+ fs/nfsd/nfssvc.c | 2 +-
+ 3 files changed, 9 insertions(+), 3 deletions(-)
+
+diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c
+index 573de0d49e172..b3b4542e31ed5 100644
+--- a/fs/nfsd/nfsctl.c
++++ b/fs/nfsd/nfsctl.c
+@@ -716,8 +716,10 @@ static ssize_t __write_ports_addfd(char *buf, struct net *net, const struct cred
+       err = svc_addsock(nn->nfsd_serv, net, fd, buf, SIMPLE_TRANSACTION_LIMIT, cred);
+-      if (err >= 0 &&
+-          !nn->nfsd_serv->sv_nrthreads && !xchg(&nn->keep_active, 1))
++      if (err < 0 && !nn->nfsd_serv->sv_nrthreads && !nn->keep_active)
++              nfsd_last_thread(net);
++      else if (err >= 0 &&
++               !nn->nfsd_serv->sv_nrthreads && !xchg(&nn->keep_active, 1))
+               svc_get(nn->nfsd_serv);
+       nfsd_put(net);
+@@ -767,6 +769,9 @@ static ssize_t __write_ports_addxprt(char *buf, struct net *net, const struct cr
+               svc_xprt_put(xprt);
+       }
+ out_err:
++      if (!nn->nfsd_serv->sv_nrthreads && !nn->keep_active)
++              nfsd_last_thread(net);
++
+       nfsd_put(net);
+       return err;
+ }
+diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h
+index fddd70372e4cb..53166cce7062c 100644
+--- a/fs/nfsd/nfsd.h
++++ b/fs/nfsd/nfsd.h
+@@ -139,6 +139,7 @@ int nfsd_vers(struct nfsd_net *nn, int vers, enum vers_op change);
+ int nfsd_minorversion(struct nfsd_net *nn, u32 minorversion, enum vers_op change);
+ void nfsd_reset_versions(struct nfsd_net *nn);
+ int nfsd_create_serv(struct net *net);
++void nfsd_last_thread(struct net *net);
+ extern int nfsd_max_blksize;
+diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c
+index 6ac18399fed2b..d8662bdca5706 100644
+--- a/fs/nfsd/nfssvc.c
++++ b/fs/nfsd/nfssvc.c
+@@ -523,7 +523,7 @@ static struct notifier_block nfsd_inet6addr_notifier = {
+ /* Only used under nfsd_mutex, so this atomic may be overkill: */
+ static atomic_t nfsd_notifier_refcount = ATOMIC_INIT(0);
+-static void nfsd_last_thread(struct net *net)
++void nfsd_last_thread(struct net *net)
+ {
+       struct nfsd_net *nn = net_generic(net, nfsd_net_id);
+       struct svc_serv *serv = nn->nfsd_serv;
+-- 
+2.43.0
+
diff --git a/queue-6.1/nfsd-separate-nfsd_last_thread-from-nfsd_put.patch b/queue-6.1/nfsd-separate-nfsd_last_thread-from-nfsd_put.patch
new file mode 100644 (file)
index 0000000..da09796
--- /dev/null
@@ -0,0 +1,182 @@
+From 08172072a5a43c346fab7d1dd36176cb949282f1 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 31 Jul 2023 16:48:32 +1000
+Subject: nfsd: separate nfsd_last_thread() from nfsd_put()
+
+From: NeilBrown <neilb@suse.de>
+
+[ Upstream commit 9f28a971ee9fdf1bf8ce8c88b103f483be610277 ]
+
+Now that the last nfsd thread is stopped by an explicit act of calling
+svc_set_num_threads() with a count of zero, we only have a limited
+number of places that can happen, and don't need to call
+nfsd_last_thread() in nfsd_put()
+
+So separate that out and call it at the two places where the number of
+threads is set to zero.
+
+Move the clearing of ->nfsd_serv and the call to svc_xprt_destroy_all()
+into nfsd_last_thread(), as they are really part of the same action.
+
+nfsd_put() is now a thin wrapper around svc_put(), so make it a static
+inline.
+
+nfsd_put() cannot be called after nfsd_last_thread(), so in a couple of
+places we have to use svc_put() instead.
+
+Signed-off-by: NeilBrown <neilb@suse.de>
+Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
+Stable-dep-of: 2a501f55cd64 ("nfsd: call nfsd_last_thread() before final nfsd_put()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/nfsd/nfsd.h   |  7 ++++++-
+ fs/nfsd/nfssvc.c | 52 ++++++++++++++++++------------------------------
+ 2 files changed, 25 insertions(+), 34 deletions(-)
+
+diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h
+index 09726c5b9a317..fddd70372e4cb 100644
+--- a/fs/nfsd/nfsd.h
++++ b/fs/nfsd/nfsd.h
+@@ -97,7 +97,12 @@ int         nfsd_pool_stats_open(struct inode *, struct file *);
+ int           nfsd_pool_stats_release(struct inode *, struct file *);
+ void          nfsd_shutdown_threads(struct net *net);
+-void          nfsd_put(struct net *net);
++static inline void nfsd_put(struct net *net)
++{
++      struct nfsd_net *nn = net_generic(net, nfsd_net_id);
++
++      svc_put(nn->nfsd_serv);
++}
+ bool          i_am_nfsd(void);
+diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c
+index f6cc99af81925..6ac18399fed2b 100644
+--- a/fs/nfsd/nfssvc.c
++++ b/fs/nfsd/nfssvc.c
+@@ -523,9 +523,14 @@ static struct notifier_block nfsd_inet6addr_notifier = {
+ /* Only used under nfsd_mutex, so this atomic may be overkill: */
+ static atomic_t nfsd_notifier_refcount = ATOMIC_INIT(0);
+-static void nfsd_last_thread(struct svc_serv *serv, struct net *net)
++static void nfsd_last_thread(struct net *net)
+ {
+       struct nfsd_net *nn = net_generic(net, nfsd_net_id);
++      struct svc_serv *serv = nn->nfsd_serv;
++
++      spin_lock(&nfsd_notifier_lock);
++      nn->nfsd_serv = NULL;
++      spin_unlock(&nfsd_notifier_lock);
+       /* check if the notifier still has clients */
+       if (atomic_dec_return(&nfsd_notifier_refcount) == 0) {
+@@ -535,6 +540,8 @@ static void nfsd_last_thread(struct svc_serv *serv, struct net *net)
+ #endif
+       }
++      svc_xprt_destroy_all(serv, net);
++
+       /*
+        * write_ports can create the server without actually starting
+        * any threads--if we get shut down before any threads are
+@@ -625,7 +632,8 @@ void nfsd_shutdown_threads(struct net *net)
+       svc_get(serv);
+       /* Kill outstanding nfsd threads */
+       svc_set_num_threads(serv, NULL, 0);
+-      nfsd_put(net);
++      nfsd_last_thread(net);
++      svc_put(serv);
+       mutex_unlock(&nfsd_mutex);
+ }
+@@ -655,9 +663,6 @@ int nfsd_create_serv(struct net *net)
+       serv->sv_maxconn = nn->max_connections;
+       error = svc_bind(serv, net);
+       if (error < 0) {
+-              /* NOT nfsd_put() as notifiers (see below) haven't
+-               * been set up yet.
+-               */
+               svc_put(serv);
+               return error;
+       }
+@@ -700,29 +705,6 @@ int nfsd_get_nrthreads(int n, int *nthreads, struct net *net)
+       return 0;
+ }
+-/* This is the callback for kref_put() below.
+- * There is no code here as the first thing to be done is
+- * call svc_shutdown_net(), but we cannot get the 'net' from
+- * the kref.  So do all the work when kref_put returns true.
+- */
+-static void nfsd_noop(struct kref *ref)
+-{
+-}
+-
+-void nfsd_put(struct net *net)
+-{
+-      struct nfsd_net *nn = net_generic(net, nfsd_net_id);
+-
+-      if (kref_put(&nn->nfsd_serv->sv_refcnt, nfsd_noop)) {
+-              svc_xprt_destroy_all(nn->nfsd_serv, net);
+-              nfsd_last_thread(nn->nfsd_serv, net);
+-              svc_destroy(&nn->nfsd_serv->sv_refcnt);
+-              spin_lock(&nfsd_notifier_lock);
+-              nn->nfsd_serv = NULL;
+-              spin_unlock(&nfsd_notifier_lock);
+-      }
+-}
+-
+ int nfsd_set_nrthreads(int n, int *nthreads, struct net *net)
+ {
+       int i = 0;
+@@ -773,7 +755,7 @@ int nfsd_set_nrthreads(int n, int *nthreads, struct net *net)
+               if (err)
+                       break;
+       }
+-      nfsd_put(net);
++      svc_put(nn->nfsd_serv);
+       return err;
+ }
+@@ -788,6 +770,7 @@ nfsd_svc(int nrservs, struct net *net, const struct cred *cred)
+       int     error;
+       bool    nfsd_up_before;
+       struct nfsd_net *nn = net_generic(net, nfsd_net_id);
++      struct svc_serv *serv;
+       mutex_lock(&nfsd_mutex);
+       dprintk("nfsd: creating service\n");
+@@ -807,22 +790,25 @@ nfsd_svc(int nrservs, struct net *net, const struct cred *cred)
+               goto out;
+       nfsd_up_before = nn->nfsd_net_up;
++      serv = nn->nfsd_serv;
+       error = nfsd_startup_net(net, cred);
+       if (error)
+               goto out_put;
+-      error = svc_set_num_threads(nn->nfsd_serv, NULL, nrservs);
++      error = svc_set_num_threads(serv, NULL, nrservs);
+       if (error)
+               goto out_shutdown;
+-      error = nn->nfsd_serv->sv_nrthreads;
++      error = serv->sv_nrthreads;
++      if (error == 0)
++              nfsd_last_thread(net);
+ out_shutdown:
+       if (error < 0 && !nfsd_up_before)
+               nfsd_shutdown_net(net);
+ out_put:
+       /* Threads now hold service active */
+       if (xchg(&nn->keep_active, 0))
+-              nfsd_put(net);
+-      nfsd_put(net);
++              svc_put(serv);
++      svc_put(serv);
+ out:
+       mutex_unlock(&nfsd_mutex);
+       return error;
+-- 
+2.43.0
+
diff --git a/queue-6.1/series b/queue-6.1/series
new file mode 100644 (file)
index 0000000..d550eab
--- /dev/null
@@ -0,0 +1,84 @@
+ksmbd-replace-one-element-arrays-with-flexible-array.patch
+ksmbd-set-smb2_session_flag_encrypt_data-when-enforc.patch
+ksmbd-use-f_setlk-when-unlocking-a-file.patch
+ksmbd-fix-resource-leak-in-smb2_lock.patch
+ksmbd-convert-to-use-sysfs_emit-sysfs_emit_at-apis.patch
+ksmbd-implements-sess-rpc_handle_list-as-xarray.patch
+ksmbd-fix-typo-syncronous-synchronous.patch
+ksmbd-remove-duplicated-codes.patch
+ksmbd-update-kconfig-to-note-kerberos-support-and-fi.patch
+ksmbd-fix-spelling-mistake-excceed-exceeded.patch
+ksmbd-fix-parameter-name-and-comment-mismatch.patch
+ksmbd-remove-unused-is_char_allowed-function.patch
+ksmbd-delete-asynchronous-work-from-list.patch
+ksmbd-set-negotiatecontextcount-once-instead-of-ever.patch
+ksmbd-avoid-duplicate-negotiate-ctx-offset-increment.patch
+ksmbd-remove-unused-compression-negotiate-ctx-packin.patch
+fs-introduce-lock_rename_child-helper.patch
+ksmbd-fix-racy-issue-from-using-d_parent-and-d_name.patch
+ksmbd-fix-uninitialized-pointer-read-in-ksmbd_vfs_re.patch
+ksmbd-fix-uninitialized-pointer-read-in-smb2_create_.patch
+ksmbd-call-putname-after-using-the-last-component.patch
+ksmbd-fix-posix_acls-and-acls-dereferencing-possible.patch
+ksmbd-add-mnt_want_write-to-ksmbd-vfs-functions.patch
+ksmbd-remove-unused-ksmbd_tree_conn_share-function.patch
+ksmbd-use-kzalloc-instead-of-__gfp_zero.patch
+ksmbd-return-a-literal-instead-of-err-in-ksmbd_vfs_k.patch
+ksmbd-change-the-return-value-of-ksmbd_vfs_query_max.patch
+ksmbd-use-kvzalloc-instead-of-kvmalloc.patch
+ksmbd-replace-the-ternary-conditional-operator-with-.patch
+ksmbd-use-struct_size-helper-in-ksmbd_negotiate_smb_.patch
+ksmbd-replace-one-element-array-with-flexible-array-.patch
+ksmbd-fix-unsigned-expression-compared-with-zero.patch
+ksmbd-check-if-a-mount-point-is-crossed-during-path-.patch
+ksmbd-switch-to-use-kmemdup_nul-helper.patch
+ksmbd-add-support-for-read-compound.patch
+ksmbd-fix-wrong-interim-response-on-compound.patch
+ksmbd-fix-force-create-mode-and-force-directory-mode.patch
+ksmbd-fix-one-kernel-doc-comment.patch
+ksmbd-add-missing-calling-smb2_set_err_rsp-on-error.patch
+ksmbd-remove-experimental-warning.patch
+ksmbd-remove-unneeded-mark_inode_dirty-in-set_info_s.patch
+ksmbd-fix-passing-freed-memory-aux_payload_buf.patch
+ksmbd-return-invalid-parameter-error-response-if-smb.patch
+ksmbd-check-iov-vector-index-in-ksmbd_conn_write.patch
+ksmbd-fix-race-condition-with-fp.patch
+ksmbd-fix-race-condition-from-parallel-smb2-logoff-r.patch
+ksmbd-fix-race-condition-from-parallel-smb2-lock-req.patch
+ksmbd-fix-race-condition-between-tree-conn-lookup-an.patch
+ksmbd-fix-wrong-error-response-status-by-using-set_s.patch
+ksmbd-fix-null-pointer-dereferences-in-ksmbd_update_.patch
+ksmbd-fix-potential-double-free-on-smb2_read_pipe-er.patch
+ksmbd-remove-unused-field-in-ksmbd_user-struct.patch
+ksmbd-reorganize-ksmbd_iov_pin_rsp.patch
+ksmbd-fix-kernel-doc-comment-of-ksmbd_vfs_setxattr.patch
+ksmbd-fix-recursive-locking-in-vfs-helpers.patch
+ksmbd-fix-missing-rdma-capable-flag-for-ipoib-device.patch
+ksmbd-add-support-for-surrogate-pair-conversion.patch
+ksmbd-no-need-to-wait-for-binded-connection-terminat.patch
+ksmbd-fix-kernel-doc-comment-of-ksmbd_vfs_kern_path_.patch
+ksmbd-prevent-memory-leak-on-error-return.patch
+ksmbd-fix-possible-deadlock-in-smb2_open.patch
+ksmbd-separately-allocate-ci-per-dentry.patch
+ksmbd-move-oplock-handling-after-unlock-parent-dir.patch
+ksmbd-release-interim-response-after-sending-status-.patch
+ksmbd-move-setting-smb2_flags_async_command-and-asyn.patch
+ksmbd-don-t-update-op_state-as-oplock_state_none-on-.patch
+ksmbd-set-epoch-in-create-context-v2-lease.patch
+ksmbd-set-v2-lease-capability.patch
+ksmbd-downgrade-rwh-lease-caching-state-to-rh-for-di.patch
+ksmbd-send-v2-lease-break-notification-for-directory.patch
+ksmbd-lazy-v2-lease-break-on-smb2_write.patch
+ksmbd-avoid-duplicate-opinfo_put-call-on-error-of-sm.patch
+ksmbd-fix-wrong-allocation-size-update-in-smb2_open.patch
+arm-dts-fix-occasional-boot-hang-for-am3-usb.patch
+usb-fotg210-hcd-delete-an-incorrect-bounds-test.patch
+spi-introduce-spi_get_device_match_data-helper.patch
+iio-imu-adis16475-add-spi_device_id-table.patch
+nfsd-separate-nfsd_last_thread-from-nfsd_put.patch
+nfsd-call-nfsd_last_thread-before-final-nfsd_put.patch
+linux-export-ensure-natural-alignment-of-kcrctab-arr.patch
+spi-reintroduce-spi_set_cs_timing.patch
+spi-add-apis-in-spi-core-to-set-get-spi-chip_select-.patch
+spi-atmel-fix-clock-issue-when-using-devices-with-di.patch
+block-renumber-queue_flag_hw_wc.patch
diff --git a/queue-6.1/spi-add-apis-in-spi-core-to-set-get-spi-chip_select-.patch b/queue-6.1/spi-add-apis-in-spi-core-to-set-get-spi-chip_select-.patch
new file mode 100644 (file)
index 0000000..2dc5d70
--- /dev/null
@@ -0,0 +1,236 @@
+From 394837184fdb35417c97e669b04b1d4c96988f37 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 20 Jan 2023 00:23:30 +0530
+Subject: spi: Add APIs in spi core to set/get spi->chip_select and
+ spi->cs_gpiod
+
+From: Amit Kumar Mahapatra <amit.kumar-mahapatra@amd.com>
+
+[ Upstream commit 303feb3cc06ac0665d0ee9c1414941200e60e8a3 ]
+
+Supporting multi-cs in spi core and spi controller drivers would require
+the chip_select & cs_gpiod members of struct spi_device to be an array.
+But changing the type of these members to array would break the spi driver
+functionality. To make the transition smoother introduced four new APIs to
+get/set the spi->chip_select & spi->cs_gpiod and replaced all
+spi->chip_select and spi->cs_gpiod references in spi core with the API
+calls.
+While adding multi-cs support in further patches the chip_select & cs_gpiod
+members of the spi_device structure would be converted to arrays & the
+"idx" parameter of the APIs would be used as array index i.e.,
+spi->chip_select[idx] & spi->cs_gpiod[idx] respectively.
+
+Suggested-by: Lars-Peter Clausen <lars@metafoo.de>
+Signed-off-by: Amit Kumar Mahapatra <amit.kumar-mahapatra@amd.com>
+Reviewed-by: Michal Simek <michal.simek@amd.com>
+Link: https://lore.kernel.org/r/20230119185342.2093323-2-amit.kumar-mahapatra@amd.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Stable-dep-of: fc70d643a2f6 ("spi: atmel: Fix clock issue when using devices with different polarities")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/spi/spi.c       | 45 ++++++++++++++++++++---------------------
+ include/linux/spi/spi.h | 20 ++++++++++++++++++
+ 2 files changed, 42 insertions(+), 23 deletions(-)
+
+diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
+index f1ed2863a183e..22d227878bc44 100644
+--- a/drivers/spi/spi.c
++++ b/drivers/spi/spi.c
+@@ -604,7 +604,7 @@ static void spi_dev_set_name(struct spi_device *spi)
+       }
+       dev_set_name(&spi->dev, "%s.%u", dev_name(&spi->controller->dev),
+-                   spi->chip_select);
++                   spi_get_chipselect(spi, 0));
+ }
+ static int spi_dev_check(struct device *dev, void *data)
+@@ -613,7 +613,7 @@ static int spi_dev_check(struct device *dev, void *data)
+       struct spi_device *new_spi = data;
+       if (spi->controller == new_spi->controller &&
+-          spi->chip_select == new_spi->chip_select)
++          spi_get_chipselect(spi, 0) == spi_get_chipselect(new_spi, 0))
+               return -EBUSY;
+       return 0;
+ }
+@@ -638,7 +638,7 @@ static int __spi_add_device(struct spi_device *spi)
+       status = bus_for_each_dev(&spi_bus_type, NULL, spi, spi_dev_check);
+       if (status) {
+               dev_err(dev, "chipselect %d already in use\n",
+-                              spi->chip_select);
++                              spi_get_chipselect(spi, 0));
+               return status;
+       }
+@@ -649,7 +649,7 @@ static int __spi_add_device(struct spi_device *spi)
+       }
+       if (ctlr->cs_gpiods)
+-              spi->cs_gpiod = ctlr->cs_gpiods[spi->chip_select];
++              spi_set_csgpiod(spi, 0, ctlr->cs_gpiods[spi_get_chipselect(spi, 0)]);
+       /*
+        * Drivers may modify this initial i/o setup, but will
+@@ -692,8 +692,8 @@ int spi_add_device(struct spi_device *spi)
+       int status;
+       /* Chipselects are numbered 0..max; validate. */
+-      if (spi->chip_select >= ctlr->num_chipselect) {
+-              dev_err(dev, "cs%d >= max %d\n", spi->chip_select,
++      if (spi_get_chipselect(spi, 0) >= ctlr->num_chipselect) {
++              dev_err(dev, "cs%d >= max %d\n", spi_get_chipselect(spi, 0),
+                       ctlr->num_chipselect);
+               return -EINVAL;
+       }
+@@ -714,8 +714,8 @@ static int spi_add_device_locked(struct spi_device *spi)
+       struct device *dev = ctlr->dev.parent;
+       /* Chipselects are numbered 0..max; validate. */
+-      if (spi->chip_select >= ctlr->num_chipselect) {
+-              dev_err(dev, "cs%d >= max %d\n", spi->chip_select,
++      if (spi_get_chipselect(spi, 0) >= ctlr->num_chipselect) {
++              dev_err(dev, "cs%d >= max %d\n", spi_get_chipselect(spi, 0),
+                       ctlr->num_chipselect);
+               return -EINVAL;
+       }
+@@ -761,7 +761,7 @@ struct spi_device *spi_new_device(struct spi_controller *ctlr,
+       WARN_ON(strlen(chip->modalias) >= sizeof(proxy->modalias));
+-      proxy->chip_select = chip->chip_select;
++      spi_set_chipselect(proxy, 0, chip->chip_select);
+       proxy->max_speed_hz = chip->max_speed_hz;
+       proxy->mode = chip->mode;
+       proxy->irq = chip->irq;
+@@ -970,24 +970,23 @@ static void spi_set_cs(struct spi_device *spi, bool enable, bool force)
+        * Avoid calling into the driver (or doing delays) if the chip select
+        * isn't actually changing from the last time this was called.
+        */
+-      if (!force && ((enable && spi->controller->last_cs == spi->chip_select) ||
+-                              (!enable && spi->controller->last_cs != spi->chip_select)) &&
++      if (!force && ((enable && spi->controller->last_cs == spi_get_chipselect(spi, 0)) ||
++                     (!enable && spi->controller->last_cs != spi_get_chipselect(spi, 0))) &&
+           (spi->controller->last_cs_mode_high == (spi->mode & SPI_CS_HIGH)))
+               return;
+       trace_spi_set_cs(spi, activate);
+-      spi->controller->last_cs = enable ? spi->chip_select : -1;
++      spi->controller->last_cs = enable ? spi_get_chipselect(spi, 0) : -1;
+       spi->controller->last_cs_mode_high = spi->mode & SPI_CS_HIGH;
+-      if ((spi->cs_gpiod || !spi->controller->set_cs_timing) && !activate) {
++      if ((spi_get_csgpiod(spi, 0) || !spi->controller->set_cs_timing) && !activate)
+               spi_delay_exec(&spi->cs_hold, NULL);
+-      }
+       if (spi->mode & SPI_CS_HIGH)
+               enable = !enable;
+-      if (spi->cs_gpiod) {
++      if (spi_get_csgpiod(spi, 0)) {
+               if (!(spi->mode & SPI_NO_CS)) {
+                       /*
+                        * Historically ACPI has no means of the GPIO polarity and
+@@ -1000,10 +999,10 @@ static void spi_set_cs(struct spi_device *spi, bool enable, bool force)
+                        * into account.
+                        */
+                       if (has_acpi_companion(&spi->dev))
+-                              gpiod_set_value_cansleep(spi->cs_gpiod, !enable);
++                              gpiod_set_value_cansleep(spi_get_csgpiod(spi, 0), !enable);
+                       else
+                               /* Polarity handled by GPIO library */
+-                              gpiod_set_value_cansleep(spi->cs_gpiod, activate);
++                              gpiod_set_value_cansleep(spi_get_csgpiod(spi, 0), activate);
+               }
+               /* Some SPI masters need both GPIO CS & slave_select */
+               if ((spi->controller->flags & SPI_MASTER_GPIO_SS) &&
+@@ -1013,7 +1012,7 @@ static void spi_set_cs(struct spi_device *spi, bool enable, bool force)
+               spi->controller->set_cs(spi, !enable);
+       }
+-      if (spi->cs_gpiod || !spi->controller->set_cs_timing) {
++      if (spi_get_csgpiod(spi, 0) || !spi->controller->set_cs_timing) {
+               if (activate)
+                       spi_delay_exec(&spi->cs_setup, NULL);
+               else
+@@ -2303,7 +2302,7 @@ static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi,
+                       nc, rc);
+               return rc;
+       }
+-      spi->chip_select = value;
++      spi_set_chipselect(spi, 0, value);
+       /* Device speed */
+       if (!of_property_read_u32(nc, "spi-max-frequency", &value))
+@@ -2417,7 +2416,7 @@ struct spi_device *spi_new_ancillary_device(struct spi_device *spi,
+       strscpy(ancillary->modalias, "dummy", sizeof(ancillary->modalias));
+       /* Use provided chip-select for ancillary device */
+-      ancillary->chip_select = chip_select;
++      spi_set_chipselect(ancillary, 0, chip_select);
+       /* Take over SPI mode/speed from SPI main device */
+       ancillary->max_speed_hz = spi->max_speed_hz;
+@@ -2664,7 +2663,7 @@ struct spi_device *acpi_spi_device_alloc(struct spi_controller *ctlr,
+       spi->mode               |= lookup.mode;
+       spi->irq                = lookup.irq;
+       spi->bits_per_word      = lookup.bits_per_word;
+-      spi->chip_select        = lookup.chip_select;
++      spi_set_chipselect(spi, 0, lookup.chip_select);
+       return spi;
+ }
+@@ -3634,7 +3633,7 @@ static int spi_set_cs_timing(struct spi_device *spi)
+       struct device *parent = spi->controller->dev.parent;
+       int status = 0;
+-      if (spi->controller->set_cs_timing && !spi->cs_gpiod) {
++      if (spi->controller->set_cs_timing && !spi_get_csgpiod(spi, 0)) {
+               if (spi->controller->auto_runtime_pm) {
+                       status = pm_runtime_get_sync(parent);
+                       if (status < 0) {
+@@ -3839,7 +3838,7 @@ static int __spi_validate(struct spi_device *spi, struct spi_message *message)
+        * cs_change is set for each transfer.
+        */
+       if ((spi->mode & SPI_CS_WORD) && (!(ctlr->mode_bits & SPI_CS_WORD) ||
+-                                        spi->cs_gpiod)) {
++                                        spi_get_csgpiod(spi, 0))) {
+               size_t maxsize;
+               int ret;
+diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
+index 635a05c30283c..a87afac9742cd 100644
+--- a/include/linux/spi/spi.h
++++ b/include/linux/spi/spi.h
+@@ -263,6 +263,26 @@ static inline void *spi_get_drvdata(struct spi_device *spi)
+       return dev_get_drvdata(&spi->dev);
+ }
++static inline u8 spi_get_chipselect(struct spi_device *spi, u8 idx)
++{
++      return spi->chip_select;
++}
++
++static inline void spi_set_chipselect(struct spi_device *spi, u8 idx, u8 chipselect)
++{
++      spi->chip_select = chipselect;
++}
++
++static inline struct gpio_desc *spi_get_csgpiod(struct spi_device *spi, u8 idx)
++{
++      return spi->cs_gpiod;
++}
++
++static inline void spi_set_csgpiod(struct spi_device *spi, u8 idx, struct gpio_desc *csgpiod)
++{
++      spi->cs_gpiod = csgpiod;
++}
++
+ struct spi_message;
+ /**
+-- 
+2.43.0
+
diff --git a/queue-6.1/spi-atmel-fix-clock-issue-when-using-devices-with-di.patch b/queue-6.1/spi-atmel-fix-clock-issue-when-using-devices-with-di.patch
new file mode 100644 (file)
index 0000000..7ddf931
--- /dev/null
@@ -0,0 +1,247 @@
+From 7f454679fa857e2f9b8c680fd804cf24aa5c1f2f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 4 Dec 2023 16:49:03 +0100
+Subject: spi: atmel: Fix clock issue when using devices with different
+ polarities
+
+From: Louis Chauvet <louis.chauvet@bootlin.com>
+
+[ Upstream commit fc70d643a2f6678cbe0f5c86433c1aeb4d613fcc ]
+
+The current Atmel SPI controller driver (v2) behaves incorrectly when
+using two SPI devices with different clock polarities and GPIO CS.
+
+When switching from one device to another, the controller driver first
+enables the CS and then applies whatever configuration suits the targeted
+device (typically, the polarities). The side effect of such order is the
+apparition of a spurious clock edge after enabling the CS when the clock
+polarity needs to be inverted wrt. the previous configuration of the
+controller.
+
+This parasitic clock edge is problematic when the SPI device uses that edge
+for internal processing, which is perfectly legitimate given that its CS
+was asserted. Indeed, devices such as HVS8080 driven by driver gpio-sr in
+the kernel are shift registers and will process this first clock edge to
+perform a first register shift. In this case, the first bit gets lost and
+the whole data block that will later be read by the kernel is all shifted
+by one.
+
+    Current behavior:
+      The actual switching of the clock polarity only occurs after the CS
+      when the controller sends the first message:
+
+    CLK ------------\   /-\ /-\
+                    |   | | | |    . . .
+                    \---/ \-/ \
+    CS  -----\
+             |
+             \------------------
+
+             ^      ^   ^
+             |      |   |
+             |      |   Actual clock of the message sent
+             |      |
+             |      Change of clock polarity, which occurs with the first
+             |      write to the bus. This edge occurs when the CS is
+             |      already asserted, and can be interpreted as
+             |      the first clock edge by the receiver.
+             |
+             GPIO CS toggle
+
+This issue is specific to this controller because while the SPI core
+performs the operations in the right order, the controller however does
+not. In practice, the controller only applies the clock configuration right
+before the first transmission.
+
+So this is not a problem when using the controller's dedicated CS, as the
+controller does things correctly, but it becomes a problem when you need to
+change the clock polarity and use an external GPIO for the CS.
+
+One possible approach to solve this problem is to send a dummy message
+before actually activating the CS, so that the controller applies the clock
+polarity beforehand.
+
+New behavior:
+
+CLK     ------\      /-\     /-\      /-\     /-\
+              |      | | ... | |      | | ... | |
+              \------/ \-   -/ \------/ \-   -/ \------
+
+CS      -\/-----------------------\
+         ||                       |
+         \/                       \---------------------
+         ^    ^       ^           ^    ^
+         |    |       |           |    |
+         |    |       |           |    Expected clock cycles when
+         |    |       |           |    sending the message
+         |    |       |           |
+         |    |       |           Actual GPIO CS activation, occurs inside
+         |    |       |           the driver
+         |    |       |
+         |    |       Dummy message, to trigger clock polarity
+         |    |       reconfiguration. This message is not received and
+         |    |       processed by the device because CS is low.
+         |    |
+         |    Change of clock polarity, forced by the dummy message. This
+         |    time, the edge is not detected by the receiver.
+         |
+         This small spike in CS activation is due to the fact that the
+         spi-core activates the CS gpio before calling the driver's
+         set_cs callback, which deactivates this gpio again until the
+         clock polarity is correct.
+
+To avoid having to systematically send a dummy packet, the driver keeps
+track of the clock's current polarity. In this way, it only sends the dummy
+packet when necessary, ensuring that the clock will have the correct
+polarity when the CS is toggled.
+
+There could be two hardware problems with this patch:
+1- Maybe the small CS activation peak can confuse SPI devices
+2- If on a design, a single wire is used to select two devices depending
+on its state, the dummy message may disturb them.
+
+Fixes: 5ee36c989831 ("spi: atmel_spi update chipselect handling")
+Cc:  <stable@vger.kernel.org>
+Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
+Link: https://msgid.link/r/20231204154903.11607-1-louis.chauvet@bootlin.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/spi/spi-atmel.c | 82 ++++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 81 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c
+index c4f22d50dba58..78daf2b2143c5 100644
+--- a/drivers/spi/spi-atmel.c
++++ b/drivers/spi/spi-atmel.c
+@@ -22,6 +22,7 @@
+ #include <linux/gpio/consumer.h>
+ #include <linux/pinctrl/consumer.h>
+ #include <linux/pm_runtime.h>
++#include <linux/iopoll.h>
+ #include <trace/events/spi.h>
+ /* SPI register offsets */
+@@ -278,6 +279,7 @@ struct atmel_spi {
+       bool                    keep_cs;
+       u32                     fifo_size;
++      bool                    last_polarity;
+       u8                      native_cs_free;
+       u8                      native_cs_for_gpio;
+ };
+@@ -290,6 +292,22 @@ struct atmel_spi_device {
+ #define SPI_MAX_DMA_XFER      65535 /* true for both PDC and DMA */
+ #define INVALID_DMA_ADDRESS   0xffffffff
++/*
++ * This frequency can be anything supported by the controller, but to avoid
++ * unnecessary delay, the highest possible frequency is chosen.
++ *
++ * This frequency is the highest possible which is not interfering with other
++ * chip select registers (see Note for Serial Clock Bit Rate configuration in
++ * Atmel-11121F-ATARM-SAMA5D3-Series-Datasheet_02-Feb-16, page 1283)
++ */
++#define DUMMY_MSG_FREQUENCY   0x02
++/*
++ * 8 bits is the minimum data the controller is capable of sending.
++ *
++ * This message can be anything as it should not be treated by any SPI device.
++ */
++#define DUMMY_MSG             0xAA
++
+ /*
+  * Version 2 of the SPI controller has
+  *  - CR.LASTXFER
+@@ -303,6 +321,43 @@ static bool atmel_spi_is_v2(struct atmel_spi *as)
+       return as->caps.is_spi2;
+ }
++/*
++ * Send a dummy message.
++ *
++ * This is sometimes needed when using a CS GPIO to force clock transition when
++ * switching between devices with different polarities.
++ */
++static void atmel_spi_send_dummy(struct atmel_spi *as, struct spi_device *spi, int chip_select)
++{
++      u32 status;
++      u32 csr;
++
++      /*
++       * Set a clock frequency to allow sending message on SPI bus.
++       * The frequency here can be anything, but is needed for
++       * the controller to send the data.
++       */
++      csr = spi_readl(as, CSR0 + 4 * chip_select);
++      csr = SPI_BFINS(SCBR, DUMMY_MSG_FREQUENCY, csr);
++      spi_writel(as, CSR0 + 4 * chip_select, csr);
++
++      /*
++       * Read all data coming from SPI bus, needed to be able to send
++       * the message.
++       */
++      spi_readl(as, RDR);
++      while (spi_readl(as, SR) & SPI_BIT(RDRF)) {
++              spi_readl(as, RDR);
++              cpu_relax();
++      }
++
++      spi_writel(as, TDR, DUMMY_MSG);
++
++      readl_poll_timeout_atomic(as->regs + SPI_SR, status,
++                                (status & SPI_BIT(TXEMPTY)), 1, 1000);
++}
++
++
+ /*
+  * Earlier SPI controllers (e.g. on at91rm9200) have a design bug whereby
+  * they assume that spi slave device state will not change on deselect, so
+@@ -319,11 +374,17 @@ static bool atmel_spi_is_v2(struct atmel_spi *as)
+  * Master on Chip Select 0.")  No workaround exists for that ... so for
+  * nCS0 on that chip, we (a) don't use the GPIO, (b) can't support CS_HIGH,
+  * and (c) will trigger that first erratum in some cases.
++ *
++ * When changing the clock polarity, the SPI controller waits for the next
++ * transmission to enforce the default clock state. This may be an issue when
++ * using a GPIO as Chip Select: the clock level is applied only when the first
++ * packet is sent, once the CS has already been asserted. The workaround is to
++ * avoid this by sending a first (dummy) message before toggling the CS state.
+  */
+-
+ static void cs_activate(struct atmel_spi *as, struct spi_device *spi)
+ {
+       struct atmel_spi_device *asd = spi->controller_state;
++      bool new_polarity;
+       int chip_select;
+       u32 mr;
+@@ -352,6 +413,25 @@ static void cs_activate(struct atmel_spi *as, struct spi_device *spi)
+               }
+               mr = spi_readl(as, MR);
++
++              /*
++               * Ensures the clock polarity is valid before we actually
++               * assert the CS to avoid spurious clock edges to be
++               * processed by the spi devices.
++               */
++              if (spi_get_csgpiod(spi, 0)) {
++                      new_polarity = (asd->csr & SPI_BIT(CPOL)) != 0;
++                      if (new_polarity != as->last_polarity) {
++                              /*
++                               * Need to disable the GPIO before sending the dummy
++                               * message because it is already set by the spi core.
++                               */
++                              gpiod_set_value_cansleep(spi_get_csgpiod(spi, 0), 0);
++                              atmel_spi_send_dummy(as, spi, chip_select);
++                              as->last_polarity = new_polarity;
++                              gpiod_set_value_cansleep(spi_get_csgpiod(spi, 0), 1);
++                      }
++              }
+       } else {
+               u32 cpol = (spi->mode & SPI_CPOL) ? SPI_BIT(CPOL) : 0;
+               int i;
+-- 
+2.43.0
+
diff --git a/queue-6.1/spi-introduce-spi_get_device_match_data-helper.patch b/queue-6.1/spi-introduce-spi_get_device_match_data-helper.patch
new file mode 100644 (file)
index 0000000..fa95714
--- /dev/null
@@ -0,0 +1,65 @@
+From 8611fe53a8a4ed513bfe83195bed931835cbebae Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 20 Oct 2022 22:54:21 +0300
+Subject: spi: Introduce spi_get_device_match_data() helper
+
+From: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+
+[ Upstream commit aea672d054a21782ed8450c75febb6ba3c208ca4 ]
+
+The proposed spi_get_device_match_data() helper is for retrieving
+a driver data associated with the ID in an ID table. First, it tries
+to get driver data of the device enumerated by firmware interface
+(usually Device Tree or ACPI). If none is found it falls back to
+the SPI ID table matching.
+
+Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+Link: https://lore.kernel.org/r/20221020195421.10482-1-andriy.shevchenko@linux.intel.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Stable-dep-of: ee4d79055aee ("iio: imu: adis16475: add spi_device_id table")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/spi/spi.c       | 12 ++++++++++++
+ include/linux/spi/spi.h |  3 +++
+ 2 files changed, 15 insertions(+)
+
+diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
+index 5d046be8b2dd5..dfce0f7d4c640 100644
+--- a/drivers/spi/spi.c
++++ b/drivers/spi/spi.c
+@@ -360,6 +360,18 @@ const struct spi_device_id *spi_get_device_id(const struct spi_device *sdev)
+ }
+ EXPORT_SYMBOL_GPL(spi_get_device_id);
++const void *spi_get_device_match_data(const struct spi_device *sdev)
++{
++      const void *match;
++
++      match = device_get_match_data(&sdev->dev);
++      if (match)
++              return match;
++
++      return (const void *)spi_get_device_id(sdev)->driver_data;
++}
++EXPORT_SYMBOL_GPL(spi_get_device_match_data);
++
+ static int spi_match_device(struct device *dev, struct device_driver *drv)
+ {
+       const struct spi_device *spi = to_spi_device(dev);
+diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
+index 877395e075afe..635a05c30283c 100644
+--- a/include/linux/spi/spi.h
++++ b/include/linux/spi/spi.h
+@@ -1515,6 +1515,9 @@ extern void spi_unregister_device(struct spi_device *spi);
+ extern const struct spi_device_id *
+ spi_get_device_id(const struct spi_device *sdev);
++extern const void *
++spi_get_device_match_data(const struct spi_device *sdev);
++
+ static inline bool
+ spi_transfer_is_last(struct spi_controller *ctlr, struct spi_transfer *xfer)
+ {
+-- 
+2.43.0
+
diff --git a/queue-6.1/spi-reintroduce-spi_set_cs_timing.patch b/queue-6.1/spi-reintroduce-spi_set_cs_timing.patch
new file mode 100644 (file)
index 0000000..708c519
--- /dev/null
@@ -0,0 +1,105 @@
+From 3009ab5427304d3514cdb9c6b147c5dbdb158c47 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 17 Nov 2022 12:52:44 +0200
+Subject: spi: Reintroduce spi_set_cs_timing()
+
+From: Tudor Ambarus <tudor.ambarus@microchip.com>
+
+[ Upstream commit 684a47847ae639689e7b823251975348a8e5434f ]
+
+commit 4ccf359849ce ("spi: remove spi_set_cs_timing()"), removed the
+method as noboby used it. Nobody used it probably because some SPI
+controllers use some default large cs-setup time that covers the usual
+cs-setup time required by the spi devices. There are though SPI controllers
+that have a smaller granularity for the cs-setup time and their default
+value can't fulfill the spi device requirements. That's the case for the
+at91 QSPI IPs where the default cs-setup time is half of the QSPI clock
+period. This was observed when using an sst26vf064b SPI NOR flash which
+needs a spi-cs-setup-ns = <7>; in order to be operated close to its maximum
+104 MHz frequency.
+
+Call spi_set_cs_timing() in spi_setup() just before calling spi_set_cs(),
+as the latter needs the CS timings already set.
+If spi->controller->set_cs_timing is not set, the method will return 0.
+There's no functional impact expected for the existing drivers. Even if the
+spi-mt65xx.c and spi-tegra114.c drivers set the set_cs_timing method,
+there's no user for them as of now. The only tested user of this support
+will be a SPI NOR flash that comunicates with the Atmel QSPI controller for
+which the support follows in the next patches.
+
+One will notice that this support is a bit different from the one that was
+removed in commit 4ccf359849ce ("spi: remove spi_set_cs_timing()"),
+because this patch adapts to the changes done after the removal: the move
+of the cs delays to the spi device, the retirement of the lelgacy GPIO
+handling. The mutex handling was removed from spi_set_cs_timing() because
+we now always call spi_set_cs_timing() in spi_setup(), which already
+handles the spi->controller->io_mutex, so use the mutex handling from
+spi_setup().
+
+Signed-off-by: Tudor Ambarus <tudor.ambarus@microchip.com>
+Link: https://lore.kernel.org/r/20221117105249.115649-4-tudor.ambarus@microchip.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Stable-dep-of: fc70d643a2f6 ("spi: atmel: Fix clock issue when using devices with different polarities")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/spi/spi.c | 37 +++++++++++++++++++++++++++++++++++++
+ 1 file changed, 37 insertions(+)
+
+diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
+index dfce0f7d4c640..f1ed2863a183e 100644
+--- a/drivers/spi/spi.c
++++ b/drivers/spi/spi.c
+@@ -3623,6 +3623,37 @@ static int __spi_validate_bits_per_word(struct spi_controller *ctlr,
+       return 0;
+ }
++/**
++ * spi_set_cs_timing - configure CS setup, hold, and inactive delays
++ * @spi: the device that requires specific CS timing configuration
++ *
++ * Return: zero on success, else a negative error code.
++ */
++static int spi_set_cs_timing(struct spi_device *spi)
++{
++      struct device *parent = spi->controller->dev.parent;
++      int status = 0;
++
++      if (spi->controller->set_cs_timing && !spi->cs_gpiod) {
++              if (spi->controller->auto_runtime_pm) {
++                      status = pm_runtime_get_sync(parent);
++                      if (status < 0) {
++                              pm_runtime_put_noidle(parent);
++                              dev_err(&spi->controller->dev, "Failed to power device: %d\n",
++                                      status);
++                              return status;
++                      }
++
++                      status = spi->controller->set_cs_timing(spi);
++                      pm_runtime_mark_last_busy(parent);
++                      pm_runtime_put_autosuspend(parent);
++              } else {
++                      status = spi->controller->set_cs_timing(spi);
++              }
++      }
++      return status;
++}
++
+ /**
+  * spi_setup - setup SPI mode and clock rate
+  * @spi: the device whose settings are being modified
+@@ -3719,6 +3750,12 @@ int spi_setup(struct spi_device *spi)
+               }
+       }
++      status = spi_set_cs_timing(spi);
++      if (status) {
++              mutex_unlock(&spi->controller->io_mutex);
++              return status;
++      }
++
+       if (spi->controller->auto_runtime_pm && spi->controller->set_cs) {
+               status = pm_runtime_resume_and_get(spi->controller->dev.parent);
+               if (status < 0) {
+-- 
+2.43.0
+
diff --git a/queue-6.1/usb-fotg210-hcd-delete-an-incorrect-bounds-test.patch b/queue-6.1/usb-fotg210-hcd-delete-an-incorrect-bounds-test.patch
new file mode 100644 (file)
index 0000000..7c70bb8
--- /dev/null
@@ -0,0 +1,63 @@
+From 88318aeb5673f00c0c7be6c91186ab9b539f69dd Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 13 Dec 2023 16:22:43 +0300
+Subject: usb: fotg210-hcd: delete an incorrect bounds test
+
+From: Dan Carpenter <dan.carpenter@linaro.org>
+
+[ Upstream commit 7fbcd195e2b8cc952e4aeaeb50867b798040314c ]
+
+Here "temp" is the number of characters that we have written and "size"
+is the size of the buffer.  The intent was clearly to say that if we have
+written to the end of the buffer then stop.
+
+However, for that to work the comparison should have been done on the
+original "size" value instead of the "size -= temp" value.  Not only
+will that not trigger when we want to, but there is a small chance that
+it will trigger incorrectly before we want it to and we break from the
+loop slightly earlier than intended.
+
+This code was recently changed from using snprintf() to scnprintf().  With
+snprintf() we likely would have continued looping and passed a negative
+size parameter to snprintf().  This would have triggered an annoying
+WARN().  Now that we have converted to scnprintf() "size" will never
+drop below 1 and there is no real need for this test.  We could change
+the condition to "if (temp <= 1) goto done;" but just deleting the test
+is cleanest.
+
+Fixes: 7d50195f6c50 ("usb: host: Faraday fotg210-hcd driver")
+Cc: stable <stable@kernel.org>
+Signed-off-by: Dan Carpenter <dan.carpenter@linaro.org>
+Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
+Reviewed-by: Lee Jones <lee@kernel.org>
+Link: https://lore.kernel.org/r/ZXmwIwHe35wGfgzu@suswa
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/usb/host/fotg210-hcd.c | 3 ---
+ 1 file changed, 3 deletions(-)
+
+diff --git a/drivers/usb/host/fotg210-hcd.c b/drivers/usb/host/fotg210-hcd.c
+index c4c1fbc12b4cd..dc968960769e1 100644
+--- a/drivers/usb/host/fotg210-hcd.c
++++ b/drivers/usb/host/fotg210-hcd.c
+@@ -429,8 +429,6 @@ static void qh_lines(struct fotg210_hcd *fotg210, struct fotg210_qh *qh,
+                       temp = size;
+               size -= temp;
+               next += temp;
+-              if (temp == size)
+-                      goto done;
+       }
+       temp = snprintf(next, size, "\n");
+@@ -440,7 +438,6 @@ static void qh_lines(struct fotg210_hcd *fotg210, struct fotg210_qh *qh,
+       size -= temp;
+       next += temp;
+-done:
+       *sizep = size;
+       *nextp = next;
+ }
+-- 
+2.43.0
+