diff options
author | Pavel Shilovsky <pshilov@microsoft.com> | 2016-11-23 18:14:57 -0500 |
---|---|---|
committer | Steve French <smfrench@gmail.com> | 2017-02-01 17:46:35 -0500 |
commit | 738f9de5cdb9175c19d24cfdf90b4543fc3b47bf (patch) | |
tree | 121fd4b54bec97e745cf6e6beb093b34408afd17 | |
parent | fb2036d817584df42504910fe104f68517e8990e (diff) |
CIFS: Send RFC1001 length in a separate iov
In order to simplify further encryption support we need to separate
RFC1001 length and SMB2 header when sending a request. Put the length
field in iov[0] and the rest of the packet into following iovs.
Signed-off-by: Pavel Shilovsky <pshilov@microsoft.com>
-rw-r--r-- | fs/cifs/cifsencrypt.c | 38 | ||||
-rw-r--r-- | fs/cifs/cifsglob.h | 2 | ||||
-rw-r--r-- | fs/cifs/cifssmb.c | 51 | ||||
-rw-r--r-- | fs/cifs/smb2pdu.c | 64 | ||||
-rw-r--r-- | fs/cifs/smb2transport.c | 28 | ||||
-rw-r--r-- | fs/cifs/transport.c | 86 |
6 files changed, 174 insertions, 95 deletions
diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c index 66bd7fa9b7a6..d8af15f19dd8 100644 --- a/fs/cifs/cifsencrypt.c +++ b/fs/cifs/cifsencrypt.c | |||
@@ -75,24 +75,20 @@ int __cifs_calc_signature(struct smb_rqst *rqst, | |||
75 | struct kvec *iov = rqst->rq_iov; | 75 | struct kvec *iov = rqst->rq_iov; |
76 | int n_vec = rqst->rq_nvec; | 76 | int n_vec = rqst->rq_nvec; |
77 | 77 | ||
78 | for (i = 0; i < n_vec; i++) { | 78 | if (n_vec < 2 || iov[0].iov_len != 4) |
79 | return -EIO; | ||
80 | |||
81 | for (i = 1; i < n_vec; i++) { | ||
79 | if (iov[i].iov_len == 0) | 82 | if (iov[i].iov_len == 0) |
80 | continue; | 83 | continue; |
81 | if (iov[i].iov_base == NULL) { | 84 | if (iov[i].iov_base == NULL) { |
82 | cifs_dbg(VFS, "null iovec entry\n"); | 85 | cifs_dbg(VFS, "null iovec entry\n"); |
83 | return -EIO; | 86 | return -EIO; |
84 | } | 87 | } |
85 | /* The first entry includes a length field (which does not get | 88 | if (i == 1 && iov[1].iov_len <= 4) |
86 | signed that occupies the first 4 bytes before the header */ | 89 | break; /* nothing to sign or corrupt header */ |
87 | if (i == 0) { | 90 | rc = crypto_shash_update(shash, |
88 | if (iov[0].iov_len <= 8) /* cmd field at offset 9 */ | 91 | iov[i].iov_base, iov[i].iov_len); |
89 | break; /* nothing to sign or corrupt header */ | ||
90 | rc = crypto_shash_update(shash, | ||
91 | iov[i].iov_base + 4, iov[i].iov_len - 4); | ||
92 | } else { | ||
93 | rc = crypto_shash_update(shash, | ||
94 | iov[i].iov_base, iov[i].iov_len); | ||
95 | } | ||
96 | if (rc) { | 92 | if (rc) { |
97 | cifs_dbg(VFS, "%s: Could not update with payload\n", | 93 | cifs_dbg(VFS, "%s: Could not update with payload\n", |
98 | __func__); | 94 | __func__); |
@@ -168,6 +164,10 @@ int cifs_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server, | |||
168 | char smb_signature[20]; | 164 | char smb_signature[20]; |
169 | struct smb_hdr *cifs_pdu = (struct smb_hdr *)rqst->rq_iov[0].iov_base; | 165 | struct smb_hdr *cifs_pdu = (struct smb_hdr *)rqst->rq_iov[0].iov_base; |
170 | 166 | ||
167 | if (rqst->rq_iov[0].iov_len != 4 || | ||
168 | rqst->rq_iov[0].iov_base + 4 != rqst->rq_iov[1].iov_base) | ||
169 | return -EIO; | ||
170 | |||
171 | if ((cifs_pdu == NULL) || (server == NULL)) | 171 | if ((cifs_pdu == NULL) || (server == NULL)) |
172 | return -EINVAL; | 172 | return -EINVAL; |
173 | 173 | ||
@@ -209,12 +209,14 @@ int cifs_sign_smbv(struct kvec *iov, int n_vec, struct TCP_Server_Info *server, | |||
209 | int cifs_sign_smb(struct smb_hdr *cifs_pdu, struct TCP_Server_Info *server, | 209 | int cifs_sign_smb(struct smb_hdr *cifs_pdu, struct TCP_Server_Info *server, |
210 | __u32 *pexpected_response_sequence_number) | 210 | __u32 *pexpected_response_sequence_number) |
211 | { | 211 | { |
212 | struct kvec iov; | 212 | struct kvec iov[2]; |
213 | 213 | ||
214 | iov.iov_base = cifs_pdu; | 214 | iov[0].iov_base = cifs_pdu; |
215 | iov.iov_len = be32_to_cpu(cifs_pdu->smb_buf_length) + 4; | 215 | iov[0].iov_len = 4; |
216 | iov[1].iov_base = (char *)cifs_pdu + 4; | ||
217 | iov[1].iov_len = be32_to_cpu(cifs_pdu->smb_buf_length); | ||
216 | 218 | ||
217 | return cifs_sign_smbv(&iov, 1, server, | 219 | return cifs_sign_smbv(iov, 2, server, |
218 | pexpected_response_sequence_number); | 220 | pexpected_response_sequence_number); |
219 | } | 221 | } |
220 | 222 | ||
@@ -227,6 +229,10 @@ int cifs_verify_signature(struct smb_rqst *rqst, | |||
227 | char what_we_think_sig_should_be[20]; | 229 | char what_we_think_sig_should_be[20]; |
228 | struct smb_hdr *cifs_pdu = (struct smb_hdr *)rqst->rq_iov[0].iov_base; | 230 | struct smb_hdr *cifs_pdu = (struct smb_hdr *)rqst->rq_iov[0].iov_base; |
229 | 231 | ||
232 | if (rqst->rq_iov[0].iov_len != 4 || | ||
233 | rqst->rq_iov[0].iov_base + 4 != rqst->rq_iov[1].iov_base) | ||
234 | return -EIO; | ||
235 | |||
230 | if (cifs_pdu == NULL || server == NULL) | 236 | if (cifs_pdu == NULL || server == NULL) |
231 | return -EINVAL; | 237 | return -EINVAL; |
232 | 238 | ||
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 6d5fc87ba335..eb0ffac73f90 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h | |||
@@ -1119,7 +1119,7 @@ struct cifs_readdata { | |||
1119 | int (*read_into_pages)(struct TCP_Server_Info *server, | 1119 | int (*read_into_pages)(struct TCP_Server_Info *server, |
1120 | struct cifs_readdata *rdata, | 1120 | struct cifs_readdata *rdata, |
1121 | unsigned int len); | 1121 | unsigned int len); |
1122 | struct kvec iov; | 1122 | struct kvec iov[2]; |
1123 | unsigned int pagesz; | 1123 | unsigned int pagesz; |
1124 | unsigned int tailsz; | 1124 | unsigned int tailsz; |
1125 | unsigned int credits; | 1125 | unsigned int credits; |
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 609ce335e6ac..be9fa7ffe799 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c | |||
@@ -708,9 +708,9 @@ CIFSSMBEcho(struct TCP_Server_Info *server) | |||
708 | { | 708 | { |
709 | ECHO_REQ *smb; | 709 | ECHO_REQ *smb; |
710 | int rc = 0; | 710 | int rc = 0; |
711 | struct kvec iov; | 711 | struct kvec iov[2]; |
712 | struct smb_rqst rqst = { .rq_iov = &iov, | 712 | struct smb_rqst rqst = { .rq_iov = iov, |
713 | .rq_nvec = 1 }; | 713 | .rq_nvec = 2 }; |
714 | 714 | ||
715 | cifs_dbg(FYI, "In echo request\n"); | 715 | cifs_dbg(FYI, "In echo request\n"); |
716 | 716 | ||
@@ -725,8 +725,11 @@ CIFSSMBEcho(struct TCP_Server_Info *server) | |||
725 | put_bcc(1, &smb->hdr); | 725 | put_bcc(1, &smb->hdr); |
726 | smb->Data[0] = 'a'; | 726 | smb->Data[0] = 'a'; |
727 | inc_rfc1001_len(smb, 3); | 727 | inc_rfc1001_len(smb, 3); |
728 | iov.iov_base = smb; | 728 | |
729 | iov.iov_len = be32_to_cpu(smb->hdr.smb_buf_length) + 4; | 729 | iov[0].iov_len = 4; |
730 | iov[0].iov_base = smb; | ||
731 | iov[1].iov_len = get_rfc1002_length(smb); | ||
732 | iov[1].iov_base = (char *)smb + 4; | ||
730 | 733 | ||
731 | rc = cifs_call_async(server, &rqst, NULL, cifs_echo_callback, | 734 | rc = cifs_call_async(server, &rqst, NULL, cifs_echo_callback, |
732 | server, CIFS_ASYNC_OP | CIFS_ECHO_OP); | 735 | server, CIFS_ASYNC_OP | CIFS_ECHO_OP); |
@@ -1509,10 +1512,12 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid) | |||
1509 | } | 1512 | } |
1510 | 1513 | ||
1511 | /* set up first iov for signature check */ | 1514 | /* set up first iov for signature check */ |
1512 | rdata->iov.iov_base = buf; | 1515 | rdata->iov[0].iov_base = buf; |
1513 | rdata->iov.iov_len = server->total_read; | 1516 | rdata->iov[0].iov_len = 4; |
1514 | cifs_dbg(FYI, "0: iov_base=%p iov_len=%zu\n", | 1517 | rdata->iov[1].iov_base = buf + 4; |
1515 | rdata->iov.iov_base, rdata->iov.iov_len); | 1518 | rdata->iov[1].iov_len = server->total_read - 4; |
1519 | cifs_dbg(FYI, "0: iov_base=%p iov_len=%u\n", | ||
1520 | rdata->iov[0].iov_base, server->total_read); | ||
1516 | 1521 | ||
1517 | /* how much data is in the response? */ | 1522 | /* how much data is in the response? */ |
1518 | data_len = server->ops->read_data_length(buf); | 1523 | data_len = server->ops->read_data_length(buf); |
@@ -1545,8 +1550,8 @@ cifs_readv_callback(struct mid_q_entry *mid) | |||
1545 | struct cifs_readdata *rdata = mid->callback_data; | 1550 | struct cifs_readdata *rdata = mid->callback_data; |
1546 | struct cifs_tcon *tcon = tlink_tcon(rdata->cfile->tlink); | 1551 | struct cifs_tcon *tcon = tlink_tcon(rdata->cfile->tlink); |
1547 | struct TCP_Server_Info *server = tcon->ses->server; | 1552 | struct TCP_Server_Info *server = tcon->ses->server; |
1548 | struct smb_rqst rqst = { .rq_iov = &rdata->iov, | 1553 | struct smb_rqst rqst = { .rq_iov = rdata->iov, |
1549 | .rq_nvec = 1, | 1554 | .rq_nvec = 2, |
1550 | .rq_pages = rdata->pages, | 1555 | .rq_pages = rdata->pages, |
1551 | .rq_npages = rdata->nr_pages, | 1556 | .rq_npages = rdata->nr_pages, |
1552 | .rq_pagesz = rdata->pagesz, | 1557 | .rq_pagesz = rdata->pagesz, |
@@ -1601,8 +1606,8 @@ cifs_async_readv(struct cifs_readdata *rdata) | |||
1601 | READ_REQ *smb = NULL; | 1606 | READ_REQ *smb = NULL; |
1602 | int wct; | 1607 | int wct; |
1603 | struct cifs_tcon *tcon = tlink_tcon(rdata->cfile->tlink); | 1608 | struct cifs_tcon *tcon = tlink_tcon(rdata->cfile->tlink); |
1604 | struct smb_rqst rqst = { .rq_iov = &rdata->iov, | 1609 | struct smb_rqst rqst = { .rq_iov = rdata->iov, |
1605 | .rq_nvec = 1 }; | 1610 | .rq_nvec = 2 }; |
1606 | 1611 | ||
1607 | cifs_dbg(FYI, "%s: offset=%llu bytes=%u\n", | 1612 | cifs_dbg(FYI, "%s: offset=%llu bytes=%u\n", |
1608 | __func__, rdata->offset, rdata->bytes); | 1613 | __func__, rdata->offset, rdata->bytes); |
@@ -1642,8 +1647,10 @@ cifs_async_readv(struct cifs_readdata *rdata) | |||
1642 | } | 1647 | } |
1643 | 1648 | ||
1644 | /* 4 for RFC1001 length + 1 for BCC */ | 1649 | /* 4 for RFC1001 length + 1 for BCC */ |
1645 | rdata->iov.iov_base = smb; | 1650 | rdata->iov[0].iov_base = smb; |
1646 | rdata->iov.iov_len = be32_to_cpu(smb->hdr.smb_buf_length) + 4; | 1651 | rdata->iov[0].iov_len = 4; |
1652 | rdata->iov[1].iov_base = (char *)smb + 4; | ||
1653 | rdata->iov[1].iov_len = get_rfc1002_length(smb); | ||
1647 | 1654 | ||
1648 | kref_get(&rdata->refcount); | 1655 | kref_get(&rdata->refcount); |
1649 | rc = cifs_call_async(tcon->ses->server, &rqst, cifs_readv_receive, | 1656 | rc = cifs_call_async(tcon->ses->server, &rqst, cifs_readv_receive, |
@@ -2096,7 +2103,7 @@ cifs_async_writev(struct cifs_writedata *wdata, | |||
2096 | WRITE_REQ *smb = NULL; | 2103 | WRITE_REQ *smb = NULL; |
2097 | int wct; | 2104 | int wct; |
2098 | struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink); | 2105 | struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink); |
2099 | struct kvec iov; | 2106 | struct kvec iov[2]; |
2100 | struct smb_rqst rqst = { }; | 2107 | struct smb_rqst rqst = { }; |
2101 | 2108 | ||
2102 | if (tcon->ses->capabilities & CAP_LARGE_FILES) { | 2109 | if (tcon->ses->capabilities & CAP_LARGE_FILES) { |
@@ -2129,11 +2136,13 @@ cifs_async_writev(struct cifs_writedata *wdata, | |||
2129 | cpu_to_le16(offsetof(struct smb_com_write_req, Data) - 4); | 2136 | cpu_to_le16(offsetof(struct smb_com_write_req, Data) - 4); |
2130 | 2137 | ||
2131 | /* 4 for RFC1001 length + 1 for BCC */ | 2138 | /* 4 for RFC1001 length + 1 for BCC */ |
2132 | iov.iov_len = be32_to_cpu(smb->hdr.smb_buf_length) + 4 + 1; | 2139 | iov[0].iov_len = 4; |
2133 | iov.iov_base = smb; | 2140 | iov[0].iov_base = smb; |
2141 | iov[1].iov_len = get_rfc1002_length(smb) + 1; | ||
2142 | iov[1].iov_base = (char *)smb + 4; | ||
2134 | 2143 | ||
2135 | rqst.rq_iov = &iov; | 2144 | rqst.rq_iov = iov; |
2136 | rqst.rq_nvec = 1; | 2145 | rqst.rq_nvec = 2; |
2137 | rqst.rq_pages = wdata->pages; | 2146 | rqst.rq_pages = wdata->pages; |
2138 | rqst.rq_npages = wdata->nr_pages; | 2147 | rqst.rq_npages = wdata->nr_pages; |
2139 | rqst.rq_pagesz = wdata->pagesz; | 2148 | rqst.rq_pagesz = wdata->pagesz; |
@@ -2154,7 +2163,7 @@ cifs_async_writev(struct cifs_writedata *wdata, | |||
2154 | (struct smb_com_writex_req *)smb; | 2163 | (struct smb_com_writex_req *)smb; |
2155 | inc_rfc1001_len(&smbw->hdr, wdata->bytes + 5); | 2164 | inc_rfc1001_len(&smbw->hdr, wdata->bytes + 5); |
2156 | put_bcc(wdata->bytes + 5, &smbw->hdr); | 2165 | put_bcc(wdata->bytes + 5, &smbw->hdr); |
2157 | iov.iov_len += 4; /* pad bigger by four bytes */ | 2166 | iov[1].iov_len += 4; /* pad bigger by four bytes */ |
2158 | } | 2167 | } |
2159 | 2168 | ||
2160 | kref_get(&wdata->refcount); | 2169 | kref_get(&wdata->refcount); |
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index fb6cf1b68dc5..438c4b108c07 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c | |||
@@ -2047,9 +2047,9 @@ SMB2_echo(struct TCP_Server_Info *server) | |||
2047 | { | 2047 | { |
2048 | struct smb2_echo_req *req; | 2048 | struct smb2_echo_req *req; |
2049 | int rc = 0; | 2049 | int rc = 0; |
2050 | struct kvec iov; | 2050 | struct kvec iov[2]; |
2051 | struct smb_rqst rqst = { .rq_iov = &iov, | 2051 | struct smb_rqst rqst = { .rq_iov = iov, |
2052 | .rq_nvec = 1 }; | 2052 | .rq_nvec = 2 }; |
2053 | 2053 | ||
2054 | cifs_dbg(FYI, "In echo request\n"); | 2054 | cifs_dbg(FYI, "In echo request\n"); |
2055 | 2055 | ||
@@ -2065,9 +2065,11 @@ SMB2_echo(struct TCP_Server_Info *server) | |||
2065 | 2065 | ||
2066 | req->hdr.sync_hdr.CreditRequest = cpu_to_le16(1); | 2066 | req->hdr.sync_hdr.CreditRequest = cpu_to_le16(1); |
2067 | 2067 | ||
2068 | iov.iov_base = (char *)req; | ||
2069 | /* 4 for rfc1002 length field */ | 2068 | /* 4 for rfc1002 length field */ |
2070 | iov.iov_len = get_rfc1002_length(req) + 4; | 2069 | iov[0].iov_len = 4; |
2070 | iov[0].iov_base = (char *)req; | ||
2071 | iov[1].iov_len = get_rfc1002_length(req); | ||
2072 | iov[1].iov_base = (char *)req + 4; | ||
2071 | 2073 | ||
2072 | rc = cifs_call_async(server, &rqst, NULL, smb2_echo_callback, server, | 2074 | rc = cifs_call_async(server, &rqst, NULL, smb2_echo_callback, server, |
2073 | CIFS_ECHO_OP); | 2075 | CIFS_ECHO_OP); |
@@ -2123,8 +2125,9 @@ SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, | |||
2123 | * have the end_of_chain boolean set to true. | 2125 | * have the end_of_chain boolean set to true. |
2124 | */ | 2126 | */ |
2125 | static int | 2127 | static int |
2126 | smb2_new_read_req(struct kvec *iov, struct cifs_io_parms *io_parms, | 2128 | smb2_new_read_req(void **buf, unsigned int *total_len, |
2127 | unsigned int remaining_bytes, int request_type) | 2129 | struct cifs_io_parms *io_parms, unsigned int remaining_bytes, |
2130 | int request_type) | ||
2128 | { | 2131 | { |
2129 | int rc = -EACCES; | 2132 | int rc = -EACCES; |
2130 | struct smb2_read_req *req = NULL; | 2133 | struct smb2_read_req *req = NULL; |
@@ -2172,9 +2175,9 @@ smb2_new_read_req(struct kvec *iov, struct cifs_io_parms *io_parms, | |||
2172 | else | 2175 | else |
2173 | req->RemainingBytes = 0; | 2176 | req->RemainingBytes = 0; |
2174 | 2177 | ||
2175 | iov[0].iov_base = (char *)req; | 2178 | *buf = req; |
2176 | /* 4 for rfc1002 length field */ | 2179 | /* 4 for rfc1002 length field */ |
2177 | iov[0].iov_len = get_rfc1002_length(req) + 4; | 2180 | *total_len = get_rfc1002_length(req) + 4; |
2178 | return rc; | 2181 | return rc; |
2179 | } | 2182 | } |
2180 | 2183 | ||
@@ -2184,10 +2187,11 @@ smb2_readv_callback(struct mid_q_entry *mid) | |||
2184 | struct cifs_readdata *rdata = mid->callback_data; | 2187 | struct cifs_readdata *rdata = mid->callback_data; |
2185 | struct cifs_tcon *tcon = tlink_tcon(rdata->cfile->tlink); | 2188 | struct cifs_tcon *tcon = tlink_tcon(rdata->cfile->tlink); |
2186 | struct TCP_Server_Info *server = tcon->ses->server; | 2189 | struct TCP_Server_Info *server = tcon->ses->server; |
2187 | struct smb2_sync_hdr *shdr = get_sync_hdr(rdata->iov.iov_base); | 2190 | struct smb2_sync_hdr *shdr = |
2191 | (struct smb2_sync_hdr *)rdata->iov[1].iov_base; | ||
2188 | unsigned int credits_received = 1; | 2192 | unsigned int credits_received = 1; |
2189 | struct smb_rqst rqst = { .rq_iov = &rdata->iov, | 2193 | struct smb_rqst rqst = { .rq_iov = rdata->iov, |
2190 | .rq_nvec = 1, | 2194 | .rq_nvec = 2, |
2191 | .rq_pages = rdata->pages, | 2195 | .rq_pages = rdata->pages, |
2192 | .rq_npages = rdata->nr_pages, | 2196 | .rq_npages = rdata->nr_pages, |
2193 | .rq_pagesz = rdata->pagesz, | 2197 | .rq_pagesz = rdata->pagesz, |
@@ -2238,7 +2242,7 @@ smb2_readv_callback(struct mid_q_entry *mid) | |||
2238 | add_credits(server, credits_received, 0); | 2242 | add_credits(server, credits_received, 0); |
2239 | } | 2243 | } |
2240 | 2244 | ||
2241 | /* smb2_async_readv - send an async write, and set up mid to handle result */ | 2245 | /* smb2_async_readv - send an async read, and set up mid to handle result */ |
2242 | int | 2246 | int |
2243 | smb2_async_readv(struct cifs_readdata *rdata) | 2247 | smb2_async_readv(struct cifs_readdata *rdata) |
2244 | { | 2248 | { |
@@ -2246,9 +2250,10 @@ smb2_async_readv(struct cifs_readdata *rdata) | |||
2246 | char *buf; | 2250 | char *buf; |
2247 | struct smb2_sync_hdr *shdr; | 2251 | struct smb2_sync_hdr *shdr; |
2248 | struct cifs_io_parms io_parms; | 2252 | struct cifs_io_parms io_parms; |
2249 | struct smb_rqst rqst = { .rq_iov = &rdata->iov, | 2253 | struct smb_rqst rqst = { .rq_iov = rdata->iov, |
2250 | .rq_nvec = 1 }; | 2254 | .rq_nvec = 2 }; |
2251 | struct TCP_Server_Info *server; | 2255 | struct TCP_Server_Info *server; |
2256 | unsigned int total_len; | ||
2252 | 2257 | ||
2253 | cifs_dbg(FYI, "%s: offset=%llu bytes=%u\n", | 2258 | cifs_dbg(FYI, "%s: offset=%llu bytes=%u\n", |
2254 | __func__, rdata->offset, rdata->bytes); | 2259 | __func__, rdata->offset, rdata->bytes); |
@@ -2262,7 +2267,7 @@ smb2_async_readv(struct cifs_readdata *rdata) | |||
2262 | 2267 | ||
2263 | server = io_parms.tcon->ses->server; | 2268 | server = io_parms.tcon->ses->server; |
2264 | 2269 | ||
2265 | rc = smb2_new_read_req(&rdata->iov, &io_parms, 0, 0); | 2270 | rc = smb2_new_read_req((void **) &buf, &total_len, &io_parms, 0, 0); |
2266 | if (rc) { | 2271 | if (rc) { |
2267 | if (rc == -EAGAIN && rdata->credits) { | 2272 | if (rc == -EAGAIN && rdata->credits) { |
2268 | /* credits was reset by reconnect */ | 2273 | /* credits was reset by reconnect */ |
@@ -2275,10 +2280,12 @@ smb2_async_readv(struct cifs_readdata *rdata) | |||
2275 | return rc; | 2280 | return rc; |
2276 | } | 2281 | } |
2277 | 2282 | ||
2278 | buf = rdata->iov.iov_base; | ||
2279 | shdr = get_sync_hdr(buf); | 2283 | shdr = get_sync_hdr(buf); |
2280 | /* 4 for rfc1002 length field */ | 2284 | /* 4 for rfc1002 length field */ |
2281 | rdata->iov.iov_len = get_rfc1002_length(rdata->iov.iov_base) + 4; | 2285 | rdata->iov[0].iov_len = 4; |
2286 | rdata->iov[0].iov_base = buf; | ||
2287 | rdata->iov[1].iov_len = total_len - 4; | ||
2288 | rdata->iov[1].iov_base = buf + 4; | ||
2282 | 2289 | ||
2283 | if (rdata->credits) { | 2290 | if (rdata->credits) { |
2284 | shdr->CreditCharge = cpu_to_le16(DIV_ROUND_UP(rdata->bytes, | 2291 | shdr->CreditCharge = cpu_to_le16(DIV_ROUND_UP(rdata->bytes, |
@@ -2314,12 +2321,17 @@ SMB2_read(const unsigned int xid, struct cifs_io_parms *io_parms, | |||
2314 | struct smb2_sync_hdr *shdr; | 2321 | struct smb2_sync_hdr *shdr; |
2315 | struct kvec iov[1]; | 2322 | struct kvec iov[1]; |
2316 | struct kvec rsp_iov; | 2323 | struct kvec rsp_iov; |
2324 | unsigned int total_len; | ||
2325 | char *req; | ||
2317 | 2326 | ||
2318 | *nbytes = 0; | 2327 | *nbytes = 0; |
2319 | rc = smb2_new_read_req(iov, io_parms, 0, 0); | 2328 | rc = smb2_new_read_req((void **)&req, &total_len, io_parms, 0, 0); |
2320 | if (rc) | 2329 | if (rc) |
2321 | return rc; | 2330 | return rc; |
2322 | 2331 | ||
2332 | iov[0].iov_base = buf; | ||
2333 | iov[0].iov_len = total_len; | ||
2334 | |||
2323 | rc = SendReceive2(xid, io_parms->tcon->ses, iov, 1, | 2335 | rc = SendReceive2(xid, io_parms->tcon->ses, iov, 1, |
2324 | &resp_buftype, CIFS_LOG_ERROR, &rsp_iov); | 2336 | &resp_buftype, CIFS_LOG_ERROR, &rsp_iov); |
2325 | cifs_small_buf_release(iov[0].iov_base); | 2337 | cifs_small_buf_release(iov[0].iov_base); |
@@ -2424,8 +2436,8 @@ smb2_async_writev(struct cifs_writedata *wdata, | |||
2424 | struct smb2_sync_hdr *shdr; | 2436 | struct smb2_sync_hdr *shdr; |
2425 | struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink); | 2437 | struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink); |
2426 | struct TCP_Server_Info *server = tcon->ses->server; | 2438 | struct TCP_Server_Info *server = tcon->ses->server; |
2427 | struct kvec iov; | 2439 | struct kvec iov[2]; |
2428 | struct smb_rqst rqst; | 2440 | struct smb_rqst rqst = { }; |
2429 | 2441 | ||
2430 | rc = small_smb2_init(SMB2_WRITE, tcon, (void **) &req); | 2442 | rc = small_smb2_init(SMB2_WRITE, tcon, (void **) &req); |
2431 | if (rc) { | 2443 | if (rc) { |
@@ -2455,11 +2467,13 @@ smb2_async_writev(struct cifs_writedata *wdata, | |||
2455 | req->RemainingBytes = 0; | 2467 | req->RemainingBytes = 0; |
2456 | 2468 | ||
2457 | /* 4 for rfc1002 length field and 1 for Buffer */ | 2469 | /* 4 for rfc1002 length field and 1 for Buffer */ |
2458 | iov.iov_len = get_rfc1002_length(req) + 4 - 1; | 2470 | iov[0].iov_len = 4; |
2459 | iov.iov_base = req; | 2471 | iov[0].iov_base = req; |
2472 | iov[1].iov_len = get_rfc1002_length(req) - 1; | ||
2473 | iov[1].iov_base = (char *)req + 4; | ||
2460 | 2474 | ||
2461 | rqst.rq_iov = &iov; | 2475 | rqst.rq_iov = iov; |
2462 | rqst.rq_nvec = 1; | 2476 | rqst.rq_nvec = 2; |
2463 | rqst.rq_pages = wdata->pages; | 2477 | rqst.rq_pages = wdata->pages; |
2464 | rqst.rq_npages = wdata->nr_pages; | 2478 | rqst.rq_npages = wdata->nr_pages; |
2465 | rqst.rq_pagesz = wdata->pagesz; | 2479 | rqst.rq_pagesz = wdata->pagesz; |
diff --git a/fs/cifs/smb2transport.c b/fs/cifs/smb2transport.c index 52ff937fd1ed..93b27752b634 100644 --- a/fs/cifs/smb2transport.c +++ b/fs/cifs/smb2transport.c | |||
@@ -138,7 +138,7 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) | |||
138 | unsigned char smb2_signature[SMB2_HMACSHA256_SIZE]; | 138 | unsigned char smb2_signature[SMB2_HMACSHA256_SIZE]; |
139 | unsigned char *sigptr = smb2_signature; | 139 | unsigned char *sigptr = smb2_signature; |
140 | struct kvec *iov = rqst->rq_iov; | 140 | struct kvec *iov = rqst->rq_iov; |
141 | struct smb2_sync_hdr *shdr = get_sync_hdr(iov[0].iov_base); | 141 | struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)iov[1].iov_base; |
142 | struct cifs_ses *ses; | 142 | struct cifs_ses *ses; |
143 | 143 | ||
144 | ses = smb2_find_smb_ses(shdr, server); | 144 | ses = smb2_find_smb_ses(shdr, server); |
@@ -355,7 +355,7 @@ smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) | |||
355 | unsigned char smb3_signature[SMB2_CMACAES_SIZE]; | 355 | unsigned char smb3_signature[SMB2_CMACAES_SIZE]; |
356 | unsigned char *sigptr = smb3_signature; | 356 | unsigned char *sigptr = smb3_signature; |
357 | struct kvec *iov = rqst->rq_iov; | 357 | struct kvec *iov = rqst->rq_iov; |
358 | struct smb2_sync_hdr *shdr = get_sync_hdr(iov[0].iov_base); | 358 | struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)iov[1].iov_base; |
359 | struct cifs_ses *ses; | 359 | struct cifs_ses *ses; |
360 | 360 | ||
361 | ses = smb2_find_smb_ses(shdr, server); | 361 | ses = smb2_find_smb_ses(shdr, server); |
@@ -400,7 +400,8 @@ static int | |||
400 | smb2_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server) | 400 | smb2_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server) |
401 | { | 401 | { |
402 | int rc = 0; | 402 | int rc = 0; |
403 | struct smb2_sync_hdr *shdr = get_sync_hdr(rqst->rq_iov[0].iov_base); | 403 | struct smb2_sync_hdr *shdr = |
404 | (struct smb2_sync_hdr *)rqst->rq_iov[1].iov_base; | ||
404 | 405 | ||
405 | if (!(shdr->Flags & SMB2_FLAGS_SIGNED) || | 406 | if (!(shdr->Flags & SMB2_FLAGS_SIGNED) || |
406 | server->tcpStatus == CifsNeedNegotiate) | 407 | server->tcpStatus == CifsNeedNegotiate) |
@@ -421,7 +422,8 @@ smb2_verify_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) | |||
421 | { | 422 | { |
422 | unsigned int rc; | 423 | unsigned int rc; |
423 | char server_response_sig[16]; | 424 | char server_response_sig[16]; |
424 | struct smb2_sync_hdr *shdr = get_sync_hdr(rqst->rq_iov[0].iov_base); | 425 | struct smb2_sync_hdr *shdr = |
426 | (struct smb2_sync_hdr *)rqst->rq_iov[1].iov_base; | ||
425 | 427 | ||
426 | if ((shdr->Command == SMB2_NEGOTIATE) || | 428 | if ((shdr->Command == SMB2_NEGOTIATE) || |
427 | (shdr->Command == SMB2_SESSION_SETUP) || | 429 | (shdr->Command == SMB2_SESSION_SETUP) || |
@@ -550,12 +552,14 @@ smb2_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server, | |||
550 | bool log_error) | 552 | bool log_error) |
551 | { | 553 | { |
552 | unsigned int len = get_rfc1002_length(mid->resp_buf); | 554 | unsigned int len = get_rfc1002_length(mid->resp_buf); |
553 | struct kvec iov; | 555 | struct kvec iov[2]; |
554 | struct smb_rqst rqst = { .rq_iov = &iov, | 556 | struct smb_rqst rqst = { .rq_iov = iov, |
555 | .rq_nvec = 1 }; | 557 | .rq_nvec = 2 }; |
556 | 558 | ||
557 | iov.iov_base = (char *)mid->resp_buf; | 559 | iov[0].iov_base = (char *)mid->resp_buf; |
558 | iov.iov_len = get_rfc1002_length(mid->resp_buf) + 4; | 560 | iov[0].iov_len = 4; |
561 | iov[1].iov_base = (char *)mid->resp_buf + 4; | ||
562 | iov[1].iov_len = len; | ||
559 | 563 | ||
560 | dump_smb(mid->resp_buf, min_t(u32, 80, len)); | 564 | dump_smb(mid->resp_buf, min_t(u32, 80, len)); |
561 | /* convert the length into a more usable form */ | 565 | /* convert the length into a more usable form */ |
@@ -575,7 +579,8 @@ struct mid_q_entry * | |||
575 | smb2_setup_request(struct cifs_ses *ses, struct smb_rqst *rqst) | 579 | smb2_setup_request(struct cifs_ses *ses, struct smb_rqst *rqst) |
576 | { | 580 | { |
577 | int rc; | 581 | int rc; |
578 | struct smb2_sync_hdr *shdr = get_sync_hdr(rqst->rq_iov[0].iov_base); | 582 | struct smb2_sync_hdr *shdr = |
583 | (struct smb2_sync_hdr *)rqst->rq_iov[1].iov_base; | ||
579 | struct mid_q_entry *mid; | 584 | struct mid_q_entry *mid; |
580 | 585 | ||
581 | smb2_seq_num_into_buf(ses->server, shdr); | 586 | smb2_seq_num_into_buf(ses->server, shdr); |
@@ -595,7 +600,8 @@ struct mid_q_entry * | |||
595 | smb2_setup_async_request(struct TCP_Server_Info *server, struct smb_rqst *rqst) | 600 | smb2_setup_async_request(struct TCP_Server_Info *server, struct smb_rqst *rqst) |
596 | { | 601 | { |
597 | int rc; | 602 | int rc; |
598 | struct smb2_sync_hdr *shdr = get_sync_hdr(rqst->rq_iov[0].iov_base); | 603 | struct smb2_sync_hdr *shdr = |
604 | (struct smb2_sync_hdr *)rqst->rq_iov[1].iov_base; | ||
599 | struct mid_q_entry *mid; | 605 | struct mid_q_entry *mid; |
600 | 606 | ||
601 | smb2_seq_num_into_buf(server, shdr); | 607 | smb2_seq_num_into_buf(server, shdr); |
diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index 878f0dceeb13..dacfdf080330 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c | |||
@@ -245,8 +245,12 @@ smb_send_rqst(struct TCP_Server_Info *server, struct smb_rqst *rqst) | |||
245 | return -EIO; | 245 | return -EIO; |
246 | } | 246 | } |
247 | 247 | ||
248 | if (n_vec < 2) | ||
249 | return -EIO; | ||
250 | |||
248 | cifs_dbg(FYI, "Sending smb: smb_len=%u\n", smb_buf_length); | 251 | cifs_dbg(FYI, "Sending smb: smb_len=%u\n", smb_buf_length); |
249 | dump_smb(iov[0].iov_base, iov[0].iov_len); | 252 | dump_smb(iov[0].iov_base, iov[0].iov_len); |
253 | dump_smb(iov[1].iov_base, iov[1].iov_len); | ||
250 | 254 | ||
251 | /* cork the socket */ | 255 | /* cork the socket */ |
252 | kernel_setsockopt(ssocket, SOL_TCP, TCP_CORK, | 256 | kernel_setsockopt(ssocket, SOL_TCP, TCP_CORK, |
@@ -321,12 +325,14 @@ int | |||
321 | smb_send(struct TCP_Server_Info *server, struct smb_hdr *smb_buffer, | 325 | smb_send(struct TCP_Server_Info *server, struct smb_hdr *smb_buffer, |
322 | unsigned int smb_buf_length) | 326 | unsigned int smb_buf_length) |
323 | { | 327 | { |
324 | struct kvec iov; | 328 | struct kvec iov[2]; |
325 | 329 | ||
326 | iov.iov_base = smb_buffer; | 330 | iov[0].iov_base = smb_buffer; |
327 | iov.iov_len = smb_buf_length + 4; | 331 | iov[0].iov_len = 4; |
332 | iov[1].iov_base = (char *)smb_buffer + 4; | ||
333 | iov[1].iov_len = smb_buf_length; | ||
328 | 334 | ||
329 | return smb_sendv(server, &iov, 1); | 335 | return smb_sendv(server, iov, 2); |
330 | } | 336 | } |
331 | 337 | ||
332 | static int | 338 | static int |
@@ -454,6 +460,10 @@ cifs_setup_async_request(struct TCP_Server_Info *server, struct smb_rqst *rqst) | |||
454 | struct smb_hdr *hdr = (struct smb_hdr *)rqst->rq_iov[0].iov_base; | 460 | struct smb_hdr *hdr = (struct smb_hdr *)rqst->rq_iov[0].iov_base; |
455 | struct mid_q_entry *mid; | 461 | struct mid_q_entry *mid; |
456 | 462 | ||
463 | if (rqst->rq_iov[0].iov_len != 4 || | ||
464 | rqst->rq_iov[0].iov_base + 4 != rqst->rq_iov[1].iov_base) | ||
465 | return ERR_PTR(-EIO); | ||
466 | |||
457 | /* enable signing if server requires it */ | 467 | /* enable signing if server requires it */ |
458 | if (server->sign) | 468 | if (server->sign) |
459 | hdr->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; | 469 | hdr->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; |
@@ -477,8 +487,8 @@ cifs_setup_async_request(struct TCP_Server_Info *server, struct smb_rqst *rqst) | |||
477 | */ | 487 | */ |
478 | int | 488 | int |
479 | cifs_call_async(struct TCP_Server_Info *server, struct smb_rqst *rqst, | 489 | cifs_call_async(struct TCP_Server_Info *server, struct smb_rqst *rqst, |
480 | mid_receive_t *receive, mid_callback_t *callback, | 490 | mid_receive_t *receive, mid_callback_t *callback, void *cbdata, |
481 | void *cbdata, const int flags) | 491 | const int flags) |
482 | { | 492 | { |
483 | int rc, timeout, optype; | 493 | int rc, timeout, optype; |
484 | struct mid_q_entry *mid; | 494 | struct mid_q_entry *mid; |
@@ -613,13 +623,15 @@ cifs_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server, | |||
613 | 623 | ||
614 | /* convert the length into a more usable form */ | 624 | /* convert the length into a more usable form */ |
615 | if (server->sign) { | 625 | if (server->sign) { |
616 | struct kvec iov; | 626 | struct kvec iov[2]; |
617 | int rc = 0; | 627 | int rc = 0; |
618 | struct smb_rqst rqst = { .rq_iov = &iov, | 628 | struct smb_rqst rqst = { .rq_iov = iov, |
619 | .rq_nvec = 1 }; | 629 | .rq_nvec = 2 }; |
620 | 630 | ||
621 | iov.iov_base = mid->resp_buf; | 631 | iov[0].iov_base = mid->resp_buf; |
622 | iov.iov_len = len; | 632 | iov[0].iov_len = 4; |
633 | iov[1].iov_base = (char *)mid->resp_buf + 4; | ||
634 | iov[1].iov_len = len - 4; | ||
623 | /* FIXME: add code to kill session */ | 635 | /* FIXME: add code to kill session */ |
624 | rc = cifs_verify_signature(&rqst, server, | 636 | rc = cifs_verify_signature(&rqst, server, |
625 | mid->sequence_number); | 637 | mid->sequence_number); |
@@ -639,6 +651,10 @@ cifs_setup_request(struct cifs_ses *ses, struct smb_rqst *rqst) | |||
639 | struct smb_hdr *hdr = (struct smb_hdr *)rqst->rq_iov[0].iov_base; | 651 | struct smb_hdr *hdr = (struct smb_hdr *)rqst->rq_iov[0].iov_base; |
640 | struct mid_q_entry *mid; | 652 | struct mid_q_entry *mid; |
641 | 653 | ||
654 | if (rqst->rq_iov[0].iov_len != 4 || | ||
655 | rqst->rq_iov[0].iov_base + 4 != rqst->rq_iov[1].iov_base) | ||
656 | return ERR_PTR(-EIO); | ||
657 | |||
642 | rc = allocate_mid(ses, hdr, &mid); | 658 | rc = allocate_mid(ses, hdr, &mid); |
643 | if (rc) | 659 | if (rc) |
644 | return ERR_PTR(rc); | 660 | return ERR_PTR(rc); |
@@ -650,18 +666,16 @@ cifs_setup_request(struct cifs_ses *ses, struct smb_rqst *rqst) | |||
650 | return mid; | 666 | return mid; |
651 | } | 667 | } |
652 | 668 | ||
653 | int | 669 | static int |
654 | SendReceive2(const unsigned int xid, struct cifs_ses *ses, | 670 | cifs_send_recv(const unsigned int xid, struct cifs_ses *ses, |
655 | struct kvec *iov, int n_vec, int *resp_buf_type /* ret */, | 671 | struct smb_rqst *rqst, int *resp_buf_type, const int flags, |
656 | const int flags, struct kvec *resp_iov) | 672 | struct kvec *resp_iov) |
657 | { | 673 | { |
658 | int rc = 0; | 674 | int rc = 0; |
659 | int timeout, optype; | 675 | int timeout, optype; |
660 | struct mid_q_entry *midQ; | 676 | struct mid_q_entry *midQ; |
661 | char *buf = iov[0].iov_base; | ||
662 | unsigned int credits = 1; | 677 | unsigned int credits = 1; |
663 | struct smb_rqst rqst = { .rq_iov = iov, | 678 | char *buf; |
664 | .rq_nvec = n_vec }; | ||
665 | 679 | ||
666 | timeout = flags & CIFS_TIMEOUT_MASK; | 680 | timeout = flags & CIFS_TIMEOUT_MASK; |
667 | optype = flags & CIFS_OP_MASK; | 681 | optype = flags & CIFS_OP_MASK; |
@@ -694,7 +708,7 @@ SendReceive2(const unsigned int xid, struct cifs_ses *ses, | |||
694 | 708 | ||
695 | mutex_lock(&ses->server->srv_mutex); | 709 | mutex_lock(&ses->server->srv_mutex); |
696 | 710 | ||
697 | midQ = ses->server->ops->setup_request(ses, &rqst); | 711 | midQ = ses->server->ops->setup_request(ses, rqst); |
698 | if (IS_ERR(midQ)) { | 712 | if (IS_ERR(midQ)) { |
699 | mutex_unlock(&ses->server->srv_mutex); | 713 | mutex_unlock(&ses->server->srv_mutex); |
700 | /* Update # of requests on wire to server */ | 714 | /* Update # of requests on wire to server */ |
@@ -704,7 +718,7 @@ SendReceive2(const unsigned int xid, struct cifs_ses *ses, | |||
704 | 718 | ||
705 | midQ->mid_state = MID_REQUEST_SUBMITTED; | 719 | midQ->mid_state = MID_REQUEST_SUBMITTED; |
706 | cifs_in_send_inc(ses->server); | 720 | cifs_in_send_inc(ses->server); |
707 | rc = smb_sendv(ses->server, iov, n_vec); | 721 | rc = smb_send_rqst(ses->server, rqst); |
708 | cifs_in_send_dec(ses->server); | 722 | cifs_in_send_dec(ses->server); |
709 | cifs_save_when_sent(midQ); | 723 | cifs_save_when_sent(midQ); |
710 | 724 | ||
@@ -720,7 +734,7 @@ SendReceive2(const unsigned int xid, struct cifs_ses *ses, | |||
720 | 734 | ||
721 | rc = wait_for_response(ses->server, midQ); | 735 | rc = wait_for_response(ses->server, midQ); |
722 | if (rc != 0) { | 736 | if (rc != 0) { |
723 | send_cancel(ses->server, &rqst, midQ); | 737 | send_cancel(ses->server, rqst, midQ); |
724 | spin_lock(&GlobalMid_Lock); | 738 | spin_lock(&GlobalMid_Lock); |
725 | if (midQ->mid_state == MID_REQUEST_SUBMITTED) { | 739 | if (midQ->mid_state == MID_REQUEST_SUBMITTED) { |
726 | midQ->callback = DeleteMidQEntry; | 740 | midQ->callback = DeleteMidQEntry; |
@@ -767,6 +781,36 @@ out: | |||
767 | } | 781 | } |
768 | 782 | ||
769 | int | 783 | int |
784 | SendReceive2(const unsigned int xid, struct cifs_ses *ses, | ||
785 | struct kvec *iov, int n_vec, int *resp_buf_type /* ret */, | ||
786 | const int flags, struct kvec *resp_iov) | ||
787 | { | ||
788 | struct smb_rqst rqst; | ||
789 | struct kvec *new_iov; | ||
790 | int rc; | ||
791 | |||
792 | new_iov = kmalloc(sizeof(struct kvec) * (n_vec + 1), GFP_KERNEL); | ||
793 | if (!new_iov) | ||
794 | return -ENOMEM; | ||
795 | |||
796 | /* 1st iov is a RFC1001 length followed by the rest of the packet */ | ||
797 | memcpy(new_iov + 1, iov, (sizeof(struct kvec) * n_vec)); | ||
798 | |||
799 | new_iov[0].iov_base = new_iov[1].iov_base; | ||
800 | new_iov[0].iov_len = 4; | ||
801 | new_iov[1].iov_base += 4; | ||
802 | new_iov[1].iov_len -= 4; | ||
803 | |||
804 | memset(&rqst, 0, sizeof(struct smb_rqst)); | ||
805 | rqst.rq_iov = new_iov; | ||
806 | rqst.rq_nvec = n_vec + 1; | ||
807 | |||
808 | rc = cifs_send_recv(xid, ses, &rqst, resp_buf_type, flags, resp_iov); | ||
809 | kfree(new_iov); | ||
810 | return rc; | ||
811 | } | ||
812 | |||
813 | int | ||
770 | SendReceive(const unsigned int xid, struct cifs_ses *ses, | 814 | SendReceive(const unsigned int xid, struct cifs_ses *ses, |
771 | struct smb_hdr *in_buf, struct smb_hdr *out_buf, | 815 | struct smb_hdr *in_buf, struct smb_hdr *out_buf, |
772 | int *pbytes_returned, const int timeout) | 816 | int *pbytes_returned, const int timeout) |