diff options
80 files changed, 14157 insertions, 0 deletions
diff --git a/queue-6.6/add-definition-for-new-smb3.1.1-command-type.patch b/queue-6.6/add-definition-for-new-smb3.1.1-command-type.patch new file mode 100644 index 0000000000..bea45d76d2 --- /dev/null +++ b/queue-6.6/add-definition-for-new-smb3.1.1-command-type.patch @@ -0,0 +1,62 @@ +From e844eeb86be35af7041595f96bb403effdc7f3ca Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Sun, 8 Oct 2023 23:04:01 -0500 +Subject: Add definition for new smb3.1.1 command type + +From: Steve French <stfrench@microsoft.com> + +[ Upstream commit 7588b83066db9b9dc10c1a43b8e52a028ad327d2 ] + +Add structs and defines for new SMB3.1.1 command, server to client notification. + +See MS-SMB2 section 2.2.44 + +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/common/smb2pdu.h | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + +diff --git a/fs/smb/common/smb2pdu.h b/fs/smb/common/smb2pdu.h +index 465b721f2c06d..a233a24352b1f 100644 +--- a/fs/smb/common/smb2pdu.h ++++ b/fs/smb/common/smb2pdu.h +@@ -34,6 +34,7 @@ + #define SMB2_QUERY_INFO_HE 0x0010 + #define SMB2_SET_INFO_HE 0x0011 + #define SMB2_OPLOCK_BREAK_HE 0x0012 ++#define SMB2_SERVER_TO_CLIENT_NOTIFICATION 0x0013 + + /* The same list in little endian */ + #define SMB2_NEGOTIATE cpu_to_le16(SMB2_NEGOTIATE_HE) +@@ -411,6 +412,7 @@ struct smb2_tree_disconnect_rsp { + #define SMB2_GLOBAL_CAP_PERSISTENT_HANDLES 0x00000010 /* New to SMB3 */ + #define SMB2_GLOBAL_CAP_DIRECTORY_LEASING 0x00000020 /* New to SMB3 */ + #define SMB2_GLOBAL_CAP_ENCRYPTION 0x00000040 /* New to SMB3 */ ++#define SMB2_GLOBAL_CAP_NOTIFICATIONS 0x00000080 /* New to SMB3.1.1 */ + /* Internal types */ + #define SMB2_NT_FIND 0x00100000 + #define SMB2_LARGE_FILES 0x00200000 +@@ -984,6 +986,19 @@ struct smb2_change_notify_rsp { + __u8 Buffer[]; /* array of file notify structs */ + } __packed; + ++/* ++ * SMB2_SERVER_TO_CLIENT_NOTIFICATION: See MS-SMB2 section 2.2.44 ++ */ ++ ++#define SMB2_NOTIFY_SESSION_CLOSED 0x0000 ++ ++struct smb2_server_client_notification { ++ struct smb2_hdr hdr; ++ __le16 StructureSize; ++ __u16 Reserved; /* MBZ */ ++ __le32 NotificationType; ++ __u8 NotificationBuffer[4]; /* MBZ */ ++} __packed; + + /* + * SMB2_CREATE See MS-SMB2 section 2.2.13 +-- +2.43.0 + diff --git a/queue-6.6/cifs-add-client-version-details-to-ntlm-authenticate.patch b/queue-6.6/cifs-add-client-version-details-to-ntlm-authenticate.patch new file mode 100644 index 0000000000..af7d1db3ac --- /dev/null +++ b/queue-6.6/cifs-add-client-version-details-to-ntlm-authenticate.patch @@ -0,0 +1,67 @@ +From c3c76c2a5c25d221c573de4ee7480522612d15ec Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Wed, 4 Oct 2023 07:17:55 -0400 +Subject: cifs: Add client version details to NTLM authenticate message + +From: Meetakshi Setiya <msetiya@microsoft.com> + +[ Upstream commit 1460720c5913c11415e4d7c4df5a287eb2ad3f3e ] + +The NTLM authenticate message currently sets the NTLMSSP_NEGOTIATE_VERSION +flag but does not populate the VERSION structure. This commit fixes this +bug by ensuring that the flag is set and the version details are included +in the message. + +Signed-off-by: Meetakshi Setiya <msetiya@microsoft.com> +Reviewed-by: Bharath SM <bharathsm@microsoft.com> +Reviewed-by: Paulo Alcantara (SUSE) <pc@manguebit.com> +Reviewed-by: Shyam Prasad N <sprasad@microsoft.com> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/client/ntlmssp.h | 4 ++-- + fs/smb/client/sess.c | 12 +++++++++--- + 2 files changed, 11 insertions(+), 5 deletions(-) + +diff --git a/fs/smb/client/ntlmssp.h b/fs/smb/client/ntlmssp.h +index 2c5dde2ece588..875de43b72de3 100644 +--- a/fs/smb/client/ntlmssp.h ++++ b/fs/smb/client/ntlmssp.h +@@ -133,8 +133,8 @@ typedef struct _AUTHENTICATE_MESSAGE { + SECURITY_BUFFER WorkstationName; + SECURITY_BUFFER SessionKey; + __le32 NegotiateFlags; +- /* SECURITY_BUFFER for version info not present since we +- do not set the version is present flag */ ++ struct ntlmssp_version Version; ++ /* SECURITY_BUFFER */ + char UserString[]; + } __attribute__((packed)) AUTHENTICATE_MESSAGE, *PAUTHENTICATE_MESSAGE; + +diff --git a/fs/smb/client/sess.c b/fs/smb/client/sess.c +index e4168cd8b6c28..bd4dcd1a9af83 100644 +--- a/fs/smb/client/sess.c ++++ b/fs/smb/client/sess.c +@@ -1201,10 +1201,16 @@ int build_ntlmssp_auth_blob(unsigned char **pbuffer, + memcpy(sec_blob->Signature, NTLMSSP_SIGNATURE, 8); + sec_blob->MessageType = NtLmAuthenticate; + ++ /* send version information in ntlmssp authenticate also */ + flags = ses->ntlmssp->server_flags | NTLMSSP_REQUEST_TARGET | +- NTLMSSP_NEGOTIATE_TARGET_INFO | NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED; +- /* we only send version information in ntlmssp negotiate, so do not set this flag */ +- flags = flags & ~NTLMSSP_NEGOTIATE_VERSION; ++ NTLMSSP_NEGOTIATE_TARGET_INFO | NTLMSSP_NEGOTIATE_VERSION | ++ NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED; ++ ++ sec_blob->Version.ProductMajorVersion = LINUX_VERSION_MAJOR; ++ sec_blob->Version.ProductMinorVersion = LINUX_VERSION_PATCHLEVEL; ++ sec_blob->Version.ProductBuild = cpu_to_le16(SMB3_PRODUCT_BUILD); ++ sec_blob->Version.NTLMRevisionCurrent = NTLMSSP_REVISION_W2K3; ++ + tmp = *pbuffer + sizeof(AUTHENTICATE_MESSAGE); + sec_blob->NegotiateFlags = cpu_to_le32(flags); + +-- +2.43.0 + diff --git a/queue-6.6/cifs-add-tracing-for-the-cifs_tcon-struct-refcountin.patch b/queue-6.6/cifs-add-tracing-for-the-cifs_tcon-struct-refcountin.patch new file mode 100644 index 0000000000..5cc3bb2c3c --- /dev/null +++ b/queue-6.6/cifs-add-tracing-for-the-cifs_tcon-struct-refcountin.patch @@ -0,0 +1,516 @@ +From e5af1e8ee41c311c93e243e739a6f2d2535bd161 Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Thu, 4 Apr 2024 13:51:36 +0100 +Subject: cifs: Add tracing for the cifs_tcon struct refcounting + +From: David Howells <dhowells@redhat.com> + +[ Upstream commit afc23febd51c7e24361e3a9c09f3e892eb0a41ea ] + +Add tracing for the refcounting/lifecycle of the cifs_tcon struct, marking +different events with different labels and giving each tcon its own debug +ID so that the tracelines corresponding to individual tcons can be +distinguished. This can be enabled with: + + echo 1 >/sys/kernel/debug/tracing/events/cifs/smb3_tcon_ref/enable + +Signed-off-by: David Howells <dhowells@redhat.com> +Acked-by: Paulo Alcantara (Red Hat) <pc@manguebit.com> +cc: Shyam Prasad N <nspmangalore@gmail.com> +cc: linux-cifs@vger.kernel.org +cc: linux-fsdevel@vger.kernel.org +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/client/cifsfs.c | 2 + + fs/smb/client/cifsglob.h | 1 + + fs/smb/client/cifsproto.h | 9 ++-- + fs/smb/client/connect.c | 21 ++++---- + fs/smb/client/fscache.c | 7 +++ + fs/smb/client/misc.c | 10 ++-- + fs/smb/client/smb2misc.c | 10 ++-- + fs/smb/client/smb2ops.c | 7 ++- + fs/smb/client/smb2pdu.c | 8 +-- + fs/smb/client/smb2transport.c | 2 + + fs/smb/client/trace.h | 92 ++++++++++++++++++++++++++++++++++- + 11 files changed, 143 insertions(+), 26 deletions(-) + +diff --git a/fs/smb/client/cifsfs.c b/fs/smb/client/cifsfs.c +index 539ac9774de1b..f1dcb86ab9894 100644 +--- a/fs/smb/client/cifsfs.c ++++ b/fs/smb/client/cifsfs.c +@@ -739,6 +739,8 @@ static void cifs_umount_begin(struct super_block *sb) + + spin_lock(&cifs_tcp_ses_lock); + spin_lock(&tcon->tc_lock); ++ trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count, ++ netfs_trace_tcon_ref_see_umount); + if ((tcon->tc_count > 1) || (tcon->status == TID_EXITING)) { + /* we have other mounts to same share or we have + already tried to umount this and woken up +diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h +index 9597887280ff3..c146f83eba9b4 100644 +--- a/fs/smb/client/cifsglob.h ++++ b/fs/smb/client/cifsglob.h +@@ -1190,6 +1190,7 @@ struct cifs_fattr { + */ + struct cifs_tcon { + struct list_head tcon_list; ++ int debug_id; /* Debugging for tracing */ + int tc_count; + struct list_head rlist; /* reconnect list */ + spinlock_t tc_lock; /* protect anything here that is not protected */ +diff --git a/fs/smb/client/cifsproto.h b/fs/smb/client/cifsproto.h +index 8e0a348f1f660..fbc358c09da3b 100644 +--- a/fs/smb/client/cifsproto.h ++++ b/fs/smb/client/cifsproto.h +@@ -303,7 +303,7 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx, + struct TCP_Server_Info *primary_server); + extern void cifs_put_tcp_session(struct TCP_Server_Info *server, + int from_reconnect); +-extern void cifs_put_tcon(struct cifs_tcon *tcon); ++extern void cifs_put_tcon(struct cifs_tcon *tcon, enum smb3_tcon_ref_trace trace); + + extern void cifs_release_automount_timer(void); + +@@ -530,8 +530,9 @@ extern int CIFSSMBLogoff(const unsigned int xid, struct cifs_ses *ses); + + extern struct cifs_ses *sesInfoAlloc(void); + extern void sesInfoFree(struct cifs_ses *); +-extern struct cifs_tcon *tcon_info_alloc(bool dir_leases_enabled); +-extern void tconInfoFree(struct cifs_tcon *); ++extern struct cifs_tcon *tcon_info_alloc(bool dir_leases_enabled, ++ enum smb3_tcon_ref_trace trace); ++extern void tconInfoFree(struct cifs_tcon *tcon, enum smb3_tcon_ref_trace trace); + + extern int cifs_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server, + __u32 *pexpected_response_sequence_number); +@@ -721,8 +722,6 @@ static inline int cifs_create_options(struct cifs_sb_info *cifs_sb, int options) + return options; + } + +-struct super_block *cifs_get_tcon_super(struct cifs_tcon *tcon); +-void cifs_put_tcon_super(struct super_block *sb); + int cifs_wait_for_server_reconnect(struct TCP_Server_Info *server, bool retry); + + /* Put references of @ses and its children */ +diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c +index 4e35970681bf0..7a16e12f5da87 100644 +--- a/fs/smb/client/connect.c ++++ b/fs/smb/client/connect.c +@@ -1943,7 +1943,7 @@ cifs_setup_ipc(struct cifs_ses *ses, struct smb3_fs_context *ctx) + } + + /* no need to setup directory caching on IPC share, so pass in false */ +- tcon = tcon_info_alloc(false); ++ tcon = tcon_info_alloc(false, netfs_trace_tcon_ref_new_ipc); + if (tcon == NULL) + return -ENOMEM; + +@@ -1960,7 +1960,7 @@ cifs_setup_ipc(struct cifs_ses *ses, struct smb3_fs_context *ctx) + + if (rc) { + cifs_server_dbg(VFS, "failed to connect to IPC (rc=%d)\n", rc); +- tconInfoFree(tcon); ++ tconInfoFree(tcon, netfs_trace_tcon_ref_free_ipc_fail); + goto out; + } + +@@ -2043,7 +2043,7 @@ void __cifs_put_smb_ses(struct cifs_ses *ses) + * files on session close, as specified in MS-SMB2 3.3.5.6 Receiving an + * SMB2 LOGOFF Request. + */ +- tconInfoFree(tcon); ++ tconInfoFree(tcon, netfs_trace_tcon_ref_free_ipc); + if (do_logoff) { + xid = get_xid(); + rc = server->ops->logoff(xid, ses); +@@ -2432,6 +2432,8 @@ cifs_find_tcon(struct cifs_ses *ses, struct smb3_fs_context *ctx) + continue; + } + ++tcon->tc_count; ++ trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count, ++ netfs_trace_tcon_ref_get_find); + spin_unlock(&tcon->tc_lock); + spin_unlock(&cifs_tcp_ses_lock); + return tcon; +@@ -2441,7 +2443,7 @@ cifs_find_tcon(struct cifs_ses *ses, struct smb3_fs_context *ctx) + } + + void +-cifs_put_tcon(struct cifs_tcon *tcon) ++cifs_put_tcon(struct cifs_tcon *tcon, enum smb3_tcon_ref_trace trace) + { + unsigned int xid; + struct cifs_ses *ses; +@@ -2457,6 +2459,7 @@ cifs_put_tcon(struct cifs_tcon *tcon) + cifs_dbg(FYI, "%s: tc_count=%d\n", __func__, tcon->tc_count); + spin_lock(&cifs_tcp_ses_lock); + spin_lock(&tcon->tc_lock); ++ trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count - 1, trace); + if (--tcon->tc_count > 0) { + spin_unlock(&tcon->tc_lock); + spin_unlock(&cifs_tcp_ses_lock); +@@ -2493,7 +2496,7 @@ cifs_put_tcon(struct cifs_tcon *tcon) + _free_xid(xid); + + cifs_fscache_release_super_cookie(tcon); +- tconInfoFree(tcon); ++ tconInfoFree(tcon, netfs_trace_tcon_ref_free); + cifs_put_smb_ses(ses); + } + +@@ -2547,7 +2550,7 @@ cifs_get_tcon(struct cifs_ses *ses, struct smb3_fs_context *ctx) + nohandlecache = ctx->nohandlecache; + else + nohandlecache = true; +- tcon = tcon_info_alloc(!nohandlecache); ++ tcon = tcon_info_alloc(!nohandlecache, netfs_trace_tcon_ref_new); + if (tcon == NULL) { + rc = -ENOMEM; + goto out_fail; +@@ -2737,7 +2740,7 @@ cifs_get_tcon(struct cifs_ses *ses, struct smb3_fs_context *ctx) + return tcon; + + out_fail: +- tconInfoFree(tcon); ++ tconInfoFree(tcon, netfs_trace_tcon_ref_free_fail); + return ERR_PTR(rc); + } + +@@ -2754,7 +2757,7 @@ cifs_put_tlink(struct tcon_link *tlink) + } + + if (!IS_ERR(tlink_tcon(tlink))) +- cifs_put_tcon(tlink_tcon(tlink)); ++ cifs_put_tcon(tlink_tcon(tlink), netfs_trace_tcon_ref_put_tlink); + kfree(tlink); + } + +@@ -3319,7 +3322,7 @@ void cifs_mount_put_conns(struct cifs_mount_ctx *mnt_ctx) + int rc = 0; + + if (mnt_ctx->tcon) +- cifs_put_tcon(mnt_ctx->tcon); ++ cifs_put_tcon(mnt_ctx->tcon, netfs_trace_tcon_ref_put_mnt_ctx); + else if (mnt_ctx->ses) + cifs_put_smb_ses(mnt_ctx->ses); + else if (mnt_ctx->server) +diff --git a/fs/smb/client/fscache.c b/fs/smb/client/fscache.c +index ecabc4b400535..98c5eebdc7b2f 100644 +--- a/fs/smb/client/fscache.c ++++ b/fs/smb/client/fscache.c +@@ -94,6 +94,11 @@ int cifs_fscache_get_super_cookie(struct cifs_tcon *tcon) + } + pr_err("Cache volume key already in use (%s)\n", key); + vcookie = NULL; ++ trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count, ++ netfs_trace_tcon_ref_see_fscache_collision); ++ } else { ++ trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count, ++ netfs_trace_tcon_ref_see_fscache_okay); + } + + tcon->fscache = vcookie; +@@ -115,6 +120,8 @@ void cifs_fscache_release_super_cookie(struct cifs_tcon *tcon) + cifs_fscache_fill_volume_coherency(tcon, &cd); + fscache_relinquish_volume(tcon->fscache, &cd, false); + tcon->fscache = NULL; ++ trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count, ++ netfs_trace_tcon_ref_see_fscache_relinq); + } + + void cifs_fscache_get_inode_cookie(struct inode *inode) +diff --git a/fs/smb/client/misc.c b/fs/smb/client/misc.c +index ad44f8d66b377..07c468ddb88a8 100644 +--- a/fs/smb/client/misc.c ++++ b/fs/smb/client/misc.c +@@ -111,9 +111,10 @@ sesInfoFree(struct cifs_ses *buf_to_free) + } + + struct cifs_tcon * +-tcon_info_alloc(bool dir_leases_enabled) ++tcon_info_alloc(bool dir_leases_enabled, enum smb3_tcon_ref_trace trace) + { + struct cifs_tcon *ret_buf; ++ static atomic_t tcon_debug_id; + + ret_buf = kzalloc(sizeof(*ret_buf), GFP_KERNEL); + if (!ret_buf) +@@ -130,7 +131,8 @@ tcon_info_alloc(bool dir_leases_enabled) + + atomic_inc(&tconInfoAllocCount); + ret_buf->status = TID_NEW; +- ++ret_buf->tc_count; ++ ret_buf->debug_id = atomic_inc_return(&tcon_debug_id); ++ ret_buf->tc_count = 1; + spin_lock_init(&ret_buf->tc_lock); + INIT_LIST_HEAD(&ret_buf->openFileList); + INIT_LIST_HEAD(&ret_buf->tcon_list); +@@ -142,17 +144,19 @@ tcon_info_alloc(bool dir_leases_enabled) + #ifdef CONFIG_CIFS_FSCACHE + mutex_init(&ret_buf->fscache_lock); + #endif ++ trace_smb3_tcon_ref(ret_buf->debug_id, ret_buf->tc_count, trace); + + return ret_buf; + } + + void +-tconInfoFree(struct cifs_tcon *tcon) ++tconInfoFree(struct cifs_tcon *tcon, enum smb3_tcon_ref_trace trace) + { + if (tcon == NULL) { + cifs_dbg(FYI, "Null buffer passed to tconInfoFree\n"); + return; + } ++ trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count, trace); + free_cached_dirs(tcon->cfids); + atomic_dec(&tconInfoAllocCount); + kfree(tcon->nativeFileSystem); +diff --git a/fs/smb/client/smb2misc.c b/fs/smb/client/smb2misc.c +index cc72be5a93a93..677ef6f99a5be 100644 +--- a/fs/smb/client/smb2misc.c ++++ b/fs/smb/client/smb2misc.c +@@ -767,7 +767,7 @@ smb2_cancelled_close_fid(struct work_struct *work) + if (rc) + cifs_tcon_dbg(VFS, "Close cancelled mid failed rc:%d\n", rc); + +- cifs_put_tcon(tcon); ++ cifs_put_tcon(tcon, netfs_trace_tcon_ref_put_cancelled_close_fid); + kfree(cancelled); + } + +@@ -811,6 +811,8 @@ smb2_handle_cancelled_close(struct cifs_tcon *tcon, __u64 persistent_fid, + if (tcon->tc_count <= 0) { + struct TCP_Server_Info *server = NULL; + ++ trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count, ++ netfs_trace_tcon_ref_see_cancelled_close); + WARN_ONCE(tcon->tc_count < 0, "tcon refcount is negative"); + spin_unlock(&cifs_tcp_ses_lock); + +@@ -823,12 +825,14 @@ smb2_handle_cancelled_close(struct cifs_tcon *tcon, __u64 persistent_fid, + return 0; + } + tcon->tc_count++; ++ trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count, ++ netfs_trace_tcon_ref_get_cancelled_close); + spin_unlock(&cifs_tcp_ses_lock); + + rc = __smb2_handle_cancelled_cmd(tcon, SMB2_CLOSE_HE, 0, + persistent_fid, volatile_fid); + if (rc) +- cifs_put_tcon(tcon); ++ cifs_put_tcon(tcon, netfs_trace_tcon_ref_put_cancelled_close); + + return rc; + } +@@ -856,7 +860,7 @@ smb2_handle_cancelled_mid(struct mid_q_entry *mid, struct TCP_Server_Info *serve + rsp->PersistentFileId, + rsp->VolatileFileId); + if (rc) +- cifs_put_tcon(tcon); ++ cifs_put_tcon(tcon, netfs_trace_tcon_ref_put_cancelled_mid); + + return rc; + } +diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c +index df6c6d31236ad..66cfce456263b 100644 +--- a/fs/smb/client/smb2ops.c ++++ b/fs/smb/client/smb2ops.c +@@ -2915,8 +2915,11 @@ smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses, + tcon = list_first_entry_or_null(&ses->tcon_list, + struct cifs_tcon, + tcon_list); +- if (tcon) ++ if (tcon) { + tcon->tc_count++; ++ trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count, ++ netfs_trace_tcon_ref_get_dfs_refer); ++ } + spin_unlock(&cifs_tcp_ses_lock); + } + +@@ -2980,6 +2983,8 @@ smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses, + /* ipc tcons are not refcounted */ + spin_lock(&cifs_tcp_ses_lock); + tcon->tc_count--; ++ trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count, ++ netfs_trace_tcon_ref_dec_dfs_refer); + /* tc_count can never go negative */ + WARN_ON(tcon->tc_count < 0); + spin_unlock(&cifs_tcp_ses_lock); +diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c +index 86c647a947ccd..a5efce03cb58e 100644 +--- a/fs/smb/client/smb2pdu.c ++++ b/fs/smb/client/smb2pdu.c +@@ -4138,6 +4138,8 @@ void smb2_reconnect_server(struct work_struct *work) + list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { + if (tcon->need_reconnect || tcon->need_reopen_files) { + tcon->tc_count++; ++ trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count, ++ netfs_trace_tcon_ref_get_reconnect_server); + list_add_tail(&tcon->rlist, &tmp_list); + tcon_selected = true; + } +@@ -4176,14 +4178,14 @@ void smb2_reconnect_server(struct work_struct *work) + if (tcon->ipc) + cifs_put_smb_ses(tcon->ses); + else +- cifs_put_tcon(tcon); ++ cifs_put_tcon(tcon, netfs_trace_tcon_ref_put_reconnect_server); + } + + if (!ses_exist) + goto done; + + /* allocate a dummy tcon struct used for reconnect */ +- tcon = tcon_info_alloc(false); ++ tcon = tcon_info_alloc(false, netfs_trace_tcon_ref_new_reconnect_server); + if (!tcon) { + resched = true; + list_for_each_entry_safe(ses, ses2, &tmp_ses_list, rlist) { +@@ -4206,7 +4208,7 @@ void smb2_reconnect_server(struct work_struct *work) + list_del_init(&ses->rlist); + cifs_put_smb_ses(ses); + } +- tconInfoFree(tcon); ++ tconInfoFree(tcon, netfs_trace_tcon_ref_free_reconnect_server); + + done: + cifs_dbg(FYI, "Reconnecting tcons and channels finished\n"); +diff --git a/fs/smb/client/smb2transport.c b/fs/smb/client/smb2transport.c +index 5a3ca62d2f07f..8f346aafc4cf8 100644 +--- a/fs/smb/client/smb2transport.c ++++ b/fs/smb/client/smb2transport.c +@@ -189,6 +189,8 @@ smb2_find_smb_sess_tcon_unlocked(struct cifs_ses *ses, __u32 tid) + if (tcon->tid != tid) + continue; + ++tcon->tc_count; ++ trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count, ++ netfs_trace_tcon_ref_get_find_sess_tcon); + return tcon; + } + +diff --git a/fs/smb/client/trace.h b/fs/smb/client/trace.h +index 5e83cb9da9028..604e52876cd2d 100644 +--- a/fs/smb/client/trace.h ++++ b/fs/smb/client/trace.h +@@ -3,6 +3,9 @@ + * Copyright (C) 2018, Microsoft Corporation. + * + * Author(s): Steve French <stfrench@microsoft.com> ++ * ++ * Please use this 3-part article as a reference for writing new tracepoints: ++ * https://lwn.net/Articles/379903/ + */ + #undef TRACE_SYSTEM + #define TRACE_SYSTEM cifs +@@ -15,9 +18,70 @@ + #include <linux/inet.h> + + /* +- * Please use this 3-part article as a reference for writing new tracepoints: +- * https://lwn.net/Articles/379903/ ++ * Specify enums for tracing information. ++ */ ++#define smb3_tcon_ref_traces \ ++ EM(netfs_trace_tcon_ref_dec_dfs_refer, "DEC DfsRef") \ ++ EM(netfs_trace_tcon_ref_free, "FRE ") \ ++ EM(netfs_trace_tcon_ref_free_fail, "FRE Fail ") \ ++ EM(netfs_trace_tcon_ref_free_ipc, "FRE Ipc ") \ ++ EM(netfs_trace_tcon_ref_free_ipc_fail, "FRE Ipc-F ") \ ++ EM(netfs_trace_tcon_ref_free_reconnect_server, "FRE Reconn") \ ++ EM(netfs_trace_tcon_ref_get_cancelled_close, "GET Cn-Cls") \ ++ EM(netfs_trace_tcon_ref_get_dfs_refer, "GET DfsRef") \ ++ EM(netfs_trace_tcon_ref_get_find, "GET Find ") \ ++ EM(netfs_trace_tcon_ref_get_find_sess_tcon, "GET FndSes") \ ++ EM(netfs_trace_tcon_ref_get_reconnect_server, "GET Reconn") \ ++ EM(netfs_trace_tcon_ref_new, "NEW ") \ ++ EM(netfs_trace_tcon_ref_new_ipc, "NEW Ipc ") \ ++ EM(netfs_trace_tcon_ref_new_reconnect_server, "NEW Reconn") \ ++ EM(netfs_trace_tcon_ref_put_cancelled_close, "PUT Cn-Cls") \ ++ EM(netfs_trace_tcon_ref_put_cancelled_close_fid, "PUT Cn-Fid") \ ++ EM(netfs_trace_tcon_ref_put_cancelled_mid, "PUT Cn-Mid") \ ++ EM(netfs_trace_tcon_ref_put_mnt_ctx, "PUT MntCtx") \ ++ EM(netfs_trace_tcon_ref_put_reconnect_server, "PUT Reconn") \ ++ EM(netfs_trace_tcon_ref_put_tlink, "PUT Tlink ") \ ++ EM(netfs_trace_tcon_ref_see_cancelled_close, "SEE Cn-Cls") \ ++ EM(netfs_trace_tcon_ref_see_fscache_collision, "SEE FV-CO!") \ ++ EM(netfs_trace_tcon_ref_see_fscache_okay, "SEE FV-Ok ") \ ++ EM(netfs_trace_tcon_ref_see_fscache_relinq, "SEE FV-Rlq") \ ++ E_(netfs_trace_tcon_ref_see_umount, "SEE Umount") ++ ++#undef EM ++#undef E_ ++ ++/* ++ * Define those tracing enums. ++ */ ++#ifndef __SMB3_DECLARE_TRACE_ENUMS_ONCE_ONLY ++#define __SMB3_DECLARE_TRACE_ENUMS_ONCE_ONLY ++ ++#define EM(a, b) a, ++#define E_(a, b) a ++ ++enum smb3_tcon_ref_trace { smb3_tcon_ref_traces } __mode(byte); ++ ++#undef EM ++#undef E_ ++#endif ++ ++/* ++ * Export enum symbols via userspace. ++ */ ++#define EM(a, b) TRACE_DEFINE_ENUM(a); ++#define E_(a, b) TRACE_DEFINE_ENUM(a); ++ ++smb3_tcon_ref_traces; ++ ++#undef EM ++#undef E_ ++ ++/* ++ * Now redefine the EM() and E_() macros to map the enums to the strings that ++ * will be printed in the output. + */ ++#define EM(a, b) { a, b }, ++#define E_(a, b) { a, b } + + /* For logging errors in read or write */ + DECLARE_EVENT_CLASS(smb3_rw_err_class, +@@ -1125,6 +1189,30 @@ DEFINE_SMB3_CREDIT_EVENT(waitff_credits); + DEFINE_SMB3_CREDIT_EVENT(overflow_credits); + DEFINE_SMB3_CREDIT_EVENT(set_credits); + ++ ++TRACE_EVENT(smb3_tcon_ref, ++ TP_PROTO(unsigned int tcon_debug_id, int ref, ++ enum smb3_tcon_ref_trace trace), ++ TP_ARGS(tcon_debug_id, ref, trace), ++ TP_STRUCT__entry( ++ __field(unsigned int, tcon) ++ __field(int, ref) ++ __field(enum smb3_tcon_ref_trace, trace) ++ ), ++ TP_fast_assign( ++ __entry->tcon = tcon_debug_id; ++ __entry->ref = ref; ++ __entry->trace = trace; ++ ), ++ TP_printk("TC=%08x %s r=%u", ++ __entry->tcon, ++ __print_symbolic(__entry->trace, smb3_tcon_ref_traces), ++ __entry->ref) ++ ); ++ ++ ++#undef EM ++#undef E_ + #endif /* _CIFS_TRACE_H */ + + #undef TRACE_INCLUDE_PATH +-- +2.43.0 + diff --git a/queue-6.6/cifs-commands-that-are-retried-should-have-replay-fl.patch b/queue-6.6/cifs-commands-that-are-retried-should-have-replay-fl.patch new file mode 100644 index 0000000000..6c69daea17 --- /dev/null +++ b/queue-6.6/cifs-commands-that-are-retried-should-have-replay-fl.patch @@ -0,0 +1,1105 @@ +From ef559a76a5d91e3d45515b6cc8d927ba5004b0a8 Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Sun, 21 Jan 2024 03:32:47 +0000 +Subject: cifs: commands that are retried should have replay flag set + +From: Shyam Prasad N <sprasad@microsoft.com> + +[ Upstream commit 4f1fffa2376922f3d1d506e49c0fd445b023a28e ] + +MS-SMB2 states that the header flag SMB2_FLAGS_REPLAY_OPERATION +needs to be set when a command needs to be retried, so that +the server is aware that this is a replay for an operation that +appeared before. + +This can be very important, for example, for state changing +operations and opens which get retried following a reconnect; +since the client maybe unaware of the status of the previous +open. + +This is particularly important for multichannel scenario, since +disconnection of one connection does not mean that the session +is lost. The requests can be replayed on another channel. + +This change also makes use of exponential back-off before replays +and also limits the number of retries to "retrans" mount option +value. + +Also, this change does not modify the read/write codepath. + +Signed-off-by: Shyam Prasad N <sprasad@microsoft.com> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/client/cached_dir.c | 23 +++- + fs/smb/client/cifsglob.h | 5 + + fs/smb/client/smb2inode.c | 33 ++++- + fs/smb/client/smb2ops.c | 123 ++++++++++++++++-- + fs/smb/client/smb2pdu.c | 260 +++++++++++++++++++++++++++++++++---- + fs/smb/client/smb2proto.h | 5 + + 6 files changed, 404 insertions(+), 45 deletions(-) + +diff --git a/fs/smb/client/cached_dir.c b/fs/smb/client/cached_dir.c +index 567f718362c52..ca0fd25236ef4 100644 +--- a/fs/smb/client/cached_dir.c ++++ b/fs/smb/client/cached_dir.c +@@ -145,21 +145,27 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon, + struct cached_fid *cfid; + struct cached_fids *cfids; + const char *npath; ++ int retries = 0, cur_sleep = 1; + + if (tcon == NULL || tcon->cfids == NULL || tcon->nohandlecache || + is_smb1_server(tcon->ses->server) || (dir_cache_timeout == 0)) + return -EOPNOTSUPP; + + ses = tcon->ses; +- server = cifs_pick_channel(ses); + cfids = tcon->cfids; + +- if (!server->ops->new_lease_key) +- return -EIO; +- + if (cifs_sb->root == NULL) + return -ENOENT; + ++replay_again: ++ /* reinitialize for possible replay */ ++ flags = 0; ++ oplock = SMB2_OPLOCK_LEVEL_II; ++ server = cifs_pick_channel(ses); ++ ++ if (!server->ops->new_lease_key) ++ return -EIO; ++ + utf16_path = cifs_convert_path_to_utf16(path, cifs_sb); + if (!utf16_path) + return -ENOMEM; +@@ -269,6 +275,11 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon, + */ + cfid->has_lease = true; + ++ if (retries) { ++ smb2_set_replay(server, &rqst[0]); ++ smb2_set_replay(server, &rqst[1]); ++ } ++ + rc = compound_send_recv(xid, ses, server, + flags, 2, rqst, + resp_buftype, rsp_iov); +@@ -369,6 +380,10 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon, + } + kfree(utf16_path); + ++ if (is_replayable_error(rc) && ++ smb2_should_replay(tcon, &retries, &cur_sleep)) ++ goto replay_again; ++ + return rc; + } + +diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h +index 6acadb53ada79..479bf0d9ad589 100644 +--- a/fs/smb/client/cifsglob.h ++++ b/fs/smb/client/cifsglob.h +@@ -49,6 +49,11 @@ + */ + #define CIFS_DEF_ACTIMEO (1 * HZ) + ++/* ++ * max sleep time before retry to server ++ */ ++#define CIFS_MAX_SLEEP 2000 ++ + /* + * max attribute cache timeout (jiffies) - 2^30 + */ +diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c +index 1388ce5421a89..94df328a1965d 100644 +--- a/fs/smb/client/smb2inode.c ++++ b/fs/smb/client/smb2inode.c +@@ -92,6 +92,14 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, + unsigned int size[2]; + void *data[2]; + int len; ++ int retries = 0, cur_sleep = 1; ++ ++replay_again: ++ /* reinitialize for possible replay */ ++ flags = 0; ++ oplock = SMB2_OPLOCK_LEVEL_NONE; ++ num_rqst = 0; ++ server = cifs_pick_channel(ses); + + vars = kzalloc(sizeof(*vars), GFP_ATOMIC); + if (vars == NULL) +@@ -99,8 +107,6 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, + rqst = &vars->rqst[0]; + rsp_iov = &vars->rsp_iov[0]; + +- server = cifs_pick_channel(ses); +- + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + +@@ -435,15 +441,24 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, + num_rqst++; + + if (cfile) { ++ if (retries) ++ for (i = 1; i < num_rqst - 2; i++) ++ smb2_set_replay(server, &rqst[i]); ++ + rc = compound_send_recv(xid, ses, server, + flags, num_rqst - 2, + &rqst[1], &resp_buftype[1], + &rsp_iov[1]); +- } else ++ } else { ++ if (retries) ++ for (i = 0; i < num_rqst; i++) ++ smb2_set_replay(server, &rqst[i]); ++ + rc = compound_send_recv(xid, ses, server, + flags, num_rqst, + rqst, resp_buftype, + rsp_iov); ++ } + + finished: + num_rqst = 0; +@@ -604,9 +619,6 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, + } + SMB2_close_free(&rqst[num_rqst]); + +- if (cfile) +- cifsFileInfo_put(cfile); +- + num_cmds += 2; + if (out_iov && out_buftype) { + memcpy(out_iov, rsp_iov, num_cmds * sizeof(*out_iov)); +@@ -616,7 +628,16 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, + for (i = 0; i < num_cmds; i++) + free_rsp_buf(resp_buftype[i], rsp_iov[i].iov_base); + } ++ num_cmds -= 2; /* correct num_cmds as there could be a retry */ + kfree(vars); ++ ++ if (is_replayable_error(rc) && ++ smb2_should_replay(tcon, &retries, &cur_sleep)) ++ goto replay_again; ++ ++ if (cfile) ++ cifsFileInfo_put(cfile); ++ + return rc; + } + +diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c +index 3e07ab1564ea7..06735c5685bf6 100644 +--- a/fs/smb/client/smb2ops.c ++++ b/fs/smb/client/smb2ops.c +@@ -1108,7 +1108,7 @@ smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon, + { + struct smb2_compound_vars *vars; + struct cifs_ses *ses = tcon->ses; +- struct TCP_Server_Info *server = cifs_pick_channel(ses); ++ struct TCP_Server_Info *server; + struct smb_rqst *rqst; + struct kvec *rsp_iov; + __le16 *utf16_path = NULL; +@@ -1124,6 +1124,13 @@ smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon, + struct smb2_file_full_ea_info *ea = NULL; + struct smb2_query_info_rsp *rsp; + int rc, used_len = 0; ++ int retries = 0, cur_sleep = 1; ++ ++replay_again: ++ /* reinitialize for possible replay */ ++ flags = CIFS_CP_CREATE_CLOSE_OP; ++ oplock = SMB2_OPLOCK_LEVEL_NONE; ++ server = cifs_pick_channel(ses); + + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; +@@ -1244,6 +1251,12 @@ smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon, + goto sea_exit; + smb2_set_related(&rqst[2]); + ++ if (retries) { ++ smb2_set_replay(server, &rqst[0]); ++ smb2_set_replay(server, &rqst[1]); ++ smb2_set_replay(server, &rqst[2]); ++ } ++ + rc = compound_send_recv(xid, ses, server, + flags, 3, rqst, + resp_buftype, rsp_iov); +@@ -1260,6 +1273,11 @@ smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon, + kfree(vars); + out_free_path: + kfree(utf16_path); ++ ++ if (is_replayable_error(rc) && ++ smb2_should_replay(tcon, &retries, &cur_sleep)) ++ goto replay_again; ++ + return rc; + } + #endif +@@ -1485,7 +1503,7 @@ smb2_ioctl_query_info(const unsigned int xid, + struct smb_rqst *rqst; + struct kvec *rsp_iov; + struct cifs_ses *ses = tcon->ses; +- struct TCP_Server_Info *server = cifs_pick_channel(ses); ++ struct TCP_Server_Info *server; + char __user *arg = (char __user *)p; + struct smb_query_info qi; + struct smb_query_info __user *pqi; +@@ -1502,6 +1520,13 @@ smb2_ioctl_query_info(const unsigned int xid, + void *data[2]; + int create_options = is_dir ? CREATE_NOT_FILE : CREATE_NOT_DIR; + void (*free_req1_func)(struct smb_rqst *r); ++ int retries = 0, cur_sleep = 1; ++ ++replay_again: ++ /* reinitialize for possible replay */ ++ flags = CIFS_CP_CREATE_CLOSE_OP; ++ oplock = SMB2_OPLOCK_LEVEL_NONE; ++ server = cifs_pick_channel(ses); + + vars = kzalloc(sizeof(*vars), GFP_ATOMIC); + if (vars == NULL) +@@ -1642,6 +1667,12 @@ smb2_ioctl_query_info(const unsigned int xid, + goto free_req_1; + smb2_set_related(&rqst[2]); + ++ if (retries) { ++ smb2_set_replay(server, &rqst[0]); ++ smb2_set_replay(server, &rqst[1]); ++ smb2_set_replay(server, &rqst[2]); ++ } ++ + rc = compound_send_recv(xid, ses, server, + flags, 3, rqst, + resp_buftype, rsp_iov); +@@ -1702,6 +1733,11 @@ smb2_ioctl_query_info(const unsigned int xid, + kfree(buffer); + free_vars: + kfree(vars); ++ ++ if (is_replayable_error(rc) && ++ smb2_should_replay(tcon, &retries, &cur_sleep)) ++ goto replay_again; ++ + return rc; + } + +@@ -2228,8 +2264,14 @@ smb2_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_open_parms oparms; + struct smb2_query_directory_rsp *qd_rsp = NULL; + struct smb2_create_rsp *op_rsp = NULL; +- struct TCP_Server_Info *server = cifs_pick_channel(tcon->ses); +- int retry_count = 0; ++ struct TCP_Server_Info *server; ++ int retries = 0, cur_sleep = 1; ++ ++replay_again: ++ /* reinitialize for possible replay */ ++ flags = 0; ++ oplock = SMB2_OPLOCK_LEVEL_NONE; ++ server = cifs_pick_channel(tcon->ses); + + utf16_path = cifs_convert_path_to_utf16(path, cifs_sb); + if (!utf16_path) +@@ -2279,14 +2321,15 @@ smb2_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon, + + smb2_set_related(&rqst[1]); + +-again: ++ if (retries) { ++ smb2_set_replay(server, &rqst[0]); ++ smb2_set_replay(server, &rqst[1]); ++ } ++ + rc = compound_send_recv(xid, tcon->ses, server, + flags, 2, rqst, + resp_buftype, rsp_iov); + +- if (rc == -EAGAIN && retry_count++ < 10) +- goto again; +- + /* If the open failed there is nothing to do */ + op_rsp = (struct smb2_create_rsp *)rsp_iov[0].iov_base; + if (op_rsp == NULL || op_rsp->hdr.Status != STATUS_SUCCESS) { +@@ -2334,6 +2377,11 @@ smb2_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon, + SMB2_query_directory_free(&rqst[1]); + free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); + free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); ++ ++ if (is_replayable_error(rc) && ++ smb2_should_replay(tcon, &retries, &cur_sleep)) ++ goto replay_again; ++ + return rc; + } + +@@ -2460,6 +2508,22 @@ smb2_oplock_response(struct cifs_tcon *tcon, __u64 persistent_fid, + CIFS_CACHE_READ(cinode) ? 1 : 0); + } + ++void ++smb2_set_replay(struct TCP_Server_Info *server, struct smb_rqst *rqst) ++{ ++ struct smb2_hdr *shdr; ++ ++ if (server->dialect < SMB30_PROT_ID) ++ return; ++ ++ shdr = (struct smb2_hdr *)(rqst->rq_iov[0].iov_base); ++ if (shdr == NULL) { ++ cifs_dbg(FYI, "shdr NULL in smb2_set_related\n"); ++ return; ++ } ++ shdr->Flags |= SMB2_FLAGS_REPLAY_OPERATION; ++} ++ + void + smb2_set_related(struct smb_rqst *rqst) + { +@@ -2532,6 +2596,27 @@ smb2_set_next_command(struct cifs_tcon *tcon, struct smb_rqst *rqst) + shdr->NextCommand = cpu_to_le32(len); + } + ++/* ++ * helper function for exponential backoff and check if replayable ++ */ ++bool smb2_should_replay(struct cifs_tcon *tcon, ++ int *pretries, ++ int *pcur_sleep) ++{ ++ if (!pretries || !pcur_sleep) ++ return false; ++ ++ if (tcon->retry || (*pretries)++ < tcon->ses->server->retrans) { ++ msleep(*pcur_sleep); ++ (*pcur_sleep) = ((*pcur_sleep) << 1); ++ if ((*pcur_sleep) > CIFS_MAX_SLEEP) ++ (*pcur_sleep) = CIFS_MAX_SLEEP; ++ return true; ++ } ++ ++ return false; ++} ++ + /* + * Passes the query info response back to the caller on success. + * Caller need to free this with free_rsp_buf(). +@@ -2545,7 +2630,7 @@ smb2_query_info_compound(const unsigned int xid, struct cifs_tcon *tcon, + { + struct smb2_compound_vars *vars; + struct cifs_ses *ses = tcon->ses; +- struct TCP_Server_Info *server = cifs_pick_channel(ses); ++ struct TCP_Server_Info *server; + int flags = CIFS_CP_CREATE_CLOSE_OP; + struct smb_rqst *rqst; + int resp_buftype[3]; +@@ -2556,6 +2641,13 @@ smb2_query_info_compound(const unsigned int xid, struct cifs_tcon *tcon, + int rc; + __le16 *utf16_path; + struct cached_fid *cfid = NULL; ++ int retries = 0, cur_sleep = 1; ++ ++replay_again: ++ /* reinitialize for possible replay */ ++ flags = CIFS_CP_CREATE_CLOSE_OP; ++ oplock = SMB2_OPLOCK_LEVEL_NONE; ++ server = cifs_pick_channel(ses); + + if (!path) + path = ""; +@@ -2636,6 +2728,14 @@ smb2_query_info_compound(const unsigned int xid, struct cifs_tcon *tcon, + goto qic_exit; + smb2_set_related(&rqst[2]); + ++ if (retries) { ++ if (!cfid) { ++ smb2_set_replay(server, &rqst[0]); ++ smb2_set_replay(server, &rqst[2]); ++ } ++ smb2_set_replay(server, &rqst[1]); ++ } ++ + if (cfid) { + rc = compound_send_recv(xid, ses, server, + flags, 1, &rqst[1], +@@ -2668,6 +2768,11 @@ smb2_query_info_compound(const unsigned int xid, struct cifs_tcon *tcon, + kfree(vars); + out_free_path: + kfree(utf16_path); ++ ++ if (is_replayable_error(rc) && ++ smb2_should_replay(tcon, &retries, &cur_sleep)) ++ goto replay_again; ++ + return rc; + } + +diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c +index 95b5b4bdb4b7f..6a5d478b3cef6 100644 +--- a/fs/smb/client/smb2pdu.c ++++ b/fs/smb/client/smb2pdu.c +@@ -2795,7 +2795,14 @@ int smb311_posix_mkdir(const unsigned int xid, struct inode *inode, + int flags = 0; + unsigned int total_len; + __le16 *utf16_path = NULL; +- struct TCP_Server_Info *server = cifs_pick_channel(ses); ++ struct TCP_Server_Info *server; ++ int retries = 0, cur_sleep = 1; ++ ++replay_again: ++ /* reinitialize for possible replay */ ++ flags = 0; ++ n_iov = 2; ++ server = cifs_pick_channel(ses); + + cifs_dbg(FYI, "mkdir\n"); + +@@ -2899,6 +2906,10 @@ int smb311_posix_mkdir(const unsigned int xid, struct inode *inode, + /* no need to inc num_remote_opens because we close it just below */ + trace_smb3_posix_mkdir_enter(xid, tcon->tid, ses->Suid, full_path, CREATE_NOT_FILE, + FILE_WRITE_ATTRIBUTES); ++ ++ if (retries) ++ smb2_set_replay(server, &rqst); ++ + /* resource #4: response buffer */ + rc = cifs_send_recv(xid, ses, server, + &rqst, &resp_buftype, flags, &rsp_iov); +@@ -2936,6 +2947,11 @@ int smb311_posix_mkdir(const unsigned int xid, struct inode *inode, + cifs_small_buf_release(req); + err_free_path: + kfree(utf16_path); ++ ++ if (is_replayable_error(rc) && ++ smb2_should_replay(tcon, &retries, &cur_sleep)) ++ goto replay_again; ++ + return rc; + } + +@@ -3131,12 +3147,18 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path, + struct smb2_create_rsp *rsp = NULL; + struct cifs_tcon *tcon = oparms->tcon; + struct cifs_ses *ses = tcon->ses; +- struct TCP_Server_Info *server = cifs_pick_channel(ses); ++ struct TCP_Server_Info *server; + struct kvec iov[SMB2_CREATE_IOV_SIZE]; + struct kvec rsp_iov = {NULL, 0}; + int resp_buftype = CIFS_NO_BUFFER; + int rc = 0; + int flags = 0; ++ int retries = 0, cur_sleep = 1; ++ ++replay_again: ++ /* reinitialize for possible replay */ ++ flags = 0; ++ server = cifs_pick_channel(ses); + + cifs_dbg(FYI, "create/open\n"); + if (!ses || !server) +@@ -3158,6 +3180,9 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path, + trace_smb3_open_enter(xid, tcon->tid, tcon->ses->Suid, oparms->path, + oparms->create_options, oparms->desired_access); + ++ if (retries) ++ smb2_set_replay(server, &rqst); ++ + rc = cifs_send_recv(xid, ses, server, + &rqst, &resp_buftype, flags, + &rsp_iov); +@@ -3211,6 +3236,11 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path, + creat_exit: + SMB2_open_free(&rqst); + free_rsp_buf(resp_buftype, rsp); ++ ++ if (is_replayable_error(rc) && ++ smb2_should_replay(tcon, &retries, &cur_sleep)) ++ goto replay_again; ++ + return rc; + } + +@@ -3335,15 +3365,7 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, + int resp_buftype = CIFS_NO_BUFFER; + int rc = 0; + int flags = 0; +- +- cifs_dbg(FYI, "SMB2 IOCTL\n"); +- +- if (out_data != NULL) +- *out_data = NULL; +- +- /* zero out returned data len, in case of error */ +- if (plen) +- *plen = 0; ++ int retries = 0, cur_sleep = 1; + + if (!tcon) + return -EIO; +@@ -3352,10 +3374,23 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, + if (!ses) + return -EIO; + ++replay_again: ++ /* reinitialize for possible replay */ ++ flags = 0; + server = cifs_pick_channel(ses); ++ + if (!server) + return -EIO; + ++ cifs_dbg(FYI, "SMB2 IOCTL\n"); ++ ++ if (out_data != NULL) ++ *out_data = NULL; ++ ++ /* zero out returned data len, in case of error */ ++ if (plen) ++ *plen = 0; ++ + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + +@@ -3370,6 +3405,9 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, + if (rc) + goto ioctl_exit; + ++ if (retries) ++ smb2_set_replay(server, &rqst); ++ + rc = cifs_send_recv(xid, ses, server, + &rqst, &resp_buftype, flags, + &rsp_iov); +@@ -3439,6 +3477,11 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, + ioctl_exit: + SMB2_ioctl_free(&rqst); + free_rsp_buf(resp_buftype, rsp); ++ ++ if (is_replayable_error(rc) && ++ smb2_should_replay(tcon, &retries, &cur_sleep)) ++ goto replay_again; ++ + return rc; + } + +@@ -3510,13 +3553,20 @@ __SMB2_close(const unsigned int xid, struct cifs_tcon *tcon, + struct smb_rqst rqst; + struct smb2_close_rsp *rsp = NULL; + struct cifs_ses *ses = tcon->ses; +- struct TCP_Server_Info *server = cifs_pick_channel(ses); ++ struct TCP_Server_Info *server; + struct kvec iov[1]; + struct kvec rsp_iov; + int resp_buftype = CIFS_NO_BUFFER; + int rc = 0; + int flags = 0; + bool query_attrs = false; ++ int retries = 0, cur_sleep = 1; ++ ++replay_again: ++ /* reinitialize for possible replay */ ++ flags = 0; ++ query_attrs = false; ++ server = cifs_pick_channel(ses); + + cifs_dbg(FYI, "Close\n"); + +@@ -3542,6 +3592,9 @@ __SMB2_close(const unsigned int xid, struct cifs_tcon *tcon, + if (rc) + goto close_exit; + ++ if (retries) ++ smb2_set_replay(server, &rqst); ++ + rc = cifs_send_recv(xid, ses, server, + &rqst, &resp_buftype, flags, &rsp_iov); + rsp = (struct smb2_close_rsp *)rsp_iov.iov_base; +@@ -3575,6 +3628,11 @@ __SMB2_close(const unsigned int xid, struct cifs_tcon *tcon, + cifs_dbg(VFS, "handle cancelled close fid 0x%llx returned error %d\n", + persistent_fid, tmp_rc); + } ++ ++ if (is_replayable_error(rc) && ++ smb2_should_replay(tcon, &retries, &cur_sleep)) ++ goto replay_again; ++ + return rc; + } + +@@ -3705,12 +3763,19 @@ query_info(const unsigned int xid, struct cifs_tcon *tcon, + struct TCP_Server_Info *server; + int flags = 0; + bool allocated = false; ++ int retries = 0, cur_sleep = 1; + + cifs_dbg(FYI, "Query Info\n"); + + if (!ses) + return -EIO; ++ ++replay_again: ++ /* reinitialize for possible replay */ ++ flags = 0; ++ allocated = false; + server = cifs_pick_channel(ses); ++ + if (!server) + return -EIO; + +@@ -3732,6 +3797,9 @@ query_info(const unsigned int xid, struct cifs_tcon *tcon, + trace_smb3_query_info_enter(xid, persistent_fid, tcon->tid, + ses->Suid, info_class, (__u32)info_type); + ++ if (retries) ++ smb2_set_replay(server, &rqst); ++ + rc = cifs_send_recv(xid, ses, server, + &rqst, &resp_buftype, flags, &rsp_iov); + rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base; +@@ -3774,6 +3842,11 @@ query_info(const unsigned int xid, struct cifs_tcon *tcon, + qinf_exit: + SMB2_query_info_free(&rqst); + free_rsp_buf(resp_buftype, rsp); ++ ++ if (is_replayable_error(rc) && ++ smb2_should_replay(tcon, &retries, &cur_sleep)) ++ goto replay_again; ++ + return rc; + } + +@@ -3874,7 +3947,7 @@ SMB2_change_notify(const unsigned int xid, struct cifs_tcon *tcon, + u32 *plen /* returned data len */) + { + struct cifs_ses *ses = tcon->ses; +- struct TCP_Server_Info *server = cifs_pick_channel(ses); ++ struct TCP_Server_Info *server; + struct smb_rqst rqst; + struct smb2_change_notify_rsp *smb_rsp; + struct kvec iov[1]; +@@ -3882,6 +3955,12 @@ SMB2_change_notify(const unsigned int xid, struct cifs_tcon *tcon, + int resp_buftype = CIFS_NO_BUFFER; + int flags = 0; + int rc = 0; ++ int retries = 0, cur_sleep = 1; ++ ++replay_again: ++ /* reinitialize for possible replay */ ++ flags = 0; ++ server = cifs_pick_channel(ses); + + cifs_dbg(FYI, "change notify\n"); + if (!ses || !server) +@@ -3906,6 +3985,10 @@ SMB2_change_notify(const unsigned int xid, struct cifs_tcon *tcon, + + trace_smb3_notify_enter(xid, persistent_fid, tcon->tid, ses->Suid, + (u8)watch_tree, completion_filter); ++ ++ if (retries) ++ smb2_set_replay(server, &rqst); ++ + rc = cifs_send_recv(xid, ses, server, + &rqst, &resp_buftype, flags, &rsp_iov); + +@@ -3940,6 +4023,11 @@ SMB2_change_notify(const unsigned int xid, struct cifs_tcon *tcon, + if (rqst.rq_iov) + cifs_small_buf_release(rqst.rq_iov[0].iov_base); /* request */ + free_rsp_buf(resp_buftype, rsp_iov.iov_base); ++ ++ if (is_replayable_error(rc) && ++ smb2_should_replay(tcon, &retries, &cur_sleep)) ++ goto replay_again; ++ + return rc; + } + +@@ -4182,10 +4270,16 @@ SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, + struct smb_rqst rqst; + struct kvec iov[1]; + struct kvec rsp_iov = {NULL, 0}; +- struct TCP_Server_Info *server = cifs_pick_channel(ses); ++ struct TCP_Server_Info *server; + int resp_buftype = CIFS_NO_BUFFER; + int flags = 0; + int rc = 0; ++ int retries = 0, cur_sleep = 1; ++ ++replay_again: ++ /* reinitialize for possible replay */ ++ flags = 0; ++ server = cifs_pick_channel(ses); + + cifs_dbg(FYI, "flush\n"); + if (!ses || !(ses->server)) +@@ -4205,6 +4299,10 @@ SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, + goto flush_exit; + + trace_smb3_flush_enter(xid, persistent_fid, tcon->tid, ses->Suid); ++ ++ if (retries) ++ smb2_set_replay(server, &rqst); ++ + rc = cifs_send_recv(xid, ses, server, + &rqst, &resp_buftype, flags, &rsp_iov); + +@@ -4219,6 +4317,11 @@ SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, + flush_exit: + SMB2_flush_free(&rqst); + free_rsp_buf(resp_buftype, rsp_iov.iov_base); ++ ++ if (is_replayable_error(rc) && ++ smb2_should_replay(tcon, &retries, &cur_sleep)) ++ goto replay_again; ++ + return rc; + } + +@@ -4856,18 +4959,21 @@ SMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms, + int flags = 0; + unsigned int total_len; + struct TCP_Server_Info *server; ++ int retries = 0, cur_sleep = 1; + ++replay_again: ++ /* reinitialize for possible replay */ ++ flags = 0; + *nbytes = 0; +- +- if (n_vec < 1) +- return rc; +- + if (!io_parms->server) + io_parms->server = cifs_pick_channel(io_parms->tcon->ses); + server = io_parms->server; + if (server == NULL) + return -ECONNABORTED; + ++ if (n_vec < 1) ++ return rc; ++ + rc = smb2_plain_req_init(SMB2_WRITE, io_parms->tcon, server, + (void **) &req, &total_len); + if (rc) +@@ -4901,6 +5007,9 @@ SMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms, + rqst.rq_iov = iov; + rqst.rq_nvec = n_vec + 1; + ++ if (retries) ++ smb2_set_replay(server, &rqst); ++ + rc = cifs_send_recv(xid, io_parms->tcon->ses, server, + &rqst, + &resp_buftype, flags, &rsp_iov); +@@ -4925,6 +5034,11 @@ SMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms, + + cifs_small_buf_release(req); + free_rsp_buf(resp_buftype, rsp); ++ ++ if (is_replayable_error(rc) && ++ smb2_should_replay(io_parms->tcon, &retries, &cur_sleep)) ++ goto replay_again; ++ + return rc; + } + +@@ -5242,8 +5356,14 @@ SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon, + struct kvec rsp_iov; + int rc = 0; + struct cifs_ses *ses = tcon->ses; +- struct TCP_Server_Info *server = cifs_pick_channel(ses); ++ struct TCP_Server_Info *server; + int flags = 0; ++ int retries = 0, cur_sleep = 1; ++ ++replay_again: ++ /* reinitialize for possible replay */ ++ flags = 0; ++ server = cifs_pick_channel(ses); + + if (!ses || !(ses->server)) + return -EIO; +@@ -5263,6 +5383,9 @@ SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon, + if (rc) + goto qdir_exit; + ++ if (retries) ++ smb2_set_replay(server, &rqst); ++ + rc = cifs_send_recv(xid, ses, server, + &rqst, &resp_buftype, flags, &rsp_iov); + rsp = (struct smb2_query_directory_rsp *)rsp_iov.iov_base; +@@ -5297,6 +5420,11 @@ SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon, + qdir_exit: + SMB2_query_directory_free(&rqst); + free_rsp_buf(resp_buftype, rsp); ++ ++ if (is_replayable_error(rc) && ++ smb2_should_replay(tcon, &retries, &cur_sleep)) ++ goto replay_again; ++ + return rc; + } + +@@ -5363,8 +5491,14 @@ send_set_info(const unsigned int xid, struct cifs_tcon *tcon, + int rc = 0; + int resp_buftype; + struct cifs_ses *ses = tcon->ses; +- struct TCP_Server_Info *server = cifs_pick_channel(ses); ++ struct TCP_Server_Info *server; + int flags = 0; ++ int retries = 0, cur_sleep = 1; ++ ++replay_again: ++ /* reinitialize for possible replay */ ++ flags = 0; ++ server = cifs_pick_channel(ses); + + if (!ses || !server) + return -EIO; +@@ -5392,6 +5526,8 @@ send_set_info(const unsigned int xid, struct cifs_tcon *tcon, + return rc; + } + ++ if (retries) ++ smb2_set_replay(server, &rqst); + + rc = cifs_send_recv(xid, ses, server, + &rqst, &resp_buftype, flags, +@@ -5407,6 +5543,11 @@ send_set_info(const unsigned int xid, struct cifs_tcon *tcon, + + free_rsp_buf(resp_buftype, rsp); + kfree(iov); ++ ++ if (is_replayable_error(rc) && ++ smb2_should_replay(tcon, &retries, &cur_sleep)) ++ goto replay_again; ++ + return rc; + } + +@@ -5459,12 +5600,18 @@ SMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon, + int rc; + struct smb2_oplock_break *req = NULL; + struct cifs_ses *ses = tcon->ses; +- struct TCP_Server_Info *server = cifs_pick_channel(ses); ++ struct TCP_Server_Info *server; + int flags = CIFS_OBREAK_OP; + unsigned int total_len; + struct kvec iov[1]; + struct kvec rsp_iov; + int resp_buf_type; ++ int retries = 0, cur_sleep = 1; ++ ++replay_again: ++ /* reinitialize for possible replay */ ++ flags = CIFS_OBREAK_OP; ++ server = cifs_pick_channel(ses); + + cifs_dbg(FYI, "SMB2_oplock_break\n"); + rc = smb2_plain_req_init(SMB2_OPLOCK_BREAK, tcon, server, +@@ -5489,15 +5636,21 @@ SMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon, + rqst.rq_iov = iov; + rqst.rq_nvec = 1; + ++ if (retries) ++ smb2_set_replay(server, &rqst); ++ + rc = cifs_send_recv(xid, ses, server, + &rqst, &resp_buf_type, flags, &rsp_iov); + cifs_small_buf_release(req); +- + if (rc) { + cifs_stats_fail_inc(tcon, SMB2_OPLOCK_BREAK_HE); + cifs_dbg(FYI, "Send error in Oplock Break = %d\n", rc); + } + ++ if (is_replayable_error(rc) && ++ smb2_should_replay(tcon, &retries, &cur_sleep)) ++ goto replay_again; ++ + return rc; + } + +@@ -5583,9 +5736,15 @@ SMB311_posix_qfs_info(const unsigned int xid, struct cifs_tcon *tcon, + int rc = 0; + int resp_buftype; + struct cifs_ses *ses = tcon->ses; +- struct TCP_Server_Info *server = cifs_pick_channel(ses); ++ struct TCP_Server_Info *server; + FILE_SYSTEM_POSIX_INFO *info = NULL; + int flags = 0; ++ int retries = 0, cur_sleep = 1; ++ ++replay_again: ++ /* reinitialize for possible replay */ ++ flags = 0; ++ server = cifs_pick_channel(ses); + + rc = build_qfs_info_req(&iov, tcon, server, + FS_POSIX_INFORMATION, +@@ -5601,6 +5760,9 @@ SMB311_posix_qfs_info(const unsigned int xid, struct cifs_tcon *tcon, + rqst.rq_iov = &iov; + rqst.rq_nvec = 1; + ++ if (retries) ++ smb2_set_replay(server, &rqst); ++ + rc = cifs_send_recv(xid, ses, server, + &rqst, &resp_buftype, flags, &rsp_iov); + free_qfs_info_req(&iov); +@@ -5620,6 +5782,11 @@ SMB311_posix_qfs_info(const unsigned int xid, struct cifs_tcon *tcon, + + posix_qfsinf_exit: + free_rsp_buf(resp_buftype, rsp_iov.iov_base); ++ ++ if (is_replayable_error(rc) && ++ smb2_should_replay(tcon, &retries, &cur_sleep)) ++ goto replay_again; ++ + return rc; + } + +@@ -5634,9 +5801,15 @@ SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon, + int rc = 0; + int resp_buftype; + struct cifs_ses *ses = tcon->ses; +- struct TCP_Server_Info *server = cifs_pick_channel(ses); ++ struct TCP_Server_Info *server; + struct smb2_fs_full_size_info *info = NULL; + int flags = 0; ++ int retries = 0, cur_sleep = 1; ++ ++replay_again: ++ /* reinitialize for possible replay */ ++ flags = 0; ++ server = cifs_pick_channel(ses); + + rc = build_qfs_info_req(&iov, tcon, server, + FS_FULL_SIZE_INFORMATION, +@@ -5652,6 +5825,9 @@ SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon, + rqst.rq_iov = &iov; + rqst.rq_nvec = 1; + ++ if (retries) ++ smb2_set_replay(server, &rqst); ++ + rc = cifs_send_recv(xid, ses, server, + &rqst, &resp_buftype, flags, &rsp_iov); + free_qfs_info_req(&iov); +@@ -5671,6 +5847,11 @@ SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon, + + qfsinf_exit: + free_rsp_buf(resp_buftype, rsp_iov.iov_base); ++ ++ if (is_replayable_error(rc) && ++ smb2_should_replay(tcon, &retries, &cur_sleep)) ++ goto replay_again; ++ + return rc; + } + +@@ -5685,9 +5866,15 @@ SMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon, + int rc = 0; + int resp_buftype, max_len, min_len; + struct cifs_ses *ses = tcon->ses; +- struct TCP_Server_Info *server = cifs_pick_channel(ses); ++ struct TCP_Server_Info *server; + unsigned int rsp_len, offset; + int flags = 0; ++ int retries = 0, cur_sleep = 1; ++ ++replay_again: ++ /* reinitialize for possible replay */ ++ flags = 0; ++ server = cifs_pick_channel(ses); + + if (level == FS_DEVICE_INFORMATION) { + max_len = sizeof(FILE_SYSTEM_DEVICE_INFO); +@@ -5719,6 +5906,9 @@ SMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon, + rqst.rq_iov = &iov; + rqst.rq_nvec = 1; + ++ if (retries) ++ smb2_set_replay(server, &rqst); ++ + rc = cifs_send_recv(xid, ses, server, + &rqst, &resp_buftype, flags, &rsp_iov); + free_qfs_info_req(&iov); +@@ -5756,6 +5946,11 @@ SMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon, + + qfsattr_exit: + free_rsp_buf(resp_buftype, rsp_iov.iov_base); ++ ++ if (is_replayable_error(rc) && ++ smb2_should_replay(tcon, &retries, &cur_sleep)) ++ goto replay_again; ++ + return rc; + } + +@@ -5773,7 +5968,13 @@ smb2_lockv(const unsigned int xid, struct cifs_tcon *tcon, + unsigned int count; + int flags = CIFS_NO_RSP_BUF; + unsigned int total_len; +- struct TCP_Server_Info *server = cifs_pick_channel(tcon->ses); ++ struct TCP_Server_Info *server; ++ int retries = 0, cur_sleep = 1; ++ ++replay_again: ++ /* reinitialize for possible replay */ ++ flags = CIFS_NO_RSP_BUF; ++ server = cifs_pick_channel(tcon->ses); + + cifs_dbg(FYI, "smb2_lockv num lock %d\n", num_lock); + +@@ -5804,6 +6005,9 @@ smb2_lockv(const unsigned int xid, struct cifs_tcon *tcon, + rqst.rq_iov = iov; + rqst.rq_nvec = 2; + ++ if (retries) ++ smb2_set_replay(server, &rqst); ++ + rc = cifs_send_recv(xid, tcon->ses, server, + &rqst, &resp_buf_type, flags, + &rsp_iov); +@@ -5815,6 +6019,10 @@ smb2_lockv(const unsigned int xid, struct cifs_tcon *tcon, + tcon->ses->Suid, rc); + } + ++ if (is_replayable_error(rc) && ++ smb2_should_replay(tcon, &retries, &cur_sleep)) ++ goto replay_again; ++ + return rc; + } + +diff --git a/fs/smb/client/smb2proto.h b/fs/smb/client/smb2proto.h +index 343ada691e763..330e36c6b91f0 100644 +--- a/fs/smb/client/smb2proto.h ++++ b/fs/smb/client/smb2proto.h +@@ -122,6 +122,11 @@ extern unsigned long smb_rqst_len(struct TCP_Server_Info *server, + extern void smb2_set_next_command(struct cifs_tcon *tcon, + struct smb_rqst *rqst); + extern void smb2_set_related(struct smb_rqst *rqst); ++extern void smb2_set_replay(struct TCP_Server_Info *server, ++ struct smb_rqst *rqst); ++extern bool smb2_should_replay(struct cifs_tcon *tcon, ++ int *pretries, ++ int *pcur_sleep); + + /* + * SMB2 Worker functions - most of protocol specific implementation details +-- +2.43.0 + diff --git a/queue-6.6/cifs-defer-close-file-handles-having-rh-lease.patch b/queue-6.6/cifs-defer-close-file-handles-having-rh-lease.patch new file mode 100644 index 0000000000..dc4ccf755d --- /dev/null +++ b/queue-6.6/cifs-defer-close-file-handles-having-rh-lease.patch @@ -0,0 +1,60 @@ +From 50097cc1be7e31fc78529d359f31ed04fde87957 Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Tue, 12 Mar 2024 21:21:41 -0500 +Subject: cifs: defer close file handles having RH lease + +From: Bharath SM <bharathsm@microsoft.com> + +[ Upstream commit dc528770edb138e26a533f8a77de5c4db18ea7f3 ] + +Previously we only deferred closing file handles with RHW +lease. To enhance performance benefits from deferred closes, +we now include handles with RH leases as well. + +Signed-off-by: Bharath SM <bharathsm@microsoft.com> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/client/file.c | 19 +++++++++++++++---- + 1 file changed, 15 insertions(+), 4 deletions(-) + +diff --git a/fs/smb/client/file.c b/fs/smb/client/file.c +index 8eaf195ef5604..6dc2e8db9c8ed 100644 +--- a/fs/smb/client/file.c ++++ b/fs/smb/client/file.c +@@ -1152,6 +1152,19 @@ void smb2_deferred_work_close(struct work_struct *work) + _cifsFileInfo_put(cfile, true, false); + } + ++static bool ++smb2_can_defer_close(struct inode *inode, struct cifs_deferred_close *dclose) ++{ ++ struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); ++ struct cifsInodeInfo *cinode = CIFS_I(inode); ++ ++ return (cifs_sb->ctx->closetimeo && cinode->lease_granted && dclose && ++ (cinode->oplock == CIFS_CACHE_RHW_FLG || ++ cinode->oplock == CIFS_CACHE_RH_FLG) && ++ !test_bit(CIFS_INO_CLOSE_ON_LOCK, &cinode->flags)); ++ ++} ++ + int cifs_close(struct inode *inode, struct file *file) + { + struct cifsFileInfo *cfile; +@@ -1165,10 +1178,8 @@ int cifs_close(struct inode *inode, struct file *file) + cfile = file->private_data; + file->private_data = NULL; + dclose = kmalloc(sizeof(struct cifs_deferred_close), GFP_KERNEL); +- if ((cifs_sb->ctx->closetimeo && cinode->oplock == CIFS_CACHE_RHW_FLG) +- && cinode->lease_granted && +- !test_bit(CIFS_INO_CLOSE_ON_LOCK, &cinode->flags) && +- dclose && !(cfile->status_file_deleted)) { ++ if ((cfile->status_file_deleted == false) && ++ (smb2_can_defer_close(inode, dclose))) { + if (test_and_clear_bit(CIFS_INO_MODIFIED_ATTR, &cinode->flags)) { + inode_set_mtime_to_ts(inode, + inode_set_ctime_current(inode)); +-- +2.43.0 + diff --git a/queue-6.6/cifs-fix-in-logging-in-cifs_chan_update_iface.patch b/queue-6.6/cifs-fix-in-logging-in-cifs_chan_update_iface.patch new file mode 100644 index 0000000000..4196638d42 --- /dev/null +++ b/queue-6.6/cifs-fix-in-logging-in-cifs_chan_update_iface.patch @@ -0,0 +1,59 @@ +From cfa7dbf28370fb91b117b58ff6ed1578ef84f9e2 Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Sat, 27 Apr 2024 20:05:11 -0500 +Subject: cifs: fix in logging in cifs_chan_update_iface + +From: Steve French <stfrench@microsoft.com> + +[ Upstream commit 516eea97f92f1e7271f20835cfe9e73774b0f8cc ] + +Recently, cifs_chan_update_iface was modified to not +remove an iface if a suitable replacement was not found. +With that, there were two conditionals that were exactly +the same. This change removes that extra condition check. + +Also, fixed a logging in the same function to indicate +the correct message. + +Signed-off-by: Shyam Prasad N <sprasad@microsoft.com> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/client/sess.c | 14 ++++++++------ + 1 file changed, 8 insertions(+), 6 deletions(-) + +diff --git a/fs/smb/client/sess.c b/fs/smb/client/sess.c +index f3d25395d0d3c..5de32640f0265 100644 +--- a/fs/smb/client/sess.c ++++ b/fs/smb/client/sess.c +@@ -442,8 +442,14 @@ cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server) + } + + if (!iface) { +- cifs_dbg(FYI, "unable to get the interface matching: %pIS\n", +- &ss); ++ if (!chan_index) ++ cifs_dbg(FYI, "unable to get the interface matching: %pIS\n", ++ &ss); ++ else { ++ cifs_dbg(FYI, "unable to find another interface to replace: %pIS\n", ++ &old_iface->sockaddr); ++ } ++ + spin_unlock(&ses->iface_lock); + return; + } +@@ -461,10 +467,6 @@ cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server) + iface->weight_fulfilled++; + + kref_put(&old_iface->refcount, release_iface); +- } else if (old_iface) { +- /* if a new candidate is not found, keep things as is */ +- cifs_dbg(FYI, "could not replace iface: %pIS\n", +- &old_iface->sockaddr); + } else if (!chan_index) { + /* special case: update interface for primary channel */ + cifs_dbg(FYI, "referencing primary channel iface: %pIS\n", +-- +2.43.0 + diff --git a/queue-6.6/cifs-fix-use-after-free-for-iface-while-disabling-se.patch b/queue-6.6/cifs-fix-use-after-free-for-iface-while-disabling-se.patch new file mode 100644 index 0000000000..4c4cb5de5c --- /dev/null +++ b/queue-6.6/cifs-fix-use-after-free-for-iface-while-disabling-se.patch @@ -0,0 +1,41 @@ +From b898e3355dbd14d9a30fd87f0c85ae0bba789dca Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Tue, 21 Nov 2023 19:13:47 +0530 +Subject: cifs: fix use after free for iface while disabling secondary channels + +From: Ritvik Budhiraja <rbudhiraja@microsoft.com> + +[ Upstream commit a15ccef82d3de9a37dc25898c60a394209368dc8 ] + +We were deferencing iface after it has been released. Fix is to +release after all dereference instances have been encountered. + +Signed-off-by: Ritvik Budhiraja <rbudhiraja@microsoft.com> +Reported-by: kernel test robot <lkp@intel.com> +Reported-by: Dan Carpenter <error27@gmail.com> +Closes: https://lore.kernel.org/r/202311110815.UJaeU3Tt-lkp@intel.com/ +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/client/sess.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/fs/smb/client/sess.c b/fs/smb/client/sess.c +index 09bb30610a901..f3d25395d0d3c 100644 +--- a/fs/smb/client/sess.c ++++ b/fs/smb/client/sess.c +@@ -337,10 +337,10 @@ cifs_disable_secondary_channels(struct cifs_ses *ses) + + if (iface) { + spin_lock(&ses->iface_lock); +- kref_put(&iface->refcount, release_iface); + iface->num_channels--; + if (iface->weight_fulfilled) + iface->weight_fulfilled--; ++ kref_put(&iface->refcount, release_iface); + spin_unlock(&ses->iface_lock); + } + +-- +2.43.0 + diff --git a/queue-6.6/cifs-fixes-for-get_inode_info.patch b/queue-6.6/cifs-fixes-for-get_inode_info.patch new file mode 100644 index 0000000000..96c07677c0 --- /dev/null +++ b/queue-6.6/cifs-fixes-for-get_inode_info.patch @@ -0,0 +1,118 @@ +From 5cda8d3e89ed5f9f2bc9fbe3212f52924e5add87 Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Thu, 14 Mar 2024 08:05:49 -0400 +Subject: cifs: fixes for get_inode_info + +From: Meetakshi Setiya <msetiya@microsoft.com> + +[ Upstream commit fc20c523211a38b87fc850a959cb2149e4fd64b0 ] + +Fix potential memory leaks, add error checking, remove unnecessary +initialisation of status_file_deleted and do not use cifs_iget() to get +inode in reparse_info_to_fattr since fattrs may not be fully set. + +Fixes: ffceb7640cbf ("smb: client: do not defer close open handles to deleted files") +Reported-by: Paulo Alcantara <pc@manguebit.com> +Signed-off-by: Meetakshi Setiya <msetiya@microsoft.com> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/client/file.c | 1 - + fs/smb/client/inode.c | 24 +++++++++++++----------- + 2 files changed, 13 insertions(+), 12 deletions(-) + +diff --git a/fs/smb/client/file.c b/fs/smb/client/file.c +index 6dc2e8db9c8ed..7ea8c3cf70f6c 100644 +--- a/fs/smb/client/file.c ++++ b/fs/smb/client/file.c +@@ -501,7 +501,6 @@ struct cifsFileInfo *cifs_new_fileinfo(struct cifs_fid *fid, struct file *file, + cfile->uid = current_fsuid(); + cfile->dentry = dget(dentry); + cfile->f_flags = file->f_flags; +- cfile->status_file_deleted = false; + cfile->invalidHandle = false; + cfile->deferred_close_scheduled = false; + cfile->tlink = cifs_get_tlink(tlink); +diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c +index 67bc1a1e54fde..67ad8eeaa7665 100644 +--- a/fs/smb/client/inode.c ++++ b/fs/smb/client/inode.c +@@ -820,8 +820,10 @@ cifs_get_file_info(struct file *filp) + void *page = alloc_dentry_path(); + const unsigned char *path; + +- if (!server->ops->query_file_info) ++ if (!server->ops->query_file_info) { ++ free_dentry_path(page); + return -ENOSYS; ++ } + + xid = get_xid(); + rc = server->ops->query_file_info(xid, tcon, cfile, &data); +@@ -835,8 +837,8 @@ cifs_get_file_info(struct file *filp) + } + path = build_path_from_dentry(dentry, page); + if (IS_ERR(path)) { +- free_dentry_path(page); +- return PTR_ERR(path); ++ rc = PTR_ERR(path); ++ goto cgfi_exit; + } + cifs_open_info_to_fattr(&fattr, &data, inode->i_sb); + if (fattr.cf_flags & CIFS_FATTR_DELETE_PENDING) +@@ -1009,7 +1011,6 @@ static int reparse_info_to_fattr(struct cifs_open_info_data *data, + struct kvec rsp_iov, *iov = NULL; + int rsp_buftype = CIFS_NO_BUFFER; + u32 tag = data->reparse.tag; +- struct inode *inode = NULL; + int rc = 0; + + if (!tag && server->ops->query_reparse_point) { +@@ -1049,12 +1050,8 @@ static int reparse_info_to_fattr(struct cifs_open_info_data *data, + + if (tcon->posix_extensions) + smb311_posix_info_to_fattr(fattr, data, sb); +- else { ++ else + cifs_open_info_to_fattr(fattr, data, sb); +- inode = cifs_iget(sb, fattr); +- if (inode && fattr->cf_flags & CIFS_FATTR_DELETE_PENDING) +- cifs_mark_open_handles_for_deleted_file(inode, full_path); +- } + out: + fattr->cf_cifstag = data->reparse.tag; + free_rsp_buf(rsp_buftype, rsp_iov.iov_base); +@@ -1109,9 +1106,9 @@ static int cifs_get_fattr(struct cifs_open_info_data *data, + full_path, fattr); + } else { + cifs_open_info_to_fattr(fattr, data, sb); +- if (fattr->cf_flags & CIFS_FATTR_DELETE_PENDING) +- cifs_mark_open_handles_for_deleted_file(*inode, full_path); + } ++ if (!rc && fattr->cf_flags & CIFS_FATTR_DELETE_PENDING) ++ cifs_mark_open_handles_for_deleted_file(*inode, full_path); + break; + case -EREMOTE: + /* DFS link, no metadata available on this server */ +@@ -1340,6 +1337,8 @@ int smb311_posix_get_inode_info(struct inode **inode, + goto out; + + rc = update_inode_info(sb, &fattr, inode); ++ if (!rc && fattr.cf_flags & CIFS_FATTR_DELETE_PENDING) ++ cifs_mark_open_handles_for_deleted_file(*inode, full_path); + out: + kfree(fattr.cf_symlink_target); + return rc; +@@ -1503,6 +1502,9 @@ struct inode *cifs_root_iget(struct super_block *sb) + goto out; + } + ++ if (!rc && fattr.cf_flags & CIFS_FATTR_DELETE_PENDING) ++ cifs_mark_open_handles_for_deleted_file(inode, path); ++ + if (rc && tcon->pipe) { + cifs_dbg(FYI, "ipc connection - fake read inode\n"); + spin_lock(&inode->i_lock); +-- +2.43.0 + diff --git a/queue-6.6/cifs-get-rid-of-dup-length-check-in-parse_reparse_po.patch b/queue-6.6/cifs-get-rid-of-dup-length-check-in-parse_reparse_po.patch new file mode 100644 index 0000000000..c80659bec9 --- /dev/null +++ b/queue-6.6/cifs-get-rid-of-dup-length-check-in-parse_reparse_po.patch @@ -0,0 +1,86 @@ +From 5aeca997d903af8749065cb291a4ae314dc095a5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Sat, 6 Jan 2024 20:05:18 -0300 +Subject: cifs: get rid of dup length check in parse_reparse_point() + +From: Paulo Alcantara <pc@manguebit.com> + +[ Upstream commit 8a3c4e44c243308c2364a00f9944c3d6fbdeb125 ] + +smb2_compound_op(SMB2_OP_GET_REPARSE) already checks if ioctl response +has a valid reparse data buffer's length, so there's no need to check +it again in parse_reparse_point(). + +In order to get rid of duplicate check, validate reparse data buffer's +length also in cifs_query_reparse_point(). + +Signed-off-by: Paulo Alcantara (SUSE) <pc@manguebit.com> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/client/cifssmb.c | 14 ++++++++++++-- + fs/smb/client/smb2ops.c | 12 ------------ + 2 files changed, 12 insertions(+), 14 deletions(-) + +diff --git a/fs/smb/client/cifssmb.c b/fs/smb/client/cifssmb.c +index e9e33b0b3ac47..01e89070df5ab 100644 +--- a/fs/smb/client/cifssmb.c ++++ b/fs/smb/client/cifssmb.c +@@ -2700,11 +2700,12 @@ int cifs_query_reparse_point(const unsigned int xid, + u32 *tag, struct kvec *rsp, + int *rsp_buftype) + { ++ struct reparse_data_buffer *buf; + struct cifs_open_parms oparms; + TRANSACT_IOCTL_REQ *io_req = NULL; + TRANSACT_IOCTL_RSP *io_rsp = NULL; + struct cifs_fid fid; +- __u32 data_offset, data_count; ++ __u32 data_offset, data_count, len; + __u8 *start, *end; + int io_rsp_len; + int oplock = 0; +@@ -2774,7 +2775,16 @@ int cifs_query_reparse_point(const unsigned int xid, + goto error; + } + +- *tag = le32_to_cpu(((struct reparse_data_buffer *)start)->ReparseTag); ++ data_count = le16_to_cpu(io_rsp->ByteCount); ++ buf = (struct reparse_data_buffer *)start; ++ len = sizeof(*buf); ++ if (data_count < len || ++ data_count < le16_to_cpu(buf->ReparseDataLength) + len) { ++ rc = -EIO; ++ goto error; ++ } ++ ++ *tag = le32_to_cpu(buf->ReparseTag); + rsp->iov_base = io_rsp; + rsp->iov_len = io_rsp_len; + *rsp_buftype = CIFS_LARGE_BUFFER; +diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c +index 9b7cdb7d7ece8..3e07ab1564ea7 100644 +--- a/fs/smb/client/smb2ops.c ++++ b/fs/smb/client/smb2ops.c +@@ -2946,18 +2946,6 @@ int parse_reparse_point(struct reparse_data_buffer *buf, + u32 plen, struct cifs_sb_info *cifs_sb, + bool unicode, struct cifs_open_info_data *data) + { +- if (plen < sizeof(*buf)) { +- cifs_dbg(VFS, "%s: reparse buffer is too small. Must be at least 8 bytes but was %d\n", +- __func__, plen); +- return -EIO; +- } +- +- if (plen < le16_to_cpu(buf->ReparseDataLength) + sizeof(*buf)) { +- cifs_dbg(VFS, "%s: invalid reparse buf length: %d\n", +- __func__, plen); +- return -EIO; +- } +- + data->reparse.buf = buf; + + /* See MS-FSCC 2.1.2 */ +-- +2.43.0 + diff --git a/queue-6.6/cifs-minor-comment-cleanup.patch b/queue-6.6/cifs-minor-comment-cleanup.patch new file mode 100644 index 0000000000..a415c3f0e5 --- /dev/null +++ b/queue-6.6/cifs-minor-comment-cleanup.patch @@ -0,0 +1,39 @@ +From 3d93dca3f404c16f29dfcba65556309ffa68bb1c Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Wed, 17 Jan 2024 16:56:05 -0600 +Subject: cifs: minor comment cleanup + +From: Steve French <stfrench@microsoft.com> + +[ Upstream commit 0b549c4f594167d7ef056393c6a06ac77f5690ff ] + +minor comment cleanup and trivial camelCase removal + +Reviewed-by: Bharath SM <bharathsm@microsoft.com> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/client/readdir.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/fs/smb/client/readdir.c b/fs/smb/client/readdir.c +index 56033e4e4bae9..47f5a82bc2507 100644 +--- a/fs/smb/client/readdir.c ++++ b/fs/smb/client/readdir.c +@@ -647,10 +647,10 @@ static int cifs_entry_is_dot(struct cifs_dirent *de, bool is_unicode) + static int is_dir_changed(struct file *file) + { + struct inode *inode = file_inode(file); +- struct cifsInodeInfo *cifsInfo = CIFS_I(inode); ++ struct cifsInodeInfo *cifs_inode_info = CIFS_I(inode); + +- if (cifsInfo->time == 0) +- return 1; /* directory was changed, perhaps due to unlink */ ++ if (cifs_inode_info->time == 0) ++ return 1; /* directory was changed, e.g. unlink or new file */ + else + return 0; + +-- +2.43.0 + diff --git a/queue-6.6/cifs-move-some-extern-decls-from-.c-files-to-.h.patch b/queue-6.6/cifs-move-some-extern-decls-from-.c-files-to-.h.patch new file mode 100644 index 0000000000..33a2d5962c --- /dev/null +++ b/queue-6.6/cifs-move-some-extern-decls-from-.c-files-to-.h.patch @@ -0,0 +1,88 @@ +From eee592d5b525e0f16fca02820a17e5df04d5e8c6 Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Wed, 1 May 2024 01:39:48 -0500 +Subject: cifs: Move some extern decls from .c files to .h + +From: Steve French <stfrench@microsoft.com> + +[ Upstream commit 5b142b37c70b1fa6936fa2d0babb0b8c16767d3a ] + +Move the following: + + extern mempool_t *cifs_sm_req_poolp; + extern mempool_t *cifs_req_poolp; + extern mempool_t *cifs_mid_poolp; + extern bool disable_legacy_dialects; + +from various .c files to cifsglob.h. + +Signed-off-by: David Howells <dhowells@redhat.com> +cc: linux-cifs@vger.kernel.org +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/client/cifsfs.c | 4 ---- + fs/smb/client/cifsglob.h | 2 ++ + fs/smb/client/connect.c | 3 --- + fs/smb/client/misc.c | 3 --- + 4 files changed, 2 insertions(+), 10 deletions(-) + +diff --git a/fs/smb/client/cifsfs.c b/fs/smb/client/cifsfs.c +index 30bf754c9fc93..539ac9774de1b 100644 +--- a/fs/smb/client/cifsfs.c ++++ b/fs/smb/client/cifsfs.c +@@ -150,10 +150,6 @@ MODULE_PARM_DESC(disable_legacy_dialects, "To improve security it may be " + "vers=1.0 (CIFS/SMB1) and vers=2.0 are weaker" + " and less secure. Default: n/N/0"); + +-extern mempool_t *cifs_sm_req_poolp; +-extern mempool_t *cifs_req_poolp; +-extern mempool_t *cifs_mid_poolp; +- + struct workqueue_struct *cifsiod_wq; + struct workqueue_struct *decrypt_wq; + struct workqueue_struct *fileinfo_put_wq; +diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h +index 70a12584375de..9597887280ff3 100644 +--- a/fs/smb/client/cifsglob.h ++++ b/fs/smb/client/cifsglob.h +@@ -2112,6 +2112,8 @@ extern struct workqueue_struct *deferredclose_wq; + extern struct workqueue_struct *serverclose_wq; + extern __u32 cifs_lock_secret; + ++extern mempool_t *cifs_sm_req_poolp; ++extern mempool_t *cifs_req_poolp; + extern mempool_t *cifs_mid_poolp; + + /* Operations for different SMB versions */ +diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c +index 5acfd2057ca04..4e35970681bf0 100644 +--- a/fs/smb/client/connect.c ++++ b/fs/smb/client/connect.c +@@ -52,9 +52,6 @@ + #include "fs_context.h" + #include "cifs_swn.h" + +-extern mempool_t *cifs_req_poolp; +-extern bool disable_legacy_dialects; +- + /* FIXME: should these be tunable? */ + #define TLINK_ERROR_EXPIRE (1 * HZ) + #define TLINK_IDLE_EXPIRE (600 * HZ) +diff --git a/fs/smb/client/misc.c b/fs/smb/client/misc.c +index 669d27b4d414a..ad44f8d66b377 100644 +--- a/fs/smb/client/misc.c ++++ b/fs/smb/client/misc.c +@@ -27,9 +27,6 @@ + #include "fs_context.h" + #include "cached_dir.h" + +-extern mempool_t *cifs_sm_req_poolp; +-extern mempool_t *cifs_req_poolp; +- + /* The xid serves as a useful identifier for each incoming vfs request, + in a similar way to the mid which is useful to track each sent smb, + and CurrentXid can also provide a running counter (although it +-- +2.43.0 + diff --git a/queue-6.6/cifs-new-mount-option-called-retrans.patch b/queue-6.6/cifs-new-mount-option-called-retrans.patch new file mode 100644 index 0000000000..8dd84c127e --- /dev/null +++ b/queue-6.6/cifs-new-mount-option-called-retrans.patch @@ -0,0 +1,129 @@ +From 064744759440bad9725fe64bbf73886ed5d1e365 Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Wed, 17 Jan 2024 06:09:16 +0000 +Subject: cifs: new mount option called retrans + +From: Shyam Prasad N <sprasad@microsoft.com> + +[ Upstream commit ce09f8d8a7130e6edfdd6fcad8eb277824d5de95 ] + +We have several places in the code where we treat the +error -EAGAIN very differently. Some code retry for +arbitrary number of times. + +Introducing this new mount option named "retrans", so +that all these handlers of -EAGAIN can retry a fixed +number of times. This applies only to soft mounts. + +Signed-off-by: Shyam Prasad N <sprasad@microsoft.com> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/client/cifsfs.c | 2 ++ + fs/smb/client/cifsglob.h | 1 + + fs/smb/client/connect.c | 4 ++++ + fs/smb/client/fs_context.c | 6 ++++++ + fs/smb/client/fs_context.h | 2 ++ + 5 files changed, 15 insertions(+) + +diff --git a/fs/smb/client/cifsfs.c b/fs/smb/client/cifsfs.c +index 44e2cc37a8b63..6d9d2174ee691 100644 +--- a/fs/smb/client/cifsfs.c ++++ b/fs/smb/client/cifsfs.c +@@ -682,6 +682,8 @@ cifs_show_options(struct seq_file *s, struct dentry *root) + seq_printf(s, ",rasize=%u", cifs_sb->ctx->rasize); + if (tcon->ses->server->min_offload) + seq_printf(s, ",esize=%u", tcon->ses->server->min_offload); ++ if (tcon->ses->server->retrans) ++ seq_printf(s, ",retrans=%u", tcon->ses->server->retrans); + seq_printf(s, ",echo_interval=%lu", + tcon->ses->server->echo_interval / HZ); + +diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h +index 414648bf816b2..6acadb53ada79 100644 +--- a/fs/smb/client/cifsglob.h ++++ b/fs/smb/client/cifsglob.h +@@ -754,6 +754,7 @@ struct TCP_Server_Info { + unsigned int max_read; + unsigned int max_write; + unsigned int min_offload; ++ unsigned int retrans; + __le16 compress_algorithm; + __u16 signing_algorithm; + __le16 cipher_type; +diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c +index 2466b28379ff8..e28f011f11d6c 100644 +--- a/fs/smb/client/connect.c ++++ b/fs/smb/client/connect.c +@@ -1592,6 +1592,9 @@ static int match_server(struct TCP_Server_Info *server, + if (server->min_offload != ctx->min_offload) + return 0; + ++ if (server->retrans != ctx->retrans) ++ return 0; ++ + return 1; + } + +@@ -1816,6 +1819,7 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx, + goto out_err_crypto_release; + } + tcp_ses->min_offload = ctx->min_offload; ++ tcp_ses->retrans = ctx->retrans; + /* + * at this point we are the only ones with the pointer + * to the struct since the kernel thread not created yet +diff --git a/fs/smb/client/fs_context.c b/fs/smb/client/fs_context.c +index 4d9e57be84dbc..f119035a82725 100644 +--- a/fs/smb/client/fs_context.c ++++ b/fs/smb/client/fs_context.c +@@ -139,6 +139,7 @@ const struct fs_parameter_spec smb3_fs_parameters[] = { + fsparam_u32("dir_mode", Opt_dirmode), + fsparam_u32("port", Opt_port), + fsparam_u32("min_enc_offload", Opt_min_enc_offload), ++ fsparam_u32("retrans", Opt_retrans), + fsparam_u32("esize", Opt_min_enc_offload), + fsparam_u32("bsize", Opt_blocksize), + fsparam_u32("rasize", Opt_rasize), +@@ -1098,6 +1099,9 @@ static int smb3_fs_context_parse_param(struct fs_context *fc, + case Opt_min_enc_offload: + ctx->min_offload = result.uint_32; + break; ++ case Opt_retrans: ++ ctx->retrans = result.uint_32; ++ break; + case Opt_blocksize: + /* + * inode blocksize realistically should never need to be +@@ -1678,6 +1682,8 @@ int smb3_init_fs_context(struct fs_context *fc) + ctx->backupuid_specified = false; /* no backup intent for a user */ + ctx->backupgid_specified = false; /* no backup intent for a group */ + ++ ctx->retrans = 1; ++ + /* + * short int override_uid = -1; + * short int override_gid = -1; +diff --git a/fs/smb/client/fs_context.h b/fs/smb/client/fs_context.h +index d7c090dbe75db..369a3fea1dfe0 100644 +--- a/fs/smb/client/fs_context.h ++++ b/fs/smb/client/fs_context.h +@@ -118,6 +118,7 @@ enum cifs_param { + Opt_file_mode, + Opt_dirmode, + Opt_min_enc_offload, ++ Opt_retrans, + Opt_blocksize, + Opt_rasize, + Opt_rsize, +@@ -249,6 +250,7 @@ struct smb3_fs_context { + unsigned int rsize; + unsigned int wsize; + unsigned int min_offload; ++ unsigned int retrans; + bool sockopt_tcp_nodelay:1; + /* attribute cache timemout for files and directories in jiffies */ + unsigned long acregmax; +-- +2.43.0 + diff --git a/queue-6.6/cifs-new-nt-status-codes-from-ms-smb2.patch b/queue-6.6/cifs-new-nt-status-codes-from-ms-smb2.patch new file mode 100644 index 0000000000..2d9bd1bef8 --- /dev/null +++ b/queue-6.6/cifs-new-nt-status-codes-from-ms-smb2.patch @@ -0,0 +1,53 @@ +From 294940c91aa1dab31a1b84d6e666fb7cc07fab2c Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Wed, 17 Jan 2024 06:21:33 +0000 +Subject: cifs: new nt status codes from MS-SMB2 + +From: Shyam Prasad N <sprasad@microsoft.com> + +[ Upstream commit 7f738527a7a03021c7e1b02e188f446845f05eb6 ] + +MS-SMB2 spec has introduced two new status codes, +STATUS_SERVER_UNAVAILABLE and STATUS_FILE_NOT_AVAILABLE +which are to be treated as retryable errors. + +This change adds these to the available mappings and +maps them to Linux errno EAGAIN. + +Signed-off-by: Shyam Prasad N <sprasad@microsoft.com> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/client/smb2maperror.c | 2 ++ + fs/smb/client/smb2status.h | 2 ++ + 2 files changed, 4 insertions(+) + +diff --git a/fs/smb/client/smb2maperror.c b/fs/smb/client/smb2maperror.c +index 1a90dd78b238f..ac1895358908a 100644 +--- a/fs/smb/client/smb2maperror.c ++++ b/fs/smb/client/smb2maperror.c +@@ -1210,6 +1210,8 @@ static const struct status_to_posix_error smb2_error_map_table[] = { + {STATUS_INVALID_TASK_INDEX, -EIO, "STATUS_INVALID_TASK_INDEX"}, + {STATUS_THREAD_ALREADY_IN_TASK, -EIO, "STATUS_THREAD_ALREADY_IN_TASK"}, + {STATUS_CALLBACK_BYPASS, -EIO, "STATUS_CALLBACK_BYPASS"}, ++ {STATUS_SERVER_UNAVAILABLE, -EAGAIN, "STATUS_SERVER_UNAVAILABLE"}, ++ {STATUS_FILE_NOT_AVAILABLE, -EAGAIN, "STATUS_FILE_NOT_AVAILABLE"}, + {STATUS_PORT_CLOSED, -EIO, "STATUS_PORT_CLOSED"}, + {STATUS_MESSAGE_LOST, -EIO, "STATUS_MESSAGE_LOST"}, + {STATUS_INVALID_MESSAGE, -EIO, "STATUS_INVALID_MESSAGE"}, +diff --git a/fs/smb/client/smb2status.h b/fs/smb/client/smb2status.h +index a9e958166fc53..9c6d79b0bd497 100644 +--- a/fs/smb/client/smb2status.h ++++ b/fs/smb/client/smb2status.h +@@ -982,6 +982,8 @@ struct ntstatus { + #define STATUS_INVALID_TASK_INDEX cpu_to_le32(0xC0000501) + #define STATUS_THREAD_ALREADY_IN_TASK cpu_to_le32(0xC0000502) + #define STATUS_CALLBACK_BYPASS cpu_to_le32(0xC0000503) ++#define STATUS_SERVER_UNAVAILABLE cpu_to_le32(0xC0000466) ++#define STATUS_FILE_NOT_AVAILABLE cpu_to_le32(0xC0000467) + #define STATUS_PORT_CLOSED cpu_to_le32(0xC0000700) + #define STATUS_MESSAGE_LOST cpu_to_le32(0xC0000701) + #define STATUS_INVALID_MESSAGE cpu_to_le32(0xC0000702) +-- +2.43.0 + diff --git a/queue-6.6/cifs-pass-unbyteswapped-eof-value-into-smb2_set_eof.patch b/queue-6.6/cifs-pass-unbyteswapped-eof-value-into-smb2_set_eof.patch new file mode 100644 index 0000000000..edad986a76 --- /dev/null +++ b/queue-6.6/cifs-pass-unbyteswapped-eof-value-into-smb2_set_eof.patch @@ -0,0 +1,193 @@ +From 7ce4a62b34ef5bb5258d8b5bb5c5111190fea8e7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Mon, 1 Jan 2024 15:40:10 +0000 +Subject: cifs: Pass unbyteswapped eof value into SMB2_set_eof() + +From: David Howells <dhowells@redhat.com> + +[ Upstream commit 6ebfede8d57a615dcbdec7e490faed585153f7f1 ] + +Change SMB2_set_eof() to take eof as CPU order rather than __le64 and pass +it directly rather than by pointer. This moves the conversion down into +SMB_set_eof() rather than all of its callers and means we don't need to +undo it for the traceline. + +Signed-off-by: David Howells <dhowells@redhat.com> +cc: Jeff Layton <jlayton@kernel.org> +cc: linux-cifs@vger.kernel.org +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/client/smb2ops.c | 37 ++++++++++++++++--------------------- + fs/smb/client/smb2pdu.c | 6 +++--- + fs/smb/client/smb2proto.h | 2 +- + 3 files changed, 20 insertions(+), 25 deletions(-) + +diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c +index a623a720db9e0..9b7cdb7d7ece8 100644 +--- a/fs/smb/client/smb2ops.c ++++ b/fs/smb/client/smb2ops.c +@@ -1932,7 +1932,6 @@ static int + smb2_set_file_size(const unsigned int xid, struct cifs_tcon *tcon, + struct cifsFileInfo *cfile, __u64 size, bool set_alloc) + { +- __le64 eof = cpu_to_le64(size); + struct inode *inode; + + /* +@@ -1949,7 +1948,7 @@ smb2_set_file_size(const unsigned int xid, struct cifs_tcon *tcon, + } + + return SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid, +- cfile->fid.volatile_fid, cfile->pid, &eof); ++ cfile->fid.volatile_fid, cfile->pid, size); + } + + static int +@@ -3196,7 +3195,6 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon, + unsigned long long new_size; + long rc; + unsigned int xid; +- __le64 eof; + + xid = get_xid(); + +@@ -3226,9 +3224,8 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon, + */ + new_size = offset + len; + if (keep_size == false && (unsigned long long)i_size_read(inode) < new_size) { +- eof = cpu_to_le64(new_size); + rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid, +- cfile->fid.volatile_fid, cfile->pid, &eof); ++ cfile->fid.volatile_fid, cfile->pid, new_size); + if (rc >= 0) { + truncate_setsize(inode, new_size); + fscache_resize_cookie(cifs_inode_cookie(inode), new_size); +@@ -3421,7 +3418,7 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon, + struct cifsFileInfo *cfile = file->private_data; + long rc = -EOPNOTSUPP; + unsigned int xid; +- __le64 eof; ++ loff_t new_eof; + + xid = get_xid(); + +@@ -3450,14 +3447,14 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon, + if (cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE) + smb2_set_sparse(xid, tcon, cfile, inode, false); + +- eof = cpu_to_le64(off + len); ++ new_eof = off + len; + rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid, +- cfile->fid.volatile_fid, cfile->pid, &eof); ++ cfile->fid.volatile_fid, cfile->pid, new_eof); + if (rc == 0) { +- cifsi->server_eof = off + len; +- cifs_setsize(inode, off + len); ++ cifsi->server_eof = new_eof; ++ cifs_setsize(inode, new_eof); + cifs_truncate_page(inode->i_mapping, inode->i_size); +- truncate_setsize(inode, off + len); ++ truncate_setsize(inode, new_eof); + } + goto out; + } +@@ -3548,8 +3545,7 @@ static long smb3_collapse_range(struct file *file, struct cifs_tcon *tcon, + struct inode *inode = file_inode(file); + struct cifsFileInfo *cfile = file->private_data; + struct cifsInodeInfo *cifsi = CIFS_I(inode); +- __le64 eof; +- loff_t old_eof; ++ loff_t old_eof, new_eof; + + xid = get_xid(); + +@@ -3574,9 +3570,9 @@ static long smb3_collapse_range(struct file *file, struct cifs_tcon *tcon, + if (rc < 0) + goto out_2; + +- eof = cpu_to_le64(old_eof - len); ++ new_eof = old_eof - len; + rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid, +- cfile->fid.volatile_fid, cfile->pid, &eof); ++ cfile->fid.volatile_fid, cfile->pid, new_eof); + if (rc < 0) + goto out_2; + +@@ -3600,8 +3596,7 @@ static long smb3_insert_range(struct file *file, struct cifs_tcon *tcon, + unsigned int xid; + struct cifsFileInfo *cfile = file->private_data; + struct inode *inode = file_inode(file); +- __le64 eof; +- __u64 count, old_eof; ++ __u64 count, old_eof, new_eof; + + xid = get_xid(); + +@@ -3614,20 +3609,20 @@ static long smb3_insert_range(struct file *file, struct cifs_tcon *tcon, + } + + count = old_eof - off; +- eof = cpu_to_le64(old_eof + len); ++ new_eof = old_eof + len; + + filemap_invalidate_lock(inode->i_mapping); +- rc = filemap_write_and_wait_range(inode->i_mapping, off, old_eof + len - 1); ++ rc = filemap_write_and_wait_range(inode->i_mapping, off, new_eof - 1); + if (rc < 0) + goto out_2; + truncate_pagecache_range(inode, off, old_eof); + + rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid, +- cfile->fid.volatile_fid, cfile->pid, &eof); ++ cfile->fid.volatile_fid, cfile->pid, new_eof); + if (rc < 0) + goto out_2; + +- truncate_setsize(inode, old_eof + len); ++ truncate_setsize(inode, new_eof); + fscache_resize_cookie(cifs_inode_cookie(inode), i_size_read(inode)); + + rc = smb2_copychunk_range(xid, cfile, cfile, off, count, off + len); +diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c +index 70530108b9bb9..51ff9a967c503 100644 +--- a/fs/smb/client/smb2pdu.c ++++ b/fs/smb/client/smb2pdu.c +@@ -5414,18 +5414,18 @@ send_set_info(const unsigned int xid, struct cifs_tcon *tcon, + + int + SMB2_set_eof(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, +- u64 volatile_fid, u32 pid, __le64 *eof) ++ u64 volatile_fid, u32 pid, loff_t new_eof) + { + struct smb2_file_eof_info info; + void *data; + unsigned int size; + +- info.EndOfFile = *eof; ++ info.EndOfFile = cpu_to_le64(new_eof); + + data = &info; + size = sizeof(struct smb2_file_eof_info); + +- trace_smb3_set_eof(xid, persistent_fid, tcon->tid, tcon->ses->Suid, le64_to_cpu(*eof)); ++ trace_smb3_set_eof(xid, persistent_fid, tcon->tid, tcon->ses->Suid, new_eof); + + return send_set_info(xid, tcon, persistent_fid, volatile_fid, + pid, FILE_END_OF_FILE_INFORMATION, SMB2_O_INFO_FILE, +diff --git a/fs/smb/client/smb2proto.h b/fs/smb/client/smb2proto.h +index 1e20f87a5f584..343ada691e763 100644 +--- a/fs/smb/client/smb2proto.h ++++ b/fs/smb/client/smb2proto.h +@@ -221,7 +221,7 @@ extern int SMB2_query_directory_init(unsigned int xid, struct cifs_tcon *tcon, + extern void SMB2_query_directory_free(struct smb_rqst *rqst); + extern int SMB2_set_eof(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, u32 pid, +- __le64 *eof); ++ loff_t new_eof); + extern int SMB2_set_info_init(struct cifs_tcon *tcon, + struct TCP_Server_Info *server, + struct smb_rqst *rqst, +-- +2.43.0 + diff --git a/queue-6.6/cifs-pick-channel-for-tcon-and-tdis.patch b/queue-6.6/cifs-pick-channel-for-tcon-and-tdis.patch new file mode 100644 index 0000000000..8b8ba87df4 --- /dev/null +++ b/queue-6.6/cifs-pick-channel-for-tcon-and-tdis.patch @@ -0,0 +1,71 @@ +From 9b4a681bd153df68f861c64d8fced290d9ab807a Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Wed, 10 Jan 2024 10:48:36 +0000 +Subject: cifs: pick channel for tcon and tdis + +From: Shyam Prasad N <sprasad@microsoft.com> + +[ Upstream commit 268b8b5797becb242013fcd63173eb28c007c8ae ] + +Today, the tree connect and disconnect requests are +sent on the primary channel only. However, the new +multichannel logic allows the session to remain active +even if one of the channels are alive. So a tree connect +can now be triggered during a reconnect on any of +its channels. + +This change changes tcon and tdis calls to pick an +active channel instead of the first one. + +Signed-off-by: Shyam Prasad N <sprasad@microsoft.com> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/client/smb2pdu.c | 10 ++++------ + 1 file changed, 4 insertions(+), 6 deletions(-) + +diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c +index 3f9448b5ada9b..95b5b4bdb4b7f 100644 +--- a/fs/smb/client/smb2pdu.c ++++ b/fs/smb/client/smb2pdu.c +@@ -2019,10 +2019,7 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree, + __le16 *unc_path = NULL; + int flags = 0; + unsigned int total_len; +- struct TCP_Server_Info *server; +- +- /* always use master channel */ +- server = ses->server; ++ struct TCP_Server_Info *server = cifs_pick_channel(ses); + + cifs_dbg(FYI, "TCON\n"); + +@@ -2155,6 +2152,7 @@ SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon) + struct smb2_tree_disconnect_req *req; /* response is trivial */ + int rc = 0; + struct cifs_ses *ses = tcon->ses; ++ struct TCP_Server_Info *server = cifs_pick_channel(ses); + int flags = 0; + unsigned int total_len; + struct kvec iov[1]; +@@ -2177,7 +2175,7 @@ SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon) + + invalidate_all_cached_dirs(tcon); + +- rc = smb2_plain_req_init(SMB2_TREE_DISCONNECT, tcon, ses->server, ++ rc = smb2_plain_req_init(SMB2_TREE_DISCONNECT, tcon, server, + (void **) &req, + &total_len); + if (rc) +@@ -2195,7 +2193,7 @@ SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon) + rqst.rq_iov = iov; + rqst.rq_nvec = 1; + +- rc = cifs_send_recv(xid, ses, ses->server, ++ rc = cifs_send_recv(xid, ses, server, + &rqst, &resp_buf_type, flags, &rsp_iov); + cifs_small_buf_release(req); + if (rc) { +-- +2.43.0 + diff --git a/queue-6.6/cifs-print-server-capabilities-in-debugdata.patch b/queue-6.6/cifs-print-server-capabilities-in-debugdata.patch new file mode 100644 index 0000000000..4f29fb76c0 --- /dev/null +++ b/queue-6.6/cifs-print-server-capabilities-in-debugdata.patch @@ -0,0 +1,36 @@ +From 3c9c02465826c0d7220e9ae2b820794057c27566 Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Mon, 30 Oct 2023 11:00:07 +0000 +Subject: cifs: print server capabilities in DebugData + +From: Shyam Prasad N <sprasad@microsoft.com> + +[ Upstream commit 52768695d36a44d352e9fb79ba27468a5363ab8d ] + +In the output of /proc/fs/cifs/DebugData, we do not +print the server->capabilities field today. +With this change, we will do that. + +Signed-off-by: Shyam Prasad N <sprasad@microsoft.com> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/client/cifs_debug.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/fs/smb/client/cifs_debug.c b/fs/smb/client/cifs_debug.c +index c53d516459fc4..058e703107fc7 100644 +--- a/fs/smb/client/cifs_debug.c ++++ b/fs/smb/client/cifs_debug.c +@@ -438,6 +438,8 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v) + if (server->nosharesock) + seq_printf(m, " nosharesock"); + ++ seq_printf(m, "\nServer capabilities: 0x%x", server->capabilities); ++ + if (server->rdma) + seq_printf(m, "\nRDMA "); + seq_printf(m, "\nTCP status: %d Instance: %d" +-- +2.43.0 + diff --git a/queue-6.6/cifs-remove-redundant-variable-assignment.patch b/queue-6.6/cifs-remove-redundant-variable-assignment.patch new file mode 100644 index 0000000000..45633103f9 --- /dev/null +++ b/queue-6.6/cifs-remove-redundant-variable-assignment.patch @@ -0,0 +1,43 @@ +From 65992ea21d7ffa8cfc35048d2a38a591461055a4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Thu, 14 Mar 2024 23:36:36 +0530 +Subject: cifs: remove redundant variable assignment + +From: Bharath SM <bharathsm@microsoft.com> + +[ Upstream commit 2760161d149f8d60c3f767fc62a823a1ead9d367 ] + +This removes an unnecessary variable assignment. The assigned +value will be overwritten by cifs_fattr_to_inode before it +is accessed, making the line redundant. + +Signed-off-by: Bharath SM <bharathsm@microsoft.com> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/client/inode.c | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c +index 67ad8eeaa7665..b304215a4d668 100644 +--- a/fs/smb/client/inode.c ++++ b/fs/smb/client/inode.c +@@ -401,7 +401,6 @@ cifs_get_file_info_unix(struct file *filp) + cifs_unix_basic_to_fattr(&fattr, &find_data, cifs_sb); + } else if (rc == -EREMOTE) { + cifs_create_junction_fattr(&fattr, inode->i_sb); +- rc = 0; + } else + goto cifs_gfiunix_out; + +@@ -846,7 +845,6 @@ cifs_get_file_info(struct file *filp) + break; + case -EREMOTE: + cifs_create_junction_fattr(&fattr, inode->i_sb); +- rc = 0; + break; + case -EOPNOTSUPP: + case -EINVAL: +-- +2.43.0 + diff --git a/queue-6.6/cifs-remove-redundant-variable-tcon_exist.patch b/queue-6.6/cifs-remove-redundant-variable-tcon_exist.patch new file mode 100644 index 0000000000..522f957752 --- /dev/null +++ b/queue-6.6/cifs-remove-redundant-variable-tcon_exist.patch @@ -0,0 +1,58 @@ +From 13bcd859cb288d7e036bd5509a26abf84ef823a2 Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Tue, 16 Jan 2024 10:51:34 +0000 +Subject: cifs: remove redundant variable tcon_exist + +From: Colin Ian King <colin.i.king@gmail.com> + +[ Upstream commit 8ca5d2641be217a78a891d4dbe2a46232d1d8eb9 ] + +The variable tcon_exist is being assigned however it is never read, the +variable is redundant and can be removed. + +Cleans up clang scan build warning: +warning: Although the value stored to 'tcon_exist' is used in +the enclosing expression, the value is never actually readfrom +'tcon_exist' [deadcode.DeadStores] + +Signed-off-by: Colin Ian King <colin.i.king@gmail.com> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/client/smb2pdu.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c +index 51ff9a967c503..3f9448b5ada9b 100644 +--- a/fs/smb/client/smb2pdu.c ++++ b/fs/smb/client/smb2pdu.c +@@ -3979,7 +3979,7 @@ void smb2_reconnect_server(struct work_struct *work) + struct cifs_ses *ses, *ses2; + struct cifs_tcon *tcon, *tcon2; + struct list_head tmp_list, tmp_ses_list; +- bool tcon_exist = false, ses_exist = false; ++ bool ses_exist = false; + bool tcon_selected = false; + int rc; + bool resched = false; +@@ -4025,7 +4025,7 @@ void smb2_reconnect_server(struct work_struct *work) + if (tcon->need_reconnect || tcon->need_reopen_files) { + tcon->tc_count++; + list_add_tail(&tcon->rlist, &tmp_list); +- tcon_selected = tcon_exist = true; ++ tcon_selected = true; + } + } + /* +@@ -4034,7 +4034,7 @@ void smb2_reconnect_server(struct work_struct *work) + */ + if (ses->tcon_ipc && ses->tcon_ipc->need_reconnect) { + list_add_tail(&ses->tcon_ipc->rlist, &tmp_list); +- tcon_selected = tcon_exist = true; ++ tcon_selected = true; + cifs_smb_ses_inc_refcount(ses); + } + /* +-- +2.43.0 + diff --git a/queue-6.6/cifs-remove-unneeded-return-statement.patch b/queue-6.6/cifs-remove-unneeded-return-statement.patch new file mode 100644 index 0000000000..f0a33af56a --- /dev/null +++ b/queue-6.6/cifs-remove-unneeded-return-statement.patch @@ -0,0 +1,34 @@ +From 524caa64e653c4749821c8f13c878080c120e49c Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Mon, 8 Jan 2024 22:37:10 -0600 +Subject: cifs: remove unneeded return statement + +From: Steve French <stfrench@microsoft.com> + +[ Upstream commit a3f763fdcb2f784c355aed66ddac6748ff8dbfa6 ] + +Return statement was not needed at end of cifs_chan_update_iface + +Suggested-by: Christophe Jaillet <christophe.jaillet@wanadoo.fr> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/client/sess.c | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/fs/smb/client/sess.c b/fs/smb/client/sess.c +index 5de32640f0265..3216f786908fb 100644 +--- a/fs/smb/client/sess.c ++++ b/fs/smb/client/sess.c +@@ -485,8 +485,6 @@ cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server) + + ses->chans[chan_index].iface = iface; + spin_unlock(&ses->chan_lock); +- +- return; + } + + /* +-- +2.43.0 + diff --git a/queue-6.6/cifs-set-replay-flag-for-retries-of-write-command.patch b/queue-6.6/cifs-set-replay-flag-for-retries-of-write-command.patch new file mode 100644 index 0000000000..67f1d208c5 --- /dev/null +++ b/queue-6.6/cifs-set-replay-flag-for-retries-of-write-command.patch @@ -0,0 +1,74 @@ +From e4f9f10921c4186f927aaa359652c50085b4bab0 Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Thu, 18 Jan 2024 09:14:10 +0000 +Subject: cifs: set replay flag for retries of write command + +From: Shyam Prasad N <sprasad@microsoft.com> + +[ Upstream commit 4cdad80261862c8cdcbb5fd232aa713d0bdefe24 ] + +Similar to the rest of the commands, this is a change +to add replay flags on retry. This one does not add a +back-off, considering that we may want to flush a write +ASAP to the server. Considering that this will be a +flush of cached pages, the retrans value is also not +honoured. + +Signed-off-by: Shyam Prasad N <sprasad@microsoft.com> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/client/cifsglob.h | 1 + + fs/smb/client/file.c | 1 + + fs/smb/client/smb2pdu.c | 4 +++- + 3 files changed, 5 insertions(+), 1 deletion(-) + +diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h +index 479bf0d9ad589..8fbdb781d70a6 100644 +--- a/fs/smb/client/cifsglob.h ++++ b/fs/smb/client/cifsglob.h +@@ -1515,6 +1515,7 @@ struct cifs_writedata { + struct smbd_mr *mr; + #endif + struct cifs_credits credits; ++ bool replay; + }; + + /* +diff --git a/fs/smb/client/file.c b/fs/smb/client/file.c +index f41804245fca1..6d44991e1ccdc 100644 +--- a/fs/smb/client/file.c ++++ b/fs/smb/client/file.c +@@ -3413,6 +3413,7 @@ cifs_resend_wdata(struct cifs_writedata *wdata, struct list_head *wdata_list, + if (wdata->cfile->invalidHandle) + rc = -EAGAIN; + else { ++ wdata->replay = true; + #ifdef CONFIG_CIFS_SMB_DIRECT + if (wdata->mr) { + wdata->mr->need_invalidate = true; +diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c +index 6a5d478b3cef6..c73a621a8b83e 100644 +--- a/fs/smb/client/smb2pdu.c ++++ b/fs/smb/client/smb2pdu.c +@@ -4801,7 +4801,7 @@ smb2_async_writev(struct cifs_writedata *wdata, + struct cifs_io_parms *io_parms = NULL; + int credit_request; + +- if (!wdata->server) ++ if (!wdata->server || wdata->replay) + server = wdata->server = cifs_pick_channel(tcon->ses); + + /* +@@ -4886,6 +4886,8 @@ smb2_async_writev(struct cifs_writedata *wdata, + rqst.rq_nvec = 1; + rqst.rq_iter = wdata->iter; + rqst.rq_iter_size = iov_iter_count(&rqst.rq_iter); ++ if (wdata->replay) ++ smb2_set_replay(server, &rqst); + #ifdef CONFIG_CIFS_SMB_DIRECT + if (wdata->mr) + iov[0].iov_len += sizeof(struct smbd_buffer_descriptor_v1); +-- +2.43.0 + diff --git a/queue-6.6/cifs-update-internal-module-version-number-for-cifs..patch b/queue-6.6/cifs-update-internal-module-version-number-for-cifs..patch new file mode 100644 index 0000000000..cddc478b24 --- /dev/null +++ b/queue-6.6/cifs-update-internal-module-version-number-for-cifs..patch @@ -0,0 +1,33 @@ +From a67adc0e5df58c64f9db7b49b5b9b9bf4b198bb3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Thu, 20 Jul 2023 08:30:32 -0500 +Subject: cifs: update internal module version number for cifs.ko + +From: Steve French <stfrench@microsoft.com> + +[ Upstream commit fd2bd7c0539e28f267a84da8d68f9378511b50a7 ] + +From 2.45 to 2.46 + +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/client/cifsfs.h | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/fs/smb/client/cifsfs.h b/fs/smb/client/cifsfs.h +index 41daebd220ff4..32ccc4d43df65 100644 +--- a/fs/smb/client/cifsfs.h ++++ b/fs/smb/client/cifsfs.h +@@ -152,6 +152,6 @@ extern const struct export_operations cifs_export_ops; + #endif /* CONFIG_CIFS_NFSD_EXPORT */ + + /* when changing internal version - update following two lines at same time */ +-#define SMB3_PRODUCT_BUILD 45 +-#define CIFS_VERSION "2.45" ++#define SMB3_PRODUCT_BUILD 46 ++#define CIFS_VERSION "2.46" + #endif /* _CIFSFS_H */ +-- +2.43.0 + diff --git a/queue-6.6/cifs-update-internal-module-version-number-for-cifs..patch-8866 b/queue-6.6/cifs-update-internal-module-version-number-for-cifs..patch-8866 new file mode 100644 index 0000000000..c3e41249ce --- /dev/null +++ b/queue-6.6/cifs-update-internal-module-version-number-for-cifs..patch-8866 @@ -0,0 +1,33 @@ +From e1f498eebf35c71cc0c1247996020849ebc5a629 Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Tue, 9 Jan 2024 23:42:51 -0600 +Subject: cifs: update internal module version number for cifs.ko + +From: Steve French <stfrench@microsoft.com> + +[ Upstream commit fd2bd7c0539e28f267a84da8d68f9378511b50a7 ] + +From 2.45 to 2.46 + +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/client/cifsfs.h | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/fs/smb/client/cifsfs.h b/fs/smb/client/cifsfs.h +index 32ccc4d43df65..4b3aaa04e7cb7 100644 +--- a/fs/smb/client/cifsfs.h ++++ b/fs/smb/client/cifsfs.h +@@ -152,6 +152,6 @@ extern const struct export_operations cifs_export_ops; + #endif /* CONFIG_CIFS_NFSD_EXPORT */ + + /* when changing internal version - update following two lines at same time */ +-#define SMB3_PRODUCT_BUILD 46 +-#define CIFS_VERSION "2.46" ++#define SMB3_PRODUCT_BUILD 47 ++#define CIFS_VERSION "2.47" + #endif /* _CIFSFS_H */ +-- +2.43.0 + diff --git a/queue-6.6/cifs-update-internal-module-version-number-for-cifs..patch-9055 b/queue-6.6/cifs-update-internal-module-version-number-for-cifs..patch-9055 new file mode 100644 index 0000000000..c828ce1842 --- /dev/null +++ b/queue-6.6/cifs-update-internal-module-version-number-for-cifs..patch-9055 @@ -0,0 +1,33 @@ +From 58a23b024f531589c313697b53a879120f3c3436 Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Sun, 25 Feb 2024 19:33:50 -0600 +Subject: cifs: update internal module version number for cifs.ko + +From: Steve French <stfrench@microsoft.com> + +[ Upstream commit fd2bd7c0539e28f267a84da8d68f9378511b50a7 ] + +From 2.45 to 2.46 + +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/client/cifsfs.h | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/fs/smb/client/cifsfs.h b/fs/smb/client/cifsfs.h +index 4b3aaa04e7cb7..10a9d39cda9a4 100644 +--- a/fs/smb/client/cifsfs.h ++++ b/fs/smb/client/cifsfs.h +@@ -152,6 +152,6 @@ extern const struct export_operations cifs_export_ops; + #endif /* CONFIG_CIFS_NFSD_EXPORT */ + + /* when changing internal version - update following two lines at same time */ +-#define SMB3_PRODUCT_BUILD 47 +-#define CIFS_VERSION "2.47" ++#define SMB3_PRODUCT_BUILD 48 ++#define CIFS_VERSION "2.48" + #endif /* _CIFSFS_H */ +-- +2.43.0 + diff --git a/queue-6.6/cifs-update-the-same-create_guid-on-replay.patch b/queue-6.6/cifs-update-the-same-create_guid-on-replay.patch new file mode 100644 index 0000000000..d8abebc38d --- /dev/null +++ b/queue-6.6/cifs-update-the-same-create_guid-on-replay.patch @@ -0,0 +1,131 @@ +From ac515e199342a708ea34a66e0815880c4a30c5ba Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Sun, 28 Apr 2024 01:32:09 -0500 +Subject: cifs: update the same create_guid on replay + +From: Steve French <stfrench@microsoft.com> + +[ Upstream commit 79520587fe42cd4988aff8695d60621e689109cb ] + +File open requests made to the server contain a +CreateGuid, which is used by the server to identify +the open request. If the same request needs to be +replayed, it needs to be sent with the same CreateGuid +in the durable handle v2 context. + +Without doing so, we could end up leaking handles on +the server when: +1. multichannel is used AND +2. connection goes down, but not for all channels + +This is because the replayed open request would have a +new CreateGuid and the server will treat this as a new +request and open a new handle. + +This change fixes this by reusing the existing create_guid +stored in the cached fid struct. + +REF: MS-SMB2 4.9 Replay Create Request on an Alternate Channel + +Fixes: 4f1fffa23769 ("cifs: commands that are retried should have replay flag set") +Signed-off-by: Shyam Prasad N <sprasad@microsoft.com> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/client/cached_dir.c | 1 + + fs/smb/client/cifsglob.h | 1 + + fs/smb/client/smb2ops.c | 4 ++++ + fs/smb/client/smb2pdu.c | 10 ++++++++-- + 4 files changed, 14 insertions(+), 2 deletions(-) + +diff --git a/fs/smb/client/cached_dir.c b/fs/smb/client/cached_dir.c +index ca0fd25236ef4..0ff2491c311d8 100644 +--- a/fs/smb/client/cached_dir.c ++++ b/fs/smb/client/cached_dir.c +@@ -243,6 +243,7 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon, + FILE_READ_EA, + .disposition = FILE_OPEN, + .fid = pfid, ++ .replay = !!(retries), + }; + + rc = SMB2_open_init(tcon, server, +diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h +index 8fbdb781d70a6..181e9d5b10f92 100644 +--- a/fs/smb/client/cifsglob.h ++++ b/fs/smb/client/cifsglob.h +@@ -1382,6 +1382,7 @@ struct cifs_open_parms { + struct cifs_fid *fid; + umode_t mode; + bool reconnect:1; ++ bool replay:1; /* indicates that this open is for a replay */ + }; + + struct cifs_fid { +diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c +index 06735c5685bf6..23cf6e92fd54c 100644 +--- a/fs/smb/client/smb2ops.c ++++ b/fs/smb/client/smb2ops.c +@@ -1204,6 +1204,7 @@ smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon, + .disposition = FILE_OPEN, + .create_options = cifs_create_options(cifs_sb, 0), + .fid = &fid, ++ .replay = !!(retries), + }; + + rc = SMB2_open_init(tcon, server, +@@ -1570,6 +1571,7 @@ smb2_ioctl_query_info(const unsigned int xid, + .disposition = FILE_OPEN, + .create_options = cifs_create_options(cifs_sb, create_options), + .fid = &fid, ++ .replay = !!(retries), + }; + + if (qi.flags & PASSTHRU_FSCTL) { +@@ -2296,6 +2298,7 @@ smb2_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon, + .disposition = FILE_OPEN, + .create_options = cifs_create_options(cifs_sb, 0), + .fid = fid, ++ .replay = !!(retries), + }; + + rc = SMB2_open_init(tcon, server, +@@ -2684,6 +2687,7 @@ smb2_query_info_compound(const unsigned int xid, struct cifs_tcon *tcon, + .disposition = FILE_OPEN, + .create_options = cifs_create_options(cifs_sb, 0), + .fid = &fid, ++ .replay = !!(retries), + }; + + rc = SMB2_open_init(tcon, server, +diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c +index c73a621a8b83e..60793143e24c6 100644 +--- a/fs/smb/client/smb2pdu.c ++++ b/fs/smb/client/smb2pdu.c +@@ -2421,8 +2421,13 @@ create_durable_v2_buf(struct cifs_open_parms *oparms) + */ + buf->dcontext.Timeout = cpu_to_le32(oparms->tcon->handle_timeout); + buf->dcontext.Flags = cpu_to_le32(SMB2_DHANDLE_FLAG_PERSISTENT); +- generate_random_uuid(buf->dcontext.CreateGuid); +- memcpy(pfid->create_guid, buf->dcontext.CreateGuid, 16); ++ ++ /* for replay, we should not overwrite the existing create guid */ ++ if (!oparms->replay) { ++ generate_random_uuid(buf->dcontext.CreateGuid); ++ memcpy(pfid->create_guid, buf->dcontext.CreateGuid, 16); ++ } else ++ memcpy(buf->dcontext.CreateGuid, pfid->create_guid, 16); + + /* SMB2_CREATE_DURABLE_HANDLE_REQUEST is "DH2Q" */ + buf->Name[0] = 'D'; +@@ -3159,6 +3164,7 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path, + /* reinitialize for possible replay */ + flags = 0; + server = cifs_pick_channel(ses); ++ oparms->replay = !!(retries); + + cifs_dbg(FYI, "create/open\n"); + if (!ses || !server) +-- +2.43.0 + diff --git a/queue-6.6/ksmbd-add-continuous-availability-share-parameter.patch b/queue-6.6/ksmbd-add-continuous-availability-share-parameter.patch new file mode 100644 index 0000000000..082e98d9ce --- /dev/null +++ b/queue-6.6/ksmbd-add-continuous-availability-share-parameter.patch @@ -0,0 +1,99 @@ +From 7c25986452b585ada1370a79afda75152b44374b Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Sat, 20 Apr 2024 09:17:58 +0900 +Subject: ksmbd: add continuous availability share parameter + +From: Namjae Jeon <linkinjeon@kernel.org> + +[ Upstream commit e9d8c2f95ab8acaf3f4d4a53682a4afa3c263692 ] + +If capabilities of the share is not SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY, +ksmbd should not grant a persistent handle to the client. +This patch add continuous availability share parameter to control it. + +Signed-off-by: Namjae Jeon <linkinjeon@kernel.org> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/server/ksmbd_netlink.h | 35 ++++++++++++++++++----------------- + fs/smb/server/smb2pdu.c | 11 +++++++++-- + 2 files changed, 27 insertions(+), 19 deletions(-) + +diff --git a/fs/smb/server/ksmbd_netlink.h b/fs/smb/server/ksmbd_netlink.h +index 686b321c5a8bb..f4e55199938d5 100644 +--- a/fs/smb/server/ksmbd_netlink.h ++++ b/fs/smb/server/ksmbd_netlink.h +@@ -340,23 +340,24 @@ enum KSMBD_TREE_CONN_STATUS { + /* + * Share config flags. + */ +-#define KSMBD_SHARE_FLAG_INVALID (0) +-#define KSMBD_SHARE_FLAG_AVAILABLE BIT(0) +-#define KSMBD_SHARE_FLAG_BROWSEABLE BIT(1) +-#define KSMBD_SHARE_FLAG_WRITEABLE BIT(2) +-#define KSMBD_SHARE_FLAG_READONLY BIT(3) +-#define KSMBD_SHARE_FLAG_GUEST_OK BIT(4) +-#define KSMBD_SHARE_FLAG_GUEST_ONLY BIT(5) +-#define KSMBD_SHARE_FLAG_STORE_DOS_ATTRS BIT(6) +-#define KSMBD_SHARE_FLAG_OPLOCKS BIT(7) +-#define KSMBD_SHARE_FLAG_PIPE BIT(8) +-#define KSMBD_SHARE_FLAG_HIDE_DOT_FILES BIT(9) +-#define KSMBD_SHARE_FLAG_INHERIT_OWNER BIT(10) +-#define KSMBD_SHARE_FLAG_STREAMS BIT(11) +-#define KSMBD_SHARE_FLAG_FOLLOW_SYMLINKS BIT(12) +-#define KSMBD_SHARE_FLAG_ACL_XATTR BIT(13) +-#define KSMBD_SHARE_FLAG_UPDATE BIT(14) +-#define KSMBD_SHARE_FLAG_CROSSMNT BIT(15) ++#define KSMBD_SHARE_FLAG_INVALID (0) ++#define KSMBD_SHARE_FLAG_AVAILABLE BIT(0) ++#define KSMBD_SHARE_FLAG_BROWSEABLE BIT(1) ++#define KSMBD_SHARE_FLAG_WRITEABLE BIT(2) ++#define KSMBD_SHARE_FLAG_READONLY BIT(3) ++#define KSMBD_SHARE_FLAG_GUEST_OK BIT(4) ++#define KSMBD_SHARE_FLAG_GUEST_ONLY BIT(5) ++#define KSMBD_SHARE_FLAG_STORE_DOS_ATTRS BIT(6) ++#define KSMBD_SHARE_FLAG_OPLOCKS BIT(7) ++#define KSMBD_SHARE_FLAG_PIPE BIT(8) ++#define KSMBD_SHARE_FLAG_HIDE_DOT_FILES BIT(9) ++#define KSMBD_SHARE_FLAG_INHERIT_OWNER BIT(10) ++#define KSMBD_SHARE_FLAG_STREAMS BIT(11) ++#define KSMBD_SHARE_FLAG_FOLLOW_SYMLINKS BIT(12) ++#define KSMBD_SHARE_FLAG_ACL_XATTR BIT(13) ++#define KSMBD_SHARE_FLAG_UPDATE BIT(14) ++#define KSMBD_SHARE_FLAG_CROSSMNT BIT(15) ++#define KSMBD_SHARE_FLAG_CONTINUOUS_AVAILABILITY BIT(16) + + /* + * Tree connect request flags. +diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c +index 75db0e63bcebd..1e536ae277618 100644 +--- a/fs/smb/server/smb2pdu.c ++++ b/fs/smb/server/smb2pdu.c +@@ -1988,7 +1988,12 @@ int smb2_tree_connect(struct ksmbd_work *work) + write_unlock(&sess->tree_conns_lock); + rsp->StructureSize = cpu_to_le16(16); + out_err1: +- rsp->Capabilities = 0; ++ if (server_conf.flags & KSMBD_GLOBAL_FLAG_DURABLE_HANDLE && ++ test_share_config_flag(share, ++ KSMBD_SHARE_FLAG_CONTINUOUS_AVAILABILITY)) ++ rsp->Capabilities = SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY; ++ else ++ rsp->Capabilities = 0; + rsp->Reserved = 0; + /* default manual caching */ + rsp->ShareFlags = SMB2_SHAREFLAG_MANUAL_CACHING; +@@ -3502,7 +3507,9 @@ int smb2_open(struct ksmbd_work *work) + memcpy(fp->client_guid, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE); + + if (dh_info.type == DURABLE_REQ_V2 || dh_info.type == DURABLE_REQ) { +- if (dh_info.type == DURABLE_REQ_V2 && dh_info.persistent) ++ if (dh_info.type == DURABLE_REQ_V2 && dh_info.persistent && ++ test_share_config_flag(work->tcon->share_conf, ++ KSMBD_SHARE_FLAG_CONTINUOUS_AVAILABILITY)) + fp->is_persistent = true; + else + fp->is_durable = true; +-- +2.43.0 + diff --git a/queue-6.6/ksmbd-add-kernel-doc-for-ksmbd_extract_sharename-fun.patch b/queue-6.6/ksmbd-add-kernel-doc-for-ksmbd_extract_sharename-fun.patch new file mode 100644 index 0000000000..3c1a101e5f --- /dev/null +++ b/queue-6.6/ksmbd-add-kernel-doc-for-ksmbd_extract_sharename-fun.patch @@ -0,0 +1,37 @@ +From 52f27f95cf3af5a015b46291848aab09bee68d84 Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Fri, 2 Feb 2024 16:13:17 +0800 +Subject: ksmbd: Add kernel-doc for ksmbd_extract_sharename() function + +From: Yang Li <yang.lee@linux.alibaba.com> + +[ Upstream commit a12bc36032a2f7917068f9ce9eb26d869e54b31a ] + +The ksmbd_extract_sharename() function lacked a complete kernel-doc +comment. This patch adds parameter descriptions and detailed function +behavior to improve code readability and maintainability. + +Signed-off-by: Yang Li <yang.lee@linux.alibaba.com> +Acked-by: Randy Dunlap <rdunlap@infradead.org> +Acked-by: Namjae Jeon <linkinjeon@kernel.org> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/server/misc.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/fs/smb/server/misc.c b/fs/smb/server/misc.c +index 9e8afaa686e3a..1a5faa6f6e7bc 100644 +--- a/fs/smb/server/misc.c ++++ b/fs/smb/server/misc.c +@@ -261,6 +261,7 @@ char *ksmbd_casefold_sharename(struct unicode_map *um, const char *name) + + /** + * ksmbd_extract_sharename() - get share name from tree connect request ++ * @um: pointer to a unicode_map structure for character encoding handling + * @treename: buffer containing tree name and share name + * + * Return: share name on success, otherwise error +-- +2.43.0 + diff --git a/queue-6.6/ksmbd-add-support-for-durable-handles-v1-v2.patch b/queue-6.6/ksmbd-add-support-for-durable-handles-v1-v2.patch new file mode 100644 index 0000000000..270dc60041 --- /dev/null +++ b/queue-6.6/ksmbd-add-support-for-durable-handles-v1-v2.patch @@ -0,0 +1,866 @@ +From b6a5f7e47f0ad13f381173dcc1b4e164c303d1d5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Tue, 12 Mar 2024 14:05:57 +0900 +Subject: ksmbd: add support for durable handles v1/v2 + +From: Namjae Jeon <linkinjeon@kernel.org> + +[ Upstream commit c8efcc786146a951091588e5fa7e3c754850cb3c ] + +Durable file handles allow reopening a file preserved on a short +network outage and transparent client reconnection within a timeout. +i.e. Durable handles aren't necessarily cleaned up when the opening +process terminates. + +This patch add support for durable handle version 1 and 2. + +To prove durable handles work on ksmbd, I have tested this patch with +the following smbtorture tests: + +smb2.durable-open.open-oplock +smb2.durable-open.open-lease +smb2.durable-open.reopen1 +smb2.durable-open.reopen1a +smb2.durable-open.reopen1a-lease +smb2.durable-open.reopen2 +smb2.durable-open.reopen2a +smb2.durable-open.reopen2-lease +smb2.durable-open.reopen2-lease-v2 +smb2.durable-open.reopen3 +smb2.durable-open.reopen4 +smb2.durable-open.delete_on_close2 +smb2.durable-open.file-position +smb2.durable-open.lease +smb2.durable-open.alloc-size +smb2.durable-open.read-only +smb2.durable-v2-open.create-blob +smb2.durable-v2-open.open-oplock +smb2.durable-v2-open.open-lease +smb2.durable-v2-open.reopen1 +smb2.durable-v2-open.reopen1a +smb2.durable-v2-open.reopen1a-lease +smb2.durable-v2-open.reopen2 +smb2.durable-v2-open.reopen2b + +Signed-off-by: Namjae Jeon <linkinjeon@kernel.org> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/server/ksmbd_netlink.h | 1 + + fs/smb/server/mgmt/user_session.c | 1 + + fs/smb/server/oplock.c | 94 +++++++++-- + fs/smb/server/oplock.h | 7 +- + fs/smb/server/smb2ops.c | 6 + + fs/smb/server/smb2pdu.c | 257 +++++++++++++++++++++++++++++- + fs/smb/server/smb2pdu.h | 15 ++ + fs/smb/server/vfs_cache.c | 137 +++++++++++++++- + fs/smb/server/vfs_cache.h | 9 ++ + 9 files changed, 506 insertions(+), 21 deletions(-) + +diff --git a/fs/smb/server/ksmbd_netlink.h b/fs/smb/server/ksmbd_netlink.h +index 4464a62228cf3..686b321c5a8bb 100644 +--- a/fs/smb/server/ksmbd_netlink.h ++++ b/fs/smb/server/ksmbd_netlink.h +@@ -75,6 +75,7 @@ struct ksmbd_heartbeat { + #define KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION BIT(1) + #define KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL BIT(2) + #define KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION_OFF BIT(3) ++#define KSMBD_GLOBAL_FLAG_DURABLE_HANDLE BIT(4) + + /* + * IPC request for ksmbd server startup +diff --git a/fs/smb/server/mgmt/user_session.c b/fs/smb/server/mgmt/user_session.c +index 83074672fe812..aec0a7a124052 100644 +--- a/fs/smb/server/mgmt/user_session.c ++++ b/fs/smb/server/mgmt/user_session.c +@@ -324,6 +324,7 @@ void destroy_previous_session(struct ksmbd_conn *conn, + memcmp(user->passkey, prev_user->passkey, user->passkey_sz)) + goto out; + ++ ksmbd_destroy_file_table(&prev_sess->file_table); + prev_sess->state = SMB2_SESSION_EXPIRED; + out: + up_write(&conn->session_lock); +diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c +index dc729ab980dc0..7bdae2adad228 100644 +--- a/fs/smb/server/oplock.c ++++ b/fs/smb/server/oplock.c +@@ -159,7 +159,8 @@ static struct oplock_info *opinfo_get_list(struct ksmbd_inode *ci) + opinfo = list_first_or_null_rcu(&ci->m_op_list, struct oplock_info, + op_entry); + if (opinfo) { +- if (!atomic_inc_not_zero(&opinfo->refcount)) ++ if (opinfo->conn == NULL || ++ !atomic_inc_not_zero(&opinfo->refcount)) + opinfo = NULL; + else { + atomic_inc(&opinfo->conn->r_count); +@@ -527,7 +528,7 @@ static struct oplock_info *same_client_has_lease(struct ksmbd_inode *ci, + */ + read_lock(&ci->m_lock); + list_for_each_entry(opinfo, &ci->m_op_list, op_entry) { +- if (!opinfo->is_lease) ++ if (!opinfo->is_lease || !opinfo->conn) + continue; + read_unlock(&ci->m_lock); + lease = opinfo->o_lease; +@@ -651,7 +652,7 @@ static void __smb2_oplock_break_noti(struct work_struct *wk) + struct smb2_hdr *rsp_hdr; + struct ksmbd_file *fp; + +- fp = ksmbd_lookup_durable_fd(br_info->fid); ++ fp = ksmbd_lookup_global_fd(br_info->fid); + if (!fp) + goto out; + +@@ -1115,7 +1116,7 @@ void smb_send_parent_lease_break_noti(struct ksmbd_file *fp, + + read_lock(&p_ci->m_lock); + list_for_each_entry(opinfo, &p_ci->m_op_list, op_entry) { +- if (!opinfo->is_lease) ++ if (opinfo->conn == NULL || !opinfo->is_lease) + continue; + + if (opinfo->o_lease->state != SMB2_OPLOCK_LEVEL_NONE && +@@ -1160,7 +1161,7 @@ void smb_lazy_parent_lease_break_close(struct ksmbd_file *fp) + + read_lock(&p_ci->m_lock); + list_for_each_entry(opinfo, &p_ci->m_op_list, op_entry) { +- if (!opinfo->is_lease) ++ if (opinfo->conn == NULL || !opinfo->is_lease) + continue; + + if (opinfo->o_lease->state != SMB2_OPLOCK_LEVEL_NONE) { +@@ -1372,6 +1373,9 @@ void smb_break_all_levII_oplock(struct ksmbd_work *work, struct ksmbd_file *fp, + + rcu_read_lock(); + list_for_each_entry_rcu(brk_op, &ci->m_op_list, op_entry) { ++ if (brk_op->conn == NULL) ++ continue; ++ + if (!atomic_inc_not_zero(&brk_op->refcount)) + continue; + +@@ -1508,11 +1512,10 @@ void create_lease_buf(u8 *rbuf, struct lease *lease) + /** + * parse_lease_state() - parse lease context containted in file open request + * @open_req: buffer containing smb2 file open(create) request +- * @is_dir: whether leasing file is directory + * + * Return: oplock state, -ENOENT if create lease context not found + */ +-struct lease_ctx_info *parse_lease_state(void *open_req, bool is_dir) ++struct lease_ctx_info *parse_lease_state(void *open_req) + { + struct create_context *cc; + struct smb2_create_req *req = (struct smb2_create_req *)open_req; +@@ -1530,12 +1533,7 @@ struct lease_ctx_info *parse_lease_state(void *open_req, bool is_dir) + struct create_lease_v2 *lc = (struct create_lease_v2 *)cc; + + memcpy(lreq->lease_key, lc->lcontext.LeaseKey, SMB2_LEASE_KEY_SIZE); +- if (is_dir) { +- lreq->req_state = lc->lcontext.LeaseState & +- ~SMB2_LEASE_WRITE_CACHING_LE; +- lreq->is_dir = true; +- } else +- lreq->req_state = lc->lcontext.LeaseState; ++ lreq->req_state = lc->lcontext.LeaseState; + lreq->flags = lc->lcontext.LeaseFlags; + lreq->epoch = lc->lcontext.Epoch; + lreq->duration = lc->lcontext.LeaseDuration; +@@ -1659,6 +1657,8 @@ void create_durable_v2_rsp_buf(char *cc, struct ksmbd_file *fp) + buf->Name[3] = 'Q'; + + buf->Timeout = cpu_to_le32(fp->durable_timeout); ++ if (fp->is_persistent) ++ buf->Flags = cpu_to_le32(SMB2_DHANDLE_FLAG_PERSISTENT); + } + + /** +@@ -1826,3 +1826,71 @@ struct oplock_info *lookup_lease_in_table(struct ksmbd_conn *conn, + read_unlock(&lease_list_lock); + return ret_op; + } ++ ++int smb2_check_durable_oplock(struct ksmbd_conn *conn, ++ struct ksmbd_share_config *share, ++ struct ksmbd_file *fp, ++ struct lease_ctx_info *lctx, ++ char *name) ++{ ++ struct oplock_info *opinfo = opinfo_get(fp); ++ int ret = 0; ++ ++ if (!opinfo) ++ return 0; ++ ++ if (opinfo->is_lease == false) { ++ if (lctx) { ++ pr_err("create context include lease\n"); ++ ret = -EBADF; ++ goto out; ++ } ++ ++ if (opinfo->level != SMB2_OPLOCK_LEVEL_BATCH) { ++ pr_err("oplock level is not equal to SMB2_OPLOCK_LEVEL_BATCH\n"); ++ ret = -EBADF; ++ } ++ ++ goto out; ++ } ++ ++ if (memcmp(conn->ClientGUID, fp->client_guid, ++ SMB2_CLIENT_GUID_SIZE)) { ++ ksmbd_debug(SMB, "Client guid of fp is not equal to the one of connction\n"); ++ ret = -EBADF; ++ goto out; ++ } ++ ++ if (!lctx) { ++ ksmbd_debug(SMB, "create context does not include lease\n"); ++ ret = -EBADF; ++ goto out; ++ } ++ ++ if (memcmp(opinfo->o_lease->lease_key, lctx->lease_key, ++ SMB2_LEASE_KEY_SIZE)) { ++ ksmbd_debug(SMB, ++ "lease key of fp does not match lease key in create context\n"); ++ ret = -EBADF; ++ goto out; ++ } ++ ++ if (!(opinfo->o_lease->state & SMB2_LEASE_HANDLE_CACHING_LE)) { ++ ksmbd_debug(SMB, "lease state does not contain SMB2_LEASE_HANDLE_CACHING\n"); ++ ret = -EBADF; ++ goto out; ++ } ++ ++ if (opinfo->o_lease->version != lctx->version) { ++ ksmbd_debug(SMB, ++ "lease version of fp does not match the one in create context\n"); ++ ret = -EBADF; ++ goto out; ++ } ++ ++ if (!ksmbd_inode_pending_delete(fp)) ++ ret = ksmbd_validate_name_reconnect(share, fp, name); ++out: ++ opinfo_put(opinfo); ++ return ret; ++} +diff --git a/fs/smb/server/oplock.h b/fs/smb/server/oplock.h +index 5b93ea9196c01..e9da63f25b206 100644 +--- a/fs/smb/server/oplock.h ++++ b/fs/smb/server/oplock.h +@@ -111,7 +111,7 @@ void opinfo_put(struct oplock_info *opinfo); + + /* Lease related functions */ + void create_lease_buf(u8 *rbuf, struct lease *lease); +-struct lease_ctx_info *parse_lease_state(void *open_req, bool is_dir); ++struct lease_ctx_info *parse_lease_state(void *open_req); + __u8 smb2_map_lease_to_oplock(__le32 lease_state); + int lease_read_to_write(struct oplock_info *opinfo); + +@@ -130,4 +130,9 @@ void destroy_lease_table(struct ksmbd_conn *conn); + void smb_send_parent_lease_break_noti(struct ksmbd_file *fp, + struct lease_ctx_info *lctx); + void smb_lazy_parent_lease_break_close(struct ksmbd_file *fp); ++int smb2_check_durable_oplock(struct ksmbd_conn *conn, ++ struct ksmbd_share_config *share, ++ struct ksmbd_file *fp, ++ struct lease_ctx_info *lctx, ++ char *name); + #endif /* __KSMBD_OPLOCK_H */ +diff --git a/fs/smb/server/smb2ops.c b/fs/smb/server/smb2ops.c +index 8600f32c981a1..606aa3c5189a2 100644 +--- a/fs/smb/server/smb2ops.c ++++ b/fs/smb/server/smb2ops.c +@@ -261,6 +261,9 @@ void init_smb3_02_server(struct ksmbd_conn *conn) + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) + conn->vals->capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL; ++ ++ if (server_conf.flags & KSMBD_GLOBAL_FLAG_DURABLE_HANDLE) ++ conn->vals->capabilities |= SMB2_GLOBAL_CAP_PERSISTENT_HANDLES; + } + + /** +@@ -283,6 +286,9 @@ int init_smb3_11_server(struct ksmbd_conn *conn) + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) + conn->vals->capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL; + ++ if (server_conf.flags & KSMBD_GLOBAL_FLAG_DURABLE_HANDLE) ++ conn->vals->capabilities |= SMB2_GLOBAL_CAP_PERSISTENT_HANDLES; ++ + INIT_LIST_HEAD(&conn->preauth_sess_table); + return 0; + } +diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c +index 61717917db765..218adb3c55816 100644 +--- a/fs/smb/server/smb2pdu.c ++++ b/fs/smb/server/smb2pdu.c +@@ -2622,6 +2622,165 @@ static void ksmbd_acls_fattr(struct smb_fattr *fattr, + } + } + ++enum { ++ DURABLE_RECONN_V2 = 1, ++ DURABLE_RECONN, ++ DURABLE_REQ_V2, ++ DURABLE_REQ, ++}; ++ ++struct durable_info { ++ struct ksmbd_file *fp; ++ unsigned short int type; ++ bool persistent; ++ bool reconnected; ++ unsigned int timeout; ++ char *CreateGuid; ++}; ++ ++static int parse_durable_handle_context(struct ksmbd_work *work, ++ struct smb2_create_req *req, ++ struct lease_ctx_info *lc, ++ struct durable_info *dh_info) ++{ ++ struct ksmbd_conn *conn = work->conn; ++ struct create_context *context; ++ int dh_idx, err = 0; ++ u64 persistent_id = 0; ++ int req_op_level; ++ static const char * const durable_arr[] = {"DH2C", "DHnC", "DH2Q", "DHnQ"}; ++ ++ req_op_level = req->RequestedOplockLevel; ++ for (dh_idx = DURABLE_RECONN_V2; dh_idx <= ARRAY_SIZE(durable_arr); ++ dh_idx++) { ++ context = smb2_find_context_vals(req, durable_arr[dh_idx - 1], 4); ++ if (IS_ERR(context)) { ++ err = PTR_ERR(context); ++ goto out; ++ } ++ if (!context) ++ continue; ++ ++ switch (dh_idx) { ++ case DURABLE_RECONN_V2: ++ { ++ struct create_durable_reconn_v2_req *recon_v2; ++ ++ if (dh_info->type == DURABLE_RECONN || ++ dh_info->type == DURABLE_REQ_V2) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ recon_v2 = (struct create_durable_reconn_v2_req *)context; ++ persistent_id = recon_v2->Fid.PersistentFileId; ++ dh_info->fp = ksmbd_lookup_durable_fd(persistent_id); ++ if (!dh_info->fp) { ++ ksmbd_debug(SMB, "Failed to get durable handle state\n"); ++ err = -EBADF; ++ goto out; ++ } ++ ++ if (memcmp(dh_info->fp->create_guid, recon_v2->CreateGuid, ++ SMB2_CREATE_GUID_SIZE)) { ++ err = -EBADF; ++ ksmbd_put_durable_fd(dh_info->fp); ++ goto out; ++ } ++ ++ dh_info->type = dh_idx; ++ dh_info->reconnected = true; ++ ksmbd_debug(SMB, ++ "reconnect v2 Persistent-id from reconnect = %llu\n", ++ persistent_id); ++ break; ++ } ++ case DURABLE_RECONN: ++ { ++ struct create_durable_reconn_req *recon; ++ ++ if (dh_info->type == DURABLE_RECONN_V2 || ++ dh_info->type == DURABLE_REQ_V2) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ recon = (struct create_durable_reconn_req *)context; ++ persistent_id = recon->Data.Fid.PersistentFileId; ++ dh_info->fp = ksmbd_lookup_durable_fd(persistent_id); ++ if (!dh_info->fp) { ++ ksmbd_debug(SMB, "Failed to get durable handle state\n"); ++ err = -EBADF; ++ goto out; ++ } ++ ++ dh_info->type = dh_idx; ++ dh_info->reconnected = true; ++ ksmbd_debug(SMB, "reconnect Persistent-id from reconnect = %llu\n", ++ persistent_id); ++ break; ++ } ++ case DURABLE_REQ_V2: ++ { ++ struct create_durable_req_v2 *durable_v2_blob; ++ ++ if (dh_info->type == DURABLE_RECONN || ++ dh_info->type == DURABLE_RECONN_V2) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ durable_v2_blob = ++ (struct create_durable_req_v2 *)context; ++ ksmbd_debug(SMB, "Request for durable v2 open\n"); ++ dh_info->fp = ksmbd_lookup_fd_cguid(durable_v2_blob->CreateGuid); ++ if (dh_info->fp) { ++ if (!memcmp(conn->ClientGUID, dh_info->fp->client_guid, ++ SMB2_CLIENT_GUID_SIZE)) { ++ if (!(req->hdr.Flags & SMB2_FLAGS_REPLAY_OPERATION)) { ++ err = -ENOEXEC; ++ goto out; ++ } ++ ++ dh_info->fp->conn = conn; ++ dh_info->reconnected = true; ++ goto out; ++ } ++ } ++ ++ if (((lc && (lc->req_state & SMB2_LEASE_HANDLE_CACHING_LE)) || ++ req_op_level == SMB2_OPLOCK_LEVEL_BATCH)) { ++ dh_info->CreateGuid = ++ durable_v2_blob->CreateGuid; ++ dh_info->persistent = ++ le32_to_cpu(durable_v2_blob->Flags); ++ dh_info->timeout = ++ le32_to_cpu(durable_v2_blob->Timeout); ++ dh_info->type = dh_idx; ++ } ++ break; ++ } ++ case DURABLE_REQ: ++ if (dh_info->type == DURABLE_RECONN) ++ goto out; ++ if (dh_info->type == DURABLE_RECONN_V2 || ++ dh_info->type == DURABLE_REQ_V2) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ if (((lc && (lc->req_state & SMB2_LEASE_HANDLE_CACHING_LE)) || ++ req_op_level == SMB2_OPLOCK_LEVEL_BATCH)) { ++ ksmbd_debug(SMB, "Request for durable open\n"); ++ dh_info->type = dh_idx; ++ } ++ } ++ } ++ ++out: ++ return err; ++} ++ + /** + * smb2_open() - handler for smb file open request + * @work: smb work containing request buffer +@@ -2645,6 +2804,7 @@ int smb2_open(struct ksmbd_work *work) + struct lease_ctx_info *lc = NULL; + struct create_ea_buf_req *ea_buf = NULL; + struct oplock_info *opinfo; ++ struct durable_info dh_info = {0}; + __le32 *next_ptr = NULL; + int req_op_level = 0, open_flags = 0, may_flags = 0, file_info = 0; + int rc = 0; +@@ -2725,6 +2885,49 @@ int smb2_open(struct ksmbd_work *work) + } + } + ++ req_op_level = req->RequestedOplockLevel; ++ ++ if (server_conf.flags & KSMBD_GLOBAL_FLAG_DURABLE_HANDLE && ++ req->CreateContextsOffset) { ++ lc = parse_lease_state(req); ++ rc = parse_durable_handle_context(work, req, lc, &dh_info); ++ if (rc) { ++ ksmbd_debug(SMB, "error parsing durable handle context\n"); ++ goto err_out2; ++ } ++ ++ if (dh_info.reconnected == true) { ++ rc = smb2_check_durable_oplock(conn, share, dh_info.fp, lc, name); ++ if (rc) { ++ ksmbd_put_durable_fd(dh_info.fp); ++ goto err_out2; ++ } ++ ++ rc = ksmbd_reopen_durable_fd(work, dh_info.fp); ++ if (rc) { ++ ksmbd_put_durable_fd(dh_info.fp); ++ goto err_out2; ++ } ++ ++ if (ksmbd_override_fsids(work)) { ++ rc = -ENOMEM; ++ ksmbd_put_durable_fd(dh_info.fp); ++ goto err_out2; ++ } ++ ++ fp = dh_info.fp; ++ file_info = FILE_OPENED; ++ ++ rc = ksmbd_vfs_getattr(&fp->filp->f_path, &stat); ++ if (rc) ++ goto err_out2; ++ ++ ksmbd_put_durable_fd(fp); ++ goto reconnected_fp; ++ } ++ } else if (req_op_level == SMB2_OPLOCK_LEVEL_LEASE) ++ lc = parse_lease_state(req); ++ + if (le32_to_cpu(req->ImpersonationLevel) > le32_to_cpu(IL_DELEGATE)) { + pr_err("Invalid impersonationlevel : 0x%x\n", + le32_to_cpu(req->ImpersonationLevel)); +@@ -3187,10 +3390,6 @@ int smb2_open(struct ksmbd_work *work) + need_truncate = 1; + } + +- req_op_level = req->RequestedOplockLevel; +- if (req_op_level == SMB2_OPLOCK_LEVEL_LEASE) +- lc = parse_lease_state(req, S_ISDIR(file_inode(filp)->i_mode)); +- + share_ret = ksmbd_smb_check_shared_mode(fp->filp, fp); + if (!test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_OPLOCKS) || + (req_op_level == SMB2_OPLOCK_LEVEL_LEASE && +@@ -3201,6 +3400,11 @@ int smb2_open(struct ksmbd_work *work) + } + } else { + if (req_op_level == SMB2_OPLOCK_LEVEL_LEASE) { ++ if (S_ISDIR(file_inode(filp)->i_mode)) { ++ lc->req_state &= ~SMB2_LEASE_WRITE_CACHING_LE; ++ lc->is_dir = true; ++ } ++ + /* + * Compare parent lease using parent key. If there is no + * a lease that has same parent key, Send lease break +@@ -3297,6 +3501,24 @@ int smb2_open(struct ksmbd_work *work) + + memcpy(fp->client_guid, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE); + ++ if (dh_info.type == DURABLE_REQ_V2 || dh_info.type == DURABLE_REQ) { ++ if (dh_info.type == DURABLE_REQ_V2 && dh_info.persistent) ++ fp->is_persistent = true; ++ else ++ fp->is_durable = true; ++ ++ if (dh_info.type == DURABLE_REQ_V2) { ++ memcpy(fp->create_guid, dh_info.CreateGuid, ++ SMB2_CREATE_GUID_SIZE); ++ if (dh_info.timeout) ++ fp->durable_timeout = min(dh_info.timeout, ++ 300000); ++ else ++ fp->durable_timeout = 60; ++ } ++ } ++ ++reconnected_fp: + rsp->StructureSize = cpu_to_le16(89); + rcu_read_lock(); + opinfo = rcu_dereference(fp->f_opinfo); +@@ -3383,6 +3605,33 @@ int smb2_open(struct ksmbd_work *work) + next_off = conn->vals->create_disk_id_size; + } + ++ if (dh_info.type == DURABLE_REQ || dh_info.type == DURABLE_REQ_V2) { ++ struct create_context *durable_ccontext; ++ ++ durable_ccontext = (struct create_context *)(rsp->Buffer + ++ le32_to_cpu(rsp->CreateContextsLength)); ++ contxt_cnt++; ++ if (dh_info.type == DURABLE_REQ) { ++ create_durable_rsp_buf(rsp->Buffer + ++ le32_to_cpu(rsp->CreateContextsLength)); ++ le32_add_cpu(&rsp->CreateContextsLength, ++ conn->vals->create_durable_size); ++ iov_len += conn->vals->create_durable_size; ++ } else { ++ create_durable_v2_rsp_buf(rsp->Buffer + ++ le32_to_cpu(rsp->CreateContextsLength), ++ fp); ++ le32_add_cpu(&rsp->CreateContextsLength, ++ conn->vals->create_durable_v2_size); ++ iov_len += conn->vals->create_durable_v2_size; ++ } ++ ++ if (next_ptr) ++ *next_ptr = cpu_to_le32(next_off); ++ next_ptr = &durable_ccontext->Next; ++ next_off = conn->vals->create_durable_size; ++ } ++ + if (posix_ctxt) { + contxt_cnt++; + create_posix_rsp_buf(rsp->Buffer + +diff --git a/fs/smb/server/smb2pdu.h b/fs/smb/server/smb2pdu.h +index d12cfd3b09278..bd1d2a0e9203a 100644 +--- a/fs/smb/server/smb2pdu.h ++++ b/fs/smb/server/smb2pdu.h +@@ -72,6 +72,18 @@ struct create_durable_req_v2 { + __u8 CreateGuid[16]; + } __packed; + ++struct create_durable_reconn_req { ++ struct create_context ccontext; ++ __u8 Name[8]; ++ union { ++ __u8 Reserved[16]; ++ struct { ++ __u64 PersistentFileId; ++ __u64 VolatileFileId; ++ } Fid; ++ } Data; ++} __packed; ++ + struct create_durable_reconn_v2_req { + struct create_context ccontext; + __u8 Name[8]; +@@ -98,6 +110,9 @@ struct create_durable_rsp { + } Data; + } __packed; + ++/* See MS-SMB2 2.2.13.2.11 */ ++/* Flags */ ++#define SMB2_DHANDLE_FLAG_PERSISTENT 0x00000002 + struct create_durable_v2_rsp { + struct create_context ccontext; + __u8 Name[8]; +diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c +index 4e82ff627d122..030f70700036c 100644 +--- a/fs/smb/server/vfs_cache.c ++++ b/fs/smb/server/vfs_cache.c +@@ -305,7 +305,8 @@ static void __ksmbd_close_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp) + + fd_limit_close(); + __ksmbd_remove_durable_fd(fp); +- __ksmbd_remove_fd(ft, fp); ++ if (ft) ++ __ksmbd_remove_fd(ft, fp); + + close_id_del_oplock(fp); + filp = fp->filp; +@@ -465,11 +466,32 @@ struct ksmbd_file *ksmbd_lookup_fd_slow(struct ksmbd_work *work, u64 id, + return fp; + } + +-struct ksmbd_file *ksmbd_lookup_durable_fd(unsigned long long id) ++struct ksmbd_file *ksmbd_lookup_global_fd(unsigned long long id) + { + return __ksmbd_lookup_fd(&global_ft, id); + } + ++struct ksmbd_file *ksmbd_lookup_durable_fd(unsigned long long id) ++{ ++ struct ksmbd_file *fp; ++ ++ fp = __ksmbd_lookup_fd(&global_ft, id); ++ if (fp && fp->conn) { ++ ksmbd_put_durable_fd(fp); ++ fp = NULL; ++ } ++ ++ return fp; ++} ++ ++void ksmbd_put_durable_fd(struct ksmbd_file *fp) ++{ ++ if (!atomic_dec_and_test(&fp->refcount)) ++ return; ++ ++ __ksmbd_close_fd(NULL, fp); ++} ++ + struct ksmbd_file *ksmbd_lookup_fd_cguid(char *cguid) + { + struct ksmbd_file *fp = NULL; +@@ -639,6 +661,32 @@ __close_file_table_ids(struct ksmbd_file_table *ft, + return num; + } + ++static inline bool is_reconnectable(struct ksmbd_file *fp) ++{ ++ struct oplock_info *opinfo = opinfo_get(fp); ++ bool reconn = false; ++ ++ if (!opinfo) ++ return false; ++ ++ if (opinfo->op_state != OPLOCK_STATE_NONE) { ++ opinfo_put(opinfo); ++ return false; ++ } ++ ++ if (fp->is_resilient || fp->is_persistent) ++ reconn = true; ++ else if (fp->is_durable && opinfo->is_lease && ++ opinfo->o_lease->state & SMB2_LEASE_HANDLE_CACHING_LE) ++ reconn = true; ++ ++ else if (fp->is_durable && opinfo->level == SMB2_OPLOCK_LEVEL_BATCH) ++ reconn = true; ++ ++ opinfo_put(opinfo); ++ return reconn; ++} ++ + static bool tree_conn_fd_check(struct ksmbd_tree_connect *tcon, + struct ksmbd_file *fp) + { +@@ -648,7 +696,28 @@ static bool tree_conn_fd_check(struct ksmbd_tree_connect *tcon, + static bool session_fd_check(struct ksmbd_tree_connect *tcon, + struct ksmbd_file *fp) + { +- return false; ++ struct ksmbd_inode *ci; ++ struct oplock_info *op; ++ struct ksmbd_conn *conn; ++ ++ if (!is_reconnectable(fp)) ++ return false; ++ ++ conn = fp->conn; ++ ci = fp->f_ci; ++ write_lock(&ci->m_lock); ++ list_for_each_entry_rcu(op, &ci->m_op_list, op_entry) { ++ if (op->conn != conn) ++ continue; ++ op->conn = NULL; ++ } ++ write_unlock(&ci->m_lock); ++ ++ fp->conn = NULL; ++ fp->tcon = NULL; ++ fp->volatile_id = KSMBD_NO_FID; ++ ++ return true; + } + + void ksmbd_close_tree_conn_fds(struct ksmbd_work *work) +@@ -687,6 +756,68 @@ void ksmbd_free_global_file_table(void) + ksmbd_destroy_file_table(&global_ft); + } + ++int ksmbd_validate_name_reconnect(struct ksmbd_share_config *share, ++ struct ksmbd_file *fp, char *name) ++{ ++ char *pathname, *ab_pathname; ++ int ret = 0; ++ ++ pathname = kmalloc(PATH_MAX, GFP_KERNEL); ++ if (!pathname) ++ return -EACCES; ++ ++ ab_pathname = d_path(&fp->filp->f_path, pathname, PATH_MAX); ++ if (IS_ERR(ab_pathname)) { ++ kfree(pathname); ++ return -EACCES; ++ } ++ ++ if (name && strcmp(&ab_pathname[share->path_sz + 1], name)) { ++ ksmbd_debug(SMB, "invalid name reconnect %s\n", name); ++ ret = -EINVAL; ++ } ++ ++ kfree(pathname); ++ ++ return ret; ++} ++ ++int ksmbd_reopen_durable_fd(struct ksmbd_work *work, struct ksmbd_file *fp) ++{ ++ struct ksmbd_inode *ci; ++ struct oplock_info *op; ++ ++ if (!fp->is_durable || fp->conn || fp->tcon) { ++ pr_err("Invalid durable fd [%p:%p]\n", fp->conn, fp->tcon); ++ return -EBADF; ++ } ++ ++ if (has_file_id(fp->volatile_id)) { ++ pr_err("Still in use durable fd: %llu\n", fp->volatile_id); ++ return -EBADF; ++ } ++ ++ fp->conn = work->conn; ++ fp->tcon = work->tcon; ++ ++ ci = fp->f_ci; ++ write_lock(&ci->m_lock); ++ list_for_each_entry_rcu(op, &ci->m_op_list, op_entry) { ++ if (op->conn) ++ continue; ++ op->conn = fp->conn; ++ } ++ write_unlock(&ci->m_lock); ++ ++ __open_id(&work->sess->file_table, fp, OPEN_ID_TYPE_VOLATILE_ID); ++ if (!has_file_id(fp->volatile_id)) { ++ fp->conn = NULL; ++ fp->tcon = NULL; ++ return -EBADF; ++ } ++ return 0; ++} ++ + int ksmbd_init_file_table(struct ksmbd_file_table *ft) + { + ft->idr = kzalloc(sizeof(struct idr), GFP_KERNEL); +diff --git a/fs/smb/server/vfs_cache.h b/fs/smb/server/vfs_cache.h +index a528f0cc775ae..ed44fb4e18e79 100644 +--- a/fs/smb/server/vfs_cache.h ++++ b/fs/smb/server/vfs_cache.h +@@ -14,6 +14,7 @@ + #include <linux/workqueue.h> + + #include "vfs.h" ++#include "mgmt/share_config.h" + + /* Windows style file permissions for extended response */ + #define FILE_GENERIC_ALL 0x1F01FF +@@ -106,6 +107,9 @@ struct ksmbd_file { + int dot_dotdot[2]; + unsigned int f_state; + bool reserve_lease_break; ++ bool is_durable; ++ bool is_persistent; ++ bool is_resilient; + }; + + static inline void set_ctx_actor(struct dir_context *ctx, +@@ -141,7 +145,9 @@ struct ksmbd_file *ksmbd_lookup_fd_slow(struct ksmbd_work *work, u64 id, + void ksmbd_fd_put(struct ksmbd_work *work, struct ksmbd_file *fp); + struct ksmbd_inode *ksmbd_inode_lookup_lock(struct dentry *d); + void ksmbd_inode_put(struct ksmbd_inode *ci); ++struct ksmbd_file *ksmbd_lookup_global_fd(unsigned long long id); + struct ksmbd_file *ksmbd_lookup_durable_fd(unsigned long long id); ++void ksmbd_put_durable_fd(struct ksmbd_file *fp); + struct ksmbd_file *ksmbd_lookup_fd_cguid(char *cguid); + struct ksmbd_file *ksmbd_lookup_fd_inode(struct dentry *dentry); + unsigned int ksmbd_open_durable_fd(struct ksmbd_file *fp); +@@ -173,6 +179,9 @@ void ksmbd_set_inode_pending_delete(struct ksmbd_file *fp); + void ksmbd_clear_inode_pending_delete(struct ksmbd_file *fp); + void ksmbd_fd_set_delete_on_close(struct ksmbd_file *fp, + int file_info); ++int ksmbd_reopen_durable_fd(struct ksmbd_work *work, struct ksmbd_file *fp); ++int ksmbd_validate_name_reconnect(struct ksmbd_share_config *share, ++ struct ksmbd_file *fp, char *name); + int ksmbd_init_file_cache(void); + void ksmbd_exit_file_cache(void); + #endif /* __VFS_CACHE_H__ */ +-- +2.43.0 + diff --git a/queue-6.6/ksmbd-auth-fix-most-kernel-doc-warnings.patch b/queue-6.6/ksmbd-auth-fix-most-kernel-doc-warnings.patch new file mode 100644 index 0000000000..5c465cd2b1 --- /dev/null +++ b/queue-6.6/ksmbd-auth-fix-most-kernel-doc-warnings.patch @@ -0,0 +1,98 @@ +From a722195c80fdb7d820d687fe0599239671f80d2f Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Fri, 15 Dec 2023 19:03:57 -0800 +Subject: ksmbd: auth: fix most kernel-doc warnings + +From: Randy Dunlap <rdunlap@infradead.org> + +[ Upstream commit b4068f1ef36d634ef44ece894738284d756d6627 ] + +Fix 12 of 17 kernel-doc warnings in auth.c: + +auth.c:221: warning: Function parameter or member 'conn' not described in 'ksmbd_auth_ntlmv2' +auth.c:221: warning: Function parameter or member 'cryptkey' not described in 'ksmbd_auth_ntlmv2' +auth.c:305: warning: Function parameter or member 'blob_len' not described in 'ksmbd_decode_ntlmssp_auth_blob' +auth.c:305: warning: Function parameter or member 'conn' not described in 'ksmbd_decode_ntlmssp_auth_blob' +auth.c:305: warning: Excess function parameter 'usr' description in 'ksmbd_decode_ntlmssp_auth_blob' +auth.c:385: warning: Function parameter or member 'blob_len' not described in 'ksmbd_decode_ntlmssp_neg_blob' +auth.c:385: warning: Function parameter or member 'conn' not described in 'ksmbd_decode_ntlmssp_neg_blob' +auth.c:385: warning: Excess function parameter 'rsp' description in 'ksmbd_decode_ntlmssp_neg_blob' +auth.c:385: warning: Excess function parameter 'sess' description in 'ksmbd_decode_ntlmssp_neg_blob' +auth.c:413: warning: Function parameter or member 'conn' not described in 'ksmbd_build_ntlmssp_challenge_blob' +auth.c:413: warning: Excess function parameter 'rsp' description in 'ksmbd_build_ntlmssp_challenge_blob' +auth.c:413: warning: Excess function parameter 'sess' description in 'ksmbd_build_ntlmssp_challenge_blob' + +The other 5 are only present when a W=1 kernel build is done or +when scripts/kernel-doc is run with -Wall. They are: + +auth.c:81: warning: No description found for return value of 'ksmbd_gen_sess_key' +auth.c:385: warning: No description found for return value of 'ksmbd_decode_ntlmssp_neg_blob' +auth.c:413: warning: No description found for return value of 'ksmbd_build_ntlmssp_challenge_blob' +auth.c:577: warning: No description found for return value of 'ksmbd_sign_smb2_pdu' +auth.c:628: warning: No description found for return value of 'ksmbd_sign_smb3_pdu' + +Signed-off-by: Randy Dunlap <rdunlap@infradead.org> +Cc: Namjae Jeon <linkinjeon@kernel.org> +Cc: Steve French <sfrench@samba.org> +Cc: Sergey Senozhatsky <senozhatsky@chromium.org> +Cc: Tom Talpey <tom@talpey.com> +Cc: linux-cifs@vger.kernel.org +Acked-by: Namjae Jeon <linkinjeon@kernel.org> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/server/auth.c | 14 ++++++++------ + 1 file changed, 8 insertions(+), 6 deletions(-) + +diff --git a/fs/smb/server/auth.c b/fs/smb/server/auth.c +index 229a6527870d0..09b20039636e7 100644 +--- a/fs/smb/server/auth.c ++++ b/fs/smb/server/auth.c +@@ -208,10 +208,12 @@ static int calc_ntlmv2_hash(struct ksmbd_conn *conn, struct ksmbd_session *sess, + + /** + * ksmbd_auth_ntlmv2() - NTLMv2 authentication handler +- * @sess: session of connection ++ * @conn: connection ++ * @sess: session of connection + * @ntlmv2: NTLMv2 challenge response + * @blen: NTLMv2 blob length + * @domain_name: domain name ++ * @cryptkey: session crypto key + * + * Return: 0 on success, error number on error + */ +@@ -294,7 +296,8 @@ int ksmbd_auth_ntlmv2(struct ksmbd_conn *conn, struct ksmbd_session *sess, + * ksmbd_decode_ntlmssp_auth_blob() - helper function to construct + * authenticate blob + * @authblob: authenticate blob source pointer +- * @usr: user details ++ * @blob_len: length of the @authblob message ++ * @conn: connection + * @sess: session of connection + * + * Return: 0 on success, error number on error +@@ -376,8 +379,8 @@ int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob, + * ksmbd_decode_ntlmssp_neg_blob() - helper function to construct + * negotiate blob + * @negblob: negotiate blob source pointer +- * @rsp: response header pointer to be updated +- * @sess: session of connection ++ * @blob_len: length of the @authblob message ++ * @conn: connection + * + */ + int ksmbd_decode_ntlmssp_neg_blob(struct negotiate_message *negblob, +@@ -403,8 +406,7 @@ int ksmbd_decode_ntlmssp_neg_blob(struct negotiate_message *negblob, + * ksmbd_build_ntlmssp_challenge_blob() - helper function to construct + * challenge blob + * @chgblob: challenge blob source pointer to initialize +- * @rsp: response header pointer to be updated +- * @sess: session of connection ++ * @conn: connection + * + */ + unsigned int +-- +2.43.0 + diff --git a/queue-6.6/ksmbd-fix-possible-null-deref-in-smb_lazy_parent_lea.patch b/queue-6.6/ksmbd-fix-possible-null-deref-in-smb_lazy_parent_lea.patch new file mode 100644 index 0000000000..c7689a2209 --- /dev/null +++ b/queue-6.6/ksmbd-fix-possible-null-deref-in-smb_lazy_parent_lea.patch @@ -0,0 +1,35 @@ +From 07b448a447d2838c0014e19f1957f7f7c98ccc1a Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Wed, 13 Mar 2024 15:11:38 +0100 +Subject: ksmbd: fix possible null-deref in smb_lazy_parent_lease_break_close + +From: Marios Makassikis <mmakassikis@freebox.fr> + +[ Upstream commit 5fb282ba4fef8985a5acf2b32681f2ec07732561 ] + +rcu_dereference can return NULL, so make sure we check against that. + +Signed-off-by: Marios Makassikis <mmakassikis@freebox.fr> +Acked-by: Namjae Jeon <linkinjeon@kernel.org> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/server/oplock.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c +index 7bdae2adad228..58bafe23ded9a 100644 +--- a/fs/smb/server/oplock.c ++++ b/fs/smb/server/oplock.c +@@ -1152,7 +1152,7 @@ void smb_lazy_parent_lease_break_close(struct ksmbd_file *fp) + opinfo = rcu_dereference(fp->f_opinfo); + rcu_read_unlock(); + +- if (!opinfo->is_lease || opinfo->o_lease->version != 2) ++ if (!opinfo || !opinfo->is_lease || opinfo->o_lease->version != 2) + return; + + p_ci = ksmbd_inode_lookup_lock(fp->filp->f_path.dentry->d_parent); +-- +2.43.0 + diff --git a/queue-6.6/ksmbd-fix-potencial-out-of-bounds-when-buffer-offset.patch b/queue-6.6/ksmbd-fix-potencial-out-of-bounds-when-buffer-offset.patch new file mode 100644 index 0000000000..dbc3eb68fd --- /dev/null +++ b/queue-6.6/ksmbd-fix-potencial-out-of-bounds-when-buffer-offset.patch @@ -0,0 +1,316 @@ +From 3f41596bd66a08ee489798a57c56243c01fe640c Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Tue, 19 Mar 2024 08:40:48 +0900 +Subject: ksmbd: fix potencial out-of-bounds when buffer offset is invalid + +From: Namjae Jeon <linkinjeon@kernel.org> + +[ Upstream commit c6cd2e8d2d9aa7ee35b1fa6a668e32a22a9753da ] + +I found potencial out-of-bounds when buffer offset fields of a few requests +is invalid. This patch set the minimum value of buffer offset field to +->Buffer offset to validate buffer length. + +Cc: stable@vger.kernel.org +Signed-off-by: Namjae Jeon <linkinjeon@kernel.org> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/server/smb2misc.c | 23 +++++++++++++------ + fs/smb/server/smb2pdu.c | 48 ++++++++++++++++++++++------------------ + 2 files changed, 42 insertions(+), 29 deletions(-) + +diff --git a/fs/smb/server/smb2misc.c b/fs/smb/server/smb2misc.c +index 7c872ffb4b0a9..727cb49926ee5 100644 +--- a/fs/smb/server/smb2misc.c ++++ b/fs/smb/server/smb2misc.c +@@ -101,7 +101,9 @@ static int smb2_get_data_area_len(unsigned int *off, unsigned int *len, + *len = le16_to_cpu(((struct smb2_sess_setup_req *)hdr)->SecurityBufferLength); + break; + case SMB2_TREE_CONNECT: +- *off = le16_to_cpu(((struct smb2_tree_connect_req *)hdr)->PathOffset); ++ *off = max_t(unsigned short int, ++ le16_to_cpu(((struct smb2_tree_connect_req *)hdr)->PathOffset), ++ offsetof(struct smb2_tree_connect_req, Buffer)); + *len = le16_to_cpu(((struct smb2_tree_connect_req *)hdr)->PathLength); + break; + case SMB2_CREATE: +@@ -110,7 +112,6 @@ static int smb2_get_data_area_len(unsigned int *off, unsigned int *len, + max_t(unsigned short int, + le16_to_cpu(((struct smb2_create_req *)hdr)->NameOffset), + offsetof(struct smb2_create_req, Buffer)); +- + unsigned short int name_len = + le16_to_cpu(((struct smb2_create_req *)hdr)->NameLength); + +@@ -131,11 +132,15 @@ static int smb2_get_data_area_len(unsigned int *off, unsigned int *len, + break; + } + case SMB2_QUERY_INFO: +- *off = le16_to_cpu(((struct smb2_query_info_req *)hdr)->InputBufferOffset); ++ *off = max_t(unsigned int, ++ le16_to_cpu(((struct smb2_query_info_req *)hdr)->InputBufferOffset), ++ offsetof(struct smb2_query_info_req, Buffer)); + *len = le32_to_cpu(((struct smb2_query_info_req *)hdr)->InputBufferLength); + break; + case SMB2_SET_INFO: +- *off = le16_to_cpu(((struct smb2_set_info_req *)hdr)->BufferOffset); ++ *off = max_t(unsigned int, ++ le16_to_cpu(((struct smb2_set_info_req *)hdr)->BufferOffset), ++ offsetof(struct smb2_set_info_req, Buffer)); + *len = le32_to_cpu(((struct smb2_set_info_req *)hdr)->BufferLength); + break; + case SMB2_READ: +@@ -145,7 +150,7 @@ static int smb2_get_data_area_len(unsigned int *off, unsigned int *len, + case SMB2_WRITE: + if (((struct smb2_write_req *)hdr)->DataOffset || + ((struct smb2_write_req *)hdr)->Length) { +- *off = max_t(unsigned int, ++ *off = max_t(unsigned short int, + le16_to_cpu(((struct smb2_write_req *)hdr)->DataOffset), + offsetof(struct smb2_write_req, Buffer)); + *len = le32_to_cpu(((struct smb2_write_req *)hdr)->Length); +@@ -156,7 +161,9 @@ static int smb2_get_data_area_len(unsigned int *off, unsigned int *len, + *len = le16_to_cpu(((struct smb2_write_req *)hdr)->WriteChannelInfoLength); + break; + case SMB2_QUERY_DIRECTORY: +- *off = le16_to_cpu(((struct smb2_query_directory_req *)hdr)->FileNameOffset); ++ *off = max_t(unsigned short int, ++ le16_to_cpu(((struct smb2_query_directory_req *)hdr)->FileNameOffset), ++ offsetof(struct smb2_query_directory_req, Buffer)); + *len = le16_to_cpu(((struct smb2_query_directory_req *)hdr)->FileNameLength); + break; + case SMB2_LOCK: +@@ -171,7 +178,9 @@ static int smb2_get_data_area_len(unsigned int *off, unsigned int *len, + break; + } + case SMB2_IOCTL: +- *off = le32_to_cpu(((struct smb2_ioctl_req *)hdr)->InputOffset); ++ *off = max_t(unsigned int, ++ le32_to_cpu(((struct smb2_ioctl_req *)hdr)->InputOffset), ++ offsetof(struct smb2_ioctl_req, Buffer)); + *len = le32_to_cpu(((struct smb2_ioctl_req *)hdr)->InputCount); + break; + default: +diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c +index 218adb3c55816..75db0e63bcebd 100644 +--- a/fs/smb/server/smb2pdu.c ++++ b/fs/smb/server/smb2pdu.c +@@ -1931,7 +1931,7 @@ int smb2_tree_connect(struct ksmbd_work *work) + + WORK_BUFFERS(work, req, rsp); + +- treename = smb_strndup_from_utf16(req->Buffer, ++ treename = smb_strndup_from_utf16((char *)req + le16_to_cpu(req->PathOffset), + le16_to_cpu(req->PathLength), true, + conn->local_nls); + if (IS_ERR(treename)) { +@@ -2844,7 +2844,7 @@ int smb2_open(struct ksmbd_work *work) + goto err_out2; + } + +- name = smb2_get_name(req->Buffer, ++ name = smb2_get_name((char *)req + le16_to_cpu(req->NameOffset), + le16_to_cpu(req->NameLength), + work->conn->local_nls); + if (IS_ERR(name)) { +@@ -4309,7 +4309,7 @@ int smb2_query_dir(struct ksmbd_work *work) + } + + srch_flag = req->Flags; +- srch_ptr = smb_strndup_from_utf16(req->Buffer, ++ srch_ptr = smb_strndup_from_utf16((char *)req + le16_to_cpu(req->FileNameOffset), + le16_to_cpu(req->FileNameLength), 1, + conn->local_nls); + if (IS_ERR(srch_ptr)) { +@@ -4569,7 +4569,8 @@ static int smb2_get_ea(struct ksmbd_work *work, struct ksmbd_file *fp, + sizeof(struct smb2_ea_info_req)) + return -EINVAL; + +- ea_req = (struct smb2_ea_info_req *)req->Buffer; ++ ea_req = (struct smb2_ea_info_req *)((char *)req + ++ le16_to_cpu(req->InputBufferOffset)); + } else { + /* need to send all EAs, if no specific EA is requested*/ + if (le32_to_cpu(req->Flags) & SL_RETURN_SINGLE_ENTRY) +@@ -6216,6 +6217,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp, + struct ksmbd_share_config *share) + { + unsigned int buf_len = le32_to_cpu(req->BufferLength); ++ char *buffer = (char *)req + le16_to_cpu(req->BufferOffset); + + switch (req->FileInfoClass) { + case FILE_BASIC_INFORMATION: +@@ -6223,7 +6225,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp, + if (buf_len < sizeof(struct smb2_file_basic_info)) + return -EINVAL; + +- return set_file_basic_info(fp, (struct smb2_file_basic_info *)req->Buffer, share); ++ return set_file_basic_info(fp, (struct smb2_file_basic_info *)buffer, share); + } + case FILE_ALLOCATION_INFORMATION: + { +@@ -6231,7 +6233,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp, + return -EINVAL; + + return set_file_allocation_info(work, fp, +- (struct smb2_file_alloc_info *)req->Buffer); ++ (struct smb2_file_alloc_info *)buffer); + } + case FILE_END_OF_FILE_INFORMATION: + { +@@ -6239,7 +6241,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp, + return -EINVAL; + + return set_end_of_file_info(work, fp, +- (struct smb2_file_eof_info *)req->Buffer); ++ (struct smb2_file_eof_info *)buffer); + } + case FILE_RENAME_INFORMATION: + { +@@ -6247,7 +6249,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp, + return -EINVAL; + + return set_rename_info(work, fp, +- (struct smb2_file_rename_info *)req->Buffer, ++ (struct smb2_file_rename_info *)buffer, + buf_len); + } + case FILE_LINK_INFORMATION: +@@ -6256,7 +6258,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp, + return -EINVAL; + + return smb2_create_link(work, work->tcon->share_conf, +- (struct smb2_file_link_info *)req->Buffer, ++ (struct smb2_file_link_info *)buffer, + buf_len, fp->filp, + work->conn->local_nls); + } +@@ -6266,7 +6268,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp, + return -EINVAL; + + return set_file_disposition_info(fp, +- (struct smb2_file_disposition_info *)req->Buffer); ++ (struct smb2_file_disposition_info *)buffer); + } + case FILE_FULL_EA_INFORMATION: + { +@@ -6279,7 +6281,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp, + if (buf_len < sizeof(struct smb2_ea_info)) + return -EINVAL; + +- return smb2_set_ea((struct smb2_ea_info *)req->Buffer, ++ return smb2_set_ea((struct smb2_ea_info *)buffer, + buf_len, &fp->filp->f_path, true); + } + case FILE_POSITION_INFORMATION: +@@ -6287,14 +6289,14 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp, + if (buf_len < sizeof(struct smb2_file_pos_info)) + return -EINVAL; + +- return set_file_position_info(fp, (struct smb2_file_pos_info *)req->Buffer); ++ return set_file_position_info(fp, (struct smb2_file_pos_info *)buffer); + } + case FILE_MODE_INFORMATION: + { + if (buf_len < sizeof(struct smb2_file_mode_info)) + return -EINVAL; + +- return set_file_mode_info(fp, (struct smb2_file_mode_info *)req->Buffer); ++ return set_file_mode_info(fp, (struct smb2_file_mode_info *)buffer); + } + } + +@@ -6375,7 +6377,7 @@ int smb2_set_info(struct ksmbd_work *work) + } + rc = smb2_set_info_sec(fp, + le32_to_cpu(req->AdditionalInformation), +- req->Buffer, ++ (char *)req + le16_to_cpu(req->BufferOffset), + le32_to_cpu(req->BufferLength)); + ksmbd_revert_fsids(work); + break; +@@ -7821,7 +7823,7 @@ static int fsctl_pipe_transceive(struct ksmbd_work *work, u64 id, + struct smb2_ioctl_rsp *rsp) + { + struct ksmbd_rpc_command *rpc_resp; +- char *data_buf = (char *)&req->Buffer[0]; ++ char *data_buf = (char *)req + le32_to_cpu(req->InputOffset); + int nbytes = 0; + + rpc_resp = ksmbd_rpc_ioctl(work->sess, id, data_buf, +@@ -7934,6 +7936,7 @@ int smb2_ioctl(struct ksmbd_work *work) + u64 id = KSMBD_NO_FID; + struct ksmbd_conn *conn = work->conn; + int ret = 0; ++ char *buffer; + + if (work->next_smb2_rcv_hdr_off) { + req = ksmbd_req_buf_next(work); +@@ -7956,6 +7959,8 @@ int smb2_ioctl(struct ksmbd_work *work) + goto out; + } + ++ buffer = (char *)req + le32_to_cpu(req->InputOffset); ++ + cnt_code = le32_to_cpu(req->CtlCode); + ret = smb2_calc_max_out_buf_len(work, 48, + le32_to_cpu(req->MaxOutputResponse)); +@@ -8013,7 +8018,7 @@ int smb2_ioctl(struct ksmbd_work *work) + } + + ret = fsctl_validate_negotiate_info(conn, +- (struct validate_negotiate_info_req *)&req->Buffer[0], ++ (struct validate_negotiate_info_req *)buffer, + (struct validate_negotiate_info_rsp *)&rsp->Buffer[0], + in_buf_len); + if (ret < 0) +@@ -8066,7 +8071,7 @@ int smb2_ioctl(struct ksmbd_work *work) + rsp->VolatileFileId = req->VolatileFileId; + rsp->PersistentFileId = req->PersistentFileId; + fsctl_copychunk(work, +- (struct copychunk_ioctl_req *)&req->Buffer[0], ++ (struct copychunk_ioctl_req *)buffer, + le32_to_cpu(req->CtlCode), + le32_to_cpu(req->InputCount), + req->VolatileFileId, +@@ -8079,8 +8084,7 @@ int smb2_ioctl(struct ksmbd_work *work) + goto out; + } + +- ret = fsctl_set_sparse(work, id, +- (struct file_sparse *)&req->Buffer[0]); ++ ret = fsctl_set_sparse(work, id, (struct file_sparse *)buffer); + if (ret < 0) + goto out; + break; +@@ -8103,7 +8107,7 @@ int smb2_ioctl(struct ksmbd_work *work) + } + + zero_data = +- (struct file_zero_data_information *)&req->Buffer[0]; ++ (struct file_zero_data_information *)buffer; + + off = le64_to_cpu(zero_data->FileOffset); + bfz = le64_to_cpu(zero_data->BeyondFinalZero); +@@ -8134,7 +8138,7 @@ int smb2_ioctl(struct ksmbd_work *work) + } + + ret = fsctl_query_allocated_ranges(work, id, +- (struct file_allocated_range_buffer *)&req->Buffer[0], ++ (struct file_allocated_range_buffer *)buffer, + (struct file_allocated_range_buffer *)&rsp->Buffer[0], + out_buf_len / + sizeof(struct file_allocated_range_buffer), &nbytes); +@@ -8178,7 +8182,7 @@ int smb2_ioctl(struct ksmbd_work *work) + goto out; + } + +- dup_ext = (struct duplicate_extents_to_file *)&req->Buffer[0]; ++ dup_ext = (struct duplicate_extents_to_file *)buffer; + + fp_in = ksmbd_lookup_fd_slow(work, dup_ext->VolatileFileHandle, + dup_ext->PersistentFileHandle); +-- +2.43.0 + diff --git a/queue-6.6/ksmbd-fix-slab-out-of-bounds-in-smb_strndup_from_utf.patch b/queue-6.6/ksmbd-fix-slab-out-of-bounds-in-smb_strndup_from_utf.patch new file mode 100644 index 0000000000..09d96965c5 --- /dev/null +++ b/queue-6.6/ksmbd-fix-slab-out-of-bounds-in-smb_strndup_from_utf.patch @@ -0,0 +1,44 @@ +From 1d1854317e5b7cf9f6498d4a7dfcb3141e0c74a5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Sat, 16 Mar 2024 23:36:36 +0900 +Subject: ksmbd: fix slab-out-of-bounds in smb_strndup_from_utf16() + +From: Namjae Jeon <linkinjeon@kernel.org> + +[ Upstream commit d10c77873ba1e9e6b91905018e29e196fd5f863d ] + +If ->NameOffset/Length is bigger than ->CreateContextsOffset/Length, +ksmbd_check_message doesn't validate request buffer it correctly. +So slab-out-of-bounds warning from calling smb_strndup_from_utf16() +in smb2_open() could happen. If ->NameLength is non-zero, Set the larger +of the two sums (Name and CreateContext size) as the offset and length of +the data area. + +Reported-by: Yang Chaoming <lometsj@live.com> +Cc: stable@vger.kernel.org +Signed-off-by: Namjae Jeon <linkinjeon@kernel.org> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/server/smb2misc.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/fs/smb/server/smb2misc.c b/fs/smb/server/smb2misc.c +index 03dded29a9804..7c872ffb4b0a9 100644 +--- a/fs/smb/server/smb2misc.c ++++ b/fs/smb/server/smb2misc.c +@@ -107,7 +107,10 @@ static int smb2_get_data_area_len(unsigned int *off, unsigned int *len, + case SMB2_CREATE: + { + unsigned short int name_off = +- le16_to_cpu(((struct smb2_create_req *)hdr)->NameOffset); ++ max_t(unsigned short int, ++ le16_to_cpu(((struct smb2_create_req *)hdr)->NameOffset), ++ offsetof(struct smb2_create_req, Buffer)); ++ + unsigned short int name_len = + le16_to_cpu(((struct smb2_create_req *)hdr)->NameLength); + +-- +2.43.0 + diff --git a/queue-6.6/ksmbd-fix-spelling-mistake-connction-connection.patch b/queue-6.6/ksmbd-fix-spelling-mistake-connction-connection.patch new file mode 100644 index 0000000000..15d8854879 --- /dev/null +++ b/queue-6.6/ksmbd-fix-spelling-mistake-connction-connection.patch @@ -0,0 +1,35 @@ +From 91048b84ab8a5cb49ebe92ddd58b1ccc38f39aab Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Wed, 13 Mar 2024 09:16:16 +0000 +Subject: ksmbd: Fix spelling mistake "connction" -> "connection" + +From: Colin Ian King <colin.i.king@gmail.com> + +[ Upstream commit e758fa6956cbc873e4819ec3dd97cfd05a4c147e ] + +There is a spelling mistake in a ksmbd_debug debug message. Fix it. + +Signed-off-by: Colin Ian King <colin.i.king@gmail.com> +Acked-by: Namjae Jeon <linkinjeon@kernel.org> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/server/oplock.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c +index 58bafe23ded9a..b7adb6549aa0f 100644 +--- a/fs/smb/server/oplock.c ++++ b/fs/smb/server/oplock.c +@@ -1856,7 +1856,7 @@ int smb2_check_durable_oplock(struct ksmbd_conn *conn, + + if (memcmp(conn->ClientGUID, fp->client_guid, + SMB2_CLIENT_GUID_SIZE)) { +- ksmbd_debug(SMB, "Client guid of fp is not equal to the one of connction\n"); ++ ksmbd_debug(SMB, "Client guid of fp is not equal to the one of connection\n"); + ret = -EBADF; + goto out; + } +-- +2.43.0 + diff --git a/queue-6.6/ksmbd-mark-smb2_session_expired-to-session-when-dest.patch b/queue-6.6/ksmbd-mark-smb2_session_expired-to-session-when-dest.patch new file mode 100644 index 0000000000..6bfaca9e6c --- /dev/null +++ b/queue-6.6/ksmbd-mark-smb2_session_expired-to-session-when-dest.patch @@ -0,0 +1,124 @@ +From bc000033032d72cf5eb0ce62198318dd427d1dc6 Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Sun, 10 Mar 2024 19:30:51 +0900 +Subject: ksmbd: mark SMB2_SESSION_EXPIRED to session when destroying previous + session + +From: Namjae Jeon <linkinjeon@kernel.org> + +[ Upstream commit fa9415d4024fd0c58d24a4ad4f1826fb8bfcc4aa ] + +Currently ksmbd exit connection as well destroying previous session. +When testing durable handle feaure, I found that +destroy_previous_session() should destroy only session, i.e. the +connection should be still alive. This patch mark SMB2_SESSION_EXPIRED +on the previous session to be destroyed later and not used anymore. + +Signed-off-by: Namjae Jeon <linkinjeon@kernel.org> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/server/mgmt/user_session.c | 27 ++++++++++++++++++++++++++- + fs/smb/server/mgmt/user_session.h | 3 +++ + fs/smb/server/smb2pdu.c | 24 ------------------------ + 3 files changed, 29 insertions(+), 25 deletions(-) + +diff --git a/fs/smb/server/mgmt/user_session.c b/fs/smb/server/mgmt/user_session.c +index 15f68ee050894..83074672fe812 100644 +--- a/fs/smb/server/mgmt/user_session.c ++++ b/fs/smb/server/mgmt/user_session.c +@@ -156,7 +156,7 @@ void ksmbd_session_destroy(struct ksmbd_session *sess) + kfree(sess); + } + +-static struct ksmbd_session *__session_lookup(unsigned long long id) ++struct ksmbd_session *__session_lookup(unsigned long long id) + { + struct ksmbd_session *sess; + +@@ -305,6 +305,31 @@ struct preauth_session *ksmbd_preauth_session_alloc(struct ksmbd_conn *conn, + return sess; + } + ++void destroy_previous_session(struct ksmbd_conn *conn, ++ struct ksmbd_user *user, u64 id) ++{ ++ struct ksmbd_session *prev_sess; ++ struct ksmbd_user *prev_user; ++ ++ down_write(&sessions_table_lock); ++ down_write(&conn->session_lock); ++ prev_sess = __session_lookup(id); ++ if (!prev_sess || prev_sess->state == SMB2_SESSION_EXPIRED) ++ goto out; ++ ++ prev_user = prev_sess->user; ++ if (!prev_user || ++ strcmp(user->name, prev_user->name) || ++ user->passkey_sz != prev_user->passkey_sz || ++ memcmp(user->passkey, prev_user->passkey, user->passkey_sz)) ++ goto out; ++ ++ prev_sess->state = SMB2_SESSION_EXPIRED; ++out: ++ up_write(&conn->session_lock); ++ up_write(&sessions_table_lock); ++} ++ + static bool ksmbd_preauth_session_id_match(struct preauth_session *sess, + unsigned long long id) + { +diff --git a/fs/smb/server/mgmt/user_session.h b/fs/smb/server/mgmt/user_session.h +index 63cb08fffde84..dc9fded2cd437 100644 +--- a/fs/smb/server/mgmt/user_session.h ++++ b/fs/smb/server/mgmt/user_session.h +@@ -88,8 +88,11 @@ struct ksmbd_session *ksmbd_session_lookup(struct ksmbd_conn *conn, + int ksmbd_session_register(struct ksmbd_conn *conn, + struct ksmbd_session *sess); + void ksmbd_sessions_deregister(struct ksmbd_conn *conn); ++struct ksmbd_session *__session_lookup(unsigned long long id); + struct ksmbd_session *ksmbd_session_lookup_all(struct ksmbd_conn *conn, + unsigned long long id); ++void destroy_previous_session(struct ksmbd_conn *conn, ++ struct ksmbd_user *user, u64 id); + struct preauth_session *ksmbd_preauth_session_alloc(struct ksmbd_conn *conn, + u64 sess_id); + struct preauth_session *ksmbd_preauth_session_lookup(struct ksmbd_conn *conn, +diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c +index fb9eea631069e..61717917db765 100644 +--- a/fs/smb/server/smb2pdu.c ++++ b/fs/smb/server/smb2pdu.c +@@ -611,30 +611,6 @@ int smb2_check_user_session(struct ksmbd_work *work) + return -ENOENT; + } + +-static void destroy_previous_session(struct ksmbd_conn *conn, +- struct ksmbd_user *user, u64 id) +-{ +- struct ksmbd_session *prev_sess = ksmbd_session_lookup_slowpath(id); +- struct ksmbd_user *prev_user; +- struct channel *chann; +- long index; +- +- if (!prev_sess) +- return; +- +- prev_user = prev_sess->user; +- +- if (!prev_user || +- strcmp(user->name, prev_user->name) || +- user->passkey_sz != prev_user->passkey_sz || +- memcmp(user->passkey, prev_user->passkey, user->passkey_sz)) +- return; +- +- prev_sess->state = SMB2_SESSION_EXPIRED; +- xa_for_each(&prev_sess->ksmbd_chann_list, index, chann) +- ksmbd_conn_set_exiting(chann->conn); +-} +- + /** + * smb2_get_name() - get filename string from on the wire smb format + * @src: source buffer +-- +2.43.0 + diff --git a/queue-6.6/ksmbd-vfs-fix-all-kernel-doc-warnings.patch b/queue-6.6/ksmbd-vfs-fix-all-kernel-doc-warnings.patch new file mode 100644 index 0000000000..2b44bf8de5 --- /dev/null +++ b/queue-6.6/ksmbd-vfs-fix-all-kernel-doc-warnings.patch @@ -0,0 +1,176 @@ +From b5a7f8ce7f3264cc3e322fa89cecec72b86ba2dc Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Fri, 15 Dec 2023 19:28:14 -0800 +Subject: ksmbd: vfs: fix all kernel-doc warnings + +From: Randy Dunlap <rdunlap@infradead.org> + +[ Upstream commit 8d99c1131d9d03053b7b1e1245b8f6e6846d9c69 ] + +Fix all kernel-doc warnings in vfs.c: + +vfs.c:54: warning: Function parameter or member 'parent' not described in 'ksmbd_vfs_lock_parent' +vfs.c:54: warning: Function parameter or member 'child' not described in 'ksmbd_vfs_lock_parent' +vfs.c:54: warning: No description found for return value of 'ksmbd_vfs_lock_parent' +vfs.c:372: warning: Function parameter or member 'fp' not described in 'ksmbd_vfs_read' +vfs.c:372: warning: Excess function parameter 'fid' description in 'ksmbd_vfs_read' +vfs.c:489: warning: Function parameter or member 'fp' not described in 'ksmbd_vfs_write' +vfs.c:489: warning: Excess function parameter 'fid' description in 'ksmbd_vfs_write' +vfs.c:555: warning: Function parameter or member 'path' not described in 'ksmbd_vfs_getattr' +vfs.c:555: warning: Function parameter or member 'stat' not described in 'ksmbd_vfs_getattr' +vfs.c:555: warning: Excess function parameter 'work' description in 'ksmbd_vfs_getattr' +vfs.c:555: warning: Excess function parameter 'fid' description in 'ksmbd_vfs_getattr' +vfs.c:555: warning: Excess function parameter 'attrs' description in 'ksmbd_vfs_getattr' +vfs.c:572: warning: Function parameter or member 'p_id' not described in 'ksmbd_vfs_fsync' +vfs.c:595: warning: Function parameter or member 'work' not described in 'ksmbd_vfs_remove_file' +vfs.c:595: warning: Function parameter or member 'path' not described in 'ksmbd_vfs_remove_file' +vfs.c:595: warning: Excess function parameter 'name' description in 'ksmbd_vfs_remove_file' +vfs.c:633: warning: Function parameter or member 'work' not described in 'ksmbd_vfs_link' +vfs.c:805: warning: Function parameter or member 'fp' not described in 'ksmbd_vfs_truncate' +vfs.c:805: warning: Excess function parameter 'fid' description in 'ksmbd_vfs_truncate' +vfs.c:846: warning: Excess function parameter 'size' description in 'ksmbd_vfs_listxattr' +vfs.c:953: warning: Function parameter or member 'option' not described in 'ksmbd_vfs_set_fadvise' +vfs.c:953: warning: Excess function parameter 'options' description in 'ksmbd_vfs_set_fadvise' +vfs.c:1167: warning: Function parameter or member 'um' not described in 'ksmbd_vfs_lookup_in_dir' +vfs.c:1203: warning: Function parameter or member 'work' not described in 'ksmbd_vfs_kern_path_locked' +vfs.c:1641: warning: No description found for return value of 'ksmbd_vfs_init_kstat' + +Signed-off-by: Randy Dunlap <rdunlap@infradead.org> +Cc: Namjae Jeon <linkinjeon@kernel.org> +Cc: Steve French <sfrench@samba.org> +Cc: Sergey Senozhatsky <senozhatsky@chromium.org> +Cc: Tom Talpey <tom@talpey.com> +Cc: linux-cifs@vger.kernel.org +Acked-by: Namjae Jeon <linkinjeon@kernel.org> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/server/vfs.c | 28 ++++++++++++++++++---------- + 1 file changed, 18 insertions(+), 10 deletions(-) + +diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c +index 626406b0cf4ac..2558119969359 100644 +--- a/fs/smb/server/vfs.c ++++ b/fs/smb/server/vfs.c +@@ -49,6 +49,10 @@ static void ksmbd_vfs_inherit_owner(struct ksmbd_work *work, + + /** + * ksmbd_vfs_lock_parent() - lock parent dentry if it is stable ++ * @parent: parent dentry ++ * @child: child dentry ++ * ++ * Returns: %0 on success, %-ENOENT if the parent dentry is not stable + */ + int ksmbd_vfs_lock_parent(struct dentry *parent, struct dentry *child) + { +@@ -360,7 +364,7 @@ static int check_lock_range(struct file *filp, loff_t start, loff_t end, + /** + * ksmbd_vfs_read() - vfs helper for smb file read + * @work: smb work +- * @fid: file id of open file ++ * @fp: ksmbd file pointer + * @count: read byte count + * @pos: file pos + * @rbuf: read data buffer +@@ -474,7 +478,7 @@ static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos, + /** + * ksmbd_vfs_write() - vfs helper for smb file write + * @work: work +- * @fid: file id of open file ++ * @fp: ksmbd file pointer + * @buf: buf containing data for writing + * @count: read byte count + * @pos: file pos +@@ -545,10 +549,8 @@ int ksmbd_vfs_write(struct ksmbd_work *work, struct ksmbd_file *fp, + + /** + * ksmbd_vfs_getattr() - vfs helper for smb getattr +- * @work: work +- * @fid: file id of open file +- * @attrs: inode attributes +- * ++ * @path: path of dentry ++ * @stat: pointer to returned kernel stat structure + * Return: 0 on success, otherwise error + */ + int ksmbd_vfs_getattr(const struct path *path, struct kstat *stat) +@@ -565,6 +567,7 @@ int ksmbd_vfs_getattr(const struct path *path, struct kstat *stat) + * ksmbd_vfs_fsync() - vfs helper for smb fsync + * @work: work + * @fid: file id of open file ++ * @p_id: persistent file id + * + * Return: 0 on success, otherwise error + */ +@@ -587,7 +590,8 @@ int ksmbd_vfs_fsync(struct ksmbd_work *work, u64 fid, u64 p_id) + + /** + * ksmbd_vfs_remove_file() - vfs helper for smb rmdir or unlink +- * @name: directory or file name that is relative to share ++ * @work: work ++ * @path: path of dentry + * + * Return: 0 on success, otherwise error + */ +@@ -623,6 +627,7 @@ int ksmbd_vfs_remove_file(struct ksmbd_work *work, const struct path *path) + + /** + * ksmbd_vfs_link() - vfs helper for creating smb hardlink ++ * @work: work + * @oldname: source file name + * @newname: hardlink name that is relative to share + * +@@ -800,7 +805,7 @@ int ksmbd_vfs_rename(struct ksmbd_work *work, const struct path *old_path, + /** + * ksmbd_vfs_truncate() - vfs helper for smb file truncate + * @work: work +- * @fid: file id of old file ++ * @fp: ksmbd file pointer + * @size: truncate to given size + * + * Return: 0 on success, otherwise error +@@ -843,7 +848,6 @@ int ksmbd_vfs_truncate(struct ksmbd_work *work, + * ksmbd_vfs_listxattr() - vfs helper for smb list extended attributes + * @dentry: dentry of file for listing xattrs + * @list: destination buffer +- * @size: destination buffer length + * + * Return: xattr list length on success, otherwise error + */ +@@ -952,7 +956,7 @@ int ksmbd_vfs_setxattr(struct mnt_idmap *idmap, + /** + * ksmbd_vfs_set_fadvise() - convert smb IO caching options to linux options + * @filp: file pointer for IO +- * @options: smb IO options ++ * @option: smb IO options + */ + void ksmbd_vfs_set_fadvise(struct file *filp, __le32 option) + { +@@ -1164,6 +1168,7 @@ static bool __caseless_lookup(struct dir_context *ctx, const char *name, + * @dir: path info + * @name: filename to lookup + * @namelen: filename length ++ * @um: &struct unicode_map to use + * + * Return: 0 on success, otherwise error + */ +@@ -1194,6 +1199,7 @@ static int ksmbd_vfs_lookup_in_dir(const struct path *dir, char *name, + + /** + * ksmbd_vfs_kern_path_locked() - lookup a file and get path info ++ * @work: work + * @name: file path that is relative to share + * @flags: lookup flags + * @parent_path: if lookup succeed, return parent_path info +@@ -1641,6 +1647,8 @@ int ksmbd_vfs_get_dos_attrib_xattr(struct mnt_idmap *idmap, + * ksmbd_vfs_init_kstat() - convert unix stat information to smb stat format + * @p: destination buffer + * @ksmbd_kstat: ksmbd kstat wrapper ++ * ++ * Returns: pointer to the converted &struct file_directory_info + */ + void *ksmbd_vfs_init_kstat(char **p, struct ksmbd_kstat *ksmbd_kstat) + { +-- +2.43.0 + diff --git a/queue-6.6/missing-field-not-being-returned-in-ioctl-cifs_ioc_g.patch b/queue-6.6/missing-field-not-being-returned-in-ioctl-cifs_ioc_g.patch new file mode 100644 index 0000000000..6d773e70a7 --- /dev/null +++ b/queue-6.6/missing-field-not-being-returned-in-ioctl-cifs_ioc_g.patch @@ -0,0 +1,36 @@ +From 2da23056afe13b1f84326db01aae30916e859209 Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Fri, 10 Nov 2023 01:24:16 -0600 +Subject: Missing field not being returned in ioctl CIFS_IOC_GET_MNT_INFO + +From: Steve French <stfrench@microsoft.com> + +[ Upstream commit 784e0e20b4c97c270b2892f677d3fad658e2c1d5 ] + +The tcon_flags field was always being set to zero in the information +about the mount returned by the ioctl CIFS_IOC_GET_MNT_INFO instead +of being set to the value of the Flags field in the tree connection +structure as intended. + +Reviewed-by: Shyam Prasad N <sprasad@microsoft.com> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/client/ioctl.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/fs/smb/client/ioctl.c b/fs/smb/client/ioctl.c +index 204dd7c47126e..682eabdd1d6cc 100644 +--- a/fs/smb/client/ioctl.c ++++ b/fs/smb/client/ioctl.c +@@ -143,6 +143,7 @@ static long smb_mnt_get_fsinfo(unsigned int xid, struct cifs_tcon *tcon, + + fsinf->version = 1; + fsinf->protocol_id = tcon->ses->server->vals->protocol_id; ++ fsinf->tcon_flags = tcon->Flags; + fsinf->device_characteristics = + le32_to_cpu(tcon->fsDevInfo.DeviceCharacteristics); + fsinf->device_type = le32_to_cpu(tcon->fsDevInfo.DeviceType); +-- +2.43.0 + diff --git a/queue-6.6/series b/queue-6.6/series new file mode 100644 index 0000000000..167c196ee8 --- /dev/null +++ b/queue-6.6/series @@ -0,0 +1,79 @@ +cifs-add-client-version-details-to-ntlm-authenticate.patch +smb3-clarify-some-of-the-unused-createoption-flags.patch +add-definition-for-new-smb3.1.1-command-type.patch +smb-use-crypto_shash_digest-in-symlink_hash.patch +cifs-print-server-capabilities-in-debugdata.patch +smb3-minor-rdma-cleanup.patch +smb3-more-minor-cleanups-for-session-handling-routin.patch +smb3-minor-cleanup-of-session-handling-code.patch +missing-field-not-being-returned-in-ioctl-cifs_ioc_g.patch +cifs-update-internal-module-version-number-for-cifs..patch +cifs-fix-use-after-free-for-iface-while-disabling-se.patch +smb-client-introduce-cifs_sfu_make_node.patch +smb-client-fix-minor-whitespace-errors-and-warnings.patch +smb-client-extend-smb2_compound_op-to-accept-more-co.patch +smb-client-allow-creating-special-files-via-reparse-.patch +smb-client-optimise-reparse-point-querying.patch +smb-client-allow-creating-symlinks-via-reparse-point.patch +smb-client-cleanup-smb2_query_reparse_point.patch +smb-client-handle-special-files-and-symlinks-in-smb3.patch +cifs-fix-in-logging-in-cifs_chan_update_iface.patch +smb3-improve-exception-handling-in-allocate_mr_list.patch +cifs-pass-unbyteswapped-eof-value-into-smb2_set_eof.patch +cifs-get-rid-of-dup-length-check-in-parse_reparse_po.patch +cifs-remove-unneeded-return-statement.patch +ksmbd-auth-fix-most-kernel-doc-warnings.patch +ksmbd-vfs-fix-all-kernel-doc-warnings.patch +cifs-update-internal-module-version-number-for-cifs..patch-8866 +cifs-remove-redundant-variable-tcon_exist.patch +cifs-minor-comment-cleanup.patch +cifs-pick-channel-for-tcon-and-tdis.patch +cifs-new-nt-status-codes-from-ms-smb2.patch +smb-client-don-t-clobber-i_rdev-from-cached-reparse-.patch +cifs-new-mount-option-called-retrans.patch +smb-fix-some-kernel-doc-comments.patch +smb-client-delete-true-false-defines.patch +cifs-commands-that-are-retried-should-have-replay-fl.patch +cifs-set-replay-flag-for-retries-of-write-command.patch +ksmbd-add-kernel-doc-for-ksmbd_extract_sharename-fun.patch +cifs-update-the-same-create_guid-on-replay.patch +smb-client-handle-path-separator-of-created-smb-syml.patch +smb3-update-allocation-size-more-accurately-on-write.patch +smb-client-parse-owner-group-when-creating-reparse-p.patch +smb-client-get-rid-of-smb311_posix_query_path_info.patch +smb-client-reuse-file-lease-key-in-compound-operatio.patch +smb-client-do-not-defer-close-open-handles-to-delete.patch +smb-client-retry-compound-request-without-reusing-le.patch +smb-client-introduce-reparse-mount-option.patch +smb-client-move-most-of-reparse-point-handling-code-.patch +smb-client-fix-potential-broken-compound-request.patch +smb-client-reduce-number-of-parameters-in-smb2_compo.patch +smb-client-add-support-for-wsl-reparse-points.patch +smb-client-fix-a-null-vs-is_err-check-in-wsl_set_xat.patch +smb-client-introduce-smb2_op_query_wsl_ea.patch +smb-client-parse-uid-gid-mode-and-dev-from-wsl-repar.patch +smb-client-set-correct-d_type-for-reparse-dfs-dfsr-a.patch +smb-client-return-reparse-type-in-proc-mounts.patch +smb3-add-dynamic-trace-point-for-ioctls.patch +smb-client-negotiate-compression-algorithms.patch +smb-common-fix-fields-sizes-in-compression_pattern_p.patch +smb-common-simplify-compression-headers.patch +cifs-update-internal-module-version-number-for-cifs..patch-9055 +ksmbd-mark-smb2_session_expired-to-session-when-dest.patch +ksmbd-add-support-for-durable-handles-v1-v2.patch +cifs-defer-close-file-handles-having-rh-lease.patch +cifs-fixes-for-get_inode_info.patch +cifs-remove-redundant-variable-assignment.patch +ksmbd-fix-possible-null-deref-in-smb_lazy_parent_lea.patch +ksmbd-fix-spelling-mistake-connction-connection.patch +ksmbd-fix-slab-out-of-bounds-in-smb_strndup_from_utf.patch +ksmbd-fix-potencial-out-of-bounds-when-buffer-offset.patch +cifs-move-some-extern-decls-from-.c-files-to-.h.patch +smb311-correct-incorrect-offset-field-in-compression.patch +smb311-additional-compression-flag-defined-in-update.patch +smb3-add-trace-event-for-mknod.patch +smb-client-fix-null-ptr-deref-in-cifs_mark_open_hand.patch +smb-client-instantiate-when-creating-sfu-files.patch +cifs-add-tracing-for-the-cifs_tcon-struct-refcountin.patch +ksmbd-add-continuous-availability-share-parameter.patch +smb-smb2pdu.h-avoid-wflex-array-member-not-at-end-wa.patch diff --git a/queue-6.6/smb-client-add-support-for-wsl-reparse-points.patch b/queue-6.6/smb-client-add-support-for-wsl-reparse-points.patch new file mode 100644 index 0000000000..a08d604b98 --- /dev/null +++ b/queue-6.6/smb-client-add-support-for-wsl-reparse-points.patch @@ -0,0 +1,428 @@ +From d32fc0d545a9d69169dc935384b7e4af30141e18 Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Fri, 26 Jan 2024 19:26:06 -0300 +Subject: smb: client: add support for WSL reparse points + +From: Paulo Alcantara <pc@manguebit.com> + +[ Upstream commit 5a4b09ecf8e8ad26ea03a37e52e310fe13f15b49 ] + +Add support for creating special files via WSL reparse points when +using 'reparse=wsl' mount option. They're faster than NFS reparse +points because they don't require extra roundtrips to figure out what +->d_type a specific dirent is as such information is already stored in +query dir responses and then making getdents() calls faster. + +Signed-off-by: Paulo Alcantara <pc@manguebit.com> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/client/cifsglob.h | 1 + + fs/smb/client/fs_context.c | 4 +- + fs/smb/client/reparse.c | 170 +++++++++++++++++++++++++++++++++++-- + fs/smb/client/reparse.h | 13 ++- + fs/smb/client/smb2inode.c | 8 +- + fs/smb/client/smb2ops.c | 2 +- + fs/smb/client/smb2pdu.c | 12 +++ + fs/smb/client/smb2pdu.h | 11 ++- + fs/smb/client/smb2proto.h | 3 +- + fs/smb/common/smbfsctl.h | 6 -- + 10 files changed, 210 insertions(+), 20 deletions(-) + +diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h +index 5a902fb20ac96..54758825fa8d9 100644 +--- a/fs/smb/client/cifsglob.h ++++ b/fs/smb/client/cifsglob.h +@@ -1379,6 +1379,7 @@ struct cifs_open_parms { + umode_t mode; + bool reconnect:1; + bool replay:1; /* indicates that this open is for a replay */ ++ struct kvec *ea_cctx; + }; + + struct cifs_fid { +diff --git a/fs/smb/client/fs_context.c b/fs/smb/client/fs_context.c +index 535885fbdf51d..fbaa901d3493a 100644 +--- a/fs/smb/client/fs_context.c ++++ b/fs/smb/client/fs_context.c +@@ -318,8 +318,8 @@ static int parse_reparse_flavor(struct fs_context *fc, char *value, + ctx->reparse_type = CIFS_REPARSE_TYPE_NFS; + break; + case Opt_reparse_wsl: +- cifs_errorf(fc, "unsupported reparse= option: %s\n", value); +- return 1; ++ ctx->reparse_type = CIFS_REPARSE_TYPE_WSL; ++ break; + default: + cifs_errorf(fc, "bad reparse= option: %s\n", value); + return 1; +diff --git a/fs/smb/client/reparse.c b/fs/smb/client/reparse.c +index c405be47c84d9..b240ccc9c887c 100644 +--- a/fs/smb/client/reparse.c ++++ b/fs/smb/client/reparse.c +@@ -11,6 +11,7 @@ + #include "cifsproto.h" + #include "cifs_unicode.h" + #include "cifs_debug.h" ++#include "fs_context.h" + #include "reparse.h" + + int smb2_create_reparse_symlink(const unsigned int xid, struct inode *inode, +@@ -68,7 +69,7 @@ int smb2_create_reparse_symlink(const unsigned int xid, struct inode *inode, + iov.iov_base = buf; + iov.iov_len = len; + new = smb2_get_reparse_inode(&data, inode->i_sb, xid, +- tcon, full_path, &iov); ++ tcon, full_path, &iov, NULL); + if (!IS_ERR(new)) + d_instantiate(dentry, new); + else +@@ -114,9 +115,9 @@ static int nfs_set_reparse_buf(struct reparse_posix_data *buf, + return 0; + } + +-int smb2_make_nfs_node(unsigned int xid, struct inode *inode, +- struct dentry *dentry, struct cifs_tcon *tcon, +- const char *full_path, umode_t mode, dev_t dev) ++static int mknod_nfs(unsigned int xid, struct inode *inode, ++ struct dentry *dentry, struct cifs_tcon *tcon, ++ const char *full_path, umode_t mode, dev_t dev) + { + struct cifs_open_info_data data; + struct reparse_posix_data *p; +@@ -136,12 +137,171 @@ int smb2_make_nfs_node(unsigned int xid, struct inode *inode, + }; + + new = smb2_get_reparse_inode(&data, inode->i_sb, xid, +- tcon, full_path, &iov); ++ tcon, full_path, &iov, NULL); ++ if (!IS_ERR(new)) ++ d_instantiate(dentry, new); ++ else ++ rc = PTR_ERR(new); ++ cifs_free_open_info(&data); ++ return rc; ++} ++ ++static int wsl_set_reparse_buf(struct reparse_data_buffer *buf, ++ mode_t mode, struct kvec *iov) ++{ ++ u32 tag; ++ ++ switch ((tag = reparse_mode_wsl_tag(mode))) { ++ case IO_REPARSE_TAG_LX_BLK: ++ case IO_REPARSE_TAG_LX_CHR: ++ case IO_REPARSE_TAG_LX_FIFO: ++ case IO_REPARSE_TAG_AF_UNIX: ++ break; ++ default: ++ return -EOPNOTSUPP; ++ } ++ ++ buf->ReparseTag = cpu_to_le32(tag); ++ buf->Reserved = 0; ++ buf->ReparseDataLength = 0; ++ iov->iov_base = buf; ++ iov->iov_len = sizeof(*buf); ++ return 0; ++} ++ ++static struct smb2_create_ea_ctx *ea_create_context(u32 dlen, size_t *cc_len) ++{ ++ struct smb2_create_ea_ctx *cc; ++ ++ *cc_len = round_up(sizeof(*cc) + dlen, 8); ++ cc = kzalloc(*cc_len, GFP_KERNEL); ++ if (!cc) ++ return ERR_PTR(-ENOMEM); ++ ++ cc->ctx.NameOffset = cpu_to_le16(offsetof(struct smb2_create_ea_ctx, ++ name)); ++ cc->ctx.NameLength = cpu_to_le16(4); ++ memcpy(cc->name, SMB2_CREATE_EA_BUFFER, strlen(SMB2_CREATE_EA_BUFFER)); ++ cc->ctx.DataOffset = cpu_to_le16(offsetof(struct smb2_create_ea_ctx, ea)); ++ cc->ctx.DataLength = cpu_to_le32(dlen); ++ return cc; ++} ++ ++struct wsl_xattr { ++ const char *name; ++ __le64 value; ++ u16 size; ++ u32 next; ++}; ++ ++static int wsl_set_xattrs(struct inode *inode, umode_t _mode, ++ dev_t _dev, struct kvec *iov) ++{ ++ struct smb2_file_full_ea_info *ea; ++ struct smb2_create_ea_ctx *cc; ++ struct smb3_fs_context *ctx = CIFS_SB(inode->i_sb)->ctx; ++ __le64 uid = cpu_to_le64(from_kuid(current_user_ns(), ctx->linux_uid)); ++ __le64 gid = cpu_to_le64(from_kgid(current_user_ns(), ctx->linux_gid)); ++ __le64 dev = cpu_to_le64(((u64)MINOR(_dev) << 32) | MAJOR(_dev)); ++ __le64 mode = cpu_to_le64(_mode); ++ struct wsl_xattr xattrs[] = { ++ { .name = "$LXUID", .value = uid, .size = 4, }, ++ { .name = "$LXGID", .value = gid, .size = 4, }, ++ { .name = "$LXMOD", .value = mode, .size = 4, }, ++ { .name = "$LXDEV", .value = dev, .size = 8, }, ++ }; ++ size_t cc_len; ++ u32 dlen = 0, next = 0; ++ int i, num_xattrs; ++ u8 name_size = strlen(xattrs[0].name) + 1; ++ ++ memset(iov, 0, sizeof(*iov)); ++ ++ /* Exclude $LXDEV xattr for sockets and fifos */ ++ if (S_ISSOCK(_mode) || S_ISFIFO(_mode)) ++ num_xattrs = ARRAY_SIZE(xattrs) - 1; ++ else ++ num_xattrs = ARRAY_SIZE(xattrs); ++ ++ for (i = 0; i < num_xattrs; i++) { ++ xattrs[i].next = ALIGN(sizeof(*ea) + name_size + ++ xattrs[i].size, 4); ++ dlen += xattrs[i].next; ++ } ++ ++ cc = ea_create_context(dlen, &cc_len); ++ if (!cc) ++ return PTR_ERR(cc); ++ ++ ea = &cc->ea; ++ for (i = 0; i < num_xattrs; i++) { ++ ea = (void *)((u8 *)ea + next); ++ next = xattrs[i].next; ++ ea->next_entry_offset = cpu_to_le32(next); ++ ++ ea->ea_name_length = name_size - 1; ++ ea->ea_value_length = cpu_to_le16(xattrs[i].size); ++ memcpy(ea->ea_data, xattrs[i].name, name_size); ++ memcpy(&ea->ea_data[name_size], ++ &xattrs[i].value, xattrs[i].size); ++ } ++ ea->next_entry_offset = 0; ++ ++ iov->iov_base = cc; ++ iov->iov_len = cc_len; ++ return 0; ++} ++ ++static int mknod_wsl(unsigned int xid, struct inode *inode, ++ struct dentry *dentry, struct cifs_tcon *tcon, ++ const char *full_path, umode_t mode, dev_t dev) ++{ ++ struct cifs_open_info_data data; ++ struct reparse_data_buffer buf; ++ struct inode *new; ++ struct kvec reparse_iov, xattr_iov; ++ int rc; ++ ++ rc = wsl_set_reparse_buf(&buf, mode, &reparse_iov); ++ if (rc) ++ return rc; ++ ++ rc = wsl_set_xattrs(inode, mode, dev, &xattr_iov); ++ if (rc) ++ return rc; ++ ++ data = (struct cifs_open_info_data) { ++ .reparse_point = true, ++ .reparse = { .tag = le32_to_cpu(buf.ReparseTag), .buf = &buf, }, ++ }; ++ ++ new = smb2_get_reparse_inode(&data, inode->i_sb, ++ xid, tcon, full_path, ++ &reparse_iov, &xattr_iov); + if (!IS_ERR(new)) + d_instantiate(dentry, new); + else + rc = PTR_ERR(new); + cifs_free_open_info(&data); ++ kfree(xattr_iov.iov_base); ++ return rc; ++} ++ ++int smb2_mknod_reparse(unsigned int xid, struct inode *inode, ++ struct dentry *dentry, struct cifs_tcon *tcon, ++ const char *full_path, umode_t mode, dev_t dev) ++{ ++ struct smb3_fs_context *ctx = CIFS_SB(inode->i_sb)->ctx; ++ int rc = -EOPNOTSUPP; ++ ++ switch (ctx->reparse_type) { ++ case CIFS_REPARSE_TYPE_NFS: ++ rc = mknod_nfs(xid, inode, dentry, tcon, full_path, mode, dev); ++ break; ++ case CIFS_REPARSE_TYPE_WSL: ++ rc = mknod_wsl(xid, inode, dentry, tcon, full_path, mode, dev); ++ break; ++ } + return rc; + } + +diff --git a/fs/smb/client/reparse.h b/fs/smb/client/reparse.h +index 3ceb90da0df90..9816bac985525 100644 +--- a/fs/smb/client/reparse.h ++++ b/fs/smb/client/reparse.h +@@ -28,6 +28,17 @@ static inline u64 reparse_mode_nfs_type(mode_t mode) + return 0; + } + ++static inline u32 reparse_mode_wsl_tag(mode_t mode) ++{ ++ switch (mode & S_IFMT) { ++ case S_IFBLK: return IO_REPARSE_TAG_LX_BLK; ++ case S_IFCHR: return IO_REPARSE_TAG_LX_CHR; ++ case S_IFIFO: return IO_REPARSE_TAG_LX_FIFO; ++ case S_IFSOCK: return IO_REPARSE_TAG_AF_UNIX; ++ } ++ return 0; ++} ++ + /* + * Match a reparse point inode if reparse tag and ctime haven't changed. + * +@@ -64,7 +75,7 @@ bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb, + int smb2_create_reparse_symlink(const unsigned int xid, struct inode *inode, + struct dentry *dentry, struct cifs_tcon *tcon, + const char *full_path, const char *symname); +-int smb2_make_nfs_node(unsigned int xid, struct inode *inode, ++int smb2_mknod_reparse(unsigned int xid, struct inode *inode, + struct dentry *dentry, struct cifs_tcon *tcon, + const char *full_path, umode_t mode, dev_t dev); + int smb2_parse_reparse_point(struct cifs_sb_info *cifs_sb, struct kvec *rsp_iov, +diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c +index e1ad54b27b63e..0e1b9a1552e71 100644 +--- a/fs/smb/client/smb2inode.c ++++ b/fs/smb/client/smb2inode.c +@@ -1060,7 +1060,8 @@ struct inode *smb2_get_reparse_inode(struct cifs_open_info_data *data, + const unsigned int xid, + struct cifs_tcon *tcon, + const char *full_path, +- struct kvec *iov) ++ struct kvec *reparse_iov, ++ struct kvec *xattr_iov) + { + struct cifs_open_parms oparms; + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); +@@ -1077,8 +1078,11 @@ struct inode *smb2_get_reparse_inode(struct cifs_open_info_data *data, + FILE_CREATE, + CREATE_NOT_DIR | OPEN_REPARSE_POINT, + ACL_NO_MODE); ++ if (xattr_iov) ++ oparms.ea_cctx = xattr_iov; ++ + cmds[0] = SMB2_OP_SET_REPARSE; +- in_iov[0] = *iov; ++ in_iov[0] = *reparse_iov; + in_iov[1].iov_base = data; + in_iov[1].iov_len = sizeof(*data); + +diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c +index 2c59a6fc2d7c7..981a922cf38f0 100644 +--- a/fs/smb/client/smb2ops.c ++++ b/fs/smb/client/smb2ops.c +@@ -5038,7 +5038,7 @@ static int smb2_make_node(unsigned int xid, struct inode *inode, + rc = cifs_sfu_make_node(xid, inode, dentry, tcon, + full_path, mode, dev); + } else { +- rc = smb2_make_nfs_node(xid, inode, dentry, tcon, ++ rc = smb2_mknod_reparse(xid, inode, dentry, tcon, + full_path, mode, dev); + } + return rc; +diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c +index 60793143e24c6..4d805933e14f3 100644 +--- a/fs/smb/client/smb2pdu.c ++++ b/fs/smb/client/smb2pdu.c +@@ -2732,6 +2732,17 @@ add_query_id_context(struct kvec *iov, unsigned int *num_iovec) + return 0; + } + ++static void add_ea_context(struct cifs_open_parms *oparms, ++ struct kvec *rq_iov, unsigned int *num_iovs) ++{ ++ struct kvec *iov = oparms->ea_cctx; ++ ++ if (iov && iov->iov_base && iov->iov_len) { ++ rq_iov[(*num_iovs)++] = *iov; ++ memset(iov, 0, sizeof(*iov)); ++ } ++} ++ + static int + alloc_path_with_tree_prefix(__le16 **out_path, int *out_size, int *out_len, + const char *treename, const __le16 *path) +@@ -3098,6 +3109,7 @@ SMB2_open_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server, + } + + add_query_id_context(iov, &n_iov); ++ add_ea_context(oparms, iov, &n_iov); + + if (n_iov > 2) { + /* +diff --git a/fs/smb/client/smb2pdu.h b/fs/smb/client/smb2pdu.h +index b00f707bddfcc..3334f2c364fb9 100644 +--- a/fs/smb/client/smb2pdu.h ++++ b/fs/smb/client/smb2pdu.h +@@ -117,9 +117,10 @@ struct share_redirect_error_context_rsp { + * [4] : posix context + * [5] : time warp context + * [6] : query id context +- * [7] : compound padding ++ * [7] : create ea context ++ * [8] : compound padding + */ +-#define SMB2_CREATE_IOV_SIZE 8 ++#define SMB2_CREATE_IOV_SIZE 9 + + /* + * Maximum size of a SMB2_CREATE response is 64 (smb2 header) + +@@ -413,4 +414,10 @@ struct smb2_posix_info_parsed { + const u8 *name; + }; + ++struct smb2_create_ea_ctx { ++ struct create_context ctx; ++ __u8 name[8]; ++ struct smb2_file_full_ea_info ea; ++} __packed; ++ + #endif /* _SMB2PDU_H */ +diff --git a/fs/smb/client/smb2proto.h b/fs/smb/client/smb2proto.h +index 64a0ef0409a6e..732169d8a67a3 100644 +--- a/fs/smb/client/smb2proto.h ++++ b/fs/smb/client/smb2proto.h +@@ -61,7 +61,8 @@ struct inode *smb2_get_reparse_inode(struct cifs_open_info_data *data, + const unsigned int xid, + struct cifs_tcon *tcon, + const char *full_path, +- struct kvec *iov); ++ struct kvec *reparse_iov, ++ struct kvec *xattr_iov); + int smb2_query_reparse_point(const unsigned int xid, + struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, +diff --git a/fs/smb/common/smbfsctl.h b/fs/smb/common/smbfsctl.h +index edd7fc2a7921b..a94d658b88e86 100644 +--- a/fs/smb/common/smbfsctl.h ++++ b/fs/smb/common/smbfsctl.h +@@ -158,12 +158,6 @@ + #define IO_REPARSE_TAG_LX_CHR 0x80000025 + #define IO_REPARSE_TAG_LX_BLK 0x80000026 + +-#define IO_REPARSE_TAG_LX_SYMLINK_LE cpu_to_le32(0xA000001D) +-#define IO_REPARSE_TAG_AF_UNIX_LE cpu_to_le32(0x80000023) +-#define IO_REPARSE_TAG_LX_FIFO_LE cpu_to_le32(0x80000024) +-#define IO_REPARSE_TAG_LX_CHR_LE cpu_to_le32(0x80000025) +-#define IO_REPARSE_TAG_LX_BLK_LE cpu_to_le32(0x80000026) +- + /* fsctl flags */ + /* If Flags is set to this value, the request is an FSCTL not ioctl request */ + #define SMB2_0_IOCTL_IS_FSCTL 0x00000001 +-- +2.43.0 + diff --git a/queue-6.6/smb-client-allow-creating-special-files-via-reparse-.patch b/queue-6.6/smb-client-allow-creating-special-files-via-reparse-.patch new file mode 100644 index 0000000000..da5a26ae3d --- /dev/null +++ b/queue-6.6/smb-client-allow-creating-special-files-via-reparse-.patch @@ -0,0 +1,616 @@ +From 4a13194d70244142c201613ce9de364e606f0647 Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Sun, 28 Apr 2024 01:12:41 -0500 +Subject: smb: client: allow creating special files via reparse points + +From: Steve French <stfrench@microsoft.com> + +[ Upstream commit 102466f303ffcd5cff207b3c122557f73f1041e6 ] + +Add support for creating special files (e.g. char/block devices, +sockets, fifos) via NFS reparse points on SMB2+, which are fully +supported by most SMB servers and documented in MS-FSCC. + +smb2_get_reparse_inode() creates the file with a corresponding reparse +point buffer set in @iov through a single roundtrip to the server. + +Reported-by: kernel test robot <lkp@intel.com> +Closes: https://lore.kernel.org/oe-kbuild-all/202311260746.HOJ039BV-lkp@intel.com/ +Signed-off-by: Paulo Alcantara (SUSE) <pc@manguebit.com> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/client/cifsproto.h | 8 +++- + fs/smb/client/dir.c | 7 +-- + fs/smb/client/file.c | 10 +++-- + fs/smb/client/inode.c | 76 ++++++++++++++++++++----------- + fs/smb/client/link.c | 10 +++-- + fs/smb/client/smb2glob.h | 25 ++++++----- + fs/smb/client/smb2inode.c | 75 +++++++++++++++++++++++++++++++ + fs/smb/client/smb2ops.c | 94 +++++++++++++++++++++++++++++++++++---- + fs/smb/client/smb2proto.h | 7 +++ + fs/smb/client/trace.h | 4 +- + 10 files changed, 256 insertions(+), 60 deletions(-) + +diff --git a/fs/smb/client/cifsproto.h b/fs/smb/client/cifsproto.h +index 9480cdb9588d5..49de2545f34ce 100644 +--- a/fs/smb/client/cifsproto.h ++++ b/fs/smb/client/cifsproto.h +@@ -213,8 +213,12 @@ int cifs_get_inode_info(struct inode **inode, const char *full_path, + bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb, + struct cifs_fattr *fattr, + struct cifs_open_info_data *data); +-extern int smb311_posix_get_inode_info(struct inode **pinode, const char *search_path, +- struct super_block *sb, unsigned int xid); ++ ++extern int smb311_posix_get_inode_info(struct inode **inode, ++ const char *full_path, ++ struct cifs_open_info_data *data, ++ struct super_block *sb, ++ const unsigned int xid); + extern int cifs_get_inode_info_unix(struct inode **pinode, + const unsigned char *search_path, + struct super_block *sb, unsigned int xid); +diff --git a/fs/smb/client/dir.c b/fs/smb/client/dir.c +index 855468a32904e..37897b919dd5a 100644 +--- a/fs/smb/client/dir.c ++++ b/fs/smb/client/dir.c +@@ -695,9 +695,10 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, + full_path, d_inode(direntry)); + + again: +- if (pTcon->posix_extensions) +- rc = smb311_posix_get_inode_info(&newInode, full_path, parent_dir_inode->i_sb, xid); +- else if (pTcon->unix_ext) { ++ if (pTcon->posix_extensions) { ++ rc = smb311_posix_get_inode_info(&newInode, full_path, NULL, ++ parent_dir_inode->i_sb, xid); ++ } else if (pTcon->unix_ext) { + rc = cifs_get_inode_info_unix(&newInode, full_path, + parent_dir_inode->i_sb, xid); + } else { +diff --git a/fs/smb/client/file.c b/fs/smb/client/file.c +index 53a8c633221b9..f41804245fca1 100644 +--- a/fs/smb/client/file.c ++++ b/fs/smb/client/file.c +@@ -1102,14 +1102,16 @@ cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush) + if (!is_interrupt_error(rc)) + mapping_set_error(inode->i_mapping, rc); + +- if (tcon->posix_extensions) +- rc = smb311_posix_get_inode_info(&inode, full_path, inode->i_sb, xid); +- else if (tcon->unix_ext) ++ if (tcon->posix_extensions) { ++ rc = smb311_posix_get_inode_info(&inode, full_path, ++ NULL, inode->i_sb, xid); ++ } else if (tcon->unix_ext) { + rc = cifs_get_inode_info_unix(&inode, full_path, + inode->i_sb, xid); +- else ++ } else { + rc = cifs_get_inode_info(&inode, full_path, NULL, + inode->i_sb, xid, NULL); ++ } + } + /* + * Else we are writing out data to server already and could deadlock if +diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c +index fa6330d586e89..391839feb29d5 100644 +--- a/fs/smb/client/inode.c ++++ b/fs/smb/client/inode.c +@@ -1063,7 +1063,9 @@ static int reparse_info_to_fattr(struct cifs_open_info_data *data, + const unsigned int xid, + struct cifs_tcon *tcon, + const char *full_path, +- struct cifs_fattr *fattr) ++ struct cifs_fattr *fattr, ++ struct cifs_sid *owner, ++ struct cifs_sid *group) + { + struct TCP_Server_Info *server = tcon->ses->server; + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); +@@ -1094,7 +1096,8 @@ static int reparse_info_to_fattr(struct cifs_open_info_data *data, + rc = 0; + goto out; + default: +- if (data->symlink_target) { ++ /* Check for cached reparse point data */ ++ if (data->symlink_target || data->reparse.buf) { + rc = 0; + } else if (server->ops->parse_reparse_point) { + rc = server->ops->parse_reparse_point(cifs_sb, +@@ -1103,7 +1106,10 @@ static int reparse_info_to_fattr(struct cifs_open_info_data *data, + break; + } + +- cifs_open_info_to_fattr(fattr, data, sb); ++ if (tcon->posix_extensions) ++ smb311_posix_info_to_fattr(fattr, data, owner, group, sb); ++ else ++ cifs_open_info_to_fattr(fattr, data, sb); + out: + fattr->cf_cifstag = data->reparse.tag; + free_rsp_buf(rsp_buftype, rsp_iov.iov_base); +@@ -1155,7 +1161,8 @@ static int cifs_get_fattr(struct cifs_open_info_data *data, + */ + if (cifs_open_data_reparse(data)) { + rc = reparse_info_to_fattr(data, sb, xid, tcon, +- full_path, fattr); ++ full_path, fattr, ++ NULL, NULL); + } else { + cifs_open_info_to_fattr(fattr, data, sb); + } +@@ -1293,18 +1300,19 @@ int cifs_get_inode_info(struct inode **inode, + return rc; + } + +-static int smb311_posix_get_fattr(struct cifs_fattr *fattr, ++static int smb311_posix_get_fattr(struct cifs_open_info_data *data, ++ struct cifs_fattr *fattr, + const char *full_path, + struct super_block *sb, + const unsigned int xid) + { +- struct cifs_open_info_data data = {}; ++ struct cifs_open_info_data tmp_data = {}; + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); + struct cifs_tcon *tcon; + struct tcon_link *tlink; + struct cifs_sid owner, group; + int tmprc; +- int rc; ++ int rc = 0; + + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) +@@ -1312,12 +1320,14 @@ static int smb311_posix_get_fattr(struct cifs_fattr *fattr, + tcon = tlink_tcon(tlink); + + /* +- * 1. Fetch file metadata ++ * 1. Fetch file metadata if not provided (data) + */ +- +- rc = smb311_posix_query_path_info(xid, tcon, cifs_sb, +- full_path, &data, +- &owner, &group); ++ if (!data) { ++ rc = smb311_posix_query_path_info(xid, tcon, cifs_sb, ++ full_path, &tmp_data, ++ &owner, &group); ++ data = &tmp_data; ++ } + + /* + * 2. Convert it to internal cifs metadata (fattr) +@@ -1325,7 +1335,14 @@ static int smb311_posix_get_fattr(struct cifs_fattr *fattr, + + switch (rc) { + case 0: +- smb311_posix_info_to_fattr(fattr, &data, &owner, &group, sb); ++ if (cifs_open_data_reparse(data)) { ++ rc = reparse_info_to_fattr(data, sb, xid, tcon, ++ full_path, fattr, ++ &owner, &group); ++ } else { ++ smb311_posix_info_to_fattr(fattr, data, ++ &owner, &group, sb); ++ } + break; + case -EREMOTE: + /* DFS link, no metadata available on this server */ +@@ -1356,12 +1373,15 @@ static int smb311_posix_get_fattr(struct cifs_fattr *fattr, + + out: + cifs_put_tlink(tlink); +- cifs_free_open_info(&data); ++ cifs_free_open_info(data); + return rc; + } + +-int smb311_posix_get_inode_info(struct inode **inode, const char *full_path, +- struct super_block *sb, const unsigned int xid) ++int smb311_posix_get_inode_info(struct inode **inode, ++ const char *full_path, ++ struct cifs_open_info_data *data, ++ struct super_block *sb, ++ const unsigned int xid) + { + struct cifs_fattr fattr = {}; + int rc; +@@ -1371,7 +1391,7 @@ int smb311_posix_get_inode_info(struct inode **inode, const char *full_path, + return 0; + } + +- rc = smb311_posix_get_fattr(&fattr, full_path, sb, xid); ++ rc = smb311_posix_get_fattr(data, &fattr, full_path, sb, xid); + if (rc) + goto out; + +@@ -1521,7 +1541,7 @@ struct inode *cifs_root_iget(struct super_block *sb) + + convert_delimiter(path, CIFS_DIR_SEP(cifs_sb)); + if (tcon->posix_extensions) +- rc = smb311_posix_get_fattr(&fattr, path, sb, xid); ++ rc = smb311_posix_get_fattr(NULL, &fattr, path, sb, xid); + else + rc = cifs_get_fattr(NULL, sb, xid, NULL, &fattr, &inode, path); + +@@ -1894,16 +1914,18 @@ cifs_mkdir_qinfo(struct inode *parent, struct dentry *dentry, umode_t mode, + int rc = 0; + struct inode *inode = NULL; + +- if (tcon->posix_extensions) +- rc = smb311_posix_get_inode_info(&inode, full_path, parent->i_sb, xid); ++ if (tcon->posix_extensions) { ++ rc = smb311_posix_get_inode_info(&inode, full_path, ++ NULL, parent->i_sb, xid); + #ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY +- else if (tcon->unix_ext) ++ } else if (tcon->unix_ext) { + rc = cifs_get_inode_info_unix(&inode, full_path, parent->i_sb, + xid); + #endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ +- else ++ } else { + rc = cifs_get_inode_info(&inode, full_path, NULL, parent->i_sb, + xid, NULL); ++ } + + if (rc) + return rc; +@@ -2585,13 +2607,15 @@ int cifs_revalidate_dentry_attr(struct dentry *dentry) + dentry, cifs_get_time(dentry), jiffies); + + again: +- if (cifs_sb_master_tcon(CIFS_SB(sb))->posix_extensions) +- rc = smb311_posix_get_inode_info(&inode, full_path, sb, xid); +- else if (cifs_sb_master_tcon(CIFS_SB(sb))->unix_ext) ++ if (cifs_sb_master_tcon(CIFS_SB(sb))->posix_extensions) { ++ rc = smb311_posix_get_inode_info(&inode, full_path, ++ NULL, sb, xid); ++ } else if (cifs_sb_master_tcon(CIFS_SB(sb))->unix_ext) { + rc = cifs_get_inode_info_unix(&inode, full_path, sb, xid); +- else ++ } else { + rc = cifs_get_inode_info(&inode, full_path, NULL, sb, + xid, NULL); ++ } + if (rc == -EAGAIN && count++ < 10) + goto again; + out: +diff --git a/fs/smb/client/link.c b/fs/smb/client/link.c +index 3ef34218a790d..691f43a1ec2bc 100644 +--- a/fs/smb/client/link.c ++++ b/fs/smb/client/link.c +@@ -614,14 +614,16 @@ cifs_symlink(struct mnt_idmap *idmap, struct inode *inode, + cifs_sb_target->local_nls); */ + + if (rc == 0) { +- if (pTcon->posix_extensions) +- rc = smb311_posix_get_inode_info(&newinode, full_path, inode->i_sb, xid); +- else if (pTcon->unix_ext) ++ if (pTcon->posix_extensions) { ++ rc = smb311_posix_get_inode_info(&newinode, full_path, ++ NULL, inode->i_sb, xid); ++ } else if (pTcon->unix_ext) { + rc = cifs_get_inode_info_unix(&newinode, full_path, + inode->i_sb, xid); +- else ++ } else { + rc = cifs_get_inode_info(&newinode, full_path, NULL, + inode->i_sb, xid, NULL); ++ } + + if (rc != 0) { + cifs_dbg(FYI, "Create symlink ok, getinodeinfo fail rc = %d\n", +diff --git a/fs/smb/client/smb2glob.h b/fs/smb/client/smb2glob.h +index 82e916ad167c0..ca87a0011c337 100644 +--- a/fs/smb/client/smb2glob.h ++++ b/fs/smb/client/smb2glob.h +@@ -23,17 +23,20 @@ + * Identifiers for functions that use the open, operation, close pattern + * in smb2inode.c:smb2_compound_op() + */ +-#define SMB2_OP_SET_DELETE 1 +-#define SMB2_OP_SET_INFO 2 +-#define SMB2_OP_QUERY_INFO 3 +-#define SMB2_OP_QUERY_DIR 4 +-#define SMB2_OP_MKDIR 5 +-#define SMB2_OP_RENAME 6 +-#define SMB2_OP_DELETE 7 +-#define SMB2_OP_HARDLINK 8 +-#define SMB2_OP_SET_EOF 9 +-#define SMB2_OP_RMDIR 10 +-#define SMB2_OP_POSIX_QUERY_INFO 11 ++enum smb2_compound_ops { ++ SMB2_OP_SET_DELETE = 1, ++ SMB2_OP_SET_INFO, ++ SMB2_OP_QUERY_INFO, ++ SMB2_OP_QUERY_DIR, ++ SMB2_OP_MKDIR, ++ SMB2_OP_RENAME, ++ SMB2_OP_DELETE, ++ SMB2_OP_HARDLINK, ++ SMB2_OP_SET_EOF, ++ SMB2_OP_RMDIR, ++ SMB2_OP_POSIX_QUERY_INFO, ++ SMB2_OP_SET_REPARSE ++}; + + /* Used when constructing chained read requests. */ + #define CHAINED_REQUEST 1 +diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c +index 4c66187eccdd2..e74d3a1e49dfa 100644 +--- a/fs/smb/client/smb2inode.c ++++ b/fs/smb/client/smb2inode.c +@@ -359,6 +359,22 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, + smb2_set_related(&rqst[num_rqst++]); + trace_smb3_hardlink_enter(xid, ses->Suid, tcon->tid, full_path); + break; ++ case SMB2_OP_SET_REPARSE: ++ rqst[num_rqst].rq_iov = vars->io_iov; ++ rqst[num_rqst].rq_nvec = ARRAY_SIZE(vars->io_iov); ++ ++ rc = SMB2_ioctl_init(tcon, server, &rqst[num_rqst], ++ COMPOUND_FID, COMPOUND_FID, ++ FSCTL_SET_REPARSE_POINT, ++ in_iov[i].iov_base, ++ in_iov[i].iov_len, 0); ++ if (rc) ++ goto finished; ++ smb2_set_next_command(tcon, &rqst[num_rqst]); ++ smb2_set_related(&rqst[num_rqst++]); ++ trace_smb3_set_reparse_compound_enter(xid, ses->Suid, ++ tcon->tid, full_path); ++ break; + default: + cifs_dbg(VFS, "Invalid command\n"); + rc = -EINVAL; +@@ -515,6 +531,16 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, + tcon->tid); + SMB2_set_info_free(&rqst[num_rqst++]); + break; ++ case SMB2_OP_SET_REPARSE: ++ if (rc) { ++ trace_smb3_set_reparse_compound_err(xid, ses->Suid, ++ tcon->tid, rc); ++ } else { ++ trace_smb3_set_reparse_compound_done(xid, ses->Suid, ++ tcon->tid); ++ } ++ SMB2_ioctl_free(&rqst[num_rqst++]); ++ break; + } + } + SMB2_close_free(&rqst[num_rqst]); +@@ -905,3 +931,52 @@ smb2_set_file_info(struct inode *inode, const char *full_path, + cifs_put_tlink(tlink); + return rc; + } ++ ++struct inode *smb2_get_reparse_inode(struct cifs_open_info_data *data, ++ struct super_block *sb, ++ const unsigned int xid, ++ struct cifs_tcon *tcon, ++ const char *full_path, ++ struct kvec *iov) ++{ ++ struct cifs_sb_info *cifs_sb = CIFS_SB(sb); ++ struct cifsFileInfo *cfile; ++ struct inode *new = NULL; ++ struct kvec in_iov[2]; ++ int cmds[2]; ++ int da, co, cd; ++ int rc; ++ ++ da = SYNCHRONIZE | DELETE | ++ FILE_READ_ATTRIBUTES | ++ FILE_WRITE_ATTRIBUTES; ++ co = CREATE_NOT_DIR | OPEN_REPARSE_POINT; ++ cd = FILE_CREATE; ++ cmds[0] = SMB2_OP_SET_REPARSE; ++ in_iov[0] = *iov; ++ in_iov[1].iov_base = data; ++ in_iov[1].iov_len = sizeof(*data); ++ ++ if (tcon->posix_extensions) { ++ cmds[1] = SMB2_OP_POSIX_QUERY_INFO; ++ cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile); ++ rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, ++ da, cd, co, ACL_NO_MODE, in_iov, ++ cmds, 2, cfile, NULL, NULL, NULL, NULL); ++ if (!rc) { ++ rc = smb311_posix_get_inode_info(&new, full_path, ++ data, sb, xid); ++ } ++ } else { ++ cmds[1] = SMB2_OP_QUERY_INFO; ++ cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile); ++ rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, ++ da, cd, co, ACL_NO_MODE, in_iov, ++ cmds, 2, cfile, NULL, NULL, NULL, NULL); ++ if (!rc) { ++ rc = cifs_get_inode_info(&new, full_path, ++ data, sb, xid, NULL); ++ } ++ } ++ return rc ? ERR_PTR(rc) : new; ++} +diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c +index 2b892a736e5f9..4fed19d5a81d4 100644 +--- a/fs/smb/client/smb2ops.c ++++ b/fs/smb/client/smb2ops.c +@@ -5170,11 +5170,88 @@ int cifs_sfu_make_node(unsigned int xid, struct inode *inode, + return rc; + } + ++static inline u64 mode_nfs_type(mode_t mode) ++{ ++ switch (mode & S_IFMT) { ++ case S_IFBLK: return NFS_SPECFILE_BLK; ++ case S_IFCHR: return NFS_SPECFILE_CHR; ++ case S_IFIFO: return NFS_SPECFILE_FIFO; ++ case S_IFSOCK: return NFS_SPECFILE_SOCK; ++ } ++ return 0; ++} ++ ++static int nfs_set_reparse_buf(struct reparse_posix_data *buf, ++ mode_t mode, dev_t dev, ++ struct kvec *iov) ++{ ++ u64 type; ++ u16 len, dlen; ++ ++ len = sizeof(*buf); ++ ++ switch ((type = mode_nfs_type(mode))) { ++ case NFS_SPECFILE_BLK: ++ case NFS_SPECFILE_CHR: ++ dlen = sizeof(__le64); ++ break; ++ case NFS_SPECFILE_FIFO: ++ case NFS_SPECFILE_SOCK: ++ dlen = 0; ++ break; ++ default: ++ return -EOPNOTSUPP; ++ } ++ ++ buf->ReparseTag = cpu_to_le32(IO_REPARSE_TAG_NFS); ++ buf->Reserved = 0; ++ buf->InodeType = cpu_to_le64(type); ++ buf->ReparseDataLength = cpu_to_le16(len + dlen - ++ sizeof(struct reparse_data_buffer)); ++ *(__le64 *)buf->DataBuffer = cpu_to_le64(((u64)MAJOR(dev) << 32) | ++ MINOR(dev)); ++ iov->iov_base = buf; ++ iov->iov_len = len + dlen; ++ return 0; ++} ++ ++static int nfs_make_node(unsigned int xid, struct inode *inode, ++ struct dentry *dentry, struct cifs_tcon *tcon, ++ const char *full_path, umode_t mode, dev_t dev) ++{ ++ struct cifs_open_info_data data; ++ struct reparse_posix_data *p; ++ struct inode *new; ++ struct kvec iov; ++ __u8 buf[sizeof(*p) + sizeof(__le64)]; ++ int rc; ++ ++ p = (struct reparse_posix_data *)buf; ++ rc = nfs_set_reparse_buf(p, mode, dev, &iov); ++ if (rc) ++ return rc; ++ ++ data = (struct cifs_open_info_data) { ++ .reparse_point = true, ++ .reparse = { .tag = IO_REPARSE_TAG_NFS, .posix = p, }, ++ }; ++ ++ new = smb2_get_reparse_inode(&data, inode->i_sb, xid, ++ tcon, full_path, &iov); ++ if (!IS_ERR(new)) ++ d_instantiate(dentry, new); ++ else ++ rc = PTR_ERR(new); ++ cifs_free_open_info(&data); ++ return rc; ++} ++ + static int smb2_make_node(unsigned int xid, struct inode *inode, + struct dentry *dentry, struct cifs_tcon *tcon, + const char *full_path, umode_t mode, dev_t dev) + { + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); ++ int rc; + + /* + * Check if mounted with mount parm 'sfu' mount parm. +@@ -5182,15 +5259,14 @@ static int smb2_make_node(unsigned int xid, struct inode *inode, + * supports block and char device (no socket & fifo), + * and was used by default in earlier versions of Windows + */ +- if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL)) +- return -EPERM; +- /* +- * TODO: Add ability to create instead via reparse point. Windows (e.g. +- * their current NFS server) uses this approach to expose special files +- * over SMB2/SMB3 and Samba will do this with SMB3.1.1 POSIX Extensions +- */ +- return cifs_sfu_make_node(xid, inode, dentry, tcon, +- full_path, mode, dev); ++ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) { ++ rc = cifs_sfu_make_node(xid, inode, dentry, tcon, ++ full_path, mode, dev); ++ } else { ++ rc = nfs_make_node(xid, inode, dentry, tcon, ++ full_path, mode, dev); ++ } ++ return rc; + } + + #ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY +diff --git a/fs/smb/client/smb2proto.h b/fs/smb/client/smb2proto.h +index a8084ce7fcbd2..efa2f8fe23449 100644 +--- a/fs/smb/client/smb2proto.h ++++ b/fs/smb/client/smb2proto.h +@@ -56,6 +56,12 @@ extern int smb3_handle_read_data(struct TCP_Server_Info *server, + extern int smb2_query_reparse_tag(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, const char *path, + __u32 *reparse_tag); ++struct inode *smb2_get_reparse_inode(struct cifs_open_info_data *data, ++ struct super_block *sb, ++ const unsigned int xid, ++ struct cifs_tcon *tcon, ++ const char *full_path, ++ struct kvec *iov); + int smb2_query_path_info(const unsigned int xid, + struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, +@@ -293,4 +299,5 @@ int smb311_posix_query_path_info(const unsigned int xid, + int posix_info_parse(const void *beg, const void *end, + struct smb2_posix_info_parsed *out); + int posix_info_sid_size(const void *beg, const void *end); ++ + #endif /* _SMB2PROTO_H */ +diff --git a/fs/smb/client/trace.h b/fs/smb/client/trace.h +index de199ec9f7263..34f507584274b 100644 +--- a/fs/smb/client/trace.h ++++ b/fs/smb/client/trace.h +@@ -370,11 +370,11 @@ DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(rename_enter); + DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(rmdir_enter); + DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(set_eof_enter); + DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(set_info_compound_enter); ++DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(set_reparse_compound_enter); + DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(delete_enter); + DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(mkdir_enter); + DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(tdis_enter); + +- + DECLARE_EVENT_CLASS(smb3_inf_compound_done_class, + TP_PROTO(unsigned int xid, + __u32 tid, +@@ -408,6 +408,7 @@ DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(rename_done); + DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(rmdir_done); + DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(set_eof_done); + DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(set_info_compound_done); ++DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(set_reparse_compound_done); + DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(delete_done); + DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(mkdir_done); + DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(tdis_done); +@@ -451,6 +452,7 @@ DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(rename_err); + DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(rmdir_err); + DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(set_eof_err); + DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(set_info_compound_err); ++DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(set_reparse_compound_err); + DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(mkdir_err); + DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(delete_err); + DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(tdis_err); +-- +2.43.0 + diff --git a/queue-6.6/smb-client-allow-creating-symlinks-via-reparse-point.patch b/queue-6.6/smb-client-allow-creating-symlinks-via-reparse-point.patch new file mode 100644 index 0000000000..73479a6450 --- /dev/null +++ b/queue-6.6/smb-client-allow-creating-symlinks-via-reparse-point.patch @@ -0,0 +1,203 @@ +From 11afd247963177dd21dd2c5e66b981df1d6c9681 Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Sat, 25 Nov 2023 23:55:04 -0300 +Subject: smb: client: allow creating symlinks via reparse points + +From: Paulo Alcantara <pc@manguebit.com> + +[ Upstream commit 514d793e27a310eb26b112c1f8f1a160472907e5 ] + +Add support for creating symlinks via IO_REPARSE_TAG_SYMLINK reparse +points in SMB2+. + +These are fully supported by most SMB servers and documented in +MS-FSCC. Also have the advantage of requiring fewer roundtrips as +their symlink targets can be parsed directly from CREATE responses on +STATUS_STOPPED_ON_SYMLINK errors. + +Reported-by: kernel test robot <lkp@intel.com> +Closes: https://lore.kernel.org/oe-kbuild-all/202311260838.nx5mkj1j-lkp@intel.com/ +Signed-off-by: Paulo Alcantara (SUSE) <pc@manguebit.com> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/client/cifsglob.h | 6 ++++ + fs/smb/client/link.c | 15 ++++++--- + fs/smb/client/smb2ops.c | 70 ++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 86 insertions(+), 5 deletions(-) + +diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h +index 3e7c3c3d73a73..414648bf816b2 100644 +--- a/fs/smb/client/cifsglob.h ++++ b/fs/smb/client/cifsglob.h +@@ -577,6 +577,12 @@ struct smb_version_operations { + int (*parse_reparse_point)(struct cifs_sb_info *cifs_sb, + struct kvec *rsp_iov, + struct cifs_open_info_data *data); ++ int (*create_reparse_symlink)(const unsigned int xid, ++ struct inode *inode, ++ struct dentry *dentry, ++ struct cifs_tcon *tcon, ++ const char *full_path, ++ const char *symname); + }; + + struct smb_version_values { +diff --git a/fs/smb/client/link.c b/fs/smb/client/link.c +index 691f43a1ec2bc..d86da949a9190 100644 +--- a/fs/smb/client/link.c ++++ b/fs/smb/client/link.c +@@ -569,6 +569,7 @@ cifs_symlink(struct mnt_idmap *idmap, struct inode *inode, + int rc = -EOPNOTSUPP; + unsigned int xid; + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); ++ struct TCP_Server_Info *server; + struct tcon_link *tlink; + struct cifs_tcon *pTcon; + const char *full_path; +@@ -590,6 +591,7 @@ cifs_symlink(struct mnt_idmap *idmap, struct inode *inode, + goto symlink_exit; + } + pTcon = tlink_tcon(tlink); ++ server = cifs_pick_channel(pTcon->ses); + + full_path = build_path_from_dentry(direntry, page); + if (IS_ERR(full_path)) { +@@ -601,17 +603,20 @@ cifs_symlink(struct mnt_idmap *idmap, struct inode *inode, + cifs_dbg(FYI, "symname is %s\n", symname); + + /* BB what if DFS and this volume is on different share? BB */ +- if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) ++ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) { + rc = create_mf_symlink(xid, pTcon, cifs_sb, full_path, symname); + #ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY +- else if (pTcon->unix_ext) ++ } else if (pTcon->unix_ext) { + rc = CIFSUnixCreateSymLink(xid, pTcon, full_path, symname, + cifs_sb->local_nls, + cifs_remap(cifs_sb)); + #endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ +- /* else +- rc = CIFSCreateReparseSymLink(xid, pTcon, fromName, toName, +- cifs_sb_target->local_nls); */ ++ } else if (server->ops->create_reparse_symlink) { ++ rc = server->ops->create_reparse_symlink(xid, inode, direntry, ++ pTcon, full_path, ++ symname); ++ goto symlink_exit; ++ } + + if (rc == 0) { + if (pTcon->posix_extensions) { +diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c +index 4fed19d5a81d4..c5957deb1a859 100644 +--- a/fs/smb/client/smb2ops.c ++++ b/fs/smb/client/smb2ops.c +@@ -5246,6 +5246,72 @@ static int nfs_make_node(unsigned int xid, struct inode *inode, + return rc; + } + ++static int smb2_create_reparse_symlink(const unsigned int xid, ++ struct inode *inode, ++ struct dentry *dentry, ++ struct cifs_tcon *tcon, ++ const char *full_path, ++ const char *symname) ++{ ++ struct reparse_symlink_data_buffer *buf = NULL; ++ struct cifs_open_info_data data; ++ struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); ++ struct inode *new; ++ struct kvec iov; ++ __le16 *path; ++ char *sym; ++ u16 len, plen; ++ int rc = 0; ++ ++ sym = kstrdup(symname, GFP_KERNEL); ++ if (!sym) ++ return -ENOMEM; ++ ++ data = (struct cifs_open_info_data) { ++ .reparse_point = true, ++ .reparse = { .tag = IO_REPARSE_TAG_SYMLINK, }, ++ .symlink_target = sym, ++ }; ++ ++ path = cifs_convert_path_to_utf16(symname, cifs_sb); ++ if (!path) { ++ rc = -ENOMEM; ++ goto out; ++ } ++ ++ plen = 2 * UniStrnlen((wchar_t *)path, PATH_MAX); ++ len = sizeof(*buf) + plen * 2; ++ buf = kzalloc(len, GFP_KERNEL); ++ if (!buf) { ++ rc = -ENOMEM; ++ goto out; ++ } ++ ++ buf->ReparseTag = cpu_to_le32(IO_REPARSE_TAG_SYMLINK); ++ buf->ReparseDataLength = cpu_to_le16(len - sizeof(struct reparse_data_buffer)); ++ buf->SubstituteNameOffset = cpu_to_le16(plen); ++ buf->SubstituteNameLength = cpu_to_le16(plen); ++ memcpy(&buf->PathBuffer[plen], path, plen); ++ buf->PrintNameOffset = 0; ++ buf->PrintNameLength = cpu_to_le16(plen); ++ memcpy(buf->PathBuffer, path, plen); ++ buf->Flags = cpu_to_le32(*symname != '/' ? SYMLINK_FLAG_RELATIVE : 0); ++ ++ iov.iov_base = buf; ++ iov.iov_len = len; ++ new = smb2_get_reparse_inode(&data, inode->i_sb, xid, ++ tcon, full_path, &iov); ++ if (!IS_ERR(new)) ++ d_instantiate(dentry, new); ++ else ++ rc = PTR_ERR(new); ++out: ++ kfree(path); ++ cifs_free_open_info(&data); ++ kfree(buf); ++ return rc; ++} ++ + static int smb2_make_node(unsigned int xid, struct inode *inode, + struct dentry *dentry, struct cifs_tcon *tcon, + const char *full_path, umode_t mode, dev_t dev) +@@ -5322,6 +5388,7 @@ struct smb_version_operations smb20_operations = { + .parse_reparse_point = smb2_parse_reparse_point, + .query_mf_symlink = smb3_query_mf_symlink, + .create_mf_symlink = smb3_create_mf_symlink, ++ .create_reparse_symlink = smb2_create_reparse_symlink, + .open = smb2_open_file, + .set_fid = smb2_set_fid, + .close = smb2_close_file, +@@ -5424,6 +5491,7 @@ struct smb_version_operations smb21_operations = { + .parse_reparse_point = smb2_parse_reparse_point, + .query_mf_symlink = smb3_query_mf_symlink, + .create_mf_symlink = smb3_create_mf_symlink, ++ .create_reparse_symlink = smb2_create_reparse_symlink, + .open = smb2_open_file, + .set_fid = smb2_set_fid, + .close = smb2_close_file, +@@ -5530,6 +5598,7 @@ struct smb_version_operations smb30_operations = { + .parse_reparse_point = smb2_parse_reparse_point, + .query_mf_symlink = smb3_query_mf_symlink, + .create_mf_symlink = smb3_create_mf_symlink, ++ .create_reparse_symlink = smb2_create_reparse_symlink, + .open = smb2_open_file, + .set_fid = smb2_set_fid, + .close = smb2_close_file, +@@ -5645,6 +5714,7 @@ struct smb_version_operations smb311_operations = { + .parse_reparse_point = smb2_parse_reparse_point, + .query_mf_symlink = smb3_query_mf_symlink, + .create_mf_symlink = smb3_create_mf_symlink, ++ .create_reparse_symlink = smb2_create_reparse_symlink, + .open = smb2_open_file, + .set_fid = smb2_set_fid, + .close = smb2_close_file, +-- +2.43.0 + diff --git a/queue-6.6/smb-client-cleanup-smb2_query_reparse_point.patch b/queue-6.6/smb-client-cleanup-smb2_query_reparse_point.patch new file mode 100644 index 0000000000..e837c4826c --- /dev/null +++ b/queue-6.6/smb-client-cleanup-smb2_query_reparse_point.patch @@ -0,0 +1,231 @@ +From 36f67e6de2109b5c25324dee4823f6cb0d42ff42 Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Sat, 25 Nov 2023 23:55:08 -0300 +Subject: smb: client: cleanup smb2_query_reparse_point() + +From: Paulo Alcantara <pc@manguebit.com> + +[ Upstream commit 3ded18a9e9d22a9cba8acad24b77a87851f9c9fa ] + +Use smb2_compound_op() with SMB2_OP_GET_REPARSE to get reparse point. + +Signed-off-by: Paulo Alcantara (SUSE) <pc@manguebit.com> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/client/smb2inode.c | 33 +++++++++ + fs/smb/client/smb2ops.c | 139 -------------------------------------- + fs/smb/client/smb2proto.h | 6 ++ + 3 files changed, 39 insertions(+), 139 deletions(-) + +diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c +index 11c1e06ab5417..1388ce5421a89 100644 +--- a/fs/smb/client/smb2inode.c ++++ b/fs/smb/client/smb2inode.c +@@ -1054,3 +1054,36 @@ struct inode *smb2_get_reparse_inode(struct cifs_open_info_data *data, + } + return rc ? ERR_PTR(rc) : new; + } ++ ++int smb2_query_reparse_point(const unsigned int xid, ++ struct cifs_tcon *tcon, ++ struct cifs_sb_info *cifs_sb, ++ const char *full_path, ++ u32 *tag, struct kvec *rsp, ++ int *rsp_buftype) ++{ ++ struct cifs_open_info_data data = {}; ++ struct cifsFileInfo *cfile; ++ struct kvec in_iov = { .iov_base = &data, .iov_len = sizeof(data), }; ++ int rc; ++ ++ cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path); ++ ++ cifs_get_readable_path(tcon, full_path, &cfile); ++ rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, ++ FILE_READ_ATTRIBUTES, FILE_OPEN, ++ OPEN_REPARSE_POINT, ACL_NO_MODE, &in_iov, ++ &(int){SMB2_OP_GET_REPARSE}, 1, cfile, ++ NULL, NULL, NULL, NULL); ++ if (rc) ++ goto out; ++ ++ *tag = data.reparse.tag; ++ *rsp = data.reparse.io.iov; ++ *rsp_buftype = data.reparse.io.buftype; ++ memset(&data.reparse.io.iov, 0, sizeof(data.reparse.io.iov)); ++ data.reparse.io.buftype = CIFS_NO_BUFFER; ++out: ++ cifs_free_open_info(&data); ++ return rc; ++} +diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c +index c5957deb1a859..a623a720db9e0 100644 +--- a/fs/smb/client/smb2ops.c ++++ b/fs/smb/client/smb2ops.c +@@ -2996,145 +2996,6 @@ static int smb2_parse_reparse_point(struct cifs_sb_info *cifs_sb, + return parse_reparse_point(buf, plen, cifs_sb, true, data); + } + +-static int smb2_query_reparse_point(const unsigned int xid, +- struct cifs_tcon *tcon, +- struct cifs_sb_info *cifs_sb, +- const char *full_path, +- u32 *tag, struct kvec *rsp, +- int *rsp_buftype) +-{ +- struct smb2_compound_vars *vars; +- int rc; +- __le16 *utf16_path = NULL; +- __u8 oplock = SMB2_OPLOCK_LEVEL_NONE; +- struct cifs_open_parms oparms; +- struct cifs_fid fid; +- struct TCP_Server_Info *server = cifs_pick_channel(tcon->ses); +- int flags = CIFS_CP_CREATE_CLOSE_OP; +- struct smb_rqst *rqst; +- int resp_buftype[3]; +- struct kvec *rsp_iov; +- struct smb2_ioctl_rsp *ioctl_rsp; +- struct reparse_data_buffer *reparse_buf; +- u32 off, count, len; +- +- cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path); +- +- if (smb3_encryption_required(tcon)) +- flags |= CIFS_TRANSFORM_REQ; +- +- utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb); +- if (!utf16_path) +- return -ENOMEM; +- +- resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER; +- vars = kzalloc(sizeof(*vars), GFP_KERNEL); +- if (!vars) { +- rc = -ENOMEM; +- goto out_free_path; +- } +- rqst = vars->rqst; +- rsp_iov = vars->rsp_iov; +- +- /* +- * setup smb2open - TODO add optimization to call cifs_get_readable_path +- * to see if there is a handle already open that we can use +- */ +- rqst[0].rq_iov = vars->open_iov; +- rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE; +- +- oparms = (struct cifs_open_parms) { +- .tcon = tcon, +- .path = full_path, +- .desired_access = FILE_READ_ATTRIBUTES, +- .disposition = FILE_OPEN, +- .create_options = cifs_create_options(cifs_sb, OPEN_REPARSE_POINT), +- .fid = &fid, +- }; +- +- rc = SMB2_open_init(tcon, server, +- &rqst[0], &oplock, &oparms, utf16_path); +- if (rc) +- goto query_rp_exit; +- smb2_set_next_command(tcon, &rqst[0]); +- +- +- /* IOCTL */ +- rqst[1].rq_iov = vars->io_iov; +- rqst[1].rq_nvec = SMB2_IOCTL_IOV_SIZE; +- +- rc = SMB2_ioctl_init(tcon, server, +- &rqst[1], COMPOUND_FID, +- COMPOUND_FID, FSCTL_GET_REPARSE_POINT, NULL, 0, +- CIFSMaxBufSize - +- MAX_SMB2_CREATE_RESPONSE_SIZE - +- MAX_SMB2_CLOSE_RESPONSE_SIZE); +- if (rc) +- goto query_rp_exit; +- +- smb2_set_next_command(tcon, &rqst[1]); +- smb2_set_related(&rqst[1]); +- +- /* Close */ +- rqst[2].rq_iov = &vars->close_iov; +- rqst[2].rq_nvec = 1; +- +- rc = SMB2_close_init(tcon, server, +- &rqst[2], COMPOUND_FID, COMPOUND_FID, false); +- if (rc) +- goto query_rp_exit; +- +- smb2_set_related(&rqst[2]); +- +- rc = compound_send_recv(xid, tcon->ses, server, +- flags, 3, rqst, +- resp_buftype, rsp_iov); +- +- ioctl_rsp = rsp_iov[1].iov_base; +- +- /* +- * Open was successful and we got an ioctl response. +- */ +- if (rc == 0) { +- /* See MS-FSCC 2.3.23 */ +- off = le32_to_cpu(ioctl_rsp->OutputOffset); +- count = le32_to_cpu(ioctl_rsp->OutputCount); +- if (check_add_overflow(off, count, &len) || +- len > rsp_iov[1].iov_len) { +- cifs_tcon_dbg(VFS, "%s: invalid ioctl: off=%d count=%d\n", +- __func__, off, count); +- rc = -EIO; +- goto query_rp_exit; +- } +- +- reparse_buf = (void *)((u8 *)ioctl_rsp + off); +- len = sizeof(*reparse_buf); +- if (count < len || +- count < le16_to_cpu(reparse_buf->ReparseDataLength) + len) { +- cifs_tcon_dbg(VFS, "%s: invalid ioctl: off=%d count=%d\n", +- __func__, off, count); +- rc = -EIO; +- goto query_rp_exit; +- } +- *tag = le32_to_cpu(reparse_buf->ReparseTag); +- *rsp = rsp_iov[1]; +- *rsp_buftype = resp_buftype[1]; +- resp_buftype[1] = CIFS_NO_BUFFER; +- } +- +- query_rp_exit: +- SMB2_open_free(&rqst[0]); +- SMB2_ioctl_free(&rqst[1]); +- SMB2_close_free(&rqst[2]); +- free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); +- free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); +- free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base); +- kfree(vars); +-out_free_path: +- kfree(utf16_path); +- return rc; +-} +- + static struct cifs_ntsd * + get_smb2_acl_by_fid(struct cifs_sb_info *cifs_sb, + const struct cifs_fid *cifsfid, u32 *pacllen, u32 info) +diff --git a/fs/smb/client/smb2proto.h b/fs/smb/client/smb2proto.h +index efa2f8fe23449..1e20f87a5f584 100644 +--- a/fs/smb/client/smb2proto.h ++++ b/fs/smb/client/smb2proto.h +@@ -62,6 +62,12 @@ struct inode *smb2_get_reparse_inode(struct cifs_open_info_data *data, + struct cifs_tcon *tcon, + const char *full_path, + struct kvec *iov); ++int smb2_query_reparse_point(const unsigned int xid, ++ struct cifs_tcon *tcon, ++ struct cifs_sb_info *cifs_sb, ++ const char *full_path, ++ u32 *tag, struct kvec *rsp, ++ int *rsp_buftype); + int smb2_query_path_info(const unsigned int xid, + struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, +-- +2.43.0 + diff --git a/queue-6.6/smb-client-delete-true-false-defines.patch b/queue-6.6/smb-client-delete-true-false-defines.patch new file mode 100644 index 0000000000..a84e843eab --- /dev/null +++ b/queue-6.6/smb-client-delete-true-false-defines.patch @@ -0,0 +1,41 @@ +From f5472d85a69f41100db29c7ebd615a143ff750f1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Tue, 23 Jan 2024 13:40:00 +0300 +Subject: smb: client: delete "true", "false" defines + +From: Alexey Dobriyan <adobriyan@gmail.com> + +[ Upstream commit 5d390df3bdd13d178eb2e02e60e9a480f7103f7b ] + +Kernel has its own official true/false definitions. + +The defines aren't even used in this file. + +Signed-off-by: Alexey Dobriyan <adobriyan@gmail.com> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/client/smbencrypt.c | 7 ------- + 1 file changed, 7 deletions(-) + +diff --git a/fs/smb/client/smbencrypt.c b/fs/smb/client/smbencrypt.c +index f0ce26414f173..1d1ee9f18f373 100644 +--- a/fs/smb/client/smbencrypt.c ++++ b/fs/smb/client/smbencrypt.c +@@ -26,13 +26,6 @@ + #include "cifsproto.h" + #include "../common/md4.h" + +-#ifndef false +-#define false 0 +-#endif +-#ifndef true +-#define true 1 +-#endif +- + /* following came from the other byteorder.h to avoid include conflicts */ + #define CVAL(buf,pos) (((unsigned char *)(buf))[pos]) + #define SSVALX(buf,pos,val) (CVAL(buf,pos)=(val)&0xFF,CVAL(buf,pos+1)=(val)>>8) +-- +2.43.0 + diff --git a/queue-6.6/smb-client-do-not-defer-close-open-handles-to-delete.patch b/queue-6.6/smb-client-do-not-defer-close-open-handles-to-delete.patch new file mode 100644 index 0000000000..49ced98f79 --- /dev/null +++ b/queue-6.6/smb-client-do-not-defer-close-open-handles-to-delete.patch @@ -0,0 +1,254 @@ +From a5fed64785dd763b30e3b29c2a9f160b797bb7d6 Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Wed, 1 May 2024 00:56:13 -0500 +Subject: smb: client: do not defer close open handles to deleted files + +From: Steve French <stfrench@microsoft.com> + +[ Upstream commit ffceb7640cbfe6ea60e7769e107451d63a2fe3d3 ] + +When a file/dentry has been deleted before closing all its open +handles, currently, closing them can add them to the deferred +close list. This can lead to problems in creating file with the +same name when the file is re-created before the deferred close +completes. This issue was seen while reusing a client's already +existing lease on a file for compound operations and xfstest 591 +failed because of the deferred close handle that remained valid +even after the file was deleted and was being reused to create a +file with the same name. The server in this case returns an error +on open with STATUS_DELETE_PENDING. Recreating the file would +fail till the deferred handles are closed (duration specified in +closetimeo). + +This patch fixes the issue by flagging all open handles for the +deleted file (file path to be precise) by setting +status_file_deleted to true in the cifsFileInfo structure. As per +the information classes specified in MS-FSCC, SMB2 query info +response from the server has a DeletePending field, set to true +to indicate that deletion has been requested on that file. If +this is the case, flag the open handles for this file too. + +When doing close in cifs_close for each of these handles, check the +value of this boolean field and do not defer close these handles +if the corresponding filepath has been deleted. + +Signed-off-by: Meetakshi Setiya <msetiya@microsoft.com> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/client/cifsglob.h | 1 + + fs/smb/client/cifsproto.h | 4 ++++ + fs/smb/client/file.c | 3 ++- + fs/smb/client/inode.c | 28 +++++++++++++++++++++++++--- + fs/smb/client/misc.c | 34 ++++++++++++++++++++++++++++++++++ + fs/smb/client/smb2inode.c | 9 ++++++++- + 6 files changed, 74 insertions(+), 5 deletions(-) + +diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h +index a149579910add..fdadda4024f46 100644 +--- a/fs/smb/client/cifsglob.h ++++ b/fs/smb/client/cifsglob.h +@@ -1427,6 +1427,7 @@ struct cifsFileInfo { + bool invalidHandle:1; /* file closed via session abend */ + bool swapfile:1; + bool oplock_break_cancelled:1; ++ bool status_file_deleted:1; /* file has been deleted */ + bool offload:1; /* offload final part of _put to a wq */ + unsigned int oplock_epoch; /* epoch from the lease break */ + __u32 oplock_level; /* oplock/lease level from the lease break */ +diff --git a/fs/smb/client/cifsproto.h b/fs/smb/client/cifsproto.h +index e9b38b279a6c5..50040990e70b9 100644 +--- a/fs/smb/client/cifsproto.h ++++ b/fs/smb/client/cifsproto.h +@@ -298,6 +298,10 @@ extern void cifs_close_all_deferred_files(struct cifs_tcon *cifs_tcon); + + extern void cifs_close_deferred_file_under_dentry(struct cifs_tcon *cifs_tcon, + const char *path); ++ ++extern void cifs_mark_open_handles_for_deleted_file(struct inode *inode, ++ const char *path); ++ + extern struct TCP_Server_Info * + cifs_get_tcp_session(struct smb3_fs_context *ctx, + struct TCP_Server_Info *primary_server); +diff --git a/fs/smb/client/file.c b/fs/smb/client/file.c +index 751ae89cefe36..8eaf195ef5604 100644 +--- a/fs/smb/client/file.c ++++ b/fs/smb/client/file.c +@@ -501,6 +501,7 @@ struct cifsFileInfo *cifs_new_fileinfo(struct cifs_fid *fid, struct file *file, + cfile->uid = current_fsuid(); + cfile->dentry = dget(dentry); + cfile->f_flags = file->f_flags; ++ cfile->status_file_deleted = false; + cfile->invalidHandle = false; + cfile->deferred_close_scheduled = false; + cfile->tlink = cifs_get_tlink(tlink); +@@ -1167,7 +1168,7 @@ int cifs_close(struct inode *inode, struct file *file) + if ((cifs_sb->ctx->closetimeo && cinode->oplock == CIFS_CACHE_RHW_FLG) + && cinode->lease_granted && + !test_bit(CIFS_INO_CLOSE_ON_LOCK, &cinode->flags) && +- dclose) { ++ dclose && !(cfile->status_file_deleted)) { + if (test_and_clear_bit(CIFS_INO_MODIFIED_ATTR, &cinode->flags)) { + inode_set_mtime_to_ts(inode, + inode_set_ctime_current(inode)); +diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c +index 156713186b3c9..2739cb8390804 100644 +--- a/fs/smb/client/inode.c ++++ b/fs/smb/client/inode.c +@@ -894,6 +894,9 @@ cifs_get_file_info(struct file *filp) + struct cifsFileInfo *cfile = filp->private_data; + struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); + struct TCP_Server_Info *server = tcon->ses->server; ++ struct dentry *dentry = filp->f_path.dentry; ++ void *page = alloc_dentry_path(); ++ const unsigned char *path; + + if (!server->ops->query_file_info) + return -ENOSYS; +@@ -908,7 +911,14 @@ cifs_get_file_info(struct file *filp) + data.symlink = true; + data.reparse.tag = IO_REPARSE_TAG_SYMLINK; + } ++ path = build_path_from_dentry(dentry, page); ++ if (IS_ERR(path)) { ++ free_dentry_path(page); ++ return PTR_ERR(path); ++ } + cifs_open_info_to_fattr(&fattr, &data, inode->i_sb); ++ if (fattr.cf_flags & CIFS_FATTR_DELETE_PENDING) ++ cifs_mark_open_handles_for_deleted_file(inode, path); + break; + case -EREMOTE: + cifs_create_junction_fattr(&fattr, inode->i_sb); +@@ -938,6 +948,7 @@ cifs_get_file_info(struct file *filp) + rc = cifs_fattr_to_inode(inode, &fattr, false); + cgfi_exit: + cifs_free_open_info(&data); ++ free_dentry_path(page); + free_xid(xid); + return rc; + } +@@ -1076,6 +1087,7 @@ static int reparse_info_to_fattr(struct cifs_open_info_data *data, + struct kvec rsp_iov, *iov = NULL; + int rsp_buftype = CIFS_NO_BUFFER; + u32 tag = data->reparse.tag; ++ struct inode *inode = NULL; + int rc = 0; + + if (!tag && server->ops->query_reparse_point) { +@@ -1115,8 +1127,12 @@ static int reparse_info_to_fattr(struct cifs_open_info_data *data, + + if (tcon->posix_extensions) + smb311_posix_info_to_fattr(fattr, data, sb); +- else ++ else { + cifs_open_info_to_fattr(fattr, data, sb); ++ inode = cifs_iget(sb, fattr); ++ if (inode && fattr->cf_flags & CIFS_FATTR_DELETE_PENDING) ++ cifs_mark_open_handles_for_deleted_file(inode, full_path); ++ } + out: + fattr->cf_cifstag = data->reparse.tag; + free_rsp_buf(rsp_buftype, rsp_iov.iov_base); +@@ -1171,6 +1187,8 @@ static int cifs_get_fattr(struct cifs_open_info_data *data, + full_path, fattr); + } else { + cifs_open_info_to_fattr(fattr, data, sb); ++ if (fattr->cf_flags & CIFS_FATTR_DELETE_PENDING) ++ cifs_mark_open_handles_for_deleted_file(*inode, full_path); + } + break; + case -EREMOTE: +@@ -1853,16 +1871,20 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry) + + psx_del_no_retry: + if (!rc) { +- if (inode) ++ if (inode) { ++ cifs_mark_open_handles_for_deleted_file(inode, full_path); + cifs_drop_nlink(inode); ++ } + } else if (rc == -ENOENT) { + d_drop(dentry); + } else if (rc == -EBUSY) { + if (server->ops->rename_pending_delete) { + rc = server->ops->rename_pending_delete(full_path, + dentry, xid); +- if (rc == 0) ++ if (rc == 0) { ++ cifs_mark_open_handles_for_deleted_file(inode, full_path); + cifs_drop_nlink(inode); ++ } + } + } else if ((rc == -EACCES) && (dosattr == 0) && inode) { + attrs = kzalloc(sizeof(*attrs), GFP_KERNEL); +diff --git a/fs/smb/client/misc.c b/fs/smb/client/misc.c +index d56959d02e36d..669d27b4d414a 100644 +--- a/fs/smb/client/misc.c ++++ b/fs/smb/client/misc.c +@@ -853,6 +853,40 @@ cifs_close_deferred_file_under_dentry(struct cifs_tcon *tcon, const char *path) + free_dentry_path(page); + } + ++/* ++ * If a dentry has been deleted, all corresponding open handles should know that ++ * so that we do not defer close them. ++ */ ++void cifs_mark_open_handles_for_deleted_file(struct inode *inode, ++ const char *path) ++{ ++ struct cifsFileInfo *cfile; ++ void *page; ++ const char *full_path; ++ struct cifsInodeInfo *cinode = CIFS_I(inode); ++ ++ page = alloc_dentry_path(); ++ spin_lock(&cinode->open_file_lock); ++ ++ /* ++ * note: we need to construct path from dentry and compare only if the ++ * inode has any hardlinks. When number of hardlinks is 1, we can just ++ * mark all open handles since they are going to be from the same file. ++ */ ++ if (inode->i_nlink > 1) { ++ list_for_each_entry(cfile, &cinode->openFileList, flist) { ++ full_path = build_path_from_dentry(cfile->dentry, page); ++ if (!IS_ERR(full_path) && strcmp(full_path, path) == 0) ++ cfile->status_file_deleted = true; ++ } ++ } else { ++ list_for_each_entry(cfile, &cinode->openFileList, flist) ++ cfile->status_file_deleted = true; ++ } ++ spin_unlock(&cinode->open_file_lock); ++ free_dentry_path(page); ++} ++ + /* parses DFS referral V3 structure + * caller is responsible for freeing target_nodes + * returns: +diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c +index e452c59c13e2d..0b7b083352919 100644 +--- a/fs/smb/client/smb2inode.c ++++ b/fs/smb/client/smb2inode.c +@@ -561,8 +561,15 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, + case SMB2_OP_DELETE: + if (rc) + trace_smb3_delete_err(xid, ses->Suid, tcon->tid, rc); +- else ++ else { ++ /* ++ * If dentry (hence, inode) is NULL, lease break is going to ++ * take care of degrading leases on handles for deleted files. ++ */ ++ if (inode) ++ cifs_mark_open_handles_for_deleted_file(inode, full_path); + trace_smb3_delete_done(xid, ses->Suid, tcon->tid); ++ } + break; + case SMB2_OP_MKDIR: + if (rc) +-- +2.43.0 + diff --git a/queue-6.6/smb-client-don-t-clobber-i_rdev-from-cached-reparse-.patch b/queue-6.6/smb-client-don-t-clobber-i_rdev-from-cached-reparse-.patch new file mode 100644 index 0000000000..cd02460551 --- /dev/null +++ b/queue-6.6/smb-client-don-t-clobber-i_rdev-from-cached-reparse-.patch @@ -0,0 +1,44 @@ +From a2226021710bf984972a8ea9b57beccb5f37598e Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Fri, 19 Jan 2024 01:08:29 -0300 +Subject: smb: client: don't clobber ->i_rdev from cached reparse points + +From: Paulo Alcantara <pc@manguebit.com> + +[ Upstream commit 66c9314b61ed5b7bfcff0d89359aa0f975c0ab53 ] + +Don't clobber ->i_rdev from valid reparse inodes over readdir(2) as it +can't be provided by query dir responses. + +Signed-off-by: Paulo Alcantara <pc@manguebit.com> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/client/readdir.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/fs/smb/client/readdir.c b/fs/smb/client/readdir.c +index 47f5a82bc2507..73ff9bd059682 100644 +--- a/fs/smb/client/readdir.c ++++ b/fs/smb/client/readdir.c +@@ -133,14 +133,14 @@ cifs_prime_dcache(struct dentry *parent, struct qstr *name, + * Query dir responses don't provide enough + * information about reparse points other than + * their reparse tags. Save an invalidation by +- * not clobbering the existing mode, size and +- * symlink target (if any) when reparse tag and +- * ctime haven't changed. ++ * not clobbering some existing attributes when ++ * reparse tag and ctime haven't changed. + */ + rc = 0; + if (fattr->cf_cifsattrs & ATTR_REPARSE) { + if (likely(reparse_inode_match(inode, fattr))) { + fattr->cf_mode = inode->i_mode; ++ fattr->cf_rdev = inode->i_rdev; + fattr->cf_eof = CIFS_I(inode)->server_eof; + fattr->cf_symlink_target = NULL; + } else { +-- +2.43.0 + diff --git a/queue-6.6/smb-client-extend-smb2_compound_op-to-accept-more-co.patch b/queue-6.6/smb-client-extend-smb2_compound_op-to-accept-more-co.patch new file mode 100644 index 0000000000..1969dedf56 --- /dev/null +++ b/queue-6.6/smb-client-extend-smb2_compound_op-to-accept-more-co.patch @@ -0,0 +1,1038 @@ +From 4a9e27a2b8864b8c30d49e0a70c85bc8cc124df9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Sun, 28 Apr 2024 01:09:59 -0500 +Subject: smb: client: extend smb2_compound_op() to accept more commands + +From: Steve French <stfrench@microsoft.com> + +[ Upstream commit 3322960ce222997b1663ffa69e691b2edfec4ac9 ] + +Make smb2_compound_op() accept up to MAX_COMPOUND(5) commands to be +sent over a single compounded request. + +This will allow next commits to read and write reparse files through a +single roundtrip to the server. + +Signed-off-by: Paulo Alcantara (SUSE) <pc@manguebit.com> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/client/cifsglob.h | 4 +- + fs/smb/client/smb2inode.c | 783 +++++++++++++++++++------------------- + 2 files changed, 403 insertions(+), 384 deletions(-) + +diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h +index b598c7ed497bb..cb86b1bf69b58 100644 +--- a/fs/smb/client/cifsglob.h ++++ b/fs/smb/client/cifsglob.h +@@ -2272,8 +2272,8 @@ static inline void cifs_sg_set_buf(struct sg_table *sgtable, + + struct smb2_compound_vars { + struct cifs_open_parms oparms; +- struct kvec rsp_iov[3]; +- struct smb_rqst rqst[3]; ++ struct kvec rsp_iov[MAX_COMPOUND]; ++ struct smb_rqst rqst[MAX_COMPOUND]; + struct kvec open_iov[SMB2_CREATE_IOV_SIZE]; + struct kvec qi_iov; + struct kvec io_iov[SMB2_IOCTL_IOV_SIZE]; +diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c +index 6cac0b107a2d0..4c66187eccdd2 100644 +--- a/fs/smb/client/smb2inode.c ++++ b/fs/smb/client/smb2inode.c +@@ -26,15 +26,6 @@ + #include "cached_dir.h" + #include "smb2status.h" + +-static void +-free_set_inf_compound(struct smb_rqst *rqst) +-{ +- if (rqst[1].rq_iov) +- SMB2_set_info_free(&rqst[1]); +- if (rqst[2].rq_iov) +- SMB2_close_free(&rqst[2]); +-} +- + static inline __u32 file_create_options(struct dentry *dentry) + { + struct cifsInodeInfo *ci; +@@ -57,8 +48,9 @@ static inline __u32 file_create_options(struct dentry *dentry) + */ + static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, const char *full_path, +- __u32 desired_access, __u32 create_disposition, __u32 create_options, +- umode_t mode, void *ptr, int command, struct cifsFileInfo *cfile, ++ __u32 desired_access, __u32 create_disposition, ++ __u32 create_options, umode_t mode, struct kvec *in_iov, ++ int *cmds, int num_cmds, struct cifsFileInfo *cfile, + __u8 **extbuf, size_t *extbuflen, + struct kvec *out_iov, int *out_buftype) + { +@@ -71,8 +63,8 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_fid fid; + struct cifs_ses *ses = tcon->ses; + struct TCP_Server_Info *server; +- int num_rqst = 0; +- int resp_buftype[3]; ++ int num_rqst = 0, i; ++ int resp_buftype[MAX_COMPOUND]; + struct smb2_query_info_rsp *qi_rsp = NULL; + struct cifs_open_info_data *idata; + int flags = 0; +@@ -92,7 +84,8 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + +- resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER; ++ for (i = 0; i < ARRAY_SIZE(resp_buftype); i++) ++ resp_buftype[i] = CIFS_NO_BUFFER; + + /* We already have a handle so we can skip the open */ + if (cfile) +@@ -130,242 +123,246 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, + num_rqst++; + rc = 0; + +- /* Operation */ +- switch (command) { +- case SMB2_OP_QUERY_INFO: +- rqst[num_rqst].rq_iov = &vars->qi_iov; +- rqst[num_rqst].rq_nvec = 1; +- +- if (cfile) +- rc = SMB2_query_info_init(tcon, server, +- &rqst[num_rqst], +- cfile->fid.persistent_fid, +- cfile->fid.volatile_fid, +- FILE_ALL_INFORMATION, +- SMB2_O_INFO_FILE, 0, +- sizeof(struct smb2_file_all_info) + +- PATH_MAX * 2, 0, NULL); +- else { +- rc = SMB2_query_info_init(tcon, server, +- &rqst[num_rqst], +- COMPOUND_FID, +- COMPOUND_FID, +- FILE_ALL_INFORMATION, +- SMB2_O_INFO_FILE, 0, +- sizeof(struct smb2_file_all_info) + +- PATH_MAX * 2, 0, NULL); +- if (!rc) { +- smb2_set_next_command(tcon, &rqst[num_rqst]); +- smb2_set_related(&rqst[num_rqst]); ++ for (i = 0; i < num_cmds; i++) { ++ /* Operation */ ++ switch (cmds[i]) { ++ case SMB2_OP_QUERY_INFO: ++ rqst[num_rqst].rq_iov = &vars->qi_iov; ++ rqst[num_rqst].rq_nvec = 1; ++ ++ if (cfile) { ++ rc = SMB2_query_info_init(tcon, server, ++ &rqst[num_rqst], ++ cfile->fid.persistent_fid, ++ cfile->fid.volatile_fid, ++ FILE_ALL_INFORMATION, ++ SMB2_O_INFO_FILE, 0, ++ sizeof(struct smb2_file_all_info) + ++ PATH_MAX * 2, 0, NULL); ++ } else { ++ rc = SMB2_query_info_init(tcon, server, ++ &rqst[num_rqst], ++ COMPOUND_FID, ++ COMPOUND_FID, ++ FILE_ALL_INFORMATION, ++ SMB2_O_INFO_FILE, 0, ++ sizeof(struct smb2_file_all_info) + ++ PATH_MAX * 2, 0, NULL); ++ if (!rc) { ++ smb2_set_next_command(tcon, &rqst[num_rqst]); ++ smb2_set_related(&rqst[num_rqst]); ++ } + } +- } + +- if (rc) +- goto finished; +- num_rqst++; +- trace_smb3_query_info_compound_enter(xid, ses->Suid, tcon->tid, +- full_path); +- break; +- case SMB2_OP_POSIX_QUERY_INFO: +- rqst[num_rqst].rq_iov = &vars->qi_iov; +- rqst[num_rqst].rq_nvec = 1; +- +- if (cfile) +- rc = SMB2_query_info_init(tcon, server, +- &rqst[num_rqst], +- cfile->fid.persistent_fid, +- cfile->fid.volatile_fid, +- SMB_FIND_FILE_POSIX_INFO, +- SMB2_O_INFO_FILE, 0, ++ if (rc) ++ goto finished; ++ num_rqst++; ++ trace_smb3_query_info_compound_enter(xid, ses->Suid, ++ tcon->tid, full_path); ++ break; ++ case SMB2_OP_POSIX_QUERY_INFO: ++ rqst[num_rqst].rq_iov = &vars->qi_iov; ++ rqst[num_rqst].rq_nvec = 1; ++ ++ if (cfile) { + /* TBD: fix following to allow for longer SIDs */ +- sizeof(struct smb311_posix_qinfo *) + (PATH_MAX * 2) + +- (sizeof(struct cifs_sid) * 2), 0, NULL); +- else { +- rc = SMB2_query_info_init(tcon, server, +- &rqst[num_rqst], +- COMPOUND_FID, +- COMPOUND_FID, +- SMB_FIND_FILE_POSIX_INFO, +- SMB2_O_INFO_FILE, 0, +- sizeof(struct smb311_posix_qinfo *) + (PATH_MAX * 2) + +- (sizeof(struct cifs_sid) * 2), 0, NULL); +- if (!rc) { +- smb2_set_next_command(tcon, &rqst[num_rqst]); +- smb2_set_related(&rqst[num_rqst]); ++ rc = SMB2_query_info_init(tcon, server, ++ &rqst[num_rqst], ++ cfile->fid.persistent_fid, ++ cfile->fid.volatile_fid, ++ SMB_FIND_FILE_POSIX_INFO, ++ SMB2_O_INFO_FILE, 0, ++ sizeof(struct smb311_posix_qinfo *) + ++ (PATH_MAX * 2) + ++ (sizeof(struct cifs_sid) * 2), 0, NULL); ++ } else { ++ rc = SMB2_query_info_init(tcon, server, ++ &rqst[num_rqst], ++ COMPOUND_FID, ++ COMPOUND_FID, ++ SMB_FIND_FILE_POSIX_INFO, ++ SMB2_O_INFO_FILE, 0, ++ sizeof(struct smb311_posix_qinfo *) + ++ (PATH_MAX * 2) + ++ (sizeof(struct cifs_sid) * 2), 0, NULL); ++ if (!rc) { ++ smb2_set_next_command(tcon, &rqst[num_rqst]); ++ smb2_set_related(&rqst[num_rqst]); ++ } + } +- } + +- if (rc) +- goto finished; +- num_rqst++; +- trace_smb3_posix_query_info_compound_enter(xid, ses->Suid, tcon->tid, full_path); +- break; +- case SMB2_OP_DELETE: +- trace_smb3_delete_enter(xid, ses->Suid, tcon->tid, full_path); +- break; +- case SMB2_OP_MKDIR: +- /* +- * Directories are created through parameters in the +- * SMB2_open() call. +- */ +- trace_smb3_mkdir_enter(xid, ses->Suid, tcon->tid, full_path); +- break; +- case SMB2_OP_RMDIR: +- rqst[num_rqst].rq_iov = &vars->si_iov[0]; +- rqst[num_rqst].rq_nvec = 1; +- +- size[0] = 1; /* sizeof __u8 See MS-FSCC section 2.4.11 */ +- data[0] = &delete_pending[0]; +- +- rc = SMB2_set_info_init(tcon, server, +- &rqst[num_rqst], COMPOUND_FID, +- COMPOUND_FID, current->tgid, +- FILE_DISPOSITION_INFORMATION, +- SMB2_O_INFO_FILE, 0, data, size); +- if (rc) +- goto finished; +- smb2_set_next_command(tcon, &rqst[num_rqst]); +- smb2_set_related(&rqst[num_rqst++]); +- trace_smb3_rmdir_enter(xid, ses->Suid, tcon->tid, full_path); +- break; +- case SMB2_OP_SET_EOF: +- rqst[num_rqst].rq_iov = &vars->si_iov[0]; +- rqst[num_rqst].rq_nvec = 1; ++ if (rc) ++ goto finished; ++ num_rqst++; ++ trace_smb3_posix_query_info_compound_enter(xid, ses->Suid, ++ tcon->tid, full_path); ++ break; ++ case SMB2_OP_DELETE: ++ trace_smb3_delete_enter(xid, ses->Suid, tcon->tid, full_path); ++ break; ++ case SMB2_OP_MKDIR: ++ /* ++ * Directories are created through parameters in the ++ * SMB2_open() call. ++ */ ++ trace_smb3_mkdir_enter(xid, ses->Suid, tcon->tid, full_path); ++ break; ++ case SMB2_OP_RMDIR: ++ rqst[num_rqst].rq_iov = &vars->si_iov[0]; ++ rqst[num_rqst].rq_nvec = 1; + +- size[0] = 8; /* sizeof __le64 */ +- data[0] = ptr; ++ size[0] = 1; /* sizeof __u8 See MS-FSCC section 2.4.11 */ ++ data[0] = &delete_pending[0]; + +- if (cfile) { + rc = SMB2_set_info_init(tcon, server, +- &rqst[num_rqst], +- cfile->fid.persistent_fid, +- cfile->fid.volatile_fid, +- current->tgid, +- FILE_END_OF_FILE_INFORMATION, +- SMB2_O_INFO_FILE, 0, +- data, size); +- } else { +- rc = SMB2_set_info_init(tcon, server, +- &rqst[num_rqst], +- COMPOUND_FID, +- COMPOUND_FID, +- current->tgid, +- FILE_END_OF_FILE_INFORMATION, +- SMB2_O_INFO_FILE, 0, +- data, size); +- if (!rc) { +- smb2_set_next_command(tcon, &rqst[num_rqst]); +- smb2_set_related(&rqst[num_rqst]); ++ &rqst[num_rqst], COMPOUND_FID, ++ COMPOUND_FID, current->tgid, ++ FILE_DISPOSITION_INFORMATION, ++ SMB2_O_INFO_FILE, 0, data, size); ++ if (rc) ++ goto finished; ++ smb2_set_next_command(tcon, &rqst[num_rqst]); ++ smb2_set_related(&rqst[num_rqst++]); ++ trace_smb3_rmdir_enter(xid, ses->Suid, tcon->tid, full_path); ++ break; ++ case SMB2_OP_SET_EOF: ++ rqst[num_rqst].rq_iov = &vars->si_iov[0]; ++ rqst[num_rqst].rq_nvec = 1; ++ ++ size[0] = in_iov[i].iov_len; ++ data[0] = in_iov[i].iov_base; ++ ++ if (cfile) { ++ rc = SMB2_set_info_init(tcon, server, ++ &rqst[num_rqst], ++ cfile->fid.persistent_fid, ++ cfile->fid.volatile_fid, ++ current->tgid, ++ FILE_END_OF_FILE_INFORMATION, ++ SMB2_O_INFO_FILE, 0, ++ data, size); ++ } else { ++ rc = SMB2_set_info_init(tcon, server, ++ &rqst[num_rqst], ++ COMPOUND_FID, ++ COMPOUND_FID, ++ current->tgid, ++ FILE_END_OF_FILE_INFORMATION, ++ SMB2_O_INFO_FILE, 0, ++ data, size); ++ if (!rc) { ++ smb2_set_next_command(tcon, &rqst[num_rqst]); ++ smb2_set_related(&rqst[num_rqst]); ++ } + } +- } +- if (rc) +- goto finished; +- num_rqst++; +- trace_smb3_set_eof_enter(xid, ses->Suid, tcon->tid, full_path); +- break; +- case SMB2_OP_SET_INFO: +- rqst[num_rqst].rq_iov = &vars->si_iov[0]; +- rqst[num_rqst].rq_nvec = 1; +- +- +- size[0] = sizeof(FILE_BASIC_INFO); +- data[0] = ptr; +- +- if (cfile) +- rc = SMB2_set_info_init(tcon, server, +- &rqst[num_rqst], +- cfile->fid.persistent_fid, +- cfile->fid.volatile_fid, current->tgid, +- FILE_BASIC_INFORMATION, +- SMB2_O_INFO_FILE, 0, data, size); +- else { +- rc = SMB2_set_info_init(tcon, server, +- &rqst[num_rqst], +- COMPOUND_FID, +- COMPOUND_FID, current->tgid, +- FILE_BASIC_INFORMATION, +- SMB2_O_INFO_FILE, 0, data, size); +- if (!rc) { +- smb2_set_next_command(tcon, &rqst[num_rqst]); +- smb2_set_related(&rqst[num_rqst]); ++ if (rc) ++ goto finished; ++ num_rqst++; ++ trace_smb3_set_eof_enter(xid, ses->Suid, tcon->tid, full_path); ++ break; ++ case SMB2_OP_SET_INFO: ++ rqst[num_rqst].rq_iov = &vars->si_iov[0]; ++ rqst[num_rqst].rq_nvec = 1; ++ ++ size[0] = in_iov[i].iov_len; ++ data[0] = in_iov[i].iov_base; ++ ++ if (cfile) { ++ rc = SMB2_set_info_init(tcon, server, ++ &rqst[num_rqst], ++ cfile->fid.persistent_fid, ++ cfile->fid.volatile_fid, current->tgid, ++ FILE_BASIC_INFORMATION, ++ SMB2_O_INFO_FILE, 0, data, size); ++ } else { ++ rc = SMB2_set_info_init(tcon, server, ++ &rqst[num_rqst], ++ COMPOUND_FID, ++ COMPOUND_FID, current->tgid, ++ FILE_BASIC_INFORMATION, ++ SMB2_O_INFO_FILE, 0, data, size); ++ if (!rc) { ++ smb2_set_next_command(tcon, &rqst[num_rqst]); ++ smb2_set_related(&rqst[num_rqst]); ++ } + } +- } + +- if (rc) +- goto finished; +- num_rqst++; +- trace_smb3_set_info_compound_enter(xid, ses->Suid, tcon->tid, +- full_path); +- break; +- case SMB2_OP_RENAME: +- rqst[num_rqst].rq_iov = &vars->si_iov[0]; +- rqst[num_rqst].rq_nvec = 2; ++ if (rc) ++ goto finished; ++ num_rqst++; ++ trace_smb3_set_info_compound_enter(xid, ses->Suid, ++ tcon->tid, full_path); ++ break; ++ case SMB2_OP_RENAME: ++ rqst[num_rqst].rq_iov = &vars->si_iov[0]; ++ rqst[num_rqst].rq_nvec = 2; + +- len = (2 * UniStrnlen((wchar_t *)ptr, PATH_MAX)); ++ len = in_iov[i].iov_len; + +- vars->rename_info.ReplaceIfExists = 1; +- vars->rename_info.RootDirectory = 0; +- vars->rename_info.FileNameLength = cpu_to_le32(len); ++ vars->rename_info.ReplaceIfExists = 1; ++ vars->rename_info.RootDirectory = 0; ++ vars->rename_info.FileNameLength = cpu_to_le32(len); + +- size[0] = sizeof(struct smb2_file_rename_info); +- data[0] = &vars->rename_info; ++ size[0] = sizeof(struct smb2_file_rename_info); ++ data[0] = &vars->rename_info; + +- size[1] = len + 2 /* null */; +- data[1] = (__le16 *)ptr; ++ size[1] = len + 2 /* null */; ++ data[1] = in_iov[i].iov_base; + +- if (cfile) +- rc = SMB2_set_info_init(tcon, server, +- &rqst[num_rqst], +- cfile->fid.persistent_fid, +- cfile->fid.volatile_fid, +- current->tgid, FILE_RENAME_INFORMATION, +- SMB2_O_INFO_FILE, 0, data, size); +- else { +- rc = SMB2_set_info_init(tcon, server, +- &rqst[num_rqst], +- COMPOUND_FID, COMPOUND_FID, +- current->tgid, FILE_RENAME_INFORMATION, +- SMB2_O_INFO_FILE, 0, data, size); +- if (!rc) { +- smb2_set_next_command(tcon, &rqst[num_rqst]); +- smb2_set_related(&rqst[num_rqst]); ++ if (cfile) { ++ rc = SMB2_set_info_init(tcon, server, ++ &rqst[num_rqst], ++ cfile->fid.persistent_fid, ++ cfile->fid.volatile_fid, ++ current->tgid, FILE_RENAME_INFORMATION, ++ SMB2_O_INFO_FILE, 0, data, size); ++ } else { ++ rc = SMB2_set_info_init(tcon, server, ++ &rqst[num_rqst], ++ COMPOUND_FID, COMPOUND_FID, ++ current->tgid, FILE_RENAME_INFORMATION, ++ SMB2_O_INFO_FILE, 0, data, size); ++ if (!rc) { ++ smb2_set_next_command(tcon, &rqst[num_rqst]); ++ smb2_set_related(&rqst[num_rqst]); ++ } + } +- } +- if (rc) +- goto finished; +- num_rqst++; +- trace_smb3_rename_enter(xid, ses->Suid, tcon->tid, full_path); +- break; +- case SMB2_OP_HARDLINK: +- rqst[num_rqst].rq_iov = &vars->si_iov[0]; +- rqst[num_rqst].rq_nvec = 2; ++ if (rc) ++ goto finished; ++ num_rqst++; ++ trace_smb3_rename_enter(xid, ses->Suid, tcon->tid, full_path); ++ break; ++ case SMB2_OP_HARDLINK: ++ rqst[num_rqst].rq_iov = &vars->si_iov[0]; ++ rqst[num_rqst].rq_nvec = 2; + +- len = (2 * UniStrnlen((wchar_t *)ptr, PATH_MAX)); ++ len = in_iov[i].iov_len; + +- vars->link_info.ReplaceIfExists = 0; +- vars->link_info.RootDirectory = 0; +- vars->link_info.FileNameLength = cpu_to_le32(len); ++ vars->link_info.ReplaceIfExists = 0; ++ vars->link_info.RootDirectory = 0; ++ vars->link_info.FileNameLength = cpu_to_le32(len); + +- size[0] = sizeof(struct smb2_file_link_info); +- data[0] = &vars->link_info; ++ size[0] = sizeof(struct smb2_file_link_info); ++ data[0] = &vars->link_info; + +- size[1] = len + 2 /* null */; +- data[1] = (__le16 *)ptr; ++ size[1] = len + 2 /* null */; ++ data[1] = in_iov[i].iov_base; + +- rc = SMB2_set_info_init(tcon, server, +- &rqst[num_rqst], COMPOUND_FID, +- COMPOUND_FID, current->tgid, +- FILE_LINK_INFORMATION, +- SMB2_O_INFO_FILE, 0, data, size); +- if (rc) +- goto finished; +- smb2_set_next_command(tcon, &rqst[num_rqst]); +- smb2_set_related(&rqst[num_rqst++]); +- trace_smb3_hardlink_enter(xid, ses->Suid, tcon->tid, full_path); +- break; +- default: +- cifs_dbg(VFS, "Invalid command\n"); +- rc = -EINVAL; ++ rc = SMB2_set_info_init(tcon, server, ++ &rqst[num_rqst], COMPOUND_FID, ++ COMPOUND_FID, current->tgid, ++ FILE_LINK_INFORMATION, ++ SMB2_O_INFO_FILE, 0, data, size); ++ if (rc) ++ goto finished; ++ smb2_set_next_command(tcon, &rqst[num_rqst]); ++ smb2_set_related(&rqst[num_rqst++]); ++ trace_smb3_hardlink_enter(xid, ses->Suid, tcon->tid, full_path); ++ break; ++ default: ++ cifs_dbg(VFS, "Invalid command\n"); ++ rc = -EINVAL; ++ } + } + if (rc) + goto finished; +@@ -397,145 +394,142 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, + rqst, resp_buftype, + rsp_iov); + +- finished: +- SMB2_open_free(&rqst[0]); ++finished: ++ num_rqst = 0; ++ SMB2_open_free(&rqst[num_rqst++]); + if (rc == -EREMCHG) { + pr_warn_once("server share %s deleted\n", tcon->tree_name); + tcon->need_reconnect = true; + } + +- switch (command) { +- case SMB2_OP_QUERY_INFO: +- idata = ptr; +- if (rc == 0 && cfile && cfile->symlink_target) { +- idata->symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL); +- if (!idata->symlink_target) +- rc = -ENOMEM; +- } +- if (rc == 0) { +- qi_rsp = (struct smb2_query_info_rsp *) +- rsp_iov[1].iov_base; +- rc = smb2_validate_and_copy_iov( +- le16_to_cpu(qi_rsp->OutputBufferOffset), +- le32_to_cpu(qi_rsp->OutputBufferLength), +- &rsp_iov[1], sizeof(idata->fi), (char *)&idata->fi); +- } +- if (rqst[1].rq_iov) +- SMB2_query_info_free(&rqst[1]); +- if (rqst[2].rq_iov) +- SMB2_close_free(&rqst[2]); +- if (rc) +- trace_smb3_query_info_compound_err(xid, ses->Suid, +- tcon->tid, rc); +- else +- trace_smb3_query_info_compound_done(xid, ses->Suid, +- tcon->tid); +- break; +- case SMB2_OP_POSIX_QUERY_INFO: +- idata = ptr; +- if (rc == 0 && cfile && cfile->symlink_target) { +- idata->symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL); +- if (!idata->symlink_target) +- rc = -ENOMEM; +- } +- if (rc == 0) { +- qi_rsp = (struct smb2_query_info_rsp *) +- rsp_iov[1].iov_base; +- rc = smb2_validate_and_copy_iov( +- le16_to_cpu(qi_rsp->OutputBufferOffset), +- le32_to_cpu(qi_rsp->OutputBufferLength), +- &rsp_iov[1], sizeof(idata->posix_fi) /* add SIDs */, +- (char *)&idata->posix_fi); +- } +- if (rc == 0) { +- unsigned int length = le32_to_cpu(qi_rsp->OutputBufferLength); +- +- if (length > sizeof(idata->posix_fi)) { +- char *base = (char *)rsp_iov[1].iov_base + +- le16_to_cpu(qi_rsp->OutputBufferOffset) + +- sizeof(idata->posix_fi); +- *extbuflen = length - sizeof(idata->posix_fi); +- *extbuf = kmemdup(base, *extbuflen, GFP_KERNEL); +- if (!*extbuf) ++ for (i = 0; i < num_cmds; i++) { ++ switch (cmds[i]) { ++ case SMB2_OP_QUERY_INFO: ++ idata = in_iov[i].iov_base; ++ if (rc == 0 && cfile && cfile->symlink_target) { ++ idata->symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL); ++ if (!idata->symlink_target) + rc = -ENOMEM; +- } else { +- rc = -EINVAL; + } ++ if (rc == 0) { ++ qi_rsp = (struct smb2_query_info_rsp *) ++ rsp_iov[i + 1].iov_base; ++ rc = smb2_validate_and_copy_iov( ++ le16_to_cpu(qi_rsp->OutputBufferOffset), ++ le32_to_cpu(qi_rsp->OutputBufferLength), ++ &rsp_iov[i + 1], sizeof(idata->fi), (char *)&idata->fi); ++ } ++ SMB2_query_info_free(&rqst[num_rqst++]); ++ if (rc) ++ trace_smb3_query_info_compound_err(xid, ses->Suid, ++ tcon->tid, rc); ++ else ++ trace_smb3_query_info_compound_done(xid, ses->Suid, ++ tcon->tid); ++ break; ++ case SMB2_OP_POSIX_QUERY_INFO: ++ idata = in_iov[i].iov_base; ++ if (rc == 0 && cfile && cfile->symlink_target) { ++ idata->symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL); ++ if (!idata->symlink_target) ++ rc = -ENOMEM; ++ } ++ if (rc == 0) { ++ qi_rsp = (struct smb2_query_info_rsp *) ++ rsp_iov[i + 1].iov_base; ++ rc = smb2_validate_and_copy_iov( ++ le16_to_cpu(qi_rsp->OutputBufferOffset), ++ le32_to_cpu(qi_rsp->OutputBufferLength), ++ &rsp_iov[i + 1], sizeof(idata->posix_fi) /* add SIDs */, ++ (char *)&idata->posix_fi); ++ } ++ if (rc == 0) { ++ unsigned int length = le32_to_cpu(qi_rsp->OutputBufferLength); ++ ++ if (length > sizeof(idata->posix_fi)) { ++ char *base = (char *)rsp_iov[i + 1].iov_base + ++ le16_to_cpu(qi_rsp->OutputBufferOffset) + ++ sizeof(idata->posix_fi); ++ *extbuflen = length - sizeof(idata->posix_fi); ++ *extbuf = kmemdup(base, *extbuflen, GFP_KERNEL); ++ if (!*extbuf) ++ rc = -ENOMEM; ++ } else { ++ rc = -EINVAL; ++ } ++ } ++ SMB2_query_info_free(&rqst[num_rqst++]); ++ if (rc) ++ trace_smb3_posix_query_info_compound_err(xid, ses->Suid, ++ tcon->tid, rc); ++ else ++ trace_smb3_posix_query_info_compound_done(xid, ses->Suid, ++ tcon->tid); ++ break; ++ case SMB2_OP_DELETE: ++ if (rc) ++ trace_smb3_delete_err(xid, ses->Suid, tcon->tid, rc); ++ else ++ trace_smb3_delete_done(xid, ses->Suid, tcon->tid); ++ break; ++ case SMB2_OP_MKDIR: ++ if (rc) ++ trace_smb3_mkdir_err(xid, ses->Suid, tcon->tid, rc); ++ else ++ trace_smb3_mkdir_done(xid, ses->Suid, tcon->tid); ++ break; ++ case SMB2_OP_HARDLINK: ++ if (rc) ++ trace_smb3_hardlink_err(xid, ses->Suid, tcon->tid, rc); ++ else ++ trace_smb3_hardlink_done(xid, ses->Suid, tcon->tid); ++ SMB2_set_info_free(&rqst[num_rqst++]); ++ break; ++ case SMB2_OP_RENAME: ++ if (rc) ++ trace_smb3_rename_err(xid, ses->Suid, tcon->tid, rc); ++ else ++ trace_smb3_rename_done(xid, ses->Suid, tcon->tid); ++ SMB2_set_info_free(&rqst[num_rqst++]); ++ break; ++ case SMB2_OP_RMDIR: ++ if (rc) ++ trace_smb3_rmdir_err(xid, ses->Suid, tcon->tid, rc); ++ else ++ trace_smb3_rmdir_done(xid, ses->Suid, tcon->tid); ++ SMB2_set_info_free(&rqst[num_rqst++]); ++ break; ++ case SMB2_OP_SET_EOF: ++ if (rc) ++ trace_smb3_set_eof_err(xid, ses->Suid, tcon->tid, rc); ++ else ++ trace_smb3_set_eof_done(xid, ses->Suid, tcon->tid); ++ SMB2_set_info_free(&rqst[num_rqst++]); ++ break; ++ case SMB2_OP_SET_INFO: ++ if (rc) ++ trace_smb3_set_info_compound_err(xid, ses->Suid, ++ tcon->tid, rc); ++ else ++ trace_smb3_set_info_compound_done(xid, ses->Suid, ++ tcon->tid); ++ SMB2_set_info_free(&rqst[num_rqst++]); ++ break; + } +- if (rqst[1].rq_iov) +- SMB2_query_info_free(&rqst[1]); +- if (rqst[2].rq_iov) +- SMB2_close_free(&rqst[2]); +- if (rc) +- trace_smb3_posix_query_info_compound_err(xid, ses->Suid, tcon->tid, rc); +- else +- trace_smb3_posix_query_info_compound_done(xid, ses->Suid, tcon->tid); +- break; +- case SMB2_OP_DELETE: +- if (rc) +- trace_smb3_delete_err(xid, ses->Suid, tcon->tid, rc); +- else +- trace_smb3_delete_done(xid, ses->Suid, tcon->tid); +- if (rqst[1].rq_iov) +- SMB2_close_free(&rqst[1]); +- break; +- case SMB2_OP_MKDIR: +- if (rc) +- trace_smb3_mkdir_err(xid, ses->Suid, tcon->tid, rc); +- else +- trace_smb3_mkdir_done(xid, ses->Suid, tcon->tid); +- if (rqst[1].rq_iov) +- SMB2_close_free(&rqst[1]); +- break; +- case SMB2_OP_HARDLINK: +- if (rc) +- trace_smb3_hardlink_err(xid, ses->Suid, tcon->tid, rc); +- else +- trace_smb3_hardlink_done(xid, ses->Suid, tcon->tid); +- free_set_inf_compound(rqst); +- break; +- case SMB2_OP_RENAME: +- if (rc) +- trace_smb3_rename_err(xid, ses->Suid, tcon->tid, rc); +- else +- trace_smb3_rename_done(xid, ses->Suid, tcon->tid); +- free_set_inf_compound(rqst); +- break; +- case SMB2_OP_RMDIR: +- if (rc) +- trace_smb3_rmdir_err(xid, ses->Suid, tcon->tid, rc); +- else +- trace_smb3_rmdir_done(xid, ses->Suid, tcon->tid); +- free_set_inf_compound(rqst); +- break; +- case SMB2_OP_SET_EOF: +- if (rc) +- trace_smb3_set_eof_err(xid, ses->Suid, tcon->tid, rc); +- else +- trace_smb3_set_eof_done(xid, ses->Suid, tcon->tid); +- free_set_inf_compound(rqst); +- break; +- case SMB2_OP_SET_INFO: +- if (rc) +- trace_smb3_set_info_compound_err(xid, ses->Suid, +- tcon->tid, rc); +- else +- trace_smb3_set_info_compound_done(xid, ses->Suid, +- tcon->tid); +- free_set_inf_compound(rqst); +- break; + } ++ SMB2_close_free(&rqst[num_rqst]); + + if (cfile) + cifsFileInfo_put(cfile); + ++ num_cmds += 2; + if (out_iov && out_buftype) { +- memcpy(out_iov, rsp_iov, 3 * sizeof(*out_iov)); +- memcpy(out_buftype, resp_buftype, 3 * sizeof(*out_buftype)); ++ memcpy(out_iov, rsp_iov, num_cmds * sizeof(*out_iov)); ++ memcpy(out_buftype, resp_buftype, ++ num_cmds * sizeof(*out_buftype)); + } else { +- free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); +- free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); +- free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base); ++ for (i = 0; i < num_cmds; i++) ++ free_rsp_buf(resp_buftype[i], rsp_iov[i].iov_base); + } + kfree(vars); + return rc; +@@ -581,9 +575,10 @@ int smb2_query_path_info(const unsigned int xid, + struct cifsFileInfo *cfile; + struct cached_fid *cfid = NULL; + struct smb2_hdr *hdr; +- struct kvec out_iov[3] = {}; ++ struct kvec in_iov, out_iov[3] = {}; + int out_buftype[3] = {}; + bool islink; ++ int cmd = SMB2_OP_QUERY_INFO; + int rc, rc2; + + data->adjust_tz = false; +@@ -605,10 +600,14 @@ int smb2_query_path_info(const unsigned int xid, + return rc; + } + ++ in_iov.iov_base = data; ++ in_iov.iov_len = sizeof(*data); ++ + cifs_get_readable_path(tcon, full_path, &cfile); +- rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, FILE_READ_ATTRIBUTES, FILE_OPEN, +- create_options, ACL_NO_MODE, data, SMB2_OP_QUERY_INFO, cfile, +- NULL, NULL, out_iov, out_buftype); ++ rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, ++ FILE_READ_ATTRIBUTES, FILE_OPEN, ++ create_options, ACL_NO_MODE, &in_iov, ++ &cmd, 1, cfile, NULL, NULL, out_iov, out_buftype); + hdr = out_iov[0].iov_base; + /* + * If first iov is unset, then SMB session was dropped or we've got a +@@ -629,9 +628,8 @@ int smb2_query_path_info(const unsigned int xid, + cifs_get_readable_path(tcon, full_path, &cfile); + rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, + FILE_READ_ATTRIBUTES, FILE_OPEN, +- create_options, ACL_NO_MODE, data, +- SMB2_OP_QUERY_INFO, cfile, NULL, NULL, +- NULL, NULL); ++ create_options, ACL_NO_MODE, &in_iov, ++ &cmd, 1, cfile, NULL, NULL, NULL, NULL); + break; + case -EREMOTE: + break; +@@ -666,12 +664,13 @@ int smb311_posix_query_path_info(const unsigned int xid, + int rc; + __u32 create_options = 0; + struct cifsFileInfo *cfile; +- struct kvec out_iov[3] = {}; ++ struct kvec in_iov, out_iov[3] = {}; + int out_buftype[3] = {}; + __u8 *sidsbuf = NULL; + __u8 *sidsbuf_end = NULL; + size_t sidsbuflen = 0; + size_t owner_len, group_len; ++ int cmd = SMB2_OP_POSIX_QUERY_INFO; + + data->adjust_tz = false; + data->reparse_point = false; +@@ -682,11 +681,14 @@ int smb311_posix_query_path_info(const unsigned int xid, + * when we already have an open file handle for this. For now this is fast enough + * (always using the compounded version). + */ ++ in_iov.iov_base = data; ++ in_iov.iov_len = sizeof(*data); + + cifs_get_readable_path(tcon, full_path, &cfile); +- rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, FILE_READ_ATTRIBUTES, FILE_OPEN, +- create_options, ACL_NO_MODE, data, SMB2_OP_POSIX_QUERY_INFO, cfile, +- &sidsbuf, &sidsbuflen, out_iov, out_buftype); ++ rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, ++ FILE_READ_ATTRIBUTES, FILE_OPEN, ++ create_options, ACL_NO_MODE, &in_iov, &cmd, 1, ++ cfile, &sidsbuf, &sidsbuflen, out_iov, out_buftype); + /* + * If first iov is unset, then SMB session was dropped or we've got a + * cached open file (@cfile). +@@ -705,10 +707,10 @@ int smb311_posix_query_path_info(const unsigned int xid, + create_options |= OPEN_REPARSE_POINT; + /* Failed on a symbolic link - query a reparse point info */ + cifs_get_readable_path(tcon, full_path, &cfile); +- rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, FILE_READ_ATTRIBUTES, +- FILE_OPEN, create_options, ACL_NO_MODE, data, +- SMB2_OP_POSIX_QUERY_INFO, cfile, +- &sidsbuf, &sidsbuflen, NULL, NULL); ++ rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, ++ FILE_READ_ATTRIBUTES, FILE_OPEN, ++ create_options, ACL_NO_MODE, &in_iov, &cmd, 1, ++ cfile, &sidsbuf, &sidsbuflen, NULL, NULL); + break; + } + +@@ -746,7 +748,8 @@ smb2_mkdir(const unsigned int xid, struct inode *parent_inode, umode_t mode, + { + return smb2_compound_op(xid, tcon, cifs_sb, name, + FILE_WRITE_ATTRIBUTES, FILE_CREATE, +- CREATE_NOT_FILE, mode, NULL, SMB2_OP_MKDIR, ++ CREATE_NOT_FILE, mode, NULL, ++ &(int){SMB2_OP_MKDIR}, 1, + NULL, NULL, NULL, NULL, NULL); + } + +@@ -755,21 +758,24 @@ smb2_mkdir_setinfo(struct inode *inode, const char *name, + struct cifs_sb_info *cifs_sb, struct cifs_tcon *tcon, + const unsigned int xid) + { +- FILE_BASIC_INFO data; ++ FILE_BASIC_INFO data = {}; + struct cifsInodeInfo *cifs_i; + struct cifsFileInfo *cfile; ++ struct kvec in_iov; + u32 dosattrs; + int tmprc; + +- memset(&data, 0, sizeof(data)); ++ in_iov.iov_base = &data; ++ in_iov.iov_len = sizeof(data); + cifs_i = CIFS_I(inode); + dosattrs = cifs_i->cifsAttrs | ATTR_READONLY; + data.Attributes = cpu_to_le32(dosattrs); + cifs_get_writable_path(tcon, name, FIND_WR_ANY, &cfile); + tmprc = smb2_compound_op(xid, tcon, cifs_sb, name, + FILE_WRITE_ATTRIBUTES, FILE_CREATE, +- CREATE_NOT_FILE, ACL_NO_MODE, +- &data, SMB2_OP_SET_INFO, cfile, NULL, NULL, NULL, NULL); ++ CREATE_NOT_FILE, ACL_NO_MODE, &in_iov, ++ &(int){SMB2_OP_SET_INFO}, 1, ++ cfile, NULL, NULL, NULL, NULL); + if (tmprc == 0) + cifs_i->cifsAttrs = dosattrs; + } +@@ -779,9 +785,10 @@ smb2_rmdir(const unsigned int xid, struct cifs_tcon *tcon, const char *name, + struct cifs_sb_info *cifs_sb) + { + drop_cached_dir_by_name(xid, tcon, name, cifs_sb); +- return smb2_compound_op(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN, +- CREATE_NOT_FILE, ACL_NO_MODE, +- NULL, SMB2_OP_RMDIR, NULL, NULL, NULL, NULL, NULL); ++ return smb2_compound_op(xid, tcon, cifs_sb, name, ++ DELETE, FILE_OPEN, CREATE_NOT_FILE, ++ ACL_NO_MODE, NULL, &(int){SMB2_OP_RMDIR}, 1, ++ NULL, NULL, NULL, NULL, NULL); + } + + int +@@ -790,7 +797,8 @@ smb2_unlink(const unsigned int xid, struct cifs_tcon *tcon, const char *name, + { + return smb2_compound_op(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN, + CREATE_DELETE_ON_CLOSE | OPEN_REPARSE_POINT, +- ACL_NO_MODE, NULL, SMB2_OP_DELETE, NULL, NULL, NULL, NULL, NULL); ++ ACL_NO_MODE, NULL, &(int){SMB2_OP_DELETE}, 1, ++ NULL, NULL, NULL, NULL, NULL); + } + + static int smb2_set_path_attr(const unsigned int xid, struct cifs_tcon *tcon, +@@ -799,6 +807,7 @@ static int smb2_set_path_attr(const unsigned int xid, struct cifs_tcon *tcon, + __u32 create_options, __u32 access, + int command, struct cifsFileInfo *cfile) + { ++ struct kvec in_iov; + __le16 *smb2_to_name = NULL; + int rc; + +@@ -807,9 +816,12 @@ static int smb2_set_path_attr(const unsigned int xid, struct cifs_tcon *tcon, + rc = -ENOMEM; + goto smb2_rename_path; + } ++ in_iov.iov_base = smb2_to_name; ++ in_iov.iov_len = 2 * UniStrnlen((wchar_t *)smb2_to_name, PATH_MAX); ++ + rc = smb2_compound_op(xid, tcon, cifs_sb, from_name, access, +- FILE_OPEN, create_options, ACL_NO_MODE, smb2_to_name, +- command, cfile, NULL, NULL, NULL, NULL); ++ FILE_OPEN, 0, ACL_NO_MODE, &in_iov, ++ &command, 1, cfile, NULL, NULL, NULL, NULL); + smb2_rename_path: + kfree(smb2_to_name); + return rc; +@@ -849,13 +861,18 @@ smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon, + const char *full_path, __u64 size, + struct cifs_sb_info *cifs_sb, bool set_alloc) + { +- __le64 eof = cpu_to_le64(size); + struct cifsFileInfo *cfile; ++ struct kvec in_iov; ++ __le64 eof = cpu_to_le64(size); + ++ in_iov.iov_base = &eof; ++ in_iov.iov_len = sizeof(eof); + cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile); + return smb2_compound_op(xid, tcon, cifs_sb, full_path, +- FILE_WRITE_DATA, FILE_OPEN, 0, ACL_NO_MODE, +- &eof, SMB2_OP_SET_EOF, cfile, NULL, NULL, NULL, NULL); ++ FILE_WRITE_DATA, FILE_OPEN, ++ 0, ACL_NO_MODE, &in_iov, ++ &(int){SMB2_OP_SET_EOF}, 1, ++ cfile, NULL, NULL, NULL, NULL); + } + + int +@@ -866,6 +883,7 @@ smb2_set_file_info(struct inode *inode, const char *full_path, + struct tcon_link *tlink; + struct cifs_tcon *tcon; + struct cifsFileInfo *cfile; ++ struct kvec in_iov = { .iov_base = buf, .iov_len = sizeof(*buf), }; + int rc; + + if ((buf->CreationTime == 0) && (buf->LastAccessTime == 0) && +@@ -881,7 +899,8 @@ smb2_set_file_info(struct inode *inode, const char *full_path, + cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile); + rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, + FILE_WRITE_ATTRIBUTES, FILE_OPEN, +- 0, ACL_NO_MODE, buf, SMB2_OP_SET_INFO, cfile, ++ 0, ACL_NO_MODE, &in_iov, ++ &(int){SMB2_OP_SET_INFO}, 1, cfile, + NULL, NULL, NULL, NULL); + cifs_put_tlink(tlink); + return rc; +-- +2.43.0 + diff --git a/queue-6.6/smb-client-fix-a-null-vs-is_err-check-in-wsl_set_xat.patch b/queue-6.6/smb-client-fix-a-null-vs-is_err-check-in-wsl_set_xat.patch new file mode 100644 index 0000000000..c349bbad78 --- /dev/null +++ b/queue-6.6/smb-client-fix-a-null-vs-is_err-check-in-wsl_set_xat.patch @@ -0,0 +1,37 @@ +From 13d034c20cb52315348b157d7f710316c3602cf7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Wed, 31 Jan 2024 10:10:18 +0300 +Subject: smb: client: Fix a NULL vs IS_ERR() check in wsl_set_xattrs() + +From: Dan Carpenter <dan.carpenter@linaro.org> + +[ Upstream commit e0e1e09b2c41d383a2483f2ee5227b724860ced1 ] + +This was intended to be an IS_ERR() check. The ea_create_context() +function doesn't return NULL. + +Fixes: 1eab17fe485c ("smb: client: add support for WSL reparse points") +Reviewed-by: Paulo Alcantara <pc@manguebit.com> +Signed-off-by: Dan Carpenter <dan.carpenter@linaro.org> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/client/reparse.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/fs/smb/client/reparse.c b/fs/smb/client/reparse.c +index b240ccc9c887c..24feeaa32280e 100644 +--- a/fs/smb/client/reparse.c ++++ b/fs/smb/client/reparse.c +@@ -230,7 +230,7 @@ static int wsl_set_xattrs(struct inode *inode, umode_t _mode, + } + + cc = ea_create_context(dlen, &cc_len); +- if (!cc) ++ if (IS_ERR(cc)) + return PTR_ERR(cc); + + ea = &cc->ea; +-- +2.43.0 + diff --git a/queue-6.6/smb-client-fix-minor-whitespace-errors-and-warnings.patch b/queue-6.6/smb-client-fix-minor-whitespace-errors-and-warnings.patch new file mode 100644 index 0000000000..52a4df4966 --- /dev/null +++ b/queue-6.6/smb-client-fix-minor-whitespace-errors-and-warnings.patch @@ -0,0 +1,143 @@ +From 80cb25d41a829bb8f9f4145de61f35567ea5c90f Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Sun, 26 Nov 2023 20:52:56 -0800 +Subject: smb: client: Fix minor whitespace errors and warnings + +From: Pierre Mariani <pierre.mariani@gmail.com> + +[ Upstream commit 0108ce08aed195d200ffbad74c1948bbaefe6625 ] + +Fixes no-op checkpatch errors and warnings. + +Signed-off-by: Pierre Mariani <pierre.mariani@gmail.com> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/client/connect.c | 25 +++++++++++++++++-------- + 1 file changed, 17 insertions(+), 8 deletions(-) + +diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c +index cb3bed8364e07..2466b28379ff8 100644 +--- a/fs/smb/client/connect.c ++++ b/fs/smb/client/connect.c +@@ -501,6 +501,7 @@ static int reconnect_target_unlocked(struct TCP_Server_Info *server, struct dfs_ + static int reconnect_dfs_server(struct TCP_Server_Info *server) + { + struct dfs_cache_tgt_iterator *target_hint = NULL; ++ + DFS_CACHE_TGT_LIST(tl); + int num_targets = 0; + int rc = 0; +@@ -763,6 +764,7 @@ cifs_read_from_socket(struct TCP_Server_Info *server, char *buf, + { + struct msghdr smb_msg = {}; + struct kvec iov = {.iov_base = buf, .iov_len = to_read}; ++ + iov_iter_kvec(&smb_msg.msg_iter, ITER_DEST, &iov, 1, to_read); + + return cifs_readv_from_socket(server, &smb_msg); +@@ -1418,11 +1420,13 @@ cifs_match_ipaddr(struct sockaddr *srcaddr, struct sockaddr *rhs) + case AF_INET: { + struct sockaddr_in *saddr4 = (struct sockaddr_in *)srcaddr; + struct sockaddr_in *vaddr4 = (struct sockaddr_in *)rhs; ++ + return (saddr4->sin_addr.s_addr == vaddr4->sin_addr.s_addr); + } + case AF_INET6: { + struct sockaddr_in6 *saddr6 = (struct sockaddr_in6 *)srcaddr; + struct sockaddr_in6 *vaddr6 = (struct sockaddr_in6 *)rhs; ++ + return (ipv6_addr_equal(&saddr6->sin6_addr, &vaddr6->sin6_addr) + && saddr6->sin6_scope_id == vaddr6->sin6_scope_id); + } +@@ -2607,8 +2611,8 @@ cifs_get_tcon(struct cifs_ses *ses, struct smb3_fs_context *ctx) + rc = -EOPNOTSUPP; + goto out_fail; + } else { +- cifs_dbg(VFS, "Check vers= mount option. SMB3.11 " +- "disabled but required for POSIX extensions\n"); ++ cifs_dbg(VFS, ++ "Check vers= mount option. SMB3.11 disabled but required for POSIX extensions\n"); + rc = -EOPNOTSUPP; + goto out_fail; + } +@@ -2751,7 +2755,6 @@ cifs_put_tlink(struct tcon_link *tlink) + if (!IS_ERR(tlink_tcon(tlink))) + cifs_put_tcon(tlink_tcon(tlink)); + kfree(tlink); +- return; + } + + static int +@@ -2892,6 +2895,7 @@ static inline void + cifs_reclassify_socket4(struct socket *sock) + { + struct sock *sk = sock->sk; ++ + BUG_ON(!sock_allow_reclassification(sk)); + sock_lock_init_class_and_name(sk, "slock-AF_INET-CIFS", + &cifs_slock_key[0], "sk_lock-AF_INET-CIFS", &cifs_key[0]); +@@ -2901,6 +2905,7 @@ static inline void + cifs_reclassify_socket6(struct socket *sock) + { + struct sock *sk = sock->sk; ++ + BUG_ON(!sock_allow_reclassification(sk)); + sock_lock_init_class_and_name(sk, "slock-AF_INET6-CIFS", + &cifs_slock_key[1], "sk_lock-AF_INET6-CIFS", &cifs_key[1]); +@@ -2935,15 +2940,18 @@ static int + bind_socket(struct TCP_Server_Info *server) + { + int rc = 0; ++ + if (server->srcaddr.ss_family != AF_UNSPEC) { + /* Bind to the specified local IP address */ + struct socket *socket = server->ssocket; ++ + rc = kernel_bind(socket, + (struct sockaddr *) &server->srcaddr, + sizeof(server->srcaddr)); + if (rc < 0) { + struct sockaddr_in *saddr4; + struct sockaddr_in6 *saddr6; ++ + saddr4 = (struct sockaddr_in *)&server->srcaddr; + saddr6 = (struct sockaddr_in6 *)&server->srcaddr; + if (saddr6->sin6_family == AF_INET6) +@@ -3173,6 +3181,7 @@ void reset_cifs_unix_caps(unsigned int xid, struct cifs_tcon *tcon, + + if (!CIFSSMBQFSUnixInfo(xid, tcon)) { + __u64 cap = le64_to_cpu(tcon->fsUnixInfo.Capability); ++ + cifs_dbg(FYI, "unix caps which server supports %lld\n", cap); + /* + * check for reconnect case in which we do not +@@ -3698,7 +3707,7 @@ CIFSTCon(const unsigned int xid, struct cifs_ses *ses, + smb_buffer_response = smb_buffer; + + header_assemble(smb_buffer, SMB_COM_TREE_CONNECT_ANDX, +- NULL /*no tid */ , 4 /*wct */ ); ++ NULL /*no tid */, 4 /*wct */); + + smb_buffer->Mid = get_next_mid(ses->server); + smb_buffer->Uid = ses->Suid; +@@ -3717,12 +3726,12 @@ CIFSTCon(const unsigned int xid, struct cifs_ses *ses, + if (ses->server->sign) + smb_buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; + +- if (ses->capabilities & CAP_STATUS32) { ++ if (ses->capabilities & CAP_STATUS32) + smb_buffer->Flags2 |= SMBFLG2_ERR_STATUS; +- } +- if (ses->capabilities & CAP_DFS) { ++ ++ if (ses->capabilities & CAP_DFS) + smb_buffer->Flags2 |= SMBFLG2_DFS; +- } ++ + if (ses->capabilities & CAP_UNICODE) { + smb_buffer->Flags2 |= SMBFLG2_UNICODE; + length = +-- +2.43.0 + diff --git a/queue-6.6/smb-client-fix-null-ptr-deref-in-cifs_mark_open_hand.patch b/queue-6.6/smb-client-fix-null-ptr-deref-in-cifs_mark_open_hand.patch new file mode 100644 index 0000000000..4a21c9394f --- /dev/null +++ b/queue-6.6/smb-client-fix-null-ptr-deref-in-cifs_mark_open_hand.patch @@ -0,0 +1,107 @@ +From ffb331b662deb3cacd96d0bba349782cc40b3584 Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Mon, 8 Apr 2024 18:32:17 -0300 +Subject: smb: client: fix NULL ptr deref in + cifs_mark_open_handles_for_deleted_file() + +From: Paulo Alcantara <pc@manguebit.com> + +[ Upstream commit ec4535b2a1d709d3a1fbec26739c672f13c98a7b ] + +cifs_get_fattr() may be called with a NULL inode, so check for a +non-NULL inode before calling +cifs_mark_open_handles_for_deleted_file(). + +This fixes the following oops: + + mount.cifs //srv/share /mnt -o ...,vers=3.1.1 + cd /mnt + touch foo; tail -f foo & + rm foo + cat foo + + BUG: kernel NULL pointer dereference, address: 00000000000005c0 + #PF: supervisor read access in kernel mode + #PF: error_code(0x0000) - not-present page + PGD 0 P4D 0 + Oops: 0000 [#1] PREEMPT SMP NOPTI + CPU: 2 PID: 696 Comm: cat Not tainted 6.9.0-rc2 #1 + Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS + 1.16.3-1.fc39 04/01/2014 + RIP: 0010:__lock_acquire+0x5d/0x1c70 + Code: 00 00 44 8b a4 24 a0 00 00 00 45 85 f6 0f 84 bb 06 00 00 8b 2d + 48 e2 95 01 45 89 c3 41 89 d2 45 89 c8 85 ed 0 0 <48> 81 3f 40 7a 76 + 83 44 0f 44 d8 83 fe 01 0f 86 1b 03 00 00 31 d2 + RSP: 0018:ffffc90000b37490 EFLAGS: 00010002 + RAX: 0000000000000000 RBX: ffff888110021ec0 RCX: 0000000000000000 + RDX: 0000000000000000 RSI: 0000000000000000 RDI: 00000000000005c0 + RBP: 0000000000000001 R08: 0000000000000000 R09: 0000000000000000 + R10: 0000000000000000 R11: 0000000000000001 R12: 0000000000000000 + R13: 0000000000000000 R14: 0000000000000001 R15: 0000000000000200 + FS: 00007f2a1fa08740(0000) GS:ffff888157a00000(0000) + knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: + 0000000080050033 + CR2: 00000000000005c0 CR3: 000000011ac7c000 CR4: 0000000000750ef0 + PKRU: 55555554 + Call Trace: + <TASK> + ? __die+0x23/0x70 + ? page_fault_oops+0x180/0x490 + ? srso_alias_return_thunk+0x5/0xfbef5 + ? exc_page_fault+0x70/0x230 + ? asm_exc_page_fault+0x26/0x30 + ? __lock_acquire+0x5d/0x1c70 + ? srso_alias_return_thunk+0x5/0xfbef5 + ? srso_alias_return_thunk+0x5/0xfbef5 + lock_acquire+0xc0/0x2d0 + ? cifs_mark_open_handles_for_deleted_file+0x3a/0x100 [cifs] + ? srso_alias_return_thunk+0x5/0xfbef5 + ? kmem_cache_alloc+0x2d9/0x370 + _raw_spin_lock+0x34/0x80 + ? cifs_mark_open_handles_for_deleted_file+0x3a/0x100 [cifs] + cifs_mark_open_handles_for_deleted_file+0x3a/0x100 [cifs] + cifs_get_fattr+0x24c/0x940 [cifs] + ? srso_alias_return_thunk+0x5/0xfbef5 + cifs_get_inode_info+0x96/0x120 [cifs] + cifs_lookup+0x16e/0x800 [cifs] + cifs_atomic_open+0xc7/0x5d0 [cifs] + ? lookup_open.isra.0+0x3ce/0x5f0 + ? __pfx_cifs_atomic_open+0x10/0x10 [cifs] + lookup_open.isra.0+0x3ce/0x5f0 + path_openat+0x42b/0xc30 + ? srso_alias_return_thunk+0x5/0xfbef5 + ? srso_alias_return_thunk+0x5/0xfbef5 + ? srso_alias_return_thunk+0x5/0xfbef5 + do_filp_open+0xc4/0x170 + do_sys_openat2+0xab/0xe0 + __x64_sys_openat+0x57/0xa0 + do_syscall_64+0xc1/0x1e0 + entry_SYSCALL_64_after_hwframe+0x72/0x7a + +Fixes: ffceb7640cbf ("smb: client: do not defer close open handles to deleted files") +Reviewed-by: Meetakshi Setiya <msetiya@microsoft.com> +Reviewed-by: Bharath SM <bharathsm@microsoft.com> +Signed-off-by: Paulo Alcantara (Red Hat) <pc@manguebit.com> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/client/inode.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c +index b304215a4d668..9cdbc3ccc1d14 100644 +--- a/fs/smb/client/inode.c ++++ b/fs/smb/client/inode.c +@@ -1105,7 +1105,8 @@ static int cifs_get_fattr(struct cifs_open_info_data *data, + } else { + cifs_open_info_to_fattr(fattr, data, sb); + } +- if (!rc && fattr->cf_flags & CIFS_FATTR_DELETE_PENDING) ++ if (!rc && *inode && ++ (fattr->cf_flags & CIFS_FATTR_DELETE_PENDING)) + cifs_mark_open_handles_for_deleted_file(*inode, full_path); + break; + case -EREMOTE: +-- +2.43.0 + diff --git a/queue-6.6/smb-client-fix-potential-broken-compound-request.patch b/queue-6.6/smb-client-fix-potential-broken-compound-request.patch new file mode 100644 index 0000000000..01df675b69 --- /dev/null +++ b/queue-6.6/smb-client-fix-potential-broken-compound-request.patch @@ -0,0 +1,200 @@ +From 567981f811fd30bbfce09d7a58dabf7122248a4c Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Thu, 25 Jan 2024 17:04:05 -0300 +Subject: smb: client: fix potential broken compound request + +From: Paulo Alcantara <pc@manguebit.com> + +[ Upstream commit 6914d288c63682e20e0f6e1e0b8e8f5847012d67 ] + +Now that smb2_compound_op() can accept up to 5 commands in a single +compound request, set the appropriate NextCommand and related flags to +all subsequent commands as well as handling the case where a valid +@cfile is passed and therefore skipping create and close requests in +the compound chain. + +This fix a potential broken compound request that could be sent from +smb2_get_reparse_inode() if the client found a valid open +file (@cfile) prior to calling smb2_compound_op(). + +Signed-off-by: Paulo Alcantara <pc@manguebit.com> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/client/smb2inode.c | 106 ++++++++++++++++++++++---------------- + 1 file changed, 63 insertions(+), 43 deletions(-) + +diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c +index add90eb8fc165..33f3fffcb8277 100644 +--- a/fs/smb/client/smb2inode.c ++++ b/fs/smb/client/smb2inode.c +@@ -223,14 +223,13 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, + SMB2_O_INFO_FILE, 0, + sizeof(struct smb2_file_all_info) + + PATH_MAX * 2, 0, NULL); +- if (!rc) { +- smb2_set_next_command(tcon, &rqst[num_rqst]); +- smb2_set_related(&rqst[num_rqst]); +- } + } +- +- if (rc) ++ if (!rc && (!cfile || num_rqst > 1)) { ++ smb2_set_next_command(tcon, &rqst[num_rqst]); ++ smb2_set_related(&rqst[num_rqst]); ++ } else if (rc) { + goto finished; ++ } + num_rqst++; + trace_smb3_query_info_compound_enter(xid, ses->Suid, + tcon->tid, full_path); +@@ -260,14 +259,13 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, + sizeof(struct smb311_posix_qinfo *) + + (PATH_MAX * 2) + + (sizeof(struct cifs_sid) * 2), 0, NULL); +- if (!rc) { +- smb2_set_next_command(tcon, &rqst[num_rqst]); +- smb2_set_related(&rqst[num_rqst]); +- } + } +- +- if (rc) ++ if (!rc && (!cfile || num_rqst > 1)) { ++ smb2_set_next_command(tcon, &rqst[num_rqst]); ++ smb2_set_related(&rqst[num_rqst]); ++ } else if (rc) { + goto finished; ++ } + num_rqst++; + trace_smb3_posix_query_info_compound_enter(xid, ses->Suid, + tcon->tid, full_path); +@@ -325,13 +323,13 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, + FILE_END_OF_FILE_INFORMATION, + SMB2_O_INFO_FILE, 0, + data, size); +- if (!rc) { +- smb2_set_next_command(tcon, &rqst[num_rqst]); +- smb2_set_related(&rqst[num_rqst]); +- } + } +- if (rc) ++ if (!rc && (!cfile || num_rqst > 1)) { ++ smb2_set_next_command(tcon, &rqst[num_rqst]); ++ smb2_set_related(&rqst[num_rqst]); ++ } else if (rc) { + goto finished; ++ } + num_rqst++; + trace_smb3_set_eof_enter(xid, ses->Suid, tcon->tid, full_path); + break; +@@ -356,14 +354,13 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, + COMPOUND_FID, current->tgid, + FILE_BASIC_INFORMATION, + SMB2_O_INFO_FILE, 0, data, size); +- if (!rc) { +- smb2_set_next_command(tcon, &rqst[num_rqst]); +- smb2_set_related(&rqst[num_rqst]); +- } + } +- +- if (rc) ++ if (!rc && (!cfile || num_rqst > 1)) { ++ smb2_set_next_command(tcon, &rqst[num_rqst]); ++ smb2_set_related(&rqst[num_rqst]); ++ } else if (rc) { + goto finished; ++ } + num_rqst++; + trace_smb3_set_info_compound_enter(xid, ses->Suid, + tcon->tid, full_path); +@@ -397,13 +394,13 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, + COMPOUND_FID, COMPOUND_FID, + current->tgid, FILE_RENAME_INFORMATION, + SMB2_O_INFO_FILE, 0, data, size); +- if (!rc) { +- smb2_set_next_command(tcon, &rqst[num_rqst]); +- smb2_set_related(&rqst[num_rqst]); +- } + } +- if (rc) ++ if (!rc && (!cfile || num_rqst > 1)) { ++ smb2_set_next_command(tcon, &rqst[num_rqst]); ++ smb2_set_related(&rqst[num_rqst]); ++ } else if (rc) { + goto finished; ++ } + num_rqst++; + trace_smb3_rename_enter(xid, ses->Suid, tcon->tid, full_path); + break; +@@ -438,15 +435,27 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, + rqst[num_rqst].rq_iov = vars->io_iov; + rqst[num_rqst].rq_nvec = ARRAY_SIZE(vars->io_iov); + +- rc = SMB2_ioctl_init(tcon, server, &rqst[num_rqst], +- COMPOUND_FID, COMPOUND_FID, +- FSCTL_SET_REPARSE_POINT, +- in_iov[i].iov_base, +- in_iov[i].iov_len, 0); +- if (rc) ++ if (cfile) { ++ rc = SMB2_ioctl_init(tcon, server, &rqst[num_rqst], ++ cfile->fid.persistent_fid, ++ cfile->fid.volatile_fid, ++ FSCTL_SET_REPARSE_POINT, ++ in_iov[i].iov_base, ++ in_iov[i].iov_len, 0); ++ } else { ++ rc = SMB2_ioctl_init(tcon, server, &rqst[num_rqst], ++ COMPOUND_FID, COMPOUND_FID, ++ FSCTL_SET_REPARSE_POINT, ++ in_iov[i].iov_base, ++ in_iov[i].iov_len, 0); ++ } ++ if (!rc && (!cfile || num_rqst > 1)) { ++ smb2_set_next_command(tcon, &rqst[num_rqst]); ++ smb2_set_related(&rqst[num_rqst]); ++ } else if (rc) { + goto finished; +- smb2_set_next_command(tcon, &rqst[num_rqst]); +- smb2_set_related(&rqst[num_rqst++]); ++ } ++ num_rqst++; + trace_smb3_set_reparse_compound_enter(xid, ses->Suid, + tcon->tid, full_path); + break; +@@ -454,14 +463,25 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, + rqst[num_rqst].rq_iov = vars->io_iov; + rqst[num_rqst].rq_nvec = ARRAY_SIZE(vars->io_iov); + +- rc = SMB2_ioctl_init(tcon, server, &rqst[num_rqst], +- COMPOUND_FID, COMPOUND_FID, +- FSCTL_GET_REPARSE_POINT, +- NULL, 0, CIFSMaxBufSize); +- if (rc) ++ if (cfile) { ++ rc = SMB2_ioctl_init(tcon, server, &rqst[num_rqst], ++ cfile->fid.persistent_fid, ++ cfile->fid.volatile_fid, ++ FSCTL_GET_REPARSE_POINT, ++ NULL, 0, CIFSMaxBufSize); ++ } else { ++ rc = SMB2_ioctl_init(tcon, server, &rqst[num_rqst], ++ COMPOUND_FID, COMPOUND_FID, ++ FSCTL_GET_REPARSE_POINT, ++ NULL, 0, CIFSMaxBufSize); ++ } ++ if (!rc && (!cfile || num_rqst > 1)) { ++ smb2_set_next_command(tcon, &rqst[num_rqst]); ++ smb2_set_related(&rqst[num_rqst]); ++ } else if (rc) { + goto finished; +- smb2_set_next_command(tcon, &rqst[num_rqst]); +- smb2_set_related(&rqst[num_rqst++]); ++ } ++ num_rqst++; + trace_smb3_get_reparse_compound_enter(xid, ses->Suid, + tcon->tid, full_path); + break; +-- +2.43.0 + diff --git a/queue-6.6/smb-client-get-rid-of-smb311_posix_query_path_info.patch b/queue-6.6/smb-client-get-rid-of-smb311_posix_query_path_info.patch new file mode 100644 index 0000000000..39b97a016b --- /dev/null +++ b/queue-6.6/smb-client-get-rid-of-smb311_posix_query_path_info.patch @@ -0,0 +1,199 @@ +From 6205c8fa40d7e8b86c3e7315e901d4fccb5e7bea Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Fri, 19 Jan 2024 01:08:28 -0300 +Subject: smb: client: get rid of smb311_posix_query_path_info() + +From: Paulo Alcantara <pc@manguebit.com> + +[ Upstream commit f83709b9e0eb7048d74ba4515f268c6eacbce9c9 ] + +Merge smb311_posix_query_path_info into ->query_path_info() to get rid +of duplicate code. + +Signed-off-by: Paulo Alcantara <pc@manguebit.com> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/client/inode.c | 4 +- + fs/smb/client/smb2inode.c | 115 +++++++++++--------------------------- + 2 files changed, 36 insertions(+), 83 deletions(-) + +diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c +index 0110589acb853..d7e3da8489a0b 100644 +--- a/fs/smb/client/inode.c ++++ b/fs/smb/client/inode.c +@@ -1313,6 +1313,7 @@ static int smb311_posix_get_fattr(struct cifs_open_info_data *data, + const unsigned int xid) + { + struct cifs_open_info_data tmp_data = {}; ++ struct TCP_Server_Info *server; + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); + struct cifs_tcon *tcon; + struct tcon_link *tlink; +@@ -1323,12 +1324,13 @@ static int smb311_posix_get_fattr(struct cifs_open_info_data *data, + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + tcon = tlink_tcon(tlink); ++ server = tcon->ses->server; + + /* + * 1. Fetch file metadata if not provided (data) + */ + if (!data) { +- rc = smb311_posix_query_path_info(xid, tcon, cifs_sb, ++ rc = server->ops->query_path_info(xid, tcon, cifs_sb, + full_path, &tmp_data); + data = &tmp_data; + } +diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c +index 4cd4b8a63316d..dfa4e5362213f 100644 +--- a/fs/smb/client/smb2inode.c ++++ b/fs/smb/client/smb2inode.c +@@ -699,7 +699,7 @@ int smb2_query_path_info(const unsigned int xid, + struct smb2_hdr *hdr; + struct kvec in_iov[2], out_iov[3] = {}; + int out_buftype[3] = {}; +- int cmds[2] = { SMB2_OP_QUERY_INFO, }; ++ int cmds[2]; + bool islink; + int i, num_cmds; + int rc, rc2; +@@ -707,20 +707,36 @@ int smb2_query_path_info(const unsigned int xid, + data->adjust_tz = false; + data->reparse_point = false; + +- if (strcmp(full_path, "")) +- rc = -ENOENT; +- else +- rc = open_cached_dir(xid, tcon, full_path, cifs_sb, false, &cfid); +- /* If it is a root and its handle is cached then use it */ +- if (!rc) { +- if (cfid->file_all_info_is_valid) { +- memcpy(&data->fi, &cfid->file_all_info, sizeof(data->fi)); ++ /* ++ * BB TODO: Add support for using cached root handle in SMB3.1.1 POSIX. ++ * Create SMB2_query_posix_info worker function to do non-compounded ++ * query when we already have an open file handle for this. For now this ++ * is fast enough (always using the compounded version). ++ */ ++ if (!tcon->posix_extensions) { ++ if (*full_path) { ++ rc = -ENOENT; + } else { +- rc = SMB2_query_info(xid, tcon, cfid->fid.persistent_fid, +- cfid->fid.volatile_fid, &data->fi); ++ rc = open_cached_dir(xid, tcon, full_path, ++ cifs_sb, false, &cfid); + } +- close_cached_dir(cfid); +- return rc; ++ /* If it is a root and its handle is cached then use it */ ++ if (!rc) { ++ if (cfid->file_all_info_is_valid) { ++ memcpy(&data->fi, &cfid->file_all_info, ++ sizeof(data->fi)); ++ } else { ++ rc = SMB2_query_info(xid, tcon, ++ cfid->fid.persistent_fid, ++ cfid->fid.volatile_fid, ++ &data->fi); ++ } ++ close_cached_dir(cfid); ++ return rc; ++ } ++ cmds[0] = SMB2_OP_QUERY_INFO; ++ } else { ++ cmds[0] = SMB2_OP_POSIX_QUERY_INFO; + } + + in_iov[0].iov_base = data; +@@ -743,6 +759,10 @@ int smb2_query_path_info(const unsigned int xid, + switch (rc) { + case 0: + case -EOPNOTSUPP: ++ /* ++ * BB TODO: When support for special files added to Samba ++ * re-verify this path. ++ */ + rc = parse_create_response(data, cifs_sb, &out_iov[0]); + if (rc || !data->reparse_point) + goto out; +@@ -782,75 +802,6 @@ int smb2_query_path_info(const unsigned int xid, + return rc; + } + +-int smb311_posix_query_path_info(const unsigned int xid, +- struct cifs_tcon *tcon, +- struct cifs_sb_info *cifs_sb, +- const char *full_path, +- struct cifs_open_info_data *data) +-{ +- int rc; +- __u32 create_options = 0; +- struct cifsFileInfo *cfile; +- struct kvec in_iov[2], out_iov[3] = {}; +- int out_buftype[3] = {}; +- int cmds[2] = { SMB2_OP_POSIX_QUERY_INFO, }; +- int i, num_cmds; +- +- data->adjust_tz = false; +- data->reparse_point = false; +- +- /* +- * BB TODO: Add support for using the cached root handle. +- * Create SMB2_query_posix_info worker function to do non-compounded query +- * when we already have an open file handle for this. For now this is fast enough +- * (always using the compounded version). +- */ +- in_iov[0].iov_base = data; +- in_iov[0].iov_len = sizeof(*data); +- in_iov[1] = in_iov[0]; +- +- cifs_get_readable_path(tcon, full_path, &cfile); +- rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, +- FILE_READ_ATTRIBUTES, FILE_OPEN, +- create_options, ACL_NO_MODE, in_iov, +- cmds, 1, cfile, out_iov, out_buftype); +- /* +- * If first iov is unset, then SMB session was dropped or we've got a +- * cached open file (@cfile). +- */ +- if (!out_iov[0].iov_base || out_buftype[0] == CIFS_NO_BUFFER) +- goto out; +- +- switch (rc) { +- case 0: +- case -EOPNOTSUPP: +- /* BB TODO: When support for special files added to Samba re-verify this path */ +- rc = parse_create_response(data, cifs_sb, &out_iov[0]); +- if (rc || !data->reparse_point) +- goto out; +- +- if (data->reparse.tag == IO_REPARSE_TAG_SYMLINK) { +- /* symlink already parsed in create response */ +- num_cmds = 1; +- } else { +- cmds[1] = SMB2_OP_GET_REPARSE; +- num_cmds = 2; +- } +- create_options |= OPEN_REPARSE_POINT; +- cifs_get_readable_path(tcon, full_path, &cfile); +- rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, +- FILE_READ_ATTRIBUTES, FILE_OPEN, +- create_options, ACL_NO_MODE, in_iov, +- cmds, num_cmds, cfile, NULL, NULL); +- break; +- } +- +-out: +- for (i = 0; i < ARRAY_SIZE(out_buftype); i++) +- free_rsp_buf(out_buftype[i], out_iov[i].iov_base); +- return rc; +-} +- + int + smb2_mkdir(const unsigned int xid, struct inode *parent_inode, umode_t mode, + struct cifs_tcon *tcon, const char *name, +-- +2.43.0 + diff --git a/queue-6.6/smb-client-handle-path-separator-of-created-smb-syml.patch b/queue-6.6/smb-client-handle-path-separator-of-created-smb-syml.patch new file mode 100644 index 0000000000..8ba8328ea3 --- /dev/null +++ b/queue-6.6/smb-client-handle-path-separator-of-created-smb-syml.patch @@ -0,0 +1,58 @@ +From 08b97675e0836f652bba699030ca65487fd0ce56 Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Sun, 11 Feb 2024 20:19:31 -0300 +Subject: smb: client: handle path separator of created SMB symlinks + +From: Paulo Alcantara <pc@manguebit.com> + +[ Upstream commit 8bde59b20de06339d598e8b05e5195f7c631c38b ] + +Convert path separator to CIFS_DIR_SEP(cifs_sb) from symlink target +before sending it over the wire otherwise the created SMB symlink may +become innaccesible from server side. + +Fixes: 514d793e27a3 ("smb: client: allow creating symlinks via reparse points") +Signed-off-by: Paulo Alcantara (Red Hat) <pc@manguebit.com> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/client/smb2ops.c | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c +index 23cf6e92fd54c..9ade347978709 100644 +--- a/fs/smb/client/smb2ops.c ++++ b/fs/smb/client/smb2ops.c +@@ -5212,7 +5212,7 @@ static int smb2_create_reparse_symlink(const unsigned int xid, + struct inode *new; + struct kvec iov; + __le16 *path; +- char *sym; ++ char *sym, sep = CIFS_DIR_SEP(cifs_sb); + u16 len, plen; + int rc = 0; + +@@ -5226,7 +5226,8 @@ static int smb2_create_reparse_symlink(const unsigned int xid, + .symlink_target = sym, + }; + +- path = cifs_convert_path_to_utf16(symname, cifs_sb); ++ convert_delimiter(sym, sep); ++ path = cifs_convert_path_to_utf16(sym, cifs_sb); + if (!path) { + rc = -ENOMEM; + goto out; +@@ -5249,7 +5250,10 @@ static int smb2_create_reparse_symlink(const unsigned int xid, + buf->PrintNameLength = cpu_to_le16(plen); + memcpy(buf->PathBuffer, path, plen); + buf->Flags = cpu_to_le32(*symname != '/' ? SYMLINK_FLAG_RELATIVE : 0); ++ if (*sym != sep) ++ buf->Flags = cpu_to_le32(SYMLINK_FLAG_RELATIVE); + ++ convert_delimiter(sym, '/'); + iov.iov_base = buf; + iov.iov_len = len; + new = smb2_get_reparse_inode(&data, inode->i_sb, xid, +-- +2.43.0 + diff --git a/queue-6.6/smb-client-handle-special-files-and-symlinks-in-smb3.patch b/queue-6.6/smb-client-handle-special-files-and-symlinks-in-smb3.patch new file mode 100644 index 0000000000..86c9cfa25c --- /dev/null +++ b/queue-6.6/smb-client-handle-special-files-and-symlinks-in-smb3.patch @@ -0,0 +1,148 @@ +From 9a0426dc1edaadce4bf824ba3ea3fbae46d7b561 Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Tue, 28 Nov 2023 18:23:33 -0300 +Subject: smb: client: handle special files and symlinks in SMB3 POSIX + +From: Paulo Alcantara <pc@manguebit.com> + +[ Upstream commit 9c38568a75c160786d5f5d5b96aeefed0c1b76bd ] + +Parse reparse points in SMB3 posix query info as they will be +supported and required by the new specification. + +Signed-off-by: Paulo Alcantara (SUSE) <pc@manguebit.com> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/client/inode.c | 50 +++++++++++++++++++++++++------------------ + 1 file changed, 29 insertions(+), 21 deletions(-) + +diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c +index 89dfb405f9c1e..b8260ace2bee9 100644 +--- a/fs/smb/client/inode.c ++++ b/fs/smb/client/inode.c +@@ -693,29 +693,36 @@ static void smb311_posix_info_to_fattr(struct cifs_fattr *fattr, + fattr->cf_mtime.tv_sec += tcon->ses->server->timeAdj; + } + ++ /* ++ * The srv fs device id is overridden on network mount so setting ++ * @fattr->cf_rdev isn't needed here. ++ */ + fattr->cf_eof = le64_to_cpu(info->EndOfFile); + fattr->cf_bytes = le64_to_cpu(info->AllocationSize); + fattr->cf_createtime = le64_to_cpu(info->CreationTime); +- + fattr->cf_nlink = le32_to_cpu(info->HardLinks); + fattr->cf_mode = (umode_t) le32_to_cpu(info->Mode); +- /* The srv fs device id is overridden on network mount so setting rdev isn't needed here */ +- /* fattr->cf_rdev = le32_to_cpu(info->DeviceId); */ + +- if (data->symlink) { +- fattr->cf_mode |= S_IFLNK; +- fattr->cf_dtype = DT_LNK; +- fattr->cf_symlink_target = data->symlink_target; +- data->symlink_target = NULL; +- } else if (fattr->cf_cifsattrs & ATTR_DIRECTORY) { ++ if (cifs_open_data_reparse(data) && ++ cifs_reparse_point_to_fattr(cifs_sb, fattr, data)) ++ goto out_reparse; ++ ++ fattr->cf_mode &= ~S_IFMT; ++ if (fattr->cf_cifsattrs & ATTR_DIRECTORY) { + fattr->cf_mode |= S_IFDIR; + fattr->cf_dtype = DT_DIR; + } else { /* file */ + fattr->cf_mode |= S_IFREG; + fattr->cf_dtype = DT_REG; + } +- /* else if reparse point ... TODO: add support for FIFO and blk dev; special file types */ + ++out_reparse: ++ if (S_ISLNK(fattr->cf_mode)) { ++ if (likely(data->symlink_target)) ++ fattr->cf_eof = strnlen(data->symlink_target, PATH_MAX); ++ fattr->cf_symlink_target = data->symlink_target; ++ data->symlink_target = NULL; ++ } + sid_to_id(cifs_sb, owner, fattr, SIDOWNER); + sid_to_id(cifs_sb, group, fattr, SIDGROUP); + +@@ -740,25 +747,25 @@ bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb, + if (tag == IO_REPARSE_TAG_NFS && buf) { + switch (le64_to_cpu(buf->InodeType)) { + case NFS_SPECFILE_CHR: +- fattr->cf_mode |= S_IFCHR | cifs_sb->ctx->file_mode; ++ fattr->cf_mode |= S_IFCHR; + fattr->cf_dtype = DT_CHR; + fattr->cf_rdev = nfs_mkdev(buf); + break; + case NFS_SPECFILE_BLK: +- fattr->cf_mode |= S_IFBLK | cifs_sb->ctx->file_mode; ++ fattr->cf_mode |= S_IFBLK; + fattr->cf_dtype = DT_BLK; + fattr->cf_rdev = nfs_mkdev(buf); + break; + case NFS_SPECFILE_FIFO: +- fattr->cf_mode |= S_IFIFO | cifs_sb->ctx->file_mode; ++ fattr->cf_mode |= S_IFIFO; + fattr->cf_dtype = DT_FIFO; + break; + case NFS_SPECFILE_SOCK: +- fattr->cf_mode |= S_IFSOCK | cifs_sb->ctx->file_mode; ++ fattr->cf_mode |= S_IFSOCK; + fattr->cf_dtype = DT_SOCK; + break; + case NFS_SPECFILE_LNK: +- fattr->cf_mode = S_IFLNK | cifs_sb->ctx->file_mode; ++ fattr->cf_mode |= S_IFLNK; + fattr->cf_dtype = DT_LNK; + break; + default: +@@ -770,29 +777,29 @@ bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb, + + switch (tag) { + case IO_REPARSE_TAG_LX_SYMLINK: +- fattr->cf_mode |= S_IFLNK | cifs_sb->ctx->file_mode; ++ fattr->cf_mode |= S_IFLNK; + fattr->cf_dtype = DT_LNK; + break; + case IO_REPARSE_TAG_LX_FIFO: +- fattr->cf_mode |= S_IFIFO | cifs_sb->ctx->file_mode; ++ fattr->cf_mode |= S_IFIFO; + fattr->cf_dtype = DT_FIFO; + break; + case IO_REPARSE_TAG_AF_UNIX: +- fattr->cf_mode |= S_IFSOCK | cifs_sb->ctx->file_mode; ++ fattr->cf_mode |= S_IFSOCK; + fattr->cf_dtype = DT_SOCK; + break; + case IO_REPARSE_TAG_LX_CHR: +- fattr->cf_mode |= S_IFCHR | cifs_sb->ctx->file_mode; ++ fattr->cf_mode |= S_IFCHR; + fattr->cf_dtype = DT_CHR; + break; + case IO_REPARSE_TAG_LX_BLK: +- fattr->cf_mode |= S_IFBLK | cifs_sb->ctx->file_mode; ++ fattr->cf_mode |= S_IFBLK; + fattr->cf_dtype = DT_BLK; + break; + case 0: /* SMB1 symlink */ + case IO_REPARSE_TAG_SYMLINK: + case IO_REPARSE_TAG_NFS: +- fattr->cf_mode = S_IFLNK | cifs_sb->ctx->file_mode; ++ fattr->cf_mode |= S_IFLNK; + fattr->cf_dtype = DT_LNK; + break; + default: +@@ -832,6 +839,7 @@ static void cifs_open_info_to_fattr(struct cifs_fattr *fattr, + fattr->cf_createtime = le64_to_cpu(info->CreationTime); + fattr->cf_nlink = le32_to_cpu(info->NumberOfLinks); + ++ fattr->cf_mode = cifs_sb->ctx->file_mode; + if (cifs_open_data_reparse(data) && + cifs_reparse_point_to_fattr(cifs_sb, fattr, data)) + goto out_reparse; +-- +2.43.0 + diff --git a/queue-6.6/smb-client-instantiate-when-creating-sfu-files.patch b/queue-6.6/smb-client-instantiate-when-creating-sfu-files.patch new file mode 100644 index 0000000000..582b2ade08 --- /dev/null +++ b/queue-6.6/smb-client-instantiate-when-creating-sfu-files.patch @@ -0,0 +1,210 @@ +From fc8466b28484332ce30a4be5bf48e9a5a756226c Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Tue, 9 Apr 2024 11:28:59 -0300 +Subject: smb: client: instantiate when creating SFU files + +From: Paulo Alcantara <pc@manguebit.com> + +[ Upstream commit c6ff459037b2e35450af2351037eac4c8aca1d6b ] + +In cifs_sfu_make_node(), on success, instantiate rather than leave it +with dentry unhashed negative to support callers that expect mknod(2) +to always instantiate. + +This fixes the following test case: + + mount.cifs //srv/share /mnt -o ...,sfu + mkfifo /mnt/fifo + ./xfstests/ltp/growfiles -b -W test -e 1 -u -i 0 -L 30 /mnt/fifo + ... + BUG: unable to handle page fault for address: 000000034cec4e58 + #PF: supervisor read access in kernel mode + #PF: error_code(0x0000) - not-present page + PGD 0 P4D 0 + Oops: 0000 1 PREEMPT SMP PTI + CPU: 0 PID: 138098 Comm: growfiles Kdump: loaded Not tainted + 5.14.0-436.3987_1240945149.el9.x86_64 #1 + Hardware name: Red Hat KVM, BIOS 0.5.1 01/01/2011 + RIP: 0010:_raw_callee_save__kvm_vcpu_is_preempted+0x0/0x20 + Code: e8 15 d9 61 00 e9 63 ff ff ff 41 bd ea ff ff ff e9 58 ff ff ff e8 + d0 71 c0 00 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 <48> 8b 04 + fd 60 2b c1 99 80 b8 90 50 03 00 00 0f 95 c0 c3 cc cc cc + RSP: 0018:ffffb6a143cf7cf8 EFLAGS: 00010206 + RAX: ffff8a9bc30fb038 RBX: ffff8a9bc666a200 RCX: ffff8a9cc0260000 + RDX: 00000000736f622e RSI: ffff8a9bc30fb038 RDI: 000000007665645f + RBP: ffffb6a143cf7d70 R08: 0000000000001000 R09: 0000000000000001 + R10: 0000000000000001 R11: 0000000000000000 R12: ffff8a9bc666a200 + R13: 0000559a302a12b0 R14: 0000000000001000 R15: 0000000000000000 + FS: 00007fbed1dbb740(0000) GS:ffff8a9cf0000000(0000) + knlGS:0000000000000000 + CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 + CR2: 000000034cec4e58 CR3: 0000000128ec6006 CR4: 0000000000770ef0 + DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 + DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 + PKRU: 55555554 + Call Trace: + <TASK> + ? show_trace_log_lvl+0x1c4/0x2df + ? show_trace_log_lvl+0x1c4/0x2df + ? __mutex_lock.constprop.0+0x5f7/0x6a0 + ? __die_body.cold+0x8/0xd + ? page_fault_oops+0x134/0x170 + ? exc_page_fault+0x62/0x150 + ? asm_exc_page_fault+0x22/0x30 + ? _pfx_raw_callee_save__kvm_vcpu_is_preempted+0x10/0x10 + __mutex_lock.constprop.0+0x5f7/0x6a0 + ? __mod_memcg_lruvec_state+0x84/0xd0 + pipe_write+0x47/0x650 + ? do_anonymous_page+0x258/0x410 + ? inode_security+0x22/0x60 + ? selinux_file_permission+0x108/0x150 + vfs_write+0x2cb/0x410 + ksys_write+0x5f/0xe0 + do_syscall_64+0x5c/0xf0 + ? syscall_exit_to_user_mode+0x22/0x40 + ? do_syscall_64+0x6b/0xf0 + ? sched_clock_cpu+0x9/0xc0 + ? exc_page_fault+0x62/0x150 + entry_SYSCALL_64_after_hwframe+0x6e/0x76 + +Cc: stable@vger.kernel.org +Fixes: 72bc63f5e23a ("smb3: fix creating FIFOs when mounting with "sfu" mount option") +Suggested-by: Al Viro <viro@zeniv.linux.org.uk> +Signed-off-by: Paulo Alcantara (Red Hat) <pc@manguebit.com> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/client/smb2ops.c | 94 ++++++++++++++++++++++++----------------- + 1 file changed, 55 insertions(+), 39 deletions(-) + +diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c +index 981a922cf38f0..df6c6d31236ad 100644 +--- a/fs/smb/client/smb2ops.c ++++ b/fs/smb/client/smb2ops.c +@@ -4956,68 +4956,84 @@ static int smb2_next_header(struct TCP_Server_Info *server, char *buf, + return 0; + } + +-int cifs_sfu_make_node(unsigned int xid, struct inode *inode, +- struct dentry *dentry, struct cifs_tcon *tcon, +- const char *full_path, umode_t mode, dev_t dev) ++static int __cifs_sfu_make_node(unsigned int xid, struct inode *inode, ++ struct dentry *dentry, struct cifs_tcon *tcon, ++ const char *full_path, umode_t mode, dev_t dev) + { +- struct cifs_open_info_data buf = {}; + struct TCP_Server_Info *server = tcon->ses->server; + struct cifs_open_parms oparms; + struct cifs_io_parms io_parms = {}; + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct cifs_fid fid; + unsigned int bytes_written; +- struct win_dev *pdev; ++ struct win_dev pdev = {}; + struct kvec iov[2]; + __u32 oplock = server->oplocks ? REQ_OPLOCK : 0; + int rc; + +- if (!S_ISCHR(mode) && !S_ISBLK(mode) && !S_ISFIFO(mode)) ++ switch (mode & S_IFMT) { ++ case S_IFCHR: ++ strscpy(pdev.type, "IntxCHR", strlen("IntxChr")); ++ pdev.major = cpu_to_le64(MAJOR(dev)); ++ pdev.minor = cpu_to_le64(MINOR(dev)); ++ break; ++ case S_IFBLK: ++ strscpy(pdev.type, "IntxBLK", strlen("IntxBLK")); ++ pdev.major = cpu_to_le64(MAJOR(dev)); ++ pdev.minor = cpu_to_le64(MINOR(dev)); ++ break; ++ case S_IFIFO: ++ strscpy(pdev.type, "LnxFIFO", strlen("LnxFIFO")); ++ break; ++ default: + return -EPERM; ++ } + +- oparms = (struct cifs_open_parms) { +- .tcon = tcon, +- .cifs_sb = cifs_sb, +- .desired_access = GENERIC_WRITE, +- .create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR | +- CREATE_OPTION_SPECIAL), +- .disposition = FILE_CREATE, +- .path = full_path, +- .fid = &fid, +- }; ++ oparms = CIFS_OPARMS(cifs_sb, tcon, full_path, GENERIC_WRITE, ++ FILE_CREATE, CREATE_NOT_DIR | ++ CREATE_OPTION_SPECIAL, ACL_NO_MODE); ++ oparms.fid = &fid; + +- rc = server->ops->open(xid, &oparms, &oplock, &buf); ++ rc = server->ops->open(xid, &oparms, &oplock, NULL); + if (rc) + return rc; + +- /* +- * BB Do not bother to decode buf since no local inode yet to put +- * timestamps in, but we can reuse it safely. +- */ +- pdev = (struct win_dev *)&buf.fi; + io_parms.pid = current->tgid; + io_parms.tcon = tcon; +- io_parms.length = sizeof(*pdev); +- iov[1].iov_base = pdev; +- iov[1].iov_len = sizeof(*pdev); +- if (S_ISCHR(mode)) { +- memcpy(pdev->type, "IntxCHR", 8); +- pdev->major = cpu_to_le64(MAJOR(dev)); +- pdev->minor = cpu_to_le64(MINOR(dev)); +- } else if (S_ISBLK(mode)) { +- memcpy(pdev->type, "IntxBLK", 8); +- pdev->major = cpu_to_le64(MAJOR(dev)); +- pdev->minor = cpu_to_le64(MINOR(dev)); +- } else if (S_ISFIFO(mode)) { +- memcpy(pdev->type, "LnxFIFO", 8); +- } ++ io_parms.length = sizeof(pdev); ++ iov[1].iov_base = &pdev; ++ iov[1].iov_len = sizeof(pdev); + + rc = server->ops->sync_write(xid, &fid, &io_parms, + &bytes_written, iov, 1); + server->ops->close(xid, tcon, &fid); +- d_drop(dentry); +- /* FIXME: add code here to set EAs */ +- cifs_free_open_info(&buf); ++ return rc; ++} ++ ++int cifs_sfu_make_node(unsigned int xid, struct inode *inode, ++ struct dentry *dentry, struct cifs_tcon *tcon, ++ const char *full_path, umode_t mode, dev_t dev) ++{ ++ struct inode *new = NULL; ++ int rc; ++ ++ rc = __cifs_sfu_make_node(xid, inode, dentry, tcon, ++ full_path, mode, dev); ++ if (rc) ++ return rc; ++ ++ if (tcon->posix_extensions) { ++ rc = smb311_posix_get_inode_info(&new, full_path, NULL, ++ inode->i_sb, xid); ++ } else if (tcon->unix_ext) { ++ rc = cifs_get_inode_info_unix(&new, full_path, ++ inode->i_sb, xid); ++ } else { ++ rc = cifs_get_inode_info(&new, full_path, NULL, ++ inode->i_sb, xid, NULL); ++ } ++ if (!rc) ++ d_instantiate(dentry, new); + return rc; + } + +-- +2.43.0 + diff --git a/queue-6.6/smb-client-introduce-cifs_sfu_make_node.patch b/queue-6.6/smb-client-introduce-cifs_sfu_make_node.patch new file mode 100644 index 0000000000..4589dacae3 --- /dev/null +++ b/queue-6.6/smb-client-introduce-cifs_sfu_make_node.patch @@ -0,0 +1,283 @@ +From 1f9941732bb0953b2e28141852a18447fe9e6263 Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Sat, 27 Apr 2024 09:16:18 -0500 +Subject: smb: client: introduce cifs_sfu_make_node() + +From: Steve French <stfrench@microsoft.com> + +[ Upstream commit b0348e459c836abdb0f4b967e006d15c77cf1c87 ] + +Remove duplicate code and add new helper for creating special files in +SFU (Services for UNIX) format that can be shared by SMB1+ code. + +Signed-off-by: Paulo Alcantara (SUSE) <pc@manguebit.com> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/client/cifsproto.h | 3 ++ + fs/smb/client/smb1ops.c | 80 ++++------------------------------- + fs/smb/client/smb2ops.c | 89 ++++++++++++++++++--------------------- + 3 files changed, 52 insertions(+), 120 deletions(-) + +diff --git a/fs/smb/client/cifsproto.h b/fs/smb/client/cifsproto.h +index 1bdad33580b57..9480cdb9588d5 100644 +--- a/fs/smb/client/cifsproto.h ++++ b/fs/smb/client/cifsproto.h +@@ -673,6 +673,9 @@ char *extract_sharename(const char *unc); + int parse_reparse_point(struct reparse_data_buffer *buf, + u32 plen, struct cifs_sb_info *cifs_sb, + bool unicode, struct cifs_open_info_data *data); ++int cifs_sfu_make_node(unsigned int xid, struct inode *inode, ++ struct dentry *dentry, struct cifs_tcon *tcon, ++ const char *full_path, umode_t mode, dev_t dev); + + #ifdef CONFIG_CIFS_DFS_UPCALL + static inline int get_dfs_path(const unsigned int xid, struct cifs_ses *ses, +diff --git a/fs/smb/client/smb1ops.c b/fs/smb/client/smb1ops.c +index 1aebcf95c1951..212ec6f66ec65 100644 +--- a/fs/smb/client/smb1ops.c ++++ b/fs/smb/client/smb1ops.c +@@ -1041,15 +1041,7 @@ cifs_make_node(unsigned int xid, struct inode *inode, + { + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct inode *newinode = NULL; +- int rc = -EPERM; +- struct cifs_open_info_data buf = {}; +- struct cifs_io_parms io_parms; +- __u32 oplock = 0; +- struct cifs_fid fid; +- struct cifs_open_parms oparms; +- unsigned int bytes_written; +- struct win_dev *pdev; +- struct kvec iov[2]; ++ int rc; + + if (tcon->unix_ext) { + /* +@@ -1083,74 +1075,18 @@ cifs_make_node(unsigned int xid, struct inode *inode, + d_instantiate(dentry, newinode); + return rc; + } +- + /* +- * SMB1 SFU emulation: should work with all servers, but only +- * support block and char device (no socket & fifo) ++ * Check if mounted with mount parm 'sfu' mount parm. ++ * SFU emulation should work with all servers, but only ++ * supports block and char device (no socket & fifo), ++ * and was used by default in earlier versions of Windows + */ + if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL)) +- return rc; +- +- if (!S_ISCHR(mode) && !S_ISBLK(mode)) +- return rc; +- +- cifs_dbg(FYI, "sfu compat create special file\n"); +- +- oparms = (struct cifs_open_parms) { +- .tcon = tcon, +- .cifs_sb = cifs_sb, +- .desired_access = GENERIC_WRITE, +- .create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR | +- CREATE_OPTION_SPECIAL), +- .disposition = FILE_CREATE, +- .path = full_path, +- .fid = &fid, +- }; +- +- if (tcon->ses->server->oplocks) +- oplock = REQ_OPLOCK; +- else +- oplock = 0; +- rc = tcon->ses->server->ops->open(xid, &oparms, &oplock, &buf); +- if (rc) +- return rc; +- +- /* +- * BB Do not bother to decode buf since no local inode yet to put +- * timestamps in, but we can reuse it safely. +- */ +- +- pdev = (struct win_dev *)&buf.fi; +- io_parms.pid = current->tgid; +- io_parms.tcon = tcon; +- io_parms.offset = 0; +- io_parms.length = sizeof(struct win_dev); +- iov[1].iov_base = &buf.fi; +- iov[1].iov_len = sizeof(struct win_dev); +- if (S_ISCHR(mode)) { +- memcpy(pdev->type, "IntxCHR", 8); +- pdev->major = cpu_to_le64(MAJOR(dev)); +- pdev->minor = cpu_to_le64(MINOR(dev)); +- rc = tcon->ses->server->ops->sync_write(xid, &fid, &io_parms, +- &bytes_written, iov, 1); +- } else if (S_ISBLK(mode)) { +- memcpy(pdev->type, "IntxBLK", 8); +- pdev->major = cpu_to_le64(MAJOR(dev)); +- pdev->minor = cpu_to_le64(MINOR(dev)); +- rc = tcon->ses->server->ops->sync_write(xid, &fid, &io_parms, +- &bytes_written, iov, 1); +- } +- tcon->ses->server->ops->close(xid, tcon, &fid); +- d_drop(dentry); +- +- /* FIXME: add code here to set EAs */ +- +- cifs_free_open_info(&buf); +- return rc; ++ return -EPERM; ++ return cifs_sfu_make_node(xid, inode, dentry, tcon, ++ full_path, mode, dev); + } + +- +- + struct smb_version_operations smb1_operations = { + .send_cancel = send_nt_cancel, + .compare_fids = cifs_compare_fids, +diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c +index 04fea874d0a33..2b892a736e5f9 100644 +--- a/fs/smb/client/smb2ops.c ++++ b/fs/smb/client/smb2ops.c +@@ -5105,41 +5105,24 @@ static int smb2_next_header(struct TCP_Server_Info *server, char *buf, + return 0; + } + +-static int +-smb2_make_node(unsigned int xid, struct inode *inode, +- struct dentry *dentry, struct cifs_tcon *tcon, +- const char *full_path, umode_t mode, dev_t dev) ++int cifs_sfu_make_node(unsigned int xid, struct inode *inode, ++ struct dentry *dentry, struct cifs_tcon *tcon, ++ const char *full_path, umode_t mode, dev_t dev) + { +- struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); +- int rc = -EPERM; + struct cifs_open_info_data buf = {}; +- struct cifs_io_parms io_parms = {0}; +- __u32 oplock = 0; +- struct cifs_fid fid; ++ struct TCP_Server_Info *server = tcon->ses->server; + struct cifs_open_parms oparms; ++ struct cifs_io_parms io_parms = {}; ++ struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); ++ struct cifs_fid fid; + unsigned int bytes_written; + struct win_dev *pdev; + struct kvec iov[2]; +- +- /* +- * Check if mounted with mount parm 'sfu' mount parm. +- * SFU emulation should work with all servers, but only +- * supports block and char device (no socket & fifo), +- * and was used by default in earlier versions of Windows +- */ +- if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL)) +- return rc; +- +- /* +- * TODO: Add ability to create instead via reparse point. Windows (e.g. +- * their current NFS server) uses this approach to expose special files +- * over SMB2/SMB3 and Samba will do this with SMB3.1.1 POSIX Extensions +- */ ++ __u32 oplock = server->oplocks ? REQ_OPLOCK : 0; ++ int rc; + + if (!S_ISCHR(mode) && !S_ISBLK(mode) && !S_ISFIFO(mode)) +- return rc; +- +- cifs_dbg(FYI, "sfu compat create special file\n"); ++ return -EPERM; + + oparms = (struct cifs_open_parms) { + .tcon = tcon, +@@ -5152,11 +5135,7 @@ smb2_make_node(unsigned int xid, struct inode *inode, + .fid = &fid, + }; + +- if (tcon->ses->server->oplocks) +- oplock = REQ_OPLOCK; +- else +- oplock = 0; +- rc = tcon->ses->server->ops->open(xid, &oparms, &oplock, &buf); ++ rc = server->ops->open(xid, &oparms, &oplock, &buf); + if (rc) + return rc; + +@@ -5164,42 +5143,56 @@ smb2_make_node(unsigned int xid, struct inode *inode, + * BB Do not bother to decode buf since no local inode yet to put + * timestamps in, but we can reuse it safely. + */ +- + pdev = (struct win_dev *)&buf.fi; + io_parms.pid = current->tgid; + io_parms.tcon = tcon; +- io_parms.offset = 0; +- io_parms.length = sizeof(struct win_dev); +- iov[1].iov_base = &buf.fi; +- iov[1].iov_len = sizeof(struct win_dev); ++ io_parms.length = sizeof(*pdev); ++ iov[1].iov_base = pdev; ++ iov[1].iov_len = sizeof(*pdev); + if (S_ISCHR(mode)) { + memcpy(pdev->type, "IntxCHR", 8); + pdev->major = cpu_to_le64(MAJOR(dev)); + pdev->minor = cpu_to_le64(MINOR(dev)); +- rc = tcon->ses->server->ops->sync_write(xid, &fid, &io_parms, +- &bytes_written, iov, 1); + } else if (S_ISBLK(mode)) { + memcpy(pdev->type, "IntxBLK", 8); + pdev->major = cpu_to_le64(MAJOR(dev)); + pdev->minor = cpu_to_le64(MINOR(dev)); +- rc = tcon->ses->server->ops->sync_write(xid, &fid, &io_parms, +- &bytes_written, iov, 1); + } else if (S_ISFIFO(mode)) { + memcpy(pdev->type, "LnxFIFO", 8); +- pdev->major = 0; +- pdev->minor = 0; +- rc = tcon->ses->server->ops->sync_write(xid, &fid, &io_parms, +- &bytes_written, iov, 1); + } +- tcon->ses->server->ops->close(xid, tcon, &fid); +- d_drop(dentry); + ++ rc = server->ops->sync_write(xid, &fid, &io_parms, ++ &bytes_written, iov, 1); ++ server->ops->close(xid, tcon, &fid); ++ d_drop(dentry); + /* FIXME: add code here to set EAs */ +- + cifs_free_open_info(&buf); + return rc; + } + ++static int smb2_make_node(unsigned int xid, struct inode *inode, ++ struct dentry *dentry, struct cifs_tcon *tcon, ++ const char *full_path, umode_t mode, dev_t dev) ++{ ++ struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); ++ ++ /* ++ * Check if mounted with mount parm 'sfu' mount parm. ++ * SFU emulation should work with all servers, but only ++ * supports block and char device (no socket & fifo), ++ * and was used by default in earlier versions of Windows ++ */ ++ if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL)) ++ return -EPERM; ++ /* ++ * TODO: Add ability to create instead via reparse point. Windows (e.g. ++ * their current NFS server) uses this approach to expose special files ++ * over SMB2/SMB3 and Samba will do this with SMB3.1.1 POSIX Extensions ++ */ ++ return cifs_sfu_make_node(xid, inode, dentry, tcon, ++ full_path, mode, dev); ++} ++ + #ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + struct smb_version_operations smb20_operations = { + .compare_fids = smb2_compare_fids, +-- +2.43.0 + diff --git a/queue-6.6/smb-client-introduce-reparse-mount-option.patch b/queue-6.6/smb-client-introduce-reparse-mount-option.patch new file mode 100644 index 0000000000..3914990c93 --- /dev/null +++ b/queue-6.6/smb-client-introduce-reparse-mount-option.patch @@ -0,0 +1,161 @@ +From c51945f7d33c5e8028723321ada219432dc79e5b Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Sun, 21 Jan 2024 13:28:21 -0300 +Subject: smb: client: introduce reparse mount option + +From: Paulo Alcantara <pc@manguebit.com> + +[ Upstream commit eb90e8ecb2b54ac1af51e28596e0ef7ba351476d ] + +Allow the user to create special files and symlinks by choosing +between WSL and NFS reparse points via 'reparse={nfs,wsl}' mount +options. If unset or 'reparse=default', the client will default to +creating them via NFS reparse points. + +Creating WSL reparse points isn't supported yet, so simply return +error when attempting to mount with 'reparse=wsl' for now. + +Signed-off-by: Paulo Alcantara <pc@manguebit.com> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/client/cifsglob.h | 6 ++++++ + fs/smb/client/connect.c | 2 ++ + fs/smb/client/fs_context.c | 35 +++++++++++++++++++++++++++++++++++ + fs/smb/client/fs_context.h | 9 +++++++++ + 4 files changed, 52 insertions(+) + +diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h +index fdadda4024f46..08540541046c1 100644 +--- a/fs/smb/client/cifsglob.h ++++ b/fs/smb/client/cifsglob.h +@@ -153,6 +153,12 @@ enum securityEnum { + Kerberos, /* Kerberos via SPNEGO */ + }; + ++enum cifs_reparse_type { ++ CIFS_REPARSE_TYPE_NFS, ++ CIFS_REPARSE_TYPE_WSL, ++ CIFS_REPARSE_TYPE_DEFAULT = CIFS_REPARSE_TYPE_NFS, ++}; ++ + struct session_key { + unsigned int len; + char *response; +diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c +index e28f011f11d6c..deba1cfd11801 100644 +--- a/fs/smb/client/connect.c ++++ b/fs/smb/client/connect.c +@@ -2805,6 +2805,8 @@ compare_mount_options(struct super_block *sb, struct cifs_mnt_data *mnt_data) + return 0; + if (old->ctx->closetimeo != new->ctx->closetimeo) + return 0; ++ if (old->ctx->reparse_type != new->ctx->reparse_type) ++ return 0; + + return 1; + } +diff --git a/fs/smb/client/fs_context.c b/fs/smb/client/fs_context.c +index f119035a82725..535885fbdf51d 100644 +--- a/fs/smb/client/fs_context.c ++++ b/fs/smb/client/fs_context.c +@@ -175,6 +175,7 @@ const struct fs_parameter_spec smb3_fs_parameters[] = { + fsparam_string("vers", Opt_vers), + fsparam_string("sec", Opt_sec), + fsparam_string("cache", Opt_cache), ++ fsparam_string("reparse", Opt_reparse), + + /* Arguments that should be ignored */ + fsparam_flag("guest", Opt_ignore), +@@ -297,6 +298,35 @@ cifs_parse_cache_flavor(struct fs_context *fc, char *value, struct smb3_fs_conte + return 0; + } + ++static const match_table_t reparse_flavor_tokens = { ++ { Opt_reparse_default, "default" }, ++ { Opt_reparse_nfs, "nfs" }, ++ { Opt_reparse_wsl, "wsl" }, ++ { Opt_reparse_err, NULL }, ++}; ++ ++static int parse_reparse_flavor(struct fs_context *fc, char *value, ++ struct smb3_fs_context *ctx) ++{ ++ substring_t args[MAX_OPT_ARGS]; ++ ++ switch (match_token(value, reparse_flavor_tokens, args)) { ++ case Opt_reparse_default: ++ ctx->reparse_type = CIFS_REPARSE_TYPE_DEFAULT; ++ break; ++ case Opt_reparse_nfs: ++ ctx->reparse_type = CIFS_REPARSE_TYPE_NFS; ++ break; ++ case Opt_reparse_wsl: ++ cifs_errorf(fc, "unsupported reparse= option: %s\n", value); ++ return 1; ++ default: ++ cifs_errorf(fc, "bad reparse= option: %s\n", value); ++ return 1; ++ } ++ return 0; ++} ++ + #define DUP_CTX_STR(field) \ + do { \ + if (ctx->field) { \ +@@ -1595,6 +1625,10 @@ static int smb3_fs_context_parse_param(struct fs_context *fc, + case Opt_rdma: + ctx->rdma = true; + break; ++ case Opt_reparse: ++ if (parse_reparse_flavor(fc, param->string, ctx)) ++ goto cifs_parse_mount_err; ++ break; + } + /* case Opt_ignore: - is ignored as expected ... */ + +@@ -1683,6 +1717,7 @@ int smb3_init_fs_context(struct fs_context *fc) + ctx->backupgid_specified = false; /* no backup intent for a group */ + + ctx->retrans = 1; ++ ctx->reparse_type = CIFS_REPARSE_TYPE_DEFAULT; + + /* + * short int override_uid = -1; +diff --git a/fs/smb/client/fs_context.h b/fs/smb/client/fs_context.h +index 369a3fea1dfe0..e77ee81846b31 100644 +--- a/fs/smb/client/fs_context.h ++++ b/fs/smb/client/fs_context.h +@@ -41,6 +41,13 @@ enum { + Opt_cache_err + }; + ++enum cifs_reparse_parm { ++ Opt_reparse_default, ++ Opt_reparse_nfs, ++ Opt_reparse_wsl, ++ Opt_reparse_err ++}; ++ + enum cifs_sec_param { + Opt_sec_krb5, + Opt_sec_krb5i, +@@ -149,6 +156,7 @@ enum cifs_param { + Opt_vers, + Opt_sec, + Opt_cache, ++ Opt_reparse, + + /* Mount options to be ignored */ + Opt_ignore, +@@ -275,6 +283,7 @@ struct smb3_fs_context { + char *leaf_fullpath; + struct cifs_ses *dfs_root_ses; + bool dfs_automount:1; /* set for dfs automount only */ ++ enum cifs_reparse_type reparse_type; + }; + + extern const struct fs_parameter_spec smb3_fs_parameters[]; +-- +2.43.0 + diff --git a/queue-6.6/smb-client-introduce-smb2_op_query_wsl_ea.patch b/queue-6.6/smb-client-introduce-smb2_op_query_wsl_ea.patch new file mode 100644 index 0000000000..e97b45050d --- /dev/null +++ b/queue-6.6/smb-client-introduce-smb2_op_query_wsl_ea.patch @@ -0,0 +1,384 @@ +From afb9ac6f566cf37556665f232b2a38b3ae8ec6cb Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Wed, 1 May 2024 01:01:06 -0500 +Subject: smb: client: introduce SMB2_OP_QUERY_WSL_EA + +From: Steve French <stfrench@microsoft.com> + +[ Upstream commit ea41367b2a602f602ea6594fc4a310520dcc64f4 ] + +Add a new command to smb2_compound_op() for querying WSL extended +attributes from reparse points. + +Signed-off-by: Paulo Alcantara <pc@manguebit.com> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/client/cifsglob.h | 5 ++ + fs/smb/client/reparse.c | 10 +-- + fs/smb/client/smb2glob.h | 3 +- + fs/smb/client/smb2inode.c | 170 +++++++++++++++++++++++++++++++++----- + fs/smb/client/smb2pdu.h | 25 ++++++ + fs/smb/client/trace.h | 2 + + 6 files changed, 190 insertions(+), 25 deletions(-) + +diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h +index 54758825fa8d9..ddb64af50a45d 100644 +--- a/fs/smb/client/cifsglob.h ++++ b/fs/smb/client/cifsglob.h +@@ -214,6 +214,10 @@ struct cifs_open_info_data { + struct reparse_posix_data *posix; + }; + } reparse; ++ struct { ++ __u8 eas[SMB2_WSL_MAX_QUERY_EA_RESP_SIZE]; ++ unsigned int eas_len; ++ } wsl; + char *symlink_target; + struct cifs_sid posix_owner; + struct cifs_sid posix_group; +@@ -2304,6 +2308,7 @@ struct smb2_compound_vars { + struct kvec close_iov; + struct smb2_file_rename_info rename_info; + struct smb2_file_link_info link_info; ++ struct kvec ea_iov; + }; + + static inline bool cifs_ses_exiting(struct cifs_ses *ses) +diff --git a/fs/smb/client/reparse.c b/fs/smb/client/reparse.c +index 24feeaa32280e..e8be756e6768c 100644 +--- a/fs/smb/client/reparse.c ++++ b/fs/smb/client/reparse.c +@@ -205,15 +205,15 @@ static int wsl_set_xattrs(struct inode *inode, umode_t _mode, + __le64 dev = cpu_to_le64(((u64)MINOR(_dev) << 32) | MAJOR(_dev)); + __le64 mode = cpu_to_le64(_mode); + struct wsl_xattr xattrs[] = { +- { .name = "$LXUID", .value = uid, .size = 4, }, +- { .name = "$LXGID", .value = gid, .size = 4, }, +- { .name = "$LXMOD", .value = mode, .size = 4, }, +- { .name = "$LXDEV", .value = dev, .size = 8, }, ++ { .name = SMB2_WSL_XATTR_UID, .value = uid, .size = SMB2_WSL_XATTR_UID_SIZE, }, ++ { .name = SMB2_WSL_XATTR_GID, .value = gid, .size = SMB2_WSL_XATTR_GID_SIZE, }, ++ { .name = SMB2_WSL_XATTR_MODE, .value = mode, .size = SMB2_WSL_XATTR_MODE_SIZE, }, ++ { .name = SMB2_WSL_XATTR_DEV, .value = dev, .size = SMB2_WSL_XATTR_DEV_SIZE, }, + }; + size_t cc_len; + u32 dlen = 0, next = 0; + int i, num_xattrs; +- u8 name_size = strlen(xattrs[0].name) + 1; ++ u8 name_size = SMB2_WSL_XATTR_NAME_LEN + 1; + + memset(iov, 0, sizeof(*iov)); + +diff --git a/fs/smb/client/smb2glob.h b/fs/smb/client/smb2glob.h +index a0c156996fc51..2466e61551369 100644 +--- a/fs/smb/client/smb2glob.h ++++ b/fs/smb/client/smb2glob.h +@@ -36,7 +36,8 @@ enum smb2_compound_ops { + SMB2_OP_RMDIR, + SMB2_OP_POSIX_QUERY_INFO, + SMB2_OP_SET_REPARSE, +- SMB2_OP_GET_REPARSE ++ SMB2_OP_GET_REPARSE, ++ SMB2_OP_QUERY_WSL_EA, + }; + + /* Used when constructing chained read requests. */ +diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c +index 0e1b9a1552e71..86f8c81791374 100644 +--- a/fs/smb/client/smb2inode.c ++++ b/fs/smb/client/smb2inode.c +@@ -85,6 +85,82 @@ static int parse_posix_sids(struct cifs_open_info_data *data, + return 0; + } + ++struct wsl_query_ea { ++ __le32 next; ++ __u8 name_len; ++ __u8 name[SMB2_WSL_XATTR_NAME_LEN + 1]; ++} __packed; ++ ++#define NEXT_OFF cpu_to_le32(sizeof(struct wsl_query_ea)) ++ ++static const struct wsl_query_ea wsl_query_eas[] = { ++ { .next = NEXT_OFF, .name_len = SMB2_WSL_XATTR_NAME_LEN, .name = SMB2_WSL_XATTR_UID, }, ++ { .next = NEXT_OFF, .name_len = SMB2_WSL_XATTR_NAME_LEN, .name = SMB2_WSL_XATTR_GID, }, ++ { .next = NEXT_OFF, .name_len = SMB2_WSL_XATTR_NAME_LEN, .name = SMB2_WSL_XATTR_MODE, }, ++ { .next = 0, .name_len = SMB2_WSL_XATTR_NAME_LEN, .name = SMB2_WSL_XATTR_DEV, }, ++}; ++ ++static int check_wsl_eas(struct kvec *rsp_iov) ++{ ++ struct smb2_file_full_ea_info *ea; ++ struct smb2_query_info_rsp *rsp = rsp_iov->iov_base; ++ unsigned long addr; ++ u32 outlen, next; ++ u16 vlen; ++ u8 nlen; ++ u8 *end; ++ ++ outlen = le32_to_cpu(rsp->OutputBufferLength); ++ if (outlen < SMB2_WSL_MIN_QUERY_EA_RESP_SIZE || ++ outlen > SMB2_WSL_MAX_QUERY_EA_RESP_SIZE) ++ return -EINVAL; ++ ++ ea = (void *)((u8 *)rsp_iov->iov_base + ++ le16_to_cpu(rsp->OutputBufferOffset)); ++ end = (u8 *)rsp_iov->iov_base + rsp_iov->iov_len; ++ for (;;) { ++ if ((u8 *)ea > end - sizeof(*ea)) ++ return -EINVAL; ++ ++ nlen = ea->ea_name_length; ++ vlen = le16_to_cpu(ea->ea_value_length); ++ if (nlen != SMB2_WSL_XATTR_NAME_LEN || ++ (u8 *)ea + nlen + 1 + vlen > end) ++ return -EINVAL; ++ ++ switch (vlen) { ++ case 4: ++ if (strncmp(ea->ea_data, SMB2_WSL_XATTR_UID, nlen) && ++ strncmp(ea->ea_data, SMB2_WSL_XATTR_GID, nlen) && ++ strncmp(ea->ea_data, SMB2_WSL_XATTR_MODE, nlen)) ++ return -EINVAL; ++ break; ++ case 8: ++ if (strncmp(ea->ea_data, SMB2_WSL_XATTR_DEV, nlen)) ++ return -EINVAL; ++ break; ++ case 0: ++ if (!strncmp(ea->ea_data, SMB2_WSL_XATTR_UID, nlen) || ++ !strncmp(ea->ea_data, SMB2_WSL_XATTR_GID, nlen) || ++ !strncmp(ea->ea_data, SMB2_WSL_XATTR_MODE, nlen) || ++ !strncmp(ea->ea_data, SMB2_WSL_XATTR_DEV, nlen)) ++ break; ++ fallthrough; ++ default: ++ return -EINVAL; ++ } ++ ++ next = le32_to_cpu(ea->next_entry_offset); ++ if (!next) ++ break; ++ if (!IS_ALIGNED(next, 4) || ++ check_add_overflow((unsigned long)ea, next, &addr)) ++ return -EINVAL; ++ ea = (void *)addr; ++ } ++ return 0; ++} ++ + /* + * note: If cfile is passed, the reference to it is dropped here. + * So make sure that you do not reuse cfile after return from this func. +@@ -119,7 +195,7 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, + __u8 delete_pending[8] = {1, 0, 0, 0, 0, 0, 0, 0}; + unsigned int size[2]; + void *data[2]; +- int len; ++ unsigned int len; + int retries = 0, cur_sleep = 1; + + replay_again: +@@ -476,6 +552,39 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, + trace_smb3_get_reparse_compound_enter(xid, ses->Suid, + tcon->tid, full_path); + break; ++ case SMB2_OP_QUERY_WSL_EA: ++ rqst[num_rqst].rq_iov = &vars->ea_iov; ++ rqst[num_rqst].rq_nvec = 1; ++ ++ if (cfile) { ++ rc = SMB2_query_info_init(tcon, server, ++ &rqst[num_rqst], ++ cfile->fid.persistent_fid, ++ cfile->fid.volatile_fid, ++ FILE_FULL_EA_INFORMATION, ++ SMB2_O_INFO_FILE, 0, ++ SMB2_WSL_MAX_QUERY_EA_RESP_SIZE, ++ sizeof(wsl_query_eas), ++ (void *)wsl_query_eas); ++ } else { ++ rc = SMB2_query_info_init(tcon, server, ++ &rqst[num_rqst], ++ COMPOUND_FID, ++ COMPOUND_FID, ++ FILE_FULL_EA_INFORMATION, ++ SMB2_O_INFO_FILE, 0, ++ SMB2_WSL_MAX_QUERY_EA_RESP_SIZE, ++ sizeof(wsl_query_eas), ++ (void *)wsl_query_eas); ++ } ++ if (!rc && (!cfile || num_rqst > 1)) { ++ smb2_set_next_command(tcon, &rqst[num_rqst]); ++ smb2_set_related(&rqst[num_rqst]); ++ } else if (rc) { ++ goto finished; ++ } ++ num_rqst++; ++ break; + default: + cifs_dbg(VFS, "Invalid command\n"); + rc = -EINVAL; +@@ -665,11 +774,32 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, + memset(iov, 0, sizeof(*iov)); + resp_buftype[i + 1] = CIFS_NO_BUFFER; + } else { +- trace_smb3_set_reparse_compound_err(xid, ses->Suid, ++ trace_smb3_set_reparse_compound_err(xid, ses->Suid, + tcon->tid, rc); + } + SMB2_ioctl_free(&rqst[num_rqst++]); + break; ++ case SMB2_OP_QUERY_WSL_EA: ++ if (!rc) { ++ idata = in_iov[i].iov_base; ++ qi_rsp = rsp_iov[i + 1].iov_base; ++ data[0] = (u8 *)qi_rsp + le16_to_cpu(qi_rsp->OutputBufferOffset); ++ size[0] = le32_to_cpu(qi_rsp->OutputBufferLength); ++ rc = check_wsl_eas(&rsp_iov[i + 1]); ++ if (!rc) { ++ memcpy(idata->wsl.eas, data[0], size[0]); ++ idata->wsl.eas_len = size[0]; ++ } ++ } ++ if (!rc) { ++ trace_smb3_query_wsl_ea_compound_done(xid, ses->Suid, ++ tcon->tid); ++ } else { ++ trace_smb3_query_wsl_ea_compound_err(xid, ses->Suid, ++ tcon->tid, rc); ++ } ++ SMB2_query_info_free(&rqst[num_rqst++]); ++ break; + } + } + SMB2_close_free(&rqst[num_rqst]); +@@ -737,11 +867,11 @@ int smb2_query_path_info(const unsigned int xid, + struct cifsFileInfo *cfile; + struct cached_fid *cfid = NULL; + struct smb2_hdr *hdr; +- struct kvec in_iov[2], out_iov[3] = {}; ++ struct kvec in_iov[3], out_iov[3] = {}; + int out_buftype[3] = {}; +- int cmds[2]; ++ int cmds[3]; + bool islink; +- int i, num_cmds; ++ int i, num_cmds = 0; + int rc, rc2; + + data->adjust_tz = false; +@@ -774,21 +904,22 @@ int smb2_query_path_info(const unsigned int xid, + close_cached_dir(cfid); + return rc; + } +- cmds[0] = SMB2_OP_QUERY_INFO; ++ cmds[num_cmds++] = SMB2_OP_QUERY_INFO; + } else { +- cmds[0] = SMB2_OP_POSIX_QUERY_INFO; ++ cmds[num_cmds++] = SMB2_OP_POSIX_QUERY_INFO; + } + + in_iov[0].iov_base = data; + in_iov[0].iov_len = sizeof(*data); + in_iov[1] = in_iov[0]; ++ in_iov[2] = in_iov[0]; + + cifs_get_readable_path(tcon, full_path, &cfile); + oparms = CIFS_OPARMS(cifs_sb, tcon, full_path, FILE_READ_ATTRIBUTES, + FILE_OPEN, create_options, ACL_NO_MODE); + rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, +- &oparms, in_iov, cmds, 1, cfile, +- out_iov, out_buftype, NULL); ++ &oparms, in_iov, cmds, num_cmds, ++ cfile, out_iov, out_buftype, NULL); + hdr = out_iov[0].iov_base; + /* + * If first iov is unset, then SMB session was dropped or we've got a +@@ -808,17 +939,18 @@ int smb2_query_path_info(const unsigned int xid, + if (rc || !data->reparse_point) + goto out; + +- if (data->reparse.tag == IO_REPARSE_TAG_SYMLINK) { +- /* symlink already parsed in create response */ +- num_cmds = 1; +- } else { +- cmds[1] = SMB2_OP_GET_REPARSE; +- num_cmds = 2; +- } ++ cmds[num_cmds++] = SMB2_OP_QUERY_WSL_EA; ++ /* ++ * Skip SMB2_OP_GET_REPARSE if symlink already parsed in create ++ * response. ++ */ ++ if (data->reparse.tag != IO_REPARSE_TAG_SYMLINK) ++ cmds[num_cmds++] = SMB2_OP_GET_REPARSE; ++ + oparms = CIFS_OPARMS(cifs_sb, tcon, full_path, +- FILE_READ_ATTRIBUTES, FILE_OPEN, +- create_options | OPEN_REPARSE_POINT, +- ACL_NO_MODE); ++ FILE_READ_ATTRIBUTES | FILE_READ_EA, ++ FILE_OPEN, create_options | ++ OPEN_REPARSE_POINT, ACL_NO_MODE); + cifs_get_readable_path(tcon, full_path, &cfile); + rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, + &oparms, in_iov, cmds, num_cmds, +diff --git a/fs/smb/client/smb2pdu.h b/fs/smb/client/smb2pdu.h +index 3334f2c364fb9..2fccf0d4f53d2 100644 +--- a/fs/smb/client/smb2pdu.h ++++ b/fs/smb/client/smb2pdu.h +@@ -420,4 +420,29 @@ struct smb2_create_ea_ctx { + struct smb2_file_full_ea_info ea; + } __packed; + ++#define SMB2_WSL_XATTR_UID "$LXUID" ++#define SMB2_WSL_XATTR_GID "$LXGID" ++#define SMB2_WSL_XATTR_MODE "$LXMOD" ++#define SMB2_WSL_XATTR_DEV "$LXDEV" ++#define SMB2_WSL_XATTR_NAME_LEN 6 ++#define SMB2_WSL_NUM_XATTRS 4 ++ ++#define SMB2_WSL_XATTR_UID_SIZE 4 ++#define SMB2_WSL_XATTR_GID_SIZE 4 ++#define SMB2_WSL_XATTR_MODE_SIZE 4 ++#define SMB2_WSL_XATTR_DEV_SIZE 8 ++ ++#define SMB2_WSL_MIN_QUERY_EA_RESP_SIZE \ ++ (ALIGN((SMB2_WSL_NUM_XATTRS - 1) * \ ++ (SMB2_WSL_XATTR_NAME_LEN + 1 + \ ++ sizeof(struct smb2_file_full_ea_info)), 4) + \ ++ SMB2_WSL_XATTR_NAME_LEN + 1 + sizeof(struct smb2_file_full_ea_info)) ++ ++#define SMB2_WSL_MAX_QUERY_EA_RESP_SIZE \ ++ (ALIGN(SMB2_WSL_MIN_QUERY_EA_RESP_SIZE + \ ++ SMB2_WSL_XATTR_UID_SIZE + \ ++ SMB2_WSL_XATTR_GID_SIZE + \ ++ SMB2_WSL_XATTR_MODE_SIZE + \ ++ SMB2_WSL_XATTR_DEV_SIZE, 4)) ++ + #endif /* _SMB2PDU_H */ +diff --git a/fs/smb/client/trace.h b/fs/smb/client/trace.h +index 522fa387fcfd7..ce90ae0d77f84 100644 +--- a/fs/smb/client/trace.h ++++ b/fs/smb/client/trace.h +@@ -411,6 +411,7 @@ DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(set_eof_done); + DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(set_info_compound_done); + DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(set_reparse_compound_done); + DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(get_reparse_compound_done); ++DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(query_wsl_ea_compound_done); + DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(delete_done); + DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(mkdir_done); + DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(tdis_done); +@@ -456,6 +457,7 @@ DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(set_eof_err); + DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(set_info_compound_err); + DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(set_reparse_compound_err); + DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(get_reparse_compound_err); ++DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(query_wsl_ea_compound_err); + DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(mkdir_err); + DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(delete_err); + DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(tdis_err); +-- +2.43.0 + diff --git a/queue-6.6/smb-client-move-most-of-reparse-point-handling-code-.patch b/queue-6.6/smb-client-move-most-of-reparse-point-handling-code-.patch new file mode 100644 index 0000000000..328c8681ee --- /dev/null +++ b/queue-6.6/smb-client-move-most-of-reparse-point-handling-code-.patch @@ -0,0 +1,924 @@ +From dcd2e2d7da6342b1ad248db0514e7177fafa64b7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Tue, 5 Mar 2024 23:28:48 -0600 +Subject: smb: client: move most of reparse point handling code to common file + +From: Paulo Alcantara <pc@manguebit.com> + +[ Upstream commit c520ba7573a84bd37f8803a3beeb8f6f995bf9e1 ] + +In preparation to add support for creating special files also via WSL +reparse points in next commits. + +Signed-off-by: Paulo Alcantara <pc@manguebit.com> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/client/Makefile | 2 +- + fs/smb/client/cifsglob.h | 13 -- + fs/smb/client/cifsproto.h | 4 - + fs/smb/client/inode.c | 79 +--------- + fs/smb/client/readdir.c | 18 +-- + fs/smb/client/reparse.c | 320 ++++++++++++++++++++++++++++++++++++++ + fs/smb/client/reparse.h | 73 +++++++++ + fs/smb/client/smb2ops.c | 254 +----------------------------- + fs/smb/client/smb2proto.h | 6 + + 9 files changed, 405 insertions(+), 364 deletions(-) + create mode 100644 fs/smb/client/reparse.c + create mode 100644 fs/smb/client/reparse.h + +diff --git a/fs/smb/client/Makefile b/fs/smb/client/Makefile +index 0b07eb94c93b3..e11985f2460b2 100644 +--- a/fs/smb/client/Makefile ++++ b/fs/smb/client/Makefile +@@ -12,7 +12,7 @@ cifs-y := trace.o cifsfs.o cifs_debug.o connect.o dir.o file.o \ + smb2ops.o smb2maperror.o smb2transport.o \ + smb2misc.o smb2pdu.o smb2inode.o smb2file.o cifsacl.o fs_context.o \ + dns_resolve.o cifs_spnego_negtokeninit.asn1.o asn1.o \ +- namespace.o ++ namespace.o reparse.o + + $(obj)/asn1.o: $(obj)/cifs_spnego_negtokeninit.asn1.h + +diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h +index 08540541046c1..296ed556be0e2 100644 +--- a/fs/smb/client/cifsglob.h ++++ b/fs/smb/client/cifsglob.h +@@ -223,19 +223,6 @@ struct cifs_open_info_data { + }; + }; + +-static inline bool cifs_open_data_reparse(struct cifs_open_info_data *data) +-{ +- struct smb2_file_all_info *fi = &data->fi; +- u32 attrs = le32_to_cpu(fi->Attributes); +- bool ret; +- +- ret = data->reparse_point || (attrs & ATTR_REPARSE); +- if (ret) +- attrs |= ATTR_REPARSE; +- fi->Attributes = cpu_to_le32(attrs); +- return ret; +-} +- + /* + ***************************************************************** + * Except the CIFS PDUs themselves all the +diff --git a/fs/smb/client/cifsproto.h b/fs/smb/client/cifsproto.h +index 50040990e70b9..8e0a348f1f660 100644 +--- a/fs/smb/client/cifsproto.h ++++ b/fs/smb/client/cifsproto.h +@@ -210,10 +210,6 @@ extern struct inode *cifs_iget(struct super_block *sb, + int cifs_get_inode_info(struct inode **inode, const char *full_path, + struct cifs_open_info_data *data, struct super_block *sb, int xid, + const struct cifs_fid *fid); +-bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb, +- struct cifs_fattr *fattr, +- struct cifs_open_info_data *data); +- + extern int smb311_posix_get_inode_info(struct inode **inode, + const char *full_path, + struct cifs_open_info_data *data, +diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c +index 2739cb8390804..8aff8382cfb54 100644 +--- a/fs/smb/client/inode.c ++++ b/fs/smb/client/inode.c +@@ -26,6 +26,7 @@ + #include "fs_context.h" + #include "cifs_ioctl.h" + #include "cached_dir.h" ++#include "reparse.h" + + static void cifs_set_ops(struct inode *inode) + { +@@ -728,84 +729,6 @@ static void smb311_posix_info_to_fattr(struct cifs_fattr *fattr, + fattr->cf_mode, fattr->cf_uniqueid, fattr->cf_nlink); + } + +-static inline dev_t nfs_mkdev(struct reparse_posix_data *buf) +-{ +- u64 v = le64_to_cpu(*(__le64 *)buf->DataBuffer); +- +- return MKDEV(v >> 32, v & 0xffffffff); +-} +- +-bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb, +- struct cifs_fattr *fattr, +- struct cifs_open_info_data *data) +-{ +- struct reparse_posix_data *buf = data->reparse.posix; +- u32 tag = data->reparse.tag; +- +- if (tag == IO_REPARSE_TAG_NFS && buf) { +- switch (le64_to_cpu(buf->InodeType)) { +- case NFS_SPECFILE_CHR: +- fattr->cf_mode |= S_IFCHR; +- fattr->cf_dtype = DT_CHR; +- fattr->cf_rdev = nfs_mkdev(buf); +- break; +- case NFS_SPECFILE_BLK: +- fattr->cf_mode |= S_IFBLK; +- fattr->cf_dtype = DT_BLK; +- fattr->cf_rdev = nfs_mkdev(buf); +- break; +- case NFS_SPECFILE_FIFO: +- fattr->cf_mode |= S_IFIFO; +- fattr->cf_dtype = DT_FIFO; +- break; +- case NFS_SPECFILE_SOCK: +- fattr->cf_mode |= S_IFSOCK; +- fattr->cf_dtype = DT_SOCK; +- break; +- case NFS_SPECFILE_LNK: +- fattr->cf_mode |= S_IFLNK; +- fattr->cf_dtype = DT_LNK; +- break; +- default: +- WARN_ON_ONCE(1); +- return false; +- } +- return true; +- } +- +- switch (tag) { +- case IO_REPARSE_TAG_LX_SYMLINK: +- fattr->cf_mode |= S_IFLNK; +- fattr->cf_dtype = DT_LNK; +- break; +- case IO_REPARSE_TAG_LX_FIFO: +- fattr->cf_mode |= S_IFIFO; +- fattr->cf_dtype = DT_FIFO; +- break; +- case IO_REPARSE_TAG_AF_UNIX: +- fattr->cf_mode |= S_IFSOCK; +- fattr->cf_dtype = DT_SOCK; +- break; +- case IO_REPARSE_TAG_LX_CHR: +- fattr->cf_mode |= S_IFCHR; +- fattr->cf_dtype = DT_CHR; +- break; +- case IO_REPARSE_TAG_LX_BLK: +- fattr->cf_mode |= S_IFBLK; +- fattr->cf_dtype = DT_BLK; +- break; +- case 0: /* SMB1 symlink */ +- case IO_REPARSE_TAG_SYMLINK: +- case IO_REPARSE_TAG_NFS: +- fattr->cf_mode |= S_IFLNK; +- fattr->cf_dtype = DT_LNK; +- break; +- default: +- return false; +- } +- return true; +-} +- + static void cifs_open_info_to_fattr(struct cifs_fattr *fattr, + struct cifs_open_info_data *data, + struct super_block *sb) +diff --git a/fs/smb/client/readdir.c b/fs/smb/client/readdir.c +index 73ff9bd059682..3e5d22b356e92 100644 +--- a/fs/smb/client/readdir.c ++++ b/fs/smb/client/readdir.c +@@ -22,6 +22,7 @@ + #include "smb2proto.h" + #include "fs_context.h" + #include "cached_dir.h" ++#include "reparse.h" + + /* + * To be safe - for UCS to UTF-8 with strings loaded with the rare long +@@ -55,23 +56,6 @@ static inline void dump_cifs_file_struct(struct file *file, char *label) + } + #endif /* DEBUG2 */ + +-/* +- * Match a reparse point inode if reparse tag and ctime haven't changed. +- * +- * Windows Server updates ctime of reparse points when their data have changed. +- * The server doesn't allow changing reparse tags from existing reparse points, +- * though it's worth checking. +- */ +-static inline bool reparse_inode_match(struct inode *inode, +- struct cifs_fattr *fattr) +-{ +- struct timespec64 ctime = inode_get_ctime(inode); +- +- return (CIFS_I(inode)->cifsAttrs & ATTR_REPARSE) && +- CIFS_I(inode)->reparse_tag == fattr->cf_cifstag && +- timespec64_equal(&ctime, &fattr->cf_ctime); +-} +- + /* + * Attempt to preload the dcache with the results from the FIND_FIRST/NEXT + * +diff --git a/fs/smb/client/reparse.c b/fs/smb/client/reparse.c +new file mode 100644 +index 0000000000000..c405be47c84d9 +--- /dev/null ++++ b/fs/smb/client/reparse.c +@@ -0,0 +1,320 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Copyright (c) 2024 Paulo Alcantara <pc@manguebit.com> ++ */ ++ ++#include <linux/fs.h> ++#include <linux/stat.h> ++#include <linux/slab.h> ++#include "cifsglob.h" ++#include "smb2proto.h" ++#include "cifsproto.h" ++#include "cifs_unicode.h" ++#include "cifs_debug.h" ++#include "reparse.h" ++ ++int smb2_create_reparse_symlink(const unsigned int xid, struct inode *inode, ++ struct dentry *dentry, struct cifs_tcon *tcon, ++ const char *full_path, const char *symname) ++{ ++ struct reparse_symlink_data_buffer *buf = NULL; ++ struct cifs_open_info_data data; ++ struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); ++ struct inode *new; ++ struct kvec iov; ++ __le16 *path; ++ char *sym, sep = CIFS_DIR_SEP(cifs_sb); ++ u16 len, plen; ++ int rc = 0; ++ ++ sym = kstrdup(symname, GFP_KERNEL); ++ if (!sym) ++ return -ENOMEM; ++ ++ data = (struct cifs_open_info_data) { ++ .reparse_point = true, ++ .reparse = { .tag = IO_REPARSE_TAG_SYMLINK, }, ++ .symlink_target = sym, ++ }; ++ ++ convert_delimiter(sym, sep); ++ path = cifs_convert_path_to_utf16(sym, cifs_sb); ++ if (!path) { ++ rc = -ENOMEM; ++ goto out; ++ } ++ ++ plen = 2 * UniStrnlen((wchar_t *)path, PATH_MAX); ++ len = sizeof(*buf) + plen * 2; ++ buf = kzalloc(len, GFP_KERNEL); ++ if (!buf) { ++ rc = -ENOMEM; ++ goto out; ++ } ++ ++ buf->ReparseTag = cpu_to_le32(IO_REPARSE_TAG_SYMLINK); ++ buf->ReparseDataLength = cpu_to_le16(len - sizeof(struct reparse_data_buffer)); ++ buf->SubstituteNameOffset = cpu_to_le16(plen); ++ buf->SubstituteNameLength = cpu_to_le16(plen); ++ memcpy(&buf->PathBuffer[plen], path, plen); ++ buf->PrintNameOffset = 0; ++ buf->PrintNameLength = cpu_to_le16(plen); ++ memcpy(buf->PathBuffer, path, plen); ++ buf->Flags = cpu_to_le32(*symname != '/' ? SYMLINK_FLAG_RELATIVE : 0); ++ if (*sym != sep) ++ buf->Flags = cpu_to_le32(SYMLINK_FLAG_RELATIVE); ++ ++ convert_delimiter(sym, '/'); ++ iov.iov_base = buf; ++ iov.iov_len = len; ++ new = smb2_get_reparse_inode(&data, inode->i_sb, xid, ++ tcon, full_path, &iov); ++ if (!IS_ERR(new)) ++ d_instantiate(dentry, new); ++ else ++ rc = PTR_ERR(new); ++out: ++ kfree(path); ++ cifs_free_open_info(&data); ++ kfree(buf); ++ return rc; ++} ++ ++static int nfs_set_reparse_buf(struct reparse_posix_data *buf, ++ mode_t mode, dev_t dev, ++ struct kvec *iov) ++{ ++ u64 type; ++ u16 len, dlen; ++ ++ len = sizeof(*buf); ++ ++ switch ((type = reparse_mode_nfs_type(mode))) { ++ case NFS_SPECFILE_BLK: ++ case NFS_SPECFILE_CHR: ++ dlen = sizeof(__le64); ++ break; ++ case NFS_SPECFILE_FIFO: ++ case NFS_SPECFILE_SOCK: ++ dlen = 0; ++ break; ++ default: ++ return -EOPNOTSUPP; ++ } ++ ++ buf->ReparseTag = cpu_to_le32(IO_REPARSE_TAG_NFS); ++ buf->Reserved = 0; ++ buf->InodeType = cpu_to_le64(type); ++ buf->ReparseDataLength = cpu_to_le16(len + dlen - ++ sizeof(struct reparse_data_buffer)); ++ *(__le64 *)buf->DataBuffer = cpu_to_le64(((u64)MAJOR(dev) << 32) | ++ MINOR(dev)); ++ iov->iov_base = buf; ++ iov->iov_len = len + dlen; ++ return 0; ++} ++ ++int smb2_make_nfs_node(unsigned int xid, struct inode *inode, ++ struct dentry *dentry, struct cifs_tcon *tcon, ++ const char *full_path, umode_t mode, dev_t dev) ++{ ++ struct cifs_open_info_data data; ++ struct reparse_posix_data *p; ++ struct inode *new; ++ struct kvec iov; ++ __u8 buf[sizeof(*p) + sizeof(__le64)]; ++ int rc; ++ ++ p = (struct reparse_posix_data *)buf; ++ rc = nfs_set_reparse_buf(p, mode, dev, &iov); ++ if (rc) ++ return rc; ++ ++ data = (struct cifs_open_info_data) { ++ .reparse_point = true, ++ .reparse = { .tag = IO_REPARSE_TAG_NFS, .posix = p, }, ++ }; ++ ++ new = smb2_get_reparse_inode(&data, inode->i_sb, xid, ++ tcon, full_path, &iov); ++ if (!IS_ERR(new)) ++ d_instantiate(dentry, new); ++ else ++ rc = PTR_ERR(new); ++ cifs_free_open_info(&data); ++ return rc; ++} ++ ++/* See MS-FSCC 2.1.2.6 for the 'NFS' style reparse tags */ ++static int parse_reparse_posix(struct reparse_posix_data *buf, ++ struct cifs_sb_info *cifs_sb, ++ struct cifs_open_info_data *data) ++{ ++ unsigned int len; ++ u64 type; ++ ++ switch ((type = le64_to_cpu(buf->InodeType))) { ++ case NFS_SPECFILE_LNK: ++ len = le16_to_cpu(buf->ReparseDataLength); ++ data->symlink_target = cifs_strndup_from_utf16(buf->DataBuffer, ++ len, true, ++ cifs_sb->local_nls); ++ if (!data->symlink_target) ++ return -ENOMEM; ++ convert_delimiter(data->symlink_target, '/'); ++ cifs_dbg(FYI, "%s: target path: %s\n", ++ __func__, data->symlink_target); ++ break; ++ case NFS_SPECFILE_CHR: ++ case NFS_SPECFILE_BLK: ++ case NFS_SPECFILE_FIFO: ++ case NFS_SPECFILE_SOCK: ++ break; ++ default: ++ cifs_dbg(VFS, "%s: unhandled inode type: 0x%llx\n", ++ __func__, type); ++ return -EOPNOTSUPP; ++ } ++ return 0; ++} ++ ++static int parse_reparse_symlink(struct reparse_symlink_data_buffer *sym, ++ u32 plen, bool unicode, ++ struct cifs_sb_info *cifs_sb, ++ struct cifs_open_info_data *data) ++{ ++ unsigned int len; ++ unsigned int offs; ++ ++ /* We handle Symbolic Link reparse tag here. See: MS-FSCC 2.1.2.4 */ ++ ++ offs = le16_to_cpu(sym->SubstituteNameOffset); ++ len = le16_to_cpu(sym->SubstituteNameLength); ++ if (offs + 20 > plen || offs + len + 20 > plen) { ++ cifs_dbg(VFS, "srv returned malformed symlink buffer\n"); ++ return -EIO; ++ } ++ ++ data->symlink_target = cifs_strndup_from_utf16(sym->PathBuffer + offs, ++ len, unicode, ++ cifs_sb->local_nls); ++ if (!data->symlink_target) ++ return -ENOMEM; ++ ++ convert_delimiter(data->symlink_target, '/'); ++ cifs_dbg(FYI, "%s: target path: %s\n", __func__, data->symlink_target); ++ ++ return 0; ++} ++ ++int parse_reparse_point(struct reparse_data_buffer *buf, ++ u32 plen, struct cifs_sb_info *cifs_sb, ++ bool unicode, struct cifs_open_info_data *data) ++{ ++ data->reparse.buf = buf; ++ ++ /* See MS-FSCC 2.1.2 */ ++ switch (le32_to_cpu(buf->ReparseTag)) { ++ case IO_REPARSE_TAG_NFS: ++ return parse_reparse_posix((struct reparse_posix_data *)buf, ++ cifs_sb, data); ++ case IO_REPARSE_TAG_SYMLINK: ++ return parse_reparse_symlink( ++ (struct reparse_symlink_data_buffer *)buf, ++ plen, unicode, cifs_sb, data); ++ case IO_REPARSE_TAG_LX_SYMLINK: ++ case IO_REPARSE_TAG_AF_UNIX: ++ case IO_REPARSE_TAG_LX_FIFO: ++ case IO_REPARSE_TAG_LX_CHR: ++ case IO_REPARSE_TAG_LX_BLK: ++ return 0; ++ default: ++ cifs_dbg(VFS, "%s: unhandled reparse tag: 0x%08x\n", ++ __func__, le32_to_cpu(buf->ReparseTag)); ++ return -EOPNOTSUPP; ++ } ++} ++ ++int smb2_parse_reparse_point(struct cifs_sb_info *cifs_sb, ++ struct kvec *rsp_iov, ++ struct cifs_open_info_data *data) ++{ ++ struct reparse_data_buffer *buf; ++ struct smb2_ioctl_rsp *io = rsp_iov->iov_base; ++ u32 plen = le32_to_cpu(io->OutputCount); ++ ++ buf = (struct reparse_data_buffer *)((u8 *)io + ++ le32_to_cpu(io->OutputOffset)); ++ return parse_reparse_point(buf, plen, cifs_sb, true, data); ++} ++ ++bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb, ++ struct cifs_fattr *fattr, ++ struct cifs_open_info_data *data) ++{ ++ struct reparse_posix_data *buf = data->reparse.posix; ++ u32 tag = data->reparse.tag; ++ ++ if (tag == IO_REPARSE_TAG_NFS && buf) { ++ switch (le64_to_cpu(buf->InodeType)) { ++ case NFS_SPECFILE_CHR: ++ fattr->cf_mode |= S_IFCHR; ++ fattr->cf_dtype = DT_CHR; ++ fattr->cf_rdev = reparse_nfs_mkdev(buf); ++ break; ++ case NFS_SPECFILE_BLK: ++ fattr->cf_mode |= S_IFBLK; ++ fattr->cf_dtype = DT_BLK; ++ fattr->cf_rdev = reparse_nfs_mkdev(buf); ++ break; ++ case NFS_SPECFILE_FIFO: ++ fattr->cf_mode |= S_IFIFO; ++ fattr->cf_dtype = DT_FIFO; ++ break; ++ case NFS_SPECFILE_SOCK: ++ fattr->cf_mode |= S_IFSOCK; ++ fattr->cf_dtype = DT_SOCK; ++ break; ++ case NFS_SPECFILE_LNK: ++ fattr->cf_mode |= S_IFLNK; ++ fattr->cf_dtype = DT_LNK; ++ break; ++ default: ++ WARN_ON_ONCE(1); ++ return false; ++ } ++ return true; ++ } ++ ++ switch (tag) { ++ case IO_REPARSE_TAG_LX_SYMLINK: ++ fattr->cf_mode |= S_IFLNK; ++ fattr->cf_dtype = DT_LNK; ++ break; ++ case IO_REPARSE_TAG_LX_FIFO: ++ fattr->cf_mode |= S_IFIFO; ++ fattr->cf_dtype = DT_FIFO; ++ break; ++ case IO_REPARSE_TAG_AF_UNIX: ++ fattr->cf_mode |= S_IFSOCK; ++ fattr->cf_dtype = DT_SOCK; ++ break; ++ case IO_REPARSE_TAG_LX_CHR: ++ fattr->cf_mode |= S_IFCHR; ++ fattr->cf_dtype = DT_CHR; ++ break; ++ case IO_REPARSE_TAG_LX_BLK: ++ fattr->cf_mode |= S_IFBLK; ++ fattr->cf_dtype = DT_BLK; ++ break; ++ case 0: /* SMB1 symlink */ ++ case IO_REPARSE_TAG_SYMLINK: ++ case IO_REPARSE_TAG_NFS: ++ fattr->cf_mode |= S_IFLNK; ++ fattr->cf_dtype = DT_LNK; ++ break; ++ default: ++ return false; ++ } ++ return true; ++} +diff --git a/fs/smb/client/reparse.h b/fs/smb/client/reparse.h +new file mode 100644 +index 0000000000000..3ceb90da0df90 +--- /dev/null ++++ b/fs/smb/client/reparse.h +@@ -0,0 +1,73 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++/* ++ * Copyright (c) 2024 Paulo Alcantara <pc@manguebit.com> ++ */ ++ ++#ifndef _CIFS_REPARSE_H ++#define _CIFS_REPARSE_H ++ ++#include <linux/fs.h> ++#include <linux/stat.h> ++#include "cifsglob.h" ++ ++static inline dev_t reparse_nfs_mkdev(struct reparse_posix_data *buf) ++{ ++ u64 v = le64_to_cpu(*(__le64 *)buf->DataBuffer); ++ ++ return MKDEV(v >> 32, v & 0xffffffff); ++} ++ ++static inline u64 reparse_mode_nfs_type(mode_t mode) ++{ ++ switch (mode & S_IFMT) { ++ case S_IFBLK: return NFS_SPECFILE_BLK; ++ case S_IFCHR: return NFS_SPECFILE_CHR; ++ case S_IFIFO: return NFS_SPECFILE_FIFO; ++ case S_IFSOCK: return NFS_SPECFILE_SOCK; ++ } ++ return 0; ++} ++ ++/* ++ * Match a reparse point inode if reparse tag and ctime haven't changed. ++ * ++ * Windows Server updates ctime of reparse points when their data have changed. ++ * The server doesn't allow changing reparse tags from existing reparse points, ++ * though it's worth checking. ++ */ ++static inline bool reparse_inode_match(struct inode *inode, ++ struct cifs_fattr *fattr) ++{ ++ struct timespec64 ctime = inode_get_ctime(inode); ++ ++ return (CIFS_I(inode)->cifsAttrs & ATTR_REPARSE) && ++ CIFS_I(inode)->reparse_tag == fattr->cf_cifstag && ++ timespec64_equal(&ctime, &fattr->cf_ctime); ++} ++ ++static inline bool cifs_open_data_reparse(struct cifs_open_info_data *data) ++{ ++ struct smb2_file_all_info *fi = &data->fi; ++ u32 attrs = le32_to_cpu(fi->Attributes); ++ bool ret; ++ ++ ret = data->reparse_point || (attrs & ATTR_REPARSE); ++ if (ret) ++ attrs |= ATTR_REPARSE; ++ fi->Attributes = cpu_to_le32(attrs); ++ return ret; ++} ++ ++bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb, ++ struct cifs_fattr *fattr, ++ struct cifs_open_info_data *data); ++int smb2_create_reparse_symlink(const unsigned int xid, struct inode *inode, ++ struct dentry *dentry, struct cifs_tcon *tcon, ++ const char *full_path, const char *symname); ++int smb2_make_nfs_node(unsigned int xid, struct inode *inode, ++ struct dentry *dentry, struct cifs_tcon *tcon, ++ const char *full_path, umode_t mode, dev_t dev); ++int smb2_parse_reparse_point(struct cifs_sb_info *cifs_sb, struct kvec *rsp_iov, ++ struct cifs_open_info_data *data); ++ ++#endif /* _CIFS_REPARSE_H */ +diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c +index 9ade347978709..2c59a6fc2d7c7 100644 +--- a/fs/smb/client/smb2ops.c ++++ b/fs/smb/client/smb2ops.c +@@ -28,6 +28,7 @@ + #include "fscache.h" + #include "fs_context.h" + #include "cached_dir.h" ++#include "reparse.h" + + /* Change credits for different ops and return the total number of credits */ + static int +@@ -2989,109 +2990,6 @@ smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses, + return rc; + } + +-/* See MS-FSCC 2.1.2.6 for the 'NFS' style reparse tags */ +-static int parse_reparse_posix(struct reparse_posix_data *buf, +- struct cifs_sb_info *cifs_sb, +- struct cifs_open_info_data *data) +-{ +- unsigned int len; +- u64 type; +- +- switch ((type = le64_to_cpu(buf->InodeType))) { +- case NFS_SPECFILE_LNK: +- len = le16_to_cpu(buf->ReparseDataLength); +- data->symlink_target = cifs_strndup_from_utf16(buf->DataBuffer, +- len, true, +- cifs_sb->local_nls); +- if (!data->symlink_target) +- return -ENOMEM; +- convert_delimiter(data->symlink_target, '/'); +- cifs_dbg(FYI, "%s: target path: %s\n", +- __func__, data->symlink_target); +- break; +- case NFS_SPECFILE_CHR: +- case NFS_SPECFILE_BLK: +- case NFS_SPECFILE_FIFO: +- case NFS_SPECFILE_SOCK: +- break; +- default: +- cifs_dbg(VFS, "%s: unhandled inode type: 0x%llx\n", +- __func__, type); +- return -EOPNOTSUPP; +- } +- return 0; +-} +- +-static int parse_reparse_symlink(struct reparse_symlink_data_buffer *sym, +- u32 plen, bool unicode, +- struct cifs_sb_info *cifs_sb, +- struct cifs_open_info_data *data) +-{ +- unsigned int len; +- unsigned int offs; +- +- /* We handle Symbolic Link reparse tag here. See: MS-FSCC 2.1.2.4 */ +- +- offs = le16_to_cpu(sym->SubstituteNameOffset); +- len = le16_to_cpu(sym->SubstituteNameLength); +- if (offs + 20 > plen || offs + len + 20 > plen) { +- cifs_dbg(VFS, "srv returned malformed symlink buffer\n"); +- return -EIO; +- } +- +- data->symlink_target = cifs_strndup_from_utf16(sym->PathBuffer + offs, +- len, unicode, +- cifs_sb->local_nls); +- if (!data->symlink_target) +- return -ENOMEM; +- +- convert_delimiter(data->symlink_target, '/'); +- cifs_dbg(FYI, "%s: target path: %s\n", __func__, data->symlink_target); +- +- return 0; +-} +- +-int parse_reparse_point(struct reparse_data_buffer *buf, +- u32 plen, struct cifs_sb_info *cifs_sb, +- bool unicode, struct cifs_open_info_data *data) +-{ +- data->reparse.buf = buf; +- +- /* See MS-FSCC 2.1.2 */ +- switch (le32_to_cpu(buf->ReparseTag)) { +- case IO_REPARSE_TAG_NFS: +- return parse_reparse_posix((struct reparse_posix_data *)buf, +- cifs_sb, data); +- case IO_REPARSE_TAG_SYMLINK: +- return parse_reparse_symlink( +- (struct reparse_symlink_data_buffer *)buf, +- plen, unicode, cifs_sb, data); +- case IO_REPARSE_TAG_LX_SYMLINK: +- case IO_REPARSE_TAG_AF_UNIX: +- case IO_REPARSE_TAG_LX_FIFO: +- case IO_REPARSE_TAG_LX_CHR: +- case IO_REPARSE_TAG_LX_BLK: +- return 0; +- default: +- cifs_dbg(VFS, "%s: unhandled reparse tag: 0x%08x\n", +- __func__, le32_to_cpu(buf->ReparseTag)); +- return -EOPNOTSUPP; +- } +-} +- +-static int smb2_parse_reparse_point(struct cifs_sb_info *cifs_sb, +- struct kvec *rsp_iov, +- struct cifs_open_info_data *data) +-{ +- struct reparse_data_buffer *buf; +- struct smb2_ioctl_rsp *io = rsp_iov->iov_base; +- u32 plen = le32_to_cpu(io->OutputCount); +- +- buf = (struct reparse_data_buffer *)((u8 *)io + +- le32_to_cpu(io->OutputOffset)); +- return parse_reparse_point(buf, plen, cifs_sb, true, data); +-} +- + static struct cifs_ntsd * + get_smb2_acl_by_fid(struct cifs_sb_info *cifs_sb, + const struct cifs_fid *cifsfid, u32 *pacllen, u32 info) +@@ -5123,152 +5021,6 @@ int cifs_sfu_make_node(unsigned int xid, struct inode *inode, + return rc; + } + +-static inline u64 mode_nfs_type(mode_t mode) +-{ +- switch (mode & S_IFMT) { +- case S_IFBLK: return NFS_SPECFILE_BLK; +- case S_IFCHR: return NFS_SPECFILE_CHR; +- case S_IFIFO: return NFS_SPECFILE_FIFO; +- case S_IFSOCK: return NFS_SPECFILE_SOCK; +- } +- return 0; +-} +- +-static int nfs_set_reparse_buf(struct reparse_posix_data *buf, +- mode_t mode, dev_t dev, +- struct kvec *iov) +-{ +- u64 type; +- u16 len, dlen; +- +- len = sizeof(*buf); +- +- switch ((type = mode_nfs_type(mode))) { +- case NFS_SPECFILE_BLK: +- case NFS_SPECFILE_CHR: +- dlen = sizeof(__le64); +- break; +- case NFS_SPECFILE_FIFO: +- case NFS_SPECFILE_SOCK: +- dlen = 0; +- break; +- default: +- return -EOPNOTSUPP; +- } +- +- buf->ReparseTag = cpu_to_le32(IO_REPARSE_TAG_NFS); +- buf->Reserved = 0; +- buf->InodeType = cpu_to_le64(type); +- buf->ReparseDataLength = cpu_to_le16(len + dlen - +- sizeof(struct reparse_data_buffer)); +- *(__le64 *)buf->DataBuffer = cpu_to_le64(((u64)MAJOR(dev) << 32) | +- MINOR(dev)); +- iov->iov_base = buf; +- iov->iov_len = len + dlen; +- return 0; +-} +- +-static int nfs_make_node(unsigned int xid, struct inode *inode, +- struct dentry *dentry, struct cifs_tcon *tcon, +- const char *full_path, umode_t mode, dev_t dev) +-{ +- struct cifs_open_info_data data; +- struct reparse_posix_data *p; +- struct inode *new; +- struct kvec iov; +- __u8 buf[sizeof(*p) + sizeof(__le64)]; +- int rc; +- +- p = (struct reparse_posix_data *)buf; +- rc = nfs_set_reparse_buf(p, mode, dev, &iov); +- if (rc) +- return rc; +- +- data = (struct cifs_open_info_data) { +- .reparse_point = true, +- .reparse = { .tag = IO_REPARSE_TAG_NFS, .posix = p, }, +- }; +- +- new = smb2_get_reparse_inode(&data, inode->i_sb, xid, +- tcon, full_path, &iov); +- if (!IS_ERR(new)) +- d_instantiate(dentry, new); +- else +- rc = PTR_ERR(new); +- cifs_free_open_info(&data); +- return rc; +-} +- +-static int smb2_create_reparse_symlink(const unsigned int xid, +- struct inode *inode, +- struct dentry *dentry, +- struct cifs_tcon *tcon, +- const char *full_path, +- const char *symname) +-{ +- struct reparse_symlink_data_buffer *buf = NULL; +- struct cifs_open_info_data data; +- struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); +- struct inode *new; +- struct kvec iov; +- __le16 *path; +- char *sym, sep = CIFS_DIR_SEP(cifs_sb); +- u16 len, plen; +- int rc = 0; +- +- sym = kstrdup(symname, GFP_KERNEL); +- if (!sym) +- return -ENOMEM; +- +- data = (struct cifs_open_info_data) { +- .reparse_point = true, +- .reparse = { .tag = IO_REPARSE_TAG_SYMLINK, }, +- .symlink_target = sym, +- }; +- +- convert_delimiter(sym, sep); +- path = cifs_convert_path_to_utf16(sym, cifs_sb); +- if (!path) { +- rc = -ENOMEM; +- goto out; +- } +- +- plen = 2 * UniStrnlen((wchar_t *)path, PATH_MAX); +- len = sizeof(*buf) + plen * 2; +- buf = kzalloc(len, GFP_KERNEL); +- if (!buf) { +- rc = -ENOMEM; +- goto out; +- } +- +- buf->ReparseTag = cpu_to_le32(IO_REPARSE_TAG_SYMLINK); +- buf->ReparseDataLength = cpu_to_le16(len - sizeof(struct reparse_data_buffer)); +- buf->SubstituteNameOffset = cpu_to_le16(plen); +- buf->SubstituteNameLength = cpu_to_le16(plen); +- memcpy(&buf->PathBuffer[plen], path, plen); +- buf->PrintNameOffset = 0; +- buf->PrintNameLength = cpu_to_le16(plen); +- memcpy(buf->PathBuffer, path, plen); +- buf->Flags = cpu_to_le32(*symname != '/' ? SYMLINK_FLAG_RELATIVE : 0); +- if (*sym != sep) +- buf->Flags = cpu_to_le32(SYMLINK_FLAG_RELATIVE); +- +- convert_delimiter(sym, '/'); +- iov.iov_base = buf; +- iov.iov_len = len; +- new = smb2_get_reparse_inode(&data, inode->i_sb, xid, +- tcon, full_path, &iov); +- if (!IS_ERR(new)) +- d_instantiate(dentry, new); +- else +- rc = PTR_ERR(new); +-out: +- kfree(path); +- cifs_free_open_info(&data); +- kfree(buf); +- return rc; +-} +- + static int smb2_make_node(unsigned int xid, struct inode *inode, + struct dentry *dentry, struct cifs_tcon *tcon, + const char *full_path, umode_t mode, dev_t dev) +@@ -5286,8 +5038,8 @@ static int smb2_make_node(unsigned int xid, struct inode *inode, + rc = cifs_sfu_make_node(xid, inode, dentry, tcon, + full_path, mode, dev); + } else { +- rc = nfs_make_node(xid, inode, dentry, tcon, +- full_path, mode, dev); ++ rc = smb2_make_nfs_node(xid, inode, dentry, tcon, ++ full_path, mode, dev); + } + return rc; + } +diff --git a/fs/smb/client/smb2proto.h b/fs/smb/client/smb2proto.h +index 221143788a1c0..64a0ef0409a6e 100644 +--- a/fs/smb/client/smb2proto.h ++++ b/fs/smb/client/smb2proto.h +@@ -310,5 +310,11 @@ int smb311_posix_query_path_info(const unsigned int xid, + int posix_info_parse(const void *beg, const void *end, + struct smb2_posix_info_parsed *out); + int posix_info_sid_size(const void *beg, const void *end); ++int smb2_create_reparse_symlink(const unsigned int xid, struct inode *inode, ++ struct dentry *dentry, struct cifs_tcon *tcon, ++ const char *full_path, const char *symname); ++int smb2_make_nfs_node(unsigned int xid, struct inode *inode, ++ struct dentry *dentry, struct cifs_tcon *tcon, ++ const char *full_path, umode_t mode, dev_t dev); + + #endif /* _SMB2PROTO_H */ +-- +2.43.0 + diff --git a/queue-6.6/smb-client-negotiate-compression-algorithms.patch b/queue-6.6/smb-client-negotiate-compression-algorithms.patch new file mode 100644 index 0000000000..c76a72ec71 --- /dev/null +++ b/queue-6.6/smb-client-negotiate-compression-algorithms.patch @@ -0,0 +1,192 @@ +From 39c4a6a119db62fb9d5ab7e929c225f99100101e Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Fri, 23 Feb 2024 11:58:57 -0300 +Subject: smb: client: negotiate compression algorithms + +From: Enzo Matsumiya <ematsumiya@suse.de> + +[ Upstream commit 8fe7062b7d11fcd21c4dcb5f530eaa1a099b24e7 ] + +Change "compress=" mount option to a boolean flag, that, if set, +will enable negotiating compression algorithms with the server. + +Do not de/compress anything for now. + +Signed-off-by: Enzo Matsumiya <ematsumiya@suse.de> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/client/cifs_debug.c | 32 ++++++++++++++++++++++++++------ + fs/smb/client/cifsglob.h | 6 +++++- + fs/smb/client/connect.c | 2 +- + fs/smb/client/fs_context.c | 2 +- + fs/smb/client/fs_context.h | 2 +- + fs/smb/client/smb2pdu.c | 20 +++++++++++++++----- + 6 files changed, 49 insertions(+), 15 deletions(-) + +diff --git a/fs/smb/client/cifs_debug.c b/fs/smb/client/cifs_debug.c +index aa95fa95ca112..c71ae5c043060 100644 +--- a/fs/smb/client/cifs_debug.c ++++ b/fs/smb/client/cifs_debug.c +@@ -280,6 +280,24 @@ static int cifs_debug_files_proc_show(struct seq_file *m, void *v) + return 0; + } + ++static __always_inline const char *compression_alg_str(__le16 alg) ++{ ++ switch (alg) { ++ case SMB3_COMPRESS_NONE: ++ return "NONE"; ++ case SMB3_COMPRESS_LZNT1: ++ return "LZNT1"; ++ case SMB3_COMPRESS_LZ77: ++ return "LZ77"; ++ case SMB3_COMPRESS_LZ77_HUFF: ++ return "LZ77-Huffman"; ++ case SMB3_COMPRESS_PATTERN: ++ return "Pattern_V1"; ++ default: ++ return "invalid"; ++ } ++} ++ + static int cifs_debug_data_proc_show(struct seq_file *m, void *v) + { + struct mid_q_entry *mid_entry; +@@ -425,12 +443,6 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v) + server->echo_credits, + server->oplock_credits, + server->dialect); +- if (server->compress_algorithm == SMB3_COMPRESS_LZNT1) +- seq_printf(m, " COMPRESS_LZNT1"); +- else if (server->compress_algorithm == SMB3_COMPRESS_LZ77) +- seq_printf(m, " COMPRESS_LZ77"); +- else if (server->compress_algorithm == SMB3_COMPRESS_LZ77_HUFF) +- seq_printf(m, " COMPRESS_LZ77_HUFF"); + if (server->sign) + seq_printf(m, " signed"); + if (server->posix_ext_supported) +@@ -462,6 +474,14 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v) + server->leaf_fullpath); + } + ++ seq_puts(m, "\nCompression: "); ++ if (!server->compression.requested) ++ seq_puts(m, "disabled on mount"); ++ else if (server->compression.enabled) ++ seq_printf(m, "enabled (%s)", compression_alg_str(server->compression.alg)); ++ else ++ seq_puts(m, "disabled (not supported by this server)"); ++ + seq_printf(m, "\n\n\tSessions: "); + i = 0; + list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { +diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h +index 053556ca6f011..70a12584375de 100644 +--- a/fs/smb/client/cifsglob.h ++++ b/fs/smb/client/cifsglob.h +@@ -772,7 +772,11 @@ struct TCP_Server_Info { + unsigned int max_write; + unsigned int min_offload; + unsigned int retrans; +- __le16 compress_algorithm; ++ struct { ++ bool requested; /* "compress" mount option set*/ ++ bool enabled; /* actually negotiated with server */ ++ __le16 alg; /* preferred alg negotiated with server */ ++ } compression; + __u16 signing_algorithm; + __le16 cipher_type; + /* save initital negprot hash */ +diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c +index deba1cfd11801..5acfd2057ca04 100644 +--- a/fs/smb/client/connect.c ++++ b/fs/smb/client/connect.c +@@ -1748,7 +1748,7 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx, + tcp_ses->channel_sequence_num = 0; /* only tracked for primary channel */ + tcp_ses->reconnect_instance = 1; + tcp_ses->lstrp = jiffies; +- tcp_ses->compress_algorithm = cpu_to_le16(ctx->compression); ++ tcp_ses->compression.requested = ctx->compress; + spin_lock_init(&tcp_ses->req_lock); + spin_lock_init(&tcp_ses->srv_lock); + spin_lock_init(&tcp_ses->mid_lock); +diff --git a/fs/smb/client/fs_context.c b/fs/smb/client/fs_context.c +index fbaa901d3493a..3bbac925d0766 100644 +--- a/fs/smb/client/fs_context.c ++++ b/fs/smb/client/fs_context.c +@@ -978,7 +978,7 @@ static int smb3_fs_context_parse_param(struct fs_context *fc, + + switch (opt) { + case Opt_compress: +- ctx->compression = UNKNOWN_TYPE; ++ ctx->compress = true; + cifs_dbg(VFS, + "SMB3 compression support is experimental\n"); + break; +diff --git a/fs/smb/client/fs_context.h b/fs/smb/client/fs_context.h +index e77ee81846b31..cf577ec0dd0ac 100644 +--- a/fs/smb/client/fs_context.h ++++ b/fs/smb/client/fs_context.h +@@ -277,7 +277,7 @@ struct smb3_fs_context { + unsigned int max_credits; /* smb3 max_credits 10 < credits < 60000 */ + unsigned int max_channels; + unsigned int max_cached_dirs; +- __u16 compression; /* compression algorithm 0xFFFF default 0=disabled */ ++ bool compress; /* enable SMB2 messages (READ/WRITE) de/compression */ + bool rootfs:1; /* if it's a SMB root file system */ + bool witness:1; /* use witness protocol */ + char *leaf_fullpath; +diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c +index 4d805933e14f3..86c647a947ccd 100644 +--- a/fs/smb/client/smb2pdu.c ++++ b/fs/smb/client/smb2pdu.c +@@ -743,7 +743,7 @@ assemble_neg_contexts(struct smb2_negotiate_req *req, + pneg_ctxt += sizeof(struct smb2_posix_neg_context); + neg_context_count++; + +- if (server->compress_algorithm) { ++ if (server->compression.requested) { + build_compression_ctxt((struct smb2_compression_capabilities_context *) + pneg_ctxt); + ctxt_len = ALIGN(sizeof(struct smb2_compression_capabilities_context), 8); +@@ -791,6 +791,9 @@ static void decode_compress_ctx(struct TCP_Server_Info *server, + struct smb2_compression_capabilities_context *ctxt) + { + unsigned int len = le16_to_cpu(ctxt->DataLength); ++ __le16 alg; ++ ++ server->compression.enabled = false; + + /* + * Caller checked that DataLength remains within SMB boundary. We still +@@ -801,15 +804,22 @@ static void decode_compress_ctx(struct TCP_Server_Info *server, + pr_warn_once("server sent bad compression cntxt\n"); + return; + } ++ + if (le16_to_cpu(ctxt->CompressionAlgorithmCount) != 1) { +- pr_warn_once("Invalid SMB3 compress algorithm count\n"); ++ pr_warn_once("invalid SMB3 compress algorithm count\n"); + return; + } +- if (le16_to_cpu(ctxt->CompressionAlgorithms[0]) > 3) { +- pr_warn_once("unknown compression algorithm\n"); ++ ++ alg = ctxt->CompressionAlgorithms[0]; ++ ++ /* 'NONE' (0) compressor type is never negotiated */ ++ if (alg == 0 || le16_to_cpu(alg) > 3) { ++ pr_warn_once("invalid compression algorithm '%u'\n", alg); + return; + } +- server->compress_algorithm = ctxt->CompressionAlgorithms[0]; ++ ++ server->compression.alg = alg; ++ server->compression.enabled = true; + } + + static int decode_encrypt_ctx(struct TCP_Server_Info *server, +-- +2.43.0 + diff --git a/queue-6.6/smb-client-optimise-reparse-point-querying.patch b/queue-6.6/smb-client-optimise-reparse-point-querying.patch new file mode 100644 index 0000000000..b1916595c6 --- /dev/null +++ b/queue-6.6/smb-client-optimise-reparse-point-querying.patch @@ -0,0 +1,371 @@ +From 53435a6a7f8ec496620979e2d9e62d0e20bf7abb Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Sun, 28 Apr 2024 01:14:29 -0500 +Subject: smb: client: optimise reparse point querying + +From: Steve French <stfrench@microsoft.com> + +[ Upstream commit 67ec9949b0dfe78c99e110dd975eb7dc5645630c ] + +Reduce number of roundtrips to server when querying reparse points in +->query_path_info() by sending a single compound request of +create+get_reparse+get_info+close. + +Signed-off-by: Paulo Alcantara (SUSE) <pc@manguebit.com> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/client/cifsglob.h | 10 ++-- + fs/smb/client/cifsproto.h | 7 +++ + fs/smb/client/inode.c | 5 +- + fs/smb/client/smb2glob.h | 3 +- + fs/smb/client/smb2inode.c | 122 ++++++++++++++++++++++++++++++-------- + fs/smb/client/trace.h | 3 + + 6 files changed, 119 insertions(+), 31 deletions(-) + +diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h +index cb86b1bf69b58..3e7c3c3d73a73 100644 +--- a/fs/smb/client/cifsglob.h ++++ b/fs/smb/client/cifsglob.h +@@ -192,6 +192,11 @@ struct cifs_open_info_data { + bool symlink; + }; + struct { ++ /* ioctl response buffer */ ++ struct { ++ int buftype; ++ struct kvec iov; ++ } io; + __u32 tag; + union { + struct reparse_data_buffer *buf; +@@ -218,11 +223,6 @@ static inline bool cifs_open_data_reparse(struct cifs_open_info_data *data) + return ret; + } + +-static inline void cifs_free_open_info(struct cifs_open_info_data *data) +-{ +- kfree(data->symlink_target); +-} +- + /* + ***************************************************************** + * Except the CIFS PDUs themselves all the +diff --git a/fs/smb/client/cifsproto.h b/fs/smb/client/cifsproto.h +index 49de2545f34ce..996ca413dd8bd 100644 +--- a/fs/smb/client/cifsproto.h ++++ b/fs/smb/client/cifsproto.h +@@ -769,4 +769,11 @@ static inline void release_mid(struct mid_q_entry *mid) + kref_put(&mid->refcount, __release_mid); + } + ++static inline void cifs_free_open_info(struct cifs_open_info_data *data) ++{ ++ kfree(data->symlink_target); ++ free_rsp_buf(data->reparse.io.buftype, data->reparse.io.iov.iov_base); ++ memset(data, 0, sizeof(*data)); ++} ++ + #endif /* _CIFSPROTO_H */ +diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c +index 391839feb29d5..89dfb405f9c1e 100644 +--- a/fs/smb/client/inode.c ++++ b/fs/smb/client/inode.c +@@ -1080,6 +1080,9 @@ static int reparse_info_to_fattr(struct cifs_open_info_data *data, + &rsp_iov, &rsp_buftype); + if (!rc) + iov = &rsp_iov; ++ } else if (data->reparse.io.buftype != CIFS_NO_BUFFER && ++ data->reparse.io.iov.iov_base) { ++ iov = &data->reparse.io.iov; + } + + rc = -EOPNOTSUPP; +@@ -1099,7 +1102,7 @@ static int reparse_info_to_fattr(struct cifs_open_info_data *data, + /* Check for cached reparse point data */ + if (data->symlink_target || data->reparse.buf) { + rc = 0; +- } else if (server->ops->parse_reparse_point) { ++ } else if (iov && server->ops->parse_reparse_point) { + rc = server->ops->parse_reparse_point(cifs_sb, + iov, data); + } +diff --git a/fs/smb/client/smb2glob.h b/fs/smb/client/smb2glob.h +index ca87a0011c337..a0c156996fc51 100644 +--- a/fs/smb/client/smb2glob.h ++++ b/fs/smb/client/smb2glob.h +@@ -35,7 +35,8 @@ enum smb2_compound_ops { + SMB2_OP_SET_EOF, + SMB2_OP_RMDIR, + SMB2_OP_POSIX_QUERY_INFO, +- SMB2_OP_SET_REPARSE ++ SMB2_OP_SET_REPARSE, ++ SMB2_OP_GET_REPARSE + }; + + /* Used when constructing chained read requests. */ +diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c +index e74d3a1e49dfa..11c1e06ab5417 100644 +--- a/fs/smb/client/smb2inode.c ++++ b/fs/smb/client/smb2inode.c +@@ -38,6 +38,24 @@ static inline __u32 file_create_options(struct dentry *dentry) + return 0; + } + ++static struct reparse_data_buffer *reparse_buf_ptr(struct kvec *iov) ++{ ++ struct reparse_data_buffer *buf; ++ struct smb2_ioctl_rsp *io = iov->iov_base; ++ u32 off, count, len; ++ ++ count = le32_to_cpu(io->OutputCount); ++ off = le32_to_cpu(io->OutputOffset); ++ if (check_add_overflow(off, count, &len) || len > iov->iov_len) ++ return ERR_PTR(-EIO); ++ ++ buf = (struct reparse_data_buffer *)((u8 *)io + off); ++ len = sizeof(*buf); ++ if (count < len || count < le16_to_cpu(buf->ReparseDataLength) + len) ++ return ERR_PTR(-EIO); ++ return buf; ++} ++ + /* + * note: If cfile is passed, the reference to it is dropped here. + * So make sure that you do not reuse cfile after return from this func. +@@ -54,8 +72,10 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, + __u8 **extbuf, size_t *extbuflen, + struct kvec *out_iov, int *out_buftype) + { ++ ++ struct reparse_data_buffer *rbuf; + struct smb2_compound_vars *vars = NULL; +- struct kvec *rsp_iov; ++ struct kvec *rsp_iov, *iov; + struct smb_rqst *rqst; + int rc; + __le16 *utf16_path = NULL; +@@ -375,6 +395,21 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, + trace_smb3_set_reparse_compound_enter(xid, ses->Suid, + tcon->tid, full_path); + break; ++ case SMB2_OP_GET_REPARSE: ++ rqst[num_rqst].rq_iov = vars->io_iov; ++ rqst[num_rqst].rq_nvec = ARRAY_SIZE(vars->io_iov); ++ ++ rc = SMB2_ioctl_init(tcon, server, &rqst[num_rqst], ++ COMPOUND_FID, COMPOUND_FID, ++ FSCTL_GET_REPARSE_POINT, ++ NULL, 0, CIFSMaxBufSize); ++ if (rc) ++ goto finished; ++ smb2_set_next_command(tcon, &rqst[num_rqst]); ++ smb2_set_related(&rqst[num_rqst++]); ++ trace_smb3_get_reparse_compound_enter(xid, ses->Suid, ++ tcon->tid, full_path); ++ break; + default: + cifs_dbg(VFS, "Invalid command\n"); + rc = -EINVAL; +@@ -541,6 +576,30 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, + } + SMB2_ioctl_free(&rqst[num_rqst++]); + break; ++ case SMB2_OP_GET_REPARSE: ++ if (!rc) { ++ iov = &rsp_iov[i + 1]; ++ idata = in_iov[i].iov_base; ++ idata->reparse.io.iov = *iov; ++ idata->reparse.io.buftype = resp_buftype[i + 1]; ++ rbuf = reparse_buf_ptr(iov); ++ if (IS_ERR(rbuf)) { ++ rc = PTR_ERR(rbuf); ++ trace_smb3_set_reparse_compound_err(xid, ses->Suid, ++ tcon->tid, rc); ++ } else { ++ idata->reparse.tag = le32_to_cpu(rbuf->ReparseTag); ++ trace_smb3_set_reparse_compound_done(xid, ses->Suid, ++ tcon->tid); ++ } ++ memset(iov, 0, sizeof(*iov)); ++ resp_buftype[i + 1] = CIFS_NO_BUFFER; ++ } else { ++ trace_smb3_set_reparse_compound_err(xid, ses->Suid, ++ tcon->tid, rc); ++ } ++ SMB2_ioctl_free(&rqst[num_rqst++]); ++ break; + } + } + SMB2_close_free(&rqst[num_rqst]); +@@ -601,10 +660,11 @@ int smb2_query_path_info(const unsigned int xid, + struct cifsFileInfo *cfile; + struct cached_fid *cfid = NULL; + struct smb2_hdr *hdr; +- struct kvec in_iov, out_iov[3] = {}; ++ struct kvec in_iov[2], out_iov[3] = {}; + int out_buftype[3] = {}; ++ int cmds[2] = { SMB2_OP_QUERY_INFO, }; + bool islink; +- int cmd = SMB2_OP_QUERY_INFO; ++ int i, num_cmds; + int rc, rc2; + + data->adjust_tz = false; +@@ -626,14 +686,16 @@ int smb2_query_path_info(const unsigned int xid, + return rc; + } + +- in_iov.iov_base = data; +- in_iov.iov_len = sizeof(*data); ++ in_iov[0].iov_base = data; ++ in_iov[0].iov_len = sizeof(*data); ++ in_iov[1] = in_iov[0]; + + cifs_get_readable_path(tcon, full_path, &cfile); + rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, + FILE_READ_ATTRIBUTES, FILE_OPEN, +- create_options, ACL_NO_MODE, &in_iov, +- &cmd, 1, cfile, NULL, NULL, out_iov, out_buftype); ++ create_options, ACL_NO_MODE, ++ in_iov, cmds, 1, cfile, ++ NULL, NULL, out_iov, out_buftype); + hdr = out_iov[0].iov_base; + /* + * If first iov is unset, then SMB session was dropped or we've got a +@@ -649,13 +711,19 @@ int smb2_query_path_info(const unsigned int xid, + if (rc || !data->reparse_point) + goto out; + ++ if (data->reparse.tag == IO_REPARSE_TAG_SYMLINK) { ++ /* symlink already parsed in create response */ ++ num_cmds = 1; ++ } else { ++ cmds[1] = SMB2_OP_GET_REPARSE; ++ num_cmds = 2; ++ } + create_options |= OPEN_REPARSE_POINT; +- /* Failed on a symbolic link - query a reparse point info */ + cifs_get_readable_path(tcon, full_path, &cfile); + rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, + FILE_READ_ATTRIBUTES, FILE_OPEN, +- create_options, ACL_NO_MODE, &in_iov, +- &cmd, 1, cfile, NULL, NULL, NULL, NULL); ++ create_options, ACL_NO_MODE, in_iov, cmds, ++ num_cmds, cfile, NULL, NULL, NULL, NULL); + break; + case -EREMOTE: + break; +@@ -673,9 +741,8 @@ int smb2_query_path_info(const unsigned int xid, + } + + out: +- free_rsp_buf(out_buftype[0], out_iov[0].iov_base); +- free_rsp_buf(out_buftype[1], out_iov[1].iov_base); +- free_rsp_buf(out_buftype[2], out_iov[2].iov_base); ++ for (i = 0; i < ARRAY_SIZE(out_buftype); i++) ++ free_rsp_buf(out_buftype[i], out_iov[i].iov_base); + return rc; + } + +@@ -690,13 +757,14 @@ int smb311_posix_query_path_info(const unsigned int xid, + int rc; + __u32 create_options = 0; + struct cifsFileInfo *cfile; +- struct kvec in_iov, out_iov[3] = {}; ++ struct kvec in_iov[2], out_iov[3] = {}; + int out_buftype[3] = {}; + __u8 *sidsbuf = NULL; + __u8 *sidsbuf_end = NULL; + size_t sidsbuflen = 0; + size_t owner_len, group_len; +- int cmd = SMB2_OP_POSIX_QUERY_INFO; ++ int cmds[2] = { SMB2_OP_POSIX_QUERY_INFO, }; ++ int i, num_cmds; + + data->adjust_tz = false; + data->reparse_point = false; +@@ -707,13 +775,14 @@ int smb311_posix_query_path_info(const unsigned int xid, + * when we already have an open file handle for this. For now this is fast enough + * (always using the compounded version). + */ +- in_iov.iov_base = data; +- in_iov.iov_len = sizeof(*data); ++ in_iov[0].iov_base = data; ++ in_iov[0].iov_len = sizeof(*data); ++ in_iov[1] = in_iov[0]; + + cifs_get_readable_path(tcon, full_path, &cfile); + rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, + FILE_READ_ATTRIBUTES, FILE_OPEN, +- create_options, ACL_NO_MODE, &in_iov, &cmd, 1, ++ create_options, ACL_NO_MODE, in_iov, cmds, 1, + cfile, &sidsbuf, &sidsbuflen, out_iov, out_buftype); + /* + * If first iov is unset, then SMB session was dropped or we've got a +@@ -730,13 +799,19 @@ int smb311_posix_query_path_info(const unsigned int xid, + if (rc || !data->reparse_point) + goto out; + ++ if (data->reparse.tag == IO_REPARSE_TAG_SYMLINK) { ++ /* symlink already parsed in create response */ ++ num_cmds = 1; ++ } else { ++ cmds[1] = SMB2_OP_GET_REPARSE; ++ num_cmds = 2; ++ } + create_options |= OPEN_REPARSE_POINT; +- /* Failed on a symbolic link - query a reparse point info */ + cifs_get_readable_path(tcon, full_path, &cfile); + rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, + FILE_READ_ATTRIBUTES, FILE_OPEN, +- create_options, ACL_NO_MODE, &in_iov, &cmd, 1, +- cfile, &sidsbuf, &sidsbuflen, NULL, NULL); ++ create_options, ACL_NO_MODE, in_iov, cmds, ++ num_cmds, cfile, &sidsbuf, &sidsbuflen, NULL, NULL); + break; + } + +@@ -761,9 +836,8 @@ int smb311_posix_query_path_info(const unsigned int xid, + } + + kfree(sidsbuf); +- free_rsp_buf(out_buftype[0], out_iov[0].iov_base); +- free_rsp_buf(out_buftype[1], out_iov[1].iov_base); +- free_rsp_buf(out_buftype[2], out_iov[2].iov_base); ++ for (i = 0; i < ARRAY_SIZE(out_buftype); i++) ++ free_rsp_buf(out_buftype[i], out_iov[i].iov_base); + return rc; + } + +diff --git a/fs/smb/client/trace.h b/fs/smb/client/trace.h +index 34f507584274b..522fa387fcfd7 100644 +--- a/fs/smb/client/trace.h ++++ b/fs/smb/client/trace.h +@@ -371,6 +371,7 @@ DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(rmdir_enter); + DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(set_eof_enter); + DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(set_info_compound_enter); + DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(set_reparse_compound_enter); ++DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(get_reparse_compound_enter); + DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(delete_enter); + DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(mkdir_enter); + DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(tdis_enter); +@@ -409,6 +410,7 @@ DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(rmdir_done); + DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(set_eof_done); + DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(set_info_compound_done); + DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(set_reparse_compound_done); ++DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(get_reparse_compound_done); + DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(delete_done); + DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(mkdir_done); + DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(tdis_done); +@@ -453,6 +455,7 @@ DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(rmdir_err); + DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(set_eof_err); + DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(set_info_compound_err); + DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(set_reparse_compound_err); ++DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(get_reparse_compound_err); + DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(mkdir_err); + DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(delete_err); + DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(tdis_err); +-- +2.43.0 + diff --git a/queue-6.6/smb-client-parse-owner-group-when-creating-reparse-p.patch b/queue-6.6/smb-client-parse-owner-group-when-creating-reparse-p.patch new file mode 100644 index 0000000000..fc76fffffd --- /dev/null +++ b/queue-6.6/smb-client-parse-owner-group-when-creating-reparse-p.patch @@ -0,0 +1,414 @@ +From 78231c59be9f76c87cbad6b455b8fe90244e4c6d Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Wed, 1 May 2024 00:35:20 -0500 +Subject: smb: client: parse owner/group when creating reparse points + +From: Steve French <stfrench@microsoft.com> + +[ Upstream commit 858e74876c5cbff1dfd5bace99e32fbce2abd4b5 ] + +Parse owner/group when creating special files and symlinks under +SMB3.1.1 POSIX mounts. + +Move the parsing of owner/group to smb2_compound_op() so we don't have +to duplicate it in both smb2_get_reparse_inode() and +smb311_posix_query_path_info(). + +Signed-off-by: Paulo Alcantara <pc@manguebit.com> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/client/cifsglob.h | 2 + + fs/smb/client/inode.c | 25 +++----- + fs/smb/client/smb2inode.c | 130 ++++++++++++++++++-------------------- + fs/smb/client/smb2proto.h | 4 +- + 4 files changed, 71 insertions(+), 90 deletions(-) + +diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h +index 181e9d5b10f92..678a9c671cdcd 100644 +--- a/fs/smb/client/cifsglob.h ++++ b/fs/smb/client/cifsglob.h +@@ -209,6 +209,8 @@ struct cifs_open_info_data { + }; + } reparse; + char *symlink_target; ++ struct cifs_sid posix_owner; ++ struct cifs_sid posix_group; + union { + struct smb2_file_all_info fi; + struct smb311_posix_qinfo posix_fi; +diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c +index b8260ace2bee9..0110589acb853 100644 +--- a/fs/smb/client/inode.c ++++ b/fs/smb/client/inode.c +@@ -666,8 +666,6 @@ static int cifs_sfu_mode(struct cifs_fattr *fattr, const unsigned char *path, + /* Fill a cifs_fattr struct with info from POSIX info struct */ + static void smb311_posix_info_to_fattr(struct cifs_fattr *fattr, + struct cifs_open_info_data *data, +- struct cifs_sid *owner, +- struct cifs_sid *group, + struct super_block *sb) + { + struct smb311_posix_qinfo *info = &data->posix_fi; +@@ -723,8 +721,8 @@ static void smb311_posix_info_to_fattr(struct cifs_fattr *fattr, + fattr->cf_symlink_target = data->symlink_target; + data->symlink_target = NULL; + } +- sid_to_id(cifs_sb, owner, fattr, SIDOWNER); +- sid_to_id(cifs_sb, group, fattr, SIDGROUP); ++ sid_to_id(cifs_sb, &data->posix_owner, fattr, SIDOWNER); ++ sid_to_id(cifs_sb, &data->posix_group, fattr, SIDGROUP); + + cifs_dbg(FYI, "POSIX query info: mode 0x%x uniqueid 0x%llx nlink %d\n", + fattr->cf_mode, fattr->cf_uniqueid, fattr->cf_nlink); +@@ -1071,9 +1069,7 @@ static int reparse_info_to_fattr(struct cifs_open_info_data *data, + const unsigned int xid, + struct cifs_tcon *tcon, + const char *full_path, +- struct cifs_fattr *fattr, +- struct cifs_sid *owner, +- struct cifs_sid *group) ++ struct cifs_fattr *fattr) + { + struct TCP_Server_Info *server = tcon->ses->server; + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); +@@ -1118,7 +1114,7 @@ static int reparse_info_to_fattr(struct cifs_open_info_data *data, + } + + if (tcon->posix_extensions) +- smb311_posix_info_to_fattr(fattr, data, owner, group, sb); ++ smb311_posix_info_to_fattr(fattr, data, sb); + else + cifs_open_info_to_fattr(fattr, data, sb); + out: +@@ -1172,8 +1168,7 @@ static int cifs_get_fattr(struct cifs_open_info_data *data, + */ + if (cifs_open_data_reparse(data)) { + rc = reparse_info_to_fattr(data, sb, xid, tcon, +- full_path, fattr, +- NULL, NULL); ++ full_path, fattr); + } else { + cifs_open_info_to_fattr(fattr, data, sb); + } +@@ -1321,7 +1316,6 @@ static int smb311_posix_get_fattr(struct cifs_open_info_data *data, + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); + struct cifs_tcon *tcon; + struct tcon_link *tlink; +- struct cifs_sid owner, group; + int tmprc; + int rc = 0; + +@@ -1335,8 +1329,7 @@ static int smb311_posix_get_fattr(struct cifs_open_info_data *data, + */ + if (!data) { + rc = smb311_posix_query_path_info(xid, tcon, cifs_sb, +- full_path, &tmp_data, +- &owner, &group); ++ full_path, &tmp_data); + data = &tmp_data; + } + +@@ -1348,11 +1341,9 @@ static int smb311_posix_get_fattr(struct cifs_open_info_data *data, + case 0: + if (cifs_open_data_reparse(data)) { + rc = reparse_info_to_fattr(data, sb, xid, tcon, +- full_path, fattr, +- &owner, &group); ++ full_path, fattr); + } else { +- smb311_posix_info_to_fattr(fattr, data, +- &owner, &group, sb); ++ smb311_posix_info_to_fattr(fattr, data, sb); + } + break; + case -EREMOTE: +diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c +index 94df328a1965d..4cd4b8a63316d 100644 +--- a/fs/smb/client/smb2inode.c ++++ b/fs/smb/client/smb2inode.c +@@ -56,6 +56,35 @@ static struct reparse_data_buffer *reparse_buf_ptr(struct kvec *iov) + return buf; + } + ++/* Parse owner and group from SMB3.1.1 POSIX query info */ ++static int parse_posix_sids(struct cifs_open_info_data *data, ++ struct kvec *rsp_iov) ++{ ++ struct smb2_query_info_rsp *qi = rsp_iov->iov_base; ++ unsigned int out_len = le32_to_cpu(qi->OutputBufferLength); ++ unsigned int qi_len = sizeof(data->posix_fi); ++ int owner_len, group_len; ++ u8 *sidsbuf, *sidsbuf_end; ++ ++ if (out_len <= qi_len) ++ return -EINVAL; ++ ++ sidsbuf = (u8 *)qi + le16_to_cpu(qi->OutputBufferOffset) + qi_len; ++ sidsbuf_end = sidsbuf + out_len - qi_len; ++ ++ owner_len = posix_info_sid_size(sidsbuf, sidsbuf_end); ++ if (owner_len == -1) ++ return -EINVAL; ++ ++ memcpy(&data->posix_owner, sidsbuf, owner_len); ++ group_len = posix_info_sid_size(sidsbuf + owner_len, sidsbuf_end); ++ if (group_len == -1) ++ return -EINVAL; ++ ++ memcpy(&data->posix_group, sidsbuf + owner_len, group_len); ++ return 0; ++} ++ + /* + * note: If cfile is passed, the reference to it is dropped here. + * So make sure that you do not reuse cfile after return from this func. +@@ -69,7 +98,6 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, + __u32 desired_access, __u32 create_disposition, + __u32 create_options, umode_t mode, struct kvec *in_iov, + int *cmds, int num_cmds, struct cifsFileInfo *cfile, +- __u8 **extbuf, size_t *extbuflen, + struct kvec *out_iov, int *out_buftype) + { + +@@ -509,21 +537,9 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, + &rsp_iov[i + 1], sizeof(idata->posix_fi) /* add SIDs */, + (char *)&idata->posix_fi); + } +- if (rc == 0) { +- unsigned int length = le32_to_cpu(qi_rsp->OutputBufferLength); +- +- if (length > sizeof(idata->posix_fi)) { +- char *base = (char *)rsp_iov[i + 1].iov_base + +- le16_to_cpu(qi_rsp->OutputBufferOffset) + +- sizeof(idata->posix_fi); +- *extbuflen = length - sizeof(idata->posix_fi); +- *extbuf = kmemdup(base, *extbuflen, GFP_KERNEL); +- if (!*extbuf) +- rc = -ENOMEM; +- } else { +- rc = -EINVAL; +- } +- } ++ if (rc == 0) ++ rc = parse_posix_sids(idata, &rsp_iov[i + 1]); ++ + SMB2_query_info_free(&rqst[num_rqst++]); + if (rc) + trace_smb3_posix_query_info_compound_err(xid, ses->Suid, +@@ -714,9 +730,8 @@ int smb2_query_path_info(const unsigned int xid, + cifs_get_readable_path(tcon, full_path, &cfile); + rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, + FILE_READ_ATTRIBUTES, FILE_OPEN, +- create_options, ACL_NO_MODE, +- in_iov, cmds, 1, cfile, +- NULL, NULL, out_iov, out_buftype); ++ create_options, ACL_NO_MODE, in_iov, ++ cmds, 1, cfile, out_iov, out_buftype); + hdr = out_iov[0].iov_base; + /* + * If first iov is unset, then SMB session was dropped or we've got a +@@ -743,8 +758,8 @@ int smb2_query_path_info(const unsigned int xid, + cifs_get_readable_path(tcon, full_path, &cfile); + rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, + FILE_READ_ATTRIBUTES, FILE_OPEN, +- create_options, ACL_NO_MODE, in_iov, cmds, +- num_cmds, cfile, NULL, NULL, NULL, NULL); ++ create_options, ACL_NO_MODE, in_iov, ++ cmds, num_cmds, cfile, NULL, NULL); + break; + case -EREMOTE: + break; +@@ -771,19 +786,13 @@ int smb311_posix_query_path_info(const unsigned int xid, + struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, + const char *full_path, +- struct cifs_open_info_data *data, +- struct cifs_sid *owner, +- struct cifs_sid *group) ++ struct cifs_open_info_data *data) + { + int rc; + __u32 create_options = 0; + struct cifsFileInfo *cfile; + struct kvec in_iov[2], out_iov[3] = {}; + int out_buftype[3] = {}; +- __u8 *sidsbuf = NULL; +- __u8 *sidsbuf_end = NULL; +- size_t sidsbuflen = 0; +- size_t owner_len, group_len; + int cmds[2] = { SMB2_OP_POSIX_QUERY_INFO, }; + int i, num_cmds; + +@@ -803,8 +812,8 @@ int smb311_posix_query_path_info(const unsigned int xid, + cifs_get_readable_path(tcon, full_path, &cfile); + rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, + FILE_READ_ATTRIBUTES, FILE_OPEN, +- create_options, ACL_NO_MODE, in_iov, cmds, 1, +- cfile, &sidsbuf, &sidsbuflen, out_iov, out_buftype); ++ create_options, ACL_NO_MODE, in_iov, ++ cmds, 1, cfile, out_iov, out_buftype); + /* + * If first iov is unset, then SMB session was dropped or we've got a + * cached open file (@cfile). +@@ -831,32 +840,12 @@ int smb311_posix_query_path_info(const unsigned int xid, + cifs_get_readable_path(tcon, full_path, &cfile); + rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, + FILE_READ_ATTRIBUTES, FILE_OPEN, +- create_options, ACL_NO_MODE, in_iov, cmds, +- num_cmds, cfile, &sidsbuf, &sidsbuflen, NULL, NULL); ++ create_options, ACL_NO_MODE, in_iov, ++ cmds, num_cmds, cfile, NULL, NULL); + break; + } + + out: +- if (rc == 0) { +- sidsbuf_end = sidsbuf + sidsbuflen; +- +- owner_len = posix_info_sid_size(sidsbuf, sidsbuf_end); +- if (owner_len == -1) { +- rc = -EINVAL; +- goto out; +- } +- memcpy(owner, sidsbuf, owner_len); +- +- group_len = posix_info_sid_size( +- sidsbuf + owner_len, sidsbuf_end); +- if (group_len == -1) { +- rc = -EINVAL; +- goto out; +- } +- memcpy(group, sidsbuf + owner_len, group_len); +- } +- +- kfree(sidsbuf); + for (i = 0; i < ARRAY_SIZE(out_buftype); i++) + free_rsp_buf(out_buftype[i], out_iov[i].iov_base); + return rc; +@@ -869,9 +858,9 @@ smb2_mkdir(const unsigned int xid, struct inode *parent_inode, umode_t mode, + { + return smb2_compound_op(xid, tcon, cifs_sb, name, + FILE_WRITE_ATTRIBUTES, FILE_CREATE, +- CREATE_NOT_FILE, mode, NULL, +- &(int){SMB2_OP_MKDIR}, 1, +- NULL, NULL, NULL, NULL, NULL); ++ CREATE_NOT_FILE, mode, ++ NULL, &(int){SMB2_OP_MKDIR}, 1, ++ NULL, NULL, NULL); + } + + void +@@ -896,7 +885,7 @@ smb2_mkdir_setinfo(struct inode *inode, const char *name, + FILE_WRITE_ATTRIBUTES, FILE_CREATE, + CREATE_NOT_FILE, ACL_NO_MODE, &in_iov, + &(int){SMB2_OP_SET_INFO}, 1, +- cfile, NULL, NULL, NULL, NULL); ++ cfile, NULL, NULL); + if (tmprc == 0) + cifs_i->cifsAttrs = dosattrs; + } +@@ -908,8 +897,9 @@ smb2_rmdir(const unsigned int xid, struct cifs_tcon *tcon, const char *name, + drop_cached_dir_by_name(xid, tcon, name, cifs_sb); + return smb2_compound_op(xid, tcon, cifs_sb, name, + DELETE, FILE_OPEN, CREATE_NOT_FILE, +- ACL_NO_MODE, NULL, &(int){SMB2_OP_RMDIR}, 1, +- NULL, NULL, NULL, NULL, NULL); ++ ACL_NO_MODE, NULL, ++ &(int){SMB2_OP_RMDIR}, 1, ++ NULL, NULL, NULL); + } + + int +@@ -918,8 +908,9 @@ smb2_unlink(const unsigned int xid, struct cifs_tcon *tcon, const char *name, + { + return smb2_compound_op(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN, + CREATE_DELETE_ON_CLOSE | OPEN_REPARSE_POINT, +- ACL_NO_MODE, NULL, &(int){SMB2_OP_DELETE}, 1, +- NULL, NULL, NULL, NULL, NULL); ++ ACL_NO_MODE, NULL, ++ &(int){SMB2_OP_DELETE}, 1, ++ NULL, NULL, NULL); + } + + static int smb2_set_path_attr(const unsigned int xid, struct cifs_tcon *tcon, +@@ -939,10 +930,9 @@ static int smb2_set_path_attr(const unsigned int xid, struct cifs_tcon *tcon, + } + in_iov.iov_base = smb2_to_name; + in_iov.iov_len = 2 * UniStrnlen((wchar_t *)smb2_to_name, PATH_MAX); +- + rc = smb2_compound_op(xid, tcon, cifs_sb, from_name, access, +- FILE_OPEN, 0, ACL_NO_MODE, &in_iov, +- &command, 1, cfile, NULL, NULL, NULL, NULL); ++ FILE_OPEN, create_options, ACL_NO_MODE, ++ &in_iov, &command, 1, cfile, NULL, NULL); + smb2_rename_path: + kfree(smb2_to_name); + return rc; +@@ -993,7 +983,7 @@ smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon, + FILE_WRITE_DATA, FILE_OPEN, + 0, ACL_NO_MODE, &in_iov, + &(int){SMB2_OP_SET_EOF}, 1, +- cfile, NULL, NULL, NULL, NULL); ++ cfile, NULL, NULL); + } + + int +@@ -1021,8 +1011,8 @@ smb2_set_file_info(struct inode *inode, const char *full_path, + rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, + FILE_WRITE_ATTRIBUTES, FILE_OPEN, + 0, ACL_NO_MODE, &in_iov, +- &(int){SMB2_OP_SET_INFO}, 1, cfile, +- NULL, NULL, NULL, NULL); ++ &(int){SMB2_OP_SET_INFO}, 1, ++ cfile, NULL, NULL); + cifs_put_tlink(tlink); + return rc; + } +@@ -1057,7 +1047,7 @@ struct inode *smb2_get_reparse_inode(struct cifs_open_info_data *data, + cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile); + rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, + da, cd, co, ACL_NO_MODE, in_iov, +- cmds, 2, cfile, NULL, NULL, NULL, NULL); ++ cmds, 2, cfile, NULL, NULL); + if (!rc) { + rc = smb311_posix_get_inode_info(&new, full_path, + data, sb, xid); +@@ -1067,7 +1057,7 @@ struct inode *smb2_get_reparse_inode(struct cifs_open_info_data *data, + cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile); + rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, + da, cd, co, ACL_NO_MODE, in_iov, +- cmds, 2, cfile, NULL, NULL, NULL, NULL); ++ cmds, 2, cfile, NULL, NULL); + if (!rc) { + rc = cifs_get_inode_info(&new, full_path, + data, sb, xid, NULL); +@@ -1094,8 +1084,8 @@ int smb2_query_reparse_point(const unsigned int xid, + rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, + FILE_READ_ATTRIBUTES, FILE_OPEN, + OPEN_REPARSE_POINT, ACL_NO_MODE, &in_iov, +- &(int){SMB2_OP_GET_REPARSE}, 1, cfile, +- NULL, NULL, NULL, NULL); ++ &(int){SMB2_OP_GET_REPARSE}, 1, ++ cfile, NULL, NULL); + if (rc) + goto out; + +diff --git a/fs/smb/client/smb2proto.h b/fs/smb/client/smb2proto.h +index 330e36c6b91f0..b3069911e9dd8 100644 +--- a/fs/smb/client/smb2proto.h ++++ b/fs/smb/client/smb2proto.h +@@ -304,9 +304,7 @@ int smb311_posix_query_path_info(const unsigned int xid, + struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, + const char *full_path, +- struct cifs_open_info_data *data, +- struct cifs_sid *owner, +- struct cifs_sid *group); ++ struct cifs_open_info_data *data); + int posix_info_parse(const void *beg, const void *end, + struct smb2_posix_info_parsed *out); + int posix_info_sid_size(const void *beg, const void *end); +-- +2.43.0 + diff --git a/queue-6.6/smb-client-parse-uid-gid-mode-and-dev-from-wsl-repar.patch b/queue-6.6/smb-client-parse-uid-gid-mode-and-dev-from-wsl-repar.patch new file mode 100644 index 0000000000..0fd2c1efbd --- /dev/null +++ b/queue-6.6/smb-client-parse-uid-gid-mode-and-dev-from-wsl-repar.patch @@ -0,0 +1,223 @@ +From a9ced8bf642069091702ea5e9635943c2b2aed29 Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Sun, 28 Jan 2024 21:52:03 -0300 +Subject: smb: client: parse uid, gid, mode and dev from WSL reparse points + +From: Paulo Alcantara <pc@manguebit.com> + +[ Upstream commit 78e26bec4d6d3aef04276e28bed48a45fd00e116 ] + +Parse the extended attributes from WSL reparse points to correctly +report uid, gid mode and dev from ther instantiated inodes. + +Signed-off-by: Paulo Alcantara <pc@manguebit.com> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/client/inode.c | 5 ++- + fs/smb/client/readdir.c | 2 ++ + fs/smb/client/reparse.c | 78 +++++++++++++++++++++++++++++++++-------- + fs/smb/client/reparse.h | 29 +++++++++++++++ + 4 files changed, 97 insertions(+), 17 deletions(-) + +diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c +index 8aff8382cfb54..67bc1a1e54fde 100644 +--- a/fs/smb/client/inode.c ++++ b/fs/smb/client/inode.c +@@ -759,6 +759,8 @@ static void cifs_open_info_to_fattr(struct cifs_fattr *fattr, + fattr->cf_bytes = le64_to_cpu(info->AllocationSize); + fattr->cf_createtime = le64_to_cpu(info->CreationTime); + fattr->cf_nlink = le32_to_cpu(info->NumberOfLinks); ++ fattr->cf_uid = cifs_sb->ctx->linux_uid; ++ fattr->cf_gid = cifs_sb->ctx->linux_gid; + + fattr->cf_mode = cifs_sb->ctx->file_mode; + if (cifs_open_data_reparse(data) && +@@ -801,9 +803,6 @@ static void cifs_open_info_to_fattr(struct cifs_fattr *fattr, + fattr->cf_symlink_target = data->symlink_target; + data->symlink_target = NULL; + } +- +- fattr->cf_uid = cifs_sb->ctx->linux_uid; +- fattr->cf_gid = cifs_sb->ctx->linux_gid; + } + + static int +diff --git a/fs/smb/client/readdir.c b/fs/smb/client/readdir.c +index 3e5d22b356e92..06111d9f39500 100644 +--- a/fs/smb/client/readdir.c ++++ b/fs/smb/client/readdir.c +@@ -125,6 +125,8 @@ cifs_prime_dcache(struct dentry *parent, struct qstr *name, + if (likely(reparse_inode_match(inode, fattr))) { + fattr->cf_mode = inode->i_mode; + fattr->cf_rdev = inode->i_rdev; ++ fattr->cf_uid = inode->i_uid; ++ fattr->cf_gid = inode->i_gid; + fattr->cf_eof = CIFS_I(inode)->server_eof; + fattr->cf_symlink_target = NULL; + } else { +diff --git a/fs/smb/client/reparse.c b/fs/smb/client/reparse.c +index e8be756e6768c..29a47f20643b1 100644 +--- a/fs/smb/client/reparse.c ++++ b/fs/smb/client/reparse.c +@@ -258,7 +258,9 @@ static int mknod_wsl(unsigned int xid, struct inode *inode, + { + struct cifs_open_info_data data; + struct reparse_data_buffer buf; ++ struct smb2_create_ea_ctx *cc; + struct inode *new; ++ unsigned int len; + struct kvec reparse_iov, xattr_iov; + int rc; + +@@ -275,6 +277,11 @@ static int mknod_wsl(unsigned int xid, struct inode *inode, + .reparse = { .tag = le32_to_cpu(buf.ReparseTag), .buf = &buf, }, + }; + ++ cc = xattr_iov.iov_base; ++ len = le32_to_cpu(cc->ctx.DataLength); ++ memcpy(data.wsl.eas, &cc->ea, len); ++ data.wsl.eas_len = len; ++ + new = smb2_get_reparse_inode(&data, inode->i_sb, + xid, tcon, full_path, + &reparse_iov, &xattr_iov); +@@ -408,6 +415,62 @@ int smb2_parse_reparse_point(struct cifs_sb_info *cifs_sb, + return parse_reparse_point(buf, plen, cifs_sb, true, data); + } + ++static void wsl_to_fattr(struct cifs_open_info_data *data, ++ struct cifs_sb_info *cifs_sb, ++ u32 tag, struct cifs_fattr *fattr) ++{ ++ struct smb2_file_full_ea_info *ea; ++ u32 next = 0; ++ ++ switch (tag) { ++ case IO_REPARSE_TAG_LX_SYMLINK: ++ fattr->cf_mode |= S_IFLNK; ++ break; ++ case IO_REPARSE_TAG_LX_FIFO: ++ fattr->cf_mode |= S_IFIFO; ++ break; ++ case IO_REPARSE_TAG_AF_UNIX: ++ fattr->cf_mode |= S_IFSOCK; ++ break; ++ case IO_REPARSE_TAG_LX_CHR: ++ fattr->cf_mode |= S_IFCHR; ++ break; ++ case IO_REPARSE_TAG_LX_BLK: ++ fattr->cf_mode |= S_IFBLK; ++ break; ++ } ++ ++ if (!data->wsl.eas_len) ++ goto out; ++ ++ ea = (struct smb2_file_full_ea_info *)data->wsl.eas; ++ do { ++ const char *name; ++ void *v; ++ u8 nlen; ++ ++ ea = (void *)((u8 *)ea + next); ++ next = le32_to_cpu(ea->next_entry_offset); ++ if (!le16_to_cpu(ea->ea_value_length)) ++ continue; ++ ++ name = ea->ea_data; ++ nlen = ea->ea_name_length; ++ v = (void *)((u8 *)ea->ea_data + ea->ea_name_length + 1); ++ ++ if (!strncmp(name, SMB2_WSL_XATTR_UID, nlen)) ++ fattr->cf_uid = wsl_make_kuid(cifs_sb, v); ++ else if (!strncmp(name, SMB2_WSL_XATTR_GID, nlen)) ++ fattr->cf_gid = wsl_make_kgid(cifs_sb, v); ++ else if (!strncmp(name, SMB2_WSL_XATTR_MODE, nlen)) ++ fattr->cf_mode = (umode_t)le32_to_cpu(*(__le32 *)v); ++ else if (!strncmp(name, SMB2_WSL_XATTR_DEV, nlen)) ++ fattr->cf_rdev = wsl_mkdev(v); ++ } while (next); ++out: ++ fattr->cf_dtype = S_DT(fattr->cf_mode); ++} ++ + bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb, + struct cifs_fattr *fattr, + struct cifs_open_info_data *data) +@@ -448,24 +511,11 @@ bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb, + + switch (tag) { + case IO_REPARSE_TAG_LX_SYMLINK: +- fattr->cf_mode |= S_IFLNK; +- fattr->cf_dtype = DT_LNK; +- break; + case IO_REPARSE_TAG_LX_FIFO: +- fattr->cf_mode |= S_IFIFO; +- fattr->cf_dtype = DT_FIFO; +- break; + case IO_REPARSE_TAG_AF_UNIX: +- fattr->cf_mode |= S_IFSOCK; +- fattr->cf_dtype = DT_SOCK; +- break; + case IO_REPARSE_TAG_LX_CHR: +- fattr->cf_mode |= S_IFCHR; +- fattr->cf_dtype = DT_CHR; +- break; + case IO_REPARSE_TAG_LX_BLK: +- fattr->cf_mode |= S_IFBLK; +- fattr->cf_dtype = DT_BLK; ++ wsl_to_fattr(data, cifs_sb, tag, fattr); + break; + case 0: /* SMB1 symlink */ + case IO_REPARSE_TAG_SYMLINK: +diff --git a/fs/smb/client/reparse.h b/fs/smb/client/reparse.h +index 9816bac985525..6b55d1df9e2f8 100644 +--- a/fs/smb/client/reparse.h ++++ b/fs/smb/client/reparse.h +@@ -8,6 +8,8 @@ + + #include <linux/fs.h> + #include <linux/stat.h> ++#include <linux/uidgid.h> ++#include "fs_context.h" + #include "cifsglob.h" + + static inline dev_t reparse_nfs_mkdev(struct reparse_posix_data *buf) +@@ -17,6 +19,33 @@ static inline dev_t reparse_nfs_mkdev(struct reparse_posix_data *buf) + return MKDEV(v >> 32, v & 0xffffffff); + } + ++static inline dev_t wsl_mkdev(void *ptr) ++{ ++ u64 v = le64_to_cpu(*(__le64 *)ptr); ++ ++ return MKDEV(v & 0xffffffff, v >> 32); ++} ++ ++static inline kuid_t wsl_make_kuid(struct cifs_sb_info *cifs_sb, ++ void *ptr) ++{ ++ u32 uid = le32_to_cpu(*(__le32 *)ptr); ++ ++ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID) ++ return cifs_sb->ctx->linux_uid; ++ return make_kuid(current_user_ns(), uid); ++} ++ ++static inline kgid_t wsl_make_kgid(struct cifs_sb_info *cifs_sb, ++ void *ptr) ++{ ++ u32 gid = le32_to_cpu(*(__le32 *)ptr); ++ ++ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID) ++ return cifs_sb->ctx->linux_gid; ++ return make_kgid(current_user_ns(), gid); ++} ++ + static inline u64 reparse_mode_nfs_type(mode_t mode) + { + switch (mode & S_IFMT) { +-- +2.43.0 + diff --git a/queue-6.6/smb-client-reduce-number-of-parameters-in-smb2_compo.patch b/queue-6.6/smb-client-reduce-number-of-parameters-in-smb2_compo.patch new file mode 100644 index 0000000000..ee7afd4014 --- /dev/null +++ b/queue-6.6/smb-client-reduce-number-of-parameters-in-smb2_compo.patch @@ -0,0 +1,367 @@ +From 27fa69b649b2242dc35955ca5f99618344c4203a Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Thu, 25 Jan 2024 19:21:48 -0300 +Subject: smb: client: reduce number of parameters in smb2_compound_op() + +From: Paulo Alcantara <pc@manguebit.com> + +[ Upstream commit fa792d8d235c20df5f422e4bd172db1efde55ab9 ] + +Replace @desired_access, @create_disposition, @create_options and +@mode parameters with a single @oparms. + +No functional changes. + +Signed-off-by: Paulo Alcantara <pc@manguebit.com> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/client/cifsglob.h | 11 +++ + fs/smb/client/smb2inode.c | 153 +++++++++++++++++++++----------------- + 2 files changed, 95 insertions(+), 69 deletions(-) + +diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h +index 296ed556be0e2..5a902fb20ac96 100644 +--- a/fs/smb/client/cifsglob.h ++++ b/fs/smb/client/cifsglob.h +@@ -2281,6 +2281,17 @@ static inline void cifs_sg_set_buf(struct sg_table *sgtable, + } + } + ++#define CIFS_OPARMS(_cifs_sb, _tcon, _path, _da, _cd, _co, _mode) \ ++ ((struct cifs_open_parms) { \ ++ .tcon = _tcon, \ ++ .path = _path, \ ++ .desired_access = (_da), \ ++ .disposition = (_cd), \ ++ .create_options = cifs_create_options(_cifs_sb, (_co)), \ ++ .mode = (_mode), \ ++ .cifs_sb = _cifs_sb, \ ++ }) ++ + struct smb2_compound_vars { + struct cifs_open_parms oparms; + struct kvec rsp_iov[MAX_COMPOUND]; +diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c +index 33f3fffcb8277..e1ad54b27b63e 100644 +--- a/fs/smb/client/smb2inode.c ++++ b/fs/smb/client/smb2inode.c +@@ -95,8 +95,7 @@ static int parse_posix_sids(struct cifs_open_info_data *data, + */ + static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, const char *full_path, +- __u32 desired_access, __u32 create_disposition, +- __u32 create_options, umode_t mode, struct kvec *in_iov, ++ struct cifs_open_parms *oparms, struct kvec *in_iov, + int *cmds, int num_cmds, struct cifsFileInfo *cfile, + struct kvec *out_iov, int *out_buftype, struct dentry *dentry) + { +@@ -173,16 +172,8 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, + } + } + +- vars->oparms = (struct cifs_open_parms) { +- .tcon = tcon, +- .path = full_path, +- .desired_access = desired_access, +- .disposition = create_disposition, +- .create_options = cifs_create_options(cifs_sb, create_options), +- .fid = &fid, +- .mode = mode, +- .cifs_sb = cifs_sb, +- }; ++ vars->oparms = *oparms; ++ vars->oparms.fid = &fid; + + rqst[num_rqst].rq_iov = &vars->open_iov[0]; + rqst[num_rqst].rq_nvec = SMB2_CREATE_IOV_SIZE; +@@ -741,6 +732,7 @@ int smb2_query_path_info(const unsigned int xid, + const char *full_path, + struct cifs_open_info_data *data) + { ++ struct cifs_open_parms oparms; + __u32 create_options = 0; + struct cifsFileInfo *cfile; + struct cached_fid *cfid = NULL; +@@ -792,10 +784,11 @@ int smb2_query_path_info(const unsigned int xid, + in_iov[1] = in_iov[0]; + + cifs_get_readable_path(tcon, full_path, &cfile); ++ oparms = CIFS_OPARMS(cifs_sb, tcon, full_path, FILE_READ_ATTRIBUTES, ++ FILE_OPEN, create_options, ACL_NO_MODE); + rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, +- FILE_READ_ATTRIBUTES, FILE_OPEN, +- create_options, ACL_NO_MODE, in_iov, +- cmds, 1, cfile, out_iov, out_buftype, NULL); ++ &oparms, in_iov, cmds, 1, cfile, ++ out_iov, out_buftype, NULL); + hdr = out_iov[0].iov_base; + /* + * If first iov is unset, then SMB session was dropped or we've got a +@@ -822,12 +815,14 @@ int smb2_query_path_info(const unsigned int xid, + cmds[1] = SMB2_OP_GET_REPARSE; + num_cmds = 2; + } +- create_options |= OPEN_REPARSE_POINT; ++ oparms = CIFS_OPARMS(cifs_sb, tcon, full_path, ++ FILE_READ_ATTRIBUTES, FILE_OPEN, ++ create_options | OPEN_REPARSE_POINT, ++ ACL_NO_MODE); + cifs_get_readable_path(tcon, full_path, &cfile); + rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, +- FILE_READ_ATTRIBUTES, FILE_OPEN, +- create_options, ACL_NO_MODE, in_iov, +- cmds, num_cmds, cfile, NULL, NULL, NULL); ++ &oparms, in_iov, cmds, num_cmds, ++ cfile, NULL, NULL, NULL); + break; + case -EREMOTE: + break; +@@ -855,10 +850,13 @@ smb2_mkdir(const unsigned int xid, struct inode *parent_inode, umode_t mode, + struct cifs_tcon *tcon, const char *name, + struct cifs_sb_info *cifs_sb) + { +- return smb2_compound_op(xid, tcon, cifs_sb, name, +- FILE_WRITE_ATTRIBUTES, FILE_CREATE, +- CREATE_NOT_FILE, mode, +- NULL, &(int){SMB2_OP_MKDIR}, 1, ++ struct cifs_open_parms oparms; ++ ++ oparms = CIFS_OPARMS(cifs_sb, tcon, name, FILE_WRITE_ATTRIBUTES, ++ FILE_CREATE, CREATE_NOT_FILE, mode); ++ return smb2_compound_op(xid, tcon, cifs_sb, ++ name, &oparms, NULL, ++ &(int){SMB2_OP_MKDIR}, 1, + NULL, NULL, NULL, NULL); + } + +@@ -867,6 +865,7 @@ smb2_mkdir_setinfo(struct inode *inode, const char *name, + struct cifs_sb_info *cifs_sb, struct cifs_tcon *tcon, + const unsigned int xid) + { ++ struct cifs_open_parms oparms; + FILE_BASIC_INFO data = {}; + struct cifsInodeInfo *cifs_i; + struct cifsFileInfo *cfile; +@@ -880,9 +879,10 @@ smb2_mkdir_setinfo(struct inode *inode, const char *name, + dosattrs = cifs_i->cifsAttrs | ATTR_READONLY; + data.Attributes = cpu_to_le32(dosattrs); + cifs_get_writable_path(tcon, name, FIND_WR_ANY, &cfile); ++ oparms = CIFS_OPARMS(cifs_sb, tcon, name, FILE_WRITE_ATTRIBUTES, ++ FILE_CREATE, CREATE_NOT_FILE, ACL_NO_MODE); + tmprc = smb2_compound_op(xid, tcon, cifs_sb, name, +- FILE_WRITE_ATTRIBUTES, FILE_CREATE, +- CREATE_NOT_FILE, ACL_NO_MODE, &in_iov, ++ &oparms, &in_iov, + &(int){SMB2_OP_SET_INFO}, 1, + cfile, NULL, NULL, NULL); + if (tmprc == 0) +@@ -893,10 +893,13 @@ int + smb2_rmdir(const unsigned int xid, struct cifs_tcon *tcon, const char *name, + struct cifs_sb_info *cifs_sb) + { ++ struct cifs_open_parms oparms; ++ + drop_cached_dir_by_name(xid, tcon, name, cifs_sb); +- return smb2_compound_op(xid, tcon, cifs_sb, name, +- DELETE, FILE_OPEN, CREATE_NOT_FILE, +- ACL_NO_MODE, NULL, ++ oparms = CIFS_OPARMS(cifs_sb, tcon, name, DELETE, ++ FILE_OPEN, CREATE_NOT_FILE, ACL_NO_MODE); ++ return smb2_compound_op(xid, tcon, cifs_sb, ++ name, &oparms, NULL, + &(int){SMB2_OP_RMDIR}, 1, + NULL, NULL, NULL, NULL); + } +@@ -905,18 +908,20 @@ int + smb2_unlink(const unsigned int xid, struct cifs_tcon *tcon, const char *name, + struct cifs_sb_info *cifs_sb, struct dentry *dentry) + { +- int rc = smb2_compound_op(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN, +- CREATE_DELETE_ON_CLOSE | OPEN_REPARSE_POINT, +- ACL_NO_MODE, NULL, +- &(int){SMB2_OP_DELETE}, 1, +- NULL, NULL, NULL, dentry); ++ struct cifs_open_parms oparms; ++ ++ oparms = CIFS_OPARMS(cifs_sb, tcon, name, ++ DELETE, FILE_OPEN, ++ CREATE_DELETE_ON_CLOSE | OPEN_REPARSE_POINT, ++ ACL_NO_MODE); ++ int rc = smb2_compound_op(xid, tcon, cifs_sb, name, &oparms, ++ NULL, &(int){SMB2_OP_DELETE}, 1, ++ NULL, NULL, NULL, dentry); + if (rc == -EINVAL) { + cifs_dbg(FYI, "invalid lease key, resending request without lease"); +- rc = smb2_compound_op(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN, +- CREATE_DELETE_ON_CLOSE | OPEN_REPARSE_POINT, +- ACL_NO_MODE, NULL, +- &(int){SMB2_OP_DELETE}, 1, +- NULL, NULL, NULL, NULL); ++ rc = smb2_compound_op(xid, tcon, cifs_sb, name, &oparms, ++ NULL, &(int){SMB2_OP_DELETE}, 1, ++ NULL, NULL, NULL, NULL); + } + return rc; + } +@@ -928,6 +933,7 @@ static int smb2_set_path_attr(const unsigned int xid, struct cifs_tcon *tcon, + int command, struct cifsFileInfo *cfile, + struct dentry *dentry) + { ++ struct cifs_open_parms oparms; + struct kvec in_iov; + __le16 *smb2_to_name = NULL; + int rc; +@@ -939,9 +945,11 @@ static int smb2_set_path_attr(const unsigned int xid, struct cifs_tcon *tcon, + } + in_iov.iov_base = smb2_to_name; + in_iov.iov_len = 2 * UniStrnlen((wchar_t *)smb2_to_name, PATH_MAX); +- rc = smb2_compound_op(xid, tcon, cifs_sb, from_name, access, +- FILE_OPEN, create_options, ACL_NO_MODE, +- &in_iov, &command, 1, cfile, NULL, NULL, dentry); ++ oparms = CIFS_OPARMS(cifs_sb, tcon, from_name, access, FILE_OPEN, ++ create_options, ACL_NO_MODE); ++ rc = smb2_compound_op(xid, tcon, cifs_sb, from_name, ++ &oparms, &in_iov, &command, 1, ++ cfile, NULL, NULL, dentry); + smb2_rename_path: + kfree(smb2_to_name); + return rc; +@@ -988,25 +996,28 @@ smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, bool set_alloc, + struct dentry *dentry) + { ++ struct cifs_open_parms oparms; + struct cifsFileInfo *cfile; + struct kvec in_iov; + __le64 eof = cpu_to_le64(size); ++ int rc; + + in_iov.iov_base = &eof; + in_iov.iov_len = sizeof(eof); + cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile); +- int rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, +- FILE_WRITE_DATA, FILE_OPEN, +- 0, ACL_NO_MODE, &in_iov, +- &(int){SMB2_OP_SET_EOF}, 1, +- cfile, NULL, NULL, dentry); ++ ++ oparms = CIFS_OPARMS(cifs_sb, tcon, full_path, FILE_WRITE_DATA, ++ FILE_OPEN, 0, ACL_NO_MODE); ++ rc = smb2_compound_op(xid, tcon, cifs_sb, ++ full_path, &oparms, &in_iov, ++ &(int){SMB2_OP_SET_EOF}, 1, ++ cfile, NULL, NULL, dentry); + if (rc == -EINVAL) { + cifs_dbg(FYI, "invalid lease key, resending request without lease"); +- rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, +- FILE_WRITE_DATA, FILE_OPEN, +- 0, ACL_NO_MODE, &in_iov, +- &(int){SMB2_OP_SET_EOF}, 1, +- cfile, NULL, NULL, NULL); ++ rc = smb2_compound_op(xid, tcon, cifs_sb, ++ full_path, &oparms, &in_iov, ++ &(int){SMB2_OP_SET_EOF}, 1, ++ cfile, NULL, NULL, NULL); + } + return rc; + } +@@ -1015,6 +1026,7 @@ int + smb2_set_file_info(struct inode *inode, const char *full_path, + FILE_BASIC_INFO *buf, const unsigned int xid) + { ++ struct cifs_open_parms oparms; + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct tcon_link *tlink; + struct cifs_tcon *tcon; +@@ -1033,9 +1045,10 @@ smb2_set_file_info(struct inode *inode, const char *full_path, + tcon = tlink_tcon(tlink); + + cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile); +- rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, +- FILE_WRITE_ATTRIBUTES, FILE_OPEN, +- 0, ACL_NO_MODE, &in_iov, ++ oparms = CIFS_OPARMS(cifs_sb, tcon, full_path, FILE_WRITE_ATTRIBUTES, ++ FILE_OPEN, 0, ACL_NO_MODE); ++ rc = smb2_compound_op(xid, tcon, cifs_sb, ++ full_path, &oparms, &in_iov, + &(int){SMB2_OP_SET_INFO}, 1, + cfile, NULL, NULL, NULL); + cifs_put_tlink(tlink); +@@ -1049,19 +1062,21 @@ struct inode *smb2_get_reparse_inode(struct cifs_open_info_data *data, + const char *full_path, + struct kvec *iov) + { ++ struct cifs_open_parms oparms; + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); + struct cifsFileInfo *cfile; + struct inode *new = NULL; + struct kvec in_iov[2]; + int cmds[2]; +- int da, co, cd; + int rc; + +- da = SYNCHRONIZE | DELETE | +- FILE_READ_ATTRIBUTES | +- FILE_WRITE_ATTRIBUTES; +- co = CREATE_NOT_DIR | OPEN_REPARSE_POINT; +- cd = FILE_CREATE; ++ oparms = CIFS_OPARMS(cifs_sb, tcon, full_path, ++ SYNCHRONIZE | DELETE | ++ FILE_READ_ATTRIBUTES | ++ FILE_WRITE_ATTRIBUTES, ++ FILE_CREATE, ++ CREATE_NOT_DIR | OPEN_REPARSE_POINT, ++ ACL_NO_MODE); + cmds[0] = SMB2_OP_SET_REPARSE; + in_iov[0] = *iov; + in_iov[1].iov_base = data; +@@ -1070,9 +1085,8 @@ struct inode *smb2_get_reparse_inode(struct cifs_open_info_data *data, + if (tcon->posix_extensions) { + cmds[1] = SMB2_OP_POSIX_QUERY_INFO; + cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile); +- rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, +- da, cd, co, ACL_NO_MODE, in_iov, +- cmds, 2, cfile, NULL, NULL, NULL); ++ rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, &oparms, ++ in_iov, cmds, 2, cfile, NULL, NULL, NULL); + if (!rc) { + rc = smb311_posix_get_inode_info(&new, full_path, + data, sb, xid); +@@ -1080,9 +1094,8 @@ struct inode *smb2_get_reparse_inode(struct cifs_open_info_data *data, + } else { + cmds[1] = SMB2_OP_QUERY_INFO; + cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile); +- rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, +- da, cd, co, ACL_NO_MODE, in_iov, +- cmds, 2, cfile, NULL, NULL, NULL); ++ rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, &oparms, ++ in_iov, cmds, 2, cfile, NULL, NULL, NULL); + if (!rc) { + rc = cifs_get_inode_info(&new, full_path, + data, sb, xid, NULL); +@@ -1098,6 +1111,7 @@ int smb2_query_reparse_point(const unsigned int xid, + u32 *tag, struct kvec *rsp, + int *rsp_buftype) + { ++ struct cifs_open_parms oparms; + struct cifs_open_info_data data = {}; + struct cifsFileInfo *cfile; + struct kvec in_iov = { .iov_base = &data, .iov_len = sizeof(data), }; +@@ -1106,9 +1120,10 @@ int smb2_query_reparse_point(const unsigned int xid, + cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path); + + cifs_get_readable_path(tcon, full_path, &cfile); +- rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, +- FILE_READ_ATTRIBUTES, FILE_OPEN, +- OPEN_REPARSE_POINT, ACL_NO_MODE, &in_iov, ++ oparms = CIFS_OPARMS(cifs_sb, tcon, full_path, FILE_READ_ATTRIBUTES, ++ FILE_OPEN, OPEN_REPARSE_POINT, ACL_NO_MODE); ++ rc = smb2_compound_op(xid, tcon, cifs_sb, ++ full_path, &oparms, &in_iov, + &(int){SMB2_OP_GET_REPARSE}, 1, + cfile, NULL, NULL, NULL); + if (rc) +-- +2.43.0 + diff --git a/queue-6.6/smb-client-retry-compound-request-without-reusing-le.patch b/queue-6.6/smb-client-retry-compound-request-without-reusing-le.patch new file mode 100644 index 0000000000..e0bb0218dc --- /dev/null +++ b/queue-6.6/smb-client-retry-compound-request-without-reusing-le.patch @@ -0,0 +1,117 @@ +From f7b27bf257108d91df968ef5c156133a27fee23a Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Tue, 5 Mar 2024 22:43:53 -0500 +Subject: smb: client: retry compound request without reusing lease + +From: Meetakshi Setiya <msetiya@microsoft.com> + +[ Upstream commit 71f15c90e785d1de4bcd65a279e7256684c25c0d ] + +There is a shortcoming in the current implementation of the file +lease mechanism exposed when the lease keys were attempted to be +reused for unlink, rename and set_path_size operations for a client. As +per MS-SMB2, lease keys are associated with the file name. Linux smb +client maintains lease keys with the inode. If the file has any hardlinks, +it is possible that the lease for a file be wrongly reused for an +operation on the hardlink or vice versa. In these cases, the mentioned +compound operations fail with STATUS_INVALID_PARAMETER. +This patch adds a fallback to the old mechanism of not sending any +lease with these compound operations if the request with lease key fails +with STATUS_INVALID_PARAMETER. +Resending the same request without lease key should not hurt any +functionality, but might impact performance especially in cases where +the error is not because of the usage of wrong lease key and we might +end up doing an extra roundtrip. + +Signed-off-by: Meetakshi Setiya <msetiya@microsoft.com> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/client/smb2inode.c | 41 ++++++++++++++++++++++++++++++++++++--- + 1 file changed, 38 insertions(+), 3 deletions(-) + +diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c +index 0b7b083352919..add90eb8fc165 100644 +--- a/fs/smb/client/smb2inode.c ++++ b/fs/smb/client/smb2inode.c +@@ -154,6 +154,17 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, + } + + /* if there is an existing lease, reuse it */ ++ ++ /* ++ * note: files with hardlinks cause unexpected behaviour. As per MS-SMB2, ++ * lease keys are associated with the filepath. We are maintaining lease keys ++ * with the inode on the client. If the file has hardlinks, it is possible ++ * that the lease for a file be reused for an operation on its hardlink or ++ * vice versa. ++ * As a workaround, send request using an existing lease key and if the server ++ * returns STATUS_INVALID_PARAMETER, which maps to EINVAL, send the request ++ * again without the lease. ++ */ + if (dentry) { + inode = d_inode(dentry); + if (CIFS_I(inode)->lease_granted && server->ops->get_lease_key) { +@@ -874,11 +885,20 @@ int + smb2_unlink(const unsigned int xid, struct cifs_tcon *tcon, const char *name, + struct cifs_sb_info *cifs_sb, struct dentry *dentry) + { +- return smb2_compound_op(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN, ++ int rc = smb2_compound_op(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN, + CREATE_DELETE_ON_CLOSE | OPEN_REPARSE_POINT, + ACL_NO_MODE, NULL, + &(int){SMB2_OP_DELETE}, 1, + NULL, NULL, NULL, dentry); ++ if (rc == -EINVAL) { ++ cifs_dbg(FYI, "invalid lease key, resending request without lease"); ++ rc = smb2_compound_op(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN, ++ CREATE_DELETE_ON_CLOSE | OPEN_REPARSE_POINT, ++ ACL_NO_MODE, NULL, ++ &(int){SMB2_OP_DELETE}, 1, ++ NULL, NULL, NULL, NULL); ++ } ++ return rc; + } + + static int smb2_set_path_attr(const unsigned int xid, struct cifs_tcon *tcon, +@@ -919,8 +939,14 @@ int smb2_rename_path(const unsigned int xid, + drop_cached_dir_by_name(xid, tcon, from_name, cifs_sb); + cifs_get_writable_path(tcon, from_name, FIND_WR_WITH_DELETE, &cfile); + +- return smb2_set_path_attr(xid, tcon, from_name, to_name, cifs_sb, ++ int rc = smb2_set_path_attr(xid, tcon, from_name, to_name, cifs_sb, + co, DELETE, SMB2_OP_RENAME, cfile, source_dentry); ++ if (rc == -EINVAL) { ++ cifs_dbg(FYI, "invalid lease key, resending request without lease"); ++ rc = smb2_set_path_attr(xid, tcon, from_name, to_name, cifs_sb, ++ co, DELETE, SMB2_OP_RENAME, cfile, NULL); ++ } ++ return rc; + } + + int smb2_create_hardlink(const unsigned int xid, +@@ -949,11 +975,20 @@ smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon, + in_iov.iov_base = &eof; + in_iov.iov_len = sizeof(eof); + cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile); +- return smb2_compound_op(xid, tcon, cifs_sb, full_path, ++ int rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, + FILE_WRITE_DATA, FILE_OPEN, + 0, ACL_NO_MODE, &in_iov, + &(int){SMB2_OP_SET_EOF}, 1, + cfile, NULL, NULL, dentry); ++ if (rc == -EINVAL) { ++ cifs_dbg(FYI, "invalid lease key, resending request without lease"); ++ rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, ++ FILE_WRITE_DATA, FILE_OPEN, ++ 0, ACL_NO_MODE, &in_iov, ++ &(int){SMB2_OP_SET_EOF}, 1, ++ cfile, NULL, NULL, NULL); ++ } ++ return rc; + } + + int +-- +2.43.0 + diff --git a/queue-6.6/smb-client-return-reparse-type-in-proc-mounts.patch b/queue-6.6/smb-client-return-reparse-type-in-proc-mounts.patch new file mode 100644 index 0000000000..aeeff34161 --- /dev/null +++ b/queue-6.6/smb-client-return-reparse-type-in-proc-mounts.patch @@ -0,0 +1,60 @@ +From 5fffe33797da8f6dc1451b0ff8e1a4dee2174d31 Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Sat, 24 Feb 2024 16:57:14 -0300 +Subject: smb: client: return reparse type in /proc/mounts + +From: Paulo Alcantara <pc@manguebit.com> + +[ Upstream commit 1e5f4240714bb238d2d17c7e14e5fb45c9140665 ] + +Add support for returning reparse mount option in /proc/mounts. + +Reported-by: kernel test robot <lkp@intel.com> +Closes: https://lore.kernel.org/oe-kbuild-all/202402262152.YZOwDlCM-lkp@intel.com/ +Signed-off-by: Paulo Alcantara <pc@manguebit.com> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/client/cifsfs.c | 2 ++ + fs/smb/client/cifsglob.h | 12 ++++++++++++ + 2 files changed, 14 insertions(+) + +diff --git a/fs/smb/client/cifsfs.c b/fs/smb/client/cifsfs.c +index 6d9d2174ee691..30bf754c9fc93 100644 +--- a/fs/smb/client/cifsfs.c ++++ b/fs/smb/client/cifsfs.c +@@ -674,6 +674,8 @@ cifs_show_options(struct seq_file *s, struct dentry *root) + seq_printf(s, ",backupgid=%u", + from_kgid_munged(&init_user_ns, + cifs_sb->ctx->backupgid)); ++ seq_show_option(s, "reparse", ++ cifs_reparse_type_str(cifs_sb->ctx->reparse_type)); + + seq_printf(s, ",rsize=%u", cifs_sb->ctx->rsize); + seq_printf(s, ",wsize=%u", cifs_sb->ctx->wsize); +diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h +index ddb64af50a45d..053556ca6f011 100644 +--- a/fs/smb/client/cifsglob.h ++++ b/fs/smb/client/cifsglob.h +@@ -159,6 +159,18 @@ enum cifs_reparse_type { + CIFS_REPARSE_TYPE_DEFAULT = CIFS_REPARSE_TYPE_NFS, + }; + ++static inline const char *cifs_reparse_type_str(enum cifs_reparse_type type) ++{ ++ switch (type) { ++ case CIFS_REPARSE_TYPE_NFS: ++ return "nfs"; ++ case CIFS_REPARSE_TYPE_WSL: ++ return "wsl"; ++ default: ++ return "unknown"; ++ } ++} ++ + struct session_key { + unsigned int len; + char *response; +-- +2.43.0 + diff --git a/queue-6.6/smb-client-reuse-file-lease-key-in-compound-operatio.patch b/queue-6.6/smb-client-reuse-file-lease-key-in-compound-operatio.patch new file mode 100644 index 0000000000..3afaa1bd87 --- /dev/null +++ b/queue-6.6/smb-client-reuse-file-lease-key-in-compound-operatio.patch @@ -0,0 +1,380 @@ +From e51edc13e72c6bfe617ff45a718ed7820812abef Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Tue, 5 Mar 2024 22:43:51 -0500 +Subject: smb: client: reuse file lease key in compound operations + +From: Meetakshi Setiya <msetiya@microsoft.com> + +[ Upstream commit 2c7d399e551ccfd87bcae4ef5573097f3313d779 ] + +Currently, when a rename, unlink or set path size compound operation +is requested on a file that has a lot of dirty pages to be written +to the server, we do not send the lease key for these requests. As a +result, the server can assume that this request is from a new client, and +send a lease break notification to the same client, on the same +connection. As a response to the lease break, the client can consume +several credits to write the dirty pages to the server. Depending on the +server's credit grant implementation, the server can stop granting more +credits to this connection, and this can cause a deadlock (which can only +be resolved when the lease timer on the server expires). +One of the problems here is that the client is sending no lease key, +even if it has a lease for the file. This patch fixes the problem by +reusing the existing lease key on the file for rename, unlink and set path +size compound operations so that the client does not break its own lease. + +A very trivial example could be a set of commands by a client that +maintains open handle (for write) to a file and then tries to copy the +contents of that file to another one, eg., + +tail -f /dev/null > myfile & +mv myfile myfile2 + +Presently, the network capture on the client shows that the move (or +rename) would trigger a lease break on the same client, for the same file. +With the lease key reused, the lease break request-response overhead is +eliminated, thereby reducing the roundtrips performed for this set of +operations. + +The patch fixes the bug described above and also provides perf benefit. + +Signed-off-by: Meetakshi Setiya <msetiya@microsoft.com> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/client/cifsglob.h | 5 ++-- + fs/smb/client/cifsproto.h | 6 +++-- + fs/smb/client/cifssmb.c | 4 ++-- + fs/smb/client/inode.c | 10 ++++---- + fs/smb/client/smb2inode.c | 48 ++++++++++++++++++++++++--------------- + fs/smb/client/smb2proto.h | 6 +++-- + 6 files changed, 48 insertions(+), 31 deletions(-) + +diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h +index 678a9c671cdcd..a149579910add 100644 +--- a/fs/smb/client/cifsglob.h ++++ b/fs/smb/client/cifsglob.h +@@ -374,7 +374,8 @@ struct smb_version_operations { + struct cifs_open_info_data *data); + /* set size by path */ + int (*set_path_size)(const unsigned int, struct cifs_tcon *, +- const char *, __u64, struct cifs_sb_info *, bool); ++ const char *, __u64, struct cifs_sb_info *, bool, ++ struct dentry *); + /* set size by file handle */ + int (*set_file_size)(const unsigned int, struct cifs_tcon *, + struct cifsFileInfo *, __u64, bool); +@@ -404,7 +405,7 @@ struct smb_version_operations { + struct cifs_sb_info *); + /* unlink file */ + int (*unlink)(const unsigned int, struct cifs_tcon *, const char *, +- struct cifs_sb_info *); ++ struct cifs_sb_info *, struct dentry *); + /* open, rename and delete file */ + int (*rename_pending_delete)(const char *, struct dentry *, + const unsigned int); +diff --git a/fs/smb/client/cifsproto.h b/fs/smb/client/cifsproto.h +index 996ca413dd8bd..e9b38b279a6c5 100644 +--- a/fs/smb/client/cifsproto.h ++++ b/fs/smb/client/cifsproto.h +@@ -404,7 +404,8 @@ extern int CIFSSMBSetFileDisposition(const unsigned int xid, + __u32 pid_of_opener); + extern int CIFSSMBSetEOF(const unsigned int xid, struct cifs_tcon *tcon, + const char *file_name, __u64 size, +- struct cifs_sb_info *cifs_sb, bool set_allocation); ++ struct cifs_sb_info *cifs_sb, bool set_allocation, ++ struct dentry *dentry); + extern int CIFSSMBSetFileSize(const unsigned int xid, struct cifs_tcon *tcon, + struct cifsFileInfo *cfile, __u64 size, + bool set_allocation); +@@ -440,7 +441,8 @@ extern int CIFSPOSIXDelFile(const unsigned int xid, struct cifs_tcon *tcon, + const struct nls_table *nls_codepage, + int remap_special_chars); + extern int CIFSSMBDelFile(const unsigned int xid, struct cifs_tcon *tcon, +- const char *name, struct cifs_sb_info *cifs_sb); ++ const char *name, struct cifs_sb_info *cifs_sb, ++ struct dentry *dentry); + int CIFSSMBRename(const unsigned int xid, struct cifs_tcon *tcon, + struct dentry *source_dentry, + const char *from_name, const char *to_name, +diff --git a/fs/smb/client/cifssmb.c b/fs/smb/client/cifssmb.c +index 01e89070df5ab..301189ee1335b 100644 +--- a/fs/smb/client/cifssmb.c ++++ b/fs/smb/client/cifssmb.c +@@ -738,7 +738,7 @@ CIFSPOSIXDelFile(const unsigned int xid, struct cifs_tcon *tcon, + + int + CIFSSMBDelFile(const unsigned int xid, struct cifs_tcon *tcon, const char *name, +- struct cifs_sb_info *cifs_sb) ++ struct cifs_sb_info *cifs_sb, struct dentry *dentry) + { + DELETE_FILE_REQ *pSMB = NULL; + DELETE_FILE_RSP *pSMBr = NULL; +@@ -4993,7 +4993,7 @@ CIFSSMBQFSPosixInfo(const unsigned int xid, struct cifs_tcon *tcon, + int + CIFSSMBSetEOF(const unsigned int xid, struct cifs_tcon *tcon, + const char *file_name, __u64 size, struct cifs_sb_info *cifs_sb, +- bool set_allocation) ++ bool set_allocation, struct dentry *dentry) + { + struct smb_com_transaction2_spi_req *pSMB = NULL; + struct smb_com_transaction2_spi_rsp *pSMBr = NULL; +diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c +index d7e3da8489a0b..156713186b3c9 100644 +--- a/fs/smb/client/inode.c ++++ b/fs/smb/client/inode.c +@@ -1849,7 +1849,7 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry) + goto psx_del_no_retry; + } + +- rc = server->ops->unlink(xid, tcon, full_path, cifs_sb); ++ rc = server->ops->unlink(xid, tcon, full_path, cifs_sb, dentry); + + psx_del_no_retry: + if (!rc) { +@@ -2800,7 +2800,7 @@ void cifs_setsize(struct inode *inode, loff_t offset) + + static int + cifs_set_file_size(struct inode *inode, struct iattr *attrs, +- unsigned int xid, const char *full_path) ++ unsigned int xid, const char *full_path, struct dentry *dentry) + { + int rc; + struct cifsFileInfo *open_file; +@@ -2851,7 +2851,7 @@ cifs_set_file_size(struct inode *inode, struct iattr *attrs, + */ + if (server->ops->set_path_size) + rc = server->ops->set_path_size(xid, tcon, full_path, +- attrs->ia_size, cifs_sb, false); ++ attrs->ia_size, cifs_sb, false, dentry); + else + rc = -ENOSYS; + cifs_dbg(FYI, "SetEOF by path (setattrs) rc = %d\n", rc); +@@ -2941,7 +2941,7 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs) + rc = 0; + + if (attrs->ia_valid & ATTR_SIZE) { +- rc = cifs_set_file_size(inode, attrs, xid, full_path); ++ rc = cifs_set_file_size(inode, attrs, xid, full_path, direntry); + if (rc != 0) + goto out; + } +@@ -3107,7 +3107,7 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs) + } + + if (attrs->ia_valid & ATTR_SIZE) { +- rc = cifs_set_file_size(inode, attrs, xid, full_path); ++ rc = cifs_set_file_size(inode, attrs, xid, full_path, direntry); + if (rc != 0) + goto cifs_setattr_exit; + } +diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c +index dfa4e5362213f..e452c59c13e2d 100644 +--- a/fs/smb/client/smb2inode.c ++++ b/fs/smb/client/smb2inode.c +@@ -98,7 +98,7 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, + __u32 desired_access, __u32 create_disposition, + __u32 create_options, umode_t mode, struct kvec *in_iov, + int *cmds, int num_cmds, struct cifsFileInfo *cfile, +- struct kvec *out_iov, int *out_buftype) ++ struct kvec *out_iov, int *out_buftype, struct dentry *dentry) + { + + struct reparse_data_buffer *rbuf; +@@ -115,6 +115,7 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, + int resp_buftype[MAX_COMPOUND]; + struct smb2_query_info_rsp *qi_rsp = NULL; + struct cifs_open_info_data *idata; ++ struct inode *inode = NULL; + int flags = 0; + __u8 delete_pending[8] = {1, 0, 0, 0, 0, 0, 0, 0}; + unsigned int size[2]; +@@ -152,6 +153,15 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, + goto finished; + } + ++ /* if there is an existing lease, reuse it */ ++ if (dentry) { ++ inode = d_inode(dentry); ++ if (CIFS_I(inode)->lease_granted && server->ops->get_lease_key) { ++ oplock = SMB2_OPLOCK_LEVEL_LEASE; ++ server->ops->get_lease_key(inode, &fid); ++ } ++ } ++ + vars->oparms = (struct cifs_open_parms) { + .tcon = tcon, + .path = full_path, +@@ -747,7 +757,7 @@ int smb2_query_path_info(const unsigned int xid, + rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, + FILE_READ_ATTRIBUTES, FILE_OPEN, + create_options, ACL_NO_MODE, in_iov, +- cmds, 1, cfile, out_iov, out_buftype); ++ cmds, 1, cfile, out_iov, out_buftype, NULL); + hdr = out_iov[0].iov_base; + /* + * If first iov is unset, then SMB session was dropped or we've got a +@@ -779,7 +789,7 @@ int smb2_query_path_info(const unsigned int xid, + rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, + FILE_READ_ATTRIBUTES, FILE_OPEN, + create_options, ACL_NO_MODE, in_iov, +- cmds, num_cmds, cfile, NULL, NULL); ++ cmds, num_cmds, cfile, NULL, NULL, NULL); + break; + case -EREMOTE: + break; +@@ -811,7 +821,7 @@ smb2_mkdir(const unsigned int xid, struct inode *parent_inode, umode_t mode, + FILE_WRITE_ATTRIBUTES, FILE_CREATE, + CREATE_NOT_FILE, mode, + NULL, &(int){SMB2_OP_MKDIR}, 1, +- NULL, NULL, NULL); ++ NULL, NULL, NULL, NULL); + } + + void +@@ -836,7 +846,7 @@ smb2_mkdir_setinfo(struct inode *inode, const char *name, + FILE_WRITE_ATTRIBUTES, FILE_CREATE, + CREATE_NOT_FILE, ACL_NO_MODE, &in_iov, + &(int){SMB2_OP_SET_INFO}, 1, +- cfile, NULL, NULL); ++ cfile, NULL, NULL, NULL); + if (tmprc == 0) + cifs_i->cifsAttrs = dosattrs; + } +@@ -850,25 +860,26 @@ smb2_rmdir(const unsigned int xid, struct cifs_tcon *tcon, const char *name, + DELETE, FILE_OPEN, CREATE_NOT_FILE, + ACL_NO_MODE, NULL, + &(int){SMB2_OP_RMDIR}, 1, +- NULL, NULL, NULL); ++ NULL, NULL, NULL, NULL); + } + + int + smb2_unlink(const unsigned int xid, struct cifs_tcon *tcon, const char *name, +- struct cifs_sb_info *cifs_sb) ++ struct cifs_sb_info *cifs_sb, struct dentry *dentry) + { + return smb2_compound_op(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN, + CREATE_DELETE_ON_CLOSE | OPEN_REPARSE_POINT, + ACL_NO_MODE, NULL, + &(int){SMB2_OP_DELETE}, 1, +- NULL, NULL, NULL); ++ NULL, NULL, NULL, dentry); + } + + static int smb2_set_path_attr(const unsigned int xid, struct cifs_tcon *tcon, + const char *from_name, const char *to_name, + struct cifs_sb_info *cifs_sb, + __u32 create_options, __u32 access, +- int command, struct cifsFileInfo *cfile) ++ int command, struct cifsFileInfo *cfile, ++ struct dentry *dentry) + { + struct kvec in_iov; + __le16 *smb2_to_name = NULL; +@@ -883,7 +894,7 @@ static int smb2_set_path_attr(const unsigned int xid, struct cifs_tcon *tcon, + in_iov.iov_len = 2 * UniStrnlen((wchar_t *)smb2_to_name, PATH_MAX); + rc = smb2_compound_op(xid, tcon, cifs_sb, from_name, access, + FILE_OPEN, create_options, ACL_NO_MODE, +- &in_iov, &command, 1, cfile, NULL, NULL); ++ &in_iov, &command, 1, cfile, NULL, NULL, dentry); + smb2_rename_path: + kfree(smb2_to_name); + return rc; +@@ -902,7 +913,7 @@ int smb2_rename_path(const unsigned int xid, + cifs_get_writable_path(tcon, from_name, FIND_WR_WITH_DELETE, &cfile); + + return smb2_set_path_attr(xid, tcon, from_name, to_name, cifs_sb, +- co, DELETE, SMB2_OP_RENAME, cfile); ++ co, DELETE, SMB2_OP_RENAME, cfile, source_dentry); + } + + int smb2_create_hardlink(const unsigned int xid, +@@ -915,13 +926,14 @@ int smb2_create_hardlink(const unsigned int xid, + + return smb2_set_path_attr(xid, tcon, from_name, to_name, + cifs_sb, co, FILE_READ_ATTRIBUTES, +- SMB2_OP_HARDLINK, NULL); ++ SMB2_OP_HARDLINK, NULL, NULL); + } + + int + smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon, + const char *full_path, __u64 size, +- struct cifs_sb_info *cifs_sb, bool set_alloc) ++ struct cifs_sb_info *cifs_sb, bool set_alloc, ++ struct dentry *dentry) + { + struct cifsFileInfo *cfile; + struct kvec in_iov; +@@ -934,7 +946,7 @@ smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon, + FILE_WRITE_DATA, FILE_OPEN, + 0, ACL_NO_MODE, &in_iov, + &(int){SMB2_OP_SET_EOF}, 1, +- cfile, NULL, NULL); ++ cfile, NULL, NULL, dentry); + } + + int +@@ -963,7 +975,7 @@ smb2_set_file_info(struct inode *inode, const char *full_path, + FILE_WRITE_ATTRIBUTES, FILE_OPEN, + 0, ACL_NO_MODE, &in_iov, + &(int){SMB2_OP_SET_INFO}, 1, +- cfile, NULL, NULL); ++ cfile, NULL, NULL, NULL); + cifs_put_tlink(tlink); + return rc; + } +@@ -998,7 +1010,7 @@ struct inode *smb2_get_reparse_inode(struct cifs_open_info_data *data, + cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile); + rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, + da, cd, co, ACL_NO_MODE, in_iov, +- cmds, 2, cfile, NULL, NULL); ++ cmds, 2, cfile, NULL, NULL, NULL); + if (!rc) { + rc = smb311_posix_get_inode_info(&new, full_path, + data, sb, xid); +@@ -1008,7 +1020,7 @@ struct inode *smb2_get_reparse_inode(struct cifs_open_info_data *data, + cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile); + rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, + da, cd, co, ACL_NO_MODE, in_iov, +- cmds, 2, cfile, NULL, NULL); ++ cmds, 2, cfile, NULL, NULL, NULL); + if (!rc) { + rc = cifs_get_inode_info(&new, full_path, + data, sb, xid, NULL); +@@ -1036,7 +1048,7 @@ int smb2_query_reparse_point(const unsigned int xid, + FILE_READ_ATTRIBUTES, FILE_OPEN, + OPEN_REPARSE_POINT, ACL_NO_MODE, &in_iov, + &(int){SMB2_OP_GET_REPARSE}, 1, +- cfile, NULL, NULL); ++ cfile, NULL, NULL, NULL); + if (rc) + goto out; + +diff --git a/fs/smb/client/smb2proto.h b/fs/smb/client/smb2proto.h +index b3069911e9dd8..221143788a1c0 100644 +--- a/fs/smb/client/smb2proto.h ++++ b/fs/smb/client/smb2proto.h +@@ -75,7 +75,8 @@ int smb2_query_path_info(const unsigned int xid, + struct cifs_open_info_data *data); + extern int smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon, + const char *full_path, __u64 size, +- struct cifs_sb_info *cifs_sb, bool set_alloc); ++ struct cifs_sb_info *cifs_sb, bool set_alloc, ++ struct dentry *dentry); + extern int smb2_set_file_info(struct inode *inode, const char *full_path, + FILE_BASIC_INFO *buf, const unsigned int xid); + extern int smb311_posix_mkdir(const unsigned int xid, struct inode *inode, +@@ -91,7 +92,8 @@ extern void smb2_mkdir_setinfo(struct inode *inode, const char *full_path, + extern int smb2_rmdir(const unsigned int xid, struct cifs_tcon *tcon, + const char *name, struct cifs_sb_info *cifs_sb); + extern int smb2_unlink(const unsigned int xid, struct cifs_tcon *tcon, +- const char *name, struct cifs_sb_info *cifs_sb); ++ const char *name, struct cifs_sb_info *cifs_sb, ++ struct dentry *dentry); + int smb2_rename_path(const unsigned int xid, + struct cifs_tcon *tcon, + struct dentry *source_dentry, +-- +2.43.0 + diff --git a/queue-6.6/smb-client-set-correct-d_type-for-reparse-dfs-dfsr-a.patch b/queue-6.6/smb-client-set-correct-d_type-for-reparse-dfs-dfsr-a.patch new file mode 100644 index 0000000000..964967677d --- /dev/null +++ b/queue-6.6/smb-client-set-correct-d_type-for-reparse-dfs-dfsr-a.patch @@ -0,0 +1,81 @@ +From 148cef4216aaf1aa8fdad581a83546f3dcde895e Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Fri, 2 Feb 2024 13:42:11 -0300 +Subject: smb: client: set correct d_type for reparse DFS/DFSR and mount point + +From: Paulo Alcantara <pc@manguebit.com> + +[ Upstream commit 8bd25b61c5a55bc769c6608e9ce95860759acdcb ] + +Set correct dirent->d_type for IO_REPARSE_TAG_DFS{,R} and +IO_REPARSE_TAG_MOUNT_POINT reparse points. + +Signed-off-by: Paulo Alcantara <pc@manguebit.com> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/client/reparse.c | 16 +++++++++------- + 1 file changed, 9 insertions(+), 7 deletions(-) + +diff --git a/fs/smb/client/reparse.c b/fs/smb/client/reparse.c +index 29a47f20643b1..a0ffbda907331 100644 +--- a/fs/smb/client/reparse.c ++++ b/fs/smb/client/reparse.c +@@ -482,34 +482,35 @@ bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb, + switch (le64_to_cpu(buf->InodeType)) { + case NFS_SPECFILE_CHR: + fattr->cf_mode |= S_IFCHR; +- fattr->cf_dtype = DT_CHR; + fattr->cf_rdev = reparse_nfs_mkdev(buf); + break; + case NFS_SPECFILE_BLK: + fattr->cf_mode |= S_IFBLK; +- fattr->cf_dtype = DT_BLK; + fattr->cf_rdev = reparse_nfs_mkdev(buf); + break; + case NFS_SPECFILE_FIFO: + fattr->cf_mode |= S_IFIFO; +- fattr->cf_dtype = DT_FIFO; + break; + case NFS_SPECFILE_SOCK: + fattr->cf_mode |= S_IFSOCK; +- fattr->cf_dtype = DT_SOCK; + break; + case NFS_SPECFILE_LNK: + fattr->cf_mode |= S_IFLNK; +- fattr->cf_dtype = DT_LNK; + break; + default: + WARN_ON_ONCE(1); + return false; + } +- return true; ++ goto out; + } + + switch (tag) { ++ case IO_REPARSE_TAG_DFS: ++ case IO_REPARSE_TAG_DFSR: ++ case IO_REPARSE_TAG_MOUNT_POINT: ++ /* See cifs_create_junction_fattr() */ ++ fattr->cf_mode = S_IFDIR | 0711; ++ break; + case IO_REPARSE_TAG_LX_SYMLINK: + case IO_REPARSE_TAG_LX_FIFO: + case IO_REPARSE_TAG_AF_UNIX: +@@ -521,10 +522,11 @@ bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb, + case IO_REPARSE_TAG_SYMLINK: + case IO_REPARSE_TAG_NFS: + fattr->cf_mode |= S_IFLNK; +- fattr->cf_dtype = DT_LNK; + break; + default: + return false; + } ++out: ++ fattr->cf_dtype = S_DT(fattr->cf_mode); + return true; + } +-- +2.43.0 + diff --git a/queue-6.6/smb-common-fix-fields-sizes-in-compression_pattern_p.patch b/queue-6.6/smb-common-fix-fields-sizes-in-compression_pattern_p.patch new file mode 100644 index 0000000000..bf21aa53f1 --- /dev/null +++ b/queue-6.6/smb-common-fix-fields-sizes-in-compression_pattern_p.patch @@ -0,0 +1,36 @@ +From e9ee29350dd71c0b81a8732f4cb85a59a5a6d842 Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Fri, 8 Mar 2024 18:34:10 -0300 +Subject: smb: common: fix fields sizes in compression_pattern_payload_v1 + +From: Enzo Matsumiya <ematsumiya@suse.de> + +[ Upstream commit f49af462875a0922167cf301cf126cd04009070e ] + +See protocol documentation in MS-SMB2 section 2.2.42.2.2 + +Signed-off-by: Enzo Matsumiya <ematsumiya@suse.de> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/common/smb2pdu.h | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/fs/smb/common/smb2pdu.h b/fs/smb/common/smb2pdu.h +index a233a24352b1f..10a9e20eec43f 100644 +--- a/fs/smb/common/smb2pdu.h ++++ b/fs/smb/common/smb2pdu.h +@@ -238,8 +238,8 @@ struct smb2_compression_transform_hdr_chained { + + /* See MS-SMB2 2.2.42.2.2 */ + struct compression_pattern_payload_v1 { +- __le16 Pattern; +- __le16 Reserved1; ++ __u8 Pattern; ++ __u8 Reserved1; + __le16 Reserved2; + __le32 Repetitions; + } __packed; +-- +2.43.0 + diff --git a/queue-6.6/smb-common-simplify-compression-headers.patch b/queue-6.6/smb-common-simplify-compression-headers.patch new file mode 100644 index 0000000000..f90e1823fe --- /dev/null +++ b/queue-6.6/smb-common-simplify-compression-headers.patch @@ -0,0 +1,97 @@ +From ba3468b11ceb9ee919f73d46ffd42cffe49b2d42 Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Fri, 8 Mar 2024 19:00:12 -0300 +Subject: smb: common: simplify compression headers + +From: Enzo Matsumiya <ematsumiya@suse.de> + +[ Upstream commit 24337b60e88219816f84d633369299660e8e8cce ] + +Unify compression headers (chained and unchained) into a single struct +so we can use it for the initial compression transform header +interchangeably. + +Also make the OriginalPayloadSize field to be always visible in the +compression payload header, and have callers subtract its size when not +needed. + +Rename the related structs to match the naming convetion used in the +other SMB2 structs. + +Signed-off-by: Enzo Matsumiya <ematsumiya@suse.de> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/common/smb2pdu.h | 45 ++++++++++++++++++++++++----------------- + 1 file changed, 26 insertions(+), 19 deletions(-) + +diff --git a/fs/smb/common/smb2pdu.h b/fs/smb/common/smb2pdu.h +index 10a9e20eec43f..61c6e72ccddc4 100644 +--- a/fs/smb/common/smb2pdu.h ++++ b/fs/smb/common/smb2pdu.h +@@ -208,36 +208,43 @@ struct smb2_transform_hdr { + __le64 SessionId; + } __packed; + ++/* ++ * These are simplified versions from the spec, as we don't need a fully fledged ++ * form of both unchained and chained structs. ++ * ++ * Moreover, even in chained compressed payloads, the initial compression header ++ * has the form of the unchained one -- i.e. it never has the ++ * OriginalPayloadSize field and ::Offset field always represent an offset ++ * (instead of a length, as it is in the chained header). ++ * ++ * See MS-SMB2 2.2.42 for more details. ++ */ ++#define SMB2_COMPRESSION_FLAG_NONE 0x0000 ++#define SMB2_COMPRESSION_FLAG_CHAINED 0x0001 + +-/* See MS-SMB2 2.2.42 */ +-struct smb2_compression_transform_hdr_unchained { +- __le32 ProtocolId; /* 0xFC 'S' 'M' 'B' */ ++struct smb2_compression_hdr { ++ __le32 ProtocolId; /* 0xFC 'S' 'M' 'B' */ + __le32 OriginalCompressedSegmentSize; + __le16 CompressionAlgorithm; + __le16 Flags; +- __le16 Length; /* if chained it is length, else offset */ ++ __le16 Offset; /* this is the size of the uncompressed SMB2 header below */ ++ /* uncompressed SMB2 header (READ or WRITE) goes here */ ++ /* compressed data goes here */ + } __packed; + +-/* See MS-SMB2 2.2.42.1 */ +-#define SMB2_COMPRESSION_FLAG_NONE 0x0000 +-#define SMB2_COMPRESSION_FLAG_CHAINED 0x0001 +- +-struct compression_payload_header { ++/* ++ * ... OTOH, set compression payload header to always have OriginalPayloadSize ++ * as it's easier to pass the struct size minus sizeof(OriginalPayloadSize) ++ * than to juggle around the header/data memory. ++ */ ++struct smb2_compression_payload_hdr { + __le16 CompressionAlgorithm; + __le16 Flags; + __le32 Length; /* length of compressed playload including field below if present */ +- /* __le32 OriginalPayloadSize; */ /* optional, present when LZNT1, LZ77, LZ77+Huffman */ +-} __packed; +- +-/* See MS-SMB2 2.2.42.2 */ +-struct smb2_compression_transform_hdr_chained { +- __le32 ProtocolId; /* 0xFC 'S' 'M' 'B' */ +- __le32 OriginalCompressedSegmentSize; +- /* struct compression_payload_header[] */ ++ __le32 OriginalPayloadSize; /* accounted when LZNT1, LZ77, LZ77+Huffman */ + } __packed; + +-/* See MS-SMB2 2.2.42.2.2 */ +-struct compression_pattern_payload_v1 { ++struct smb2_compression_pattern_v1 { + __u8 Pattern; + __u8 Reserved1; + __le16 Reserved2; +-- +2.43.0 + diff --git a/queue-6.6/smb-fix-some-kernel-doc-comments.patch b/queue-6.6/smb-fix-some-kernel-doc-comments.patch new file mode 100644 index 0000000000..74fb45dff4 --- /dev/null +++ b/queue-6.6/smb-fix-some-kernel-doc-comments.patch @@ -0,0 +1,44 @@ +From dbd018721c5f81232beff364fcdd729b0f98b6f2 Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Fri, 19 Jan 2024 17:57:07 +0800 +Subject: smb: Fix some kernel-doc comments + +From: Yang Li <yang.lee@linux.alibaba.com> + +[ Upstream commit 72b0cbf6b81003c01d63c60180b335f7692d170e ] + +Fix some kernel-doc comments to silence the warnings: +fs/smb/server/transport_tcp.c:374: warning: Function parameter or struct member 'max_retries' not described in 'ksmbd_tcp_read' +fs/smb/server/transport_tcp.c:423: warning: Function parameter or struct member 'iface' not described in 'create_socket' + +Signed-off-by: Yang Li <yang.lee@linux.alibaba.com> +Acked-by: Namjae Jeon <linkinjeon@kernel.org> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/server/transport_tcp.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/fs/smb/server/transport_tcp.c b/fs/smb/server/transport_tcp.c +index 0012919309f11..6633fa78e9b96 100644 +--- a/fs/smb/server/transport_tcp.c ++++ b/fs/smb/server/transport_tcp.c +@@ -365,6 +365,7 @@ static int ksmbd_tcp_readv(struct tcp_transport *t, struct kvec *iov_orig, + * @t: TCP transport instance + * @buf: buffer to store read data from socket + * @to_read: number of bytes to read from socket ++ * @max_retries: number of retries if reading from socket fails + * + * Return: on success return number of bytes read from socket, + * otherwise return error number +@@ -416,6 +417,7 @@ static void tcp_destroy_socket(struct socket *ksmbd_socket) + + /** + * create_socket - create socket for ksmbd/0 ++ * @iface: interface to bind the created socket to + * + * Return: 0 on success, error number otherwise + */ +-- +2.43.0 + diff --git a/queue-6.6/smb-smb2pdu.h-avoid-wflex-array-member-not-at-end-wa.patch b/queue-6.6/smb-smb2pdu.h-avoid-wflex-array-member-not-at-end-wa.patch new file mode 100644 index 0000000000..786f4d58b6 --- /dev/null +++ b/queue-6.6/smb-smb2pdu.h-avoid-wflex-array-member-not-at-end-wa.patch @@ -0,0 +1,273 @@ +From 5485b6988657801f1eed94bea0f1786fff6f6b14 Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Thu, 11 Apr 2024 09:35:42 -0600 +Subject: smb: smb2pdu.h: Avoid -Wflex-array-member-not-at-end warnings + +From: Gustavo A. R. Silva <gustavoars@kernel.org> + +-Wflex-array-member-not-at-end is coming in GCC-14, and we are getting +ready to enable it globally. + +So, in order to avoid ending up with a flexible-array member in the +middle of multiple other structs, we use the `__struct_group()` helper +to separate the flexible array from the rest of the members in the +flexible structure, and use the tagged `struct create_context_hdr` +instead of `struct create_context`. + +So, with these changes, fix 51 of the following warnings[1]: + +fs/smb/client/../common/smb2pdu.h:1225:31: warning: structure containing a flexible array member is not at the end of another structure [-Wflex-array-member-not-at-end] + +Link: https://gist.github.com/GustavoARSilva/772526a39be3dd4db39e71497f0a9893 [1] +Link: https://github.com/KSPP/linux/issues/202 +Signed-off-by: Gustavo A. R. Silva <gustavoars@kernel.org> +Signed-off-by: Steve French <stfrench@microsoft.com> +--- + fs/smb/client/smb2pdu.h | 12 ++++++------ + fs/smb/common/smb2pdu.h | 33 ++++++++++++++++++--------------- + fs/smb/server/smb2pdu.h | 18 +++++++++--------- + 3 files changed, 33 insertions(+), 30 deletions(-) + +diff --git a/fs/smb/client/smb2pdu.h b/fs/smb/client/smb2pdu.h +index 2fccf0d4f53d2..5c458ab3b05a4 100644 +--- a/fs/smb/client/smb2pdu.h ++++ b/fs/smb/client/smb2pdu.h +@@ -145,7 +145,7 @@ struct durable_context_v2 { + } __packed; + + struct create_durable_v2 { +- struct create_context ccontext; ++ struct create_context_hdr ccontext; + __u8 Name[8]; + struct durable_context_v2 dcontext; + } __packed; +@@ -167,7 +167,7 @@ struct durable_reconnect_context_v2_rsp { + } __packed; + + struct create_durable_handle_reconnect_v2 { +- struct create_context ccontext; ++ struct create_context_hdr ccontext; + __u8 Name[8]; + struct durable_reconnect_context_v2 dcontext; + __u8 Pad[4]; +@@ -175,7 +175,7 @@ struct create_durable_handle_reconnect_v2 { + + /* See MS-SMB2 2.2.13.2.5 */ + struct crt_twarp_ctxt { +- struct create_context ccontext; ++ struct create_context_hdr ccontext; + __u8 Name[8]; + __le64 Timestamp; + +@@ -183,12 +183,12 @@ struct crt_twarp_ctxt { + + /* See MS-SMB2 2.2.13.2.9 */ + struct crt_query_id_ctxt { +- struct create_context ccontext; ++ struct create_context_hdr ccontext; + __u8 Name[8]; + } __packed; + + struct crt_sd_ctxt { +- struct create_context ccontext; ++ struct create_context_hdr ccontext; + __u8 Name[8]; + struct smb3_sd sd; + } __packed; +@@ -415,7 +415,7 @@ struct smb2_posix_info_parsed { + }; + + struct smb2_create_ea_ctx { +- struct create_context ctx; ++ struct create_context_hdr ctx; + __u8 name[8]; + struct smb2_file_full_ea_info ea; + } __packed; +diff --git a/fs/smb/common/smb2pdu.h b/fs/smb/common/smb2pdu.h +index 202ff91281560..8d10be1fe18a8 100644 +--- a/fs/smb/common/smb2pdu.h ++++ b/fs/smb/common/smb2pdu.h +@@ -1171,12 +1171,15 @@ struct smb2_server_client_notification { + #define SMB2_CREATE_FLAG_REPARSEPOINT 0x01 + + struct create_context { +- __le32 Next; +- __le16 NameOffset; +- __le16 NameLength; +- __le16 Reserved; +- __le16 DataOffset; +- __le32 DataLength; ++ /* New members must be added within the struct_group() macro below. */ ++ __struct_group(create_context_hdr, hdr, __packed, ++ __le32 Next; ++ __le16 NameOffset; ++ __le16 NameLength; ++ __le16 Reserved; ++ __le16 DataOffset; ++ __le32 DataLength; ++ ); + __u8 Buffer[]; + } __packed; + +@@ -1222,7 +1225,7 @@ struct smb2_create_rsp { + } __packed; + + struct create_posix { +- struct create_context ccontext; ++ struct create_context_hdr ccontext; + __u8 Name[16]; + __le32 Mode; + __u32 Reserved; +@@ -1230,7 +1233,7 @@ struct create_posix { + + /* See MS-SMB2 2.2.13.2.3 and MS-SMB2 2.2.13.2.4 */ + struct create_durable { +- struct create_context ccontext; ++ struct create_context_hdr ccontext; + __u8 Name[8]; + union { + __u8 Reserved[16]; +@@ -1243,14 +1246,14 @@ struct create_durable { + + /* See MS-SMB2 2.2.13.2.5 */ + struct create_mxac_req { +- struct create_context ccontext; ++ struct create_context_hdr ccontext; + __u8 Name[8]; + __le64 Timestamp; + } __packed; + + /* See MS-SMB2 2.2.14.2.5 */ + struct create_mxac_rsp { +- struct create_context ccontext; ++ struct create_context_hdr ccontext; + __u8 Name[8]; + __le32 QueryStatus; + __le32 MaximalAccess; +@@ -1286,13 +1289,13 @@ struct lease_context_v2 { + } __packed; + + struct create_lease { +- struct create_context ccontext; ++ struct create_context_hdr ccontext; + __u8 Name[8]; + struct lease_context lcontext; + } __packed; + + struct create_lease_v2 { +- struct create_context ccontext; ++ struct create_context_hdr ccontext; + __u8 Name[8]; + struct lease_context_v2 lcontext; + __u8 Pad[4]; +@@ -1300,7 +1303,7 @@ struct create_lease_v2 { + + /* See MS-SMB2 2.2.14.2.9 */ + struct create_disk_id_rsp { +- struct create_context ccontext; ++ struct create_context_hdr ccontext; + __u8 Name[8]; + __le64 DiskFileId; + __le64 VolumeId; +@@ -1309,7 +1312,7 @@ struct create_disk_id_rsp { + + /* See MS-SMB2 2.2.13.2.13 */ + struct create_app_inst_id { +- struct create_context ccontext; ++ struct create_context_hdr ccontext; + __u8 Name[16]; + __le32 StructureSize; /* Must be 20 */ + __u16 Reserved; +@@ -1318,7 +1321,7 @@ struct create_app_inst_id { + + /* See MS-SMB2 2.2.13.2.15 */ + struct create_app_inst_id_vers { +- struct create_context ccontext; ++ struct create_context_hdr ccontext; + __u8 Name[16]; + __le32 StructureSize; /* Must be 24 */ + __u16 Reserved; +diff --git a/fs/smb/server/smb2pdu.h b/fs/smb/server/smb2pdu.h +index bd1d2a0e9203a..643f5e1cfe357 100644 +--- a/fs/smb/server/smb2pdu.h ++++ b/fs/smb/server/smb2pdu.h +@@ -64,7 +64,7 @@ struct preauth_integrity_info { + #define SMB2_SESSION_TIMEOUT (10 * HZ) + + struct create_durable_req_v2 { +- struct create_context ccontext; ++ struct create_context_hdr ccontext; + __u8 Name[8]; + __le32 Timeout; + __le32 Flags; +@@ -73,7 +73,7 @@ struct create_durable_req_v2 { + } __packed; + + struct create_durable_reconn_req { +- struct create_context ccontext; ++ struct create_context_hdr ccontext; + __u8 Name[8]; + union { + __u8 Reserved[16]; +@@ -85,7 +85,7 @@ struct create_durable_reconn_req { + } __packed; + + struct create_durable_reconn_v2_req { +- struct create_context ccontext; ++ struct create_context_hdr ccontext; + __u8 Name[8]; + struct { + __u64 PersistentFileId; +@@ -96,13 +96,13 @@ struct create_durable_reconn_v2_req { + } __packed; + + struct create_alloc_size_req { +- struct create_context ccontext; ++ struct create_context_hdr ccontext; + __u8 Name[8]; + __le64 AllocationSize; + } __packed; + + struct create_durable_rsp { +- struct create_context ccontext; ++ struct create_context_hdr ccontext; + __u8 Name[8]; + union { + __u8 Reserved[8]; +@@ -114,7 +114,7 @@ struct create_durable_rsp { + /* Flags */ + #define SMB2_DHANDLE_FLAG_PERSISTENT 0x00000002 + struct create_durable_v2_rsp { +- struct create_context ccontext; ++ struct create_context_hdr ccontext; + __u8 Name[8]; + __le32 Timeout; + __le32 Flags; +@@ -122,7 +122,7 @@ struct create_durable_v2_rsp { + + /* equivalent of the contents of SMB3.1.1 POSIX open context response */ + struct create_posix_rsp { +- struct create_context ccontext; ++ struct create_context_hdr ccontext; + __u8 Name[16]; + __le32 nlink; + __le32 reparse_tag; +@@ -381,13 +381,13 @@ struct smb2_ea_info { + } __packed; /* level 15 Query */ + + struct create_ea_buf_req { +- struct create_context ccontext; ++ struct create_context_hdr ccontext; + __u8 Name[8]; + struct smb2_ea_info ea; + } __packed; + + struct create_sd_buf_req { +- struct create_context ccontext; ++ struct create_context_hdr ccontext; + __u8 Name[8]; + struct smb_ntsd ntsd; + } __packed; +-- +2.43.0 + diff --git a/queue-6.6/smb-use-crypto_shash_digest-in-symlink_hash.patch b/queue-6.6/smb-use-crypto_shash_digest-in-symlink_hash.patch new file mode 100644 index 0000000000..4465f6207d --- /dev/null +++ b/queue-6.6/smb-use-crypto_shash_digest-in-symlink_hash.patch @@ -0,0 +1,53 @@ +From 1c6ee581f8d0171730a04fbcab9406ee83392f86 Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Sat, 28 Oct 2023 22:03:00 -0700 +Subject: smb: use crypto_shash_digest() in symlink_hash() + +From: Eric Biggers <ebiggers@google.com> + +[ Upstream commit 783fa2c94f4150fe1b7f7d88b3baf6d98f82b41b ] + +Simplify symlink_hash() by using crypto_shash_digest() instead of an +init+update+final sequence. This should also improve performance. + +Signed-off-by: Eric Biggers <ebiggers@google.com> +Reviewed-by: Paulo Alcantara (SUSE) <pc@manguebit.com> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/client/link.c | 16 ++-------------- + 1 file changed, 2 insertions(+), 14 deletions(-) + +diff --git a/fs/smb/client/link.c b/fs/smb/client/link.c +index 6c4ae52ddc04f..3ef34218a790d 100644 +--- a/fs/smb/client/link.c ++++ b/fs/smb/client/link.c +@@ -42,23 +42,11 @@ symlink_hash(unsigned int link_len, const char *link_str, u8 *md5_hash) + + rc = cifs_alloc_hash("md5", &md5); + if (rc) +- goto symlink_hash_err; ++ return rc; + +- rc = crypto_shash_init(md5); +- if (rc) { +- cifs_dbg(VFS, "%s: Could not init md5 shash\n", __func__); +- goto symlink_hash_err; +- } +- rc = crypto_shash_update(md5, link_str, link_len); +- if (rc) { +- cifs_dbg(VFS, "%s: Could not update with link_str\n", __func__); +- goto symlink_hash_err; +- } +- rc = crypto_shash_final(md5, md5_hash); ++ rc = crypto_shash_digest(md5, link_str, link_len, md5_hash); + if (rc) + cifs_dbg(VFS, "%s: Could not generate md5 hash\n", __func__); +- +-symlink_hash_err: + cifs_free_hash(&md5); + return rc; + } +-- +2.43.0 + diff --git a/queue-6.6/smb3-add-dynamic-trace-point-for-ioctls.patch b/queue-6.6/smb3-add-dynamic-trace-point-for-ioctls.patch new file mode 100644 index 0000000000..b601b4de30 --- /dev/null +++ b/queue-6.6/smb3-add-dynamic-trace-point-for-ioctls.patch @@ -0,0 +1,89 @@ +From 1c432cabb811faa93a7ee744ddd6fa401e59cd89 Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Wed, 6 Mar 2024 01:03:59 -0600 +Subject: smb3: add dynamic trace point for ioctls + +From: Steve French <stfrench@microsoft.com> + +[ Upstream commit 073dd87c8e1ee55ca163956f0c71249dc28aac51 ] + +It can be helpful in debugging to know which ioctls are called to better +correlate them with smb3 fsctls (and opens). Add a dynamic trace point +to trace ioctls into cifs.ko + +Here is sample output: + + TASK-PID CPU# ||||| TIMESTAMP FUNCTION + | | | ||||| | | + new-inotify-ioc-90418 [001] ..... 142157.397024: smb3_ioctl: xid=18 fid=0x0 ioctl cmd=0xc009cf0b + new-inotify-ioc-90457 [007] ..... 142217.943569: smb3_ioctl: xid=22 fid=0x389bf5b6 ioctl cmd=0xc009cf0b + +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/client/ioctl.c | 5 +++++ + fs/smb/client/trace.h | 32 ++++++++++++++++++++++++++++++++ + 2 files changed, 37 insertions(+) + +diff --git a/fs/smb/client/ioctl.c b/fs/smb/client/ioctl.c +index 682eabdd1d6cc..855ac5a62edfa 100644 +--- a/fs/smb/client/ioctl.c ++++ b/fs/smb/client/ioctl.c +@@ -349,6 +349,11 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg) + xid = get_xid(); + + cifs_dbg(FYI, "cifs ioctl 0x%x\n", command); ++ if (pSMBFile == NULL) ++ trace_smb3_ioctl(xid, 0, command); ++ else ++ trace_smb3_ioctl(xid, pSMBFile->fid.persistent_fid, command); ++ + switch (command) { + case FS_IOC_GETFLAGS: + if (pSMBFile == NULL) +diff --git a/fs/smb/client/trace.h b/fs/smb/client/trace.h +index ce90ae0d77f84..f9c1fd32d0b8c 100644 +--- a/fs/smb/client/trace.h ++++ b/fs/smb/client/trace.h +@@ -1032,6 +1032,38 @@ DEFINE_EVENT(smb3_ses_class, smb3_##name, \ + + DEFINE_SMB3_SES_EVENT(ses_not_found); + ++DECLARE_EVENT_CLASS(smb3_ioctl_class, ++ TP_PROTO(unsigned int xid, ++ __u64 fid, ++ unsigned int command), ++ TP_ARGS(xid, fid, command), ++ TP_STRUCT__entry( ++ __field(unsigned int, xid) ++ __field(__u64, fid) ++ __field(unsigned int, command) ++ ), ++ TP_fast_assign( ++ __entry->xid = xid; ++ __entry->fid = fid; ++ __entry->command = command; ++ ), ++ TP_printk("xid=%u fid=0x%llx ioctl cmd=0x%x", ++ __entry->xid, __entry->fid, __entry->command) ++) ++ ++#define DEFINE_SMB3_IOCTL_EVENT(name) \ ++DEFINE_EVENT(smb3_ioctl_class, smb3_##name, \ ++ TP_PROTO(unsigned int xid, \ ++ __u64 fid, \ ++ unsigned int command), \ ++ TP_ARGS(xid, fid, command)) ++ ++DEFINE_SMB3_IOCTL_EVENT(ioctl); ++ ++ ++ ++ ++ + DECLARE_EVENT_CLASS(smb3_credit_class, + TP_PROTO(__u64 currmid, + __u64 conn_id, +-- +2.43.0 + diff --git a/queue-6.6/smb3-add-trace-event-for-mknod.patch b/queue-6.6/smb3-add-trace-event-for-mknod.patch new file mode 100644 index 0000000000..e6a63207be --- /dev/null +++ b/queue-6.6/smb3-add-trace-event-for-mknod.patch @@ -0,0 +1,86 @@ +From 98b55d3cd8298c48b5caf02de4fcf2553cd055aa Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Sun, 24 Mar 2024 00:01:02 -0500 +Subject: smb3: add trace event for mknod + +From: Steve French <stfrench@microsoft.com> + +[ Upstream commit e9e9fbeb83f65d3d487e0a0838c0867292c99fb2 ] + +Add trace points to help debug mknod and mkfifo: + + smb3_mknod_done + smb3_mknod_enter + smb3_mknod_err + +Example output: + + TASK-PID CPU# ||||| TIMESTAMP FUNCTION + | | | ||||| | | + mkfifo-6163 [003] ..... 960.425558: smb3_mknod_enter: xid=12 sid=0xb55130f6 tid=0x46e6241c path=\fifo1 + mkfifo-6163 [003] ..... 960.432719: smb3_mknod_done: xid=12 sid=0xb55130f6 tid=0x46e6241c + +Reviewed-by: Bharath SM <bharathsm@microsoft.com> +Reviewed-by: Meetakshi Setiya <msetiya@microsoft.com> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/client/dir.c | 7 +++++++ + fs/smb/client/trace.h | 4 +++- + 2 files changed, 10 insertions(+), 1 deletion(-) + +diff --git a/fs/smb/client/dir.c b/fs/smb/client/dir.c +index 37897b919dd5a..864b194dbaa0a 100644 +--- a/fs/smb/client/dir.c ++++ b/fs/smb/client/dir.c +@@ -627,11 +627,18 @@ int cifs_mknod(struct mnt_idmap *idmap, struct inode *inode, + goto mknod_out; + } + ++ trace_smb3_mknod_enter(xid, tcon->ses->Suid, tcon->tid, full_path); ++ + rc = tcon->ses->server->ops->make_node(xid, inode, direntry, tcon, + full_path, mode, + device_number); + + mknod_out: ++ if (rc) ++ trace_smb3_mknod_err(xid, tcon->ses->Suid, tcon->tid, rc); ++ else ++ trace_smb3_mknod_done(xid, tcon->ses->Suid, tcon->tid); ++ + free_dentry_path(page); + free_xid(xid); + cifs_put_tlink(tlink); +diff --git a/fs/smb/client/trace.h b/fs/smb/client/trace.h +index f9c1fd32d0b8c..5e83cb9da9028 100644 +--- a/fs/smb/client/trace.h ++++ b/fs/smb/client/trace.h +@@ -375,6 +375,7 @@ DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(get_reparse_compound_enter); + DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(delete_enter); + DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(mkdir_enter); + DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(tdis_enter); ++DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(mknod_enter); + + DECLARE_EVENT_CLASS(smb3_inf_compound_done_class, + TP_PROTO(unsigned int xid, +@@ -415,7 +416,7 @@ DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(query_wsl_ea_compound_done); + DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(delete_done); + DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(mkdir_done); + DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(tdis_done); +- ++DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(mknod_done); + + DECLARE_EVENT_CLASS(smb3_inf_compound_err_class, + TP_PROTO(unsigned int xid, +@@ -461,6 +462,7 @@ DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(query_wsl_ea_compound_err); + DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(mkdir_err); + DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(delete_err); + DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(tdis_err); ++DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(mknod_err); + + /* + * For logging SMB3 Status code and Command for responses which return errors +-- +2.43.0 + diff --git a/queue-6.6/smb3-clarify-some-of-the-unused-createoption-flags.patch b/queue-6.6/smb3-clarify-some-of-the-unused-createoption-flags.patch new file mode 100644 index 0000000000..3deb6fbffa --- /dev/null +++ b/queue-6.6/smb3-clarify-some-of-the-unused-createoption-flags.patch @@ -0,0 +1,51 @@ +From 58f6d04ffd3b6b136721256bc19e9b8ac32ab677 Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Sun, 8 Oct 2023 23:11:38 -0500 +Subject: SMB3: clarify some of the unused CreateOption flags + +From: Steve French <stfrench@microsoft.com> + +[ Upstream commit d5a3c153fd00f5e951c4f20b4c65feb1e1cfbfcb ] + +Update comments to show flags which should be not set (zero). + +See MS-SMB2 section 2.2.13 + +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/common/smb2pdu.h | 9 ++++++++- + 1 file changed, 8 insertions(+), 1 deletion(-) + +diff --git a/fs/smb/common/smb2pdu.h b/fs/smb/common/smb2pdu.h +index 22c94dea52116..465b721f2c06d 100644 +--- a/fs/smb/common/smb2pdu.h ++++ b/fs/smb/common/smb2pdu.h +@@ -1100,16 +1100,23 @@ struct smb2_change_notify_rsp { + #define FILE_WRITE_THROUGH_LE cpu_to_le32(0x00000002) + #define FILE_SEQUENTIAL_ONLY_LE cpu_to_le32(0x00000004) + #define FILE_NO_INTERMEDIATE_BUFFERING_LE cpu_to_le32(0x00000008) ++/* FILE_SYNCHRONOUS_IO_ALERT_LE cpu_to_le32(0x00000010) should be zero, ignored */ ++/* FILE_SYNCHRONOUS_IO_NONALERT cpu_to_le32(0x00000020) should be zero, ignored */ + #define FILE_NON_DIRECTORY_FILE_LE cpu_to_le32(0x00000040) + #define FILE_COMPLETE_IF_OPLOCKED_LE cpu_to_le32(0x00000100) + #define FILE_NO_EA_KNOWLEDGE_LE cpu_to_le32(0x00000200) ++/* FILE_OPEN_REMOTE_INSTANCE cpu_to_le32(0x00000400) should be zero, ignored */ + #define FILE_RANDOM_ACCESS_LE cpu_to_le32(0x00000800) +-#define FILE_DELETE_ON_CLOSE_LE cpu_to_le32(0x00001000) ++#define FILE_DELETE_ON_CLOSE_LE cpu_to_le32(0x00001000) /* MBZ */ + #define FILE_OPEN_BY_FILE_ID_LE cpu_to_le32(0x00002000) + #define FILE_OPEN_FOR_BACKUP_INTENT_LE cpu_to_le32(0x00004000) + #define FILE_NO_COMPRESSION_LE cpu_to_le32(0x00008000) ++/* FILE_OPEN_REQUIRING_OPLOCK cpu_to_le32(0x00010000) should be zero, ignored */ ++/* FILE_DISALLOW_EXCLUSIVE cpu_to_le32(0x00020000) should be zero, ignored */ ++/* FILE_RESERVE_OPFILTER cpu_to_le32(0x00100000) MBZ */ + #define FILE_OPEN_REPARSE_POINT_LE cpu_to_le32(0x00200000) + #define FILE_OPEN_NO_RECALL_LE cpu_to_le32(0x00400000) ++/* #define FILE_OPEN_FOR_FREE_SPACE_QUERY cpu_to_le32(0x00800000) should be zero, ignored */ + #define CREATE_OPTIONS_MASK_LE cpu_to_le32(0x00FFFFFF) + + #define FILE_READ_RIGHTS_LE (FILE_READ_DATA_LE | FILE_READ_EA_LE \ +-- +2.43.0 + diff --git a/queue-6.6/smb3-improve-exception-handling-in-allocate_mr_list.patch b/queue-6.6/smb3-improve-exception-handling-in-allocate_mr_list.patch new file mode 100644 index 0000000000..8530f7fb64 --- /dev/null +++ b/queue-6.6/smb3-improve-exception-handling-in-allocate_mr_list.patch @@ -0,0 +1,48 @@ +From 681fd44c7ffaa056239439f513b37a6bb3b64b54 Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Fri, 29 Dec 2023 20:43:12 +0100 +Subject: smb3: Improve exception handling in allocate_mr_list() + +From: Markus Elfring <elfring@users.sourceforge.net> + +[ Upstream commit 96d566b6c933be96e9f5b216f04024ab522e0465 ] + +The kfree() function was called in one case by +the allocate_mr_list() function during error handling +even if the passed variable contained a null pointer. +This issue was detected by using the Coccinelle software. + +Thus use another label. + +Signed-off-by: Markus Elfring <elfring@users.sourceforge.net> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/client/smbdirect.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/fs/smb/client/smbdirect.c b/fs/smb/client/smbdirect.c +index 94df9eec3d8d1..d74e829de51c2 100644 +--- a/fs/smb/client/smbdirect.c ++++ b/fs/smb/client/smbdirect.c +@@ -2136,7 +2136,7 @@ static int allocate_mr_list(struct smbd_connection *info) + for (i = 0; i < info->responder_resources * 2; i++) { + smbdirect_mr = kzalloc(sizeof(*smbdirect_mr), GFP_KERNEL); + if (!smbdirect_mr) +- goto out; ++ goto cleanup_entries; + smbdirect_mr->mr = ib_alloc_mr(info->pd, info->mr_type, + info->max_frmr_depth); + if (IS_ERR(smbdirect_mr->mr)) { +@@ -2162,7 +2162,7 @@ static int allocate_mr_list(struct smbd_connection *info) + + out: + kfree(smbdirect_mr); +- ++cleanup_entries: + list_for_each_entry_safe(smbdirect_mr, tmp, &info->mr_list, list) { + list_del(&smbdirect_mr->list); + ib_dereg_mr(smbdirect_mr->mr); +-- +2.43.0 + diff --git a/queue-6.6/smb3-minor-cleanup-of-session-handling-code.patch b/queue-6.6/smb3-minor-cleanup-of-session-handling-code.patch new file mode 100644 index 0000000000..54af60d293 --- /dev/null +++ b/queue-6.6/smb3-minor-cleanup-of-session-handling-code.patch @@ -0,0 +1,106 @@ +From be3a3d1ca2bee4e7e29a3ce103c84faa10e2cc55 Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Mon, 6 Nov 2023 15:37:03 -0600 +Subject: smb3: minor cleanup of session handling code + +From: Steve French <stfrench@microsoft.com> + +[ Upstream commit f72d96507640835726d4f5ba26c1c11acbe1bc97 ] + +Minor cleanup of style issues found by checkpatch + +Reviewed-by: Bharath SM <bharathsm@microsoft.com> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/client/sess.c | 18 ++++++++++++------ + 1 file changed, 12 insertions(+), 6 deletions(-) + +diff --git a/fs/smb/client/sess.c b/fs/smb/client/sess.c +index 70a53dde83eec..09bb30610a901 100644 +--- a/fs/smb/client/sess.c ++++ b/fs/smb/client/sess.c +@@ -108,6 +108,7 @@ cifs_chan_clear_in_reconnect(struct cifs_ses *ses, + struct TCP_Server_Info *server) + { + unsigned int chan_index = cifs_ses_get_chan_index(ses, server); ++ + if (chan_index == CIFS_INVAL_CHAN_INDEX) + return; + +@@ -119,6 +120,7 @@ cifs_chan_in_reconnect(struct cifs_ses *ses, + struct TCP_Server_Info *server) + { + unsigned int chan_index = cifs_ses_get_chan_index(ses, server); ++ + if (chan_index == CIFS_INVAL_CHAN_INDEX) + return true; /* err on the safer side */ + +@@ -130,6 +132,7 @@ cifs_chan_set_need_reconnect(struct cifs_ses *ses, + struct TCP_Server_Info *server) + { + unsigned int chan_index = cifs_ses_get_chan_index(ses, server); ++ + if (chan_index == CIFS_INVAL_CHAN_INDEX) + return; + +@@ -143,6 +146,7 @@ cifs_chan_clear_need_reconnect(struct cifs_ses *ses, + struct TCP_Server_Info *server) + { + unsigned int chan_index = cifs_ses_get_chan_index(ses, server); ++ + if (chan_index == CIFS_INVAL_CHAN_INDEX) + return; + +@@ -156,6 +160,7 @@ cifs_chan_needs_reconnect(struct cifs_ses *ses, + struct TCP_Server_Info *server) + { + unsigned int chan_index = cifs_ses_get_chan_index(ses, server); ++ + if (chan_index == CIFS_INVAL_CHAN_INDEX) + return true; /* err on the safer side */ + +@@ -167,6 +172,7 @@ cifs_chan_is_iface_active(struct cifs_ses *ses, + struct TCP_Server_Info *server) + { + unsigned int chan_index = cifs_ses_get_chan_index(ses, server); ++ + if (chan_index == CIFS_INVAL_CHAN_INDEX) + return true; /* err on the safer side */ + +@@ -677,8 +683,7 @@ static __u32 cifs_ssetup_hdr(struct cifs_ses *ses, + + /* Now no need to set SMBFLG_CASELESS or obsolete CANONICAL PATH */ + +- /* BB verify whether signing required on neg or just on auth frame +- (and NTLM case) */ ++ /* BB verify whether signing required on neg or just auth frame (and NTLM case) */ + + capabilities = CAP_LARGE_FILES | CAP_NT_SMBS | CAP_LEVEL_II_OPLOCKS | + CAP_LARGE_WRITE_X | CAP_LARGE_READ_X; +@@ -735,8 +740,10 @@ static void unicode_domain_string(char **pbcc_area, struct cifs_ses *ses, + + /* copy domain */ + if (ses->domainName == NULL) { +- /* Sending null domain better than using a bogus domain name (as +- we did briefly in 2.6.18) since server will use its default */ ++ /* ++ * Sending null domain better than using a bogus domain name (as ++ * we did briefly in 2.6.18) since server will use its default ++ */ + *bcc_ptr = 0; + *(bcc_ptr+1) = 0; + bytes_ret = 0; +@@ -755,8 +762,7 @@ static void unicode_ssetup_strings(char **pbcc_area, struct cifs_ses *ses, + char *bcc_ptr = *pbcc_area; + int bytes_ret = 0; + +- /* BB FIXME add check that strings total less +- than 335 or will need to send them as arrays */ ++ /* BB FIXME add check that strings less than 335 or will need to send as arrays */ + + /* copy user */ + if (ses->user_name == NULL) { +-- +2.43.0 + diff --git a/queue-6.6/smb3-minor-rdma-cleanup.patch b/queue-6.6/smb3-minor-rdma-cleanup.patch new file mode 100644 index 0000000000..e4d9ffde66 --- /dev/null +++ b/queue-6.6/smb3-minor-rdma-cleanup.patch @@ -0,0 +1,43 @@ +From 30a5023bb14154df6fd059349d31218c13d2a3d4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Mon, 6 Nov 2023 13:31:45 -0600 +Subject: smb3: minor RDMA cleanup + +From: Steve French <stfrench@microsoft.com> + +[ Upstream commit 43960dc2328e554c4c61b22c47e77e8b1c48d854 ] + +Some minor smbdirect debug cleanup spotted by checkpatch + +Cc: Long Li <longli@microsoft.com> +Reviewed-by: Bharath SM <bharathsm@microsoft.com> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/client/cifs_debug.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/fs/smb/client/cifs_debug.c b/fs/smb/client/cifs_debug.c +index 058e703107fc7..aa95fa95ca112 100644 +--- a/fs/smb/client/cifs_debug.c ++++ b/fs/smb/client/cifs_debug.c +@@ -779,14 +779,14 @@ static ssize_t name##_write(struct file *file, const char __user *buffer, \ + size_t count, loff_t *ppos) \ + { \ + int rc; \ +- rc = kstrtoint_from_user(buffer, count, 10, & name); \ ++ rc = kstrtoint_from_user(buffer, count, 10, &name); \ + if (rc) \ + return rc; \ + return count; \ + } \ + static int name##_proc_show(struct seq_file *m, void *v) \ + { \ +- seq_printf(m, "%d\n", name ); \ ++ seq_printf(m, "%d\n", name); \ + return 0; \ + } \ + static int name##_open(struct inode *inode, struct file *file) \ +-- +2.43.0 + diff --git a/queue-6.6/smb3-more-minor-cleanups-for-session-handling-routin.patch b/queue-6.6/smb3-more-minor-cleanups-for-session-handling-routin.patch new file mode 100644 index 0000000000..bb8a7c631e --- /dev/null +++ b/queue-6.6/smb3-more-minor-cleanups-for-session-handling-routin.patch @@ -0,0 +1,71 @@ +From bf12a83b40ec4083af0c0f3f910752fe4312fd2e Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Mon, 6 Nov 2023 22:40:38 -0600 +Subject: smb3: more minor cleanups for session handling routines + +From: Steve French <stfrench@microsoft.com> + +[ Upstream commit 1bc081b67a79b6e75fae686e98048cea1038ae31 ] + +Some trivial cleanup pointed out by checkpatch + +Reviewed-by: Bharath SM <bharathsm@microsoft.com> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/client/sess.c | 25 +++++++++++++++---------- + 1 file changed, 15 insertions(+), 10 deletions(-) + +diff --git a/fs/smb/client/sess.c b/fs/smb/client/sess.c +index bd4dcd1a9af83..70a53dde83eec 100644 +--- a/fs/smb/client/sess.c ++++ b/fs/smb/client/sess.c +@@ -801,8 +801,7 @@ static void ascii_ssetup_strings(char **pbcc_area, struct cifs_ses *ses, + if (WARN_ON_ONCE(len < 0)) + len = CIFS_MAX_DOMAINNAME_LEN - 1; + bcc_ptr += len; +- } /* else we will send a null domain name +- so the server will default to its own domain */ ++ } /* else we send a null domain name so server will default to its own domain */ + *bcc_ptr = 0; + bcc_ptr++; + +@@ -898,11 +897,14 @@ static void decode_ascii_ssetup(char **pbcc_area, __u16 bleft, + if (len > bleft) + return; + +- /* No domain field in LANMAN case. Domain is +- returned by old servers in the SMB negprot response */ +- /* BB For newer servers which do not support Unicode, +- but thus do return domain here we could add parsing +- for it later, but it is not very important */ ++ /* ++ * No domain field in LANMAN case. Domain is ++ * returned by old servers in the SMB negprot response ++ * ++ * BB For newer servers which do not support Unicode, ++ * but thus do return domain here, we could add parsing ++ * for it later, but it is not very important ++ */ + cifs_dbg(FYI, "ascii: bytes left %d\n", bleft); + } + #endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ +@@ -958,9 +960,12 @@ int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len, + ses->ntlmssp->server_flags = server_flags; + + memcpy(ses->ntlmssp->cryptkey, pblob->Challenge, CIFS_CRYPTO_KEY_SIZE); +- /* In particular we can examine sign flags */ +- /* BB spec says that if AvId field of MsvAvTimestamp is populated then +- we must set the MIC field of the AUTHENTICATE_MESSAGE */ ++ /* ++ * In particular we can examine sign flags ++ * ++ * BB spec says that if AvId field of MsvAvTimestamp is populated then ++ * we must set the MIC field of the AUTHENTICATE_MESSAGE ++ */ + + tioffset = le32_to_cpu(pblob->TargetInfoArray.BufferOffset); + tilen = le16_to_cpu(pblob->TargetInfoArray.Length); +-- +2.43.0 + diff --git a/queue-6.6/smb3-update-allocation-size-more-accurately-on-write.patch b/queue-6.6/smb3-update-allocation-size-more-accurately-on-write.patch new file mode 100644 index 0000000000..0139e5ea81 --- /dev/null +++ b/queue-6.6/smb3-update-allocation-size-more-accurately-on-write.patch @@ -0,0 +1,48 @@ +From fc5d8dd520420aa09d24b69ad279a23ffe0a34c0 Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Thu, 22 Feb 2024 00:26:52 -0600 +Subject: smb3: update allocation size more accurately on write completion + +From: Steve French <stfrench@microsoft.com> + +[ Upstream commit dbfdff402d89854126658376cbcb08363194d3cd ] + +Changes to allocation size are approximated for extending writes of cached +files until the server returns the actual value (on SMB3 close or query info +for example), but it was setting the estimated value for number of blocks +to larger than the file size even if the file is likely sparse which +breaks various xfstests (e.g. generic/129, 130, 221, 228). + +When i_size and i_blocks are updated in write completion do not increase +allocation size more than what was written (rounded up to 512 bytes). + +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/client/file.c | 9 ++++++++- + 1 file changed, 8 insertions(+), 1 deletion(-) + +diff --git a/fs/smb/client/file.c b/fs/smb/client/file.c +index 6d44991e1ccdc..751ae89cefe36 100644 +--- a/fs/smb/client/file.c ++++ b/fs/smb/client/file.c +@@ -3204,8 +3204,15 @@ static int cifs_write_end(struct file *file, struct address_space *mapping, + if (rc > 0) { + spin_lock(&inode->i_lock); + if (pos > inode->i_size) { ++ loff_t additional_blocks = (512 - 1 + copied) >> 9; ++ + i_size_write(inode, pos); +- inode->i_blocks = (512 - 1 + pos) >> 9; ++ /* ++ * Estimate new allocation size based on the amount written. ++ * This will be updated from server on close (and on queryinfo) ++ */ ++ inode->i_blocks = min_t(blkcnt_t, (512 - 1 + pos) >> 9, ++ inode->i_blocks + additional_blocks); + } + spin_unlock(&inode->i_lock); + } +-- +2.43.0 + diff --git a/queue-6.6/smb311-additional-compression-flag-defined-in-update.patch b/queue-6.6/smb311-additional-compression-flag-defined-in-update.patch new file mode 100644 index 0000000000..aba367075d --- /dev/null +++ b/queue-6.6/smb311-additional-compression-flag-defined-in-update.patch @@ -0,0 +1,56 @@ +From 871f6c51097fd69b725ab79ed9043acb7c8d31a0 Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Tue, 19 Mar 2024 17:00:01 -0500 +Subject: smb311: additional compression flag defined in updated protocol spec + +From: Steve French <stfrench@microsoft.com> + +[ Upstream commit e56bc745fa1de77abc2ad8debc4b1b83e0426c49 ] + +Added new compression flag that was recently documented, in +addition fix some typos and clarify the sid_attr_data struct +definition. + +Reviewed-by: Bharath SM <bharathsm@microsoft.com> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/common/smb2pdu.h | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/fs/smb/common/smb2pdu.h b/fs/smb/common/smb2pdu.h +index 735614d233a06..202ff91281560 100644 +--- a/fs/smb/common/smb2pdu.h ++++ b/fs/smb/common/smb2pdu.h +@@ -280,15 +280,16 @@ struct smb3_blob_data { + #define SE_GROUP_RESOURCE 0x20000000 + #define SE_GROUP_LOGON_ID 0xC0000000 + +-/* struct sid_attr_data is SidData array in BlobData format then le32 Attr */ +- + struct sid_array_data { + __le16 SidAttrCount; + /* SidAttrList - array of sid_attr_data structs */ + } __packed; + +-struct luid_attr_data { +- ++/* struct sid_attr_data is SidData array in BlobData format then le32 Attr */ ++struct sid_attr_data { ++ __le16 BlobSize; ++ __u8 BlobData[]; ++ /* __le32 Attr */ + } __packed; + + /* +@@ -502,6 +503,7 @@ struct smb2_encryption_neg_context { + #define SMB3_COMPRESS_LZ77_HUFF cpu_to_le16(0x0003) + /* Pattern scanning algorithm See MS-SMB2 3.1.4.4.1 */ + #define SMB3_COMPRESS_PATTERN cpu_to_le16(0x0004) /* Pattern_V1 */ ++#define SMB3_COMPRESS_LZ4 cpu_to_le16(0x0005) + + /* Compression Flags */ + #define SMB2_COMPRESSION_CAPABILITIES_FLAG_NONE cpu_to_le32(0x00000000) +-- +2.43.0 + diff --git a/queue-6.6/smb311-correct-incorrect-offset-field-in-compression.patch b/queue-6.6/smb311-correct-incorrect-offset-field-in-compression.patch new file mode 100644 index 0000000000..9b70fc12d2 --- /dev/null +++ b/queue-6.6/smb311-correct-incorrect-offset-field-in-compression.patch @@ -0,0 +1,35 @@ +From 4efd28063a87b005c34d3a26603363d13979330f Mon Sep 17 00:00:00 2001 +From: Sasha Levin <sashal@kernel.org> +Date: Tue, 19 Mar 2024 15:59:38 -0500 +Subject: smb311: correct incorrect offset field in compression header + +From: Steve French <stfrench@microsoft.com> + +[ Upstream commit 68c5818a27afcb5cdddab041b82e9d47c996cb6a ] + +The offset field in the compression header is 32 bits not 16. + +Reviewed-by: Bharath SM <bharathsm@microsoft.com> +Reported-by: Enzo Matsumiya <ematsumiya@suse.de> +Signed-off-by: Steve French <stfrench@microsoft.com> +Signed-off-by: Sasha Levin <sashal@kernel.org> +--- + fs/smb/common/smb2pdu.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/fs/smb/common/smb2pdu.h b/fs/smb/common/smb2pdu.h +index 61c6e72ccddc4..735614d233a06 100644 +--- a/fs/smb/common/smb2pdu.h ++++ b/fs/smb/common/smb2pdu.h +@@ -227,7 +227,7 @@ struct smb2_compression_hdr { + __le32 OriginalCompressedSegmentSize; + __le16 CompressionAlgorithm; + __le16 Flags; +- __le16 Offset; /* this is the size of the uncompressed SMB2 header below */ ++ __le32 Offset; /* this is the size of the uncompressed SMB2 header below */ + /* uncompressed SMB2 header (READ or WRITE) goes here */ + /* compressed data goes here */ + } __packed; +-- +2.43.0 + |