From b5d4d81a0076cbbc58780987f340de095da5e76b Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Sun, 7 Oct 2018 18:26:47 +0200 Subject: [PATCH 01/65] s4:torture: FinderInfo conversion test with AppleDouble without xattr data This testcase demonstrates that the AppleDouble conversion in vfs_fruit doesn't correctly convert the FinderInfo data from the AppleDouble file to a stream. Bug: https://bugzilla.samba.org/show_bug.cgi?id=13649 Signed-off-by: Ralph Boehme (cherry picked from commit 65250acce46fa8caa1abb2db8cccf4c31d6616a4) --- selftest/knownfail.d/samba3.vfs.fruit | 3 + source4/torture/vfs/fruit.c | 258 ++++++++++++++++++++++++++ 2 files changed, 261 insertions(+) diff --git a/selftest/knownfail.d/samba3.vfs.fruit b/selftest/knownfail.d/samba3.vfs.fruit index 8df25bccb79..bc46c2f4922 100644 --- a/selftest/knownfail.d/samba3.vfs.fruit +++ b/selftest/knownfail.d/samba3.vfs.fruit @@ -1 +1,4 @@ ^samba3.vfs.fruit streams_depot.OS X AppleDouble file conversion\(nt4_dc\) +^samba3.vfs.fruit metadata_netatalk.OS X AppleDouble file conversion without embedded xattr\(nt4_dc\) +^samba3.vfs.fruit metadata_stream.OS X AppleDouble file conversion without embedded xattr\(nt4_dc\) +^samba3.vfs.fruit streams_depot.OS X AppleDouble file conversion without embedded xattr\(nt4_dc\) diff --git a/source4/torture/vfs/fruit.c b/source4/torture/vfs/fruit.c index ab9bbf4ed22..3e95b745944 100644 --- a/source4/torture/vfs/fruit.c +++ b/source4/torture/vfs/fruit.c @@ -886,6 +886,147 @@ static char osx_adouble_w_xattr[] = { 0x00, 0x00, 0x00, 0x1c, 0x00, 0x1e, 0xff, 0xff }; +/* + * The buf below contains the following AppleDouble encoded data: + * + * ------------------------------------------------------------------------------- + * MagicNumber: 00051607 : AppleDouble + * Version : 00020000 : Version 2 + * Filler : 4D 61 63 20 4F 53 20 58 20 20 20 20 20 20 20 20 : Mac OS X + * Num. of ent: 0002 : 2 + * + * ------------------------------------------------------------------------------- + * Entry ID : 00000002 : Resource Fork + * Offset : 00000052 : 82 + * Length : 0000011E : 286 + * + * -RAW DUMP--: 0 1 2 3 4 5 6 7 8 9 A B C D E F : (ASCII) + * 00000000 : 00 00 01 00 00 00 01 00 00 00 00 00 00 00 00 1E : ................ + * 00000010 : 54 68 69 73 20 72 65 73 6F 75 72 63 65 20 66 6F : This resource fo + * 00000020 : 72 6B 20 69 6E 74 65 6E 74 69 6F 6E 61 6C 6C 79 : rk intentionally + * 00000030 : 20 6C 65 66 74 20 62 6C 61 6E 6B 20 20 20 00 00 : left blank .. + * 00000040 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ + * 00000050 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ + * 00000060 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ + * 00000070 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ + * 00000080 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ + * 00000090 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ + * 000000A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ + * 000000B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ + * 000000C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ + * 000000D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ + * 000000E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ + * 000000F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ + * 00000100 : 00 00 01 00 00 00 01 00 00 00 00 00 00 00 00 1E : ................ + * 00000110 : 00 00 00 00 00 00 00 00 00 1C 00 1E FF FF : .............. + * + * Entry ID : 00000009 : Finder Info + * Offset : 00000032 : 50 + * Length : 00000020 : 32 + * + * -NOTE------: cannot detect whether FInfo or DInfo. assume FInfo. + * + * -FInfo-----: + * Type : 57415645 : WAVE + * Creator : 5054756C : PTul + * isAlias : 0 + * Invisible : 0 + * hasBundle : 0 + * nameLocked : 0 + * Stationery : 0 + * CustomIcon : 0 + * Reserved : 0 + * Inited : 0 + * NoINITS : 0 + * Shared : 0 + * SwitchLaunc: 0 + * Hidden Ext : 0 + * color : 000 : none + * isOnDesk : 0 + * Location v : 0000 : 0 + * Location h : 0000 : 0 + * Fldr : 0000 : .. + * + * -FXInfo----: + * Rsvd|IconID: 0000 : 0 + * Rsvd : 0000 : .. + * Rsvd : 0000 : .. + * Rsvd : 0000 : .. + * AreInvalid : 0 + * unknown bit: 0 + * unknown bit: 0 + * unknown bit: 0 + * unknown bit: 0 + * unknown bit: 0 + * unknown bit: 0 + * CustomBadge: 0 + * ObjctIsBusy: 0 + * unknown bit: 0 + * unknown bit: 0 + * unknown bit: 0 + * unknown bit: 0 + * RoutingInfo: 0 + * unknown bit: 0 + * unknown bit: 0 + * Rsvd|commnt: 0000 : 0 + * PutAway : 00000000 : 0 + * + * -RAW DUMP--: 0 1 2 3 4 5 6 7 8 9 A B C D E F : (ASCII) + * 00000000 : 57 41 56 45 50 54 75 6C 00 00 00 00 00 00 00 00 : WAVEPTul........ + * 00000010 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ + * * + * It was created with: + * $ hexdump -ve '"\t" 7/1 "0x%02x, " 1/1 " 0x%02x," "\n"' + */ +static char osx_adouble_without_xattr[] = { + 0x00, 0x05, 0x16, 0x07, 0x00, 0x02, 0x00, 0x00, + 0x4d, 0x61, 0x63, 0x20, 0x4f, 0x53, 0x20, 0x58, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x52, 0x00, 0x00, 0x01, 0x1e, 0x00, 0x00, + 0x00, 0x09, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, + 0x00, 0x20, 0x57, 0x41, 0x56, 0x45, 0x50, 0x54, + 0x75, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1e, 0x54, 0x68, 0x69, 0x73, 0x20, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, + 0x66, 0x6f, 0x72, 0x6b, 0x20, 0x69, 0x6e, 0x74, + 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, + 0x6c, 0x79, 0x20, 0x6c, 0x65, 0x66, 0x74, 0x20, + 0x62, 0x6c, 0x61, 0x6e, 0x6b, 0x20, 0x20, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x1c, 0x00, 0x1e, 0xff, 0xff +}; + /** * talloc and intialize an AfpInfo **/ @@ -2021,6 +2162,122 @@ static bool test_adouble_conversion(struct torture_context *tctx, return ret; } +/* + * Test conversion of AppleDouble file without embedded xattr data + */ +static bool test_adouble_conversion_wo_xattr(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + const char *fname = BASEDIR "\\test_adouble_conversion"; + const char *adname = BASEDIR "/._test_adouble_conversion"; + NTSTATUS status; + struct smb2_handle testdirh; + bool ret = true; + const char *streams[] = { + "::$DATA", + AFPINFO_STREAM, + AFPRESOURCE_STREAM + }; + struct smb2_create create; + struct smb2_find find; + unsigned int count; + union smb_search_data *d; + const char *data = "This resource fork intentionally left blank"; + size_t datalen = strlen(data); + + smb2_deltree(tree, BASEDIR); + + status = torture_smb2_testdir(tree, BASEDIR, &testdirh); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testdir failed\n"); + smb2_util_close(tree, testdirh); + + ret = torture_setup_file(tctx, tree, fname, false); + torture_assert_goto(tctx, ret == true, ret, done, + "torture_setup_file failed\n"); + + ret = torture_setup_file(tctx, tree, adname, false); + torture_assert_goto(tctx, ret == true, ret, done, + "torture_setup_file failed\n"); + + ret = write_stream(tree, __location__, tctx, mem_ctx, + adname, NULL, 0, + sizeof(osx_adouble_without_xattr), + osx_adouble_without_xattr); + torture_assert_goto(tctx, ret == true, ret, done, + "write_stream failed\n"); + + ret = enable_aapl(tctx, tree); + torture_assert_goto(tctx, ret == true, ret, done, "enable_aapl failed"); + + /* + * Issue a smb2_find(), this triggers the server-side conversion + */ + + create = (struct smb2_create) { + .in.desired_access = SEC_RIGHTS_DIR_READ, + .in.create_options = NTCREATEX_OPTIONS_DIRECTORY, + .in.file_attributes = FILE_ATTRIBUTE_DIRECTORY, + .in.share_access = NTCREATEX_SHARE_ACCESS_READ, + .in.create_disposition = NTCREATEX_DISP_OPEN, + .in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS, + .in.fname = BASEDIR, + }; + + status = smb2_create(tree, tctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed\n"); + + find = (struct smb2_find) { + .in.file.handle = create.out.file.handle, + .in.pattern = "*", + .in.max_response_size = 0x1000, + .in.level = SMB2_FIND_ID_BOTH_DIRECTORY_INFO, + }; + + status = smb2_find_level(tree, tree, &find, &count, &d); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_find_level failed\n"); + + status = smb2_util_close(tree, create.out.file.handle); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_close failed"); + + /* + * Check number of streams + */ + + ret = check_stream_list(tree, tctx, fname, 3, streams, false); + torture_assert_goto(tctx, ret == true, ret, done, "check_stream_list"); + + + /* + * Check Resourcefork data can be read. + */ + + ret = check_stream(tree, __location__, tctx, mem_ctx, + fname, AFPRESOURCE_STREAM, + 16, datalen, 0, datalen, data); + torture_assert_goto(tctx, ret == true, ret, done, + "check AFPRESOURCE_STREAM failed\n"); + + /* + * Check FinderInfo data has been migrated to stream. + */ + + ret = check_stream(tree, __location__, tctx, mem_ctx, + fname, AFPINFO_STREAM, + 0, 60, 16, 8, "WAVEPTul"); + torture_assert_goto(tctx, ret == true, ret, done, + "check AFPINFO_STREAM failed\n"); + +done: + smb2_deltree(tree, BASEDIR); + talloc_free(mem_ctx); + return ret; +} + static bool test_aapl(struct torture_context *tctx, struct smb2_tree *tree) { @@ -4861,6 +5118,7 @@ struct torture_suite *torture_vfs_fruit(TALLOC_CTX *ctx) torture_suite_add_1smb2_test(suite, "copy-chunk streams", test_copy_chunk_streams); 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); return suite; } -- 2.17.2 From 40a320387c2c356067682d8ddbe5be2b516a9c51 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Tue, 11 Sep 2018 14:05:43 +0200 Subject: [PATCH 02/65] vfs_fruit: fix two comments Thanks to the recent addition of ad_convert_xattr() we now correctly handle this case. Bug: https://bugzilla.samba.org/show_bug.cgi?id=13649 Signed-off-by: Ralph Boehme (cherry picked from commit 5c3f7c615e91f86a9732b89b914e426adf1b47e4) --- source3/modules/vfs_fruit.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c index 78d3720a9e7..4ad41da46a3 100644 --- a/source3/modules/vfs_fruit.c +++ b/source3/modules/vfs_fruit.c @@ -1055,8 +1055,7 @@ static bool ad_convert_xattr(struct adouble *ad, * Convert from Apple's ._ file to Netatalk * * Apple's AppleDouble may contain a FinderInfo entry longer then 32 - * bytes containing packed xattrs. Netatalk can't deal with that, so - * we simply discard the packed xattrs. + * bytes containing packed xattrs. * * @return -1 in case an error occurred, 0 if no conversion was done, 1 * otherwise @@ -1374,9 +1373,7 @@ static ssize_t ad_read_rsrc_adouble(struct adouble *ad, /* * Try to fixup AppleDouble files created by OS X with xattrs - * appended to the ADEID_FINDERI entry. We simply remove the - * xattrs blob, this means any fancy xattr that was stored - * there is lost. + * appended to the ADEID_FINDERI entry. */ ret = ad_convert(ad, smb_fname, ad->ad_fd); -- 2.17.2 From ad381fb03c6d99d9280936992af3d8c444773d58 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Fri, 5 Oct 2018 15:12:44 +0200 Subject: [PATCH 03/65] vfs_fruit: store filler bytes from AppleDouble file header in struct adouble This can later be used to distinguish between macOS created AppleDouble files and AppleDouble files created by Samba or Netatalk. macOS: "Mac OS X " Samba: "Netatalk " Netatalk: "Netatalk " Bug: https://bugzilla.samba.org/show_bug.cgi?id=13649 Signed-off-by: Ralph Boehme (cherry picked from commit c4813e5e0ead77c55d5cadd1d6dee7ce54a3662e) --- source3/modules/vfs_fruit.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c index 4ad41da46a3..45937969d4e 100644 --- a/source3/modules/vfs_fruit.c +++ b/source3/modules/vfs_fruit.c @@ -262,6 +262,7 @@ typedef enum {ADOUBLE_META, ADOUBLE_RSRC} adouble_type_t; #define ADEDLEN_VERSION 4 #define ADEDLEN_FILLER 16 #define AD_FILLER_TAG "Netatalk " /* should be 16 bytes */ +#define AD_FILLER_TAG_OSX "Mac OS X " /* should be 16 bytes */ #define ADEDLEN_NENTRIES 2 #define AD_HEADER_LEN (ADEDLEN_MAGIC + ADEDLEN_VERSION + \ ADEDLEN_FILLER + ADEDLEN_NENTRIES) /* 26 */ @@ -414,6 +415,7 @@ struct adouble { adouble_type_t ad_type; uint32_t ad_magic; uint32_t ad_version; + uint8_t ad_filler[ADEDLEN_FILLER]; struct ad_entry ad_eid[ADEID_MAX]; char *ad_data; struct ad_xattr_header adx_header; @@ -837,6 +839,8 @@ static bool ad_unpack(struct adouble *ad, const size_t nentries, return false; } + memcpy(ad->ad_filler, ad->ad_data + ADEDOFF_FILLER, ADEDLEN_FILLER); + adentries = RSVAL(ad->ad_data, ADEDOFF_NENTRIES); if (adentries != nentries) { DEBUG(1, ("invalid number of entries: %zu\n", -- 2.17.2 From 42ca2d79c467e400c4ceae408946cbf43727b829 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Tue, 2 Oct 2018 14:51:05 +0200 Subject: [PATCH 04/65] vfs_fruit: move setting ADEID_FINDERI length to ad_convert_xattr() ad_convert_xattr() does the conversion of the xattr data in the AppleDouble file, so we should update it's size there and should not defer it to the caller. Bug: https://bugzilla.samba.org/show_bug.cgi?id=13649 Signed-off-by: Ralph Boehme (cherry picked from commit 40dd4d79cea164f90a9d874b9787ba46f2c5476c) --- source3/modules/vfs_fruit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c index 45937969d4e..493bd7511a4 100644 --- a/source3/modules/vfs_fruit.c +++ b/source3/modules/vfs_fruit.c @@ -1052,6 +1052,7 @@ static bool ad_convert_xattr(struct adouble *ad, fsp = NULL; } + ad_setentrylen(ad, ADEID_FINDERI, ADEDLEN_FINDERI); return true; } @@ -1096,7 +1097,6 @@ static int ad_convert(struct adouble *ad, ad_getentrylen(ad, ADEID_RFORK)); } - ad_setentrylen(ad, ADEID_FINDERI, ADEDLEN_FINDERI); ad_setentryoff(ad, ADEID_RFORK, ad_getentryoff(ad, ADEID_FINDERI) + ADEDLEN_FINDERI); -- 2.17.2 From 22b597534d64dab3c418df82428f0fa2869377c6 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Thu, 4 Oct 2018 08:23:59 +0200 Subject: [PATCH 05/65] vfs_fruit: do direct return from error checks in ad_convert() Subsequent commits will move the mmap() into the subfunctions. This change just prepares for that. Bug: https://bugzilla.samba.org/show_bug.cgi?id=13649 Signed-off-by: Ralph Boehme (cherry picked from commit 163ffc54eddde63fe388fbeff7343eb4584349bc) --- source3/modules/vfs_fruit.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c index 493bd7511a4..87329405e71 100644 --- a/source3/modules/vfs_fruit.c +++ b/source3/modules/vfs_fruit.c @@ -1081,8 +1081,7 @@ static int ad_convert(struct adouble *ad, map = mmap(NULL, origlen, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); if (map == MAP_FAILED) { DEBUG(2, ("mmap AppleDouble: %s\n", strerror(errno))); - rc = -1; - goto exit; + return -1; } ok = ad_convert_xattr(ad, smb_fname, map); @@ -1106,12 +1105,18 @@ static int ad_convert(struct adouble *ad, */ rc = ftruncate(fd, ad_getentryoff(ad, ADEID_RFORK) + ad_getentrylen(ad, ADEID_RFORK)); - -exit: - if (map != MAP_FAILED) { + if (rc != 0) { munmap(map, origlen); + return -1; } - return rc; + + rc = munmap(map, origlen); + if (rc != 0) { + DBG_ERR("munmap failed: %s\n", strerror(errno)); + return -1; + } + + return 0; } /** -- 2.17.2 From b836da44cbec900336429529467e52d4e2adedf4 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Thu, 4 Oct 2018 08:51:28 +0200 Subject: [PATCH 06/65] vfs_fruit: remove unneeded fd argument from ad_convert() Use the struct adouble member ad_fd instead of passing it as an argument. Who did that in the first place? :) Bug: https://bugzilla.samba.org/show_bug.cgi?id=13649 Signed-off-by: Ralph Boehme (cherry picked from commit 13f2062cedd4c14942b773744667e0961904cf74) --- source3/modules/vfs_fruit.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c index 87329405e71..ab85739daa2 100644 --- a/source3/modules/vfs_fruit.c +++ b/source3/modules/vfs_fruit.c @@ -1066,8 +1066,7 @@ static bool ad_convert_xattr(struct adouble *ad, * otherwise **/ static int ad_convert(struct adouble *ad, - const struct smb_filename *smb_fname, - int fd) + const struct smb_filename *smb_fname) { int rc = 0; char *map = MAP_FAILED; @@ -1078,7 +1077,8 @@ static int ad_convert(struct adouble *ad, ad_getentrylen(ad, ADEID_RFORK); /* FIXME: direct use of mmap(), vfs_aio_fork does it too */ - map = mmap(NULL, origlen, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + map = mmap(NULL, origlen, PROT_READ|PROT_WRITE, MAP_SHARED, + ad->ad_fd, 0); if (map == MAP_FAILED) { DEBUG(2, ("mmap AppleDouble: %s\n", strerror(errno))); return -1; @@ -1103,7 +1103,7 @@ static int ad_convert(struct adouble *ad, * FIXME: direct ftruncate(), but we don't have a fsp for the * VFS call */ - rc = ftruncate(fd, ad_getentryoff(ad, ADEID_RFORK) + rc = ftruncate(ad->ad_fd, ad_getentryoff(ad, ADEID_RFORK) + ad_getentrylen(ad, ADEID_RFORK)); if (rc != 0) { munmap(map, origlen); @@ -1385,7 +1385,7 @@ static ssize_t ad_read_rsrc_adouble(struct adouble *ad, * appended to the ADEID_FINDERI entry. */ - ret = ad_convert(ad, smb_fname, ad->ad_fd); + ret = ad_convert(ad, smb_fname); if (ret != 0) { DBG_WARNING("Failed to convert [%s]\n", smb_fname->base_name); return len; -- 2.17.2 From 16bda6ae6e70a4f1a35dfec97ea71a148d76235c Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Fri, 5 Oct 2018 16:14:40 +0200 Subject: [PATCH 07/65] vfs_fruit: move storing of modified struct adouble to ad_convert() ad_convert() modified it, so let ad_convert() also save it to disk. No change in behaviour. Bug: https://bugzilla.samba.org/show_bug.cgi?id=13649 Signed-off-by: Ralph Boehme (cherry picked from commit 23e6d3972af7146ecc95875c6811ffeab9274f43) --- source3/modules/vfs_fruit.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c index ab85739daa2..62cd60c9a9c 100644 --- a/source3/modules/vfs_fruit.c +++ b/source3/modules/vfs_fruit.c @@ -1071,6 +1071,7 @@ static int ad_convert(struct adouble *ad, int rc = 0; char *map = MAP_FAILED; size_t origlen; + ssize_t len; bool ok; origlen = ad_getentryoff(ad, ADEID_RFORK) + @@ -1116,6 +1117,18 @@ static int ad_convert(struct adouble *ad, return -1; } + ok = ad_pack(ad); + if (!ok) { + DBG_WARNING("ad_pack [%s] failed\n", smb_fname->base_name); + return -1; + } + + len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0); + if (len != AD_DATASZ_DOT_UND) { + DBG_ERR("%s: bad size: %zd\n", smb_fname->base_name, len); + return -1; + } + return 0; } @@ -1391,18 +1404,6 @@ static ssize_t ad_read_rsrc_adouble(struct adouble *ad, return len; } - ok = ad_pack(ad); - if (!ok) { - DBG_WARNING("ad_pack [%s] failed\n", smb_fname->base_name); - return -1; - } - - len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0); - if (len != AD_DATASZ_DOT_UND) { - DBG_ERR("%s: bad size: %zd\n", smb_fname->base_name, len); - return -1; - } - p_ad = ad_get_entry(ad, ADEID_FINDERI); if (p_ad == NULL) { return -1; -- 2.17.2 From a2bbfaafa52fb8de5afb3af240ef9bd54b737648 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Fri, 5 Oct 2018 16:25:27 +0200 Subject: [PATCH 08/65] vfs_fruit: move FinderInfo conversion to helper function and call it from ad_convert() No change in behaviour. Bug: https://bugzilla.samba.org/show_bug.cgi?id=13649 Signed-off-by: Ralph Boehme (cherry picked from commit b8addd5bfbeabee4f586dbdcbdf841881e3bf131) --- source3/modules/vfs_fruit.c | 192 +++++++++++++++++++----------------- 1 file changed, 104 insertions(+), 88 deletions(-) diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c index 62cd60c9a9c..3acd43698d0 100644 --- a/source3/modules/vfs_fruit.c +++ b/source3/modules/vfs_fruit.c @@ -1056,6 +1056,102 @@ static bool ad_convert_xattr(struct adouble *ad, return true; } +static bool ad_convert_finderinfo(struct adouble *ad, + const struct smb_filename *smb_fname) +{ + char *p_ad = NULL; + AfpInfo *ai = NULL; + DATA_BLOB aiblob; + struct smb_filename *stream_name = NULL; + files_struct *fsp = NULL; + size_t size; + ssize_t nwritten; + NTSTATUS status; + int saved_errno = 0; + + p_ad = ad_get_entry(ad, ADEID_FINDERI); + if (p_ad == NULL) { + return false; + } + + ai = afpinfo_new(talloc_tos()); + if (ai == NULL) { + return false; + } + + memcpy(ai->afpi_FinderInfo, p_ad, ADEDLEN_FINDERI); + + aiblob = data_blob_talloc(talloc_tos(), NULL, AFP_INFO_SIZE); + if (aiblob.data == NULL) { + TALLOC_FREE(ai); + return false; + } + + size = afpinfo_pack(ai, (char *)aiblob.data); + TALLOC_FREE(ai); + if (size != AFP_INFO_SIZE) { + return false; + } + + stream_name = synthetic_smb_fname(talloc_tos(), + smb_fname->base_name, + AFPINFO_STREAM, + NULL, + smb_fname->flags); + if (stream_name == NULL) { + data_blob_free(&aiblob); + DBG_ERR("synthetic_smb_fname failed\n"); + return false; + } + + DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name)); + + status = SMB_VFS_CREATE_FILE( + ad->ad_handle->conn, /* conn */ + NULL, /* req */ + 0, /* root_dir_fid */ + stream_name, /* fname */ + FILE_GENERIC_WRITE, /* access_mask */ + FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */ + FILE_OPEN_IF, /* create_disposition */ + 0, /* create_options */ + 0, /* file_attributes */ + INTERNAL_OPEN_ONLY, /* oplock_request */ + NULL, /* lease */ + 0, /* allocation_size */ + 0, /* private_flags */ + NULL, /* sd */ + NULL, /* ea_list */ + &fsp, /* result */ + NULL, /* psbuf */ + NULL, NULL); /* create context */ + TALLOC_FREE(stream_name); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("SMB_VFS_CREATE_FILE failed\n"); + return false; + } + + nwritten = SMB_VFS_PWRITE(fsp, + aiblob.data, + aiblob.length, + 0); + if (nwritten == -1) { + DBG_ERR("SMB_VFS_PWRITE failed\n"); + saved_errno = errno; + close_file(NULL, fsp, ERROR_CLOSE); + errno = saved_errno; + return false; + } + + status = close_file(NULL, fsp, NORMAL_CLOSE); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + fsp = NULL; + + return true; +} + /** * Convert from Apple's ._ file to Netatalk * @@ -1129,6 +1225,13 @@ static int ad_convert(struct adouble *ad, return -1; } + ok = ad_convert_finderinfo(ad, smb_fname); + if (!ok) { + DBG_ERR("Failed to convert [%s]\n", + smb_fname_str_dbg(smb_fname)); + return -1; + } + return 0; } @@ -1325,15 +1428,8 @@ static ssize_t ad_read_rsrc_adouble(struct adouble *ad, { SMB_STRUCT_STAT sbuf; char *p_ad = NULL; - AfpInfo *ai = NULL; - DATA_BLOB aiblob; - struct smb_filename *stream_name = NULL; - files_struct *fsp = NULL; - ssize_t len; size_t size; - ssize_t nwritten; - NTSTATUS status; - int saved_errno = 0; + ssize_t len; int ret; bool ok; @@ -1404,86 +1500,6 @@ static ssize_t ad_read_rsrc_adouble(struct adouble *ad, return len; } - p_ad = ad_get_entry(ad, ADEID_FINDERI); - if (p_ad == NULL) { - return -1; - } - - ai = afpinfo_new(talloc_tos()); - if (ai == NULL) { - return -1; - } - - memcpy(ai->afpi_FinderInfo, p_ad, ADEDLEN_FINDERI); - - aiblob = data_blob_talloc(talloc_tos(), NULL, AFP_INFO_SIZE); - if (aiblob.data == NULL) { - TALLOC_FREE(ai); - return -1; - } - - size = afpinfo_pack(ai, (char *)aiblob.data); - TALLOC_FREE(ai); - if (size != AFP_INFO_SIZE) { - return -1; - } - - stream_name = synthetic_smb_fname(talloc_tos(), - smb_fname->base_name, - AFPINFO_STREAM, - NULL, - smb_fname->flags); - if (stream_name == NULL) { - data_blob_free(&aiblob); - DBG_ERR("synthetic_smb_fname failed\n"); - return -1; - } - - DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name)); - - status = SMB_VFS_CREATE_FILE( - ad->ad_handle->conn, /* conn */ - NULL, /* req */ - 0, /* root_dir_fid */ - stream_name, /* fname */ - FILE_GENERIC_WRITE, /* access_mask */ - FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */ - FILE_OPEN_IF, /* create_disposition */ - 0, /* create_options */ - 0, /* file_attributes */ - INTERNAL_OPEN_ONLY, /* oplock_request */ - NULL, /* lease */ - 0, /* allocation_size */ - 0, /* private_flags */ - NULL, /* sd */ - NULL, /* ea_list */ - &fsp, /* result */ - NULL, /* psbuf */ - NULL, NULL); /* create context */ - TALLOC_FREE(stream_name); - if (!NT_STATUS_IS_OK(status)) { - DBG_ERR("SMB_VFS_CREATE_FILE failed\n"); - return -1; - } - - nwritten = SMB_VFS_PWRITE(fsp, - aiblob.data, - aiblob.length, - 0); - if (nwritten == -1) { - DBG_ERR("SMB_VFS_PWRITE failed\n"); - saved_errno = errno; - close_file(NULL, fsp, ERROR_CLOSE); - errno = saved_errno; - return -1; - } - - status = close_file(NULL, fsp, NORMAL_CLOSE); - if (!NT_STATUS_IS_OK(status)) { - return -1; - } - fsp = NULL; - return len; } -- 2.17.2 From e78c24f0b784c9d57ca9ff32b68f6d72e1ce611a Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Fri, 5 Oct 2018 16:26:46 +0200 Subject: [PATCH 09/65] vfs_fruit: move FinderInfo lenght check to ad_convert() The final step in consolidating all conversion related work in ad_convert(). No change in behaviour. Bug: https://bugzilla.samba.org/show_bug.cgi?id=13649 Signed-off-by: Ralph Boehme (cherry picked from commit bed21cdab7b7a088820d0f146f11d5208968bc37) --- source3/modules/vfs_fruit.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c index 3acd43698d0..aa49422ee67 100644 --- a/source3/modules/vfs_fruit.c +++ b/source3/modules/vfs_fruit.c @@ -1170,6 +1170,10 @@ static int ad_convert(struct adouble *ad, ssize_t len; bool ok; + if (ad_getentrylen(ad, ADEID_FINDERI) == ADEDLEN_FINDERI) { + return 0; + } + origlen = ad_getentryoff(ad, ADEID_RFORK) + ad_getentrylen(ad, ADEID_RFORK); @@ -1485,10 +1489,6 @@ static ssize_t ad_read_rsrc_adouble(struct adouble *ad, return -1; } - if (ad_getentrylen(ad, ADEID_FINDERI) == ADEDLEN_FINDERI) { - return len; - } - /* * Try to fixup AppleDouble files created by OS X with xattrs * appended to the ADEID_FINDERI entry. -- 2.17.2 From 681ff4f8e82ce71de675348a8e62d6fe0b91bae6 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Fri, 5 Oct 2018 19:13:16 +0200 Subject: [PATCH 10/65] vfs_fruit: split out truncating from ad_convert() This may look a little ill-advised as this increases line count, but the goal here is modularizing ad_convert() itself and making it as slick as possible helps achieving that goal. Bug: https://bugzilla.samba.org/show_bug.cgi?id=13649 Signed-off-by: Ralph Boehme (cherry picked from commit 778ffde8e1167397de3639ee8cb150a43788a56a) --- source3/modules/vfs_fruit.c | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c index aa49422ee67..a77f365f5ae 100644 --- a/source3/modules/vfs_fruit.c +++ b/source3/modules/vfs_fruit.c @@ -1152,6 +1152,24 @@ static bool ad_convert_finderinfo(struct adouble *ad, return true; } +static bool ad_convert_truncate(struct adouble *ad, + const struct smb_filename *smb_fname) +{ + int rc; + + /* + * FIXME: direct ftruncate(), but we don't have a fsp for the + * VFS call + */ + rc = ftruncate(ad->ad_fd, ad_getentryoff(ad, ADEID_RFORK) + + ad_getentrylen(ad, ADEID_RFORK)); + if (rc != 0) { + return false; + } + + return true; +} + /** * Convert from Apple's ._ file to Netatalk * @@ -1200,13 +1218,8 @@ static int ad_convert(struct adouble *ad, ad_setentryoff(ad, ADEID_RFORK, ad_getentryoff(ad, ADEID_FINDERI) + ADEDLEN_FINDERI); - /* - * FIXME: direct ftruncate(), but we don't have a fsp for the - * VFS call - */ - rc = ftruncate(ad->ad_fd, ad_getentryoff(ad, ADEID_RFORK) - + ad_getentrylen(ad, ADEID_RFORK)); - if (rc != 0) { + ok = ad_convert_truncate(ad, smb_fname); + if (!ok) { munmap(map, origlen); return -1; } -- 2.17.2 From 5362a934d558db35473f1a639e7005af9fcf9a90 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Fri, 5 Oct 2018 19:15:04 +0200 Subject: [PATCH 11/65] vfs_fruit: use ADEDOFF_RFORK_DOT_UND offset macro in ad_convert_truncate() We really want the fixed size offset here, not a calculated one. Note that "ad_getentryoff(ad, ADEID_RFORK)" is equal to ADEDOFF_RFORK_DOT_UND in this case. Bug: https://bugzilla.samba.org/show_bug.cgi?id=13649 Signed-off-by: Ralph Boehme (cherry picked from commit 262d6b3d9ce697ea3fbdd8cc54c2a6ef5b1da773) --- source3/modules/vfs_fruit.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c index a77f365f5ae..d606be3742d 100644 --- a/source3/modules/vfs_fruit.c +++ b/source3/modules/vfs_fruit.c @@ -1161,8 +1161,8 @@ static bool ad_convert_truncate(struct adouble *ad, * FIXME: direct ftruncate(), but we don't have a fsp for the * VFS call */ - rc = ftruncate(ad->ad_fd, ad_getentryoff(ad, ADEID_RFORK) - + ad_getentrylen(ad, ADEID_RFORK)); + rc = ftruncate(ad->ad_fd, ADEDOFF_RFORK_DOT_UND + + ad_getentrylen(ad, ADEID_RFORK)); if (rc != 0) { return false; } -- 2.17.2 From 92880e31c5a60adb51f7bb610a90f08285672c00 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Fri, 5 Oct 2018 16:44:53 +0200 Subject: [PATCH 12/65] vfs_fruit: split out moving of the resource fork No change in behaviour. Bug: https://bugzilla.samba.org/show_bug.cgi?id=13649 Signed-off-by: Ralph Boehme (cherry picked from commit b1096d24d2811a4686310dde974a8e799e5342be) --- source3/modules/vfs_fruit.c | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c index d606be3742d..2ea5d53b1fd 100644 --- a/source3/modules/vfs_fruit.c +++ b/source3/modules/vfs_fruit.c @@ -1170,6 +1170,24 @@ static bool ad_convert_truncate(struct adouble *ad, return true; } +static bool ad_convert_move_reso(struct adouble *ad, + const struct smb_filename *smb_fname, + char *map) +{ + if (ad_getentrylen(ad, ADEID_RFORK) == 0) { + return true; + } + + memmove(map + ad_getentryoff(ad, ADEID_FINDERI) + ADEDLEN_FINDERI, + map + ad_getentryoff(ad, ADEID_RFORK), + ad_getentrylen(ad, ADEID_RFORK)); + + ad_setentryoff(ad, ADEID_RFORK, + ad_getentryoff(ad, ADEID_FINDERI) + ADEDLEN_FINDERI); + + return true; +} + /** * Convert from Apple's ._ file to Netatalk * @@ -1209,15 +1227,12 @@ static int ad_convert(struct adouble *ad, return -1; } - if (ad_getentrylen(ad, ADEID_RFORK) > 0) { - memmove(map + ad_getentryoff(ad, ADEID_FINDERI) + ADEDLEN_FINDERI, - map + ad_getentryoff(ad, ADEID_RFORK), - ad_getentrylen(ad, ADEID_RFORK)); + ok = ad_convert_move_reso(ad, smb_fname, map); + if (!ok) { + munmap(map, origlen); + return -1; } - ad_setentryoff(ad, ADEID_RFORK, - ad_getentryoff(ad, ADEID_FINDERI) + ADEDLEN_FINDERI); - ok = ad_convert_truncate(ad, smb_fname); if (!ok) { munmap(map, origlen); -- 2.17.2 From 276e07daebdc1c6f45e662b1bb5ccceaa7743daa Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Fri, 5 Oct 2018 19:15:04 +0200 Subject: [PATCH 13/65] vfs_fruit: use ADEDOFF_RFORK_DOT_UND offset macro in ad_convert_move_reso() We really want the fixed size offset here, not a calculated one. Note that "ad_getentryoff(ad, ADEID_FINDERI) + ADEDLEN_FINDERI" is equal to ADEDOFF_RFORK_DOT_UND. Bug: https://bugzilla.samba.org/show_bug.cgi?id=13649 Signed-off-by: Ralph Boehme (cherry picked from commit aa160ecd240363f133cb8eb76589463c9e233a32) --- source3/modules/vfs_fruit.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c index 2ea5d53b1fd..94da6eb7e84 100644 --- a/source3/modules/vfs_fruit.c +++ b/source3/modules/vfs_fruit.c @@ -1178,12 +1178,11 @@ static bool ad_convert_move_reso(struct adouble *ad, return true; } - memmove(map + ad_getentryoff(ad, ADEID_FINDERI) + ADEDLEN_FINDERI, + memmove(map + ADEDOFF_RFORK_DOT_UND, map + ad_getentryoff(ad, ADEID_RFORK), ad_getentrylen(ad, ADEID_RFORK)); - ad_setentryoff(ad, ADEID_RFORK, - ad_getentryoff(ad, ADEID_FINDERI) + ADEDLEN_FINDERI); + ad_setentryoff(ad, ADEID_RFORK, ADEDOFF_RFORK_DOT_UND); return true; } -- 2.17.2 From 2cebe5a3eaf2d01b02e9f3403d2165105f4cf1cc Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Fri, 5 Oct 2018 16:52:32 +0200 Subject: [PATCH 14/65] vfs_fruit: fix error returns in ad_convert_xattr() Bug: https://bugzilla.samba.org/show_bug.cgi?id=13649 Signed-off-by: Ralph Boehme (cherry picked from commit acc20ebfd4a4581153dc940b7057d84761135c65) --- source3/modules/vfs_fruit.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c index 94da6eb7e84..be03be24f79 100644 --- a/source3/modules/vfs_fruit.c +++ b/source3/modules/vfs_fruit.c @@ -985,14 +985,14 @@ static bool ad_convert_xattr(struct adouble *ad, !NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) { DBG_ERR("string_replace_allocate failed\n"); - return -1; + return false; } tmp = mapped_name; mapped_name = talloc_asprintf(talloc_tos(), ":%s", tmp); TALLOC_FREE(tmp); if (mapped_name == NULL) { - return -1; + return false; } stream_name = synthetic_smb_fname(talloc_tos(), @@ -1003,7 +1003,7 @@ static bool ad_convert_xattr(struct adouble *ad, TALLOC_FREE(mapped_name); if (stream_name == NULL) { DBG_ERR("synthetic_smb_fname failed\n"); - return -1; + return false; } DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name)); @@ -1030,7 +1030,7 @@ static bool ad_convert_xattr(struct adouble *ad, TALLOC_FREE(stream_name); if (!NT_STATUS_IS_OK(status)) { DBG_ERR("SMB_VFS_CREATE_FILE failed\n"); - return -1; + return false; } nwritten = SMB_VFS_PWRITE(fsp, @@ -1042,12 +1042,12 @@ static bool ad_convert_xattr(struct adouble *ad, saved_errno = errno; close_file(NULL, fsp, ERROR_CLOSE); errno = saved_errno; - return -1; + return false; } status = close_file(NULL, fsp, NORMAL_CLOSE); if (!NT_STATUS_IS_OK(status)) { - return -1; + return false; } fsp = NULL; } -- 2.17.2 From 4ab800ba8ff7723a09f6797ba6c4beecba3e566c Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Fri, 5 Oct 2018 16:59:18 +0200 Subject: [PATCH 15/65] vfs_fruit: let the ad_convert_*() subfunctions mmap as needed This may mean that we mmap twice when we convert an AppleDouble file, but this is the only sane way to cleanly modularize ad_convert(). Bug: https://bugzilla.samba.org/show_bug.cgi?id=13649 Signed-off-by: Ralph Boehme (cherry picked from commit 074293d2f22af12bcc3c384b67a542360a6d2ee5) --- source3/modules/vfs_fruit.c | 98 +++++++++++++++++++++++-------------- 1 file changed, 62 insertions(+), 36 deletions(-) diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c index be03be24f79..28f24316502 100644 --- a/source3/modules/vfs_fruit.c +++ b/source3/modules/vfs_fruit.c @@ -943,13 +943,16 @@ static bool ad_unpack(struct adouble *ad, const size_t nentries, } static bool ad_convert_xattr(struct adouble *ad, - const struct smb_filename *smb_fname, - char *map) + const struct smb_filename *smb_fname) { static struct char_mappings **string_replace_cmaps = NULL; + char *map = MAP_FAILED; + size_t maplen; uint16_t i; int saved_errno = 0; NTSTATUS status; + int rc; + bool ok; if (ad->adx_header.adx_num_attrs == 0) { return true; @@ -967,6 +970,17 @@ static bool ad_convert_xattr(struct adouble *ad, TALLOC_FREE(mappings); } + maplen = ad_getentryoff(ad, ADEID_RFORK) + + ad_getentrylen(ad, ADEID_RFORK); + + /* FIXME: direct use of mmap(), vfs_aio_fork does it too */ + map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED, + ad->ad_fd, 0); + if (map == MAP_FAILED) { + DBG_ERR("mmap AppleDouble: %s\n", strerror(errno)); + return false; + } + for (i = 0; i < ad->adx_header.adx_num_attrs; i++) { struct ad_xattr_entry *e = &ad->adx_entries[i]; char *mapped_name = NULL; @@ -985,14 +999,16 @@ static bool ad_convert_xattr(struct adouble *ad, !NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) { DBG_ERR("string_replace_allocate failed\n"); - return false; + ok = false; + goto fail; } tmp = mapped_name; mapped_name = talloc_asprintf(talloc_tos(), ":%s", tmp); TALLOC_FREE(tmp); if (mapped_name == NULL) { - return false; + ok = false; + goto fail; } stream_name = synthetic_smb_fname(talloc_tos(), @@ -1003,7 +1019,8 @@ static bool ad_convert_xattr(struct adouble *ad, TALLOC_FREE(mapped_name); if (stream_name == NULL) { DBG_ERR("synthetic_smb_fname failed\n"); - return false; + ok = false; + goto fail; } DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name)); @@ -1030,7 +1047,8 @@ static bool ad_convert_xattr(struct adouble *ad, TALLOC_FREE(stream_name); if (!NT_STATUS_IS_OK(status)) { DBG_ERR("SMB_VFS_CREATE_FILE failed\n"); - return false; + ok = false; + goto fail; } nwritten = SMB_VFS_PWRITE(fsp, @@ -1042,18 +1060,29 @@ static bool ad_convert_xattr(struct adouble *ad, saved_errno = errno; close_file(NULL, fsp, ERROR_CLOSE); errno = saved_errno; - return false; + ok = false; + goto fail; } status = close_file(NULL, fsp, NORMAL_CLOSE); if (!NT_STATUS_IS_OK(status)) { - return false; + ok = false; + goto fail; } fsp = NULL; } ad_setentrylen(ad, ADEID_FINDERI, ADEDLEN_FINDERI); - return true; + ok = true; + +fail: + rc = munmap(map, maplen); + if (rc != 0) { + DBG_ERR("munmap failed: %s\n", strerror(errno)); + return false; + } + + return ok; } static bool ad_convert_finderinfo(struct adouble *ad, @@ -1171,17 +1200,37 @@ static bool ad_convert_truncate(struct adouble *ad, } static bool ad_convert_move_reso(struct adouble *ad, - const struct smb_filename *smb_fname, - char *map) + const struct smb_filename *smb_fname) { + char *map = MAP_FAILED; + size_t maplen; + int rc; + if (ad_getentrylen(ad, ADEID_RFORK) == 0) { return true; } + maplen = ad_getentryoff(ad, ADEID_RFORK) + + ad_getentrylen(ad, ADEID_RFORK); + + /* FIXME: direct use of mmap(), vfs_aio_fork does it too */ + map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED, + ad->ad_fd, 0); + if (map == MAP_FAILED) { + DBG_ERR("mmap AppleDouble: %s\n", strerror(errno)); + return false; + } + memmove(map + ADEDOFF_RFORK_DOT_UND, map + ad_getentryoff(ad, ADEID_RFORK), ad_getentrylen(ad, ADEID_RFORK)); + rc = munmap(map, maplen); + if (rc != 0) { + DBG_ERR("munmap failed: %s\n", strerror(errno)); + return false; + } + ad_setentryoff(ad, ADEID_RFORK, ADEDOFF_RFORK_DOT_UND); return true; @@ -1199,9 +1248,6 @@ static bool ad_convert_move_reso(struct adouble *ad, static int ad_convert(struct adouble *ad, const struct smb_filename *smb_fname) { - int rc = 0; - char *map = MAP_FAILED; - size_t origlen; ssize_t len; bool ok; @@ -1209,38 +1255,18 @@ static int ad_convert(struct adouble *ad, return 0; } - origlen = ad_getentryoff(ad, ADEID_RFORK) + - ad_getentrylen(ad, ADEID_RFORK); - - /* FIXME: direct use of mmap(), vfs_aio_fork does it too */ - map = mmap(NULL, origlen, PROT_READ|PROT_WRITE, MAP_SHARED, - ad->ad_fd, 0); - if (map == MAP_FAILED) { - DEBUG(2, ("mmap AppleDouble: %s\n", strerror(errno))); - return -1; - } - - ok = ad_convert_xattr(ad, smb_fname, map); + ok = ad_convert_xattr(ad, smb_fname); if (!ok) { - munmap(map, origlen); return -1; } - ok = ad_convert_move_reso(ad, smb_fname, map); + ok = ad_convert_move_reso(ad, smb_fname); if (!ok) { - munmap(map, origlen); return -1; } ok = ad_convert_truncate(ad, smb_fname); if (!ok) { - munmap(map, origlen); - return -1; - } - - rc = munmap(map, origlen); - if (rc != 0) { - DBG_ERR("munmap failed: %s\n", strerror(errno)); return -1; } -- 2.17.2 From 45b2cd9373ceace5f0e8c576ca6d627870ece928 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Fri, 5 Oct 2018 17:07:45 +0200 Subject: [PATCH 16/65] vfs_fruit: let the ad_convert_*() subfunction update the on-disk AppleDoube header as needed Another step in simplifying ad_convert() itself. It means that we may write to disk twice, but is only ever done once per AppleDouble file. Bug: https://bugzilla.samba.org/show_bug.cgi?id=13649 Signed-off-by: Ralph Boehme (cherry picked from commit 8808bc48402c6852427af6858cf4cebc5122d485) --- source3/modules/vfs_fruit.c | 42 +++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c index 28f24316502..a519effb72a 100644 --- a/source3/modules/vfs_fruit.c +++ b/source3/modules/vfs_fruit.c @@ -949,6 +949,7 @@ static bool ad_convert_xattr(struct adouble *ad, char *map = MAP_FAILED; size_t maplen; uint16_t i; + ssize_t len; int saved_errno = 0; NTSTATUS status; int rc; @@ -1073,6 +1074,20 @@ static bool ad_convert_xattr(struct adouble *ad, } ad_setentrylen(ad, ADEID_FINDERI, ADEDLEN_FINDERI); + + ok = ad_pack(ad); + if (!ok) { + DBG_WARNING("ad_pack [%s] failed\n", smb_fname->base_name); + goto fail; + } + + len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0); + if (len != AD_DATASZ_DOT_UND) { + DBG_ERR("%s: bad size: %zd\n", smb_fname->base_name, len); + ok = false; + goto fail; + } + ok = true; fail: @@ -1204,7 +1219,9 @@ static bool ad_convert_move_reso(struct adouble *ad, { char *map = MAP_FAILED; size_t maplen; + ssize_t len; int rc; + bool ok; if (ad_getentrylen(ad, ADEID_RFORK) == 0) { return true; @@ -1233,6 +1250,18 @@ static bool ad_convert_move_reso(struct adouble *ad, ad_setentryoff(ad, ADEID_RFORK, ADEDOFF_RFORK_DOT_UND); + ok = ad_pack(ad); + if (!ok) { + DBG_WARNING("ad_pack [%s] failed\n", smb_fname->base_name); + return false; + } + + len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0); + if (len != AD_DATASZ_DOT_UND) { + DBG_ERR("%s: bad size: %zd\n", smb_fname->base_name, len); + return false; + } + return true; } @@ -1248,7 +1277,6 @@ static bool ad_convert_move_reso(struct adouble *ad, static int ad_convert(struct adouble *ad, const struct smb_filename *smb_fname) { - ssize_t len; bool ok; if (ad_getentrylen(ad, ADEID_FINDERI) == ADEDLEN_FINDERI) { @@ -1270,18 +1298,6 @@ static int ad_convert(struct adouble *ad, return -1; } - ok = ad_pack(ad); - if (!ok) { - DBG_WARNING("ad_pack [%s] failed\n", smb_fname->base_name); - return -1; - } - - len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0); - if (len != AD_DATASZ_DOT_UND) { - DBG_ERR("%s: bad size: %zd\n", smb_fname->base_name, len); - return -1; - } - ok = ad_convert_finderinfo(ad, smb_fname); if (!ok) { DBG_ERR("Failed to convert [%s]\n", -- 2.17.2 From be89e1b31f16977e8c1cd8fbfc3382a2f3113642 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Fri, 5 Oct 2018 22:05:43 +0200 Subject: [PATCH 17/65] vfs_fruit: call ad_convert_move_reso() from ad_convert_xattr() ad_convert_xattr() is the place that triggers the need to move the resource fork, so it should also call ad_convert_move_reso(). Bug: https://bugzilla.samba.org/show_bug.cgi?id=13649 Signed-off-by: Ralph Boehme (cherry picked from commit 33657f076bfaec77c08678188a65b4b24ea98660) --- source3/modules/vfs_fruit.c | 113 ++++++++++++++++++------------------ 1 file changed, 57 insertions(+), 56 deletions(-) diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c index a519effb72a..8e486c20ec4 100644 --- a/source3/modules/vfs_fruit.c +++ b/source3/modules/vfs_fruit.c @@ -942,6 +942,58 @@ static bool ad_unpack(struct adouble *ad, const size_t nentries, return true; } +static bool ad_convert_move_reso(struct adouble *ad, + const struct smb_filename *smb_fname) +{ + char *map = MAP_FAILED; + size_t maplen; + ssize_t len; + int rc; + bool ok; + + if (ad_getentrylen(ad, ADEID_RFORK) == 0) { + return true; + } + + maplen = ad_getentryoff(ad, ADEID_RFORK) + + ad_getentrylen(ad, ADEID_RFORK); + + /* FIXME: direct use of mmap(), vfs_aio_fork does it too */ + map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED, + ad->ad_fd, 0); + if (map == MAP_FAILED) { + DBG_ERR("mmap AppleDouble: %s\n", strerror(errno)); + return false; + } + + + memmove(map + ADEDOFF_RFORK_DOT_UND, + map + ad_getentryoff(ad, ADEID_RFORK), + ad_getentrylen(ad, ADEID_RFORK)); + + rc = munmap(map, maplen); + if (rc != 0) { + DBG_ERR("munmap failed: %s\n", strerror(errno)); + return false; + } + + ad_setentryoff(ad, ADEID_RFORK, ADEDOFF_RFORK_DOT_UND); + + ok = ad_pack(ad); + if (!ok) { + DBG_WARNING("ad_pack [%s] failed\n", smb_fname->base_name); + return false; + } + + len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0); + if (len != AD_DATASZ_DOT_UND) { + DBG_ERR("%s: bad size: %zd\n", smb_fname->base_name, len); + return false; + } + + return true; +} + static bool ad_convert_xattr(struct adouble *ad, const struct smb_filename *smb_fname) { @@ -1088,6 +1140,11 @@ static bool ad_convert_xattr(struct adouble *ad, goto fail; } + ok = ad_convert_move_reso(ad, smb_fname); + if (!ok) { + goto fail; + } + ok = true; fail: @@ -1214,57 +1271,6 @@ static bool ad_convert_truncate(struct adouble *ad, return true; } -static bool ad_convert_move_reso(struct adouble *ad, - const struct smb_filename *smb_fname) -{ - char *map = MAP_FAILED; - size_t maplen; - ssize_t len; - int rc; - bool ok; - - if (ad_getentrylen(ad, ADEID_RFORK) == 0) { - return true; - } - - maplen = ad_getentryoff(ad, ADEID_RFORK) + - ad_getentrylen(ad, ADEID_RFORK); - - /* FIXME: direct use of mmap(), vfs_aio_fork does it too */ - map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED, - ad->ad_fd, 0); - if (map == MAP_FAILED) { - DBG_ERR("mmap AppleDouble: %s\n", strerror(errno)); - return false; - } - - memmove(map + ADEDOFF_RFORK_DOT_UND, - map + ad_getentryoff(ad, ADEID_RFORK), - ad_getentrylen(ad, ADEID_RFORK)); - - rc = munmap(map, maplen); - if (rc != 0) { - DBG_ERR("munmap failed: %s\n", strerror(errno)); - return false; - } - - ad_setentryoff(ad, ADEID_RFORK, ADEDOFF_RFORK_DOT_UND); - - ok = ad_pack(ad); - if (!ok) { - DBG_WARNING("ad_pack [%s] failed\n", smb_fname->base_name); - return false; - } - - len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0); - if (len != AD_DATASZ_DOT_UND) { - DBG_ERR("%s: bad size: %zd\n", smb_fname->base_name, len); - return false; - } - - return true; -} - /** * Convert from Apple's ._ file to Netatalk * @@ -1288,11 +1294,6 @@ static int ad_convert(struct adouble *ad, return -1; } - ok = ad_convert_move_reso(ad, smb_fname); - if (!ok) { - return -1; - } - ok = ad_convert_truncate(ad, smb_fname); if (!ok) { return -1; -- 2.17.2 From bff731d4589c86df69db9689a1513472f40ff50c Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Mon, 8 Oct 2018 12:51:37 +0200 Subject: [PATCH 18/65] vfs_fruit: add check for OS X filler in FinderInfo conversion This ensures that the function only acts on AppleDouble files created by macOS and not AppleDouble files created by us that are already in the correct format (only using the Resource Fork). Bug: https://bugzilla.samba.org/show_bug.cgi?id=13649 Signed-off-by: Ralph Boehme (cherry picked from commit 0a606ddcf3d6ae53525caf160e68ee7abef28e27) --- source3/modules/vfs_fruit.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c index 8e486c20ec4..9126e1237db 100644 --- a/source3/modules/vfs_fruit.c +++ b/source3/modules/vfs_fruit.c @@ -1169,6 +1169,12 @@ static bool ad_convert_finderinfo(struct adouble *ad, ssize_t nwritten; NTSTATUS status; int saved_errno = 0; + int cmp; + + cmp = memcmp(ad->ad_filler, AD_FILLER_TAG_OSX, ADEDLEN_FILLER); + if (cmp != 0) { + return true; + } p_ad = ad_get_entry(ad, ADEID_FINDERI); if (p_ad == NULL) { -- 2.17.2 From 45e0b524e035afc0638bf9e46e73f236d2d30923 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Mon, 8 Oct 2018 18:43:51 +0200 Subject: [PATCH 19/65] vfs_fruit: add out arg "converted_xattr" to ad_convert_xattr Used to let the caller know if a conversion has been done. Currently not used in the caller, that comes next. Bug: https://bugzilla.samba.org/show_bug.cgi?id=13649 Signed-off-by: Ralph Boehme (cherry picked from commit f01058cae0a122dabeea79916b8e350b541bbcff) --- source3/modules/vfs_fruit.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c index 9126e1237db..33988f7f5b7 100644 --- a/source3/modules/vfs_fruit.c +++ b/source3/modules/vfs_fruit.c @@ -995,7 +995,8 @@ static bool ad_convert_move_reso(struct adouble *ad, } static bool ad_convert_xattr(struct adouble *ad, - const struct smb_filename *smb_fname) + const struct smb_filename *smb_fname, + bool *converted_xattr) { static struct char_mappings **string_replace_cmaps = NULL; char *map = MAP_FAILED; @@ -1007,6 +1008,8 @@ static bool ad_convert_xattr(struct adouble *ad, int rc; bool ok; + *converted_xattr = false; + if (ad->adx_header.adx_num_attrs == 0) { return true; } @@ -1145,6 +1148,7 @@ static bool ad_convert_xattr(struct adouble *ad, goto fail; } + *converted_xattr = true; ok = true; fail: @@ -1290,12 +1294,13 @@ static int ad_convert(struct adouble *ad, const struct smb_filename *smb_fname) { bool ok; + bool converted_xattr; if (ad_getentrylen(ad, ADEID_FINDERI) == ADEDLEN_FINDERI) { return 0; } - ok = ad_convert_xattr(ad, smb_fname); + ok = ad_convert_xattr(ad, smb_fname, &converted_xattr); if (!ok) { return -1; } -- 2.17.2 From b784c261ac5d25a9bf34fe283cd862f27f482fb2 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Mon, 8 Oct 2018 18:47:32 +0200 Subject: [PATCH 20/65] vfs_fruit: make call to ad_convert_truncate() optional Call ad_convert_truncate() based on whether the previous call ad_convert_xattr() returned converted_xattr=true. Upcoming fixes for a different Samba bug (#13642) will hook into calling ad_convert_truncate() in other cases, this also prepares for that. Bug: https://bugzilla.samba.org/show_bug.cgi?id=13649 Signed-off-by: Ralph Boehme (cherry picked from commit 2f350ce6e82facdfed420fbc1c89ef438ddcdb40) --- source3/modules/vfs_fruit.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c index 33988f7f5b7..f11523a16d2 100644 --- a/source3/modules/vfs_fruit.c +++ b/source3/modules/vfs_fruit.c @@ -1305,9 +1305,11 @@ static int ad_convert(struct adouble *ad, return -1; } - ok = ad_convert_truncate(ad, smb_fname); - if (!ok) { - return -1; + if (converted_xattr) { + ok = ad_convert_truncate(ad, smb_fname); + if (!ok) { + return -1; + } } ok = ad_convert_finderinfo(ad, smb_fname); -- 2.17.2 From 2a2488f62901300b381513c15afa4f752dfab1ab Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Tue, 9 Oct 2018 10:15:37 +0200 Subject: [PATCH 21/65] vfs_fruit: move check in ad_convert() to ad_convert_*() subfunctions Currently the whole conversion is skipped if the FinderInfo entry in the AppleDouble file is of the default size (ie not containing xattrs). That also means we never converted FinderInfo from the AppleDouble file to stream format. This change finally fixes this. Note that this keeps failing with streams_depot, much like the existing known-fail of "samba3.vfs.fruit streams_depot.OS X AppleDouble file conversion". Fixing the conversion to work with vfs_streams_depot is a task for another day. Bug: https://bugzilla.samba.org/show_bug.cgi?id=13649 Signed-off-by: Ralph Boehme (cherry picked from commit c53ac91e7628a2068875e03627365c0411a19ffb) --- selftest/knownfail.d/samba3.vfs.fruit | 2 -- source3/modules/vfs_fruit.c | 8 ++++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/selftest/knownfail.d/samba3.vfs.fruit b/selftest/knownfail.d/samba3.vfs.fruit index bc46c2f4922..6307e2b3404 100644 --- a/selftest/knownfail.d/samba3.vfs.fruit +++ b/selftest/knownfail.d/samba3.vfs.fruit @@ -1,4 +1,2 @@ ^samba3.vfs.fruit streams_depot.OS X AppleDouble file conversion\(nt4_dc\) -^samba3.vfs.fruit metadata_netatalk.OS X AppleDouble file conversion without embedded xattr\(nt4_dc\) -^samba3.vfs.fruit metadata_stream.OS X AppleDouble file conversion without embedded xattr\(nt4_dc\) ^samba3.vfs.fruit streams_depot.OS X AppleDouble file conversion without embedded xattr\(nt4_dc\) diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c index f11523a16d2..c0944525587 100644 --- a/source3/modules/vfs_fruit.c +++ b/source3/modules/vfs_fruit.c @@ -1010,6 +1010,10 @@ static bool ad_convert_xattr(struct adouble *ad, *converted_xattr = false; + if (ad_getentrylen(ad, ADEID_FINDERI) == ADEDLEN_FINDERI) { + return true; + } + if (ad->adx_header.adx_num_attrs == 0) { return true; } @@ -1296,10 +1300,6 @@ static int ad_convert(struct adouble *ad, bool ok; bool converted_xattr; - if (ad_getentrylen(ad, ADEID_FINDERI) == ADEDLEN_FINDERI) { - return 0; - } - ok = ad_convert_xattr(ad, smb_fname, &converted_xattr); if (!ok) { return -1; -- 2.17.2 From 7212d536af57f92d8544f13b520c5bd820dd73d7 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Tue, 2 Oct 2018 16:31:15 +0200 Subject: [PATCH 22/65] docs:vfs_fruit: add "wipe_intentionally_left_blank_rfork" option Bug: https://bugzilla.samba.org/show_bug.cgi?id=13642 Signed-off-by: Ralph Boehme (cherry picked from commit ae842636f3553700d841fbc91197b8021a7c49bc) --- docs-xml/manpages/vfs_fruit.8.xml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/docs-xml/manpages/vfs_fruit.8.xml b/docs-xml/manpages/vfs_fruit.8.xml index 7f6a0e7c022..6e247b40323 100644 --- a/docs-xml/manpages/vfs_fruit.8.xml +++ b/docs-xml/manpages/vfs_fruit.8.xml @@ -378,6 +378,20 @@ + + fruit:wipe_intentionally_left_blank_rfork = yes | no + + Whether to wipe Resource Fork data that matches the special + 286 bytes sized placeholder blob that macOS client create on + occasion. The blob contains a string This resource fork + intentionally left blank, the remaining bytes being mostly + zero. There being no one use of this data, it is probably safe to + discard it. When this option is enabled, this module truncates the + Resource Fork stream to 0 bytes. + The default is no. + + + -- 2.17.2 From 92d0195c6c177c46630c7605de361c51b6e07b16 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Thu, 4 Oct 2018 18:22:31 +0200 Subject: [PATCH 23/65] docs:vfs_fruit: add "delete_empty_adfiles" option Bug: https://bugzilla.samba.org/show_bug.cgi?id=13642 Signed-off-by: Ralph Boehme (cherry picked from commit 1f74c348c63c2d9938186f0ff86b77ffaa58262c) --- docs-xml/manpages/vfs_fruit.8.xml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs-xml/manpages/vfs_fruit.8.xml b/docs-xml/manpages/vfs_fruit.8.xml index 6e247b40323..fff89ab6ca4 100644 --- a/docs-xml/manpages/vfs_fruit.8.xml +++ b/docs-xml/manpages/vfs_fruit.8.xml @@ -392,6 +392,17 @@ + + fruit:delete_empty_adfiles = yes | no + + Whether to delete empty AppleDouble files. Empty means that + the resource fork entry in the AppleDouble files is of size 0, or + the size is exactly 286 bytes and the content matches a special + boilerplate resource fork created my macOS. + The default is no. + + + -- 2.17.2 From edb4a9cbd85be2457dcaac87d04d2f6a5dea2389 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Thu, 4 Oct 2018 13:47:20 +0200 Subject: [PATCH 24/65] s3:selftest: list vfs testssuites one per line Bug: https://bugzilla.samba.org/show_bug.cgi?id=13642 Signed-off-by: Ralph Boehme (cherry picked from commit cbdf345df726dce485b27879c199f0cdbbda5975) --- source3/selftest/tests.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/source3/selftest/tests.py b/source3/selftest/tests.py index 4526ac10a47..850663aca68 100755 --- a/source3/selftest/tests.py +++ b/source3/selftest/tests.py @@ -418,7 +418,13 @@ nbt = ["nbt.dgram" ] libsmbclient = ["libsmbclient"] -vfs = ["vfs.fruit", "vfs.acl_xattr", "vfs.fruit_netatalk", "vfs.fruit_file_id", "vfs.fruit_timemachine"] +vfs = [ + "vfs.fruit", + "vfs.acl_xattr", + "vfs.fruit_netatalk", + "vfs.fruit_file_id", + "vfs.fruit_timemachine", +] tests= base + raw + smb2 + rpc + unix + local + rap + nbt + libsmbclient + idmap + vfs -- 2.17.2 From 7806689950c23e8abb430c2a6cd180d1806f70ba Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Thu, 4 Oct 2018 14:28:15 +0200 Subject: [PATCH 25/65] s4:torture: add test for AppleDouble ResourceFork conversion Bug: https://bugzilla.samba.org/show_bug.cgi?id=13642 Signed-off-by: Ralph Boehme (cherry picked from commit 52502855195459389734954ba374b21921671c84) --- selftest/knownfail.d/samba3.vfs.fruit | 2 + selftest/target/Samba3.pm | 18 +++ source3/selftest/tests.py | 4 + source4/torture/vfs/fruit.c | 191 ++++++++++++++++++++++++++ source4/torture/vfs/vfs.c | 1 + 5 files changed, 216 insertions(+) diff --git a/selftest/knownfail.d/samba3.vfs.fruit b/selftest/knownfail.d/samba3.vfs.fruit index 6307e2b3404..f2a27040a8e 100644 --- a/selftest/knownfail.d/samba3.vfs.fruit +++ b/selftest/knownfail.d/samba3.vfs.fruit @@ -1,2 +1,4 @@ ^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_conversion wipe_intentionally_left_blank_rfork.convert_xattr_and_empty_rfork_then_delete\(nt4_dc\) +^samba3.vfs.fruit_conversion delete_empty_adfiles.convert_xattr_and_empty_rfork_then_delete\(nt4_dc\) diff --git a/selftest/target/Samba3.pm b/selftest/target/Samba3.pm index ca0d8092c83..173d799558c 100755 --- a/selftest/target/Samba3.pm +++ b/selftest/target/Samba3.pm @@ -1981,6 +1981,24 @@ sub provision($$$$$$$$$) fruit:time machine = yes fruit:time machine max size = 32K +[vfs_fruit_wipe_intentionally_left_blank_rfork] + path = $shrdir + vfs objects = fruit streams_xattr acl_xattr xattr_tdb + fruit:resource = file + fruit:metadata = stream + fruit:wipe_intentionally_left_blank_rfork = true + fruit:delete_empty_adfiles = false + fruit:veto_appledouble = no + +[vfs_fruit_delete_empty_adfiles] + path = $shrdir + vfs objects = fruit streams_xattr acl_xattr xattr_tdb + fruit:resource = file + fruit:metadata = stream + fruit:wipe_intentionally_left_blank_rfork = true + fruit:delete_empty_adfiles = true + fruit:veto_appledouble = no + [badname-tmp] path = $badnames_shrdir guest ok = yes diff --git a/source3/selftest/tests.py b/source3/selftest/tests.py index 850663aca68..c6909c3a432 100755 --- a/source3/selftest/tests.py +++ b/source3/selftest/tests.py @@ -424,6 +424,7 @@ vfs = [ "vfs.fruit_netatalk", "vfs.fruit_file_id", "vfs.fruit_timemachine", + "vfs.fruit_conversion", ] tests= base + raw + smb2 + rpc + unix + local + rap + nbt + libsmbclient + idmap + vfs @@ -533,6 +534,9 @@ tests= base + raw + smb2 + rpc + unix + local + rap + nbt + libsmbclient + idmap plansmbtorture4testsuite(t, "nt4_dc", '//$SERVER_IP/vfs_fruit_timemachine -U$USERNAME%$PASSWORD --option=torture:localdir=$SELFTEST_PREFIX/nt4_dc/share') elif t == "vfs.fruit_file_id": plansmbtorture4testsuite(t, "nt4_dc", '//$SERVER_IP/vfs_fruit -U$USERNAME%$PASSWORD') + elif t == "vfs.fruit_conversion": + plansmbtorture4testsuite(t, "nt4_dc", '//$SERVER_IP/tmp -U$USERNAME%$PASSWORD --option=torture:share2=vfs_fruit_wipe_intentionally_left_blank_rfork --option=torture:delete_empty_adfiles=false', 'wipe_intentionally_left_blank_rfork') + plansmbtorture4testsuite(t, "nt4_dc", '//$SERVER_IP/tmp -U$USERNAME%$PASSWORD --option=torture:share2=vfs_fruit_delete_empty_adfiles --option=torture:delete_empty_adfiles=true', 'delete_empty_adfiles') elif t == "rpc.schannel_anon_setpw": plansmbtorture4testsuite(t, "nt4_dc", '//$SERVER_IP/tmp -U$%', description="anonymous password set") plansmbtorture4testsuite(t, "nt4_dc_schannel", '//$SERVER_IP/tmp -U$%', description="anonymous password set (schannel enforced server-side)") diff --git a/source4/torture/vfs/fruit.c b/source4/torture/vfs/fruit.c index 3e95b745944..b0bec2afe5b 100644 --- a/source4/torture/vfs/fruit.c +++ b/source4/torture/vfs/fruit.c @@ -5407,3 +5407,194 @@ struct torture_suite *torture_vfs_fruit_timemachine(TALLOC_CTX *ctx) return suite; } + +static bool test_convert_xattr_and_empty_rfork_then_delete( + struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + const char *fname = BASEDIR "\\test_adouble_conversion"; + const char *adname = BASEDIR "/._test_adouble_conversion"; + const char *rfork = BASEDIR "\\test_adouble_conversion" AFPRESOURCE_STREAM_NAME; + NTSTATUS status; + struct smb2_handle testdirh; + bool ret = true; + const char *streams[] = { + "::$DATA", + AFPINFO_STREAM, + ":com.apple.metadata" "\xef\x80\xa2" "_kMDItemUserTags:$DATA", + ":foo" "\xef\x80\xa2" "bar:$DATA", /* "foo:bar:$DATA" */ + }; + struct smb2_create create; + struct smb2_find find; + unsigned int count; + union smb_search_data *d; + bool delete_empty_adfiles; + int expected_num_files; + + delete_empty_adfiles = torture_setting_bool(tctx, + "delete_empty_adfiles", + false); + + smb2_deltree(tree1, BASEDIR); + + status = torture_smb2_testdir(tree1, BASEDIR, &testdirh); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testdir failed\n"); + smb2_util_close(tree1, testdirh); + + ret = torture_setup_file(tctx, tree1, fname, false); + torture_assert_goto(tctx, ret == true, ret, done, + "torture_setup_file failed\n"); + + ret = torture_setup_file(tctx, tree1, adname, false); + torture_assert_goto(tctx, ret == true, ret, done, + "torture_setup_file failed\n"); + + ret = write_stream(tree1, __location__, tctx, mem_ctx, + adname, NULL, + 0, sizeof(osx_adouble_w_xattr), osx_adouble_w_xattr); + torture_assert_goto(tctx, ret == true, ret, done, + "write_stream failed\n"); + + ret = enable_aapl(tctx, tree2); + torture_assert_goto(tctx, ret == true, ret, done, "enable_aapl failed"); + + /* + * Issue a smb2_find(), this triggers the server-side conversion + */ + + create = (struct smb2_create) { + .in.desired_access = SEC_RIGHTS_DIR_READ, + .in.create_options = NTCREATEX_OPTIONS_DIRECTORY, + .in.file_attributes = FILE_ATTRIBUTE_DIRECTORY, + .in.share_access = NTCREATEX_SHARE_ACCESS_READ, + .in.create_disposition = NTCREATEX_DISP_OPEN, + .in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS, + .in.fname = BASEDIR, + }; + + status = smb2_create(tree2, tctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed\n"); + + find = (struct smb2_find) { + .in.file.handle = create.out.file.handle, + .in.pattern = "*", + .in.max_response_size = 0x1000, + .in.level = SMB2_FIND_ID_BOTH_DIRECTORY_INFO, + }; + + status = smb2_find_level(tree2, tree2, &find, &count, &d); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_find_level failed\n"); + + status = smb2_util_close(tree2, create.out.file.handle); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_close failed"); + + /* + * Check number of streams + */ + + ret = check_stream_list(tree2, tctx, fname, 4, streams, false); + torture_assert_goto(tctx, ret == true, ret, done, "check_stream_list"); + + /* + * Check Resource Fork is gone + */ + + create = (struct smb2_create) { + .in.desired_access = SEC_RIGHTS_FILE_READ|SEC_RIGHTS_FILE_WRITE, + .in.file_attributes = FILE_ATTRIBUTE_NORMAL, + .in.share_access = NTCREATEX_SHARE_ACCESS_READ, + .in.create_disposition = NTCREATEX_DISP_OPEN, + .in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS, + .in.fname = rfork, + }; + + status = smb2_create(tree2, mem_ctx, &create); + torture_assert_ntstatus_equal_goto( + tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND, + ret, done, "Bad smb2_create return\n"); + + /* + * Check xattr data has been migrated from the AppleDouble file to + * streams. + */ + + ret = check_stream(tree2, __location__, tctx, mem_ctx, + fname, AFPINFO_STREAM, + 0, 60, 16, 8, "TESTSLOW"); + torture_assert_goto(tctx, ret == true, ret, done, + "check AFPINFO_STREAM failed\n"); + + ret = check_stream(tree2, __location__, tctx, mem_ctx, + fname, ":foo" "\xef\x80\xa2" "bar", /* foo:bar */ + 0, 3, 0, 3, "baz"); + torture_assert_goto(tctx, ret == true, ret, done, + "check foo stream failed\n"); + + /* + * Now check number of files. If delete_empty_adfiles is set, the + * AppleDouble files should have been deleted. + */ + + create = (struct smb2_create) { + .in.desired_access = SEC_RIGHTS_DIR_READ, + .in.create_options = NTCREATEX_OPTIONS_DIRECTORY, + .in.file_attributes = FILE_ATTRIBUTE_DIRECTORY, + .in.share_access = NTCREATEX_SHARE_ACCESS_READ, + .in.create_disposition = NTCREATEX_DISP_OPEN, + .in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS, + .in.fname = BASEDIR, + }; + + status = smb2_create(tree2, tctx, &create); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed\n"); + + find = (struct smb2_find) { + .in.file.handle = create.out.file.handle, + .in.pattern = "*", + .in.max_response_size = 0x1000, + .in.level = SMB2_FIND_ID_BOTH_DIRECTORY_INFO, + }; + + status = smb2_find_level(tree2, tree2, &find, &count, &d); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_find_level failed\n"); + + status = smb2_util_close(tree2, create.out.file.handle); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_close failed"); + + if (delete_empty_adfiles) { + expected_num_files = 3; + } else { + expected_num_files = 4; + } + torture_assert_int_equal_goto(tctx, count, expected_num_files, ret, done, + "Wrong number of files\n"); + +done: + smb2_deltree(tree1, BASEDIR); + talloc_free(mem_ctx); + return ret; +} + +struct torture_suite *torture_vfs_fruit_conversion(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = torture_suite_create( + ctx, "fruit_conversion"); + + suite->description = talloc_strdup( + suite, "vfs_fruit conversion tests"); + + torture_suite_add_2ns_smb2_test( + suite, "convert_xattr_and_empty_rfork_then_delete", + test_convert_xattr_and_empty_rfork_then_delete); + + return suite; +} diff --git a/source4/torture/vfs/vfs.c b/source4/torture/vfs/vfs.c index 64cb0e3d6c4..39cd573c9d6 100644 --- a/source4/torture/vfs/vfs.c +++ b/source4/torture/vfs/vfs.c @@ -113,6 +113,7 @@ NTSTATUS torture_vfs_init(TALLOC_CTX *ctx) torture_suite_add_suite(suite, torture_acl_xattr(suite)); torture_suite_add_suite(suite, torture_vfs_fruit_file_id(suite)); torture_suite_add_suite(suite, torture_vfs_fruit_timemachine(suite)); + torture_suite_add_suite(suite, torture_vfs_fruit_conversion(suite)); torture_register_suite(ctx, suite); -- 2.17.2 From 36a9397dd48c1150de57262ed20a45945d59d1bb Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Wed, 3 Oct 2018 12:01:00 +0200 Subject: [PATCH 26/65] vfs_fruit: add option "wipe_intentionally_left_blank_rfork" Bug: https://bugzilla.samba.org/show_bug.cgi?id=13642 Signed-off-by: Ralph Boehme (cherry picked from commit ac8c8bddc430c0e6ac4b59e37705183e57adcc5b) --- source3/modules/vfs_fruit.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c index c0944525587..d0ddbc5ccfd 100644 --- a/source3/modules/vfs_fruit.c +++ b/source3/modules/vfs_fruit.c @@ -142,6 +142,7 @@ struct fruit_config_data { const char *model; bool time_machine; off_t time_machine_max_size; + bool wipe_intentionally_left_blank_rfork; /* * Additional options, all enabled by default, @@ -2098,6 +2099,10 @@ static int init_fruit_config(vfs_handle_struct *handle) config->time_machine_max_size = conv_str_size(tm_size_str); } + config->wipe_intentionally_left_blank_rfork = lp_parm_bool( + SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, + "wipe_intentionally_left_blank_rfork", false); + SMB_VFS_HANDLE_SET_DATA(handle, config, NULL, struct fruit_config_data, return -1); -- 2.17.2 From e475a45ee0571cf29961f9dd5d1a74e9c310f623 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Tue, 2 Oct 2018 16:05:28 +0200 Subject: [PATCH 27/65] vfs_fruit: detect empty resource forks in ad_convert() For some reason the macOS client often writes AppleDouble files with a non-zero sized resource fork, but the resource fork data is just boilerplate data with the following string close to the start This resource fork intentionally left blank A dump with apple_dump looks like this: Entry ID : 00000002 : Resource Fork Offset : 00000052 : 82 Length : 0000011E : 286 -RAW DUMP--: 0 1 2 3 4 5 6 7 8 9 A B C D E F : (ASCII) 00000000 : 00 00 01 00 00 00 01 00 00 00 00 00 00 00 00 1E : ................ 00000010 : 54 68 69 73 20 72 65 73 6F 75 72 63 65 20 66 6F : This resource fo 00000020 : 72 6B 20 69 6E 74 65 6E 74 69 6F 6E 61 6C 6C 79 : rk intentionally 00000030 : 20 6C 65 66 74 20 62 6C 61 6E 6B 20 20 20 00 00 : left blank .. 00000040 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ 00000050 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ 00000060 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ 00000070 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ 00000080 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ 00000090 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ 000000A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ 000000B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ 000000C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ 000000D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ 000000E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ 000000F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : ................ 00000100 : 00 00 01 00 00 00 01 00 00 00 00 00 00 00 00 1E : ................ 00000110 : 00 00 00 00 00 00 00 00 00 1C 00 1E FF FF : .............. We can safely discard this Resource Fork data. Bug: https://bugzilla.samba.org/show_bug.cgi?id=13642 Signed-off-by: Ralph Boehme (cherry picked from commit d4a2acb2b767e512c8ed642bb7abac48281f61ff) --- selftest/knownfail.d/samba3.vfs.fruit | 1 - source3/modules/vfs_fruit.c | 111 +++++++++++++++++++++++++- 2 files changed, 110 insertions(+), 2 deletions(-) diff --git a/selftest/knownfail.d/samba3.vfs.fruit b/selftest/knownfail.d/samba3.vfs.fruit index f2a27040a8e..8b9aed48b63 100644 --- a/selftest/knownfail.d/samba3.vfs.fruit +++ b/selftest/knownfail.d/samba3.vfs.fruit @@ -1,4 +1,3 @@ ^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_conversion wipe_intentionally_left_blank_rfork.convert_xattr_and_empty_rfork_then_delete\(nt4_dc\) ^samba3.vfs.fruit_conversion delete_empty_adfiles.convert_xattr_and_empty_rfork_then_delete\(nt4_dc\) diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c index d0ddbc5ccfd..0434d036e34 100644 --- a/source3/modules/vfs_fruit.c +++ b/source3/modules/vfs_fruit.c @@ -468,6 +468,45 @@ static const uint32_t set_eid[] = { AD_DEV, AD_INO, AD_SYN, AD_ID }; +static char empty_resourcefork[] = { + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, + 0x54, 0x68, 0x69, 0x73, 0x20, 0x72, 0x65, 0x73, + 0x6F, 0x75, 0x72, 0x63, 0x65, 0x20, 0x66, 0x6F, + 0x72, 0x6B, 0x20, 0x69, 0x6E, 0x74, 0x65, 0x6E, + 0x74, 0x69, 0x6F, 0x6E, 0x61, 0x6C, 0x6C, 0x79, + 0x20, 0x6C, 0x65, 0x66, 0x74, 0x20, 0x62, 0x6C, + 0x61, 0x6E, 0x6B, 0x20, 0x20, 0x20, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1C, 0x00, 0x1E, 0xFF, 0xFF +}; + struct fio { /* tcon config handle */ struct fruit_config_data *config; @@ -1286,6 +1325,70 @@ static bool ad_convert_truncate(struct adouble *ad, return true; } +static bool ad_convert_blank_rfork(struct adouble *ad, + bool *blank) +{ + struct fruit_config_data *config = NULL; + uint8_t *map = MAP_FAILED; + size_t maplen; + int cmp; + ssize_t len; + int rc; + bool ok; + + *blank = false; + + SMB_VFS_HANDLE_GET_DATA(ad->ad_handle, config, + struct fruit_config_data, return false); + + if (!config->wipe_intentionally_left_blank_rfork) { + return true; + } + + if (ad_getentrylen(ad, ADEID_RFORK) != sizeof(empty_resourcefork)) { + return true; + } + + maplen = ad_getentryoff(ad, ADEID_RFORK) + + ad_getentrylen(ad, ADEID_RFORK); + + /* FIXME: direct use of mmap(), vfs_aio_fork does it too */ + map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED, + ad->ad_fd, 0); + if (map == MAP_FAILED) { + DBG_ERR("mmap AppleDouble: %s\n", strerror(errno)); + return false; + } + + cmp = memcmp(map + ADEDOFF_RFORK_DOT_UND, + empty_resourcefork, + sizeof(empty_resourcefork)); + rc = munmap(map, maplen); + if (rc != 0) { + DBG_ERR("munmap failed: %s\n", strerror(errno)); + return false; + } + + if (cmp != 0) { + return true; + } + + ad_setentrylen(ad, ADEID_RFORK, 0); + + ok = ad_pack(ad); + if (!ok) { + return false; + } + + len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0); + if (len != AD_DATASZ_DOT_UND) { + return false; + } + + *blank = true; + return true; +} + /** * Convert from Apple's ._ file to Netatalk * @@ -1300,13 +1403,19 @@ static int ad_convert(struct adouble *ad, { bool ok; bool converted_xattr; + bool blank; ok = ad_convert_xattr(ad, smb_fname, &converted_xattr); if (!ok) { return -1; } - if (converted_xattr) { + ok = ad_convert_blank_rfork(ad, &blank); + if (!ok) { + return -1; + } + + if (converted_xattr || blank) { ok = ad_convert_truncate(ad, smb_fname); if (!ok) { return -1; -- 2.17.2 From b3279e776bda5c483fb771578f27a9c648c0d823 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Wed, 3 Oct 2018 12:01:00 +0200 Subject: [PATCH 28/65] vfs_fruit: add option "delete_empty_adfiles" Bug: https://bugzilla.samba.org/show_bug.cgi?id=13642 Signed-off-by: Ralph Boehme (cherry picked from commit 78c13a8ad3f1a7acdc56ca31a5fe4d33a4972fdc) --- source3/modules/vfs_fruit.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c index 0434d036e34..c56a1f8618e 100644 --- a/source3/modules/vfs_fruit.c +++ b/source3/modules/vfs_fruit.c @@ -143,6 +143,7 @@ struct fruit_config_data { bool time_machine; off_t time_machine_max_size; bool wipe_intentionally_left_blank_rfork; + bool delete_empty_adfiles; /* * Additional options, all enabled by default, @@ -2212,6 +2213,10 @@ static int init_fruit_config(vfs_handle_struct *handle) SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "wipe_intentionally_left_blank_rfork", false); + config->delete_empty_adfiles = lp_parm_bool( + SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, + "delete_empty_adfiles", false); + SMB_VFS_HANDLE_SET_DATA(handle, config, NULL, struct fruit_config_data, return -1); -- 2.17.2 From 4833f2d93c2da78f478e0b257b0ea8138117ba73 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Tue, 9 Oct 2018 14:54:31 +0200 Subject: [PATCH 29/65] vfs_fruit: optionally delete AppleDouble files without Resourcefork data Bug: https://bugzilla.samba.org/show_bug.cgi?id=13642 Signed-off-by: Ralph Boehme (cherry picked from commit 7c3de356797038eebbf1656c4926cbdb185a0425) --- selftest/knownfail.d/samba3.vfs.fruit | 2 +- source3/modules/vfs_fruit.c | 42 +++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/selftest/knownfail.d/samba3.vfs.fruit b/selftest/knownfail.d/samba3.vfs.fruit index 8b9aed48b63..2b49e827d7e 100644 --- a/selftest/knownfail.d/samba3.vfs.fruit +++ b/selftest/knownfail.d/samba3.vfs.fruit @@ -1,3 +1,3 @@ ^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_conversion delete_empty_adfiles.convert_xattr_and_empty_rfork_then_delete\(nt4_dc\) + diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c index c56a1f8618e..c420d78930e 100644 --- a/source3/modules/vfs_fruit.c +++ b/source3/modules/vfs_fruit.c @@ -1390,6 +1390,43 @@ static bool ad_convert_blank_rfork(struct adouble *ad, return true; } +static bool ad_convert_delete_adfile(struct adouble *ad, + const struct smb_filename *smb_fname) +{ + struct fruit_config_data *config = NULL; + struct smb_filename *ad_name = NULL; + int rc; + + if (ad_getentrylen(ad, ADEID_RFORK) > 0) { + return true; + } + + SMB_VFS_HANDLE_GET_DATA(ad->ad_handle, config, + struct fruit_config_data, return false); + + if (!config->delete_empty_adfiles) { + return true; + } + + rc = adouble_path(talloc_tos(), smb_fname, &ad_name); + if (rc != 0) { + return false; + } + + rc = SMB_VFS_NEXT_UNLINK(ad->ad_handle, ad_name); + if (rc != 0) { + DBG_ERR("Unlinking [%s] failed: %s\n", + smb_fname_str_dbg(ad_name), strerror(errno)); + TALLOC_FREE(ad_name); + return false; + } + + DBG_WARNING("Unlinked [%s] after conversion\n", smb_fname_str_dbg(ad_name)); + TALLOC_FREE(ad_name); + + return true; +} + /** * Convert from Apple's ._ file to Netatalk * @@ -1430,6 +1467,11 @@ static int ad_convert(struct adouble *ad, return -1; } + ok = ad_convert_delete_adfile(ad, smb_fname); + if (!ok) { + return -1; + } + return 0; } -- 2.17.2 From 9733181bd9586cd98ddc186ca57fad23562b0fe6 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Fri, 19 Oct 2018 12:15:42 +0200 Subject: [PATCH 30/65] vfs_fruit: remove check for number of xattrs from ad_convert_xattr Turns out that there exist AppleDouble files with an extended FinderInfo entry that includes the xattr marshall buffer, but the count of xattrs in the buffer is just zero. We do want to discard this extended FinderInfo entry and convert it to a simple fixed size FinderInfo entry, so remove the check. Bug: https://bugzilla.samba.org/show_bug.cgi?id=13649 Signed-off-by: Ralph Boehme --- source3/modules/vfs_fruit.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c index c420d78930e..9f195f5ade4 100644 --- a/source3/modules/vfs_fruit.c +++ b/source3/modules/vfs_fruit.c @@ -1055,10 +1055,6 @@ static bool ad_convert_xattr(struct adouble *ad, return true; } - if (ad->adx_header.adx_num_attrs == 0) { - return true; - } - if (string_replace_cmaps == NULL) { const char **mappings = NULL; -- 2.17.2 From d522809dbd8dfa6f2c383de33e1634c715e551de Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Thu, 23 Aug 2018 12:07:20 +0200 Subject: [PATCH 31/65] 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 (cherry picked from commit a008566bee2c3533c6b7f86bbbaa26d024c0bcd5) --- 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 c6ab0f646d61e06068bb482b226fa02eab5af05f Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Wed, 17 Oct 2018 10:54:10 +0200 Subject: [PATCH 32/65] vfs_streams_xattr: consistently log dev/ino as hex open_file_ntcreate() logs dev/ino in hex format if it hits a "dev/ino mismatch". Logging it consistently as hex helps debugging such mismatch issues. Bug: https://bugzilla.samba.org/show_bug.cgi?id=13646 Signed-off-by: Ralph Boehme (cherry picked from commit 9329cf35286eaadbcca1844b129d191d538340ae) --- source3/modules/vfs_streams_xattr.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/source3/modules/vfs_streams_xattr.c b/source3/modules/vfs_streams_xattr.c index 7f930d96d6b..22ab7da2b28 100644 --- a/source3/modules/vfs_streams_xattr.c +++ b/source3/modules/vfs_streams_xattr.c @@ -52,9 +52,9 @@ static SMB_INO_T stream_inode(const SMB_STRUCT_STAT *sbuf, const char *sname) SMB_INO_T result; char *upper_sname; - DEBUG(10, ("stream_inode called for %lu/%lu [%s]\n", - (unsigned long)sbuf->st_ex_dev, - (unsigned long)sbuf->st_ex_ino, sname)); + DBG_DEBUG("stream_inode called for 0x%jx/0x%jx [%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); @@ -73,7 +73,7 @@ static SMB_INO_T stream_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, ("stream_inode returns %lu\n", (unsigned long)result)); + DBG_DEBUG("stream_inode returns 0x%jx\n", (uintmax_t)result); return result; } -- 2.17.2 From 2d660da4499acb32075afea3aa9025e7a493e369 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Fri, 19 Oct 2018 22:21:10 +0200 Subject: [PATCH 33/65] 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 (cherry picked from commit 49a0e63eba7c9210df400f532a8bb1e311f90cd9) --- 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 b0bec2afe5b..659953968de 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); @@ -4737,6 +4747,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 72648d6f83c3eff744bcefb67cbd5df902aff6cd Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Wed, 17 Oct 2018 10:51:45 +0200 Subject: [PATCH 34/65] 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 (cherry picked from commit c097015954e5ab967006ad88a8e53e7426b3f49e) --- 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 659953968de..4aa4c2785bf 100644 --- a/source4/torture/vfs/fruit.c +++ b/source4/torture/vfs/fruit.c @@ -3670,8 +3670,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); @@ -3681,8 +3681,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 d03858636351088a897ddb67983639f0dd1fe191 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Thu, 11 Oct 2018 17:13:52 +0200 Subject: [PATCH 35/65] 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 (cherry picked from commit e8b5bb2b6859bf6fa2307c99510f46a5749a262d) --- 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 4aa4c2785bf..4f96ebe23ca 100644 --- a/source4/torture/vfs/fruit.c +++ b/source4/torture/vfs/fruit.c @@ -3048,6 +3048,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 241617a3daf7db7feea14bfc50ee82b9bbb3b95b Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Thu, 11 Jan 2018 12:25:49 +0100 Subject: [PATCH 36/65] s4/test: fix AAPL size check A recent commit changed the ModelString from "Samba" to "MacSamba". Signed-off-by: Ralph Boehme Reviewed-by: Jeremy Allison --- 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 4f96ebe23ca..a37e2680d2c 100644 --- a/source4/torture/vfs/fruit.c +++ b/source4/torture/vfs/fruit.c @@ -2311,6 +2311,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); @@ -2367,7 +2368,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; @@ -2380,11 +2384,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 a90ec522dcc295f479234492d941f77b7ec96600 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Mon, 15 Oct 2018 15:31:21 +0200 Subject: [PATCH 37/65] 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 (cherry picked from commit 3db3ef5395482517db37676179093a95f1934710) --- 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 9f8d0c3e6698ec0767bb9f5b0aa1ac89db5bcbba Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Mon, 15 Oct 2018 15:39:12 +0200 Subject: [PATCH 38/65] 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 (cherry picked from commit f46630c1d2206f52df2c3705b395772624cac231) --- 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 377ff1986062858c06666e33a9fc65d4552d6073 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Mon, 15 Oct 2018 16:03:03 +0200 Subject: [PATCH 39/65] s4:torture/vfs/fruit: update test "rename_dir_openfile" to work against macOS The test opens a directory twice where the first open didn't allow sharing. No idea why this works against Samba, against macOS the second open got a sharing violation. Obvious fix: add proper sharing. Bug: https://bugzilla.samba.org/show_bug.cgi?id=13646 Signed-off-by: Ralph Boehme (cherry picked from commit b888d60a911c1f764e484988f6ab3fa72572a6e5) --- source4/torture/vfs/fruit.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source4/torture/vfs/fruit.c b/source4/torture/vfs/fruit.c index 0750026f1d5..29f8d579cbe 100644 --- a/source4/torture/vfs/fruit.c +++ b/source4/torture/vfs/fruit.c @@ -3190,7 +3190,7 @@ static bool test_rename_dir_openfile(struct torture_context *torture, io.smb2.in.desired_access = 0x0017019f; io.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; io.smb2.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY; - io.smb2.in.share_access = 0; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_MASK; io.smb2.in.alloc_size = 0; io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; @@ -3252,7 +3252,7 @@ static bool test_rename_dir_openfile(struct torture_context *torture, io.generic.level = RAW_OPEN_SMB2; io.smb2.in.desired_access = 0x0017019f; io.smb2.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY; - io.smb2.in.share_access = 0; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_MASK; io.smb2.in.alloc_size = 0; io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN; io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; -- 2.17.2 From 81cba431d5fade565accbaad10af547b1983a43c Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Mon, 15 Oct 2018 16:24:19 +0200 Subject: [PATCH 40/65] 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 (cherry picked from commit a29c47337ff95006f26547d366dad487f9d50599) --- 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 29f8d579cbe..b3f64260012 100644 --- a/source4/torture/vfs/fruit.c +++ b/source4/torture/vfs/fruit.c @@ -4182,6 +4182,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"); @@ -4232,14 +4234,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 55d0717898a3ea0b20b9e73523466153d664f09c Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Tue, 9 Oct 2018 18:48:08 +0200 Subject: [PATCH 41/65] 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 (cherry picked from commit 7fdcd222782be00d9c1e184bdfe19cdf15ad73b6) --- 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 b3f64260012..6c5ef3461e1 100644 --- a/source4/torture/vfs/fruit.c +++ b/source4/torture/vfs/fruit.c @@ -3453,6 +3453,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" }; @@ -3494,6 +3498,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 bb1e956872f365aaec64e4945bec1e94bdd5d51a Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Wed, 10 Oct 2018 12:47:07 +0200 Subject: [PATCH 42/65] 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 (cherry picked from commit 80e91ed1aba51bf09d090eab29079055498d39dc) --- 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 6c5ef3461e1..19c71352f27 100644 --- a/source4/torture/vfs/fruit.c +++ b/source4/torture/vfs/fruit.c @@ -4094,6 +4094,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"); @@ -4132,6 +4134,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 c3a99aa09908e0e73f2d51503ab077691a906002 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Mon, 15 Oct 2018 15:17:08 +0200 Subject: [PATCH 43/65] 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 (cherry picked from commit bb3969b69fe5caa595f0bd73899218a066da56d0) --- selftest/knownfail.d/samba3.vfs.fruit | 4 +++- source4/torture/vfs/fruit.c | 28 +-------------------------- 2 files changed, 4 insertions(+), 28 deletions(-) diff --git a/selftest/knownfail.d/samba3.vfs.fruit b/selftest/knownfail.d/samba3.vfs.fruit index 2b49e827d7e..a2758ffeded 100644 --- a/selftest/knownfail.d/samba3.vfs.fruit +++ b/selftest/knownfail.d/samba3.vfs.fruit @@ -1,3 +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 19c71352f27..c8013037c4a 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 4e7638a90f6d09c4474d5e9c9cf8a41fbf7ca943 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Mon, 22 Oct 2018 12:32:09 +0200 Subject: [PATCH 44/65] 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 (cherry picked from commit 3d8d6ea1e27892ad19d15eef3240768bae12300d) --- 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 9f195f5ade4..aa52aeafda0 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 6399732bd8308970c7fdd488efa4c6a7fee70f4f Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Mon, 22 Oct 2018 12:43:16 +0200 Subject: [PATCH 45/65] 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 (cherry picked from commit 73c9f15711496d821ef62b55d412527f1e11a041) --- 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 c8013037c4a..c1ac11b9b01 100644 --- a/source4/torture/vfs/fruit.c +++ b/source4/torture/vfs/fruit.c @@ -4984,8 +4984,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, @@ -5064,6 +5088,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); @@ -5123,6 +5159,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 c60a67df591d04dd721628d5b86a95558ff33047 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Sat, 20 Oct 2018 14:52:23 +0200 Subject: [PATCH 46/65] 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 (cherry picked from commit d9a2517d2297b5831dbeb4cc9381ef40b6aa43ec) --- 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 c1ac11b9b01..0374c1c0f7a 100644 --- a/source4/torture/vfs/fruit.c +++ b/source4/torture/vfs/fruit.c @@ -5285,6 +5285,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 d29f5eb14cc0575d51204b2d4770855e18226c14 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Sat, 20 Oct 2018 14:54:48 +0200 Subject: [PATCH 47/65] 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 (cherry picked from commit a9417a65b236353ba953ed7052ae9c0b77466d83) --- 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 aa52aeafda0..caf50109002 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 f338056837bd62f8cc05170d96fececf6564fa6d Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Mon, 22 Oct 2018 14:01:34 +0200 Subject: [PATCH 48/65] 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 (cherry picked from commit d083a7c1db999a4e7ef70dc2be6c2a9524a4662c) --- 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 0374c1c0f7a..2cc5d0d694b 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); @@ -3938,6 +3944,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); @@ -4904,6 +4913,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 3665f14e3b24addfd2c0ee60d2fd7aab3829a631 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Sat, 20 Oct 2018 15:28:06 +0200 Subject: [PATCH 49/65] 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 (cherry picked from commit ba6668746a6a792e0d8afce5954954505c65d276) --- 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 caf50109002..027f069fe1e 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 77cc6399650e54d6fb77060845ecfa789d1d2504 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Sat, 20 Oct 2018 14:53:50 +0200 Subject: [PATCH 50/65] 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 (cherry picked from commit cd667e7abe0fd962f8eefab1fac6c7334e60c444) --- 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 027f069fe1e..41d8fd7b244 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 4b21548dc5e82bb3d610dcc0a2e495a5b4acd197 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Wed, 10 Oct 2018 18:45:56 +0200 Subject: [PATCH 51/65] 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 (cherry picked from commit 16f7e59a1bd7fc7e36c694fd7f20993418c627b5) --- 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 c8e61cd209613cdacf0cac099e5a5e60140979b3 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Thu, 11 Oct 2018 17:14:50 +0200 Subject: [PATCH 52/65] s4:torture/vfs/fruit: add check_stream_list_handle() Bug: https://bugzilla.samba.org/show_bug.cgi?id=13646 Signed-off-by: Ralph Boehme (cherry picked from commit 6e025de7db3e5a77766d5504f1b0353f7ff9201e) --- 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 2cc5d0d694b..75b3a6bab25 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 53/65] 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 (cherry picked from commit 7dabe93b5168a9ce881b4c9e294786a7c3e3902d) --- 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 75b3a6bab25..e288762dcd4 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 @@ -5264,6 +5262,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 @@ -5307,6 +5915,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 1c85fe2137c5e550490cafd414a2d8e8e60a5604 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Wed, 17 Oct 2018 19:07:11 +0200 Subject: [PATCH 54/65] vfs_fruit: update and 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 (cherry picked from commit f5828d90d26cf1fe2f55414019165c7012f51053) --- 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 41d8fd7b244..858b3a351b9 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 0x%jx/0x%jx [%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=0x%jx\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 [0x%jx] ino [0x%jx]\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 d0660bd75ef2e3bbaa80fc10f41d103f3d64b7df Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Mon, 15 Oct 2018 18:38:33 +0200 Subject: [PATCH 55/65] 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 (cherry picked from commit ecb152b8b0072811d119ab96c80894f9797c0084) --- 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 858b3a351b9..499230eb3e6 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 c72217baf8740fb89d61f9ce8d94d775d55d0670 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Mon, 22 Oct 2018 16:56:46 +0200 Subject: [PATCH 56/65] 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 (cherry picked from commit 3794f21cf670b72cadd8faae16517246819095c5) --- 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 499230eb3e6..a81d231a135 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 d8aa5bd349415a41ae7df36085c758d5e1b4ed16 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Wed, 22 Aug 2018 15:22:57 +0200 Subject: [PATCH 57/65] 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 (cherry picked from commit 8d8b0a53654c5bbbd571d6fe82379984a5b4d8a8) --- 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 a81d231a135..e96abe5619d 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 23ba6a44959002b865e19df81005a901fbb73be5 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Wed, 22 Aug 2018 15:21:08 +0200 Subject: [PATCH 58/65] 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 (cherry picked from commit e4ba9f90fbcef89354d27948b201a898ae21554a) --- 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 e96abe5619d..0a861c73af8 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 a1093f12cf8f9478ea94ade3eb34cdf2c4739f68 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Wed, 22 Aug 2018 15:22:08 +0200 Subject: [PATCH 59/65] 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 (cherry picked from commit 4c7dadd74146b8dbbfdd5da07756381dfe560386) --- 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 0a861c73af8..0c5eb631cd0 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 nread; + } + return nread; } -- 2.17.2 From c9225efed339e2eb6828d4519887aca6b29c72d0 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Wed, 22 Aug 2018 16:49:23 +0200 Subject: [PATCH 60/65] 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 (cherry picked from commit 05a396fa5aeaafa98975924de5a4102d1edc7055) --- 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 0c5eb631cd0..9baa52654b4 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 30b921dfd6ab8f110c22de4a9a267a78275884d8 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Wed, 17 Oct 2018 16:51:34 +0200 Subject: [PATCH 61/65] 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 (cherry picked from commit efc934cd7c4f1c0017a6ffdc43d1cf6398589773) --- 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 9baa52654b4..7a78cd4aead 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 31bb4c062345bdd051d53c0260f1e34d9a937dc3 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Sat, 20 Oct 2018 23:46:43 +0200 Subject: [PATCH 62/65] 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 (cherry picked from commit f9c53873dbc0c8aaf6674f389c3905ea23747efe) --- 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 7a78cd4aead..55d2d2d2435 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 4c0e00d937973e1e4337c4132c2b7680c8944ff4 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Sat, 20 Oct 2018 23:40:14 +0200 Subject: [PATCH 63/65] 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 (cherry picked from commit ae33a7168ebcc92de64d873214f7e818ae28207d) --- 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 55d2d2d2435..def307c9024 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 fd8c5cbc03b88a99387d298f30d6f8dac85e971d Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Sat, 20 Oct 2018 23:50:32 +0200 Subject: [PATCH 64/65] 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 (cherry picked from commit dcc10d7d5ea3bd72bb8aa82bea13ea368d034236) --- 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 def307c9024..5794a4e900c 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 f5fa736e5eb1c02ead34976dad00ff7e0f92f3ca Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Wed, 22 Aug 2018 15:25:26 +0200 Subject: [PATCH 65/65] 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 (cherry picked from commit dcd6d880aa552ea73db8b23c45d0029acd9f304a) --- 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 5794a4e900c..3b1efa127b5 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