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 Calculate a safe next_entry_offset.
29 ****************************************************************************/
31 static size_t calc_next_entry_offset(const char *base
, const char *pdata_end
)
33 size_t next_entry_offset
= (size_t)IVAL(base
,0);
35 if (next_entry_offset
== 0 ||
36 base
+ next_entry_offset
< base
||
37 base
+ next_entry_offset
> pdata_end
) {
38 next_entry_offset
= pdata_end
- base
;
40 return next_entry_offset
;
43 /****************************************************************************
44 Interpret a long filename structure - this is mostly guesses at the moment.
45 The length of the structure is returned
46 The structure of a long filename depends on the info level.
47 SMB_FIND_FILE_BOTH_DIRECTORY_INFO is used
48 by NT and SMB_FIND_EA_SIZE is used by OS/2
49 ****************************************************************************/
51 static size_t interpret_long_filename(TALLOC_CTX
*ctx
,
52 struct cli_state
*cli
,
57 const char *pdata_end
,
58 struct file_info
*finfo
,
60 DATA_BLOB
*p_last_name_raw
)
66 data_blob_free(p_last_name_raw
);
74 case SMB_FIND_INFO_STANDARD
: /* OS/2 understands this */
75 /* these dates are converted to GMT by
77 if (pdata_end
- base
< 27) {
78 return pdata_end
- base
;
80 finfo
->ctime_ts
= convert_time_t_to_timespec(
81 make_unix_date2(p
+4, smb1cli_conn_server_time_zone(cli
->conn
)));
82 finfo
->atime_ts
= convert_time_t_to_timespec(
83 make_unix_date2(p
+8, smb1cli_conn_server_time_zone(cli
->conn
)));
84 finfo
->mtime_ts
= convert_time_t_to_timespec(
85 make_unix_date2(p
+12, smb1cli_conn_server_time_zone(cli
->conn
)));
86 finfo
->size
= IVAL(p
,16);
87 finfo
->mode
= CVAL(p
,24);
90 p
+= align_string(base_ptr
, p
, 0);
92 /* We can safely use len here (which is required by OS/2)
93 * and the NAS-BASIC server instead of +2 or +1 as the
94 * STR_TERMINATE flag below is
95 * actually used as the length calculation.
96 * The len is merely an upper bound.
97 * Due to the explicit 2 byte null termination
98 * in cli_receive_trans/cli_receive_nt_trans
99 * we know this is safe. JRA + kukks
102 if (p
+ len
> pdata_end
) {
103 return pdata_end
- base
;
106 /* the len+2 below looks strange but it is
107 important to cope with the differences
108 between win2000 and win9x for this call
110 ret
= clistr_pull_talloc(ctx
,
117 if (ret
== (size_t)-1) {
118 return pdata_end
- base
;
121 return PTR_DIFF(p
, base
);
123 case SMB_FIND_EA_SIZE
: /* this is what OS/2 uses mostly */
124 /* these dates are converted to GMT by
126 if (pdata_end
- base
< 31) {
127 return pdata_end
- base
;
129 finfo
->ctime_ts
= convert_time_t_to_timespec(
130 make_unix_date2(p
+4, smb1cli_conn_server_time_zone(cli
->conn
)));
131 finfo
->atime_ts
= convert_time_t_to_timespec(
132 make_unix_date2(p
+8, smb1cli_conn_server_time_zone(cli
->conn
)));
133 finfo
->mtime_ts
= convert_time_t_to_timespec(
134 make_unix_date2(p
+12, smb1cli_conn_server_time_zone(cli
->conn
)));
135 finfo
->size
= IVAL(p
,16);
136 finfo
->mode
= CVAL(p
,24);
139 /* check for unisys! */
140 if (p
+ len
+ 1 > pdata_end
) {
141 return pdata_end
- base
;
143 ret
= clistr_pull_talloc(ctx
,
150 if (ret
== (size_t)-1) {
151 return pdata_end
- base
;
154 return PTR_DIFF(p
, base
) + 1;
156 case SMB_FIND_FILE_BOTH_DIRECTORY_INFO
: /* NT uses this, but also accepts 2 */
158 size_t namelen
, slen
;
160 if (pdata_end
- base
< 94) {
161 return pdata_end
- base
;
164 p
+= 4; /* next entry offset */
167 *p_resume_key
= IVAL(p
,0);
169 p
+= 4; /* fileindex */
171 /* Offset zero is "create time", not "change time". */
173 finfo
->atime_ts
= interpret_long_date(p
);
175 finfo
->mtime_ts
= interpret_long_date(p
);
177 finfo
->ctime_ts
= interpret_long_date(p
);
179 finfo
->size
= IVAL2_TO_SMB_BIG_UINT(p
,0);
181 p
+= 8; /* alloc size */
182 finfo
->mode
= CVAL(p
,0);
186 p
+= 4; /* EA size */
189 /* Bad short name length. */
190 return pdata_end
- base
;
193 ret
= clistr_pull_talloc(ctx
,
200 if (ret
== (size_t)-1) {
201 return pdata_end
- base
;
203 p
+= 24; /* short name? */
204 if (p
+ namelen
< p
|| p
+ namelen
> pdata_end
) {
205 return pdata_end
- base
;
207 ret
= clistr_pull_talloc(ctx
,
214 if (ret
== (size_t)-1) {
215 return pdata_end
- base
;
218 /* To be robust in the face of unicode conversion failures
219 we need to copy the raw bytes of the last name seen here.
220 Namelen doesn't include the terminating unicode null, so
223 if (p_last_name_raw
) {
224 *p_last_name_raw
= data_blob(NULL
, namelen
+2);
225 memcpy(p_last_name_raw
->data
, p
, namelen
);
226 SSVAL(p_last_name_raw
->data
, namelen
, 0);
228 return calc_next_entry_offset(base
, pdata_end
);
232 DEBUG(1,("Unknown long filename format %d\n",level
));
233 return calc_next_entry_offset(base
, pdata_end
);
236 /****************************************************************************
237 Interpret a short filename structure.
238 The length of the structure is returned.
239 ****************************************************************************/
241 static bool interpret_short_filename(TALLOC_CTX
*ctx
,
242 struct cli_state
*cli
,
244 struct file_info
*finfo
)
249 finfo
->mode
= CVAL(p
,21);
251 /* this date is converted to GMT by make_unix_date */
252 finfo
->ctime_ts
.tv_sec
= make_unix_date(p
+22, smb1cli_conn_server_time_zone(cli
->conn
));
253 finfo
->ctime_ts
.tv_nsec
= 0;
254 finfo
->mtime_ts
.tv_sec
= finfo
->atime_ts
.tv_sec
= finfo
->ctime_ts
.tv_sec
;
255 finfo
->mtime_ts
.tv_nsec
= finfo
->atime_ts
.tv_nsec
= 0;
256 finfo
->size
= IVAL(p
,26);
257 ret
= clistr_pull_talloc(ctx
,
264 if (ret
== (size_t)-1) {
269 finfo
->short_name
= talloc_strdup(ctx
, finfo
->name
);
270 if (finfo
->short_name
== NULL
) {
277 struct cli_list_old_state
{
278 struct tevent_context
*ev
;
279 struct cli_state
*cli
;
284 uint8_t search_status
[23];
290 static void cli_list_old_done(struct tevent_req
*subreq
);
292 static struct tevent_req
*cli_list_old_send(TALLOC_CTX
*mem_ctx
,
293 struct tevent_context
*ev
,
294 struct cli_state
*cli
,
298 struct tevent_req
*req
, *subreq
;
299 struct cli_list_old_state
*state
;
301 static const uint16_t zero
= 0;
302 uint32_t usable_space
;
304 req
= tevent_req_create(mem_ctx
, &state
, struct cli_list_old_state
);
310 state
->attribute
= attribute
;
312 state
->mask
= talloc_strdup(state
, mask
);
313 if (tevent_req_nomem(state
->mask
, req
)) {
314 return tevent_req_post(req
, ev
);
316 usable_space
= cli_state_available_size(cli
, 100);
317 state
->num_asked
= usable_space
/ DIR_STRUCT_SIZE
;
319 SSVAL(state
->vwv
+ 0, 0, state
->num_asked
);
320 SSVAL(state
->vwv
+ 1, 0, state
->attribute
);
322 bytes
= talloc_array(state
, uint8_t, 1);
323 if (tevent_req_nomem(bytes
, req
)) {
324 return tevent_req_post(req
, ev
);
327 bytes
= smb_bytes_push_str(bytes
, smbXcli_conn_use_unicode(cli
->conn
), mask
,
328 strlen(mask
)+1, NULL
);
330 bytes
= smb_bytes_push_bytes(bytes
, 5, (const uint8_t *)&zero
, 2);
331 if (tevent_req_nomem(bytes
, req
)) {
332 return tevent_req_post(req
, ev
);
335 subreq
= cli_smb_send(state
, state
->ev
, state
->cli
, SMBsearch
,
336 0, 2, state
->vwv
, talloc_get_size(bytes
), bytes
);
337 if (tevent_req_nomem(subreq
, req
)) {
338 return tevent_req_post(req
, ev
);
340 tevent_req_set_callback(subreq
, cli_list_old_done
, req
);
344 static void cli_list_old_done(struct tevent_req
*subreq
)
346 struct tevent_req
*req
= tevent_req_callback_data(
347 subreq
, struct tevent_req
);
348 struct cli_list_old_state
*state
= tevent_req_data(
349 req
, struct cli_list_old_state
);
360 status
= cli_smb_recv(subreq
, state
, NULL
, 0, &wct
, &vwv
, &num_bytes
,
362 if (!NT_STATUS_IS_OK(status
)
363 && !NT_STATUS_EQUAL(status
, NT_STATUS_DOS(ERRDOS
, ERRnofiles
))
364 && !NT_STATUS_EQUAL(status
, STATUS_NO_MORE_FILES
)) {
366 tevent_req_nterror(req
, status
);
369 if (NT_STATUS_EQUAL(status
, NT_STATUS_DOS(ERRDOS
, ERRnofiles
))
370 || NT_STATUS_EQUAL(status
, STATUS_NO_MORE_FILES
)) {
376 req
, NT_STATUS_INVALID_NETWORK_RESPONSE
);
379 received
= SVAL(vwv
+ 0, 0);
384 * I don't think this can wrap. received is
385 * initialized from a 16-bit value.
387 if (num_bytes
< (received
* DIR_STRUCT_SIZE
+ 3)) {
390 req
, NT_STATUS_INVALID_NETWORK_RESPONSE
);
394 dirlist_len
= talloc_get_size(state
->dirlist
);
396 tmp
= talloc_realloc(
397 state
, state
->dirlist
, uint8_t,
398 dirlist_len
+ received
* DIR_STRUCT_SIZE
);
399 if (tevent_req_nomem(tmp
, req
)) {
402 state
->dirlist
= tmp
;
403 memcpy(state
->dirlist
+ dirlist_len
, bytes
+ 3,
404 received
* DIR_STRUCT_SIZE
);
406 SSVAL(state
->search_status
, 0, 21);
407 memcpy(state
->search_status
+ 2,
408 bytes
+ 3 + (received
-1)*DIR_STRUCT_SIZE
, 21);
411 if (state
->first
|| state
->done
) {
412 tevent_req_done(req
);
416 state
->num_asked
= 0;
421 state
->first
= false;
423 SSVAL(state
->vwv
+ 0, 0, state
->num_asked
);
424 SSVAL(state
->vwv
+ 1, 0, state
->attribute
);
426 bytes
= talloc_array(state
, uint8_t, 1);
427 if (tevent_req_nomem(bytes
, req
)) {
431 bytes
= smb_bytes_push_str(bytes
, smbXcli_conn_use_unicode(state
->cli
->conn
), "",
433 bytes
= smb_bytes_push_bytes(bytes
, 5, state
->search_status
,
434 sizeof(state
->search_status
));
435 if (tevent_req_nomem(bytes
, req
)) {
438 subreq
= cli_smb_send(state
, state
->ev
, state
->cli
, cmd
, 0,
439 2, state
->vwv
, talloc_get_size(bytes
), bytes
);
440 if (tevent_req_nomem(subreq
, req
)) {
443 tevent_req_set_callback(subreq
, cli_list_old_done
, req
);
446 static NTSTATUS
cli_list_old_recv(struct tevent_req
*req
, TALLOC_CTX
*mem_ctx
,
447 struct file_info
**pfinfo
)
449 struct cli_list_old_state
*state
= tevent_req_data(
450 req
, struct cli_list_old_state
);
452 size_t i
, num_received
;
453 struct file_info
*finfo
;
455 if (tevent_req_is_nterror(req
, &status
)) {
459 num_received
= talloc_array_length(state
->dirlist
) / DIR_STRUCT_SIZE
;
461 finfo
= talloc_array(mem_ctx
, struct file_info
, num_received
);
463 return NT_STATUS_NO_MEMORY
;
466 for (i
=0; i
<num_received
; i
++) {
467 if (!interpret_short_filename(
469 (char *)state
->dirlist
+ i
* DIR_STRUCT_SIZE
,
472 return NT_STATUS_NO_MEMORY
;
479 NTSTATUS
cli_list_old(struct cli_state
*cli
, const char *mask
,
481 NTSTATUS (*fn
)(const char *, struct file_info
*,
482 const char *, void *), void *state
)
484 TALLOC_CTX
*frame
= talloc_stackframe();
485 struct event_context
*ev
;
486 struct tevent_req
*req
;
487 NTSTATUS status
= NT_STATUS_NO_MEMORY
;
488 struct file_info
*finfo
;
491 if (smbXcli_conn_has_async_calls(cli
->conn
)) {
493 * Can't use sync call while an async call is in flight
495 status
= NT_STATUS_INVALID_PARAMETER
;
498 ev
= event_context_init(frame
);
502 req
= cli_list_old_send(frame
, ev
, cli
, mask
, attribute
);
506 if (!tevent_req_poll(req
, ev
)) {
507 status
= map_nt_error_from_unix(errno
);
510 status
= cli_list_old_recv(req
, frame
, &finfo
);
511 if (!NT_STATUS_IS_OK(status
)) {
514 num_finfo
= talloc_array_length(finfo
);
515 for (i
=0; i
<num_finfo
; i
++) {
516 status
= fn(cli
->dfs_mountpoint
, &finfo
[i
], mask
, state
);
517 if (!NT_STATUS_IS_OK(status
)) {
526 struct cli_list_trans_state
{
527 struct tevent_context
*ev
;
528 struct cli_state
*cli
;
535 uint16_t max_matches
;
544 struct file_info
*finfo
;
547 static void cli_list_trans_done(struct tevent_req
*subreq
);
549 static struct tevent_req
*cli_list_trans_send(TALLOC_CTX
*mem_ctx
,
550 struct tevent_context
*ev
,
551 struct cli_state
*cli
,
556 struct tevent_req
*req
, *subreq
;
557 struct cli_list_trans_state
*state
;
560 req
= tevent_req_create(mem_ctx
, &state
,
561 struct cli_list_trans_state
);
567 state
->mask
= talloc_strdup(state
, mask
);
568 if (tevent_req_nomem(state
->mask
, req
)) {
569 return tevent_req_post(req
, ev
);
571 state
->attribute
= attribute
;
572 state
->info_level
= info_level
;
573 state
->loop_count
= 0;
576 state
->max_matches
= 1366; /* Match W2k */
578 SSVAL(&state
->setup
[0], 0, TRANSACT2_FINDFIRST
);
580 state
->param
= talloc_array(state
, uint8_t, 12);
581 if (tevent_req_nomem(state
->param
, req
)) {
582 return tevent_req_post(req
, ev
);
585 SSVAL(state
->param
, 0, state
->attribute
);
586 SSVAL(state
->param
, 2, state
->max_matches
);
587 SSVAL(state
->param
, 4,
588 FLAG_TRANS2_FIND_REQUIRE_RESUME
589 |FLAG_TRANS2_FIND_CLOSE_IF_END
590 |(cli
->backup_intent
? FLAG_TRANS2_FIND_BACKUP_INTENT
: 0));
591 SSVAL(state
->param
, 6, state
->info_level
);
592 SIVAL(state
->param
, 8, 0);
594 state
->param
= trans2_bytes_push_str(state
->param
, smbXcli_conn_use_unicode(cli
->conn
),
595 state
->mask
, strlen(state
->mask
)+1,
597 if (tevent_req_nomem(state
->param
, req
)) {
598 return tevent_req_post(req
, ev
);
600 param_len
= talloc_get_size(state
->param
);
602 subreq
= cli_trans_send(state
, state
->ev
, state
->cli
,
603 SMBtrans2
, NULL
, -1, 0, 0,
605 state
->param
, param_len
, 10,
606 NULL
, 0, CLI_BUFFER_SIZE
);
607 if (tevent_req_nomem(subreq
, req
)) {
608 return tevent_req_post(req
, ev
);
610 tevent_req_set_callback(subreq
, cli_list_trans_done
, req
);
614 static void cli_list_trans_done(struct tevent_req
*subreq
)
616 struct tevent_req
*req
= tevent_req_callback_data(
617 subreq
, struct tevent_req
);
618 struct cli_list_trans_state
*state
= tevent_req_data(
619 req
, struct cli_list_trans_state
);
627 struct file_info
*tmp
;
628 size_t old_num_finfo
;
629 uint16_t recv_flags2
;
633 uint32_t resume_key
= 0;
635 DATA_BLOB last_name_raw
;
636 struct file_info
*finfo
= NULL
;
639 min_param
= (state
->first
? 6 : 4);
641 status
= cli_trans_recv(subreq
, talloc_tos(), &recv_flags2
,
643 ¶m
, min_param
, &num_param
,
644 &data
, 0, &num_data
);
646 if (!NT_STATUS_IS_OK(status
)) {
648 * TODO: retry, OS/2 nofiles
650 tevent_req_nterror(req
, status
);
655 state
->ff_dir_handle
= SVAL(param
, 0);
656 ff_searchcount
= SVAL(param
, 2);
657 ff_eos
= SVAL(param
, 4) != 0;
659 ff_searchcount
= SVAL(param
, 0);
660 ff_eos
= SVAL(param
, 2) != 0;
663 old_num_finfo
= talloc_array_length(state
->finfo
);
665 tmp
= talloc_realloc(state
, state
->finfo
, struct file_info
,
666 old_num_finfo
+ ff_searchcount
);
667 if (tevent_req_nomem(tmp
, req
)) {
672 p2
= p
= (char *)data
;
673 data_end
= (char *)data
+ num_data
;
674 last_name_raw
= data_blob_null
;
676 for (i
=0; i
<ff_searchcount
; i
++) {
677 if (p2
>= data_end
) {
681 if ((state
->info_level
== SMB_FIND_FILE_BOTH_DIRECTORY_INFO
)
682 && (i
== ff_searchcount
-1)) {
683 /* Last entry - fixup the last offset length. */
684 SIVAL(p2
, 0, PTR_DIFF((data
+ num_data
), p2
));
687 data_blob_free(&last_name_raw
);
689 finfo
= &state
->finfo
[old_num_finfo
+ i
];
691 p2
+= interpret_long_filename(
692 state
->finfo
, /* Stick fname to the array as such */
693 state
->cli
, state
->info_level
,
694 (char *)data
, recv_flags2
, p2
,
695 data_end
, finfo
, &resume_key
, &last_name_raw
);
697 if (finfo
->name
== NULL
) {
698 DEBUG(1, ("cli_list: Error: unable to parse name from "
699 "info level %d\n", state
->info_level
));
703 if (!state
->first
&& (state
->mask
[0] != '\0') &&
704 strcsequal(finfo
->name
, state
->mask
)) {
705 DEBUG(1, ("Error: Looping in FIND_NEXT as name %s has "
706 "already been seen?\n", finfo
->name
));
712 if (ff_searchcount
== 0) {
720 * Shrink state->finfo to the real length we received
722 tmp
= talloc_realloc(state
, state
->finfo
, struct file_info
,
724 if (tevent_req_nomem(tmp
, req
)) {
729 state
->first
= false;
732 data_blob_free(&last_name_raw
);
733 tevent_req_done(req
);
737 TALLOC_FREE(state
->mask
);
738 state
->mask
= talloc_strdup(state
, finfo
->name
);
739 if (tevent_req_nomem(state
->mask
, req
)) {
743 SSVAL(&state
->setup
[0], 0, TRANSACT2_FINDNEXT
);
745 param
= talloc_realloc(state
, state
->param
, uint8_t, 12);
746 if (tevent_req_nomem(param
, req
)) {
749 state
->param
= param
;
751 SSVAL(param
, 0, state
->ff_dir_handle
);
752 SSVAL(param
, 2, state
->max_matches
); /* max count */
753 SSVAL(param
, 4, state
->info_level
);
755 * For W2K servers serving out FAT filesystems we *must* set
756 * the resume key. If it's not FAT then it's returned as zero.
758 SIVAL(param
, 6, resume_key
); /* ff_resume_key */
760 * NB. *DON'T* use continue here. If you do it seems that W2K
761 * and bretheren can miss filenames. Use last filename
762 * continue instead. JRA
764 SSVAL(param
, 10, (FLAG_TRANS2_FIND_REQUIRE_RESUME
765 |FLAG_TRANS2_FIND_CLOSE_IF_END
766 |(state
->cli
->backup_intent
? FLAG_TRANS2_FIND_BACKUP_INTENT
: 0)));
767 if (last_name_raw
.length
) {
768 state
->param
= trans2_bytes_push_bytes(state
->param
,
770 last_name_raw
.length
);
771 if (tevent_req_nomem(state
->param
, req
)) {
774 data_blob_free(&last_name_raw
);
776 state
->param
= trans2_bytes_push_str(state
->param
,
777 smbXcli_conn_use_unicode(state
->cli
->conn
),
779 strlen(state
->mask
)+1,
781 if (tevent_req_nomem(state
->param
, req
)) {
785 param_len
= talloc_get_size(state
->param
);
787 subreq
= cli_trans_send(state
, state
->ev
, state
->cli
,
788 SMBtrans2
, NULL
, -1, 0, 0,
790 state
->param
, param_len
, 10,
791 NULL
, 0, CLI_BUFFER_SIZE
);
792 if (tevent_req_nomem(subreq
, req
)) {
795 tevent_req_set_callback(subreq
, cli_list_trans_done
, req
);
798 static NTSTATUS
cli_list_trans_recv(struct tevent_req
*req
,
800 struct file_info
**finfo
)
802 struct cli_list_trans_state
*state
= tevent_req_data(
803 req
, struct cli_list_trans_state
);
806 if (tevent_req_is_nterror(req
, &status
)) {
809 *finfo
= talloc_move(mem_ctx
, &state
->finfo
);
813 NTSTATUS
cli_list_trans(struct cli_state
*cli
, const char *mask
,
814 uint16_t attribute
, int info_level
,
815 NTSTATUS (*fn
)(const char *mnt
, struct file_info
*finfo
,
816 const char *mask
, void *private_data
),
819 TALLOC_CTX
*frame
= talloc_stackframe();
820 struct event_context
*ev
;
821 struct tevent_req
*req
;
823 struct file_info
*finfo
= NULL
;
824 NTSTATUS status
= NT_STATUS_NO_MEMORY
;
826 if (smbXcli_conn_has_async_calls(cli
->conn
)) {
828 * Can't use sync call while an async call is in flight
830 status
= NT_STATUS_INVALID_PARAMETER
;
833 ev
= event_context_init(frame
);
837 req
= cli_list_trans_send(frame
, ev
, cli
, mask
, attribute
, info_level
);
841 if (!tevent_req_poll_ntstatus(req
, ev
, &status
)) {
844 status
= cli_list_trans_recv(req
, frame
, &finfo
);
845 if (!NT_STATUS_IS_OK(status
)) {
848 num_finfo
= talloc_array_length(finfo
);
849 for (i
=0; i
<num_finfo
; i
++) {
850 status
= fn(cli
->dfs_mountpoint
, &finfo
[i
], mask
, private_data
);
851 if (!NT_STATUS_IS_OK(status
)) {
860 struct cli_list_state
{
861 NTSTATUS (*recv_fn
)(struct tevent_req
*req
, TALLOC_CTX
*mem_ctx
,
862 struct file_info
**finfo
);
863 struct file_info
*finfo
;
866 static void cli_list_done(struct tevent_req
*subreq
);
868 struct tevent_req
*cli_list_send(TALLOC_CTX
*mem_ctx
,
869 struct tevent_context
*ev
,
870 struct cli_state
*cli
,
875 struct tevent_req
*req
, *subreq
;
876 struct cli_list_state
*state
;
878 req
= tevent_req_create(mem_ctx
, &state
, struct cli_list_state
);
883 if (smbXcli_conn_protocol(cli
->conn
) <= PROTOCOL_LANMAN1
) {
884 subreq
= cli_list_old_send(state
, ev
, cli
, mask
, attribute
);
885 state
->recv_fn
= cli_list_old_recv
;
887 subreq
= cli_list_trans_send(state
, ev
, cli
, mask
, attribute
,
889 state
->recv_fn
= cli_list_trans_recv
;
891 if (tevent_req_nomem(subreq
, req
)) {
892 return tevent_req_post(req
, ev
);
894 tevent_req_set_callback(subreq
, cli_list_done
, req
);
898 static void cli_list_done(struct tevent_req
*subreq
)
900 struct tevent_req
*req
= tevent_req_callback_data(
901 subreq
, struct tevent_req
);
902 struct cli_list_state
*state
= tevent_req_data(
903 req
, struct cli_list_state
);
906 status
= state
->recv_fn(subreq
, state
, &state
->finfo
);
908 if (!NT_STATUS_IS_OK(status
)) {
909 tevent_req_nterror(req
, status
);
912 tevent_req_done(req
);
915 NTSTATUS
cli_list_recv(struct tevent_req
*req
, TALLOC_CTX
*mem_ctx
,
916 struct file_info
**finfo
, size_t *num_finfo
)
918 struct cli_list_state
*state
= tevent_req_data(
919 req
, struct cli_list_state
);
922 if (tevent_req_is_nterror(req
, &status
)) {
925 *num_finfo
= talloc_array_length(state
->finfo
);
926 *finfo
= talloc_move(mem_ctx
, &state
->finfo
);
930 NTSTATUS
cli_list(struct cli_state
*cli
, const char *mask
, uint16 attribute
,
931 NTSTATUS (*fn
)(const char *, struct file_info
*, const char *,
932 void *), void *state
)
934 TALLOC_CTX
*frame
= talloc_stackframe();
935 struct event_context
*ev
;
936 struct tevent_req
*req
;
937 NTSTATUS status
= NT_STATUS_NO_MEMORY
;
938 struct file_info
*finfo
;
942 if (smbXcli_conn_has_async_calls(cli
->conn
)) {
944 * Can't use sync call while an async call is in flight
946 status
= NT_STATUS_INVALID_PARAMETER
;
949 ev
= event_context_init(frame
);
954 info_level
= (smb1cli_conn_capabilities(cli
->conn
) & CAP_NT_SMBS
)
955 ? SMB_FIND_FILE_BOTH_DIRECTORY_INFO
: SMB_FIND_INFO_STANDARD
;
957 req
= cli_list_send(frame
, ev
, cli
, mask
, attribute
, info_level
);
961 if (!tevent_req_poll(req
, ev
)) {
962 status
= map_nt_error_from_unix(errno
);
966 status
= cli_list_recv(req
, frame
, &finfo
, &num_finfo
);
967 if (!NT_STATUS_IS_OK(status
)) {
971 for (i
=0; i
<num_finfo
; i
++) {
972 status
= fn(cli
->dfs_mountpoint
, &finfo
[i
], mask
, state
);
973 if (!NT_STATUS_IS_OK(status
)) {