From aaa2caa638b4a5709f4651a9c49625ba8a7e1c50 Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Sat, 10 Oct 2009 11:15:42 +0200 Subject: [PATCH] s3: Fix bug 6606 This is a port of 1f34ffa0caae5 and 24309bdb2efc to 3.4. --- source3/libsmb/clireadwrite.c | 189 +++++++++++++++++++++++++++++++++++------ 1 files changed, 164 insertions(+), 25 deletions(-) diff --git a/source3/libsmb/clireadwrite.c b/source3/libsmb/clireadwrite.c index b1064ba..e6c7fb4 100644 --- a/source3/libsmb/clireadwrite.c +++ b/source3/libsmb/clireadwrite.c @@ -195,6 +195,135 @@ NTSTATUS cli_read_andx_recv(struct async_req *req, ssize_t *received, return NT_STATUS_OK; } +struct cli_readall_state { + struct tevent_context *ev; + 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 event_context *ev, + struct cli_state *cli, + uint16_t fnum, + off_t offset, size_t size) +{ + struct async_req *req, *subreq; + struct cli_readall_state *state; + + if (!async_req_setup(mem_ctx, &req, &state, + struct cli_readall_state)) { + return NULL; + } + state->ev = ev; + 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, ev, cli, fnum, offset, size); + if (async_req_nomem(subreq, req)) { + 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_nterror(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->ev, 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); + NTSTATUS status; + + if (async_req_is_nterror(req, &status)) { + return status; + } + *received = state->received; + *rcvbuf = state->buf; + return NT_STATUS_OK; +} + +struct cli_pull_subreq { + struct async_req *req; + ssize_t received; + uint8_t *buf; +}; + /* * Parallel read support. * @@ -221,7 +350,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? @@ -308,7 +437,7 @@ struct async_req *cli_pull_send(TALLOC_CTX *mem_ctx, 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; @@ -317,6 +446,7 @@ struct async_req *cli_pull_send(TALLOC_CTX *mem_ctx, 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; @@ -328,17 +458,17 @@ struct async_req *cli_pull_send(TALLOC_CTX *mem_ctx, size_left = size - state->requested; request_thistime = MIN(size_left, state->chunk_size); - state->reqs[i] = cli_read_andx_send( + subreq->req = cli_readall_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; + subreq->req->async.fn = cli_pull_read_done; + subreq->req->async.priv = result; state->requested += request_thistime; } @@ -360,12 +490,24 @@ static void cli_pull_read_done(struct async_req *read_req) 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 = talloc_get_type_abort( - read_req->private_data, struct cli_request); + 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 (read_req == pull_subreq->req) { + break; + } + } + if (i == state->num_reqs) { + /* Huh -- received something we did not send?? */ + async_req_nterror(pull_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_readall_recv(read_req, &pull_subreq->received, + &pull_subreq->buf); if (!NT_STATUS_IS_OK(status)) { async_req_nterror(state->req, status); return; @@ -380,36 +522,33 @@ 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_read; DEBUG(11, ("cli_pull_read_done: top_req = %d\n", state->top_req)); - if (state->reqs[state->top_req]->state < ASYNC_REQ_DONE) { + top_read = &state->reqs[state->top_req]; + + if (state->reqs[state->top_req].req->state < ASYNC_REQ_DONE) { DEBUG(11, ("cli_pull_read_done: top request not yet " "done\n")); return; } - top_read = talloc_get_type_abort( - state->reqs[state->top_req]->private_data, - struct cli_request); - DEBUG(10, ("cli_pull_read_done: Pushing %d bytes, %d already " - "pushed\n", (int)top_read->data.read.received, + "pushed\n", (int)top_read->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_read->buf, + top_read->received, state->priv); if (!NT_STATUS_IS_OK(status)) { async_req_nterror(state->req, status); return; } - state->pushed += top_read->data.read.received; + state->pushed += top_read->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; @@ -426,7 +565,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->ev, state->cli, state->fnum, state->start_offset + state->requested, @@ -439,7 +578,7 @@ static void cli_pull_read_done(struct async_req *read_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; } -- 1.6.0.4