2 Unix SMB/CIFS implementation.
5 Copyright (C) Stefan Metzmacher 2009
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "smbd/smbd.h"
23 #include "smbd/globals.h"
24 #include "../libcli/smb/smb_common.h"
26 #include "../lib/util/tevent_ntstatus.h"
27 #include "system/filesys.h"
30 #define DBGC_CLASS DBGC_SMB2
32 static struct tevent_req *smbd_smb2_query_directory_send(TALLOC_CTX *mem_ctx,
33 struct tevent_context *ev,
34 struct smbd_smb2_request *smb2req,
35 struct files_struct *in_fsp,
36 uint8_t in_file_info_class,
38 uint32_t in_file_index,
39 uint32_t in_output_buffer_length,
40 const char *in_file_name);
41 static NTSTATUS smbd_smb2_query_directory_recv(struct tevent_req *req,
43 DATA_BLOB *out_output_buffer);
45 static void smbd_smb2_request_find_done(struct tevent_req *subreq);
46 NTSTATUS smbd_smb2_request_process_query_directory(struct smbd_smb2_request *req)
49 const uint8_t *inbody;
50 uint8_t in_file_info_class;
52 uint32_t in_file_index;
53 uint64_t in_file_id_persistent;
54 uint64_t in_file_id_volatile;
55 struct files_struct *in_fsp;
56 uint16_t in_file_name_offset;
57 uint16_t in_file_name_length;
58 DATA_BLOB in_file_name_buffer;
59 char *in_file_name_string;
60 size_t in_file_name_string_size;
61 uint32_t in_output_buffer_length;
62 struct tevent_req *subreq;
65 status = smbd_smb2_request_verify_sizes(req, 0x21);
66 if (!NT_STATUS_IS_OK(status)) {
67 return smbd_smb2_request_error(req, status);
69 inbody = SMBD_SMB2_IN_BODY_PTR(req);
71 in_file_info_class = CVAL(inbody, 0x02);
72 in_flags = CVAL(inbody, 0x03);
73 in_file_index = IVAL(inbody, 0x04);
74 in_file_id_persistent = BVAL(inbody, 0x08);
75 in_file_id_volatile = BVAL(inbody, 0x10);
76 in_file_name_offset = SVAL(inbody, 0x18);
77 in_file_name_length = SVAL(inbody, 0x1A);
78 in_output_buffer_length = IVAL(inbody, 0x1C);
80 if (in_file_name_offset == 0 && in_file_name_length == 0) {
82 } else if (in_file_name_offset !=
83 (SMB2_HDR_BODY + SMBD_SMB2_IN_BODY_LEN(req))) {
84 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
87 if (in_file_name_length > SMBD_SMB2_IN_DYN_LEN(req)) {
88 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
91 /* The output header is 8 bytes. */
92 if (in_output_buffer_length <= 8) {
93 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
96 DEBUG(10,("smbd_smb2_request_find_done: in_output_buffer_length = %u\n",
97 (unsigned int)in_output_buffer_length ));
99 /* Take into account the output header. */
100 in_output_buffer_length -= 8;
102 in_file_name_buffer.data = SMBD_SMB2_IN_DYN_PTR(req);
103 in_file_name_buffer.length = in_file_name_length;
105 ok = convert_string_talloc(req, CH_UTF16, CH_UNIX,
106 in_file_name_buffer.data,
107 in_file_name_buffer.length,
108 &in_file_name_string,
109 &in_file_name_string_size);
111 return smbd_smb2_request_error(req, NT_STATUS_ILLEGAL_CHARACTER);
114 if (in_file_name_buffer.length == 0) {
115 in_file_name_string_size = 0;
118 if (strlen(in_file_name_string) != in_file_name_string_size) {
119 return smbd_smb2_request_error(req, NT_STATUS_OBJECT_NAME_INVALID);
122 in_fsp = file_fsp_smb2(req, in_file_id_persistent, in_file_id_volatile);
123 if (in_fsp == NULL) {
124 return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED);
127 subreq = smbd_smb2_query_directory_send(req, req->ev_ctx,
132 in_output_buffer_length,
133 in_file_name_string);
134 if (subreq == NULL) {
135 return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
137 tevent_req_set_callback(subreq, smbd_smb2_request_find_done, req);
139 return smbd_smb2_request_pending_queue(req, subreq, 500);
142 static void smbd_smb2_request_find_done(struct tevent_req *subreq)
144 struct smbd_smb2_request *req = tevent_req_callback_data(subreq,
145 struct smbd_smb2_request);
148 uint16_t out_output_buffer_offset;
149 DATA_BLOB out_output_buffer = data_blob_null;
151 NTSTATUS error; /* transport error */
153 status = smbd_smb2_query_directory_recv(subreq,
157 if (!NT_STATUS_IS_OK(status)) {
158 error = smbd_smb2_request_error(req, status);
159 if (!NT_STATUS_IS_OK(error)) {
160 smbd_server_connection_terminate(req->xconn,
167 out_output_buffer_offset = SMB2_HDR_BODY + 0x08;
169 outbody = smbd_smb2_generate_outbody(req, 0x08);
170 if (outbody.data == NULL) {
171 error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
172 if (!NT_STATUS_IS_OK(error)) {
173 smbd_server_connection_terminate(req->xconn,
180 SSVAL(outbody.data, 0x00, 0x08 + 1); /* struct size */
181 SSVAL(outbody.data, 0x02,
182 out_output_buffer_offset); /* output buffer offset */
183 SIVAL(outbody.data, 0x04,
184 out_output_buffer.length); /* output buffer length */
186 DEBUG(10,("smbd_smb2_request_find_done: out_output_buffer.length = %u\n",
187 (unsigned int)out_output_buffer.length ));
189 outdyn = out_output_buffer;
191 error = smbd_smb2_request_done(req, outbody, &outdyn);
192 if (!NT_STATUS_IS_OK(error)) {
193 smbd_server_connection_terminate(req->xconn,
199 static struct tevent_req *fetch_write_time_send(TALLOC_CTX *mem_ctx,
200 struct tevent_context *ev,
201 connection_struct *conn,
204 char *entry_marshall_buf,
206 static NTSTATUS fetch_write_time_recv(struct tevent_req *req);
208 struct smbd_smb2_query_directory_state {
209 struct tevent_context *ev;
210 struct smbd_smb2_request *smb2req;
211 uint64_t async_sharemode_count;
212 uint32_t find_async_delay_usec;
213 DATA_BLOB out_output_buffer;
214 struct smb_request *smbreq;
215 int in_output_buffer_length;
216 struct files_struct *fsp;
217 const char *in_file_name;
218 NTSTATUS empty_status;
228 bool async_ask_sharemode;
233 static bool smb2_query_directory_next_entry(struct tevent_req *req);
234 static void smb2_query_directory_fetch_write_time_done(struct tevent_req *subreq);
235 static void smb2_query_directory_waited(struct tevent_req *subreq);
237 static struct tevent_req *smbd_smb2_query_directory_send(TALLOC_CTX *mem_ctx,
238 struct tevent_context *ev,
239 struct smbd_smb2_request *smb2req,
240 struct files_struct *fsp,
241 uint8_t in_file_info_class,
243 uint32_t in_file_index,
244 uint32_t in_output_buffer_length,
245 const char *in_file_name)
247 struct smbXsrv_connection *xconn = smb2req->xconn;
248 struct tevent_req *req;
249 struct smbd_smb2_query_directory_state *state;
250 connection_struct *conn = smb2req->tcon->compat;
252 bool wcard_has_wild = false;
257 req = tevent_req_create(mem_ctx, &state,
258 struct smbd_smb2_query_directory_state);
264 state->smb2req = smb2req;
265 state->in_output_buffer_length = in_output_buffer_length;
266 state->in_file_name = in_file_name;
267 state->out_output_buffer = data_blob_null;
268 state->dirtype = FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_DIRECTORY;
270 DEBUG(10,("smbd_smb2_query_directory_send: %s - %s\n",
271 fsp_str_dbg(fsp), fsp_fnum_dbg(fsp)));
273 state->smbreq = smbd_smb2_fake_smb_request(smb2req);
274 if (tevent_req_nomem(state->smbreq, req)) {
275 return tevent_req_post(req, ev);
278 if (!fsp->is_directory) {
279 tevent_req_nterror(req, NT_STATUS_NOT_SUPPORTED);
280 return tevent_req_post(req, ev);
283 if (strcmp(state->in_file_name, "") == 0) {
284 tevent_req_nterror(req, NT_STATUS_OBJECT_NAME_INVALID);
285 return tevent_req_post(req, ev);
287 if (strchr_m(state->in_file_name, '\\') != NULL) {
288 tevent_req_nterror(req, NT_STATUS_OBJECT_NAME_INVALID);
289 return tevent_req_post(req, ev);
291 if (strchr_m(state->in_file_name, '/') != NULL) {
292 tevent_req_nterror(req, NT_STATUS_OBJECT_NAME_INVALID);
293 return tevent_req_post(req, ev);
296 p = strptime(state->in_file_name, GMT_FORMAT, &tm);
297 if ((p != NULL) && (*p =='\0')) {
299 * Bogus find that asks for a shadow copy timestamp as a
300 * directory. The correct response is that it does not exist as
303 tevent_req_nterror(req, NT_STATUS_NO_SUCH_FILE);
304 return tevent_req_post(req, ev);
307 if (in_output_buffer_length > xconn->smb2.server.max_trans) {
308 DEBUG(2,("smbd_smb2_query_directory_send: "
309 "client ignored max trans:%s: 0x%08X: 0x%08X\n",
310 __location__, in_output_buffer_length,
311 xconn->smb2.server.max_trans));
312 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
313 return tevent_req_post(req, ev);
316 status = smbd_smb2_request_verify_creditcharge(smb2req,
317 in_output_buffer_length);
319 if (!NT_STATUS_IS_OK(status)) {
320 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
321 return tevent_req_post(req, ev);
324 switch (in_file_info_class) {
325 case SMB2_FIND_DIRECTORY_INFO:
326 state->info_level = SMB_FIND_FILE_DIRECTORY_INFO;
329 case SMB2_FIND_FULL_DIRECTORY_INFO:
330 state->info_level = SMB_FIND_FILE_FULL_DIRECTORY_INFO;
333 case SMB2_FIND_BOTH_DIRECTORY_INFO:
334 state->info_level = SMB_FIND_FILE_BOTH_DIRECTORY_INFO;
337 case SMB2_FIND_NAME_INFO:
338 state->info_level = SMB_FIND_FILE_NAMES_INFO;
341 case SMB2_FIND_ID_BOTH_DIRECTORY_INFO:
342 state->info_level = SMB_FIND_ID_BOTH_DIRECTORY_INFO;
345 case SMB2_FIND_ID_FULL_DIRECTORY_INFO:
346 state->info_level = SMB_FIND_ID_FULL_DIRECTORY_INFO;
350 tevent_req_nterror(req, NT_STATUS_INVALID_INFO_CLASS);
351 return tevent_req_post(req, ev);
354 if (in_flags & SMB2_CONTINUE_FLAG_REOPEN) {
357 status = fd_close(fsp);
358 if (tevent_req_nterror(req, status)) {
359 return tevent_req_post(req, ev);
363 * fd_close() will close and invalidate the fsp's file
364 * descriptor. So we have to reopen it.
369 flags |= O_DIRECTORY;
371 status = fd_open(conn, fsp, flags, 0);
372 if (tevent_req_nterror(req, status)) {
373 return tevent_req_post(req, ev);
377 if (!state->smbreq->posix_pathnames) {
378 wcard_has_wild = ms_has_wild(state->in_file_name);
381 /* Ensure we've canonicalized any search path if not a wildcard. */
382 if (!wcard_has_wild) {
383 struct smb_filename *smb_fname = NULL;
384 const char *fullpath;
385 char tmpbuf[PATH_MAX];
386 char *to_free = NULL;
387 uint32_t ucf_flags = UCF_SAVE_LCOMP |
388 UCF_ALWAYS_ALLOW_WCARD_LCOMP |
389 (state->smbreq->posix_pathnames ?
390 UCF_POSIX_PATHNAMES : 0);
392 if (ISDOT(fsp->fsp_name->base_name)) {
393 fullpath = state->in_file_name;
399 fsp->fsp_name->base_name, state->in_file_name,
400 tmpbuf, sizeof(tmpbuf), &tmp, &to_free);
403 return tevent_req_post(req, ev);
407 status = filename_convert(state,
414 TALLOC_FREE(to_free);
416 if (tevent_req_nterror(req, status)) {
417 return tevent_req_post(req, ev);
420 state->in_file_name = smb_fname->original_lcomp;
423 if (fsp->dptr == NULL) {
424 status = dptr_create(conn,
428 false, /* old_handle */
429 false, /* expect_close */
431 state->in_file_name, /* wcard */
435 if (!NT_STATUS_IS_OK(status)) {
436 tevent_req_nterror(req, status);
437 return tevent_req_post(req, ev);
440 state->empty_status = NT_STATUS_NO_SUCH_FILE;
442 state->empty_status = STATUS_NO_MORE_FILES;
445 if (in_flags & SMB2_CONTINUE_FLAG_RESTART) {
446 dptr_SeekDir(fsp->dptr, 0);
449 if (in_flags & SMB2_CONTINUE_FLAG_SINGLE) {
450 state->max_count = 1;
452 state->max_count = UINT16_MAX;
455 #define DIR_ENTRY_SAFETY_MARGIN 4096
457 state->out_output_buffer = data_blob_talloc(state, NULL,
458 in_output_buffer_length + DIR_ENTRY_SAFETY_MARGIN);
459 if (tevent_req_nomem(state->out_output_buffer.data, req)) {
460 return tevent_req_post(req, ev);
463 state->out_output_buffer.length = 0;
464 state->pdata = (char *)state->out_output_buffer.data;
465 state->base_data = state->pdata;
467 * end_data must include the safety margin as it's what is
468 * used to determine if pushed strings have been truncated.
470 state->end_data = state->pdata + in_output_buffer_length + DIR_ENTRY_SAFETY_MARGIN - 1;
472 DEBUG(8,("smbd_smb2_query_directory_send: dirpath=<%s> dontdescend=<%s>, "
473 "in_output_buffer_length = %u\n",
474 fsp->fsp_name->base_name, lp_dont_descend(talloc_tos(), SNUM(conn)),
475 (unsigned int)in_output_buffer_length ));
476 if (in_list(fsp->fsp_name->base_name,lp_dont_descend(talloc_tos(), SNUM(conn)),
477 conn->case_sensitive)) {
478 state->dont_descend = true;
482 * SMB_FIND_FILE_NAMES_INFO doesn't need stat information
484 * This may change when we try to improve the delete on close
485 * handling in future.
487 if (state->info_level != SMB_FIND_FILE_NAMES_INFO) {
488 state->ask_sharemode = lp_parm_bool(
489 SNUM(conn), "smbd", "search ask sharemode", true);
492 if (state->ask_sharemode && lp_clustering()) {
493 state->ask_sharemode = false;
494 state->async_ask_sharemode = true;
497 * Should we only set async_internal
498 * if we're not the last request in
501 smb2_request_set_async_internal(smb2req, true);
505 * This gets set in autobuild for some tests
507 state->find_async_delay_usec = lp_parm_ulong(SNUM(conn), "smbd",
508 "find async delay usec",
512 stop = smb2_query_directory_next_entry(req);
515 if (!tevent_req_is_in_progress(req)) {
516 return tevent_req_post(req, ev);
522 static bool smb2_query_directory_next_entry(struct tevent_req *req)
524 struct smbd_smb2_query_directory_state *state = tevent_req_data(
525 req, struct smbd_smb2_query_directory_state);
526 bool got_exact_match = false;
527 int off = state->out_output_buffer.length;
528 int space_remaining = state->in_output_buffer_length - off;
529 struct file_id file_id;
533 SMB_ASSERT(space_remaining >= 0);
535 status = smbd_dirptr_lanman2_entry(state,
538 state->smbreq->flags2,
542 false, /* requires_resume_key */
544 state->ask_sharemode,
546 8, /* align to 8 bytes */
547 false, /* no padding */
553 &state->last_entry_off,
557 off = (int)PTR_DIFF(state->pdata, state->base_data);
559 if (!NT_STATUS_IS_OK(status)) {
560 if (NT_STATUS_EQUAL(status, NT_STATUS_ILLEGAL_CHARACTER)) {
562 * Bad character conversion on name. Ignore this
566 } else if (state->num > 0) {
567 goto last_entry_done;
568 } else if (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
569 tevent_req_nterror(req, NT_STATUS_INFO_LENGTH_MISMATCH);
572 tevent_req_nterror(req, state->empty_status);
577 if (state->async_ask_sharemode) {
578 struct tevent_req *subreq = NULL;
579 char *buf = state->base_data + state->last_entry_off;
581 subreq = fetch_write_time_send(state,
588 if (tevent_req_nomem(subreq, req)) {
591 tevent_req_set_callback(
593 smb2_query_directory_fetch_write_time_done,
595 state->async_sharemode_count++;
599 state->out_output_buffer.length = off;
601 if (!state->done && state->num < state->max_count) {
606 SIVAL(state->out_output_buffer.data, state->last_entry_off, 0);
610 if (state->async_sharemode_count > 0) {
611 DBG_DEBUG("Stopping after %"PRIu64" async mtime "
612 "updates\n", state->async_sharemode_count);
616 if (state->find_async_delay_usec > 0) {
618 struct tevent_req *subreq = NULL;
621 * Should we only set async_internal
622 * if we're not the last request in
625 smb2_request_set_async_internal(state->smb2req, true);
627 tv = timeval_current_ofs(0, state->find_async_delay_usec);
629 subreq = tevent_wakeup_send(state, state->ev, tv);
630 if (tevent_req_nomem(subreq, req)) {
633 tevent_req_set_callback(subreq,
634 smb2_query_directory_waited,
639 tevent_req_done(req);
643 static void smb2_query_directory_check_next_entry(struct tevent_req *req);
645 static void smb2_query_directory_fetch_write_time_done(struct tevent_req *subreq)
647 struct tevent_req *req = tevent_req_callback_data(
648 subreq, struct tevent_req);
649 struct smbd_smb2_query_directory_state *state = tevent_req_data(
650 req, struct smbd_smb2_query_directory_state);
653 state->async_sharemode_count--;
655 status = fetch_write_time_recv(subreq);
657 if (tevent_req_nterror(req, status)) {
661 smb2_query_directory_check_next_entry(req);
665 static void smb2_query_directory_check_next_entry(struct tevent_req *req)
667 struct smbd_smb2_query_directory_state *state = tevent_req_data(
668 req, struct smbd_smb2_query_directory_state);
673 stop = smb2_query_directory_next_entry(req);
678 if (state->async_sharemode_count > 0) {
682 if (state->find_async_delay_usec > 0) {
684 struct tevent_req *subreq = NULL;
686 tv = timeval_current_ofs(0, state->find_async_delay_usec);
688 subreq = tevent_wakeup_send(state, state->ev, tv);
689 if (tevent_req_nomem(subreq, req)) {
690 tevent_req_post(req, state->ev);
693 tevent_req_set_callback(subreq,
694 smb2_query_directory_waited,
699 tevent_req_done(req);
703 static void smb2_query_directory_waited(struct tevent_req *subreq)
705 struct tevent_req *req = tevent_req_callback_data(
706 subreq, struct tevent_req);
709 ok = tevent_wakeup_recv(subreq);
715 tevent_req_done(req);
718 static NTSTATUS smbd_smb2_query_directory_recv(struct tevent_req *req,
720 DATA_BLOB *out_output_buffer)
723 struct smbd_smb2_query_directory_state *state = tevent_req_data(req,
724 struct smbd_smb2_query_directory_state);
726 if (tevent_req_is_nterror(req, &status)) {
727 tevent_req_received(req);
731 *out_output_buffer = state->out_output_buffer;
732 talloc_steal(mem_ctx, out_output_buffer->data);
734 tevent_req_received(req);
738 struct fetch_write_time_state {
739 connection_struct *conn;
742 char *entry_marshall_buf;
745 static void fetch_write_time_done(struct tevent_req *subreq);
747 static struct tevent_req *fetch_write_time_send(TALLOC_CTX *mem_ctx,
748 struct tevent_context *ev,
749 connection_struct *conn,
752 char *entry_marshall_buf,
755 struct tevent_req *req = NULL;
756 struct fetch_write_time_state *state = NULL;
757 struct tevent_req *subreq = NULL;
762 req = tevent_req_create(mem_ctx, &state, struct fetch_write_time_state);
767 *state = (struct fetch_write_time_state) {
770 .info_level = info_level,
771 .entry_marshall_buf = entry_marshall_buf,
774 subreq = fetch_share_mode_send(state, ev, id, &req_queued);
775 if (tevent_req_nomem(subreq, req)) {
776 return tevent_req_post(req, ev);
778 tevent_req_set_callback(subreq, fetch_write_time_done, req);
786 static void fetch_write_time_done(struct tevent_req *subreq)
788 struct tevent_req *req = tevent_req_callback_data(
789 subreq, struct tevent_req);
790 struct fetch_write_time_state *state = tevent_req_data(
791 req, struct fetch_write_time_state);
792 struct timespec write_time;
793 struct share_mode_lock *lck = NULL;
797 status = fetch_share_mode_recv(subreq, state, &lck);
799 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
800 tevent_req_done(req);
803 if (!NT_STATUS_IS_OK(status)) {
804 tevent_req_nterror(req, status);
808 write_time = get_share_mode_write_time(lck);
811 if (null_timespec(write_time)) {
812 tevent_req_done(req);
816 switch (state->info_level) {
817 case SMB_FIND_FILE_DIRECTORY_INFO:
818 case SMB_FIND_FILE_FULL_DIRECTORY_INFO:
819 case SMB_FIND_FILE_BOTH_DIRECTORY_INFO:
820 case SMB_FIND_ID_FULL_DIRECTORY_INFO:
821 case SMB_FIND_ID_BOTH_DIRECTORY_INFO:
826 DBG_ERR("Unsupported info_level [%d]\n", state->info_level);
827 tevent_req_nterror(req, NT_STATUS_INVALID_LEVEL);
831 put_long_date_timespec(state->conn->ts_res,
832 state->entry_marshall_buf + off,
835 tevent_req_done(req);
839 static NTSTATUS fetch_write_time_recv(struct tevent_req *req)
843 if (tevent_req_is_nterror(req, &status)) {
844 tevent_req_received(req);
848 tevent_req_received(req);