From ef00eb90e56dfac2d823582cec92abf1fa9905f1 Mon Sep 17 00:00:00 2001 From: David Disseldorp Date: Tue, 15 Jan 2013 17:22:59 +0100 Subject: s3-vfs: add copy_chunk vfs hooks copy_chunk copies n bytes from a source file at a specific offset to a destination file at a given offset. This interface will be used in handling smb2 FSCTL_SRV_COPYCHUNK ioctl requests. Use a pread/pwrite loop in vfs_default, so that requests referring to the same src and dest file are possible. Provide send and receive hooks for copy chunk VFS interface, allowing asynchronous behaviour. Check whether the request source offset + length exceeds the current size. Return STATUS_INVALID_VIEW_SIZE under such a condition, matching Windows server behaviour. Reviewed by: Jeremy Allison --- source3/modules/vfs_default.c | 111 +++++++++++++++++++++++++++++++++++++++ source3/modules/vfs_full_audit.c | 38 ++++++++++++++ source3/modules/vfs_time_audit.c | 85 ++++++++++++++++++++++++++++++ 3 files changed, 234 insertions(+) (limited to 'source3/modules') diff --git a/source3/modules/vfs_default.c b/source3/modules/vfs_default.c index 0f651dca51..d937c4a862 100644 --- a/source3/modules/vfs_default.c +++ b/source3/modules/vfs_default.c @@ -31,6 +31,7 @@ #include "librpc/gen_ndr/ndr_dfsblobs.h" #include "lib/util/tevent_unix.h" #include "lib/asys/asys.h" +#include "lib/util/tevent_ntstatus.h" #undef DBGC_CLASS #define DBGC_CLASS DBGC_VFS @@ -1323,6 +1324,114 @@ static NTSTATUS vfswrap_fsctl(struct vfs_handle_struct *handle, return NT_STATUS_NOT_SUPPORTED; } +struct vfs_cc_state { + off_t copied; + uint8_t buf[65536]; +}; + +static struct tevent_req *vfswrap_copy_chunk_send(struct vfs_handle_struct *handle, + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct files_struct *src_fsp, + off_t src_off, + struct files_struct *dest_fsp, + off_t dest_off, + off_t num) +{ + struct tevent_req *req; + struct vfs_cc_state *vfs_cc_state; + NTSTATUS status; + + DEBUG(10, ("performing server side copy chunk of length %lu\n", + (unsigned long)num)); + + req = tevent_req_create(mem_ctx, &vfs_cc_state, struct vfs_cc_state); + if (req == NULL) { + return NULL; + } + + status = vfs_stat_fsp(src_fsp); + if (tevent_req_nterror(req, status)) { + return tevent_req_post(req, ev); + } + + if (src_fsp->fsp_name->st.st_ex_size < src_off + num) { + /* + * [MS-SMB2] 3.3.5.15.6 Handling a Server-Side Data Copy Request + * If the SourceOffset or SourceOffset + Length extends beyond + * the end of file, the server SHOULD<240> treat this as a + * STATUS_END_OF_FILE error. + * ... + * <240> Section 3.3.5.15.6: Windows servers will return + * STATUS_INVALID_VIEW_SIZE instead of STATUS_END_OF_FILE. + */ + tevent_req_nterror(req, NT_STATUS_INVALID_VIEW_SIZE); + return tevent_req_post(req, ev); + } + + /* could use 2.6.33+ sendfile here to do this in kernel */ + while (vfs_cc_state->copied < num) { + ssize_t ret; + off_t this_num = MIN(sizeof(vfs_cc_state->buf), + num - vfs_cc_state->copied); + + ret = SMB_VFS_PREAD(src_fsp, vfs_cc_state->buf, + this_num, src_off); + if (ret == -1) { + tevent_req_nterror(req, map_nt_error_from_unix(errno)); + return tevent_req_post(req, ev); + } + if (ret != this_num) { + /* zero tolerance for short reads */ + tevent_req_nterror(req, NT_STATUS_IO_DEVICE_ERROR); + return tevent_req_post(req, ev); + } + src_off += ret; + + ret = SMB_VFS_PWRITE(dest_fsp, vfs_cc_state->buf, + this_num, dest_off); + if (ret == -1) { + tevent_req_nterror(req, map_nt_error_from_unix(errno)); + return tevent_req_post(req, ev); + } + if (ret != this_num) { + /* zero tolerance for short writes */ + tevent_req_nterror(req, NT_STATUS_IO_DEVICE_ERROR); + return tevent_req_post(req, ev); + } + dest_off += ret; + + vfs_cc_state->copied += this_num; + } + + tevent_req_done(req); + return tevent_req_post(req, ev); +} + +static NTSTATUS vfswrap_copy_chunk_recv(struct vfs_handle_struct *handle, + struct tevent_req *req, + off_t *copied) +{ + struct vfs_cc_state *vfs_cc_state = tevent_req_data(req, + struct vfs_cc_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + DEBUG(2, ("server side copy chunk failed: %s\n", + nt_errstr(status))); + *copied = 0; + tevent_req_received(req); + return status; + } + + *copied = vfs_cc_state->copied; + DEBUG(10, ("server side copy chunk copied %lu\n", + (unsigned long)*copied)); + tevent_req_received(req); + + return NT_STATUS_OK; +} + /******************************************************************** Given a stat buffer return the allocated size on disk, taking into account sparse files. @@ -2367,6 +2476,8 @@ static struct vfs_fn_pointers vfs_default_fns = { .strict_unlock_fn = vfswrap_strict_unlock, .translate_name_fn = vfswrap_translate_name, .fsctl_fn = vfswrap_fsctl, + .copy_chunk_send_fn = vfswrap_copy_chunk_send, + .copy_chunk_recv_fn = vfswrap_copy_chunk_recv, /* NT ACL operations. */ diff --git a/source3/modules/vfs_full_audit.c b/source3/modules/vfs_full_audit.c index b1fb090dc6..549f55e189 100644 --- a/source3/modules/vfs_full_audit.c +++ b/source3/modules/vfs_full_audit.c @@ -161,6 +161,8 @@ typedef enum _vfs_op_type { SMB_VFS_OP_STRICT_LOCK, SMB_VFS_OP_STRICT_UNLOCK, SMB_VFS_OP_TRANSLATE_NAME, + SMB_VFS_OP_COPY_CHUNK_SEND, + SMB_VFS_OP_COPY_CHUNK_RECV, /* NT ACL operations. */ @@ -281,6 +283,8 @@ static struct { { SMB_VFS_OP_STRICT_LOCK, "strict_lock" }, { SMB_VFS_OP_STRICT_UNLOCK, "strict_unlock" }, { SMB_VFS_OP_TRANSLATE_NAME, "translate_name" }, + { SMB_VFS_OP_COPY_CHUNK_SEND, "copy_chunk_send" }, + { SMB_VFS_OP_COPY_CHUNK_RECV, "copy_chunk_recv" }, { SMB_VFS_OP_FGET_NT_ACL, "fget_nt_acl" }, { SMB_VFS_OP_GET_NT_ACL, "get_nt_acl" }, { SMB_VFS_OP_FSET_NT_ACL, "fset_nt_acl" }, @@ -1732,6 +1736,38 @@ static NTSTATUS smb_full_audit_translate_name(struct vfs_handle_struct *handle, return result; } +static struct tevent_req *smb_full_audit_copy_chunk_send(struct vfs_handle_struct *handle, + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct files_struct *src_fsp, + off_t src_off, + struct files_struct *dest_fsp, + off_t dest_off, + off_t num) +{ + struct tevent_req *req; + + req = SMB_VFS_NEXT_COPY_CHUNK_SEND(handle, mem_ctx, ev, src_fsp, + src_off, dest_fsp, dest_off, num); + + do_log(SMB_VFS_OP_COPY_CHUNK_SEND, req, handle, ""); + + return req; +} + +static NTSTATUS smb_full_audit_copy_chunk_recv(struct vfs_handle_struct *handle, + struct tevent_req *req, + off_t *copied) +{ + NTSTATUS result; + + result = SMB_VFS_NEXT_COPY_CHUNK_RECV(handle, req, copied); + + do_log(SMB_VFS_OP_COPY_CHUNK_RECV, NT_STATUS_IS_OK(result), handle, ""); + + return result; +} + static NTSTATUS smb_full_audit_fget_nt_acl(vfs_handle_struct *handle, files_struct *fsp, uint32 security_info, TALLOC_CTX *mem_ctx, @@ -2131,6 +2167,8 @@ static struct vfs_fn_pointers vfs_full_audit_fns = { .strict_lock_fn = smb_full_audit_strict_lock, .strict_unlock_fn = smb_full_audit_strict_unlock, .translate_name_fn = smb_full_audit_translate_name, + .copy_chunk_send_fn = smb_full_audit_copy_chunk_send, + .copy_chunk_recv_fn = smb_full_audit_copy_chunk_recv, .fget_nt_acl_fn = smb_full_audit_fget_nt_acl, .get_nt_acl_fn = smb_full_audit_get_nt_acl, .fset_nt_acl_fn = smb_full_audit_fset_nt_acl, diff --git a/source3/modules/vfs_time_audit.c b/source3/modules/vfs_time_audit.c index 95b4148232..1b14d650af 100644 --- a/source3/modules/vfs_time_audit.c +++ b/source3/modules/vfs_time_audit.c @@ -29,6 +29,7 @@ #include "smbd/smbd.h" #include "ntioctl.h" #include "lib/util/tevent_unix.h" +#include "lib/util/tevent_ntstatus.h" #undef DBGC_CLASS #define DBGC_CLASS DBGC_VFS @@ -1668,6 +1669,88 @@ static NTSTATUS smb_time_audit_translate_name(struct vfs_handle_struct *handle, return result; } +struct time_audit_cc_state { + struct timespec ts_send; + struct vfs_handle_struct *handle; + off_t copied; +}; +static void smb_time_audit_copy_chunk_done(struct tevent_req *subreq); + +static struct tevent_req *smb_time_audit_copy_chunk_send(struct vfs_handle_struct *handle, + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct files_struct *src_fsp, + off_t src_off, + struct files_struct *dest_fsp, + off_t dest_off, + off_t num) +{ + struct tevent_req *req; + struct tevent_req *subreq; + struct time_audit_cc_state *cc_state; + + req = tevent_req_create(mem_ctx, &cc_state, struct time_audit_cc_state); + if (req == NULL) { + return NULL; + } + + cc_state->handle = handle; + clock_gettime_mono(&cc_state->ts_send); + subreq = SMB_VFS_NEXT_COPY_CHUNK_SEND(handle, cc_state, ev, + src_fsp, src_off, + dest_fsp, dest_off, num); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + + tevent_req_set_callback(subreq, smb_time_audit_copy_chunk_done, req); + return req; +} + +static void smb_time_audit_copy_chunk_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct time_audit_cc_state *cc_state + = tevent_req_data(req, struct time_audit_cc_state); + NTSTATUS status; + + status = SMB_VFS_NEXT_COPY_CHUNK_RECV(cc_state->handle, + subreq, + &cc_state->copied); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + tevent_req_done(req); +} + +static NTSTATUS smb_time_audit_copy_chunk_recv(struct vfs_handle_struct *handle, + struct tevent_req *req, + off_t *copied) +{ + struct time_audit_cc_state *cc_state + = tevent_req_data(req, struct time_audit_cc_state); + struct timespec ts_recv; + double timediff; + NTSTATUS status; + + clock_gettime_mono(&ts_recv); + timediff = nsec_time_diff(&ts_recv, &cc_state->ts_send)*1.0e-9; + if (timediff > audit_timeout) { + smb_time_audit_log("copy_chunk", timediff); + } + + *copied = cc_state->copied; + if (tevent_req_is_nterror(req, &status)) { + tevent_req_received(req); + return status; + } + + tevent_req_received(req); + return NT_STATUS_OK; +} + static NTSTATUS smb_time_audit_fget_nt_acl(vfs_handle_struct *handle, files_struct *fsp, uint32 security_info, @@ -2179,6 +2262,8 @@ static struct vfs_fn_pointers vfs_time_audit_fns = { .strict_lock_fn = smb_time_audit_strict_lock, .strict_unlock_fn = smb_time_audit_strict_unlock, .translate_name_fn = smb_time_audit_translate_name, + .copy_chunk_send_fn = smb_time_audit_copy_chunk_send, + .copy_chunk_recv_fn = smb_time_audit_copy_chunk_recv, .fget_nt_acl_fn = smb_time_audit_fget_nt_acl, .get_nt_acl_fn = smb_time_audit_get_nt_acl, .fset_nt_acl_fn = smb_time_audit_fset_nt_acl, -- cgit