]> git.ipfire.org Git - thirdparty/samba.git/commitdiff
smbd: factor out smb2_query_directory_next_entry() from smbd_smb2_query_directory_send()
authorRalph Boehme <slow@samba.org>
Tue, 3 Jul 2018 09:25:57 +0000 (11:25 +0200)
committerRalph Boehme <slow@samba.org>
Fri, 27 Jul 2018 11:07:15 +0000 (13:07 +0200)
This paves the way for adding async functions into the enumeration loop.

Best viewed with: git show -w

Signed-off-by: Ralph Boehme <slow@samba.org>
Reviewed-by: Stefan Metzmacher <metze@samba.org>
source3/smbd/smb2_query_directory.c

index 7584598c2c391ae0586a59866b90e9f901a0c487..6577ab81eaaf14d67b075402c70a8236f8da6645 100644 (file)
@@ -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);