]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
Fixes for 6.6
authorSasha Levin <sashal@kernel.org>
Fri, 17 May 2024 19:00:02 +0000 (15:00 -0400)
committerSasha Levin <sashal@kernel.org>
Fri, 17 May 2024 19:00:02 +0000 (15:00 -0400)
Signed-off-by: Sasha Levin <sashal@kernel.org>
80 files changed:
queue-6.6/add-definition-for-new-smb3.1.1-command-type.patch [new file with mode: 0644]
queue-6.6/cifs-add-client-version-details-to-ntlm-authenticate.patch [new file with mode: 0644]
queue-6.6/cifs-add-tracing-for-the-cifs_tcon-struct-refcountin.patch [new file with mode: 0644]
queue-6.6/cifs-commands-that-are-retried-should-have-replay-fl.patch [new file with mode: 0644]
queue-6.6/cifs-defer-close-file-handles-having-rh-lease.patch [new file with mode: 0644]
queue-6.6/cifs-fix-in-logging-in-cifs_chan_update_iface.patch [new file with mode: 0644]
queue-6.6/cifs-fix-use-after-free-for-iface-while-disabling-se.patch [new file with mode: 0644]
queue-6.6/cifs-fixes-for-get_inode_info.patch [new file with mode: 0644]
queue-6.6/cifs-get-rid-of-dup-length-check-in-parse_reparse_po.patch [new file with mode: 0644]
queue-6.6/cifs-minor-comment-cleanup.patch [new file with mode: 0644]
queue-6.6/cifs-move-some-extern-decls-from-.c-files-to-.h.patch [new file with mode: 0644]
queue-6.6/cifs-new-mount-option-called-retrans.patch [new file with mode: 0644]
queue-6.6/cifs-new-nt-status-codes-from-ms-smb2.patch [new file with mode: 0644]
queue-6.6/cifs-pass-unbyteswapped-eof-value-into-smb2_set_eof.patch [new file with mode: 0644]
queue-6.6/cifs-pick-channel-for-tcon-and-tdis.patch [new file with mode: 0644]
queue-6.6/cifs-print-server-capabilities-in-debugdata.patch [new file with mode: 0644]
queue-6.6/cifs-remove-redundant-variable-assignment.patch [new file with mode: 0644]
queue-6.6/cifs-remove-redundant-variable-tcon_exist.patch [new file with mode: 0644]
queue-6.6/cifs-remove-unneeded-return-statement.patch [new file with mode: 0644]
queue-6.6/cifs-set-replay-flag-for-retries-of-write-command.patch [new file with mode: 0644]
queue-6.6/cifs-update-internal-module-version-number-for-cifs..patch [new file with mode: 0644]
queue-6.6/cifs-update-internal-module-version-number-for-cifs..patch-8866 [new file with mode: 0644]
queue-6.6/cifs-update-internal-module-version-number-for-cifs..patch-9055 [new file with mode: 0644]
queue-6.6/cifs-update-the-same-create_guid-on-replay.patch [new file with mode: 0644]
queue-6.6/ksmbd-add-continuous-availability-share-parameter.patch [new file with mode: 0644]
queue-6.6/ksmbd-add-kernel-doc-for-ksmbd_extract_sharename-fun.patch [new file with mode: 0644]
queue-6.6/ksmbd-add-support-for-durable-handles-v1-v2.patch [new file with mode: 0644]
queue-6.6/ksmbd-auth-fix-most-kernel-doc-warnings.patch [new file with mode: 0644]
queue-6.6/ksmbd-fix-possible-null-deref-in-smb_lazy_parent_lea.patch [new file with mode: 0644]
queue-6.6/ksmbd-fix-potencial-out-of-bounds-when-buffer-offset.patch [new file with mode: 0644]
queue-6.6/ksmbd-fix-slab-out-of-bounds-in-smb_strndup_from_utf.patch [new file with mode: 0644]
queue-6.6/ksmbd-fix-spelling-mistake-connction-connection.patch [new file with mode: 0644]
queue-6.6/ksmbd-mark-smb2_session_expired-to-session-when-dest.patch [new file with mode: 0644]
queue-6.6/ksmbd-vfs-fix-all-kernel-doc-warnings.patch [new file with mode: 0644]
queue-6.6/missing-field-not-being-returned-in-ioctl-cifs_ioc_g.patch [new file with mode: 0644]
queue-6.6/series [new file with mode: 0644]
queue-6.6/smb-client-add-support-for-wsl-reparse-points.patch [new file with mode: 0644]
queue-6.6/smb-client-allow-creating-special-files-via-reparse-.patch [new file with mode: 0644]
queue-6.6/smb-client-allow-creating-symlinks-via-reparse-point.patch [new file with mode: 0644]
queue-6.6/smb-client-cleanup-smb2_query_reparse_point.patch [new file with mode: 0644]
queue-6.6/smb-client-delete-true-false-defines.patch [new file with mode: 0644]
queue-6.6/smb-client-do-not-defer-close-open-handles-to-delete.patch [new file with mode: 0644]
queue-6.6/smb-client-don-t-clobber-i_rdev-from-cached-reparse-.patch [new file with mode: 0644]
queue-6.6/smb-client-extend-smb2_compound_op-to-accept-more-co.patch [new file with mode: 0644]
queue-6.6/smb-client-fix-a-null-vs-is_err-check-in-wsl_set_xat.patch [new file with mode: 0644]
queue-6.6/smb-client-fix-minor-whitespace-errors-and-warnings.patch [new file with mode: 0644]
queue-6.6/smb-client-fix-null-ptr-deref-in-cifs_mark_open_hand.patch [new file with mode: 0644]
queue-6.6/smb-client-fix-potential-broken-compound-request.patch [new file with mode: 0644]
queue-6.6/smb-client-get-rid-of-smb311_posix_query_path_info.patch [new file with mode: 0644]
queue-6.6/smb-client-handle-path-separator-of-created-smb-syml.patch [new file with mode: 0644]
queue-6.6/smb-client-handle-special-files-and-symlinks-in-smb3.patch [new file with mode: 0644]
queue-6.6/smb-client-instantiate-when-creating-sfu-files.patch [new file with mode: 0644]
queue-6.6/smb-client-introduce-cifs_sfu_make_node.patch [new file with mode: 0644]
queue-6.6/smb-client-introduce-reparse-mount-option.patch [new file with mode: 0644]
queue-6.6/smb-client-introduce-smb2_op_query_wsl_ea.patch [new file with mode: 0644]
queue-6.6/smb-client-move-most-of-reparse-point-handling-code-.patch [new file with mode: 0644]
queue-6.6/smb-client-negotiate-compression-algorithms.patch [new file with mode: 0644]
queue-6.6/smb-client-optimise-reparse-point-querying.patch [new file with mode: 0644]
queue-6.6/smb-client-parse-owner-group-when-creating-reparse-p.patch [new file with mode: 0644]
queue-6.6/smb-client-parse-uid-gid-mode-and-dev-from-wsl-repar.patch [new file with mode: 0644]
queue-6.6/smb-client-reduce-number-of-parameters-in-smb2_compo.patch [new file with mode: 0644]
queue-6.6/smb-client-retry-compound-request-without-reusing-le.patch [new file with mode: 0644]
queue-6.6/smb-client-return-reparse-type-in-proc-mounts.patch [new file with mode: 0644]
queue-6.6/smb-client-reuse-file-lease-key-in-compound-operatio.patch [new file with mode: 0644]
queue-6.6/smb-client-set-correct-d_type-for-reparse-dfs-dfsr-a.patch [new file with mode: 0644]
queue-6.6/smb-common-fix-fields-sizes-in-compression_pattern_p.patch [new file with mode: 0644]
queue-6.6/smb-common-simplify-compression-headers.patch [new file with mode: 0644]
queue-6.6/smb-fix-some-kernel-doc-comments.patch [new file with mode: 0644]
queue-6.6/smb-smb2pdu.h-avoid-wflex-array-member-not-at-end-wa.patch [new file with mode: 0644]
queue-6.6/smb-use-crypto_shash_digest-in-symlink_hash.patch [new file with mode: 0644]
queue-6.6/smb3-add-dynamic-trace-point-for-ioctls.patch [new file with mode: 0644]
queue-6.6/smb3-add-trace-event-for-mknod.patch [new file with mode: 0644]
queue-6.6/smb3-clarify-some-of-the-unused-createoption-flags.patch [new file with mode: 0644]
queue-6.6/smb3-improve-exception-handling-in-allocate_mr_list.patch [new file with mode: 0644]
queue-6.6/smb3-minor-cleanup-of-session-handling-code.patch [new file with mode: 0644]
queue-6.6/smb3-minor-rdma-cleanup.patch [new file with mode: 0644]
queue-6.6/smb3-more-minor-cleanups-for-session-handling-routin.patch [new file with mode: 0644]
queue-6.6/smb3-update-allocation-size-more-accurately-on-write.patch [new file with mode: 0644]
queue-6.6/smb311-additional-compression-flag-defined-in-update.patch [new file with mode: 0644]
queue-6.6/smb311-correct-incorrect-offset-field-in-compression.patch [new file with mode: 0644]

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 (file)
index 0000000..bea45d7
--- /dev/null
@@ -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 (file)
index 0000000..af7d1db
--- /dev/null
@@ -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 (file)
index 0000000..5cc3bb2
--- /dev/null
@@ -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 (file)
index 0000000..6c69dae
--- /dev/null
@@ -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 (file)
index 0000000..dc4ccf7
--- /dev/null
@@ -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 (file)
index 0000000..4196638
--- /dev/null
@@ -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 (file)
index 0000000..4c4cb5d
--- /dev/null
@@ -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 (file)
index 0000000..96c0767
--- /dev/null
@@ -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 (file)
index 0000000..c80659b
--- /dev/null
@@ -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 (file)
index 0000000..a415c3f
--- /dev/null
@@ -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 (file)
index 0000000..33a2d59
--- /dev/null
@@ -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 (file)
index 0000000..8dd84c1
--- /dev/null
@@ -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 (file)
index 0000000..2d9bd1b
--- /dev/null
@@ -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 (file)
index 0000000..edad986
--- /dev/null
@@ -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 (file)
index 0000000..8b8ba87
--- /dev/null
@@ -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 (file)
index 0000000..4f29fb7
--- /dev/null
@@ -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 (file)
index 0000000..4563310
--- /dev/null
@@ -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 (file)
index 0000000..522f957
--- /dev/null
@@ -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 (file)
index 0000000..f0a33af
--- /dev/null
@@ -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 (file)
index 0000000..67f1d20
--- /dev/null
@@ -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 (file)
index 0000000..cddc478
--- /dev/null
@@ -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 (file)
index 0000000..c3e4124
--- /dev/null
@@ -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 (file)
index 0000000..c828ce1
--- /dev/null
@@ -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 (file)
index 0000000..d8abebc
--- /dev/null
@@ -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 (file)
index 0000000..082e98d
--- /dev/null
@@ -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 (file)
index 0000000..3c1a101
--- /dev/null
@@ -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 (file)
index 0000000..270dc60
--- /dev/null
@@ -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 (file)
index 0000000..5c465cd
--- /dev/null
@@ -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 (file)
index 0000000..c7689a2
--- /dev/null
@@ -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 (file)
index 0000000..dbc3eb6
--- /dev/null
@@ -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 (file)
index 0000000..09d9696
--- /dev/null
@@ -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 (file)
index 0000000..15d8854
--- /dev/null
@@ -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 (file)
index 0000000..6bfaca9
--- /dev/null
@@ -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 (file)
index 0000000..2b44bf8
--- /dev/null
@@ -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 (file)
index 0000000..6d773e7
--- /dev/null
@@ -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 (file)
index 0000000..167c196
--- /dev/null
@@ -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 (file)
index 0000000..a08d604
--- /dev/null
@@ -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 (file)
index 0000000..da5a26a
--- /dev/null
@@ -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 (file)
index 0000000..73479a6
--- /dev/null
@@ -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 (file)
index 0000000..e837c48
--- /dev/null
@@ -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 (file)
index 0000000..a84e843
--- /dev/null
@@ -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 (file)
index 0000000..49ced98
--- /dev/null
@@ -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 (file)
index 0000000..cd02460
--- /dev/null
@@ -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 (file)
index 0000000..1969ded
--- /dev/null
@@ -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 (file)
index 0000000..c349bba
--- /dev/null
@@ -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 (file)
index 0000000..52a4df4
--- /dev/null
@@ -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 (file)
index 0000000..4a21c93
--- /dev/null
@@ -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 (file)
index 0000000..01df675
--- /dev/null
@@ -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 (file)
index 0000000..39b97a0
--- /dev/null
@@ -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 (file)
index 0000000..8ba8328
--- /dev/null
@@ -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 (file)
index 0000000..86c9cfa
--- /dev/null
@@ -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 (file)
index 0000000..582b2ad
--- /dev/null
@@ -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 (file)
index 0000000..4589dac
--- /dev/null
@@ -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 (file)
index 0000000..3914990
--- /dev/null
@@ -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 (file)
index 0000000..e97b450
--- /dev/null
@@ -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 (file)
index 0000000..328c868
--- /dev/null
@@ -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 (file)
index 0000000..c76a72e
--- /dev/null
@@ -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 (file)
index 0000000..b191659
--- /dev/null
@@ -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 (file)
index 0000000..fc76fff
--- /dev/null
@@ -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 (file)
index 0000000..0fd2c1e
--- /dev/null
@@ -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 (file)
index 0000000..ee7afd4
--- /dev/null
@@ -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 (file)
index 0000000..e0bb021
--- /dev/null
@@ -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 (file)
index 0000000..aeeff34
--- /dev/null
@@ -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 (file)
index 0000000..3afaa1b
--- /dev/null
@@ -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 (file)
index 0000000..9649676
--- /dev/null
@@ -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 (file)
index 0000000..bf21aa5
--- /dev/null
@@ -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 (file)
index 0000000..f90e182
--- /dev/null
@@ -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 (file)
index 0000000..74fb45d
--- /dev/null
@@ -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 (file)
index 0000000..786f4d5
--- /dev/null
@@ -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 (file)
index 0000000..4465f62
--- /dev/null
@@ -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 (file)
index 0000000..b601b4d
--- /dev/null
@@ -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 (file)
index 0000000..e6a6320
--- /dev/null
@@ -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 (file)
index 0000000..3deb6fb
--- /dev/null
@@ -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 (file)
index 0000000..8530f7f
--- /dev/null
@@ -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 (file)
index 0000000..54af60d
--- /dev/null
@@ -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 (file)
index 0000000..e4d9ffd
--- /dev/null
@@ -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 (file)
index 0000000..bb8a7c6
--- /dev/null
@@ -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 (file)
index 0000000..0139e5e
--- /dev/null
@@ -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 (file)
index 0000000..aba3670
--- /dev/null
@@ -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 (file)
index 0000000..9b70fc1
--- /dev/null
@@ -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
+