[PATCH] Small (*cough*) patchset for vfs_fruit bug #12427

Ralph Böhme slow at samba.org
Tue Jan 31 11:56:45 UTC 2017


Hi Uri,

thanks a lot for looking into this! Comments below:

On Sun, Jan 29, 2017 at 09:13:41PM +0200, Uri Simchoni wrote:
> So far I got up to 35/56, although the catia patches deserve another
> look. I have some minor comments, see below.
> Will be looking at the rest, but it would take a few more days to get
> back to - hopefully next weekend.
>
> [10/56] -
> On error, shouldn't we be calling SMB_VFS_NEXT_CLOSE instead of
> SMB_VFS_CLOSE? The choice between SMB_VFS_XXX and SMB_VFS_NEXT_XXX is
> never an easy one. However, in the case of
> close-from-within-failed-open, it's the file we've just opened, and
> higher layers may have not seen the open return.

agreed, fixed.

> I also don't understand the BUGBUGBUG comment - fd_close_posix() *is*
> called if it's a regular file and we get to the bottom of the vfs stack.

Well, that's essentially c/p from vfs_streams_xattr. :/ That's probably a
historic artefact, removing it...

> [13/56] -
> That doesn't seem right... if we update btime out of the Mac metadata,
> we do it no matter where the metadata is stored.

Not quite. :) In all cases execept the Netatalk case the btime will also be set
and fetched by the client (!) via the usual SMB protocol semantics and Samba FSA
processing. But that's not the case with Netatalk. There the primary source for
bime is the Netatalk metadata xattr, updated in fruit_ntimes() and queried here
in update_btime().

> One way is to call
> fruit_open_meta(), read the metadata via fruit_pread_meta(), and parse
> the metadata.
> 
> BTW, I was a bit puzzled by the use of update_btime() while stat'ing a
> stream. What is the meaning of stat'ing a stream?

just like a stat on the file: get the file metadata. The VFS backends then
implement this somehow, iirc streas_xattr stats the basefile while streams_depot
stats the actual stream file in the "depot".

> stat is a UNIX concept and a stream is a Windows (Mac too...) concept. Are
> there any "rules" of what should a stat of a stream return?

Afacit this stuff is somewhat underspecified is the MS protocol specs. There are
also no tests for this in the MS protocol test suite. I asked them for a pointer
to stream related tests while attending MS-IOLab and it turned out there aren't
any.

> [15/56] -
> Seems like readdir_attr_meta_finderi_stream() would work for netatalk
> too, since we know how to open and read the meta stream no matter where
> it's being stored. Is the separate netatalk implementation a performance
> optimization?

yes. Going through create_file sucks, but there's no other way from
readdir_attr_meta_finderi_stream().

> [17/56]
> 
> > +static int fruit_unlink_rsrc_xattr(vfs_handle_struct *handle,
> > +                                  const struct smb_filename *smb_fname,
> > +                                  bool force_unlink)
> > +{
> > +       /* Nothing to do here, removing the file will remove the xattr */
>
> About this comment - If I understand correctly, we do nothing because
> OS-X don't delete the resource.

This comments describes OS X SMB *server* behaviour. That one ignores unlink
requests on the resource fork (no idea at which layer, may just be the OS X SMB
server FSA-layer that exposes this behaviour).

> [26/56]
> in fruit_streaminfo_meta_netatalk():
> > +       if (is_fi_empty) {
> > +               return NT_STATUS_OK;
> > +       }
> > +
>
> I think this early return doesn't let us remove the :$DATA "stream".

good catch! Fixed in the updated patchset.

> in fruit_streaminfo_rsrc_xattr():
> Seems like the code assumes that someone already added the resource
> stream, and we just have to filter it if it's empty.

This function is only called for the case when fruit:resource=xattr which
only works on Solaris, as it uses the Solaris xattr API (which supports xattrs
of arbitrary size and the POSIX file handle base API) and it is indeed
incomplete. Thanks for spotting this.

I added the code to handle fruit:resource=xattr on Solaris to the initial
vfs_fruit version, but it is long a time ago since I last tested it.

It was broken in several places before this patchset and the patchset improves
on this in some places but not all.

To be honest, I'm not aware of anyone using fruit:resource=xattr (on a Solaris
(or derived) system), so lacking time to properly implement and test it I see
two options:

- mark it as incomplete and broken (via a patch for the manpage on top)

- remove it

What do you think?

> This might be the case if we have streams_xattr below, but:
>
> 1. Streams_xattr could prvide us with a wrongly-named stream, especially
> if streams_xattr:prefix is set
> 2. We may have another stream engine which doesn't tough extended
> attributes.
> 
> [28/56]
> > +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);
> > +       }
> > +
> > +       return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
> > +}
> > +
> For safety against future mistakes, and also for readability, I would
> enclose the _NEXT call with #ifdef HAVE_ATTROPEN.

How did you spot that one?! :) Fixed in updated patchset.

> [30/56] and [31/56]:
> Doing path translation inside a handle-based function raises some
> warning flags inside my head. True, it's been that way before. But now,
> other users of vfs_catia (assuming they exist :)) pay the toll as well.

And they get the fix is well -- which this is all about. Not doing the
translation on the names attached to handles is just a bug.

> The file name gets analyze on every pread().
> 
> Maybe you can cache the path during open to avoid this? Even as a
> follow-up patch set...

Yup. It's probably simple, just attach the path as an fsp extension, but I don't
have the time atm, so I'd prefer postponing this.

Updated patchset, also rebased on current master, attached.

Cheerio!
-slow
-------------- next part --------------
From 5f6f4c424dced22586ce812cf9e61d839a7d0473 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Tue, 15 Nov 2016 13:07:12 +0100
Subject: [PATCH 01/56] selftest: don't run vfs_fruit tests against ad_dc env

This is just redundant and didn't add test coverage.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12427

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 source3/selftest/tests.py | 1 -
 1 file changed, 1 deletion(-)

diff --git a/source3/selftest/tests.py b/source3/selftest/tests.py
index 8e1c33d..88cdd6a 100755
--- a/source3/selftest/tests.py
+++ b/source3/selftest/tests.py
@@ -418,7 +418,6 @@ for t in tests:
         plansmbtorture4testsuite(t, "ad_dc", '//$SERVER/tmp -U$USERNAME%$PASSWORD')
     elif t == "vfs.fruit":
         plansmbtorture4testsuite(t, "nt4_dc", '//$SERVER_IP/vfs_fruit -U$USERNAME%$PASSWORD --option=torture:localdir=$SELFTEST_PREFIX/nt4_dc/share')
-        plansmbtorture4testsuite(t, "ad_dc", '//$SERVER_IP/vfs_fruit -U$USERNAME%$PASSWORD --option=torture:localdir=$SELFTEST_PREFIX/ad_dc/share')
     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)")
-- 
2.9.3


From 2477bd51e71c1f31355debbea7a189ce6788eb5b Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Wed, 16 Nov 2016 11:24:59 +0100
Subject: [PATCH 02/56] s3/includes: add FinderInfo offset define to
 MacExtensions.h

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12427

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 source3/include/MacExtensions.h | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/source3/include/MacExtensions.h b/source3/include/MacExtensions.h
index 23dcde9..e17d39b 100644
--- a/source3/include/MacExtensions.h
+++ b/source3/include/MacExtensions.h
@@ -51,6 +51,9 @@
 #define AFP_Version			0x00000100
 #define AFP_BackupTime		0x80000000
 #define AFP_FinderSize		32
+
+#define AFP_OFF_FinderInfo	16
+
 /*
 ** Orginal AFP_AfpInfo stream used by NT 
 ** We needed a way to store the create date so SAMBA
-- 
2.9.3


From 5a3d5c4da326e7c7a9183bbc126ba8308bad7bd5 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Thu, 1 Dec 2016 11:17:48 +0100
Subject: [PATCH 03/56] vfs_streams_xattr: call SMB_VFS_OPEN with
 smb_fname_base

In case an SMB_VFS_OPEN() on a stream basename fails with EISDIR, we
retry the open as O_RDONLY. The retry should be done with the
smb_fname_base as well.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12427

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 source3/modules/vfs_streams_xattr.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/source3/modules/vfs_streams_xattr.c b/source3/modules/vfs_streams_xattr.c
index d9eb2e1..2d797f0 100644
--- a/source3/modules/vfs_streams_xattr.c
+++ b/source3/modules/vfs_streams_xattr.c
@@ -452,18 +452,18 @@ static int streams_xattr_open(vfs_handle_struct *handle,
         hostfd = SMB_VFS_OPEN(handle->conn, smb_fname_base, fsp,
 			      baseflags, mode);
 
-	TALLOC_FREE(smb_fname_base);
-
         /* It is legit to open a stream on a directory, but the base
          * fd has to be read-only.
          */
         if ((hostfd == -1) && (errno == EISDIR)) {
                 baseflags &= ~O_ACCMODE;
                 baseflags |= O_RDONLY;
-                hostfd = SMB_VFS_OPEN(handle->conn, smb_fname, fsp, baseflags,
+                hostfd = SMB_VFS_OPEN(handle->conn, smb_fname_base, fsp, baseflags,
 				      mode);
         }
 
+	TALLOC_FREE(smb_fname_base);
+
         if (hostfd == -1) {
 		goto fail;
         }
-- 
2.9.3


From b4e5065be1cb3cd2d82cb08334622033dfafd6ae Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Wed, 16 Nov 2016 09:34:13 +0100
Subject: [PATCH 04/56] vfs_fruit: fix fruit_pread with metadata=stream

This make the test "fix offset and len handling for AFP_AfpInfo stream"
pass with fruit:metadata=stream.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12427

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 source3/modules/vfs_fruit.c | 37 +++++++++++++++++++++----------------
 1 file changed, 21 insertions(+), 16 deletions(-)

diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c
index 3599dcb..d3cb1d8 100644
--- a/source3/modules/vfs_fruit.c
+++ b/source3/modules/vfs_fruit.c
@@ -2692,6 +2692,7 @@ static ssize_t fruit_pread(vfs_handle_struct *handle,
 	char *name = NULL;
 	char *tmp_base_name = NULL;
 	NTSTATUS status;
+	size_t to_return = n;
 
 	DEBUG(10, ("fruit_pread: offset=%d, size=%d\n", (int)offset, (int)n));
 
@@ -2721,8 +2722,27 @@ static ssize_t fruit_pread(vfs_handle_struct *handle,
 	}
 	fsp->base_fsp->fsp_name->base_name = name;
 
+	if (is_afpinfo_stream(fsp->fsp_name)) {
+		/*
+		 * OS X has a off-by-1 error in the offset calculation, so we're
+		 * bug compatible here. It won't hurt, as any relevant real
+		 * world read requests from the AFP_AfpInfo stream will be
+		 * offset=0 n=60. offset is ignored anyway, see below.
+		 */
+		if ((offset < 0) || (offset >= AFP_INFO_SIZE + 1)) {
+			len = 0;
+			rc = 0;
+			goto exit;
+		}
+
+		to_return = MIN(n, AFP_INFO_SIZE);
+
+		/* Yes, macOS always reads from offset 0 */
+		offset = 0;
+	}
+
 	if (ad == NULL) {
-		len = SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
+		len = SMB_VFS_NEXT_PREAD(handle, fsp, data, to_return, offset);
 		if (len == -1) {
 			rc = -1;
 			goto exit;
@@ -2737,21 +2757,6 @@ static ssize_t fruit_pread(vfs_handle_struct *handle,
 
 	if (ad->ad_type == ADOUBLE_META) {
 		char afpinfo_buf[AFP_INFO_SIZE];
-		size_t to_return;
-
-		/*
-		 * OS X has a off-by-1 error in the offset calculation, so we're
-		 * bug compatible here. It won't hurt, as any relevant real
-		 * world read requests from the AFP_AfpInfo stream will be
-		 * offset=0 n=60. offset is ignored anyway, see below.
-		 */
-		if ((offset < 0) || (offset >= AFP_INFO_SIZE + 1)) {
-			len = 0;
-			rc = 0;
-			goto exit;
-		}
-
-		to_return = MIN(n, AFP_INFO_SIZE);
 
 		ai = afpinfo_new(talloc_tos());
 		if (ai == NULL) {
-- 
2.9.3


From 6cae675ab35be76b94192e6b3eead29ba149c025 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Tue, 15 Nov 2016 20:32:05 +0100
Subject: [PATCH 05/56] vfs_fruit: fix fruit_ftruncate with metadata=stream

With this the test "setinfo eof AFP_AfpInfo" passed with
fruit:metadata=stream.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12427

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 source3/modules/vfs_fruit.c | 44 ++++++++++++++++----------------------------
 1 file changed, 16 insertions(+), 28 deletions(-)

diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c
index d3cb1d8..6e7e3bb 100644
--- a/source3/modules/vfs_fruit.c
+++ b/source3/modules/vfs_fruit.c
@@ -3325,30 +3325,6 @@ static int fruit_fallocate(struct vfs_handle_struct *handle,
 	return -1;
 }
 
-static int fruit_ftruncate_meta(struct vfs_handle_struct *handle,
-				struct files_struct *fsp,
-				off_t offset,
-				struct adouble *ad)
-{
-	struct fruit_config_data *config;
-
-	SMB_VFS_HANDLE_GET_DATA(handle, config,
-				struct fruit_config_data, return -1);
-
-	if (offset > 60) {
-		DBG_WARNING("ftruncate %s to %jd",
-			    fsp_str_dbg(fsp), (intmax_t)offset);
-		/* OS X returns NT_STATUS_ALLOTTED_SPACE_EXCEEDED  */
-		errno = EOVERFLOW;
-		return -1;
-	}
-
-	DBG_WARNING("ignoring ftruncate %s to %jd",
-		    fsp_str_dbg(fsp), (intmax_t)offset);
-	/* OS X returns success but does nothing  */
-	return 0;
-}
-
 static int fruit_ftruncate_rsrc(struct vfs_handle_struct *handle,
 				struct files_struct *fsp,
 				off_t offset,
@@ -3396,6 +3372,21 @@ static int fruit_ftruncate(struct vfs_handle_struct *handle,
 	DBG_DEBUG("fruit_ftruncate called for file %s offset %.0f\n",
 		   fsp_str_dbg(fsp), (double)offset);
 
+	if (is_afpinfo_stream(fsp->fsp_name)) {
+		if (offset > 60) {
+			DBG_WARNING("ftruncate %s to %jd",
+				    fsp_str_dbg(fsp), (intmax_t)offset);
+			/* OS X returns NT_STATUS_ALLOTTED_SPACE_EXCEEDED  */
+			errno = EOVERFLOW;
+			return -1;
+		}
+
+		DBG_WARNING("ignoring ftruncate %s to %jd",
+			    fsp_str_dbg(fsp), (intmax_t)offset);
+		/* OS X returns success but does nothing  */
+		return 0;
+	}
+
 	if (ad == NULL) {
 		return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
 	}
@@ -3405,15 +3396,12 @@ static int fruit_ftruncate(struct vfs_handle_struct *handle,
 	}
 
 	switch (ad->ad_type) {
-	case ADOUBLE_META:
-		rc = fruit_ftruncate_meta(handle, fsp, offset, ad);
-		break;
-
 	case ADOUBLE_RSRC:
 		rc = fruit_ftruncate_rsrc(handle, fsp, offset, ad);
 		break;
 
 	default:
+		DBG_ERR("unexpected ad_type [%d]\n", ad->ad_type);
 		return -1;
 	}
 
-- 
2.9.3


From cf4deae7ff9e42f38f95524db9fc4899fc66bb45 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Fri, 9 Dec 2016 16:25:38 +0100
Subject: [PATCH 06/56] vfs_fruit: rename empty_finderinfo() and make it more
 robust

No change in behaviour, but ad_entry(ad, ADEID_FINDERI) can in theory
return NULL.

The next commit will add the same function for a AfpInfo type, so rename
this function that works on struct adouble to ad_empty_finderinfo().

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12427

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 source3/modules/vfs_fruit.c | 22 +++++++++++++---------
 1 file changed, 13 insertions(+), 9 deletions(-)

diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c
index 6e7e3bb..68b8bec 100644
--- a/source3/modules/vfs_fruit.c
+++ b/source3/modules/vfs_fruit.c
@@ -1578,16 +1578,20 @@ static bool del_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
 	return true;
 }
 
-static bool empty_finderinfo(const struct adouble *ad)
+static bool ad_empty_finderinfo(const struct adouble *ad)
 {
-
+	int cmp;
 	char emptybuf[ADEDLEN_FINDERI] = {0};
-	if (memcmp(emptybuf,
-		   ad_entry(ad, ADEID_FINDERI),
-		   ADEDLEN_FINDERI) == 0) {
-		return true;
+	char *fi = NULL;
+
+	fi = ad_entry(ad, ADEID_FINDERI);
+	if (fi == NULL) {
+		DBG_ERR("Missing FinderInfo in struct adouble [%p]\n", ad);
+		return false;
 	}
-	return false;
+
+	cmp = memcmp(emptybuf, fi, ADEDLEN_FINDERI);
+	return (cmp == 0);
 }
 
 /**
@@ -2873,7 +2877,7 @@ static ssize_t fruit_pwrite(vfs_handle_struct *handle,
 		}
 		memcpy(ad_entry(ad, ADEID_FINDERI),
 		       &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
-		if (empty_finderinfo(ad)) {
+		if (ad_empty_finderinfo(ad)) {
 			/* Discard metadata */
 			if (config->meta == FRUIT_META_STREAM) {
 				rc = SMB_VFS_FTRUNCATE(fsp, 0);
@@ -3220,7 +3224,7 @@ static NTSTATUS fruit_streaminfo(vfs_handle_struct *handle,
 	if (config->meta == FRUIT_META_NETATALK) {
 		ad = ad_get(talloc_tos(), handle,
 			    smb_fname->base_name, ADOUBLE_META);
-		if (ad && !empty_finderinfo(ad)) {
+		if (ad && !ad_empty_finderinfo(ad)) {
 			if (!add_fruit_stream(
 				    mem_ctx, pnum_streams, pstreams,
 				    AFPINFO_STREAM_NAME, AFP_INFO_SIZE,
-- 
2.9.3


From 4cfbd23df610cf1c318ddc2323d23c850ef7cf9b Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Tue, 15 Nov 2016 21:32:25 +0100
Subject: [PATCH 07/56] vfs_fruit: fix fruit_pwrite() with metadata=stream

This makes the test "delete AFP_AfpInfo by writing all 0" pass with
fruit:metadata=stream.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12427

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 source3/modules/vfs_fruit.c | 83 +++++++++++++++++++++++++++++++--------------
 1 file changed, 58 insertions(+), 25 deletions(-)

diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c
index 68b8bec..1b10668 100644
--- a/source3/modules/vfs_fruit.c
+++ b/source3/modules/vfs_fruit.c
@@ -1594,6 +1594,15 @@ static bool ad_empty_finderinfo(const struct adouble *ad)
 	return (cmp == 0);
 }
 
+static bool ai_empty_finderinfo(const AfpInfo *ai)
+{
+	int cmp;
+	char emptybuf[ADEDLEN_FINDERI] = {0};
+
+	cmp = memcmp(emptybuf, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
+	return (cmp == 0);
+}
+
 /**
  * Update btime with btime from Netatalk
  **/
@@ -2849,21 +2858,14 @@ static ssize_t fruit_pwrite(vfs_handle_struct *handle,
 	}
 	fsp->base_fsp->fsp_name->base_name = name;
 
-	if (ad == NULL) {
-		len = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
-		if (len != n) {
-			rc = -1;
-			goto exit;
-		}
-		goto exit;
-	}
-
-	if (!fruit_fsp_recheck(ad, fsp)) {
-		rc = -1;
-		goto exit;
-	}
-
-	if (ad->ad_type == ADOUBLE_META) {
+	if (is_afpinfo_stream(fsp->fsp_name)) {
+		/*
+		 * Writing an all 0 blob to the metadata stream
+		 * results in the stream being removed on a macOS
+		 * server. This ensures we behave the same and it
+		 * verified by the "delete AFP_AfpInfo by writing all
+		 * 0" test.
+		 */
 		if (n != AFP_INFO_SIZE || offset != 0) {
 			DEBUG(1, ("unexpected offset=%jd or size=%jd\n",
 				  (intmax_t)offset, (intmax_t)n));
@@ -2875,25 +2877,56 @@ static ssize_t fruit_pwrite(vfs_handle_struct *handle,
 			rc = -1;
 			goto exit;
 		}
-		memcpy(ad_entry(ad, ADEID_FINDERI),
-		       &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
-		if (ad_empty_finderinfo(ad)) {
-			/* Discard metadata */
-			if (config->meta == FRUIT_META_STREAM) {
-				rc = SMB_VFS_FTRUNCATE(fsp, 0);
-			} else {
-				rc = SMB_VFS_REMOVEXATTR(handle->conn,
-							 fsp->fsp_name->base_name,
-							 AFPINFO_EA_NETATALK);
+
+		if (ai_empty_finderinfo(ai)) {
+			switch (config->meta) {
+			case FRUIT_META_STREAM:
+				rc = SMB_VFS_UNLINK(handle->conn, fsp->fsp_name);
+				break;
+
+			case FRUIT_META_NETATALK:
+				rc = SMB_VFS_REMOVEXATTR(
+					handle->conn,
+					fsp->fsp_name->base_name,
+					AFPINFO_EA_NETATALK);
+				break;
+
+			default:
+				DBG_ERR("Unexpected meta config [%d]\n",
+					config->meta);
+				rc = -1;
+				goto exit;
 			}
+
 			if (rc != 0 && errno != ENOENT && errno != ENOATTR) {
 				DBG_WARNING("Can't delete metadata for %s: %s\n",
 					    fsp->fsp_name->base_name, strerror(errno));
 				goto exit;
 			}
+
 			rc = 0;
 			goto exit;
 		}
+	}
+
+	if (ad == NULL) {
+		len = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
+		if (len != n) {
+			rc = -1;
+			goto exit;
+		}
+		goto exit;
+	}
+
+	if (!fruit_fsp_recheck(ad, fsp)) {
+		rc = -1;
+		goto exit;
+	}
+
+	if (ad->ad_type == ADOUBLE_META) {
+		memcpy(ad_entry(ad, ADEID_FINDERI),
+		       &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
+
 		rc = ad_write(ad, name);
 	} else {
 		len = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n,
-- 
2.9.3


From efb19e92c9ec444b6770c09bf969851ee3f0f8dd Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Wed, 16 Nov 2016 11:01:45 +0100
Subject: [PATCH 08/56] vfs_fruit: replace unsafe ad_entry macro with a
 function

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12427

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 source3/modules/vfs_fruit.c | 113 +++++++++++++++++++++++++++++++++-----------
 1 file changed, 86 insertions(+), 27 deletions(-)

diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c
index 1b10668..0686f93 100644
--- a/source3/modules/vfs_fruit.c
+++ b/source3/modules/vfs_fruit.c
@@ -343,7 +343,6 @@ typedef enum {ADOUBLE_META, ADOUBLE_RSRC} adouble_type_t;
 #define ad_getentryoff(ad,eid)     ((ad)->ad_eid[(eid)].ade_off)
 #define ad_setentrylen(ad,eid,len) ((ad)->ad_eid[(eid)].ade_len = (len))
 #define ad_setentryoff(ad,eid,off) ((ad)->ad_eid[(eid)].ade_off = (off))
-#define ad_entry(ad,eid)           ((ad)->ad_data + ad_getentryoff((ad),(eid)))
 
 struct ad_entry {
 	size_t ade_off;
@@ -414,6 +413,23 @@ static int ad_write(struct adouble *ad, const char *path);
 static int adouble_path(TALLOC_CTX *ctx, const char *path_in, char **path_out);
 
 /**
+ * Return a pointer to an AppleDouble entry
+ *
+ * Returns NULL if the entry is not present
+ **/
+static char *ad_get_entry(const struct adouble *ad, int eid)
+{
+	off_t off = ad_getentryoff(ad, eid);
+	size_t len = ad_getentrylen(ad, eid);
+
+	if (off == 0 || len == 0) {
+		return NULL;
+	}
+
+	return ad->ad_data + off;
+}
+
+/**
  * Get a date
  **/
 static int ad_getdate(const struct adouble *ad,
@@ -421,18 +437,19 @@ static int ad_getdate(const struct adouble *ad,
 		      uint32_t *date)
 {
 	bool xlate = (dateoff & AD_DATE_UNIX);
+	char *p = NULL;
 
 	dateoff &= AD_DATE_MASK;
-	if (!ad_getentryoff(ad, ADEID_FILEDATESI)) {
+	p = ad_get_entry(ad, ADEID_FILEDATESI);
+	if (p == NULL) {
 		return -1;
 	}
 
 	if (dateoff > AD_DATE_ACCESS) {
 	    return -1;
 	}
-	memcpy(date,
-	       ad_entry(ad, ADEID_FILEDATESI) + dateoff,
-	       sizeof(uint32_t));
+
+	memcpy(date, p + dateoff, sizeof(uint32_t));
 
 	if (xlate) {
 		*date = AD_DATE_TO_UNIX(*date);
@@ -446,9 +463,11 @@ static int ad_getdate(const struct adouble *ad,
 static int ad_setdate(struct adouble *ad, unsigned int dateoff, uint32_t date)
 {
 	bool xlate = (dateoff & AD_DATE_UNIX);
+	char *p = NULL;
 
-	if (!ad_getentryoff(ad, ADEID_FILEDATESI)) {
-		return 0;
+	p = ad_get_entry(ad, ADEID_FILEDATESI);
+	if (p == NULL) {
+		return -1;
 	}
 
 	dateoff &= AD_DATE_MASK;
@@ -460,7 +479,7 @@ static int ad_setdate(struct adouble *ad, unsigned int dateoff, uint32_t date)
 		return -1;
 	}
 
-	memcpy(ad_entry(ad, ADEID_FILEDATESI) + dateoff, &date, sizeof(date));
+	memcpy(p + dateoff, &date, sizeof(date));
 
 	return 0;
 }
@@ -946,6 +965,9 @@ static ssize_t ad_header_read_rsrc(struct adouble *ad, const char *path)
 
 		if ((mode == O_RDWR)
 		    && (ad_getentrylen(ad, ADEID_FINDERI) > ADEDLEN_FINDERI)) {
+			char *p_ad = NULL;
+			char *p_meta_ad = NULL;
+
 			rc = ad_convert(ad, fd);
 			if (rc != 0) {
 				rc = -1;
@@ -975,9 +997,18 @@ static ssize_t ad_header_read_rsrc(struct adouble *ad, const char *path)
 				goto exit;
 			}
 
-			memcpy(ad_entry(meta_ad, ADEID_FINDERI),
-			       ad_entry(ad, ADEID_FINDERI),
-			       ADEDLEN_FINDERI);
+			p_ad = ad_get_entry(ad, ADEID_FINDERI);
+			if (p_ad == NULL) {
+				rc = -1;
+				goto exit;
+			}
+			p_meta_ad = ad_get_entry(meta_ad, ADEID_FINDERI);
+			if (p_meta_ad == NULL) {
+				rc = -1;
+				goto exit;
+			}
+
+			memcpy(p_meta_ad, p_ad, ADEDLEN_FINDERI);
 
 			rc = ad_write(meta_ad, path);
 			if (rc != 0) {
@@ -1584,7 +1615,7 @@ static bool ad_empty_finderinfo(const struct adouble *ad)
 	char emptybuf[ADEDLEN_FINDERI] = {0};
 	char *fi = NULL;
 
-	fi = ad_entry(ad, ADEID_FINDERI);
+	fi = ad_get_entry(ad, ADEID_FINDERI);
 	if (fi == NULL) {
 		DBG_ERR("Missing FinderInfo in struct adouble [%p]\n", ad);
 		return false;
@@ -2009,23 +2040,32 @@ static NTSTATUS readdir_attr_macmeta(struct vfs_handle_struct *handle,
 		ad = ad_get(talloc_tos(), handle, smb_fname->base_name,
 			    ADOUBLE_META);
 		if (ad) {
+			char *p = ad_get_entry(ad, ADEID_FINDERI);
+
+			if (p == NULL) {
+				DBG_ERR("No ADEID_FINDERI for [%s]\n",
+					smb_fname->base_name);
+				status = NT_STATUS_INVALID_PARAMETER;
+				goto out;
+			}
+
 			if (S_ISREG(smb_fname->st.st_ex_mode)) {
 				/* finder_type */
 				memcpy(&attr_data->attr_data.aapl.finder_info[0],
-				       ad_entry(ad, ADEID_FINDERI), 4);
+				       p, 4);
 
 				/* finder_creator */
 				memcpy(&attr_data->attr_data.aapl.finder_info[0] + 4,
-				       ad_entry(ad, ADEID_FINDERI) + 4, 4);
+				       p + 4, 4);
 			}
 
 			/* finder_flags */
 			memcpy(&attr_data->attr_data.aapl.finder_info[0] + 8,
-			       ad_entry(ad, ADEID_FINDERI) + 8, 2);
+			       p + 8, 2);
 
 			/* finder_ext_flags */
 			memcpy(&attr_data->attr_data.aapl.finder_info[0] + 10,
-			       ad_entry(ad, ADEID_FINDERI) + 24, 2);
+			       p + 24, 2);
 
 			/* creation date */
 			date_added = convert_time_t_to_uint32_t(
@@ -2036,6 +2076,7 @@ static NTSTATUS readdir_attr_macmeta(struct vfs_handle_struct *handle,
 		}
 	}
 
+out:
 	TALLOC_FREE(ad);
 	return status;
 }
@@ -2770,6 +2811,7 @@ static ssize_t fruit_pread(vfs_handle_struct *handle,
 
 	if (ad->ad_type == ADOUBLE_META) {
 		char afpinfo_buf[AFP_INFO_SIZE];
+		char *p = NULL;
 
 		ai = afpinfo_new(talloc_tos());
 		if (ai == NULL) {
@@ -2783,9 +2825,16 @@ static ssize_t fruit_pread(vfs_handle_struct *handle,
 			goto exit;
 		}
 
-		memcpy(&ai->afpi_FinderInfo[0],
-		       ad_entry(ad, ADEID_FINDERI),
-		       ADEDLEN_FINDERI);
+		p = ad_get_entry(ad, ADEID_FINDERI);
+		if (p == NULL) {
+			DBG_ERR("No ADEID_FINDERI for [%s]\n",
+				fsp->fsp_name->base_name);
+			rc = -1;
+			goto exit;
+		}
+
+		memcpy(&ai->afpi_FinderInfo[0], p, ADEDLEN_FINDERI);
+
 		len = afpinfo_pack(ai, afpinfo_buf);
 		if (len != AFP_INFO_SIZE) {
 			rc = -1;
@@ -2924,9 +2973,17 @@ static ssize_t fruit_pwrite(vfs_handle_struct *handle,
 	}
 
 	if (ad->ad_type == ADOUBLE_META) {
-		memcpy(ad_entry(ad, ADEID_FINDERI),
-		       &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
+		char *p = NULL;
 
+		p = ad_get_entry(ad, ADEID_FINDERI);
+		if (p == NULL) {
+			DBG_ERR("No ADEID_FINDERI for [%s]\n",
+				fsp->fsp_name->base_name);
+			rc = -1;
+			goto exit;
+		}
+
+		memcpy(p, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
 		rc = ad_write(ad, name);
 	} else {
 		len = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n,
@@ -3255,14 +3312,16 @@ static NTSTATUS fruit_streaminfo(vfs_handle_struct *handle,
 	DEBUG(10, ("fruit_streaminfo called for %s\n", smb_fname->base_name));
 
 	if (config->meta == FRUIT_META_NETATALK) {
+		bool ok;
+
 		ad = ad_get(talloc_tos(), handle,
 			    smb_fname->base_name, ADOUBLE_META);
-		if (ad && !ad_empty_finderinfo(ad)) {
-			if (!add_fruit_stream(
-				    mem_ctx, pnum_streams, pstreams,
-				    AFPINFO_STREAM_NAME, AFP_INFO_SIZE,
-				    smb_roundup(handle->conn,
-						AFP_INFO_SIZE))) {
+		if ((ad != NULL) && !ad_empty_finderinfo(ad)) {
+			ok = add_fruit_stream(
+				mem_ctx, pnum_streams, pstreams,
+				AFPINFO_STREAM_NAME, AFP_INFO_SIZE,
+				smb_roundup(handle->conn, AFP_INFO_SIZE));
+			if (!ok) {
 				TALLOC_FREE(ad);
 				return NT_STATUS_NO_MEMORY;
 			}
-- 
2.9.3


From 531029816c26007fd30fd90380dcb2f6004944fc Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Tue, 29 Nov 2016 16:56:00 +0100
Subject: [PATCH 09/56] vfs_fruit: refactor fruit_open_meta()

Just split out the fruit:metadata=stream case into a helper function, no
change in behaviour.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12427

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 source3/modules/vfs_fruit.c | 59 +++++++++++++++++++++++++++++++++++----------
 1 file changed, 46 insertions(+), 13 deletions(-)

diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c
index 0686f93..85e18d1 100644
--- a/source3/modules/vfs_fruit.c
+++ b/source3/modules/vfs_fruit.c
@@ -2187,26 +2187,27 @@ static int fruit_connect(vfs_handle_struct *handle,
 	return rc;
 }
 
-static int fruit_open_meta(vfs_handle_struct *handle,
-			   struct smb_filename *smb_fname,
-			   files_struct *fsp, int flags, mode_t mode)
+static int fruit_open_meta_stream(vfs_handle_struct *handle,
+				  struct smb_filename *smb_fname,
+				  files_struct *fsp,
+				  int flags,
+				  mode_t mode)
+{
+	return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
+}
+
+static int fruit_open_meta_netatalk(vfs_handle_struct *handle,
+				    struct smb_filename *smb_fname,
+				    files_struct *fsp,
+				    int flags,
+				    mode_t mode)
 {
 	int rc = 0;
-	struct fruit_config_data *config = NULL;
 	struct smb_filename *smb_fname_base = NULL;
 	int baseflags;
 	int hostfd = -1;
 	struct adouble *ad = NULL;
 
-	DEBUG(10, ("fruit_open_meta for %s\n", smb_fname_str_dbg(smb_fname)));
-
-	SMB_VFS_HANDLE_GET_DATA(handle, config,
-				struct fruit_config_data, return -1);
-
-	if (config->meta == FRUIT_META_STREAM) {
-		return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
-	}
-
 	/* Create an smb_filename with stream_name == NULL. */
 	smb_fname_base = synthetic_smb_fname(talloc_tos(),
 					smb_fname->base_name,
@@ -2299,6 +2300,38 @@ exit:
 	return hostfd;
 }
 
+static int fruit_open_meta(vfs_handle_struct *handle,
+			   struct smb_filename *smb_fname,
+			   files_struct *fsp, int flags, mode_t mode)
+{
+	int rc;
+	struct fruit_config_data *config = NULL;
+
+	DBG_DEBUG("path [%s]\n", smb_fname_str_dbg(smb_fname));
+
+	SMB_VFS_HANDLE_GET_DATA(handle, config,
+				struct fruit_config_data, return -1);
+
+	switch (config->meta) {
+	case FRUIT_META_STREAM:
+		rc = fruit_open_meta_stream(handle, smb_fname,
+					    fsp, flags, mode);
+		break;
+
+	case FRUIT_META_NETATALK:
+		rc = fruit_open_meta_netatalk(handle, smb_fname,
+					      fsp, flags, mode);
+		break;
+
+	default:
+		DBG_ERR("Unexpected meta config [%d]\n", config->meta);
+		return -1;
+	}
+
+	DBG_DEBUG("path [%s] rc [%d]\n", smb_fname_str_dbg(smb_fname), rc);
+	return rc;
+}
+
 static int fruit_open_rsrc(vfs_handle_struct *handle,
 			   struct smb_filename *smb_fname,
 			   files_struct *fsp, int flags, mode_t mode)
-- 
2.9.3


From 04085034598f0953edcdf7a158dc3cc3bdbb2eaf Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Fri, 9 Dec 2016 17:01:37 +0100
Subject: [PATCH 10/56] vfs_fruit: correct fruit_open_meta_stream()
 implementation

This needs to create and write a metadata blob when the stream is
created.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12427

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 source3/modules/vfs_fruit.c | 56 ++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 55 insertions(+), 1 deletion(-)

diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c
index 85e18d1..eaf0fb5 100644
--- a/source3/modules/vfs_fruit.c
+++ b/source3/modules/vfs_fruit.c
@@ -2193,7 +2193,61 @@ static int fruit_open_meta_stream(vfs_handle_struct *handle,
 				  int flags,
 				  mode_t mode)
 {
-	return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
+	AfpInfo *ai = NULL;
+	char afpinfo_buf[AFP_INFO_SIZE];
+	ssize_t len, written;
+	int hostfd = -1;
+	int rc = -1;
+
+	hostfd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
+	if (hostfd == -1) {
+		return -1;
+	}
+
+	if (!(flags & (O_CREAT | O_TRUNC))) {
+		return hostfd;
+	}
+
+	ai = afpinfo_new(talloc_tos());
+	if (ai == NULL) {
+		rc = -1;
+		goto fail;
+	}
+
+	len = afpinfo_pack(ai, afpinfo_buf);
+	if (len != AFP_INFO_SIZE) {
+		rc = -1;
+		goto fail;
+	}
+
+	/* 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);
+	if (written != AFP_INFO_SIZE) {
+		DBG_ERR("bad write [%zd/%d]\n", written, AFP_INFO_SIZE);
+		rc = -1;
+		goto fail;
+	}
+
+	/* Reset fd, set above just for SMB_VFS_NEXT_PWRITE() */
+	fsp->fh->fd = -1;
+
+	rc = 0;
+fail:
+	DBG_DEBUG("rc=%d, fd=%d\n", rc, hostfd);
+
+	if (rc != 0) {
+		int saved_errno = errno;
+		if (hostfd >= 0) {
+			fsp->fh->fd = hostfd;
+			SMB_VFS_CLOSE(fsp);
+		}
+		hostfd = -1;
+		errno = saved_errno;
+	}
+	return hostfd;
 }
 
 static int fruit_open_meta_netatalk(vfs_handle_struct *handle,
-- 
2.9.3


From d98cb1362d0ddf536cfccf8399ad7087aeddd7fb Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Fri, 2 Dec 2016 11:05:50 +0100
Subject: [PATCH 11/56] vfs_fruit: refactor fruit_stat_meta()

Handle config->meta in helper functions. No change in behaviour. The next step
will add the correct implementation of fruit_stat_meta_stream().

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12427

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 source3/modules/vfs_fruit.c | 30 +++++++++++++++++++++++++++---
 1 file changed, 27 insertions(+), 3 deletions(-)

diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c
index eaf0fb5..29e4be6 100644
--- a/source3/modules/vfs_fruit.c
+++ b/source3/modules/vfs_fruit.c
@@ -3125,9 +3125,9 @@ static int fruit_stat_base(vfs_handle_struct *handle,
 	return rc;
 }
 
-static int fruit_stat_meta(vfs_handle_struct *handle,
-			   struct smb_filename *smb_fname,
-			   bool follow_links)
+static int fruit_stat_meta_netatalk(vfs_handle_struct *handle,
+				    struct smb_filename *smb_fname,
+				    bool follow_links)
 {
 	struct adouble *ad = NULL;
 
@@ -3150,6 +3150,30 @@ static int fruit_stat_meta(vfs_handle_struct *handle,
 	return 0;
 }
 
+static int fruit_stat_meta(vfs_handle_struct *handle,
+			   struct smb_filename *smb_fname,
+			   bool follow_links)
+{
+	struct fruit_config_data *config = NULL;
+	int ret;
+
+	SMB_VFS_HANDLE_GET_DATA(handle, config,
+				struct fruit_config_data, return -1);
+
+	switch (config->meta) {
+	case FRUIT_META_STREAM:
+	case FRUIT_META_NETATALK:
+		ret = fruit_stat_meta_netatalk(handle, smb_fname, follow_links);
+		break;
+
+	default:
+		DBG_ERR("Unexpected meta config [%d]\n", config->meta);
+		return -1;
+	}
+
+	return ret;
+}
+
 static int fruit_stat_rsrc(vfs_handle_struct *handle,
 			   struct smb_filename *smb_fname,
 			   bool follow_links)
-- 
2.9.3


From dccea9eaa87abf2a68510c002e3650a985e09a18 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Fri, 2 Dec 2016 11:12:18 +0100
Subject: [PATCH 12/56] vfs_fruit: correct fruit_stat_meta_stream()
 implementation

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12427

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 source3/modules/vfs_fruit.c | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c
index 29e4be6..e698b07 100644
--- a/source3/modules/vfs_fruit.c
+++ b/source3/modules/vfs_fruit.c
@@ -3125,6 +3125,21 @@ static int fruit_stat_base(vfs_handle_struct *handle,
 	return rc;
 }
 
+static int fruit_stat_meta_stream(vfs_handle_struct *handle,
+				  struct smb_filename *smb_fname,
+				  bool follow_links)
+{
+	int ret;
+
+	if (follow_links) {
+		ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
+	} else {
+		ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
+	}
+
+	return ret;
+}
+
 static int fruit_stat_meta_netatalk(vfs_handle_struct *handle,
 				    struct smb_filename *smb_fname,
 				    bool follow_links)
@@ -3162,6 +3177,9 @@ static int fruit_stat_meta(vfs_handle_struct *handle,
 
 	switch (config->meta) {
 	case FRUIT_META_STREAM:
+		ret = fruit_stat_meta_stream(handle, smb_fname, follow_links);
+		break;
+
 	case FRUIT_META_NETATALK:
 		ret = fruit_stat_meta_netatalk(handle, smb_fname, follow_links);
 		break;
-- 
2.9.3


From 5de0a2f7874f21a802ad597e4b630d7d612fa063 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Fri, 2 Dec 2016 17:25:47 +0100
Subject: [PATCH 13/56] vfs_fruit: update_btime() is only needed for
 metadata=netatalk

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12427

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 source3/modules/vfs_fruit.c | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c
index e698b07..5b37975 100644
--- a/source3/modules/vfs_fruit.c
+++ b/source3/modules/vfs_fruit.c
@@ -1643,6 +1643,21 @@ static void update_btime(vfs_handle_struct *handle,
 	uint32_t t;
 	struct timespec creation_time = {0};
 	struct adouble *ad;
+	struct fruit_config_data *config = NULL;
+
+	SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
+				return);
+
+	switch (config->meta) {
+	case FRUIT_META_STREAM:
+		return;
+	case FRUIT_META_NETATALK:
+		/* Handled below */
+		break;
+	default:
+		DBG_ERR("Unexpected meta config [%d]\n", config->meta);
+		return;
+	}
 
 	ad = ad_get(talloc_tos(), handle, smb_fname->base_name, ADOUBLE_META);
 	if (ad == NULL) {
-- 
2.9.3


From ec3bd1d9de67eeaaef0ffbad99d0c09c8e5e497f Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Thu, 1 Dec 2016 17:04:35 +0100
Subject: [PATCH 14/56] vfs_fruit: refactor readdir_attr_meta()

Move the FinderInfo stuff to helper functions, no change in behaviour so
far.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12427

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 source3/modules/vfs_fruit.c | 137 +++++++++++++++++++++++++++++++-------------
 1 file changed, 98 insertions(+), 39 deletions(-)

diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c
index 5b37975..430c29b 100644
--- a/source3/modules/vfs_fruit.c
+++ b/source3/modules/vfs_fruit.c
@@ -2016,14 +2016,108 @@ static NTSTATUS check_aapl(vfs_handle_struct *handle,
 	return status;
 }
 
+static bool readdir_attr_meta_finderi_stream(
+	struct vfs_handle_struct *handle,
+	const struct smb_filename *smb_fname,
+	AfpInfo *ai)
+{
+	return true;
+}
+
+static bool readdir_attr_meta_finderi_netatalk(
+	struct vfs_handle_struct *handle,
+	const struct smb_filename *smb_fname,
+	AfpInfo *ai)
+{
+	struct adouble *ad = NULL;
+	char *p = NULL;
+
+	ad = ad_get(talloc_tos(), handle, smb_fname->base_name, ADOUBLE_META);
+	if (ad == NULL) {
+		return false;
+	}
+
+	p = ad_get_entry(ad, ADEID_FINDERI);
+	if (p == NULL) {
+		DBG_ERR("No ADEID_FINDERI for [%s]\n", smb_fname->base_name);
+		TALLOC_FREE(ad);
+		return false;
+	}
+
+	memcpy(&ai->afpi_FinderInfo[0], p, AFP_FinderSize);
+	TALLOC_FREE(ad);
+	return true;
+}
+
+static bool readdir_attr_meta_finderi(struct vfs_handle_struct *handle,
+				      const struct smb_filename *smb_fname,
+				      struct readdir_attr_data *attr_data)
+{
+	struct fruit_config_data *config = NULL;
+	uint32_t date_added;
+	AfpInfo ai = {0};
+	bool ok;
+
+	SMB_VFS_HANDLE_GET_DATA(handle, config,
+				struct fruit_config_data,
+				return false);
+
+	switch (config->meta) {
+	case FRUIT_META_NETATALK:
+		ok = readdir_attr_meta_finderi_netatalk(
+			handle, smb_fname, &ai);
+		break;
+
+	case FRUIT_META_STREAM:
+		ok = readdir_attr_meta_finderi_stream(
+			handle, smb_fname, &ai);
+		break;
+
+	default:
+		DBG_ERR("Unexpected meta config [%d]\n", config->meta);
+		return false;
+	}
+
+	if (!ok) {
+		/* Don't bother with errors, it's likely ENOENT */
+		return true;
+	}
+
+	if (S_ISREG(smb_fname->st.st_ex_mode)) {
+		/* finder_type */
+		memcpy(&attr_data->attr_data.aapl.finder_info[0],
+		       &ai.afpi_FinderInfo[0], 4);
+
+		/* finder_creator */
+		memcpy(&attr_data->attr_data.aapl.finder_info[0] + 4,
+		       &ai.afpi_FinderInfo[4], 4);
+	}
+
+	/* finder_flags */
+	memcpy(&attr_data->attr_data.aapl.finder_info[0] + 8,
+	       &ai.afpi_FinderInfo[8], 2);
+
+	/* finder_ext_flags */
+	memcpy(&attr_data->attr_data.aapl.finder_info[0] + 10,
+	       &ai.afpi_FinderInfo[24], 2);
+
+	/* creation date */
+	date_added = convert_time_t_to_uint32_t(
+		smb_fname->st.st_ex_btime.tv_sec - AD_DATE_DELTA);
+
+	RSIVAL(&attr_data->attr_data.aapl.finder_info[0], 12, date_added);
+
+	return true;
+}
+
 static NTSTATUS readdir_attr_macmeta(struct vfs_handle_struct *handle,
 				     const struct smb_filename *smb_fname,
 				     struct readdir_attr_data *attr_data)
 {
 	NTSTATUS status = NT_STATUS_OK;
-	uint32_t date_added;
 	struct adouble *ad = NULL;
 	struct fruit_config_data *config = NULL;
+	bool ok;
 
 	SMB_VFS_HANDLE_GET_DATA(handle, config,
 				struct fruit_config_data,
@@ -2052,47 +2146,12 @@ static NTSTATUS readdir_attr_macmeta(struct vfs_handle_struct *handle,
 	 */
 
 	if (config->readdir_attr_finder_info) {
-		ad = ad_get(talloc_tos(), handle, smb_fname->base_name,
-			    ADOUBLE_META);
-		if (ad) {
-			char *p = ad_get_entry(ad, ADEID_FINDERI);
-
-			if (p == NULL) {
-				DBG_ERR("No ADEID_FINDERI for [%s]\n",
-					smb_fname->base_name);
-				status = NT_STATUS_INVALID_PARAMETER;
-				goto out;
-			}
-
-			if (S_ISREG(smb_fname->st.st_ex_mode)) {
-				/* finder_type */
-				memcpy(&attr_data->attr_data.aapl.finder_info[0],
-				       p, 4);
-
-				/* finder_creator */
-				memcpy(&attr_data->attr_data.aapl.finder_info[0] + 4,
-				       p + 4, 4);
-			}
-
-			/* finder_flags */
-			memcpy(&attr_data->attr_data.aapl.finder_info[0] + 8,
-			       p + 8, 2);
-
-			/* finder_ext_flags */
-			memcpy(&attr_data->attr_data.aapl.finder_info[0] + 10,
-			       p + 24, 2);
-
-			/* creation date */
-			date_added = convert_time_t_to_uint32_t(
-				smb_fname->st.st_ex_btime.tv_sec - AD_DATE_DELTA);
-			RSIVAL(&attr_data->attr_data.aapl.finder_info[0], 12, date_added);
-
-			TALLOC_FREE(ad);
+		ok = readdir_attr_meta_finderi(handle, smb_fname, attr_data);
+		if (!ok) {
+			status = NT_STATUS_INTERNAL_ERROR;
 		}
 	}
 
-out:
-	TALLOC_FREE(ad);
 	return status;
 }
 
-- 
2.9.3


From 0b74668bc860fa522592528440b1dc1f4da1fb39 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Fri, 9 Dec 2016 17:24:18 +0100
Subject: [PATCH 15/56] vfs_fruit: correct readdir_attr_meta_finderi_stream()
 implementation

This gets correct behaviour in readdir_attr_meta_finderi for the
metadata=stream case.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12427

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 source3/modules/vfs_fruit.c | 68 ++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 67 insertions(+), 1 deletion(-)

diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c
index 430c29b..08fb182 100644
--- a/source3/modules/vfs_fruit.c
+++ b/source3/modules/vfs_fruit.c
@@ -2021,7 +2021,73 @@ static bool readdir_attr_meta_finderi_stream(
 	const struct smb_filename *smb_fname,
 	AfpInfo *ai)
 {
-	return true;
+	struct smb_filename *stream_name = NULL;
+	files_struct *fsp = NULL;
+	ssize_t nread;
+	NTSTATUS status;
+	int ret;
+	bool ok;
+	uint8_t buf[AFP_INFO_SIZE];
+
+	stream_name = synthetic_smb_fname(talloc_tos(),
+					  smb_fname->base_name,
+					  AFPINFO_STREAM_NAME,
+					  NULL, smb_fname->flags);
+	if (stream_name == NULL) {
+		return false;
+	}
+
+	ret = SMB_VFS_STAT(handle->conn, stream_name);
+	if (ret != 0) {
+		return false;
+	}
+
+	status = SMB_VFS_CREATE_FILE(
+		handle->conn,                           /* conn */
+		NULL,                                   /* req */
+		0,                                      /* root_dir_fid */
+		stream_name,				/* fname */
+		FILE_READ_DATA,                         /* access_mask */
+		(FILE_SHARE_READ | FILE_SHARE_WRITE |   /* share_access */
+			FILE_SHARE_DELETE),
+		FILE_OPEN,                              /* 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,                                   /* pinfo */
+		NULL, NULL);				/* create context */
+
+	TALLOC_FREE(stream_name);
+
+	if (!NT_STATUS_IS_OK(status)) {
+		return false;
+	}
+
+	nread = SMB_VFS_PREAD(fsp, &buf[0], AFP_INFO_SIZE, 0);
+	if (nread != AFP_INFO_SIZE) {
+		DBG_ERR("short read [%s] [%zd/%d]\n",
+			smb_fname_str_dbg(stream_name), nread, AFP_INFO_SIZE);
+		ok = false;
+		goto fail;
+	}
+
+	memcpy(&ai->afpi_FinderInfo[0], &buf[AFP_OFF_FinderInfo],
+	       AFP_FinderSize);
+
+	ok = true;
+
+fail:
+	if (fsp != NULL) {
+		close_file(NULL, fsp, NORMAL_CLOSE);
+	}
+
+	return ok;
 }
 
 static bool readdir_attr_meta_finderi_netatalk(
-- 
2.9.3


From 15852ae9d503e707927c5f6a09b0eda14ecfab61 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Fri, 2 Dec 2016 08:47:36 +0100
Subject: [PATCH 16/56] vfs_fruit: fix fruit_rename() for the
 fruit:resource!=file case

o fix the config check, we must only run following code for the
  fruit:resource=file

o properly call SMB_VFS_NEXT_RENAME() instead diretly calling rename()

o bail out if we get an invalid stat

Otherwise, no change in behaviour.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12427

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 source3/modules/vfs_fruit.c | 55 +++++++++++++++++++++++++++++++++------------
 1 file changed, 41 insertions(+), 14 deletions(-)

diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c
index 08fb182..6603678 100644
--- a/source3/modules/vfs_fruit.c
+++ b/source3/modules/vfs_fruit.c
@@ -2691,19 +2691,27 @@ static int fruit_rename(struct vfs_handle_struct *handle,
 	char *src_adouble_path = NULL;
 	char *dst_adouble_path = NULL;
 	struct fruit_config_data *config = NULL;
+	struct smb_filename *src_adp_smb_fname = NULL;
+	struct smb_filename *dst_adp_smb_fname = NULL;
 
-	rc = SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
+	SMB_VFS_HANDLE_GET_DATA(handle, config,
+				struct fruit_config_data, return -1);
 
-	if (!VALID_STAT(smb_fname_src->st)
-	    || !S_ISREG(smb_fname_src->st.st_ex_mode)) {
-		return rc;
+	if (!VALID_STAT(smb_fname_src->st)) {
+		DBG_ERR("Need valid stat for [%s]\n",
+			smb_fname_str_dbg(smb_fname_src));
+		return -1;
 	}
 
-	SMB_VFS_HANDLE_GET_DATA(handle, config,
-				struct fruit_config_data, return -1);
+	rc = SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
+	if (rc != 0) {
+		return -1;
+	}
 
-	if (config->rsrc == FRUIT_RSRC_XATTR) {
-		return rc;
+	if ((config->rsrc != FRUIT_RSRC_ADFILE) ||
+	    (!S_ISREG(smb_fname_src->st.st_ex_mode)))
+	{
+		return 0;
 	}
 
 	rc = adouble_path(talloc_tos(), smb_fname_src->base_name,
@@ -2711,24 +2719,43 @@ static int fruit_rename(struct vfs_handle_struct *handle,
 	if (rc != 0) {
 		goto done;
 	}
+	src_adp_smb_fname = synthetic_smb_fname(talloc_tos(),
+						src_adouble_path,
+						NULL, NULL,
+						smb_fname_src->flags);
+	TALLOC_FREE(src_adouble_path);
+	if (src_adp_smb_fname == NULL) {
+		rc = -1;
+		goto done;
+	}
+
 	rc = adouble_path(talloc_tos(), smb_fname_dst->base_name,
 			  &dst_adouble_path);
 	if (rc != 0) {
 		goto done;
 	}
+	dst_adp_smb_fname = synthetic_smb_fname(talloc_tos(),
+						dst_adouble_path,
+						NULL, NULL,
+						smb_fname_dst->flags);
+	TALLOC_FREE(dst_adouble_path);
+	if (dst_adp_smb_fname == NULL) {
+		rc = -1;
+		goto done;
+	}
 
-	DEBUG(10, ("fruit_rename: %s -> %s\n",
-		   src_adouble_path, dst_adouble_path));
+	DBG_DEBUG("%s -> %s\n",
+		  smb_fname_str_dbg(src_adp_smb_fname),
+		  smb_fname_str_dbg(dst_adp_smb_fname));
 
-	rc = rename(src_adouble_path, dst_adouble_path);
+	rc = SMB_VFS_NEXT_RENAME(handle, src_adp_smb_fname, dst_adp_smb_fname);
 	if (errno == ENOENT) {
 		rc = 0;
 	}
 
-	TALLOC_FREE(src_adouble_path);
-	TALLOC_FREE(dst_adouble_path);
-
 done:
+	TALLOC_FREE(src_adp_smb_fname);
+	TALLOC_FREE(dst_adp_smb_fname);
 	return rc;
 }
 
-- 
2.9.3


From d8bfe3a495e10e42dab3a08eeadecaddc1f6bd5b Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Fri, 2 Dec 2016 09:00:31 +0100
Subject: [PATCH 17/56] vfs_fruit: refactor fruit_unlink()

Refactor fruit_unlink() addin helper functions for all fruit:metadata
and fruit:resource settings.

No change in behaviour for fruit:metadata=netatalk and
fruit:resource=file (both the default), but fixes behaviour for the
other cases.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12427

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 source3/modules/vfs_fruit.c | 225 +++++++++++++++++++++++++++++++++++++-------
 1 file changed, 192 insertions(+), 33 deletions(-)

diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c
index 6603678..5c7a30b 100644
--- a/source3/modules/vfs_fruit.c
+++ b/source3/modules/vfs_fruit.c
@@ -2759,25 +2759,59 @@ done:
 	return rc;
 }
 
-static int fruit_unlink(vfs_handle_struct *handle,
-			const struct smb_filename *smb_fname)
+static int fruit_unlink_meta_stream(vfs_handle_struct *handle,
+				    const struct smb_filename *smb_fname)
+{
+	return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
+}
+
+static int fruit_unlink_meta_netatalk(vfs_handle_struct *handle,
+				      const struct smb_filename *smb_fname)
+{
+	return SMB_VFS_REMOVEXATTR(handle->conn,
+				   smb_fname->base_name,
+				   AFPINFO_EA_NETATALK);
+}
+
+static int fruit_unlink_meta(vfs_handle_struct *handle,
+			     const struct smb_filename *smb_fname)
 {
-	int rc = -1;
 	struct fruit_config_data *config = NULL;
+	int rc;
 
 	SMB_VFS_HANDLE_GET_DATA(handle, config,
 				struct fruit_config_data, return -1);
 
-	if (!is_ntfs_stream_smb_fname(smb_fname)) {
-		char *adp = NULL;
+	switch (config->meta) {
+	case FRUIT_META_STREAM:
+		rc = fruit_unlink_meta_stream(handle, smb_fname);
+		break;
 
-		rc = SMB_VFS_NEXT_UNLINK(handle, smb_fname);
-		if (rc != 0) {
-			return -1;
-		}
+	case FRUIT_META_NETATALK:
+		rc = fruit_unlink_meta_netatalk(handle, smb_fname);
+		break;
 
-		if (config->rsrc != FRUIT_RSRC_ADFILE) {
-			return 0;
+	default:
+		DBG_ERR("Unsupported meta config [%d]\n", config->meta);
+		return -1;
+	}
+
+	return rc;
+}
+
+static int fruit_unlink_rsrc_stream(vfs_handle_struct *handle,
+				    const struct smb_filename *smb_fname,
+				    bool force_unlink)
+{
+	int ret;
+
+	if (!force_unlink) {
+		struct smb_filename *smb_fname_cp = NULL;
+		off_t size;
+
+		smb_fname_cp = cp_smb_filename(talloc_tos(), smb_fname);
+		if (smb_fname_cp == NULL) {
+			return -1;
 		}
 
 		/*
@@ -2785,46 +2819,171 @@ static int fruit_unlink(vfs_handle_struct *handle,
 		 * vfs_streaminfo, as a result stream cleanup/deletion of file
 		 * deletion doesn't remove the resourcefork stream.
 		 */
-		rc = adouble_path(talloc_tos(),
-				  smb_fname->base_name, &adp);
-		if (rc != 0) {
+
+		ret = SMB_VFS_NEXT_STAT(handle, smb_fname_cp);
+		if (ret != 0) {
+			TALLOC_FREE(smb_fname_cp);
+			DBG_ERR("stat [%s] failed [%s]\n",
+				smb_fname_str_dbg(smb_fname_cp), strerror(errno));
 			return -1;
 		}
 
-		/* FIXME: direct unlink(), missing smb_fname */
-		DBG_DEBUG("fruit_unlink: %s\n", adp);
-		rc = unlink(adp);
-		if ((rc == -1) && (errno == ENOENT)) {
-			rc = 0;
+		size = smb_fname_cp->st.st_ex_size;
+		TALLOC_FREE(smb_fname_cp);
+
+		if (size > 0) {
+			/* OS X ignores resource fork stream delete requests */
+			return 0;
 		}
+	}
 
-		TALLOC_FREE(adp);
-		return 0;
+	ret = SMB_VFS_NEXT_UNLINK(handle, smb_fname);
+	if ((ret != 0) && (errno == ENOENT) && force_unlink) {
+		ret = 0;
 	}
 
-	if (is_afpinfo_stream(smb_fname)) {
-		if (config->meta == FRUIT_META_STREAM) {
-			rc = SMB_VFS_NEXT_UNLINK(handle, smb_fname);
-		} else {
-			rc = SMB_VFS_REMOVEXATTR(handle->conn,
-						 smb_fname->base_name,
-						 AFPINFO_EA_NETATALK);
+	return ret;
+}
+
+static int fruit_unlink_rsrc_adouble(vfs_handle_struct *handle,
+				     const struct smb_filename *smb_fname,
+				     bool force_unlink)
+{
+	int rc;
+	char *adp = NULL;
+	struct adouble *ad = NULL;
+	struct smb_filename *adp_smb_fname = NULL;
+
+	if (!force_unlink) {
+		ad = ad_get(talloc_tos(), handle, smb_fname->base_name,
+			    ADOUBLE_RSRC);
+		if (ad == NULL) {
+			errno = ENOENT;
+			return -1;
 		}
 
-		return rc;
+
+		/*
+		 * 0 byte resource fork streams are not listed by
+		 * vfs_streaminfo, as a result stream cleanup/deletion of file
+		 * deletion doesn't remove the resourcefork stream.
+		 */
+
+		if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
+			/* OS X ignores resource fork stream delete requests */
+			TALLOC_FREE(ad);
+			return 0;
+		}
+
+		TALLOC_FREE(ad);
 	}
 
-	if (is_afpresource_stream(smb_fname)) {
-		/* OS X ignores deletes on the AFP_Resource stream */
-		return 0;
+	rc = adouble_path(talloc_tos(), smb_fname->base_name, &adp);
+	if (rc != 0) {
+		return -1;
 	}
 
-	return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
+	adp_smb_fname = synthetic_smb_fname(talloc_tos(), adp,
+					    NULL, NULL,
+					    smb_fname->flags);
+	TALLOC_FREE(adp);
+	if (adp_smb_fname == NULL) {
+		return -1;
+	}
+
+	rc = SMB_VFS_NEXT_UNLINK(handle, adp_smb_fname);
+	TALLOC_FREE(adp_smb_fname);
+	if ((rc != 0) && (errno == ENOENT) && force_unlink) {
+		rc = 0;
+	}
 
+	return rc;
+}
 
+static int fruit_unlink_rsrc_xattr(vfs_handle_struct *handle,
+				   const struct smb_filename *smb_fname,
+				   bool force_unlink)
+{
+	/* Nothing to do here, removing the file will remove the xattr */
 	return 0;
 }
 
+static int fruit_unlink_rsrc(vfs_handle_struct *handle,
+			     const struct smb_filename *smb_fname,
+			     bool force_unlink)
+{
+	struct fruit_config_data *config = NULL;
+	int rc;
+
+	SMB_VFS_HANDLE_GET_DATA(handle, config,
+				struct fruit_config_data, return -1);
+
+	switch (config->rsrc) {
+	case FRUIT_RSRC_STREAM:
+		rc = fruit_unlink_rsrc_stream(handle, smb_fname, force_unlink);
+		break;
+
+	case FRUIT_RSRC_ADFILE:
+		rc = fruit_unlink_rsrc_adouble(handle, smb_fname, force_unlink);
+		break;
+
+	case FRUIT_RSRC_XATTR:
+		rc = fruit_unlink_rsrc_xattr(handle, smb_fname, force_unlink);
+		break;
+
+	default:
+		DBG_ERR("Unsupported rsrc config [%d]\n", config->rsrc);
+		return -1;
+	}
+
+	return rc;
+}
+
+static int fruit_unlink(vfs_handle_struct *handle,
+			const struct smb_filename *smb_fname)
+{
+	int rc;
+	struct fruit_config_data *config = NULL;
+	struct smb_filename *rsrc_smb_fname = NULL;
+
+	SMB_VFS_HANDLE_GET_DATA(handle, config,
+				struct fruit_config_data, return -1);
+
+	if (is_afpinfo_stream(smb_fname)) {
+		return fruit_unlink_meta(handle, smb_fname);
+	} else if (is_afpresource_stream(smb_fname)) {
+		return fruit_unlink_rsrc(handle, smb_fname, false);
+	} if (is_ntfs_stream_smb_fname(smb_fname)) {
+		return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
+	}
+
+	/*
+	 * A request to delete the base file. Because 0 byte resource
+	 * fork streams are not listed by fruit_streaminfo,
+	 * delete_all_streams() can't remove 0 byte resource fork
+	 * streams, so we have to cleanup this here.
+	 */
+	rsrc_smb_fname = synthetic_smb_fname(talloc_tos(),
+					     smb_fname->base_name,
+					     AFPRESOURCE_STREAM_NAME,
+					     NULL,
+					     smb_fname->flags);
+	if (rsrc_smb_fname == NULL) {
+		return -1;
+	}
+
+	rc = fruit_unlink_rsrc(handle, rsrc_smb_fname, true);
+	if ((rc != 0) && (errno != ENOENT)) {
+		DBG_ERR("Forced unlink of [%s] failed [%s]\n",
+			smb_fname_str_dbg(rsrc_smb_fname), strerror(errno));
+		TALLOC_FREE(rsrc_smb_fname);
+		return -1;
+	}
+	TALLOC_FREE(rsrc_smb_fname);
+
+	return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
+}
+
 static int fruit_chmod(vfs_handle_struct *handle,
 		       const struct smb_filename *smb_fname,
 		       mode_t mode)
-- 
2.9.3


From 96289a446fb7eea80576c36d07c605fbcfc86ec4 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Fri, 2 Dec 2016 09:02:27 +0100
Subject: [PATCH 18/56] vfs_fruit: fix fruit_chmod() for the
 fruit:resource!=file case

The following code must only be executed for the fruit:resource=file
case.

While at it, remove an unnecessary lstat, use the stat info from
smb_fname.

Otherwise no change in behaviour for the fruit:resource=file case (the
default).

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12427

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 source3/modules/vfs_fruit.c | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c
index 5c7a30b..3726c6f 100644
--- a/source3/modules/vfs_fruit.c
+++ b/source3/modules/vfs_fruit.c
@@ -2991,7 +2991,6 @@ static int fruit_chmod(vfs_handle_struct *handle,
 	int rc = -1;
 	char *adp = NULL;
 	struct fruit_config_data *config = NULL;
-	SMB_STRUCT_STAT sb;
 	const char *path = smb_fname->base_name;
 	struct smb_filename *smb_fname_adp = NULL;
 
@@ -3003,14 +3002,16 @@ static int fruit_chmod(vfs_handle_struct *handle,
 	SMB_VFS_HANDLE_GET_DATA(handle, config,
 				struct fruit_config_data, return -1);
 
-	if (config->rsrc == FRUIT_RSRC_XATTR) {
+	if (config->rsrc != FRUIT_RSRC_ADFILE) {
 		return 0;
 	}
 
-	/* FIXME: direct sys_lstat(), missing smb_fname */
-	rc = sys_lstat(path, &sb, false);
-	if (rc != 0 || !S_ISREG(sb.st_ex_mode)) {
-		return rc;
+	if (!VALID_STAT(smb_fname->st)) {
+		return 0;
+	}
+
+	if (!S_ISREG(smb_fname->st.st_ex_mode)) {
+		return 0;
 	}
 
 	rc = adouble_path(talloc_tos(), path, &adp);
-- 
2.9.3


From 64f2100a1274a4aea6019f805d7c1eaf756dbdb7 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Fri, 2 Dec 2016 09:04:37 +0100
Subject: [PATCH 19/56] vfs_fruit: fix fruit_chown() for the
 fruit:resource!=file case

The following code must only be executed for the fruit:resource=file
case.

While at it, remove an unnecessary lstat, use the stat info from
smb_fname.

Otherwise no change in behaviour for the fruit:resource=file case (the
default).

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12427

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 source3/modules/vfs_fruit.c | 15 ++++++++-------
 1 file changed, 8 insertions(+), 7 deletions(-)

diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c
index 3726c6f..ec3e970 100644
--- a/source3/modules/vfs_fruit.c
+++ b/source3/modules/vfs_fruit.c
@@ -3051,7 +3051,6 @@ static int fruit_chown(vfs_handle_struct *handle,
 	char *adp = NULL;
 	struct fruit_config_data *config = NULL;
 	struct smb_filename *adp_smb_fname = NULL;
-	SMB_STRUCT_STAT sb;
 
 	rc = SMB_VFS_NEXT_CHOWN(handle, smb_fname, uid, gid);
 	if (rc != 0) {
@@ -3061,14 +3060,16 @@ static int fruit_chown(vfs_handle_struct *handle,
 	SMB_VFS_HANDLE_GET_DATA(handle, config,
 				struct fruit_config_data, return -1);
 
-	if (config->rsrc == FRUIT_RSRC_XATTR) {
-		return rc;
+	if (config->rsrc != FRUIT_RSRC_ADFILE) {
+		return 0;
 	}
 
-	/* FIXME: direct sys_lstat(), need non-const smb_fname */
-	rc = sys_lstat(smb_fname->base_name, &sb, false);
-	if (rc != 0 || !S_ISREG(sb.st_ex_mode)) {
-		return rc;
+	if (!VALID_STAT(smb_fname->st)) {
+		return 0;
+	}
+
+	if (!S_ISREG(smb_fname->st.st_ex_mode)) {
+		return 0;
 	}
 
 	rc = adouble_path(talloc_tos(), smb_fname->base_name, &adp);
-- 
2.9.3


From a5a945e0c81e8a5fdbdbb53c3b3eb45fc35571a9 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Fri, 9 Dec 2016 18:22:49 +0100
Subject: [PATCH 20/56] vfs_fruit: fix fruit_rmdir() for the
 fruit:resource!=file case

The following code must only be executed for the fruit:resource=file
case. Otherwise no change in behaviour.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12427

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 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 ec3e970..1c8ae01 100644
--- a/source3/modules/vfs_fruit.c
+++ b/source3/modules/vfs_fruit.c
@@ -3112,7 +3112,7 @@ static int fruit_rmdir(struct vfs_handle_struct *handle,
 	SMB_VFS_HANDLE_GET_DATA(handle, config,
 				struct fruit_config_data, return -1);
 
-	if (!handle->conn->cwd || !path || (config->rsrc == FRUIT_RSRC_XATTR)) {
+	if (config->rsrc != FRUIT_RSRC_ADFILE) {
 		goto exit_rmdir;
 	}
 
-- 
2.9.3


From 3252a5d1b94e6c2bccd600e7b97996a3bd0a9640 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Fri, 9 Dec 2016 18:24:40 +0100
Subject: [PATCH 21/56] vfs_fruit: in fruit_rmdir() check ._ files before
 deleting them

This ensures we only delete valid AppleDouble files whose names begin
with "._", not just *any* file that matches "^._*".

Also use proper VFS functions instead of direclty calling the C library
functions.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12427

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 source3/modules/vfs_fruit.c | 61 ++++++++++++++++++++++++++++++++++-----------
 1 file changed, 47 insertions(+), 14 deletions(-)

diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c
index 1c8ae01..a3c76eb 100644
--- a/source3/modules/vfs_fruit.c
+++ b/source3/modules/vfs_fruit.c
@@ -3107,7 +3107,6 @@ static int fruit_rmdir(struct vfs_handle_struct *handle,
 	DIR *dh = NULL;
 	struct dirent *de;
 	struct fruit_config_data *config;
-	const char *path = smb_fname->base_name;
 
 	SMB_VFS_HANDLE_GET_DATA(handle, config,
 				struct fruit_config_data, return -1);
@@ -3120,24 +3119,58 @@ static int fruit_rmdir(struct vfs_handle_struct *handle,
 	 * Due to there is no way to change bDeleteVetoFiles variable
 	 * from this module, need to clean up ourselves
 	 */
-	dh = opendir(path);
+
+	dh = SMB_VFS_OPENDIR(handle->conn, smb_fname, NULL, 0);
 	if (dh == NULL) {
 		goto exit_rmdir;
 	}
 
-	while ((de = readdir(dh)) != NULL) {
-		if ((strncmp(de->d_name,
-			     ADOUBLE_NAME_PREFIX,
-			     strlen(ADOUBLE_NAME_PREFIX))) == 0) {
-			char *p = talloc_asprintf(talloc_tos(),
-						  "%s/%s",
-						  path, de->d_name);
-			if (p == NULL) {
-				goto exit_rmdir;
-			}
-			DEBUG(10, ("fruit_rmdir: delete %s\n", p));
-			(void)unlink(p);
+	while ((de = SMB_VFS_READDIR(handle->conn, dh, NULL)) != NULL) {
+		int match;
+		struct adouble *ad = NULL;
+		char *p = NULL;
+		struct smb_filename *ad_smb_fname = NULL;
+		int ret;
+
+		match = strncmp(de->d_name,
+				ADOUBLE_NAME_PREFIX,
+				strlen(ADOUBLE_NAME_PREFIX));
+		if (match != 0) {
+			continue;
+		}
+
+		p = talloc_asprintf(talloc_tos(), "%s/%s",
+				    smb_fname->base_name, de->d_name);
+		if (p == NULL) {
+			DBG_ERR("talloc_asprintf failed\n");
+			return -1;
+		}
+
+		/*
+		 * Check whether it's a valid AppleDouble file, if
+		 * yes, delete it, ignore it otherwise.
+		 */
+		ad = ad_get(talloc_tos(), handle, p, ADOUBLE_RSRC);
+		if (ad == NULL) {
 			TALLOC_FREE(p);
+			continue;
+		}
+		TALLOC_FREE(ad);
+
+		ad_smb_fname = synthetic_smb_fname(talloc_tos(), p,
+						    NULL, NULL,
+						    smb_fname->flags);
+		TALLOC_FREE(p);
+		if (ad_smb_fname == NULL) {
+			DBG_ERR("synthetic_smb_fname failed\n");
+			return -1;
+		}
+
+		ret = SMB_VFS_NEXT_UNLINK(handle, ad_smb_fname);
+		TALLOC_FREE(ad_smb_fname);
+		if (ret != 0) {
+			DBG_ERR("Deleting [%s] failed\n",
+				smb_fname_str_dbg(ad_smb_fname));
 		}
 	}
 
-- 
2.9.3


From ac54f15e8912d323cb959288362fd882d3a904ff Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Fri, 2 Dec 2016 10:46:55 +0100
Subject: [PATCH 22/56] vfs_fruit: refactor fruit_open_rsrc()

This just splits up fruit_open_rsrc() to use helper functions for each
config->rsrc setting. No change in behaviour.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12427

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 source3/modules/vfs_fruit.c | 105 ++++++++++++++++++++++++++++++--------------
 1 file changed, 71 insertions(+), 34 deletions(-)

diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c
index a3c76eb..544bc22 100644
--- a/source3/modules/vfs_fruit.c
+++ b/source3/modules/vfs_fruit.c
@@ -2526,47 +2526,18 @@ static int fruit_open_meta(vfs_handle_struct *handle,
 	return rc;
 }
 
-static int fruit_open_rsrc(vfs_handle_struct *handle,
-			   struct smb_filename *smb_fname,
-			   files_struct *fsp, int flags, mode_t mode)
+static int fruit_open_rsrc_adouble(vfs_handle_struct *handle,
+				   struct smb_filename *smb_fname,
+				   files_struct *fsp,
+				   int flags,
+				   mode_t mode)
 {
 	int rc = 0;
-	struct fruit_config_data *config = NULL;
 	struct adouble *ad = NULL;
 	struct smb_filename *smb_fname_base = NULL;
 	char *adpath = NULL;
 	int hostfd = -1;
 
-	DEBUG(10, ("fruit_open_rsrc for %s\n", smb_fname_str_dbg(smb_fname)));
-
-	SMB_VFS_HANDLE_GET_DATA(handle, config,
-				struct fruit_config_data, return -1);
-
-	switch (config->rsrc) {
-	case FRUIT_RSRC_STREAM:
-		return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
-	case FRUIT_RSRC_XATTR:
-#ifdef HAVE_ATTROPEN
-		hostfd = attropen(smb_fname->base_name,
-				  AFPRESOURCE_EA_NETATALK, flags, mode);
-		if (hostfd == -1) {
-			return -1;
-		}
-		ad = ad_init(VFS_MEMCTX_FSP_EXTENSION(handle, fsp),
-			     handle, ADOUBLE_RSRC, fsp);
-		if (ad == NULL) {
-			rc = -1;
-			goto exit;
-		}
-		goto exit;
-#else
-		errno = ENOTSUP;
-		return -1;
-#endif
-	default:
-		break;
-	}
-
 	if (!(flags & O_CREAT) && !VALID_STAT(smb_fname->st)) {
 		rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
 		if (rc != 0) {
@@ -2663,6 +2634,72 @@ exit:
 	return hostfd;
 }
 
+static int fruit_open_rsrc_xattr(vfs_handle_struct *handle,
+				 struct smb_filename *smb_fname,
+				 files_struct *fsp,
+				 int flags,
+				 mode_t mode)
+{
+#ifdef HAVE_ATTROPEN
+	int fd = -1;
+	struct adouble *ad = NULL;
+
+	ad = ad_init(VFS_MEMCTX_FSP_EXTENSION(handle, fsp),
+		     handle, ADOUBLE_RSRC, fsp);
+	if (ad == NULL) {
+		return -1;
+	}
+
+	fd = attropen(smb_fname->base_name,
+			  AFPRESOURCE_EA_NETATALK, flags, mode);
+	if (fd == -1) {
+		return -1;
+	}
+
+	return fd;
+
+#else
+	errno = ENOSYS;
+	return -1;
+#endif
+}
+
+static int fruit_open_rsrc(vfs_handle_struct *handle,
+			   struct smb_filename *smb_fname,
+			   files_struct *fsp, int flags, mode_t mode)
+{
+	int fd;
+	struct fruit_config_data *config = NULL;
+
+	DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
+
+	SMB_VFS_HANDLE_GET_DATA(handle, config,
+				struct fruit_config_data, return -1);
+
+	switch (config->rsrc) {
+	case FRUIT_RSRC_STREAM:
+		fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
+		break;
+
+	case FRUIT_RSRC_ADFILE:
+		fd = fruit_open_rsrc_adouble(handle, smb_fname,
+					     fsp, flags, mode);
+		break;
+
+	case FRUIT_RSRC_XATTR:
+		fd = fruit_open_rsrc_xattr(handle, smb_fname,
+					   fsp, flags, mode);
+		break;
+
+	default:
+		DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
+		return -1;
+	}
+
+	DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
+	return fd;
+}
+
 static int fruit_open(vfs_handle_struct *handle,
                       struct smb_filename *smb_fname,
                       files_struct *fsp, int flags, mode_t mode)
-- 
2.9.3


From 3ba90350e2c6632975641372d52b9b9828449b5b Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Fri, 2 Dec 2016 11:26:22 +0100
Subject: [PATCH 23/56] vfs_fruit: refactor fruit_stat_rsrc()

Use helper functions for the fruit:resource cases. No change in
behaveour.

The next patch will add the proper helper functions for
fruit:resource=xattr and fruit:resource=stream.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12427

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 source3/modules/vfs_fruit.c | 41 +++++++++++++++++++++++++++++++++--------
 1 file changed, 33 insertions(+), 8 deletions(-)

diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c
index 544bc22..fac9955 100644
--- a/source3/modules/vfs_fruit.c
+++ b/source3/modules/vfs_fruit.c
@@ -3590,15 +3590,12 @@ static int fruit_stat_meta(vfs_handle_struct *handle,
 	return ret;
 }
 
-static int fruit_stat_rsrc(vfs_handle_struct *handle,
-			   struct smb_filename *smb_fname,
-			   bool follow_links)
-
+static int fruit_stat_rsrc_netatalk(vfs_handle_struct *handle,
+				    struct smb_filename *smb_fname,
+				    bool follow_links)
 {
 	struct adouble *ad = NULL;
-
-	DEBUG(10, ("fruit_stat_rsrc called for %s\n",
-		   smb_fname_str_dbg(smb_fname)));
+	int ret;
 
 	ad = ad_get(talloc_tos(), handle, smb_fname->base_name, ADOUBLE_RSRC);
 	if (ad == NULL) {
@@ -3607,7 +3604,8 @@ static int fruit_stat_rsrc(vfs_handle_struct *handle,
 	}
 
 	/* Populate the stat struct with info from the base file. */
-	if (fruit_stat_base(handle, smb_fname, follow_links) == -1) {
+	ret = fruit_stat_base(handle, smb_fname, follow_links);
+	if (ret != 0) {
 		TALLOC_FREE(ad);
 		return -1;
 	}
@@ -3619,6 +3617,33 @@ static int fruit_stat_rsrc(vfs_handle_struct *handle,
 	return 0;
 }
 
+static int fruit_stat_rsrc(vfs_handle_struct *handle,
+			   struct smb_filename *smb_fname,
+			   bool follow_links)
+{
+	struct fruit_config_data *config = NULL;
+	int ret;
+
+	DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
+
+	SMB_VFS_HANDLE_GET_DATA(handle, config,
+				struct fruit_config_data, return -1);
+
+	switch (config->rsrc) {
+	case FRUIT_RSRC_STREAM:
+	case FRUIT_RSRC_XATTR:
+	case FRUIT_RSRC_ADFILE:
+		ret = fruit_stat_rsrc_netatalk(handle, smb_fname, follow_links);
+		break;
+
+	default:
+		DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
+		return -1;
+	}
+
+	return ret;
+}
+
 static int fruit_stat(vfs_handle_struct *handle,
 		      struct smb_filename *smb_fname)
 {
-- 
2.9.3


From b3222c09a573829d30a6571458d63adfcb57b48c Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Fri, 2 Dec 2016 11:30:06 +0100
Subject: [PATCH 24/56] vfs_fruit: add fruit_stat_rsrc_stream() implementation

This fixes fruit_stat_rsrc for the fruit:resource=stream case.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12427

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 source3/modules/vfs_fruit.c | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c
index fac9955..e912ee9 100644
--- a/source3/modules/vfs_fruit.c
+++ b/source3/modules/vfs_fruit.c
@@ -3617,6 +3617,21 @@ static int fruit_stat_rsrc_netatalk(vfs_handle_struct *handle,
 	return 0;
 }
 
+static int fruit_stat_rsrc_stream(vfs_handle_struct *handle,
+				  struct smb_filename *smb_fname,
+				  bool follow_links)
+{
+	int ret;
+
+	if (follow_links) {
+		ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
+	} else {
+		ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
+	}
+
+	return ret;
+}
+
 static int fruit_stat_rsrc(vfs_handle_struct *handle,
 			   struct smb_filename *smb_fname,
 			   bool follow_links)
@@ -3631,6 +3646,9 @@ static int fruit_stat_rsrc(vfs_handle_struct *handle,
 
 	switch (config->rsrc) {
 	case FRUIT_RSRC_STREAM:
+		ret = fruit_stat_rsrc_stream(handle, smb_fname, follow_links);
+		break;
+
 	case FRUIT_RSRC_XATTR:
 	case FRUIT_RSRC_ADFILE:
 		ret = fruit_stat_rsrc_netatalk(handle, smb_fname, follow_links);
-- 
2.9.3


From 8c365004e00178d27e33cf93b790f27a7a83d20c Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Fri, 2 Dec 2016 11:44:53 +0100
Subject: [PATCH 25/56] vfs_fruit: add fruit_stat_rsrc_xattr() implementation

This fixes fruit_stat_rsrc for the fruit:resource=xattr case.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12427

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 source3/modules/vfs_fruit.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 45 insertions(+)

diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c
index e912ee9..2cf0c0d 100644
--- a/source3/modules/vfs_fruit.c
+++ b/source3/modules/vfs_fruit.c
@@ -3632,6 +3632,48 @@ static int fruit_stat_rsrc_stream(vfs_handle_struct *handle,
 	return ret;
 }
 
+static int fruit_stat_rsrc_xattr(vfs_handle_struct *handle,
+				 struct smb_filename *smb_fname,
+				 bool follow_links)
+{
+#ifdef HAVE_ATTROPEN
+	int ret;
+	int fd = -1;
+
+	/* Populate the stat struct with info from the base file. */
+	ret = fruit_stat_base(handle, smb_fname, follow_links);
+	if (ret != 0) {
+		return -1;
+	}
+
+	fd = attropen(smb_fname->base_name,
+		      AFPRESOURCE_EA_NETATALK,
+		      O_RDONLY);
+	if (fd == -1) {
+		return 0;
+	}
+
+	ret = sys_fstat(fd, &smb_fname->st, false);
+	if (ret != 0) {
+		close(fd);
+		DBG_ERR("fstat [%s:%s] failed\n", smb_fname->base_name,
+			AFPRESOURCE_EA_NETATALK);
+		return -1;
+	}
+	close(fd);
+	fd = -1;
+
+	smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
+					      smb_fname->stream_name);
+
+	return ret;
+
+#else
+	errno = ENOSYS;
+	return -1;
+#endif
+}
+
 static int fruit_stat_rsrc(vfs_handle_struct *handle,
 			   struct smb_filename *smb_fname,
 			   bool follow_links)
@@ -3650,6 +3692,9 @@ static int fruit_stat_rsrc(vfs_handle_struct *handle,
 		break;
 
 	case FRUIT_RSRC_XATTR:
+		ret = fruit_stat_rsrc_xattr(handle, smb_fname, follow_links);
+		break;
+
 	case FRUIT_RSRC_ADFILE:
 		ret = fruit_stat_rsrc_netatalk(handle, smb_fname, follow_links);
 		break;
-- 
2.9.3


From bcc60211f3a1eb9f7d91fa4e216641db6adfc036 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Fri, 2 Dec 2016 15:49:03 +0100
Subject: [PATCH 26/56] vfs_fruit: refactor fruit_streaminfo()

Handle all settings of fruit:metadata and fruit:resource in helper
functions.

Resource fork streams of 0 bytes must be filtered out, this wasn't done
previously for the fruit:resource=stream and xattr case.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12427

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 source3/modules/vfs_fruit.c | 285 +++++++++++++++++++++++++++++++++++++-------
 1 file changed, 241 insertions(+), 44 deletions(-)

diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c
index 2cf0c0d..af6ec23 100644
--- a/source3/modules/vfs_fruit.c
+++ b/source3/modules/vfs_fruit.c
@@ -1578,6 +1578,40 @@ static bool add_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
 	return true;
 }
 
+static bool filter_empty_rsrc_stream(unsigned int *num_streams,
+				     struct stream_struct **streams)
+{
+	struct stream_struct *tmp = *streams;
+	unsigned int i;
+
+	if (*num_streams == 0) {
+		return true;
+	}
+
+	for (i = 0; i < *num_streams; i++) {
+		if (strequal_m(tmp[i].name, AFPRESOURCE_STREAM)) {
+			break;
+		}
+	}
+
+	if (i == *num_streams) {
+		return true;
+	}
+
+	if (tmp[i].size > 0) {
+		return true;
+	}
+
+	TALLOC_FREE(tmp[i].name);
+	if (*num_streams - 1 > i) {
+		memmove(&tmp[i], &tmp[i+1],
+			(*num_streams - i - 1) * sizeof(struct stream_struct));
+	}
+
+	*num_streams -= 1;
+	return true;
+}
+
 static bool del_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
 			     struct stream_struct **streams,
 			     const char *name)
@@ -3911,6 +3945,202 @@ exit:
 	return rc;
 }
 
+static NTSTATUS fruit_streaminfo_meta_stream(
+	vfs_handle_struct *handle,
+	struct files_struct *fsp,
+	const struct smb_filename *smb_fname,
+	TALLOC_CTX *mem_ctx,
+	unsigned int *pnum_streams,
+	struct stream_struct **pstreams)
+{
+	return NT_STATUS_OK;
+}
+
+static NTSTATUS fruit_streaminfo_meta_netatalk(
+	vfs_handle_struct *handle,
+	struct files_struct *fsp,
+	const struct smb_filename *smb_fname,
+	TALLOC_CTX *mem_ctx,
+	unsigned int *pnum_streams,
+	struct stream_struct **pstreams)
+{
+	struct adouble *ad = NULL;
+	bool is_fi_empty;
+	bool ok;
+
+	/* Remove the Netatalk xattr from the list */
+	ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
+			      ":" NETATALK_META_XATTR ":$DATA");
+	if (!ok) {
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	ad = ad_get(talloc_tos(), handle,
+		    smb_fname->base_name, ADOUBLE_META);
+	if (ad == NULL) {
+		return NT_STATUS_OK;
+	}
+
+	is_fi_empty = ad_empty_finderinfo(ad);
+	TALLOC_FREE(ad);
+
+	if (is_fi_empty) {
+		return NT_STATUS_OK;
+	}
+
+	ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
+			      AFPINFO_STREAM_NAME, AFP_INFO_SIZE,
+			      smb_roundup(handle->conn, AFP_INFO_SIZE));
+	if (!ok) {
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	return NT_STATUS_OK;
+}
+
+static NTSTATUS fruit_streaminfo_meta(vfs_handle_struct *handle,
+				      struct files_struct *fsp,
+				      const struct smb_filename *smb_fname,
+				      TALLOC_CTX *mem_ctx,
+				      unsigned int *pnum_streams,
+				      struct stream_struct **pstreams)
+{
+	struct fruit_config_data *config = NULL;
+	NTSTATUS status;
+
+	SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
+				return NT_STATUS_INTERNAL_ERROR);
+
+	switch (config->meta) {
+	case FRUIT_META_NETATALK:
+		status = fruit_streaminfo_meta_netatalk(handle, fsp, smb_fname,
+							mem_ctx, pnum_streams,
+							pstreams);
+		break;
+
+	case FRUIT_META_STREAM:
+		status = fruit_streaminfo_meta_stream(handle, fsp, smb_fname,
+						      mem_ctx, pnum_streams,
+						      pstreams);
+		break;
+
+	default:
+		return NT_STATUS_INTERNAL_ERROR;
+	}
+
+	return status;
+}
+
+static NTSTATUS fruit_streaminfo_rsrc_stream(
+	vfs_handle_struct *handle,
+	struct files_struct *fsp,
+	const struct smb_filename *smb_fname,
+	TALLOC_CTX *mem_ctx,
+	unsigned int *pnum_streams,
+	struct stream_struct **pstreams)
+{
+	bool ok;
+
+	ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
+	if (!ok) {
+		DBG_ERR("Filtering resource stream failed\n");
+		return NT_STATUS_INTERNAL_ERROR;
+	}
+	return NT_STATUS_OK;
+}
+
+static NTSTATUS fruit_streaminfo_rsrc_xattr(
+	vfs_handle_struct *handle,
+	struct files_struct *fsp,
+	const struct smb_filename *smb_fname,
+	TALLOC_CTX *mem_ctx,
+	unsigned int *pnum_streams,
+	struct stream_struct **pstreams)
+{
+	bool ok;
+
+	ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
+	if (!ok) {
+		DBG_ERR("Filtering resource stream failed\n");
+		return NT_STATUS_INTERNAL_ERROR;
+	}
+	return NT_STATUS_OK;
+}
+
+static NTSTATUS fruit_streaminfo_rsrc_adouble(
+	vfs_handle_struct *handle,
+	struct files_struct *fsp,
+	const struct smb_filename *smb_fname,
+	TALLOC_CTX *mem_ctx,
+	unsigned int *pnum_streams,
+	struct stream_struct **pstreams)
+{
+	struct adouble *ad = NULL;
+	bool ok;
+	size_t rlen;
+
+	ad = ad_get(talloc_tos(), handle, smb_fname->base_name,
+		    ADOUBLE_RSRC);
+	if (ad == NULL) {
+		return NT_STATUS_OK;
+	}
+
+	rlen = ad_getentrylen(ad, ADEID_RFORK);
+	TALLOC_FREE(ad);
+
+	if (rlen == 0) {
+		return NT_STATUS_OK;
+	}
+
+	ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
+			      AFPRESOURCE_STREAM_NAME, rlen,
+			      smb_roundup(handle->conn, rlen));
+	if (!ok) {
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	return NT_STATUS_OK;
+}
+
+static NTSTATUS fruit_streaminfo_rsrc(vfs_handle_struct *handle,
+				      struct files_struct *fsp,
+				      const struct smb_filename *smb_fname,
+				      TALLOC_CTX *mem_ctx,
+				      unsigned int *pnum_streams,
+				      struct stream_struct **pstreams)
+{
+	struct fruit_config_data *config = NULL;
+	NTSTATUS status;
+
+	SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
+				return NT_STATUS_INTERNAL_ERROR);
+
+	switch (config->rsrc) {
+	case FRUIT_RSRC_STREAM:
+		status = fruit_streaminfo_rsrc_stream(handle, fsp, smb_fname,
+						      mem_ctx, pnum_streams,
+						      pstreams);
+		break;
+
+	case FRUIT_RSRC_XATTR:
+		status = fruit_streaminfo_rsrc_xattr(handle, fsp, smb_fname,
+						     mem_ctx, pnum_streams,
+						     pstreams);
+		break;
+
+	case FRUIT_RSRC_ADFILE:
+		status = fruit_streaminfo_rsrc_adouble(handle, fsp, smb_fname,
+						       mem_ctx, pnum_streams,
+						       pstreams);
+		break;
+
+	default:
+		return NT_STATUS_INTERNAL_ERROR;
+	}
+
+	return status;
+}
+
 static NTSTATUS fruit_streaminfo(vfs_handle_struct *handle,
 				 struct files_struct *fsp,
 				 const struct smb_filename *smb_fname,
@@ -3919,48 +4149,12 @@ static NTSTATUS fruit_streaminfo(vfs_handle_struct *handle,
 				 struct stream_struct **pstreams)
 {
 	struct fruit_config_data *config = NULL;
-	struct adouble *ad = NULL;
 	NTSTATUS status;
 
 	SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
 				return NT_STATUS_UNSUCCESSFUL);
-	DEBUG(10, ("fruit_streaminfo called for %s\n", smb_fname->base_name));
-
-	if (config->meta == FRUIT_META_NETATALK) {
-		bool ok;
-
-		ad = ad_get(talloc_tos(), handle,
-			    smb_fname->base_name, ADOUBLE_META);
-		if ((ad != NULL) && !ad_empty_finderinfo(ad)) {
-			ok = add_fruit_stream(
-				mem_ctx, pnum_streams, pstreams,
-				AFPINFO_STREAM_NAME, AFP_INFO_SIZE,
-				smb_roundup(handle->conn, AFP_INFO_SIZE));
-			if (!ok) {
-				TALLOC_FREE(ad);
-				return NT_STATUS_NO_MEMORY;
-			}
-		}
-		TALLOC_FREE(ad);
-	}
 
-	if (config->rsrc != FRUIT_RSRC_STREAM) {
-		ad = ad_get(talloc_tos(), handle, smb_fname->base_name,
-			    ADOUBLE_RSRC);
-		if (ad && (ad_getentrylen(ad, ADEID_RFORK) > 0)) {
-			if (!add_fruit_stream(
-				    mem_ctx, pnum_streams, pstreams,
-				    AFPRESOURCE_STREAM_NAME,
-				    ad_getentrylen(ad, ADEID_RFORK),
-				    smb_roundup(handle->conn,
-						ad_getentrylen(
-							ad, ADEID_RFORK)))) {
-				TALLOC_FREE(ad);
-				return NT_STATUS_NO_MEMORY;
-			}
-		}
-		TALLOC_FREE(ad);
-	}
+	DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
 
 	status = SMB_VFS_NEXT_STREAMINFO(handle, fsp, smb_fname, mem_ctx,
 					 pnum_streams, pstreams);
@@ -3968,13 +4162,16 @@ static NTSTATUS fruit_streaminfo(vfs_handle_struct *handle,
 		return status;
 	}
 
-	if (config->meta == FRUIT_META_NETATALK) {
-		/* Remove the Netatalk xattr from the list */
-		if (!del_fruit_stream(mem_ctx, pnum_streams, pstreams,
-				      ":" NETATALK_META_XATTR ":$DATA")) {
-				TALLOC_FREE(ad);
-				return NT_STATUS_NO_MEMORY;
-		}
+	status = fruit_streaminfo_meta(handle, fsp, smb_fname,
+				       mem_ctx, pnum_streams, pstreams);
+	if (!NT_STATUS_IS_OK(status)) {
+		return status;
+	}
+
+	status = fruit_streaminfo_rsrc(handle, fsp, smb_fname,
+				       mem_ctx, pnum_streams, pstreams);
+	if (!NT_STATUS_IS_OK(status)) {
+		return status;
 	}
 
 	return NT_STATUS_OK;
-- 
2.9.3


From b56b74390f4f83830e06fe9efe4d0799a8130175 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Fri, 2 Dec 2016 15:57:22 +0100
Subject: [PATCH 27/56] vfs_fruit: fix fruit_ntimes() for the
 fruit:metadata!=netatalk case

The following code must only be executed for the fruit:metadata=netatalk
case. Otherwise no change in behaviour.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12427

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 source3/modules/vfs_fruit.c | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c
index af6ec23..b137839 100644
--- a/source3/modules/vfs_fruit.c
+++ b/source3/modules/vfs_fruit.c
@@ -4183,9 +4183,15 @@ static int fruit_ntimes(vfs_handle_struct *handle,
 {
 	int rc = 0;
 	struct adouble *ad = NULL;
+	struct fruit_config_data *config = NULL;
 
-	if (null_timespec(ft->create_time)) {
-		goto exit;
+	SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
+				return -1);
+
+	if ((config->meta != FRUIT_META_NETATALK) ||
+	    null_timespec(ft->create_time))
+	{
+		return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
 	}
 
 	DEBUG(10,("set btime for %s to %s\n", smb_fname_str_dbg(smb_fname),
-- 
2.9.3


From 023de07bef2a0e0ef3a905612c21b22d995ab459 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Fri, 2 Dec 2016 16:20:46 +0100
Subject: [PATCH 28/56] vfs_fruit: refactor fruit_ftruncate() and fix stream
 case

The fruit:metadata=stream case wasn't handled previosly, this is now
done in fruit_ftruncate_rsrc_stream(). The rest is just refactoring and
no change in behaviour.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12427

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 source3/modules/vfs_fruit.c | 120 ++++++++++++++++++++++++++++----------------
 1 file changed, 78 insertions(+), 42 deletions(-)

diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c
index b137839..cb66fb1 100644
--- a/source3/modules/vfs_fruit.c
+++ b/source3/modules/vfs_fruit.c
@@ -4239,50 +4239,100 @@ static int fruit_fallocate(struct vfs_handle_struct *handle,
 	return -1;
 }
 
+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
+	return 0;
+}
+
+static int fruit_ftruncate_rsrc_adouble(struct vfs_handle_struct *handle,
+					struct files_struct *fsp,
+					off_t offset)
+{
+	int rc;
+        struct adouble *ad =
+		(struct adouble *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
+	off_t ad_off = ad_getentryoff(ad, ADEID_RFORK);
+
+	if (!fruit_fsp_recheck(ad, fsp)) {
+		return -1;
+	}
+
+	rc = SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset + ad_off);
+	if (rc != 0) {
+		return -1;
+	}
+
+	ad_setentrylen(ad, ADEID_RFORK, offset);
+
+	rc = ad_write(ad, NULL);
+	if (rc != 0) {
+		DBG_ERR("ad_write [%s] failed [%s]\n",
+			fsp_str_dbg(fsp), strerror(errno));
+		return -1;
+	}
+
+	DBG_DEBUG("Path [%s] offset [%jd]\n",
+		  fsp_str_dbg(fsp), (intmax_t)offset);
+
+	return 0;
+}
+
+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);
+}
+
 static int fruit_ftruncate_rsrc(struct vfs_handle_struct *handle,
 				struct files_struct *fsp,
-				off_t offset,
-				struct adouble *ad)
+				off_t offset)
 {
-	int rc;
+	int ret;
 	struct fruit_config_data *config;
 
 	SMB_VFS_HANDLE_GET_DATA(handle, config,
 				struct fruit_config_data, return -1);
 
-	if (config->rsrc == FRUIT_RSRC_XATTR && offset == 0) {
-		return SMB_VFS_FREMOVEXATTR(fsp,
-					    AFPRESOURCE_EA_NETATALK);
-	}
+	switch (config->rsrc) {
+	case FRUIT_RSRC_XATTR:
+		ret = fruit_ftruncate_rsrc_xattr(handle, fsp, offset);
+		break;
 
-	rc = SMB_VFS_NEXT_FTRUNCATE(
-		handle, fsp,
-		offset + ad_getentryoff(ad, ADEID_RFORK));
-	if (rc != 0) {
+	case FRUIT_RSRC_ADFILE:
+		ret = fruit_ftruncate_rsrc_adouble(handle, fsp, offset);
+		break;
+
+	case FRUIT_RSRC_STREAM:
+		ret = fruit_ftruncate_rsrc_stream(handle, fsp, offset);
+		break;
+
+	default:
+		DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
 		return -1;
 	}
 
-	if (config->rsrc == FRUIT_RSRC_ADFILE) {
-		ad_setentrylen(ad, ADEID_RFORK, offset);
-		rc = ad_write(ad, NULL);
-		if (rc != 0) {
-			return -1;
-		}
-		DEBUG(10, ("fruit_ftruncate_rsrc file %s offset %jd\n",
-			   fsp_str_dbg(fsp), (intmax_t)offset));
-	}
 
-	return 0;
+	return ret;
 }
 
 static int fruit_ftruncate(struct vfs_handle_struct *handle,
 			   struct files_struct *fsp,
 			   off_t offset)
 {
-	int rc = 0;
-        struct adouble *ad =
-		(struct adouble *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
-
 	DBG_DEBUG("fruit_ftruncate called for file %s offset %.0f\n",
 		   fsp_str_dbg(fsp), (double)offset);
 
@@ -4301,25 +4351,11 @@ static int fruit_ftruncate(struct vfs_handle_struct *handle,
 		return 0;
 	}
 
-	if (ad == NULL) {
-		return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
-	}
-
-	if (!fruit_fsp_recheck(ad, fsp)) {
-		return -1;
-	}
-
-	switch (ad->ad_type) {
-	case ADOUBLE_RSRC:
-		rc = fruit_ftruncate_rsrc(handle, fsp, offset, ad);
-		break;
-
-	default:
-		DBG_ERR("unexpected ad_type [%d]\n", ad->ad_type);
-		return -1;
+	if (is_afpresource_stream(fsp->fsp_name)) {
+		return fruit_ftruncate_rsrc(handle, fsp, offset);
 	}
 
-	return rc;
+	return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
 }
 
 static NTSTATUS fruit_create_file(vfs_handle_struct *handle,
-- 
2.9.3


From 9cbed592414a333d672691e1c025759d5f0e2891 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Fri, 2 Dec 2016 17:00:03 +0100
Subject: [PATCH 29/56] vfs_fruit: refactor readdir_attr_macmeta() resource
 fork size

Move resource fork size calculation to helper functions.

Adds correct handling for the fruit:resource=stream case in
readdir_attr_rfork_size_stream(), this wasn't done previously.

Otherwise no change in behaviour.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12427

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 source3/modules/vfs_fruit.c | 90 +++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 82 insertions(+), 8 deletions(-)

diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c
index cb66fb1..9728d1d 100644
--- a/source3/modules/vfs_fruit.c
+++ b/source3/modules/vfs_fruit.c
@@ -2210,12 +2210,89 @@ static bool readdir_attr_meta_finderi(struct vfs_handle_struct *handle,
 	return true;
 }
 
+static uint64_t readdir_attr_rfork_size_adouble(
+	struct vfs_handle_struct *handle,
+	const struct smb_filename *smb_fname)
+{
+	struct adouble *ad = NULL;
+	uint64_t rfork_size;
+
+	ad = ad_get(talloc_tos(), handle, smb_fname->base_name,
+		    ADOUBLE_RSRC);
+	if (ad == NULL) {
+		return 0;
+	}
+
+	rfork_size = ad_getentrylen(ad, ADEID_RFORK);
+	TALLOC_FREE(ad);
+
+	return rfork_size;
+}
+
+static uint64_t readdir_attr_rfork_size_stream(
+	struct vfs_handle_struct *handle,
+	const struct smb_filename *smb_fname)
+{
+	struct smb_filename *stream_name = NULL;
+	int ret;
+	uint64_t rfork_size;
+
+	stream_name = synthetic_smb_fname(talloc_tos(),
+					  smb_fname->base_name,
+					  AFPRESOURCE_STREAM_NAME,
+					  NULL, 0);
+	if (stream_name == NULL) {
+		return 0;
+	}
+
+	ret = SMB_VFS_STAT(handle->conn, stream_name);
+	if (ret != 0) {
+		TALLOC_FREE(stream_name);
+		return 0;
+	}
+
+	rfork_size = stream_name->st.st_ex_size;
+	TALLOC_FREE(stream_name);
+
+	return rfork_size;
+}
+
+static uint64_t readdir_attr_rfork_size(struct vfs_handle_struct *handle,
+					const struct smb_filename *smb_fname)
+{
+	struct fruit_config_data *config = NULL;
+	uint64_t rfork_size;
+
+	SMB_VFS_HANDLE_GET_DATA(handle, config,
+				struct fruit_config_data,
+				return 0);
+
+	switch (config->rsrc) {
+	case FRUIT_RSRC_ADFILE:
+	case FRUIT_RSRC_XATTR:
+		rfork_size = readdir_attr_rfork_size_adouble(handle,
+							     smb_fname);
+		break;
+
+	case FRUIT_META_STREAM:
+		rfork_size = readdir_attr_rfork_size_stream(handle,
+							    smb_fname);
+		break;
+
+	default:
+		DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
+		rfork_size = 0;
+		break;
+	}
+
+	return rfork_size;
+}
+
 static NTSTATUS readdir_attr_macmeta(struct vfs_handle_struct *handle,
 				     const struct smb_filename *smb_fname,
 				     struct readdir_attr_data *attr_data)
 {
 	NTSTATUS status = NT_STATUS_OK;
-	struct adouble *ad = NULL;
 	struct fruit_config_data *config = NULL;
 	bool ok;
 
@@ -2232,13 +2309,10 @@ static NTSTATUS readdir_attr_macmeta(struct vfs_handle_struct *handle,
 	 */
 
 	if (config->readdir_attr_rsize) {
-		ad = ad_get(talloc_tos(), handle, smb_fname->base_name,
-			    ADOUBLE_RSRC);
-		if (ad) {
-			attr_data->attr_data.aapl.rfork_size = ad_getentrylen(
-				ad, ADEID_RFORK);
-			TALLOC_FREE(ad);
-		}
+		uint64_t rfork_size;
+
+		rfork_size = readdir_attr_rfork_size(handle, smb_fname);
+		attr_data->attr_data.aapl.rfork_size = rfork_size;
 	}
 
 	/*
-- 
2.9.3


From b4a49c42709f342b5f6c8156f59ac4ff05d21932 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Tue, 6 Dec 2016 15:00:58 +0100
Subject: [PATCH 30/56] vfs_catia: run translation on all handle based VFS
 functions

Also run translation of fsp->fsp_name and optionally
fsp->base_fsp->fsp_name if set for all handle based VFS functions.

This is necessary because some modules might use the path for something
in the handle based VFS ops.

An existing example is vfs_fruit. The next commit will remove the
translation that was done there because catia didn't before this commit.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12427

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 source3/modules/vfs_catia.c | 954 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 954 insertions(+)

diff --git a/source3/modules/vfs_catia.c b/source3/modules/vfs_catia.c
index f4c77d9..c454247 100644
--- a/source3/modules/vfs_catia.c
+++ b/source3/modules/vfs_catia.c
@@ -1114,6 +1114,941 @@ catia_setxattr(vfs_handle_struct *handle, const char *path,
 	return ret;
 }
 
+static int catia_fstat(vfs_handle_struct *handle,
+		       files_struct *fsp,
+		       SMB_STRUCT_STAT *sbuf)
+{
+	char *fname = NULL;
+	char *tmp_fname = NULL;
+	char *base_fname = NULL;
+	char *tmp_base_fname = NULL;
+	int ret = -1;
+	NTSTATUS status;
+
+	status = catia_string_replace_allocate(handle->conn,
+					       fsp->fsp_name->base_name,
+					       &fname, vfs_translate_to_unix);
+	if (!NT_STATUS_IS_OK(status)) {
+		errno = map_errno_from_nt_status(status);
+		goto done;
+	}
+
+	if (fsp->base_fsp != NULL) {
+		status = catia_string_replace_allocate(
+			handle->conn,
+			fsp->base_fsp->fsp_name->base_name,
+			&base_fname, vfs_translate_to_unix);
+		if (!NT_STATUS_IS_OK(status)) {
+			errno = map_errno_from_nt_status(status);
+			goto done;
+		}
+
+		tmp_base_fname = fsp->base_fsp->fsp_name->base_name;
+		fsp->base_fsp->fsp_name->base_name = base_fname;
+	}
+
+	tmp_fname = fsp->fsp_name->base_name;
+	fsp->fsp_name->base_name = fname;
+
+	ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
+
+	fsp->fsp_name->base_name = tmp_fname;
+	if (fsp->base_fsp != NULL) {
+		fsp->base_fsp->fsp_name->base_name = tmp_base_fname;
+	}
+
+done:
+	TALLOC_FREE(fname);
+	TALLOC_FREE(base_fname);
+
+	return ret;
+}
+
+static ssize_t catia_pread(vfs_handle_struct *handle,
+			   files_struct *fsp, void *data,
+			   size_t n, off_t offset)
+{
+	char *fname = NULL;
+	char *tmp_fname = NULL;
+	char *base_fname = NULL;
+	char *tmp_base_fname = NULL;
+	int ret = -1;
+	NTSTATUS status;
+
+	status = catia_string_replace_allocate(handle->conn,
+					       fsp->fsp_name->base_name,
+					       &fname, vfs_translate_to_unix);
+	if (!NT_STATUS_IS_OK(status)) {
+		errno = map_errno_from_nt_status(status);
+		goto done;
+	}
+
+	if (fsp->base_fsp != NULL) {
+		status = catia_string_replace_allocate(
+			handle->conn,
+			fsp->base_fsp->fsp_name->base_name,
+			&base_fname, vfs_translate_to_unix);
+		if (!NT_STATUS_IS_OK(status)) {
+			errno = map_errno_from_nt_status(status);
+			goto done;
+		}
+
+		tmp_base_fname = fsp->base_fsp->fsp_name->base_name;
+		fsp->base_fsp->fsp_name->base_name = base_fname;
+	}
+
+	tmp_fname = fsp->fsp_name->base_name;
+	fsp->fsp_name->base_name = fname;
+
+	ret = SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
+
+	fsp->fsp_name->base_name = tmp_fname;
+	if (fsp->base_fsp != NULL) {
+		fsp->base_fsp->fsp_name->base_name = tmp_base_fname;
+	}
+
+done:
+	TALLOC_FREE(fname);
+	TALLOC_FREE(base_fname);
+
+	return ret;
+}
+
+static ssize_t catia_pwrite(vfs_handle_struct *handle,
+			    files_struct *fsp, const void *data,
+			    size_t n, off_t offset)
+{
+	char *fname = NULL;
+	char *tmp_fname = NULL;
+	char *base_fname = NULL;
+	char *tmp_base_fname = NULL;
+	int ret = -1;
+	NTSTATUS status;
+
+	status = catia_string_replace_allocate(handle->conn,
+					       fsp->fsp_name->base_name,
+					       &fname, vfs_translate_to_unix);
+	if (!NT_STATUS_IS_OK(status)) {
+		errno = map_errno_from_nt_status(status);
+		goto done;
+	}
+
+	if (fsp->base_fsp != NULL) {
+		status = catia_string_replace_allocate(
+			handle->conn,
+			fsp->base_fsp->fsp_name->base_name,
+			&base_fname, vfs_translate_to_unix);
+		if (!NT_STATUS_IS_OK(status)) {
+			errno = map_errno_from_nt_status(status);
+			goto done;
+		}
+
+		tmp_base_fname = fsp->base_fsp->fsp_name->base_name;
+		fsp->base_fsp->fsp_name->base_name = base_fname;
+	}
+
+	tmp_fname = fsp->fsp_name->base_name;
+	fsp->fsp_name->base_name = fname;
+
+	ret = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
+
+	fsp->fsp_name->base_name = tmp_fname;
+	if (fsp->base_fsp != NULL) {
+		fsp->base_fsp->fsp_name->base_name = tmp_base_fname;
+	}
+
+done:
+	TALLOC_FREE(fname);
+	TALLOC_FREE(base_fname);
+
+	return ret;
+}
+
+static int catia_ftruncate(struct vfs_handle_struct *handle,
+			   struct files_struct *fsp,
+			   off_t offset)
+{
+	char *fname = NULL;
+	char *tmp_fname = NULL;
+	char *base_fname = NULL;
+	char *tmp_base_fname = NULL;
+	int ret = -1;
+	NTSTATUS status;
+
+	status = catia_string_replace_allocate(handle->conn,
+					       fsp->fsp_name->base_name,
+					       &fname, vfs_translate_to_unix);
+	if (!NT_STATUS_IS_OK(status)) {
+		errno = map_errno_from_nt_status(status);
+		goto done;
+	}
+
+	if (fsp->base_fsp != NULL) {
+		status = catia_string_replace_allocate(
+			handle->conn,
+			fsp->base_fsp->fsp_name->base_name,
+			&base_fname, vfs_translate_to_unix);
+		if (!NT_STATUS_IS_OK(status)) {
+			errno = map_errno_from_nt_status(status);
+			goto done;
+		}
+
+		tmp_base_fname = fsp->base_fsp->fsp_name->base_name;
+		fsp->base_fsp->fsp_name->base_name = base_fname;
+	}
+
+	tmp_fname = fsp->fsp_name->base_name;
+	fsp->fsp_name->base_name = fname;
+
+	ret = SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
+
+	fsp->fsp_name->base_name = tmp_fname;
+	if (fsp->base_fsp != NULL) {
+		fsp->base_fsp->fsp_name->base_name = tmp_base_fname;
+	}
+
+done:
+	TALLOC_FREE(fname);
+	TALLOC_FREE(base_fname);
+
+	return ret;
+}
+
+static int catia_fallocate(struct vfs_handle_struct *handle,
+			   struct files_struct *fsp,
+			   uint32_t mode,
+			   off_t offset,
+			   off_t len)
+{
+	char *fname = NULL;
+	char *tmp_fname = NULL;
+	char *base_fname = NULL;
+	char *tmp_base_fname = NULL;
+	int ret = -1;
+	NTSTATUS status;
+
+	status = catia_string_replace_allocate(handle->conn,
+					       fsp->fsp_name->base_name,
+					       &fname, vfs_translate_to_unix);
+	if (!NT_STATUS_IS_OK(status)) {
+		errno = map_errno_from_nt_status(status);
+		goto done;
+	}
+
+	if (fsp->base_fsp != NULL) {
+		status = catia_string_replace_allocate(
+			handle->conn,
+			fsp->base_fsp->fsp_name->base_name,
+			&base_fname, vfs_translate_to_unix);
+		if (!NT_STATUS_IS_OK(status)) {
+			errno = map_errno_from_nt_status(status);
+			goto done;
+		}
+
+		tmp_base_fname = fsp->base_fsp->fsp_name->base_name;
+		fsp->base_fsp->fsp_name->base_name = base_fname;
+	}
+
+	tmp_fname = fsp->fsp_name->base_name;
+	fsp->fsp_name->base_name = fname;
+
+	ret = SMB_VFS_NEXT_FALLOCATE(handle, fsp, mode, offset, len);
+
+	fsp->fsp_name->base_name = tmp_fname;
+	if (fsp->base_fsp != NULL) {
+		fsp->base_fsp->fsp_name->base_name = tmp_base_fname;
+	}
+
+done:
+	TALLOC_FREE(fname);
+	TALLOC_FREE(base_fname);
+
+	return ret;
+}
+
+static ssize_t catia_fgetxattr(struct vfs_handle_struct *handle, struct files_struct *fsp, const char *name, void *value, size_t size)
+{
+	char *fname = NULL;
+	char *tmp_fname = NULL;
+	char *base_fname = NULL;
+	char *tmp_base_fname = NULL;
+	ssize_t result = -1;
+	NTSTATUS status;
+
+	status = catia_string_replace_allocate(handle->conn,
+					       fsp->fsp_name->base_name,
+					       &fname, vfs_translate_to_unix);
+	if (!NT_STATUS_IS_OK(status)) {
+		errno = map_errno_from_nt_status(status);
+		goto done;
+	}
+
+	if (fsp->base_fsp != NULL) {
+		status = catia_string_replace_allocate(
+			handle->conn,
+			fsp->base_fsp->fsp_name->base_name,
+			&base_fname, vfs_translate_to_unix);
+		if (!NT_STATUS_IS_OK(status)) {
+			errno = map_errno_from_nt_status(status);
+			goto done;
+		}
+
+		tmp_base_fname = fsp->base_fsp->fsp_name->base_name;
+		fsp->base_fsp->fsp_name->base_name = base_fname;
+	}
+
+	tmp_fname = fsp->fsp_name->base_name;
+	fsp->fsp_name->base_name = fname;
+
+	result = SMB_VFS_NEXT_FGETXATTR(handle, fsp, name, value, size);
+
+	fsp->fsp_name->base_name = tmp_fname;
+	if (fsp->base_fsp != NULL) {
+		fsp->base_fsp->fsp_name->base_name = tmp_base_fname;
+	}
+
+done:
+	TALLOC_FREE(fname);
+	TALLOC_FREE(base_fname);
+
+	return result;
+}
+
+static ssize_t catia_flistxattr(struct vfs_handle_struct *handle, struct files_struct *fsp, char *list, size_t size)
+{
+	char *fname = NULL;
+	char *tmp_fname = NULL;
+	char *base_fname = NULL;
+	char *tmp_base_fname = NULL;
+	ssize_t result = -1;
+	NTSTATUS status;
+
+	status = catia_string_replace_allocate(handle->conn,
+					       fsp->fsp_name->base_name,
+					       &fname, vfs_translate_to_unix);
+	if (!NT_STATUS_IS_OK(status)) {
+		errno = map_errno_from_nt_status(status);
+		goto done;
+	}
+
+	if (fsp->base_fsp != NULL) {
+		status = catia_string_replace_allocate(
+			handle->conn,
+			fsp->base_fsp->fsp_name->base_name,
+			&base_fname, vfs_translate_to_unix);
+		if (!NT_STATUS_IS_OK(status)) {
+			errno = map_errno_from_nt_status(status);
+			goto done;
+		}
+
+		tmp_base_fname = fsp->base_fsp->fsp_name->base_name;
+		fsp->base_fsp->fsp_name->base_name = base_fname;
+	}
+
+	tmp_fname = fsp->fsp_name->base_name;
+	fsp->fsp_name->base_name = fname;
+
+	result = SMB_VFS_NEXT_FLISTXATTR(handle, fsp, list, size);
+
+	fsp->fsp_name->base_name = tmp_fname;
+	if (fsp->base_fsp != NULL) {
+		fsp->base_fsp->fsp_name->base_name = tmp_base_fname;
+	}
+
+done:
+	TALLOC_FREE(fname);
+	TALLOC_FREE(base_fname);
+
+	return result;
+}
+
+static int catia_fremovexattr(struct vfs_handle_struct *handle, struct files_struct *fsp, const char *name)
+{
+	char *fname = NULL;
+	char *tmp_fname = NULL;
+	char *base_fname = NULL;
+	char *tmp_base_fname = NULL;
+	int ret = -1;
+	NTSTATUS status;
+
+	status = catia_string_replace_allocate(handle->conn,
+					       fsp->fsp_name->base_name,
+					       &fname, vfs_translate_to_unix);
+	if (!NT_STATUS_IS_OK(status)) {
+		errno = map_errno_from_nt_status(status);
+		goto done;
+	}
+
+	if (fsp->base_fsp != NULL) {
+		status = catia_string_replace_allocate(
+			handle->conn,
+			fsp->base_fsp->fsp_name->base_name,
+			&base_fname, vfs_translate_to_unix);
+		if (!NT_STATUS_IS_OK(status)) {
+			errno = map_errno_from_nt_status(status);
+			goto done;
+		}
+
+		tmp_base_fname = fsp->base_fsp->fsp_name->base_name;
+		fsp->base_fsp->fsp_name->base_name = base_fname;
+	}
+
+	tmp_fname = fsp->fsp_name->base_name;
+	fsp->fsp_name->base_name = fname;
+
+	ret = SMB_VFS_NEXT_FREMOVEXATTR(handle, fsp, name);
+
+	fsp->fsp_name->base_name = tmp_fname;
+	if (fsp->base_fsp != NULL) {
+		fsp->base_fsp->fsp_name->base_name = tmp_base_fname;
+	}
+
+done:
+	TALLOC_FREE(fname);
+	TALLOC_FREE(base_fname);
+
+	return ret;
+}
+
+static int catia_fsetxattr(struct vfs_handle_struct *handle, struct files_struct *fsp, const char *name, const void *value, size_t size, int flags)
+{
+	char *fname = NULL;
+	char *tmp_fname = NULL;
+	char *base_fname = NULL;
+	char *tmp_base_fname = NULL;
+	int ret = -1;
+	NTSTATUS status;
+
+	status = catia_string_replace_allocate(handle->conn,
+					       fsp->fsp_name->base_name,
+					       &fname, vfs_translate_to_unix);
+	if (!NT_STATUS_IS_OK(status)) {
+		errno = map_errno_from_nt_status(status);
+		goto done;
+	}
+
+	if (fsp->base_fsp != NULL) {
+		status = catia_string_replace_allocate(
+			handle->conn,
+			fsp->base_fsp->fsp_name->base_name,
+			&base_fname, vfs_translate_to_unix);
+		if (!NT_STATUS_IS_OK(status)) {
+			errno = map_errno_from_nt_status(status);
+			goto done;
+		}
+
+		tmp_base_fname = fsp->base_fsp->fsp_name->base_name;
+		fsp->base_fsp->fsp_name->base_name = base_fname;
+	}
+
+	tmp_fname = fsp->fsp_name->base_name;
+	fsp->fsp_name->base_name = fname;
+
+	ret = SMB_VFS_NEXT_FSETXATTR(handle, fsp, name, value, size, flags);
+
+	fsp->fsp_name->base_name = tmp_fname;
+	if (fsp->base_fsp != NULL) {
+		fsp->base_fsp->fsp_name->base_name = tmp_base_fname;
+	}
+
+done:
+	TALLOC_FREE(fname);
+	TALLOC_FREE(base_fname);
+
+	return ret;
+}
+
+static SMB_ACL_T catia_sys_acl_get_fd(vfs_handle_struct *handle,
+				      files_struct *fsp,
+				      TALLOC_CTX *mem_ctx)
+{
+	struct smb_acl_t *result = NULL;
+	char *fname = NULL;
+	char *tmp_fname = NULL;
+	char *base_fname = NULL;
+	char *tmp_base_fname = NULL;
+	NTSTATUS status;
+
+	status = catia_string_replace_allocate(handle->conn,
+					       fsp->fsp_name->base_name,
+					       &fname, vfs_translate_to_unix);
+	if (!NT_STATUS_IS_OK(status)) {
+		errno = map_errno_from_nt_status(status);
+		goto done;
+	}
+
+	if (fsp->base_fsp != NULL) {
+		status = catia_string_replace_allocate(
+			handle->conn,
+			fsp->base_fsp->fsp_name->base_name,
+			&base_fname, vfs_translate_to_unix);
+		if (!NT_STATUS_IS_OK(status)) {
+			errno = map_errno_from_nt_status(status);
+			goto done;
+		}
+
+		tmp_base_fname = fsp->base_fsp->fsp_name->base_name;
+		fsp->base_fsp->fsp_name->base_name = base_fname;
+	}
+
+	tmp_fname = fsp->fsp_name->base_name;
+	fsp->fsp_name->base_name = fname;
+
+	result = SMB_VFS_NEXT_SYS_ACL_GET_FD(handle, fsp, mem_ctx);
+
+	fsp->fsp_name->base_name = tmp_fname;
+	if (fsp->base_fsp != NULL) {
+		fsp->base_fsp->fsp_name->base_name = tmp_base_fname;
+	}
+
+done:
+	TALLOC_FREE(fname);
+	TALLOC_FREE(base_fname);
+
+	return result;
+}
+
+static int catia_sys_acl_blob_get_fd(vfs_handle_struct *handle,
+				     files_struct *fsp,
+				     TALLOC_CTX *mem_ctx,
+				     char **blob_description,
+				     DATA_BLOB *blob)
+{
+	char *fname = NULL;
+	char *tmp_fname = NULL;
+	char *base_fname = NULL;
+	char *tmp_base_fname = NULL;
+	int ret = -1;
+	NTSTATUS status;
+
+	status = catia_string_replace_allocate(handle->conn,
+					       fsp->fsp_name->base_name,
+					       &fname, vfs_translate_to_unix);
+	if (!NT_STATUS_IS_OK(status)) {
+		errno = map_errno_from_nt_status(status);
+		goto done;
+	}
+
+	if (fsp->base_fsp != NULL) {
+		status = catia_string_replace_allocate(
+			handle->conn,
+			fsp->base_fsp->fsp_name->base_name,
+			&base_fname, vfs_translate_to_unix);
+		if (!NT_STATUS_IS_OK(status)) {
+			errno = map_errno_from_nt_status(status);
+			goto done;
+		}
+
+		tmp_base_fname = fsp->base_fsp->fsp_name->base_name;
+		fsp->base_fsp->fsp_name->base_name = base_fname;
+	}
+
+	tmp_fname = fsp->fsp_name->base_name;
+	fsp->fsp_name->base_name = fname;
+
+	ret = SMB_VFS_NEXT_SYS_ACL_BLOB_GET_FD(handle, fsp, mem_ctx,
+					       blob_description, blob);
+
+	fsp->fsp_name->base_name = tmp_fname;
+	if (fsp->base_fsp != NULL) {
+		fsp->base_fsp->fsp_name->base_name = tmp_base_fname;
+	}
+
+done:
+	TALLOC_FREE(fname);
+	TALLOC_FREE(base_fname);
+
+	return ret;
+}
+
+static int catia_sys_acl_set_fd(vfs_handle_struct *handle, files_struct *fsp, SMB_ACL_T theacl)
+{
+	char *fname = NULL;
+	char *tmp_fname = NULL;
+	char *base_fname = NULL;
+	char *tmp_base_fname = NULL;
+	int ret = -1;
+	NTSTATUS status;
+
+	status = catia_string_replace_allocate(handle->conn,
+					       fsp->fsp_name->base_name,
+					       &fname, vfs_translate_to_unix);
+	if (!NT_STATUS_IS_OK(status)) {
+		errno = map_errno_from_nt_status(status);
+		goto done;
+	}
+
+	if (fsp->base_fsp != NULL) {
+		status = catia_string_replace_allocate(
+			handle->conn,
+			fsp->base_fsp->fsp_name->base_name,
+			&base_fname, vfs_translate_to_unix);
+		if (!NT_STATUS_IS_OK(status)) {
+			errno = map_errno_from_nt_status(status);
+			goto done;
+		}
+
+		tmp_base_fname = fsp->base_fsp->fsp_name->base_name;
+		fsp->base_fsp->fsp_name->base_name = base_fname;
+	}
+
+	tmp_fname = fsp->fsp_name->base_name;
+	fsp->fsp_name->base_name = fname;
+
+	ret = SMB_VFS_NEXT_SYS_ACL_SET_FD(handle, fsp, theacl);
+
+	fsp->fsp_name->base_name = tmp_fname;
+	if (fsp->base_fsp != NULL) {
+		fsp->base_fsp->fsp_name->base_name = tmp_base_fname;
+	}
+
+done:
+	TALLOC_FREE(fname);
+	TALLOC_FREE(base_fname);
+
+	return ret;
+}
+
+static int catia_fchmod_acl(vfs_handle_struct *handle, files_struct *fsp, mode_t mode)
+{
+	char *fname = NULL;
+	char *tmp_fname = NULL;
+	char *base_fname = NULL;
+	char *tmp_base_fname = NULL;
+	int ret = -1;
+	NTSTATUS status;
+
+	status = catia_string_replace_allocate(handle->conn,
+					       fsp->fsp_name->base_name,
+					       &fname, vfs_translate_to_unix);
+	if (!NT_STATUS_IS_OK(status)) {
+		errno = map_errno_from_nt_status(status);
+		goto done;
+	}
+
+	if (fsp->base_fsp != NULL) {
+		status = catia_string_replace_allocate(
+			handle->conn,
+			fsp->base_fsp->fsp_name->base_name,
+			&base_fname, vfs_translate_to_unix);
+		if (!NT_STATUS_IS_OK(status)) {
+			errno = map_errno_from_nt_status(status);
+			goto done;
+		}
+
+		tmp_base_fname = fsp->base_fsp->fsp_name->base_name;
+		fsp->base_fsp->fsp_name->base_name = base_fname;
+	}
+
+	tmp_fname = fsp->fsp_name->base_name;
+	fsp->fsp_name->base_name = fname;
+
+	ret = SMB_VFS_NEXT_FCHMOD_ACL(handle, fsp, mode);
+
+	fsp->fsp_name->base_name = tmp_fname;
+	if (fsp->base_fsp != NULL) {
+		fsp->base_fsp->fsp_name->base_name = tmp_base_fname;
+	}
+
+done:
+	TALLOC_FREE(fname);
+	TALLOC_FREE(base_fname);
+
+	return ret;
+}
+
+static NTSTATUS catia_fget_nt_acl(vfs_handle_struct *handle,
+				  files_struct *fsp,
+				  uint32_t security_info,
+				  TALLOC_CTX *mem_ctx,
+				  struct security_descriptor **ppdesc)
+{
+	char *fname = NULL;
+	char *tmp_fname = NULL;
+	char *base_fname = NULL;
+	char *tmp_base_fname = NULL;
+	NTSTATUS status;
+
+	status = catia_string_replace_allocate(handle->conn,
+					       fsp->fsp_name->base_name,
+					       &fname, vfs_translate_to_unix);
+	if (!NT_STATUS_IS_OK(status)) {
+		errno = map_errno_from_nt_status(status);
+		goto done;
+	}
+
+	if (fsp->base_fsp != NULL) {
+		status = catia_string_replace_allocate(
+			handle->conn,
+			fsp->base_fsp->fsp_name->base_name,
+			&base_fname, vfs_translate_to_unix);
+		if (!NT_STATUS_IS_OK(status)) {
+			errno = map_errno_from_nt_status(status);
+			goto done;
+		}
+
+		tmp_base_fname = fsp->base_fsp->fsp_name->base_name;
+		fsp->base_fsp->fsp_name->base_name = base_fname;
+	}
+
+	tmp_fname = fsp->fsp_name->base_name;
+	fsp->fsp_name->base_name = fname;
+
+	status = SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info, mem_ctx, ppdesc);
+
+	fsp->fsp_name->base_name = tmp_fname;
+	if (fsp->base_fsp != NULL) {
+		fsp->base_fsp->fsp_name->base_name = tmp_base_fname;
+	}
+
+done:
+	TALLOC_FREE(fname);
+	TALLOC_FREE(base_fname);
+
+	return status;
+}
+
+static NTSTATUS catia_fset_nt_acl(vfs_handle_struct *handle, files_struct *fsp, uint32_t security_info_sent, const struct security_descriptor *psd)
+{
+	char *fname = NULL;
+	char *tmp_fname = NULL;
+	char *base_fname = NULL;
+	char *tmp_base_fname = NULL;
+	NTSTATUS status;
+
+	status = catia_string_replace_allocate(handle->conn,
+					       fsp->fsp_name->base_name,
+					       &fname, vfs_translate_to_unix);
+	if (!NT_STATUS_IS_OK(status)) {
+		errno = map_errno_from_nt_status(status);
+		goto done;
+	}
+
+	if (fsp->base_fsp != NULL) {
+		status = catia_string_replace_allocate(
+			handle->conn,
+			fsp->base_fsp->fsp_name->base_name,
+			&base_fname, vfs_translate_to_unix);
+		if (!NT_STATUS_IS_OK(status)) {
+			errno = map_errno_from_nt_status(status);
+			goto done;
+		}
+
+		tmp_base_fname = fsp->base_fsp->fsp_name->base_name;
+		fsp->base_fsp->fsp_name->base_name = base_fname;
+	}
+
+	tmp_fname = fsp->fsp_name->base_name;
+	fsp->fsp_name->base_name = fname;
+
+	status = SMB_VFS_NEXT_FSET_NT_ACL(handle, fsp, security_info_sent, psd);
+
+	fsp->fsp_name->base_name = tmp_fname;
+	if (fsp->base_fsp != NULL) {
+		fsp->base_fsp->fsp_name->base_name = tmp_base_fname;
+	}
+
+done:
+	TALLOC_FREE(fname);
+	TALLOC_FREE(base_fname);
+
+	return status;
+}
+
+static NTSTATUS catia_fset_dos_attributes(struct vfs_handle_struct *handle,
+					  struct files_struct *fsp,
+					  uint32_t dosmode)
+{
+	char *fname = NULL;
+	char *tmp_fname = NULL;
+	char *base_fname = NULL;
+	char *tmp_base_fname = NULL;
+	NTSTATUS status;
+
+	status = catia_string_replace_allocate(handle->conn,
+					       fsp->fsp_name->base_name,
+					       &fname, vfs_translate_to_unix);
+	if (!NT_STATUS_IS_OK(status)) {
+		errno = map_errno_from_nt_status(status);
+		goto done;
+	}
+
+	if (fsp->base_fsp != NULL) {
+		status = catia_string_replace_allocate(
+			handle->conn,
+			fsp->base_fsp->fsp_name->base_name,
+			&base_fname, vfs_translate_to_unix);
+		if (!NT_STATUS_IS_OK(status)) {
+			errno = map_errno_from_nt_status(status);
+			goto done;
+		}
+
+		tmp_base_fname = fsp->base_fsp->fsp_name->base_name;
+		fsp->base_fsp->fsp_name->base_name = base_fname;
+	}
+
+	tmp_fname = fsp->fsp_name->base_name;
+	fsp->fsp_name->base_name = fname;
+
+	status = SMB_VFS_NEXT_FSET_DOS_ATTRIBUTES(handle, fsp, dosmode);
+
+	fsp->fsp_name->base_name = tmp_fname;
+	if (fsp->base_fsp != NULL) {
+		fsp->base_fsp->fsp_name->base_name = tmp_base_fname;
+	}
+
+done:
+	TALLOC_FREE(fname);
+	TALLOC_FREE(base_fname);
+
+	return status;
+}
+
+static NTSTATUS catia_fget_dos_attributes(struct vfs_handle_struct *handle,
+					  struct files_struct *fsp,
+					  uint32_t *dosmode)
+{
+	char *fname = NULL;
+	char *tmp_fname = NULL;
+	char *base_fname = NULL;
+	char *tmp_base_fname = NULL;
+	NTSTATUS status;
+
+	status = catia_string_replace_allocate(handle->conn,
+					       fsp->fsp_name->base_name,
+					       &fname, vfs_translate_to_unix);
+	if (!NT_STATUS_IS_OK(status)) {
+		errno = map_errno_from_nt_status(status);
+		goto done;
+	}
+
+	if (fsp->base_fsp != NULL) {
+		status = catia_string_replace_allocate(
+			handle->conn,
+			fsp->base_fsp->fsp_name->base_name,
+			&base_fname, vfs_translate_to_unix);
+		if (!NT_STATUS_IS_OK(status)) {
+			errno = map_errno_from_nt_status(status);
+			goto done;
+		}
+
+		tmp_base_fname = fsp->base_fsp->fsp_name->base_name;
+		fsp->base_fsp->fsp_name->base_name = base_fname;
+	}
+
+	tmp_fname = fsp->fsp_name->base_name;
+	fsp->fsp_name->base_name = fname;
+
+	status = SMB_VFS_NEXT_FGET_DOS_ATTRIBUTES(handle, fsp, dosmode);
+
+	fsp->fsp_name->base_name = tmp_fname;
+	if (fsp->base_fsp != NULL) {
+		fsp->base_fsp->fsp_name->base_name = tmp_base_fname;
+	}
+
+done:
+	TALLOC_FREE(fname);
+	TALLOC_FREE(base_fname);
+
+	return status;
+}
+
+static int catia_fchown(vfs_handle_struct *handle, files_struct *fsp, uid_t uid, gid_t gid)
+{
+	char *fname = NULL;
+	char *tmp_fname = NULL;
+	char *base_fname = NULL;
+	char *tmp_base_fname = NULL;
+	int ret = -1;
+	NTSTATUS status;
+
+	status = catia_string_replace_allocate(handle->conn,
+					       fsp->fsp_name->base_name,
+					       &fname, vfs_translate_to_unix);
+	if (!NT_STATUS_IS_OK(status)) {
+		errno = map_errno_from_nt_status(status);
+		goto done;
+	}
+
+	if (fsp->base_fsp != NULL) {
+		status = catia_string_replace_allocate(
+			handle->conn,
+			fsp->base_fsp->fsp_name->base_name,
+			&base_fname, vfs_translate_to_unix);
+		if (!NT_STATUS_IS_OK(status)) {
+			errno = map_errno_from_nt_status(status);
+			goto done;
+		}
+
+		tmp_base_fname = fsp->base_fsp->fsp_name->base_name;
+		fsp->base_fsp->fsp_name->base_name = base_fname;
+	}
+
+	tmp_fname = fsp->fsp_name->base_name;
+	fsp->fsp_name->base_name = fname;
+
+	ret = SMB_VFS_NEXT_FCHOWN(handle, fsp, uid, gid);
+
+	fsp->fsp_name->base_name = tmp_fname;
+	if (fsp->base_fsp != NULL) {
+		fsp->base_fsp->fsp_name->base_name = tmp_base_fname;
+	}
+
+done:
+	TALLOC_FREE(fname);
+	TALLOC_FREE(base_fname);
+
+	return ret;
+}
+
+static int catia_fchmod(vfs_handle_struct *handle, files_struct *fsp, mode_t mode)
+{
+	char *fname = NULL;
+	char *tmp_fname = NULL;
+	char *base_fname = NULL;
+	char *tmp_base_fname = NULL;
+	int ret = -1;
+	NTSTATUS status;
+
+	status = catia_string_replace_allocate(handle->conn,
+					       fsp->fsp_name->base_name,
+					       &fname, vfs_translate_to_unix);
+	if (!NT_STATUS_IS_OK(status)) {
+		errno = map_errno_from_nt_status(status);
+		goto done;
+	}
+
+	if (fsp->base_fsp != NULL) {
+		status = catia_string_replace_allocate(
+			handle->conn,
+			fsp->base_fsp->fsp_name->base_name,
+			&base_fname, vfs_translate_to_unix);
+		if (!NT_STATUS_IS_OK(status)) {
+			errno = map_errno_from_nt_status(status);
+			goto done;
+		}
+
+		tmp_base_fname = fsp->base_fsp->fsp_name->base_name;
+		fsp->base_fsp->fsp_name->base_name = base_fname;
+	}
+
+	tmp_fname = fsp->fsp_name->base_name;
+	fsp->fsp_name->base_name = fname;
+
+	ret = SMB_VFS_NEXT_FCHMOD(handle, fsp, mode);
+
+	fsp->fsp_name->base_name = tmp_fname;
+	if (fsp->base_fsp != NULL) {
+		fsp->base_fsp->fsp_name->base_name = tmp_base_fname;
+	}
+
+done:
+	TALLOC_FREE(fname);
+	TALLOC_FREE(base_fname);
+
+	return ret;
+}
+
 static struct vfs_fn_pointers vfs_catia_fns = {
 	.mkdir_fn = catia_mkdir,
 	.rmdir_fn = catia_rmdir,
@@ -1141,6 +2076,25 @@ static struct vfs_fn_pointers vfs_catia_fns = {
 	.listxattr_fn = catia_listxattr,
 	.removexattr_fn = catia_removexattr,
 	.setxattr_fn = catia_setxattr,
+	.pread_fn = catia_pread,
+	.pwrite_fn = catia_pwrite,
+	.fstat_fn = catia_fstat,
+	.ftruncate_fn = catia_ftruncate,
+	.fallocate_fn = catia_fallocate,
+	.fgetxattr_fn = catia_fgetxattr,
+	.flistxattr_fn = catia_flistxattr,
+	.fremovexattr_fn = catia_fremovexattr,
+	.fsetxattr_fn = catia_fsetxattr,
+	.sys_acl_get_fd_fn = catia_sys_acl_get_fd,
+	.sys_acl_blob_get_fd_fn = catia_sys_acl_blob_get_fd,
+	.sys_acl_set_fd_fn = catia_sys_acl_set_fd,
+	.fchmod_acl_fn = catia_fchmod_acl,
+	.fget_nt_acl_fn = catia_fget_nt_acl,
+	.fset_nt_acl_fn = catia_fset_nt_acl,
+	.fset_dos_attributes_fn = catia_fset_dos_attributes,
+	.fget_dos_attributes_fn = catia_fget_dos_attributes,
+	.fchown_fn = catia_fchown,
+	.fchmod_fn = catia_fchmod,
 };
 
 static_decl_vfs;
-- 
2.9.3


From 81803591895e2301de10bfa25241c11935d8cae3 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Tue, 6 Dec 2016 15:07:20 +0100
Subject: [PATCH 31/56] vfs_fruit: remove base_fsp name translation

This is now nicely done by vfs_catia for us.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12427

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 source3/modules/vfs_fruit.c | 83 ++-------------------------------------------
 1 file changed, 3 insertions(+), 80 deletions(-)

diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c
index 9728d1d..a39f3f2 100644
--- a/source3/modules/vfs_fruit.c
+++ b/source3/modules/vfs_fruit.c
@@ -3336,9 +3336,6 @@ static ssize_t fruit_pread(vfs_handle_struct *handle,
 	struct fruit_config_data *config = NULL;
 	AfpInfo *ai = NULL;
 	ssize_t len = -1;
-	char *name = NULL;
-	char *tmp_base_name = NULL;
-	NTSTATUS status;
 	size_t to_return = n;
 
 	DEBUG(10, ("fruit_pread: offset=%d, size=%d\n", (int)offset, (int)n));
@@ -3350,25 +3347,6 @@ static ssize_t fruit_pread(vfs_handle_struct *handle,
 	SMB_VFS_HANDLE_GET_DATA(handle, config,
 				struct fruit_config_data, return -1);
 
-	/* fsp_name is not converted with vfs_catia */
-	tmp_base_name = fsp->base_fsp->fsp_name->base_name;
-	status = SMB_VFS_TRANSLATE_NAME(handle->conn,
-					fsp->base_fsp->fsp_name->base_name,
-					vfs_translate_to_unix,
-					talloc_tos(), &name);
-	if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
-		name = talloc_strdup(talloc_tos(), tmp_base_name);
-		if (name == NULL) {
-			rc = -1;
-			goto exit;
-		}
-	} else if (!NT_STATUS_IS_OK(status)) {
-		errno = map_errno_from_nt_status(status);
-		rc = -1;
-		goto exit;
-	}
-	fsp->base_fsp->fsp_name->base_name = name;
-
 	if (is_afpinfo_stream(fsp->fsp_name)) {
 		/*
 		 * OS X has a off-by-1 error in the offset calculation, so we're
@@ -3449,8 +3427,6 @@ static ssize_t fruit_pread(vfs_handle_struct *handle,
 		}
 	}
 exit:
-	fsp->base_fsp->fsp_name->base_name = tmp_base_name;
-	TALLOC_FREE(name);
 	TALLOC_FREE(ai);
 	if (rc != 0) {
 		len = -1;
@@ -3469,9 +3445,6 @@ static ssize_t fruit_pwrite(vfs_handle_struct *handle,
 	struct fruit_config_data *config = NULL;
 	AfpInfo *ai = NULL;
 	ssize_t len;
-	char *name = NULL;
-	char *tmp_base_name = NULL;
-	NTSTATUS status;
 
 	DEBUG(10, ("fruit_pwrite: offset=%d, size=%d\n", (int)offset, (int)n));
 
@@ -3482,24 +3455,6 @@ static ssize_t fruit_pwrite(vfs_handle_struct *handle,
 	SMB_VFS_HANDLE_GET_DATA(handle, config,
 				struct fruit_config_data, return -1);
 
-	tmp_base_name = fsp->base_fsp->fsp_name->base_name;
-	status = SMB_VFS_TRANSLATE_NAME(handle->conn,
-					fsp->base_fsp->fsp_name->base_name,
-					vfs_translate_to_unix,
-					talloc_tos(), &name);
-	if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
-		name = talloc_strdup(talloc_tos(), tmp_base_name);
-		if (name == NULL) {
-			rc = -1;
-			goto exit;
-		}
-	} else if (!NT_STATUS_IS_OK(status)) {
-		errno = map_errno_from_nt_status(status);
-		rc = -1;
-		goto exit;
-	}
-	fsp->base_fsp->fsp_name->base_name = name;
-
 	if (is_afpinfo_stream(fsp->fsp_name)) {
 		/*
 		 * Writing an all 0 blob to the metadata stream
@@ -3577,7 +3532,7 @@ static ssize_t fruit_pwrite(vfs_handle_struct *handle,
 		}
 
 		memcpy(p, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
-		rc = ad_write(ad, name);
+		rc = ad_write(ad, fsp->base_fsp->fsp_name->base_name);
 	} else {
 		len = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n,
                                    offset + ad_getentryoff(ad, ADEID_RFORK));
@@ -3587,7 +3542,7 @@ static ssize_t fruit_pwrite(vfs_handle_struct *handle,
 		}
 
 		if (config->rsrc == FRUIT_RSRC_ADFILE) {
-			rc = ad_read(ad, name);
+			rc = ad_read(ad, fsp->base_fsp->fsp_name->base_name);
 			if (rc == -1) {
 				goto exit;
 			}
@@ -3595,14 +3550,12 @@ static ssize_t fruit_pwrite(vfs_handle_struct *handle,
 
 			if ((len + offset) > ad_getentrylen(ad, ADEID_RFORK)) {
 				ad_setentrylen(ad, ADEID_RFORK, len + offset);
-				rc = ad_write(ad, name);
+				rc = ad_write(ad, fsp->base_fsp->fsp_name->base_name);
 			}
 		}
 	}
 
 exit:
-	fsp->base_fsp->fsp_name->base_name = tmp_base_name;
-	TALLOC_FREE(name);
 	TALLOC_FREE(ai);
 	if (rc != 0) {
 		return -1;
@@ -3946,38 +3899,12 @@ static int fruit_fstat(vfs_handle_struct *handle, files_struct *fsp,
 		       SMB_STRUCT_STAT *sbuf)
 {
 	int rc;
-	char *name = NULL;
-	char *tmp_base_name = NULL;
-	NTSTATUS status;
 	struct adouble *ad = (struct adouble *)
 		VFS_FETCH_FSP_EXTENSION(handle, fsp);
 
 	DEBUG(10, ("fruit_fstat called for %s\n",
 		   smb_fname_str_dbg(fsp->fsp_name)));
 
-	if (fsp->base_fsp) {
-		tmp_base_name = fsp->base_fsp->fsp_name->base_name;
-		/* fsp_name is not converted with vfs_catia */
-		status = SMB_VFS_TRANSLATE_NAME(
-			handle->conn,
-			fsp->base_fsp->fsp_name->base_name,
-			vfs_translate_to_unix,
-			talloc_tos(), &name);
-
-		if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
-			name = talloc_strdup(talloc_tos(), tmp_base_name);
-			if (name == NULL) {
-				rc = -1;
-				goto exit;
-			}
-		} else if (!NT_STATUS_IS_OK(status)) {
-			errno = map_errno_from_nt_status(status);
-			rc = -1;
-			goto exit;
-		}
-		fsp->base_fsp->fsp_name->base_name = name;
-	}
-
 	if (ad == NULL || fsp->base_fsp == NULL) {
 		rc = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
 		goto exit;
@@ -4012,10 +3939,6 @@ exit:
 	DEBUG(10, ("fruit_fstat %s, size: %zd\n",
 		   smb_fname_str_dbg(fsp->fsp_name),
 		   (ssize_t)sbuf->st_ex_size));
-	if (tmp_base_name) {
-		fsp->base_fsp->fsp_name->base_name = tmp_base_name;
-	}
-	TALLOC_FREE(name);
 	return rc;
 }
 
-- 
2.9.3


From bc75d7ccd73d2c597e50743a06facd0bf77a7a24 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Thu, 8 Dec 2016 15:44:56 +0100
Subject: [PATCH 32/56] vfs_catia: add catia_readdir_attr

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12427

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 source3/modules/vfs_catia.c | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)

diff --git a/source3/modules/vfs_catia.c b/source3/modules/vfs_catia.c
index c454247..8f4e1a5 100644
--- a/source3/modules/vfs_catia.c
+++ b/source3/modules/vfs_catia.c
@@ -2049,6 +2049,33 @@ done:
 	return ret;
 }
 
+static NTSTATUS catia_readdir_attr(struct vfs_handle_struct *handle,
+				   const struct smb_filename *smb_fname_in,
+				   TALLOC_CTX *mem_ctx,
+				   struct readdir_attr_data **pattr_data)
+{
+	struct smb_filename *smb_fname;
+	char *fname = NULL;
+	NTSTATUS status;
+
+	status = catia_string_replace_allocate(handle->conn,
+					       smb_fname_in->base_name,
+					       &fname,
+					       vfs_translate_to_unix);
+	if (!NT_STATUS_IS_OK(status)) {
+		errno = map_errno_from_nt_status(status);
+		return status;
+	}
+
+	smb_fname = synthetic_smb_fname(talloc_tos(), fname, NULL,
+					&smb_fname_in->st, 0);
+
+	status = SMB_VFS_NEXT_READDIR_ATTR(handle, smb_fname, mem_ctx, pattr_data);
+
+	TALLOC_FREE(smb_fname);
+	return status;
+}
+
 static struct vfs_fn_pointers vfs_catia_fns = {
 	.mkdir_fn = catia_mkdir,
 	.rmdir_fn = catia_rmdir,
@@ -2095,6 +2122,7 @@ static struct vfs_fn_pointers vfs_catia_fns = {
 	.fget_dos_attributes_fn = catia_fget_dos_attributes,
 	.fchown_fn = catia_fchown,
 	.fchmod_fn = catia_fchmod,
+	.readdir_attr_fn = catia_readdir_attr,
 };
 
 static_decl_vfs;
-- 
2.9.3


From 08891e31c24d3d19d2a6cd40d3d1fbc79605b520 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Thu, 8 Dec 2016 17:18:51 +0100
Subject: [PATCH 33/56] vfs_catia: add catia_(g|s)et_dos_attributes

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12427

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 source3/modules/vfs_catia.c | 70 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 70 insertions(+)

diff --git a/source3/modules/vfs_catia.c b/source3/modules/vfs_catia.c
index 8f4e1a5..56d6447 100644
--- a/source3/modules/vfs_catia.c
+++ b/source3/modules/vfs_catia.c
@@ -2076,6 +2076,74 @@ static NTSTATUS catia_readdir_attr(struct vfs_handle_struct *handle,
 	return status;
 }
 
+static NTSTATUS catia_get_dos_attributes(struct vfs_handle_struct *handle,
+					 struct smb_filename *smb_fname,
+					 uint32_t *dosmode)
+{
+	char *mapped_name = NULL;
+	const char *path = smb_fname->base_name;
+	struct smb_filename *mapped_smb_fname = NULL;
+	NTSTATUS status;
+
+	status = catia_string_replace_allocate(handle->conn,
+				path, &mapped_name, vfs_translate_to_unix);
+	if (!NT_STATUS_IS_OK(status)) {
+		errno = map_errno_from_nt_status(status);
+		return status;
+	}
+	mapped_smb_fname = synthetic_smb_fname(talloc_tos(),
+					mapped_name,
+					NULL,
+					NULL,
+					smb_fname->flags);
+	if (mapped_smb_fname == NULL) {
+		TALLOC_FREE(mapped_name);
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	status = SMB_VFS_NEXT_GET_DOS_ATTRIBUTES(handle,
+						 mapped_smb_fname,
+						 dosmode);
+	TALLOC_FREE(mapped_name);
+	TALLOC_FREE(mapped_smb_fname);
+
+	return status;
+}
+
+static NTSTATUS catia_set_dos_attributes(struct vfs_handle_struct *handle,
+					 const struct smb_filename *smb_fname,
+					 uint32_t dosmode)
+{
+	char *mapped_name = NULL;
+	const char *path = smb_fname->base_name;
+	struct smb_filename *mapped_smb_fname = NULL;
+	NTSTATUS status;
+
+	status = catia_string_replace_allocate(handle->conn,
+				path, &mapped_name, vfs_translate_to_unix);
+	if (!NT_STATUS_IS_OK(status)) {
+		errno = map_errno_from_nt_status(status);
+		return status;
+	}
+	mapped_smb_fname = synthetic_smb_fname(talloc_tos(),
+					mapped_name,
+					NULL,
+					NULL,
+					smb_fname->flags);
+	if (mapped_smb_fname == NULL) {
+		TALLOC_FREE(mapped_name);
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	status = SMB_VFS_NEXT_SET_DOS_ATTRIBUTES(handle,
+						 mapped_smb_fname,
+						 dosmode);
+	TALLOC_FREE(mapped_name);
+	TALLOC_FREE(mapped_smb_fname);
+
+	return status;
+}
+
 static struct vfs_fn_pointers vfs_catia_fns = {
 	.mkdir_fn = catia_mkdir,
 	.rmdir_fn = catia_rmdir,
@@ -2118,6 +2186,8 @@ static struct vfs_fn_pointers vfs_catia_fns = {
 	.fchmod_acl_fn = catia_fchmod_acl,
 	.fget_nt_acl_fn = catia_fget_nt_acl,
 	.fset_nt_acl_fn = catia_fset_nt_acl,
+	.get_dos_attributes_fn = catia_get_dos_attributes,
+	.set_dos_attributes_fn = catia_set_dos_attributes,
 	.fset_dos_attributes_fn = catia_fset_dos_attributes,
 	.fget_dos_attributes_fn = catia_fget_dos_attributes,
 	.fchown_fn = catia_fchown,
-- 
2.9.3


From 4417da052609c979c9030b5b1956943f6b8215aa Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Thu, 8 Dec 2016 11:08:53 +0100
Subject: [PATCH 34/56] vfs_fruit: fix fruit_check_access()

Applying fcntl read locks requires an fd opened for reading. This means
we have to check the open flags of an fd and if the fd is not opened for
reading, we can't use it to set shared fctnl locks.

Iow we won't be applying interop locks with Netatalk for files openend
write-only.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12427

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 source3/modules/vfs_fruit.c | 30 ++++++++++++++++++++++++++----
 1 file changed, 26 insertions(+), 4 deletions(-)

diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c
index a39f3f2..0338527 100644
--- a/source3/modules/vfs_fruit.c
+++ b/source3/modules/vfs_fruit.c
@@ -1807,6 +1807,8 @@ static NTSTATUS fruit_check_access(vfs_handle_struct *handle,
 	struct byte_range_lock *br_lck = NULL;
 	bool open_for_reading, open_for_writing, deny_read, deny_write;
 	off_t off;
+	bool have_read = false;
+	int flags;
 
 	/* FIXME: hardcoded data fork, add resource fork */
 	enum apple_fork fork_type = APPLE_FORK_DATA;
@@ -1818,6 +1820,26 @@ static NTSTATUS fruit_check_access(vfs_handle_struct *handle,
 		  deny_mode & DENY_READ ? "DENY_READ" : "-",
 		  deny_mode & DENY_WRITE ? "DENY_WRITE" : "-"));
 
+	if (fsp->fh->fd == -1) {
+		return NT_STATUS_OK;
+	}
+
+	flags = fcntl(fsp->fh->fd, F_GETFL);
+	if (flags == -1) {
+		DBG_ERR("fcntl get flags [%s] fd [%d] failed [%s]\n",
+			fsp_str_dbg(fsp), fsp->fh->fd, strerror(errno));
+		return map_nt_error_from_unix(errno);
+	}
+
+	if (flags & (O_RDONLY|O_RDWR)) {
+		/*
+		 * Applying fcntl read locks requires an fd opened for
+		 * reading. This means we won't be applying locks for
+		 * files openend write-only, but what can we do...
+		 */
+		have_read = true;
+	}
+
 	/*
 	 * Check read access and deny read mode
 	 */
@@ -1839,7 +1861,7 @@ static NTSTATUS fruit_check_access(vfs_handle_struct *handle,
 		}
 
 		/* Set locks */
-		if (access_mask & FILE_READ_DATA) {
+		if ((access_mask & FILE_READ_DATA) && have_read) {
 			off = access_to_netatalk_brl(fork_type, FILE_READ_DATA);
 			br_lck = do_lock(
 				handle->conn->sconn->msg_ctx, fsp,
@@ -1853,7 +1875,7 @@ static NTSTATUS fruit_check_access(vfs_handle_struct *handle,
 			TALLOC_FREE(br_lck);
 		}
 
-		if (deny_mode & DENY_READ) {
+		if ((deny_mode & DENY_READ) && have_read) {
 			off = denymode_to_netatalk_brl(fork_type, DENY_READ);
 			br_lck = do_lock(
 				handle->conn->sconn->msg_ctx, fsp,
@@ -1889,7 +1911,7 @@ static NTSTATUS fruit_check_access(vfs_handle_struct *handle,
 		}
 
 		/* Set locks */
-		if (access_mask & FILE_WRITE_DATA) {
+		if ((access_mask & FILE_WRITE_DATA) && have_read) {
 			off = access_to_netatalk_brl(fork_type, FILE_WRITE_DATA);
 			br_lck = do_lock(
 				handle->conn->sconn->msg_ctx, fsp,
@@ -1903,7 +1925,7 @@ static NTSTATUS fruit_check_access(vfs_handle_struct *handle,
 			TALLOC_FREE(br_lck);
 
 		}
-		if (deny_mode & DENY_WRITE) {
+		if ((deny_mode & DENY_WRITE) && have_read) {
 			off = denymode_to_netatalk_brl(fork_type, DENY_WRITE);
 			br_lck = do_lock(
 				handle->conn->sconn->msg_ctx, fsp,
-- 
2.9.3


From b734cad6910882f58049f064c49e0ea631f976e5 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Thu, 8 Dec 2016 22:54:46 +0100
Subject: [PATCH 35/56] selftest: disable vfs_fruit tests

The next commits will completely change handle based VFS ops. This
couldn't be done in a piecemeal fashion, I had to wipe the slate clean.

Disabling tests in order to not break git bisects.

Tests will be reenabled after a few commits that apply the larger change
in a somewhat digestible fashion.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12427

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 source3/selftest/tests.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/source3/selftest/tests.py b/source3/selftest/tests.py
index 88cdd6a..0c99b1a 100755
--- a/source3/selftest/tests.py
+++ b/source3/selftest/tests.py
@@ -334,7 +334,7 @@ nbt = ["nbt.dgram" ]
 
 libsmbclient = ["libsmbclient"]
 
-vfs = ["vfs.fruit", "vfs.acl_xattr"]
+vfs = ["vfs.acl_xattr"]
 
 tests= base + raw + smb2 + rpc + unix + local + rap + nbt + libsmbclient + idmap + vfs
 
-- 
2.9.3


From d6ee5a4a93f68cf44cc81d8bb8705be6c431523b Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Thu, 8 Dec 2016 19:12:32 +0100
Subject: [PATCH 36/56] vfs_fruit: rework struct adouble API

o factor out ad_open(), opens an fd to be used with IO on the metadata
  streams

o rename ad_write() to ad_set() as this aligns nicely with the existing
  ad_get. This is the pathname based version used to solely set metadata
  on a file, there's also a handle based version:

o add ad_fset(), a handle based version that can be used to set metadata
  and to update the AppleDouble header in a ._ AppleDouble file

o remove fruit_fsp_recheck(), looking at it more closely, it is not
  needed

This commit *compiles*, but all callers in the VFS ops must now be
updated to use the new semantics, this comes next...

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12427

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 source3/modules/vfs_fruit.c | 711 ++++++++++++++++++++++++++------------------
 1 file changed, 421 insertions(+), 290 deletions(-)

diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c
index 0338527..74428d9 100644
--- a/source3/modules/vfs_fruit.c
+++ b/source3/modules/vfs_fruit.c
@@ -351,7 +351,8 @@ struct ad_entry {
 
 struct adouble {
 	vfs_handle_struct        *ad_handle;
-	files_struct             *ad_fsp;
+	int                       ad_fd;
+	bool                      ad_opened;
 	adouble_type_t            ad_type;
 	uint32_t                  ad_magic;
 	uint32_t                  ad_version;
@@ -404,12 +405,21 @@ static const uint32_t set_eid[] = {
 	AD_DEV, AD_INO, AD_SYN, AD_ID
 };
 
+struct fio {
+	/* tcon config handle */
+	struct fruit_config_data *config;
+
+	/* Denote stream type, meta or rsrc */
+	adouble_type_t type;
+};
+
 /*
  * Forward declarations
  */
 static struct adouble *ad_init(TALLOC_CTX *ctx, vfs_handle_struct *handle,
-			       adouble_type_t type, files_struct *fsp);
-static int ad_write(struct adouble *ad, const char *path);
+			       adouble_type_t type);
+static int ad_set(struct adouble *ad, const char *path);
+static int ad_fset(struct adouble *ad, files_struct *fsp);
 static int adouble_path(TALLOC_CTX *ctx, const char *path_in, char **path_out);
 
 /**
@@ -771,7 +781,7 @@ exit:
 /**
  * Read and parse Netatalk AppleDouble metadata xattr
  **/
-static ssize_t ad_header_read_meta(struct adouble *ad, const char *path)
+static ssize_t ad_read_meta(struct adouble *ad, const char *path)
 {
 	int      rc = 0;
 	ssize_t  ealen;
@@ -843,196 +853,220 @@ exit:
 	return ealen;
 }
 
-/**
- * Read and parse resource fork, either ._ AppleDouble file or xattr
- **/
-static ssize_t ad_header_read_rsrc(struct adouble *ad, const char *path)
+static int ad_open_meta(const char *path, int flags, mode_t mode)
+{
+	return open(path, flags, mode);
+}
+
+static int ad_open_rsrc_xattr(const char *path, int flags, mode_t mode)
+{
+#ifdef HAVE_ATTROPEN
+	/* FIXME: direct Solaris xattr syscall */
+	return attropen(path, AFPRESOURCE_EA_NETATALK, flags, mode);
+#else
+	errno = ENOSYS;
+	return -1;
+#endif
+}
+
+static int ad_open_rsrc_adouble(const char *path, int flags, mode_t mode)
+{
+	char *adp = NULL;
+	int ret;
+	int fd;
+
+	ret = adouble_path(talloc_tos(), path, &adp);
+	if (ret != 0) {
+		return -1;
+	}
+
+	fd = open(adp, flags, mode);
+	TALLOC_FREE(adp);
+
+	return fd;
+}
+
+static int ad_open_rsrc(vfs_handle_struct *handle,
+			const char *path,
+			int flags,
+			mode_t mode)
 {
 	struct fruit_config_data *config = NULL;
-	int fd = -1;
-	int rc = 0;
-	ssize_t len;
-	char *adpath = NULL;
-	bool opened = false;
-	int mode;
-	struct adouble *meta_ad = NULL;
-	SMB_STRUCT_STAT sbuf;
-	bool ok;
-	int saved_errno = 0;
+	int fd;
 
-	SMB_VFS_HANDLE_GET_DATA(ad->ad_handle, config,
+	SMB_VFS_HANDLE_GET_DATA(handle, config,
 				struct fruit_config_data, return -1);
 
-	/* Try rw first so we can use the fd in ad_convert() */
-	mode = O_RDWR;
-
-	if (ad->ad_fsp && ad->ad_fsp->fh && (ad->ad_fsp->fh->fd != -1)) {
-		fd = ad->ad_fsp->fh->fd;
+	if (config->rsrc == FRUIT_RSRC_XATTR) {
+		fd = ad_open_rsrc_xattr(path, flags, mode);
 	} else {
-		if (config->rsrc == FRUIT_RSRC_XATTR) {
-			adpath = talloc_strdup(talloc_tos(), path);
-		} else {
-			rc = adouble_path(talloc_tos(), path, &adpath);
-			if (rc != 0) {
-				goto exit;
-			}
-		}
+		fd = ad_open_rsrc_adouble(path, flags, mode);
+	}
 
-	retry:
-		if (config->rsrc == FRUIT_RSRC_XATTR) {
-#ifndef HAVE_ATTROPEN
-			errno = ENOSYS;
-			rc = -1;
-			goto exit;
-#else
-			/* FIXME: direct Solaris xattr syscall */
-			fd = attropen(adpath, AFPRESOURCE_EA_NETATALK,
-				      mode, 0);
-#endif
-		} else {
-			/* FIXME: direct open(), don't have an fsp */
-			fd = open(adpath, mode);
-		}
+	return fd;
+}
 
-		if (fd == -1) {
-			switch (errno) {
-			case EROFS:
-			case EACCES:
-				if (mode == O_RDWR) {
-					mode = O_RDONLY;
-					goto retry;
-				}
-				/* fall through ... */
-			default:
-				DEBUG(2, ("open AppleDouble: %s, %s\n",
-					  adpath, strerror(errno)));
-				rc = -1;
-				goto exit;
-			}
-		}
-		opened = true;
+static int ad_open(vfs_handle_struct *handle,
+		   struct adouble *ad,
+		   const char *path,
+		   adouble_type_t t,
+		   int flags,
+		   mode_t mode)
+{
+	int fd;
+
+	DBG_DEBUG("Path [%s] type [%s]\n",
+		  path, t == ADOUBLE_META ? "meta" : "rsrc");
+
+	if (t == ADOUBLE_META) {
+		fd = ad_open_meta(path, flags, mode);
+	} else {
+		fd = ad_open_rsrc(handle, path, flags, mode);
 	}
 
-	if (config->rsrc == FRUIT_RSRC_XATTR) {
-		/* FIXME: direct sys_fstat(), don't have an fsp */
-		rc = sys_fstat(
-			fd, &sbuf,
+	if (fd != -1) {
+		ad->ad_opened = true;
+		ad->ad_fd = fd;
+	}
+
+	DBG_DEBUG("Path [%s] type [%s] fd [%d]\n",
+		  path, t == ADOUBLE_META ? "meta" : "rsrc", fd);
+
+	return fd;
+}
+
+static ssize_t ad_read_rsrc_xattr(struct adouble *ad,
+				  const char *path)
+{
+	int ret;
+	SMB_STRUCT_STAT st;
+
+	/* FIXME: direct sys_fstat(), don't have an fsp */
+	ret = sys_fstat(ad->ad_fd, &st,
 			lp_fake_directory_create_times(
 				SNUM(ad->ad_handle->conn)));
-		if (rc != 0) {
-			goto exit;
-		}
-		len = sbuf.st_ex_size;
-		ad_setentrylen(ad, ADEID_RFORK, len);
-	} else {
-		/* FIXME: direct sys_pread(), don't have an fsp */
-		len = sys_pread(fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
-		if (len != AD_DATASZ_DOT_UND) {
-			DEBUG(2, ("%s: bad size: %zd\n",
-				  strerror(errno), len));
-			rc = -1;
-			goto exit;
-		}
+	if (ret != 0) {
+		return -1;
+	}
 
-		/* FIXME: direct sys_fstat(), we don't have an fsp */
-		rc = sys_fstat(fd, &sbuf,
-			       lp_fake_directory_create_times(
-				       SNUM(ad->ad_handle->conn)));
-		if (rc != 0) {
-			goto exit;
-		}
+	ad_setentrylen(ad, ADEID_RFORK, st.st_ex_size);
+	return st.st_ex_size;
+}
 
-		/* Now parse entries */
-		ok = ad_unpack(ad, ADEID_NUM_DOT_UND, sbuf.st_ex_size);
-		if (!ok) {
-			DEBUG(1, ("invalid AppleDouble resource %s\n", path));
-			errno = EINVAL;
-			rc = -1;
-			goto exit;
-		}
+static ssize_t ad_read_rsrc_adouble(struct adouble *ad,
+				    const char *path)
+{
+	struct adouble *meta_ad = NULL;
+	SMB_STRUCT_STAT sbuf;
+	char *p_ad = NULL;
+	char *p_meta_ad = NULL;
+	ssize_t len;
+	int ret;
+	bool ok;
 
-		if ((ad_getentryoff(ad, ADEID_FINDERI)
-		     != ADEDOFF_FINDERI_DOT_UND)
-		    || (ad_getentrylen(ad, ADEID_FINDERI)
-			< ADEDLEN_FINDERI)
-		    || (ad_getentryoff(ad, ADEID_RFORK)
-			< ADEDOFF_RFORK_DOT_UND)) {
-			DEBUG(2, ("invalid AppleDouble resource %s\n", path));
-			errno = EINVAL;
-			rc = -1;
-			goto exit;
-		}
+	len = sys_pread(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
+	if (len != AD_DATASZ_DOT_UND) {
+		DBG_NOTICE("%s %s: bad size: %zd\n",
+			   path, strerror(errno), len);
+		return -1;
+	}
 
-		if ((mode == O_RDWR)
-		    && (ad_getentrylen(ad, ADEID_FINDERI) > ADEDLEN_FINDERI)) {
-			char *p_ad = NULL;
-			char *p_meta_ad = NULL;
+	ret = sys_fstat(ad->ad_fd, &sbuf, lp_fake_directory_create_times(
+				SNUM(ad->ad_handle->conn)));
+	if (ret != 0) {
+		return -1;
+	}
 
-			rc = ad_convert(ad, fd);
-			if (rc != 0) {
-				rc = -1;
-				goto exit;
-			}
-			/*
-			 * Can't use ad_write() because we might not have a fsp
-			 */
-			ok = ad_pack(ad);
-			if (!ok) {
-				rc = -1;
-				goto exit;
-			}
-			/* FIXME: direct sys_pwrite(), don't have an fsp */
-			len = sys_pwrite(fd, ad->ad_data,
-					 AD_DATASZ_DOT_UND, 0);
-			if (len != AD_DATASZ_DOT_UND) {
-				DEBUG(2, ("%s: bad size: %zd\n", adpath, len));
-				rc = -1;
-				goto exit;
-			}
+	/* Now parse entries */
+	ok = ad_unpack(ad, ADEID_NUM_DOT_UND, sbuf.st_ex_size);
+	if (!ok) {
+		DBG_ERR("invalid AppleDouble resource %s\n", path);
+		errno = EINVAL;
+		return -1;
+	}
 
-			meta_ad = ad_init(talloc_tos(), ad->ad_handle,
-					  ADOUBLE_META, NULL);
-			if (meta_ad == NULL) {
-				rc = -1;
-				goto exit;
-			}
+	if ((ad_getentryoff(ad, ADEID_FINDERI) != ADEDOFF_FINDERI_DOT_UND)
+	    || (ad_getentrylen(ad, ADEID_FINDERI) < ADEDLEN_FINDERI)
+	    || (ad_getentryoff(ad, ADEID_RFORK)	< ADEDOFF_RFORK_DOT_UND)) {
+		DBG_ERR("invalid AppleDouble resource %s\n", path);
+		errno = EINVAL;
+		return -1;
+	}
 
-			p_ad = ad_get_entry(ad, ADEID_FINDERI);
-			if (p_ad == NULL) {
-				rc = -1;
-				goto exit;
-			}
-			p_meta_ad = ad_get_entry(meta_ad, ADEID_FINDERI);
-			if (p_meta_ad == NULL) {
-				rc = -1;
-				goto exit;
-			}
+	if (ad_getentrylen(ad, ADEID_FINDERI) == ADEDLEN_FINDERI) {
+		return len;
+	}
 
-			memcpy(p_meta_ad, p_ad, ADEDLEN_FINDERI);
+	/*
+	 * 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.
+	 */
 
-			rc = ad_write(meta_ad, path);
-			if (rc != 0) {
-				rc = -1;
-				goto exit;
-			}
-		}
+	ret = ad_convert(ad, ad->ad_fd);
+	if (ret != 0) {
+		DBG_WARNING("Failed to convert [%s]\n", path);
+		return len;
 	}
 
-	DEBUG(10, ("opened AppleDouble: %s\n", path));
+	ok = ad_pack(ad);
+	if (!ok) {
+		DBG_WARNING("ad_pack [%s] failed\n", path);
+		return -1;
+	}
 
-exit:
-	if (rc != 0) {
-		saved_errno = errno;
-		len = -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", path, len);
+		return -1;
 	}
-	if (opened && fd != -1) {
-		close(fd);
+
+	meta_ad = ad_init(talloc_tos(), ad->ad_handle, ADOUBLE_META);
+	if (meta_ad == NULL) {
+		return -1;
 	}
-	TALLOC_FREE(adpath);
+
+	p_ad = ad_get_entry(ad, ADEID_FINDERI);
+	if (p_ad == NULL) {
+		TALLOC_FREE(meta_ad);
+		return -1;
+	}
+	p_meta_ad = ad_get_entry(meta_ad, ADEID_FINDERI);
+	if (p_meta_ad == NULL) {
+		TALLOC_FREE(meta_ad);
+		return -1;
+	}
+
+	memcpy(p_meta_ad, p_ad, ADEDLEN_FINDERI);
+
+	ret = ad_set(meta_ad, path);
 	TALLOC_FREE(meta_ad);
-	if (rc != 0) {
-		errno = saved_errno;
+	if (ret != 0) {
+		return -1;
 	}
+
+	return len;
+}
+
+/**
+ * Read and parse resource fork, either ._ AppleDouble file or xattr
+ **/
+static ssize_t ad_read_rsrc(struct adouble *ad,
+			    const char *path)
+{
+	struct fruit_config_data *config = NULL;
+	ssize_t len;
+
+	SMB_VFS_HANDLE_GET_DATA(ad->ad_handle, config,
+				struct fruit_config_data, return -1);
+
+	if (config->rsrc == FRUIT_RSRC_XATTR) {
+		len = ad_read_rsrc_xattr(ad, path);
+	} else {
+		len = ad_read_rsrc_adouble(ad, path);
+	}
+
 	return len;
 }
 
@@ -1043,14 +1077,23 @@ static ssize_t ad_read(struct adouble *ad, const char *path)
 {
 	switch (ad->ad_type) {
 	case ADOUBLE_META:
-		return ad_header_read_meta(ad, path);
+		return ad_read_meta(ad, path);
 	case ADOUBLE_RSRC:
-		return ad_header_read_rsrc(ad, path);
+		return ad_read_rsrc(ad, path);
 	default:
 		return -1;
 	}
 }
 
+static int adouble_destructor(struct adouble *ad)
+{
+	if ((ad->ad_fd != -1) && ad->ad_opened) {
+		close(ad->ad_fd);
+		ad->ad_fd = -1;
+	}
+	return 0;
+}
+
 /**
  * Allocate a struct adouble without initialiing it
  *
@@ -1060,14 +1103,11 @@ static ssize_t ad_read(struct adouble *ad, const char *path)
  * @param[in] ctx        talloc context
  * @param[in] handle     vfs handle
  * @param[in] type       type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
-
- * @param[in] fsp        if not NULL (for stream IO), the adouble handle is
- *                       added as an fsp extension
  *
  * @return               adouble handle
  **/
 static struct adouble *ad_alloc(TALLOC_CTX *ctx, vfs_handle_struct *handle,
-				adouble_type_t type, files_struct *fsp)
+				adouble_type_t type)
 {
 	int rc = 0;
 	size_t adsize = 0;
@@ -1090,39 +1130,27 @@ static struct adouble *ad_alloc(TALLOC_CTX *ctx, vfs_handle_struct *handle,
 		return NULL;
 	}
 
-	if (!fsp) {
-		ad = talloc_zero(ctx, struct adouble);
-		if (ad == NULL) {
-			rc = -1;
-			goto exit;
-		}
-		if (adsize) {
-			ad->ad_data = talloc_zero_array(ad, char, adsize);
-		}
-	} else {
-		ad = (struct adouble *)VFS_ADD_FSP_EXTENSION(handle, fsp,
-							     struct adouble,
-							     NULL);
-		if (ad == NULL) {
+	ad = talloc_zero(ctx, struct adouble);
+	if (ad == NULL) {
+		rc = -1;
+		goto exit;
+	}
+
+	if (adsize) {
+		ad->ad_data = talloc_zero_array(ad, char, adsize);
+		if (ad->ad_data == NULL) {
 			rc = -1;
 			goto exit;
 		}
-		if (adsize) {
-			ad->ad_data = talloc_zero_array(
-				VFS_MEMCTX_FSP_EXTENSION(handle, fsp),
-				char, adsize);
-		}
-		ad->ad_fsp = fsp;
 	}
 
-	if (adsize && ad->ad_data == NULL) {
-		rc = -1;
-		goto exit;
-	}
 	ad->ad_handle = handle;
 	ad->ad_type = type;
 	ad->ad_magic = AD_MAGIC;
 	ad->ad_version = AD_VERSION;
+	ad->ad_fd = -1;
+
+	talloc_set_destructor(ad, adouble_destructor);
 
 exit:
 	if (rc != 0) {
@@ -1137,12 +1165,11 @@ exit:
  * @param[in] ctx        talloc context
  * @param[in] handle     vfs handle
  * @param[in] type       type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
- * @param[in] fsp        file handle, may be NULL for a type of e_ad_meta
  *
  * @return               adouble handle, initialized
  **/
 static struct adouble *ad_init(TALLOC_CTX *ctx, vfs_handle_struct *handle,
-			       adouble_type_t type, files_struct *fsp)
+			       adouble_type_t type)
 {
 	int rc = 0;
 	const struct ad_entry_order  *eid;
@@ -1168,7 +1195,7 @@ static struct adouble *ad_init(TALLOC_CTX *ctx, vfs_handle_struct *handle,
 		return NULL;
 	}
 
-	ad = ad_alloc(ctx, handle, type, fsp);
+	ad = ad_alloc(ctx, handle, type);
 	if (ad == NULL) {
 		return NULL;
 	}
@@ -1207,16 +1234,42 @@ static struct adouble *ad_get(TALLOC_CTX *ctx, vfs_handle_struct *handle,
 	int rc = 0;
 	ssize_t len;
 	struct adouble *ad = NULL;
+	int fd;
+	int mode;
 
 	DEBUG(10, ("ad_get(%s) called for %s\n",
 		   type == ADOUBLE_META ? "meta" : "rsrc", path));
 
-	ad = ad_alloc(ctx, handle, type, NULL);
+	ad = ad_alloc(ctx, handle, type);
 	if (ad == NULL) {
 		rc = -1;
 		goto exit;
 	}
 
+	/*
+	 * Here's the deal: for ADOUBLE_META we can do without an fd
+	 * as we can issue path based xattr calls. For ADOUBLE_RSRC
+	 * however we need a full-fledged fd for file IO on the ._
+	 * file.
+	 */
+	if (type == ADOUBLE_RSRC) {
+		/* Try rw first so we can use the fd in ad_convert() */
+		mode = O_RDWR;
+
+		fd = ad_open(handle, ad, path, ADOUBLE_RSRC, mode, 0);
+		if (fd == -1 && ((errno == EROFS) || (errno == EACCES))) {
+			mode = O_RDONLY;
+			fd = ad_open(handle, ad, path, ADOUBLE_RSRC, mode, 0);
+		}
+
+		if (fd == -1) {
+			DBG_DEBUG("ad_open [%s] error [%s]\n",
+				  path, strerror(errno));
+			rc = -1;
+			goto exit;
+		}
+	}
+
 	len = ad_read(ad, path);
 	if (len == -1) {
 		DEBUG(10, ("error reading AppleDouble for %s\n", path));
@@ -1235,21 +1288,144 @@ exit:
 }
 
 /**
+ * Return AppleDouble data for a file
+ *
+ * @param[in] ctx      talloc context
+ * @param[in] handle   vfs handle
+ * @param[in] fsp      fsp to use for IO
+ * @param[in] type     type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
+ *
+ * @return             talloced struct adouble or NULL on error
+ **/
+static struct adouble *ad_fget(TALLOC_CTX *ctx, vfs_handle_struct *handle,
+			       files_struct *fsp, adouble_type_t type)
+{
+	int rc = 0;
+	ssize_t len;
+	struct adouble *ad = NULL;
+	char *path = fsp->base_fsp->fsp_name->base_name;
+
+	DBG_DEBUG("ad_get(%s) path [%s]\n",
+		  type == ADOUBLE_META ? "meta" : "rsrc",
+		  fsp_str_dbg(fsp));
+
+	ad = ad_alloc(ctx, handle, type);
+	if (ad == NULL) {
+		rc = -1;
+		goto exit;
+	}
+
+	if ((fsp->fh != NULL) && (fsp->fh->fd != -1)) {
+		ad->ad_fd = fsp->fh->fd;
+	} else {
+		/*
+		 * Here's the deal: for ADOUBLE_META we can do without an fd
+		 * as we can issue path based xattr calls. For ADOUBLE_RSRC
+		 * however we need a full-fledged fd for file IO on the ._
+		 * file.
+		 */
+		int fd;
+		int mode;
+
+		if (type == ADOUBLE_RSRC) {
+			/* Try rw first so we can use the fd in ad_convert() */
+			mode = O_RDWR;
+
+			fd = ad_open(handle, ad, path, ADOUBLE_RSRC, mode, 0);
+			if (fd == -1 &&
+			    ((errno == EROFS) || (errno == EACCES)))
+			{
+				mode = O_RDONLY;
+				fd = ad_open(handle, ad, path, ADOUBLE_RSRC,
+					     mode, 0);
+			}
+
+			if (fd == -1) {
+				DBG_DEBUG("error opening AppleDouble for %s\n", path);
+				rc = -1;
+				goto exit;
+			}
+		}
+	}
+
+	len = ad_read(ad, path);
+	if (len == -1) {
+		DBG_DEBUG("error reading AppleDouble for %s\n", path);
+		rc = -1;
+		goto exit;
+	}
+
+exit:
+	DBG_DEBUG("ad_get(%s) path [%s] rc [%d]\n",
+		  type == ADOUBLE_META ? "meta" : "rsrc",
+		  fsp_str_dbg(fsp), rc);
+
+	if (rc != 0) {
+		TALLOC_FREE(ad);
+	}
+	return ad;
+}
+
+/**
  * Set AppleDouble metadata on a file or directory
  *
  * @param[in] ad      adouble handle
+ *
+ * @param[in] path    pathname to file or directory
+ *
+ * @return            status code, 0 means success
+ **/
+static int ad_set(struct adouble *ad, const char *path)
+{
+	bool ok;
+	int ret;
+
+	DBG_DEBUG("Path [%s]\n", path);
+
+	if (ad->ad_type != ADOUBLE_META) {
+		DBG_ERR("ad_set on [%s] used with ADOUBLE_RSRC\n", path);
+		return -1;
+	}
+
+	ok = ad_pack(ad);
+	if (!ok) {
+		return -1;
+	}
 
- * @param[in] path    pathname to file or directory, may be NULL for a
- *                    resource fork
+	ret = SMB_VFS_SETXATTR(ad->ad_handle->conn,
+			       path,
+			       AFPINFO_EA_NETATALK,
+			       ad->ad_data,
+			       AD_DATASZ_XATTR, 0);
+
+	DBG_DEBUG("Path [%s] ret [%d]\n", path, ret);
+
+	return ret;
+}
+
+/**
+ * Set AppleDouble metadata on a file or directory
+ *
+ * @param[in] ad      adouble handle
+ * @param[in] fsp     file handle
  *
  * @return            status code, 0 means success
  **/
-static int ad_write(struct adouble *ad, const char *path)
+static int ad_fset(struct adouble *ad, files_struct *fsp)
 {
-	int rc = 0;
+	int rc = -1;
 	ssize_t len;
 	bool ok;
 
+	DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
+
+	if ((fsp == NULL)
+	    || (fsp->fh == NULL)
+	    || (fsp->fh->fd == -1))
+	{
+		smb_panic("bad fsp");
+	}
+
 	ok = ad_pack(ad);
 	if (!ok) {
 		return -1;
@@ -1257,31 +1433,30 @@ static int ad_write(struct adouble *ad, const char *path)
 
 	switch (ad->ad_type) {
 	case ADOUBLE_META:
-		rc = SMB_VFS_SETXATTR(ad->ad_handle->conn, path,
-				      AFPINFO_EA_NETATALK, ad->ad_data,
-				      AD_DATASZ_XATTR, 0);
+		rc = SMB_VFS_FSETXATTR(fsp, AFPINFO_EA_NETATALK,
+				       ad->ad_data,
+				       AD_DATASZ_XATTR, 0);
 		break;
+
 	case ADOUBLE_RSRC:
-		if ((ad->ad_fsp == NULL)
-		    || (ad->ad_fsp->fh == NULL)
-		    || (ad->ad_fsp->fh->fd == -1)) {
-			rc = -1;
-			goto exit;
-		}
-		/* FIXME: direct sys_pwrite(), don't have an fsp */
-		len = sys_pwrite(ad->ad_fsp->fh->fd, ad->ad_data,
-				 talloc_get_size(ad->ad_data), 0);
-		if (len != talloc_get_size(ad->ad_data)) {
-			DEBUG(1, ("short write on %s: %zd",
-				  fsp_str_dbg(ad->ad_fsp), len));
-			rc = -1;
-			goto exit;
+		len = SMB_VFS_NEXT_PWRITE(ad->ad_handle,
+					  fsp,
+					  ad->ad_data,
+					  talloc_get_size(ad->ad_data),
+					  0);
+		if (len != (ssize_t)talloc_get_size(ad->ad_data)) {
+			DBG_ERR("short write on %s: %zd", fsp_str_dbg(fsp), len);
+			return -1;
 		}
+		rc = 0;
 		break;
+
 	default:
 		return -1;
 	}
-exit:
+
+	DBG_DEBUG("Path [%s] rc [%d]\n", fsp_str_dbg(fsp), rc);
+
 	return rc;
 }
 
@@ -1539,19 +1714,6 @@ static SMB_INO_T fruit_inode(const SMB_STRUCT_STAT *sbuf, const char *sname)
 	return result;
 }
 
-/**
- * Ensure ad_fsp is still valid
- **/
-static bool fruit_fsp_recheck(struct adouble *ad, files_struct *fsp)
-{
-	if (ad->ad_fsp == fsp) {
-		return true;
-	}
-	ad->ad_fsp = fsp;
-
-	return true;
-}
-
 static bool add_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
 			     struct stream_struct **streams,
 			     const char *name, off_t size,
@@ -2580,29 +2742,22 @@ static int fruit_open_meta_netatalk(vfs_handle_struct *handle,
 		 * The attribute does not exist or needs to be truncated,
 		 * create an AppleDouble EA
 		 */
-		ad = ad_init(VFS_MEMCTX_FSP_EXTENSION(handle, fsp),
-			     handle, ADOUBLE_META, fsp);
+		ad = ad_init(fsp, handle, ADOUBLE_META);
 		if (ad == NULL) {
 			rc = -1;
 			goto exit;
 		}
 
-		rc = ad_write(ad, smb_fname->base_name);
+		fsp->fh->fd = hostfd;
+
+		rc = ad_fset(ad, fsp);
+		fsp->fh->fd = -1;
 		if (rc != 0) {
 			rc = -1;
 			goto exit;
 		}
-	} else {
-		ad = ad_alloc(VFS_MEMCTX_FSP_EXTENSION(handle, fsp),
-			      handle, ADOUBLE_META, fsp);
-		if (ad == NULL) {
-			rc = -1;
-			goto exit;
-		}
-		if (ad_read(ad, smb_fname->base_name) == -1) {
-			rc = -1;
-			goto exit;
-		}
+
+		TALLOC_FREE(ad);
 	}
 
 exit:
@@ -2717,28 +2872,21 @@ static int fruit_open_rsrc_adouble(vfs_handle_struct *handle,
 	fsp->fh->fd = hostfd;
 
 	if (flags & (O_CREAT | O_TRUNC)) {
-		ad = ad_init(VFS_MEMCTX_FSP_EXTENSION(handle, fsp),
-			     handle, ADOUBLE_RSRC, fsp);
+		ad = ad_init(fsp, handle, ADOUBLE_RSRC);
 		if (ad == NULL) {
 			rc = -1;
 			goto exit;
 		}
-		rc = ad_write(ad, smb_fname->base_name);
+
+		fsp->fh->fd = hostfd;
+
+		rc = ad_fset(ad, fsp);
+		fsp->fh->fd = -1;
 		if (rc != 0) {
 			rc = -1;
 			goto exit;
 		}
-	} else {
-		ad = ad_alloc(VFS_MEMCTX_FSP_EXTENSION(handle, fsp),
-			      handle, ADOUBLE_RSRC, fsp);
-		if (ad == NULL) {
-			rc = -1;
-			goto exit;
-		}
-		if (ad_read(ad, smb_fname->base_name) == -1) {
-			rc = -1;
-			goto exit;
-		}
+		TALLOC_FREE(ad);
 	}
 
 exit:
@@ -3397,11 +3545,6 @@ static ssize_t fruit_pread(vfs_handle_struct *handle,
 		goto exit;
 	}
 
-	if (!fruit_fsp_recheck(ad, fsp)) {
-		rc = -1;
-		goto exit;
-	}
-
 	if (ad->ad_type == ADOUBLE_META) {
 		char afpinfo_buf[AFP_INFO_SIZE];
 		char *p = NULL;
@@ -3537,11 +3680,6 @@ static ssize_t fruit_pwrite(vfs_handle_struct *handle,
 		goto exit;
 	}
 
-	if (!fruit_fsp_recheck(ad, fsp)) {
-		rc = -1;
-		goto exit;
-	}
-
 	if (ad->ad_type == ADOUBLE_META) {
 		char *p = NULL;
 
@@ -3554,7 +3692,7 @@ static ssize_t fruit_pwrite(vfs_handle_struct *handle,
 		}
 
 		memcpy(p, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
-		rc = ad_write(ad, fsp->base_fsp->fsp_name->base_name);
+		rc = ad_fset(ad, fsp);
 	} else {
 		len = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n,
                                    offset + ad_getentryoff(ad, ADEID_RFORK));
@@ -3572,7 +3710,7 @@ static ssize_t fruit_pwrite(vfs_handle_struct *handle,
 
 			if ((len + offset) > ad_getentrylen(ad, ADEID_RFORK)) {
 				ad_setentrylen(ad, ADEID_RFORK, len + offset);
-				rc = ad_write(ad, fsp->base_fsp->fsp_name->base_name);
+				rc = ad_fset(ad, fsp);
 			}
 		}
 	}
@@ -3932,11 +4070,6 @@ static int fruit_fstat(vfs_handle_struct *handle, files_struct *fsp,
 		goto exit;
 	}
 
-	if (!fruit_fsp_recheck(ad, fsp)) {
-		rc = -1;
-		goto exit;
-	}
-
 	switch (ad->ad_type) {
 	case ADOUBLE_META:
 		rc = fruit_fstat_meta(handle, fsp, sbuf);
@@ -4224,7 +4357,7 @@ static int fruit_ntimes(vfs_handle_struct *handle,
 	ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX,
 		   convert_time_t_to_uint32_t(ft->create_time.tv_sec));
 
-	rc = ad_write(ad, smb_fname->base_name);
+	rc = ad_set(ad, smb_fname->base_name);
 
 exit:
 
@@ -4249,10 +4382,6 @@ static int fruit_fallocate(struct vfs_handle_struct *handle,
 		return SMB_VFS_NEXT_FALLOCATE(handle, fsp, mode, offset, len);
 	}
 
-	if (!fruit_fsp_recheck(ad, fsp)) {
-		return -1;
-	}
-
 	/* Let the pwrite code path handle it. */
 	errno = ENOSYS;
 	return -1;
@@ -4277,14 +4406,16 @@ static int fruit_ftruncate_rsrc_adouble(struct vfs_handle_struct *handle,
 					off_t offset)
 {
 	int rc;
-        struct adouble *ad =
-		(struct adouble *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
-	off_t ad_off = ad_getentryoff(ad, ADEID_RFORK);
+	struct adouble *ad = NULL;
+	off_t ad_off;
 
-	if (!fruit_fsp_recheck(ad, fsp)) {
+	ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
+	if (ad == NULL) {
 		return -1;
 	}
 
+	ad_off = ad_getentryoff(ad, ADEID_RFORK);
+
 	rc = SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset + ad_off);
 	if (rc != 0) {
 		return -1;
@@ -4292,7 +4423,7 @@ static int fruit_ftruncate_rsrc_adouble(struct vfs_handle_struct *handle,
 
 	ad_setentrylen(ad, ADEID_RFORK, offset);
 
-	rc = ad_write(ad, NULL);
+	rc = ad_fset(ad, fsp);
 	if (rc != 0) {
 		DBG_ERR("ad_write [%s] failed [%s]\n",
 			fsp_str_dbg(fsp), strerror(errno));
-- 
2.9.3


From 36d00ce30fbf389b7bfdb12727294ac490a2fae4 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Thu, 8 Dec 2016 20:34:55 +0100
Subject: [PATCH 37/56] vfs_fruit: refactor fruit_open and use new adouble API

Use struct fio to denote a fsp handle is for a stream we care about.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12427

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 source3/modules/vfs_fruit.c | 65 ++++++++++++++++++++++++++++++---------------
 1 file changed, 44 insertions(+), 21 deletions(-)

diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c
index 74428d9..94dcc1b 100644
--- a/source3/modules/vfs_fruit.c
+++ b/source3/modules/vfs_fruit.c
@@ -2694,6 +2694,8 @@ static int fruit_open_meta_netatalk(vfs_handle_struct *handle,
 	int hostfd = -1;
 	struct adouble *ad = NULL;
 
+	DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
+
 	/* Create an smb_filename with stream_name == NULL. */
 	smb_fname_base = synthetic_smb_fname(talloc_tos(),
 					smb_fname->base_name,
@@ -2783,8 +2785,9 @@ static int fruit_open_meta(vfs_handle_struct *handle,
 			   struct smb_filename *smb_fname,
 			   files_struct *fsp, int flags, mode_t mode)
 {
-	int rc;
+	int fd;
 	struct fruit_config_data *config = NULL;
+	struct fio *fio = NULL;
 
 	DBG_DEBUG("path [%s]\n", smb_fname_str_dbg(smb_fname));
 
@@ -2793,12 +2796,12 @@ static int fruit_open_meta(vfs_handle_struct *handle,
 
 	switch (config->meta) {
 	case FRUIT_META_STREAM:
-		rc = fruit_open_meta_stream(handle, smb_fname,
+		fd = fruit_open_meta_stream(handle, smb_fname,
 					    fsp, flags, mode);
 		break;
 
 	case FRUIT_META_NETATALK:
-		rc = fruit_open_meta_netatalk(handle, smb_fname,
+		fd = fruit_open_meta_netatalk(handle, smb_fname,
 					      fsp, flags, mode);
 		break;
 
@@ -2807,8 +2810,17 @@ static int fruit_open_meta(vfs_handle_struct *handle,
 		return -1;
 	}
 
-	DBG_DEBUG("path [%s] rc [%d]\n", smb_fname_str_dbg(smb_fname), rc);
-	return rc;
+	DBG_DEBUG("path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
+
+	if (fd == -1) {
+		return -1;
+	}
+
+	fio = (struct fio *)VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
+	fio->type = ADOUBLE_META;
+	fio->config = config;
+
+	return fd;
 }
 
 static int fruit_open_rsrc_adouble(vfs_handle_struct *handle,
@@ -2820,9 +2832,13 @@ static int fruit_open_rsrc_adouble(vfs_handle_struct *handle,
 	int rc = 0;
 	struct adouble *ad = NULL;
 	struct smb_filename *smb_fname_base = NULL;
+	struct fruit_config_data *config = NULL;
 	char *adpath = NULL;
 	int hostfd = -1;
 
+	SMB_VFS_HANDLE_GET_DATA(handle, config,
+				struct fruit_config_data, return -1);
+
 	if (!(flags & O_CREAT) && !VALID_STAT(smb_fname->st)) {
 		rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
 		if (rc != 0) {
@@ -2868,9 +2884,6 @@ static int fruit_open_rsrc_adouble(vfs_handle_struct *handle,
 		goto exit;
 	}
 
-	/* REVIEW: we need this in ad_write() */
-	fsp->fh->fd = hostfd;
-
 	if (flags & (O_CREAT | O_TRUNC)) {
 		ad = ad_init(fsp, handle, ADOUBLE_RSRC);
 		if (ad == NULL) {
@@ -2920,16 +2933,11 @@ static int fruit_open_rsrc_xattr(vfs_handle_struct *handle,
 {
 #ifdef HAVE_ATTROPEN
 	int fd = -1;
-	struct adouble *ad = NULL;
-
-	ad = ad_init(VFS_MEMCTX_FSP_EXTENSION(handle, fsp),
-		     handle, ADOUBLE_RSRC, fsp);
-	if (ad == NULL) {
-		return -1;
-	}
 
 	fd = attropen(smb_fname->base_name,
-			  AFPRESOURCE_EA_NETATALK, flags, mode);
+		      AFPRESOURCE_EA_NETATALK,
+		      flags,
+		      mode);
 	if (fd == -1) {
 		return -1;
 	}
@@ -2948,6 +2956,7 @@ static int fruit_open_rsrc(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));
 
@@ -2975,6 +2984,15 @@ static int fruit_open_rsrc(vfs_handle_struct *handle,
 	}
 
 	DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
+
+	if (fd == -1) {
+		return -1;
+	}
+
+	fio = (struct fio *)VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
+	fio->type = ADOUBLE_RSRC;
+	fio->config = config;
+
 	return fd;
 }
 
@@ -2982,20 +3000,25 @@ static int fruit_open(vfs_handle_struct *handle,
                       struct smb_filename *smb_fname,
                       files_struct *fsp, int flags, mode_t mode)
 {
-	DEBUG(10, ("fruit_open called for %s\n",
-		   smb_fname_str_dbg(smb_fname)));
+	int fd;
+
+	DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
 
 	if (!is_ntfs_stream_smb_fname(smb_fname)) {
 		return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
 	}
 
 	if (is_afpinfo_stream(smb_fname)) {
-		return fruit_open_meta(handle, smb_fname, fsp, flags, mode);
+		fd = fruit_open_meta(handle, smb_fname, fsp, flags, mode);
 	} else if (is_afpresource_stream(smb_fname)) {
-		return fruit_open_rsrc(handle, smb_fname, fsp, flags, mode);
+		fd = fruit_open_rsrc(handle, smb_fname, fsp, flags, mode);
+	} else {
+		fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
 	}
 
-	return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
+	DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
+
+	return fd;
 }
 
 static int fruit_rename(struct vfs_handle_struct *handle,
-- 
2.9.3


From 58c1166765e36efe8f030ba8fc897e2d332a4705 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Thu, 8 Dec 2016 20:38:17 +0100
Subject: [PATCH 38/56] vfs_fruit: refactor fruit_pread and fruit_pwrite and
 use new adouble API

Use struct fio to denote a fsp handle is for a stream we care about.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12427

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 source3/modules/vfs_fruit.c | 524 +++++++++++++++++++++++++++++---------------
 1 file changed, 349 insertions(+), 175 deletions(-)

diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c
index 94dcc1b..230e807 100644
--- a/source3/modules/vfs_fruit.c
+++ b/source3/modules/vfs_fruit.c
@@ -3519,233 +3519,407 @@ exit_rmdir:
 	return SMB_VFS_NEXT_RMDIR(handle, smb_fname);
 }
 
-static ssize_t fruit_pread(vfs_handle_struct *handle,
-			   files_struct *fsp, void *data,
-			   size_t n, off_t offset)
+static ssize_t fruit_pread_meta_stream(vfs_handle_struct *handle,
+				       files_struct *fsp, void *data,
+				       size_t n, off_t offset)
+{
+	return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
+}
+
+static ssize_t fruit_pread_meta_adouble(vfs_handle_struct *handle,
+					files_struct *fsp, void *data,
+					size_t n, off_t offset)
 {
-	int rc = 0;
-        struct adouble *ad = (struct adouble *)VFS_FETCH_FSP_EXTENSION(
-		handle, fsp);
-	struct fruit_config_data *config = NULL;
 	AfpInfo *ai = NULL;
-	ssize_t len = -1;
-	size_t to_return = n;
+	struct adouble *ad = NULL;
+	char afpinfo_buf[AFP_INFO_SIZE];
+	char *p = NULL;
+	ssize_t nread;
 
-	DEBUG(10, ("fruit_pread: offset=%d, size=%d\n", (int)offset, (int)n));
+	ai = afpinfo_new(talloc_tos());
+	if (ai == NULL) {
+		return -1;
+	}
 
-	if (!fsp->base_fsp) {
-		return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
+	ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
+	if (ad == NULL) {
+		nread = -1;
+		goto fail;
 	}
 
-	SMB_VFS_HANDLE_GET_DATA(handle, config,
-				struct fruit_config_data, return -1);
+	p = ad_get_entry(ad, ADEID_FINDERI);
+	if (p == NULL) {
+		DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
+		nread = -1;
+		goto fail;
+	}
 
-	if (is_afpinfo_stream(fsp->fsp_name)) {
-		/*
-		 * OS X has a off-by-1 error in the offset calculation, so we're
-		 * bug compatible here. It won't hurt, as any relevant real
-		 * world read requests from the AFP_AfpInfo stream will be
-		 * offset=0 n=60. offset is ignored anyway, see below.
-		 */
-		if ((offset < 0) || (offset >= AFP_INFO_SIZE + 1)) {
-			len = 0;
-			rc = 0;
-			goto exit;
-		}
+	memcpy(&ai->afpi_FinderInfo[0], p, ADEDLEN_FINDERI);
+
+	nread = afpinfo_pack(ai, afpinfo_buf);
+	if (nread != AFP_INFO_SIZE) {
+		nread = -1;
+		goto fail;
+	}
 
-		to_return = MIN(n, AFP_INFO_SIZE);
+	memcpy(data, afpinfo_buf, n);
+	nread = n;
 
-		/* Yes, macOS always reads from offset 0 */
-		offset = 0;
+fail:
+	TALLOC_FREE(ai);
+	return nread;
+}
+
+static ssize_t fruit_pread_meta(vfs_handle_struct *handle,
+				files_struct *fsp, void *data,
+				size_t n, off_t offset)
+{
+	struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
+	ssize_t nread;
+	ssize_t to_return;
+
+	/*
+	 * OS X has a off-by-1 error in the offset calculation, so we're
+	 * bug compatible here. It won't hurt, as any relevant real
+	 * world read requests from the AFP_AfpInfo stream will be
+	 * offset=0 n=60. offset is ignored anyway, see below.
+	 */
+	if ((offset < 0) || (offset >= AFP_INFO_SIZE + 1)) {
+		return 0;
+	}
+
+	/* Yes, macOS always reads from offset 0 */
+	offset = 0;
+	to_return = MIN(n, AFP_INFO_SIZE);
+
+	switch (fio->config->meta) {
+	case FRUIT_META_STREAM:
+		nread = fruit_pread_meta_stream(handle, fsp, data,
+						to_return, offset);
+		break;
+
+	case FRUIT_META_NETATALK:
+		nread = fruit_pread_meta_adouble(handle, fsp, data,
+						 to_return, offset);
+		break;
+
+	default:
+		DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
+		return -1;
 	}
 
+	return nread;
+}
+
+static ssize_t fruit_pread_rsrc_stream(vfs_handle_struct *handle,
+				       files_struct *fsp, void *data,
+				       size_t n, off_t offset)
+{
+	return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
+}
+
+static ssize_t fruit_pread_rsrc_xattr(vfs_handle_struct *handle,
+				      files_struct *fsp, void *data,
+				      size_t n, off_t offset)
+{
+	return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
+}
+
+static ssize_t fruit_pread_rsrc_adouble(vfs_handle_struct *handle,
+					files_struct *fsp, void *data,
+					size_t n, off_t offset)
+{
+	struct adouble *ad = NULL;
+	ssize_t nread;
+
+	ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
 	if (ad == NULL) {
-		len = SMB_VFS_NEXT_PREAD(handle, fsp, data, to_return, offset);
-		if (len == -1) {
-			rc = -1;
-			goto exit;
-		}
-		goto exit;
+		return -1;
 	}
 
-	if (ad->ad_type == ADOUBLE_META) {
-		char afpinfo_buf[AFP_INFO_SIZE];
-		char *p = NULL;
+	nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n,
+				   offset + ad_getentryoff(ad, ADEID_RFORK));
 
-		ai = afpinfo_new(talloc_tos());
-		if (ai == NULL) {
-			rc = -1;
-			goto exit;
-		}
+	TALLOC_FREE(ad);
+	return nread;
+}
 
-		len = ad_read(ad, fsp->base_fsp->fsp_name->base_name);
-		if (len == -1) {
-			rc = -1;
-			goto exit;
-		}
+static ssize_t fruit_pread_rsrc(vfs_handle_struct *handle,
+				files_struct *fsp, void *data,
+				size_t n, off_t offset)
+{
+	struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
+	ssize_t nread;
 
-		p = ad_get_entry(ad, ADEID_FINDERI);
-		if (p == NULL) {
-			DBG_ERR("No ADEID_FINDERI for [%s]\n",
-				fsp->fsp_name->base_name);
-			rc = -1;
-			goto exit;
-		}
+	switch (fio->config->rsrc) {
+	case FRUIT_RSRC_STREAM:
+		nread = fruit_pread_rsrc_stream(handle, fsp, data, n, offset);
+		break;
 
-		memcpy(&ai->afpi_FinderInfo[0], p, ADEDLEN_FINDERI);
+	case FRUIT_RSRC_ADFILE:
+		nread = fruit_pread_rsrc_adouble(handle, fsp, data, n, offset);
+		break;
 
-		len = afpinfo_pack(ai, afpinfo_buf);
-		if (len != AFP_INFO_SIZE) {
-			rc = -1;
-			goto exit;
-		}
+	case FRUIT_RSRC_XATTR:
+		nread = fruit_pread_rsrc_xattr(handle, fsp, data, n, offset);
+		break;
 
-		/*
-		 * OS X ignores offset when reading from AFP_AfpInfo stream!
-		 */
-		memcpy(data, afpinfo_buf, to_return);
-		len = to_return;
-	} else {
-		len = SMB_VFS_NEXT_PREAD(
-			handle, fsp, data, n,
-			offset + ad_getentryoff(ad, ADEID_RFORK));
-		if (len == -1) {
-			rc = -1;
-			goto exit;
-		}
+	default:
+		DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
+		return -1;
 	}
-exit:
-	TALLOC_FREE(ai);
-	if (rc != 0) {
-		len = -1;
+
+	return nread;
+}
+
+static ssize_t fruit_pread(vfs_handle_struct *handle,
+			   files_struct *fsp, void *data,
+			   size_t n, off_t offset)
+{
+	struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
+	ssize_t nread;
+
+	DBG_DEBUG("Path [%s] offset=%zd, size=%zd\n",
+		  fsp_str_dbg(fsp), offset, n);
+
+	if (fio == NULL) {
+		return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
 	}
-	DEBUG(10, ("fruit_pread: rc=%d, len=%zd\n", rc, len));
-	return len;
+
+	if (fio->type == ADOUBLE_META) {
+		nread = fruit_pread_meta(handle, fsp, data, n, offset);
+	} else {
+		nread = fruit_pread_rsrc(handle, fsp, data, n, offset);
+	}
+
+	DBG_DEBUG("Path [%s] nread [%zd]\n", fsp_str_dbg(fsp), nread);
+	return nread;
 }
 
-static ssize_t fruit_pwrite(vfs_handle_struct *handle,
-			    files_struct *fsp, const void *data,
-			    size_t n, off_t offset)
+static ssize_t fruit_pwrite_meta_stream(vfs_handle_struct *handle,
+					files_struct *fsp, const void *data,
+					size_t n, off_t offset)
 {
-	int rc = 0;
-	struct adouble *ad = (struct adouble *)VFS_FETCH_FSP_EXTENSION(
-		handle, fsp);
-	struct fruit_config_data *config = NULL;
 	AfpInfo *ai = NULL;
-	ssize_t len;
+	int ret;
 
-	DEBUG(10, ("fruit_pwrite: offset=%d, size=%d\n", (int)offset, (int)n));
+	ai = afpinfo_unpack(talloc_tos(), data);
+	if (ai == NULL) {
+		return -1;
+	}
 
-	if (!fsp->base_fsp) {
-		return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
+	if (ai_empty_finderinfo(ai)) {
+		ret = SMB_VFS_NEXT_UNLINK(handle, fsp->fsp_name);
+		if (ret != 0 && errno != ENOENT && errno != ENOATTR) {
+			DBG_ERR("Can't delete metadata for %s: %s\n",
+				fsp_str_dbg(fsp), strerror(errno));
+			TALLOC_FREE(ai);
+			return -1;
+		}
+
+		return n;
 	}
 
-	SMB_VFS_HANDLE_GET_DATA(handle, config,
-				struct fruit_config_data, return -1);
+	return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
+}
 
-	if (is_afpinfo_stream(fsp->fsp_name)) {
-		/*
-		 * Writing an all 0 blob to the metadata stream
-		 * results in the stream being removed on a macOS
-		 * server. This ensures we behave the same and it
-		 * verified by the "delete AFP_AfpInfo by writing all
-		 * 0" test.
-		 */
-		if (n != AFP_INFO_SIZE || offset != 0) {
-			DEBUG(1, ("unexpected offset=%jd or size=%jd\n",
-				  (intmax_t)offset, (intmax_t)n));
-			rc = -1;
-			goto exit;
-		}
-		ai = afpinfo_unpack(talloc_tos(), data);
-		if (ai == NULL) {
-			rc = -1;
-			goto exit;
-		}
+static ssize_t fruit_pwrite_meta_netatalk(vfs_handle_struct *handle,
+					  files_struct *fsp, const void *data,
+					  size_t n, off_t offset)
+{
+	struct adouble *ad = NULL;
+	AfpInfo *ai = NULL;
+	char *p = NULL;
+	int ret;
 
-		if (ai_empty_finderinfo(ai)) {
-			switch (config->meta) {
-			case FRUIT_META_STREAM:
-				rc = SMB_VFS_UNLINK(handle->conn, fsp->fsp_name);
-				break;
-
-			case FRUIT_META_NETATALK:
-				rc = SMB_VFS_REMOVEXATTR(
-					handle->conn,
-					fsp->fsp_name->base_name,
-					AFPINFO_EA_NETATALK);
-				break;
-
-			default:
-				DBG_ERR("Unexpected meta config [%d]\n",
-					config->meta);
-				rc = -1;
-				goto exit;
-			}
+	ai = afpinfo_unpack(talloc_tos(), data);
+	if (ai == NULL) {
+		return -1;
+	}
 
-			if (rc != 0 && errno != ENOENT && errno != ENOATTR) {
-				DBG_WARNING("Can't delete metadata for %s: %s\n",
-					    fsp->fsp_name->base_name, strerror(errno));
-				goto exit;
-			}
+	if (ai_empty_finderinfo(ai)) {
+		ret = SMB_VFS_REMOVEXATTR(handle->conn,
+					  fsp->fsp_name->base_name,
+					  AFPINFO_EA_NETATALK);
 
-			rc = 0;
-			goto exit;
+		if (ret != 0 && errno != ENOENT && errno != ENOATTR) {
+			DBG_ERR("Can't delete metadata for %s: %s\n",
+				fsp_str_dbg(fsp), strerror(errno));
+			return -1;
 		}
+
+		return n;
 	}
 
+	ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
 	if (ad == NULL) {
-		len = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
-		if (len != n) {
-			rc = -1;
-			goto exit;
+		ad = ad_init(talloc_tos(), handle, ADOUBLE_META);
+		if (ad == NULL) {
+			return -1;
 		}
-		goto exit;
+	}
+	p = ad_get_entry(ad, ADEID_FINDERI);
+	if (p == NULL) {
+		DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
+		TALLOC_FREE(ad);
+		return -1;
 	}
 
-	if (ad->ad_type == ADOUBLE_META) {
-		char *p = NULL;
+	memcpy(p, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
 
-		p = ad_get_entry(ad, ADEID_FINDERI);
-		if (p == NULL) {
-			DBG_ERR("No ADEID_FINDERI for [%s]\n",
-				fsp->fsp_name->base_name);
-			rc = -1;
-			goto exit;
-		}
+	ret = ad_fset(ad, fsp);
+	if (ret != 0) {
+		DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp));
+		TALLOC_FREE(ad);
+		return -1;
+	}
 
-		memcpy(p, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
-		rc = ad_fset(ad, fsp);
-	} else {
-		len = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n,
-                                   offset + ad_getentryoff(ad, ADEID_RFORK));
-		if (len != n) {
-			rc = -1;
-			goto exit;
-		}
+	TALLOC_FREE(ad);
+	return n;
+}
 
-		if (config->rsrc == FRUIT_RSRC_ADFILE) {
-			rc = ad_read(ad, fsp->base_fsp->fsp_name->base_name);
-			if (rc == -1) {
-				goto exit;
-			}
-			rc = 0;
+static ssize_t fruit_pwrite_meta(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);
+	ssize_t nwritten;
 
-			if ((len + offset) > ad_getentrylen(ad, ADEID_RFORK)) {
-				ad_setentrylen(ad, ADEID_RFORK, len + offset);
-				rc = ad_fset(ad, fsp);
-			}
-		}
+	/*
+	 * Writing an all 0 blob to the metadata stream
+	 * results in the stream being removed on a macOS
+	 * server. This ensures we behave the same and it
+	 * verified by the "delete AFP_AfpInfo by writing all
+	 * 0" test.
+	 */
+	if (n != AFP_INFO_SIZE || offset != 0) {
+		DBG_ERR("unexpected offset=%jd or size=%jd\n",
+			(intmax_t)offset, (intmax_t)n);
+		return -1;
 	}
 
-exit:
-	TALLOC_FREE(ai);
-	if (rc != 0) {
+	switch (fio->config->meta) {
+	case FRUIT_META_STREAM:
+		nwritten = fruit_pwrite_meta_stream(handle, fsp, data,
+						    n, offset);
+		break;
+
+	case FRUIT_META_NETATALK:
+		nwritten = fruit_pwrite_meta_netatalk(handle, fsp, data,
+						      n, offset);
+		break;
+
+	default:
+		DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
+		return -1;
+	}
+
+	return nwritten;
+}
+
+static ssize_t fruit_pwrite_rsrc_stream(vfs_handle_struct *handle,
+					files_struct *fsp, const void *data,
+					size_t n, off_t offset)
+{
+	return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
+}
+
+static ssize_t fruit_pwrite_rsrc_xattr(vfs_handle_struct *handle,
+				       files_struct *fsp, const void *data,
+				       size_t n, off_t offset)
+{
+	return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
+}
+
+static ssize_t fruit_pwrite_rsrc_adouble(vfs_handle_struct *handle,
+					 files_struct *fsp, const void *data,
+					 size_t n, off_t offset)
+{
+	struct adouble *ad = NULL;
+	ssize_t nwritten;
+	int ret;
+
+	ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
+	if (ad == NULL) {
+		DBG_ERR("ad_get [%s] failed\n", fsp_str_dbg(fsp));
 		return -1;
 	}
+
+	nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n,
+				       offset + ad_getentryoff(ad, ADEID_RFORK));
+	if (nwritten != n) {
+		DBG_ERR("Short write on [%s] [%zd/%zd]\n",
+			fsp_str_dbg(fsp), nwritten, n);
+		TALLOC_FREE(ad);
+		return -1;
+	}
+
+	if ((n + offset) > ad_getentrylen(ad, ADEID_RFORK)) {
+		ad_setentrylen(ad, ADEID_RFORK, n + offset);
+		ret = ad_fset(ad, fsp);
+		if (ret != 0) {
+			DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp));
+			TALLOC_FREE(ad);
+			return -1;
+		}
+	}
+
+	TALLOC_FREE(ad);
 	return n;
 }
 
+static ssize_t fruit_pwrite_rsrc(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);
+	ssize_t nwritten;
+
+	switch (fio->config->rsrc) {
+	case FRUIT_RSRC_STREAM:
+		nwritten = fruit_pwrite_rsrc_stream(handle, fsp, data, n, offset);
+		break;
+
+	case FRUIT_RSRC_ADFILE:
+		nwritten = fruit_pwrite_rsrc_adouble(handle, fsp, data, n, offset);
+		break;
+
+	case FRUIT_RSRC_XATTR:
+		nwritten = fruit_pwrite_rsrc_xattr(handle, fsp, data, n, offset);
+		break;
+
+	default:
+		DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
+		return -1;
+	}
+
+	return nwritten;
+}
+
+static ssize_t fruit_pwrite(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);
+	ssize_t nwritten;
+
+	DBG_DEBUG("Path [%s] offset=%zd, size=%zd\n",
+		  fsp_str_dbg(fsp), offset, n);
+
+	if (fio == NULL) {
+		return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
+	}
+
+	if (fio->type == ADOUBLE_META) {
+		nwritten = fruit_pwrite_meta(handle, fsp, data, n, offset);
+	} else {
+		nwritten = fruit_pwrite_rsrc(handle, fsp, data, n, offset);
+	}
+
+	DBG_DEBUG("Path [%s] nwritten=%zd\n", fsp_str_dbg(fsp), nwritten);
+	return nwritten;
+}
+
 /**
  * Helper to stat/lstat the base file of an smb_fname.
  */
-- 
2.9.3


From 229029d4d1298e716f27e4d8a61dbcacdebfc1f2 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Thu, 8 Dec 2016 20:39:38 +0100
Subject: [PATCH 39/56] vfs_fruit: refactor fruit_fstat and use new adouble API

Use struct fio to denote a fsp handle is for a stream we care about.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12427

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 source3/modules/vfs_fruit.c | 152 +++++++++++++++++++++++++++++++-------------
 1 file changed, 108 insertions(+), 44 deletions(-)

diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c
index 230e807..5c58127 100644
--- a/source3/modules/vfs_fruit.c
+++ b/source3/modules/vfs_fruit.c
@@ -4202,17 +4202,24 @@ static int fruit_lstat(vfs_handle_struct *handle,
 	return rc;
 }
 
-static int fruit_fstat_meta(vfs_handle_struct *handle,
-			    files_struct *fsp,
-			    SMB_STRUCT_STAT *sbuf)
+static int fruit_fstat_meta_stream(vfs_handle_struct *handle,
+				   files_struct *fsp,
+				   SMB_STRUCT_STAT *sbuf)
 {
-	DEBUG(10, ("fruit_fstat_meta called for %s\n",
-		   smb_fname_str_dbg(fsp->base_fsp->fsp_name)));
+	return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
+}
 
-	/* Populate the stat struct with info from the base file. */
-	if (fruit_stat_base(handle, fsp->base_fsp->fsp_name, false) == -1) {
+static int fruit_fstat_meta_netatalk(vfs_handle_struct *handle,
+				     files_struct *fsp,
+				     SMB_STRUCT_STAT *sbuf)
+{
+	int ret;
+
+	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);
@@ -4220,65 +4227,124 @@ static int fruit_fstat_meta(vfs_handle_struct *handle,
 	return 0;
 }
 
-static int fruit_fstat_rsrc(vfs_handle_struct *handle, files_struct *fsp,
+static int fruit_fstat_meta(vfs_handle_struct *handle,
+			    files_struct *fsp,
 			    SMB_STRUCT_STAT *sbuf)
 {
-	struct fruit_config_data *config;
-	struct adouble *ad = (struct adouble *)VFS_FETCH_FSP_EXTENSION(
-		handle, fsp);
+	struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
+	int ret;
 
-	DEBUG(10, ("fruit_fstat_rsrc called for %s\n",
-		   smb_fname_str_dbg(fsp->base_fsp->fsp_name)));
+	if (fio == NULL) {
+		return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
+	}
 
-	SMB_VFS_HANDLE_GET_DATA(handle, config,
-				struct fruit_config_data, return -1);
+	DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
 
-	if (config->rsrc == FRUIT_RSRC_STREAM) {
-		return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
+	switch (fio->config->meta) {
+	case FRUIT_META_STREAM:
+		ret = fruit_fstat_meta_stream(handle, fsp, sbuf);
+		break;
+
+	case FRUIT_META_NETATALK:
+		ret = fruit_fstat_meta_netatalk(handle, fsp, sbuf);
+		break;
+
+	default:
+		DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
+		return -1;
 	}
 
+	DBG_DEBUG("Path [%s] ret [%d]\n", fsp_str_dbg(fsp), ret);
+	return ret;
+}
+
+static int fruit_fstat_rsrc_xattr(vfs_handle_struct *handle,
+				  files_struct *fsp,
+				  SMB_STRUCT_STAT *sbuf)
+{
+	return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
+}
+
+static int fruit_fstat_rsrc_stream(vfs_handle_struct *handle,
+				   files_struct *fsp,
+				   SMB_STRUCT_STAT *sbuf)
+{
+	return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
+}
+
+static int fruit_fstat_rsrc_adouble(vfs_handle_struct *handle,
+				    files_struct *fsp,
+				    SMB_STRUCT_STAT *sbuf)
+{
+	struct adouble *ad = NULL;
+	int ret;
+
 	/* Populate the stat struct with info from the base file. */
-	if (fruit_stat_base(handle, fsp->base_fsp->fsp_name, false) == -1) {
+	ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
+	if (ret == -1) {
+		return -1;
+	}
+
+	ad = ad_get(talloc_tos(), handle,
+		    fsp->base_fsp->fsp_name->base_name,
+		    ADOUBLE_RSRC);
+	if (ad == NULL) {
+		DBG_ERR("ad_get [%s] failed [%s]\n",
+			fsp_str_dbg(fsp), strerror(errno));
 		return -1;
 	}
+
 	*sbuf = fsp->base_fsp->fsp_name->st;
 	sbuf->st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
 	sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
 
-	DEBUG(10, ("fruit_fstat_rsrc %s, size: %zd\n",
-		   smb_fname_str_dbg(fsp->fsp_name),
-		   (ssize_t)sbuf->st_ex_size));
-
+	TALLOC_FREE(ad);
 	return 0;
 }
 
+static int fruit_fstat_rsrc(vfs_handle_struct *handle, files_struct *fsp,
+			    SMB_STRUCT_STAT *sbuf)
+{
+	struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
+	int ret;
+
+	switch (fio->config->rsrc) {
+	case FRUIT_RSRC_STREAM:
+		ret = fruit_fstat_rsrc_stream(handle, fsp, sbuf);
+		break;
+
+	case FRUIT_RSRC_ADFILE:
+		ret = fruit_fstat_rsrc_adouble(handle, fsp, sbuf);
+		break;
+
+	case FRUIT_RSRC_XATTR:
+		ret = fruit_fstat_rsrc_xattr(handle, fsp, sbuf);
+		break;
+
+	default:
+		DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
+		return -1;
+	}
+
+	return ret;
+}
+
 static int fruit_fstat(vfs_handle_struct *handle, files_struct *fsp,
 		       SMB_STRUCT_STAT *sbuf)
 {
+	struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
 	int rc;
-	struct adouble *ad = (struct adouble *)
-		VFS_FETCH_FSP_EXTENSION(handle, fsp);
-
-	DEBUG(10, ("fruit_fstat called for %s\n",
-		   smb_fname_str_dbg(fsp->fsp_name)));
 
-	if (ad == NULL || fsp->base_fsp == NULL) {
-		rc = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
-		goto exit;
+	if (fio == NULL) {
+		return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
 	}
 
-	switch (ad->ad_type) {
-	case ADOUBLE_META:
+	DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
+
+	if (fio->type == ADOUBLE_META) {
 		rc = fruit_fstat_meta(handle, fsp, sbuf);
-		break;
-	case ADOUBLE_RSRC:
+	} else {
 		rc = fruit_fstat_rsrc(handle, fsp, sbuf);
-		break;
-	default:
-		DEBUG(10, ("fruit_fstat %s: bad type\n",
-			   smb_fname_str_dbg(fsp->fsp_name)));
-		rc = -1;
-		goto exit;
 	}
 
 	if (rc == 0) {
@@ -4287,10 +4353,8 @@ static int fruit_fstat(vfs_handle_struct *handle, files_struct *fsp,
 		sbuf->st_ex_blocks = sbuf->st_ex_size / STAT_ST_BLOCKSIZE + 1;
 	}
 
-exit:
-	DEBUG(10, ("fruit_fstat %s, size: %zd\n",
-		   smb_fname_str_dbg(fsp->fsp_name),
-		   (ssize_t)sbuf->st_ex_size));
+	DBG_DEBUG("Path [%s] rc [%d] size [%zd]\n",
+		  fsp_str_dbg(fsp), rc, sbuf->st_ex_size);
 	return rc;
 }
 
-- 
2.9.3


From b19887a6c3cb312ce69abbe54972525e3667ff1c Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Thu, 8 Dec 2016 20:41:55 +0100
Subject: [PATCH 40/56] vfs_fruit: use fio in fruit_fallocate

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12427

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 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 5c58127..7edb95c 100644
--- a/source3/modules/vfs_fruit.c
+++ b/source3/modules/vfs_fruit.c
@@ -4636,10 +4636,9 @@ static int fruit_fallocate(struct vfs_handle_struct *handle,
 			   off_t offset,
 			   off_t len)
 {
-        struct adouble *ad =
-		(struct adouble *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
+	struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
 
-	if (ad == NULL) {
+	if (fio == NULL) {
 		return SMB_VFS_NEXT_FALLOCATE(handle, fsp, mode, offset, len);
 	}
 
-- 
2.9.3


From aee844400ac3ff394e148f6b2f6efdcb1e9fec22 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Thu, 8 Dec 2016 20:42:54 +0100
Subject: [PATCH 41/56] vfs_fruit: refactor fruit_ftruncate and use new adouble
 API

Use struct fio to denote a fsp handle is for a stream we care about.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12427

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 source3/modules/vfs_fruit.c | 65 ++++++++++++++++++++++++++-------------------
 1 file changed, 38 insertions(+), 27 deletions(-)

diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c
index 7edb95c..b223f1a 100644
--- a/source3/modules/vfs_fruit.c
+++ b/source3/modules/vfs_fruit.c
@@ -4671,6 +4671,8 @@ static int fruit_ftruncate_rsrc_adouble(struct vfs_handle_struct *handle,
 
 	ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
 	if (ad == NULL) {
+		DBG_DEBUG("ad_get [%s] failed [%s]\n",
+			  fsp_str_dbg(fsp), strerror(errno));
 		return -1;
 	}
 
@@ -4678,6 +4680,7 @@ static int fruit_ftruncate_rsrc_adouble(struct vfs_handle_struct *handle,
 
 	rc = SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset + ad_off);
 	if (rc != 0) {
+		TALLOC_FREE(ad);
 		return -1;
 	}
 
@@ -4685,14 +4688,13 @@ static int fruit_ftruncate_rsrc_adouble(struct vfs_handle_struct *handle,
 
 	rc = ad_fset(ad, fsp);
 	if (rc != 0) {
-		DBG_ERR("ad_write [%s] failed [%s]\n",
+		DBG_ERR("ad_fset [%s] failed [%s]\n",
 			fsp_str_dbg(fsp), strerror(errno));
+		TALLOC_FREE(ad);
 		return -1;
 	}
 
-	DBG_DEBUG("Path [%s] offset [%jd]\n",
-		  fsp_str_dbg(fsp), (intmax_t)offset);
-
+	TALLOC_FREE(ad);
 	return 0;
 }
 
@@ -4711,13 +4713,10 @@ static int fruit_ftruncate_rsrc(struct vfs_handle_struct *handle,
 				struct files_struct *fsp,
 				off_t offset)
 {
+	struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
 	int ret;
-	struct fruit_config_data *config;
-
-	SMB_VFS_HANDLE_GET_DATA(handle, config,
-				struct fruit_config_data, return -1);
 
-	switch (config->rsrc) {
+	switch (fio->config->rsrc) {
 	case FRUIT_RSRC_XATTR:
 		ret = fruit_ftruncate_rsrc_xattr(handle, fsp, offset);
 		break;
@@ -4731,7 +4730,7 @@ static int fruit_ftruncate_rsrc(struct vfs_handle_struct *handle,
 		break;
 
 	default:
-		DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
+		DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
 		return -1;
 	}
 
@@ -4739,33 +4738,45 @@ static int fruit_ftruncate_rsrc(struct vfs_handle_struct *handle,
 	return ret;
 }
 
+static int fruit_ftruncate_meta(struct vfs_handle_struct *handle,
+				struct files_struct *fsp,
+				off_t offset)
+{
+	if (offset > 60) {
+		DBG_WARNING("ftruncate %s to %jd",
+			    fsp_str_dbg(fsp), (intmax_t)offset);
+		/* OS X returns NT_STATUS_ALLOTTED_SPACE_EXCEEDED  */
+		errno = EOVERFLOW;
+		return -1;
+	}
+
+	/* OS X returns success but does nothing  */
+	DBG_INFO("ignoring ftruncate %s to %jd\n",
+		 fsp_str_dbg(fsp), (intmax_t)offset);
+	return 0;
+}
+
 static int fruit_ftruncate(struct vfs_handle_struct *handle,
 			   struct files_struct *fsp,
 			   off_t offset)
 {
-	DBG_DEBUG("fruit_ftruncate called for file %s offset %.0f\n",
-		   fsp_str_dbg(fsp), (double)offset);
+	struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
+	int ret;
 
-	if (is_afpinfo_stream(fsp->fsp_name)) {
-		if (offset > 60) {
-			DBG_WARNING("ftruncate %s to %jd",
-				    fsp_str_dbg(fsp), (intmax_t)offset);
-			/* OS X returns NT_STATUS_ALLOTTED_SPACE_EXCEEDED  */
-			errno = EOVERFLOW;
-			return -1;
-		}
+	DBG_DEBUG("Path [%s] offset [%zd]\n", fsp_str_dbg(fsp), offset);
 
-		DBG_WARNING("ignoring ftruncate %s to %jd",
-			    fsp_str_dbg(fsp), (intmax_t)offset);
-		/* OS X returns success but does nothing  */
-		return 0;
+	if (fio == NULL) {
+		return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
 	}
 
-	if (is_afpresource_stream(fsp->fsp_name)) {
-		return fruit_ftruncate_rsrc(handle, fsp, offset);
+	if (fio->type == ADOUBLE_META) {
+		ret = fruit_ftruncate_meta(handle, fsp, offset);
+	} else {
+		ret = fruit_ftruncate_rsrc(handle, fsp, offset);
 	}
 
-	return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
+	DBG_DEBUG("Path [%s] result [%d]\n", fsp_str_dbg(fsp), ret);
+	return ret;
 }
 
 static NTSTATUS fruit_create_file(vfs_handle_struct *handle,
-- 
2.9.3


From 0a47489455c79391eb82b996fd45cb107469ae9e Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Thu, 8 Dec 2016 20:43:21 +0100
Subject: [PATCH 42/56] selftest: reenable vfs_fruit tests

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12427

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 source3/selftest/tests.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/source3/selftest/tests.py b/source3/selftest/tests.py
index 0c99b1a..88cdd6a 100755
--- a/source3/selftest/tests.py
+++ b/source3/selftest/tests.py
@@ -334,7 +334,7 @@ nbt = ["nbt.dgram" ]
 
 libsmbclient = ["libsmbclient"]
 
-vfs = ["vfs.acl_xattr"]
+vfs = ["vfs.fruit", "vfs.acl_xattr"]
 
 tests= base + raw + smb2 + rpc + unix + local + rap + nbt + libsmbclient + idmap + vfs
 
-- 
2.9.3


From 25be451abc6a14e72eb76d8278044bc2c5d6fd51 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Tue, 15 Nov 2016 22:31:20 +0100
Subject: [PATCH 43/56] selftest: move vfs_fruit tests that require
 "fruit:metadata=netatalk" to vfs.fruit_netatalk

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12427

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 source3/selftest/tests.py   |  4 +++-
 source4/torture/vfs/fruit.c | 15 +++++++++++++--
 source4/torture/vfs/vfs.c   |  1 +
 3 files changed, 17 insertions(+), 3 deletions(-)

diff --git a/source3/selftest/tests.py b/source3/selftest/tests.py
index 88cdd6a..0eecc70 100755
--- a/source3/selftest/tests.py
+++ b/source3/selftest/tests.py
@@ -334,7 +334,7 @@ nbt = ["nbt.dgram" ]
 
 libsmbclient = ["libsmbclient"]
 
-vfs = ["vfs.fruit", "vfs.acl_xattr"]
+vfs = ["vfs.fruit", "vfs.acl_xattr", "vfs.fruit_netatalk"]
 
 tests= base + raw + smb2 + rpc + unix + local + rap + nbt + libsmbclient + idmap + vfs
 
@@ -418,6 +418,8 @@ for t in tests:
         plansmbtorture4testsuite(t, "ad_dc", '//$SERVER/tmp -U$USERNAME%$PASSWORD')
     elif t == "vfs.fruit":
         plansmbtorture4testsuite(t, "nt4_dc", '//$SERVER_IP/vfs_fruit -U$USERNAME%$PASSWORD --option=torture:localdir=$SELFTEST_PREFIX/nt4_dc/share')
+    elif t == "vfs.fruit_netatalk":
+        plansmbtorture4testsuite(t, "nt4_dc", '//$SERVER_IP/vfs_fruit -U$USERNAME%$PASSWORD --option=torture:localdir=$SELFTEST_PREFIX/nt4_dc/share')
     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 d5de9d5..ff3bfa2 100644
--- a/source4/torture/vfs/fruit.c
+++ b/source4/torture/vfs/fruit.c
@@ -3542,11 +3542,9 @@ struct torture_suite *torture_vfs_fruit(void)
 	suite->description = talloc_strdup(suite, "vfs_fruit tests");
 
 	torture_suite_add_1smb2_test(suite, "copyfile", test_copyfile);
-	torture_suite_add_1smb2_test(suite, "read netatalk metadata", test_read_netatalk_metadata);
 	torture_suite_add_1smb2_test(suite, "read metadata", test_read_afpinfo);
 	torture_suite_add_1smb2_test(suite, "write metadata", test_write_atalk_metadata);
 	torture_suite_add_1smb2_test(suite, "resource fork IO", test_write_atalk_rfork_io);
-	torture_suite_add_1smb2_test(suite, "OS X AppleDouble file conversion", test_adouble_conversion);
 	torture_suite_add_1smb2_test(suite, "SMB2/CREATE context AAPL", test_aapl);
 	torture_suite_add_1smb2_test(suite, "stream names", test_stream_names);
 	torture_suite_add_1smb2_test(suite, "truncate resource fork to 0 bytes", test_rfork_truncate);
@@ -3563,3 +3561,16 @@ struct torture_suite *torture_vfs_fruit(void)
 
 	return suite;
 }
+
+struct torture_suite *torture_vfs_fruit_netatalk(void)
+{
+	struct torture_suite *suite = torture_suite_create(
+		talloc_autofree_context(), "fruit_netatalk");
+
+	suite->description = talloc_strdup(suite, "vfs_fruit tests for Netatalk interop that require fruit:metadata=netatalk");
+
+	torture_suite_add_1smb2_test(suite, "read netatalk metadata", test_read_netatalk_metadata);
+	torture_suite_add_1smb2_test(suite, "OS X AppleDouble file conversion", test_adouble_conversion);
+
+	return suite;
+}
diff --git a/source4/torture/vfs/vfs.c b/source4/torture/vfs/vfs.c
index 7f805f4..8b443fb 100644
--- a/source4/torture/vfs/vfs.c
+++ b/source4/torture/vfs/vfs.c
@@ -107,6 +107,7 @@ NTSTATUS torture_vfs_init(void)
 	suite->description = talloc_strdup(suite, "VFS modules tests");
 
 	torture_suite_add_suite(suite, torture_vfs_fruit());
+	torture_suite_add_suite(suite, torture_vfs_fruit_netatalk());
 	torture_suite_add_suite(suite, torture_acl_xattr());
 
 	torture_register_suite(suite);
-- 
2.9.3


From e3d72011984d3737d6479a9efa0170f0b82446cc Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Tue, 15 Nov 2016 15:25:14 +0100
Subject: [PATCH 44/56] selftest: run vfs_fruit tests against share with
 fruit:metadata=stream

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12427

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 selftest/target/Samba3.pm | 7 +++++++
 source3/selftest/tests.py | 1 +
 2 files changed, 8 insertions(+)

diff --git a/selftest/target/Samba3.pm b/selftest/target/Samba3.pm
index 0ec2917..b2a23af 100755
--- a/selftest/target/Samba3.pm
+++ b/selftest/target/Samba3.pm
@@ -1660,6 +1660,13 @@ sub provision($$$$$$$$)
 	fruit:locking = netatalk
 	fruit:encoding = native
 
+[vfs_fruit_metadata_stream]
+	path = $shrdir
+	vfs objects = fruit streams_xattr acl_xattr
+	ea support = yes
+	fruit:resource = file
+	fruit:metadata = stream
+
 [badname-tmp]
 	path = $badnames_shrdir
 	guest ok = yes
diff --git a/source3/selftest/tests.py b/source3/selftest/tests.py
index 0eecc70..07cfad1 100755
--- a/source3/selftest/tests.py
+++ b/source3/selftest/tests.py
@@ -418,6 +418,7 @@ for t in tests:
         plansmbtorture4testsuite(t, "ad_dc", '//$SERVER/tmp -U$USERNAME%$PASSWORD')
     elif t == "vfs.fruit":
         plansmbtorture4testsuite(t, "nt4_dc", '//$SERVER_IP/vfs_fruit -U$USERNAME%$PASSWORD --option=torture:localdir=$SELFTEST_PREFIX/nt4_dc/share')
+        plansmbtorture4testsuite(t, "nt4_dc", '//$SERVER_IP/vfs_fruit_metadata_stream -U$USERNAME%$PASSWORD')
     elif t == "vfs.fruit_netatalk":
         plansmbtorture4testsuite(t, "nt4_dc", '//$SERVER_IP/vfs_fruit -U$USERNAME%$PASSWORD --option=torture:localdir=$SELFTEST_PREFIX/nt4_dc/share')
     elif t == "rpc.schannel_anon_setpw":
-- 
2.9.3


From 56b5d00c0d41b37c56763a8bf77d437f889595cd Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Fri, 2 Dec 2016 07:42:07 +0100
Subject: [PATCH 45/56] selftest: also run vfs_fruit tests with streams_depot

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12427

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 selftest/target/Samba3.pm | 7 +++++++
 source3/selftest/tests.py | 1 +
 2 files changed, 8 insertions(+)

diff --git a/selftest/target/Samba3.pm b/selftest/target/Samba3.pm
index b2a23af..0b6fff8 100755
--- a/selftest/target/Samba3.pm
+++ b/selftest/target/Samba3.pm
@@ -1667,6 +1667,13 @@ sub provision($$$$$$$$)
 	fruit:resource = file
 	fruit:metadata = stream
 
+[vfs_fruit_stream_depot]
+	path = $shrdir
+	vfs objects = fruit streams_depot acl_xattr
+	ea support = yes
+	fruit:resource = stream
+	fruit:metadata = stream
+
 [badname-tmp]
 	path = $badnames_shrdir
 	guest ok = yes
diff --git a/source3/selftest/tests.py b/source3/selftest/tests.py
index 07cfad1..1e948aa 100755
--- a/source3/selftest/tests.py
+++ b/source3/selftest/tests.py
@@ -419,6 +419,7 @@ for t in tests:
     elif t == "vfs.fruit":
         plansmbtorture4testsuite(t, "nt4_dc", '//$SERVER_IP/vfs_fruit -U$USERNAME%$PASSWORD --option=torture:localdir=$SELFTEST_PREFIX/nt4_dc/share')
         plansmbtorture4testsuite(t, "nt4_dc", '//$SERVER_IP/vfs_fruit_metadata_stream -U$USERNAME%$PASSWORD')
+        plansmbtorture4testsuite(t, "nt4_dc", '//$SERVER_IP/vfs_fruit_stream_depot -U$USERNAME%$PASSWORD')
     elif t == "vfs.fruit_netatalk":
         plansmbtorture4testsuite(t, "nt4_dc", '//$SERVER_IP/vfs_fruit -U$USERNAME%$PASSWORD --option=torture:localdir=$SELFTEST_PREFIX/nt4_dc/share')
     elif t == "rpc.schannel_anon_setpw":
-- 
2.9.3


From cc6bda3e9e0ef6425c4e7a061235920f6468ab52 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Thu, 8 Dec 2016 17:41:14 +0100
Subject: [PATCH 46/56] selftest: add description to vfs_fruit testsuites

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12427

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 source3/selftest/tests.py | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/source3/selftest/tests.py b/source3/selftest/tests.py
index 1e948aa..e00ac85 100755
--- a/source3/selftest/tests.py
+++ b/source3/selftest/tests.py
@@ -417,9 +417,9 @@ for t in tests:
         plansmbtorture4testsuite(t, "nt4_dc", '//$SERVER_IP/tmpsort -U$USERNAME%$PASSWORD')
         plansmbtorture4testsuite(t, "ad_dc", '//$SERVER/tmp -U$USERNAME%$PASSWORD')
     elif t == "vfs.fruit":
-        plansmbtorture4testsuite(t, "nt4_dc", '//$SERVER_IP/vfs_fruit -U$USERNAME%$PASSWORD --option=torture:localdir=$SELFTEST_PREFIX/nt4_dc/share')
-        plansmbtorture4testsuite(t, "nt4_dc", '//$SERVER_IP/vfs_fruit_metadata_stream -U$USERNAME%$PASSWORD')
-        plansmbtorture4testsuite(t, "nt4_dc", '//$SERVER_IP/vfs_fruit_stream_depot -U$USERNAME%$PASSWORD')
+        plansmbtorture4testsuite(t, "nt4_dc", '//$SERVER_IP/vfs_fruit -U$USERNAME%$PASSWORD --option=torture:localdir=$SELFTEST_PREFIX/nt4_dc/share', 'metadata_netatalk')
+        plansmbtorture4testsuite(t, "nt4_dc", '//$SERVER_IP/vfs_fruit_metadata_stream -U$USERNAME%$PASSWORD', 'metadata_stream')
+        plansmbtorture4testsuite(t, "nt4_dc", '//$SERVER_IP/vfs_fruit_stream_depot -U$USERNAME%$PASSWORD', 'streams_depot')
     elif t == "vfs.fruit_netatalk":
         plansmbtorture4testsuite(t, "nt4_dc", '//$SERVER_IP/vfs_fruit -U$USERNAME%$PASSWORD --option=torture:localdir=$SELFTEST_PREFIX/nt4_dc/share')
     elif t == "rpc.schannel_anon_setpw":
-- 
2.9.3


From 614f6f28e189d2ff993900e12e244003f9ed3596 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Tue, 29 Nov 2016 16:21:08 +0100
Subject: [PATCH 47/56] s4/torture: vfs_fruit: add test_null_afpinfo test

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12427

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 source4/torture/vfs/fruit.c | 86 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 86 insertions(+)

diff --git a/source4/torture/vfs/fruit.c b/source4/torture/vfs/fruit.c
index ff3bfa2..27d6065 100644
--- a/source4/torture/vfs/fruit.c
+++ b/source4/torture/vfs/fruit.c
@@ -3527,6 +3527,91 @@ done:
 }
 
 /*
+ * This tests that right after creating the AFP_AfpInfo stream,
+ * reading from the stream returns an empty, default metadata blob of
+ * 60 bytes.
+ *
+ * NOTE: against OS X SMB server this only works if the read request
+ * is compounded with the create that created the stream, is fails
+ * otherwise. We don't care...
+ */
+static bool test_null_afpinfo(struct torture_context *tctx,
+			      struct smb2_tree *tree)
+{
+	TALLOC_CTX *mem_ctx = talloc_new(tctx);
+	const char *fname = "test_null_afpinfo";
+	const char *sname = "test_null_afpinfo" AFPINFO_STREAM_NAME;
+	NTSTATUS status;
+	bool ret = true;
+	struct smb2_request *req[3];
+	struct smb2_handle handle;
+	struct smb2_create create;
+	struct smb2_read read;
+	AfpInfo *afpinfo = NULL;
+	char *afpinfo_buf = NULL;
+	const char *type_creator = "SMB,OLE!";
+
+	torture_comment(tctx, "Checking create of AfpInfo stream\n");
+
+	smb2_util_unlink(tree, fname);
+
+	ret = torture_setup_file(mem_ctx, tree, fname, false);
+	torture_assert_goto(tctx, ret == true, ret, done, "torture_setup_file failed");
+
+	ZERO_STRUCT(create);
+	create.in.desired_access = SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA;
+	create.in.share_access = FILE_SHARE_READ | FILE_SHARE_DELETE;
+	create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
+	create.in.impersonation_level = SMB2_IMPERSONATION_IMPERSONATION;
+	create.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
+	create.in.fname = sname;
+
+	smb2_transport_compound_start(tree->session->transport, 2);
+
+	req[0] = smb2_create_send(tree, &create);
+
+	handle.data[0] = UINT64_MAX;
+	handle.data[1] = UINT64_MAX;
+
+	smb2_transport_compound_set_related(tree->session->transport, true);
+
+	ZERO_STRUCT(read);
+	read.in.file.handle = handle;
+	read.in.length = AFP_INFO_SIZE;
+	req[1] = smb2_read_send(tree, &read);
+
+	status = smb2_create_recv(req[0], tree, &create);
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create_recv failed");
+
+	handle = create.out.file.handle;
+
+	status = smb2_read_recv(req[1], tree, &read);
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_read_recv failed");
+
+	afpinfo = torture_afpinfo_new(mem_ctx);
+	torture_assert_goto(tctx, afpinfo != NULL, ret, done, "torture_afpinfo_new failed");
+
+	memcpy(afpinfo->afpi_FinderInfo, type_creator, 8);
+
+	afpinfo_buf = torture_afpinfo_pack(tctx, afpinfo);
+	torture_assert_goto(tctx, afpinfo_buf != NULL, ret, done, "torture_afpinfo_new failed");
+
+	status = smb2_util_write(tree, handle, afpinfo_buf, 0, AFP_INFO_SIZE);
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_util_write failed");
+
+	smb2_util_close(tree, handle);
+
+	ret = check_stream(tree, __location__, tctx, mem_ctx, fname, AFPINFO_STREAM,
+			   0, 60, 16, 8, type_creator);
+	torture_assert_goto(tctx, ret == true, ret, done, "check_stream failed");
+
+done:
+	smb2_util_unlink(tree, fname);
+	talloc_free(mem_ctx);
+	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
  * argument with the local path to the share:
@@ -3558,6 +3643,7 @@ struct torture_suite *torture_vfs_fruit(void)
 	torture_suite_add_1smb2_test(suite, "create delete-on-close AFP_AfpResource", test_create_delete_on_close_resource);
 	torture_suite_add_1smb2_test(suite, "setinfo delete-on-close AFP_AfpResource", test_setinfo_delete_on_close_resource);
 	torture_suite_add_1smb2_test(suite, "setinfo eof AFP_AfpResource", test_setinfo_eof_resource);
+	torture_suite_add_1smb2_test(suite, "null afpinfo", test_null_afpinfo);
 
 	return suite;
 }
-- 
2.9.3


From 2cdd2a9b57a70a452b423a9f8f9aa1a9094a7c1e Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Mon, 5 Dec 2016 11:21:15 +0100
Subject: [PATCH 48/56] s4/torture: vfs_fruit: test deleting a file with
 resource fork

All the other tests ignore the return value of smb2_util_unlink().

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12427

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 source4/torture/vfs/fruit.c | 33 +++++++++++++++++++++++++++++++++
 1 file changed, 33 insertions(+)

diff --git a/source4/torture/vfs/fruit.c b/source4/torture/vfs/fruit.c
index 27d6065..2167032 100644
--- a/source4/torture/vfs/fruit.c
+++ b/source4/torture/vfs/fruit.c
@@ -3611,6 +3611,38 @@ done:
 	return ret;
 }
 
+static bool test_delete_file_with_rfork(struct torture_context *tctx,
+					struct smb2_tree *tree)
+{
+	const char *fname = "torture_write_rfork_io";
+	const char *rfork_content = "1234567890";
+	NTSTATUS status;
+	bool ret = true;
+
+	smb2_util_unlink(tree, fname);
+
+	torture_comment(tctx, "Test deleting file with resource fork\n");
+
+	ret = torture_setup_file(tctx, tree, fname, false);
+	torture_assert_goto(tctx, ret == true, ret, done, "torture_setup_file failed\n");
+
+	ret = write_stream(tree, __location__, tctx, tctx,
+			   fname, AFPRESOURCE_STREAM_NAME,
+			   10, 10, rfork_content);
+	torture_assert_goto(tctx, ret == true, ret, done, "write_stream failed\n");
+
+	ret = check_stream(tree, __location__, tctx, tctx,
+			   fname, AFPRESOURCE_STREAM_NAME,
+			   0, 20, 10, 10, rfork_content);
+	torture_assert_goto(tctx, ret == true, ret, done, "check_stream failed\n");
+
+	status = smb2_util_unlink(tree, fname);
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "check_stream failed\n");
+
+done:
+	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
@@ -3644,6 +3676,7 @@ struct torture_suite *torture_vfs_fruit(void)
 	torture_suite_add_1smb2_test(suite, "setinfo delete-on-close AFP_AfpResource", test_setinfo_delete_on_close_resource);
 	torture_suite_add_1smb2_test(suite, "setinfo eof AFP_AfpResource", test_setinfo_eof_resource);
 	torture_suite_add_1smb2_test(suite, "null afpinfo", test_null_afpinfo);
+	torture_suite_add_1smb2_test(suite, "delete", test_delete_file_with_rfork);
 
 	return suite;
 }
-- 
2.9.3


From 6ded5bbcd62a2e51585f15cd77748e0c0461ed14 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Tue, 6 Dec 2016 10:25:46 +0100
Subject: [PATCH 49/56] s4/torture: add a vfs_fruit renaming test with open
 rsrc fork

Verify IO on the resource fork works after a rename of the basefile.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12427

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 source4/torture/vfs/fruit.c | 95 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 95 insertions(+)

diff --git a/source4/torture/vfs/fruit.c b/source4/torture/vfs/fruit.c
index 2167032..d4d5bd5 100644
--- a/source4/torture/vfs/fruit.c
+++ b/source4/torture/vfs/fruit.c
@@ -3643,6 +3643,100 @@ done:
 	return ret;
 }
 
+static bool test_rename_and_read_rsrc(struct torture_context *tctx,
+				      struct smb2_tree *tree)
+{
+	bool ret = true;
+	NTSTATUS status;
+	struct smb2_create create, create2;
+	struct smb2_handle h1, h2;
+	const char *fname = "test_rename_openfile";
+	const char *sname = "test_rename_openfile" AFPRESOURCE_STREAM_NAME;
+	const char *fname_renamed = "test_rename_openfile_renamed";
+	const char *data = "1234567890";
+	union smb_setfileinfo sinfo;
+	struct smb2_read r;
+
+	ret = enable_aapl(tctx, tree);
+	torture_assert_goto(tctx, ret == true, ret, done, "enable_aapl failed");
+
+	torture_comment(tctx, "Create file with resource fork\n");
+
+	ret = torture_setup_file(tctx, tree, fname, false);
+	torture_assert_goto(tctx, ret == true, ret, done, "torture_setup_file");
+
+	ret = write_stream(tree, __location__, tctx, tctx,
+			   fname, AFPRESOURCE_STREAM_NAME, 0, 10, data);
+	torture_assert_goto(tctx, ret == true, ret, done, "write_stream failed");
+
+	torture_comment(tctx, "Open resource fork\n");
+
+	ZERO_STRUCT(create);
+	create.in.desired_access = SEC_FILE_ALL;
+	create.in.share_access = NTCREATEX_SHARE_ACCESS_MASK;
+	create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
+	create.in.create_disposition = NTCREATEX_DISP_OPEN;
+	create.in.impersonation_level = SMB2_IMPERSONATION_IMPERSONATION;
+	create.in.fname = sname;
+
+	status = smb2_create(tree, tctx, &create);
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create failed");
+
+	h1 = create.out.file.handle;
+
+	torture_comment(tctx, "Rename base file\n");
+
+	ZERO_STRUCT(create2);
+	create2.in.desired_access = SEC_FILE_ALL;
+	create2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
+	create2.in.share_access = NTCREATEX_SHARE_ACCESS_MASK;
+	create2.in.create_disposition = NTCREATEX_DISP_OPEN;
+	create2.in.impersonation_level = SMB2_IMPERSONATION_IMPERSONATION;
+	create2.in.fname = fname;
+
+	status = smb2_create(tree, tctx, &create2);
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create failed");
+
+	h2 = create2.out.file.handle;
+
+	ZERO_STRUCT(sinfo);
+	sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION;
+	sinfo.rename_information.in.file.handle = h2;
+	sinfo.rename_information.in.overwrite = 0;
+	sinfo.rename_information.in.root_fid = 0;
+	sinfo.rename_information.in.new_name = fname_renamed;
+
+	status = smb2_setinfo_file(tree, &sinfo);
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_setinfo_file failed");
+
+	smb2_util_close(tree, h2);
+
+	ZERO_STRUCT(r);
+	r.in.file.handle = h1;
+	r.in.length      = 10;
+	r.in.offset      = 0;
+
+	torture_comment(tctx, "Read resource fork of renamed file\n");
+
+	status = smb2_read(tree, tree, &r);
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_read failed");
+
+	smb2_util_close(tree, h1);
+
+	torture_assert_goto(tctx, r.out.data.length == 10, ret, done,
+			    talloc_asprintf(tctx, "smb2_read returned %jd bytes, expected 10\n",
+					    (intmax_t)r.out.data.length));
+
+	torture_assert_goto(tctx, memcmp(r.out.data.data, data, 10) == 0, ret, done,
+			    talloc_asprintf(tctx, "Bad data in stream\n"));
+
+done:
+	smb2_util_unlink(tree, fname);
+	smb2_util_unlink(tree, fname_renamed);
+
+	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
@@ -3677,6 +3771,7 @@ struct torture_suite *torture_vfs_fruit(void)
 	torture_suite_add_1smb2_test(suite, "setinfo eof AFP_AfpResource", test_setinfo_eof_resource);
 	torture_suite_add_1smb2_test(suite, "null afpinfo", test_null_afpinfo);
 	torture_suite_add_1smb2_test(suite, "delete", test_delete_file_with_rfork);
+	torture_suite_add_1smb2_test(suite, "read open rsrc after rename", test_rename_and_read_rsrc);
 
 	return suite;
 }
-- 
2.9.3


From f8c191b7f1a46b6936c6f34ffad6d56ca3e84a2e Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Thu, 8 Dec 2016 15:44:37 +0100
Subject: [PATCH 50/56] lib/torture: add torture_assert_mem_equal_goto

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12427

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 lib/torture/torture.h | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/lib/torture/torture.h b/lib/torture/torture.h
index 45332b2..f889211 100644
--- a/lib/torture/torture.h
+++ b/lib/torture/torture.h
@@ -357,6 +357,16 @@ void torture_result(struct torture_context *test,
 	} \
 	} while(0)
 
+#define torture_assert_mem_equal_goto(torture_ctx,got,expected,len,ret,label,cmt) \
+	do { const void *__got = (got), *__expected = (expected); \
+	if (memcmp(__got, __expected, len) != 0) { \
+		torture_result(torture_ctx, TORTURE_FAIL, \
+			       __location__": "#got" of len %d did not match "#expected": %s", (int)len, cmt); \
+		return false; \
+		goto label; \
+	} \
+	} while(0)
+
 static inline void torture_dump_data_str_cb(const char *buf, void *private_data)
 {
 	char **dump = (char **)private_data;
-- 
2.9.3


From 250515735cfbc1e3e82eb23402ed017d5f2fb42a Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Thu, 8 Dec 2016 15:45:12 +0100
Subject: [PATCH 51/56] s4/torture: add test for AAPL find with name with
 illegal NTFS characters

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12427

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 source4/torture/vfs/fruit.c | 109 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 109 insertions(+)

diff --git a/source4/torture/vfs/fruit.c b/source4/torture/vfs/fruit.c
index d4d5bd5..38f5059 100644
--- a/source4/torture/vfs/fruit.c
+++ b/source4/torture/vfs/fruit.c
@@ -3737,6 +3737,114 @@ done:
 	return ret;
 }
 
+static bool test_readdir_attr_illegal_ntfs(struct torture_context *tctx,
+					   struct smb2_tree *tree)
+{
+	TALLOC_CTX *mem_ctx = talloc_new(tctx);
+	const char *name = "test" "\xef\x80\xa2" "aapl"; /* "test:aapl" */
+	const char *fname = BASEDIR "\\test" "\xef\x80\xa2" "aapl"; /* "test:aapl" */
+	NTSTATUS status;
+	struct smb2_handle testdirh;
+	bool ret = true;
+	struct smb2_create io;
+	AfpInfo *info;
+	const char *type_creator = "SMB,OLE!";
+	struct smb2_find f;
+	unsigned int count;
+	union smb_search_data *d;
+	uint64_t rfork_len;
+	int i;
+
+	smb2_deltree(tree, BASEDIR);
+
+	status = torture_smb2_testdir(tree, BASEDIR, &testdirh);
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "torture_smb2_testdir failed");
+	smb2_util_close(tree, testdirh);
+
+	torture_comment(tctx, "Enabling AAPL\n");
+
+	ret = enable_aapl(tctx, tree);
+	torture_assert_goto(tctx, ret == true, ret, done, "enable_aapl failed");
+
+	/*
+	 * Now that Requested AAPL extensions are enabled, setup some
+	 * Mac files with metadata and resource fork
+	 */
+
+	torture_comment(tctx, "Preparing file\n");
+
+	ret = torture_setup_file(mem_ctx, tree, fname, false);
+	torture_assert_goto(tctx, ret == true, ret, done, "torture_setup_file failed");
+
+	info = torture_afpinfo_new(mem_ctx);
+	torture_assert_not_null_goto(tctx, info, ret, done, "torture_afpinfo_new failed");
+
+	memcpy(info->afpi_FinderInfo, type_creator, 8);
+	ret = torture_write_afpinfo(tree, tctx, mem_ctx, fname, info);
+	torture_assert_goto(tctx, ret == true, ret, done, "torture_write_afpinfo failed");
+
+	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");
+
+	/*
+	 * Ok, file is prepared, now call smb2/find
+	 */
+
+	torture_comment(tctx, "Issue find\n");
+
+	ZERO_STRUCT(io);
+	io.in.desired_access = SEC_RIGHTS_DIR_READ;
+	io.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
+	io.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
+	io.in.share_access = (NTCREATEX_SHARE_ACCESS_READ |
+			      NTCREATEX_SHARE_ACCESS_WRITE |
+			      NTCREATEX_SHARE_ACCESS_DELETE);
+	io.in.create_disposition = NTCREATEX_DISP_OPEN;
+	io.in.fname = BASEDIR;
+	status = smb2_create(tree, tctx, &io);
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create failed");
+
+	ZERO_STRUCT(f);
+	f.in.file.handle	= io.out.file.handle;
+	f.in.pattern		= "*";
+	f.in.max_response_size	= 0x1000;
+	f.in.level              = SMB2_FIND_ID_BOTH_DIRECTORY_INFO;
+
+	status = smb2_find_level(tree, tree, &f, &count, &d);
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_find_level failed");
+
+	status = smb2_util_close(tree, io.out.file.handle);
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_util_close failed");
+
+	torture_comment(tctx, "Checking find response with enriched macOS metadata\n");
+
+	for (i = 0; i < count; i++) {
+		const char *found = d[i].id_both_directory_info.name.s;
+
+		if (!strcmp(found, ".") || !strcmp(found, ".."))
+			continue;
+		break;
+	}
+
+	torture_assert_str_equal_goto(tctx,
+				      d[i].id_both_directory_info.name.s, name,
+				      ret, done, "bad name");
+
+	rfork_len = BVAL(d[i].id_both_directory_info.short_name_buf, 0);
+	torture_assert_int_equal_goto(tctx, rfork_len, 3, ret, done, "bad resource fork length");
+
+	torture_assert_mem_equal_goto(tctx, type_creator,
+				      d[i].id_both_directory_info.short_name_buf + 8,
+				      8, ret, done, "Bad FinderInfo");
+done:
+	smb2_util_unlink(tree, fname);
+	smb2_deltree(tree, BASEDIR);
+	talloc_free(mem_ctx);
+	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
@@ -3772,6 +3880,7 @@ struct torture_suite *torture_vfs_fruit(void)
 	torture_suite_add_1smb2_test(suite, "null afpinfo", test_null_afpinfo);
 	torture_suite_add_1smb2_test(suite, "delete", test_delete_file_with_rfork);
 	torture_suite_add_1smb2_test(suite, "read open rsrc after rename", test_rename_and_read_rsrc);
+	torture_suite_add_1smb2_test(suite, "readdir_attr with names with illegal ntfs characters", test_readdir_attr_illegal_ntfs);
 
 	return suite;
 }
-- 
2.9.3


From 5783c394b453a85b6b9b2400aa5647817b739bd0 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Thu, 8 Dec 2016 17:47:36 +0100
Subject: [PATCH 52/56] docs/vfs_fruit: document known limitations with
 fruit:encoding=native

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12427

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 docs-xml/manpages/vfs_fruit.8.xml | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/docs-xml/manpages/vfs_fruit.8.xml b/docs-xml/manpages/vfs_fruit.8.xml
index 6976d7d..31a4fe7 100644
--- a/docs-xml/manpages/vfs_fruit.8.xml
+++ b/docs-xml/manpages/vfs_fruit.8.xml
@@ -154,9 +154,13 @@
 
 	      <para>Controls how the set of illegal NTFS ASCII
 	      character, commonly used by OS X clients, are stored in
-	      the filesystem:</para>
+	      the filesystem.</para>
 
-	      <itemizedlist>
+	      <para><emphasis>Important:</emphasis> this is known to not fully
+	      work with <emphasis>fruit:metadata=stream</emphasis> or
+	      <emphasis>fruit:resource=stream</emphasis>.</para>
+
+ 	      <itemizedlist>
 
 		<listitem><para><command>private (default)</command> -
 		store characters as encoded by the OS X client: mapped
-- 
2.9.3


From 707f6e0f3560b52963c268dd87bea746b2e42554 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Sun, 11 Dec 2016 19:02:37 +0100
Subject: [PATCH 53/56] s4/torture: change shares in used
 torture_suite_add_2ns_smb2_test()

torture_suite_add_2ns_smb2_test wan't used, change it to use the default
share as share 1 and a second share taken from torture option
"torture:share2".

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12427

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 source4/torture/vfs/vfs.c | 36 +++++++++++++++++++-----------------
 1 file changed, 19 insertions(+), 17 deletions(-)

diff --git a/source4/torture/vfs/vfs.c b/source4/torture/vfs/vfs.c
index 8b443fb..a4f8125 100644
--- a/source4/torture/vfs/vfs.c
+++ b/source4/torture/vfs/vfs.c
@@ -38,42 +38,44 @@ static bool wrap_2ns_smb2_test(struct torture_context *torture_ctx,
 			       struct torture_test *test)
 {
 	bool (*fn) (struct torture_context *, struct smb2_tree *, struct smb2_tree *);
-	bool ret = false;
+	bool ok;
 
-	struct smb2_tree *tree1;
-	struct smb2_tree *tree2;
+	struct smb2_tree *tree1 = NULL;
+	struct smb2_tree *tree2 = NULL;
 	TALLOC_CTX *mem_ctx = talloc_new(torture_ctx);
 
-	if (!torture_smb2_con_sopt(torture_ctx, "share1", &tree1)) {
+	if (!torture_smb2_connection(torture_ctx, &tree1)) {
 		torture_fail(torture_ctx,
-		    "Establishing SMB2 connection failed\n");
-		goto done;
+			    "Establishing SMB2 connection failed\n");
+		return false;
 	}
 
+	/*
+	 * This is a trick:
+	 * The test might close the connection. If we steal the tree context
+	 * before that and free the parent instead of tree directly, we avoid
+	 * a double free error.
+	 */
 	talloc_steal(mem_ctx, tree1);
 
-	if (!torture_smb2_con_sopt(torture_ctx, "share2", &tree2)) {
-		torture_fail(torture_ctx,
-		    "Establishing SMB2 connection failed\n");
-		goto done;
+	ok = torture_smb2_con_sopt(torture_ctx, "share2", &tree2);
+	if (ok) {
+		talloc_steal(mem_ctx, tree2);
 	}
 
-	talloc_steal(mem_ctx, tree2);
-
 	fn = test->fn;
 
-	ret = fn(torture_ctx, tree1, tree2);
+	ok = fn(torture_ctx, tree1, tree2);
 
-done:
 	/* the test may already have closed some of the connections */
 	talloc_free(mem_ctx);
 
-	return ret;
+	return ok;
 }
 
 /*
- * Run a test with 2 connected trees, Share names to connect are taken
- * from option strings "torture:share1" and "torture:share2"
+ * Run a test with 2 connected trees, the default share and another
+ * taken from option strings "torture:share2"
  */
 struct torture_test *torture_suite_add_2ns_smb2_test(struct torture_suite *suite,
 						     const char *name,
-- 
2.9.3


From dd9854c63af240525537d61843cb6a27c6e59f1e Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Sun, 11 Dec 2016 19:06:46 +0100
Subject: [PATCH 54/56] selftest: add shares without vfs_fruit for the
 vfs_fruit tests

Not used for now, but the next commit will add a test that makes use of
this.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12427

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 selftest/target/Samba3.pm | 10 ++++++++++
 source3/selftest/tests.py |  6 +++---
 2 files changed, 13 insertions(+), 3 deletions(-)

diff --git a/selftest/target/Samba3.pm b/selftest/target/Samba3.pm
index 0b6fff8..aa50f07 100755
--- a/selftest/target/Samba3.pm
+++ b/selftest/target/Samba3.pm
@@ -1674,6 +1674,16 @@ sub provision($$$$$$$$)
 	fruit:resource = stream
 	fruit:metadata = stream
 
+[vfs_wo_fruit]
+	path = $shrdir
+	vfs objects = streams_xattr acl_xattr
+	ea support = yes
+
+[vfs_wo_fruit_stream_depot]
+	path = $shrdir
+	vfs objects = streams_depot acl_xattr
+	ea support = yes
+
 [badname-tmp]
 	path = $badnames_shrdir
 	guest ok = yes
diff --git a/source3/selftest/tests.py b/source3/selftest/tests.py
index e00ac85..06e0f32 100755
--- a/source3/selftest/tests.py
+++ b/source3/selftest/tests.py
@@ -417,9 +417,9 @@ for t in tests:
         plansmbtorture4testsuite(t, "nt4_dc", '//$SERVER_IP/tmpsort -U$USERNAME%$PASSWORD')
         plansmbtorture4testsuite(t, "ad_dc", '//$SERVER/tmp -U$USERNAME%$PASSWORD')
     elif t == "vfs.fruit":
-        plansmbtorture4testsuite(t, "nt4_dc", '//$SERVER_IP/vfs_fruit -U$USERNAME%$PASSWORD --option=torture:localdir=$SELFTEST_PREFIX/nt4_dc/share', 'metadata_netatalk')
-        plansmbtorture4testsuite(t, "nt4_dc", '//$SERVER_IP/vfs_fruit_metadata_stream -U$USERNAME%$PASSWORD', 'metadata_stream')
-        plansmbtorture4testsuite(t, "nt4_dc", '//$SERVER_IP/vfs_fruit_stream_depot -U$USERNAME%$PASSWORD', 'streams_depot')
+        plansmbtorture4testsuite(t, "nt4_dc", '//$SERVER_IP/vfs_fruit -U$USERNAME%$PASSWORD --option=torture:localdir=$SELFTEST_PREFIX/nt4_dc/share --option=torture:share2=vfs_wo_fruit', 'metadata_netatalk')
+        plansmbtorture4testsuite(t, "nt4_dc", '//$SERVER_IP/vfs_fruit_metadata_stream --option=torture:share2=vfs_wo_fruit -U$USERNAME%$PASSWORD', 'metadata_stream')
+        plansmbtorture4testsuite(t, "nt4_dc", '//$SERVER_IP/vfs_fruit_stream_depot --option=torture:share2=vfs_wo_fruit_stream_depot -U$USERNAME%$PASSWORD', 'streams_depot')
     elif t == "vfs.fruit_netatalk":
         plansmbtorture4testsuite(t, "nt4_dc", '//$SERVER_IP/vfs_fruit -U$USERNAME%$PASSWORD --option=torture:localdir=$SELFTEST_PREFIX/nt4_dc/share')
     elif t == "rpc.schannel_anon_setpw":
-- 
2.9.3


From c95e66e806e3ae77eaac2d54b83b6bd1219b8890 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Sun, 11 Dec 2016 19:10:05 +0100
Subject: [PATCH 55/56] vfs_fruit: ignore or delete invalid AFP_AfpInfo streams

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12427

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 source3/modules/vfs_fruit.c | 89 ++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 88 insertions(+), 1 deletion(-)

diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c
index b223f1a..3378006 100644
--- a/source3/modules/vfs_fruit.c
+++ b/source3/modules/vfs_fruit.c
@@ -3523,7 +3523,26 @@ static ssize_t fruit_pread_meta_stream(vfs_handle_struct *handle,
 				       files_struct *fsp, void *data,
 				       size_t n, off_t offset)
 {
-	return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
+	ssize_t nread;
+	int ret;
+
+	nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
+
+	if (nread == n) {
+		return nread;
+	}
+
+	DBG_ERR("Removing [%s] after short read [%zd]\n",
+		fsp_str_dbg(fsp), nread);
+
+	ret = SMB_VFS_NEXT_UNLINK(handle, fsp->fsp_name);
+	if (ret != 0) {
+		DBG_ERR("Removing [%s] failed\n", fsp_str_dbg(fsp));
+		return -1;
+	}
+
+	errno = EINVAL;
+	return -1;
 }
 
 static ssize_t fruit_pread_meta_adouble(vfs_handle_struct *handle,
@@ -4366,6 +4385,50 @@ static NTSTATUS fruit_streaminfo_meta_stream(
 	unsigned int *pnum_streams,
 	struct stream_struct **pstreams)
 {
+	struct stream_struct *stream = *pstreams;
+	unsigned int num_streams = *pnum_streams;
+	struct smb_filename *sname = NULL;
+	int i;
+	int ret;
+	bool ok;
+
+	for (i = 0; i < num_streams; i++) {
+		if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
+			break;
+		}
+	}
+
+	if (i == num_streams) {
+		return NT_STATUS_OK;
+	}
+
+	if (stream[i].size == AFP_INFO_SIZE) {
+		return NT_STATUS_OK;
+	}
+
+	DBG_ERR("Removing invalid AFPINFO_STREAM size [%zd] from [%s]\n",
+		stream[i].size, smb_fname_str_dbg(smb_fname));
+
+	ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams, AFPINFO_STREAM);
+	if (!ok) {
+		return NT_STATUS_INTERNAL_ERROR;
+	}
+
+	sname = synthetic_smb_fname(talloc_tos(),
+				    smb_fname->base_name,
+				    AFPINFO_STREAM_NAME,
+				    NULL, 0);
+	if (sname == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	ret = SMB_VFS_NEXT_UNLINK(handle, sname);
+	TALLOC_FREE(sname);
+	if (ret != 0) {
+		DBG_ERR("Removing [%s] failed\n", smb_fname_str_dbg(sname));
+		return map_nt_error_from_unix(errno);
+	}
+
 	return NT_STATUS_OK;
 }
 
@@ -4377,8 +4440,11 @@ static NTSTATUS fruit_streaminfo_meta_netatalk(
 	unsigned int *pnum_streams,
 	struct stream_struct **pstreams)
 {
+	struct stream_struct *stream = *pstreams;
+	unsigned int num_streams = *pnum_streams;
 	struct adouble *ad = NULL;
 	bool is_fi_empty;
+	int i;
 	bool ok;
 
 	/* Remove the Netatalk xattr from the list */
@@ -4388,6 +4454,27 @@ static NTSTATUS fruit_streaminfo_meta_netatalk(
 		return NT_STATUS_NO_MEMORY;
 	}
 
+	/*
+	 * Check if there's a AFPINFO_STREAM from the VFS streams
+	 * backend and if yes, remove it from the list
+	 */
+	for (i = 0; i < num_streams; i++) {
+		if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
+			break;
+		}
+	}
+
+	if (i < num_streams) {
+		DBG_WARNING("Unexpected AFPINFO_STREAM on [%s]\n",
+			    smb_fname_str_dbg(smb_fname));
+
+		ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
+				      AFPINFO_STREAM);
+		if (!ok) {
+			return NT_STATUS_INTERNAL_ERROR;
+		}
+	}
+
 	ad = ad_get(talloc_tos(), handle,
 		    smb_fname->base_name, ADOUBLE_META);
 	if (ad == NULL) {
-- 
2.9.3


From 60b3a0114e8006934401cf9ed83e2b406a6eca04 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Sun, 11 Dec 2016 19:11:09 +0100
Subject: [PATCH 56/56] s4/torture: vfs_fruit: test invalid AFPINFO_STREAM_NAME

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12427

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 source4/torture/vfs/fruit.c | 60 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 60 insertions(+)

diff --git a/source4/torture/vfs/fruit.c b/source4/torture/vfs/fruit.c
index 38f5059..92ef06a 100644
--- a/source4/torture/vfs/fruit.c
+++ b/source4/torture/vfs/fruit.c
@@ -3845,6 +3845,64 @@ done:
 	return ret;
 }
 
+static bool test_invalid_afpinfo(struct torture_context *tctx,
+				 struct smb2_tree *tree1,
+				 struct smb2_tree *tree2)
+{
+	const char *fname = "filtest_invalid_afpinfo";
+	const char *sname = "filtest_invalid_afpinfo" AFPINFO_STREAM_NAME;
+	struct smb2_create create;
+	const char *streams_basic[] = {
+		"::$DATA"
+	};
+	const char *streams_afpinfo[] = {
+		"::$DATA",
+		AFPINFO_STREAM
+	};
+	NTSTATUS status;
+	bool ret = true;
+
+	if (tree2 == NULL) {
+		torture_skip_goto(tctx, done, "need second share without fruit\n");
+	}
+
+	torture_comment(tctx, "Testing invalid AFP_AfpInfo stream\n");
+
+	ret = torture_setup_file(tctx, tree2, fname, false);
+	torture_assert_goto(tctx, ret == true, ret, done, "torture_setup_file");
+
+	ret = write_stream(tree2, __location__, tctx, tctx,
+			   fname, AFPINFO_STREAM_NAME,
+			   0, 3, "foo");
+	torture_assert_goto(tctx, ret == true, ret, done, "write_stream failed");
+
+	ret = check_stream_list(tree2, tctx, fname, 2, streams_afpinfo, false);
+	torture_assert_goto(tctx, ret == true, ret, done, "Bad streams");
+
+	torture_comment(tctx, "Listing streams, bad AFPINFO stream must not be present\n");
+
+	ret = check_stream_list(tree1, tctx, fname, 1, streams_basic, false);
+	torture_assert_goto(tctx, ret == true, ret, done, "Bad streams");
+
+	torture_comment(tctx, "Try to open AFPINFO stream, must fail\n");
+
+	ZERO_STRUCT(create);
+	create.in.desired_access = SEC_FILE_ALL;
+	create.in.share_access = NTCREATEX_SHARE_ACCESS_MASK;
+	create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
+	create.in.create_disposition = NTCREATEX_DISP_OPEN;
+	create.in.impersonation_level = SMB2_IMPERSONATION_IMPERSONATION;
+	create.in.fname = sname;
+
+	status = smb2_create(tree1, tctx, &create);
+	torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND,
+					   ret, done, "Stream still around?");
+
+done:
+	smb2_util_unlink(tree1, fname);
+	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
@@ -3882,6 +3940,8 @@ struct torture_suite *torture_vfs_fruit(void)
 	torture_suite_add_1smb2_test(suite, "read open rsrc after rename", test_rename_and_read_rsrc);
 	torture_suite_add_1smb2_test(suite, "readdir_attr with names with illegal ntfs characters", test_readdir_attr_illegal_ntfs);
 
+	torture_suite_add_2ns_smb2_test(suite, "invalid AFP_AfpInfo", test_invalid_afpinfo);
+
 	return suite;
 }
 
-- 
2.9.3



More information about the samba-technical mailing list