cifs: remove rfc1002 length field from all SMB2 response structures
Subsystems (2)
cover cifs: remove rfc1002 length field from all SMB2 response structures
Steve, All
Review but do not merge to for-next. This patch still breaks SMBD. I have tested the patch with SMB1/2/3 with and without signing as well as with SMB3 encryption and it works in my tests.
The first patch removes the rfc1002 length from all the response structures hopefully making it easier to implement compounding later. It also gets rid of a lot of magic add/subtract 4 from various computations.
The second patch updates the demultiplex loop so that it will now be able to handle compounded responses we receive from the server. In theory, we will not know it this works until we can actually send compounded requests.
Next steps will be to start implementing the actual compounding. That, I think, will consist of refactoring the SMB2_open/query/close functions into dedicated marshalling functions and then create a new compound_send_recv() function.
The second part will require some additional plumbing.
In smb2_send_recv() we currently create iovectors as
[0] length
[1] smb2 header
[2+] additional data
or, when smb3 encryption is used
[0] length
[1] transform header
[2] smb2 header
[3+] additional data
Instead, in compound_send_recv() we will need to create an array of iovectors, one for each pdu, that would look like
[0] smb2 header
[1+] additional data
and similar for smb3 encryption.
These iovectors would then be propagated down the stack without an initial length field until we are ready to transmit them.
So, for the TCP case, we would not prepend the vectors we want to send with a [0] length field until the point where we are just about to start writing the data to the tcp socket.
That shouldnt be too hard. But we need these patches first.
A message that is not in the archive (sent off-list or not yet received): 20180121220644.7907-1-lsahlber-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org
PATCH 1/2 cifs: remove rfc1002 header from all SMB2 response structures
Separate out all the 4 byte rfc1002 headers so that they are no longer part of the SMB2 header structures to prepare for future work to add compounding support.
--- fs/cifs/cifsglob.h | 2 + fs/cifs/cifssmb.c | 9 ++- fs/cifs/connect.c | 32 ++++++--- fs/cifs/misc.c | 2 +- fs/cifs/smb1ops.c | 1 + fs/cifs/smb2glob.h | 5 -- fs/cifs/smb2maperror.c | 8 ++- fs/cifs/smb2misc.c | 111 +++++++++++++++--------------- fs/cifs/smb2ops.c | 177 ++++++++++++++++++++++++++++++------------------ fs/cifs/smb2pdu.c | 105 ++++++++++++++-------------- fs/cifs/smb2pdu.h | 65 ++++++------------ fs/cifs/smb2proto.h | 5 +- fs/cifs/smb2transport.c | 6 +- fs/cifs/transport.c | 4 +- 14 files changed, 283 insertions(+), 249 deletions(-)
fs/cifs/cifsglob.h +2 -0
@@ -460,6 +460,7 @@ struct smb_version_values {__u32 exclusive_lock_type;__u32 shared_lock_type;__u32 unlock_lock_type; +size_t header_preamble_size;size_t header_size;size_t max_header_size;size_t read_rsp_size;
@@ -656,6 +657,7 @@ struct TCP_Server_Info {struct delayed_work echo;/* echo ping workqueue job */ char *smallbuf;/* pointer to current "small" buffer */ char *bigbuf;/* pointer to current "big" buffer */ +unsigned int total_size;/* Total size of this PDU */ unsigned int total_read;/* total amount of data read in this pass */ #ifdef CONFIG_CIFS_FSCACHE struct fscache_cookie *fscache;/* client index cache cookie */
fs/cifs/cifssmb.c +6 -3
@@ -1453,7 +1453,9 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)unsigned int data_offset, data_len;struct cifs_readdata *rdata = mid->callback_data;char *buf = server->smallbuf; -unsigned int buflen =get_rfc1002_length (buf) +4 ; +unsigned int buflen; + + buflen = server->total_size + server->vals->header_preamble_size;cifs_dbg (FYI ,"%s: mid=%llu offset=%llu bytes=%u\n" , __func__, mid->mid, rdata->offset, rdata->bytes);
@@ -1464,7 +1466,7 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)* the Mid. */ len = min_t(unsigned int, buflen, server->vals->read_rsp_size) - -HEADER_SIZE (server) +1 ; +HEADER_SIZE (server) +1 ; length =cifs_read_from_socket (server, buf +HEADER_SIZE (server) -1 , len);
@@ -1502,7 +1504,8 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)return cifs_readv_discard (server, mid); } - data_offset = server->ops->read_data_offset (buf) +4 ; + data_offset = server->ops->read_data_offset (buf) + + server->vals->header_preamble_size;if (data_offset < server->total_read) {/* * win2k8 sometimes sends an offset of 0 when the read
fs/cifs/connect.c +22 -10
@@ -763,10 +763,11 @@ standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid) {int length;char *buf = server->smallbuf; -unsigned int pdu_length =get_rfc1002_length (buf); +unsigned int pdu_length = server->total_size;/* make sure this will fit in a large buffer */ -if (pdu_length > CIFSMaxBufSize +MAX_HEADER_SIZE (server) -4 ) { +if (pdu_length > CIFSMaxBufSize +MAX_HEADER_SIZE (server) - + server->vals->header_preamble_size) {cifs_dbg (VFS ,"SMB response too long (%u bytes)\n" , pdu_length);cifs_reconnect (server);wake_up (&server->response_q);
@@ -781,8 +782,11 @@ standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid) }/* now read the rest */ - length =cifs_read_from_socket (server, buf +HEADER_SIZE (server) -1 , - pdu_length -HEADER_SIZE (server) +1 +4 ); + length =cifs_read_from_socket (server, + buf +HEADER_SIZE (server) -1 , + pdu_length -HEADER_SIZE (server) +1 + + server->vals->header_preamble_size); +if (length <0 )return length; server->total_read += length;
@@ -862,20 +866,26 @@ cifs_demultiplex_thread(void *p) length =cifs_read_from_socket (server, buf, pdu_length);if (length <0 )continue ; - server->total_read = length; + +if (server->vals->header_preamble_size ==0 ) + server->total_read =0 ; +else + server->total_read = length;/* * The right amount was read from socket - 4 bytes, * so we can now interpret the length field. */ pdu_length =get_rfc1002_length (buf); + server->total_size = pdu_length;cifs_dbg (FYI ,"RFC1002 header 0x%x\n" , pdu_length);if (!is_smb_response (server, buf[0 ]))continue ;/* make sure we have enough to get to the MID */ -if (pdu_length <HEADER_SIZE (server) -1 -4 ) { +if (pdu_length <HEADER_SIZE (server) -1 - + server->vals->header_preamble_size) {cifs_dbg (VFS ,"SMB response too short (%u bytes)\n" , pdu_length);cifs_reconnect (server);
@@ -884,18 +894,20 @@ cifs_demultiplex_thread(void *p) }/* read down to the MID */ - length =cifs_read_from_socket (server, buf +4 , -HEADER_SIZE (server) -1 -4 ); + length =cifs_read_from_socket (server, + buf + server->vals->header_preamble_size, +HEADER_SIZE (server) -1 + - server->vals->header_preamble_size);if (length <0 )continue ; server->total_read += length;if (server->ops->is_transform_hdr && server->ops->receive_transform && - server->ops->is_transform_hdr (buf)) { + server->ops->is_transform_hdr (buf)) length = server->ops->receive_transform (server, &mid_entry); - }else { +else { mid_entry = server->ops->find_mid (server, buf);if (!mid_entry || !mid_entry->receive)
fs/cifs/misc.c +1 -1
@@ -151,7 +151,7 @@ cifs_buf_get(void)* SMB2 header is bigger than CIFS one - no problems to clean some * more bytes for CIFS. */ -size_t buf_size =sizeof (struct smb2_hdr ); +size_t buf_size =sizeof (struct smb2_sync_hdr );/* * We could use negotiated size instead of max_msgsize -
fs/cifs/smb1ops.c +1 -0
@@ -1120,6 +1120,7 @@ struct smb_version_values smb1_values = { .exclusive_lock_type = 0, .shared_lock_type = LOCKING_ANDX_SHARED_LOCK, .unlock_lock_type = 0, + .header_preamble_size = 4, .header_size = sizeof(struct smb_hdr), .max_header_size = MAX_CIFS_HDR_SIZE, .read_rsp_size = sizeof(READ_RSP),
fs/cifs/smb2glob.h +0 -5
@@ -61,9 +61,4 @@/* Maximum buffer size value we can send with 1 credit */ #define SMB2_MAX_BUFFER_SIZE 65536 -static inline struct smb2_sync_hdr *get_sync_hdr (void *buf) -{ -return &(((struct smb2_hdr *)buf)->sync_hdr); -} -#endif /* _SMB2_GLOB_H */
fs/cifs/smb2maperror.c +6 -2
@@ -2450,10 +2450,14 @@ smb2_print_status(__le32 status)int map_smb2_to_linux_error (char *buf,bool log_err) { -struct smb2_sync_hdr *shdr =get_sync_hdr (buf); +struct smb2_sync_hdr *shdr;unsigned int i;int rc = -EIO ; -__le32 smb2err = shdr->Status; +__le32 smb2err; + + shdr = (struct smb2_sync_hdr *)buf; + + smb2err = shdr->Status;if (smb2err ==0 )return 0 ;
fs/cifs/smb2misc.c +56 -55
247 lines not on this page · raw message
fs/cifs/smb2ops.c +112 -65
448 lines not on this page · raw message
fs/cifs/smb2pdu.c +49 -56
296 lines not on this page · raw message
fs/cifs/smb2pdu.h +19 -46
189 lines not on this page · raw message
fs/cifs/smb2proto.h +3 -2
@@ -37,7 +37,8 @@ extern int map_smb2_to_linux_error(char *buf, bool log_err);extern int smb2_check_message (char *buf,unsigned int length,struct TCP_Server_Info *server);extern unsigned int smb2_calc_size (void *buf); -extern char *smb2_get_data_area_len (int *off,int *len,struct smb2_hdr *hdr); +extern char *smb2_get_data_area_len (int *off,int *len, +struct smb2_sync_hdr *shdr);extern __le16 *cifs_convert_path_to_utf16 (const char *from,struct cifs_sb_info *cifs_sb);
@@ -122,7 +123,7 @@ extern int SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon);extern int SMB2_open (const unsigned int xid,struct cifs_open_parms *oparms,__le16 *path,__u8 *oplock,struct smb2_file_all_info *buf, -struct smb2_err_rsp **err_buf); +struct kvec *err_iov); extern int SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, u64 volatile_fid, u32 opcode, bool is_fsctl, bool use_ipc,
fs/cifs/smb2transport.c +3 -3
@@ -624,10 +624,10 @@ smb2_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server,struct smb_rqst rqst = { .rq_iov = iov, .rq_nvec =2 }; - iov[0 ].iov_base = (char *)mid->resp_buf; + iov[0 ].iov_base =NULL ; iov[0 ].iov_len =4 ; - iov[1 ].iov_base = (char *)mid->resp_buf +4 ; - iov[1 ].iov_len = len; + iov[1 ].iov_base = (char *)mid->resp_buf; + iov[1 ].iov_len = server->total_size;dump_smb (mid->resp_buf,min_t (u32,80 , len));/* convert the length into a more usable form */
fs/cifs/transport.c +3 -1
@@ -779,7 +779,9 @@ cifs_send_recv(const unsigned int xid, struct cifs_ses *ses, buf = (char *)midQ->resp_buf; resp_iov->iov_base = buf; - resp_iov->iov_len =get_rfc1002_length (buf) +4 ; + resp_iov->iov_len = ses->server->total_size + + ses->server->vals->header_preamble_size; +if (midQ->large_buf) *resp_buf_type =CIFS_LARGE_BUFFER ;else
Signature · 1 line
2.13.3
PATCH 2/2 cifs: update multiplex loop to handle compounded responses
In reply to a message not in the archive--- fs/cifs/cifsglob.h | 2 +- fs/cifs/connect.c | 18 +++++++++++++++--- fs/cifs/smb2ops.c | 26 ++++++++++++++++++++++++-- 3 files changed, 40 insertions(+), 6 deletions(-)
fs/cifs/cifsglob.h +1 -1
@@ -449,7 +449,7 @@ struct smb_version_operations { struct mid_q_entry **);enum securityEnum (*select_sectype)(struct TCP_Server_Info *,enum securityEnum ); - +int (*next_header)(char *); };struct smb_version_values {
fs/cifs/connect.c +15 -3
@@ -840,6 +840,7 @@ cifs_demultiplex_thread(void *p)int length;struct TCP_Server_Info *server = p;unsigned int pdu_length; +unsigned int next_offset;char *buf =NULL ;struct task_struct *task_to_wake =NULL ;struct mid_q_entry *mid_entry;
@@ -877,17 +878,18 @@ cifs_demultiplex_thread(void *p)* so we can now interpret the length field. */ pdu_length =get_rfc1002_length (buf); +next_pdu: server->total_size = pdu_length; -cifs_dbg (FYI ,"RFC1002 header 0x%x\n" , pdu_length); +cifs_dbg (FYI ,"RFC1002 header 0x%x\n" , server->total_size);if (!is_smb_response (server, buf[0 ]))continue ;/* make sure we have enough to get to the MID */ -if (pdu_length <HEADER_SIZE (server) -1 - +if (server->total_size <HEADER_SIZE (server) -1 - server->vals->header_preamble_size) {cifs_dbg (VFS ,"SMB response too short (%u bytes)\n" , - pdu_length); + server->total_size);cifs_reconnect (server);wake_up (&server->response_q);continue ;
@@ -902,6 +904,12 @@ cifs_demultiplex_thread(void *p)continue ; server->total_read += length; +if (server->ops->next_header) { + next_offset = server->ops->next_header (buf); +if (next_offset) + server->total_size = next_offset; + } +if (server->ops->is_transform_hdr && server->ops->receive_transform && server->ops->is_transform_hdr (buf))
@@ -947,6 +955,10 @@ cifs_demultiplex_thread(void *p)cifs_dump_mids (server);#endif /* CIFS_DEBUG2 */ +if (pdu_length > server->total_size) { + pdu_length -= server->total_size; + goto next_pdu; + } } }/* end while !EXITING */
fs/cifs/smb2ops.c +24 -2
@@ -125,8 +125,8 @@ smb2_get_credits(struct mid_q_entry *mid)char *buf = mid->resp_buf;struct smb2_sync_hdr *shdr; -if ( *(__u32 *)buf ==SMB2_PROTO_NUMBER || - *(__u32 *)buf ==SMB2_TRANSFORM_PROTO_NUM ) +if ( *(__le32 *)buf ==SMB2_PROTO_NUMBER || + *(__le32 *)buf ==SMB2_TRANSFORM_PROTO_NUM ) shdr = (struct smb2_sync_hdr *)buf;else shdr = (struct smb2_sync_hdr *)(buf +4 );
@@ -2718,6 +2718,24 @@ smb3_handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid) NULL, 0, 0); } +static int +smb2_next_header (char *buf) +{ +struct smb2_sync_hdr *hdr = (struct smb2_sync_hdr *)buf; +struct smb2_transform_hdr *t_hdr = (struct smb2_transform_hdr *)buf; + +/* FIXME: compounding. +* How does compounding work with smb3 encryption? +* For now, assume that we will have one transform header for each +* smb2 header (and assume that smb2->NextOffset is 0 for all of them.) +*/ +if (hdr->ProtocolId ==SMB2_TRANSFORM_PROTO_NUM ) +return sizeof (struct smb2_transform_hdr ) + +le32_to_cpu (t_hdr->OriginalMessageSize); + +return le32_to_cpu (hdr->NextCommand); +} +struct smb_version_operations smb20_operations = { .compare_fids = smb2_compare_fids, .setup_request = smb2_setup_request,
@@ -2809,6 +2827,7 @@ struct smb_version_operations smb20_operations = { .get_acl_by_fid = get_smb2_acl_by_fid, .set_acl = set_smb2_acl,#endif /* CIFS_ACL */ + .next_header = smb2_next_header, };struct smb_version_operations smb21_operations = {
@@ -2903,6 +2922,7 @@ struct smb_version_operations smb21_operations = { .get_acl_by_fid = get_smb2_acl_by_fid, .set_acl = set_smb2_acl,#endif /* CIFS_ACL */ + .next_header = smb2_next_header, };struct smb_version_operations smb30_operations = {
@@ -3007,6 +3027,7 @@ struct smb_version_operations smb30_operations = { .get_acl_by_fid = get_smb2_acl_by_fid, .set_acl = set_smb2_acl,#endif /* CIFS_ACL */ + .next_header = smb2_next_header, };#ifdef CONFIG_CIFS_SMB311
@@ -3107,6 +3128,7 @@ struct smb_version_operations smb311_operations = { .query_all_EAs = smb2_query_eas, .set_EA = smb2_set_ea,#endif /* CIFS_XATTR */ + .next_header = smb2_next_header, };#endif /* CIFS_SMB311 */
Signature · 1 line
2.13.3
Hi Ronnie,
These are dense patches and even though I have an idea of what it is supposed to do I'm not familiar enough with the data structures and their purpose to evaluate all of this right now (well I've put it aside for some time).
Could you guide us throught the added struct members and maybe tell us in which order we should start reading the changes to make this easier to grok?
Cheers,
Signature · 4 lines
Aurélien Aptel / SUSE Labs Samba Team GPG: 1839 CB5F 9F5B FB9B AA97 8C99 03C8 A49B 521B D5D3 SUSE Linux GmbH, Maxfeldstraße 5, 90409 Nürnberg, Germany GF: Felix Imendörffer, Jane Smithard, Graham Norton, HRB 21284 (AG Nürnberg)