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"
26 /****************************************************************************
27 Calculate a safe next_entry_offset.
28 ****************************************************************************/
30 static size_t calc_next_entry_offset(const char *base
, const char *pdata_end
)
32 size_t next_entry_offset
= (size_t)IVAL(base
,0);
34 if (next_entry_offset
== 0 ||
35 base
+ next_entry_offset
< base
||
36 base
+ next_entry_offset
> pdata_end
) {
37 next_entry_offset
= pdata_end
- base
;
39 return next_entry_offset
;
42 /****************************************************************************
43 Interpret a long filename structure - this is mostly guesses at the moment.
44 The length of the structure is returned
45 The structure of a long filename depends on the info level.
46 SMB_FIND_FILE_BOTH_DIRECTORY_INFO is used
47 by NT and SMB_FIND_EA_SIZE is used by OS/2
48 ****************************************************************************/
50 static size_t interpret_long_filename(TALLOC_CTX
*ctx
,
51 struct cli_state
*cli
,
56 const char *pdata_end
,
57 struct file_info
*finfo
,
59 DATA_BLOB
*p_last_name_raw
)
65 data_blob_free(p_last_name_raw
);
73 case SMB_FIND_INFO_STANDARD
: /* OS/2 understands this */
74 /* these dates are converted to GMT by
76 if (pdata_end
- base
< 27) {
77 return pdata_end
- base
;
79 finfo
->ctime_ts
= convert_time_t_to_timespec(
80 make_unix_date2(p
+4, cli
->serverzone
));
81 finfo
->atime_ts
= convert_time_t_to_timespec(
82 make_unix_date2(p
+8, cli
->serverzone
));
83 finfo
->mtime_ts
= convert_time_t_to_timespec(
84 make_unix_date2(p
+12, cli
->serverzone
));
85 finfo
->size
= IVAL(p
,16);
86 finfo
->mode
= CVAL(p
,24);
89 p
+= align_string(base_ptr
, p
, 0);
91 /* We can safely use len here (which is required by OS/2)
92 * and the NAS-BASIC server instead of +2 or +1 as the
93 * STR_TERMINATE flag below is
94 * actually used as the length calculation.
95 * The len is merely an upper bound.
96 * Due to the explicit 2 byte null termination
97 * in cli_receive_trans/cli_receive_nt_trans
98 * we know this is safe. JRA + kukks
101 if (p
+ len
> pdata_end
) {
102 return pdata_end
- base
;
105 /* the len+2 below looks strange but it is
106 important to cope with the differences
107 between win2000 and win9x for this call
109 ret
= clistr_pull_talloc(ctx
,
116 if (ret
== (size_t)-1) {
117 return pdata_end
- base
;
120 return PTR_DIFF(p
, base
);
122 case SMB_FIND_EA_SIZE
: /* this is what OS/2 uses mostly */
123 /* these dates are converted to GMT by
125 if (pdata_end
- base
< 31) {
126 return pdata_end
- base
;
128 finfo
->ctime_ts
= convert_time_t_to_timespec(
129 make_unix_date2(p
+4, cli
->serverzone
));
130 finfo
->atime_ts
= convert_time_t_to_timespec(
131 make_unix_date2(p
+8, cli
->serverzone
));
132 finfo
->mtime_ts
= convert_time_t_to_timespec(
133 make_unix_date2(p
+12, cli
->serverzone
));
134 finfo
->size
= IVAL(p
,16);
135 finfo
->mode
= CVAL(p
,24);
138 /* check for unisys! */
139 if (p
+ len
+ 1 > pdata_end
) {
140 return pdata_end
- base
;
142 ret
= clistr_pull_talloc(ctx
,
149 if (ret
== (size_t)-1) {
150 return pdata_end
- base
;
153 return PTR_DIFF(p
, base
) + 1;
155 case SMB_FIND_FILE_BOTH_DIRECTORY_INFO
: /* NT uses this, but also accepts 2 */
157 size_t namelen
, slen
;
159 if (pdata_end
- base
< 94) {
160 return pdata_end
- base
;
163 p
+= 4; /* next entry offset */
166 *p_resume_key
= IVAL(p
,0);
168 p
+= 4; /* fileindex */
170 /* Offset zero is "create time", not "change time". */
172 finfo
->atime_ts
= interpret_long_date(p
);
174 finfo
->mtime_ts
= interpret_long_date(p
);
176 finfo
->ctime_ts
= interpret_long_date(p
);
178 finfo
->size
= IVAL2_TO_SMB_BIG_UINT(p
,0);
180 p
+= 8; /* alloc size */
181 finfo
->mode
= CVAL(p
,0);
185 p
+= 4; /* EA size */
188 /* Bad short name length. */
189 return pdata_end
- base
;
193 /* stupid NT bugs. grr */
195 if (p
[1] == 0 && namelen
> 1) flags
|= STR_UNICODE
;
196 clistr_pull(base_ptr
, finfo
->short_name
, p
,
197 sizeof(finfo
->short_name
),
200 p
+= 24; /* short name? */
201 if (p
+ namelen
< p
|| p
+ namelen
> pdata_end
) {
202 return pdata_end
- base
;
204 ret
= clistr_pull_talloc(ctx
,
211 if (ret
== (size_t)-1) {
212 return pdata_end
- base
;
215 /* To be robust in the face of unicode conversion failures
216 we need to copy the raw bytes of the last name seen here.
217 Namelen doesn't include the terminating unicode null, so
220 if (p_last_name_raw
) {
221 *p_last_name_raw
= data_blob(NULL
, namelen
+2);
222 memcpy(p_last_name_raw
->data
, p
, namelen
);
223 SSVAL(p_last_name_raw
->data
, namelen
, 0);
225 return calc_next_entry_offset(base
, pdata_end
);
229 DEBUG(1,("Unknown long filename format %d\n",level
));
230 return calc_next_entry_offset(base
, pdata_end
);
233 /****************************************************************************
234 Interpret a short filename structure.
235 The length of the structure is returned.
236 ****************************************************************************/
238 static bool interpret_short_filename(TALLOC_CTX
*ctx
,
239 struct cli_state
*cli
,
241 struct file_info
*finfo
)
246 finfo
->mode
= CVAL(p
,21);
248 /* this date is converted to GMT by make_unix_date */
249 finfo
->ctime_ts
.tv_sec
= make_unix_date(p
+22, cli
->serverzone
);
250 finfo
->ctime_ts
.tv_nsec
= 0;
251 finfo
->mtime_ts
.tv_sec
= finfo
->atime_ts
.tv_sec
= finfo
->ctime_ts
.tv_sec
;
252 finfo
->mtime_ts
.tv_nsec
= finfo
->atime_ts
.tv_nsec
= 0;
253 finfo
->size
= IVAL(p
,26);
254 ret
= clistr_pull_talloc(ctx
,
256 SVAL(cli
->inbuf
, smb_flg2
),
261 if (ret
== (size_t)-1) {
266 strlcpy(finfo
->short_name
,
268 sizeof(finfo
->short_name
));
273 struct cli_list_old_state
{
274 struct tevent_context
*ev
;
275 struct cli_state
*cli
;
280 uint8_t search_status
[23];
286 static void cli_list_old_done(struct tevent_req
*subreq
);
288 static struct tevent_req
*cli_list_old_send(TALLOC_CTX
*mem_ctx
,
289 struct tevent_context
*ev
,
290 struct cli_state
*cli
,
294 struct tevent_req
*req
, *subreq
;
295 struct cli_list_old_state
*state
;
297 static const uint16_t zero
= 0;
299 req
= tevent_req_create(mem_ctx
, &state
, struct cli_list_old_state
);
305 state
->attribute
= attribute
;
307 state
->mask
= talloc_strdup(state
, mask
);
308 if (tevent_req_nomem(state
->mask
, req
)) {
309 return tevent_req_post(req
, ev
);
311 state
->num_asked
= (cli
->max_xmit
- 100) / DIR_STRUCT_SIZE
;
313 SSVAL(state
->vwv
+ 0, 0, state
->num_asked
);
314 SSVAL(state
->vwv
+ 1, 0, state
->attribute
);
316 bytes
= talloc_array(state
, uint8_t, 1);
317 if (tevent_req_nomem(bytes
, req
)) {
318 return tevent_req_post(req
, ev
);
321 bytes
= smb_bytes_push_str(bytes
, cli_ucs2(cli
), mask
,
322 strlen(mask
)+1, NULL
);
324 bytes
= smb_bytes_push_bytes(bytes
, 5, (uint8_t *)&zero
, 2);
325 if (tevent_req_nomem(bytes
, req
)) {
326 return tevent_req_post(req
, ev
);
329 subreq
= cli_smb_send(state
, state
->ev
, state
->cli
, SMBsearch
,
330 0, 2, state
->vwv
, talloc_get_size(bytes
), bytes
);
331 if (tevent_req_nomem(subreq
, req
)) {
332 return tevent_req_post(req
, ev
);
334 tevent_req_set_callback(subreq
, cli_list_old_done
, req
);
338 static void cli_list_old_done(struct tevent_req
*subreq
)
340 struct tevent_req
*req
= tevent_req_callback_data(
341 subreq
, struct tevent_req
);
342 struct cli_list_old_state
*state
= tevent_req_data(
343 req
, struct cli_list_old_state
);
354 status
= cli_smb_recv(subreq
, state
, NULL
, 0, &wct
, &vwv
, &num_bytes
,
356 if (!NT_STATUS_IS_OK(status
)
357 && !NT_STATUS_EQUAL(status
, NT_STATUS_DOS(ERRDOS
, ERRnofiles
))
358 && !NT_STATUS_EQUAL(status
, STATUS_NO_MORE_FILES
)) {
360 tevent_req_nterror(req
, status
);
363 if (NT_STATUS_EQUAL(status
, NT_STATUS_DOS(ERRDOS
, ERRnofiles
))
364 || NT_STATUS_EQUAL(status
, STATUS_NO_MORE_FILES
)) {
370 req
, NT_STATUS_INVALID_NETWORK_RESPONSE
);
373 received
= SVAL(vwv
+ 0, 0);
378 * I don't think this can wrap. received is
379 * initialized from a 16-bit value.
381 if (num_bytes
< (received
* DIR_STRUCT_SIZE
+ 3)) {
384 req
, NT_STATUS_INVALID_NETWORK_RESPONSE
);
388 dirlist_len
= talloc_get_size(state
->dirlist
);
390 tmp
= TALLOC_REALLOC_ARRAY(
391 state
, state
->dirlist
, uint8_t,
392 dirlist_len
+ received
* DIR_STRUCT_SIZE
);
393 if (tevent_req_nomem(tmp
, req
)) {
396 state
->dirlist
= tmp
;
397 memcpy(state
->dirlist
+ dirlist_len
, bytes
+ 3,
398 received
* DIR_STRUCT_SIZE
);
400 SSVAL(state
->search_status
, 0, 21);
401 memcpy(state
->search_status
+ 2,
402 bytes
+ 3 + (received
-1)*DIR_STRUCT_SIZE
, 21);
405 if (state
->first
|| state
->done
) {
406 tevent_req_done(req
);
410 state
->num_asked
= 0;
415 state
->first
= false;
417 SSVAL(state
->vwv
+ 0, 0, state
->num_asked
);
418 SSVAL(state
->vwv
+ 1, 0, state
->attribute
);
420 bytes
= talloc_array(state
, uint8_t, 1);
421 if (tevent_req_nomem(bytes
, req
)) {
425 bytes
= smb_bytes_push_str(bytes
, cli_ucs2(state
->cli
), "",
427 bytes
= smb_bytes_push_bytes(bytes
, 5, state
->search_status
,
428 sizeof(state
->search_status
));
429 if (tevent_req_nomem(bytes
, req
)) {
432 subreq
= cli_smb_send(state
, state
->ev
, state
->cli
, cmd
, 0,
433 2, state
->vwv
, talloc_get_size(bytes
), bytes
);
434 if (tevent_req_nomem(subreq
, req
)) {
437 tevent_req_set_callback(subreq
, cli_list_old_done
, req
);
440 static NTSTATUS
cli_list_old_recv(struct tevent_req
*req
, TALLOC_CTX
*mem_ctx
,
441 struct file_info
**pfinfo
)
443 struct cli_list_old_state
*state
= tevent_req_data(
444 req
, struct cli_list_old_state
);
446 size_t i
, num_received
;
447 struct file_info
*finfo
;
449 if (tevent_req_is_nterror(req
, &status
)) {
453 num_received
= talloc_array_length(state
->dirlist
) / DIR_STRUCT_SIZE
;
455 finfo
= TALLOC_ARRAY(mem_ctx
, struct file_info
, num_received
);
457 return NT_STATUS_NO_MEMORY
;
460 for (i
=0; i
<num_received
; i
++) {
461 if (!interpret_short_filename(
463 (char *)state
->dirlist
+ i
* DIR_STRUCT_SIZE
,
466 return NT_STATUS_NO_MEMORY
;
473 NTSTATUS
cli_list_old(struct cli_state
*cli
, const char *mask
,
475 NTSTATUS (*fn
)(const char *, struct file_info
*,
476 const char *, void *), void *state
)
478 TALLOC_CTX
*frame
= talloc_stackframe();
479 struct event_context
*ev
;
480 struct tevent_req
*req
;
481 NTSTATUS status
= NT_STATUS_NO_MEMORY
;
482 struct file_info
*finfo
;
485 if (cli_has_async_calls(cli
)) {
487 * Can't use sync call while an async call is in flight
489 status
= NT_STATUS_INVALID_PARAMETER
;
492 ev
= event_context_init(frame
);
496 req
= cli_list_old_send(frame
, ev
, cli
, mask
, attribute
);
500 if (!tevent_req_poll(req
, ev
)) {
501 status
= map_nt_error_from_unix(errno
);
504 status
= cli_list_old_recv(req
, frame
, &finfo
);
505 if (!NT_STATUS_IS_OK(status
)) {
508 num_finfo
= talloc_array_length(finfo
);
509 for (i
=0; i
<num_finfo
; i
++) {
510 status
= fn(cli
->dfs_mountpoint
, &finfo
[i
], mask
, state
);
511 if (!NT_STATUS_IS_OK(status
)) {
520 struct cli_list_trans_state
{
521 struct tevent_context
*ev
;
522 struct cli_state
*cli
;
529 uint16_t max_matches
;
538 struct file_info
*finfo
;
541 static void cli_list_trans_done(struct tevent_req
*subreq
);
543 static struct tevent_req
*cli_list_trans_send(TALLOC_CTX
*mem_ctx
,
544 struct tevent_context
*ev
,
545 struct cli_state
*cli
,
550 struct tevent_req
*req
, *subreq
;
551 struct cli_list_trans_state
*state
;
552 size_t nlen
, param_len
;
555 req
= tevent_req_create(mem_ctx
, &state
,
556 struct cli_list_trans_state
);
562 state
->mask
= talloc_strdup(state
, mask
);
563 if (tevent_req_nomem(state
->mask
, req
)) {
564 return tevent_req_post(req
, ev
);
566 state
->attribute
= attribute
;
567 state
->info_level
= info_level
;
568 state
->loop_count
= 0;
571 state
->max_matches
= 1366; /* Match W2k */
573 SSVAL(&state
->setup
[0], 0, TRANSACT2_FINDFIRST
);
575 nlen
= 2*(strlen(mask
)+1);
576 state
->param
= TALLOC_ARRAY(state
, uint8_t, 12+nlen
+2);
577 if (tevent_req_nomem(state
->param
, req
)) {
578 return tevent_req_post(req
, ev
);
581 SSVAL(state
->param
, 0, state
->attribute
);
582 SSVAL(state
->param
, 2, state
->max_matches
);
583 SSVAL(state
->param
, 4,
584 FLAG_TRANS2_FIND_REQUIRE_RESUME
585 |FLAG_TRANS2_FIND_CLOSE_IF_END
);
586 SSVAL(state
->param
, 6, state
->info_level
);
587 SIVAL(state
->param
, 8, 0);
589 p
= ((char *)state
->param
)+12;
590 p
+= clistr_push(state
->cli
, p
, state
->mask
, nlen
,
592 param_len
= PTR_DIFF(p
, state
->param
);
594 subreq
= cli_trans_send(state
, state
->ev
, state
->cli
,
595 SMBtrans2
, NULL
, -1, 0, 0,
597 state
->param
, param_len
, 10,
598 NULL
, 0, cli
->max_xmit
);
599 if (tevent_req_nomem(subreq
, req
)) {
600 return tevent_req_post(req
, ev
);
602 tevent_req_set_callback(subreq
, cli_list_trans_done
, req
);
606 static void cli_list_trans_done(struct tevent_req
*subreq
)
608 struct tevent_req
*req
= tevent_req_callback_data(
609 subreq
, struct tevent_req
);
610 struct cli_list_trans_state
*state
= tevent_req_data(
611 req
, struct cli_list_trans_state
);
619 struct file_info
*tmp
;
620 size_t old_num_finfo
;
621 uint16_t recv_flags2
;
625 uint32_t resume_key
= 0;
627 DATA_BLOB last_name_raw
;
628 struct file_info
*finfo
= NULL
;
629 size_t nlen
, param_len
;
631 min_param
= (state
->first
? 6 : 4);
633 status
= cli_trans_recv(subreq
, talloc_tos(), &recv_flags2
,
635 ¶m
, min_param
, &num_param
,
636 &data
, 0, &num_data
);
638 if (!NT_STATUS_IS_OK(status
)) {
640 * TODO: retry, OS/2 nofiles
642 tevent_req_nterror(req
, status
);
647 state
->ff_dir_handle
= SVAL(param
, 0);
648 ff_searchcount
= SVAL(param
, 2);
649 ff_eos
= SVAL(param
, 4) != 0;
651 ff_searchcount
= SVAL(param
, 0);
652 ff_eos
= SVAL(param
, 2) != 0;
655 old_num_finfo
= talloc_array_length(state
->finfo
);
657 tmp
= TALLOC_REALLOC_ARRAY(state
, state
->finfo
, struct file_info
,
658 old_num_finfo
+ ff_searchcount
);
659 if (tevent_req_nomem(tmp
, req
)) {
664 p2
= p
= (char *)data
;
665 data_end
= (char *)data
+ num_data
;
666 last_name_raw
= data_blob_null
;
668 for (i
=0; i
<ff_searchcount
; i
++) {
669 if (p2
>= data_end
) {
673 if ((state
->info_level
== SMB_FIND_FILE_BOTH_DIRECTORY_INFO
)
674 && (i
== ff_searchcount
-1)) {
675 /* Last entry - fixup the last offset length. */
676 SIVAL(p2
, 0, PTR_DIFF((data
+ num_data
), p2
));
679 data_blob_free(&last_name_raw
);
681 finfo
= &state
->finfo
[old_num_finfo
+ i
];
683 p2
+= interpret_long_filename(
684 state
->finfo
, /* Stick fname to the array as such */
685 state
->cli
, state
->info_level
,
686 (char *)data
, recv_flags2
, p2
,
687 data_end
, finfo
, &resume_key
, &last_name_raw
);
689 if (finfo
->name
== NULL
) {
690 DEBUG(1, ("cli_list: Error: unable to parse name from "
691 "info level %d\n", state
->info_level
));
695 if (!state
->first
&& (state
->mask
[0] != '\0') &&
696 strcsequal(finfo
->name
, state
->mask
)) {
697 DEBUG(1, ("Error: Looping in FIND_NEXT as name %s has "
698 "already been seen?\n", finfo
->name
));
704 if (ff_searchcount
== 0) {
712 * Shrink state->finfo to the real length we received
714 tmp
= TALLOC_REALLOC_ARRAY(state
, state
->finfo
, struct file_info
,
716 if (tevent_req_nomem(tmp
, req
)) {
721 state
->first
= false;
724 data_blob_free(&last_name_raw
);
725 tevent_req_done(req
);
729 TALLOC_FREE(state
->mask
);
730 state
->mask
= talloc_strdup(state
, finfo
->name
);
731 if (tevent_req_nomem(state
->mask
, req
)) {
735 SSVAL(&state
->setup
[0], 0, TRANSACT2_FINDNEXT
);
737 nlen
= 2*(strlen(state
->mask
) + 1);
739 param
= TALLOC_REALLOC_ARRAY(state
, state
->param
, uint8_t,
740 12 + nlen
+ last_name_raw
.length
+ 2);
741 if (tevent_req_nomem(param
, req
)) {
744 state
->param
= param
;
746 SSVAL(param
, 0, state
->ff_dir_handle
);
747 SSVAL(param
, 2, state
->max_matches
); /* max count */
748 SSVAL(param
, 4, state
->info_level
);
750 * For W2K servers serving out FAT filesystems we *must* set
751 * the resume key. If it's not FAT then it's returned as zero.
753 SIVAL(param
, 6, resume_key
); /* ff_resume_key */
755 * NB. *DON'T* use continue here. If you do it seems that W2K
756 * and bretheren can miss filenames. Use last filename
757 * continue instead. JRA
759 SSVAL(param
, 10, (FLAG_TRANS2_FIND_REQUIRE_RESUME
760 |FLAG_TRANS2_FIND_CLOSE_IF_END
));
761 p
= ((char *)param
)+12;
762 if (last_name_raw
.length
) {
763 memcpy(p
, last_name_raw
.data
, last_name_raw
.length
);
764 p
+= last_name_raw
.length
;
765 data_blob_free(&last_name_raw
);
767 p
+= clistr_push(state
->cli
, p
, state
->mask
, nlen
,
771 param_len
= PTR_DIFF(p
, param
);
773 subreq
= cli_trans_send(state
, state
->ev
, state
->cli
,
774 SMBtrans2
, NULL
, -1, 0, 0,
776 state
->param
, param_len
, 10,
777 NULL
, 0, state
->cli
->max_xmit
);
778 if (tevent_req_nomem(subreq
, req
)) {
781 tevent_req_set_callback(subreq
, cli_list_trans_done
, req
);
784 static NTSTATUS
cli_list_trans_recv(struct tevent_req
*req
,
786 struct file_info
**finfo
)
788 struct cli_list_trans_state
*state
= tevent_req_data(
789 req
, struct cli_list_trans_state
);
792 if (tevent_req_is_nterror(req
, &status
)) {
795 *finfo
= talloc_move(mem_ctx
, &state
->finfo
);
799 NTSTATUS
cli_list_trans(struct cli_state
*cli
, const char *mask
,
800 uint16_t attribute
, int info_level
,
801 NTSTATUS (*fn
)(const char *mnt
, struct file_info
*finfo
,
802 const char *mask
, void *private_data
),
805 TALLOC_CTX
*frame
= talloc_stackframe();
806 struct event_context
*ev
;
807 struct tevent_req
*req
;
809 struct file_info
*finfo
= NULL
;
810 NTSTATUS status
= NT_STATUS_NO_MEMORY
;
812 if (cli_has_async_calls(cli
)) {
814 * Can't use sync call while an async call is in flight
816 status
= NT_STATUS_INVALID_PARAMETER
;
819 ev
= event_context_init(frame
);
823 req
= cli_list_trans_send(frame
, ev
, cli
, mask
, attribute
, info_level
);
827 if (!tevent_req_poll_ntstatus(req
, ev
, &status
)) {
830 status
= cli_list_trans_recv(req
, frame
, &finfo
);
831 if (!NT_STATUS_IS_OK(status
)) {
834 num_finfo
= talloc_array_length(finfo
);
835 for (i
=0; i
<num_finfo
; i
++) {
836 status
= fn(cli
->dfs_mountpoint
, &finfo
[i
], mask
, private_data
);
837 if (!NT_STATUS_IS_OK(status
)) {
846 struct cli_list_state
{
847 NTSTATUS (*recv_fn
)(struct tevent_req
*req
, TALLOC_CTX
*mem_ctx
,
848 struct file_info
**finfo
);
849 struct file_info
*finfo
;
852 static void cli_list_done(struct tevent_req
*subreq
);
854 struct tevent_req
*cli_list_send(TALLOC_CTX
*mem_ctx
,
855 struct tevent_context
*ev
,
856 struct cli_state
*cli
,
861 struct tevent_req
*req
, *subreq
;
862 struct cli_list_state
*state
;
864 req
= tevent_req_create(mem_ctx
, &state
, struct cli_list_state
);
869 if (cli
->protocol
<= PROTOCOL_LANMAN1
) {
870 subreq
= cli_list_old_send(state
, ev
, cli
, mask
, attribute
);
871 state
->recv_fn
= cli_list_old_recv
;
873 subreq
= cli_list_trans_send(state
, ev
, cli
, mask
, attribute
,
875 state
->recv_fn
= cli_list_trans_recv
;
877 if (tevent_req_nomem(subreq
, req
)) {
878 return tevent_req_post(req
, ev
);
880 tevent_req_set_callback(subreq
, cli_list_done
, req
);
884 static void cli_list_done(struct tevent_req
*subreq
)
886 struct tevent_req
*req
= tevent_req_callback_data(
887 subreq
, struct tevent_req
);
888 struct cli_list_state
*state
= tevent_req_data(
889 req
, struct cli_list_state
);
892 status
= state
->recv_fn(subreq
, state
, &state
->finfo
);
894 if (!NT_STATUS_IS_OK(status
)) {
895 tevent_req_nterror(req
, status
);
898 tevent_req_done(req
);
901 NTSTATUS
cli_list_recv(struct tevent_req
*req
, TALLOC_CTX
*mem_ctx
,
902 struct file_info
**finfo
, size_t *num_finfo
)
904 struct cli_list_state
*state
= tevent_req_data(
905 req
, struct cli_list_state
);
908 if (tevent_req_is_nterror(req
, &status
)) {
911 *num_finfo
= talloc_array_length(state
->finfo
);
912 *finfo
= talloc_move(mem_ctx
, &state
->finfo
);
916 NTSTATUS
cli_list(struct cli_state
*cli
, const char *mask
, uint16 attribute
,
917 NTSTATUS (*fn
)(const char *, struct file_info
*, const char *,
918 void *), void *state
)
920 TALLOC_CTX
*frame
= talloc_stackframe();
921 struct event_context
*ev
;
922 struct tevent_req
*req
;
923 NTSTATUS status
= NT_STATUS_NO_MEMORY
;
924 struct file_info
*finfo
;
928 if (cli_has_async_calls(cli
)) {
930 * Can't use sync call while an async call is in flight
932 status
= NT_STATUS_INVALID_PARAMETER
;
935 ev
= event_context_init(frame
);
940 info_level
= (cli
->capabilities
& CAP_NT_SMBS
)
941 ? SMB_FIND_FILE_BOTH_DIRECTORY_INFO
: SMB_FIND_INFO_STANDARD
;
943 req
= cli_list_send(frame
, ev
, cli
, mask
, attribute
, info_level
);
947 if (!tevent_req_poll(req
, ev
)) {
948 status
= map_nt_error_from_unix(errno
);
952 status
= cli_list_recv(req
, frame
, &finfo
, &num_finfo
);
953 if (!NT_STATUS_IS_OK(status
)) {
957 for (i
=0; i
<num_finfo
; i
++) {
958 status
= fn(cli
->dfs_mountpoint
, &finfo
[i
], mask
, state
);
959 if (!NT_STATUS_IS_OK(status
)) {