summaryrefslogtreecommitdiff
path: root/source3/modules/vfs_default.c
diff options
context:
space:
mode:
Diffstat (limited to 'source3/modules/vfs_default.c')
-rw-r--r--source3/modules/vfs_default.c111
1 files changed, 111 insertions, 0 deletions
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. */