diff options
author | Ralph Boehme <slow@samba.org> | 2017-03-21 09:17:03 +0100 |
---|---|---|
committer | Jeremy Allison <jra@samba.org> | 2017-03-28 17:45:20 +0200 |
commit | 8bc94a9f9d54134489bf4c98ced8e60e2712ddf4 (patch) | |
tree | bcd9aa23799a73e78fc1aec0af6cbe5d6cd21b70 /source3 | |
parent | 6314dda7fe0a83a3e16fe59d87232782817daadc (diff) | |
download | samba-8bc94a9f9d54134489bf4c98ced8e60e2712ddf4.tar.gz |
s3/smbd: implement a serializing async copy-chunk loop
Later commits will make the low level copy-chunk implementation async
using a thread pool. That means the individual chunks may be scheduled
and copied out-of-order at the low level.
According to conversation with MS Dochelp, a server implementation
must process individual chunks in order.
Signed-off-by: Ralph Boehme <slow@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>
Diffstat (limited to 'source3')
-rw-r--r-- | source3/smbd/smb2_ioctl_network_fs.c | 188 |
1 files changed, 102 insertions, 86 deletions
diff --git a/source3/smbd/smb2_ioctl_network_fs.c b/source3/smbd/smb2_ioctl_network_fs.c index efb6fe7343b..42d98579e79 100644 --- a/source3/smbd/smb2_ioctl_network_fs.c +++ b/source3/smbd/smb2_ioctl_network_fs.c @@ -76,11 +76,11 @@ static NTSTATUS copychunk_check_limits(struct srv_copychunk_copy *cc_copy) } struct fsctl_srv_copychunk_state { + struct tevent_context *ev; struct connection_struct *conn; struct srv_copychunk_copy cc_copy; - uint32_t dispatch_count; - uint32_t recv_count; - uint32_t bad_recv_count; + uint32_t current_chunk; + uint32_t next_chunk; NTSTATUS status; off_t total_written; struct files_struct *src_fsp; @@ -156,6 +156,8 @@ static NTSTATUS copychunk_check_handles(uint32_t ctl_code, return NT_STATUS_OK; } +static NTSTATUS fsctl_srv_copychunk_loop(struct tevent_req *req); + static struct tevent_req *fsctl_srv_copychunk_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, uint32_t ctl_code, @@ -164,13 +166,12 @@ static struct tevent_req *fsctl_srv_copychunk_send(TALLOC_CTX *mem_ctx, size_t in_max_output, struct smbd_smb2_request *smb2req) { - struct tevent_req *req; - enum ndr_err_code ndr_ret; + struct tevent_req *req = NULL; + struct fsctl_srv_copychunk_state *state = NULL; uint64_t src_persistent_h; uint64_t src_volatile_h; - int i; - struct srv_copychunk *chunk; - struct fsctl_srv_copychunk_state *state; + enum ndr_err_code ndr_ret; + NTSTATUS status; /* handler for both copy-chunk variants */ SMB_ASSERT((ctl_code == FSCTL_SRV_COPYCHUNK) @@ -181,7 +182,10 @@ static struct tevent_req *fsctl_srv_copychunk_send(TALLOC_CTX *mem_ctx, if (req == NULL) { return NULL; } - state->conn = dst_fsp->conn; + *state = (struct fsctl_srv_copychunk_state) { + .conn = dst_fsp->conn, + .ev = ev, + }; if (in_max_output < sizeof(struct srv_copychunk_rsp)) { DEBUG(3, ("max output %d not large enough to hold copy chunk " @@ -234,109 +238,123 @@ static struct tevent_req *fsctl_srv_copychunk_send(TALLOC_CTX *mem_ctx, /* any errors from here onwards should carry copychunk response data */ state->out_data = COPYCHUNK_OUT_RSP; + status = fsctl_srv_copychunk_loop(req); + if (tevent_req_nterror(req, status)) { + return tevent_req_post(req, ev); + } + + return req; +} + +static NTSTATUS fsctl_srv_copychunk_loop(struct tevent_req *req) +{ + struct fsctl_srv_copychunk_state *state = tevent_req_data( + req, struct fsctl_srv_copychunk_state); + struct tevent_req *subreq = NULL; + struct srv_copychunk *chunk = NULL; + + if (state->next_chunk > state->cc_copy.chunk_count) { + DBG_ERR("Copy-chunk loop next_chunk [%d] chunk_count [%d]\n", + state->next_chunk, state->cc_copy.chunk_count); + return NT_STATUS_INTERNAL_ERROR; + } + if (state->cc_copy.chunk_count == 0) { - struct tevent_req *vfs_subreq; /* * Process as OS X copyfile request. This is currently * the only copychunk request with a chunk count of 0 * we will process. */ - if (!state->src_fsp->aapl_copyfile_supported) { - tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); - return tevent_req_post(req, ev); - } - if (!state->dst_fsp->aapl_copyfile_supported) { - tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); - return tevent_req_post(req, ev); + if (!state->src_fsp->aapl_copyfile_supported || + !state->dst_fsp->aapl_copyfile_supported) + { + /* + * This must not return an error but just a chunk count + * of 0 in the response. + */ + tevent_req_done(req); + tevent_req_post(req, state->ev); + return NT_STATUS_OK; } state->aapl_copyfile = true; - vfs_subreq = SMB_VFS_COPY_CHUNK_SEND(dst_fsp->conn, - state, ev, - state->src_fsp, - 0, - state->dst_fsp, - 0, - 0); - if (tevent_req_nomem(vfs_subreq, req)) { - return tevent_req_post(req, ev); + + subreq = SMB_VFS_COPY_CHUNK_SEND(state->dst_fsp->conn, + state, + state->ev, + state->src_fsp, + 0, + state->dst_fsp, + 0, + 0); + if (subreq == NULL) { + return NT_STATUS_NO_MEMORY; } - tevent_req_set_callback(vfs_subreq, + tevent_req_set_callback(subreq, fsctl_srv_copychunk_vfs_done, req); - state->dispatch_count++; - return req; + return NT_STATUS_OK; } - for (i = 0; i < state->cc_copy.chunk_count; i++) { - struct tevent_req *vfs_subreq; - chunk = &state->cc_copy.chunks[i]; - vfs_subreq = SMB_VFS_COPY_CHUNK_SEND(dst_fsp->conn, - state, ev, - state->src_fsp, - chunk->source_off, - state->dst_fsp, - chunk->target_off, - chunk->length); - if (vfs_subreq == NULL) { - DEBUG(0, ("VFS copy chunk send failed\n")); - state->status = NT_STATUS_NO_MEMORY; - if (state->dispatch_count == 0) { - /* nothing dispatched, return immediately */ - tevent_req_nterror(req, state->status); - return tevent_req_post(req, ev); - } else { - /* - * wait for dispatched to complete before - * returning error. - */ - break; - } - } - tevent_req_set_callback(vfs_subreq, - fsctl_srv_copychunk_vfs_done, req); - state->dispatch_count++; + chunk = &state->cc_copy.chunks[state->current_chunk]; + + /* + * Doing the increment and calculation for the next chunk here and not + * in the done function, as a later commit will make this a more + * sophisticated logic. + */ + state->next_chunk++; + + subreq = SMB_VFS_COPY_CHUNK_SEND(state->dst_fsp->conn, + state, + state->ev, + state->src_fsp, + chunk->source_off, + state->dst_fsp, + chunk->target_off, + chunk->length); + if (tevent_req_nomem(subreq, req)) { + return NT_STATUS_NO_MEMORY; } + tevent_req_set_callback(subreq, fsctl_srv_copychunk_vfs_done, req); - return req; + return NT_STATUS_OK; } static void fsctl_srv_copychunk_vfs_done(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data( subreq, struct tevent_req); - struct fsctl_srv_copychunk_state *state = tevent_req_data(req, - struct fsctl_srv_copychunk_state); + struct fsctl_srv_copychunk_state *state = tevent_req_data( + req, struct fsctl_srv_copychunk_state); off_t chunk_nwritten; NTSTATUS status; - state->recv_count++; status = SMB_VFS_COPY_CHUNK_RECV(state->conn, subreq, &chunk_nwritten); TALLOC_FREE(subreq); - if (NT_STATUS_IS_OK(status)) { - DEBUG(10, ("good copy chunk recv %u of %u\n", - (unsigned int)state->recv_count, - (unsigned int)state->dispatch_count)); - state->total_written += chunk_nwritten; - } else { - DEBUG(0, ("bad status in copy chunk recv %u of %u: %s\n", - (unsigned int)state->recv_count, - (unsigned int)state->dispatch_count, - nt_errstr(status))); - state->bad_recv_count++; - /* may overwrite previous failed status */ - state->status = status; - } - - if (state->recv_count != state->dispatch_count) { - /* - * Wait for all VFS copy_chunk requests to complete, even - * if an error is received for a specific chunk. - */ + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("copy chunk failed [%s] chunk [%u] of [%u]\n", + nt_errstr(status), + (unsigned int)state->next_chunk, + (unsigned int)state->cc_copy.chunk_count); + tevent_req_nterror(req, status); return; } - if (!tevent_req_nterror(req, state->status)) { + DBG_DEBUG("good copy chunk [%u] of [%u]\n", + (unsigned int)state->next_chunk, + (unsigned int)state->cc_copy.chunk_count); + state->total_written += chunk_nwritten; + + state->current_chunk = state->next_chunk; + + if (state->next_chunk == state->cc_copy.chunk_count) { tevent_req_done(req); + return; + } + + status = fsctl_srv_copychunk_loop(req); + if (tevent_req_nterror(req, status)) { + return; } } @@ -361,7 +379,7 @@ static NTSTATUS fsctl_srv_copychunk_recv(struct tevent_req *req, if (state->aapl_copyfile == true) { cc_rsp->chunks_written = 0; } else { - cc_rsp->chunks_written = state->recv_count - state->bad_recv_count; + cc_rsp->chunks_written = state->current_chunk; } cc_rsp->chunk_bytes_written = 0; cc_rsp->total_bytes_written = state->total_written; @@ -371,9 +389,7 @@ static NTSTATUS fsctl_srv_copychunk_recv(struct tevent_req *req, assert(1); break; } - status = state->status; - tevent_req_received(req); - + status = tevent_req_simple_recv_ntstatus(req); return status; } |