[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20250806203705.2560493-24-dhowells@redhat.com>
Date: Wed, 6 Aug 2025 21:36:44 +0100
From: David Howells <dhowells@...hat.com>
To: Steve French <sfrench@...ba.org>
Cc: David Howells <dhowells@...hat.com>,
Paulo Alcantara <pc@...guebit.org>,
Shyam Prasad N <sprasad@...rosoft.com>,
Tom Talpey <tom@...pey.com>,
Wang Zhaolong <wangzhaolong@...weicloud.com>,
Stefan Metzmacher <metze@...ba.org>,
Mina Almasry <almasrymina@...gle.com>,
linux-cifs@...r.kernel.org,
linux-kernel@...r.kernel.org,
netfs@...ts.linux.dev,
linux-fsdevel@...r.kernel.org
Subject: [RFC PATCH 23/31] cifs: Add more pieces to smb_message
Add more pieces to the smb_message struct to facilitate future changes.
Also move towards not needing the server pointer in smb_message.
Signed-off-by: David Howells <dhowells@...hat.com>
cc: Steve French <sfrench@...ba.org>
cc: Paulo Alcantara <pc@...guebit.org>
cc: Shyam Prasad N <sprasad@...rosoft.com>
cc: Tom Talpey <tom@...pey.com>
cc: linux-cifs@...r.kernel.org
cc: netfs@...ts.linux.dev
cc: linux-fsdevel@...r.kernel.org
---
fs/smb/client/cifsglob.h | 7 ++
fs/smb/client/cifsproto.h | 15 +++
fs/smb/client/cifstransport.c | 2 +-
fs/smb/client/smb2ops.c | 10 ++
fs/smb/client/smb2pdu.c | 198 ++++++++++++++++++++++++++++++++++
fs/smb/client/smb2proto.h | 1 +
fs/smb/client/transport.c | 28 ++++-
7 files changed, 259 insertions(+), 2 deletions(-)
diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h
index 045a29cedf0e..0cc71f504c68 100644
--- a/fs/smb/client/cifsglob.h
+++ b/fs/smb/client/cifsglob.h
@@ -1774,7 +1774,12 @@ struct smb_message {
/* Request details */
enum smb2_command command_id; /* Command ID */
s16 pre_offset; /* Offset of pre-headers from ->body (negative) */
+ u16 ext_offset; /* Offset of extensions from ->body */
+ u16 latest_record; /* Offset of latest context record (or 0) */
+ u16 offset; /* Running offset during assembly */
+ u16 data_offset; /* Offset of data in message (maybe in ->body) */
unsigned int total_len; /* Total length of from hdr_offset onwards */
+ struct iov_iter iter; /* Data iterator */
/* Response */
void *response; /* Protocol part of response */
u32 response_len; /* Size of response */
@@ -1782,6 +1787,8 @@ struct smb_message {
struct smb_rqst rqst;
int *resp_buf_type;
struct kvec *resp_iov;
+ /* Variable-length request fragment list - must be last! */
+ struct bvecq bvecq; /* List of request frags (passed to socket) */
};
struct close_cancelled_open {
diff --git a/fs/smb/client/cifsproto.h b/fs/smb/client/cifsproto.h
index ccd70a402567..60a0c9b64d98 100644
--- a/fs/smb/client/cifsproto.h
+++ b/fs/smb/client/cifsproto.h
@@ -765,8 +765,23 @@ void smb_see_message(struct smb_message *smb, enum smb_message_trace trace);
void smb_get_message(struct smb_message *smb, enum smb_message_trace trace);
void smb_put_message(struct smb_message *smb, enum smb_message_trace trace);
void smb_put_messages(struct smb_message *smb);
+void smb_clear_request(struct smb_message *smb);
void *cifs_allocate_tx_buf(struct TCP_Server_Info *server, size_t size);
void cifs_free_tx_buf(void *p);
+/*
+ * Add a segment to a message. This should be allocated with
+ * cifs_allocate_tx_buf() so that it can be used with MSG_SPLICE_PAGES.
+ */
+static inline void smb_add_segment_to_tx_buf(struct smb_message *smb,
+ void *key_buf, size_t size)
+{
+ unsigned int nr = smb->bvecq.nr_segs;
+
+ bvec_set_virt(&smb->bvecq.bv[nr], key_buf, size);
+ smb->bvecq.nr_segs = nr++;
+ smb->total_len += size;
+}
+
#endif /* _CIFSPROTO_H */
diff --git a/fs/smb/client/cifstransport.c b/fs/smb/client/cifstransport.c
index 1e2a8839d742..b93dd2be68e1 100644
--- a/fs/smb/client/cifstransport.c
+++ b/fs/smb/client/cifstransport.c
@@ -177,7 +177,7 @@ cifs_check_receive(struct smb_message *smb, struct TCP_Server_Info *server,
}
/* BB special case reconnect tid and uid here? */
- return map_and_check_smb_error(smb, log_error);
+ return map_and_check_smb_error(server, smb, log_error);
}
int
diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
index 1e24489b55e3..9db383ec22e8 100644
--- a/fs/smb/client/smb2ops.c
+++ b/fs/smb/client/smb2ops.c
@@ -2602,6 +2602,16 @@ smb2_set_replay(struct TCP_Server_Info *server, struct smb_rqst *rqst)
shdr->Flags |= SMB2_FLAGS_REPLAY_OPERATION;
}
+void smb2_set_replay_smb(struct TCP_Server_Info *server, struct smb_message *smb)
+{
+ struct smb2_hdr *shdr = smb->request;
+
+ if (server->dialect < SMB30_PROT_ID)
+ return;
+
+ shdr->Flags |= SMB2_FLAGS_REPLAY_OPERATION;
+}
+
void
smb2_set_related(struct smb_rqst *rqst)
{
diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c
index 3009acf0d884..58a2a4ff3368 100644
--- a/fs/smb/client/smb2pdu.c
+++ b/fs/smb/client/smb2pdu.c
@@ -91,6 +91,204 @@ int smb3_encryption_required(const struct cifs_tcon *tcon)
return 0;
}
+static void smb2_enc_header(struct smb_message *smb,
+ const struct cifs_tcon *tcon,
+ struct TCP_Server_Info *server)
+{
+ struct smb2_hdr *shdr = smb->request;
+ struct smb3_hdr_req *smb3_hdr = (struct smb3_hdr_req *)shdr;
+
+ shdr->ProtocolId = SMB2_PROTO_NUMBER;
+ shdr->StructureSize = cpu_to_le16(64);
+ shdr->CreditCharge = 0;
+ shdr->Status = 0; /* ChanSeq for smb3 */
+ shdr->Command = cpu_to_le16(smb->command_id);
+ shdr->CreditRequest = cpu_to_le16(2);
+ shdr->Flags = 0;
+ shdr->NextCommand = 0;
+ shdr->MessageId = 0;
+ shdr->Id.SyncId.ProcessId = cpu_to_le32((__u16)current->tgid);
+ shdr->SessionId = 0;
+
+ if (server) {
+ /* After reconnect SMB3 must set ChannelSequence on subsequent reqs */
+ if (server->dialect >= SMB30_PROT_ID) {
+ /*
+ * if primary channel is not set yet, use default
+ * channel for chan sequence num
+ */
+ if (SERVER_IS_CHAN(server))
+ smb3_hdr->ChannelSequence =
+ cpu_to_le16(server->primary_server->channel_sequence_num);
+ else
+ smb3_hdr->ChannelSequence =
+ cpu_to_le16(server->channel_sequence_num);
+ }
+ spin_lock(&server->req_lock);
+ /* Request up to 10 credits but don't go over the limit. */
+ if (server->credits >= server->max_credits)
+ shdr->CreditRequest = cpu_to_le16(0);
+ else
+ shdr->CreditRequest = cpu_to_le16(
+ min_t(int, server->max_credits -
+ server->credits, 10));
+ spin_unlock(&server->req_lock);
+ }
+
+ if (tcon) {
+ /* GLOBAL_CAP_LARGE_MTU will only be set if dialect > SMB2.02 */
+ /* See sections 2.2.4 and 3.2.4.1.5 of MS-SMB2 */
+ if (server && (server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU))
+ shdr->CreditCharge = cpu_to_le16(1);
+ /* else CreditCharge MBZ */
+
+ shdr->Id.SyncId.TreeId = cpu_to_le32(tcon->tid);
+ /* Uid is not converted */
+ if (tcon->ses)
+ shdr->SessionId = cpu_to_le64(tcon->ses->Suid);
+
+ /*
+ * If we would set SMB2_FLAGS_DFS_OPERATIONS on open we also
+ * would have to pass the path on the Open SMB prefixed by
+ * \\server\share. Not sure when we would need to do the
+ * augmented path (if ever) and setting this flag breaks the
+ * SMB2 open operation since it is illegal to send an empty
+ * path name (without \\server\share prefix) when the DFS flag
+ * is set in the SMB open header. We could consider setting the
+ * flag on all operations other than open but it is safer to
+ * net set it for now.
+ */
+/* if (tcon->share_flags & SHI1005_FLAGS_DFS)
+ shdr->Flags |= SMB2_FLAGS_DFS_OPERATIONS; */
+
+ if (server && server->sign && !smb3_encryption_required(tcon))
+ shdr->Flags |= SMB2_FLAGS_SIGNED;
+ }
+}
+
+/* Flags for smb2_create_request() */
+#define SMB2_REQ_DYNAMIC 0x01 /* Dynamic request */
+#define SMB2_REQ_HEAD 0x02 /* Head of compound */
+#define SMB2_REQ_SENSITIVE 0x04 /* May contain sensitive crypto data */
+
+/*
+ * smb2_create_request: Allocate and set up a request
+ * @command: The command type we're going to issue
+ * @server: The server the command is going to go to
+ * @header_size: The size of the base protocol structure
+ * @protocol_size: The size of the header plus extensions
+ * @data_size: The size of the data payload
+ * @head: If this is the head of a compound
+ * @flags: Mask of SMB2_REQ_* flags
+ *
+ * Create a request and allocate netmem memory to hold the netbios header (if
+ * appropriate) and the protocol part of the message. Memory will also be
+ * allocated for the data part of the message if this is to be encrypted by the
+ * CPU. The allocated buffers will be attached to a bvec-queue struct so that
+ * they can be chained together and passed to the socket.
+ */
+static struct smb_message *smb2_create_request(enum smb2_command command,
+ struct TCP_Server_Info *server,
+ struct cifs_tcon *tcon,
+ size_t header_size,
+ size_t protocol_size,
+ size_t data_size,
+ unsigned int flags)
+{
+ struct smb_message *smb;
+ const size_t max_segs = 3; /* We preallocate 3 segment slots in the message */
+ size_t pre_size;
+ void *body;
+ bool encrypted = false; //, rdma = false;
+ u16 ssize;
+
+ smb = kzalloc(struct_size(smb, bvecq.bv, max_segs), GFP_NOFS);
+ if (!smb)
+ return NULL;
+
+ smb->command_id = command;
+ smb->sensitive = flags & SMB2_REQ_SENSITIVE;
+
+ /* We allocate space for inter-SMB padding or rfc1002 header plus
+ * transform headers (as needed), but don't add them in at this time.
+ */
+ pre_size = 8;
+ if (encrypted)
+ pre_size += sizeof(struct smb2_transform_hdr);
+ smb->pre_offset = -pre_size;
+
+ if (encrypted)
+ /* We want the encrypted blob to be correctly aligned. */
+ pre_size = round_up(pre_size, 16);
+
+ /* Allocate space for the SMB header, the request struct (both in
+ * header_size) plus any extension bits, bearing in mind that some bits
+ * may follow the header directly (header_added_size) and some may have
+ * to be padded to an 8-byte alignment first (extension_size). The
+ * Negotiate Request has both.
+ */
+ smb->ext_offset = header_size;
+ smb->offset = header_size;
+ smb->data_offset = protocol_size;
+ smb->total_len = protocol_size;
+
+ body = cifs_allocate_tx_buf(server, pre_size + protocol_size);
+ if (!body) {
+ kfree(smb);
+ return NULL;
+ }
+
+ smb_add_segment_to_tx_buf(smb, body + pre_size, protocol_size);
+ smb->request = body + pre_size;
+ smb->bvecq.max_segs = max_segs;
+
+ struct smb2_pdu *spdu = body;
+
+ smb2_enc_header(smb, tcon, server);
+ ssize = header_size - sizeof(spdu->hdr);
+ if (flags & SMB2_REQ_DYNAMIC)
+ ssize |= SMB2_STRUCT_HAS_DYNAMIC_PART;
+ spdu->StructureSize2 = cpu_to_le16(ssize);
+
+ if (tcon) {
+ cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_sent[command]);
+ cifs_stats_inc(&tcon->num_smbs_sent);
+ }
+
+ /* Include the buffer from the start of the RFC1002 header in the
+ * iterator, but may need to adjust it later.
+ */
+ iov_iter_bvec_queue(&smb->iter, ITER_SOURCE, &smb->bvecq, 0,
+ pre_size, protocol_size);
+
+ return smb;
+}
+
+static void cifs_pad_to_8(struct smb_message *smb)
+{
+ size_t offset = smb->offset;
+ u8 *p = smb->request;
+
+ while (offset & 7)
+ p[offset++] = 0;
+ smb->offset = offset;
+}
+
+/*
+ * Begin adding an extension to a message. The offset is padded to an 8-byte
+ * alignment;
+ */
+static void *cifs_begin_extension(struct smb_message *smb)
+{
+ cifs_pad_to_8(smb);
+ return smb->request + smb->offset;
+}
+
+static void cifs_end_extension(struct smb_message *smb, size_t size)
+{
+ smb->offset += size;
+}
+
static void
smb2_hdr_assemble(struct smb2_hdr *shdr, enum smb2_command smb2_cmd,
const struct cifs_tcon *tcon,
diff --git a/fs/smb/client/smb2proto.h b/fs/smb/client/smb2proto.h
index 3018b171c6de..22284a52f300 100644
--- a/fs/smb/client/smb2proto.h
+++ b/fs/smb/client/smb2proto.h
@@ -131,6 +131,7 @@ extern void smb2_set_next_command(struct cifs_tcon *tcon,
extern void smb2_set_related(struct smb_rqst *rqst);
extern void smb2_set_replay(struct TCP_Server_Info *server,
struct smb_rqst *rqst);
+void smb2_set_replay_smb(struct TCP_Server_Info *server, struct smb_message *smb);
extern bool smb2_should_replay(struct cifs_tcon *tcon,
int *pretries,
int *pcur_sleep);
diff --git a/fs/smb/client/transport.c b/fs/smb/client/transport.c
index b497bf319a7e..1d732953a90b 100644
--- a/fs/smb/client/transport.c
+++ b/fs/smb/client/transport.c
@@ -93,9 +93,31 @@ static void smb_free_message(struct smb_message *smb)
{
trace_smb3_message(smb->debug_id, refcount_read(&smb->ref),
smb_message_trace_free);
+ cifs_free_tx_buf(smb->request);
mempool_free(smb, &smb_message_pool);
}
+/*
+ * Clear the request parts of a message.
+ */
+void smb_clear_request(struct smb_message *smb)
+{
+ for (; smb; smb = smb->next) {
+ if (smb->request) {
+ if (smb->sensitive) {
+ iov_iter_bvec_queue(&smb->iter, ITER_SOURCE,
+ &smb->bvecq, 3,
+ -smb->pre_offset,
+ smb->ext_offset - -smb->pre_offset);
+ iov_iter_zero(smb->ext_offset - -smb->pre_offset,
+ &smb->iter);
+ }
+ cifs_free_tx_buf(smb->request);
+ smb->request = NULL;
+ }
+ }
+}
+
/*
* Drop a ref on a message. This does not touch the chained messages.
*/
@@ -120,6 +142,8 @@ void smb_put_messages(struct smb_message *smb)
{
struct smb_message *next;
+ smb_clear_request(smb);
+
for (; smb; smb = next) {
unsigned int debug_id = smb->debug_id;
bool dead;
@@ -1099,9 +1123,11 @@ int smb_send_recv_messages(const unsigned int xid, struct cifs_ses *ses,
optype = flags & CIFS_OP_MASK;
/* TODO: Stitch together the messages in a compound. */
+ //u32 last_next = 0;
nr_reqs = 0;
- for (struct smb_message *smb = head_smb; smb; smb = smb->next)
+ for (struct smb_message *smb = head_smb; smb; smb = smb->next) {
nr_reqs++;
+ }
spin_lock(&server->srv_lock);
if (server->tcpStatus == CifsExiting) {
Powered by blists - more mailing lists