The Samba-Bugzilla – Attachment 11152 Details for
Bug 11317
Implement OS X style copyfile in vfs_fruit
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Patch for master
copyfile.patch (text/plain), 32.91 KB, created by
Ralph Böhme
on 2015-06-15 08:18:03 UTC
(
hide
)
Description:
Patch for master
Filename:
MIME Type:
Creator:
Ralph Böhme
Created:
2015-06-15 08:18:03 UTC
Size:
32.91 KB
patch
obsolete
>From 5d391607435bbda03fdc1fa2e20624a61ed37027 Mon Sep 17 00:00:00 2001 >From: Ralph Boehme <slow@samba.org> >Date: Tue, 9 Jun 2015 17:47:31 +0200 >Subject: [PATCH 1/4] smbd/smb2_ioctl: error handling > >tevent_req_nterror must be called directly as the last step before >returning with tevent_req_post. > >Signed-off-by: Ralph Boehme <slow@samba.org> >--- > source3/smbd/smb2_ioctl_network_fs.c | 3 ++- > 1 file changed, 2 insertions(+), 1 deletion(-) > >diff --git a/source3/smbd/smb2_ioctl_network_fs.c b/source3/smbd/smb2_ioctl_network_fs.c >index 5e70703..36b8e89 100644 >--- a/source3/smbd/smb2_ioctl_network_fs.c >+++ b/source3/smbd/smb2_ioctl_network_fs.c >@@ -225,9 +225,10 @@ static struct tevent_req *fsctl_srv_copychunk_send(TALLOC_CTX *mem_ctx, > } > > state->status = copychunk_check_limits(&cc_copy); >- if (tevent_req_nterror(req, state->status)) { >+ if (!NT_STATUS_IS_OK(state->status)) { > DEBUG(3, ("copy chunk req exceeds limits\n")); > state->out_data = COPYCHUNK_OUT_LIMITS; >+ tevent_req_nterror(req, state->status); > return tevent_req_post(req, ev); > } > >-- >2.1.0 > > >From 479898f85431c5076ae3b58f187f6a153c26f289 Mon Sep 17 00:00:00 2001 >From: Ralph Boehme <slow@samba.org> >Date: Mon, 27 Apr 2015 12:16:16 +0200 >Subject: [PATCH 2/4] s3:util: use pread/pwrite in transfer_file > >read/write aren't overloaded in the streams VFS modules, using >pread/pwrite instead this makes it possible to use transfer_file() with >named streams. > >Bug: https://bugzilla.samba.org/show_bug.cgi?id=11317 > >Signed-off-by: Ralph Boehme <slow@samba.org> >--- > source3/include/transfer_file.h | 4 ++-- > source3/lib/util_transfer_file.c | 23 +++++++++++++---------- > source3/smbd/vfs.c | 10 +++++----- > 3 files changed, 20 insertions(+), 17 deletions(-) > >diff --git a/source3/include/transfer_file.h b/source3/include/transfer_file.h >index 546104f..2f1bff4 100644 >--- a/source3/include/transfer_file.h >+++ b/source3/include/transfer_file.h >@@ -24,8 +24,8 @@ > ssize_t transfer_file_internal(void *in_file, > void *out_file, > size_t n, >- ssize_t (*read_fn)(void *, void *, size_t), >- ssize_t (*write_fn)(void *, const void *, size_t)); >+ ssize_t (*pread_fn)(void *, void *, size_t, off_t), >+ ssize_t (*pwrite_fn)(void *, const void *, size_t, off_t)); > > off_t transfer_file(int infd, int outfd, off_t n); > >diff --git a/source3/lib/util_transfer_file.c b/source3/lib/util_transfer_file.c >index d415d7f..91f4f6f 100644 >--- a/source3/lib/util_transfer_file.c >+++ b/source3/lib/util_transfer_file.c >@@ -36,8 +36,8 @@ > ssize_t transfer_file_internal(void *in_file, > void *out_file, > size_t n, >- ssize_t (*read_fn)(void *, void *, size_t), >- ssize_t (*write_fn)(void *, const void *, size_t)) >+ ssize_t (*pread_fn)(void *, void *, size_t, off_t), >+ ssize_t (*pwrite_fn)(void *, const void *, size_t, off_t)) > { > char *buf; > size_t total = 0; >@@ -45,6 +45,7 @@ ssize_t transfer_file_internal(void *in_file, > ssize_t write_ret; > size_t num_to_read_thistime; > size_t num_written = 0; >+ off_t offset = 0; > > if (n == 0) { > return 0; >@@ -57,7 +58,7 @@ ssize_t transfer_file_internal(void *in_file, > do { > num_to_read_thistime = MIN((n - total), TRANSFER_BUF_SIZE); > >- read_ret = (*read_fn)(in_file, buf, num_to_read_thistime); >+ read_ret = (*pread_fn)(in_file, buf, num_to_read_thistime, offset); > if (read_ret == -1) { > DEBUG(0,("transfer_file_internal: read failure. " > "Error = %s\n", strerror(errno) )); >@@ -71,8 +72,9 @@ ssize_t transfer_file_internal(void *in_file, > num_written = 0; > > while (num_written < read_ret) { >- write_ret = (*write_fn)(out_file, buf + num_written, >- read_ret - num_written); >+ write_ret = (*pwrite_fn)(out_file, buf + num_written, >+ read_ret - num_written, >+ offset + num_written); > > if (write_ret == -1) { > DEBUG(0,("transfer_file_internal: " >@@ -89,28 +91,29 @@ ssize_t transfer_file_internal(void *in_file, > } > > total += (size_t)read_ret; >+ offset += (off_t)read_ret; > } while (total < n); > > SAFE_FREE(buf); > return (ssize_t)total; > } > >-static ssize_t sys_read_fn(void *file, void *buf, size_t len) >+static ssize_t sys_pread_fn(void *file, void *buf, size_t len, off_t offset) > { > int *fd = (int *)file; > >- return sys_read(*fd, buf, len); >+ return sys_pread(*fd, buf, len, offset); > } > >-static ssize_t sys_write_fn(void *file, const void *buf, size_t len) >+static ssize_t sys_pwrite_fn(void *file, const void *buf, size_t len, off_t offset) > { > int *fd = (int *)file; > >- return sys_write(*fd, buf, len); >+ return sys_pwrite(*fd, buf, len, offset); > } > > off_t transfer_file(int infd, int outfd, off_t n) > { > return (off_t)transfer_file_internal(&infd, &outfd, (size_t)n, >- sys_read_fn, sys_write_fn); >+ sys_pread_fn, sys_pwrite_fn); > } >diff --git a/source3/smbd/vfs.c b/source3/smbd/vfs.c >index 4ab7723..b267387 100644 >--- a/source3/smbd/vfs.c >+++ b/source3/smbd/vfs.c >@@ -756,24 +756,24 @@ int vfs_fill_sparse(files_struct *fsp, off_t len) > Transfer some data (n bytes) between two file_struct's. > ****************************************************************************/ > >-static ssize_t vfs_read_fn(void *file, void *buf, size_t len) >+static ssize_t vfs_pread_fn(void *file, void *buf, size_t len, off_t offset) > { > struct files_struct *fsp = (struct files_struct *)file; > >- return SMB_VFS_READ(fsp, buf, len); >+ return SMB_VFS_PREAD(fsp, buf, len, offset); > } > >-static ssize_t vfs_write_fn(void *file, const void *buf, size_t len) >+static ssize_t vfs_pwrite_fn(void *file, const void *buf, size_t len, off_t offset) > { > struct files_struct *fsp = (struct files_struct *)file; > >- return SMB_VFS_WRITE(fsp, buf, len); >+ return SMB_VFS_PWRITE(fsp, buf, len, offset); > } > > off_t vfs_transfer_file(files_struct *in, files_struct *out, off_t n) > { > return transfer_file_internal((void *)in, (void *)out, n, >- vfs_read_fn, vfs_write_fn); >+ vfs_pread_fn, vfs_pwrite_fn); > } > > /******************************************************************* >-- >2.1.0 > > >From 2f5ec5631e1ba44a1be972d25ddefbc52517c36c Mon Sep 17 00:00:00 2001 >From: Ralph Boehme <slow@samba.org> >Date: Wed, 22 Apr 2015 22:29:16 +0200 >Subject: [PATCH 3/4] vfs:fruit: implement copyfile style copy_chunk > >Implement Apple's special copy_chunk ioctl that requests a copy of the >whole file along with all attached metadata. > >These copy_chunk requests have a chunk count of 0 that we translate to a >copy_chunk_send VFS call overloading the parameters src_off = dest_off = >num = 0. > >Bug: https://bugzilla.samba.org/show_bug.cgi?id=11317 > >Signed-off-by: Ralph Boehme <slow@samba.org> >--- > docs-xml/manpages/vfs_fruit.8.xml | 10 ++ > source3/include/vfs.h | 1 + > source3/modules/vfs_fruit.c | 217 +++++++++++++++++++++++++++++++++++ > source3/smbd/smb2_ioctl_network_fs.c | 36 +++++- > 4 files changed, 263 insertions(+), 1 deletion(-) > >diff --git a/docs-xml/manpages/vfs_fruit.8.xml b/docs-xml/manpages/vfs_fruit.8.xml >index e407b54..63cf925 100644 >--- a/docs-xml/manpages/vfs_fruit.8.xml >+++ b/docs-xml/manpages/vfs_fruit.8.xml >@@ -214,6 +214,16 @@ > </listitem> > </varlistentry> > >+ <varlistentry> >+ <term>fruit:copyfile = yes | no</term> >+ <listitem> >+ <para>Whether to enable OS X specific copychunk ioctl >+ that requests a copy of a whole file along with all >+ attached metadata.</para> >+ <para>The default is <emphasis>no</emphasis>.</para> >+ </listitem> >+ </varlistentry> >+ > </variablelist> > </refsect1> > >diff --git a/source3/include/vfs.h b/source3/include/vfs.h >index aa1a880..747f2bf 100644 >--- a/source3/include/vfs.h >+++ b/source3/include/vfs.h >@@ -257,6 +257,7 @@ 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 copyfile_enabled; > struct smb_filename *fsp_name; > uint32_t name_hash; /* Jenkins hash of full pathname. */ > uint64_t mid; /* Mid of the operation that created us. */ >diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c >index 2547838..0736313 100644 >--- a/source3/modules/vfs_fruit.c >+++ b/source3/modules/vfs_fruit.c >@@ -30,6 +30,7 @@ > #include "libcli/security/security.h" > #include "../libcli/smb/smb2_create_ctx.h" > #include "lib/sys_rw.h" >+#include "lib/util/tevent_ntstatus.h" > > /* > * Enhanced OS X and Netatalk compatibility >@@ -124,8 +125,10 @@ struct fruit_config_data { > enum fruit_locking locking; > enum fruit_encoding encoding; > bool use_aapl; >+ bool use_copyfile; > bool readdir_attr_enabled; > bool unix_info_enabled; >+ bool copyfile_enabled; > bool veto_appledouble; > > /* >@@ -1348,6 +1351,11 @@ static int init_fruit_config(vfs_handle_struct *handle) > config->unix_info_enabled = true; > } > >+ if (lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME, >+ "copyfile", false)) { >+ config->use_copyfile = true; >+ } >+ > if (lp_parm_bool(SNUM(handle->conn), > "readdir_attr", "aapl_rsize", true)) { > config->readdir_attr_rsize = true; >@@ -1837,6 +1845,11 @@ static NTSTATUS check_aapl(vfs_handle_struct *handle, > config->readdir_attr_enabled = true; > } > >+ if (config->use_copyfile) { >+ server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_OSX_COPYFILE; >+ config->copyfile_enabled = true; >+ } >+ > /* > * The client doesn't set the flag, so we can't check > * for it and just set it unconditionally >@@ -3252,6 +3265,15 @@ static NTSTATUS fruit_create_file(vfs_handle_struct *handle, > return status; > } > >+ 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. >+ */ >+ (*result)->copyfile_enabled = true; >+ } > if (is_ntfs_stream_smb_fname(smb_fname) > || (*result == NULL) > || ((*result)->is_directory)) { >@@ -3462,6 +3484,199 @@ static NTSTATUS fruit_fset_nt_acl(vfs_handle_struct *handle, > return NT_STATUS_OK; > } > >+struct fruit_cc_state { >+ struct vfs_handle_struct *handle; >+ off_t copied; >+ struct files_struct *src_fsp; >+ struct files_struct *dst_fsp; >+ 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, >+ TALLOC_CTX *mem_ctx, >+ struct tevent_context *ev, >+ struct files_struct *src_fsp, >+ off_t src_off, >+ struct files_struct *dest_fsp, >+ off_t dest_off, >+ off_t num) >+{ >+ struct tevent_req *req, *subreq; >+ struct fruit_cc_state *fruit_cc_state; >+ NTSTATUS status; >+ struct fruit_config_data *config; >+ off_t to_copy = num; >+ >+ DEBUG(10,("soff: %zd, doff: %zd, len: %zd\n", >+ src_off, dest_off, num)); >+ >+ SMB_VFS_HANDLE_GET_DATA(handle, config, >+ struct fruit_config_data, >+ return NULL); >+ >+ req = tevent_req_create(mem_ctx, &fruit_cc_state, struct fruit_cc_state); >+ if (req == NULL) { >+ return NULL; >+ } >+ fruit_cc_state->handle = handle; >+ fruit_cc_state->src_fsp = src_fsp; >+ fruit_cc_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 >+ * = dest_off = num = 0. >+ */ >+ if ((src_off == 0) && (dest_off == 0) && (num == 0) && >+ src_fsp->copyfile_enabled && dest_fsp->copyfile_enabled) { >+ status = vfs_stat_fsp(src_fsp); >+ if (tevent_req_nterror(req, status)) { >+ return tevent_req_post(req, ev); >+ } >+ >+ to_copy = src_fsp->fsp_name->st.st_ex_size; >+ fruit_cc_state->is_copyfile = true; >+ DEBUG(1,("copyfile\n")); >+ } >+ >+ subreq = SMB_VFS_NEXT_COPY_CHUNK_SEND(handle, >+ mem_ctx, >+ ev, >+ src_fsp, >+ src_off, >+ dest_fsp, >+ dest_off, >+ to_copy); >+ if (tevent_req_nomem(subreq, req)) { >+ return tevent_req_post(req, ev); >+ } >+ >+ tevent_req_set_callback(subreq, fruit_copy_chunk_done, req); >+ return req; >+} >+ >+static void fruit_copy_chunk_done(struct tevent_req *subreq) >+{ >+ struct tevent_req *req = tevent_req_callback_data( >+ subreq, struct tevent_req); >+ struct fruit_cc_state *state = tevent_req_data( >+ req, struct fruit_cc_state); >+ NTSTATUS status; >+ unsigned int num_streams = 0; >+ struct stream_struct *streams = NULL; >+ int i; >+ struct smb_filename *src_fname_tmp = NULL; >+ struct smb_filename *dst_fname_tmp = NULL; >+ >+ status = SMB_VFS_NEXT_COPY_CHUNK_RECV(state->handle, >+ subreq, >+ &state->copied); >+ TALLOC_FREE(subreq); >+ if (tevent_req_nterror(req, status)) { >+ return; >+ } >+ >+ if (!state->is_copyfile) { >+ tevent_req_done(req); >+ return; >+ } >+ >+ /* >+ * Now copy all reamining streams. We know the share supports >+ * streams, because we're in vfs_fruit. We don't do this async >+ * because streams are few and small. >+ */ >+ status = vfs_streaminfo(state->handle->conn, NULL, >+ state->src_fsp->fsp_name->base_name, >+ req, &num_streams, &streams); >+ if (tevent_req_nterror(req, status)) { >+ return; >+ } >+ >+ if (num_streams == 1) { >+ /* There is always one stream, ::$DATA. */ >+ tevent_req_done(req); >+ return; >+ } >+ >+ for (i = 0; i < num_streams; i++) { >+ DEBUG(10, ("%s: stream: '%s'/%zd\n", >+ __func__, streams[i].name, streams[i].size)); >+ >+ src_fname_tmp = synthetic_smb_fname( >+ req, >+ state->src_fsp->fsp_name->base_name, >+ streams[i].name, >+ NULL); >+ if (src_fname_tmp == NULL) { >+ tevent_req_nterror(req, NT_STATUS_NO_MEMORY); >+ return; >+ } >+ >+ if (is_ntfs_default_stream_smb_fname(src_fname_tmp)) { >+ TALLOC_FREE(src_fname_tmp); >+ continue; >+ } >+ >+ dst_fname_tmp = synthetic_smb_fname( >+ req, >+ state->dst_fsp->fsp_name->base_name, >+ streams[i].name, >+ NULL); >+ if (dst_fname_tmp == NULL) { >+ tevent_req_nterror(req, NT_STATUS_NO_MEMORY); >+ return; >+ } >+ >+ status = copy_file(req, >+ state->handle->conn, >+ src_fname_tmp, >+ dst_fname_tmp, >+ OPENX_FILE_CREATE_IF_NOT_EXIST, >+ 0, false); >+ if (!NT_STATUS_IS_OK(status)) { >+ DEBUG(1, ("%s: copy %s to %s failed: %s\n", __func__, >+ smb_fname_str_dbg(src_fname_tmp), >+ smb_fname_str_dbg(dst_fname_tmp), >+ nt_errstr(status))); >+ tevent_req_nterror(req, status); >+ return; >+ } >+ >+ TALLOC_FREE(src_fname_tmp); >+ TALLOC_FREE(dst_fname_tmp); >+ } >+ >+ TALLOC_FREE(streams); >+ TALLOC_FREE(src_fname_tmp); >+ TALLOC_FREE(dst_fname_tmp); >+ tevent_req_done(req); >+} >+ >+static NTSTATUS fruit_copy_chunk_recv(struct vfs_handle_struct *handle, >+ struct tevent_req *req, >+ off_t *copied) >+{ >+ struct fruit_cc_state *fruit_cc_state = tevent_req_data( >+ req, struct fruit_cc_state); >+ NTSTATUS status; >+ >+ if (tevent_req_is_nterror(req, &status)) { >+ DEBUG(1, ("server side copy chunk failed: %s\n", >+ nt_errstr(status))); >+ *copied = 0; >+ tevent_req_received(req); >+ return status; >+ } >+ >+ *copied = fruit_cc_state->copied; >+ tevent_req_received(req); >+ >+ return NT_STATUS_OK; >+} >+ > static struct vfs_fn_pointers vfs_fruit_fns = { > .connect_fn = fruit_connect, > >@@ -3483,6 +3698,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, >+ .copy_chunk_send_fn = fruit_copy_chunk_send, >+ .copy_chunk_recv_fn = fruit_copy_chunk_recv, > > /* NT ACL operations */ > .fget_nt_acl_fn = fruit_fget_nt_acl, >diff --git a/source3/smbd/smb2_ioctl_network_fs.c b/source3/smbd/smb2_ioctl_network_fs.c >index 36b8e89..6fa8741 100644 >--- a/source3/smbd/smb2_ioctl_network_fs.c >+++ b/source3/smbd/smb2_ioctl_network_fs.c >@@ -91,6 +91,7 @@ struct fsctl_srv_copychunk_state { > COPYCHUNK_OUT_LIMITS, > COPYCHUNK_OUT_RSP, > } out_data; >+ bool aapl_copyfile; > }; > static void fsctl_srv_copychunk_vfs_done(struct tevent_req *subreq); > >@@ -235,6 +236,35 @@ static struct tevent_req *fsctl_srv_copychunk_send(TALLOC_CTX *mem_ctx, > /* any errors from here onwards should carry copychunk response data */ > state->out_data = COPYCHUNK_OUT_RSP; > >+ if (cc_copy.chunk_count == 0) { >+ struct tevent_req *vfs_subreq; >+ /* >+ * Process as OS X copyfile request. This is currently >+ * copychunk request with a chunk count of 0 we >+ * process. >+ */ >+ if (!state->src_fsp->copyfile_enabled || >+ !state->dst_fsp->copyfile_enabled) { >+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); >+ return tevent_req_post(req, ev); >+ } >+ state->aapl_copyfile = true; >+ vfs_subreq = SMB_VFS_COPY_CHUNK_SEND(dst_fsp->conn, >+ state, ev, >+ state->src_fsp, >+ 0, >+ state->dst_fsp, >+ 0, >+ 0); >+ if (tevent_req_nomem(vfs_subreq, req)) { >+ return tevent_req_post(req, ev); >+ } >+ tevent_req_set_callback(vfs_subreq, >+ fsctl_srv_copychunk_vfs_done, req); >+ state->dispatch_count++; >+ return req; >+ } >+ > for (i = 0; i < cc_copy.chunk_count; i++) { > struct tevent_req *vfs_subreq; > chunk = &cc_copy.chunks[i]; >@@ -327,7 +357,11 @@ static NTSTATUS fsctl_srv_copychunk_recv(struct tevent_req *req, > *pack_rsp = true; > break; > case COPYCHUNK_OUT_RSP: >- cc_rsp->chunks_written = state->recv_count - state->bad_recv_count; >+ if (state->aapl_copyfile == true) { >+ cc_rsp->chunks_written = 0; >+ } else { >+ cc_rsp->chunks_written = state->recv_count - state->bad_recv_count; >+ } > cc_rsp->chunk_bytes_written = 0; > cc_rsp->total_bytes_written = state->total_written; > *pack_rsp = true; >-- >2.1.0 > > >From 4155574302c32ddeeb31463d870930a11a480e08 Mon Sep 17 00:00:00 2001 >From: Ralph Boehme <slow@samba.org> >Date: Wed, 10 Jun 2015 15:30:04 +0200 >Subject: [PATCH 4/4] s4:torture:vfs_fruit: copyfile > >Bug: https://bugzilla.samba.org/show_bug.cgi?id=11317 > >Signed-off-by: Ralph Boehme <slow@samba.org> >--- > selftest/target/Samba3.pm | 3 + > selftest/target/Samba4.pm | 3 + > source4/torture/vfs/fruit.c | 450 +++++++++++++++++++++++++++++++++++++++++++- > 3 files changed, 455 insertions(+), 1 deletion(-) > >diff --git a/selftest/target/Samba3.pm b/selftest/target/Samba3.pm >index 50898f2..f761ba0 100755 >--- a/selftest/target/Samba3.pm >+++ b/selftest/target/Samba3.pm >@@ -1214,6 +1214,9 @@ sub provision($$$$$$$$) > # fsrvp server requires registry shares > registry shares = yes > >+ # fruit:copyfile is a global option >+ fruit:copyfile = yes >+ > # Begin extra options > $extra_options > # End extra options >diff --git a/selftest/target/Samba4.pm b/selftest/target/Samba4.pm >index 3a5b409..4f9aaea 100755 >--- a/selftest/target/Samba4.pm >+++ b/selftest/target/Samba4.pm >@@ -917,6 +917,9 @@ sub provision($$$$$$$$$$) > lanman auth = yes > allow nt4 crypto = yes > >+ # fruit:copyfile is a global option >+ fruit:copyfile = yes >+ > $extra_smbconf_options > > [tmp] >diff --git a/source4/torture/vfs/fruit.c b/source4/torture/vfs/fruit.c >index 162885e..e04d995 100644 >--- a/source4/torture/vfs/fruit.c >+++ b/source4/torture/vfs/fruit.c >@@ -34,8 +34,11 @@ > #include "torture/util.h" > #include "torture/smb2/proto.h" > #include "torture/vfs/proto.h" >+#include "librpc/gen_ndr/ndr_ioctl.h" > > #define BASEDIR "vfs_fruit_dir" >+#define FNAME_CC_SRC "testfsctl.dat" >+#define FNAME_CC_DST "testfsctl2.dat" > > #define CHECK_STATUS(status, correct) do { \ > if (!NT_STATUS_EQUAL(status, correct)) { \ >@@ -1473,7 +1476,8 @@ static bool test_aapl(struct torture_context *tctx, > aapl_server_caps = BVAL(aapl->data.data, 16); > if (aapl_server_caps != (SMB2_CRTCTX_AAPL_UNIX_BASED | > SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR | >- SMB2_CRTCTX_AAPL_SUPPORTS_NFS_ACE)) { >+ SMB2_CRTCTX_AAPL_SUPPORTS_NFS_ACE | >+ SMB2_CRTCTX_AAPL_SUPPORTS_OSX_COPYFILE)) { > torture_result(tctx, TORTURE_FAIL, > "(%s) unexpected server_caps: %d", > __location__, (int)aapl_server_caps); >@@ -1624,6 +1628,449 @@ done: > return ret; > } > >+static uint64_t patt_hash(uint64_t off) >+{ >+ return off; >+} >+ >+static bool write_pattern(struct torture_context *torture, >+ struct smb2_tree *tree, TALLOC_CTX *mem_ctx, >+ struct smb2_handle h, uint64_t off, uint64_t len, >+ uint64_t patt_off) >+{ >+ NTSTATUS status; >+ uint64_t i; >+ uint8_t *buf; >+ uint64_t io_sz = MIN(1024 * 64, len); >+ >+ if (len == 0) { >+ return true; >+ } >+ >+ torture_assert(torture, (len % 8) == 0, "invalid write len"); >+ >+ buf = talloc_zero_size(mem_ctx, io_sz); >+ torture_assert(torture, (buf != NULL), "no memory for file data buf"); >+ >+ while (len > 0) { >+ for (i = 0; i <= io_sz - 8; i += 8) { >+ SBVAL(buf, i, patt_hash(patt_off)); >+ patt_off += 8; >+ } >+ >+ status = smb2_util_write(tree, h, >+ buf, off, io_sz); >+ torture_assert_ntstatus_ok(torture, status, "file write"); >+ >+ len -= io_sz; >+ off += io_sz; >+ } >+ >+ talloc_free(buf); >+ >+ return true; >+} >+ >+static bool check_pattern(struct torture_context *torture, >+ struct smb2_tree *tree, TALLOC_CTX *mem_ctx, >+ struct smb2_handle h, uint64_t off, uint64_t len, >+ uint64_t patt_off) >+{ >+ if (len == 0) { >+ return true; >+ } >+ >+ torture_assert(torture, (len % 8) == 0, "invalid read len"); >+ >+ while (len > 0) { >+ uint64_t i; >+ struct smb2_read r; >+ NTSTATUS status; >+ uint64_t io_sz = MIN(1024 * 64, len); >+ >+ ZERO_STRUCT(r); >+ r.in.file.handle = h; >+ r.in.length = io_sz; >+ r.in.offset = off; >+ status = smb2_read(tree, mem_ctx, &r); >+ torture_assert_ntstatus_ok(torture, status, "read"); >+ >+ torture_assert_u64_equal(torture, r.out.data.length, io_sz, >+ "read data len mismatch"); >+ >+ for (i = 0; i <= io_sz - 8; i += 8, patt_off += 8) { >+ uint64_t data = BVAL(r.out.data.data, i); >+ torture_assert_u64_equal(torture, data, patt_hash(patt_off), >+ talloc_asprintf(torture, "read data " >+ "pattern bad at %llu\n", >+ (unsigned long long)off + i)); >+ } >+ talloc_free(r.out.data.data); >+ len -= io_sz; >+ off += io_sz; >+ } >+ >+ return true; >+} >+ >+static bool test_setup_open(struct torture_context *torture, >+ struct smb2_tree *tree, TALLOC_CTX *mem_ctx, >+ const char *fname, >+ struct smb2_handle *fh, >+ uint32_t desired_access, >+ uint32_t file_attributes) >+{ >+ struct smb2_create io; >+ NTSTATUS status; >+ >+ ZERO_STRUCT(io); >+ io.in.desired_access = desired_access; >+ io.in.file_attributes = file_attributes; >+ io.in.create_disposition = NTCREATEX_DISP_OPEN_IF; >+ io.in.share_access = >+ NTCREATEX_SHARE_ACCESS_DELETE| >+ NTCREATEX_SHARE_ACCESS_READ| >+ NTCREATEX_SHARE_ACCESS_WRITE; >+ if (file_attributes & FILE_ATTRIBUTE_DIRECTORY) { >+ io.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; >+ } >+ io.in.fname = fname; >+ >+ status = smb2_create(tree, mem_ctx, &io); >+ torture_assert_ntstatus_ok(torture, status, "file create"); >+ >+ *fh = io.out.file.handle; >+ >+ return true; >+} >+ >+static bool test_setup_create_fill(struct torture_context *torture, >+ struct smb2_tree *tree, TALLOC_CTX *mem_ctx, >+ const char *fname, >+ struct smb2_handle *fh, >+ uint64_t size, >+ uint32_t desired_access, >+ uint32_t file_attributes) >+{ >+ bool ok; >+ >+ ok = test_setup_open(torture, tree, mem_ctx, >+ fname, >+ fh, >+ desired_access, >+ file_attributes); >+ torture_assert(torture, ok, "file open"); >+ >+ if (size > 0) { >+ ok = write_pattern(torture, tree, mem_ctx, *fh, 0, size, 0); >+ torture_assert(torture, ok, "write pattern"); >+ } >+ return true; >+} >+ >+static bool test_setup_copy_chunk(struct torture_context *torture, >+ struct smb2_tree *tree, TALLOC_CTX *mem_ctx, >+ uint32_t nchunks, >+ struct smb2_handle *src_h, >+ uint64_t src_size, >+ uint32_t src_desired_access, >+ struct smb2_handle *dest_h, >+ uint64_t dest_size, >+ uint32_t dest_desired_access, >+ struct srv_copychunk_copy *cc_copy, >+ union smb_ioctl *ioctl) >+{ >+ struct req_resume_key_rsp res_key; >+ bool ok; >+ NTSTATUS status; >+ enum ndr_err_code ndr_ret; >+ >+ ok = test_setup_create_fill(torture, tree, mem_ctx, FNAME_CC_SRC, >+ src_h, src_size, src_desired_access, >+ FILE_ATTRIBUTE_NORMAL); >+ torture_assert(torture, ok, "src file create fill"); >+ >+ ok = test_setup_create_fill(torture, tree, mem_ctx, FNAME_CC_DST, >+ dest_h, dest_size, dest_desired_access, >+ FILE_ATTRIBUTE_NORMAL); >+ torture_assert(torture, ok, "dest file create fill"); >+ >+ ZERO_STRUCTPN(ioctl); >+ ioctl->smb2.level = RAW_IOCTL_SMB2; >+ ioctl->smb2.in.file.handle = *src_h; >+ ioctl->smb2.in.function = FSCTL_SRV_REQUEST_RESUME_KEY; >+ /* Allow for Key + ContextLength + Context */ >+ ioctl->smb2.in.max_response_size = 32; >+ ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL; >+ >+ status = smb2_ioctl(tree, mem_ctx, &ioctl->smb2); >+ torture_assert_ntstatus_ok(torture, status, >+ "FSCTL_SRV_REQUEST_RESUME_KEY"); >+ >+ ndr_ret = ndr_pull_struct_blob(&ioctl->smb2.out.out, mem_ctx, &res_key, >+ (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp); >+ >+ torture_assert_ndr_success(torture, ndr_ret, >+ "ndr_pull_req_resume_key_rsp"); >+ >+ ZERO_STRUCTPN(ioctl); >+ ioctl->smb2.level = RAW_IOCTL_SMB2; >+ ioctl->smb2.in.file.handle = *dest_h; >+ ioctl->smb2.in.function = FSCTL_SRV_COPYCHUNK; >+ ioctl->smb2.in.max_response_size = sizeof(struct srv_copychunk_rsp); >+ ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL; >+ >+ ZERO_STRUCTPN(cc_copy); >+ memcpy(cc_copy->source_key, res_key.resume_key, ARRAY_SIZE(cc_copy->source_key)); >+ cc_copy->chunk_count = nchunks; >+ cc_copy->chunks = talloc_zero_array(mem_ctx, struct srv_copychunk, nchunks); >+ torture_assert(torture, (cc_copy->chunks != NULL), "no memory for chunks"); >+ >+ return true; >+} >+ >+ >+static bool check_copy_chunk_rsp(struct torture_context *torture, >+ struct srv_copychunk_rsp *cc_rsp, >+ uint32_t ex_chunks_written, >+ uint32_t ex_chunk_bytes_written, >+ uint32_t ex_total_bytes_written) >+{ >+ torture_assert_int_equal(torture, cc_rsp->chunks_written, >+ ex_chunks_written, "num chunks"); >+ torture_assert_int_equal(torture, cc_rsp->chunk_bytes_written, >+ ex_chunk_bytes_written, "chunk bytes written"); >+ torture_assert_int_equal(torture, cc_rsp->total_bytes_written, >+ ex_total_bytes_written, "chunk total bytes"); >+ return true; >+} >+ >+static bool neg_aapl_copyfile(struct torture_context *tctx, >+ struct smb2_tree *tree, >+ uint64_t flags) >+{ >+ TALLOC_CTX *mem_ctx = talloc_new(tctx); >+ const char *fname = "aapl"; >+ NTSTATUS status; >+ struct smb2_create io; >+ DATA_BLOB data; >+ struct smb2_create_blob *aapl = NULL; >+ uint32_t aapl_cmd; >+ uint32_t aapl_reply_bitmap; >+ uint32_t aapl_server_caps; >+ bool ret = true; >+ >+ ZERO_STRUCT(io); >+ io.in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED; >+ io.in.file_attributes = FILE_ATTRIBUTE_NORMAL; >+ io.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF; >+ io.in.share_access = (NTCREATEX_SHARE_ACCESS_DELETE | >+ NTCREATEX_SHARE_ACCESS_READ | >+ NTCREATEX_SHARE_ACCESS_WRITE); >+ io.in.fname = fname; >+ >+ data = data_blob_talloc(mem_ctx, NULL, 3 * sizeof(uint64_t)); >+ SBVAL(data.data, 0, SMB2_CRTCTX_AAPL_SERVER_QUERY); >+ SBVAL(data.data, 8, (SMB2_CRTCTX_AAPL_SERVER_CAPS)); >+ SBVAL(data.data, 16, flags); >+ >+ status = smb2_create_blob_add(tctx, &io.in.blobs, "AAPL", data); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ >+ status = smb2_create(tree, tctx, &io); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ >+ aapl = smb2_create_blob_find(&io.out.blobs, >+ SMB2_CREATE_TAG_AAPL); >+ if (aapl == NULL) { >+ ret = false; >+ goto done; >+ >+ } >+ if (aapl->data.length < 24) { >+ ret = false; >+ goto done; >+ } >+ >+ aapl_cmd = IVAL(aapl->data.data, 0); >+ if (aapl_cmd != SMB2_CRTCTX_AAPL_SERVER_QUERY) { >+ torture_result(tctx, TORTURE_FAIL, >+ "(%s) unexpected cmd: %d", >+ __location__, (int)aapl_cmd); >+ ret = false; >+ goto done; >+ } >+ >+ aapl_reply_bitmap = BVAL(aapl->data.data, 8); >+ if (!(aapl_reply_bitmap & SMB2_CRTCTX_AAPL_SERVER_CAPS)) { >+ torture_result(tctx, TORTURE_FAIL, >+ "(%s) unexpected reply_bitmap: %d", >+ __location__, (int)aapl_reply_bitmap); >+ ret = false; >+ goto done; >+ } >+ >+ aapl_server_caps = BVAL(aapl->data.data, 16); >+ if (!(aapl_server_caps & flags)) { >+ torture_result(tctx, TORTURE_FAIL, >+ "(%s) unexpected server_caps: %d", >+ __location__, (int)aapl_server_caps); >+ ret = false; >+ goto done; >+ } >+ >+done: >+ status = smb2_util_close(tree, io.out.file.handle); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ >+ smb2_util_unlink(tree, "aapl"); >+ talloc_free(mem_ctx); >+ return ret; >+} >+ >+static bool test_copyfile(struct torture_context *torture, >+ struct smb2_tree *tree) >+{ >+ struct smb2_handle src_h; >+ struct smb2_handle dest_h; >+ NTSTATUS status; >+ union smb_ioctl ioctl; >+ TALLOC_CTX *tmp_ctx = talloc_new(tree); >+ struct srv_copychunk_copy cc_copy; >+ struct srv_copychunk_rsp cc_rsp; >+ enum ndr_err_code ndr_ret; >+ bool ok; >+ >+ /* >+ * First test a copy_chunk with a 0 chunk count without having >+ * enabled this via AAPL. The request must not fail and the >+ * copied length in the response must be 0. This is verified >+ * against Windows 2008r2. >+ */ >+ >+ ok = test_setup_copy_chunk(torture, tree, tmp_ctx, >+ 0, /* 0 chunks, copyfile semantics */ >+ &src_h, 4096, /* fill 4096 byte src file */ >+ SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA, >+ &dest_h, 0, /* 0 byte dest file */ >+ SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA, >+ &cc_copy, >+ &ioctl); >+ if (!ok) { >+ torture_fail_goto(torture, done, "setup copy chunk error"); >+ } >+ >+ ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx, >+ &cc_copy, >+ (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy); >+ torture_assert_ndr_success(torture, ndr_ret, >+ "ndr_push_srv_copychunk_copy"); >+ >+ status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); >+ torture_assert_ntstatus_ok_goto(torture, status, ok, done, "FSCTL_SRV_COPYCHUNK"); >+ >+ ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx, >+ &cc_rsp, >+ (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp); >+ torture_assert_ndr_success(torture, ndr_ret, >+ "ndr_pull_srv_copychunk_rsp"); >+ >+ ok = check_copy_chunk_rsp(torture, &cc_rsp, >+ 0, /* chunks written */ >+ 0, /* chunk bytes unsuccessfully written */ >+ 0); /* total bytes written */ >+ if (!ok) { >+ torture_fail_goto(torture, done, "bad copy chunk response data"); >+ } >+ >+ /* >+ * Now enable AAPL copyfile and test again, the file and the >+ * stream must be copied by the server. >+ */ >+ ok = neg_aapl_copyfile(torture, tree, >+ SMB2_CRTCTX_AAPL_SUPPORTS_OSX_COPYFILE); >+ if (!ok) { >+ torture_skip_goto(torture, done, "missing AAPL copyfile"); >+ goto done; >+ } >+ >+ smb2_util_close(tree, src_h); >+ smb2_util_close(tree, dest_h); >+ smb2_util_unlink(tree, FNAME_CC_SRC); >+ smb2_util_unlink(tree, FNAME_CC_DST); >+ >+ ok = torture_setup_file(tmp_ctx, tree, FNAME_CC_SRC, false); >+ if (!ok) { >+ torture_fail(torture, "setup file error"); >+ } >+ ok = write_stream(tree, __location__, torture, tmp_ctx, >+ FNAME_CC_SRC, AFPRESOURCE_STREAM, >+ 10, 10, "1234567890"); >+ if (!ok) { >+ torture_fail(torture, "setup stream error"); >+ } >+ >+ ok = test_setup_copy_chunk(torture, tree, tmp_ctx, >+ 0, /* 0 chunks, copyfile semantics */ >+ &src_h, 4096, /* fill 4096 byte src file */ >+ SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA, >+ &dest_h, 0, /* 0 byte dest file */ >+ SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA, >+ &cc_copy, >+ &ioctl); >+ if (!ok) { >+ torture_fail_goto(torture, done, "setup copy chunk error"); >+ } >+ >+ ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx, >+ &cc_copy, >+ (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy); >+ torture_assert_ndr_success(torture, ndr_ret, >+ "ndr_push_srv_copychunk_copy"); >+ >+ status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); >+ torture_assert_ntstatus_ok_goto(torture, status, ok, done, "FSCTL_SRV_COPYCHUNK"); >+ >+ ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx, >+ &cc_rsp, >+ (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp); >+ torture_assert_ndr_success(torture, ndr_ret, >+ "ndr_pull_srv_copychunk_rsp"); >+ >+ ok = check_copy_chunk_rsp(torture, &cc_rsp, >+ 0, /* chunks written */ >+ 0, /* chunk bytes unsuccessfully written */ >+ 4096); /* total bytes written */ >+ if (!ok) { >+ torture_fail_goto(torture, done, "bad copy chunk response data"); >+ } >+ >+ ok = test_setup_open(torture, tree, tmp_ctx, FNAME_CC_DST, &dest_h, >+ SEC_FILE_READ_DATA, FILE_ATTRIBUTE_NORMAL); >+ if (!ok) { >+ torture_fail_goto(torture, done,"open failed"); >+ } >+ ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0); >+ if (!ok) { >+ torture_fail_goto(torture, done, "inconsistent file data"); >+ } >+ >+ ok = check_stream(tree, __location__, torture, tmp_ctx, >+ FNAME_CC_DST, AFPRESOURCE_STREAM, >+ 0, 20, 10, 10, "1234567890"); >+ if (!ok) { >+ torture_fail_goto(torture, done, "inconsistent stream data"); >+ } >+ >+done: >+ smb2_util_close(tree, src_h); >+ smb2_util_close(tree, dest_h); >+ smb2_util_unlink(tree, FNAME_CC_SRC); >+ smb2_util_unlink(tree, FNAME_CC_DST); >+ talloc_free(tmp_ctx); >+ return true; >+} >+ > /* > * Note: This test depends on "vfs objects = catia fruit > * streams_xattr". Note: To run this test, use >@@ -1638,6 +2085,7 @@ struct torture_suite *torture_vfs_fruit(void) > > suite->description = talloc_strdup(suite, "vfs_fruit tests"); > >+ torture_suite_add_1smb2_test(suite, "copyfile", test_copyfile); > torture_suite_add_2ns_smb2_test(suite, "read metadata", test_read_atalk_metadata); > torture_suite_add_2ns_smb2_test(suite, "write metadata", test_write_atalk_metadata); > torture_suite_add_2ns_smb2_test(suite, "resource fork IO", test_write_atalk_rfork_io); >-- >2.1.0 >
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Raw
Actions:
View
Attachments on
bug 11317
:
11133
|
11134
|
11143
|
11152
|
11156
|
11168
|
11324
|
11327