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"
28 #include "lib/pthreadpool/pthreadpool_tevent.h"
31 #define DBGC_CLASS DBGC_SMB2
33 static struct tevent_req *smbd_smb2_query_directory_send(TALLOC_CTX *mem_ctx,
34 struct tevent_context *ev,
35 struct smbd_smb2_request *smb2req,
36 struct files_struct *in_fsp,
37 uint8_t in_file_info_class,
39 uint32_t in_file_index,
40 uint32_t in_output_buffer_length,
41 const char *in_file_name);
42 static NTSTATUS smbd_smb2_query_directory_recv(struct tevent_req *req,
44 DATA_BLOB *out_output_buffer);
46 static void smbd_smb2_request_find_done(struct tevent_req *subreq);
47 NTSTATUS smbd_smb2_request_process_query_directory(struct smbd_smb2_request *req)
50 const uint8_t *inbody;
51 uint8_t in_file_info_class;
53 uint32_t in_file_index;
54 uint64_t in_file_id_persistent;
55 uint64_t in_file_id_volatile;
56 struct files_struct *in_fsp;
57 uint16_t in_file_name_offset;
58 uint16_t in_file_name_length;
59 DATA_BLOB in_file_name_buffer;
60 char *in_file_name_string;
61 size_t in_file_name_string_size;
62 uint32_t in_output_buffer_length;
63 struct tevent_req *subreq;
66 status = smbd_smb2_request_verify_sizes(req, 0x21);
67 if (!NT_STATUS_IS_OK(status)) {
68 return smbd_smb2_request_error(req, status);
70 inbody = SMBD_SMB2_IN_BODY_PTR(req);
72 in_file_info_class = CVAL(inbody, 0x02);
73 in_flags = CVAL(inbody, 0x03);
74 in_file_index = IVAL(inbody, 0x04);
75 in_file_id_persistent = BVAL(inbody, 0x08);
76 in_file_id_volatile = BVAL(inbody, 0x10);
77 in_file_name_offset = SVAL(inbody, 0x18);
78 in_file_name_length = SVAL(inbody, 0x1A);
79 in_output_buffer_length = IVAL(inbody, 0x1C);
81 if (in_file_name_offset == 0 && in_file_name_length == 0) {
83 } else if (in_file_name_offset !=
84 (SMB2_HDR_BODY + SMBD_SMB2_IN_BODY_LEN(req))) {
85 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
88 if (in_file_name_length > SMBD_SMB2_IN_DYN_LEN(req)) {
89 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
92 /* The output header is 8 bytes. */
93 if (in_output_buffer_length <= 8) {
94 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
97 DEBUG(10,("smbd_smb2_request_find_done: in_output_buffer_length = %u\n",
98 (unsigned int)in_output_buffer_length ));
100 /* Take into account the output header. */
101 in_output_buffer_length -= 8;
103 in_file_name_buffer.data = SMBD_SMB2_IN_DYN_PTR(req);
104 in_file_name_buffer.length = in_file_name_length;
106 ok = convert_string_talloc(req, CH_UTF16, CH_UNIX,
107 in_file_name_buffer.data,
108 in_file_name_buffer.length,
109 &in_file_name_string,
110 &in_file_name_string_size);
112 return smbd_smb2_request_error(req, NT_STATUS_ILLEGAL_CHARACTER);
115 if (in_file_name_buffer.length == 0) {
116 in_file_name_string_size = 0;
119 if (strlen(in_file_name_string) != in_file_name_string_size) {
120 return smbd_smb2_request_error(req, NT_STATUS_OBJECT_NAME_INVALID);
123 in_fsp = file_fsp_smb2(req, in_file_id_persistent, in_file_id_volatile);
124 if (in_fsp == NULL) {
125 return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED);
128 subreq = smbd_smb2_query_directory_send(req, req->sconn->ev_ctx,
133 in_output_buffer_length,
134 in_file_name_string);
135 if (subreq == NULL) {
136 return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
138 tevent_req_set_callback(subreq, smbd_smb2_request_find_done, req);
140 return smbd_smb2_request_pending_queue(req, subreq, 500);
143 static void smbd_smb2_request_find_done(struct tevent_req *subreq)
145 struct smbd_smb2_request *req = tevent_req_callback_data(subreq,
146 struct smbd_smb2_request);
149 uint16_t out_output_buffer_offset;
150 DATA_BLOB out_output_buffer = data_blob_null;
152 NTSTATUS error; /* transport error */
154 status = smbd_smb2_query_directory_recv(subreq,
158 if (!NT_STATUS_IS_OK(status)) {
159 error = smbd_smb2_request_error(req, status);
160 if (!NT_STATUS_IS_OK(error)) {
161 smbd_server_connection_terminate(req->xconn,
168 out_output_buffer_offset = SMB2_HDR_BODY + 0x08;
170 outbody = smbd_smb2_generate_outbody(req, 0x08);
171 if (outbody.data == NULL) {
172 error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
173 if (!NT_STATUS_IS_OK(error)) {
174 smbd_server_connection_terminate(req->xconn,
181 SSVAL(outbody.data, 0x00, 0x08 + 1); /* struct size */
182 SSVAL(outbody.data, 0x02,
183 out_output_buffer_offset); /* output buffer offset */
184 SIVAL(outbody.data, 0x04,
185 out_output_buffer.length); /* output buffer length */
187 DEBUG(10,("smbd_smb2_request_find_done: out_output_buffer.length = %u\n",
188 (unsigned int)out_output_buffer.length ));
190 outdyn = out_output_buffer;
192 error = smbd_smb2_request_done(req, outbody, &outdyn);
193 if (!NT_STATUS_IS_OK(error)) {
194 smbd_server_connection_terminate(req->xconn,
200 static struct tevent_req *fetch_write_time_send(TALLOC_CTX *mem_ctx,
201 struct tevent_context *ev,
202 connection_struct *conn,
205 char *entry_marshall_buf,
207 static NTSTATUS fetch_write_time_recv(struct tevent_req *req);
209 static struct tevent_req *fetch_dos_mode_send(
211 struct tevent_context *ev,
212 struct files_struct *dir_fsp,
213 struct smb_filename **smb_fname,
215 uint8_t *entry_marshall_buf);
217 static NTSTATUS fetch_dos_mode_recv(struct tevent_req *req);
219 struct smbd_smb2_query_directory_state {
220 struct tevent_context *ev;
221 struct smbd_smb2_request *smb2req;
222 uint64_t async_sharemode_count;
223 uint32_t find_async_delay_usec;
224 DATA_BLOB out_output_buffer;
225 struct smb_request *smbreq;
226 int in_output_buffer_length;
227 struct files_struct *fsp;
228 const char *in_file_name;
229 NTSTATUS empty_status;
240 bool async_ask_sharemode;
242 size_t max_async_dosmode_active;
243 uint32_t async_dosmode_active;
247 static bool smb2_query_directory_next_entry(struct tevent_req *req);
248 static void smb2_query_directory_fetch_write_time_done(struct tevent_req *subreq);
249 static void smb2_query_directory_dos_mode_done(struct tevent_req *subreq);
250 static void smb2_query_directory_waited(struct tevent_req *subreq);
252 static struct tevent_req *smbd_smb2_query_directory_send(TALLOC_CTX *mem_ctx,
253 struct tevent_context *ev,
254 struct smbd_smb2_request *smb2req,
255 struct files_struct *fsp,
256 uint8_t in_file_info_class,
258 uint32_t in_file_index,
259 uint32_t in_output_buffer_length,
260 const char *in_file_name)
262 struct smbXsrv_connection *xconn = smb2req->xconn;
263 struct tevent_req *req;
264 struct smbd_smb2_query_directory_state *state;
265 connection_struct *conn = smb2req->tcon->compat;
266 const struct loadparm_substitution *lp_sub =
267 loadparm_s3_global_substitution();
269 bool wcard_has_wild = false;
275 req = tevent_req_create(mem_ctx, &state,
276 struct smbd_smb2_query_directory_state);
282 state->smb2req = smb2req;
283 state->in_output_buffer_length = in_output_buffer_length;
284 state->in_file_name = in_file_name;
285 state->out_output_buffer = data_blob_null;
286 state->dirtype = FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_DIRECTORY;
288 DEBUG(10,("smbd_smb2_query_directory_send: %s - %s\n",
289 fsp_str_dbg(fsp), fsp_fnum_dbg(fsp)));
291 state->smbreq = smbd_smb2_fake_smb_request(smb2req);
292 if (tevent_req_nomem(state->smbreq, req)) {
293 return tevent_req_post(req, ev);
296 if (!fsp->fsp_flags.is_directory) {
297 tevent_req_nterror(req, NT_STATUS_NOT_SUPPORTED);
298 return tevent_req_post(req, ev);
301 if (strcmp(state->in_file_name, "") == 0) {
302 tevent_req_nterror(req, NT_STATUS_OBJECT_NAME_INVALID);
303 return tevent_req_post(req, ev);
305 if (strchr_m(state->in_file_name, '\\') != NULL) {
306 tevent_req_nterror(req, NT_STATUS_OBJECT_NAME_INVALID);
307 return tevent_req_post(req, ev);
309 if (strchr_m(state->in_file_name, '/') != NULL) {
310 tevent_req_nterror(req, NT_STATUS_OBJECT_NAME_INVALID);
311 return tevent_req_post(req, ev);
314 p = strptime(state->in_file_name, GMT_FORMAT, &tm);
315 if ((p != NULL) && (*p =='\0')) {
317 * Bogus find that asks for a shadow copy timestamp as a
318 * directory. The correct response is that it does not exist as
321 tevent_req_nterror(req, NT_STATUS_NO_SUCH_FILE);
322 return tevent_req_post(req, ev);
325 if (in_output_buffer_length > xconn->smb2.server.max_trans) {
326 DEBUG(2,("smbd_smb2_query_directory_send: "
327 "client ignored max trans:%s: 0x%08X: 0x%08X\n",
328 __location__, in_output_buffer_length,
329 xconn->smb2.server.max_trans));
330 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
331 return tevent_req_post(req, ev);
334 status = smbd_smb2_request_verify_creditcharge(smb2req,
335 in_output_buffer_length);
337 if (!NT_STATUS_IS_OK(status)) {
338 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
339 return tevent_req_post(req, ev);
342 switch (in_file_info_class) {
343 case SMB2_FIND_DIRECTORY_INFO:
344 state->info_level = SMB_FIND_FILE_DIRECTORY_INFO;
347 case SMB2_FIND_FULL_DIRECTORY_INFO:
348 state->info_level = SMB_FIND_FILE_FULL_DIRECTORY_INFO;
351 case SMB2_FIND_BOTH_DIRECTORY_INFO:
352 state->info_level = SMB_FIND_FILE_BOTH_DIRECTORY_INFO;
355 case SMB2_FIND_NAME_INFO:
356 state->info_level = SMB_FIND_FILE_NAMES_INFO;
359 case SMB2_FIND_ID_BOTH_DIRECTORY_INFO:
360 state->info_level = SMB_FIND_ID_BOTH_DIRECTORY_INFO;
363 case SMB2_FIND_ID_FULL_DIRECTORY_INFO:
364 state->info_level = SMB_FIND_ID_FULL_DIRECTORY_INFO;
368 tevent_req_nterror(req, NT_STATUS_INVALID_INFO_CLASS);
369 return tevent_req_post(req, ev);
372 if (in_flags & SMB2_CONTINUE_FLAG_REOPEN) {
375 status = fd_close(fsp);
376 if (tevent_req_nterror(req, status)) {
377 return tevent_req_post(req, ev);
381 * fd_close() will close and invalidate the fsp's file
382 * descriptor. So we have to reopen it.
387 flags |= O_DIRECTORY;
389 status = fd_open(fsp, flags, 0);
390 if (tevent_req_nterror(req, status)) {
391 return tevent_req_post(req, ev);
395 if (!state->smbreq->posix_pathnames) {
396 wcard_has_wild = ms_has_wild(state->in_file_name);
399 /* Ensure we've canonicalized any search path if not a wildcard. */
400 if (!wcard_has_wild) {
401 struct smb_filename *smb_fname = NULL;
402 const char *fullpath;
403 char tmpbuf[PATH_MAX];
404 char *to_free = NULL;
405 uint32_t ucf_flags = UCF_ALWAYS_ALLOW_WCARD_LCOMP |
406 (state->smbreq->posix_pathnames ?
407 UCF_POSIX_PATHNAMES : 0);
409 if (ISDOT(fsp->fsp_name->base_name)) {
410 fullpath = state->in_file_name;
416 fsp->fsp_name->base_name, state->in_file_name,
417 tmpbuf, sizeof(tmpbuf), &tmp, &to_free);
420 return tevent_req_post(req, ev);
424 status = filename_convert(state,
432 TALLOC_FREE(to_free);
434 if (tevent_req_nterror(req, status)) {
435 return tevent_req_post(req, ev);
439 * We still need to do the case processing
440 * to save off the client-supplied last component.
441 * At least we know there's no @GMT normalization
442 * or MS-DFS paths to do in a directory mask.
444 state->in_file_name = get_original_lcomp(state,
448 if (state->in_file_name == NULL) {
450 return tevent_req_post(req, ev);
454 if (fsp->dptr == NULL) {
455 status = dptr_create(conn,
458 false, /* old_handle */
459 false, /* expect_close */
461 state->in_file_name, /* wcard */
465 if (!NT_STATUS_IS_OK(status)) {
466 tevent_req_nterror(req, status);
467 return tevent_req_post(req, ev);
470 state->empty_status = NT_STATUS_NO_SUCH_FILE;
472 state->empty_status = STATUS_NO_MORE_FILES;
475 if (in_flags & SMB2_CONTINUE_FLAG_RESTART) {
476 dptr_SeekDir(fsp->dptr, 0);
479 if (in_flags & SMB2_CONTINUE_FLAG_SINGLE) {
480 state->max_count = 1;
482 state->max_count = UINT16_MAX;
485 #define DIR_ENTRY_SAFETY_MARGIN 4096
487 state->out_output_buffer = data_blob_talloc(state, NULL,
488 in_output_buffer_length + DIR_ENTRY_SAFETY_MARGIN);
489 if (tevent_req_nomem(state->out_output_buffer.data, req)) {
490 return tevent_req_post(req, ev);
493 state->out_output_buffer.length = 0;
494 state->pdata = (char *)state->out_output_buffer.data;
495 state->base_data = state->pdata;
497 * end_data must include the safety margin as it's what is
498 * used to determine if pushed strings have been truncated.
500 state->end_data = state->pdata + in_output_buffer_length + DIR_ENTRY_SAFETY_MARGIN - 1;
502 DEBUG(8,("smbd_smb2_query_directory_send: dirpath=<%s> dontdescend=<%s>, "
503 "in_output_buffer_length = %u\n",
504 fsp->fsp_name->base_name, lp_dont_descend(talloc_tos(), lp_sub, SNUM(conn)),
505 (unsigned int)in_output_buffer_length ));
506 if (in_list(fsp->fsp_name->base_name,lp_dont_descend(talloc_tos(), lp_sub, SNUM(conn)),
507 conn->case_sensitive)) {
508 state->dont_descend = true;
512 * SMB_FIND_FILE_NAMES_INFO doesn't need stat information
514 * This may change when we try to improve the delete on close
515 * handling in future.
517 if (state->info_level != SMB_FIND_FILE_NAMES_INFO) {
518 state->ask_sharemode = lp_smbd_search_ask_sharemode(SNUM(conn));
520 state->async_dosmode = lp_smbd_async_dosmode(SNUM(conn));
523 if (state->ask_sharemode && lp_clustering()) {
524 state->ask_sharemode = false;
525 state->async_ask_sharemode = true;
528 if (state->async_dosmode) {
531 max_threads = pthreadpool_tevent_max_threads(conn->sconn->pool);
532 if (max_threads == 0 || !per_thread_cwd_supported()) {
533 state->async_dosmode = false;
536 state->max_async_dosmode_active = lp_smbd_max_async_dosmode(
538 if (state->max_async_dosmode_active == 0) {
539 state->max_async_dosmode_active = max_threads * 2;
543 if (state->async_dosmode || state->async_ask_sharemode) {
545 * Should we only set async_internal
546 * if we're not the last request in
549 smb2_request_set_async_internal(smb2req, true);
553 * This gets set in autobuild for some tests
555 state->find_async_delay_usec = lp_parm_ulong(SNUM(conn), "smbd",
556 "find async delay usec",
560 stop = smb2_query_directory_next_entry(req);
563 if (!tevent_req_is_in_progress(req)) {
564 return tevent_req_post(req, ev);
567 ok = aio_add_req_to_fsp(fsp, req);
569 DBG_ERR("Could not add req to fsp\n");
570 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
571 return tevent_req_post(req, ev);
577 static bool smb2_query_directory_next_entry(struct tevent_req *req)
579 struct smbd_smb2_query_directory_state *state = tevent_req_data(
580 req, struct smbd_smb2_query_directory_state);
581 struct smb_filename *smb_fname = NULL; /* relative to fsp !! */
582 bool got_exact_match = false;
583 int off = state->out_output_buffer.length;
584 int space_remaining = state->in_output_buffer_length - off;
585 struct file_id file_id;
587 bool get_dosmode = !state->async_dosmode;
590 SMB_ASSERT(space_remaining >= 0);
592 status = smbd_dirptr_lanman2_entry(state,
595 state->smbreq->flags2,
599 false, /* requires_resume_key */
601 state->ask_sharemode,
603 8, /* align to 8 bytes */
604 false, /* no padding */
611 &state->last_entry_off,
615 off = (int)PTR_DIFF(state->pdata, state->base_data);
617 if (!NT_STATUS_IS_OK(status)) {
618 if (NT_STATUS_EQUAL(status, NT_STATUS_ILLEGAL_CHARACTER)) {
620 * Bad character conversion on name. Ignore this
624 } else if (state->num > 0) {
625 goto last_entry_done;
626 } else if (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
627 tevent_req_nterror(req, NT_STATUS_INFO_LENGTH_MISMATCH);
630 tevent_req_nterror(req, state->empty_status);
635 if (state->async_ask_sharemode &&
636 !S_ISDIR(smb_fname->st.st_ex_mode))
638 struct tevent_req *subreq = NULL;
639 char *buf = state->base_data + state->last_entry_off;
641 subreq = fetch_write_time_send(state,
648 if (tevent_req_nomem(subreq, req)) {
651 tevent_req_set_callback(
653 smb2_query_directory_fetch_write_time_done,
655 state->async_sharemode_count++;
658 if (state->async_dosmode) {
659 struct tevent_req *subreq = NULL;
661 size_t outstanding_aio;
663 buf = (uint8_t *)state->base_data + state->last_entry_off;
665 subreq = fetch_dos_mode_send(state,
671 if (tevent_req_nomem(subreq, req)) {
674 tevent_req_set_callback(subreq,
675 smb2_query_directory_dos_mode_done,
678 state->async_dosmode_active++;
680 outstanding_aio = pthreadpool_tevent_queued_jobs(
681 state->fsp->conn->sconn->pool);
683 if (outstanding_aio > state->max_async_dosmode_active) {
688 TALLOC_FREE(smb_fname);
691 state->out_output_buffer.length = off;
693 if (!state->done && state->num < state->max_count) {
698 SIVAL(state->out_output_buffer.data, state->last_entry_off, 0);
702 if (state->async_sharemode_count > 0) {
703 DBG_DEBUG("Stopping after %"PRIu64" async mtime "
704 "updates\n", state->async_sharemode_count);
708 if (state->async_dosmode_active > 0) {
712 if (state->find_async_delay_usec > 0) {
714 struct tevent_req *subreq = NULL;
717 * Should we only set async_internal
718 * if we're not the last request in
721 smb2_request_set_async_internal(state->smb2req, true);
723 tv = timeval_current_ofs(0, state->find_async_delay_usec);
725 subreq = tevent_wakeup_send(state, state->ev, tv);
726 if (tevent_req_nomem(subreq, req)) {
729 tevent_req_set_callback(subreq,
730 smb2_query_directory_waited,
735 tevent_req_done(req);
739 static void smb2_query_directory_check_next_entry(struct tevent_req *req);
741 static void smb2_query_directory_fetch_write_time_done(struct tevent_req *subreq)
743 struct tevent_req *req = tevent_req_callback_data(
744 subreq, struct tevent_req);
745 struct smbd_smb2_query_directory_state *state = tevent_req_data(
746 req, struct smbd_smb2_query_directory_state);
751 * Make sure we run as the user again
753 ok = change_to_user_and_service_by_fsp(state->fsp);
756 state->async_sharemode_count--;
758 status = fetch_write_time_recv(subreq);
760 if (tevent_req_nterror(req, status)) {
764 smb2_query_directory_check_next_entry(req);
768 static void smb2_query_directory_dos_mode_done(struct tevent_req *subreq)
770 struct tevent_req *req =
771 tevent_req_callback_data(subreq,
773 struct smbd_smb2_query_directory_state *state =
775 struct smbd_smb2_query_directory_state);
780 * Make sure we run as the user again
782 ok = change_to_user_and_service_by_fsp(state->fsp);
785 status = fetch_dos_mode_recv(subreq);
787 if (tevent_req_nterror(req, status)) {
791 state->async_dosmode_active--;
793 smb2_query_directory_check_next_entry(req);
797 static void smb2_query_directory_check_next_entry(struct tevent_req *req)
799 struct smbd_smb2_query_directory_state *state = tevent_req_data(
800 req, struct smbd_smb2_query_directory_state);
805 stop = smb2_query_directory_next_entry(req);
810 if (state->async_sharemode_count > 0 ||
811 state->async_dosmode_active > 0)
816 if (state->find_async_delay_usec > 0) {
818 struct tevent_req *subreq = NULL;
820 tv = timeval_current_ofs(0, state->find_async_delay_usec);
822 subreq = tevent_wakeup_send(state, state->ev, tv);
823 if (tevent_req_nomem(subreq, req)) {
824 tevent_req_post(req, state->ev);
827 tevent_req_set_callback(subreq,
828 smb2_query_directory_waited,
833 tevent_req_done(req);
837 static void smb2_query_directory_waited(struct tevent_req *subreq)
839 struct tevent_req *req = tevent_req_callback_data(
840 subreq, struct tevent_req);
843 ok = tevent_wakeup_recv(subreq);
849 tevent_req_done(req);
852 static NTSTATUS smbd_smb2_query_directory_recv(struct tevent_req *req,
854 DATA_BLOB *out_output_buffer)
857 struct smbd_smb2_query_directory_state *state = tevent_req_data(req,
858 struct smbd_smb2_query_directory_state);
860 if (tevent_req_is_nterror(req, &status)) {
861 tevent_req_received(req);
865 *out_output_buffer = state->out_output_buffer;
866 talloc_steal(mem_ctx, out_output_buffer->data);
868 tevent_req_received(req);
872 struct fetch_write_time_state {
873 connection_struct *conn;
876 char *entry_marshall_buf;
879 static void fetch_write_time_done(struct tevent_req *subreq);
881 static struct tevent_req *fetch_write_time_send(TALLOC_CTX *mem_ctx,
882 struct tevent_context *ev,
883 connection_struct *conn,
886 char *entry_marshall_buf,
889 struct tevent_req *req = NULL;
890 struct fetch_write_time_state *state = NULL;
891 struct tevent_req *subreq = NULL;
896 req = tevent_req_create(mem_ctx, &state, struct fetch_write_time_state);
901 *state = (struct fetch_write_time_state) {
904 .info_level = info_level,
905 .entry_marshall_buf = entry_marshall_buf,
908 subreq = fetch_share_mode_send(state, ev, id, &req_queued);
909 if (tevent_req_nomem(subreq, req)) {
910 return tevent_req_post(req, ev);
912 tevent_req_set_callback(subreq, fetch_write_time_done, req);
920 static void fetch_write_time_done(struct tevent_req *subreq)
922 struct tevent_req *req = tevent_req_callback_data(
923 subreq, struct tevent_req);
924 struct fetch_write_time_state *state = tevent_req_data(
925 req, struct fetch_write_time_state);
926 struct timespec write_time;
927 struct share_mode_lock *lck = NULL;
931 status = fetch_share_mode_recv(subreq, state, &lck);
933 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
934 tevent_req_done(req);
937 if (!NT_STATUS_IS_OK(status)) {
938 tevent_req_nterror(req, status);
942 write_time = get_share_mode_write_time(lck);
945 if (is_omit_timespec(&write_time)) {
946 tevent_req_done(req);
950 switch (state->info_level) {
951 case SMB_FIND_FILE_DIRECTORY_INFO:
952 case SMB_FIND_FILE_FULL_DIRECTORY_INFO:
953 case SMB_FIND_FILE_BOTH_DIRECTORY_INFO:
954 case SMB_FIND_ID_FULL_DIRECTORY_INFO:
955 case SMB_FIND_ID_BOTH_DIRECTORY_INFO:
960 DBG_ERR("Unsupported info_level [%d]\n", state->info_level);
961 tevent_req_nterror(req, NT_STATUS_INVALID_LEVEL);
965 put_long_date_full_timespec(state->conn->ts_res,
966 state->entry_marshall_buf + off,
969 tevent_req_done(req);
973 static NTSTATUS fetch_write_time_recv(struct tevent_req *req)
977 if (tevent_req_is_nterror(req, &status)) {
978 tevent_req_received(req);
982 tevent_req_received(req);
986 struct fetch_dos_mode_state {
987 struct files_struct *dir_fsp;
988 struct smb_filename *smb_fname;
990 uint8_t *entry_marshall_buf;
993 static void fetch_dos_mode_done(struct tevent_req *subreq);
995 static struct tevent_req *fetch_dos_mode_send(
997 struct tevent_context *ev,
998 struct files_struct *dir_fsp,
999 struct smb_filename **smb_fname,
1000 uint32_t info_level,
1001 uint8_t *entry_marshall_buf)
1003 struct tevent_req *req = NULL;
1004 struct fetch_dos_mode_state *state = NULL;
1005 struct tevent_req *subreq = NULL;
1007 req = tevent_req_create(mem_ctx, &state, struct fetch_dos_mode_state);
1011 *state = (struct fetch_dos_mode_state) {
1013 .info_level = info_level,
1014 .entry_marshall_buf = entry_marshall_buf,
1017 state->smb_fname = talloc_move(state, smb_fname);
1019 subreq = dos_mode_at_send(state, ev, dir_fsp, state->smb_fname);
1020 if (tevent_req_nomem(subreq, req)) {
1021 return tevent_req_post(req, ev);
1023 tevent_req_set_callback(subreq, fetch_dos_mode_done, req);
1028 static void fetch_dos_mode_done(struct tevent_req *subreq)
1030 struct tevent_req *req =
1031 tevent_req_callback_data(subreq,
1033 struct fetch_dos_mode_state *state =
1034 tevent_req_data(req,
1035 struct fetch_dos_mode_state);
1036 uint32_t dfs_dosmode;
1038 struct timespec btime_ts = {0};
1039 bool need_file_id = false;
1046 status = dos_mode_at_recv(subreq, &dosmode);
1047 TALLOC_FREE(subreq);
1048 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1049 tevent_req_done(req);
1052 if (!NT_STATUS_IS_OK(status)) {
1053 tevent_req_nterror(req, status);
1057 switch (state->info_level) {
1058 case SMB_FIND_ID_BOTH_DIRECTORY_INFO:
1059 case SMB_FIND_FILE_BOTH_DIRECTORY_INFO:
1060 case SMB_FIND_FILE_DIRECTORY_INFO:
1061 case SMB_FIND_FILE_FULL_DIRECTORY_INFO:
1062 case SMB_FIND_ID_FULL_DIRECTORY_INFO:
1068 DBG_ERR("Unsupported info_level [%u]\n", state->info_level);
1069 tevent_req_nterror(req, NT_STATUS_INVALID_LEVEL);
1074 dfs_dosmode = IVAL(state->entry_marshall_buf, dosmode_off);
1075 if (dfs_dosmode == 0) {
1077 * DOS mode for a DFS link, only overwrite if still set to 0 and
1078 * not already populated by the lower layer for a DFS link in
1079 * smbd_dirptr_lanman2_mode_fn().
1081 SIVAL(state->entry_marshall_buf, dosmode_off, dosmode);
1084 btime_ts = get_create_timespec(state->dir_fsp->conn,
1087 if (lp_dos_filetime_resolution(SNUM(state->dir_fsp->conn))) {
1088 dos_filetime_timespec(&btime_ts);
1091 put_long_date_full_timespec(state->dir_fsp->conn->ts_res,
1092 (char *)state->entry_marshall_buf + btime_off,
1095 switch (state->info_level) {
1096 case SMB_FIND_ID_BOTH_DIRECTORY_INFO:
1098 need_file_id = true;
1100 case SMB_FIND_ID_FULL_DIRECTORY_INFO:
1102 need_file_id = true;
1110 * File-ID might have been updated from calculated (based on
1111 * inode) to storage based, fetch via DOS attributes in
1114 file_id = SMB_VFS_FS_FILE_ID(state->dir_fsp->conn,
1115 &state->smb_fname->st);
1116 SBVAL(state->entry_marshall_buf, file_id_off, file_id);
1119 tevent_req_done(req);
1123 static NTSTATUS fetch_dos_mode_recv(struct tevent_req *req)
1127 if (tevent_req_is_nterror(req, &status)) {
1128 tevent_req_received(req);
1132 tevent_req_received(req);
1133 return NT_STATUS_OK;