impersonation part8/async dosmode

Stefan Metzmacher metze at samba.org
Thu Jul 26 21:47:28 UTC 2018


Hi,

here's the almost finished patchset regarding the async fetching of
dosmode (via xattrs) during a directory listing.

More details are in the commit message of
[PATCH 18/25] smbd: use async dos_mode_at_send in
 smbd_smb2_query_directory_send()

We just need to discuss which test we should use
[PATCH 21/25] or [PATCH 23/25].

Pipelines are here:
https://gitlab.com/samba-team/devel/samba/pipelines/26592331
and
https://gitlab.com/samba-team/devel/samba/pipelines/26596041

metze

-------------- next part --------------
From e60fbfffe816e366fc39f7aa8fbd0d28943eca1a Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 26 Jul 2018 09:41:22 +0200
Subject: [PATCH 01/25] pthreadpool: we need to use
 pthreadpool_tevent_per_thread_cwd() on the callers pool

In pthreadpool_tevent_job_send() we remember if the job will be chdir
safe. It means we means we need to ask the callers pool when calling
pthreadpool_tevent_per_thread_cwd(), as the callers pool might
be a wrapper using pthreadpool_tevent_force_per_thread_cwd().

Signed-off-by: Stefan Metzmacher <metze at samba.org>
Reviewed-by: Ralph Boehme <slow at samba.org>
---
 lib/pthreadpool/pthreadpool_tevent.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/lib/pthreadpool/pthreadpool_tevent.c b/lib/pthreadpool/pthreadpool_tevent.c
index f88f82d17d80..b4a9786f231a 100644
--- a/lib/pthreadpool/pthreadpool_tevent.c
+++ b/lib/pthreadpool/pthreadpool_tevent.c
@@ -992,6 +992,7 @@ struct tevent_req *pthreadpool_tevent_job_send(
 	struct pthreadpool_tevent_job_state *state = NULL;
 	struct pthreadpool_tevent_job *job = NULL;
 	int ret;
+	struct pthreadpool_tevent *caller_pool = pool;
 	struct pthreadpool_tevent_wrapper *wrapper = pool->wrapper.ctx;
 
 	pthreadpool_tevent_cleanup_orphaned_jobs();
@@ -1037,7 +1038,7 @@ struct tevent_req *pthreadpool_tevent_job_send(
 		return tevent_req_post(req, ev);
 	}
 	PTHREAD_TEVENT_JOB_THREAD_FENCE_INIT(job);
-	job->per_thread_cwd = pthreadpool_tevent_per_thread_cwd(pool);
+	job->per_thread_cwd = pthreadpool_tevent_per_thread_cwd(caller_pool);
 	talloc_set_destructor(job, pthreadpool_tevent_job_destructor);
 	DLIST_ADD_END(job->pool->jobs, job);
 	job->state = state;
-- 
2.17.1


From 7bfb846b271c030fd3db570d29a627f18fa188c6 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Tue, 13 Mar 2018 08:14:53 +0100
Subject: [PATCH 02/25] s3: vfs: add SMB_VFS_GETXATTRAT_SEND/RECV

Signed-off-by: Ralph Boehme <slow at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 examples/VFS/skel_opaque.c            |  56 +++++++++++
 examples/VFS/skel_transparent.c       |  89 ++++++++++++++++
 source3/include/smbprofile.h          |   1 +
 source3/include/vfs.h                 |  37 +++++++
 source3/include/vfs_macros.h          |  18 ++++
 source3/modules/vfs_cap.c             |   2 +
 source3/modules/vfs_catia.c           |   2 +
 source3/modules/vfs_ceph.c            |   2 +
 source3/modules/vfs_default.c         |   2 +
 source3/modules/vfs_full_audit.c      | 140 ++++++++++++++++++++++++++
 source3/modules/vfs_glusterfs.c       |   2 +
 source3/modules/vfs_media_harmony.c   |   2 +
 source3/modules/vfs_not_implemented.c |  55 ++++++++++
 source3/modules/vfs_posix_eadb.c      |   2 +
 source3/modules/vfs_shadow_copy2.c    |   2 +
 source3/modules/vfs_snapper.c         |   2 +
 source3/modules/vfs_time_audit.c      | 124 +++++++++++++++++++++++
 source3/modules/vfs_unityed_media.c   |   2 +
 source3/modules/vfs_vxfs.c            |   2 +
 source3/modules/vfs_xattr_tdb.c       |   2 +
 source3/smbd/vfs.c                    | 104 ++++++++++++++++++-
 21 files changed, 646 insertions(+), 2 deletions(-)

diff --git a/examples/VFS/skel_opaque.c b/examples/VFS/skel_opaque.c
index 6fc4d58022e1..971303436b01 100644
--- a/examples/VFS/skel_opaque.c
+++ b/examples/VFS/skel_opaque.c
@@ -22,6 +22,7 @@
  */
 
 #include "../source3/include/includes.h"
+#include "lib/util/tevent_unix.h"
 #include "lib/util/tevent_ntstatus.h"
 
 /* PLEASE,PLEASE READ THE VFS MODULES CHAPTER OF THE 
@@ -818,6 +819,59 @@ static ssize_t skel_getxattr(vfs_handle_struct *handle,
 	return -1;
 }
 
+struct skel_getxattrat_state {
+	struct vfs_aio_state aio_state;
+	ssize_t xattr_size;
+	uint8_t *xattr_value;
+};
+
+static struct tevent_req *skel_getxattrat_send(
+			TALLOC_CTX *mem_ctx,
+			const struct smb_vfs_ev_glue *evg,
+			struct vfs_handle_struct *handle,
+			files_struct *dir_fsp,
+			const struct smb_filename *smb_fname,
+			const char *xattr_name,
+			size_t alloc_hint)
+{
+	struct tevent_context *ev = smb_vfs_ev_glue_ev_ctx(evg);
+	struct tevent_req *req = NULL;
+	struct skel_getxattrat_state *state = NULL;
+
+	req = tevent_req_create(mem_ctx, &state,
+				struct skel_getxattrat_state);
+	if (req == NULL) {
+		return NULL;
+	}
+
+	tevent_req_error(req, ENOSYS);
+	return tevent_req_post(req, ev);
+}
+
+static ssize_t skel_getxattrat_recv(struct tevent_req *req,
+				    struct vfs_aio_state *aio_state,
+				    TALLOC_CTX *mem_ctx,
+				    uint8_t **xattr_value)
+{
+	struct skel_getxattrat_state *state = tevent_req_data(
+		req, struct skel_getxattrat_state);
+	ssize_t xattr_size;
+
+	if (tevent_req_is_unix_error(req, &aio_state->error)) {
+		tevent_req_received(req);
+		return -1;
+	}
+
+	*aio_state = state->aio_state;
+	xattr_size = state->xattr_size;
+	if (xattr_value != NULL) {
+		*xattr_value = talloc_move(mem_ctx, &state->xattr_value);
+	}
+
+	tevent_req_received(req);
+	return xattr_size;
+}
+
 static ssize_t skel_fgetxattr(vfs_handle_struct *handle,
 			      struct files_struct *fsp, const char *name,
 			      void *value, size_t size)
@@ -1037,6 +1091,8 @@ static struct vfs_fn_pointers skel_opaque_fns = {
 
 	/* EA operations. */
 	.getxattr_fn = skel_getxattr,
+	.getxattrat_send_fn = skel_getxattrat_send,
+	.getxattrat_recv_fn = skel_getxattrat_recv,
 	.fgetxattr_fn = skel_fgetxattr,
 	.listxattr_fn = skel_listxattr,
 	.flistxattr_fn = skel_flistxattr,
diff --git a/examples/VFS/skel_transparent.c b/examples/VFS/skel_transparent.c
index d84e6deee479..503c14edcc0a 100644
--- a/examples/VFS/skel_transparent.c
+++ b/examples/VFS/skel_transparent.c
@@ -1004,6 +1004,93 @@ static ssize_t skel_getxattr(vfs_handle_struct *handle,
 	return SMB_VFS_NEXT_GETXATTR(handle, smb_fname, name, value, size);
 }
 
+struct skel_getxattrat_state {
+	struct vfs_aio_state aio_state;
+	ssize_t xattr_size;
+	uint8_t *xattr_value;
+};
+
+static void skel_getxattrat_done(struct tevent_req *subreq);
+
+static struct tevent_req *skel_getxattrat_send(
+			TALLOC_CTX *mem_ctx,
+			const struct smb_vfs_ev_glue *evg,
+			struct vfs_handle_struct *handle,
+			files_struct *dir_fsp,
+			const struct smb_filename *smb_fname,
+			const char *xattr_name,
+			size_t alloc_hint)
+{
+	struct tevent_context *ev = smb_vfs_ev_glue_ev_ctx(evg);
+	struct tevent_req *req = NULL;
+	struct skel_getxattrat_state *state = NULL;
+	struct tevent_req *subreq = NULL;
+
+	req = tevent_req_create(mem_ctx, &state,
+				struct skel_getxattrat_state);
+	if (req == NULL) {
+		return NULL;
+	}
+
+	subreq = SMB_VFS_NEXT_GETXATTRAT_SEND(state,
+					      evg,
+					      handle,
+					      dir_fsp,
+					      smb_fname,
+					      xattr_name,
+					      alloc_hint);
+	if (tevent_req_nomem(subreq, req)) {
+		return tevent_req_post(req, ev);
+	}
+	tevent_req_set_callback(subreq, skel_getxattrat_done, req);
+
+	return req;
+}
+
+static void skel_getxattrat_done(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	struct skel_getxattrat_state *state = tevent_req_data(
+		req, struct skel_getxattrat_state);
+
+	state->xattr_size = SMB_VFS_NEXT_GETXATTRAT_RECV(subreq,
+							 &state->aio_state,
+							 state,
+							 &state->xattr_value);
+	TALLOC_FREE(subreq);
+	if (state->xattr_size == -1) {
+		tevent_req_error(req, state->aio_state.error);
+		return;
+	}
+
+	tevent_req_done(req);
+}
+
+static ssize_t skel_getxattrat_recv(struct tevent_req *req,
+				    struct vfs_aio_state *aio_state,
+				    TALLOC_CTX *mem_ctx,
+				    uint8_t **xattr_value)
+{
+	struct skel_getxattrat_state *state = tevent_req_data(
+		req, struct skel_getxattrat_state);
+	ssize_t xattr_size;
+
+	if (tevent_req_is_unix_error(req, &aio_state->error)) {
+		tevent_req_received(req);
+		return -1;
+	}
+
+	*aio_state = state->aio_state;
+	xattr_size = state->xattr_size;
+	if (xattr_value != NULL) {
+		*xattr_value = talloc_move(mem_ctx, &state->xattr_value);
+	}
+
+	tevent_req_received(req);
+	return xattr_size;
+}
+
 static ssize_t skel_fgetxattr(vfs_handle_struct *handle,
 			      struct files_struct *fsp, const char *name,
 			      void *value, size_t size)
@@ -1233,6 +1320,8 @@ static struct vfs_fn_pointers skel_transparent_fns = {
 
 	/* EA operations. */
 	.getxattr_fn = skel_getxattr,
+	.getxattrat_send_fn = skel_getxattrat_send,
+	.getxattrat_recv_fn = skel_getxattrat_recv,
 	.fgetxattr_fn = skel_fgetxattr,
 	.listxattr_fn = skel_listxattr,
 	.flistxattr_fn = skel_flistxattr,
diff --git a/source3/include/smbprofile.h b/source3/include/smbprofile.h
index e33f77e70e35..8888ae3eaa85 100644
--- a/source3/include/smbprofile.h
+++ b/source3/include/smbprofile.h
@@ -95,6 +95,7 @@ struct tevent_context;
 	SMBPROFILE_STATS_BASIC(syscall_brl_lock) \
 	SMBPROFILE_STATS_BASIC(syscall_brl_unlock) \
 	SMBPROFILE_STATS_BASIC(syscall_brl_cancel) \
+	SMBPROFILE_STATS_BYTES(syscall_asys_getxattrat) \
 	SMBPROFILE_STATS_SECTION_END \
 	\
 	SMBPROFILE_STATS_SECTION_START(acl, "ACL Calls") \
diff --git a/source3/include/vfs.h b/source3/include/vfs.h
index 3d3718d7c075..def802c9772b 100644
--- a/source3/include/vfs.h
+++ b/source3/include/vfs.h
@@ -259,6 +259,7 @@
 /* Bump to version 40, Samba 4.10 will ship with that */
 /* Version 40 - Introduce smb_vfs_ev_glue infrastructure. */
 /* Version 40 - Add vfs_not_implemented_* helper functions. */
+/* Version 40 - Add SMB_VFS_GETXATTRAT_SEND/RECV */
 
 #define SMB_VFS_INTERFACE_VERSION 40
 
@@ -953,6 +954,18 @@ struct vfs_fn_pointers {
 					const char *name,
 					void *value,
 					size_t size);
+	struct tevent_req *(*getxattrat_send_fn)(
+				TALLOC_CTX *mem_ctx,
+				const struct smb_vfs_ev_glue *evg,
+				struct vfs_handle_struct *handle,
+				files_struct *dir_fsp,
+				const struct smb_filename *smb_fname,
+				const char *xattr_name,
+				size_t alloc_hint);
+	ssize_t (*getxattrat_recv_fn)(struct tevent_req *req,
+				      struct vfs_aio_state *aio_state,
+				      TALLOC_CTX *mem_ctx,
+				      uint8_t **xattr_value);
 	ssize_t (*fgetxattr_fn)(struct vfs_handle_struct *handle, struct files_struct *fsp, const char *name, void *value, size_t size);
 	ssize_t (*listxattr_fn)(struct vfs_handle_struct *handle,
 					const struct smb_filename *smb_fname,
@@ -1443,6 +1456,18 @@ ssize_t smb_vfs_call_getxattr(struct vfs_handle_struct *handle,
 				const char *name,
 				void *value,
 				size_t size);
+struct tevent_req *smb_vfs_call_getxattrat_send(
+			TALLOC_CTX *mem_ctx,
+			const struct smb_vfs_ev_glue *evg,
+			struct vfs_handle_struct *handle,
+			files_struct *dir_fsp,
+			const struct smb_filename *smb_fname,
+			const char *xattr_name,
+			size_t alloc_hint);
+ssize_t smb_vfs_call_getxattrat_recv(struct tevent_req *req,
+				     struct vfs_aio_state *aio_state,
+				     TALLOC_CTX *mem_ctx,
+				     uint8_t **xattr_value);
 ssize_t smb_vfs_call_fgetxattr(struct vfs_handle_struct *handle,
 			       struct files_struct *fsp, const char *name,
 			       void *value, size_t size);
@@ -1850,6 +1875,18 @@ ssize_t vfs_not_implemented_getxattr(vfs_handle_struct *handle,
 				const char *name,
 				void *value,
 				size_t size);
+struct tevent_req *vfs_not_implemented_getxattrat_send(
+			TALLOC_CTX *mem_ctx,
+			const struct smb_vfs_ev_glue *evg,
+			struct vfs_handle_struct *handle,
+			files_struct *dir_fsp,
+			const struct smb_filename *smb_fname,
+			const char *xattr_name,
+			size_t alloc_hint);
+ssize_t vfs_not_implemented_getxattrat_recv(struct tevent_req *req,
+				    struct vfs_aio_state *aio_state,
+				    TALLOC_CTX *mem_ctx,
+				    uint8_t **xattr_value);
 ssize_t vfs_not_implemented_fgetxattr(vfs_handle_struct *handle,
 			      struct files_struct *fsp, const char *name,
 			      void *value, size_t size);
diff --git a/source3/include/vfs_macros.h b/source3/include/vfs_macros.h
index 46d6728629c3..d4863536cc22 100644
--- a/source3/include/vfs_macros.h
+++ b/source3/include/vfs_macros.h
@@ -500,6 +500,24 @@
 #define SMB_VFS_NEXT_GETXATTR(handle,smb_fname,name,value,size) \
 	smb_vfs_call_getxattr((handle)->next,(smb_fname),(name),(value),(size))
 
+#define SMB_VFS_GETXATTRAT_SEND(mem_ctx,evg,dir_fsp,smb_fname, \
+				xattr_name, alloc_hint) \
+	smb_vfs_call_getxattrat_send((mem_ctx),(evg), \
+				     (dir_fsp)->conn->vfs_handles, \
+				     (dir_fsp),(smb_fname),(xattr_name), \
+				     (alloc_hint))
+#define SMB_VFS_GETXATTRAT_RECV(req, aio_state, mem_ctx, xattr_value) \
+	smb_vfs_call_getxattrat_recv((req),(aio_state),(mem_ctx),(xattr_value))
+
+#define SMB_VFS_NEXT_GETXATTRAT_SEND(mem_ctx,evg,handle,dir_fsp,smb_fname, \
+				     xattr_name,alloc_hint) \
+	smb_vfs_call_getxattrat_send((mem_ctx),(evg), \
+				     (handle)->next, \
+				     (dir_fsp), (smb_fname),(xattr_name), \
+				     (alloc_hint))
+#define SMB_VFS_NEXT_GETXATTRAT_RECV(req, aio_state, mem_ctx, xattr_value) \
+	smb_vfs_call_getxattrat_recv((req),(aio_state),(mem_ctx),(xattr_value))
+
 #define SMB_VFS_FGETXATTR(fsp,name,value,size) \
 	smb_vfs_call_fgetxattr((fsp)->conn->vfs_handles, (fsp), (name),(value),(size))
 #define SMB_VFS_NEXT_FGETXATTR(handle,fsp,name,value,size) \
diff --git a/source3/modules/vfs_cap.c b/source3/modules/vfs_cap.c
index ffc35d604073..56b7efe1b42a 100644
--- a/source3/modules/vfs_cap.c
+++ b/source3/modules/vfs_cap.c
@@ -1027,6 +1027,8 @@ static struct vfs_fn_pointers vfs_cap_fns = {
 	.sys_acl_set_file_fn = cap_sys_acl_set_file,
 	.sys_acl_delete_def_file_fn = cap_sys_acl_delete_def_file,
 	.getxattr_fn = cap_getxattr,
+	.getxattrat_send_fn = vfs_not_implemented_getxattrat_send,
+	.getxattrat_recv_fn = vfs_not_implemented_getxattrat_recv,
 	.fgetxattr_fn = cap_fgetxattr,
 	.listxattr_fn = cap_listxattr,
 	.removexattr_fn = cap_removexattr,
diff --git a/source3/modules/vfs_catia.c b/source3/modules/vfs_catia.c
index fce2dcf8ca59..d2dddaf69213 100644
--- a/source3/modules/vfs_catia.c
+++ b/source3/modules/vfs_catia.c
@@ -2468,6 +2468,8 @@ static struct vfs_fn_pointers vfs_catia_fns = {
 
 	/* EA operations. */
 	.getxattr_fn = catia_getxattr,
+	.getxattrat_send_fn = vfs_not_implemented_getxattrat_send,
+	.getxattrat_recv_fn = vfs_not_implemented_getxattrat_recv,
 	.listxattr_fn = catia_listxattr,
 	.removexattr_fn = catia_removexattr,
 	.setxattr_fn = catia_setxattr,
diff --git a/source3/modules/vfs_ceph.c b/source3/modules/vfs_ceph.c
index 47371bc9e082..8b709eddc908 100644
--- a/source3/modules/vfs_ceph.c
+++ b/source3/modules/vfs_ceph.c
@@ -1528,6 +1528,8 @@ static struct vfs_fn_pointers ceph_fns = {
 
 	/* EA operations. */
 	.getxattr_fn = cephwrap_getxattr,
+	.getxattrat_send_fn = vfs_not_implemented_getxattrat_send,
+	.getxattrat_recv_fn = vfs_not_implemented_getxattrat_recv,
 	.fgetxattr_fn = cephwrap_fgetxattr,
 	.listxattr_fn = cephwrap_listxattr,
 	.flistxattr_fn = cephwrap_flistxattr,
diff --git a/source3/modules/vfs_default.c b/source3/modules/vfs_default.c
index 72dbd7567359..d985cba9cb7f 100644
--- a/source3/modules/vfs_default.c
+++ b/source3/modules/vfs_default.c
@@ -2986,6 +2986,8 @@ static struct vfs_fn_pointers vfs_default_fns = {
 
 	/* EA operations. */
 	.getxattr_fn = vfswrap_getxattr,
+	.getxattrat_send_fn = vfs_not_implemented_getxattrat_send,
+	.getxattrat_recv_fn = vfs_not_implemented_getxattrat_recv,
 	.fgetxattr_fn = vfswrap_fgetxattr,
 	.listxattr_fn = vfswrap_listxattr,
 	.flistxattr_fn = vfswrap_flistxattr,
diff --git a/source3/modules/vfs_full_audit.c b/source3/modules/vfs_full_audit.c
index bd904e8ef875..f28413b83c28 100644
--- a/source3/modules/vfs_full_audit.c
+++ b/source3/modules/vfs_full_audit.c
@@ -201,6 +201,8 @@ typedef enum _vfs_op_type {
 
 	/* EA operations. */
 	SMB_VFS_OP_GETXATTR,
+	SMB_VFS_OP_GETXATTRAT_SEND,
+	SMB_VFS_OP_GETXATTRAT_RECV,
 	SMB_VFS_OP_FGETXATTR,
 	SMB_VFS_OP_LISTXATTR,
 	SMB_VFS_OP_FLISTXATTR,
@@ -330,6 +332,8 @@ static struct {
 	{ SMB_VFS_OP_SYS_ACL_SET_FD,	"sys_acl_set_fd" },
 	{ SMB_VFS_OP_SYS_ACL_DELETE_DEF_FILE,	"sys_acl_delete_def_file" },
 	{ SMB_VFS_OP_GETXATTR,	"getxattr" },
+	{ SMB_VFS_OP_GETXATTRAT_SEND, "getxattrat_send" },
+	{ SMB_VFS_OP_GETXATTRAT_RECV, "getxattrat_recv" },
 	{ SMB_VFS_OP_FGETXATTR,	"fgetxattr" },
 	{ SMB_VFS_OP_LISTXATTR,	"listxattr" },
 	{ SMB_VFS_OP_FLISTXATTR,	"flistxattr" },
@@ -2317,6 +2321,140 @@ static ssize_t smb_full_audit_getxattr(struct vfs_handle_struct *handle,
 	return result;
 }
 
+struct smb_full_audit_getxattrat_state {
+	struct vfs_aio_state aio_state;
+	vfs_handle_struct *handle;
+	files_struct *dir_fsp;
+	const struct smb_filename *smb_fname;
+	const char *xattr_name;
+	ssize_t xattr_size;
+	uint8_t *xattr_value;
+};
+
+static void smb_full_audit_getxattrat_done(struct tevent_req *subreq);
+
+static struct tevent_req *smb_full_audit_getxattrat_send(
+			TALLOC_CTX *mem_ctx,
+			const struct smb_vfs_ev_glue *evg,
+			struct vfs_handle_struct *handle,
+			files_struct *dir_fsp,
+			const struct smb_filename *smb_fname,
+			const char *xattr_name,
+			size_t alloc_hint)
+{
+	struct tevent_context *ev = smb_vfs_ev_glue_ev_ctx(evg);
+	struct tevent_req *req = NULL;
+	struct tevent_req *subreq = NULL;
+	struct smb_full_audit_getxattrat_state *state = NULL;
+
+	req = tevent_req_create(mem_ctx, &state,
+				struct smb_full_audit_getxattrat_state);
+	if (req == NULL) {
+		do_log(SMB_VFS_OP_GETXATTRAT_SEND,
+		       false,
+		       handle,
+		       "%s/%s|%s",
+		       fsp_str_do_log(dir_fsp),
+		       smb_fname->base_name,
+		       xattr_name);
+		return NULL;
+	}
+	*state = (struct smb_full_audit_getxattrat_state) {
+		.handle = handle,
+		.dir_fsp = dir_fsp,
+		.smb_fname = smb_fname,
+		.xattr_name = xattr_name,
+	};
+
+	subreq = SMB_VFS_NEXT_GETXATTRAT_SEND(state,
+					      evg,
+					      handle,
+					      dir_fsp,
+					      smb_fname,
+					      xattr_name,
+					      alloc_hint);
+	if (tevent_req_nomem(subreq, req)) {
+		do_log(SMB_VFS_OP_GETXATTRAT_SEND,
+		       false,
+		       handle,
+		       "%s/%s|%s",
+		       fsp_str_do_log(dir_fsp),
+		       smb_fname->base_name,
+		       xattr_name);
+		return tevent_req_post(req, ev);
+	}
+	tevent_req_set_callback(subreq, smb_full_audit_getxattrat_done, req);
+
+	do_log(SMB_VFS_OP_GETXATTRAT_SEND,
+	       true,
+	       handle,
+	       "%s/%s|%s",
+	       fsp_str_do_log(dir_fsp),
+	       smb_fname->base_name,
+	       xattr_name);
+
+	return req;
+}
+
+static void smb_full_audit_getxattrat_done(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	struct smb_full_audit_getxattrat_state *state = tevent_req_data(
+		req, struct smb_full_audit_getxattrat_state);
+
+	state->xattr_size = SMB_VFS_NEXT_GETXATTRAT_RECV(subreq,
+							 &state->aio_state,
+							 state,
+							 &state->xattr_value);
+	TALLOC_FREE(subreq);
+	if (state->xattr_size == -1) {
+		tevent_req_error(req, state->aio_state.error);
+		return;
+	}
+
+	tevent_req_done(req);
+}
+
+static ssize_t smb_full_audit_getxattrat_recv(struct tevent_req *req,
+					      struct vfs_aio_state *aio_state,
+					      TALLOC_CTX *mem_ctx,
+					      uint8_t **xattr_value)
+{
+	struct smb_full_audit_getxattrat_state *state = tevent_req_data(
+		req, struct smb_full_audit_getxattrat_state);
+	ssize_t xattr_size;
+
+	if (tevent_req_is_unix_error(req, &aio_state->error)) {
+		do_log(SMB_VFS_OP_GETXATTRAT_RECV,
+		       false,
+		       state->handle,
+		       "%s/%s|%s",
+		       fsp_str_do_log(state->dir_fsp),
+		       state->smb_fname->base_name,
+		       state->xattr_name);
+		tevent_req_received(req);
+		return -1;
+	}
+
+	do_log(SMB_VFS_OP_GETXATTRAT_RECV,
+	       true,
+	       state->handle,
+	       "%s/%s|%s",
+	       fsp_str_do_log(state->dir_fsp),
+	       state->smb_fname->base_name,
+	       state->xattr_name);
+
+	*aio_state = state->aio_state;
+	xattr_size = state->xattr_size;
+	if (xattr_value != NULL) {
+		*xattr_value = talloc_move(mem_ctx, &state->xattr_value);
+	}
+
+	tevent_req_received(req);
+	return xattr_size;
+}
+
 static ssize_t smb_full_audit_fgetxattr(struct vfs_handle_struct *handle,
 			       struct files_struct *fsp,
 			       const char *name, void *value, size_t size)
@@ -2593,6 +2731,8 @@ static struct vfs_fn_pointers vfs_full_audit_fns = {
 	.sys_acl_set_fd_fn = smb_full_audit_sys_acl_set_fd,
 	.sys_acl_delete_def_file_fn = smb_full_audit_sys_acl_delete_def_file,
 	.getxattr_fn = smb_full_audit_getxattr,
+	.getxattrat_send_fn = smb_full_audit_getxattrat_send,
+	.getxattrat_recv_fn = smb_full_audit_getxattrat_recv,
 	.fgetxattr_fn = smb_full_audit_fgetxattr,
 	.listxattr_fn = smb_full_audit_listxattr,
 	.flistxattr_fn = smb_full_audit_flistxattr,
diff --git a/source3/modules/vfs_glusterfs.c b/source3/modules/vfs_glusterfs.c
index 02289a64e505..98be3c6d4e2a 100644
--- a/source3/modules/vfs_glusterfs.c
+++ b/source3/modules/vfs_glusterfs.c
@@ -1542,6 +1542,8 @@ static struct vfs_fn_pointers glusterfs_fns = {
 
 	/* EA Operations */
 	.getxattr_fn = vfs_gluster_getxattr,
+	.getxattrat_send_fn = vfs_not_implemented_getxattrat_send,
+	.getxattrat_recv_fn = vfs_not_implemented_getxattrat_recv,
 	.fgetxattr_fn = vfs_gluster_fgetxattr,
 	.listxattr_fn = vfs_gluster_listxattr,
 	.flistxattr_fn = vfs_gluster_flistxattr,
diff --git a/source3/modules/vfs_media_harmony.c b/source3/modules/vfs_media_harmony.c
index 32ba45a7fe89..ea49eff6d8ff 100644
--- a/source3/modules/vfs_media_harmony.c
+++ b/source3/modules/vfs_media_harmony.c
@@ -2311,6 +2311,8 @@ static struct vfs_fn_pointers vfs_mh_fns = {
 
 	/* EA operations. */
 	.getxattr_fn = mh_getxattr,
+	.getxattrat_send_fn = vfs_not_implemented_getxattrat_send,
+	.getxattrat_recv_fn = vfs_not_implemented_getxattrat_recv,
 	.listxattr_fn = mh_listxattr,
 	.removexattr_fn = mh_removexattr,
 	.setxattr_fn = mh_setxattr,
diff --git a/source3/modules/vfs_not_implemented.c b/source3/modules/vfs_not_implemented.c
index 45c3a01c9f57..bb4854d8bf8c 100644
--- a/source3/modules/vfs_not_implemented.c
+++ b/source3/modules/vfs_not_implemented.c
@@ -822,6 +822,59 @@ ssize_t vfs_not_implemented_getxattr(vfs_handle_struct *handle,
 	return -1;
 }
 
+struct vfs_not_implemented_getxattrat_state {
+	struct vfs_aio_state aio_state;
+	ssize_t xattr_size;
+	uint8_t *xattr_value;
+};
+
+struct tevent_req *vfs_not_implemented_getxattrat_send(
+			TALLOC_CTX *mem_ctx,
+			const struct smb_vfs_ev_glue *evg,
+			struct vfs_handle_struct *handle,
+			files_struct *dir_fsp,
+			const struct smb_filename *smb_fname,
+			const char *xattr_name,
+			size_t alloc_hint)
+{
+	struct tevent_context *ev = smb_vfs_ev_glue_ev_ctx(evg);
+	struct tevent_req *req = NULL;
+	struct vfs_not_implemented_getxattrat_state *state = NULL;
+
+	req = tevent_req_create(mem_ctx, &state,
+				struct vfs_not_implemented_getxattrat_state);
+	if (req == NULL) {
+		return NULL;
+	}
+
+	tevent_req_error(req, ENOSYS);
+	return tevent_req_post(req, ev);
+}
+
+ssize_t vfs_not_implemented_getxattrat_recv(struct tevent_req *req,
+				    struct vfs_aio_state *aio_state,
+				    TALLOC_CTX *mem_ctx,
+				    uint8_t **xattr_value)
+{
+	struct vfs_not_implemented_getxattrat_state *state = tevent_req_data(
+		req, struct vfs_not_implemented_getxattrat_state);
+	ssize_t xattr_size;
+
+	if (tevent_req_is_unix_error(req, &aio_state->error)) {
+		tevent_req_received(req);
+		return -1;
+	}
+
+	*aio_state = state->aio_state;
+	xattr_size = state->xattr_size;
+	if (xattr_value != NULL) {
+		*xattr_value = talloc_move(mem_ctx, &state->xattr_value);
+	}
+
+	tevent_req_received(req);
+	return xattr_size;
+}
+
 ssize_t vfs_not_implemented_fgetxattr(vfs_handle_struct *handle,
 			      struct files_struct *fsp, const char *name,
 			      void *value, size_t size)
@@ -1041,6 +1094,8 @@ static struct vfs_fn_pointers vfs_not_implemented_fns = {
 
 	/* EA operations. */
 	.getxattr_fn = vfs_not_implemented_getxattr,
+	.getxattrat_send_fn = vfs_not_implemented_getxattrat_send,
+	.getxattrat_recv_fn = vfs_not_implemented_getxattrat_recv,
 	.fgetxattr_fn = vfs_not_implemented_fgetxattr,
 	.listxattr_fn = vfs_not_implemented_listxattr,
 	.flistxattr_fn = vfs_not_implemented_flistxattr,
diff --git a/source3/modules/vfs_posix_eadb.c b/source3/modules/vfs_posix_eadb.c
index 889655e52ce0..44bef9f9d82e 100644
--- a/source3/modules/vfs_posix_eadb.c
+++ b/source3/modules/vfs_posix_eadb.c
@@ -431,6 +431,8 @@ static int posix_eadb_connect(vfs_handle_struct *handle, const char *service,
 
 static struct vfs_fn_pointers vfs_posix_eadb_fns = {
 	.getxattr_fn = posix_eadb_getxattr,
+	.getxattrat_send_fn = vfs_not_implemented_getxattrat_send,
+	.getxattrat_recv_fn = vfs_not_implemented_getxattrat_recv,
 	.fgetxattr_fn = posix_eadb_fgetxattr,
 	.setxattr_fn = posix_eadb_setxattr,
 	.fsetxattr_fn = posix_eadb_fsetxattr,
diff --git a/source3/modules/vfs_shadow_copy2.c b/source3/modules/vfs_shadow_copy2.c
index aa7cd9c61d0f..79c1ee5cf337 100644
--- a/source3/modules/vfs_shadow_copy2.c
+++ b/source3/modules/vfs_shadow_copy2.c
@@ -3212,6 +3212,8 @@ static struct vfs_fn_pointers vfs_shadow_copy2_fns = {
 	.mkdir_fn = shadow_copy2_mkdir,
 	.rmdir_fn = shadow_copy2_rmdir,
 	.getxattr_fn = shadow_copy2_getxattr,
+	.getxattrat_send_fn = vfs_not_implemented_getxattrat_send,
+	.getxattrat_recv_fn = vfs_not_implemented_getxattrat_recv,
 	.listxattr_fn = shadow_copy2_listxattr,
 	.removexattr_fn = shadow_copy2_removexattr,
 	.setxattr_fn = shadow_copy2_setxattr,
diff --git a/source3/modules/vfs_snapper.c b/source3/modules/vfs_snapper.c
index 6b935c3df41e..443a940b17a8 100644
--- a/source3/modules/vfs_snapper.c
+++ b/source3/modules/vfs_snapper.c
@@ -3125,6 +3125,8 @@ static struct vfs_fn_pointers snapper_fns = {
 	.mkdir_fn = snapper_gmt_mkdir,
 	.rmdir_fn = snapper_gmt_rmdir,
 	.getxattr_fn = snapper_gmt_getxattr,
+	.getxattrat_send_fn = vfs_not_implemented_getxattrat_send,
+	.getxattrat_recv_fn = vfs_not_implemented_getxattrat_recv,
 	.listxattr_fn = snapper_gmt_listxattr,
 	.removexattr_fn = snapper_gmt_removexattr,
 	.setxattr_fn = snapper_gmt_setxattr,
diff --git a/source3/modules/vfs_time_audit.c b/source3/modules/vfs_time_audit.c
index e85ded5e4c36..07c14415312f 100644
--- a/source3/modules/vfs_time_audit.c
+++ b/source3/modules/vfs_time_audit.c
@@ -86,6 +86,23 @@ static void smb_time_audit_log_fsp(const char *syscallname, double elapsed,
 	TALLOC_FREE(msg);
 }
 
+static void smb_time_audit_log_at(const char *syscallname,
+				  double elapsed,
+				  const struct files_struct *dir_fsp,
+				  const struct smb_filename *smb_fname)
+{
+	char *msg = NULL;
+
+	msg = talloc_asprintf(talloc_tos(),
+			      "filename = \"%s/%s/%s\"",
+			      dir_fsp->conn->connectpath,
+			      dir_fsp->fsp_name->base_name,
+			      smb_fname->base_name);
+
+	smb_time_audit_log_msg(syscallname, elapsed, msg);
+	TALLOC_FREE(msg);
+}
+
 static void smb_time_audit_log_fname(const char *syscallname, double elapsed,
 				    const char *fname)
 {
@@ -2334,6 +2351,111 @@ static ssize_t smb_time_audit_getxattr(struct vfs_handle_struct *handle,
 	return result;
 }
 
+struct smb_time_audit_getxattrat_state {
+	struct vfs_aio_state aio_state;
+	files_struct *dir_fsp;
+	const struct smb_filename *smb_fname;
+	const char *xattr_name;
+	ssize_t xattr_size;
+	uint8_t *xattr_value;
+};
+
+static void smb_time_audit_getxattrat_done(struct tevent_req *subreq);
+
+static struct tevent_req *smb_time_audit_getxattrat_send(
+			TALLOC_CTX *mem_ctx,
+			const struct smb_vfs_ev_glue *evg,
+			struct vfs_handle_struct *handle,
+			files_struct *dir_fsp,
+			const struct smb_filename *smb_fname,
+			const char *xattr_name,
+			size_t alloc_hint)
+{
+	struct tevent_context *ev = smb_vfs_ev_glue_ev_ctx(evg);
+	struct tevent_req *req = NULL;
+	struct tevent_req *subreq = NULL;
+	struct smb_time_audit_getxattrat_state *state = NULL;
+
+	req = tevent_req_create(mem_ctx, &state,
+				struct smb_time_audit_getxattrat_state);
+	if (req == NULL) {
+		return NULL;
+	}
+	*state = (struct smb_time_audit_getxattrat_state) {
+		.dir_fsp = dir_fsp,
+		.smb_fname = smb_fname,
+		.xattr_name = xattr_name,
+	};
+
+	subreq = SMB_VFS_NEXT_GETXATTRAT_SEND(state,
+					      evg,
+					      handle,
+					      dir_fsp,
+					      smb_fname,
+					      xattr_name,
+					      alloc_hint);
+	if (tevent_req_nomem(subreq, req)) {
+		return tevent_req_post(req, ev);
+	}
+	tevent_req_set_callback(subreq, smb_time_audit_getxattrat_done, req);
+
+	return req;
+}
+
+static void smb_time_audit_getxattrat_done(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	struct smb_time_audit_getxattrat_state *state = tevent_req_data(
+		req, struct smb_time_audit_getxattrat_state);
+
+	state->xattr_size = SMB_VFS_NEXT_GETXATTRAT_RECV(subreq,
+							 &state->aio_state,
+							 state,
+							 &state->xattr_value);
+	TALLOC_FREE(subreq);
+	if (state->xattr_size == -1) {
+		tevent_req_error(req, state->aio_state.error);
+		return;
+	}
+
+	tevent_req_done(req);
+}
+
+static ssize_t smb_time_audit_getxattrat_recv(struct tevent_req *req,
+					      struct vfs_aio_state *aio_state,
+					      TALLOC_CTX *mem_ctx,
+					      uint8_t **xattr_value)
+{
+	struct smb_time_audit_getxattrat_state *state = tevent_req_data(
+		req, struct smb_time_audit_getxattrat_state);
+	ssize_t xattr_size;
+	double timediff;
+
+	timediff = state->aio_state.duration * 1.0e-9;
+
+	if (timediff > audit_timeout) {
+		smb_time_audit_log_at("async getxattrat",
+				      timediff,
+				      state->dir_fsp,
+				      state->smb_fname);
+	}
+
+	if (tevent_req_is_unix_error(req, &aio_state->error)) {
+		tevent_req_received(req);
+		return -1;
+	}
+
+	*aio_state = state->aio_state;
+	xattr_size = state->xattr_size;
+	if (xattr_value != NULL) {
+		*xattr_value = talloc_move(mem_ctx, &state->xattr_value);
+	}
+
+	tevent_req_received(req);
+	return xattr_size;
+}
+
 static ssize_t smb_time_audit_fgetxattr(struct vfs_handle_struct *handle,
 					struct files_struct *fsp,
 					const char *name, void *value,
@@ -2667,6 +2789,8 @@ static struct vfs_fn_pointers vfs_time_audit_fns = {
 	.sys_acl_set_fd_fn = smb_time_audit_sys_acl_set_fd,
 	.sys_acl_delete_def_file_fn = smb_time_audit_sys_acl_delete_def_file,
 	.getxattr_fn = smb_time_audit_getxattr,
+	.getxattrat_send_fn = smb_time_audit_getxattrat_send,
+	.getxattrat_recv_fn = smb_time_audit_getxattrat_recv,
 	.fgetxattr_fn = smb_time_audit_fgetxattr,
 	.listxattr_fn = smb_time_audit_listxattr,
 	.flistxattr_fn = smb_time_audit_flistxattr,
diff --git a/source3/modules/vfs_unityed_media.c b/source3/modules/vfs_unityed_media.c
index 328a93d70436..235adf523a3d 100644
--- a/source3/modules/vfs_unityed_media.c
+++ b/source3/modules/vfs_unityed_media.c
@@ -1904,6 +1904,8 @@ static struct vfs_fn_pointers vfs_um_fns = {
 
 	/* EA operations. */
 	.getxattr_fn = um_getxattr,
+	.getxattrat_send_fn = vfs_not_implemented_getxattrat_send,
+	.getxattrat_recv_fn = vfs_not_implemented_getxattrat_recv,
 	.listxattr_fn = um_listxattr,
 	.removexattr_fn = um_removexattr,
 	.setxattr_fn = um_setxattr,
diff --git a/source3/modules/vfs_vxfs.c b/source3/modules/vfs_vxfs.c
index 1295c7541e79..5baa38568676 100644
--- a/source3/modules/vfs_vxfs.c
+++ b/source3/modules/vfs_vxfs.c
@@ -944,6 +944,8 @@ static struct vfs_fn_pointers vfs_vxfs_fns = {
 	.set_dos_attributes_fn = vxfs_set_ea_dos_attributes,
 	.fset_dos_attributes_fn = vxfs_fset_ea_dos_attributes,
 	.getxattr_fn = vxfs_get_xattr,
+	.getxattrat_send_fn = vfs_not_implemented_getxattrat_send,
+	.getxattrat_recv_fn = vfs_not_implemented_getxattrat_recv,
 	.fgetxattr_fn = vxfs_fget_xattr,
 	.listxattr_fn = vxfs_listxattr,
 	.flistxattr_fn = vxfs_flistxattr,
diff --git a/source3/modules/vfs_xattr_tdb.c b/source3/modules/vfs_xattr_tdb.c
index a8b3ee2174a0..ceed3a9cbf8b 100644
--- a/source3/modules/vfs_xattr_tdb.c
+++ b/source3/modules/vfs_xattr_tdb.c
@@ -602,6 +602,8 @@ static int xattr_tdb_connect(vfs_handle_struct *handle, const char *service,
 
 static struct vfs_fn_pointers vfs_xattr_tdb_fns = {
 	.getxattr_fn = xattr_tdb_getxattr,
+	.getxattrat_send_fn = vfs_not_implemented_getxattrat_send,
+	.getxattrat_recv_fn = vfs_not_implemented_getxattrat_recv,
 	.fgetxattr_fn = xattr_tdb_fgetxattr,
 	.setxattr_fn = xattr_tdb_setxattr,
 	.fsetxattr_fn = xattr_tdb_fsetxattr,
diff --git a/source3/smbd/vfs.c b/source3/smbd/vfs.c
index a0fc005dad3d..501eafe1c028 100644
--- a/source3/smbd/vfs.c
+++ b/source3/smbd/vfs.c
@@ -2287,7 +2287,6 @@ struct smb_vfs_ev_glue *smb_vfs_ev_glue_create_switch(
 	return evg_u;
 }
 
-_UNUSED_
 static bool smb_vfs_ev_glue_push_use(const struct smb_vfs_ev_glue *evg,
 				     struct tevent_req *req)
 {
@@ -2312,7 +2311,6 @@ static bool smb_vfs_ev_glue_push_use(const struct smb_vfs_ev_glue *evg,
 	return tevent_context_push_use(evg->run_ev);
 }
 
-_UNUSED_
 static void smb_vfs_ev_glue_pop_use(const struct smb_vfs_ev_glue *evg)
 {
 	if (evg->run_ev == evg->return_ev) {
@@ -3427,6 +3425,108 @@ ssize_t smb_vfs_call_getxattr(struct vfs_handle_struct *handle,
 	return handle->fns->getxattr_fn(handle, smb_fname, name, value, size);
 }
 
+
+struct smb_vfs_call_getxattrat_state {
+	ssize_t (*recv_fn)(struct tevent_req *req,
+			   struct vfs_aio_state *aio_state,
+			   TALLOC_CTX *mem_ctx,
+			   uint8_t **xattr_value);
+	ssize_t retval;
+	uint8_t *xattr_value;
+	struct vfs_aio_state aio_state;
+};
+
+static void smb_vfs_call_getxattrat_done(struct tevent_req *subreq);
+
+struct tevent_req *smb_vfs_call_getxattrat_send(
+			TALLOC_CTX *mem_ctx,
+			const struct smb_vfs_ev_glue *evg,
+			struct vfs_handle_struct *handle,
+			files_struct *dir_fsp,
+			const struct smb_filename *smb_fname,
+			const char *xattr_name,
+			size_t alloc_hint)
+{
+	struct tevent_req *req = NULL;
+	struct smb_vfs_call_getxattrat_state *state = NULL;
+	struct tevent_req *subreq = NULL;
+	bool ok;
+
+	req = tevent_req_create(mem_ctx, &state,
+				struct smb_vfs_call_getxattrat_state);
+	if (req == NULL) {
+		return NULL;
+	}
+
+	VFS_FIND(getxattrat_send);
+	state->recv_fn = handle->fns->getxattrat_recv_fn;
+
+	ok = smb_vfs_ev_glue_push_use(evg, req);
+	if (!ok) {
+		tevent_req_error(req, EIO);
+		return tevent_req_post(req, evg->return_ev);
+	}
+
+	subreq = handle->fns->getxattrat_send_fn(mem_ctx,
+						 evg->next_glue,
+						 handle,
+						 dir_fsp,
+						 smb_fname,
+						 xattr_name,
+						 alloc_hint);
+	smb_vfs_ev_glue_pop_use(evg);
+
+	if (tevent_req_nomem(subreq, req)) {
+		return tevent_req_post(req, evg->return_ev);
+	}
+	tevent_req_set_callback(subreq, smb_vfs_call_getxattrat_done, req);
+	return req;
+}
+
+static void smb_vfs_call_getxattrat_done(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	struct smb_vfs_call_getxattrat_state *state = tevent_req_data(
+		req, struct smb_vfs_call_getxattrat_state);
+
+	state->retval = state->recv_fn(subreq,
+				       &state->aio_state,
+				       state,
+				       &state->xattr_value);
+	TALLOC_FREE(subreq);
+	if (state->retval == -1) {
+		tevent_req_error(req, state->aio_state.error);
+		return;
+	}
+
+	tevent_req_done(req);
+}
+
+ssize_t smb_vfs_call_getxattrat_recv(struct tevent_req *req,
+				     struct vfs_aio_state *aio_state,
+				     TALLOC_CTX *mem_ctx,
+				     uint8_t **xattr_value)
+{
+	struct smb_vfs_call_getxattrat_state *state = tevent_req_data(
+		req, struct smb_vfs_call_getxattrat_state);
+	size_t xattr_size;
+
+	if (tevent_req_is_unix_error(req, &aio_state->error)) {
+		tevent_req_received(req);
+		return -1;
+	}
+
+	*aio_state = state->aio_state;
+	xattr_size = state->retval;
+	if (xattr_value != NULL) {
+		*xattr_value = talloc_move(mem_ctx, &state->xattr_value);
+	}
+
+	tevent_req_received(req);
+	return xattr_size;
+}
+
 ssize_t smb_vfs_call_fgetxattr(struct vfs_handle_struct *handle,
 			       struct files_struct *fsp, const char *name,
 			       void *value, size_t size)
-- 
2.17.1


From ed5e9fe983d6f83c2a84a3cf9ae9069f00a0ce20 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Tue, 13 Mar 2018 16:17:27 +0100
Subject: [PATCH 03/25] vfs_default: implement SMB_VFS_GETXATTRAT_SEND/RECV

Signed-off-by: Ralph Boehme <slow at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 source3/modules/vfs_default.c | 209 +++++++++++++++++++++++++++++++++-
 1 file changed, 207 insertions(+), 2 deletions(-)

diff --git a/source3/modules/vfs_default.c b/source3/modules/vfs_default.c
index d985cba9cb7f..0523644ac983 100644
--- a/source3/modules/vfs_default.c
+++ b/source3/modules/vfs_default.c
@@ -2762,6 +2762,211 @@ static ssize_t vfswrap_getxattr(struct vfs_handle_struct *handle,
 	return getxattr(smb_fname->base_name, name, value, size);
 }
 
+struct vfswrap_getxattrat_state {
+	int dirfd;
+	char *name;
+	size_t xattr_bufsize;
+	const char *xattr_name;
+	ssize_t xattr_size;
+	uint8_t *xattr_value;
+
+	struct vfs_aio_state vfs_aio_state;
+	SMBPROFILE_BYTES_ASYNC_STATE(profile_bytes);
+};
+
+static int vfswrap_getxattrat_state_destructor(
+		struct vfswrap_getxattrat_state *state)
+{
+	return -1;
+}
+
+static void vfswrap_getxattrat_do(void *private_data);
+static void vfswrap_getxattrat_done(struct tevent_req *subreq);
+
+static struct tevent_req *vfswrap_getxattrat_send(
+			TALLOC_CTX *mem_ctx,
+			const struct smb_vfs_ev_glue *evg,
+			struct vfs_handle_struct *handle,
+			files_struct *dir_fsp,
+			const struct smb_filename *smb_fname,
+			const char *xattr_name,
+			size_t alloc_hint)
+{
+	struct tevent_context *ev = smb_vfs_ev_glue_ev_ctx(evg);
+	struct pthreadpool_tevent *tp = smb_vfs_ev_glue_tp_chdir_safe(evg);
+	struct tevent_req *req = NULL;
+	struct tevent_req *subreq = NULL;
+	struct vfswrap_getxattrat_state *state = NULL;
+
+	req = tevent_req_create(mem_ctx, &state,
+				struct vfswrap_getxattrat_state);
+	if (req == NULL) {
+		return NULL;
+	}
+	*state = (struct vfswrap_getxattrat_state) {
+		.dirfd = dir_fsp->fh->fd,
+		.xattr_bufsize = alloc_hint,
+	};
+
+	SMBPROFILE_BYTES_ASYNC_START(syscall_asys_getxattrat, profile_p,
+				     state->profile_bytes, 0);
+
+	if (state->dirfd == -1) {
+		DBG_ERR("Need a valid directory fd\n");
+		tevent_req_error(req, EINVAL);
+		return tevent_req_post(req, ev);
+	}
+
+	/*
+	 * Now allocate all parameters from a memory context that won't go away
+	 * no matter what. These paremeters will get used in threads and we
+	 * can't reliably cancel threads, so all buffers passed to the threads
+	 * must not be freed before all referencing threads terminate.
+	 */
+
+	state->name = talloc_strdup(state, smb_fname->base_name);
+	if (tevent_req_nomem(state->name, req)) {
+		return tevent_req_post(req, ev);
+	}
+
+	state->xattr_name = talloc_strdup(state, xattr_name);
+	if (tevent_req_nomem(state->xattr_name, req)) {
+		return tevent_req_post(req, ev);
+	}
+
+	if (state->xattr_bufsize > 0) {
+		state->xattr_value = talloc_zero_array(state,
+						       uint8_t,
+						       state->xattr_bufsize);
+		if (tevent_req_nomem(state->xattr_value, req)) {
+			return tevent_req_post(req, ev);
+		}
+	}
+
+	SMBPROFILE_BYTES_ASYNC_SET_IDLE(state->profile_bytes);
+
+	subreq = pthreadpool_tevent_job_send(state,
+					     ev,
+					     tp,
+					     vfswrap_getxattrat_do,
+					     state);
+	if (tevent_req_nomem(subreq, req)) {
+		return tevent_req_post(req, ev);
+	}
+	tevent_req_set_callback(subreq, vfswrap_getxattrat_done, req);
+
+	talloc_set_destructor(state, vfswrap_getxattrat_state_destructor);
+
+	return req;
+}
+
+static void vfswrap_getxattrat_do(void *private_data)
+{
+	struct vfswrap_getxattrat_state *state = talloc_get_type_abort(
+		private_data, struct vfswrap_getxattrat_state);
+	struct timespec start_time;
+	struct timespec end_time;
+	int ret;
+
+	PROFILE_TIMESTAMP(&start_time);
+	SMBPROFILE_BYTES_ASYNC_SET_BUSY(state->profile_bytes);
+
+	/*
+	 * Here we simulate a getxattrat()
+	 * call using fchdir();getxattr()
+	 *
+	 * We don't need to revert the directory
+	 * change as pthreadpool_tevent wrapper
+	 * handlers that.
+	 */
+	SMB_ASSERT(pthreadpool_tevent_current_job_per_thread_cwd());
+
+	ret = fchdir(state->dirfd);
+	if (ret == -1) {
+		state->xattr_size = -1;
+		state->vfs_aio_state.error = errno;
+		goto end_profile;
+	}
+
+	state->xattr_size = getxattr(state->name,
+				     state->xattr_name,
+				     state->xattr_value,
+				     state->xattr_bufsize);
+	if (state->xattr_size == -1) {
+		state->vfs_aio_state.error = errno;
+	}
+
+end_profile:
+	PROFILE_TIMESTAMP(&end_time);
+	state->vfs_aio_state.duration = nsec_time_diff(&end_time, &start_time);
+	SMBPROFILE_BYTES_ASYNC_SET_IDLE(state->profile_bytes);
+}
+
+static void vfswrap_getxattrat_done(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	struct vfswrap_getxattrat_state *state = tevent_req_data(
+		req, struct vfswrap_getxattrat_state);
+	int ret;
+
+	ret = pthreadpool_tevent_job_recv(subreq);
+	TALLOC_FREE(subreq);
+	SMBPROFILE_BYTES_ASYNC_END(state->profile_bytes);
+	talloc_set_destructor(state, NULL);
+	if (tevent_req_error(req, ret)) {
+		return;
+	}
+
+	if (state->xattr_size == -1) {
+		tevent_req_error(req, state->vfs_aio_state.error);
+		return;
+	}
+
+	if (state->xattr_value == NULL) {
+		/*
+		 * The caller only wanted the size.
+		 */
+		tevent_req_done(req);
+		return;
+	}
+
+	/*
+	 * shrink the buffer to the returned size.
+	 * (can't fail). It means NULL if size is 0.
+	 */
+	state->xattr_value = talloc_realloc(state,
+					    state->xattr_value,
+					    uint8_t,
+					    state->xattr_size);
+
+	tevent_req_done(req);
+}
+
+static ssize_t vfswrap_getxattrat_recv(struct tevent_req *req,
+				       struct vfs_aio_state *aio_state,
+				       TALLOC_CTX *mem_ctx,
+				       uint8_t **xattr_value)
+{
+	struct vfswrap_getxattrat_state *state = tevent_req_data(
+		req, struct vfswrap_getxattrat_state);
+	ssize_t xattr_size;
+
+	if (tevent_req_is_unix_error(req, &aio_state->error)) {
+		tevent_req_received(req);
+		return -1;
+	}
+
+	*aio_state = state->vfs_aio_state;
+	xattr_size = state->xattr_size;
+	if (xattr_value != NULL) {
+		*xattr_value = talloc_move(mem_ctx, &state->xattr_value);
+	}
+
+	tevent_req_received(req);
+	return xattr_size;
+}
+
 static ssize_t vfswrap_fgetxattr(struct vfs_handle_struct *handle, struct files_struct *fsp, const char *name, void *value, size_t size)
 {
 	return fgetxattr(fsp->fh->fd, name, value, size);
@@ -2986,8 +3191,8 @@ static struct vfs_fn_pointers vfs_default_fns = {
 
 	/* EA operations. */
 	.getxattr_fn = vfswrap_getxattr,
-	.getxattrat_send_fn = vfs_not_implemented_getxattrat_send,
-	.getxattrat_recv_fn = vfs_not_implemented_getxattrat_recv,
+	.getxattrat_send_fn = vfswrap_getxattrat_send,
+	.getxattrat_recv_fn = vfswrap_getxattrat_recv,
 	.fgetxattr_fn = vfswrap_fgetxattr,
 	.listxattr_fn = vfswrap_listxattr,
 	.flistxattr_fn = vfswrap_flistxattr,
-- 
2.17.1


From 09faf93eb9dca458a82cdadc913a4d4e57f10321 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Thu, 21 Jun 2018 17:04:22 +0200
Subject: [PATCH 04/25] vfs_xattr_tdb: implement SMB_VFS_GETXATTRAT_SEND/RECV

Signed-off-by: Ralph Boehme <slow at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 source3/modules/vfs_xattr_tdb.c | 134 +++++++++++++++++++++++++++++++-
 1 file changed, 132 insertions(+), 2 deletions(-)

diff --git a/source3/modules/vfs_xattr_tdb.c b/source3/modules/vfs_xattr_tdb.c
index ceed3a9cbf8b..f67a86f80276 100644
--- a/source3/modules/vfs_xattr_tdb.c
+++ b/source3/modules/vfs_xattr_tdb.c
@@ -24,6 +24,7 @@
 #include "dbwrap/dbwrap.h"
 #include "dbwrap/dbwrap_open.h"
 #include "source3/lib/xattr_tdb.h"
+#include "lib/util/tevent_unix.h"
 
 #undef DBGC_CLASS
 #define DBGC_CLASS DBGC_VFS
@@ -103,6 +104,135 @@ static ssize_t xattr_tdb_getxattr(struct vfs_handle_struct *handle,
 	return xattr_size;
 }
 
+struct xattr_tdb_getxattrat_state {
+	struct vfs_aio_state vfs_aio_state;
+	ssize_t xattr_size;
+	uint8_t *xattr_value;
+};
+
+static struct tevent_req *xattr_tdb_getxattrat_send(
+			TALLOC_CTX *mem_ctx,
+			const struct smb_vfs_ev_glue *evg,
+			struct vfs_handle_struct *handle,
+			files_struct *dir_fsp,
+			const struct smb_filename *smb_fname,
+			const char *xattr_name,
+			size_t alloc_hint)
+{
+	struct tevent_context *ev = smb_vfs_ev_glue_ev_ctx(evg);
+	struct tevent_req *req = NULL;
+	struct xattr_tdb_getxattrat_state *state = NULL;
+	struct smb_filename *cwd = NULL;
+	struct db_context *db = NULL;
+	struct file_id id;
+	int ret;
+	int error;
+	int cwd_ret;
+	DATA_BLOB xattr_blob;
+
+	req = tevent_req_create(mem_ctx, &state,
+				struct xattr_tdb_getxattrat_state);
+	if (req == NULL) {
+		return NULL;
+	}
+	state->xattr_size = -1;
+
+	SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context,
+				if (!xattr_tdb_init(-1, state, &db)) {
+					tevent_req_error(req, EIO);
+					return tevent_req_post(req, ev);
+				});
+
+	cwd = SMB_VFS_GETWD(dir_fsp->conn, state);
+	if (tevent_req_nomem(cwd, req)) {
+		return tevent_req_post(req, ev);
+	}
+
+	ret = SMB_VFS_CHDIR(dir_fsp->conn, dir_fsp->fsp_name);
+	if (ret != 0) {
+		tevent_req_error(req, errno);
+		return tevent_req_post(req, ev);
+	}
+
+	ret = xattr_tdb_get_file_id(handle, smb_fname->base_name, &id);
+	error = errno;
+
+	cwd_ret = SMB_VFS_CHDIR(dir_fsp->conn, cwd);
+	SMB_ASSERT(cwd_ret == 0);
+
+	if (ret == -1) {
+		tevent_req_error(req, error);
+		return tevent_req_post(req, ev);
+	}
+
+	state->xattr_size = xattr_tdb_getattr(db,
+					      state,
+					      &id,
+					      xattr_name,
+					      &xattr_blob);
+	if (state->xattr_size == -1) {
+		tevent_req_error(req, errno);
+		return tevent_req_post(req, ev);
+	}
+
+	if (alloc_hint == 0) {
+		/*
+		 * The caller only wants to know the size.
+		 */
+		tevent_req_done(req);
+		return tevent_req_post(req, ev);
+	}
+
+	if (state->xattr_size == 0) {
+		/*
+		 * There's no data.
+		 */
+		tevent_req_done(req);
+		return tevent_req_post(req, ev);
+	}
+
+	if (xattr_blob.length > alloc_hint) {
+		/*
+		 * The data doesn't fit.
+		 */
+		state->xattr_size = -1;
+		tevent_req_error(req, ERANGE);
+		return tevent_req_post(req, ev);
+	}
+
+	/*
+	 * take the whole blob.
+	 */
+	state->xattr_value = xattr_blob.data;
+
+	tevent_req_done(req);
+	return tevent_req_post(req, ev);
+}
+
+static ssize_t xattr_tdb_getxattrat_recv(struct tevent_req *req,
+					 struct vfs_aio_state *aio_state,
+					 TALLOC_CTX *mem_ctx,
+					 uint8_t **xattr_value)
+{
+	struct xattr_tdb_getxattrat_state *state = tevent_req_data(
+		req, struct xattr_tdb_getxattrat_state);
+	ssize_t xattr_size;
+
+	if (tevent_req_is_unix_error(req, &aio_state->error)) {
+		tevent_req_received(req);
+		return -1;
+	}
+
+	*aio_state = state->vfs_aio_state;
+	xattr_size = state->xattr_size;
+	if (xattr_value != NULL) {
+		*xattr_value = talloc_move(mem_ctx, &state->xattr_value);
+	}
+
+	tevent_req_received(req);
+	return xattr_size;
+}
+
 static ssize_t xattr_tdb_fgetxattr(struct vfs_handle_struct *handle,
 				   struct files_struct *fsp,
 				   const char *name, void *value, size_t size)
@@ -602,8 +732,8 @@ static int xattr_tdb_connect(vfs_handle_struct *handle, const char *service,
 
 static struct vfs_fn_pointers vfs_xattr_tdb_fns = {
 	.getxattr_fn = xattr_tdb_getxattr,
-	.getxattrat_send_fn = vfs_not_implemented_getxattrat_send,
-	.getxattrat_recv_fn = vfs_not_implemented_getxattrat_recv,
+	.getxattrat_send_fn = xattr_tdb_getxattrat_send,
+	.getxattrat_recv_fn = xattr_tdb_getxattrat_recv,
 	.fgetxattr_fn = xattr_tdb_fgetxattr,
 	.setxattr_fn = xattr_tdb_setxattr,
 	.fsetxattr_fn = xattr_tdb_fsetxattr,
-- 
2.17.1


From 1f4685bedd12d57a0998dc4e1b8803ed22c0049c Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Thu, 15 Mar 2018 13:08:55 +0100
Subject: [PATCH 05/25] s3: vfs: add SMB_VFS_GET_DOS_ATTRIBUTES_SEND/RECV

Signed-off-by: Ralph Boehme <slow at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 examples/VFS/skel_opaque.c            |  48 ++++++++++
 examples/VFS/skel_transparent.c       |  82 ++++++++++++++++
 source3/include/vfs.h                 |  33 +++++++
 source3/include/vfs_macros.h          |  15 +++
 source3/modules/vfs_catia.c           |   2 +
 source3/modules/vfs_default.c         |   2 +
 source3/modules/vfs_full_audit.c      | 129 ++++++++++++++++++++++++++
 source3/modules/vfs_gpfs.c            |   2 +
 source3/modules/vfs_not_implemented.c |  49 ++++++++++
 source3/modules/vfs_offline.c         |   2 +
 source3/modules/vfs_time_audit.c      | 100 ++++++++++++++++++++
 source3/modules/vfs_tsmsm.c           |   2 +
 source3/smbd/vfs.c                    |  97 +++++++++++++++++++
 13 files changed, 563 insertions(+)

diff --git a/examples/VFS/skel_opaque.c b/examples/VFS/skel_opaque.c
index 971303436b01..054de50197e3 100644
--- a/examples/VFS/skel_opaque.c
+++ b/examples/VFS/skel_opaque.c
@@ -707,6 +707,52 @@ static NTSTATUS skel_get_dos_attributes(struct vfs_handle_struct *handle,
 	return NT_STATUS_NOT_IMPLEMENTED;
 }
 
+struct skel_get_dos_attributes_state {
+	struct vfs_aio_state aio_state;
+	uint32_t dosmode;
+};
+
+static struct tevent_req *skel_get_dos_attributes_send(
+			TALLOC_CTX *mem_ctx,
+			const struct smb_vfs_ev_glue *evg,
+			struct vfs_handle_struct *handle,
+			files_struct *dir_fsp,
+			struct smb_filename *smb_fname)
+{
+	struct tevent_context *ev = smb_vfs_ev_glue_ev_ctx(evg);
+	struct tevent_req *req = NULL;
+	struct skel_get_dos_attributes_state *state = NULL;
+
+	req = tevent_req_create(mem_ctx, &state,
+				struct skel_get_dos_attributes_state);
+	if (req == NULL) {
+		return NULL;
+	}
+
+	tevent_req_nterror(req, NT_STATUS_NOT_IMPLEMENTED);
+	return tevent_req_post(req, ev);
+}
+
+static NTSTATUS skel_get_dos_attributes_recv(struct tevent_req *req,
+					     struct vfs_aio_state *aio_state,
+					     uint32_t *dosmode)
+{
+	struct skel_get_dos_attributes_state *state =
+		tevent_req_data(req,
+		struct skel_get_dos_attributes_state);
+	NTSTATUS status;
+
+	if (tevent_req_is_nterror(req, &status)) {
+		tevent_req_received(req);
+		return status;
+	}
+
+	*aio_state = state->aio_state;
+	*dosmode = state->dosmode;
+	tevent_req_received(req);
+	return NT_STATUS_OK;
+}
+
 static NTSTATUS skel_fget_dos_attributes(struct vfs_handle_struct *handle,
 				struct files_struct *fsp,
 				uint32_t *dosmode)
@@ -1069,6 +1115,8 @@ static struct vfs_fn_pointers skel_opaque_fns = {
 
 	/* DOS attributes. */
 	.get_dos_attributes_fn = skel_get_dos_attributes,
+	.get_dos_attributes_send_fn = skel_get_dos_attributes_send,
+	.get_dos_attributes_recv_fn = skel_get_dos_attributes_recv,
 	.fget_dos_attributes_fn = skel_fget_dos_attributes,
 	.set_dos_attributes_fn = skel_set_dos_attributes,
 	.fset_dos_attributes_fn = skel_fset_dos_attributes,
diff --git a/examples/VFS/skel_transparent.c b/examples/VFS/skel_transparent.c
index 503c14edcc0a..cff52fa185ef 100644
--- a/examples/VFS/skel_transparent.c
+++ b/examples/VFS/skel_transparent.c
@@ -886,6 +886,86 @@ static NTSTATUS skel_get_dos_attributes(struct vfs_handle_struct *handle,
 				dosmode);
 }
 
+struct skel_get_dos_attributes_state {
+	struct vfs_aio_state aio_state;
+	uint32_t dosmode;
+};
+
+static void skel_get_dos_attributes_done(struct tevent_req *subreq);
+
+static struct tevent_req *skel_get_dos_attributes_send(
+			TALLOC_CTX *mem_ctx,
+			const struct smb_vfs_ev_glue *evg,
+			struct vfs_handle_struct *handle,
+			files_struct *dir_fsp,
+			struct smb_filename *smb_fname)
+{
+	struct tevent_context *ev = smb_vfs_ev_glue_ev_ctx(evg);
+	struct tevent_req *req = NULL;
+	struct skel_get_dos_attributes_state *state = NULL;
+	struct tevent_req *subreq = NULL;
+
+	req = tevent_req_create(mem_ctx, &state,
+				struct skel_get_dos_attributes_state);
+	if (req == NULL) {
+		return NULL;
+	}
+
+	subreq = SMB_VFS_NEXT_GET_DOS_ATTRIBUTES_SEND(mem_ctx,
+						      evg,
+						      handle,
+						      dir_fsp,
+						      smb_fname);
+	if (tevent_req_nomem(subreq, req)) {
+		return tevent_req_post(req, ev);
+	}
+	tevent_req_set_callback(subreq, skel_get_dos_attributes_done, req);
+
+	return req;
+}
+
+static void skel_get_dos_attributes_done(struct tevent_req *subreq)
+{
+	struct tevent_req *req =
+		tevent_req_callback_data(subreq,
+		struct tevent_req);
+	struct skel_get_dos_attributes_state *state =
+		tevent_req_data(req,
+		struct skel_get_dos_attributes_state);
+	NTSTATUS status;
+
+	status = SMB_VFS_NEXT_GET_DOS_ATTRIBUTES_RECV(subreq,
+						      &state->aio_state,
+						      &state->dosmode);
+	TALLOC_FREE(subreq);
+	if (tevent_req_nterror(req, status)) {
+		return;
+	}
+
+	tevent_req_done(req);
+	return;
+}
+
+static NTSTATUS skel_get_dos_attributes_recv(struct tevent_req *req,
+					     struct vfs_aio_state *aio_state,
+					     uint32_t *dosmode)
+{
+	struct skel_get_dos_attributes_state *state =
+		tevent_req_data(req,
+		struct skel_get_dos_attributes_state);
+	NTSTATUS status;
+
+	if (tevent_req_is_nterror(req, &status)) {
+		tevent_req_received(req);
+		return status;
+	}
+
+	*aio_state = state->aio_state;
+	*dosmode = state->dosmode;
+	tevent_req_received(req);
+	return NT_STATUS_OK;
+}
+
 static NTSTATUS skel_fget_dos_attributes(struct vfs_handle_struct *handle,
 				struct files_struct *fsp,
 				uint32_t *dosmode)
@@ -1298,6 +1378,8 @@ static struct vfs_fn_pointers skel_transparent_fns = {
 
 	/* DOS attributes. */
 	.get_dos_attributes_fn = skel_get_dos_attributes,
+	.get_dos_attributes_send_fn = skel_get_dos_attributes_send,
+	.get_dos_attributes_recv_fn = skel_get_dos_attributes_recv,
 	.fget_dos_attributes_fn = skel_fget_dos_attributes,
 	.set_dos_attributes_fn = skel_set_dos_attributes,
 	.fset_dos_attributes_fn = skel_fset_dos_attributes,
diff --git a/source3/include/vfs.h b/source3/include/vfs.h
index def802c9772b..4f3db6948964 100644
--- a/source3/include/vfs.h
+++ b/source3/include/vfs.h
@@ -260,6 +260,7 @@
 /* Version 40 - Introduce smb_vfs_ev_glue infrastructure. */
 /* Version 40 - Add vfs_not_implemented_* helper functions. */
 /* Version 40 - Add SMB_VFS_GETXATTRAT_SEND/RECV */
+/* Version 40 - Add SMB_VFS_GET_DOS_ATTRIBUTES_SEND/RECV */
 
 #define SMB_VFS_INTERFACE_VERSION 40
 
@@ -900,6 +901,18 @@ struct vfs_fn_pointers {
 					   struct files_struct *fsp,
 					   uint32_t dosmode);
 
+	struct tevent_req *(*get_dos_attributes_send_fn)(
+				TALLOC_CTX *mem_ctx,
+				const struct smb_vfs_ev_glue *evg,
+				struct vfs_handle_struct *handle,
+				files_struct *dir_fsp,
+				struct smb_filename *smb_fname);
+
+	NTSTATUS (*get_dos_attributes_recv_fn)(
+				struct tevent_req *req,
+				struct vfs_aio_state *aio_state,
+				uint32_t *dosmode);
+
 	/* NT ACL operations. */
 
 	NTSTATUS (*fget_nt_acl_fn)(struct vfs_handle_struct *handle,
@@ -1355,6 +1368,16 @@ NTSTATUS smb_vfs_call_set_dos_attributes(struct vfs_handle_struct *handle,
 NTSTATUS smb_vfs_call_fset_dos_attributes(struct vfs_handle_struct *handle,
 					  struct files_struct *fsp,
 					  uint32_t dosmode);
+struct tevent_req *smb_vfs_call_get_dos_attributes_send(
+			TALLOC_CTX *mem_ctx,
+			const struct smb_vfs_ev_glue *evg,
+			struct vfs_handle_struct *handle,
+			files_struct *dir_fsp,
+			struct smb_filename *smb_fname);
+NTSTATUS smb_vfs_call_get_dos_attributes_recv(
+			struct tevent_req *req,
+			struct vfs_aio_state *aio_state,
+			uint32_t *dosmode);
 struct tevent_req *smb_vfs_call_offload_read_send(
 	TALLOC_CTX *mem_ctx,
 	struct tevent_context *ev,
@@ -1827,6 +1850,16 @@ NTSTATUS vfs_not_implemented_readdir_attr(struct vfs_handle_struct *handle,
 NTSTATUS vfs_not_implemented_get_dos_attributes(struct vfs_handle_struct *handle,
 						struct smb_filename *smb_fname,
 						uint32_t *dosmode);
+struct tevent_req *vfs_not_implemented_get_dos_attributes_send(
+			TALLOC_CTX *mem_ctx,
+			const struct smb_vfs_ev_glue *evg,
+			struct vfs_handle_struct *handle,
+			files_struct *dir_fsp,
+			struct smb_filename *smb_fname);
+NTSTATUS vfs_not_implemented_get_dos_attributes_recv(
+			struct tevent_req *req,
+			struct vfs_aio_state *aio_state,
+			uint32_t *dosmode);
 NTSTATUS vfs_not_implemented_fget_dos_attributes(struct vfs_handle_struct *handle,
 						 struct files_struct *fsp,
 						 uint32_t *dosmode);
diff --git a/source3/include/vfs_macros.h b/source3/include/vfs_macros.h
index d4863536cc22..a13680c239e2 100644
--- a/source3/include/vfs_macros.h
+++ b/source3/include/vfs_macros.h
@@ -386,6 +386,21 @@
 #define SMB_VFS_NEXT_FGET_DOS_ATTRIBUTES(handle, fsp, attributes) \
 	smb_vfs_call_fget_dos_attributes((handle)->next, (fsp), (attributes))
 
+#define SMB_VFS_GET_DOS_ATTRIBUTES_SEND(mem_ctx, evg, dir_fsp, smb_fname) \
+	smb_vfs_call_get_dos_attributes_send((mem_ctx), (evg), \
+					     (dir_fsp)->conn->vfs_handles, \
+					     (dir_fsp), (smb_fname))
+#define SMB_VFS_GET_DOS_ATTRIBUTES_RECV(req, aio_state, dosmode) \
+	smb_vfs_call_get_dos_attributes_recv((req), (aio_state), (dosmode))
+
+#define SMB_VFS_NEXT_GET_DOS_ATTRIBUTES_SEND(mem_ctx, evg, handle, dir_fsp, \
+					     smb_fname) \
+	smb_vfs_call_get_dos_attributes_send((mem_ctx), (evg), \
+					     (handle)->next, \
+					     (dir_fsp), (smb_fname))
+#define SMB_VFS_NEXT_GET_DOS_ATTRIBUTES_RECV(req, aio_state, dosmode) \
+	smb_vfs_call_get_dos_attributes_recv((req), (aio_state), (dosmode))
+
 #define SMB_VFS_SET_DOS_ATTRIBUTES(conn, smb_fname, attributes) \
 	smb_vfs_call_set_dos_attributes((conn)->vfs_handles, (smb_fname), (attributes))
 #define SMB_VFS_NEXT_SET_DOS_ATTRIBUTES(handle, smb_fname, attributes) \
diff --git a/source3/modules/vfs_catia.c b/source3/modules/vfs_catia.c
index d2dddaf69213..12995dda9bfa 100644
--- a/source3/modules/vfs_catia.c
+++ b/source3/modules/vfs_catia.c
@@ -2447,6 +2447,8 @@ static struct vfs_fn_pointers vfs_catia_fns = {
 	.translate_name_fn = catia_translate_name,
 	.fsctl_fn = catia_fsctl,
 	.get_dos_attributes_fn = catia_get_dos_attributes,
+	.get_dos_attributes_send_fn = vfs_not_implemented_get_dos_attributes_send,
+	.get_dos_attributes_recv_fn = vfs_not_implemented_get_dos_attributes_recv,
 	.set_dos_attributes_fn = catia_set_dos_attributes,
 	.fset_dos_attributes_fn = catia_fset_dos_attributes,
 	.fget_dos_attributes_fn = catia_fget_dos_attributes,
diff --git a/source3/modules/vfs_default.c b/source3/modules/vfs_default.c
index 0523644ac983..81c707d15ecb 100644
--- a/source3/modules/vfs_default.c
+++ b/source3/modules/vfs_default.c
@@ -3164,6 +3164,8 @@ static struct vfs_fn_pointers vfs_default_fns = {
 	.set_dos_attributes_fn = vfswrap_set_dos_attributes,
 	.fset_dos_attributes_fn = vfswrap_fset_dos_attributes,
 	.get_dos_attributes_fn = vfswrap_get_dos_attributes,
+	.get_dos_attributes_send_fn = vfs_not_implemented_get_dos_attributes_send,
+	.get_dos_attributes_recv_fn = vfs_not_implemented_get_dos_attributes_recv,
 	.fget_dos_attributes_fn = vfswrap_fget_dos_attributes,
 	.offload_read_send_fn = vfswrap_offload_read_send,
 	.offload_read_recv_fn = vfswrap_offload_read_recv,
diff --git a/source3/modules/vfs_full_audit.c b/source3/modules/vfs_full_audit.c
index f28413b83c28..0fbf9ecafe32 100644
--- a/source3/modules/vfs_full_audit.c
+++ b/source3/modules/vfs_full_audit.c
@@ -69,6 +69,7 @@
 #include "lib/util/tevent_unix.h"
 #include "libcli/security/sddl.h"
 #include "passdb/machine_sid.h"
+#include "lib/util/tevent_ntstatus.h"
 
 static int vfs_full_audit_debug_level = DBGC_VFS;
 
@@ -178,6 +179,8 @@ typedef enum _vfs_op_type {
 
 	/* DOS attribute operations. */
 	SMB_VFS_OP_GET_DOS_ATTRIBUTES,
+	SMB_VFS_OP_GET_DOS_ATTRIBUTES_SEND,
+	SMB_VFS_OP_GET_DOS_ATTRIBUTES_RECV,
 	SMB_VFS_OP_FGET_DOS_ATTRIBUTES,
 	SMB_VFS_OP_SET_DOS_ATTRIBUTES,
 	SMB_VFS_OP_FSET_DOS_ATTRIBUTES,
@@ -317,6 +320,8 @@ static struct {
 	{ SMB_VFS_OP_SNAP_CREATE, "snap_create" },
 	{ SMB_VFS_OP_SNAP_DELETE, "snap_delete" },
 	{ SMB_VFS_OP_GET_DOS_ATTRIBUTES, "get_dos_attributes" },
+	{ SMB_VFS_OP_GET_DOS_ATTRIBUTES_SEND, "get_dos_attributes_send" },
+	{ SMB_VFS_OP_GET_DOS_ATTRIBUTES_RECV, "get_dos_attributes_recv" },
 	{ SMB_VFS_OP_FGET_DOS_ATTRIBUTES, "fget_dos_attributes" },
 	{ SMB_VFS_OP_SET_DOS_ATTRIBUTES, "set_dos_attributes" },
 	{ SMB_VFS_OP_FSET_DOS_ATTRIBUTES, "fset_dos_attributes" },
@@ -2064,6 +2069,128 @@ static NTSTATUS smb_full_audit_get_dos_attributes(
 	return status;
 }
 
+struct smb_full_audit_get_dos_attributes_state {
+	struct vfs_aio_state aio_state;
+	vfs_handle_struct *handle;
+	files_struct *dir_fsp;
+	const struct smb_filename *smb_fname;
+	uint32_t dosmode;
+};
+
+static void smb_full_audit_get_dos_attributes_done(struct tevent_req *subreq);
+
+static struct tevent_req *smb_full_audit_get_dos_attributes_send(
+		TALLOC_CTX *mem_ctx,
+		const struct smb_vfs_ev_glue *evg,
+		struct vfs_handle_struct *handle,
+		files_struct *dir_fsp,
+		struct smb_filename *smb_fname)
+{
+	struct tevent_context *ev = smb_vfs_ev_glue_ev_ctx(evg);
+	struct tevent_req *req = NULL;
+	struct smb_full_audit_get_dos_attributes_state *state = NULL;
+	struct tevent_req *subreq = NULL;
+
+	req = tevent_req_create(mem_ctx, &state,
+				struct smb_full_audit_get_dos_attributes_state);
+	if (req == NULL) {
+		do_log(SMB_VFS_OP_GET_DOS_ATTRIBUTES_SEND,
+		       false,
+		       handle,
+		       "%s/%s",
+		       fsp_str_do_log(dir_fsp),
+		       smb_fname->base_name);
+		return NULL;
+	}
+	*state = (struct smb_full_audit_get_dos_attributes_state) {
+		.handle = handle,
+		.dir_fsp = dir_fsp,
+		.smb_fname = smb_fname,
+	};
+
+	subreq = SMB_VFS_NEXT_GET_DOS_ATTRIBUTES_SEND(mem_ctx,
+						      evg,
+						      handle,
+						      dir_fsp,
+						      smb_fname);
+	if (tevent_req_nomem(subreq, req)) {
+		do_log(SMB_VFS_OP_GET_DOS_ATTRIBUTES_SEND,
+		       false,
+		       handle,
+		       "%s/%s",
+		       fsp_str_do_log(dir_fsp),
+		       smb_fname->base_name);
+		return tevent_req_post(req, ev);
+	}
+	tevent_req_set_callback(subreq,
+				smb_full_audit_get_dos_attributes_done,
+				req);
+
+	do_log(SMB_VFS_OP_GET_DOS_ATTRIBUTES_SEND,
+	       true,
+	       handle,
+	       "%s/%s",
+	       fsp_str_do_log(dir_fsp),
+	       smb_fname->base_name);
+
+	return req;
+}
+
+static void smb_full_audit_get_dos_attributes_done(struct tevent_req *subreq)
+{
+	struct tevent_req *req =
+		tevent_req_callback_data(subreq,
+		struct tevent_req);
+	struct smb_full_audit_get_dos_attributes_state *state =
+		tevent_req_data(req,
+		struct smb_full_audit_get_dos_attributes_state);
+	NTSTATUS status;
+
+	status = SMB_VFS_NEXT_GET_DOS_ATTRIBUTES_RECV(subreq,
+						      &state->aio_state,
+						      &state->dosmode);
+	TALLOC_FREE(subreq);
+	if (tevent_req_nterror(req, status)) {
+		return;
+	}
+
+	tevent_req_done(req);
+	return;
+}
+
+static NTSTATUS smb_full_audit_get_dos_attributes_recv(struct tevent_req *req,
+						struct vfs_aio_state *aio_state,
+						uint32_t *dosmode)
+{
+	struct smb_full_audit_get_dos_attributes_state *state =
+		tevent_req_data(req,
+		struct smb_full_audit_get_dos_attributes_state);
+	NTSTATUS status;
+
+	if (tevent_req_is_nterror(req, &status)) {
+		do_log(SMB_VFS_OP_GET_DOS_ATTRIBUTES_RECV,
+		       false,
+		       state->handle,
+		       "%s/%s",
+		       fsp_str_do_log(state->dir_fsp),
+		       state->smb_fname->base_name);
+		tevent_req_received(req);
+		return status;
+	}
+
+	do_log(SMB_VFS_OP_GET_DOS_ATTRIBUTES_RECV,
+	       true,
+	       state->handle,
+	       "%s/%s",
+	       fsp_str_do_log(state->dir_fsp),
+	       state->smb_fname->base_name);
+
+	*aio_state = state->aio_state;
+	*dosmode = state->dosmode;
+	tevent_req_received(req);
+	return NT_STATUS_OK;
+}
+
 static NTSTATUS smb_full_audit_fget_dos_attributes(
 				struct vfs_handle_struct *handle,
 				struct files_struct *fsp,
@@ -2716,6 +2843,8 @@ static struct vfs_fn_pointers vfs_full_audit_fns = {
 	.translate_name_fn = smb_full_audit_translate_name,
 	.fsctl_fn = smb_full_audit_fsctl,
 	.get_dos_attributes_fn = smb_full_audit_get_dos_attributes,
+	.get_dos_attributes_send_fn = smb_full_audit_get_dos_attributes_send,
+	.get_dos_attributes_recv_fn = smb_full_audit_get_dos_attributes_recv,
 	.fget_dos_attributes_fn = smb_full_audit_fget_dos_attributes,
 	.set_dos_attributes_fn = smb_full_audit_set_dos_attributes,
 	.fset_dos_attributes_fn = smb_full_audit_fset_dos_attributes,
diff --git a/source3/modules/vfs_gpfs.c b/source3/modules/vfs_gpfs.c
index d733df0aa5cf..5f21bc0826d5 100644
--- a/source3/modules/vfs_gpfs.c
+++ b/source3/modules/vfs_gpfs.c
@@ -2561,6 +2561,8 @@ static struct vfs_fn_pointers vfs_gpfs_fns = {
 	.linux_setlease_fn = vfs_gpfs_setlease,
 	.get_real_filename_fn = vfs_gpfs_get_real_filename,
 	.get_dos_attributes_fn = vfs_gpfs_get_dos_attributes,
+	.get_dos_attributes_send_fn = vfs_not_implemented_get_dos_attributes_send,
+	.get_dos_attributes_recv_fn = vfs_not_implemented_get_dos_attributes_recv,
 	.fget_dos_attributes_fn = vfs_gpfs_fget_dos_attributes,
 	.set_dos_attributes_fn = vfs_gpfs_set_dos_attributes,
 	.fset_dos_attributes_fn = vfs_gpfs_fset_dos_attributes,
diff --git a/source3/modules/vfs_not_implemented.c b/source3/modules/vfs_not_implemented.c
index bb4854d8bf8c..e20b7eb76ed9 100644
--- a/source3/modules/vfs_not_implemented.c
+++ b/source3/modules/vfs_not_implemented.c
@@ -710,6 +710,53 @@ NTSTATUS vfs_not_implemented_get_dos_attributes(struct vfs_handle_struct *handle
 	return NT_STATUS_NOT_IMPLEMENTED;
 }
 
+struct vfs_not_implemented_get_dos_attributes_state {
+	struct vfs_aio_state aio_state;
+	uint32_t dosmode;
+};
+
+struct tevent_req *vfs_not_implemented_get_dos_attributes_send(
+			TALLOC_CTX *mem_ctx,
+			const struct smb_vfs_ev_glue *evg,
+			struct vfs_handle_struct *handle,
+			files_struct *dir_fsp,
+			struct smb_filename *smb_fname)
+{
+	struct tevent_context *ev = smb_vfs_ev_glue_ev_ctx(evg);
+	struct tevent_req *req = NULL;
+	struct vfs_not_implemented_get_dos_attributes_state *state = NULL;
+
+	req = tevent_req_create(mem_ctx, &state,
+			struct vfs_not_implemented_get_dos_attributes_state);
+	if (req == NULL) {
+		return NULL;
+	}
+
+	tevent_req_nterror(req, NT_STATUS_NOT_IMPLEMENTED);
+	return tevent_req_post(req, ev);
+}
+
+NTSTATUS vfs_not_implemented_get_dos_attributes_recv(
+			struct tevent_req *req,
+			struct vfs_aio_state *aio_state,
+			uint32_t *dosmode)
+{
+	struct vfs_not_implemented_get_dos_attributes_state *state =
+		tevent_req_data(req,
+		struct vfs_not_implemented_get_dos_attributes_state);
+	NTSTATUS status;
+
+	if (tevent_req_is_nterror(req, &status)) {
+		tevent_req_received(req);
+		return status;
+	}
+
+	*aio_state = state->aio_state;
+	*dosmode = state->dosmode;
+	tevent_req_received(req);
+	return NT_STATUS_OK;
+}
+
 NTSTATUS vfs_not_implemented_fget_dos_attributes(struct vfs_handle_struct *handle,
 						 struct files_struct *fsp,
 						 uint32_t *dosmode)
@@ -1072,6 +1119,8 @@ static struct vfs_fn_pointers vfs_not_implemented_fns = {
 
 	/* DOS attributes. */
 	.get_dos_attributes_fn = vfs_not_implemented_get_dos_attributes,
+	.get_dos_attributes_send_fn = vfs_not_implemented_get_dos_attributes_send,
+	.get_dos_attributes_recv_fn = vfs_not_implemented_get_dos_attributes_recv,
 	.fget_dos_attributes_fn = vfs_not_implemented_fget_dos_attributes,
 	.set_dos_attributes_fn = vfs_not_implemented_set_dos_attributes,
 	.fset_dos_attributes_fn = vfs_not_implemented_fset_dos_attributes,
diff --git a/source3/modules/vfs_offline.c b/source3/modules/vfs_offline.c
index d70fad4c0424..0a7e8af469c9 100644
--- a/source3/modules/vfs_offline.c
+++ b/source3/modules/vfs_offline.c
@@ -46,6 +46,8 @@ static NTSTATUS offline_fget_dos_attributes(struct vfs_handle_struct *handle,
 static struct vfs_fn_pointers offline_fns = {
     .fs_capabilities_fn = offline_fs_capabilities,
 	.get_dos_attributes_fn = offline_get_dos_attributes,
+	.get_dos_attributes_send_fn = vfs_not_implemented_get_dos_attributes_send,
+	.get_dos_attributes_recv_fn = vfs_not_implemented_get_dos_attributes_recv,
 	.fget_dos_attributes_fn = offline_fget_dos_attributes,
 };
 
diff --git a/source3/modules/vfs_time_audit.c b/source3/modules/vfs_time_audit.c
index 07c14415312f..64e1f5d4d13c 100644
--- a/source3/modules/vfs_time_audit.c
+++ b/source3/modules/vfs_time_audit.c
@@ -1757,6 +1757,104 @@ static NTSTATUS smb_time_get_dos_attributes(struct vfs_handle_struct *handle,
 	return result;
 }
 
+struct smb_time_audit_get_dos_attributes_state {
+	struct vfs_aio_state aio_state;
+	files_struct *dir_fsp;
+	const struct smb_filename *smb_fname;
+	uint32_t dosmode;
+};
+
+static void smb_time_audit_get_dos_attributes_done(struct tevent_req *subreq);
+
+static struct tevent_req *smb_time_audit_get_dos_attributes_send(
+			TALLOC_CTX *mem_ctx,
+			const struct smb_vfs_ev_glue *evg,
+			struct vfs_handle_struct *handle,
+			files_struct *dir_fsp,
+			struct smb_filename *smb_fname)
+{
+	struct tevent_context *ev = smb_vfs_ev_glue_ev_ctx(evg);
+	struct tevent_req *req = NULL;
+	struct smb_time_audit_get_dos_attributes_state *state = NULL;
+	struct tevent_req *subreq = NULL;
+
+	req = tevent_req_create(mem_ctx, &state,
+				struct smb_time_audit_get_dos_attributes_state);
+	if (req == NULL) {
+		return NULL;
+	}
+	*state = (struct smb_time_audit_get_dos_attributes_state) {
+		.dir_fsp = dir_fsp,
+		.smb_fname = smb_fname,
+	};
+
+	subreq = SMB_VFS_NEXT_GET_DOS_ATTRIBUTES_SEND(mem_ctx,
+						      evg,
+						      handle,
+						      dir_fsp,
+						      smb_fname);
+	if (tevent_req_nomem(subreq, req)) {
+		return tevent_req_post(req, ev);
+	}
+	tevent_req_set_callback(subreq,
+				smb_time_audit_get_dos_attributes_done,
+				req);
+
+	return req;
+}
+
+static void smb_time_audit_get_dos_attributes_done(struct tevent_req *subreq)
+{
+	struct tevent_req *req =
+		tevent_req_callback_data(subreq,
+		struct tevent_req);
+	struct smb_time_audit_get_dos_attributes_state *state =
+		tevent_req_data(req,
+		struct smb_time_audit_get_dos_attributes_state);
+	NTSTATUS status;
+
+	status = SMB_VFS_NEXT_GET_DOS_ATTRIBUTES_RECV(subreq,
+						      &state->aio_state,
+						      &state->dosmode);
+	TALLOC_FREE(subreq);
+	if (tevent_req_nterror(req, status)) {
+		return;
+	}
+
+	tevent_req_done(req);
+	return;
+}
+
+static NTSTATUS smb_time_audit_get_dos_attributes_recv(struct tevent_req *req,
+						struct vfs_aio_state *aio_state,
+						uint32_t *dosmode)
+{
+	struct smb_time_audit_get_dos_attributes_state *state =
+		tevent_req_data(req,
+		struct smb_time_audit_get_dos_attributes_state);
+	NTSTATUS status;
+	double timediff;
+
+	timediff = state->aio_state.duration * 1.0e-9;
+
+	if (timediff > audit_timeout) {
+		smb_time_audit_log_at("async get_dos_attributes",
+				      timediff,
+				      state->dir_fsp,
+				      state->smb_fname);
+	}
+
+	if (tevent_req_is_nterror(req, &status)) {
+		tevent_req_received(req);
+		return status;
+	}
+
+	*aio_state = state->aio_state;
+	*dosmode = state->dosmode;
+	tevent_req_received(req);
+	return NT_STATUS_OK;
+}
+
 static NTSTATUS smb_time_fget_dos_attributes(struct vfs_handle_struct *handle,
 					struct files_struct *fsp,
 					uint32_t *dosmode)
@@ -2774,6 +2872,8 @@ static struct vfs_fn_pointers vfs_time_audit_fns = {
 	.translate_name_fn = smb_time_audit_translate_name,
 	.fsctl_fn = smb_time_audit_fsctl,
 	.get_dos_attributes_fn = smb_time_get_dos_attributes,
+	.get_dos_attributes_send_fn = smb_time_audit_get_dos_attributes_send,
+	.get_dos_attributes_recv_fn = smb_time_audit_get_dos_attributes_recv,
 	.fget_dos_attributes_fn = smb_time_fget_dos_attributes,
 	.set_dos_attributes_fn = smb_time_set_dos_attributes,
 	.fset_dos_attributes_fn = smb_time_fset_dos_attributes,
diff --git a/source3/modules/vfs_tsmsm.c b/source3/modules/vfs_tsmsm.c
index 99d11a53f9df..85a9bfdfa9c0 100644
--- a/source3/modules/vfs_tsmsm.c
+++ b/source3/modules/vfs_tsmsm.c
@@ -605,6 +605,8 @@ static struct vfs_fn_pointers tsmsm_fns = {
 	.set_dos_attributes_fn = tsmsm_set_dos_attributes,
 	.fset_dos_attributes_fn = tsmsm_fset_dos_attributes,
 	.get_dos_attributes_fn = tsmsm_get_dos_attributes,
+	.get_dos_attributes_send_fn = vfs_not_implemented_get_dos_attributes_send,
+	.get_dos_attributes_recv_fn = vfs_not_implemented_get_dos_attributes_recv,
 	.fget_dos_attributes_fn = tsmsm_fget_dos_attributes,
 };
 
diff --git a/source3/smbd/vfs.c b/source3/smbd/vfs.c
index 501eafe1c028..2687e3540b89 100644
--- a/source3/smbd/vfs.c
+++ b/source3/smbd/vfs.c
@@ -31,6 +31,7 @@
 #include "transfer_file.h"
 #include "ntioctl.h"
 #include "lib/util/tevent_unix.h"
+#include "lib/util/tevent_ntstatus.h"
 
 #undef DBGC_CLASS
 #define DBGC_CLASS DBGC_VFS
@@ -3250,6 +3251,102 @@ NTSTATUS smb_vfs_call_offload_write_recv(struct vfs_handle_struct *handle,
 	return handle->fns->offload_write_recv_fn(handle, req, copied);
 }
 
+struct smb_vfs_call_get_dos_attributes_state {
+	NTSTATUS (*recv_fn)(struct tevent_req *req,
+			    struct vfs_aio_state *aio_state,
+			    uint32_t *dosmode);
+	struct vfs_aio_state aio_state;
+	uint32_t dos_attributes;
+};
+
+static void smb_vfs_call_get_dos_attributes_done(struct tevent_req *subreq);
+
+struct tevent_req *smb_vfs_call_get_dos_attributes_send(
+			TALLOC_CTX *mem_ctx,
+			const struct smb_vfs_ev_glue *evg,
+			struct vfs_handle_struct *handle,
+			files_struct *dir_fsp,
+			struct smb_filename *smb_fname)
+{
+	struct tevent_req *req = NULL;
+	struct smb_vfs_call_get_dos_attributes_state *state = NULL;
+	struct tevent_req *subreq = NULL;
+	bool ok;
+
+	req = tevent_req_create(mem_ctx, &state,
+				struct smb_vfs_call_get_dos_attributes_state);
+	if (req == NULL) {
+		return NULL;
+	}
+
+	VFS_FIND(get_dos_attributes_send);
+	state->recv_fn = handle->fns->get_dos_attributes_recv_fn;
+
+	ok = smb_vfs_ev_glue_push_use(evg, req);
+	if (!ok) {
+		tevent_req_error(req, EIO);
+		return tevent_req_post(req, evg->return_ev);
+	}
+
+	subreq = handle->fns->get_dos_attributes_send_fn(mem_ctx,
+							 evg->next_glue,
+							 handle,
+							 dir_fsp,
+							 smb_fname);
+	smb_vfs_ev_glue_pop_use(evg);
+
+	if (tevent_req_nomem(subreq, req)) {
+		return tevent_req_post(req, evg->return_ev);
+	}
+	tevent_req_set_callback(subreq,
+				smb_vfs_call_get_dos_attributes_done,
+				req);
+
+	return req;
+}
+
+static void smb_vfs_call_get_dos_attributes_done(struct tevent_req *subreq)
+{
+	struct tevent_req *req =
+		tevent_req_callback_data(subreq,
+		struct tevent_req);
+	struct smb_vfs_call_get_dos_attributes_state *state =
+		tevent_req_data(req,
+		struct smb_vfs_call_get_dos_attributes_state);
+	NTSTATUS status;
+
+	status = state->recv_fn(subreq,
+				&state->aio_state,
+				&state->dos_attributes);
+	TALLOC_FREE(subreq);
+	if (tevent_req_nterror(req, status)) {
+		return;
+	}
+
+	tevent_req_done(req);
+}
+
+NTSTATUS smb_vfs_call_get_dos_attributes_recv(
+		struct tevent_req *req,
+		struct vfs_aio_state *aio_state,
+		uint32_t *dos_attributes)
+{
+	struct smb_vfs_call_get_dos_attributes_state *state =
+		tevent_req_data(req,
+		struct smb_vfs_call_get_dos_attributes_state);
+	NTSTATUS status;
+
+	if (tevent_req_is_nterror(req, &status)) {
+		tevent_req_received(req);
+		return status;
+	}
+
+	*aio_state = state->aio_state;
+	*dos_attributes = state->dos_attributes;
+	tevent_req_received(req);
+	return NT_STATUS_OK;
+}
+
 NTSTATUS smb_vfs_call_get_compression(vfs_handle_struct *handle,
 				      TALLOC_CTX *mem_ctx,
 				      struct files_struct *fsp,
-- 
2.17.1


From 556318f44992e023705b200f486624dc83566866 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Thu, 15 Mar 2018 10:56:28 +0100
Subject: [PATCH 06/25] smbd: split out public parse_dos_attribute_blob() from
 get_ea_dos_attribute()

In preperation of adding an async version of get_ea_dos_attribute() that
will then call parse_dos_attribute_blob() too.

Signed-off-by: Ralph Boehme <slow at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 source3/smbd/dosmode.c | 160 ++++++++++++++++++++++-------------------
 source3/smbd/proto.h   |   4 ++
 2 files changed, 90 insertions(+), 74 deletions(-)

diff --git a/source3/smbd/dosmode.c b/source3/smbd/dosmode.c
index 7ac876a47bf6..ed5ecc9120c5 100644
--- a/source3/smbd/dosmode.c
+++ b/source3/smbd/dosmode.c
@@ -260,16 +260,96 @@ static uint32_t dos_mode_from_sbuf(connection_struct *conn,
  This can also pull the create time into the stat struct inside smb_fname.
 ****************************************************************************/
 
+NTSTATUS parse_dos_attribute_blob(struct smb_filename *smb_fname,
+				  DATA_BLOB blob,
+				  uint32_t *pattr)
+{
+	struct xattr_DOSATTRIB dosattrib;
+	enum ndr_err_code ndr_err;
+	uint32_t dosattr;
+
+	ndr_err = ndr_pull_struct_blob(&blob, talloc_tos(), &dosattrib,
+			(ndr_pull_flags_fn_t)ndr_pull_xattr_DOSATTRIB);
+
+	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+		DBG_WARNING("bad ndr decode "
+			    "from EA on file %s: Error = %s\n",
+			    smb_fname_str_dbg(smb_fname),
+			    ndr_errstr(ndr_err));
+		return ndr_map_error2ntstatus(ndr_err);
+	}
+
+	DBG_DEBUG("%s attr = %s\n",
+		  smb_fname_str_dbg(smb_fname), dosattrib.attrib_hex);
+
+	switch (dosattrib.version) {
+	case 0xFFFF:
+		dosattr = dosattrib.info.compatinfoFFFF.attrib;
+		break;
+	case 1:
+		dosattr = dosattrib.info.info1.attrib;
+		if (!null_nttime(dosattrib.info.info1.create_time)) {
+			struct timespec create_time =
+				nt_time_to_unix_timespec(
+					dosattrib.info.info1.create_time);
+
+			update_stat_ex_create_time(&smb_fname->st,
+						   create_time);
+
+			DBG_DEBUG("file %s case 1 set btime %s\n",
+				  smb_fname_str_dbg(smb_fname),
+				  time_to_asc(convert_timespec_to_time_t(
+						      create_time)));
+		}
+		break;
+	case 2:
+		dosattr = dosattrib.info.oldinfo2.attrib;
+		/* Don't know what flags to check for this case. */
+		break;
+	case 3:
+		dosattr = dosattrib.info.info3.attrib;
+		if ((dosattrib.info.info3.valid_flags & XATTR_DOSINFO_CREATE_TIME) &&
+		    !null_nttime(dosattrib.info.info3.create_time)) {
+			struct timespec create_time =
+				nt_time_to_unix_timespec(
+					dosattrib.info.info3.create_time);
+
+			update_stat_ex_create_time(&smb_fname->st,
+						   create_time);
+
+			DBG_DEBUG("file %s case 3 set btime %s\n",
+				  smb_fname_str_dbg(smb_fname),
+				  time_to_asc(convert_timespec_to_time_t(
+						      create_time)));
+		}
+		break;
+	default:
+		DBG_WARNING("Badly formed DOSATTRIB on file %s - %s\n",
+			    smb_fname_str_dbg(smb_fname), blob.data);
+		/* Should this be INTERNAL_ERROR? */
+		return NT_STATUS_INVALID_PARAMETER;
+	}
+
+	if (S_ISDIR(smb_fname->st.st_ex_mode)) {
+		dosattr |= FILE_ATTRIBUTE_DIRECTORY;
+	}
+
+	/* FILE_ATTRIBUTE_SPARSE is valid on get but not on set. */
+	*pattr |= (uint32_t)(dosattr & (SAMBA_ATTRIBUTES_MASK|FILE_ATTRIBUTE_SPARSE));
+
+	dos_mode_debug_print(__func__, *pattr);
+
+	return NT_STATUS_OK;
+}
+
 NTSTATUS get_ea_dos_attribute(connection_struct *conn,
 			      struct smb_filename *smb_fname,
 			      uint32_t *pattr)
 {
-	struct xattr_DOSATTRIB dosattrib;
-	enum ndr_err_code ndr_err;
 	DATA_BLOB blob;
 	ssize_t sizeret;
 	fstring attrstr;
-	uint32_t dosattr;
+	NTSTATUS status;
 
 	if (!lp_store_dos_attributes(SNUM(conn))) {
 		return NT_STATUS_NOT_IMPLEMENTED;
@@ -327,78 +407,10 @@ NTSTATUS get_ea_dos_attribute(connection_struct *conn,
 	blob.data = (uint8_t *)attrstr;
 	blob.length = sizeret;
 
-	ndr_err = ndr_pull_struct_blob(&blob, talloc_tos(), &dosattrib,
-			(ndr_pull_flags_fn_t)ndr_pull_xattr_DOSATTRIB);
-
-	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
-		DEBUG(1,("get_ea_dos_attribute: bad ndr decode "
-			 "from EA on file %s: Error = %s\n",
-			 smb_fname_str_dbg(smb_fname),
-			 ndr_errstr(ndr_err)));
-		return ndr_map_error2ntstatus(ndr_err);
-	}
-
-	DEBUG(10,("get_ea_dos_attribute: %s attr = %s\n",
-		  smb_fname_str_dbg(smb_fname), dosattrib.attrib_hex));
-
-	switch (dosattrib.version) {
-		case 0xFFFF:
-			dosattr = dosattrib.info.compatinfoFFFF.attrib;
-			break;
-		case 1:
-			dosattr = dosattrib.info.info1.attrib;
-			if (!null_nttime(dosattrib.info.info1.create_time)) {
-				struct timespec create_time =
-					nt_time_to_unix_timespec(
-						dosattrib.info.info1.create_time);
-
-				update_stat_ex_create_time(&smb_fname->st,
-							create_time);
-
-				DEBUG(10,("get_ea_dos_attribute: file %s case 1 "
-					"set btime %s\n",
-					smb_fname_str_dbg(smb_fname),
-					time_to_asc(convert_timespec_to_time_t(
-						create_time)) ));
-			}
-			break;
-		case 2:
-			dosattr = dosattrib.info.oldinfo2.attrib;
-			/* Don't know what flags to check for this case. */
-			break;
-		case 3:
-			dosattr = dosattrib.info.info3.attrib;
-			if ((dosattrib.info.info3.valid_flags & XATTR_DOSINFO_CREATE_TIME) &&
-					!null_nttime(dosattrib.info.info3.create_time)) {
-				struct timespec create_time =
-					nt_time_to_unix_timespec(
-						dosattrib.info.info3.create_time);
-
-				update_stat_ex_create_time(&smb_fname->st,
-							create_time);
-
-				DEBUG(10,("get_ea_dos_attribute: file %s case 3 "
-					"set btime %s\n",
-					smb_fname_str_dbg(smb_fname),
-					time_to_asc(convert_timespec_to_time_t(
-						create_time)) ));
-			}
-			break;
-		default:
-			DEBUG(1,("get_ea_dos_attribute: Badly formed DOSATTRIB on "
-				 "file %s - %s\n", smb_fname_str_dbg(smb_fname),
-				 attrstr));
-			/* Should this be INTERNAL_ERROR? */
-	                return NT_STATUS_INVALID_PARAMETER;
-	}
-
-	if (S_ISDIR(smb_fname->st.st_ex_mode)) {
-		dosattr |= FILE_ATTRIBUTE_DIRECTORY;
+	status = parse_dos_attribute_blob(smb_fname, blob, pattr);
+	if (!NT_STATUS_IS_OK(status)) {
+		return status;
 	}
-	/* FILE_ATTRIBUTE_SPARSE is valid on get but not on set. */
-	*pattr |= (uint32_t)(dosattr & (SAMBA_ATTRIBUTES_MASK|FILE_ATTRIBUTE_SPARSE));
-
-	dos_mode_debug_print(__func__, *pattr);
 
 	return NT_STATUS_OK;
 }
diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h
index 0697c1b9c56d..7c426fdf5e34 100644
--- a/source3/smbd/proto.h
+++ b/source3/smbd/proto.h
@@ -290,6 +290,10 @@ struct timespec get_change_timespec(connection_struct *conn,
 				struct files_struct *fsp,
 				const struct smb_filename *smb_fname);
 
+NTSTATUS parse_dos_attribute_blob(struct smb_filename *smb_fname,
+				  DATA_BLOB blob,
+				  uint32_t *pattr);
+
 /* The following definitions come from smbd/error.c  */
 
 bool use_nt_status(void);
-- 
2.17.1


From acf43de96a262946e0e0c87daf1497df6158c506 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Thu, 15 Mar 2018 12:35:13 +0100
Subject: [PATCH 07/25] vfs_default: implement
 SMB_VFS_GET_DOS_ATTRIBUTES_SEND/RECV

Signed-off-by: Ralph Boehme <slow at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 source3/modules/vfs_default.c | 142 +++++++++++++++++++++++++++++++++-
 1 file changed, 140 insertions(+), 2 deletions(-)

diff --git a/source3/modules/vfs_default.c b/source3/modules/vfs_default.c
index 81c707d15ecb..a492c9d36276 100644
--- a/source3/modules/vfs_default.c
+++ b/source3/modules/vfs_default.c
@@ -1487,6 +1487,144 @@ static NTSTATUS vfswrap_get_dos_attributes(struct vfs_handle_struct *handle,
 	return get_ea_dos_attribute(handle->conn, smb_fname, dosmode);
 }
 
+struct vfswrap_get_dos_attributes_state {
+	struct vfs_aio_state aio_state;
+	connection_struct *conn;
+	TALLOC_CTX *mem_ctx;
+	const struct smb_vfs_ev_glue *evg;
+	files_struct *dir_fsp;
+	struct smb_filename *smb_fname;
+	uint32_t dosmode;
+	bool as_root;
+};
+
+static void vfswrap_get_dos_attributes_getxattr_done(struct tevent_req *subreq);
+
+static struct tevent_req *vfswrap_get_dos_attributes_send(
+			TALLOC_CTX *mem_ctx,
+			const struct smb_vfs_ev_glue *evg,
+			struct vfs_handle_struct *handle,
+			files_struct *dir_fsp,
+			struct smb_filename *smb_fname)
+{
+	struct tevent_context *ev = smb_vfs_ev_glue_ev_ctx(evg);
+	struct tevent_req *req = NULL;
+	struct tevent_req *subreq = NULL;
+	struct vfswrap_get_dos_attributes_state *state = NULL;
+
+	req = tevent_req_create(mem_ctx, &state,
+				struct vfswrap_get_dos_attributes_state);
+	if (req == NULL) {
+		return NULL;
+	}
+
+	*state = (struct vfswrap_get_dos_attributes_state) {
+		.conn = dir_fsp->conn,
+		.mem_ctx = mem_ctx,
+		.evg = evg,
+		.dir_fsp = dir_fsp,
+		.smb_fname = smb_fname,
+	};
+
+	subreq = SMB_VFS_GETXATTRAT_SEND(state,
+					 evg,
+					 dir_fsp,
+					 smb_fname,
+					 SAMBA_XATTR_DOS_ATTRIB,
+					 sizeof(fstring));
+	if (tevent_req_nomem(subreq, req)) {
+		return tevent_req_post(req, ev);
+	}
+	tevent_req_set_callback(subreq,
+				vfswrap_get_dos_attributes_getxattr_done,
+				req);
+
+	return req;
+}
+
+static void vfswrap_get_dos_attributes_getxattr_done(struct tevent_req *subreq)
+{
+	struct tevent_req *req =
+		tevent_req_callback_data(subreq,
+		struct tevent_req);
+	struct vfswrap_get_dos_attributes_state *state =
+		tevent_req_data(req,
+		struct vfswrap_get_dos_attributes_state);
+	ssize_t xattr_size;
+	DATA_BLOB blob = {0};
+	NTSTATUS status;
+
+	xattr_size = SMB_VFS_GETXATTRAT_RECV(subreq,
+					     &state->aio_state,
+					     state,
+					     &blob.data);
+	TALLOC_FREE(subreq);
+	if (xattr_size == -1) {
+		const struct smb_vfs_ev_glue *root_evg = NULL;
+
+		status = map_nt_error_from_unix(state->aio_state.error);
+
+		if (state->as_root) {
+			tevent_req_nterror(req, status);
+			return;
+		}
+		if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+			tevent_req_nterror(req, status);
+			return;
+		}
+
+		state->as_root = true;
+		root_evg = smb_vfs_ev_glue_get_root_glue(state->evg);
+
+		subreq = SMB_VFS_GETXATTRAT_SEND(state,
+						 root_evg,
+						 state->dir_fsp,
+						 state->smb_fname,
+						 SAMBA_XATTR_DOS_ATTRIB,
+						 sizeof(fstring));
+		if (tevent_req_nomem(subreq, req)) {
+			return;
+		}
+		tevent_req_set_callback(subreq,
+					vfswrap_get_dos_attributes_getxattr_done,
+					req);
+		return;
+	}
+
+	blob.length = xattr_size;
+
+	status = parse_dos_attribute_blob(state->smb_fname,
+					  blob,
+					  &state->dosmode);
+	if (!NT_STATUS_IS_OK(status)) {
+		tevent_req_nterror(req, status);
+		return;
+	}
+
+	tevent_req_done(req);
+	return;
+}
+
+static NTSTATUS vfswrap_get_dos_attributes_recv(struct tevent_req *req,
+						struct vfs_aio_state *aio_state,
+						uint32_t *dosmode)
+{
+	struct vfswrap_get_dos_attributes_state *state =
+		tevent_req_data(req,
+		struct vfswrap_get_dos_attributes_state);
+	NTSTATUS status;
+
+	if (tevent_req_is_nterror(req, &status)) {
+		tevent_req_received(req);
+		return status;
+	}
+
+	*aio_state = state->aio_state;
+	*dosmode = state->dosmode;
+	tevent_req_received(req);
+	return NT_STATUS_OK;
+}
+
 static NTSTATUS vfswrap_fget_dos_attributes(struct vfs_handle_struct *handle,
 					    struct files_struct *fsp,
 					    uint32_t *dosmode)
@@ -3164,8 +3302,8 @@ static struct vfs_fn_pointers vfs_default_fns = {
 	.set_dos_attributes_fn = vfswrap_set_dos_attributes,
 	.fset_dos_attributes_fn = vfswrap_fset_dos_attributes,
 	.get_dos_attributes_fn = vfswrap_get_dos_attributes,
-	.get_dos_attributes_send_fn = vfs_not_implemented_get_dos_attributes_send,
-	.get_dos_attributes_recv_fn = vfs_not_implemented_get_dos_attributes_recv,
+	.get_dos_attributes_send_fn = vfswrap_get_dos_attributes_send,
+	.get_dos_attributes_recv_fn = vfswrap_get_dos_attributes_recv,
 	.fget_dos_attributes_fn = vfswrap_fget_dos_attributes,
 	.offload_read_send_fn = vfswrap_offload_read_send,
 	.offload_read_recv_fn = vfswrap_offload_read_recv,
-- 
2.17.1


From 4a287e4d51fffe1ba01f0c4153a4450262a93ea8 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Wed, 25 Jul 2018 17:15:46 +0200
Subject: [PATCH 08/25] smbd: factor out dosmode post processing

We apply some post processing to the dosmode returned from the VFS
function. Move this to a seperate function which will be reused in the
next commit in the async dos_mode_send/recv post processing.

Best viewed with: git show --histogram

Signed-off-by: Ralph Boehme <slow at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 source3/smbd/dosmode.c | 79 ++++++++++++++++++++++++------------------
 1 file changed, 45 insertions(+), 34 deletions(-)

diff --git a/source3/smbd/dosmode.c b/source3/smbd/dosmode.c
index ed5ecc9120c5..141001c74a9f 100644
--- a/source3/smbd/dosmode.c
+++ b/source3/smbd/dosmode.c
@@ -665,33 +665,12 @@ static uint32_t dos_mode_from_name(connection_struct *conn,
 	return result;
 }
 
-/****************************************************************************
- Change a unix mode to a dos mode.
- May also read the create timespec into the stat struct in smb_fname
- if "store dos attributes" is true.
-****************************************************************************/
-
-uint32_t dos_mode(connection_struct *conn, struct smb_filename *smb_fname)
+static uint32_t dos_mode_post(uint32_t dosmode,
+			      connection_struct *conn,
+			      struct smb_filename *smb_fname,
+			      const char *func)
 {
-	uint32_t result = 0;
-	NTSTATUS status = NT_STATUS_OK;
-
-	DEBUG(8,("dos_mode: %s\n", smb_fname_str_dbg(smb_fname)));
-
-	if (!VALID_STAT(smb_fname->st)) {
-		return 0;
-	}
-
-	/* Get the DOS attributes via the VFS if we can */
-	status = SMB_VFS_GET_DOS_ATTRIBUTES(conn, smb_fname, &result);
-	if (!NT_STATUS_IS_OK(status)) {
-		/*
-		 * Only fall back to using UNIX modes if we get NOT_IMPLEMENTED.
-		 */
-		if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
-			result |= dos_mode_from_sbuf(conn, smb_fname);
-		}
-	}
+	NTSTATUS status;
 
 	/*
 	 * According to MS-FSA a stream name does not have
@@ -711,31 +690,63 @@ uint32_t dos_mode(connection_struct *conn, struct smb_filename *smb_fname)
 			/*
 			 * Non-default stream name, not a posix path.
 			 */
-			result &= ~(FILE_ATTRIBUTE_DIRECTORY);
+			dosmode &= ~(FILE_ATTRIBUTE_DIRECTORY);
 		}
 	}
 
 	if (conn->fs_capabilities & FILE_FILE_COMPRESSION) {
 		bool compressed = false;
+
 		status = dos_mode_check_compressed(conn, smb_fname,
 						   &compressed);
 		if (NT_STATUS_IS_OK(status) && compressed) {
-			result |= FILE_ATTRIBUTE_COMPRESSED;
+			dosmode |= FILE_ATTRIBUTE_COMPRESSED;
 		}
 	}
 
-	result |= dos_mode_from_name(conn, smb_fname, result);
+	dosmode |= dos_mode_from_name(conn, smb_fname, dosmode);
 
 	if (S_ISDIR(smb_fname->st.st_ex_mode)) {
-		result |= FILE_ATTRIBUTE_DIRECTORY;
-	} else if (result == 0) {
-		result = FILE_ATTRIBUTE_NORMAL;
+		dosmode |= FILE_ATTRIBUTE_DIRECTORY;
+	} else if (dosmode == 0) {
+		dosmode = FILE_ATTRIBUTE_NORMAL;
 	}
 
-	result = filter_mode_by_protocol(result);
+	dosmode = filter_mode_by_protocol(dosmode);
 
-	dos_mode_debug_print(__func__, result);
+	dos_mode_debug_print(func, dosmode);
+	return dosmode;
+}
+
+/****************************************************************************
+ Change a unix mode to a dos mode.
+ May also read the create timespec into the stat struct in smb_fname
+ if "store dos attributes" is true.
+****************************************************************************/
+
+uint32_t dos_mode(connection_struct *conn, struct smb_filename *smb_fname)
+{
+	uint32_t result = 0;
+	NTSTATUS status = NT_STATUS_OK;
+
+	DEBUG(8,("dos_mode: %s\n", smb_fname_str_dbg(smb_fname)));
+
+	if (!VALID_STAT(smb_fname->st)) {
+		return 0;
+	}
+
+	/* Get the DOS attributes via the VFS if we can */
+	status = SMB_VFS_GET_DOS_ATTRIBUTES(conn, smb_fname, &result);
+	if (!NT_STATUS_IS_OK(status)) {
+		/*
+		 * Only fall back to using UNIX modes if we get NOT_IMPLEMENTED.
+		 */
+		if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
+			result |= dos_mode_from_sbuf(conn, smb_fname);
+		}
+	}
 
+	result = dos_mode_post(result, conn, smb_fname, __func__);
 	return result;
 }
 
-- 
2.17.1


From 48631438ca3c38cb2b118f844b68aa721cff6951 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Thu, 15 Mar 2018 15:21:53 +0100
Subject: [PATCH 09/25] smbd: add dos_mode_at_send/recv()

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 source3/smbd/dosmode.c | 137 +++++++++++++++++++++++++++++++++++++++++
 source3/smbd/proto.h   |   5 ++
 2 files changed, 142 insertions(+)

diff --git a/source3/smbd/dosmode.c b/source3/smbd/dosmode.c
index 141001c74a9f..966679a2c9c4 100644
--- a/source3/smbd/dosmode.c
+++ b/source3/smbd/dosmode.c
@@ -25,6 +25,7 @@
 #include "../libcli/security/security.h"
 #include "smbd/smbd.h"
 #include "lib/param/loadparm.h"
+#include "lib/util/tevent_ntstatus.h"
 
 static NTSTATUS get_file_handle_for_metadata(connection_struct *conn,
 				const struct smb_filename *smb_fname,
@@ -750,6 +751,142 @@ uint32_t dos_mode(connection_struct *conn, struct smb_filename *smb_fname)
 	return result;
 }
 
+struct dos_mode_at_state {
+	files_struct *dir_fsp;
+	struct smb_filename *smb_fname;
+	uint32_t dosmode;
+};
+
+static void dos_mode_at_vfs_get_dosmode_done(struct tevent_req *subreq);
+
+struct tevent_req *dos_mode_at_send(TALLOC_CTX *mem_ctx,
+				    struct smb_vfs_ev_glue *evg,
+				    files_struct *dir_fsp,
+				    struct smb_filename *smb_fname)
+{
+	struct tevent_context *ev = smb_vfs_ev_glue_ev_ctx(evg);
+	struct tevent_req *req = NULL;
+	struct dos_mode_at_state *state = NULL;
+	struct tevent_req *subreq = NULL;
+
+	DBG_DEBUG("%s\n", smb_fname_str_dbg(smb_fname));
+
+	req = tevent_req_create(mem_ctx, &state,
+				struct dos_mode_at_state);
+	if (req == NULL) {
+		return NULL;
+	}
+
+	*state = (struct dos_mode_at_state) {
+		.dir_fsp = dir_fsp,
+		.smb_fname = smb_fname,
+	};
+
+	if (!VALID_STAT(smb_fname->st)) {
+		tevent_req_done(req);
+		return tevent_req_post(req, ev);
+	}
+
+	subreq = SMB_VFS_GET_DOS_ATTRIBUTES_SEND(state,
+						 evg,
+						 dir_fsp,
+						 smb_fname);
+	if (tevent_req_nomem(subreq, req)) {
+		return tevent_req_post(req, ev);
+	}
+	tevent_req_set_callback(subreq, dos_mode_at_vfs_get_dosmode_done, req);
+
+	return req;
+}
+
+static void dos_mode_at_vfs_get_dosmode_done(struct tevent_req *subreq)
+{
+	struct tevent_req *req =
+		tevent_req_callback_data(subreq,
+		struct tevent_req);
+	struct dos_mode_at_state *state =
+		tevent_req_data(req,
+		struct dos_mode_at_state);
+	char *path = NULL;
+	struct smb_filename *smb_path = NULL;
+	struct vfs_aio_state aio_state;
+	NTSTATUS status;
+
+	status = SMB_VFS_GET_DOS_ATTRIBUTES_RECV(subreq,
+						 &aio_state,
+						 &state->dosmode);
+	TALLOC_FREE(subreq);
+	if (!NT_STATUS_IS_OK(status)) {
+		/*
+		 * Both the sync dos_mode() as well as the async
+		 * dos_mode_at_[send|recv] have no real error return, the only
+		 * unhandled error is when the stat info in smb_fname is not
+		 * valid (cf the checks in dos_mode() and dos_mode_at_send().
+		 *
+		 * If SMB_VFS_GET_DOS_ATTRIBUTES[_SEND|_RECV] fails we must call
+		 * dos_mode_post() which also does the mapping of a last ressort
+		 * from S_IFMT(st_mode).
+		 *
+		 * Only if we get NT_STATUS_NOT_IMPLEMENTED from a stacked VFS
+		 * module we must fallback to sync processing.
+		 */
+		if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
+			/*
+			 * state->dosmode should still be 0, but reset
+			 * it to be sure.
+			 */
+			state->dosmode = 0;
+			status = NT_STATUS_OK;
+		}
+	}
+	if (NT_STATUS_IS_OK(status)) {
+		state->dosmode = dos_mode_post(state->dosmode,
+					       state->dir_fsp->conn,
+					       state->smb_fname,
+					       __func__);
+		tevent_req_done(req);
+		return;
+	}
+
+	/*
+	 * Fall back to sync dos_mode() if we got NOT_IMPLEMENTED.
+	 */
+
+	path = talloc_asprintf(state,
+			       "%s/%s",
+			       state->dir_fsp->fsp_name->base_name,
+			       state->smb_fname->base_name);
+	if (tevent_req_nomem(path, req)) {
+		return;
+	}
+
+	smb_path = synthetic_smb_fname(state, path, NULL, NULL, 0);
+	if (tevent_req_nomem(path, req)) {
+		return;
+	}
+
+	state->dosmode = dos_mode(state->dir_fsp->conn, smb_path);
+	tevent_req_done(req);
+	return;
+}
+
+NTSTATUS dos_mode_at_recv(struct tevent_req *req, uint32_t *dosmode)
+{
+	struct dos_mode_at_state *state =
+		tevent_req_data(req,
+		struct dos_mode_at_state);
+	NTSTATUS status;
+
+	if (tevent_req_is_nterror(req, &status)) {
+		tevent_req_received(req);
+		return status;
+	}
+
+	*dosmode = state->dosmode;
+	tevent_req_received(req);
+	return NT_STATUS_OK;
+}
+
 /*******************************************************************
  chmod a file - but preserve some bits.
  If "store dos attributes" is also set it will store the create time
diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h
index 7c426fdf5e34..2980935e5990 100644
--- a/source3/smbd/proto.h
+++ b/source3/smbd/proto.h
@@ -260,6 +260,11 @@ mode_t unix_mode(connection_struct *conn, int dosmode,
 uint32_t dos_mode_msdfs(connection_struct *conn,
 		      const struct smb_filename *smb_fname);
 uint32_t dos_mode(connection_struct *conn, struct smb_filename *smb_fname);
+struct tevent_req *dos_mode_at_send(TALLOC_CTX *mem_ctx,
+				    struct smb_vfs_ev_glue *evg,
+				    files_struct *dir_fsp,
+				    struct smb_filename *smb_fname);
+NTSTATUS dos_mode_at_recv(struct tevent_req *req, uint32_t *dosmode);
 int file_set_dosmode(connection_struct *conn, struct smb_filename *smb_fname,
 		     uint32_t dosmode, const char *parent_dir, bool newfile);
 NTSTATUS file_set_sparse(connection_struct *conn,
-- 
2.17.1


From 351ffc7214c3b5312afd72dd07d2b0df4fe9f7dd Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Thu, 15 Mar 2018 15:50:41 +0100
Subject: [PATCH 10/25] smbd: add "get_dosmode" argument to
 smbd_dirptr_lanman2_entry()

Signed-off-by: Ralph Boehme <slow at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 source3/smbd/globals.h              | 1 +
 source3/smbd/smb2_query_directory.c | 1 +
 source3/smbd/trans2.c               | 3 ++-
 3 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/source3/smbd/globals.h b/source3/smbd/globals.h
index 19a130e64f64..c0c41250fcb0 100644
--- a/source3/smbd/globals.h
+++ b/source3/smbd/globals.h
@@ -201,6 +201,7 @@ NTSTATUS smbd_dirptr_lanman2_entry(TALLOC_CTX *ctx,
 			       int requires_resume_key,
 			       bool dont_descend,
 			       bool ask_sharemode,
+			       bool get_dosmode,
 			       uint8_t align,
 			       bool do_pad,
 			       char **ppdata,
diff --git a/source3/smbd/smb2_query_directory.c b/source3/smbd/smb2_query_directory.c
index 50a5bca5d7b2..7584598c2c39 100644
--- a/source3/smbd/smb2_query_directory.c
+++ b/source3/smbd/smb2_query_directory.c
@@ -522,6 +522,7 @@ static struct tevent_req *smbd_smb2_query_directory_send(TALLOC_CTX *mem_ctx,
 					       false, /* requires_resume_key */
 					       dont_descend,
 					       ask_sharemode,
+					       true,
 					       8, /* align to 8 bytes */
 					       false, /* no padding */
 					       &pdata,
diff --git a/source3/smbd/trans2.c b/source3/smbd/trans2.c
index af7a0d724842..118f44c6e24b 100644
--- a/source3/smbd/trans2.c
+++ b/source3/smbd/trans2.c
@@ -2450,6 +2450,7 @@ NTSTATUS smbd_dirptr_lanman2_entry(TALLOC_CTX *ctx,
 			       int requires_resume_key,
 			       bool dont_descend,
 			       bool ask_sharemode,
+			       bool get_dosmode,
 			       uint8_t align,
 			       bool do_pad,
 			       char **ppdata,
@@ -2588,7 +2589,7 @@ static NTSTATUS get_lanman2_dir_entry(TALLOC_CTX *ctx,
 	return smbd_dirptr_lanman2_entry(ctx, conn, dirptr, flags2,
 					 path_mask, dirtype, info_level,
 					 requires_resume_key, dont_descend, ask_sharemode,
-					 align, do_pad,
+					 true, align, do_pad,
 					 ppdata, base_data, end_data,
 					 space_remaining,
 					 got_exact_match,
-- 
2.17.1


From ff2418542926f9cb6ed60dbf435b98fba7c4d287 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Thu, 15 Mar 2018 16:08:11 +0100
Subject: [PATCH 11/25] smbd: pass get_dosmode to smbd_dirptr_get_entry()

Signed-off-by: Ralph Boehme <slow at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 source3/smbd/dir.c     | 2 ++
 source3/smbd/globals.h | 1 +
 source3/smbd/trans2.c  | 1 +
 3 files changed, 4 insertions(+)

diff --git a/source3/smbd/dir.c b/source3/smbd/dir.c
index 8bb66b819361..8f2f22d3eb13 100644
--- a/source3/smbd/dir.c
+++ b/source3/smbd/dir.c
@@ -1087,6 +1087,7 @@ bool smbd_dirptr_get_entry(TALLOC_CTX *ctx,
 			   uint32_t dirtype,
 			   bool dont_descend,
 			   bool ask_sharemode,
+			   bool get_dosmode,
 			   bool (*match_fn)(TALLOC_CTX *ctx,
 					    void *private_data,
 					    const char *dname,
@@ -1361,6 +1362,7 @@ bool get_dir_entry(TALLOC_CTX *ctx,
 				   dirtype,
 				   check_descend,
 				   ask_sharemode,
+				   true,
 				   smbd_dirptr_8_3_match_fn,
 				   smbd_dirptr_8_3_mode_fn,
 				   conn,
diff --git a/source3/smbd/globals.h b/source3/smbd/globals.h
index c0c41250fcb0..e8a1e53a4193 100644
--- a/source3/smbd/globals.h
+++ b/source3/smbd/globals.h
@@ -176,6 +176,7 @@ bool smbd_dirptr_get_entry(TALLOC_CTX *ctx,
 			   uint32_t dirtype,
 			   bool dont_descend,
 			   bool ask_sharemode,
+			   bool get_dosmode,
 			   bool (*match_fn)(TALLOC_CTX *ctx,
 					    void *private_data,
 					    const char *dname,
diff --git a/source3/smbd/trans2.c b/source3/smbd/trans2.c
index 118f44c6e24b..cd147f031dae 100644
--- a/source3/smbd/trans2.c
+++ b/source3/smbd/trans2.c
@@ -2505,6 +2505,7 @@ NTSTATUS smbd_dirptr_lanman2_entry(TALLOC_CTX *ctx,
 				   dirtype,
 				   dont_descend,
 				   ask_sharemode,
+				   get_dosmode,
 				   smbd_dirptr_lanman2_match_fn,
 				   smbd_dirptr_lanman2_mode_fn,
 				   &state,
-- 
2.17.1


From dd5e6ec4e5a707b51e50b29475c43720b458f7de Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Thu, 15 Mar 2018 16:48:38 +0100
Subject: [PATCH 12/25] smbd: pass get_dosmode to mode_fn in
 smbd_dirptr_get_entry()

This finally uses "get_dosmode" as passed in from the SMB2 layer, but
all callers still pass true, so no change in behaviour.

Signed-off-by: Ralph Boehme <slow at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 source3/smbd/dir.c     | 4 +++-
 source3/smbd/globals.h | 1 +
 source3/smbd/trans2.c  | 3 ++-
 3 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/source3/smbd/dir.c b/source3/smbd/dir.c
index 8f2f22d3eb13..c3af5233e986 100644
--- a/source3/smbd/dir.c
+++ b/source3/smbd/dir.c
@@ -1096,6 +1096,7 @@ bool smbd_dirptr_get_entry(TALLOC_CTX *ctx,
 			   bool (*mode_fn)(TALLOC_CTX *ctx,
 					   void *private_data,
 					   struct smb_filename *smb_fname,
+					   bool get_dosmode,
 					   uint32_t *_mode),
 			   void *private_data,
 			   char **_fname,
@@ -1190,7 +1191,7 @@ bool smbd_dirptr_get_entry(TALLOC_CTX *ctx,
 			.base_name = pathreal, .st = sbuf
 		};
 
-		ok = mode_fn(ctx, private_data, &smb_fname, &mode);
+		ok = mode_fn(ctx, private_data, &smb_fname, get_dosmode, &mode);
 		if (!ok) {
 			TALLOC_FREE(dname);
 			TALLOC_FREE(fname);
@@ -1319,6 +1320,7 @@ static bool smbd_dirptr_8_3_match_fn(TALLOC_CTX *ctx,
 static bool smbd_dirptr_8_3_mode_fn(TALLOC_CTX *ctx,
 				    void *private_data,
 				    struct smb_filename *smb_fname,
+				    bool get_dosmode,
 				    uint32_t *_mode)
 {
 	connection_struct *conn = (connection_struct *)private_data;
diff --git a/source3/smbd/globals.h b/source3/smbd/globals.h
index e8a1e53a4193..d151d33b968a 100644
--- a/source3/smbd/globals.h
+++ b/source3/smbd/globals.h
@@ -185,6 +185,7 @@ bool smbd_dirptr_get_entry(TALLOC_CTX *ctx,
 			   bool (*mode_fn)(TALLOC_CTX *ctx,
 					   void *private_data,
 					   struct smb_filename *smb_fname,
+					   bool get_dosmode,
 					   uint32_t *_mode),
 			   void *private_data,
 			   char **_fname,
diff --git a/source3/smbd/trans2.c b/source3/smbd/trans2.c
index cd147f031dae..238cd9f1cef8 100644
--- a/source3/smbd/trans2.c
+++ b/source3/smbd/trans2.c
@@ -1708,6 +1708,7 @@ static bool smbd_dirptr_lanman2_match_fn(TALLOC_CTX *ctx,
 static bool smbd_dirptr_lanman2_mode_fn(TALLOC_CTX *ctx,
 					void *private_data,
 					struct smb_filename *smb_fname,
+					bool get_dosmode,
 					uint32_t *_mode)
 {
 	struct smbd_dirptr_lanman2_state *state =
@@ -1741,7 +1742,7 @@ static bool smbd_dirptr_lanman2_mode_fn(TALLOC_CTX *ctx,
 
 	if (ms_dfs_link) {
 		mode = dos_mode_msdfs(state->conn, smb_fname);
-	} else {
+	} else if (get_dosmode) {
 		mode = dos_mode(state->conn, smb_fname);
 	}
 
-- 
2.17.1


From 4402b57f58e6f60462b3b8acc2eac075c690222b Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Thu, 15 Mar 2018 18:57:50 +0100
Subject: [PATCH 13/25] smbd: rework error exit in smbd_dirptr_lanman2_entry()

The next commit will add code that must be run if status is NT_STATUS_OK
or STATUS_MORE_ENTRIES.

Signed-off-by: Ralph Boehme <slow at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 source3/smbd/trans2.c | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/source3/smbd/trans2.c b/source3/smbd/trans2.c
index 238cd9f1cef8..005f55f77b78 100644
--- a/source3/smbd/trans2.c
+++ b/source3/smbd/trans2.c
@@ -2550,11 +2550,15 @@ NTSTATUS smbd_dirptr_lanman2_entry(TALLOC_CTX *ctx,
 
 	TALLOC_FREE(fname);
 	TALLOC_FREE(smb_fname);
-	if (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
-		dptr_SeekDir(dirptr, prev_dirpos);
+
+	if (!NT_STATUS_IS_OK(status) &&
+	    !NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES))
+	{
 		return status;
 	}
-	if (!NT_STATUS_IS_OK(status)) {
+
+	if (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
+		dptr_SeekDir(dirptr, prev_dirpos);
 		return status;
 	}
 
-- 
2.17.1


From d8e43d30515de5f7b163bc42c65f014c199fe825 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Tue, 3 Jul 2018 11:25:57 +0200
Subject: [PATCH 14/25] smbd: factor out smb2_query_directory_next_entry() from
 smbd_smb2_query_directory_send()

This paves the way for adding async functions into the enumeration loop.

Best viewed with: git show -w

Signed-off-by: Ralph Boehme <slow at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 source3/smbd/smb2_query_directory.c | 350 +++++++++++++++-------------
 1 file changed, 192 insertions(+), 158 deletions(-)

diff --git a/source3/smbd/smb2_query_directory.c b/source3/smbd/smb2_query_directory.c
index 7584598c2c39..6577ab81eaaf 100644
--- a/source3/smbd/smb2_query_directory.c
+++ b/source3/smbd/smb2_query_directory.c
@@ -205,15 +205,32 @@ static struct tevent_req *fetch_write_time_send(TALLOC_CTX *mem_ctx,
 						bool *stop);
 static NTSTATUS fetch_write_time_recv(struct tevent_req *req);
 
-
 struct smbd_smb2_query_directory_state {
 	struct tevent_context *ev;
 	struct smbd_smb2_request *smb2req;
-	uint64_t async_count;
+	uint64_t async_sharemode_count;
 	uint32_t find_async_delay_usec;
 	DATA_BLOB out_output_buffer;
+	struct smb_request *smbreq;
+	int in_output_buffer_length;
+	struct files_struct *fsp;
+	const char *in_file_name;
+	NTSTATUS empty_status;
+	uint32_t info_level;
+	uint32_t max_count;
+	char *pdata;
+	char *base_data;
+	char *end_data;
+	uint32_t num;
+	uint32_t dirtype;
+	bool dont_descend;
+	bool ask_sharemode;
+	bool async_ask_sharemode;
+	int last_entry_off;
+	bool done;
 };
 
+static bool smb2_query_directory_next_entry(struct tevent_req *req);
 static void smb2_query_directory_fetch_write_time_done(struct tevent_req *subreq);
 static void smb2_query_directory_waited(struct tevent_req *subreq);
 
@@ -230,25 +247,12 @@ static struct tevent_req *smbd_smb2_query_directory_send(TALLOC_CTX *mem_ctx,
 	struct smbXsrv_connection *xconn = smb2req->xconn;
 	struct tevent_req *req;
 	struct smbd_smb2_query_directory_state *state;
-	struct smb_request *smbreq;
 	connection_struct *conn = smb2req->tcon->compat;
 	NTSTATUS status;
-	NTSTATUS empty_status;
-	uint32_t info_level;
-	uint32_t max_count;
-	char *pdata;
-	char *base_data;
-	char *end_data;
-	int last_entry_off = 0;
-	int off = 0;
-	uint32_t num = 0;
-	uint32_t dirtype = FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_DIRECTORY;
-	bool dont_descend = false;
-	bool ask_sharemode = false;
-	bool async_ask_sharemode = false;
 	bool wcard_has_wild = false;
 	struct tm tm;
 	char *p;
+	bool stop = false;
 
 	req = tevent_req_create(mem_ctx, &state,
 				struct smbd_smb2_query_directory_state);
@@ -256,14 +260,18 @@ static struct tevent_req *smbd_smb2_query_directory_send(TALLOC_CTX *mem_ctx,
 		return NULL;
 	}
 	state->ev = ev;
+	state->fsp = fsp;
 	state->smb2req = smb2req;
+	state->in_output_buffer_length = in_output_buffer_length;
+	state->in_file_name = in_file_name;
 	state->out_output_buffer = data_blob_null;
+	state->dirtype = FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_DIRECTORY;
 
 	DEBUG(10,("smbd_smb2_query_directory_send: %s - %s\n",
 		  fsp_str_dbg(fsp), fsp_fnum_dbg(fsp)));
 
-	smbreq = smbd_smb2_fake_smb_request(smb2req);
-	if (tevent_req_nomem(smbreq, req)) {
+	state->smbreq = smbd_smb2_fake_smb_request(smb2req);
+	if (tevent_req_nomem(state->smbreq, req)) {
 		return tevent_req_post(req, ev);
 	}
 
@@ -272,20 +280,20 @@ static struct tevent_req *smbd_smb2_query_directory_send(TALLOC_CTX *mem_ctx,
 		return tevent_req_post(req, ev);
 	}
 
-	if (strcmp(in_file_name, "") == 0) {
+	if (strcmp(state->in_file_name, "") == 0) {
 		tevent_req_nterror(req, NT_STATUS_OBJECT_NAME_INVALID);
 		return tevent_req_post(req, ev);
 	}
-	if (strchr_m(in_file_name, '\\') != NULL) {
+	if (strchr_m(state->in_file_name, '\\') != NULL) {
 		tevent_req_nterror(req, NT_STATUS_OBJECT_NAME_INVALID);
 		return tevent_req_post(req, ev);
 	}
-	if (strchr_m(in_file_name, '/') != NULL) {
+	if (strchr_m(state->in_file_name, '/') != NULL) {
 		tevent_req_nterror(req, NT_STATUS_OBJECT_NAME_INVALID);
 		return tevent_req_post(req, ev);
 	}
 
-	p = strptime(in_file_name, GMT_FORMAT, &tm);
+	p = strptime(state->in_file_name, GMT_FORMAT, &tm);
 	if ((p != NULL) && (*p =='\0')) {
 		/*
 		 * Bogus find that asks for a shadow copy timestamp as a
@@ -315,27 +323,27 @@ static struct tevent_req *smbd_smb2_query_directory_send(TALLOC_CTX *mem_ctx,
 
 	switch (in_file_info_class) {
 	case SMB2_FIND_DIRECTORY_INFO:
-		info_level = SMB_FIND_FILE_DIRECTORY_INFO;
+		state->info_level = SMB_FIND_FILE_DIRECTORY_INFO;
 		break;
 
 	case SMB2_FIND_FULL_DIRECTORY_INFO:
-		info_level = SMB_FIND_FILE_FULL_DIRECTORY_INFO;
+		state->info_level = SMB_FIND_FILE_FULL_DIRECTORY_INFO;
 		break;
 
 	case SMB2_FIND_BOTH_DIRECTORY_INFO:
-		info_level = SMB_FIND_FILE_BOTH_DIRECTORY_INFO;
+		state->info_level = SMB_FIND_FILE_BOTH_DIRECTORY_INFO;
 		break;
 
 	case SMB2_FIND_NAME_INFO:
-		info_level = SMB_FIND_FILE_NAMES_INFO;
+		state->info_level = SMB_FIND_FILE_NAMES_INFO;
 		break;
 
 	case SMB2_FIND_ID_BOTH_DIRECTORY_INFO:
-		info_level = SMB_FIND_ID_BOTH_DIRECTORY_INFO;
+		state->info_level = SMB_FIND_ID_BOTH_DIRECTORY_INFO;
 		break;
 
 	case SMB2_FIND_ID_FULL_DIRECTORY_INFO:
-		info_level = SMB_FIND_ID_FULL_DIRECTORY_INFO;
+		state->info_level = SMB_FIND_ID_FULL_DIRECTORY_INFO;
 		break;
 
 	default:
@@ -366,8 +374,8 @@ static struct tevent_req *smbd_smb2_query_directory_send(TALLOC_CTX *mem_ctx,
 		}
 	}
 
-	if (!smbreq->posix_pathnames) {
-		wcard_has_wild = ms_has_wild(in_file_name);
+	if (!state->smbreq->posix_pathnames) {
+		wcard_has_wild = ms_has_wild(state->in_file_name);
 	}
 
 	/* Ensure we've canonicalized any search path if not a wildcard. */
@@ -378,17 +386,17 @@ static struct tevent_req *smbd_smb2_query_directory_send(TALLOC_CTX *mem_ctx,
 		char *to_free = NULL;
 		uint32_t ucf_flags = UCF_SAVE_LCOMP |
 				     UCF_ALWAYS_ALLOW_WCARD_LCOMP |
-				     (smbreq->posix_pathnames ?
+				     (state->smbreq->posix_pathnames ?
 					UCF_POSIX_PATHNAMES : 0);
 
 		if (ISDOT(fsp->fsp_name->base_name)) {
-			fullpath = in_file_name;
+			fullpath = state->in_file_name;
 		} else {
 			size_t len;
 			char *tmp;
 
 			len = full_path_tos(
-				fsp->fsp_name->base_name, in_file_name,
+				fsp->fsp_name->base_name, state->in_file_name,
 				tmpbuf, sizeof(tmpbuf), &tmp, &to_free);
 			if (len == -1) {
 				tevent_req_oom(req);
@@ -409,7 +417,7 @@ static struct tevent_req *smbd_smb2_query_directory_send(TALLOC_CTX *mem_ctx,
 			return tevent_req_post(req, ev);
 		}
 
-		in_file_name = smb_fname->original_lcomp;
+		state->in_file_name = smb_fname->original_lcomp;
 	}
 
 	if (fsp->dptr == NULL) {
@@ -420,18 +428,18 @@ static struct tevent_req *smbd_smb2_query_directory_send(TALLOC_CTX *mem_ctx,
 				     false, /* old_handle */
 				     false, /* expect_close */
 				     0, /* spid */
-				     in_file_name, /* wcard */
+				     state->in_file_name, /* wcard */
 				     wcard_has_wild,
-				     dirtype,
+				     state->dirtype,
 				     &fsp->dptr);
 		if (!NT_STATUS_IS_OK(status)) {
 			tevent_req_nterror(req, status);
 			return tevent_req_post(req, ev);
 		}
 
-		empty_status = NT_STATUS_NO_SUCH_FILE;
+		state->empty_status = NT_STATUS_NO_SUCH_FILE;
 	} else {
-		empty_status = STATUS_NO_MORE_FILES;
+		state->empty_status = STATUS_NO_MORE_FILES;
 	}
 
 	if (in_flags & SMB2_CONTINUE_FLAG_RESTART) {
@@ -439,9 +447,9 @@ static struct tevent_req *smbd_smb2_query_directory_send(TALLOC_CTX *mem_ctx,
 	}
 
 	if (in_flags & SMB2_CONTINUE_FLAG_SINGLE) {
-		max_count = 1;
+		state->max_count = 1;
 	} else {
-		max_count = UINT16_MAX;
+		state->max_count = UINT16_MAX;
 	}
 
 #define DIR_ENTRY_SAFETY_MARGIN 4096
@@ -453,16 +461,13 @@ static struct tevent_req *smbd_smb2_query_directory_send(TALLOC_CTX *mem_ctx,
 	}
 
 	state->out_output_buffer.length = 0;
-	pdata = (char *)state->out_output_buffer.data;
-	base_data = pdata;
+	state->pdata = (char *)state->out_output_buffer.data;
+	state->base_data = state->pdata;
 	/*
 	 * end_data must include the safety margin as it's what is
 	 * used to determine if pushed strings have been truncated.
 	 */
-	end_data = pdata + in_output_buffer_length + DIR_ENTRY_SAFETY_MARGIN - 1;
-	last_entry_off = 0;
-	off = 0;
-	num = 0;
+	state->end_data = state->pdata + in_output_buffer_length + DIR_ENTRY_SAFETY_MARGIN - 1;
 
 	DEBUG(8,("smbd_smb2_query_directory_send: dirpath=<%s> dontdescend=<%s>, "
 		"in_output_buffer_length = %u\n",
@@ -470,7 +475,7 @@ static struct tevent_req *smbd_smb2_query_directory_send(TALLOC_CTX *mem_ctx,
 		(unsigned int)in_output_buffer_length ));
 	if (in_list(fsp->fsp_name->base_name,lp_dont_descend(talloc_tos(), SNUM(conn)),
 			conn->case_sensitive)) {
-		dont_descend = true;
+		state->dont_descend = true;
 	}
 
 	/*
@@ -479,15 +484,14 @@ static struct tevent_req *smbd_smb2_query_directory_send(TALLOC_CTX *mem_ctx,
 	 * This may change when we try to improve the delete on close
 	 * handling in future.
 	 */
-	if (info_level != SMB_FIND_FILE_NAMES_INFO) {
-		ask_sharemode = lp_parm_bool(SNUM(conn),
-					     "smbd", "search ask sharemode",
-					     true);
+	if (state->info_level != SMB_FIND_FILE_NAMES_INFO) {
+		state->ask_sharemode = lp_parm_bool(
+			SNUM(conn), "smbd", "search ask sharemode", true);
 	}
 
-	if (ask_sharemode && lp_clustering()) {
-		ask_sharemode = false;
-		async_ask_sharemode = true;
+	if (state->ask_sharemode && lp_clustering()) {
+		state->ask_sharemode = false;
+		state->async_ask_sharemode = true;
 
 		/*
 		 * Should we only set async_internal
@@ -504,127 +508,139 @@ static struct tevent_req *smbd_smb2_query_directory_send(TALLOC_CTX *mem_ctx,
 						     "find async delay usec",
 						     0);
 
-	while (true) {
-		bool got_exact_match = false;
-		int space_remaining = in_output_buffer_length - off;
-		struct file_id file_id;
-		bool stop = false;
-
-		SMB_ASSERT(space_remaining >= 0);
-
-		status = smbd_dirptr_lanman2_entry(state,
-					       conn,
-					       fsp->dptr,
-					       smbreq->flags2,
-					       in_file_name,
-					       dirtype,
-					       info_level,
-					       false, /* requires_resume_key */
-					       dont_descend,
-					       ask_sharemode,
-					       true,
-					       8, /* align to 8 bytes */
-					       false, /* no padding */
-					       &pdata,
-					       base_data,
-					       end_data,
-					       space_remaining,
-					       &got_exact_match,
-					       &last_entry_off,
-					       NULL,
-					       &file_id);
-
-		off = (int)PTR_DIFF(pdata, base_data);
+	while (!stop) {
+		stop = smb2_query_directory_next_entry(req);
+	}
 
-		if (!NT_STATUS_IS_OK(status)) {
-			if (NT_STATUS_EQUAL(status, NT_STATUS_ILLEGAL_CHARACTER)) {
-				/*
-				 * Bad character conversion on name. Ignore this
-				 * entry.
-				 */
-				continue;
-			} else if (num > 0) {
-				goto last_entry_done;
-			} else if (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
-				tevent_req_nterror(req, NT_STATUS_INFO_LENGTH_MISMATCH);
-				return tevent_req_post(req, ev);
-			} else {
-				tevent_req_nterror(req, empty_status);
-				return tevent_req_post(req, ev);
-			}
-		}
+	if (!tevent_req_is_in_progress(req)) {
+		return tevent_req_post(req, ev);
+	}
 
-		if (async_ask_sharemode) {
-			struct tevent_req *subreq = NULL;
-
-			subreq = fetch_write_time_send(req,
-						       ev,
-						       conn,
-						       file_id,
-						       info_level,
-						       base_data + last_entry_off,
-						       &stop);
-			if (tevent_req_nomem(subreq, req)) {
-				return tevent_req_post(req, ev);
-			}
-			tevent_req_set_callback(
-				subreq,
-				smb2_query_directory_fetch_write_time_done,
-				req);
+	return req;
+}
+
+static bool smb2_query_directory_next_entry(struct tevent_req *req)
+{
+	struct smbd_smb2_query_directory_state *state = tevent_req_data(
+		req, struct smbd_smb2_query_directory_state);
+	bool got_exact_match = false;
+	int off = state->out_output_buffer.length;
+	int space_remaining = state->in_output_buffer_length - off;
+	struct file_id file_id;
+	NTSTATUS status;
+	bool stop = false;
+
+	SMB_ASSERT(space_remaining >= 0);
+
+	status = smbd_dirptr_lanman2_entry(state,
+					   state->fsp->conn,
+					   state->fsp->dptr,
+					   state->smbreq->flags2,
+					   state->in_file_name,
+					   state->dirtype,
+					   state->info_level,
+					   false, /* requires_resume_key */
+					   state->dont_descend,
+					   state->ask_sharemode,
+					   true,
+					   8, /* align to 8 bytes */
+					   false, /* no padding */
+					   &state->pdata,
+					   state->base_data,
+					   state->end_data,
+					   space_remaining,
+					   &got_exact_match,
+					   &state->last_entry_off,
+					   NULL,
+					   &file_id);
+
+	off = (int)PTR_DIFF(state->pdata, state->base_data);
 
-			state->async_count++;
+	if (!NT_STATUS_IS_OK(status)) {
+		if (NT_STATUS_EQUAL(status, NT_STATUS_ILLEGAL_CHARACTER)) {
+			/*
+			 * Bad character conversion on name. Ignore this
+			 * entry.
+			 */
+			return false;
+		} else if (state->num > 0) {
+			goto last_entry_done;
+		} else if (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
+			tevent_req_nterror(req, NT_STATUS_INFO_LENGTH_MISMATCH);
+			return true;
+		} else {
+			tevent_req_nterror(req, state->empty_status);
+			return true;
 		}
+	}
 
-		num++;
-		state->out_output_buffer.length = off;
+	if (state->async_ask_sharemode) {
+		struct tevent_req *subreq = NULL;
 
-		if (num >= max_count) {
-			stop = true;
+		subreq = fetch_write_time_send(state,
+					       state->ev,
+					       state->fsp->conn,
+					       file_id,
+					       state->info_level,
+					       state->base_data + state->last_entry_off,
+					       &stop);
+		if (tevent_req_nomem(subreq, req)) {
+			return true;
 		}
+		tevent_req_set_callback(
+			subreq,
+			smb2_query_directory_fetch_write_time_done,
+			req);
+		state->async_sharemode_count++;
+	}
 
-		if (!stop) {
-			continue;
-		}
+	state->num++;
+	state->out_output_buffer.length = off;
+
+	if (!state->done && state->num < state->max_count) {
+		return stop;
+	}
 
 last_entry_done:
-		SIVAL(state->out_output_buffer.data, last_entry_off, 0);
-		if (state->async_count > 0) {
-			DBG_DEBUG("Stopping after %"PRIu64" async mtime "
-				  "updates\n", state->async_count);
-			return req;
-		}
+	SIVAL(state->out_output_buffer.data, state->last_entry_off, 0);
 
-		if (state->find_async_delay_usec > 0) {
-			struct timeval tv;
-			struct tevent_req *subreq = NULL;
+	state->done = true;
 
-			/*
-			 * Should we only set async_internal
-			 * if we're not the last request in
-			 * a compound chain?
-			 */
-			smb2_request_set_async_internal(smb2req, true);
+	if (state->async_sharemode_count > 0) {
+		DBG_DEBUG("Stopping after %"PRIu64" async mtime "
+			  "updates\n", state->async_sharemode_count);
+		return true;
+	}
 
-			tv = timeval_current_ofs(0, state->find_async_delay_usec);
+	if (state->find_async_delay_usec > 0) {
+		struct timeval tv;
+		struct tevent_req *subreq = NULL;
 
-			subreq = tevent_wakeup_send(state, ev, tv);
-			if (tevent_req_nomem(subreq, req)) {
-				return tevent_req_post(req, ev);
-			}
-			tevent_req_set_callback(subreq,
-						smb2_query_directory_waited,
-						req);
-			return req;
-		}
+		/*
+		 * Should we only set async_internal
+		 * if we're not the last request in
+		 * a compound chain?
+		 */
+		smb2_request_set_async_internal(state->smb2req, true);
 
-		tevent_req_done(req);
-		return tevent_req_post(req, ev);
+		tv = timeval_current_ofs(0, state->find_async_delay_usec);
+
+		subreq = tevent_wakeup_send(state, state->ev, tv);
+		if (tevent_req_nomem(subreq, req)) {
+			return true;
+		}
+		tevent_req_set_callback(subreq,
+					smb2_query_directory_waited,
+					req);
+		return req;
 	}
 
-	tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
-	return tevent_req_post(req, ev);
+	tevent_req_done(req);
+	return true;
 }
 
+static void smb2_query_directory_check_next_entry(struct tevent_req *req);
+
 static void smb2_query_directory_fetch_write_time_done(struct tevent_req *subreq)
 {
 	struct tevent_req *req = tevent_req_callback_data(
@@ -633,7 +649,7 @@ static void smb2_query_directory_fetch_write_time_done(struct tevent_req *subreq
 		req, struct smbd_smb2_query_directory_state);
 	NTSTATUS status;
 
-	state->async_count--;
+	state->async_sharemode_count--;
 
 	status = fetch_write_time_recv(subreq);
 	TALLOC_FREE(subreq);
@@ -641,12 +657,30 @@ static void smb2_query_directory_fetch_write_time_done(struct tevent_req *subreq
 		return;
 	}
 
-	if (state->async_count > 0) {
+	smb2_query_directory_check_next_entry(req);
+	return;
+}
+
+static void smb2_query_directory_check_next_entry(struct tevent_req *req)
+{
+	struct smbd_smb2_query_directory_state *state = tevent_req_data(
+		req, struct smbd_smb2_query_directory_state);
+	bool stop = false;
+
+	if (!state->done) {
+		while (!stop) {
+			stop = smb2_query_directory_next_entry(req);
+		}
+		return;
+	}
+
+	if (state->async_sharemode_count > 0) {
 		return;
 	}
 
 	if (state->find_async_delay_usec > 0) {
 		struct timeval tv;
+		struct tevent_req *subreq = NULL;
 
 		tv = timeval_current_ofs(0, state->find_async_delay_usec);
 
-- 
2.17.1


From 6b4319546bec5d0180bbc5644fb07d2143816670 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Thu, 21 Jun 2018 12:44:42 +0200
Subject: [PATCH 15/25] smbd: fix a long line in
 smb2_query_directory_next_entry()

Signed-off-by: Ralph Boehme <slow at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 source3/smbd/smb2_query_directory.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/source3/smbd/smb2_query_directory.c b/source3/smbd/smb2_query_directory.c
index 6577ab81eaaf..1a7137ce8793 100644
--- a/source3/smbd/smb2_query_directory.c
+++ b/source3/smbd/smb2_query_directory.c
@@ -576,13 +576,14 @@ static bool smb2_query_directory_next_entry(struct tevent_req *req)
 
 	if (state->async_ask_sharemode) {
 		struct tevent_req *subreq = NULL;
+		char *buf = state->base_data + state->last_entry_off;
 
 		subreq = fetch_write_time_send(state,
 					       state->ev,
 					       state->fsp->conn,
 					       file_id,
 					       state->info_level,
-					       state->base_data + state->last_entry_off,
+					       buf,
 					       &stop);
 		if (tevent_req_nomem(subreq, req)) {
 			return true;
-- 
2.17.1


From d970223d57a04f3bbf4b62e500be10a7ba12fc37 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Wed, 21 Mar 2018 20:41:44 +0100
Subject: [PATCH 16/25] smbd: deal with fsp->aio_requests in close_directory()

Signed-off-by: Ralph Boehme <slow at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 source3/smbd/close.c                | 25 +++++++++++++++++++++++++
 source3/smbd/smb2_query_directory.c |  8 ++++++++
 2 files changed, 33 insertions(+)

diff --git a/source3/smbd/close.c b/source3/smbd/close.c
index 8581b04e8aaf..7820fff3519d 100644
--- a/source3/smbd/close.c
+++ b/source3/smbd/close.c
@@ -1085,6 +1085,31 @@ static NTSTATUS close_directory(struct smb_request *req, files_struct *fsp,
 		notify_status = NT_STATUS_OK;
 	}
 
+	if (fsp->num_aio_requests != 0) {
+		if (close_type != SHUTDOWN_CLOSE) {
+			/*
+			 * We panic here because if we close() the fd while we
+			 * have outstanding async I/O requests, an async IO
+			 * request might use the fd. For directories the fd is
+			 * read-only, so this is not as bad as with files, but
+			 * still, better safe then sorry.
+			 */
+			DBG_ERR("fsp->num_aio_requests=%u\n",
+				fsp->num_aio_requests);
+			smb_panic("close with outstanding aio requests");
+			return NT_STATUS_INTERNAL_ERROR;
+		}
+
+		while (fsp->num_aio_requests != 0) {
+			/*
+			 * The destructor of the req will remove itself from the
+			 * fsp.  Don't use TALLOC_FREE here, this will overwrite
+			 * what the destructor just wrote into aio_requests[0].
+			 */
+			talloc_free(fsp->aio_requests[0]);
+		}
+	}
+
 	/*
 	 * NT can set delete_on_close of the last open
 	 * reference to a directory also.
diff --git a/source3/smbd/smb2_query_directory.c b/source3/smbd/smb2_query_directory.c
index 1a7137ce8793..3c241f2e04a0 100644
--- a/source3/smbd/smb2_query_directory.c
+++ b/source3/smbd/smb2_query_directory.c
@@ -253,6 +253,7 @@ static struct tevent_req *smbd_smb2_query_directory_send(TALLOC_CTX *mem_ctx,
 	struct tm tm;
 	char *p;
 	bool stop = false;
+	bool ok;
 
 	req = tevent_req_create(mem_ctx, &state,
 				struct smbd_smb2_query_directory_state);
@@ -516,6 +517,13 @@ static struct tevent_req *smbd_smb2_query_directory_send(TALLOC_CTX *mem_ctx,
 		return tevent_req_post(req, ev);
 	}
 
+	ok = aio_add_req_to_fsp(fsp, req);
+	if (!ok) {
+		DBG_ERR("Could not add req to fsp\n");
+		tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+		return tevent_req_post(req, ev);
+	}
+
 	return req;
 }
 
-- 
2.17.1


From 9c6d30216d63494bd5d244b772cf9d2d1731e82c Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Thu, 15 Mar 2018 19:03:59 +0100
Subject: [PATCH 17/25] smbd: let smbd_dirptr_lanman2_entry return smb_fname

Note that smb_fname is relative to fsp, not conn!

Signed-off-by: Ralph Boehme <slow at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 source3/smbd/globals.h              |  1 +
 source3/smbd/smb2_query_directory.c |  4 ++++
 source3/smbd/trans2.c               | 22 +++++++++++++++++++---
 3 files changed, 24 insertions(+), 3 deletions(-)

diff --git a/source3/smbd/globals.h b/source3/smbd/globals.h
index d151d33b968a..6ae571844178 100644
--- a/source3/smbd/globals.h
+++ b/source3/smbd/globals.h
@@ -210,6 +210,7 @@ NTSTATUS smbd_dirptr_lanman2_entry(TALLOC_CTX *ctx,
 			       char *base_data,
 			       char *end_data,
 			       int space_remaining,
+			       struct smb_filename **smb_fname,
 			       bool *got_exact_match,
 			       int *_last_entry_off,
 			       struct ea_list *name_list,
diff --git a/source3/smbd/smb2_query_directory.c b/source3/smbd/smb2_query_directory.c
index 3c241f2e04a0..aeba134810fa 100644
--- a/source3/smbd/smb2_query_directory.c
+++ b/source3/smbd/smb2_query_directory.c
@@ -531,6 +531,7 @@ static bool smb2_query_directory_next_entry(struct tevent_req *req)
 {
 	struct smbd_smb2_query_directory_state *state = tevent_req_data(
 		req, struct smbd_smb2_query_directory_state);
+	struct smb_filename *smb_fname = NULL; /* relative to fsp !! */
 	bool got_exact_match = false;
 	int off = state->out_output_buffer.length;
 	int space_remaining = state->in_output_buffer_length - off;
@@ -557,6 +558,7 @@ static bool smb2_query_directory_next_entry(struct tevent_req *req)
 					   state->base_data,
 					   state->end_data,
 					   space_remaining,
+					   &smb_fname,
 					   &got_exact_match,
 					   &state->last_entry_off,
 					   NULL,
@@ -603,6 +605,8 @@ static bool smb2_query_directory_next_entry(struct tevent_req *req)
 		state->async_sharemode_count++;
 	}
 
+	TALLOC_FREE(smb_fname);
+
 	state->num++;
 	state->out_output_buffer.length = off;
 
diff --git a/source3/smbd/trans2.c b/source3/smbd/trans2.c
index 005f55f77b78..b4a481fbf865 100644
--- a/source3/smbd/trans2.c
+++ b/source3/smbd/trans2.c
@@ -2458,6 +2458,7 @@ NTSTATUS smbd_dirptr_lanman2_entry(TALLOC_CTX *ctx,
 			       char *base_data,
 			       char *end_data,
 			       int space_remaining,
+			       struct smb_filename **_smb_fname,
 			       bool *got_exact_match,
 			       int *_last_entry_off,
 			       struct ea_list *name_list,
@@ -2548,15 +2549,29 @@ NTSTATUS smbd_dirptr_lanman2_entry(TALLOC_CTX *ctx,
 		*file_id = vfs_file_id_from_sbuf(conn, &smb_fname->st);
 	}
 
-	TALLOC_FREE(fname);
-	TALLOC_FREE(smb_fname);
-
 	if (!NT_STATUS_IS_OK(status) &&
 	    !NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES))
 	{
+		TALLOC_FREE(smb_fname);
+		TALLOC_FREE(fname);
 		return status;
 	}
 
+	if (_smb_fname != NULL) {
+		struct smb_filename *name = NULL;
+
+		name = synthetic_smb_fname(ctx, fname, NULL, &smb_fname->st, 0);
+		if (name == NULL) {
+			TALLOC_FREE(smb_fname);
+			TALLOC_FREE(fname);
+			return NT_STATUS_NO_MEMORY;
+		}
+		*_smb_fname = name;
+	}
+
+	TALLOC_FREE(smb_fname);
+	TALLOC_FREE(fname);
+
 	if (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
 		dptr_SeekDir(dirptr, prev_dirpos);
 		return status;
@@ -2598,6 +2613,7 @@ static NTSTATUS get_lanman2_dir_entry(TALLOC_CTX *ctx,
 					 true, align, do_pad,
 					 ppdata, base_data, end_data,
 					 space_remaining,
+					 NULL,
 					 got_exact_match,
 					 last_entry_off, name_list, NULL);
 }
-- 
2.17.1


From 78a4df4becb9f2edd3688edd7f83c8ce8dcd8645 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Wed, 25 Jul 2018 19:14:25 +0200
Subject: [PATCH 18/25] smbd: use async dos_mode_at_send in
 smbd_smb2_query_directory_send()

Finally: use the new dos_mode_at_send() in the directory enumeration
loop. This means that fetching the DOS attributes for directory entries
is done asynchronously with regard to the enumeration loop.

As the DOS attribute is typically read from an extended attribute in the
filesytem, this avoids sequentially blocking on IO. If the IO subsystem
is slow servicing these request, enabling async processing can result in
performance improvements.

A parametric option

  smbd:async dosmode = true | false (default: false)

can be used to enable the new async processing.

Simulating slow IO with usleep(5000) in the synchronous and asynchronous
versions of SMB_VFS_GET_DOS_ATTRIBUTES(), the results of enumerating a
directory with 10,000 files are:

    smbd:async dosmode = no:

        $ time bin/smbclient -U slow%x //localhost/test -c "ls dir\*" > /dev/null
        real    0m59.597s
        user    0m0.024s
        sys     0m0.012s

    smbd:async dosmode = yes:

        $ time bin/smbclient -U slow%x //localhost/test -c "ls dir\*" > /dev/null
        real    0m0.698s
        user    0m0.038s
        sys     0m0.025s

Performance gains in real world workloads depends on whether the actual
IO requests can be merged and parallelized by the kernel. Without such
wins at the IO layer, the async processing may even be slower then the
sync processing due to the additional overhead.

The following parameters can be used to adapt async processing behaviour
for specific workloads and systems:

        aio max threads = X (default: 100)
        smbd:max async dosmode = Y (default: "aio max threads" * 2)

By default we have at most twice the number of async requests in flight
as threads provided by the underlying threadpool. This ensures a worker
thread that finishes a job can directly pick up a new one without going
to sleep.

It may be advisable to reduce the number of threads to avoid scheduling
overhead while also increasing "smbd:max async dosmode".

Note that we disable async processing for certain VFS modules in the VFS
connect function to avoid the overhead of triggering the sync fallback
in dos_mode_at_send(). This is done for VFS modules that implement the
sync SMB_VFS_GET_DOS_ATTRIBUTES(), but not the async version (gpfs), and
for VFS modules that don't share a real filesystem where fchdir() can be
used (ceph, gluster). It is disabled for catia, because we realized that
the catia name translation macros used on
fsps (CATIA_FETCH_FSP_[PRE|POST]_NEXT) have a bug (#13547).

We use threadpool = smb_vfs_ev_glue_tp_chdir_safe() and then
pthreadpool_tevent_max_threads(threadpool) to get the number of maximum
worker threads which matches the pool used by the low level
SMB_VFS_GETXATTRAT_[SEND|RECV] implementation in vfs_default.

This is a terrible abstraction leak that should be removed in the future
by maybe making it possible to ask a VFS function which threadpool it
uses, internally suporting chaining so VFS function FOO that internally
uses BAR can forward the question to BAR.

On a hyphotetical system that had a getxattrat(dirfd, path, ...)
syscall and at the same time doesn't support per-thread current working
directories (eg FreeBSD doesn't have the latter) but has support for
per-thread-credentials, pthreadpool_tevent_max_threads() on the
tp_chdir_safe threadpool returns 1.

So when hooking the hyphotetical getxattrat() into the async
SMB_VFS_GETXATTRAT_[SEND|RECV] implementation in an VFS module, the
implementation could use the tp_path_safe threadpool, but the SMB2
layer would use the wrong threadpool in the call to
pthreadpool_tevent_max_threads(), resulting in no parallelism.

Signed-off-by: Ralph Boehme <slow at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 source3/modules/vfs_catia.c         |  15 ++
 source3/modules/vfs_ceph.c          |   5 +
 source3/modules/vfs_glusterfs.c     |   5 +
 source3/modules/vfs_gpfs.c          |   6 +
 source3/smbd/smb2_query_directory.c | 225 +++++++++++++++++++++++++++-
 5 files changed, 254 insertions(+), 2 deletions(-)

diff --git a/source3/modules/vfs_catia.c b/source3/modules/vfs_catia.c
index 12995dda9bfa..c362be764cc5 100644
--- a/source3/modules/vfs_catia.c
+++ b/source3/modules/vfs_catia.c
@@ -158,6 +158,19 @@ static NTSTATUS catia_string_replace_allocate(connection_struct *conn,
 	return status;
 }
 
+static int catia_connect(struct vfs_handle_struct *handle,
+			 const char *service,
+			 const char *user)
+{
+	/*
+	 * Unless we have an async implementation of get_dos_attributes turn
+	 * this off.
+	 */
+	lp_do_parameter(SNUM(handle->conn), "smbd:async dosmode", "false");
+
+	return SMB_VFS_NEXT_CONNECT(handle, service, user);
+}
+
 static DIR *catia_opendir(vfs_handle_struct *handle,
 			const struct smb_filename *smb_fname,
 			const char *mask,
@@ -2405,6 +2418,8 @@ static NTSTATUS catia_set_dos_attributes(struct vfs_handle_struct *handle,
 }
 
 static struct vfs_fn_pointers vfs_catia_fns = {
+	.connect_fn = catia_connect,
+
 	/* Directory operations */
 	.mkdir_fn = catia_mkdir,
 	.rmdir_fn = catia_rmdir,
diff --git a/source3/modules/vfs_ceph.c b/source3/modules/vfs_ceph.c
index 8b709eddc908..d863c8add5a9 100644
--- a/source3/modules/vfs_ceph.c
+++ b/source3/modules/vfs_ceph.c
@@ -132,6 +132,11 @@ static int cephwrap_connect(struct vfs_handle_struct *handle,  const char *servi
 	handle->data = cmount;
 	cmount_cnt++;
 
+	/*
+	 * Unless we have an async implementation of getxattrat turn this off.
+	 */
+	lp_do_parameter(SNUM(handle->conn), "smbd:async dosmode", "false");
+
 	return 0;
 
 err_cm_release:
diff --git a/source3/modules/vfs_glusterfs.c b/source3/modules/vfs_glusterfs.c
index 98be3c6d4e2a..431f6fff48c0 100644
--- a/source3/modules/vfs_glusterfs.c
+++ b/source3/modules/vfs_glusterfs.c
@@ -362,6 +362,11 @@ static int vfs_gluster_connect(struct vfs_handle_struct *handle,
 	 */
 	lp_do_parameter(SNUM(handle->conn), "shadow:mountpoint", "/");
 
+	/*
+	 * Unless we have an async implementation of getxattrat turn this off.
+	 */
+	lp_do_parameter(SNUM(handle->conn), "smbd:async dosmode", "false");
+
 done:
 	if (ret < 0) {
 		if (fs)
diff --git a/source3/modules/vfs_gpfs.c b/source3/modules/vfs_gpfs.c
index 5f21bc0826d5..982dc19e785f 100644
--- a/source3/modules/vfs_gpfs.c
+++ b/source3/modules/vfs_gpfs.c
@@ -2163,6 +2163,12 @@ static int vfs_gpfs_connect(struct vfs_handle_struct *handle,
 		}
 	}
 
+	/*
+	 * Unless we have an async implementation of get_dos_attributes turn
+	 * this off.
+	 */
+	lp_do_parameter(SNUM(handle->conn), "smbd:async dosmode", "false");
+
 	return 0;
 }
 
diff --git a/source3/smbd/smb2_query_directory.c b/source3/smbd/smb2_query_directory.c
index aeba134810fa..905277908178 100644
--- a/source3/smbd/smb2_query_directory.c
+++ b/source3/smbd/smb2_query_directory.c
@@ -25,6 +25,7 @@
 #include "trans2.h"
 #include "../lib/util/tevent_ntstatus.h"
 #include "system/filesys.h"
+#include "lib/pthreadpool/pthreadpool_tevent.h"
 
 #undef DBGC_CLASS
 #define DBGC_CLASS DBGC_SMB2
@@ -205,7 +206,18 @@ static struct tevent_req *fetch_write_time_send(TALLOC_CTX *mem_ctx,
 						bool *stop);
 static NTSTATUS fetch_write_time_recv(struct tevent_req *req);
 
+static struct tevent_req *fetch_dos_mode_send(
+	TALLOC_CTX *mem_ctx,
+	struct smb_vfs_ev_glue *evg,
+	struct files_struct *dir_fsp,
+	struct smb_filename **smb_fname,
+	uint32_t info_level,
+	uint8_t *entry_marshall_buf);
+
+static NTSTATUS fetch_dos_mode_recv(struct tevent_req *req);
+
 struct smbd_smb2_query_directory_state {
+	struct smb_vfs_ev_glue *evg;
 	struct tevent_context *ev;
 	struct smbd_smb2_request *smb2req;
 	uint64_t async_sharemode_count;
@@ -225,13 +237,18 @@ struct smbd_smb2_query_directory_state {
 	uint32_t dirtype;
 	bool dont_descend;
 	bool ask_sharemode;
+	bool async_dosmode;
 	bool async_ask_sharemode;
 	int last_entry_off;
+	struct pthreadpool_tevent *tp_chdir_safe;
+	size_t max_async_dosmode_active;
+	uint32_t async_dosmode_active;
 	bool done;
 };
 
 static bool smb2_query_directory_next_entry(struct tevent_req *req);
 static void smb2_query_directory_fetch_write_time_done(struct tevent_req *subreq);
+static void smb2_query_directory_dos_mode_done(struct tevent_req *subreq);
 static void smb2_query_directory_waited(struct tevent_req *subreq);
 
 static struct tevent_req *smbd_smb2_query_directory_send(TALLOC_CTX *mem_ctx,
@@ -260,7 +277,9 @@ static struct tevent_req *smbd_smb2_query_directory_send(TALLOC_CTX *mem_ctx,
 	if (req == NULL) {
 		return NULL;
 	}
+	state->evg = conn->user_vfs_evg;
 	state->ev = ev;
+	state->tp_chdir_safe = smb_vfs_ev_glue_tp_chdir_safe(state->evg);
 	state->fsp = fsp;
 	state->smb2req = smb2req;
 	state->in_output_buffer_length = in_output_buffer_length;
@@ -488,12 +507,31 @@ static struct tevent_req *smbd_smb2_query_directory_send(TALLOC_CTX *mem_ctx,
 	if (state->info_level != SMB_FIND_FILE_NAMES_INFO) {
 		state->ask_sharemode = lp_parm_bool(
 			SNUM(conn), "smbd", "search ask sharemode", true);
+
+		state->async_dosmode = lp_parm_bool(
+                        SNUM(conn), "smbd", "async dosmode", false);
 	}
 
 	if (state->ask_sharemode && lp_clustering()) {
 		state->ask_sharemode = false;
 		state->async_ask_sharemode = true;
+	}
 
+	if (state->async_dosmode) {
+		size_t max_threads;
+
+		max_threads = pthreadpool_tevent_max_threads(state->tp_chdir_safe);
+
+		state->max_async_dosmode_active = lp_parm_ulong(
+			SNUM(conn), "smbd", "max async dosmode",
+			max_threads * 2);
+
+		if (state->max_async_dosmode_active == 0) {
+			state->max_async_dosmode_active = 1;
+		}
+	}
+
+	if (state->async_dosmode || state->async_ask_sharemode) {
 		/*
 		 * Should we only set async_internal
 		 * if we're not the last request in
@@ -537,6 +575,7 @@ static bool smb2_query_directory_next_entry(struct tevent_req *req)
 	int space_remaining = state->in_output_buffer_length - off;
 	struct file_id file_id;
 	NTSTATUS status;
+	bool get_dosmode = !state->async_dosmode;
 	bool stop = false;
 
 	SMB_ASSERT(space_remaining >= 0);
@@ -551,7 +590,7 @@ static bool smb2_query_directory_next_entry(struct tevent_req *req)
 					   false, /* requires_resume_key */
 					   state->dont_descend,
 					   state->ask_sharemode,
-					   true,
+					   get_dosmode,
 					   8, /* align to 8 bytes */
 					   false, /* no padding */
 					   &state->pdata,
@@ -605,6 +644,36 @@ static bool smb2_query_directory_next_entry(struct tevent_req *req)
 		state->async_sharemode_count++;
 	}
 
+	if (state->async_dosmode) {
+		struct tevent_req *subreq = NULL;
+		uint8_t *buf = NULL;
+		size_t outstanding_aio;
+
+		buf = (uint8_t *)state->base_data + state->last_entry_off;
+
+		subreq = fetch_dos_mode_send(state,
+					     state->evg,
+					     state->fsp,
+					     &smb_fname,
+					     state->info_level,
+					     buf);
+		if (tevent_req_nomem(subreq, req)) {
+			return true;
+		}
+		tevent_req_set_callback(subreq,
+					smb2_query_directory_dos_mode_done,
+					req);
+
+		state->async_dosmode_active++;
+
+		outstanding_aio = pthreadpool_tevent_queued_jobs(
+					state->tp_chdir_safe);
+
+		if (outstanding_aio > state->max_async_dosmode_active) {
+			stop = true;
+		}
+	}
+
 	TALLOC_FREE(smb_fname);
 
 	state->num++;
@@ -625,6 +694,10 @@ last_entry_done:
 		return true;
 	}
 
+	if (state->async_dosmode_active > 0) {
+		return true;
+	}
+
 	if (state->find_async_delay_usec > 0) {
 		struct timeval tv;
 		struct tevent_req *subreq = NULL;
@@ -674,6 +747,28 @@ static void smb2_query_directory_fetch_write_time_done(struct tevent_req *subreq
 	return;
 }
 
+static void smb2_query_directory_dos_mode_done(struct tevent_req *subreq)
+{
+	struct tevent_req *req =
+		tevent_req_callback_data(subreq,
+		struct tevent_req);
+	struct smbd_smb2_query_directory_state *state =
+		tevent_req_data(req,
+		struct smbd_smb2_query_directory_state);
+	NTSTATUS status;
+
+	status = fetch_dos_mode_recv(subreq);
+	TALLOC_FREE(subreq);
+	if (tevent_req_nterror(req, status)) {
+		return;
+	}
+
+	state->async_dosmode_active--;
+
+	smb2_query_directory_check_next_entry(req);
+	return;
+}
+
 static void smb2_query_directory_check_next_entry(struct tevent_req *req)
 {
 	struct smbd_smb2_query_directory_state *state = tevent_req_data(
@@ -687,7 +782,9 @@ static void smb2_query_directory_check_next_entry(struct tevent_req *req)
 		return;
 	}
 
-	if (state->async_sharemode_count > 0) {
+	if (state->async_sharemode_count > 0 ||
+	    state->async_dosmode_active > 0)
+	{
 		return;
 	}
 
@@ -860,3 +957,127 @@ static NTSTATUS fetch_write_time_recv(struct tevent_req *req)
 	tevent_req_received(req);
 	return NT_STATUS_OK;
 }
+
+struct fetch_dos_mode_state {
+	struct files_struct *dir_fsp;
+	struct smb_filename *smb_fname;
+	uint32_t info_level;
+	uint8_t *entry_marshall_buf;
+};
+
+static void fetch_dos_mode_done(struct tevent_req *subreq);
+
+static struct tevent_req *fetch_dos_mode_send(
+			TALLOC_CTX *mem_ctx,
+			struct smb_vfs_ev_glue *evg,
+			struct files_struct *dir_fsp,
+			struct smb_filename **smb_fname,
+			uint32_t info_level,
+			uint8_t *entry_marshall_buf)
+{
+	struct tevent_context *ev = smb_vfs_ev_glue_ev_ctx(evg);
+	struct tevent_req *req = NULL;
+	struct fetch_dos_mode_state *state = NULL;
+	struct tevent_req *subreq = NULL;
+
+	req = tevent_req_create(mem_ctx, &state, struct fetch_dos_mode_state);
+	if (req == NULL) {
+		return NULL;
+	}
+	*state = (struct fetch_dos_mode_state) {
+		.dir_fsp = dir_fsp,
+		.info_level = info_level,
+		.entry_marshall_buf = entry_marshall_buf,
+	};
+
+	state->smb_fname = talloc_move(state, smb_fname);
+
+	subreq = dos_mode_at_send(state, evg, dir_fsp, state->smb_fname);
+	if (tevent_req_nomem(subreq, req)) {
+		return tevent_req_post(req, ev);
+	}
+	tevent_req_set_callback(subreq, fetch_dos_mode_done, req);
+
+	return req;
+}
+
+static void fetch_dos_mode_done(struct tevent_req *subreq)
+{
+	struct tevent_req *req =
+		tevent_req_callback_data(subreq,
+		struct tevent_req);
+	struct fetch_dos_mode_state *state =
+		tevent_req_data(req,
+		struct fetch_dos_mode_state);
+	uint32_t dfs_dosmode;
+	uint32_t dosmode;
+	struct timespec btime_ts = {0};
+	off_t dosmode_off;
+	off_t btime_off;
+	NTSTATUS status;
+
+	status = dos_mode_at_recv(subreq, &dosmode);
+	TALLOC_FREE(subreq);
+	if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+		tevent_req_done(req);
+		return;
+	}
+	if (!NT_STATUS_IS_OK(status)) {
+		tevent_req_nterror(req, status);
+		return;
+	}
+
+	switch (state->info_level) {
+	case SMB_FIND_ID_BOTH_DIRECTORY_INFO:
+	case SMB_FIND_FILE_BOTH_DIRECTORY_INFO:
+	case SMB_FIND_FILE_DIRECTORY_INFO:
+	case SMB_FIND_FILE_FULL_DIRECTORY_INFO:
+	case SMB_FIND_ID_FULL_DIRECTORY_INFO:
+		btime_off = 8;
+		dosmode_off = 56;
+		break;
+
+	default:
+		DBG_ERR("Unsupported info_level [%u]\n", state->info_level);
+		tevent_req_nterror(req, NT_STATUS_INVALID_LEVEL);
+		return;
+	}
+
+
+	dfs_dosmode = IVAL(state->entry_marshall_buf, dosmode_off);
+	if (dfs_dosmode == 0) {
+		/*
+		 * DOS mode for a DFS link, only overwrite if still set to 0 and
+		 * not already populated by the lower layer for a DFS link in
+		 * smbd_dirptr_lanman2_mode_fn().
+		 */
+		SIVAL(state->entry_marshall_buf, dosmode_off, dosmode);
+	}
+
+	btime_ts = get_create_timespec(state->dir_fsp->conn,
+				       NULL,
+				       state->smb_fname);
+	if (lp_dos_filetime_resolution(SNUM(state->dir_fsp->conn))) {
+		dos_filetime_timespec(&btime_ts);
+	}
+
+	put_long_date_timespec(state->dir_fsp->conn->ts_res,
+			       (char *)state->entry_marshall_buf + btime_off,
+			       btime_ts);
+
+	tevent_req_done(req);
+	return;
+}
+
+static NTSTATUS fetch_dos_mode_recv(struct tevent_req *req)
+{
+	NTSTATUS status;
+
+	if (tevent_req_is_nterror(req, &status)) {
+		tevent_req_received(req);
+		return status;
+	}
+
+	tevent_req_received(req);
+	return NT_STATUS_OK;
+}
-- 
2.17.1


From 4c035ebd0e960b826ecd37389c484c5c8db7258f Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Thu, 22 Mar 2018 09:22:08 +0100
Subject: [PATCH 19/25] s4: torture: test closing dir handle with in-flight
 find

Signed-off-by: Ralph Boehme <slow at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 source4/torture/smb2/compound.c | 69 +++++++++++++++++++++++++++++++++
 1 file changed, 69 insertions(+)

diff --git a/source4/torture/smb2/compound.c b/source4/torture/smb2/compound.c
index d2d4d7e41fcb..11fed0bb40cc 100644
--- a/source4/torture/smb2/compound.c
+++ b/source4/torture/smb2/compound.c
@@ -1306,6 +1306,74 @@ done:
 	return ret;
 }
 
+/* Test compound related finds */
+static bool test_compound_find_close(struct torture_context *tctx,
+				     struct smb2_tree *tree)
+{
+	TALLOC_CTX *mem_ctx = talloc_new(tctx);
+	const char *dname = "compound_find_dir";
+	struct smb2_create create;
+	struct smb2_find f;
+	struct smb2_handle h;
+	struct smb2_request *req = NULL;
+	const int num_files = 5000;
+	int i;
+	NTSTATUS status;
+	bool ret = true;
+
+	smb2_deltree(tree, dname);
+
+	ZERO_STRUCT(create);
+	create.in.desired_access = SEC_RIGHTS_DIR_ALL;
+	create.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
+	create.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
+	create.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
+				 NTCREATEX_SHARE_ACCESS_WRITE |
+				 NTCREATEX_SHARE_ACCESS_DELETE;
+	create.in.create_disposition = NTCREATEX_DISP_CREATE;
+	create.in.fname = dname;
+
+	smb2cli_conn_set_max_credits(tree->session->transport->conn, 256);
+
+	status = smb2_create(tree, mem_ctx, &create);
+	h = create.out.file.handle;
+
+	ZERO_STRUCT(create);
+	create.in.desired_access = SEC_RIGHTS_FILE_ALL;
+	create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
+	create.in.create_disposition = NTCREATEX_DISP_CREATE;
+
+	for (i = 0; i < num_files; i++) {
+		create.in.fname = talloc_asprintf(mem_ctx, "%s\\file%d",
+						  dname, i);
+		status = smb2_create(tree, mem_ctx, &create);
+		torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "");
+		smb2_util_close(tree, create.out.file.handle);
+	}
+
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create failed\n");
+
+	ZERO_STRUCT(f);
+	f.in.file.handle	= h;
+	f.in.pattern		= "*";
+	f.in.max_response_size	= 8*1024*1024;
+	f.in.level              = SMB2_FIND_BOTH_DIRECTORY_INFO;
+
+	req = smb2_find_send(tree, &f);
+
+	status = smb2_util_close(tree, h);
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_util_close failed\n");
+
+	status = smb2_find_recv(req, mem_ctx, &f);
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_find_recv failed\n");
+
+done:
+	smb2_util_close(tree, h);
+	smb2_deltree(tree, dname);
+	TALLOC_FREE(mem_ctx);
+	return ret;
+}
+
 /* Test compound unrelated finds */
 static bool test_compound_find_unrelated(struct torture_context *tctx,
 					 struct smb2_tree *tree)
@@ -1392,6 +1460,7 @@ struct torture_suite *torture_smb2_compound_find_init(TALLOC_CTX *ctx)
 
 	torture_suite_add_1smb2_test(suite, "compound_find_related", test_compound_find_related);
 	torture_suite_add_1smb2_test(suite, "compound_find_unrelated", test_compound_find_unrelated);
+	torture_suite_add_1smb2_test(suite, "compound_find_close", test_compound_find_close);
 
 	suite->description = talloc_strdup(suite, "SMB2-COMPOUND-FIND tests");
 
-- 
2.17.1


From 0db8a26206cdb05d74b208e530f516e5634d0958 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Thu, 26 Jul 2018 18:11:22 +0200
Subject: [PATCH 20/25] selftest: set "smbd:async dosmode = no" in the
 vfs_aio_pthread share

This just explicitly sets the current default, to ensure the tests that
use this share always use the same "smbd:async dosmode" setting even if
the default changes in the future.

Signed-off-by: Ralph Boehme <slow at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 selftest/target/Samba3.pm | 1 +
 1 file changed, 1 insertion(+)

diff --git a/selftest/target/Samba3.pm b/selftest/target/Samba3.pm
index a553405db0d8..253acf7aa5f5 100755
--- a/selftest/target/Samba3.pm
+++ b/selftest/target/Samba3.pm
@@ -775,6 +775,7 @@ sub setup_simpleserver
 	read only = no
 	vfs objects = aio_pthread
 	aio_pthread:aio open = yes
+	smbd:async dosmode = no
 
 [vfs_aio_fork]
 	path = $prefix_abs/share
-- 
2.17.1


From 16af236c3593d860e9857b988eb375d8dc61a6fb Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Thu, 26 Jul 2018 18:13:44 +0200
Subject: [PATCH 21/25] SKIP USE LATER VERSION selftest: run smbtorture3
 SMB2-BASIC tests against additional shares

This runs the smbtorture3 SMB2-BASIC tests against shares with
"smbd:async dosmode" enabled.

On one of them (vfs_aio_pthread_async_dosmode_force_sync_chdir_safe) we
force a sync threadpool which ensures we test behaviour on systems that
don't support unshare(CLONE_FS) in an autobuild on systems that do
support it.

On another one (vfs_aio_pthread_async_dosmode_force_sync_path_safe) we
test behaviour of systems without per-thread-credentials.

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

diff --git a/selftest/target/Samba3.pm b/selftest/target/Samba3.pm
index 253acf7aa5f5..83fdcf088d25 100755
--- a/selftest/target/Samba3.pm
+++ b/selftest/target/Samba3.pm
@@ -777,6 +777,32 @@ sub setup_simpleserver
 	aio_pthread:aio open = yes
 	smbd:async dosmode = no
 
+[vfs_aio_pthread_async_dosmode]
+	path = $prefix_abs/share
+	read only = no
+	vfs objects = aio_pthread xattr_tdb
+	store dos attributes = yes
+	aio_pthread:aio open = yes
+	smbd:async dosmode = yes
+
+[vfs_aio_pthread_async_dosmode_force_sync_chdir_safe]
+	path = $prefix_abs/share
+	read only = no
+	vfs objects = aio_pthread xattr_tdb
+	store dos attributes = yes
+	aio_pthread:aio open = yes
+	smbd:async dosmode = yes
+	smbd:force sync user chdir safe threadpool = yes
+
+[vfs_aio_pthread_async_dosmode_force_sync_path_safe]
+	path = $prefix_abs/share
+	read only = no
+	vfs objects = aio_pthread xattr_tdb
+	store dos attributes = yes
+	aio_pthread:aio open = yes
+	smbd:async dosmode = yes
+	smbd:force sync user path safe threadpool = yes
+
 [vfs_aio_fork]
 	path = $prefix_abs/share
         vfs objects = aio_fork
diff --git a/source3/selftest/tests.py b/source3/selftest/tests.py
index bf0ba2a44546..0abd7199a355 100755
--- a/source3/selftest/tests.py
+++ b/source3/selftest/tests.py
@@ -119,6 +119,10 @@ for t in tests:
     plantestsuite("samba3.smbtorture_s3.vfs_aio_pthread(simpleserver).%s" % t, "simpleserver", [os.path.join(samba3srcdir, "script/tests/test_smbtorture_s3.sh"), t, '//$SERVER_IP/vfs_aio_pthread', '$USERNAME', '$PASSWORD', smbtorture3, "", "-l $LOCAL_PATH"])
     plantestsuite("samba3.smbtorture_s3.vfs_aio_fork(simpleserver).%s" % t, "simpleserver", [os.path.join(samba3srcdir, "script/tests/test_smbtorture_s3.sh"), t, '//$SERVER_IP/vfs_aio_fork', '$USERNAME', '$PASSWORD', smbtorture3, "", "-l $LOCAL_PATH"])
 
+tests = ["vfs_aio_pthread_async_dosmode", "vfs_aio_pthread_async_dosmode_force_sync_chdir_safe", "vfs_aio_pthread_async_dosmode_force_sync_path_safe"]
+for t in tests:
+    plantestsuite("samba3.smbtorture_s3.%s(simpleserver).SMB2-BASIC" % t, "simpleserver", [os.path.join(samba3srcdir, "script/tests/test_smbtorture_s3.sh"), 'SMB2-BASIC', '//$SERVER_IP/' + t, '$USERNAME', '$PASSWORD', smbtorture3, "", "-l $LOCAL_PATH"])
+
 posix_tests = ["POSIX", "POSIX-APPEND", "POSIX-SYMLINK-ACL", "POSIX-SYMLINK-EA", "POSIX-OFD-LOCK",
               "POSIX-STREAM-DELETE", "WINDOWS-BAD-SYMLINK" ]
 
-- 
2.17.1


From 4518cef693b167c603bdb4b40040e017f851b0c9 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 26 Jul 2018 23:32:22 +0200
Subject: [PATCH 22/25] Revert "SKIP USE LATER VERSION selftest: run
 smbtorture3 SMB2-BASIC tests against additional shares"

This reverts commit 16af236c3593d860e9857b988eb375d8dc61a6fb.
---
 selftest/target/Samba3.pm | 26 --------------------------
 source3/selftest/tests.py |  4 ----
 2 files changed, 30 deletions(-)

diff --git a/selftest/target/Samba3.pm b/selftest/target/Samba3.pm
index 83fdcf088d25..253acf7aa5f5 100755
--- a/selftest/target/Samba3.pm
+++ b/selftest/target/Samba3.pm
@@ -777,32 +777,6 @@ sub setup_simpleserver
 	aio_pthread:aio open = yes
 	smbd:async dosmode = no
 
-[vfs_aio_pthread_async_dosmode]
-	path = $prefix_abs/share
-	read only = no
-	vfs objects = aio_pthread xattr_tdb
-	store dos attributes = yes
-	aio_pthread:aio open = yes
-	smbd:async dosmode = yes
-
-[vfs_aio_pthread_async_dosmode_force_sync_chdir_safe]
-	path = $prefix_abs/share
-	read only = no
-	vfs objects = aio_pthread xattr_tdb
-	store dos attributes = yes
-	aio_pthread:aio open = yes
-	smbd:async dosmode = yes
-	smbd:force sync user chdir safe threadpool = yes
-
-[vfs_aio_pthread_async_dosmode_force_sync_path_safe]
-	path = $prefix_abs/share
-	read only = no
-	vfs objects = aio_pthread xattr_tdb
-	store dos attributes = yes
-	aio_pthread:aio open = yes
-	smbd:async dosmode = yes
-	smbd:force sync user path safe threadpool = yes
-
 [vfs_aio_fork]
 	path = $prefix_abs/share
         vfs objects = aio_fork
diff --git a/source3/selftest/tests.py b/source3/selftest/tests.py
index 0abd7199a355..bf0ba2a44546 100755
--- a/source3/selftest/tests.py
+++ b/source3/selftest/tests.py
@@ -119,10 +119,6 @@ for t in tests:
     plantestsuite("samba3.smbtorture_s3.vfs_aio_pthread(simpleserver).%s" % t, "simpleserver", [os.path.join(samba3srcdir, "script/tests/test_smbtorture_s3.sh"), t, '//$SERVER_IP/vfs_aio_pthread', '$USERNAME', '$PASSWORD', smbtorture3, "", "-l $LOCAL_PATH"])
     plantestsuite("samba3.smbtorture_s3.vfs_aio_fork(simpleserver).%s" % t, "simpleserver", [os.path.join(samba3srcdir, "script/tests/test_smbtorture_s3.sh"), t, '//$SERVER_IP/vfs_aio_fork', '$USERNAME', '$PASSWORD', smbtorture3, "", "-l $LOCAL_PATH"])
 
-tests = ["vfs_aio_pthread_async_dosmode", "vfs_aio_pthread_async_dosmode_force_sync_chdir_safe", "vfs_aio_pthread_async_dosmode_force_sync_path_safe"]
-for t in tests:
-    plantestsuite("samba3.smbtorture_s3.%s(simpleserver).SMB2-BASIC" % t, "simpleserver", [os.path.join(samba3srcdir, "script/tests/test_smbtorture_s3.sh"), 'SMB2-BASIC', '//$SERVER_IP/' + t, '$USERNAME', '$PASSWORD', smbtorture3, "", "-l $LOCAL_PATH"])
-
 posix_tests = ["POSIX", "POSIX-APPEND", "POSIX-SYMLINK-ACL", "POSIX-SYMLINK-EA", "POSIX-OFD-LOCK",
               "POSIX-STREAM-DELETE", "WINDOWS-BAD-SYMLINK" ]
 
-- 
2.17.1


From 17a931ca2f831013d050bff3c58fbb071753f3ee Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Thu, 26 Jul 2018 18:13:44 +0200
Subject: [PATCH 23/25] selftest: run smbtorture3 SMB2-BASIC tests against
 additional shares

This runs the smbtorture3 SMB2-BASIC and smb2.compound_find tests against shares
with "smbd:async dosmode" enabled.

On the vfs_aio_pthread_async_dosmode_force_sync* shares we
force a sync threadpool which ensures we test behaviour on systems that
don't support unshare(CLONE_FS) and also don't support
per-thread-credentials. This simulates the code path of non linux
systems. And makes sure that we don't regress there.

We also test with xattr_tdb and without.

Pair-Programmed-With: Stefan Metzmacher <metze at samba.org>

Signed-off-by: Ralph Boehme <slow at samba.org>
Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 selftest/target/Samba3.pm | 42 +++++++++++++++++++++++++++++++++++++++
 source3/selftest/tests.py | 11 ++++++++++
 2 files changed, 53 insertions(+)

diff --git a/selftest/target/Samba3.pm b/selftest/target/Samba3.pm
index 253acf7aa5f5..5b8cf9ea6d81 100755
--- a/selftest/target/Samba3.pm
+++ b/selftest/target/Samba3.pm
@@ -777,6 +777,48 @@ sub setup_simpleserver
 	aio_pthread:aio open = yes
 	smbd:async dosmode = no
 
+[vfs_aio_pthread_async_dosmode_default1]
+	path = $prefix_abs/share
+	read only = no
+	vfs objects = aio_pthread
+	store dos attributes = yes
+	aio_pthread:aio open = yes
+	smbd:async dosmode = yes
+
+[vfs_aio_pthread_async_dosmode_default2]
+	path = $prefix_abs/share
+	read only = no
+	vfs objects = aio_pthread xattr_tdb
+	store dos attributes = yes
+	aio_pthread:aio open = yes
+	smbd:async dosmode = yes
+
+[vfs_aio_pthread_async_dosmode_force_sync1]
+	path = $prefix_abs/share
+	read only = no
+	vfs objects = aio_pthread
+	store dos attributes = yes
+	aio_pthread:aio open = yes
+	smbd:async dosmode = yes
+	# This simulates non linux systems
+	smbd:force sync user path safe threadpool = yes
+	smbd:force sync user chdir safe threadpool = yes
+	smbd:force sync root path safe threadpool = yes
+	smbd:force sync root chdir safe threadpool = yes
+
+[vfs_aio_pthread_async_dosmode_force_sync2]
+	path = $prefix_abs/share
+	read only = no
+	vfs objects = aio_pthread xattr_tdb
+	store dos attributes = yes
+	aio_pthread:aio open = yes
+	smbd:async dosmode = yes
+	# This simulates non linux systems
+	smbd:force sync user path safe threadpool = yes
+	smbd:force sync user chdir safe threadpool = yes
+	smbd:force sync root path safe threadpool = yes
+	smbd:force sync root chdir safe threadpool = yes
+
 [vfs_aio_fork]
 	path = $prefix_abs/share
         vfs objects = aio_fork
diff --git a/source3/selftest/tests.py b/source3/selftest/tests.py
index bf0ba2a44546..b2b2ff260775 100755
--- a/source3/selftest/tests.py
+++ b/source3/selftest/tests.py
@@ -119,6 +119,17 @@ for t in tests:
     plantestsuite("samba3.smbtorture_s3.vfs_aio_pthread(simpleserver).%s" % t, "simpleserver", [os.path.join(samba3srcdir, "script/tests/test_smbtorture_s3.sh"), t, '//$SERVER_IP/vfs_aio_pthread', '$USERNAME', '$PASSWORD', smbtorture3, "", "-l $LOCAL_PATH"])
     plantestsuite("samba3.smbtorture_s3.vfs_aio_fork(simpleserver).%s" % t, "simpleserver", [os.path.join(samba3srcdir, "script/tests/test_smbtorture_s3.sh"), t, '//$SERVER_IP/vfs_aio_fork', '$USERNAME', '$PASSWORD', smbtorture3, "", "-l $LOCAL_PATH"])
 
+shares = [
+    "vfs_aio_pthread_async_dosmode_default1",
+    "vfs_aio_pthread_async_dosmode_default2",
+    "vfs_aio_pthread_async_dosmode_force_sync1",
+    "vfs_aio_pthread_async_dosmode_force_sync2"
+]
+for s in shares:
+    plantestsuite("samba3.smbtorture_s3.%s(simpleserver).SMB2-BASIC" % s, "simpleserver", [os.path.join(samba3srcdir, "script/tests/test_smbtorture_s3.sh"), 'SMB2-BASIC', '//$SERVER_IP/' + s, '$USERNAME', '$PASSWORD', smbtorture3, "", "-l $LOCAL_PATH"])
+    t = "smb2.compound_find"
+    plansmbtorture4testsuite(t, "simpleserver", "//%s/%s %s" % ('$SERVER_IP', s, ' -U$USERNAME%$PASSWORD'), description=s)
+
 posix_tests = ["POSIX", "POSIX-APPEND", "POSIX-SYMLINK-ACL", "POSIX-SYMLINK-EA", "POSIX-OFD-LOCK",
               "POSIX-STREAM-DELETE", "WINDOWS-BAD-SYMLINK" ]
 
-- 
2.17.1


From 6b480814761f0f081cea8d6e0f6f62c637b4fed6 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Wed, 25 Jul 2018 20:37:46 +0200
Subject: [PATCH 24/25] usleep

---
 source3/modules/vfs_default.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/source3/modules/vfs_default.c b/source3/modules/vfs_default.c
index a492c9d36276..3471843730a8 100644
--- a/source3/modules/vfs_default.c
+++ b/source3/modules/vfs_default.c
@@ -1484,6 +1484,8 @@ static NTSTATUS vfswrap_get_dos_attributes(struct vfs_handle_struct *handle,
 		*dosmode |= FILE_ATTRIBUTE_OFFLINE;
 	}
 
+	usleep(5*1000);
+
 	return get_ea_dos_attribute(handle->conn, smb_fname, dosmode);
 }
 
@@ -3009,6 +3011,8 @@ static void vfswrap_getxattrat_do(void *private_data)
 	PROFILE_TIMESTAMP(&start_time);
 	SMBPROFILE_BYTES_ASYNC_SET_BUSY(state->profile_bytes);
 
+	usleep(5*1000);
+
 	/*
 	 * Here we simulate a getxattrat()
 	 * call using fchdir();getxattr()
-- 
2.17.1


From acba1d9403f3fac5e413057adea1676360acb18b Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 26 Jul 2018 15:13:10 +0200
Subject: [PATCH 25/25] Revert "usleep"

This reverts commit bb2d1c558bf68e86c5698fff93356db04c69c099.
---
 source3/modules/vfs_default.c | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/source3/modules/vfs_default.c b/source3/modules/vfs_default.c
index 3471843730a8..a492c9d36276 100644
--- a/source3/modules/vfs_default.c
+++ b/source3/modules/vfs_default.c
@@ -1484,8 +1484,6 @@ static NTSTATUS vfswrap_get_dos_attributes(struct vfs_handle_struct *handle,
 		*dosmode |= FILE_ATTRIBUTE_OFFLINE;
 	}
 
-	usleep(5*1000);
-
 	return get_ea_dos_attribute(handle->conn, smb_fname, dosmode);
 }
 
@@ -3011,8 +3009,6 @@ static void vfswrap_getxattrat_do(void *private_data)
 	PROFILE_TIMESTAMP(&start_time);
 	SMBPROFILE_BYTES_ASYNC_SET_BUSY(state->profile_bytes);
 
-	usleep(5*1000);
-
 	/*
 	 * Here we simulate a getxattrat()
 	 * call using fchdir();getxattr()
-- 
2.17.1

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 833 bytes
Desc: OpenPGP digital signature
URL: <http://lists.samba.org/pipermail/samba-technical/attachments/20180726/ea84951e/signature-0001.sig>


More information about the samba-technical mailing list