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 "../lib/util/tevent_ntstatus.h"
22 #include "async_smb.h"
25 /****************************************************************************
26 Calculate a safe next_entry_offset.
27 ****************************************************************************/
29 static size_t calc_next_entry_offset(const char *base, const char *pdata_end)
31 size_t next_entry_offset = (size_t)IVAL(base,0);
33 if (next_entry_offset == 0 ||
34 base + next_entry_offset < base ||
35 base + next_entry_offset > pdata_end) {
36 next_entry_offset = pdata_end - base;
38 return next_entry_offset;
41 /****************************************************************************
42 Interpret a long filename structure - this is mostly guesses at the moment.
43 The length of the structure is returned
44 The structure of a long filename depends on the info level.
45 SMB_FIND_FILE_BOTH_DIRECTORY_INFO is used
46 by NT and SMB_FIND_EA_SIZE is used by OS/2
47 ****************************************************************************/
49 static size_t interpret_long_filename(TALLOC_CTX *ctx,
50 struct cli_state *cli,
55 const char *pdata_end,
56 struct file_info *finfo,
58 DATA_BLOB *p_last_name_raw)
64 data_blob_free(p_last_name_raw);
72 case SMB_FIND_INFO_STANDARD: /* OS/2 understands this */
73 /* these dates are converted to GMT by
75 if (pdata_end - base < 27) {
76 return pdata_end - base;
78 finfo->ctime_ts = convert_time_t_to_timespec(
79 make_unix_date2(p+4, cli->serverzone));
80 finfo->atime_ts = convert_time_t_to_timespec(
81 make_unix_date2(p+8, cli->serverzone));
82 finfo->mtime_ts = convert_time_t_to_timespec(
83 make_unix_date2(p+12, cli->serverzone));
84 finfo->size = IVAL(p,16);
85 finfo->mode = CVAL(p,24);
88 p += align_string(base_ptr, p, 0);
90 /* We can safely use len here (which is required by OS/2)
91 * and the NAS-BASIC server instead of +2 or +1 as the
92 * STR_TERMINATE flag below is
93 * actually used as the length calculation.
94 * The len is merely an upper bound.
95 * Due to the explicit 2 byte null termination
96 * in cli_receive_trans/cli_receive_nt_trans
97 * we know this is safe. JRA + kukks
100 if (p + len > pdata_end) {
101 return pdata_end - base;
104 /* the len+2 below looks strange but it is
105 important to cope with the differences
106 between win2000 and win9x for this call
108 ret = clistr_pull_talloc(ctx,
115 if (ret == (size_t)-1) {
116 return pdata_end - base;
119 return PTR_DIFF(p, base);
121 case SMB_FIND_EA_SIZE: /* this is what OS/2 uses mostly */
122 /* these dates are converted to GMT by
124 if (pdata_end - base < 31) {
125 return pdata_end - base;
127 finfo->ctime_ts = convert_time_t_to_timespec(
128 make_unix_date2(p+4, cli->serverzone));
129 finfo->atime_ts = convert_time_t_to_timespec(
130 make_unix_date2(p+8, cli->serverzone));
131 finfo->mtime_ts = convert_time_t_to_timespec(
132 make_unix_date2(p+12, cli->serverzone));
133 finfo->size = IVAL(p,16);
134 finfo->mode = CVAL(p,24);
137 /* check for unisys! */
138 if (p + len + 1 > pdata_end) {
139 return pdata_end - base;
141 ret = clistr_pull_talloc(ctx,
148 if (ret == (size_t)-1) {
149 return pdata_end - base;
152 return PTR_DIFF(p, base) + 1;
154 case SMB_FIND_FILE_BOTH_DIRECTORY_INFO: /* NT uses this, but also accepts 2 */
156 size_t namelen, slen;
158 if (pdata_end - base < 94) {
159 return pdata_end - base;
162 p += 4; /* next entry offset */
165 *p_resume_key = IVAL(p,0);
167 p += 4; /* fileindex */
169 /* Offset zero is "create time", not "change time". */
171 finfo->atime_ts = interpret_long_date(p);
173 finfo->mtime_ts = interpret_long_date(p);
175 finfo->ctime_ts = interpret_long_date(p);
177 finfo->size = IVAL2_TO_SMB_BIG_UINT(p,0);
179 p += 8; /* alloc size */
180 finfo->mode = CVAL(p,0);
184 p += 4; /* EA size */
187 /* Bad short name length. */
188 return pdata_end - base;
192 /* stupid NT bugs. grr */
194 if (p[1] == 0 && namelen > 1) flags |= STR_UNICODE;
195 clistr_pull(base_ptr, finfo->short_name, p,
196 sizeof(finfo->short_name),
199 p += 24; /* short name? */
200 if (p + namelen < p || p + namelen > pdata_end) {
201 return pdata_end - base;
203 ret = clistr_pull_talloc(ctx,
210 if (ret == (size_t)-1) {
211 return pdata_end - base;
214 /* To be robust in the face of unicode conversion failures
215 we need to copy the raw bytes of the last name seen here.
216 Namelen doesn't include the terminating unicode null, so
219 if (p_last_name_raw) {
220 *p_last_name_raw = data_blob(NULL, namelen+2);
221 memcpy(p_last_name_raw->data, p, namelen);
222 SSVAL(p_last_name_raw->data, namelen, 0);
224 return calc_next_entry_offset(base, pdata_end);
228 DEBUG(1,("Unknown long filename format %d\n",level));
229 return calc_next_entry_offset(base, pdata_end);
232 /****************************************************************************
233 Interpret a short filename structure.
234 The length of the structure is returned.
235 ****************************************************************************/
237 static bool interpret_short_filename(TALLOC_CTX *ctx,
238 struct cli_state *cli,
240 struct file_info *finfo)
245 finfo->mode = CVAL(p,21);
247 /* this date is converted to GMT by make_unix_date */
248 finfo->ctime_ts.tv_sec = make_unix_date(p+22, cli->serverzone);
249 finfo->ctime_ts.tv_nsec = 0;
250 finfo->mtime_ts.tv_sec = finfo->atime_ts.tv_sec = finfo->ctime_ts.tv_sec;
251 finfo->mtime_ts.tv_nsec = finfo->atime_ts.tv_nsec = 0;
252 finfo->size = IVAL(p,26);
253 ret = clistr_pull_talloc(ctx,
255 SVAL(cli->inbuf, smb_flg2),
260 if (ret == (size_t)-1) {
265 strlcpy(finfo->short_name,
267 sizeof(finfo->short_name));
272 struct cli_list_old_state {
273 struct tevent_context *ev;
274 struct cli_state *cli;
279 uint8_t search_status[23];
285 static void cli_list_old_done(struct tevent_req *subreq);
287 static struct tevent_req *cli_list_old_send(TALLOC_CTX *mem_ctx,
288 struct tevent_context *ev,
289 struct cli_state *cli,
293 struct tevent_req *req, *subreq;
294 struct cli_list_old_state *state;
296 static const uint16_t zero = 0;
298 req = tevent_req_create(mem_ctx, &state, struct cli_list_old_state);
304 state->attribute = attribute;
306 state->mask = talloc_strdup(state, mask);
307 if (tevent_req_nomem(state->mask, req)) {
308 return tevent_req_post(req, ev);
310 state->num_asked = (cli->max_xmit - 100) / DIR_STRUCT_SIZE;
312 SSVAL(state->vwv + 0, 0, state->num_asked);
313 SSVAL(state->vwv + 1, 0, state->attribute);
315 bytes = talloc_array(state, uint8_t, 1);
316 if (tevent_req_nomem(bytes, req)) {
317 return tevent_req_post(req, ev);
320 bytes = smb_bytes_push_str(bytes, cli_ucs2(cli), mask,
321 strlen(mask)+1, NULL);
323 bytes = smb_bytes_push_bytes(bytes, 5, (const uint8_t *)&zero, 2);
324 if (tevent_req_nomem(bytes, req)) {
325 return tevent_req_post(req, ev);
328 subreq = cli_smb_send(state, state->ev, state->cli, SMBsearch,
329 0, 2, state->vwv, talloc_get_size(bytes), bytes);
330 if (tevent_req_nomem(subreq, req)) {
331 return tevent_req_post(req, ev);
333 tevent_req_set_callback(subreq, cli_list_old_done, req);
337 static void cli_list_old_done(struct tevent_req *subreq)
339 struct tevent_req *req = tevent_req_callback_data(
340 subreq, struct tevent_req);
341 struct cli_list_old_state *state = tevent_req_data(
342 req, struct cli_list_old_state);
353 status = cli_smb_recv(subreq, state, NULL, 0, &wct, &vwv, &num_bytes,
355 if (!NT_STATUS_IS_OK(status)
356 && !NT_STATUS_EQUAL(status, NT_STATUS_DOS(ERRDOS, ERRnofiles))
357 && !NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) {
359 tevent_req_nterror(req, status);
362 if (NT_STATUS_EQUAL(status, NT_STATUS_DOS(ERRDOS, ERRnofiles))
363 || NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) {
369 req, NT_STATUS_INVALID_NETWORK_RESPONSE);
372 received = SVAL(vwv + 0, 0);
377 * I don't think this can wrap. received is
378 * initialized from a 16-bit value.
380 if (num_bytes < (received * DIR_STRUCT_SIZE + 3)) {
383 req, NT_STATUS_INVALID_NETWORK_RESPONSE);
387 dirlist_len = talloc_get_size(state->dirlist);
389 tmp = TALLOC_REALLOC_ARRAY(
390 state, state->dirlist, uint8_t,
391 dirlist_len + received * DIR_STRUCT_SIZE);
392 if (tevent_req_nomem(tmp, req)) {
395 state->dirlist = tmp;
396 memcpy(state->dirlist + dirlist_len, bytes + 3,
397 received * DIR_STRUCT_SIZE);
399 SSVAL(state->search_status, 0, 21);
400 memcpy(state->search_status + 2,
401 bytes + 3 + (received-1)*DIR_STRUCT_SIZE, 21);
404 if (state->first || state->done) {
405 tevent_req_done(req);
409 state->num_asked = 0;
414 state->first = false;
416 SSVAL(state->vwv + 0, 0, state->num_asked);
417 SSVAL(state->vwv + 1, 0, state->attribute);
419 bytes = talloc_array(state, uint8_t, 1);
420 if (tevent_req_nomem(bytes, req)) {
424 bytes = smb_bytes_push_str(bytes, cli_ucs2(state->cli), "",
426 bytes = smb_bytes_push_bytes(bytes, 5, state->search_status,
427 sizeof(state->search_status));
428 if (tevent_req_nomem(bytes, req)) {
431 subreq = cli_smb_send(state, state->ev, state->cli, cmd, 0,
432 2, state->vwv, talloc_get_size(bytes), bytes);
433 if (tevent_req_nomem(subreq, req)) {
436 tevent_req_set_callback(subreq, cli_list_old_done, req);
439 static NTSTATUS cli_list_old_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
440 struct file_info **pfinfo)
442 struct cli_list_old_state *state = tevent_req_data(
443 req, struct cli_list_old_state);
445 size_t i, num_received;
446 struct file_info *finfo;
448 if (tevent_req_is_nterror(req, &status)) {
452 num_received = talloc_array_length(state->dirlist) / DIR_STRUCT_SIZE;
454 finfo = TALLOC_ARRAY(mem_ctx, struct file_info, num_received);
456 return NT_STATUS_NO_MEMORY;
459 for (i=0; i<num_received; i++) {
460 if (!interpret_short_filename(
462 (char *)state->dirlist + i * DIR_STRUCT_SIZE,
465 return NT_STATUS_NO_MEMORY;
472 NTSTATUS cli_list_old(struct cli_state *cli, const char *mask,
474 NTSTATUS (*fn)(const char *, struct file_info *,
475 const char *, void *), void *state)
477 TALLOC_CTX *frame = talloc_stackframe();
478 struct event_context *ev;
479 struct tevent_req *req;
480 NTSTATUS status = NT_STATUS_NO_MEMORY;
481 struct file_info *finfo;
484 if (cli_has_async_calls(cli)) {
486 * Can't use sync call while an async call is in flight
488 status = NT_STATUS_INVALID_PARAMETER;
491 ev = event_context_init(frame);
495 req = cli_list_old_send(frame, ev, cli, mask, attribute);
499 if (!tevent_req_poll(req, ev)) {
500 status = map_nt_error_from_unix(errno);
503 status = cli_list_old_recv(req, frame, &finfo);
504 if (!NT_STATUS_IS_OK(status)) {
507 num_finfo = talloc_array_length(finfo);
508 for (i=0; i<num_finfo; i++) {
509 status = fn(cli->dfs_mountpoint, &finfo[i], mask, state);
510 if (!NT_STATUS_IS_OK(status)) {
516 if (!NT_STATUS_IS_OK(status)) {
517 cli_set_error(cli, status);
522 struct cli_list_trans_state {
523 struct tevent_context *ev;
524 struct cli_state *cli;
531 uint16_t max_matches;
540 struct file_info *finfo;
543 static void cli_list_trans_done(struct tevent_req *subreq);
545 static struct tevent_req *cli_list_trans_send(TALLOC_CTX *mem_ctx,
546 struct tevent_context *ev,
547 struct cli_state *cli,
552 struct tevent_req *req, *subreq;
553 struct cli_list_trans_state *state;
554 size_t nlen, param_len;
557 req = tevent_req_create(mem_ctx, &state,
558 struct cli_list_trans_state);
564 state->mask = talloc_strdup(state, mask);
565 if (tevent_req_nomem(state->mask, req)) {
566 return tevent_req_post(req, ev);
568 state->attribute = attribute;
569 state->info_level = info_level;
570 state->loop_count = 0;
573 state->max_matches = 1366; /* Match W2k */
575 state->setup[0] = TRANSACT2_FINDFIRST;
577 nlen = 2*(strlen(mask)+1);
578 state->param = TALLOC_ARRAY(state, uint8_t, 12+nlen+2);
579 if (tevent_req_nomem(state->param, req)) {
580 return tevent_req_post(req, ev);
583 SSVAL(state->param, 0, state->attribute);
584 SSVAL(state->param, 2, state->max_matches);
585 SSVAL(state->param, 4,
586 FLAG_TRANS2_FIND_REQUIRE_RESUME
587 |FLAG_TRANS2_FIND_CLOSE_IF_END);
588 SSVAL(state->param, 6, state->info_level);
589 SIVAL(state->param, 8, 0);
591 p = ((char *)state->param)+12;
592 p += clistr_push(state->cli, p, state->mask, nlen,
594 param_len = PTR_DIFF(p, state->param);
596 subreq = cli_trans_send(state, state->ev, state->cli,
597 SMBtrans2, NULL, -1, 0, 0,
599 state->param, param_len, 10,
600 NULL, 0, cli->max_xmit);
601 if (tevent_req_nomem(subreq, req)) {
602 return tevent_req_post(req, ev);
604 tevent_req_set_callback(subreq, cli_list_trans_done, req);
608 static void cli_list_trans_done(struct tevent_req *subreq)
610 struct tevent_req *req = tevent_req_callback_data(
611 subreq, struct tevent_req);
612 struct cli_list_trans_state *state = tevent_req_data(
613 req, struct cli_list_trans_state);
621 struct file_info *tmp;
622 size_t old_num_finfo;
623 uint16_t recv_flags2;
627 uint32_t resume_key = 0;
629 DATA_BLOB last_name_raw;
630 struct file_info *finfo = NULL;
631 size_t nlen, param_len;
633 min_param = (state->first ? 6 : 4);
635 status = cli_trans_recv(subreq, talloc_tos(), &recv_flags2,
637 ¶m, min_param, &num_param,
638 &data, 0, &num_data);
640 if (!NT_STATUS_IS_OK(status)) {
642 * TODO: retry, OS/2 nofiles
644 tevent_req_nterror(req, status);
649 state->ff_dir_handle = SVAL(param, 0);
650 ff_searchcount = SVAL(param, 2);
651 ff_eos = SVAL(param, 4) != 0;
653 ff_searchcount = SVAL(param, 0);
654 ff_eos = SVAL(param, 2) != 0;
657 old_num_finfo = talloc_array_length(state->finfo);
659 tmp = TALLOC_REALLOC_ARRAY(state, state->finfo, struct file_info,
660 old_num_finfo + ff_searchcount);
661 if (tevent_req_nomem(tmp, req)) {
666 p2 = p = (char *)data;
667 data_end = (char *)data + num_data;
668 last_name_raw = data_blob_null;
670 for (i=0; i<ff_searchcount; i++) {
671 if (p2 >= data_end) {
675 if ((state->info_level == SMB_FIND_FILE_BOTH_DIRECTORY_INFO)
676 && (i == ff_searchcount-1)) {
677 /* Last entry - fixup the last offset length. */
678 SIVAL(p2, 0, PTR_DIFF((data + num_data), p2));
681 data_blob_free(&last_name_raw);
683 finfo = &state->finfo[old_num_finfo + i];
685 p2 += interpret_long_filename(
686 state->finfo, /* Stick fname to the array as such */
687 state->cli, state->info_level,
688 (char *)data, recv_flags2, p2,
689 data_end, finfo, &resume_key, &last_name_raw);
691 if (finfo->name == NULL) {
692 DEBUG(1, ("cli_list: Error: unable to parse name from "
693 "info level %d\n", state->info_level));
697 if (!state->first && (state->mask[0] != '\0') &&
698 strcsequal(finfo->name, state->mask)) {
699 DEBUG(1, ("Error: Looping in FIND_NEXT as name %s has "
700 "already been seen?\n", finfo->name));
706 if (ff_searchcount == 0) {
714 * Shrink state->finfo to the real length we received
716 tmp = TALLOC_REALLOC_ARRAY(state, state->finfo, struct file_info,
718 if (tevent_req_nomem(tmp, req)) {
723 state->first = false;
726 data_blob_free(&last_name_raw);
727 tevent_req_done(req);
731 TALLOC_FREE(state->mask);
732 state->mask = talloc_strdup(state, finfo->name);
733 if (tevent_req_nomem(state->mask, req)) {
737 state->setup[0] = TRANSACT2_FINDNEXT;
739 nlen = 2*(strlen(state->mask) + 1);
741 param = TALLOC_REALLOC_ARRAY(state, state->param, uint8_t,
742 12 + nlen + last_name_raw.length + 2);
743 if (tevent_req_nomem(param, req)) {
746 state->param = param;
748 SSVAL(param, 0, state->ff_dir_handle);
749 SSVAL(param, 2, state->max_matches); /* max count */
750 SSVAL(param, 4, state->info_level);
752 * For W2K servers serving out FAT filesystems we *must* set
753 * the resume key. If it's not FAT then it's returned as zero.
755 SIVAL(param, 6, resume_key); /* ff_resume_key */
757 * NB. *DON'T* use continue here. If you do it seems that W2K
758 * and bretheren can miss filenames. Use last filename
759 * continue instead. JRA
761 SSVAL(param, 10, (FLAG_TRANS2_FIND_REQUIRE_RESUME
762 |FLAG_TRANS2_FIND_CLOSE_IF_END));
763 p = ((char *)param)+12;
764 if (last_name_raw.length) {
765 memcpy(p, last_name_raw.data, last_name_raw.length);
766 p += last_name_raw.length;
767 data_blob_free(&last_name_raw);
769 p += clistr_push(state->cli, p, state->mask, nlen,
773 param_len = PTR_DIFF(p, param);
775 subreq = cli_trans_send(state, state->ev, state->cli,
776 SMBtrans2, NULL, -1, 0, 0,
778 state->param, param_len, 10,
779 NULL, 0, state->cli->max_xmit);
780 if (tevent_req_nomem(subreq, req)) {
783 tevent_req_set_callback(subreq, cli_list_trans_done, req);
786 static NTSTATUS cli_list_trans_recv(struct tevent_req *req,
788 struct file_info **finfo)
790 struct cli_list_trans_state *state = tevent_req_data(
791 req, struct cli_list_trans_state);
794 if (tevent_req_is_nterror(req, &status)) {
797 *finfo = talloc_move(mem_ctx, &state->finfo);
801 NTSTATUS cli_list_trans(struct cli_state *cli, const char *mask,
802 uint16_t attribute, int info_level,
803 NTSTATUS (*fn)(const char *mnt, struct file_info *finfo,
804 const char *mask, void *private_data),
807 TALLOC_CTX *frame = talloc_stackframe();
808 struct event_context *ev;
809 struct tevent_req *req;
811 struct file_info *finfo = NULL;
812 NTSTATUS status = NT_STATUS_NO_MEMORY;
814 if (cli_has_async_calls(cli)) {
816 * Can't use sync call while an async call is in flight
818 status = NT_STATUS_INVALID_PARAMETER;
821 ev = event_context_init(frame);
825 req = cli_list_trans_send(frame, ev, cli, mask, attribute, info_level);
829 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
832 status = cli_list_trans_recv(req, frame, &finfo);
833 if (!NT_STATUS_IS_OK(status)) {
836 num_finfo = talloc_array_length(finfo);
837 for (i=0; i<num_finfo; i++) {
838 status = fn(cli->dfs_mountpoint, &finfo[i], mask, private_data);
839 if (!NT_STATUS_IS_OK(status)) {
845 if (!NT_STATUS_IS_OK(status)) {
846 cli_set_error(cli, status);
851 struct cli_list_state {
852 NTSTATUS (*recv_fn)(struct tevent_req *req, TALLOC_CTX *mem_ctx,
853 struct file_info **finfo);
854 struct file_info *finfo;
857 static void cli_list_done(struct tevent_req *subreq);
859 struct tevent_req *cli_list_send(TALLOC_CTX *mem_ctx,
860 struct tevent_context *ev,
861 struct cli_state *cli,
866 struct tevent_req *req, *subreq;
867 struct cli_list_state *state;
869 req = tevent_req_create(mem_ctx, &state, struct cli_list_state);
874 if (cli->protocol <= PROTOCOL_LANMAN1) {
875 subreq = cli_list_old_send(state, ev, cli, mask, attribute);
876 state->recv_fn = cli_list_old_recv;
878 subreq = cli_list_trans_send(state, ev, cli, mask, attribute,
880 state->recv_fn = cli_list_trans_recv;
882 if (tevent_req_nomem(subreq, req)) {
883 return tevent_req_post(req, ev);
885 tevent_req_set_callback(subreq, cli_list_done, req);
889 static void cli_list_done(struct tevent_req *subreq)
891 struct tevent_req *req = tevent_req_callback_data(
892 subreq, struct tevent_req);
893 struct cli_list_state *state = tevent_req_data(
894 req, struct cli_list_state);
897 status = state->recv_fn(subreq, state, &state->finfo);
899 if (!NT_STATUS_IS_OK(status)) {
900 tevent_req_nterror(req, status);
903 tevent_req_done(req);
906 NTSTATUS cli_list_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
907 struct file_info **finfo, size_t *num_finfo)
909 struct cli_list_state *state = tevent_req_data(
910 req, struct cli_list_state);
913 if (tevent_req_is_nterror(req, &status)) {
916 *num_finfo = talloc_array_length(state->finfo);
917 *finfo = talloc_move(mem_ctx, &state->finfo);
921 NTSTATUS cli_list(struct cli_state *cli, const char *mask, uint16 attribute,
922 NTSTATUS (*fn)(const char *, struct file_info *, const char *,
923 void *), void *state)
925 TALLOC_CTX *frame = talloc_stackframe();
926 struct event_context *ev;
927 struct tevent_req *req;
928 NTSTATUS status = NT_STATUS_NO_MEMORY;
929 struct file_info *finfo;
933 if (cli_has_async_calls(cli)) {
935 * Can't use sync call while an async call is in flight
937 status = NT_STATUS_INVALID_PARAMETER;
940 ev = event_context_init(frame);
945 info_level = (cli->capabilities & CAP_NT_SMBS)
946 ? SMB_FIND_FILE_BOTH_DIRECTORY_INFO : SMB_FIND_INFO_STANDARD;
948 req = cli_list_send(frame, ev, cli, mask, attribute, info_level);
952 if (!tevent_req_poll(req, ev)) {
953 status = map_nt_error_from_unix(errno);
957 status = cli_list_recv(req, frame, &finfo, &num_finfo);
958 if (!NT_STATUS_IS_OK(status)) {
962 for (i=0; i<num_finfo; i++) {
963 status = fn(cli->dfs_mountpoint, &finfo[i], mask, state);
964 if (!NT_STATUS_IS_OK(status)) {
970 if (!NT_STATUS_IS_OK(status)) {
971 cli_set_error(cli, status);