s3-tevent: only include ../lib/util/tevent wrappers where needed.
[bbaumbach/samba-autobuild/.git] / source3 / smbd / smb2_find.c
index 89ba54c7e144912a93d376a0db597ac1fdd10172..9fc8f1fef26696aa4c0f92baeb9df337fe1b5fe9 100644 (file)
 */
 
 #include "includes.h"
+#include "smbd/smbd.h"
 #include "smbd/globals.h"
-#include "../source4/libcli/smb2/smb2_constants.h"
+#include "../libcli/smb/smb_common.h"
+#include "trans2.h"
+#include "../lib/util/tevent_ntstatus.h"
 
 static struct tevent_req *smbd_smb2_find_send(TALLOC_CTX *mem_ctx,
                                              struct tevent_context *ev,
@@ -89,6 +92,17 @@ NTSTATUS smbd_smb2_request_process_find(struct smbd_smb2_request *req)
                return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
        }
 
+       /* The output header is 8 bytes. */
+       if (in_output_buffer_length <= 8) {
+               return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
+       }
+
+       DEBUG(10,("smbd_smb2_request_find_done: in_output_buffer_length = %u\n",
+               (unsigned int)in_output_buffer_length ));
+
+       /* Take into account the output header. */
+       in_output_buffer_length -= 8;
+
        in_file_name_buffer.data = (uint8_t *)req->in.vector[i+2].iov_base;
        in_file_name_buffer.length = in_file_name_length;
 
@@ -96,19 +110,19 @@ NTSTATUS smbd_smb2_request_process_find(struct smbd_smb2_request *req)
                                   in_file_name_buffer.data,
                                   in_file_name_buffer.length,
                                   &in_file_name_string,
-                                  &in_file_name_string_size, false);
+                                  &in_file_name_string_size);
        if (!ok) {
                return smbd_smb2_request_error(req, NT_STATUS_ILLEGAL_CHARACTER);
        }
 
        if (req->compat_chain_fsp) {
                /* skip check */
-       } else if (in_file_id_persistent != 0) {
+       } else if (in_file_id_persistent != in_file_id_volatile) {
                return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED);
        }
 
        subreq = smbd_smb2_find_send(req,
-                                    req->conn->smb2.event_ctx,
+                                    req->sconn->smb2.event_ctx,
                                     req,
                                     in_file_info_class,
                                     in_flags,
@@ -121,11 +135,7 @@ NTSTATUS smbd_smb2_request_process_find(struct smbd_smb2_request *req)
        }
        tevent_req_set_callback(subreq, smbd_smb2_request_find_done, req);
 
-       if (tevent_req_is_in_progress(subreq)) {
-               return smbd_smb2_request_pending_queue(req);
-       }
-
-       return NT_STATUS_OK;
+       return smbd_smb2_request_pending_queue(req, subreq);
 }
 
 static void smbd_smb2_request_find_done(struct tevent_req *subreq)
@@ -148,7 +158,7 @@ static void smbd_smb2_request_find_done(struct tevent_req *subreq)
        if (!NT_STATUS_IS_OK(status)) {
                error = smbd_smb2_request_error(req, status);
                if (!NT_STATUS_IS_OK(error)) {
-                       smbd_server_connection_terminate(req->conn,
+                       smbd_server_connection_terminate(req->sconn,
                                                         nt_errstr(error));
                        return;
                }
@@ -163,7 +173,7 @@ static void smbd_smb2_request_find_done(struct tevent_req *subreq)
        if (outbody.data == NULL) {
                error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
                if (!NT_STATUS_IS_OK(error)) {
-                       smbd_server_connection_terminate(req->conn,
+                       smbd_server_connection_terminate(req->sconn,
                                                         nt_errstr(error));
                        return;
                }
@@ -176,11 +186,14 @@ static void smbd_smb2_request_find_done(struct tevent_req *subreq)
        SIVAL(outbody.data, 0x04,
              out_output_buffer.length);        /* output buffer length */
 
+       DEBUG(10,("smbd_smb2_request_find_done: out_output_buffer.length = %u\n",
+               (unsigned int)out_output_buffer.length ));
+
        outdyn = out_output_buffer;
 
        error = smbd_smb2_request_done(req, outbody, &outdyn);
        if (!NT_STATUS_IS_OK(error)) {
-               smbd_server_connection_terminate(req->conn,
+               smbd_server_connection_terminate(req->sconn,
                                                 nt_errstr(error));
                return;
        }
@@ -206,6 +219,19 @@ static struct tevent_req *smbd_smb2_find_send(TALLOC_CTX *mem_ctx,
        struct smb_request *smbreq;
        connection_struct *conn = smb2req->tcon->compat_conn;
        files_struct *fsp;
+       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 = true;
 
        req = tevent_req_create(mem_ctx, &state,
                                struct smbd_smb2_find_state);
@@ -237,7 +263,194 @@ static struct tevent_req *smbd_smb2_find_send(TALLOC_CTX *mem_ctx,
                return tevent_req_post(req, ev);
        }
 
-       tevent_req_nterror(req, NT_STATUS_NOT_IMPLEMENTED);
+       if (!fsp->is_directory) {
+               tevent_req_nterror(req, NT_STATUS_NOT_SUPPORTED);
+               return tevent_req_post(req, ev);
+       }
+
+       if (strcmp(in_file_name, "") == 0) {
+               tevent_req_nterror(req, NT_STATUS_OBJECT_NAME_INVALID);
+               return tevent_req_post(req, ev);
+       }
+       if (strcmp(in_file_name, "\\") == 0) {
+               tevent_req_nterror(req, NT_STATUS_OBJECT_NAME_INVALID);
+               return tevent_req_post(req, ev);
+       }
+       if (strcmp(in_file_name, "/") == 0) {
+               tevent_req_nterror(req, NT_STATUS_OBJECT_NAME_INVALID);
+               return tevent_req_post(req, ev);
+       }
+
+       if (in_output_buffer_length > 0x10000) {
+               tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+               return tevent_req_post(req, ev);
+       }
+
+       switch (in_file_info_class) {
+       case SMB2_FIND_DIRECTORY_INFO:
+               info_level = SMB_FIND_FILE_DIRECTORY_INFO;
+               break;
+
+       case SMB2_FIND_FULL_DIRECTORY_INFO:
+               info_level = SMB_FIND_FILE_FULL_DIRECTORY_INFO;
+               break;
+
+       case SMB2_FIND_BOTH_DIRECTORY_INFO:
+               info_level = SMB_FIND_FILE_BOTH_DIRECTORY_INFO;
+               break;
+
+       case SMB2_FIND_NAME_INFO:
+               info_level = SMB_FIND_FILE_NAMES_INFO;
+               break;
+
+       case SMB2_FIND_ID_BOTH_DIRECTORY_INFO:
+               info_level = SMB_FIND_ID_BOTH_DIRECTORY_INFO;
+               break;
+
+       case SMB2_FIND_ID_FULL_DIRECTORY_INFO:
+               info_level = SMB_FIND_ID_FULL_DIRECTORY_INFO;
+               break;
+
+       default:
+               tevent_req_nterror(req, NT_STATUS_INVALID_INFO_CLASS);
+               return tevent_req_post(req, ev);
+       }
+
+       if (in_flags & SMB2_CONTINUE_FLAG_REOPEN) {
+               dptr_CloseDir(fsp);
+       }
+
+       if (fsp->dptr == NULL) {
+               bool wcard_has_wild;
+
+               if (!(fsp->access_mask & SEC_DIR_LIST)) {
+                       tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
+                       return tevent_req_post(req, ev);
+               }
+
+               wcard_has_wild = ms_has_wild(in_file_name);
+
+               status = dptr_create(conn,
+                                    fsp,
+                                    fsp->fsp_name->base_name,
+                                    false, /* old_handle */
+                                    false, /* expect_close */
+                                    0, /* spid */
+                                    in_file_name, /* wcard */
+                                    wcard_has_wild,
+                                    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;
+       } else {
+               empty_status = STATUS_NO_MORE_FILES;
+       }
+
+       if (in_flags & SMB2_CONTINUE_FLAG_RESTART) {
+               dptr_SeekDir(fsp->dptr, 0);
+       }
+
+       if (in_flags & SMB2_CONTINUE_FLAG_SINGLE) {
+               max_count = 1;
+       } else {
+               max_count = UINT16_MAX;
+       }
+
+#define DIR_ENTRY_SAFETY_MARGIN 4096
+
+       state->out_output_buffer = data_blob_talloc(state, NULL,
+                       in_output_buffer_length + DIR_ENTRY_SAFETY_MARGIN);
+       if (tevent_req_nomem(state->out_output_buffer.data, req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       state->out_output_buffer.length = 0;
+       pdata = (char *)state->out_output_buffer.data;
+       base_data = 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;
+
+       DEBUG(8,("smbd_smb2_find_send: dirpath=<%s> dontdescend=<%s>, "
+               "in_output_buffer_length = %u\n",
+               fsp->fsp_name->base_name, lp_dontdescend(SNUM(conn)),
+               (unsigned int)in_output_buffer_length ));
+       if (in_list(fsp->fsp_name->base_name,lp_dontdescend(SNUM(conn)),
+                       conn->case_sensitive)) {
+               dont_descend = true;
+       }
+
+       ask_sharemode = lp_parm_bool(SNUM(conn),
+                                    "smbd", "search ask sharemode",
+                                    true);
+
+       while (true) {
+               bool ok;
+               bool got_exact_match = false;
+               bool out_of_space = false;
+               int space_remaining = in_output_buffer_length - off;
+
+               SMB_ASSERT(space_remaining >= 0);
+
+               ok = smbd_dirptr_lanman2_entry(state,
+                                              conn,
+                                              fsp->dptr,
+                                              smbreq->flags2,
+                                              in_file_name,
+                                              dirtype,
+                                              info_level,
+                                              false, /* requires_resume_key */
+                                              dont_descend,
+                                              ask_sharemode,
+                                              8, /* align to 8 bytes */
+                                              false, /* no padding */
+                                              &pdata,
+                                              base_data,
+                                              end_data,
+                                              space_remaining,
+                                              &out_of_space,
+                                              &got_exact_match,
+                                              &last_entry_off,
+                                              NULL);
+
+               off = (int)PTR_DIFF(pdata, base_data);
+
+               if (!ok) {
+                       if (num > 0) {
+                               SIVAL(state->out_output_buffer.data, last_entry_off, 0);
+                               tevent_req_done(req);
+                               return tevent_req_post(req, ev);
+                       } else if (out_of_space) {
+                               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);
+                       }
+               }
+
+               num++;
+               state->out_output_buffer.length = off;
+
+               if (num < max_count) {
+                       continue;
+               }
+
+               SIVAL(state->out_output_buffer.data, last_entry_off, 0);
+               tevent_req_done(req);
+               return tevent_req_post(req, ev);
+       }
+
+       tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
        return tevent_req_post(req, ev);
 }