[PATCH] Fix the new async copy-chunk for streams
Ralph Böhme
slow at samba.org
Sat May 13 14:06:03 UTC 2017
Hi!
A few days ago I noticed that a recent macOS client requests copy-chunk of named
streams and we fail miserably. That's my fault: since making copy-chunk async
[1] in vfs_default, we use the async pread/pwrite VFS versions to do the actual
copy. :/
With vfs_streams_xattr this silently copies data in the default data stream, not
in the requested named stream, so the copy-chunk seems to succeed.
After requesting the copy-chunk, the client does a setinfo(size) on the stream,
so after this the stream has the correct size, alas it's all 0 -- voila, silent
(meta)data corruption.
Luckily the new async copy-chunk is only in master, so even though I filed a
bugreport, we don't need to backport and the alarm whistel can stay off. *phew*
The attached patch adds implementations of async pread and pwrite to
vfs_streams_xattr and vfs_fruit. vfs_streams_depot doesn't need changes, as it
works correctly with the vfs_default implementation of async pread/pwrite as it
provides usable fds.
Btw, with these changes in place, the only thing missing for allowing read/write aio
on named streams is flush_send/recv. Or was there any other reason for not
allowing aio on named streams?
Fwiw, a Windows server supports copy-chunk of named streams.
Please review & push if ok. Thanks!
-slow
[1] 60e45a2d25401eaf9a15a86d19114670ccfde259
-------------- next part --------------
From 214a0e2ac8efa4d39f89ffbbc53448abbe1f229a Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Fri, 12 May 2017 07:58:01 +0200
Subject: [PATCH 1/5] vfs_streams_xattr: add pread_send/recv and
pwrite_send/recv
This is needed to support copy-chunk of streams. vfs_default issues
calls to async pread and pwrite (send/recv versions) since commit
60e45a2d25401eaf9a15a86d19114670ccfde259.
Bug: https://bugzilla.samba.org/show_bug.cgi?id=12787
Signed-off-by: Ralph Boehme <slow at samba.org>
---
source3/modules/vfs_streams_xattr.c | 165 ++++++++++++++++++++++++++++++++++++
1 file changed, 165 insertions(+)
diff --git a/source3/modules/vfs_streams_xattr.c b/source3/modules/vfs_streams_xattr.c
index 2943e52..f481f27 100644
--- a/source3/modules/vfs_streams_xattr.c
+++ b/source3/modules/vfs_streams_xattr.c
@@ -25,6 +25,7 @@
#include "smbd/smbd.h"
#include "system/filesys.h"
#include "../lib/crypto/md5.h"
+#include "lib/util/tevent_unix.h"
#undef DBGC_CLASS
#define DBGC_CLASS DBGC_VFS
@@ -1048,6 +1049,166 @@ static ssize_t streams_xattr_pread(vfs_handle_struct *handle,
return overlap;
}
+struct streams_xattr_pread_state {
+ ssize_t nread;
+ struct vfs_aio_state vfs_aio_state;
+};
+
+static void streams_xattr_pread_done(struct tevent_req *subreq);
+
+static struct tevent_req *streams_xattr_pread_send(
+ struct vfs_handle_struct *handle,
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct files_struct *fsp,
+ void *data,
+ size_t n, off_t offset)
+{
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+ struct streams_xattr_pread_state *state = NULL;
+ struct stream_io *sio =
+ (struct stream_io *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct streams_xattr_pread_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ if (sio == NULL) {
+ subreq = SMB_VFS_NEXT_PREAD_SEND(state, ev, handle, fsp,
+ data, n, offset);
+ if (tevent_req_nomem(req, subreq)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, streams_xattr_pread_done, req);
+ return req;
+ }
+
+ state->nread = SMB_VFS_PREAD(fsp, data, n, offset);
+ if (state->nread != n) {
+ if (state->nread != -1) {
+ errno = EIO;
+ }
+ tevent_req_error(req, errno);
+ return tevent_req_post(req, ev);
+ }
+
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+}
+
+static void streams_xattr_pread_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct streams_xattr_pread_state *state = tevent_req_data(
+ req, struct streams_xattr_pread_state);
+
+ state->nread = SMB_VFS_PREAD_RECV(subreq, &state->vfs_aio_state);
+ TALLOC_FREE(subreq);
+
+ if (tevent_req_error(req, state->vfs_aio_state.error)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+static ssize_t streams_xattr_pread_recv(struct tevent_req *req,
+ struct vfs_aio_state *vfs_aio_state)
+{
+ struct streams_xattr_pread_state *state = tevent_req_data(
+ req, struct streams_xattr_pread_state);
+
+ if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
+ return -1;
+ }
+
+ *vfs_aio_state = state->vfs_aio_state;
+ return state->nread;
+}
+
+struct streams_xattr_pwrite_state {
+ ssize_t nwritten;
+ struct vfs_aio_state vfs_aio_state;
+};
+
+static void streams_xattr_pwrite_done(struct tevent_req *subreq);
+
+static struct tevent_req *streams_xattr_pwrite_send(
+ struct vfs_handle_struct *handle,
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct files_struct *fsp,
+ const void *data,
+ size_t n, off_t offset)
+{
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+ struct streams_xattr_pwrite_state *state = NULL;
+ struct stream_io *sio =
+ (struct stream_io *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct streams_xattr_pwrite_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ if (sio == NULL) {
+ subreq = SMB_VFS_NEXT_PWRITE_SEND(state, ev, handle, fsp,
+ data, n, offset);
+ if (tevent_req_nomem(req, subreq)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, streams_xattr_pwrite_done, req);
+ return req;
+ }
+
+ state->nwritten = SMB_VFS_PWRITE(fsp, data, n, offset);
+ if (state->nwritten != n) {
+ if (state->nwritten != -1) {
+ errno = EIO;
+ }
+ tevent_req_error(req, errno);
+ return tevent_req_post(req, ev);
+ }
+
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+}
+
+static void streams_xattr_pwrite_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct streams_xattr_pwrite_state *state = tevent_req_data(
+ req, struct streams_xattr_pwrite_state);
+
+ state->nwritten = SMB_VFS_PWRITE_RECV(subreq, &state->vfs_aio_state);
+ TALLOC_FREE(subreq);
+
+ if (tevent_req_error(req, state->vfs_aio_state.error)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+static ssize_t streams_xattr_pwrite_recv(struct tevent_req *req,
+ struct vfs_aio_state *vfs_aio_state)
+{
+ struct streams_xattr_pwrite_state *state = tevent_req_data(
+ req, struct streams_xattr_pwrite_state);
+
+ if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
+ return -1;
+ }
+
+ *vfs_aio_state = state->vfs_aio_state;
+ return state->nwritten;
+}
+
static int streams_xattr_ftruncate(struct vfs_handle_struct *handle,
struct files_struct *fsp,
off_t offset)
@@ -1151,6 +1312,10 @@ static struct vfs_fn_pointers vfs_streams_xattr_fns = {
.lstat_fn = streams_xattr_lstat,
.pread_fn = streams_xattr_pread,
.pwrite_fn = streams_xattr_pwrite,
+ .pread_send_fn = streams_xattr_pread_send,
+ .pread_recv_fn = streams_xattr_pread_recv,
+ .pwrite_send_fn = streams_xattr_pwrite_send,
+ .pwrite_recv_fn = streams_xattr_pwrite_recv,
.unlink_fn = streams_xattr_unlink,
.rename_fn = streams_xattr_rename,
.ftruncate_fn = streams_xattr_ftruncate,
--
2.9.3
From 569ba0f4dbb65fafb3c7793967ae9318cfdf921d Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Fri, 12 May 2017 14:40:03 +0200
Subject: [PATCH 2/5] vfs_fruit: add pread_send/recv and pwrite_send/recv
This is needed to support copy-chunk of streams. vfs_default issues
calls to async pread and pwrite (send/recv versions) since
commit60e45a2d25401eaf9a15a86d19114670ccfde259.
Bug: https://bugzilla.samba.org/show_bug.cgi?id=12787
Signed-off-by: Ralph Boehme <slow at samba.org>
---
source3/modules/vfs_fruit.c | 182 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 182 insertions(+)
diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c
index 273540e..63acdf8 100644
--- a/source3/modules/vfs_fruit.c
+++ b/source3/modules/vfs_fruit.c
@@ -31,6 +31,7 @@
#include "../libcli/smb/smb2_create_ctx.h"
#include "lib/util/sys_rw.h"
#include "lib/util/tevent_ntstatus.h"
+#include "lib/util/tevent_unix.h"
/*
* Enhanced OS X and Netatalk compatibility
@@ -3755,6 +3756,105 @@ static ssize_t fruit_pread(vfs_handle_struct *handle,
return nread;
}
+static bool fruit_must_handle_aio_stream(struct fio *fio)
+{
+ if (fio == NULL) {
+ return false;
+ };
+
+ if ((fio->type == ADOUBLE_META) &&
+ (fio->config->meta == FRUIT_META_NETATALK))
+ {
+ return true;
+ }
+
+ if ((fio->type == ADOUBLE_RSRC) &&
+ (fio->config->rsrc == FRUIT_RSRC_ADFILE))
+ {
+ return true;
+ }
+
+ return false;
+}
+
+struct fruit_pread_state {
+ ssize_t nread;
+ struct vfs_aio_state vfs_aio_state;
+};
+
+static void fruit_pread_done(struct tevent_req *subreq);
+
+static struct tevent_req *fruit_pread_send(
+ struct vfs_handle_struct *handle,
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct files_struct *fsp,
+ void *data,
+ size_t n, off_t offset)
+{
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+ struct fruit_pread_state *state = NULL;
+ struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct fruit_pread_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ if (fruit_must_handle_aio_stream(fio)) {
+ state->nread = SMB_VFS_PREAD(fsp, data, n, offset);
+ if (state->nread != n) {
+ if (state->nread != -1) {
+ errno = EIO;
+ }
+ tevent_req_error(req, errno);
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = SMB_VFS_NEXT_PREAD_SEND(state, ev, handle, fsp,
+ data, n, offset);
+ if (tevent_req_nomem(req, subreq)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, fruit_pread_done, req);
+ return req;
+}
+
+static void fruit_pread_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct fruit_pread_state *state = tevent_req_data(
+ req, struct fruit_pread_state);
+
+ state->nread = SMB_VFS_PREAD_RECV(subreq, &state->vfs_aio_state);
+ TALLOC_FREE(subreq);
+
+ if (tevent_req_error(req, state->vfs_aio_state.error)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+static ssize_t fruit_pread_recv(struct tevent_req *req,
+ struct vfs_aio_state *vfs_aio_state)
+{
+ struct fruit_pread_state *state = tevent_req_data(
+ req, struct fruit_pread_state);
+
+ if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
+ return -1;
+ }
+
+ *vfs_aio_state = state->vfs_aio_state;
+ return state->nread;
+}
+
static ssize_t fruit_pwrite_meta_stream(vfs_handle_struct *handle,
files_struct *fsp, const void *data,
size_t n, off_t offset)
@@ -3979,6 +4079,84 @@ static ssize_t fruit_pwrite(vfs_handle_struct *handle,
return nwritten;
}
+struct fruit_pwrite_state {
+ ssize_t nwritten;
+ struct vfs_aio_state vfs_aio_state;
+};
+
+static void fruit_pwrite_done(struct tevent_req *subreq);
+
+static struct tevent_req *fruit_pwrite_send(
+ struct vfs_handle_struct *handle,
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct files_struct *fsp,
+ const void *data,
+ size_t n, off_t offset)
+{
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+ struct fruit_pwrite_state *state = NULL;
+ struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct fruit_pwrite_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ if (fruit_must_handle_aio_stream(fio)) {
+ state->nwritten = SMB_VFS_PWRITE(fsp, data, n, offset);
+ if (state->nwritten != n) {
+ if (state->nwritten != -1) {
+ errno = EIO;
+ }
+ tevent_req_error(req, errno);
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = SMB_VFS_NEXT_PWRITE_SEND(state, ev, handle, fsp,
+ data, n, offset);
+ if (tevent_req_nomem(req, subreq)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, fruit_pwrite_done, req);
+ return req;
+}
+
+static void fruit_pwrite_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct fruit_pwrite_state *state = tevent_req_data(
+ req, struct fruit_pwrite_state);
+
+ state->nwritten = SMB_VFS_PWRITE_RECV(subreq, &state->vfs_aio_state);
+ TALLOC_FREE(subreq);
+
+ if (tevent_req_error(req, state->vfs_aio_state.error)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+static ssize_t fruit_pwrite_recv(struct tevent_req *req,
+ struct vfs_aio_state *vfs_aio_state)
+{
+ struct fruit_pwrite_state *state = tevent_req_data(
+ req, struct fruit_pwrite_state);
+
+ if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
+ return -1;
+ }
+
+ *vfs_aio_state = state->vfs_aio_state;
+ return state->nwritten;
+}
+
/**
* Helper to stat/lstat the base file of an smb_fname.
*/
@@ -5427,6 +5605,10 @@ static struct vfs_fn_pointers vfs_fruit_fns = {
.open_fn = fruit_open,
.pread_fn = fruit_pread,
.pwrite_fn = fruit_pwrite,
+ .pread_send_fn = fruit_pread_send,
+ .pread_recv_fn = fruit_pread_recv,
+ .pwrite_send_fn = fruit_pwrite_send,
+ .pwrite_recv_fn = fruit_pwrite_recv,
.stat_fn = fruit_stat,
.lstat_fn = fruit_lstat,
.fstat_fn = fruit_fstat,
--
2.9.3
From b8e99bbef3f0dc37bd35a983a631c850d8d12ba4 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Fri, 12 May 2017 17:09:08 +0200
Subject: [PATCH 3/5] lib/torture: add two more ndr assert macros
Bug: https://bugzilla.samba.org/show_bug.cgi?id=12787
Signed-off-by: Ralph Boehme <slow at samba.org>
---
lib/torture/torture.h | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/lib/torture/torture.h b/lib/torture/torture.h
index 668458a..6b373a9 100644
--- a/lib/torture/torture.h
+++ b/lib/torture/torture.h
@@ -293,6 +293,15 @@ void torture_result(struct torture_context *test,
}\
} while(0)
+#define torture_assert_ndr_err_equal_goto(torture_ctx,got,expected,ret,label,cmt) \
+ do { enum ndr_err_code __got = got, __expected = expected; \
+ if (__got != __expected) { \
+ torture_result(torture_ctx, TORTURE_FAIL, __location__": "#got" was %d (%s), expected %d (%s): %s", __got, ndr_errstr(__got), __expected, __STRING(expected), cmt); \
+ ret = false; \
+ goto label; \
+ }\
+ } while(0)
+
#define torture_assert_hresult_equal(torture_ctx, got, expected, cmt) \
do { HRESULT __got = got, __expected = expected; \
if (!HRES_IS_EQUAL(__got, __expected)) { \
@@ -647,6 +656,9 @@ static inline void torture_dump_data_str_cb(const char *buf, void *private_data)
#define torture_assert_ndr_success(torture_ctx,expr,cmt) \
torture_assert_ndr_err_equal(torture_ctx,expr,NDR_ERR_SUCCESS,cmt)
+#define torture_assert_ndr_success_goto(torture_ctx,expr,ret,label,cmt) \
+ torture_assert_ndr_err_equal_goto(torture_ctx,expr,NDR_ERR_SUCCESS,ret,label,cmt)
+
#define torture_assert_hresult_ok(torture_ctx,expr,cmt) \
torture_assert_hresult_equal(torture_ctx,expr,HRES_ERROR(0), cmt)
--
2.9.3
From 956e5360a173eccb5ed84c186ba38b404511b858 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Fri, 12 May 2017 14:56:53 +0200
Subject: [PATCH 4/5] s4/torture: vfs_fruit: add src and dst path args to
test_setup_copy_chunk
Just let the caller pass in the paths, no change in behaviour. A new
test in a subsequent commit will use it to pass paths to streams.
Bug: https://bugzilla.samba.org/show_bug.cgi?id=12787
Signed-off-by: Ralph Boehme <slow at samba.org>
---
source4/torture/vfs/fruit.c | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/source4/torture/vfs/fruit.c b/source4/torture/vfs/fruit.c
index 96edec2..7d59fee 100644
--- a/source4/torture/vfs/fruit.c
+++ b/source4/torture/vfs/fruit.c
@@ -2353,9 +2353,11 @@ static bool test_setup_create_fill(struct torture_context *torture,
static bool test_setup_copy_chunk(struct torture_context *torture,
struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
uint32_t nchunks,
+ const char *src_name,
struct smb2_handle *src_h,
uint64_t src_size,
uint32_t src_desired_access,
+ const char *dst_name,
struct smb2_handle *dest_h,
uint64_t dest_size,
uint32_t dest_desired_access,
@@ -2367,12 +2369,12 @@ static bool test_setup_copy_chunk(struct torture_context *torture,
NTSTATUS status;
enum ndr_err_code ndr_ret;
- ok = test_setup_create_fill(torture, tree, mem_ctx, FNAME_CC_SRC,
+ ok = test_setup_create_fill(torture, tree, mem_ctx, src_name,
src_h, src_size, src_desired_access,
FILE_ATTRIBUTE_NORMAL);
torture_assert(torture, ok, "src file create fill");
- ok = test_setup_create_fill(torture, tree, mem_ctx, FNAME_CC_DST,
+ ok = test_setup_create_fill(torture, tree, mem_ctx, dst_name,
dest_h, dest_size, dest_desired_access,
FILE_ATTRIBUTE_NORMAL);
torture_assert(torture, ok, "dest file create fill");
@@ -2533,8 +2535,10 @@ static bool test_copyfile(struct torture_context *torture,
ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
0, /* 0 chunks, copyfile semantics */
+ FNAME_CC_SRC,
&src_h, 4096, /* fill 4096 byte src file */
SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA,
+ FNAME_CC_DST,
&dest_h, 0, /* 0 byte dest file */
SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA,
&cc_copy,
@@ -2600,8 +2604,10 @@ static bool test_copyfile(struct torture_context *torture,
ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
0, /* 0 chunks, copyfile semantics */
+ FNAME_CC_SRC,
&src_h, 4096, /* fill 4096 byte src file */
SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA,
+ FNAME_CC_DST,
&dest_h, 0, /* 0 byte dest file */
SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA,
&cc_copy,
--
2.9.3
From 9d8027a8d3a9f010376541b7c7c1627918facb46 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Fri, 12 May 2017 17:10:07 +0200
Subject: [PATCH 5/5] s4/torture: vfs_fruit: test copy-chunk on streams
Bug: https://bugzilla.samba.org/show_bug.cgi?id=12787
Signed-off-by: Ralph Boehme <slow at samba.org>
---
source4/torture/vfs/fruit.c | 225 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 225 insertions(+)
diff --git a/source4/torture/vfs/fruit.c b/source4/torture/vfs/fruit.c
index 7d59fee..2ab153a 100644
--- a/source4/torture/vfs/fruit.c
+++ b/source4/torture/vfs/fruit.c
@@ -4048,6 +4048,230 @@ done:
return ret;
}
+static bool copy_one_stream(struct torture_context *torture,
+ struct smb2_tree *tree,
+ TALLOC_CTX *tmp_ctx,
+ const char *src_sname,
+ const char *dst_sname)
+{
+ struct smb2_handle src_h = {{0}};
+ struct smb2_handle dest_h = {{0}};
+ NTSTATUS status;
+ union smb_ioctl io;
+ struct srv_copychunk_copy cc_copy;
+ struct srv_copychunk_rsp cc_rsp;
+ enum ndr_err_code ndr_ret;
+ bool ok = false;
+
+ ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
+ 1, /* 1 chunk */
+ src_sname,
+ &src_h, 256, /* fill 256 byte src file */
+ SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA,
+ dst_sname,
+ &dest_h, 0, /* 0 byte dest file */
+ SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA,
+ &cc_copy,
+ &io);
+ torture_assert_goto(torture, ok == true, ok, done,
+ "setup copy chunk error\n");
+
+ /* copy all src file data (via a single chunk desc) */
+ cc_copy.chunks[0].source_off = 0;
+ cc_copy.chunks[0].target_off = 0;
+ cc_copy.chunks[0].length = 256;
+
+ ndr_ret = ndr_push_struct_blob(
+ &io.smb2.in.out, tmp_ctx, &cc_copy,
+ (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
+
+ torture_assert_ndr_success_goto(torture, ndr_ret, ok, done,
+ "ndr_push_srv_copychunk_copy\n");
+
+ status = smb2_ioctl(tree, tmp_ctx, &io.smb2);
+ torture_assert_ntstatus_ok_goto(torture, status, ok, done,
+ "FSCTL_SRV_COPYCHUNK\n");
+
+ ndr_ret = ndr_pull_struct_blob(
+ &io.smb2.out.out, tmp_ctx, &cc_rsp,
+ (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
+
+ torture_assert_ndr_success_goto(torture, ndr_ret, ok, done,
+ "ndr_pull_srv_copychunk_rsp\n");
+
+ ok = check_copy_chunk_rsp(torture, &cc_rsp,
+ 1, /* chunks written */
+ 0, /* chunk bytes unsuccessfully written */
+ 256); /* total bytes written */
+ torture_assert_goto(torture, ok == true, ok, done,
+ "bad copy chunk response data\n");
+
+ ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 256, 0);
+ if (!ok) {
+ torture_fail(torture, "inconsistent file data\n");
+ }
+
+done:
+ if (!smb2_util_handle_empty(src_h)) {
+ smb2_util_close(tree, src_h);
+ }
+ if (!smb2_util_handle_empty(dest_h)) {
+ smb2_util_close(tree, dest_h);
+ }
+
+ return ok;
+}
+
+static bool copy_finderinfo_stream(struct torture_context *torture,
+ struct smb2_tree *tree,
+ TALLOC_CTX *tmp_ctx,
+ const char *src_name,
+ const char *dst_name)
+{
+ struct smb2_handle src_h = {{0}};
+ struct smb2_handle dest_h = {{0}};
+ NTSTATUS status;
+ union smb_ioctl io;
+ struct srv_copychunk_copy cc_copy;
+ struct srv_copychunk_rsp cc_rsp;
+ enum ndr_err_code ndr_ret;
+ const char *type_creator = "SMB,OLE!";
+ AfpInfo *info = NULL;
+ const char *src_name_afpinfo = NULL;
+ const char *dst_name_afpinfo = NULL;
+ bool ok = false;
+
+ src_name_afpinfo = talloc_asprintf(tmp_ctx, "%s%s", src_name,
+ AFPINFO_STREAM);
+ torture_assert_not_null_goto(torture, src_name_afpinfo, ok, done,
+ "talloc_asprintf failed\n");
+
+ dst_name_afpinfo = talloc_asprintf(tmp_ctx, "%s%s", dst_name,
+ AFPINFO_STREAM);
+ torture_assert_not_null_goto(torture, dst_name_afpinfo, ok, done,
+ "talloc_asprintf failed\n");
+
+ info = torture_afpinfo_new(tmp_ctx);
+ torture_assert_not_null_goto(torture, info, ok, done,
+ "torture_afpinfo_new failed\n");
+
+ memcpy(info->afpi_FinderInfo, type_creator, 8);
+ ok = torture_write_afpinfo(tree, torture, tmp_ctx, src_name, info);
+ torture_assert_goto(torture, ok == true, ok, done,
+ "torture_write_afpinfo failed\n");
+
+ ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
+ 1, /* 1 chunk */
+ src_name_afpinfo,
+ &src_h, 0,
+ SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA,
+ dst_name_afpinfo,
+ &dest_h, 0,
+ SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA,
+ &cc_copy,
+ &io);
+ torture_assert_goto(torture, ok == true, ok, done,
+ "setup copy chunk error\n");
+
+ /* copy all src file data (via a single chunk desc) */
+ cc_copy.chunks[0].source_off = 0;
+ cc_copy.chunks[0].target_off = 0;
+ cc_copy.chunks[0].length = 60;
+
+ ndr_ret = ndr_push_struct_blob(
+ &io.smb2.in.out, tmp_ctx, &cc_copy,
+ (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
+
+ torture_assert_ndr_success_goto(torture, ndr_ret, ok, done,
+ "ndr_push_srv_copychunk_copy\n");
+
+ status = smb2_ioctl(tree, tmp_ctx, &io.smb2);
+ torture_assert_ntstatus_ok_goto(torture, status, ok, done,
+ "FSCTL_SRV_COPYCHUNK\n");
+
+ ndr_ret = ndr_pull_struct_blob(
+ &io.smb2.out.out, tmp_ctx, &cc_rsp,
+ (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
+
+ torture_assert_ndr_success_goto(torture, ndr_ret, ok, done,
+ "ndr_pull_srv_copychunk_rsp\n");
+
+ smb2_util_close(tree, src_h);
+ ZERO_STRUCT(src_h);
+ smb2_util_close(tree, dest_h);
+ ZERO_STRUCT(dest_h);
+
+ ok = check_copy_chunk_rsp(torture, &cc_rsp,
+ 1, /* chunks written */
+ 0, /* chunk bytes unsuccessfully written */
+ 60); /* total bytes written */
+ torture_assert_goto(torture, ok == true, ok, done,
+ "bad copy chunk response data\n");
+
+ ok = check_stream(tree, __location__, torture, tmp_ctx,
+ dst_name, AFPINFO_STREAM,
+ 0, 60, 16, 8, type_creator);
+ torture_assert_goto(torture, ok == true, ok, done, "check_stream failed\n");
+
+done:
+ if (!smb2_util_handle_empty(src_h)) {
+ smb2_util_close(tree, src_h);
+ }
+ if (!smb2_util_handle_empty(dest_h)) {
+ smb2_util_close(tree, dest_h);
+ }
+
+ return ok;
+}
+
+static bool test_copy_chunk_streams(struct torture_context *torture,
+ struct smb2_tree *tree)
+{
+ const char *src_name = "src";
+ const char *dst_name = "dst";
+ struct names {
+ const char *src_sname;
+ const char *dst_sname;
+ } names[] = {
+ { "src:foo", "dst:foo" },
+ { "src" AFPRESOURCE_STREAM, "dst" AFPRESOURCE_STREAM }
+ };
+ int i;
+ TALLOC_CTX *tmp_ctx = NULL;
+ bool ok = false;
+
+ tmp_ctx = talloc_new(tree);
+ torture_assert_not_null_goto(torture, tmp_ctx, ok, done,
+ "torture_setup_file\n");
+
+ smb2_util_unlink(tree, src_name);
+ smb2_util_unlink(tree, dst_name);
+
+ ok = torture_setup_file(torture, tree, src_name, false);
+ torture_assert_goto(torture, ok == true, ok, done, "torture_setup_file\n");
+ ok = torture_setup_file(torture, tree, dst_name, false);
+ torture_assert_goto(torture, ok == true, ok, done, "torture_setup_file\n");
+
+ for (i = 0; i < ARRAY_SIZE(names); i++) {
+ ok = copy_one_stream(torture, tree, tmp_ctx,
+ names[i].src_sname,
+ names[i].dst_sname);
+ torture_assert_goto(torture, ok == true, ok, done,
+ "copy_one_stream failed\n");
+ }
+
+ ok = copy_finderinfo_stream(torture, tree, tmp_ctx,
+ src_name, dst_name);
+ torture_assert_goto(torture, ok == true, ok, done,
+ "copy_finderinfo_stream failed\n");
+
+done:
+ smb2_util_unlink(tree, src_name);
+ smb2_util_unlink(tree, dst_name);
+ talloc_free(tmp_ctx);
+ return ok;
+}
+
/*
* Note: This test depends on "vfs objects = catia fruit streams_xattr". For
* some tests torture must be run on the host it tests and takes an additional
@@ -4086,6 +4310,7 @@ struct torture_suite *torture_vfs_fruit(TALLOC_CTX *ctx)
torture_suite_add_1smb2_test(suite, "readdir_attr with names with illegal ntfs characters", test_readdir_attr_illegal_ntfs);
torture_suite_add_2ns_smb2_test(suite, "invalid AFP_AfpInfo", test_invalid_afpinfo);
torture_suite_add_1smb2_test(suite, "creating rsrc with read-only access", test_rfork_create_ro);
+ torture_suite_add_1smb2_test(suite, "copy-chunk streams", test_copy_chunk_streams);
return suite;
}
--
2.9.3
More information about the samba-technical
mailing list