Commit 35e2cc1b authored by Paulo Alcantara's avatar Paulo Alcantara Committed by Steve French

cifs: Use correct packet length in SMB2_TRANSFORM header

In smb3_init_transform_rq(), 'orig_len' was only counting the request
length, but forgot to count any data pages in the request.

Writing or creating files with the 'seal' mount option was broken.

In addition, do some code refactoring by exporting smb2_rqst_len() to
calculate the appropriate packet size and avoid duplicating the same
calculation all over the code.

The start of the io vector is either the rfc1002 length (4 bytes) or a
SMB2 header which is always > 4. Use this fact to check and skip the
rfc1002 length if requested.
Signed-off-by: default avatarPaulo Alcantara <palcantara@suse.de>
Reviewed-by: default avatarAurelien Aptel <aaptel@suse.com>
Signed-off-by: default avatarSteve French <stfrench@microsoft.com>
parent d819d298
...@@ -2485,7 +2485,7 @@ smb3_init_transform_rq(struct TCP_Server_Info *server, struct smb_rqst *new_rq, ...@@ -2485,7 +2485,7 @@ smb3_init_transform_rq(struct TCP_Server_Info *server, struct smb_rqst *new_rq,
struct page **pages; struct page **pages;
struct smb2_transform_hdr *tr_hdr; struct smb2_transform_hdr *tr_hdr;
unsigned int npages = old_rq->rq_npages; unsigned int npages = old_rq->rq_npages;
unsigned int orig_len = 0; unsigned int orig_len;
int i; int i;
int rc = -ENOMEM; int rc = -ENOMEM;
...@@ -2499,9 +2499,6 @@ smb3_init_transform_rq(struct TCP_Server_Info *server, struct smb_rqst *new_rq, ...@@ -2499,9 +2499,6 @@ smb3_init_transform_rq(struct TCP_Server_Info *server, struct smb_rqst *new_rq,
new_rq->rq_pagesz = old_rq->rq_pagesz; new_rq->rq_pagesz = old_rq->rq_pagesz;
new_rq->rq_tailsz = old_rq->rq_tailsz; new_rq->rq_tailsz = old_rq->rq_tailsz;
for (i = 0; i < old_rq->rq_nvec; i++)
orig_len += old_rq->rq_iov[i].iov_len;
for (i = 0; i < npages; i++) { for (i = 0; i < npages; i++) {
pages[i] = alloc_page(GFP_KERNEL|__GFP_HIGHMEM); pages[i] = alloc_page(GFP_KERNEL|__GFP_HIGHMEM);
if (!pages[i]) if (!pages[i])
...@@ -2524,6 +2521,8 @@ smb3_init_transform_rq(struct TCP_Server_Info *server, struct smb_rqst *new_rq, ...@@ -2524,6 +2521,8 @@ smb3_init_transform_rq(struct TCP_Server_Info *server, struct smb_rqst *new_rq,
if (!tr_hdr) if (!tr_hdr)
goto err_free_iov; goto err_free_iov;
orig_len = smb2_rqst_len(old_rq, false);
/* fill the 2nd iov with a transform header */ /* fill the 2nd iov with a transform header */
fill_transform_hdr(tr_hdr, orig_len, old_rq); fill_transform_hdr(tr_hdr, orig_len, old_rq);
new_rq->rq_iov[0].iov_base = tr_hdr; new_rq->rq_iov[0].iov_base = tr_hdr;
......
...@@ -113,6 +113,8 @@ extern int smb2_unlock_range(struct cifsFileInfo *cfile, ...@@ -113,6 +113,8 @@ extern int smb2_unlock_range(struct cifsFileInfo *cfile,
extern int smb2_push_mandatory_locks(struct cifsFileInfo *cfile); extern int smb2_push_mandatory_locks(struct cifsFileInfo *cfile);
extern void smb2_reconnect_server(struct work_struct *work); extern void smb2_reconnect_server(struct work_struct *work);
extern int smb3_crypto_aead_allocate(struct TCP_Server_Info *server); extern int smb3_crypto_aead_allocate(struct TCP_Server_Info *server);
extern unsigned long
smb2_rqst_len(struct smb_rqst *rqst, bool skip_rfc1002_marker);
/* /*
* SMB2 Worker functions - most of protocol specific implementation details * SMB2 Worker functions - most of protocol specific implementation details
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include "smbdirect.h" #include "smbdirect.h"
#include "cifs_debug.h" #include "cifs_debug.h"
#include "cifsproto.h" #include "cifsproto.h"
#include "smb2proto.h"
static struct smbd_response *get_empty_queue_buffer( static struct smbd_response *get_empty_queue_buffer(
struct smbd_connection *info); struct smbd_connection *info);
...@@ -2087,7 +2088,7 @@ int smbd_send(struct smbd_connection *info, struct smb_rqst *rqst) ...@@ -2087,7 +2088,7 @@ int smbd_send(struct smbd_connection *info, struct smb_rqst *rqst)
struct kvec vec; struct kvec vec;
int nvecs; int nvecs;
int size; int size;
unsigned int buflen = 0, remaining_data_length; unsigned int buflen, remaining_data_length;
int start, i, j; int start, i, j;
int max_iov_size = int max_iov_size =
info->max_send_size - sizeof(struct smbd_data_transfer); info->max_send_size - sizeof(struct smbd_data_transfer);
...@@ -2111,25 +2112,13 @@ int smbd_send(struct smbd_connection *info, struct smb_rqst *rqst) ...@@ -2111,25 +2112,13 @@ int smbd_send(struct smbd_connection *info, struct smb_rqst *rqst)
log_write(ERR, "expected the pdu length in 1st iov, but got %zu\n", rqst->rq_iov[0].iov_len); log_write(ERR, "expected the pdu length in 1st iov, but got %zu\n", rqst->rq_iov[0].iov_len);
return -EINVAL; return -EINVAL;
} }
iov = &rqst->rq_iov[1];
/* total up iov array first */
for (i = 0; i < rqst->rq_nvec-1; i++) {
buflen += iov[i].iov_len;
}
/* /*
* Add in the page array if there is one. The caller needs to set * Add in the page array if there is one. The caller needs to set
* rq_tailsz to PAGE_SIZE when the buffer has multiple pages and * rq_tailsz to PAGE_SIZE when the buffer has multiple pages and
* ends at page boundary * ends at page boundary
*/ */
if (rqst->rq_npages) { buflen = smb2_rqst_len(rqst, true);
if (rqst->rq_npages == 1)
buflen += rqst->rq_tailsz;
else
buflen += rqst->rq_pagesz * (rqst->rq_npages - 1) -
rqst->rq_offset + rqst->rq_tailsz;
}
if (buflen + sizeof(struct smbd_data_transfer) > if (buflen + sizeof(struct smbd_data_transfer) >
info->max_fragmented_send_size) { info->max_fragmented_send_size) {
...@@ -2139,6 +2128,8 @@ int smbd_send(struct smbd_connection *info, struct smb_rqst *rqst) ...@@ -2139,6 +2128,8 @@ int smbd_send(struct smbd_connection *info, struct smb_rqst *rqst)
goto done; goto done;
} }
iov = &rqst->rq_iov[1];
cifs_dbg(FYI, "Sending smb (RDMA): smb_len=%u\n", buflen); cifs_dbg(FYI, "Sending smb (RDMA): smb_len=%u\n", buflen);
for (i = 0; i < rqst->rq_nvec-1; i++) for (i = 0; i < rqst->rq_nvec-1; i++)
dump_smb(iov[i].iov_base, iov[i].iov_len); dump_smb(iov[i].iov_base, iov[i].iov_len);
......
...@@ -201,15 +201,24 @@ smb_send_kvec(struct TCP_Server_Info *server, struct msghdr *smb_msg, ...@@ -201,15 +201,24 @@ smb_send_kvec(struct TCP_Server_Info *server, struct msghdr *smb_msg,
return 0; return 0;
} }
static unsigned long unsigned long
smb2_rqst_len(struct smb_rqst *rqst) smb2_rqst_len(struct smb_rqst *rqst, bool skip_rfc1002_marker)
{ {
unsigned int i; unsigned int i;
struct kvec *iov = rqst->rq_iov; struct kvec *iov;
int nvec;
unsigned long buflen = 0; unsigned long buflen = 0;
if (skip_rfc1002_marker && rqst->rq_iov[0].iov_len == 4) {
iov = &rqst->rq_iov[1];
nvec = rqst->rq_nvec - 1;
} else {
iov = rqst->rq_iov;
nvec = rqst->rq_nvec;
}
/* total up iov array first */ /* total up iov array first */
for (i = 0; i < rqst->rq_nvec; i++) for (i = 0; i < nvec; i++)
buflen += iov[i].iov_len; buflen += iov[i].iov_len;
/* /*
...@@ -262,7 +271,7 @@ __smb_send_rqst(struct TCP_Server_Info *server, int num_rqst, ...@@ -262,7 +271,7 @@ __smb_send_rqst(struct TCP_Server_Info *server, int num_rqst,
(char *)&val, sizeof(val)); (char *)&val, sizeof(val));
for (j = 0; j < num_rqst; j++) for (j = 0; j < num_rqst; j++)
send_length += smb2_rqst_len(&rqst[j]); send_length += smb2_rqst_len(&rqst[j], true);
rfc1002_marker = cpu_to_be32(send_length); rfc1002_marker = cpu_to_be32(send_length);
/* Generate a rfc1002 marker for SMB2+ */ /* Generate a rfc1002 marker for SMB2+ */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment