Add an entry for the "check" command to the tdbtool manpage.
[Samba/gebeck_regimport.git] / source3 / libsmb / clireadwrite.c
blob1c2a0d56c4cda7919a51d6b066374f54f8b935e7
1 /*
2 Unix SMB/CIFS implementation.
3 client file read/write routines
4 Copyright (C) Andrew Tridgell 1994-1998
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.
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.
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/>.
20 #include "includes.h"
22 /****************************************************************************
23 Calculate the recommended read buffer size
24 ****************************************************************************/
25 static size_t cli_read_max_bufsize(struct cli_state *cli)
27 if (!client_is_signing_on(cli) && !cli_encryption_on(cli)
28 && (cli->posix_capabilities & CIFS_UNIX_LARGE_READ_CAP)) {
29 return CLI_SAMBA_MAX_POSIX_LARGE_READX_SIZE;
31 if (cli->capabilities & CAP_LARGE_READX) {
32 return cli->is_samba
33 ? CLI_SAMBA_MAX_LARGE_READX_SIZE
34 : CLI_WINDOWS_MAX_LARGE_READX_SIZE;
36 return (cli->max_xmit - (smb_size+32)) & ~1023;
40 * Send a read&x request
43 struct async_req *cli_read_andx_send(TALLOC_CTX *mem_ctx,
44 struct event_context *ev,
45 struct cli_state *cli, int fnum,
46 off_t offset, size_t size)
48 struct async_req *result;
49 struct cli_request *req;
50 bool bigoffset = False;
52 uint16_t vwv[12];
53 uint8_t wct = 10;
55 if (size > cli_read_max_bufsize(cli)) {
56 DEBUG(0, ("cli_read_andx_send got size=%d, can only handle "
57 "size=%d\n", (int)size,
58 (int)cli_read_max_bufsize(cli)));
59 return NULL;
62 SCVAL(vwv + 0, 0, 0xFF);
63 SCVAL(vwv + 0, 1, 0);
64 SSVAL(vwv + 1, 0, 0);
65 SSVAL(vwv + 2, 0, fnum);
66 SIVAL(vwv + 3, 0, offset);
67 SSVAL(vwv + 5, 0, size);
68 SSVAL(vwv + 6, 0, size);
69 SSVAL(vwv + 7, 0, (size >> 16));
70 SSVAL(vwv + 8, 0, 0);
71 SSVAL(vwv + 9, 0, 0);
73 if ((uint64_t)offset >> 32) {
74 bigoffset = True;
75 SIVAL(vwv + 10, 0,
76 (((uint64_t)offset)>>32) & 0xffffffff);
77 wct += 2;
80 result = cli_request_send(mem_ctx, ev, cli, SMBreadX, 0, wct, vwv,
81 0, NULL);
82 if (result == NULL) {
83 return NULL;
86 req = talloc_get_type_abort(result->private_data, struct cli_request);
88 req->data.read.ofs = offset;
89 req->data.read.size = size;
90 req->data.read.received = 0;
91 req->data.read.rcvbuf = NULL;
93 return result;
97 * Pull the data out of a finished async read_and_x request. rcvbuf is
98 * talloced from the request, so better make sure that you copy it away before
99 * you talloc_free(req). "rcvbuf" is NOT a talloc_ctx of its own, so do not
100 * talloc_move it!
103 NTSTATUS cli_read_andx_recv(struct async_req *req, ssize_t *received,
104 uint8_t **rcvbuf)
106 struct cli_request *cli_req = talloc_get_type_abort(
107 req->private_data, struct cli_request);
108 uint8_t wct;
109 uint16_t *vwv;
110 uint16_t num_bytes;
111 uint8_t *bytes;
112 uint8_t *buf;
113 NTSTATUS status;
114 size_t size;
116 if (async_req_is_error(req, &status)) {
117 return status;
120 status = cli_pull_reply(req, &wct, &vwv, &num_bytes, &bytes);
122 if (NT_STATUS_IS_ERR(status)) {
123 return status;
126 if (wct < 12) {
127 return NT_STATUS_INVALID_NETWORK_RESPONSE;
130 /* size is the number of bytes the server returned.
131 * Might be zero. */
132 size = SVAL(vwv + 5, 0);
133 size |= (((unsigned int)SVAL(vwv + 7, 0)) << 16);
135 if (size > cli_req->data.read.size) {
136 DEBUG(5,("server returned more than we wanted!\n"));
137 return NT_STATUS_UNEXPECTED_IO_ERROR;
141 * bcc field must be valid for small reads, for large reads the 16-bit
142 * bcc field can't be correct.
145 if ((size < 0xffff) && (size > num_bytes)) {
146 DEBUG(5, ("server announced more bytes than sent\n"));
147 return NT_STATUS_INVALID_NETWORK_RESPONSE;
150 buf = (uint8_t *)smb_base(cli_req->inbuf) + SVAL(vwv+6, 0);
152 if (trans_oob(smb_len(cli_req->inbuf), SVAL(vwv+6, 0), size)
153 || (buf < bytes)) {
154 DEBUG(5, ("server returned invalid read&x data offset\n"));
155 return NT_STATUS_INVALID_NETWORK_RESPONSE;
158 *rcvbuf = (uint8_t *)(smb_base(cli_req->inbuf) + SVAL(vwv + 6, 0));
159 *received = size;
160 return NT_STATUS_OK;
164 * Parallel read support.
166 * cli_pull sends as many read&x requests as the server would allow via
167 * max_mux at a time. When replies flow back in, the data is written into
168 * the callback function "sink" in the right order.
171 struct cli_pull_state {
172 struct async_req *req;
174 struct event_context *ev;
175 struct cli_state *cli;
176 uint16_t fnum;
177 off_t start_offset;
178 SMB_OFF_T size;
180 NTSTATUS (*sink)(char *buf, size_t n, void *priv);
181 void *priv;
183 size_t chunk_size;
186 * Outstanding requests
188 int num_reqs;
189 struct async_req **reqs;
192 * For how many bytes did we send requests already?
194 SMB_OFF_T requested;
197 * Next request index to push into "sink". This walks around the "req"
198 * array, taking care that the requests are pushed to "sink" in the
199 * right order. If necessary (i.e. replies don't come in in the right
200 * order), replies are held back in "reqs".
202 int top_req;
205 * How many bytes did we push into "sink"?
208 SMB_OFF_T pushed;
211 static char *cli_pull_print(TALLOC_CTX *mem_ctx, struct async_req *req)
213 struct cli_pull_state *state = talloc_get_type_abort(
214 req->private_data, struct cli_pull_state);
215 char *result;
217 result = async_req_print(mem_ctx, req);
218 if (result == NULL) {
219 return NULL;
222 return talloc_asprintf_append_buffer(
223 result, "num_reqs=%d, top_req=%d",
224 state->num_reqs, state->top_req);
227 static void cli_pull_read_done(struct async_req *read_req);
230 * Prepare an async pull request
233 struct async_req *cli_pull_send(TALLOC_CTX *mem_ctx,
234 struct event_context *ev,
235 struct cli_state *cli,
236 uint16_t fnum, off_t start_offset,
237 SMB_OFF_T size, size_t window_size,
238 NTSTATUS (*sink)(char *buf, size_t n,
239 void *priv),
240 void *priv)
242 struct async_req *result;
243 struct cli_pull_state *state;
244 int i;
246 result = async_req_new(mem_ctx, ev);
247 if (result == NULL) {
248 goto failed;
250 state = talloc(result, struct cli_pull_state);
251 if (state == NULL) {
252 goto failed;
254 result->private_data = state;
255 result->print = cli_pull_print;
256 state->req = result;
258 state->cli = cli;
259 state->ev = ev;
260 state->fnum = fnum;
261 state->start_offset = start_offset;
262 state->size = size;
263 state->sink = sink;
264 state->priv = priv;
266 state->pushed = 0;
267 state->top_req = 0;
269 if (size == 0) {
270 if (!async_post_status(result, NT_STATUS_OK)) {
271 goto failed;
273 return result;
276 state->chunk_size = cli_read_max_bufsize(cli);
278 state->num_reqs = MAX(window_size/state->chunk_size, 1);
279 state->num_reqs = MIN(state->num_reqs, cli->max_mux);
281 state->reqs = TALLOC_ZERO_ARRAY(state, struct async_req *,
282 state->num_reqs);
283 if (state->reqs == NULL) {
284 goto failed;
287 state->requested = 0;
289 for (i=0; i<state->num_reqs; i++) {
290 SMB_OFF_T size_left;
291 size_t request_thistime;
293 if (state->requested >= size) {
294 state->num_reqs = i;
295 break;
298 size_left = size - state->requested;
299 request_thistime = MIN(size_left, state->chunk_size);
301 state->reqs[i] = cli_read_andx_send(
302 state->reqs, ev, cli, fnum,
303 state->start_offset + state->requested,
304 request_thistime);
306 if (state->reqs[i] == NULL) {
307 goto failed;
310 state->reqs[i]->async.fn = cli_pull_read_done;
311 state->reqs[i]->async.priv = result;
313 state->requested += request_thistime;
315 return result;
317 failed:
318 TALLOC_FREE(result);
319 return NULL;
323 * Handle incoming read replies, push the data into sink and send out new
324 * requests if necessary.
327 static void cli_pull_read_done(struct async_req *read_req)
329 struct async_req *pull_req = talloc_get_type_abort(
330 read_req->async.priv, struct async_req);
331 struct cli_pull_state *state = talloc_get_type_abort(
332 pull_req->private_data, struct cli_pull_state);
333 struct cli_request *read_state = talloc_get_type_abort(
334 read_req->private_data, struct cli_request);
335 NTSTATUS status;
337 status = cli_read_andx_recv(read_req, &read_state->data.read.received,
338 &read_state->data.read.rcvbuf);
339 if (!NT_STATUS_IS_OK(status)) {
340 async_req_error(state->req, status);
341 return;
345 * This loop is the one to take care of out-of-order replies. All
346 * pending requests are in state->reqs, state->reqs[top_req] is the
347 * one that is to be pushed next. If however a request later than
348 * top_req is replied to, then we can't push yet. If top_req is
349 * replied to at a later point then, we need to push all the finished
350 * requests.
353 while (state->reqs[state->top_req] != NULL) {
354 struct cli_request *top_read;
356 DEBUG(11, ("cli_pull_read_done: top_req = %d\n",
357 state->top_req));
359 if (state->reqs[state->top_req]->state < ASYNC_REQ_DONE) {
360 DEBUG(11, ("cli_pull_read_done: top request not yet "
361 "done\n"));
362 return;
365 top_read = talloc_get_type_abort(
366 state->reqs[state->top_req]->private_data,
367 struct cli_request);
369 DEBUG(10, ("cli_pull_read_done: Pushing %d bytes, %d already "
370 "pushed\n", (int)top_read->data.read.received,
371 (int)state->pushed));
373 status = state->sink((char *)top_read->data.read.rcvbuf,
374 top_read->data.read.received,
375 state->priv);
376 if (!NT_STATUS_IS_OK(status)) {
377 async_req_error(state->req, status);
378 return;
380 state->pushed += top_read->data.read.received;
382 TALLOC_FREE(state->reqs[state->top_req]);
384 if (state->requested < state->size) {
385 struct async_req *new_req;
386 SMB_OFF_T size_left;
387 size_t request_thistime;
389 size_left = state->size - state->requested;
390 request_thistime = MIN(size_left, state->chunk_size);
392 DEBUG(10, ("cli_pull_read_done: Requesting %d bytes "
393 "at %d, position %d\n",
394 (int)request_thistime,
395 (int)(state->start_offset
396 + state->requested),
397 state->top_req));
399 new_req = cli_read_andx_send(
400 state->reqs, state->ev, state->cli,
401 state->fnum,
402 state->start_offset + state->requested,
403 request_thistime);
405 if (async_req_nomem(new_req, state->req)) {
406 return;
409 new_req->async.fn = cli_pull_read_done;
410 new_req->async.priv = pull_req;
412 state->reqs[state->top_req] = new_req;
413 state->requested += request_thistime;
416 state->top_req = (state->top_req+1) % state->num_reqs;
419 async_req_done(pull_req);
422 NTSTATUS cli_pull_recv(struct async_req *req, SMB_OFF_T *received)
424 struct cli_pull_state *state = talloc_get_type_abort(
425 req->private_data, struct cli_pull_state);
426 NTSTATUS status;
428 if (async_req_is_error(req, &status)) {
429 return status;
431 *received = state->pushed;
432 return NT_STATUS_OK;
435 NTSTATUS cli_pull(struct cli_state *cli, uint16_t fnum,
436 off_t start_offset, SMB_OFF_T size, size_t window_size,
437 NTSTATUS (*sink)(char *buf, size_t n, void *priv),
438 void *priv, SMB_OFF_T *received)
440 TALLOC_CTX *frame = talloc_stackframe();
441 struct event_context *ev;
442 struct async_req *req;
443 NTSTATUS result = NT_STATUS_NO_MEMORY;
445 if (cli->fd_event != NULL) {
447 * Can't use sync call while an async call is in flight
449 return NT_STATUS_INVALID_PARAMETER;
452 ev = event_context_init(frame);
453 if (ev == NULL) {
454 goto nomem;
457 req = cli_pull_send(frame, ev, cli, fnum, start_offset, size,
458 window_size, sink, priv);
459 if (req == NULL) {
460 goto nomem;
463 while (req->state < ASYNC_REQ_DONE) {
464 event_loop_once(ev);
467 result = cli_pull_recv(req, received);
468 nomem:
469 TALLOC_FREE(frame);
470 return result;
473 static NTSTATUS cli_read_sink(char *buf, size_t n, void *priv)
475 char **pbuf = (char **)priv;
476 memcpy(*pbuf, buf, n);
477 *pbuf += n;
478 return NT_STATUS_OK;
481 ssize_t cli_read(struct cli_state *cli, int fnum, char *buf,
482 off_t offset, size_t size)
484 NTSTATUS status;
485 SMB_OFF_T ret;
487 status = cli_pull(cli, fnum, offset, size, size,
488 cli_read_sink, &buf, &ret);
489 if (!NT_STATUS_IS_OK(status)) {
490 cli_set_error(cli, status);
491 return -1;
493 return ret;
496 /****************************************************************************
497 Issue a single SMBwrite and don't wait for a reply.
498 ****************************************************************************/
500 static bool cli_issue_write(struct cli_state *cli,
501 int fnum,
502 off_t offset,
503 uint16 mode,
504 const char *buf,
505 size_t size,
506 int i)
508 char *p;
509 bool large_writex = false;
510 /* We can only do direct writes if not signing and not encrypting. */
511 bool direct_writes = !client_is_signing_on(cli) && !cli_encryption_on(cli);
513 if (!direct_writes && size + 1 > cli->bufsize) {
514 cli->outbuf = (char *)SMB_REALLOC(cli->outbuf, size + 1024);
515 if (!cli->outbuf) {
516 return False;
518 cli->inbuf = (char *)SMB_REALLOC(cli->inbuf, size + 1024);
519 if (cli->inbuf == NULL) {
520 SAFE_FREE(cli->outbuf);
521 return False;
523 cli->bufsize = size + 1024;
526 memset(cli->outbuf,'\0',smb_size);
527 memset(cli->inbuf,'\0',smb_size);
529 if (cli->capabilities & CAP_LARGE_FILES) {
530 large_writex = True;
533 if (large_writex) {
534 cli_set_message(cli->outbuf,14,0,True);
535 } else {
536 cli_set_message(cli->outbuf,12,0,True);
539 SCVAL(cli->outbuf,smb_com,SMBwriteX);
540 SSVAL(cli->outbuf,smb_tid,cli->cnum);
541 cli_setup_packet(cli);
543 SCVAL(cli->outbuf,smb_vwv0,0xFF);
544 SSVAL(cli->outbuf,smb_vwv2,fnum);
546 SIVAL(cli->outbuf,smb_vwv3,offset);
547 SIVAL(cli->outbuf,smb_vwv5,0);
548 SSVAL(cli->outbuf,smb_vwv7,mode);
550 SSVAL(cli->outbuf,smb_vwv8,(mode & 0x0008) ? size : 0);
552 * According to CIFS-TR-1p00, this following field should only
553 * be set if CAP_LARGE_WRITEX is set. We should check this
554 * locally. However, this check might already have been
555 * done by our callers.
557 SSVAL(cli->outbuf,smb_vwv9,(size>>16));
558 SSVAL(cli->outbuf,smb_vwv10,size);
559 /* +1 is pad byte. */
560 SSVAL(cli->outbuf,smb_vwv11,
561 smb_buf(cli->outbuf) - smb_base(cli->outbuf) + 1);
563 if (large_writex) {
564 SIVAL(cli->outbuf,smb_vwv12,(((uint64_t)offset)>>32) & 0xffffffff);
567 p = smb_base(cli->outbuf) + SVAL(cli->outbuf,smb_vwv11) -1;
568 *p++ = '\0'; /* pad byte. */
569 if (!direct_writes) {
570 memcpy(p, buf, size);
572 if (size > 0x1FFFF) {
573 /* This is a POSIX 14 word large write. */
574 set_message_bcc(cli->outbuf, 0); /* Set bcc to zero. */
575 _smb_setlen_large(cli->outbuf,smb_size + 28 + 1 /* pad */ + size - 4);
576 } else {
577 cli_setup_bcc(cli, p+size);
580 SSVAL(cli->outbuf,smb_mid,cli->mid + i);
582 show_msg(cli->outbuf);
583 if (direct_writes) {
584 /* For direct writes we now need to write the data
585 * directly out of buf. */
586 return cli_send_smb_direct_writeX(cli, buf, size);
587 } else {
588 return cli_send_smb(cli);
592 /****************************************************************************
593 write to a file
594 write_mode: 0x0001 disallow write cacheing
595 0x0002 return bytes remaining
596 0x0004 use raw named pipe protocol
597 0x0008 start of message mode named pipe protocol
598 ****************************************************************************/
600 ssize_t cli_write(struct cli_state *cli,
601 int fnum, uint16 write_mode,
602 const char *buf, off_t offset, size_t size)
604 ssize_t bwritten = 0;
605 unsigned int issued = 0;
606 unsigned int received = 0;
607 int mpx = 1;
608 size_t writesize;
609 int blocks;
611 if(cli->max_mux > 1) {
612 mpx = cli->max_mux-1;
613 } else {
614 mpx = 1;
617 /* Default (small) writesize. */
618 writesize = (cli->max_xmit - (smb_size+32)) & ~1023;
620 if (write_mode == 0 &&
621 !client_is_signing_on(cli) &&
622 !cli_encryption_on(cli) &&
623 (cli->posix_capabilities & CIFS_UNIX_LARGE_WRITE_CAP) &&
624 (cli->capabilities & CAP_LARGE_FILES)) {
625 /* Only do massive writes if we can do them direct
626 * with no signing or encrypting - not on a pipe. */
627 writesize = CLI_SAMBA_MAX_POSIX_LARGE_WRITEX_SIZE;
628 } else if ((cli->capabilities & CAP_LARGE_WRITEX) &&
629 (strcmp(cli->dev, "LPT1:") != 0)) {
631 /* Printer devices are restricted to max_xmit
632 * writesize in Vista and XPSP3. */
634 if (cli->is_samba) {
635 writesize = CLI_SAMBA_MAX_LARGE_WRITEX_SIZE;
636 } else if (!client_is_signing_on(cli)) {
637 /* Windows restricts signed writes to max_xmit.
638 * Found by Volker. */
639 writesize = CLI_WINDOWS_MAX_LARGE_WRITEX_SIZE;
643 blocks = (size + (writesize-1)) / writesize;
645 while (received < blocks) {
647 while ((issued - received < mpx) && (issued < blocks)) {
648 ssize_t bsent = issued * writesize;
649 ssize_t size1 = MIN(writesize, size - bsent);
651 if (!cli_issue_write(cli, fnum, offset + bsent,
652 write_mode,
653 buf + bsent,
654 size1, issued))
655 return -1;
656 issued++;
659 if (!cli_receive_smb(cli)) {
660 return bwritten;
663 received++;
665 if (cli_is_error(cli))
666 break;
668 bwritten += SVAL(cli->inbuf, smb_vwv2);
669 if (writesize > 0xFFFF) {
670 bwritten += (((int)(SVAL(cli->inbuf, smb_vwv4)))<<16);
674 while (received < issued && cli_receive_smb(cli)) {
675 received++;
678 return bwritten;
681 /****************************************************************************
682 write to a file using a SMBwrite and not bypassing 0 byte writes
683 ****************************************************************************/
685 ssize_t cli_smbwrite(struct cli_state *cli,
686 int fnum, char *buf, off_t offset, size_t size1)
688 char *p;
689 ssize_t total = 0;
691 do {
692 size_t size = MIN(size1, cli->max_xmit - 48);
694 memset(cli->outbuf,'\0',smb_size);
695 memset(cli->inbuf,'\0',smb_size);
697 cli_set_message(cli->outbuf,5, 0,True);
699 SCVAL(cli->outbuf,smb_com,SMBwrite);
700 SSVAL(cli->outbuf,smb_tid,cli->cnum);
701 cli_setup_packet(cli);
703 SSVAL(cli->outbuf,smb_vwv0,fnum);
704 SSVAL(cli->outbuf,smb_vwv1,size);
705 SIVAL(cli->outbuf,smb_vwv2,offset);
706 SSVAL(cli->outbuf,smb_vwv4,0);
708 p = smb_buf(cli->outbuf);
709 *p++ = 1;
710 SSVAL(p, 0, size); p += 2;
711 memcpy(p, buf + total, size); p += size;
713 cli_setup_bcc(cli, p);
715 if (!cli_send_smb(cli))
716 return -1;
718 if (!cli_receive_smb(cli))
719 return -1;
721 if (cli_is_error(cli))
722 return -1;
724 size = SVAL(cli->inbuf,smb_vwv0);
725 if (size == 0)
726 break;
728 size1 -= size;
729 total += size;
730 offset += size;
732 } while (size1);
734 return total;