[PATCH] Fix the new async copy-chunk for streams

Ralph Böhme slow at samba.org
Tue May 16 11:37:46 UTC 2017


On Mon, May 15, 2017 at 08:29:00PM +0200, Ralph Böhme via samba-technical wrote:
> Nevertheless I agree that it makes sense to add a basic test to smb2.ioctl
> itself. I will post an updated patchset.

attached.

Fwiw, I tried to consolidate test_setup_copy_chunk() and make it a public
utility function but I failed miserably to figure out the required headers and
deps.

-slow
-------------- next part --------------
From 48981924657018db9379251ad8edaec5a8c999e5 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Fri, 12 May 2017 07:58:01 +0200
Subject: [PATCH 1/7] vfs_streams_xattr: add pread_send/recv and
 pwrite_send/recv

This is needed to support copy-chunk of streams. vfs_default issues
calls to async pread and pwrite (send/recv versions) since commit
60e45a2d25401eaf9a15a86d19114670ccfde259.

Bug: https://bugzilla.samba.org/show_bug.cgi?id=12787

Signed-off-by: Ralph Boehme <slow at samba.org>
Reviewed-by: David Disseldorp <ddiss at samba.org>
---
 source3/modules/vfs_streams_xattr.c | 165 ++++++++++++++++++++++++++++++++++++
 1 file changed, 165 insertions(+)

diff --git a/source3/modules/vfs_streams_xattr.c b/source3/modules/vfs_streams_xattr.c
index 2943e52..f481f27 100644
--- a/source3/modules/vfs_streams_xattr.c
+++ b/source3/modules/vfs_streams_xattr.c
@@ -25,6 +25,7 @@
 #include "smbd/smbd.h"
 #include "system/filesys.h"
 #include "../lib/crypto/md5.h"
+#include "lib/util/tevent_unix.h"
 
 #undef DBGC_CLASS
 #define DBGC_CLASS DBGC_VFS
@@ -1048,6 +1049,166 @@ static ssize_t streams_xattr_pread(vfs_handle_struct *handle,
         return overlap;
 }
 
+struct streams_xattr_pread_state {
+	ssize_t nread;
+	struct vfs_aio_state vfs_aio_state;
+};
+
+static void streams_xattr_pread_done(struct tevent_req *subreq);
+
+static struct tevent_req *streams_xattr_pread_send(
+	struct vfs_handle_struct *handle,
+	TALLOC_CTX *mem_ctx,
+	struct tevent_context *ev,
+	struct files_struct *fsp,
+	void *data,
+	size_t n, off_t offset)
+{
+	struct tevent_req *req = NULL;
+	struct tevent_req *subreq = NULL;
+	struct streams_xattr_pread_state *state = NULL;
+	struct stream_io *sio =
+		(struct stream_io *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
+
+	req = tevent_req_create(mem_ctx, &state,
+				struct streams_xattr_pread_state);
+	if (req == NULL) {
+		return NULL;
+	}
+
+	if (sio == NULL) {
+		subreq = SMB_VFS_NEXT_PREAD_SEND(state, ev, handle, fsp,
+						 data, n, offset);
+		if (tevent_req_nomem(req, subreq)) {
+			return tevent_req_post(req, ev);
+		}
+		tevent_req_set_callback(subreq, streams_xattr_pread_done, req);
+		return req;
+	}
+
+	state->nread = SMB_VFS_PREAD(fsp, data, n, offset);
+	if (state->nread != n) {
+		if (state->nread != -1) {
+			errno = EIO;
+		}
+		tevent_req_error(req, errno);
+		return tevent_req_post(req, ev);
+	}
+
+	tevent_req_done(req);
+	return tevent_req_post(req, ev);
+}
+
+static void streams_xattr_pread_done(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	struct streams_xattr_pread_state *state = tevent_req_data(
+		req, struct streams_xattr_pread_state);
+
+	state->nread = SMB_VFS_PREAD_RECV(subreq, &state->vfs_aio_state);
+	TALLOC_FREE(subreq);
+
+	if (tevent_req_error(req, state->vfs_aio_state.error)) {
+		return;
+	}
+	tevent_req_done(req);
+}
+
+static ssize_t streams_xattr_pread_recv(struct tevent_req *req,
+					struct vfs_aio_state *vfs_aio_state)
+{
+	struct streams_xattr_pread_state *state = tevent_req_data(
+		req, struct streams_xattr_pread_state);
+
+	if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
+		return -1;
+	}
+
+	*vfs_aio_state = state->vfs_aio_state;
+	return state->nread;
+}
+
+struct streams_xattr_pwrite_state {
+	ssize_t nwritten;
+	struct vfs_aio_state vfs_aio_state;
+};
+
+static void streams_xattr_pwrite_done(struct tevent_req *subreq);
+
+static struct tevent_req *streams_xattr_pwrite_send(
+	struct vfs_handle_struct *handle,
+	TALLOC_CTX *mem_ctx,
+	struct tevent_context *ev,
+	struct files_struct *fsp,
+	const void *data,
+	size_t n, off_t offset)
+{
+	struct tevent_req *req = NULL;
+	struct tevent_req *subreq = NULL;
+	struct streams_xattr_pwrite_state *state = NULL;
+	struct stream_io *sio =
+		(struct stream_io *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
+
+	req = tevent_req_create(mem_ctx, &state,
+				struct streams_xattr_pwrite_state);
+	if (req == NULL) {
+		return NULL;
+	}
+
+	if (sio == NULL) {
+		subreq = SMB_VFS_NEXT_PWRITE_SEND(state, ev, handle, fsp,
+						  data, n, offset);
+		if (tevent_req_nomem(req, subreq)) {
+			return tevent_req_post(req, ev);
+		}
+		tevent_req_set_callback(subreq, streams_xattr_pwrite_done, req);
+		return req;
+	}
+
+	state->nwritten = SMB_VFS_PWRITE(fsp, data, n, offset);
+	if (state->nwritten != n) {
+		if (state->nwritten != -1) {
+			errno = EIO;
+		}
+		tevent_req_error(req, errno);
+		return tevent_req_post(req, ev);
+	}
+
+	tevent_req_done(req);
+	return tevent_req_post(req, ev);
+}
+
+static void streams_xattr_pwrite_done(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	struct streams_xattr_pwrite_state *state = tevent_req_data(
+		req, struct streams_xattr_pwrite_state);
+
+	state->nwritten = SMB_VFS_PWRITE_RECV(subreq, &state->vfs_aio_state);
+	TALLOC_FREE(subreq);
+
+	if (tevent_req_error(req, state->vfs_aio_state.error)) {
+		return;
+	}
+	tevent_req_done(req);
+}
+
+static ssize_t streams_xattr_pwrite_recv(struct tevent_req *req,
+					 struct vfs_aio_state *vfs_aio_state)
+{
+	struct streams_xattr_pwrite_state *state = tevent_req_data(
+		req, struct streams_xattr_pwrite_state);
+
+	if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
+		return -1;
+	}
+
+	*vfs_aio_state = state->vfs_aio_state;
+	return state->nwritten;
+}
+
 static int streams_xattr_ftruncate(struct vfs_handle_struct *handle,
 					struct files_struct *fsp,
 					off_t offset)
@@ -1151,6 +1312,10 @@ static struct vfs_fn_pointers vfs_streams_xattr_fns = {
 	.lstat_fn = streams_xattr_lstat,
 	.pread_fn = streams_xattr_pread,
 	.pwrite_fn = streams_xattr_pwrite,
+	.pread_send_fn = streams_xattr_pread_send,
+	.pread_recv_fn = streams_xattr_pread_recv,
+	.pwrite_send_fn = streams_xattr_pwrite_send,
+	.pwrite_recv_fn = streams_xattr_pwrite_recv,
 	.unlink_fn = streams_xattr_unlink,
 	.rename_fn = streams_xattr_rename,
 	.ftruncate_fn = streams_xattr_ftruncate,
-- 
2.9.3


From 164faa37d3295a377aba0adfc27dede107c0fd68 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Fri, 12 May 2017 14:40:03 +0200
Subject: [PATCH 2/7] vfs_fruit: add pread_send/recv and pwrite_send/recv

This is needed to support copy-chunk of streams. vfs_default issues
calls to async pread and pwrite (send/recv versions) since
commit60e45a2d25401eaf9a15a86d19114670ccfde259.

Bug: https://bugzilla.samba.org/show_bug.cgi?id=12787

Signed-off-by: Ralph Boehme <slow at samba.org>
Reviewed-by: David Disseldorp <ddiss at samba.org>
---
 source3/modules/vfs_fruit.c | 182 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 182 insertions(+)

diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c
index 273540e..63acdf8 100644
--- a/source3/modules/vfs_fruit.c
+++ b/source3/modules/vfs_fruit.c
@@ -31,6 +31,7 @@
 #include "../libcli/smb/smb2_create_ctx.h"
 #include "lib/util/sys_rw.h"
 #include "lib/util/tevent_ntstatus.h"
+#include "lib/util/tevent_unix.h"
 
 /*
  * Enhanced OS X and Netatalk compatibility
@@ -3755,6 +3756,105 @@ static ssize_t fruit_pread(vfs_handle_struct *handle,
 	return nread;
 }
 
+static bool fruit_must_handle_aio_stream(struct fio *fio)
+{
+	if (fio == NULL) {
+		return false;
+	};
+
+	if ((fio->type == ADOUBLE_META) &&
+	    (fio->config->meta == FRUIT_META_NETATALK))
+	{
+		return true;
+	}
+
+	if ((fio->type == ADOUBLE_RSRC) &&
+	    (fio->config->rsrc == FRUIT_RSRC_ADFILE))
+	{
+		return true;
+	}
+
+	return false;
+}
+
+struct fruit_pread_state {
+	ssize_t nread;
+	struct vfs_aio_state vfs_aio_state;
+};
+
+static void fruit_pread_done(struct tevent_req *subreq);
+
+static struct tevent_req *fruit_pread_send(
+	struct vfs_handle_struct *handle,
+	TALLOC_CTX *mem_ctx,
+	struct tevent_context *ev,
+	struct files_struct *fsp,
+	void *data,
+	size_t n, off_t offset)
+{
+	struct tevent_req *req = NULL;
+	struct tevent_req *subreq = NULL;
+	struct fruit_pread_state *state = NULL;
+	struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
+
+	req = tevent_req_create(mem_ctx, &state,
+				struct fruit_pread_state);
+	if (req == NULL) {
+		return NULL;
+	}
+
+	if (fruit_must_handle_aio_stream(fio)) {
+		state->nread = SMB_VFS_PREAD(fsp, data, n, offset);
+		if (state->nread != n) {
+			if (state->nread != -1) {
+				errno = EIO;
+			}
+			tevent_req_error(req, errno);
+			return tevent_req_post(req, ev);
+		}
+		tevent_req_done(req);
+		return tevent_req_post(req, ev);
+	}
+
+	subreq = SMB_VFS_NEXT_PREAD_SEND(state, ev, handle, fsp,
+					 data, n, offset);
+	if (tevent_req_nomem(req, subreq)) {
+		return tevent_req_post(req, ev);
+	}
+	tevent_req_set_callback(subreq, fruit_pread_done, req);
+	return req;
+}
+
+static void fruit_pread_done(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	struct fruit_pread_state *state = tevent_req_data(
+		req, struct fruit_pread_state);
+
+	state->nread = SMB_VFS_PREAD_RECV(subreq, &state->vfs_aio_state);
+	TALLOC_FREE(subreq);
+
+	if (tevent_req_error(req, state->vfs_aio_state.error)) {
+		return;
+	}
+	tevent_req_done(req);
+}
+
+static ssize_t fruit_pread_recv(struct tevent_req *req,
+					struct vfs_aio_state *vfs_aio_state)
+{
+	struct fruit_pread_state *state = tevent_req_data(
+		req, struct fruit_pread_state);
+
+	if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
+		return -1;
+	}
+
+	*vfs_aio_state = state->vfs_aio_state;
+	return state->nread;
+}
+
 static ssize_t fruit_pwrite_meta_stream(vfs_handle_struct *handle,
 					files_struct *fsp, const void *data,
 					size_t n, off_t offset)
@@ -3979,6 +4079,84 @@ static ssize_t fruit_pwrite(vfs_handle_struct *handle,
 	return nwritten;
 }
 
+struct fruit_pwrite_state {
+	ssize_t nwritten;
+	struct vfs_aio_state vfs_aio_state;
+};
+
+static void fruit_pwrite_done(struct tevent_req *subreq);
+
+static struct tevent_req *fruit_pwrite_send(
+	struct vfs_handle_struct *handle,
+	TALLOC_CTX *mem_ctx,
+	struct tevent_context *ev,
+	struct files_struct *fsp,
+	const void *data,
+	size_t n, off_t offset)
+{
+	struct tevent_req *req = NULL;
+	struct tevent_req *subreq = NULL;
+	struct fruit_pwrite_state *state = NULL;
+	struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
+
+	req = tevent_req_create(mem_ctx, &state,
+				struct fruit_pwrite_state);
+	if (req == NULL) {
+		return NULL;
+	}
+
+	if (fruit_must_handle_aio_stream(fio)) {
+		state->nwritten = SMB_VFS_PWRITE(fsp, data, n, offset);
+		if (state->nwritten != n) {
+			if (state->nwritten != -1) {
+				errno = EIO;
+			}
+			tevent_req_error(req, errno);
+			return tevent_req_post(req, ev);
+		}
+		tevent_req_done(req);
+		return tevent_req_post(req, ev);
+	}
+
+	subreq = SMB_VFS_NEXT_PWRITE_SEND(state, ev, handle, fsp,
+					  data, n, offset);
+	if (tevent_req_nomem(req, subreq)) {
+		return tevent_req_post(req, ev);
+	}
+	tevent_req_set_callback(subreq, fruit_pwrite_done, req);
+	return req;
+}
+
+static void fruit_pwrite_done(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	struct fruit_pwrite_state *state = tevent_req_data(
+		req, struct fruit_pwrite_state);
+
+	state->nwritten = SMB_VFS_PWRITE_RECV(subreq, &state->vfs_aio_state);
+	TALLOC_FREE(subreq);
+
+	if (tevent_req_error(req, state->vfs_aio_state.error)) {
+		return;
+	}
+	tevent_req_done(req);
+}
+
+static ssize_t fruit_pwrite_recv(struct tevent_req *req,
+					 struct vfs_aio_state *vfs_aio_state)
+{
+	struct fruit_pwrite_state *state = tevent_req_data(
+		req, struct fruit_pwrite_state);
+
+	if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
+		return -1;
+	}
+
+	*vfs_aio_state = state->vfs_aio_state;
+	return state->nwritten;
+}
+
 /**
  * Helper to stat/lstat the base file of an smb_fname.
  */
@@ -5427,6 +5605,10 @@ static struct vfs_fn_pointers vfs_fruit_fns = {
 	.open_fn = fruit_open,
 	.pread_fn = fruit_pread,
 	.pwrite_fn = fruit_pwrite,
+	.pread_send_fn = fruit_pread_send,
+	.pread_recv_fn = fruit_pread_recv,
+	.pwrite_send_fn = fruit_pwrite_send,
+	.pwrite_recv_fn = fruit_pwrite_recv,
 	.stat_fn = fruit_stat,
 	.lstat_fn = fruit_lstat,
 	.fstat_fn = fruit_fstat,
-- 
2.9.3


From f4fa85425440a11bb7dc777a97868f608bf0f117 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Fri, 12 May 2017 17:09:08 +0200
Subject: [PATCH 3/7] lib/torture: add two more ndr assert macros

Bug: https://bugzilla.samba.org/show_bug.cgi?id=12787

Signed-off-by: Ralph Boehme <slow at samba.org>
Reviewed-by: David Disseldorp <ddiss at samba.org>
---
 lib/torture/torture.h | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/lib/torture/torture.h b/lib/torture/torture.h
index 668458a..6b373a9 100644
--- a/lib/torture/torture.h
+++ b/lib/torture/torture.h
@@ -293,6 +293,15 @@ void torture_result(struct torture_context *test,
 	}\
 	} while(0)
 
+#define torture_assert_ndr_err_equal_goto(torture_ctx,got,expected,ret,label,cmt) \
+	do { enum ndr_err_code __got = got, __expected = expected; \
+	if (__got != __expected) { \
+		torture_result(torture_ctx, TORTURE_FAIL, __location__": "#got" was %d (%s), expected %d (%s): %s", __got, ndr_errstr(__got), __expected, __STRING(expected), cmt); \
+		ret = false; \
+		goto label; \
+	}\
+	} while(0)
+
 #define torture_assert_hresult_equal(torture_ctx, got, expected, cmt) \
 	do { HRESULT __got = got, __expected = expected; \
 	if (!HRES_IS_EQUAL(__got, __expected)) { \
@@ -647,6 +656,9 @@ static inline void torture_dump_data_str_cb(const char *buf, void *private_data)
 #define torture_assert_ndr_success(torture_ctx,expr,cmt) \
 		torture_assert_ndr_err_equal(torture_ctx,expr,NDR_ERR_SUCCESS,cmt)
 
+#define torture_assert_ndr_success_goto(torture_ctx,expr,ret,label,cmt) \
+		torture_assert_ndr_err_equal_goto(torture_ctx,expr,NDR_ERR_SUCCESS,ret,label,cmt)
+
 #define torture_assert_hresult_ok(torture_ctx,expr,cmt) \
 		torture_assert_hresult_equal(torture_ctx,expr,HRES_ERROR(0), cmt)
 
-- 
2.9.3


From f239d98b3d299d5f71127ddc46f34814cc4ba5a9 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Tue, 16 May 2017 13:13:08 +0200
Subject: [PATCH 4/7] s4/torture: smb2.ioctl: add src and dst path args to
 test_setup_copy_chunk

Just let the caller pass in the paths, no change in behaviour. A new
test in a subsequent commit will use it to pass paths to streams.

Bug: https://bugzilla.samba.org/show_bug.cgi?id=12787

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 source4/torture/smb2/ioctl.c | 62 +++++++++++++++++++++++++++++++++++---------
 1 file changed, 50 insertions(+), 12 deletions(-)

diff --git a/source4/torture/smb2/ioctl.c b/source4/torture/smb2/ioctl.c
index 53476fa..c9fc121 100644
--- a/source4/torture/smb2/ioctl.c
+++ b/source4/torture/smb2/ioctl.c
@@ -309,9 +309,11 @@ static bool test_setup_create_fill(struct torture_context *torture,
 static bool test_setup_copy_chunk(struct torture_context *torture,
 				  struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
 				  uint32_t nchunks,
+				  const char *src_name,
 				  struct smb2_handle *src_h,
 				  uint64_t src_size,
 				  uint32_t src_desired_access,
+				  const char *dst_name,
 				  struct smb2_handle *dest_h,
 				  uint64_t dest_size,
 				  uint32_t dest_desired_access,
@@ -323,12 +325,12 @@ static bool test_setup_copy_chunk(struct torture_context *torture,
 	NTSTATUS status;
 	enum ndr_err_code ndr_ret;
 
-	ok = test_setup_create_fill(torture, tree, mem_ctx, FNAME,
+	ok = test_setup_create_fill(torture, tree, mem_ctx, src_name,
 				    src_h, src_size, src_desired_access,
 				    FILE_ATTRIBUTE_NORMAL);
 	torture_assert(torture, ok, "src file create fill");
 
-	ok = test_setup_create_fill(torture, tree, mem_ctx, FNAME2,
+	ok = test_setup_create_fill(torture, tree, mem_ctx, dst_name,
 				    dest_h, dest_size, dest_desired_access,
 				    FILE_ATTRIBUTE_NORMAL);
 	torture_assert(torture, ok, "dest file create fill");
@@ -398,8 +400,10 @@ static bool test_ioctl_copy_chunk_simple(struct torture_context *torture,
 
 	ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
 				   1, /* 1 chunk */
+				   FNAME,
 				   &src_h, 4096, /* fill 4096 byte src file */
 				   SEC_RIGHTS_FILE_ALL,
+				   FNAME2,
 				   &dest_h, 0,	/* 0 byte dest file */
 				   SEC_RIGHTS_FILE_ALL,
 				   &cc_copy,
@@ -462,8 +466,10 @@ static bool test_ioctl_copy_chunk_multi(struct torture_context *torture,
 
 	ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
 				   2, /* chunks */
+				   FNAME,
 				   &src_h, 8192, /* src file */
 				   SEC_RIGHTS_FILE_ALL,
+				   FNAME2,
 				   &dest_h, 0,	/* dest file */
 				   SEC_RIGHTS_FILE_ALL,
 				   &cc_copy,
@@ -525,8 +531,10 @@ static bool test_ioctl_copy_chunk_tiny(struct torture_context *torture,
 
 	ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
 				   2, /* chunks */
+				   FNAME,
 				   &src_h, 96, /* src file */
 				   SEC_RIGHTS_FILE_ALL,
+				   FNAME2,
 				   &dest_h, 0,	/* dest file */
 				   SEC_RIGHTS_FILE_ALL,
 				   &cc_copy,
@@ -593,8 +601,10 @@ static bool test_ioctl_copy_chunk_over(struct torture_context *torture,
 
 	ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
 				   2, /* chunks */
+				   FNAME,
 				   &src_h, 8192, /* src file */
 				   SEC_RIGHTS_FILE_ALL,
+				   FNAME2,
 				   &dest_h, 4096, /* dest file */
 				   SEC_RIGHTS_FILE_ALL,
 				   &cc_copy,
@@ -662,8 +672,10 @@ static bool test_ioctl_copy_chunk_append(struct torture_context *torture,
 
 	ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
 				   2, /* chunks */
+				   FNAME,
 				   &src_h, 4096, /* src file */
 				   SEC_RIGHTS_FILE_ALL,
+				   FNAME2,
 				   &dest_h, 0,	/* dest file */
 				   SEC_RIGHTS_FILE_ALL,
 				   &cc_copy,
@@ -735,8 +747,10 @@ static bool test_ioctl_copy_chunk_limits(struct torture_context *torture,
 
 	ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
 				   1, /* chunks */
+				   FNAME,
 				   &src_h, 4096, /* src file */
 				   SEC_RIGHTS_FILE_ALL,
+				   FNAME2,
 				   &dest_h, 0,	/* dest file */
 				   SEC_RIGHTS_FILE_ALL,
 				   &cc_copy,
@@ -796,8 +810,10 @@ static bool test_ioctl_copy_chunk_src_lck(struct torture_context *torture,
 
 	ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
 				   1, /* chunks */
+				   FNAME,
 				   &src_h, 4096, /* src file */
 				   SEC_RIGHTS_FILE_ALL,
+				   FNAME2,
 				   &dest_h, 0,	/* dest file */
 				   SEC_RIGHTS_FILE_ALL,
 				   &cc_copy,
@@ -924,8 +940,10 @@ static bool test_ioctl_copy_chunk_dest_lck(struct torture_context *torture,
 
 	ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
 				   1, /* chunks */
+				   FNAME,
 				   &src_h, 4096, /* src file */
 				   SEC_RIGHTS_FILE_ALL,
+				   FNAME2,
 				   &dest_h, 4096,	/* dest file */
 				   SEC_RIGHTS_FILE_ALL,
 				   &cc_copy,
@@ -1020,8 +1038,10 @@ static bool test_ioctl_copy_chunk_bad_key(struct torture_context *torture,
 
 	ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
 				   1,
+				   FNAME,
 				   &src_h, 4096,
 				   SEC_RIGHTS_FILE_ALL,
+				   FNAME2,
 				   &dest_h, 0,
 				   SEC_RIGHTS_FILE_ALL,
 				   &cc_copy,
@@ -1070,8 +1090,10 @@ static bool test_ioctl_copy_chunk_src_is_dest(struct torture_context *torture,
 
 	ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
 				   1,
+				   FNAME,
 				   &src_h, 8192,
 				   SEC_RIGHTS_FILE_ALL,
+				   FNAME2,
 				   &dest_h, 0,
 				   SEC_RIGHTS_FILE_ALL,
 				   &cc_copy,
@@ -1187,8 +1209,10 @@ test_ioctl_copy_chunk_src_is_dest_overlap(struct torture_context *torture,
 	/* exceed the vfs_default copy buffer */
 	ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
 				   1,
+				   FNAME,
 				   &src_h, 2048 * 2,
 				   SEC_RIGHTS_FILE_ALL,
+				   FNAME2,
 				   &dest_h, 0,
 				   SEC_RIGHTS_FILE_ALL,
 				   &cc_copy,
@@ -1257,9 +1281,9 @@ static bool test_ioctl_copy_chunk_bad_access(struct torture_context *torture,
 	bool ok;
 	/* read permission on src */
 	ok = test_setup_copy_chunk(torture, tree, tmp_ctx, 1, /* 1 chunk */
-				   &src_h, 4096, /* fill 4096 byte src file */
+				   FNAME, &src_h, 4096, /* fill 4096 byte src file */
 				   SEC_FILE_READ_DATA | SEC_FILE_READ_ATTRIBUTE,
-				   &dest_h, 0, /* 0 byte dest file */
+				   FNAME2, &dest_h, 0, /* 0 byte dest file */
 				   SEC_RIGHTS_FILE_ALL, &cc_copy, &ioctl);
 	if (!ok) {
 		torture_fail(torture, "setup copy chunk error");
@@ -1284,9 +1308,9 @@ static bool test_ioctl_copy_chunk_bad_access(struct torture_context *torture,
 
 	/* execute permission on src */
 	ok = test_setup_copy_chunk(torture, tree, tmp_ctx, 1, /* 1 chunk */
-				   &src_h, 4096, /* fill 4096 byte src file */
+				   FNAME, &src_h, 4096, /* fill 4096 byte src file */
 				   SEC_FILE_EXECUTE | SEC_FILE_READ_ATTRIBUTE,
-				   &dest_h, 0, /* 0 byte dest file */
+				   FNAME2, &dest_h, 0, /* 0 byte dest file */
 				   SEC_RIGHTS_FILE_ALL, &cc_copy, &ioctl);
 	if (!ok) {
 		torture_fail(torture, "setup copy chunk error");
@@ -1311,8 +1335,8 @@ static bool test_ioctl_copy_chunk_bad_access(struct torture_context *torture,
 
 	/* neither read nor execute permission on src */
 	ok = test_setup_copy_chunk(torture, tree, tmp_ctx, 1, /* 1 chunk */
-				   &src_h, 4096, /* fill 4096 byte src file */
-				   SEC_FILE_READ_ATTRIBUTE, &dest_h,
+				   FNAME, &src_h, 4096, /* fill 4096 byte src file */
+				   SEC_FILE_READ_ATTRIBUTE, FNAME2, &dest_h,
 				   0, /* 0 byte dest file */
 				   SEC_RIGHTS_FILE_ALL, &cc_copy, &ioctl);
 	if (!ok) {
@@ -1340,8 +1364,8 @@ static bool test_ioctl_copy_chunk_bad_access(struct torture_context *torture,
 	/* no write permission on dest */
 	ok = test_setup_copy_chunk(
 	    torture, tree, tmp_ctx, 1, /* 1 chunk */
-	    &src_h, 4096,	      /* fill 4096 byte src file */
-	    SEC_FILE_READ_DATA | SEC_FILE_READ_ATTRIBUTE, &dest_h,
+	    FNAME, &src_h, 4096, /* fill 4096 byte src file */
+	    SEC_FILE_READ_DATA | SEC_FILE_READ_ATTRIBUTE, FNAME2, &dest_h,
 	    0, /* 0 byte dest file */
 	    (SEC_RIGHTS_FILE_ALL &
 	     ~(SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA)),
@@ -1370,9 +1394,9 @@ static bool test_ioctl_copy_chunk_bad_access(struct torture_context *torture,
 
 	/* no read permission on dest */
 	ok = test_setup_copy_chunk(torture, tree, tmp_ctx, 1, /* 1 chunk */
-				   &src_h, 4096, /* fill 4096 byte src file */
+				   FNAME, &src_h, 4096, /* fill 4096 byte src file */
 				   SEC_FILE_READ_DATA | SEC_FILE_READ_ATTRIBUTE,
-				   &dest_h, 0, /* 0 byte dest file */
+				   FNAME2, &dest_h, 0, /* 0 byte dest file */
 				   (SEC_RIGHTS_FILE_ALL & ~SEC_FILE_READ_DATA),
 				   &cc_copy, &ioctl);
 	if (!ok) {
@@ -1420,8 +1444,10 @@ static bool test_ioctl_copy_chunk_write_access(struct torture_context *torture,
 	/* no read permission on dest with FSCTL_SRV_COPYCHUNK_WRITE */
 	ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
 				   1, /* 1 chunk */
+				   FNAME,
 				   &src_h, 4096, /* fill 4096 byte src file */
 				   SEC_RIGHTS_FILE_ALL,
+				   FNAME2,
 				   &dest_h, 0,	/* 0 byte dest file */
 				   (SEC_RIGHTS_FILE_WRITE
 				    | SEC_RIGHTS_FILE_EXECUTE),
@@ -1468,8 +1494,10 @@ static bool test_ioctl_copy_chunk_src_exceed(struct torture_context *torture,
 
 	ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
 				   1, /* 1 chunk */
+				   FNAME,
 				   &src_h, 4096, /* fill 4096 byte src file */
 				   SEC_RIGHTS_FILE_ALL,
+				   FNAME2,
 				   &dest_h, 0,	/* 0 byte dest file */
 				   SEC_RIGHTS_FILE_ALL,
 				   &cc_copy,
@@ -1550,8 +1578,10 @@ test_ioctl_copy_chunk_src_exceed_multi(struct torture_context *torture,
 
 	ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
 				   2, /* 2 chunks */
+				   FNAME,
 				   &src_h, 8192, /* fill 8192 byte src file */
 				   SEC_RIGHTS_FILE_ALL,
+				   FNAME2,
 				   &dest_h, 0,	/* 0 byte dest file */
 				   SEC_RIGHTS_FILE_ALL,
 				   &cc_copy,
@@ -1620,8 +1650,10 @@ static bool test_ioctl_copy_chunk_sparse_dest(struct torture_context *torture,
 
 	ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
 				   1, /* 1 chunk */
+				   FNAME,
 				   &src_h, 4096, /* fill 4096 byte src file */
 				   SEC_RIGHTS_FILE_ALL,
+				   FNAME2,
 				   &dest_h, 0,	/* 0 byte dest file */
 				   SEC_RIGHTS_FILE_ALL,
 				   &cc_copy,
@@ -1703,8 +1735,10 @@ static bool test_ioctl_copy_chunk_max_output_sz(struct torture_context *torture,
 
 	ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
 				   1, /* 1 chunk */
+				   FNAME,
 				   &src_h, 4096, /* fill 4096 byte src file */
 				   SEC_RIGHTS_FILE_ALL,
+				   FNAME2,
 				   &dest_h, 0,	/* 0 byte dest file */
 				   SEC_RIGHTS_FILE_ALL,
 				   &cc_copy,
@@ -1752,8 +1786,10 @@ static bool test_ioctl_copy_chunk_zero_length(struct torture_context *torture,
 
 	ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
 				   1, /* 1 chunk */
+				   FNAME,
 				   &src_h, 4096, /* fill 4096 byte src file */
 				   SEC_RIGHTS_FILE_ALL,
+				   FNAME2,
 				   &dest_h, 0,	/* 0 byte dest file */
 				   SEC_RIGHTS_FILE_ALL,
 				   &cc_copy,
@@ -3885,8 +3921,10 @@ static bool test_ioctl_sparse_copy_chunk(struct torture_context *torture,
 
 	ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
 				   1, /* chunks */
+				   FNAME,
 				   &src_h, 0, /* src file */
 				   SEC_RIGHTS_FILE_ALL,
+				   FNAME2,
 				   &dest_h, 0,	/* dest file */
 				   SEC_RIGHTS_FILE_ALL,
 				   &cc_copy,
-- 
2.9.3


From 2371d487d2abd54c9a1f1139737fce425a7aaa44 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Tue, 16 May 2017 13:14:16 +0200
Subject: [PATCH 5/7] s4/torture: smb2.ioctl: add copy-chunk test with stream
 to smb2.ioctl

Bug: https://bugzilla.samba.org/show_bug.cgi?id=12787

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 selftest/knownfail           |   1 +
 source4/torture/smb2/ioctl.c | 150 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 151 insertions(+)

diff --git a/selftest/knownfail b/selftest/knownfail
index 2cc9c70..b16ff52 100644
--- a/selftest/knownfail
+++ b/selftest/knownfail
@@ -157,6 +157,7 @@
 ^samba3.smb2.durable-v2-open.reopen1a-lease\(ad_dc\)$
 ^samba4.smb2.ioctl.req_resume_key\(ad_dc_ntvfs\) # not supported by s4 ntvfs server
 ^samba4.smb2.ioctl.copy_chunk_\w*\(ad_dc_ntvfs\)	# not supported by s4 ntvfs server
+^samba4.smb2.ioctl.copy-chunk streams\(ad_dc_ntvfs\) # not supported by s4 ntvfs server
 ^samba3.smb2.dir.one
 ^samba3.smb2.dir.modify
 ^samba3.smb2.oplock.batch20
diff --git a/source4/torture/smb2/ioctl.c b/source4/torture/smb2/ioctl.c
index c9fc121..f81c027 100644
--- a/source4/torture/smb2/ioctl.c
+++ b/source4/torture/smb2/ioctl.c
@@ -1834,6 +1834,154 @@ static bool test_ioctl_copy_chunk_zero_length(struct torture_context *torture,
 	return true;
 }
 
+static bool copy_one_stream(struct torture_context *torture,
+			    struct smb2_tree *tree,
+			    TALLOC_CTX *tmp_ctx,
+			    const char *src_sname,
+			    const char *dst_sname)
+{
+	struct smb2_handle src_h = {{0}};
+	struct smb2_handle dest_h = {{0}};
+	NTSTATUS status;
+	union smb_ioctl io;
+	struct srv_copychunk_copy cc_copy;
+	struct srv_copychunk_rsp cc_rsp;
+	enum ndr_err_code ndr_ret;
+	bool ok = false;
+
+	ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
+				   1, /* 1 chunk */
+				   src_sname,
+				   &src_h, 256, /* fill 256 byte src file */
+				   SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA,
+				   dst_sname,
+				   &dest_h, 0,	/* 0 byte dest file */
+				   SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA,
+				   &cc_copy,
+				   &io);
+	torture_assert_goto(torture, ok == true, ok, done,
+			    "setup copy chunk error\n");
+
+	/* copy all src file data (via a single chunk desc) */
+	cc_copy.chunks[0].source_off = 0;
+	cc_copy.chunks[0].target_off = 0;
+	cc_copy.chunks[0].length = 256;
+
+	ndr_ret = ndr_push_struct_blob(
+		&io.smb2.in.out, tmp_ctx, &cc_copy,
+		(ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
+
+	torture_assert_ndr_success_goto(torture, ndr_ret, ok, done,
+				   "ndr_push_srv_copychunk_copy\n");
+
+	status = smb2_ioctl(tree, tmp_ctx, &io.smb2);
+	torture_assert_ntstatus_ok_goto(torture, status, ok, done,
+					"FSCTL_SRV_COPYCHUNK\n");
+
+	ndr_ret = ndr_pull_struct_blob(
+		&io.smb2.out.out, tmp_ctx, &cc_rsp,
+		(ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
+
+	torture_assert_ndr_success_goto(torture, ndr_ret, ok, done,
+				   "ndr_pull_srv_copychunk_rsp\n");
+
+	ok = check_copy_chunk_rsp(torture, &cc_rsp,
+				  1,	/* chunks written */
+				  0,	/* chunk bytes unsuccessfully written */
+				  256); /* total bytes written */
+	torture_assert_goto(torture, ok == true, ok, done,
+			    "bad copy chunk response data\n");
+
+	ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 256, 0);
+	if (!ok) {
+		torture_fail(torture, "inconsistent file data\n");
+	}
+
+done:
+	if (!smb2_util_handle_empty(src_h)) {
+		smb2_util_close(tree, src_h);
+	}
+	if (!smb2_util_handle_empty(dest_h)) {
+		smb2_util_close(tree, dest_h);
+	}
+
+	return ok;
+}
+
+/**
+ * Create a file
+ **/
+static bool torture_setup_file(TALLOC_CTX *mem_ctx,
+			       struct smb2_tree *tree,
+			       const char *name)
+{
+	struct smb2_create io;
+	NTSTATUS status;
+
+	smb2_util_unlink(tree, name);
+	ZERO_STRUCT(io);
+	io.in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED;
+	io.in.file_attributes   = FILE_ATTRIBUTE_NORMAL;
+	io.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF;
+	io.in.share_access =
+		NTCREATEX_SHARE_ACCESS_DELETE|
+		NTCREATEX_SHARE_ACCESS_READ|
+		NTCREATEX_SHARE_ACCESS_WRITE;
+	io.in.create_options = 0;
+	io.in.fname = name;
+
+	status = smb2_create(tree, mem_ctx, &io);
+	if (!NT_STATUS_IS_OK(status)) {
+		return false;
+	}
+
+	status = smb2_util_close(tree, io.out.file.handle);
+	if (!NT_STATUS_IS_OK(status)) {
+		return false;
+	}
+
+	return true;
+}
+
+static bool test_copy_chunk_streams(struct torture_context *torture,
+				    struct smb2_tree *tree)
+{
+	const char *src_name = "src";
+	const char *dst_name = "dst";
+	struct names {
+		const char *src_sname;
+		const char *dst_sname;
+	} names[] = {
+		{ "src:foo", "dst:foo" }
+	};
+	int i;
+	TALLOC_CTX *tmp_ctx = NULL;
+	bool ok = false;
+
+	tmp_ctx = talloc_new(tree);
+	torture_assert_not_null_goto(torture, tmp_ctx, ok, done,
+				     "torture_setup_file\n");
+
+	ok = torture_setup_file(torture, tree, src_name);
+	torture_assert_goto(torture, ok == true, ok, done, "torture_setup_file\n");
+	ok = torture_setup_file(torture, tree, dst_name);
+	torture_assert_goto(torture, ok == true, ok, done, "torture_setup_file\n");
+
+	for (i = 0; i < ARRAY_SIZE(names); i++) {
+		ok = copy_one_stream(torture, tree, tmp_ctx,
+				     names[i].src_sname,
+				     names[i].dst_sname);
+		torture_assert_goto(torture, ok == true, ok, done,
+				    "copy_one_stream failed\n");
+	}
+
+done:
+	smb2_util_unlink(tree, src_name);
+	smb2_util_unlink(tree, dst_name);
+	talloc_free(tmp_ctx);
+	return ok;
+}
+
 static NTSTATUS test_ioctl_compress_fs_supported(struct torture_context *torture,
 						 struct smb2_tree *tree,
 						 TALLOC_CTX *mem_ctx,
@@ -6270,6 +6418,8 @@ struct torture_suite *torture_smb2_ioctl_init(TALLOC_CTX *ctx)
 				     test_ioctl_copy_chunk_max_output_sz);
 	torture_suite_add_1smb2_test(suite, "copy_chunk_zero_length",
 				     test_ioctl_copy_chunk_zero_length);
+	torture_suite_add_1smb2_test(suite, "copy-chunk streams",
+				     test_copy_chunk_streams);
 	torture_suite_add_1smb2_test(suite, "compress_file_flag",
 				     test_ioctl_compress_file_flag);
 	torture_suite_add_1smb2_test(suite, "compress_dir_inherit",
-- 
2.9.3


From 765c4ffe688425b14e87a2e52d57dc3799ddea10 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Fri, 12 May 2017 14:56:53 +0200
Subject: [PATCH 6/7] s4/torture: vfs_fruit: add src and dst path args to
 test_setup_copy_chunk

Just let the caller pass in the paths, no change in behaviour. A new
test in a subsequent commit will use it to pass paths to streams.

Bug: https://bugzilla.samba.org/show_bug.cgi?id=12787

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 source4/torture/vfs/fruit.c | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/source4/torture/vfs/fruit.c b/source4/torture/vfs/fruit.c
index 96edec2..7d59fee 100644
--- a/source4/torture/vfs/fruit.c
+++ b/source4/torture/vfs/fruit.c
@@ -2353,9 +2353,11 @@ static bool test_setup_create_fill(struct torture_context *torture,
 static bool test_setup_copy_chunk(struct torture_context *torture,
 				  struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
 				  uint32_t nchunks,
+				  const char *src_name,
 				  struct smb2_handle *src_h,
 				  uint64_t src_size,
 				  uint32_t src_desired_access,
+				  const char *dst_name,
 				  struct smb2_handle *dest_h,
 				  uint64_t dest_size,
 				  uint32_t dest_desired_access,
@@ -2367,12 +2369,12 @@ static bool test_setup_copy_chunk(struct torture_context *torture,
 	NTSTATUS status;
 	enum ndr_err_code ndr_ret;
 
-	ok = test_setup_create_fill(torture, tree, mem_ctx, FNAME_CC_SRC,
+	ok = test_setup_create_fill(torture, tree, mem_ctx, src_name,
 				    src_h, src_size, src_desired_access,
 				    FILE_ATTRIBUTE_NORMAL);
 	torture_assert(torture, ok, "src file create fill");
 
-	ok = test_setup_create_fill(torture, tree, mem_ctx, FNAME_CC_DST,
+	ok = test_setup_create_fill(torture, tree, mem_ctx, dst_name,
 				    dest_h, dest_size, dest_desired_access,
 				    FILE_ATTRIBUTE_NORMAL);
 	torture_assert(torture, ok, "dest file create fill");
@@ -2533,8 +2535,10 @@ static bool test_copyfile(struct torture_context *torture,
 
 	ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
 				   0, /* 0 chunks, copyfile semantics */
+				   FNAME_CC_SRC,
 				   &src_h, 4096, /* fill 4096 byte src file */
 				   SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA,
+				   FNAME_CC_DST,
 				   &dest_h, 0,	/* 0 byte dest file */
 				   SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA,
 				   &cc_copy,
@@ -2600,8 +2604,10 @@ static bool test_copyfile(struct torture_context *torture,
 
 	ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
 				   0, /* 0 chunks, copyfile semantics */
+				   FNAME_CC_SRC,
 				   &src_h, 4096, /* fill 4096 byte src file */
 				   SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA,
+				   FNAME_CC_DST,
 				   &dest_h, 0,	/* 0 byte dest file */
 				   SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA,
 				   &cc_copy,
-- 
2.9.3


From 98cc3445816c39f592e3c679aa253c38fff197e3 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Fri, 12 May 2017 17:10:07 +0200
Subject: [PATCH 7/7] s4/torture: vfs_fruit: test copy-chunk on streams

Bug: https://bugzilla.samba.org/show_bug.cgi?id=12787

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 source4/torture/vfs/fruit.c | 225 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 225 insertions(+)

diff --git a/source4/torture/vfs/fruit.c b/source4/torture/vfs/fruit.c
index 7d59fee..2ab153a 100644
--- a/source4/torture/vfs/fruit.c
+++ b/source4/torture/vfs/fruit.c
@@ -4048,6 +4048,230 @@ done:
 	return ret;
 }
 
+static bool copy_one_stream(struct torture_context *torture,
+			    struct smb2_tree *tree,
+			    TALLOC_CTX *tmp_ctx,
+			    const char *src_sname,
+			    const char *dst_sname)
+{
+	struct smb2_handle src_h = {{0}};
+	struct smb2_handle dest_h = {{0}};
+	NTSTATUS status;
+	union smb_ioctl io;
+	struct srv_copychunk_copy cc_copy;
+	struct srv_copychunk_rsp cc_rsp;
+	enum ndr_err_code ndr_ret;
+	bool ok = false;
+
+	ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
+				   1, /* 1 chunk */
+				   src_sname,
+				   &src_h, 256, /* fill 256 byte src file */
+				   SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA,
+				   dst_sname,
+				   &dest_h, 0,	/* 0 byte dest file */
+				   SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA,
+				   &cc_copy,
+				   &io);
+	torture_assert_goto(torture, ok == true, ok, done,
+			    "setup copy chunk error\n");
+
+	/* copy all src file data (via a single chunk desc) */
+	cc_copy.chunks[0].source_off = 0;
+	cc_copy.chunks[0].target_off = 0;
+	cc_copy.chunks[0].length = 256;
+
+	ndr_ret = ndr_push_struct_blob(
+		&io.smb2.in.out, tmp_ctx, &cc_copy,
+		(ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
+
+	torture_assert_ndr_success_goto(torture, ndr_ret, ok, done,
+				   "ndr_push_srv_copychunk_copy\n");
+
+	status = smb2_ioctl(tree, tmp_ctx, &io.smb2);
+	torture_assert_ntstatus_ok_goto(torture, status, ok, done,
+					"FSCTL_SRV_COPYCHUNK\n");
+
+	ndr_ret = ndr_pull_struct_blob(
+		&io.smb2.out.out, tmp_ctx, &cc_rsp,
+		(ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
+
+	torture_assert_ndr_success_goto(torture, ndr_ret, ok, done,
+				   "ndr_pull_srv_copychunk_rsp\n");
+
+	ok = check_copy_chunk_rsp(torture, &cc_rsp,
+				  1,	/* chunks written */
+				  0,	/* chunk bytes unsuccessfully written */
+				  256); /* total bytes written */
+	torture_assert_goto(torture, ok == true, ok, done,
+			    "bad copy chunk response data\n");
+
+	ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 256, 0);
+	if (!ok) {
+		torture_fail(torture, "inconsistent file data\n");
+	}
+
+done:
+	if (!smb2_util_handle_empty(src_h)) {
+		smb2_util_close(tree, src_h);
+	}
+	if (!smb2_util_handle_empty(dest_h)) {
+		smb2_util_close(tree, dest_h);
+	}
+
+	return ok;
+}
+
+static bool copy_finderinfo_stream(struct torture_context *torture,
+				   struct smb2_tree *tree,
+				   TALLOC_CTX *tmp_ctx,
+				   const char *src_name,
+				   const char *dst_name)
+{
+	struct smb2_handle src_h = {{0}};
+	struct smb2_handle dest_h = {{0}};
+	NTSTATUS status;
+	union smb_ioctl io;
+	struct srv_copychunk_copy cc_copy;
+	struct srv_copychunk_rsp cc_rsp;
+	enum ndr_err_code ndr_ret;
+	const char *type_creator = "SMB,OLE!";
+	AfpInfo *info = NULL;
+	const char *src_name_afpinfo = NULL;
+	const char *dst_name_afpinfo = NULL;
+	bool ok = false;
+
+	src_name_afpinfo = talloc_asprintf(tmp_ctx, "%s%s", src_name,
+					   AFPINFO_STREAM);
+	torture_assert_not_null_goto(torture, src_name_afpinfo, ok, done,
+				     "talloc_asprintf failed\n");
+
+	dst_name_afpinfo = talloc_asprintf(tmp_ctx, "%s%s", dst_name,
+					   AFPINFO_STREAM);
+	torture_assert_not_null_goto(torture, dst_name_afpinfo, ok, done,
+				     "talloc_asprintf failed\n");
+
+	info = torture_afpinfo_new(tmp_ctx);
+	torture_assert_not_null_goto(torture, info, ok, done,
+				     "torture_afpinfo_new failed\n");
+
+	memcpy(info->afpi_FinderInfo, type_creator, 8);
+	ok = torture_write_afpinfo(tree, torture, tmp_ctx, src_name, info);
+	torture_assert_goto(torture, ok == true, ok, done,
+			    "torture_write_afpinfo failed\n");
+
+	ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
+				   1, /* 1 chunk */
+				   src_name_afpinfo,
+				   &src_h, 0,
+				   SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA,
+				   dst_name_afpinfo,
+				   &dest_h, 0,
+				   SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA,
+				   &cc_copy,
+				   &io);
+	torture_assert_goto(torture, ok == true, ok, done,
+			    "setup copy chunk error\n");
+
+	/* copy all src file data (via a single chunk desc) */
+	cc_copy.chunks[0].source_off = 0;
+	cc_copy.chunks[0].target_off = 0;
+	cc_copy.chunks[0].length = 60;
+
+	ndr_ret = ndr_push_struct_blob(
+		&io.smb2.in.out, tmp_ctx, &cc_copy,
+		(ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
+
+	torture_assert_ndr_success_goto(torture, ndr_ret, ok, done,
+				   "ndr_push_srv_copychunk_copy\n");
+
+	status = smb2_ioctl(tree, tmp_ctx, &io.smb2);
+	torture_assert_ntstatus_ok_goto(torture, status, ok, done,
+					"FSCTL_SRV_COPYCHUNK\n");
+
+	ndr_ret = ndr_pull_struct_blob(
+		&io.smb2.out.out, tmp_ctx, &cc_rsp,
+		(ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
+
+	torture_assert_ndr_success_goto(torture, ndr_ret, ok, done,
+				   "ndr_pull_srv_copychunk_rsp\n");
+
+	smb2_util_close(tree, src_h);
+	ZERO_STRUCT(src_h);
+	smb2_util_close(tree, dest_h);
+	ZERO_STRUCT(dest_h);
+
+	ok = check_copy_chunk_rsp(torture, &cc_rsp,
+				  1,	/* chunks written */
+				  0,	/* chunk bytes unsuccessfully written */
+				  60); /* total bytes written */
+	torture_assert_goto(torture, ok == true, ok, done,
+			    "bad copy chunk response data\n");
+
+	ok = check_stream(tree, __location__, torture, tmp_ctx,
+			  dst_name, AFPINFO_STREAM,
+			  0, 60, 16, 8, type_creator);
+	torture_assert_goto(torture, ok == true, ok, done, "check_stream failed\n");
+
+done:
+	if (!smb2_util_handle_empty(src_h)) {
+		smb2_util_close(tree, src_h);
+	}
+	if (!smb2_util_handle_empty(dest_h)) {
+		smb2_util_close(tree, dest_h);
+	}
+
+	return ok;
+}
+
+static bool test_copy_chunk_streams(struct torture_context *torture,
+				    struct smb2_tree *tree)
+{
+	const char *src_name = "src";
+	const char *dst_name = "dst";
+	struct names {
+		const char *src_sname;
+		const char *dst_sname;
+	} names[] = {
+		{ "src:foo", "dst:foo" },
+		{ "src" AFPRESOURCE_STREAM, "dst" AFPRESOURCE_STREAM }
+	};
+	int i;
+	TALLOC_CTX *tmp_ctx = NULL;
+	bool ok = false;
+
+	tmp_ctx = talloc_new(tree);
+	torture_assert_not_null_goto(torture, tmp_ctx, ok, done,
+				     "torture_setup_file\n");
+
+	smb2_util_unlink(tree, src_name);
+	smb2_util_unlink(tree, dst_name);
+
+	ok = torture_setup_file(torture, tree, src_name, false);
+	torture_assert_goto(torture, ok == true, ok, done, "torture_setup_file\n");
+	ok = torture_setup_file(torture, tree, dst_name, false);
+	torture_assert_goto(torture, ok == true, ok, done, "torture_setup_file\n");
+
+	for (i = 0; i < ARRAY_SIZE(names); i++) {
+		ok = copy_one_stream(torture, tree, tmp_ctx,
+				     names[i].src_sname,
+				     names[i].dst_sname);
+		torture_assert_goto(torture, ok == true, ok, done,
+				    "copy_one_stream failed\n");
+	}
+
+	ok = copy_finderinfo_stream(torture, tree, tmp_ctx,
+				    src_name, dst_name);
+	torture_assert_goto(torture, ok == true, ok, done,
+			    "copy_finderinfo_stream failed\n");
+
+done:
+	smb2_util_unlink(tree, src_name);
+	smb2_util_unlink(tree, dst_name);
+	talloc_free(tmp_ctx);
+	return ok;
+}
+
 /*
  * Note: This test depends on "vfs objects = catia fruit streams_xattr".  For
  * some tests torture must be run on the host it tests and takes an additional
@@ -4086,6 +4310,7 @@ struct torture_suite *torture_vfs_fruit(TALLOC_CTX *ctx)
 	torture_suite_add_1smb2_test(suite, "readdir_attr with names with illegal ntfs characters", test_readdir_attr_illegal_ntfs);
 	torture_suite_add_2ns_smb2_test(suite, "invalid AFP_AfpInfo", test_invalid_afpinfo);
 	torture_suite_add_1smb2_test(suite, "creating rsrc with read-only access", test_rfork_create_ro);
+	torture_suite_add_1smb2_test(suite, "copy-chunk streams", test_copy_chunk_streams);
 
 	return suite;
 }
-- 
2.9.3



More information about the samba-technical mailing list