[PATCH] copy-chunk fails if src and dst file are on different tcons

Ralph Böhme slow at samba.org
Fri Jun 30 18:07:56 UTC 2017


On Sat, Apr 22, 2017 at 02:36:35PM +0200, Ralph Böhme wrote:
> On Fri, Apr 21, 2017 at 02:21:41PM -0700, Jeremy Allison wrote:
> > On Fri, Apr 21, 2017 at 06:39:38PM +0200, Ralph Böhme via samba-technical wrote:
> > > On Fri, Apr 21, 2017 at 04:52:10PM +0200, Ralph Böhme wrote:
> > > > On Fri, Apr 21, 2017 at 12:55:55PM +0200, Ralph Böhme via samba-technical wrote:
> > > > > Hi!
> > > > > 
> > > > > As the subject says, this fails against Samba but works against
> > > > > Windows. Verified against Windows 2016.
> > > > > 
> > > > > Attached is a possible fix plus test.
> > > > > 
> > > > > Please review & push if happy. Thanks!
> > > > 
> > > > please ignore for now. metze raised an issue over private RPC dealing with the
> > > > VFS handles that we end up using in the send functions.
> > > 
> > > metze, maybe we can just do the check at the vfs layer. See attached patch for
> > > the basic idea. Should be expanded to cover all vfs functions that take a fsp
> > > and a vfs handle.
> > > 
> > > Just fetching and "looking" at an fsp at the smb layer should be possible, it's
> > > where we start using it where we should check. Or am I still missing something?
> > 
> > Just my 2cents. This looks like the right approach but does mean
> > changing all of the fsp-based VFS calls.
> 
> d'oh! Most VFS macros that take an fsp don't take a conn directly anyway, instead
> the macro pulls the vfs handle out of the fsp (fsp->conn->vfs_handles). Which is
> exactly what we want.
> 
> There are a few VFS macros that don't adhere to this scheme (eg
> SMB_VFS_GET_ALLOC_SIZE, SMB_VFS_STREAMINFO, SMB_VFS_STRICT_LOCK,
> SMB_VFS_COPY_CHUNK_SEND), but I guess that's not an purpose and could be fixed.
> 
> Thinking more about it, my change would result in VFS calls running with their
> curdir set to a different tcon then their fsp. We currently rely on doing a
> chdir to the share root when processing an smb req for a tcon, don't we? This
> might actually sink my ship. I imagine shadow_copy2 might not be happy when
> processing a request to read from an fsp that is assodicated with tcon 1 while
> the curdir is the share root of tcon 2. Hm.

so attached is a patchset that adds some magic to the relevant VFS functions
(copy-chunk) that allows them to work across shares -- something that Windows
supports.

The basic idea is to associate a token/cookie with an fsp in the VFS module
function that sees the source fsp (so the resume-key ioctl in case of
copy-chunk) and then fetch the source fsp from the cookie again later.

To generalize on this mechanism and to prepare for future support of ODX
(Windows Offloaded Data Transfer) I'm consolidating the VFS interface on two
token based VFS function: OFFLOAD_READ and OFFLOAD_WRITE.

For now I'm essentially renaming COPY_CHUNK adding the additional fsp/cookie
magic to allow for copied across shares. SMB layer support for ODX is not yet
implemented. After some research it seems there is some support in the Linux
kernel already, exposed via ioctl()s, but we'd need some real ODX capable
hardware to implement this in Samba.

Additional reviewers *highly* welcome!

Cheerio!
-slow
-------------- next part --------------
From d676b36ca1d332f863f5f7e7d702672bfabde974 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Fri, 2 Jun 2017 13:05:22 +0200
Subject: [PATCH 01/17] librpc/idl: convert offload flags to a bitmap

Signed-off-by: Ralph Boehme <slow at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 librpc/idl/ioctl.idl | 13 ++++++++-----
 1 file changed, 8 insertions(+), 5 deletions(-)

diff --git a/librpc/idl/ioctl.idl b/librpc/idl/ioctl.idl
index f72f9d2..17dfdf8 100644
--- a/librpc/idl/ioctl.idl
+++ b/librpc/idl/ioctl.idl
@@ -67,19 +67,22 @@ interface copychunk
 		hyper length;
 	} fsctl_offload_read_input;
 
-	const uint32 OFFLOAD_READ_FLAG_FILE_TOO_SMALL = 0x00000001;
-	const uint32 OFFLOAD_READ_FLAG_ALL_ZERO_BEYOND_RANGE = 0x00000002;
-	const uint32 OFFLOAD_READ_FLAG_CANNOT_OFFLOAD_BEYOND_RANGE = 0x00000004;
+	typedef [public,bitmap32bit] bitmap {
+		OFFLOAD_READ_FLAG_FILE_TOO_SMALL = 0x01,
+		OFFLOAD_READ_FLAG_ALL_ZERO_BEYOND_RANGE = 0x02,
+		OFFLOAD_READ_FLAG_CANNOT_OFFLOAD_BEYOND_RANGE = 0x04
+	} offload_flags;
+
 	typedef [public] struct {
 		uint32 size;
-		uint32 flags;
+		offload_flags flags;
 		hyper xfer_length;
 		uint8 token[512];
 	} fsctl_offload_read_output;
 
 	typedef [public] struct {
 		uint32 size;
-		uint32 flags;
+		offload_flags flags;
 		hyper file_offset;
 		hyper copy_length;
 		hyper xfer_offset;
-- 
2.9.4


From 1b3404241bfed80295804537c95eae9d1bd5bd8b Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Fri, 2 Jun 2017 13:06:31 +0200
Subject: [PATCH 02/17] librpc/idl: fix STORAGE_OFFLOAD_TOKEN_TYPE_ZERO_DATA
 definition

STORAGE_OFFLOAD_TOKEN_TYPE_ZERO_DATA is defined as 0xffff0001 in MS-FSCC
2.3.79.

Signed-off-by: Ralph Boehme <slow at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 librpc/idl/ioctl.idl | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/librpc/idl/ioctl.idl b/librpc/idl/ioctl.idl
index 17dfdf8..e891834 100644
--- a/librpc/idl/ioctl.idl
+++ b/librpc/idl/ioctl.idl
@@ -49,8 +49,8 @@ interface copychunk
 		uint8 reserved[2];
 	} device_copy_offload_descriptor;
 
-	/* XXX: 0x00000001 is unconfirmed */
-	const uint32 STORAGE_OFFLOAD_TOKEN_TYPE_ZERO_DATA = 0x00000001;
+	const uint32 STORAGE_OFFLOAD_TOKEN_TYPE_ZERO_DATA = 0xffff0001;
+
 	typedef [public] struct {
 		uint32 token_type;
 		uint8 reserved[2];
-- 
2.9.4


From 5e40b5d5842d2d581f27ab9973f28c9cc60aa4fb Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Fri, 2 Jun 2017 13:09:41 +0200
Subject: [PATCH 03/17] librpc/idl: make use storage_offload_token

Signed-off-by: Ralph Boehme <slow at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 librpc/idl/ioctl.idl | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/librpc/idl/ioctl.idl b/librpc/idl/ioctl.idl
index e891834..ba68fbc 100644
--- a/librpc/idl/ioctl.idl
+++ b/librpc/idl/ioctl.idl
@@ -77,7 +77,7 @@ interface copychunk
 		uint32 size;
 		offload_flags flags;
 		hyper xfer_length;
-		uint8 token[512];
+		storage_offload_token token;
 	} fsctl_offload_read_output;
 
 	typedef [public] struct {
@@ -86,7 +86,7 @@ interface copychunk
 		hyper file_offset;
 		hyper copy_length;
 		hyper xfer_offset;
-		uint8 token[512];
+		storage_offload_token token;
 	} fsctl_offload_write_input;
 
 	typedef [public] struct {
-- 
2.9.4


From 339965ca843b97aef64d84c556a8eb4d567df6d8 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Tue, 6 Jun 2017 14:50:15 +0200
Subject: [PATCH 04/17] s4/torture: pass destination tree to
 test_setup_copy_chunk

No change in behaviour, will be used in subsequent commits.

Signed-off-by: Ralph Boehme <slow at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 source4/torture/smb2/ioctl.c | 58 +++++++++++++++++++++++---------------------
 1 file changed, 30 insertions(+), 28 deletions(-)

diff --git a/source4/torture/smb2/ioctl.c b/source4/torture/smb2/ioctl.c
index e7a289b..d9b7f4a 100644
--- a/source4/torture/smb2/ioctl.c
+++ b/source4/torture/smb2/ioctl.c
@@ -307,7 +307,9 @@ 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,
+				  struct smb2_tree *src_tree,
+				  struct smb2_tree *dst_tree,
+				  TALLOC_CTX *mem_ctx,
 				  uint32_t nchunks,
 				  const char *src_name,
 				  struct smb2_handle *src_h,
@@ -325,12 +327,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, src_name,
+	ok = test_setup_create_fill(torture, src_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, dst_name,
+	ok = test_setup_create_fill(torture, dst_tree, mem_ctx, dst_name,
 				    dest_h, dest_size, dest_desired_access,
 				    FILE_ATTRIBUTE_NORMAL);
 	torture_assert(torture, ok, "dest file create fill");
@@ -343,7 +345,7 @@ static bool test_setup_copy_chunk(struct torture_context *torture,
 	ioctl->smb2.in.max_response_size = 32;
 	ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
 
-	status = smb2_ioctl(tree, mem_ctx, &ioctl->smb2);
+	status = smb2_ioctl(src_tree, mem_ctx, &ioctl->smb2);
 	torture_assert_ntstatus_ok(torture, status,
 				   "FSCTL_SRV_REQUEST_RESUME_KEY");
 
@@ -398,7 +400,7 @@ static bool test_ioctl_copy_chunk_simple(struct torture_context *torture,
 	enum ndr_err_code ndr_ret;
 	bool ok;
 
-	ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
+	ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
 				   1, /* 1 chunk */
 				   FNAME,
 				   &src_h, 4096, /* fill 4096 byte src file */
@@ -464,7 +466,7 @@ static bool test_ioctl_copy_chunk_multi(struct torture_context *torture,
 	enum ndr_err_code ndr_ret;
 	bool ok;
 
-	ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
+	ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
 				   2, /* chunks */
 				   FNAME,
 				   &src_h, 8192, /* src file */
@@ -529,7 +531,7 @@ static bool test_ioctl_copy_chunk_tiny(struct torture_context *torture,
 	enum ndr_err_code ndr_ret;
 	bool ok;
 
-	ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
+	ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
 				   2, /* chunks */
 				   FNAME,
 				   &src_h, 96, /* src file */
@@ -599,7 +601,7 @@ static bool test_ioctl_copy_chunk_over(struct torture_context *torture,
 	enum ndr_err_code ndr_ret;
 	bool ok;
 
-	ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
+	ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
 				   2, /* chunks */
 				   FNAME,
 				   &src_h, 8192, /* src file */
@@ -670,7 +672,7 @@ static bool test_ioctl_copy_chunk_append(struct torture_context *torture,
 	enum ndr_err_code ndr_ret;
 	bool ok;
 
-	ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
+	ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
 				   2, /* chunks */
 				   FNAME,
 				   &src_h, 4096, /* src file */
@@ -745,7 +747,7 @@ static bool test_ioctl_copy_chunk_limits(struct torture_context *torture,
 	enum ndr_err_code ndr_ret;
 	bool ok;
 
-	ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
+	ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
 				   1, /* chunks */
 				   FNAME,
 				   &src_h, 4096, /* src file */
@@ -808,7 +810,7 @@ static bool test_ioctl_copy_chunk_src_lck(struct torture_context *torture,
 	struct smb2_lock lck;
 	struct smb2_lock_element el[1];
 
-	ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
+	ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
 				   1, /* chunks */
 				   FNAME,
 				   &src_h, 4096, /* src file */
@@ -938,7 +940,7 @@ static bool test_ioctl_copy_chunk_dest_lck(struct torture_context *torture,
 	struct smb2_lock lck;
 	struct smb2_lock_element el[1];
 
-	ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
+	ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
 				   1, /* chunks */
 				   FNAME,
 				   &src_h, 4096, /* src file */
@@ -1036,7 +1038,7 @@ static bool test_ioctl_copy_chunk_bad_key(struct torture_context *torture,
 	enum ndr_err_code ndr_ret;
 	bool ok;
 
-	ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
+	ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
 				   1,
 				   FNAME,
 				   &src_h, 4096,
@@ -1088,7 +1090,7 @@ static bool test_ioctl_copy_chunk_src_is_dest(struct torture_context *torture,
 	enum ndr_err_code ndr_ret;
 	bool ok;
 
-	ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
+	ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
 				   1,
 				   FNAME,
 				   &src_h, 8192,
@@ -1207,7 +1209,7 @@ test_ioctl_copy_chunk_src_is_dest_overlap(struct torture_context *torture,
 	bool ok;
 
 	/* exceed the vfs_default copy buffer */
-	ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
+	ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
 				   1,
 				   FNAME,
 				   &src_h, 2048 * 2,
@@ -1280,7 +1282,7 @@ static bool test_ioctl_copy_chunk_bad_access(struct torture_context *torture,
 	enum ndr_err_code ndr_ret;
 	bool ok;
 	/* read permission on src */
-	ok = test_setup_copy_chunk(torture, tree, tmp_ctx, 1, /* 1 chunk */
+	ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx, 1, /* 1 chunk */
 				   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 */
@@ -1307,7 +1309,7 @@ static bool test_ioctl_copy_chunk_bad_access(struct torture_context *torture,
 	smb2_util_close(tree, dest_h);
 
 	/* execute permission on src */
-	ok = test_setup_copy_chunk(torture, tree, tmp_ctx, 1, /* 1 chunk */
+	ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx, 1, /* 1 chunk */
 				   FNAME, &src_h, 4096, /* fill 4096 byte src file */
 				   SEC_FILE_EXECUTE | SEC_FILE_READ_ATTRIBUTE,
 				   FNAME2, &dest_h, 0, /* 0 byte dest file */
@@ -1334,7 +1336,7 @@ static bool test_ioctl_copy_chunk_bad_access(struct torture_context *torture,
 	smb2_util_close(tree, dest_h);
 
 	/* neither read nor execute permission on src */
-	ok = test_setup_copy_chunk(torture, tree, tmp_ctx, 1, /* 1 chunk */
+	ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx, 1, /* 1 chunk */
 				   FNAME, &src_h, 4096, /* fill 4096 byte src file */
 				   SEC_FILE_READ_ATTRIBUTE, FNAME2, &dest_h,
 				   0, /* 0 byte dest file */
@@ -1363,7 +1365,7 @@ 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 */
+	    torture, tree, tree, tmp_ctx, 1, /* 1 chunk */
 	    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 */
@@ -1393,7 +1395,7 @@ static bool test_ioctl_copy_chunk_bad_access(struct torture_context *torture,
 	smb2_util_close(tree, dest_h);
 
 	/* no read permission on dest */
-	ok = test_setup_copy_chunk(torture, tree, tmp_ctx, 1, /* 1 chunk */
+	ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx, 1, /* 1 chunk */
 				   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 */
@@ -1442,7 +1444,7 @@ static bool test_ioctl_copy_chunk_write_access(struct torture_context *torture,
 	bool ok;
 
 	/* no read permission on dest with FSCTL_SRV_COPYCHUNK_WRITE */
-	ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
+	ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
 				   1, /* 1 chunk */
 				   FNAME,
 				   &src_h, 4096, /* fill 4096 byte src file */
@@ -1492,7 +1494,7 @@ static bool test_ioctl_copy_chunk_src_exceed(struct torture_context *torture,
 	enum ndr_err_code ndr_ret;
 	bool ok;
 
-	ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
+	ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
 				   1, /* 1 chunk */
 				   FNAME,
 				   &src_h, 4096, /* fill 4096 byte src file */
@@ -1576,7 +1578,7 @@ test_ioctl_copy_chunk_src_exceed_multi(struct torture_context *torture,
 	enum ndr_err_code ndr_ret;
 	bool ok;
 
-	ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
+	ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
 				   2, /* 2 chunks */
 				   FNAME,
 				   &src_h, 8192, /* fill 8192 byte src file */
@@ -1648,7 +1650,7 @@ static bool test_ioctl_copy_chunk_sparse_dest(struct torture_context *torture,
 	bool ok;
 	int i;
 
-	ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
+	ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
 				   1, /* 1 chunk */
 				   FNAME,
 				   &src_h, 4096, /* fill 4096 byte src file */
@@ -1733,7 +1735,7 @@ static bool test_ioctl_copy_chunk_max_output_sz(struct torture_context *torture,
 	enum ndr_err_code ndr_ret;
 	bool ok;
 
-	ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
+	ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
 				   1, /* 1 chunk */
 				   FNAME,
 				   &src_h, 4096, /* fill 4096 byte src file */
@@ -1784,7 +1786,7 @@ static bool test_ioctl_copy_chunk_zero_length(struct torture_context *torture,
 	enum ndr_err_code ndr_ret;
 	bool ok;
 
-	ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
+	ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
 				   1, /* 1 chunk */
 				   FNAME,
 				   &src_h, 4096, /* fill 4096 byte src file */
@@ -1849,7 +1851,7 @@ static bool copy_one_stream(struct torture_context *torture,
 	enum ndr_err_code ndr_ret;
 	bool ok = false;
 
-	ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
+	ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
 				   1, /* 1 chunk */
 				   src_sname,
 				   &src_h, 256, /* fill 256 byte src file */
@@ -4067,7 +4069,7 @@ static bool test_ioctl_sparse_copy_chunk(struct torture_context *torture,
 		torture_skip(torture, "Sparse files not supported\n");
 	}
 
-	ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
+	ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
 				   1, /* chunks */
 				   FNAME,
 				   &src_h, 0, /* src file */
-- 
2.9.4


From 328db6c23d9357deb4b3d53dcf50b8a8bbe7df2e Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Sat, 3 Jun 2017 12:57:59 +0200
Subject: [PATCH 05/17] s3/vfs: add SMB_VFS_OFFLOAD_READ_SEND/RECV

Add SMB_VFS_OFFLOAD_READ_SEND an SMB_VFS_OFFLOAD_READ_RECV.

This paves the way for supporting server-side copy-chunk with source and
destination file-handles on different shares. It can be used to
implement copy offload fsctl in the future, but for now this will be
used as a mere copy-chunk replacement.

SMB_VFS_OFFLOAD_READ generates a token that associates an fsp with the
token and stores the fsp in a in-memory db.

Initially only a copy-chunk resume key fsctl is supported. In the future
this can be enhanced to support real offload fsctl.

Signed-off-by: Ralph Boehme <slow at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 examples/VFS/skel_opaque.c       |  44 +++++++++
 examples/VFS/skel_transparent.c  |  90 +++++++++++++++++
 source3/include/vfs.h            |  26 +++++
 source3/include/vfs_macros.h     |  10 ++
 source3/modules/offload_token.c  | 207 +++++++++++++++++++++++++++++++++++++++
 source3/modules/offload_token.h  |  37 +++++++
 source3/modules/vfs_btrfs.c      | 119 ++++++++++++++++++++++
 source3/modules/vfs_default.c    |  77 +++++++++++++++
 source3/modules/vfs_fruit.c      | 110 +++++++++++++++++++++
 source3/modules/vfs_full_audit.c |  42 ++++++++
 source3/modules/vfs_time_audit.c |  99 +++++++++++++++++++
 source3/modules/wscript_build    |  10 +-
 source3/smbd/vfs.c               |  24 +++++
 13 files changed, 892 insertions(+), 3 deletions(-)
 create mode 100644 source3/modules/offload_token.c
 create mode 100644 source3/modules/offload_token.h

diff --git a/examples/VFS/skel_opaque.c b/examples/VFS/skel_opaque.c
index d5c468e..a7835f3 100644
--- a/examples/VFS/skel_opaque.c
+++ b/examples/VFS/skel_opaque.c
@@ -533,6 +533,48 @@ static struct file_id skel_file_id_create(vfs_handle_struct *handle,
 	return id;
 }
 
+struct skel_offload_read_state {
+	bool dummy;
+};
+
+static struct tevent_req *skel_offload_read_send(
+	TALLOC_CTX *mem_ctx,
+	struct tevent_context *ev,
+	struct vfs_handle_struct *handle,
+	struct files_struct *fsp,
+	uint32_t fsctl,
+	uint32_t ttl,
+	off_t offset,
+	size_t to_copy)
+{
+	struct tevent_req *req = NULL;
+	struct skel_offload_read_state *state = NULL;
+
+	req = tevent_req_create(mem_ctx, &state, struct skel_offload_read_state);
+	if (req == NULL) {
+		return NULL;
+	}
+
+	tevent_req_nterror(req, NT_STATUS_NOT_IMPLEMENTED);
+	return tevent_req_post(req, ev);
+}
+
+static NTSTATUS skel_offload_read_recv(struct tevent_req *req,
+				       struct vfs_handle_struct *handle,
+				       TALLOC_CTX *mem_ctx,
+				       DATA_BLOB *_token_blob)
+{
+	NTSTATUS status;
+
+	if (tevent_req_is_nterror(req, &status)) {
+		tevent_req_received(req);
+		return status;
+	}
+	tevent_req_received(req);
+
+	return NT_STATUS_OK;
+}
+
 struct skel_cc_state {
 	uint64_t unused;
 };
@@ -963,6 +1005,8 @@ struct vfs_fn_pointers skel_opaque_fns = {
 	.realpath_fn = skel_realpath,
 	.chflags_fn = skel_chflags,
 	.file_id_create_fn = skel_file_id_create,
+	.offload_read_send_fn = skel_offload_read_send,
+	.offload_read_recv_fn = skel_offload_read_recv,
 	.copy_chunk_send_fn = skel_copy_chunk_send,
 	.copy_chunk_recv_fn = skel_copy_chunk_recv,
 	.get_compression_fn = skel_get_compression,
diff --git a/examples/VFS/skel_transparent.c b/examples/VFS/skel_transparent.c
index 9387276..9e53870 100644
--- a/examples/VFS/skel_transparent.c
+++ b/examples/VFS/skel_transparent.c
@@ -618,6 +618,94 @@ static struct file_id skel_file_id_create(vfs_handle_struct *handle,
 	return SMB_VFS_NEXT_FILE_ID_CREATE(handle, sbuf);
 }
 
+struct skel_offload_read_state {
+	struct vfs_handle_struct *handle;
+	DATA_BLOB token;
+};
+
+static void skel_offload_read_done(struct tevent_req *subreq);
+
+static struct tevent_req *skel_offload_read_send(
+	TALLOC_CTX *mem_ctx,
+	struct tevent_context *ev,
+	struct vfs_handle_struct *handle,
+	struct files_struct *fsp,
+	uint32_t fsctl,
+	uint32_t ttl,
+	off_t offset,
+	size_t to_copy)
+{
+	struct tevent_req *req = NULL;
+	struct skel_offload_read_state *state = NULL;
+	struct tevent_req *subreq = NULL;
+
+	req = tevent_req_create(mem_ctx, &state, struct skel_offload_read_state);
+	if (req == NULL) {
+		return NULL;
+	}
+	*state = (struct skel_offload_read_state) {
+		.handle = handle,
+	};
+
+	subreq = SMB_VFS_NEXT_OFFLOAD_READ_SEND(mem_ctx, ev, handle, fsp,
+						fsctl, ttl, offset, to_copy);
+	if (tevent_req_nomem(subreq, req)) {
+		return tevent_req_post(req, ev);
+	}
+	tevent_req_set_callback(subreq, skel_offload_read_done, req);
+	return req;
+}
+
+static void skel_offload_read_done(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	struct skel_offload_read_state *state = tevent_req_data(
+		req, struct skel_offload_read_state);
+	NTSTATUS status;
+
+	status = SMB_VFS_NEXT_OFFLOAD_READ_RECV(subreq,
+						state->handle,
+						state,
+						&state->token);
+	TALLOC_FREE(subreq);
+	if (tevent_req_nterror(req, status)) {
+		return;
+	}
+
+	tevent_req_done(req);
+	return;
+}
+
+static NTSTATUS skel_offload_read_recv(struct tevent_req *req,
+				       struct vfs_handle_struct *handle,
+				       TALLOC_CTX *mem_ctx,
+				       DATA_BLOB *_token)
+{
+	struct skel_offload_read_state *state = tevent_req_data(
+		req, struct skel_offload_read_state);
+	DATA_BLOB token;
+	NTSTATUS status;
+
+	if (tevent_req_is_nterror(req, &status)) {
+		tevent_req_received(req);
+		return status;
+	}
+
+	token = data_blob_talloc(mem_ctx,
+				 state->token.data,
+				 state->token.length);
+
+	tevent_req_received(req);
+
+	if (token.data == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	*_token = token;
+	return NT_STATUS_OK;
+}
+
 struct skel_cc_state {
 	struct vfs_handle_struct *handle;
 	off_t copied;
@@ -1090,6 +1178,8 @@ struct vfs_fn_pointers skel_transparent_fns = {
 	.realpath_fn = skel_realpath,
 	.chflags_fn = skel_chflags,
 	.file_id_create_fn = skel_file_id_create,
+	.offload_read_send_fn = skel_offload_read_send,
+	.offload_read_recv_fn = skel_offload_read_recv,
 	.copy_chunk_send_fn = skel_copy_chunk_send,
 	.copy_chunk_recv_fn = skel_copy_chunk_recv,
 	.get_compression_fn = skel_get_compression,
diff --git a/source3/include/vfs.h b/source3/include/vfs.h
index db555f2..f935ec6 100644
--- a/source3/include/vfs.h
+++ b/source3/include/vfs.h
@@ -228,6 +228,7 @@
 		to const struct smb_filename * */
 /* Version 37 - Change symlink from const char *
 		to const struct smb_filename * */
+/* Leave at 37 - Add SMB_VFS_OFFLOAD_READ_SEND/RECV */
 
 #define SMB_VFS_INTERFACE_VERSION 37
 
@@ -767,6 +768,18 @@ struct vfs_fn_pointers {
 				unsigned int flags);
 	struct file_id (*file_id_create_fn)(struct vfs_handle_struct *handle,
 					    const SMB_STRUCT_STAT *sbuf);
+	struct tevent_req *(*offload_read_send_fn)(TALLOC_CTX *mem_ctx,
+						   struct tevent_context *ev,
+						   struct vfs_handle_struct *handle,
+						   struct files_struct *fsp,
+						   uint32_t fsctl,
+						   uint32_t ttl,
+						   off_t offset,
+						   size_t to_copy);
+	NTSTATUS (*offload_read_recv_fn)(struct tevent_req *req,
+					 struct vfs_handle_struct *handle,
+					 TALLOC_CTX *mem_ctx,
+					 DATA_BLOB *token_blob);
 	struct tevent_req *(*copy_chunk_send_fn)(struct vfs_handle_struct *handle,
 						 TALLOC_CTX *mem_ctx,
 						 struct tevent_context *ev,
@@ -1330,6 +1343,19 @@ NTSTATUS smb_vfs_call_set_dos_attributes(struct vfs_handle_struct *handle,
 NTSTATUS smb_vfs_call_fset_dos_attributes(struct vfs_handle_struct *handle,
 					  struct files_struct *fsp,
 					  uint32_t dosmode);
+struct tevent_req *smb_vfs_call_offload_read_send(
+	TALLOC_CTX *mem_ctx,
+	struct tevent_context *ev,
+	struct vfs_handle_struct *handle,
+	struct files_struct *fsp,
+	uint32_t fsctl,
+	uint32_t ttl,
+	off_t offset,
+	size_t to_copy);
+NTSTATUS smb_vfs_call_offload_read_recv(struct tevent_req *req,
+					struct vfs_handle_struct *handle,
+					TALLOC_CTX *mem_ctx,
+					DATA_BLOB *token_blob);
 struct tevent_req *smb_vfs_call_copy_chunk_send(struct vfs_handle_struct *handle,
 						TALLOC_CTX *mem_ctx,
 						struct tevent_context *ev,
diff --git a/source3/include/vfs_macros.h b/source3/include/vfs_macros.h
index 77646b4..bb97091 100644
--- a/source3/include/vfs_macros.h
+++ b/source3/include/vfs_macros.h
@@ -415,6 +415,16 @@
 #define SMB_VFS_NEXT_FSET_DOS_ATTRIBUTES(handle, fsp, attributes) \
 	smb_vfs_call_fset_dos_attributes((handle)->next, (fsp), (attributes))
 
+#define SMB_VFS_OFFLOAD_READ_SEND(mem_ctx, ev, fsp, fsctl, ttl, offset, to_copy) \
+	smb_vfs_call_offload_read_send((mem_ctx), (ev), (fsp)->conn->vfs_handles, fsp, (fsctl), (ttl), (offset), (to_copy))
+#define SMB_VFS_NEXT_OFFLOAD_READ_SEND(mem_ctx, ev, handle, fsp, fsctl, ttl, offset, to_copy) \
+	smb_vfs_call_offload_read_send((mem_ctx), (ev), (handle)->next, (fsp), (fsctl), (ttl), (offset), (to_copy))
+
+#define SMB_VFS_OFFLOAD_READ_RECV(req, conn, mem_ctx, token_blob) \
+	smb_vfs_call_offload_read_recv((req), (conn)->vfs_handles, (mem_ctx), (token_blob))
+#define SMB_VFS_NEXT_OFFLOAD_READ_RECV(req, handle, mem_ctx, token_blob) \
+	smb_vfs_call_offload_read_recv((req), (handle)->next, (mem_ctx), (token_blob))
+
 #define SMB_VFS_COPY_CHUNK_SEND(conn, mem_ctx, ev, src_fsp, src_off, dest_fsp, dest_off, num, flags) \
 	smb_vfs_call_copy_chunk_send((conn)->vfs_handles, (mem_ctx), (ev), (src_fsp), (src_off), (dest_fsp), (dest_off), (num), (flags))
 #define SMB_VFS_NEXT_COPY_CHUNK_SEND(handle, mem_ctx, ev, src_fsp, src_off, dest_fsp, dest_off, num, flags) \
diff --git a/source3/modules/offload_token.c b/source3/modules/offload_token.c
new file mode 100644
index 0000000..aae41be
--- /dev/null
+++ b/source3/modules/offload_token.c
@@ -0,0 +1,207 @@
+/*
+   Unix SMB/CIFS implementation.
+
+   Copyright (C) Ralph Boehme 2017
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "smbd/smbd.h"
+#include "smbd/globals.h"
+#include "dbwrap/dbwrap.h"
+#include "dbwrap/dbwrap_rbt.h"
+#include "dbwrap/dbwrap_open.h"
+#include "../lib/util/util_tdb.h"
+#include "librpc/gen_ndr/ndr_ioctl.h"
+#include "librpc/gen_ndr/ioctl.h"
+#include "offload_token.h"
+
+struct vfs_offload_ctx {
+	bool initialized;
+	struct db_context *db_ctx;
+};
+
+NTSTATUS vfs_offload_token_ctx_init(TALLOC_CTX *mem_ctx,
+				    struct vfs_offload_ctx **_ctx)
+{
+	struct vfs_offload_ctx *ctx = *_ctx;
+
+	if (ctx != NULL) {
+		if (!ctx->initialized) {
+			return NT_STATUS_INTERNAL_ERROR;
+		}
+		return NT_STATUS_OK;
+	}
+
+	ctx = talloc_zero(mem_ctx, struct vfs_offload_ctx);
+	if (ctx == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	ctx->db_ctx = db_open_rbt(mem_ctx);
+	if (ctx->db_ctx == NULL) {
+		TALLOC_FREE(ctx);
+		return NT_STATUS_INTERNAL_ERROR;
+	}
+
+	ctx->initialized = true;
+	*_ctx = ctx;
+	return NT_STATUS_OK;
+}
+
+struct fsp_token_link {
+	struct vfs_offload_ctx *ctx;
+	DATA_BLOB token_blob;
+};
+
+static int fsp_token_link_destructor(struct fsp_token_link *link)
+{
+	DATA_BLOB token_blob = link->token_blob;
+	TDB_DATA key = make_tdb_data(token_blob.data, token_blob.length);
+	NTSTATUS status;
+
+	status = dbwrap_delete(link->ctx->db_ctx, key);
+	if (!NT_STATUS_IS_OK(status)) {
+		DBG_ERR("dbwrap_delete failed: %s. Token:\n", nt_errstr(status));
+		dump_data(0, token_blob.data, token_blob.length);
+		return -1;
+	}
+
+	return 0;
+}
+
+NTSTATUS vfs_offload_token_db_store_fsp(struct vfs_offload_ctx *ctx,
+					const files_struct *fsp,
+					const DATA_BLOB *token_blob)
+{
+	struct db_record *rec = NULL;
+	struct fsp_token_link *link = NULL;
+	TDB_DATA key = make_tdb_data(token_blob->data, token_blob->length);
+	TDB_DATA value;
+	NTSTATUS status;
+
+	link = talloc_zero(fsp, struct fsp_token_link);
+	if (link == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
+	link->ctx = ctx;
+	link->token_blob = data_blob_talloc(link, token_blob->data,
+					    token_blob->length);
+	if (link->token_blob.data == NULL) {
+		TALLOC_FREE(link);
+		return NT_STATUS_NO_MEMORY;
+	}
+	talloc_set_destructor(link, fsp_token_link_destructor);
+
+	rec = dbwrap_fetch_locked(ctx->db_ctx, talloc_tos(), key);
+	if (rec == NULL) {
+		TALLOC_FREE(link);
+		return NT_STATUS_INTERNAL_ERROR;
+	}
+
+	value = dbwrap_record_get_value(rec);
+	if (value.dsize != 0) {
+		DBG_ERR("Found existing token for fsp [%s]. Token:\n",
+			fsp_str_dbg(fsp));
+		dump_data(0, token_blob->data, token_blob->length);
+		TALLOC_FREE(link);
+		TALLOC_FREE(rec);
+		return NT_STATUS_INTERNAL_ERROR;
+	}
+
+	value = make_tdb_data((uint8_t *)&fsp, sizeof(files_struct *));
+
+	status = dbwrap_record_store(rec, value, 0);
+	if (!NT_STATUS_IS_OK(status)) {
+		DBG_ERR("dbwrap_record_store for [%s] failed: %s. Token\n",
+			fsp_str_dbg(fsp), nt_errstr(status));
+		dump_data(0, token_blob->data, token_blob->length);
+		TALLOC_FREE(link);
+		TALLOC_FREE(rec);
+		return status;
+	}
+
+	TALLOC_FREE(rec);
+	return NT_STATUS_OK;
+}
+
+NTSTATUS vfs_offload_token_db_fetch_fsp(struct vfs_offload_ctx *ctx,
+					const DATA_BLOB *token_blob,
+					files_struct **fsp)
+{
+	struct db_record *rec = NULL;
+	TDB_DATA key = make_tdb_data(token_blob->data, token_blob->length);
+	TDB_DATA value;
+	void *ptr = NULL;
+
+	rec = dbwrap_fetch_locked(ctx->db_ctx, talloc_tos(), key);
+	if (rec == NULL) {
+		return NT_STATUS_INTERNAL_ERROR;
+	}
+
+	value = dbwrap_record_get_value(rec);
+	if (value.dsize == 0) {
+		DBG_DEBUG("Unknown token:\n");
+		dump_data(10, token_blob->data, token_blob->length);
+		TALLOC_FREE(rec);
+		return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+	}
+
+	if (value.dsize != sizeof(ptr)) {
+		DBG_ERR("Bad db entry for token:\n");
+		dump_data(1, token_blob->data, token_blob->length);
+		TALLOC_FREE(rec);
+		return NT_STATUS_INTERNAL_ERROR;
+	}
+
+	memcpy(&ptr, value.dptr, value.dsize);
+	TALLOC_FREE(rec);
+
+	*fsp = talloc_get_type_abort(ptr, struct files_struct);
+	return NT_STATUS_OK;
+}
+
+NTSTATUS vfs_offload_token_create_blob(TALLOC_CTX *mem_ctx,
+				       const files_struct *fsp,
+				       uint32_t fsctl,
+				       DATA_BLOB *token_blob)
+{
+	size_t len;
+
+	switch (fsctl) {
+	case FSCTL_DUP_EXTENTS_TO_FILE:
+		len = 20;
+		break;
+	case FSCTL_SRV_REQUEST_RESUME_KEY:
+		len = 24;
+		break;
+	default:
+		DBG_ERR("Invalid fsctl [%" PRIu32 "]\n", fsctl);
+		return NT_STATUS_NOT_SUPPORTED;
+	}
+
+	*token_blob = data_blob_talloc_zero(mem_ctx, len);
+	if (token_blob->length == 0) {
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	/* combine persistent and volatile handles for the resume key */
+	SBVAL(token_blob->data,  0, fsp->op->global->open_persistent_id);
+	SBVAL(token_blob->data,  8, fsp->op->global->open_volatile_id);
+	SIVAL(token_blob->data, 16, fsctl);
+
+	return NT_STATUS_OK;
+}
+
diff --git a/source3/modules/offload_token.h b/source3/modules/offload_token.h
new file mode 100644
index 0000000..569edcf
--- /dev/null
+++ b/source3/modules/offload_token.h
@@ -0,0 +1,37 @@
+/*
+   Unix SMB/Netbios implementation.
+   Copyright (c) 2017 Ralph Boehme <slow at samba.org>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _OFFLOAD_TOKEN_H_
+#define _OFFLOAD_TOKEN_H_
+
+struct vfs_offload_ctx;
+struct req_resume_key_rsp;
+
+NTSTATUS vfs_offload_token_ctx_init(TALLOC_CTX *mem_ctx,
+				    struct vfs_offload_ctx **_ctx);
+NTSTATUS vfs_offload_token_db_store_fsp(struct vfs_offload_ctx *ctx,
+					const files_struct *fsp,
+					const DATA_BLOB *token_blob);
+NTSTATUS vfs_offload_token_db_fetch_fsp(struct vfs_offload_ctx *ctx,
+					const DATA_BLOB *token_blob,
+					files_struct **fsp);
+NTSTATUS vfs_offload_token_create_blob(TALLOC_CTX *mem_ctx,
+				       const files_struct *fsp,
+				       uint32_t fsctl,
+				       DATA_BLOB *token_blob);
+#endif
diff --git a/source3/modules/vfs_btrfs.c b/source3/modules/vfs_btrfs.c
index e306ece..b175a84 100644
--- a/source3/modules/vfs_btrfs.c
+++ b/source3/modules/vfs_btrfs.c
@@ -27,9 +27,11 @@
 #include "system/filesys.h"
 #include "includes.h"
 #include "smbd/smbd.h"
+#include "smbd/globals.h"
 #include "librpc/gen_ndr/smbXsrv.h"
 #include "librpc/gen_ndr/ioctl.h"
 #include "lib/util/tevent_ntstatus.h"
+#include "offload_token.h"
 
 static uint32_t btrfs_fs_capabilities(struct vfs_handle_struct *handle,
 				      enum timestamp_set_resolution *_ts_res)
@@ -79,6 +81,121 @@ struct btrfs_ioctl_clone_range_args {
 #define BTRFS_IOC_SNAP_CREATE_V2 _IOW(BTRFS_IOCTL_MAGIC, 23, \
 				      struct btrfs_ioctl_vol_args_v2)
 
+static struct vfs_offload_ctx *btrfs_offload_ctx;
+
+struct btrfs_offload_read_state {
+	struct vfs_handle_struct *handle;
+	files_struct *fsp;
+	DATA_BLOB token;
+};
+
+static void btrfs_offload_read_done(struct tevent_req *subreq);
+
+static struct tevent_req *btrfs_offload_read_send(
+	TALLOC_CTX *mem_ctx,
+	struct tevent_context *ev,
+	struct vfs_handle_struct *handle,
+	files_struct *fsp,
+	uint32_t fsctl,
+	uint32_t ttl,
+	off_t offset,
+	size_t to_copy)
+{
+	struct tevent_req *req = NULL;
+	struct tevent_req *subreq = NULL;
+	struct btrfs_offload_read_state *state = NULL;
+	NTSTATUS status;
+
+	req = tevent_req_create(mem_ctx, &state,
+				struct btrfs_offload_read_state);
+	if (req == NULL) {
+		return NULL;
+	}
+	*state = (struct btrfs_offload_read_state) {
+		.handle = handle,
+		.fsp = fsp,
+	};
+
+	status = vfs_offload_token_ctx_init(fsp->conn->sconn->client,
+					    &btrfs_offload_ctx);
+	if (tevent_req_nterror(req, status)) {
+		return tevent_req_post(req, ev);
+	}
+
+	if (fsctl == FSCTL_DUP_EXTENTS_TO_FILE) {
+		status = vfs_offload_token_create_blob(state, fsp, fsctl,
+						       &state->token);
+		if (tevent_req_nterror(req, status)) {
+			return tevent_req_post(req, ev);
+		}
+
+		status = vfs_offload_token_db_store_fsp(btrfs_offload_ctx, fsp,
+							&state->token);
+		if (tevent_req_nterror(req, status)) {
+			return tevent_req_post(req, ev);
+		}
+		tevent_req_done(req);
+		return tevent_req_post(req, ev);
+	}
+
+	subreq = SMB_VFS_NEXT_OFFLOAD_READ_SEND(mem_ctx, ev, handle, fsp,
+						fsctl, ttl, offset, to_copy);
+	if (tevent_req_nomem(subreq, req)) {
+		return tevent_req_post(req, ev);
+	}
+	tevent_req_set_callback(subreq, btrfs_offload_read_done, req);
+	return req;
+}
+
+static void btrfs_offload_read_done(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	struct btrfs_offload_read_state *state = tevent_req_data(
+		req, struct btrfs_offload_read_state);
+	NTSTATUS status;
+
+	status = SMB_VFS_NEXT_OFFLOAD_READ_RECV(subreq,
+						state->handle,
+						state,
+						&state->token);
+	TALLOC_FREE(subreq);
+	if (tevent_req_nterror(req, status)) {
+		return;
+	}
+
+	status = vfs_offload_token_db_store_fsp(btrfs_offload_ctx,
+						state->fsp,
+						&state->token);
+	if (tevent_req_nterror(req, status)) {
+		return;
+	}
+
+	tevent_req_done(req);
+	return;
+}
+
+static NTSTATUS btrfs_offload_read_recv(struct tevent_req *req,
+					struct vfs_handle_struct *handle,
+					TALLOC_CTX *mem_ctx,
+					DATA_BLOB *token)
+{
+	struct btrfs_offload_read_state *state = tevent_req_data(
+		req, struct btrfs_offload_read_state);
+	NTSTATUS status;
+
+	if (tevent_req_is_nterror(req, &status)) {
+		tevent_req_received(req);
+		return status;
+	}
+
+	token->length = state->token.length;
+	token->data = talloc_move(mem_ctx, &state->token.data);
+
+	tevent_req_received(req);
+	return NT_STATUS_OK;
+}
+
 struct btrfs_cc_state {
 	struct vfs_handle_struct *handle;
 	off_t copied;
@@ -681,6 +798,8 @@ static NTSTATUS btrfs_snap_delete(struct vfs_handle_struct *handle,
 
 static struct vfs_fn_pointers btrfs_fns = {
 	.fs_capabilities_fn = btrfs_fs_capabilities,
+	.offload_read_send_fn = btrfs_offload_read_send,
+	.offload_read_recv_fn = btrfs_offload_read_recv,
 	.copy_chunk_send_fn = btrfs_copy_chunk_send,
 	.copy_chunk_recv_fn = btrfs_copy_chunk_recv,
 	.get_compression_fn = btrfs_get_compression,
diff --git a/source3/modules/vfs_default.c b/source3/modules/vfs_default.c
index d339f39..5b99884 100644
--- a/source3/modules/vfs_default.c
+++ b/source3/modules/vfs_default.c
@@ -34,6 +34,7 @@
 #include "lib/util/sys_rw.h"
 #include "lib/pthreadpool/pthreadpool_tevent.h"
 #include "librpc/gen_ndr/ndr_ioctl.h"
+#include "offload_token.h"
 
 #undef DBGC_CLASS
 #define DBGC_CLASS DBGC_VFS
@@ -1599,6 +1600,80 @@ static NTSTATUS vfswrap_fset_dos_attributes(struct vfs_handle_struct *handle,
 	return set_ea_dos_attribute(handle->conn, fsp->fsp_name, dosmode);
 }
 
+static struct vfs_offload_ctx *vfswrap_offload_ctx;
+
+struct vfswrap_offload_read_state {
+	DATA_BLOB token;
+};
+
+static struct tevent_req *vfswrap_offload_read_send(
+	TALLOC_CTX *mem_ctx,
+	struct tevent_context *ev,
+	struct vfs_handle_struct *handle,
+	struct files_struct *fsp,
+	uint32_t fsctl,
+	uint32_t ttl,
+	off_t offset,
+	size_t to_copy)
+{
+	struct tevent_req *req = NULL;
+	struct vfswrap_offload_read_state *state = NULL;
+	NTSTATUS status;
+
+	req = tevent_req_create(mem_ctx, &state,
+				struct vfswrap_offload_read_state);
+	if (req == NULL) {
+		return NULL;
+	}
+
+	status = vfs_offload_token_ctx_init(fsp->conn->sconn->client,
+					    &vfswrap_offload_ctx);
+	if (tevent_req_nterror(req, status)) {
+		return tevent_req_post(req, ev);
+	}
+
+	if (fsctl != FSCTL_SRV_REQUEST_RESUME_KEY) {
+		tevent_req_nterror(req, NT_STATUS_INVALID_DEVICE_REQUEST);
+		return tevent_req_post(req, ev);
+	}
+
+	status = vfs_offload_token_create_blob(state, fsp, fsctl,
+					       &state->token);
+	if (tevent_req_nterror(req, status)) {
+		return tevent_req_post(req, ev);
+	}
+
+	status = vfs_offload_token_db_store_fsp(vfswrap_offload_ctx, fsp,
+						&state->token);
+	if (tevent_req_nterror(req, status)) {
+		return tevent_req_post(req, ev);
+	}
+
+	tevent_req_done(req);
+	return tevent_req_post(req, ev);
+}
+
+static NTSTATUS vfswrap_offload_read_recv(struct tevent_req *req,
+					  struct vfs_handle_struct *handle,
+					  TALLOC_CTX *mem_ctx,
+					  DATA_BLOB *token)
+{
+	struct vfswrap_offload_read_state *state = tevent_req_data(
+		req, struct vfswrap_offload_read_state);
+	NTSTATUS status;
+
+	if (tevent_req_is_nterror(req, &status)) {
+		tevent_req_received(req);
+		return status;
+	}
+
+	token->length = state->token.length;
+	token->data = talloc_move(mem_ctx, &state->token.data);
+
+	tevent_req_received(req);
+	return NT_STATUS_OK;
+}
+
 struct vfs_cc_state {
 	struct tevent_context *ev;
 	uint8_t *buf;
@@ -2976,6 +3051,8 @@ static struct vfs_fn_pointers vfs_default_fns = {
 	.fset_dos_attributes_fn = vfswrap_fset_dos_attributes,
 	.get_dos_attributes_fn = vfswrap_get_dos_attributes,
 	.fget_dos_attributes_fn = vfswrap_fget_dos_attributes,
+	.offload_read_send_fn = vfswrap_offload_read_send,
+	.offload_read_recv_fn = vfswrap_offload_read_recv,
 	.copy_chunk_send_fn = vfswrap_copy_chunk_send,
 	.copy_chunk_recv_fn = vfswrap_copy_chunk_recv,
 	.get_compression_fn = vfswrap_get_compression,
diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c
index c4277b9..20d15b3 100644
--- a/source3/modules/vfs_fruit.c
+++ b/source3/modules/vfs_fruit.c
@@ -32,6 +32,7 @@
 #include "lib/util/sys_rw.h"
 #include "lib/util/tevent_ntstatus.h"
 #include "lib/util/tevent_unix.h"
+#include "offload_token.h"
 
 /*
  * Enhanced OS X and Netatalk compatibility
@@ -5365,6 +5366,113 @@ static NTSTATUS fruit_fset_nt_acl(vfs_handle_struct *handle,
 	return NT_STATUS_OK;
 }
 
+static struct vfs_offload_ctx *fruit_offload_ctx;
+
+struct fruit_offload_read_state {
+	struct vfs_handle_struct *handle;
+	struct tevent_context *ev;
+	files_struct *fsp;
+	uint32_t fsctl;
+	DATA_BLOB token;
+};
+
+static void fruit_offload_read_done(struct tevent_req *subreq);
+
+static struct tevent_req *fruit_offload_read_send(
+	TALLOC_CTX *mem_ctx,
+	struct tevent_context *ev,
+	struct vfs_handle_struct *handle,
+	files_struct *fsp,
+	uint32_t fsctl,
+	uint32_t ttl,
+	off_t offset,
+	size_t to_copy)
+{
+	struct tevent_req *req = NULL;
+	struct tevent_req *subreq = NULL;
+	struct fruit_offload_read_state *state = NULL;
+
+	req = tevent_req_create(mem_ctx, &state,
+				struct fruit_offload_read_state);
+	if (req == NULL) {
+		return NULL;
+	}
+	*state = (struct fruit_offload_read_state) {
+		.handle = handle,
+		.ev = ev,
+		.fsp = fsp,
+		.fsctl = fsctl,
+	};
+
+	subreq = SMB_VFS_NEXT_OFFLOAD_READ_SEND(mem_ctx, ev, handle, fsp,
+						fsctl, ttl, offset, to_copy);
+	if (tevent_req_nomem(subreq, req)) {
+		return tevent_req_post(req, ev);
+	}
+	tevent_req_set_callback(subreq, fruit_offload_read_done, req);
+	return req;
+}
+
+static void fruit_offload_read_done(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	struct fruit_offload_read_state *state = tevent_req_data(
+		req, struct fruit_offload_read_state);
+	NTSTATUS status;
+
+	status = SMB_VFS_NEXT_OFFLOAD_READ_RECV(subreq,
+						state->handle,
+						state,
+						&state->token);
+	TALLOC_FREE(subreq);
+	if (tevent_req_nterror(req, status)) {
+		return;
+	}
+
+	if (state->fsctl != FSCTL_SRV_REQUEST_RESUME_KEY) {
+		tevent_req_done(req);
+		return;
+	}
+
+	status = vfs_offload_token_ctx_init(state->fsp->conn->sconn->client,
+					    &fruit_offload_ctx);
+	if (tevent_req_nterror(req, status)) {
+		return;
+	}
+
+	status = vfs_offload_token_db_store_fsp(fruit_offload_ctx,
+						state->fsp,
+						&state->token);
+	if (tevent_req_nterror(req, status)) {
+		return;
+	}
+
+	tevent_req_done(req);
+	return;
+}
+
+static NTSTATUS fruit_offload_read_recv(struct tevent_req *req,
+					struct vfs_handle_struct *handle,
+					TALLOC_CTX *mem_ctx,
+					DATA_BLOB *token)
+{
+	struct fruit_offload_read_state *state = tevent_req_data(
+		req, struct fruit_offload_read_state);
+	NTSTATUS status;
+
+	if (tevent_req_is_nterror(req, &status)) {
+		tevent_req_received(req);
+		return status;
+	}
+
+	token->length = state->token.length;
+	token->data = talloc_move(mem_ctx, &state->token.data);
+
+	tevent_req_received(req);
+	return NT_STATUS_OK;
+}
+
 struct fruit_copy_chunk_state {
 	struct vfs_handle_struct *handle;
 	off_t copied;
@@ -5590,6 +5698,8 @@ static struct vfs_fn_pointers vfs_fruit_fns = {
 	.fallocate_fn = fruit_fallocate,
 	.create_file_fn = fruit_create_file,
 	.readdir_attr_fn = fruit_readdir_attr,
+	.offload_read_send_fn = fruit_offload_read_send,
+	.offload_read_recv_fn = fruit_offload_read_recv,
 	.copy_chunk_send_fn = fruit_copy_chunk_send,
 	.copy_chunk_recv_fn = fruit_copy_chunk_recv,
 
diff --git a/source3/modules/vfs_full_audit.c b/source3/modules/vfs_full_audit.c
index 1267ef3..f548457 100644
--- a/source3/modules/vfs_full_audit.c
+++ b/source3/modules/vfs_full_audit.c
@@ -168,6 +168,8 @@ typedef enum _vfs_op_type {
 	SMB_VFS_OP_STRICT_UNLOCK,
 	SMB_VFS_OP_TRANSLATE_NAME,
 	SMB_VFS_OP_FSCTL,
+	SMB_VFS_OP_OFFLOAD_READ_SEND,
+	SMB_VFS_OP_OFFLOAD_READ_RECV,
 	SMB_VFS_OP_COPY_CHUNK_SEND,
 	SMB_VFS_OP_COPY_CHUNK_RECV,
 	SMB_VFS_OP_GET_COMPRESSION,
@@ -310,6 +312,8 @@ static struct {
 	{ SMB_VFS_OP_STRICT_UNLOCK, "strict_unlock" },
 	{ SMB_VFS_OP_TRANSLATE_NAME,	"translate_name" },
 	{ SMB_VFS_OP_FSCTL,		"fsctl" },
+	{ SMB_VFS_OP_OFFLOAD_READ_SEND,	"offload_read_send" },
+	{ SMB_VFS_OP_OFFLOAD_READ_RECV,	"offload_read_recv" },
 	{ SMB_VFS_OP_COPY_CHUNK_SEND,	"copy_chunk_send" },
 	{ SMB_VFS_OP_COPY_CHUNK_RECV,	"copy_chunk_recv" },
 	{ SMB_VFS_OP_GET_COMPRESSION,	"get_compression" },
@@ -1897,6 +1901,42 @@ static NTSTATUS smb_full_audit_fsctl(struct vfs_handle_struct *handle,
 	return result;
 }
 
+static struct tevent_req *smb_full_audit_offload_read_send(
+	TALLOC_CTX *mem_ctx,
+	struct tevent_context *ev,
+	struct vfs_handle_struct *handle,
+	struct files_struct *fsp,
+	uint32_t fsctl,
+	uint32_t ttl,
+	off_t offset,
+	size_t to_copy)
+{
+	struct tevent_req *req = NULL;
+
+	req = SMB_VFS_NEXT_OFFLOAD_READ_SEND(mem_ctx, ev, handle, fsp,
+					     fsctl, ttl, offset, to_copy);
+
+	do_log(SMB_VFS_OP_OFFLOAD_READ_SEND, req, handle, "");
+
+	return req;
+}
+
+static NTSTATUS smb_full_audit_offload_read_recv(
+	struct tevent_req *req,
+	struct vfs_handle_struct *handle,
+	TALLOC_CTX *mem_ctx,
+	DATA_BLOB *_token_blob)
+{
+	NTSTATUS status;
+
+	status = SMB_VFS_NEXT_OFFLOAD_READ_RECV(req, handle, mem_ctx,
+						_token_blob);
+
+	do_log(SMB_VFS_OP_OFFLOAD_READ_RECV, NT_STATUS_IS_OK(status), handle, "");
+
+	return status;
+}
+
 static struct tevent_req *smb_full_audit_copy_chunk_send(struct vfs_handle_struct *handle,
 							 TALLOC_CTX *mem_ctx,
 							 struct tevent_context *ev,
@@ -2530,6 +2570,8 @@ static struct vfs_fn_pointers vfs_full_audit_fns = {
 	.realpath_fn = smb_full_audit_realpath,
 	.chflags_fn = smb_full_audit_chflags,
 	.file_id_create_fn = smb_full_audit_file_id_create,
+	.offload_read_send_fn = smb_full_audit_offload_read_send,
+	.offload_read_recv_fn = smb_full_audit_offload_read_recv,
 	.copy_chunk_send_fn = smb_full_audit_copy_chunk_send,
 	.copy_chunk_recv_fn = smb_full_audit_copy_chunk_recv,
 	.get_compression_fn = smb_full_audit_get_compression,
diff --git a/source3/modules/vfs_time_audit.c b/source3/modules/vfs_time_audit.c
index d399201..7fe9912 100644
--- a/source3/modules/vfs_time_audit.c
+++ b/source3/modules/vfs_time_audit.c
@@ -1891,6 +1891,103 @@ static NTSTATUS smb_time_fset_dos_attributes(struct vfs_handle_struct *handle,
 	return result;
 }
 
+struct time_audit_offload_read_state {
+	struct vfs_handle_struct *handle;
+	struct timespec ts_send;
+	DATA_BLOB token_blob;
+};
+
+static void smb_time_audit_offload_read_done(struct tevent_req *subreq);
+
+static struct tevent_req *smb_time_audit_offload_read_send(
+	TALLOC_CTX *mem_ctx,
+	struct tevent_context *ev,
+	struct vfs_handle_struct *handle,
+	struct files_struct *fsp,
+	uint32_t fsctl,
+	uint32_t ttl,
+	off_t offset,
+	size_t to_copy)
+{
+	struct tevent_req *req = NULL;
+	struct tevent_req *subreq = NULL;
+	struct time_audit_offload_read_state *state = NULL;
+
+	req = tevent_req_create(mem_ctx, &state,
+				struct time_audit_offload_read_state);
+	if (req == NULL) {
+		return NULL;
+	}
+	state->handle = handle;
+	clock_gettime_mono(&state->ts_send);
+
+	subreq = SMB_VFS_NEXT_OFFLOAD_READ_SEND(mem_ctx, ev,
+						handle, fsp,
+						fsctl, ttl,
+						offset, to_copy);
+	if (tevent_req_nomem(subreq, req)) {
+		return tevent_req_post(req, ev);
+	}
+
+	tevent_req_set_callback(subreq, smb_time_audit_offload_read_done, req);
+	return req;
+}
+
+static void smb_time_audit_offload_read_done(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	struct time_audit_offload_read_state *state = tevent_req_data(
+		req, struct time_audit_offload_read_state);
+	NTSTATUS status;
+
+	status = SMB_VFS_NEXT_OFFLOAD_READ_RECV(subreq,
+						state->handle,
+						state,
+						&state->token_blob);
+	TALLOC_FREE(subreq);
+	if (tevent_req_nterror(req, status)) {
+		return;
+	}
+	tevent_req_done(req);
+}
+
+static NTSTATUS smb_time_audit_offload_read_recv(
+	struct tevent_req *req,
+	struct vfs_handle_struct *handle,
+	TALLOC_CTX *mem_ctx,
+	DATA_BLOB *_token_blob)
+{
+	struct time_audit_offload_read_state *state = tevent_req_data(
+		req, struct time_audit_offload_read_state);
+	struct timespec ts_recv;
+	double timediff;
+	DATA_BLOB token_blob;
+	NTSTATUS status;
+
+	clock_gettime_mono(&ts_recv);
+	timediff = nsec_time_diff(&ts_recv, &state->ts_send) * 1.0e-9;
+	if (timediff > audit_timeout) {
+		smb_time_audit_log("offload_read", timediff);
+	}
+
+	if (tevent_req_is_nterror(req, &status)) {
+		tevent_req_received(req);
+		return status;
+	}
+
+	token_blob = data_blob_talloc(mem_ctx,
+				      state->token_blob.data,
+				      state->token_blob.length);
+	if (token_blob.data == NULL) {
+		tevent_req_received(req);
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	tevent_req_received(req);
+	return NT_STATUS_OK;
+}
+
 struct time_audit_cc_state {
 	struct timespec ts_send;
 	struct vfs_handle_struct *handle;
@@ -2665,6 +2762,8 @@ static struct vfs_fn_pointers vfs_time_audit_fns = {
 	.realpath_fn = smb_time_audit_realpath,
 	.chflags_fn = smb_time_audit_chflags,
 	.file_id_create_fn = smb_time_audit_file_id_create,
+	.offload_read_send_fn = smb_time_audit_offload_read_send,
+	.offload_read_recv_fn = smb_time_audit_offload_read_recv,
 	.copy_chunk_send_fn = smb_time_audit_copy_chunk_send,
 	.copy_chunk_recv_fn = smb_time_audit_copy_chunk_recv,
 	.get_compression_fn = smb_time_audit_get_compression,
diff --git a/source3/modules/wscript_build b/source3/modules/wscript_build
index a5d8407..840fdef 100644
--- a/source3/modules/wscript_build
+++ b/source3/modules/wscript_build
@@ -22,10 +22,14 @@ bld.SAMBA3_SUBSYSTEM('vfs',
                     source='',
                     deps='smbd_base')
 
+bld.SAMBA3_SUBSYSTEM('OFFLOAD_TOKEN',
+                    source='offload_token.c',
+                    deps='samba-util')
+
 bld.SAMBA3_MODULE('vfs_default',
                  subsystem='vfs',
                  source='vfs_default.c',
-                 deps='samba-util NDR_DFSBLOBS',
+                 deps='samba-util NDR_DFSBLOBS OFFLOAD_TOKEN',
                  init_function='',
                  internal_module=bld.SAMBA3_IS_STATIC_MODULE('vfs_default'),
                  enabled=bld.SAMBA3_IS_ENABLED_MODULE('vfs_default'))
@@ -89,7 +93,7 @@ bld.SAMBA3_MODULE('vfs_netatalk',
 bld.SAMBA3_MODULE('vfs_fruit',
                  subsystem='vfs',
                  source='vfs_fruit.c',
-                 deps='samba-util',
+                 deps='samba-util OFFLOAD_TOKEN',
                  init_function='',
                  internal_module=bld.SAMBA3_IS_STATIC_MODULE('vfs_fruit'),
                  enabled=bld.SAMBA3_IS_ENABLED_MODULE('vfs_fruit'))
@@ -425,7 +429,7 @@ bld.SAMBA3_MODULE('vfs_dfs_samba4',
 bld.SAMBA3_MODULE('vfs_btrfs',
                  subsystem='vfs',
                  source='vfs_btrfs.c',
-                 deps='samba-util',
+                 deps='samba-util OFFLOAD_TOKEN',
                  init_function='',
                  internal_module=bld.SAMBA3_IS_STATIC_MODULE('vfs_btrfs'),
                  enabled=bld.SAMBA3_IS_ENABLED_MODULE('vfs_btrfs'))
diff --git a/source3/smbd/vfs.c b/source3/smbd/vfs.c
index 5acfb9a..938013a 100644
--- a/source3/smbd/vfs.c
+++ b/source3/smbd/vfs.c
@@ -2311,6 +2311,30 @@ NTSTATUS smb_vfs_call_fset_dos_attributes(struct vfs_handle_struct *handle,
 	return handle->fns->fset_dos_attributes_fn(handle, fsp, dosmode);
 }
 
+struct tevent_req *smb_vfs_call_offload_read_send(TALLOC_CTX *mem_ctx,
+						  struct tevent_context *ev,
+						  struct vfs_handle_struct *handle,
+						  struct files_struct *fsp,
+						  uint32_t fsctl,
+						  uint32_t ttl,
+						  off_t offset,
+						  size_t to_copy)
+{
+	VFS_FIND(offload_read_send);
+	return handle->fns->offload_read_send_fn(mem_ctx, ev, handle,
+						 fsp, fsctl,
+						 ttl, offset, to_copy);
+}
+
+NTSTATUS smb_vfs_call_offload_read_recv(struct tevent_req *req,
+					struct vfs_handle_struct *handle,
+					TALLOC_CTX *mem_ctx,
+					DATA_BLOB *token_blob)
+{
+	VFS_FIND(offload_read_recv);
+	return handle->fns->offload_read_recv_fn(req, handle, mem_ctx, token_blob);
+}
+
 struct tevent_req *smb_vfs_call_copy_chunk_send(struct vfs_handle_struct *handle,
 						TALLOC_CTX *mem_ctx,
 						struct tevent_context *ev,
-- 
2.9.4


From a13485aa486778d24c4510489d46ba4ae4cc0952 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Tue, 6 Jun 2017 12:23:27 +0200
Subject: [PATCH 06/17] s3/smbd: use SMB_VFS_OFFLOAD_READ_SEND/RECV

No change in behaviour, this just uses the new SMB_VFS_OFFLOAD_READ_SEND
in the duplicate extents and the resume key ioctls.

In the copy-chunk/resume-key case this means using
SMB_VFS_OFFLOAD_READ_SEND to create the resume-key token that is
returned to the client.

In the duplicate-extents case this ensures we can later call
offload-write, which requires a previous call to offload-read that
associates a token with a file-handle.

Signed-off-by: Ralph Boehme <slow at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 source3/smbd/smb2_ioctl_filesys.c    | 47 +++++++++++++++---
 source3/smbd/smb2_ioctl_network_fs.c | 93 ++++++++++++++++++++----------------
 2 files changed, 91 insertions(+), 49 deletions(-)

diff --git a/source3/smbd/smb2_ioctl_filesys.c b/source3/smbd/smb2_ioctl_filesys.c
index 392372f..7008a47 100644
--- a/source3/smbd/smb2_ioctl_filesys.c
+++ b/source3/smbd/smb2_ioctl_filesys.c
@@ -161,9 +161,12 @@ static NTSTATUS fsctl_dup_extents_check_sparse(struct files_struct *src_fsp,
 struct fsctl_dup_extents_state {
 	struct tevent_context *ev;
 	struct connection_struct *conn;
+	struct files_struct *src_fsp;
+	struct files_struct *dst_fsp;
 	struct fsctl_dup_extents_to_file dup_extents;
 };
 
+static void fsctl_dup_extents_offload_read_done(struct tevent_req *subreq);
 static void fsctl_dup_extents_vfs_done(struct tevent_req *subreq);
 
 static struct tevent_req *fsctl_dup_extents_send(TALLOC_CTX *mem_ctx,
@@ -195,6 +198,7 @@ static struct tevent_req *fsctl_dup_extents_send(TALLOC_CTX *mem_ctx,
 	*state = (struct fsctl_dup_extents_state) {
 		.conn = dst_fsp->conn,
 		.ev = ev,
+		.dst_fsp = dst_fsp,
 	};
 
 	if ((dst_fsp->conn->fs_capabilities
@@ -230,6 +234,7 @@ static struct tevent_req *fsctl_dup_extents_send(TALLOC_CTX *mem_ctx,
 		tevent_req_nterror(req, NT_STATUS_INVALID_HANDLE);
 		return tevent_req_post(req, ev);
 	}
+	state->src_fsp = src_fsp;
 
 	status = fsctl_dup_extents_check_lengths(src_fsp, dst_fsp,
 						 &state->dup_extents);
@@ -257,20 +262,48 @@ static struct tevent_req *fsctl_dup_extents_send(TALLOC_CTX *mem_ctx,
 		return tevent_req_post(req, ev);
 	}
 
+	subreq = SMB_VFS_OFFLOAD_READ_SEND(state, ev, src_fsp,
+					   FSCTL_DUP_EXTENTS_TO_FILE,
+					   0, 0, 0);
+	if (tevent_req_nomem(subreq, req)) {
+		return tevent_req_post(req, ev);
+	}
+	tevent_req_set_callback(subreq, fsctl_dup_extents_offload_read_done,
+				req);
+	return req;
+}
+
+static void fsctl_dup_extents_offload_read_done(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	struct fsctl_dup_extents_state *state = tevent_req_data(
+		req, struct fsctl_dup_extents_state);
+	DATA_BLOB token_blob;
+	NTSTATUS status;
+
+	status = SMB_VFS_OFFLOAD_READ_RECV(subreq, state->dst_fsp->conn,
+					   state, &token_blob);
+	if (tevent_req_nterror(req, status)) {
+		return;
+	}
+
 	/* tell the VFS to ignore locks across the clone, matching ReFS */
-	subreq = SMB_VFS_COPY_CHUNK_SEND(dst_fsp->conn, state, ev,
-					 src_fsp, state->dup_extents.source_off,
-					 dst_fsp, state->dup_extents.target_off,
+	subreq = SMB_VFS_COPY_CHUNK_SEND(state->dst_fsp->conn,
+					 state,
+					 state->ev,
+					 state->src_fsp,
+					 state->dup_extents.source_off,
+					 state->dst_fsp,
+					 state->dup_extents.target_off,
 					 state->dup_extents.byte_count,
 					 VFS_COPY_CHUNK_FL_MUST_CLONE
 					 | VFS_COPY_CHUNK_FL_IGNORE_LOCKS);
 	if (tevent_req_nomem(subreq, req)) {
-		return tevent_req_post(req, ev);
+		return;
 	}
-
 	tevent_req_set_callback(subreq, fsctl_dup_extents_vfs_done, req);
-
-	return subreq;
+	return;
 }
 
 static void fsctl_dup_extents_vfs_done(struct tevent_req *subreq)
diff --git a/source3/smbd/smb2_ioctl_network_fs.c b/source3/smbd/smb2_ioctl_network_fs.c
index 016a0e4..acb17c3 100644
--- a/source3/smbd/smb2_ioctl_network_fs.c
+++ b/source3/smbd/smb2_ioctl_network_fs.c
@@ -672,42 +672,8 @@ static NTSTATUS fsctl_validate_neg_info(TALLOC_CTX *mem_ctx,
 	return NT_STATUS_OK;
 }
 
-static NTSTATUS fsctl_srv_req_resume_key(TALLOC_CTX *mem_ctx,
-					 struct tevent_context *ev,
-					 struct files_struct *fsp,
-					 uint32_t in_max_output,
-					 DATA_BLOB *out_output)
-{
-	struct req_resume_key_rsp rkey_rsp;
-	enum ndr_err_code ndr_ret;
-	DATA_BLOB output;
-
-	if (fsp == NULL) {
-		return NT_STATUS_FILE_CLOSED;
-	}
-
-	ZERO_STRUCT(rkey_rsp);
-	/* combine persistent and volatile handles for the resume key */
-	SBVAL(rkey_rsp.resume_key, 0, fsp->op->global->open_persistent_id);
-	SBVAL(rkey_rsp.resume_key, 8, fsp->op->global->open_volatile_id);
-
-	ndr_ret = ndr_push_struct_blob(&output, mem_ctx, &rkey_rsp,
-			(ndr_push_flags_fn_t)ndr_push_req_resume_key_rsp);
-	if (ndr_ret != NDR_ERR_SUCCESS) {
-		return NT_STATUS_INTERNAL_ERROR;
-	}
-
-	if (in_max_output < output.length) {
-		DEBUG(1, ("max output %u too small for resume key rsp %ld\n",
-			  (unsigned int)in_max_output, (long int)output.length));
-		return NT_STATUS_INVALID_PARAMETER;
-	}
-	*out_output = output;
-
-	return NT_STATUS_OK;
-}
-
 static void smb2_ioctl_network_fs_copychunk_done(struct tevent_req *subreq);
+static void smb2_ioctl_network_fs_offload_read_done(struct tevent_req *subreq);
 
 struct tevent_req *smb2_ioctl_network_fs(uint32_t ctl_code,
 					 struct tevent_context *ev,
@@ -777,14 +743,18 @@ struct tevent_req *smb2_ioctl_network_fs(uint32_t ctl_code,
 		return tevent_req_post(req, ev);
 		break;
 	case FSCTL_SRV_REQUEST_RESUME_KEY:
-		status = fsctl_srv_req_resume_key(state, ev, state->fsp,
-						  state->in_max_output,
-						  &state->out_output);
-		if (!tevent_req_nterror(req, status)) {
-			tevent_req_done(req);
+		subreq = SMB_VFS_OFFLOAD_READ_SEND(state,
+						   ev,
+						   state->fsp,
+						   FSCTL_SRV_REQUEST_RESUME_KEY,
+						   0, 0, 0);
+		if (tevent_req_nomem(subreq, req)) {
+			return tevent_req_post(req, ev);
 		}
-		return tevent_req_post(req, ev);
-		break;
+		tevent_req_set_callback(
+			subreq, smb2_ioctl_network_fs_offload_read_done, req);
+		return req;
+
 	default: {
 		uint8_t *out_data = NULL;
 		uint32_t out_data_len = 0;
@@ -854,3 +824,42 @@ static void smb2_ioctl_network_fs_copychunk_done(struct tevent_req *subreq)
 		tevent_req_done(req);
 	}
 }
+
+static void smb2_ioctl_network_fs_offload_read_done(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	struct smbd_smb2_ioctl_state *state = tevent_req_data(
+		req, struct smbd_smb2_ioctl_state);
+	struct req_resume_key_rsp rkey_rsp;
+	enum ndr_err_code ndr_ret;
+	DATA_BLOB token;
+	NTSTATUS status;
+
+	status = SMB_VFS_OFFLOAD_READ_RECV(subreq,
+					   state->fsp->conn,
+					   state,
+					   &token);
+	TALLOC_FREE(subreq);
+	if (tevent_req_nterror(req, status)) {
+		return;
+	}
+
+	if (token.length != sizeof(rkey_rsp.resume_key)) {
+		tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+		return;
+	}
+
+	ZERO_STRUCT(rkey_rsp);
+	memcpy(rkey_rsp.resume_key, token.data, token.length);
+
+	ndr_ret = ndr_push_struct_blob(&state->out_output, state, &rkey_rsp,
+			(ndr_push_flags_fn_t)ndr_push_req_resume_key_rsp);
+	if (ndr_ret != NDR_ERR_SUCCESS) {
+		tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+		return;
+	}
+
+	tevent_req_done(req);
+	return;
+}
-- 
2.9.4


From 4f5f67f2bb58a3e068a7a59085557641265551e2 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Sun, 4 Jun 2017 13:50:33 +0200
Subject: [PATCH 07/17] s3/vfs: rename SMB_VFS_COPY_CHUNK_SEND/RECV to
 SMB_VFS_OFFLOAD_WRITE_SEND/RECV

No change in behaviour, just a rename in preperation of more changes to
SMB_VFS_OFFLOAD_WRITE_SEND. It helps keeping the diff of the actual
changes smaller.

Signed-off-by: Ralph Boehme <slow at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 examples/VFS/skel_opaque.c           |  8 ++--
 examples/VFS/skel_transparent.c      | 38 +++++++++---------
 source3/include/vfs.h                | 66 +++++++++++++++---------------
 source3/include/vfs_macros.h         | 18 ++++-----
 source3/modules/vfs_btrfs.c          | 28 ++++++-------
 source3/modules/vfs_default.c        | 78 +++++++++++++++++++-----------------
 source3/modules/vfs_fruit.c          | 46 ++++++++++-----------
 source3/modules/vfs_full_audit.c     | 24 +++++------
 source3/modules/vfs_glusterfs.c      |  2 -
 source3/modules/vfs_time_audit.c     | 45 +++++++++++----------
 source3/smbd/smb2_ioctl_filesys.c    | 22 +++++-----
 source3/smbd/smb2_ioctl_network_fs.c |  6 +--
 source3/smbd/vfs.c                   | 34 ++++++++--------
 13 files changed, 211 insertions(+), 204 deletions(-)

diff --git a/examples/VFS/skel_opaque.c b/examples/VFS/skel_opaque.c
index a7835f3..63008e8 100644
--- a/examples/VFS/skel_opaque.c
+++ b/examples/VFS/skel_opaque.c
@@ -578,7 +578,7 @@ static NTSTATUS skel_offload_read_recv(struct tevent_req *req,
 struct skel_cc_state {
 	uint64_t unused;
 };
-static struct tevent_req *skel_copy_chunk_send(struct vfs_handle_struct *handle,
+static struct tevent_req *skel_offload_write_send(struct vfs_handle_struct *handle,
 					       TALLOC_CTX *mem_ctx,
 					       struct tevent_context *ev,
 					       struct files_struct *src_fsp,
@@ -600,7 +600,7 @@ static struct tevent_req *skel_copy_chunk_send(struct vfs_handle_struct *handle,
 	return tevent_req_post(req, ev);
 }
 
-static NTSTATUS skel_copy_chunk_recv(struct vfs_handle_struct *handle,
+static NTSTATUS skel_offload_write_recv(struct vfs_handle_struct *handle,
 				     struct tevent_req *req,
 				     off_t *copied)
 {
@@ -1007,8 +1007,8 @@ struct vfs_fn_pointers skel_opaque_fns = {
 	.file_id_create_fn = skel_file_id_create,
 	.offload_read_send_fn = skel_offload_read_send,
 	.offload_read_recv_fn = skel_offload_read_recv,
-	.copy_chunk_send_fn = skel_copy_chunk_send,
-	.copy_chunk_recv_fn = skel_copy_chunk_recv,
+	.offload_write_send_fn = skel_offload_write_send,
+	.offload_write_recv_fn = skel_offload_write_recv,
 	.get_compression_fn = skel_get_compression,
 	.set_compression_fn = skel_set_compression,
 
diff --git a/examples/VFS/skel_transparent.c b/examples/VFS/skel_transparent.c
index 9e53870..b39489b 100644
--- a/examples/VFS/skel_transparent.c
+++ b/examples/VFS/skel_transparent.c
@@ -706,13 +706,13 @@ static NTSTATUS skel_offload_read_recv(struct tevent_req *req,
 	return NT_STATUS_OK;
 }
 
-struct skel_cc_state {
+struct skel_offload_write_state {
 	struct vfs_handle_struct *handle;
 	off_t copied;
 };
-static void skel_copy_chunk_done(struct tevent_req *subreq);
+static void skel_offload_write_done(struct tevent_req *subreq);
 
-static struct tevent_req *skel_copy_chunk_send(struct vfs_handle_struct *handle,
+static struct tevent_req *skel_offload_write_send(struct vfs_handle_struct *handle,
 					       TALLOC_CTX *mem_ctx,
 					       struct tevent_context *ev,
 					       struct files_struct *src_fsp,
@@ -724,36 +724,36 @@ static struct tevent_req *skel_copy_chunk_send(struct vfs_handle_struct *handle,
 {
 	struct tevent_req *req;
 	struct tevent_req *subreq;
-	struct skel_cc_state *cc_state;
+	struct skel_offload_write_state *state;
 
-	req = tevent_req_create(mem_ctx, &cc_state, struct skel_cc_state);
+	req = tevent_req_create(mem_ctx, &state, struct skel_offload_write_state);
 	if (req == NULL) {
 		return NULL;
 	}
 
-	cc_state->handle = handle;
-	subreq = SMB_VFS_NEXT_COPY_CHUNK_SEND(handle, cc_state, ev,
+	state->handle = handle;
+	subreq = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle, state, ev,
 					      src_fsp, src_off,
 					      dest_fsp, dest_off, num, flags);
 	if (tevent_req_nomem(subreq, req)) {
 		return tevent_req_post(req, ev);
 	}
 
-	tevent_req_set_callback(subreq, skel_copy_chunk_done, req);
+	tevent_req_set_callback(subreq, skel_offload_write_done, req);
 	return req;
 }
 
-static void skel_copy_chunk_done(struct tevent_req *subreq)
+static void skel_offload_write_done(struct tevent_req *subreq)
 {
 	struct tevent_req *req = tevent_req_callback_data(
 		subreq, struct tevent_req);
-	struct skel_cc_state *cc_state
-			= tevent_req_data(req, struct skel_cc_state);
+	struct skel_offload_write_state *state
+			= tevent_req_data(req, struct skel_offload_write_state);
 	NTSTATUS status;
 
-	status = SMB_VFS_NEXT_COPY_CHUNK_RECV(cc_state->handle,
+	status = SMB_VFS_NEXT_OFFLOAD_WRITE_RECV(state->handle,
 					      subreq,
-					      &cc_state->copied);
+					      &state->copied);
 	TALLOC_FREE(subreq);
 	if (tevent_req_nterror(req, status)) {
 		return;
@@ -761,15 +761,15 @@ static void skel_copy_chunk_done(struct tevent_req *subreq)
 	tevent_req_done(req);
 }
 
-static NTSTATUS skel_copy_chunk_recv(struct vfs_handle_struct *handle,
+static NTSTATUS skel_offload_write_recv(struct vfs_handle_struct *handle,
 				     struct tevent_req *req,
 				     off_t *copied)
 {
-	struct skel_cc_state *cc_state
-			= tevent_req_data(req, struct skel_cc_state);
+	struct skel_offload_write_state *state
+			= tevent_req_data(req, struct skel_offload_write_state);
 	NTSTATUS status;
 
-	*copied = cc_state->copied;
+	*copied = state->copied;
 	if (tevent_req_is_nterror(req, &status)) {
 		tevent_req_received(req);
 		return status;
@@ -1180,8 +1180,8 @@ struct vfs_fn_pointers skel_transparent_fns = {
 	.file_id_create_fn = skel_file_id_create,
 	.offload_read_send_fn = skel_offload_read_send,
 	.offload_read_recv_fn = skel_offload_read_recv,
-	.copy_chunk_send_fn = skel_copy_chunk_send,
-	.copy_chunk_recv_fn = skel_copy_chunk_recv,
+	.offload_write_send_fn = skel_offload_write_send,
+	.offload_write_recv_fn = skel_offload_write_recv,
 	.get_compression_fn = skel_get_compression,
 	.set_compression_fn = skel_set_compression,
 
diff --git a/source3/include/vfs.h b/source3/include/vfs.h
index f935ec6..4adc315 100644
--- a/source3/include/vfs.h
+++ b/source3/include/vfs.h
@@ -229,6 +229,8 @@
 /* Version 37 - Change symlink from const char *
 		to const struct smb_filename * */
 /* Leave at 37 - Add SMB_VFS_OFFLOAD_READ_SEND/RECV */
+/* Leave at 37 - Rename SMB_VFS_COPY_CHUNK_SEND/RECV to
+                 SMB_VFS_OFFLOAD_READ_SEND/RECV */
 
 #define SMB_VFS_INTERFACE_VERSION 37
 
@@ -582,17 +584,17 @@ enum vfs_fallocate_flags {
 };
 
 /*
- * @VFS_COPY_CHUNK_FL_MUST_CLONE: indicates that copy_chunk_send_fn() copy must
+ * @VFS_OFFLOAD_WRITE_FL_MUST_CLONE: indicates that offload_write_send_fn() copy must
  *				  be handled as a COW clone, AKA reflink.
- * @VFS_COPY_CHUNK_FL_MASK_ALL: all valid copychunk flags.
+ * @VFS_OFFLOAD_WRITE_FL_MASK_ALL: all valid flags.
  */
-enum vfs_copy_chunk_flags {
-	VFS_COPY_CHUNK_FL_MUST_CLONE		= 0x0001,
-	VFS_COPY_CHUNK_FL_IGNORE_LOCKS		= 0x0002,
+enum vfs_offload_write_flags {
+	VFS_OFFLOAD_WRITE_FL_MUST_CLONE		= 0x0001,
+	VFS_OFFLOAD_WRITE_FL_IGNORE_LOCKS	= 0x0002,
 
-	VFS_COPY_CHUNK_FL_MASK_ALL		=
-					(VFS_COPY_CHUNK_FL_MUST_CLONE
-					 | VFS_COPY_CHUNK_FL_IGNORE_LOCKS),
+	VFS_OFFLOAD_WRITE_FL_MASK_ALL		=
+					(VFS_OFFLOAD_WRITE_FL_MUST_CLONE
+					 | VFS_OFFLOAD_WRITE_FL_IGNORE_LOCKS),
 };
 
 struct vfs_aio_state {
@@ -780,18 +782,18 @@ struct vfs_fn_pointers {
 					 struct vfs_handle_struct *handle,
 					 TALLOC_CTX *mem_ctx,
 					 DATA_BLOB *token_blob);
-	struct tevent_req *(*copy_chunk_send_fn)(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 to_copy,
-						 uint32_t flags);
-	NTSTATUS (*copy_chunk_recv_fn)(struct vfs_handle_struct *handle,
-				       struct tevent_req *req,
-				       off_t *copied);
+	struct tevent_req *(*offload_write_send_fn)(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 to_copy,
+						    uint32_t flags);
+	NTSTATUS (*offload_write_recv_fn)(struct vfs_handle_struct *handle,
+					  struct tevent_req *req,
+					  off_t *copied);
 	NTSTATUS (*get_compression_fn)(struct vfs_handle_struct *handle,
 				       TALLOC_CTX *mem_ctx,
 				       struct files_struct *fsp,
@@ -1356,18 +1358,18 @@ NTSTATUS smb_vfs_call_offload_read_recv(struct tevent_req *req,
 					struct vfs_handle_struct *handle,
 					TALLOC_CTX *mem_ctx,
 					DATA_BLOB *token_blob);
-struct tevent_req *smb_vfs_call_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,
-						uint32_t flags);
-NTSTATUS smb_vfs_call_copy_chunk_recv(struct vfs_handle_struct *handle,
-				      struct tevent_req *req,
-				      off_t *copied);
+struct tevent_req *smb_vfs_call_offload_write_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,
+						   uint32_t flags);
+NTSTATUS smb_vfs_call_offload_write_recv(struct vfs_handle_struct *handle,
+					 struct tevent_req *req,
+					 off_t *copied);
 NTSTATUS smb_vfs_call_get_compression(struct vfs_handle_struct *handle,
 				      TALLOC_CTX *mem_ctx,
 				      struct files_struct *fsp,
diff --git a/source3/include/vfs_macros.h b/source3/include/vfs_macros.h
index bb97091..eb5886f 100644
--- a/source3/include/vfs_macros.h
+++ b/source3/include/vfs_macros.h
@@ -425,15 +425,15 @@
 #define SMB_VFS_NEXT_OFFLOAD_READ_RECV(req, handle, mem_ctx, token_blob) \
 	smb_vfs_call_offload_read_recv((req), (handle)->next, (mem_ctx), (token_blob))
 
-#define SMB_VFS_COPY_CHUNK_SEND(conn, mem_ctx, ev, src_fsp, src_off, dest_fsp, dest_off, num, flags) \
-	smb_vfs_call_copy_chunk_send((conn)->vfs_handles, (mem_ctx), (ev), (src_fsp), (src_off), (dest_fsp), (dest_off), (num), (flags))
-#define SMB_VFS_NEXT_COPY_CHUNK_SEND(handle, mem_ctx, ev, src_fsp, src_off, dest_fsp, dest_off, num, flags) \
-	smb_vfs_call_copy_chunk_send((handle)->next, (mem_ctx), (ev), (src_fsp), (src_off), (dest_fsp), (dest_off), (num), (flags))
-
-#define SMB_VFS_COPY_CHUNK_RECV(conn, req, copied) \
-	smb_vfs_call_copy_chunk_recv((conn)->vfs_handles, (req), (copied))
-#define SMB_VFS_NEXT_COPY_CHUNK_RECV(handle, req, copied) \
-	smb_vfs_call_copy_chunk_recv((handle)->next, (req), (copied))
+#define SMB_VFS_OFFLOAD_WRITE_SEND(conn, mem_ctx, ev, src_fsp, src_off, dest_fsp, dest_off, num, flags) \
+	smb_vfs_call_offload_write_send((conn)->vfs_handles, (mem_ctx), (ev), (src_fsp), (src_off), (dest_fsp), (dest_off), (num), (flags))
+#define SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle, mem_ctx, ev, src_fsp, src_off, dest_fsp, dest_off, num, flags) \
+	smb_vfs_call_offload_write_send((handle)->next, (mem_ctx), (ev), (src_fsp), (src_off), (dest_fsp), (dest_off), (num), (flags))
+
+#define SMB_VFS_OFFLOAD_WRITE_RECV(conn, req, copied) \
+	smb_vfs_call_offload_write_recv((conn)->vfs_handles, (req), (copied))
+#define SMB_VFS_NEXT_OFFLOAD_WRITE_RECV(handle, req, copied) \
+	smb_vfs_call_offload_write_recv((handle)->next, (req), (copied))
 
 #define SMB_VFS_GET_COMPRESSION(conn, mem_ctx, fsp, smb_fname, _compression_fmt)		\
 	smb_vfs_call_get_compression((conn)->vfs_handles, (mem_ctx), (fsp), (smb_fname), (_compression_fmt))
diff --git a/source3/modules/vfs_btrfs.c b/source3/modules/vfs_btrfs.c
index b175a84..7917537 100644
--- a/source3/modules/vfs_btrfs.c
+++ b/source3/modules/vfs_btrfs.c
@@ -201,9 +201,9 @@ struct btrfs_cc_state {
 	off_t copied;
 	struct tevent_req *subreq;	/* non-null if passed to next VFS fn */
 };
-static void btrfs_copy_chunk_done(struct tevent_req *subreq);
+static void btrfs_offload_write_done(struct tevent_req *subreq);
 
-static struct tevent_req *btrfs_copy_chunk_send(struct vfs_handle_struct *handle,
+static struct tevent_req *btrfs_offload_write_send(struct vfs_handle_struct *handle,
 						TALLOC_CTX *mem_ctx,
 						struct tevent_context *ev,
 						struct files_struct *src_fsp,
@@ -226,7 +226,7 @@ static struct tevent_req *btrfs_copy_chunk_send(struct vfs_handle_struct *handle
 		return NULL;
 	}
 
-	if (flags & ~VFS_COPY_CHUNK_FL_MASK_ALL) {
+	if (flags & ~VFS_OFFLOAD_WRITE_FL_MASK_ALL) {
 		tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
 		return tevent_req_post(req, ev);
 	}
@@ -239,7 +239,7 @@ static struct tevent_req *btrfs_copy_chunk_send(struct vfs_handle_struct *handle
 		 * all data from @src_offset->EOF! This is certainly not what
 		 * the caller expects, and not what vfs_default does.
 		 */
-		cc_state->subreq = SMB_VFS_NEXT_COPY_CHUNK_SEND(handle,
+		cc_state->subreq = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle,
 								cc_state, ev,
 								src_fsp,
 								src_off,
@@ -250,7 +250,7 @@ static struct tevent_req *btrfs_copy_chunk_send(struct vfs_handle_struct *handle
 			return tevent_req_post(req, ev);
 		}
 		tevent_req_set_callback(cc_state->subreq,
-					btrfs_copy_chunk_done,
+					btrfs_offload_write_done,
 					req);
 		return req;
 	}
@@ -271,7 +271,7 @@ static struct tevent_req *btrfs_copy_chunk_send(struct vfs_handle_struct *handle
 		return tevent_req_post(req, ev);
 	}
 
-	if (!(flags & VFS_COPY_CHUNK_FL_IGNORE_LOCKS)) {
+	if (!(flags & VFS_OFFLOAD_WRITE_FL_IGNORE_LOCKS)) {
 		init_strict_lock_struct(src_fsp,
 					src_fsp->op->global->open_persistent_id,
 					src_off,
@@ -303,7 +303,7 @@ static struct tevent_req *btrfs_copy_chunk_send(struct vfs_handle_struct *handle
 	cr_args.src_length = (uint64_t)num;
 
 	ret = ioctl(dest_fsp->fh->fd, BTRFS_IOC_CLONE_RANGE, &cr_args);
-	if (!(flags & VFS_COPY_CHUNK_FL_IGNORE_LOCKS)) {
+	if (!(flags & VFS_OFFLOAD_WRITE_FL_IGNORE_LOCKS)) {
 		SMB_VFS_STRICT_UNLOCK(dest_fsp->conn, dest_fsp, &dest_lck);
 		SMB_VFS_STRICT_UNLOCK(src_fsp->conn, src_fsp, &src_lck);
 	}
@@ -321,7 +321,7 @@ static struct tevent_req *btrfs_copy_chunk_send(struct vfs_handle_struct *handle
 			  (unsigned long long)cr_args.src_offset,
 			  dest_fsp->fh->fd,
 			  (unsigned long long)cr_args.dest_offset));
-		cc_state->subreq = SMB_VFS_NEXT_COPY_CHUNK_SEND(handle,
+		cc_state->subreq = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle,
 								cc_state, ev,
 								src_fsp,
 								src_off,
@@ -333,7 +333,7 @@ static struct tevent_req *btrfs_copy_chunk_send(struct vfs_handle_struct *handle
 		}
 		/* wait for subreq completion */
 		tevent_req_set_callback(cc_state->subreq,
-					btrfs_copy_chunk_done,
+					btrfs_offload_write_done,
 					req);
 		return req;
 	}
@@ -346,7 +346,7 @@ static struct tevent_req *btrfs_copy_chunk_send(struct vfs_handle_struct *handle
 }
 
 /* only used if the request is passed through to next VFS module */
-static void btrfs_copy_chunk_done(struct tevent_req *subreq)
+static void btrfs_offload_write_done(struct tevent_req *subreq)
 {
 	struct tevent_req *req = tevent_req_callback_data(
 		subreq, struct tevent_req);
@@ -354,7 +354,7 @@ static void btrfs_copy_chunk_done(struct tevent_req *subreq)
 							struct btrfs_cc_state);
 	NTSTATUS status;
 
-	status = SMB_VFS_NEXT_COPY_CHUNK_RECV(cc_state->handle,
+	status = SMB_VFS_NEXT_OFFLOAD_WRITE_RECV(cc_state->handle,
 					      cc_state->subreq,
 					      &cc_state->copied);
 	if (tevent_req_nterror(req, status)) {
@@ -363,7 +363,7 @@ static void btrfs_copy_chunk_done(struct tevent_req *subreq)
 	tevent_req_done(req);
 }
 
-static NTSTATUS btrfs_copy_chunk_recv(struct vfs_handle_struct *handle,
+static NTSTATUS btrfs_offload_write_recv(struct vfs_handle_struct *handle,
 				      struct tevent_req *req,
 				      off_t *copied)
 {
@@ -800,8 +800,8 @@ static struct vfs_fn_pointers btrfs_fns = {
 	.fs_capabilities_fn = btrfs_fs_capabilities,
 	.offload_read_send_fn = btrfs_offload_read_send,
 	.offload_read_recv_fn = btrfs_offload_read_recv,
-	.copy_chunk_send_fn = btrfs_copy_chunk_send,
-	.copy_chunk_recv_fn = btrfs_copy_chunk_recv,
+	.offload_write_send_fn = btrfs_offload_write_send,
+	.offload_write_recv_fn = btrfs_offload_write_recv,
 	.get_compression_fn = btrfs_get_compression,
 	.set_compression_fn = btrfs_set_compression,
 	.snap_check_path_fn = btrfs_snap_check_path,
diff --git a/source3/modules/vfs_default.c b/source3/modules/vfs_default.c
index 5b99884..c7b114e 100644
--- a/source3/modules/vfs_default.c
+++ b/source3/modules/vfs_default.c
@@ -1674,7 +1674,7 @@ static NTSTATUS vfswrap_offload_read_recv(struct tevent_req *req,
 	return NT_STATUS_OK;
 }
 
-struct vfs_cc_state {
+struct vfswrap_offload_write_state {
 	struct tevent_context *ev;
 	uint8_t *buf;
 	bool read_lck_locked;
@@ -1691,42 +1691,44 @@ struct vfs_cc_state {
 	uint32_t flags;
 };
 
-static NTSTATUS copy_chunk_loop(struct tevent_req *req);
+static NTSTATUS vfswrap_offload_write_loop(struct tevent_req *req);
 
-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 to_copy,
-						  uint32_t flags)
+static struct tevent_req *vfswrap_offload_write_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 to_copy,
+	uint32_t flags)
 {
 	struct tevent_req *req;
-	struct vfs_cc_state *state = NULL;
+	struct vfswrap_offload_write_state *state = NULL;
 	size_t num = MIN(to_copy, COPYCHUNK_MAX_TOTAL_LEN);
 	NTSTATUS status;
 
 	DBG_DEBUG("server side copy chunk of length %" PRIu64 "\n", to_copy);
 
-	req = tevent_req_create(mem_ctx, &state, struct vfs_cc_state);
+	req = tevent_req_create(mem_ctx, &state,
+				struct vfswrap_offload_write_state);
 	if (req == NULL) {
 		return NULL;
 	}
 
-	if (flags & ~VFS_COPY_CHUNK_FL_MASK_ALL) {
+	if (flags & ~VFS_OFFLOAD_WRITE_FL_MASK_ALL) {
 		tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
 		return tevent_req_post(req, ev);
 	}
 
-	if (flags & VFS_COPY_CHUNK_FL_MUST_CLONE) {
+	if (flags & VFS_OFFLOAD_WRITE_FL_MUST_CLONE) {
 		DEBUG(10, ("COW clones not supported by vfs_default\n"));
 		tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
 		return tevent_req_post(req, ev);
 	}
 
-	*state = (struct vfs_cc_state) {
+	*state = (struct vfswrap_offload_write_state) {
 		.ev = ev,
 		.src_fsp = src_fsp,
 		.src_off = src_off,
@@ -1770,7 +1772,7 @@ static struct tevent_req *vfswrap_copy_chunk_send(struct vfs_handle_struct *hand
 		return tevent_req_post(req, ev);
 	}
 
-	status = copy_chunk_loop(req);
+	status = vfswrap_offload_write_loop(req);
 	if (!NT_STATUS_IS_OK(status)) {
 		tevent_req_nterror(req, status);
 		return tevent_req_post(req, ev);
@@ -1779,17 +1781,18 @@ static struct tevent_req *vfswrap_copy_chunk_send(struct vfs_handle_struct *hand
 	return req;
 }
 
-static void vfswrap_copy_chunk_read_done(struct tevent_req *subreq);
+static void vfswrap_offload_write_read_done(struct tevent_req *subreq);
 
-static NTSTATUS copy_chunk_loop(struct tevent_req *req)
+static NTSTATUS vfswrap_offload_write_loop(struct tevent_req *req)
 {
-	struct vfs_cc_state *state = tevent_req_data(req, struct vfs_cc_state);
+	struct vfswrap_offload_write_state *state = tevent_req_data(
+		req, struct vfswrap_offload_write_state);
 	struct tevent_req *subreq = NULL;
 	bool ok;
 
 	state->next_io_size = MIN(state->remaining, talloc_array_length(state->buf));
 
-	if (!(state->flags & VFS_COPY_CHUNK_FL_IGNORE_LOCKS)) {
+	if (!(state->flags & VFS_OFFLOAD_WRITE_FL_IGNORE_LOCKS)) {
 		init_strict_lock_struct(state->src_fsp,
 				state->src_fsp->op->global->open_persistent_id,
 					state->src_off,
@@ -1814,23 +1817,24 @@ static NTSTATUS copy_chunk_loop(struct tevent_req *req)
 	if (subreq == NULL) {
 		return NT_STATUS_NO_MEMORY;
 	}
-	tevent_req_set_callback(subreq, vfswrap_copy_chunk_read_done, req);
+	tevent_req_set_callback(subreq, vfswrap_offload_write_read_done, req);
 
 	return NT_STATUS_OK;
 }
 
-static void vfswrap_copy_chunk_write_done(struct tevent_req *subreq);
+static void vfswrap_offload_write_write_done(struct tevent_req *subreq);
 
-static void vfswrap_copy_chunk_read_done(struct tevent_req *subreq)
+static void vfswrap_offload_write_read_done(struct tevent_req *subreq)
 {
 	struct tevent_req *req = tevent_req_callback_data(
 		subreq, struct tevent_req);
-	struct vfs_cc_state *state = tevent_req_data(req, struct vfs_cc_state);
+	struct vfswrap_offload_write_state *state = tevent_req_data(
+		req, struct vfswrap_offload_write_state);
 	struct vfs_aio_state aio_state;
 	ssize_t nread;
 	bool ok;
 
-	if (!(state->flags & VFS_COPY_CHUNK_FL_IGNORE_LOCKS)) {
+	if (!(state->flags & VFS_OFFLOAD_WRITE_FL_IGNORE_LOCKS)) {
 		SMB_VFS_STRICT_UNLOCK(state->src_fsp->conn,
 				      state->src_fsp,
 				      &state->read_lck);
@@ -1853,7 +1857,7 @@ static void vfswrap_copy_chunk_read_done(struct tevent_req *subreq)
 
 	state->src_off += nread;
 
-	if (!(state->flags & VFS_COPY_CHUNK_FL_IGNORE_LOCKS)) {
+	if (!(state->flags & VFS_OFFLOAD_WRITE_FL_IGNORE_LOCKS)) {
 		init_strict_lock_struct(state->dst_fsp,
 				state->dst_fsp->op->global->open_persistent_id,
 					state->dst_off,
@@ -1880,19 +1884,20 @@ static void vfswrap_copy_chunk_read_done(struct tevent_req *subreq)
 		tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
 		return;
 	}
-	tevent_req_set_callback(subreq, vfswrap_copy_chunk_write_done, req);
+	tevent_req_set_callback(subreq, vfswrap_offload_write_write_done, req);
 }
 
-static void vfswrap_copy_chunk_write_done(struct tevent_req *subreq)
+static void vfswrap_offload_write_write_done(struct tevent_req *subreq)
 {
 	struct tevent_req *req = tevent_req_callback_data(
 		subreq, struct tevent_req);
-	struct vfs_cc_state *state = tevent_req_data(req, struct vfs_cc_state);
+	struct vfswrap_offload_write_state *state = tevent_req_data(
+		req, struct vfswrap_offload_write_state);
 	struct vfs_aio_state aio_state;
 	ssize_t nwritten;
 	NTSTATUS status;
 
-	if (!(state->flags & VFS_COPY_CHUNK_FL_IGNORE_LOCKS)) {
+	if (!(state->flags & VFS_OFFLOAD_WRITE_FL_IGNORE_LOCKS)) {
 		SMB_VFS_STRICT_UNLOCK(state->dst_fsp->conn,
 				      state->dst_fsp,
 				      &state->write_lck);
@@ -1925,7 +1930,7 @@ static void vfswrap_copy_chunk_write_done(struct tevent_req *subreq)
 		return;
 	}
 
-	status = copy_chunk_loop(req);
+	status = vfswrap_offload_write_loop(req);
 	if (!NT_STATUS_IS_OK(status)) {
 		tevent_req_nterror(req, status);
 		return;
@@ -1934,11 +1939,12 @@ static void vfswrap_copy_chunk_write_done(struct tevent_req *subreq)
 	return;
 }
 
-static NTSTATUS vfswrap_copy_chunk_recv(struct vfs_handle_struct *handle,
+static NTSTATUS vfswrap_offload_write_recv(struct vfs_handle_struct *handle,
 					struct tevent_req *req,
 					off_t *copied)
 {
-	struct vfs_cc_state *state = tevent_req_data(req, struct vfs_cc_state);
+	struct vfswrap_offload_write_state *state = tevent_req_data(
+		req, struct vfswrap_offload_write_state);
 	NTSTATUS status;
 
 	if (tevent_req_is_nterror(req, &status)) {
@@ -3053,8 +3059,8 @@ static struct vfs_fn_pointers vfs_default_fns = {
 	.fget_dos_attributes_fn = vfswrap_fget_dos_attributes,
 	.offload_read_send_fn = vfswrap_offload_read_send,
 	.offload_read_recv_fn = vfswrap_offload_read_recv,
-	.copy_chunk_send_fn = vfswrap_copy_chunk_send,
-	.copy_chunk_recv_fn = vfswrap_copy_chunk_recv,
+	.offload_write_send_fn = vfswrap_offload_write_send,
+	.offload_write_recv_fn = vfswrap_offload_write_recv,
 	.get_compression_fn = vfswrap_get_compression,
 	.set_compression_fn = vfswrap_set_compression,
 
diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c
index 20d15b3..279366c 100644
--- a/source3/modules/vfs_fruit.c
+++ b/source3/modules/vfs_fruit.c
@@ -5473,7 +5473,7 @@ static NTSTATUS fruit_offload_read_recv(struct tevent_req *req,
 	return NT_STATUS_OK;
 }
 
-struct fruit_copy_chunk_state {
+struct fruit_offload_write_state {
 	struct vfs_handle_struct *handle;
 	off_t copied;
 	struct files_struct *src_fsp;
@@ -5481,8 +5481,8 @@ struct fruit_copy_chunk_state {
 	bool is_copyfile;
 };
 
-static void fruit_copy_chunk_done(struct tevent_req *subreq);
-static struct tevent_req *fruit_copy_chunk_send(struct vfs_handle_struct *handle,
+static void fruit_offload_write_done(struct tevent_req *subreq);
+static struct tevent_req *fruit_offload_write_send(struct vfs_handle_struct *handle,
 						TALLOC_CTX *mem_ctx,
 						struct tevent_context *ev,
 						struct files_struct *src_fsp,
@@ -5493,7 +5493,7 @@ static struct tevent_req *fruit_copy_chunk_send(struct vfs_handle_struct *handle
 						uint32_t flags)
 {
 	struct tevent_req *req, *subreq;
-	struct fruit_copy_chunk_state *fruit_copy_chunk_state;
+	struct fruit_offload_write_state *state;
 	NTSTATUS status;
 	struct fruit_config_data *config;
 	off_t to_copy = num;
@@ -5505,19 +5505,19 @@ static struct tevent_req *fruit_copy_chunk_send(struct vfs_handle_struct *handle
 				struct fruit_config_data,
 				return NULL);
 
-	req = tevent_req_create(mem_ctx, &fruit_copy_chunk_state,
-				struct fruit_copy_chunk_state);
+	req = tevent_req_create(mem_ctx, &state,
+				struct fruit_offload_write_state);
 	if (req == NULL) {
 		return NULL;
 	}
-	fruit_copy_chunk_state->handle = handle;
-	fruit_copy_chunk_state->src_fsp = src_fsp;
-	fruit_copy_chunk_state->dst_fsp = dest_fsp;
+	state->handle = handle;
+	state->src_fsp = src_fsp;
+	state->dst_fsp = dest_fsp;
 
 	/*
 	 * Check if this a OS X copyfile style copychunk request with
 	 * a requested chunk count of 0 that was translated to a
-	 * copy_chunk_send VFS call overloading the parameters src_off
+	 * offload_write_send VFS call overloading the parameters src_off
 	 * = dest_off = num = 0.
 	 */
 	if ((src_off == 0) && (dest_off == 0) && (num == 0) &&
@@ -5530,10 +5530,10 @@ static struct tevent_req *fruit_copy_chunk_send(struct vfs_handle_struct *handle
 		}
 
 		to_copy = src_fsp->fsp_name->st.st_ex_size;
-		fruit_copy_chunk_state->is_copyfile = true;
+		state->is_copyfile = true;
 	}
 
-	subreq = SMB_VFS_NEXT_COPY_CHUNK_SEND(handle,
+	subreq = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle,
 					      mem_ctx,
 					      ev,
 					      src_fsp,
@@ -5546,16 +5546,16 @@ static struct tevent_req *fruit_copy_chunk_send(struct vfs_handle_struct *handle
 		return tevent_req_post(req, ev);
 	}
 
-	tevent_req_set_callback(subreq, fruit_copy_chunk_done, req);
+	tevent_req_set_callback(subreq, fruit_offload_write_done, req);
 	return req;
 }
 
-static void fruit_copy_chunk_done(struct tevent_req *subreq)
+static void fruit_offload_write_done(struct tevent_req *subreq)
 {
 	struct tevent_req *req = tevent_req_callback_data(
 		subreq, struct tevent_req);
-	struct fruit_copy_chunk_state *state = tevent_req_data(
-		req, struct fruit_copy_chunk_state);
+	struct fruit_offload_write_state *state = tevent_req_data(
+		req, struct fruit_offload_write_state);
 	NTSTATUS status;
 	unsigned int num_streams = 0;
 	struct stream_struct *streams = NULL;
@@ -5563,7 +5563,7 @@ static void fruit_copy_chunk_done(struct tevent_req *subreq)
 	struct smb_filename *src_fname_tmp = NULL;
 	struct smb_filename *dst_fname_tmp = NULL;
 
-	status = SMB_VFS_NEXT_COPY_CHUNK_RECV(state->handle,
+	status = SMB_VFS_NEXT_OFFLOAD_WRITE_RECV(state->handle,
 					      subreq,
 					      &state->copied);
 	TALLOC_FREE(subreq);
@@ -5651,12 +5651,12 @@ static void fruit_copy_chunk_done(struct tevent_req *subreq)
 	tevent_req_done(req);
 }
 
-static NTSTATUS fruit_copy_chunk_recv(struct vfs_handle_struct *handle,
+static NTSTATUS fruit_offload_write_recv(struct vfs_handle_struct *handle,
 				      struct tevent_req *req,
 				      off_t *copied)
 {
-	struct fruit_copy_chunk_state *fruit_copy_chunk_state = tevent_req_data(
-		req, struct fruit_copy_chunk_state);
+	struct fruit_offload_write_state *state = tevent_req_data(
+		req, struct fruit_offload_write_state);
 	NTSTATUS status;
 
 	if (tevent_req_is_nterror(req, &status)) {
@@ -5667,7 +5667,7 @@ static NTSTATUS fruit_copy_chunk_recv(struct vfs_handle_struct *handle,
 		return status;
 	}
 
-	*copied = fruit_copy_chunk_state->copied;
+	*copied = state->copied;
 	tevent_req_received(req);
 
 	return NT_STATUS_OK;
@@ -5700,8 +5700,8 @@ static struct vfs_fn_pointers vfs_fruit_fns = {
 	.readdir_attr_fn = fruit_readdir_attr,
 	.offload_read_send_fn = fruit_offload_read_send,
 	.offload_read_recv_fn = fruit_offload_read_recv,
-	.copy_chunk_send_fn = fruit_copy_chunk_send,
-	.copy_chunk_recv_fn = fruit_copy_chunk_recv,
+	.offload_write_send_fn = fruit_offload_write_send,
+	.offload_write_recv_fn = fruit_offload_write_recv,
 
 	/* NT ACL operations */
 	.fget_nt_acl_fn = fruit_fget_nt_acl,
diff --git a/source3/modules/vfs_full_audit.c b/source3/modules/vfs_full_audit.c
index f548457..dba508e 100644
--- a/source3/modules/vfs_full_audit.c
+++ b/source3/modules/vfs_full_audit.c
@@ -170,8 +170,8 @@ typedef enum _vfs_op_type {
 	SMB_VFS_OP_FSCTL,
 	SMB_VFS_OP_OFFLOAD_READ_SEND,
 	SMB_VFS_OP_OFFLOAD_READ_RECV,
-	SMB_VFS_OP_COPY_CHUNK_SEND,
-	SMB_VFS_OP_COPY_CHUNK_RECV,
+	SMB_VFS_OP_OFFLOAD_WRITE_SEND,
+	SMB_VFS_OP_OFFLOAD_WRITE_RECV,
 	SMB_VFS_OP_GET_COMPRESSION,
 	SMB_VFS_OP_SET_COMPRESSION,
 	SMB_VFS_OP_SNAP_CHECK_PATH,
@@ -314,8 +314,8 @@ static struct {
 	{ SMB_VFS_OP_FSCTL,		"fsctl" },
 	{ SMB_VFS_OP_OFFLOAD_READ_SEND,	"offload_read_send" },
 	{ SMB_VFS_OP_OFFLOAD_READ_RECV,	"offload_read_recv" },
-	{ SMB_VFS_OP_COPY_CHUNK_SEND,	"copy_chunk_send" },
-	{ SMB_VFS_OP_COPY_CHUNK_RECV,	"copy_chunk_recv" },
+	{ SMB_VFS_OP_OFFLOAD_WRITE_SEND,	"offload_write_send" },
+	{ SMB_VFS_OP_OFFLOAD_WRITE_RECV,	"offload_write_recv" },
 	{ SMB_VFS_OP_GET_COMPRESSION,	"get_compression" },
 	{ SMB_VFS_OP_SET_COMPRESSION,	"set_compression" },
 	{ SMB_VFS_OP_SNAP_CHECK_PATH, "snap_check_path" },
@@ -1937,7 +1937,7 @@ static NTSTATUS smb_full_audit_offload_read_recv(
 	return status;
 }
 
-static struct tevent_req *smb_full_audit_copy_chunk_send(struct vfs_handle_struct *handle,
+static struct tevent_req *smb_full_audit_offload_write_send(struct vfs_handle_struct *handle,
 							 TALLOC_CTX *mem_ctx,
 							 struct tevent_context *ev,
 							 struct files_struct *src_fsp,
@@ -1949,24 +1949,24 @@ static struct tevent_req *smb_full_audit_copy_chunk_send(struct vfs_handle_struc
 {
 	struct tevent_req *req;
 
-	req = SMB_VFS_NEXT_COPY_CHUNK_SEND(handle, mem_ctx, ev, src_fsp,
+	req = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle, mem_ctx, ev, src_fsp,
 					   src_off, dest_fsp, dest_off, num,
 					   flags);
 
-	do_log(SMB_VFS_OP_COPY_CHUNK_SEND, req, handle, "");
+	do_log(SMB_VFS_OP_OFFLOAD_WRITE_SEND, req, handle, "");
 
 	return req;
 }
 
-static NTSTATUS smb_full_audit_copy_chunk_recv(struct vfs_handle_struct *handle,
+static NTSTATUS smb_full_audit_offload_write_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);
+	result = SMB_VFS_NEXT_OFFLOAD_WRITE_RECV(handle, req, copied);
 
-	do_log(SMB_VFS_OP_COPY_CHUNK_RECV, NT_STATUS_IS_OK(result), handle, "");
+	do_log(SMB_VFS_OP_OFFLOAD_WRITE_RECV, NT_STATUS_IS_OK(result), handle, "");
 
 	return result;
 }
@@ -2572,8 +2572,8 @@ static struct vfs_fn_pointers vfs_full_audit_fns = {
 	.file_id_create_fn = smb_full_audit_file_id_create,
 	.offload_read_send_fn = smb_full_audit_offload_read_send,
 	.offload_read_recv_fn = smb_full_audit_offload_read_recv,
-	.copy_chunk_send_fn = smb_full_audit_copy_chunk_send,
-	.copy_chunk_recv_fn = smb_full_audit_copy_chunk_recv,
+	.offload_write_send_fn = smb_full_audit_offload_write_send,
+	.offload_write_recv_fn = smb_full_audit_offload_write_recv,
 	.get_compression_fn = smb_full_audit_get_compression,
 	.set_compression_fn = smb_full_audit_set_compression,
 	.snap_check_path_fn =  smb_full_audit_snap_check_path,
diff --git a/source3/modules/vfs_glusterfs.c b/source3/modules/vfs_glusterfs.c
index 2528959..dfac2b5 100644
--- a/source3/modules/vfs_glusterfs.c
+++ b/source3/modules/vfs_glusterfs.c
@@ -1459,8 +1459,6 @@ static struct vfs_fn_pointers glusterfs_fns = {
 	.realpath_fn = vfs_gluster_realpath,
 	.chflags_fn = vfs_gluster_chflags,
 	.file_id_create_fn = NULL,
-	.copy_chunk_send_fn = NULL,
-	.copy_chunk_recv_fn = NULL,
 	.streaminfo_fn = NULL,
 	.get_real_filename_fn = vfs_gluster_get_real_filename,
 	.connectpath_fn = vfs_gluster_connectpath,
diff --git a/source3/modules/vfs_time_audit.c b/source3/modules/vfs_time_audit.c
index 7fe9912..581679d 100644
--- a/source3/modules/vfs_time_audit.c
+++ b/source3/modules/vfs_time_audit.c
@@ -1988,14 +1988,14 @@ static NTSTATUS smb_time_audit_offload_read_recv(
 	return NT_STATUS_OK;
 }
 
-struct time_audit_cc_state {
+struct time_audit_offload_write_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 void smb_time_audit_offload_write_done(struct tevent_req *subreq);
 
-static struct tevent_req *smb_time_audit_copy_chunk_send(struct vfs_handle_struct *handle,
+static struct tevent_req *smb_time_audit_offload_write_send(struct vfs_handle_struct *handle,
 							 TALLOC_CTX *mem_ctx,
 							 struct tevent_context *ev,
 							 struct files_struct *src_fsp,
@@ -2007,37 +2007,38 @@ static struct tevent_req *smb_time_audit_copy_chunk_send(struct vfs_handle_struc
 {
 	struct tevent_req *req;
 	struct tevent_req *subreq;
-	struct time_audit_cc_state *cc_state;
+	struct time_audit_offload_write_state *state;
 
-	req = tevent_req_create(mem_ctx, &cc_state, struct time_audit_cc_state);
+	req = tevent_req_create(mem_ctx, &state,
+				struct time_audit_offload_write_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,
+	state->handle = handle;
+	clock_gettime_mono(&state->ts_send);
+	subreq = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle, state, ev,
 					      src_fsp, src_off,
 					      dest_fsp, dest_off, num, flags);
 	if (tevent_req_nomem(subreq, req)) {
 		return tevent_req_post(req, ev);
 	}
 
-	tevent_req_set_callback(subreq, smb_time_audit_copy_chunk_done, req);
+	tevent_req_set_callback(subreq, smb_time_audit_offload_write_done, req);
 	return req;
 }
 
-static void smb_time_audit_copy_chunk_done(struct tevent_req *subreq)
+static void smb_time_audit_offload_write_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);
+	struct time_audit_offload_write_state *state = tevent_req_data(
+		req, struct time_audit_offload_write_state);
 	NTSTATUS status;
 
-	status = SMB_VFS_NEXT_COPY_CHUNK_RECV(cc_state->handle,
+	status = SMB_VFS_NEXT_OFFLOAD_WRITE_RECV(state->handle,
 					      subreq,
-					      &cc_state->copied);
+					      &state->copied);
 	TALLOC_FREE(subreq);
 	if (tevent_req_nterror(req, status)) {
 		return;
@@ -2045,23 +2046,23 @@ static void smb_time_audit_copy_chunk_done(struct tevent_req *subreq)
 	tevent_req_done(req);
 }
 
-static NTSTATUS smb_time_audit_copy_chunk_recv(struct vfs_handle_struct *handle,
+static NTSTATUS smb_time_audit_offload_write_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 time_audit_offload_write_state *state = tevent_req_data(
+		req, struct time_audit_offload_write_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;
+	timediff = nsec_time_diff(&ts_recv, &state->ts_send)*1.0e-9;
 	if (timediff > audit_timeout) {
-		smb_time_audit_log("copy_chunk", timediff);
+		smb_time_audit_log("offload_write", timediff);
 	}
 
-	*copied = cc_state->copied;
+	*copied = state->copied;
 	if (tevent_req_is_nterror(req, &status)) {
 		tevent_req_received(req);
 		return status;
@@ -2764,8 +2765,8 @@ static struct vfs_fn_pointers vfs_time_audit_fns = {
 	.file_id_create_fn = smb_time_audit_file_id_create,
 	.offload_read_send_fn = smb_time_audit_offload_read_send,
 	.offload_read_recv_fn = smb_time_audit_offload_read_recv,
-	.copy_chunk_send_fn = smb_time_audit_copy_chunk_send,
-	.copy_chunk_recv_fn = smb_time_audit_copy_chunk_recv,
+	.offload_write_send_fn = smb_time_audit_offload_write_send,
+	.offload_write_recv_fn = smb_time_audit_offload_write_recv,
 	.get_compression_fn = smb_time_audit_get_compression,
 	.set_compression_fn = smb_time_audit_set_compression,
 	.snap_check_path_fn = smb_time_audit_snap_check_path,
diff --git a/source3/smbd/smb2_ioctl_filesys.c b/source3/smbd/smb2_ioctl_filesys.c
index 7008a47..5bbeada 100644
--- a/source3/smbd/smb2_ioctl_filesys.c
+++ b/source3/smbd/smb2_ioctl_filesys.c
@@ -289,16 +289,16 @@ static void fsctl_dup_extents_offload_read_done(struct tevent_req *subreq)
 	}
 
 	/* tell the VFS to ignore locks across the clone, matching ReFS */
-	subreq = SMB_VFS_COPY_CHUNK_SEND(state->dst_fsp->conn,
-					 state,
-					 state->ev,
-					 state->src_fsp,
-					 state->dup_extents.source_off,
-					 state->dst_fsp,
-					 state->dup_extents.target_off,
-					 state->dup_extents.byte_count,
-					 VFS_COPY_CHUNK_FL_MUST_CLONE
-					 | VFS_COPY_CHUNK_FL_IGNORE_LOCKS);
+	subreq = SMB_VFS_OFFLOAD_WRITE_SEND(state->dst_fsp->conn,
+					    state,
+					    state->ev,
+					    state->src_fsp,
+					    state->dup_extents.source_off,
+					    state->dst_fsp,
+					    state->dup_extents.target_off,
+					    state->dup_extents.byte_count,
+					    VFS_OFFLOAD_WRITE_FL_MUST_CLONE
+					    | VFS_OFFLOAD_WRITE_FL_IGNORE_LOCKS);
 	if (tevent_req_nomem(subreq, req)) {
 		return;
 	}
@@ -315,7 +315,7 @@ static void fsctl_dup_extents_vfs_done(struct tevent_req *subreq)
 	off_t nb_chunk;
 	NTSTATUS status;
 
-	status = SMB_VFS_COPY_CHUNK_RECV(state->conn, subreq, &nb_chunk);
+	status = SMB_VFS_OFFLOAD_WRITE_RECV(state->conn, subreq, &nb_chunk);
 	TALLOC_FREE(subreq);
 	if (tevent_req_nterror(req, status)) {
 		return;
diff --git a/source3/smbd/smb2_ioctl_network_fs.c b/source3/smbd/smb2_ioctl_network_fs.c
index acb17c3..82432ba 100644
--- a/source3/smbd/smb2_ioctl_network_fs.c
+++ b/source3/smbd/smb2_ioctl_network_fs.c
@@ -340,7 +340,7 @@ static NTSTATUS fsctl_srv_copychunk_loop(struct tevent_req *req)
 		}
 		state->aapl_copyfile = true;
 
-		subreq = SMB_VFS_COPY_CHUNK_SEND(state->dst_fsp->conn,
+		subreq = SMB_VFS_OFFLOAD_WRITE_SEND(state->dst_fsp->conn,
 						 state,
 						 state->ev,
 						 state->src_fsp,
@@ -360,7 +360,7 @@ static NTSTATUS fsctl_srv_copychunk_loop(struct tevent_req *req)
 	chunk = &state->cc_copy.chunks[state->current_chunk];
 	length = next_merged_io(state);
 
-	subreq = SMB_VFS_COPY_CHUNK_SEND(state->dst_fsp->conn,
+	subreq = SMB_VFS_OFFLOAD_WRITE_SEND(state->dst_fsp->conn,
 					 state,
 					 state->ev,
 					 state->src_fsp,
@@ -386,7 +386,7 @@ static void fsctl_srv_copychunk_vfs_done(struct tevent_req *subreq)
 	off_t chunk_nwritten;
 	NTSTATUS status;
 
-	status = SMB_VFS_COPY_CHUNK_RECV(state->conn, subreq,
+	status = SMB_VFS_OFFLOAD_WRITE_RECV(state->conn, subreq,
 					 &chunk_nwritten);
 	TALLOC_FREE(subreq);
 	if (!NT_STATUS_IS_OK(status)) {
diff --git a/source3/smbd/vfs.c b/source3/smbd/vfs.c
index 938013a..7bc8aa7 100644
--- a/source3/smbd/vfs.c
+++ b/source3/smbd/vfs.c
@@ -2335,28 +2335,28 @@ NTSTATUS smb_vfs_call_offload_read_recv(struct tevent_req *req,
 	return handle->fns->offload_read_recv_fn(req, handle, mem_ctx, token_blob);
 }
 
-struct tevent_req *smb_vfs_call_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,
-						uint32_t flags)
-{
-	VFS_FIND(copy_chunk_send);
-	return handle->fns->copy_chunk_send_fn(handle, mem_ctx, ev, src_fsp,
+struct tevent_req *smb_vfs_call_offload_write_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,
+						   uint32_t flags)
+{
+	VFS_FIND(offload_write_send);
+	return handle->fns->offload_write_send_fn(handle, mem_ctx, ev, src_fsp,
 					       src_off, dest_fsp, dest_off, num,
 					       flags);
 }
 
-NTSTATUS smb_vfs_call_copy_chunk_recv(struct vfs_handle_struct *handle,
-				      struct tevent_req *req,
-				      off_t *copied)
+NTSTATUS smb_vfs_call_offload_write_recv(struct vfs_handle_struct *handle,
+					 struct tevent_req *req,
+					 off_t *copied)
 {
-	VFS_FIND(copy_chunk_recv);
-	return handle->fns->copy_chunk_recv_fn(handle, req, copied);
+	VFS_FIND(offload_write_recv);
+	return handle->fns->offload_write_recv_fn(handle, req, copied);
 }
 
 NTSTATUS smb_vfs_call_get_compression(vfs_handle_struct *handle,
-- 
2.9.4


From 52f19457d633ed96fcc455d879c86b805267bb64 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Fri, 9 Jun 2017 12:57:03 +0200
Subject: [PATCH 08/17] s3/smbd: remove ununsed req arg from CHECK_READ_IOCTL
 macro

Signed-off-by: Ralph Boehme <slow at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 source3/include/smb_macros.h         | 2 +-
 source3/smbd/smb2_ioctl_network_fs.c | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/source3/include/smb_macros.h b/source3/include/smb_macros.h
index f8656c7..de39bf6 100644
--- a/source3/include/smb_macros.h
+++ b/source3/include/smb_macros.h
@@ -62,7 +62,7 @@
  * ). On Windows servers, this is done by the IO manager, which is unaware of
  * the "if execute is granted then also grant read" arrangement.
  */
-#define CHECK_READ_IOCTL(fsp, req) (((fsp)->fh->fd != -1) && ((fsp)->can_read))
+#define CHECK_READ_IOCTL(fsp) (((fsp)->fh->fd != -1) && ((fsp)->can_read))
 
 #define CHECK_WRITE(fsp) ((fsp)->can_write && ((fsp)->fh->fd != -1))
 
diff --git a/source3/smbd/smb2_ioctl_network_fs.c b/source3/smbd/smb2_ioctl_network_fs.c
index 82432ba..180fac2 100644
--- a/source3/smbd/smb2_ioctl_network_fs.c
+++ b/source3/smbd/smb2_ioctl_network_fs.c
@@ -116,7 +116,7 @@ static NTSTATUS copychunk_check_handles(uint32_t ctl_code,
 	 *   FILE_READ_DATA, and the CtlCode is FSCTL_SRV_COPYCHUNK.
 	 */
 	if ((ctl_code == FSCTL_SRV_COPYCHUNK) &&
-	    !CHECK_READ_IOCTL(dst_fsp, smb1req)) {
+	    !CHECK_READ_IOCTL(dst_fsp)) {
 		DEBUG(5, ("copy chunk no read on dest handle (%s).\n",
 			smb_fname_str_dbg(dst_fsp->fsp_name) ));
 		return NT_STATUS_ACCESS_DENIED;
-- 
2.9.4


From 4c5275bae85b6d0eabb1fd3993b70fc825010b03 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Fri, 9 Jun 2017 13:02:49 +0200
Subject: [PATCH 09/17] s3/smbd: remove flags2 FLAGS2_READ_PERMIT_EXECUTE hack
 in the SMB2 code

By adding a SMB2 specific CHECK_READ_SMB2 macro called that always
grants read access if execute was granted, we can get rid of the flags2
hack.

All callers in the SMB2 code are converted to use the CHECK_READ_SMB2
macro.

Amongs other things, this later allows moving the handle checks in
copychunk_check_handles() down into the VFS layer where we don't have
access to the smbreq.

Signed-off-by: Ralph Boehme <slow at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 source3/include/smb_macros.h         | 16 ++++++++++++++++
 source3/smbd/smb2_glue.c             | 15 ---------------
 source3/smbd/smb2_ioctl_network_fs.c |  2 +-
 source3/smbd/smb2_read.c             |  2 +-
 4 files changed, 18 insertions(+), 17 deletions(-)

diff --git a/source3/include/smb_macros.h b/source3/include/smb_macros.h
index de39bf6..f1191ac 100644
--- a/source3/include/smb_macros.h
+++ b/source3/include/smb_macros.h
@@ -56,6 +56,22 @@
 			((req->flags2 & FLAGS2_READ_PERMIT_EXECUTE) && \
 			 (fsp->access_mask & FILE_EXECUTE))))
 
+/*
+ * This is not documented in revision 49 of [MS-SMB2] but should be added in a
+ * later revision (and torture test smb2.read.access as well as
+ * smb2.ioctl_copy_chunk_bad_access against Server 2012R2 confirms this)
+ *
+ * If FILE_EXECUTE is granted to a handle then the SMB2 server acts as if
+ * FILE_READ_DATA has also been granted. We must still keep the original granted
+ * mask, because with ioctl requests, access checks are made on the file handle,
+ * "below" the SMB2 server, and the object store below the SMB layer is not
+ * aware of this arrangement (see smb2.ioctl.copy_chunk_bad_access torture
+ * test).
+ */
+#define CHECK_READ_SMB2(fsp) \
+	(((fsp)->fh->fd != -1) && \
+	 ((fsp)->can_read || (fsp->access_mask & FILE_EXECUTE)))
+
 /* An IOCTL readability check (validating read access
  * when the IOCTL code requires it)
  * http://social.technet.microsoft.com/wiki/contents/articles/24653.decoding-io-control-codes-ioctl-fsctl-and-deviceiocodes-with-table-of-known-values.aspx
diff --git a/source3/smbd/smb2_glue.c b/source3/smbd/smb2_glue.c
index 0bb34be..bf2ea5a 100644
--- a/source3/smbd/smb2_glue.c
+++ b/source3/smbd/smb2_glue.c
@@ -49,21 +49,6 @@ struct smb_request *smbd_smb2_fake_smb_request(struct smbd_smb2_request *req)
 			 FLAGS2_LONG_PATH_COMPONENTS |
 			 FLAGS2_IS_LONG_NAME;
 
-	/* This is not documented in revision 49 of [MS-SMB2] but should be
-	 * added in a later revision (and torture test smb2.read.access
-	 * as well as smb2.ioctl_copy_chunk_bad_access against
-	 * Server 2012R2 confirms this)
-	 *
-	 * If FILE_EXECUTE is granted to a handle then the SMB2 server
-	 * acts as if FILE_READ_DATA has also been granted. We must still
-	 * keep the original granted mask, because with ioctl requests,
-	 * access checks are made on the file handle, "below" the SMB2
-	 * server, and the object store below the SMB layer is not aware
-	 * of this arrangement (see smb2.ioctl.copy_chunk_bad_access
-	 * torture test).
-	 */
-	smbreq->flags2 |= FLAGS2_READ_PERMIT_EXECUTE;
-
 	if (IVAL(inhdr, SMB2_HDR_FLAGS) & SMB2_HDR_FLAG_DFS) {
 		smbreq->flags2 |= FLAGS2_DFS_PATHNAMES;
 	}
diff --git a/source3/smbd/smb2_ioctl_network_fs.c b/source3/smbd/smb2_ioctl_network_fs.c
index 180fac2..b90a8b3f 100644
--- a/source3/smbd/smb2_ioctl_network_fs.c
+++ b/source3/smbd/smb2_ioctl_network_fs.c
@@ -125,7 +125,7 @@ static NTSTATUS copychunk_check_handles(uint32_t ctl_code,
 	 * - The Open.GrantedAccess of the source file does not include
 	 *   FILE_READ_DATA access.
 	 */
-	if (!CHECK_READ(src_fsp, smb1req)) {
+	if (!CHECK_READ_SMB2(src_fsp)) {
 		DEBUG(5, ("copy chunk no read on src handle (%s).\n",
 			smb_fname_str_dbg(src_fsp->fsp_name) ));
 		return NT_STATUS_ACCESS_DENIED;
diff --git a/source3/smbd/smb2_read.c b/source3/smbd/smb2_read.c
index 1c85840..d639bbf 100644
--- a/source3/smbd/smb2_read.c
+++ b/source3/smbd/smb2_read.c
@@ -508,7 +508,7 @@ static struct tevent_req *smbd_smb2_read_send(TALLOC_CTX *mem_ctx,
 		return req;
 	}
 
-	if (!CHECK_READ(fsp, smbreq)) {
+	if (!CHECK_READ_SMB2(fsp)) {
 		tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
 		return tevent_req_post(req, ev);
 	}
-- 
2.9.4


From b7904615bcb4ac6d759e2be81f022adc8801f808 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Fri, 9 Jun 2017 13:08:43 +0200
Subject: [PATCH 10/17] s3/smbd: remove unused arg smb1req from
 copychunk_check_handles()

Signed-off-by: Ralph Boehme <slow at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 source3/smbd/smb2_ioctl_network_fs.c | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/source3/smbd/smb2_ioctl_network_fs.c b/source3/smbd/smb2_ioctl_network_fs.c
index b90a8b3f..fa3e429 100644
--- a/source3/smbd/smb2_ioctl_network_fs.c
+++ b/source3/smbd/smb2_ioctl_network_fs.c
@@ -96,8 +96,7 @@ static void fsctl_srv_copychunk_vfs_done(struct tevent_req *subreq);
 
 static NTSTATUS copychunk_check_handles(uint32_t ctl_code,
 					struct files_struct *src_fsp,
-					struct files_struct *dst_fsp,
-					struct smb_request *smb1req)
+					struct files_struct *dst_fsp)
 {
 	/*
 	 * [MS-SMB2] 3.3.5.15.6 Handling a Server-Side Data Copy Request
@@ -220,8 +219,7 @@ static struct tevent_req *fsctl_srv_copychunk_send(TALLOC_CTX *mem_ctx,
 
 	state->status = copychunk_check_handles(ctl_code,
 						state->src_fsp,
-						state->dst_fsp,
-						smb2req->smb1req);
+						state->dst_fsp);
 	if (!NT_STATUS_IS_OK(state->status)) {
 		tevent_req_nterror(req, state->status);
 		return tevent_req_post(req, ev);
-- 
2.9.4


From ac60876a34aeb8251bc627cd51139a8b2e29ced2 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Fri, 9 Jun 2017 16:35:39 +0200
Subject: [PATCH 11/17] s3/smbd: remove copy-chunk chunk merging optimisation

As we won't have the source fsp around with the coming token based
offload read/write based code, we can't merge chunks as that requires
checking against the source file size.

We could still merge chunks without checking, but getting the error
handling correct would require comlicated logic for the SMB2 ioctl
copy-chunk error reporting.

Signed-off-by: Ralph Boehme <slow at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 source3/smbd/smb2_ioctl_network_fs.c | 78 ++----------------------------------
 1 file changed, 4 insertions(+), 74 deletions(-)

diff --git a/source3/smbd/smb2_ioctl_network_fs.c b/source3/smbd/smb2_ioctl_network_fs.c
index fa3e429..98355e2 100644
--- a/source3/smbd/smb2_ioctl_network_fs.c
+++ b/source3/smbd/smb2_ioctl_network_fs.c
@@ -80,7 +80,6 @@ struct fsctl_srv_copychunk_state {
 	struct connection_struct *conn;
 	struct srv_copychunk_copy cc_copy;
 	uint32_t current_chunk;
-	uint32_t next_chunk;
 	NTSTATUS status;
 	off_t total_written;
 	struct files_struct *src_fsp;
@@ -244,80 +243,12 @@ static struct tevent_req *fsctl_srv_copychunk_send(TALLOC_CTX *mem_ctx,
 	return req;
 }
 
-/*
- * Work out the next IO request from the remaining chunks starting with
- * state->current_chunk. Chunks with left to right adjacent ranges can be merged
- * into a single IO request.
- */
-static uint32_t next_merged_io(struct fsctl_srv_copychunk_state *state)
-{
-	struct srv_copychunk *chunk = NULL;
-	uint32_t length;
-	off_t next_src_offset;
-	off_t next_dst_offset;
-
-	/*
-	 * We're expected to process at least one chunk and return it's length,
-	 * so let's do this first.
-	 */
-
-	chunk = &state->cc_copy.chunks[state->current_chunk];
-	length = chunk->length;
-	state->next_chunk++;
-
-	/*
-	 * Now process subsequent chunks merging chunks as long as ranges are
-	 * adjacent and the source file size is not exceeded (this is needed
-	 * for error reporting).
-	 */
-
-	next_src_offset = chunk->source_off + chunk->length;
-	next_dst_offset = chunk->target_off + chunk->length;
-
-	while (state->next_chunk < state->cc_copy.chunk_count) {
-		chunk = &state->cc_copy.chunks[state->next_chunk];
-
-		if ((chunk->source_off != next_src_offset) ||
-		    (chunk->target_off != next_dst_offset))
-		{
-			/* Not adjacent, stop merging */
-			break;
-		}
-
-		next_src_offset += chunk->length;
-		next_dst_offset += chunk->length;
-
-		if (next_src_offset > state->src_fsp->fsp_name->st.st_ex_size) {
-			/* Source filesize exceeded, stop merging */
-			break;
-		}
-
-		/*
-		 * Found a mergable chunk, merge it and continue searching.
-		 * Note: this can't wrap, limits were already checked in
-		 * copychunk_check_limits().
-		 */
-		length += chunk->length;
-
-		state->next_chunk++;
-	}
-
-	return length;
-}
-
 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;
-	uint32_t length;
-
-	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) {
 		/*
@@ -356,7 +287,6 @@ static NTSTATUS fsctl_srv_copychunk_loop(struct tevent_req *req)
 	}
 
 	chunk = &state->cc_copy.chunks[state->current_chunk];
-	length = next_merged_io(state);
 
 	subreq = SMB_VFS_OFFLOAD_WRITE_SEND(state->dst_fsp->conn,
 					 state,
@@ -365,7 +295,7 @@ static NTSTATUS fsctl_srv_copychunk_loop(struct tevent_req *req)
 					 chunk->source_off,
 					 state->dst_fsp,
 					 chunk->target_off,
-					 length,
+					 chunk->length,
 					 0);
 	if (tevent_req_nomem(subreq, req)) {
 		return NT_STATUS_NO_MEMORY;
@@ -390,18 +320,18 @@ static void fsctl_srv_copychunk_vfs_done(struct tevent_req *subreq)
 	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->current_chunk,
 			(unsigned int)state->cc_copy.chunk_count);
 		tevent_req_nterror(req, status);
 		return;
 	}
 
 	DBG_DEBUG("good copy chunk [%u] of [%u]\n",
-		  (unsigned int)state->next_chunk,
+		  (unsigned int)state->current_chunk,
 		  (unsigned int)state->cc_copy.chunk_count);
 	state->total_written += chunk_nwritten;
 
-	state->current_chunk = state->next_chunk;
+	state->current_chunk++;
 	if (state->current_chunk == state->cc_copy.chunk_count) {
 		tevent_req_done(req);
 		return;
-- 
2.9.4


From 6688b10e408a308d26e04ed1996e27cc5214a438 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Fri, 9 Jun 2017 16:50:05 +0200
Subject: [PATCH 12/17] s3/smbd: redesign macOS copyfile copy-chunk

The copy-chunk request chunk_count can be 0 and Windows server just
returns success saying number of copied chunks is 0.

macOS client overload this after negotiating AAPL via their SMB2
extensions, meaning it's a so called copyfile request (copy whole file
and all streams).

We previously checked this at the SMB layer, with this patch we just
send this down the VFS, if vfs_fruit is loaded it implements the macOS
copyile semantics, otherwise we get Windows behavour..

No change in behaviour.

Signed-off-by: Ralph Boehme <slow at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 source3/modules/vfs_default.c        |  6 +++
 source3/smbd/smb2_ioctl_network_fs.c | 76 +++++++++++++++---------------------
 2 files changed, 37 insertions(+), 45 deletions(-)

diff --git a/source3/modules/vfs_default.c b/source3/modules/vfs_default.c
index c7b114e..2c14b0a 100644
--- a/source3/modules/vfs_default.c
+++ b/source3/modules/vfs_default.c
@@ -1738,6 +1738,12 @@ static struct tevent_req *vfswrap_offload_write_send(
 		.remaining = to_copy,
 		.flags = flags,
 	};
+
+	if (to_copy == 0) {
+		tevent_req_done(req);
+		return tevent_req_post(req, ev);
+	}
+
 	state->buf = talloc_array(state, uint8_t, num);
 	if (tevent_req_nomem(state->buf, req)) {
 		return tevent_req_post(req, ev);
diff --git a/source3/smbd/smb2_ioctl_network_fs.c b/source3/smbd/smb2_ioctl_network_fs.c
index 98355e2..b528ff2 100644
--- a/source3/smbd/smb2_ioctl_network_fs.c
+++ b/source3/smbd/smb2_ioctl_network_fs.c
@@ -248,54 +248,35 @@ 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;
+	uint32_t length = 0;
+	off_t source_off = 0;
+	off_t target_off = 0;
 
-	if (state->cc_copy.chunk_count == 0) {
-		/*
-		 * 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 ||
-		    !state->dst_fsp->aapl_copyfile_supported)
-		{
-			/*
-			 * This must not produce an error but just return 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;
-
-		subreq = SMB_VFS_OFFLOAD_WRITE_SEND(state->dst_fsp->conn,
-						 state,
-						 state->ev,
-						 state->src_fsp,
-						 0,
-						 state->dst_fsp,
-						 0,
-						 0,
-						 0);
-		if (subreq == NULL) {
-			return NT_STATUS_NO_MEMORY;
-		}
-		tevent_req_set_callback(subreq,
-					fsctl_srv_copychunk_vfs_done, req);
-		return NT_STATUS_OK;
-	}
+	/*
+	 * chunk_count can be 0 which must either just do nothing returning
+	 * success saying number of copied chunks is 0 (verified against
+	 * Windows).
+	 *
+	 * Or it can be a special macOS copyfile request, so we send this into
+	 * the VFS, vfs_fruit if loaded implements the macOS copyile semantics.
+	 */
+	if (state->cc_copy.chunk_count > 0) {
+		struct srv_copychunk *chunk = NULL;
 
-	chunk = &state->cc_copy.chunks[state->current_chunk];
+		chunk = &state->cc_copy.chunks[state->current_chunk];
+		length = chunk->length;
+		source_off = chunk->source_off;
+		target_off = chunk->target_off;
+	}
 
 	subreq = SMB_VFS_OFFLOAD_WRITE_SEND(state->dst_fsp->conn,
 					 state,
 					 state->ev,
 					 state->src_fsp,
-					 chunk->source_off,
+					 source_off,
 					 state->dst_fsp,
-					 chunk->target_off,
-					 chunk->length,
+					 target_off,
+					 length,
 					 0);
 	if (tevent_req_nomem(subreq, req)) {
 		return NT_STATUS_NO_MEMORY;
@@ -331,6 +312,15 @@ static void fsctl_srv_copychunk_vfs_done(struct tevent_req *subreq)
 		  (unsigned int)state->cc_copy.chunk_count);
 	state->total_written += chunk_nwritten;
 
+	if (state->cc_copy.chunk_count == 0) {
+		/*
+		 * This must not produce an error but just return a chunk count
+		 * of 0 in the response.
+		 */
+		tevent_req_done(req);
+		return;
+	}
+
 	state->current_chunk++;
 	if (state->current_chunk == state->cc_copy.chunk_count) {
 		tevent_req_done(req);
@@ -361,11 +351,7 @@ static NTSTATUS fsctl_srv_copychunk_recv(struct tevent_req *req,
 		*pack_rsp = true;
 		break;
 	case COPYCHUNK_OUT_RSP:
-		if (state->aapl_copyfile == true) {
-			cc_rsp->chunks_written = 0;
-		} else {
-			cc_rsp->chunks_written = state->current_chunk;
-		}
+		cc_rsp->chunks_written = state->current_chunk;
 		cc_rsp->chunk_bytes_written = 0;
 		cc_rsp->total_bytes_written = state->total_written;
 		*pack_rsp = true;
-- 
2.9.4


From 45f349613aa6d1c68717b68bbfc76deb97b30d18 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Mon, 5 Jun 2017 08:31:19 +0200
Subject: [PATCH 13/17] s4/torture: add a test for copy-chunk across shares

Signed-off-by: Ralph Boehme <slow at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 .../smb2.ioctl.copy-chunk-across-shares            |  2 +
 source4/torture/smb2/ioctl.c                       | 85 ++++++++++++++++++++++
 2 files changed, 87 insertions(+)
 create mode 100644 selftest/knownfail.d/smb2.ioctl.copy-chunk-across-shares

diff --git a/selftest/knownfail.d/smb2.ioctl.copy-chunk-across-shares b/selftest/knownfail.d/smb2.ioctl.copy-chunk-across-shares
new file mode 100644
index 0000000..4a1f859
--- /dev/null
+++ b/selftest/knownfail.d/smb2.ioctl.copy-chunk-across-shares
@@ -0,0 +1,2 @@
+^samba3.smb2.ioctl.copy_chunk_across_shares
+^samba3.smb2.ioctl fs_specific.copy_chunk_across_shares
diff --git a/source4/torture/smb2/ioctl.c b/source4/torture/smb2/ioctl.c
index d9b7f4a..3ccf7be 100644
--- a/source4/torture/smb2/ioctl.c
+++ b/source4/torture/smb2/ioctl.c
@@ -1984,6 +1984,89 @@ done:
 	return ok;
 }
 
+static bool test_copy_chunk_across_shares(struct torture_context *tctx,
+					  struct smb2_tree *tree)
+{
+	TALLOC_CTX *mem_ctx = NULL;
+	struct smb2_tree *tree2 = NULL;
+	struct smb2_handle src_h = {{0}};
+	struct smb2_handle dest_h = {{0}};
+	union smb_ioctl ioctl;
+	struct srv_copychunk_copy cc_copy;
+	struct srv_copychunk_rsp cc_rsp;
+	enum ndr_err_code ndr_ret;
+	NTSTATUS status;
+	bool ok = false;
+
+	mem_ctx = talloc_new(tctx);
+	torture_assert_not_null_goto(tctx, mem_ctx, ok, done,
+				     "talloc_new\n");
+
+	ok = torture_smb2_tree_connect(tctx, tree->session, tctx, &tree2);
+	torture_assert_goto(tctx, ok == true, ok, done,
+			    "torture_smb2_tree_connect failed\n");
+
+	ok = test_setup_copy_chunk(tctx, tree, tree2, mem_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,
+				   &ioctl);
+	torture_assert_goto(tctx, ok == true, ok, done,
+			    "test_setup_copy_chunk failed\n");
+
+	cc_copy.chunks[0].source_off = 0;
+	cc_copy.chunks[0].target_off = 0;
+	cc_copy.chunks[0].length = 4096;
+
+	ndr_ret = ndr_push_struct_blob(
+		&ioctl.smb2.in.out, mem_ctx, &cc_copy,
+		(ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
+	torture_assert_ndr_success_goto(tctx, ndr_ret, ok, done,
+					"ndr_push_srv_copychunk_copy\n");
+
+	status = smb2_ioctl(tree2, mem_ctx, &ioctl.smb2);
+	torture_assert_ntstatus_ok_goto(tctx, status, ok, done,
+					"FSCTL_SRV_COPYCHUNK\n");
+
+	ndr_ret = ndr_pull_struct_blob(
+		&ioctl.smb2.out.out, mem_ctx, &cc_rsp,
+		(ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
+
+	torture_assert_ndr_success_goto(tctx, ndr_ret, ok, done,
+				   "ndr_pull_srv_copychunk_rsp\n");
+
+	ok = check_copy_chunk_rsp(tctx, &cc_rsp,
+				  1,	/* chunks written */
+				  0,	/* chunk bytes unsuccessfully written */
+				  4096); /* total bytes written */
+	torture_assert_goto(tctx, ok == true, ok, done,
+			    "bad copy chunk response data\n");
+
+	ok = check_pattern(tctx, tree2, mem_ctx, dest_h, 0, 4096, 0);
+	torture_assert_goto(tctx, ok == true, ok, done,
+			    "inconsistent file data\n");
+
+done:
+	TALLOC_FREE(mem_ctx);
+	if (!smb2_util_handle_empty(src_h)) {
+		smb2_util_close(tree, src_h);
+	}
+	if (!smb2_util_handle_empty(dest_h)) {
+		smb2_util_close(tree2, dest_h);
+	}
+	smb2_util_unlink(tree, FNAME);
+	smb2_util_unlink(tree2, FNAME2);
+	if (tree2 != NULL) {
+		smb2_tdis(tree2);
+	}
+	return ok;
+}
+
 static NTSTATUS test_ioctl_compress_fs_supported(struct torture_context *torture,
 						 struct smb2_tree *tree,
 						 TALLOC_CTX *mem_ctx,
@@ -6427,6 +6510,8 @@ struct torture_suite *torture_smb2_ioctl_init(TALLOC_CTX *ctx)
 				     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, "copy_chunk_across_shares",
+				     test_copy_chunk_across_shares);
 	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.4


From 70d23654a4aa60779b7b2b0a7b76cb50f71f24d6 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Fri, 9 Jun 2017 13:02:49 +0200
Subject: [PATCH 14/17] s3/vfs: make SMB_VFS_OFFLOAD_WRITE_SEND offload token
 based

Remove the source fsp argument and instead pass the offload token
generated with SMB_VFS_OFFLOAD_READ_SEND/RECV.

An actual offload fsctl is not implemented yet, neither in the VFS nor
at the SMB ioctl layer, and returns NT_STATUS_NOT_IMPLEMENTED

With these changes we now pass the copy-chunk-across-shares test.

Signed-off-by: Ralph Boehme <slow at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 examples/VFS/skel_opaque.c                         |  5 +-
 examples/VFS/skel_transparent.c                    |  7 +-
 .../smb2.ioctl.copy-chunk-across-shares            |  2 -
 source3/include/vfs.h                              | 10 ++-
 source3/include/vfs_macros.h                       |  8 +-
 source3/modules/offload_token.c                    | 90 +++++++++++++++++++++
 source3/modules/offload_token.h                    |  3 +
 source3/modules/vfs_btrfs.c                        | 46 +++++++++--
 source3/modules/vfs_default.c                      | 48 +++++++++--
 source3/modules/vfs_fruit.c                        | 29 +++++--
 source3/modules/vfs_full_audit.c                   | 10 ++-
 source3/modules/vfs_time_audit.c                   |  7 +-
 source3/smbd/smb2_ioctl_filesys.c                  |  9 +--
 source3/smbd/smb2_ioctl_network_fs.c               | 92 ++--------------------
 source3/smbd/vfs.c                                 | 10 ++-
 15 files changed, 242 insertions(+), 134 deletions(-)
 delete mode 100644 selftest/knownfail.d/smb2.ioctl.copy-chunk-across-shares

diff --git a/examples/VFS/skel_opaque.c b/examples/VFS/skel_opaque.c
index 63008e8..76fa7e4 100644
--- a/examples/VFS/skel_opaque.c
+++ b/examples/VFS/skel_opaque.c
@@ -581,8 +581,9 @@ struct skel_cc_state {
 static struct tevent_req *skel_offload_write_send(struct vfs_handle_struct *handle,
 					       TALLOC_CTX *mem_ctx,
 					       struct tevent_context *ev,
-					       struct files_struct *src_fsp,
-					       off_t src_off,
+					       uint32_t fsctl,
+					       DATA_BLOB *token,
+					       off_t transfer_offset,
 					       struct files_struct *dest_fsp,
 					       off_t dest_off,
 					       off_t num,
diff --git a/examples/VFS/skel_transparent.c b/examples/VFS/skel_transparent.c
index b39489b..069cb18 100644
--- a/examples/VFS/skel_transparent.c
+++ b/examples/VFS/skel_transparent.c
@@ -715,8 +715,9 @@ static void skel_offload_write_done(struct tevent_req *subreq);
 static struct tevent_req *skel_offload_write_send(struct vfs_handle_struct *handle,
 					       TALLOC_CTX *mem_ctx,
 					       struct tevent_context *ev,
-					       struct files_struct *src_fsp,
-					       off_t src_off,
+					       uint32_t fsctl,
+					       DATA_BLOB *token,
+					       off_t transfer_offset,
 					       struct files_struct *dest_fsp,
 					       off_t dest_off,
 					       off_t num,
@@ -733,7 +734,7 @@ static struct tevent_req *skel_offload_write_send(struct vfs_handle_struct *hand
 
 	state->handle = handle;
 	subreq = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle, state, ev,
-					      src_fsp, src_off,
+					      fsctl, token, transfer_offset,
 					      dest_fsp, dest_off, num, flags);
 	if (tevent_req_nomem(subreq, req)) {
 		return tevent_req_post(req, ev);
diff --git a/selftest/knownfail.d/smb2.ioctl.copy-chunk-across-shares b/selftest/knownfail.d/smb2.ioctl.copy-chunk-across-shares
deleted file mode 100644
index 4a1f859..0000000
--- a/selftest/knownfail.d/smb2.ioctl.copy-chunk-across-shares
+++ /dev/null
@@ -1,2 +0,0 @@
-^samba3.smb2.ioctl.copy_chunk_across_shares
-^samba3.smb2.ioctl fs_specific.copy_chunk_across_shares
diff --git a/source3/include/vfs.h b/source3/include/vfs.h
index 4adc315..dab5b50 100644
--- a/source3/include/vfs.h
+++ b/source3/include/vfs.h
@@ -785,8 +785,9 @@ struct vfs_fn_pointers {
 	struct tevent_req *(*offload_write_send_fn)(struct vfs_handle_struct *handle,
 						    TALLOC_CTX *mem_ctx,
 						    struct tevent_context *ev,
-						    struct files_struct *src_fsp,
-						    off_t src_off,
+						    uint32_t fsctl,
+						    DATA_BLOB *token,
+						    off_t transfer_offset,
 						    struct files_struct *dest_fsp,
 						    off_t dest_off,
 						    off_t to_copy,
@@ -1361,8 +1362,9 @@ NTSTATUS smb_vfs_call_offload_read_recv(struct tevent_req *req,
 struct tevent_req *smb_vfs_call_offload_write_send(struct vfs_handle_struct *handle,
 						   TALLOC_CTX *mem_ctx,
 						   struct tevent_context *ev,
-						   struct files_struct *src_fsp,
-						   off_t src_off,
+						   uint32_t fsctl,
+						   DATA_BLOB *token,
+						   off_t transfer_offset,
 						   struct files_struct *dest_fsp,
 						   off_t dest_off,
 						   off_t num,
diff --git a/source3/include/vfs_macros.h b/source3/include/vfs_macros.h
index eb5886f..d9593cf 100644
--- a/source3/include/vfs_macros.h
+++ b/source3/include/vfs_macros.h
@@ -425,10 +425,10 @@
 #define SMB_VFS_NEXT_OFFLOAD_READ_RECV(req, handle, mem_ctx, token_blob) \
 	smb_vfs_call_offload_read_recv((req), (handle)->next, (mem_ctx), (token_blob))
 
-#define SMB_VFS_OFFLOAD_WRITE_SEND(conn, mem_ctx, ev, src_fsp, src_off, dest_fsp, dest_off, num, flags) \
-	smb_vfs_call_offload_write_send((conn)->vfs_handles, (mem_ctx), (ev), (src_fsp), (src_off), (dest_fsp), (dest_off), (num), (flags))
-#define SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle, mem_ctx, ev, src_fsp, src_off, dest_fsp, dest_off, num, flags) \
-	smb_vfs_call_offload_write_send((handle)->next, (mem_ctx), (ev), (src_fsp), (src_off), (dest_fsp), (dest_off), (num), (flags))
+#define SMB_VFS_OFFLOAD_WRITE_SEND(conn, mem_ctx, ev, fsctl, token, transfer_offset, dest_fsp, dest_off, num, flags) \
+	smb_vfs_call_offload_write_send((conn)->vfs_handles, (mem_ctx), (ev), (fsctl), (token), (transfer_offset), (dest_fsp), (dest_off), (num), (flags))
+#define SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle, mem_ctx, ev, fsctl, token, transfer_offset, dest_fsp, dest_off, num, flags) \
+	smb_vfs_call_offload_write_send((handle)->next, (mem_ctx), (ev), (fsctl), (token), (transfer_offset), (dest_fsp), (dest_off), (num), (flags))
 
 #define SMB_VFS_OFFLOAD_WRITE_RECV(conn, req, copied) \
 	smb_vfs_call_offload_write_recv((conn)->vfs_handles, (req), (copied))
diff --git a/source3/modules/offload_token.c b/source3/modules/offload_token.c
index aae41be..91618a1 100644
--- a/source3/modules/offload_token.c
+++ b/source3/modules/offload_token.c
@@ -20,6 +20,7 @@
 #include "includes.h"
 #include "smbd/smbd.h"
 #include "smbd/globals.h"
+#include "../libcli/security/security.h"
 #include "dbwrap/dbwrap.h"
 #include "dbwrap/dbwrap_rbt.h"
 #include "dbwrap/dbwrap_open.h"
@@ -205,3 +206,92 @@ NTSTATUS vfs_offload_token_create_blob(TALLOC_CTX *mem_ctx,
 	return NT_STATUS_OK;
 }
 
+
+NTSTATUS vfs_offload_token_check_handles(uint32_t fsctl,
+					 files_struct *src_fsp,
+					 files_struct *dst_fsp)
+{
+	if (src_fsp->vuid != dst_fsp->vuid) {
+		DBG_INFO("copy chunk handles not in the same session.\n");
+		return NT_STATUS_ACCESS_DENIED;
+	}
+
+	if (!NT_STATUS_IS_OK(src_fsp->op->status)) {
+		DBG_INFO("copy chunk source handle invalid: %s\n",
+			 nt_errstr(src_fsp->op->status));
+		return NT_STATUS_ACCESS_DENIED;
+	}
+
+	if (!NT_STATUS_IS_OK(dst_fsp->op->status)) {
+		DBG_INFO("copy chunk destination handle invalid: %s\n",
+			 nt_errstr(dst_fsp->op->status));
+		return NT_STATUS_ACCESS_DENIED;
+	}
+
+	if (src_fsp->deferred_close != NULL) {
+		DBG_INFO("copy chunk src handle with deferred close.\n");
+		return NT_STATUS_ACCESS_DENIED;
+	}
+
+	if (dst_fsp->deferred_close != NULL) {
+		DBG_INFO("copy chunk dst handle with deferred close.\n");
+		return NT_STATUS_ACCESS_DENIED;
+	}
+
+	if (src_fsp->is_directory) {
+		DBG_INFO("copy chunk no read on src directory handle (%s).\n",
+			 smb_fname_str_dbg(src_fsp->fsp_name));
+		return NT_STATUS_ACCESS_DENIED;
+	}
+
+	if (dst_fsp->is_directory) {
+		DBG_INFO("copy chunk no read on dst directory handle (%s).\n",
+			 smb_fname_str_dbg(dst_fsp->fsp_name));
+		return NT_STATUS_ACCESS_DENIED;
+	}
+
+	if (IS_IPC(src_fsp->conn) || IS_IPC(dst_fsp->conn)) {
+		DBG_INFO("copy chunk no access on IPC$ handle.\n");
+		return NT_STATUS_ACCESS_DENIED;
+	}
+
+	if (IS_PRINT(src_fsp->conn) || IS_PRINT(dst_fsp->conn)) {
+		DBG_INFO("copy chunk no access on PRINT handle.\n");
+		return NT_STATUS_ACCESS_DENIED;
+	}
+
+	/*
+	 * [MS-SMB2] 3.3.5.15.6 Handling a Server-Side Data Copy Request
+	 * The server MUST fail the request with STATUS_ACCESS_DENIED if any of
+	 * the following are true:
+	 * - The Open.GrantedAccess of the destination file does not include
+	 *   FILE_WRITE_DATA or FILE_APPEND_DATA.
+	 *
+	 * A non writable dst handle also doesn't make sense for other fsctls.
+	 */
+	if (!CHECK_WRITE(dst_fsp)) {
+		DBG_INFO("dest handle not writable (%s).\n",
+			smb_fname_str_dbg(dst_fsp->fsp_name));
+		return NT_STATUS_ACCESS_DENIED;
+	}
+	/*
+	 * - The Open.GrantedAccess of the destination file does not include
+	 *   FILE_READ_DATA, and the CtlCode is FSCTL_SRV_COPYCHUNK.
+	 */
+	if ((fsctl == FSCTL_SRV_COPYCHUNK) && !CHECK_READ_IOCTL(dst_fsp)) {
+		DBG_INFO("copy chunk no read on dest handle (%s).\n",
+			 smb_fname_str_dbg(dst_fsp->fsp_name));
+		return NT_STATUS_ACCESS_DENIED;
+	}
+	/*
+	 * - The Open.GrantedAccess of the source file does not include
+	 *   FILE_READ_DATA access.
+	 */
+	if (!CHECK_READ_SMB2(src_fsp)) {
+		DBG_INFO("src handle not readable (%s).\n",
+			 smb_fname_str_dbg(src_fsp->fsp_name));
+		return NT_STATUS_ACCESS_DENIED;
+	}
+
+	return NT_STATUS_OK;
+}
diff --git a/source3/modules/offload_token.h b/source3/modules/offload_token.h
index 569edcf..1e8e26d 100644
--- a/source3/modules/offload_token.h
+++ b/source3/modules/offload_token.h
@@ -34,4 +34,7 @@ NTSTATUS vfs_offload_token_create_blob(TALLOC_CTX *mem_ctx,
 				       const files_struct *fsp,
 				       uint32_t fsctl,
 				       DATA_BLOB *token_blob);
+NTSTATUS vfs_offload_token_check_handles(uint32_t fsctl,
+					 files_struct *src_fsp,
+					 files_struct *dst_fsp);
 #endif
diff --git a/source3/modules/vfs_btrfs.c b/source3/modules/vfs_btrfs.c
index 7917537..f310a3d 100644
--- a/source3/modules/vfs_btrfs.c
+++ b/source3/modules/vfs_btrfs.c
@@ -206,8 +206,9 @@ static void btrfs_offload_write_done(struct tevent_req *subreq);
 static struct tevent_req *btrfs_offload_write_send(struct vfs_handle_struct *handle,
 						TALLOC_CTX *mem_ctx,
 						struct tevent_context *ev,
-						struct files_struct *src_fsp,
-						off_t src_off,
+						uint32_t fsctl,
+						DATA_BLOB *token,
+						off_t transfer_offset,
 						struct files_struct *dest_fsp,
 						off_t dest_off,
 						off_t num,
@@ -218,7 +219,10 @@ static struct tevent_req *btrfs_offload_write_send(struct vfs_handle_struct *han
 	struct btrfs_ioctl_clone_range_args cr_args;
 	struct lock_struct src_lck;
 	struct lock_struct dest_lck;
+	off_t src_off = transfer_offset;
+	files_struct *src_fsp = NULL;
 	int ret;
+	bool handle_offload_write = true;
 	NTSTATUS status;
 
 	req = tevent_req_create(mem_ctx, &cc_state, struct btrfs_cc_state);
@@ -233,16 +237,38 @@ static struct tevent_req *btrfs_offload_write_send(struct vfs_handle_struct *han
 
 	cc_state->handle = handle;
 
+	status = vfs_offload_token_db_fetch_fsp(btrfs_offload_ctx,
+						token, &src_fsp);
+	if (tevent_req_nterror(req, status)) {
+		return tevent_req_post(req, ev);
+	}
+
+	switch (fsctl) {
+	case FSCTL_SRV_COPYCHUNK:
+	case FSCTL_SRV_COPYCHUNK_WRITE:
+	case FSCTL_DUP_EXTENTS_TO_FILE:
+		break;
+
+	default:
+		handle_offload_write = false;
+		break;
+	}
+
 	if (num == 0) {
 		/*
 		 * With a @src_length of zero, BTRFS_IOC_CLONE_RANGE clones
 		 * all data from @src_offset->EOF! This is certainly not what
 		 * the caller expects, and not what vfs_default does.
 		 */
+		handle_offload_write = false;
+	}
+
+	if (!handle_offload_write) {
 		cc_state->subreq = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle,
 								cc_state, ev,
-								src_fsp,
-								src_off,
+								fsctl,
+								token,
+								transfer_offset,
 								dest_fsp,
 								dest_off,
 								num, flags);
@@ -255,6 +281,13 @@ static struct tevent_req *btrfs_offload_write_send(struct vfs_handle_struct *han
 		return req;
 	}
 
+	status = vfs_offload_token_check_handles(
+		fsctl, src_fsp, dest_fsp);
+	if (!NT_STATUS_IS_OK(status)) {
+		tevent_req_nterror(req, status);
+		return tevent_req_post(req, ev);
+	}
+
 	status = vfs_stat_fsp(src_fsp);
 	if (tevent_req_nterror(req, status)) {
 		return tevent_req_post(req, ev);
@@ -323,8 +356,9 @@ static struct tevent_req *btrfs_offload_write_send(struct vfs_handle_struct *han
 			  (unsigned long long)cr_args.dest_offset));
 		cc_state->subreq = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle,
 								cc_state, ev,
-								src_fsp,
-								src_off,
+								fsctl,
+								token,
+								transfer_offset,
 								dest_fsp,
 								dest_off,
 								num, flags);
diff --git a/source3/modules/vfs_default.c b/source3/modules/vfs_default.c
index 2c14b0a..7925734 100644
--- a/source3/modules/vfs_default.c
+++ b/source3/modules/vfs_default.c
@@ -1681,6 +1681,7 @@ struct vfswrap_offload_write_state {
 	struct lock_struct read_lck;
 	bool write_lck_locked;
 	struct lock_struct write_lck;
+	DATA_BLOB *token;
 	struct files_struct *src_fsp;
 	off_t src_off;
 	struct files_struct *dst_fsp;
@@ -1697,8 +1698,9 @@ static struct tevent_req *vfswrap_offload_write_send(
 	struct vfs_handle_struct *handle,
 	TALLOC_CTX *mem_ctx,
 	struct tevent_context *ev,
-	struct files_struct *src_fsp,
-	off_t src_off,
+	uint32_t fsctl,
+	DATA_BLOB *token,
+	off_t transfer_offset,
 	struct files_struct *dest_fsp,
 	off_t dest_off,
 	off_t to_copy,
@@ -1707,10 +1709,9 @@ static struct tevent_req *vfswrap_offload_write_send(
 	struct tevent_req *req;
 	struct vfswrap_offload_write_state *state = NULL;
 	size_t num = MIN(to_copy, COPYCHUNK_MAX_TOTAL_LEN);
+	files_struct *src_fsp = NULL;
 	NTSTATUS status;
 
-	DBG_DEBUG("server side copy chunk of length %" PRIu64 "\n", to_copy);
-
 	req = tevent_req_create(mem_ctx, &state,
 				struct vfswrap_offload_write_state);
 	if (req == NULL) {
@@ -1730,8 +1731,8 @@ static struct tevent_req *vfswrap_offload_write_send(
 
 	*state = (struct vfswrap_offload_write_state) {
 		.ev = ev,
-		.src_fsp = src_fsp,
-		.src_off = src_off,
+		.token = token,
+		.src_off = transfer_offset,
 		.dst_fsp = dest_fsp,
 		.dst_off = dest_off,
 		.to_copy = to_copy,
@@ -1739,11 +1740,44 @@ static struct tevent_req *vfswrap_offload_write_send(
 		.flags = flags,
 	};
 
+	switch (fsctl) {
+	case FSCTL_SRV_COPYCHUNK:
+	case FSCTL_SRV_COPYCHUNK_WRITE:
+		break;
+
+	case FSCTL_OFFLOAD_WRITE:
+		tevent_req_nterror(req, NT_STATUS_NOT_IMPLEMENTED);
+		return tevent_req_post(req, ev);
+
+	default:
+		tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+		return tevent_req_post(req, ev);
+	}
+
+	/*
+	 * From here on we assume a copy-chunk fsctl
+	 */
+
 	if (to_copy == 0) {
 		tevent_req_done(req);
 		return tevent_req_post(req, ev);
 	}
 
+	status = vfs_offload_token_db_fetch_fsp(vfswrap_offload_ctx,
+						token, &src_fsp);
+	if (tevent_req_nterror(req, status)) {
+		return tevent_req_post(req, ev);
+	}
+	state->src_fsp = src_fsp;
+
+	DBG_DEBUG("server side copy chunk of length %" PRIu64 "\n", to_copy);
+
+	status = vfs_offload_token_check_handles(fsctl, src_fsp, dest_fsp);
+	if (!NT_STATUS_IS_OK(status)) {
+		tevent_req_nterror(req, status);
+		return tevent_req_post(req, ev);
+	}
+
 	state->buf = talloc_array(state, uint8_t, num);
 	if (tevent_req_nomem(state->buf, req)) {
 		return tevent_req_post(req, ev);
@@ -1754,7 +1788,7 @@ static struct tevent_req *vfswrap_offload_write_send(
 		return tevent_req_post(req, ev);
 	}
 
-	if (src_fsp->fsp_name->st.st_ex_size < src_off + num) {
+	if (src_fsp->fsp_name->st.st_ex_size < state->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
diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c
index 279366c..db6324a 100644
--- a/source3/modules/vfs_fruit.c
+++ b/source3/modules/vfs_fruit.c
@@ -5485,8 +5485,9 @@ static void fruit_offload_write_done(struct tevent_req *subreq);
 static struct tevent_req *fruit_offload_write_send(struct vfs_handle_struct *handle,
 						TALLOC_CTX *mem_ctx,
 						struct tevent_context *ev,
-						struct files_struct *src_fsp,
-						off_t src_off,
+						uint32_t fsctl,
+						DATA_BLOB *token,
+						off_t transfer_offset,
 						struct files_struct *dest_fsp,
 						off_t dest_off,
 						off_t num,
@@ -5496,6 +5497,8 @@ static struct tevent_req *fruit_offload_write_send(struct vfs_handle_struct *han
 	struct fruit_offload_write_state *state;
 	NTSTATUS status;
 	struct fruit_config_data *config;
+	off_t src_off = transfer_offset;
+	files_struct *src_fsp = NULL;
 	off_t to_copy = num;
 
 	DEBUG(10,("soff: %ju, doff: %ju, len: %ju\n",
@@ -5511,9 +5514,22 @@ static struct tevent_req *fruit_offload_write_send(struct vfs_handle_struct *han
 		return NULL;
 	}
 	state->handle = handle;
-	state->src_fsp = src_fsp;
 	state->dst_fsp = dest_fsp;
 
+	switch (fsctl) {
+	case FSCTL_SRV_COPYCHUNK:
+	case FSCTL_SRV_COPYCHUNK_WRITE:
+		status = vfs_offload_token_db_fetch_fsp(fruit_offload_ctx,
+							token, &src_fsp);
+		if (tevent_req_nterror(req, status)) {
+			return tevent_req_post(req, ev);
+		}
+		state->src_fsp = src_fsp;
+		break;
+	default:
+		break;
+	}
+
 	/*
 	 * Check if this a OS X copyfile style copychunk request with
 	 * a requested chunk count of 0 that was translated to a
@@ -5521,7 +5537,7 @@ static struct tevent_req *fruit_offload_write_send(struct vfs_handle_struct *han
 	 * = dest_off = num = 0.
 	 */
 	if ((src_off == 0) && (dest_off == 0) && (num == 0) &&
-	    src_fsp->aapl_copyfile_supported &&
+	    src_fsp != NULL && src_fsp->aapl_copyfile_supported &&
 	    dest_fsp->aapl_copyfile_supported)
 	{
 		status = vfs_stat_fsp(src_fsp);
@@ -5536,8 +5552,9 @@ static struct tevent_req *fruit_offload_write_send(struct vfs_handle_struct *han
 	subreq = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle,
 					      mem_ctx,
 					      ev,
-					      src_fsp,
-					      src_off,
+					      fsctl,
+					      token,
+					      transfer_offset,
 					      dest_fsp,
 					      dest_off,
 					      to_copy,
diff --git a/source3/modules/vfs_full_audit.c b/source3/modules/vfs_full_audit.c
index dba508e..66fe7d8 100644
--- a/source3/modules/vfs_full_audit.c
+++ b/source3/modules/vfs_full_audit.c
@@ -1940,8 +1940,9 @@ static NTSTATUS smb_full_audit_offload_read_recv(
 static struct tevent_req *smb_full_audit_offload_write_send(struct vfs_handle_struct *handle,
 							 TALLOC_CTX *mem_ctx,
 							 struct tevent_context *ev,
-							 struct files_struct *src_fsp,
-							 off_t src_off,
+							 uint32_t fsctl,
+							 DATA_BLOB *token,
+							 off_t transfer_offset,
 							 struct files_struct *dest_fsp,
 							 off_t dest_off,
 							 off_t num,
@@ -1949,8 +1950,9 @@ static struct tevent_req *smb_full_audit_offload_write_send(struct vfs_handle_st
 {
 	struct tevent_req *req;
 
-	req = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle, mem_ctx, ev, src_fsp,
-					   src_off, dest_fsp, dest_off, num,
+	req = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle, mem_ctx, ev,
+					   fsctl, token, transfer_offset,
+					   dest_fsp, dest_off, num,
 					   flags);
 
 	do_log(SMB_VFS_OP_OFFLOAD_WRITE_SEND, req, handle, "");
diff --git a/source3/modules/vfs_time_audit.c b/source3/modules/vfs_time_audit.c
index 581679d..6e71343 100644
--- a/source3/modules/vfs_time_audit.c
+++ b/source3/modules/vfs_time_audit.c
@@ -1998,8 +1998,9 @@ static void smb_time_audit_offload_write_done(struct tevent_req *subreq);
 static struct tevent_req *smb_time_audit_offload_write_send(struct vfs_handle_struct *handle,
 							 TALLOC_CTX *mem_ctx,
 							 struct tevent_context *ev,
-							 struct files_struct *src_fsp,
-							 off_t src_off,
+							 uint32_t fsctl,
+							 DATA_BLOB *token,
+							 off_t transfer_offset,
 							 struct files_struct *dest_fsp,
 							 off_t dest_off,
 							 off_t num,
@@ -2018,7 +2019,7 @@ static struct tevent_req *smb_time_audit_offload_write_send(struct vfs_handle_st
 	state->handle = handle;
 	clock_gettime_mono(&state->ts_send);
 	subreq = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle, state, ev,
-					      src_fsp, src_off,
+					      fsctl, token, transfer_offset,
 					      dest_fsp, dest_off, num, flags);
 	if (tevent_req_nomem(subreq, req)) {
 		return tevent_req_post(req, ev);
diff --git a/source3/smbd/smb2_ioctl_filesys.c b/source3/smbd/smb2_ioctl_filesys.c
index 5bbeada..fa1cb93 100644
--- a/source3/smbd/smb2_ioctl_filesys.c
+++ b/source3/smbd/smb2_ioctl_filesys.c
@@ -161,7 +161,6 @@ static NTSTATUS fsctl_dup_extents_check_sparse(struct files_struct *src_fsp,
 struct fsctl_dup_extents_state {
 	struct tevent_context *ev;
 	struct connection_struct *conn;
-	struct files_struct *src_fsp;
 	struct files_struct *dst_fsp;
 	struct fsctl_dup_extents_to_file dup_extents;
 };
@@ -234,7 +233,6 @@ static struct tevent_req *fsctl_dup_extents_send(TALLOC_CTX *mem_ctx,
 		tevent_req_nterror(req, NT_STATUS_INVALID_HANDLE);
 		return tevent_req_post(req, ev);
 	}
-	state->src_fsp = src_fsp;
 
 	status = fsctl_dup_extents_check_lengths(src_fsp, dst_fsp,
 						 &state->dup_extents);
@@ -279,11 +277,11 @@ static void fsctl_dup_extents_offload_read_done(struct tevent_req *subreq)
 		subreq, struct tevent_req);
 	struct fsctl_dup_extents_state *state = tevent_req_data(
 		req, struct fsctl_dup_extents_state);
-	DATA_BLOB token_blob;
+	DATA_BLOB token;
 	NTSTATUS status;
 
 	status = SMB_VFS_OFFLOAD_READ_RECV(subreq, state->dst_fsp->conn,
-					   state, &token_blob);
+					   state, &token);
 	if (tevent_req_nterror(req, status)) {
 		return;
 	}
@@ -292,7 +290,8 @@ static void fsctl_dup_extents_offload_read_done(struct tevent_req *subreq)
 	subreq = SMB_VFS_OFFLOAD_WRITE_SEND(state->dst_fsp->conn,
 					    state,
 					    state->ev,
-					    state->src_fsp,
+					    FSCTL_DUP_EXTENTS_TO_FILE,
+					    &token,
 					    state->dup_extents.source_off,
 					    state->dst_fsp,
 					    state->dup_extents.target_off,
diff --git a/source3/smbd/smb2_ioctl_network_fs.c b/source3/smbd/smb2_ioctl_network_fs.c
index b528ff2..993f6f8 100644
--- a/source3/smbd/smb2_ioctl_network_fs.c
+++ b/source3/smbd/smb2_ioctl_network_fs.c
@@ -82,6 +82,8 @@ struct fsctl_srv_copychunk_state {
 	uint32_t current_chunk;
 	NTSTATUS status;
 	off_t total_written;
+	uint32_t ctl_code;
+	DATA_BLOB token;
 	struct files_struct *src_fsp;
 	struct files_struct *dst_fsp;
 	enum {
@@ -93,67 +95,6 @@ struct fsctl_srv_copychunk_state {
 };
 static void fsctl_srv_copychunk_vfs_done(struct tevent_req *subreq);
 
-static NTSTATUS copychunk_check_handles(uint32_t ctl_code,
-					struct files_struct *src_fsp,
-					struct files_struct *dst_fsp)
-{
-	/*
-	 * [MS-SMB2] 3.3.5.15.6 Handling a Server-Side Data Copy Request
-	 * The server MUST fail the request with STATUS_ACCESS_DENIED if any of
-	 * the following are true:
-	 * - The Open.GrantedAccess of the destination file does not include
-	 *   FILE_WRITE_DATA or FILE_APPEND_DATA.
-	 */
-	if (!CHECK_WRITE(dst_fsp)) {
-		DEBUG(5, ("copy chunk no write on dest handle (%s).\n",
-			smb_fname_str_dbg(dst_fsp->fsp_name) ));
-		return NT_STATUS_ACCESS_DENIED;
-	}
-	/*
-	 * - The Open.GrantedAccess of the destination file does not include
-	 *   FILE_READ_DATA, and the CtlCode is FSCTL_SRV_COPYCHUNK.
-	 */
-	if ((ctl_code == FSCTL_SRV_COPYCHUNK) &&
-	    !CHECK_READ_IOCTL(dst_fsp)) {
-		DEBUG(5, ("copy chunk no read on dest handle (%s).\n",
-			smb_fname_str_dbg(dst_fsp->fsp_name) ));
-		return NT_STATUS_ACCESS_DENIED;
-	}
-	/*
-	 * - The Open.GrantedAccess of the source file does not include
-	 *   FILE_READ_DATA access.
-	 */
-	if (!CHECK_READ_SMB2(src_fsp)) {
-		DEBUG(5, ("copy chunk no read on src handle (%s).\n",
-			smb_fname_str_dbg(src_fsp->fsp_name) ));
-		return NT_STATUS_ACCESS_DENIED;
-	}
-
-	if (src_fsp->is_directory) {
-		DEBUG(5, ("copy chunk no read on src directory handle (%s).\n",
-			smb_fname_str_dbg(src_fsp->fsp_name) ));
-		return NT_STATUS_ACCESS_DENIED;
-	}
-
-	if (dst_fsp->is_directory) {
-		DEBUG(5, ("copy chunk no read on dst directory handle (%s).\n",
-			smb_fname_str_dbg(dst_fsp->fsp_name) ));
-		return NT_STATUS_ACCESS_DENIED;
-	}
-
-	if (IS_IPC(src_fsp->conn) || IS_IPC(dst_fsp->conn)) {
-		DEBUG(5, ("copy chunk no access on IPC$ handle.\n"));
-		return NT_STATUS_ACCESS_DENIED;
-	}
-
-	if (IS_PRINT(src_fsp->conn) || IS_PRINT(dst_fsp->conn)) {
-		DEBUG(5, ("copy chunk no access on PRINT handle.\n"));
-		return NT_STATUS_ACCESS_DENIED;
-	}
-
-	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,
@@ -166,8 +107,6 @@ static struct tevent_req *fsctl_srv_copychunk_send(TALLOC_CTX *mem_ctx,
 {
 	struct tevent_req *req = NULL;
 	struct fsctl_srv_copychunk_state *state = NULL;
-	uint64_t src_persistent_h;
-	uint64_t src_volatile_h;
 	enum ndr_err_code ndr_ret;
 	NTSTATUS status;
 
@@ -183,6 +122,8 @@ static struct tevent_req *fsctl_srv_copychunk_send(TALLOC_CTX *mem_ctx,
 	*state = (struct fsctl_srv_copychunk_state) {
 		.conn = dst_fsp->conn,
 		.ev = ev,
+		.ctl_code = ctl_code,
+		.dst_fsp = dst_fsp,
 	};
 
 	if (in_max_output < sizeof(struct srv_copychunk_rsp)) {
@@ -203,26 +144,8 @@ static struct tevent_req *fsctl_srv_copychunk_send(TALLOC_CTX *mem_ctx,
 		return tevent_req_post(req, ev);
 	}
 
-	/* persistent/volatile keys sent as the resume key */
-	src_persistent_h = BVAL(state->cc_copy.source_key, 0);
-	src_volatile_h = BVAL(state->cc_copy.source_key, 8);
-	state->src_fsp = file_fsp_get(smb2req, src_persistent_h, src_volatile_h);
-	if (state->src_fsp == NULL) {
-		DEBUG(3, ("invalid resume key in copy chunk req\n"));
-		state->status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
-		tevent_req_nterror(req, state->status);
-		return tevent_req_post(req, ev);
-	}
-
-	state->dst_fsp = dst_fsp;
-
-	state->status = copychunk_check_handles(ctl_code,
-						state->src_fsp,
-						state->dst_fsp);
-	if (!NT_STATUS_IS_OK(state->status)) {
-		tevent_req_nterror(req, state->status);
-		return tevent_req_post(req, ev);
-	}
+	state->token = data_blob_const(state->cc_copy.source_key,
+				       sizeof(state->cc_copy.source_key));
 
 	state->status = copychunk_check_limits(&state->cc_copy);
 	if (!NT_STATUS_IS_OK(state->status)) {
@@ -272,7 +195,8 @@ static NTSTATUS fsctl_srv_copychunk_loop(struct tevent_req *req)
 	subreq = SMB_VFS_OFFLOAD_WRITE_SEND(state->dst_fsp->conn,
 					 state,
 					 state->ev,
-					 state->src_fsp,
+					 state->ctl_code,
+					 &state->token,
 					 source_off,
 					 state->dst_fsp,
 					 target_off,
diff --git a/source3/smbd/vfs.c b/source3/smbd/vfs.c
index 7bc8aa7..17ef73f 100644
--- a/source3/smbd/vfs.c
+++ b/source3/smbd/vfs.c
@@ -2338,16 +2338,18 @@ NTSTATUS smb_vfs_call_offload_read_recv(struct tevent_req *req,
 struct tevent_req *smb_vfs_call_offload_write_send(struct vfs_handle_struct *handle,
 						   TALLOC_CTX *mem_ctx,
 						   struct tevent_context *ev,
-						   struct files_struct *src_fsp,
-						   off_t src_off,
+						   uint32_t fsctl,
+						   DATA_BLOB *token,
+						   off_t transfer_offset,
 						   struct files_struct *dest_fsp,
 						   off_t dest_off,
 						   off_t num,
 						   uint32_t flags)
 {
 	VFS_FIND(offload_write_send);
-	return handle->fns->offload_write_send_fn(handle, mem_ctx, ev, src_fsp,
-					       src_off, dest_fsp, dest_off, num,
+	return handle->fns->offload_write_send_fn(handle, mem_ctx, ev, fsctl,
+					       token, transfer_offset,
+					       dest_fsp, dest_off, num,
 					       flags);
 }
 
-- 
2.9.4


From c8fa4ac7c50be88b66f91ba195d8a7eda5192d21 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Tue, 6 Jun 2017 14:36:38 +0200
Subject: [PATCH 15/17] s4/torture: more tests for copy-chunk across shares

Signed-off-by: Ralph Boehme <slow at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 source4/torture/smb2/ioctl.c | 166 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 166 insertions(+)

diff --git a/source4/torture/smb2/ioctl.c b/source4/torture/smb2/ioctl.c
index 3ccf7be..9585c11 100644
--- a/source4/torture/smb2/ioctl.c
+++ b/source4/torture/smb2/ioctl.c
@@ -2067,6 +2067,168 @@ done:
 	return ok;
 }
 
+/* Test closing the src handle */
+static bool test_copy_chunk_across_shares2(struct torture_context *tctx,
+					   struct smb2_tree *tree)
+{
+	TALLOC_CTX *mem_ctx = NULL;
+	struct smb2_tree *tree2 = NULL;
+	struct smb2_handle src_h = {{0}};
+	struct smb2_handle dest_h = {{0}};
+	union smb_ioctl ioctl;
+	struct srv_copychunk_copy cc_copy;
+	enum ndr_err_code ndr_ret;
+	NTSTATUS status;
+	bool ok = false;
+
+	mem_ctx = talloc_new(tctx);
+	torture_assert_not_null_goto(tctx, mem_ctx, ok, done,
+				     "talloc_new\n");
+
+	ok = torture_smb2_tree_connect(tctx, tree->session, tctx, &tree2);
+	torture_assert_goto(tctx, ok == true, ok, done,
+			    "torture_smb2_tree_connect failed\n");
+
+	ok = test_setup_copy_chunk(tctx, tree, tree2, mem_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,
+				   &ioctl);
+	torture_assert_goto(tctx, ok == true, ok, done,
+			    "test_setup_copy_chunk failed\n");
+
+	status = smb2_util_close(tree, src_h);
+	torture_assert_ntstatus_ok_goto(tctx, status, ok, done,
+			    "smb2_util_close failed\n");
+	ZERO_STRUCT(src_h);
+
+	cc_copy.chunks[0].source_off = 0;
+	cc_copy.chunks[0].target_off = 0;
+	cc_copy.chunks[0].length = 4096;
+
+	ndr_ret = ndr_push_struct_blob(
+		&ioctl.smb2.in.out, mem_ctx, &cc_copy,
+		(ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
+	torture_assert_ndr_success_goto(tctx, ndr_ret, ok, done,
+					"ndr_push_srv_copychunk_copy\n");
+
+	status = smb2_ioctl(tree2, mem_ctx, &ioctl.smb2);
+	torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND,
+					   ok, done, "smb2_ioctl failed\n");
+
+done:
+	TALLOC_FREE(mem_ctx);
+	if (!smb2_util_handle_empty(src_h)) {
+		smb2_util_close(tree, src_h);
+	}
+	if (!smb2_util_handle_empty(dest_h)) {
+		smb2_util_close(tree2, dest_h);
+	}
+	smb2_util_unlink(tree, FNAME);
+	smb2_util_unlink(tree2, FNAME2);
+	if (tree2 != NULL) {
+		smb2_tdis(tree2);
+	}
+	return ok;
+}
+
+/* Test offset works */
+static bool test_copy_chunk_across_shares3(struct torture_context *tctx,
+					   struct smb2_tree *tree)
+{
+	TALLOC_CTX *mem_ctx = NULL;
+	struct smb2_tree *tree2 = NULL;
+	struct smb2_handle src_h = {{0}};
+	struct smb2_handle dest_h = {{0}};
+	union smb_ioctl ioctl;
+	struct srv_copychunk_copy cc_copy;
+	struct srv_copychunk_rsp cc_rsp;
+	enum ndr_err_code ndr_ret;
+	NTSTATUS status;
+	bool ok = false;
+
+	mem_ctx = talloc_new(tctx);
+	torture_assert_not_null_goto(tctx, mem_ctx, ok, done,
+				     "talloc_new\n");
+
+	ok = torture_smb2_tree_connect(tctx, tree->session, tctx, &tree2);
+	torture_assert_goto(tctx, ok == true, ok, done,
+			    "torture_smb2_tree_connect failed\n");
+
+	ok = test_setup_copy_chunk(tctx, tree, tree2, mem_ctx,
+				   2, /* 2 chunks */
+				   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,
+				   &ioctl);
+	torture_assert_goto(tctx, ok == true, ok, done,
+			    "test_setup_copy_chunk failed\n");
+
+	cc_copy.chunks[0].source_off = 0;
+	cc_copy.chunks[0].target_off = 0;
+	cc_copy.chunks[0].length = 4096;
+
+	/* second chunk appends the same data to the first */
+	cc_copy.chunks[1].source_off = 0;
+	cc_copy.chunks[1].target_off = 4096;
+	cc_copy.chunks[1].length = 4096;
+
+	ndr_ret = ndr_push_struct_blob(
+		&ioctl.smb2.in.out, mem_ctx, &cc_copy,
+		(ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
+	torture_assert_ndr_success_goto(tctx, ndr_ret, ok, done,
+					"ndr_push_srv_copychunk_copy\n");
+
+	status = smb2_ioctl(tree2, mem_ctx, &ioctl.smb2);
+	torture_assert_ntstatus_ok_goto(tctx, status, ok, done, "smb2_ioctl failed\n");
+
+	ndr_ret = ndr_pull_struct_blob(
+		&ioctl.smb2.out.out, mem_ctx, &cc_rsp,
+		(ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
+
+	torture_assert_ndr_success_goto(tctx, ndr_ret, ok, done,
+				   "ndr_pull_srv_copychunk_rsp\n");
+
+	ok = check_copy_chunk_rsp(tctx, &cc_rsp,
+				  2,	/* chunks written */
+				  0,	/* chunk bytes unsuccessfully written */
+				  8192); /* total bytes written */
+	torture_assert_goto(tctx, ok == true, ok, done,
+			    "check_copy_chunk_rsp failed\n");
+
+	ok = check_pattern(tctx, tree2, mem_ctx, dest_h, 0, 4096, 0);
+	torture_assert_goto(tctx, ok == true, ok, done,
+			    "check_pattern failed\n");
+
+	ok = check_pattern(tctx, tree2, mem_ctx, dest_h, 4096, 4096, 0);
+	torture_assert_goto(tctx, ok == true, ok, done,
+			    "check_pattern failed\n");
+
+done:
+	TALLOC_FREE(mem_ctx);
+	if (!smb2_util_handle_empty(src_h)) {
+		smb2_util_close(tree, src_h);
+	}
+	if (!smb2_util_handle_empty(dest_h)) {
+		smb2_util_close(tree2, dest_h);
+	}
+	smb2_util_unlink(tree, FNAME);
+	smb2_util_unlink(tree2, FNAME2);
+	if (tree2 != NULL) {
+		smb2_tdis(tree2);
+	}
+	return ok;
+}
+
 static NTSTATUS test_ioctl_compress_fs_supported(struct torture_context *torture,
 						 struct smb2_tree *tree,
 						 TALLOC_CTX *mem_ctx,
@@ -6512,6 +6674,10 @@ struct torture_suite *torture_smb2_ioctl_init(TALLOC_CTX *ctx)
 				     test_copy_chunk_streams);
 	torture_suite_add_1smb2_test(suite, "copy_chunk_across_shares",
 				     test_copy_chunk_across_shares);
+	torture_suite_add_1smb2_test(suite, "copy_chunk_across_shares2",
+				     test_copy_chunk_across_shares2);
+	torture_suite_add_1smb2_test(suite, "copy_chunk_across_shares3",
+				     test_copy_chunk_across_shares3);
 	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.4


From 61bc5c76da6cf43b5bae33102416fe63a9b461ca Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Fri, 9 Jun 2017 17:27:17 +0200
Subject: [PATCH 16/17] s3/smbd: get rid of
 files_struct.aapl_copyfile_supported

A previous commit removed the special hook from the SMB layer, so we
don't need this anymore.

Signed-off-by: Ralph Boehme <slow at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 source3/include/vfs.h       |  1 -
 source3/modules/vfs_fruit.c | 31 ++++++++++---------------------
 2 files changed, 10 insertions(+), 22 deletions(-)

diff --git a/source3/include/vfs.h b/source3/include/vfs.h
index dab5b50..378920b 100644
--- a/source3/include/vfs.h
+++ b/source3/include/vfs.h
@@ -325,7 +325,6 @@ typedef struct files_struct {
 	bool is_sparse;
 	bool backup_intent; /* Handle was successfully opened with backup intent
 				and opener has privilege to do so. */
-	bool aapl_copyfile_supported;
 	bool use_ofd_locks; /* Are we using open file description locks ? */
 	struct smb_filename *fsp_name;
 	uint32_t name_hash;		/* Jenkins hash of full pathname. */
diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c
index db6324a..36552c1 100644
--- a/source3/modules/vfs_fruit.c
+++ b/source3/modules/vfs_fruit.c
@@ -5124,17 +5124,6 @@ static NTSTATUS fruit_create_file(vfs_handle_struct *handle,
 	fsp = *result;
 
 	if (global_fruit_config.nego_aapl) {
-		if (config->copyfile_enabled) {
-			/*
-			 * Set a flag in the fsp. Gets used in
-			 * copychunk to check whether the special
-			 * Apple copyfile semantics for copychunk
-			 * should be allowed in a copychunk request
-			 * with a count of 0.
-			 */
-			fsp->aapl_copyfile_supported = true;
-		}
-
 		if (config->posix_rename && fsp->is_directory) {
 			/*
 			 * Enable POSIX directory rename behaviour
@@ -5500,6 +5489,7 @@ static struct tevent_req *fruit_offload_write_send(struct vfs_handle_struct *han
 	off_t src_off = transfer_offset;
 	files_struct *src_fsp = NULL;
 	off_t to_copy = num;
+	bool copyfile_enabled = false;
 
 	DEBUG(10,("soff: %ju, doff: %ju, len: %ju\n",
 		  (uintmax_t)src_off, (uintmax_t)dest_off, (uintmax_t)num));
@@ -5519,12 +5509,7 @@ static struct tevent_req *fruit_offload_write_send(struct vfs_handle_struct *han
 	switch (fsctl) {
 	case FSCTL_SRV_COPYCHUNK:
 	case FSCTL_SRV_COPYCHUNK_WRITE:
-		status = vfs_offload_token_db_fetch_fsp(fruit_offload_ctx,
-							token, &src_fsp);
-		if (tevent_req_nterror(req, status)) {
-			return tevent_req_post(req, ev);
-		}
-		state->src_fsp = src_fsp;
+		copyfile_enabled = config->copyfile_enabled;
 		break;
 	default:
 		break;
@@ -5536,10 +5521,14 @@ static struct tevent_req *fruit_offload_write_send(struct vfs_handle_struct *han
 	 * offload_write_send VFS call overloading the parameters src_off
 	 * = dest_off = num = 0.
 	 */
-	if ((src_off == 0) && (dest_off == 0) && (num == 0) &&
-	    src_fsp != NULL && src_fsp->aapl_copyfile_supported &&
-	    dest_fsp->aapl_copyfile_supported)
-	{
+	if (copyfile_enabled && num == 0 && src_off == 0 && dest_off == 0) {
+		status = vfs_offload_token_db_fetch_fsp(
+			fruit_offload_ctx, token, &src_fsp);
+		if (tevent_req_nterror(req, status)) {
+			return tevent_req_post(req, ev);
+		}
+		state->src_fsp = src_fsp;
+
 		status = vfs_stat_fsp(src_fsp);
 		if (tevent_req_nterror(req, status)) {
 			return tevent_req_post(req, ev);
-- 
2.9.4


From 8532a9c0d48af3b61db29f48db18cb7df4b5d220 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Sat, 10 Jun 2017 09:05:55 +0200
Subject: [PATCH 17/17] s3/smbd: remove unneeded flags argument from
 SMB_VFS_OFFLOAD_WRITE_SEND

...and instead use the fsctl to infer required behaviour in the VFS
backends.

Note that this removes the check from vfs_default because there we only
handle FSCTL_SRV_COPYCHUNK(_WRITE) and must always perform the lock
checks.

Signed-off-by: Ralph Boehme <slow at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 examples/VFS/skel_opaque.c           |  3 +-
 examples/VFS/skel_transparent.c      |  5 +-
 source3/include/vfs.h                | 20 +-------
 source3/include/vfs_macros.h         |  8 ++--
 source3/modules/vfs_btrfs.c          | 21 ++++-----
 source3/modules/vfs_default.c        | 91 +++++++++++++++---------------------
 source3/modules/vfs_fruit.c          |  6 +--
 source3/modules/vfs_full_audit.c     |  6 +--
 source3/modules/vfs_time_audit.c     |  5 +-
 source3/smbd/smb2_ioctl_filesys.c    |  4 +-
 source3/smbd/smb2_ioctl_network_fs.c |  3 +-
 source3/smbd/vfs.c                   |  6 +--
 12 files changed, 66 insertions(+), 112 deletions(-)

diff --git a/examples/VFS/skel_opaque.c b/examples/VFS/skel_opaque.c
index 76fa7e4..e5a232b 100644
--- a/examples/VFS/skel_opaque.c
+++ b/examples/VFS/skel_opaque.c
@@ -586,8 +586,7 @@ static struct tevent_req *skel_offload_write_send(struct vfs_handle_struct *hand
 					       off_t transfer_offset,
 					       struct files_struct *dest_fsp,
 					       off_t dest_off,
-					       off_t num,
-					       uint32_t flags)
+					       off_t num)
 {
 	struct tevent_req *req;
 	struct skel_cc_state *cc_state;
diff --git a/examples/VFS/skel_transparent.c b/examples/VFS/skel_transparent.c
index 069cb18..efd0228 100644
--- a/examples/VFS/skel_transparent.c
+++ b/examples/VFS/skel_transparent.c
@@ -720,8 +720,7 @@ static struct tevent_req *skel_offload_write_send(struct vfs_handle_struct *hand
 					       off_t transfer_offset,
 					       struct files_struct *dest_fsp,
 					       off_t dest_off,
-					       off_t num,
-					       uint32_t flags)
+					       off_t num)
 {
 	struct tevent_req *req;
 	struct tevent_req *subreq;
@@ -735,7 +734,7 @@ static struct tevent_req *skel_offload_write_send(struct vfs_handle_struct *hand
 	state->handle = handle;
 	subreq = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle, state, ev,
 					      fsctl, token, transfer_offset,
-					      dest_fsp, dest_off, num, flags);
+					      dest_fsp, dest_off, num);
 	if (tevent_req_nomem(subreq, req)) {
 		return tevent_req_post(req, ev);
 	}
diff --git a/source3/include/vfs.h b/source3/include/vfs.h
index 378920b..fa39ef4 100644
--- a/source3/include/vfs.h
+++ b/source3/include/vfs.h
@@ -582,20 +582,6 @@ enum vfs_fallocate_flags {
 	VFS_FALLOCATE_FL_PUNCH_HOLE		= 0x0002,
 };
 
-/*
- * @VFS_OFFLOAD_WRITE_FL_MUST_CLONE: indicates that offload_write_send_fn() copy must
- *				  be handled as a COW clone, AKA reflink.
- * @VFS_OFFLOAD_WRITE_FL_MASK_ALL: all valid flags.
- */
-enum vfs_offload_write_flags {
-	VFS_OFFLOAD_WRITE_FL_MUST_CLONE		= 0x0001,
-	VFS_OFFLOAD_WRITE_FL_IGNORE_LOCKS	= 0x0002,
-
-	VFS_OFFLOAD_WRITE_FL_MASK_ALL		=
-					(VFS_OFFLOAD_WRITE_FL_MUST_CLONE
-					 | VFS_OFFLOAD_WRITE_FL_IGNORE_LOCKS),
-};
-
 struct vfs_aio_state {
 	int error;
 	uint64_t duration;
@@ -789,8 +775,7 @@ struct vfs_fn_pointers {
 						    off_t transfer_offset,
 						    struct files_struct *dest_fsp,
 						    off_t dest_off,
-						    off_t to_copy,
-						    uint32_t flags);
+						    off_t to_copy);
 	NTSTATUS (*offload_write_recv_fn)(struct vfs_handle_struct *handle,
 					  struct tevent_req *req,
 					  off_t *copied);
@@ -1366,8 +1351,7 @@ struct tevent_req *smb_vfs_call_offload_write_send(struct vfs_handle_struct *han
 						   off_t transfer_offset,
 						   struct files_struct *dest_fsp,
 						   off_t dest_off,
-						   off_t num,
-						   uint32_t flags);
+						   off_t num);
 NTSTATUS smb_vfs_call_offload_write_recv(struct vfs_handle_struct *handle,
 					 struct tevent_req *req,
 					 off_t *copied);
diff --git a/source3/include/vfs_macros.h b/source3/include/vfs_macros.h
index d9593cf..021e6b9 100644
--- a/source3/include/vfs_macros.h
+++ b/source3/include/vfs_macros.h
@@ -425,10 +425,10 @@
 #define SMB_VFS_NEXT_OFFLOAD_READ_RECV(req, handle, mem_ctx, token_blob) \
 	smb_vfs_call_offload_read_recv((req), (handle)->next, (mem_ctx), (token_blob))
 
-#define SMB_VFS_OFFLOAD_WRITE_SEND(conn, mem_ctx, ev, fsctl, token, transfer_offset, dest_fsp, dest_off, num, flags) \
-	smb_vfs_call_offload_write_send((conn)->vfs_handles, (mem_ctx), (ev), (fsctl), (token), (transfer_offset), (dest_fsp), (dest_off), (num), (flags))
-#define SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle, mem_ctx, ev, fsctl, token, transfer_offset, dest_fsp, dest_off, num, flags) \
-	smb_vfs_call_offload_write_send((handle)->next, (mem_ctx), (ev), (fsctl), (token), (transfer_offset), (dest_fsp), (dest_off), (num), (flags))
+#define SMB_VFS_OFFLOAD_WRITE_SEND(conn, mem_ctx, ev, fsctl, token, transfer_offset, dest_fsp, dest_off, num) \
+	smb_vfs_call_offload_write_send((conn)->vfs_handles, (mem_ctx), (ev), (fsctl), (token), (transfer_offset), (dest_fsp), (dest_off), (num))
+#define SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle, mem_ctx, ev, fsctl, token, transfer_offset, dest_fsp, dest_off, num) \
+	smb_vfs_call_offload_write_send((handle)->next, (mem_ctx), (ev), (fsctl), (token), (transfer_offset), (dest_fsp), (dest_off), (num))
 
 #define SMB_VFS_OFFLOAD_WRITE_RECV(conn, req, copied) \
 	smb_vfs_call_offload_write_recv((conn)->vfs_handles, (req), (copied))
diff --git a/source3/modules/vfs_btrfs.c b/source3/modules/vfs_btrfs.c
index f310a3d..0fd4c6c 100644
--- a/source3/modules/vfs_btrfs.c
+++ b/source3/modules/vfs_btrfs.c
@@ -211,8 +211,7 @@ static struct tevent_req *btrfs_offload_write_send(struct vfs_handle_struct *han
 						off_t transfer_offset,
 						struct files_struct *dest_fsp,
 						off_t dest_off,
-						off_t num,
-						uint32_t flags)
+						off_t num)
 {
 	struct tevent_req *req;
 	struct btrfs_cc_state *cc_state;
@@ -223,6 +222,7 @@ static struct tevent_req *btrfs_offload_write_send(struct vfs_handle_struct *han
 	files_struct *src_fsp = NULL;
 	int ret;
 	bool handle_offload_write = true;
+	bool do_locking = false;
 	NTSTATUS status;
 
 	req = tevent_req_create(mem_ctx, &cc_state, struct btrfs_cc_state);
@@ -230,11 +230,6 @@ static struct tevent_req *btrfs_offload_write_send(struct vfs_handle_struct *han
 		return NULL;
 	}
 
-	if (flags & ~VFS_OFFLOAD_WRITE_FL_MASK_ALL) {
-		tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
-		return tevent_req_post(req, ev);
-	}
-
 	cc_state->handle = handle;
 
 	status = vfs_offload_token_db_fetch_fsp(btrfs_offload_ctx,
@@ -246,7 +241,11 @@ static struct tevent_req *btrfs_offload_write_send(struct vfs_handle_struct *han
 	switch (fsctl) {
 	case FSCTL_SRV_COPYCHUNK:
 	case FSCTL_SRV_COPYCHUNK_WRITE:
+		do_locking = true;
+		break;
+
 	case FSCTL_DUP_EXTENTS_TO_FILE:
+		/* dup extents does not use locking */
 		break;
 
 	default:
@@ -271,7 +270,7 @@ static struct tevent_req *btrfs_offload_write_send(struct vfs_handle_struct *han
 								transfer_offset,
 								dest_fsp,
 								dest_off,
-								num, flags);
+								num);
 		if (tevent_req_nomem(cc_state->subreq, req)) {
 			return tevent_req_post(req, ev);
 		}
@@ -304,7 +303,7 @@ static struct tevent_req *btrfs_offload_write_send(struct vfs_handle_struct *han
 		return tevent_req_post(req, ev);
 	}
 
-	if (!(flags & VFS_OFFLOAD_WRITE_FL_IGNORE_LOCKS)) {
+	if (do_locking) {
 		init_strict_lock_struct(src_fsp,
 					src_fsp->op->global->open_persistent_id,
 					src_off,
@@ -336,7 +335,7 @@ static struct tevent_req *btrfs_offload_write_send(struct vfs_handle_struct *han
 	cr_args.src_length = (uint64_t)num;
 
 	ret = ioctl(dest_fsp->fh->fd, BTRFS_IOC_CLONE_RANGE, &cr_args);
-	if (!(flags & VFS_OFFLOAD_WRITE_FL_IGNORE_LOCKS)) {
+	if (do_locking) {
 		SMB_VFS_STRICT_UNLOCK(dest_fsp->conn, dest_fsp, &dest_lck);
 		SMB_VFS_STRICT_UNLOCK(src_fsp->conn, src_fsp, &src_lck);
 	}
@@ -361,7 +360,7 @@ static struct tevent_req *btrfs_offload_write_send(struct vfs_handle_struct *han
 								transfer_offset,
 								dest_fsp,
 								dest_off,
-								num, flags);
+								num);
 		if (tevent_req_nomem(cc_state->subreq, req)) {
 			return tevent_req_post(req, ev);
 		}
diff --git a/source3/modules/vfs_default.c b/source3/modules/vfs_default.c
index 7925734..a8f6f09 100644
--- a/source3/modules/vfs_default.c
+++ b/source3/modules/vfs_default.c
@@ -1689,7 +1689,6 @@ struct vfswrap_offload_write_state {
 	off_t to_copy;
 	off_t remaining;
 	size_t next_io_size;
-	uint32_t flags;
 };
 
 static NTSTATUS vfswrap_offload_write_loop(struct tevent_req *req);
@@ -1703,8 +1702,7 @@ static struct tevent_req *vfswrap_offload_write_send(
 	off_t transfer_offset,
 	struct files_struct *dest_fsp,
 	off_t dest_off,
-	off_t to_copy,
-	uint32_t flags)
+	off_t to_copy)
 {
 	struct tevent_req *req;
 	struct vfswrap_offload_write_state *state = NULL;
@@ -1718,17 +1716,6 @@ static struct tevent_req *vfswrap_offload_write_send(
 		return NULL;
 	}
 
-	if (flags & ~VFS_OFFLOAD_WRITE_FL_MASK_ALL) {
-		tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
-		return tevent_req_post(req, ev);
-	}
-
-	if (flags & VFS_OFFLOAD_WRITE_FL_MUST_CLONE) {
-		DEBUG(10, ("COW clones not supported by vfs_default\n"));
-		tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
-		return tevent_req_post(req, ev);
-	}
-
 	*state = (struct vfswrap_offload_write_state) {
 		.ev = ev,
 		.token = token,
@@ -1737,7 +1724,6 @@ static struct tevent_req *vfswrap_offload_write_send(
 		.dst_off = dest_off,
 		.to_copy = to_copy,
 		.remaining = to_copy,
-		.flags = flags,
 	};
 
 	switch (fsctl) {
@@ -1749,6 +1735,11 @@ static struct tevent_req *vfswrap_offload_write_send(
 		tevent_req_nterror(req, NT_STATUS_NOT_IMPLEMENTED);
 		return tevent_req_post(req, ev);
 
+	case FSCTL_DUP_EXTENTS_TO_FILE:
+		DBG_DEBUG("COW clones not supported by vfs_default\n");
+		tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+		return tevent_req_post(req, ev);
+
 	default:
 		tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
 		return tevent_req_post(req, ev);
@@ -1832,20 +1823,18 @@ static NTSTATUS vfswrap_offload_write_loop(struct tevent_req *req)
 
 	state->next_io_size = MIN(state->remaining, talloc_array_length(state->buf));
 
-	if (!(state->flags & VFS_OFFLOAD_WRITE_FL_IGNORE_LOCKS)) {
-		init_strict_lock_struct(state->src_fsp,
+	init_strict_lock_struct(state->src_fsp,
 				state->src_fsp->op->global->open_persistent_id,
-					state->src_off,
-					state->next_io_size,
-					READ_LOCK,
-					&state->read_lck);
-
-		ok = SMB_VFS_STRICT_LOCK(state->src_fsp->conn,
-					 state->src_fsp,
-					 &state->read_lck);
-		if (!ok) {
-			return NT_STATUS_FILE_LOCK_CONFLICT;
-		}
+				state->src_off,
+				state->next_io_size,
+				READ_LOCK,
+				&state->read_lck);
+
+	ok = SMB_VFS_STRICT_LOCK(state->src_fsp->conn,
+				 state->src_fsp,
+				 &state->read_lck);
+	if (!ok) {
+		return NT_STATUS_FILE_LOCK_CONFLICT;
 	}
 
 	subreq = SMB_VFS_PREAD_SEND(state,
@@ -1874,12 +1863,10 @@ static void vfswrap_offload_write_read_done(struct tevent_req *subreq)
 	ssize_t nread;
 	bool ok;
 
-	if (!(state->flags & VFS_OFFLOAD_WRITE_FL_IGNORE_LOCKS)) {
-		SMB_VFS_STRICT_UNLOCK(state->src_fsp->conn,
-				      state->src_fsp,
-				      &state->read_lck);
-		ZERO_STRUCT(state->read_lck);
-	}
+	SMB_VFS_STRICT_UNLOCK(state->src_fsp->conn,
+			      state->src_fsp,
+			      &state->read_lck);
+	ZERO_STRUCT(state->read_lck);
 
 	nread = SMB_VFS_PREAD_RECV(subreq, &aio_state);
 	TALLOC_FREE(subreq);
@@ -1897,21 +1884,19 @@ static void vfswrap_offload_write_read_done(struct tevent_req *subreq)
 
 	state->src_off += nread;
 
-	if (!(state->flags & VFS_OFFLOAD_WRITE_FL_IGNORE_LOCKS)) {
-		init_strict_lock_struct(state->dst_fsp,
+	init_strict_lock_struct(state->dst_fsp,
 				state->dst_fsp->op->global->open_persistent_id,
-					state->dst_off,
-					state->next_io_size,
-					WRITE_LOCK,
-					&state->write_lck);
-
-		ok = SMB_VFS_STRICT_LOCK(state->dst_fsp->conn,
-					 state->dst_fsp,
-					 &state->write_lck);
-		if (!ok) {
-			tevent_req_nterror(req, NT_STATUS_FILE_LOCK_CONFLICT);
-			return;
-		}
+				state->dst_off,
+				state->next_io_size,
+				WRITE_LOCK,
+				&state->write_lck);
+
+	ok = SMB_VFS_STRICT_LOCK(state->dst_fsp->conn,
+				 state->dst_fsp,
+				 &state->write_lck);
+	if (!ok) {
+		tevent_req_nterror(req, NT_STATUS_FILE_LOCK_CONFLICT);
+		return;
 	}
 
 	subreq = SMB_VFS_PWRITE_SEND(state,
@@ -1937,12 +1922,10 @@ static void vfswrap_offload_write_write_done(struct tevent_req *subreq)
 	ssize_t nwritten;
 	NTSTATUS status;
 
-	if (!(state->flags & VFS_OFFLOAD_WRITE_FL_IGNORE_LOCKS)) {
-		SMB_VFS_STRICT_UNLOCK(state->dst_fsp->conn,
-				      state->dst_fsp,
-				      &state->write_lck);
-		ZERO_STRUCT(state->write_lck);
-	}
+	SMB_VFS_STRICT_UNLOCK(state->dst_fsp->conn,
+			      state->dst_fsp,
+			      &state->write_lck);
+	ZERO_STRUCT(state->write_lck);
 
 	nwritten = SMB_VFS_PWRITE_RECV(subreq, &aio_state);
 	TALLOC_FREE(subreq);
diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c
index 36552c1..b49ee1c 100644
--- a/source3/modules/vfs_fruit.c
+++ b/source3/modules/vfs_fruit.c
@@ -5479,8 +5479,7 @@ static struct tevent_req *fruit_offload_write_send(struct vfs_handle_struct *han
 						off_t transfer_offset,
 						struct files_struct *dest_fsp,
 						off_t dest_off,
-						off_t num,
-						uint32_t flags)
+						off_t num)
 {
 	struct tevent_req *req, *subreq;
 	struct fruit_offload_write_state *state;
@@ -5546,8 +5545,7 @@ static struct tevent_req *fruit_offload_write_send(struct vfs_handle_struct *han
 					      transfer_offset,
 					      dest_fsp,
 					      dest_off,
-					      to_copy,
-					      flags);
+					      to_copy);
 	if (tevent_req_nomem(subreq, req)) {
 		return tevent_req_post(req, ev);
 	}
diff --git a/source3/modules/vfs_full_audit.c b/source3/modules/vfs_full_audit.c
index 66fe7d8..e45b31c 100644
--- a/source3/modules/vfs_full_audit.c
+++ b/source3/modules/vfs_full_audit.c
@@ -1945,15 +1945,13 @@ static struct tevent_req *smb_full_audit_offload_write_send(struct vfs_handle_st
 							 off_t transfer_offset,
 							 struct files_struct *dest_fsp,
 							 off_t dest_off,
-							 off_t num,
-							 uint32_t flags)
+							    off_t num)
 {
 	struct tevent_req *req;
 
 	req = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle, mem_ctx, ev,
 					   fsctl, token, transfer_offset,
-					   dest_fsp, dest_off, num,
-					   flags);
+					   dest_fsp, dest_off, num);
 
 	do_log(SMB_VFS_OP_OFFLOAD_WRITE_SEND, req, handle, "");
 
diff --git a/source3/modules/vfs_time_audit.c b/source3/modules/vfs_time_audit.c
index 6e71343..803084f 100644
--- a/source3/modules/vfs_time_audit.c
+++ b/source3/modules/vfs_time_audit.c
@@ -2003,8 +2003,7 @@ static struct tevent_req *smb_time_audit_offload_write_send(struct vfs_handle_st
 							 off_t transfer_offset,
 							 struct files_struct *dest_fsp,
 							 off_t dest_off,
-							 off_t num,
-							 uint32_t flags)
+							 off_t num)
 {
 	struct tevent_req *req;
 	struct tevent_req *subreq;
@@ -2020,7 +2019,7 @@ static struct tevent_req *smb_time_audit_offload_write_send(struct vfs_handle_st
 	clock_gettime_mono(&state->ts_send);
 	subreq = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle, state, ev,
 					      fsctl, token, transfer_offset,
-					      dest_fsp, dest_off, num, flags);
+					      dest_fsp, dest_off, num);
 	if (tevent_req_nomem(subreq, req)) {
 		return tevent_req_post(req, ev);
 	}
diff --git a/source3/smbd/smb2_ioctl_filesys.c b/source3/smbd/smb2_ioctl_filesys.c
index fa1cb93..732e3ab 100644
--- a/source3/smbd/smb2_ioctl_filesys.c
+++ b/source3/smbd/smb2_ioctl_filesys.c
@@ -295,9 +295,7 @@ static void fsctl_dup_extents_offload_read_done(struct tevent_req *subreq)
 					    state->dup_extents.source_off,
 					    state->dst_fsp,
 					    state->dup_extents.target_off,
-					    state->dup_extents.byte_count,
-					    VFS_OFFLOAD_WRITE_FL_MUST_CLONE
-					    | VFS_OFFLOAD_WRITE_FL_IGNORE_LOCKS);
+					    state->dup_extents.byte_count);
 	if (tevent_req_nomem(subreq, req)) {
 		return;
 	}
diff --git a/source3/smbd/smb2_ioctl_network_fs.c b/source3/smbd/smb2_ioctl_network_fs.c
index 993f6f8..4006ccf 100644
--- a/source3/smbd/smb2_ioctl_network_fs.c
+++ b/source3/smbd/smb2_ioctl_network_fs.c
@@ -200,8 +200,7 @@ static NTSTATUS fsctl_srv_copychunk_loop(struct tevent_req *req)
 					 source_off,
 					 state->dst_fsp,
 					 target_off,
-					 length,
-					 0);
+					 length);
 	if (tevent_req_nomem(subreq, req)) {
 		return NT_STATUS_NO_MEMORY;
 	}
diff --git a/source3/smbd/vfs.c b/source3/smbd/vfs.c
index 17ef73f..947355a 100644
--- a/source3/smbd/vfs.c
+++ b/source3/smbd/vfs.c
@@ -2343,14 +2343,12 @@ struct tevent_req *smb_vfs_call_offload_write_send(struct vfs_handle_struct *han
 						   off_t transfer_offset,
 						   struct files_struct *dest_fsp,
 						   off_t dest_off,
-						   off_t num,
-						   uint32_t flags)
+						   off_t num)
 {
 	VFS_FIND(offload_write_send);
 	return handle->fns->offload_write_send_fn(handle, mem_ctx, ev, fsctl,
 					       token, transfer_offset,
-					       dest_fsp, dest_off, num,
-					       flags);
+					       dest_fsp, dest_off, num);
 }
 
 NTSTATUS smb_vfs_call_offload_write_recv(struct vfs_handle_struct *handle,
-- 
2.9.4



More information about the samba-technical mailing list