diff options
-rw-r--r-- | source3/smbd/smb2_find.c | 198 |
1 files changed, 197 insertions, 1 deletions
diff --git a/source3/smbd/smb2_find.c b/source3/smbd/smb2_find.c index 40ba320fc5..546aed8db3 100644 --- a/source3/smbd/smb2_find.c +++ b/source3/smbd/smb2_find.c @@ -202,6 +202,20 @@ 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; + uint64_t off = 0; + uint32_t num = 0; + uint32_t dirtype = aHIDDEN | aSYSTEM | aDIR; + const char *directory; + bool dont_descend = false; + bool ask_sharemode = true; req = tevent_req_create(mem_ctx, &state, struct smbd_smb2_find_state); @@ -233,7 +247,189 @@ 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); + } + + directory = fsp->fsp_name->base_name; + + 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) { + if (fsp->dptr) { + dptr_CloseDir(fsp->dptr); + fsp->dptr = NULL; + } + } + + 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, + directory, + 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 = pdata + in_output_buffer_length; + last_entry_off = 0; + off = 0; + num = 0; + + DEBUG(8,("smbd_smb2_find_send: dirpath=<%s> dontdescend=<%s>\n", + directory, lp_dontdescend(SNUM(conn)))); + if (in_list(directory,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; + + 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 = 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); } |