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
)) {
517 if (!NT_STATUS_IS_OK(status
)) {
518 cli_set_error(cli
, status
);
523 struct cli_list_trans_state
{
524 struct tevent_context
*ev
;
525 struct cli_state
*cli
;
532 uint16_t max_matches
;
541 struct file_info
*finfo
;
544 static void cli_list_trans_done(struct tevent_req
*subreq
);
546 static struct tevent_req
*cli_list_trans_send(TALLOC_CTX
*mem_ctx
,
547 struct tevent_context
*ev
,
548 struct cli_state
*cli
,
553 struct tevent_req
*req
, *subreq
;
554 struct cli_list_trans_state
*state
;
555 size_t nlen
, param_len
;
558 req
= tevent_req_create(mem_ctx
, &state
,
559 struct cli_list_trans_state
);
565 state
->mask
= talloc_strdup(state
, mask
);
566 if (tevent_req_nomem(state
->mask
, req
)) {
567 return tevent_req_post(req
, ev
);
569 state
->attribute
= attribute
;
570 state
->info_level
= info_level
;
571 state
->loop_count
= 0;
574 state
->max_matches
= 1366; /* Match W2k */
576 SSVAL(&state
->setup
[0], 0, TRANSACT2_FINDFIRST
);
578 nlen
= 2*(strlen(mask
)+1);
579 state
->param
= TALLOC_ARRAY(state
, uint8_t, 12+nlen
+2);
580 if (tevent_req_nomem(state
->param
, req
)) {
581 return tevent_req_post(req
, ev
);
584 SSVAL(state
->param
, 0, state
->attribute
);
585 SSVAL(state
->param
, 2, state
->max_matches
);
586 SSVAL(state
->param
, 4,
587 FLAG_TRANS2_FIND_REQUIRE_RESUME
588 |FLAG_TRANS2_FIND_CLOSE_IF_END
);
589 SSVAL(state
->param
, 6, state
->info_level
);
590 SIVAL(state
->param
, 8, 0);
592 p
= ((char *)state
->param
)+12;
593 p
+= clistr_push(state
->cli
, p
, state
->mask
, nlen
,
595 param_len
= PTR_DIFF(p
, state
->param
);
597 subreq
= cli_trans_send(state
, state
->ev
, state
->cli
,
598 SMBtrans2
, NULL
, -1, 0, 0,
600 state
->param
, param_len
, 10,
601 NULL
, 0, cli
->max_xmit
);
602 if (tevent_req_nomem(subreq
, req
)) {
603 return tevent_req_post(req
, ev
);
605 tevent_req_set_callback(subreq
, cli_list_trans_done
, req
);
609 static void cli_list_trans_done(struct tevent_req
*subreq
)
611 struct tevent_req
*req
= tevent_req_callback_data(
612 subreq
, struct tevent_req
);
613 struct cli_list_trans_state
*state
= tevent_req_data(
614 req
, struct cli_list_trans_state
);
622 struct file_info
*tmp
;
623 size_t old_num_finfo
;
624 uint16_t recv_flags2
;
628 uint32_t resume_key
= 0;
630 DATA_BLOB last_name_raw
;
631 struct file_info
*finfo
= NULL
;
632 size_t nlen
, param_len
;
634 min_param
= (state
->first
? 6 : 4);
636 status
= cli_trans_recv(subreq
, talloc_tos(), &recv_flags2
,
638 ¶m
, min_param
, &num_param
,
639 &data
, 0, &num_data
);
641 if (!NT_STATUS_IS_OK(status
)) {
643 * TODO: retry, OS/2 nofiles
645 tevent_req_nterror(req
, status
);
650 state
->ff_dir_handle
= SVAL(param
, 0);
651 ff_searchcount
= SVAL(param
, 2);
652 ff_eos
= SVAL(param
, 4) != 0;
654 ff_searchcount
= SVAL(param
, 0);
655 ff_eos
= SVAL(param
, 2) != 0;
658 old_num_finfo
= talloc_array_length(state
->finfo
);
660 tmp
= TALLOC_REALLOC_ARRAY(state
, state
->finfo
, struct file_info
,
661 old_num_finfo
+ ff_searchcount
);
662 if (tevent_req_nomem(tmp
, req
)) {
667 p2
= p
= (char *)data
;
668 data_end
= (char *)data
+ num_data
;
669 last_name_raw
= data_blob_null
;
671 for (i
=0; i
<ff_searchcount
; i
++) {
672 if (p2
>= data_end
) {
676 if ((state
->info_level
== SMB_FIND_FILE_BOTH_DIRECTORY_INFO
)
677 && (i
== ff_searchcount
-1)) {
678 /* Last entry - fixup the last offset length. */
679 SIVAL(p2
, 0, PTR_DIFF((data
+ num_data
), p2
));
682 data_blob_free(&last_name_raw
);
684 finfo
= &state
->finfo
[old_num_finfo
+ i
];
686 p2
+= interpret_long_filename(
687 state
->finfo
, /* Stick fname to the array as such */
688 state
->cli
, state
->info_level
,
689 (char *)data
, recv_flags2
, p2
,
690 data_end
, finfo
, &resume_key
, &last_name_raw
);
692 if (finfo
->name
== NULL
) {
693 DEBUG(1, ("cli_list: Error: unable to parse name from "
694 "info level %d\n", state
->info_level
));
698 if (!state
->first
&& (state
->mask
[0] != '\0') &&
699 strcsequal(finfo
->name
, state
->mask
)) {
700 DEBUG(1, ("Error: Looping in FIND_NEXT as name %s has "
701 "already been seen?\n", finfo
->name
));
707 if (ff_searchcount
== 0) {
715 * Shrink state->finfo to the real length we received
717 tmp
= TALLOC_REALLOC_ARRAY(state
, state
->finfo
, struct file_info
,
719 if (tevent_req_nomem(tmp
, req
)) {
724 state
->first
= false;
727 data_blob_free(&last_name_raw
);
728 tevent_req_done(req
);
732 TALLOC_FREE(state
->mask
);
733 state
->mask
= talloc_strdup(state
, finfo
->name
);
734 if (tevent_req_nomem(state
->mask
, req
)) {
738 SSVAL(&state
->setup
[0], 0, TRANSACT2_FINDNEXT
);
740 nlen
= 2*(strlen(state
->mask
) + 1);
742 param
= TALLOC_REALLOC_ARRAY(state
, state
->param
, uint8_t,
743 12 + nlen
+ last_name_raw
.length
+ 2);
744 if (tevent_req_nomem(param
, req
)) {
747 state
->param
= param
;
749 SSVAL(param
, 0, state
->ff_dir_handle
);
750 SSVAL(param
, 2, state
->max_matches
); /* max count */
751 SSVAL(param
, 4, state
->info_level
);
753 * For W2K servers serving out FAT filesystems we *must* set
754 * the resume key. If it's not FAT then it's returned as zero.
756 SIVAL(param
, 6, resume_key
); /* ff_resume_key */
758 * NB. *DON'T* use continue here. If you do it seems that W2K
759 * and bretheren can miss filenames. Use last filename
760 * continue instead. JRA
762 SSVAL(param
, 10, (FLAG_TRANS2_FIND_REQUIRE_RESUME
763 |FLAG_TRANS2_FIND_CLOSE_IF_END
));
764 p
= ((char *)param
)+12;
765 if (last_name_raw
.length
) {
766 memcpy(p
, last_name_raw
.data
, last_name_raw
.length
);
767 p
+= last_name_raw
.length
;
768 data_blob_free(&last_name_raw
);
770 p
+= clistr_push(state
->cli
, p
, state
->mask
, nlen
,
774 param_len
= PTR_DIFF(p
, param
);
776 subreq
= cli_trans_send(state
, state
->ev
, state
->cli
,
777 SMBtrans2
, NULL
, -1, 0, 0,
779 state
->param
, param_len
, 10,
780 NULL
, 0, state
->cli
->max_xmit
);
781 if (tevent_req_nomem(subreq
, req
)) {
784 tevent_req_set_callback(subreq
, cli_list_trans_done
, req
);
787 static NTSTATUS
cli_list_trans_recv(struct tevent_req
*req
,
789 struct file_info
**finfo
)
791 struct cli_list_trans_state
*state
= tevent_req_data(
792 req
, struct cli_list_trans_state
);
795 if (tevent_req_is_nterror(req
, &status
)) {
798 *finfo
= talloc_move(mem_ctx
, &state
->finfo
);
802 NTSTATUS
cli_list_trans(struct cli_state
*cli
, const char *mask
,
803 uint16_t attribute
, int info_level
,
804 NTSTATUS (*fn
)(const char *mnt
, struct file_info
*finfo
,
805 const char *mask
, void *private_data
),
808 TALLOC_CTX
*frame
= talloc_stackframe();
809 struct event_context
*ev
;
810 struct tevent_req
*req
;
812 struct file_info
*finfo
= NULL
;
813 NTSTATUS status
= NT_STATUS_NO_MEMORY
;
815 if (cli_has_async_calls(cli
)) {
817 * Can't use sync call while an async call is in flight
819 status
= NT_STATUS_INVALID_PARAMETER
;
822 ev
= event_context_init(frame
);
826 req
= cli_list_trans_send(frame
, ev
, cli
, mask
, attribute
, info_level
);
830 if (!tevent_req_poll_ntstatus(req
, ev
, &status
)) {
833 status
= cli_list_trans_recv(req
, frame
, &finfo
);
834 if (!NT_STATUS_IS_OK(status
)) {
837 num_finfo
= talloc_array_length(finfo
);
838 for (i
=0; i
<num_finfo
; i
++) {
839 status
= fn(cli
->dfs_mountpoint
, &finfo
[i
], mask
, private_data
);
840 if (!NT_STATUS_IS_OK(status
)) {
846 if (!NT_STATUS_IS_OK(status
)) {
847 cli_set_error(cli
, status
);
852 struct cli_list_state
{
853 NTSTATUS (*recv_fn
)(struct tevent_req
*req
, TALLOC_CTX
*mem_ctx
,
854 struct file_info
**finfo
);
855 struct file_info
*finfo
;
858 static void cli_list_done(struct tevent_req
*subreq
);
860 struct tevent_req
*cli_list_send(TALLOC_CTX
*mem_ctx
,
861 struct tevent_context
*ev
,
862 struct cli_state
*cli
,
867 struct tevent_req
*req
, *subreq
;
868 struct cli_list_state
*state
;
870 req
= tevent_req_create(mem_ctx
, &state
, struct cli_list_state
);
875 if (cli
->protocol
<= PROTOCOL_LANMAN1
) {
876 subreq
= cli_list_old_send(state
, ev
, cli
, mask
, attribute
);
877 state
->recv_fn
= cli_list_old_recv
;
879 subreq
= cli_list_trans_send(state
, ev
, cli
, mask
, attribute
,
881 state
->recv_fn
= cli_list_trans_recv
;
883 if (tevent_req_nomem(subreq
, req
)) {
884 return tevent_req_post(req
, ev
);
886 tevent_req_set_callback(subreq
, cli_list_done
, req
);
890 static void cli_list_done(struct tevent_req
*subreq
)
892 struct tevent_req
*req
= tevent_req_callback_data(
893 subreq
, struct tevent_req
);
894 struct cli_list_state
*state
= tevent_req_data(
895 req
, struct cli_list_state
);
898 status
= state
->recv_fn(subreq
, state
, &state
->finfo
);
900 if (!NT_STATUS_IS_OK(status
)) {
901 tevent_req_nterror(req
, status
);
904 tevent_req_done(req
);
907 NTSTATUS
cli_list_recv(struct tevent_req
*req
, TALLOC_CTX
*mem_ctx
,
908 struct file_info
**finfo
, size_t *num_finfo
)
910 struct cli_list_state
*state
= tevent_req_data(
911 req
, struct cli_list_state
);
914 if (tevent_req_is_nterror(req
, &status
)) {
917 *num_finfo
= talloc_array_length(state
->finfo
);
918 *finfo
= talloc_move(mem_ctx
, &state
->finfo
);
922 NTSTATUS
cli_list(struct cli_state
*cli
, const char *mask
, uint16 attribute
,
923 NTSTATUS (*fn
)(const char *, struct file_info
*, const char *,
924 void *), void *state
)
926 TALLOC_CTX
*frame
= talloc_stackframe();
927 struct event_context
*ev
;
928 struct tevent_req
*req
;
929 NTSTATUS status
= NT_STATUS_NO_MEMORY
;
930 struct file_info
*finfo
;
934 if (cli_has_async_calls(cli
)) {
936 * Can't use sync call while an async call is in flight
938 status
= NT_STATUS_INVALID_PARAMETER
;
941 ev
= event_context_init(frame
);
946 info_level
= (cli
->capabilities
& CAP_NT_SMBS
)
947 ? SMB_FIND_FILE_BOTH_DIRECTORY_INFO
: SMB_FIND_INFO_STANDARD
;
949 req
= cli_list_send(frame
, ev
, cli
, mask
, attribute
, info_level
);
953 if (!tevent_req_poll(req
, ev
)) {
954 status
= map_nt_error_from_unix(errno
);
958 status
= cli_list_recv(req
, frame
, &finfo
, &num_finfo
);
959 if (!NT_STATUS_IS_OK(status
)) {
963 for (i
=0; i
<num_finfo
; i
++) {
964 status
= fn(cli
->dfs_mountpoint
, &finfo
[i
], mask
, state
);
965 if (!NT_STATUS_IS_OK(status
)) {
971 if (!NT_STATUS_IS_OK(status
)) {
972 cli_set_error(cli
, status
);