[PATCH] Add optional AppleDouble cleanup to vfs_fruit

Ralph Böhme slow at samba.org
Tue Oct 23 13:36:20 UTC 2018


Hi!

Attached is a patchset with a nice improvement to vfs_fruit's capability to 
convert data from ._ AppleDouble files that where created on the server before 
a streams module was configured.

After converting FinderInfo and xattrs data from the AppleDouble to the streams 
backend, if the AppleDouble doesn't contain resource fork data, we can delete 
it.

Turns out many AppleDouble files do contain resource fork data, but it's only a 
placeholder added by the SMB VFS to reserver storage, it 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 and vfs_fruit does that if 
fruit:wipe_intentionally_left_blank_rfork is set to true (disabled by default).

Deletion of the AppleDouble files is guarded by fruit:delete_empty_adfiles 
(disabled by default).

If you have suggestion for better option names, I'm all ear. :)

Please review&push if happy. Thanks!

-slow

-- 
Ralph Boehme, Samba Team       https://samba.org/
Samba Developer, SerNet GmbH   https://sernet.de/en/samba/
GPG Key Fingerprint:           FAE2 C608 8A24 2520 51C5
                               59E4 AA1E 9B71 2639 9E46
-------------- next part --------------
From f178c10b4a928205073057e2de28322043d8220b Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Tue, 2 Oct 2018 16:31:15 +0200
Subject: [PATCH 1/8] 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 <slow at samba.org>
---
 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 25833038d96..0df5a7e5a79 100644
--- a/docs-xml/manpages/vfs_fruit.8.xml
+++ b/docs-xml/manpages/vfs_fruit.8.xml
@@ -378,6 +378,20 @@
 	    </listitem>
 	  </varlistentry>
 
+	  <varlistentry>
+	    <term>fruit:wipe_intentionally_left_blank_rfork = yes | no</term>
+	    <listitem>
+	      <para>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 <quote>This resource fork
+	      intentionally left blank</quote>, 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.</para>
+	      <para>The default is <emphasis>no</emphasis>.</para>
+	    </listitem>
+	  </varlistentry>
+
 	</variablelist>
 </refsect1>
 
-- 
2.17.2


From 88ba12d08cfec93c66cd81e443be0afe4266f39b Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Thu, 4 Oct 2018 18:22:31 +0200
Subject: [PATCH 2/8] docs:vfs_fruit: add "delete_empty_adfiles" option

Bug: https://bugzilla.samba.org/show_bug.cgi?id=13642

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 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 0df5a7e5a79..e4ca7bd1828 100644
--- a/docs-xml/manpages/vfs_fruit.8.xml
+++ b/docs-xml/manpages/vfs_fruit.8.xml
@@ -392,6 +392,17 @@
 	    </listitem>
 	  </varlistentry>
 
+	  <varlistentry>
+	    <term>fruit:delete_empty_adfiles = yes | no</term>
+	    <listitem>
+	      <para>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.</para>
+	      <para>The default is <emphasis>no</emphasis>.</para>
+	    </listitem>
+	  </varlistentry>
+
 	</variablelist>
 </refsect1>
 
-- 
2.17.2


From fc49a26df16b2ec29cb26d110b7da355f42a4d9d Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Thu, 4 Oct 2018 13:47:20 +0200
Subject: [PATCH 3/8] s3:selftest: list vfs testssuites one per line

Bug: https://bugzilla.samba.org/show_bug.cgi?id=13642

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 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 e03c02c018f..38df45f1fcc 100755
--- a/source3/selftest/tests.py
+++ b/source3/selftest/tests.py
@@ -436,7 +436,13 @@ libsmbclient = ["libsmbclient.version", "libsmbclient.initialize",
                 "libsmbclient.options", "libsmbclient.opendir",
                 "libsmbclient.list_shares", "libsmbclient.readdirplus"]
 
-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 6dcc787783bc7dadb9f4f5739919eb391395d3a9 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Thu, 4 Oct 2018 14:28:15 +0200
Subject: [PATCH 4/8] s4:torture: add test for AppleDouble ResourceFork
 conversion

Bug: https://bugzilla.samba.org/show_bug.cgi?id=13642

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 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 356b4d3f82a..f7957cf3a7d 100755
--- a/selftest/target/Samba3.pm
+++ b/selftest/target/Samba3.pm
@@ -2006,6 +2006,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 38df45f1fcc..7dd96f358c1 100755
--- a/source3/selftest/tests.py
+++ b/source3/selftest/tests.py
@@ -442,6 +442,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
@@ -552,6 +553,9 @@ tests = base + raw + smb2 + rpc + unix + local + rap + nbt + libsmbclient + idma
         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 58a94dd143c..2eaf698c4e0 100644
--- a/source4/torture/vfs/fruit.c
+++ b/source4/torture/vfs/fruit.c
@@ -5412,3 +5412,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 1a3d3c5d0d153bab5ed7e12d113627f64678217d Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Wed, 3 Oct 2018 12:01:00 +0200
Subject: [PATCH 5/8] vfs_fruit: add option
 "wipe_intentionally_left_blank_rfork"

Bug: https://bugzilla.samba.org/show_bug.cgi?id=13642

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 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 cbb9e09e859..421ea3d8399 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,
@@ -2094,6 +2095,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 3bf164842f224a87ea320caa50db42d0b557d1a7 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Tue, 2 Oct 2018 16:05:28 +0200
Subject: [PATCH 6/8] 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 <slow at samba.org>
---
 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 421ea3d8399..bff63483876 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;
@@ -1282,6 +1321,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
  *
@@ -1296,13 +1399,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 b4a68bc21f790d33e373a081fd711df03f51d82f Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Wed, 3 Oct 2018 12:01:00 +0200
Subject: [PATCH 7/8] vfs_fruit: add option "delete_empty_adfiles"

Bug: https://bugzilla.samba.org/show_bug.cgi?id=13642

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 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 bff63483876..677e40ffc9f 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,
@@ -2208,6 +2209,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 6b9effad3bb53ffbef763c34807bf3fcc4d5d480 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Tue, 9 Oct 2018 14:54:31 +0200
Subject: [PATCH 8/8] vfs_fruit: optionally delete AppleDouble files without
 Resourcefork data

Bug: https://bugzilla.samba.org/show_bug.cgi?id=13642

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 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 677e40ffc9f..ca6b0287ded 100644
--- a/source3/modules/vfs_fruit.c
+++ b/source3/modules/vfs_fruit.c
@@ -1386,6 +1386,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
  *
@@ -1426,6 +1463,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



More information about the samba-technical mailing list