2 Unix SMB/CIFS implementation.
3 client directory list routines
4 Copyright (C) Andrew Tridgell 1994-1998
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include "libsmb/libsmb.h"
22 #include "../lib/util/tevent_ntstatus.h"
23 #include "async_smb.h"
25 #include "../libcli/smb/smbXcli_base.h"
27 /****************************************************************************
28 Check if a returned directory name is safe.
29 ****************************************************************************/
31 static NTSTATUS is_bad_name(bool windows_names, const char *name)
33 const char *bad_name_p = NULL;
35 bad_name_p = strchr(name, '/');
36 if (bad_name_p != NULL) {
38 * Windows and POSIX names can't have '/'.
39 * Server is attacking us.
41 return NT_STATUS_INVALID_NETWORK_RESPONSE;
44 bad_name_p = strchr(name, '\\');
45 if (bad_name_p != NULL) {
47 * Windows names can't have '\\'.
48 * Server is attacking us.
50 return NT_STATUS_INVALID_NETWORK_RESPONSE;
56 /****************************************************************************
57 Check if a returned directory name is safe. Disconnect if server is
59 ****************************************************************************/
61 NTSTATUS is_bad_finfo_name(const struct cli_state *cli,
62 const struct file_info *finfo)
64 NTSTATUS status = NT_STATUS_OK;
65 bool windows_names = true;
67 if (cli->requested_posix_capabilities & CIFS_UNIX_POSIX_PATHNAMES_CAP) {
68 windows_names = false;
70 if (finfo->name != NULL) {
71 status = is_bad_name(windows_names, finfo->name);
72 if (!NT_STATUS_IS_OK(status)) {
73 DBG_ERR("bad finfo->name\n");
77 if (finfo->short_name != NULL) {
78 status = is_bad_name(windows_names, finfo->short_name);
79 if (!NT_STATUS_IS_OK(status)) {
80 DBG_ERR("bad finfo->short_name\n");
87 /****************************************************************************
88 Calculate a safe next_entry_offset.
89 ****************************************************************************/
91 static size_t calc_next_entry_offset(const char *base, const char *pdata_end)
93 size_t next_entry_offset = (size_t)IVAL(base,0);
95 if (next_entry_offset == 0 ||
96 base + next_entry_offset < base ||
97 base + next_entry_offset > pdata_end) {
98 next_entry_offset = pdata_end - base;
100 return next_entry_offset;
103 /****************************************************************************
104 Interpret a long filename structure - this is mostly guesses at the moment.
105 The length of the structure is returned
106 The structure of a long filename depends on the info level.
107 SMB_FIND_FILE_BOTH_DIRECTORY_INFO is used
108 by NT and SMB_FIND_EA_SIZE is used by OS/2
109 ****************************************************************************/
111 static size_t interpret_long_filename(TALLOC_CTX *ctx,
112 struct cli_state *cli,
114 const char *base_ptr,
115 uint16_t recv_flags2,
117 const char *pdata_end,
118 struct file_info *finfo,
119 uint32_t *p_resume_key,
120 DATA_BLOB *p_last_name_raw)
124 const char *base = p;
126 data_blob_free(p_last_name_raw);
134 case SMB_FIND_INFO_STANDARD: /* OS/2 understands this */
135 /* these dates are converted to GMT by
137 if (pdata_end - base < 27) {
138 return pdata_end - base;
141 * What we're returning here as ctime_ts is
142 * actually the server create time.
144 finfo->btime_ts = convert_time_t_to_timespec(
146 smb1cli_conn_server_time_zone(
148 finfo->ctime_ts = convert_time_t_to_timespec(
149 make_unix_date2(p+4, smb1cli_conn_server_time_zone(cli->conn)));
150 finfo->atime_ts = convert_time_t_to_timespec(
151 make_unix_date2(p+8, smb1cli_conn_server_time_zone(cli->conn)));
152 finfo->mtime_ts = convert_time_t_to_timespec(
153 make_unix_date2(p+12, smb1cli_conn_server_time_zone(cli->conn)));
154 finfo->size = IVAL(p,16);
155 finfo->attr = SVAL(p,24);
158 if (recv_flags2 & FLAGS2_UNICODE_STRINGS) {
159 p += ucs2_align(base_ptr, p, STR_UNICODE);
162 /* We can safely use len here (which is required by OS/2)
163 * and the NAS-BASIC server instead of +2 or +1 as the
164 * STR_TERMINATE flag below is
165 * actually used as the length calculation.
166 * The len is merely an upper bound.
167 * Due to the explicit 2 byte null termination
168 * in cli_receive_trans/cli_receive_nt_trans
169 * we know this is safe. JRA + kukks
172 if (p + len > pdata_end) {
173 return pdata_end - base;
176 /* the len+2 below looks strange but it is
177 important to cope with the differences
178 between win2000 and win9x for this call
180 ret = pull_string_talloc(ctx,
187 if (ret == (size_t)-1) {
188 return pdata_end - base;
191 return PTR_DIFF(p, base);
193 case SMB_FIND_EA_SIZE: /* this is what OS/2 uses mostly */
194 /* these dates are converted to GMT by
196 if (pdata_end - base < 31) {
197 return pdata_end - base;
200 * What we're returning here as ctime_ts is
201 * actually the server create time.
203 finfo->btime_ts = convert_time_t_to_timespec(
205 smb1cli_conn_server_time_zone(
207 finfo->ctime_ts = convert_time_t_to_timespec(
208 make_unix_date2(p+4, smb1cli_conn_server_time_zone(cli->conn)));
209 finfo->atime_ts = convert_time_t_to_timespec(
210 make_unix_date2(p+8, smb1cli_conn_server_time_zone(cli->conn)));
211 finfo->mtime_ts = convert_time_t_to_timespec(
212 make_unix_date2(p+12, smb1cli_conn_server_time_zone(cli->conn)));
213 finfo->size = IVAL(p,16);
214 finfo->attr = SVAL(p,24);
217 /* check for unisys! */
218 if (p + len + 1 > pdata_end) {
219 return pdata_end - base;
221 ret = pull_string_talloc(ctx,
228 if (ret == (size_t)-1) {
229 return pdata_end - base;
232 return PTR_DIFF(p, base) + 1;
234 case SMB_FIND_FILE_BOTH_DIRECTORY_INFO: /* NT uses this, but also accepts 2 */
236 size_t namelen, slen;
238 if (pdata_end - base < 94) {
239 return pdata_end - base;
242 p += 4; /* next entry offset */
245 *p_resume_key = IVAL(p,0);
247 p += 4; /* fileindex */
249 /* Offset zero is "create time", not "change time". */
251 finfo->atime_ts = interpret_long_date(p);
253 finfo->mtime_ts = interpret_long_date(p);
255 finfo->ctime_ts = interpret_long_date(p);
257 finfo->size = IVAL2_TO_SMB_BIG_UINT(p,0);
259 p += 8; /* alloc size */
260 finfo->attr = IVAL(p,0);
264 p += 4; /* EA size */
267 /* Bad short name length. */
268 return pdata_end - base;
271 ret = pull_string_talloc(ctx,
278 if (ret == (size_t)-1) {
279 return pdata_end - base;
281 p += 24; /* short name? */
282 if (p + namelen < p || p + namelen > pdata_end) {
283 return pdata_end - base;
285 ret = pull_string_talloc(ctx,
292 if (ret == (size_t)-1) {
293 return pdata_end - base;
296 /* To be robust in the face of unicode conversion failures
297 we need to copy the raw bytes of the last name seen here.
298 Namelen doesn't include the terminating unicode null, so
301 if (p_last_name_raw) {
302 *p_last_name_raw = data_blob(NULL, namelen+2);
303 memcpy(p_last_name_raw->data, p, namelen);
304 SSVAL(p_last_name_raw->data, namelen, 0);
306 return calc_next_entry_offset(base, pdata_end);
310 DEBUG(1,("Unknown long filename format %d\n",level));
311 return calc_next_entry_offset(base, pdata_end);
314 /****************************************************************************
315 Interpret a short filename structure.
316 The length of the structure is returned.
317 ****************************************************************************/
319 static bool interpret_short_filename(TALLOC_CTX *ctx,
320 struct cli_state *cli,
322 struct file_info *finfo)
327 finfo->attr = CVAL(p,21);
329 /* We don't get birth time. */
330 finfo->btime_ts.tv_sec = 0;
331 finfo->btime_ts.tv_nsec = 0;
332 /* this date is converted to GMT by make_unix_date */
333 finfo->ctime_ts.tv_sec = make_unix_date(p+22, smb1cli_conn_server_time_zone(cli->conn));
334 finfo->ctime_ts.tv_nsec = 0;
335 finfo->mtime_ts.tv_sec = finfo->atime_ts.tv_sec = finfo->ctime_ts.tv_sec;
336 finfo->mtime_ts.tv_nsec = finfo->atime_ts.tv_nsec = 0;
337 finfo->size = IVAL(p,26);
338 ret = pull_string_talloc(ctx,
345 if (ret == (size_t)-1) {
350 finfo->short_name = talloc_strdup(ctx, finfo->name);
351 if (finfo->short_name == NULL) {
358 struct cli_list_old_state {
359 struct tevent_context *ev;
360 struct cli_state *cli;
365 uint8_t search_status[23];
371 static void cli_list_old_done(struct tevent_req *subreq);
373 static struct tevent_req *cli_list_old_send(TALLOC_CTX *mem_ctx,
374 struct tevent_context *ev,
375 struct cli_state *cli,
379 struct tevent_req *req, *subreq;
380 struct cli_list_old_state *state;
382 static const uint16_t zero = 0;
383 uint32_t usable_space;
385 req = tevent_req_create(mem_ctx, &state, struct cli_list_old_state);
391 state->attribute = attribute;
393 state->mask = talloc_strdup(state, mask);
394 if (tevent_req_nomem(state->mask, req)) {
395 return tevent_req_post(req, ev);
397 state->mask = smb1_dfs_share_path(state, cli, state->mask);
398 if (tevent_req_nomem(state->mask, req)) {
399 return tevent_req_post(req, ev);
401 usable_space = cli_state_available_size(cli, 100);
402 state->num_asked = usable_space / DIR_STRUCT_SIZE;
404 SSVAL(state->vwv + 0, 0, state->num_asked);
405 SSVAL(state->vwv + 1, 0, state->attribute);
407 bytes = talloc_array(state, uint8_t, 1);
408 if (tevent_req_nomem(bytes, req)) {
409 return tevent_req_post(req, ev);
412 bytes = smb_bytes_push_str(bytes,
413 smbXcli_conn_use_unicode(cli->conn),
415 strlen(state->mask)+1,
418 bytes = smb_bytes_push_bytes(bytes, 5, (const uint8_t *)&zero, 2);
419 if (tevent_req_nomem(bytes, req)) {
420 return tevent_req_post(req, ev);
423 subreq = cli_smb_send(state, state->ev, state->cli, SMBsearch, 0, 0,
424 2, state->vwv, talloc_get_size(bytes), bytes);
425 if (tevent_req_nomem(subreq, req)) {
426 return tevent_req_post(req, ev);
428 tevent_req_set_callback(subreq, cli_list_old_done, req);
432 static void cli_list_old_done(struct tevent_req *subreq)
434 struct tevent_req *req = tevent_req_callback_data(
435 subreq, struct tevent_req);
436 struct cli_list_old_state *state = tevent_req_data(
437 req, struct cli_list_old_state);
448 status = cli_smb_recv(subreq, state, NULL, 0, &wct, &vwv, &num_bytes,
450 if (!NT_STATUS_IS_OK(status)
451 && !NT_STATUS_EQUAL(status, NT_STATUS_DOS(ERRDOS, ERRnofiles))
452 && !NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) {
454 tevent_req_nterror(req, status);
457 if (NT_STATUS_EQUAL(status, NT_STATUS_DOS(ERRDOS, ERRnofiles))
458 || NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) {
464 req, NT_STATUS_INVALID_NETWORK_RESPONSE);
467 received = SVAL(vwv + 0, 0);
472 * I don't think this can wrap. received is
473 * initialized from a 16-bit value.
475 if (num_bytes < ((uint32_t)received * DIR_STRUCT_SIZE + 3)) {
478 req, NT_STATUS_INVALID_NETWORK_RESPONSE);
482 dirlist_len = talloc_get_size(state->dirlist);
484 tmp = talloc_realloc(
485 state, state->dirlist, uint8_t,
486 dirlist_len + received * DIR_STRUCT_SIZE);
487 if (tevent_req_nomem(tmp, req)) {
490 state->dirlist = tmp;
491 memcpy(state->dirlist + dirlist_len, bytes + 3,
492 received * DIR_STRUCT_SIZE);
494 SSVAL(state->search_status, 0, 21);
495 memcpy(state->search_status + 2,
496 bytes + 3 + (received-1)*DIR_STRUCT_SIZE, 21);
499 if (state->first || state->done) {
500 tevent_req_done(req);
504 state->num_asked = 0;
509 state->first = false;
511 SSVAL(state->vwv + 0, 0, state->num_asked);
512 SSVAL(state->vwv + 1, 0, state->attribute);
514 bytes = talloc_array(state, uint8_t, 1);
515 if (tevent_req_nomem(bytes, req)) {
519 bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(state->cli->conn), "",
521 bytes = smb_bytes_push_bytes(bytes, 5, state->search_status,
522 sizeof(state->search_status));
523 if (tevent_req_nomem(bytes, req)) {
526 subreq = cli_smb_send(state, state->ev, state->cli, cmd, 0, 0,
527 2, state->vwv, talloc_get_size(bytes), bytes);
528 if (tevent_req_nomem(subreq, req)) {
531 tevent_req_set_callback(subreq, cli_list_old_done, req);
534 static NTSTATUS cli_list_old_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
535 struct file_info **pfinfo)
537 struct cli_list_old_state *state = tevent_req_data(
538 req, struct cli_list_old_state);
540 size_t i, num_received;
541 struct file_info *finfo;
543 if (tevent_req_is_nterror(req, &status)) {
547 num_received = talloc_array_length(state->dirlist) / DIR_STRUCT_SIZE;
549 finfo = talloc_array(mem_ctx, struct file_info, num_received);
551 return NT_STATUS_NO_MEMORY;
554 for (i=0; i<num_received; i++) {
555 if (!interpret_short_filename(
557 (char *)state->dirlist + i * DIR_STRUCT_SIZE,
560 return NT_STATUS_NO_MEMORY;
562 if (finfo->name == NULL) {
564 return NT_STATUS_INVALID_NETWORK_RESPONSE;
566 status = is_bad_finfo_name(state->cli, finfo);
567 if (!NT_STATUS_IS_OK(status)) {
568 smbXcli_conn_disconnect(state->cli->conn, status);
577 NTSTATUS cli_list_old(struct cli_state *cli, const char *mask,
579 NTSTATUS (*fn)(struct file_info *,
580 const char *, void *), void *state)
582 TALLOC_CTX *frame = talloc_stackframe();
583 struct tevent_context *ev;
584 struct tevent_req *req;
585 NTSTATUS status = NT_STATUS_NO_MEMORY;
586 struct file_info *finfo = NULL;
589 if (smbXcli_conn_has_async_calls(cli->conn)) {
591 * Can't use sync call while an async call is in flight
593 status = NT_STATUS_INVALID_PARAMETER;
596 ev = samba_tevent_context_init(frame);
600 req = cli_list_old_send(frame, ev, cli, mask, attribute);
604 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
607 status = cli_list_old_recv(req, frame, &finfo);
608 if (!NT_STATUS_IS_OK(status)) {
611 num_finfo = talloc_array_length(finfo);
612 for (i=0; i<num_finfo; i++) {
613 status = fn(&finfo[i], mask, state);
614 if (!NT_STATUS_IS_OK(status)) {
623 struct cli_list_trans_state {
624 struct tevent_context *ev;
625 struct cli_state *cli;
632 uint16_t max_matches;
641 struct file_info *finfo;
644 static void cli_list_trans_done(struct tevent_req *subreq);
646 static struct tevent_req *cli_list_trans_send(TALLOC_CTX *mem_ctx,
647 struct tevent_context *ev,
648 struct cli_state *cli,
653 struct tevent_req *req, *subreq;
654 struct cli_list_trans_state *state;
656 uint16_t additional_flags2 = 0;
658 req = tevent_req_create(mem_ctx, &state,
659 struct cli_list_trans_state);
665 state->mask = talloc_strdup(state, mask);
666 if (tevent_req_nomem(state->mask, req)) {
667 return tevent_req_post(req, ev);
669 state->mask = smb1_dfs_share_path(state, cli, state->mask);
670 if (tevent_req_nomem(state->mask, req)) {
671 return tevent_req_post(req, ev);
673 state->attribute = attribute;
674 state->info_level = info_level;
675 state->loop_count = 0;
678 state->max_matches = 1366; /* Match W2k */
680 SSVAL(&state->setup[0], 0, TRANSACT2_FINDFIRST);
682 state->param = talloc_array(state, uint8_t, 12);
683 if (tevent_req_nomem(state->param, req)) {
684 return tevent_req_post(req, ev);
687 SSVAL(state->param, 0, state->attribute);
688 SSVAL(state->param, 2, state->max_matches);
689 SSVAL(state->param, 4,
690 FLAG_TRANS2_FIND_REQUIRE_RESUME
691 |FLAG_TRANS2_FIND_CLOSE_IF_END
692 |(cli->backup_intent ? FLAG_TRANS2_FIND_BACKUP_INTENT : 0));
693 SSVAL(state->param, 6, state->info_level);
694 SIVAL(state->param, 8, 0);
696 state->param = trans2_bytes_push_str(state->param, smbXcli_conn_use_unicode(cli->conn),
697 state->mask, strlen(state->mask)+1,
699 if (tevent_req_nomem(state->param, req)) {
700 return tevent_req_post(req, ev);
703 if (clistr_is_previous_version_path(state->mask)) {
704 additional_flags2 = FLAGS2_REPARSE_PATH;
707 param_len = talloc_get_size(state->param);
709 subreq = cli_trans_send(state, state->ev, state->cli, additional_flags2,
710 SMBtrans2, NULL, -1, 0, 0,
712 state->param, param_len, 10,
713 NULL, 0, CLI_BUFFER_SIZE);
714 if (tevent_req_nomem(subreq, req)) {
715 return tevent_req_post(req, ev);
717 tevent_req_set_callback(subreq, cli_list_trans_done, req);
721 static void cli_list_trans_done(struct tevent_req *subreq)
723 struct tevent_req *req = tevent_req_callback_data(
724 subreq, struct tevent_req);
725 struct cli_list_trans_state *state = tevent_req_data(
726 req, struct cli_list_trans_state);
734 struct file_info *tmp;
735 size_t old_num_finfo;
736 uint16_t recv_flags2;
740 uint32_t resume_key = 0;
742 DATA_BLOB last_name_raw;
743 struct file_info *finfo = NULL;
745 uint16_t additional_flags2 = 0;
747 min_param = (state->first ? 6 : 4);
749 status = cli_trans_recv(subreq, talloc_tos(), &recv_flags2,
751 ¶m, min_param, &num_param,
752 &data, 0, &num_data);
754 if (!NT_STATUS_IS_OK(status)) {
756 * TODO: retry, OS/2 nofiles
758 tevent_req_nterror(req, status);
763 state->ff_dir_handle = SVAL(param, 0);
764 ff_searchcount = SVAL(param, 2);
765 ff_eos = SVAL(param, 4) != 0;
767 ff_searchcount = SVAL(param, 0);
768 ff_eos = SVAL(param, 2) != 0;
771 old_num_finfo = talloc_array_length(state->finfo);
773 tmp = talloc_realloc(state, state->finfo, struct file_info,
774 old_num_finfo + ff_searchcount);
775 if (tevent_req_nomem(tmp, req)) {
780 p2 = p = (char *)data;
781 data_end = (char *)data + num_data;
782 last_name_raw = data_blob_null;
784 for (i=0; i<ff_searchcount; i++) {
785 if (p2 >= data_end) {
789 if ((state->info_level == SMB_FIND_FILE_BOTH_DIRECTORY_INFO)
790 && (i == ff_searchcount-1)) {
791 /* Last entry - fixup the last offset length. */
792 SIVAL(p2, 0, PTR_DIFF((data + num_data), p2));
795 data_blob_free(&last_name_raw);
797 finfo = &state->finfo[old_num_finfo + i];
799 p2 += interpret_long_filename(
800 state->finfo, /* Stick fname to the array as such */
801 state->cli, state->info_level,
802 (char *)data, recv_flags2, p2,
803 data_end, finfo, &resume_key, &last_name_raw);
805 if (finfo->name == NULL) {
806 DEBUG(1, ("cli_list: Error: unable to parse name from "
807 "info level %d\n", state->info_level));
808 tevent_req_nterror(req,
809 NT_STATUS_INVALID_NETWORK_RESPONSE);
813 status = is_bad_finfo_name(state->cli, finfo);
814 if (!NT_STATUS_IS_OK(status)) {
815 smbXcli_conn_disconnect(state->cli->conn, status);
816 tevent_req_nterror(req, status);
820 if (!state->first && (state->mask[0] != '\0') &&
821 strcsequal(finfo->name, state->mask)) {
822 DEBUG(1, ("Error: Looping in FIND_NEXT as name %s has "
823 "already been seen?\n", finfo->name));
829 if (ff_searchcount == 0) {
837 * Shrink state->finfo to the real length we received
839 tmp = talloc_realloc(state, state->finfo, struct file_info,
841 if (tevent_req_nomem(tmp, req)) {
846 state->first = false;
849 data_blob_free(&last_name_raw);
850 tevent_req_done(req);
854 TALLOC_FREE(state->mask);
855 state->mask = talloc_strdup(state, finfo->name);
856 if (tevent_req_nomem(state->mask, req)) {
860 SSVAL(&state->setup[0], 0, TRANSACT2_FINDNEXT);
862 param = talloc_realloc(state, state->param, uint8_t, 12);
863 if (tevent_req_nomem(param, req)) {
866 state->param = param;
868 SSVAL(param, 0, state->ff_dir_handle);
869 SSVAL(param, 2, state->max_matches); /* max count */
870 SSVAL(param, 4, state->info_level);
872 * For W2K servers serving out FAT filesystems we *must* set
873 * the resume key. If it's not FAT then it's returned as zero.
875 SIVAL(param, 6, resume_key); /* ff_resume_key */
877 * NB. *DON'T* use continue here. If you do it seems that W2K
878 * and bretheren can miss filenames. Use last filename
879 * continue instead. JRA
881 SSVAL(param, 10, (FLAG_TRANS2_FIND_REQUIRE_RESUME
882 |FLAG_TRANS2_FIND_CLOSE_IF_END
883 |(state->cli->backup_intent ? FLAG_TRANS2_FIND_BACKUP_INTENT : 0)));
884 if (last_name_raw.length) {
885 state->param = trans2_bytes_push_bytes(state->param,
887 last_name_raw.length);
888 if (tevent_req_nomem(state->param, req)) {
891 data_blob_free(&last_name_raw);
893 state->param = trans2_bytes_push_str(state->param,
894 smbXcli_conn_use_unicode(state->cli->conn),
896 strlen(state->mask)+1,
898 if (tevent_req_nomem(state->param, req)) {
902 param_len = talloc_get_size(state->param);
904 if (clistr_is_previous_version_path(state->mask)) {
905 additional_flags2 = FLAGS2_REPARSE_PATH;
908 subreq = cli_trans_send(state, state->ev, state->cli, additional_flags2,
909 SMBtrans2, NULL, -1, 0, 0,
911 state->param, param_len, 10,
912 NULL, 0, CLI_BUFFER_SIZE);
913 if (tevent_req_nomem(subreq, req)) {
916 tevent_req_set_callback(subreq, cli_list_trans_done, req);
919 static NTSTATUS cli_list_trans_recv(struct tevent_req *req,
921 struct file_info **finfo)
923 struct cli_list_trans_state *state = tevent_req_data(
924 req, struct cli_list_trans_state);
927 if (tevent_req_is_nterror(req, &status)) {
930 *finfo = talloc_move(mem_ctx, &state->finfo);
934 NTSTATUS cli_list_trans(struct cli_state *cli, const char *mask,
935 uint32_t attribute, int info_level,
937 struct file_info *finfo,
942 TALLOC_CTX *frame = talloc_stackframe();
943 struct tevent_context *ev;
944 struct tevent_req *req;
946 struct file_info *finfo = NULL;
947 NTSTATUS status = NT_STATUS_NO_MEMORY;
949 if (smbXcli_conn_has_async_calls(cli->conn)) {
951 * Can't use sync call while an async call is in flight
953 status = NT_STATUS_INVALID_PARAMETER;
956 ev = samba_tevent_context_init(frame);
960 req = cli_list_trans_send(frame, ev, cli, mask, attribute, info_level);
964 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
967 status = cli_list_trans_recv(req, frame, &finfo);
968 if (!NT_STATUS_IS_OK(status)) {
971 num_finfo = talloc_array_length(finfo);
972 for (i=0; i<num_finfo; i++) {
973 status = fn(&finfo[i], mask, private_data);
974 if (!NT_STATUS_IS_OK(status)) {
983 struct cli_list_state {
984 struct tevent_context *ev;
985 struct tevent_req *subreq;
986 NTSTATUS (*recv_fn)(struct tevent_req *req, TALLOC_CTX *mem_ctx,
987 struct file_info **finfo);
988 struct file_info *finfo;
992 static void cli_list_done(struct tevent_req *subreq);
994 struct tevent_req *cli_list_send(TALLOC_CTX *mem_ctx,
995 struct tevent_context *ev,
996 struct cli_state *cli,
1002 struct tevent_req *req = NULL;
1003 struct cli_list_state *state;
1004 enum protocol_types proto = smbXcli_conn_protocol(cli->conn);
1006 req = tevent_req_create(mem_ctx, &state, struct cli_list_state);
1012 if (proto >= PROTOCOL_SMB2_02) {
1013 state->subreq = cli_smb2_list_send(state, ev, cli, mask,
1015 state->recv_fn = cli_smb2_list_recv;
1016 } else if (proto >= PROTOCOL_LANMAN2) {
1017 state->subreq = cli_list_trans_send(
1018 state, ev, cli, mask, attribute, info_level);
1019 state->recv_fn = cli_list_trans_recv;
1021 state->subreq = cli_list_old_send(
1022 state, ev, cli, mask, attribute);
1023 state->recv_fn = cli_list_old_recv;
1025 if (tevent_req_nomem(state->subreq, req)) {
1026 return tevent_req_post(req, ev);
1028 tevent_req_set_callback(state->subreq, cli_list_done, req);
1032 static void cli_list_done(struct tevent_req *subreq)
1034 struct tevent_req *req = tevent_req_callback_data(
1035 subreq, struct tevent_req);
1036 struct cli_list_state *state = tevent_req_data(
1037 req, struct cli_list_state);
1040 SMB_ASSERT(subreq == state->subreq);
1043 * We don't want to be called by the lowerlevel routines
1044 * from within state->recv_fn()
1046 tevent_req_set_callback(subreq, NULL, NULL);
1048 status = state->recv_fn(subreq, state, &state->finfo);
1049 if (NT_STATUS_EQUAL(status, NT_STATUS_RETRY)) {
1050 /* We'll get back here */
1051 tevent_req_set_callback(subreq, cli_list_done, req);
1055 if (tevent_req_nterror(req, status)) {
1058 tevent_req_notify_callback(req);
1061 NTSTATUS cli_list_recv(
1062 struct tevent_req *req,
1063 TALLOC_CTX *mem_ctx,
1064 struct file_info **pfinfo)
1066 struct cli_list_state *state = tevent_req_data(
1067 req, struct cli_list_state);
1069 struct file_info *finfo = NULL;
1073 in_progress = tevent_req_is_in_progress(req);
1076 if (!tevent_req_is_nterror(req, &status)) {
1077 status = NT_STATUS_NO_MORE_FILES;
1082 if (state->finfo == NULL) {
1083 status = state->recv_fn(state->subreq, state, &state->finfo);
1085 if (NT_STATUS_EQUAL(status, NT_STATUS_RETRY)) {
1086 tevent_req_set_callback(
1087 state->subreq, cli_list_done, req);
1088 return NT_STATUS_RETRY;
1091 if (NT_STATUS_IS_OK(status) && (state->finfo == NULL)) {
1092 status = NT_STATUS_NO_MORE_FILES;
1095 if (tevent_req_nterror(req, status)) {
1099 state->num_received = 0;
1102 num_results = talloc_array_length(state->finfo);
1104 if (num_results == 1) {
1105 finfo = talloc_move(mem_ctx, &state->finfo);
1107 struct file_info *src_finfo =
1108 &state->finfo[state->num_received];
1110 finfo = talloc(mem_ctx, struct file_info);
1111 if (finfo == NULL) {
1112 return NT_STATUS_NO_MEMORY;
1114 *finfo = *src_finfo;
1115 finfo->name = talloc_move(finfo, &src_finfo->name);
1116 finfo->short_name = talloc_move(finfo, &src_finfo->short_name);
1119 state->num_received += 1;
1121 if (state->num_received == num_results) {
1122 TALLOC_FREE(state->finfo);
1125 tevent_req_defer_callback(req, state->ev);
1126 tevent_req_notify_callback(req);
1129 return NT_STATUS_OK;
1132 struct cli_list_sync_state {
1135 NTSTATUS (*fn)(struct file_info *finfo,
1137 void *private_data);
1140 bool processed_file;
1143 static void cli_list_sync_cb(struct tevent_req *subreq)
1145 struct cli_list_sync_state *state =
1146 tevent_req_callback_data_void(subreq);
1147 struct file_info *finfo;
1150 state->status = cli_list_recv(subreq, talloc_tos(), &finfo);
1151 /* No TALLOC_FREE(subreq), we get here more than once */
1153 if (NT_STATUS_EQUAL(state->status, NT_STATUS_RETRY)) {
1155 * The lowlevel SMB call was rearmed, we'll get back
1156 * here when it's done.
1158 state->status = NT_STATUS_OK;
1162 if (!NT_STATUS_IS_OK(state->status)) {
1166 ok = dir_check_ftype(finfo->attr, state->attribute);
1169 * Only process if attributes match. On SMB1 server
1170 * does this, so on SMB2 we need to emulate in the
1173 * https://bugzilla.samba.org/show_bug.cgi?id=10260
1178 state->status = state->fn(finfo, state->mask, state->private_data);
1180 state->processed_file = true;
1185 NTSTATUS cli_list(struct cli_state *cli,
1188 NTSTATUS (*fn)(struct file_info *finfo,
1190 void *private_data),
1193 TALLOC_CTX *frame = NULL;
1194 struct cli_list_sync_state state = {
1196 .attribute = attribute,
1198 .private_data = private_data,
1200 struct tevent_context *ev;
1201 struct tevent_req *req;
1202 NTSTATUS status = NT_STATUS_NO_MEMORY;
1203 uint16_t info_level;
1204 enum protocol_types proto = smbXcli_conn_protocol(cli->conn);
1206 frame = talloc_stackframe();
1208 if (smbXcli_conn_has_async_calls(cli->conn)) {
1210 * Can't use sync call while an async call is in flight
1212 status = NT_STATUS_INVALID_PARAMETER;
1215 ev = samba_tevent_context_init(frame);
1220 if (proto >= PROTOCOL_SMB2_02) {
1221 info_level = SMB2_FIND_ID_BOTH_DIRECTORY_INFO;
1223 info_level = (smb1cli_conn_capabilities(cli->conn) & CAP_NT_SMBS)
1224 ? SMB_FIND_FILE_BOTH_DIRECTORY_INFO : SMB_FIND_INFO_STANDARD;
1227 req = cli_list_send(frame, ev, cli, mask, attribute, info_level, false);
1231 tevent_req_set_callback(req, cli_list_sync_cb, &state);
1233 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
1237 status = state.status;
1239 if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_FILES)) {
1240 status = NT_STATUS_OK;
1243 if (NT_STATUS_IS_OK(status) && !state.processed_file) {
1244 status = NT_STATUS_NO_SUCH_FILE;