|
@@ -1843,6 +1843,63 @@ decrypt_raw_data(struct TCP_Server_Info *server, char *buf,
|
|
|
return rc;
|
|
|
}
|
|
|
|
|
|
+static int
|
|
|
+read_data_into_pages(struct TCP_Server_Info *server, struct page **pages,
|
|
|
+ unsigned int npages, unsigned int len)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+ int length;
|
|
|
+
|
|
|
+ for (i = 0; i < npages; i++) {
|
|
|
+ struct page *page = pages[i];
|
|
|
+ size_t n;
|
|
|
+
|
|
|
+ n = len;
|
|
|
+ if (len >= PAGE_SIZE) {
|
|
|
+ /* enough data to fill the page */
|
|
|
+ n = PAGE_SIZE;
|
|
|
+ len -= n;
|
|
|
+ } else {
|
|
|
+ zero_user(page, len, PAGE_SIZE - len);
|
|
|
+ len = 0;
|
|
|
+ }
|
|
|
+ length = cifs_read_page_from_socket(server, page, n);
|
|
|
+ if (length < 0)
|
|
|
+ return length;
|
|
|
+ server->total_read += length;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int
|
|
|
+init_read_bvec(struct page **pages, unsigned int npages, unsigned int data_size,
|
|
|
+ unsigned int cur_off, struct bio_vec **page_vec)
|
|
|
+{
|
|
|
+ struct bio_vec *bvec;
|
|
|
+ int i;
|
|
|
+
|
|
|
+ bvec = kcalloc(npages, sizeof(struct bio_vec), GFP_KERNEL);
|
|
|
+ if (!bvec)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ for (i = 0; i < npages; i++) {
|
|
|
+ bvec[i].bv_page = pages[i];
|
|
|
+ bvec[i].bv_offset = (i == 0) ? cur_off : 0;
|
|
|
+ bvec[i].bv_len = min_t(unsigned int, PAGE_SIZE, data_size);
|
|
|
+ data_size -= bvec[i].bv_len;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (data_size != 0) {
|
|
|
+ cifs_dbg(VFS, "%s: something went wrong\n", __func__);
|
|
|
+ kfree(bvec);
|
|
|
+ return -EIO;
|
|
|
+ }
|
|
|
+
|
|
|
+ *page_vec = bvec;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static int
|
|
|
handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid,
|
|
|
char *buf, unsigned int buf_len, struct page **pages,
|
|
@@ -1850,6 +1907,9 @@ handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid,
|
|
|
{
|
|
|
unsigned int data_offset;
|
|
|
unsigned int data_len;
|
|
|
+ unsigned int cur_off;
|
|
|
+ unsigned int cur_page_idx;
|
|
|
+ unsigned int pad_len;
|
|
|
struct cifs_readdata *rdata = mid->callback_data;
|
|
|
struct smb2_sync_hdr *shdr = get_sync_hdr(buf);
|
|
|
struct bio_vec *bvec = NULL;
|
|
@@ -1895,9 +1955,37 @@ handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+ pad_len = data_offset - server->vals->read_rsp_size;
|
|
|
+
|
|
|
if (buf_len <= data_offset) {
|
|
|
/* read response payload is in pages */
|
|
|
- /* BB add code to init iter with pages */
|
|
|
+ cur_page_idx = pad_len / PAGE_SIZE;
|
|
|
+ cur_off = pad_len % PAGE_SIZE;
|
|
|
+
|
|
|
+ if (cur_page_idx != 0) {
|
|
|
+ /* data offset is beyond the 1st page of response */
|
|
|
+ cifs_dbg(FYI, "%s: data offset (%u) beyond 1st page of response\n",
|
|
|
+ __func__, data_offset);
|
|
|
+ rdata->result = -EIO;
|
|
|
+ dequeue_mid(mid, rdata->result);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (data_len > page_data_size - pad_len) {
|
|
|
+ /* data_len is corrupt -- discard frame */
|
|
|
+ rdata->result = -EIO;
|
|
|
+ dequeue_mid(mid, rdata->result);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ rdata->result = init_read_bvec(pages, npages, page_data_size,
|
|
|
+ cur_off, &bvec);
|
|
|
+ if (rdata->result != 0) {
|
|
|
+ dequeue_mid(mid, rdata->result);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ iov_iter_bvec(&iter, WRITE | ITER_BVEC, bvec, npages, data_len);
|
|
|
} else if (buf_len >= data_offset + data_len) {
|
|
|
/* read response payload is in buf */
|
|
|
WARN_ONCE(npages > 0, "read data can be either in buf or in pages");
|
|
@@ -1931,6 +2019,79 @@ handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid,
|
|
|
return length;
|
|
|
}
|
|
|
|
|
|
+static int
|
|
|
+receive_encrypted_read(struct TCP_Server_Info *server, struct mid_q_entry **mid)
|
|
|
+{
|
|
|
+ char *buf = server->smallbuf;
|
|
|
+ struct smb2_transform_hdr *tr_hdr = (struct smb2_transform_hdr *)buf;
|
|
|
+ unsigned int npages;
|
|
|
+ struct page **pages;
|
|
|
+ unsigned int len;
|
|
|
+ unsigned int buflen = get_rfc1002_length(buf) + 4;
|
|
|
+ int rc;
|
|
|
+ int i = 0;
|
|
|
+
|
|
|
+ len = min_t(unsigned int, buflen, server->vals->read_rsp_size - 4 +
|
|
|
+ sizeof(struct smb2_transform_hdr)) - HEADER_SIZE(server) + 1;
|
|
|
+
|
|
|
+ rc = cifs_read_from_socket(server, buf + HEADER_SIZE(server) - 1, len);
|
|
|
+ if (rc < 0)
|
|
|
+ return rc;
|
|
|
+ server->total_read += rc;
|
|
|
+
|
|
|
+ len = le32_to_cpu(tr_hdr->OriginalMessageSize) + 4 -
|
|
|
+ server->vals->read_rsp_size;
|
|
|
+ npages = DIV_ROUND_UP(len, PAGE_SIZE);
|
|
|
+
|
|
|
+ pages = kmalloc_array(npages, sizeof(struct page *), GFP_KERNEL);
|
|
|
+ if (!pages) {
|
|
|
+ rc = -ENOMEM;
|
|
|
+ goto discard_data;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (; i < npages; i++) {
|
|
|
+ pages[i] = alloc_page(GFP_KERNEL|__GFP_HIGHMEM);
|
|
|
+ if (!pages[i]) {
|
|
|
+ rc = -ENOMEM;
|
|
|
+ goto discard_data;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* read read data into pages */
|
|
|
+ rc = read_data_into_pages(server, pages, npages, len);
|
|
|
+ if (rc)
|
|
|
+ goto free_pages;
|
|
|
+
|
|
|
+ rc = cifs_discard_remaining_data(server);
|
|
|
+ if (rc)
|
|
|
+ goto free_pages;
|
|
|
+
|
|
|
+ rc = decrypt_raw_data(server, buf, server->vals->read_rsp_size - 4,
|
|
|
+ pages, npages, len);
|
|
|
+ if (rc)
|
|
|
+ goto free_pages;
|
|
|
+
|
|
|
+ *mid = smb2_find_mid(server, buf);
|
|
|
+ if (*mid == NULL)
|
|
|
+ cifs_dbg(FYI, "mid not found\n");
|
|
|
+ else {
|
|
|
+ cifs_dbg(FYI, "mid found\n");
|
|
|
+ (*mid)->decrypted = true;
|
|
|
+ rc = handle_read_data(server, *mid, buf,
|
|
|
+ server->vals->read_rsp_size,
|
|
|
+ pages, npages, len);
|
|
|
+ }
|
|
|
+
|
|
|
+free_pages:
|
|
|
+ for (i = i - 1; i >= 0; i--)
|
|
|
+ put_page(pages[i]);
|
|
|
+ kfree(pages);
|
|
|
+ return rc;
|
|
|
+discard_data:
|
|
|
+ cifs_discard_remaining_data(server);
|
|
|
+ goto free_pages;
|
|
|
+}
|
|
|
+
|
|
|
static int
|
|
|
receive_encrypted_standard(struct TCP_Server_Info *server,
|
|
|
struct mid_q_entry **mid)
|
|
@@ -2000,14 +2161,8 @@ smb3_receive_transform(struct TCP_Server_Info *server, struct mid_q_entry **mid)
|
|
|
return -ECONNABORTED;
|
|
|
}
|
|
|
|
|
|
- if (pdu_length + 4 > CIFSMaxBufSize + MAX_HEADER_SIZE(server)) {
|
|
|
- cifs_dbg(VFS, "Decoding responses of big size (%u) is not supported\n",
|
|
|
- pdu_length);
|
|
|
- /* BB add code to allocate and fill highmem pages here */
|
|
|
- cifs_reconnect(server);
|
|
|
- wake_up(&server->response_q);
|
|
|
- return -ECONNABORTED;
|
|
|
- }
|
|
|
+ if (pdu_length + 4 > CIFSMaxBufSize + MAX_HEADER_SIZE(server))
|
|
|
+ return receive_encrypted_read(server, mid);
|
|
|
|
|
|
return receive_encrypted_standard(server, mid);
|
|
|
}
|