From e0ee973165cd75f50e82c687aec9f23dbf2f8d15 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Tue, 20 Sep 2022 12:08:29 -0700 Subject: [PATCH 1/2] s4: smbtorture: Add fsync_resource_fork test to fruit tests. This shows we currently hang when sending an SMB2_OP_FLUSH on an AFP_Resource fork. Adds knownfail. BUG: https://bugzilla.samba.org/show_bug.cgi?id=15182 Signed-off-by: Jeremy Allison --- selftest/knownfail.d/fruit_fsync | 3 ++ source4/torture/vfs/fruit.c | 80 ++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+) create mode 100644 selftest/knownfail.d/fruit_fsync diff --git a/selftest/knownfail.d/fruit_fsync b/selftest/knownfail.d/fruit_fsync new file mode 100644 index 00000000000..15745bdae0e --- /dev/null +++ b/selftest/knownfail.d/fruit_fsync @@ -0,0 +1,3 @@ +^samba3.vfs.fruit\ metadata_stream.fsync_resource_fork.nt4_dc +^samba3.vfs.fruit\ metadata_netatalk.fsync_resource_fork.nt4_dc +^samba3.vfs.fruit\ fruit_delete_empty_adfiles.fsync_resource_fork.nt4_dc diff --git a/source4/torture/vfs/fruit.c b/source4/torture/vfs/fruit.c index fa758794368..3621fec460c 100644 --- a/source4/torture/vfs/fruit.c +++ b/source4/torture/vfs/fruit.c @@ -2718,6 +2718,85 @@ done: return ret; } +/* + * BUG: https://bugzilla.samba.org/show_bug.cgi?id=15182 + */ + +static bool test_rfork_fsync(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + const char *fname = BASEDIR "\\torture_rfork_fsync"; + const char *rfork = BASEDIR "\\torture_rfork_fsync" AFPRESOURCE_STREAM; + NTSTATUS status; + struct smb2_handle testdirh; + bool ret = true; + struct smb2_create create; + struct smb2_handle fh1; + struct smb2_flush f; + + ZERO_STRUCT(fh1); + + ret = enable_aapl(tctx, tree); + torture_assert_goto(tctx, ret == true, ret, done, "enable_aapl failed"); + + smb2_util_unlink(tree, fname); + + status = torture_smb2_testdir(tree, BASEDIR, &testdirh); + torture_assert_ntstatus_ok_goto(tctx, + status, + ret, + done, + "torture_smb2_testdir"); + smb2_util_close(tree, testdirh); + + ret = torture_setup_file(mem_ctx, tree, fname, false); + if (ret == false) { + goto done; + } + + torture_comment(tctx, "(%s) create resource fork %s\n", + __location__, + rfork); + + ZERO_STRUCT(create); + create.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + create.in.desired_access = SEC_STD_READ_CONTROL | SEC_FILE_ALL; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.fname = rfork; + create.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE | + NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + status = smb2_create(tree, mem_ctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create"); + fh1 = create.out.file.handle; + + torture_comment(tctx, "(%s) Write 10 bytes to resource fork %s\n", + __location__, + rfork); + + status = smb2_util_write(tree, fh1, "1234567890", 0, 10); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_write failed\n"); + + torture_comment(tctx, "(%s) fsync on resource fork %s\n", + __location__, + rfork); + + f.in.file.handle = fh1; + status = smb2_flush(tree, &f); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_flush failed\n"); + +done: + + smb2_util_close(tree, fh1); + smb2_util_unlink(tree, fname); + smb2_deltree(tree, BASEDIR); + talloc_free(mem_ctx); + return ret; +} + static bool test_rfork_create_ro(struct torture_context *tctx, struct smb2_tree *tree) { @@ -6961,6 +7040,7 @@ struct torture_suite *torture_vfs_fruit(TALLOC_CTX *ctx) torture_suite_add_1smb2_test(suite, "stream names", test_stream_names); torture_suite_add_1smb2_test(suite, "truncate resource fork to 0 bytes", test_rfork_truncate); torture_suite_add_1smb2_test(suite, "opening and creating resource fork", test_rfork_create); + torture_suite_add_1smb2_test(suite, "fsync_resource_fork", test_rfork_fsync); torture_suite_add_1smb2_test(suite, "rename_dir_openfile", test_rename_dir_openfile); torture_suite_add_1smb2_test(suite, "File without AFP_AfpInfo", test_afpinfo_enoent); torture_suite_add_1smb2_test(suite, "create delete-on-close AFP_AfpInfo", test_create_delete_on_close); -- 2.34.1 From 6f002d73e265a47da8dd061182f59a43b6bb10b6 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Tue, 20 Sep 2022 13:25:22 -0700 Subject: [PATCH 2/2] s3: VFS: fruit. Implement fsync_send()/fsync_recv(). For type == ADOUBLE_META, fio->fake_fd is true so writes are already synchronous, just call tevent_req_post(). For type == ADOUBLE_RSRC we know we are configured with FRUIT_RSRC_ADFILE (because fruit_must_handle_aio_stream() returned true), so we can just call SMB_VFS_NEXT_FSYNC_SEND() after replacing fsp with fio->ad_fsp. Remove knownfail. BUG: https://bugzilla.samba.org/show_bug.cgi?id=15182 Signed-off-by: Jeremy Allison --- selftest/knownfail.d/fruit_fsync | 3 - source3/modules/vfs_fruit.c | 98 ++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+), 3 deletions(-) delete mode 100644 selftest/knownfail.d/fruit_fsync diff --git a/selftest/knownfail.d/fruit_fsync b/selftest/knownfail.d/fruit_fsync deleted file mode 100644 index 15745bdae0e..00000000000 --- a/selftest/knownfail.d/fruit_fsync +++ /dev/null @@ -1,3 +0,0 @@ -^samba3.vfs.fruit\ metadata_stream.fsync_resource_fork.nt4_dc -^samba3.vfs.fruit\ metadata_netatalk.fsync_resource_fork.nt4_dc -^samba3.vfs.fruit\ fruit_delete_empty_adfiles.fsync_resource_fork.nt4_dc diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c index 13033096dc9..0a718d8baba 100644 --- a/source3/modules/vfs_fruit.c +++ b/source3/modules/vfs_fruit.c @@ -3071,6 +3071,102 @@ static ssize_t fruit_pwrite_recv(struct tevent_req *req, return state->nwritten; } +struct fruit_fsync_state { + int ret; + struct vfs_aio_state vfs_aio_state; +}; + +static void fruit_fsync_done(struct tevent_req *subreq); + +static struct tevent_req *fruit_fsync_send( + struct vfs_handle_struct *handle, + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct files_struct *fsp) +{ + struct tevent_req *req = NULL; + struct tevent_req *subreq = NULL; + struct fruit_fsync_state *state = NULL; + struct fio *fio = fruit_get_complete_fio(handle, fsp); + + req = tevent_req_create(mem_ctx, &state, + struct fruit_fsync_state); + if (req == NULL) { + return NULL; + } + + if (fruit_must_handle_aio_stream(fio)) { + struct adouble *ad = NULL; + + if (fio->type == ADOUBLE_META) { + /* + * We must never pass a fake_fd + * to lower level fsync calls. + * Everything is already done + * synchronously, so just return + * true. + */ + SMB_ASSERT(fio->fake_fd); + tevent_req_done(req); + return tevent_req_post(req, ev); + } + + /* + * We know the following must be true, + * as it's the condition for fruit_must_handle_aio_stream() + * to return true if fio->type == ADOUBLE_RSRC. + */ + SMB_ASSERT(fio->config->rsrc == FRUIT_RSRC_ADFILE); + if (fio->ad_fsp == NULL) { + tevent_req_error(req, EBADF); + return tevent_req_post(req, ev); + } + ad = ad_fget(talloc_tos(), handle, fio->ad_fsp, ADOUBLE_RSRC); + if (ad == NULL) { + tevent_req_error(req, ENOMEM); + return tevent_req_post(req, ev); + } + fsp = fio->ad_fsp; + } + + subreq = SMB_VFS_NEXT_FSYNC_SEND(state, ev, handle, fsp); + if (tevent_req_nomem(req, subreq)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, fruit_fsync_done, req); + return req; +} + +static void fruit_fsync_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct fruit_fsync_state *state = tevent_req_data( + req, struct fruit_fsync_state); + + state->ret = SMB_VFS_FSYNC_RECV(subreq, &state->vfs_aio_state); + TALLOC_FREE(subreq); + if (state->ret != 0) { + tevent_req_error(req, errno); + return; + } + tevent_req_done(req); +} + +static int fruit_fsync_recv(struct tevent_req *req, + struct vfs_aio_state *vfs_aio_state) +{ + struct fruit_fsync_state *state = tevent_req_data( + req, struct fruit_fsync_state); + + if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) { + return -1; + } + + *vfs_aio_state = state->vfs_aio_state; + return state->ret; +} + /** * Helper to stat/lstat the base file of an smb_fname. */ @@ -5305,6 +5401,8 @@ static struct vfs_fn_pointers vfs_fruit_fns = { .pread_recv_fn = fruit_pread_recv, .pwrite_send_fn = fruit_pwrite_send, .pwrite_recv_fn = fruit_pwrite_recv, + .fsync_send_fn = fruit_fsync_send, + .fsync_recv_fn = fruit_fsync_recv, .stat_fn = fruit_stat, .lstat_fn = fruit_lstat, .fstat_fn = fruit_fstat, -- 2.34.1