From 9fd489ddf24ad0d99c57eeb674abbd320250567d Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Fri, 23 Nov 2018 10:07:29 +0100 Subject: [PATCH 1/7] vfs_error_inject: add pwrite Bug: https://bugzilla.samba.org/show_bug.cgi?id=13688 Signed-off-by: Ralph Boehme Reviewed-by: Jeremy Allison (cherry picked from commit 55a82f907f6410ff478e82b0cf7f1caeacaf5ddd) --- source3/modules/vfs_error_inject.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/source3/modules/vfs_error_inject.c b/source3/modules/vfs_error_inject.c index bb5477a449f..9f0a25fb73f 100644 --- a/source3/modules/vfs_error_inject.c +++ b/source3/modules/vfs_error_inject.c @@ -88,8 +88,26 @@ static int vfs_error_inject_chdir(vfs_handle_struct *handle, return SMB_VFS_NEXT_CHDIR(handle, smb_fname); } +static ssize_t vfs_error_inject_pwrite(vfs_handle_struct *handle, + files_struct *fsp, + const void *data, + size_t n, + off_t offset) +{ + int error; + + error = inject_unix_error("pwrite", handle); + if (error != 0) { + errno = error; + return -1; + } + + return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset); +} + static struct vfs_fn_pointers vfs_error_inject_fns = { .chdir_fn = vfs_error_inject_chdir, + .pwrite_fn = vfs_error_inject_pwrite, }; static_decl_vfs; -- 2.17.2 From 75c9af9542a62884c0419e7e456cb8b2ea430329 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Fri, 23 Nov 2018 10:18:10 +0100 Subject: [PATCH 2/7] vfs_error_inject: add EBADF error Bug: https://bugzilla.samba.org/show_bug.cgi?id=13688 Signed-off-by: Ralph Boehme Reviewed-by: Jeremy Allison (cherry picked from commit 523a9b312c9f09178a5afefb48343e684e41d817) --- source3/modules/vfs_error_inject.c | 1 + 1 file changed, 1 insertion(+) diff --git a/source3/modules/vfs_error_inject.c b/source3/modules/vfs_error_inject.c index 9f0a25fb73f..c8c3ea4701f 100644 --- a/source3/modules/vfs_error_inject.c +++ b/source3/modules/vfs_error_inject.c @@ -28,6 +28,7 @@ struct unix_error_map { int error; } unix_error_map_array[] = { { "ESTALE", ESTALE }, + { "EBADF", EBADF }, }; static int find_unix_error_from_string(const char *err_str) -- 2.17.2 From 8888f962a4ce3b4c3c113de8653a1bc1faae2eca Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Wed, 14 Nov 2018 13:45:11 +0100 Subject: [PATCH 3/7] s4:torture: add a test-suite for VSS This test will not be run from the main torture test runner in selftest, as there we don't pass the required arguments 'twrp_file' and 'twrp_snapshot'. The test needs a carefully prepared environment with provisioned snapshot data, so the test will be started from a blackbox test script. That comes next. Bug: https://bugzilla.samba.org/show_bug.cgi?id=13688 Signed-off-by: Ralph Boehme Reviewed-by: Jeremy Allison (cherry picked from commit 48ddb87a32ca44c2fcc5aac0cc28c5527dc7eade) --- source4/torture/smb2/create.c | 87 +++++++++++++++++++++++++++++++++++ source4/torture/smb2/smb2.c | 1 + 2 files changed, 88 insertions(+) diff --git a/source4/torture/smb2/create.c b/source4/torture/smb2/create.c index ead56eb5c40..68447935bd0 100644 --- a/source4/torture/smb2/create.c +++ b/source4/torture/smb2/create.c @@ -1733,6 +1733,82 @@ static bool test_dir_alloc_size(struct torture_context *tctx, return ret; } +static bool test_twrp_write(struct torture_context *tctx, struct smb2_tree *tree) +{ + struct smb2_create io; + struct smb2_handle h1 = {{0}}; + NTSTATUS status; + bool ret = true; + char *p = NULL; + struct tm tm; + time_t t; + uint64_t nttime; + const char *file = NULL; + const char *snapshot = NULL; + + file = torture_setting_string(tctx, "twrp_file", NULL); + if (file == NULL) { + torture_skip(tctx, "missing 'twrp_file' option\n"); + } + + snapshot = torture_setting_string(tctx, "twrp_snapshot", NULL); + if (snapshot == NULL) { + torture_skip(tctx, "missing 'twrp_snapshot' option\n"); + } + + torture_comment(tctx, "Testing timewarp (%s) (%s)\n", file, snapshot); + + setenv("TZ", "GMT", 1); + p = strptime(snapshot, "@GMT-%Y.%m.%d-%H.%M.%S", &tm); + torture_assert_goto(tctx, p != NULL, ret, done, "strptime\n"); + torture_assert_goto(tctx, *p == '\0', ret, done, "strptime\n"); + + t = mktime(&tm); + unix_to_nt_time(&nttime, t); + + io = (struct smb2_create) { + .in.desired_access = SEC_FILE_READ_DATA, + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.create_disposition = NTCREATEX_DISP_OPEN, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.fname = file, + .in.query_maximal_access = true, + .in.timewarp = nttime, + }; + + status = smb2_create(tree, tctx, &io); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create\n"); + smb2_util_close(tree, io.out.file.handle); + + ret = io.out.maximal_access & (SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA); + torture_assert_goto(tctx, ret, ret, done, "Bad access\n"); + + io = (struct smb2_create) { + .in.desired_access = SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA, + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.create_disposition = NTCREATEX_DISP_OPEN, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.fname = file, + .in.timewarp = nttime, + }; + + status = smb2_create(tree, tctx, &io); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create\n"); + h1 = io.out.file.handle; + + status = smb2_util_write(tree, h1, "123", 0, 3); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_MEDIA_WRITE_PROTECTED, + ret, done, "smb2_create\n"); + + smb2_util_close(tree, h1); + +done: + return ret; +} + /* basic testing of SMB2 read */ @@ -1758,3 +1834,14 @@ struct torture_suite *torture_smb2_create_init(TALLOC_CTX *ctx) return suite; } + +struct torture_suite *torture_smb2_twrp_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create(ctx, "twrp"); + + torture_suite_add_1smb2_test(suite, "write", test_twrp_write); + + suite->description = talloc_strdup(suite, "SMB2-TWRP tests"); + + return suite; +} diff --git a/source4/torture/smb2/smb2.c b/source4/torture/smb2/smb2.c index 6f9884e3c27..a835dc7c050 100644 --- a/source4/torture/smb2/smb2.c +++ b/source4/torture/smb2/smb2.c @@ -154,6 +154,7 @@ NTSTATUS torture_smb2_init(TALLOC_CTX *ctx) torture_suite_add_suite(suite, torture_smb2_read_init(suite)); torture_suite_add_suite(suite, torture_smb2_aio_delay_init(suite)); torture_suite_add_suite(suite, torture_smb2_create_init(suite)); + torture_suite_add_suite(suite, torture_smb2_twrp_init(suite)); torture_suite_add_suite(suite, torture_smb2_acls_init(suite)); torture_suite_add_suite(suite, torture_smb2_notify_init(suite)); torture_suite_add_suite(suite, torture_smb2_notify_inotify_init(suite)); -- 2.17.2 From ab1a5c33670d8da4f1d50b8cf03de8c8ca9785d3 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Fri, 23 Nov 2018 10:18:44 +0100 Subject: [PATCH 4/7] s3:script/tests: add a test for VSS write behaviour Bug: https://bugzilla.samba.org/show_bug.cgi?id=13688 Signed-off-by: Ralph Boehme Reviewed-by: Jeremy Allison (backported from commit 12778f015988f7e8755016c72c26939998758dae) --- selftest/knownfail.d/samba3.blackbox | 1 + selftest/target/Samba3.pm | 9 +++ .../script/tests/test_shadow_copy_torture.sh | 76 +++++++++++++++++++ source3/selftest/tests.py | 1 + 4 files changed, 87 insertions(+) create mode 100644 selftest/knownfail.d/samba3.blackbox create mode 100755 source3/script/tests/test_shadow_copy_torture.sh diff --git a/selftest/knownfail.d/samba3.blackbox b/selftest/knownfail.d/samba3.blackbox new file mode 100644 index 00000000000..16537e58aeb --- /dev/null +++ b/selftest/knownfail.d/samba3.blackbox @@ -0,0 +1 @@ +^samba3.blackbox.shadow_copy_torture.writing to shadow copy of a file\(fileserver\) diff --git a/selftest/target/Samba3.pm b/selftest/target/Samba3.pm index 438cb3409bb..9e85d229670 100755 --- a/selftest/target/Samba3.pm +++ b/selftest/target/Samba3.pm @@ -2134,6 +2134,15 @@ sub provision($$$$$$$$$) vfs objects = shadow_copy2 shadow:mountpoint = $shadow_mntdir wide links = yes + +[shadow_write] + path = $shadow_tstdir + comment = previous versions snapshots under mount point + vfs objects = shadow_copy2 error_inject + aio write size = 0 + error_inject:pwrite = EBADF + shadow:mountpoint = $shadow_tstdir + [dfq] path = $shrdir/dfree vfs objects = acl_xattr fake_acls xattr_tdb fake_dfq diff --git a/source3/script/tests/test_shadow_copy_torture.sh b/source3/script/tests/test_shadow_copy_torture.sh new file mode 100755 index 00000000000..d47cd512a20 --- /dev/null +++ b/source3/script/tests/test_shadow_copy_torture.sh @@ -0,0 +1,76 @@ +#!/bin/bash +# +# Blackbox test for shadow_copy2 VFS. +# + +if [ $# -lt 7 ]; then +cat < $destdir/foo +} + +# build a snapshots directory +build_snapshots() +{ + local snapdir + + snapdir=$WORKDIR/.snapshots + + mkdir -p $snapdir + mkdir $snapdir/$SNAPSHOT + + build_files $snapdir/$SNAPSHOT +} + +test_shadow_copy_write() +{ + local msg + + msg=$1 + + #delete snapshots from previous tests + find $WORKDIR -name ".snapshots" -exec rm -rf {} \; 1>/dev/null 2>&1 + build_snapshots + + testit "writing to shadow copy of a file" \ + $SMBTORTURE \ + -U$USERNAME%$PASSWORD \ + "//$SERVER/shadow_write" \ + --option="torture:twrp_file=foo" \ + --option="torture:twrp_snapshot=$SNAPSHOT" \ + smb2.twrp.write || \ + failed=`expr $failed + 1` +} + +build_files $WORKDIR + +# test open for writing and write behaviour of snapshoted files +test_shadow_copy_write "write behaviour of snapshoted files" + +exit $failed diff --git a/source3/selftest/tests.py b/source3/selftest/tests.py index 09cd5159a0d..f91cb5a6cd9 100755 --- a/source3/selftest/tests.py +++ b/source3/selftest/tests.py @@ -275,6 +275,7 @@ plantestsuite("samba3.blackbox.smbclient_ntlm.plain (%s)" % env, env, [os.path.j plantestsuite("samba3.blackbox.offline (%s)" % env, env, [os.path.join(samba3srcdir, "script/tests/test_offline.sh"), '$SERVER', '$SERVER_IP', '$DOMAIN', '$USERNAME', '$PASSWORD', '$LOCAL_PATH/offline', smbclient3]) plantestsuite("samba3.blackbox.shadow_copy2 NT1 (%s)" % env, env, [os.path.join(samba3srcdir, "script/tests/test_shadow_copy.sh"), '$SERVER', '$SERVER_IP', '$DOMAIN', '$USERNAME', '$PASSWORD', '$LOCAL_PATH/shadow', smbclient3, '-m', 'NT1']) plantestsuite("samba3.blackbox.shadow_copy2 SMB3 (%s)" % env, env, [os.path.join(samba3srcdir, "script/tests/test_shadow_copy.sh"), '$SERVER', '$SERVER_IP', '$DOMAIN', '$USERNAME', '$PASSWORD', '$LOCAL_PATH/shadow', smbclient3, '-m', 'SMB3']) + plantestsuite("samba3.blackbox.shadow_copy_torture", env, [os.path.join(samba3srcdir, "script/tests/test_shadow_copy_torture.sh"), '$SERVER', '$SERVER_IP', '$DOMAIN', '$USERNAME', '$PASSWORD', '$LOCAL_PATH/shadow', smbtorture4]) plantestsuite("samba3.blackbox.smbclient.forceuser_validusers (%s)" % env, env, [os.path.join(samba3srcdir, "script/tests/test_forceuser_validusers.sh"), '$SERVER', '$DOMAIN', '$USERNAME', '$PASSWORD', '$LOCAL_PATH', smbclient3]) plantestsuite("samba3.blackbox.smbget (%s)" % env, env, [os.path.join(samba3srcdir, "script/tests/test_smbget.sh"), '$SERVER', '$SERVER_IP', '$DOMAIN', 'smbget_user', '$PASSWORD', '$LOCAL_PATH/smbget', smbget]) plantestsuite("samba3.blackbox.netshareenum (%s)" % env, env, [os.path.join(samba3srcdir, "script/tests/test_shareenum.sh"), '$SERVER', '$USERNAME', '$PASSWORD', rpcclient]) -- 2.17.2 From 9834c64404b68646c693c84fae50611dfc3ceb01 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Thu, 22 Nov 2018 11:02:24 +0100 Subject: [PATCH 5/7] vfs_shadow_copy2: add _already_converted arg to shadow_copy2_strip_snapshot_internal() Not used for now, all existing callers pass NULL. Bug: https://bugzilla.samba.org/show_bug.cgi?id=13688 Signed-off-by: Ralph Boehme Reviewed-by: Jeremy Allison (cherry picked from commit 87bf06ed790dad8a4f650c0cd1b6781864666cbf) --- source3/modules/vfs_shadow_copy2.c | 32 ++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/source3/modules/vfs_shadow_copy2.c b/source3/modules/vfs_shadow_copy2.c index aa7cd9c61d0..c7fc5ee4550 100644 --- a/source3/modules/vfs_shadow_copy2.c +++ b/source3/modules/vfs_shadow_copy2.c @@ -587,7 +587,8 @@ static bool shadow_copy2_strip_snapshot_internal(TALLOC_CTX *mem_ctx, const char *orig_name, time_t *ptimestamp, char **pstripped, - char **psnappath) + char **psnappath, + bool *_already_converted) { struct tm tm; time_t timestamp = 0; @@ -608,6 +609,10 @@ static bool shadow_copy2_strip_snapshot_internal(TALLOC_CTX *mem_ctx, DEBUG(10, (__location__ ": enter path '%s'\n", name)); + if (_already_converted != NULL) { + *_already_converted = false; + } + abs_path = make_path_absolute(mem_ctx, priv, name); if (abs_path == NULL) { ret = false; @@ -630,6 +635,9 @@ static bool shadow_copy2_strip_snapshot_internal(TALLOC_CTX *mem_ctx, } if (already_converted) { + if (_already_converted != NULL) { + *_already_converted = true; + } goto out; } @@ -759,6 +767,7 @@ static bool shadow_copy2_strip_snapshot(TALLOC_CTX *mem_ctx, orig_name, ptimestamp, pstripped, + NULL, NULL); } @@ -1119,12 +1128,14 @@ static int shadow_copy2_rename(vfs_handle_struct *handle, if (!shadow_copy2_strip_snapshot_internal(talloc_tos(), handle, smb_fname_src->base_name, - ×tamp_src, NULL, &snappath_src)) { + ×tamp_src, NULL, &snappath_src, + NULL)) { return -1; } if (!shadow_copy2_strip_snapshot_internal(talloc_tos(), handle, smb_fname_dst->base_name, - ×tamp_dst, NULL, &snappath_dst)) { + ×tamp_dst, NULL, &snappath_dst, + NULL)) { return -1; } if (timestamp_src != 0) { @@ -1163,7 +1174,8 @@ static int shadow_copy2_symlink(vfs_handle_struct *handle, link_contents, ×tamp_old, NULL, - &snappath_old)) { + &snappath_old, + NULL)) { return -1; } if (!shadow_copy2_strip_snapshot_internal(talloc_tos(), @@ -1171,7 +1183,8 @@ static int shadow_copy2_symlink(vfs_handle_struct *handle, new_smb_fname->base_name, ×tamp_new, NULL, - &snappath_new)) { + &snappath_new, + NULL)) { return -1; } if ((timestamp_old != 0) || (timestamp_new != 0)) { @@ -1202,7 +1215,8 @@ static int shadow_copy2_link(vfs_handle_struct *handle, old_smb_fname->base_name, ×tamp_old, NULL, - &snappath_old)) { + &snappath_old, + NULL)) { return -1; } if (!shadow_copy2_strip_snapshot_internal(talloc_tos(), @@ -1210,7 +1224,8 @@ static int shadow_copy2_link(vfs_handle_struct *handle, new_smb_fname->base_name, ×tamp_new, NULL, - &snappath_new)) { + &snappath_new, + NULL)) { return -1; } if ((timestamp_old != 0) || (timestamp_new != 0)) { @@ -1566,7 +1581,8 @@ static int shadow_copy2_chdir(vfs_handle_struct *handle, smb_fname->base_name, ×tamp, &stripped, - &snappath)) { + &snappath, + NULL)) { return -1; } if (stripped != NULL) { -- 2.17.2 From f072056b544def6e26dea9326d5dd2ecf066ca71 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Thu, 22 Nov 2018 11:04:54 +0100 Subject: [PATCH 6/7] vfs_shadow_copy2: add shadow_copy2_strip_snapshot_converted Can be used by callers to determine if a path is in fact pointing at a file in a snapshot. Will be used in the next commit. Bug: https://bugzilla.samba.org/show_bug.cgi?id=13688 Signed-off-by: Ralph Boehme Reviewed-by: Jeremy Allison (cherry picked from commit 14d6488d355e960ab02e72c414cbbc316f1db718) --- source3/modules/vfs_shadow_copy2.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/source3/modules/vfs_shadow_copy2.c b/source3/modules/vfs_shadow_copy2.c index c7fc5ee4550..30a202cc9cc 100644 --- a/source3/modules/vfs_shadow_copy2.c +++ b/source3/modules/vfs_shadow_copy2.c @@ -771,6 +771,22 @@ static bool shadow_copy2_strip_snapshot(TALLOC_CTX *mem_ctx, NULL); } +static bool shadow_copy2_strip_snapshot_converted(TALLOC_CTX *mem_ctx, + struct vfs_handle_struct *handle, + const char *orig_name, + time_t *ptimestamp, + char **pstripped, + bool *is_converted) +{ + return shadow_copy2_strip_snapshot_internal(mem_ctx, + handle, + orig_name, + ptimestamp, + pstripped, + NULL, + is_converted); +} + static char *shadow_copy2_find_mount_point(TALLOC_CTX *mem_ctx, vfs_handle_struct *handle) { -- 2.17.2 From c77ad18bdc1dd0a451029bf43abe85df4e4486d2 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Fri, 23 Nov 2018 14:08:15 +0100 Subject: [PATCH 7/7] vfs_shadow_copy2: nicely deal with attempts to open previous version for writing Bug: https://bugzilla.samba.org/show_bug.cgi?id=13688 Signed-off-by: Ralph Boehme Reviewed-by: Jeremy Allison (cherry picked from commit cf95756235f718478e556ce1fbf7c032f9c9acfb) --- selftest/knownfail.d/samba3.blackbox | 1 - source3/modules/vfs_shadow_copy2.c | 124 ++++++++++++++++++++++++++- 2 files changed, 122 insertions(+), 3 deletions(-) delete mode 100644 selftest/knownfail.d/samba3.blackbox diff --git a/selftest/knownfail.d/samba3.blackbox b/selftest/knownfail.d/samba3.blackbox deleted file mode 100644 index 16537e58aeb..00000000000 --- a/selftest/knownfail.d/samba3.blackbox +++ /dev/null @@ -1 +0,0 @@ -^samba3.blackbox.shadow_copy_torture.writing to shadow copy of a file\(fileserver\) diff --git a/source3/modules/vfs_shadow_copy2.c b/source3/modules/vfs_shadow_copy2.c index 30a202cc9cc..746fbd470a9 100644 --- a/source3/modules/vfs_shadow_copy2.c +++ b/source3/modules/vfs_shadow_copy2.c @@ -36,6 +36,8 @@ #include "include/ntioctl.h" #include "util_tdb.h" #include "lib/util_path.h" +#include "libcli/security/security.h" +#include "lib/util/tevent_unix.h" struct shadow_copy2_config { char *gmt_format; @@ -1376,15 +1378,27 @@ static int shadow_copy2_open(vfs_handle_struct *handle, time_t timestamp = 0; char *stripped = NULL; char *tmp; + bool is_converted = false; int saved_errno = 0; int ret; - if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, + if (!shadow_copy2_strip_snapshot_converted(talloc_tos(), handle, smb_fname->base_name, - ×tamp, &stripped)) { + ×tamp, &stripped, + &is_converted)) { return -1; } if (timestamp == 0) { + if (is_converted) { + /* + * Just pave over the user requested mode and use + * O_RDONLY. Later attempts by the client to write on + * the handle will fail in the pwrite() syscall with + * EINVAL which we carefully map to EROFS. In sum, this + * matches Windows behaviour. + */ + flags = O_RDONLY; + } return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode); } @@ -1398,6 +1412,14 @@ static int shadow_copy2_open(vfs_handle_struct *handle, return -1; } + /* + * Just pave over the user requested mode and use O_RDONLY. Later + * attempts by the client to write on the handle will fail in the + * pwrite() syscall with EINVAL which we carefully map to EROFS. In sum, + * this matches Windows behaviour. + */ + flags = O_RDONLY; + ret = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode); if (ret == -1) { saved_errno = errno; @@ -2887,6 +2909,101 @@ static int shadow_copy2_get_quota(vfs_handle_struct *handle, return ret; } +static ssize_t shadow_copy2_pwrite(vfs_handle_struct *handle, + files_struct *fsp, + const void *data, + size_t n, + off_t offset) +{ + ssize_t nwritten; + + nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset); + if (nwritten == -1) { + if (errno == EBADF && fsp->can_write) { + errno = EROFS; + } + } + + return nwritten; +} + +struct shadow_copy2_pwrite_state { + vfs_handle_struct *handle; + files_struct *fsp; + ssize_t ret; + struct vfs_aio_state vfs_aio_state; +}; + +static void shadow_copy2_pwrite_done(struct tevent_req *subreq); + +static struct tevent_req *shadow_copy2_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, *subreq = NULL; + struct shadow_copy2_pwrite_state *state = NULL; + + req = tevent_req_create(mem_ctx, &state, + struct shadow_copy2_pwrite_state); + if (req == NULL) { + return NULL; + } + state->handle = handle; + state->fsp = fsp; + + subreq = SMB_VFS_NEXT_PWRITE_SEND(state, + ev, + handle, + fsp, + data, + n, + offset); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, shadow_copy2_pwrite_done, req); + + return req; +} + +static void shadow_copy2_pwrite_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct shadow_copy2_pwrite_state *state = tevent_req_data( + req, struct shadow_copy2_pwrite_state); + + state->ret = SMB_VFS_PWRITE_RECV(subreq, &state->vfs_aio_state); + TALLOC_FREE(subreq); + if (state->ret == -1) { + tevent_req_error(req, state->vfs_aio_state.error); + return; + } + + tevent_req_done(req); +} + +static ssize_t shadow_copy2_pwrite_recv(struct tevent_req *req, + struct vfs_aio_state *vfs_aio_state) +{ + struct shadow_copy2_pwrite_state *state = tevent_req_data( + req, struct shadow_copy2_pwrite_state); + + if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) { + if ((vfs_aio_state->error == EBADF) && + state->fsp->can_write) + { + vfs_aio_state->error = EROFS; + errno = EROFS; + } + return -1; + } + + *vfs_aio_state = state->vfs_aio_state; + return state->ret; +} + static int shadow_copy2_connect(struct vfs_handle_struct *handle, const char *service, const char *user) { @@ -3249,6 +3366,9 @@ static struct vfs_fn_pointers vfs_shadow_copy2_fns = { .setxattr_fn = shadow_copy2_setxattr, .chflags_fn = shadow_copy2_chflags, .get_real_filename_fn = shadow_copy2_get_real_filename, + .pwrite_fn = shadow_copy2_pwrite, + .pwrite_send_fn = shadow_copy2_pwrite_send, + .pwrite_recv_fn = shadow_copy2_pwrite_recv, .connectpath_fn = shadow_copy2_connectpath, }; -- 2.17.2