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 /* NB. We need to enlarge finfo->attr to be 32-bits. */
261 finfo->attr = (uint16_t)IVAL(p,0);
265 p += 4; /* EA size */
268 /* Bad short name length. */
269 return pdata_end - base;
272 ret = pull_string_talloc(ctx,
279 if (ret == (size_t)-1) {
280 return pdata_end - base;
282 p += 24; /* short name? */
283 if (p + namelen < p || p + namelen > pdata_end) {
284 return pdata_end - base;
286 ret = pull_string_talloc(ctx,
293 if (ret == (size_t)-1) {
294 return pdata_end - base;
297 /* To be robust in the face of unicode conversion failures
298 we need to copy the raw bytes of the last name seen here.
299 Namelen doesn't include the terminating unicode null, so
302 if (p_last_name_raw) {
303 *p_last_name_raw = data_blob(NULL, namelen+2);
304 memcpy(p_last_name_raw->data, p, namelen);
305 SSVAL(p_last_name_raw->data, namelen, 0);
307 return calc_next_entry_offset(base, pdata_end);
311 DEBUG(1,("Unknown long filename format %d\n",level));
312 return calc_next_entry_offset(base, pdata_end);
315 /****************************************************************************
316 Interpret a short filename structure.
317 The length of the structure is returned.
318 ****************************************************************************/
320 static bool interpret_short_filename(TALLOC_CTX *ctx,
321 struct cli_state *cli,
323 struct file_info *finfo)
328 finfo->attr = CVAL(p,21);
330 /* We don't get birth time. */
331 finfo->btime_ts.tv_sec = 0;
332 finfo->btime_ts.tv_nsec = 0;
333 /* this date is converted to GMT by make_unix_date */
334 finfo->ctime_ts.tv_sec = make_unix_date(p+22, smb1cli_conn_server_time_zone(cli->conn));
335 finfo->ctime_ts.tv_nsec = 0;
336 finfo->mtime_ts.tv_sec = finfo->atime_ts.tv_sec = finfo->ctime_ts.tv_sec;
337 finfo->mtime_ts.tv_nsec = finfo->atime_ts.tv_nsec = 0;
338 finfo->size = IVAL(p,26);
339 ret = pull_string_talloc(ctx,
346 if (ret == (size_t)-1) {
351 finfo->short_name = talloc_strdup(ctx, finfo->name);
352 if (finfo->short_name == NULL) {
359 struct cli_list_old_state {
360 struct tevent_context *ev;
361 struct cli_state *cli;
366 uint8_t search_status[23];
372 static void cli_list_old_done(struct tevent_req *subreq);
374 static struct tevent_req *cli_list_old_send(TALLOC_CTX *mem_ctx,
375 struct tevent_context *ev,
376 struct cli_state *cli,
380 struct tevent_req *req, *subreq;
381 struct cli_list_old_state *state;
383 static const uint16_t zero = 0;
384 uint32_t usable_space;
386 req = tevent_req_create(mem_ctx, &state, struct cli_list_old_state);
392 state->attribute = attribute;
394 state->mask = talloc_strdup(state, mask);
395 if (tevent_req_nomem(state->mask, req)) {
396 return tevent_req_post(req, ev);
398 usable_space = cli_state_available_size(cli, 100);
399 state->num_asked = usable_space / DIR_STRUCT_SIZE;
401 SSVAL(state->vwv + 0, 0, state->num_asked);
402 SSVAL(state->vwv + 1, 0, state->attribute);
404 bytes = talloc_array(state, uint8_t, 1);
405 if (tevent_req_nomem(bytes, req)) {
406 return tevent_req_post(req, ev);
409 bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), mask,
410 strlen(mask)+1, NULL);
412 bytes = smb_bytes_push_bytes(bytes, 5, (const uint8_t *)&zero, 2);
413 if (tevent_req_nomem(bytes, req)) {
414 return tevent_req_post(req, ev);
417 subreq = cli_smb_send(state, state->ev, state->cli, SMBsearch, 0, 0,
418 2, state->vwv, talloc_get_size(bytes), bytes);
419 if (tevent_req_nomem(subreq, req)) {
420 return tevent_req_post(req, ev);
422 tevent_req_set_callback(subreq, cli_list_old_done, req);
426 static void cli_list_old_done(struct tevent_req *subreq)
428 struct tevent_req *req = tevent_req_callback_data(
429 subreq, struct tevent_req);
430 struct cli_list_old_state *state = tevent_req_data(
431 req, struct cli_list_old_state);
442 status = cli_smb_recv(subreq, state, NULL, 0, &wct, &vwv, &num_bytes,
444 if (!NT_STATUS_IS_OK(status)
445 && !NT_STATUS_EQUAL(status, NT_STATUS_DOS(ERRDOS, ERRnofiles))
446 && !NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) {
448 tevent_req_nterror(req, status);
451 if (NT_STATUS_EQUAL(status, NT_STATUS_DOS(ERRDOS, ERRnofiles))
452 || NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) {
458 req, NT_STATUS_INVALID_NETWORK_RESPONSE);
461 received = SVAL(vwv + 0, 0);
466 * I don't think this can wrap. received is
467 * initialized from a 16-bit value.
469 if (num_bytes < (received * DIR_STRUCT_SIZE + 3)) {
472 req, NT_STATUS_INVALID_NETWORK_RESPONSE);
476 dirlist_len = talloc_get_size(state->dirlist);
478 tmp = talloc_realloc(
479 state, state->dirlist, uint8_t,
480 dirlist_len + received * DIR_STRUCT_SIZE);
481 if (tevent_req_nomem(tmp, req)) {
484 state->dirlist = tmp;
485 memcpy(state->dirlist + dirlist_len, bytes + 3,
486 received * DIR_STRUCT_SIZE);
488 SSVAL(state->search_status, 0, 21);
489 memcpy(state->search_status + 2,
490 bytes + 3 + (received-1)*DIR_STRUCT_SIZE, 21);
493 if (state->first || state->done) {
494 tevent_req_done(req);
498 state->num_asked = 0;
503 state->first = false;
505 SSVAL(state->vwv + 0, 0, state->num_asked);
506 SSVAL(state->vwv + 1, 0, state->attribute);
508 bytes = talloc_array(state, uint8_t, 1);
509 if (tevent_req_nomem(bytes, req)) {
513 bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(state->cli->conn), "",
515 bytes = smb_bytes_push_bytes(bytes, 5, state->search_status,
516 sizeof(state->search_status));
517 if (tevent_req_nomem(bytes, req)) {
520 subreq = cli_smb_send(state, state->ev, state->cli, cmd, 0, 0,
521 2, state->vwv, talloc_get_size(bytes), bytes);
522 if (tevent_req_nomem(subreq, req)) {
525 tevent_req_set_callback(subreq, cli_list_old_done, req);
528 static NTSTATUS cli_list_old_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
529 struct file_info **pfinfo)
531 struct cli_list_old_state *state = tevent_req_data(
532 req, struct cli_list_old_state);
534 size_t i, num_received;
535 struct file_info *finfo;
537 if (tevent_req_is_nterror(req, &status)) {
541 num_received = talloc_array_length(state->dirlist) / DIR_STRUCT_SIZE;
543 finfo = talloc_array(mem_ctx, struct file_info, num_received);
545 return NT_STATUS_NO_MEMORY;
548 for (i=0; i<num_received; i++) {
549 if (!interpret_short_filename(
551 (char *)state->dirlist + i * DIR_STRUCT_SIZE,
554 return NT_STATUS_NO_MEMORY;
556 if (finfo->name == NULL) {
558 return NT_STATUS_INVALID_NETWORK_RESPONSE;
560 status = is_bad_finfo_name(state->cli, finfo);
561 if (!NT_STATUS_IS_OK(status)) {
562 smbXcli_conn_disconnect(state->cli->conn, status);
571 NTSTATUS cli_list_old(struct cli_state *cli, const char *mask,
573 NTSTATUS (*fn)(const char *, struct file_info *,
574 const char *, void *), void *state)
576 TALLOC_CTX *frame = talloc_stackframe();
577 struct tevent_context *ev;
578 struct tevent_req *req;
579 NTSTATUS status = NT_STATUS_NO_MEMORY;
580 struct file_info *finfo = NULL;
583 if (smbXcli_conn_has_async_calls(cli->conn)) {
585 * Can't use sync call while an async call is in flight
587 status = NT_STATUS_INVALID_PARAMETER;
590 ev = samba_tevent_context_init(frame);
594 req = cli_list_old_send(frame, ev, cli, mask, attribute);
598 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
601 status = cli_list_old_recv(req, frame, &finfo);
602 if (!NT_STATUS_IS_OK(status)) {
605 num_finfo = talloc_array_length(finfo);
606 for (i=0; i<num_finfo; i++) {
607 status = fn(cli->dfs_mountpoint, &finfo[i], mask, state);
608 if (!NT_STATUS_IS_OK(status)) {
617 struct cli_list_trans_state {
618 struct tevent_context *ev;
619 struct cli_state *cli;
626 uint16_t max_matches;
635 struct file_info *finfo;
638 static void cli_list_trans_done(struct tevent_req *subreq);
640 static struct tevent_req *cli_list_trans_send(TALLOC_CTX *mem_ctx,
641 struct tevent_context *ev,
642 struct cli_state *cli,
647 struct tevent_req *req, *subreq;
648 struct cli_list_trans_state *state;
650 uint16_t additional_flags2 = 0;
652 req = tevent_req_create(mem_ctx, &state,
653 struct cli_list_trans_state);
659 state->mask = talloc_strdup(state, mask);
660 if (tevent_req_nomem(state->mask, req)) {
661 return tevent_req_post(req, ev);
663 state->attribute = attribute;
664 state->info_level = info_level;
665 state->loop_count = 0;
668 state->max_matches = 1366; /* Match W2k */
670 SSVAL(&state->setup[0], 0, TRANSACT2_FINDFIRST);
672 state->param = talloc_array(state, uint8_t, 12);
673 if (tevent_req_nomem(state->param, req)) {
674 return tevent_req_post(req, ev);
677 SSVAL(state->param, 0, state->attribute);
678 SSVAL(state->param, 2, state->max_matches);
679 SSVAL(state->param, 4,
680 FLAG_TRANS2_FIND_REQUIRE_RESUME
681 |FLAG_TRANS2_FIND_CLOSE_IF_END
682 |(cli->backup_intent ? FLAG_TRANS2_FIND_BACKUP_INTENT : 0));
683 SSVAL(state->param, 6, state->info_level);
684 SIVAL(state->param, 8, 0);
686 state->param = trans2_bytes_push_str(state->param, smbXcli_conn_use_unicode(cli->conn),
687 state->mask, strlen(state->mask)+1,
689 if (tevent_req_nomem(state->param, req)) {
690 return tevent_req_post(req, ev);
693 if (clistr_is_previous_version_path(state->mask, NULL, NULL, NULL)) {
694 additional_flags2 = FLAGS2_REPARSE_PATH;
697 param_len = talloc_get_size(state->param);
699 subreq = cli_trans_send(state, state->ev, state->cli, additional_flags2,
700 SMBtrans2, NULL, -1, 0, 0,
702 state->param, param_len, 10,
703 NULL, 0, CLI_BUFFER_SIZE);
704 if (tevent_req_nomem(subreq, req)) {
705 return tevent_req_post(req, ev);
707 tevent_req_set_callback(subreq, cli_list_trans_done, req);
711 static void cli_list_trans_done(struct tevent_req *subreq)
713 struct tevent_req *req = tevent_req_callback_data(
714 subreq, struct tevent_req);
715 struct cli_list_trans_state *state = tevent_req_data(
716 req, struct cli_list_trans_state);
724 struct file_info *tmp;
725 size_t old_num_finfo;
726 uint16_t recv_flags2;
730 uint32_t resume_key = 0;
732 DATA_BLOB last_name_raw;
733 struct file_info *finfo = NULL;
735 uint16_t additional_flags2 = 0;
737 min_param = (state->first ? 6 : 4);
739 status = cli_trans_recv(subreq, talloc_tos(), &recv_flags2,
741 ¶m, min_param, &num_param,
742 &data, 0, &num_data);
744 if (!NT_STATUS_IS_OK(status)) {
746 * TODO: retry, OS/2 nofiles
748 tevent_req_nterror(req, status);
753 state->ff_dir_handle = SVAL(param, 0);
754 ff_searchcount = SVAL(param, 2);
755 ff_eos = SVAL(param, 4) != 0;
757 ff_searchcount = SVAL(param, 0);
758 ff_eos = SVAL(param, 2) != 0;
761 old_num_finfo = talloc_array_length(state->finfo);
763 tmp = talloc_realloc(state, state->finfo, struct file_info,
764 old_num_finfo + ff_searchcount);
765 if (tevent_req_nomem(tmp, req)) {
770 p2 = p = (char *)data;
771 data_end = (char *)data + num_data;
772 last_name_raw = data_blob_null;
774 for (i=0; i<ff_searchcount; i++) {
775 if (p2 >= data_end) {
779 if ((state->info_level == SMB_FIND_FILE_BOTH_DIRECTORY_INFO)
780 && (i == ff_searchcount-1)) {
781 /* Last entry - fixup the last offset length. */
782 SIVAL(p2, 0, PTR_DIFF((data + num_data), p2));
785 data_blob_free(&last_name_raw);
787 finfo = &state->finfo[old_num_finfo + i];
789 p2 += interpret_long_filename(
790 state->finfo, /* Stick fname to the array as such */
791 state->cli, state->info_level,
792 (char *)data, recv_flags2, p2,
793 data_end, finfo, &resume_key, &last_name_raw);
795 if (finfo->name == NULL) {
796 DEBUG(1, ("cli_list: Error: unable to parse name from "
797 "info level %d\n", state->info_level));
798 tevent_req_nterror(req,
799 NT_STATUS_INVALID_NETWORK_RESPONSE);
803 status = is_bad_finfo_name(state->cli, finfo);
804 if (!NT_STATUS_IS_OK(status)) {
805 smbXcli_conn_disconnect(state->cli->conn, status);
806 tevent_req_nterror(req, status);
810 if (!state->first && (state->mask[0] != '\0') &&
811 strcsequal(finfo->name, state->mask)) {
812 DEBUG(1, ("Error: Looping in FIND_NEXT as name %s has "
813 "already been seen?\n", finfo->name));
819 if (ff_searchcount == 0) {
827 * Shrink state->finfo to the real length we received
829 tmp = talloc_realloc(state, state->finfo, struct file_info,
831 if (tevent_req_nomem(tmp, req)) {
836 state->first = false;
839 data_blob_free(&last_name_raw);
840 tevent_req_done(req);
844 TALLOC_FREE(state->mask);
845 state->mask = talloc_strdup(state, finfo->name);
846 if (tevent_req_nomem(state->mask, req)) {
850 SSVAL(&state->setup[0], 0, TRANSACT2_FINDNEXT);
852 param = talloc_realloc(state, state->param, uint8_t, 12);
853 if (tevent_req_nomem(param, req)) {
856 state->param = param;
858 SSVAL(param, 0, state->ff_dir_handle);
859 SSVAL(param, 2, state->max_matches); /* max count */
860 SSVAL(param, 4, state->info_level);
862 * For W2K servers serving out FAT filesystems we *must* set
863 * the resume key. If it's not FAT then it's returned as zero.
865 SIVAL(param, 6, resume_key); /* ff_resume_key */
867 * NB. *DON'T* use continue here. If you do it seems that W2K
868 * and bretheren can miss filenames. Use last filename
869 * continue instead. JRA
871 SSVAL(param, 10, (FLAG_TRANS2_FIND_REQUIRE_RESUME
872 |FLAG_TRANS2_FIND_CLOSE_IF_END
873 |(state->cli->backup_intent ? FLAG_TRANS2_FIND_BACKUP_INTENT : 0)));
874 if (last_name_raw.length) {
875 state->param = trans2_bytes_push_bytes(state->param,
877 last_name_raw.length);
878 if (tevent_req_nomem(state->param, req)) {
881 data_blob_free(&last_name_raw);
883 state->param = trans2_bytes_push_str(state->param,
884 smbXcli_conn_use_unicode(state->cli->conn),
886 strlen(state->mask)+1,
888 if (tevent_req_nomem(state->param, req)) {
892 param_len = talloc_get_size(state->param);
894 if (clistr_is_previous_version_path(state->mask, NULL, NULL, NULL)) {
895 additional_flags2 = FLAGS2_REPARSE_PATH;
898 subreq = cli_trans_send(state, state->ev, state->cli, additional_flags2,
899 SMBtrans2, NULL, -1, 0, 0,
901 state->param, param_len, 10,
902 NULL, 0, CLI_BUFFER_SIZE);
903 if (tevent_req_nomem(subreq, req)) {
906 tevent_req_set_callback(subreq, cli_list_trans_done, req);
909 static NTSTATUS cli_list_trans_recv(struct tevent_req *req,
911 struct file_info **finfo)
913 struct cli_list_trans_state *state = tevent_req_data(
914 req, struct cli_list_trans_state);
917 if (tevent_req_is_nterror(req, &status)) {
920 *finfo = talloc_move(mem_ctx, &state->finfo);
924 NTSTATUS cli_list_trans(struct cli_state *cli, const char *mask,
925 uint16_t attribute, int info_level,
926 NTSTATUS (*fn)(const char *mnt, struct file_info *finfo,
927 const char *mask, void *private_data),
930 TALLOC_CTX *frame = talloc_stackframe();
931 struct tevent_context *ev;
932 struct tevent_req *req;
934 struct file_info *finfo = NULL;
935 NTSTATUS status = NT_STATUS_NO_MEMORY;
937 if (smbXcli_conn_has_async_calls(cli->conn)) {
939 * Can't use sync call while an async call is in flight
941 status = NT_STATUS_INVALID_PARAMETER;
944 ev = samba_tevent_context_init(frame);
948 req = cli_list_trans_send(frame, ev, cli, mask, attribute, info_level);
952 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
955 status = cli_list_trans_recv(req, frame, &finfo);
956 if (!NT_STATUS_IS_OK(status)) {
959 num_finfo = talloc_array_length(finfo);
960 for (i=0; i<num_finfo; i++) {
961 status = fn(cli->dfs_mountpoint, &finfo[i], mask, private_data);
962 if (!NT_STATUS_IS_OK(status)) {
971 struct cli_list_state {
972 NTSTATUS (*recv_fn)(struct tevent_req *req, TALLOC_CTX *mem_ctx,
973 struct file_info **finfo);
974 struct file_info *finfo;
977 static void cli_list_done(struct tevent_req *subreq);
979 struct tevent_req *cli_list_send(TALLOC_CTX *mem_ctx,
980 struct tevent_context *ev,
981 struct cli_state *cli,
986 struct tevent_req *req, *subreq;
987 struct cli_list_state *state;
989 req = tevent_req_create(mem_ctx, &state, struct cli_list_state);
994 if (smbXcli_conn_protocol(cli->conn) <= PROTOCOL_LANMAN1) {
995 subreq = cli_list_old_send(state, ev, cli, mask, attribute);
996 state->recv_fn = cli_list_old_recv;
998 subreq = cli_list_trans_send(state, ev, cli, mask, attribute,
1000 state->recv_fn = cli_list_trans_recv;
1002 if (tevent_req_nomem(subreq, req)) {
1003 return tevent_req_post(req, ev);
1005 tevent_req_set_callback(subreq, cli_list_done, req);
1009 static void cli_list_done(struct tevent_req *subreq)
1011 struct tevent_req *req = tevent_req_callback_data(
1012 subreq, struct tevent_req);
1013 struct cli_list_state *state = tevent_req_data(
1014 req, struct cli_list_state);
1017 status = state->recv_fn(subreq, state, &state->finfo);
1018 TALLOC_FREE(subreq);
1019 if (!NT_STATUS_IS_OK(status)) {
1020 tevent_req_nterror(req, status);
1023 tevent_req_done(req);
1026 NTSTATUS cli_list_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
1027 struct file_info **finfo, size_t *num_finfo)
1029 struct cli_list_state *state = tevent_req_data(
1030 req, struct cli_list_state);
1033 if (tevent_req_is_nterror(req, &status)) {
1036 *num_finfo = talloc_array_length(state->finfo);
1037 *finfo = talloc_move(mem_ctx, &state->finfo);
1038 return NT_STATUS_OK;
1041 NTSTATUS cli_list(struct cli_state *cli, const char *mask, uint16_t attribute,
1042 NTSTATUS (*fn)(const char *, struct file_info *, const char *,
1043 void *), void *state)
1045 TALLOC_CTX *frame = NULL;
1046 struct tevent_context *ev;
1047 struct tevent_req *req;
1048 NTSTATUS status = NT_STATUS_NO_MEMORY;
1049 struct file_info *finfo;
1050 size_t i, num_finfo = 0;
1051 uint16_t info_level;
1053 if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
1054 return cli_smb2_list(cli, mask, attribute, fn, state);
1057 frame = talloc_stackframe();
1059 if (smbXcli_conn_has_async_calls(cli->conn)) {
1061 * Can't use sync call while an async call is in flight
1063 status = NT_STATUS_INVALID_PARAMETER;
1066 ev = samba_tevent_context_init(frame);
1071 info_level = (smb1cli_conn_capabilities(cli->conn) & CAP_NT_SMBS)
1072 ? SMB_FIND_FILE_BOTH_DIRECTORY_INFO : SMB_FIND_INFO_STANDARD;
1074 req = cli_list_send(frame, ev, cli, mask, attribute, info_level);
1078 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
1082 status = cli_list_recv(req, frame, &finfo, &num_finfo);
1083 if (!NT_STATUS_IS_OK(status)) {
1087 for (i=0; i<num_finfo; i++) {
1088 status = fn(cli->dfs_mountpoint, &finfo[i], mask, state);
1089 if (!NT_STATUS_IS_OK(status)) {