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_state_server_time_zone(cli
)));
81 finfo
->atime_ts
= convert_time_t_to_timespec(
82 make_unix_date2(p
+8, cli_state_server_time_zone(cli
)));
83 finfo
->mtime_ts
= convert_time_t_to_timespec(
84 make_unix_date2(p
+12, cli_state_server_time_zone(cli
)));
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_state_server_time_zone(cli
)));
130 finfo
->atime_ts
= convert_time_t_to_timespec(
131 make_unix_date2(p
+8, cli_state_server_time_zone(cli
)));
132 finfo
->mtime_ts
= convert_time_t_to_timespec(
133 make_unix_date2(p
+12, cli_state_server_time_zone(cli
)));
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
;
192 ret
= clistr_pull_talloc(ctx
,
199 if (ret
== (size_t)-1) {
200 return pdata_end
- base
;
202 p
+= 24; /* short name? */
203 if (p
+ namelen
< p
|| p
+ namelen
> pdata_end
) {
204 return pdata_end
- base
;
206 ret
= clistr_pull_talloc(ctx
,
213 if (ret
== (size_t)-1) {
214 return pdata_end
- base
;
217 /* To be robust in the face of unicode conversion failures
218 we need to copy the raw bytes of the last name seen here.
219 Namelen doesn't include the terminating unicode null, so
222 if (p_last_name_raw
) {
223 *p_last_name_raw
= data_blob(NULL
, namelen
+2);
224 memcpy(p_last_name_raw
->data
, p
, namelen
);
225 SSVAL(p_last_name_raw
->data
, namelen
, 0);
227 return calc_next_entry_offset(base
, pdata_end
);
231 DEBUG(1,("Unknown long filename format %d\n",level
));
232 return calc_next_entry_offset(base
, pdata_end
);
235 /****************************************************************************
236 Interpret a short filename structure.
237 The length of the structure is returned.
238 ****************************************************************************/
240 static bool interpret_short_filename(TALLOC_CTX
*ctx
,
241 struct cli_state
*cli
,
243 struct file_info
*finfo
)
248 finfo
->mode
= CVAL(p
,21);
250 /* this date is converted to GMT by make_unix_date */
251 finfo
->ctime_ts
.tv_sec
= make_unix_date(p
+22, cli_state_server_time_zone(cli
));
252 finfo
->ctime_ts
.tv_nsec
= 0;
253 finfo
->mtime_ts
.tv_sec
= finfo
->atime_ts
.tv_sec
= finfo
->ctime_ts
.tv_sec
;
254 finfo
->mtime_ts
.tv_nsec
= finfo
->atime_ts
.tv_nsec
= 0;
255 finfo
->size
= IVAL(p
,26);
256 ret
= clistr_pull_talloc(ctx
,
263 if (ret
== (size_t)-1) {
268 finfo
->short_name
= talloc_strdup(ctx
, finfo
->name
);
269 if (finfo
->short_name
== NULL
) {
276 struct cli_list_old_state
{
277 struct tevent_context
*ev
;
278 struct cli_state
*cli
;
283 uint8_t search_status
[23];
289 static void cli_list_old_done(struct tevent_req
*subreq
);
291 static struct tevent_req
*cli_list_old_send(TALLOC_CTX
*mem_ctx
,
292 struct tevent_context
*ev
,
293 struct cli_state
*cli
,
297 struct tevent_req
*req
, *subreq
;
298 struct cli_list_old_state
*state
;
300 static const uint16_t zero
= 0;
301 uint32_t usable_space
;
303 req
= tevent_req_create(mem_ctx
, &state
, struct cli_list_old_state
);
309 state
->attribute
= attribute
;
311 state
->mask
= talloc_strdup(state
, mask
);
312 if (tevent_req_nomem(state
->mask
, req
)) {
313 return tevent_req_post(req
, ev
);
315 usable_space
= cli_state_available_size(cli
, 100);
316 state
->num_asked
= usable_space
/ DIR_STRUCT_SIZE
;
318 SSVAL(state
->vwv
+ 0, 0, state
->num_asked
);
319 SSVAL(state
->vwv
+ 1, 0, state
->attribute
);
321 bytes
= talloc_array(state
, uint8_t, 1);
322 if (tevent_req_nomem(bytes
, req
)) {
323 return tevent_req_post(req
, ev
);
326 bytes
= smb_bytes_push_str(bytes
, cli_ucs2(cli
), mask
,
327 strlen(mask
)+1, NULL
);
329 bytes
= smb_bytes_push_bytes(bytes
, 5, (const uint8_t *)&zero
, 2);
330 if (tevent_req_nomem(bytes
, req
)) {
331 return tevent_req_post(req
, ev
);
334 subreq
= cli_smb_send(state
, state
->ev
, state
->cli
, SMBsearch
,
335 0, 2, state
->vwv
, talloc_get_size(bytes
), bytes
);
336 if (tevent_req_nomem(subreq
, req
)) {
337 return tevent_req_post(req
, ev
);
339 tevent_req_set_callback(subreq
, cli_list_old_done
, req
);
343 static void cli_list_old_done(struct tevent_req
*subreq
)
345 struct tevent_req
*req
= tevent_req_callback_data(
346 subreq
, struct tevent_req
);
347 struct cli_list_old_state
*state
= tevent_req_data(
348 req
, struct cli_list_old_state
);
359 status
= cli_smb_recv(subreq
, state
, NULL
, 0, &wct
, &vwv
, &num_bytes
,
361 if (!NT_STATUS_IS_OK(status
)
362 && !NT_STATUS_EQUAL(status
, NT_STATUS_DOS(ERRDOS
, ERRnofiles
))
363 && !NT_STATUS_EQUAL(status
, STATUS_NO_MORE_FILES
)) {
365 tevent_req_nterror(req
, status
);
368 if (NT_STATUS_EQUAL(status
, NT_STATUS_DOS(ERRDOS
, ERRnofiles
))
369 || NT_STATUS_EQUAL(status
, STATUS_NO_MORE_FILES
)) {
375 req
, NT_STATUS_INVALID_NETWORK_RESPONSE
);
378 received
= SVAL(vwv
+ 0, 0);
383 * I don't think this can wrap. received is
384 * initialized from a 16-bit value.
386 if (num_bytes
< (received
* DIR_STRUCT_SIZE
+ 3)) {
389 req
, NT_STATUS_INVALID_NETWORK_RESPONSE
);
393 dirlist_len
= talloc_get_size(state
->dirlist
);
395 tmp
= talloc_realloc(
396 state
, state
->dirlist
, uint8_t,
397 dirlist_len
+ received
* DIR_STRUCT_SIZE
);
398 if (tevent_req_nomem(tmp
, req
)) {
401 state
->dirlist
= tmp
;
402 memcpy(state
->dirlist
+ dirlist_len
, bytes
+ 3,
403 received
* DIR_STRUCT_SIZE
);
405 SSVAL(state
->search_status
, 0, 21);
406 memcpy(state
->search_status
+ 2,
407 bytes
+ 3 + (received
-1)*DIR_STRUCT_SIZE
, 21);
410 if (state
->first
|| state
->done
) {
411 tevent_req_done(req
);
415 state
->num_asked
= 0;
420 state
->first
= false;
422 SSVAL(state
->vwv
+ 0, 0, state
->num_asked
);
423 SSVAL(state
->vwv
+ 1, 0, state
->attribute
);
425 bytes
= talloc_array(state
, uint8_t, 1);
426 if (tevent_req_nomem(bytes
, req
)) {
430 bytes
= smb_bytes_push_str(bytes
, cli_ucs2(state
->cli
), "",
432 bytes
= smb_bytes_push_bytes(bytes
, 5, state
->search_status
,
433 sizeof(state
->search_status
));
434 if (tevent_req_nomem(bytes
, req
)) {
437 subreq
= cli_smb_send(state
, state
->ev
, state
->cli
, cmd
, 0,
438 2, state
->vwv
, talloc_get_size(bytes
), bytes
);
439 if (tevent_req_nomem(subreq
, req
)) {
442 tevent_req_set_callback(subreq
, cli_list_old_done
, req
);
445 static NTSTATUS
cli_list_old_recv(struct tevent_req
*req
, TALLOC_CTX
*mem_ctx
,
446 struct file_info
**pfinfo
)
448 struct cli_list_old_state
*state
= tevent_req_data(
449 req
, struct cli_list_old_state
);
451 size_t i
, num_received
;
452 struct file_info
*finfo
;
454 if (tevent_req_is_nterror(req
, &status
)) {
458 num_received
= talloc_array_length(state
->dirlist
) / DIR_STRUCT_SIZE
;
460 finfo
= talloc_array(mem_ctx
, struct file_info
, num_received
);
462 return NT_STATUS_NO_MEMORY
;
465 for (i
=0; i
<num_received
; i
++) {
466 if (!interpret_short_filename(
468 (char *)state
->dirlist
+ i
* DIR_STRUCT_SIZE
,
471 return NT_STATUS_NO_MEMORY
;
478 NTSTATUS
cli_list_old(struct cli_state
*cli
, const char *mask
,
480 NTSTATUS (*fn
)(const char *, struct file_info
*,
481 const char *, void *), void *state
)
483 TALLOC_CTX
*frame
= talloc_stackframe();
484 struct event_context
*ev
;
485 struct tevent_req
*req
;
486 NTSTATUS status
= NT_STATUS_NO_MEMORY
;
487 struct file_info
*finfo
;
490 if (cli_has_async_calls(cli
)) {
492 * Can't use sync call while an async call is in flight
494 status
= NT_STATUS_INVALID_PARAMETER
;
497 ev
= event_context_init(frame
);
501 req
= cli_list_old_send(frame
, ev
, cli
, mask
, attribute
);
505 if (!tevent_req_poll(req
, ev
)) {
506 status
= map_nt_error_from_unix(errno
);
509 status
= cli_list_old_recv(req
, frame
, &finfo
);
510 if (!NT_STATUS_IS_OK(status
)) {
513 num_finfo
= talloc_array_length(finfo
);
514 for (i
=0; i
<num_finfo
; i
++) {
515 status
= fn(cli
->dfs_mountpoint
, &finfo
[i
], mask
, state
);
516 if (!NT_STATUS_IS_OK(status
)) {
525 struct cli_list_trans_state
{
526 struct tevent_context
*ev
;
527 struct cli_state
*cli
;
534 uint16_t max_matches
;
543 struct file_info
*finfo
;
546 static void cli_list_trans_done(struct tevent_req
*subreq
);
548 static struct tevent_req
*cli_list_trans_send(TALLOC_CTX
*mem_ctx
,
549 struct tevent_context
*ev
,
550 struct cli_state
*cli
,
555 struct tevent_req
*req
, *subreq
;
556 struct cli_list_trans_state
*state
;
559 req
= tevent_req_create(mem_ctx
, &state
,
560 struct cli_list_trans_state
);
566 state
->mask
= talloc_strdup(state
, mask
);
567 if (tevent_req_nomem(state
->mask
, req
)) {
568 return tevent_req_post(req
, ev
);
570 state
->attribute
= attribute
;
571 state
->info_level
= info_level
;
572 state
->loop_count
= 0;
575 state
->max_matches
= 1366; /* Match W2k */
577 SSVAL(&state
->setup
[0], 0, TRANSACT2_FINDFIRST
);
579 state
->param
= talloc_array(state
, uint8_t, 12);
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 state
->param
= trans2_bytes_push_str(state
->param
, cli_ucs2(cli
),
593 state
->mask
, strlen(state
->mask
)+1,
595 if (tevent_req_nomem(state
->param
, req
)) {
596 return tevent_req_post(req
, ev
);
598 param_len
= talloc_get_size(state
->param
);
600 subreq
= cli_trans_send(state
, state
->ev
, state
->cli
,
601 SMBtrans2
, NULL
, -1, 0, 0,
603 state
->param
, param_len
, 10,
604 NULL
, 0, CLI_BUFFER_SIZE
);
605 if (tevent_req_nomem(subreq
, req
)) {
606 return tevent_req_post(req
, ev
);
608 tevent_req_set_callback(subreq
, cli_list_trans_done
, req
);
612 static void cli_list_trans_done(struct tevent_req
*subreq
)
614 struct tevent_req
*req
= tevent_req_callback_data(
615 subreq
, struct tevent_req
);
616 struct cli_list_trans_state
*state
= tevent_req_data(
617 req
, struct cli_list_trans_state
);
625 struct file_info
*tmp
;
626 size_t old_num_finfo
;
627 uint16_t recv_flags2
;
631 uint32_t resume_key
= 0;
633 DATA_BLOB last_name_raw
;
634 struct file_info
*finfo
= NULL
;
637 min_param
= (state
->first
? 6 : 4);
639 status
= cli_trans_recv(subreq
, talloc_tos(), &recv_flags2
,
641 ¶m
, min_param
, &num_param
,
642 &data
, 0, &num_data
);
644 if (!NT_STATUS_IS_OK(status
)) {
646 * TODO: retry, OS/2 nofiles
648 tevent_req_nterror(req
, status
);
653 state
->ff_dir_handle
= SVAL(param
, 0);
654 ff_searchcount
= SVAL(param
, 2);
655 ff_eos
= SVAL(param
, 4) != 0;
657 ff_searchcount
= SVAL(param
, 0);
658 ff_eos
= SVAL(param
, 2) != 0;
661 old_num_finfo
= talloc_array_length(state
->finfo
);
663 tmp
= talloc_realloc(state
, state
->finfo
, struct file_info
,
664 old_num_finfo
+ ff_searchcount
);
665 if (tevent_req_nomem(tmp
, req
)) {
670 p2
= p
= (char *)data
;
671 data_end
= (char *)data
+ num_data
;
672 last_name_raw
= data_blob_null
;
674 for (i
=0; i
<ff_searchcount
; i
++) {
675 if (p2
>= data_end
) {
679 if ((state
->info_level
== SMB_FIND_FILE_BOTH_DIRECTORY_INFO
)
680 && (i
== ff_searchcount
-1)) {
681 /* Last entry - fixup the last offset length. */
682 SIVAL(p2
, 0, PTR_DIFF((data
+ num_data
), p2
));
685 data_blob_free(&last_name_raw
);
687 finfo
= &state
->finfo
[old_num_finfo
+ i
];
689 p2
+= interpret_long_filename(
690 state
->finfo
, /* Stick fname to the array as such */
691 state
->cli
, state
->info_level
,
692 (char *)data
, recv_flags2
, p2
,
693 data_end
, finfo
, &resume_key
, &last_name_raw
);
695 if (finfo
->name
== NULL
) {
696 DEBUG(1, ("cli_list: Error: unable to parse name from "
697 "info level %d\n", state
->info_level
));
701 if (!state
->first
&& (state
->mask
[0] != '\0') &&
702 strcsequal(finfo
->name
, state
->mask
)) {
703 DEBUG(1, ("Error: Looping in FIND_NEXT as name %s has "
704 "already been seen?\n", finfo
->name
));
710 if (ff_searchcount
== 0) {
718 * Shrink state->finfo to the real length we received
720 tmp
= talloc_realloc(state
, state
->finfo
, struct file_info
,
722 if (tevent_req_nomem(tmp
, req
)) {
727 state
->first
= false;
730 data_blob_free(&last_name_raw
);
731 tevent_req_done(req
);
735 TALLOC_FREE(state
->mask
);
736 state
->mask
= talloc_strdup(state
, finfo
->name
);
737 if (tevent_req_nomem(state
->mask
, req
)) {
741 SSVAL(&state
->setup
[0], 0, TRANSACT2_FINDNEXT
);
743 param
= talloc_realloc(state
, state
->param
, uint8_t, 12);
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 if (last_name_raw
.length
) {
765 state
->param
= trans2_bytes_push_bytes(state
->param
,
767 last_name_raw
.length
);
768 if (tevent_req_nomem(state
->param
, req
)) {
771 data_blob_free(&last_name_raw
);
773 state
->param
= trans2_bytes_push_str(state
->param
,
774 cli_ucs2(state
->cli
),
776 strlen(state
->mask
)+1,
778 if (tevent_req_nomem(state
->param
, req
)) {
782 param_len
= talloc_get_size(state
->param
);
784 subreq
= cli_trans_send(state
, state
->ev
, state
->cli
,
785 SMBtrans2
, NULL
, -1, 0, 0,
787 state
->param
, param_len
, 10,
788 NULL
, 0, CLI_BUFFER_SIZE
);
789 if (tevent_req_nomem(subreq
, req
)) {
792 tevent_req_set_callback(subreq
, cli_list_trans_done
, req
);
795 static NTSTATUS
cli_list_trans_recv(struct tevent_req
*req
,
797 struct file_info
**finfo
)
799 struct cli_list_trans_state
*state
= tevent_req_data(
800 req
, struct cli_list_trans_state
);
803 if (tevent_req_is_nterror(req
, &status
)) {
806 *finfo
= talloc_move(mem_ctx
, &state
->finfo
);
810 NTSTATUS
cli_list_trans(struct cli_state
*cli
, const char *mask
,
811 uint16_t attribute
, int info_level
,
812 NTSTATUS (*fn
)(const char *mnt
, struct file_info
*finfo
,
813 const char *mask
, void *private_data
),
816 TALLOC_CTX
*frame
= talloc_stackframe();
817 struct event_context
*ev
;
818 struct tevent_req
*req
;
820 struct file_info
*finfo
= NULL
;
821 NTSTATUS status
= NT_STATUS_NO_MEMORY
;
823 if (cli_has_async_calls(cli
)) {
825 * Can't use sync call while an async call is in flight
827 status
= NT_STATUS_INVALID_PARAMETER
;
830 ev
= event_context_init(frame
);
834 req
= cli_list_trans_send(frame
, ev
, cli
, mask
, attribute
, info_level
);
838 if (!tevent_req_poll_ntstatus(req
, ev
, &status
)) {
841 status
= cli_list_trans_recv(req
, frame
, &finfo
);
842 if (!NT_STATUS_IS_OK(status
)) {
845 num_finfo
= talloc_array_length(finfo
);
846 for (i
=0; i
<num_finfo
; i
++) {
847 status
= fn(cli
->dfs_mountpoint
, &finfo
[i
], mask
, private_data
);
848 if (!NT_STATUS_IS_OK(status
)) {
857 struct cli_list_state
{
858 NTSTATUS (*recv_fn
)(struct tevent_req
*req
, TALLOC_CTX
*mem_ctx
,
859 struct file_info
**finfo
);
860 struct file_info
*finfo
;
863 static void cli_list_done(struct tevent_req
*subreq
);
865 struct tevent_req
*cli_list_send(TALLOC_CTX
*mem_ctx
,
866 struct tevent_context
*ev
,
867 struct cli_state
*cli
,
872 struct tevent_req
*req
, *subreq
;
873 struct cli_list_state
*state
;
875 req
= tevent_req_create(mem_ctx
, &state
, struct cli_list_state
);
880 if (cli_state_protocol(cli
) <= PROTOCOL_LANMAN1
) {
881 subreq
= cli_list_old_send(state
, ev
, cli
, mask
, attribute
);
882 state
->recv_fn
= cli_list_old_recv
;
884 subreq
= cli_list_trans_send(state
, ev
, cli
, mask
, attribute
,
886 state
->recv_fn
= cli_list_trans_recv
;
888 if (tevent_req_nomem(subreq
, req
)) {
889 return tevent_req_post(req
, ev
);
891 tevent_req_set_callback(subreq
, cli_list_done
, req
);
895 static void cli_list_done(struct tevent_req
*subreq
)
897 struct tevent_req
*req
= tevent_req_callback_data(
898 subreq
, struct tevent_req
);
899 struct cli_list_state
*state
= tevent_req_data(
900 req
, struct cli_list_state
);
903 status
= state
->recv_fn(subreq
, state
, &state
->finfo
);
905 if (!NT_STATUS_IS_OK(status
)) {
906 tevent_req_nterror(req
, status
);
909 tevent_req_done(req
);
912 NTSTATUS
cli_list_recv(struct tevent_req
*req
, TALLOC_CTX
*mem_ctx
,
913 struct file_info
**finfo
, size_t *num_finfo
)
915 struct cli_list_state
*state
= tevent_req_data(
916 req
, struct cli_list_state
);
919 if (tevent_req_is_nterror(req
, &status
)) {
922 *num_finfo
= talloc_array_length(state
->finfo
);
923 *finfo
= talloc_move(mem_ctx
, &state
->finfo
);
927 NTSTATUS
cli_list(struct cli_state
*cli
, const char *mask
, uint16 attribute
,
928 NTSTATUS (*fn
)(const char *, struct file_info
*, const char *,
929 void *), void *state
)
931 TALLOC_CTX
*frame
= talloc_stackframe();
932 struct event_context
*ev
;
933 struct tevent_req
*req
;
934 NTSTATUS status
= NT_STATUS_NO_MEMORY
;
935 struct file_info
*finfo
;
939 if (cli_has_async_calls(cli
)) {
941 * Can't use sync call while an async call is in flight
943 status
= NT_STATUS_INVALID_PARAMETER
;
946 ev
= event_context_init(frame
);
951 info_level
= (cli_state_capabilities(cli
) & CAP_NT_SMBS
)
952 ? SMB_FIND_FILE_BOTH_DIRECTORY_INFO
: SMB_FIND_INFO_STANDARD
;
954 req
= cli_list_send(frame
, ev
, cli
, mask
, attribute
, info_level
);
958 if (!tevent_req_poll(req
, ev
)) {
959 status
= map_nt_error_from_unix(errno
);
963 status
= cli_list_recv(req
, frame
, &finfo
, &num_finfo
);
964 if (!NT_STATUS_IS_OK(status
)) {
968 for (i
=0; i
<num_finfo
; i
++) {
969 status
= fn(cli
->dfs_mountpoint
, &finfo
[i
], mask
, state
);
970 if (!NT_STATUS_IS_OK(status
)) {