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 "async_smb.h"
24 /****************************************************************************
25 Calculate a safe next_entry_offset.
26 ****************************************************************************/
28 static size_t calc_next_entry_offset(const char *base
, const char *pdata_end
)
30 size_t next_entry_offset
= (size_t)IVAL(base
,0);
32 if (next_entry_offset
== 0 ||
33 base
+ next_entry_offset
< base
||
34 base
+ next_entry_offset
> pdata_end
) {
35 next_entry_offset
= pdata_end
- base
;
37 return next_entry_offset
;
40 /****************************************************************************
41 Interpret a long filename structure - this is mostly guesses at the moment.
42 The length of the structure is returned
43 The structure of a long filename depends on the info level.
44 SMB_FIND_FILE_BOTH_DIRECTORY_INFO is used
45 by NT and SMB_FIND_EA_SIZE is used by OS/2
46 ****************************************************************************/
48 static size_t interpret_long_filename(TALLOC_CTX
*ctx
,
49 struct cli_state
*cli
,
54 const char *pdata_end
,
55 struct file_info
*finfo
,
57 DATA_BLOB
*p_last_name_raw
)
63 data_blob_free(p_last_name_raw
);
71 case SMB_FIND_INFO_STANDARD
: /* OS/2 understands this */
72 /* these dates are converted to GMT by
74 if (pdata_end
- base
< 27) {
75 return pdata_end
- base
;
77 finfo
->ctime_ts
= convert_time_t_to_timespec(
78 make_unix_date2(p
+4, cli
->serverzone
));
79 finfo
->atime_ts
= convert_time_t_to_timespec(
80 make_unix_date2(p
+8, cli
->serverzone
));
81 finfo
->mtime_ts
= convert_time_t_to_timespec(
82 make_unix_date2(p
+12, cli
->serverzone
));
83 finfo
->size
= IVAL(p
,16);
84 finfo
->mode
= CVAL(p
,24);
87 p
+= align_string(base_ptr
, p
, 0);
89 /* We can safely use len here (which is required by OS/2)
90 * and the NAS-BASIC server instead of +2 or +1 as the
91 * STR_TERMINATE flag below is
92 * actually used as the length calculation.
93 * The len is merely an upper bound.
94 * Due to the explicit 2 byte null termination
95 * in cli_receive_trans/cli_receive_nt_trans
96 * we know this is safe. JRA + kukks
99 if (p
+ len
> pdata_end
) {
100 return pdata_end
- base
;
103 /* the len+2 below looks strange but it is
104 important to cope with the differences
105 between win2000 and win9x for this call
107 ret
= clistr_pull_talloc(ctx
,
114 if (ret
== (size_t)-1) {
115 return pdata_end
- base
;
118 return PTR_DIFF(p
, base
);
120 case SMB_FIND_EA_SIZE
: /* this is what OS/2 uses mostly */
121 /* these dates are converted to GMT by
123 if (pdata_end
- base
< 31) {
124 return pdata_end
- base
;
126 finfo
->ctime_ts
= convert_time_t_to_timespec(
127 make_unix_date2(p
+4, cli
->serverzone
));
128 finfo
->atime_ts
= convert_time_t_to_timespec(
129 make_unix_date2(p
+8, cli
->serverzone
));
130 finfo
->mtime_ts
= convert_time_t_to_timespec(
131 make_unix_date2(p
+12, cli
->serverzone
));
132 finfo
->size
= IVAL(p
,16);
133 finfo
->mode
= CVAL(p
,24);
136 /* check for unisys! */
137 if (p
+ len
+ 1 > pdata_end
) {
138 return pdata_end
- base
;
140 ret
= clistr_pull_talloc(ctx
,
147 if (ret
== (size_t)-1) {
148 return pdata_end
- base
;
151 return PTR_DIFF(p
, base
) + 1;
153 case SMB_FIND_FILE_BOTH_DIRECTORY_INFO
: /* NT uses this, but also accepts 2 */
155 size_t namelen
, slen
;
157 if (pdata_end
- base
< 94) {
158 return pdata_end
- base
;
161 p
+= 4; /* next entry offset */
164 *p_resume_key
= IVAL(p
,0);
166 p
+= 4; /* fileindex */
168 /* Offset zero is "create time", not "change time". */
170 finfo
->atime_ts
= interpret_long_date(p
);
172 finfo
->mtime_ts
= interpret_long_date(p
);
174 finfo
->ctime_ts
= interpret_long_date(p
);
176 finfo
->size
= IVAL2_TO_SMB_BIG_UINT(p
,0);
178 p
+= 8; /* alloc size */
179 finfo
->mode
= CVAL(p
,0);
183 p
+= 4; /* EA size */
186 /* Bad short name length. */
187 return pdata_end
- base
;
191 /* stupid NT bugs. grr */
193 if (p
[1] == 0 && namelen
> 1) flags
|= STR_UNICODE
;
194 clistr_pull(base_ptr
, finfo
->short_name
, p
,
195 sizeof(finfo
->short_name
),
198 p
+= 24; /* short name? */
199 if (p
+ namelen
< p
|| p
+ namelen
> pdata_end
) {
200 return pdata_end
- base
;
202 ret
= clistr_pull_talloc(ctx
,
209 if (ret
== (size_t)-1) {
210 return pdata_end
- base
;
213 /* To be robust in the face of unicode conversion failures
214 we need to copy the raw bytes of the last name seen here.
215 Namelen doesn't include the terminating unicode null, so
218 if (p_last_name_raw
) {
219 *p_last_name_raw
= data_blob(NULL
, namelen
+2);
220 memcpy(p_last_name_raw
->data
, p
, namelen
);
221 SSVAL(p_last_name_raw
->data
, namelen
, 0);
223 return calc_next_entry_offset(base
, pdata_end
);
227 DEBUG(1,("Unknown long filename format %d\n",level
));
228 return calc_next_entry_offset(base
, pdata_end
);
231 /****************************************************************************
232 Interpret a short filename structure.
233 The length of the structure is returned.
234 ****************************************************************************/
236 static bool interpret_short_filename(TALLOC_CTX
*ctx
,
237 struct cli_state
*cli
,
239 struct file_info
*finfo
)
244 finfo
->mode
= CVAL(p
,21);
246 /* this date is converted to GMT by make_unix_date */
247 finfo
->ctime_ts
.tv_sec
= make_unix_date(p
+22, cli
->serverzone
);
248 finfo
->ctime_ts
.tv_nsec
= 0;
249 finfo
->mtime_ts
.tv_sec
= finfo
->atime_ts
.tv_sec
= finfo
->ctime_ts
.tv_sec
;
250 finfo
->mtime_ts
.tv_nsec
= finfo
->atime_ts
.tv_nsec
= 0;
251 finfo
->size
= IVAL(p
,26);
252 ret
= clistr_pull_talloc(ctx
,
254 SVAL(cli
->inbuf
, smb_flg2
),
259 if (ret
== (size_t)-1) {
264 strlcpy(finfo
->short_name
,
266 sizeof(finfo
->short_name
));
271 struct cli_list_old_state
{
272 struct tevent_context
*ev
;
273 struct cli_state
*cli
;
278 uint8_t search_status
[23];
284 static void cli_list_old_done(struct tevent_req
*subreq
);
286 static struct tevent_req
*cli_list_old_send(TALLOC_CTX
*mem_ctx
,
287 struct tevent_context
*ev
,
288 struct cli_state
*cli
,
292 struct tevent_req
*req
, *subreq
;
293 struct cli_list_old_state
*state
;
295 static const uint16_t zero
= 0;
297 req
= tevent_req_create(mem_ctx
, &state
, struct cli_list_old_state
);
303 state
->attribute
= attribute
;
305 state
->mask
= talloc_strdup(state
, mask
);
306 if (tevent_req_nomem(state
->mask
, req
)) {
307 return tevent_req_post(req
, ev
);
309 state
->num_asked
= (cli
->max_xmit
- 100) / DIR_STRUCT_SIZE
;
311 SSVAL(state
->vwv
+ 0, 0, state
->num_asked
);
312 SSVAL(state
->vwv
+ 1, 0, state
->attribute
);
314 bytes
= talloc_array(state
, uint8_t, 1);
315 if (tevent_req_nomem(bytes
, req
)) {
316 return tevent_req_post(req
, ev
);
319 bytes
= smb_bytes_push_str(bytes
, cli_ucs2(cli
), mask
,
320 strlen(mask
)+1, NULL
);
322 bytes
= smb_bytes_push_bytes(bytes
, 5, (uint8_t *)&zero
, 2);
323 if (tevent_req_nomem(bytes
, req
)) {
324 return tevent_req_post(req
, ev
);
327 subreq
= cli_smb_send(state
, state
->ev
, state
->cli
, SMBsearch
,
328 0, 2, state
->vwv
, talloc_get_size(bytes
), bytes
);
329 if (tevent_req_nomem(subreq
, req
)) {
330 return tevent_req_post(req
, ev
);
332 tevent_req_set_callback(subreq
, cli_list_old_done
, req
);
336 static void cli_list_old_done(struct tevent_req
*subreq
)
338 struct tevent_req
*req
= tevent_req_callback_data(
339 subreq
, struct tevent_req
);
340 struct cli_list_old_state
*state
= tevent_req_data(
341 req
, struct cli_list_old_state
);
352 status
= cli_smb_recv(subreq
, state
, NULL
, 0, &wct
, &vwv
, &num_bytes
,
354 if (!NT_STATUS_IS_OK(status
)
355 && !NT_STATUS_EQUAL(status
, NT_STATUS_DOS(ERRDOS
, ERRnofiles
))
356 && !NT_STATUS_EQUAL(status
, STATUS_NO_MORE_FILES
)) {
358 tevent_req_nterror(req
, status
);
361 if (NT_STATUS_EQUAL(status
, NT_STATUS_DOS(ERRDOS
, ERRnofiles
))
362 || NT_STATUS_EQUAL(status
, STATUS_NO_MORE_FILES
)) {
368 req
, NT_STATUS_INVALID_NETWORK_RESPONSE
);
371 received
= SVAL(vwv
+ 0, 0);
376 * I don't think this can wrap. received is
377 * initialized from a 16-bit value.
379 if (num_bytes
< (received
* DIR_STRUCT_SIZE
+ 3)) {
382 req
, NT_STATUS_INVALID_NETWORK_RESPONSE
);
386 dirlist_len
= talloc_get_size(state
->dirlist
);
388 tmp
= TALLOC_REALLOC_ARRAY(
389 state
, state
->dirlist
, uint8_t,
390 dirlist_len
+ received
* DIR_STRUCT_SIZE
);
391 if (tevent_req_nomem(tmp
, req
)) {
394 state
->dirlist
= tmp
;
395 memcpy(state
->dirlist
+ dirlist_len
, bytes
+ 3,
396 received
* DIR_STRUCT_SIZE
);
398 SSVAL(state
->search_status
, 0, 21);
399 memcpy(state
->search_status
+ 2,
400 bytes
+ 3 + (received
-1)*DIR_STRUCT_SIZE
, 21);
403 if (state
->first
|| state
->done
) {
404 tevent_req_done(req
);
408 state
->num_asked
= 0;
413 state
->first
= false;
415 SSVAL(state
->vwv
+ 0, 0, state
->num_asked
);
416 SSVAL(state
->vwv
+ 1, 0, state
->attribute
);
418 bytes
= talloc_array(state
, uint8_t, 1);
419 if (tevent_req_nomem(bytes
, req
)) {
423 bytes
= smb_bytes_push_str(bytes
, cli_ucs2(state
->cli
), "",
425 bytes
= smb_bytes_push_bytes(bytes
, 5, state
->search_status
,
426 sizeof(state
->search_status
));
427 if (tevent_req_nomem(bytes
, req
)) {
430 subreq
= cli_smb_send(state
, state
->ev
, state
->cli
, cmd
, 0,
431 2, state
->vwv
, talloc_get_size(bytes
), bytes
);
432 if (tevent_req_nomem(subreq
, req
)) {
435 tevent_req_set_callback(subreq
, cli_list_old_done
, req
);
438 static NTSTATUS
cli_list_old_recv(struct tevent_req
*req
, TALLOC_CTX
*mem_ctx
,
439 struct file_info
**pfinfo
)
441 struct cli_list_old_state
*state
= tevent_req_data(
442 req
, struct cli_list_old_state
);
444 size_t i
, num_received
;
445 struct file_info
*finfo
;
447 if (tevent_req_is_nterror(req
, &status
)) {
451 num_received
= talloc_array_length(state
->dirlist
) / DIR_STRUCT_SIZE
;
453 finfo
= TALLOC_ARRAY(mem_ctx
, struct file_info
, num_received
);
455 return NT_STATUS_NO_MEMORY
;
458 for (i
=0; i
<num_received
; i
++) {
459 if (!interpret_short_filename(
461 (char *)state
->dirlist
+ i
* DIR_STRUCT_SIZE
,
464 return NT_STATUS_NO_MEMORY
;
471 NTSTATUS
cli_list_old(struct cli_state
*cli
, const char *mask
,
473 NTSTATUS (*fn
)(const char *, struct file_info
*,
474 const char *, void *), void *state
)
476 TALLOC_CTX
*frame
= talloc_stackframe();
477 struct event_context
*ev
;
478 struct tevent_req
*req
;
479 NTSTATUS status
= NT_STATUS_NO_MEMORY
;
480 struct file_info
*finfo
;
483 if (cli_has_async_calls(cli
)) {
485 * Can't use sync call while an async call is in flight
487 status
= NT_STATUS_INVALID_PARAMETER
;
490 ev
= event_context_init(frame
);
494 req
= cli_list_old_send(frame
, ev
, cli
, mask
, attribute
);
498 if (!tevent_req_poll(req
, ev
)) {
499 status
= map_nt_error_from_unix(errno
);
502 status
= cli_list_old_recv(req
, frame
, &finfo
);
503 if (!NT_STATUS_IS_OK(status
)) {
506 num_finfo
= talloc_array_length(finfo
);
507 for (i
=0; i
<num_finfo
; i
++) {
508 status
= fn(cli
->dfs_mountpoint
, &finfo
[i
], mask
, state
);
509 if (!NT_STATUS_IS_OK(status
)) {
515 if (!NT_STATUS_IS_OK(status
)) {
516 cli_set_error(cli
, status
);
521 struct cli_list_trans_state
{
522 struct tevent_context
*ev
;
523 struct cli_state
*cli
;
530 uint16_t max_matches
;
539 struct file_info
*finfo
;
542 static void cli_list_trans_done(struct tevent_req
*subreq
);
544 static struct tevent_req
*cli_list_trans_send(TALLOC_CTX
*mem_ctx
,
545 struct tevent_context
*ev
,
546 struct cli_state
*cli
,
551 struct tevent_req
*req
, *subreq
;
552 struct cli_list_trans_state
*state
;
553 size_t nlen
, param_len
;
556 req
= tevent_req_create(mem_ctx
, &state
,
557 struct cli_list_trans_state
);
563 state
->mask
= talloc_strdup(state
, mask
);
564 if (tevent_req_nomem(state
->mask
, req
)) {
565 return tevent_req_post(req
, ev
);
567 state
->attribute
= attribute
;
568 state
->info_level
= info_level
;
569 state
->loop_count
= 0;
572 state
->max_matches
= 1366; /* Match W2k */
574 state
->setup
[0] = TRANSACT2_FINDFIRST
;
576 nlen
= 2*(strlen(mask
)+1);
577 state
->param
= TALLOC_ARRAY(state
, uint8_t, 12+nlen
+2);
578 if (tevent_req_nomem(state
->param
, req
)) {
579 return tevent_req_post(req
, ev
);
582 SSVAL(state
->param
, 0, state
->attribute
);
583 SSVAL(state
->param
, 2, state
->max_matches
);
584 SSVAL(state
->param
, 4,
585 FLAG_TRANS2_FIND_REQUIRE_RESUME
586 |FLAG_TRANS2_FIND_CLOSE_IF_END
);
587 SSVAL(state
->param
, 6, state
->info_level
);
588 SIVAL(state
->param
, 8, 0);
590 p
= ((char *)state
->param
)+12;
591 p
+= clistr_push(state
->cli
, p
, state
->mask
, nlen
,
593 param_len
= PTR_DIFF(p
, state
->param
);
595 subreq
= cli_trans_send(state
, state
->ev
, state
->cli
,
596 SMBtrans2
, NULL
, -1, 0, 0,
598 state
->param
, param_len
, 10,
599 NULL
, 0, cli
->max_xmit
);
600 if (tevent_req_nomem(subreq
, req
)) {
601 return tevent_req_post(req
, ev
);
603 tevent_req_set_callback(subreq
, cli_list_trans_done
, req
);
607 static void cli_list_trans_done(struct tevent_req
*subreq
)
609 struct tevent_req
*req
= tevent_req_callback_data(
610 subreq
, struct tevent_req
);
611 struct cli_list_trans_state
*state
= tevent_req_data(
612 req
, struct cli_list_trans_state
);
620 struct file_info
*tmp
;
621 size_t old_num_finfo
;
622 uint16_t recv_flags2
;
626 uint32_t resume_key
= 0;
628 DATA_BLOB last_name_raw
;
629 struct file_info
*finfo
= NULL
;
630 size_t nlen
, param_len
;
632 min_param
= (state
->first
? 6 : 4);
634 status
= cli_trans_recv(subreq
, talloc_tos(), &recv_flags2
,
636 ¶m
, min_param
, &num_param
,
637 &data
, 0, &num_data
);
639 if (!NT_STATUS_IS_OK(status
)) {
641 * TODO: retry, OS/2 nofiles
643 tevent_req_nterror(req
, status
);
648 state
->ff_dir_handle
= SVAL(param
, 0);
649 ff_searchcount
= SVAL(param
, 2);
650 ff_eos
= SVAL(param
, 4) != 0;
652 ff_searchcount
= SVAL(param
, 0);
653 ff_eos
= SVAL(param
, 2) != 0;
656 old_num_finfo
= talloc_array_length(state
->finfo
);
658 tmp
= TALLOC_REALLOC_ARRAY(state
, state
->finfo
, struct file_info
,
659 old_num_finfo
+ ff_searchcount
);
660 if (tevent_req_nomem(tmp
, req
)) {
665 p2
= p
= (char *)data
;
666 data_end
= (char *)data
+ num_data
;
667 last_name_raw
= data_blob_null
;
669 for (i
=0; i
<ff_searchcount
; i
++) {
670 if (p2
>= data_end
) {
674 if ((state
->info_level
== SMB_FIND_FILE_BOTH_DIRECTORY_INFO
)
675 && (i
== ff_searchcount
-1)) {
676 /* Last entry - fixup the last offset length. */
677 SIVAL(p2
, 0, PTR_DIFF((data
+ num_data
), p2
));
680 data_blob_free(&last_name_raw
);
682 finfo
= &state
->finfo
[old_num_finfo
+ i
];
684 p2
+= interpret_long_filename(
685 state
->finfo
, /* Stick fname to the array as such */
686 state
->cli
, state
->info_level
,
687 (char *)data
, recv_flags2
, p2
,
688 data_end
, finfo
, &resume_key
, &last_name_raw
);
690 if (finfo
->name
== NULL
) {
691 DEBUG(1, ("cli_list: Error: unable to parse name from "
692 "info level %d\n", state
->info_level
));
696 if (!state
->first
&& (state
->mask
[0] != '\0') &&
697 strcsequal(finfo
->name
, state
->mask
)) {
698 DEBUG(1, ("Error: Looping in FIND_NEXT as name %s has "
699 "already been seen?\n", finfo
->name
));
705 if (ff_searchcount
== 0) {
713 * Shrink state->finfo to the real length we received
715 tmp
= TALLOC_REALLOC_ARRAY(state
, state
->finfo
, struct file_info
,
717 if (tevent_req_nomem(tmp
, req
)) {
722 state
->first
= false;
725 data_blob_free(&last_name_raw
);
726 tevent_req_done(req
);
730 TALLOC_FREE(state
->mask
);
731 state
->mask
= talloc_strdup(state
, finfo
->name
);
732 if (tevent_req_nomem(state
->mask
, req
)) {
736 state
->setup
[0] = TRANSACT2_FINDNEXT
;
738 nlen
= 2*(strlen(state
->mask
) + 1);
740 param
= TALLOC_REALLOC_ARRAY(state
, state
->param
, uint8_t,
741 12 + nlen
+ last_name_raw
.length
+ 2);
742 if (tevent_req_nomem(param
, req
)) {
745 state
->param
= param
;
747 SSVAL(param
, 0, state
->ff_dir_handle
);
748 SSVAL(param
, 2, state
->max_matches
); /* max count */
749 SSVAL(param
, 4, state
->info_level
);
751 * For W2K servers serving out FAT filesystems we *must* set
752 * the resume key. If it's not FAT then it's returned as zero.
754 SIVAL(param
, 6, resume_key
); /* ff_resume_key */
756 * NB. *DON'T* use continue here. If you do it seems that W2K
757 * and bretheren can miss filenames. Use last filename
758 * continue instead. JRA
760 SSVAL(param
, 10, (FLAG_TRANS2_FIND_REQUIRE_RESUME
761 |FLAG_TRANS2_FIND_CLOSE_IF_END
));
762 p
= ((char *)param
)+12;
763 if (last_name_raw
.length
) {
764 memcpy(p
, last_name_raw
.data
, last_name_raw
.length
);
765 p
+= last_name_raw
.length
;
766 data_blob_free(&last_name_raw
);
768 p
+= clistr_push(state
->cli
, p
, state
->mask
, nlen
,
772 param_len
= PTR_DIFF(p
, param
);
774 subreq
= cli_trans_send(state
, state
->ev
, state
->cli
,
775 SMBtrans2
, NULL
, -1, 0, 0,
777 state
->param
, param_len
, 10,
778 NULL
, 0, state
->cli
->max_xmit
);
779 if (tevent_req_nomem(subreq
, req
)) {
782 tevent_req_set_callback(subreq
, cli_list_trans_done
, req
);
785 static NTSTATUS
cli_list_trans_recv(struct tevent_req
*req
,
787 struct file_info
**finfo
)
789 struct cli_list_trans_state
*state
= tevent_req_data(
790 req
, struct cli_list_trans_state
);
793 if (tevent_req_is_nterror(req
, &status
)) {
796 *finfo
= talloc_move(mem_ctx
, &state
->finfo
);
800 NTSTATUS
cli_list_trans(struct cli_state
*cli
, const char *mask
,
801 uint16_t attribute
, int info_level
,
802 NTSTATUS (*fn
)(const char *mnt
, struct file_info
*finfo
,
803 const char *mask
, void *private_data
),
806 TALLOC_CTX
*frame
= talloc_stackframe();
807 struct event_context
*ev
;
808 struct tevent_req
*req
;
810 struct file_info
*finfo
= NULL
;
811 NTSTATUS status
= NT_STATUS_NO_MEMORY
;
813 if (cli_has_async_calls(cli
)) {
815 * Can't use sync call while an async call is in flight
817 status
= NT_STATUS_INVALID_PARAMETER
;
820 ev
= event_context_init(frame
);
824 req
= cli_list_trans_send(frame
, ev
, cli
, mask
, attribute
, info_level
);
828 if (!tevent_req_poll_ntstatus(req
, ev
, &status
)) {
831 status
= cli_list_trans_recv(req
, frame
, &finfo
);
832 if (!NT_STATUS_IS_OK(status
)) {
835 num_finfo
= talloc_array_length(finfo
);
836 for (i
=0; i
<num_finfo
; i
++) {
837 status
= fn(cli
->dfs_mountpoint
, &finfo
[i
], mask
, private_data
);
838 if (!NT_STATUS_IS_OK(status
)) {
844 if (!NT_STATUS_IS_OK(status
)) {
845 cli_set_error(cli
, status
);
850 struct cli_list_state
{
851 NTSTATUS (*recv_fn
)(struct tevent_req
*req
, TALLOC_CTX
*mem_ctx
,
852 struct file_info
**finfo
);
853 struct file_info
*finfo
;
856 static void cli_list_done(struct tevent_req
*subreq
);
858 struct tevent_req
*cli_list_send(TALLOC_CTX
*mem_ctx
,
859 struct tevent_context
*ev
,
860 struct cli_state
*cli
,
865 struct tevent_req
*req
, *subreq
;
866 struct cli_list_state
*state
;
868 req
= tevent_req_create(mem_ctx
, &state
, struct cli_list_state
);
873 if (cli
->protocol
<= PROTOCOL_LANMAN1
) {
874 subreq
= cli_list_old_send(state
, ev
, cli
, mask
, attribute
);
875 state
->recv_fn
= cli_list_old_recv
;
877 subreq
= cli_list_trans_send(state
, ev
, cli
, mask
, attribute
,
879 state
->recv_fn
= cli_list_trans_recv
;
881 if (tevent_req_nomem(subreq
, req
)) {
882 return tevent_req_post(req
, ev
);
884 tevent_req_set_callback(subreq
, cli_list_done
, req
);
888 static void cli_list_done(struct tevent_req
*subreq
)
890 struct tevent_req
*req
= tevent_req_callback_data(
891 subreq
, struct tevent_req
);
892 struct cli_list_state
*state
= tevent_req_data(
893 req
, struct cli_list_state
);
896 status
= state
->recv_fn(subreq
, state
, &state
->finfo
);
898 if (!NT_STATUS_IS_OK(status
)) {
899 tevent_req_nterror(req
, status
);
902 tevent_req_done(req
);
905 NTSTATUS
cli_list_recv(struct tevent_req
*req
, TALLOC_CTX
*mem_ctx
,
906 struct file_info
**finfo
, size_t *num_finfo
)
908 struct cli_list_state
*state
= tevent_req_data(
909 req
, struct cli_list_state
);
912 if (tevent_req_is_nterror(req
, &status
)) {
915 *num_finfo
= talloc_array_length(state
->finfo
);
916 *finfo
= talloc_move(mem_ctx
, &state
->finfo
);
920 NTSTATUS
cli_list(struct cli_state
*cli
, const char *mask
, uint16 attribute
,
921 NTSTATUS (*fn
)(const char *, struct file_info
*, const char *,
922 void *), void *state
)
924 TALLOC_CTX
*frame
= talloc_stackframe();
925 struct event_context
*ev
;
926 struct tevent_req
*req
;
927 NTSTATUS status
= NT_STATUS_NO_MEMORY
;
928 struct file_info
*finfo
;
932 if (cli_has_async_calls(cli
)) {
934 * Can't use sync call while an async call is in flight
936 status
= NT_STATUS_INVALID_PARAMETER
;
939 ev
= event_context_init(frame
);
944 info_level
= (cli
->capabilities
& CAP_NT_SMBS
)
945 ? SMB_FIND_FILE_BOTH_DIRECTORY_INFO
: SMB_FIND_INFO_STANDARD
;
947 req
= cli_list_send(frame
, ev
, cli
, mask
, attribute
, info_level
);
951 if (!tevent_req_poll(req
, ev
)) {
952 status
= map_nt_error_from_unix(errno
);
956 status
= cli_list_recv(req
, frame
, &finfo
, &num_finfo
);
957 if (!NT_STATUS_IS_OK(status
)) {
961 for (i
=0; i
<num_finfo
; i
++) {
962 status
= fn(cli
->dfs_mountpoint
, &finfo
[i
], mask
, state
);
963 if (!NT_STATUS_IS_OK(status
)) {
969 if (!NT_STATUS_IS_OK(status
)) {
970 cli_set_error(cli
, status
);