From 3748dd20ae00b50d014612f8ec2393712ad8986c Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Thu, 11 Jan 2018 12:25:49 +0100 Subject: [PATCH 01/35] s4/test: fix AAPL size check A recent commit changed the ModelString from "Samba" to "MacSamba". Bug: https://bugzilla.samba.org/show_bug.cgi?id=13646 Signed-off-by: Ralph Boehme Reviewed-by: Jeremy Allison (cherry picked from commit 11589a26ba40e9da90822a85b21327f49a69afda) --- source4/torture/vfs/fruit.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/source4/torture/vfs/fruit.c b/source4/torture/vfs/fruit.c index b0bec2afe5b..6843d120f18 100644 --- a/source4/torture/vfs/fruit.c +++ b/source4/torture/vfs/fruit.c @@ -2301,6 +2301,7 @@ static bool test_aapl(struct torture_context *tctx, unsigned int count; union smb_search_data *d; uint64_t rfork_len; + bool is_osx_server = torture_setting_bool(tctx, "osx", false); smb2_deltree(tree, BASEDIR); @@ -2357,7 +2358,10 @@ static bool test_aapl(struct torture_context *tctx, goto done; } - if (aapl->data.length != 50) { + if (!is_osx_server) { + size_t expected_aapl_ctx_size; + bool size_ok; + /* * uint32_t CommandCode = kAAPL_SERVER_QUERY * uint32_t Reserved = 0; @@ -2370,11 +2374,12 @@ static bool test_aapl(struct torture_context *tctx, * kAAPL_CASE_SENSITIVE; * uint32_t Pad2 = 0; * uint32_t ModelStringLen = 10; - * ucs2_t ModelString[5] = "Samba"; + * ucs2_t ModelString[5] = "MacSamba"; */ - torture_warning(tctx, - "(%s) unexpected AAPL context length: %zd, expected 50", - __location__, aapl->data.length); + expected_aapl_ctx_size = strlen("MacSamba") * 2 + 40; + + size_ok = aapl->data.length == expected_aapl_ctx_size; + torture_assert_goto(tctx, size_ok, ret, done, "bad AAPL size"); } aapl_cmd = IVAL(aapl->data.data, 0); -- 2.17.2 From d54950cba76bf2052b9d6c5c6aa5242c922003c0 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Thu, 23 Aug 2018 12:07:20 +0200 Subject: [PATCH 02/35] vfs_streams_xattr: fix open implementation Since a long time the modules's open function happily returned success when opening a non existent stream without O_CREAT. This change fixes it to return -1 and errno=ENOATTR if o get_ea_value() returns NT_STATUS_NOT_FOUND (eg mapped from getxattr() = -1, errno=ENOATTR) and o flags doesn't contain O_CREAT Bug: https://bugzilla.samba.org/show_bug.cgi?id=13646 Signed-off-by: Ralph Boehme Reviewed-by: Jeremy Allison (cherry picked from commit 9f58997d7a1ab0358963a45cb8bf291e7afa8068) --- source3/modules/vfs_streams_xattr.c | 64 +++++++++++++++++------------ 1 file changed, 38 insertions(+), 26 deletions(-) diff --git a/source3/modules/vfs_streams_xattr.c b/source3/modules/vfs_streams_xattr.c index c653656e5f8..7f930d96d6b 100644 --- a/source3/modules/vfs_streams_xattr.c +++ b/source3/modules/vfs_streams_xattr.c @@ -407,6 +407,7 @@ static int streams_xattr_open(vfs_handle_struct *handle, char *xattr_name = NULL; int pipe_fds[2]; int fakefd = -1; + bool set_empty_xattr = false; int ret; SMB_VFS_HANDLE_GET_DATA(handle, config, struct streams_xattr_config, @@ -440,39 +441,37 @@ static int streams_xattr_open(vfs_handle_struct *handle, goto fail; } - /* - * Return a valid fd, but ensure any attempt to use it returns an error - * (EPIPE). - */ - ret = pipe(pipe_fds); - if (ret != 0) { - goto fail; - } - - close(pipe_fds[1]); - pipe_fds[1] = -1; - fakefd = pipe_fds[0]; - status = get_ea_value(talloc_tos(), handle->conn, NULL, smb_fname, xattr_name, &ea); DEBUG(10, ("get_ea_value returned %s\n", nt_errstr(status))); - if (!NT_STATUS_IS_OK(status) - && !NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) { - /* - * The base file is not there. This is an error even if we got - * O_CREAT, the higher levels should have created the base - * file for us. - */ - DEBUG(10, ("streams_xattr_open: base file %s not around, " - "returning ENOENT\n", smb_fname->base_name)); - errno = ENOENT; - goto fail; + if (!NT_STATUS_IS_OK(status)) { + if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) { + /* + * The base file is not there. This is an error even if + * we got O_CREAT, the higher levels should have created + * the base file for us. + */ + DBG_DEBUG("streams_xattr_open: base file %s not around, " + "returning ENOENT\n", smb_fname->base_name); + errno = ENOENT; + goto fail; + } + + if (!(flags & O_CREAT)) { + errno = ENOATTR; + goto fail; + } + + set_empty_xattr = true; } - if ((!NT_STATUS_IS_OK(status) && (flags & O_CREAT)) || - (flags & O_TRUNC)) { + if (flags & O_TRUNC) { + set_empty_xattr = true; + } + + if (set_empty_xattr) { /* * The attribute does not exist or needs to be truncated */ @@ -495,6 +494,19 @@ static int streams_xattr_open(vfs_handle_struct *handle, } } + /* + * Return a valid fd, but ensure any attempt to use it returns an error + * (EPIPE). + */ + ret = pipe(pipe_fds); + if (ret != 0) { + goto fail; + } + + close(pipe_fds[1]); + pipe_fds[1] = -1; + fakefd = pipe_fds[0]; + sio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct stream_io, NULL); if (sio == NULL) { errno = ENOMEM; -- 2.17.2 From 729a2ce2be978ee94b0f218cbeb3ac5c32fa6216 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Fri, 19 Oct 2018 22:21:10 +0200 Subject: [PATCH 03/35] s4:torture/vfs/fruit: skip a few tests when running against a macOS SMB server These tests are designed to test specific vfs_fruit functionality. Bug: https://bugzilla.samba.org/show_bug.cgi?id=13646 Signed-off-by: Ralph Boehme Reviewed-by: Jeremy Allison (cherry picked from commit 242f42ef79e51e35ab33331e2f2d7c9feaac36f8) --- source4/torture/vfs/fruit.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/source4/torture/vfs/fruit.c b/source4/torture/vfs/fruit.c index 6843d120f18..c8357f00f98 100644 --- a/source4/torture/vfs/fruit.c +++ b/source4/torture/vfs/fruit.c @@ -2111,6 +2111,11 @@ static bool test_adouble_conversion(struct torture_context *tctx, ":com.apple.metadata" "\xef\x80\xa2" "_kMDItemUserTags:$DATA", ":foo" "\xef\x80\xa2" "bar:$DATA", /* "foo:bar:$DATA" */ }; + bool is_osx = torture_setting_bool(tctx, "osx", false); + + if (is_osx) { + torture_skip(tctx, "Test only works with Samba\n"); + } smb2_deltree(tree, BASEDIR); @@ -2185,6 +2190,11 @@ static bool test_adouble_conversion_wo_xattr(struct torture_context *tctx, union smb_search_data *d; const char *data = "This resource fork intentionally left blank"; size_t datalen = strlen(data); + bool is_osx = torture_setting_bool(tctx, "osx", false); + + if (is_osx) { + torture_skip(tctx, "Test only works with Samba\n"); + } smb2_deltree(tree, BASEDIR); @@ -4742,6 +4752,11 @@ static bool test_nfs_aces(struct torture_context *tctx, struct security_descriptor *psd = NULL; NTSTATUS status; bool ret = true; + bool is_osx = torture_setting_bool(tctx, "osx", false); + + if (is_osx) { + torture_skip(tctx, "Test only works with Samba\n"); + } ret = enable_aapl(tctx, tree); torture_assert(tctx, ret == true, "enable_aapl failed"); -- 2.17.2 From 8df16cebe9c4f7aa1b543ce5865eb0d06a90d58b Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Wed, 17 Oct 2018 10:51:45 +0200 Subject: [PATCH 04/35] s4:torture/vfs/fruit: fix a few error checks in "delete AFP_AfpInfo by writing all 0" Bug: https://bugzilla.samba.org/show_bug.cgi?id=13646 Signed-off-by: Ralph Boehme Reviewed-by: Jeremy Allison (cherry picked from commit 394d1fc9ac8373ea7419ea9514750422bd6e504d) --- source4/torture/vfs/fruit.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/source4/torture/vfs/fruit.c b/source4/torture/vfs/fruit.c index c8357f00f98..44feaf44b1b 100644 --- a/source4/torture/vfs/fruit.c +++ b/source4/torture/vfs/fruit.c @@ -3675,8 +3675,8 @@ static bool test_afpinfo_all0(struct torture_context *tctx, create.in.fname = fname; status = smb2_create(tree, mem_ctx, &create); - torture_assert_goto(tctx, ret == true, ret, done, - "smb2_create failed\n"); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed\n"); baseh = create.out.file.handle; ZERO_STRUCT(create); @@ -3686,8 +3686,8 @@ static bool test_afpinfo_all0(struct torture_context *tctx, create.in.fname = sname; status = smb2_create(tree, mem_ctx, &create); - torture_assert_goto(tctx, ret == true, ret, done, - "smb2_create failed\n"); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed\n"); h1 = create.out.file.handle; status = smb2_util_write(tree, h1, infobuf, 0, AFP_INFO_SIZE); -- 2.17.2 From d72631194a6b3a7dd38084c3c1f8c00eefa321aa Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Thu, 11 Oct 2018 17:13:52 +0200 Subject: [PATCH 05/35] s4:torture/vfs/fruit: set share_access to NTCREATEX_SHARE_ACCESS_MASK in check_stream_list Avoid sharing conflicts with other opens on the basefile. Bug: https://bugzilla.samba.org/show_bug.cgi?id=13646 Signed-off-by: Ralph Boehme Reviewed-by: Jeremy Allison (cherry picked from commit 1d4e50649b02c7c2314aa04e08f9946e21b6b37d) --- source4/torture/vfs/fruit.c | 1 + 1 file changed, 1 insertion(+) diff --git a/source4/torture/vfs/fruit.c b/source4/torture/vfs/fruit.c index 44feaf44b1b..a37e2680d2c 100644 --- a/source4/torture/vfs/fruit.c +++ b/source4/torture/vfs/fruit.c @@ -3053,6 +3053,7 @@ static bool check_stream_list(struct smb2_tree *tree, create.in.desired_access = SEC_FILE_ALL; create.in.create_options = is_dir ? NTCREATEX_OPTIONS_DIRECTORY : 0; create.in.file_attributes = is_dir ? FILE_ATTRIBUTE_DIRECTORY : FILE_ATTRIBUTE_NORMAL; + create.in.share_access = NTCREATEX_SHARE_ACCESS_MASK; status = smb2_create(tree, tmp_ctx, &create); torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create"); h = create.out.file.handle; -- 2.17.2 From 9c7cd67beacd1ca2be567a9626fce6f58b2df608 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Mon, 15 Oct 2018 15:31:21 +0200 Subject: [PATCH 06/35] s4:torture/vfs/fruit: update test "SMB2/CREATE context AAPL" to work against macOS Bug: https://bugzilla.samba.org/show_bug.cgi?id=13646 Signed-off-by: Ralph Boehme Reviewed-by: Jeremy Allison (cherry picked from commit dba9a4743fc42b57f03b6ace24753ce8091f629f) --- source4/torture/vfs/fruit.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/source4/torture/vfs/fruit.c b/source4/torture/vfs/fruit.c index a37e2680d2c..f563fb87643 100644 --- a/source4/torture/vfs/fruit.c +++ b/source4/torture/vfs/fruit.c @@ -2306,6 +2306,7 @@ static bool test_aapl(struct torture_context *tctx, uint32_t aapl_reply_bitmap; uint32_t aapl_server_caps; uint32_t aapl_vol_caps; + uint32_t expected_vol_caps = 0; char *model; struct smb2_find f; unsigned int count; @@ -2424,8 +2425,11 @@ static bool test_aapl(struct torture_context *tctx, goto done; } + if (is_osx_server) { + expected_vol_caps = 5; + } aapl_vol_caps = BVAL(aapl->data.data, 24); - if (aapl_vol_caps != 0) { + if (aapl_vol_caps != expected_vol_caps) { /* this will fail on a case insensitive fs ... */ torture_result(tctx, TORTURE_FAIL, "(%s) unexpected vol_caps: %d", -- 2.17.2 From eb7f75b7e7f9e99a20f057357d5a0fb7f5662147 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Mon, 15 Oct 2018 15:39:12 +0200 Subject: [PATCH 07/35] s4:torture/vfs/fruit: update test "stream names" to work with macOS o create the basefile before trying to create a stream on it, otherwise this fails on macOS o write something to the stream, otherwise the stream is not listed as macOS hides 0-byte sized streams Bug: https://bugzilla.samba.org/show_bug.cgi?id=13646 Signed-off-by: Ralph Boehme Reviewed-by: Jeremy Allison (cherry picked from commit 125498861abeeed5bde727ff98d88d9ce4af6c75) --- source4/torture/vfs/fruit.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/source4/torture/vfs/fruit.c b/source4/torture/vfs/fruit.c index f563fb87643..0750026f1d5 100644 --- a/source4/torture/vfs/fruit.c +++ b/source4/torture/vfs/fruit.c @@ -3132,6 +3132,9 @@ static bool test_stream_names(struct torture_context *tctx, CHECK_STATUS(status, NT_STATUS_OK); smb2_util_close(tree, h); + ret = torture_setup_file(mem_ctx, tree, fname, false); + torture_assert_goto(tctx, ret, ret, done, "torture_setup_file"); + torture_comment(tctx, "(%s) testing stream names\n", __location__); ZERO_STRUCT(create); create.in.desired_access = SEC_FILE_WRITE_DATA; @@ -3146,6 +3149,11 @@ static bool test_stream_names(struct torture_context *tctx, status = smb2_create(tree, mem_ctx, &create); CHECK_STATUS(status, NT_STATUS_OK); + + status = smb2_util_write(tree, create.out.file.handle, "foo", 0, 3); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_write failed\n"); + smb2_util_close(tree, create.out.file.handle); ret = check_stream_list(tree, tctx, fname, 2, streams, false); -- 2.17.2 From de4a97e9da07b0ec5d8ae61206cfd8b8f270bab2 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Wed, 31 Oct 2018 15:49:45 +0100 Subject: [PATCH 08/35] s4:torture/vfs/fruit: ensure a directory handle is closed in all code paths Otherwise we get a sharing violation when running against Samba and opening the directory a second time. Bug: https://bugzilla.samba.org/show_bug.cgi?id=13646 Signed-off-by: Ralph Boehme Reviewed-by: Jeremy Allison (cherry picked from commit f7551d8fb3599a6a71dd0570bad02bdc48324107) --- source4/torture/vfs/fruit.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/source4/torture/vfs/fruit.c b/source4/torture/vfs/fruit.c index 0750026f1d5..c34ae1a73d4 100644 --- a/source4/torture/vfs/fruit.c +++ b/source4/torture/vfs/fruit.c @@ -3232,15 +3232,12 @@ static bool test_rename_dir_openfile(struct torture_context *torture, torture_assert_ntstatus_equal(torture, status, NT_STATUS_ACCESS_DENIED, "smb2_setinfo_file"); - - ZERO_STRUCT(cl.smb2); - cl.smb2.level = RAW_CLOSE_SMB2; - cl.smb2.in.file.handle = d1; - status = smb2_close(tree, &(cl.smb2)); - torture_assert_ntstatus_ok(torture, status, "smb2_close"); - ZERO_STRUCT(d1); } + status = smb2_util_close(tree, d1); + torture_assert_ntstatus_ok(torture, status, "smb2_util_close\n"); + ZERO_STRUCT(d1); + torture_comment(torture, "Enabling AAPL\n"); ret = enable_aapl(torture, tree); -- 2.17.2 From 4d904e230f8f42f6fab0588c8760fa13fcf36a23 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Mon, 15 Oct 2018 16:24:19 +0200 Subject: [PATCH 09/35] s4:torture/vfs/fruit: update test "read open rsrc after rename" to work with macOS macOS SMB server seems to return NT_STATUS_SHARING_VIOLATION in this case while Windows 2016 returns NT_STATUS_ACCESS_DENIED. Lets stick with the Windows error code for now in the Samba fileserver, but let the test pass against macOS. Bug: https://bugzilla.samba.org/show_bug.cgi?id=13646 Signed-off-by: Ralph Boehme Reviewed-by: Jeremy Allison (cherry picked from commit 9cd9859dc10d425d29774d221ec9ad697192b466) --- source4/torture/vfs/fruit.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/source4/torture/vfs/fruit.c b/source4/torture/vfs/fruit.c index c34ae1a73d4..83b19d4694c 100644 --- a/source4/torture/vfs/fruit.c +++ b/source4/torture/vfs/fruit.c @@ -4179,6 +4179,8 @@ static bool test_rename_and_read_rsrc(struct torture_context *tctx, const char *fname_renamed = "test_rename_openfile_renamed"; const char *data = "1234567890"; union smb_setfileinfo sinfo; + bool server_is_macos = torture_setting_bool(tctx, "osx", false); + NTSTATUS expected_status; ret = enable_aapl(tctx, tree); torture_assert_goto(tctx, ret == true, ret, done, "enable_aapl failed"); @@ -4229,14 +4231,25 @@ static bool test_rename_and_read_rsrc(struct torture_context *tctx, sinfo.rename_information.in.root_fid = 0; sinfo.rename_information.in.new_name = fname_renamed; + if (server_is_macos) { + expected_status = NT_STATUS_SHARING_VIOLATION; + } else { + expected_status = NT_STATUS_ACCESS_DENIED; + } + status = smb2_setinfo_file(tree, &sinfo); torture_assert_ntstatus_equal_goto( - tctx, status, NT_STATUS_ACCESS_DENIED, ret, done, + tctx, status, expected_status, ret, done, "smb2_setinfo_file failed"); - smb2_util_close(tree, h1); smb2_util_close(tree, h2); + status = smb2_util_write(tree, h1, "foo", 0, 3); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "write failed\n"); + + smb2_util_close(tree, h1); + done: smb2_util_unlink(tree, fname); smb2_util_unlink(tree, fname_renamed); -- 2.17.2 From 665f029ea7ad75c1a10c94fc7e617841ad4ccb68 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Tue, 9 Oct 2018 18:48:08 +0200 Subject: [PATCH 10/35] s4:torture/vfs/fruit: expand existing test "setinfo delete-on-close AFP_AfpInfo" a little bit Add a check that verifies a create on a stream gets NT_STATUS_DELETE_PENDING after delete-on-close has been set. Bug: https://bugzilla.samba.org/show_bug.cgi?id=13646 Signed-off-by: Ralph Boehme Reviewed-by: Jeremy Allison (cherry picked from commit 0d9a80b6b2e3f51cad122ee0dc78c2f4dfdebcb5) --- source4/torture/vfs/fruit.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/source4/torture/vfs/fruit.c b/source4/torture/vfs/fruit.c index 83b19d4694c..ac7b6682e6d 100644 --- a/source4/torture/vfs/fruit.c +++ b/source4/torture/vfs/fruit.c @@ -3450,6 +3450,10 @@ static bool test_setinfo_delete_on_close(struct torture_context *tctx, const char *sname = BASEDIR "\\file" AFPINFO_STREAM_NAME; const char *type_creator = "SMB,OLE!"; AfpInfo *info = NULL; + const char *streams[] = { + AFPINFO_STREAM, + "::$DATA" + }; const char *streams_basic[] = { "::$DATA" }; @@ -3491,6 +3495,19 @@ static bool test_setinfo_delete_on_close(struct torture_context *tctx, status = smb2_setinfo_file(tree, &sfinfo); torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "set delete-on-close failed"); + ret = check_stream_list(tree, tctx, fname, 2, streams, false); + torture_assert_goto(tctx, ret == true, ret, done, "Bad streams"); + + ZERO_STRUCT(create); + create.in.create_disposition = NTCREATEX_DISP_OPEN; + create.in.desired_access = SEC_FILE_ALL; + create.in.fname = sname; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION; + status = smb2_create(tree, mem_ctx, &create); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_DELETE_PENDING, + ret, done, "Got unexpected AFP_AfpInfo stream"); + smb2_util_close(tree, h1); ret = check_stream_list(tree, tctx, fname, 1, streams_basic, false); -- 2.17.2 From a864c93b927e24ae96e4149ff3771e59df3fae4a Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Wed, 10 Oct 2018 12:47:07 +0200 Subject: [PATCH 11/35] s4:torture/vfs/fruit: expand existing vfs_test "null afpinfo" This adds a check that a read on a seperate handle also sees the previously created AFP_AfpInfo stream. Bug: https://bugzilla.samba.org/show_bug.cgi?id=13646 Signed-off-by: Ralph Boehme Reviewed-by: Jeremy Allison (cherry picked from commit 6f428607e35055b9ce1a04a625d43740bf5c76b2) --- source4/torture/vfs/fruit.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/source4/torture/vfs/fruit.c b/source4/torture/vfs/fruit.c index ac7b6682e6d..ff3ea274f65 100644 --- a/source4/torture/vfs/fruit.c +++ b/source4/torture/vfs/fruit.c @@ -4091,6 +4091,8 @@ static bool test_null_afpinfo(struct torture_context *tctx, AfpInfo *afpinfo = NULL; char *afpinfo_buf = NULL; const char *type_creator = "SMB,OLE!"; + struct smb2_handle handle2; + struct smb2_read r; torture_comment(tctx, "Checking create of AfpInfo stream\n"); @@ -4129,6 +4131,20 @@ static bool test_null_afpinfo(struct torture_context *tctx, status = smb2_read_recv(req[1], tree, &read); torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_read_recv failed"); + status = torture_smb2_testfile_access(tree, sname, &handle2, + SEC_FILE_READ_DATA); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testfile failed\n"); + r = (struct smb2_read) { + .in.file.handle = handle2, + .in.length = AFP_INFO_SIZE, + }; + + status = smb2_read(tree, tree, &r); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testfile failed\n"); + smb2_util_close(tree, handle2); + afpinfo = torture_afpinfo_new(mem_ctx); torture_assert_goto(tctx, afpinfo != NULL, ret, done, "torture_afpinfo_new failed"); -- 2.17.2 From a82592dc4b1db43f93f1297cef1ee3aefe6162ed Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Mon, 15 Oct 2018 15:17:08 +0200 Subject: [PATCH 12/35] s4:torture/vfs/fruit: update test "creating rsrc with read-only access" for newer macOS versions While this operation failed against older macOS versions, it passes against versions 10.12 and newer. Update the test accordingly, a subsequent commit will then update our implementation. Bug: https://bugzilla.samba.org/show_bug.cgi?id=13646 Signed-off-by: Ralph Boehme Reviewed-by: Jeremy Allison (cherry picked from commit c3cf09a0791ed4dfb7ca18d175396ac1aca5d2ed) --- selftest/knownfail.d/samba3.vfs.fruit | 3 +++ source4/torture/vfs/fruit.c | 28 +-------------------------- 2 files changed, 4 insertions(+), 27 deletions(-) diff --git a/selftest/knownfail.d/samba3.vfs.fruit b/selftest/knownfail.d/samba3.vfs.fruit index 6307e2b3404..a2758ffeded 100644 --- a/selftest/knownfail.d/samba3.vfs.fruit +++ b/selftest/knownfail.d/samba3.vfs.fruit @@ -1,2 +1,5 @@ ^samba3.vfs.fruit streams_depot.OS X AppleDouble file conversion\(nt4_dc\) ^samba3.vfs.fruit streams_depot.OS X AppleDouble file conversion without embedded xattr\(nt4_dc\) +^samba3.vfs.fruit metadata_netatalk.creating rsrc with read-only access\(nt4_dc\) +^samba3.vfs.fruit metadata_stream.creating rsrc with read-only access\(nt4_dc\) +^samba3.vfs.fruit streams_depot.creating rsrc with read-only access\(nt4_dc\) diff --git a/source4/torture/vfs/fruit.c b/source4/torture/vfs/fruit.c index ff3ea274f65..22a434398e7 100644 --- a/source4/torture/vfs/fruit.c +++ b/source4/torture/vfs/fruit.c @@ -2045,35 +2045,9 @@ static bool test_rfork_create_ro(struct torture_context *tctx, } torture_comment(tctx, "(%s) Try opening read-only with " - "open_if create disposition, should return ENOENT\n", + "open_if create disposition, should work\n", __location__); - ZERO_STRUCT(create); - create.in.fname = rfork; - create.in.create_disposition = NTCREATEX_DISP_OPEN_IF; - create.in.desired_access = SEC_FILE_READ_DATA | SEC_STD_READ_CONTROL; - create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; - create.in.share_access = FILE_SHARE_READ | FILE_SHARE_DELETE; - status = smb2_create(tree, mem_ctx, &(create)); - torture_assert_ntstatus_equal_goto(tctx, status, - NT_STATUS_OBJECT_NAME_NOT_FOUND, - ret, done, "smb2_create failed\n"); - - torture_comment(tctx, "(%s) Now write something to the " - "rsrc stream, then the same open should succeed\n", - __location__); - - ret = write_stream(tree, __location__, tctx, mem_ctx, - fname, AFPRESOURCE_STREAM_NAME, - 0, 3, "foo"); - torture_assert_goto(tctx, ret == true, ret, done, - "write_stream failed\n"); - - ret = check_stream(tree, __location__, tctx, mem_ctx, - fname, AFPRESOURCE_STREAM, - 0, 3, 0, 3, "foo"); - torture_assert_goto(tctx, ret == true, ret, done, "check_stream"); - ZERO_STRUCT(create); create.in.fname = rfork; create.in.create_disposition = NTCREATEX_DISP_OPEN_IF; -- 2.17.2 From b6377fd1015ede78c2d5edbfe1775952b3ec1027 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Mon, 22 Oct 2018 12:32:09 +0200 Subject: [PATCH 13/35] vfs_fruit: update handling of read-only creation of resource fork macOS SMB server versions supports this since 10.12, so we adapt our behaviour. Bug: https://bugzilla.samba.org/show_bug.cgi?id=13646 Signed-off-by: Ralph Boehme Reviewed-by: Jeremy Allison (cherry picked from commit 055ca448f1a2bd995291af1810c9ec94f90f32dd) --- selftest/knownfail.d/samba3.vfs.fruit | 3 --- source3/modules/vfs_fruit.c | 23 +++-------------------- 2 files changed, 3 insertions(+), 23 deletions(-) diff --git a/selftest/knownfail.d/samba3.vfs.fruit b/selftest/knownfail.d/samba3.vfs.fruit index a2758ffeded..6307e2b3404 100644 --- a/selftest/knownfail.d/samba3.vfs.fruit +++ b/selftest/knownfail.d/samba3.vfs.fruit @@ -1,5 +1,2 @@ ^samba3.vfs.fruit streams_depot.OS X AppleDouble file conversion\(nt4_dc\) ^samba3.vfs.fruit streams_depot.OS X AppleDouble file conversion without embedded xattr\(nt4_dc\) -^samba3.vfs.fruit metadata_netatalk.creating rsrc with read-only access\(nt4_dc\) -^samba3.vfs.fruit metadata_stream.creating rsrc with read-only access\(nt4_dc\) -^samba3.vfs.fruit streams_depot.creating rsrc with read-only access\(nt4_dc\) diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c index e8c45f19092..dfb1ba446e4 100644 --- a/source3/modules/vfs_fruit.c +++ b/source3/modules/vfs_fruit.c @@ -3575,12 +3575,9 @@ static int fruit_open_rsrc_adouble(vfs_handle_struct *handle, goto exit; } - /* Sanitize flags */ - if (flags & O_WRONLY) { - /* We always need read access for the metadata header too */ - flags &= ~O_WRONLY; - flags |= O_RDWR; - } + /* We always need read/write access for the metadata header too */ + flags &= ~(O_RDONLY | O_WRONLY); + flags |= O_RDWR; hostfd = SMB_VFS_NEXT_OPEN(handle, smb_fname_base, fsp, flags, mode); @@ -3667,20 +3664,6 @@ static int fruit_open_rsrc(vfs_handle_struct *handle, SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data, return -1); - if (((flags & O_ACCMODE) == O_RDONLY) - && (flags & O_CREAT) - && !VALID_STAT(fsp->fsp_name->st)) - { - /* - * This means the stream doesn't exist. macOS SMB server fails - * this with NT_STATUS_OBJECT_NAME_NOT_FOUND, so must we. Cf bug - * 12565 and the test for this combination in - * test_rfork_create(). - */ - errno = ENOENT; - return -1; - } - switch (config->rsrc) { case FRUIT_RSRC_STREAM: fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode); -- 2.17.2 From 42c26b22c46c523481abff88568775d5c2862b0c Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Mon, 22 Oct 2018 12:43:16 +0200 Subject: [PATCH 14/35] s4:torture/vfs/fruit: expand test "setinfo eof stream" o Adds checks verifying that after setting eof to 0 on a stream, a subsequent open gets ENOENT, before and after closing the handle that had been used to set eof to 0. o Verify that a write to a handle succeeds after that handle has been used to set eof to 0 on a stream. Bug: https://bugzilla.samba.org/show_bug.cgi?id=13646 Signed-off-by: Ralph Boehme Reviewed-by: Jeremy Allison (cherry picked from commit b35d9e74028727da24f349242cb0bc62403a9b09) --- selftest/knownfail.d/samba3.vfs.fruit | 3 ++ source4/torture/vfs/fruit.c | 61 +++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/selftest/knownfail.d/samba3.vfs.fruit b/selftest/knownfail.d/samba3.vfs.fruit index 6307e2b3404..5ecba522d4a 100644 --- a/selftest/knownfail.d/samba3.vfs.fruit +++ b/selftest/knownfail.d/samba3.vfs.fruit @@ -1,2 +1,5 @@ ^samba3.vfs.fruit streams_depot.OS X AppleDouble file conversion\(nt4_dc\) ^samba3.vfs.fruit streams_depot.OS X AppleDouble file conversion without embedded xattr\(nt4_dc\) +^samba3.vfs.fruit metadata_netatalk.setinfo eof stream\(nt4_dc\) +^samba3.vfs.fruit metadata_stream.setinfo eof stream\(nt4_dc\) +^samba3.vfs.fruit streams_depot.setinfo eof stream\(nt4_dc\) diff --git a/source4/torture/vfs/fruit.c b/source4/torture/vfs/fruit.c index 22a434398e7..7be516593f1 100644 --- a/source4/torture/vfs/fruit.c +++ b/source4/torture/vfs/fruit.c @@ -4981,8 +4981,32 @@ static bool test_setinfo_stream_eof(struct torture_context *tctx, torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "set eof 0 failed\n"); + ZERO_STRUCT(create); + create.in.desired_access = SEC_FILE_READ_ATTRIBUTE; + create.in.share_access = NTCREATEX_SHARE_ACCESS_MASK; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.create_disposition = NTCREATEX_DISP_OPEN; + create.in.fname = sname; + + status = smb2_create(tree, tctx, &create); + torture_assert_ntstatus_equal_goto( + tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND, ret, done, + "Unexpected status\n"); + smb2_util_close(tree, h1); + ZERO_STRUCT(create); + create.in.desired_access = SEC_FILE_READ_ATTRIBUTE; + create.in.share_access = NTCREATEX_SHARE_ACCESS_MASK; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.create_disposition = NTCREATEX_DISP_OPEN; + create.in.fname = sname; + + status = smb2_create(tree, tctx, &create); + torture_assert_ntstatus_equal_goto( + tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND, ret, done, + "Unexpected status\n"); + status = torture_smb2_testfile_access(tree, sname, &h1, SEC_FILE_WRITE_DATA); torture_assert_ntstatus_ok_goto(tctx, status, ret, done, @@ -5061,6 +5085,18 @@ static bool test_setinfo_stream_eof(struct torture_context *tctx, torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "set eof 0 failed\n"); + ZERO_STRUCT(create); + create.in.desired_access = SEC_FILE_READ_ATTRIBUTE; + create.in.share_access = NTCREATEX_SHARE_ACCESS_MASK; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.create_disposition = NTCREATEX_DISP_OPEN; + create.in.fname = sname; + + status = smb2_create(tree, tctx, &create); + torture_assert_ntstatus_equal_goto( + tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND, ret, done, + "Unexpected status\n"); + smb2_util_close(tree, h1); ZERO_STRUCT(create); @@ -5120,6 +5156,31 @@ static bool test_setinfo_stream_eof(struct torture_context *tctx, torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "torture_smb2_testfile failed\n"); smb2_util_close(tree, h1); + + torture_comment(tctx, "Writing to stream after setting EOF to 0\n"); + status = torture_smb2_testfile_access(tree, sname, &h1, + SEC_FILE_WRITE_DATA); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testfile failed\n"); + + status = smb2_util_write(tree, h1, "1234567890", 0, 10); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_write failed\n"); + + ZERO_STRUCT(sfinfo); + sfinfo.generic.in.file.handle = h1; + sfinfo.generic.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION; + sfinfo.position_information.in.position = 0; + status = smb2_setinfo_file(tree, &sfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "set eof 0 failed\n"); + + status = smb2_util_write(tree, h1, "1234567890", 0, 10); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_write failed\n"); + + smb2_util_close(tree, h1); + done: smb2_util_unlink(tree, fname); smb2_util_rmdir(tree, BASEDIR); -- 2.17.2 From ca644aa2096810c15dda763e7dda42cb0731f2c8 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Sat, 20 Oct 2018 14:52:23 +0200 Subject: [PATCH 15/35] s4:torture/vfs/fruit: write some data to a just created teststream Doesn't currently make a difference, but this prepares for a later change in vfs_fruit that will filter out empty streams (which is the macOS behaviour). Bug: https://bugzilla.samba.org/show_bug.cgi?id=13646 Signed-off-by: Ralph Boehme Reviewed-by: Jeremy Allison (cherry picked from commit 3195ff9547824dd129e3fbcbbb8823fb10a0a5d4) --- source4/torture/vfs/fruit.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/source4/torture/vfs/fruit.c b/source4/torture/vfs/fruit.c index 7be516593f1..5da7d11a9d4 100644 --- a/source4/torture/vfs/fruit.c +++ b/source4/torture/vfs/fruit.c @@ -5282,6 +5282,11 @@ static bool test_stream_names_local(struct torture_context *tctx, status = smb2_create(tree, mem_ctx, &create); CHECK_STATUS(status, NT_STATUS_OK); + + status = smb2_util_write(tree, create.out.file.handle, "foo", 0, 3); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_write failed\n"); + smb2_util_close(tree, create.out.file.handle); ret = torture_setup_local_xattr(tctx, "localdir", BASEDIR "/stream_names.txt", -- 2.17.2 From 36357486e5002271aadee26bf2ca2c9c2ba2d469 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Sat, 20 Oct 2018 14:54:48 +0200 Subject: [PATCH 16/35] vfs_fruit: don't unlink 0-byte size truncated streams This caused all sort of havoc with subsequent SMB request that acted on the handle of the then deleted backend storage (file or blob, depending on the used streams module). Bug: https://bugzilla.samba.org/show_bug.cgi?id=13646 Signed-off-by: Ralph Boehme Reviewed-by: Jeremy Allison (cherry picked from commit 287082d739362ad068df37076712927ccd7ca90c) --- source3/modules/vfs_fruit.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c index dfb1ba446e4..80b125a3019 100644 --- a/source3/modules/vfs_fruit.c +++ b/source3/modules/vfs_fruit.c @@ -5791,13 +5791,6 @@ static int fruit_ftruncate(struct vfs_handle_struct *handle, (intmax_t)offset); if (fio == NULL) { - if (offset == 0 && - global_fruit_config.nego_aapl && - is_ntfs_stream_smb_fname(fsp->fsp_name) && - !is_ntfs_default_stream_smb_fname(fsp->fsp_name)) - { - return SMB_VFS_NEXT_UNLINK(handle, fsp->fsp_name); - } return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset); } -- 2.17.2 From 126547b53288e6ad22d665d4f03d4a0419228f2b Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Mon, 22 Oct 2018 14:01:34 +0200 Subject: [PATCH 17/35] s4:torture/vfs/fruit: enable AAPL extensions in a bunch of tests These tests check for macOS SMB server specific behaviour. They work currently against Samba without enabling AAPL because in vfs_fruit we're currently don't check whether AAPL has been negotiated in one place. A subsequent commit will change that and this commit prepares for that change. Bug: https://bugzilla.samba.org/show_bug.cgi?id=13646 Signed-off-by: Ralph Boehme Reviewed-by: Jeremy Allison (cherry picked from commit 7485007bb0a69dc8237c78be90f4bae06aad49b2) --- source4/torture/vfs/fruit.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/source4/torture/vfs/fruit.c b/source4/torture/vfs/fruit.c index 5da7d11a9d4..669b7a66ac1 100644 --- a/source4/torture/vfs/fruit.c +++ b/source4/torture/vfs/fruit.c @@ -1810,6 +1810,9 @@ static bool test_rfork_truncate(struct torture_context *tctx, struct smb2_handle fh1, fh2, fh3; union smb_setfileinfo sinfo; + 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); @@ -1928,6 +1931,9 @@ static bool test_rfork_create(struct torture_context *tctx, }; union smb_fileinfo finfo; + 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); @@ -3935,6 +3941,9 @@ static bool test_setinfo_eof_resource(struct torture_context *tctx, torture_assert_goto(tctx, mem_ctx != NULL, ret, done, "talloc_new"); + ret = enable_aapl(tctx, tree); + torture_assert_goto(tctx, ret == true, ret, done, "enable_aapl failed"); + torture_comment(tctx, "Set AFP_AfpResource EOF to 1 and 0\n"); smb2_deltree(tree, BASEDIR); @@ -4901,6 +4910,9 @@ static bool test_setinfo_stream_eof(struct torture_context *tctx, torture_assert_goto(tctx, mem_ctx != NULL, ret, done, "talloc_new failed\n"); + ret = enable_aapl(tctx, tree); + torture_assert(tctx, ret == true, "enable_aapl failed"); + torture_comment(tctx, "Test setting EOF on a stream\n"); smb2_deltree(tree, BASEDIR); -- 2.17.2 From ad1f31f3b6a1fd78e6e2eaa9b84bfb0dbd408185 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Sat, 20 Oct 2018 15:28:06 +0200 Subject: [PATCH 18/35] vfs_fruit: use check on global_fruit_config.nego_aapl for macOS specific behaviour Ensure any non MS compliant protocol behaviour targetted at supporting macOS clients are only effective if the client negotiated AAPL. Currently this only guards the resource fork which only macOS client are going to use, but subsequent commits add more this at this place. Bug: https://bugzilla.samba.org/show_bug.cgi?id=13646 Signed-off-by: Ralph Boehme Reviewed-by: Jeremy Allison (cherry picked from commit 046456fca60b9fef38a0cdf5a116883fb1c7dffe) --- source3/modules/vfs_fruit.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c index 80b125a3019..a36e131be30 100644 --- a/source3/modules/vfs_fruit.c +++ b/source3/modules/vfs_fruit.c @@ -5867,7 +5867,8 @@ static NTSTATUS fruit_create_file(vfs_handle_struct *handle, * * Cf the vfs_fruit torture tests in test_rfork_create(). */ - if (is_afpresource_stream(fsp->fsp_name) && + if (global_fruit_config.nego_aapl && + is_afpresource_stream(fsp->fsp_name) && create_disposition == FILE_OPEN) { if (fsp->fsp_name->st.st_ex_size == 0) { -- 2.17.2 From 94e30adf1998ec8510d37939822e9347488ab5fb Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Sat, 20 Oct 2018 14:53:50 +0200 Subject: [PATCH 19/35] vfs_fruit: filter empty streams First step in achieving macOS compliant behaviour wrt to empty streams: - hide empty streams in streaminfo - prevent opens of empty streams This means that we may carry 0-byte sized streams in our streams backend, but this shouldn't really hurt. The previous attempt of deleting the streams when an SMB setinfo eof to 0 request came in, turned out be a road into desaster. We could set delete-on-close on the stream, but that means we'd have to check for it for every write on a stream and checking the delete-on-close bits requires fetching the locking.tdb record, so this is expensive and I'd like to avoid that overhead. Bug: https://bugzilla.samba.org/show_bug.cgi?id=13646 Signed-off-by: Ralph Boehme Reviewed-by: Jeremy Allison (cherry picked from commit ebfcf75e993b1a792db76b94aa898532e1c81eeb) --- selftest/knownfail.d/samba3.vfs.fruit | 3 -- source3/modules/vfs_fruit.c | 44 +++++++++++++++++++++++---- 2 files changed, 38 insertions(+), 9 deletions(-) diff --git a/selftest/knownfail.d/samba3.vfs.fruit b/selftest/knownfail.d/samba3.vfs.fruit index 5ecba522d4a..6307e2b3404 100644 --- a/selftest/knownfail.d/samba3.vfs.fruit +++ b/selftest/knownfail.d/samba3.vfs.fruit @@ -1,5 +1,2 @@ ^samba3.vfs.fruit streams_depot.OS X AppleDouble file conversion\(nt4_dc\) ^samba3.vfs.fruit streams_depot.OS X AppleDouble file conversion without embedded xattr\(nt4_dc\) -^samba3.vfs.fruit metadata_netatalk.setinfo eof stream\(nt4_dc\) -^samba3.vfs.fruit metadata_stream.setinfo eof stream\(nt4_dc\) -^samba3.vfs.fruit streams_depot.setinfo eof stream\(nt4_dc\) diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c index a36e131be30..9932249e315 100644 --- a/source3/modules/vfs_fruit.c +++ b/source3/modules/vfs_fruit.c @@ -5578,6 +5578,36 @@ static NTSTATUS fruit_streaminfo_rsrc(vfs_handle_struct *handle, return status; } +static void fruit_filter_empty_streams(unsigned int *pnum_streams, + struct stream_struct **pstreams) +{ + unsigned num_streams = *pnum_streams; + struct stream_struct *streams = *pstreams; + unsigned i = 0; + + if (!global_fruit_config.nego_aapl) { + return; + } + + while (i < num_streams) { + struct smb_filename smb_fname = (struct smb_filename) { + .stream_name = streams[i].name, + }; + + if (is_ntfs_default_stream_smb_fname(&smb_fname) + || streams[i].size > 0) + { + i++; + continue; + } + + streams[i] = streams[num_streams - 1]; + num_streams--; + } + + *pnum_streams = num_streams; +} + static NTSTATUS fruit_streaminfo(vfs_handle_struct *handle, struct files_struct *fsp, const struct smb_filename *smb_fname, @@ -5599,6 +5629,8 @@ static NTSTATUS fruit_streaminfo(vfs_handle_struct *handle, return status; } + fruit_filter_empty_streams(pnum_streams, pstreams); + status = fruit_streaminfo_meta(handle, fsp, smb_fname, mem_ctx, pnum_streams, pstreams); if (!NT_STATUS_IS_OK(status)) { @@ -5868,13 +5900,13 @@ static NTSTATUS fruit_create_file(vfs_handle_struct *handle, * Cf the vfs_fruit torture tests in test_rfork_create(). */ if (global_fruit_config.nego_aapl && - is_afpresource_stream(fsp->fsp_name) && - create_disposition == FILE_OPEN) + create_disposition == FILE_OPEN && + smb_fname->st.st_ex_size == 0 && + is_ntfs_stream_smb_fname(smb_fname) && + !(is_ntfs_default_stream_smb_fname(smb_fname))) { - if (fsp->fsp_name->st.st_ex_size == 0) { - status = NT_STATUS_OBJECT_NAME_NOT_FOUND; - goto fail; - } + status = NT_STATUS_OBJECT_NAME_NOT_FOUND; + goto fail; } if (is_ntfs_stream_smb_fname(smb_fname) -- 2.17.2 From aaa0e08301a90d4f2eee83f184aac8cfd6343881 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Wed, 10 Oct 2018 18:45:56 +0200 Subject: [PATCH 20/35] s4:torture/util: add torture_smb2_open() This seems to be missing: a simple wrapper to just open a file without fancy options. Bug: https://bugzilla.samba.org/show_bug.cgi?id=13646 Signed-off-by: Ralph Boehme Reviewed-by: Jeremy Allison (cherry picked from commit 64b28e4a94365092f46052712a38e6fcfbb9f20c) --- source4/torture/smb2/util.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/source4/torture/smb2/util.c b/source4/torture/smb2/util.c index 65090b0e8b7..ac8a0d5df77 100644 --- a/source4/torture/smb2/util.c +++ b/source4/torture/smb2/util.c @@ -527,6 +527,36 @@ NTSTATUS torture_smb2_testfile(struct smb2_tree *tree, const char *fname, SEC_RIGHTS_FILE_ALL); } +/* + create and return a handle to a test file + with a specific access mask +*/ +NTSTATUS torture_smb2_open(struct smb2_tree *tree, + const char *fname, + uint32_t desired_access, + struct smb2_handle *handle) +{ + struct smb2_create io; + NTSTATUS status; + + io = (struct smb2_create) { + .in.fname = fname, + .in.desired_access = desired_access, + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.create_disposition = NTCREATEX_DISP_OPEN, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + }; + + status = smb2_create(tree, tree, &io); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + *handle = io.out.file.handle; + + return NT_STATUS_OK; +} + /* create and return a handle to a test directory with specific desired access -- 2.17.2 From ef3bd114105d1ba7992c3a0d341fa69e390bea77 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Thu, 11 Oct 2018 17:14:50 +0200 Subject: [PATCH 21/35] s4:torture/vfs/fruit: add check_stream_list_handle() Bug: https://bugzilla.samba.org/show_bug.cgi?id=13646 Signed-off-by: Ralph Boehme Reviewed-by: Jeremy Allison (cherry picked from commit aba502d440ae3478543d3be5c5bbcea39fb0a463) --- source4/torture/vfs/fruit.c | 62 +++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/source4/torture/vfs/fruit.c b/source4/torture/vfs/fruit.c index 669b7a66ac1..20f0e2b4e5b 100644 --- a/source4/torture/vfs/fruit.c +++ b/source4/torture/vfs/fruit.c @@ -3082,6 +3082,68 @@ static bool check_stream_list(struct smb2_tree *tree, return ret; } +#if 0 +static bool check_stream_list_handle(struct smb2_tree *tree, + struct torture_context *tctx, + struct smb2_handle h, + int num_exp, + const char **exp, + bool is_dir) +{ + bool ret = true; + union smb_fileinfo finfo; + NTSTATUS status; + int i; + TALLOC_CTX *tmp_ctx = talloc_new(tctx); + char **exp_sort; + struct stream_struct *stream_sort; + + torture_assert_goto(tctx, tmp_ctx != NULL, ret, done, + "talloc_new failed\n"); + + finfo = (union smb_fileinfo) { + .stream_info.level = RAW_FILEINFO_STREAM_INFORMATION, + .stream_info.in.file.handle = h, + }; + + status = smb2_getinfo_file(tree, tctx, &finfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "get stream info\n"); + + torture_assert_int_equal_goto(tctx, finfo.stream_info.out.num_streams, + num_exp, ret, done, "stream count\n"); + + if (num_exp == 0) { + TALLOC_FREE(tmp_ctx); + goto done; + } + + exp_sort = talloc_memdup(tmp_ctx, exp, num_exp * sizeof(*exp)); + torture_assert_goto(tctx, exp_sort != NULL, ret, done, __location__); + + TYPESAFE_QSORT(exp_sort, num_exp, qsort_string); + + stream_sort = talloc_memdup(tmp_ctx, finfo.stream_info.out.streams, + finfo.stream_info.out.num_streams * + sizeof(*stream_sort)); + torture_assert_goto(tctx, stream_sort != NULL, ret, done, __location__); + + TYPESAFE_QSORT(stream_sort, finfo.stream_info.out.num_streams, qsort_stream); + + for (i=0; i Date: Mon, 22 Oct 2018 16:21:21 +0200 Subject: [PATCH 22/35] s4:torture/vfs/fruit: add test "empty_stream" One to rule them all: consistently test critical operations on all streams relevant to macOS clients: the FinderInfo stream, the Resource Fork stream and an arbitrary stream that macOS maps to xattrs when written to on a macOS SMB server. Bug: https://bugzilla.samba.org/show_bug.cgi?id=13646 Signed-off-by: Ralph Boehme Reviewed-by: Jeremy Allison (cherry picked from commit 9d8751db64de75a0de442365b317c1b9ce194170) --- selftest/knownfail.d/samba3.vfs.fruit | 3 + source4/torture/vfs/fruit.c | 613 +++++++++++++++++++++++++- 2 files changed, 614 insertions(+), 2 deletions(-) diff --git a/selftest/knownfail.d/samba3.vfs.fruit b/selftest/knownfail.d/samba3.vfs.fruit index 6307e2b3404..2d3c21caf91 100644 --- a/selftest/knownfail.d/samba3.vfs.fruit +++ b/selftest/knownfail.d/samba3.vfs.fruit @@ -1,2 +1,5 @@ ^samba3.vfs.fruit streams_depot.OS X AppleDouble file conversion\(nt4_dc\) ^samba3.vfs.fruit streams_depot.OS X AppleDouble file conversion without embedded xattr\(nt4_dc\) +^samba3.vfs.fruit metadata_netatalk.empty_stream\(nt4_dc\) +^samba3.vfs.fruit metadata_stream.empty_stream\(nt4_dc\) +^samba3.vfs.fruit streams_depot.empty_stream\(nt4_dc\) diff --git a/source4/torture/vfs/fruit.c b/source4/torture/vfs/fruit.c index 20f0e2b4e5b..66cd0b6d4b9 100644 --- a/source4/torture/vfs/fruit.c +++ b/source4/torture/vfs/fruit.c @@ -3082,7 +3082,6 @@ static bool check_stream_list(struct smb2_tree *tree, return ret; } -#if 0 static bool check_stream_list_handle(struct smb2_tree *tree, struct torture_context *tctx, struct smb2_handle h, @@ -3142,7 +3141,6 @@ static bool check_stream_list_handle(struct smb2_tree *tree, TALLOC_FREE(tmp_ctx); return ret; } -#endif /* test stream names @@ -5261,6 +5259,616 @@ static bool test_setinfo_stream_eof(struct torture_context *tctx, return ret; } +#define MAX_STREAMS 16 + +struct tcase { + const char *name; + uint32_t access; + const char *write_data; + size_t write_size; + struct tcase_results { + size_t size; + NTSTATUS initial_status; + NTSTATUS final_status; + int num_streams_open_handle; + const char *streams_open_handle[MAX_STREAMS]; + int num_streams_closed_handle; + const char *streams_closed_handle[MAX_STREAMS]; + } create, write, overwrite, eof, doc; +}; + +typedef enum {T_CREATE, T_WRITE, T_OVERWRITE, T_EOF, T_DOC} subtcase_t; + +static bool test_empty_stream_do_checks( + struct torture_context *tctx, + struct smb2_tree *tree, + struct smb2_tree *tree2, + struct tcase *tcase, + TALLOC_CTX *mem_ctx, + struct smb2_handle baseh, + struct smb2_handle streamh, + subtcase_t subcase) +{ + bool ret = false; + NTSTATUS status; + struct smb2_handle h1; + union smb_fileinfo finfo; + struct tcase_results *tcase_results = NULL; + + switch (subcase) { + case T_CREATE: + tcase_results = &tcase->create; + break; + case T_OVERWRITE: + tcase_results = &tcase->overwrite; + break; + case T_WRITE: + tcase_results = &tcase->write; + break; + case T_EOF: + tcase_results = &tcase->eof; + break; + case T_DOC: + tcase_results = &tcase->doc; + break; + } + + finfo = (union smb_fileinfo) { + .generic.level = RAW_FILEINFO_STANDARD_INFORMATION, + .generic.in.file.handle = streamh, + }; + + /* + * Test: check size, same client + */ + + status = smb2_getinfo_file(tree, mem_ctx, &finfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testfile failed\n"); + + torture_assert_int_equal_goto(tctx, finfo.standard_info.out.size, + tcase_results->size, + ret, done, "Wrong size\n"); + + /* + * Test: open, same client + */ + + status = torture_smb2_open(tree, tcase->name, + SEC_FILE_READ_ATTRIBUTE, &h1); + torture_assert_ntstatus_equal_goto(tctx, status, + tcase_results->initial_status, + ret, done, + "smb2_create failed\n"); + if (NT_STATUS_IS_OK(status)) { + status = smb2_util_close(tree, h1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_close failed\n"); + } + + /* + * Test: check streams, same client + */ + + ret = check_stream_list_handle(tree, tctx, baseh, + tcase_results->num_streams_open_handle, + tcase_results->streams_open_handle, + false); + torture_assert_goto(tctx, ret == true, ret, done, "Bad streams"); + + /* + * Test: open, different client + */ + + status = torture_smb2_open(tree2, tcase->name, + SEC_FILE_READ_ATTRIBUTE, &h1); + torture_assert_ntstatus_equal_goto(tctx, status, + tcase_results->initial_status, + ret, done, + "smb2_create failed\n"); + if (NT_STATUS_IS_OK(status)) { + finfo = (union smb_fileinfo) { + .generic.level = RAW_FILEINFO_STANDARD_INFORMATION, + .generic.in.file.handle = h1, + }; + + /* + * Test: check size, different client + */ + + status = smb2_getinfo_file(tree2, mem_ctx, &finfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed\n"); + + torture_assert_int_equal_goto(tctx, finfo.standard_info.out.size, + tcase_results->size, + ret, done, "Wrong size\n"); + + /* + * Test: check streams, different client + */ + + ret = check_stream_list(tree2, tctx, BASEDIR "\\file", + tcase_results->num_streams_open_handle, + tcase_results->streams_open_handle, + false); + torture_assert_goto(tctx, ret == true, ret, done, "Bad streams"); + + status = smb2_util_close(tree2, h1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_close failed\n"); + } + + status = smb2_util_close(tree, streamh); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_close failed\n"); + + /* + * Test: open after close, same client + */ + + status = torture_smb2_open(tree, tcase->name, + SEC_FILE_READ_DATA, &h1); + torture_assert_ntstatus_equal_goto(tctx, status, + tcase_results->final_status, + ret, done, + "smb2_create failed\n"); + if (NT_STATUS_IS_OK(status)) { + status = smb2_util_close(tree, h1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_close failed\n"); + } + + /* + * Test: open after close, different client + */ + + status = torture_smb2_open(tree2, tcase->name, + SEC_FILE_READ_DATA, &h1); + torture_assert_ntstatus_equal_goto(tctx, status, + tcase_results->final_status, + ret, done, + "smb2_create failed\n"); + if (NT_STATUS_IS_OK(status)) { + status = smb2_util_close(tree2, h1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_close failed\n"); + } + + /* + * Test: check streams after close, same client + */ + + ret = check_stream_list_handle(tree, tctx, baseh, + tcase_results->num_streams_closed_handle, + tcase_results->streams_closed_handle, + false); + torture_assert_goto(tctx, ret == true, ret, done, "Bad streams"); + + ret = true; + +done: + smb2_util_close(tree, streamh); + smb2_util_close(tree, baseh); + return ret; +} + +static bool test_empty_stream_do_one( + struct torture_context *tctx, + struct smb2_tree *tree, + struct smb2_tree *tree2, + struct tcase *tcase) +{ + bool ret = false; + NTSTATUS status; + struct smb2_handle baseh; + struct smb2_handle streamh; + struct smb2_create create; + union smb_setfileinfo sfinfo; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + + torture_comment(tctx, "Testing stream [%s]\n", tcase->name); + + torture_assert_goto(tctx, mem_ctx != NULL, ret, done, "talloc_new\n"); + + /* + * Subtest: create + */ + torture_comment(tctx, "Subtest: T_CREATE\n"); + + status = smb2_util_unlink(tree, BASEDIR "\\file"); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_unlink failed\n"); + + status = torture_smb2_testfile_access(tree, BASEDIR "\\file", + &baseh, SEC_FILE_ALL); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testfile_access failed\n"); + + status = torture_smb2_testfile_access(tree, tcase->name, &streamh, + tcase->access); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testfile_access failed\n"); + + ret = test_empty_stream_do_checks(tctx, tree, tree2, tcase, + mem_ctx, baseh, streamh, T_CREATE); + torture_assert_goto(tctx, ret, ret, done, "test failed\n"); + + if (!(tcase->access & SEC_FILE_WRITE_DATA)) { + /* + * All subsequent tests require write access + */ + ret = true; + goto done; + } + + /* + * Subtest: create and write + */ + torture_comment(tctx, "Subtest: T_WRITE\n"); + + status = smb2_util_unlink(tree, BASEDIR "\\file"); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_unlink failed\n"); + + status = torture_smb2_testfile_access(tree, BASEDIR "\\file", + &baseh, SEC_FILE_ALL); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testfile_access failed\n"); + + status = torture_smb2_testfile_access(tree, tcase->name, &streamh, + tcase->access); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testfile_access failed\n"); + + status = smb2_util_write(tree, streamh, tcase->write_data, 0, + tcase->write_size); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_open failed\n"); + + ret = test_empty_stream_do_checks(tctx, tree, tree2, tcase, + mem_ctx, baseh, streamh, T_WRITE); + torture_assert_goto(tctx, ret, ret, done, "test failed\n"); + + /* + * Subtest: overwrite + */ + torture_comment(tctx, "Subtest: T_OVERWRITE\n"); + + status = smb2_util_unlink(tree, BASEDIR "\\file"); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_unlink failed\n"); + + status = torture_smb2_testfile_access(tree, BASEDIR "\\file", + &baseh, SEC_FILE_ALL); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testfile_access failed\n"); + + create = (struct smb2_create) { + .in.desired_access = SEC_FILE_ALL, + .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF, + .in.fname = tcase->name, + }; + + status = smb2_create(tree, tctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testfile failed\n"); + streamh = create.out.file.handle; + + ret = test_empty_stream_do_checks(tctx, tree, tree2, tcase, + mem_ctx, baseh, streamh, T_OVERWRITE); + torture_assert_goto(tctx, ret, ret, done, "test failed\n"); + + /* + * Subtest: setinfo EOF 0 + */ + torture_comment(tctx, "Subtest: T_EOF\n"); + + status = smb2_util_unlink(tree, BASEDIR "\\file"); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_unlink failed\n"); + + status = torture_smb2_testfile_access(tree, BASEDIR "\\file", + &baseh, SEC_FILE_ALL); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testfile_access failed\n"); + + status = torture_smb2_testfile_access(tree, tcase->name, &streamh, + tcase->access); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testfile_access failed\n"); + + status = smb2_util_write(tree, streamh, tcase->write_data, 0, + tcase->write_size); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_open failed\n"); + + sfinfo = (union smb_setfileinfo) { + .end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION, + .end_of_file_info.in.file.handle = streamh, + .end_of_file_info.in.size = 0, + }; + status = smb2_setinfo_file(tree, &sfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "set eof 0 failed\n"); + + ret = test_empty_stream_do_checks(tctx, tree, tree2, tcase, + mem_ctx, baseh, streamh, T_EOF); + torture_assert_goto(tctx, ret, ret, done, "test failed\n"); + + /* + * Subtest: delete-on-close + */ + torture_comment(tctx, "Subtest: T_DOC\n"); + + status = smb2_util_unlink(tree, BASEDIR "\\file"); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_unlink failed\n"); + + status = torture_smb2_testfile_access(tree, BASEDIR "\\file", + &baseh, SEC_FILE_ALL); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testfile_access failed\n"); + + status = torture_smb2_testfile_access(tree, tcase->name, &streamh, + tcase->access); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testfile_access failed\n"); + + status = smb2_util_write(tree, streamh, tcase->write_data, 0, + tcase->write_size); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_open failed\n"); + + sfinfo = (union smb_setfileinfo) { + .disposition_info.level = RAW_SFILEINFO_DISPOSITION_INFORMATION, + .disposition_info.in.file.handle = streamh, + .disposition_info.in.delete_on_close = true, + }; + status = smb2_setinfo_file(tree, &sfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "set eof 0 failed\n"); + + ret = test_empty_stream_do_checks(tctx, tree, tree2, tcase, + mem_ctx, baseh, streamh, + T_DOC); + torture_assert_goto(tctx, ret, ret, done, "test failed\n"); + + ret = true; + +done: + smb2_util_close(tree, baseh); + TALLOC_FREE(mem_ctx); + return ret; +} + +static bool test_empty_stream(struct torture_context *tctx, + struct smb2_tree *tree) +{ + struct smb2_tree *tree2 = NULL; + struct tcase *tcase = NULL; + const char *fname = BASEDIR "\\file"; + struct smb2_handle h1; + bool ret = true; + NTSTATUS status; + AfpInfo ai = (AfpInfo) { + .afpi_Signature = AFP_Signature, + .afpi_Version = AFP_Version, + .afpi_BackupTime = AFP_BackupTime, + .afpi_FinderInfo = "FOO BAR ", + }; + char *ai_blob = torture_afpinfo_pack(tctx, &ai); + struct tcase tcase_afpinfo_ro = (struct tcase) { + .name = BASEDIR "\\file" AFPINFO_STREAM, + .access = SEC_FILE_READ_DATA|SEC_FILE_READ_ATTRIBUTE, + .create.size = 60, + .create.initial_status = NT_STATUS_OBJECT_NAME_NOT_FOUND, + .create.final_status = NT_STATUS_OBJECT_NAME_NOT_FOUND, + .create.num_streams_open_handle = 1, + .create.num_streams_closed_handle = 1, + .create.streams_open_handle = {"::$DATA"}, + .create.streams_closed_handle = {"::$DATA"}, + }; + struct tcase tcase_afpinfo_rw = (struct tcase) { + .name = BASEDIR "\\file" AFPINFO_STREAM, + .access = SEC_FILE_READ_DATA|SEC_FILE_READ_ATTRIBUTE|SEC_FILE_WRITE_DATA|SEC_STD_DELETE, + .write_data = ai_blob, + .write_size = AFP_INFO_SIZE, + .create.size = 60, + .create.initial_status = NT_STATUS_OBJECT_NAME_NOT_FOUND, + .create.final_status = NT_STATUS_OBJECT_NAME_NOT_FOUND, + .create.num_streams_open_handle = 1, + .create.num_streams_closed_handle = 1, + .create.streams_open_handle = {"::$DATA"}, + .create.streams_closed_handle = {"::$DATA"}, + .write.size = 60, + .write.initial_status = NT_STATUS_OK, + .write.final_status = NT_STATUS_OK, + .write.num_streams_open_handle = 2, + .write.num_streams_closed_handle = 2, + .write.streams_open_handle = {"::$DATA", AFPINFO_STREAM}, + .write.streams_closed_handle = {"::$DATA", AFPINFO_STREAM}, + .overwrite.size = 60, + .overwrite.initial_status = NT_STATUS_OBJECT_NAME_NOT_FOUND, + .overwrite.final_status = NT_STATUS_OBJECT_NAME_NOT_FOUND, + .overwrite.num_streams_open_handle = 1, + .overwrite.num_streams_closed_handle = 1, + .overwrite.streams_open_handle = {"::$DATA"}, + .overwrite.streams_closed_handle = {"::$DATA"}, + .eof.size = 60, + .eof.initial_status = NT_STATUS_OK, + .eof.final_status = NT_STATUS_OK, + .eof.num_streams_open_handle = 2, + .eof.num_streams_closed_handle = 2, + .eof.streams_open_handle = {"::$DATA", AFPINFO_STREAM}, + .eof.streams_closed_handle = {"::$DATA", AFPINFO_STREAM}, + .doc.size = 60, + .doc.initial_status = NT_STATUS_DELETE_PENDING, + .doc.final_status = NT_STATUS_OBJECT_NAME_NOT_FOUND, + .doc.num_streams_open_handle = 2, + .doc.num_streams_closed_handle = 1, + .doc.streams_open_handle = {"::$DATA", AFPINFO_STREAM}, + .doc.streams_closed_handle = {"::$DATA"}, + }; + + struct tcase tcase_afpresource_ro = (struct tcase) { + .name = BASEDIR "\\file" AFPRESOURCE_STREAM, + .access = SEC_FILE_READ_DATA|SEC_FILE_READ_ATTRIBUTE, + .create.size = 0, + .create.initial_status = NT_STATUS_OBJECT_NAME_NOT_FOUND, + .create.final_status = NT_STATUS_OBJECT_NAME_NOT_FOUND, + .create.num_streams_open_handle = 1, + .create.num_streams_closed_handle = 1, + .create.streams_open_handle = {"::$DATA"}, + .create.streams_closed_handle = {"::$DATA"}, + }; + struct tcase tcase_afpresource_rw = (struct tcase) { + .name = BASEDIR "\\file" AFPRESOURCE_STREAM, + .access = SEC_FILE_READ_DATA|SEC_FILE_READ_ATTRIBUTE|SEC_FILE_WRITE_DATA|SEC_STD_DELETE, + .write_data = "foo", + .write_size = 3, + .create.size = 0, + .create.initial_status = NT_STATUS_OBJECT_NAME_NOT_FOUND, + .create.final_status = NT_STATUS_OBJECT_NAME_NOT_FOUND, + .create.num_streams_open_handle = 1, + .create.num_streams_closed_handle = 1, + .create.streams_open_handle = {"::$DATA"}, + .create.streams_closed_handle = {"::$DATA"}, + .write.size = 3, + .write.initial_status = NT_STATUS_OK, + .write.final_status = NT_STATUS_OK, + .write.num_streams_open_handle = 2, + .write.num_streams_closed_handle = 2, + .write.streams_open_handle = {"::$DATA", AFPRESOURCE_STREAM}, + .write.streams_closed_handle = {"::$DATA", AFPRESOURCE_STREAM}, + .overwrite.size = 0, + .overwrite.initial_status = NT_STATUS_OBJECT_NAME_NOT_FOUND, + .overwrite.final_status = NT_STATUS_OBJECT_NAME_NOT_FOUND, + .overwrite.num_streams_open_handle = 1, + .overwrite.num_streams_closed_handle = 1, + .overwrite.streams_open_handle = {"::$DATA"}, + .overwrite.streams_closed_handle = {"::$DATA"}, + .eof.size = 0, + .eof.initial_status = NT_STATUS_OBJECT_NAME_NOT_FOUND, + .eof.final_status = NT_STATUS_OBJECT_NAME_NOT_FOUND, + .eof.num_streams_open_handle = 1, + .eof.num_streams_closed_handle = 1, + .eof.streams_open_handle = {"::$DATA"}, + .eof.streams_closed_handle = {"::$DATA"}, + .doc.size = 3, + .doc.initial_status = NT_STATUS_DELETE_PENDING, + .doc.final_status = NT_STATUS_OK, + .doc.num_streams_open_handle = 2, + .doc.num_streams_closed_handle = 2, + .doc.streams_open_handle = {"::$DATA", AFPRESOURCE_STREAM}, + .doc.streams_closed_handle = {"::$DATA", AFPRESOURCE_STREAM}, + }; + + struct tcase tcase_foo_ro = (struct tcase) { + .name = BASEDIR "\\file:foo", + .access = SEC_FILE_READ_DATA|SEC_FILE_READ_ATTRIBUTE, + .write_data = "foo", + .write_size = 3, + .create.size = 0, + .create.initial_status = NT_STATUS_OBJECT_NAME_NOT_FOUND, + .create.final_status = NT_STATUS_OBJECT_NAME_NOT_FOUND, + .create.num_streams_open_handle = 1, + .create.num_streams_closed_handle = 1, + .create.streams_open_handle = {"::$DATA"}, + .create.streams_closed_handle = {"::$DATA"}, + }; + + struct tcase tcase_foo_rw = (struct tcase) { + .name = BASEDIR "\\file:foo", + .access = SEC_FILE_READ_DATA|SEC_FILE_READ_ATTRIBUTE|SEC_FILE_WRITE_DATA|SEC_STD_DELETE, + .write_data = "foo", + .write_size = 3, + .create.size = 0, + .create.initial_status = NT_STATUS_OBJECT_NAME_NOT_FOUND, + .create.final_status = NT_STATUS_OBJECT_NAME_NOT_FOUND, + .create.num_streams_open_handle = 1, + .create.num_streams_closed_handle = 1, + .create.streams_open_handle = {"::$DATA"}, + .create.streams_closed_handle = {"::$DATA"}, + .write.size = 3, + .write.initial_status = NT_STATUS_OK, + .write.final_status = NT_STATUS_OK, + .write.num_streams_open_handle = 2, + .write.num_streams_closed_handle = 2, + .write.streams_open_handle = {"::$DATA", ":foo:$DATA"}, + .write.streams_closed_handle = {"::$DATA", ":foo:$DATA"}, + .overwrite.size = 0, + .overwrite.initial_status = NT_STATUS_OBJECT_NAME_NOT_FOUND, + .overwrite.final_status = NT_STATUS_OBJECT_NAME_NOT_FOUND, + .overwrite.num_streams_open_handle = 1, + .overwrite.num_streams_closed_handle = 1, + .overwrite.streams_open_handle = {"::$DATA"}, + .overwrite.streams_closed_handle = {"::$DATA"}, + .eof.size = 0, + .eof.initial_status = NT_STATUS_OBJECT_NAME_NOT_FOUND, + .eof.final_status = NT_STATUS_OBJECT_NAME_NOT_FOUND, + .eof.num_streams_open_handle = 1, + .eof.num_streams_closed_handle = 1, + .eof.streams_open_handle = {"::$DATA"}, + .eof.streams_closed_handle = {"::$DATA"}, + .doc.size = 3, + .doc.initial_status = NT_STATUS_DELETE_PENDING, + .doc.final_status = NT_STATUS_OBJECT_NAME_NOT_FOUND, + .doc.num_streams_open_handle = 2, + .doc.num_streams_closed_handle = 1, + .doc.streams_open_handle = {"::$DATA", ":foo:$DATA"}, + .doc.streams_closed_handle = {"::$DATA"}, + }; + + struct tcase tcases[] = { + tcase_afpinfo_ro, + tcase_afpinfo_rw, + tcase_afpresource_ro, + tcase_afpresource_rw, + tcase_foo_ro, + tcase_foo_rw, + {NULL} + }; + + ret = torture_smb2_connection(tctx, &tree2); + torture_assert_goto(tctx, ret == true, ret, done, + "torture_smb2_connection failed\n"); + + ret = enable_aapl(tctx, tree); + torture_assert(tctx, ret == true, "enable_aapl failed\n"); + + ret = enable_aapl(tctx, tree2); + torture_assert(tctx, ret == true, "enable_aapl failed\n"); + + smb2_deltree(tree, BASEDIR); + + status = torture_smb2_testdir(tree, BASEDIR, &h1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testdir\n"); + smb2_util_close(tree, h1); + + for (tcase = &tcases[0]; tcase->name != NULL; tcase++) { + ret = torture_setup_file(tctx, tree, fname, false); + torture_assert_goto(tctx, ret == true, ret, done, + "torture_setup_file failed\n"); + + ret = test_empty_stream_do_one(tctx, tree, tree2, tcase); + torture_assert_goto(tctx, ret == true, ret, done, + "subtest failed\n"); + + status = smb2_util_unlink(tree, fname); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_unlink failed\n"); + } + +done: + smb2_deltree(tree, BASEDIR); + TALLOC_FREE(tree2); + return ret; +} + /* * 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 @@ -5304,6 +5912,7 @@ struct torture_suite *torture_vfs_fruit(TALLOC_CTX *ctx) torture_suite_add_1smb2_test(suite, "OS X AppleDouble file conversion", test_adouble_conversion); torture_suite_add_1smb2_test(suite, "NFS ACE entries", test_nfs_aces); torture_suite_add_1smb2_test(suite, "OS X AppleDouble file conversion without embedded xattr", test_adouble_conversion_wo_xattr); + torture_suite_add_1smb2_test(suite, "empty_stream", test_empty_stream); return suite; } -- 2.17.2 From bbd5e50d09fb1c27246c86f35b7099df855f7e97 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Wed, 17 Oct 2018 19:07:11 +0200 Subject: [PATCH 23/35] vfs_fruit: add some debugging of dev/ino Aids in debugging dev/ino mismatch failures in open_file_ntcreate. Bug: https://bugzilla.samba.org/show_bug.cgi?id=13646 Signed-off-by: Ralph Boehme Reviewed-by: Jeremy Allison (cherry picked from commit 993c7c4e711612225bd07b8fa5544c4bfef88a9e) --- source3/modules/vfs_fruit.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c index 9932249e315..8660befd3d9 100644 --- a/source3/modules/vfs_fruit.c +++ b/source3/modules/vfs_fruit.c @@ -2374,6 +2374,10 @@ static SMB_INO_T fruit_inode(const SMB_STRUCT_STAT *sbuf, const char *sname) SMB_INO_T result; char *upper_sname; + DBG_DEBUG("fruit_inode called for %ju/%ju [%s]\n", + (uintmax_t)sbuf->st_ex_dev, + (uintmax_t)sbuf->st_ex_ino, sname); + upper_sname = talloc_strdup_upper(talloc_tos(), sname); SMB_ASSERT(upper_sname != NULL); @@ -2391,8 +2395,8 @@ static SMB_INO_T fruit_inode(const SMB_STRUCT_STAT *sbuf, const char *sname) /* Hopefully all the variation is in the lower 4 (or 8) bytes! */ memcpy(&result, hash, sizeof(result)); - DEBUG(10, ("fruit_inode \"%s\": ino=0x%llu\n", - sname, (unsigned long long)result)); + DBG_DEBUG("fruit_inode \"%s\": ino=%ju\n", + sname, (uintmax_t)result); return result; } @@ -4795,6 +4799,11 @@ static int fruit_stat_base(vfs_handle_struct *handle, rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname); } smb_fname->stream_name = tmp_stream_name; + + DBG_DEBUG("fruit_stat_base [%s] dev [%ju] ino [%ju]\n", + smb_fname->base_name, + (uintmax_t)smb_fname->st.st_ex_dev, + (uintmax_t)smb_fname->st.st_ex_ino); return rc; } -- 2.17.2 From 89fc9a072de2de0bfda6d074f670b5c0020a0110 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Mon, 15 Oct 2018 18:38:33 +0200 Subject: [PATCH 24/35] vfs_fruit: remove resource fork special casing Directly unlinking a file with open handles is not good, don't do it. Bug: https://bugzilla.samba.org/show_bug.cgi?id=13646 Signed-off-by: Ralph Boehme Reviewed-by: Jeremy Allison (cherry picked from commit a26032c3f68028b01fb6a7d38851409db1858161) --- source3/modules/vfs_fruit.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c index 8660befd3d9..e5b3897b6e4 100644 --- a/source3/modules/vfs_fruit.c +++ b/source3/modules/vfs_fruit.c @@ -5716,10 +5716,6 @@ static int fruit_ftruncate_rsrc_xattr(struct vfs_handle_struct *handle, struct files_struct *fsp, off_t offset) { - if (offset == 0) { - return SMB_VFS_FREMOVEXATTR(fsp, AFPRESOURCE_EA_NETATALK); - } - #ifdef HAVE_ATTROPEN return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset); #endif @@ -5767,10 +5763,6 @@ static int fruit_ftruncate_rsrc_stream(struct vfs_handle_struct *handle, struct files_struct *fsp, off_t offset) { - if (offset == 0) { - return SMB_VFS_NEXT_UNLINK(handle, fsp->fsp_name); - } - return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset); } -- 2.17.2 From a75bfbb59ecb2227f9306001f28a7b7133be744a Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Mon, 22 Oct 2018 16:56:46 +0200 Subject: [PATCH 25/35] vfs_fruit: add fio->created fio->created tracks whether a create created a stream. Bug: https://bugzilla.samba.org/show_bug.cgi?id=13646 Signed-off-by: Ralph Boehme Reviewed-by: Jeremy Allison (cherry picked from commit 1e055a79541eb69eb2deeae897dde3665c5ffee2) --- source3/modules/vfs_fruit.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c index e5b3897b6e4..bbfacad7811 100644 --- a/source3/modules/vfs_fruit.c +++ b/source3/modules/vfs_fruit.c @@ -514,6 +514,9 @@ struct fio { /* Denote stream type, meta or rsrc */ adouble_type_t type; + + /* Whether the create created the stream */ + bool created; }; /* @@ -5860,6 +5863,7 @@ static NTSTATUS fruit_create_file(vfs_handle_struct *handle, NTSTATUS status; struct fruit_config_data *config = NULL; files_struct *fsp = NULL; + struct fio *fio = NULL; status = check_aapl(handle, req, in_context_blobs, out_context_blobs); if (!NT_STATUS_IS_OK(status)) { @@ -5910,6 +5914,11 @@ static NTSTATUS fruit_create_file(vfs_handle_struct *handle, goto fail; } + fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp); + if (fio != NULL && pinfo != NULL && *pinfo == FILE_WAS_CREATED) { + fio->created = true; + } + if (is_ntfs_stream_smb_fname(smb_fname) || fsp->is_directory) { return status; -- 2.17.2 From 592d2ef04851be7227f52609735ac732073304fb Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Wed, 22 Aug 2018 15:22:57 +0200 Subject: [PATCH 26/35] vfs_fruit: prepare struct fio for fake-fd and on-demand opening Not used for now, that comes in the subsequent commits. Bug: https://bugzilla.samba.org/show_bug.cgi?id=13646 Signed-off-by: Ralph Boehme Reviewed-by: Jeremy Allison (cherry picked from commit 96320eccc9a63b793ff8d05842bce54f120286f9) --- source3/modules/vfs_fruit.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c index bbfacad7811..4062b930254 100644 --- a/source3/modules/vfs_fruit.c +++ b/source3/modules/vfs_fruit.c @@ -517,6 +517,17 @@ struct fio { /* Whether the create created the stream */ bool created; + + /* + * AFP_AfpInfo stream created, but not written yet, thus still a fake + * pipe fd. This is set to true in fruit_open_meta if there was no + * exisiting stream but the caller requested O_CREAT. It is later set to + * false when we get a write on the stream that then does open and + * create the stream. + */ + bool fake_fd; + int flags; + int mode; }; /* -- 2.17.2 From 011ed1e648dd547f13cbebf70cf69d2ec8f987d9 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Wed, 22 Aug 2018 15:21:08 +0200 Subject: [PATCH 27/35] vfs_fruit: prepare fruit_pwrite_meta() for on-demand opening and writing This avoid creating files or blobs in our streams backend when a client creates a stream but hasn't written anything yet. This is the only sane way to implement the following semantics: * client 1: create stream "file:foo" * client 2: open stream "file:foo" The second operation of client 2 must fail with NT_STATUS_NOT_FOUND. Bug: https://bugzilla.samba.org/show_bug.cgi?id=13646 Signed-off-by: Ralph Boehme Reviewed-by: Jeremy Allison (cherry picked from commit 4a5c9a9e73230f640eb045a3c47af75b5be9f1d6) --- source3/modules/vfs_fruit.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c index 4062b930254..8ff82f6e461 100644 --- a/source3/modules/vfs_fruit.c +++ b/source3/modules/vfs_fruit.c @@ -4485,10 +4485,44 @@ static ssize_t fruit_pwrite_meta_stream(vfs_handle_struct *handle, files_struct *fsp, const void *data, size_t n, off_t offset) { + struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp); AfpInfo *ai = NULL; size_t nwritten; + int ret; bool ok; + DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n", + fsp_str_dbg(fsp), (intmax_t)offset, n); + + if (fio == NULL) { + return -1; + } + + if (fio->fake_fd) { + int fd; + + ret = SMB_VFS_NEXT_CLOSE(handle, fsp); + if (ret != 0) { + DBG_ERR("Close [%s] failed: %s\n", + fsp_str_dbg(fsp), strerror(errno)); + fsp->fh->fd = -1; + return -1; + } + + fd = SMB_VFS_NEXT_OPEN(handle, + fsp->fsp_name, + fsp, + fio->flags, + fio->mode); + if (fd == -1) { + DBG_ERR("On-demand create [%s] in write failed: %s\n", + fsp_str_dbg(fsp), strerror(errno)); + return -1; + } + fsp->fh->fd = fd; + fio->fake_fd = false; + } + ai = afpinfo_unpack(talloc_tos(), data); if (ai == NULL) { return -1; -- 2.17.2 From 8dfdae832ad1ccff1b1fa79d3118c89b950f154c Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Wed, 22 Aug 2018 15:22:08 +0200 Subject: [PATCH 28/35] vfs_fruit: prepare fruit_pread_meta() for reading on fake-fd If the read on the stream fails we may have hit a handle on a just created stream (fio->created=true) with no data written yet. If that's the case return an empty initialized FinderInfo blob. Bug: https://bugzilla.samba.org/show_bug.cgi?id=13646 Signed-off-by: Ralph Boehme Reviewed-by: Jeremy Allison (cherry picked from commit d7d92710711f6e555ed45c1dda528cd6a83e1bf5) --- source3/modules/vfs_fruit.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c index 8ff82f6e461..442ed1a9052 100644 --- a/source3/modules/vfs_fruit.c +++ b/source3/modules/vfs_fruit.c @@ -4194,8 +4194,7 @@ static ssize_t fruit_pread_meta_stream(vfs_handle_struct *handle, int ret; nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset); - - if (nread == n) { + if (nread == -1 || nread == n) { return nread; } @@ -4294,6 +4293,25 @@ static ssize_t fruit_pread_meta(vfs_handle_struct *handle, return -1; } + if (nread == -1 && fio->created) { + AfpInfo *ai = NULL; + char afpinfo_buf[AFP_INFO_SIZE]; + + ai = afpinfo_new(talloc_tos()); + if (ai == NULL) { + return -1; + } + + nread = afpinfo_pack(ai, afpinfo_buf); + TALLOC_FREE(ai); + if (nread != AFP_INFO_SIZE) { + return -1; + } + + memcpy(data, afpinfo_buf, to_return); + return to_return; + } + return nread; } -- 2.17.2 From 0c42af2c7a2e9893ed75363e28cddb08f9900f94 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Wed, 22 Aug 2018 16:49:23 +0200 Subject: [PATCH 29/35] vfs_fruit: do ino calculation As we'll start returning fake fds in open shortly, we can't rely on the next module to calculat correct inode numbers for streams and must take over that responsibility. Bug: https://bugzilla.samba.org/show_bug.cgi?id=13646 Signed-off-by: Ralph Boehme Reviewed-by: Jeremy Allison (cherry picked from commit 80afafe398566fd622f431966808d08ba9ec6473) --- source3/modules/vfs_fruit.c | 50 +++++++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c index 442ed1a9052..8e83183810e 100644 --- a/source3/modules/vfs_fruit.c +++ b/source3/modules/vfs_fruit.c @@ -4878,6 +4878,14 @@ static int fruit_stat_meta_stream(vfs_handle_struct *handle, bool follow_links) { int ret; + ino_t ino; + + ret = fruit_stat_base(handle, smb_fname, false); + if (ret != 0) { + return -1; + } + + ino = fruit_inode(&smb_fname->st, smb_fname->stream_name); if (follow_links) { ret = SMB_VFS_NEXT_STAT(handle, smb_fname); @@ -4885,6 +4893,8 @@ static int fruit_stat_meta_stream(vfs_handle_struct *handle, ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname); } + smb_fname->st.st_ex_ino = ino; + return ret; } @@ -5138,7 +5148,41 @@ static int fruit_fstat_meta_stream(vfs_handle_struct *handle, files_struct *fsp, SMB_STRUCT_STAT *sbuf) { - return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf); + struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp); + ino_t ino; + int ret; + + if (fio == NULL) { + return -1; + } + + if (fio->fake_fd) { + ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false); + if (ret != 0) { + return -1; + } + + *sbuf = fsp->base_fsp->fsp_name->st; + sbuf->st_ex_size = AFP_INFO_SIZE; + sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name); + return 0; + } + + ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false); + if (ret != 0) { + return -1; + } + *sbuf = fsp->base_fsp->fsp_name->st; + + ino = fruit_inode(sbuf, fsp->fsp_name->stream_name); + + ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf); + if (ret != 0) { + return -1; + } + + sbuf->st_ex_ino = ino; + return 0; } static int fruit_fstat_meta_netatalk(vfs_handle_struct *handle, @@ -5373,12 +5417,14 @@ static NTSTATUS fruit_streaminfo_meta_stream( goto out; } - ret = SMB_VFS_NEXT_STAT(handle, sname); + ret = fruit_stat_base(handle, sname, false); if (ret != 0) { status = map_nt_error_from_unix(errno); goto out; } + sname->st.st_ex_ino = fruit_inode(&sname->st, AFPINFO_STREAM); + id = SMB_VFS_NEXT_FILE_ID_CREATE(handle, &sname->st); lck = get_existing_share_mode_lock(talloc_tos(), id); -- 2.17.2 From 37cf7f8c687a25c30ef6d509a0cffaa4dbbb9545 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Wed, 17 Oct 2018 16:51:34 +0200 Subject: [PATCH 30/35] vfs_fruit: let fruit handle all aio on the FinderInfo metadata stream This will be required to support using fake fds for the FinderInfo metadata stream. Bug: https://bugzilla.samba.org/show_bug.cgi?id=13646 Signed-off-by: Ralph Boehme Reviewed-by: Jeremy Allison (cherry picked from commit 6fd256afcd5fedc894036efbfba1fc6d2264fba9) --- source3/modules/vfs_fruit.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c index 8e83183810e..ff981d0479b 100644 --- a/source3/modules/vfs_fruit.c +++ b/source3/modules/vfs_fruit.c @@ -4406,9 +4406,7 @@ static bool fruit_must_handle_aio_stream(struct fio *fio) return false; }; - if ((fio->type == ADOUBLE_META) && - (fio->config->meta == FRUIT_META_NETATALK)) - { + if (fio->type == ADOUBLE_META) { return true; } -- 2.17.2 From 42a0ae2e426c09b91a6cd9fed7a852d0f280fd2f Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Sat, 20 Oct 2018 23:46:43 +0200 Subject: [PATCH 31/35] vfs_fruit: pass stream size to delete_invalid_meta_stream() delete_invalid_meta_stream() is meant to guard against random data being present in the FinderInfo stream. If the stream size is 0, it's likely a freshly created stream where no data has been written to yet, so don't delete it. Bug: https://bugzilla.samba.org/show_bug.cgi?id=13646 Signed-off-by: Ralph Boehme Reviewed-by: Jeremy Allison (cherry picked from commit 6e13dbddaec9f8118e11309297f85c3cdfd22e43) --- source3/modules/vfs_fruit.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c index ff981d0479b..07ee5654e5d 100644 --- a/source3/modules/vfs_fruit.c +++ b/source3/modules/vfs_fruit.c @@ -5332,7 +5332,8 @@ static NTSTATUS delete_invalid_meta_stream( const struct smb_filename *smb_fname, TALLOC_CTX *mem_ctx, unsigned int *pnum_streams, - struct stream_struct **pstreams) + struct stream_struct **pstreams, + off_t size) { struct smb_filename *sname = NULL; int ret; @@ -5343,6 +5344,10 @@ static NTSTATUS delete_invalid_meta_stream( return NT_STATUS_INTERNAL_ERROR; } + if (size == 0) { + return NT_STATUS_OK; + } + sname = synthetic_smb_fname(talloc_tos(), smb_fname->base_name, AFPINFO_STREAM_NAME, @@ -5396,8 +5401,12 @@ static NTSTATUS fruit_streaminfo_meta_stream( DBG_ERR("Removing invalid AFPINFO_STREAM size [%jd] from [%s]\n", (intmax_t)stream[i].size, smb_fname_str_dbg(smb_fname)); - return delete_invalid_meta_stream(handle, smb_fname, mem_ctx, - pnum_streams, pstreams); + return delete_invalid_meta_stream(handle, + smb_fname, + mem_ctx, + pnum_streams, + pstreams, + stream[i].size); } /* -- 2.17.2 From 090b54f0d12e20579f16424e53e0fe8b68c852fd Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Sat, 20 Oct 2018 23:40:14 +0200 Subject: [PATCH 32/35] vfs_fruit: let fruit_pwrite_meta_stream also ftruncate empty FinderInfo fruit_streaminfo currently filters out the FinderInfo stream is delete-on-close is set. We set it here internally, but the client may also set it over SMB. Turns out that the macOS SMB server does NOT filter out FinderInfo stream with delete-on-close set, so we must change the way filtering is done in fruit_streaminfo. Filtering is now done based on the FinderInfo stream being 0-bytes large which is why I'm adding the ftruncate here. No idea why the tests that check the filtering passed the commits leading up to this one, but if you revert this commit after applying the whole patchset, the "delete AFP_AfpInfo by writing all 0" test will fail. Bug: https://bugzilla.samba.org/show_bug.cgi?id=13646 Signed-off-by: Ralph Boehme Reviewed-by: Jeremy Allison (cherry picked from commit 480695cd723cc4949e0b39ddb83560efac393412) --- source3/modules/vfs_fruit.c | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c index 07ee5654e5d..c459046a7e1 100644 --- a/source3/modules/vfs_fruit.c +++ b/source3/modules/vfs_fruit.c @@ -4544,23 +4544,29 @@ static ssize_t fruit_pwrite_meta_stream(vfs_handle_struct *handle, return -1; } - nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset); - if (nwritten != n) { - return -1; - } - - if (!ai_empty_finderinfo(ai)) { - return n; - } + if (ai_empty_finderinfo(ai)) { + ret = SMB_VFS_NEXT_FTRUNCATE(handle, fsp, 0); + if (ret != 0) { + DBG_ERR("SMB_VFS_NEXT_FTRUNCATE on [%s] failed\n", + fsp_str_dbg(fsp)); + return -1; + } - ok = set_delete_on_close( + ok = set_delete_on_close( fsp, true, handle->conn->session_info->security_token, handle->conn->session_info->unix_token); - if (!ok) { - DBG_ERR("set_delete_on_close on [%s] failed\n", - fsp_str_dbg(fsp)); + if (!ok) { + DBG_ERR("set_delete_on_close on [%s] failed\n", + fsp_str_dbg(fsp)); + return -1; + } + return n; + } + + nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset); + if (nwritten != n) { return -1; } -- 2.17.2 From dcdd2726117aa9e4555d53256d050b4264115ffb Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Sat, 20 Oct 2018 23:50:32 +0200 Subject: [PATCH 33/35] vfs_fruit: don't check for delete-on-close on the FinderInfo stream macOS SMB server doesn't filter out the FinderInfo stream if it has delete-on-close set. Bug: https://bugzilla.samba.org/show_bug.cgi?id=13646 Signed-off-by: Ralph Boehme Reviewed-by: Jeremy Allison (cherry picked from commit d8c1bb52018289878b9397d513ebbae63933a05f) --- source3/modules/vfs_fruit.c | 73 +------------------------------------ 1 file changed, 1 insertion(+), 72 deletions(-) diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c index c459046a7e1..a4594c33567 100644 --- a/source3/modules/vfs_fruit.c +++ b/source3/modules/vfs_fruit.c @@ -5382,16 +5382,7 @@ static NTSTATUS fruit_streaminfo_meta_stream( { struct stream_struct *stream = *pstreams; unsigned int num_streams = *pnum_streams; - struct smb_filename *sname = NULL; - char *full_name = NULL; - uint32_t name_hash; - struct share_mode_lock *lck = NULL; - struct file_id id = {0}; - bool delete_on_close_set; int i; - int ret; - NTSTATUS status; - bool ok; for (i = 0; i < num_streams; i++) { if (strequal_m(stream[i].name, AFPINFO_STREAM)) { @@ -5415,70 +5406,8 @@ static NTSTATUS fruit_streaminfo_meta_stream( stream[i].size); } - /* - * Now check if there's a delete-on-close pending on the stream. If so, - * hide the stream. This behaviour was verified against a macOS 10.12 - * SMB server. - */ - sname = synthetic_smb_fname(talloc_tos(), - smb_fname->base_name, - AFPINFO_STREAM_NAME, - NULL, 0); - if (sname == NULL) { - status = NT_STATUS_NO_MEMORY; - goto out; - } - - ret = fruit_stat_base(handle, sname, false); - if (ret != 0) { - status = map_nt_error_from_unix(errno); - goto out; - } - - sname->st.st_ex_ino = fruit_inode(&sname->st, AFPINFO_STREAM); - - id = SMB_VFS_NEXT_FILE_ID_CREATE(handle, &sname->st); - - lck = get_existing_share_mode_lock(talloc_tos(), id); - if (lck == NULL) { - status = NT_STATUS_OK; - goto out; - } - - full_name = talloc_asprintf(talloc_tos(), - "%s%s", - sname->base_name, - AFPINFO_STREAM); - if (full_name == NULL) { - status = NT_STATUS_NO_MEMORY; - goto out; - } - - status = file_name_hash(handle->conn, full_name, &name_hash); - if (!NT_STATUS_IS_OK(status)) { - goto out; - } - - delete_on_close_set = is_delete_on_close_set(lck, name_hash); - if (delete_on_close_set) { - ok = del_fruit_stream(mem_ctx, - pnum_streams, - pstreams, - AFPINFO_STREAM); - if (!ok) { - status = NT_STATUS_INTERNAL_ERROR; - goto out; - } - } - - status = NT_STATUS_OK; - -out: - TALLOC_FREE(sname); - TALLOC_FREE(lck); - TALLOC_FREE(full_name); - return status; + return NT_STATUS_OK; } static NTSTATUS fruit_streaminfo_meta_netatalk( -- 2.17.2 From a070a0282f3054e83b88e9d74cd45d8ff34cc1c8 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Wed, 22 Aug 2018 15:25:26 +0200 Subject: [PATCH 34/35] vfs_fruit: let fruit_open_meta() with O_CREAT return a fake-fd This is the final step in implementing the needed macOS semantics on the FinderInfo stream: as long as the client hasn't written a non-zero FinderInfo blob to the stream, there mustn't be a visible filesystem entry for other openers. Bug: https://bugzilla.samba.org/show_bug.cgi?id=13646 Signed-off-by: Ralph Boehme Reviewed-by: Jeremy Allison Autobuild-User(master): Jeremy Allison Autobuild-Date(master): Thu Nov 1 01:14:23 CET 2018 on sn-devel-144 (cherry picked from commit 1b2de44ea8114cf2025e8b8c843131e2f2dbed27) --- selftest/knownfail.d/samba3.vfs.fruit | 3 - source3/modules/vfs_fruit.c | 165 +++++++++++--------------- 2 files changed, 72 insertions(+), 96 deletions(-) diff --git a/selftest/knownfail.d/samba3.vfs.fruit b/selftest/knownfail.d/samba3.vfs.fruit index 2d3c21caf91..6307e2b3404 100644 --- a/selftest/knownfail.d/samba3.vfs.fruit +++ b/selftest/knownfail.d/samba3.vfs.fruit @@ -1,5 +1,2 @@ ^samba3.vfs.fruit streams_depot.OS X AppleDouble file conversion\(nt4_dc\) ^samba3.vfs.fruit streams_depot.OS X AppleDouble file conversion without embedded xattr\(nt4_dc\) -^samba3.vfs.fruit metadata_netatalk.empty_stream\(nt4_dc\) -^samba3.vfs.fruit metadata_stream.empty_stream\(nt4_dc\) -^samba3.vfs.fruit streams_depot.empty_stream\(nt4_dc\) diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c index a4594c33567..d75ce01b118 100644 --- a/source3/modules/vfs_fruit.c +++ b/source3/modules/vfs_fruit.c @@ -3403,66 +3403,68 @@ static int fruit_connect(vfs_handle_struct *handle, return rc; } +static int fruit_fake_fd(void) +{ + int pipe_fds[2]; + int fd; + int ret; + + /* + * Return a valid fd, but ensure any attempt to use it returns + * an error (EPIPE). Once we get a write on the handle, we open + * the real fd. + */ + ret = pipe(pipe_fds); + if (ret != 0) { + return -1; + } + fd = pipe_fds[0]; + close(pipe_fds[1]); + + return fd; +} + static int fruit_open_meta_stream(vfs_handle_struct *handle, struct smb_filename *smb_fname, files_struct *fsp, int flags, mode_t mode) { - AfpInfo *ai = NULL; - char afpinfo_buf[AFP_INFO_SIZE]; - ssize_t len, written; - int hostfd = -1; - int rc = -1; + struct fruit_config_data *config = NULL; + struct fio *fio = NULL; + int open_flags = flags & ~O_CREAT; + int fd; - hostfd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode); - if (hostfd == -1) { - return -1; - } + DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname)); - if (!(flags & (O_CREAT | O_TRUNC))) { - return hostfd; - } + SMB_VFS_HANDLE_GET_DATA(handle, config, + struct fruit_config_data, return -1); - ai = afpinfo_new(talloc_tos()); - if (ai == NULL) { - rc = -1; - goto fail; - } + fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL); + fio->type = ADOUBLE_META; + fio->config = config; - len = afpinfo_pack(ai, afpinfo_buf); - if (len != AFP_INFO_SIZE) { - rc = -1; - goto fail; + fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, open_flags, mode); + if (fd != -1) { + return fd; } - /* Set fd, needed in SMB_VFS_NEXT_PWRITE() */ - fsp->fh->fd = hostfd; - - written = SMB_VFS_NEXT_PWRITE(handle, fsp, afpinfo_buf, - AFP_INFO_SIZE, 0); - fsp->fh->fd = -1; - if (written != AFP_INFO_SIZE) { - DBG_ERR("bad write [%zd/%d]\n", written, AFP_INFO_SIZE); - rc = -1; - goto fail; + if (!(flags & O_CREAT)) { + VFS_REMOVE_FSP_EXTENSION(handle, fsp); + return -1; } - rc = 0; + fd = fruit_fake_fd(); + if (fd == -1) { + VFS_REMOVE_FSP_EXTENSION(handle, fsp); + return -1; + } -fail: - DBG_DEBUG("rc=%d, fd=%d\n", rc, hostfd); + fio->fake_fd = true; + fio->flags = flags; + fio->mode = mode; - if (rc != 0) { - int saved_errno = errno; - if (hostfd >= 0) { - fsp->fh->fd = hostfd; - SMB_VFS_NEXT_CLOSE(handle, fsp); - } - hostfd = -1; - errno = saved_errno; - } - return hostfd; + return fd; } static int fruit_open_meta_netatalk(vfs_handle_struct *handle, @@ -3471,56 +3473,42 @@ static int fruit_open_meta_netatalk(vfs_handle_struct *handle, int flags, mode_t mode) { - int rc; - int fakefd = -1; + struct fruit_config_data *config = NULL; + struct fio *fio = NULL; struct adouble *ad = NULL; - int fds[2]; + bool meta_exists = false; + int fd; DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname)); - /* - * Return a valid fd, but ensure any attempt to use it returns an error - * (EPIPE). All operations on the smb_fname or the fsp will use path - * based syscalls. - */ - rc = pipe(fds); - if (rc != 0) { - goto exit; + ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META); + if (ad != NULL) { + meta_exists = true; } - fakefd = fds[0]; - close(fds[1]); - - if (flags & (O_CREAT | O_TRUNC)) { - /* - * The attribute does not exist or needs to be truncated, - * create an AppleDouble EA - */ - ad = ad_init(fsp, handle, ADOUBLE_META); - if (ad == NULL) { - rc = -1; - goto exit; - } - rc = ad_set(ad, fsp->fsp_name); - if (rc != 0) { - rc = -1; - goto exit; - } + TALLOC_FREE(ad); - TALLOC_FREE(ad); + if (!meta_exists && !(flags & O_CREAT)) { + errno = ENOENT; + return -1; } -exit: - DEBUG(10, ("fruit_open meta rc=%d, fd=%d\n", rc, fakefd)); - if (rc != 0) { - int saved_errno = errno; - if (fakefd >= 0) { - close(fakefd); - } - fakefd = -1; - errno = saved_errno; + fd = fruit_fake_fd(); + if (fd == -1) { + return -1; } - return fakefd; + + SMB_VFS_HANDLE_GET_DATA(handle, config, + struct fruit_config_data, return -1); + + fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL); + fio->type = ADOUBLE_META; + fio->config = config; + fio->fake_fd = true; + fio->flags = flags; + fio->mode = mode; + + return fd; } static int fruit_open_meta(vfs_handle_struct *handle, @@ -3529,7 +3517,6 @@ static int fruit_open_meta(vfs_handle_struct *handle, { int fd; struct fruit_config_data *config = NULL; - struct fio *fio = NULL; DBG_DEBUG("path [%s]\n", smb_fname_str_dbg(smb_fname)); @@ -3554,14 +3541,6 @@ static int fruit_open_meta(vfs_handle_struct *handle, DBG_DEBUG("path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd); - if (fd == -1) { - return -1; - } - - fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL); - fio->type = ADOUBLE_META; - fio->config = config; - return fd; } -- 2.17.2 From c735706ce23d33da0aaecb539ae2699616451527 Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Thu, 1 Nov 2018 12:09:14 +0100 Subject: [PATCH 35/35] torture: Fix the clang build It's used uninitialized if an early torture_assert fails Bug: https://bugzilla.samba.org/show_bug.cgi?id=13646 Signed-off-by: Volker Lendecke Reviewed-by: Ralph Boehme Autobuild-User(master): Volker Lendecke Autobuild-Date(master): Thu Nov 1 17:34:31 CET 2018 on sn-devel-144 (cherry picked from commit 9b28d47b0d86570be5a7c5628e460e01207afb00) --- source4/torture/vfs/fruit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source4/torture/vfs/fruit.c b/source4/torture/vfs/fruit.c index 66cd0b6d4b9..1232aaec92a 100644 --- a/source4/torture/vfs/fruit.c +++ b/source4/torture/vfs/fruit.c @@ -5461,7 +5461,7 @@ static bool test_empty_stream_do_one( { bool ret = false; NTSTATUS status; - struct smb2_handle baseh; + struct smb2_handle baseh = {{0}}; struct smb2_handle streamh; struct smb2_create create; union smb_setfileinfo sfinfo; -- 2.17.2