diff options
-rw-r--r-- | source3/libsmb/clireadwrite.c | 122 |
1 files changed, 118 insertions, 4 deletions
diff --git a/source3/libsmb/clireadwrite.c b/source3/libsmb/clireadwrite.c index 27f9e4fd98..b6901961d0 100644 --- a/source3/libsmb/clireadwrite.c +++ b/source3/libsmb/clireadwrite.c @@ -230,6 +230,120 @@ NTSTATUS cli_read_andx_recv(struct tevent_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 tevent_req *subreq); + +static struct tevent_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 tevent_req *req, *subreq; + struct cli_readall_state *state; + + req = tevent_req_create(mem_ctx, &state, struct cli_readall_state); + if (req == NULL) { + 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 (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_readall_done, req); + return req; +} + +static void cli_readall_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_readall_state *state = tevent_req_data( + req, 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)) { + tevent_req_nterror(req, status); + return; + } + + if ((state->received == 0) && (received == state->size)) { + /* Ideal case: Got it all in one run */ + state->buf = buf; + state->received += received; + tevent_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 (tevent_req_nomem(state->buf, req)) { + return; + } + } + memcpy(state->buf + state->received, buf, received); + state->received += received; + + TALLOC_FREE(subreq); + + if (state->received >= state->size) { + tevent_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 (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, cli_readall_done, req); +} + +static NTSTATUS cli_readall_recv(struct tevent_req *req, ssize_t *received, + uint8_t **rcvbuf) +{ + struct cli_readall_state *state = tevent_req_data( + req, struct cli_readall_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + *received = state->received; + *rcvbuf = state->buf; + return NT_STATUS_OK; +} + struct cli_pull_subreq { struct tevent_req *req; ssize_t received; @@ -368,7 +482,7 @@ struct tevent_req *cli_pull_send(TALLOC_CTX *mem_ctx, size_left = size - state->requested; request_thistime = MIN(size_left, state->chunk_size); - subreq->req = cli_read_andx_send( + subreq->req = cli_readall_send( state->reqs, ev, cli, fnum, state->start_offset + state->requested, request_thistime); @@ -413,8 +527,8 @@ static void cli_pull_read_done(struct tevent_req *subreq) return; } - status = cli_read_andx_recv(subreq, &pull_subreq->received, - &pull_subreq->buf); + status = cli_readall_recv(subreq, &pull_subreq->received, + &pull_subreq->buf); if (!NT_STATUS_IS_OK(status)) { tevent_req_nterror(state->req, status); return; @@ -472,7 +586,7 @@ static void cli_pull_read_done(struct tevent_req *subreq) + 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, |