X-Git-Url: http://git.samba.org/?a=blobdiff_plain;f=source3%2Flibsmb%2Fclireadwrite.c;h=d38de1950800d4f4d25ac6b4ef9558b056f9de07;hb=05379f01252fc8b449f36130bcf00c321d7a1c37;hp=4d3027694f984aaa363f1910782df832e9e923fc;hpb=56cd17dfe145c2df2b39ad295136c4922bee8e43;p=amitay%2Fsamba.git diff --git a/source3/libsmb/clireadwrite.c b/source3/libsmb/clireadwrite.c index 4d3027694f9..d38de195080 100644 --- a/source3/libsmb/clireadwrite.c +++ b/source3/libsmb/clireadwrite.c @@ -36,18 +36,60 @@ static size_t cli_read_max_bufsize(struct cli_state *cli) return (cli->max_xmit - (smb_size+32)) & ~1023; } -/* - * Send a read&x request - */ +/**************************************************************************** + Calculate the recommended write buffer size +****************************************************************************/ +static size_t cli_write_max_bufsize(struct cli_state *cli, uint16_t write_mode) +{ + if (write_mode == 0 && + !client_is_signing_on(cli) && + !cli_encryption_on(cli) && + (cli->posix_capabilities & CIFS_UNIX_LARGE_WRITE_CAP) && + (cli->capabilities & CAP_LARGE_FILES)) { + /* Only do massive writes if we can do them direct + * with no signing or encrypting - not on a pipe. */ + return CLI_SAMBA_MAX_POSIX_LARGE_WRITEX_SIZE; + } -struct async_req *cli_read_andx_send(TALLOC_CTX *mem_ctx, - struct cli_state *cli, int fnum, - off_t offset, size_t size) + if (cli->is_samba) { + return CLI_SAMBA_MAX_LARGE_WRITEX_SIZE; + } + + if (((cli->capabilities & CAP_LARGE_WRITEX) == 0) + || client_is_signing_on(cli) + || strequal(cli->dev, "LPT1:")) { + + /* + * Printer devices are restricted to max_xmit writesize in + * Vista and XPSP3 as are signing connections. + */ + + return (cli->max_xmit - (smb_size+32)) & ~1023; + } + + return CLI_WINDOWS_MAX_LARGE_WRITEX_SIZE; +} + +struct cli_read_andx_state { + size_t size; + uint16_t vwv[12]; + NTSTATUS status; + size_t received; + uint8_t *buf; +}; + +static void cli_read_andx_done(struct tevent_req *subreq); + +struct tevent_req *cli_read_andx_create(TALLOC_CTX *mem_ctx, + struct event_context *ev, + struct cli_state *cli, uint16_t fnum, + off_t offset, size_t size, + struct tevent_req **psmbreq) { - struct async_req *result; - struct cli_request *req; + struct tevent_req *req, *subreq; + struct cli_read_andx_state *state; bool bigoffset = False; - char *enc_buf; + uint8_t wct = 10; if (size > cli_read_max_bufsize(cli)) { DEBUG(0, ("cli_read_andx_send got size=%d, can only handle " @@ -56,61 +98,114 @@ struct async_req *cli_read_andx_send(TALLOC_CTX *mem_ctx, return NULL; } - result = cli_request_new(mem_ctx, cli->event_ctx, cli, 12, 0, &req); - if (result == NULL) { - DEBUG(0, ("cli_request_new failed\n")); + req = tevent_req_create(mem_ctx, &state, struct cli_read_andx_state); + if (req == NULL) { return NULL; } + state->size = size; - req->data.read.ofs = offset; - req->data.read.size = size; - req->data.read.received = 0; - req->data.read.rcvbuf = NULL; + SCVAL(state->vwv + 0, 0, 0xFF); + SCVAL(state->vwv + 0, 1, 0); + SSVAL(state->vwv + 1, 0, 0); + SSVAL(state->vwv + 2, 0, fnum); + SIVAL(state->vwv + 3, 0, offset); + SSVAL(state->vwv + 5, 0, size); + SSVAL(state->vwv + 6, 0, size); + SSVAL(state->vwv + 7, 0, (size >> 16)); + SSVAL(state->vwv + 8, 0, 0); + SSVAL(state->vwv + 9, 0, 0); + + if ((uint64_t)offset >> 32) { + bigoffset = true; + SIVAL(state->vwv + 10, 0, + (((uint64_t)offset)>>32) & 0xffffffff); + wct += 2; + } - if ((SMB_BIG_UINT)offset >> 32) - bigoffset = True; + subreq = cli_smb_req_create(state, ev, cli, SMBreadX, 0, wct, + state->vwv, 0, NULL); + if (subreq == NULL) { + TALLOC_FREE(req); + return NULL; + } + tevent_req_set_callback(subreq, cli_read_andx_done, req); + *psmbreq = subreq; + return req; +} - cli_set_message(req->outbuf, bigoffset ? 12 : 10, 0, False); +struct tevent_req *cli_read_andx_send(TALLOC_CTX *mem_ctx, + struct event_context *ev, + struct cli_state *cli, uint16_t fnum, + off_t offset, size_t size) +{ + struct tevent_req *req, *subreq; + NTSTATUS status; - SCVAL(req->outbuf,smb_com,SMBreadX); - SSVAL(req->outbuf,smb_tid,cli->cnum); - cli_setup_packet_buf(cli, req->outbuf); + req = cli_read_andx_create(mem_ctx, ev, cli, fnum, offset, size, + &subreq); + if (req == NULL) { + return NULL; + } - SCVAL(req->outbuf,smb_vwv0,0xFF); - SCVAL(req->outbuf,smb_vwv0+1,0); - SSVAL(req->outbuf,smb_vwv1,0); - SSVAL(req->outbuf,smb_vwv2,fnum); - SIVAL(req->outbuf,smb_vwv3,offset); - SSVAL(req->outbuf,smb_vwv5,size); - SSVAL(req->outbuf,smb_vwv6,size); - SSVAL(req->outbuf,smb_vwv7,(size >> 16)); - SSVAL(req->outbuf,smb_vwv8,0); - SSVAL(req->outbuf,smb_vwv9,0); - SSVAL(req->outbuf,smb_mid,req->mid); + status = cli_smb_req_send(subreq); + if (!NT_STATUS_IS_OK(status)) { + tevent_req_nterror(req, status); + return tevent_req_post(req, ev); + } + return req; +} - if (bigoffset) { - SIVAL(req->outbuf, smb_vwv10, - (((SMB_BIG_UINT)offset)>>32) & 0xffffffff); +static void cli_read_andx_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_read_andx_state *state = tevent_req_data( + req, struct cli_read_andx_state); + uint8_t *inbuf; + uint8_t wct; + uint16_t *vwv; + uint32_t num_bytes; + uint8_t *bytes; + + state->status = cli_smb_recv(subreq, 12, &wct, &vwv, &num_bytes, + &bytes); + if (NT_STATUS_IS_ERR(state->status)) { + tevent_req_nterror(req, state->status); + return; } - cli_calculate_sign_mac(cli, req->outbuf); + /* size is the number of bytes the server returned. + * Might be zero. */ + state->received = SVAL(vwv + 5, 0); + state->received |= (((unsigned int)SVAL(vwv + 7, 0)) << 16); - event_fd_set_writeable(cli->fd_event); + if (state->received > state->size) { + DEBUG(5,("server returned more than we wanted!\n")); + tevent_req_nterror(req, NT_STATUS_UNEXPECTED_IO_ERROR); + return; + } - if (cli_encryption_on(cli)) { - NTSTATUS status; - status = cli_encrypt_message(cli, req->outbuf, &enc_buf); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(0, ("Error in encrypting client message. " - "Error %s\n", nt_errstr(status))); - TALLOC_FREE(req); - return NULL; - } - req->outbuf = enc_buf; - req->enc_state = cli->trans_enc_state; + /* + * bcc field must be valid for small reads, for large reads the 16-bit + * bcc field can't be correct. + */ + + if ((state->received < 0xffff) && (state->received > num_bytes)) { + DEBUG(5, ("server announced more bytes than sent\n")); + tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE); + return; } - return result; + inbuf = cli_smb_inbuf(subreq); + state->buf = (uint8_t *)smb_base(inbuf) + SVAL(vwv+6, 0); + + if (trans_oob(smb_len(inbuf), SVAL(vwv+6, 0), state->received) + || (state->buf < bytes)) { + DEBUG(5, ("server returned invalid read&x data offset\n")); + tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE); + return; + } + tevent_req_done(req); } /* @@ -120,44 +215,27 @@ struct async_req *cli_read_andx_send(TALLOC_CTX *mem_ctx, * talloc_move it! */ -NTSTATUS cli_read_andx_recv(struct async_req *req, ssize_t *received, +NTSTATUS cli_read_andx_recv(struct tevent_req *req, ssize_t *received, uint8_t **rcvbuf) { - struct cli_request *cli_req = cli_request_get(req); + struct cli_read_andx_state *state = tevent_req_data( + req, struct cli_read_andx_state); NTSTATUS status; - size_t size; - SMB_ASSERT(req->state >= ASYNC_REQ_DONE); - if (req->state == ASYNC_REQ_ERROR) { - return req->status; - } - - status = cli_pull_error(cli_req->inbuf); - - if (NT_STATUS_IS_ERR(status)) { + if (tevent_req_is_nterror(req, &status)) { return status; } - - if (wct < 12) { - return NT_STATUS_INVALID_NETWORK_RESPONSE; - } - - /* size is the number of bytes the server returned. - * Might be zero. */ - size = SVAL(cli_req->inbuf, smb_vwv5); - size |= (((unsigned int)(SVAL(cli_req->inbuf, smb_vwv7))) << 16); - - if (size > cli_req->data.read.size) { - DEBUG(5,("server returned more than we wanted!\n")); - return NT_STATUS_UNEXPECTED_IO_ERROR; - } - - *rcvbuf = (uint8_t *) - (smb_base(cli_req->inbuf) + SVAL(cli_req->inbuf, smb_vwv6)); - *received = size; + *received = state->received; + *rcvbuf = state->buf; return NT_STATUS_OK; } +struct cli_pull_subreq { + struct tevent_req *req; + ssize_t received; + uint8_t *buf; +}; + /* * Parallel read support. * @@ -167,8 +245,9 @@ NTSTATUS cli_read_andx_recv(struct async_req *req, ssize_t *received, */ struct cli_pull_state { - struct async_req *req; + struct tevent_req *req; + struct event_context *ev; struct cli_state *cli; uint16_t fnum; off_t start_offset; @@ -183,7 +262,7 @@ struct cli_pull_state { * Outstanding requests */ int num_reqs; - struct async_req **reqs; + struct cli_pull_subreq *reqs; /* * For how many bytes did we send requests already? @@ -205,13 +284,13 @@ struct cli_pull_state { SMB_OFF_T pushed; }; -static char *cli_pull_print(TALLOC_CTX *mem_ctx, struct async_req *req) +static char *cli_pull_print(struct tevent_req *req, TALLOC_CTX *mem_ctx) { - struct cli_pull_state *state = talloc_get_type_abort( - req->private_data, struct cli_pull_state); + struct cli_pull_state *state = tevent_req_data( + req, struct cli_pull_state); char *result; - result = async_req_print(mem_ctx, req); + result = tevent_req_print(mem_ctx, req); if (result == NULL) { return NULL; } @@ -221,36 +300,34 @@ static char *cli_pull_print(TALLOC_CTX *mem_ctx, struct async_req *req) state->num_reqs, state->top_req); } -static void cli_pull_read_done(struct async_req *read_req); +static void cli_pull_read_done(struct tevent_req *read_req); /* * Prepare an async pull request */ -struct async_req *cli_pull_send(TALLOC_CTX *mem_ctx, struct cli_state *cli, - uint16_t fnum, off_t start_offset, - SMB_OFF_T size, size_t window_size, - NTSTATUS (*sink)(char *buf, size_t n, - void *priv), - void *priv) +struct tevent_req *cli_pull_send(TALLOC_CTX *mem_ctx, + struct event_context *ev, + struct cli_state *cli, + uint16_t fnum, off_t start_offset, + SMB_OFF_T size, size_t window_size, + NTSTATUS (*sink)(char *buf, size_t n, + void *priv), + void *priv) { - struct async_req *result; + struct tevent_req *req; struct cli_pull_state *state; int i; - result = async_req_new(mem_ctx, cli->event_ctx); - if (result == NULL) { - goto failed; - } - state = talloc(result, struct cli_pull_state); - if (state == NULL) { - goto failed; + req = tevent_req_create(mem_ctx, &state, struct cli_pull_state); + if (req == NULL) { + return NULL; } - result->private_data = state; - result->print = cli_pull_print; - state->req = result; + tevent_req_set_print_fn(req, cli_pull_print); + state->req = req; state->cli = cli; + state->ev = ev; state->fnum = fnum; state->start_offset = start_offset; state->size = size; @@ -261,10 +338,8 @@ struct async_req *cli_pull_send(TALLOC_CTX *mem_ctx, struct cli_state *cli, state->top_req = 0; if (size == 0) { - if (!async_post_status(result, NT_STATUS_OK)) { - goto failed; - } - return result; + tevent_req_done(req); + return tevent_req_post(req, ev); } state->chunk_size = cli_read_max_bufsize(cli); @@ -272,7 +347,7 @@ struct async_req *cli_pull_send(TALLOC_CTX *mem_ctx, struct cli_state *cli, state->num_reqs = MAX(window_size/state->chunk_size, 1); state->num_reqs = MIN(state->num_reqs, cli->max_mux); - state->reqs = TALLOC_ZERO_ARRAY(state, struct async_req *, + state->reqs = TALLOC_ZERO_ARRAY(state, struct cli_pull_subreq, state->num_reqs); if (state->reqs == NULL) { goto failed; @@ -281,6 +356,7 @@ struct async_req *cli_pull_send(TALLOC_CTX *mem_ctx, struct cli_state *cli, state->requested = 0; for (i=0; inum_reqs; i++) { + struct cli_pull_subreq *subreq = &state->reqs[i]; SMB_OFF_T size_left; size_t request_thistime; @@ -292,24 +368,21 @@ struct async_req *cli_pull_send(TALLOC_CTX *mem_ctx, struct cli_state *cli, size_left = size - state->requested; request_thistime = MIN(size_left, state->chunk_size); - state->reqs[i] = cli_read_andx_send( - state->reqs, cli, fnum, + subreq->req = cli_read_andx_send( + state->reqs, ev, cli, fnum, state->start_offset + state->requested, request_thistime); - if (state->reqs[i] == NULL) { + if (subreq->req == NULL) { goto failed; } - - state->reqs[i]->async.fn = cli_pull_read_done; - state->reqs[i]->async.priv = result; - + tevent_req_set_callback(subreq->req, cli_pull_read_done, req); state->requested += request_thistime; } - return result; + return req; failed: - TALLOC_FREE(result); + TALLOC_FREE(req); return NULL; } @@ -318,19 +391,32 @@ failed: * requests if necessary. */ -static void cli_pull_read_done(struct async_req *read_req) +static void cli_pull_read_done(struct tevent_req *subreq) { - struct async_req *pull_req = talloc_get_type_abort( - read_req->async.priv, struct async_req); - struct cli_pull_state *state = talloc_get_type_abort( - pull_req->private_data, struct cli_pull_state); - struct cli_request *read_state = cli_request_get(read_req); + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_pull_state *state = tevent_req_data( + req, struct cli_pull_state); + struct cli_pull_subreq *pull_subreq = NULL; NTSTATUS status; + int i; + + for (i = 0; i < state->num_reqs; i++) { + pull_subreq = &state->reqs[i]; + if (subreq == pull_subreq->req) { + break; + } + } + if (i == state->num_reqs) { + /* Huh -- received something we did not send?? */ + tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR); + return; + } - status = cli_read_andx_recv(read_req, &read_state->data.read.received, - &read_state->data.read.rcvbuf); + status = cli_read_andx_recv(subreq, &pull_subreq->received, + &pull_subreq->buf); if (!NT_STATUS_IS_OK(status)) { - async_req_error(state->req, status); + tevent_req_nterror(state->req, status); return; } @@ -343,37 +429,36 @@ static void cli_pull_read_done(struct async_req *read_req) * requests. */ - while (state->reqs[state->top_req] != NULL) { - struct cli_request *top_read; + while (state->reqs[state->top_req].req != NULL) { + struct cli_pull_subreq *top_subreq; DEBUG(11, ("cli_pull_read_done: top_req = %d\n", state->top_req)); - if (state->reqs[state->top_req]->state < ASYNC_REQ_DONE) { + top_subreq = &state->reqs[state->top_req]; + + if (tevent_req_is_in_progress(top_subreq->req)) { DEBUG(11, ("cli_pull_read_done: top request not yet " "done\n")); return; } - top_read = cli_request_get(state->reqs[state->top_req]); - DEBUG(10, ("cli_pull_read_done: Pushing %d bytes, %d already " - "pushed\n", (int)top_read->data.read.received, + "pushed\n", (int)top_subreq->received, (int)state->pushed)); - status = state->sink((char *)top_read->data.read.rcvbuf, - top_read->data.read.received, - state->priv); + status = state->sink((char *)top_subreq->buf, + top_subreq->received, state->priv); if (!NT_STATUS_IS_OK(status)) { - async_req_error(state->req, status); + tevent_req_nterror(state->req, status); return; } - state->pushed += top_read->data.read.received; + state->pushed += top_subreq->received; - TALLOC_FREE(state->reqs[state->top_req]); + TALLOC_FREE(state->reqs[state->top_req].req); if (state->requested < state->size) { - struct async_req *new_req; + struct tevent_req *new_req; SMB_OFF_T size_left; size_t request_thistime; @@ -388,35 +473,35 @@ static void cli_pull_read_done(struct async_req *read_req) state->top_req)); new_req = cli_read_andx_send( - state->reqs, state->cli, state->fnum, + state->reqs, state->ev, state->cli, + state->fnum, state->start_offset + state->requested, request_thistime); - if (async_req_nomem(new_req, state->req)) { + if (tevent_req_nomem(new_req, state->req)) { return; } + tevent_req_set_callback(new_req, cli_pull_read_done, + req); - new_req->async.fn = cli_pull_read_done; - new_req->async.priv = pull_req; - - state->reqs[state->top_req] = new_req; + state->reqs[state->top_req].req = new_req; state->requested += request_thistime; } state->top_req = (state->top_req+1) % state->num_reqs; } - async_req_done(pull_req); + tevent_req_done(req); } -NTSTATUS cli_pull_recv(struct async_req *req, SMB_OFF_T *received) +NTSTATUS cli_pull_recv(struct tevent_req *req, SMB_OFF_T *received) { - struct cli_pull_state *state = talloc_get_type_abort( - req->private_data, struct cli_pull_state); + struct cli_pull_state *state = tevent_req_data( + req, struct cli_pull_state); + NTSTATUS status; - SMB_ASSERT(req->state >= ASYNC_REQ_DONE); - if (req->state == ASYNC_REQ_ERROR) { - return req->status; + if (tevent_req_is_nterror(req, &status)) { + return status; } *received = state->pushed; return NT_STATUS_OK; @@ -428,27 +513,43 @@ NTSTATUS cli_pull(struct cli_state *cli, uint16_t fnum, void *priv, SMB_OFF_T *received) { TALLOC_CTX *frame = talloc_stackframe(); - struct async_req *req; - NTSTATUS result = NT_STATUS_NO_MEMORY; + struct event_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_OK; + + if (cli_has_async_calls(cli)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } - if (cli_tmp_event_ctx(frame, cli) == NULL) { - goto nomem; + ev = event_context_init(frame); + if (ev == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; } - req = cli_pull_send(frame, cli, fnum, start_offset, size, window_size, - sink, priv); + req = cli_pull_send(frame, ev, cli, fnum, start_offset, size, + window_size, sink, priv); if (req == NULL) { - goto nomem; + status = NT_STATUS_NO_MEMORY; + goto fail; } - while (req->state < ASYNC_REQ_DONE) { - event_loop_once(cli->event_ctx); + if (!tevent_req_poll(req, ev)) { + status = map_nt_error_from_unix(errno); + goto fail; } - result = cli_pull_recv(req, received); - nomem: + status = cli_pull_recv(req, received); + fail: TALLOC_FREE(frame); - return result; + if (!NT_STATUS_IS_OK(status)) { + cli_set_error(cli, status); + } + return status; } static NTSTATUS cli_read_sink(char *buf, size_t n, void *priv) @@ -459,7 +560,7 @@ static NTSTATUS cli_read_sink(char *buf, size_t n, void *priv) return NT_STATUS_OK; } -ssize_t cli_read(struct cli_state *cli, int fnum, char *buf, +ssize_t cli_read(struct cli_state *cli, uint16_t fnum, char *buf, off_t offset, size_t size) { NTSTATUS status; @@ -479,7 +580,7 @@ ssize_t cli_read(struct cli_state *cli, int fnum, char *buf, ****************************************************************************/ static bool cli_issue_write(struct cli_state *cli, - int fnum, + uint16_t fnum, off_t offset, uint16 mode, const char *buf, @@ -542,7 +643,7 @@ static bool cli_issue_write(struct cli_state *cli, smb_buf(cli->outbuf) - smb_base(cli->outbuf) + 1); if (large_writex) { - SIVAL(cli->outbuf,smb_vwv12,(((SMB_BIG_UINT)offset)>>32) & 0xffffffff); + SIVAL(cli->outbuf,smb_vwv12,(((uint64_t)offset)>>32) & 0xffffffff); } p = smb_base(cli->outbuf) + SVAL(cli->outbuf,smb_vwv11) -1; @@ -579,7 +680,7 @@ static bool cli_issue_write(struct cli_state *cli, ****************************************************************************/ ssize_t cli_write(struct cli_state *cli, - int fnum, uint16 write_mode, + uint16_t fnum, uint16 write_mode, const char *buf, off_t offset, size_t size) { ssize_t bwritten = 0; @@ -595,31 +696,7 @@ ssize_t cli_write(struct cli_state *cli, mpx = 1; } - /* Default (small) writesize. */ - writesize = (cli->max_xmit - (smb_size+32)) & ~1023; - - if (write_mode == 0 && - !client_is_signing_on(cli) && - !cli_encryption_on(cli) && - (cli->posix_capabilities & CIFS_UNIX_LARGE_WRITE_CAP) && - (cli->capabilities & CAP_LARGE_FILES)) { - /* Only do massive writes if we can do them direct - * with no signing or encrypting - not on a pipe. */ - writesize = CLI_SAMBA_MAX_POSIX_LARGE_WRITEX_SIZE; - } else if ((cli->capabilities & CAP_LARGE_WRITEX) && - (strcmp(cli->dev, "LPT1:") != 0)) { - - /* Printer devices are restricted to max_xmit - * writesize in Vista and XPSP3. */ - - if (cli->is_samba) { - writesize = CLI_SAMBA_MAX_LARGE_WRITEX_SIZE; - } else if (!client_is_signing_on(cli)) { - /* Windows restricts signed writes to max_xmit. - * Found by Volker. */ - writesize = CLI_WINDOWS_MAX_LARGE_WRITEX_SIZE; - } - } + writesize = cli_write_max_bufsize(cli, write_mode); blocks = (size + (writesize-1)) / writesize; @@ -664,7 +741,7 @@ ssize_t cli_write(struct cli_state *cli, ****************************************************************************/ ssize_t cli_smbwrite(struct cli_state *cli, - int fnum, char *buf, off_t offset, size_t size1) + uint16_t fnum, char *buf, off_t offset, size_t size1) { char *p; ssize_t total = 0; @@ -714,3 +791,462 @@ ssize_t cli_smbwrite(struct cli_state *cli, return total; } + +/* + * Send a write&x request + */ + +struct cli_write_andx_state { + size_t size; + uint16_t vwv[14]; + size_t written; + uint8_t pad; + struct iovec iov[2]; +}; + +static void cli_write_andx_done(struct tevent_req *subreq); + +struct tevent_req *cli_write_andx_create(TALLOC_CTX *mem_ctx, + struct event_context *ev, + struct cli_state *cli, uint16_t fnum, + uint16_t mode, const uint8_t *buf, + off_t offset, size_t size, + struct tevent_req **reqs_before, + int num_reqs_before, + struct tevent_req **psmbreq) +{ + struct tevent_req *req, *subreq; + struct cli_write_andx_state *state; + bool bigoffset = ((cli->capabilities & CAP_LARGE_FILES) != 0); + uint8_t wct = bigoffset ? 14 : 12; + size_t max_write = cli_write_max_bufsize(cli, mode); + uint16_t *vwv; + + req = tevent_req_create(mem_ctx, &state, struct cli_write_andx_state); + if (req == NULL) { + return NULL; + } + + size = MIN(size, max_write); + + vwv = state->vwv; + + SCVAL(vwv+0, 0, 0xFF); + SCVAL(vwv+0, 1, 0); + SSVAL(vwv+1, 0, 0); + SSVAL(vwv+2, 0, fnum); + SIVAL(vwv+3, 0, offset); + SIVAL(vwv+5, 0, 0); + SSVAL(vwv+7, 0, mode); + SSVAL(vwv+8, 0, 0); + SSVAL(vwv+9, 0, (size>>16)); + SSVAL(vwv+10, 0, size); + + SSVAL(vwv+11, 0, + cli_smb_wct_ofs(reqs_before, num_reqs_before) + + 1 /* the wct field */ + + wct * 2 /* vwv */ + + 2 /* num_bytes field */ + + 1 /* pad */); + + if (bigoffset) { + SIVAL(vwv+12, 0, (((uint64_t)offset)>>32) & 0xffffffff); + } + + state->pad = 0; + state->iov[0].iov_base = (void *)&state->pad; + state->iov[0].iov_len = 1; + state->iov[1].iov_base = CONST_DISCARD(void *, buf); + state->iov[1].iov_len = size; + + subreq = cli_smb_req_create(state, ev, cli, SMBwriteX, 0, wct, vwv, + 2, state->iov); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_write_andx_done, req); + *psmbreq = subreq; + return req; +} + +struct tevent_req *cli_write_andx_send(TALLOC_CTX *mem_ctx, + struct event_context *ev, + struct cli_state *cli, uint16_t fnum, + uint16_t mode, const uint8_t *buf, + off_t offset, size_t size) +{ + struct tevent_req *req, *subreq; + NTSTATUS status; + + req = cli_write_andx_create(mem_ctx, ev, cli, fnum, mode, buf, offset, + size, NULL, 0, &subreq); + if (req == NULL) { + return NULL; + } + + status = cli_smb_req_send(subreq); + if (!NT_STATUS_IS_OK(status)) { + tevent_req_nterror(req, status); + return tevent_req_post(req, ev); + } + return req; +} + +static void cli_write_andx_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_write_andx_state *state = tevent_req_data( + req, struct cli_write_andx_state); + uint8_t wct; + uint16_t *vwv; + NTSTATUS status; + + status = cli_smb_recv(subreq, 6, &wct, &vwv, NULL, NULL); + if (NT_STATUS_IS_ERR(status)) { + TALLOC_FREE(subreq); + tevent_req_nterror(req, status); + return; + } + state->written = SVAL(vwv+2, 0); + state->written |= SVAL(vwv+4, 0)<<16; + tevent_req_done(req); +} + +NTSTATUS cli_write_andx_recv(struct tevent_req *req, size_t *pwritten) +{ + struct cli_write_andx_state *state = tevent_req_data( + req, struct cli_write_andx_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + *pwritten = state->written; + return NT_STATUS_OK; +} + +struct cli_writeall_state { + struct event_context *ev; + struct cli_state *cli; + uint16_t fnum; + uint16_t mode; + const uint8_t *buf; + off_t offset; + size_t size; + size_t written; +}; + +static void cli_writeall_written(struct tevent_req *req); + +static struct tevent_req *cli_writeall_send(TALLOC_CTX *mem_ctx, + struct event_context *ev, + struct cli_state *cli, + uint16_t fnum, + uint16_t mode, + const uint8_t *buf, + off_t offset, size_t size) +{ + struct tevent_req *req, *subreq; + struct cli_writeall_state *state; + + req = tevent_req_create(mem_ctx, &state, struct cli_writeall_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->cli = cli; + state->fnum = fnum; + state->mode = mode; + state->buf = buf; + state->offset = offset; + state->size = size; + state->written = 0; + + subreq = cli_write_andx_send(state, state->ev, state->cli, state->fnum, + state->mode, state->buf, state->offset, + state->size); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_writeall_written, req); + return req; +} + +static void cli_writeall_written(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_writeall_state *state = tevent_req_data( + req, struct cli_writeall_state); + NTSTATUS status; + size_t written, to_write; + + status = cli_write_andx_recv(subreq, &written); + TALLOC_FREE(subreq); + if (!NT_STATUS_IS_OK(status)) { + tevent_req_nterror(req, status); + return; + } + + state->written += written; + + if (state->written > state->size) { + tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE); + return; + } + + to_write = state->size - state->written; + + if (to_write == 0) { + tevent_req_done(req); + return; + } + + subreq = cli_write_andx_send(state, state->ev, state->cli, state->fnum, + state->mode, + state->buf + state->written, + state->offset + state->written, to_write); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, cli_writeall_written, req); +} + +static NTSTATUS cli_writeall_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +struct cli_push_write_state { + struct tevent_req *req;/* This is the main request! Not the subreq */ + uint32_t idx; + off_t ofs; + uint8_t *buf; + size_t size; +}; + +struct cli_push_state { + struct event_context *ev; + struct cli_state *cli; + uint16_t fnum; + uint16_t mode; + off_t start_offset; + size_t window_size; + + size_t (*source)(uint8_t *buf, size_t n, void *priv); + void *priv; + + bool eof; + + size_t chunk_size; + off_t next_offset; + + /* + * Outstanding requests + */ + uint32_t pending; + uint32_t num_reqs; + struct cli_push_write_state **reqs; +}; + +static void cli_push_written(struct tevent_req *req); + +static bool cli_push_write_setup(struct tevent_req *req, + struct cli_push_state *state, + uint32_t idx) +{ + struct cli_push_write_state *substate; + struct tevent_req *subreq; + + substate = talloc(state->reqs, struct cli_push_write_state); + if (!substate) { + return false; + } + substate->req = req; + substate->idx = idx; + substate->ofs = state->next_offset; + substate->buf = talloc_array(substate, uint8_t, state->chunk_size); + if (!substate->buf) { + talloc_free(substate); + return false; + } + substate->size = state->source(substate->buf, + state->chunk_size, + state->priv); + if (substate->size == 0) { + state->eof = true; + /* nothing to send */ + talloc_free(substate); + return true; + } + + subreq = cli_writeall_send(substate, + state->ev, state->cli, + state->fnum, state->mode, + substate->buf, + substate->ofs, + substate->size); + if (!subreq) { + talloc_free(substate); + return false; + } + tevent_req_set_callback(subreq, cli_push_written, substate); + + state->reqs[idx] = substate; + state->pending += 1; + state->next_offset += substate->size; + + return true; +} + +struct tevent_req *cli_push_send(TALLOC_CTX *mem_ctx, struct event_context *ev, + struct cli_state *cli, + uint16_t fnum, uint16_t mode, + off_t start_offset, size_t window_size, + size_t (*source)(uint8_t *buf, size_t n, + void *priv), + void *priv) +{ + struct tevent_req *req; + struct cli_push_state *state; + uint32_t i; + + req = tevent_req_create(mem_ctx, &state, struct cli_push_state); + if (req == NULL) { + return NULL; + } + state->cli = cli; + state->ev = ev; + state->fnum = fnum; + state->start_offset = start_offset; + state->mode = mode; + state->source = source; + state->priv = priv; + state->eof = false; + state->pending = 0; + state->next_offset = start_offset; + + state->chunk_size = cli_write_max_bufsize(cli, mode); + + if (window_size == 0) { + window_size = cli->max_mux * state->chunk_size; + } + state->num_reqs = window_size/state->chunk_size; + if ((window_size % state->chunk_size) > 0) { + state->num_reqs += 1; + } + state->num_reqs = MIN(state->num_reqs, cli->max_mux); + state->num_reqs = MAX(state->num_reqs, 1); + + state->reqs = TALLOC_ZERO_ARRAY(state, struct cli_push_write_state *, + state->num_reqs); + if (state->reqs == NULL) { + goto failed; + } + + for (i=0; inum_reqs; i++) { + if (!cli_push_write_setup(req, state, i)) { + goto failed; + } + + if (state->eof) { + break; + } + } + + if (state->pending == 0) { + tevent_req_done(req); + return tevent_req_post(req, ev); + } + + return req; + + failed: + tevent_req_nterror(req, NT_STATUS_NO_MEMORY); + return tevent_req_post(req, ev); +} + +static void cli_push_written(struct tevent_req *subreq) +{ + struct cli_push_write_state *substate = tevent_req_callback_data( + subreq, struct cli_push_write_state); + struct tevent_req *req = substate->req; + struct cli_push_state *state = tevent_req_data( + req, struct cli_push_state); + NTSTATUS status; + uint32_t idx = substate->idx; + + state->reqs[idx] = NULL; + state->pending -= 1; + + status = cli_writeall_recv(subreq); + TALLOC_FREE(subreq); + TALLOC_FREE(substate); + if (!NT_STATUS_IS_OK(status)) { + tevent_req_nterror(req, status); + return; + } + + if (!state->eof) { + if (!cli_push_write_setup(req, state, idx)) { + tevent_req_nterror(req, NT_STATUS_NO_MEMORY); + return; + } + } + + if (state->pending == 0) { + tevent_req_done(req); + return; + } +} + +NTSTATUS cli_push_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +NTSTATUS cli_push(struct cli_state *cli, uint16_t fnum, uint16_t mode, + off_t start_offset, size_t window_size, + size_t (*source)(uint8_t *buf, size_t n, void *priv), + void *priv) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct event_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_OK; + + if (cli_has_async_calls(cli)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + ev = event_context_init(frame); + if (ev == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + req = cli_push_send(frame, ev, cli, fnum, mode, start_offset, + window_size, source, priv); + if (req == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + if (!tevent_req_poll(req, ev)) { + status = map_nt_error_from_unix(errno); + goto fail; + } + + status = cli_push_recv(req); + fail: + TALLOC_FREE(frame); + if (!NT_STATUS_IS_OK(status)) { + cli_set_error(cli, status); + } + return status; +}