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

Ralph Böhme slow at samba.org
Fri Jul 28 06:04:12 UTC 2017


Hi

On Thu, Jul 27, 2017 at 01:38:22PM -0700, Richard Sharpe wrote:
> Reviewing the second patch set.
> 
> First patch: RB+
> Second patch:
> 
> I have one cosmetic suggestion with respect to this:
> 
> --- 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,
> 
> I agree with what you are doing, but I suggest you move the new
> DBG_DEBUG to the original position and use fsp->fsp_name. This is
> because there are two early exits from that function and if they are
> taken we will have no idea that this function was called.

Good point. Fixed in the attached patchset.

> Fifth patch. I have a question. AFAIK, there is only one DACL/SD for
> the whole files, so in streams_xattr_sys_acl_blob_get_fd, shouldn't it
> be fsp->base_fsp->fsp_name here?
> 
> +       return SMB_VFS_NEXT_SYS_ACL_BLOB_GET_FILE(
> +               handle, fsp->fsp_name, mem_ctx,
> +               blob_description, blob);
> +}

Good catch, thanks! Guess that mistake crept in in the rebase on current master
when adopting to the recent VFS changes from Jeremy. Fixed.

> Otherwise Fifth paths: Would like someone else to review the stuff in
> streams_xattr_fsync_send etc. It looks OK to me but my tevent FU is
> weak.

This one is marked at TODO in the attached patchset, can someone please take a
look?

> Sixth patch (the one with the pipes): RB+
> Seventh patch: RB+
> Eighth patch: RB+
> Ninth patch: RB+
> Tenth patch: ad_get and ad_fget seem to have duplicated code. A
> separate function might be useful although there are minor
> differences.

Hey, it's a Christmas patchset after all, so your wishes came true, look at
patch no 12... :)

> Otherwise: Tenth patch: RB+
> 
> Eleventh patch: RB+
> Christmas patch: RB+

Marry patchmess! :)

-slow
-------------- next part --------------
From 213dc7f5897f557bb2513f57596a63fae45d0d96 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>
Reviewed-by: Richard Sharpe <realrichardsharpe at gmail.com>
---
 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 f5cc69d0b9c226c708cd805cf35481126c6844fa 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>
Reviewed-by: Richard Sharpe <realrichardsharpe at gmail.com>
---
 source3/modules/vfs_streams_xattr.c | 50 +++++++++++--------------------------
 1 file changed, 15 insertions(+), 35 deletions(-)

diff --git a/source3/modules/vfs_streams_xattr.c b/source3/modules/vfs_streams_xattr.c
index fc79935..90e9771 100644
--- a/source3/modules/vfs_streams_xattr.c
+++ b/source3/modules/vfs_streams_xattr.c
@@ -232,7 +232,7 @@ 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));
+	DBG_DEBUG("streams_xattr_fstat called for %s\n", fsp_str_dbg(io->fsp));
 
 	if (io == NULL || fsp->base_fsp == NULL) {
 		return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
@@ -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 bce66f669da22f947ea50d9543813ea2675f75f6 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>
Reviewed-by: Richard Sharpe <realrichardsharpe at gmail.com>
---
 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 90e9771..971a00c 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 b48756d75d69d73ab5348c2f2d47ffa368e9ebcd 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>
Reviewed-by: Richard Sharpe <realrichardsharpe at gmail.com>
---
 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 971a00c..5eb2b9b 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 92f4f55a4dd98a9ccaf32b46afb3c6e16ef6e625 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] TODO: REVIEW fsync_send, otherwise reviewed-by Richard
 / 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 5eb2b9b..988d2f1 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->base_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 f544138433308e08ce4168ab8efba37eb749dc31 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>
Reviewed-by: Richard Sharpe <realrichardsharpe at gmail.com>
---
 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 988d2f1..7a31227 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 bb5607fbb6c727025dcb49a54e0bc05c6a10e3a2 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>
Reviewed-by: Richard Sharpe <realrichardsharpe at gmail.com>
---
 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 cc01a0d63c1a913e9411017f34da81a69a3320f8 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>
Reviewed-by: Richard Sharpe <realrichardsharpe at gmail.com>
---
 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 82faf9a2f7526c1d57590adc412951042c6e2920 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>
Reviewed-by: Richard Sharpe <realrichardsharpe at gmail.com>
---
 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 5b471b328ee9989884606aa3168f12736ab473f8 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>
Reviewed-by: Richard Sharpe <realrichardsharpe at gmail.com>
---
 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 b511c076a3ca6e5c91c92cc832dcb2363effa2d2 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>
Reviewed-by: Richard Sharpe <realrichardsharpe at gmail.com>
---
 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 3e024ef6ce02242038ff75b4257f8be879b4c2a5 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>
Reviewed-by: Richard Sharpe <realrichardsharpe at gmail.com>
---
 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