aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--queue-6.6/add-definition-for-new-smb3.1.1-command-type.patch62
-rw-r--r--queue-6.6/cifs-add-client-version-details-to-ntlm-authenticate.patch67
-rw-r--r--queue-6.6/cifs-add-tracing-for-the-cifs_tcon-struct-refcountin.patch516
-rw-r--r--queue-6.6/cifs-commands-that-are-retried-should-have-replay-fl.patch1105
-rw-r--r--queue-6.6/cifs-defer-close-file-handles-having-rh-lease.patch60
-rw-r--r--queue-6.6/cifs-fix-in-logging-in-cifs_chan_update_iface.patch59
-rw-r--r--queue-6.6/cifs-fix-use-after-free-for-iface-while-disabling-se.patch41
-rw-r--r--queue-6.6/cifs-fixes-for-get_inode_info.patch118
-rw-r--r--queue-6.6/cifs-get-rid-of-dup-length-check-in-parse_reparse_po.patch86
-rw-r--r--queue-6.6/cifs-minor-comment-cleanup.patch39
-rw-r--r--queue-6.6/cifs-move-some-extern-decls-from-.c-files-to-.h.patch88
-rw-r--r--queue-6.6/cifs-new-mount-option-called-retrans.patch129
-rw-r--r--queue-6.6/cifs-new-nt-status-codes-from-ms-smb2.patch53
-rw-r--r--queue-6.6/cifs-pass-unbyteswapped-eof-value-into-smb2_set_eof.patch193
-rw-r--r--queue-6.6/cifs-pick-channel-for-tcon-and-tdis.patch71
-rw-r--r--queue-6.6/cifs-print-server-capabilities-in-debugdata.patch36
-rw-r--r--queue-6.6/cifs-remove-redundant-variable-assignment.patch43
-rw-r--r--queue-6.6/cifs-remove-redundant-variable-tcon_exist.patch58
-rw-r--r--queue-6.6/cifs-remove-unneeded-return-statement.patch34
-rw-r--r--queue-6.6/cifs-set-replay-flag-for-retries-of-write-command.patch74
-rw-r--r--queue-6.6/cifs-update-internal-module-version-number-for-cifs..patch33
-rw-r--r--queue-6.6/cifs-update-internal-module-version-number-for-cifs..patch-886633
-rw-r--r--queue-6.6/cifs-update-internal-module-version-number-for-cifs..patch-905533
-rw-r--r--queue-6.6/cifs-update-the-same-create_guid-on-replay.patch131
-rw-r--r--queue-6.6/ksmbd-add-continuous-availability-share-parameter.patch99
-rw-r--r--queue-6.6/ksmbd-add-kernel-doc-for-ksmbd_extract_sharename-fun.patch37
-rw-r--r--queue-6.6/ksmbd-add-support-for-durable-handles-v1-v2.patch866
-rw-r--r--queue-6.6/ksmbd-auth-fix-most-kernel-doc-warnings.patch98
-rw-r--r--queue-6.6/ksmbd-fix-possible-null-deref-in-smb_lazy_parent_lea.patch35
-rw-r--r--queue-6.6/ksmbd-fix-potencial-out-of-bounds-when-buffer-offset.patch316
-rw-r--r--queue-6.6/ksmbd-fix-slab-out-of-bounds-in-smb_strndup_from_utf.patch44
-rw-r--r--queue-6.6/ksmbd-fix-spelling-mistake-connction-connection.patch35
-rw-r--r--queue-6.6/ksmbd-mark-smb2_session_expired-to-session-when-dest.patch124
-rw-r--r--queue-6.6/ksmbd-vfs-fix-all-kernel-doc-warnings.patch176
-rw-r--r--queue-6.6/missing-field-not-being-returned-in-ioctl-cifs_ioc_g.patch36
-rw-r--r--queue-6.6/series79
-rw-r--r--queue-6.6/smb-client-add-support-for-wsl-reparse-points.patch428
-rw-r--r--queue-6.6/smb-client-allow-creating-special-files-via-reparse-.patch616
-rw-r--r--queue-6.6/smb-client-allow-creating-symlinks-via-reparse-point.patch203
-rw-r--r--queue-6.6/smb-client-cleanup-smb2_query_reparse_point.patch231
-rw-r--r--queue-6.6/smb-client-delete-true-false-defines.patch41
-rw-r--r--queue-6.6/smb-client-do-not-defer-close-open-handles-to-delete.patch254
-rw-r--r--queue-6.6/smb-client-don-t-clobber-i_rdev-from-cached-reparse-.patch44
-rw-r--r--queue-6.6/smb-client-extend-smb2_compound_op-to-accept-more-co.patch1038
-rw-r--r--queue-6.6/smb-client-fix-a-null-vs-is_err-check-in-wsl_set_xat.patch37
-rw-r--r--queue-6.6/smb-client-fix-minor-whitespace-errors-and-warnings.patch143
-rw-r--r--queue-6.6/smb-client-fix-null-ptr-deref-in-cifs_mark_open_hand.patch107
-rw-r--r--queue-6.6/smb-client-fix-potential-broken-compound-request.patch200
-rw-r--r--queue-6.6/smb-client-get-rid-of-smb311_posix_query_path_info.patch199
-rw-r--r--queue-6.6/smb-client-handle-path-separator-of-created-smb-syml.patch58
-rw-r--r--queue-6.6/smb-client-handle-special-files-and-symlinks-in-smb3.patch148
-rw-r--r--queue-6.6/smb-client-instantiate-when-creating-sfu-files.patch210
-rw-r--r--queue-6.6/smb-client-introduce-cifs_sfu_make_node.patch283
-rw-r--r--queue-6.6/smb-client-introduce-reparse-mount-option.patch161
-rw-r--r--queue-6.6/smb-client-introduce-smb2_op_query_wsl_ea.patch384
-rw-r--r--queue-6.6/smb-client-move-most-of-reparse-point-handling-code-.patch924
-rw-r--r--queue-6.6/smb-client-negotiate-compression-algorithms.patch192
-rw-r--r--queue-6.6/smb-client-optimise-reparse-point-querying.patch371
-rw-r--r--queue-6.6/smb-client-parse-owner-group-when-creating-reparse-p.patch414
-rw-r--r--queue-6.6/smb-client-parse-uid-gid-mode-and-dev-from-wsl-repar.patch223
-rw-r--r--queue-6.6/smb-client-reduce-number-of-parameters-in-smb2_compo.patch367
-rw-r--r--queue-6.6/smb-client-retry-compound-request-without-reusing-le.patch117
-rw-r--r--queue-6.6/smb-client-return-reparse-type-in-proc-mounts.patch60
-rw-r--r--queue-6.6/smb-client-reuse-file-lease-key-in-compound-operatio.patch380
-rw-r--r--queue-6.6/smb-client-set-correct-d_type-for-reparse-dfs-dfsr-a.patch81
-rw-r--r--queue-6.6/smb-common-fix-fields-sizes-in-compression_pattern_p.patch36
-rw-r--r--queue-6.6/smb-common-simplify-compression-headers.patch97
-rw-r--r--queue-6.6/smb-fix-some-kernel-doc-comments.patch44
-rw-r--r--queue-6.6/smb-smb2pdu.h-avoid-wflex-array-member-not-at-end-wa.patch273
-rw-r--r--queue-6.6/smb-use-crypto_shash_digest-in-symlink_hash.patch53
-rw-r--r--queue-6.6/smb3-add-dynamic-trace-point-for-ioctls.patch89
-rw-r--r--queue-6.6/smb3-add-trace-event-for-mknod.patch86
-rw-r--r--queue-6.6/smb3-clarify-some-of-the-unused-createoption-flags.patch51
-rw-r--r--queue-6.6/smb3-improve-exception-handling-in-allocate_mr_list.patch48
-rw-r--r--queue-6.6/smb3-minor-cleanup-of-session-handling-code.patch106
-rw-r--r--queue-6.6/smb3-minor-rdma-cleanup.patch43
-rw-r--r--queue-6.6/smb3-more-minor-cleanups-for-session-handling-routin.patch71
-rw-r--r--queue-6.6/smb3-update-allocation-size-more-accurately-on-write.patch48
-rw-r--r--queue-6.6/smb311-additional-compression-flag-defined-in-update.patch56
-rw-r--r--queue-6.6/smb311-correct-incorrect-offset-field-in-compression.patch35
80 files changed, 14157 insertions, 0 deletions
diff --git a/queue-6.6/add-definition-for-new-smb3.1.1-command-type.patch b/queue-6.6/add-definition-for-new-smb3.1.1-command-type.patch
new file mode 100644
index 0000000000..bea45d76d2
--- /dev/null
+++ b/queue-6.6/add-definition-for-new-smb3.1.1-command-type.patch
@@ -0,0 +1,62 @@
+From e844eeb86be35af7041595f96bb403effdc7f3ca Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 8 Oct 2023 23:04:01 -0500
+Subject: Add definition for new smb3.1.1 command type
+
+From: Steve French <stfrench@microsoft.com>
+
+[ Upstream commit 7588b83066db9b9dc10c1a43b8e52a028ad327d2 ]
+
+Add structs and defines for new SMB3.1.1 command, server to client notification.
+
+See MS-SMB2 section 2.2.44
+
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/common/smb2pdu.h | 15 +++++++++++++++
+ 1 file changed, 15 insertions(+)
+
+diff --git a/fs/smb/common/smb2pdu.h b/fs/smb/common/smb2pdu.h
+index 465b721f2c06d..a233a24352b1f 100644
+--- a/fs/smb/common/smb2pdu.h
++++ b/fs/smb/common/smb2pdu.h
+@@ -34,6 +34,7 @@
+ #define SMB2_QUERY_INFO_HE 0x0010
+ #define SMB2_SET_INFO_HE 0x0011
+ #define SMB2_OPLOCK_BREAK_HE 0x0012
++#define SMB2_SERVER_TO_CLIENT_NOTIFICATION 0x0013
+
+ /* The same list in little endian */
+ #define SMB2_NEGOTIATE cpu_to_le16(SMB2_NEGOTIATE_HE)
+@@ -411,6 +412,7 @@ struct smb2_tree_disconnect_rsp {
+ #define SMB2_GLOBAL_CAP_PERSISTENT_HANDLES 0x00000010 /* New to SMB3 */
+ #define SMB2_GLOBAL_CAP_DIRECTORY_LEASING 0x00000020 /* New to SMB3 */
+ #define SMB2_GLOBAL_CAP_ENCRYPTION 0x00000040 /* New to SMB3 */
++#define SMB2_GLOBAL_CAP_NOTIFICATIONS 0x00000080 /* New to SMB3.1.1 */
+ /* Internal types */
+ #define SMB2_NT_FIND 0x00100000
+ #define SMB2_LARGE_FILES 0x00200000
+@@ -984,6 +986,19 @@ struct smb2_change_notify_rsp {
+ __u8 Buffer[]; /* array of file notify structs */
+ } __packed;
+
++/*
++ * SMB2_SERVER_TO_CLIENT_NOTIFICATION: See MS-SMB2 section 2.2.44
++ */
++
++#define SMB2_NOTIFY_SESSION_CLOSED 0x0000
++
++struct smb2_server_client_notification {
++ struct smb2_hdr hdr;
++ __le16 StructureSize;
++ __u16 Reserved; /* MBZ */
++ __le32 NotificationType;
++ __u8 NotificationBuffer[4]; /* MBZ */
++} __packed;
+
+ /*
+ * SMB2_CREATE See MS-SMB2 section 2.2.13
+--
+2.43.0
+
diff --git a/queue-6.6/cifs-add-client-version-details-to-ntlm-authenticate.patch b/queue-6.6/cifs-add-client-version-details-to-ntlm-authenticate.patch
new file mode 100644
index 0000000000..af7d1db3ac
--- /dev/null
+++ b/queue-6.6/cifs-add-client-version-details-to-ntlm-authenticate.patch
@@ -0,0 +1,67 @@
+From c3c76c2a5c25d221c573de4ee7480522612d15ec Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 4 Oct 2023 07:17:55 -0400
+Subject: cifs: Add client version details to NTLM authenticate message
+
+From: Meetakshi Setiya <msetiya@microsoft.com>
+
+[ Upstream commit 1460720c5913c11415e4d7c4df5a287eb2ad3f3e ]
+
+The NTLM authenticate message currently sets the NTLMSSP_NEGOTIATE_VERSION
+flag but does not populate the VERSION structure. This commit fixes this
+bug by ensuring that the flag is set and the version details are included
+in the message.
+
+Signed-off-by: Meetakshi Setiya <msetiya@microsoft.com>
+Reviewed-by: Bharath SM <bharathsm@microsoft.com>
+Reviewed-by: Paulo Alcantara (SUSE) <pc@manguebit.com>
+Reviewed-by: Shyam Prasad N <sprasad@microsoft.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/ntlmssp.h | 4 ++--
+ fs/smb/client/sess.c | 12 +++++++++---
+ 2 files changed, 11 insertions(+), 5 deletions(-)
+
+diff --git a/fs/smb/client/ntlmssp.h b/fs/smb/client/ntlmssp.h
+index 2c5dde2ece588..875de43b72de3 100644
+--- a/fs/smb/client/ntlmssp.h
++++ b/fs/smb/client/ntlmssp.h
+@@ -133,8 +133,8 @@ typedef struct _AUTHENTICATE_MESSAGE {
+ SECURITY_BUFFER WorkstationName;
+ SECURITY_BUFFER SessionKey;
+ __le32 NegotiateFlags;
+- /* SECURITY_BUFFER for version info not present since we
+- do not set the version is present flag */
++ struct ntlmssp_version Version;
++ /* SECURITY_BUFFER */
+ char UserString[];
+ } __attribute__((packed)) AUTHENTICATE_MESSAGE, *PAUTHENTICATE_MESSAGE;
+
+diff --git a/fs/smb/client/sess.c b/fs/smb/client/sess.c
+index e4168cd8b6c28..bd4dcd1a9af83 100644
+--- a/fs/smb/client/sess.c
++++ b/fs/smb/client/sess.c
+@@ -1201,10 +1201,16 @@ int build_ntlmssp_auth_blob(unsigned char **pbuffer,
+ memcpy(sec_blob->Signature, NTLMSSP_SIGNATURE, 8);
+ sec_blob->MessageType = NtLmAuthenticate;
+
++ /* send version information in ntlmssp authenticate also */
+ flags = ses->ntlmssp->server_flags | NTLMSSP_REQUEST_TARGET |
+- NTLMSSP_NEGOTIATE_TARGET_INFO | NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED;
+- /* we only send version information in ntlmssp negotiate, so do not set this flag */
+- flags = flags & ~NTLMSSP_NEGOTIATE_VERSION;
++ NTLMSSP_NEGOTIATE_TARGET_INFO | NTLMSSP_NEGOTIATE_VERSION |
++ NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED;
++
++ sec_blob->Version.ProductMajorVersion = LINUX_VERSION_MAJOR;
++ sec_blob->Version.ProductMinorVersion = LINUX_VERSION_PATCHLEVEL;
++ sec_blob->Version.ProductBuild = cpu_to_le16(SMB3_PRODUCT_BUILD);
++ sec_blob->Version.NTLMRevisionCurrent = NTLMSSP_REVISION_W2K3;
++
+ tmp = *pbuffer + sizeof(AUTHENTICATE_MESSAGE);
+ sec_blob->NegotiateFlags = cpu_to_le32(flags);
+
+--
+2.43.0
+
diff --git a/queue-6.6/cifs-add-tracing-for-the-cifs_tcon-struct-refcountin.patch b/queue-6.6/cifs-add-tracing-for-the-cifs_tcon-struct-refcountin.patch
new file mode 100644
index 0000000000..5cc3bb2c3c
--- /dev/null
+++ b/queue-6.6/cifs-add-tracing-for-the-cifs_tcon-struct-refcountin.patch
@@ -0,0 +1,516 @@
+From e5af1e8ee41c311c93e243e739a6f2d2535bd161 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 4 Apr 2024 13:51:36 +0100
+Subject: cifs: Add tracing for the cifs_tcon struct refcounting
+
+From: David Howells <dhowells@redhat.com>
+
+[ Upstream commit afc23febd51c7e24361e3a9c09f3e892eb0a41ea ]
+
+Add tracing for the refcounting/lifecycle of the cifs_tcon struct, marking
+different events with different labels and giving each tcon its own debug
+ID so that the tracelines corresponding to individual tcons can be
+distinguished. This can be enabled with:
+
+ echo 1 >/sys/kernel/debug/tracing/events/cifs/smb3_tcon_ref/enable
+
+Signed-off-by: David Howells <dhowells@redhat.com>
+Acked-by: Paulo Alcantara (Red Hat) <pc@manguebit.com>
+cc: Shyam Prasad N <nspmangalore@gmail.com>
+cc: linux-cifs@vger.kernel.org
+cc: linux-fsdevel@vger.kernel.org
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/cifsfs.c | 2 +
+ fs/smb/client/cifsglob.h | 1 +
+ fs/smb/client/cifsproto.h | 9 ++--
+ fs/smb/client/connect.c | 21 ++++----
+ fs/smb/client/fscache.c | 7 +++
+ fs/smb/client/misc.c | 10 ++--
+ fs/smb/client/smb2misc.c | 10 ++--
+ fs/smb/client/smb2ops.c | 7 ++-
+ fs/smb/client/smb2pdu.c | 8 +--
+ fs/smb/client/smb2transport.c | 2 +
+ fs/smb/client/trace.h | 92 ++++++++++++++++++++++++++++++++++-
+ 11 files changed, 143 insertions(+), 26 deletions(-)
+
+diff --git a/fs/smb/client/cifsfs.c b/fs/smb/client/cifsfs.c
+index 539ac9774de1b..f1dcb86ab9894 100644
+--- a/fs/smb/client/cifsfs.c
++++ b/fs/smb/client/cifsfs.c
+@@ -739,6 +739,8 @@ static void cifs_umount_begin(struct super_block *sb)
+
+ spin_lock(&cifs_tcp_ses_lock);
+ spin_lock(&tcon->tc_lock);
++ trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count,
++ netfs_trace_tcon_ref_see_umount);
+ if ((tcon->tc_count > 1) || (tcon->status == TID_EXITING)) {
+ /* we have other mounts to same share or we have
+ already tried to umount this and woken up
+diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h
+index 9597887280ff3..c146f83eba9b4 100644
+--- a/fs/smb/client/cifsglob.h
++++ b/fs/smb/client/cifsglob.h
+@@ -1190,6 +1190,7 @@ struct cifs_fattr {
+ */
+ struct cifs_tcon {
+ struct list_head tcon_list;
++ int debug_id; /* Debugging for tracing */
+ int tc_count;
+ struct list_head rlist; /* reconnect list */
+ spinlock_t tc_lock; /* protect anything here that is not protected */
+diff --git a/fs/smb/client/cifsproto.h b/fs/smb/client/cifsproto.h
+index 8e0a348f1f660..fbc358c09da3b 100644
+--- a/fs/smb/client/cifsproto.h
++++ b/fs/smb/client/cifsproto.h
+@@ -303,7 +303,7 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx,
+ struct TCP_Server_Info *primary_server);
+ extern void cifs_put_tcp_session(struct TCP_Server_Info *server,
+ int from_reconnect);
+-extern void cifs_put_tcon(struct cifs_tcon *tcon);
++extern void cifs_put_tcon(struct cifs_tcon *tcon, enum smb3_tcon_ref_trace trace);
+
+ extern void cifs_release_automount_timer(void);
+
+@@ -530,8 +530,9 @@ extern int CIFSSMBLogoff(const unsigned int xid, struct cifs_ses *ses);
+
+ extern struct cifs_ses *sesInfoAlloc(void);
+ extern void sesInfoFree(struct cifs_ses *);
+-extern struct cifs_tcon *tcon_info_alloc(bool dir_leases_enabled);
+-extern void tconInfoFree(struct cifs_tcon *);
++extern struct cifs_tcon *tcon_info_alloc(bool dir_leases_enabled,
++ enum smb3_tcon_ref_trace trace);
++extern void tconInfoFree(struct cifs_tcon *tcon, enum smb3_tcon_ref_trace trace);
+
+ extern int cifs_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server,
+ __u32 *pexpected_response_sequence_number);
+@@ -721,8 +722,6 @@ static inline int cifs_create_options(struct cifs_sb_info *cifs_sb, int options)
+ return options;
+ }
+
+-struct super_block *cifs_get_tcon_super(struct cifs_tcon *tcon);
+-void cifs_put_tcon_super(struct super_block *sb);
+ int cifs_wait_for_server_reconnect(struct TCP_Server_Info *server, bool retry);
+
+ /* Put references of @ses and its children */
+diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c
+index 4e35970681bf0..7a16e12f5da87 100644
+--- a/fs/smb/client/connect.c
++++ b/fs/smb/client/connect.c
+@@ -1943,7 +1943,7 @@ cifs_setup_ipc(struct cifs_ses *ses, struct smb3_fs_context *ctx)
+ }
+
+ /* no need to setup directory caching on IPC share, so pass in false */
+- tcon = tcon_info_alloc(false);
++ tcon = tcon_info_alloc(false, netfs_trace_tcon_ref_new_ipc);
+ if (tcon == NULL)
+ return -ENOMEM;
+
+@@ -1960,7 +1960,7 @@ cifs_setup_ipc(struct cifs_ses *ses, struct smb3_fs_context *ctx)
+
+ if (rc) {
+ cifs_server_dbg(VFS, "failed to connect to IPC (rc=%d)\n", rc);
+- tconInfoFree(tcon);
++ tconInfoFree(tcon, netfs_trace_tcon_ref_free_ipc_fail);
+ goto out;
+ }
+
+@@ -2043,7 +2043,7 @@ void __cifs_put_smb_ses(struct cifs_ses *ses)
+ * files on session close, as specified in MS-SMB2 3.3.5.6 Receiving an
+ * SMB2 LOGOFF Request.
+ */
+- tconInfoFree(tcon);
++ tconInfoFree(tcon, netfs_trace_tcon_ref_free_ipc);
+ if (do_logoff) {
+ xid = get_xid();
+ rc = server->ops->logoff(xid, ses);
+@@ -2432,6 +2432,8 @@ cifs_find_tcon(struct cifs_ses *ses, struct smb3_fs_context *ctx)
+ continue;
+ }
+ ++tcon->tc_count;
++ trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count,
++ netfs_trace_tcon_ref_get_find);
+ spin_unlock(&tcon->tc_lock);
+ spin_unlock(&cifs_tcp_ses_lock);
+ return tcon;
+@@ -2441,7 +2443,7 @@ cifs_find_tcon(struct cifs_ses *ses, struct smb3_fs_context *ctx)
+ }
+
+ void
+-cifs_put_tcon(struct cifs_tcon *tcon)
++cifs_put_tcon(struct cifs_tcon *tcon, enum smb3_tcon_ref_trace trace)
+ {
+ unsigned int xid;
+ struct cifs_ses *ses;
+@@ -2457,6 +2459,7 @@ cifs_put_tcon(struct cifs_tcon *tcon)
+ cifs_dbg(FYI, "%s: tc_count=%d\n", __func__, tcon->tc_count);
+ spin_lock(&cifs_tcp_ses_lock);
+ spin_lock(&tcon->tc_lock);
++ trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count - 1, trace);
+ if (--tcon->tc_count > 0) {
+ spin_unlock(&tcon->tc_lock);
+ spin_unlock(&cifs_tcp_ses_lock);
+@@ -2493,7 +2496,7 @@ cifs_put_tcon(struct cifs_tcon *tcon)
+ _free_xid(xid);
+
+ cifs_fscache_release_super_cookie(tcon);
+- tconInfoFree(tcon);
++ tconInfoFree(tcon, netfs_trace_tcon_ref_free);
+ cifs_put_smb_ses(ses);
+ }
+
+@@ -2547,7 +2550,7 @@ cifs_get_tcon(struct cifs_ses *ses, struct smb3_fs_context *ctx)
+ nohandlecache = ctx->nohandlecache;
+ else
+ nohandlecache = true;
+- tcon = tcon_info_alloc(!nohandlecache);
++ tcon = tcon_info_alloc(!nohandlecache, netfs_trace_tcon_ref_new);
+ if (tcon == NULL) {
+ rc = -ENOMEM;
+ goto out_fail;
+@@ -2737,7 +2740,7 @@ cifs_get_tcon(struct cifs_ses *ses, struct smb3_fs_context *ctx)
+ return tcon;
+
+ out_fail:
+- tconInfoFree(tcon);
++ tconInfoFree(tcon, netfs_trace_tcon_ref_free_fail);
+ return ERR_PTR(rc);
+ }
+
+@@ -2754,7 +2757,7 @@ cifs_put_tlink(struct tcon_link *tlink)
+ }
+
+ if (!IS_ERR(tlink_tcon(tlink)))
+- cifs_put_tcon(tlink_tcon(tlink));
++ cifs_put_tcon(tlink_tcon(tlink), netfs_trace_tcon_ref_put_tlink);
+ kfree(tlink);
+ }
+
+@@ -3319,7 +3322,7 @@ void cifs_mount_put_conns(struct cifs_mount_ctx *mnt_ctx)
+ int rc = 0;
+
+ if (mnt_ctx->tcon)
+- cifs_put_tcon(mnt_ctx->tcon);
++ cifs_put_tcon(mnt_ctx->tcon, netfs_trace_tcon_ref_put_mnt_ctx);
+ else if (mnt_ctx->ses)
+ cifs_put_smb_ses(mnt_ctx->ses);
+ else if (mnt_ctx->server)
+diff --git a/fs/smb/client/fscache.c b/fs/smb/client/fscache.c
+index ecabc4b400535..98c5eebdc7b2f 100644
+--- a/fs/smb/client/fscache.c
++++ b/fs/smb/client/fscache.c
+@@ -94,6 +94,11 @@ int cifs_fscache_get_super_cookie(struct cifs_tcon *tcon)
+ }
+ pr_err("Cache volume key already in use (%s)\n", key);
+ vcookie = NULL;
++ trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count,
++ netfs_trace_tcon_ref_see_fscache_collision);
++ } else {
++ trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count,
++ netfs_trace_tcon_ref_see_fscache_okay);
+ }
+
+ tcon->fscache = vcookie;
+@@ -115,6 +120,8 @@ void cifs_fscache_release_super_cookie(struct cifs_tcon *tcon)
+ cifs_fscache_fill_volume_coherency(tcon, &cd);
+ fscache_relinquish_volume(tcon->fscache, &cd, false);
+ tcon->fscache = NULL;
++ trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count,
++ netfs_trace_tcon_ref_see_fscache_relinq);
+ }
+
+ void cifs_fscache_get_inode_cookie(struct inode *inode)
+diff --git a/fs/smb/client/misc.c b/fs/smb/client/misc.c
+index ad44f8d66b377..07c468ddb88a8 100644
+--- a/fs/smb/client/misc.c
++++ b/fs/smb/client/misc.c
+@@ -111,9 +111,10 @@ sesInfoFree(struct cifs_ses *buf_to_free)
+ }
+
+ struct cifs_tcon *
+-tcon_info_alloc(bool dir_leases_enabled)
++tcon_info_alloc(bool dir_leases_enabled, enum smb3_tcon_ref_trace trace)
+ {
+ struct cifs_tcon *ret_buf;
++ static atomic_t tcon_debug_id;
+
+ ret_buf = kzalloc(sizeof(*ret_buf), GFP_KERNEL);
+ if (!ret_buf)
+@@ -130,7 +131,8 @@ tcon_info_alloc(bool dir_leases_enabled)
+
+ atomic_inc(&tconInfoAllocCount);
+ ret_buf->status = TID_NEW;
+- ++ret_buf->tc_count;
++ ret_buf->debug_id = atomic_inc_return(&tcon_debug_id);
++ ret_buf->tc_count = 1;
+ spin_lock_init(&ret_buf->tc_lock);
+ INIT_LIST_HEAD(&ret_buf->openFileList);
+ INIT_LIST_HEAD(&ret_buf->tcon_list);
+@@ -142,17 +144,19 @@ tcon_info_alloc(bool dir_leases_enabled)
+ #ifdef CONFIG_CIFS_FSCACHE
+ mutex_init(&ret_buf->fscache_lock);
+ #endif
++ trace_smb3_tcon_ref(ret_buf->debug_id, ret_buf->tc_count, trace);
+
+ return ret_buf;
+ }
+
+ void
+-tconInfoFree(struct cifs_tcon *tcon)
++tconInfoFree(struct cifs_tcon *tcon, enum smb3_tcon_ref_trace trace)
+ {
+ if (tcon == NULL) {
+ cifs_dbg(FYI, "Null buffer passed to tconInfoFree\n");
+ return;
+ }
++ trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count, trace);
+ free_cached_dirs(tcon->cfids);
+ atomic_dec(&tconInfoAllocCount);
+ kfree(tcon->nativeFileSystem);
+diff --git a/fs/smb/client/smb2misc.c b/fs/smb/client/smb2misc.c
+index cc72be5a93a93..677ef6f99a5be 100644
+--- a/fs/smb/client/smb2misc.c
++++ b/fs/smb/client/smb2misc.c
+@@ -767,7 +767,7 @@ smb2_cancelled_close_fid(struct work_struct *work)
+ if (rc)
+ cifs_tcon_dbg(VFS, "Close cancelled mid failed rc:%d\n", rc);
+
+- cifs_put_tcon(tcon);
++ cifs_put_tcon(tcon, netfs_trace_tcon_ref_put_cancelled_close_fid);
+ kfree(cancelled);
+ }
+
+@@ -811,6 +811,8 @@ smb2_handle_cancelled_close(struct cifs_tcon *tcon, __u64 persistent_fid,
+ if (tcon->tc_count <= 0) {
+ struct TCP_Server_Info *server = NULL;
+
++ trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count,
++ netfs_trace_tcon_ref_see_cancelled_close);
+ WARN_ONCE(tcon->tc_count < 0, "tcon refcount is negative");
+ spin_unlock(&cifs_tcp_ses_lock);
+
+@@ -823,12 +825,14 @@ smb2_handle_cancelled_close(struct cifs_tcon *tcon, __u64 persistent_fid,
+ return 0;
+ }
+ tcon->tc_count++;
++ trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count,
++ netfs_trace_tcon_ref_get_cancelled_close);
+ spin_unlock(&cifs_tcp_ses_lock);
+
+ rc = __smb2_handle_cancelled_cmd(tcon, SMB2_CLOSE_HE, 0,
+ persistent_fid, volatile_fid);
+ if (rc)
+- cifs_put_tcon(tcon);
++ cifs_put_tcon(tcon, netfs_trace_tcon_ref_put_cancelled_close);
+
+ return rc;
+ }
+@@ -856,7 +860,7 @@ smb2_handle_cancelled_mid(struct mid_q_entry *mid, struct TCP_Server_Info *serve
+ rsp->PersistentFileId,
+ rsp->VolatileFileId);
+ if (rc)
+- cifs_put_tcon(tcon);
++ cifs_put_tcon(tcon, netfs_trace_tcon_ref_put_cancelled_mid);
+
+ return rc;
+ }
+diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
+index df6c6d31236ad..66cfce456263b 100644
+--- a/fs/smb/client/smb2ops.c
++++ b/fs/smb/client/smb2ops.c
+@@ -2915,8 +2915,11 @@ smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses,
+ tcon = list_first_entry_or_null(&ses->tcon_list,
+ struct cifs_tcon,
+ tcon_list);
+- if (tcon)
++ if (tcon) {
+ tcon->tc_count++;
++ trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count,
++ netfs_trace_tcon_ref_get_dfs_refer);
++ }
+ spin_unlock(&cifs_tcp_ses_lock);
+ }
+
+@@ -2980,6 +2983,8 @@ smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses,
+ /* ipc tcons are not refcounted */
+ spin_lock(&cifs_tcp_ses_lock);
+ tcon->tc_count--;
++ trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count,
++ netfs_trace_tcon_ref_dec_dfs_refer);
+ /* tc_count can never go negative */
+ WARN_ON(tcon->tc_count < 0);
+ spin_unlock(&cifs_tcp_ses_lock);
+diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c
+index 86c647a947ccd..a5efce03cb58e 100644
+--- a/fs/smb/client/smb2pdu.c
++++ b/fs/smb/client/smb2pdu.c
+@@ -4138,6 +4138,8 @@ void smb2_reconnect_server(struct work_struct *work)
+ list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
+ if (tcon->need_reconnect || tcon->need_reopen_files) {
+ tcon->tc_count++;
++ trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count,
++ netfs_trace_tcon_ref_get_reconnect_server);
+ list_add_tail(&tcon->rlist, &tmp_list);
+ tcon_selected = true;
+ }
+@@ -4176,14 +4178,14 @@ void smb2_reconnect_server(struct work_struct *work)
+ if (tcon->ipc)
+ cifs_put_smb_ses(tcon->ses);
+ else
+- cifs_put_tcon(tcon);
++ cifs_put_tcon(tcon, netfs_trace_tcon_ref_put_reconnect_server);
+ }
+
+ if (!ses_exist)
+ goto done;
+
+ /* allocate a dummy tcon struct used for reconnect */
+- tcon = tcon_info_alloc(false);
++ tcon = tcon_info_alloc(false, netfs_trace_tcon_ref_new_reconnect_server);
+ if (!tcon) {
+ resched = true;
+ list_for_each_entry_safe(ses, ses2, &tmp_ses_list, rlist) {
+@@ -4206,7 +4208,7 @@ void smb2_reconnect_server(struct work_struct *work)
+ list_del_init(&ses->rlist);
+ cifs_put_smb_ses(ses);
+ }
+- tconInfoFree(tcon);
++ tconInfoFree(tcon, netfs_trace_tcon_ref_free_reconnect_server);
+
+ done:
+ cifs_dbg(FYI, "Reconnecting tcons and channels finished\n");
+diff --git a/fs/smb/client/smb2transport.c b/fs/smb/client/smb2transport.c
+index 5a3ca62d2f07f..8f346aafc4cf8 100644
+--- a/fs/smb/client/smb2transport.c
++++ b/fs/smb/client/smb2transport.c
+@@ -189,6 +189,8 @@ smb2_find_smb_sess_tcon_unlocked(struct cifs_ses *ses, __u32 tid)
+ if (tcon->tid != tid)
+ continue;
+ ++tcon->tc_count;
++ trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count,
++ netfs_trace_tcon_ref_get_find_sess_tcon);
+ return tcon;
+ }
+
+diff --git a/fs/smb/client/trace.h b/fs/smb/client/trace.h
+index 5e83cb9da9028..604e52876cd2d 100644
+--- a/fs/smb/client/trace.h
++++ b/fs/smb/client/trace.h
+@@ -3,6 +3,9 @@
+ * Copyright (C) 2018, Microsoft Corporation.
+ *
+ * Author(s): Steve French <stfrench@microsoft.com>
++ *
++ * Please use this 3-part article as a reference for writing new tracepoints:
++ * https://lwn.net/Articles/379903/
+ */
+ #undef TRACE_SYSTEM
+ #define TRACE_SYSTEM cifs
+@@ -15,9 +18,70 @@
+ #include <linux/inet.h>
+
+ /*
+- * Please use this 3-part article as a reference for writing new tracepoints:
+- * https://lwn.net/Articles/379903/
++ * Specify enums for tracing information.
++ */
++#define smb3_tcon_ref_traces \
++ EM(netfs_trace_tcon_ref_dec_dfs_refer, "DEC DfsRef") \
++ EM(netfs_trace_tcon_ref_free, "FRE ") \
++ EM(netfs_trace_tcon_ref_free_fail, "FRE Fail ") \
++ EM(netfs_trace_tcon_ref_free_ipc, "FRE Ipc ") \
++ EM(netfs_trace_tcon_ref_free_ipc_fail, "FRE Ipc-F ") \
++ EM(netfs_trace_tcon_ref_free_reconnect_server, "FRE Reconn") \
++ EM(netfs_trace_tcon_ref_get_cancelled_close, "GET Cn-Cls") \
++ EM(netfs_trace_tcon_ref_get_dfs_refer, "GET DfsRef") \
++ EM(netfs_trace_tcon_ref_get_find, "GET Find ") \
++ EM(netfs_trace_tcon_ref_get_find_sess_tcon, "GET FndSes") \
++ EM(netfs_trace_tcon_ref_get_reconnect_server, "GET Reconn") \
++ EM(netfs_trace_tcon_ref_new, "NEW ") \
++ EM(netfs_trace_tcon_ref_new_ipc, "NEW Ipc ") \
++ EM(netfs_trace_tcon_ref_new_reconnect_server, "NEW Reconn") \
++ EM(netfs_trace_tcon_ref_put_cancelled_close, "PUT Cn-Cls") \
++ EM(netfs_trace_tcon_ref_put_cancelled_close_fid, "PUT Cn-Fid") \
++ EM(netfs_trace_tcon_ref_put_cancelled_mid, "PUT Cn-Mid") \
++ EM(netfs_trace_tcon_ref_put_mnt_ctx, "PUT MntCtx") \
++ EM(netfs_trace_tcon_ref_put_reconnect_server, "PUT Reconn") \
++ EM(netfs_trace_tcon_ref_put_tlink, "PUT Tlink ") \
++ EM(netfs_trace_tcon_ref_see_cancelled_close, "SEE Cn-Cls") \
++ EM(netfs_trace_tcon_ref_see_fscache_collision, "SEE FV-CO!") \
++ EM(netfs_trace_tcon_ref_see_fscache_okay, "SEE FV-Ok ") \
++ EM(netfs_trace_tcon_ref_see_fscache_relinq, "SEE FV-Rlq") \
++ E_(netfs_trace_tcon_ref_see_umount, "SEE Umount")
++
++#undef EM
++#undef E_
++
++/*
++ * Define those tracing enums.
++ */
++#ifndef __SMB3_DECLARE_TRACE_ENUMS_ONCE_ONLY
++#define __SMB3_DECLARE_TRACE_ENUMS_ONCE_ONLY
++
++#define EM(a, b) a,
++#define E_(a, b) a
++
++enum smb3_tcon_ref_trace { smb3_tcon_ref_traces } __mode(byte);
++
++#undef EM
++#undef E_
++#endif
++
++/*
++ * Export enum symbols via userspace.
++ */
++#define EM(a, b) TRACE_DEFINE_ENUM(a);
++#define E_(a, b) TRACE_DEFINE_ENUM(a);
++
++smb3_tcon_ref_traces;
++
++#undef EM
++#undef E_
++
++/*
++ * Now redefine the EM() and E_() macros to map the enums to the strings that
++ * will be printed in the output.
+ */
++#define EM(a, b) { a, b },
++#define E_(a, b) { a, b }
+
+ /* For logging errors in read or write */
+ DECLARE_EVENT_CLASS(smb3_rw_err_class,
+@@ -1125,6 +1189,30 @@ DEFINE_SMB3_CREDIT_EVENT(waitff_credits);
+ DEFINE_SMB3_CREDIT_EVENT(overflow_credits);
+ DEFINE_SMB3_CREDIT_EVENT(set_credits);
+
++
++TRACE_EVENT(smb3_tcon_ref,
++ TP_PROTO(unsigned int tcon_debug_id, int ref,
++ enum smb3_tcon_ref_trace trace),
++ TP_ARGS(tcon_debug_id, ref, trace),
++ TP_STRUCT__entry(
++ __field(unsigned int, tcon)
++ __field(int, ref)
++ __field(enum smb3_tcon_ref_trace, trace)
++ ),
++ TP_fast_assign(
++ __entry->tcon = tcon_debug_id;
++ __entry->ref = ref;
++ __entry->trace = trace;
++ ),
++ TP_printk("TC=%08x %s r=%u",
++ __entry->tcon,
++ __print_symbolic(__entry->trace, smb3_tcon_ref_traces),
++ __entry->ref)
++ );
++
++
++#undef EM
++#undef E_
+ #endif /* _CIFS_TRACE_H */
+
+ #undef TRACE_INCLUDE_PATH
+--
+2.43.0
+
diff --git a/queue-6.6/cifs-commands-that-are-retried-should-have-replay-fl.patch b/queue-6.6/cifs-commands-that-are-retried-should-have-replay-fl.patch
new file mode 100644
index 0000000000..6c69daea17
--- /dev/null
+++ b/queue-6.6/cifs-commands-that-are-retried-should-have-replay-fl.patch
@@ -0,0 +1,1105 @@
+From ef559a76a5d91e3d45515b6cc8d927ba5004b0a8 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 21 Jan 2024 03:32:47 +0000
+Subject: cifs: commands that are retried should have replay flag set
+
+From: Shyam Prasad N <sprasad@microsoft.com>
+
+[ Upstream commit 4f1fffa2376922f3d1d506e49c0fd445b023a28e ]
+
+MS-SMB2 states that the header flag SMB2_FLAGS_REPLAY_OPERATION
+needs to be set when a command needs to be retried, so that
+the server is aware that this is a replay for an operation that
+appeared before.
+
+This can be very important, for example, for state changing
+operations and opens which get retried following a reconnect;
+since the client maybe unaware of the status of the previous
+open.
+
+This is particularly important for multichannel scenario, since
+disconnection of one connection does not mean that the session
+is lost. The requests can be replayed on another channel.
+
+This change also makes use of exponential back-off before replays
+and also limits the number of retries to "retrans" mount option
+value.
+
+Also, this change does not modify the read/write codepath.
+
+Signed-off-by: Shyam Prasad N <sprasad@microsoft.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/cached_dir.c | 23 +++-
+ fs/smb/client/cifsglob.h | 5 +
+ fs/smb/client/smb2inode.c | 33 ++++-
+ fs/smb/client/smb2ops.c | 123 ++++++++++++++++--
+ fs/smb/client/smb2pdu.c | 260 +++++++++++++++++++++++++++++++++----
+ fs/smb/client/smb2proto.h | 5 +
+ 6 files changed, 404 insertions(+), 45 deletions(-)
+
+diff --git a/fs/smb/client/cached_dir.c b/fs/smb/client/cached_dir.c
+index 567f718362c52..ca0fd25236ef4 100644
+--- a/fs/smb/client/cached_dir.c
++++ b/fs/smb/client/cached_dir.c
+@@ -145,21 +145,27 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
+ struct cached_fid *cfid;
+ struct cached_fids *cfids;
+ const char *npath;
++ int retries = 0, cur_sleep = 1;
+
+ if (tcon == NULL || tcon->cfids == NULL || tcon->nohandlecache ||
+ is_smb1_server(tcon->ses->server) || (dir_cache_timeout == 0))
+ return -EOPNOTSUPP;
+
+ ses = tcon->ses;
+- server = cifs_pick_channel(ses);
+ cfids = tcon->cfids;
+
+- if (!server->ops->new_lease_key)
+- return -EIO;
+-
+ if (cifs_sb->root == NULL)
+ return -ENOENT;
+
++replay_again:
++ /* reinitialize for possible replay */
++ flags = 0;
++ oplock = SMB2_OPLOCK_LEVEL_II;
++ server = cifs_pick_channel(ses);
++
++ if (!server->ops->new_lease_key)
++ return -EIO;
++
+ utf16_path = cifs_convert_path_to_utf16(path, cifs_sb);
+ if (!utf16_path)
+ return -ENOMEM;
+@@ -269,6 +275,11 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
+ */
+ cfid->has_lease = true;
+
++ if (retries) {
++ smb2_set_replay(server, &rqst[0]);
++ smb2_set_replay(server, &rqst[1]);
++ }
++
+ rc = compound_send_recv(xid, ses, server,
+ flags, 2, rqst,
+ resp_buftype, rsp_iov);
+@@ -369,6 +380,10 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
+ }
+ kfree(utf16_path);
+
++ if (is_replayable_error(rc) &&
++ smb2_should_replay(tcon, &retries, &cur_sleep))
++ goto replay_again;
++
+ return rc;
+ }
+
+diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h
+index 6acadb53ada79..479bf0d9ad589 100644
+--- a/fs/smb/client/cifsglob.h
++++ b/fs/smb/client/cifsglob.h
+@@ -49,6 +49,11 @@
+ */
+ #define CIFS_DEF_ACTIMEO (1 * HZ)
+
++/*
++ * max sleep time before retry to server
++ */
++#define CIFS_MAX_SLEEP 2000
++
+ /*
+ * max attribute cache timeout (jiffies) - 2^30
+ */
+diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c
+index 1388ce5421a89..94df328a1965d 100644
+--- a/fs/smb/client/smb2inode.c
++++ b/fs/smb/client/smb2inode.c
+@@ -92,6 +92,14 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
+ unsigned int size[2];
+ void *data[2];
+ int len;
++ int retries = 0, cur_sleep = 1;
++
++replay_again:
++ /* reinitialize for possible replay */
++ flags = 0;
++ oplock = SMB2_OPLOCK_LEVEL_NONE;
++ num_rqst = 0;
++ server = cifs_pick_channel(ses);
+
+ vars = kzalloc(sizeof(*vars), GFP_ATOMIC);
+ if (vars == NULL)
+@@ -99,8 +107,6 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
+ rqst = &vars->rqst[0];
+ rsp_iov = &vars->rsp_iov[0];
+
+- server = cifs_pick_channel(ses);
+-
+ if (smb3_encryption_required(tcon))
+ flags |= CIFS_TRANSFORM_REQ;
+
+@@ -435,15 +441,24 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
+ num_rqst++;
+
+ if (cfile) {
++ if (retries)
++ for (i = 1; i < num_rqst - 2; i++)
++ smb2_set_replay(server, &rqst[i]);
++
+ rc = compound_send_recv(xid, ses, server,
+ flags, num_rqst - 2,
+ &rqst[1], &resp_buftype[1],
+ &rsp_iov[1]);
+- } else
++ } else {
++ if (retries)
++ for (i = 0; i < num_rqst; i++)
++ smb2_set_replay(server, &rqst[i]);
++
+ rc = compound_send_recv(xid, ses, server,
+ flags, num_rqst,
+ rqst, resp_buftype,
+ rsp_iov);
++ }
+
+ finished:
+ num_rqst = 0;
+@@ -604,9 +619,6 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
+ }
+ SMB2_close_free(&rqst[num_rqst]);
+
+- if (cfile)
+- cifsFileInfo_put(cfile);
+-
+ num_cmds += 2;
+ if (out_iov && out_buftype) {
+ memcpy(out_iov, rsp_iov, num_cmds * sizeof(*out_iov));
+@@ -616,7 +628,16 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
+ for (i = 0; i < num_cmds; i++)
+ free_rsp_buf(resp_buftype[i], rsp_iov[i].iov_base);
+ }
++ num_cmds -= 2; /* correct num_cmds as there could be a retry */
+ kfree(vars);
++
++ if (is_replayable_error(rc) &&
++ smb2_should_replay(tcon, &retries, &cur_sleep))
++ goto replay_again;
++
++ if (cfile)
++ cifsFileInfo_put(cfile);
++
+ return rc;
+ }
+
+diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
+index 3e07ab1564ea7..06735c5685bf6 100644
+--- a/fs/smb/client/smb2ops.c
++++ b/fs/smb/client/smb2ops.c
+@@ -1108,7 +1108,7 @@ smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon,
+ {
+ struct smb2_compound_vars *vars;
+ struct cifs_ses *ses = tcon->ses;
+- struct TCP_Server_Info *server = cifs_pick_channel(ses);
++ struct TCP_Server_Info *server;
+ struct smb_rqst *rqst;
+ struct kvec *rsp_iov;
+ __le16 *utf16_path = NULL;
+@@ -1124,6 +1124,13 @@ smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon,
+ struct smb2_file_full_ea_info *ea = NULL;
+ struct smb2_query_info_rsp *rsp;
+ int rc, used_len = 0;
++ int retries = 0, cur_sleep = 1;
++
++replay_again:
++ /* reinitialize for possible replay */
++ flags = CIFS_CP_CREATE_CLOSE_OP;
++ oplock = SMB2_OPLOCK_LEVEL_NONE;
++ server = cifs_pick_channel(ses);
+
+ if (smb3_encryption_required(tcon))
+ flags |= CIFS_TRANSFORM_REQ;
+@@ -1244,6 +1251,12 @@ smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon,
+ goto sea_exit;
+ smb2_set_related(&rqst[2]);
+
++ if (retries) {
++ smb2_set_replay(server, &rqst[0]);
++ smb2_set_replay(server, &rqst[1]);
++ smb2_set_replay(server, &rqst[2]);
++ }
++
+ rc = compound_send_recv(xid, ses, server,
+ flags, 3, rqst,
+ resp_buftype, rsp_iov);
+@@ -1260,6 +1273,11 @@ smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon,
+ kfree(vars);
+ out_free_path:
+ kfree(utf16_path);
++
++ if (is_replayable_error(rc) &&
++ smb2_should_replay(tcon, &retries, &cur_sleep))
++ goto replay_again;
++
+ return rc;
+ }
+ #endif
+@@ -1485,7 +1503,7 @@ smb2_ioctl_query_info(const unsigned int xid,
+ struct smb_rqst *rqst;
+ struct kvec *rsp_iov;
+ struct cifs_ses *ses = tcon->ses;
+- struct TCP_Server_Info *server = cifs_pick_channel(ses);
++ struct TCP_Server_Info *server;
+ char __user *arg = (char __user *)p;
+ struct smb_query_info qi;
+ struct smb_query_info __user *pqi;
+@@ -1502,6 +1520,13 @@ smb2_ioctl_query_info(const unsigned int xid,
+ void *data[2];
+ int create_options = is_dir ? CREATE_NOT_FILE : CREATE_NOT_DIR;
+ void (*free_req1_func)(struct smb_rqst *r);
++ int retries = 0, cur_sleep = 1;
++
++replay_again:
++ /* reinitialize for possible replay */
++ flags = CIFS_CP_CREATE_CLOSE_OP;
++ oplock = SMB2_OPLOCK_LEVEL_NONE;
++ server = cifs_pick_channel(ses);
+
+ vars = kzalloc(sizeof(*vars), GFP_ATOMIC);
+ if (vars == NULL)
+@@ -1642,6 +1667,12 @@ smb2_ioctl_query_info(const unsigned int xid,
+ goto free_req_1;
+ smb2_set_related(&rqst[2]);
+
++ if (retries) {
++ smb2_set_replay(server, &rqst[0]);
++ smb2_set_replay(server, &rqst[1]);
++ smb2_set_replay(server, &rqst[2]);
++ }
++
+ rc = compound_send_recv(xid, ses, server,
+ flags, 3, rqst,
+ resp_buftype, rsp_iov);
+@@ -1702,6 +1733,11 @@ smb2_ioctl_query_info(const unsigned int xid,
+ kfree(buffer);
+ free_vars:
+ kfree(vars);
++
++ if (is_replayable_error(rc) &&
++ smb2_should_replay(tcon, &retries, &cur_sleep))
++ goto replay_again;
++
+ return rc;
+ }
+
+@@ -2228,8 +2264,14 @@ smb2_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon,
+ struct cifs_open_parms oparms;
+ struct smb2_query_directory_rsp *qd_rsp = NULL;
+ struct smb2_create_rsp *op_rsp = NULL;
+- struct TCP_Server_Info *server = cifs_pick_channel(tcon->ses);
+- int retry_count = 0;
++ struct TCP_Server_Info *server;
++ int retries = 0, cur_sleep = 1;
++
++replay_again:
++ /* reinitialize for possible replay */
++ flags = 0;
++ oplock = SMB2_OPLOCK_LEVEL_NONE;
++ server = cifs_pick_channel(tcon->ses);
+
+ utf16_path = cifs_convert_path_to_utf16(path, cifs_sb);
+ if (!utf16_path)
+@@ -2279,14 +2321,15 @@ smb2_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon,
+
+ smb2_set_related(&rqst[1]);
+
+-again:
++ if (retries) {
++ smb2_set_replay(server, &rqst[0]);
++ smb2_set_replay(server, &rqst[1]);
++ }
++
+ rc = compound_send_recv(xid, tcon->ses, server,
+ flags, 2, rqst,
+ resp_buftype, rsp_iov);
+
+- if (rc == -EAGAIN && retry_count++ < 10)
+- goto again;
+-
+ /* If the open failed there is nothing to do */
+ op_rsp = (struct smb2_create_rsp *)rsp_iov[0].iov_base;
+ if (op_rsp == NULL || op_rsp->hdr.Status != STATUS_SUCCESS) {
+@@ -2334,6 +2377,11 @@ smb2_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon,
+ SMB2_query_directory_free(&rqst[1]);
+ free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base);
+ free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base);
++
++ if (is_replayable_error(rc) &&
++ smb2_should_replay(tcon, &retries, &cur_sleep))
++ goto replay_again;
++
+ return rc;
+ }
+
+@@ -2460,6 +2508,22 @@ smb2_oplock_response(struct cifs_tcon *tcon, __u64 persistent_fid,
+ CIFS_CACHE_READ(cinode) ? 1 : 0);
+ }
+
++void
++smb2_set_replay(struct TCP_Server_Info *server, struct smb_rqst *rqst)
++{
++ struct smb2_hdr *shdr;
++
++ if (server->dialect < SMB30_PROT_ID)
++ return;
++
++ shdr = (struct smb2_hdr *)(rqst->rq_iov[0].iov_base);
++ if (shdr == NULL) {
++ cifs_dbg(FYI, "shdr NULL in smb2_set_related\n");
++ return;
++ }
++ shdr->Flags |= SMB2_FLAGS_REPLAY_OPERATION;
++}
++
+ void
+ smb2_set_related(struct smb_rqst *rqst)
+ {
+@@ -2532,6 +2596,27 @@ smb2_set_next_command(struct cifs_tcon *tcon, struct smb_rqst *rqst)
+ shdr->NextCommand = cpu_to_le32(len);
+ }
+
++/*
++ * helper function for exponential backoff and check if replayable
++ */
++bool smb2_should_replay(struct cifs_tcon *tcon,
++ int *pretries,
++ int *pcur_sleep)
++{
++ if (!pretries || !pcur_sleep)
++ return false;
++
++ if (tcon->retry || (*pretries)++ < tcon->ses->server->retrans) {
++ msleep(*pcur_sleep);
++ (*pcur_sleep) = ((*pcur_sleep) << 1);
++ if ((*pcur_sleep) > CIFS_MAX_SLEEP)
++ (*pcur_sleep) = CIFS_MAX_SLEEP;
++ return true;
++ }
++
++ return false;
++}
++
+ /*
+ * Passes the query info response back to the caller on success.
+ * Caller need to free this with free_rsp_buf().
+@@ -2545,7 +2630,7 @@ smb2_query_info_compound(const unsigned int xid, struct cifs_tcon *tcon,
+ {
+ struct smb2_compound_vars *vars;
+ struct cifs_ses *ses = tcon->ses;
+- struct TCP_Server_Info *server = cifs_pick_channel(ses);
++ struct TCP_Server_Info *server;
+ int flags = CIFS_CP_CREATE_CLOSE_OP;
+ struct smb_rqst *rqst;
+ int resp_buftype[3];
+@@ -2556,6 +2641,13 @@ smb2_query_info_compound(const unsigned int xid, struct cifs_tcon *tcon,
+ int rc;
+ __le16 *utf16_path;
+ struct cached_fid *cfid = NULL;
++ int retries = 0, cur_sleep = 1;
++
++replay_again:
++ /* reinitialize for possible replay */
++ flags = CIFS_CP_CREATE_CLOSE_OP;
++ oplock = SMB2_OPLOCK_LEVEL_NONE;
++ server = cifs_pick_channel(ses);
+
+ if (!path)
+ path = "";
+@@ -2636,6 +2728,14 @@ smb2_query_info_compound(const unsigned int xid, struct cifs_tcon *tcon,
+ goto qic_exit;
+ smb2_set_related(&rqst[2]);
+
++ if (retries) {
++ if (!cfid) {
++ smb2_set_replay(server, &rqst[0]);
++ smb2_set_replay(server, &rqst[2]);
++ }
++ smb2_set_replay(server, &rqst[1]);
++ }
++
+ if (cfid) {
+ rc = compound_send_recv(xid, ses, server,
+ flags, 1, &rqst[1],
+@@ -2668,6 +2768,11 @@ smb2_query_info_compound(const unsigned int xid, struct cifs_tcon *tcon,
+ kfree(vars);
+ out_free_path:
+ kfree(utf16_path);
++
++ if (is_replayable_error(rc) &&
++ smb2_should_replay(tcon, &retries, &cur_sleep))
++ goto replay_again;
++
+ return rc;
+ }
+
+diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c
+index 95b5b4bdb4b7f..6a5d478b3cef6 100644
+--- a/fs/smb/client/smb2pdu.c
++++ b/fs/smb/client/smb2pdu.c
+@@ -2795,7 +2795,14 @@ int smb311_posix_mkdir(const unsigned int xid, struct inode *inode,
+ int flags = 0;
+ unsigned int total_len;
+ __le16 *utf16_path = NULL;
+- struct TCP_Server_Info *server = cifs_pick_channel(ses);
++ struct TCP_Server_Info *server;
++ int retries = 0, cur_sleep = 1;
++
++replay_again:
++ /* reinitialize for possible replay */
++ flags = 0;
++ n_iov = 2;
++ server = cifs_pick_channel(ses);
+
+ cifs_dbg(FYI, "mkdir\n");
+
+@@ -2899,6 +2906,10 @@ int smb311_posix_mkdir(const unsigned int xid, struct inode *inode,
+ /* no need to inc num_remote_opens because we close it just below */
+ trace_smb3_posix_mkdir_enter(xid, tcon->tid, ses->Suid, full_path, CREATE_NOT_FILE,
+ FILE_WRITE_ATTRIBUTES);
++
++ if (retries)
++ smb2_set_replay(server, &rqst);
++
+ /* resource #4: response buffer */
+ rc = cifs_send_recv(xid, ses, server,
+ &rqst, &resp_buftype, flags, &rsp_iov);
+@@ -2936,6 +2947,11 @@ int smb311_posix_mkdir(const unsigned int xid, struct inode *inode,
+ cifs_small_buf_release(req);
+ err_free_path:
+ kfree(utf16_path);
++
++ if (is_replayable_error(rc) &&
++ smb2_should_replay(tcon, &retries, &cur_sleep))
++ goto replay_again;
++
+ return rc;
+ }
+
+@@ -3131,12 +3147,18 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
+ struct smb2_create_rsp *rsp = NULL;
+ struct cifs_tcon *tcon = oparms->tcon;
+ struct cifs_ses *ses = tcon->ses;
+- struct TCP_Server_Info *server = cifs_pick_channel(ses);
++ struct TCP_Server_Info *server;
+ struct kvec iov[SMB2_CREATE_IOV_SIZE];
+ struct kvec rsp_iov = {NULL, 0};
+ int resp_buftype = CIFS_NO_BUFFER;
+ int rc = 0;
+ int flags = 0;
++ int retries = 0, cur_sleep = 1;
++
++replay_again:
++ /* reinitialize for possible replay */
++ flags = 0;
++ server = cifs_pick_channel(ses);
+
+ cifs_dbg(FYI, "create/open\n");
+ if (!ses || !server)
+@@ -3158,6 +3180,9 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
+ trace_smb3_open_enter(xid, tcon->tid, tcon->ses->Suid, oparms->path,
+ oparms->create_options, oparms->desired_access);
+
++ if (retries)
++ smb2_set_replay(server, &rqst);
++
+ rc = cifs_send_recv(xid, ses, server,
+ &rqst, &resp_buftype, flags,
+ &rsp_iov);
+@@ -3211,6 +3236,11 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
+ creat_exit:
+ SMB2_open_free(&rqst);
+ free_rsp_buf(resp_buftype, rsp);
++
++ if (is_replayable_error(rc) &&
++ smb2_should_replay(tcon, &retries, &cur_sleep))
++ goto replay_again;
++
+ return rc;
+ }
+
+@@ -3335,15 +3365,7 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
+ int resp_buftype = CIFS_NO_BUFFER;
+ int rc = 0;
+ int flags = 0;
+-
+- cifs_dbg(FYI, "SMB2 IOCTL\n");
+-
+- if (out_data != NULL)
+- *out_data = NULL;
+-
+- /* zero out returned data len, in case of error */
+- if (plen)
+- *plen = 0;
++ int retries = 0, cur_sleep = 1;
+
+ if (!tcon)
+ return -EIO;
+@@ -3352,10 +3374,23 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
+ if (!ses)
+ return -EIO;
+
++replay_again:
++ /* reinitialize for possible replay */
++ flags = 0;
+ server = cifs_pick_channel(ses);
++
+ if (!server)
+ return -EIO;
+
++ cifs_dbg(FYI, "SMB2 IOCTL\n");
++
++ if (out_data != NULL)
++ *out_data = NULL;
++
++ /* zero out returned data len, in case of error */
++ if (plen)
++ *plen = 0;
++
+ if (smb3_encryption_required(tcon))
+ flags |= CIFS_TRANSFORM_REQ;
+
+@@ -3370,6 +3405,9 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
+ if (rc)
+ goto ioctl_exit;
+
++ if (retries)
++ smb2_set_replay(server, &rqst);
++
+ rc = cifs_send_recv(xid, ses, server,
+ &rqst, &resp_buftype, flags,
+ &rsp_iov);
+@@ -3439,6 +3477,11 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
+ ioctl_exit:
+ SMB2_ioctl_free(&rqst);
+ free_rsp_buf(resp_buftype, rsp);
++
++ if (is_replayable_error(rc) &&
++ smb2_should_replay(tcon, &retries, &cur_sleep))
++ goto replay_again;
++
+ return rc;
+ }
+
+@@ -3510,13 +3553,20 @@ __SMB2_close(const unsigned int xid, struct cifs_tcon *tcon,
+ struct smb_rqst rqst;
+ struct smb2_close_rsp *rsp = NULL;
+ struct cifs_ses *ses = tcon->ses;
+- struct TCP_Server_Info *server = cifs_pick_channel(ses);
++ struct TCP_Server_Info *server;
+ struct kvec iov[1];
+ struct kvec rsp_iov;
+ int resp_buftype = CIFS_NO_BUFFER;
+ int rc = 0;
+ int flags = 0;
+ bool query_attrs = false;
++ int retries = 0, cur_sleep = 1;
++
++replay_again:
++ /* reinitialize for possible replay */
++ flags = 0;
++ query_attrs = false;
++ server = cifs_pick_channel(ses);
+
+ cifs_dbg(FYI, "Close\n");
+
+@@ -3542,6 +3592,9 @@ __SMB2_close(const unsigned int xid, struct cifs_tcon *tcon,
+ if (rc)
+ goto close_exit;
+
++ if (retries)
++ smb2_set_replay(server, &rqst);
++
+ rc = cifs_send_recv(xid, ses, server,
+ &rqst, &resp_buftype, flags, &rsp_iov);
+ rsp = (struct smb2_close_rsp *)rsp_iov.iov_base;
+@@ -3575,6 +3628,11 @@ __SMB2_close(const unsigned int xid, struct cifs_tcon *tcon,
+ cifs_dbg(VFS, "handle cancelled close fid 0x%llx returned error %d\n",
+ persistent_fid, tmp_rc);
+ }
++
++ if (is_replayable_error(rc) &&
++ smb2_should_replay(tcon, &retries, &cur_sleep))
++ goto replay_again;
++
+ return rc;
+ }
+
+@@ -3705,12 +3763,19 @@ query_info(const unsigned int xid, struct cifs_tcon *tcon,
+ struct TCP_Server_Info *server;
+ int flags = 0;
+ bool allocated = false;
++ int retries = 0, cur_sleep = 1;
+
+ cifs_dbg(FYI, "Query Info\n");
+
+ if (!ses)
+ return -EIO;
++
++replay_again:
++ /* reinitialize for possible replay */
++ flags = 0;
++ allocated = false;
+ server = cifs_pick_channel(ses);
++
+ if (!server)
+ return -EIO;
+
+@@ -3732,6 +3797,9 @@ query_info(const unsigned int xid, struct cifs_tcon *tcon,
+ trace_smb3_query_info_enter(xid, persistent_fid, tcon->tid,
+ ses->Suid, info_class, (__u32)info_type);
+
++ if (retries)
++ smb2_set_replay(server, &rqst);
++
+ rc = cifs_send_recv(xid, ses, server,
+ &rqst, &resp_buftype, flags, &rsp_iov);
+ rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base;
+@@ -3774,6 +3842,11 @@ query_info(const unsigned int xid, struct cifs_tcon *tcon,
+ qinf_exit:
+ SMB2_query_info_free(&rqst);
+ free_rsp_buf(resp_buftype, rsp);
++
++ if (is_replayable_error(rc) &&
++ smb2_should_replay(tcon, &retries, &cur_sleep))
++ goto replay_again;
++
+ return rc;
+ }
+
+@@ -3874,7 +3947,7 @@ SMB2_change_notify(const unsigned int xid, struct cifs_tcon *tcon,
+ u32 *plen /* returned data len */)
+ {
+ struct cifs_ses *ses = tcon->ses;
+- struct TCP_Server_Info *server = cifs_pick_channel(ses);
++ struct TCP_Server_Info *server;
+ struct smb_rqst rqst;
+ struct smb2_change_notify_rsp *smb_rsp;
+ struct kvec iov[1];
+@@ -3882,6 +3955,12 @@ SMB2_change_notify(const unsigned int xid, struct cifs_tcon *tcon,
+ int resp_buftype = CIFS_NO_BUFFER;
+ int flags = 0;
+ int rc = 0;
++ int retries = 0, cur_sleep = 1;
++
++replay_again:
++ /* reinitialize for possible replay */
++ flags = 0;
++ server = cifs_pick_channel(ses);
+
+ cifs_dbg(FYI, "change notify\n");
+ if (!ses || !server)
+@@ -3906,6 +3985,10 @@ SMB2_change_notify(const unsigned int xid, struct cifs_tcon *tcon,
+
+ trace_smb3_notify_enter(xid, persistent_fid, tcon->tid, ses->Suid,
+ (u8)watch_tree, completion_filter);
++
++ if (retries)
++ smb2_set_replay(server, &rqst);
++
+ rc = cifs_send_recv(xid, ses, server,
+ &rqst, &resp_buftype, flags, &rsp_iov);
+
+@@ -3940,6 +4023,11 @@ SMB2_change_notify(const unsigned int xid, struct cifs_tcon *tcon,
+ if (rqst.rq_iov)
+ cifs_small_buf_release(rqst.rq_iov[0].iov_base); /* request */
+ free_rsp_buf(resp_buftype, rsp_iov.iov_base);
++
++ if (is_replayable_error(rc) &&
++ smb2_should_replay(tcon, &retries, &cur_sleep))
++ goto replay_again;
++
+ return rc;
+ }
+
+@@ -4182,10 +4270,16 @@ SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
+ struct smb_rqst rqst;
+ struct kvec iov[1];
+ struct kvec rsp_iov = {NULL, 0};
+- struct TCP_Server_Info *server = cifs_pick_channel(ses);
++ struct TCP_Server_Info *server;
+ int resp_buftype = CIFS_NO_BUFFER;
+ int flags = 0;
+ int rc = 0;
++ int retries = 0, cur_sleep = 1;
++
++replay_again:
++ /* reinitialize for possible replay */
++ flags = 0;
++ server = cifs_pick_channel(ses);
+
+ cifs_dbg(FYI, "flush\n");
+ if (!ses || !(ses->server))
+@@ -4205,6 +4299,10 @@ SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
+ goto flush_exit;
+
+ trace_smb3_flush_enter(xid, persistent_fid, tcon->tid, ses->Suid);
++
++ if (retries)
++ smb2_set_replay(server, &rqst);
++
+ rc = cifs_send_recv(xid, ses, server,
+ &rqst, &resp_buftype, flags, &rsp_iov);
+
+@@ -4219,6 +4317,11 @@ SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
+ flush_exit:
+ SMB2_flush_free(&rqst);
+ free_rsp_buf(resp_buftype, rsp_iov.iov_base);
++
++ if (is_replayable_error(rc) &&
++ smb2_should_replay(tcon, &retries, &cur_sleep))
++ goto replay_again;
++
+ return rc;
+ }
+
+@@ -4856,18 +4959,21 @@ SMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms,
+ int flags = 0;
+ unsigned int total_len;
+ struct TCP_Server_Info *server;
++ int retries = 0, cur_sleep = 1;
+
++replay_again:
++ /* reinitialize for possible replay */
++ flags = 0;
+ *nbytes = 0;
+-
+- if (n_vec < 1)
+- return rc;
+-
+ if (!io_parms->server)
+ io_parms->server = cifs_pick_channel(io_parms->tcon->ses);
+ server = io_parms->server;
+ if (server == NULL)
+ return -ECONNABORTED;
+
++ if (n_vec < 1)
++ return rc;
++
+ rc = smb2_plain_req_init(SMB2_WRITE, io_parms->tcon, server,
+ (void **) &req, &total_len);
+ if (rc)
+@@ -4901,6 +5007,9 @@ SMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms,
+ rqst.rq_iov = iov;
+ rqst.rq_nvec = n_vec + 1;
+
++ if (retries)
++ smb2_set_replay(server, &rqst);
++
+ rc = cifs_send_recv(xid, io_parms->tcon->ses, server,
+ &rqst,
+ &resp_buftype, flags, &rsp_iov);
+@@ -4925,6 +5034,11 @@ SMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms,
+
+ cifs_small_buf_release(req);
+ free_rsp_buf(resp_buftype, rsp);
++
++ if (is_replayable_error(rc) &&
++ smb2_should_replay(io_parms->tcon, &retries, &cur_sleep))
++ goto replay_again;
++
+ return rc;
+ }
+
+@@ -5242,8 +5356,14 @@ SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon,
+ struct kvec rsp_iov;
+ int rc = 0;
+ struct cifs_ses *ses = tcon->ses;
+- struct TCP_Server_Info *server = cifs_pick_channel(ses);
++ struct TCP_Server_Info *server;
+ int flags = 0;
++ int retries = 0, cur_sleep = 1;
++
++replay_again:
++ /* reinitialize for possible replay */
++ flags = 0;
++ server = cifs_pick_channel(ses);
+
+ if (!ses || !(ses->server))
+ return -EIO;
+@@ -5263,6 +5383,9 @@ SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon,
+ if (rc)
+ goto qdir_exit;
+
++ if (retries)
++ smb2_set_replay(server, &rqst);
++
+ rc = cifs_send_recv(xid, ses, server,
+ &rqst, &resp_buftype, flags, &rsp_iov);
+ rsp = (struct smb2_query_directory_rsp *)rsp_iov.iov_base;
+@@ -5297,6 +5420,11 @@ SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon,
+ qdir_exit:
+ SMB2_query_directory_free(&rqst);
+ free_rsp_buf(resp_buftype, rsp);
++
++ if (is_replayable_error(rc) &&
++ smb2_should_replay(tcon, &retries, &cur_sleep))
++ goto replay_again;
++
+ return rc;
+ }
+
+@@ -5363,8 +5491,14 @@ send_set_info(const unsigned int xid, struct cifs_tcon *tcon,
+ int rc = 0;
+ int resp_buftype;
+ struct cifs_ses *ses = tcon->ses;
+- struct TCP_Server_Info *server = cifs_pick_channel(ses);
++ struct TCP_Server_Info *server;
+ int flags = 0;
++ int retries = 0, cur_sleep = 1;
++
++replay_again:
++ /* reinitialize for possible replay */
++ flags = 0;
++ server = cifs_pick_channel(ses);
+
+ if (!ses || !server)
+ return -EIO;
+@@ -5392,6 +5526,8 @@ send_set_info(const unsigned int xid, struct cifs_tcon *tcon,
+ return rc;
+ }
+
++ if (retries)
++ smb2_set_replay(server, &rqst);
+
+ rc = cifs_send_recv(xid, ses, server,
+ &rqst, &resp_buftype, flags,
+@@ -5407,6 +5543,11 @@ send_set_info(const unsigned int xid, struct cifs_tcon *tcon,
+
+ free_rsp_buf(resp_buftype, rsp);
+ kfree(iov);
++
++ if (is_replayable_error(rc) &&
++ smb2_should_replay(tcon, &retries, &cur_sleep))
++ goto replay_again;
++
+ return rc;
+ }
+
+@@ -5459,12 +5600,18 @@ SMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon,
+ int rc;
+ struct smb2_oplock_break *req = NULL;
+ struct cifs_ses *ses = tcon->ses;
+- struct TCP_Server_Info *server = cifs_pick_channel(ses);
++ struct TCP_Server_Info *server;
+ int flags = CIFS_OBREAK_OP;
+ unsigned int total_len;
+ struct kvec iov[1];
+ struct kvec rsp_iov;
+ int resp_buf_type;
++ int retries = 0, cur_sleep = 1;
++
++replay_again:
++ /* reinitialize for possible replay */
++ flags = CIFS_OBREAK_OP;
++ server = cifs_pick_channel(ses);
+
+ cifs_dbg(FYI, "SMB2_oplock_break\n");
+ rc = smb2_plain_req_init(SMB2_OPLOCK_BREAK, tcon, server,
+@@ -5489,15 +5636,21 @@ SMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon,
+ rqst.rq_iov = iov;
+ rqst.rq_nvec = 1;
+
++ if (retries)
++ smb2_set_replay(server, &rqst);
++
+ rc = cifs_send_recv(xid, ses, server,
+ &rqst, &resp_buf_type, flags, &rsp_iov);
+ cifs_small_buf_release(req);
+-
+ if (rc) {
+ cifs_stats_fail_inc(tcon, SMB2_OPLOCK_BREAK_HE);
+ cifs_dbg(FYI, "Send error in Oplock Break = %d\n", rc);
+ }
+
++ if (is_replayable_error(rc) &&
++ smb2_should_replay(tcon, &retries, &cur_sleep))
++ goto replay_again;
++
+ return rc;
+ }
+
+@@ -5583,9 +5736,15 @@ SMB311_posix_qfs_info(const unsigned int xid, struct cifs_tcon *tcon,
+ int rc = 0;
+ int resp_buftype;
+ struct cifs_ses *ses = tcon->ses;
+- struct TCP_Server_Info *server = cifs_pick_channel(ses);
++ struct TCP_Server_Info *server;
+ FILE_SYSTEM_POSIX_INFO *info = NULL;
+ int flags = 0;
++ int retries = 0, cur_sleep = 1;
++
++replay_again:
++ /* reinitialize for possible replay */
++ flags = 0;
++ server = cifs_pick_channel(ses);
+
+ rc = build_qfs_info_req(&iov, tcon, server,
+ FS_POSIX_INFORMATION,
+@@ -5601,6 +5760,9 @@ SMB311_posix_qfs_info(const unsigned int xid, struct cifs_tcon *tcon,
+ rqst.rq_iov = &iov;
+ rqst.rq_nvec = 1;
+
++ if (retries)
++ smb2_set_replay(server, &rqst);
++
+ rc = cifs_send_recv(xid, ses, server,
+ &rqst, &resp_buftype, flags, &rsp_iov);
+ free_qfs_info_req(&iov);
+@@ -5620,6 +5782,11 @@ SMB311_posix_qfs_info(const unsigned int xid, struct cifs_tcon *tcon,
+
+ posix_qfsinf_exit:
+ free_rsp_buf(resp_buftype, rsp_iov.iov_base);
++
++ if (is_replayable_error(rc) &&
++ smb2_should_replay(tcon, &retries, &cur_sleep))
++ goto replay_again;
++
+ return rc;
+ }
+
+@@ -5634,9 +5801,15 @@ SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon,
+ int rc = 0;
+ int resp_buftype;
+ struct cifs_ses *ses = tcon->ses;
+- struct TCP_Server_Info *server = cifs_pick_channel(ses);
++ struct TCP_Server_Info *server;
+ struct smb2_fs_full_size_info *info = NULL;
+ int flags = 0;
++ int retries = 0, cur_sleep = 1;
++
++replay_again:
++ /* reinitialize for possible replay */
++ flags = 0;
++ server = cifs_pick_channel(ses);
+
+ rc = build_qfs_info_req(&iov, tcon, server,
+ FS_FULL_SIZE_INFORMATION,
+@@ -5652,6 +5825,9 @@ SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon,
+ rqst.rq_iov = &iov;
+ rqst.rq_nvec = 1;
+
++ if (retries)
++ smb2_set_replay(server, &rqst);
++
+ rc = cifs_send_recv(xid, ses, server,
+ &rqst, &resp_buftype, flags, &rsp_iov);
+ free_qfs_info_req(&iov);
+@@ -5671,6 +5847,11 @@ SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon,
+
+ qfsinf_exit:
+ free_rsp_buf(resp_buftype, rsp_iov.iov_base);
++
++ if (is_replayable_error(rc) &&
++ smb2_should_replay(tcon, &retries, &cur_sleep))
++ goto replay_again;
++
+ return rc;
+ }
+
+@@ -5685,9 +5866,15 @@ SMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon,
+ int rc = 0;
+ int resp_buftype, max_len, min_len;
+ struct cifs_ses *ses = tcon->ses;
+- struct TCP_Server_Info *server = cifs_pick_channel(ses);
++ struct TCP_Server_Info *server;
+ unsigned int rsp_len, offset;
+ int flags = 0;
++ int retries = 0, cur_sleep = 1;
++
++replay_again:
++ /* reinitialize for possible replay */
++ flags = 0;
++ server = cifs_pick_channel(ses);
+
+ if (level == FS_DEVICE_INFORMATION) {
+ max_len = sizeof(FILE_SYSTEM_DEVICE_INFO);
+@@ -5719,6 +5906,9 @@ SMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon,
+ rqst.rq_iov = &iov;
+ rqst.rq_nvec = 1;
+
++ if (retries)
++ smb2_set_replay(server, &rqst);
++
+ rc = cifs_send_recv(xid, ses, server,
+ &rqst, &resp_buftype, flags, &rsp_iov);
+ free_qfs_info_req(&iov);
+@@ -5756,6 +5946,11 @@ SMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon,
+
+ qfsattr_exit:
+ free_rsp_buf(resp_buftype, rsp_iov.iov_base);
++
++ if (is_replayable_error(rc) &&
++ smb2_should_replay(tcon, &retries, &cur_sleep))
++ goto replay_again;
++
+ return rc;
+ }
+
+@@ -5773,7 +5968,13 @@ smb2_lockv(const unsigned int xid, struct cifs_tcon *tcon,
+ unsigned int count;
+ int flags = CIFS_NO_RSP_BUF;
+ unsigned int total_len;
+- struct TCP_Server_Info *server = cifs_pick_channel(tcon->ses);
++ struct TCP_Server_Info *server;
++ int retries = 0, cur_sleep = 1;
++
++replay_again:
++ /* reinitialize for possible replay */
++ flags = CIFS_NO_RSP_BUF;
++ server = cifs_pick_channel(tcon->ses);
+
+ cifs_dbg(FYI, "smb2_lockv num lock %d\n", num_lock);
+
+@@ -5804,6 +6005,9 @@ smb2_lockv(const unsigned int xid, struct cifs_tcon *tcon,
+ rqst.rq_iov = iov;
+ rqst.rq_nvec = 2;
+
++ if (retries)
++ smb2_set_replay(server, &rqst);
++
+ rc = cifs_send_recv(xid, tcon->ses, server,
+ &rqst, &resp_buf_type, flags,
+ &rsp_iov);
+@@ -5815,6 +6019,10 @@ smb2_lockv(const unsigned int xid, struct cifs_tcon *tcon,
+ tcon->ses->Suid, rc);
+ }
+
++ if (is_replayable_error(rc) &&
++ smb2_should_replay(tcon, &retries, &cur_sleep))
++ goto replay_again;
++
+ return rc;
+ }
+
+diff --git a/fs/smb/client/smb2proto.h b/fs/smb/client/smb2proto.h
+index 343ada691e763..330e36c6b91f0 100644
+--- a/fs/smb/client/smb2proto.h
++++ b/fs/smb/client/smb2proto.h
+@@ -122,6 +122,11 @@ extern unsigned long smb_rqst_len(struct TCP_Server_Info *server,
+ extern void smb2_set_next_command(struct cifs_tcon *tcon,
+ struct smb_rqst *rqst);
+ extern void smb2_set_related(struct smb_rqst *rqst);
++extern void smb2_set_replay(struct TCP_Server_Info *server,
++ struct smb_rqst *rqst);
++extern bool smb2_should_replay(struct cifs_tcon *tcon,
++ int *pretries,
++ int *pcur_sleep);
+
+ /*
+ * SMB2 Worker functions - most of protocol specific implementation details
+--
+2.43.0
+
diff --git a/queue-6.6/cifs-defer-close-file-handles-having-rh-lease.patch b/queue-6.6/cifs-defer-close-file-handles-having-rh-lease.patch
new file mode 100644
index 0000000000..dc4ccf755d
--- /dev/null
+++ b/queue-6.6/cifs-defer-close-file-handles-having-rh-lease.patch
@@ -0,0 +1,60 @@
+From 50097cc1be7e31fc78529d359f31ed04fde87957 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 12 Mar 2024 21:21:41 -0500
+Subject: cifs: defer close file handles having RH lease
+
+From: Bharath SM <bharathsm@microsoft.com>
+
+[ Upstream commit dc528770edb138e26a533f8a77de5c4db18ea7f3 ]
+
+Previously we only deferred closing file handles with RHW
+lease. To enhance performance benefits from deferred closes,
+we now include handles with RH leases as well.
+
+Signed-off-by: Bharath SM <bharathsm@microsoft.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/file.c | 19 +++++++++++++++----
+ 1 file changed, 15 insertions(+), 4 deletions(-)
+
+diff --git a/fs/smb/client/file.c b/fs/smb/client/file.c
+index 8eaf195ef5604..6dc2e8db9c8ed 100644
+--- a/fs/smb/client/file.c
++++ b/fs/smb/client/file.c
+@@ -1152,6 +1152,19 @@ void smb2_deferred_work_close(struct work_struct *work)
+ _cifsFileInfo_put(cfile, true, false);
+ }
+
++static bool
++smb2_can_defer_close(struct inode *inode, struct cifs_deferred_close *dclose)
++{
++ struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
++ struct cifsInodeInfo *cinode = CIFS_I(inode);
++
++ return (cifs_sb->ctx->closetimeo && cinode->lease_granted && dclose &&
++ (cinode->oplock == CIFS_CACHE_RHW_FLG ||
++ cinode->oplock == CIFS_CACHE_RH_FLG) &&
++ !test_bit(CIFS_INO_CLOSE_ON_LOCK, &cinode->flags));
++
++}
++
+ int cifs_close(struct inode *inode, struct file *file)
+ {
+ struct cifsFileInfo *cfile;
+@@ -1165,10 +1178,8 @@ int cifs_close(struct inode *inode, struct file *file)
+ cfile = file->private_data;
+ file->private_data = NULL;
+ dclose = kmalloc(sizeof(struct cifs_deferred_close), GFP_KERNEL);
+- if ((cifs_sb->ctx->closetimeo && cinode->oplock == CIFS_CACHE_RHW_FLG)
+- && cinode->lease_granted &&
+- !test_bit(CIFS_INO_CLOSE_ON_LOCK, &cinode->flags) &&
+- dclose && !(cfile->status_file_deleted)) {
++ if ((cfile->status_file_deleted == false) &&
++ (smb2_can_defer_close(inode, dclose))) {
+ if (test_and_clear_bit(CIFS_INO_MODIFIED_ATTR, &cinode->flags)) {
+ inode_set_mtime_to_ts(inode,
+ inode_set_ctime_current(inode));
+--
+2.43.0
+
diff --git a/queue-6.6/cifs-fix-in-logging-in-cifs_chan_update_iface.patch b/queue-6.6/cifs-fix-in-logging-in-cifs_chan_update_iface.patch
new file mode 100644
index 0000000000..4196638d42
--- /dev/null
+++ b/queue-6.6/cifs-fix-in-logging-in-cifs_chan_update_iface.patch
@@ -0,0 +1,59 @@
+From cfa7dbf28370fb91b117b58ff6ed1578ef84f9e2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 27 Apr 2024 20:05:11 -0500
+Subject: cifs: fix in logging in cifs_chan_update_iface
+
+From: Steve French <stfrench@microsoft.com>
+
+[ Upstream commit 516eea97f92f1e7271f20835cfe9e73774b0f8cc ]
+
+Recently, cifs_chan_update_iface was modified to not
+remove an iface if a suitable replacement was not found.
+With that, there were two conditionals that were exactly
+the same. This change removes that extra condition check.
+
+Also, fixed a logging in the same function to indicate
+the correct message.
+
+Signed-off-by: Shyam Prasad N <sprasad@microsoft.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/sess.c | 14 ++++++++------
+ 1 file changed, 8 insertions(+), 6 deletions(-)
+
+diff --git a/fs/smb/client/sess.c b/fs/smb/client/sess.c
+index f3d25395d0d3c..5de32640f0265 100644
+--- a/fs/smb/client/sess.c
++++ b/fs/smb/client/sess.c
+@@ -442,8 +442,14 @@ cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server)
+ }
+
+ if (!iface) {
+- cifs_dbg(FYI, "unable to get the interface matching: %pIS\n",
+- &ss);
++ if (!chan_index)
++ cifs_dbg(FYI, "unable to get the interface matching: %pIS\n",
++ &ss);
++ else {
++ cifs_dbg(FYI, "unable to find another interface to replace: %pIS\n",
++ &old_iface->sockaddr);
++ }
++
+ spin_unlock(&ses->iface_lock);
+ return;
+ }
+@@ -461,10 +467,6 @@ cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server)
+ iface->weight_fulfilled++;
+
+ kref_put(&old_iface->refcount, release_iface);
+- } else if (old_iface) {
+- /* if a new candidate is not found, keep things as is */
+- cifs_dbg(FYI, "could not replace iface: %pIS\n",
+- &old_iface->sockaddr);
+ } else if (!chan_index) {
+ /* special case: update interface for primary channel */
+ cifs_dbg(FYI, "referencing primary channel iface: %pIS\n",
+--
+2.43.0
+
diff --git a/queue-6.6/cifs-fix-use-after-free-for-iface-while-disabling-se.patch b/queue-6.6/cifs-fix-use-after-free-for-iface-while-disabling-se.patch
new file mode 100644
index 0000000000..4c4cb5de5c
--- /dev/null
+++ b/queue-6.6/cifs-fix-use-after-free-for-iface-while-disabling-se.patch
@@ -0,0 +1,41 @@
+From b898e3355dbd14d9a30fd87f0c85ae0bba789dca Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 21 Nov 2023 19:13:47 +0530
+Subject: cifs: fix use after free for iface while disabling secondary channels
+
+From: Ritvik Budhiraja <rbudhiraja@microsoft.com>
+
+[ Upstream commit a15ccef82d3de9a37dc25898c60a394209368dc8 ]
+
+We were deferencing iface after it has been released. Fix is to
+release after all dereference instances have been encountered.
+
+Signed-off-by: Ritvik Budhiraja <rbudhiraja@microsoft.com>
+Reported-by: kernel test robot <lkp@intel.com>
+Reported-by: Dan Carpenter <error27@gmail.com>
+Closes: https://lore.kernel.org/r/202311110815.UJaeU3Tt-lkp@intel.com/
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/sess.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/fs/smb/client/sess.c b/fs/smb/client/sess.c
+index 09bb30610a901..f3d25395d0d3c 100644
+--- a/fs/smb/client/sess.c
++++ b/fs/smb/client/sess.c
+@@ -337,10 +337,10 @@ cifs_disable_secondary_channels(struct cifs_ses *ses)
+
+ if (iface) {
+ spin_lock(&ses->iface_lock);
+- kref_put(&iface->refcount, release_iface);
+ iface->num_channels--;
+ if (iface->weight_fulfilled)
+ iface->weight_fulfilled--;
++ kref_put(&iface->refcount, release_iface);
+ spin_unlock(&ses->iface_lock);
+ }
+
+--
+2.43.0
+
diff --git a/queue-6.6/cifs-fixes-for-get_inode_info.patch b/queue-6.6/cifs-fixes-for-get_inode_info.patch
new file mode 100644
index 0000000000..96c07677c0
--- /dev/null
+++ b/queue-6.6/cifs-fixes-for-get_inode_info.patch
@@ -0,0 +1,118 @@
+From 5cda8d3e89ed5f9f2bc9fbe3212f52924e5add87 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 14 Mar 2024 08:05:49 -0400
+Subject: cifs: fixes for get_inode_info
+
+From: Meetakshi Setiya <msetiya@microsoft.com>
+
+[ Upstream commit fc20c523211a38b87fc850a959cb2149e4fd64b0 ]
+
+Fix potential memory leaks, add error checking, remove unnecessary
+initialisation of status_file_deleted and do not use cifs_iget() to get
+inode in reparse_info_to_fattr since fattrs may not be fully set.
+
+Fixes: ffceb7640cbf ("smb: client: do not defer close open handles to deleted files")
+Reported-by: Paulo Alcantara <pc@manguebit.com>
+Signed-off-by: Meetakshi Setiya <msetiya@microsoft.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/file.c | 1 -
+ fs/smb/client/inode.c | 24 +++++++++++++-----------
+ 2 files changed, 13 insertions(+), 12 deletions(-)
+
+diff --git a/fs/smb/client/file.c b/fs/smb/client/file.c
+index 6dc2e8db9c8ed..7ea8c3cf70f6c 100644
+--- a/fs/smb/client/file.c
++++ b/fs/smb/client/file.c
+@@ -501,7 +501,6 @@ struct cifsFileInfo *cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
+ cfile->uid = current_fsuid();
+ cfile->dentry = dget(dentry);
+ cfile->f_flags = file->f_flags;
+- cfile->status_file_deleted = false;
+ cfile->invalidHandle = false;
+ cfile->deferred_close_scheduled = false;
+ cfile->tlink = cifs_get_tlink(tlink);
+diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c
+index 67bc1a1e54fde..67ad8eeaa7665 100644
+--- a/fs/smb/client/inode.c
++++ b/fs/smb/client/inode.c
+@@ -820,8 +820,10 @@ cifs_get_file_info(struct file *filp)
+ void *page = alloc_dentry_path();
+ const unsigned char *path;
+
+- if (!server->ops->query_file_info)
++ if (!server->ops->query_file_info) {
++ free_dentry_path(page);
+ return -ENOSYS;
++ }
+
+ xid = get_xid();
+ rc = server->ops->query_file_info(xid, tcon, cfile, &data);
+@@ -835,8 +837,8 @@ cifs_get_file_info(struct file *filp)
+ }
+ path = build_path_from_dentry(dentry, page);
+ if (IS_ERR(path)) {
+- free_dentry_path(page);
+- return PTR_ERR(path);
++ rc = PTR_ERR(path);
++ goto cgfi_exit;
+ }
+ cifs_open_info_to_fattr(&fattr, &data, inode->i_sb);
+ if (fattr.cf_flags & CIFS_FATTR_DELETE_PENDING)
+@@ -1009,7 +1011,6 @@ static int reparse_info_to_fattr(struct cifs_open_info_data *data,
+ struct kvec rsp_iov, *iov = NULL;
+ int rsp_buftype = CIFS_NO_BUFFER;
+ u32 tag = data->reparse.tag;
+- struct inode *inode = NULL;
+ int rc = 0;
+
+ if (!tag && server->ops->query_reparse_point) {
+@@ -1049,12 +1050,8 @@ static int reparse_info_to_fattr(struct cifs_open_info_data *data,
+
+ if (tcon->posix_extensions)
+ smb311_posix_info_to_fattr(fattr, data, sb);
+- else {
++ else
+ cifs_open_info_to_fattr(fattr, data, sb);
+- inode = cifs_iget(sb, fattr);
+- if (inode && fattr->cf_flags & CIFS_FATTR_DELETE_PENDING)
+- cifs_mark_open_handles_for_deleted_file(inode, full_path);
+- }
+ out:
+ fattr->cf_cifstag = data->reparse.tag;
+ free_rsp_buf(rsp_buftype, rsp_iov.iov_base);
+@@ -1109,9 +1106,9 @@ static int cifs_get_fattr(struct cifs_open_info_data *data,
+ full_path, fattr);
+ } else {
+ cifs_open_info_to_fattr(fattr, data, sb);
+- if (fattr->cf_flags & CIFS_FATTR_DELETE_PENDING)
+- cifs_mark_open_handles_for_deleted_file(*inode, full_path);
+ }
++ if (!rc && fattr->cf_flags & CIFS_FATTR_DELETE_PENDING)
++ cifs_mark_open_handles_for_deleted_file(*inode, full_path);
+ break;
+ case -EREMOTE:
+ /* DFS link, no metadata available on this server */
+@@ -1340,6 +1337,8 @@ int smb311_posix_get_inode_info(struct inode **inode,
+ goto out;
+
+ rc = update_inode_info(sb, &fattr, inode);
++ if (!rc && fattr.cf_flags & CIFS_FATTR_DELETE_PENDING)
++ cifs_mark_open_handles_for_deleted_file(*inode, full_path);
+ out:
+ kfree(fattr.cf_symlink_target);
+ return rc;
+@@ -1503,6 +1502,9 @@ struct inode *cifs_root_iget(struct super_block *sb)
+ goto out;
+ }
+
++ if (!rc && fattr.cf_flags & CIFS_FATTR_DELETE_PENDING)
++ cifs_mark_open_handles_for_deleted_file(inode, path);
++
+ if (rc && tcon->pipe) {
+ cifs_dbg(FYI, "ipc connection - fake read inode\n");
+ spin_lock(&inode->i_lock);
+--
+2.43.0
+
diff --git a/queue-6.6/cifs-get-rid-of-dup-length-check-in-parse_reparse_po.patch b/queue-6.6/cifs-get-rid-of-dup-length-check-in-parse_reparse_po.patch
new file mode 100644
index 0000000000..c80659bec9
--- /dev/null
+++ b/queue-6.6/cifs-get-rid-of-dup-length-check-in-parse_reparse_po.patch
@@ -0,0 +1,86 @@
+From 5aeca997d903af8749065cb291a4ae314dc095a5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 6 Jan 2024 20:05:18 -0300
+Subject: cifs: get rid of dup length check in parse_reparse_point()
+
+From: Paulo Alcantara <pc@manguebit.com>
+
+[ Upstream commit 8a3c4e44c243308c2364a00f9944c3d6fbdeb125 ]
+
+smb2_compound_op(SMB2_OP_GET_REPARSE) already checks if ioctl response
+has a valid reparse data buffer's length, so there's no need to check
+it again in parse_reparse_point().
+
+In order to get rid of duplicate check, validate reparse data buffer's
+length also in cifs_query_reparse_point().
+
+Signed-off-by: Paulo Alcantara (SUSE) <pc@manguebit.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/cifssmb.c | 14 ++++++++++++--
+ fs/smb/client/smb2ops.c | 12 ------------
+ 2 files changed, 12 insertions(+), 14 deletions(-)
+
+diff --git a/fs/smb/client/cifssmb.c b/fs/smb/client/cifssmb.c
+index e9e33b0b3ac47..01e89070df5ab 100644
+--- a/fs/smb/client/cifssmb.c
++++ b/fs/smb/client/cifssmb.c
+@@ -2700,11 +2700,12 @@ int cifs_query_reparse_point(const unsigned int xid,
+ u32 *tag, struct kvec *rsp,
+ int *rsp_buftype)
+ {
++ struct reparse_data_buffer *buf;
+ struct cifs_open_parms oparms;
+ TRANSACT_IOCTL_REQ *io_req = NULL;
+ TRANSACT_IOCTL_RSP *io_rsp = NULL;
+ struct cifs_fid fid;
+- __u32 data_offset, data_count;
++ __u32 data_offset, data_count, len;
+ __u8 *start, *end;
+ int io_rsp_len;
+ int oplock = 0;
+@@ -2774,7 +2775,16 @@ int cifs_query_reparse_point(const unsigned int xid,
+ goto error;
+ }
+
+- *tag = le32_to_cpu(((struct reparse_data_buffer *)start)->ReparseTag);
++ data_count = le16_to_cpu(io_rsp->ByteCount);
++ buf = (struct reparse_data_buffer *)start;
++ len = sizeof(*buf);
++ if (data_count < len ||
++ data_count < le16_to_cpu(buf->ReparseDataLength) + len) {
++ rc = -EIO;
++ goto error;
++ }
++
++ *tag = le32_to_cpu(buf->ReparseTag);
+ rsp->iov_base = io_rsp;
+ rsp->iov_len = io_rsp_len;
+ *rsp_buftype = CIFS_LARGE_BUFFER;
+diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
+index 9b7cdb7d7ece8..3e07ab1564ea7 100644
+--- a/fs/smb/client/smb2ops.c
++++ b/fs/smb/client/smb2ops.c
+@@ -2946,18 +2946,6 @@ int parse_reparse_point(struct reparse_data_buffer *buf,
+ u32 plen, struct cifs_sb_info *cifs_sb,
+ bool unicode, struct cifs_open_info_data *data)
+ {
+- if (plen < sizeof(*buf)) {
+- cifs_dbg(VFS, "%s: reparse buffer is too small. Must be at least 8 bytes but was %d\n",
+- __func__, plen);
+- return -EIO;
+- }
+-
+- if (plen < le16_to_cpu(buf->ReparseDataLength) + sizeof(*buf)) {
+- cifs_dbg(VFS, "%s: invalid reparse buf length: %d\n",
+- __func__, plen);
+- return -EIO;
+- }
+-
+ data->reparse.buf = buf;
+
+ /* See MS-FSCC 2.1.2 */
+--
+2.43.0
+
diff --git a/queue-6.6/cifs-minor-comment-cleanup.patch b/queue-6.6/cifs-minor-comment-cleanup.patch
new file mode 100644
index 0000000000..a415c3f0e5
--- /dev/null
+++ b/queue-6.6/cifs-minor-comment-cleanup.patch
@@ -0,0 +1,39 @@
+From 3d93dca3f404c16f29dfcba65556309ffa68bb1c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 17 Jan 2024 16:56:05 -0600
+Subject: cifs: minor comment cleanup
+
+From: Steve French <stfrench@microsoft.com>
+
+[ Upstream commit 0b549c4f594167d7ef056393c6a06ac77f5690ff ]
+
+minor comment cleanup and trivial camelCase removal
+
+Reviewed-by: Bharath SM <bharathsm@microsoft.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/readdir.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/fs/smb/client/readdir.c b/fs/smb/client/readdir.c
+index 56033e4e4bae9..47f5a82bc2507 100644
+--- a/fs/smb/client/readdir.c
++++ b/fs/smb/client/readdir.c
+@@ -647,10 +647,10 @@ static int cifs_entry_is_dot(struct cifs_dirent *de, bool is_unicode)
+ static int is_dir_changed(struct file *file)
+ {
+ struct inode *inode = file_inode(file);
+- struct cifsInodeInfo *cifsInfo = CIFS_I(inode);
++ struct cifsInodeInfo *cifs_inode_info = CIFS_I(inode);
+
+- if (cifsInfo->time == 0)
+- return 1; /* directory was changed, perhaps due to unlink */
++ if (cifs_inode_info->time == 0)
++ return 1; /* directory was changed, e.g. unlink or new file */
+ else
+ return 0;
+
+--
+2.43.0
+
diff --git a/queue-6.6/cifs-move-some-extern-decls-from-.c-files-to-.h.patch b/queue-6.6/cifs-move-some-extern-decls-from-.c-files-to-.h.patch
new file mode 100644
index 0000000000..33a2d5962c
--- /dev/null
+++ b/queue-6.6/cifs-move-some-extern-decls-from-.c-files-to-.h.patch
@@ -0,0 +1,88 @@
+From eee592d5b525e0f16fca02820a17e5df04d5e8c6 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 1 May 2024 01:39:48 -0500
+Subject: cifs: Move some extern decls from .c files to .h
+
+From: Steve French <stfrench@microsoft.com>
+
+[ Upstream commit 5b142b37c70b1fa6936fa2d0babb0b8c16767d3a ]
+
+Move the following:
+
+ extern mempool_t *cifs_sm_req_poolp;
+ extern mempool_t *cifs_req_poolp;
+ extern mempool_t *cifs_mid_poolp;
+ extern bool disable_legacy_dialects;
+
+from various .c files to cifsglob.h.
+
+Signed-off-by: David Howells <dhowells@redhat.com>
+cc: linux-cifs@vger.kernel.org
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/cifsfs.c | 4 ----
+ fs/smb/client/cifsglob.h | 2 ++
+ fs/smb/client/connect.c | 3 ---
+ fs/smb/client/misc.c | 3 ---
+ 4 files changed, 2 insertions(+), 10 deletions(-)
+
+diff --git a/fs/smb/client/cifsfs.c b/fs/smb/client/cifsfs.c
+index 30bf754c9fc93..539ac9774de1b 100644
+--- a/fs/smb/client/cifsfs.c
++++ b/fs/smb/client/cifsfs.c
+@@ -150,10 +150,6 @@ MODULE_PARM_DESC(disable_legacy_dialects, "To improve security it may be "
+ "vers=1.0 (CIFS/SMB1) and vers=2.0 are weaker"
+ " and less secure. Default: n/N/0");
+
+-extern mempool_t *cifs_sm_req_poolp;
+-extern mempool_t *cifs_req_poolp;
+-extern mempool_t *cifs_mid_poolp;
+-
+ struct workqueue_struct *cifsiod_wq;
+ struct workqueue_struct *decrypt_wq;
+ struct workqueue_struct *fileinfo_put_wq;
+diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h
+index 70a12584375de..9597887280ff3 100644
+--- a/fs/smb/client/cifsglob.h
++++ b/fs/smb/client/cifsglob.h
+@@ -2112,6 +2112,8 @@ extern struct workqueue_struct *deferredclose_wq;
+ extern struct workqueue_struct *serverclose_wq;
+ extern __u32 cifs_lock_secret;
+
++extern mempool_t *cifs_sm_req_poolp;
++extern mempool_t *cifs_req_poolp;
+ extern mempool_t *cifs_mid_poolp;
+
+ /* Operations for different SMB versions */
+diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c
+index 5acfd2057ca04..4e35970681bf0 100644
+--- a/fs/smb/client/connect.c
++++ b/fs/smb/client/connect.c
+@@ -52,9 +52,6 @@
+ #include "fs_context.h"
+ #include "cifs_swn.h"
+
+-extern mempool_t *cifs_req_poolp;
+-extern bool disable_legacy_dialects;
+-
+ /* FIXME: should these be tunable? */
+ #define TLINK_ERROR_EXPIRE (1 * HZ)
+ #define TLINK_IDLE_EXPIRE (600 * HZ)
+diff --git a/fs/smb/client/misc.c b/fs/smb/client/misc.c
+index 669d27b4d414a..ad44f8d66b377 100644
+--- a/fs/smb/client/misc.c
++++ b/fs/smb/client/misc.c
+@@ -27,9 +27,6 @@
+ #include "fs_context.h"
+ #include "cached_dir.h"
+
+-extern mempool_t *cifs_sm_req_poolp;
+-extern mempool_t *cifs_req_poolp;
+-
+ /* The xid serves as a useful identifier for each incoming vfs request,
+ in a similar way to the mid which is useful to track each sent smb,
+ and CurrentXid can also provide a running counter (although it
+--
+2.43.0
+
diff --git a/queue-6.6/cifs-new-mount-option-called-retrans.patch b/queue-6.6/cifs-new-mount-option-called-retrans.patch
new file mode 100644
index 0000000000..8dd84c127e
--- /dev/null
+++ b/queue-6.6/cifs-new-mount-option-called-retrans.patch
@@ -0,0 +1,129 @@
+From 064744759440bad9725fe64bbf73886ed5d1e365 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 17 Jan 2024 06:09:16 +0000
+Subject: cifs: new mount option called retrans
+
+From: Shyam Prasad N <sprasad@microsoft.com>
+
+[ Upstream commit ce09f8d8a7130e6edfdd6fcad8eb277824d5de95 ]
+
+We have several places in the code where we treat the
+error -EAGAIN very differently. Some code retry for
+arbitrary number of times.
+
+Introducing this new mount option named "retrans", so
+that all these handlers of -EAGAIN can retry a fixed
+number of times. This applies only to soft mounts.
+
+Signed-off-by: Shyam Prasad N <sprasad@microsoft.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/cifsfs.c | 2 ++
+ fs/smb/client/cifsglob.h | 1 +
+ fs/smb/client/connect.c | 4 ++++
+ fs/smb/client/fs_context.c | 6 ++++++
+ fs/smb/client/fs_context.h | 2 ++
+ 5 files changed, 15 insertions(+)
+
+diff --git a/fs/smb/client/cifsfs.c b/fs/smb/client/cifsfs.c
+index 44e2cc37a8b63..6d9d2174ee691 100644
+--- a/fs/smb/client/cifsfs.c
++++ b/fs/smb/client/cifsfs.c
+@@ -682,6 +682,8 @@ cifs_show_options(struct seq_file *s, struct dentry *root)
+ seq_printf(s, ",rasize=%u", cifs_sb->ctx->rasize);
+ if (tcon->ses->server->min_offload)
+ seq_printf(s, ",esize=%u", tcon->ses->server->min_offload);
++ if (tcon->ses->server->retrans)
++ seq_printf(s, ",retrans=%u", tcon->ses->server->retrans);
+ seq_printf(s, ",echo_interval=%lu",
+ tcon->ses->server->echo_interval / HZ);
+
+diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h
+index 414648bf816b2..6acadb53ada79 100644
+--- a/fs/smb/client/cifsglob.h
++++ b/fs/smb/client/cifsglob.h
+@@ -754,6 +754,7 @@ struct TCP_Server_Info {
+ unsigned int max_read;
+ unsigned int max_write;
+ unsigned int min_offload;
++ unsigned int retrans;
+ __le16 compress_algorithm;
+ __u16 signing_algorithm;
+ __le16 cipher_type;
+diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c
+index 2466b28379ff8..e28f011f11d6c 100644
+--- a/fs/smb/client/connect.c
++++ b/fs/smb/client/connect.c
+@@ -1592,6 +1592,9 @@ static int match_server(struct TCP_Server_Info *server,
+ if (server->min_offload != ctx->min_offload)
+ return 0;
+
++ if (server->retrans != ctx->retrans)
++ return 0;
++
+ return 1;
+ }
+
+@@ -1816,6 +1819,7 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx,
+ goto out_err_crypto_release;
+ }
+ tcp_ses->min_offload = ctx->min_offload;
++ tcp_ses->retrans = ctx->retrans;
+ /*
+ * at this point we are the only ones with the pointer
+ * to the struct since the kernel thread not created yet
+diff --git a/fs/smb/client/fs_context.c b/fs/smb/client/fs_context.c
+index 4d9e57be84dbc..f119035a82725 100644
+--- a/fs/smb/client/fs_context.c
++++ b/fs/smb/client/fs_context.c
+@@ -139,6 +139,7 @@ const struct fs_parameter_spec smb3_fs_parameters[] = {
+ fsparam_u32("dir_mode", Opt_dirmode),
+ fsparam_u32("port", Opt_port),
+ fsparam_u32("min_enc_offload", Opt_min_enc_offload),
++ fsparam_u32("retrans", Opt_retrans),
+ fsparam_u32("esize", Opt_min_enc_offload),
+ fsparam_u32("bsize", Opt_blocksize),
+ fsparam_u32("rasize", Opt_rasize),
+@@ -1098,6 +1099,9 @@ static int smb3_fs_context_parse_param(struct fs_context *fc,
+ case Opt_min_enc_offload:
+ ctx->min_offload = result.uint_32;
+ break;
++ case Opt_retrans:
++ ctx->retrans = result.uint_32;
++ break;
+ case Opt_blocksize:
+ /*
+ * inode blocksize realistically should never need to be
+@@ -1678,6 +1682,8 @@ int smb3_init_fs_context(struct fs_context *fc)
+ ctx->backupuid_specified = false; /* no backup intent for a user */
+ ctx->backupgid_specified = false; /* no backup intent for a group */
+
++ ctx->retrans = 1;
++
+ /*
+ * short int override_uid = -1;
+ * short int override_gid = -1;
+diff --git a/fs/smb/client/fs_context.h b/fs/smb/client/fs_context.h
+index d7c090dbe75db..369a3fea1dfe0 100644
+--- a/fs/smb/client/fs_context.h
++++ b/fs/smb/client/fs_context.h
+@@ -118,6 +118,7 @@ enum cifs_param {
+ Opt_file_mode,
+ Opt_dirmode,
+ Opt_min_enc_offload,
++ Opt_retrans,
+ Opt_blocksize,
+ Opt_rasize,
+ Opt_rsize,
+@@ -249,6 +250,7 @@ struct smb3_fs_context {
+ unsigned int rsize;
+ unsigned int wsize;
+ unsigned int min_offload;
++ unsigned int retrans;
+ bool sockopt_tcp_nodelay:1;
+ /* attribute cache timemout for files and directories in jiffies */
+ unsigned long acregmax;
+--
+2.43.0
+
diff --git a/queue-6.6/cifs-new-nt-status-codes-from-ms-smb2.patch b/queue-6.6/cifs-new-nt-status-codes-from-ms-smb2.patch
new file mode 100644
index 0000000000..2d9bd1bef8
--- /dev/null
+++ b/queue-6.6/cifs-new-nt-status-codes-from-ms-smb2.patch
@@ -0,0 +1,53 @@
+From 294940c91aa1dab31a1b84d6e666fb7cc07fab2c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 17 Jan 2024 06:21:33 +0000
+Subject: cifs: new nt status codes from MS-SMB2
+
+From: Shyam Prasad N <sprasad@microsoft.com>
+
+[ Upstream commit 7f738527a7a03021c7e1b02e188f446845f05eb6 ]
+
+MS-SMB2 spec has introduced two new status codes,
+STATUS_SERVER_UNAVAILABLE and STATUS_FILE_NOT_AVAILABLE
+which are to be treated as retryable errors.
+
+This change adds these to the available mappings and
+maps them to Linux errno EAGAIN.
+
+Signed-off-by: Shyam Prasad N <sprasad@microsoft.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/smb2maperror.c | 2 ++
+ fs/smb/client/smb2status.h | 2 ++
+ 2 files changed, 4 insertions(+)
+
+diff --git a/fs/smb/client/smb2maperror.c b/fs/smb/client/smb2maperror.c
+index 1a90dd78b238f..ac1895358908a 100644
+--- a/fs/smb/client/smb2maperror.c
++++ b/fs/smb/client/smb2maperror.c
+@@ -1210,6 +1210,8 @@ static const struct status_to_posix_error smb2_error_map_table[] = {
+ {STATUS_INVALID_TASK_INDEX, -EIO, "STATUS_INVALID_TASK_INDEX"},
+ {STATUS_THREAD_ALREADY_IN_TASK, -EIO, "STATUS_THREAD_ALREADY_IN_TASK"},
+ {STATUS_CALLBACK_BYPASS, -EIO, "STATUS_CALLBACK_BYPASS"},
++ {STATUS_SERVER_UNAVAILABLE, -EAGAIN, "STATUS_SERVER_UNAVAILABLE"},
++ {STATUS_FILE_NOT_AVAILABLE, -EAGAIN, "STATUS_FILE_NOT_AVAILABLE"},
+ {STATUS_PORT_CLOSED, -EIO, "STATUS_PORT_CLOSED"},
+ {STATUS_MESSAGE_LOST, -EIO, "STATUS_MESSAGE_LOST"},
+ {STATUS_INVALID_MESSAGE, -EIO, "STATUS_INVALID_MESSAGE"},
+diff --git a/fs/smb/client/smb2status.h b/fs/smb/client/smb2status.h
+index a9e958166fc53..9c6d79b0bd497 100644
+--- a/fs/smb/client/smb2status.h
++++ b/fs/smb/client/smb2status.h
+@@ -982,6 +982,8 @@ struct ntstatus {
+ #define STATUS_INVALID_TASK_INDEX cpu_to_le32(0xC0000501)
+ #define STATUS_THREAD_ALREADY_IN_TASK cpu_to_le32(0xC0000502)
+ #define STATUS_CALLBACK_BYPASS cpu_to_le32(0xC0000503)
++#define STATUS_SERVER_UNAVAILABLE cpu_to_le32(0xC0000466)
++#define STATUS_FILE_NOT_AVAILABLE cpu_to_le32(0xC0000467)
+ #define STATUS_PORT_CLOSED cpu_to_le32(0xC0000700)
+ #define STATUS_MESSAGE_LOST cpu_to_le32(0xC0000701)
+ #define STATUS_INVALID_MESSAGE cpu_to_le32(0xC0000702)
+--
+2.43.0
+
diff --git a/queue-6.6/cifs-pass-unbyteswapped-eof-value-into-smb2_set_eof.patch b/queue-6.6/cifs-pass-unbyteswapped-eof-value-into-smb2_set_eof.patch
new file mode 100644
index 0000000000..edad986a76
--- /dev/null
+++ b/queue-6.6/cifs-pass-unbyteswapped-eof-value-into-smb2_set_eof.patch
@@ -0,0 +1,193 @@
+From 7ce4a62b34ef5bb5258d8b5bb5c5111190fea8e7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 1 Jan 2024 15:40:10 +0000
+Subject: cifs: Pass unbyteswapped eof value into SMB2_set_eof()
+
+From: David Howells <dhowells@redhat.com>
+
+[ Upstream commit 6ebfede8d57a615dcbdec7e490faed585153f7f1 ]
+
+Change SMB2_set_eof() to take eof as CPU order rather than __le64 and pass
+it directly rather than by pointer. This moves the conversion down into
+SMB_set_eof() rather than all of its callers and means we don't need to
+undo it for the traceline.
+
+Signed-off-by: David Howells <dhowells@redhat.com>
+cc: Jeff Layton <jlayton@kernel.org>
+cc: linux-cifs@vger.kernel.org
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/smb2ops.c | 37 ++++++++++++++++---------------------
+ fs/smb/client/smb2pdu.c | 6 +++---
+ fs/smb/client/smb2proto.h | 2 +-
+ 3 files changed, 20 insertions(+), 25 deletions(-)
+
+diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
+index a623a720db9e0..9b7cdb7d7ece8 100644
+--- a/fs/smb/client/smb2ops.c
++++ b/fs/smb/client/smb2ops.c
+@@ -1932,7 +1932,6 @@ static int
+ smb2_set_file_size(const unsigned int xid, struct cifs_tcon *tcon,
+ struct cifsFileInfo *cfile, __u64 size, bool set_alloc)
+ {
+- __le64 eof = cpu_to_le64(size);
+ struct inode *inode;
+
+ /*
+@@ -1949,7 +1948,7 @@ smb2_set_file_size(const unsigned int xid, struct cifs_tcon *tcon,
+ }
+
+ return SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid,
+- cfile->fid.volatile_fid, cfile->pid, &eof);
++ cfile->fid.volatile_fid, cfile->pid, size);
+ }
+
+ static int
+@@ -3196,7 +3195,6 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
+ unsigned long long new_size;
+ long rc;
+ unsigned int xid;
+- __le64 eof;
+
+ xid = get_xid();
+
+@@ -3226,9 +3224,8 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
+ */
+ new_size = offset + len;
+ if (keep_size == false && (unsigned long long)i_size_read(inode) < new_size) {
+- eof = cpu_to_le64(new_size);
+ rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid,
+- cfile->fid.volatile_fid, cfile->pid, &eof);
++ cfile->fid.volatile_fid, cfile->pid, new_size);
+ if (rc >= 0) {
+ truncate_setsize(inode, new_size);
+ fscache_resize_cookie(cifs_inode_cookie(inode), new_size);
+@@ -3421,7 +3418,7 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
+ struct cifsFileInfo *cfile = file->private_data;
+ long rc = -EOPNOTSUPP;
+ unsigned int xid;
+- __le64 eof;
++ loff_t new_eof;
+
+ xid = get_xid();
+
+@@ -3450,14 +3447,14 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
+ if (cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE)
+ smb2_set_sparse(xid, tcon, cfile, inode, false);
+
+- eof = cpu_to_le64(off + len);
++ new_eof = off + len;
+ rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid,
+- cfile->fid.volatile_fid, cfile->pid, &eof);
++ cfile->fid.volatile_fid, cfile->pid, new_eof);
+ if (rc == 0) {
+- cifsi->server_eof = off + len;
+- cifs_setsize(inode, off + len);
++ cifsi->server_eof = new_eof;
++ cifs_setsize(inode, new_eof);
+ cifs_truncate_page(inode->i_mapping, inode->i_size);
+- truncate_setsize(inode, off + len);
++ truncate_setsize(inode, new_eof);
+ }
+ goto out;
+ }
+@@ -3548,8 +3545,7 @@ static long smb3_collapse_range(struct file *file, struct cifs_tcon *tcon,
+ struct inode *inode = file_inode(file);
+ struct cifsFileInfo *cfile = file->private_data;
+ struct cifsInodeInfo *cifsi = CIFS_I(inode);
+- __le64 eof;
+- loff_t old_eof;
++ loff_t old_eof, new_eof;
+
+ xid = get_xid();
+
+@@ -3574,9 +3570,9 @@ static long smb3_collapse_range(struct file *file, struct cifs_tcon *tcon,
+ if (rc < 0)
+ goto out_2;
+
+- eof = cpu_to_le64(old_eof - len);
++ new_eof = old_eof - len;
+ rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid,
+- cfile->fid.volatile_fid, cfile->pid, &eof);
++ cfile->fid.volatile_fid, cfile->pid, new_eof);
+ if (rc < 0)
+ goto out_2;
+
+@@ -3600,8 +3596,7 @@ static long smb3_insert_range(struct file *file, struct cifs_tcon *tcon,
+ unsigned int xid;
+ struct cifsFileInfo *cfile = file->private_data;
+ struct inode *inode = file_inode(file);
+- __le64 eof;
+- __u64 count, old_eof;
++ __u64 count, old_eof, new_eof;
+
+ xid = get_xid();
+
+@@ -3614,20 +3609,20 @@ static long smb3_insert_range(struct file *file, struct cifs_tcon *tcon,
+ }
+
+ count = old_eof - off;
+- eof = cpu_to_le64(old_eof + len);
++ new_eof = old_eof + len;
+
+ filemap_invalidate_lock(inode->i_mapping);
+- rc = filemap_write_and_wait_range(inode->i_mapping, off, old_eof + len - 1);
++ rc = filemap_write_and_wait_range(inode->i_mapping, off, new_eof - 1);
+ if (rc < 0)
+ goto out_2;
+ truncate_pagecache_range(inode, off, old_eof);
+
+ rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid,
+- cfile->fid.volatile_fid, cfile->pid, &eof);
++ cfile->fid.volatile_fid, cfile->pid, new_eof);
+ if (rc < 0)
+ goto out_2;
+
+- truncate_setsize(inode, old_eof + len);
++ truncate_setsize(inode, new_eof);
+ fscache_resize_cookie(cifs_inode_cookie(inode), i_size_read(inode));
+
+ rc = smb2_copychunk_range(xid, cfile, cfile, off, count, off + len);
+diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c
+index 70530108b9bb9..51ff9a967c503 100644
+--- a/fs/smb/client/smb2pdu.c
++++ b/fs/smb/client/smb2pdu.c
+@@ -5414,18 +5414,18 @@ send_set_info(const unsigned int xid, struct cifs_tcon *tcon,
+
+ int
+ SMB2_set_eof(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
+- u64 volatile_fid, u32 pid, __le64 *eof)
++ u64 volatile_fid, u32 pid, loff_t new_eof)
+ {
+ struct smb2_file_eof_info info;
+ void *data;
+ unsigned int size;
+
+- info.EndOfFile = *eof;
++ info.EndOfFile = cpu_to_le64(new_eof);
+
+ data = &info;
+ size = sizeof(struct smb2_file_eof_info);
+
+- trace_smb3_set_eof(xid, persistent_fid, tcon->tid, tcon->ses->Suid, le64_to_cpu(*eof));
++ trace_smb3_set_eof(xid, persistent_fid, tcon->tid, tcon->ses->Suid, new_eof);
+
+ return send_set_info(xid, tcon, persistent_fid, volatile_fid,
+ pid, FILE_END_OF_FILE_INFORMATION, SMB2_O_INFO_FILE,
+diff --git a/fs/smb/client/smb2proto.h b/fs/smb/client/smb2proto.h
+index 1e20f87a5f584..343ada691e763 100644
+--- a/fs/smb/client/smb2proto.h
++++ b/fs/smb/client/smb2proto.h
+@@ -221,7 +221,7 @@ extern int SMB2_query_directory_init(unsigned int xid, struct cifs_tcon *tcon,
+ extern void SMB2_query_directory_free(struct smb_rqst *rqst);
+ extern int SMB2_set_eof(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_fid, u64 volatile_fid, u32 pid,
+- __le64 *eof);
++ loff_t new_eof);
+ extern int SMB2_set_info_init(struct cifs_tcon *tcon,
+ struct TCP_Server_Info *server,
+ struct smb_rqst *rqst,
+--
+2.43.0
+
diff --git a/queue-6.6/cifs-pick-channel-for-tcon-and-tdis.patch b/queue-6.6/cifs-pick-channel-for-tcon-and-tdis.patch
new file mode 100644
index 0000000000..8b8ba87df4
--- /dev/null
+++ b/queue-6.6/cifs-pick-channel-for-tcon-and-tdis.patch
@@ -0,0 +1,71 @@
+From 9b4a681bd153df68f861c64d8fced290d9ab807a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 10 Jan 2024 10:48:36 +0000
+Subject: cifs: pick channel for tcon and tdis
+
+From: Shyam Prasad N <sprasad@microsoft.com>
+
+[ Upstream commit 268b8b5797becb242013fcd63173eb28c007c8ae ]
+
+Today, the tree connect and disconnect requests are
+sent on the primary channel only. However, the new
+multichannel logic allows the session to remain active
+even if one of the channels are alive. So a tree connect
+can now be triggered during a reconnect on any of
+its channels.
+
+This change changes tcon and tdis calls to pick an
+active channel instead of the first one.
+
+Signed-off-by: Shyam Prasad N <sprasad@microsoft.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/smb2pdu.c | 10 ++++------
+ 1 file changed, 4 insertions(+), 6 deletions(-)
+
+diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c
+index 3f9448b5ada9b..95b5b4bdb4b7f 100644
+--- a/fs/smb/client/smb2pdu.c
++++ b/fs/smb/client/smb2pdu.c
+@@ -2019,10 +2019,7 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree,
+ __le16 *unc_path = NULL;
+ int flags = 0;
+ unsigned int total_len;
+- struct TCP_Server_Info *server;
+-
+- /* always use master channel */
+- server = ses->server;
++ struct TCP_Server_Info *server = cifs_pick_channel(ses);
+
+ cifs_dbg(FYI, "TCON\n");
+
+@@ -2155,6 +2152,7 @@ SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon)
+ struct smb2_tree_disconnect_req *req; /* response is trivial */
+ int rc = 0;
+ struct cifs_ses *ses = tcon->ses;
++ struct TCP_Server_Info *server = cifs_pick_channel(ses);
+ int flags = 0;
+ unsigned int total_len;
+ struct kvec iov[1];
+@@ -2177,7 +2175,7 @@ SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon)
+
+ invalidate_all_cached_dirs(tcon);
+
+- rc = smb2_plain_req_init(SMB2_TREE_DISCONNECT, tcon, ses->server,
++ rc = smb2_plain_req_init(SMB2_TREE_DISCONNECT, tcon, server,
+ (void **) &req,
+ &total_len);
+ if (rc)
+@@ -2195,7 +2193,7 @@ SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon)
+ rqst.rq_iov = iov;
+ rqst.rq_nvec = 1;
+
+- rc = cifs_send_recv(xid, ses, ses->server,
++ rc = cifs_send_recv(xid, ses, server,
+ &rqst, &resp_buf_type, flags, &rsp_iov);
+ cifs_small_buf_release(req);
+ if (rc) {
+--
+2.43.0
+
diff --git a/queue-6.6/cifs-print-server-capabilities-in-debugdata.patch b/queue-6.6/cifs-print-server-capabilities-in-debugdata.patch
new file mode 100644
index 0000000000..4f29fb76c0
--- /dev/null
+++ b/queue-6.6/cifs-print-server-capabilities-in-debugdata.patch
@@ -0,0 +1,36 @@
+From 3c9c02465826c0d7220e9ae2b820794057c27566 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 30 Oct 2023 11:00:07 +0000
+Subject: cifs: print server capabilities in DebugData
+
+From: Shyam Prasad N <sprasad@microsoft.com>
+
+[ Upstream commit 52768695d36a44d352e9fb79ba27468a5363ab8d ]
+
+In the output of /proc/fs/cifs/DebugData, we do not
+print the server->capabilities field today.
+With this change, we will do that.
+
+Signed-off-by: Shyam Prasad N <sprasad@microsoft.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/cifs_debug.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/fs/smb/client/cifs_debug.c b/fs/smb/client/cifs_debug.c
+index c53d516459fc4..058e703107fc7 100644
+--- a/fs/smb/client/cifs_debug.c
++++ b/fs/smb/client/cifs_debug.c
+@@ -438,6 +438,8 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
+ if (server->nosharesock)
+ seq_printf(m, " nosharesock");
+
++ seq_printf(m, "\nServer capabilities: 0x%x", server->capabilities);
++
+ if (server->rdma)
+ seq_printf(m, "\nRDMA ");
+ seq_printf(m, "\nTCP status: %d Instance: %d"
+--
+2.43.0
+
diff --git a/queue-6.6/cifs-remove-redundant-variable-assignment.patch b/queue-6.6/cifs-remove-redundant-variable-assignment.patch
new file mode 100644
index 0000000000..45633103f9
--- /dev/null
+++ b/queue-6.6/cifs-remove-redundant-variable-assignment.patch
@@ -0,0 +1,43 @@
+From 65992ea21d7ffa8cfc35048d2a38a591461055a4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 14 Mar 2024 23:36:36 +0530
+Subject: cifs: remove redundant variable assignment
+
+From: Bharath SM <bharathsm@microsoft.com>
+
+[ Upstream commit 2760161d149f8d60c3f767fc62a823a1ead9d367 ]
+
+This removes an unnecessary variable assignment. The assigned
+value will be overwritten by cifs_fattr_to_inode before it
+is accessed, making the line redundant.
+
+Signed-off-by: Bharath SM <bharathsm@microsoft.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/inode.c | 2 --
+ 1 file changed, 2 deletions(-)
+
+diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c
+index 67ad8eeaa7665..b304215a4d668 100644
+--- a/fs/smb/client/inode.c
++++ b/fs/smb/client/inode.c
+@@ -401,7 +401,6 @@ cifs_get_file_info_unix(struct file *filp)
+ cifs_unix_basic_to_fattr(&fattr, &find_data, cifs_sb);
+ } else if (rc == -EREMOTE) {
+ cifs_create_junction_fattr(&fattr, inode->i_sb);
+- rc = 0;
+ } else
+ goto cifs_gfiunix_out;
+
+@@ -846,7 +845,6 @@ cifs_get_file_info(struct file *filp)
+ break;
+ case -EREMOTE:
+ cifs_create_junction_fattr(&fattr, inode->i_sb);
+- rc = 0;
+ break;
+ case -EOPNOTSUPP:
+ case -EINVAL:
+--
+2.43.0
+
diff --git a/queue-6.6/cifs-remove-redundant-variable-tcon_exist.patch b/queue-6.6/cifs-remove-redundant-variable-tcon_exist.patch
new file mode 100644
index 0000000000..522f957752
--- /dev/null
+++ b/queue-6.6/cifs-remove-redundant-variable-tcon_exist.patch
@@ -0,0 +1,58 @@
+From 13bcd859cb288d7e036bd5509a26abf84ef823a2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 16 Jan 2024 10:51:34 +0000
+Subject: cifs: remove redundant variable tcon_exist
+
+From: Colin Ian King <colin.i.king@gmail.com>
+
+[ Upstream commit 8ca5d2641be217a78a891d4dbe2a46232d1d8eb9 ]
+
+The variable tcon_exist is being assigned however it is never read, the
+variable is redundant and can be removed.
+
+Cleans up clang scan build warning:
+warning: Although the value stored to 'tcon_exist' is used in
+the enclosing expression, the value is never actually readfrom
+'tcon_exist' [deadcode.DeadStores]
+
+Signed-off-by: Colin Ian King <colin.i.king@gmail.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/smb2pdu.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c
+index 51ff9a967c503..3f9448b5ada9b 100644
+--- a/fs/smb/client/smb2pdu.c
++++ b/fs/smb/client/smb2pdu.c
+@@ -3979,7 +3979,7 @@ void smb2_reconnect_server(struct work_struct *work)
+ struct cifs_ses *ses, *ses2;
+ struct cifs_tcon *tcon, *tcon2;
+ struct list_head tmp_list, tmp_ses_list;
+- bool tcon_exist = false, ses_exist = false;
++ bool ses_exist = false;
+ bool tcon_selected = false;
+ int rc;
+ bool resched = false;
+@@ -4025,7 +4025,7 @@ void smb2_reconnect_server(struct work_struct *work)
+ if (tcon->need_reconnect || tcon->need_reopen_files) {
+ tcon->tc_count++;
+ list_add_tail(&tcon->rlist, &tmp_list);
+- tcon_selected = tcon_exist = true;
++ tcon_selected = true;
+ }
+ }
+ /*
+@@ -4034,7 +4034,7 @@ void smb2_reconnect_server(struct work_struct *work)
+ */
+ if (ses->tcon_ipc && ses->tcon_ipc->need_reconnect) {
+ list_add_tail(&ses->tcon_ipc->rlist, &tmp_list);
+- tcon_selected = tcon_exist = true;
++ tcon_selected = true;
+ cifs_smb_ses_inc_refcount(ses);
+ }
+ /*
+--
+2.43.0
+
diff --git a/queue-6.6/cifs-remove-unneeded-return-statement.patch b/queue-6.6/cifs-remove-unneeded-return-statement.patch
new file mode 100644
index 0000000000..f0a33af56a
--- /dev/null
+++ b/queue-6.6/cifs-remove-unneeded-return-statement.patch
@@ -0,0 +1,34 @@
+From 524caa64e653c4749821c8f13c878080c120e49c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 8 Jan 2024 22:37:10 -0600
+Subject: cifs: remove unneeded return statement
+
+From: Steve French <stfrench@microsoft.com>
+
+[ Upstream commit a3f763fdcb2f784c355aed66ddac6748ff8dbfa6 ]
+
+Return statement was not needed at end of cifs_chan_update_iface
+
+Suggested-by: Christophe Jaillet <christophe.jaillet@wanadoo.fr>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/sess.c | 2 --
+ 1 file changed, 2 deletions(-)
+
+diff --git a/fs/smb/client/sess.c b/fs/smb/client/sess.c
+index 5de32640f0265..3216f786908fb 100644
+--- a/fs/smb/client/sess.c
++++ b/fs/smb/client/sess.c
+@@ -485,8 +485,6 @@ cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server)
+
+ ses->chans[chan_index].iface = iface;
+ spin_unlock(&ses->chan_lock);
+-
+- return;
+ }
+
+ /*
+--
+2.43.0
+
diff --git a/queue-6.6/cifs-set-replay-flag-for-retries-of-write-command.patch b/queue-6.6/cifs-set-replay-flag-for-retries-of-write-command.patch
new file mode 100644
index 0000000000..67f1d208c5
--- /dev/null
+++ b/queue-6.6/cifs-set-replay-flag-for-retries-of-write-command.patch
@@ -0,0 +1,74 @@
+From e4f9f10921c4186f927aaa359652c50085b4bab0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 18 Jan 2024 09:14:10 +0000
+Subject: cifs: set replay flag for retries of write command
+
+From: Shyam Prasad N <sprasad@microsoft.com>
+
+[ Upstream commit 4cdad80261862c8cdcbb5fd232aa713d0bdefe24 ]
+
+Similar to the rest of the commands, this is a change
+to add replay flags on retry. This one does not add a
+back-off, considering that we may want to flush a write
+ASAP to the server. Considering that this will be a
+flush of cached pages, the retrans value is also not
+honoured.
+
+Signed-off-by: Shyam Prasad N <sprasad@microsoft.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/cifsglob.h | 1 +
+ fs/smb/client/file.c | 1 +
+ fs/smb/client/smb2pdu.c | 4 +++-
+ 3 files changed, 5 insertions(+), 1 deletion(-)
+
+diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h
+index 479bf0d9ad589..8fbdb781d70a6 100644
+--- a/fs/smb/client/cifsglob.h
++++ b/fs/smb/client/cifsglob.h
+@@ -1515,6 +1515,7 @@ struct cifs_writedata {
+ struct smbd_mr *mr;
+ #endif
+ struct cifs_credits credits;
++ bool replay;
+ };
+
+ /*
+diff --git a/fs/smb/client/file.c b/fs/smb/client/file.c
+index f41804245fca1..6d44991e1ccdc 100644
+--- a/fs/smb/client/file.c
++++ b/fs/smb/client/file.c
+@@ -3413,6 +3413,7 @@ cifs_resend_wdata(struct cifs_writedata *wdata, struct list_head *wdata_list,
+ if (wdata->cfile->invalidHandle)
+ rc = -EAGAIN;
+ else {
++ wdata->replay = true;
+ #ifdef CONFIG_CIFS_SMB_DIRECT
+ if (wdata->mr) {
+ wdata->mr->need_invalidate = true;
+diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c
+index 6a5d478b3cef6..c73a621a8b83e 100644
+--- a/fs/smb/client/smb2pdu.c
++++ b/fs/smb/client/smb2pdu.c
+@@ -4801,7 +4801,7 @@ smb2_async_writev(struct cifs_writedata *wdata,
+ struct cifs_io_parms *io_parms = NULL;
+ int credit_request;
+
+- if (!wdata->server)
++ if (!wdata->server || wdata->replay)
+ server = wdata->server = cifs_pick_channel(tcon->ses);
+
+ /*
+@@ -4886,6 +4886,8 @@ smb2_async_writev(struct cifs_writedata *wdata,
+ rqst.rq_nvec = 1;
+ rqst.rq_iter = wdata->iter;
+ rqst.rq_iter_size = iov_iter_count(&rqst.rq_iter);
++ if (wdata->replay)
++ smb2_set_replay(server, &rqst);
+ #ifdef CONFIG_CIFS_SMB_DIRECT
+ if (wdata->mr)
+ iov[0].iov_len += sizeof(struct smbd_buffer_descriptor_v1);
+--
+2.43.0
+
diff --git a/queue-6.6/cifs-update-internal-module-version-number-for-cifs..patch b/queue-6.6/cifs-update-internal-module-version-number-for-cifs..patch
new file mode 100644
index 0000000000..cddc478b24
--- /dev/null
+++ b/queue-6.6/cifs-update-internal-module-version-number-for-cifs..patch
@@ -0,0 +1,33 @@
+From a67adc0e5df58c64f9db7b49b5b9b9bf4b198bb3 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 20 Jul 2023 08:30:32 -0500
+Subject: cifs: update internal module version number for cifs.ko
+
+From: Steve French <stfrench@microsoft.com>
+
+[ Upstream commit fd2bd7c0539e28f267a84da8d68f9378511b50a7 ]
+
+From 2.45 to 2.46
+
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/cifsfs.h | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/fs/smb/client/cifsfs.h b/fs/smb/client/cifsfs.h
+index 41daebd220ff4..32ccc4d43df65 100644
+--- a/fs/smb/client/cifsfs.h
++++ b/fs/smb/client/cifsfs.h
+@@ -152,6 +152,6 @@ extern const struct export_operations cifs_export_ops;
+ #endif /* CONFIG_CIFS_NFSD_EXPORT */
+
+ /* when changing internal version - update following two lines at same time */
+-#define SMB3_PRODUCT_BUILD 45
+-#define CIFS_VERSION "2.45"
++#define SMB3_PRODUCT_BUILD 46
++#define CIFS_VERSION "2.46"
+ #endif /* _CIFSFS_H */
+--
+2.43.0
+
diff --git a/queue-6.6/cifs-update-internal-module-version-number-for-cifs..patch-8866 b/queue-6.6/cifs-update-internal-module-version-number-for-cifs..patch-8866
new file mode 100644
index 0000000000..c3e41249ce
--- /dev/null
+++ b/queue-6.6/cifs-update-internal-module-version-number-for-cifs..patch-8866
@@ -0,0 +1,33 @@
+From e1f498eebf35c71cc0c1247996020849ebc5a629 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 9 Jan 2024 23:42:51 -0600
+Subject: cifs: update internal module version number for cifs.ko
+
+From: Steve French <stfrench@microsoft.com>
+
+[ Upstream commit fd2bd7c0539e28f267a84da8d68f9378511b50a7 ]
+
+From 2.45 to 2.46
+
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/cifsfs.h | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/fs/smb/client/cifsfs.h b/fs/smb/client/cifsfs.h
+index 32ccc4d43df65..4b3aaa04e7cb7 100644
+--- a/fs/smb/client/cifsfs.h
++++ b/fs/smb/client/cifsfs.h
+@@ -152,6 +152,6 @@ extern const struct export_operations cifs_export_ops;
+ #endif /* CONFIG_CIFS_NFSD_EXPORT */
+
+ /* when changing internal version - update following two lines at same time */
+-#define SMB3_PRODUCT_BUILD 46
+-#define CIFS_VERSION "2.46"
++#define SMB3_PRODUCT_BUILD 47
++#define CIFS_VERSION "2.47"
+ #endif /* _CIFSFS_H */
+--
+2.43.0
+
diff --git a/queue-6.6/cifs-update-internal-module-version-number-for-cifs..patch-9055 b/queue-6.6/cifs-update-internal-module-version-number-for-cifs..patch-9055
new file mode 100644
index 0000000000..c828ce1842
--- /dev/null
+++ b/queue-6.6/cifs-update-internal-module-version-number-for-cifs..patch-9055
@@ -0,0 +1,33 @@
+From 58a23b024f531589c313697b53a879120f3c3436 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 25 Feb 2024 19:33:50 -0600
+Subject: cifs: update internal module version number for cifs.ko
+
+From: Steve French <stfrench@microsoft.com>
+
+[ Upstream commit fd2bd7c0539e28f267a84da8d68f9378511b50a7 ]
+
+From 2.45 to 2.46
+
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/cifsfs.h | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/fs/smb/client/cifsfs.h b/fs/smb/client/cifsfs.h
+index 4b3aaa04e7cb7..10a9d39cda9a4 100644
+--- a/fs/smb/client/cifsfs.h
++++ b/fs/smb/client/cifsfs.h
+@@ -152,6 +152,6 @@ extern const struct export_operations cifs_export_ops;
+ #endif /* CONFIG_CIFS_NFSD_EXPORT */
+
+ /* when changing internal version - update following two lines at same time */
+-#define SMB3_PRODUCT_BUILD 47
+-#define CIFS_VERSION "2.47"
++#define SMB3_PRODUCT_BUILD 48
++#define CIFS_VERSION "2.48"
+ #endif /* _CIFSFS_H */
+--
+2.43.0
+
diff --git a/queue-6.6/cifs-update-the-same-create_guid-on-replay.patch b/queue-6.6/cifs-update-the-same-create_guid-on-replay.patch
new file mode 100644
index 0000000000..d8abebc38d
--- /dev/null
+++ b/queue-6.6/cifs-update-the-same-create_guid-on-replay.patch
@@ -0,0 +1,131 @@
+From ac515e199342a708ea34a66e0815880c4a30c5ba Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 28 Apr 2024 01:32:09 -0500
+Subject: cifs: update the same create_guid on replay
+
+From: Steve French <stfrench@microsoft.com>
+
+[ Upstream commit 79520587fe42cd4988aff8695d60621e689109cb ]
+
+File open requests made to the server contain a
+CreateGuid, which is used by the server to identify
+the open request. If the same request needs to be
+replayed, it needs to be sent with the same CreateGuid
+in the durable handle v2 context.
+
+Without doing so, we could end up leaking handles on
+the server when:
+1. multichannel is used AND
+2. connection goes down, but not for all channels
+
+This is because the replayed open request would have a
+new CreateGuid and the server will treat this as a new
+request and open a new handle.
+
+This change fixes this by reusing the existing create_guid
+stored in the cached fid struct.
+
+REF: MS-SMB2 4.9 Replay Create Request on an Alternate Channel
+
+Fixes: 4f1fffa23769 ("cifs: commands that are retried should have replay flag set")
+Signed-off-by: Shyam Prasad N <sprasad@microsoft.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/cached_dir.c | 1 +
+ fs/smb/client/cifsglob.h | 1 +
+ fs/smb/client/smb2ops.c | 4 ++++
+ fs/smb/client/smb2pdu.c | 10 ++++++++--
+ 4 files changed, 14 insertions(+), 2 deletions(-)
+
+diff --git a/fs/smb/client/cached_dir.c b/fs/smb/client/cached_dir.c
+index ca0fd25236ef4..0ff2491c311d8 100644
+--- a/fs/smb/client/cached_dir.c
++++ b/fs/smb/client/cached_dir.c
+@@ -243,6 +243,7 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
+ FILE_READ_EA,
+ .disposition = FILE_OPEN,
+ .fid = pfid,
++ .replay = !!(retries),
+ };
+
+ rc = SMB2_open_init(tcon, server,
+diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h
+index 8fbdb781d70a6..181e9d5b10f92 100644
+--- a/fs/smb/client/cifsglob.h
++++ b/fs/smb/client/cifsglob.h
+@@ -1382,6 +1382,7 @@ struct cifs_open_parms {
+ struct cifs_fid *fid;
+ umode_t mode;
+ bool reconnect:1;
++ bool replay:1; /* indicates that this open is for a replay */
+ };
+
+ struct cifs_fid {
+diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
+index 06735c5685bf6..23cf6e92fd54c 100644
+--- a/fs/smb/client/smb2ops.c
++++ b/fs/smb/client/smb2ops.c
+@@ -1204,6 +1204,7 @@ smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon,
+ .disposition = FILE_OPEN,
+ .create_options = cifs_create_options(cifs_sb, 0),
+ .fid = &fid,
++ .replay = !!(retries),
+ };
+
+ rc = SMB2_open_init(tcon, server,
+@@ -1570,6 +1571,7 @@ smb2_ioctl_query_info(const unsigned int xid,
+ .disposition = FILE_OPEN,
+ .create_options = cifs_create_options(cifs_sb, create_options),
+ .fid = &fid,
++ .replay = !!(retries),
+ };
+
+ if (qi.flags & PASSTHRU_FSCTL) {
+@@ -2296,6 +2298,7 @@ smb2_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon,
+ .disposition = FILE_OPEN,
+ .create_options = cifs_create_options(cifs_sb, 0),
+ .fid = fid,
++ .replay = !!(retries),
+ };
+
+ rc = SMB2_open_init(tcon, server,
+@@ -2684,6 +2687,7 @@ smb2_query_info_compound(const unsigned int xid, struct cifs_tcon *tcon,
+ .disposition = FILE_OPEN,
+ .create_options = cifs_create_options(cifs_sb, 0),
+ .fid = &fid,
++ .replay = !!(retries),
+ };
+
+ rc = SMB2_open_init(tcon, server,
+diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c
+index c73a621a8b83e..60793143e24c6 100644
+--- a/fs/smb/client/smb2pdu.c
++++ b/fs/smb/client/smb2pdu.c
+@@ -2421,8 +2421,13 @@ create_durable_v2_buf(struct cifs_open_parms *oparms)
+ */
+ buf->dcontext.Timeout = cpu_to_le32(oparms->tcon->handle_timeout);
+ buf->dcontext.Flags = cpu_to_le32(SMB2_DHANDLE_FLAG_PERSISTENT);
+- generate_random_uuid(buf->dcontext.CreateGuid);
+- memcpy(pfid->create_guid, buf->dcontext.CreateGuid, 16);
++
++ /* for replay, we should not overwrite the existing create guid */
++ if (!oparms->replay) {
++ generate_random_uuid(buf->dcontext.CreateGuid);
++ memcpy(pfid->create_guid, buf->dcontext.CreateGuid, 16);
++ } else
++ memcpy(buf->dcontext.CreateGuid, pfid->create_guid, 16);
+
+ /* SMB2_CREATE_DURABLE_HANDLE_REQUEST is "DH2Q" */
+ buf->Name[0] = 'D';
+@@ -3159,6 +3164,7 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
+ /* reinitialize for possible replay */
+ flags = 0;
+ server = cifs_pick_channel(ses);
++ oparms->replay = !!(retries);
+
+ cifs_dbg(FYI, "create/open\n");
+ if (!ses || !server)
+--
+2.43.0
+
diff --git a/queue-6.6/ksmbd-add-continuous-availability-share-parameter.patch b/queue-6.6/ksmbd-add-continuous-availability-share-parameter.patch
new file mode 100644
index 0000000000..082e98d9ce
--- /dev/null
+++ b/queue-6.6/ksmbd-add-continuous-availability-share-parameter.patch
@@ -0,0 +1,99 @@
+From 7c25986452b585ada1370a79afda75152b44374b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 20 Apr 2024 09:17:58 +0900
+Subject: ksmbd: add continuous availability share parameter
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit e9d8c2f95ab8acaf3f4d4a53682a4afa3c263692 ]
+
+If capabilities of the share is not SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY,
+ksmbd should not grant a persistent handle to the client.
+This patch add continuous availability share parameter to control it.
+
+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 | 35 ++++++++++++++++++-----------------
+ fs/smb/server/smb2pdu.c | 11 +++++++++--
+ 2 files changed, 27 insertions(+), 19 deletions(-)
+
+diff --git a/fs/smb/server/ksmbd_netlink.h b/fs/smb/server/ksmbd_netlink.h
+index 686b321c5a8bb..f4e55199938d5 100644
+--- a/fs/smb/server/ksmbd_netlink.h
++++ b/fs/smb/server/ksmbd_netlink.h
+@@ -340,23 +340,24 @@ enum KSMBD_TREE_CONN_STATUS {
+ /*
+ * Share config flags.
+ */
+-#define KSMBD_SHARE_FLAG_INVALID (0)
+-#define KSMBD_SHARE_FLAG_AVAILABLE BIT(0)
+-#define KSMBD_SHARE_FLAG_BROWSEABLE BIT(1)
+-#define KSMBD_SHARE_FLAG_WRITEABLE BIT(2)
+-#define KSMBD_SHARE_FLAG_READONLY BIT(3)
+-#define KSMBD_SHARE_FLAG_GUEST_OK BIT(4)
+-#define KSMBD_SHARE_FLAG_GUEST_ONLY BIT(5)
+-#define KSMBD_SHARE_FLAG_STORE_DOS_ATTRS BIT(6)
+-#define KSMBD_SHARE_FLAG_OPLOCKS BIT(7)
+-#define KSMBD_SHARE_FLAG_PIPE BIT(8)
+-#define KSMBD_SHARE_FLAG_HIDE_DOT_FILES BIT(9)
+-#define KSMBD_SHARE_FLAG_INHERIT_OWNER BIT(10)
+-#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_CROSSMNT BIT(15)
++#define KSMBD_SHARE_FLAG_INVALID (0)
++#define KSMBD_SHARE_FLAG_AVAILABLE BIT(0)
++#define KSMBD_SHARE_FLAG_BROWSEABLE BIT(1)
++#define KSMBD_SHARE_FLAG_WRITEABLE BIT(2)
++#define KSMBD_SHARE_FLAG_READONLY BIT(3)
++#define KSMBD_SHARE_FLAG_GUEST_OK BIT(4)
++#define KSMBD_SHARE_FLAG_GUEST_ONLY BIT(5)
++#define KSMBD_SHARE_FLAG_STORE_DOS_ATTRS BIT(6)
++#define KSMBD_SHARE_FLAG_OPLOCKS BIT(7)
++#define KSMBD_SHARE_FLAG_PIPE BIT(8)
++#define KSMBD_SHARE_FLAG_HIDE_DOT_FILES BIT(9)
++#define KSMBD_SHARE_FLAG_INHERIT_OWNER BIT(10)
++#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_CROSSMNT BIT(15)
++#define KSMBD_SHARE_FLAG_CONTINUOUS_AVAILABILITY BIT(16)
+
+ /*
+ * Tree connect request flags.
+diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
+index 75db0e63bcebd..1e536ae277618 100644
+--- a/fs/smb/server/smb2pdu.c
++++ b/fs/smb/server/smb2pdu.c
+@@ -1988,7 +1988,12 @@ int smb2_tree_connect(struct ksmbd_work *work)
+ write_unlock(&sess->tree_conns_lock);
+ rsp->StructureSize = cpu_to_le16(16);
+ out_err1:
+- rsp->Capabilities = 0;
++ if (server_conf.flags & KSMBD_GLOBAL_FLAG_DURABLE_HANDLE &&
++ test_share_config_flag(share,
++ KSMBD_SHARE_FLAG_CONTINUOUS_AVAILABILITY))
++ rsp->Capabilities = SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY;
++ else
++ rsp->Capabilities = 0;
+ rsp->Reserved = 0;
+ /* default manual caching */
+ rsp->ShareFlags = SMB2_SHAREFLAG_MANUAL_CACHING;
+@@ -3502,7 +3507,9 @@ int smb2_open(struct ksmbd_work *work)
+ memcpy(fp->client_guid, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE);
+
+ if (dh_info.type == DURABLE_REQ_V2 || dh_info.type == DURABLE_REQ) {
+- if (dh_info.type == DURABLE_REQ_V2 && dh_info.persistent)
++ if (dh_info.type == DURABLE_REQ_V2 && dh_info.persistent &&
++ test_share_config_flag(work->tcon->share_conf,
++ KSMBD_SHARE_FLAG_CONTINUOUS_AVAILABILITY))
+ fp->is_persistent = true;
+ else
+ fp->is_durable = true;
+--
+2.43.0
+
diff --git a/queue-6.6/ksmbd-add-kernel-doc-for-ksmbd_extract_sharename-fun.patch b/queue-6.6/ksmbd-add-kernel-doc-for-ksmbd_extract_sharename-fun.patch
new file mode 100644
index 0000000000..3c1a101e5f
--- /dev/null
+++ b/queue-6.6/ksmbd-add-kernel-doc-for-ksmbd_extract_sharename-fun.patch
@@ -0,0 +1,37 @@
+From 52f27f95cf3af5a015b46291848aab09bee68d84 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 2 Feb 2024 16:13:17 +0800
+Subject: ksmbd: Add kernel-doc for ksmbd_extract_sharename() function
+
+From: Yang Li <yang.lee@linux.alibaba.com>
+
+[ Upstream commit a12bc36032a2f7917068f9ce9eb26d869e54b31a ]
+
+The ksmbd_extract_sharename() function lacked a complete kernel-doc
+comment. This patch adds parameter descriptions and detailed function
+behavior to improve code readability and maintainability.
+
+Signed-off-by: Yang Li <yang.lee@linux.alibaba.com>
+Acked-by: Randy Dunlap <rdunlap@infradead.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/misc.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/fs/smb/server/misc.c b/fs/smb/server/misc.c
+index 9e8afaa686e3a..1a5faa6f6e7bc 100644
+--- a/fs/smb/server/misc.c
++++ b/fs/smb/server/misc.c
+@@ -261,6 +261,7 @@ char *ksmbd_casefold_sharename(struct unicode_map *um, const char *name)
+
+ /**
+ * ksmbd_extract_sharename() - get share name from tree connect request
++ * @um: pointer to a unicode_map structure for character encoding handling
+ * @treename: buffer containing tree name and share name
+ *
+ * Return: share name on success, otherwise error
+--
+2.43.0
+
diff --git a/queue-6.6/ksmbd-add-support-for-durable-handles-v1-v2.patch b/queue-6.6/ksmbd-add-support-for-durable-handles-v1-v2.patch
new file mode 100644
index 0000000000..270dc60041
--- /dev/null
+++ b/queue-6.6/ksmbd-add-support-for-durable-handles-v1-v2.patch
@@ -0,0 +1,866 @@
+From b6a5f7e47f0ad13f381173dcc1b4e164c303d1d5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 12 Mar 2024 14:05:57 +0900
+Subject: ksmbd: add support for durable handles v1/v2
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit c8efcc786146a951091588e5fa7e3c754850cb3c ]
+
+Durable file handles allow reopening a file preserved on a short
+network outage and transparent client reconnection within a timeout.
+i.e. Durable handles aren't necessarily cleaned up when the opening
+process terminates.
+
+This patch add support for durable handle version 1 and 2.
+
+To prove durable handles work on ksmbd, I have tested this patch with
+the following smbtorture tests:
+
+smb2.durable-open.open-oplock
+smb2.durable-open.open-lease
+smb2.durable-open.reopen1
+smb2.durable-open.reopen1a
+smb2.durable-open.reopen1a-lease
+smb2.durable-open.reopen2
+smb2.durable-open.reopen2a
+smb2.durable-open.reopen2-lease
+smb2.durable-open.reopen2-lease-v2
+smb2.durable-open.reopen3
+smb2.durable-open.reopen4
+smb2.durable-open.delete_on_close2
+smb2.durable-open.file-position
+smb2.durable-open.lease
+smb2.durable-open.alloc-size
+smb2.durable-open.read-only
+smb2.durable-v2-open.create-blob
+smb2.durable-v2-open.open-oplock
+smb2.durable-v2-open.open-lease
+smb2.durable-v2-open.reopen1
+smb2.durable-v2-open.reopen1a
+smb2.durable-v2-open.reopen1a-lease
+smb2.durable-v2-open.reopen2
+smb2.durable-v2-open.reopen2b
+
+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/mgmt/user_session.c | 1 +
+ fs/smb/server/oplock.c | 94 +++++++++--
+ fs/smb/server/oplock.h | 7 +-
+ fs/smb/server/smb2ops.c | 6 +
+ fs/smb/server/smb2pdu.c | 257 +++++++++++++++++++++++++++++-
+ fs/smb/server/smb2pdu.h | 15 ++
+ fs/smb/server/vfs_cache.c | 137 +++++++++++++++-
+ fs/smb/server/vfs_cache.h | 9 ++
+ 9 files changed, 506 insertions(+), 21 deletions(-)
+
+diff --git a/fs/smb/server/ksmbd_netlink.h b/fs/smb/server/ksmbd_netlink.h
+index 4464a62228cf3..686b321c5a8bb 100644
+--- a/fs/smb/server/ksmbd_netlink.h
++++ b/fs/smb/server/ksmbd_netlink.h
+@@ -75,6 +75,7 @@ struct ksmbd_heartbeat {
+ #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)
++#define KSMBD_GLOBAL_FLAG_DURABLE_HANDLE BIT(4)
+
+ /*
+ * IPC request for ksmbd server startup
+diff --git a/fs/smb/server/mgmt/user_session.c b/fs/smb/server/mgmt/user_session.c
+index 83074672fe812..aec0a7a124052 100644
+--- a/fs/smb/server/mgmt/user_session.c
++++ b/fs/smb/server/mgmt/user_session.c
+@@ -324,6 +324,7 @@ void destroy_previous_session(struct ksmbd_conn *conn,
+ memcmp(user->passkey, prev_user->passkey, user->passkey_sz))
+ goto out;
+
++ ksmbd_destroy_file_table(&prev_sess->file_table);
+ prev_sess->state = SMB2_SESSION_EXPIRED;
+ out:
+ up_write(&conn->session_lock);
+diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c
+index dc729ab980dc0..7bdae2adad228 100644
+--- a/fs/smb/server/oplock.c
++++ b/fs/smb/server/oplock.c
+@@ -159,7 +159,8 @@ static struct oplock_info *opinfo_get_list(struct ksmbd_inode *ci)
+ opinfo = list_first_or_null_rcu(&ci->m_op_list, struct oplock_info,
+ op_entry);
+ if (opinfo) {
+- if (!atomic_inc_not_zero(&opinfo->refcount))
++ if (opinfo->conn == NULL ||
++ !atomic_inc_not_zero(&opinfo->refcount))
+ opinfo = NULL;
+ else {
+ atomic_inc(&opinfo->conn->r_count);
+@@ -527,7 +528,7 @@ static struct oplock_info *same_client_has_lease(struct ksmbd_inode *ci,
+ */
+ read_lock(&ci->m_lock);
+ list_for_each_entry(opinfo, &ci->m_op_list, op_entry) {
+- if (!opinfo->is_lease)
++ if (!opinfo->is_lease || !opinfo->conn)
+ continue;
+ read_unlock(&ci->m_lock);
+ lease = opinfo->o_lease;
+@@ -651,7 +652,7 @@ static void __smb2_oplock_break_noti(struct work_struct *wk)
+ struct smb2_hdr *rsp_hdr;
+ struct ksmbd_file *fp;
+
+- fp = ksmbd_lookup_durable_fd(br_info->fid);
++ fp = ksmbd_lookup_global_fd(br_info->fid);
+ if (!fp)
+ goto out;
+
+@@ -1115,7 +1116,7 @@ void smb_send_parent_lease_break_noti(struct ksmbd_file *fp,
+
+ read_lock(&p_ci->m_lock);
+ list_for_each_entry(opinfo, &p_ci->m_op_list, op_entry) {
+- if (!opinfo->is_lease)
++ if (opinfo->conn == NULL || !opinfo->is_lease)
+ continue;
+
+ if (opinfo->o_lease->state != SMB2_OPLOCK_LEVEL_NONE &&
+@@ -1160,7 +1161,7 @@ void smb_lazy_parent_lease_break_close(struct ksmbd_file *fp)
+
+ read_lock(&p_ci->m_lock);
+ list_for_each_entry(opinfo, &p_ci->m_op_list, op_entry) {
+- if (!opinfo->is_lease)
++ if (opinfo->conn == NULL || !opinfo->is_lease)
+ continue;
+
+ if (opinfo->o_lease->state != SMB2_OPLOCK_LEVEL_NONE) {
+@@ -1372,6 +1373,9 @@ void smb_break_all_levII_oplock(struct ksmbd_work *work, struct ksmbd_file *fp,
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(brk_op, &ci->m_op_list, op_entry) {
++ if (brk_op->conn == NULL)
++ continue;
++
+ if (!atomic_inc_not_zero(&brk_op->refcount))
+ continue;
+
+@@ -1508,11 +1512,10 @@ 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, bool is_dir)
++struct lease_ctx_info *parse_lease_state(void *open_req)
+ {
+ struct create_context *cc;
+ struct smb2_create_req *req = (struct smb2_create_req *)open_req;
+@@ -1530,12 +1533,7 @@ 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) {
+- lreq->req_state = lc->lcontext.LeaseState &
+- ~SMB2_LEASE_WRITE_CACHING_LE;
+- lreq->is_dir = true;
+- } else
+- lreq->req_state = lc->lcontext.LeaseState;
++ lreq->req_state = lc->lcontext.LeaseState;
+ lreq->flags = lc->lcontext.LeaseFlags;
+ lreq->epoch = lc->lcontext.Epoch;
+ lreq->duration = lc->lcontext.LeaseDuration;
+@@ -1659,6 +1657,8 @@ void create_durable_v2_rsp_buf(char *cc, struct ksmbd_file *fp)
+ buf->Name[3] = 'Q';
+
+ buf->Timeout = cpu_to_le32(fp->durable_timeout);
++ if (fp->is_persistent)
++ buf->Flags = cpu_to_le32(SMB2_DHANDLE_FLAG_PERSISTENT);
+ }
+
+ /**
+@@ -1826,3 +1826,71 @@ struct oplock_info *lookup_lease_in_table(struct ksmbd_conn *conn,
+ read_unlock(&lease_list_lock);
+ return ret_op;
+ }
++
++int smb2_check_durable_oplock(struct ksmbd_conn *conn,
++ struct ksmbd_share_config *share,
++ struct ksmbd_file *fp,
++ struct lease_ctx_info *lctx,
++ char *name)
++{
++ struct oplock_info *opinfo = opinfo_get(fp);
++ int ret = 0;
++
++ if (!opinfo)
++ return 0;
++
++ if (opinfo->is_lease == false) {
++ if (lctx) {
++ pr_err("create context include lease\n");
++ ret = -EBADF;
++ goto out;
++ }
++
++ if (opinfo->level != SMB2_OPLOCK_LEVEL_BATCH) {
++ pr_err("oplock level is not equal to SMB2_OPLOCK_LEVEL_BATCH\n");
++ ret = -EBADF;
++ }
++
++ goto out;
++ }
++
++ if (memcmp(conn->ClientGUID, fp->client_guid,
++ SMB2_CLIENT_GUID_SIZE)) {
++ ksmbd_debug(SMB, "Client guid of fp is not equal to the one of connction\n");
++ ret = -EBADF;
++ goto out;
++ }
++
++ if (!lctx) {
++ ksmbd_debug(SMB, "create context does not include lease\n");
++ ret = -EBADF;
++ goto out;
++ }
++
++ if (memcmp(opinfo->o_lease->lease_key, lctx->lease_key,
++ SMB2_LEASE_KEY_SIZE)) {
++ ksmbd_debug(SMB,
++ "lease key of fp does not match lease key in create context\n");
++ ret = -EBADF;
++ goto out;
++ }
++
++ if (!(opinfo->o_lease->state & SMB2_LEASE_HANDLE_CACHING_LE)) {
++ ksmbd_debug(SMB, "lease state does not contain SMB2_LEASE_HANDLE_CACHING\n");
++ ret = -EBADF;
++ goto out;
++ }
++
++ if (opinfo->o_lease->version != lctx->version) {
++ ksmbd_debug(SMB,
++ "lease version of fp does not match the one in create context\n");
++ ret = -EBADF;
++ goto out;
++ }
++
++ if (!ksmbd_inode_pending_delete(fp))
++ ret = ksmbd_validate_name_reconnect(share, fp, name);
++out:
++ opinfo_put(opinfo);
++ return ret;
++}
+diff --git a/fs/smb/server/oplock.h b/fs/smb/server/oplock.h
+index 5b93ea9196c01..e9da63f25b206 100644
+--- a/fs/smb/server/oplock.h
++++ b/fs/smb/server/oplock.h
+@@ -111,7 +111,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, bool is_dir);
++struct lease_ctx_info *parse_lease_state(void *open_req);
+ __u8 smb2_map_lease_to_oplock(__le32 lease_state);
+ int lease_read_to_write(struct oplock_info *opinfo);
+
+@@ -130,4 +130,9 @@ 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);
++int smb2_check_durable_oplock(struct ksmbd_conn *conn,
++ struct ksmbd_share_config *share,
++ struct ksmbd_file *fp,
++ struct lease_ctx_info *lctx,
++ char *name);
+ #endif /* __KSMBD_OPLOCK_H */
+diff --git a/fs/smb/server/smb2ops.c b/fs/smb/server/smb2ops.c
+index 8600f32c981a1..606aa3c5189a2 100644
+--- a/fs/smb/server/smb2ops.c
++++ b/fs/smb/server/smb2ops.c
+@@ -261,6 +261,9 @@ void init_smb3_02_server(struct ksmbd_conn *conn)
+
+ if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL)
+ conn->vals->capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL;
++
++ if (server_conf.flags & KSMBD_GLOBAL_FLAG_DURABLE_HANDLE)
++ conn->vals->capabilities |= SMB2_GLOBAL_CAP_PERSISTENT_HANDLES;
+ }
+
+ /**
+@@ -283,6 +286,9 @@ int init_smb3_11_server(struct ksmbd_conn *conn)
+ if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL)
+ conn->vals->capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL;
+
++ if (server_conf.flags & KSMBD_GLOBAL_FLAG_DURABLE_HANDLE)
++ conn->vals->capabilities |= SMB2_GLOBAL_CAP_PERSISTENT_HANDLES;
++
+ INIT_LIST_HEAD(&conn->preauth_sess_table);
+ return 0;
+ }
+diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
+index 61717917db765..218adb3c55816 100644
+--- a/fs/smb/server/smb2pdu.c
++++ b/fs/smb/server/smb2pdu.c
+@@ -2622,6 +2622,165 @@ static void ksmbd_acls_fattr(struct smb_fattr *fattr,
+ }
+ }
+
++enum {
++ DURABLE_RECONN_V2 = 1,
++ DURABLE_RECONN,
++ DURABLE_REQ_V2,
++ DURABLE_REQ,
++};
++
++struct durable_info {
++ struct ksmbd_file *fp;
++ unsigned short int type;
++ bool persistent;
++ bool reconnected;
++ unsigned int timeout;
++ char *CreateGuid;
++};
++
++static int parse_durable_handle_context(struct ksmbd_work *work,
++ struct smb2_create_req *req,
++ struct lease_ctx_info *lc,
++ struct durable_info *dh_info)
++{
++ struct ksmbd_conn *conn = work->conn;
++ struct create_context *context;
++ int dh_idx, err = 0;
++ u64 persistent_id = 0;
++ int req_op_level;
++ static const char * const durable_arr[] = {"DH2C", "DHnC", "DH2Q", "DHnQ"};
++
++ req_op_level = req->RequestedOplockLevel;
++ for (dh_idx = DURABLE_RECONN_V2; dh_idx <= ARRAY_SIZE(durable_arr);
++ dh_idx++) {
++ context = smb2_find_context_vals(req, durable_arr[dh_idx - 1], 4);
++ if (IS_ERR(context)) {
++ err = PTR_ERR(context);
++ goto out;
++ }
++ if (!context)
++ continue;
++
++ switch (dh_idx) {
++ case DURABLE_RECONN_V2:
++ {
++ struct create_durable_reconn_v2_req *recon_v2;
++
++ if (dh_info->type == DURABLE_RECONN ||
++ dh_info->type == DURABLE_REQ_V2) {
++ err = -EINVAL;
++ goto out;
++ }
++
++ recon_v2 = (struct create_durable_reconn_v2_req *)context;
++ persistent_id = recon_v2->Fid.PersistentFileId;
++ dh_info->fp = ksmbd_lookup_durable_fd(persistent_id);
++ if (!dh_info->fp) {
++ ksmbd_debug(SMB, "Failed to get durable handle state\n");
++ err = -EBADF;
++ goto out;
++ }
++
++ if (memcmp(dh_info->fp->create_guid, recon_v2->CreateGuid,
++ SMB2_CREATE_GUID_SIZE)) {
++ err = -EBADF;
++ ksmbd_put_durable_fd(dh_info->fp);
++ goto out;
++ }
++
++ dh_info->type = dh_idx;
++ dh_info->reconnected = true;
++ ksmbd_debug(SMB,
++ "reconnect v2 Persistent-id from reconnect = %llu\n",
++ persistent_id);
++ break;
++ }
++ case DURABLE_RECONN:
++ {
++ struct create_durable_reconn_req *recon;
++
++ if (dh_info->type == DURABLE_RECONN_V2 ||
++ dh_info->type == DURABLE_REQ_V2) {
++ err = -EINVAL;
++ goto out;
++ }
++
++ recon = (struct create_durable_reconn_req *)context;
++ persistent_id = recon->Data.Fid.PersistentFileId;
++ dh_info->fp = ksmbd_lookup_durable_fd(persistent_id);
++ if (!dh_info->fp) {
++ ksmbd_debug(SMB, "Failed to get durable handle state\n");
++ err = -EBADF;
++ goto out;
++ }
++
++ dh_info->type = dh_idx;
++ dh_info->reconnected = true;
++ ksmbd_debug(SMB, "reconnect Persistent-id from reconnect = %llu\n",
++ persistent_id);
++ break;
++ }
++ case DURABLE_REQ_V2:
++ {
++ struct create_durable_req_v2 *durable_v2_blob;
++
++ if (dh_info->type == DURABLE_RECONN ||
++ dh_info->type == DURABLE_RECONN_V2) {
++ err = -EINVAL;
++ goto out;
++ }
++
++ durable_v2_blob =
++ (struct create_durable_req_v2 *)context;
++ ksmbd_debug(SMB, "Request for durable v2 open\n");
++ dh_info->fp = ksmbd_lookup_fd_cguid(durable_v2_blob->CreateGuid);
++ if (dh_info->fp) {
++ if (!memcmp(conn->ClientGUID, dh_info->fp->client_guid,
++ SMB2_CLIENT_GUID_SIZE)) {
++ if (!(req->hdr.Flags & SMB2_FLAGS_REPLAY_OPERATION)) {
++ err = -ENOEXEC;
++ goto out;
++ }
++
++ dh_info->fp->conn = conn;
++ dh_info->reconnected = true;
++ goto out;
++ }
++ }
++
++ if (((lc && (lc->req_state & SMB2_LEASE_HANDLE_CACHING_LE)) ||
++ req_op_level == SMB2_OPLOCK_LEVEL_BATCH)) {
++ dh_info->CreateGuid =
++ durable_v2_blob->CreateGuid;
++ dh_info->persistent =
++ le32_to_cpu(durable_v2_blob->Flags);
++ dh_info->timeout =
++ le32_to_cpu(durable_v2_blob->Timeout);
++ dh_info->type = dh_idx;
++ }
++ break;
++ }
++ case DURABLE_REQ:
++ if (dh_info->type == DURABLE_RECONN)
++ goto out;
++ if (dh_info->type == DURABLE_RECONN_V2 ||
++ dh_info->type == DURABLE_REQ_V2) {
++ err = -EINVAL;
++ goto out;
++ }
++
++ if (((lc && (lc->req_state & SMB2_LEASE_HANDLE_CACHING_LE)) ||
++ req_op_level == SMB2_OPLOCK_LEVEL_BATCH)) {
++ ksmbd_debug(SMB, "Request for durable open\n");
++ dh_info->type = dh_idx;
++ }
++ }
++ }
++
++out:
++ return err;
++}
++
+ /**
+ * smb2_open() - handler for smb file open request
+ * @work: smb work containing request buffer
+@@ -2645,6 +2804,7 @@ int smb2_open(struct ksmbd_work *work)
+ struct lease_ctx_info *lc = NULL;
+ struct create_ea_buf_req *ea_buf = NULL;
+ struct oplock_info *opinfo;
++ struct durable_info dh_info = {0};
+ __le32 *next_ptr = NULL;
+ int req_op_level = 0, open_flags = 0, may_flags = 0, file_info = 0;
+ int rc = 0;
+@@ -2725,6 +2885,49 @@ int smb2_open(struct ksmbd_work *work)
+ }
+ }
+
++ req_op_level = req->RequestedOplockLevel;
++
++ if (server_conf.flags & KSMBD_GLOBAL_FLAG_DURABLE_HANDLE &&
++ req->CreateContextsOffset) {
++ lc = parse_lease_state(req);
++ rc = parse_durable_handle_context(work, req, lc, &dh_info);
++ if (rc) {
++ ksmbd_debug(SMB, "error parsing durable handle context\n");
++ goto err_out2;
++ }
++
++ if (dh_info.reconnected == true) {
++ rc = smb2_check_durable_oplock(conn, share, dh_info.fp, lc, name);
++ if (rc) {
++ ksmbd_put_durable_fd(dh_info.fp);
++ goto err_out2;
++ }
++
++ rc = ksmbd_reopen_durable_fd(work, dh_info.fp);
++ if (rc) {
++ ksmbd_put_durable_fd(dh_info.fp);
++ goto err_out2;
++ }
++
++ if (ksmbd_override_fsids(work)) {
++ rc = -ENOMEM;
++ ksmbd_put_durable_fd(dh_info.fp);
++ goto err_out2;
++ }
++
++ fp = dh_info.fp;
++ file_info = FILE_OPENED;
++
++ rc = ksmbd_vfs_getattr(&fp->filp->f_path, &stat);
++ if (rc)
++ goto err_out2;
++
++ ksmbd_put_durable_fd(fp);
++ goto reconnected_fp;
++ }
++ } else 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));
+@@ -3187,10 +3390,6 @@ 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 &&
+@@ -3201,6 +3400,11 @@ int smb2_open(struct ksmbd_work *work)
+ }
+ } else {
+ if (req_op_level == SMB2_OPLOCK_LEVEL_LEASE) {
++ if (S_ISDIR(file_inode(filp)->i_mode)) {
++ lc->req_state &= ~SMB2_LEASE_WRITE_CACHING_LE;
++ lc->is_dir = true;
++ }
++
+ /*
+ * Compare parent lease using parent key. If there is no
+ * a lease that has same parent key, Send lease break
+@@ -3297,6 +3501,24 @@ int smb2_open(struct ksmbd_work *work)
+
+ memcpy(fp->client_guid, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE);
+
++ if (dh_info.type == DURABLE_REQ_V2 || dh_info.type == DURABLE_REQ) {
++ if (dh_info.type == DURABLE_REQ_V2 && dh_info.persistent)
++ fp->is_persistent = true;
++ else
++ fp->is_durable = true;
++
++ if (dh_info.type == DURABLE_REQ_V2) {
++ memcpy(fp->create_guid, dh_info.CreateGuid,
++ SMB2_CREATE_GUID_SIZE);
++ if (dh_info.timeout)
++ fp->durable_timeout = min(dh_info.timeout,
++ 300000);
++ else
++ fp->durable_timeout = 60;
++ }
++ }
++
++reconnected_fp:
+ rsp->StructureSize = cpu_to_le16(89);
+ rcu_read_lock();
+ opinfo = rcu_dereference(fp->f_opinfo);
+@@ -3383,6 +3605,33 @@ int smb2_open(struct ksmbd_work *work)
+ next_off = conn->vals->create_disk_id_size;
+ }
+
++ if (dh_info.type == DURABLE_REQ || dh_info.type == DURABLE_REQ_V2) {
++ struct create_context *durable_ccontext;
++
++ durable_ccontext = (struct create_context *)(rsp->Buffer +
++ le32_to_cpu(rsp->CreateContextsLength));
++ contxt_cnt++;
++ if (dh_info.type == DURABLE_REQ) {
++ create_durable_rsp_buf(rsp->Buffer +
++ le32_to_cpu(rsp->CreateContextsLength));
++ le32_add_cpu(&rsp->CreateContextsLength,
++ conn->vals->create_durable_size);
++ iov_len += conn->vals->create_durable_size;
++ } else {
++ create_durable_v2_rsp_buf(rsp->Buffer +
++ le32_to_cpu(rsp->CreateContextsLength),
++ fp);
++ le32_add_cpu(&rsp->CreateContextsLength,
++ conn->vals->create_durable_v2_size);
++ iov_len += conn->vals->create_durable_v2_size;
++ }
++
++ if (next_ptr)
++ *next_ptr = cpu_to_le32(next_off);
++ next_ptr = &durable_ccontext->Next;
++ next_off = conn->vals->create_durable_size;
++ }
++
+ if (posix_ctxt) {
+ contxt_cnt++;
+ create_posix_rsp_buf(rsp->Buffer +
+diff --git a/fs/smb/server/smb2pdu.h b/fs/smb/server/smb2pdu.h
+index d12cfd3b09278..bd1d2a0e9203a 100644
+--- a/fs/smb/server/smb2pdu.h
++++ b/fs/smb/server/smb2pdu.h
+@@ -72,6 +72,18 @@ struct create_durable_req_v2 {
+ __u8 CreateGuid[16];
+ } __packed;
+
++struct create_durable_reconn_req {
++ struct create_context ccontext;
++ __u8 Name[8];
++ union {
++ __u8 Reserved[16];
++ struct {
++ __u64 PersistentFileId;
++ __u64 VolatileFileId;
++ } Fid;
++ } Data;
++} __packed;
++
+ struct create_durable_reconn_v2_req {
+ struct create_context ccontext;
+ __u8 Name[8];
+@@ -98,6 +110,9 @@ struct create_durable_rsp {
+ } Data;
+ } __packed;
+
++/* See MS-SMB2 2.2.13.2.11 */
++/* Flags */
++#define SMB2_DHANDLE_FLAG_PERSISTENT 0x00000002
+ struct create_durable_v2_rsp {
+ struct create_context ccontext;
+ __u8 Name[8];
+diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c
+index 4e82ff627d122..030f70700036c 100644
+--- a/fs/smb/server/vfs_cache.c
++++ b/fs/smb/server/vfs_cache.c
+@@ -305,7 +305,8 @@ static void __ksmbd_close_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp)
+
+ fd_limit_close();
+ __ksmbd_remove_durable_fd(fp);
+- __ksmbd_remove_fd(ft, fp);
++ if (ft)
++ __ksmbd_remove_fd(ft, fp);
+
+ close_id_del_oplock(fp);
+ filp = fp->filp;
+@@ -465,11 +466,32 @@ struct ksmbd_file *ksmbd_lookup_fd_slow(struct ksmbd_work *work, u64 id,
+ return fp;
+ }
+
+-struct ksmbd_file *ksmbd_lookup_durable_fd(unsigned long long id)
++struct ksmbd_file *ksmbd_lookup_global_fd(unsigned long long id)
+ {
+ return __ksmbd_lookup_fd(&global_ft, id);
+ }
+
++struct ksmbd_file *ksmbd_lookup_durable_fd(unsigned long long id)
++{
++ struct ksmbd_file *fp;
++
++ fp = __ksmbd_lookup_fd(&global_ft, id);
++ if (fp && fp->conn) {
++ ksmbd_put_durable_fd(fp);
++ fp = NULL;
++ }
++
++ return fp;
++}
++
++void ksmbd_put_durable_fd(struct ksmbd_file *fp)
++{
++ if (!atomic_dec_and_test(&fp->refcount))
++ return;
++
++ __ksmbd_close_fd(NULL, fp);
++}
++
+ struct ksmbd_file *ksmbd_lookup_fd_cguid(char *cguid)
+ {
+ struct ksmbd_file *fp = NULL;
+@@ -639,6 +661,32 @@ __close_file_table_ids(struct ksmbd_file_table *ft,
+ return num;
+ }
+
++static inline bool is_reconnectable(struct ksmbd_file *fp)
++{
++ struct oplock_info *opinfo = opinfo_get(fp);
++ bool reconn = false;
++
++ if (!opinfo)
++ return false;
++
++ if (opinfo->op_state != OPLOCK_STATE_NONE) {
++ opinfo_put(opinfo);
++ return false;
++ }
++
++ if (fp->is_resilient || fp->is_persistent)
++ reconn = true;
++ else if (fp->is_durable && opinfo->is_lease &&
++ opinfo->o_lease->state & SMB2_LEASE_HANDLE_CACHING_LE)
++ reconn = true;
++
++ else if (fp->is_durable && opinfo->level == SMB2_OPLOCK_LEVEL_BATCH)
++ reconn = true;
++
++ opinfo_put(opinfo);
++ return reconn;
++}
++
+ static bool tree_conn_fd_check(struct ksmbd_tree_connect *tcon,
+ struct ksmbd_file *fp)
+ {
+@@ -648,7 +696,28 @@ static bool tree_conn_fd_check(struct ksmbd_tree_connect *tcon,
+ static bool session_fd_check(struct ksmbd_tree_connect *tcon,
+ struct ksmbd_file *fp)
+ {
+- return false;
++ struct ksmbd_inode *ci;
++ struct oplock_info *op;
++ struct ksmbd_conn *conn;
++
++ if (!is_reconnectable(fp))
++ return false;
++
++ conn = fp->conn;
++ ci = fp->f_ci;
++ write_lock(&ci->m_lock);
++ list_for_each_entry_rcu(op, &ci->m_op_list, op_entry) {
++ if (op->conn != conn)
++ continue;
++ op->conn = NULL;
++ }
++ write_unlock(&ci->m_lock);
++
++ fp->conn = NULL;
++ fp->tcon = NULL;
++ fp->volatile_id = KSMBD_NO_FID;
++
++ return true;
+ }
+
+ void ksmbd_close_tree_conn_fds(struct ksmbd_work *work)
+@@ -687,6 +756,68 @@ void ksmbd_free_global_file_table(void)
+ ksmbd_destroy_file_table(&global_ft);
+ }
+
++int ksmbd_validate_name_reconnect(struct ksmbd_share_config *share,
++ struct ksmbd_file *fp, char *name)
++{
++ char *pathname, *ab_pathname;
++ int ret = 0;
++
++ pathname = kmalloc(PATH_MAX, GFP_KERNEL);
++ if (!pathname)
++ return -EACCES;
++
++ ab_pathname = d_path(&fp->filp->f_path, pathname, PATH_MAX);
++ if (IS_ERR(ab_pathname)) {
++ kfree(pathname);
++ return -EACCES;
++ }
++
++ if (name && strcmp(&ab_pathname[share->path_sz + 1], name)) {
++ ksmbd_debug(SMB, "invalid name reconnect %s\n", name);
++ ret = -EINVAL;
++ }
++
++ kfree(pathname);
++
++ return ret;
++}
++
++int ksmbd_reopen_durable_fd(struct ksmbd_work *work, struct ksmbd_file *fp)
++{
++ struct ksmbd_inode *ci;
++ struct oplock_info *op;
++
++ if (!fp->is_durable || fp->conn || fp->tcon) {
++ pr_err("Invalid durable fd [%p:%p]\n", fp->conn, fp->tcon);
++ return -EBADF;
++ }
++
++ if (has_file_id(fp->volatile_id)) {
++ pr_err("Still in use durable fd: %llu\n", fp->volatile_id);
++ return -EBADF;
++ }
++
++ fp->conn = work->conn;
++ fp->tcon = work->tcon;
++
++ ci = fp->f_ci;
++ write_lock(&ci->m_lock);
++ list_for_each_entry_rcu(op, &ci->m_op_list, op_entry) {
++ if (op->conn)
++ continue;
++ op->conn = fp->conn;
++ }
++ write_unlock(&ci->m_lock);
++
++ __open_id(&work->sess->file_table, fp, OPEN_ID_TYPE_VOLATILE_ID);
++ if (!has_file_id(fp->volatile_id)) {
++ fp->conn = NULL;
++ fp->tcon = NULL;
++ return -EBADF;
++ }
++ return 0;
++}
++
+ int ksmbd_init_file_table(struct ksmbd_file_table *ft)
+ {
+ ft->idr = kzalloc(sizeof(struct idr), GFP_KERNEL);
+diff --git a/fs/smb/server/vfs_cache.h b/fs/smb/server/vfs_cache.h
+index a528f0cc775ae..ed44fb4e18e79 100644
+--- a/fs/smb/server/vfs_cache.h
++++ b/fs/smb/server/vfs_cache.h
+@@ -14,6 +14,7 @@
+ #include <linux/workqueue.h>
+
+ #include "vfs.h"
++#include "mgmt/share_config.h"
+
+ /* Windows style file permissions for extended response */
+ #define FILE_GENERIC_ALL 0x1F01FF
+@@ -106,6 +107,9 @@ struct ksmbd_file {
+ int dot_dotdot[2];
+ unsigned int f_state;
+ bool reserve_lease_break;
++ bool is_durable;
++ bool is_persistent;
++ bool is_resilient;
+ };
+
+ static inline void set_ctx_actor(struct dir_context *ctx,
+@@ -141,7 +145,9 @@ 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_inode *ksmbd_inode_lookup_lock(struct dentry *d);
+ void ksmbd_inode_put(struct ksmbd_inode *ci);
++struct ksmbd_file *ksmbd_lookup_global_fd(unsigned long long id);
+ struct ksmbd_file *ksmbd_lookup_durable_fd(unsigned long long id);
++void ksmbd_put_durable_fd(struct ksmbd_file *fp);
+ struct ksmbd_file *ksmbd_lookup_fd_cguid(char *cguid);
+ struct ksmbd_file *ksmbd_lookup_fd_inode(struct dentry *dentry);
+ unsigned int ksmbd_open_durable_fd(struct ksmbd_file *fp);
+@@ -173,6 +179,9 @@ void ksmbd_set_inode_pending_delete(struct ksmbd_file *fp);
+ void ksmbd_clear_inode_pending_delete(struct ksmbd_file *fp);
+ void ksmbd_fd_set_delete_on_close(struct ksmbd_file *fp,
+ int file_info);
++int ksmbd_reopen_durable_fd(struct ksmbd_work *work, struct ksmbd_file *fp);
++int ksmbd_validate_name_reconnect(struct ksmbd_share_config *share,
++ struct ksmbd_file *fp, char *name);
+ int ksmbd_init_file_cache(void);
+ void ksmbd_exit_file_cache(void);
+ #endif /* __VFS_CACHE_H__ */
+--
+2.43.0
+
diff --git a/queue-6.6/ksmbd-auth-fix-most-kernel-doc-warnings.patch b/queue-6.6/ksmbd-auth-fix-most-kernel-doc-warnings.patch
new file mode 100644
index 0000000000..5c465cd2b1
--- /dev/null
+++ b/queue-6.6/ksmbd-auth-fix-most-kernel-doc-warnings.patch
@@ -0,0 +1,98 @@
+From a722195c80fdb7d820d687fe0599239671f80d2f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 15 Dec 2023 19:03:57 -0800
+Subject: ksmbd: auth: fix most kernel-doc warnings
+
+From: Randy Dunlap <rdunlap@infradead.org>
+
+[ Upstream commit b4068f1ef36d634ef44ece894738284d756d6627 ]
+
+Fix 12 of 17 kernel-doc warnings in auth.c:
+
+auth.c:221: warning: Function parameter or member 'conn' not described in 'ksmbd_auth_ntlmv2'
+auth.c:221: warning: Function parameter or member 'cryptkey' not described in 'ksmbd_auth_ntlmv2'
+auth.c:305: warning: Function parameter or member 'blob_len' not described in 'ksmbd_decode_ntlmssp_auth_blob'
+auth.c:305: warning: Function parameter or member 'conn' not described in 'ksmbd_decode_ntlmssp_auth_blob'
+auth.c:305: warning: Excess function parameter 'usr' description in 'ksmbd_decode_ntlmssp_auth_blob'
+auth.c:385: warning: Function parameter or member 'blob_len' not described in 'ksmbd_decode_ntlmssp_neg_blob'
+auth.c:385: warning: Function parameter or member 'conn' not described in 'ksmbd_decode_ntlmssp_neg_blob'
+auth.c:385: warning: Excess function parameter 'rsp' description in 'ksmbd_decode_ntlmssp_neg_blob'
+auth.c:385: warning: Excess function parameter 'sess' description in 'ksmbd_decode_ntlmssp_neg_blob'
+auth.c:413: warning: Function parameter or member 'conn' not described in 'ksmbd_build_ntlmssp_challenge_blob'
+auth.c:413: warning: Excess function parameter 'rsp' description in 'ksmbd_build_ntlmssp_challenge_blob'
+auth.c:413: warning: Excess function parameter 'sess' description in 'ksmbd_build_ntlmssp_challenge_blob'
+
+The other 5 are only present when a W=1 kernel build is done or
+when scripts/kernel-doc is run with -Wall. They are:
+
+auth.c:81: warning: No description found for return value of 'ksmbd_gen_sess_key'
+auth.c:385: warning: No description found for return value of 'ksmbd_decode_ntlmssp_neg_blob'
+auth.c:413: warning: No description found for return value of 'ksmbd_build_ntlmssp_challenge_blob'
+auth.c:577: warning: No description found for return value of 'ksmbd_sign_smb2_pdu'
+auth.c:628: warning: No description found for return value of 'ksmbd_sign_smb3_pdu'
+
+Signed-off-by: Randy Dunlap <rdunlap@infradead.org>
+Cc: Namjae Jeon <linkinjeon@kernel.org>
+Cc: Steve French <sfrench@samba.org>
+Cc: Sergey Senozhatsky <senozhatsky@chromium.org>
+Cc: Tom Talpey <tom@talpey.com>
+Cc: linux-cifs@vger.kernel.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/auth.c | 14 ++++++++------
+ 1 file changed, 8 insertions(+), 6 deletions(-)
+
+diff --git a/fs/smb/server/auth.c b/fs/smb/server/auth.c
+index 229a6527870d0..09b20039636e7 100644
+--- a/fs/smb/server/auth.c
++++ b/fs/smb/server/auth.c
+@@ -208,10 +208,12 @@ static int calc_ntlmv2_hash(struct ksmbd_conn *conn, struct ksmbd_session *sess,
+
+ /**
+ * ksmbd_auth_ntlmv2() - NTLMv2 authentication handler
+- * @sess: session of connection
++ * @conn: connection
++ * @sess: session of connection
+ * @ntlmv2: NTLMv2 challenge response
+ * @blen: NTLMv2 blob length
+ * @domain_name: domain name
++ * @cryptkey: session crypto key
+ *
+ * Return: 0 on success, error number on error
+ */
+@@ -294,7 +296,8 @@ int ksmbd_auth_ntlmv2(struct ksmbd_conn *conn, struct ksmbd_session *sess,
+ * ksmbd_decode_ntlmssp_auth_blob() - helper function to construct
+ * authenticate blob
+ * @authblob: authenticate blob source pointer
+- * @usr: user details
++ * @blob_len: length of the @authblob message
++ * @conn: connection
+ * @sess: session of connection
+ *
+ * Return: 0 on success, error number on error
+@@ -376,8 +379,8 @@ int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob,
+ * ksmbd_decode_ntlmssp_neg_blob() - helper function to construct
+ * negotiate blob
+ * @negblob: negotiate blob source pointer
+- * @rsp: response header pointer to be updated
+- * @sess: session of connection
++ * @blob_len: length of the @authblob message
++ * @conn: connection
+ *
+ */
+ int ksmbd_decode_ntlmssp_neg_blob(struct negotiate_message *negblob,
+@@ -403,8 +406,7 @@ int ksmbd_decode_ntlmssp_neg_blob(struct negotiate_message *negblob,
+ * ksmbd_build_ntlmssp_challenge_blob() - helper function to construct
+ * challenge blob
+ * @chgblob: challenge blob source pointer to initialize
+- * @rsp: response header pointer to be updated
+- * @sess: session of connection
++ * @conn: connection
+ *
+ */
+ unsigned int
+--
+2.43.0
+
diff --git a/queue-6.6/ksmbd-fix-possible-null-deref-in-smb_lazy_parent_lea.patch b/queue-6.6/ksmbd-fix-possible-null-deref-in-smb_lazy_parent_lea.patch
new file mode 100644
index 0000000000..c7689a2209
--- /dev/null
+++ b/queue-6.6/ksmbd-fix-possible-null-deref-in-smb_lazy_parent_lea.patch
@@ -0,0 +1,35 @@
+From 07b448a447d2838c0014e19f1957f7f7c98ccc1a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 13 Mar 2024 15:11:38 +0100
+Subject: ksmbd: fix possible null-deref in smb_lazy_parent_lease_break_close
+
+From: Marios Makassikis <mmakassikis@freebox.fr>
+
+[ Upstream commit 5fb282ba4fef8985a5acf2b32681f2ec07732561 ]
+
+rcu_dereference can return NULL, so make sure we check against that.
+
+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/oplock.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c
+index 7bdae2adad228..58bafe23ded9a 100644
+--- a/fs/smb/server/oplock.c
++++ b/fs/smb/server/oplock.c
+@@ -1152,7 +1152,7 @@ void smb_lazy_parent_lease_break_close(struct ksmbd_file *fp)
+ opinfo = rcu_dereference(fp->f_opinfo);
+ rcu_read_unlock();
+
+- if (!opinfo->is_lease || opinfo->o_lease->version != 2)
++ if (!opinfo || !opinfo->is_lease || opinfo->o_lease->version != 2)
+ return;
+
+ p_ci = ksmbd_inode_lookup_lock(fp->filp->f_path.dentry->d_parent);
+--
+2.43.0
+
diff --git a/queue-6.6/ksmbd-fix-potencial-out-of-bounds-when-buffer-offset.patch b/queue-6.6/ksmbd-fix-potencial-out-of-bounds-when-buffer-offset.patch
new file mode 100644
index 0000000000..dbc3eb68fd
--- /dev/null
+++ b/queue-6.6/ksmbd-fix-potencial-out-of-bounds-when-buffer-offset.patch
@@ -0,0 +1,316 @@
+From 3f41596bd66a08ee489798a57c56243c01fe640c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 19 Mar 2024 08:40:48 +0900
+Subject: ksmbd: fix potencial out-of-bounds when buffer offset is invalid
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit c6cd2e8d2d9aa7ee35b1fa6a668e32a22a9753da ]
+
+I found potencial out-of-bounds when buffer offset fields of a few requests
+is invalid. This patch set the minimum value of buffer offset field to
+->Buffer offset to validate buffer length.
+
+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/smb2misc.c | 23 +++++++++++++------
+ fs/smb/server/smb2pdu.c | 48 ++++++++++++++++++++++------------------
+ 2 files changed, 42 insertions(+), 29 deletions(-)
+
+diff --git a/fs/smb/server/smb2misc.c b/fs/smb/server/smb2misc.c
+index 7c872ffb4b0a9..727cb49926ee5 100644
+--- a/fs/smb/server/smb2misc.c
++++ b/fs/smb/server/smb2misc.c
+@@ -101,7 +101,9 @@ static int smb2_get_data_area_len(unsigned int *off, unsigned int *len,
+ *len = le16_to_cpu(((struct smb2_sess_setup_req *)hdr)->SecurityBufferLength);
+ break;
+ case SMB2_TREE_CONNECT:
+- *off = le16_to_cpu(((struct smb2_tree_connect_req *)hdr)->PathOffset);
++ *off = max_t(unsigned short int,
++ le16_to_cpu(((struct smb2_tree_connect_req *)hdr)->PathOffset),
++ offsetof(struct smb2_tree_connect_req, Buffer));
+ *len = le16_to_cpu(((struct smb2_tree_connect_req *)hdr)->PathLength);
+ break;
+ case SMB2_CREATE:
+@@ -110,7 +112,6 @@ static int smb2_get_data_area_len(unsigned int *off, unsigned int *len,
+ max_t(unsigned short int,
+ le16_to_cpu(((struct smb2_create_req *)hdr)->NameOffset),
+ offsetof(struct smb2_create_req, Buffer));
+-
+ unsigned short int name_len =
+ le16_to_cpu(((struct smb2_create_req *)hdr)->NameLength);
+
+@@ -131,11 +132,15 @@ static int smb2_get_data_area_len(unsigned int *off, unsigned int *len,
+ break;
+ }
+ case SMB2_QUERY_INFO:
+- *off = le16_to_cpu(((struct smb2_query_info_req *)hdr)->InputBufferOffset);
++ *off = max_t(unsigned int,
++ le16_to_cpu(((struct smb2_query_info_req *)hdr)->InputBufferOffset),
++ offsetof(struct smb2_query_info_req, Buffer));
+ *len = le32_to_cpu(((struct smb2_query_info_req *)hdr)->InputBufferLength);
+ break;
+ case SMB2_SET_INFO:
+- *off = le16_to_cpu(((struct smb2_set_info_req *)hdr)->BufferOffset);
++ *off = max_t(unsigned int,
++ le16_to_cpu(((struct smb2_set_info_req *)hdr)->BufferOffset),
++ offsetof(struct smb2_set_info_req, Buffer));
+ *len = le32_to_cpu(((struct smb2_set_info_req *)hdr)->BufferLength);
+ break;
+ case SMB2_READ:
+@@ -145,7 +150,7 @@ static int smb2_get_data_area_len(unsigned int *off, unsigned int *len,
+ case SMB2_WRITE:
+ if (((struct smb2_write_req *)hdr)->DataOffset ||
+ ((struct smb2_write_req *)hdr)->Length) {
+- *off = max_t(unsigned int,
++ *off = max_t(unsigned short int,
+ le16_to_cpu(((struct smb2_write_req *)hdr)->DataOffset),
+ offsetof(struct smb2_write_req, Buffer));
+ *len = le32_to_cpu(((struct smb2_write_req *)hdr)->Length);
+@@ -156,7 +161,9 @@ static int smb2_get_data_area_len(unsigned int *off, unsigned int *len,
+ *len = le16_to_cpu(((struct smb2_write_req *)hdr)->WriteChannelInfoLength);
+ break;
+ case SMB2_QUERY_DIRECTORY:
+- *off = le16_to_cpu(((struct smb2_query_directory_req *)hdr)->FileNameOffset);
++ *off = max_t(unsigned short int,
++ le16_to_cpu(((struct smb2_query_directory_req *)hdr)->FileNameOffset),
++ offsetof(struct smb2_query_directory_req, Buffer));
+ *len = le16_to_cpu(((struct smb2_query_directory_req *)hdr)->FileNameLength);
+ break;
+ case SMB2_LOCK:
+@@ -171,7 +178,9 @@ static int smb2_get_data_area_len(unsigned int *off, unsigned int *len,
+ break;
+ }
+ case SMB2_IOCTL:
+- *off = le32_to_cpu(((struct smb2_ioctl_req *)hdr)->InputOffset);
++ *off = max_t(unsigned int,
++ le32_to_cpu(((struct smb2_ioctl_req *)hdr)->InputOffset),
++ offsetof(struct smb2_ioctl_req, Buffer));
+ *len = le32_to_cpu(((struct smb2_ioctl_req *)hdr)->InputCount);
+ break;
+ default:
+diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
+index 218adb3c55816..75db0e63bcebd 100644
+--- a/fs/smb/server/smb2pdu.c
++++ b/fs/smb/server/smb2pdu.c
+@@ -1931,7 +1931,7 @@ int smb2_tree_connect(struct ksmbd_work *work)
+
+ WORK_BUFFERS(work, req, rsp);
+
+- treename = smb_strndup_from_utf16(req->Buffer,
++ treename = smb_strndup_from_utf16((char *)req + le16_to_cpu(req->PathOffset),
+ le16_to_cpu(req->PathLength), true,
+ conn->local_nls);
+ if (IS_ERR(treename)) {
+@@ -2844,7 +2844,7 @@ int smb2_open(struct ksmbd_work *work)
+ goto err_out2;
+ }
+
+- name = smb2_get_name(req->Buffer,
++ name = smb2_get_name((char *)req + le16_to_cpu(req->NameOffset),
+ le16_to_cpu(req->NameLength),
+ work->conn->local_nls);
+ if (IS_ERR(name)) {
+@@ -4309,7 +4309,7 @@ int smb2_query_dir(struct ksmbd_work *work)
+ }
+
+ srch_flag = req->Flags;
+- srch_ptr = smb_strndup_from_utf16(req->Buffer,
++ srch_ptr = smb_strndup_from_utf16((char *)req + le16_to_cpu(req->FileNameOffset),
+ le16_to_cpu(req->FileNameLength), 1,
+ conn->local_nls);
+ if (IS_ERR(srch_ptr)) {
+@@ -4569,7 +4569,8 @@ static int smb2_get_ea(struct ksmbd_work *work, struct ksmbd_file *fp,
+ sizeof(struct smb2_ea_info_req))
+ return -EINVAL;
+
+- ea_req = (struct smb2_ea_info_req *)req->Buffer;
++ ea_req = (struct smb2_ea_info_req *)((char *)req +
++ le16_to_cpu(req->InputBufferOffset));
+ } else {
+ /* need to send all EAs, if no specific EA is requested*/
+ if (le32_to_cpu(req->Flags) & SL_RETURN_SINGLE_ENTRY)
+@@ -6216,6 +6217,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp,
+ struct ksmbd_share_config *share)
+ {
+ unsigned int buf_len = le32_to_cpu(req->BufferLength);
++ char *buffer = (char *)req + le16_to_cpu(req->BufferOffset);
+
+ switch (req->FileInfoClass) {
+ case FILE_BASIC_INFORMATION:
+@@ -6223,7 +6225,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp,
+ if (buf_len < sizeof(struct smb2_file_basic_info))
+ return -EINVAL;
+
+- return set_file_basic_info(fp, (struct smb2_file_basic_info *)req->Buffer, share);
++ return set_file_basic_info(fp, (struct smb2_file_basic_info *)buffer, share);
+ }
+ case FILE_ALLOCATION_INFORMATION:
+ {
+@@ -6231,7 +6233,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp,
+ return -EINVAL;
+
+ return set_file_allocation_info(work, fp,
+- (struct smb2_file_alloc_info *)req->Buffer);
++ (struct smb2_file_alloc_info *)buffer);
+ }
+ case FILE_END_OF_FILE_INFORMATION:
+ {
+@@ -6239,7 +6241,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp,
+ return -EINVAL;
+
+ return set_end_of_file_info(work, fp,
+- (struct smb2_file_eof_info *)req->Buffer);
++ (struct smb2_file_eof_info *)buffer);
+ }
+ case FILE_RENAME_INFORMATION:
+ {
+@@ -6247,7 +6249,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp,
+ return -EINVAL;
+
+ return set_rename_info(work, fp,
+- (struct smb2_file_rename_info *)req->Buffer,
++ (struct smb2_file_rename_info *)buffer,
+ buf_len);
+ }
+ case FILE_LINK_INFORMATION:
+@@ -6256,7 +6258,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp,
+ return -EINVAL;
+
+ return smb2_create_link(work, work->tcon->share_conf,
+- (struct smb2_file_link_info *)req->Buffer,
++ (struct smb2_file_link_info *)buffer,
+ buf_len, fp->filp,
+ work->conn->local_nls);
+ }
+@@ -6266,7 +6268,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp,
+ return -EINVAL;
+
+ return set_file_disposition_info(fp,
+- (struct smb2_file_disposition_info *)req->Buffer);
++ (struct smb2_file_disposition_info *)buffer);
+ }
+ case FILE_FULL_EA_INFORMATION:
+ {
+@@ -6279,7 +6281,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp,
+ if (buf_len < sizeof(struct smb2_ea_info))
+ return -EINVAL;
+
+- return smb2_set_ea((struct smb2_ea_info *)req->Buffer,
++ return smb2_set_ea((struct smb2_ea_info *)buffer,
+ buf_len, &fp->filp->f_path, true);
+ }
+ case FILE_POSITION_INFORMATION:
+@@ -6287,14 +6289,14 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp,
+ if (buf_len < sizeof(struct smb2_file_pos_info))
+ return -EINVAL;
+
+- return set_file_position_info(fp, (struct smb2_file_pos_info *)req->Buffer);
++ return set_file_position_info(fp, (struct smb2_file_pos_info *)buffer);
+ }
+ case FILE_MODE_INFORMATION:
+ {
+ if (buf_len < sizeof(struct smb2_file_mode_info))
+ return -EINVAL;
+
+- return set_file_mode_info(fp, (struct smb2_file_mode_info *)req->Buffer);
++ return set_file_mode_info(fp, (struct smb2_file_mode_info *)buffer);
+ }
+ }
+
+@@ -6375,7 +6377,7 @@ int smb2_set_info(struct ksmbd_work *work)
+ }
+ rc = smb2_set_info_sec(fp,
+ le32_to_cpu(req->AdditionalInformation),
+- req->Buffer,
++ (char *)req + le16_to_cpu(req->BufferOffset),
+ le32_to_cpu(req->BufferLength));
+ ksmbd_revert_fsids(work);
+ break;
+@@ -7821,7 +7823,7 @@ static int fsctl_pipe_transceive(struct ksmbd_work *work, u64 id,
+ struct smb2_ioctl_rsp *rsp)
+ {
+ struct ksmbd_rpc_command *rpc_resp;
+- char *data_buf = (char *)&req->Buffer[0];
++ char *data_buf = (char *)req + le32_to_cpu(req->InputOffset);
+ int nbytes = 0;
+
+ rpc_resp = ksmbd_rpc_ioctl(work->sess, id, data_buf,
+@@ -7934,6 +7936,7 @@ int smb2_ioctl(struct ksmbd_work *work)
+ u64 id = KSMBD_NO_FID;
+ struct ksmbd_conn *conn = work->conn;
+ int ret = 0;
++ char *buffer;
+
+ if (work->next_smb2_rcv_hdr_off) {
+ req = ksmbd_req_buf_next(work);
+@@ -7956,6 +7959,8 @@ int smb2_ioctl(struct ksmbd_work *work)
+ goto out;
+ }
+
++ buffer = (char *)req + le32_to_cpu(req->InputOffset);
++
+ cnt_code = le32_to_cpu(req->CtlCode);
+ ret = smb2_calc_max_out_buf_len(work, 48,
+ le32_to_cpu(req->MaxOutputResponse));
+@@ -8013,7 +8018,7 @@ int smb2_ioctl(struct ksmbd_work *work)
+ }
+
+ ret = fsctl_validate_negotiate_info(conn,
+- (struct validate_negotiate_info_req *)&req->Buffer[0],
++ (struct validate_negotiate_info_req *)buffer,
+ (struct validate_negotiate_info_rsp *)&rsp->Buffer[0],
+ in_buf_len);
+ if (ret < 0)
+@@ -8066,7 +8071,7 @@ int smb2_ioctl(struct ksmbd_work *work)
+ rsp->VolatileFileId = req->VolatileFileId;
+ rsp->PersistentFileId = req->PersistentFileId;
+ fsctl_copychunk(work,
+- (struct copychunk_ioctl_req *)&req->Buffer[0],
++ (struct copychunk_ioctl_req *)buffer,
+ le32_to_cpu(req->CtlCode),
+ le32_to_cpu(req->InputCount),
+ req->VolatileFileId,
+@@ -8079,8 +8084,7 @@ int smb2_ioctl(struct ksmbd_work *work)
+ goto out;
+ }
+
+- ret = fsctl_set_sparse(work, id,
+- (struct file_sparse *)&req->Buffer[0]);
++ ret = fsctl_set_sparse(work, id, (struct file_sparse *)buffer);
+ if (ret < 0)
+ goto out;
+ break;
+@@ -8103,7 +8107,7 @@ int smb2_ioctl(struct ksmbd_work *work)
+ }
+
+ zero_data =
+- (struct file_zero_data_information *)&req->Buffer[0];
++ (struct file_zero_data_information *)buffer;
+
+ off = le64_to_cpu(zero_data->FileOffset);
+ bfz = le64_to_cpu(zero_data->BeyondFinalZero);
+@@ -8134,7 +8138,7 @@ int smb2_ioctl(struct ksmbd_work *work)
+ }
+
+ ret = fsctl_query_allocated_ranges(work, id,
+- (struct file_allocated_range_buffer *)&req->Buffer[0],
++ (struct file_allocated_range_buffer *)buffer,
+ (struct file_allocated_range_buffer *)&rsp->Buffer[0],
+ out_buf_len /
+ sizeof(struct file_allocated_range_buffer), &nbytes);
+@@ -8178,7 +8182,7 @@ int smb2_ioctl(struct ksmbd_work *work)
+ goto out;
+ }
+
+- dup_ext = (struct duplicate_extents_to_file *)&req->Buffer[0];
++ dup_ext = (struct duplicate_extents_to_file *)buffer;
+
+ fp_in = ksmbd_lookup_fd_slow(work, dup_ext->VolatileFileHandle,
+ dup_ext->PersistentFileHandle);
+--
+2.43.0
+
diff --git a/queue-6.6/ksmbd-fix-slab-out-of-bounds-in-smb_strndup_from_utf.patch b/queue-6.6/ksmbd-fix-slab-out-of-bounds-in-smb_strndup_from_utf.patch
new file mode 100644
index 0000000000..09d96965c5
--- /dev/null
+++ b/queue-6.6/ksmbd-fix-slab-out-of-bounds-in-smb_strndup_from_utf.patch
@@ -0,0 +1,44 @@
+From 1d1854317e5b7cf9f6498d4a7dfcb3141e0c74a5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 16 Mar 2024 23:36:36 +0900
+Subject: ksmbd: fix slab-out-of-bounds in smb_strndup_from_utf16()
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit d10c77873ba1e9e6b91905018e29e196fd5f863d ]
+
+If ->NameOffset/Length is bigger than ->CreateContextsOffset/Length,
+ksmbd_check_message doesn't validate request buffer it correctly.
+So slab-out-of-bounds warning from calling smb_strndup_from_utf16()
+in smb2_open() could happen. If ->NameLength is non-zero, Set the larger
+of the two sums (Name and CreateContext size) as the offset and length of
+the data area.
+
+Reported-by: Yang Chaoming <lometsj@live.com>
+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/smb2misc.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/fs/smb/server/smb2misc.c b/fs/smb/server/smb2misc.c
+index 03dded29a9804..7c872ffb4b0a9 100644
+--- a/fs/smb/server/smb2misc.c
++++ b/fs/smb/server/smb2misc.c
+@@ -107,7 +107,10 @@ static int smb2_get_data_area_len(unsigned int *off, unsigned int *len,
+ case SMB2_CREATE:
+ {
+ unsigned short int name_off =
+- le16_to_cpu(((struct smb2_create_req *)hdr)->NameOffset);
++ max_t(unsigned short int,
++ le16_to_cpu(((struct smb2_create_req *)hdr)->NameOffset),
++ offsetof(struct smb2_create_req, Buffer));
++
+ unsigned short int name_len =
+ le16_to_cpu(((struct smb2_create_req *)hdr)->NameLength);
+
+--
+2.43.0
+
diff --git a/queue-6.6/ksmbd-fix-spelling-mistake-connction-connection.patch b/queue-6.6/ksmbd-fix-spelling-mistake-connction-connection.patch
new file mode 100644
index 0000000000..15d8854879
--- /dev/null
+++ b/queue-6.6/ksmbd-fix-spelling-mistake-connction-connection.patch
@@ -0,0 +1,35 @@
+From 91048b84ab8a5cb49ebe92ddd58b1ccc38f39aab Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 13 Mar 2024 09:16:16 +0000
+Subject: ksmbd: Fix spelling mistake "connction" -> "connection"
+
+From: Colin Ian King <colin.i.king@gmail.com>
+
+[ Upstream commit e758fa6956cbc873e4819ec3dd97cfd05a4c147e ]
+
+There is a spelling mistake in a ksmbd_debug debug 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/oplock.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c
+index 58bafe23ded9a..b7adb6549aa0f 100644
+--- a/fs/smb/server/oplock.c
++++ b/fs/smb/server/oplock.c
+@@ -1856,7 +1856,7 @@ int smb2_check_durable_oplock(struct ksmbd_conn *conn,
+
+ if (memcmp(conn->ClientGUID, fp->client_guid,
+ SMB2_CLIENT_GUID_SIZE)) {
+- ksmbd_debug(SMB, "Client guid of fp is not equal to the one of connction\n");
++ ksmbd_debug(SMB, "Client guid of fp is not equal to the one of connection\n");
+ ret = -EBADF;
+ goto out;
+ }
+--
+2.43.0
+
diff --git a/queue-6.6/ksmbd-mark-smb2_session_expired-to-session-when-dest.patch b/queue-6.6/ksmbd-mark-smb2_session_expired-to-session-when-dest.patch
new file mode 100644
index 0000000000..6bfaca9e6c
--- /dev/null
+++ b/queue-6.6/ksmbd-mark-smb2_session_expired-to-session-when-dest.patch
@@ -0,0 +1,124 @@
+From bc000033032d72cf5eb0ce62198318dd427d1dc6 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 10 Mar 2024 19:30:51 +0900
+Subject: ksmbd: mark SMB2_SESSION_EXPIRED to session when destroying previous
+ session
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit fa9415d4024fd0c58d24a4ad4f1826fb8bfcc4aa ]
+
+Currently ksmbd exit connection as well destroying previous session.
+When testing durable handle feaure, I found that
+destroy_previous_session() should destroy only session, i.e. the
+connection should be still alive. This patch mark SMB2_SESSION_EXPIRED
+on the previous session to be destroyed later and not used anymore.
+
+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/user_session.c | 27 ++++++++++++++++++++++++++-
+ fs/smb/server/mgmt/user_session.h | 3 +++
+ fs/smb/server/smb2pdu.c | 24 ------------------------
+ 3 files changed, 29 insertions(+), 25 deletions(-)
+
+diff --git a/fs/smb/server/mgmt/user_session.c b/fs/smb/server/mgmt/user_session.c
+index 15f68ee050894..83074672fe812 100644
+--- a/fs/smb/server/mgmt/user_session.c
++++ b/fs/smb/server/mgmt/user_session.c
+@@ -156,7 +156,7 @@ void ksmbd_session_destroy(struct ksmbd_session *sess)
+ kfree(sess);
+ }
+
+-static struct ksmbd_session *__session_lookup(unsigned long long id)
++struct ksmbd_session *__session_lookup(unsigned long long id)
+ {
+ struct ksmbd_session *sess;
+
+@@ -305,6 +305,31 @@ struct preauth_session *ksmbd_preauth_session_alloc(struct ksmbd_conn *conn,
+ return sess;
+ }
+
++void destroy_previous_session(struct ksmbd_conn *conn,
++ struct ksmbd_user *user, u64 id)
++{
++ struct ksmbd_session *prev_sess;
++ struct ksmbd_user *prev_user;
++
++ down_write(&sessions_table_lock);
++ down_write(&conn->session_lock);
++ prev_sess = __session_lookup(id);
++ if (!prev_sess || prev_sess->state == SMB2_SESSION_EXPIRED)
++ goto out;
++
++ prev_user = prev_sess->user;
++ if (!prev_user ||
++ strcmp(user->name, prev_user->name) ||
++ user->passkey_sz != prev_user->passkey_sz ||
++ memcmp(user->passkey, prev_user->passkey, user->passkey_sz))
++ goto out;
++
++ prev_sess->state = SMB2_SESSION_EXPIRED;
++out:
++ up_write(&conn->session_lock);
++ up_write(&sessions_table_lock);
++}
++
+ static bool ksmbd_preauth_session_id_match(struct preauth_session *sess,
+ unsigned long long id)
+ {
+diff --git a/fs/smb/server/mgmt/user_session.h b/fs/smb/server/mgmt/user_session.h
+index 63cb08fffde84..dc9fded2cd437 100644
+--- a/fs/smb/server/mgmt/user_session.h
++++ b/fs/smb/server/mgmt/user_session.h
+@@ -88,8 +88,11 @@ struct ksmbd_session *ksmbd_session_lookup(struct ksmbd_conn *conn,
+ int ksmbd_session_register(struct ksmbd_conn *conn,
+ struct ksmbd_session *sess);
+ void ksmbd_sessions_deregister(struct ksmbd_conn *conn);
++struct ksmbd_session *__session_lookup(unsigned long long id);
+ struct ksmbd_session *ksmbd_session_lookup_all(struct ksmbd_conn *conn,
+ unsigned long long id);
++void destroy_previous_session(struct ksmbd_conn *conn,
++ struct ksmbd_user *user, u64 id);
+ struct preauth_session *ksmbd_preauth_session_alloc(struct ksmbd_conn *conn,
+ u64 sess_id);
+ struct preauth_session *ksmbd_preauth_session_lookup(struct ksmbd_conn *conn,
+diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
+index fb9eea631069e..61717917db765 100644
+--- a/fs/smb/server/smb2pdu.c
++++ b/fs/smb/server/smb2pdu.c
+@@ -611,30 +611,6 @@ int smb2_check_user_session(struct ksmbd_work *work)
+ return -ENOENT;
+ }
+
+-static void destroy_previous_session(struct ksmbd_conn *conn,
+- struct ksmbd_user *user, u64 id)
+-{
+- struct ksmbd_session *prev_sess = ksmbd_session_lookup_slowpath(id);
+- struct ksmbd_user *prev_user;
+- struct channel *chann;
+- long index;
+-
+- if (!prev_sess)
+- return;
+-
+- prev_user = prev_sess->user;
+-
+- if (!prev_user ||
+- strcmp(user->name, prev_user->name) ||
+- user->passkey_sz != prev_user->passkey_sz ||
+- memcmp(user->passkey, prev_user->passkey, user->passkey_sz))
+- return;
+-
+- prev_sess->state = SMB2_SESSION_EXPIRED;
+- xa_for_each(&prev_sess->ksmbd_chann_list, index, chann)
+- ksmbd_conn_set_exiting(chann->conn);
+-}
+-
+ /**
+ * smb2_get_name() - get filename string from on the wire smb format
+ * @src: source buffer
+--
+2.43.0
+
diff --git a/queue-6.6/ksmbd-vfs-fix-all-kernel-doc-warnings.patch b/queue-6.6/ksmbd-vfs-fix-all-kernel-doc-warnings.patch
new file mode 100644
index 0000000000..2b44bf8de5
--- /dev/null
+++ b/queue-6.6/ksmbd-vfs-fix-all-kernel-doc-warnings.patch
@@ -0,0 +1,176 @@
+From b5a7f8ce7f3264cc3e322fa89cecec72b86ba2dc Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 15 Dec 2023 19:28:14 -0800
+Subject: ksmbd: vfs: fix all kernel-doc warnings
+
+From: Randy Dunlap <rdunlap@infradead.org>
+
+[ Upstream commit 8d99c1131d9d03053b7b1e1245b8f6e6846d9c69 ]
+
+Fix all kernel-doc warnings in vfs.c:
+
+vfs.c:54: warning: Function parameter or member 'parent' not described in 'ksmbd_vfs_lock_parent'
+vfs.c:54: warning: Function parameter or member 'child' not described in 'ksmbd_vfs_lock_parent'
+vfs.c:54: warning: No description found for return value of 'ksmbd_vfs_lock_parent'
+vfs.c:372: warning: Function parameter or member 'fp' not described in 'ksmbd_vfs_read'
+vfs.c:372: warning: Excess function parameter 'fid' description in 'ksmbd_vfs_read'
+vfs.c:489: warning: Function parameter or member 'fp' not described in 'ksmbd_vfs_write'
+vfs.c:489: warning: Excess function parameter 'fid' description in 'ksmbd_vfs_write'
+vfs.c:555: warning: Function parameter or member 'path' not described in 'ksmbd_vfs_getattr'
+vfs.c:555: warning: Function parameter or member 'stat' not described in 'ksmbd_vfs_getattr'
+vfs.c:555: warning: Excess function parameter 'work' description in 'ksmbd_vfs_getattr'
+vfs.c:555: warning: Excess function parameter 'fid' description in 'ksmbd_vfs_getattr'
+vfs.c:555: warning: Excess function parameter 'attrs' description in 'ksmbd_vfs_getattr'
+vfs.c:572: warning: Function parameter or member 'p_id' not described in 'ksmbd_vfs_fsync'
+vfs.c:595: warning: Function parameter or member 'work' not described in 'ksmbd_vfs_remove_file'
+vfs.c:595: warning: Function parameter or member 'path' not described in 'ksmbd_vfs_remove_file'
+vfs.c:595: warning: Excess function parameter 'name' description in 'ksmbd_vfs_remove_file'
+vfs.c:633: warning: Function parameter or member 'work' not described in 'ksmbd_vfs_link'
+vfs.c:805: warning: Function parameter or member 'fp' not described in 'ksmbd_vfs_truncate'
+vfs.c:805: warning: Excess function parameter 'fid' description in 'ksmbd_vfs_truncate'
+vfs.c:846: warning: Excess function parameter 'size' description in 'ksmbd_vfs_listxattr'
+vfs.c:953: warning: Function parameter or member 'option' not described in 'ksmbd_vfs_set_fadvise'
+vfs.c:953: warning: Excess function parameter 'options' description in 'ksmbd_vfs_set_fadvise'
+vfs.c:1167: warning: Function parameter or member 'um' not described in 'ksmbd_vfs_lookup_in_dir'
+vfs.c:1203: warning: Function parameter or member 'work' not described in 'ksmbd_vfs_kern_path_locked'
+vfs.c:1641: warning: No description found for return value of 'ksmbd_vfs_init_kstat'
+
+Signed-off-by: Randy Dunlap <rdunlap@infradead.org>
+Cc: Namjae Jeon <linkinjeon@kernel.org>
+Cc: Steve French <sfrench@samba.org>
+Cc: Sergey Senozhatsky <senozhatsky@chromium.org>
+Cc: Tom Talpey <tom@talpey.com>
+Cc: linux-cifs@vger.kernel.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/vfs.c | 28 ++++++++++++++++++----------
+ 1 file changed, 18 insertions(+), 10 deletions(-)
+
+diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c
+index 626406b0cf4ac..2558119969359 100644
+--- a/fs/smb/server/vfs.c
++++ b/fs/smb/server/vfs.c
+@@ -49,6 +49,10 @@ static void ksmbd_vfs_inherit_owner(struct ksmbd_work *work,
+
+ /**
+ * ksmbd_vfs_lock_parent() - lock parent dentry if it is stable
++ * @parent: parent dentry
++ * @child: child dentry
++ *
++ * Returns: %0 on success, %-ENOENT if the parent dentry is not stable
+ */
+ int ksmbd_vfs_lock_parent(struct dentry *parent, struct dentry *child)
+ {
+@@ -360,7 +364,7 @@ static int check_lock_range(struct file *filp, loff_t start, loff_t end,
+ /**
+ * ksmbd_vfs_read() - vfs helper for smb file read
+ * @work: smb work
+- * @fid: file id of open file
++ * @fp: ksmbd file pointer
+ * @count: read byte count
+ * @pos: file pos
+ * @rbuf: read data buffer
+@@ -474,7 +478,7 @@ static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos,
+ /**
+ * ksmbd_vfs_write() - vfs helper for smb file write
+ * @work: work
+- * @fid: file id of open file
++ * @fp: ksmbd file pointer
+ * @buf: buf containing data for writing
+ * @count: read byte count
+ * @pos: file pos
+@@ -545,10 +549,8 @@ int ksmbd_vfs_write(struct ksmbd_work *work, struct ksmbd_file *fp,
+
+ /**
+ * ksmbd_vfs_getattr() - vfs helper for smb getattr
+- * @work: work
+- * @fid: file id of open file
+- * @attrs: inode attributes
+- *
++ * @path: path of dentry
++ * @stat: pointer to returned kernel stat structure
+ * Return: 0 on success, otherwise error
+ */
+ int ksmbd_vfs_getattr(const struct path *path, struct kstat *stat)
+@@ -565,6 +567,7 @@ int ksmbd_vfs_getattr(const struct path *path, struct kstat *stat)
+ * ksmbd_vfs_fsync() - vfs helper for smb fsync
+ * @work: work
+ * @fid: file id of open file
++ * @p_id: persistent file id
+ *
+ * Return: 0 on success, otherwise error
+ */
+@@ -587,7 +590,8 @@ int ksmbd_vfs_fsync(struct ksmbd_work *work, u64 fid, u64 p_id)
+
+ /**
+ * ksmbd_vfs_remove_file() - vfs helper for smb rmdir or unlink
+- * @name: directory or file name that is relative to share
++ * @work: work
++ * @path: path of dentry
+ *
+ * Return: 0 on success, otherwise error
+ */
+@@ -623,6 +627,7 @@ int ksmbd_vfs_remove_file(struct ksmbd_work *work, const struct path *path)
+
+ /**
+ * ksmbd_vfs_link() - vfs helper for creating smb hardlink
++ * @work: work
+ * @oldname: source file name
+ * @newname: hardlink name that is relative to share
+ *
+@@ -800,7 +805,7 @@ int ksmbd_vfs_rename(struct ksmbd_work *work, const struct path *old_path,
+ /**
+ * ksmbd_vfs_truncate() - vfs helper for smb file truncate
+ * @work: work
+- * @fid: file id of old file
++ * @fp: ksmbd file pointer
+ * @size: truncate to given size
+ *
+ * Return: 0 on success, otherwise error
+@@ -843,7 +848,6 @@ int ksmbd_vfs_truncate(struct ksmbd_work *work,
+ * ksmbd_vfs_listxattr() - vfs helper for smb list extended attributes
+ * @dentry: dentry of file for listing xattrs
+ * @list: destination buffer
+- * @size: destination buffer length
+ *
+ * Return: xattr list length on success, otherwise error
+ */
+@@ -952,7 +956,7 @@ int ksmbd_vfs_setxattr(struct mnt_idmap *idmap,
+ /**
+ * ksmbd_vfs_set_fadvise() - convert smb IO caching options to linux options
+ * @filp: file pointer for IO
+- * @options: smb IO options
++ * @option: smb IO options
+ */
+ void ksmbd_vfs_set_fadvise(struct file *filp, __le32 option)
+ {
+@@ -1164,6 +1168,7 @@ static bool __caseless_lookup(struct dir_context *ctx, const char *name,
+ * @dir: path info
+ * @name: filename to lookup
+ * @namelen: filename length
++ * @um: &struct unicode_map to use
+ *
+ * Return: 0 on success, otherwise error
+ */
+@@ -1194,6 +1199,7 @@ 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
++ * @work: work
+ * @name: file path that is relative to share
+ * @flags: lookup flags
+ * @parent_path: if lookup succeed, return parent_path info
+@@ -1641,6 +1647,8 @@ int ksmbd_vfs_get_dos_attrib_xattr(struct mnt_idmap *idmap,
+ * ksmbd_vfs_init_kstat() - convert unix stat information to smb stat format
+ * @p: destination buffer
+ * @ksmbd_kstat: ksmbd kstat wrapper
++ *
++ * Returns: pointer to the converted &struct file_directory_info
+ */
+ void *ksmbd_vfs_init_kstat(char **p, struct ksmbd_kstat *ksmbd_kstat)
+ {
+--
+2.43.0
+
diff --git a/queue-6.6/missing-field-not-being-returned-in-ioctl-cifs_ioc_g.patch b/queue-6.6/missing-field-not-being-returned-in-ioctl-cifs_ioc_g.patch
new file mode 100644
index 0000000000..6d773e70a7
--- /dev/null
+++ b/queue-6.6/missing-field-not-being-returned-in-ioctl-cifs_ioc_g.patch
@@ -0,0 +1,36 @@
+From 2da23056afe13b1f84326db01aae30916e859209 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 10 Nov 2023 01:24:16 -0600
+Subject: Missing field not being returned in ioctl CIFS_IOC_GET_MNT_INFO
+
+From: Steve French <stfrench@microsoft.com>
+
+[ Upstream commit 784e0e20b4c97c270b2892f677d3fad658e2c1d5 ]
+
+The tcon_flags field was always being set to zero in the information
+about the mount returned by the ioctl CIFS_IOC_GET_MNT_INFO instead
+of being set to the value of the Flags field in the tree connection
+structure as intended.
+
+Reviewed-by: Shyam Prasad N <sprasad@microsoft.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/ioctl.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/fs/smb/client/ioctl.c b/fs/smb/client/ioctl.c
+index 204dd7c47126e..682eabdd1d6cc 100644
+--- a/fs/smb/client/ioctl.c
++++ b/fs/smb/client/ioctl.c
+@@ -143,6 +143,7 @@ static long smb_mnt_get_fsinfo(unsigned int xid, struct cifs_tcon *tcon,
+
+ fsinf->version = 1;
+ fsinf->protocol_id = tcon->ses->server->vals->protocol_id;
++ fsinf->tcon_flags = tcon->Flags;
+ fsinf->device_characteristics =
+ le32_to_cpu(tcon->fsDevInfo.DeviceCharacteristics);
+ fsinf->device_type = le32_to_cpu(tcon->fsDevInfo.DeviceType);
+--
+2.43.0
+
diff --git a/queue-6.6/series b/queue-6.6/series
new file mode 100644
index 0000000000..167c196ee8
--- /dev/null
+++ b/queue-6.6/series
@@ -0,0 +1,79 @@
+cifs-add-client-version-details-to-ntlm-authenticate.patch
+smb3-clarify-some-of-the-unused-createoption-flags.patch
+add-definition-for-new-smb3.1.1-command-type.patch
+smb-use-crypto_shash_digest-in-symlink_hash.patch
+cifs-print-server-capabilities-in-debugdata.patch
+smb3-minor-rdma-cleanup.patch
+smb3-more-minor-cleanups-for-session-handling-routin.patch
+smb3-minor-cleanup-of-session-handling-code.patch
+missing-field-not-being-returned-in-ioctl-cifs_ioc_g.patch
+cifs-update-internal-module-version-number-for-cifs..patch
+cifs-fix-use-after-free-for-iface-while-disabling-se.patch
+smb-client-introduce-cifs_sfu_make_node.patch
+smb-client-fix-minor-whitespace-errors-and-warnings.patch
+smb-client-extend-smb2_compound_op-to-accept-more-co.patch
+smb-client-allow-creating-special-files-via-reparse-.patch
+smb-client-optimise-reparse-point-querying.patch
+smb-client-allow-creating-symlinks-via-reparse-point.patch
+smb-client-cleanup-smb2_query_reparse_point.patch
+smb-client-handle-special-files-and-symlinks-in-smb3.patch
+cifs-fix-in-logging-in-cifs_chan_update_iface.patch
+smb3-improve-exception-handling-in-allocate_mr_list.patch
+cifs-pass-unbyteswapped-eof-value-into-smb2_set_eof.patch
+cifs-get-rid-of-dup-length-check-in-parse_reparse_po.patch
+cifs-remove-unneeded-return-statement.patch
+ksmbd-auth-fix-most-kernel-doc-warnings.patch
+ksmbd-vfs-fix-all-kernel-doc-warnings.patch
+cifs-update-internal-module-version-number-for-cifs..patch-8866
+cifs-remove-redundant-variable-tcon_exist.patch
+cifs-minor-comment-cleanup.patch
+cifs-pick-channel-for-tcon-and-tdis.patch
+cifs-new-nt-status-codes-from-ms-smb2.patch
+smb-client-don-t-clobber-i_rdev-from-cached-reparse-.patch
+cifs-new-mount-option-called-retrans.patch
+smb-fix-some-kernel-doc-comments.patch
+smb-client-delete-true-false-defines.patch
+cifs-commands-that-are-retried-should-have-replay-fl.patch
+cifs-set-replay-flag-for-retries-of-write-command.patch
+ksmbd-add-kernel-doc-for-ksmbd_extract_sharename-fun.patch
+cifs-update-the-same-create_guid-on-replay.patch
+smb-client-handle-path-separator-of-created-smb-syml.patch
+smb3-update-allocation-size-more-accurately-on-write.patch
+smb-client-parse-owner-group-when-creating-reparse-p.patch
+smb-client-get-rid-of-smb311_posix_query_path_info.patch
+smb-client-reuse-file-lease-key-in-compound-operatio.patch
+smb-client-do-not-defer-close-open-handles-to-delete.patch
+smb-client-retry-compound-request-without-reusing-le.patch
+smb-client-introduce-reparse-mount-option.patch
+smb-client-move-most-of-reparse-point-handling-code-.patch
+smb-client-fix-potential-broken-compound-request.patch
+smb-client-reduce-number-of-parameters-in-smb2_compo.patch
+smb-client-add-support-for-wsl-reparse-points.patch
+smb-client-fix-a-null-vs-is_err-check-in-wsl_set_xat.patch
+smb-client-introduce-smb2_op_query_wsl_ea.patch
+smb-client-parse-uid-gid-mode-and-dev-from-wsl-repar.patch
+smb-client-set-correct-d_type-for-reparse-dfs-dfsr-a.patch
+smb-client-return-reparse-type-in-proc-mounts.patch
+smb3-add-dynamic-trace-point-for-ioctls.patch
+smb-client-negotiate-compression-algorithms.patch
+smb-common-fix-fields-sizes-in-compression_pattern_p.patch
+smb-common-simplify-compression-headers.patch
+cifs-update-internal-module-version-number-for-cifs..patch-9055
+ksmbd-mark-smb2_session_expired-to-session-when-dest.patch
+ksmbd-add-support-for-durable-handles-v1-v2.patch
+cifs-defer-close-file-handles-having-rh-lease.patch
+cifs-fixes-for-get_inode_info.patch
+cifs-remove-redundant-variable-assignment.patch
+ksmbd-fix-possible-null-deref-in-smb_lazy_parent_lea.patch
+ksmbd-fix-spelling-mistake-connction-connection.patch
+ksmbd-fix-slab-out-of-bounds-in-smb_strndup_from_utf.patch
+ksmbd-fix-potencial-out-of-bounds-when-buffer-offset.patch
+cifs-move-some-extern-decls-from-.c-files-to-.h.patch
+smb311-correct-incorrect-offset-field-in-compression.patch
+smb311-additional-compression-flag-defined-in-update.patch
+smb3-add-trace-event-for-mknod.patch
+smb-client-fix-null-ptr-deref-in-cifs_mark_open_hand.patch
+smb-client-instantiate-when-creating-sfu-files.patch
+cifs-add-tracing-for-the-cifs_tcon-struct-refcountin.patch
+ksmbd-add-continuous-availability-share-parameter.patch
+smb-smb2pdu.h-avoid-wflex-array-member-not-at-end-wa.patch
diff --git a/queue-6.6/smb-client-add-support-for-wsl-reparse-points.patch b/queue-6.6/smb-client-add-support-for-wsl-reparse-points.patch
new file mode 100644
index 0000000000..a08d604b98
--- /dev/null
+++ b/queue-6.6/smb-client-add-support-for-wsl-reparse-points.patch
@@ -0,0 +1,428 @@
+From d32fc0d545a9d69169dc935384b7e4af30141e18 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Jan 2024 19:26:06 -0300
+Subject: smb: client: add support for WSL reparse points
+
+From: Paulo Alcantara <pc@manguebit.com>
+
+[ Upstream commit 5a4b09ecf8e8ad26ea03a37e52e310fe13f15b49 ]
+
+Add support for creating special files via WSL reparse points when
+using 'reparse=wsl' mount option. They're faster than NFS reparse
+points because they don't require extra roundtrips to figure out what
+->d_type a specific dirent is as such information is already stored in
+query dir responses and then making getdents() calls faster.
+
+Signed-off-by: Paulo Alcantara <pc@manguebit.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/cifsglob.h | 1 +
+ fs/smb/client/fs_context.c | 4 +-
+ fs/smb/client/reparse.c | 170 +++++++++++++++++++++++++++++++++++--
+ fs/smb/client/reparse.h | 13 ++-
+ fs/smb/client/smb2inode.c | 8 +-
+ fs/smb/client/smb2ops.c | 2 +-
+ fs/smb/client/smb2pdu.c | 12 +++
+ fs/smb/client/smb2pdu.h | 11 ++-
+ fs/smb/client/smb2proto.h | 3 +-
+ fs/smb/common/smbfsctl.h | 6 --
+ 10 files changed, 210 insertions(+), 20 deletions(-)
+
+diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h
+index 5a902fb20ac96..54758825fa8d9 100644
+--- a/fs/smb/client/cifsglob.h
++++ b/fs/smb/client/cifsglob.h
+@@ -1379,6 +1379,7 @@ struct cifs_open_parms {
+ umode_t mode;
+ bool reconnect:1;
+ bool replay:1; /* indicates that this open is for a replay */
++ struct kvec *ea_cctx;
+ };
+
+ struct cifs_fid {
+diff --git a/fs/smb/client/fs_context.c b/fs/smb/client/fs_context.c
+index 535885fbdf51d..fbaa901d3493a 100644
+--- a/fs/smb/client/fs_context.c
++++ b/fs/smb/client/fs_context.c
+@@ -318,8 +318,8 @@ static int parse_reparse_flavor(struct fs_context *fc, char *value,
+ ctx->reparse_type = CIFS_REPARSE_TYPE_NFS;
+ break;
+ case Opt_reparse_wsl:
+- cifs_errorf(fc, "unsupported reparse= option: %s\n", value);
+- return 1;
++ ctx->reparse_type = CIFS_REPARSE_TYPE_WSL;
++ break;
+ default:
+ cifs_errorf(fc, "bad reparse= option: %s\n", value);
+ return 1;
+diff --git a/fs/smb/client/reparse.c b/fs/smb/client/reparse.c
+index c405be47c84d9..b240ccc9c887c 100644
+--- a/fs/smb/client/reparse.c
++++ b/fs/smb/client/reparse.c
+@@ -11,6 +11,7 @@
+ #include "cifsproto.h"
+ #include "cifs_unicode.h"
+ #include "cifs_debug.h"
++#include "fs_context.h"
+ #include "reparse.h"
+
+ int smb2_create_reparse_symlink(const unsigned int xid, struct inode *inode,
+@@ -68,7 +69,7 @@ int smb2_create_reparse_symlink(const unsigned int xid, struct inode *inode,
+ iov.iov_base = buf;
+ iov.iov_len = len;
+ new = smb2_get_reparse_inode(&data, inode->i_sb, xid,
+- tcon, full_path, &iov);
++ tcon, full_path, &iov, NULL);
+ if (!IS_ERR(new))
+ d_instantiate(dentry, new);
+ else
+@@ -114,9 +115,9 @@ static int nfs_set_reparse_buf(struct reparse_posix_data *buf,
+ return 0;
+ }
+
+-int smb2_make_nfs_node(unsigned int xid, struct inode *inode,
+- struct dentry *dentry, struct cifs_tcon *tcon,
+- const char *full_path, umode_t mode, dev_t dev)
++static int mknod_nfs(unsigned int xid, struct inode *inode,
++ struct dentry *dentry, struct cifs_tcon *tcon,
++ const char *full_path, umode_t mode, dev_t dev)
+ {
+ struct cifs_open_info_data data;
+ struct reparse_posix_data *p;
+@@ -136,12 +137,171 @@ int smb2_make_nfs_node(unsigned int xid, struct inode *inode,
+ };
+
+ new = smb2_get_reparse_inode(&data, inode->i_sb, xid,
+- tcon, full_path, &iov);
++ tcon, full_path, &iov, NULL);
++ if (!IS_ERR(new))
++ d_instantiate(dentry, new);
++ else
++ rc = PTR_ERR(new);
++ cifs_free_open_info(&data);
++ return rc;
++}
++
++static int wsl_set_reparse_buf(struct reparse_data_buffer *buf,
++ mode_t mode, struct kvec *iov)
++{
++ u32 tag;
++
++ switch ((tag = reparse_mode_wsl_tag(mode))) {
++ case IO_REPARSE_TAG_LX_BLK:
++ case IO_REPARSE_TAG_LX_CHR:
++ case IO_REPARSE_TAG_LX_FIFO:
++ case IO_REPARSE_TAG_AF_UNIX:
++ break;
++ default:
++ return -EOPNOTSUPP;
++ }
++
++ buf->ReparseTag = cpu_to_le32(tag);
++ buf->Reserved = 0;
++ buf->ReparseDataLength = 0;
++ iov->iov_base = buf;
++ iov->iov_len = sizeof(*buf);
++ return 0;
++}
++
++static struct smb2_create_ea_ctx *ea_create_context(u32 dlen, size_t *cc_len)
++{
++ struct smb2_create_ea_ctx *cc;
++
++ *cc_len = round_up(sizeof(*cc) + dlen, 8);
++ cc = kzalloc(*cc_len, GFP_KERNEL);
++ if (!cc)
++ return ERR_PTR(-ENOMEM);
++
++ cc->ctx.NameOffset = cpu_to_le16(offsetof(struct smb2_create_ea_ctx,
++ name));
++ cc->ctx.NameLength = cpu_to_le16(4);
++ memcpy(cc->name, SMB2_CREATE_EA_BUFFER, strlen(SMB2_CREATE_EA_BUFFER));
++ cc->ctx.DataOffset = cpu_to_le16(offsetof(struct smb2_create_ea_ctx, ea));
++ cc->ctx.DataLength = cpu_to_le32(dlen);
++ return cc;
++}
++
++struct wsl_xattr {
++ const char *name;
++ __le64 value;
++ u16 size;
++ u32 next;
++};
++
++static int wsl_set_xattrs(struct inode *inode, umode_t _mode,
++ dev_t _dev, struct kvec *iov)
++{
++ struct smb2_file_full_ea_info *ea;
++ struct smb2_create_ea_ctx *cc;
++ struct smb3_fs_context *ctx = CIFS_SB(inode->i_sb)->ctx;
++ __le64 uid = cpu_to_le64(from_kuid(current_user_ns(), ctx->linux_uid));
++ __le64 gid = cpu_to_le64(from_kgid(current_user_ns(), ctx->linux_gid));
++ __le64 dev = cpu_to_le64(((u64)MINOR(_dev) << 32) | MAJOR(_dev));
++ __le64 mode = cpu_to_le64(_mode);
++ struct wsl_xattr xattrs[] = {
++ { .name = "$LXUID", .value = uid, .size = 4, },
++ { .name = "$LXGID", .value = gid, .size = 4, },
++ { .name = "$LXMOD", .value = mode, .size = 4, },
++ { .name = "$LXDEV", .value = dev, .size = 8, },
++ };
++ size_t cc_len;
++ u32 dlen = 0, next = 0;
++ int i, num_xattrs;
++ u8 name_size = strlen(xattrs[0].name) + 1;
++
++ memset(iov, 0, sizeof(*iov));
++
++ /* Exclude $LXDEV xattr for sockets and fifos */
++ if (S_ISSOCK(_mode) || S_ISFIFO(_mode))
++ num_xattrs = ARRAY_SIZE(xattrs) - 1;
++ else
++ num_xattrs = ARRAY_SIZE(xattrs);
++
++ for (i = 0; i < num_xattrs; i++) {
++ xattrs[i].next = ALIGN(sizeof(*ea) + name_size +
++ xattrs[i].size, 4);
++ dlen += xattrs[i].next;
++ }
++
++ cc = ea_create_context(dlen, &cc_len);
++ if (!cc)
++ return PTR_ERR(cc);
++
++ ea = &cc->ea;
++ for (i = 0; i < num_xattrs; i++) {
++ ea = (void *)((u8 *)ea + next);
++ next = xattrs[i].next;
++ ea->next_entry_offset = cpu_to_le32(next);
++
++ ea->ea_name_length = name_size - 1;
++ ea->ea_value_length = cpu_to_le16(xattrs[i].size);
++ memcpy(ea->ea_data, xattrs[i].name, name_size);
++ memcpy(&ea->ea_data[name_size],
++ &xattrs[i].value, xattrs[i].size);
++ }
++ ea->next_entry_offset = 0;
++
++ iov->iov_base = cc;
++ iov->iov_len = cc_len;
++ return 0;
++}
++
++static int mknod_wsl(unsigned int xid, struct inode *inode,
++ struct dentry *dentry, struct cifs_tcon *tcon,
++ const char *full_path, umode_t mode, dev_t dev)
++{
++ struct cifs_open_info_data data;
++ struct reparse_data_buffer buf;
++ struct inode *new;
++ struct kvec reparse_iov, xattr_iov;
++ int rc;
++
++ rc = wsl_set_reparse_buf(&buf, mode, &reparse_iov);
++ if (rc)
++ return rc;
++
++ rc = wsl_set_xattrs(inode, mode, dev, &xattr_iov);
++ if (rc)
++ return rc;
++
++ data = (struct cifs_open_info_data) {
++ .reparse_point = true,
++ .reparse = { .tag = le32_to_cpu(buf.ReparseTag), .buf = &buf, },
++ };
++
++ new = smb2_get_reparse_inode(&data, inode->i_sb,
++ xid, tcon, full_path,
++ &reparse_iov, &xattr_iov);
+ if (!IS_ERR(new))
+ d_instantiate(dentry, new);
+ else
+ rc = PTR_ERR(new);
+ cifs_free_open_info(&data);
++ kfree(xattr_iov.iov_base);
++ return rc;
++}
++
++int smb2_mknod_reparse(unsigned int xid, struct inode *inode,
++ struct dentry *dentry, struct cifs_tcon *tcon,
++ const char *full_path, umode_t mode, dev_t dev)
++{
++ struct smb3_fs_context *ctx = CIFS_SB(inode->i_sb)->ctx;
++ int rc = -EOPNOTSUPP;
++
++ switch (ctx->reparse_type) {
++ case CIFS_REPARSE_TYPE_NFS:
++ rc = mknod_nfs(xid, inode, dentry, tcon, full_path, mode, dev);
++ break;
++ case CIFS_REPARSE_TYPE_WSL:
++ rc = mknod_wsl(xid, inode, dentry, tcon, full_path, mode, dev);
++ break;
++ }
+ return rc;
+ }
+
+diff --git a/fs/smb/client/reparse.h b/fs/smb/client/reparse.h
+index 3ceb90da0df90..9816bac985525 100644
+--- a/fs/smb/client/reparse.h
++++ b/fs/smb/client/reparse.h
+@@ -28,6 +28,17 @@ static inline u64 reparse_mode_nfs_type(mode_t mode)
+ return 0;
+ }
+
++static inline u32 reparse_mode_wsl_tag(mode_t mode)
++{
++ switch (mode & S_IFMT) {
++ case S_IFBLK: return IO_REPARSE_TAG_LX_BLK;
++ case S_IFCHR: return IO_REPARSE_TAG_LX_CHR;
++ case S_IFIFO: return IO_REPARSE_TAG_LX_FIFO;
++ case S_IFSOCK: return IO_REPARSE_TAG_AF_UNIX;
++ }
++ return 0;
++}
++
+ /*
+ * Match a reparse point inode if reparse tag and ctime haven't changed.
+ *
+@@ -64,7 +75,7 @@ bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb,
+ int smb2_create_reparse_symlink(const unsigned int xid, struct inode *inode,
+ struct dentry *dentry, struct cifs_tcon *tcon,
+ const char *full_path, const char *symname);
+-int smb2_make_nfs_node(unsigned int xid, struct inode *inode,
++int smb2_mknod_reparse(unsigned int xid, struct inode *inode,
+ struct dentry *dentry, struct cifs_tcon *tcon,
+ const char *full_path, umode_t mode, dev_t dev);
+ int smb2_parse_reparse_point(struct cifs_sb_info *cifs_sb, struct kvec *rsp_iov,
+diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c
+index e1ad54b27b63e..0e1b9a1552e71 100644
+--- a/fs/smb/client/smb2inode.c
++++ b/fs/smb/client/smb2inode.c
+@@ -1060,7 +1060,8 @@ struct inode *smb2_get_reparse_inode(struct cifs_open_info_data *data,
+ const unsigned int xid,
+ struct cifs_tcon *tcon,
+ const char *full_path,
+- struct kvec *iov)
++ struct kvec *reparse_iov,
++ struct kvec *xattr_iov)
+ {
+ struct cifs_open_parms oparms;
+ struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
+@@ -1077,8 +1078,11 @@ struct inode *smb2_get_reparse_inode(struct cifs_open_info_data *data,
+ FILE_CREATE,
+ CREATE_NOT_DIR | OPEN_REPARSE_POINT,
+ ACL_NO_MODE);
++ if (xattr_iov)
++ oparms.ea_cctx = xattr_iov;
++
+ cmds[0] = SMB2_OP_SET_REPARSE;
+- in_iov[0] = *iov;
++ in_iov[0] = *reparse_iov;
+ in_iov[1].iov_base = data;
+ in_iov[1].iov_len = sizeof(*data);
+
+diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
+index 2c59a6fc2d7c7..981a922cf38f0 100644
+--- a/fs/smb/client/smb2ops.c
++++ b/fs/smb/client/smb2ops.c
+@@ -5038,7 +5038,7 @@ static int smb2_make_node(unsigned int xid, struct inode *inode,
+ rc = cifs_sfu_make_node(xid, inode, dentry, tcon,
+ full_path, mode, dev);
+ } else {
+- rc = smb2_make_nfs_node(xid, inode, dentry, tcon,
++ rc = smb2_mknod_reparse(xid, inode, dentry, tcon,
+ full_path, mode, dev);
+ }
+ return rc;
+diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c
+index 60793143e24c6..4d805933e14f3 100644
+--- a/fs/smb/client/smb2pdu.c
++++ b/fs/smb/client/smb2pdu.c
+@@ -2732,6 +2732,17 @@ add_query_id_context(struct kvec *iov, unsigned int *num_iovec)
+ return 0;
+ }
+
++static void add_ea_context(struct cifs_open_parms *oparms,
++ struct kvec *rq_iov, unsigned int *num_iovs)
++{
++ struct kvec *iov = oparms->ea_cctx;
++
++ if (iov && iov->iov_base && iov->iov_len) {
++ rq_iov[(*num_iovs)++] = *iov;
++ memset(iov, 0, sizeof(*iov));
++ }
++}
++
+ static int
+ alloc_path_with_tree_prefix(__le16 **out_path, int *out_size, int *out_len,
+ const char *treename, const __le16 *path)
+@@ -3098,6 +3109,7 @@ SMB2_open_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server,
+ }
+
+ add_query_id_context(iov, &n_iov);
++ add_ea_context(oparms, iov, &n_iov);
+
+ if (n_iov > 2) {
+ /*
+diff --git a/fs/smb/client/smb2pdu.h b/fs/smb/client/smb2pdu.h
+index b00f707bddfcc..3334f2c364fb9 100644
+--- a/fs/smb/client/smb2pdu.h
++++ b/fs/smb/client/smb2pdu.h
+@@ -117,9 +117,10 @@ struct share_redirect_error_context_rsp {
+ * [4] : posix context
+ * [5] : time warp context
+ * [6] : query id context
+- * [7] : compound padding
++ * [7] : create ea context
++ * [8] : compound padding
+ */
+-#define SMB2_CREATE_IOV_SIZE 8
++#define SMB2_CREATE_IOV_SIZE 9
+
+ /*
+ * Maximum size of a SMB2_CREATE response is 64 (smb2 header) +
+@@ -413,4 +414,10 @@ struct smb2_posix_info_parsed {
+ const u8 *name;
+ };
+
++struct smb2_create_ea_ctx {
++ struct create_context ctx;
++ __u8 name[8];
++ struct smb2_file_full_ea_info ea;
++} __packed;
++
+ #endif /* _SMB2PDU_H */
+diff --git a/fs/smb/client/smb2proto.h b/fs/smb/client/smb2proto.h
+index 64a0ef0409a6e..732169d8a67a3 100644
+--- a/fs/smb/client/smb2proto.h
++++ b/fs/smb/client/smb2proto.h
+@@ -61,7 +61,8 @@ struct inode *smb2_get_reparse_inode(struct cifs_open_info_data *data,
+ const unsigned int xid,
+ struct cifs_tcon *tcon,
+ const char *full_path,
+- struct kvec *iov);
++ struct kvec *reparse_iov,
++ struct kvec *xattr_iov);
+ int smb2_query_reparse_point(const unsigned int xid,
+ struct cifs_tcon *tcon,
+ struct cifs_sb_info *cifs_sb,
+diff --git a/fs/smb/common/smbfsctl.h b/fs/smb/common/smbfsctl.h
+index edd7fc2a7921b..a94d658b88e86 100644
+--- a/fs/smb/common/smbfsctl.h
++++ b/fs/smb/common/smbfsctl.h
+@@ -158,12 +158,6 @@
+ #define IO_REPARSE_TAG_LX_CHR 0x80000025
+ #define IO_REPARSE_TAG_LX_BLK 0x80000026
+
+-#define IO_REPARSE_TAG_LX_SYMLINK_LE cpu_to_le32(0xA000001D)
+-#define IO_REPARSE_TAG_AF_UNIX_LE cpu_to_le32(0x80000023)
+-#define IO_REPARSE_TAG_LX_FIFO_LE cpu_to_le32(0x80000024)
+-#define IO_REPARSE_TAG_LX_CHR_LE cpu_to_le32(0x80000025)
+-#define IO_REPARSE_TAG_LX_BLK_LE cpu_to_le32(0x80000026)
+-
+ /* fsctl flags */
+ /* If Flags is set to this value, the request is an FSCTL not ioctl request */
+ #define SMB2_0_IOCTL_IS_FSCTL 0x00000001
+--
+2.43.0
+
diff --git a/queue-6.6/smb-client-allow-creating-special-files-via-reparse-.patch b/queue-6.6/smb-client-allow-creating-special-files-via-reparse-.patch
new file mode 100644
index 0000000000..da5a26ae3d
--- /dev/null
+++ b/queue-6.6/smb-client-allow-creating-special-files-via-reparse-.patch
@@ -0,0 +1,616 @@
+From 4a13194d70244142c201613ce9de364e606f0647 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 28 Apr 2024 01:12:41 -0500
+Subject: smb: client: allow creating special files via reparse points
+
+From: Steve French <stfrench@microsoft.com>
+
+[ Upstream commit 102466f303ffcd5cff207b3c122557f73f1041e6 ]
+
+Add support for creating special files (e.g. char/block devices,
+sockets, fifos) via NFS reparse points on SMB2+, which are fully
+supported by most SMB servers and documented in MS-FSCC.
+
+smb2_get_reparse_inode() creates the file with a corresponding reparse
+point buffer set in @iov through a single roundtrip to the server.
+
+Reported-by: kernel test robot <lkp@intel.com>
+Closes: https://lore.kernel.org/oe-kbuild-all/202311260746.HOJ039BV-lkp@intel.com/
+Signed-off-by: Paulo Alcantara (SUSE) <pc@manguebit.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/cifsproto.h | 8 +++-
+ fs/smb/client/dir.c | 7 +--
+ fs/smb/client/file.c | 10 +++--
+ fs/smb/client/inode.c | 76 ++++++++++++++++++++-----------
+ fs/smb/client/link.c | 10 +++--
+ fs/smb/client/smb2glob.h | 25 ++++++-----
+ fs/smb/client/smb2inode.c | 75 +++++++++++++++++++++++++++++++
+ fs/smb/client/smb2ops.c | 94 +++++++++++++++++++++++++++++++++++----
+ fs/smb/client/smb2proto.h | 7 +++
+ fs/smb/client/trace.h | 4 +-
+ 10 files changed, 256 insertions(+), 60 deletions(-)
+
+diff --git a/fs/smb/client/cifsproto.h b/fs/smb/client/cifsproto.h
+index 9480cdb9588d5..49de2545f34ce 100644
+--- a/fs/smb/client/cifsproto.h
++++ b/fs/smb/client/cifsproto.h
+@@ -213,8 +213,12 @@ int cifs_get_inode_info(struct inode **inode, const char *full_path,
+ bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb,
+ struct cifs_fattr *fattr,
+ struct cifs_open_info_data *data);
+-extern int smb311_posix_get_inode_info(struct inode **pinode, const char *search_path,
+- struct super_block *sb, unsigned int xid);
++
++extern int smb311_posix_get_inode_info(struct inode **inode,
++ const char *full_path,
++ struct cifs_open_info_data *data,
++ struct super_block *sb,
++ const unsigned int xid);
+ extern int cifs_get_inode_info_unix(struct inode **pinode,
+ const unsigned char *search_path,
+ struct super_block *sb, unsigned int xid);
+diff --git a/fs/smb/client/dir.c b/fs/smb/client/dir.c
+index 855468a32904e..37897b919dd5a 100644
+--- a/fs/smb/client/dir.c
++++ b/fs/smb/client/dir.c
+@@ -695,9 +695,10 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
+ full_path, d_inode(direntry));
+
+ again:
+- if (pTcon->posix_extensions)
+- rc = smb311_posix_get_inode_info(&newInode, full_path, parent_dir_inode->i_sb, xid);
+- else if (pTcon->unix_ext) {
++ if (pTcon->posix_extensions) {
++ rc = smb311_posix_get_inode_info(&newInode, full_path, NULL,
++ parent_dir_inode->i_sb, xid);
++ } else if (pTcon->unix_ext) {
+ rc = cifs_get_inode_info_unix(&newInode, full_path,
+ parent_dir_inode->i_sb, xid);
+ } else {
+diff --git a/fs/smb/client/file.c b/fs/smb/client/file.c
+index 53a8c633221b9..f41804245fca1 100644
+--- a/fs/smb/client/file.c
++++ b/fs/smb/client/file.c
+@@ -1102,14 +1102,16 @@ cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush)
+ if (!is_interrupt_error(rc))
+ mapping_set_error(inode->i_mapping, rc);
+
+- if (tcon->posix_extensions)
+- rc = smb311_posix_get_inode_info(&inode, full_path, inode->i_sb, xid);
+- else if (tcon->unix_ext)
++ if (tcon->posix_extensions) {
++ rc = smb311_posix_get_inode_info(&inode, full_path,
++ NULL, inode->i_sb, xid);
++ } else if (tcon->unix_ext) {
+ rc = cifs_get_inode_info_unix(&inode, full_path,
+ inode->i_sb, xid);
+- else
++ } else {
+ rc = cifs_get_inode_info(&inode, full_path, NULL,
+ inode->i_sb, xid, NULL);
++ }
+ }
+ /*
+ * Else we are writing out data to server already and could deadlock if
+diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c
+index fa6330d586e89..391839feb29d5 100644
+--- a/fs/smb/client/inode.c
++++ b/fs/smb/client/inode.c
+@@ -1063,7 +1063,9 @@ static int reparse_info_to_fattr(struct cifs_open_info_data *data,
+ const unsigned int xid,
+ struct cifs_tcon *tcon,
+ const char *full_path,
+- struct cifs_fattr *fattr)
++ struct cifs_fattr *fattr,
++ struct cifs_sid *owner,
++ struct cifs_sid *group)
+ {
+ struct TCP_Server_Info *server = tcon->ses->server;
+ struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
+@@ -1094,7 +1096,8 @@ static int reparse_info_to_fattr(struct cifs_open_info_data *data,
+ rc = 0;
+ goto out;
+ default:
+- if (data->symlink_target) {
++ /* Check for cached reparse point data */
++ if (data->symlink_target || data->reparse.buf) {
+ rc = 0;
+ } else if (server->ops->parse_reparse_point) {
+ rc = server->ops->parse_reparse_point(cifs_sb,
+@@ -1103,7 +1106,10 @@ static int reparse_info_to_fattr(struct cifs_open_info_data *data,
+ break;
+ }
+
+- cifs_open_info_to_fattr(fattr, data, sb);
++ if (tcon->posix_extensions)
++ smb311_posix_info_to_fattr(fattr, data, owner, group, sb);
++ else
++ cifs_open_info_to_fattr(fattr, data, sb);
+ out:
+ fattr->cf_cifstag = data->reparse.tag;
+ free_rsp_buf(rsp_buftype, rsp_iov.iov_base);
+@@ -1155,7 +1161,8 @@ static int cifs_get_fattr(struct cifs_open_info_data *data,
+ */
+ if (cifs_open_data_reparse(data)) {
+ rc = reparse_info_to_fattr(data, sb, xid, tcon,
+- full_path, fattr);
++ full_path, fattr,
++ NULL, NULL);
+ } else {
+ cifs_open_info_to_fattr(fattr, data, sb);
+ }
+@@ -1293,18 +1300,19 @@ int cifs_get_inode_info(struct inode **inode,
+ return rc;
+ }
+
+-static int smb311_posix_get_fattr(struct cifs_fattr *fattr,
++static int smb311_posix_get_fattr(struct cifs_open_info_data *data,
++ struct cifs_fattr *fattr,
+ const char *full_path,
+ struct super_block *sb,
+ const unsigned int xid)
+ {
+- struct cifs_open_info_data data = {};
++ struct cifs_open_info_data tmp_data = {};
+ struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
+ struct cifs_tcon *tcon;
+ struct tcon_link *tlink;
+ struct cifs_sid owner, group;
+ int tmprc;
+- int rc;
++ int rc = 0;
+
+ tlink = cifs_sb_tlink(cifs_sb);
+ if (IS_ERR(tlink))
+@@ -1312,12 +1320,14 @@ static int smb311_posix_get_fattr(struct cifs_fattr *fattr,
+ tcon = tlink_tcon(tlink);
+
+ /*
+- * 1. Fetch file metadata
++ * 1. Fetch file metadata if not provided (data)
+ */
+-
+- rc = smb311_posix_query_path_info(xid, tcon, cifs_sb,
+- full_path, &data,
+- &owner, &group);
++ if (!data) {
++ rc = smb311_posix_query_path_info(xid, tcon, cifs_sb,
++ full_path, &tmp_data,
++ &owner, &group);
++ data = &tmp_data;
++ }
+
+ /*
+ * 2. Convert it to internal cifs metadata (fattr)
+@@ -1325,7 +1335,14 @@ static int smb311_posix_get_fattr(struct cifs_fattr *fattr,
+
+ switch (rc) {
+ case 0:
+- smb311_posix_info_to_fattr(fattr, &data, &owner, &group, sb);
++ if (cifs_open_data_reparse(data)) {
++ rc = reparse_info_to_fattr(data, sb, xid, tcon,
++ full_path, fattr,
++ &owner, &group);
++ } else {
++ smb311_posix_info_to_fattr(fattr, data,
++ &owner, &group, sb);
++ }
+ break;
+ case -EREMOTE:
+ /* DFS link, no metadata available on this server */
+@@ -1356,12 +1373,15 @@ static int smb311_posix_get_fattr(struct cifs_fattr *fattr,
+
+ out:
+ cifs_put_tlink(tlink);
+- cifs_free_open_info(&data);
++ cifs_free_open_info(data);
+ return rc;
+ }
+
+-int smb311_posix_get_inode_info(struct inode **inode, const char *full_path,
+- struct super_block *sb, const unsigned int xid)
++int smb311_posix_get_inode_info(struct inode **inode,
++ const char *full_path,
++ struct cifs_open_info_data *data,
++ struct super_block *sb,
++ const unsigned int xid)
+ {
+ struct cifs_fattr fattr = {};
+ int rc;
+@@ -1371,7 +1391,7 @@ int smb311_posix_get_inode_info(struct inode **inode, const char *full_path,
+ return 0;
+ }
+
+- rc = smb311_posix_get_fattr(&fattr, full_path, sb, xid);
++ rc = smb311_posix_get_fattr(data, &fattr, full_path, sb, xid);
+ if (rc)
+ goto out;
+
+@@ -1521,7 +1541,7 @@ struct inode *cifs_root_iget(struct super_block *sb)
+
+ convert_delimiter(path, CIFS_DIR_SEP(cifs_sb));
+ if (tcon->posix_extensions)
+- rc = smb311_posix_get_fattr(&fattr, path, sb, xid);
++ rc = smb311_posix_get_fattr(NULL, &fattr, path, sb, xid);
+ else
+ rc = cifs_get_fattr(NULL, sb, xid, NULL, &fattr, &inode, path);
+
+@@ -1894,16 +1914,18 @@ cifs_mkdir_qinfo(struct inode *parent, struct dentry *dentry, umode_t mode,
+ int rc = 0;
+ struct inode *inode = NULL;
+
+- if (tcon->posix_extensions)
+- rc = smb311_posix_get_inode_info(&inode, full_path, parent->i_sb, xid);
++ if (tcon->posix_extensions) {
++ rc = smb311_posix_get_inode_info(&inode, full_path,
++ NULL, parent->i_sb, xid);
+ #ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
+- else if (tcon->unix_ext)
++ } else if (tcon->unix_ext) {
+ rc = cifs_get_inode_info_unix(&inode, full_path, parent->i_sb,
+ xid);
+ #endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
+- else
++ } else {
+ rc = cifs_get_inode_info(&inode, full_path, NULL, parent->i_sb,
+ xid, NULL);
++ }
+
+ if (rc)
+ return rc;
+@@ -2585,13 +2607,15 @@ int cifs_revalidate_dentry_attr(struct dentry *dentry)
+ dentry, cifs_get_time(dentry), jiffies);
+
+ again:
+- if (cifs_sb_master_tcon(CIFS_SB(sb))->posix_extensions)
+- rc = smb311_posix_get_inode_info(&inode, full_path, sb, xid);
+- else if (cifs_sb_master_tcon(CIFS_SB(sb))->unix_ext)
++ if (cifs_sb_master_tcon(CIFS_SB(sb))->posix_extensions) {
++ rc = smb311_posix_get_inode_info(&inode, full_path,
++ NULL, sb, xid);
++ } else if (cifs_sb_master_tcon(CIFS_SB(sb))->unix_ext) {
+ rc = cifs_get_inode_info_unix(&inode, full_path, sb, xid);
+- else
++ } else {
+ rc = cifs_get_inode_info(&inode, full_path, NULL, sb,
+ xid, NULL);
++ }
+ if (rc == -EAGAIN && count++ < 10)
+ goto again;
+ out:
+diff --git a/fs/smb/client/link.c b/fs/smb/client/link.c
+index 3ef34218a790d..691f43a1ec2bc 100644
+--- a/fs/smb/client/link.c
++++ b/fs/smb/client/link.c
+@@ -614,14 +614,16 @@ cifs_symlink(struct mnt_idmap *idmap, struct inode *inode,
+ cifs_sb_target->local_nls); */
+
+ if (rc == 0) {
+- if (pTcon->posix_extensions)
+- rc = smb311_posix_get_inode_info(&newinode, full_path, inode->i_sb, xid);
+- else if (pTcon->unix_ext)
++ if (pTcon->posix_extensions) {
++ rc = smb311_posix_get_inode_info(&newinode, full_path,
++ NULL, inode->i_sb, xid);
++ } else if (pTcon->unix_ext) {
+ rc = cifs_get_inode_info_unix(&newinode, full_path,
+ inode->i_sb, xid);
+- else
++ } else {
+ rc = cifs_get_inode_info(&newinode, full_path, NULL,
+ inode->i_sb, xid, NULL);
++ }
+
+ if (rc != 0) {
+ cifs_dbg(FYI, "Create symlink ok, getinodeinfo fail rc = %d\n",
+diff --git a/fs/smb/client/smb2glob.h b/fs/smb/client/smb2glob.h
+index 82e916ad167c0..ca87a0011c337 100644
+--- a/fs/smb/client/smb2glob.h
++++ b/fs/smb/client/smb2glob.h
+@@ -23,17 +23,20 @@
+ * Identifiers for functions that use the open, operation, close pattern
+ * in smb2inode.c:smb2_compound_op()
+ */
+-#define SMB2_OP_SET_DELETE 1
+-#define SMB2_OP_SET_INFO 2
+-#define SMB2_OP_QUERY_INFO 3
+-#define SMB2_OP_QUERY_DIR 4
+-#define SMB2_OP_MKDIR 5
+-#define SMB2_OP_RENAME 6
+-#define SMB2_OP_DELETE 7
+-#define SMB2_OP_HARDLINK 8
+-#define SMB2_OP_SET_EOF 9
+-#define SMB2_OP_RMDIR 10
+-#define SMB2_OP_POSIX_QUERY_INFO 11
++enum smb2_compound_ops {
++ SMB2_OP_SET_DELETE = 1,
++ SMB2_OP_SET_INFO,
++ SMB2_OP_QUERY_INFO,
++ SMB2_OP_QUERY_DIR,
++ SMB2_OP_MKDIR,
++ SMB2_OP_RENAME,
++ SMB2_OP_DELETE,
++ SMB2_OP_HARDLINK,
++ SMB2_OP_SET_EOF,
++ SMB2_OP_RMDIR,
++ SMB2_OP_POSIX_QUERY_INFO,
++ SMB2_OP_SET_REPARSE
++};
+
+ /* Used when constructing chained read requests. */
+ #define CHAINED_REQUEST 1
+diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c
+index 4c66187eccdd2..e74d3a1e49dfa 100644
+--- a/fs/smb/client/smb2inode.c
++++ b/fs/smb/client/smb2inode.c
+@@ -359,6 +359,22 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
+ smb2_set_related(&rqst[num_rqst++]);
+ trace_smb3_hardlink_enter(xid, ses->Suid, tcon->tid, full_path);
+ break;
++ case SMB2_OP_SET_REPARSE:
++ rqst[num_rqst].rq_iov = vars->io_iov;
++ rqst[num_rqst].rq_nvec = ARRAY_SIZE(vars->io_iov);
++
++ rc = SMB2_ioctl_init(tcon, server, &rqst[num_rqst],
++ COMPOUND_FID, COMPOUND_FID,
++ FSCTL_SET_REPARSE_POINT,
++ in_iov[i].iov_base,
++ in_iov[i].iov_len, 0);
++ if (rc)
++ goto finished;
++ smb2_set_next_command(tcon, &rqst[num_rqst]);
++ smb2_set_related(&rqst[num_rqst++]);
++ trace_smb3_set_reparse_compound_enter(xid, ses->Suid,
++ tcon->tid, full_path);
++ break;
+ default:
+ cifs_dbg(VFS, "Invalid command\n");
+ rc = -EINVAL;
+@@ -515,6 +531,16 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
+ tcon->tid);
+ SMB2_set_info_free(&rqst[num_rqst++]);
+ break;
++ case SMB2_OP_SET_REPARSE:
++ if (rc) {
++ trace_smb3_set_reparse_compound_err(xid, ses->Suid,
++ tcon->tid, rc);
++ } else {
++ trace_smb3_set_reparse_compound_done(xid, ses->Suid,
++ tcon->tid);
++ }
++ SMB2_ioctl_free(&rqst[num_rqst++]);
++ break;
+ }
+ }
+ SMB2_close_free(&rqst[num_rqst]);
+@@ -905,3 +931,52 @@ smb2_set_file_info(struct inode *inode, const char *full_path,
+ cifs_put_tlink(tlink);
+ return rc;
+ }
++
++struct inode *smb2_get_reparse_inode(struct cifs_open_info_data *data,
++ struct super_block *sb,
++ const unsigned int xid,
++ struct cifs_tcon *tcon,
++ const char *full_path,
++ struct kvec *iov)
++{
++ struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
++ struct cifsFileInfo *cfile;
++ struct inode *new = NULL;
++ struct kvec in_iov[2];
++ int cmds[2];
++ int da, co, cd;
++ int rc;
++
++ da = SYNCHRONIZE | DELETE |
++ FILE_READ_ATTRIBUTES |
++ FILE_WRITE_ATTRIBUTES;
++ co = CREATE_NOT_DIR | OPEN_REPARSE_POINT;
++ cd = FILE_CREATE;
++ cmds[0] = SMB2_OP_SET_REPARSE;
++ in_iov[0] = *iov;
++ in_iov[1].iov_base = data;
++ in_iov[1].iov_len = sizeof(*data);
++
++ if (tcon->posix_extensions) {
++ cmds[1] = SMB2_OP_POSIX_QUERY_INFO;
++ cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile);
++ rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
++ da, cd, co, ACL_NO_MODE, in_iov,
++ cmds, 2, cfile, NULL, NULL, NULL, NULL);
++ if (!rc) {
++ rc = smb311_posix_get_inode_info(&new, full_path,
++ data, sb, xid);
++ }
++ } else {
++ cmds[1] = SMB2_OP_QUERY_INFO;
++ cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile);
++ rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
++ da, cd, co, ACL_NO_MODE, in_iov,
++ cmds, 2, cfile, NULL, NULL, NULL, NULL);
++ if (!rc) {
++ rc = cifs_get_inode_info(&new, full_path,
++ data, sb, xid, NULL);
++ }
++ }
++ return rc ? ERR_PTR(rc) : new;
++}
+diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
+index 2b892a736e5f9..4fed19d5a81d4 100644
+--- a/fs/smb/client/smb2ops.c
++++ b/fs/smb/client/smb2ops.c
+@@ -5170,11 +5170,88 @@ int cifs_sfu_make_node(unsigned int xid, struct inode *inode,
+ return rc;
+ }
+
++static inline u64 mode_nfs_type(mode_t mode)
++{
++ switch (mode & S_IFMT) {
++ case S_IFBLK: return NFS_SPECFILE_BLK;
++ case S_IFCHR: return NFS_SPECFILE_CHR;
++ case S_IFIFO: return NFS_SPECFILE_FIFO;
++ case S_IFSOCK: return NFS_SPECFILE_SOCK;
++ }
++ return 0;
++}
++
++static int nfs_set_reparse_buf(struct reparse_posix_data *buf,
++ mode_t mode, dev_t dev,
++ struct kvec *iov)
++{
++ u64 type;
++ u16 len, dlen;
++
++ len = sizeof(*buf);
++
++ switch ((type = mode_nfs_type(mode))) {
++ case NFS_SPECFILE_BLK:
++ case NFS_SPECFILE_CHR:
++ dlen = sizeof(__le64);
++ break;
++ case NFS_SPECFILE_FIFO:
++ case NFS_SPECFILE_SOCK:
++ dlen = 0;
++ break;
++ default:
++ return -EOPNOTSUPP;
++ }
++
++ buf->ReparseTag = cpu_to_le32(IO_REPARSE_TAG_NFS);
++ buf->Reserved = 0;
++ buf->InodeType = cpu_to_le64(type);
++ buf->ReparseDataLength = cpu_to_le16(len + dlen -
++ sizeof(struct reparse_data_buffer));
++ *(__le64 *)buf->DataBuffer = cpu_to_le64(((u64)MAJOR(dev) << 32) |
++ MINOR(dev));
++ iov->iov_base = buf;
++ iov->iov_len = len + dlen;
++ return 0;
++}
++
++static int nfs_make_node(unsigned int xid, struct inode *inode,
++ struct dentry *dentry, struct cifs_tcon *tcon,
++ const char *full_path, umode_t mode, dev_t dev)
++{
++ struct cifs_open_info_data data;
++ struct reparse_posix_data *p;
++ struct inode *new;
++ struct kvec iov;
++ __u8 buf[sizeof(*p) + sizeof(__le64)];
++ int rc;
++
++ p = (struct reparse_posix_data *)buf;
++ rc = nfs_set_reparse_buf(p, mode, dev, &iov);
++ if (rc)
++ return rc;
++
++ data = (struct cifs_open_info_data) {
++ .reparse_point = true,
++ .reparse = { .tag = IO_REPARSE_TAG_NFS, .posix = p, },
++ };
++
++ new = smb2_get_reparse_inode(&data, inode->i_sb, xid,
++ tcon, full_path, &iov);
++ if (!IS_ERR(new))
++ d_instantiate(dentry, new);
++ else
++ rc = PTR_ERR(new);
++ cifs_free_open_info(&data);
++ return rc;
++}
++
+ static int smb2_make_node(unsigned int xid, struct inode *inode,
+ struct dentry *dentry, struct cifs_tcon *tcon,
+ const char *full_path, umode_t mode, dev_t dev)
+ {
+ struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
++ int rc;
+
+ /*
+ * Check if mounted with mount parm 'sfu' mount parm.
+@@ -5182,15 +5259,14 @@ static int smb2_make_node(unsigned int xid, struct inode *inode,
+ * supports block and char device (no socket & fifo),
+ * and was used by default in earlier versions of Windows
+ */
+- if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL))
+- return -EPERM;
+- /*
+- * TODO: Add ability to create instead via reparse point. Windows (e.g.
+- * their current NFS server) uses this approach to expose special files
+- * over SMB2/SMB3 and Samba will do this with SMB3.1.1 POSIX Extensions
+- */
+- return cifs_sfu_make_node(xid, inode, dentry, tcon,
+- full_path, mode, dev);
++ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) {
++ rc = cifs_sfu_make_node(xid, inode, dentry, tcon,
++ full_path, mode, dev);
++ } else {
++ rc = nfs_make_node(xid, inode, dentry, tcon,
++ full_path, mode, dev);
++ }
++ return rc;
+ }
+
+ #ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
+diff --git a/fs/smb/client/smb2proto.h b/fs/smb/client/smb2proto.h
+index a8084ce7fcbd2..efa2f8fe23449 100644
+--- a/fs/smb/client/smb2proto.h
++++ b/fs/smb/client/smb2proto.h
+@@ -56,6 +56,12 @@ extern int smb3_handle_read_data(struct TCP_Server_Info *server,
+ extern int smb2_query_reparse_tag(const unsigned int xid, struct cifs_tcon *tcon,
+ struct cifs_sb_info *cifs_sb, const char *path,
+ __u32 *reparse_tag);
++struct inode *smb2_get_reparse_inode(struct cifs_open_info_data *data,
++ struct super_block *sb,
++ const unsigned int xid,
++ struct cifs_tcon *tcon,
++ const char *full_path,
++ struct kvec *iov);
+ int smb2_query_path_info(const unsigned int xid,
+ struct cifs_tcon *tcon,
+ struct cifs_sb_info *cifs_sb,
+@@ -293,4 +299,5 @@ int smb311_posix_query_path_info(const unsigned int xid,
+ int posix_info_parse(const void *beg, const void *end,
+ struct smb2_posix_info_parsed *out);
+ int posix_info_sid_size(const void *beg, const void *end);
++
+ #endif /* _SMB2PROTO_H */
+diff --git a/fs/smb/client/trace.h b/fs/smb/client/trace.h
+index de199ec9f7263..34f507584274b 100644
+--- a/fs/smb/client/trace.h
++++ b/fs/smb/client/trace.h
+@@ -370,11 +370,11 @@ DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(rename_enter);
+ DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(rmdir_enter);
+ DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(set_eof_enter);
+ DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(set_info_compound_enter);
++DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(set_reparse_compound_enter);
+ DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(delete_enter);
+ DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(mkdir_enter);
+ DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(tdis_enter);
+
+-
+ DECLARE_EVENT_CLASS(smb3_inf_compound_done_class,
+ TP_PROTO(unsigned int xid,
+ __u32 tid,
+@@ -408,6 +408,7 @@ DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(rename_done);
+ DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(rmdir_done);
+ DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(set_eof_done);
+ DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(set_info_compound_done);
++DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(set_reparse_compound_done);
+ DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(delete_done);
+ DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(mkdir_done);
+ DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(tdis_done);
+@@ -451,6 +452,7 @@ DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(rename_err);
+ DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(rmdir_err);
+ DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(set_eof_err);
+ DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(set_info_compound_err);
++DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(set_reparse_compound_err);
+ DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(mkdir_err);
+ DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(delete_err);
+ DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(tdis_err);
+--
+2.43.0
+
diff --git a/queue-6.6/smb-client-allow-creating-symlinks-via-reparse-point.patch b/queue-6.6/smb-client-allow-creating-symlinks-via-reparse-point.patch
new file mode 100644
index 0000000000..73479a6450
--- /dev/null
+++ b/queue-6.6/smb-client-allow-creating-symlinks-via-reparse-point.patch
@@ -0,0 +1,203 @@
+From 11afd247963177dd21dd2c5e66b981df1d6c9681 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 25 Nov 2023 23:55:04 -0300
+Subject: smb: client: allow creating symlinks via reparse points
+
+From: Paulo Alcantara <pc@manguebit.com>
+
+[ Upstream commit 514d793e27a310eb26b112c1f8f1a160472907e5 ]
+
+Add support for creating symlinks via IO_REPARSE_TAG_SYMLINK reparse
+points in SMB2+.
+
+These are fully supported by most SMB servers and documented in
+MS-FSCC. Also have the advantage of requiring fewer roundtrips as
+their symlink targets can be parsed directly from CREATE responses on
+STATUS_STOPPED_ON_SYMLINK errors.
+
+Reported-by: kernel test robot <lkp@intel.com>
+Closes: https://lore.kernel.org/oe-kbuild-all/202311260838.nx5mkj1j-lkp@intel.com/
+Signed-off-by: Paulo Alcantara (SUSE) <pc@manguebit.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/cifsglob.h | 6 ++++
+ fs/smb/client/link.c | 15 ++++++---
+ fs/smb/client/smb2ops.c | 70 ++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 86 insertions(+), 5 deletions(-)
+
+diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h
+index 3e7c3c3d73a73..414648bf816b2 100644
+--- a/fs/smb/client/cifsglob.h
++++ b/fs/smb/client/cifsglob.h
+@@ -577,6 +577,12 @@ struct smb_version_operations {
+ int (*parse_reparse_point)(struct cifs_sb_info *cifs_sb,
+ struct kvec *rsp_iov,
+ struct cifs_open_info_data *data);
++ int (*create_reparse_symlink)(const unsigned int xid,
++ struct inode *inode,
++ struct dentry *dentry,
++ struct cifs_tcon *tcon,
++ const char *full_path,
++ const char *symname);
+ };
+
+ struct smb_version_values {
+diff --git a/fs/smb/client/link.c b/fs/smb/client/link.c
+index 691f43a1ec2bc..d86da949a9190 100644
+--- a/fs/smb/client/link.c
++++ b/fs/smb/client/link.c
+@@ -569,6 +569,7 @@ cifs_symlink(struct mnt_idmap *idmap, struct inode *inode,
+ int rc = -EOPNOTSUPP;
+ unsigned int xid;
+ struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
++ struct TCP_Server_Info *server;
+ struct tcon_link *tlink;
+ struct cifs_tcon *pTcon;
+ const char *full_path;
+@@ -590,6 +591,7 @@ cifs_symlink(struct mnt_idmap *idmap, struct inode *inode,
+ goto symlink_exit;
+ }
+ pTcon = tlink_tcon(tlink);
++ server = cifs_pick_channel(pTcon->ses);
+
+ full_path = build_path_from_dentry(direntry, page);
+ if (IS_ERR(full_path)) {
+@@ -601,17 +603,20 @@ cifs_symlink(struct mnt_idmap *idmap, struct inode *inode,
+ cifs_dbg(FYI, "symname is %s\n", symname);
+
+ /* BB what if DFS and this volume is on different share? BB */
+- if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS)
++ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) {
+ rc = create_mf_symlink(xid, pTcon, cifs_sb, full_path, symname);
+ #ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
+- else if (pTcon->unix_ext)
++ } else if (pTcon->unix_ext) {
+ rc = CIFSUnixCreateSymLink(xid, pTcon, full_path, symname,
+ cifs_sb->local_nls,
+ cifs_remap(cifs_sb));
+ #endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
+- /* else
+- rc = CIFSCreateReparseSymLink(xid, pTcon, fromName, toName,
+- cifs_sb_target->local_nls); */
++ } else if (server->ops->create_reparse_symlink) {
++ rc = server->ops->create_reparse_symlink(xid, inode, direntry,
++ pTcon, full_path,
++ symname);
++ goto symlink_exit;
++ }
+
+ if (rc == 0) {
+ if (pTcon->posix_extensions) {
+diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
+index 4fed19d5a81d4..c5957deb1a859 100644
+--- a/fs/smb/client/smb2ops.c
++++ b/fs/smb/client/smb2ops.c
+@@ -5246,6 +5246,72 @@ static int nfs_make_node(unsigned int xid, struct inode *inode,
+ return rc;
+ }
+
++static int smb2_create_reparse_symlink(const unsigned int xid,
++ struct inode *inode,
++ struct dentry *dentry,
++ struct cifs_tcon *tcon,
++ const char *full_path,
++ const char *symname)
++{
++ struct reparse_symlink_data_buffer *buf = NULL;
++ struct cifs_open_info_data data;
++ struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
++ struct inode *new;
++ struct kvec iov;
++ __le16 *path;
++ char *sym;
++ u16 len, plen;
++ int rc = 0;
++
++ sym = kstrdup(symname, GFP_KERNEL);
++ if (!sym)
++ return -ENOMEM;
++
++ data = (struct cifs_open_info_data) {
++ .reparse_point = true,
++ .reparse = { .tag = IO_REPARSE_TAG_SYMLINK, },
++ .symlink_target = sym,
++ };
++
++ path = cifs_convert_path_to_utf16(symname, cifs_sb);
++ if (!path) {
++ rc = -ENOMEM;
++ goto out;
++ }
++
++ plen = 2 * UniStrnlen((wchar_t *)path, PATH_MAX);
++ len = sizeof(*buf) + plen * 2;
++ buf = kzalloc(len, GFP_KERNEL);
++ if (!buf) {
++ rc = -ENOMEM;
++ goto out;
++ }
++
++ buf->ReparseTag = cpu_to_le32(IO_REPARSE_TAG_SYMLINK);
++ buf->ReparseDataLength = cpu_to_le16(len - sizeof(struct reparse_data_buffer));
++ buf->SubstituteNameOffset = cpu_to_le16(plen);
++ buf->SubstituteNameLength = cpu_to_le16(plen);
++ memcpy(&buf->PathBuffer[plen], path, plen);
++ buf->PrintNameOffset = 0;
++ buf->PrintNameLength = cpu_to_le16(plen);
++ memcpy(buf->PathBuffer, path, plen);
++ buf->Flags = cpu_to_le32(*symname != '/' ? SYMLINK_FLAG_RELATIVE : 0);
++
++ iov.iov_base = buf;
++ iov.iov_len = len;
++ new = smb2_get_reparse_inode(&data, inode->i_sb, xid,
++ tcon, full_path, &iov);
++ if (!IS_ERR(new))
++ d_instantiate(dentry, new);
++ else
++ rc = PTR_ERR(new);
++out:
++ kfree(path);
++ cifs_free_open_info(&data);
++ kfree(buf);
++ return rc;
++}
++
+ static int smb2_make_node(unsigned int xid, struct inode *inode,
+ struct dentry *dentry, struct cifs_tcon *tcon,
+ const char *full_path, umode_t mode, dev_t dev)
+@@ -5322,6 +5388,7 @@ struct smb_version_operations smb20_operations = {
+ .parse_reparse_point = smb2_parse_reparse_point,
+ .query_mf_symlink = smb3_query_mf_symlink,
+ .create_mf_symlink = smb3_create_mf_symlink,
++ .create_reparse_symlink = smb2_create_reparse_symlink,
+ .open = smb2_open_file,
+ .set_fid = smb2_set_fid,
+ .close = smb2_close_file,
+@@ -5424,6 +5491,7 @@ struct smb_version_operations smb21_operations = {
+ .parse_reparse_point = smb2_parse_reparse_point,
+ .query_mf_symlink = smb3_query_mf_symlink,
+ .create_mf_symlink = smb3_create_mf_symlink,
++ .create_reparse_symlink = smb2_create_reparse_symlink,
+ .open = smb2_open_file,
+ .set_fid = smb2_set_fid,
+ .close = smb2_close_file,
+@@ -5530,6 +5598,7 @@ struct smb_version_operations smb30_operations = {
+ .parse_reparse_point = smb2_parse_reparse_point,
+ .query_mf_symlink = smb3_query_mf_symlink,
+ .create_mf_symlink = smb3_create_mf_symlink,
++ .create_reparse_symlink = smb2_create_reparse_symlink,
+ .open = smb2_open_file,
+ .set_fid = smb2_set_fid,
+ .close = smb2_close_file,
+@@ -5645,6 +5714,7 @@ struct smb_version_operations smb311_operations = {
+ .parse_reparse_point = smb2_parse_reparse_point,
+ .query_mf_symlink = smb3_query_mf_symlink,
+ .create_mf_symlink = smb3_create_mf_symlink,
++ .create_reparse_symlink = smb2_create_reparse_symlink,
+ .open = smb2_open_file,
+ .set_fid = smb2_set_fid,
+ .close = smb2_close_file,
+--
+2.43.0
+
diff --git a/queue-6.6/smb-client-cleanup-smb2_query_reparse_point.patch b/queue-6.6/smb-client-cleanup-smb2_query_reparse_point.patch
new file mode 100644
index 0000000000..e837c4826c
--- /dev/null
+++ b/queue-6.6/smb-client-cleanup-smb2_query_reparse_point.patch
@@ -0,0 +1,231 @@
+From 36f67e6de2109b5c25324dee4823f6cb0d42ff42 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 25 Nov 2023 23:55:08 -0300
+Subject: smb: client: cleanup smb2_query_reparse_point()
+
+From: Paulo Alcantara <pc@manguebit.com>
+
+[ Upstream commit 3ded18a9e9d22a9cba8acad24b77a87851f9c9fa ]
+
+Use smb2_compound_op() with SMB2_OP_GET_REPARSE to get reparse point.
+
+Signed-off-by: Paulo Alcantara (SUSE) <pc@manguebit.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/smb2inode.c | 33 +++++++++
+ fs/smb/client/smb2ops.c | 139 --------------------------------------
+ fs/smb/client/smb2proto.h | 6 ++
+ 3 files changed, 39 insertions(+), 139 deletions(-)
+
+diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c
+index 11c1e06ab5417..1388ce5421a89 100644
+--- a/fs/smb/client/smb2inode.c
++++ b/fs/smb/client/smb2inode.c
+@@ -1054,3 +1054,36 @@ struct inode *smb2_get_reparse_inode(struct cifs_open_info_data *data,
+ }
+ return rc ? ERR_PTR(rc) : new;
+ }
++
++int smb2_query_reparse_point(const unsigned int xid,
++ struct cifs_tcon *tcon,
++ struct cifs_sb_info *cifs_sb,
++ const char *full_path,
++ u32 *tag, struct kvec *rsp,
++ int *rsp_buftype)
++{
++ struct cifs_open_info_data data = {};
++ struct cifsFileInfo *cfile;
++ struct kvec in_iov = { .iov_base = &data, .iov_len = sizeof(data), };
++ int rc;
++
++ cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path);
++
++ cifs_get_readable_path(tcon, full_path, &cfile);
++ rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
++ FILE_READ_ATTRIBUTES, FILE_OPEN,
++ OPEN_REPARSE_POINT, ACL_NO_MODE, &in_iov,
++ &(int){SMB2_OP_GET_REPARSE}, 1, cfile,
++ NULL, NULL, NULL, NULL);
++ if (rc)
++ goto out;
++
++ *tag = data.reparse.tag;
++ *rsp = data.reparse.io.iov;
++ *rsp_buftype = data.reparse.io.buftype;
++ memset(&data.reparse.io.iov, 0, sizeof(data.reparse.io.iov));
++ data.reparse.io.buftype = CIFS_NO_BUFFER;
++out:
++ cifs_free_open_info(&data);
++ return rc;
++}
+diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
+index c5957deb1a859..a623a720db9e0 100644
+--- a/fs/smb/client/smb2ops.c
++++ b/fs/smb/client/smb2ops.c
+@@ -2996,145 +2996,6 @@ static int smb2_parse_reparse_point(struct cifs_sb_info *cifs_sb,
+ return parse_reparse_point(buf, plen, cifs_sb, true, data);
+ }
+
+-static int smb2_query_reparse_point(const unsigned int xid,
+- struct cifs_tcon *tcon,
+- struct cifs_sb_info *cifs_sb,
+- const char *full_path,
+- u32 *tag, struct kvec *rsp,
+- int *rsp_buftype)
+-{
+- struct smb2_compound_vars *vars;
+- int rc;
+- __le16 *utf16_path = NULL;
+- __u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
+- struct cifs_open_parms oparms;
+- struct cifs_fid fid;
+- struct TCP_Server_Info *server = cifs_pick_channel(tcon->ses);
+- int flags = CIFS_CP_CREATE_CLOSE_OP;
+- struct smb_rqst *rqst;
+- int resp_buftype[3];
+- struct kvec *rsp_iov;
+- struct smb2_ioctl_rsp *ioctl_rsp;
+- struct reparse_data_buffer *reparse_buf;
+- u32 off, count, len;
+-
+- cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path);
+-
+- if (smb3_encryption_required(tcon))
+- flags |= CIFS_TRANSFORM_REQ;
+-
+- utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb);
+- if (!utf16_path)
+- return -ENOMEM;
+-
+- resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER;
+- vars = kzalloc(sizeof(*vars), GFP_KERNEL);
+- if (!vars) {
+- rc = -ENOMEM;
+- goto out_free_path;
+- }
+- rqst = vars->rqst;
+- rsp_iov = vars->rsp_iov;
+-
+- /*
+- * setup smb2open - TODO add optimization to call cifs_get_readable_path
+- * to see if there is a handle already open that we can use
+- */
+- rqst[0].rq_iov = vars->open_iov;
+- rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE;
+-
+- oparms = (struct cifs_open_parms) {
+- .tcon = tcon,
+- .path = full_path,
+- .desired_access = FILE_READ_ATTRIBUTES,
+- .disposition = FILE_OPEN,
+- .create_options = cifs_create_options(cifs_sb, OPEN_REPARSE_POINT),
+- .fid = &fid,
+- };
+-
+- rc = SMB2_open_init(tcon, server,
+- &rqst[0], &oplock, &oparms, utf16_path);
+- if (rc)
+- goto query_rp_exit;
+- smb2_set_next_command(tcon, &rqst[0]);
+-
+-
+- /* IOCTL */
+- rqst[1].rq_iov = vars->io_iov;
+- rqst[1].rq_nvec = SMB2_IOCTL_IOV_SIZE;
+-
+- rc = SMB2_ioctl_init(tcon, server,
+- &rqst[1], COMPOUND_FID,
+- COMPOUND_FID, FSCTL_GET_REPARSE_POINT, NULL, 0,
+- CIFSMaxBufSize -
+- MAX_SMB2_CREATE_RESPONSE_SIZE -
+- MAX_SMB2_CLOSE_RESPONSE_SIZE);
+- if (rc)
+- goto query_rp_exit;
+-
+- smb2_set_next_command(tcon, &rqst[1]);
+- smb2_set_related(&rqst[1]);
+-
+- /* Close */
+- rqst[2].rq_iov = &vars->close_iov;
+- rqst[2].rq_nvec = 1;
+-
+- rc = SMB2_close_init(tcon, server,
+- &rqst[2], COMPOUND_FID, COMPOUND_FID, false);
+- if (rc)
+- goto query_rp_exit;
+-
+- smb2_set_related(&rqst[2]);
+-
+- rc = compound_send_recv(xid, tcon->ses, server,
+- flags, 3, rqst,
+- resp_buftype, rsp_iov);
+-
+- ioctl_rsp = rsp_iov[1].iov_base;
+-
+- /*
+- * Open was successful and we got an ioctl response.
+- */
+- if (rc == 0) {
+- /* See MS-FSCC 2.3.23 */
+- off = le32_to_cpu(ioctl_rsp->OutputOffset);
+- count = le32_to_cpu(ioctl_rsp->OutputCount);
+- if (check_add_overflow(off, count, &len) ||
+- len > rsp_iov[1].iov_len) {
+- cifs_tcon_dbg(VFS, "%s: invalid ioctl: off=%d count=%d\n",
+- __func__, off, count);
+- rc = -EIO;
+- goto query_rp_exit;
+- }
+-
+- reparse_buf = (void *)((u8 *)ioctl_rsp + off);
+- len = sizeof(*reparse_buf);
+- if (count < len ||
+- count < le16_to_cpu(reparse_buf->ReparseDataLength) + len) {
+- cifs_tcon_dbg(VFS, "%s: invalid ioctl: off=%d count=%d\n",
+- __func__, off, count);
+- rc = -EIO;
+- goto query_rp_exit;
+- }
+- *tag = le32_to_cpu(reparse_buf->ReparseTag);
+- *rsp = rsp_iov[1];
+- *rsp_buftype = resp_buftype[1];
+- resp_buftype[1] = CIFS_NO_BUFFER;
+- }
+-
+- query_rp_exit:
+- SMB2_open_free(&rqst[0]);
+- SMB2_ioctl_free(&rqst[1]);
+- SMB2_close_free(&rqst[2]);
+- free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base);
+- free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base);
+- free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base);
+- kfree(vars);
+-out_free_path:
+- kfree(utf16_path);
+- return rc;
+-}
+-
+ static struct cifs_ntsd *
+ get_smb2_acl_by_fid(struct cifs_sb_info *cifs_sb,
+ const struct cifs_fid *cifsfid, u32 *pacllen, u32 info)
+diff --git a/fs/smb/client/smb2proto.h b/fs/smb/client/smb2proto.h
+index efa2f8fe23449..1e20f87a5f584 100644
+--- a/fs/smb/client/smb2proto.h
++++ b/fs/smb/client/smb2proto.h
+@@ -62,6 +62,12 @@ struct inode *smb2_get_reparse_inode(struct cifs_open_info_data *data,
+ struct cifs_tcon *tcon,
+ const char *full_path,
+ struct kvec *iov);
++int smb2_query_reparse_point(const unsigned int xid,
++ struct cifs_tcon *tcon,
++ struct cifs_sb_info *cifs_sb,
++ const char *full_path,
++ u32 *tag, struct kvec *rsp,
++ int *rsp_buftype);
+ int smb2_query_path_info(const unsigned int xid,
+ struct cifs_tcon *tcon,
+ struct cifs_sb_info *cifs_sb,
+--
+2.43.0
+
diff --git a/queue-6.6/smb-client-delete-true-false-defines.patch b/queue-6.6/smb-client-delete-true-false-defines.patch
new file mode 100644
index 0000000000..a84e843eab
--- /dev/null
+++ b/queue-6.6/smb-client-delete-true-false-defines.patch
@@ -0,0 +1,41 @@
+From f5472d85a69f41100db29c7ebd615a143ff750f1 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 23 Jan 2024 13:40:00 +0300
+Subject: smb: client: delete "true", "false" defines
+
+From: Alexey Dobriyan <adobriyan@gmail.com>
+
+[ Upstream commit 5d390df3bdd13d178eb2e02e60e9a480f7103f7b ]
+
+Kernel has its own official true/false definitions.
+
+The defines aren't even used in this file.
+
+Signed-off-by: Alexey Dobriyan <adobriyan@gmail.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/smbencrypt.c | 7 -------
+ 1 file changed, 7 deletions(-)
+
+diff --git a/fs/smb/client/smbencrypt.c b/fs/smb/client/smbencrypt.c
+index f0ce26414f173..1d1ee9f18f373 100644
+--- a/fs/smb/client/smbencrypt.c
++++ b/fs/smb/client/smbencrypt.c
+@@ -26,13 +26,6 @@
+ #include "cifsproto.h"
+ #include "../common/md4.h"
+
+-#ifndef false
+-#define false 0
+-#endif
+-#ifndef true
+-#define true 1
+-#endif
+-
+ /* following came from the other byteorder.h to avoid include conflicts */
+ #define CVAL(buf,pos) (((unsigned char *)(buf))[pos])
+ #define SSVALX(buf,pos,val) (CVAL(buf,pos)=(val)&0xFF,CVAL(buf,pos+1)=(val)>>8)
+--
+2.43.0
+
diff --git a/queue-6.6/smb-client-do-not-defer-close-open-handles-to-delete.patch b/queue-6.6/smb-client-do-not-defer-close-open-handles-to-delete.patch
new file mode 100644
index 0000000000..49ced98f79
--- /dev/null
+++ b/queue-6.6/smb-client-do-not-defer-close-open-handles-to-delete.patch
@@ -0,0 +1,254 @@
+From a5fed64785dd763b30e3b29c2a9f160b797bb7d6 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 1 May 2024 00:56:13 -0500
+Subject: smb: client: do not defer close open handles to deleted files
+
+From: Steve French <stfrench@microsoft.com>
+
+[ Upstream commit ffceb7640cbfe6ea60e7769e107451d63a2fe3d3 ]
+
+When a file/dentry has been deleted before closing all its open
+handles, currently, closing them can add them to the deferred
+close list. This can lead to problems in creating file with the
+same name when the file is re-created before the deferred close
+completes. This issue was seen while reusing a client's already
+existing lease on a file for compound operations and xfstest 591
+failed because of the deferred close handle that remained valid
+even after the file was deleted and was being reused to create a
+file with the same name. The server in this case returns an error
+on open with STATUS_DELETE_PENDING. Recreating the file would
+fail till the deferred handles are closed (duration specified in
+closetimeo).
+
+This patch fixes the issue by flagging all open handles for the
+deleted file (file path to be precise) by setting
+status_file_deleted to true in the cifsFileInfo structure. As per
+the information classes specified in MS-FSCC, SMB2 query info
+response from the server has a DeletePending field, set to true
+to indicate that deletion has been requested on that file. If
+this is the case, flag the open handles for this file too.
+
+When doing close in cifs_close for each of these handles, check the
+value of this boolean field and do not defer close these handles
+if the corresponding filepath has been deleted.
+
+Signed-off-by: Meetakshi Setiya <msetiya@microsoft.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/cifsglob.h | 1 +
+ fs/smb/client/cifsproto.h | 4 ++++
+ fs/smb/client/file.c | 3 ++-
+ fs/smb/client/inode.c | 28 +++++++++++++++++++++++++---
+ fs/smb/client/misc.c | 34 ++++++++++++++++++++++++++++++++++
+ fs/smb/client/smb2inode.c | 9 ++++++++-
+ 6 files changed, 74 insertions(+), 5 deletions(-)
+
+diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h
+index a149579910add..fdadda4024f46 100644
+--- a/fs/smb/client/cifsglob.h
++++ b/fs/smb/client/cifsglob.h
+@@ -1427,6 +1427,7 @@ struct cifsFileInfo {
+ bool invalidHandle:1; /* file closed via session abend */
+ bool swapfile:1;
+ bool oplock_break_cancelled:1;
++ bool status_file_deleted:1; /* file has been deleted */
+ bool offload:1; /* offload final part of _put to a wq */
+ unsigned int oplock_epoch; /* epoch from the lease break */
+ __u32 oplock_level; /* oplock/lease level from the lease break */
+diff --git a/fs/smb/client/cifsproto.h b/fs/smb/client/cifsproto.h
+index e9b38b279a6c5..50040990e70b9 100644
+--- a/fs/smb/client/cifsproto.h
++++ b/fs/smb/client/cifsproto.h
+@@ -298,6 +298,10 @@ extern void cifs_close_all_deferred_files(struct cifs_tcon *cifs_tcon);
+
+ extern void cifs_close_deferred_file_under_dentry(struct cifs_tcon *cifs_tcon,
+ const char *path);
++
++extern void cifs_mark_open_handles_for_deleted_file(struct inode *inode,
++ const char *path);
++
+ extern struct TCP_Server_Info *
+ cifs_get_tcp_session(struct smb3_fs_context *ctx,
+ struct TCP_Server_Info *primary_server);
+diff --git a/fs/smb/client/file.c b/fs/smb/client/file.c
+index 751ae89cefe36..8eaf195ef5604 100644
+--- a/fs/smb/client/file.c
++++ b/fs/smb/client/file.c
+@@ -501,6 +501,7 @@ struct cifsFileInfo *cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
+ cfile->uid = current_fsuid();
+ cfile->dentry = dget(dentry);
+ cfile->f_flags = file->f_flags;
++ cfile->status_file_deleted = false;
+ cfile->invalidHandle = false;
+ cfile->deferred_close_scheduled = false;
+ cfile->tlink = cifs_get_tlink(tlink);
+@@ -1167,7 +1168,7 @@ int cifs_close(struct inode *inode, struct file *file)
+ if ((cifs_sb->ctx->closetimeo && cinode->oplock == CIFS_CACHE_RHW_FLG)
+ && cinode->lease_granted &&
+ !test_bit(CIFS_INO_CLOSE_ON_LOCK, &cinode->flags) &&
+- dclose) {
++ dclose && !(cfile->status_file_deleted)) {
+ if (test_and_clear_bit(CIFS_INO_MODIFIED_ATTR, &cinode->flags)) {
+ inode_set_mtime_to_ts(inode,
+ inode_set_ctime_current(inode));
+diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c
+index 156713186b3c9..2739cb8390804 100644
+--- a/fs/smb/client/inode.c
++++ b/fs/smb/client/inode.c
+@@ -894,6 +894,9 @@ cifs_get_file_info(struct file *filp)
+ struct cifsFileInfo *cfile = filp->private_data;
+ struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
+ struct TCP_Server_Info *server = tcon->ses->server;
++ struct dentry *dentry = filp->f_path.dentry;
++ void *page = alloc_dentry_path();
++ const unsigned char *path;
+
+ if (!server->ops->query_file_info)
+ return -ENOSYS;
+@@ -908,7 +911,14 @@ cifs_get_file_info(struct file *filp)
+ data.symlink = true;
+ data.reparse.tag = IO_REPARSE_TAG_SYMLINK;
+ }
++ path = build_path_from_dentry(dentry, page);
++ if (IS_ERR(path)) {
++ free_dentry_path(page);
++ return PTR_ERR(path);
++ }
+ cifs_open_info_to_fattr(&fattr, &data, inode->i_sb);
++ if (fattr.cf_flags & CIFS_FATTR_DELETE_PENDING)
++ cifs_mark_open_handles_for_deleted_file(inode, path);
+ break;
+ case -EREMOTE:
+ cifs_create_junction_fattr(&fattr, inode->i_sb);
+@@ -938,6 +948,7 @@ cifs_get_file_info(struct file *filp)
+ rc = cifs_fattr_to_inode(inode, &fattr, false);
+ cgfi_exit:
+ cifs_free_open_info(&data);
++ free_dentry_path(page);
+ free_xid(xid);
+ return rc;
+ }
+@@ -1076,6 +1087,7 @@ static int reparse_info_to_fattr(struct cifs_open_info_data *data,
+ struct kvec rsp_iov, *iov = NULL;
+ int rsp_buftype = CIFS_NO_BUFFER;
+ u32 tag = data->reparse.tag;
++ struct inode *inode = NULL;
+ int rc = 0;
+
+ if (!tag && server->ops->query_reparse_point) {
+@@ -1115,8 +1127,12 @@ static int reparse_info_to_fattr(struct cifs_open_info_data *data,
+
+ if (tcon->posix_extensions)
+ smb311_posix_info_to_fattr(fattr, data, sb);
+- else
++ else {
+ cifs_open_info_to_fattr(fattr, data, sb);
++ inode = cifs_iget(sb, fattr);
++ if (inode && fattr->cf_flags & CIFS_FATTR_DELETE_PENDING)
++ cifs_mark_open_handles_for_deleted_file(inode, full_path);
++ }
+ out:
+ fattr->cf_cifstag = data->reparse.tag;
+ free_rsp_buf(rsp_buftype, rsp_iov.iov_base);
+@@ -1171,6 +1187,8 @@ static int cifs_get_fattr(struct cifs_open_info_data *data,
+ full_path, fattr);
+ } else {
+ cifs_open_info_to_fattr(fattr, data, sb);
++ if (fattr->cf_flags & CIFS_FATTR_DELETE_PENDING)
++ cifs_mark_open_handles_for_deleted_file(*inode, full_path);
+ }
+ break;
+ case -EREMOTE:
+@@ -1853,16 +1871,20 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry)
+
+ psx_del_no_retry:
+ if (!rc) {
+- if (inode)
++ if (inode) {
++ cifs_mark_open_handles_for_deleted_file(inode, full_path);
+ cifs_drop_nlink(inode);
++ }
+ } else if (rc == -ENOENT) {
+ d_drop(dentry);
+ } else if (rc == -EBUSY) {
+ if (server->ops->rename_pending_delete) {
+ rc = server->ops->rename_pending_delete(full_path,
+ dentry, xid);
+- if (rc == 0)
++ if (rc == 0) {
++ cifs_mark_open_handles_for_deleted_file(inode, full_path);
+ cifs_drop_nlink(inode);
++ }
+ }
+ } else if ((rc == -EACCES) && (dosattr == 0) && inode) {
+ attrs = kzalloc(sizeof(*attrs), GFP_KERNEL);
+diff --git a/fs/smb/client/misc.c b/fs/smb/client/misc.c
+index d56959d02e36d..669d27b4d414a 100644
+--- a/fs/smb/client/misc.c
++++ b/fs/smb/client/misc.c
+@@ -853,6 +853,40 @@ cifs_close_deferred_file_under_dentry(struct cifs_tcon *tcon, const char *path)
+ free_dentry_path(page);
+ }
+
++/*
++ * If a dentry has been deleted, all corresponding open handles should know that
++ * so that we do not defer close them.
++ */
++void cifs_mark_open_handles_for_deleted_file(struct inode *inode,
++ const char *path)
++{
++ struct cifsFileInfo *cfile;
++ void *page;
++ const char *full_path;
++ struct cifsInodeInfo *cinode = CIFS_I(inode);
++
++ page = alloc_dentry_path();
++ spin_lock(&cinode->open_file_lock);
++
++ /*
++ * note: we need to construct path from dentry and compare only if the
++ * inode has any hardlinks. When number of hardlinks is 1, we can just
++ * mark all open handles since they are going to be from the same file.
++ */
++ if (inode->i_nlink > 1) {
++ list_for_each_entry(cfile, &cinode->openFileList, flist) {
++ full_path = build_path_from_dentry(cfile->dentry, page);
++ if (!IS_ERR(full_path) && strcmp(full_path, path) == 0)
++ cfile->status_file_deleted = true;
++ }
++ } else {
++ list_for_each_entry(cfile, &cinode->openFileList, flist)
++ cfile->status_file_deleted = true;
++ }
++ spin_unlock(&cinode->open_file_lock);
++ free_dentry_path(page);
++}
++
+ /* parses DFS referral V3 structure
+ * caller is responsible for freeing target_nodes
+ * returns:
+diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c
+index e452c59c13e2d..0b7b083352919 100644
+--- a/fs/smb/client/smb2inode.c
++++ b/fs/smb/client/smb2inode.c
+@@ -561,8 +561,15 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
+ case SMB2_OP_DELETE:
+ if (rc)
+ trace_smb3_delete_err(xid, ses->Suid, tcon->tid, rc);
+- else
++ else {
++ /*
++ * If dentry (hence, inode) is NULL, lease break is going to
++ * take care of degrading leases on handles for deleted files.
++ */
++ if (inode)
++ cifs_mark_open_handles_for_deleted_file(inode, full_path);
+ trace_smb3_delete_done(xid, ses->Suid, tcon->tid);
++ }
+ break;
+ case SMB2_OP_MKDIR:
+ if (rc)
+--
+2.43.0
+
diff --git a/queue-6.6/smb-client-don-t-clobber-i_rdev-from-cached-reparse-.patch b/queue-6.6/smb-client-don-t-clobber-i_rdev-from-cached-reparse-.patch
new file mode 100644
index 0000000000..cd02460551
--- /dev/null
+++ b/queue-6.6/smb-client-don-t-clobber-i_rdev-from-cached-reparse-.patch
@@ -0,0 +1,44 @@
+From a2226021710bf984972a8ea9b57beccb5f37598e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 19 Jan 2024 01:08:29 -0300
+Subject: smb: client: don't clobber ->i_rdev from cached reparse points
+
+From: Paulo Alcantara <pc@manguebit.com>
+
+[ Upstream commit 66c9314b61ed5b7bfcff0d89359aa0f975c0ab53 ]
+
+Don't clobber ->i_rdev from valid reparse inodes over readdir(2) as it
+can't be provided by query dir responses.
+
+Signed-off-by: Paulo Alcantara <pc@manguebit.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/readdir.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/fs/smb/client/readdir.c b/fs/smb/client/readdir.c
+index 47f5a82bc2507..73ff9bd059682 100644
+--- a/fs/smb/client/readdir.c
++++ b/fs/smb/client/readdir.c
+@@ -133,14 +133,14 @@ cifs_prime_dcache(struct dentry *parent, struct qstr *name,
+ * Query dir responses don't provide enough
+ * information about reparse points other than
+ * their reparse tags. Save an invalidation by
+- * not clobbering the existing mode, size and
+- * symlink target (if any) when reparse tag and
+- * ctime haven't changed.
++ * not clobbering some existing attributes when
++ * reparse tag and ctime haven't changed.
+ */
+ rc = 0;
+ if (fattr->cf_cifsattrs & ATTR_REPARSE) {
+ if (likely(reparse_inode_match(inode, fattr))) {
+ fattr->cf_mode = inode->i_mode;
++ fattr->cf_rdev = inode->i_rdev;
+ fattr->cf_eof = CIFS_I(inode)->server_eof;
+ fattr->cf_symlink_target = NULL;
+ } else {
+--
+2.43.0
+
diff --git a/queue-6.6/smb-client-extend-smb2_compound_op-to-accept-more-co.patch b/queue-6.6/smb-client-extend-smb2_compound_op-to-accept-more-co.patch
new file mode 100644
index 0000000000..1969dedf56
--- /dev/null
+++ b/queue-6.6/smb-client-extend-smb2_compound_op-to-accept-more-co.patch
@@ -0,0 +1,1038 @@
+From 4a9e27a2b8864b8c30d49e0a70c85bc8cc124df9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 28 Apr 2024 01:09:59 -0500
+Subject: smb: client: extend smb2_compound_op() to accept more commands
+
+From: Steve French <stfrench@microsoft.com>
+
+[ Upstream commit 3322960ce222997b1663ffa69e691b2edfec4ac9 ]
+
+Make smb2_compound_op() accept up to MAX_COMPOUND(5) commands to be
+sent over a single compounded request.
+
+This will allow next commits to read and write reparse files through a
+single roundtrip to the server.
+
+Signed-off-by: Paulo Alcantara (SUSE) <pc@manguebit.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/cifsglob.h | 4 +-
+ fs/smb/client/smb2inode.c | 783 +++++++++++++++++++-------------------
+ 2 files changed, 403 insertions(+), 384 deletions(-)
+
+diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h
+index b598c7ed497bb..cb86b1bf69b58 100644
+--- a/fs/smb/client/cifsglob.h
++++ b/fs/smb/client/cifsglob.h
+@@ -2272,8 +2272,8 @@ static inline void cifs_sg_set_buf(struct sg_table *sgtable,
+
+ struct smb2_compound_vars {
+ struct cifs_open_parms oparms;
+- struct kvec rsp_iov[3];
+- struct smb_rqst rqst[3];
++ struct kvec rsp_iov[MAX_COMPOUND];
++ struct smb_rqst rqst[MAX_COMPOUND];
+ struct kvec open_iov[SMB2_CREATE_IOV_SIZE];
+ struct kvec qi_iov;
+ struct kvec io_iov[SMB2_IOCTL_IOV_SIZE];
+diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c
+index 6cac0b107a2d0..4c66187eccdd2 100644
+--- a/fs/smb/client/smb2inode.c
++++ b/fs/smb/client/smb2inode.c
+@@ -26,15 +26,6 @@
+ #include "cached_dir.h"
+ #include "smb2status.h"
+
+-static void
+-free_set_inf_compound(struct smb_rqst *rqst)
+-{
+- if (rqst[1].rq_iov)
+- SMB2_set_info_free(&rqst[1]);
+- if (rqst[2].rq_iov)
+- SMB2_close_free(&rqst[2]);
+-}
+-
+ static inline __u32 file_create_options(struct dentry *dentry)
+ {
+ struct cifsInodeInfo *ci;
+@@ -57,8 +48,9 @@ static inline __u32 file_create_options(struct dentry *dentry)
+ */
+ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
+ struct cifs_sb_info *cifs_sb, const char *full_path,
+- __u32 desired_access, __u32 create_disposition, __u32 create_options,
+- umode_t mode, void *ptr, int command, struct cifsFileInfo *cfile,
++ __u32 desired_access, __u32 create_disposition,
++ __u32 create_options, umode_t mode, struct kvec *in_iov,
++ int *cmds, int num_cmds, struct cifsFileInfo *cfile,
+ __u8 **extbuf, size_t *extbuflen,
+ struct kvec *out_iov, int *out_buftype)
+ {
+@@ -71,8 +63,8 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
+ struct cifs_fid fid;
+ struct cifs_ses *ses = tcon->ses;
+ struct TCP_Server_Info *server;
+- int num_rqst = 0;
+- int resp_buftype[3];
++ int num_rqst = 0, i;
++ int resp_buftype[MAX_COMPOUND];
+ struct smb2_query_info_rsp *qi_rsp = NULL;
+ struct cifs_open_info_data *idata;
+ int flags = 0;
+@@ -92,7 +84,8 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
+ if (smb3_encryption_required(tcon))
+ flags |= CIFS_TRANSFORM_REQ;
+
+- resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER;
++ for (i = 0; i < ARRAY_SIZE(resp_buftype); i++)
++ resp_buftype[i] = CIFS_NO_BUFFER;
+
+ /* We already have a handle so we can skip the open */
+ if (cfile)
+@@ -130,242 +123,246 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
+ num_rqst++;
+ rc = 0;
+
+- /* Operation */
+- switch (command) {
+- case SMB2_OP_QUERY_INFO:
+- rqst[num_rqst].rq_iov = &vars->qi_iov;
+- rqst[num_rqst].rq_nvec = 1;
+-
+- if (cfile)
+- rc = SMB2_query_info_init(tcon, server,
+- &rqst[num_rqst],
+- cfile->fid.persistent_fid,
+- cfile->fid.volatile_fid,
+- FILE_ALL_INFORMATION,
+- SMB2_O_INFO_FILE, 0,
+- sizeof(struct smb2_file_all_info) +
+- PATH_MAX * 2, 0, NULL);
+- else {
+- rc = SMB2_query_info_init(tcon, server,
+- &rqst[num_rqst],
+- COMPOUND_FID,
+- COMPOUND_FID,
+- FILE_ALL_INFORMATION,
+- SMB2_O_INFO_FILE, 0,
+- sizeof(struct smb2_file_all_info) +
+- PATH_MAX * 2, 0, NULL);
+- if (!rc) {
+- smb2_set_next_command(tcon, &rqst[num_rqst]);
+- smb2_set_related(&rqst[num_rqst]);
++ for (i = 0; i < num_cmds; i++) {
++ /* Operation */
++ switch (cmds[i]) {
++ case SMB2_OP_QUERY_INFO:
++ rqst[num_rqst].rq_iov = &vars->qi_iov;
++ rqst[num_rqst].rq_nvec = 1;
++
++ if (cfile) {
++ rc = SMB2_query_info_init(tcon, server,
++ &rqst[num_rqst],
++ cfile->fid.persistent_fid,
++ cfile->fid.volatile_fid,
++ FILE_ALL_INFORMATION,
++ SMB2_O_INFO_FILE, 0,
++ sizeof(struct smb2_file_all_info) +
++ PATH_MAX * 2, 0, NULL);
++ } else {
++ rc = SMB2_query_info_init(tcon, server,
++ &rqst[num_rqst],
++ COMPOUND_FID,
++ COMPOUND_FID,
++ FILE_ALL_INFORMATION,
++ SMB2_O_INFO_FILE, 0,
++ sizeof(struct smb2_file_all_info) +
++ PATH_MAX * 2, 0, NULL);
++ if (!rc) {
++ smb2_set_next_command(tcon, &rqst[num_rqst]);
++ smb2_set_related(&rqst[num_rqst]);
++ }
+ }
+- }
+
+- if (rc)
+- goto finished;
+- num_rqst++;
+- trace_smb3_query_info_compound_enter(xid, ses->Suid, tcon->tid,
+- full_path);
+- break;
+- case SMB2_OP_POSIX_QUERY_INFO:
+- rqst[num_rqst].rq_iov = &vars->qi_iov;
+- rqst[num_rqst].rq_nvec = 1;
+-
+- if (cfile)
+- rc = SMB2_query_info_init(tcon, server,
+- &rqst[num_rqst],
+- cfile->fid.persistent_fid,
+- cfile->fid.volatile_fid,
+- SMB_FIND_FILE_POSIX_INFO,
+- SMB2_O_INFO_FILE, 0,
++ if (rc)
++ goto finished;
++ num_rqst++;
++ trace_smb3_query_info_compound_enter(xid, ses->Suid,
++ tcon->tid, full_path);
++ break;
++ case SMB2_OP_POSIX_QUERY_INFO:
++ rqst[num_rqst].rq_iov = &vars->qi_iov;
++ rqst[num_rqst].rq_nvec = 1;
++
++ if (cfile) {
+ /* TBD: fix following to allow for longer SIDs */
+- sizeof(struct smb311_posix_qinfo *) + (PATH_MAX * 2) +
+- (sizeof(struct cifs_sid) * 2), 0, NULL);
+- else {
+- rc = SMB2_query_info_init(tcon, server,
+- &rqst[num_rqst],
+- COMPOUND_FID,
+- COMPOUND_FID,
+- SMB_FIND_FILE_POSIX_INFO,
+- SMB2_O_INFO_FILE, 0,
+- sizeof(struct smb311_posix_qinfo *) + (PATH_MAX * 2) +
+- (sizeof(struct cifs_sid) * 2), 0, NULL);
+- if (!rc) {
+- smb2_set_next_command(tcon, &rqst[num_rqst]);
+- smb2_set_related(&rqst[num_rqst]);
++ rc = SMB2_query_info_init(tcon, server,
++ &rqst[num_rqst],
++ cfile->fid.persistent_fid,
++ cfile->fid.volatile_fid,
++ SMB_FIND_FILE_POSIX_INFO,
++ SMB2_O_INFO_FILE, 0,
++ sizeof(struct smb311_posix_qinfo *) +
++ (PATH_MAX * 2) +
++ (sizeof(struct cifs_sid) * 2), 0, NULL);
++ } else {
++ rc = SMB2_query_info_init(tcon, server,
++ &rqst[num_rqst],
++ COMPOUND_FID,
++ COMPOUND_FID,
++ SMB_FIND_FILE_POSIX_INFO,
++ SMB2_O_INFO_FILE, 0,
++ sizeof(struct smb311_posix_qinfo *) +
++ (PATH_MAX * 2) +
++ (sizeof(struct cifs_sid) * 2), 0, NULL);
++ if (!rc) {
++ smb2_set_next_command(tcon, &rqst[num_rqst]);
++ smb2_set_related(&rqst[num_rqst]);
++ }
+ }
+- }
+
+- if (rc)
+- goto finished;
+- num_rqst++;
+- trace_smb3_posix_query_info_compound_enter(xid, ses->Suid, tcon->tid, full_path);
+- break;
+- case SMB2_OP_DELETE:
+- trace_smb3_delete_enter(xid, ses->Suid, tcon->tid, full_path);
+- break;
+- case SMB2_OP_MKDIR:
+- /*
+- * Directories are created through parameters in the
+- * SMB2_open() call.
+- */
+- trace_smb3_mkdir_enter(xid, ses->Suid, tcon->tid, full_path);
+- break;
+- case SMB2_OP_RMDIR:
+- rqst[num_rqst].rq_iov = &vars->si_iov[0];
+- rqst[num_rqst].rq_nvec = 1;
+-
+- size[0] = 1; /* sizeof __u8 See MS-FSCC section 2.4.11 */
+- data[0] = &delete_pending[0];
+-
+- rc = SMB2_set_info_init(tcon, server,
+- &rqst[num_rqst], COMPOUND_FID,
+- COMPOUND_FID, current->tgid,
+- FILE_DISPOSITION_INFORMATION,
+- SMB2_O_INFO_FILE, 0, data, size);
+- if (rc)
+- goto finished;
+- smb2_set_next_command(tcon, &rqst[num_rqst]);
+- smb2_set_related(&rqst[num_rqst++]);
+- trace_smb3_rmdir_enter(xid, ses->Suid, tcon->tid, full_path);
+- break;
+- case SMB2_OP_SET_EOF:
+- rqst[num_rqst].rq_iov = &vars->si_iov[0];
+- rqst[num_rqst].rq_nvec = 1;
++ if (rc)
++ goto finished;
++ num_rqst++;
++ trace_smb3_posix_query_info_compound_enter(xid, ses->Suid,
++ tcon->tid, full_path);
++ break;
++ case SMB2_OP_DELETE:
++ trace_smb3_delete_enter(xid, ses->Suid, tcon->tid, full_path);
++ break;
++ case SMB2_OP_MKDIR:
++ /*
++ * Directories are created through parameters in the
++ * SMB2_open() call.
++ */
++ trace_smb3_mkdir_enter(xid, ses->Suid, tcon->tid, full_path);
++ break;
++ case SMB2_OP_RMDIR:
++ rqst[num_rqst].rq_iov = &vars->si_iov[0];
++ rqst[num_rqst].rq_nvec = 1;
+
+- size[0] = 8; /* sizeof __le64 */
+- data[0] = ptr;
++ size[0] = 1; /* sizeof __u8 See MS-FSCC section 2.4.11 */
++ data[0] = &delete_pending[0];
+
+- if (cfile) {
+ rc = SMB2_set_info_init(tcon, server,
+- &rqst[num_rqst],
+- cfile->fid.persistent_fid,
+- cfile->fid.volatile_fid,
+- current->tgid,
+- FILE_END_OF_FILE_INFORMATION,
+- SMB2_O_INFO_FILE, 0,
+- data, size);
+- } else {
+- rc = SMB2_set_info_init(tcon, server,
+- &rqst[num_rqst],
+- COMPOUND_FID,
+- COMPOUND_FID,
+- current->tgid,
+- FILE_END_OF_FILE_INFORMATION,
+- SMB2_O_INFO_FILE, 0,
+- data, size);
+- if (!rc) {
+- smb2_set_next_command(tcon, &rqst[num_rqst]);
+- smb2_set_related(&rqst[num_rqst]);
++ &rqst[num_rqst], COMPOUND_FID,
++ COMPOUND_FID, current->tgid,
++ FILE_DISPOSITION_INFORMATION,
++ SMB2_O_INFO_FILE, 0, data, size);
++ if (rc)
++ goto finished;
++ smb2_set_next_command(tcon, &rqst[num_rqst]);
++ smb2_set_related(&rqst[num_rqst++]);
++ trace_smb3_rmdir_enter(xid, ses->Suid, tcon->tid, full_path);
++ break;
++ case SMB2_OP_SET_EOF:
++ rqst[num_rqst].rq_iov = &vars->si_iov[0];
++ rqst[num_rqst].rq_nvec = 1;
++
++ size[0] = in_iov[i].iov_len;
++ data[0] = in_iov[i].iov_base;
++
++ if (cfile) {
++ rc = SMB2_set_info_init(tcon, server,
++ &rqst[num_rqst],
++ cfile->fid.persistent_fid,
++ cfile->fid.volatile_fid,
++ current->tgid,
++ FILE_END_OF_FILE_INFORMATION,
++ SMB2_O_INFO_FILE, 0,
++ data, size);
++ } else {
++ rc = SMB2_set_info_init(tcon, server,
++ &rqst[num_rqst],
++ COMPOUND_FID,
++ COMPOUND_FID,
++ current->tgid,
++ FILE_END_OF_FILE_INFORMATION,
++ SMB2_O_INFO_FILE, 0,
++ data, size);
++ if (!rc) {
++ smb2_set_next_command(tcon, &rqst[num_rqst]);
++ smb2_set_related(&rqst[num_rqst]);
++ }
+ }
+- }
+- if (rc)
+- goto finished;
+- num_rqst++;
+- trace_smb3_set_eof_enter(xid, ses->Suid, tcon->tid, full_path);
+- break;
+- case SMB2_OP_SET_INFO:
+- rqst[num_rqst].rq_iov = &vars->si_iov[0];
+- rqst[num_rqst].rq_nvec = 1;
+-
+-
+- size[0] = sizeof(FILE_BASIC_INFO);
+- data[0] = ptr;
+-
+- if (cfile)
+- rc = SMB2_set_info_init(tcon, server,
+- &rqst[num_rqst],
+- cfile->fid.persistent_fid,
+- cfile->fid.volatile_fid, current->tgid,
+- FILE_BASIC_INFORMATION,
+- SMB2_O_INFO_FILE, 0, data, size);
+- else {
+- rc = SMB2_set_info_init(tcon, server,
+- &rqst[num_rqst],
+- COMPOUND_FID,
+- COMPOUND_FID, current->tgid,
+- FILE_BASIC_INFORMATION,
+- SMB2_O_INFO_FILE, 0, data, size);
+- if (!rc) {
+- smb2_set_next_command(tcon, &rqst[num_rqst]);
+- smb2_set_related(&rqst[num_rqst]);
++ if (rc)
++ goto finished;
++ num_rqst++;
++ trace_smb3_set_eof_enter(xid, ses->Suid, tcon->tid, full_path);
++ break;
++ case SMB2_OP_SET_INFO:
++ rqst[num_rqst].rq_iov = &vars->si_iov[0];
++ rqst[num_rqst].rq_nvec = 1;
++
++ size[0] = in_iov[i].iov_len;
++ data[0] = in_iov[i].iov_base;
++
++ if (cfile) {
++ rc = SMB2_set_info_init(tcon, server,
++ &rqst[num_rqst],
++ cfile->fid.persistent_fid,
++ cfile->fid.volatile_fid, current->tgid,
++ FILE_BASIC_INFORMATION,
++ SMB2_O_INFO_FILE, 0, data, size);
++ } else {
++ rc = SMB2_set_info_init(tcon, server,
++ &rqst[num_rqst],
++ COMPOUND_FID,
++ COMPOUND_FID, current->tgid,
++ FILE_BASIC_INFORMATION,
++ SMB2_O_INFO_FILE, 0, data, size);
++ if (!rc) {
++ smb2_set_next_command(tcon, &rqst[num_rqst]);
++ smb2_set_related(&rqst[num_rqst]);
++ }
+ }
+- }
+
+- if (rc)
+- goto finished;
+- num_rqst++;
+- trace_smb3_set_info_compound_enter(xid, ses->Suid, tcon->tid,
+- full_path);
+- break;
+- case SMB2_OP_RENAME:
+- rqst[num_rqst].rq_iov = &vars->si_iov[0];
+- rqst[num_rqst].rq_nvec = 2;
++ if (rc)
++ goto finished;
++ num_rqst++;
++ trace_smb3_set_info_compound_enter(xid, ses->Suid,
++ tcon->tid, full_path);
++ break;
++ case SMB2_OP_RENAME:
++ rqst[num_rqst].rq_iov = &vars->si_iov[0];
++ rqst[num_rqst].rq_nvec = 2;
+
+- len = (2 * UniStrnlen((wchar_t *)ptr, PATH_MAX));
++ len = in_iov[i].iov_len;
+
+- vars->rename_info.ReplaceIfExists = 1;
+- vars->rename_info.RootDirectory = 0;
+- vars->rename_info.FileNameLength = cpu_to_le32(len);
++ vars->rename_info.ReplaceIfExists = 1;
++ vars->rename_info.RootDirectory = 0;
++ vars->rename_info.FileNameLength = cpu_to_le32(len);
+
+- size[0] = sizeof(struct smb2_file_rename_info);
+- data[0] = &vars->rename_info;
++ size[0] = sizeof(struct smb2_file_rename_info);
++ data[0] = &vars->rename_info;
+
+- size[1] = len + 2 /* null */;
+- data[1] = (__le16 *)ptr;
++ size[1] = len + 2 /* null */;
++ data[1] = in_iov[i].iov_base;
+
+- if (cfile)
+- rc = SMB2_set_info_init(tcon, server,
+- &rqst[num_rqst],
+- cfile->fid.persistent_fid,
+- cfile->fid.volatile_fid,
+- current->tgid, FILE_RENAME_INFORMATION,
+- SMB2_O_INFO_FILE, 0, data, size);
+- else {
+- rc = SMB2_set_info_init(tcon, server,
+- &rqst[num_rqst],
+- COMPOUND_FID, COMPOUND_FID,
+- current->tgid, FILE_RENAME_INFORMATION,
+- SMB2_O_INFO_FILE, 0, data, size);
+- if (!rc) {
+- smb2_set_next_command(tcon, &rqst[num_rqst]);
+- smb2_set_related(&rqst[num_rqst]);
++ if (cfile) {
++ rc = SMB2_set_info_init(tcon, server,
++ &rqst[num_rqst],
++ cfile->fid.persistent_fid,
++ cfile->fid.volatile_fid,
++ current->tgid, FILE_RENAME_INFORMATION,
++ SMB2_O_INFO_FILE, 0, data, size);
++ } else {
++ rc = SMB2_set_info_init(tcon, server,
++ &rqst[num_rqst],
++ COMPOUND_FID, COMPOUND_FID,
++ current->tgid, FILE_RENAME_INFORMATION,
++ SMB2_O_INFO_FILE, 0, data, size);
++ if (!rc) {
++ smb2_set_next_command(tcon, &rqst[num_rqst]);
++ smb2_set_related(&rqst[num_rqst]);
++ }
+ }
+- }
+- if (rc)
+- goto finished;
+- num_rqst++;
+- trace_smb3_rename_enter(xid, ses->Suid, tcon->tid, full_path);
+- break;
+- case SMB2_OP_HARDLINK:
+- rqst[num_rqst].rq_iov = &vars->si_iov[0];
+- rqst[num_rqst].rq_nvec = 2;
++ if (rc)
++ goto finished;
++ num_rqst++;
++ trace_smb3_rename_enter(xid, ses->Suid, tcon->tid, full_path);
++ break;
++ case SMB2_OP_HARDLINK:
++ rqst[num_rqst].rq_iov = &vars->si_iov[0];
++ rqst[num_rqst].rq_nvec = 2;
+
+- len = (2 * UniStrnlen((wchar_t *)ptr, PATH_MAX));
++ len = in_iov[i].iov_len;
+
+- vars->link_info.ReplaceIfExists = 0;
+- vars->link_info.RootDirectory = 0;
+- vars->link_info.FileNameLength = cpu_to_le32(len);
++ vars->link_info.ReplaceIfExists = 0;
++ vars->link_info.RootDirectory = 0;
++ vars->link_info.FileNameLength = cpu_to_le32(len);
+
+- size[0] = sizeof(struct smb2_file_link_info);
+- data[0] = &vars->link_info;
++ size[0] = sizeof(struct smb2_file_link_info);
++ data[0] = &vars->link_info;
+
+- size[1] = len + 2 /* null */;
+- data[1] = (__le16 *)ptr;
++ size[1] = len + 2 /* null */;
++ data[1] = in_iov[i].iov_base;
+
+- rc = SMB2_set_info_init(tcon, server,
+- &rqst[num_rqst], COMPOUND_FID,
+- COMPOUND_FID, current->tgid,
+- FILE_LINK_INFORMATION,
+- SMB2_O_INFO_FILE, 0, data, size);
+- if (rc)
+- goto finished;
+- smb2_set_next_command(tcon, &rqst[num_rqst]);
+- smb2_set_related(&rqst[num_rqst++]);
+- trace_smb3_hardlink_enter(xid, ses->Suid, tcon->tid, full_path);
+- break;
+- default:
+- cifs_dbg(VFS, "Invalid command\n");
+- rc = -EINVAL;
++ rc = SMB2_set_info_init(tcon, server,
++ &rqst[num_rqst], COMPOUND_FID,
++ COMPOUND_FID, current->tgid,
++ FILE_LINK_INFORMATION,
++ SMB2_O_INFO_FILE, 0, data, size);
++ if (rc)
++ goto finished;
++ smb2_set_next_command(tcon, &rqst[num_rqst]);
++ smb2_set_related(&rqst[num_rqst++]);
++ trace_smb3_hardlink_enter(xid, ses->Suid, tcon->tid, full_path);
++ break;
++ default:
++ cifs_dbg(VFS, "Invalid command\n");
++ rc = -EINVAL;
++ }
+ }
+ if (rc)
+ goto finished;
+@@ -397,145 +394,142 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
+ rqst, resp_buftype,
+ rsp_iov);
+
+- finished:
+- SMB2_open_free(&rqst[0]);
++finished:
++ num_rqst = 0;
++ SMB2_open_free(&rqst[num_rqst++]);
+ if (rc == -EREMCHG) {
+ pr_warn_once("server share %s deleted\n", tcon->tree_name);
+ tcon->need_reconnect = true;
+ }
+
+- switch (command) {
+- case SMB2_OP_QUERY_INFO:
+- idata = ptr;
+- if (rc == 0 && cfile && cfile->symlink_target) {
+- idata->symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL);
+- if (!idata->symlink_target)
+- rc = -ENOMEM;
+- }
+- if (rc == 0) {
+- qi_rsp = (struct smb2_query_info_rsp *)
+- rsp_iov[1].iov_base;
+- rc = smb2_validate_and_copy_iov(
+- le16_to_cpu(qi_rsp->OutputBufferOffset),
+- le32_to_cpu(qi_rsp->OutputBufferLength),
+- &rsp_iov[1], sizeof(idata->fi), (char *)&idata->fi);
+- }
+- if (rqst[1].rq_iov)
+- SMB2_query_info_free(&rqst[1]);
+- if (rqst[2].rq_iov)
+- SMB2_close_free(&rqst[2]);
+- if (rc)
+- trace_smb3_query_info_compound_err(xid, ses->Suid,
+- tcon->tid, rc);
+- else
+- trace_smb3_query_info_compound_done(xid, ses->Suid,
+- tcon->tid);
+- break;
+- case SMB2_OP_POSIX_QUERY_INFO:
+- idata = ptr;
+- if (rc == 0 && cfile && cfile->symlink_target) {
+- idata->symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL);
+- if (!idata->symlink_target)
+- rc = -ENOMEM;
+- }
+- if (rc == 0) {
+- qi_rsp = (struct smb2_query_info_rsp *)
+- rsp_iov[1].iov_base;
+- rc = smb2_validate_and_copy_iov(
+- le16_to_cpu(qi_rsp->OutputBufferOffset),
+- le32_to_cpu(qi_rsp->OutputBufferLength),
+- &rsp_iov[1], sizeof(idata->posix_fi) /* add SIDs */,
+- (char *)&idata->posix_fi);
+- }
+- if (rc == 0) {
+- unsigned int length = le32_to_cpu(qi_rsp->OutputBufferLength);
+-
+- if (length > sizeof(idata->posix_fi)) {
+- char *base = (char *)rsp_iov[1].iov_base +
+- le16_to_cpu(qi_rsp->OutputBufferOffset) +
+- sizeof(idata->posix_fi);
+- *extbuflen = length - sizeof(idata->posix_fi);
+- *extbuf = kmemdup(base, *extbuflen, GFP_KERNEL);
+- if (!*extbuf)
++ for (i = 0; i < num_cmds; i++) {
++ switch (cmds[i]) {
++ case SMB2_OP_QUERY_INFO:
++ idata = in_iov[i].iov_base;
++ if (rc == 0 && cfile && cfile->symlink_target) {
++ idata->symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL);
++ if (!idata->symlink_target)
+ rc = -ENOMEM;
+- } else {
+- rc = -EINVAL;
+ }
++ if (rc == 0) {
++ qi_rsp = (struct smb2_query_info_rsp *)
++ rsp_iov[i + 1].iov_base;
++ rc = smb2_validate_and_copy_iov(
++ le16_to_cpu(qi_rsp->OutputBufferOffset),
++ le32_to_cpu(qi_rsp->OutputBufferLength),
++ &rsp_iov[i + 1], sizeof(idata->fi), (char *)&idata->fi);
++ }
++ SMB2_query_info_free(&rqst[num_rqst++]);
++ if (rc)
++ trace_smb3_query_info_compound_err(xid, ses->Suid,
++ tcon->tid, rc);
++ else
++ trace_smb3_query_info_compound_done(xid, ses->Suid,
++ tcon->tid);
++ break;
++ case SMB2_OP_POSIX_QUERY_INFO:
++ idata = in_iov[i].iov_base;
++ if (rc == 0 && cfile && cfile->symlink_target) {
++ idata->symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL);
++ if (!idata->symlink_target)
++ rc = -ENOMEM;
++ }
++ if (rc == 0) {
++ qi_rsp = (struct smb2_query_info_rsp *)
++ rsp_iov[i + 1].iov_base;
++ rc = smb2_validate_and_copy_iov(
++ le16_to_cpu(qi_rsp->OutputBufferOffset),
++ le32_to_cpu(qi_rsp->OutputBufferLength),
++ &rsp_iov[i + 1], sizeof(idata->posix_fi) /* add SIDs */,
++ (char *)&idata->posix_fi);
++ }
++ if (rc == 0) {
++ unsigned int length = le32_to_cpu(qi_rsp->OutputBufferLength);
++
++ if (length > sizeof(idata->posix_fi)) {
++ char *base = (char *)rsp_iov[i + 1].iov_base +
++ le16_to_cpu(qi_rsp->OutputBufferOffset) +
++ sizeof(idata->posix_fi);
++ *extbuflen = length - sizeof(idata->posix_fi);
++ *extbuf = kmemdup(base, *extbuflen, GFP_KERNEL);
++ if (!*extbuf)
++ rc = -ENOMEM;
++ } else {
++ rc = -EINVAL;
++ }
++ }
++ SMB2_query_info_free(&rqst[num_rqst++]);
++ if (rc)
++ trace_smb3_posix_query_info_compound_err(xid, ses->Suid,
++ tcon->tid, rc);
++ else
++ trace_smb3_posix_query_info_compound_done(xid, ses->Suid,
++ tcon->tid);
++ break;
++ case SMB2_OP_DELETE:
++ if (rc)
++ trace_smb3_delete_err(xid, ses->Suid, tcon->tid, rc);
++ else
++ trace_smb3_delete_done(xid, ses->Suid, tcon->tid);
++ break;
++ case SMB2_OP_MKDIR:
++ if (rc)
++ trace_smb3_mkdir_err(xid, ses->Suid, tcon->tid, rc);
++ else
++ trace_smb3_mkdir_done(xid, ses->Suid, tcon->tid);
++ break;
++ case SMB2_OP_HARDLINK:
++ if (rc)
++ trace_smb3_hardlink_err(xid, ses->Suid, tcon->tid, rc);
++ else
++ trace_smb3_hardlink_done(xid, ses->Suid, tcon->tid);
++ SMB2_set_info_free(&rqst[num_rqst++]);
++ break;
++ case SMB2_OP_RENAME:
++ if (rc)
++ trace_smb3_rename_err(xid, ses->Suid, tcon->tid, rc);
++ else
++ trace_smb3_rename_done(xid, ses->Suid, tcon->tid);
++ SMB2_set_info_free(&rqst[num_rqst++]);
++ break;
++ case SMB2_OP_RMDIR:
++ if (rc)
++ trace_smb3_rmdir_err(xid, ses->Suid, tcon->tid, rc);
++ else
++ trace_smb3_rmdir_done(xid, ses->Suid, tcon->tid);
++ SMB2_set_info_free(&rqst[num_rqst++]);
++ break;
++ case SMB2_OP_SET_EOF:
++ if (rc)
++ trace_smb3_set_eof_err(xid, ses->Suid, tcon->tid, rc);
++ else
++ trace_smb3_set_eof_done(xid, ses->Suid, tcon->tid);
++ SMB2_set_info_free(&rqst[num_rqst++]);
++ break;
++ case SMB2_OP_SET_INFO:
++ if (rc)
++ trace_smb3_set_info_compound_err(xid, ses->Suid,
++ tcon->tid, rc);
++ else
++ trace_smb3_set_info_compound_done(xid, ses->Suid,
++ tcon->tid);
++ SMB2_set_info_free(&rqst[num_rqst++]);
++ break;
+ }
+- if (rqst[1].rq_iov)
+- SMB2_query_info_free(&rqst[1]);
+- if (rqst[2].rq_iov)
+- SMB2_close_free(&rqst[2]);
+- if (rc)
+- trace_smb3_posix_query_info_compound_err(xid, ses->Suid, tcon->tid, rc);
+- else
+- trace_smb3_posix_query_info_compound_done(xid, ses->Suid, tcon->tid);
+- break;
+- case SMB2_OP_DELETE:
+- if (rc)
+- trace_smb3_delete_err(xid, ses->Suid, tcon->tid, rc);
+- else
+- trace_smb3_delete_done(xid, ses->Suid, tcon->tid);
+- if (rqst[1].rq_iov)
+- SMB2_close_free(&rqst[1]);
+- break;
+- case SMB2_OP_MKDIR:
+- if (rc)
+- trace_smb3_mkdir_err(xid, ses->Suid, tcon->tid, rc);
+- else
+- trace_smb3_mkdir_done(xid, ses->Suid, tcon->tid);
+- if (rqst[1].rq_iov)
+- SMB2_close_free(&rqst[1]);
+- break;
+- case SMB2_OP_HARDLINK:
+- if (rc)
+- trace_smb3_hardlink_err(xid, ses->Suid, tcon->tid, rc);
+- else
+- trace_smb3_hardlink_done(xid, ses->Suid, tcon->tid);
+- free_set_inf_compound(rqst);
+- break;
+- case SMB2_OP_RENAME:
+- if (rc)
+- trace_smb3_rename_err(xid, ses->Suid, tcon->tid, rc);
+- else
+- trace_smb3_rename_done(xid, ses->Suid, tcon->tid);
+- free_set_inf_compound(rqst);
+- break;
+- case SMB2_OP_RMDIR:
+- if (rc)
+- trace_smb3_rmdir_err(xid, ses->Suid, tcon->tid, rc);
+- else
+- trace_smb3_rmdir_done(xid, ses->Suid, tcon->tid);
+- free_set_inf_compound(rqst);
+- break;
+- case SMB2_OP_SET_EOF:
+- if (rc)
+- trace_smb3_set_eof_err(xid, ses->Suid, tcon->tid, rc);
+- else
+- trace_smb3_set_eof_done(xid, ses->Suid, tcon->tid);
+- free_set_inf_compound(rqst);
+- break;
+- case SMB2_OP_SET_INFO:
+- if (rc)
+- trace_smb3_set_info_compound_err(xid, ses->Suid,
+- tcon->tid, rc);
+- else
+- trace_smb3_set_info_compound_done(xid, ses->Suid,
+- tcon->tid);
+- free_set_inf_compound(rqst);
+- break;
+ }
++ SMB2_close_free(&rqst[num_rqst]);
+
+ if (cfile)
+ cifsFileInfo_put(cfile);
+
++ num_cmds += 2;
+ if (out_iov && out_buftype) {
+- memcpy(out_iov, rsp_iov, 3 * sizeof(*out_iov));
+- memcpy(out_buftype, resp_buftype, 3 * sizeof(*out_buftype));
++ memcpy(out_iov, rsp_iov, num_cmds * sizeof(*out_iov));
++ memcpy(out_buftype, resp_buftype,
++ num_cmds * sizeof(*out_buftype));
+ } else {
+- free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base);
+- free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base);
+- free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base);
++ for (i = 0; i < num_cmds; i++)
++ free_rsp_buf(resp_buftype[i], rsp_iov[i].iov_base);
+ }
+ kfree(vars);
+ return rc;
+@@ -581,9 +575,10 @@ int smb2_query_path_info(const unsigned int xid,
+ struct cifsFileInfo *cfile;
+ struct cached_fid *cfid = NULL;
+ struct smb2_hdr *hdr;
+- struct kvec out_iov[3] = {};
++ struct kvec in_iov, out_iov[3] = {};
+ int out_buftype[3] = {};
+ bool islink;
++ int cmd = SMB2_OP_QUERY_INFO;
+ int rc, rc2;
+
+ data->adjust_tz = false;
+@@ -605,10 +600,14 @@ int smb2_query_path_info(const unsigned int xid,
+ return rc;
+ }
+
++ in_iov.iov_base = data;
++ in_iov.iov_len = sizeof(*data);
++
+ cifs_get_readable_path(tcon, full_path, &cfile);
+- rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, FILE_READ_ATTRIBUTES, FILE_OPEN,
+- create_options, ACL_NO_MODE, data, SMB2_OP_QUERY_INFO, cfile,
+- NULL, NULL, out_iov, out_buftype);
++ rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
++ FILE_READ_ATTRIBUTES, FILE_OPEN,
++ create_options, ACL_NO_MODE, &in_iov,
++ &cmd, 1, cfile, NULL, NULL, out_iov, out_buftype);
+ hdr = out_iov[0].iov_base;
+ /*
+ * If first iov is unset, then SMB session was dropped or we've got a
+@@ -629,9 +628,8 @@ int smb2_query_path_info(const unsigned int xid,
+ cifs_get_readable_path(tcon, full_path, &cfile);
+ rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
+ FILE_READ_ATTRIBUTES, FILE_OPEN,
+- create_options, ACL_NO_MODE, data,
+- SMB2_OP_QUERY_INFO, cfile, NULL, NULL,
+- NULL, NULL);
++ create_options, ACL_NO_MODE, &in_iov,
++ &cmd, 1, cfile, NULL, NULL, NULL, NULL);
+ break;
+ case -EREMOTE:
+ break;
+@@ -666,12 +664,13 @@ int smb311_posix_query_path_info(const unsigned int xid,
+ int rc;
+ __u32 create_options = 0;
+ struct cifsFileInfo *cfile;
+- struct kvec out_iov[3] = {};
++ struct kvec in_iov, out_iov[3] = {};
+ int out_buftype[3] = {};
+ __u8 *sidsbuf = NULL;
+ __u8 *sidsbuf_end = NULL;
+ size_t sidsbuflen = 0;
+ size_t owner_len, group_len;
++ int cmd = SMB2_OP_POSIX_QUERY_INFO;
+
+ data->adjust_tz = false;
+ data->reparse_point = false;
+@@ -682,11 +681,14 @@ int smb311_posix_query_path_info(const unsigned int xid,
+ * when we already have an open file handle for this. For now this is fast enough
+ * (always using the compounded version).
+ */
++ in_iov.iov_base = data;
++ in_iov.iov_len = sizeof(*data);
+
+ cifs_get_readable_path(tcon, full_path, &cfile);
+- rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, FILE_READ_ATTRIBUTES, FILE_OPEN,
+- create_options, ACL_NO_MODE, data, SMB2_OP_POSIX_QUERY_INFO, cfile,
+- &sidsbuf, &sidsbuflen, out_iov, out_buftype);
++ rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
++ FILE_READ_ATTRIBUTES, FILE_OPEN,
++ create_options, ACL_NO_MODE, &in_iov, &cmd, 1,
++ cfile, &sidsbuf, &sidsbuflen, out_iov, out_buftype);
+ /*
+ * If first iov is unset, then SMB session was dropped or we've got a
+ * cached open file (@cfile).
+@@ -705,10 +707,10 @@ int smb311_posix_query_path_info(const unsigned int xid,
+ create_options |= OPEN_REPARSE_POINT;
+ /* Failed on a symbolic link - query a reparse point info */
+ cifs_get_readable_path(tcon, full_path, &cfile);
+- rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, FILE_READ_ATTRIBUTES,
+- FILE_OPEN, create_options, ACL_NO_MODE, data,
+- SMB2_OP_POSIX_QUERY_INFO, cfile,
+- &sidsbuf, &sidsbuflen, NULL, NULL);
++ rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
++ FILE_READ_ATTRIBUTES, FILE_OPEN,
++ create_options, ACL_NO_MODE, &in_iov, &cmd, 1,
++ cfile, &sidsbuf, &sidsbuflen, NULL, NULL);
+ break;
+ }
+
+@@ -746,7 +748,8 @@ smb2_mkdir(const unsigned int xid, struct inode *parent_inode, umode_t mode,
+ {
+ return smb2_compound_op(xid, tcon, cifs_sb, name,
+ FILE_WRITE_ATTRIBUTES, FILE_CREATE,
+- CREATE_NOT_FILE, mode, NULL, SMB2_OP_MKDIR,
++ CREATE_NOT_FILE, mode, NULL,
++ &(int){SMB2_OP_MKDIR}, 1,
+ NULL, NULL, NULL, NULL, NULL);
+ }
+
+@@ -755,21 +758,24 @@ smb2_mkdir_setinfo(struct inode *inode, const char *name,
+ struct cifs_sb_info *cifs_sb, struct cifs_tcon *tcon,
+ const unsigned int xid)
+ {
+- FILE_BASIC_INFO data;
++ FILE_BASIC_INFO data = {};
+ struct cifsInodeInfo *cifs_i;
+ struct cifsFileInfo *cfile;
++ struct kvec in_iov;
+ u32 dosattrs;
+ int tmprc;
+
+- memset(&data, 0, sizeof(data));
++ in_iov.iov_base = &data;
++ in_iov.iov_len = sizeof(data);
+ cifs_i = CIFS_I(inode);
+ dosattrs = cifs_i->cifsAttrs | ATTR_READONLY;
+ data.Attributes = cpu_to_le32(dosattrs);
+ cifs_get_writable_path(tcon, name, FIND_WR_ANY, &cfile);
+ tmprc = smb2_compound_op(xid, tcon, cifs_sb, name,
+ FILE_WRITE_ATTRIBUTES, FILE_CREATE,
+- CREATE_NOT_FILE, ACL_NO_MODE,
+- &data, SMB2_OP_SET_INFO, cfile, NULL, NULL, NULL, NULL);
++ CREATE_NOT_FILE, ACL_NO_MODE, &in_iov,
++ &(int){SMB2_OP_SET_INFO}, 1,
++ cfile, NULL, NULL, NULL, NULL);
+ if (tmprc == 0)
+ cifs_i->cifsAttrs = dosattrs;
+ }
+@@ -779,9 +785,10 @@ smb2_rmdir(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
+ struct cifs_sb_info *cifs_sb)
+ {
+ drop_cached_dir_by_name(xid, tcon, name, cifs_sb);
+- return smb2_compound_op(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN,
+- CREATE_NOT_FILE, ACL_NO_MODE,
+- NULL, SMB2_OP_RMDIR, NULL, NULL, NULL, NULL, NULL);
++ return smb2_compound_op(xid, tcon, cifs_sb, name,
++ DELETE, FILE_OPEN, CREATE_NOT_FILE,
++ ACL_NO_MODE, NULL, &(int){SMB2_OP_RMDIR}, 1,
++ NULL, NULL, NULL, NULL, NULL);
+ }
+
+ int
+@@ -790,7 +797,8 @@ smb2_unlink(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
+ {
+ return smb2_compound_op(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN,
+ CREATE_DELETE_ON_CLOSE | OPEN_REPARSE_POINT,
+- ACL_NO_MODE, NULL, SMB2_OP_DELETE, NULL, NULL, NULL, NULL, NULL);
++ ACL_NO_MODE, NULL, &(int){SMB2_OP_DELETE}, 1,
++ NULL, NULL, NULL, NULL, NULL);
+ }
+
+ static int smb2_set_path_attr(const unsigned int xid, struct cifs_tcon *tcon,
+@@ -799,6 +807,7 @@ static int smb2_set_path_attr(const unsigned int xid, struct cifs_tcon *tcon,
+ __u32 create_options, __u32 access,
+ int command, struct cifsFileInfo *cfile)
+ {
++ struct kvec in_iov;
+ __le16 *smb2_to_name = NULL;
+ int rc;
+
+@@ -807,9 +816,12 @@ static int smb2_set_path_attr(const unsigned int xid, struct cifs_tcon *tcon,
+ rc = -ENOMEM;
+ goto smb2_rename_path;
+ }
++ in_iov.iov_base = smb2_to_name;
++ in_iov.iov_len = 2 * UniStrnlen((wchar_t *)smb2_to_name, PATH_MAX);
++
+ rc = smb2_compound_op(xid, tcon, cifs_sb, from_name, access,
+- FILE_OPEN, create_options, ACL_NO_MODE, smb2_to_name,
+- command, cfile, NULL, NULL, NULL, NULL);
++ FILE_OPEN, 0, ACL_NO_MODE, &in_iov,
++ &command, 1, cfile, NULL, NULL, NULL, NULL);
+ smb2_rename_path:
+ kfree(smb2_to_name);
+ return rc;
+@@ -849,13 +861,18 @@ smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *full_path, __u64 size,
+ struct cifs_sb_info *cifs_sb, bool set_alloc)
+ {
+- __le64 eof = cpu_to_le64(size);
+ struct cifsFileInfo *cfile;
++ struct kvec in_iov;
++ __le64 eof = cpu_to_le64(size);
+
++ in_iov.iov_base = &eof;
++ in_iov.iov_len = sizeof(eof);
+ cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile);
+ return smb2_compound_op(xid, tcon, cifs_sb, full_path,
+- FILE_WRITE_DATA, FILE_OPEN, 0, ACL_NO_MODE,
+- &eof, SMB2_OP_SET_EOF, cfile, NULL, NULL, NULL, NULL);
++ FILE_WRITE_DATA, FILE_OPEN,
++ 0, ACL_NO_MODE, &in_iov,
++ &(int){SMB2_OP_SET_EOF}, 1,
++ cfile, NULL, NULL, NULL, NULL);
+ }
+
+ int
+@@ -866,6 +883,7 @@ smb2_set_file_info(struct inode *inode, const char *full_path,
+ struct tcon_link *tlink;
+ struct cifs_tcon *tcon;
+ struct cifsFileInfo *cfile;
++ struct kvec in_iov = { .iov_base = buf, .iov_len = sizeof(*buf), };
+ int rc;
+
+ if ((buf->CreationTime == 0) && (buf->LastAccessTime == 0) &&
+@@ -881,7 +899,8 @@ smb2_set_file_info(struct inode *inode, const char *full_path,
+ cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile);
+ rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
+ FILE_WRITE_ATTRIBUTES, FILE_OPEN,
+- 0, ACL_NO_MODE, buf, SMB2_OP_SET_INFO, cfile,
++ 0, ACL_NO_MODE, &in_iov,
++ &(int){SMB2_OP_SET_INFO}, 1, cfile,
+ NULL, NULL, NULL, NULL);
+ cifs_put_tlink(tlink);
+ return rc;
+--
+2.43.0
+
diff --git a/queue-6.6/smb-client-fix-a-null-vs-is_err-check-in-wsl_set_xat.patch b/queue-6.6/smb-client-fix-a-null-vs-is_err-check-in-wsl_set_xat.patch
new file mode 100644
index 0000000000..c349bbad78
--- /dev/null
+++ b/queue-6.6/smb-client-fix-a-null-vs-is_err-check-in-wsl_set_xat.patch
@@ -0,0 +1,37 @@
+From 13d034c20cb52315348b157d7f710316c3602cf7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 31 Jan 2024 10:10:18 +0300
+Subject: smb: client: Fix a NULL vs IS_ERR() check in wsl_set_xattrs()
+
+From: Dan Carpenter <dan.carpenter@linaro.org>
+
+[ Upstream commit e0e1e09b2c41d383a2483f2ee5227b724860ced1 ]
+
+This was intended to be an IS_ERR() check. The ea_create_context()
+function doesn't return NULL.
+
+Fixes: 1eab17fe485c ("smb: client: add support for WSL reparse points")
+Reviewed-by: Paulo Alcantara <pc@manguebit.com>
+Signed-off-by: Dan Carpenter <dan.carpenter@linaro.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/reparse.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/fs/smb/client/reparse.c b/fs/smb/client/reparse.c
+index b240ccc9c887c..24feeaa32280e 100644
+--- a/fs/smb/client/reparse.c
++++ b/fs/smb/client/reparse.c
+@@ -230,7 +230,7 @@ static int wsl_set_xattrs(struct inode *inode, umode_t _mode,
+ }
+
+ cc = ea_create_context(dlen, &cc_len);
+- if (!cc)
++ if (IS_ERR(cc))
+ return PTR_ERR(cc);
+
+ ea = &cc->ea;
+--
+2.43.0
+
diff --git a/queue-6.6/smb-client-fix-minor-whitespace-errors-and-warnings.patch b/queue-6.6/smb-client-fix-minor-whitespace-errors-and-warnings.patch
new file mode 100644
index 0000000000..52a4df4966
--- /dev/null
+++ b/queue-6.6/smb-client-fix-minor-whitespace-errors-and-warnings.patch
@@ -0,0 +1,143 @@
+From 80cb25d41a829bb8f9f4145de61f35567ea5c90f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 26 Nov 2023 20:52:56 -0800
+Subject: smb: client: Fix minor whitespace errors and warnings
+
+From: Pierre Mariani <pierre.mariani@gmail.com>
+
+[ Upstream commit 0108ce08aed195d200ffbad74c1948bbaefe6625 ]
+
+Fixes no-op checkpatch errors and warnings.
+
+Signed-off-by: Pierre Mariani <pierre.mariani@gmail.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/connect.c | 25 +++++++++++++++++--------
+ 1 file changed, 17 insertions(+), 8 deletions(-)
+
+diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c
+index cb3bed8364e07..2466b28379ff8 100644
+--- a/fs/smb/client/connect.c
++++ b/fs/smb/client/connect.c
+@@ -501,6 +501,7 @@ static int reconnect_target_unlocked(struct TCP_Server_Info *server, struct dfs_
+ static int reconnect_dfs_server(struct TCP_Server_Info *server)
+ {
+ struct dfs_cache_tgt_iterator *target_hint = NULL;
++
+ DFS_CACHE_TGT_LIST(tl);
+ int num_targets = 0;
+ int rc = 0;
+@@ -763,6 +764,7 @@ cifs_read_from_socket(struct TCP_Server_Info *server, char *buf,
+ {
+ struct msghdr smb_msg = {};
+ struct kvec iov = {.iov_base = buf, .iov_len = to_read};
++
+ iov_iter_kvec(&smb_msg.msg_iter, ITER_DEST, &iov, 1, to_read);
+
+ return cifs_readv_from_socket(server, &smb_msg);
+@@ -1418,11 +1420,13 @@ cifs_match_ipaddr(struct sockaddr *srcaddr, struct sockaddr *rhs)
+ case AF_INET: {
+ struct sockaddr_in *saddr4 = (struct sockaddr_in *)srcaddr;
+ struct sockaddr_in *vaddr4 = (struct sockaddr_in *)rhs;
++
+ return (saddr4->sin_addr.s_addr == vaddr4->sin_addr.s_addr);
+ }
+ case AF_INET6: {
+ struct sockaddr_in6 *saddr6 = (struct sockaddr_in6 *)srcaddr;
+ struct sockaddr_in6 *vaddr6 = (struct sockaddr_in6 *)rhs;
++
+ return (ipv6_addr_equal(&saddr6->sin6_addr, &vaddr6->sin6_addr)
+ && saddr6->sin6_scope_id == vaddr6->sin6_scope_id);
+ }
+@@ -2607,8 +2611,8 @@ cifs_get_tcon(struct cifs_ses *ses, struct smb3_fs_context *ctx)
+ rc = -EOPNOTSUPP;
+ goto out_fail;
+ } else {
+- cifs_dbg(VFS, "Check vers= mount option. SMB3.11 "
+- "disabled but required for POSIX extensions\n");
++ cifs_dbg(VFS,
++ "Check vers= mount option. SMB3.11 disabled but required for POSIX extensions\n");
+ rc = -EOPNOTSUPP;
+ goto out_fail;
+ }
+@@ -2751,7 +2755,6 @@ cifs_put_tlink(struct tcon_link *tlink)
+ if (!IS_ERR(tlink_tcon(tlink)))
+ cifs_put_tcon(tlink_tcon(tlink));
+ kfree(tlink);
+- return;
+ }
+
+ static int
+@@ -2892,6 +2895,7 @@ static inline void
+ cifs_reclassify_socket4(struct socket *sock)
+ {
+ struct sock *sk = sock->sk;
++
+ BUG_ON(!sock_allow_reclassification(sk));
+ sock_lock_init_class_and_name(sk, "slock-AF_INET-CIFS",
+ &cifs_slock_key[0], "sk_lock-AF_INET-CIFS", &cifs_key[0]);
+@@ -2901,6 +2905,7 @@ static inline void
+ cifs_reclassify_socket6(struct socket *sock)
+ {
+ struct sock *sk = sock->sk;
++
+ BUG_ON(!sock_allow_reclassification(sk));
+ sock_lock_init_class_and_name(sk, "slock-AF_INET6-CIFS",
+ &cifs_slock_key[1], "sk_lock-AF_INET6-CIFS", &cifs_key[1]);
+@@ -2935,15 +2940,18 @@ static int
+ bind_socket(struct TCP_Server_Info *server)
+ {
+ int rc = 0;
++
+ if (server->srcaddr.ss_family != AF_UNSPEC) {
+ /* Bind to the specified local IP address */
+ struct socket *socket = server->ssocket;
++
+ rc = kernel_bind(socket,
+ (struct sockaddr *) &server->srcaddr,
+ sizeof(server->srcaddr));
+ if (rc < 0) {
+ struct sockaddr_in *saddr4;
+ struct sockaddr_in6 *saddr6;
++
+ saddr4 = (struct sockaddr_in *)&server->srcaddr;
+ saddr6 = (struct sockaddr_in6 *)&server->srcaddr;
+ if (saddr6->sin6_family == AF_INET6)
+@@ -3173,6 +3181,7 @@ void reset_cifs_unix_caps(unsigned int xid, struct cifs_tcon *tcon,
+
+ if (!CIFSSMBQFSUnixInfo(xid, tcon)) {
+ __u64 cap = le64_to_cpu(tcon->fsUnixInfo.Capability);
++
+ cifs_dbg(FYI, "unix caps which server supports %lld\n", cap);
+ /*
+ * check for reconnect case in which we do not
+@@ -3698,7 +3707,7 @@ CIFSTCon(const unsigned int xid, struct cifs_ses *ses,
+ smb_buffer_response = smb_buffer;
+
+ header_assemble(smb_buffer, SMB_COM_TREE_CONNECT_ANDX,
+- NULL /*no tid */ , 4 /*wct */ );
++ NULL /*no tid */, 4 /*wct */);
+
+ smb_buffer->Mid = get_next_mid(ses->server);
+ smb_buffer->Uid = ses->Suid;
+@@ -3717,12 +3726,12 @@ CIFSTCon(const unsigned int xid, struct cifs_ses *ses,
+ if (ses->server->sign)
+ smb_buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
+
+- if (ses->capabilities & CAP_STATUS32) {
++ if (ses->capabilities & CAP_STATUS32)
+ smb_buffer->Flags2 |= SMBFLG2_ERR_STATUS;
+- }
+- if (ses->capabilities & CAP_DFS) {
++
++ if (ses->capabilities & CAP_DFS)
+ smb_buffer->Flags2 |= SMBFLG2_DFS;
+- }
++
+ if (ses->capabilities & CAP_UNICODE) {
+ smb_buffer->Flags2 |= SMBFLG2_UNICODE;
+ length =
+--
+2.43.0
+
diff --git a/queue-6.6/smb-client-fix-null-ptr-deref-in-cifs_mark_open_hand.patch b/queue-6.6/smb-client-fix-null-ptr-deref-in-cifs_mark_open_hand.patch
new file mode 100644
index 0000000000..4a21c9394f
--- /dev/null
+++ b/queue-6.6/smb-client-fix-null-ptr-deref-in-cifs_mark_open_hand.patch
@@ -0,0 +1,107 @@
+From ffb331b662deb3cacd96d0bba349782cc40b3584 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 8 Apr 2024 18:32:17 -0300
+Subject: smb: client: fix NULL ptr deref in
+ cifs_mark_open_handles_for_deleted_file()
+
+From: Paulo Alcantara <pc@manguebit.com>
+
+[ Upstream commit ec4535b2a1d709d3a1fbec26739c672f13c98a7b ]
+
+cifs_get_fattr() may be called with a NULL inode, so check for a
+non-NULL inode before calling
+cifs_mark_open_handles_for_deleted_file().
+
+This fixes the following oops:
+
+ mount.cifs //srv/share /mnt -o ...,vers=3.1.1
+ cd /mnt
+ touch foo; tail -f foo &
+ rm foo
+ cat foo
+
+ BUG: kernel NULL pointer dereference, address: 00000000000005c0
+ #PF: supervisor read access in kernel mode
+ #PF: error_code(0x0000) - not-present page
+ PGD 0 P4D 0
+ Oops: 0000 [#1] PREEMPT SMP NOPTI
+ CPU: 2 PID: 696 Comm: cat Not tainted 6.9.0-rc2 #1
+ Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS
+ 1.16.3-1.fc39 04/01/2014
+ RIP: 0010:__lock_acquire+0x5d/0x1c70
+ Code: 00 00 44 8b a4 24 a0 00 00 00 45 85 f6 0f 84 bb 06 00 00 8b 2d
+ 48 e2 95 01 45 89 c3 41 89 d2 45 89 c8 85 ed 0 0 <48> 81 3f 40 7a 76
+ 83 44 0f 44 d8 83 fe 01 0f 86 1b 03 00 00 31 d2
+ RSP: 0018:ffffc90000b37490 EFLAGS: 00010002
+ RAX: 0000000000000000 RBX: ffff888110021ec0 RCX: 0000000000000000
+ RDX: 0000000000000000 RSI: 0000000000000000 RDI: 00000000000005c0
+ RBP: 0000000000000001 R08: 0000000000000000 R09: 0000000000000000
+ R10: 0000000000000000 R11: 0000000000000001 R12: 0000000000000000
+ R13: 0000000000000000 R14: 0000000000000001 R15: 0000000000000200
+ FS: 00007f2a1fa08740(0000) GS:ffff888157a00000(0000)
+ knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0:
+ 0000000080050033
+ CR2: 00000000000005c0 CR3: 000000011ac7c000 CR4: 0000000000750ef0
+ PKRU: 55555554
+ Call Trace:
+ <TASK>
+ ? __die+0x23/0x70
+ ? page_fault_oops+0x180/0x490
+ ? srso_alias_return_thunk+0x5/0xfbef5
+ ? exc_page_fault+0x70/0x230
+ ? asm_exc_page_fault+0x26/0x30
+ ? __lock_acquire+0x5d/0x1c70
+ ? srso_alias_return_thunk+0x5/0xfbef5
+ ? srso_alias_return_thunk+0x5/0xfbef5
+ lock_acquire+0xc0/0x2d0
+ ? cifs_mark_open_handles_for_deleted_file+0x3a/0x100 [cifs]
+ ? srso_alias_return_thunk+0x5/0xfbef5
+ ? kmem_cache_alloc+0x2d9/0x370
+ _raw_spin_lock+0x34/0x80
+ ? cifs_mark_open_handles_for_deleted_file+0x3a/0x100 [cifs]
+ cifs_mark_open_handles_for_deleted_file+0x3a/0x100 [cifs]
+ cifs_get_fattr+0x24c/0x940 [cifs]
+ ? srso_alias_return_thunk+0x5/0xfbef5
+ cifs_get_inode_info+0x96/0x120 [cifs]
+ cifs_lookup+0x16e/0x800 [cifs]
+ cifs_atomic_open+0xc7/0x5d0 [cifs]
+ ? lookup_open.isra.0+0x3ce/0x5f0
+ ? __pfx_cifs_atomic_open+0x10/0x10 [cifs]
+ lookup_open.isra.0+0x3ce/0x5f0
+ path_openat+0x42b/0xc30
+ ? srso_alias_return_thunk+0x5/0xfbef5
+ ? srso_alias_return_thunk+0x5/0xfbef5
+ ? srso_alias_return_thunk+0x5/0xfbef5
+ do_filp_open+0xc4/0x170
+ do_sys_openat2+0xab/0xe0
+ __x64_sys_openat+0x57/0xa0
+ do_syscall_64+0xc1/0x1e0
+ entry_SYSCALL_64_after_hwframe+0x72/0x7a
+
+Fixes: ffceb7640cbf ("smb: client: do not defer close open handles to deleted files")
+Reviewed-by: Meetakshi Setiya <msetiya@microsoft.com>
+Reviewed-by: Bharath SM <bharathsm@microsoft.com>
+Signed-off-by: Paulo Alcantara (Red Hat) <pc@manguebit.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/inode.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c
+index b304215a4d668..9cdbc3ccc1d14 100644
+--- a/fs/smb/client/inode.c
++++ b/fs/smb/client/inode.c
+@@ -1105,7 +1105,8 @@ static int cifs_get_fattr(struct cifs_open_info_data *data,
+ } else {
+ cifs_open_info_to_fattr(fattr, data, sb);
+ }
+- if (!rc && fattr->cf_flags & CIFS_FATTR_DELETE_PENDING)
++ if (!rc && *inode &&
++ (fattr->cf_flags & CIFS_FATTR_DELETE_PENDING))
+ cifs_mark_open_handles_for_deleted_file(*inode, full_path);
+ break;
+ case -EREMOTE:
+--
+2.43.0
+
diff --git a/queue-6.6/smb-client-fix-potential-broken-compound-request.patch b/queue-6.6/smb-client-fix-potential-broken-compound-request.patch
new file mode 100644
index 0000000000..01df675b69
--- /dev/null
+++ b/queue-6.6/smb-client-fix-potential-broken-compound-request.patch
@@ -0,0 +1,200 @@
+From 567981f811fd30bbfce09d7a58dabf7122248a4c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 25 Jan 2024 17:04:05 -0300
+Subject: smb: client: fix potential broken compound request
+
+From: Paulo Alcantara <pc@manguebit.com>
+
+[ Upstream commit 6914d288c63682e20e0f6e1e0b8e8f5847012d67 ]
+
+Now that smb2_compound_op() can accept up to 5 commands in a single
+compound request, set the appropriate NextCommand and related flags to
+all subsequent commands as well as handling the case where a valid
+@cfile is passed and therefore skipping create and close requests in
+the compound chain.
+
+This fix a potential broken compound request that could be sent from
+smb2_get_reparse_inode() if the client found a valid open
+file (@cfile) prior to calling smb2_compound_op().
+
+Signed-off-by: Paulo Alcantara <pc@manguebit.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/smb2inode.c | 106 ++++++++++++++++++++++----------------
+ 1 file changed, 63 insertions(+), 43 deletions(-)
+
+diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c
+index add90eb8fc165..33f3fffcb8277 100644
+--- a/fs/smb/client/smb2inode.c
++++ b/fs/smb/client/smb2inode.c
+@@ -223,14 +223,13 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
+ SMB2_O_INFO_FILE, 0,
+ sizeof(struct smb2_file_all_info) +
+ PATH_MAX * 2, 0, NULL);
+- if (!rc) {
+- smb2_set_next_command(tcon, &rqst[num_rqst]);
+- smb2_set_related(&rqst[num_rqst]);
+- }
+ }
+-
+- if (rc)
++ if (!rc && (!cfile || num_rqst > 1)) {
++ smb2_set_next_command(tcon, &rqst[num_rqst]);
++ smb2_set_related(&rqst[num_rqst]);
++ } else if (rc) {
+ goto finished;
++ }
+ num_rqst++;
+ trace_smb3_query_info_compound_enter(xid, ses->Suid,
+ tcon->tid, full_path);
+@@ -260,14 +259,13 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
+ sizeof(struct smb311_posix_qinfo *) +
+ (PATH_MAX * 2) +
+ (sizeof(struct cifs_sid) * 2), 0, NULL);
+- if (!rc) {
+- smb2_set_next_command(tcon, &rqst[num_rqst]);
+- smb2_set_related(&rqst[num_rqst]);
+- }
+ }
+-
+- if (rc)
++ if (!rc && (!cfile || num_rqst > 1)) {
++ smb2_set_next_command(tcon, &rqst[num_rqst]);
++ smb2_set_related(&rqst[num_rqst]);
++ } else if (rc) {
+ goto finished;
++ }
+ num_rqst++;
+ trace_smb3_posix_query_info_compound_enter(xid, ses->Suid,
+ tcon->tid, full_path);
+@@ -325,13 +323,13 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
+ FILE_END_OF_FILE_INFORMATION,
+ SMB2_O_INFO_FILE, 0,
+ data, size);
+- if (!rc) {
+- smb2_set_next_command(tcon, &rqst[num_rqst]);
+- smb2_set_related(&rqst[num_rqst]);
+- }
+ }
+- if (rc)
++ if (!rc && (!cfile || num_rqst > 1)) {
++ smb2_set_next_command(tcon, &rqst[num_rqst]);
++ smb2_set_related(&rqst[num_rqst]);
++ } else if (rc) {
+ goto finished;
++ }
+ num_rqst++;
+ trace_smb3_set_eof_enter(xid, ses->Suid, tcon->tid, full_path);
+ break;
+@@ -356,14 +354,13 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
+ COMPOUND_FID, current->tgid,
+ FILE_BASIC_INFORMATION,
+ SMB2_O_INFO_FILE, 0, data, size);
+- if (!rc) {
+- smb2_set_next_command(tcon, &rqst[num_rqst]);
+- smb2_set_related(&rqst[num_rqst]);
+- }
+ }
+-
+- if (rc)
++ if (!rc && (!cfile || num_rqst > 1)) {
++ smb2_set_next_command(tcon, &rqst[num_rqst]);
++ smb2_set_related(&rqst[num_rqst]);
++ } else if (rc) {
+ goto finished;
++ }
+ num_rqst++;
+ trace_smb3_set_info_compound_enter(xid, ses->Suid,
+ tcon->tid, full_path);
+@@ -397,13 +394,13 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
+ COMPOUND_FID, COMPOUND_FID,
+ current->tgid, FILE_RENAME_INFORMATION,
+ SMB2_O_INFO_FILE, 0, data, size);
+- if (!rc) {
+- smb2_set_next_command(tcon, &rqst[num_rqst]);
+- smb2_set_related(&rqst[num_rqst]);
+- }
+ }
+- if (rc)
++ if (!rc && (!cfile || num_rqst > 1)) {
++ smb2_set_next_command(tcon, &rqst[num_rqst]);
++ smb2_set_related(&rqst[num_rqst]);
++ } else if (rc) {
+ goto finished;
++ }
+ num_rqst++;
+ trace_smb3_rename_enter(xid, ses->Suid, tcon->tid, full_path);
+ break;
+@@ -438,15 +435,27 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
+ rqst[num_rqst].rq_iov = vars->io_iov;
+ rqst[num_rqst].rq_nvec = ARRAY_SIZE(vars->io_iov);
+
+- rc = SMB2_ioctl_init(tcon, server, &rqst[num_rqst],
+- COMPOUND_FID, COMPOUND_FID,
+- FSCTL_SET_REPARSE_POINT,
+- in_iov[i].iov_base,
+- in_iov[i].iov_len, 0);
+- if (rc)
++ if (cfile) {
++ rc = SMB2_ioctl_init(tcon, server, &rqst[num_rqst],
++ cfile->fid.persistent_fid,
++ cfile->fid.volatile_fid,
++ FSCTL_SET_REPARSE_POINT,
++ in_iov[i].iov_base,
++ in_iov[i].iov_len, 0);
++ } else {
++ rc = SMB2_ioctl_init(tcon, server, &rqst[num_rqst],
++ COMPOUND_FID, COMPOUND_FID,
++ FSCTL_SET_REPARSE_POINT,
++ in_iov[i].iov_base,
++ in_iov[i].iov_len, 0);
++ }
++ if (!rc && (!cfile || num_rqst > 1)) {
++ smb2_set_next_command(tcon, &rqst[num_rqst]);
++ smb2_set_related(&rqst[num_rqst]);
++ } else if (rc) {
+ goto finished;
+- smb2_set_next_command(tcon, &rqst[num_rqst]);
+- smb2_set_related(&rqst[num_rqst++]);
++ }
++ num_rqst++;
+ trace_smb3_set_reparse_compound_enter(xid, ses->Suid,
+ tcon->tid, full_path);
+ break;
+@@ -454,14 +463,25 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
+ rqst[num_rqst].rq_iov = vars->io_iov;
+ rqst[num_rqst].rq_nvec = ARRAY_SIZE(vars->io_iov);
+
+- rc = SMB2_ioctl_init(tcon, server, &rqst[num_rqst],
+- COMPOUND_FID, COMPOUND_FID,
+- FSCTL_GET_REPARSE_POINT,
+- NULL, 0, CIFSMaxBufSize);
+- if (rc)
++ if (cfile) {
++ rc = SMB2_ioctl_init(tcon, server, &rqst[num_rqst],
++ cfile->fid.persistent_fid,
++ cfile->fid.volatile_fid,
++ FSCTL_GET_REPARSE_POINT,
++ NULL, 0, CIFSMaxBufSize);
++ } else {
++ rc = SMB2_ioctl_init(tcon, server, &rqst[num_rqst],
++ COMPOUND_FID, COMPOUND_FID,
++ FSCTL_GET_REPARSE_POINT,
++ NULL, 0, CIFSMaxBufSize);
++ }
++ if (!rc && (!cfile || num_rqst > 1)) {
++ smb2_set_next_command(tcon, &rqst[num_rqst]);
++ smb2_set_related(&rqst[num_rqst]);
++ } else if (rc) {
+ goto finished;
+- smb2_set_next_command(tcon, &rqst[num_rqst]);
+- smb2_set_related(&rqst[num_rqst++]);
++ }
++ num_rqst++;
+ trace_smb3_get_reparse_compound_enter(xid, ses->Suid,
+ tcon->tid, full_path);
+ break;
+--
+2.43.0
+
diff --git a/queue-6.6/smb-client-get-rid-of-smb311_posix_query_path_info.patch b/queue-6.6/smb-client-get-rid-of-smb311_posix_query_path_info.patch
new file mode 100644
index 0000000000..39b97a016b
--- /dev/null
+++ b/queue-6.6/smb-client-get-rid-of-smb311_posix_query_path_info.patch
@@ -0,0 +1,199 @@
+From 6205c8fa40d7e8b86c3e7315e901d4fccb5e7bea Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 19 Jan 2024 01:08:28 -0300
+Subject: smb: client: get rid of smb311_posix_query_path_info()
+
+From: Paulo Alcantara <pc@manguebit.com>
+
+[ Upstream commit f83709b9e0eb7048d74ba4515f268c6eacbce9c9 ]
+
+Merge smb311_posix_query_path_info into ->query_path_info() to get rid
+of duplicate code.
+
+Signed-off-by: Paulo Alcantara <pc@manguebit.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/inode.c | 4 +-
+ fs/smb/client/smb2inode.c | 115 +++++++++++---------------------------
+ 2 files changed, 36 insertions(+), 83 deletions(-)
+
+diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c
+index 0110589acb853..d7e3da8489a0b 100644
+--- a/fs/smb/client/inode.c
++++ b/fs/smb/client/inode.c
+@@ -1313,6 +1313,7 @@ static int smb311_posix_get_fattr(struct cifs_open_info_data *data,
+ const unsigned int xid)
+ {
+ struct cifs_open_info_data tmp_data = {};
++ struct TCP_Server_Info *server;
+ struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
+ struct cifs_tcon *tcon;
+ struct tcon_link *tlink;
+@@ -1323,12 +1324,13 @@ static int smb311_posix_get_fattr(struct cifs_open_info_data *data,
+ if (IS_ERR(tlink))
+ return PTR_ERR(tlink);
+ tcon = tlink_tcon(tlink);
++ server = tcon->ses->server;
+
+ /*
+ * 1. Fetch file metadata if not provided (data)
+ */
+ if (!data) {
+- rc = smb311_posix_query_path_info(xid, tcon, cifs_sb,
++ rc = server->ops->query_path_info(xid, tcon, cifs_sb,
+ full_path, &tmp_data);
+ data = &tmp_data;
+ }
+diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c
+index 4cd4b8a63316d..dfa4e5362213f 100644
+--- a/fs/smb/client/smb2inode.c
++++ b/fs/smb/client/smb2inode.c
+@@ -699,7 +699,7 @@ int smb2_query_path_info(const unsigned int xid,
+ struct smb2_hdr *hdr;
+ struct kvec in_iov[2], out_iov[3] = {};
+ int out_buftype[3] = {};
+- int cmds[2] = { SMB2_OP_QUERY_INFO, };
++ int cmds[2];
+ bool islink;
+ int i, num_cmds;
+ int rc, rc2;
+@@ -707,20 +707,36 @@ int smb2_query_path_info(const unsigned int xid,
+ data->adjust_tz = false;
+ data->reparse_point = false;
+
+- if (strcmp(full_path, ""))
+- rc = -ENOENT;
+- else
+- rc = open_cached_dir(xid, tcon, full_path, cifs_sb, false, &cfid);
+- /* If it is a root and its handle is cached then use it */
+- if (!rc) {
+- if (cfid->file_all_info_is_valid) {
+- memcpy(&data->fi, &cfid->file_all_info, sizeof(data->fi));
++ /*
++ * BB TODO: Add support for using cached root handle in SMB3.1.1 POSIX.
++ * Create SMB2_query_posix_info worker function to do non-compounded
++ * query when we already have an open file handle for this. For now this
++ * is fast enough (always using the compounded version).
++ */
++ if (!tcon->posix_extensions) {
++ if (*full_path) {
++ rc = -ENOENT;
+ } else {
+- rc = SMB2_query_info(xid, tcon, cfid->fid.persistent_fid,
+- cfid->fid.volatile_fid, &data->fi);
++ rc = open_cached_dir(xid, tcon, full_path,
++ cifs_sb, false, &cfid);
+ }
+- close_cached_dir(cfid);
+- return rc;
++ /* If it is a root and its handle is cached then use it */
++ if (!rc) {
++ if (cfid->file_all_info_is_valid) {
++ memcpy(&data->fi, &cfid->file_all_info,
++ sizeof(data->fi));
++ } else {
++ rc = SMB2_query_info(xid, tcon,
++ cfid->fid.persistent_fid,
++ cfid->fid.volatile_fid,
++ &data->fi);
++ }
++ close_cached_dir(cfid);
++ return rc;
++ }
++ cmds[0] = SMB2_OP_QUERY_INFO;
++ } else {
++ cmds[0] = SMB2_OP_POSIX_QUERY_INFO;
+ }
+
+ in_iov[0].iov_base = data;
+@@ -743,6 +759,10 @@ int smb2_query_path_info(const unsigned int xid,
+ switch (rc) {
+ case 0:
+ case -EOPNOTSUPP:
++ /*
++ * BB TODO: When support for special files added to Samba
++ * re-verify this path.
++ */
+ rc = parse_create_response(data, cifs_sb, &out_iov[0]);
+ if (rc || !data->reparse_point)
+ goto out;
+@@ -782,75 +802,6 @@ int smb2_query_path_info(const unsigned int xid,
+ return rc;
+ }
+
+-int smb311_posix_query_path_info(const unsigned int xid,
+- struct cifs_tcon *tcon,
+- struct cifs_sb_info *cifs_sb,
+- const char *full_path,
+- struct cifs_open_info_data *data)
+-{
+- int rc;
+- __u32 create_options = 0;
+- struct cifsFileInfo *cfile;
+- struct kvec in_iov[2], out_iov[3] = {};
+- int out_buftype[3] = {};
+- int cmds[2] = { SMB2_OP_POSIX_QUERY_INFO, };
+- int i, num_cmds;
+-
+- data->adjust_tz = false;
+- data->reparse_point = false;
+-
+- /*
+- * BB TODO: Add support for using the cached root handle.
+- * Create SMB2_query_posix_info worker function to do non-compounded query
+- * when we already have an open file handle for this. For now this is fast enough
+- * (always using the compounded version).
+- */
+- in_iov[0].iov_base = data;
+- in_iov[0].iov_len = sizeof(*data);
+- in_iov[1] = in_iov[0];
+-
+- cifs_get_readable_path(tcon, full_path, &cfile);
+- rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
+- FILE_READ_ATTRIBUTES, FILE_OPEN,
+- create_options, ACL_NO_MODE, in_iov,
+- cmds, 1, cfile, out_iov, out_buftype);
+- /*
+- * If first iov is unset, then SMB session was dropped or we've got a
+- * cached open file (@cfile).
+- */
+- if (!out_iov[0].iov_base || out_buftype[0] == CIFS_NO_BUFFER)
+- goto out;
+-
+- switch (rc) {
+- case 0:
+- case -EOPNOTSUPP:
+- /* BB TODO: When support for special files added to Samba re-verify this path */
+- rc = parse_create_response(data, cifs_sb, &out_iov[0]);
+- if (rc || !data->reparse_point)
+- goto out;
+-
+- if (data->reparse.tag == IO_REPARSE_TAG_SYMLINK) {
+- /* symlink already parsed in create response */
+- num_cmds = 1;
+- } else {
+- cmds[1] = SMB2_OP_GET_REPARSE;
+- num_cmds = 2;
+- }
+- create_options |= OPEN_REPARSE_POINT;
+- cifs_get_readable_path(tcon, full_path, &cfile);
+- rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
+- FILE_READ_ATTRIBUTES, FILE_OPEN,
+- create_options, ACL_NO_MODE, in_iov,
+- cmds, num_cmds, cfile, NULL, NULL);
+- break;
+- }
+-
+-out:
+- for (i = 0; i < ARRAY_SIZE(out_buftype); i++)
+- free_rsp_buf(out_buftype[i], out_iov[i].iov_base);
+- return rc;
+-}
+-
+ int
+ smb2_mkdir(const unsigned int xid, struct inode *parent_inode, umode_t mode,
+ struct cifs_tcon *tcon, const char *name,
+--
+2.43.0
+
diff --git a/queue-6.6/smb-client-handle-path-separator-of-created-smb-syml.patch b/queue-6.6/smb-client-handle-path-separator-of-created-smb-syml.patch
new file mode 100644
index 0000000000..8ba8328ea3
--- /dev/null
+++ b/queue-6.6/smb-client-handle-path-separator-of-created-smb-syml.patch
@@ -0,0 +1,58 @@
+From 08b97675e0836f652bba699030ca65487fd0ce56 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 11 Feb 2024 20:19:31 -0300
+Subject: smb: client: handle path separator of created SMB symlinks
+
+From: Paulo Alcantara <pc@manguebit.com>
+
+[ Upstream commit 8bde59b20de06339d598e8b05e5195f7c631c38b ]
+
+Convert path separator to CIFS_DIR_SEP(cifs_sb) from symlink target
+before sending it over the wire otherwise the created SMB symlink may
+become innaccesible from server side.
+
+Fixes: 514d793e27a3 ("smb: client: allow creating symlinks via reparse points")
+Signed-off-by: Paulo Alcantara (Red Hat) <pc@manguebit.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/smb2ops.c | 8 ++++++--
+ 1 file changed, 6 insertions(+), 2 deletions(-)
+
+diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
+index 23cf6e92fd54c..9ade347978709 100644
+--- a/fs/smb/client/smb2ops.c
++++ b/fs/smb/client/smb2ops.c
+@@ -5212,7 +5212,7 @@ static int smb2_create_reparse_symlink(const unsigned int xid,
+ struct inode *new;
+ struct kvec iov;
+ __le16 *path;
+- char *sym;
++ char *sym, sep = CIFS_DIR_SEP(cifs_sb);
+ u16 len, plen;
+ int rc = 0;
+
+@@ -5226,7 +5226,8 @@ static int smb2_create_reparse_symlink(const unsigned int xid,
+ .symlink_target = sym,
+ };
+
+- path = cifs_convert_path_to_utf16(symname, cifs_sb);
++ convert_delimiter(sym, sep);
++ path = cifs_convert_path_to_utf16(sym, cifs_sb);
+ if (!path) {
+ rc = -ENOMEM;
+ goto out;
+@@ -5249,7 +5250,10 @@ static int smb2_create_reparse_symlink(const unsigned int xid,
+ buf->PrintNameLength = cpu_to_le16(plen);
+ memcpy(buf->PathBuffer, path, plen);
+ buf->Flags = cpu_to_le32(*symname != '/' ? SYMLINK_FLAG_RELATIVE : 0);
++ if (*sym != sep)
++ buf->Flags = cpu_to_le32(SYMLINK_FLAG_RELATIVE);
+
++ convert_delimiter(sym, '/');
+ iov.iov_base = buf;
+ iov.iov_len = len;
+ new = smb2_get_reparse_inode(&data, inode->i_sb, xid,
+--
+2.43.0
+
diff --git a/queue-6.6/smb-client-handle-special-files-and-symlinks-in-smb3.patch b/queue-6.6/smb-client-handle-special-files-and-symlinks-in-smb3.patch
new file mode 100644
index 0000000000..86c9cfa25c
--- /dev/null
+++ b/queue-6.6/smb-client-handle-special-files-and-symlinks-in-smb3.patch
@@ -0,0 +1,148 @@
+From 9a0426dc1edaadce4bf824ba3ea3fbae46d7b561 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Nov 2023 18:23:33 -0300
+Subject: smb: client: handle special files and symlinks in SMB3 POSIX
+
+From: Paulo Alcantara <pc@manguebit.com>
+
+[ Upstream commit 9c38568a75c160786d5f5d5b96aeefed0c1b76bd ]
+
+Parse reparse points in SMB3 posix query info as they will be
+supported and required by the new specification.
+
+Signed-off-by: Paulo Alcantara (SUSE) <pc@manguebit.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/inode.c | 50 +++++++++++++++++++++++++------------------
+ 1 file changed, 29 insertions(+), 21 deletions(-)
+
+diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c
+index 89dfb405f9c1e..b8260ace2bee9 100644
+--- a/fs/smb/client/inode.c
++++ b/fs/smb/client/inode.c
+@@ -693,29 +693,36 @@ static void smb311_posix_info_to_fattr(struct cifs_fattr *fattr,
+ fattr->cf_mtime.tv_sec += tcon->ses->server->timeAdj;
+ }
+
++ /*
++ * The srv fs device id is overridden on network mount so setting
++ * @fattr->cf_rdev isn't needed here.
++ */
+ fattr->cf_eof = le64_to_cpu(info->EndOfFile);
+ fattr->cf_bytes = le64_to_cpu(info->AllocationSize);
+ fattr->cf_createtime = le64_to_cpu(info->CreationTime);
+-
+ fattr->cf_nlink = le32_to_cpu(info->HardLinks);
+ fattr->cf_mode = (umode_t) le32_to_cpu(info->Mode);
+- /* The srv fs device id is overridden on network mount so setting rdev isn't needed here */
+- /* fattr->cf_rdev = le32_to_cpu(info->DeviceId); */
+
+- if (data->symlink) {
+- fattr->cf_mode |= S_IFLNK;
+- fattr->cf_dtype = DT_LNK;
+- fattr->cf_symlink_target = data->symlink_target;
+- data->symlink_target = NULL;
+- } else if (fattr->cf_cifsattrs & ATTR_DIRECTORY) {
++ if (cifs_open_data_reparse(data) &&
++ cifs_reparse_point_to_fattr(cifs_sb, fattr, data))
++ goto out_reparse;
++
++ fattr->cf_mode &= ~S_IFMT;
++ if (fattr->cf_cifsattrs & ATTR_DIRECTORY) {
+ fattr->cf_mode |= S_IFDIR;
+ fattr->cf_dtype = DT_DIR;
+ } else { /* file */
+ fattr->cf_mode |= S_IFREG;
+ fattr->cf_dtype = DT_REG;
+ }
+- /* else if reparse point ... TODO: add support for FIFO and blk dev; special file types */
+
++out_reparse:
++ if (S_ISLNK(fattr->cf_mode)) {
++ if (likely(data->symlink_target))
++ fattr->cf_eof = strnlen(data->symlink_target, PATH_MAX);
++ fattr->cf_symlink_target = data->symlink_target;
++ data->symlink_target = NULL;
++ }
+ sid_to_id(cifs_sb, owner, fattr, SIDOWNER);
+ sid_to_id(cifs_sb, group, fattr, SIDGROUP);
+
+@@ -740,25 +747,25 @@ bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb,
+ if (tag == IO_REPARSE_TAG_NFS && buf) {
+ switch (le64_to_cpu(buf->InodeType)) {
+ case NFS_SPECFILE_CHR:
+- fattr->cf_mode |= S_IFCHR | cifs_sb->ctx->file_mode;
++ fattr->cf_mode |= S_IFCHR;
+ fattr->cf_dtype = DT_CHR;
+ fattr->cf_rdev = nfs_mkdev(buf);
+ break;
+ case NFS_SPECFILE_BLK:
+- fattr->cf_mode |= S_IFBLK | cifs_sb->ctx->file_mode;
++ fattr->cf_mode |= S_IFBLK;
+ fattr->cf_dtype = DT_BLK;
+ fattr->cf_rdev = nfs_mkdev(buf);
+ break;
+ case NFS_SPECFILE_FIFO:
+- fattr->cf_mode |= S_IFIFO | cifs_sb->ctx->file_mode;
++ fattr->cf_mode |= S_IFIFO;
+ fattr->cf_dtype = DT_FIFO;
+ break;
+ case NFS_SPECFILE_SOCK:
+- fattr->cf_mode |= S_IFSOCK | cifs_sb->ctx->file_mode;
++ fattr->cf_mode |= S_IFSOCK;
+ fattr->cf_dtype = DT_SOCK;
+ break;
+ case NFS_SPECFILE_LNK:
+- fattr->cf_mode = S_IFLNK | cifs_sb->ctx->file_mode;
++ fattr->cf_mode |= S_IFLNK;
+ fattr->cf_dtype = DT_LNK;
+ break;
+ default:
+@@ -770,29 +777,29 @@ bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb,
+
+ switch (tag) {
+ case IO_REPARSE_TAG_LX_SYMLINK:
+- fattr->cf_mode |= S_IFLNK | cifs_sb->ctx->file_mode;
++ fattr->cf_mode |= S_IFLNK;
+ fattr->cf_dtype = DT_LNK;
+ break;
+ case IO_REPARSE_TAG_LX_FIFO:
+- fattr->cf_mode |= S_IFIFO | cifs_sb->ctx->file_mode;
++ fattr->cf_mode |= S_IFIFO;
+ fattr->cf_dtype = DT_FIFO;
+ break;
+ case IO_REPARSE_TAG_AF_UNIX:
+- fattr->cf_mode |= S_IFSOCK | cifs_sb->ctx->file_mode;
++ fattr->cf_mode |= S_IFSOCK;
+ fattr->cf_dtype = DT_SOCK;
+ break;
+ case IO_REPARSE_TAG_LX_CHR:
+- fattr->cf_mode |= S_IFCHR | cifs_sb->ctx->file_mode;
++ fattr->cf_mode |= S_IFCHR;
+ fattr->cf_dtype = DT_CHR;
+ break;
+ case IO_REPARSE_TAG_LX_BLK:
+- fattr->cf_mode |= S_IFBLK | cifs_sb->ctx->file_mode;
++ fattr->cf_mode |= S_IFBLK;
+ fattr->cf_dtype = DT_BLK;
+ break;
+ case 0: /* SMB1 symlink */
+ case IO_REPARSE_TAG_SYMLINK:
+ case IO_REPARSE_TAG_NFS:
+- fattr->cf_mode = S_IFLNK | cifs_sb->ctx->file_mode;
++ fattr->cf_mode |= S_IFLNK;
+ fattr->cf_dtype = DT_LNK;
+ break;
+ default:
+@@ -832,6 +839,7 @@ static void cifs_open_info_to_fattr(struct cifs_fattr *fattr,
+ fattr->cf_createtime = le64_to_cpu(info->CreationTime);
+ fattr->cf_nlink = le32_to_cpu(info->NumberOfLinks);
+
++ fattr->cf_mode = cifs_sb->ctx->file_mode;
+ if (cifs_open_data_reparse(data) &&
+ cifs_reparse_point_to_fattr(cifs_sb, fattr, data))
+ goto out_reparse;
+--
+2.43.0
+
diff --git a/queue-6.6/smb-client-instantiate-when-creating-sfu-files.patch b/queue-6.6/smb-client-instantiate-when-creating-sfu-files.patch
new file mode 100644
index 0000000000..582b2ade08
--- /dev/null
+++ b/queue-6.6/smb-client-instantiate-when-creating-sfu-files.patch
@@ -0,0 +1,210 @@
+From fc8466b28484332ce30a4be5bf48e9a5a756226c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 9 Apr 2024 11:28:59 -0300
+Subject: smb: client: instantiate when creating SFU files
+
+From: Paulo Alcantara <pc@manguebit.com>
+
+[ Upstream commit c6ff459037b2e35450af2351037eac4c8aca1d6b ]
+
+In cifs_sfu_make_node(), on success, instantiate rather than leave it
+with dentry unhashed negative to support callers that expect mknod(2)
+to always instantiate.
+
+This fixes the following test case:
+
+ mount.cifs //srv/share /mnt -o ...,sfu
+ mkfifo /mnt/fifo
+ ./xfstests/ltp/growfiles -b -W test -e 1 -u -i 0 -L 30 /mnt/fifo
+ ...
+ BUG: unable to handle page fault for address: 000000034cec4e58
+ #PF: supervisor read access in kernel mode
+ #PF: error_code(0x0000) - not-present page
+ PGD 0 P4D 0
+ Oops: 0000 1 PREEMPT SMP PTI
+ CPU: 0 PID: 138098 Comm: growfiles Kdump: loaded Not tainted
+ 5.14.0-436.3987_1240945149.el9.x86_64 #1
+ Hardware name: Red Hat KVM, BIOS 0.5.1 01/01/2011
+ RIP: 0010:_raw_callee_save__kvm_vcpu_is_preempted+0x0/0x20
+ Code: e8 15 d9 61 00 e9 63 ff ff ff 41 bd ea ff ff ff e9 58 ff ff ff e8
+ d0 71 c0 00 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 <48> 8b 04
+ fd 60 2b c1 99 80 b8 90 50 03 00 00 0f 95 c0 c3 cc cc cc
+ RSP: 0018:ffffb6a143cf7cf8 EFLAGS: 00010206
+ RAX: ffff8a9bc30fb038 RBX: ffff8a9bc666a200 RCX: ffff8a9cc0260000
+ RDX: 00000000736f622e RSI: ffff8a9bc30fb038 RDI: 000000007665645f
+ RBP: ffffb6a143cf7d70 R08: 0000000000001000 R09: 0000000000000001
+ R10: 0000000000000001 R11: 0000000000000000 R12: ffff8a9bc666a200
+ R13: 0000559a302a12b0 R14: 0000000000001000 R15: 0000000000000000
+ FS: 00007fbed1dbb740(0000) GS:ffff8a9cf0000000(0000)
+ knlGS:0000000000000000
+ CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
+ CR2: 000000034cec4e58 CR3: 0000000128ec6006 CR4: 0000000000770ef0
+ DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
+ DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
+ PKRU: 55555554
+ Call Trace:
+ <TASK>
+ ? show_trace_log_lvl+0x1c4/0x2df
+ ? show_trace_log_lvl+0x1c4/0x2df
+ ? __mutex_lock.constprop.0+0x5f7/0x6a0
+ ? __die_body.cold+0x8/0xd
+ ? page_fault_oops+0x134/0x170
+ ? exc_page_fault+0x62/0x150
+ ? asm_exc_page_fault+0x22/0x30
+ ? _pfx_raw_callee_save__kvm_vcpu_is_preempted+0x10/0x10
+ __mutex_lock.constprop.0+0x5f7/0x6a0
+ ? __mod_memcg_lruvec_state+0x84/0xd0
+ pipe_write+0x47/0x650
+ ? do_anonymous_page+0x258/0x410
+ ? inode_security+0x22/0x60
+ ? selinux_file_permission+0x108/0x150
+ vfs_write+0x2cb/0x410
+ ksys_write+0x5f/0xe0
+ do_syscall_64+0x5c/0xf0
+ ? syscall_exit_to_user_mode+0x22/0x40
+ ? do_syscall_64+0x6b/0xf0
+ ? sched_clock_cpu+0x9/0xc0
+ ? exc_page_fault+0x62/0x150
+ entry_SYSCALL_64_after_hwframe+0x6e/0x76
+
+Cc: stable@vger.kernel.org
+Fixes: 72bc63f5e23a ("smb3: fix creating FIFOs when mounting with "sfu" mount option")
+Suggested-by: Al Viro <viro@zeniv.linux.org.uk>
+Signed-off-by: Paulo Alcantara (Red Hat) <pc@manguebit.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/smb2ops.c | 94 ++++++++++++++++++++++++-----------------
+ 1 file changed, 55 insertions(+), 39 deletions(-)
+
+diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
+index 981a922cf38f0..df6c6d31236ad 100644
+--- a/fs/smb/client/smb2ops.c
++++ b/fs/smb/client/smb2ops.c
+@@ -4956,68 +4956,84 @@ static int smb2_next_header(struct TCP_Server_Info *server, char *buf,
+ return 0;
+ }
+
+-int cifs_sfu_make_node(unsigned int xid, struct inode *inode,
+- struct dentry *dentry, struct cifs_tcon *tcon,
+- const char *full_path, umode_t mode, dev_t dev)
++static int __cifs_sfu_make_node(unsigned int xid, struct inode *inode,
++ struct dentry *dentry, struct cifs_tcon *tcon,
++ const char *full_path, umode_t mode, dev_t dev)
+ {
+- struct cifs_open_info_data buf = {};
+ struct TCP_Server_Info *server = tcon->ses->server;
+ struct cifs_open_parms oparms;
+ struct cifs_io_parms io_parms = {};
+ struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+ struct cifs_fid fid;
+ unsigned int bytes_written;
+- struct win_dev *pdev;
++ struct win_dev pdev = {};
+ struct kvec iov[2];
+ __u32 oplock = server->oplocks ? REQ_OPLOCK : 0;
+ int rc;
+
+- if (!S_ISCHR(mode) && !S_ISBLK(mode) && !S_ISFIFO(mode))
++ switch (mode & S_IFMT) {
++ case S_IFCHR:
++ strscpy(pdev.type, "IntxCHR", strlen("IntxChr"));
++ pdev.major = cpu_to_le64(MAJOR(dev));
++ pdev.minor = cpu_to_le64(MINOR(dev));
++ break;
++ case S_IFBLK:
++ strscpy(pdev.type, "IntxBLK", strlen("IntxBLK"));
++ pdev.major = cpu_to_le64(MAJOR(dev));
++ pdev.minor = cpu_to_le64(MINOR(dev));
++ break;
++ case S_IFIFO:
++ strscpy(pdev.type, "LnxFIFO", strlen("LnxFIFO"));
++ break;
++ default:
+ return -EPERM;
++ }
+
+- oparms = (struct cifs_open_parms) {
+- .tcon = tcon,
+- .cifs_sb = cifs_sb,
+- .desired_access = GENERIC_WRITE,
+- .create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR |
+- CREATE_OPTION_SPECIAL),
+- .disposition = FILE_CREATE,
+- .path = full_path,
+- .fid = &fid,
+- };
++ oparms = CIFS_OPARMS(cifs_sb, tcon, full_path, GENERIC_WRITE,
++ FILE_CREATE, CREATE_NOT_DIR |
++ CREATE_OPTION_SPECIAL, ACL_NO_MODE);
++ oparms.fid = &fid;
+
+- rc = server->ops->open(xid, &oparms, &oplock, &buf);
++ rc = server->ops->open(xid, &oparms, &oplock, NULL);
+ if (rc)
+ return rc;
+
+- /*
+- * BB Do not bother to decode buf since no local inode yet to put
+- * timestamps in, but we can reuse it safely.
+- */
+- pdev = (struct win_dev *)&buf.fi;
+ io_parms.pid = current->tgid;
+ io_parms.tcon = tcon;
+- io_parms.length = sizeof(*pdev);
+- iov[1].iov_base = pdev;
+- iov[1].iov_len = sizeof(*pdev);
+- if (S_ISCHR(mode)) {
+- memcpy(pdev->type, "IntxCHR", 8);
+- pdev->major = cpu_to_le64(MAJOR(dev));
+- pdev->minor = cpu_to_le64(MINOR(dev));
+- } else if (S_ISBLK(mode)) {
+- memcpy(pdev->type, "IntxBLK", 8);
+- pdev->major = cpu_to_le64(MAJOR(dev));
+- pdev->minor = cpu_to_le64(MINOR(dev));
+- } else if (S_ISFIFO(mode)) {
+- memcpy(pdev->type, "LnxFIFO", 8);
+- }
++ io_parms.length = sizeof(pdev);
++ iov[1].iov_base = &pdev;
++ iov[1].iov_len = sizeof(pdev);
+
+ rc = server->ops->sync_write(xid, &fid, &io_parms,
+ &bytes_written, iov, 1);
+ server->ops->close(xid, tcon, &fid);
+- d_drop(dentry);
+- /* FIXME: add code here to set EAs */
+- cifs_free_open_info(&buf);
++ return rc;
++}
++
++int cifs_sfu_make_node(unsigned int xid, struct inode *inode,
++ struct dentry *dentry, struct cifs_tcon *tcon,
++ const char *full_path, umode_t mode, dev_t dev)
++{
++ struct inode *new = NULL;
++ int rc;
++
++ rc = __cifs_sfu_make_node(xid, inode, dentry, tcon,
++ full_path, mode, dev);
++ if (rc)
++ return rc;
++
++ if (tcon->posix_extensions) {
++ rc = smb311_posix_get_inode_info(&new, full_path, NULL,
++ inode->i_sb, xid);
++ } else if (tcon->unix_ext) {
++ rc = cifs_get_inode_info_unix(&new, full_path,
++ inode->i_sb, xid);
++ } else {
++ rc = cifs_get_inode_info(&new, full_path, NULL,
++ inode->i_sb, xid, NULL);
++ }
++ if (!rc)
++ d_instantiate(dentry, new);
+ return rc;
+ }
+
+--
+2.43.0
+
diff --git a/queue-6.6/smb-client-introduce-cifs_sfu_make_node.patch b/queue-6.6/smb-client-introduce-cifs_sfu_make_node.patch
new file mode 100644
index 0000000000..4589dacae3
--- /dev/null
+++ b/queue-6.6/smb-client-introduce-cifs_sfu_make_node.patch
@@ -0,0 +1,283 @@
+From 1f9941732bb0953b2e28141852a18447fe9e6263 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 27 Apr 2024 09:16:18 -0500
+Subject: smb: client: introduce cifs_sfu_make_node()
+
+From: Steve French <stfrench@microsoft.com>
+
+[ Upstream commit b0348e459c836abdb0f4b967e006d15c77cf1c87 ]
+
+Remove duplicate code and add new helper for creating special files in
+SFU (Services for UNIX) format that can be shared by SMB1+ code.
+
+Signed-off-by: Paulo Alcantara (SUSE) <pc@manguebit.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/cifsproto.h | 3 ++
+ fs/smb/client/smb1ops.c | 80 ++++-------------------------------
+ fs/smb/client/smb2ops.c | 89 ++++++++++++++++++---------------------
+ 3 files changed, 52 insertions(+), 120 deletions(-)
+
+diff --git a/fs/smb/client/cifsproto.h b/fs/smb/client/cifsproto.h
+index 1bdad33580b57..9480cdb9588d5 100644
+--- a/fs/smb/client/cifsproto.h
++++ b/fs/smb/client/cifsproto.h
+@@ -673,6 +673,9 @@ char *extract_sharename(const char *unc);
+ int parse_reparse_point(struct reparse_data_buffer *buf,
+ u32 plen, struct cifs_sb_info *cifs_sb,
+ bool unicode, struct cifs_open_info_data *data);
++int cifs_sfu_make_node(unsigned int xid, struct inode *inode,
++ struct dentry *dentry, struct cifs_tcon *tcon,
++ const char *full_path, umode_t mode, dev_t dev);
+
+ #ifdef CONFIG_CIFS_DFS_UPCALL
+ static inline int get_dfs_path(const unsigned int xid, struct cifs_ses *ses,
+diff --git a/fs/smb/client/smb1ops.c b/fs/smb/client/smb1ops.c
+index 1aebcf95c1951..212ec6f66ec65 100644
+--- a/fs/smb/client/smb1ops.c
++++ b/fs/smb/client/smb1ops.c
+@@ -1041,15 +1041,7 @@ cifs_make_node(unsigned int xid, struct inode *inode,
+ {
+ struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+ struct inode *newinode = NULL;
+- int rc = -EPERM;
+- struct cifs_open_info_data buf = {};
+- struct cifs_io_parms io_parms;
+- __u32 oplock = 0;
+- struct cifs_fid fid;
+- struct cifs_open_parms oparms;
+- unsigned int bytes_written;
+- struct win_dev *pdev;
+- struct kvec iov[2];
++ int rc;
+
+ if (tcon->unix_ext) {
+ /*
+@@ -1083,74 +1075,18 @@ cifs_make_node(unsigned int xid, struct inode *inode,
+ d_instantiate(dentry, newinode);
+ return rc;
+ }
+-
+ /*
+- * SMB1 SFU emulation: should work with all servers, but only
+- * support block and char device (no socket & fifo)
++ * Check if mounted with mount parm 'sfu' mount parm.
++ * SFU emulation should work with all servers, but only
++ * supports block and char device (no socket & fifo),
++ * and was used by default in earlier versions of Windows
+ */
+ if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL))
+- return rc;
+-
+- if (!S_ISCHR(mode) && !S_ISBLK(mode))
+- return rc;
+-
+- cifs_dbg(FYI, "sfu compat create special file\n");
+-
+- oparms = (struct cifs_open_parms) {
+- .tcon = tcon,
+- .cifs_sb = cifs_sb,
+- .desired_access = GENERIC_WRITE,
+- .create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR |
+- CREATE_OPTION_SPECIAL),
+- .disposition = FILE_CREATE,
+- .path = full_path,
+- .fid = &fid,
+- };
+-
+- if (tcon->ses->server->oplocks)
+- oplock = REQ_OPLOCK;
+- else
+- oplock = 0;
+- rc = tcon->ses->server->ops->open(xid, &oparms, &oplock, &buf);
+- if (rc)
+- return rc;
+-
+- /*
+- * BB Do not bother to decode buf since no local inode yet to put
+- * timestamps in, but we can reuse it safely.
+- */
+-
+- pdev = (struct win_dev *)&buf.fi;
+- io_parms.pid = current->tgid;
+- io_parms.tcon = tcon;
+- io_parms.offset = 0;
+- io_parms.length = sizeof(struct win_dev);
+- iov[1].iov_base = &buf.fi;
+- iov[1].iov_len = sizeof(struct win_dev);
+- if (S_ISCHR(mode)) {
+- memcpy(pdev->type, "IntxCHR", 8);
+- pdev->major = cpu_to_le64(MAJOR(dev));
+- pdev->minor = cpu_to_le64(MINOR(dev));
+- rc = tcon->ses->server->ops->sync_write(xid, &fid, &io_parms,
+- &bytes_written, iov, 1);
+- } else if (S_ISBLK(mode)) {
+- memcpy(pdev->type, "IntxBLK", 8);
+- pdev->major = cpu_to_le64(MAJOR(dev));
+- pdev->minor = cpu_to_le64(MINOR(dev));
+- rc = tcon->ses->server->ops->sync_write(xid, &fid, &io_parms,
+- &bytes_written, iov, 1);
+- }
+- tcon->ses->server->ops->close(xid, tcon, &fid);
+- d_drop(dentry);
+-
+- /* FIXME: add code here to set EAs */
+-
+- cifs_free_open_info(&buf);
+- return rc;
++ return -EPERM;
++ return cifs_sfu_make_node(xid, inode, dentry, tcon,
++ full_path, mode, dev);
+ }
+
+-
+-
+ struct smb_version_operations smb1_operations = {
+ .send_cancel = send_nt_cancel,
+ .compare_fids = cifs_compare_fids,
+diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
+index 04fea874d0a33..2b892a736e5f9 100644
+--- a/fs/smb/client/smb2ops.c
++++ b/fs/smb/client/smb2ops.c
+@@ -5105,41 +5105,24 @@ static int smb2_next_header(struct TCP_Server_Info *server, char *buf,
+ return 0;
+ }
+
+-static int
+-smb2_make_node(unsigned int xid, struct inode *inode,
+- struct dentry *dentry, struct cifs_tcon *tcon,
+- const char *full_path, umode_t mode, dev_t dev)
++int cifs_sfu_make_node(unsigned int xid, struct inode *inode,
++ struct dentry *dentry, struct cifs_tcon *tcon,
++ const char *full_path, umode_t mode, dev_t dev)
+ {
+- struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+- int rc = -EPERM;
+ struct cifs_open_info_data buf = {};
+- struct cifs_io_parms io_parms = {0};
+- __u32 oplock = 0;
+- struct cifs_fid fid;
++ struct TCP_Server_Info *server = tcon->ses->server;
+ struct cifs_open_parms oparms;
++ struct cifs_io_parms io_parms = {};
++ struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
++ struct cifs_fid fid;
+ unsigned int bytes_written;
+ struct win_dev *pdev;
+ struct kvec iov[2];
+-
+- /*
+- * Check if mounted with mount parm 'sfu' mount parm.
+- * SFU emulation should work with all servers, but only
+- * supports block and char device (no socket & fifo),
+- * and was used by default in earlier versions of Windows
+- */
+- if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL))
+- return rc;
+-
+- /*
+- * TODO: Add ability to create instead via reparse point. Windows (e.g.
+- * their current NFS server) uses this approach to expose special files
+- * over SMB2/SMB3 and Samba will do this with SMB3.1.1 POSIX Extensions
+- */
++ __u32 oplock = server->oplocks ? REQ_OPLOCK : 0;
++ int rc;
+
+ if (!S_ISCHR(mode) && !S_ISBLK(mode) && !S_ISFIFO(mode))
+- return rc;
+-
+- cifs_dbg(FYI, "sfu compat create special file\n");
++ return -EPERM;
+
+ oparms = (struct cifs_open_parms) {
+ .tcon = tcon,
+@@ -5152,11 +5135,7 @@ smb2_make_node(unsigned int xid, struct inode *inode,
+ .fid = &fid,
+ };
+
+- if (tcon->ses->server->oplocks)
+- oplock = REQ_OPLOCK;
+- else
+- oplock = 0;
+- rc = tcon->ses->server->ops->open(xid, &oparms, &oplock, &buf);
++ rc = server->ops->open(xid, &oparms, &oplock, &buf);
+ if (rc)
+ return rc;
+
+@@ -5164,42 +5143,56 @@ smb2_make_node(unsigned int xid, struct inode *inode,
+ * BB Do not bother to decode buf since no local inode yet to put
+ * timestamps in, but we can reuse it safely.
+ */
+-
+ pdev = (struct win_dev *)&buf.fi;
+ io_parms.pid = current->tgid;
+ io_parms.tcon = tcon;
+- io_parms.offset = 0;
+- io_parms.length = sizeof(struct win_dev);
+- iov[1].iov_base = &buf.fi;
+- iov[1].iov_len = sizeof(struct win_dev);
++ io_parms.length = sizeof(*pdev);
++ iov[1].iov_base = pdev;
++ iov[1].iov_len = sizeof(*pdev);
+ if (S_ISCHR(mode)) {
+ memcpy(pdev->type, "IntxCHR", 8);
+ pdev->major = cpu_to_le64(MAJOR(dev));
+ pdev->minor = cpu_to_le64(MINOR(dev));
+- rc = tcon->ses->server->ops->sync_write(xid, &fid, &io_parms,
+- &bytes_written, iov, 1);
+ } else if (S_ISBLK(mode)) {
+ memcpy(pdev->type, "IntxBLK", 8);
+ pdev->major = cpu_to_le64(MAJOR(dev));
+ pdev->minor = cpu_to_le64(MINOR(dev));
+- rc = tcon->ses->server->ops->sync_write(xid, &fid, &io_parms,
+- &bytes_written, iov, 1);
+ } else if (S_ISFIFO(mode)) {
+ memcpy(pdev->type, "LnxFIFO", 8);
+- pdev->major = 0;
+- pdev->minor = 0;
+- rc = tcon->ses->server->ops->sync_write(xid, &fid, &io_parms,
+- &bytes_written, iov, 1);
+ }
+- tcon->ses->server->ops->close(xid, tcon, &fid);
+- d_drop(dentry);
+
++ rc = server->ops->sync_write(xid, &fid, &io_parms,
++ &bytes_written, iov, 1);
++ server->ops->close(xid, tcon, &fid);
++ d_drop(dentry);
+ /* FIXME: add code here to set EAs */
+-
+ cifs_free_open_info(&buf);
+ return rc;
+ }
+
++static int smb2_make_node(unsigned int xid, struct inode *inode,
++ struct dentry *dentry, struct cifs_tcon *tcon,
++ const char *full_path, umode_t mode, dev_t dev)
++{
++ struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
++
++ /*
++ * Check if mounted with mount parm 'sfu' mount parm.
++ * SFU emulation should work with all servers, but only
++ * supports block and char device (no socket & fifo),
++ * and was used by default in earlier versions of Windows
++ */
++ if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL))
++ return -EPERM;
++ /*
++ * TODO: Add ability to create instead via reparse point. Windows (e.g.
++ * their current NFS server) uses this approach to expose special files
++ * over SMB2/SMB3 and Samba will do this with SMB3.1.1 POSIX Extensions
++ */
++ return cifs_sfu_make_node(xid, inode, dentry, tcon,
++ full_path, mode, dev);
++}
++
+ #ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
+ struct smb_version_operations smb20_operations = {
+ .compare_fids = smb2_compare_fids,
+--
+2.43.0
+
diff --git a/queue-6.6/smb-client-introduce-reparse-mount-option.patch b/queue-6.6/smb-client-introduce-reparse-mount-option.patch
new file mode 100644
index 0000000000..3914990c93
--- /dev/null
+++ b/queue-6.6/smb-client-introduce-reparse-mount-option.patch
@@ -0,0 +1,161 @@
+From c51945f7d33c5e8028723321ada219432dc79e5b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 21 Jan 2024 13:28:21 -0300
+Subject: smb: client: introduce reparse mount option
+
+From: Paulo Alcantara <pc@manguebit.com>
+
+[ Upstream commit eb90e8ecb2b54ac1af51e28596e0ef7ba351476d ]
+
+Allow the user to create special files and symlinks by choosing
+between WSL and NFS reparse points via 'reparse={nfs,wsl}' mount
+options. If unset or 'reparse=default', the client will default to
+creating them via NFS reparse points.
+
+Creating WSL reparse points isn't supported yet, so simply return
+error when attempting to mount with 'reparse=wsl' for now.
+
+Signed-off-by: Paulo Alcantara <pc@manguebit.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/cifsglob.h | 6 ++++++
+ fs/smb/client/connect.c | 2 ++
+ fs/smb/client/fs_context.c | 35 +++++++++++++++++++++++++++++++++++
+ fs/smb/client/fs_context.h | 9 +++++++++
+ 4 files changed, 52 insertions(+)
+
+diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h
+index fdadda4024f46..08540541046c1 100644
+--- a/fs/smb/client/cifsglob.h
++++ b/fs/smb/client/cifsglob.h
+@@ -153,6 +153,12 @@ enum securityEnum {
+ Kerberos, /* Kerberos via SPNEGO */
+ };
+
++enum cifs_reparse_type {
++ CIFS_REPARSE_TYPE_NFS,
++ CIFS_REPARSE_TYPE_WSL,
++ CIFS_REPARSE_TYPE_DEFAULT = CIFS_REPARSE_TYPE_NFS,
++};
++
+ struct session_key {
+ unsigned int len;
+ char *response;
+diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c
+index e28f011f11d6c..deba1cfd11801 100644
+--- a/fs/smb/client/connect.c
++++ b/fs/smb/client/connect.c
+@@ -2805,6 +2805,8 @@ compare_mount_options(struct super_block *sb, struct cifs_mnt_data *mnt_data)
+ return 0;
+ if (old->ctx->closetimeo != new->ctx->closetimeo)
+ return 0;
++ if (old->ctx->reparse_type != new->ctx->reparse_type)
++ return 0;
+
+ return 1;
+ }
+diff --git a/fs/smb/client/fs_context.c b/fs/smb/client/fs_context.c
+index f119035a82725..535885fbdf51d 100644
+--- a/fs/smb/client/fs_context.c
++++ b/fs/smb/client/fs_context.c
+@@ -175,6 +175,7 @@ const struct fs_parameter_spec smb3_fs_parameters[] = {
+ fsparam_string("vers", Opt_vers),
+ fsparam_string("sec", Opt_sec),
+ fsparam_string("cache", Opt_cache),
++ fsparam_string("reparse", Opt_reparse),
+
+ /* Arguments that should be ignored */
+ fsparam_flag("guest", Opt_ignore),
+@@ -297,6 +298,35 @@ cifs_parse_cache_flavor(struct fs_context *fc, char *value, struct smb3_fs_conte
+ return 0;
+ }
+
++static const match_table_t reparse_flavor_tokens = {
++ { Opt_reparse_default, "default" },
++ { Opt_reparse_nfs, "nfs" },
++ { Opt_reparse_wsl, "wsl" },
++ { Opt_reparse_err, NULL },
++};
++
++static int parse_reparse_flavor(struct fs_context *fc, char *value,
++ struct smb3_fs_context *ctx)
++{
++ substring_t args[MAX_OPT_ARGS];
++
++ switch (match_token(value, reparse_flavor_tokens, args)) {
++ case Opt_reparse_default:
++ ctx->reparse_type = CIFS_REPARSE_TYPE_DEFAULT;
++ break;
++ case Opt_reparse_nfs:
++ ctx->reparse_type = CIFS_REPARSE_TYPE_NFS;
++ break;
++ case Opt_reparse_wsl:
++ cifs_errorf(fc, "unsupported reparse= option: %s\n", value);
++ return 1;
++ default:
++ cifs_errorf(fc, "bad reparse= option: %s\n", value);
++ return 1;
++ }
++ return 0;
++}
++
+ #define DUP_CTX_STR(field) \
+ do { \
+ if (ctx->field) { \
+@@ -1595,6 +1625,10 @@ static int smb3_fs_context_parse_param(struct fs_context *fc,
+ case Opt_rdma:
+ ctx->rdma = true;
+ break;
++ case Opt_reparse:
++ if (parse_reparse_flavor(fc, param->string, ctx))
++ goto cifs_parse_mount_err;
++ break;
+ }
+ /* case Opt_ignore: - is ignored as expected ... */
+
+@@ -1683,6 +1717,7 @@ int smb3_init_fs_context(struct fs_context *fc)
+ ctx->backupgid_specified = false; /* no backup intent for a group */
+
+ ctx->retrans = 1;
++ ctx->reparse_type = CIFS_REPARSE_TYPE_DEFAULT;
+
+ /*
+ * short int override_uid = -1;
+diff --git a/fs/smb/client/fs_context.h b/fs/smb/client/fs_context.h
+index 369a3fea1dfe0..e77ee81846b31 100644
+--- a/fs/smb/client/fs_context.h
++++ b/fs/smb/client/fs_context.h
+@@ -41,6 +41,13 @@ enum {
+ Opt_cache_err
+ };
+
++enum cifs_reparse_parm {
++ Opt_reparse_default,
++ Opt_reparse_nfs,
++ Opt_reparse_wsl,
++ Opt_reparse_err
++};
++
+ enum cifs_sec_param {
+ Opt_sec_krb5,
+ Opt_sec_krb5i,
+@@ -149,6 +156,7 @@ enum cifs_param {
+ Opt_vers,
+ Opt_sec,
+ Opt_cache,
++ Opt_reparse,
+
+ /* Mount options to be ignored */
+ Opt_ignore,
+@@ -275,6 +283,7 @@ struct smb3_fs_context {
+ char *leaf_fullpath;
+ struct cifs_ses *dfs_root_ses;
+ bool dfs_automount:1; /* set for dfs automount only */
++ enum cifs_reparse_type reparse_type;
+ };
+
+ extern const struct fs_parameter_spec smb3_fs_parameters[];
+--
+2.43.0
+
diff --git a/queue-6.6/smb-client-introduce-smb2_op_query_wsl_ea.patch b/queue-6.6/smb-client-introduce-smb2_op_query_wsl_ea.patch
new file mode 100644
index 0000000000..e97b45050d
--- /dev/null
+++ b/queue-6.6/smb-client-introduce-smb2_op_query_wsl_ea.patch
@@ -0,0 +1,384 @@
+From afb9ac6f566cf37556665f232b2a38b3ae8ec6cb Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 1 May 2024 01:01:06 -0500
+Subject: smb: client: introduce SMB2_OP_QUERY_WSL_EA
+
+From: Steve French <stfrench@microsoft.com>
+
+[ Upstream commit ea41367b2a602f602ea6594fc4a310520dcc64f4 ]
+
+Add a new command to smb2_compound_op() for querying WSL extended
+attributes from reparse points.
+
+Signed-off-by: Paulo Alcantara <pc@manguebit.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/cifsglob.h | 5 ++
+ fs/smb/client/reparse.c | 10 +--
+ fs/smb/client/smb2glob.h | 3 +-
+ fs/smb/client/smb2inode.c | 170 +++++++++++++++++++++++++++++++++-----
+ fs/smb/client/smb2pdu.h | 25 ++++++
+ fs/smb/client/trace.h | 2 +
+ 6 files changed, 190 insertions(+), 25 deletions(-)
+
+diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h
+index 54758825fa8d9..ddb64af50a45d 100644
+--- a/fs/smb/client/cifsglob.h
++++ b/fs/smb/client/cifsglob.h
+@@ -214,6 +214,10 @@ struct cifs_open_info_data {
+ struct reparse_posix_data *posix;
+ };
+ } reparse;
++ struct {
++ __u8 eas[SMB2_WSL_MAX_QUERY_EA_RESP_SIZE];
++ unsigned int eas_len;
++ } wsl;
+ char *symlink_target;
+ struct cifs_sid posix_owner;
+ struct cifs_sid posix_group;
+@@ -2304,6 +2308,7 @@ struct smb2_compound_vars {
+ struct kvec close_iov;
+ struct smb2_file_rename_info rename_info;
+ struct smb2_file_link_info link_info;
++ struct kvec ea_iov;
+ };
+
+ static inline bool cifs_ses_exiting(struct cifs_ses *ses)
+diff --git a/fs/smb/client/reparse.c b/fs/smb/client/reparse.c
+index 24feeaa32280e..e8be756e6768c 100644
+--- a/fs/smb/client/reparse.c
++++ b/fs/smb/client/reparse.c
+@@ -205,15 +205,15 @@ static int wsl_set_xattrs(struct inode *inode, umode_t _mode,
+ __le64 dev = cpu_to_le64(((u64)MINOR(_dev) << 32) | MAJOR(_dev));
+ __le64 mode = cpu_to_le64(_mode);
+ struct wsl_xattr xattrs[] = {
+- { .name = "$LXUID", .value = uid, .size = 4, },
+- { .name = "$LXGID", .value = gid, .size = 4, },
+- { .name = "$LXMOD", .value = mode, .size = 4, },
+- { .name = "$LXDEV", .value = dev, .size = 8, },
++ { .name = SMB2_WSL_XATTR_UID, .value = uid, .size = SMB2_WSL_XATTR_UID_SIZE, },
++ { .name = SMB2_WSL_XATTR_GID, .value = gid, .size = SMB2_WSL_XATTR_GID_SIZE, },
++ { .name = SMB2_WSL_XATTR_MODE, .value = mode, .size = SMB2_WSL_XATTR_MODE_SIZE, },
++ { .name = SMB2_WSL_XATTR_DEV, .value = dev, .size = SMB2_WSL_XATTR_DEV_SIZE, },
+ };
+ size_t cc_len;
+ u32 dlen = 0, next = 0;
+ int i, num_xattrs;
+- u8 name_size = strlen(xattrs[0].name) + 1;
++ u8 name_size = SMB2_WSL_XATTR_NAME_LEN + 1;
+
+ memset(iov, 0, sizeof(*iov));
+
+diff --git a/fs/smb/client/smb2glob.h b/fs/smb/client/smb2glob.h
+index a0c156996fc51..2466e61551369 100644
+--- a/fs/smb/client/smb2glob.h
++++ b/fs/smb/client/smb2glob.h
+@@ -36,7 +36,8 @@ enum smb2_compound_ops {
+ SMB2_OP_RMDIR,
+ SMB2_OP_POSIX_QUERY_INFO,
+ SMB2_OP_SET_REPARSE,
+- SMB2_OP_GET_REPARSE
++ SMB2_OP_GET_REPARSE,
++ SMB2_OP_QUERY_WSL_EA,
+ };
+
+ /* Used when constructing chained read requests. */
+diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c
+index 0e1b9a1552e71..86f8c81791374 100644
+--- a/fs/smb/client/smb2inode.c
++++ b/fs/smb/client/smb2inode.c
+@@ -85,6 +85,82 @@ static int parse_posix_sids(struct cifs_open_info_data *data,
+ return 0;
+ }
+
++struct wsl_query_ea {
++ __le32 next;
++ __u8 name_len;
++ __u8 name[SMB2_WSL_XATTR_NAME_LEN + 1];
++} __packed;
++
++#define NEXT_OFF cpu_to_le32(sizeof(struct wsl_query_ea))
++
++static const struct wsl_query_ea wsl_query_eas[] = {
++ { .next = NEXT_OFF, .name_len = SMB2_WSL_XATTR_NAME_LEN, .name = SMB2_WSL_XATTR_UID, },
++ { .next = NEXT_OFF, .name_len = SMB2_WSL_XATTR_NAME_LEN, .name = SMB2_WSL_XATTR_GID, },
++ { .next = NEXT_OFF, .name_len = SMB2_WSL_XATTR_NAME_LEN, .name = SMB2_WSL_XATTR_MODE, },
++ { .next = 0, .name_len = SMB2_WSL_XATTR_NAME_LEN, .name = SMB2_WSL_XATTR_DEV, },
++};
++
++static int check_wsl_eas(struct kvec *rsp_iov)
++{
++ struct smb2_file_full_ea_info *ea;
++ struct smb2_query_info_rsp *rsp = rsp_iov->iov_base;
++ unsigned long addr;
++ u32 outlen, next;
++ u16 vlen;
++ u8 nlen;
++ u8 *end;
++
++ outlen = le32_to_cpu(rsp->OutputBufferLength);
++ if (outlen < SMB2_WSL_MIN_QUERY_EA_RESP_SIZE ||
++ outlen > SMB2_WSL_MAX_QUERY_EA_RESP_SIZE)
++ return -EINVAL;
++
++ ea = (void *)((u8 *)rsp_iov->iov_base +
++ le16_to_cpu(rsp->OutputBufferOffset));
++ end = (u8 *)rsp_iov->iov_base + rsp_iov->iov_len;
++ for (;;) {
++ if ((u8 *)ea > end - sizeof(*ea))
++ return -EINVAL;
++
++ nlen = ea->ea_name_length;
++ vlen = le16_to_cpu(ea->ea_value_length);
++ if (nlen != SMB2_WSL_XATTR_NAME_LEN ||
++ (u8 *)ea + nlen + 1 + vlen > end)
++ return -EINVAL;
++
++ switch (vlen) {
++ case 4:
++ if (strncmp(ea->ea_data, SMB2_WSL_XATTR_UID, nlen) &&
++ strncmp(ea->ea_data, SMB2_WSL_XATTR_GID, nlen) &&
++ strncmp(ea->ea_data, SMB2_WSL_XATTR_MODE, nlen))
++ return -EINVAL;
++ break;
++ case 8:
++ if (strncmp(ea->ea_data, SMB2_WSL_XATTR_DEV, nlen))
++ return -EINVAL;
++ break;
++ case 0:
++ if (!strncmp(ea->ea_data, SMB2_WSL_XATTR_UID, nlen) ||
++ !strncmp(ea->ea_data, SMB2_WSL_XATTR_GID, nlen) ||
++ !strncmp(ea->ea_data, SMB2_WSL_XATTR_MODE, nlen) ||
++ !strncmp(ea->ea_data, SMB2_WSL_XATTR_DEV, nlen))
++ break;
++ fallthrough;
++ default:
++ return -EINVAL;
++ }
++
++ next = le32_to_cpu(ea->next_entry_offset);
++ if (!next)
++ break;
++ if (!IS_ALIGNED(next, 4) ||
++ check_add_overflow((unsigned long)ea, next, &addr))
++ return -EINVAL;
++ ea = (void *)addr;
++ }
++ return 0;
++}
++
+ /*
+ * note: If cfile is passed, the reference to it is dropped here.
+ * So make sure that you do not reuse cfile after return from this func.
+@@ -119,7 +195,7 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
+ __u8 delete_pending[8] = {1, 0, 0, 0, 0, 0, 0, 0};
+ unsigned int size[2];
+ void *data[2];
+- int len;
++ unsigned int len;
+ int retries = 0, cur_sleep = 1;
+
+ replay_again:
+@@ -476,6 +552,39 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
+ trace_smb3_get_reparse_compound_enter(xid, ses->Suid,
+ tcon->tid, full_path);
+ break;
++ case SMB2_OP_QUERY_WSL_EA:
++ rqst[num_rqst].rq_iov = &vars->ea_iov;
++ rqst[num_rqst].rq_nvec = 1;
++
++ if (cfile) {
++ rc = SMB2_query_info_init(tcon, server,
++ &rqst[num_rqst],
++ cfile->fid.persistent_fid,
++ cfile->fid.volatile_fid,
++ FILE_FULL_EA_INFORMATION,
++ SMB2_O_INFO_FILE, 0,
++ SMB2_WSL_MAX_QUERY_EA_RESP_SIZE,
++ sizeof(wsl_query_eas),
++ (void *)wsl_query_eas);
++ } else {
++ rc = SMB2_query_info_init(tcon, server,
++ &rqst[num_rqst],
++ COMPOUND_FID,
++ COMPOUND_FID,
++ FILE_FULL_EA_INFORMATION,
++ SMB2_O_INFO_FILE, 0,
++ SMB2_WSL_MAX_QUERY_EA_RESP_SIZE,
++ sizeof(wsl_query_eas),
++ (void *)wsl_query_eas);
++ }
++ if (!rc && (!cfile || num_rqst > 1)) {
++ smb2_set_next_command(tcon, &rqst[num_rqst]);
++ smb2_set_related(&rqst[num_rqst]);
++ } else if (rc) {
++ goto finished;
++ }
++ num_rqst++;
++ break;
+ default:
+ cifs_dbg(VFS, "Invalid command\n");
+ rc = -EINVAL;
+@@ -665,11 +774,32 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
+ memset(iov, 0, sizeof(*iov));
+ resp_buftype[i + 1] = CIFS_NO_BUFFER;
+ } else {
+- trace_smb3_set_reparse_compound_err(xid, ses->Suid,
++ trace_smb3_set_reparse_compound_err(xid, ses->Suid,
+ tcon->tid, rc);
+ }
+ SMB2_ioctl_free(&rqst[num_rqst++]);
+ break;
++ case SMB2_OP_QUERY_WSL_EA:
++ if (!rc) {
++ idata = in_iov[i].iov_base;
++ qi_rsp = rsp_iov[i + 1].iov_base;
++ data[0] = (u8 *)qi_rsp + le16_to_cpu(qi_rsp->OutputBufferOffset);
++ size[0] = le32_to_cpu(qi_rsp->OutputBufferLength);
++ rc = check_wsl_eas(&rsp_iov[i + 1]);
++ if (!rc) {
++ memcpy(idata->wsl.eas, data[0], size[0]);
++ idata->wsl.eas_len = size[0];
++ }
++ }
++ if (!rc) {
++ trace_smb3_query_wsl_ea_compound_done(xid, ses->Suid,
++ tcon->tid);
++ } else {
++ trace_smb3_query_wsl_ea_compound_err(xid, ses->Suid,
++ tcon->tid, rc);
++ }
++ SMB2_query_info_free(&rqst[num_rqst++]);
++ break;
+ }
+ }
+ SMB2_close_free(&rqst[num_rqst]);
+@@ -737,11 +867,11 @@ int smb2_query_path_info(const unsigned int xid,
+ struct cifsFileInfo *cfile;
+ struct cached_fid *cfid = NULL;
+ struct smb2_hdr *hdr;
+- struct kvec in_iov[2], out_iov[3] = {};
++ struct kvec in_iov[3], out_iov[3] = {};
+ int out_buftype[3] = {};
+- int cmds[2];
++ int cmds[3];
+ bool islink;
+- int i, num_cmds;
++ int i, num_cmds = 0;
+ int rc, rc2;
+
+ data->adjust_tz = false;
+@@ -774,21 +904,22 @@ int smb2_query_path_info(const unsigned int xid,
+ close_cached_dir(cfid);
+ return rc;
+ }
+- cmds[0] = SMB2_OP_QUERY_INFO;
++ cmds[num_cmds++] = SMB2_OP_QUERY_INFO;
+ } else {
+- cmds[0] = SMB2_OP_POSIX_QUERY_INFO;
++ cmds[num_cmds++] = SMB2_OP_POSIX_QUERY_INFO;
+ }
+
+ in_iov[0].iov_base = data;
+ in_iov[0].iov_len = sizeof(*data);
+ in_iov[1] = in_iov[0];
++ in_iov[2] = in_iov[0];
+
+ cifs_get_readable_path(tcon, full_path, &cfile);
+ oparms = CIFS_OPARMS(cifs_sb, tcon, full_path, FILE_READ_ATTRIBUTES,
+ FILE_OPEN, create_options, ACL_NO_MODE);
+ rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
+- &oparms, in_iov, cmds, 1, cfile,
+- out_iov, out_buftype, NULL);
++ &oparms, in_iov, cmds, num_cmds,
++ cfile, out_iov, out_buftype, NULL);
+ hdr = out_iov[0].iov_base;
+ /*
+ * If first iov is unset, then SMB session was dropped or we've got a
+@@ -808,17 +939,18 @@ int smb2_query_path_info(const unsigned int xid,
+ if (rc || !data->reparse_point)
+ goto out;
+
+- if (data->reparse.tag == IO_REPARSE_TAG_SYMLINK) {
+- /* symlink already parsed in create response */
+- num_cmds = 1;
+- } else {
+- cmds[1] = SMB2_OP_GET_REPARSE;
+- num_cmds = 2;
+- }
++ cmds[num_cmds++] = SMB2_OP_QUERY_WSL_EA;
++ /*
++ * Skip SMB2_OP_GET_REPARSE if symlink already parsed in create
++ * response.
++ */
++ if (data->reparse.tag != IO_REPARSE_TAG_SYMLINK)
++ cmds[num_cmds++] = SMB2_OP_GET_REPARSE;
++
+ oparms = CIFS_OPARMS(cifs_sb, tcon, full_path,
+- FILE_READ_ATTRIBUTES, FILE_OPEN,
+- create_options | OPEN_REPARSE_POINT,
+- ACL_NO_MODE);
++ FILE_READ_ATTRIBUTES | FILE_READ_EA,
++ FILE_OPEN, create_options |
++ OPEN_REPARSE_POINT, ACL_NO_MODE);
+ cifs_get_readable_path(tcon, full_path, &cfile);
+ rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
+ &oparms, in_iov, cmds, num_cmds,
+diff --git a/fs/smb/client/smb2pdu.h b/fs/smb/client/smb2pdu.h
+index 3334f2c364fb9..2fccf0d4f53d2 100644
+--- a/fs/smb/client/smb2pdu.h
++++ b/fs/smb/client/smb2pdu.h
+@@ -420,4 +420,29 @@ struct smb2_create_ea_ctx {
+ struct smb2_file_full_ea_info ea;
+ } __packed;
+
++#define SMB2_WSL_XATTR_UID "$LXUID"
++#define SMB2_WSL_XATTR_GID "$LXGID"
++#define SMB2_WSL_XATTR_MODE "$LXMOD"
++#define SMB2_WSL_XATTR_DEV "$LXDEV"
++#define SMB2_WSL_XATTR_NAME_LEN 6
++#define SMB2_WSL_NUM_XATTRS 4
++
++#define SMB2_WSL_XATTR_UID_SIZE 4
++#define SMB2_WSL_XATTR_GID_SIZE 4
++#define SMB2_WSL_XATTR_MODE_SIZE 4
++#define SMB2_WSL_XATTR_DEV_SIZE 8
++
++#define SMB2_WSL_MIN_QUERY_EA_RESP_SIZE \
++ (ALIGN((SMB2_WSL_NUM_XATTRS - 1) * \
++ (SMB2_WSL_XATTR_NAME_LEN + 1 + \
++ sizeof(struct smb2_file_full_ea_info)), 4) + \
++ SMB2_WSL_XATTR_NAME_LEN + 1 + sizeof(struct smb2_file_full_ea_info))
++
++#define SMB2_WSL_MAX_QUERY_EA_RESP_SIZE \
++ (ALIGN(SMB2_WSL_MIN_QUERY_EA_RESP_SIZE + \
++ SMB2_WSL_XATTR_UID_SIZE + \
++ SMB2_WSL_XATTR_GID_SIZE + \
++ SMB2_WSL_XATTR_MODE_SIZE + \
++ SMB2_WSL_XATTR_DEV_SIZE, 4))
++
+ #endif /* _SMB2PDU_H */
+diff --git a/fs/smb/client/trace.h b/fs/smb/client/trace.h
+index 522fa387fcfd7..ce90ae0d77f84 100644
+--- a/fs/smb/client/trace.h
++++ b/fs/smb/client/trace.h
+@@ -411,6 +411,7 @@ DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(set_eof_done);
+ DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(set_info_compound_done);
+ DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(set_reparse_compound_done);
+ DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(get_reparse_compound_done);
++DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(query_wsl_ea_compound_done);
+ DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(delete_done);
+ DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(mkdir_done);
+ DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(tdis_done);
+@@ -456,6 +457,7 @@ DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(set_eof_err);
+ DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(set_info_compound_err);
+ DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(set_reparse_compound_err);
+ DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(get_reparse_compound_err);
++DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(query_wsl_ea_compound_err);
+ DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(mkdir_err);
+ DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(delete_err);
+ DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(tdis_err);
+--
+2.43.0
+
diff --git a/queue-6.6/smb-client-move-most-of-reparse-point-handling-code-.patch b/queue-6.6/smb-client-move-most-of-reparse-point-handling-code-.patch
new file mode 100644
index 0000000000..328c8681ee
--- /dev/null
+++ b/queue-6.6/smb-client-move-most-of-reparse-point-handling-code-.patch
@@ -0,0 +1,924 @@
+From dcd2e2d7da6342b1ad248db0514e7177fafa64b7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 5 Mar 2024 23:28:48 -0600
+Subject: smb: client: move most of reparse point handling code to common file
+
+From: Paulo Alcantara <pc@manguebit.com>
+
+[ Upstream commit c520ba7573a84bd37f8803a3beeb8f6f995bf9e1 ]
+
+In preparation to add support for creating special files also via WSL
+reparse points in next commits.
+
+Signed-off-by: Paulo Alcantara <pc@manguebit.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/Makefile | 2 +-
+ fs/smb/client/cifsglob.h | 13 --
+ fs/smb/client/cifsproto.h | 4 -
+ fs/smb/client/inode.c | 79 +---------
+ fs/smb/client/readdir.c | 18 +--
+ fs/smb/client/reparse.c | 320 ++++++++++++++++++++++++++++++++++++++
+ fs/smb/client/reparse.h | 73 +++++++++
+ fs/smb/client/smb2ops.c | 254 +-----------------------------
+ fs/smb/client/smb2proto.h | 6 +
+ 9 files changed, 405 insertions(+), 364 deletions(-)
+ create mode 100644 fs/smb/client/reparse.c
+ create mode 100644 fs/smb/client/reparse.h
+
+diff --git a/fs/smb/client/Makefile b/fs/smb/client/Makefile
+index 0b07eb94c93b3..e11985f2460b2 100644
+--- a/fs/smb/client/Makefile
++++ b/fs/smb/client/Makefile
+@@ -12,7 +12,7 @@ cifs-y := trace.o cifsfs.o cifs_debug.o connect.o dir.o file.o \
+ smb2ops.o smb2maperror.o smb2transport.o \
+ smb2misc.o smb2pdu.o smb2inode.o smb2file.o cifsacl.o fs_context.o \
+ dns_resolve.o cifs_spnego_negtokeninit.asn1.o asn1.o \
+- namespace.o
++ namespace.o reparse.o
+
+ $(obj)/asn1.o: $(obj)/cifs_spnego_negtokeninit.asn1.h
+
+diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h
+index 08540541046c1..296ed556be0e2 100644
+--- a/fs/smb/client/cifsglob.h
++++ b/fs/smb/client/cifsglob.h
+@@ -223,19 +223,6 @@ struct cifs_open_info_data {
+ };
+ };
+
+-static inline bool cifs_open_data_reparse(struct cifs_open_info_data *data)
+-{
+- struct smb2_file_all_info *fi = &data->fi;
+- u32 attrs = le32_to_cpu(fi->Attributes);
+- bool ret;
+-
+- ret = data->reparse_point || (attrs & ATTR_REPARSE);
+- if (ret)
+- attrs |= ATTR_REPARSE;
+- fi->Attributes = cpu_to_le32(attrs);
+- return ret;
+-}
+-
+ /*
+ *****************************************************************
+ * Except the CIFS PDUs themselves all the
+diff --git a/fs/smb/client/cifsproto.h b/fs/smb/client/cifsproto.h
+index 50040990e70b9..8e0a348f1f660 100644
+--- a/fs/smb/client/cifsproto.h
++++ b/fs/smb/client/cifsproto.h
+@@ -210,10 +210,6 @@ extern struct inode *cifs_iget(struct super_block *sb,
+ int cifs_get_inode_info(struct inode **inode, const char *full_path,
+ struct cifs_open_info_data *data, struct super_block *sb, int xid,
+ const struct cifs_fid *fid);
+-bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb,
+- struct cifs_fattr *fattr,
+- struct cifs_open_info_data *data);
+-
+ extern int smb311_posix_get_inode_info(struct inode **inode,
+ const char *full_path,
+ struct cifs_open_info_data *data,
+diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c
+index 2739cb8390804..8aff8382cfb54 100644
+--- a/fs/smb/client/inode.c
++++ b/fs/smb/client/inode.c
+@@ -26,6 +26,7 @@
+ #include "fs_context.h"
+ #include "cifs_ioctl.h"
+ #include "cached_dir.h"
++#include "reparse.h"
+
+ static void cifs_set_ops(struct inode *inode)
+ {
+@@ -728,84 +729,6 @@ static void smb311_posix_info_to_fattr(struct cifs_fattr *fattr,
+ fattr->cf_mode, fattr->cf_uniqueid, fattr->cf_nlink);
+ }
+
+-static inline dev_t nfs_mkdev(struct reparse_posix_data *buf)
+-{
+- u64 v = le64_to_cpu(*(__le64 *)buf->DataBuffer);
+-
+- return MKDEV(v >> 32, v & 0xffffffff);
+-}
+-
+-bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb,
+- struct cifs_fattr *fattr,
+- struct cifs_open_info_data *data)
+-{
+- struct reparse_posix_data *buf = data->reparse.posix;
+- u32 tag = data->reparse.tag;
+-
+- if (tag == IO_REPARSE_TAG_NFS && buf) {
+- switch (le64_to_cpu(buf->InodeType)) {
+- case NFS_SPECFILE_CHR:
+- fattr->cf_mode |= S_IFCHR;
+- fattr->cf_dtype = DT_CHR;
+- fattr->cf_rdev = nfs_mkdev(buf);
+- break;
+- case NFS_SPECFILE_BLK:
+- fattr->cf_mode |= S_IFBLK;
+- fattr->cf_dtype = DT_BLK;
+- fattr->cf_rdev = nfs_mkdev(buf);
+- break;
+- case NFS_SPECFILE_FIFO:
+- fattr->cf_mode |= S_IFIFO;
+- fattr->cf_dtype = DT_FIFO;
+- break;
+- case NFS_SPECFILE_SOCK:
+- fattr->cf_mode |= S_IFSOCK;
+- fattr->cf_dtype = DT_SOCK;
+- break;
+- case NFS_SPECFILE_LNK:
+- fattr->cf_mode |= S_IFLNK;
+- fattr->cf_dtype = DT_LNK;
+- break;
+- default:
+- WARN_ON_ONCE(1);
+- return false;
+- }
+- return true;
+- }
+-
+- switch (tag) {
+- case IO_REPARSE_TAG_LX_SYMLINK:
+- fattr->cf_mode |= S_IFLNK;
+- fattr->cf_dtype = DT_LNK;
+- break;
+- case IO_REPARSE_TAG_LX_FIFO:
+- fattr->cf_mode |= S_IFIFO;
+- fattr->cf_dtype = DT_FIFO;
+- break;
+- case IO_REPARSE_TAG_AF_UNIX:
+- fattr->cf_mode |= S_IFSOCK;
+- fattr->cf_dtype = DT_SOCK;
+- break;
+- case IO_REPARSE_TAG_LX_CHR:
+- fattr->cf_mode |= S_IFCHR;
+- fattr->cf_dtype = DT_CHR;
+- break;
+- case IO_REPARSE_TAG_LX_BLK:
+- fattr->cf_mode |= S_IFBLK;
+- fattr->cf_dtype = DT_BLK;
+- break;
+- case 0: /* SMB1 symlink */
+- case IO_REPARSE_TAG_SYMLINK:
+- case IO_REPARSE_TAG_NFS:
+- fattr->cf_mode |= S_IFLNK;
+- fattr->cf_dtype = DT_LNK;
+- break;
+- default:
+- return false;
+- }
+- return true;
+-}
+-
+ static void cifs_open_info_to_fattr(struct cifs_fattr *fattr,
+ struct cifs_open_info_data *data,
+ struct super_block *sb)
+diff --git a/fs/smb/client/readdir.c b/fs/smb/client/readdir.c
+index 73ff9bd059682..3e5d22b356e92 100644
+--- a/fs/smb/client/readdir.c
++++ b/fs/smb/client/readdir.c
+@@ -22,6 +22,7 @@
+ #include "smb2proto.h"
+ #include "fs_context.h"
+ #include "cached_dir.h"
++#include "reparse.h"
+
+ /*
+ * To be safe - for UCS to UTF-8 with strings loaded with the rare long
+@@ -55,23 +56,6 @@ static inline void dump_cifs_file_struct(struct file *file, char *label)
+ }
+ #endif /* DEBUG2 */
+
+-/*
+- * Match a reparse point inode if reparse tag and ctime haven't changed.
+- *
+- * Windows Server updates ctime of reparse points when their data have changed.
+- * The server doesn't allow changing reparse tags from existing reparse points,
+- * though it's worth checking.
+- */
+-static inline bool reparse_inode_match(struct inode *inode,
+- struct cifs_fattr *fattr)
+-{
+- struct timespec64 ctime = inode_get_ctime(inode);
+-
+- return (CIFS_I(inode)->cifsAttrs & ATTR_REPARSE) &&
+- CIFS_I(inode)->reparse_tag == fattr->cf_cifstag &&
+- timespec64_equal(&ctime, &fattr->cf_ctime);
+-}
+-
+ /*
+ * Attempt to preload the dcache with the results from the FIND_FIRST/NEXT
+ *
+diff --git a/fs/smb/client/reparse.c b/fs/smb/client/reparse.c
+new file mode 100644
+index 0000000000000..c405be47c84d9
+--- /dev/null
++++ b/fs/smb/client/reparse.c
+@@ -0,0 +1,320 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copyright (c) 2024 Paulo Alcantara <pc@manguebit.com>
++ */
++
++#include <linux/fs.h>
++#include <linux/stat.h>
++#include <linux/slab.h>
++#include "cifsglob.h"
++#include "smb2proto.h"
++#include "cifsproto.h"
++#include "cifs_unicode.h"
++#include "cifs_debug.h"
++#include "reparse.h"
++
++int smb2_create_reparse_symlink(const unsigned int xid, struct inode *inode,
++ struct dentry *dentry, struct cifs_tcon *tcon,
++ const char *full_path, const char *symname)
++{
++ struct reparse_symlink_data_buffer *buf = NULL;
++ struct cifs_open_info_data data;
++ struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
++ struct inode *new;
++ struct kvec iov;
++ __le16 *path;
++ char *sym, sep = CIFS_DIR_SEP(cifs_sb);
++ u16 len, plen;
++ int rc = 0;
++
++ sym = kstrdup(symname, GFP_KERNEL);
++ if (!sym)
++ return -ENOMEM;
++
++ data = (struct cifs_open_info_data) {
++ .reparse_point = true,
++ .reparse = { .tag = IO_REPARSE_TAG_SYMLINK, },
++ .symlink_target = sym,
++ };
++
++ convert_delimiter(sym, sep);
++ path = cifs_convert_path_to_utf16(sym, cifs_sb);
++ if (!path) {
++ rc = -ENOMEM;
++ goto out;
++ }
++
++ plen = 2 * UniStrnlen((wchar_t *)path, PATH_MAX);
++ len = sizeof(*buf) + plen * 2;
++ buf = kzalloc(len, GFP_KERNEL);
++ if (!buf) {
++ rc = -ENOMEM;
++ goto out;
++ }
++
++ buf->ReparseTag = cpu_to_le32(IO_REPARSE_TAG_SYMLINK);
++ buf->ReparseDataLength = cpu_to_le16(len - sizeof(struct reparse_data_buffer));
++ buf->SubstituteNameOffset = cpu_to_le16(plen);
++ buf->SubstituteNameLength = cpu_to_le16(plen);
++ memcpy(&buf->PathBuffer[plen], path, plen);
++ buf->PrintNameOffset = 0;
++ buf->PrintNameLength = cpu_to_le16(plen);
++ memcpy(buf->PathBuffer, path, plen);
++ buf->Flags = cpu_to_le32(*symname != '/' ? SYMLINK_FLAG_RELATIVE : 0);
++ if (*sym != sep)
++ buf->Flags = cpu_to_le32(SYMLINK_FLAG_RELATIVE);
++
++ convert_delimiter(sym, '/');
++ iov.iov_base = buf;
++ iov.iov_len = len;
++ new = smb2_get_reparse_inode(&data, inode->i_sb, xid,
++ tcon, full_path, &iov);
++ if (!IS_ERR(new))
++ d_instantiate(dentry, new);
++ else
++ rc = PTR_ERR(new);
++out:
++ kfree(path);
++ cifs_free_open_info(&data);
++ kfree(buf);
++ return rc;
++}
++
++static int nfs_set_reparse_buf(struct reparse_posix_data *buf,
++ mode_t mode, dev_t dev,
++ struct kvec *iov)
++{
++ u64 type;
++ u16 len, dlen;
++
++ len = sizeof(*buf);
++
++ switch ((type = reparse_mode_nfs_type(mode))) {
++ case NFS_SPECFILE_BLK:
++ case NFS_SPECFILE_CHR:
++ dlen = sizeof(__le64);
++ break;
++ case NFS_SPECFILE_FIFO:
++ case NFS_SPECFILE_SOCK:
++ dlen = 0;
++ break;
++ default:
++ return -EOPNOTSUPP;
++ }
++
++ buf->ReparseTag = cpu_to_le32(IO_REPARSE_TAG_NFS);
++ buf->Reserved = 0;
++ buf->InodeType = cpu_to_le64(type);
++ buf->ReparseDataLength = cpu_to_le16(len + dlen -
++ sizeof(struct reparse_data_buffer));
++ *(__le64 *)buf->DataBuffer = cpu_to_le64(((u64)MAJOR(dev) << 32) |
++ MINOR(dev));
++ iov->iov_base = buf;
++ iov->iov_len = len + dlen;
++ return 0;
++}
++
++int smb2_make_nfs_node(unsigned int xid, struct inode *inode,
++ struct dentry *dentry, struct cifs_tcon *tcon,
++ const char *full_path, umode_t mode, dev_t dev)
++{
++ struct cifs_open_info_data data;
++ struct reparse_posix_data *p;
++ struct inode *new;
++ struct kvec iov;
++ __u8 buf[sizeof(*p) + sizeof(__le64)];
++ int rc;
++
++ p = (struct reparse_posix_data *)buf;
++ rc = nfs_set_reparse_buf(p, mode, dev, &iov);
++ if (rc)
++ return rc;
++
++ data = (struct cifs_open_info_data) {
++ .reparse_point = true,
++ .reparse = { .tag = IO_REPARSE_TAG_NFS, .posix = p, },
++ };
++
++ new = smb2_get_reparse_inode(&data, inode->i_sb, xid,
++ tcon, full_path, &iov);
++ if (!IS_ERR(new))
++ d_instantiate(dentry, new);
++ else
++ rc = PTR_ERR(new);
++ cifs_free_open_info(&data);
++ return rc;
++}
++
++/* See MS-FSCC 2.1.2.6 for the 'NFS' style reparse tags */
++static int parse_reparse_posix(struct reparse_posix_data *buf,
++ struct cifs_sb_info *cifs_sb,
++ struct cifs_open_info_data *data)
++{
++ unsigned int len;
++ u64 type;
++
++ switch ((type = le64_to_cpu(buf->InodeType))) {
++ case NFS_SPECFILE_LNK:
++ len = le16_to_cpu(buf->ReparseDataLength);
++ data->symlink_target = cifs_strndup_from_utf16(buf->DataBuffer,
++ len, true,
++ cifs_sb->local_nls);
++ if (!data->symlink_target)
++ return -ENOMEM;
++ convert_delimiter(data->symlink_target, '/');
++ cifs_dbg(FYI, "%s: target path: %s\n",
++ __func__, data->symlink_target);
++ break;
++ case NFS_SPECFILE_CHR:
++ case NFS_SPECFILE_BLK:
++ case NFS_SPECFILE_FIFO:
++ case NFS_SPECFILE_SOCK:
++ break;
++ default:
++ cifs_dbg(VFS, "%s: unhandled inode type: 0x%llx\n",
++ __func__, type);
++ return -EOPNOTSUPP;
++ }
++ return 0;
++}
++
++static int parse_reparse_symlink(struct reparse_symlink_data_buffer *sym,
++ u32 plen, bool unicode,
++ struct cifs_sb_info *cifs_sb,
++ struct cifs_open_info_data *data)
++{
++ unsigned int len;
++ unsigned int offs;
++
++ /* We handle Symbolic Link reparse tag here. See: MS-FSCC 2.1.2.4 */
++
++ offs = le16_to_cpu(sym->SubstituteNameOffset);
++ len = le16_to_cpu(sym->SubstituteNameLength);
++ if (offs + 20 > plen || offs + len + 20 > plen) {
++ cifs_dbg(VFS, "srv returned malformed symlink buffer\n");
++ return -EIO;
++ }
++
++ data->symlink_target = cifs_strndup_from_utf16(sym->PathBuffer + offs,
++ len, unicode,
++ cifs_sb->local_nls);
++ if (!data->symlink_target)
++ return -ENOMEM;
++
++ convert_delimiter(data->symlink_target, '/');
++ cifs_dbg(FYI, "%s: target path: %s\n", __func__, data->symlink_target);
++
++ return 0;
++}
++
++int parse_reparse_point(struct reparse_data_buffer *buf,
++ u32 plen, struct cifs_sb_info *cifs_sb,
++ bool unicode, struct cifs_open_info_data *data)
++{
++ data->reparse.buf = buf;
++
++ /* See MS-FSCC 2.1.2 */
++ switch (le32_to_cpu(buf->ReparseTag)) {
++ case IO_REPARSE_TAG_NFS:
++ return parse_reparse_posix((struct reparse_posix_data *)buf,
++ cifs_sb, data);
++ case IO_REPARSE_TAG_SYMLINK:
++ return parse_reparse_symlink(
++ (struct reparse_symlink_data_buffer *)buf,
++ plen, unicode, cifs_sb, data);
++ case IO_REPARSE_TAG_LX_SYMLINK:
++ case IO_REPARSE_TAG_AF_UNIX:
++ case IO_REPARSE_TAG_LX_FIFO:
++ case IO_REPARSE_TAG_LX_CHR:
++ case IO_REPARSE_TAG_LX_BLK:
++ return 0;
++ default:
++ cifs_dbg(VFS, "%s: unhandled reparse tag: 0x%08x\n",
++ __func__, le32_to_cpu(buf->ReparseTag));
++ return -EOPNOTSUPP;
++ }
++}
++
++int smb2_parse_reparse_point(struct cifs_sb_info *cifs_sb,
++ struct kvec *rsp_iov,
++ struct cifs_open_info_data *data)
++{
++ struct reparse_data_buffer *buf;
++ struct smb2_ioctl_rsp *io = rsp_iov->iov_base;
++ u32 plen = le32_to_cpu(io->OutputCount);
++
++ buf = (struct reparse_data_buffer *)((u8 *)io +
++ le32_to_cpu(io->OutputOffset));
++ return parse_reparse_point(buf, plen, cifs_sb, true, data);
++}
++
++bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb,
++ struct cifs_fattr *fattr,
++ struct cifs_open_info_data *data)
++{
++ struct reparse_posix_data *buf = data->reparse.posix;
++ u32 tag = data->reparse.tag;
++
++ if (tag == IO_REPARSE_TAG_NFS && buf) {
++ switch (le64_to_cpu(buf->InodeType)) {
++ case NFS_SPECFILE_CHR:
++ fattr->cf_mode |= S_IFCHR;
++ fattr->cf_dtype = DT_CHR;
++ fattr->cf_rdev = reparse_nfs_mkdev(buf);
++ break;
++ case NFS_SPECFILE_BLK:
++ fattr->cf_mode |= S_IFBLK;
++ fattr->cf_dtype = DT_BLK;
++ fattr->cf_rdev = reparse_nfs_mkdev(buf);
++ break;
++ case NFS_SPECFILE_FIFO:
++ fattr->cf_mode |= S_IFIFO;
++ fattr->cf_dtype = DT_FIFO;
++ break;
++ case NFS_SPECFILE_SOCK:
++ fattr->cf_mode |= S_IFSOCK;
++ fattr->cf_dtype = DT_SOCK;
++ break;
++ case NFS_SPECFILE_LNK:
++ fattr->cf_mode |= S_IFLNK;
++ fattr->cf_dtype = DT_LNK;
++ break;
++ default:
++ WARN_ON_ONCE(1);
++ return false;
++ }
++ return true;
++ }
++
++ switch (tag) {
++ case IO_REPARSE_TAG_LX_SYMLINK:
++ fattr->cf_mode |= S_IFLNK;
++ fattr->cf_dtype = DT_LNK;
++ break;
++ case IO_REPARSE_TAG_LX_FIFO:
++ fattr->cf_mode |= S_IFIFO;
++ fattr->cf_dtype = DT_FIFO;
++ break;
++ case IO_REPARSE_TAG_AF_UNIX:
++ fattr->cf_mode |= S_IFSOCK;
++ fattr->cf_dtype = DT_SOCK;
++ break;
++ case IO_REPARSE_TAG_LX_CHR:
++ fattr->cf_mode |= S_IFCHR;
++ fattr->cf_dtype = DT_CHR;
++ break;
++ case IO_REPARSE_TAG_LX_BLK:
++ fattr->cf_mode |= S_IFBLK;
++ fattr->cf_dtype = DT_BLK;
++ break;
++ case 0: /* SMB1 symlink */
++ case IO_REPARSE_TAG_SYMLINK:
++ case IO_REPARSE_TAG_NFS:
++ fattr->cf_mode |= S_IFLNK;
++ fattr->cf_dtype = DT_LNK;
++ break;
++ default:
++ return false;
++ }
++ return true;
++}
+diff --git a/fs/smb/client/reparse.h b/fs/smb/client/reparse.h
+new file mode 100644
+index 0000000000000..3ceb90da0df90
+--- /dev/null
++++ b/fs/smb/client/reparse.h
+@@ -0,0 +1,73 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++/*
++ * Copyright (c) 2024 Paulo Alcantara <pc@manguebit.com>
++ */
++
++#ifndef _CIFS_REPARSE_H
++#define _CIFS_REPARSE_H
++
++#include <linux/fs.h>
++#include <linux/stat.h>
++#include "cifsglob.h"
++
++static inline dev_t reparse_nfs_mkdev(struct reparse_posix_data *buf)
++{
++ u64 v = le64_to_cpu(*(__le64 *)buf->DataBuffer);
++
++ return MKDEV(v >> 32, v & 0xffffffff);
++}
++
++static inline u64 reparse_mode_nfs_type(mode_t mode)
++{
++ switch (mode & S_IFMT) {
++ case S_IFBLK: return NFS_SPECFILE_BLK;
++ case S_IFCHR: return NFS_SPECFILE_CHR;
++ case S_IFIFO: return NFS_SPECFILE_FIFO;
++ case S_IFSOCK: return NFS_SPECFILE_SOCK;
++ }
++ return 0;
++}
++
++/*
++ * Match a reparse point inode if reparse tag and ctime haven't changed.
++ *
++ * Windows Server updates ctime of reparse points when their data have changed.
++ * The server doesn't allow changing reparse tags from existing reparse points,
++ * though it's worth checking.
++ */
++static inline bool reparse_inode_match(struct inode *inode,
++ struct cifs_fattr *fattr)
++{
++ struct timespec64 ctime = inode_get_ctime(inode);
++
++ return (CIFS_I(inode)->cifsAttrs & ATTR_REPARSE) &&
++ CIFS_I(inode)->reparse_tag == fattr->cf_cifstag &&
++ timespec64_equal(&ctime, &fattr->cf_ctime);
++}
++
++static inline bool cifs_open_data_reparse(struct cifs_open_info_data *data)
++{
++ struct smb2_file_all_info *fi = &data->fi;
++ u32 attrs = le32_to_cpu(fi->Attributes);
++ bool ret;
++
++ ret = data->reparse_point || (attrs & ATTR_REPARSE);
++ if (ret)
++ attrs |= ATTR_REPARSE;
++ fi->Attributes = cpu_to_le32(attrs);
++ return ret;
++}
++
++bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb,
++ struct cifs_fattr *fattr,
++ struct cifs_open_info_data *data);
++int smb2_create_reparse_symlink(const unsigned int xid, struct inode *inode,
++ struct dentry *dentry, struct cifs_tcon *tcon,
++ const char *full_path, const char *symname);
++int smb2_make_nfs_node(unsigned int xid, struct inode *inode,
++ struct dentry *dentry, struct cifs_tcon *tcon,
++ const char *full_path, umode_t mode, dev_t dev);
++int smb2_parse_reparse_point(struct cifs_sb_info *cifs_sb, struct kvec *rsp_iov,
++ struct cifs_open_info_data *data);
++
++#endif /* _CIFS_REPARSE_H */
+diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
+index 9ade347978709..2c59a6fc2d7c7 100644
+--- a/fs/smb/client/smb2ops.c
++++ b/fs/smb/client/smb2ops.c
+@@ -28,6 +28,7 @@
+ #include "fscache.h"
+ #include "fs_context.h"
+ #include "cached_dir.h"
++#include "reparse.h"
+
+ /* Change credits for different ops and return the total number of credits */
+ static int
+@@ -2989,109 +2990,6 @@ smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses,
+ return rc;
+ }
+
+-/* See MS-FSCC 2.1.2.6 for the 'NFS' style reparse tags */
+-static int parse_reparse_posix(struct reparse_posix_data *buf,
+- struct cifs_sb_info *cifs_sb,
+- struct cifs_open_info_data *data)
+-{
+- unsigned int len;
+- u64 type;
+-
+- switch ((type = le64_to_cpu(buf->InodeType))) {
+- case NFS_SPECFILE_LNK:
+- len = le16_to_cpu(buf->ReparseDataLength);
+- data->symlink_target = cifs_strndup_from_utf16(buf->DataBuffer,
+- len, true,
+- cifs_sb->local_nls);
+- if (!data->symlink_target)
+- return -ENOMEM;
+- convert_delimiter(data->symlink_target, '/');
+- cifs_dbg(FYI, "%s: target path: %s\n",
+- __func__, data->symlink_target);
+- break;
+- case NFS_SPECFILE_CHR:
+- case NFS_SPECFILE_BLK:
+- case NFS_SPECFILE_FIFO:
+- case NFS_SPECFILE_SOCK:
+- break;
+- default:
+- cifs_dbg(VFS, "%s: unhandled inode type: 0x%llx\n",
+- __func__, type);
+- return -EOPNOTSUPP;
+- }
+- return 0;
+-}
+-
+-static int parse_reparse_symlink(struct reparse_symlink_data_buffer *sym,
+- u32 plen, bool unicode,
+- struct cifs_sb_info *cifs_sb,
+- struct cifs_open_info_data *data)
+-{
+- unsigned int len;
+- unsigned int offs;
+-
+- /* We handle Symbolic Link reparse tag here. See: MS-FSCC 2.1.2.4 */
+-
+- offs = le16_to_cpu(sym->SubstituteNameOffset);
+- len = le16_to_cpu(sym->SubstituteNameLength);
+- if (offs + 20 > plen || offs + len + 20 > plen) {
+- cifs_dbg(VFS, "srv returned malformed symlink buffer\n");
+- return -EIO;
+- }
+-
+- data->symlink_target = cifs_strndup_from_utf16(sym->PathBuffer + offs,
+- len, unicode,
+- cifs_sb->local_nls);
+- if (!data->symlink_target)
+- return -ENOMEM;
+-
+- convert_delimiter(data->symlink_target, '/');
+- cifs_dbg(FYI, "%s: target path: %s\n", __func__, data->symlink_target);
+-
+- return 0;
+-}
+-
+-int parse_reparse_point(struct reparse_data_buffer *buf,
+- u32 plen, struct cifs_sb_info *cifs_sb,
+- bool unicode, struct cifs_open_info_data *data)
+-{
+- data->reparse.buf = buf;
+-
+- /* See MS-FSCC 2.1.2 */
+- switch (le32_to_cpu(buf->ReparseTag)) {
+- case IO_REPARSE_TAG_NFS:
+- return parse_reparse_posix((struct reparse_posix_data *)buf,
+- cifs_sb, data);
+- case IO_REPARSE_TAG_SYMLINK:
+- return parse_reparse_symlink(
+- (struct reparse_symlink_data_buffer *)buf,
+- plen, unicode, cifs_sb, data);
+- case IO_REPARSE_TAG_LX_SYMLINK:
+- case IO_REPARSE_TAG_AF_UNIX:
+- case IO_REPARSE_TAG_LX_FIFO:
+- case IO_REPARSE_TAG_LX_CHR:
+- case IO_REPARSE_TAG_LX_BLK:
+- return 0;
+- default:
+- cifs_dbg(VFS, "%s: unhandled reparse tag: 0x%08x\n",
+- __func__, le32_to_cpu(buf->ReparseTag));
+- return -EOPNOTSUPP;
+- }
+-}
+-
+-static int smb2_parse_reparse_point(struct cifs_sb_info *cifs_sb,
+- struct kvec *rsp_iov,
+- struct cifs_open_info_data *data)
+-{
+- struct reparse_data_buffer *buf;
+- struct smb2_ioctl_rsp *io = rsp_iov->iov_base;
+- u32 plen = le32_to_cpu(io->OutputCount);
+-
+- buf = (struct reparse_data_buffer *)((u8 *)io +
+- le32_to_cpu(io->OutputOffset));
+- return parse_reparse_point(buf, plen, cifs_sb, true, data);
+-}
+-
+ static struct cifs_ntsd *
+ get_smb2_acl_by_fid(struct cifs_sb_info *cifs_sb,
+ const struct cifs_fid *cifsfid, u32 *pacllen, u32 info)
+@@ -5123,152 +5021,6 @@ int cifs_sfu_make_node(unsigned int xid, struct inode *inode,
+ return rc;
+ }
+
+-static inline u64 mode_nfs_type(mode_t mode)
+-{
+- switch (mode & S_IFMT) {
+- case S_IFBLK: return NFS_SPECFILE_BLK;
+- case S_IFCHR: return NFS_SPECFILE_CHR;
+- case S_IFIFO: return NFS_SPECFILE_FIFO;
+- case S_IFSOCK: return NFS_SPECFILE_SOCK;
+- }
+- return 0;
+-}
+-
+-static int nfs_set_reparse_buf(struct reparse_posix_data *buf,
+- mode_t mode, dev_t dev,
+- struct kvec *iov)
+-{
+- u64 type;
+- u16 len, dlen;
+-
+- len = sizeof(*buf);
+-
+- switch ((type = mode_nfs_type(mode))) {
+- case NFS_SPECFILE_BLK:
+- case NFS_SPECFILE_CHR:
+- dlen = sizeof(__le64);
+- break;
+- case NFS_SPECFILE_FIFO:
+- case NFS_SPECFILE_SOCK:
+- dlen = 0;
+- break;
+- default:
+- return -EOPNOTSUPP;
+- }
+-
+- buf->ReparseTag = cpu_to_le32(IO_REPARSE_TAG_NFS);
+- buf->Reserved = 0;
+- buf->InodeType = cpu_to_le64(type);
+- buf->ReparseDataLength = cpu_to_le16(len + dlen -
+- sizeof(struct reparse_data_buffer));
+- *(__le64 *)buf->DataBuffer = cpu_to_le64(((u64)MAJOR(dev) << 32) |
+- MINOR(dev));
+- iov->iov_base = buf;
+- iov->iov_len = len + dlen;
+- return 0;
+-}
+-
+-static int nfs_make_node(unsigned int xid, struct inode *inode,
+- struct dentry *dentry, struct cifs_tcon *tcon,
+- const char *full_path, umode_t mode, dev_t dev)
+-{
+- struct cifs_open_info_data data;
+- struct reparse_posix_data *p;
+- struct inode *new;
+- struct kvec iov;
+- __u8 buf[sizeof(*p) + sizeof(__le64)];
+- int rc;
+-
+- p = (struct reparse_posix_data *)buf;
+- rc = nfs_set_reparse_buf(p, mode, dev, &iov);
+- if (rc)
+- return rc;
+-
+- data = (struct cifs_open_info_data) {
+- .reparse_point = true,
+- .reparse = { .tag = IO_REPARSE_TAG_NFS, .posix = p, },
+- };
+-
+- new = smb2_get_reparse_inode(&data, inode->i_sb, xid,
+- tcon, full_path, &iov);
+- if (!IS_ERR(new))
+- d_instantiate(dentry, new);
+- else
+- rc = PTR_ERR(new);
+- cifs_free_open_info(&data);
+- return rc;
+-}
+-
+-static int smb2_create_reparse_symlink(const unsigned int xid,
+- struct inode *inode,
+- struct dentry *dentry,
+- struct cifs_tcon *tcon,
+- const char *full_path,
+- const char *symname)
+-{
+- struct reparse_symlink_data_buffer *buf = NULL;
+- struct cifs_open_info_data data;
+- struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+- struct inode *new;
+- struct kvec iov;
+- __le16 *path;
+- char *sym, sep = CIFS_DIR_SEP(cifs_sb);
+- u16 len, plen;
+- int rc = 0;
+-
+- sym = kstrdup(symname, GFP_KERNEL);
+- if (!sym)
+- return -ENOMEM;
+-
+- data = (struct cifs_open_info_data) {
+- .reparse_point = true,
+- .reparse = { .tag = IO_REPARSE_TAG_SYMLINK, },
+- .symlink_target = sym,
+- };
+-
+- convert_delimiter(sym, sep);
+- path = cifs_convert_path_to_utf16(sym, cifs_sb);
+- if (!path) {
+- rc = -ENOMEM;
+- goto out;
+- }
+-
+- plen = 2 * UniStrnlen((wchar_t *)path, PATH_MAX);
+- len = sizeof(*buf) + plen * 2;
+- buf = kzalloc(len, GFP_KERNEL);
+- if (!buf) {
+- rc = -ENOMEM;
+- goto out;
+- }
+-
+- buf->ReparseTag = cpu_to_le32(IO_REPARSE_TAG_SYMLINK);
+- buf->ReparseDataLength = cpu_to_le16(len - sizeof(struct reparse_data_buffer));
+- buf->SubstituteNameOffset = cpu_to_le16(plen);
+- buf->SubstituteNameLength = cpu_to_le16(plen);
+- memcpy(&buf->PathBuffer[plen], path, plen);
+- buf->PrintNameOffset = 0;
+- buf->PrintNameLength = cpu_to_le16(plen);
+- memcpy(buf->PathBuffer, path, plen);
+- buf->Flags = cpu_to_le32(*symname != '/' ? SYMLINK_FLAG_RELATIVE : 0);
+- if (*sym != sep)
+- buf->Flags = cpu_to_le32(SYMLINK_FLAG_RELATIVE);
+-
+- convert_delimiter(sym, '/');
+- iov.iov_base = buf;
+- iov.iov_len = len;
+- new = smb2_get_reparse_inode(&data, inode->i_sb, xid,
+- tcon, full_path, &iov);
+- if (!IS_ERR(new))
+- d_instantiate(dentry, new);
+- else
+- rc = PTR_ERR(new);
+-out:
+- kfree(path);
+- cifs_free_open_info(&data);
+- kfree(buf);
+- return rc;
+-}
+-
+ static int smb2_make_node(unsigned int xid, struct inode *inode,
+ struct dentry *dentry, struct cifs_tcon *tcon,
+ const char *full_path, umode_t mode, dev_t dev)
+@@ -5286,8 +5038,8 @@ static int smb2_make_node(unsigned int xid, struct inode *inode,
+ rc = cifs_sfu_make_node(xid, inode, dentry, tcon,
+ full_path, mode, dev);
+ } else {
+- rc = nfs_make_node(xid, inode, dentry, tcon,
+- full_path, mode, dev);
++ rc = smb2_make_nfs_node(xid, inode, dentry, tcon,
++ full_path, mode, dev);
+ }
+ return rc;
+ }
+diff --git a/fs/smb/client/smb2proto.h b/fs/smb/client/smb2proto.h
+index 221143788a1c0..64a0ef0409a6e 100644
+--- a/fs/smb/client/smb2proto.h
++++ b/fs/smb/client/smb2proto.h
+@@ -310,5 +310,11 @@ int smb311_posix_query_path_info(const unsigned int xid,
+ int posix_info_parse(const void *beg, const void *end,
+ struct smb2_posix_info_parsed *out);
+ int posix_info_sid_size(const void *beg, const void *end);
++int smb2_create_reparse_symlink(const unsigned int xid, struct inode *inode,
++ struct dentry *dentry, struct cifs_tcon *tcon,
++ const char *full_path, const char *symname);
++int smb2_make_nfs_node(unsigned int xid, struct inode *inode,
++ struct dentry *dentry, struct cifs_tcon *tcon,
++ const char *full_path, umode_t mode, dev_t dev);
+
+ #endif /* _SMB2PROTO_H */
+--
+2.43.0
+
diff --git a/queue-6.6/smb-client-negotiate-compression-algorithms.patch b/queue-6.6/smb-client-negotiate-compression-algorithms.patch
new file mode 100644
index 0000000000..c76a72ec71
--- /dev/null
+++ b/queue-6.6/smb-client-negotiate-compression-algorithms.patch
@@ -0,0 +1,192 @@
+From 39c4a6a119db62fb9d5ab7e929c225f99100101e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 23 Feb 2024 11:58:57 -0300
+Subject: smb: client: negotiate compression algorithms
+
+From: Enzo Matsumiya <ematsumiya@suse.de>
+
+[ Upstream commit 8fe7062b7d11fcd21c4dcb5f530eaa1a099b24e7 ]
+
+Change "compress=" mount option to a boolean flag, that, if set,
+will enable negotiating compression algorithms with the server.
+
+Do not de/compress anything for now.
+
+Signed-off-by: Enzo Matsumiya <ematsumiya@suse.de>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/cifs_debug.c | 32 ++++++++++++++++++++++++++------
+ fs/smb/client/cifsglob.h | 6 +++++-
+ fs/smb/client/connect.c | 2 +-
+ fs/smb/client/fs_context.c | 2 +-
+ fs/smb/client/fs_context.h | 2 +-
+ fs/smb/client/smb2pdu.c | 20 +++++++++++++++-----
+ 6 files changed, 49 insertions(+), 15 deletions(-)
+
+diff --git a/fs/smb/client/cifs_debug.c b/fs/smb/client/cifs_debug.c
+index aa95fa95ca112..c71ae5c043060 100644
+--- a/fs/smb/client/cifs_debug.c
++++ b/fs/smb/client/cifs_debug.c
+@@ -280,6 +280,24 @@ static int cifs_debug_files_proc_show(struct seq_file *m, void *v)
+ return 0;
+ }
+
++static __always_inline const char *compression_alg_str(__le16 alg)
++{
++ switch (alg) {
++ case SMB3_COMPRESS_NONE:
++ return "NONE";
++ case SMB3_COMPRESS_LZNT1:
++ return "LZNT1";
++ case SMB3_COMPRESS_LZ77:
++ return "LZ77";
++ case SMB3_COMPRESS_LZ77_HUFF:
++ return "LZ77-Huffman";
++ case SMB3_COMPRESS_PATTERN:
++ return "Pattern_V1";
++ default:
++ return "invalid";
++ }
++}
++
+ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
+ {
+ struct mid_q_entry *mid_entry;
+@@ -425,12 +443,6 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
+ server->echo_credits,
+ server->oplock_credits,
+ server->dialect);
+- if (server->compress_algorithm == SMB3_COMPRESS_LZNT1)
+- seq_printf(m, " COMPRESS_LZNT1");
+- else if (server->compress_algorithm == SMB3_COMPRESS_LZ77)
+- seq_printf(m, " COMPRESS_LZ77");
+- else if (server->compress_algorithm == SMB3_COMPRESS_LZ77_HUFF)
+- seq_printf(m, " COMPRESS_LZ77_HUFF");
+ if (server->sign)
+ seq_printf(m, " signed");
+ if (server->posix_ext_supported)
+@@ -462,6 +474,14 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
+ server->leaf_fullpath);
+ }
+
++ seq_puts(m, "\nCompression: ");
++ if (!server->compression.requested)
++ seq_puts(m, "disabled on mount");
++ else if (server->compression.enabled)
++ seq_printf(m, "enabled (%s)", compression_alg_str(server->compression.alg));
++ else
++ seq_puts(m, "disabled (not supported by this server)");
++
+ seq_printf(m, "\n\n\tSessions: ");
+ i = 0;
+ list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
+diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h
+index 053556ca6f011..70a12584375de 100644
+--- a/fs/smb/client/cifsglob.h
++++ b/fs/smb/client/cifsglob.h
+@@ -772,7 +772,11 @@ struct TCP_Server_Info {
+ unsigned int max_write;
+ unsigned int min_offload;
+ unsigned int retrans;
+- __le16 compress_algorithm;
++ struct {
++ bool requested; /* "compress" mount option set*/
++ bool enabled; /* actually negotiated with server */
++ __le16 alg; /* preferred alg negotiated with server */
++ } compression;
+ __u16 signing_algorithm;
+ __le16 cipher_type;
+ /* save initital negprot hash */
+diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c
+index deba1cfd11801..5acfd2057ca04 100644
+--- a/fs/smb/client/connect.c
++++ b/fs/smb/client/connect.c
+@@ -1748,7 +1748,7 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx,
+ tcp_ses->channel_sequence_num = 0; /* only tracked for primary channel */
+ tcp_ses->reconnect_instance = 1;
+ tcp_ses->lstrp = jiffies;
+- tcp_ses->compress_algorithm = cpu_to_le16(ctx->compression);
++ tcp_ses->compression.requested = ctx->compress;
+ spin_lock_init(&tcp_ses->req_lock);
+ spin_lock_init(&tcp_ses->srv_lock);
+ spin_lock_init(&tcp_ses->mid_lock);
+diff --git a/fs/smb/client/fs_context.c b/fs/smb/client/fs_context.c
+index fbaa901d3493a..3bbac925d0766 100644
+--- a/fs/smb/client/fs_context.c
++++ b/fs/smb/client/fs_context.c
+@@ -978,7 +978,7 @@ static int smb3_fs_context_parse_param(struct fs_context *fc,
+
+ switch (opt) {
+ case Opt_compress:
+- ctx->compression = UNKNOWN_TYPE;
++ ctx->compress = true;
+ cifs_dbg(VFS,
+ "SMB3 compression support is experimental\n");
+ break;
+diff --git a/fs/smb/client/fs_context.h b/fs/smb/client/fs_context.h
+index e77ee81846b31..cf577ec0dd0ac 100644
+--- a/fs/smb/client/fs_context.h
++++ b/fs/smb/client/fs_context.h
+@@ -277,7 +277,7 @@ struct smb3_fs_context {
+ unsigned int max_credits; /* smb3 max_credits 10 < credits < 60000 */
+ unsigned int max_channels;
+ unsigned int max_cached_dirs;
+- __u16 compression; /* compression algorithm 0xFFFF default 0=disabled */
++ bool compress; /* enable SMB2 messages (READ/WRITE) de/compression */
+ bool rootfs:1; /* if it's a SMB root file system */
+ bool witness:1; /* use witness protocol */
+ char *leaf_fullpath;
+diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c
+index 4d805933e14f3..86c647a947ccd 100644
+--- a/fs/smb/client/smb2pdu.c
++++ b/fs/smb/client/smb2pdu.c
+@@ -743,7 +743,7 @@ assemble_neg_contexts(struct smb2_negotiate_req *req,
+ pneg_ctxt += sizeof(struct smb2_posix_neg_context);
+ neg_context_count++;
+
+- if (server->compress_algorithm) {
++ if (server->compression.requested) {
+ build_compression_ctxt((struct smb2_compression_capabilities_context *)
+ pneg_ctxt);
+ ctxt_len = ALIGN(sizeof(struct smb2_compression_capabilities_context), 8);
+@@ -791,6 +791,9 @@ static void decode_compress_ctx(struct TCP_Server_Info *server,
+ struct smb2_compression_capabilities_context *ctxt)
+ {
+ unsigned int len = le16_to_cpu(ctxt->DataLength);
++ __le16 alg;
++
++ server->compression.enabled = false;
+
+ /*
+ * Caller checked that DataLength remains within SMB boundary. We still
+@@ -801,15 +804,22 @@ static void decode_compress_ctx(struct TCP_Server_Info *server,
+ pr_warn_once("server sent bad compression cntxt\n");
+ return;
+ }
++
+ if (le16_to_cpu(ctxt->CompressionAlgorithmCount) != 1) {
+- pr_warn_once("Invalid SMB3 compress algorithm count\n");
++ pr_warn_once("invalid SMB3 compress algorithm count\n");
+ return;
+ }
+- if (le16_to_cpu(ctxt->CompressionAlgorithms[0]) > 3) {
+- pr_warn_once("unknown compression algorithm\n");
++
++ alg = ctxt->CompressionAlgorithms[0];
++
++ /* 'NONE' (0) compressor type is never negotiated */
++ if (alg == 0 || le16_to_cpu(alg) > 3) {
++ pr_warn_once("invalid compression algorithm '%u'\n", alg);
+ return;
+ }
+- server->compress_algorithm = ctxt->CompressionAlgorithms[0];
++
++ server->compression.alg = alg;
++ server->compression.enabled = true;
+ }
+
+ static int decode_encrypt_ctx(struct TCP_Server_Info *server,
+--
+2.43.0
+
diff --git a/queue-6.6/smb-client-optimise-reparse-point-querying.patch b/queue-6.6/smb-client-optimise-reparse-point-querying.patch
new file mode 100644
index 0000000000..b1916595c6
--- /dev/null
+++ b/queue-6.6/smb-client-optimise-reparse-point-querying.patch
@@ -0,0 +1,371 @@
+From 53435a6a7f8ec496620979e2d9e62d0e20bf7abb Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 28 Apr 2024 01:14:29 -0500
+Subject: smb: client: optimise reparse point querying
+
+From: Steve French <stfrench@microsoft.com>
+
+[ Upstream commit 67ec9949b0dfe78c99e110dd975eb7dc5645630c ]
+
+Reduce number of roundtrips to server when querying reparse points in
+->query_path_info() by sending a single compound request of
+create+get_reparse+get_info+close.
+
+Signed-off-by: Paulo Alcantara (SUSE) <pc@manguebit.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/cifsglob.h | 10 ++--
+ fs/smb/client/cifsproto.h | 7 +++
+ fs/smb/client/inode.c | 5 +-
+ fs/smb/client/smb2glob.h | 3 +-
+ fs/smb/client/smb2inode.c | 122 ++++++++++++++++++++++++++++++--------
+ fs/smb/client/trace.h | 3 +
+ 6 files changed, 119 insertions(+), 31 deletions(-)
+
+diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h
+index cb86b1bf69b58..3e7c3c3d73a73 100644
+--- a/fs/smb/client/cifsglob.h
++++ b/fs/smb/client/cifsglob.h
+@@ -192,6 +192,11 @@ struct cifs_open_info_data {
+ bool symlink;
+ };
+ struct {
++ /* ioctl response buffer */
++ struct {
++ int buftype;
++ struct kvec iov;
++ } io;
+ __u32 tag;
+ union {
+ struct reparse_data_buffer *buf;
+@@ -218,11 +223,6 @@ static inline bool cifs_open_data_reparse(struct cifs_open_info_data *data)
+ return ret;
+ }
+
+-static inline void cifs_free_open_info(struct cifs_open_info_data *data)
+-{
+- kfree(data->symlink_target);
+-}
+-
+ /*
+ *****************************************************************
+ * Except the CIFS PDUs themselves all the
+diff --git a/fs/smb/client/cifsproto.h b/fs/smb/client/cifsproto.h
+index 49de2545f34ce..996ca413dd8bd 100644
+--- a/fs/smb/client/cifsproto.h
++++ b/fs/smb/client/cifsproto.h
+@@ -769,4 +769,11 @@ static inline void release_mid(struct mid_q_entry *mid)
+ kref_put(&mid->refcount, __release_mid);
+ }
+
++static inline void cifs_free_open_info(struct cifs_open_info_data *data)
++{
++ kfree(data->symlink_target);
++ free_rsp_buf(data->reparse.io.buftype, data->reparse.io.iov.iov_base);
++ memset(data, 0, sizeof(*data));
++}
++
+ #endif /* _CIFSPROTO_H */
+diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c
+index 391839feb29d5..89dfb405f9c1e 100644
+--- a/fs/smb/client/inode.c
++++ b/fs/smb/client/inode.c
+@@ -1080,6 +1080,9 @@ static int reparse_info_to_fattr(struct cifs_open_info_data *data,
+ &rsp_iov, &rsp_buftype);
+ if (!rc)
+ iov = &rsp_iov;
++ } else if (data->reparse.io.buftype != CIFS_NO_BUFFER &&
++ data->reparse.io.iov.iov_base) {
++ iov = &data->reparse.io.iov;
+ }
+
+ rc = -EOPNOTSUPP;
+@@ -1099,7 +1102,7 @@ static int reparse_info_to_fattr(struct cifs_open_info_data *data,
+ /* Check for cached reparse point data */
+ if (data->symlink_target || data->reparse.buf) {
+ rc = 0;
+- } else if (server->ops->parse_reparse_point) {
++ } else if (iov && server->ops->parse_reparse_point) {
+ rc = server->ops->parse_reparse_point(cifs_sb,
+ iov, data);
+ }
+diff --git a/fs/smb/client/smb2glob.h b/fs/smb/client/smb2glob.h
+index ca87a0011c337..a0c156996fc51 100644
+--- a/fs/smb/client/smb2glob.h
++++ b/fs/smb/client/smb2glob.h
+@@ -35,7 +35,8 @@ enum smb2_compound_ops {
+ SMB2_OP_SET_EOF,
+ SMB2_OP_RMDIR,
+ SMB2_OP_POSIX_QUERY_INFO,
+- SMB2_OP_SET_REPARSE
++ SMB2_OP_SET_REPARSE,
++ SMB2_OP_GET_REPARSE
+ };
+
+ /* Used when constructing chained read requests. */
+diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c
+index e74d3a1e49dfa..11c1e06ab5417 100644
+--- a/fs/smb/client/smb2inode.c
++++ b/fs/smb/client/smb2inode.c
+@@ -38,6 +38,24 @@ static inline __u32 file_create_options(struct dentry *dentry)
+ return 0;
+ }
+
++static struct reparse_data_buffer *reparse_buf_ptr(struct kvec *iov)
++{
++ struct reparse_data_buffer *buf;
++ struct smb2_ioctl_rsp *io = iov->iov_base;
++ u32 off, count, len;
++
++ count = le32_to_cpu(io->OutputCount);
++ off = le32_to_cpu(io->OutputOffset);
++ if (check_add_overflow(off, count, &len) || len > iov->iov_len)
++ return ERR_PTR(-EIO);
++
++ buf = (struct reparse_data_buffer *)((u8 *)io + off);
++ len = sizeof(*buf);
++ if (count < len || count < le16_to_cpu(buf->ReparseDataLength) + len)
++ return ERR_PTR(-EIO);
++ return buf;
++}
++
+ /*
+ * note: If cfile is passed, the reference to it is dropped here.
+ * So make sure that you do not reuse cfile after return from this func.
+@@ -54,8 +72,10 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
+ __u8 **extbuf, size_t *extbuflen,
+ struct kvec *out_iov, int *out_buftype)
+ {
++
++ struct reparse_data_buffer *rbuf;
+ struct smb2_compound_vars *vars = NULL;
+- struct kvec *rsp_iov;
++ struct kvec *rsp_iov, *iov;
+ struct smb_rqst *rqst;
+ int rc;
+ __le16 *utf16_path = NULL;
+@@ -375,6 +395,21 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
+ trace_smb3_set_reparse_compound_enter(xid, ses->Suid,
+ tcon->tid, full_path);
+ break;
++ case SMB2_OP_GET_REPARSE:
++ rqst[num_rqst].rq_iov = vars->io_iov;
++ rqst[num_rqst].rq_nvec = ARRAY_SIZE(vars->io_iov);
++
++ rc = SMB2_ioctl_init(tcon, server, &rqst[num_rqst],
++ COMPOUND_FID, COMPOUND_FID,
++ FSCTL_GET_REPARSE_POINT,
++ NULL, 0, CIFSMaxBufSize);
++ if (rc)
++ goto finished;
++ smb2_set_next_command(tcon, &rqst[num_rqst]);
++ smb2_set_related(&rqst[num_rqst++]);
++ trace_smb3_get_reparse_compound_enter(xid, ses->Suid,
++ tcon->tid, full_path);
++ break;
+ default:
+ cifs_dbg(VFS, "Invalid command\n");
+ rc = -EINVAL;
+@@ -541,6 +576,30 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
+ }
+ SMB2_ioctl_free(&rqst[num_rqst++]);
+ break;
++ case SMB2_OP_GET_REPARSE:
++ if (!rc) {
++ iov = &rsp_iov[i + 1];
++ idata = in_iov[i].iov_base;
++ idata->reparse.io.iov = *iov;
++ idata->reparse.io.buftype = resp_buftype[i + 1];
++ rbuf = reparse_buf_ptr(iov);
++ if (IS_ERR(rbuf)) {
++ rc = PTR_ERR(rbuf);
++ trace_smb3_set_reparse_compound_err(xid, ses->Suid,
++ tcon->tid, rc);
++ } else {
++ idata->reparse.tag = le32_to_cpu(rbuf->ReparseTag);
++ trace_smb3_set_reparse_compound_done(xid, ses->Suid,
++ tcon->tid);
++ }
++ memset(iov, 0, sizeof(*iov));
++ resp_buftype[i + 1] = CIFS_NO_BUFFER;
++ } else {
++ trace_smb3_set_reparse_compound_err(xid, ses->Suid,
++ tcon->tid, rc);
++ }
++ SMB2_ioctl_free(&rqst[num_rqst++]);
++ break;
+ }
+ }
+ SMB2_close_free(&rqst[num_rqst]);
+@@ -601,10 +660,11 @@ int smb2_query_path_info(const unsigned int xid,
+ struct cifsFileInfo *cfile;
+ struct cached_fid *cfid = NULL;
+ struct smb2_hdr *hdr;
+- struct kvec in_iov, out_iov[3] = {};
++ struct kvec in_iov[2], out_iov[3] = {};
+ int out_buftype[3] = {};
++ int cmds[2] = { SMB2_OP_QUERY_INFO, };
+ bool islink;
+- int cmd = SMB2_OP_QUERY_INFO;
++ int i, num_cmds;
+ int rc, rc2;
+
+ data->adjust_tz = false;
+@@ -626,14 +686,16 @@ int smb2_query_path_info(const unsigned int xid,
+ return rc;
+ }
+
+- in_iov.iov_base = data;
+- in_iov.iov_len = sizeof(*data);
++ in_iov[0].iov_base = data;
++ in_iov[0].iov_len = sizeof(*data);
++ in_iov[1] = in_iov[0];
+
+ cifs_get_readable_path(tcon, full_path, &cfile);
+ rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
+ FILE_READ_ATTRIBUTES, FILE_OPEN,
+- create_options, ACL_NO_MODE, &in_iov,
+- &cmd, 1, cfile, NULL, NULL, out_iov, out_buftype);
++ create_options, ACL_NO_MODE,
++ in_iov, cmds, 1, cfile,
++ NULL, NULL, out_iov, out_buftype);
+ hdr = out_iov[0].iov_base;
+ /*
+ * If first iov is unset, then SMB session was dropped or we've got a
+@@ -649,13 +711,19 @@ int smb2_query_path_info(const unsigned int xid,
+ if (rc || !data->reparse_point)
+ goto out;
+
++ if (data->reparse.tag == IO_REPARSE_TAG_SYMLINK) {
++ /* symlink already parsed in create response */
++ num_cmds = 1;
++ } else {
++ cmds[1] = SMB2_OP_GET_REPARSE;
++ num_cmds = 2;
++ }
+ create_options |= OPEN_REPARSE_POINT;
+- /* Failed on a symbolic link - query a reparse point info */
+ cifs_get_readable_path(tcon, full_path, &cfile);
+ rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
+ FILE_READ_ATTRIBUTES, FILE_OPEN,
+- create_options, ACL_NO_MODE, &in_iov,
+- &cmd, 1, cfile, NULL, NULL, NULL, NULL);
++ create_options, ACL_NO_MODE, in_iov, cmds,
++ num_cmds, cfile, NULL, NULL, NULL, NULL);
+ break;
+ case -EREMOTE:
+ break;
+@@ -673,9 +741,8 @@ int smb2_query_path_info(const unsigned int xid,
+ }
+
+ out:
+- free_rsp_buf(out_buftype[0], out_iov[0].iov_base);
+- free_rsp_buf(out_buftype[1], out_iov[1].iov_base);
+- free_rsp_buf(out_buftype[2], out_iov[2].iov_base);
++ for (i = 0; i < ARRAY_SIZE(out_buftype); i++)
++ free_rsp_buf(out_buftype[i], out_iov[i].iov_base);
+ return rc;
+ }
+
+@@ -690,13 +757,14 @@ int smb311_posix_query_path_info(const unsigned int xid,
+ int rc;
+ __u32 create_options = 0;
+ struct cifsFileInfo *cfile;
+- struct kvec in_iov, out_iov[3] = {};
++ struct kvec in_iov[2], out_iov[3] = {};
+ int out_buftype[3] = {};
+ __u8 *sidsbuf = NULL;
+ __u8 *sidsbuf_end = NULL;
+ size_t sidsbuflen = 0;
+ size_t owner_len, group_len;
+- int cmd = SMB2_OP_POSIX_QUERY_INFO;
++ int cmds[2] = { SMB2_OP_POSIX_QUERY_INFO, };
++ int i, num_cmds;
+
+ data->adjust_tz = false;
+ data->reparse_point = false;
+@@ -707,13 +775,14 @@ int smb311_posix_query_path_info(const unsigned int xid,
+ * when we already have an open file handle for this. For now this is fast enough
+ * (always using the compounded version).
+ */
+- in_iov.iov_base = data;
+- in_iov.iov_len = sizeof(*data);
++ in_iov[0].iov_base = data;
++ in_iov[0].iov_len = sizeof(*data);
++ in_iov[1] = in_iov[0];
+
+ cifs_get_readable_path(tcon, full_path, &cfile);
+ rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
+ FILE_READ_ATTRIBUTES, FILE_OPEN,
+- create_options, ACL_NO_MODE, &in_iov, &cmd, 1,
++ create_options, ACL_NO_MODE, in_iov, cmds, 1,
+ cfile, &sidsbuf, &sidsbuflen, out_iov, out_buftype);
+ /*
+ * If first iov is unset, then SMB session was dropped or we've got a
+@@ -730,13 +799,19 @@ int smb311_posix_query_path_info(const unsigned int xid,
+ if (rc || !data->reparse_point)
+ goto out;
+
++ if (data->reparse.tag == IO_REPARSE_TAG_SYMLINK) {
++ /* symlink already parsed in create response */
++ num_cmds = 1;
++ } else {
++ cmds[1] = SMB2_OP_GET_REPARSE;
++ num_cmds = 2;
++ }
+ create_options |= OPEN_REPARSE_POINT;
+- /* Failed on a symbolic link - query a reparse point info */
+ cifs_get_readable_path(tcon, full_path, &cfile);
+ rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
+ FILE_READ_ATTRIBUTES, FILE_OPEN,
+- create_options, ACL_NO_MODE, &in_iov, &cmd, 1,
+- cfile, &sidsbuf, &sidsbuflen, NULL, NULL);
++ create_options, ACL_NO_MODE, in_iov, cmds,
++ num_cmds, cfile, &sidsbuf, &sidsbuflen, NULL, NULL);
+ break;
+ }
+
+@@ -761,9 +836,8 @@ int smb311_posix_query_path_info(const unsigned int xid,
+ }
+
+ kfree(sidsbuf);
+- free_rsp_buf(out_buftype[0], out_iov[0].iov_base);
+- free_rsp_buf(out_buftype[1], out_iov[1].iov_base);
+- free_rsp_buf(out_buftype[2], out_iov[2].iov_base);
++ for (i = 0; i < ARRAY_SIZE(out_buftype); i++)
++ free_rsp_buf(out_buftype[i], out_iov[i].iov_base);
+ return rc;
+ }
+
+diff --git a/fs/smb/client/trace.h b/fs/smb/client/trace.h
+index 34f507584274b..522fa387fcfd7 100644
+--- a/fs/smb/client/trace.h
++++ b/fs/smb/client/trace.h
+@@ -371,6 +371,7 @@ DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(rmdir_enter);
+ DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(set_eof_enter);
+ DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(set_info_compound_enter);
+ DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(set_reparse_compound_enter);
++DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(get_reparse_compound_enter);
+ DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(delete_enter);
+ DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(mkdir_enter);
+ DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(tdis_enter);
+@@ -409,6 +410,7 @@ DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(rmdir_done);
+ DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(set_eof_done);
+ DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(set_info_compound_done);
+ DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(set_reparse_compound_done);
++DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(get_reparse_compound_done);
+ DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(delete_done);
+ DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(mkdir_done);
+ DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(tdis_done);
+@@ -453,6 +455,7 @@ DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(rmdir_err);
+ DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(set_eof_err);
+ DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(set_info_compound_err);
+ DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(set_reparse_compound_err);
++DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(get_reparse_compound_err);
+ DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(mkdir_err);
+ DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(delete_err);
+ DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(tdis_err);
+--
+2.43.0
+
diff --git a/queue-6.6/smb-client-parse-owner-group-when-creating-reparse-p.patch b/queue-6.6/smb-client-parse-owner-group-when-creating-reparse-p.patch
new file mode 100644
index 0000000000..fc76fffffd
--- /dev/null
+++ b/queue-6.6/smb-client-parse-owner-group-when-creating-reparse-p.patch
@@ -0,0 +1,414 @@
+From 78231c59be9f76c87cbad6b455b8fe90244e4c6d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 1 May 2024 00:35:20 -0500
+Subject: smb: client: parse owner/group when creating reparse points
+
+From: Steve French <stfrench@microsoft.com>
+
+[ Upstream commit 858e74876c5cbff1dfd5bace99e32fbce2abd4b5 ]
+
+Parse owner/group when creating special files and symlinks under
+SMB3.1.1 POSIX mounts.
+
+Move the parsing of owner/group to smb2_compound_op() so we don't have
+to duplicate it in both smb2_get_reparse_inode() and
+smb311_posix_query_path_info().
+
+Signed-off-by: Paulo Alcantara <pc@manguebit.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/cifsglob.h | 2 +
+ fs/smb/client/inode.c | 25 +++-----
+ fs/smb/client/smb2inode.c | 130 ++++++++++++++++++--------------------
+ fs/smb/client/smb2proto.h | 4 +-
+ 4 files changed, 71 insertions(+), 90 deletions(-)
+
+diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h
+index 181e9d5b10f92..678a9c671cdcd 100644
+--- a/fs/smb/client/cifsglob.h
++++ b/fs/smb/client/cifsglob.h
+@@ -209,6 +209,8 @@ struct cifs_open_info_data {
+ };
+ } reparse;
+ char *symlink_target;
++ struct cifs_sid posix_owner;
++ struct cifs_sid posix_group;
+ union {
+ struct smb2_file_all_info fi;
+ struct smb311_posix_qinfo posix_fi;
+diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c
+index b8260ace2bee9..0110589acb853 100644
+--- a/fs/smb/client/inode.c
++++ b/fs/smb/client/inode.c
+@@ -666,8 +666,6 @@ static int cifs_sfu_mode(struct cifs_fattr *fattr, const unsigned char *path,
+ /* Fill a cifs_fattr struct with info from POSIX info struct */
+ static void smb311_posix_info_to_fattr(struct cifs_fattr *fattr,
+ struct cifs_open_info_data *data,
+- struct cifs_sid *owner,
+- struct cifs_sid *group,
+ struct super_block *sb)
+ {
+ struct smb311_posix_qinfo *info = &data->posix_fi;
+@@ -723,8 +721,8 @@ static void smb311_posix_info_to_fattr(struct cifs_fattr *fattr,
+ fattr->cf_symlink_target = data->symlink_target;
+ data->symlink_target = NULL;
+ }
+- sid_to_id(cifs_sb, owner, fattr, SIDOWNER);
+- sid_to_id(cifs_sb, group, fattr, SIDGROUP);
++ sid_to_id(cifs_sb, &data->posix_owner, fattr, SIDOWNER);
++ sid_to_id(cifs_sb, &data->posix_group, fattr, SIDGROUP);
+
+ cifs_dbg(FYI, "POSIX query info: mode 0x%x uniqueid 0x%llx nlink %d\n",
+ fattr->cf_mode, fattr->cf_uniqueid, fattr->cf_nlink);
+@@ -1071,9 +1069,7 @@ static int reparse_info_to_fattr(struct cifs_open_info_data *data,
+ const unsigned int xid,
+ struct cifs_tcon *tcon,
+ const char *full_path,
+- struct cifs_fattr *fattr,
+- struct cifs_sid *owner,
+- struct cifs_sid *group)
++ struct cifs_fattr *fattr)
+ {
+ struct TCP_Server_Info *server = tcon->ses->server;
+ struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
+@@ -1118,7 +1114,7 @@ static int reparse_info_to_fattr(struct cifs_open_info_data *data,
+ }
+
+ if (tcon->posix_extensions)
+- smb311_posix_info_to_fattr(fattr, data, owner, group, sb);
++ smb311_posix_info_to_fattr(fattr, data, sb);
+ else
+ cifs_open_info_to_fattr(fattr, data, sb);
+ out:
+@@ -1172,8 +1168,7 @@ static int cifs_get_fattr(struct cifs_open_info_data *data,
+ */
+ if (cifs_open_data_reparse(data)) {
+ rc = reparse_info_to_fattr(data, sb, xid, tcon,
+- full_path, fattr,
+- NULL, NULL);
++ full_path, fattr);
+ } else {
+ cifs_open_info_to_fattr(fattr, data, sb);
+ }
+@@ -1321,7 +1316,6 @@ static int smb311_posix_get_fattr(struct cifs_open_info_data *data,
+ struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
+ struct cifs_tcon *tcon;
+ struct tcon_link *tlink;
+- struct cifs_sid owner, group;
+ int tmprc;
+ int rc = 0;
+
+@@ -1335,8 +1329,7 @@ static int smb311_posix_get_fattr(struct cifs_open_info_data *data,
+ */
+ if (!data) {
+ rc = smb311_posix_query_path_info(xid, tcon, cifs_sb,
+- full_path, &tmp_data,
+- &owner, &group);
++ full_path, &tmp_data);
+ data = &tmp_data;
+ }
+
+@@ -1348,11 +1341,9 @@ static int smb311_posix_get_fattr(struct cifs_open_info_data *data,
+ case 0:
+ if (cifs_open_data_reparse(data)) {
+ rc = reparse_info_to_fattr(data, sb, xid, tcon,
+- full_path, fattr,
+- &owner, &group);
++ full_path, fattr);
+ } else {
+- smb311_posix_info_to_fattr(fattr, data,
+- &owner, &group, sb);
++ smb311_posix_info_to_fattr(fattr, data, sb);
+ }
+ break;
+ case -EREMOTE:
+diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c
+index 94df328a1965d..4cd4b8a63316d 100644
+--- a/fs/smb/client/smb2inode.c
++++ b/fs/smb/client/smb2inode.c
+@@ -56,6 +56,35 @@ static struct reparse_data_buffer *reparse_buf_ptr(struct kvec *iov)
+ return buf;
+ }
+
++/* Parse owner and group from SMB3.1.1 POSIX query info */
++static int parse_posix_sids(struct cifs_open_info_data *data,
++ struct kvec *rsp_iov)
++{
++ struct smb2_query_info_rsp *qi = rsp_iov->iov_base;
++ unsigned int out_len = le32_to_cpu(qi->OutputBufferLength);
++ unsigned int qi_len = sizeof(data->posix_fi);
++ int owner_len, group_len;
++ u8 *sidsbuf, *sidsbuf_end;
++
++ if (out_len <= qi_len)
++ return -EINVAL;
++
++ sidsbuf = (u8 *)qi + le16_to_cpu(qi->OutputBufferOffset) + qi_len;
++ sidsbuf_end = sidsbuf + out_len - qi_len;
++
++ owner_len = posix_info_sid_size(sidsbuf, sidsbuf_end);
++ if (owner_len == -1)
++ return -EINVAL;
++
++ memcpy(&data->posix_owner, sidsbuf, owner_len);
++ group_len = posix_info_sid_size(sidsbuf + owner_len, sidsbuf_end);
++ if (group_len == -1)
++ return -EINVAL;
++
++ memcpy(&data->posix_group, sidsbuf + owner_len, group_len);
++ return 0;
++}
++
+ /*
+ * note: If cfile is passed, the reference to it is dropped here.
+ * So make sure that you do not reuse cfile after return from this func.
+@@ -69,7 +98,6 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
+ __u32 desired_access, __u32 create_disposition,
+ __u32 create_options, umode_t mode, struct kvec *in_iov,
+ int *cmds, int num_cmds, struct cifsFileInfo *cfile,
+- __u8 **extbuf, size_t *extbuflen,
+ struct kvec *out_iov, int *out_buftype)
+ {
+
+@@ -509,21 +537,9 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
+ &rsp_iov[i + 1], sizeof(idata->posix_fi) /* add SIDs */,
+ (char *)&idata->posix_fi);
+ }
+- if (rc == 0) {
+- unsigned int length = le32_to_cpu(qi_rsp->OutputBufferLength);
+-
+- if (length > sizeof(idata->posix_fi)) {
+- char *base = (char *)rsp_iov[i + 1].iov_base +
+- le16_to_cpu(qi_rsp->OutputBufferOffset) +
+- sizeof(idata->posix_fi);
+- *extbuflen = length - sizeof(idata->posix_fi);
+- *extbuf = kmemdup(base, *extbuflen, GFP_KERNEL);
+- if (!*extbuf)
+- rc = -ENOMEM;
+- } else {
+- rc = -EINVAL;
+- }
+- }
++ if (rc == 0)
++ rc = parse_posix_sids(idata, &rsp_iov[i + 1]);
++
+ SMB2_query_info_free(&rqst[num_rqst++]);
+ if (rc)
+ trace_smb3_posix_query_info_compound_err(xid, ses->Suid,
+@@ -714,9 +730,8 @@ int smb2_query_path_info(const unsigned int xid,
+ cifs_get_readable_path(tcon, full_path, &cfile);
+ rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
+ FILE_READ_ATTRIBUTES, FILE_OPEN,
+- create_options, ACL_NO_MODE,
+- in_iov, cmds, 1, cfile,
+- NULL, NULL, out_iov, out_buftype);
++ create_options, ACL_NO_MODE, in_iov,
++ cmds, 1, cfile, out_iov, out_buftype);
+ hdr = out_iov[0].iov_base;
+ /*
+ * If first iov is unset, then SMB session was dropped or we've got a
+@@ -743,8 +758,8 @@ int smb2_query_path_info(const unsigned int xid,
+ cifs_get_readable_path(tcon, full_path, &cfile);
+ rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
+ FILE_READ_ATTRIBUTES, FILE_OPEN,
+- create_options, ACL_NO_MODE, in_iov, cmds,
+- num_cmds, cfile, NULL, NULL, NULL, NULL);
++ create_options, ACL_NO_MODE, in_iov,
++ cmds, num_cmds, cfile, NULL, NULL);
+ break;
+ case -EREMOTE:
+ break;
+@@ -771,19 +786,13 @@ int smb311_posix_query_path_info(const unsigned int xid,
+ struct cifs_tcon *tcon,
+ struct cifs_sb_info *cifs_sb,
+ const char *full_path,
+- struct cifs_open_info_data *data,
+- struct cifs_sid *owner,
+- struct cifs_sid *group)
++ struct cifs_open_info_data *data)
+ {
+ int rc;
+ __u32 create_options = 0;
+ struct cifsFileInfo *cfile;
+ struct kvec in_iov[2], out_iov[3] = {};
+ int out_buftype[3] = {};
+- __u8 *sidsbuf = NULL;
+- __u8 *sidsbuf_end = NULL;
+- size_t sidsbuflen = 0;
+- size_t owner_len, group_len;
+ int cmds[2] = { SMB2_OP_POSIX_QUERY_INFO, };
+ int i, num_cmds;
+
+@@ -803,8 +812,8 @@ int smb311_posix_query_path_info(const unsigned int xid,
+ cifs_get_readable_path(tcon, full_path, &cfile);
+ rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
+ FILE_READ_ATTRIBUTES, FILE_OPEN,
+- create_options, ACL_NO_MODE, in_iov, cmds, 1,
+- cfile, &sidsbuf, &sidsbuflen, out_iov, out_buftype);
++ create_options, ACL_NO_MODE, in_iov,
++ cmds, 1, cfile, out_iov, out_buftype);
+ /*
+ * If first iov is unset, then SMB session was dropped or we've got a
+ * cached open file (@cfile).
+@@ -831,32 +840,12 @@ int smb311_posix_query_path_info(const unsigned int xid,
+ cifs_get_readable_path(tcon, full_path, &cfile);
+ rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
+ FILE_READ_ATTRIBUTES, FILE_OPEN,
+- create_options, ACL_NO_MODE, in_iov, cmds,
+- num_cmds, cfile, &sidsbuf, &sidsbuflen, NULL, NULL);
++ create_options, ACL_NO_MODE, in_iov,
++ cmds, num_cmds, cfile, NULL, NULL);
+ break;
+ }
+
+ out:
+- if (rc == 0) {
+- sidsbuf_end = sidsbuf + sidsbuflen;
+-
+- owner_len = posix_info_sid_size(sidsbuf, sidsbuf_end);
+- if (owner_len == -1) {
+- rc = -EINVAL;
+- goto out;
+- }
+- memcpy(owner, sidsbuf, owner_len);
+-
+- group_len = posix_info_sid_size(
+- sidsbuf + owner_len, sidsbuf_end);
+- if (group_len == -1) {
+- rc = -EINVAL;
+- goto out;
+- }
+- memcpy(group, sidsbuf + owner_len, group_len);
+- }
+-
+- kfree(sidsbuf);
+ for (i = 0; i < ARRAY_SIZE(out_buftype); i++)
+ free_rsp_buf(out_buftype[i], out_iov[i].iov_base);
+ return rc;
+@@ -869,9 +858,9 @@ smb2_mkdir(const unsigned int xid, struct inode *parent_inode, umode_t mode,
+ {
+ return smb2_compound_op(xid, tcon, cifs_sb, name,
+ FILE_WRITE_ATTRIBUTES, FILE_CREATE,
+- CREATE_NOT_FILE, mode, NULL,
+- &(int){SMB2_OP_MKDIR}, 1,
+- NULL, NULL, NULL, NULL, NULL);
++ CREATE_NOT_FILE, mode,
++ NULL, &(int){SMB2_OP_MKDIR}, 1,
++ NULL, NULL, NULL);
+ }
+
+ void
+@@ -896,7 +885,7 @@ smb2_mkdir_setinfo(struct inode *inode, const char *name,
+ FILE_WRITE_ATTRIBUTES, FILE_CREATE,
+ CREATE_NOT_FILE, ACL_NO_MODE, &in_iov,
+ &(int){SMB2_OP_SET_INFO}, 1,
+- cfile, NULL, NULL, NULL, NULL);
++ cfile, NULL, NULL);
+ if (tmprc == 0)
+ cifs_i->cifsAttrs = dosattrs;
+ }
+@@ -908,8 +897,9 @@ smb2_rmdir(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
+ drop_cached_dir_by_name(xid, tcon, name, cifs_sb);
+ return smb2_compound_op(xid, tcon, cifs_sb, name,
+ DELETE, FILE_OPEN, CREATE_NOT_FILE,
+- ACL_NO_MODE, NULL, &(int){SMB2_OP_RMDIR}, 1,
+- NULL, NULL, NULL, NULL, NULL);
++ ACL_NO_MODE, NULL,
++ &(int){SMB2_OP_RMDIR}, 1,
++ NULL, NULL, NULL);
+ }
+
+ int
+@@ -918,8 +908,9 @@ smb2_unlink(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
+ {
+ return smb2_compound_op(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN,
+ CREATE_DELETE_ON_CLOSE | OPEN_REPARSE_POINT,
+- ACL_NO_MODE, NULL, &(int){SMB2_OP_DELETE}, 1,
+- NULL, NULL, NULL, NULL, NULL);
++ ACL_NO_MODE, NULL,
++ &(int){SMB2_OP_DELETE}, 1,
++ NULL, NULL, NULL);
+ }
+
+ static int smb2_set_path_attr(const unsigned int xid, struct cifs_tcon *tcon,
+@@ -939,10 +930,9 @@ static int smb2_set_path_attr(const unsigned int xid, struct cifs_tcon *tcon,
+ }
+ in_iov.iov_base = smb2_to_name;
+ in_iov.iov_len = 2 * UniStrnlen((wchar_t *)smb2_to_name, PATH_MAX);
+-
+ rc = smb2_compound_op(xid, tcon, cifs_sb, from_name, access,
+- FILE_OPEN, 0, ACL_NO_MODE, &in_iov,
+- &command, 1, cfile, NULL, NULL, NULL, NULL);
++ FILE_OPEN, create_options, ACL_NO_MODE,
++ &in_iov, &command, 1, cfile, NULL, NULL);
+ smb2_rename_path:
+ kfree(smb2_to_name);
+ return rc;
+@@ -993,7 +983,7 @@ smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon,
+ FILE_WRITE_DATA, FILE_OPEN,
+ 0, ACL_NO_MODE, &in_iov,
+ &(int){SMB2_OP_SET_EOF}, 1,
+- cfile, NULL, NULL, NULL, NULL);
++ cfile, NULL, NULL);
+ }
+
+ int
+@@ -1021,8 +1011,8 @@ smb2_set_file_info(struct inode *inode, const char *full_path,
+ rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
+ FILE_WRITE_ATTRIBUTES, FILE_OPEN,
+ 0, ACL_NO_MODE, &in_iov,
+- &(int){SMB2_OP_SET_INFO}, 1, cfile,
+- NULL, NULL, NULL, NULL);
++ &(int){SMB2_OP_SET_INFO}, 1,
++ cfile, NULL, NULL);
+ cifs_put_tlink(tlink);
+ return rc;
+ }
+@@ -1057,7 +1047,7 @@ struct inode *smb2_get_reparse_inode(struct cifs_open_info_data *data,
+ cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile);
+ rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
+ da, cd, co, ACL_NO_MODE, in_iov,
+- cmds, 2, cfile, NULL, NULL, NULL, NULL);
++ cmds, 2, cfile, NULL, NULL);
+ if (!rc) {
+ rc = smb311_posix_get_inode_info(&new, full_path,
+ data, sb, xid);
+@@ -1067,7 +1057,7 @@ struct inode *smb2_get_reparse_inode(struct cifs_open_info_data *data,
+ cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile);
+ rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
+ da, cd, co, ACL_NO_MODE, in_iov,
+- cmds, 2, cfile, NULL, NULL, NULL, NULL);
++ cmds, 2, cfile, NULL, NULL);
+ if (!rc) {
+ rc = cifs_get_inode_info(&new, full_path,
+ data, sb, xid, NULL);
+@@ -1094,8 +1084,8 @@ int smb2_query_reparse_point(const unsigned int xid,
+ rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
+ FILE_READ_ATTRIBUTES, FILE_OPEN,
+ OPEN_REPARSE_POINT, ACL_NO_MODE, &in_iov,
+- &(int){SMB2_OP_GET_REPARSE}, 1, cfile,
+- NULL, NULL, NULL, NULL);
++ &(int){SMB2_OP_GET_REPARSE}, 1,
++ cfile, NULL, NULL);
+ if (rc)
+ goto out;
+
+diff --git a/fs/smb/client/smb2proto.h b/fs/smb/client/smb2proto.h
+index 330e36c6b91f0..b3069911e9dd8 100644
+--- a/fs/smb/client/smb2proto.h
++++ b/fs/smb/client/smb2proto.h
+@@ -304,9 +304,7 @@ int smb311_posix_query_path_info(const unsigned int xid,
+ struct cifs_tcon *tcon,
+ struct cifs_sb_info *cifs_sb,
+ const char *full_path,
+- struct cifs_open_info_data *data,
+- struct cifs_sid *owner,
+- struct cifs_sid *group);
++ struct cifs_open_info_data *data);
+ int posix_info_parse(const void *beg, const void *end,
+ struct smb2_posix_info_parsed *out);
+ int posix_info_sid_size(const void *beg, const void *end);
+--
+2.43.0
+
diff --git a/queue-6.6/smb-client-parse-uid-gid-mode-and-dev-from-wsl-repar.patch b/queue-6.6/smb-client-parse-uid-gid-mode-and-dev-from-wsl-repar.patch
new file mode 100644
index 0000000000..0fd2c1efbd
--- /dev/null
+++ b/queue-6.6/smb-client-parse-uid-gid-mode-and-dev-from-wsl-repar.patch
@@ -0,0 +1,223 @@
+From a9ced8bf642069091702ea5e9635943c2b2aed29 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 28 Jan 2024 21:52:03 -0300
+Subject: smb: client: parse uid, gid, mode and dev from WSL reparse points
+
+From: Paulo Alcantara <pc@manguebit.com>
+
+[ Upstream commit 78e26bec4d6d3aef04276e28bed48a45fd00e116 ]
+
+Parse the extended attributes from WSL reparse points to correctly
+report uid, gid mode and dev from ther instantiated inodes.
+
+Signed-off-by: Paulo Alcantara <pc@manguebit.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/inode.c | 5 ++-
+ fs/smb/client/readdir.c | 2 ++
+ fs/smb/client/reparse.c | 78 +++++++++++++++++++++++++++++++++--------
+ fs/smb/client/reparse.h | 29 +++++++++++++++
+ 4 files changed, 97 insertions(+), 17 deletions(-)
+
+diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c
+index 8aff8382cfb54..67bc1a1e54fde 100644
+--- a/fs/smb/client/inode.c
++++ b/fs/smb/client/inode.c
+@@ -759,6 +759,8 @@ static void cifs_open_info_to_fattr(struct cifs_fattr *fattr,
+ fattr->cf_bytes = le64_to_cpu(info->AllocationSize);
+ fattr->cf_createtime = le64_to_cpu(info->CreationTime);
+ fattr->cf_nlink = le32_to_cpu(info->NumberOfLinks);
++ fattr->cf_uid = cifs_sb->ctx->linux_uid;
++ fattr->cf_gid = cifs_sb->ctx->linux_gid;
+
+ fattr->cf_mode = cifs_sb->ctx->file_mode;
+ if (cifs_open_data_reparse(data) &&
+@@ -801,9 +803,6 @@ static void cifs_open_info_to_fattr(struct cifs_fattr *fattr,
+ fattr->cf_symlink_target = data->symlink_target;
+ data->symlink_target = NULL;
+ }
+-
+- fattr->cf_uid = cifs_sb->ctx->linux_uid;
+- fattr->cf_gid = cifs_sb->ctx->linux_gid;
+ }
+
+ static int
+diff --git a/fs/smb/client/readdir.c b/fs/smb/client/readdir.c
+index 3e5d22b356e92..06111d9f39500 100644
+--- a/fs/smb/client/readdir.c
++++ b/fs/smb/client/readdir.c
+@@ -125,6 +125,8 @@ cifs_prime_dcache(struct dentry *parent, struct qstr *name,
+ if (likely(reparse_inode_match(inode, fattr))) {
+ fattr->cf_mode = inode->i_mode;
+ fattr->cf_rdev = inode->i_rdev;
++ fattr->cf_uid = inode->i_uid;
++ fattr->cf_gid = inode->i_gid;
+ fattr->cf_eof = CIFS_I(inode)->server_eof;
+ fattr->cf_symlink_target = NULL;
+ } else {
+diff --git a/fs/smb/client/reparse.c b/fs/smb/client/reparse.c
+index e8be756e6768c..29a47f20643b1 100644
+--- a/fs/smb/client/reparse.c
++++ b/fs/smb/client/reparse.c
+@@ -258,7 +258,9 @@ static int mknod_wsl(unsigned int xid, struct inode *inode,
+ {
+ struct cifs_open_info_data data;
+ struct reparse_data_buffer buf;
++ struct smb2_create_ea_ctx *cc;
+ struct inode *new;
++ unsigned int len;
+ struct kvec reparse_iov, xattr_iov;
+ int rc;
+
+@@ -275,6 +277,11 @@ static int mknod_wsl(unsigned int xid, struct inode *inode,
+ .reparse = { .tag = le32_to_cpu(buf.ReparseTag), .buf = &buf, },
+ };
+
++ cc = xattr_iov.iov_base;
++ len = le32_to_cpu(cc->ctx.DataLength);
++ memcpy(data.wsl.eas, &cc->ea, len);
++ data.wsl.eas_len = len;
++
+ new = smb2_get_reparse_inode(&data, inode->i_sb,
+ xid, tcon, full_path,
+ &reparse_iov, &xattr_iov);
+@@ -408,6 +415,62 @@ int smb2_parse_reparse_point(struct cifs_sb_info *cifs_sb,
+ return parse_reparse_point(buf, plen, cifs_sb, true, data);
+ }
+
++static void wsl_to_fattr(struct cifs_open_info_data *data,
++ struct cifs_sb_info *cifs_sb,
++ u32 tag, struct cifs_fattr *fattr)
++{
++ struct smb2_file_full_ea_info *ea;
++ u32 next = 0;
++
++ switch (tag) {
++ case IO_REPARSE_TAG_LX_SYMLINK:
++ fattr->cf_mode |= S_IFLNK;
++ break;
++ case IO_REPARSE_TAG_LX_FIFO:
++ fattr->cf_mode |= S_IFIFO;
++ break;
++ case IO_REPARSE_TAG_AF_UNIX:
++ fattr->cf_mode |= S_IFSOCK;
++ break;
++ case IO_REPARSE_TAG_LX_CHR:
++ fattr->cf_mode |= S_IFCHR;
++ break;
++ case IO_REPARSE_TAG_LX_BLK:
++ fattr->cf_mode |= S_IFBLK;
++ break;
++ }
++
++ if (!data->wsl.eas_len)
++ goto out;
++
++ ea = (struct smb2_file_full_ea_info *)data->wsl.eas;
++ do {
++ const char *name;
++ void *v;
++ u8 nlen;
++
++ ea = (void *)((u8 *)ea + next);
++ next = le32_to_cpu(ea->next_entry_offset);
++ if (!le16_to_cpu(ea->ea_value_length))
++ continue;
++
++ name = ea->ea_data;
++ nlen = ea->ea_name_length;
++ v = (void *)((u8 *)ea->ea_data + ea->ea_name_length + 1);
++
++ if (!strncmp(name, SMB2_WSL_XATTR_UID, nlen))
++ fattr->cf_uid = wsl_make_kuid(cifs_sb, v);
++ else if (!strncmp(name, SMB2_WSL_XATTR_GID, nlen))
++ fattr->cf_gid = wsl_make_kgid(cifs_sb, v);
++ else if (!strncmp(name, SMB2_WSL_XATTR_MODE, nlen))
++ fattr->cf_mode = (umode_t)le32_to_cpu(*(__le32 *)v);
++ else if (!strncmp(name, SMB2_WSL_XATTR_DEV, nlen))
++ fattr->cf_rdev = wsl_mkdev(v);
++ } while (next);
++out:
++ fattr->cf_dtype = S_DT(fattr->cf_mode);
++}
++
+ bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb,
+ struct cifs_fattr *fattr,
+ struct cifs_open_info_data *data)
+@@ -448,24 +511,11 @@ bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb,
+
+ switch (tag) {
+ case IO_REPARSE_TAG_LX_SYMLINK:
+- fattr->cf_mode |= S_IFLNK;
+- fattr->cf_dtype = DT_LNK;
+- break;
+ case IO_REPARSE_TAG_LX_FIFO:
+- fattr->cf_mode |= S_IFIFO;
+- fattr->cf_dtype = DT_FIFO;
+- break;
+ case IO_REPARSE_TAG_AF_UNIX:
+- fattr->cf_mode |= S_IFSOCK;
+- fattr->cf_dtype = DT_SOCK;
+- break;
+ case IO_REPARSE_TAG_LX_CHR:
+- fattr->cf_mode |= S_IFCHR;
+- fattr->cf_dtype = DT_CHR;
+- break;
+ case IO_REPARSE_TAG_LX_BLK:
+- fattr->cf_mode |= S_IFBLK;
+- fattr->cf_dtype = DT_BLK;
++ wsl_to_fattr(data, cifs_sb, tag, fattr);
+ break;
+ case 0: /* SMB1 symlink */
+ case IO_REPARSE_TAG_SYMLINK:
+diff --git a/fs/smb/client/reparse.h b/fs/smb/client/reparse.h
+index 9816bac985525..6b55d1df9e2f8 100644
+--- a/fs/smb/client/reparse.h
++++ b/fs/smb/client/reparse.h
+@@ -8,6 +8,8 @@
+
+ #include <linux/fs.h>
+ #include <linux/stat.h>
++#include <linux/uidgid.h>
++#include "fs_context.h"
+ #include "cifsglob.h"
+
+ static inline dev_t reparse_nfs_mkdev(struct reparse_posix_data *buf)
+@@ -17,6 +19,33 @@ static inline dev_t reparse_nfs_mkdev(struct reparse_posix_data *buf)
+ return MKDEV(v >> 32, v & 0xffffffff);
+ }
+
++static inline dev_t wsl_mkdev(void *ptr)
++{
++ u64 v = le64_to_cpu(*(__le64 *)ptr);
++
++ return MKDEV(v & 0xffffffff, v >> 32);
++}
++
++static inline kuid_t wsl_make_kuid(struct cifs_sb_info *cifs_sb,
++ void *ptr)
++{
++ u32 uid = le32_to_cpu(*(__le32 *)ptr);
++
++ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID)
++ return cifs_sb->ctx->linux_uid;
++ return make_kuid(current_user_ns(), uid);
++}
++
++static inline kgid_t wsl_make_kgid(struct cifs_sb_info *cifs_sb,
++ void *ptr)
++{
++ u32 gid = le32_to_cpu(*(__le32 *)ptr);
++
++ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID)
++ return cifs_sb->ctx->linux_gid;
++ return make_kgid(current_user_ns(), gid);
++}
++
+ static inline u64 reparse_mode_nfs_type(mode_t mode)
+ {
+ switch (mode & S_IFMT) {
+--
+2.43.0
+
diff --git a/queue-6.6/smb-client-reduce-number-of-parameters-in-smb2_compo.patch b/queue-6.6/smb-client-reduce-number-of-parameters-in-smb2_compo.patch
new file mode 100644
index 0000000000..ee7afd4014
--- /dev/null
+++ b/queue-6.6/smb-client-reduce-number-of-parameters-in-smb2_compo.patch
@@ -0,0 +1,367 @@
+From 27fa69b649b2242dc35955ca5f99618344c4203a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 25 Jan 2024 19:21:48 -0300
+Subject: smb: client: reduce number of parameters in smb2_compound_op()
+
+From: Paulo Alcantara <pc@manguebit.com>
+
+[ Upstream commit fa792d8d235c20df5f422e4bd172db1efde55ab9 ]
+
+Replace @desired_access, @create_disposition, @create_options and
+@mode parameters with a single @oparms.
+
+No functional changes.
+
+Signed-off-by: Paulo Alcantara <pc@manguebit.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/cifsglob.h | 11 +++
+ fs/smb/client/smb2inode.c | 153 +++++++++++++++++++++-----------------
+ 2 files changed, 95 insertions(+), 69 deletions(-)
+
+diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h
+index 296ed556be0e2..5a902fb20ac96 100644
+--- a/fs/smb/client/cifsglob.h
++++ b/fs/smb/client/cifsglob.h
+@@ -2281,6 +2281,17 @@ static inline void cifs_sg_set_buf(struct sg_table *sgtable,
+ }
+ }
+
++#define CIFS_OPARMS(_cifs_sb, _tcon, _path, _da, _cd, _co, _mode) \
++ ((struct cifs_open_parms) { \
++ .tcon = _tcon, \
++ .path = _path, \
++ .desired_access = (_da), \
++ .disposition = (_cd), \
++ .create_options = cifs_create_options(_cifs_sb, (_co)), \
++ .mode = (_mode), \
++ .cifs_sb = _cifs_sb, \
++ })
++
+ struct smb2_compound_vars {
+ struct cifs_open_parms oparms;
+ struct kvec rsp_iov[MAX_COMPOUND];
+diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c
+index 33f3fffcb8277..e1ad54b27b63e 100644
+--- a/fs/smb/client/smb2inode.c
++++ b/fs/smb/client/smb2inode.c
+@@ -95,8 +95,7 @@ static int parse_posix_sids(struct cifs_open_info_data *data,
+ */
+ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
+ struct cifs_sb_info *cifs_sb, const char *full_path,
+- __u32 desired_access, __u32 create_disposition,
+- __u32 create_options, umode_t mode, struct kvec *in_iov,
++ struct cifs_open_parms *oparms, struct kvec *in_iov,
+ int *cmds, int num_cmds, struct cifsFileInfo *cfile,
+ struct kvec *out_iov, int *out_buftype, struct dentry *dentry)
+ {
+@@ -173,16 +172,8 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
+ }
+ }
+
+- vars->oparms = (struct cifs_open_parms) {
+- .tcon = tcon,
+- .path = full_path,
+- .desired_access = desired_access,
+- .disposition = create_disposition,
+- .create_options = cifs_create_options(cifs_sb, create_options),
+- .fid = &fid,
+- .mode = mode,
+- .cifs_sb = cifs_sb,
+- };
++ vars->oparms = *oparms;
++ vars->oparms.fid = &fid;
+
+ rqst[num_rqst].rq_iov = &vars->open_iov[0];
+ rqst[num_rqst].rq_nvec = SMB2_CREATE_IOV_SIZE;
+@@ -741,6 +732,7 @@ int smb2_query_path_info(const unsigned int xid,
+ const char *full_path,
+ struct cifs_open_info_data *data)
+ {
++ struct cifs_open_parms oparms;
+ __u32 create_options = 0;
+ struct cifsFileInfo *cfile;
+ struct cached_fid *cfid = NULL;
+@@ -792,10 +784,11 @@ int smb2_query_path_info(const unsigned int xid,
+ in_iov[1] = in_iov[0];
+
+ cifs_get_readable_path(tcon, full_path, &cfile);
++ oparms = CIFS_OPARMS(cifs_sb, tcon, full_path, FILE_READ_ATTRIBUTES,
++ FILE_OPEN, create_options, ACL_NO_MODE);
+ rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
+- FILE_READ_ATTRIBUTES, FILE_OPEN,
+- create_options, ACL_NO_MODE, in_iov,
+- cmds, 1, cfile, out_iov, out_buftype, NULL);
++ &oparms, in_iov, cmds, 1, cfile,
++ out_iov, out_buftype, NULL);
+ hdr = out_iov[0].iov_base;
+ /*
+ * If first iov is unset, then SMB session was dropped or we've got a
+@@ -822,12 +815,14 @@ int smb2_query_path_info(const unsigned int xid,
+ cmds[1] = SMB2_OP_GET_REPARSE;
+ num_cmds = 2;
+ }
+- create_options |= OPEN_REPARSE_POINT;
++ oparms = CIFS_OPARMS(cifs_sb, tcon, full_path,
++ FILE_READ_ATTRIBUTES, FILE_OPEN,
++ create_options | OPEN_REPARSE_POINT,
++ ACL_NO_MODE);
+ cifs_get_readable_path(tcon, full_path, &cfile);
+ rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
+- FILE_READ_ATTRIBUTES, FILE_OPEN,
+- create_options, ACL_NO_MODE, in_iov,
+- cmds, num_cmds, cfile, NULL, NULL, NULL);
++ &oparms, in_iov, cmds, num_cmds,
++ cfile, NULL, NULL, NULL);
+ break;
+ case -EREMOTE:
+ break;
+@@ -855,10 +850,13 @@ smb2_mkdir(const unsigned int xid, struct inode *parent_inode, umode_t mode,
+ struct cifs_tcon *tcon, const char *name,
+ struct cifs_sb_info *cifs_sb)
+ {
+- return smb2_compound_op(xid, tcon, cifs_sb, name,
+- FILE_WRITE_ATTRIBUTES, FILE_CREATE,
+- CREATE_NOT_FILE, mode,
+- NULL, &(int){SMB2_OP_MKDIR}, 1,
++ struct cifs_open_parms oparms;
++
++ oparms = CIFS_OPARMS(cifs_sb, tcon, name, FILE_WRITE_ATTRIBUTES,
++ FILE_CREATE, CREATE_NOT_FILE, mode);
++ return smb2_compound_op(xid, tcon, cifs_sb,
++ name, &oparms, NULL,
++ &(int){SMB2_OP_MKDIR}, 1,
+ NULL, NULL, NULL, NULL);
+ }
+
+@@ -867,6 +865,7 @@ smb2_mkdir_setinfo(struct inode *inode, const char *name,
+ struct cifs_sb_info *cifs_sb, struct cifs_tcon *tcon,
+ const unsigned int xid)
+ {
++ struct cifs_open_parms oparms;
+ FILE_BASIC_INFO data = {};
+ struct cifsInodeInfo *cifs_i;
+ struct cifsFileInfo *cfile;
+@@ -880,9 +879,10 @@ smb2_mkdir_setinfo(struct inode *inode, const char *name,
+ dosattrs = cifs_i->cifsAttrs | ATTR_READONLY;
+ data.Attributes = cpu_to_le32(dosattrs);
+ cifs_get_writable_path(tcon, name, FIND_WR_ANY, &cfile);
++ oparms = CIFS_OPARMS(cifs_sb, tcon, name, FILE_WRITE_ATTRIBUTES,
++ FILE_CREATE, CREATE_NOT_FILE, ACL_NO_MODE);
+ tmprc = smb2_compound_op(xid, tcon, cifs_sb, name,
+- FILE_WRITE_ATTRIBUTES, FILE_CREATE,
+- CREATE_NOT_FILE, ACL_NO_MODE, &in_iov,
++ &oparms, &in_iov,
+ &(int){SMB2_OP_SET_INFO}, 1,
+ cfile, NULL, NULL, NULL);
+ if (tmprc == 0)
+@@ -893,10 +893,13 @@ int
+ smb2_rmdir(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
+ struct cifs_sb_info *cifs_sb)
+ {
++ struct cifs_open_parms oparms;
++
+ drop_cached_dir_by_name(xid, tcon, name, cifs_sb);
+- return smb2_compound_op(xid, tcon, cifs_sb, name,
+- DELETE, FILE_OPEN, CREATE_NOT_FILE,
+- ACL_NO_MODE, NULL,
++ oparms = CIFS_OPARMS(cifs_sb, tcon, name, DELETE,
++ FILE_OPEN, CREATE_NOT_FILE, ACL_NO_MODE);
++ return smb2_compound_op(xid, tcon, cifs_sb,
++ name, &oparms, NULL,
+ &(int){SMB2_OP_RMDIR}, 1,
+ NULL, NULL, NULL, NULL);
+ }
+@@ -905,18 +908,20 @@ int
+ smb2_unlink(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
+ struct cifs_sb_info *cifs_sb, struct dentry *dentry)
+ {
+- int rc = smb2_compound_op(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN,
+- CREATE_DELETE_ON_CLOSE | OPEN_REPARSE_POINT,
+- ACL_NO_MODE, NULL,
+- &(int){SMB2_OP_DELETE}, 1,
+- NULL, NULL, NULL, dentry);
++ struct cifs_open_parms oparms;
++
++ oparms = CIFS_OPARMS(cifs_sb, tcon, name,
++ DELETE, FILE_OPEN,
++ CREATE_DELETE_ON_CLOSE | OPEN_REPARSE_POINT,
++ ACL_NO_MODE);
++ int rc = smb2_compound_op(xid, tcon, cifs_sb, name, &oparms,
++ NULL, &(int){SMB2_OP_DELETE}, 1,
++ NULL, NULL, NULL, dentry);
+ if (rc == -EINVAL) {
+ cifs_dbg(FYI, "invalid lease key, resending request without lease");
+- rc = smb2_compound_op(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN,
+- CREATE_DELETE_ON_CLOSE | OPEN_REPARSE_POINT,
+- ACL_NO_MODE, NULL,
+- &(int){SMB2_OP_DELETE}, 1,
+- NULL, NULL, NULL, NULL);
++ rc = smb2_compound_op(xid, tcon, cifs_sb, name, &oparms,
++ NULL, &(int){SMB2_OP_DELETE}, 1,
++ NULL, NULL, NULL, NULL);
+ }
+ return rc;
+ }
+@@ -928,6 +933,7 @@ static int smb2_set_path_attr(const unsigned int xid, struct cifs_tcon *tcon,
+ int command, struct cifsFileInfo *cfile,
+ struct dentry *dentry)
+ {
++ struct cifs_open_parms oparms;
+ struct kvec in_iov;
+ __le16 *smb2_to_name = NULL;
+ int rc;
+@@ -939,9 +945,11 @@ static int smb2_set_path_attr(const unsigned int xid, struct cifs_tcon *tcon,
+ }
+ in_iov.iov_base = smb2_to_name;
+ in_iov.iov_len = 2 * UniStrnlen((wchar_t *)smb2_to_name, PATH_MAX);
+- rc = smb2_compound_op(xid, tcon, cifs_sb, from_name, access,
+- FILE_OPEN, create_options, ACL_NO_MODE,
+- &in_iov, &command, 1, cfile, NULL, NULL, dentry);
++ oparms = CIFS_OPARMS(cifs_sb, tcon, from_name, access, FILE_OPEN,
++ create_options, ACL_NO_MODE);
++ rc = smb2_compound_op(xid, tcon, cifs_sb, from_name,
++ &oparms, &in_iov, &command, 1,
++ cfile, NULL, NULL, dentry);
+ smb2_rename_path:
+ kfree(smb2_to_name);
+ return rc;
+@@ -988,25 +996,28 @@ smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon,
+ struct cifs_sb_info *cifs_sb, bool set_alloc,
+ struct dentry *dentry)
+ {
++ struct cifs_open_parms oparms;
+ struct cifsFileInfo *cfile;
+ struct kvec in_iov;
+ __le64 eof = cpu_to_le64(size);
++ int rc;
+
+ in_iov.iov_base = &eof;
+ in_iov.iov_len = sizeof(eof);
+ cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile);
+- int rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
+- FILE_WRITE_DATA, FILE_OPEN,
+- 0, ACL_NO_MODE, &in_iov,
+- &(int){SMB2_OP_SET_EOF}, 1,
+- cfile, NULL, NULL, dentry);
++
++ oparms = CIFS_OPARMS(cifs_sb, tcon, full_path, FILE_WRITE_DATA,
++ FILE_OPEN, 0, ACL_NO_MODE);
++ rc = smb2_compound_op(xid, tcon, cifs_sb,
++ full_path, &oparms, &in_iov,
++ &(int){SMB2_OP_SET_EOF}, 1,
++ cfile, NULL, NULL, dentry);
+ if (rc == -EINVAL) {
+ cifs_dbg(FYI, "invalid lease key, resending request without lease");
+- rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
+- FILE_WRITE_DATA, FILE_OPEN,
+- 0, ACL_NO_MODE, &in_iov,
+- &(int){SMB2_OP_SET_EOF}, 1,
+- cfile, NULL, NULL, NULL);
++ rc = smb2_compound_op(xid, tcon, cifs_sb,
++ full_path, &oparms, &in_iov,
++ &(int){SMB2_OP_SET_EOF}, 1,
++ cfile, NULL, NULL, NULL);
+ }
+ return rc;
+ }
+@@ -1015,6 +1026,7 @@ int
+ smb2_set_file_info(struct inode *inode, const char *full_path,
+ FILE_BASIC_INFO *buf, const unsigned int xid)
+ {
++ struct cifs_open_parms oparms;
+ struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+ struct tcon_link *tlink;
+ struct cifs_tcon *tcon;
+@@ -1033,9 +1045,10 @@ smb2_set_file_info(struct inode *inode, const char *full_path,
+ tcon = tlink_tcon(tlink);
+
+ cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile);
+- rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
+- FILE_WRITE_ATTRIBUTES, FILE_OPEN,
+- 0, ACL_NO_MODE, &in_iov,
++ oparms = CIFS_OPARMS(cifs_sb, tcon, full_path, FILE_WRITE_ATTRIBUTES,
++ FILE_OPEN, 0, ACL_NO_MODE);
++ rc = smb2_compound_op(xid, tcon, cifs_sb,
++ full_path, &oparms, &in_iov,
+ &(int){SMB2_OP_SET_INFO}, 1,
+ cfile, NULL, NULL, NULL);
+ cifs_put_tlink(tlink);
+@@ -1049,19 +1062,21 @@ struct inode *smb2_get_reparse_inode(struct cifs_open_info_data *data,
+ const char *full_path,
+ struct kvec *iov)
+ {
++ struct cifs_open_parms oparms;
+ struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
+ struct cifsFileInfo *cfile;
+ struct inode *new = NULL;
+ struct kvec in_iov[2];
+ int cmds[2];
+- int da, co, cd;
+ int rc;
+
+- da = SYNCHRONIZE | DELETE |
+- FILE_READ_ATTRIBUTES |
+- FILE_WRITE_ATTRIBUTES;
+- co = CREATE_NOT_DIR | OPEN_REPARSE_POINT;
+- cd = FILE_CREATE;
++ oparms = CIFS_OPARMS(cifs_sb, tcon, full_path,
++ SYNCHRONIZE | DELETE |
++ FILE_READ_ATTRIBUTES |
++ FILE_WRITE_ATTRIBUTES,
++ FILE_CREATE,
++ CREATE_NOT_DIR | OPEN_REPARSE_POINT,
++ ACL_NO_MODE);
+ cmds[0] = SMB2_OP_SET_REPARSE;
+ in_iov[0] = *iov;
+ in_iov[1].iov_base = data;
+@@ -1070,9 +1085,8 @@ struct inode *smb2_get_reparse_inode(struct cifs_open_info_data *data,
+ if (tcon->posix_extensions) {
+ cmds[1] = SMB2_OP_POSIX_QUERY_INFO;
+ cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile);
+- rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
+- da, cd, co, ACL_NO_MODE, in_iov,
+- cmds, 2, cfile, NULL, NULL, NULL);
++ rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, &oparms,
++ in_iov, cmds, 2, cfile, NULL, NULL, NULL);
+ if (!rc) {
+ rc = smb311_posix_get_inode_info(&new, full_path,
+ data, sb, xid);
+@@ -1080,9 +1094,8 @@ struct inode *smb2_get_reparse_inode(struct cifs_open_info_data *data,
+ } else {
+ cmds[1] = SMB2_OP_QUERY_INFO;
+ cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile);
+- rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
+- da, cd, co, ACL_NO_MODE, in_iov,
+- cmds, 2, cfile, NULL, NULL, NULL);
++ rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, &oparms,
++ in_iov, cmds, 2, cfile, NULL, NULL, NULL);
+ if (!rc) {
+ rc = cifs_get_inode_info(&new, full_path,
+ data, sb, xid, NULL);
+@@ -1098,6 +1111,7 @@ int smb2_query_reparse_point(const unsigned int xid,
+ u32 *tag, struct kvec *rsp,
+ int *rsp_buftype)
+ {
++ struct cifs_open_parms oparms;
+ struct cifs_open_info_data data = {};
+ struct cifsFileInfo *cfile;
+ struct kvec in_iov = { .iov_base = &data, .iov_len = sizeof(data), };
+@@ -1106,9 +1120,10 @@ int smb2_query_reparse_point(const unsigned int xid,
+ cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path);
+
+ cifs_get_readable_path(tcon, full_path, &cfile);
+- rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
+- FILE_READ_ATTRIBUTES, FILE_OPEN,
+- OPEN_REPARSE_POINT, ACL_NO_MODE, &in_iov,
++ oparms = CIFS_OPARMS(cifs_sb, tcon, full_path, FILE_READ_ATTRIBUTES,
++ FILE_OPEN, OPEN_REPARSE_POINT, ACL_NO_MODE);
++ rc = smb2_compound_op(xid, tcon, cifs_sb,
++ full_path, &oparms, &in_iov,
+ &(int){SMB2_OP_GET_REPARSE}, 1,
+ cfile, NULL, NULL, NULL);
+ if (rc)
+--
+2.43.0
+
diff --git a/queue-6.6/smb-client-retry-compound-request-without-reusing-le.patch b/queue-6.6/smb-client-retry-compound-request-without-reusing-le.patch
new file mode 100644
index 0000000000..e0bb0218dc
--- /dev/null
+++ b/queue-6.6/smb-client-retry-compound-request-without-reusing-le.patch
@@ -0,0 +1,117 @@
+From f7b27bf257108d91df968ef5c156133a27fee23a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 5 Mar 2024 22:43:53 -0500
+Subject: smb: client: retry compound request without reusing lease
+
+From: Meetakshi Setiya <msetiya@microsoft.com>
+
+[ Upstream commit 71f15c90e785d1de4bcd65a279e7256684c25c0d ]
+
+There is a shortcoming in the current implementation of the file
+lease mechanism exposed when the lease keys were attempted to be
+reused for unlink, rename and set_path_size operations for a client. As
+per MS-SMB2, lease keys are associated with the file name. Linux smb
+client maintains lease keys with the inode. If the file has any hardlinks,
+it is possible that the lease for a file be wrongly reused for an
+operation on the hardlink or vice versa. In these cases, the mentioned
+compound operations fail with STATUS_INVALID_PARAMETER.
+This patch adds a fallback to the old mechanism of not sending any
+lease with these compound operations if the request with lease key fails
+with STATUS_INVALID_PARAMETER.
+Resending the same request without lease key should not hurt any
+functionality, but might impact performance especially in cases where
+the error is not because of the usage of wrong lease key and we might
+end up doing an extra roundtrip.
+
+Signed-off-by: Meetakshi Setiya <msetiya@microsoft.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/smb2inode.c | 41 ++++++++++++++++++++++++++++++++++++---
+ 1 file changed, 38 insertions(+), 3 deletions(-)
+
+diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c
+index 0b7b083352919..add90eb8fc165 100644
+--- a/fs/smb/client/smb2inode.c
++++ b/fs/smb/client/smb2inode.c
+@@ -154,6 +154,17 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
+ }
+
+ /* if there is an existing lease, reuse it */
++
++ /*
++ * note: files with hardlinks cause unexpected behaviour. As per MS-SMB2,
++ * lease keys are associated with the filepath. We are maintaining lease keys
++ * with the inode on the client. If the file has hardlinks, it is possible
++ * that the lease for a file be reused for an operation on its hardlink or
++ * vice versa.
++ * As a workaround, send request using an existing lease key and if the server
++ * returns STATUS_INVALID_PARAMETER, which maps to EINVAL, send the request
++ * again without the lease.
++ */
+ if (dentry) {
+ inode = d_inode(dentry);
+ if (CIFS_I(inode)->lease_granted && server->ops->get_lease_key) {
+@@ -874,11 +885,20 @@ int
+ smb2_unlink(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
+ struct cifs_sb_info *cifs_sb, struct dentry *dentry)
+ {
+- return smb2_compound_op(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN,
++ int rc = smb2_compound_op(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN,
+ CREATE_DELETE_ON_CLOSE | OPEN_REPARSE_POINT,
+ ACL_NO_MODE, NULL,
+ &(int){SMB2_OP_DELETE}, 1,
+ NULL, NULL, NULL, dentry);
++ if (rc == -EINVAL) {
++ cifs_dbg(FYI, "invalid lease key, resending request without lease");
++ rc = smb2_compound_op(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN,
++ CREATE_DELETE_ON_CLOSE | OPEN_REPARSE_POINT,
++ ACL_NO_MODE, NULL,
++ &(int){SMB2_OP_DELETE}, 1,
++ NULL, NULL, NULL, NULL);
++ }
++ return rc;
+ }
+
+ static int smb2_set_path_attr(const unsigned int xid, struct cifs_tcon *tcon,
+@@ -919,8 +939,14 @@ int smb2_rename_path(const unsigned int xid,
+ drop_cached_dir_by_name(xid, tcon, from_name, cifs_sb);
+ cifs_get_writable_path(tcon, from_name, FIND_WR_WITH_DELETE, &cfile);
+
+- return smb2_set_path_attr(xid, tcon, from_name, to_name, cifs_sb,
++ int rc = smb2_set_path_attr(xid, tcon, from_name, to_name, cifs_sb,
+ co, DELETE, SMB2_OP_RENAME, cfile, source_dentry);
++ if (rc == -EINVAL) {
++ cifs_dbg(FYI, "invalid lease key, resending request without lease");
++ rc = smb2_set_path_attr(xid, tcon, from_name, to_name, cifs_sb,
++ co, DELETE, SMB2_OP_RENAME, cfile, NULL);
++ }
++ return rc;
+ }
+
+ int smb2_create_hardlink(const unsigned int xid,
+@@ -949,11 +975,20 @@ smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon,
+ in_iov.iov_base = &eof;
+ in_iov.iov_len = sizeof(eof);
+ cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile);
+- return smb2_compound_op(xid, tcon, cifs_sb, full_path,
++ int rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
+ FILE_WRITE_DATA, FILE_OPEN,
+ 0, ACL_NO_MODE, &in_iov,
+ &(int){SMB2_OP_SET_EOF}, 1,
+ cfile, NULL, NULL, dentry);
++ if (rc == -EINVAL) {
++ cifs_dbg(FYI, "invalid lease key, resending request without lease");
++ rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
++ FILE_WRITE_DATA, FILE_OPEN,
++ 0, ACL_NO_MODE, &in_iov,
++ &(int){SMB2_OP_SET_EOF}, 1,
++ cfile, NULL, NULL, NULL);
++ }
++ return rc;
+ }
+
+ int
+--
+2.43.0
+
diff --git a/queue-6.6/smb-client-return-reparse-type-in-proc-mounts.patch b/queue-6.6/smb-client-return-reparse-type-in-proc-mounts.patch
new file mode 100644
index 0000000000..aeeff34161
--- /dev/null
+++ b/queue-6.6/smb-client-return-reparse-type-in-proc-mounts.patch
@@ -0,0 +1,60 @@
+From 5fffe33797da8f6dc1451b0ff8e1a4dee2174d31 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 24 Feb 2024 16:57:14 -0300
+Subject: smb: client: return reparse type in /proc/mounts
+
+From: Paulo Alcantara <pc@manguebit.com>
+
+[ Upstream commit 1e5f4240714bb238d2d17c7e14e5fb45c9140665 ]
+
+Add support for returning reparse mount option in /proc/mounts.
+
+Reported-by: kernel test robot <lkp@intel.com>
+Closes: https://lore.kernel.org/oe-kbuild-all/202402262152.YZOwDlCM-lkp@intel.com/
+Signed-off-by: Paulo Alcantara <pc@manguebit.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/cifsfs.c | 2 ++
+ fs/smb/client/cifsglob.h | 12 ++++++++++++
+ 2 files changed, 14 insertions(+)
+
+diff --git a/fs/smb/client/cifsfs.c b/fs/smb/client/cifsfs.c
+index 6d9d2174ee691..30bf754c9fc93 100644
+--- a/fs/smb/client/cifsfs.c
++++ b/fs/smb/client/cifsfs.c
+@@ -674,6 +674,8 @@ cifs_show_options(struct seq_file *s, struct dentry *root)
+ seq_printf(s, ",backupgid=%u",
+ from_kgid_munged(&init_user_ns,
+ cifs_sb->ctx->backupgid));
++ seq_show_option(s, "reparse",
++ cifs_reparse_type_str(cifs_sb->ctx->reparse_type));
+
+ seq_printf(s, ",rsize=%u", cifs_sb->ctx->rsize);
+ seq_printf(s, ",wsize=%u", cifs_sb->ctx->wsize);
+diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h
+index ddb64af50a45d..053556ca6f011 100644
+--- a/fs/smb/client/cifsglob.h
++++ b/fs/smb/client/cifsglob.h
+@@ -159,6 +159,18 @@ enum cifs_reparse_type {
+ CIFS_REPARSE_TYPE_DEFAULT = CIFS_REPARSE_TYPE_NFS,
+ };
+
++static inline const char *cifs_reparse_type_str(enum cifs_reparse_type type)
++{
++ switch (type) {
++ case CIFS_REPARSE_TYPE_NFS:
++ return "nfs";
++ case CIFS_REPARSE_TYPE_WSL:
++ return "wsl";
++ default:
++ return "unknown";
++ }
++}
++
+ struct session_key {
+ unsigned int len;
+ char *response;
+--
+2.43.0
+
diff --git a/queue-6.6/smb-client-reuse-file-lease-key-in-compound-operatio.patch b/queue-6.6/smb-client-reuse-file-lease-key-in-compound-operatio.patch
new file mode 100644
index 0000000000..3afaa1bd87
--- /dev/null
+++ b/queue-6.6/smb-client-reuse-file-lease-key-in-compound-operatio.patch
@@ -0,0 +1,380 @@
+From e51edc13e72c6bfe617ff45a718ed7820812abef Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 5 Mar 2024 22:43:51 -0500
+Subject: smb: client: reuse file lease key in compound operations
+
+From: Meetakshi Setiya <msetiya@microsoft.com>
+
+[ Upstream commit 2c7d399e551ccfd87bcae4ef5573097f3313d779 ]
+
+Currently, when a rename, unlink or set path size compound operation
+is requested on a file that has a lot of dirty pages to be written
+to the server, we do not send the lease key for these requests. As a
+result, the server can assume that this request is from a new client, and
+send a lease break notification to the same client, on the same
+connection. As a response to the lease break, the client can consume
+several credits to write the dirty pages to the server. Depending on the
+server's credit grant implementation, the server can stop granting more
+credits to this connection, and this can cause a deadlock (which can only
+be resolved when the lease timer on the server expires).
+One of the problems here is that the client is sending no lease key,
+even if it has a lease for the file. This patch fixes the problem by
+reusing the existing lease key on the file for rename, unlink and set path
+size compound operations so that the client does not break its own lease.
+
+A very trivial example could be a set of commands by a client that
+maintains open handle (for write) to a file and then tries to copy the
+contents of that file to another one, eg.,
+
+tail -f /dev/null > myfile &
+mv myfile myfile2
+
+Presently, the network capture on the client shows that the move (or
+rename) would trigger a lease break on the same client, for the same file.
+With the lease key reused, the lease break request-response overhead is
+eliminated, thereby reducing the roundtrips performed for this set of
+operations.
+
+The patch fixes the bug described above and also provides perf benefit.
+
+Signed-off-by: Meetakshi Setiya <msetiya@microsoft.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/cifsglob.h | 5 ++--
+ fs/smb/client/cifsproto.h | 6 +++--
+ fs/smb/client/cifssmb.c | 4 ++--
+ fs/smb/client/inode.c | 10 ++++----
+ fs/smb/client/smb2inode.c | 48 ++++++++++++++++++++++++---------------
+ fs/smb/client/smb2proto.h | 6 +++--
+ 6 files changed, 48 insertions(+), 31 deletions(-)
+
+diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h
+index 678a9c671cdcd..a149579910add 100644
+--- a/fs/smb/client/cifsglob.h
++++ b/fs/smb/client/cifsglob.h
+@@ -374,7 +374,8 @@ struct smb_version_operations {
+ struct cifs_open_info_data *data);
+ /* set size by path */
+ int (*set_path_size)(const unsigned int, struct cifs_tcon *,
+- const char *, __u64, struct cifs_sb_info *, bool);
++ const char *, __u64, struct cifs_sb_info *, bool,
++ struct dentry *);
+ /* set size by file handle */
+ int (*set_file_size)(const unsigned int, struct cifs_tcon *,
+ struct cifsFileInfo *, __u64, bool);
+@@ -404,7 +405,7 @@ struct smb_version_operations {
+ struct cifs_sb_info *);
+ /* unlink file */
+ int (*unlink)(const unsigned int, struct cifs_tcon *, const char *,
+- struct cifs_sb_info *);
++ struct cifs_sb_info *, struct dentry *);
+ /* open, rename and delete file */
+ int (*rename_pending_delete)(const char *, struct dentry *,
+ const unsigned int);
+diff --git a/fs/smb/client/cifsproto.h b/fs/smb/client/cifsproto.h
+index 996ca413dd8bd..e9b38b279a6c5 100644
+--- a/fs/smb/client/cifsproto.h
++++ b/fs/smb/client/cifsproto.h
+@@ -404,7 +404,8 @@ extern int CIFSSMBSetFileDisposition(const unsigned int xid,
+ __u32 pid_of_opener);
+ extern int CIFSSMBSetEOF(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *file_name, __u64 size,
+- struct cifs_sb_info *cifs_sb, bool set_allocation);
++ struct cifs_sb_info *cifs_sb, bool set_allocation,
++ struct dentry *dentry);
+ extern int CIFSSMBSetFileSize(const unsigned int xid, struct cifs_tcon *tcon,
+ struct cifsFileInfo *cfile, __u64 size,
+ bool set_allocation);
+@@ -440,7 +441,8 @@ extern int CIFSPOSIXDelFile(const unsigned int xid, struct cifs_tcon *tcon,
+ const struct nls_table *nls_codepage,
+ int remap_special_chars);
+ extern int CIFSSMBDelFile(const unsigned int xid, struct cifs_tcon *tcon,
+- const char *name, struct cifs_sb_info *cifs_sb);
++ const char *name, struct cifs_sb_info *cifs_sb,
++ struct dentry *dentry);
+ int CIFSSMBRename(const unsigned int xid, struct cifs_tcon *tcon,
+ struct dentry *source_dentry,
+ const char *from_name, const char *to_name,
+diff --git a/fs/smb/client/cifssmb.c b/fs/smb/client/cifssmb.c
+index 01e89070df5ab..301189ee1335b 100644
+--- a/fs/smb/client/cifssmb.c
++++ b/fs/smb/client/cifssmb.c
+@@ -738,7 +738,7 @@ CIFSPOSIXDelFile(const unsigned int xid, struct cifs_tcon *tcon,
+
+ int
+ CIFSSMBDelFile(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
+- struct cifs_sb_info *cifs_sb)
++ struct cifs_sb_info *cifs_sb, struct dentry *dentry)
+ {
+ DELETE_FILE_REQ *pSMB = NULL;
+ DELETE_FILE_RSP *pSMBr = NULL;
+@@ -4993,7 +4993,7 @@ CIFSSMBQFSPosixInfo(const unsigned int xid, struct cifs_tcon *tcon,
+ int
+ CIFSSMBSetEOF(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *file_name, __u64 size, struct cifs_sb_info *cifs_sb,
+- bool set_allocation)
++ bool set_allocation, struct dentry *dentry)
+ {
+ struct smb_com_transaction2_spi_req *pSMB = NULL;
+ struct smb_com_transaction2_spi_rsp *pSMBr = NULL;
+diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c
+index d7e3da8489a0b..156713186b3c9 100644
+--- a/fs/smb/client/inode.c
++++ b/fs/smb/client/inode.c
+@@ -1849,7 +1849,7 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry)
+ goto psx_del_no_retry;
+ }
+
+- rc = server->ops->unlink(xid, tcon, full_path, cifs_sb);
++ rc = server->ops->unlink(xid, tcon, full_path, cifs_sb, dentry);
+
+ psx_del_no_retry:
+ if (!rc) {
+@@ -2800,7 +2800,7 @@ void cifs_setsize(struct inode *inode, loff_t offset)
+
+ static int
+ cifs_set_file_size(struct inode *inode, struct iattr *attrs,
+- unsigned int xid, const char *full_path)
++ unsigned int xid, const char *full_path, struct dentry *dentry)
+ {
+ int rc;
+ struct cifsFileInfo *open_file;
+@@ -2851,7 +2851,7 @@ cifs_set_file_size(struct inode *inode, struct iattr *attrs,
+ */
+ if (server->ops->set_path_size)
+ rc = server->ops->set_path_size(xid, tcon, full_path,
+- attrs->ia_size, cifs_sb, false);
++ attrs->ia_size, cifs_sb, false, dentry);
+ else
+ rc = -ENOSYS;
+ cifs_dbg(FYI, "SetEOF by path (setattrs) rc = %d\n", rc);
+@@ -2941,7 +2941,7 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs)
+ rc = 0;
+
+ if (attrs->ia_valid & ATTR_SIZE) {
+- rc = cifs_set_file_size(inode, attrs, xid, full_path);
++ rc = cifs_set_file_size(inode, attrs, xid, full_path, direntry);
+ if (rc != 0)
+ goto out;
+ }
+@@ -3107,7 +3107,7 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs)
+ }
+
+ if (attrs->ia_valid & ATTR_SIZE) {
+- rc = cifs_set_file_size(inode, attrs, xid, full_path);
++ rc = cifs_set_file_size(inode, attrs, xid, full_path, direntry);
+ if (rc != 0)
+ goto cifs_setattr_exit;
+ }
+diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c
+index dfa4e5362213f..e452c59c13e2d 100644
+--- a/fs/smb/client/smb2inode.c
++++ b/fs/smb/client/smb2inode.c
+@@ -98,7 +98,7 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
+ __u32 desired_access, __u32 create_disposition,
+ __u32 create_options, umode_t mode, struct kvec *in_iov,
+ int *cmds, int num_cmds, struct cifsFileInfo *cfile,
+- struct kvec *out_iov, int *out_buftype)
++ struct kvec *out_iov, int *out_buftype, struct dentry *dentry)
+ {
+
+ struct reparse_data_buffer *rbuf;
+@@ -115,6 +115,7 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
+ int resp_buftype[MAX_COMPOUND];
+ struct smb2_query_info_rsp *qi_rsp = NULL;
+ struct cifs_open_info_data *idata;
++ struct inode *inode = NULL;
+ int flags = 0;
+ __u8 delete_pending[8] = {1, 0, 0, 0, 0, 0, 0, 0};
+ unsigned int size[2];
+@@ -152,6 +153,15 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
+ goto finished;
+ }
+
++ /* if there is an existing lease, reuse it */
++ if (dentry) {
++ inode = d_inode(dentry);
++ if (CIFS_I(inode)->lease_granted && server->ops->get_lease_key) {
++ oplock = SMB2_OPLOCK_LEVEL_LEASE;
++ server->ops->get_lease_key(inode, &fid);
++ }
++ }
++
+ vars->oparms = (struct cifs_open_parms) {
+ .tcon = tcon,
+ .path = full_path,
+@@ -747,7 +757,7 @@ int smb2_query_path_info(const unsigned int xid,
+ rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
+ FILE_READ_ATTRIBUTES, FILE_OPEN,
+ create_options, ACL_NO_MODE, in_iov,
+- cmds, 1, cfile, out_iov, out_buftype);
++ cmds, 1, cfile, out_iov, out_buftype, NULL);
+ hdr = out_iov[0].iov_base;
+ /*
+ * If first iov is unset, then SMB session was dropped or we've got a
+@@ -779,7 +789,7 @@ int smb2_query_path_info(const unsigned int xid,
+ rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
+ FILE_READ_ATTRIBUTES, FILE_OPEN,
+ create_options, ACL_NO_MODE, in_iov,
+- cmds, num_cmds, cfile, NULL, NULL);
++ cmds, num_cmds, cfile, NULL, NULL, NULL);
+ break;
+ case -EREMOTE:
+ break;
+@@ -811,7 +821,7 @@ smb2_mkdir(const unsigned int xid, struct inode *parent_inode, umode_t mode,
+ FILE_WRITE_ATTRIBUTES, FILE_CREATE,
+ CREATE_NOT_FILE, mode,
+ NULL, &(int){SMB2_OP_MKDIR}, 1,
+- NULL, NULL, NULL);
++ NULL, NULL, NULL, NULL);
+ }
+
+ void
+@@ -836,7 +846,7 @@ smb2_mkdir_setinfo(struct inode *inode, const char *name,
+ FILE_WRITE_ATTRIBUTES, FILE_CREATE,
+ CREATE_NOT_FILE, ACL_NO_MODE, &in_iov,
+ &(int){SMB2_OP_SET_INFO}, 1,
+- cfile, NULL, NULL);
++ cfile, NULL, NULL, NULL);
+ if (tmprc == 0)
+ cifs_i->cifsAttrs = dosattrs;
+ }
+@@ -850,25 +860,26 @@ smb2_rmdir(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
+ DELETE, FILE_OPEN, CREATE_NOT_FILE,
+ ACL_NO_MODE, NULL,
+ &(int){SMB2_OP_RMDIR}, 1,
+- NULL, NULL, NULL);
++ NULL, NULL, NULL, NULL);
+ }
+
+ int
+ smb2_unlink(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
+- struct cifs_sb_info *cifs_sb)
++ struct cifs_sb_info *cifs_sb, struct dentry *dentry)
+ {
+ return smb2_compound_op(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN,
+ CREATE_DELETE_ON_CLOSE | OPEN_REPARSE_POINT,
+ ACL_NO_MODE, NULL,
+ &(int){SMB2_OP_DELETE}, 1,
+- NULL, NULL, NULL);
++ NULL, NULL, NULL, dentry);
+ }
+
+ static int smb2_set_path_attr(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *from_name, const char *to_name,
+ struct cifs_sb_info *cifs_sb,
+ __u32 create_options, __u32 access,
+- int command, struct cifsFileInfo *cfile)
++ int command, struct cifsFileInfo *cfile,
++ struct dentry *dentry)
+ {
+ struct kvec in_iov;
+ __le16 *smb2_to_name = NULL;
+@@ -883,7 +894,7 @@ static int smb2_set_path_attr(const unsigned int xid, struct cifs_tcon *tcon,
+ in_iov.iov_len = 2 * UniStrnlen((wchar_t *)smb2_to_name, PATH_MAX);
+ rc = smb2_compound_op(xid, tcon, cifs_sb, from_name, access,
+ FILE_OPEN, create_options, ACL_NO_MODE,
+- &in_iov, &command, 1, cfile, NULL, NULL);
++ &in_iov, &command, 1, cfile, NULL, NULL, dentry);
+ smb2_rename_path:
+ kfree(smb2_to_name);
+ return rc;
+@@ -902,7 +913,7 @@ int smb2_rename_path(const unsigned int xid,
+ cifs_get_writable_path(tcon, from_name, FIND_WR_WITH_DELETE, &cfile);
+
+ return smb2_set_path_attr(xid, tcon, from_name, to_name, cifs_sb,
+- co, DELETE, SMB2_OP_RENAME, cfile);
++ co, DELETE, SMB2_OP_RENAME, cfile, source_dentry);
+ }
+
+ int smb2_create_hardlink(const unsigned int xid,
+@@ -915,13 +926,14 @@ int smb2_create_hardlink(const unsigned int xid,
+
+ return smb2_set_path_attr(xid, tcon, from_name, to_name,
+ cifs_sb, co, FILE_READ_ATTRIBUTES,
+- SMB2_OP_HARDLINK, NULL);
++ SMB2_OP_HARDLINK, NULL, NULL);
+ }
+
+ int
+ smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *full_path, __u64 size,
+- struct cifs_sb_info *cifs_sb, bool set_alloc)
++ struct cifs_sb_info *cifs_sb, bool set_alloc,
++ struct dentry *dentry)
+ {
+ struct cifsFileInfo *cfile;
+ struct kvec in_iov;
+@@ -934,7 +946,7 @@ smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon,
+ FILE_WRITE_DATA, FILE_OPEN,
+ 0, ACL_NO_MODE, &in_iov,
+ &(int){SMB2_OP_SET_EOF}, 1,
+- cfile, NULL, NULL);
++ cfile, NULL, NULL, dentry);
+ }
+
+ int
+@@ -963,7 +975,7 @@ smb2_set_file_info(struct inode *inode, const char *full_path,
+ FILE_WRITE_ATTRIBUTES, FILE_OPEN,
+ 0, ACL_NO_MODE, &in_iov,
+ &(int){SMB2_OP_SET_INFO}, 1,
+- cfile, NULL, NULL);
++ cfile, NULL, NULL, NULL);
+ cifs_put_tlink(tlink);
+ return rc;
+ }
+@@ -998,7 +1010,7 @@ struct inode *smb2_get_reparse_inode(struct cifs_open_info_data *data,
+ cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile);
+ rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
+ da, cd, co, ACL_NO_MODE, in_iov,
+- cmds, 2, cfile, NULL, NULL);
++ cmds, 2, cfile, NULL, NULL, NULL);
+ if (!rc) {
+ rc = smb311_posix_get_inode_info(&new, full_path,
+ data, sb, xid);
+@@ -1008,7 +1020,7 @@ struct inode *smb2_get_reparse_inode(struct cifs_open_info_data *data,
+ cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile);
+ rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
+ da, cd, co, ACL_NO_MODE, in_iov,
+- cmds, 2, cfile, NULL, NULL);
++ cmds, 2, cfile, NULL, NULL, NULL);
+ if (!rc) {
+ rc = cifs_get_inode_info(&new, full_path,
+ data, sb, xid, NULL);
+@@ -1036,7 +1048,7 @@ int smb2_query_reparse_point(const unsigned int xid,
+ FILE_READ_ATTRIBUTES, FILE_OPEN,
+ OPEN_REPARSE_POINT, ACL_NO_MODE, &in_iov,
+ &(int){SMB2_OP_GET_REPARSE}, 1,
+- cfile, NULL, NULL);
++ cfile, NULL, NULL, NULL);
+ if (rc)
+ goto out;
+
+diff --git a/fs/smb/client/smb2proto.h b/fs/smb/client/smb2proto.h
+index b3069911e9dd8..221143788a1c0 100644
+--- a/fs/smb/client/smb2proto.h
++++ b/fs/smb/client/smb2proto.h
+@@ -75,7 +75,8 @@ int smb2_query_path_info(const unsigned int xid,
+ struct cifs_open_info_data *data);
+ extern int smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *full_path, __u64 size,
+- struct cifs_sb_info *cifs_sb, bool set_alloc);
++ struct cifs_sb_info *cifs_sb, bool set_alloc,
++ struct dentry *dentry);
+ extern int smb2_set_file_info(struct inode *inode, const char *full_path,
+ FILE_BASIC_INFO *buf, const unsigned int xid);
+ extern int smb311_posix_mkdir(const unsigned int xid, struct inode *inode,
+@@ -91,7 +92,8 @@ extern void smb2_mkdir_setinfo(struct inode *inode, const char *full_path,
+ extern int smb2_rmdir(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *name, struct cifs_sb_info *cifs_sb);
+ extern int smb2_unlink(const unsigned int xid, struct cifs_tcon *tcon,
+- const char *name, struct cifs_sb_info *cifs_sb);
++ const char *name, struct cifs_sb_info *cifs_sb,
++ struct dentry *dentry);
+ int smb2_rename_path(const unsigned int xid,
+ struct cifs_tcon *tcon,
+ struct dentry *source_dentry,
+--
+2.43.0
+
diff --git a/queue-6.6/smb-client-set-correct-d_type-for-reparse-dfs-dfsr-a.patch b/queue-6.6/smb-client-set-correct-d_type-for-reparse-dfs-dfsr-a.patch
new file mode 100644
index 0000000000..964967677d
--- /dev/null
+++ b/queue-6.6/smb-client-set-correct-d_type-for-reparse-dfs-dfsr-a.patch
@@ -0,0 +1,81 @@
+From 148cef4216aaf1aa8fdad581a83546f3dcde895e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 2 Feb 2024 13:42:11 -0300
+Subject: smb: client: set correct d_type for reparse DFS/DFSR and mount point
+
+From: Paulo Alcantara <pc@manguebit.com>
+
+[ Upstream commit 8bd25b61c5a55bc769c6608e9ce95860759acdcb ]
+
+Set correct dirent->d_type for IO_REPARSE_TAG_DFS{,R} and
+IO_REPARSE_TAG_MOUNT_POINT reparse points.
+
+Signed-off-by: Paulo Alcantara <pc@manguebit.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/reparse.c | 16 +++++++++-------
+ 1 file changed, 9 insertions(+), 7 deletions(-)
+
+diff --git a/fs/smb/client/reparse.c b/fs/smb/client/reparse.c
+index 29a47f20643b1..a0ffbda907331 100644
+--- a/fs/smb/client/reparse.c
++++ b/fs/smb/client/reparse.c
+@@ -482,34 +482,35 @@ bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb,
+ switch (le64_to_cpu(buf->InodeType)) {
+ case NFS_SPECFILE_CHR:
+ fattr->cf_mode |= S_IFCHR;
+- fattr->cf_dtype = DT_CHR;
+ fattr->cf_rdev = reparse_nfs_mkdev(buf);
+ break;
+ case NFS_SPECFILE_BLK:
+ fattr->cf_mode |= S_IFBLK;
+- fattr->cf_dtype = DT_BLK;
+ fattr->cf_rdev = reparse_nfs_mkdev(buf);
+ break;
+ case NFS_SPECFILE_FIFO:
+ fattr->cf_mode |= S_IFIFO;
+- fattr->cf_dtype = DT_FIFO;
+ break;
+ case NFS_SPECFILE_SOCK:
+ fattr->cf_mode |= S_IFSOCK;
+- fattr->cf_dtype = DT_SOCK;
+ break;
+ case NFS_SPECFILE_LNK:
+ fattr->cf_mode |= S_IFLNK;
+- fattr->cf_dtype = DT_LNK;
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ return false;
+ }
+- return true;
++ goto out;
+ }
+
+ switch (tag) {
++ case IO_REPARSE_TAG_DFS:
++ case IO_REPARSE_TAG_DFSR:
++ case IO_REPARSE_TAG_MOUNT_POINT:
++ /* See cifs_create_junction_fattr() */
++ fattr->cf_mode = S_IFDIR | 0711;
++ break;
+ case IO_REPARSE_TAG_LX_SYMLINK:
+ case IO_REPARSE_TAG_LX_FIFO:
+ case IO_REPARSE_TAG_AF_UNIX:
+@@ -521,10 +522,11 @@ bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb,
+ case IO_REPARSE_TAG_SYMLINK:
+ case IO_REPARSE_TAG_NFS:
+ fattr->cf_mode |= S_IFLNK;
+- fattr->cf_dtype = DT_LNK;
+ break;
+ default:
+ return false;
+ }
++out:
++ fattr->cf_dtype = S_DT(fattr->cf_mode);
+ return true;
+ }
+--
+2.43.0
+
diff --git a/queue-6.6/smb-common-fix-fields-sizes-in-compression_pattern_p.patch b/queue-6.6/smb-common-fix-fields-sizes-in-compression_pattern_p.patch
new file mode 100644
index 0000000000..bf21aa53f1
--- /dev/null
+++ b/queue-6.6/smb-common-fix-fields-sizes-in-compression_pattern_p.patch
@@ -0,0 +1,36 @@
+From e9ee29350dd71c0b81a8732f4cb85a59a5a6d842 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 8 Mar 2024 18:34:10 -0300
+Subject: smb: common: fix fields sizes in compression_pattern_payload_v1
+
+From: Enzo Matsumiya <ematsumiya@suse.de>
+
+[ Upstream commit f49af462875a0922167cf301cf126cd04009070e ]
+
+See protocol documentation in MS-SMB2 section 2.2.42.2.2
+
+Signed-off-by: Enzo Matsumiya <ematsumiya@suse.de>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/common/smb2pdu.h | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/fs/smb/common/smb2pdu.h b/fs/smb/common/smb2pdu.h
+index a233a24352b1f..10a9e20eec43f 100644
+--- a/fs/smb/common/smb2pdu.h
++++ b/fs/smb/common/smb2pdu.h
+@@ -238,8 +238,8 @@ struct smb2_compression_transform_hdr_chained {
+
+ /* See MS-SMB2 2.2.42.2.2 */
+ struct compression_pattern_payload_v1 {
+- __le16 Pattern;
+- __le16 Reserved1;
++ __u8 Pattern;
++ __u8 Reserved1;
+ __le16 Reserved2;
+ __le32 Repetitions;
+ } __packed;
+--
+2.43.0
+
diff --git a/queue-6.6/smb-common-simplify-compression-headers.patch b/queue-6.6/smb-common-simplify-compression-headers.patch
new file mode 100644
index 0000000000..f90e1823fe
--- /dev/null
+++ b/queue-6.6/smb-common-simplify-compression-headers.patch
@@ -0,0 +1,97 @@
+From ba3468b11ceb9ee919f73d46ffd42cffe49b2d42 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 8 Mar 2024 19:00:12 -0300
+Subject: smb: common: simplify compression headers
+
+From: Enzo Matsumiya <ematsumiya@suse.de>
+
+[ Upstream commit 24337b60e88219816f84d633369299660e8e8cce ]
+
+Unify compression headers (chained and unchained) into a single struct
+so we can use it for the initial compression transform header
+interchangeably.
+
+Also make the OriginalPayloadSize field to be always visible in the
+compression payload header, and have callers subtract its size when not
+needed.
+
+Rename the related structs to match the naming convetion used in the
+other SMB2 structs.
+
+Signed-off-by: Enzo Matsumiya <ematsumiya@suse.de>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/common/smb2pdu.h | 45 ++++++++++++++++++++++++-----------------
+ 1 file changed, 26 insertions(+), 19 deletions(-)
+
+diff --git a/fs/smb/common/smb2pdu.h b/fs/smb/common/smb2pdu.h
+index 10a9e20eec43f..61c6e72ccddc4 100644
+--- a/fs/smb/common/smb2pdu.h
++++ b/fs/smb/common/smb2pdu.h
+@@ -208,36 +208,43 @@ struct smb2_transform_hdr {
+ __le64 SessionId;
+ } __packed;
+
++/*
++ * These are simplified versions from the spec, as we don't need a fully fledged
++ * form of both unchained and chained structs.
++ *
++ * Moreover, even in chained compressed payloads, the initial compression header
++ * has the form of the unchained one -- i.e. it never has the
++ * OriginalPayloadSize field and ::Offset field always represent an offset
++ * (instead of a length, as it is in the chained header).
++ *
++ * See MS-SMB2 2.2.42 for more details.
++ */
++#define SMB2_COMPRESSION_FLAG_NONE 0x0000
++#define SMB2_COMPRESSION_FLAG_CHAINED 0x0001
+
+-/* See MS-SMB2 2.2.42 */
+-struct smb2_compression_transform_hdr_unchained {
+- __le32 ProtocolId; /* 0xFC 'S' 'M' 'B' */
++struct smb2_compression_hdr {
++ __le32 ProtocolId; /* 0xFC 'S' 'M' 'B' */
+ __le32 OriginalCompressedSegmentSize;
+ __le16 CompressionAlgorithm;
+ __le16 Flags;
+- __le16 Length; /* if chained it is length, else offset */
++ __le16 Offset; /* this is the size of the uncompressed SMB2 header below */
++ /* uncompressed SMB2 header (READ or WRITE) goes here */
++ /* compressed data goes here */
+ } __packed;
+
+-/* See MS-SMB2 2.2.42.1 */
+-#define SMB2_COMPRESSION_FLAG_NONE 0x0000
+-#define SMB2_COMPRESSION_FLAG_CHAINED 0x0001
+-
+-struct compression_payload_header {
++/*
++ * ... OTOH, set compression payload header to always have OriginalPayloadSize
++ * as it's easier to pass the struct size minus sizeof(OriginalPayloadSize)
++ * than to juggle around the header/data memory.
++ */
++struct smb2_compression_payload_hdr {
+ __le16 CompressionAlgorithm;
+ __le16 Flags;
+ __le32 Length; /* length of compressed playload including field below if present */
+- /* __le32 OriginalPayloadSize; */ /* optional, present when LZNT1, LZ77, LZ77+Huffman */
+-} __packed;
+-
+-/* See MS-SMB2 2.2.42.2 */
+-struct smb2_compression_transform_hdr_chained {
+- __le32 ProtocolId; /* 0xFC 'S' 'M' 'B' */
+- __le32 OriginalCompressedSegmentSize;
+- /* struct compression_payload_header[] */
++ __le32 OriginalPayloadSize; /* accounted when LZNT1, LZ77, LZ77+Huffman */
+ } __packed;
+
+-/* See MS-SMB2 2.2.42.2.2 */
+-struct compression_pattern_payload_v1 {
++struct smb2_compression_pattern_v1 {
+ __u8 Pattern;
+ __u8 Reserved1;
+ __le16 Reserved2;
+--
+2.43.0
+
diff --git a/queue-6.6/smb-fix-some-kernel-doc-comments.patch b/queue-6.6/smb-fix-some-kernel-doc-comments.patch
new file mode 100644
index 0000000000..74fb45dff4
--- /dev/null
+++ b/queue-6.6/smb-fix-some-kernel-doc-comments.patch
@@ -0,0 +1,44 @@
+From dbd018721c5f81232beff364fcdd729b0f98b6f2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 19 Jan 2024 17:57:07 +0800
+Subject: smb: Fix some kernel-doc comments
+
+From: Yang Li <yang.lee@linux.alibaba.com>
+
+[ Upstream commit 72b0cbf6b81003c01d63c60180b335f7692d170e ]
+
+Fix some kernel-doc comments to silence the warnings:
+fs/smb/server/transport_tcp.c:374: warning: Function parameter or struct member 'max_retries' not described in 'ksmbd_tcp_read'
+fs/smb/server/transport_tcp.c:423: warning: Function parameter or struct member 'iface' not described in 'create_socket'
+
+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/transport_tcp.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/fs/smb/server/transport_tcp.c b/fs/smb/server/transport_tcp.c
+index 0012919309f11..6633fa78e9b96 100644
+--- a/fs/smb/server/transport_tcp.c
++++ b/fs/smb/server/transport_tcp.c
+@@ -365,6 +365,7 @@ static int ksmbd_tcp_readv(struct tcp_transport *t, struct kvec *iov_orig,
+ * @t: TCP transport instance
+ * @buf: buffer to store read data from socket
+ * @to_read: number of bytes to read from socket
++ * @max_retries: number of retries if reading from socket fails
+ *
+ * Return: on success return number of bytes read from socket,
+ * otherwise return error number
+@@ -416,6 +417,7 @@ static void tcp_destroy_socket(struct socket *ksmbd_socket)
+
+ /**
+ * create_socket - create socket for ksmbd/0
++ * @iface: interface to bind the created socket to
+ *
+ * Return: 0 on success, error number otherwise
+ */
+--
+2.43.0
+
diff --git a/queue-6.6/smb-smb2pdu.h-avoid-wflex-array-member-not-at-end-wa.patch b/queue-6.6/smb-smb2pdu.h-avoid-wflex-array-member-not-at-end-wa.patch
new file mode 100644
index 0000000000..786f4d58b6
--- /dev/null
+++ b/queue-6.6/smb-smb2pdu.h-avoid-wflex-array-member-not-at-end-wa.patch
@@ -0,0 +1,273 @@
+From 5485b6988657801f1eed94bea0f1786fff6f6b14 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 11 Apr 2024 09:35:42 -0600
+Subject: smb: smb2pdu.h: Avoid -Wflex-array-member-not-at-end warnings
+
+From: Gustavo A. R. Silva <gustavoars@kernel.org>
+
+-Wflex-array-member-not-at-end is coming in GCC-14, and we are getting
+ready to enable it globally.
+
+So, in order to avoid ending up with a flexible-array member in the
+middle of multiple other structs, we use the `__struct_group()` helper
+to separate the flexible array from the rest of the members in the
+flexible structure, and use the tagged `struct create_context_hdr`
+instead of `struct create_context`.
+
+So, with these changes, fix 51 of the following warnings[1]:
+
+fs/smb/client/../common/smb2pdu.h:1225:31: warning: structure containing a flexible array member is not at the end of another structure [-Wflex-array-member-not-at-end]
+
+Link: https://gist.github.com/GustavoARSilva/772526a39be3dd4db39e71497f0a9893 [1]
+Link: https://github.com/KSPP/linux/issues/202
+Signed-off-by: Gustavo A. R. Silva <gustavoars@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+---
+ fs/smb/client/smb2pdu.h | 12 ++++++------
+ fs/smb/common/smb2pdu.h | 33 ++++++++++++++++++---------------
+ fs/smb/server/smb2pdu.h | 18 +++++++++---------
+ 3 files changed, 33 insertions(+), 30 deletions(-)
+
+diff --git a/fs/smb/client/smb2pdu.h b/fs/smb/client/smb2pdu.h
+index 2fccf0d4f53d2..5c458ab3b05a4 100644
+--- a/fs/smb/client/smb2pdu.h
++++ b/fs/smb/client/smb2pdu.h
+@@ -145,7 +145,7 @@ struct durable_context_v2 {
+ } __packed;
+
+ struct create_durable_v2 {
+- struct create_context ccontext;
++ struct create_context_hdr ccontext;
+ __u8 Name[8];
+ struct durable_context_v2 dcontext;
+ } __packed;
+@@ -167,7 +167,7 @@ struct durable_reconnect_context_v2_rsp {
+ } __packed;
+
+ struct create_durable_handle_reconnect_v2 {
+- struct create_context ccontext;
++ struct create_context_hdr ccontext;
+ __u8 Name[8];
+ struct durable_reconnect_context_v2 dcontext;
+ __u8 Pad[4];
+@@ -175,7 +175,7 @@ struct create_durable_handle_reconnect_v2 {
+
+ /* See MS-SMB2 2.2.13.2.5 */
+ struct crt_twarp_ctxt {
+- struct create_context ccontext;
++ struct create_context_hdr ccontext;
+ __u8 Name[8];
+ __le64 Timestamp;
+
+@@ -183,12 +183,12 @@ struct crt_twarp_ctxt {
+
+ /* See MS-SMB2 2.2.13.2.9 */
+ struct crt_query_id_ctxt {
+- struct create_context ccontext;
++ struct create_context_hdr ccontext;
+ __u8 Name[8];
+ } __packed;
+
+ struct crt_sd_ctxt {
+- struct create_context ccontext;
++ struct create_context_hdr ccontext;
+ __u8 Name[8];
+ struct smb3_sd sd;
+ } __packed;
+@@ -415,7 +415,7 @@ struct smb2_posix_info_parsed {
+ };
+
+ struct smb2_create_ea_ctx {
+- struct create_context ctx;
++ struct create_context_hdr ctx;
+ __u8 name[8];
+ struct smb2_file_full_ea_info ea;
+ } __packed;
+diff --git a/fs/smb/common/smb2pdu.h b/fs/smb/common/smb2pdu.h
+index 202ff91281560..8d10be1fe18a8 100644
+--- a/fs/smb/common/smb2pdu.h
++++ b/fs/smb/common/smb2pdu.h
+@@ -1171,12 +1171,15 @@ struct smb2_server_client_notification {
+ #define SMB2_CREATE_FLAG_REPARSEPOINT 0x01
+
+ struct create_context {
+- __le32 Next;
+- __le16 NameOffset;
+- __le16 NameLength;
+- __le16 Reserved;
+- __le16 DataOffset;
+- __le32 DataLength;
++ /* New members must be added within the struct_group() macro below. */
++ __struct_group(create_context_hdr, hdr, __packed,
++ __le32 Next;
++ __le16 NameOffset;
++ __le16 NameLength;
++ __le16 Reserved;
++ __le16 DataOffset;
++ __le32 DataLength;
++ );
+ __u8 Buffer[];
+ } __packed;
+
+@@ -1222,7 +1225,7 @@ struct smb2_create_rsp {
+ } __packed;
+
+ struct create_posix {
+- struct create_context ccontext;
++ struct create_context_hdr ccontext;
+ __u8 Name[16];
+ __le32 Mode;
+ __u32 Reserved;
+@@ -1230,7 +1233,7 @@ struct create_posix {
+
+ /* See MS-SMB2 2.2.13.2.3 and MS-SMB2 2.2.13.2.4 */
+ struct create_durable {
+- struct create_context ccontext;
++ struct create_context_hdr ccontext;
+ __u8 Name[8];
+ union {
+ __u8 Reserved[16];
+@@ -1243,14 +1246,14 @@ struct create_durable {
+
+ /* See MS-SMB2 2.2.13.2.5 */
+ struct create_mxac_req {
+- struct create_context ccontext;
++ struct create_context_hdr ccontext;
+ __u8 Name[8];
+ __le64 Timestamp;
+ } __packed;
+
+ /* See MS-SMB2 2.2.14.2.5 */
+ struct create_mxac_rsp {
+- struct create_context ccontext;
++ struct create_context_hdr ccontext;
+ __u8 Name[8];
+ __le32 QueryStatus;
+ __le32 MaximalAccess;
+@@ -1286,13 +1289,13 @@ struct lease_context_v2 {
+ } __packed;
+
+ struct create_lease {
+- struct create_context ccontext;
++ struct create_context_hdr ccontext;
+ __u8 Name[8];
+ struct lease_context lcontext;
+ } __packed;
+
+ struct create_lease_v2 {
+- struct create_context ccontext;
++ struct create_context_hdr ccontext;
+ __u8 Name[8];
+ struct lease_context_v2 lcontext;
+ __u8 Pad[4];
+@@ -1300,7 +1303,7 @@ struct create_lease_v2 {
+
+ /* See MS-SMB2 2.2.14.2.9 */
+ struct create_disk_id_rsp {
+- struct create_context ccontext;
++ struct create_context_hdr ccontext;
+ __u8 Name[8];
+ __le64 DiskFileId;
+ __le64 VolumeId;
+@@ -1309,7 +1312,7 @@ struct create_disk_id_rsp {
+
+ /* See MS-SMB2 2.2.13.2.13 */
+ struct create_app_inst_id {
+- struct create_context ccontext;
++ struct create_context_hdr ccontext;
+ __u8 Name[16];
+ __le32 StructureSize; /* Must be 20 */
+ __u16 Reserved;
+@@ -1318,7 +1321,7 @@ struct create_app_inst_id {
+
+ /* See MS-SMB2 2.2.13.2.15 */
+ struct create_app_inst_id_vers {
+- struct create_context ccontext;
++ struct create_context_hdr ccontext;
+ __u8 Name[16];
+ __le32 StructureSize; /* Must be 24 */
+ __u16 Reserved;
+diff --git a/fs/smb/server/smb2pdu.h b/fs/smb/server/smb2pdu.h
+index bd1d2a0e9203a..643f5e1cfe357 100644
+--- a/fs/smb/server/smb2pdu.h
++++ b/fs/smb/server/smb2pdu.h
+@@ -64,7 +64,7 @@ struct preauth_integrity_info {
+ #define SMB2_SESSION_TIMEOUT (10 * HZ)
+
+ struct create_durable_req_v2 {
+- struct create_context ccontext;
++ struct create_context_hdr ccontext;
+ __u8 Name[8];
+ __le32 Timeout;
+ __le32 Flags;
+@@ -73,7 +73,7 @@ struct create_durable_req_v2 {
+ } __packed;
+
+ struct create_durable_reconn_req {
+- struct create_context ccontext;
++ struct create_context_hdr ccontext;
+ __u8 Name[8];
+ union {
+ __u8 Reserved[16];
+@@ -85,7 +85,7 @@ struct create_durable_reconn_req {
+ } __packed;
+
+ struct create_durable_reconn_v2_req {
+- struct create_context ccontext;
++ struct create_context_hdr ccontext;
+ __u8 Name[8];
+ struct {
+ __u64 PersistentFileId;
+@@ -96,13 +96,13 @@ struct create_durable_reconn_v2_req {
+ } __packed;
+
+ struct create_alloc_size_req {
+- struct create_context ccontext;
++ struct create_context_hdr ccontext;
+ __u8 Name[8];
+ __le64 AllocationSize;
+ } __packed;
+
+ struct create_durable_rsp {
+- struct create_context ccontext;
++ struct create_context_hdr ccontext;
+ __u8 Name[8];
+ union {
+ __u8 Reserved[8];
+@@ -114,7 +114,7 @@ struct create_durable_rsp {
+ /* Flags */
+ #define SMB2_DHANDLE_FLAG_PERSISTENT 0x00000002
+ struct create_durable_v2_rsp {
+- struct create_context ccontext;
++ struct create_context_hdr ccontext;
+ __u8 Name[8];
+ __le32 Timeout;
+ __le32 Flags;
+@@ -122,7 +122,7 @@ struct create_durable_v2_rsp {
+
+ /* equivalent of the contents of SMB3.1.1 POSIX open context response */
+ struct create_posix_rsp {
+- struct create_context ccontext;
++ struct create_context_hdr ccontext;
+ __u8 Name[16];
+ __le32 nlink;
+ __le32 reparse_tag;
+@@ -381,13 +381,13 @@ struct smb2_ea_info {
+ } __packed; /* level 15 Query */
+
+ struct create_ea_buf_req {
+- struct create_context ccontext;
++ struct create_context_hdr ccontext;
+ __u8 Name[8];
+ struct smb2_ea_info ea;
+ } __packed;
+
+ struct create_sd_buf_req {
+- struct create_context ccontext;
++ struct create_context_hdr ccontext;
+ __u8 Name[8];
+ struct smb_ntsd ntsd;
+ } __packed;
+--
+2.43.0
+
diff --git a/queue-6.6/smb-use-crypto_shash_digest-in-symlink_hash.patch b/queue-6.6/smb-use-crypto_shash_digest-in-symlink_hash.patch
new file mode 100644
index 0000000000..4465f6207d
--- /dev/null
+++ b/queue-6.6/smb-use-crypto_shash_digest-in-symlink_hash.patch
@@ -0,0 +1,53 @@
+From 1c6ee581f8d0171730a04fbcab9406ee83392f86 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 28 Oct 2023 22:03:00 -0700
+Subject: smb: use crypto_shash_digest() in symlink_hash()
+
+From: Eric Biggers <ebiggers@google.com>
+
+[ Upstream commit 783fa2c94f4150fe1b7f7d88b3baf6d98f82b41b ]
+
+Simplify symlink_hash() by using crypto_shash_digest() instead of an
+init+update+final sequence. This should also improve performance.
+
+Signed-off-by: Eric Biggers <ebiggers@google.com>
+Reviewed-by: Paulo Alcantara (SUSE) <pc@manguebit.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/link.c | 16 ++--------------
+ 1 file changed, 2 insertions(+), 14 deletions(-)
+
+diff --git a/fs/smb/client/link.c b/fs/smb/client/link.c
+index 6c4ae52ddc04f..3ef34218a790d 100644
+--- a/fs/smb/client/link.c
++++ b/fs/smb/client/link.c
+@@ -42,23 +42,11 @@ symlink_hash(unsigned int link_len, const char *link_str, u8 *md5_hash)
+
+ rc = cifs_alloc_hash("md5", &md5);
+ if (rc)
+- goto symlink_hash_err;
++ return rc;
+
+- rc = crypto_shash_init(md5);
+- if (rc) {
+- cifs_dbg(VFS, "%s: Could not init md5 shash\n", __func__);
+- goto symlink_hash_err;
+- }
+- rc = crypto_shash_update(md5, link_str, link_len);
+- if (rc) {
+- cifs_dbg(VFS, "%s: Could not update with link_str\n", __func__);
+- goto symlink_hash_err;
+- }
+- rc = crypto_shash_final(md5, md5_hash);
++ rc = crypto_shash_digest(md5, link_str, link_len, md5_hash);
+ if (rc)
+ cifs_dbg(VFS, "%s: Could not generate md5 hash\n", __func__);
+-
+-symlink_hash_err:
+ cifs_free_hash(&md5);
+ return rc;
+ }
+--
+2.43.0
+
diff --git a/queue-6.6/smb3-add-dynamic-trace-point-for-ioctls.patch b/queue-6.6/smb3-add-dynamic-trace-point-for-ioctls.patch
new file mode 100644
index 0000000000..b601b4de30
--- /dev/null
+++ b/queue-6.6/smb3-add-dynamic-trace-point-for-ioctls.patch
@@ -0,0 +1,89 @@
+From 1c432cabb811faa93a7ee744ddd6fa401e59cd89 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 Mar 2024 01:03:59 -0600
+Subject: smb3: add dynamic trace point for ioctls
+
+From: Steve French <stfrench@microsoft.com>
+
+[ Upstream commit 073dd87c8e1ee55ca163956f0c71249dc28aac51 ]
+
+It can be helpful in debugging to know which ioctls are called to better
+correlate them with smb3 fsctls (and opens). Add a dynamic trace point
+to trace ioctls into cifs.ko
+
+Here is sample output:
+
+ TASK-PID CPU# ||||| TIMESTAMP FUNCTION
+ | | | ||||| | |
+ new-inotify-ioc-90418 [001] ..... 142157.397024: smb3_ioctl: xid=18 fid=0x0 ioctl cmd=0xc009cf0b
+ new-inotify-ioc-90457 [007] ..... 142217.943569: smb3_ioctl: xid=22 fid=0x389bf5b6 ioctl cmd=0xc009cf0b
+
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/ioctl.c | 5 +++++
+ fs/smb/client/trace.h | 32 ++++++++++++++++++++++++++++++++
+ 2 files changed, 37 insertions(+)
+
+diff --git a/fs/smb/client/ioctl.c b/fs/smb/client/ioctl.c
+index 682eabdd1d6cc..855ac5a62edfa 100644
+--- a/fs/smb/client/ioctl.c
++++ b/fs/smb/client/ioctl.c
+@@ -349,6 +349,11 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
+ xid = get_xid();
+
+ cifs_dbg(FYI, "cifs ioctl 0x%x\n", command);
++ if (pSMBFile == NULL)
++ trace_smb3_ioctl(xid, 0, command);
++ else
++ trace_smb3_ioctl(xid, pSMBFile->fid.persistent_fid, command);
++
+ switch (command) {
+ case FS_IOC_GETFLAGS:
+ if (pSMBFile == NULL)
+diff --git a/fs/smb/client/trace.h b/fs/smb/client/trace.h
+index ce90ae0d77f84..f9c1fd32d0b8c 100644
+--- a/fs/smb/client/trace.h
++++ b/fs/smb/client/trace.h
+@@ -1032,6 +1032,38 @@ DEFINE_EVENT(smb3_ses_class, smb3_##name, \
+
+ DEFINE_SMB3_SES_EVENT(ses_not_found);
+
++DECLARE_EVENT_CLASS(smb3_ioctl_class,
++ TP_PROTO(unsigned int xid,
++ __u64 fid,
++ unsigned int command),
++ TP_ARGS(xid, fid, command),
++ TP_STRUCT__entry(
++ __field(unsigned int, xid)
++ __field(__u64, fid)
++ __field(unsigned int, command)
++ ),
++ TP_fast_assign(
++ __entry->xid = xid;
++ __entry->fid = fid;
++ __entry->command = command;
++ ),
++ TP_printk("xid=%u fid=0x%llx ioctl cmd=0x%x",
++ __entry->xid, __entry->fid, __entry->command)
++)
++
++#define DEFINE_SMB3_IOCTL_EVENT(name) \
++DEFINE_EVENT(smb3_ioctl_class, smb3_##name, \
++ TP_PROTO(unsigned int xid, \
++ __u64 fid, \
++ unsigned int command), \
++ TP_ARGS(xid, fid, command))
++
++DEFINE_SMB3_IOCTL_EVENT(ioctl);
++
++
++
++
++
+ DECLARE_EVENT_CLASS(smb3_credit_class,
+ TP_PROTO(__u64 currmid,
+ __u64 conn_id,
+--
+2.43.0
+
diff --git a/queue-6.6/smb3-add-trace-event-for-mknod.patch b/queue-6.6/smb3-add-trace-event-for-mknod.patch
new file mode 100644
index 0000000000..e6a63207be
--- /dev/null
+++ b/queue-6.6/smb3-add-trace-event-for-mknod.patch
@@ -0,0 +1,86 @@
+From 98b55d3cd8298c48b5caf02de4fcf2553cd055aa Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 24 Mar 2024 00:01:02 -0500
+Subject: smb3: add trace event for mknod
+
+From: Steve French <stfrench@microsoft.com>
+
+[ Upstream commit e9e9fbeb83f65d3d487e0a0838c0867292c99fb2 ]
+
+Add trace points to help debug mknod and mkfifo:
+
+ smb3_mknod_done
+ smb3_mknod_enter
+ smb3_mknod_err
+
+Example output:
+
+ TASK-PID CPU# ||||| TIMESTAMP FUNCTION
+ | | | ||||| | |
+ mkfifo-6163 [003] ..... 960.425558: smb3_mknod_enter: xid=12 sid=0xb55130f6 tid=0x46e6241c path=\fifo1
+ mkfifo-6163 [003] ..... 960.432719: smb3_mknod_done: xid=12 sid=0xb55130f6 tid=0x46e6241c
+
+Reviewed-by: Bharath SM <bharathsm@microsoft.com>
+Reviewed-by: Meetakshi Setiya <msetiya@microsoft.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/dir.c | 7 +++++++
+ fs/smb/client/trace.h | 4 +++-
+ 2 files changed, 10 insertions(+), 1 deletion(-)
+
+diff --git a/fs/smb/client/dir.c b/fs/smb/client/dir.c
+index 37897b919dd5a..864b194dbaa0a 100644
+--- a/fs/smb/client/dir.c
++++ b/fs/smb/client/dir.c
+@@ -627,11 +627,18 @@ int cifs_mknod(struct mnt_idmap *idmap, struct inode *inode,
+ goto mknod_out;
+ }
+
++ trace_smb3_mknod_enter(xid, tcon->ses->Suid, tcon->tid, full_path);
++
+ rc = tcon->ses->server->ops->make_node(xid, inode, direntry, tcon,
+ full_path, mode,
+ device_number);
+
+ mknod_out:
++ if (rc)
++ trace_smb3_mknod_err(xid, tcon->ses->Suid, tcon->tid, rc);
++ else
++ trace_smb3_mknod_done(xid, tcon->ses->Suid, tcon->tid);
++
+ free_dentry_path(page);
+ free_xid(xid);
+ cifs_put_tlink(tlink);
+diff --git a/fs/smb/client/trace.h b/fs/smb/client/trace.h
+index f9c1fd32d0b8c..5e83cb9da9028 100644
+--- a/fs/smb/client/trace.h
++++ b/fs/smb/client/trace.h
+@@ -375,6 +375,7 @@ DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(get_reparse_compound_enter);
+ DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(delete_enter);
+ DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(mkdir_enter);
+ DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(tdis_enter);
++DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(mknod_enter);
+
+ DECLARE_EVENT_CLASS(smb3_inf_compound_done_class,
+ TP_PROTO(unsigned int xid,
+@@ -415,7 +416,7 @@ DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(query_wsl_ea_compound_done);
+ DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(delete_done);
+ DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(mkdir_done);
+ DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(tdis_done);
+-
++DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(mknod_done);
+
+ DECLARE_EVENT_CLASS(smb3_inf_compound_err_class,
+ TP_PROTO(unsigned int xid,
+@@ -461,6 +462,7 @@ DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(query_wsl_ea_compound_err);
+ DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(mkdir_err);
+ DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(delete_err);
+ DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(tdis_err);
++DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(mknod_err);
+
+ /*
+ * For logging SMB3 Status code and Command for responses which return errors
+--
+2.43.0
+
diff --git a/queue-6.6/smb3-clarify-some-of-the-unused-createoption-flags.patch b/queue-6.6/smb3-clarify-some-of-the-unused-createoption-flags.patch
new file mode 100644
index 0000000000..3deb6fbffa
--- /dev/null
+++ b/queue-6.6/smb3-clarify-some-of-the-unused-createoption-flags.patch
@@ -0,0 +1,51 @@
+From 58f6d04ffd3b6b136721256bc19e9b8ac32ab677 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 8 Oct 2023 23:11:38 -0500
+Subject: SMB3: clarify some of the unused CreateOption flags
+
+From: Steve French <stfrench@microsoft.com>
+
+[ Upstream commit d5a3c153fd00f5e951c4f20b4c65feb1e1cfbfcb ]
+
+Update comments to show flags which should be not set (zero).
+
+See MS-SMB2 section 2.2.13
+
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/common/smb2pdu.h | 9 ++++++++-
+ 1 file changed, 8 insertions(+), 1 deletion(-)
+
+diff --git a/fs/smb/common/smb2pdu.h b/fs/smb/common/smb2pdu.h
+index 22c94dea52116..465b721f2c06d 100644
+--- a/fs/smb/common/smb2pdu.h
++++ b/fs/smb/common/smb2pdu.h
+@@ -1100,16 +1100,23 @@ struct smb2_change_notify_rsp {
+ #define FILE_WRITE_THROUGH_LE cpu_to_le32(0x00000002)
+ #define FILE_SEQUENTIAL_ONLY_LE cpu_to_le32(0x00000004)
+ #define FILE_NO_INTERMEDIATE_BUFFERING_LE cpu_to_le32(0x00000008)
++/* FILE_SYNCHRONOUS_IO_ALERT_LE cpu_to_le32(0x00000010) should be zero, ignored */
++/* FILE_SYNCHRONOUS_IO_NONALERT cpu_to_le32(0x00000020) should be zero, ignored */
+ #define FILE_NON_DIRECTORY_FILE_LE cpu_to_le32(0x00000040)
+ #define FILE_COMPLETE_IF_OPLOCKED_LE cpu_to_le32(0x00000100)
+ #define FILE_NO_EA_KNOWLEDGE_LE cpu_to_le32(0x00000200)
++/* FILE_OPEN_REMOTE_INSTANCE cpu_to_le32(0x00000400) should be zero, ignored */
+ #define FILE_RANDOM_ACCESS_LE cpu_to_le32(0x00000800)
+-#define FILE_DELETE_ON_CLOSE_LE cpu_to_le32(0x00001000)
++#define FILE_DELETE_ON_CLOSE_LE cpu_to_le32(0x00001000) /* MBZ */
+ #define FILE_OPEN_BY_FILE_ID_LE cpu_to_le32(0x00002000)
+ #define FILE_OPEN_FOR_BACKUP_INTENT_LE cpu_to_le32(0x00004000)
+ #define FILE_NO_COMPRESSION_LE cpu_to_le32(0x00008000)
++/* FILE_OPEN_REQUIRING_OPLOCK cpu_to_le32(0x00010000) should be zero, ignored */
++/* FILE_DISALLOW_EXCLUSIVE cpu_to_le32(0x00020000) should be zero, ignored */
++/* FILE_RESERVE_OPFILTER cpu_to_le32(0x00100000) MBZ */
+ #define FILE_OPEN_REPARSE_POINT_LE cpu_to_le32(0x00200000)
+ #define FILE_OPEN_NO_RECALL_LE cpu_to_le32(0x00400000)
++/* #define FILE_OPEN_FOR_FREE_SPACE_QUERY cpu_to_le32(0x00800000) should be zero, ignored */
+ #define CREATE_OPTIONS_MASK_LE cpu_to_le32(0x00FFFFFF)
+
+ #define FILE_READ_RIGHTS_LE (FILE_READ_DATA_LE | FILE_READ_EA_LE \
+--
+2.43.0
+
diff --git a/queue-6.6/smb3-improve-exception-handling-in-allocate_mr_list.patch b/queue-6.6/smb3-improve-exception-handling-in-allocate_mr_list.patch
new file mode 100644
index 0000000000..8530f7fb64
--- /dev/null
+++ b/queue-6.6/smb3-improve-exception-handling-in-allocate_mr_list.patch
@@ -0,0 +1,48 @@
+From 681fd44c7ffaa056239439f513b37a6bb3b64b54 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 Dec 2023 20:43:12 +0100
+Subject: smb3: Improve exception handling in allocate_mr_list()
+
+From: Markus Elfring <elfring@users.sourceforge.net>
+
+[ Upstream commit 96d566b6c933be96e9f5b216f04024ab522e0465 ]
+
+The kfree() function was called in one case by
+the allocate_mr_list() function during error handling
+even if the passed variable contained a null pointer.
+This issue was detected by using the Coccinelle software.
+
+Thus use another label.
+
+Signed-off-by: Markus Elfring <elfring@users.sourceforge.net>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/smbdirect.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/fs/smb/client/smbdirect.c b/fs/smb/client/smbdirect.c
+index 94df9eec3d8d1..d74e829de51c2 100644
+--- a/fs/smb/client/smbdirect.c
++++ b/fs/smb/client/smbdirect.c
+@@ -2136,7 +2136,7 @@ static int allocate_mr_list(struct smbd_connection *info)
+ for (i = 0; i < info->responder_resources * 2; i++) {
+ smbdirect_mr = kzalloc(sizeof(*smbdirect_mr), GFP_KERNEL);
+ if (!smbdirect_mr)
+- goto out;
++ goto cleanup_entries;
+ smbdirect_mr->mr = ib_alloc_mr(info->pd, info->mr_type,
+ info->max_frmr_depth);
+ if (IS_ERR(smbdirect_mr->mr)) {
+@@ -2162,7 +2162,7 @@ static int allocate_mr_list(struct smbd_connection *info)
+
+ out:
+ kfree(smbdirect_mr);
+-
++cleanup_entries:
+ list_for_each_entry_safe(smbdirect_mr, tmp, &info->mr_list, list) {
+ list_del(&smbdirect_mr->list);
+ ib_dereg_mr(smbdirect_mr->mr);
+--
+2.43.0
+
diff --git a/queue-6.6/smb3-minor-cleanup-of-session-handling-code.patch b/queue-6.6/smb3-minor-cleanup-of-session-handling-code.patch
new file mode 100644
index 0000000000..54af60d293
--- /dev/null
+++ b/queue-6.6/smb3-minor-cleanup-of-session-handling-code.patch
@@ -0,0 +1,106 @@
+From be3a3d1ca2bee4e7e29a3ce103c84faa10e2cc55 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 6 Nov 2023 15:37:03 -0600
+Subject: smb3: minor cleanup of session handling code
+
+From: Steve French <stfrench@microsoft.com>
+
+[ Upstream commit f72d96507640835726d4f5ba26c1c11acbe1bc97 ]
+
+Minor cleanup of style issues found by checkpatch
+
+Reviewed-by: Bharath SM <bharathsm@microsoft.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/sess.c | 18 ++++++++++++------
+ 1 file changed, 12 insertions(+), 6 deletions(-)
+
+diff --git a/fs/smb/client/sess.c b/fs/smb/client/sess.c
+index 70a53dde83eec..09bb30610a901 100644
+--- a/fs/smb/client/sess.c
++++ b/fs/smb/client/sess.c
+@@ -108,6 +108,7 @@ cifs_chan_clear_in_reconnect(struct cifs_ses *ses,
+ struct TCP_Server_Info *server)
+ {
+ unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
++
+ if (chan_index == CIFS_INVAL_CHAN_INDEX)
+ return;
+
+@@ -119,6 +120,7 @@ cifs_chan_in_reconnect(struct cifs_ses *ses,
+ struct TCP_Server_Info *server)
+ {
+ unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
++
+ if (chan_index == CIFS_INVAL_CHAN_INDEX)
+ return true; /* err on the safer side */
+
+@@ -130,6 +132,7 @@ cifs_chan_set_need_reconnect(struct cifs_ses *ses,
+ struct TCP_Server_Info *server)
+ {
+ unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
++
+ if (chan_index == CIFS_INVAL_CHAN_INDEX)
+ return;
+
+@@ -143,6 +146,7 @@ cifs_chan_clear_need_reconnect(struct cifs_ses *ses,
+ struct TCP_Server_Info *server)
+ {
+ unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
++
+ if (chan_index == CIFS_INVAL_CHAN_INDEX)
+ return;
+
+@@ -156,6 +160,7 @@ cifs_chan_needs_reconnect(struct cifs_ses *ses,
+ struct TCP_Server_Info *server)
+ {
+ unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
++
+ if (chan_index == CIFS_INVAL_CHAN_INDEX)
+ return true; /* err on the safer side */
+
+@@ -167,6 +172,7 @@ cifs_chan_is_iface_active(struct cifs_ses *ses,
+ struct TCP_Server_Info *server)
+ {
+ unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
++
+ if (chan_index == CIFS_INVAL_CHAN_INDEX)
+ return true; /* err on the safer side */
+
+@@ -677,8 +683,7 @@ static __u32 cifs_ssetup_hdr(struct cifs_ses *ses,
+
+ /* Now no need to set SMBFLG_CASELESS or obsolete CANONICAL PATH */
+
+- /* BB verify whether signing required on neg or just on auth frame
+- (and NTLM case) */
++ /* BB verify whether signing required on neg or just auth frame (and NTLM case) */
+
+ capabilities = CAP_LARGE_FILES | CAP_NT_SMBS | CAP_LEVEL_II_OPLOCKS |
+ CAP_LARGE_WRITE_X | CAP_LARGE_READ_X;
+@@ -735,8 +740,10 @@ static void unicode_domain_string(char **pbcc_area, struct cifs_ses *ses,
+
+ /* copy domain */
+ if (ses->domainName == NULL) {
+- /* Sending null domain better than using a bogus domain name (as
+- we did briefly in 2.6.18) since server will use its default */
++ /*
++ * Sending null domain better than using a bogus domain name (as
++ * we did briefly in 2.6.18) since server will use its default
++ */
+ *bcc_ptr = 0;
+ *(bcc_ptr+1) = 0;
+ bytes_ret = 0;
+@@ -755,8 +762,7 @@ static void unicode_ssetup_strings(char **pbcc_area, struct cifs_ses *ses,
+ char *bcc_ptr = *pbcc_area;
+ int bytes_ret = 0;
+
+- /* BB FIXME add check that strings total less
+- than 335 or will need to send them as arrays */
++ /* BB FIXME add check that strings less than 335 or will need to send as arrays */
+
+ /* copy user */
+ if (ses->user_name == NULL) {
+--
+2.43.0
+
diff --git a/queue-6.6/smb3-minor-rdma-cleanup.patch b/queue-6.6/smb3-minor-rdma-cleanup.patch
new file mode 100644
index 0000000000..e4d9ffde66
--- /dev/null
+++ b/queue-6.6/smb3-minor-rdma-cleanup.patch
@@ -0,0 +1,43 @@
+From 30a5023bb14154df6fd059349d31218c13d2a3d4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 6 Nov 2023 13:31:45 -0600
+Subject: smb3: minor RDMA cleanup
+
+From: Steve French <stfrench@microsoft.com>
+
+[ Upstream commit 43960dc2328e554c4c61b22c47e77e8b1c48d854 ]
+
+Some minor smbdirect debug cleanup spotted by checkpatch
+
+Cc: Long Li <longli@microsoft.com>
+Reviewed-by: Bharath SM <bharathsm@microsoft.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/cifs_debug.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/fs/smb/client/cifs_debug.c b/fs/smb/client/cifs_debug.c
+index 058e703107fc7..aa95fa95ca112 100644
+--- a/fs/smb/client/cifs_debug.c
++++ b/fs/smb/client/cifs_debug.c
+@@ -779,14 +779,14 @@ static ssize_t name##_write(struct file *file, const char __user *buffer, \
+ size_t count, loff_t *ppos) \
+ { \
+ int rc; \
+- rc = kstrtoint_from_user(buffer, count, 10, & name); \
++ rc = kstrtoint_from_user(buffer, count, 10, &name); \
+ if (rc) \
+ return rc; \
+ return count; \
+ } \
+ static int name##_proc_show(struct seq_file *m, void *v) \
+ { \
+- seq_printf(m, "%d\n", name ); \
++ seq_printf(m, "%d\n", name); \
+ return 0; \
+ } \
+ static int name##_open(struct inode *inode, struct file *file) \
+--
+2.43.0
+
diff --git a/queue-6.6/smb3-more-minor-cleanups-for-session-handling-routin.patch b/queue-6.6/smb3-more-minor-cleanups-for-session-handling-routin.patch
new file mode 100644
index 0000000000..bb8a7c631e
--- /dev/null
+++ b/queue-6.6/smb3-more-minor-cleanups-for-session-handling-routin.patch
@@ -0,0 +1,71 @@
+From bf12a83b40ec4083af0c0f3f910752fe4312fd2e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 6 Nov 2023 22:40:38 -0600
+Subject: smb3: more minor cleanups for session handling routines
+
+From: Steve French <stfrench@microsoft.com>
+
+[ Upstream commit 1bc081b67a79b6e75fae686e98048cea1038ae31 ]
+
+Some trivial cleanup pointed out by checkpatch
+
+Reviewed-by: Bharath SM <bharathsm@microsoft.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/sess.c | 25 +++++++++++++++----------
+ 1 file changed, 15 insertions(+), 10 deletions(-)
+
+diff --git a/fs/smb/client/sess.c b/fs/smb/client/sess.c
+index bd4dcd1a9af83..70a53dde83eec 100644
+--- a/fs/smb/client/sess.c
++++ b/fs/smb/client/sess.c
+@@ -801,8 +801,7 @@ static void ascii_ssetup_strings(char **pbcc_area, struct cifs_ses *ses,
+ if (WARN_ON_ONCE(len < 0))
+ len = CIFS_MAX_DOMAINNAME_LEN - 1;
+ bcc_ptr += len;
+- } /* else we will send a null domain name
+- so the server will default to its own domain */
++ } /* else we send a null domain name so server will default to its own domain */
+ *bcc_ptr = 0;
+ bcc_ptr++;
+
+@@ -898,11 +897,14 @@ static void decode_ascii_ssetup(char **pbcc_area, __u16 bleft,
+ if (len > bleft)
+ return;
+
+- /* No domain field in LANMAN case. Domain is
+- returned by old servers in the SMB negprot response */
+- /* BB For newer servers which do not support Unicode,
+- but thus do return domain here we could add parsing
+- for it later, but it is not very important */
++ /*
++ * No domain field in LANMAN case. Domain is
++ * returned by old servers in the SMB negprot response
++ *
++ * BB For newer servers which do not support Unicode,
++ * but thus do return domain here, we could add parsing
++ * for it later, but it is not very important
++ */
+ cifs_dbg(FYI, "ascii: bytes left %d\n", bleft);
+ }
+ #endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
+@@ -958,9 +960,12 @@ int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len,
+ ses->ntlmssp->server_flags = server_flags;
+
+ memcpy(ses->ntlmssp->cryptkey, pblob->Challenge, CIFS_CRYPTO_KEY_SIZE);
+- /* In particular we can examine sign flags */
+- /* BB spec says that if AvId field of MsvAvTimestamp is populated then
+- we must set the MIC field of the AUTHENTICATE_MESSAGE */
++ /*
++ * In particular we can examine sign flags
++ *
++ * BB spec says that if AvId field of MsvAvTimestamp is populated then
++ * we must set the MIC field of the AUTHENTICATE_MESSAGE
++ */
+
+ tioffset = le32_to_cpu(pblob->TargetInfoArray.BufferOffset);
+ tilen = le16_to_cpu(pblob->TargetInfoArray.Length);
+--
+2.43.0
+
diff --git a/queue-6.6/smb3-update-allocation-size-more-accurately-on-write.patch b/queue-6.6/smb3-update-allocation-size-more-accurately-on-write.patch
new file mode 100644
index 0000000000..0139e5ea81
--- /dev/null
+++ b/queue-6.6/smb3-update-allocation-size-more-accurately-on-write.patch
@@ -0,0 +1,48 @@
+From fc5d8dd520420aa09d24b69ad279a23ffe0a34c0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 22 Feb 2024 00:26:52 -0600
+Subject: smb3: update allocation size more accurately on write completion
+
+From: Steve French <stfrench@microsoft.com>
+
+[ Upstream commit dbfdff402d89854126658376cbcb08363194d3cd ]
+
+Changes to allocation size are approximated for extending writes of cached
+files until the server returns the actual value (on SMB3 close or query info
+for example), but it was setting the estimated value for number of blocks
+to larger than the file size even if the file is likely sparse which
+breaks various xfstests (e.g. generic/129, 130, 221, 228).
+
+When i_size and i_blocks are updated in write completion do not increase
+allocation size more than what was written (rounded up to 512 bytes).
+
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/client/file.c | 9 ++++++++-
+ 1 file changed, 8 insertions(+), 1 deletion(-)
+
+diff --git a/fs/smb/client/file.c b/fs/smb/client/file.c
+index 6d44991e1ccdc..751ae89cefe36 100644
+--- a/fs/smb/client/file.c
++++ b/fs/smb/client/file.c
+@@ -3204,8 +3204,15 @@ static int cifs_write_end(struct file *file, struct address_space *mapping,
+ if (rc > 0) {
+ spin_lock(&inode->i_lock);
+ if (pos > inode->i_size) {
++ loff_t additional_blocks = (512 - 1 + copied) >> 9;
++
+ i_size_write(inode, pos);
+- inode->i_blocks = (512 - 1 + pos) >> 9;
++ /*
++ * Estimate new allocation size based on the amount written.
++ * This will be updated from server on close (and on queryinfo)
++ */
++ inode->i_blocks = min_t(blkcnt_t, (512 - 1 + pos) >> 9,
++ inode->i_blocks + additional_blocks);
+ }
+ spin_unlock(&inode->i_lock);
+ }
+--
+2.43.0
+
diff --git a/queue-6.6/smb311-additional-compression-flag-defined-in-update.patch b/queue-6.6/smb311-additional-compression-flag-defined-in-update.patch
new file mode 100644
index 0000000000..aba367075d
--- /dev/null
+++ b/queue-6.6/smb311-additional-compression-flag-defined-in-update.patch
@@ -0,0 +1,56 @@
+From 871f6c51097fd69b725ab79ed9043acb7c8d31a0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 19 Mar 2024 17:00:01 -0500
+Subject: smb311: additional compression flag defined in updated protocol spec
+
+From: Steve French <stfrench@microsoft.com>
+
+[ Upstream commit e56bc745fa1de77abc2ad8debc4b1b83e0426c49 ]
+
+Added new compression flag that was recently documented, in
+addition fix some typos and clarify the sid_attr_data struct
+definition.
+
+Reviewed-by: Bharath SM <bharathsm@microsoft.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/common/smb2pdu.h | 10 ++++++----
+ 1 file changed, 6 insertions(+), 4 deletions(-)
+
+diff --git a/fs/smb/common/smb2pdu.h b/fs/smb/common/smb2pdu.h
+index 735614d233a06..202ff91281560 100644
+--- a/fs/smb/common/smb2pdu.h
++++ b/fs/smb/common/smb2pdu.h
+@@ -280,15 +280,16 @@ struct smb3_blob_data {
+ #define SE_GROUP_RESOURCE 0x20000000
+ #define SE_GROUP_LOGON_ID 0xC0000000
+
+-/* struct sid_attr_data is SidData array in BlobData format then le32 Attr */
+-
+ struct sid_array_data {
+ __le16 SidAttrCount;
+ /* SidAttrList - array of sid_attr_data structs */
+ } __packed;
+
+-struct luid_attr_data {
+-
++/* struct sid_attr_data is SidData array in BlobData format then le32 Attr */
++struct sid_attr_data {
++ __le16 BlobSize;
++ __u8 BlobData[];
++ /* __le32 Attr */
+ } __packed;
+
+ /*
+@@ -502,6 +503,7 @@ struct smb2_encryption_neg_context {
+ #define SMB3_COMPRESS_LZ77_HUFF cpu_to_le16(0x0003)
+ /* Pattern scanning algorithm See MS-SMB2 3.1.4.4.1 */
+ #define SMB3_COMPRESS_PATTERN cpu_to_le16(0x0004) /* Pattern_V1 */
++#define SMB3_COMPRESS_LZ4 cpu_to_le16(0x0005)
+
+ /* Compression Flags */
+ #define SMB2_COMPRESSION_CAPABILITIES_FLAG_NONE cpu_to_le32(0x00000000)
+--
+2.43.0
+
diff --git a/queue-6.6/smb311-correct-incorrect-offset-field-in-compression.patch b/queue-6.6/smb311-correct-incorrect-offset-field-in-compression.patch
new file mode 100644
index 0000000000..9b70fc12d2
--- /dev/null
+++ b/queue-6.6/smb311-correct-incorrect-offset-field-in-compression.patch
@@ -0,0 +1,35 @@
+From 4efd28063a87b005c34d3a26603363d13979330f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 19 Mar 2024 15:59:38 -0500
+Subject: smb311: correct incorrect offset field in compression header
+
+From: Steve French <stfrench@microsoft.com>
+
+[ Upstream commit 68c5818a27afcb5cdddab041b82e9d47c996cb6a ]
+
+The offset field in the compression header is 32 bits not 16.
+
+Reviewed-by: Bharath SM <bharathsm@microsoft.com>
+Reported-by: Enzo Matsumiya <ematsumiya@suse.de>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/common/smb2pdu.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/fs/smb/common/smb2pdu.h b/fs/smb/common/smb2pdu.h
+index 61c6e72ccddc4..735614d233a06 100644
+--- a/fs/smb/common/smb2pdu.h
++++ b/fs/smb/common/smb2pdu.h
+@@ -227,7 +227,7 @@ struct smb2_compression_hdr {
+ __le32 OriginalCompressedSegmentSize;
+ __le16 CompressionAlgorithm;
+ __le16 Flags;
+- __le16 Offset; /* this is the size of the uncompressed SMB2 header below */
++ __le32 Offset; /* this is the size of the uncompressed SMB2 header below */
+ /* uncompressed SMB2 header (READ or WRITE) goes here */
+ /* compressed data goes here */
+ } __packed;
+--
+2.43.0
+