/src/samba/source3/libsmb/clireadwrite.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | Unix SMB/CIFS implementation. |
3 | | client file read/write routines |
4 | | Copyright (C) Andrew Tridgell 1994-1998 |
5 | | |
6 | | This program is free software; you can redistribute it and/or modify |
7 | | it under the terms of the GNU General Public License as published by |
8 | | the Free Software Foundation; either version 3 of the License, or |
9 | | (at your option) any later version. |
10 | | |
11 | | This program is distributed in the hope that it will be useful, |
12 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | | GNU General Public License for more details. |
15 | | |
16 | | You should have received a copy of the GNU General Public License |
17 | | along with this program. If not, see <http://www.gnu.org/licenses/>. |
18 | | */ |
19 | | |
20 | | #include "includes.h" |
21 | | #include "source3/include/client.h" |
22 | | #include "source3/libsmb/proto.h" |
23 | | #include "source3/libsmb/cli_smb2_fnum.h" |
24 | | #include "../lib/util/tevent_ntstatus.h" |
25 | | #include "async_smb.h" |
26 | | #include "trans2.h" |
27 | | #include "../libcli/smb/smbXcli_base.h" |
28 | | |
29 | | /**************************************************************************** |
30 | | Calculate the recommended read buffer size |
31 | | ****************************************************************************/ |
32 | | static size_t cli_read_max_bufsize(struct cli_state *cli) |
33 | 0 | { |
34 | 0 | uint8_t wct = 12; |
35 | 0 | uint32_t min_space; |
36 | 0 | uint32_t data_offset; |
37 | 0 | uint32_t useable_space = 0; |
38 | |
|
39 | 0 | data_offset = HDR_VWV; |
40 | 0 | data_offset += wct * sizeof(uint16_t); |
41 | 0 | data_offset += sizeof(uint16_t); /* byte count */ |
42 | 0 | data_offset += 1; /* pad */ |
43 | |
|
44 | 0 | min_space = cli_state_available_size(cli, data_offset); |
45 | |
|
46 | 0 | if (cli->server_posix_capabilities & CIFS_UNIX_LARGE_READ_CAP) { |
47 | 0 | useable_space = 0xFFFFFF - data_offset; |
48 | |
|
49 | 0 | if (smb1cli_conn_signing_is_active(cli->conn)) { |
50 | 0 | return min_space; |
51 | 0 | } |
52 | | |
53 | 0 | if (smb1cli_conn_encryption_on(cli->conn)) { |
54 | 0 | return min_space; |
55 | 0 | } |
56 | | |
57 | 0 | return useable_space; |
58 | 0 | } |
59 | | |
60 | 0 | if (smb1cli_conn_capabilities(cli->conn) & CAP_LARGE_READX) { |
61 | | /* |
62 | | * Note: CAP_LARGE_READX also works with signing |
63 | | */ |
64 | 0 | useable_space = 0x1FFFF - data_offset; |
65 | |
|
66 | 0 | useable_space = MIN(useable_space, UINT16_MAX); |
67 | |
|
68 | 0 | return useable_space; |
69 | 0 | } |
70 | | |
71 | 0 | return min_space; |
72 | 0 | } |
73 | | |
74 | | /**************************************************************************** |
75 | | Calculate the recommended write buffer size |
76 | | ****************************************************************************/ |
77 | | static size_t cli_write_max_bufsize(struct cli_state *cli, |
78 | | uint16_t write_mode, |
79 | | uint8_t wct) |
80 | 0 | { |
81 | 0 | uint32_t min_space; |
82 | 0 | uint32_t data_offset; |
83 | 0 | uint32_t useable_space = 0; |
84 | |
|
85 | 0 | data_offset = HDR_VWV; |
86 | 0 | data_offset += wct * sizeof(uint16_t); |
87 | 0 | data_offset += sizeof(uint16_t); /* byte count */ |
88 | 0 | data_offset += 1; /* pad */ |
89 | |
|
90 | 0 | min_space = cli_state_available_size(cli, data_offset); |
91 | |
|
92 | 0 | if (cli->server_posix_capabilities & CIFS_UNIX_LARGE_WRITE_CAP) { |
93 | 0 | useable_space = 0xFFFFFF - data_offset; |
94 | 0 | } else if (smb1cli_conn_capabilities(cli->conn) & CAP_LARGE_WRITEX) { |
95 | 0 | useable_space = 0x1FFFF - data_offset; |
96 | 0 | } else { |
97 | 0 | return min_space; |
98 | 0 | } |
99 | | |
100 | 0 | if (write_mode != 0) { |
101 | 0 | return min_space; |
102 | 0 | } |
103 | | |
104 | 0 | if (smb1cli_conn_signing_is_active(cli->conn)) { |
105 | 0 | return min_space; |
106 | 0 | } |
107 | | |
108 | 0 | if (smb1cli_conn_encryption_on(cli->conn)) { |
109 | 0 | return min_space; |
110 | 0 | } |
111 | | |
112 | 0 | if (strequal(cli->dev, "LPT1:")) { |
113 | 0 | return min_space; |
114 | 0 | } |
115 | | |
116 | 0 | return useable_space; |
117 | 0 | } |
118 | | |
119 | | struct cli_read_andx_state { |
120 | | size_t size; |
121 | | uint16_t vwv[12]; |
122 | | NTSTATUS status; |
123 | | size_t received; |
124 | | uint8_t *buf; |
125 | | }; |
126 | | |
127 | | static void cli_read_andx_done(struct tevent_req *subreq); |
128 | | |
129 | | struct tevent_req *cli_read_andx_create(TALLOC_CTX *mem_ctx, |
130 | | struct tevent_context *ev, |
131 | | struct cli_state *cli, uint16_t fnum, |
132 | | off_t offset, size_t size, |
133 | | struct tevent_req **psmbreq) |
134 | 0 | { |
135 | 0 | struct tevent_req *req, *subreq; |
136 | 0 | struct cli_read_andx_state *state; |
137 | 0 | uint8_t wct = 10; |
138 | |
|
139 | 0 | req = tevent_req_create(mem_ctx, &state, struct cli_read_andx_state); |
140 | 0 | if (req == NULL) { |
141 | 0 | return NULL; |
142 | 0 | } |
143 | 0 | state->size = size; |
144 | |
|
145 | 0 | SCVAL(state->vwv + 0, 0, 0xFF); |
146 | 0 | SCVAL(state->vwv + 0, 1, 0); |
147 | 0 | SSVAL(state->vwv + 1, 0, 0); |
148 | 0 | SSVAL(state->vwv + 2, 0, fnum); |
149 | 0 | SIVAL(state->vwv + 3, 0, offset); |
150 | 0 | SSVAL(state->vwv + 5, 0, size); |
151 | 0 | SSVAL(state->vwv + 6, 0, size); |
152 | 0 | SSVAL(state->vwv + 7, 0, (size >> 16)); |
153 | 0 | SSVAL(state->vwv + 8, 0, 0); |
154 | 0 | SSVAL(state->vwv + 9, 0, 0); |
155 | |
|
156 | 0 | if (smb1cli_conn_capabilities(cli->conn) & CAP_LARGE_FILES) { |
157 | 0 | SIVAL(state->vwv + 10, 0, |
158 | 0 | (((uint64_t)offset)>>32) & 0xffffffff); |
159 | 0 | wct = 12; |
160 | 0 | } else { |
161 | 0 | if ((((uint64_t)offset) & 0xffffffff00000000LL) != 0) { |
162 | 0 | DEBUG(10, ("cli_read_andx_send got large offset where " |
163 | 0 | "the server does not support it\n")); |
164 | 0 | tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); |
165 | 0 | return tevent_req_post(req, ev); |
166 | 0 | } |
167 | 0 | } |
168 | | |
169 | 0 | subreq = cli_smb_req_create(state, ev, cli, SMBreadX, 0, 0, wct, |
170 | 0 | state->vwv, 0, NULL); |
171 | 0 | if (subreq == NULL) { |
172 | 0 | TALLOC_FREE(req); |
173 | 0 | return NULL; |
174 | 0 | } |
175 | 0 | tevent_req_set_callback(subreq, cli_read_andx_done, req); |
176 | 0 | *psmbreq = subreq; |
177 | 0 | return req; |
178 | 0 | } |
179 | | |
180 | | struct tevent_req *cli_read_andx_send(TALLOC_CTX *mem_ctx, |
181 | | struct tevent_context *ev, |
182 | | struct cli_state *cli, uint16_t fnum, |
183 | | off_t offset, size_t size) |
184 | 0 | { |
185 | 0 | struct tevent_req *req, *subreq; |
186 | 0 | NTSTATUS status; |
187 | |
|
188 | 0 | req = cli_read_andx_create(mem_ctx, ev, cli, fnum, offset, size, |
189 | 0 | &subreq); |
190 | 0 | if (req == NULL) { |
191 | 0 | return NULL; |
192 | 0 | } |
193 | | |
194 | 0 | status = smb1cli_req_chain_submit(&subreq, 1); |
195 | 0 | if (tevent_req_nterror(req, status)) { |
196 | 0 | return tevent_req_post(req, ev); |
197 | 0 | } |
198 | 0 | return req; |
199 | 0 | } |
200 | | |
201 | | static void cli_read_andx_done(struct tevent_req *subreq) |
202 | 0 | { |
203 | 0 | struct tevent_req *req = tevent_req_callback_data( |
204 | 0 | subreq, struct tevent_req); |
205 | 0 | struct cli_read_andx_state *state = tevent_req_data( |
206 | 0 | req, struct cli_read_andx_state); |
207 | 0 | uint8_t *inbuf; |
208 | 0 | uint8_t wct; |
209 | 0 | uint16_t *vwv; |
210 | 0 | uint32_t num_bytes; |
211 | 0 | uint8_t *bytes; |
212 | |
|
213 | 0 | state->status = cli_smb_recv(subreq, state, &inbuf, 12, &wct, &vwv, |
214 | 0 | &num_bytes, &bytes); |
215 | 0 | TALLOC_FREE(subreq); |
216 | 0 | if (NT_STATUS_IS_ERR(state->status)) { |
217 | 0 | tevent_req_nterror(req, state->status); |
218 | 0 | return; |
219 | 0 | } |
220 | | |
221 | | /* size is the number of bytes the server returned. |
222 | | * Might be zero. */ |
223 | 0 | state->received = SVAL(vwv + 5, 0); |
224 | 0 | state->received |= (((unsigned int)SVAL(vwv + 7, 0)) << 16); |
225 | |
|
226 | 0 | if (state->received > state->size) { |
227 | 0 | DEBUG(5,("server returned more than we wanted!\n")); |
228 | 0 | tevent_req_nterror(req, NT_STATUS_UNEXPECTED_IO_ERROR); |
229 | 0 | return; |
230 | 0 | } |
231 | | |
232 | | /* |
233 | | * bcc field must be valid for small reads, for large reads the 16-bit |
234 | | * bcc field can't be correct. |
235 | | */ |
236 | | |
237 | 0 | if ((state->received < 0xffff) && (state->received > num_bytes)) { |
238 | 0 | DEBUG(5, ("server announced more bytes than sent\n")); |
239 | 0 | tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE); |
240 | 0 | return; |
241 | 0 | } |
242 | | |
243 | 0 | state->buf = discard_const_p(uint8_t, smb_base(inbuf)) + SVAL(vwv+6, 0); |
244 | |
|
245 | 0 | if (smb_buffer_oob(smb_len_tcp(inbuf), SVAL(vwv+6, 0), state->received) |
246 | 0 | || ((state->received != 0) && (state->buf < bytes))) { |
247 | 0 | DEBUG(5, ("server returned invalid read&x data offset\n")); |
248 | 0 | tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE); |
249 | 0 | return; |
250 | 0 | } |
251 | 0 | tevent_req_done(req); |
252 | 0 | } |
253 | | |
254 | | /* |
255 | | * Pull the data out of a finished async read_and_x request. rcvbuf is |
256 | | * talloced from the request, so better make sure that you copy it away before |
257 | | * you talloc_free(req). "rcvbuf" is NOT a talloc_ctx of its own, so do not |
258 | | * talloc_move it! |
259 | | */ |
260 | | |
261 | | NTSTATUS cli_read_andx_recv(struct tevent_req *req, ssize_t *received, |
262 | | uint8_t **rcvbuf) |
263 | 0 | { |
264 | 0 | struct cli_read_andx_state *state = tevent_req_data( |
265 | 0 | req, struct cli_read_andx_state); |
266 | 0 | NTSTATUS status; |
267 | |
|
268 | 0 | if (tevent_req_is_nterror(req, &status)) { |
269 | 0 | return status; |
270 | 0 | } |
271 | 0 | *received = state->received; |
272 | 0 | *rcvbuf = state->buf; |
273 | 0 | return NT_STATUS_OK; |
274 | 0 | } |
275 | | |
276 | | struct cli_pull_chunk; |
277 | | |
278 | | struct cli_pull_state { |
279 | | struct tevent_context *ev; |
280 | | struct cli_state *cli; |
281 | | uint16_t fnum; |
282 | | off_t start_offset; |
283 | | off_t size; |
284 | | |
285 | | NTSTATUS (*sink)(char *buf, size_t n, void *priv); |
286 | | void *priv; |
287 | | |
288 | | size_t chunk_size; |
289 | | off_t next_offset; |
290 | | off_t remaining; |
291 | | |
292 | | /* |
293 | | * How many bytes did we push into "sink"? |
294 | | */ |
295 | | off_t pushed; |
296 | | |
297 | | /* |
298 | | * Outstanding requests |
299 | | * |
300 | | * The maximum is 256: |
301 | | * - which would be a window of 256 MByte |
302 | | * for SMB2 with multi-credit |
303 | | * or smb1 unix extensions. |
304 | | */ |
305 | | uint16_t max_chunks; |
306 | | uint16_t num_chunks; |
307 | | uint16_t num_waiting; |
308 | | struct cli_pull_chunk *chunks; |
309 | | }; |
310 | | |
311 | | struct cli_pull_chunk { |
312 | | struct cli_pull_chunk *prev, *next; |
313 | | struct tevent_req *req;/* This is the main request! Not the subreq */ |
314 | | struct tevent_req *subreq; |
315 | | off_t ofs; |
316 | | uint8_t *buf; |
317 | | size_t total_size; |
318 | | size_t tmp_size; |
319 | | bool done; |
320 | | }; |
321 | | |
322 | | static void cli_pull_setup_chunks(struct tevent_req *req); |
323 | | static void cli_pull_chunk_ship(struct cli_pull_chunk *chunk); |
324 | | static void cli_pull_chunk_done(struct tevent_req *subreq); |
325 | | |
326 | | /* |
327 | | * Parallel read support. |
328 | | * |
329 | | * cli_pull sends as many read&x requests as the server would allow via |
330 | | * max_mux at a time. When replies flow back in, the data is written into |
331 | | * the callback function "sink" in the right order. |
332 | | */ |
333 | | |
334 | | struct tevent_req *cli_pull_send(TALLOC_CTX *mem_ctx, |
335 | | struct tevent_context *ev, |
336 | | struct cli_state *cli, |
337 | | uint16_t fnum, off_t start_offset, |
338 | | off_t size, size_t window_size, |
339 | | NTSTATUS (*sink)(char *buf, size_t n, |
340 | | void *priv), |
341 | | void *priv) |
342 | 0 | { |
343 | 0 | struct tevent_req *req; |
344 | 0 | struct cli_pull_state *state; |
345 | 0 | size_t page_size = 1024; |
346 | 0 | uint64_t tmp64; |
347 | |
|
348 | 0 | req = tevent_req_create(mem_ctx, &state, struct cli_pull_state); |
349 | 0 | if (req == NULL) { |
350 | 0 | return NULL; |
351 | 0 | } |
352 | 0 | state->cli = cli; |
353 | 0 | state->ev = ev; |
354 | 0 | state->fnum = fnum; |
355 | 0 | state->start_offset = start_offset; |
356 | 0 | state->size = size; |
357 | 0 | state->sink = sink; |
358 | 0 | state->priv = priv; |
359 | 0 | state->next_offset = start_offset; |
360 | 0 | state->remaining = size; |
361 | |
|
362 | 0 | if (size == 0) { |
363 | 0 | tevent_req_done(req); |
364 | 0 | return tevent_req_post(req, ev); |
365 | 0 | } |
366 | | |
367 | 0 | if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) { |
368 | 0 | state->chunk_size = smb2cli_conn_max_read_size(cli->conn); |
369 | 0 | } else { |
370 | 0 | state->chunk_size = cli_read_max_bufsize(cli); |
371 | 0 | } |
372 | 0 | if (state->chunk_size > page_size) { |
373 | 0 | state->chunk_size &= ~(page_size - 1); |
374 | 0 | } |
375 | |
|
376 | 0 | if (window_size == 0) { |
377 | | /* |
378 | | * We use 16 MByte as default window size. |
379 | | */ |
380 | 0 | window_size = 16 * 1024 * 1024; |
381 | 0 | } |
382 | |
|
383 | 0 | tmp64 = window_size/state->chunk_size; |
384 | 0 | if ((window_size % state->chunk_size) > 0) { |
385 | 0 | tmp64 += 1; |
386 | 0 | } |
387 | 0 | tmp64 = MAX(tmp64, 1); |
388 | 0 | tmp64 = MIN(tmp64, 256); |
389 | 0 | state->max_chunks = tmp64; |
390 | | |
391 | | /* |
392 | | * We defer the callback because of the complex |
393 | | * substate/subfunction logic |
394 | | */ |
395 | 0 | tevent_req_defer_callback(req, ev); |
396 | |
|
397 | 0 | cli_pull_setup_chunks(req); |
398 | 0 | if (!tevent_req_is_in_progress(req)) { |
399 | 0 | return tevent_req_post(req, ev); |
400 | 0 | } |
401 | | |
402 | 0 | return req; |
403 | 0 | } |
404 | | |
405 | | static void cli_pull_setup_chunks(struct tevent_req *req) |
406 | 0 | { |
407 | 0 | struct cli_pull_state *state = |
408 | 0 | tevent_req_data(req, |
409 | 0 | struct cli_pull_state); |
410 | 0 | struct cli_pull_chunk *chunk, *next = NULL; |
411 | 0 | size_t i; |
412 | |
|
413 | 0 | for (chunk = state->chunks; chunk; chunk = next) { |
414 | | /* |
415 | | * Note that chunk might be removed from this call. |
416 | | */ |
417 | 0 | next = chunk->next; |
418 | 0 | cli_pull_chunk_ship(chunk); |
419 | 0 | if (!tevent_req_is_in_progress(req)) { |
420 | 0 | return; |
421 | 0 | } |
422 | 0 | } |
423 | | |
424 | 0 | for (i = state->num_chunks; i < state->max_chunks; i++) { |
425 | |
|
426 | 0 | if (state->num_waiting > 0) { |
427 | 0 | return; |
428 | 0 | } |
429 | | |
430 | 0 | if (state->remaining == 0) { |
431 | 0 | break; |
432 | 0 | } |
433 | | |
434 | 0 | chunk = talloc_zero(state, struct cli_pull_chunk); |
435 | 0 | if (tevent_req_nomem(chunk, req)) { |
436 | 0 | return; |
437 | 0 | } |
438 | 0 | chunk->req = req; |
439 | 0 | chunk->ofs = state->next_offset; |
440 | 0 | chunk->total_size = MIN(state->remaining, state->chunk_size); |
441 | 0 | state->next_offset += chunk->total_size; |
442 | 0 | state->remaining -= chunk->total_size; |
443 | |
|
444 | 0 | DLIST_ADD_END(state->chunks, chunk); |
445 | 0 | state->num_chunks++; |
446 | 0 | state->num_waiting++; |
447 | |
|
448 | 0 | cli_pull_chunk_ship(chunk); |
449 | 0 | if (!tevent_req_is_in_progress(req)) { |
450 | 0 | return; |
451 | 0 | } |
452 | 0 | } |
453 | | |
454 | 0 | if (state->remaining > 0) { |
455 | 0 | return; |
456 | 0 | } |
457 | | |
458 | 0 | if (state->num_chunks > 0) { |
459 | 0 | return; |
460 | 0 | } |
461 | | |
462 | 0 | tevent_req_done(req); |
463 | 0 | } |
464 | | |
465 | | static void cli_pull_chunk_ship(struct cli_pull_chunk *chunk) |
466 | 0 | { |
467 | 0 | struct tevent_req *req = chunk->req; |
468 | 0 | struct cli_pull_state *state = |
469 | 0 | tevent_req_data(req, |
470 | 0 | struct cli_pull_state); |
471 | 0 | bool ok; |
472 | 0 | off_t ofs; |
473 | 0 | size_t size; |
474 | |
|
475 | 0 | if (chunk->done) { |
476 | 0 | NTSTATUS status; |
477 | |
|
478 | 0 | if (chunk != state->chunks) { |
479 | | /* |
480 | | * this chunk is not the |
481 | | * first one in the list. |
482 | | * |
483 | | * which means we should not |
484 | | * push it into the sink yet. |
485 | | */ |
486 | 0 | return; |
487 | 0 | } |
488 | | |
489 | 0 | if (chunk->tmp_size == 0) { |
490 | | /* |
491 | | * we got a short read, we're done |
492 | | */ |
493 | 0 | tevent_req_done(req); |
494 | 0 | return; |
495 | 0 | } |
496 | | |
497 | 0 | status = state->sink((char *)chunk->buf, |
498 | 0 | chunk->tmp_size, |
499 | 0 | state->priv); |
500 | 0 | if (tevent_req_nterror(req, status)) { |
501 | 0 | return; |
502 | 0 | } |
503 | 0 | state->pushed += chunk->tmp_size; |
504 | |
|
505 | 0 | if (chunk->tmp_size < chunk->total_size) { |
506 | | /* |
507 | | * we got a short read, we're done |
508 | | */ |
509 | 0 | tevent_req_done(req); |
510 | 0 | return; |
511 | 0 | } |
512 | | |
513 | 0 | DLIST_REMOVE(state->chunks, chunk); |
514 | 0 | SMB_ASSERT(state->num_chunks > 0); |
515 | 0 | state->num_chunks--; |
516 | 0 | TALLOC_FREE(chunk); |
517 | |
|
518 | 0 | return; |
519 | 0 | } |
520 | | |
521 | 0 | if (chunk->subreq != NULL) { |
522 | 0 | return; |
523 | 0 | } |
524 | | |
525 | 0 | SMB_ASSERT(state->num_waiting > 0); |
526 | | |
527 | 0 | ofs = chunk->ofs + chunk->tmp_size; |
528 | 0 | size = chunk->total_size - chunk->tmp_size; |
529 | |
|
530 | 0 | if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) { |
531 | 0 | uint32_t max_size; |
532 | |
|
533 | 0 | ok = smb2cli_conn_req_possible(state->cli->conn, &max_size); |
534 | 0 | if (!ok) { |
535 | 0 | return; |
536 | 0 | } |
537 | | |
538 | | /* |
539 | | * downgrade depending on the available credits |
540 | | */ |
541 | 0 | size = MIN(max_size, size); |
542 | |
|
543 | 0 | chunk->subreq = cli_smb2_read_send(chunk, |
544 | 0 | state->ev, |
545 | 0 | state->cli, |
546 | 0 | state->fnum, |
547 | 0 | ofs, |
548 | 0 | size); |
549 | 0 | if (tevent_req_nomem(chunk->subreq, req)) { |
550 | 0 | return; |
551 | 0 | } |
552 | 0 | } else { |
553 | 0 | ok = smb1cli_conn_req_possible(state->cli->conn); |
554 | 0 | if (!ok) { |
555 | 0 | return; |
556 | 0 | } |
557 | | |
558 | 0 | chunk->subreq = cli_read_andx_send(chunk, |
559 | 0 | state->ev, |
560 | 0 | state->cli, |
561 | 0 | state->fnum, |
562 | 0 | ofs, |
563 | 0 | size); |
564 | 0 | if (tevent_req_nomem(chunk->subreq, req)) { |
565 | 0 | return; |
566 | 0 | } |
567 | 0 | } |
568 | 0 | tevent_req_set_callback(chunk->subreq, |
569 | 0 | cli_pull_chunk_done, |
570 | 0 | chunk); |
571 | |
|
572 | 0 | state->num_waiting--; |
573 | 0 | return; |
574 | 0 | } |
575 | | |
576 | | static void cli_pull_chunk_done(struct tevent_req *subreq) |
577 | 0 | { |
578 | 0 | struct cli_pull_chunk *chunk = |
579 | 0 | tevent_req_callback_data(subreq, |
580 | 0 | struct cli_pull_chunk); |
581 | 0 | struct tevent_req *req = chunk->req; |
582 | 0 | struct cli_pull_state *state = |
583 | 0 | tevent_req_data(req, |
584 | 0 | struct cli_pull_state); |
585 | 0 | NTSTATUS status; |
586 | 0 | size_t expected = chunk->total_size - chunk->tmp_size; |
587 | 0 | ssize_t received = 0; |
588 | 0 | uint8_t *buf = NULL; |
589 | |
|
590 | 0 | chunk->subreq = NULL; |
591 | |
|
592 | 0 | if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) { |
593 | 0 | status = cli_smb2_read_recv(subreq, &received, &buf); |
594 | 0 | } else { |
595 | 0 | status = cli_read_andx_recv(subreq, &received, &buf); |
596 | 0 | } |
597 | 0 | if (NT_STATUS_EQUAL(status, NT_STATUS_END_OF_FILE)) { |
598 | 0 | received = 0; |
599 | 0 | status = NT_STATUS_OK; |
600 | 0 | } |
601 | 0 | if (tevent_req_nterror(req, status)) { |
602 | 0 | return; |
603 | 0 | } |
604 | | |
605 | 0 | if (received > expected) { |
606 | 0 | tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE); |
607 | 0 | return; |
608 | 0 | } |
609 | | |
610 | 0 | if (received == 0) { |
611 | | /* |
612 | | * We got EOF we're done |
613 | | */ |
614 | 0 | chunk->done = true; |
615 | 0 | cli_pull_setup_chunks(req); |
616 | 0 | return; |
617 | 0 | } |
618 | | |
619 | 0 | if (received == chunk->total_size) { |
620 | | /* |
621 | | * We got it in the first run. |
622 | | * |
623 | | * We don't call TALLOC_FREE(subreq) |
624 | | * here and keep the returned buffer. |
625 | | */ |
626 | 0 | chunk->buf = buf; |
627 | 0 | } else if (chunk->buf == NULL) { |
628 | 0 | chunk->buf = talloc_array(chunk, uint8_t, chunk->total_size); |
629 | 0 | if (tevent_req_nomem(chunk->buf, req)) { |
630 | 0 | return; |
631 | 0 | } |
632 | 0 | } |
633 | | |
634 | 0 | if (received != chunk->total_size) { |
635 | 0 | uint8_t *p = chunk->buf + chunk->tmp_size; |
636 | 0 | memcpy(p, buf, received); |
637 | 0 | TALLOC_FREE(subreq); |
638 | 0 | } |
639 | |
|
640 | 0 | chunk->tmp_size += received; |
641 | |
|
642 | 0 | if (chunk->tmp_size == chunk->total_size) { |
643 | 0 | chunk->done = true; |
644 | 0 | } else { |
645 | 0 | state->num_waiting++; |
646 | 0 | } |
647 | |
|
648 | 0 | cli_pull_setup_chunks(req); |
649 | 0 | } |
650 | | |
651 | | NTSTATUS cli_pull_recv(struct tevent_req *req, off_t *received) |
652 | 0 | { |
653 | 0 | struct cli_pull_state *state = tevent_req_data( |
654 | 0 | req, struct cli_pull_state); |
655 | 0 | NTSTATUS status; |
656 | |
|
657 | 0 | if (tevent_req_is_nterror(req, &status)) { |
658 | 0 | tevent_req_received(req); |
659 | 0 | return status; |
660 | 0 | } |
661 | 0 | *received = state->pushed; |
662 | 0 | tevent_req_received(req); |
663 | 0 | return NT_STATUS_OK; |
664 | 0 | } |
665 | | |
666 | | NTSTATUS cli_pull(struct cli_state *cli, uint16_t fnum, |
667 | | off_t start_offset, off_t size, size_t window_size, |
668 | | NTSTATUS (*sink)(char *buf, size_t n, void *priv), |
669 | | void *priv, off_t *received) |
670 | 0 | { |
671 | 0 | TALLOC_CTX *frame = talloc_stackframe(); |
672 | 0 | struct tevent_context *ev; |
673 | 0 | struct tevent_req *req; |
674 | 0 | NTSTATUS status = NT_STATUS_OK; |
675 | |
|
676 | 0 | if (smbXcli_conn_has_async_calls(cli->conn)) { |
677 | | /* |
678 | | * Can't use sync call while an async call is in flight |
679 | | */ |
680 | 0 | status = NT_STATUS_INVALID_PARAMETER; |
681 | 0 | goto fail; |
682 | 0 | } |
683 | | |
684 | 0 | ev = samba_tevent_context_init(frame); |
685 | 0 | if (ev == NULL) { |
686 | 0 | status = NT_STATUS_NO_MEMORY; |
687 | 0 | goto fail; |
688 | 0 | } |
689 | | |
690 | 0 | req = cli_pull_send(frame, ev, cli, fnum, start_offset, size, |
691 | 0 | window_size, sink, priv); |
692 | 0 | if (req == NULL) { |
693 | 0 | status = NT_STATUS_NO_MEMORY; |
694 | 0 | goto fail; |
695 | 0 | } |
696 | | |
697 | 0 | if (!tevent_req_poll_ntstatus(req, ev, &status)) { |
698 | 0 | goto fail; |
699 | 0 | } |
700 | | |
701 | 0 | status = cli_pull_recv(req, received); |
702 | 0 | fail: |
703 | 0 | TALLOC_FREE(frame); |
704 | 0 | return status; |
705 | 0 | } |
706 | | |
707 | | struct cli_read_state { |
708 | | struct cli_state *cli; |
709 | | char *buf; |
710 | | size_t buflen; |
711 | | size_t received; |
712 | | }; |
713 | | |
714 | | static void cli_read_done(struct tevent_req *subreq); |
715 | | |
716 | | struct tevent_req *cli_read_send( |
717 | | TALLOC_CTX *mem_ctx, |
718 | | struct tevent_context *ev, |
719 | | struct cli_state *cli, |
720 | | uint16_t fnum, |
721 | | char *buf, |
722 | | off_t offset, |
723 | | size_t size) |
724 | 0 | { |
725 | 0 | struct tevent_req *req, *subreq; |
726 | 0 | struct cli_read_state *state; |
727 | |
|
728 | 0 | req = tevent_req_create(mem_ctx, &state, struct cli_read_state); |
729 | 0 | if (req == NULL) { |
730 | 0 | return NULL; |
731 | 0 | } |
732 | 0 | state->cli = cli; |
733 | 0 | state->buf = buf; |
734 | 0 | state->buflen = size; |
735 | |
|
736 | 0 | if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) { |
737 | 0 | uint32_t max_size; |
738 | 0 | bool ok; |
739 | |
|
740 | 0 | ok = smb2cli_conn_req_possible(state->cli->conn, &max_size); |
741 | 0 | if (!ok) { |
742 | 0 | tevent_req_nterror( |
743 | 0 | req, |
744 | 0 | NT_STATUS_INSUFFICIENT_RESOURCES); |
745 | 0 | return tevent_req_post(req, ev); |
746 | 0 | } |
747 | | |
748 | | /* |
749 | | * downgrade depending on the available credits |
750 | | */ |
751 | 0 | size = MIN(max_size, size); |
752 | |
|
753 | 0 | subreq = cli_smb2_read_send( |
754 | 0 | state, ev, cli, fnum, offset, size); |
755 | 0 | if (tevent_req_nomem(subreq, req)) { |
756 | 0 | return tevent_req_post(req, ev); |
757 | 0 | } |
758 | 0 | } else { |
759 | 0 | bool ok; |
760 | 0 | ok = smb1cli_conn_req_possible(state->cli->conn); |
761 | 0 | if (!ok) { |
762 | 0 | tevent_req_nterror( |
763 | 0 | req, |
764 | 0 | NT_STATUS_INSUFFICIENT_RESOURCES); |
765 | 0 | return tevent_req_post(req, ev); |
766 | 0 | } |
767 | | |
768 | 0 | subreq = cli_read_andx_send( |
769 | 0 | state, ev, cli, fnum, offset, size); |
770 | 0 | if (tevent_req_nomem(subreq, req)) { |
771 | 0 | return tevent_req_post(req, ev); |
772 | 0 | } |
773 | 0 | } |
774 | | |
775 | 0 | tevent_req_set_callback(subreq, cli_read_done, req); |
776 | |
|
777 | 0 | return req; |
778 | 0 | } |
779 | | |
780 | | static void cli_read_done(struct tevent_req *subreq) |
781 | 0 | { |
782 | 0 | struct tevent_req *req = tevent_req_callback_data( |
783 | 0 | subreq, struct tevent_req); |
784 | 0 | struct cli_read_state *state = tevent_req_data( |
785 | 0 | req, struct cli_read_state); |
786 | 0 | NTSTATUS status; |
787 | 0 | ssize_t received; |
788 | 0 | uint8_t *buf = NULL; |
789 | |
|
790 | 0 | if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) { |
791 | 0 | status = cli_smb2_read_recv(subreq, &received, &buf); |
792 | 0 | } else { |
793 | 0 | status = cli_read_andx_recv(subreq, &received, &buf); |
794 | 0 | } |
795 | |
|
796 | 0 | if (NT_STATUS_EQUAL(status, NT_STATUS_END_OF_FILE)) { |
797 | 0 | received = 0; |
798 | 0 | status = NT_STATUS_OK; |
799 | 0 | } |
800 | 0 | if (tevent_req_nterror(req, status)) { |
801 | 0 | return; |
802 | 0 | } |
803 | 0 | if ((buf == NULL) || (received < 0) || (received > state->buflen)) { |
804 | 0 | state->received = 0; |
805 | 0 | tevent_req_nterror(req, NT_STATUS_UNEXPECTED_IO_ERROR); |
806 | 0 | return; |
807 | 0 | } |
808 | | |
809 | 0 | memcpy(state->buf, buf, received); |
810 | 0 | state->received = received; |
811 | 0 | tevent_req_done(req); |
812 | 0 | } |
813 | | |
814 | | NTSTATUS cli_read_recv(struct tevent_req *req, size_t *received) |
815 | 0 | { |
816 | 0 | struct cli_read_state *state = tevent_req_data( |
817 | 0 | req, struct cli_read_state); |
818 | 0 | NTSTATUS status; |
819 | |
|
820 | 0 | if (tevent_req_is_nterror(req, &status)) { |
821 | 0 | return status; |
822 | 0 | } |
823 | 0 | if (received != NULL) { |
824 | 0 | *received = state->received; |
825 | 0 | } |
826 | 0 | return NT_STATUS_OK; |
827 | 0 | } |
828 | | |
829 | | /* |
830 | | * Helper function for cli_pull(). This takes a chunk of data (buf) read from |
831 | | * a remote file and copies it into the return buffer (priv). |
832 | | */ |
833 | | NTSTATUS cli_read_sink(char *buf, size_t n, void *priv) |
834 | 0 | { |
835 | 0 | char **pbuf = (char **)priv; |
836 | 0 | memcpy(*pbuf, buf, n); |
837 | 0 | *pbuf += n; |
838 | 0 | return NT_STATUS_OK; |
839 | 0 | } |
840 | | |
841 | | NTSTATUS cli_read(struct cli_state *cli, uint16_t fnum, |
842 | | char *buf, off_t offset, size_t size, |
843 | | size_t *nread) |
844 | 0 | { |
845 | 0 | NTSTATUS status; |
846 | 0 | off_t ret = 0; |
847 | |
|
848 | 0 | status = cli_pull(cli, fnum, offset, size, size, |
849 | 0 | cli_read_sink, &buf, &ret); |
850 | 0 | if (!NT_STATUS_IS_OK(status)) { |
851 | 0 | return status; |
852 | 0 | } |
853 | | |
854 | 0 | if (nread) { |
855 | 0 | *nread = ret; |
856 | 0 | } |
857 | |
|
858 | 0 | return NT_STATUS_OK; |
859 | 0 | } |
860 | | |
861 | | /* |
862 | | * Send a write&x request |
863 | | */ |
864 | | |
865 | | struct cli_write_andx_state { |
866 | | size_t size; |
867 | | uint16_t vwv[14]; |
868 | | size_t written; |
869 | | uint8_t pad; |
870 | | struct iovec iov[2]; |
871 | | }; |
872 | | |
873 | | static void cli_write_andx_done(struct tevent_req *subreq); |
874 | | |
875 | | struct tevent_req *cli_write_andx_create(TALLOC_CTX *mem_ctx, |
876 | | struct tevent_context *ev, |
877 | | struct cli_state *cli, uint16_t fnum, |
878 | | uint16_t mode, const uint8_t *buf, |
879 | | off_t offset, size_t size, |
880 | | struct tevent_req **reqs_before, |
881 | | int num_reqs_before, |
882 | | struct tevent_req **psmbreq) |
883 | 0 | { |
884 | 0 | struct tevent_req *req, *subreq; |
885 | 0 | struct cli_write_andx_state *state; |
886 | 0 | bool bigoffset = ((smb1cli_conn_capabilities(cli->conn) & CAP_LARGE_FILES) != 0); |
887 | 0 | uint8_t wct = bigoffset ? 14 : 12; |
888 | 0 | size_t max_write = cli_write_max_bufsize(cli, mode, wct); |
889 | 0 | uint16_t *vwv; |
890 | |
|
891 | 0 | req = tevent_req_create(mem_ctx, &state, struct cli_write_andx_state); |
892 | 0 | if (req == NULL) { |
893 | 0 | return NULL; |
894 | 0 | } |
895 | | |
896 | 0 | state->size = MIN(size, max_write); |
897 | |
|
898 | 0 | vwv = state->vwv; |
899 | |
|
900 | 0 | SCVAL(vwv+0, 0, 0xFF); |
901 | 0 | SCVAL(vwv+0, 1, 0); |
902 | 0 | SSVAL(vwv+1, 0, 0); |
903 | 0 | SSVAL(vwv+2, 0, fnum); |
904 | 0 | SIVAL(vwv+3, 0, offset); |
905 | 0 | SIVAL(vwv+5, 0, 0); |
906 | 0 | SSVAL(vwv+7, 0, mode); |
907 | 0 | SSVAL(vwv+8, 0, 0); |
908 | 0 | SSVAL(vwv+9, 0, (state->size>>16)); |
909 | 0 | SSVAL(vwv+10, 0, state->size); |
910 | |
|
911 | 0 | SSVAL(vwv+11, 0, |
912 | 0 | smb1cli_req_wct_ofs(reqs_before, num_reqs_before) |
913 | 0 | + 1 /* the wct field */ |
914 | 0 | + wct * 2 /* vwv */ |
915 | 0 | + 2 /* num_bytes field */ |
916 | 0 | + 1 /* pad */); |
917 | |
|
918 | 0 | if (bigoffset) { |
919 | 0 | SIVAL(vwv+12, 0, (((uint64_t)offset)>>32) & 0xffffffff); |
920 | 0 | } |
921 | |
|
922 | 0 | state->pad = 0; |
923 | 0 | state->iov[0].iov_base = (void *)&state->pad; |
924 | 0 | state->iov[0].iov_len = 1; |
925 | 0 | state->iov[1].iov_base = discard_const_p(void, buf); |
926 | 0 | state->iov[1].iov_len = state->size; |
927 | |
|
928 | 0 | subreq = cli_smb_req_create(state, ev, cli, SMBwriteX, 0, 0, wct, vwv, |
929 | 0 | 2, state->iov); |
930 | 0 | if (tevent_req_nomem(subreq, req)) { |
931 | 0 | return tevent_req_post(req, ev); |
932 | 0 | } |
933 | 0 | tevent_req_set_callback(subreq, cli_write_andx_done, req); |
934 | 0 | *psmbreq = subreq; |
935 | 0 | return req; |
936 | 0 | } |
937 | | |
938 | | struct tevent_req *cli_write_andx_send(TALLOC_CTX *mem_ctx, |
939 | | struct tevent_context *ev, |
940 | | struct cli_state *cli, uint16_t fnum, |
941 | | uint16_t mode, const uint8_t *buf, |
942 | | off_t offset, size_t size) |
943 | 0 | { |
944 | 0 | struct tevent_req *req, *subreq; |
945 | 0 | NTSTATUS status; |
946 | |
|
947 | 0 | req = cli_write_andx_create(mem_ctx, ev, cli, fnum, mode, buf, offset, |
948 | 0 | size, NULL, 0, &subreq); |
949 | 0 | if (req == NULL) { |
950 | 0 | return NULL; |
951 | 0 | } |
952 | | |
953 | 0 | status = smb1cli_req_chain_submit(&subreq, 1); |
954 | 0 | if (tevent_req_nterror(req, status)) { |
955 | 0 | return tevent_req_post(req, ev); |
956 | 0 | } |
957 | 0 | return req; |
958 | 0 | } |
959 | | |
960 | | static void cli_write_andx_done(struct tevent_req *subreq) |
961 | 0 | { |
962 | 0 | struct tevent_req *req = tevent_req_callback_data( |
963 | 0 | subreq, struct tevent_req); |
964 | 0 | struct cli_write_andx_state *state = tevent_req_data( |
965 | 0 | req, struct cli_write_andx_state); |
966 | 0 | uint8_t wct; |
967 | 0 | uint16_t *vwv; |
968 | 0 | NTSTATUS status; |
969 | |
|
970 | 0 | status = cli_smb_recv(subreq, state, NULL, 6, &wct, &vwv, |
971 | 0 | NULL, NULL); |
972 | 0 | TALLOC_FREE(subreq); |
973 | 0 | if (NT_STATUS_IS_ERR(status)) { |
974 | 0 | tevent_req_nterror(req, status); |
975 | 0 | return; |
976 | 0 | } |
977 | 0 | state->written = SVAL(vwv+2, 0); |
978 | 0 | if (state->size > UINT16_MAX) { |
979 | | /* |
980 | | * It is important that we only set the |
981 | | * high bits only if we asked for a large write. |
982 | | * |
983 | | * OS/2 print shares get this wrong and may send |
984 | | * invalid values. |
985 | | * |
986 | | * See bug #5326. |
987 | | */ |
988 | 0 | state->written |= SVAL(vwv+4, 0)<<16; |
989 | 0 | } |
990 | 0 | tevent_req_done(req); |
991 | 0 | } |
992 | | |
993 | | NTSTATUS cli_write_andx_recv(struct tevent_req *req, size_t *pwritten) |
994 | 0 | { |
995 | 0 | struct cli_write_andx_state *state = tevent_req_data( |
996 | 0 | req, struct cli_write_andx_state); |
997 | 0 | NTSTATUS status; |
998 | |
|
999 | 0 | if (tevent_req_is_nterror(req, &status)) { |
1000 | 0 | return status; |
1001 | 0 | } |
1002 | 0 | if (pwritten != 0) { |
1003 | 0 | *pwritten = state->written; |
1004 | 0 | } |
1005 | 0 | return NT_STATUS_OK; |
1006 | 0 | } |
1007 | | |
1008 | | struct cli_write_state { |
1009 | | struct cli_state *cli; |
1010 | | size_t written; |
1011 | | }; |
1012 | | |
1013 | | static void cli_write_done(struct tevent_req *subreq); |
1014 | | |
1015 | | /* |
1016 | | * Used to write to a file remotely. |
1017 | | * This is similar in functionality to cli_push_send(), except this is a more |
1018 | | * finer-grain API. For example, if the data we want to write exceeds the max |
1019 | | * write size of the underlying connection, then it's the caller's |
1020 | | * responsibility to handle this. |
1021 | | * For writing a small amount of data to file, this is a simpler API to use. |
1022 | | */ |
1023 | | struct tevent_req *cli_write_send(TALLOC_CTX *mem_ctx, |
1024 | | struct tevent_context *ev, |
1025 | | struct cli_state *cli, uint16_t fnum, |
1026 | | uint16_t mode, const uint8_t *buf, |
1027 | | off_t offset, size_t size) |
1028 | 0 | { |
1029 | 0 | struct tevent_req *req = NULL; |
1030 | 0 | struct cli_write_state *state = NULL; |
1031 | 0 | struct tevent_req *subreq = NULL; |
1032 | |
|
1033 | 0 | req = tevent_req_create(mem_ctx, &state, struct cli_write_state); |
1034 | 0 | if (req == NULL) { |
1035 | 0 | return NULL; |
1036 | 0 | } |
1037 | 0 | state->cli = cli; |
1038 | |
|
1039 | 0 | if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { |
1040 | 0 | uint32_t max_size; |
1041 | 0 | bool ok; |
1042 | |
|
1043 | 0 | ok = smb2cli_conn_req_possible(state->cli->conn, &max_size); |
1044 | 0 | if (!ok) { |
1045 | 0 | tevent_req_nterror( |
1046 | 0 | req, |
1047 | 0 | NT_STATUS_INSUFFICIENT_RESOURCES); |
1048 | 0 | return tevent_req_post(req, ev); |
1049 | 0 | } |
1050 | | |
1051 | | /* |
1052 | | * downgrade depending on the available credits |
1053 | | */ |
1054 | 0 | size = MIN(max_size, size); |
1055 | |
|
1056 | 0 | subreq = cli_smb2_write_send(state, |
1057 | 0 | ev, |
1058 | 0 | cli, |
1059 | 0 | fnum, |
1060 | 0 | mode, |
1061 | 0 | buf, |
1062 | 0 | offset, |
1063 | 0 | size); |
1064 | 0 | } else { |
1065 | 0 | bool ok; |
1066 | |
|
1067 | 0 | ok = smb1cli_conn_req_possible(state->cli->conn); |
1068 | 0 | if (!ok) { |
1069 | 0 | tevent_req_nterror( |
1070 | 0 | req, |
1071 | 0 | NT_STATUS_INSUFFICIENT_RESOURCES); |
1072 | 0 | return tevent_req_post(req, ev); |
1073 | 0 | } |
1074 | | |
1075 | 0 | subreq = cli_write_andx_send(state, |
1076 | 0 | ev, |
1077 | 0 | cli, |
1078 | 0 | fnum, |
1079 | 0 | mode, |
1080 | 0 | buf, |
1081 | 0 | offset, |
1082 | 0 | size); |
1083 | 0 | } |
1084 | 0 | if (tevent_req_nomem(subreq, req)) { |
1085 | 0 | return tevent_req_post(req, ev); |
1086 | 0 | } |
1087 | 0 | tevent_req_set_callback(subreq, cli_write_done, req); |
1088 | |
|
1089 | 0 | return req; |
1090 | 0 | } |
1091 | | |
1092 | | static void cli_write_done(struct tevent_req *subreq) |
1093 | 0 | { |
1094 | 0 | struct tevent_req *req = |
1095 | 0 | tevent_req_callback_data(subreq, |
1096 | 0 | struct tevent_req); |
1097 | 0 | struct cli_write_state *state = |
1098 | 0 | tevent_req_data(req, |
1099 | 0 | struct cli_write_state); |
1100 | 0 | NTSTATUS status; |
1101 | |
|
1102 | 0 | if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) { |
1103 | 0 | status = cli_smb2_write_recv(subreq, &state->written); |
1104 | 0 | } else { |
1105 | 0 | status = cli_write_andx_recv(subreq, &state->written); |
1106 | 0 | } |
1107 | 0 | TALLOC_FREE(subreq); |
1108 | 0 | if (tevent_req_nterror(req, status)) { |
1109 | 0 | return; |
1110 | 0 | } |
1111 | 0 | tevent_req_done(req); |
1112 | 0 | } |
1113 | | |
1114 | | NTSTATUS cli_write_recv(struct tevent_req *req, size_t *pwritten) |
1115 | 0 | { |
1116 | 0 | struct cli_write_state *state = |
1117 | 0 | tevent_req_data(req, |
1118 | 0 | struct cli_write_state); |
1119 | 0 | NTSTATUS status; |
1120 | |
|
1121 | 0 | if (tevent_req_is_nterror(req, &status)) { |
1122 | 0 | tevent_req_received(req); |
1123 | 0 | return status; |
1124 | 0 | } |
1125 | 0 | if (pwritten != NULL) { |
1126 | 0 | *pwritten = state->written; |
1127 | 0 | } |
1128 | 0 | tevent_req_received(req); |
1129 | 0 | return NT_STATUS_OK; |
1130 | 0 | } |
1131 | | |
1132 | | struct cli_smb1_writeall_state { |
1133 | | struct tevent_context *ev; |
1134 | | struct cli_state *cli; |
1135 | | uint16_t fnum; |
1136 | | uint16_t mode; |
1137 | | const uint8_t *buf; |
1138 | | off_t offset; |
1139 | | size_t size; |
1140 | | size_t written; |
1141 | | }; |
1142 | | |
1143 | | static void cli_smb1_writeall_written(struct tevent_req *req); |
1144 | | |
1145 | | static struct tevent_req *cli_smb1_writeall_send(TALLOC_CTX *mem_ctx, |
1146 | | struct tevent_context *ev, |
1147 | | struct cli_state *cli, |
1148 | | uint16_t fnum, |
1149 | | uint16_t mode, |
1150 | | const uint8_t *buf, |
1151 | | off_t offset, size_t size) |
1152 | 0 | { |
1153 | 0 | struct tevent_req *req, *subreq; |
1154 | 0 | struct cli_smb1_writeall_state *state; |
1155 | |
|
1156 | 0 | req = tevent_req_create(mem_ctx, &state, |
1157 | 0 | struct cli_smb1_writeall_state); |
1158 | 0 | if (req == NULL) { |
1159 | 0 | return NULL; |
1160 | 0 | } |
1161 | 0 | state->ev = ev; |
1162 | 0 | state->cli = cli; |
1163 | 0 | state->fnum = fnum; |
1164 | 0 | state->mode = mode; |
1165 | 0 | state->buf = buf; |
1166 | 0 | state->offset = offset; |
1167 | 0 | state->size = size; |
1168 | 0 | state->written = 0; |
1169 | |
|
1170 | 0 | subreq = cli_write_andx_send(state, state->ev, state->cli, state->fnum, |
1171 | 0 | state->mode, state->buf, state->offset, |
1172 | 0 | state->size); |
1173 | 0 | if (tevent_req_nomem(subreq, req)) { |
1174 | 0 | return tevent_req_post(req, ev); |
1175 | 0 | } |
1176 | 0 | tevent_req_set_callback(subreq, cli_smb1_writeall_written, req); |
1177 | 0 | return req; |
1178 | 0 | } |
1179 | | |
1180 | | static void cli_smb1_writeall_written(struct tevent_req *subreq) |
1181 | 0 | { |
1182 | 0 | struct tevent_req *req = tevent_req_callback_data( |
1183 | 0 | subreq, struct tevent_req); |
1184 | 0 | struct cli_smb1_writeall_state *state = tevent_req_data( |
1185 | 0 | req, struct cli_smb1_writeall_state); |
1186 | 0 | NTSTATUS status; |
1187 | 0 | size_t written = 0, to_write; |
1188 | |
|
1189 | 0 | status = cli_write_andx_recv(subreq, &written); |
1190 | 0 | TALLOC_FREE(subreq); |
1191 | 0 | if (tevent_req_nterror(req, status)) { |
1192 | 0 | return; |
1193 | 0 | } |
1194 | | |
1195 | 0 | state->written += written; |
1196 | |
|
1197 | 0 | if (state->written > state->size) { |
1198 | 0 | tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE); |
1199 | 0 | return; |
1200 | 0 | } |
1201 | | |
1202 | 0 | to_write = state->size - state->written; |
1203 | |
|
1204 | 0 | if (to_write == 0) { |
1205 | 0 | tevent_req_done(req); |
1206 | 0 | return; |
1207 | 0 | } |
1208 | | |
1209 | 0 | subreq = cli_write_andx_send(state, state->ev, state->cli, state->fnum, |
1210 | 0 | state->mode, |
1211 | 0 | state->buf + state->written, |
1212 | 0 | state->offset + state->written, to_write); |
1213 | 0 | if (tevent_req_nomem(subreq, req)) { |
1214 | 0 | return; |
1215 | 0 | } |
1216 | 0 | tevent_req_set_callback(subreq, cli_smb1_writeall_written, req); |
1217 | 0 | } |
1218 | | |
1219 | | static NTSTATUS cli_smb1_writeall_recv(struct tevent_req *req, |
1220 | | size_t *pwritten) |
1221 | 0 | { |
1222 | 0 | struct cli_smb1_writeall_state *state = tevent_req_data( |
1223 | 0 | req, struct cli_smb1_writeall_state); |
1224 | 0 | NTSTATUS status; |
1225 | |
|
1226 | 0 | if (tevent_req_is_nterror(req, &status)) { |
1227 | 0 | return status; |
1228 | 0 | } |
1229 | 0 | if (pwritten != NULL) { |
1230 | 0 | *pwritten = state->written; |
1231 | 0 | } |
1232 | 0 | return NT_STATUS_OK; |
1233 | 0 | } |
1234 | | |
1235 | | struct cli_writeall_state { |
1236 | | struct cli_state *cli; |
1237 | | size_t written; |
1238 | | }; |
1239 | | |
1240 | | static void cli_writeall_done(struct tevent_req *subreq); |
1241 | | |
1242 | | struct tevent_req *cli_writeall_send( |
1243 | | TALLOC_CTX *mem_ctx, |
1244 | | struct tevent_context *ev, |
1245 | | struct cli_state *cli, |
1246 | | uint16_t fnum, |
1247 | | uint16_t mode, |
1248 | | const uint8_t *buf, |
1249 | | off_t offset, |
1250 | | size_t size) |
1251 | 0 | { |
1252 | 0 | struct tevent_req *req, *subreq; |
1253 | 0 | struct cli_writeall_state *state; |
1254 | |
|
1255 | 0 | req = tevent_req_create(mem_ctx, &state, struct cli_writeall_state); |
1256 | 0 | if (req == NULL) { |
1257 | 0 | return NULL; |
1258 | 0 | } |
1259 | 0 | state->cli = cli; |
1260 | |
|
1261 | 0 | if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { |
1262 | 0 | subreq = cli_smb2_writeall_send( |
1263 | 0 | state, |
1264 | 0 | ev, |
1265 | 0 | cli, |
1266 | 0 | fnum, |
1267 | 0 | mode, |
1268 | 0 | buf, |
1269 | 0 | offset, |
1270 | 0 | size); |
1271 | 0 | } else { |
1272 | 0 | subreq = cli_smb1_writeall_send( |
1273 | 0 | state, |
1274 | 0 | ev, |
1275 | 0 | cli, |
1276 | 0 | fnum, |
1277 | 0 | mode, |
1278 | 0 | buf, |
1279 | 0 | offset, |
1280 | 0 | size); |
1281 | 0 | } |
1282 | |
|
1283 | 0 | if (tevent_req_nomem(subreq, req)) { |
1284 | 0 | return tevent_req_post(req, ev); |
1285 | 0 | } |
1286 | 0 | tevent_req_set_callback(subreq, cli_writeall_done, req); |
1287 | |
|
1288 | 0 | return req; |
1289 | 0 | } |
1290 | | |
1291 | | static void cli_writeall_done(struct tevent_req *subreq) |
1292 | 0 | { |
1293 | 0 | struct tevent_req *req = tevent_req_callback_data( |
1294 | 0 | subreq, struct tevent_req); |
1295 | 0 | struct cli_writeall_state *state = tevent_req_data( |
1296 | 0 | req, struct cli_writeall_state); |
1297 | 0 | NTSTATUS status; |
1298 | |
|
1299 | 0 | if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) { |
1300 | 0 | status = cli_smb2_writeall_recv(subreq, &state->written); |
1301 | 0 | } else { |
1302 | 0 | status = cli_smb1_writeall_recv(subreq, &state->written); |
1303 | 0 | } |
1304 | 0 | TALLOC_FREE(subreq); |
1305 | 0 | if (tevent_req_nterror(req, status)) { |
1306 | 0 | return; |
1307 | 0 | } |
1308 | 0 | tevent_req_done(req); |
1309 | 0 | } |
1310 | | |
1311 | | NTSTATUS cli_writeall_recv(struct tevent_req *req, size_t *pwritten) |
1312 | 0 | { |
1313 | 0 | struct cli_writeall_state *state = tevent_req_data( |
1314 | 0 | req, struct cli_writeall_state); |
1315 | 0 | NTSTATUS status; |
1316 | |
|
1317 | 0 | if (tevent_req_is_nterror(req, &status)) { |
1318 | 0 | return status; |
1319 | 0 | } |
1320 | 0 | if (pwritten != NULL) { |
1321 | 0 | *pwritten = state->written; |
1322 | 0 | } |
1323 | 0 | return NT_STATUS_OK; |
1324 | 0 | } |
1325 | | |
1326 | | |
1327 | | NTSTATUS cli_writeall(struct cli_state *cli, uint16_t fnum, uint16_t mode, |
1328 | | const uint8_t *buf, off_t offset, size_t size, |
1329 | | size_t *pwritten) |
1330 | 0 | { |
1331 | 0 | TALLOC_CTX *frame = talloc_stackframe(); |
1332 | 0 | struct tevent_context *ev; |
1333 | 0 | struct tevent_req *req; |
1334 | 0 | NTSTATUS status = NT_STATUS_NO_MEMORY; |
1335 | |
|
1336 | 0 | if (smbXcli_conn_has_async_calls(cli->conn)) { |
1337 | | /* |
1338 | | * Can't use sync call while an async call is in flight |
1339 | | */ |
1340 | 0 | status = NT_STATUS_INVALID_PARAMETER; |
1341 | 0 | goto fail; |
1342 | 0 | } |
1343 | 0 | ev = samba_tevent_context_init(frame); |
1344 | 0 | if (ev == NULL) { |
1345 | 0 | goto fail; |
1346 | 0 | } |
1347 | 0 | req = cli_writeall_send(frame, ev, cli, fnum, mode, buf, offset, size); |
1348 | 0 | if (req == NULL) { |
1349 | 0 | goto fail; |
1350 | 0 | } |
1351 | 0 | if (!tevent_req_poll_ntstatus(req, ev, &status)) { |
1352 | 0 | goto fail; |
1353 | 0 | } |
1354 | 0 | status = cli_writeall_recv(req, pwritten); |
1355 | 0 | fail: |
1356 | 0 | TALLOC_FREE(frame); |
1357 | 0 | return status; |
1358 | 0 | } |
1359 | | |
1360 | | struct cli_push_chunk; |
1361 | | |
1362 | | struct cli_push_state { |
1363 | | struct tevent_context *ev; |
1364 | | struct cli_state *cli; |
1365 | | uint16_t fnum; |
1366 | | uint16_t mode; |
1367 | | off_t start_offset; |
1368 | | |
1369 | | size_t (*source)(uint8_t *buf, size_t n, void *priv); |
1370 | | void *priv; |
1371 | | |
1372 | | bool eof; |
1373 | | |
1374 | | size_t chunk_size; |
1375 | | off_t next_offset; |
1376 | | |
1377 | | /* |
1378 | | * Outstanding requests |
1379 | | * |
1380 | | * The maximum is 256: |
1381 | | * - which would be a window of 256 MByte |
1382 | | * for SMB2 with multi-credit |
1383 | | * or smb1 unix extensions. |
1384 | | */ |
1385 | | uint16_t max_chunks; |
1386 | | uint16_t num_chunks; |
1387 | | uint16_t num_waiting; |
1388 | | struct cli_push_chunk *chunks; |
1389 | | }; |
1390 | | |
1391 | | struct cli_push_chunk { |
1392 | | struct cli_push_chunk *prev, *next; |
1393 | | struct tevent_req *req;/* This is the main request! Not the subreq */ |
1394 | | struct tevent_req *subreq; |
1395 | | off_t ofs; |
1396 | | uint8_t *buf; |
1397 | | size_t total_size; |
1398 | | size_t tmp_size; |
1399 | | bool done; |
1400 | | }; |
1401 | | |
1402 | | static void cli_push_setup_chunks(struct tevent_req *req); |
1403 | | static void cli_push_chunk_ship(struct cli_push_chunk *chunk); |
1404 | | static void cli_push_chunk_done(struct tevent_req *subreq); |
1405 | | |
1406 | | /* |
1407 | | * Used to write to a file remotely. |
1408 | | * This is similar in functionality to cli_write_send(), except this API |
1409 | | * handles writing a large file by breaking the data into chunks (so we don't |
1410 | | * exceed the max write size of the underlying connection). To do this, the |
1411 | | * (*source) callback handles copying the underlying file data into a message |
1412 | | * buffer, one chunk at a time. |
1413 | | * This API is recommended when writing a potentially large amount of data, |
1414 | | * e.g. when copying a file (or doing a 'put'). |
1415 | | */ |
1416 | | struct tevent_req *cli_push_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, |
1417 | | struct cli_state *cli, |
1418 | | uint16_t fnum, uint16_t mode, |
1419 | | off_t start_offset, size_t window_size, |
1420 | | size_t (*source)(uint8_t *buf, size_t n, |
1421 | | void *priv), |
1422 | | void *priv) |
1423 | 0 | { |
1424 | 0 | struct tevent_req *req; |
1425 | 0 | struct cli_push_state *state; |
1426 | 0 | size_t page_size = 1024; |
1427 | 0 | uint64_t tmp64; |
1428 | |
|
1429 | 0 | req = tevent_req_create(mem_ctx, &state, struct cli_push_state); |
1430 | 0 | if (req == NULL) { |
1431 | 0 | return NULL; |
1432 | 0 | } |
1433 | 0 | state->cli = cli; |
1434 | 0 | state->ev = ev; |
1435 | 0 | state->fnum = fnum; |
1436 | 0 | state->start_offset = start_offset; |
1437 | 0 | state->mode = mode; |
1438 | 0 | state->source = source; |
1439 | 0 | state->priv = priv; |
1440 | 0 | state->next_offset = start_offset; |
1441 | |
|
1442 | 0 | if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) { |
1443 | 0 | state->chunk_size = smb2cli_conn_max_write_size(cli->conn); |
1444 | 0 | } else { |
1445 | 0 | state->chunk_size = cli_write_max_bufsize(cli, mode, 14); |
1446 | 0 | } |
1447 | 0 | if (state->chunk_size > page_size) { |
1448 | 0 | state->chunk_size &= ~(page_size - 1); |
1449 | 0 | } |
1450 | |
|
1451 | 0 | if (window_size == 0) { |
1452 | | /* |
1453 | | * We use 16 MByte as default window size. |
1454 | | */ |
1455 | 0 | window_size = 16 * 1024 * 1024; |
1456 | 0 | } |
1457 | |
|
1458 | 0 | tmp64 = window_size/state->chunk_size; |
1459 | 0 | if ((window_size % state->chunk_size) > 0) { |
1460 | 0 | tmp64 += 1; |
1461 | 0 | } |
1462 | 0 | tmp64 = MAX(tmp64, 1); |
1463 | 0 | tmp64 = MIN(tmp64, 256); |
1464 | 0 | state->max_chunks = tmp64; |
1465 | | |
1466 | | /* |
1467 | | * We defer the callback because of the complex |
1468 | | * substate/subfunction logic |
1469 | | */ |
1470 | 0 | tevent_req_defer_callback(req, ev); |
1471 | |
|
1472 | 0 | cli_push_setup_chunks(req); |
1473 | 0 | if (!tevent_req_is_in_progress(req)) { |
1474 | 0 | return tevent_req_post(req, ev); |
1475 | 0 | } |
1476 | | |
1477 | 0 | return req; |
1478 | 0 | } |
1479 | | |
1480 | | static void cli_push_setup_chunks(struct tevent_req *req) |
1481 | 0 | { |
1482 | 0 | struct cli_push_state *state = |
1483 | 0 | tevent_req_data(req, |
1484 | 0 | struct cli_push_state); |
1485 | 0 | struct cli_push_chunk *chunk, *next = NULL; |
1486 | 0 | size_t i; |
1487 | |
|
1488 | 0 | for (chunk = state->chunks; chunk; chunk = next) { |
1489 | | /* |
1490 | | * Note that chunk might be removed from this call. |
1491 | | */ |
1492 | 0 | next = chunk->next; |
1493 | 0 | cli_push_chunk_ship(chunk); |
1494 | 0 | if (!tevent_req_is_in_progress(req)) { |
1495 | 0 | return; |
1496 | 0 | } |
1497 | 0 | } |
1498 | | |
1499 | 0 | for (i = state->num_chunks; i < state->max_chunks; i++) { |
1500 | |
|
1501 | 0 | if (state->num_waiting > 0) { |
1502 | 0 | return; |
1503 | 0 | } |
1504 | | |
1505 | 0 | if (state->eof) { |
1506 | 0 | break; |
1507 | 0 | } |
1508 | | |
1509 | 0 | chunk = talloc_zero(state, struct cli_push_chunk); |
1510 | 0 | if (tevent_req_nomem(chunk, req)) { |
1511 | 0 | return; |
1512 | 0 | } |
1513 | 0 | chunk->req = req; |
1514 | 0 | chunk->ofs = state->next_offset; |
1515 | 0 | chunk->buf = talloc_array(chunk, |
1516 | 0 | uint8_t, |
1517 | 0 | state->chunk_size); |
1518 | 0 | if (tevent_req_nomem(chunk->buf, req)) { |
1519 | 0 | return; |
1520 | 0 | } |
1521 | 0 | chunk->total_size = state->source(chunk->buf, |
1522 | 0 | state->chunk_size, |
1523 | 0 | state->priv); |
1524 | 0 | if (chunk->total_size == 0) { |
1525 | | /* nothing to send */ |
1526 | 0 | talloc_free(chunk); |
1527 | 0 | state->eof = true; |
1528 | 0 | break; |
1529 | 0 | } |
1530 | 0 | state->next_offset += chunk->total_size; |
1531 | |
|
1532 | 0 | DLIST_ADD_END(state->chunks, chunk); |
1533 | 0 | state->num_chunks++; |
1534 | 0 | state->num_waiting++; |
1535 | |
|
1536 | 0 | cli_push_chunk_ship(chunk); |
1537 | 0 | if (!tevent_req_is_in_progress(req)) { |
1538 | 0 | return; |
1539 | 0 | } |
1540 | 0 | } |
1541 | | |
1542 | 0 | if (!state->eof) { |
1543 | 0 | return; |
1544 | 0 | } |
1545 | | |
1546 | 0 | if (state->num_chunks > 0) { |
1547 | 0 | return; |
1548 | 0 | } |
1549 | | |
1550 | 0 | tevent_req_done(req); |
1551 | 0 | } |
1552 | | |
1553 | | static void cli_push_chunk_ship(struct cli_push_chunk *chunk) |
1554 | 0 | { |
1555 | 0 | struct tevent_req *req = chunk->req; |
1556 | 0 | struct cli_push_state *state = |
1557 | 0 | tevent_req_data(req, |
1558 | 0 | struct cli_push_state); |
1559 | 0 | bool ok; |
1560 | 0 | const uint8_t *buf; |
1561 | 0 | off_t ofs; |
1562 | 0 | size_t size; |
1563 | |
|
1564 | 0 | if (chunk->done) { |
1565 | 0 | DLIST_REMOVE(state->chunks, chunk); |
1566 | 0 | SMB_ASSERT(state->num_chunks > 0); |
1567 | 0 | state->num_chunks--; |
1568 | 0 | TALLOC_FREE(chunk); |
1569 | |
|
1570 | 0 | return; |
1571 | 0 | } |
1572 | | |
1573 | 0 | if (chunk->subreq != NULL) { |
1574 | 0 | return; |
1575 | 0 | } |
1576 | | |
1577 | 0 | SMB_ASSERT(state->num_waiting > 0); |
1578 | | |
1579 | 0 | buf = chunk->buf + chunk->tmp_size; |
1580 | 0 | ofs = chunk->ofs + chunk->tmp_size; |
1581 | 0 | size = chunk->total_size - chunk->tmp_size; |
1582 | |
|
1583 | 0 | if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) { |
1584 | 0 | uint32_t max_size; |
1585 | |
|
1586 | 0 | ok = smb2cli_conn_req_possible(state->cli->conn, &max_size); |
1587 | 0 | if (!ok) { |
1588 | 0 | return; |
1589 | 0 | } |
1590 | | |
1591 | | /* |
1592 | | * downgrade depending on the available credits |
1593 | | */ |
1594 | 0 | size = MIN(max_size, size); |
1595 | |
|
1596 | 0 | chunk->subreq = cli_smb2_write_send(chunk, |
1597 | 0 | state->ev, |
1598 | 0 | state->cli, |
1599 | 0 | state->fnum, |
1600 | 0 | state->mode, |
1601 | 0 | buf, |
1602 | 0 | ofs, |
1603 | 0 | size); |
1604 | 0 | if (tevent_req_nomem(chunk->subreq, req)) { |
1605 | 0 | return; |
1606 | 0 | } |
1607 | 0 | } else { |
1608 | 0 | ok = smb1cli_conn_req_possible(state->cli->conn); |
1609 | 0 | if (!ok) { |
1610 | 0 | return; |
1611 | 0 | } |
1612 | | |
1613 | 0 | chunk->subreq = cli_write_andx_send(chunk, |
1614 | 0 | state->ev, |
1615 | 0 | state->cli, |
1616 | 0 | state->fnum, |
1617 | 0 | state->mode, |
1618 | 0 | buf, |
1619 | 0 | ofs, |
1620 | 0 | size); |
1621 | 0 | if (tevent_req_nomem(chunk->subreq, req)) { |
1622 | 0 | return; |
1623 | 0 | } |
1624 | 0 | } |
1625 | 0 | tevent_req_set_callback(chunk->subreq, |
1626 | 0 | cli_push_chunk_done, |
1627 | 0 | chunk); |
1628 | |
|
1629 | 0 | state->num_waiting--; |
1630 | 0 | return; |
1631 | 0 | } |
1632 | | |
1633 | | static void cli_push_chunk_done(struct tevent_req *subreq) |
1634 | 0 | { |
1635 | 0 | struct cli_push_chunk *chunk = |
1636 | 0 | tevent_req_callback_data(subreq, |
1637 | 0 | struct cli_push_chunk); |
1638 | 0 | struct tevent_req *req = chunk->req; |
1639 | 0 | struct cli_push_state *state = |
1640 | 0 | tevent_req_data(req, |
1641 | 0 | struct cli_push_state); |
1642 | 0 | NTSTATUS status; |
1643 | 0 | size_t expected = chunk->total_size - chunk->tmp_size; |
1644 | 0 | size_t written; |
1645 | |
|
1646 | 0 | chunk->subreq = NULL; |
1647 | |
|
1648 | 0 | if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) { |
1649 | 0 | status = cli_smb2_write_recv(subreq, &written); |
1650 | 0 | } else { |
1651 | 0 | status = cli_write_andx_recv(subreq, &written); |
1652 | 0 | } |
1653 | 0 | TALLOC_FREE(subreq); |
1654 | 0 | if (tevent_req_nterror(req, status)) { |
1655 | 0 | return; |
1656 | 0 | } |
1657 | | |
1658 | 0 | if (written > expected) { |
1659 | 0 | tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE); |
1660 | 0 | return; |
1661 | 0 | } |
1662 | | |
1663 | 0 | if (written == 0) { |
1664 | 0 | tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE); |
1665 | 0 | return; |
1666 | 0 | } |
1667 | | |
1668 | 0 | chunk->tmp_size += written; |
1669 | |
|
1670 | 0 | if (chunk->tmp_size == chunk->total_size) { |
1671 | 0 | chunk->done = true; |
1672 | 0 | } else { |
1673 | 0 | state->num_waiting++; |
1674 | 0 | } |
1675 | |
|
1676 | 0 | cli_push_setup_chunks(req); |
1677 | 0 | } |
1678 | | |
1679 | | NTSTATUS cli_push_recv(struct tevent_req *req) |
1680 | 0 | { |
1681 | 0 | return tevent_req_simple_recv_ntstatus(req); |
1682 | 0 | } |
1683 | | |
1684 | | NTSTATUS cli_push(struct cli_state *cli, uint16_t fnum, uint16_t mode, |
1685 | | off_t start_offset, size_t window_size, |
1686 | | size_t (*source)(uint8_t *buf, size_t n, void *priv), |
1687 | | void *priv) |
1688 | 0 | { |
1689 | 0 | TALLOC_CTX *frame = talloc_stackframe(); |
1690 | 0 | struct tevent_context *ev; |
1691 | 0 | struct tevent_req *req; |
1692 | 0 | NTSTATUS status = NT_STATUS_OK; |
1693 | |
|
1694 | 0 | if (smbXcli_conn_has_async_calls(cli->conn)) { |
1695 | | /* |
1696 | | * Can't use sync call while an async call is in flight |
1697 | | */ |
1698 | 0 | status = NT_STATUS_INVALID_PARAMETER; |
1699 | 0 | goto fail; |
1700 | 0 | } |
1701 | | |
1702 | 0 | ev = samba_tevent_context_init(frame); |
1703 | 0 | if (ev == NULL) { |
1704 | 0 | status = NT_STATUS_NO_MEMORY; |
1705 | 0 | goto fail; |
1706 | 0 | } |
1707 | | |
1708 | 0 | req = cli_push_send(frame, ev, cli, fnum, mode, start_offset, |
1709 | 0 | window_size, source, priv); |
1710 | 0 | if (req == NULL) { |
1711 | 0 | status = NT_STATUS_NO_MEMORY; |
1712 | 0 | goto fail; |
1713 | 0 | } |
1714 | | |
1715 | 0 | if (!tevent_req_poll_ntstatus(req, ev, &status)) { |
1716 | 0 | goto fail; |
1717 | 0 | } |
1718 | | |
1719 | 0 | status = cli_push_recv(req); |
1720 | 0 | fail: |
1721 | 0 | TALLOC_FREE(frame); |
1722 | 0 | return status; |
1723 | 0 | } |
1724 | | |
1725 | | #define SPLICE_BLOCK_SIZE 1024 * 1024 |
1726 | | |
1727 | | static NTSTATUS cli_splice_fallback(TALLOC_CTX *frame, |
1728 | | struct cli_state *srccli, |
1729 | | struct cli_state *dstcli, |
1730 | | uint16_t src_fnum, uint16_t dst_fnum, |
1731 | | off_t initial_size, |
1732 | | off_t src_offset, off_t dst_offset, |
1733 | | off_t *written, |
1734 | | int (*splice_cb)(off_t n, void *priv), |
1735 | | void *priv) |
1736 | 0 | { |
1737 | 0 | NTSTATUS status; |
1738 | 0 | uint8_t *buf = talloc_size(frame, SPLICE_BLOCK_SIZE); |
1739 | 0 | size_t nread; |
1740 | 0 | off_t remaining = initial_size; |
1741 | 0 | *written = 0; |
1742 | |
|
1743 | 0 | while (remaining) { |
1744 | 0 | size_t to_read = MIN(remaining, SPLICE_BLOCK_SIZE); |
1745 | |
|
1746 | 0 | status = cli_read(srccli, src_fnum, |
1747 | 0 | (char *)buf, src_offset, to_read, |
1748 | 0 | &nread); |
1749 | 0 | if (!NT_STATUS_IS_OK(status)) { |
1750 | 0 | return status; |
1751 | 0 | } |
1752 | | |
1753 | 0 | status = cli_writeall(dstcli, dst_fnum, 0, |
1754 | 0 | buf, dst_offset, nread, NULL); |
1755 | 0 | if (!NT_STATUS_IS_OK(status)) { |
1756 | 0 | return status; |
1757 | 0 | } |
1758 | | |
1759 | 0 | if ((src_offset > INT64_MAX - nread) || |
1760 | 0 | (dst_offset > INT64_MAX - nread)) { |
1761 | 0 | return NT_STATUS_FILE_TOO_LARGE; |
1762 | 0 | } |
1763 | 0 | src_offset += nread; |
1764 | 0 | dst_offset += nread; |
1765 | 0 | *written += nread; |
1766 | 0 | if (remaining < nread) { |
1767 | 0 | return NT_STATUS_INTERNAL_ERROR; |
1768 | 0 | } |
1769 | 0 | remaining -= nread; |
1770 | 0 | if (!splice_cb(initial_size - remaining, priv)) { |
1771 | 0 | return NT_STATUS_CANCELLED; |
1772 | 0 | } |
1773 | 0 | } |
1774 | | |
1775 | 0 | return NT_STATUS_OK; |
1776 | 0 | } |
1777 | | |
1778 | | NTSTATUS cli_splice(struct cli_state *srccli, struct cli_state *dstcli, |
1779 | | uint16_t src_fnum, uint16_t dst_fnum, |
1780 | | off_t size, |
1781 | | off_t src_offset, off_t dst_offset, |
1782 | | off_t *written, |
1783 | | int (*splice_cb)(off_t n, void *priv), void *priv) |
1784 | 0 | { |
1785 | 0 | TALLOC_CTX *frame = talloc_stackframe(); |
1786 | 0 | struct tevent_context *ev; |
1787 | 0 | struct tevent_req *req; |
1788 | 0 | NTSTATUS status = NT_STATUS_NO_MEMORY; |
1789 | 0 | bool retry_fallback = false; |
1790 | |
|
1791 | 0 | if (smbXcli_conn_has_async_calls(srccli->conn) || |
1792 | 0 | smbXcli_conn_has_async_calls(dstcli->conn)) |
1793 | 0 | { |
1794 | | /* |
1795 | | * Can't use sync call while an async call is in flight |
1796 | | */ |
1797 | 0 | status = NT_STATUS_INVALID_PARAMETER; |
1798 | 0 | goto out; |
1799 | 0 | } |
1800 | | |
1801 | 0 | do { |
1802 | 0 | ev = samba_tevent_context_init(frame); |
1803 | 0 | if (ev == NULL) { |
1804 | 0 | goto out; |
1805 | 0 | } |
1806 | 0 | if (srccli == dstcli && |
1807 | 0 | smbXcli_conn_protocol(srccli->conn) >= PROTOCOL_SMB2_02 && |
1808 | 0 | !retry_fallback) |
1809 | 0 | { |
1810 | 0 | req = cli_smb2_splice_send(frame, ev, |
1811 | 0 | srccli, src_fnum, dst_fnum, |
1812 | 0 | size, src_offset, dst_offset, |
1813 | 0 | splice_cb, priv); |
1814 | 0 | } else { |
1815 | 0 | status = cli_splice_fallback(frame, |
1816 | 0 | srccli, dstcli, |
1817 | 0 | src_fnum, dst_fnum, |
1818 | 0 | size, |
1819 | 0 | src_offset, dst_offset, |
1820 | 0 | written, |
1821 | 0 | splice_cb, priv); |
1822 | 0 | goto out; |
1823 | 0 | } |
1824 | 0 | if (req == NULL) { |
1825 | 0 | goto out; |
1826 | 0 | } |
1827 | 0 | if (!tevent_req_poll(req, ev)) { |
1828 | 0 | status = map_nt_error_from_unix(errno); |
1829 | 0 | goto out; |
1830 | 0 | } |
1831 | 0 | status = cli_smb2_splice_recv(req, written); |
1832 | | |
1833 | | /* |
1834 | | * Older versions of Samba don't support |
1835 | | * FSCTL_SRV_COPYCHUNK_WRITE so use the fallback. |
1836 | | */ |
1837 | 0 | retry_fallback = NT_STATUS_EQUAL(status, NT_STATUS_INVALID_DEVICE_REQUEST); |
1838 | 0 | } while (retry_fallback); |
1839 | | |
1840 | 0 | out: |
1841 | 0 | TALLOC_FREE(frame); |
1842 | 0 | return status; |
1843 | 0 | } |