[PATCH] Fix streams_xattr (and fruit) with kernel oplocks

Ralph Böhme slow at samba.org
Thu Jul 27 20:08:28 UTC 2017


Hey Richard!

On Thu, Jul 27, 2017 at 12:09:36PM -0700, Richard Sharpe wrote:
> I have read through the patch and it looks OK, although I will need to
> read it carefully one more time before I can approve it.

thanks for taking the stab! :)

> However, did you consider O_PATH?

No, and I don't want to at this point. :) You'd have to proove that using a fd
returned from pipe() where the other end is close doesn't have the same
effect. Plus I *know* this one can't trigger oplock breaks in the kernel... ;)

> Since this seems to be Linux
> specific (kernel oplocks) that might have avoided the need for a pipe,
> although I will have to check the kernel code to see of an open with
> O_PATH will trigger a kernel oplock break. Any attempt to use an FD
> opened with O_PATH for anything serious will result in EBADF ...
> 
> Also, there seems to be some funny space vs tab issue here:
> 
> +static int streams_xattr_fchown(vfs_handle_struct *handle, files_struct *fsp,
> +                               uid_t uid, gid_t gid)
> +{
> +        struct stream_io *sio =
> +               (struct stream_io *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
> +
> +       if (sio == NULL) {
> +               return SMB_VFS_NEXT_FCHOWN(handle, fsp, uid, gid);
> +       }
> +
> +       return 0;
> +}
> +
> 
> and all the others in vfs_streams_xattr.c. The struct stream_io like
> seems to have spaces at the beginning while the rest have tabs.

oh, didn't notice, thanks! Looks like I inherited this when copying the lines
from the fallocate function directly above. :)

Updated patchset attached.

Thanks!
-slow
-------------- next part --------------
From 23cb561b9e2f5508963614c4d40ad9ae1b8cb35a Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Thu, 11 May 2017 07:59:20 +0200
Subject: [PATCH 01/12] vfs_streams_xattr: invalidate stat info if xattr was
 not found

We stat the basefile so we leave valid stat info from the base file
behind, even though the xattr for the stream was not there.

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

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

diff --git a/source3/modules/vfs_streams_xattr.c b/source3/modules/vfs_streams_xattr.c
index 5114c09..fc79935 100644
--- a/source3/modules/vfs_streams_xattr.c
+++ b/source3/modules/vfs_streams_xattr.c
@@ -269,6 +269,7 @@ static int streams_xattr_fstat(vfs_handle_struct *handle, files_struct *fsp,
 					smb_fname_base, io->xattr_name);
 	if (sbuf->st_ex_size == -1) {
 		TALLOC_FREE(smb_fname_base);
+		SET_STAT_INVALID(*sbuf);
 		return -1;
 	}
 
@@ -322,6 +323,7 @@ static int streams_xattr_stat(vfs_handle_struct *handle,
 						  smb_fname,
 						  xattr_name);
 	if (smb_fname->st.st_ex_size == -1) {
+		SET_STAT_INVALID(smb_fname->st);
 		errno = ENOENT;
 		result = -1;
 		goto fail;
@@ -373,6 +375,7 @@ static int streams_xattr_lstat(vfs_handle_struct *handle,
 						  smb_fname,
 						  xattr_name);
 	if (smb_fname->st.st_ex_size == -1) {
+		SET_STAT_INVALID(smb_fname->st);
 		errno = ENOENT;
 		result = -1;
 		goto fail;
-- 
2.9.4


From c7b7f5f53e3eecd0bd39bfbfebbe8db158eb7be5 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Thu, 11 May 2017 15:05:23 +0200
Subject: [PATCH 02/12] vfs_streams_xattr: remove all uses of fd, use name
 based functions

We don't really need an fd in this module, all calls to the VFS xattr
API can just use the name based versions.

This paves the way for removing the open of the basefile in
streams_xattr_open() in a later commit.

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

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

diff --git a/source3/modules/vfs_streams_xattr.c b/source3/modules/vfs_streams_xattr.c
index fc79935..11ee596 100644
--- a/source3/modules/vfs_streams_xattr.c
+++ b/source3/modules/vfs_streams_xattr.c
@@ -232,8 +232,6 @@ static int streams_xattr_fstat(vfs_handle_struct *handle, files_struct *fsp,
 	struct stream_io *io = (struct stream_io *)
 		VFS_FETCH_FSP_EXTENSION(handle, fsp);
 
-	DEBUG(10, ("streams_xattr_fstat called for %d\n", fsp->fh->fd));
-
 	if (io == NULL || fsp->base_fsp == NULL) {
 		return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
 	}
@@ -242,6 +240,8 @@ static int streams_xattr_fstat(vfs_handle_struct *handle, files_struct *fsp,
 		return -1;
 	}
 
+	DBG_DEBUG("streams_xattr_fstat called for %s\n", fsp_str_dbg(io->fsp));
+
 	/* Create an smb_filename with stream_name == NULL. */
 	smb_fname_base = synthetic_smb_fname(talloc_tos(),
 					io->base,
@@ -506,11 +506,11 @@ static int streams_xattr_open(vfs_handle_struct *handle,
 		DEBUG(10, ("creating or truncating attribute %s on file %s\n",
 			   xattr_name, smb_fname->base_name));
 
-		fsp->fh->fd = hostfd;
-		ret = SMB_VFS_FSETXATTR(fsp, xattr_name,
-					&null, sizeof(null),
-					flags & O_EXCL ? XATTR_CREATE : 0);
-		fsp->fh->fd = -1;
+		ret = SMB_VFS_SETXATTR(fsp->conn,
+				       smb_fname,
+				       xattr_name,
+				       &null, sizeof(null),
+				       flags & O_EXCL ? XATTR_CREATE : 0);
 		if (ret != 0) {
 			goto fail;
 		}
@@ -844,14 +844,7 @@ static NTSTATUS streams_xattr_streaminfo(vfs_handle_struct *handle,
 	NTSTATUS status;
 	struct streaminfo_state state;
 
-	if ((fsp != NULL) && (fsp->fh->fd != -1)) {
-		ret = SMB_VFS_FSTAT(fsp, &sbuf);
-	} else {
-		ret = vfs_stat_smb_basename(handle->conn,
-				smb_fname,
-				&sbuf);
-	}
-
+	ret = vfs_stat_smb_basename(handle->conn, smb_fname, &sbuf);
 	if (ret == -1) {
 		return map_nt_error_from_unix(errno);
 	}
@@ -1003,16 +996,10 @@ static ssize_t streams_xattr_pwrite(vfs_handle_struct *handle,
 
         memcpy(ea.value.data + offset, data, n);
 
-	if (fsp->fh->fd != -1) {
-		ret = SMB_VFS_FSETXATTR(fsp,
-				sio->xattr_name,
-				ea.value.data, ea.value.length, 0);
-	} else {
-		ret = SMB_VFS_SETXATTR(fsp->conn,
-				       fsp->fsp_name,
-				sio->xattr_name,
-				ea.value.data, ea.value.length, 0);
-	}
+	ret = SMB_VFS_SETXATTR(fsp->conn,
+			       fsp->fsp_name,
+			       sio->xattr_name,
+			       ea.value.data, ea.value.length, 0);
 	TALLOC_FREE(ea.value.data);
 
 	if (ret == -1) {
@@ -1297,17 +1284,10 @@ static int streams_xattr_ftruncate(struct vfs_handle_struct *handle,
 	ea.value.length = offset + 1;
 	ea.value.data[offset] = 0;
 
-	if (fsp->fh->fd != -1) {
-		ret = SMB_VFS_FSETXATTR(fsp,
-				sio->xattr_name,
-				ea.value.data, ea.value.length, 0);
-	} else {
-		ret = SMB_VFS_SETXATTR(fsp->conn,
-				fsp->fsp_name,
-				sio->xattr_name,
-				ea.value.data, ea.value.length, 0);
-	}
-
+	ret = SMB_VFS_SETXATTR(fsp->conn,
+			       fsp->fsp_name,
+			       sio->xattr_name,
+			       ea.value.data, ea.value.length, 0);
 	TALLOC_FREE(ea.value.data);
 
 	if (ret == -1) {
-- 
2.9.4


From c3c4345226f2aa7ebba25e05363622eec14fc439 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Thu, 11 May 2017 17:36:15 +0200
Subject: [PATCH 03/12] vfs_streams_xattr: remove fsp argument from
 get_xattr_size()

Still in the process of changing all handle based operations to use path
based operations.

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

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

diff --git a/source3/modules/vfs_streams_xattr.c b/source3/modules/vfs_streams_xattr.c
index 11ee596..42b37e8 100644
--- a/source3/modules/vfs_streams_xattr.c
+++ b/source3/modules/vfs_streams_xattr.c
@@ -78,7 +78,6 @@ static SMB_INO_T stream_inode(const SMB_STRUCT_STAT *sbuf, const char *sname)
 }
 
 static ssize_t get_xattr_size(connection_struct *conn,
-				files_struct *fsp,
 				const struct smb_filename *smb_fname,
 				const char *xattr_name)
 {
@@ -86,7 +85,7 @@ static ssize_t get_xattr_size(connection_struct *conn,
 	struct ea_struct ea;
 	ssize_t result;
 
-	status = get_ea_value(talloc_tos(), conn, fsp, smb_fname,
+	status = get_ea_value(talloc_tos(), conn, NULL, smb_fname,
 			      xattr_name, &ea);
 
 	if (!NT_STATUS_IS_OK(status)) {
@@ -265,7 +264,7 @@ static int streams_xattr_fstat(vfs_handle_struct *handle, files_struct *fsp,
 		return -1;
 	}
 
-	sbuf->st_ex_size = get_xattr_size(handle->conn, fsp,
+	sbuf->st_ex_size = get_xattr_size(handle->conn,
 					smb_fname_base, io->xattr_name);
 	if (sbuf->st_ex_size == -1) {
 		TALLOC_FREE(smb_fname_base);
@@ -319,7 +318,7 @@ static int streams_xattr_stat(vfs_handle_struct *handle,
 	}
 
 	/* Augment the base file's stat information before returning. */
-	smb_fname->st.st_ex_size = get_xattr_size(handle->conn, NULL,
+	smb_fname->st.st_ex_size = get_xattr_size(handle->conn,
 						  smb_fname,
 						  xattr_name);
 	if (smb_fname->st.st_ex_size == -1) {
@@ -371,7 +370,7 @@ static int streams_xattr_lstat(vfs_handle_struct *handle,
 	}
 
 	/* Augment the base file's stat information before returning. */
-	smb_fname->st.st_ex_size = get_xattr_size(handle->conn, NULL,
+	smb_fname->st.st_ex_size = get_xattr_size(handle->conn,
 						  smb_fname,
 						  xattr_name);
 	if (smb_fname->st.st_ex_size == -1) {
-- 
2.9.4


From 0b0d8f8cb4a62d904ee59100f3077c19387b52a7 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Thu, 11 May 2017 17:38:00 +0200
Subject: [PATCH 04/12] vfs_streams_xattr: always pass NULL as fsp arg to
 get_ea_value()

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

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

diff --git a/source3/modules/vfs_streams_xattr.c b/source3/modules/vfs_streams_xattr.c
index 42b37e8..d627c59 100644
--- a/source3/modules/vfs_streams_xattr.c
+++ b/source3/modules/vfs_streams_xattr.c
@@ -747,7 +747,7 @@ static NTSTATUS walk_xattr_streams(vfs_handle_struct *handle,
 
 		status = get_ea_value(names,
 					handle->conn,
-					fsp,
+					NULL,
 					smb_fname,
 					names[i],
 					&ea);
@@ -971,7 +971,7 @@ static ssize_t streams_xattr_pwrite(vfs_handle_struct *handle,
 		return -1;
 	}
 
-	status = get_ea_value(talloc_tos(), handle->conn, fsp,
+	status = get_ea_value(talloc_tos(), handle->conn, NULL,
 			      smb_fname_base, sio->xattr_name, &ea);
 	if (!NT_STATUS_IS_OK(status)) {
 		return -1;
@@ -1041,7 +1041,7 @@ static ssize_t streams_xattr_pread(vfs_handle_struct *handle,
 		return -1;
 	}
 
-	status = get_ea_value(talloc_tos(), handle->conn, fsp,
+	status = get_ea_value(talloc_tos(), handle->conn, NULL,
 			      smb_fname_base, sio->xattr_name, &ea);
 	if (!NT_STATUS_IS_OK(status)) {
 		return -1;
@@ -1258,7 +1258,7 @@ static int streams_xattr_ftruncate(struct vfs_handle_struct *handle,
 		return -1;
 	}
 
-	status = get_ea_value(talloc_tos(), handle->conn, fsp,
+	status = get_ea_value(talloc_tos(), handle->conn, NULL,
 			      smb_fname_base, sio->xattr_name, &ea);
 	if (!NT_STATUS_IS_OK(status)) {
 		return -1;
-- 
2.9.4


From c2723e70b8fbe35b88e71ecab1a947ca70b8d266 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Thu, 11 May 2017 18:05:18 +0200
Subject: [PATCH 05/12] vfs_streams_xattr: implement all missing handle based
 VFS functions

Implement all missing handle based VFS function. If the call is on a
named stream, implement the appropriate action for the VFS function, in
most cases a no-op.

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

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

diff --git a/source3/modules/vfs_streams_xattr.c b/source3/modules/vfs_streams_xattr.c
index d627c59..d71ac24 100644
--- a/source3/modules/vfs_streams_xattr.c
+++ b/source3/modules/vfs_streams_xattr.c
@@ -26,6 +26,7 @@
 #include "system/filesys.h"
 #include "../lib/crypto/md5.h"
 #include "lib/util/tevent_unix.h"
+#include "librpc/gen_ndr/ioctl.h"
 
 #undef DBGC_CLASS
 #define DBGC_CLASS DBGC_VFS
@@ -1322,6 +1323,396 @@ static int streams_xattr_fallocate(struct vfs_handle_struct *handle,
 	return -1;
 }
 
+static int streams_xattr_fchown(vfs_handle_struct *handle, files_struct *fsp,
+				uid_t uid, gid_t gid)
+{
+	struct stream_io *sio =
+		(struct stream_io *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
+
+	if (sio == NULL) {
+		return SMB_VFS_NEXT_FCHOWN(handle, fsp, uid, gid);
+	}
+
+	return 0;
+}
+
+static int streams_xattr_fchmod(vfs_handle_struct *handle,
+				files_struct *fsp,
+				mode_t mode)
+{
+	struct stream_io *sio =
+		(struct stream_io *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
+
+	if (sio == NULL) {
+		return SMB_VFS_NEXT_FCHMOD(handle, fsp, mode);
+	}
+
+	return 0;
+}
+
+static int streams_xattr_fsync(vfs_handle_struct *handle, files_struct *fsp)
+{
+	struct stream_io *sio =
+		(struct stream_io *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
+
+	if (sio == NULL) {
+		return SMB_VFS_NEXT_FSYNC(handle, fsp);
+	}
+
+	return 0;
+}
+
+static ssize_t streams_xattr_fgetxattr(struct vfs_handle_struct *handle,
+				       struct files_struct *fsp,
+				       const char *name,
+				       void *value,
+				       size_t size)
+{
+	struct stream_io *sio =
+		(struct stream_io *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
+
+	if (sio == NULL) {
+		return SMB_VFS_NEXT_FGETXATTR(handle, fsp, name, value, size);
+	}
+
+	errno = ENOTSUP;
+	return -1;
+}
+
+static ssize_t streams_xattr_flistxattr(struct vfs_handle_struct *handle,
+					struct files_struct *fsp,
+					char *list,
+					size_t size)
+{
+	struct stream_io *sio =
+		(struct stream_io *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
+
+	if (sio == NULL) {
+		return SMB_VFS_NEXT_FLISTXATTR(handle, fsp, list, size);
+	}
+
+	errno = ENOTSUP;
+	return -1;
+}
+
+static int streams_xattr_fremovexattr(struct vfs_handle_struct *handle,
+				      struct files_struct *fsp,
+				      const char *name)
+{
+	struct stream_io *sio =
+		(struct stream_io *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
+
+	if (sio == NULL) {
+		return SMB_VFS_NEXT_FREMOVEXATTR(handle, fsp, name);
+	}
+
+	errno = ENOTSUP;
+	return -1;
+}
+
+static int streams_xattr_fsetxattr(struct vfs_handle_struct *handle,
+				   struct files_struct *fsp,
+				   const char *name,
+				   const void *value,
+				   size_t size,
+				   int flags)
+{
+	struct stream_io *sio =
+		(struct stream_io *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
+
+	if (sio == NULL) {
+		return SMB_VFS_NEXT_FSETXATTR(handle, fsp, name, value,
+					      size, flags);
+	}
+
+	errno = ENOTSUP;
+	return -1;
+}
+
+static int streams_xattr_fchmod_acl(vfs_handle_struct *handle,
+				    files_struct *fsp,
+				    mode_t mode)
+{
+	struct stream_io *sio =
+		(struct stream_io *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
+
+	if (sio == NULL) {
+		return SMB_VFS_NEXT_FCHMOD_ACL(handle, fsp, mode);
+	}
+
+	return 0;
+}
+
+static SMB_ACL_T streams_xattr_sys_acl_get_fd(vfs_handle_struct *handle,
+					      files_struct *fsp,
+					      TALLOC_CTX *mem_ctx)
+{
+	struct stream_io *sio =
+		(struct stream_io *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
+
+	if (sio == NULL) {
+		return SMB_VFS_NEXT_SYS_ACL_GET_FD(handle, fsp, mem_ctx);
+	}
+
+	return SMB_VFS_NEXT_SYS_ACL_GET_FILE(
+		handle, fsp->base_fsp->fsp_name,
+		SMB_ACL_TYPE_ACCESS, mem_ctx);
+}
+
+static int streams_xattr_sys_acl_set_fd(vfs_handle_struct *handle,
+					files_struct *fsp,
+					SMB_ACL_T theacl)
+{
+	struct stream_io *sio =
+		(struct stream_io *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
+
+	if (sio == NULL) {
+		return SMB_VFS_NEXT_SYS_ACL_SET_FD(handle, fsp, theacl);
+	}
+
+	return 0;
+}
+
+static int streams_xattr_sys_acl_blob_get_fd(vfs_handle_struct *handle,
+					     files_struct *fsp,
+					     TALLOC_CTX *mem_ctx,
+					     char **blob_description,
+					     DATA_BLOB *blob)
+{
+	struct stream_io *sio =
+		(struct stream_io *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
+
+	if (sio == NULL) {
+		return SMB_VFS_NEXT_SYS_ACL_BLOB_GET_FD(handle, fsp, mem_ctx,
+							blob_description, blob);
+	}
+
+	return SMB_VFS_NEXT_SYS_ACL_BLOB_GET_FILE(
+		handle, fsp->fsp_name, mem_ctx,
+		blob_description, blob);
+}
+
+static NTSTATUS streams_xattr_fget_nt_acl(vfs_handle_struct *handle,
+					  files_struct *fsp,
+					  uint32_t security_info,
+					  TALLOC_CTX *mem_ctx,
+					  struct security_descriptor **ppdesc)
+{
+	struct stream_io *sio =
+		(struct stream_io *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
+
+	if (sio == NULL) {
+		return SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
+						mem_ctx, ppdesc);
+	}
+
+	return SMB_VFS_NEXT_GET_NT_ACL(handle, fsp->base_fsp->fsp_name,
+				       security_info, mem_ctx, ppdesc);
+}
+
+static NTSTATUS streams_xattr_fset_nt_acl(vfs_handle_struct *handle,
+					  files_struct *fsp,
+					  uint32_t security_info_sent,
+					  const struct security_descriptor *psd)
+{
+	struct stream_io *sio =
+		(struct stream_io *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
+
+	if (sio == NULL) {
+		return SMB_VFS_NEXT_FSET_NT_ACL(handle, fsp,
+						security_info_sent, psd);
+	}
+
+	return NT_STATUS_OK;
+}
+
+struct streams_xattr_fsync_state {
+	int ret;
+	struct vfs_aio_state vfs_aio_state;
+};
+
+static void streams_xattr_fsync_done(struct tevent_req *subreq);
+
+static struct tevent_req *streams_xattr_fsync_send(
+	struct vfs_handle_struct *handle,
+	TALLOC_CTX *mem_ctx,
+	struct tevent_context *ev,
+	struct files_struct *fsp)
+{
+	struct tevent_req *req = NULL;
+	struct tevent_req *subreq = NULL;
+	struct streams_xattr_fsync_state *state = NULL;
+	struct stream_io *sio =
+		(struct stream_io *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
+
+	req = tevent_req_create(mem_ctx, &state,
+				struct streams_xattr_fsync_state);
+	if (req == NULL) {
+		return NULL;
+	}
+
+	if (sio == NULL) {
+		subreq = SMB_VFS_NEXT_FSYNC_SEND(state, ev, handle, fsp);
+		if (tevent_req_nomem(req, subreq)) {
+			return tevent_req_post(req, ev);
+		}
+		tevent_req_set_callback(subreq, streams_xattr_fsync_done, req);
+		return req;
+	}
+
+	/*
+	 * There's no pathname based sync variant and we don't have access to
+	 * the basefile handle, so we can't do anything here.
+	 */
+
+	tevent_req_done(req);
+	return tevent_req_post(req, ev);
+}
+
+static void streams_xattr_fsync_done(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	struct streams_xattr_fsync_state *state = tevent_req_data(
+		req, struct streams_xattr_fsync_state);
+
+	state->ret = SMB_VFS_FSYNC_RECV(subreq, &state->vfs_aio_state);
+	TALLOC_FREE(subreq);
+	if (state->ret != 0) {
+		tevent_req_error(req, errno);
+		return;
+	}
+
+	tevent_req_done(req);
+}
+
+static int streams_xattr_fsync_recv(struct tevent_req *req,
+				    struct vfs_aio_state *vfs_aio_state)
+{
+	struct streams_xattr_fsync_state *state = tevent_req_data(
+		req, struct streams_xattr_fsync_state);
+
+	if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
+		return -1;
+	}
+
+	*vfs_aio_state = state->vfs_aio_state;
+	return state->ret;
+}
+
+static bool streams_xattr_lock(vfs_handle_struct *handle,
+			       files_struct *fsp,
+			       int op,
+			       off_t offset,
+			       off_t count,
+			       int type)
+{
+	struct stream_io *sio =
+		(struct stream_io *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
+
+	if (sio == NULL) {
+		return SMB_VFS_NEXT_LOCK(handle, fsp, op, offset, count, type);
+	}
+
+	return true;
+}
+
+static bool streams_xattr_getlock(vfs_handle_struct *handle,
+				  files_struct *fsp,
+				  off_t *poffset,
+				  off_t *pcount,
+				  int *ptype,
+				  pid_t *ppid)
+{
+	struct stream_io *sio =
+		(struct stream_io *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
+
+	if (sio == NULL) {
+		return SMB_VFS_NEXT_GETLOCK(handle, fsp, poffset,
+					    pcount, ptype, ppid);
+	}
+
+	errno = ENOTSUP;
+	return false;
+}
+
+static int streams_xattr_kernel_flock(vfs_handle_struct *handle,
+				      files_struct *fsp,
+				      uint32_t share_mode,
+				      uint32_t access_mask)
+{
+	struct stream_io *sio =
+		(struct stream_io *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
+
+	if (sio == NULL) {
+		return SMB_VFS_NEXT_KERNEL_FLOCK(handle, fsp,
+						 share_mode, access_mask);
+	}
+
+	return 0;
+}
+
+static int streams_xattr_linux_setlease(vfs_handle_struct *handle,
+					files_struct *fsp,
+					int leasetype)
+{
+	struct stream_io *sio =
+		(struct stream_io *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
+
+	if (sio == NULL) {
+		return SMB_VFS_NEXT_LINUX_SETLEASE(handle, fsp, leasetype);
+	}
+
+	return 0;
+}
+
+static bool streams_xattr_strict_lock_check(struct vfs_handle_struct *handle,
+					    files_struct *fsp,
+					    struct lock_struct *plock)
+{
+	struct stream_io *sio =
+		(struct stream_io *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
+
+	if (sio == NULL) {
+		return SMB_VFS_NEXT_STRICT_LOCK_CHECK(handle, fsp, plock);
+	}
+
+	return true;
+}
+
+static NTSTATUS streams_xattr_get_compression(struct vfs_handle_struct *handle,
+					      TALLOC_CTX *mem_ctx,
+					      struct files_struct *fsp,
+					      struct smb_filename *smb_fname,
+					      uint16_t *_compression_fmt)
+{
+	struct stream_io *sio =
+		(struct stream_io *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
+
+	if (sio == NULL) {
+		return SMB_VFS_NEXT_GET_COMPRESSION(handle, mem_ctx, fsp,
+						    smb_fname, _compression_fmt);
+	}
+
+	*_compression_fmt = COMPRESSION_FORMAT_NONE;
+	return NT_STATUS_OK;
+}
+
+static NTSTATUS streams_xattr_set_compression(struct vfs_handle_struct *handle,
+					      TALLOC_CTX *mem_ctx,
+					      struct files_struct *fsp,
+					      uint16_t compression_fmt)
+{
+	struct stream_io *sio =
+		(struct stream_io *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
+
+	if (sio == NULL) {
+		return SMB_VFS_NEXT_SET_COMPRESSION(handle, mem_ctx, fsp,
+						    compression_fmt);
+	}
+
+	return NT_STATUS_NOT_SUPPORTED;
+}
 
 static struct vfs_fn_pointers vfs_streams_xattr_fns = {
 	.fs_capabilities_fn = streams_xattr_fs_capabilities,
@@ -1341,6 +1732,36 @@ static struct vfs_fn_pointers vfs_streams_xattr_fns = {
 	.ftruncate_fn = streams_xattr_ftruncate,
 	.fallocate_fn = streams_xattr_fallocate,
 	.streaminfo_fn = streams_xattr_streaminfo,
+
+	.fsync_send_fn = streams_xattr_fsync_send,
+	.fsync_recv_fn = streams_xattr_fsync_recv,
+
+	.lock_fn = streams_xattr_lock,
+	.getlock_fn = streams_xattr_getlock,
+	.kernel_flock_fn = streams_xattr_kernel_flock,
+	.linux_setlease_fn = streams_xattr_linux_setlease,
+	.strict_lock_check_fn = streams_xattr_strict_lock_check,
+
+	.get_compression_fn = streams_xattr_get_compression,
+	.set_compression_fn = streams_xattr_set_compression,
+
+	.fchown_fn = streams_xattr_fchown,
+	.fchmod_fn = streams_xattr_fchmod,
+	.fsync_fn = streams_xattr_fsync,
+
+	.fgetxattr_fn = streams_xattr_fgetxattr,
+	.flistxattr_fn = streams_xattr_flistxattr,
+	.fremovexattr_fn = streams_xattr_fremovexattr,
+	.fsetxattr_fn = streams_xattr_fsetxattr,
+
+	.fchmod_acl_fn = streams_xattr_fchmod_acl,
+
+	.sys_acl_get_fd_fn = streams_xattr_sys_acl_get_fd,
+	.sys_acl_blob_get_fd_fn = streams_xattr_sys_acl_blob_get_fd,
+	.sys_acl_set_fd_fn = streams_xattr_sys_acl_set_fd,
+
+	.fget_nt_acl_fn = streams_xattr_fget_nt_acl,
+	.fset_nt_acl_fn = streams_xattr_fset_nt_acl,
 };
 
 NTSTATUS vfs_streams_xattr_init(TALLOC_CTX *);
-- 
2.9.4


From 5bc403bbce6b04b2bca0a694fb06dca181e0d073 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Thu, 11 May 2017 18:08:56 +0200
Subject: [PATCH 06/12] vfs_streams_xattr: return a fake fd in
 streams_xattr_open()

The final step in changing vfs_streams_xattr to not call open() on the
basefile anymore. Instead, we just return a fake file fd based on
dup'ing a pipe fd. Previous commits ensured all calls to VFS API
functions use pathname based versions to do their work.

This ensures we don't trigger kernel oplock breaks for client "open
stream" requests when needlessly opening the basefile.

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

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

diff --git a/source3/modules/vfs_streams_xattr.c b/source3/modules/vfs_streams_xattr.c
index d71ac24..fca39a4 100644
--- a/source3/modules/vfs_streams_xattr.c
+++ b/source3/modules/vfs_streams_xattr.c
@@ -399,14 +399,17 @@ static int streams_xattr_open(vfs_handle_struct *handle,
 			      files_struct *fsp, int flags, mode_t mode)
 {
 	NTSTATUS status;
-	struct smb_filename *smb_fname_base = NULL;
-	struct stream_io *sio;
+	struct streams_xattr_config *config = NULL;
+	struct stream_io *sio = NULL;
 	struct ea_struct ea;
 	char *xattr_name = NULL;
-	int baseflags;
-	int hostfd = -1;
+	int pipe_fds[2];
+	int fakefd = -1;
 	int ret;
 
+	SMB_VFS_HANDLE_GET_DATA(handle, config, struct streams_xattr_config,
+				return -1);
+
 	DEBUG(10, ("streams_xattr_open called for %s with flags 0x%x\n",
 		   smb_fname_str_dbg(smb_fname), flags));
 
@@ -435,44 +438,18 @@ static int streams_xattr_open(vfs_handle_struct *handle,
 		goto fail;
 	}
 
-	/* Create an smb_filename with stream_name == NULL. */
-	smb_fname_base = synthetic_smb_fname(talloc_tos(),
-				smb_fname->base_name,
-				NULL,
-				NULL,
-				smb_fname->flags);
-	if (smb_fname_base == NULL) {
-		errno = ENOMEM;
+	/*
+	 * Return a valid fd, but ensure any attempt to use it returns an error
+	 * (EPIPE).
+	 */
+	ret = pipe(&pipe_fds[0]);
+	if (ret != 0) {
 		goto fail;
 	}
 
-	/*
-	 * We use baseflags to turn off nasty side-effects when opening the
-	 * underlying file.
-         */
-        baseflags = flags;
-        baseflags &= ~O_TRUNC;
-        baseflags &= ~O_EXCL;
-        baseflags &= ~O_CREAT;
-
-        hostfd = SMB_VFS_NEXT_OPEN(handle, smb_fname_base, fsp,
-				   baseflags, mode);
-
-        /* 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_NEXT_OPEN(handle, smb_fname_base, fsp, baseflags,
-					   mode);
-        }
-
-	TALLOC_FREE(smb_fname_base);
-
-        if (hostfd == -1) {
-		goto fail;
-        }
+	close(pipe_fds[1]);
+	pipe_fds[1] = -1;
+	fakefd = pipe_fds[0];
 
 	status = get_ea_value(talloc_tos(), handle->conn, NULL,
 			      smb_fname, xattr_name, &ea);
@@ -544,16 +521,12 @@ static int streams_xattr_open(vfs_handle_struct *handle,
 		goto fail;
 	}
 
-	return hostfd;
+	return fakefd;
 
  fail:
-	if (hostfd >= 0) {
-		/*
-		 * BUGBUGBUG -- we would need to call fd_close_posix here, but
-		 * we don't have a full fsp yet
-		 */
-		fsp->fh->fd = hostfd;
-		SMB_VFS_NEXT_CLOSE(handle, fsp);
+	if (fakefd >= 0) {
+		close(fakefd);
+		fakefd = -1;
 	}
 
 	return -1;
-- 
2.9.4


From 7e29e6de3ed1190102b4839c3e9ea041b66df978 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Wed, 10 May 2017 11:38:06 +0200
Subject: [PATCH 07/12] s4/torture: reproducer for kernel oplocks issue with
 streams

test_smb2_kernel_oplocks3() wouldn't have failed without the patches,
I'm just adding it to have at least one test that tests with 2
clients. All other tests use just one client.

test_smb2_kernel_oplocks4() is the reproducer.

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

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

diff --git a/source4/torture/smb2/oplock.c b/source4/torture/smb2/oplock.c
index 858bdcf..7c59780 100644
--- a/source4/torture/smb2/oplock.c
+++ b/source4/torture/smb2/oplock.c
@@ -4332,6 +4332,165 @@ done:
 	return ret;
 }
 
+/**
+ * 1. 1st client opens file with oplock
+ * 2. 2nd client opens file
+ *
+ * Verify 2 triggers an oplock break
+ **/
+static bool test_smb2_kernel_oplocks3(struct torture_context *tctx,
+				      struct smb2_tree *tree,
+				      struct smb2_tree *tree2)
+{
+	const char *fname = "test_kernel_oplock3.dat";
+	NTSTATUS status;
+	bool ret = true;
+	struct smb2_create create;
+	struct smb2_handle h1 = {{0}}, h2 = {{0}};
+
+	smb2_util_unlink(tree, fname);
+	status = torture_smb2_testfile(tree, fname, &h1);
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+					"Error creating testfile\n");
+	smb2_util_close(tree, h1);
+	ZERO_STRUCT(h1);
+
+	tree->session->transport->oplock.handler = torture_oplock_handler;
+	tree->session->transport->oplock.private_data = tree;
+	ZERO_STRUCT(break_info);
+
+	/* 1 */
+	ZERO_STRUCT(create);
+	create.in.desired_access = SEC_RIGHTS_FILE_ALL;
+	create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
+	create.in.share_access = NTCREATEX_SHARE_ACCESS_MASK;
+	create.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
+	create.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
+	create.in.fname = fname;
+	create.in.oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE;
+
+	status = smb2_create(tree, tctx, &create);
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "Error opening the file\n");
+	h1 = create.out.file.handle;
+
+	torture_assert_goto(tctx, create.out.oplock_level == SMB2_OPLOCK_LEVEL_EXCLUSIVE, ret, done,
+			    "Oplock level is not SMB2_OPLOCK_LEVEL_EXCLUSIVE\n");
+
+	/* 2 */
+	ZERO_STRUCT(create);
+	create.in.desired_access = SEC_RIGHTS_FILE_READ;
+	create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
+	create.in.share_access = NTCREATEX_SHARE_ACCESS_MASK;
+	create.in.create_disposition = NTCREATEX_DISP_OPEN;
+	create.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
+	create.in.fname = fname;
+
+	status = smb2_create(tree2, tctx, &create);
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "Error opening the file\n");
+	h2 = create.out.file.handle;
+
+	torture_wait_for_oplock_break(tctx);
+	torture_assert_goto(tctx, break_info.count == 1, ret, done, "Expected 1 oplock break\n");
+
+done:
+	if (!smb2_util_handle_empty(h1)) {
+		smb2_util_close(tree, h1);
+	}
+	if (!smb2_util_handle_empty(h2)) {
+		smb2_util_close(tree, h2);
+	}
+	smb2_util_unlink(tree, fname);
+	return ret;
+}
+
+/**
+ * 1) create testfile with stream
+ * 2) open file r/w with batch oplock, sharing read/delete
+ * 3) open stream on file for reading
+ *
+ * Verify 3) doesn't trigger an oplock break
+ **/
+static bool test_smb2_kernel_oplocks4(struct torture_context *tctx,
+				      struct smb2_tree *tree)
+{
+	const char *fname = "test_kernel_oplock4.dat";
+	const char *sname = "test_kernel_oplock4.dat:foo";
+	NTSTATUS status;
+	bool ret = true;
+	struct smb2_create create;
+	struct smb2_handle h1 = {{0}}, h2 = {{0}};
+
+	tree->session->transport->oplock.handler = torture_oplock_handler;
+	tree->session->transport->oplock.private_data = tree;
+	ZERO_STRUCT(break_info);
+	smb2_util_unlink(tree, fname);
+
+	/* 1 */
+	status = torture_smb2_testfile(tree, fname, &h1);
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+					"Error creating testfile\n");
+	smb2_util_close(tree, h1);
+	ZERO_STRUCT(h1);
+
+	ZERO_STRUCT(create);
+	create.in.desired_access = SEC_RIGHTS_FILE_READ;
+	create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
+	create.in.share_access = NTCREATEX_SHARE_ACCESS_MASK;
+	create.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
+	create.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
+	create.in.fname = sname;
+
+	status = smb2_create(tree, tctx, &create);
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "Error opening the file\n");
+	h1 = create.out.file.handle;
+	smb2_util_close(tree, h1);
+	ZERO_STRUCT(h1);
+
+	/* 2 */
+	ZERO_STRUCT(create);
+	create.in.desired_access = SEC_RIGHTS_FILE_ALL;
+	create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
+	create.in.share_access = NTCREATEX_SHARE_ACCESS_READ|NTCREATEX_SHARE_ACCESS_DELETE;
+	create.in.create_disposition = NTCREATEX_DISP_OPEN;
+	create.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
+	create.in.fname = fname;
+	create.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH;
+
+	status = smb2_create(tree, tctx, &create);
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "Error opening the file\n");
+	h1 = create.out.file.handle;
+
+	torture_assert_goto(tctx, create.out.oplock_level == SMB2_OPLOCK_LEVEL_BATCH, ret, done,
+			    "Oplock level is not SMB2_OPLOCK_LEVEL_BATCH\n");
+
+	ZERO_STRUCT(create);
+	create.in.desired_access = SEC_RIGHTS_FILE_READ;
+	create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
+	create.in.share_access = NTCREATEX_SHARE_ACCESS_READ|NTCREATEX_SHARE_ACCESS_DELETE;
+	create.in.create_disposition = NTCREATEX_DISP_OPEN;
+	create.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
+	create.in.fname = sname;
+
+	status = smb2_create(tree, tctx, &create);
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "Error opening the file\n");
+	h2 = create.out.file.handle;
+
+	torture_wait_for_oplock_break(tctx);
+	if (break_info.count != 0) {
+		torture_warning(tctx, "Stream open caused oplock break\n");
+	}
+
+done:
+	if (!smb2_util_handle_empty(h1)) {
+		smb2_util_close(tree, h1);
+	}
+	if (!smb2_util_handle_empty(h2)) {
+		smb2_util_close(tree, h2);
+	}
+	smb2_util_unlink(tree, fname);
+	return ret;
+}
+
 struct torture_suite *torture_smb2_kernel_oplocks_init(TALLOC_CTX *ctx)
 {
 	struct torture_suite *suite =
@@ -4339,6 +4498,8 @@ struct torture_suite *torture_smb2_kernel_oplocks_init(TALLOC_CTX *ctx)
 
 	torture_suite_add_1smb2_test(suite, "kernel_oplocks1", test_smb2_kernel_oplocks1);
 	torture_suite_add_1smb2_test(suite, "kernel_oplocks2", test_smb2_kernel_oplocks2);
+	torture_suite_add_2smb2_test(suite, "kernel_oplocks3", test_smb2_kernel_oplocks3);
+	torture_suite_add_1smb2_test(suite, "kernel_oplocks4", test_smb2_kernel_oplocks4);
 
 	suite->description = talloc_strdup(suite, "SMB2-KERNEL-OPLOCK tests");
 
-- 
2.9.4


From dcd0f90706be86ab71d7b85988fdb4dc4c5fb9ee Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Thu, 18 May 2017 13:17:38 +0200
Subject: [PATCH 08/12] s4/torture: additional tests for kernel-oplocks

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

diff --git a/source4/torture/smb2/oplock.c b/source4/torture/smb2/oplock.c
index 7c59780..e0db5ec 100644
--- a/source4/torture/smb2/oplock.c
+++ b/source4/torture/smb2/oplock.c
@@ -4491,6 +4491,189 @@ done:
 	return ret;
 }
 
+/**
+ * 1) create testfile with stream
+ * 2) open stream r/w with batch oplock -> batch oplock granted
+ * 3) open stream r/o with batch oplock
+ *
+ * Verify 3) does trigger an oplock break
+ **/
+static bool test_smb2_kernel_oplocks5(struct torture_context *tctx,
+				      struct smb2_tree *tree)
+{
+	const char *fname = "test_kernel_oplock4.dat";
+	const char *sname = "test_kernel_oplock4.dat:foo";
+	NTSTATUS status;
+	bool ret = true;
+	struct smb2_create create;
+	struct smb2_handle h1 = {{0}}, h2 = {{0}};
+
+	tree->session->transport->oplock.handler = torture_oplock_handler;
+	tree->session->transport->oplock.private_data = tree;
+	ZERO_STRUCT(break_info);
+	smb2_util_unlink(tree, fname);
+
+	/* 1 */
+	status = torture_smb2_testfile(tree, fname, &h1);
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+					"Error creating testfile\n");
+	smb2_util_close(tree, h1);
+	ZERO_STRUCT(h1);
+
+	ZERO_STRUCT(create);
+	create.in.desired_access = SEC_RIGHTS_FILE_READ;
+	create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
+	create.in.share_access = NTCREATEX_SHARE_ACCESS_MASK;
+	create.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
+	create.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
+	create.in.fname = sname;
+
+	status = smb2_create(tree, tctx, &create);
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "Error opening the file\n");
+	h1 = create.out.file.handle;
+	smb2_util_close(tree, h1);
+	ZERO_STRUCT(h1);
+
+	/* 2 */
+	ZERO_STRUCT(create);
+	create.in.desired_access = SEC_RIGHTS_FILE_ALL;
+	create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
+	create.in.share_access = NTCREATEX_SHARE_ACCESS_MASK;
+	create.in.create_disposition = NTCREATEX_DISP_OPEN;
+	create.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
+	create.in.fname = sname;
+	create.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH;
+
+	status = smb2_create(tree, tctx, &create);
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "Error opening the file\n");
+	h1 = create.out.file.handle;
+
+	torture_assert_goto(tctx, create.out.oplock_level == SMB2_OPLOCK_LEVEL_BATCH, ret, done,
+			    "Oplock level is not SMB2_OPLOCK_LEVEL_BATCH\n");
+
+	ZERO_STRUCT(create);
+	create.in.desired_access = SEC_RIGHTS_FILE_READ;
+	create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
+	create.in.share_access = NTCREATEX_SHARE_ACCESS_MASK;
+	create.in.create_disposition = NTCREATEX_DISP_OPEN;
+	create.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
+	create.in.fname = sname;
+	create.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH;
+
+	status = smb2_create(tree, tctx, &create);
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "Error opening the file\n");
+	h2 = create.out.file.handle;
+
+	torture_assert_goto(tctx, create.out.oplock_level == SMB2_OPLOCK_LEVEL_NONE, ret, done,
+			    "Oplock level is not SMB2_OPLOCK_LEVEL_NONE\n");
+
+	torture_wait_for_oplock_break(tctx);
+	if (break_info.count != 1) {
+		torture_warning(tctx, "Stream open didn't cause oplock break\n");
+	}
+
+done:
+	if (!smb2_util_handle_empty(h1)) {
+		smb2_util_close(tree, h1);
+	}
+	if (!smb2_util_handle_empty(h2)) {
+		smb2_util_close(tree, h2);
+	}
+	smb2_util_unlink(tree, fname);
+	return ret;
+}
+
+/**
+ * 1) create testfile with stream
+ * 2) 1st client opens stream r/w with batch oplock -> batch oplock granted
+ * 3) 2nd client opens stream r/o with batch oplock
+ *
+ * Verify 3) does trigger an oplock break
+ **/
+static bool test_smb2_kernel_oplocks6(struct torture_context *tctx,
+				      struct smb2_tree *tree,
+				      struct smb2_tree *tree2)
+{
+	const char *fname = "test_kernel_oplock6.dat";
+	const char *sname = "test_kernel_oplock6.dat:foo";
+	NTSTATUS status;
+	bool ret = true;
+	struct smb2_create create;
+	struct smb2_handle h1 = {{0}}, h2 = {{0}};
+
+	smb2_util_unlink(tree, fname);
+	status = torture_smb2_testfile(tree, fname, &h1);
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+					"Error creating testfile\n");
+	smb2_util_close(tree, h1);
+	ZERO_STRUCT(h1);
+
+	tree->session->transport->oplock.handler = torture_oplock_handler;
+	tree->session->transport->oplock.private_data = tree;
+	ZERO_STRUCT(break_info);
+
+	/* 1 */
+	ZERO_STRUCT(create);
+	create.in.desired_access = SEC_RIGHTS_FILE_READ;
+	create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
+	create.in.share_access = NTCREATEX_SHARE_ACCESS_MASK;
+	create.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
+	create.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
+	create.in.fname = sname;
+
+	status = smb2_create(tree, tctx, &create);
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "Error opening the file\n");
+	h1 = create.out.file.handle;
+	smb2_util_close(tree, h1);
+	ZERO_STRUCT(h1);
+
+	/* 2 */
+	ZERO_STRUCT(create);
+	create.in.desired_access = SEC_RIGHTS_FILE_ALL;
+	create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
+	create.in.share_access = NTCREATEX_SHARE_ACCESS_MASK;
+	create.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
+	create.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
+	create.in.fname = fname;
+	create.in.oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE;
+
+	status = smb2_create(tree, tctx, &create);
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "Error opening the file\n");
+	h1 = create.out.file.handle;
+
+	torture_assert_goto(tctx, create.out.oplock_level == SMB2_OPLOCK_LEVEL_EXCLUSIVE, ret, done,
+			    "Oplock level is not SMB2_OPLOCK_LEVEL_EXCLUSIVE\n");
+
+	/* 3 */
+	ZERO_STRUCT(create);
+	create.in.desired_access = SEC_RIGHTS_FILE_READ;
+	create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
+	create.in.share_access = NTCREATEX_SHARE_ACCESS_MASK;
+	create.in.create_disposition = NTCREATEX_DISP_OPEN;
+	create.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
+	create.in.fname = fname;
+
+	status = smb2_create(tree2, tctx, &create);
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "Error opening the file\n");
+	h2 = create.out.file.handle;
+
+	torture_assert_goto(tctx, create.out.oplock_level == SMB2_OPLOCK_LEVEL_NONE, ret, done,
+			    "Oplock level is not SMB2_OPLOCK_LEVEL_NONE\n");
+
+	torture_wait_for_oplock_break(tctx);
+	torture_assert_goto(tctx, break_info.count == 1, ret, done, "Expected 1 oplock break\n");
+
+done:
+	if (!smb2_util_handle_empty(h1)) {
+		smb2_util_close(tree, h1);
+	}
+	if (!smb2_util_handle_empty(h2)) {
+		smb2_util_close(tree, h2);
+	}
+	smb2_util_unlink(tree, fname);
+	return ret;
+}
+
 struct torture_suite *torture_smb2_kernel_oplocks_init(TALLOC_CTX *ctx)
 {
 	struct torture_suite *suite =
@@ -4500,6 +4683,8 @@ struct torture_suite *torture_smb2_kernel_oplocks_init(TALLOC_CTX *ctx)
 	torture_suite_add_1smb2_test(suite, "kernel_oplocks2", test_smb2_kernel_oplocks2);
 	torture_suite_add_2smb2_test(suite, "kernel_oplocks3", test_smb2_kernel_oplocks3);
 	torture_suite_add_1smb2_test(suite, "kernel_oplocks4", test_smb2_kernel_oplocks4);
+	torture_suite_add_1smb2_test(suite, "kernel_oplocks5", test_smb2_kernel_oplocks5);
+	torture_suite_add_2smb2_test(suite, "kernel_oplocks6", test_smb2_kernel_oplocks6);
 
 	suite->description = talloc_strdup(suite, "SMB2-KERNEL-OPLOCK tests");
 
-- 
2.9.4


From e5706e3d3e60f130ab0a4ed101098ef1a7af1cb4 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Tue, 23 May 2017 17:39:46 +0200
Subject: [PATCH 09/12] vfs_fruit: use path based setxattr call in ad_fset()

This allows later commits to remove opening of the basefile which
conflict with "kernel oplocks = yes".

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

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

diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c
index 7c481cd..e15af83 100644
--- a/source3/modules/vfs_fruit.c
+++ b/source3/modules/vfs_fruit.c
@@ -1461,11 +1461,11 @@ static int ad_fset(struct adouble *ad, files_struct *fsp)
 
 	switch (ad->ad_type) {
 	case ADOUBLE_META:
-		rc = SMB_VFS_NEXT_FSETXATTR(ad->ad_handle,
-					    fsp,
-					    AFPINFO_EA_NETATALK,
-					    ad->ad_data,
-					    AD_DATASZ_XATTR, 0);
+		rc = SMB_VFS_NEXT_SETXATTR(ad->ad_handle,
+					   fsp->fsp_name,
+					   AFPINFO_EA_NETATALK,
+					   ad->ad_data,
+					   AD_DATASZ_XATTR, 0);
 		break;
 
 	case ADOUBLE_RSRC:
-- 
2.9.4


From d05feb15a95b7a33db252f5dd2959dfdeac237af Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Tue, 23 May 2017 17:31:47 +0200
Subject: [PATCH 10/12] vfs_fruit: don't open basefile in ad_open() and
 simplify API

We never need an fd on the basefile when operating on the metadata, as
we can always use path based syscalls. Opening the basefile conflicts
with "kernel oplocks" so just don't do it.

Additional changes:

- remove the adouble_type_t argument to ad_open(), the type is passed
  and set when allocating a struct adouble with ad_alloc()

- additionally pass an optional fsp to ad_open() (so the caller can pass
  NULL). With this change we can move the fd inheritance from fsp to ad
  into ad_open() itself where it belongs and remove it from the caller
  ad_fget()

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

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

diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c
index e15af83..1f2d489 100644
--- a/source3/modules/vfs_fruit.c
+++ b/source3/modules/vfs_fruit.c
@@ -863,13 +863,6 @@ exit:
 	return ealen;
 }
 
-static int ad_open_meta(const struct smb_filename *smb_fname,
-			int flags,
-			mode_t mode)
-{
-	return open(smb_fname->base_name, flags, mode);
-}
-
 static int ad_open_rsrc_xattr(const struct smb_filename *smb_fname,
 				int flags,
 				mode_t mode)
@@ -923,34 +916,45 @@ static int ad_open_rsrc(vfs_handle_struct *handle,
 	return fd;
 }
 
+/*
+ * 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.
+ */
 static int ad_open(vfs_handle_struct *handle,
 		   struct adouble *ad,
+		   files_struct *fsp,
 		   const struct smb_filename *smb_fname,
-		   adouble_type_t t,
 		   int flags,
 		   mode_t mode)
 {
 	int fd;
 
-	DBG_DEBUG("Path [%s] type [%s]\n",
-		  smb_fname->base_name, t == ADOUBLE_META ? "meta" : "rsrc");
+	DBG_DEBUG("Path [%s] type [%s]\n", smb_fname->base_name,
+		  ad->ad_type == ADOUBLE_META ? "meta" : "rsrc");
 
-	if (t == ADOUBLE_META) {
-		fd = ad_open_meta(smb_fname, flags, mode);
-	} else {
-		fd = ad_open_rsrc(handle, smb_fname, flags, mode);
+	if (ad->ad_type == ADOUBLE_META) {
+		return 0;
 	}
 
-	if (fd != -1) {
-		ad->ad_opened = true;
-		ad->ad_fd = fd;
+	if ((fsp != NULL) && (fsp->fh != NULL) && (fsp->fh->fd != -1)) {
+		ad->ad_fd = fsp->fh->fd;
+		ad->ad_opened = false;
+		return 0;
+	}
+
+	fd = ad_open_rsrc(handle, smb_fname, flags, mode);
+	if (fd == -1) {
+		return -1;
 	}
+	ad->ad_opened = true;
+	ad->ad_fd = fd;
 
 	DBG_DEBUG("Path [%s] type [%s] fd [%d]\n",
 		  smb_fname->base_name,
-		  t == ADOUBLE_META ? "meta" : "rsrc", fd);
+		  ad->ad_type == ADOUBLE_META ? "meta" : "rsrc", fd);
 
-	return fd;
+	return 0;
 }
 
 static ssize_t ad_read_rsrc_xattr(struct adouble *ad)
@@ -1254,7 +1258,6 @@ 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",
@@ -1267,29 +1270,20 @@ static struct adouble *ad_get(TALLOC_CTX *ctx, vfs_handle_struct *handle,
 		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, smb_fname, ADOUBLE_RSRC, mode, 0);
-		if (fd == -1 && ((errno == EROFS) || (errno == EACCES))) {
-			mode = O_RDONLY;
-			fd = ad_open(handle, ad, smb_fname,
-					ADOUBLE_RSRC, mode, 0);
-		}
+	/* Try rw first so we can use the fd in ad_convert() */
+	mode = O_RDWR;
+
+	rc = ad_open(handle, ad, NULL, smb_fname, mode, 0);
+	if (rc == -1 && ((errno == EROFS) || (errno == EACCES))) {
+		mode = O_RDONLY;
+		rc = ad_open(handle, ad, NULL, smb_fname, mode, 0);
+	}
+
+	if (rc == -1) {
+		DBG_DEBUG("ad_open [%s] error [%s]\n",
+			  smb_fname->base_name, strerror(errno));
+		goto exit;
 
-		if (fd == -1) {
-			DBG_DEBUG("ad_open [%s] error [%s]\n",
-				  smb_fname->base_name, strerror(errno));
-			rc = -1;
-			goto exit;
-		}
 	}
 
 	len = ad_read(ad, smb_fname);
@@ -1327,6 +1321,7 @@ static struct adouble *ad_fget(TALLOC_CTX *ctx, vfs_handle_struct *handle,
 	int rc = 0;
 	ssize_t len;
 	struct adouble *ad = NULL;
+	int mode;
 
 	DBG_DEBUG("ad_get(%s) path [%s]\n",
 		  type == ADOUBLE_META ? "meta" : "rsrc",
@@ -1338,40 +1333,17 @@ static struct adouble *ad_fget(TALLOC_CTX *ctx, vfs_handle_struct *handle,
 		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, fsp->base_fsp->fsp_name,
-					ADOUBLE_RSRC, mode, 0);
-			if (fd == -1 &&
-			    ((errno == EROFS) || (errno == EACCES)))
-			{
-				mode = O_RDONLY;
-				fd = ad_open(handle, ad,
-					fsp->base_fsp->fsp_name, ADOUBLE_RSRC,
-					mode, 0);
-			}
+	/* Try rw first so we can use the fd in ad_convert() */
+	mode = O_RDWR;
 
-			if (fd == -1) {
-				DBG_DEBUG("error opening AppleDouble for %s\n",
-					fsp_str_dbg(fsp));
-				rc = -1;
-				goto exit;
-			}
-		}
+	rc = ad_open(handle, ad, fsp, fsp->base_fsp->fsp_name, mode, 0);
+	if (rc == -1 && ((errno == EROFS) || (errno == EACCES))) {
+		mode = O_RDONLY;
+		rc = ad_open(handle, ad, fsp, fsp->base_fsp->fsp_name, mode, 0);
+	}
+	if (rc == -1) {
+		DBG_DEBUG("error opening AppleDouble [%s]\n", fsp_str_dbg(fsp));
+		goto exit;
 	}
 
 	len = ad_read(ad, fsp->base_fsp->fsp_name);
-- 
2.9.4


From 5b6080426c5dcc89f99722e211640e8b07eb626d Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Tue, 23 May 2017 17:44:16 +0200
Subject: [PATCH 11/12] vfs_fruit: return fake pipe fd in
 fruit_open_meta_netatalk()

Do not open the basefile, that conflict with "kernel oplocks = yes". We
just return a fake file fd based on dup'ing a pipe fd and ensure all VFS
functions that go through vfs_fruit and work on the metadata stream can
deal with it.

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

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

diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c
index 1f2d489..cdf8120 100644
--- a/source3/modules/vfs_fruit.c
+++ b/source3/modules/vfs_fruit.c
@@ -2739,56 +2739,24 @@ static int fruit_open_meta_netatalk(vfs_handle_struct *handle,
 				    int flags,
 				    mode_t mode)
 {
-	int rc = 0;
-	struct smb_filename *smb_fname_base = NULL;
-	int baseflags;
-	int hostfd = -1;
+	int rc;
+	int fakefd = -1;
 	struct adouble *ad = NULL;
+	int fds[2];
 
 	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,
-					NULL,
-					NULL,
-					smb_fname->flags);
-
-	if (smb_fname_base == NULL) {
-		errno = ENOMEM;
-		rc = -1;
-		goto exit;
-	}
-
 	/*
-	 * We use baseflags to turn off nasty side-effects when opening the
-	 * underlying file.
+	 * Return a valid fd, but ensure any attempt to use it returns an error
+	 * (EPIPE). All operations on the smb_fname or the fsp will use path
+	 * based syscalls.
 	 */
-	baseflags = flags;
-	baseflags &= ~O_TRUNC;
-	baseflags &= ~O_EXCL;
-	baseflags &= ~O_CREAT;
-
-	hostfd = SMB_VFS_NEXT_OPEN(handle, smb_fname_base, fsp,
-				   baseflags, mode);
-
-	/*
-	 * 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_NEXT_OPEN(handle, smb_fname_base, fsp,
-					   baseflags, mode);
-	}
-
-	TALLOC_FREE(smb_fname_base);
-
-	if (hostfd == -1) {
-		rc = -1;
+	rc = pipe(&fds[0]);
+	if (rc != 0) {
 		goto exit;
 	}
+	fakefd = fds[0];
+	close(fds[1]);
 
 	if (flags & (O_CREAT | O_TRUNC)) {
 		/*
@@ -2801,10 +2769,7 @@ static int fruit_open_meta_netatalk(vfs_handle_struct *handle,
 			goto exit;
 		}
 
-		fsp->fh->fd = hostfd;
-
-		rc = ad_fset(ad, fsp);
-		fsp->fh->fd = -1;
+		rc = ad_set(ad, fsp->fsp_name);
 		if (rc != 0) {
 			rc = -1;
 			goto exit;
@@ -2814,22 +2779,16 @@ static int fruit_open_meta_netatalk(vfs_handle_struct *handle,
 	}
 
 exit:
-	DEBUG(10, ("fruit_open meta rc=%d, fd=%d\n", rc, hostfd));
+	DEBUG(10, ("fruit_open meta rc=%d, fd=%d\n", rc, fakefd));
 	if (rc != 0) {
 		int saved_errno = errno;
-		if (hostfd >= 0) {
-			/*
-			 * BUGBUGBUG -- we would need to call
-			 * fd_close_posix here, but we don't have a
-			 * full fsp yet
-			 */
-			fsp->fh->fd = hostfd;
-			SMB_VFS_NEXT_CLOSE(handle, fsp);
+		if (fakefd >= 0) {
+			close(fakefd);
 		}
-		hostfd = -1;
+		fakefd = -1;
 		errno = saved_errno;
 	}
-	return hostfd;
+	return fakefd;
 }
 
 static int fruit_open_meta(vfs_handle_struct *handle,
-- 
2.9.4


From 47bb4cb63e6468b17ab0aabf5266ed55a98f63e2 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Wed, 24 May 2017 09:17:19 +0200
Subject: [PATCH 12/12] vfs_fruit: factor out common code from ad_get() and
 ad_fget()

As a result of the previous changes ad_get() and ad_fget() do completey
the same, so factor out the common code to a new helper function. No
change in behaviour.

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

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

diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c
index cdf8120..47e4b20 100644
--- a/source3/modules/vfs_fruit.c
+++ b/source3/modules/vfs_fruit.c
@@ -1241,25 +1241,21 @@ static struct adouble *ad_init(TALLOC_CTX *ctx, vfs_handle_struct *handle,
 	return ad;
 }
 
-/**
- * Return AppleDouble data for a file
- *
- * @param[in] ctx      talloc context
- * @param[in] handle   vfs handle
- * @param[in] smb_fname     pathname to file or directory
- * @param[in] type     type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
- *
- * @return             talloced struct adouble or NULL on error
- **/
-static struct adouble *ad_get(TALLOC_CTX *ctx, vfs_handle_struct *handle,
-			const struct smb_filename *smb_fname,
-			adouble_type_t type)
+static struct adouble *ad_get_internal(TALLOC_CTX *ctx,
+				       vfs_handle_struct *handle,
+				       files_struct *fsp,
+				       const struct smb_filename *smb_fname,
+				       adouble_type_t type)
 {
 	int rc = 0;
 	ssize_t len;
 	struct adouble *ad = NULL;
 	int mode;
 
+	if (fsp != NULL) {
+		smb_fname = fsp->base_fsp->fsp_name;
+	}
+
 	DEBUG(10, ("ad_get(%s) called for %s\n",
 		   type == ADOUBLE_META ? "meta" : "rsrc",
 		   smb_fname->base_name));
@@ -1273,12 +1269,11 @@ static struct adouble *ad_get(TALLOC_CTX *ctx, vfs_handle_struct *handle,
 	/* Try rw first so we can use the fd in ad_convert() */
 	mode = O_RDWR;
 
-	rc = ad_open(handle, ad, NULL, smb_fname, mode, 0);
+	rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
 	if (rc == -1 && ((errno == EROFS) || (errno == EACCES))) {
 		mode = O_RDONLY;
-		rc = ad_open(handle, ad, NULL, smb_fname, mode, 0);
+		rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
 	}
-
 	if (rc == -1) {
 		DBG_DEBUG("ad_open [%s] error [%s]\n",
 			  smb_fname->base_name, strerror(errno));
@@ -1310,6 +1305,24 @@ exit:
  *
  * @param[in] ctx      talloc context
  * @param[in] handle   vfs handle
+ * @param[in] smb_fname pathname to file or directory
+ * @param[in] type     type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
+ *
+ * @return             talloced struct adouble or NULL on error
+ **/
+static struct adouble *ad_get(TALLOC_CTX *ctx,
+			      vfs_handle_struct *handle,
+			      const struct smb_filename *smb_fname,
+			      adouble_type_t type)
+{
+	return ad_get_internal(ctx, handle, NULL, smb_fname, type);
+}
+
+/**
+ * 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
  *
@@ -1318,51 +1331,7 @@ exit:
 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;
-	int mode;
-
-	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;
-	}
-
-	/* Try rw first so we can use the fd in ad_convert() */
-	mode = O_RDWR;
-
-	rc = ad_open(handle, ad, fsp, fsp->base_fsp->fsp_name, mode, 0);
-	if (rc == -1 && ((errno == EROFS) || (errno == EACCES))) {
-		mode = O_RDONLY;
-		rc = ad_open(handle, ad, fsp, fsp->base_fsp->fsp_name, mode, 0);
-	}
-	if (rc == -1) {
-		DBG_DEBUG("error opening AppleDouble [%s]\n", fsp_str_dbg(fsp));
-		goto exit;
-	}
-
-	len = ad_read(ad, fsp->base_fsp->fsp_name);
-	if (len == -1) {
-		DBG_DEBUG("error reading AppleDouble for %s\n",
-			fsp_str_dbg(fsp));
-		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;
+	return ad_get_internal(ctx, handle, fsp, NULL, type);
 }
 
 /**
-- 
2.9.4



More information about the samba-technical mailing list