[PATCHSET] support FSCTL_DUPLICATE_EXTENTS_TO_FILE

Ralph Böhme slow at samba.org
Mon May 8 09:05:38 UTC 2017


On Mon, May 08, 2017 at 02:22:11AM +0200, David Disseldorp wrote:
> Thanks a lot for the review, Ralph!
> 
> Please find a version 2 patchset attached. Further comments below...

better, but now it has a memory leak. :)

Fix for that and an additional fixup attached.

> > > - The server behaviour for success on truncated clone (where the
> > >   dest file's allocation size is less than the size of the clone) seems
> > >   prone to races, especially given that the operation ignores locks.
> > >   + See test_ioctl_dup_extents_len_beyond_dest(). Dochelp are aware of
> > >     this inconsistency.
> > >   + There's not much we can do here, as we need to match Windows Server
> > >     2016 + ReFS behaviour.  
> > 
> > ok.
> > 
> > [PATCH 03/10] vfs: add parameter to copy chunk VFS function to handle dup_extents
> > 
> > TODO: please move "[ddiss at samba.org: split VFS API change from  ioctl code]" to subject body
> 
> For the kernel this is a pretty common way of flagging changes which
> have been made after an author (aaptel) has added his sign off:
> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/process/submitting-patches.rst#n466
> If Aurélien says that he's fine with the change as it is now, then I'll
> drop the line completely.

yup. But afaict for us the signed-off tag already implies the person worked on
the patch and they all jointly agree that the state of the patch is ok. So I
don't really see a need for this extra flag.

> > [PATCH 4/10] smbd/smb2_ioctl: add support for FSCTL_DUPLICATE_EXTENTS_TO_FILE
> > 
> > - initialize all pointer vars to NULL
> > - use DEBUG helper macros DBG_ERR, DBG_INFO, ...
> > - fsctl_dup_extents_send/recv() must create a tevent_req and return that
> >   and not just return the SMB_VFS_COPY_CHUNK_SEND subreq
> > - empty line between var declaration and subsequent first statement
> 
> I've added a SQUASH commit with these changes. If you and Aurélien are
> okay with it then I'll squash it in.

see above.

> > I don't see clone-range on the wire, trace attached. Am I missing something?
> 
> Interesting, thanks for checking this. I've been using the cifs.ko
> client until now.

ah, ok. Which kernel version do I need? I have 4.10.13-200.fc25.x86_64

-slow
-------------- next part --------------
From ac40949b09289d89bf819a1b206fbc5dbe780454 Mon Sep 17 00:00:00 2001
From: David Disseldorp <ddiss at samba.org>
Date: Wed, 3 May 2017 09:26:43 +0200
Subject: [PATCH 01/12] torture/ioctl: fix dup_extents destination truncate
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

The dup_extents_compressed_dest test fails to correctly truncate the
dup_extents destination. Fix it.

Signed-off-by: David Disseldorp <ddiss at samba.org>
Reviewed-by: Ralph Böhme <slow at samba.org>
---
 source4/torture/smb2/ioctl.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/source4/torture/smb2/ioctl.c b/source4/torture/smb2/ioctl.c
index 53476fa..bd8310d 100644
--- a/source4/torture/smb2/ioctl.c
+++ b/source4/torture/smb2/ioctl.c
@@ -5847,7 +5847,7 @@ static bool test_ioctl_dup_extents_compressed_dest(struct torture_context *tctx,
 	ZERO_STRUCT(sinfo);
 	sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
 	sinfo.end_of_file_info.in.file.handle = dest_h;
-	sinfo.end_of_file_info.in.size = dup_ext_buf.byte_count;
+	sinfo.end_of_file_info.in.size = 4096;
 	status = smb2_setinfo_file(tree, &sinfo);
 	torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file");
 
-- 
2.9.3


From 4aced6c4a36f9c15dc79c1b7448f1efc78c06872 Mon Sep 17 00:00:00 2001
From: David Disseldorp <ddiss at samba.org>
Date: Fri, 30 Sep 2016 21:35:09 +0200
Subject: [PATCH 02/12] torture/ioctl: expect dup_extents(dest=compressed) to
 pass
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

The MS-FSCC spec doesn't mention anything about dup-extents against
compressed files.
This can't be tested against Windows, as ReFS doesn't support
compression, but COW clones of compressed files work on Btrfs.

Signed-off-by: David Disseldorp <ddiss at samba.org>
Reviewed-by: Ralph Böhme <slow at samba.org>
---
 source4/torture/smb2/ioctl.c | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/source4/torture/smb2/ioctl.c b/source4/torture/smb2/ioctl.c
index bd8310d..81f25a7 100644
--- a/source4/torture/smb2/ioctl.c
+++ b/source4/torture/smb2/ioctl.c
@@ -5803,7 +5803,7 @@ static bool test_ioctl_dup_extents_compressed_src(struct torture_context *tctx,
 }
 
 static bool test_ioctl_dup_extents_compressed_dest(struct torture_context *tctx,
-					       struct smb2_tree *tree)
+						   struct smb2_tree *tree)
 {
 	struct smb2_handle src_h;
 	struct smb2_handle dest_h;
@@ -5863,8 +5863,13 @@ static bool test_ioctl_dup_extents_compressed_dest(struct torture_context *tctx,
 				   "ndr_push_fsctl_dup_extents_to_file");
 
 	status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
-	torture_assert_ntstatus_equal(tctx, status, NT_STATUS_NOT_SUPPORTED,
-				      "FSCTL_DUP_EXTENTS_TO_FILE");
+	torture_assert_ntstatus_ok(tctx, status,
+				   "FSCTL_DUP_EXTENTS_TO_FILE");
+
+	ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 4096, 0);
+	if (!ok) {
+		torture_fail(tctx, "inconsistent file data");
+	}
 
 	smb2_util_close(tree, src_h);
 	smb2_util_close(tree, dest_h);
-- 
2.9.3


From 8e5f0f8b6fd08dfb80843b4f4d0bbbc6b840ac83 Mon Sep 17 00:00:00 2001
From: Aurelien Aptel <aaptel at suse.com>
Date: Mon, 24 Aug 2015 18:22:56 +0200
Subject: [PATCH 03/12] vfs: add parameter to copy chunk VFS function to handle
 dup_extents

FSCTL_DUPLICATE_EXTENTS_TO_FILE must be handled as a COW clone. Add a
copy-chunk flags parameter to the VFS to handle this.

Signed-off-by: Aurelien Aptel <aaptel at suse.com>
[ddiss at samba.org: split VFS API change from ioctl code]
Signed-off-by: David Disseldorp <ddiss at samba.org>
---
 examples/VFS/skel_opaque.c           |  3 ++-
 examples/VFS/skel_transparent.c      |  5 +++--
 source3/include/vfs.h                | 18 ++++++++++++++++--
 source3/include/vfs_macros.h         |  8 ++++----
 source3/modules/vfs_btrfs.c          | 15 ++++++++++++---
 source3/modules/vfs_default.c        | 14 +++++++++++++-
 source3/modules/vfs_fruit.c          |  6 ++++--
 source3/modules/vfs_full_audit.c     |  6 ++++--
 source3/modules/vfs_time_audit.c     |  5 +++--
 source3/smbd/smb2_ioctl_network_fs.c |  4 +++-
 source3/smbd/vfs.c                   |  6 ++++--
 11 files changed, 68 insertions(+), 22 deletions(-)

diff --git a/examples/VFS/skel_opaque.c b/examples/VFS/skel_opaque.c
index ffd951c..af119f3 100644
--- a/examples/VFS/skel_opaque.c
+++ b/examples/VFS/skel_opaque.c
@@ -531,7 +531,8 @@ static struct tevent_req *skel_copy_chunk_send(struct vfs_handle_struct *handle,
 					       off_t src_off,
 					       struct files_struct *dest_fsp,
 					       off_t dest_off,
-					       off_t num)
+					       off_t num,
+					       uint32_t flags)
 {
 	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 afadbc1..e974529 100644
--- a/examples/VFS/skel_transparent.c
+++ b/examples/VFS/skel_transparent.c
@@ -619,7 +619,8 @@ static struct tevent_req *skel_copy_chunk_send(struct vfs_handle_struct *handle,
 					       off_t src_off,
 					       struct files_struct *dest_fsp,
 					       off_t dest_off,
-					       off_t num)
+					       off_t num,
+					       uint32_t flags)
 {
 	struct tevent_req *req;
 	struct tevent_req *subreq;
@@ -633,7 +634,7 @@ static struct tevent_req *skel_copy_chunk_send(struct vfs_handle_struct *handle,
 	cc_state->handle = handle;
 	subreq = SMB_VFS_NEXT_COPY_CHUNK_SEND(handle, cc_state, ev,
 					      src_fsp, src_off,
-					      dest_fsp, dest_off, num);
+					      dest_fsp, dest_off, num, flags);
 	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 f6df93a..fd493e4 100644
--- a/source3/include/vfs.h
+++ b/source3/include/vfs.h
@@ -196,6 +196,7 @@
 /* Bump to version 36 - Samba 4.6 will ship with that */
 /* Version 36 - Remove is_offline and set_offline */
 /* Version 37 - Module init functions now take a TALLOC_CTX * parameter. */
+/* Version 37 - Add vfs_copy_chunk_flags for DUP_EXTENTS_TO_FILE */
 
 #define SMB_VFS_INTERFACE_VERSION 37
 
@@ -548,6 +549,17 @@ enum vfs_fallocate_flags {
 	VFS_FALLOCATE_FL_PUNCH_HOLE		= 0x0002,
 };
 
+/*
+ * @VFS_COPY_CHUNK_FL_MUST_CLONE: indicates that copy_chunk_send_fn() copy must
+ *				  be handled as a COW clone, AKA reflink.
+ * @VFS_COPY_CHUNK_FL_MASK_ALL: all valid copychunk flags.
+ */
+enum vfs_copy_chunk_flags {
+	VFS_COPY_CHUNK_FL_MUST_CLONE		= 0x0001,
+
+	VFS_COPY_CHUNK_FL_MASK_ALL		= 0x0001,
+};
+
 struct vfs_aio_state {
 	int error;
 	uint64_t duration;
@@ -709,7 +721,8 @@ struct vfs_fn_pointers {
 						 off_t src_off,
 						 struct files_struct *dest_fsp,
 						 off_t dest_off,
-						 off_t num);
+						 off_t to_copy,
+						 uint32_t flags);
 	NTSTATUS (*copy_chunk_recv_fn)(struct vfs_handle_struct *handle,
 				       struct tevent_req *req,
 				       off_t *copied);
@@ -1241,7 +1254,8 @@ struct tevent_req *smb_vfs_call_copy_chunk_send(struct vfs_handle_struct *handle
 						off_t src_off,
 						struct files_struct *dest_fsp,
 						off_t dest_off,
-						off_t num);
+						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);
diff --git a/source3/include/vfs_macros.h b/source3/include/vfs_macros.h
index 40c93f8..0cbcf89 100644
--- a/source3/include/vfs_macros.h
+++ b/source3/include/vfs_macros.h
@@ -415,10 +415,10 @@
 #define SMB_VFS_NEXT_FSET_DOS_ATTRIBUTES(handle, fsp, attributes) \
 	smb_vfs_call_fset_dos_attributes((handle)->next, (fsp), (attributes))
 
-#define SMB_VFS_COPY_CHUNK_SEND(conn, mem_ctx, ev, src_fsp, src_off, dest_fsp, dest_off, num) \
-	smb_vfs_call_copy_chunk_send((conn)->vfs_handles, (mem_ctx), (ev), (src_fsp), (src_off), (dest_fsp), (dest_off), (num))
-#define SMB_VFS_NEXT_COPY_CHUNK_SEND(handle, mem_ctx, ev, src_fsp, src_off, dest_fsp, dest_off, num) \
-	smb_vfs_call_copy_chunk_send((handle)->next, (mem_ctx), (ev), (src_fsp), (src_off), (dest_fsp), (dest_off), (num))
+#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))
diff --git a/source3/modules/vfs_btrfs.c b/source3/modules/vfs_btrfs.c
index ab4fd99..bf0a080 100644
--- a/source3/modules/vfs_btrfs.c
+++ b/source3/modules/vfs_btrfs.c
@@ -92,7 +92,8 @@ static struct tevent_req *btrfs_copy_chunk_send(struct vfs_handle_struct *handle
 						off_t src_off,
 						struct files_struct *dest_fsp,
 						off_t dest_off,
-						off_t num)
+						off_t num,
+						uint32_t flags)
 {
 	struct tevent_req *req;
 	struct btrfs_cc_state *cc_state;
@@ -106,6 +107,12 @@ static struct tevent_req *btrfs_copy_chunk_send(struct vfs_handle_struct *handle
 	if (req == NULL) {
 		return NULL;
 	}
+
+	if (flags & ~VFS_COPY_CHUNK_FL_MASK_ALL) {
+		tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+		return tevent_req_post(req, ev);
+	}
+
 	cc_state->handle = handle;
 
 	if (num == 0) {
@@ -119,7 +126,8 @@ static struct tevent_req *btrfs_copy_chunk_send(struct vfs_handle_struct *handle
 								src_fsp,
 								src_off,
 								dest_fsp,
-								dest_off, num);
+								dest_off,
+								num, flags);
 		if (tevent_req_nomem(cc_state->subreq, req)) {
 			return tevent_req_post(req, ev);
 		}
@@ -196,7 +204,8 @@ static struct tevent_req *btrfs_copy_chunk_send(struct vfs_handle_struct *handle
 								src_fsp,
 								src_off,
 								dest_fsp,
-								dest_off, num);
+								dest_off,
+								num, flags);
 		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 d660120..ffdc40a 100644
--- a/source3/modules/vfs_default.c
+++ b/source3/modules/vfs_default.c
@@ -1616,7 +1616,8 @@ static struct tevent_req *vfswrap_copy_chunk_send(struct vfs_handle_struct *hand
 						  off_t src_off,
 						  struct files_struct *dest_fsp,
 						  off_t dest_off,
-						  off_t to_copy)
+						  off_t to_copy,
+						  uint32_t flags)
 {
 	struct tevent_req *req;
 	struct vfs_cc_state *state = NULL;
@@ -1630,6 +1631,17 @@ static struct tevent_req *vfswrap_copy_chunk_send(struct vfs_handle_struct *hand
 		return NULL;
 	}
 
+	if (flags & ~VFS_COPY_CHUNK_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) {
+		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) {
 		.ev = ev,
 		.src_fsp = src_fsp,
diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c
index 273540e..ee8cdb7 100644
--- a/source3/modules/vfs_fruit.c
+++ b/source3/modules/vfs_fruit.c
@@ -5233,7 +5233,8 @@ static struct tevent_req *fruit_copy_chunk_send(struct vfs_handle_struct *handle
 						off_t src_off,
 						struct files_struct *dest_fsp,
 						off_t dest_off,
-						off_t num)
+						off_t num,
+						uint32_t flags)
 {
 	struct tevent_req *req, *subreq;
 	struct fruit_copy_chunk_state *fruit_copy_chunk_state;
@@ -5283,7 +5284,8 @@ static struct tevent_req *fruit_copy_chunk_send(struct vfs_handle_struct *handle
 					      src_off,
 					      dest_fsp,
 					      dest_off,
-					      to_copy);
+					      to_copy,
+					      flags);
 	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 6614fbb..4c2eb87 100644
--- a/source3/modules/vfs_full_audit.c
+++ b/source3/modules/vfs_full_audit.c
@@ -1889,12 +1889,14 @@ static struct tevent_req *smb_full_audit_copy_chunk_send(struct vfs_handle_struc
 							 off_t src_off,
 							 struct files_struct *dest_fsp,
 							 off_t dest_off,
-							 off_t num)
+							 off_t num,
+							 uint32_t flags)
 {
 	struct tevent_req *req;
 
 	req = SMB_VFS_NEXT_COPY_CHUNK_SEND(handle, mem_ctx, ev, src_fsp,
-					   src_off, dest_fsp, dest_off, num);
+					   src_off, dest_fsp, dest_off, num,
+					   flags);
 
 	do_log(SMB_VFS_OP_COPY_CHUNK_SEND, req, handle, "");
 
diff --git a/source3/modules/vfs_time_audit.c b/source3/modules/vfs_time_audit.c
index 2ac3d97..52bd00e 100644
--- a/source3/modules/vfs_time_audit.c
+++ b/source3/modules/vfs_time_audit.c
@@ -1886,7 +1886,8 @@ static struct tevent_req *smb_time_audit_copy_chunk_send(struct vfs_handle_struc
 							 off_t src_off,
 							 struct files_struct *dest_fsp,
 							 off_t dest_off,
-							 off_t num)
+							 off_t num,
+							 uint32_t flags)
 {
 	struct tevent_req *req;
 	struct tevent_req *subreq;
@@ -1901,7 +1902,7 @@ static struct tevent_req *smb_time_audit_copy_chunk_send(struct vfs_handle_struc
 	clock_gettime_mono(&cc_state->ts_send);
 	subreq = SMB_VFS_NEXT_COPY_CHUNK_SEND(handle, cc_state, ev,
 					      src_fsp, src_off,
-					      dest_fsp, dest_off, num);
+					      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_network_fs.c b/source3/smbd/smb2_ioctl_network_fs.c
index 5b869ec..7b496ef 100644
--- a/source3/smbd/smb2_ioctl_network_fs.c
+++ b/source3/smbd/smb2_ioctl_network_fs.c
@@ -347,6 +347,7 @@ static NTSTATUS fsctl_srv_copychunk_loop(struct tevent_req *req)
 						 0,
 						 state->dst_fsp,
 						 0,
+						 0,
 						 0);
 		if (subreq == NULL) {
 			return NT_STATUS_NO_MEMORY;
@@ -366,7 +367,8 @@ static NTSTATUS fsctl_srv_copychunk_loop(struct tevent_req *req)
 					 chunk->source_off,
 					 state->dst_fsp,
 					 chunk->target_off,
-					 length);
+					 length,
+					 0);
 	if (tevent_req_nomem(subreq, req)) {
 		return NT_STATUS_NO_MEMORY;
 	}
diff --git a/source3/smbd/vfs.c b/source3/smbd/vfs.c
index f75172a..560c4b2 100644
--- a/source3/smbd/vfs.c
+++ b/source3/smbd/vfs.c
@@ -2305,11 +2305,13 @@ struct tevent_req *smb_vfs_call_copy_chunk_send(struct vfs_handle_struct *handle
 						off_t src_off,
 						struct files_struct *dest_fsp,
 						off_t dest_off,
-						off_t num)
+						off_t num,
+						uint32_t flags)
 {
 	VFS_FIND(copy_chunk_send);
 	return handle->fns->copy_chunk_send_fn(handle, mem_ctx, ev, src_fsp,
-					       src_off, dest_fsp, dest_off, num);
+					       src_off, dest_fsp, dest_off, num,
+					       flags);
 }
 
 NTSTATUS smb_vfs_call_copy_chunk_recv(struct vfs_handle_struct *handle,
-- 
2.9.3


From 414c3853cccbb91b81c8bdcef1e079cfcc65ea69 Mon Sep 17 00:00:00 2001
From: Aurelien Aptel <aaptel at suse.com>
Date: Thu, 30 Jul 2015 16:16:15 +0200
Subject: [PATCH 04/12] smbd/smb2_ioctl: add support for
 FSCTL_DUPLICATE_EXTENTS_TO_FILE

Signed-off-by: Aurelien Aptel <aaptel at suse.com>
Signed-off-by: David Disseldorp <ddiss at samba.org>
---
 source3/smbd/smb2_ioctl_filesys.c | 141 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 141 insertions(+)

diff --git a/source3/smbd/smb2_ioctl_filesys.c b/source3/smbd/smb2_ioctl_filesys.c
index 64b5454..9f1d4a9 100644
--- a/source3/smbd/smb2_ioctl_filesys.c
+++ b/source3/smbd/smb2_ioctl_filesys.c
@@ -31,6 +31,117 @@
 #include "librpc/gen_ndr/ndr_ioctl.h"
 #include "smb2_ioctl_private.h"
 
+struct fsctl_dup_extents_state {
+	struct tevent_context *ev;
+	struct connection_struct *conn;
+};
+static void fsctl_dup_extents_vfs_done(struct tevent_req *subreq);
+
+static struct tevent_req *fsctl_dup_extents_send(TALLOC_CTX *mem_ctx,
+						 struct tevent_context *ev,
+						 struct files_struct *dst_fsp,
+						 DATA_BLOB *in_input,
+						 struct smbd_smb2_request *smb2req)
+{
+	struct tevent_req *req = NULL;
+	struct tevent_req *subreq = NULL;
+	struct fsctl_dup_extents_state *state = NULL;
+	struct fsctl_dup_extents_to_file dup_extents;
+	uint64_t src_fid_persistent = 0;
+	uint64_t src_fid_volatile = 0;
+	struct files_struct *src_fsp = NULL;
+	int ndr_ret;
+
+	req = tevent_req_create(mem_ctx, &state,
+				struct fsctl_dup_extents_state);
+	if (req == NULL) {
+		return NULL;
+	}
+	*state = (struct fsctl_dup_extents_state) {
+		.conn = dst_fsp->conn,
+		.ev = ev,
+	};
+
+	if (dst_fsp == NULL) {
+		tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+		return tevent_req_post(req, ev);
+	}
+
+	if ((dst_fsp->conn->fs_capabilities
+				& FILE_SUPPORTS_BLOCK_REFCOUNTING) == 0) {
+		DBG_INFO("FS does not advertise block refcounting support\n");
+		tevent_req_nterror(req, NT_STATUS_INVALID_DEVICE_REQUEST);
+		return tevent_req_post(req, ev);
+	}
+
+	ndr_ret = ndr_pull_struct_blob(in_input, mem_ctx, &dup_extents,
+		       (ndr_pull_flags_fn_t)ndr_pull_fsctl_dup_extents_to_file);
+	if (ndr_ret != NDR_ERR_SUCCESS) {
+		DBG_ERR("failed to unmarshall dup extents to file req\n");
+		tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+		return tevent_req_post(req, ev);
+	}
+
+	src_fid_persistent = BVAL(dup_extents.source_fid, 0);
+	src_fid_volatile = BVAL(dup_extents.source_fid, 8);
+	src_fsp = file_fsp_get(smb2req, src_fid_persistent, src_fid_volatile);
+	if ((src_fsp == NULL)
+		      || (src_fsp->file_id.devid != dst_fsp->file_id.devid)) {
+		/*
+		 * [MS-FSCC] 2.3.8 FSCTL_DUPLICATE_EXTENTS_TO_FILE Reply
+		 * STATUS_INVALID_PARAMETER:
+		 * The FileHandle parameter is either invalid or does not
+		 * represent a handle to an opened file on the same volume.
+		 *
+		 * Windows Server responds with NT_STATUS_INVALID_HANDLE instead
+		 * of STATUS_INVALID_PARAMETER here, despite the above spec.
+		 */
+		DBG_ERR("invalid src_fsp for dup_extents\n");
+		tevent_req_nterror(req, NT_STATUS_INVALID_HANDLE);
+		return tevent_req_post(req, ev);
+	}
+
+	if (dup_extents.byte_count == 0) {
+		DBG_ERR("skipping zero length dup extents\n");
+		tevent_req_done(req);
+		return tevent_req_post(req, ev);
+	}
+
+	subreq = SMB_VFS_COPY_CHUNK_SEND(dst_fsp->conn, mem_ctx, ev,
+					 src_fsp, dup_extents.source_off,
+					 dst_fsp, dup_extents.target_off,
+					 dup_extents.byte_count,
+					 VFS_COPY_CHUNK_FL_MUST_CLONE);
+	if (tevent_req_nomem(subreq, req)) {
+		return tevent_req_post(req, ev);
+	}
+
+	tevent_req_set_callback(subreq, fsctl_dup_extents_vfs_done, req);
+
+	return subreq;
+}
+
+static void fsctl_dup_extents_vfs_done(struct tevent_req *subreq)
+{
+	struct tevent_req *req = NULL;
+	struct fsctl_dup_extents_state *state = NULL;
+	off_t nb_chunk;
+	NTSTATUS status;
+
+	req = tevent_req_callback_data(subreq, struct tevent_req);
+	state = tevent_req_data(req, struct fsctl_dup_extents_state);
+
+	status = SMB_VFS_COPY_CHUNK_RECV(state->conn, subreq, &nb_chunk);
+	if (!tevent_req_nterror(req, status)) {
+		tevent_req_done(req);
+	}
+}
+
+static NTSTATUS fsctl_dup_extents_recv(struct tevent_req *req)
+{
+	return tevent_req_simple_recv_ntstatus(req);
+}
+
 static NTSTATUS fsctl_get_cmprn(TALLOC_CTX *mem_ctx,
 				struct tevent_context *ev,
 				struct files_struct *fsp,
@@ -437,6 +548,8 @@ static NTSTATUS fsctl_qar(TALLOC_CTX *mem_ctx,
 	return NT_STATUS_OK;
 }
 
+static void smb2_ioctl_filesys_dup_extents_done(struct tevent_req *subreq);
+
 struct tevent_req *smb2_ioctl_filesys(uint32_t ctl_code,
 				      struct tevent_context *ev,
 				      struct tevent_req *req,
@@ -480,6 +593,22 @@ struct tevent_req *smb2_ioctl_filesys(uint32_t ctl_code,
 		}
 		return tevent_req_post(req, ev);
 		break;
+	case FSCTL_DUP_EXTENTS_TO_FILE: {
+		struct tevent_req *subreq = NULL;
+
+		subreq = fsctl_dup_extents_send(state, ev,
+						state->fsp,
+						&state->in_input,
+						state->smb2req);
+		if (tevent_req_nomem(subreq, req)) {
+			return tevent_req_post(req, ev);
+		}
+		tevent_req_set_callback(subreq,
+					smb2_ioctl_filesys_dup_extents_done,
+					req);
+		return req;
+		break;
+	}
 	default: {
 		uint8_t *out_data = NULL;
 		uint32_t out_data_len = 0;
@@ -520,3 +649,15 @@ struct tevent_req *smb2_ioctl_filesys(uint32_t ctl_code,
 	tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
 	return tevent_req_post(req, ev);
 }
+
+static void smb2_ioctl_filesys_dup_extents_done(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(subreq,
+							  struct tevent_req);
+	NTSTATUS status;
+
+	status = fsctl_dup_extents_recv(subreq);
+	if (!tevent_req_nterror(req, status)) {
+		tevent_req_done(req);
+	}
+}
-- 
2.9.3


From 4f09924aa5d77a5589a5bc31edbc52c2aa9eeb26 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Mon, 8 May 2017 10:42:49 +0200
Subject: [PATCH 05/12] !fixup smbd/smb2_ioctl: add support for
 FSCTL_DUPLICATE_EXTENTS_TO_FILE

---
 source3/smbd/smb2_ioctl_filesys.c | 16 +++++++++-------
 1 file changed, 9 insertions(+), 7 deletions(-)

diff --git a/source3/smbd/smb2_ioctl_filesys.c b/source3/smbd/smb2_ioctl_filesys.c
index 9f1d4a9..53e62ff 100644
--- a/source3/smbd/smb2_ioctl_filesys.c
+++ b/source3/smbd/smb2_ioctl_filesys.c
@@ -123,18 +123,20 @@ static struct tevent_req *fsctl_dup_extents_send(TALLOC_CTX *mem_ctx,
 
 static void fsctl_dup_extents_vfs_done(struct tevent_req *subreq)
 {
-	struct tevent_req *req = NULL;
-	struct fsctl_dup_extents_state *state = NULL;
+	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);
 	off_t nb_chunk;
 	NTSTATUS status;
 
-	req = tevent_req_callback_data(subreq, struct tevent_req);
-	state = tevent_req_data(req, struct fsctl_dup_extents_state);
-
 	status = SMB_VFS_COPY_CHUNK_RECV(state->conn, subreq, &nb_chunk);
-	if (!tevent_req_nterror(req, status)) {
-		tevent_req_done(req);
+	TALLOC_FREE(subreq);
+	if (tevent_req_nterror(req, status)) {
+		return;
 	}
+
+	tevent_req_done(req);
 }
 
 static NTSTATUS fsctl_dup_extents_recv(struct tevent_req *req)
-- 
2.9.3


From d22ce807299ffd33f0e72efa9c18d9a7865d2208 Mon Sep 17 00:00:00 2001
From: David Disseldorp <ddiss at samba.org>
Date: Tue, 20 Sep 2016 09:48:37 -0700
Subject: [PATCH 06/12] smbd/smb2_ioctl: check for for overlap of dup extent
 ranges
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: David Disseldorp <ddiss at samba.org>
Reviewed-by: Ralph Böhme <slow at samba.org>
---
 source3/smbd/smb2_ioctl_filesys.c | 55 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 55 insertions(+)

diff --git a/source3/smbd/smb2_ioctl_filesys.c b/source3/smbd/smb2_ioctl_filesys.c
index 53e62ff..70784d3 100644
--- a/source3/smbd/smb2_ioctl_filesys.c
+++ b/source3/smbd/smb2_ioctl_filesys.c
@@ -31,6 +31,53 @@
 #include "librpc/gen_ndr/ndr_ioctl.h"
 #include "smb2_ioctl_private.h"
 
+static NTSTATUS fsctl_dup_extents_check_overlap(struct files_struct *src_fsp,
+						struct files_struct *dst_fsp,
+				struct fsctl_dup_extents_to_file *dup_extents)
+{
+	uint64_t src_off_last;
+	uint64_t tgt_off_last;
+
+	if (!file_id_equal(&src_fsp->file_id, &dst_fsp->file_id)) {
+		/* src and dest refer to different files */
+		return NT_STATUS_OK;
+	}
+
+	if (dup_extents->byte_count == 0) {
+		/* no range to overlap */
+		return NT_STATUS_OK;
+	}
+
+	/*
+	 * [MS-FSCC] 2.3.8 FSCTL_DUPLICATE_EXTENTS_TO_FILE Reply
+	 * STATUS_NOT_SUPPORTED:
+	 * The source and target destination ranges overlap on the same file.
+	 */
+
+	src_off_last = dup_extents->source_off + dup_extents->byte_count - 1;
+	if ((dup_extents->target_off >= dup_extents->source_off)
+				&& (dup_extents->target_off <= src_off_last)) {
+		/*
+		 * src: |-----------|
+		 * tgt:       |-----------|
+		 */
+		return NT_STATUS_NOT_SUPPORTED;
+	}
+
+
+	tgt_off_last = dup_extents->target_off + dup_extents->byte_count - 1;
+	if ((tgt_off_last >= dup_extents->source_off)
+					&& (tgt_off_last <= src_off_last)) {
+		/*
+		 * src:       |-----------|
+		 * tgt: |-----------|
+		 */
+		return NT_STATUS_NOT_SUPPORTED;
+	}
+
+	return NT_STATUS_OK;
+}
+
 struct fsctl_dup_extents_state {
 	struct tevent_context *ev;
 	struct connection_struct *conn;
@@ -51,6 +98,7 @@ static struct tevent_req *fsctl_dup_extents_send(TALLOC_CTX *mem_ctx,
 	uint64_t src_fid_volatile = 0;
 	struct files_struct *src_fsp = NULL;
 	int ndr_ret;
+	NTSTATUS status;
 
 	req = tevent_req_create(mem_ctx, &state,
 				struct fsctl_dup_extents_state);
@@ -107,6 +155,13 @@ static struct tevent_req *fsctl_dup_extents_send(TALLOC_CTX *mem_ctx,
 		return tevent_req_post(req, ev);
 	}
 
+	status = fsctl_dup_extents_check_overlap(src_fsp, dst_fsp,
+						 &dup_extents);
+	if (!NT_STATUS_IS_OK(status)) {
+		tevent_req_nterror(req, status);
+		return tevent_req_post(req, ev);
+	}
+
 	subreq = SMB_VFS_COPY_CHUNK_SEND(dst_fsp->conn, mem_ctx, ev,
 					 src_fsp, dup_extents.source_off,
 					 dst_fsp, dup_extents.target_off,
-- 
2.9.3


From a004e048a64b7b49e5d4163363a28bc9a35635f0 Mon Sep 17 00:00:00 2001
From: Aurelien Aptel <aaptel at suse.com>
Date: Thu, 30 Jul 2015 16:33:08 +0200
Subject: [PATCH 07/12] vfs_btrfs: report FILE_SUPPORTS_BLOCK_REFCOUNTING
 capability
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Aurelien Aptel <aaptel at suse.com>
Reviewed-by: David Disseldorp <ddiss at samba.org>
Reviewed-by: Ralph Böhme <slow at samba.org>
---
 source3/modules/vfs_btrfs.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/source3/modules/vfs_btrfs.c b/source3/modules/vfs_btrfs.c
index bf0a080..4be4ef6 100644
--- a/source3/modules/vfs_btrfs.c
+++ b/source3/modules/vfs_btrfs.c
@@ -39,7 +39,8 @@ static uint32_t btrfs_fs_capabilities(struct vfs_handle_struct *handle,
 
 	/* inherit default capabilities, expose compression support */
 	fs_capabilities = SMB_VFS_NEXT_FS_CAPABILITIES(handle, &ts_res);
-	fs_capabilities |= FILE_FILE_COMPRESSION;
+	fs_capabilities |= (FILE_FILE_COMPRESSION
+			    | FILE_SUPPORTS_BLOCK_REFCOUNTING);
 	*_ts_res = ts_res;
 
 	return fs_capabilities;
-- 
2.9.3


From c378919cb442d4eeae295790e0985494ca1ec8d6 Mon Sep 17 00:00:00 2001
From: David Disseldorp <ddiss at samba.org>
Date: Fri, 30 Sep 2016 20:53:07 +0200
Subject: [PATCH 08/12] smbd/smb2_ioctl: check sparseness for dup extents
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

FSCTL_DUPLICATE_EXTENTS_TO_FILE should fail if the source is marked
sparse while the target is not:

From: Jeff McCashland
To: David Disseldorp
Subject: RE: FSCTL_DUPLICATE_EXTENTS_TO_FILE questions, 116092214702946
Date: Tue, 27 Dec 2016 18:06:14 +0000

...
We have updated the spec for future release:

Section 2.3.8 FSCTL_DUPLICATE_EXTENTS_TO_FILE Reply
Changed description of STATUS_NOT_SUPPORTED error code to:
"--The source and target destination ranges overlap on the same file.
--Source file is sparse, while -target is a non-sparse file.
--The source range is beyond the source file's allocation size."

Signed-off-by: David Disseldorp <ddiss at samba.org>
Reviewed-by: Ralph Böhme <slow at samba.org>
---
 source3/smbd/smb2_ioctl_filesys.c | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)

diff --git a/source3/smbd/smb2_ioctl_filesys.c b/source3/smbd/smb2_ioctl_filesys.c
index 70784d3..c2d966f 100644
--- a/source3/smbd/smb2_ioctl_filesys.c
+++ b/source3/smbd/smb2_ioctl_filesys.c
@@ -78,6 +78,28 @@ static NTSTATUS fsctl_dup_extents_check_overlap(struct files_struct *src_fsp,
 	return NT_STATUS_OK;
 }
 
+static NTSTATUS fsctl_dup_extents_check_sparse(struct files_struct *src_fsp,
+					       struct files_struct *dst_fsp)
+{
+	/*
+	 * 2.3.8 FSCTL_DUPLICATE_EXTENTS_TO_FILE Reply...
+	 * STATUS_NOT_SUPPORTED: Target file is sparse, while source
+	 *			 is a non-sparse file.
+	 *
+	 * WS2016 has the following behaviour (MS are in the process of fixing
+	 * the spec):
+	 * STATUS_NOT_SUPPORTED is returned if the source is sparse, while the
+	 * target is non-sparse. However, if target is sparse while the source
+	 * is non-sparse, then FSCTL_DUPLICATE_EXTENTS_TO_FILE completes
+	 * successfully.
+	 */
+	if ((src_fsp->is_sparse) && (!dst_fsp->is_sparse)) {
+		return NT_STATUS_NOT_SUPPORTED;
+	}
+
+	return NT_STATUS_OK;
+}
+
 struct fsctl_dup_extents_state {
 	struct tevent_context *ev;
 	struct connection_struct *conn;
@@ -162,6 +184,12 @@ static struct tevent_req *fsctl_dup_extents_send(TALLOC_CTX *mem_ctx,
 		return tevent_req_post(req, ev);
 	}
 
+	status = fsctl_dup_extents_check_sparse(src_fsp, dst_fsp);
+	if (!NT_STATUS_IS_OK(status)) {
+		tevent_req_nterror(req, status);
+		return tevent_req_post(req, ev);
+	}
+
 	subreq = SMB_VFS_COPY_CHUNK_SEND(dst_fsp->conn, mem_ctx, ev,
 					 src_fsp, dup_extents.source_off,
 					 dst_fsp, dup_extents.target_off,
-- 
2.9.3


From 3ea98af19e4b32b8c61dcf94cb06b94daf591720 Mon Sep 17 00:00:00 2001
From: David Disseldorp <ddiss at samba.org>
Date: Sat, 1 Oct 2016 23:40:13 +0200
Subject: [PATCH 09/12] smbd/smb2_ioctl: validate dup_extent request lengths
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

FSCTL_DUPLICATE_EXTENTS_TO_FILE has some interesting behaviour when
exceeding the destination file length - the clone is truncated to only
cover the existing file region. If the existing length is zero, then
nothing is cloned.

Signed-off-by: David Disseldorp <ddiss at samba.org>
Reviewed-by: Ralph Böhme <slow at samba.org>
---
 source3/smbd/smb2_ioctl_filesys.c | 65 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 65 insertions(+)

diff --git a/source3/smbd/smb2_ioctl_filesys.c b/source3/smbd/smb2_ioctl_filesys.c
index c2d966f..0819667 100644
--- a/source3/smbd/smb2_ioctl_filesys.c
+++ b/source3/smbd/smb2_ioctl_filesys.c
@@ -31,6 +31,64 @@
 #include "librpc/gen_ndr/ndr_ioctl.h"
 #include "smb2_ioctl_private.h"
 
+/*
+ * XXX this may reduce dup_extents->byte_count so that it's less than the
+ * target file size.
+ */
+static NTSTATUS fsctl_dup_extents_check_lengths(struct files_struct *src_fsp,
+						struct files_struct *dst_fsp,
+				struct fsctl_dup_extents_to_file *dup_extents)
+{
+	NTSTATUS status;
+
+	if ((dup_extents->source_off + dup_extents->byte_count
+						< dup_extents->source_off)
+	 || (dup_extents->target_off + dup_extents->byte_count
+						< dup_extents->target_off)) {
+		return NT_STATUS_INVALID_PARAMETER;	/* wrap */
+	}
+
+	status = vfs_stat_fsp(src_fsp);
+	if (!NT_STATUS_IS_OK(status)) {
+		return status;
+	}
+
+	/*
+	 * XXX vfs_btrfs and vfs_default have size checks in the copychunk
+	 * handler, as this needs to be rechecked after the src has potentially
+	 * been extended by a previous chunk in the compound copychunk req.
+	 */
+	if (src_fsp->fsp_name->st.st_ex_size
+			< dup_extents->source_off + dup_extents->byte_count) {
+		DEBUG(2, ("dup_extents req exceeds src size\n"));
+		return NT_STATUS_NOT_SUPPORTED;
+	}
+
+	status = vfs_stat_fsp(dst_fsp);
+	if (!NT_STATUS_IS_OK(status)) {
+		return status;
+	}
+
+	if (dst_fsp->fsp_name->st.st_ex_size
+			< dup_extents->target_off + dup_extents->byte_count) {
+
+		if (dst_fsp->fsp_name->st.st_ex_size - dup_extents->target_off
+					> dst_fsp->fsp_name->st.st_ex_size) {
+			return NT_STATUS_INVALID_PARAMETER;	/* wrap */
+		}
+
+		/*
+		 * this server behaviour is pretty hairy, but we need to match
+		 * Windows, so...
+		 */
+		DEBUG(2, ("dup_extents req exceeds target size, capping\n"));
+		dup_extents->byte_count = dst_fsp->fsp_name->st.st_ex_size
+						- dup_extents->target_off;
+	}
+
+	return NT_STATUS_OK;
+}
+
 static NTSTATUS fsctl_dup_extents_check_overlap(struct files_struct *src_fsp,
 						struct files_struct *dst_fsp,
 				struct fsctl_dup_extents_to_file *dup_extents)
@@ -171,6 +229,13 @@ static struct tevent_req *fsctl_dup_extents_send(TALLOC_CTX *mem_ctx,
 		return tevent_req_post(req, ev);
 	}
 
+	status = fsctl_dup_extents_check_lengths(src_fsp, dst_fsp,
+						 &dup_extents);
+	if (!NT_STATUS_IS_OK(status)) {
+		tevent_req_nterror(req, status);
+		return tevent_req_post(req, ev);
+	}
+
 	if (dup_extents.byte_count == 0) {
 		DBG_ERR("skipping zero length dup extents\n");
 		tevent_req_done(req);
-- 
2.9.3


From 62fba9ab22758e0dcd7f1fc2883bace84f7e43df Mon Sep 17 00:00:00 2001
From: David Disseldorp <ddiss at samba.org>
Date: Thu, 4 May 2017 14:55:43 +0200
Subject: [PATCH 10/12] vfs: add VFS_COPY_CHUNK_FL_IGNORE_LOCKS for dup extents
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

As confirmed by the Microsoft Protocol Open Specifications Team: Windows
Server 2016 (ReFS) ignores locks for FSCTL_DUPLICATE_EXTENTS_TO_FILE...

From: Jeff McCashland
To: David Disseldorp
CC: "cifs-protocol at lists.samba.org" <cifs-protocol at lists.samba.org>
Subject: RE: [116100414754619] FSCTL_DUPLICATE_EXTENTS_TO_FILE appears
	 to completely bypass file locks
Date: Mon, 12 Dec 2016 20:44:08 +0000

Hi David,

We have made the following spec changes for the next doc release:

In section 2.1.5.9.4 FSCTL_DUPLICATE_EXTENTS_TO_FILE behavior notes have
been added to the following paragraphs.

Before:
§ The object store MUST check for byte range lock conflicts on
Open.Stream using the algorithm described in section 2.1.4.10 with
ByteOffset set to InputBuffer.TargetFileOffset, Length set to
InputBuffer.ByteCount, IsExclusive set to TRUE, LockIntent set to FALSE,
and Open set to Open. If a conflict is detected, the operation MUST be
failed with STATUS_FILE_LOCK_CONFLICT.

§ The object store MUST check for byte range lock conflicts on Source
using the algorithm described in section 2.1.4.10 with ByteOffset set to
InputBuffer.SourceFileOffset, Length set to InputBuffer.ByteCount,
IsExclusive set to FALSE, LockIntent set to FALSE, and Open set to
InputBuffer.FileHandle. If a conflict is detected, the operation MUST be
failed with STATUS_FILE_LOCK_CONFLICT.

After:
§ The object store SHOULD<WBN1> check for byte range lock conflicts on
Open.Stream using the algorithm described in section 2.1.4.10 with
ByteOffset set to InputBuffer.TargetFileOffset, Length set to
InputBuffer.ByteCount, IsExclusive set to TRUE, LockIntent set to FALSE,
and Open set to Open. If a conflict is detected, the operation MUST be
failed with STATUS_FILE_LOCK_CONFLICT.

§ The object store SHOULD<WBN2> check for byte range lock conflicts on
Source using the algorithm described in section 2.1.4.10 with ByteOffset
set to InputBuffer.SourceFileOffset, Length set to
InputBuffer.ByteCount, IsExclusive set to FALSE, LockIntent set to
FALSE, and Open set to InputBuffer.FileHandle. If a conflict is
detected, the operation MUST be failed with STATUS_FILE_LOCK_CONFLICT.

WBN1: The ReFS file system in Windows Server 2016 does not check for
byte range lock conflicts on Open.Stream.
WBN2: The ReFS file system in Windows Server 2016 does not check for
byte range lock conflicts on Source.

Signed-off-by: David Disseldorp <ddiss at samba.org>
Reviewed-by: Ralph Böhme <slow at samba.org>
---
 source3/include/vfs.h         |  5 ++-
 source3/modules/vfs_btrfs.c   | 50 ++++++++++++++++--------------
 source3/modules/vfs_default.c | 72 ++++++++++++++++++++++++-------------------
 3 files changed, 72 insertions(+), 55 deletions(-)

diff --git a/source3/include/vfs.h b/source3/include/vfs.h
index fd493e4..7908980 100644
--- a/source3/include/vfs.h
+++ b/source3/include/vfs.h
@@ -556,8 +556,11 @@ enum vfs_fallocate_flags {
  */
 enum vfs_copy_chunk_flags {
 	VFS_COPY_CHUNK_FL_MUST_CLONE		= 0x0001,
+	VFS_COPY_CHUNK_FL_IGNORE_LOCKS		= 0x0002,
 
-	VFS_COPY_CHUNK_FL_MASK_ALL		= 0x0001,
+	VFS_COPY_CHUNK_FL_MASK_ALL		=
+					(VFS_COPY_CHUNK_FL_MUST_CLONE
+					 | VFS_COPY_CHUNK_FL_IGNORE_LOCKS),
 };
 
 struct vfs_aio_state {
diff --git a/source3/modules/vfs_btrfs.c b/source3/modules/vfs_btrfs.c
index 4be4ef6..e306ece 100644
--- a/source3/modules/vfs_btrfs.c
+++ b/source3/modules/vfs_btrfs.c
@@ -154,27 +154,29 @@ static struct tevent_req *btrfs_copy_chunk_send(struct vfs_handle_struct *handle
 		return tevent_req_post(req, ev);
 	}
 
-	init_strict_lock_struct(src_fsp,
-				src_fsp->op->global->open_persistent_id,
-				src_off,
-				num,
-				READ_LOCK,
-				&src_lck);
-	init_strict_lock_struct(dest_fsp,
-				dest_fsp->op->global->open_persistent_id,
-				dest_off,
-				num,
-				WRITE_LOCK,
-				&dest_lck);
-
-	if (!SMB_VFS_STRICT_LOCK(src_fsp->conn, src_fsp, &src_lck)) {
-		tevent_req_nterror(req, NT_STATUS_FILE_LOCK_CONFLICT);
-		return tevent_req_post(req, ev);
-	}
-	if (!SMB_VFS_STRICT_LOCK(dest_fsp->conn, dest_fsp, &dest_lck)) {
-		SMB_VFS_STRICT_UNLOCK(src_fsp->conn, src_fsp, &src_lck);
-		tevent_req_nterror(req, NT_STATUS_FILE_LOCK_CONFLICT);
-		return tevent_req_post(req, ev);
+	if (!(flags & VFS_COPY_CHUNK_FL_IGNORE_LOCKS)) {
+		init_strict_lock_struct(src_fsp,
+					src_fsp->op->global->open_persistent_id,
+					src_off,
+					num,
+					READ_LOCK,
+					&src_lck);
+		init_strict_lock_struct(dest_fsp,
+				       dest_fsp->op->global->open_persistent_id,
+					dest_off,
+					num,
+					WRITE_LOCK,
+					&dest_lck);
+
+		if (!SMB_VFS_STRICT_LOCK(src_fsp->conn, src_fsp, &src_lck)) {
+			tevent_req_nterror(req, NT_STATUS_FILE_LOCK_CONFLICT);
+			return tevent_req_post(req, ev);
+		}
+		if (!SMB_VFS_STRICT_LOCK(dest_fsp->conn, dest_fsp, &dest_lck)) {
+			SMB_VFS_STRICT_UNLOCK(src_fsp->conn, src_fsp, &src_lck);
+			tevent_req_nterror(req, NT_STATUS_FILE_LOCK_CONFLICT);
+			return tevent_req_post(req, ev);
+		}
 	}
 
 	ZERO_STRUCT(cr_args);
@@ -184,8 +186,10 @@ 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);
-	SMB_VFS_STRICT_UNLOCK(dest_fsp->conn, dest_fsp, &dest_lck);
-	SMB_VFS_STRICT_UNLOCK(src_fsp->conn, src_fsp, &src_lck);
+	if (!(flags & VFS_COPY_CHUNK_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);
+	}
 	if (ret < 0) {
 		/*
 		 * BTRFS_IOC_CLONE_RANGE only supports 'sectorsize' aligned
diff --git a/source3/modules/vfs_default.c b/source3/modules/vfs_default.c
index ffdc40a..fa89f7f 100644
--- a/source3/modules/vfs_default.c
+++ b/source3/modules/vfs_default.c
@@ -1605,6 +1605,7 @@ struct vfs_cc_state {
 	off_t to_copy;
 	off_t remaining;
 	size_t next_io_size;
+	uint32_t flags;
 };
 
 static NTSTATUS copy_chunk_loop(struct tevent_req *req);
@@ -1650,6 +1651,7 @@ static struct tevent_req *vfswrap_copy_chunk_send(struct vfs_handle_struct *hand
 		.dst_off = dest_off,
 		.to_copy = to_copy,
 		.remaining = to_copy,
+		.flags = flags,
 	};
 	state->buf = talloc_array(state, uint8_t, num);
 	if (tevent_req_nomem(state->buf, req)) {
@@ -1704,18 +1706,20 @@ static NTSTATUS copy_chunk_loop(struct tevent_req *req)
 
 	state->next_io_size = MIN(state->remaining, talloc_array_length(state->buf));
 
-	init_strict_lock_struct(state->src_fsp,
+	if (!(state->flags & VFS_COPY_CHUNK_FL_IGNORE_LOCKS)) {
+		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,
@@ -1743,10 +1747,12 @@ static void vfswrap_copy_chunk_read_done(struct tevent_req *subreq)
 	ssize_t nread;
 	bool ok;
 
-	SMB_VFS_STRICT_UNLOCK(state->src_fsp->conn,
-			      state->src_fsp,
-			      &state->read_lck);
-	ZERO_STRUCT(state->read_lck);
+	if (!(state->flags & VFS_COPY_CHUNK_FL_IGNORE_LOCKS)) {
+		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);
@@ -1764,19 +1770,21 @@ static void vfswrap_copy_chunk_read_done(struct tevent_req *subreq)
 
 	state->src_off += nread;
 
-	init_strict_lock_struct(state->dst_fsp,
+	if (!(state->flags & VFS_COPY_CHUNK_FL_IGNORE_LOCKS)) {
+		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,
@@ -1801,10 +1809,12 @@ static void vfswrap_copy_chunk_write_done(struct tevent_req *subreq)
 	ssize_t nwritten;
 	NTSTATUS status;
 
-	SMB_VFS_STRICT_UNLOCK(state->dst_fsp->conn,
-			      state->dst_fsp,
-			      &state->write_lck);
-	ZERO_STRUCT(state->write_lck);
+	if (!(state->flags & VFS_COPY_CHUNK_FL_IGNORE_LOCKS)) {
+		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);
-- 
2.9.3


From 12a33ebc29b862bc26f956412a797fd9e35931d2 Mon Sep 17 00:00:00 2001
From: David Disseldorp <ddiss at samba.org>
Date: Thu, 4 May 2017 15:09:36 +0200
Subject: [PATCH 11/12] smbd/smb2_ioctl: instruct VFS to ignore locks for dup
 extents
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

As described in the previous commit - Windows Server 2016 (ReFS) ignores
locks for FSCTL_DUPLICATE_EXTENTS_TO_FILE. Do the same for Samba.

Signed-off-by: David Disseldorp <ddiss at samba.org>
Reviewed-by: Ralph Böhme <slow at samba.org>
---
 source3/smbd/smb2_ioctl_filesys.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/source3/smbd/smb2_ioctl_filesys.c b/source3/smbd/smb2_ioctl_filesys.c
index 0819667..c4e2ced 100644
--- a/source3/smbd/smb2_ioctl_filesys.c
+++ b/source3/smbd/smb2_ioctl_filesys.c
@@ -255,11 +255,13 @@ static struct tevent_req *fsctl_dup_extents_send(TALLOC_CTX *mem_ctx,
 		return tevent_req_post(req, ev);
 	}
 
+	/* tell the VFS to ignore locks across the clone, matching ReFS */
 	subreq = SMB_VFS_COPY_CHUNK_SEND(dst_fsp->conn, mem_ctx, ev,
 					 src_fsp, dup_extents.source_off,
 					 dst_fsp, dup_extents.target_off,
 					 dup_extents.byte_count,
-					 VFS_COPY_CHUNK_FL_MUST_CLONE);
+					 VFS_COPY_CHUNK_FL_MUST_CLONE
+					 | VFS_COPY_CHUNK_FL_IGNORE_LOCKS);
 	if (tevent_req_nomem(subreq, req)) {
 		return tevent_req_post(req, ev);
 	}
-- 
2.9.3


From 33ea5dbc4cf627ff1b62ab774d93a08f379289ec Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Mon, 8 May 2017 10:50:44 +0200
Subject: [PATCH 12/12] !fixup: several...

- move dup_extents to fsctl_dup_extents_state
- check copy_chunk copied the amount we requested
---
 source3/smbd/smb2_ioctl_filesys.c | 26 ++++++++++++++++----------
 1 file changed, 16 insertions(+), 10 deletions(-)

diff --git a/source3/smbd/smb2_ioctl_filesys.c b/source3/smbd/smb2_ioctl_filesys.c
index c4e2ced..4757f21 100644
--- a/source3/smbd/smb2_ioctl_filesys.c
+++ b/source3/smbd/smb2_ioctl_filesys.c
@@ -161,7 +161,9 @@ 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 fsctl_dup_extents_to_file dup_extents;
 };
+
 static void fsctl_dup_extents_vfs_done(struct tevent_req *subreq);
 
 static struct tevent_req *fsctl_dup_extents_send(TALLOC_CTX *mem_ctx,
@@ -173,7 +175,6 @@ static struct tevent_req *fsctl_dup_extents_send(TALLOC_CTX *mem_ctx,
 	struct tevent_req *req = NULL;
 	struct tevent_req *subreq = NULL;
 	struct fsctl_dup_extents_state *state = NULL;
-	struct fsctl_dup_extents_to_file dup_extents;
 	uint64_t src_fid_persistent = 0;
 	uint64_t src_fid_volatile = 0;
 	struct files_struct *src_fsp = NULL;
@@ -202,7 +203,7 @@ static struct tevent_req *fsctl_dup_extents_send(TALLOC_CTX *mem_ctx,
 		return tevent_req_post(req, ev);
 	}
 
-	ndr_ret = ndr_pull_struct_blob(in_input, mem_ctx, &dup_extents,
+	ndr_ret = ndr_pull_struct_blob(in_input, mem_ctx, &state->dup_extents,
 		       (ndr_pull_flags_fn_t)ndr_pull_fsctl_dup_extents_to_file);
 	if (ndr_ret != NDR_ERR_SUCCESS) {
 		DBG_ERR("failed to unmarshall dup extents to file req\n");
@@ -210,8 +211,8 @@ static struct tevent_req *fsctl_dup_extents_send(TALLOC_CTX *mem_ctx,
 		return tevent_req_post(req, ev);
 	}
 
-	src_fid_persistent = BVAL(dup_extents.source_fid, 0);
-	src_fid_volatile = BVAL(dup_extents.source_fid, 8);
+	src_fid_persistent = BVAL(state->dup_extents.source_fid, 0);
+	src_fid_volatile = BVAL(state->dup_extents.source_fid, 8);
 	src_fsp = file_fsp_get(smb2req, src_fid_persistent, src_fid_volatile);
 	if ((src_fsp == NULL)
 		      || (src_fsp->file_id.devid != dst_fsp->file_id.devid)) {
@@ -230,20 +231,20 @@ static struct tevent_req *fsctl_dup_extents_send(TALLOC_CTX *mem_ctx,
 	}
 
 	status = fsctl_dup_extents_check_lengths(src_fsp, dst_fsp,
-						 &dup_extents);
+						 &state->dup_extents);
 	if (!NT_STATUS_IS_OK(status)) {
 		tevent_req_nterror(req, status);
 		return tevent_req_post(req, ev);
 	}
 
-	if (dup_extents.byte_count == 0) {
+	if (state->dup_extents.byte_count == 0) {
 		DBG_ERR("skipping zero length dup extents\n");
 		tevent_req_done(req);
 		return tevent_req_post(req, ev);
 	}
 
 	status = fsctl_dup_extents_check_overlap(src_fsp, dst_fsp,
-						 &dup_extents);
+						 &state->dup_extents);
 	if (!NT_STATUS_IS_OK(status)) {
 		tevent_req_nterror(req, status);
 		return tevent_req_post(req, ev);
@@ -257,9 +258,9 @@ static struct tevent_req *fsctl_dup_extents_send(TALLOC_CTX *mem_ctx,
 
 	/* tell the VFS to ignore locks across the clone, matching ReFS */
 	subreq = SMB_VFS_COPY_CHUNK_SEND(dst_fsp->conn, mem_ctx, ev,
-					 src_fsp, dup_extents.source_off,
-					 dst_fsp, dup_extents.target_off,
-					 dup_extents.byte_count,
+					 src_fsp, state->dup_extents.source_off,
+					 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)) {
@@ -286,6 +287,11 @@ static void fsctl_dup_extents_vfs_done(struct tevent_req *subreq)
 		return;
 	}
 
+	if (nb_chunk != state->dup_extents.byte_count) {
+		tevent_req_nterror(req, NT_STATUS_IO_DEVICE_ERROR);
+		return;
+	}
+
 	tevent_req_done(req);
 }
 
-- 
2.9.3



More information about the samba-technical mailing list