s3:smbd: pass (raw) ev to fetch_dos_mode_send instead of smb_vfs_ev_glue
[samba.git] / source3 / smbd / smb2_query_directory.c
index 7584598c2c391ae0586a59866b90e9f901a0c487..1a3447c3a828ed06bbd941db5a822f5a42f754c4 100644 (file)
@@ -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,16 +206,47 @@ 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 tevent_context *ev,
+       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 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_dosmode;
+       bool async_ask_sharemode;
+       int last_entry_off;
+       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,
@@ -230,40 +262,32 @@ 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;
+       bool ok;
 
        req = tevent_req_create(mem_ctx, &state,
                                struct smbd_smb2_query_directory_state);
        if (req == NULL) {
                return NULL;
        }
-       state->ev = ev;
+       state->ev = fsp->conn->sconn->raw_ev_ctx;
+       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 +296,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 +339,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 +390,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 +402,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);
@@ -400,6 +424,7 @@ static struct tevent_req *smbd_smb2_query_directory_send(TALLOC_CTX *mem_ctx,
                                conn,
                                fullpath,
                                ucf_flags,
+                               NULL,
                                &wcard_has_wild,
                                &smb_fname);
 
@@ -409,7 +434,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 +445,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 +464,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 +478,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 +492,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,16 +501,30 @@ 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_smbd_search_ask_sharemode(SNUM(conn));
+
+               state->async_dosmode = lp_smbd_async_dosmode(SNUM(conn));
+       }
+
+       if (state->ask_sharemode && lp_clustering()) {
+               state->ask_sharemode = false;
+               state->async_ask_sharemode = true;
        }
 
-       if (ask_sharemode && lp_clustering()) {
-               ask_sharemode = false;
-               async_ask_sharemode = true;
+       if (state->async_dosmode) {
+               size_t max_threads;
+
+               max_threads = pthreadpool_tevent_max_threads(conn->sconn->raw_thread_pool);
+
+               state->max_async_dosmode_active = lp_smbd_max_async_dosmode(
+                                                       SNUM(conn));
+               if (state->max_async_dosmode_active == 0) {
+                       state->max_async_dosmode_active = max_threads * 2;
+               }
+       }
 
+       if (state->async_dosmode || state->async_ask_sharemode) {
                /*
                 * Should we only set async_internal
                 * if we're not the last request in
@@ -504,127 +540,186 @@ 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);
+       }
+
+       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;
+}
+
+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;
+       struct file_id file_id;
+       NTSTATUS status;
+       bool get_dosmode = !state->async_dosmode;
+       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,
+                                          get_dosmode,
+                                          8, /* align to 8 bytes */
+                                          false, /* no padding */
+                                          &state->pdata,
+                                          state->base_data,
+                                          state->end_data,
+                                          space_remaining,
+                                          &smb_fname,
+                                          &got_exact_match,
+                                          &state->last_entry_off,
+                                          NULL,
+                                          &file_id);
+
+       off = (int)PTR_DIFF(state->pdata, state->base_data);
+
+       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;
                }
+       }
 
-               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);
+       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,
+                                              buf,
+                                              &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++;
+       }
 
-                       state->async_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->ev,
+                                            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++;
 
-               num++;
-               state->out_output_buffer.length = off;
+               outstanding_aio = pthreadpool_tevent_queued_jobs(
+                                       state->fsp->conn->sconn->raw_thread_pool);
 
-               if (num >= max_count) {
+               if (outstanding_aio > state->max_async_dosmode_active) {
                        stop = true;
                }
+       }
 
-               if (!stop) {
-                       continue;
-               }
+       TALLOC_FREE(smb_fname);
+
+       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->async_dosmode_active > 0) {
+               return true;
+       }
 
-                       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;
-               }
+       if (state->find_async_delay_usec > 0) {
+               struct timeval tv;
+               struct tevent_req *subreq = NULL;
 
-               tevent_req_done(req);
-               return tevent_req_post(req, ev);
+               /*
+                * 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);
+
+               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 true;
        }
 
-       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 +728,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 +736,54 @@ 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_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(
+               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 ||
+           state->async_dosmode_active > 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);
 
@@ -813,3 +950,126 @@ 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 tevent_context *ev,
+                       struct files_struct *dir_fsp,
+                       struct smb_filename **smb_fname,
+                       uint32_t info_level,
+                       uint8_t *entry_marshall_buf)
+{
+       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, ev, 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;
+}