From c66fd3932beaf13ef6e3a4fe25f7d49c72895077 Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Fri, 18 Sep 2009 19:45:36 +0200 Subject: [PATCH] s3:smbclient: Fix bug 6606 (reported as 6744) in 3.3 This is a port of 1f34ffa0ca and 24309bdb2efc to 3.3. --- source/libsmb/clireadwrite.c | 133 ++++++++++++++++++++++++++++++++++++++++- 1 files changed, 129 insertions(+), 4 deletions(-) diff --git a/source/libsmb/clireadwrite.c b/source/libsmb/clireadwrite.c index a57f1e0..db59798 100644 --- a/source/libsmb/clireadwrite.c +++ b/source/libsmb/clireadwrite.c @@ -154,6 +154,131 @@ NTSTATUS cli_read_andx_recv(struct async_req *req, ssize_t *received, return NT_STATUS_OK; } +struct cli_readall_state { + struct cli_state *cli; + uint16_t fnum; + off_t start_offset; + size_t size; + size_t received; + uint8_t *buf; +}; + +static void cli_readall_done(struct async_req *subreq); + +static struct async_req *cli_readall_send(TALLOC_CTX *mem_ctx, + struct cli_state *cli, + uint16_t fnum, + off_t offset, size_t size) +{ + struct async_req *req, *subreq; + struct cli_readall_state *state; + + req = async_req_new(mem_ctx, cli->event_ctx); + if (req == NULL) { + return NULL; + } + state = talloc(req, struct cli_readall_state); + if (state == NULL) { + TALLOC_FREE(req); + return NULL; + } + state->cli = cli; + state->fnum = fnum; + state->start_offset = offset; + state->size = size; + state->received = 0; + state->buf = NULL; + + subreq = cli_read_andx_send(state, cli, fnum, offset, size); + if (subreq == NULL) { + TALLOC_FREE(req); + return NULL; + } + subreq->async.fn = cli_readall_done; + subreq->async.priv = req; + return req; +} + +static void cli_readall_done(struct async_req *subreq) +{ + struct async_req *req = talloc_get_type_abort( + subreq->async.priv, struct async_req); + struct cli_readall_state *state = talloc_get_type_abort( + req->private_data, struct cli_readall_state); + ssize_t received; + uint8_t *buf; + NTSTATUS status; + + status = cli_read_andx_recv(subreq, &received, &buf); + if (!NT_STATUS_IS_OK(status)) { + async_req_error(req, status); + return; + } + + if (received == 0) { + /* EOF */ + async_req_done(req); + return; + } + + if ((state->received == 0) && (received == state->size)) { + /* Ideal case: Got it all in one run */ + state->buf = buf; + state->received += received; + async_req_done(req); + return; + } + + /* + * We got a short read, issue a read for the + * rest. Unfortunately we have to allocate the buffer + * ourselves now, as our caller expects to receive a single + * buffer. cli_read_andx does it from the buffer received from + * the net, but with a short read we have to put it together + * from several reads. + */ + + if (state->buf == NULL) { + state->buf = talloc_array(state, uint8_t, state->size); + if (async_req_nomem(state->buf, req)) { + return; + } + } + memcpy(state->buf + state->received, buf, received); + state->received += received; + + TALLOC_FREE(subreq); + + if (state->received >= state->size) { + async_req_done(req); + return; + } + + subreq = cli_read_andx_send(state, state->cli, state->fnum, + state->start_offset + state->received, + state->size - state->received); + if (async_req_nomem(subreq, req)) { + return; + } + subreq->async.fn = cli_readall_done; + subreq->async.priv = req; +} + +static NTSTATUS cli_readall_recv(struct async_req *req, ssize_t *received, + uint8_t **rcvbuf) +{ + struct cli_readall_state *state = talloc_get_type_abort( + req->private_data, struct cli_readall_state); + + SMB_ASSERT(req->state >= ASYNC_REQ_DONE); + if (req->state == ASYNC_REQ_ERROR) { + return req->status; + } + *received = state->received; + *rcvbuf = state->buf; + return NT_STATUS_OK; +} + /* * Parallel read support. * @@ -288,7 +413,7 @@ 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[i] = cli_readall_send( state->reqs, cli, fnum, state->start_offset + state->requested, request_thistime); @@ -323,8 +448,8 @@ static void cli_pull_read_done(struct async_req *read_req) struct cli_request *read_state = cli_request_get(read_req); NTSTATUS status; - status = cli_read_andx_recv(read_req, &read_state->data.read.received, - &read_state->data.read.rcvbuf); + status = cli_readall_recv(read_req, &read_state->data.read.received, + &read_state->data.read.rcvbuf); if (!NT_STATUS_IS_OK(status)) { async_req_error(state->req, status); return; @@ -383,7 +508,7 @@ static void cli_pull_read_done(struct async_req *read_req) + state->requested), state->top_req)); - new_req = cli_read_andx_send( + new_req = cli_readall_send( state->reqs, state->cli, state->fnum, state->start_offset + state->requested, request_thistime); -- 1.6.0.4