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 Check if a returned directory name is safe.
29 ****************************************************************************/
31 static NTSTATUS
is_bad_name(bool windows_names
, const char *name
)
33 const char *bad_name_p
= NULL
;
35 bad_name_p
= strchr(name
, '/');
36 if (bad_name_p
!= NULL
) {
38 * Windows and POSIX names can't have '/'.
39 * Server is attacking us.
41 return NT_STATUS_INVALID_NETWORK_RESPONSE
;
44 bad_name_p
= strchr(name
, '\\');
45 if (bad_name_p
!= NULL
) {
47 * Windows names can't have '\\'.
48 * Server is attacking us.
50 return NT_STATUS_INVALID_NETWORK_RESPONSE
;
56 /****************************************************************************
57 Check if a returned directory name is safe. Disconnect if server is
59 ****************************************************************************/
61 NTSTATUS
is_bad_finfo_name(const struct cli_state
*cli
,
62 const struct file_info
*finfo
)
64 NTSTATUS status
= NT_STATUS_OK
;
65 bool windows_names
= true;
67 if (cli
->requested_posix_capabilities
& CIFS_UNIX_POSIX_PATHNAMES_CAP
) {
68 windows_names
= false;
70 if (finfo
->name
!= NULL
) {
71 status
= is_bad_name(windows_names
, finfo
->name
);
72 if (!NT_STATUS_IS_OK(status
)) {
73 DBG_ERR("bad finfo->name\n");
77 if (finfo
->short_name
!= NULL
) {
78 status
= is_bad_name(windows_names
, finfo
->short_name
);
79 if (!NT_STATUS_IS_OK(status
)) {
80 DBG_ERR("bad finfo->short_name\n");
87 /****************************************************************************
88 Calculate a safe next_entry_offset.
89 ****************************************************************************/
91 static size_t calc_next_entry_offset(const char *base
, const char *pdata_end
)
93 size_t next_entry_offset
= (size_t)IVAL(base
,0);
95 if (next_entry_offset
== 0 ||
96 base
+ next_entry_offset
< base
||
97 base
+ next_entry_offset
> pdata_end
) {
98 next_entry_offset
= pdata_end
- base
;
100 return next_entry_offset
;
103 /****************************************************************************
104 Interpret a long filename structure - this is mostly guesses at the moment.
105 The length of the structure is returned
106 The structure of a long filename depends on the info level.
107 SMB_FIND_FILE_BOTH_DIRECTORY_INFO is used
108 by NT and SMB_FIND_EA_SIZE is used by OS/2
109 ****************************************************************************/
111 static size_t interpret_long_filename(TALLOC_CTX
*ctx
,
112 struct cli_state
*cli
,
114 const char *base_ptr
,
115 uint16_t recv_flags2
,
117 const char *pdata_end
,
118 struct file_info
*finfo
,
119 uint32_t *p_resume_key
,
120 DATA_BLOB
*p_last_name_raw
)
124 const char *base
= p
;
126 data_blob_free(p_last_name_raw
);
134 case SMB_FIND_INFO_STANDARD
: /* OS/2 understands this */
135 /* these dates are converted to GMT by
137 if (pdata_end
- base
< 27) {
138 return pdata_end
- base
;
141 * What we're returning here as ctime_ts is
142 * actually the server create time.
144 finfo
->btime_ts
= convert_time_t_to_timespec(
146 smb1cli_conn_server_time_zone(
148 finfo
->ctime_ts
= convert_time_t_to_timespec(
149 make_unix_date2(p
+4, smb1cli_conn_server_time_zone(cli
->conn
)));
150 finfo
->atime_ts
= convert_time_t_to_timespec(
151 make_unix_date2(p
+8, smb1cli_conn_server_time_zone(cli
->conn
)));
152 finfo
->mtime_ts
= convert_time_t_to_timespec(
153 make_unix_date2(p
+12, smb1cli_conn_server_time_zone(cli
->conn
)));
154 finfo
->size
= IVAL(p
,16);
155 finfo
->attr
= SVAL(p
,24);
158 if (recv_flags2
& FLAGS2_UNICODE_STRINGS
) {
159 p
+= ucs2_align(base_ptr
, p
, STR_UNICODE
);
162 /* We can safely use len here (which is required by OS/2)
163 * and the NAS-BASIC server instead of +2 or +1 as the
164 * STR_TERMINATE flag below is
165 * actually used as the length calculation.
166 * The len is merely an upper bound.
167 * Due to the explicit 2 byte null termination
168 * in cli_receive_trans/cli_receive_nt_trans
169 * we know this is safe. JRA + kukks
172 if (p
+ len
> pdata_end
) {
173 return pdata_end
- base
;
176 /* the len+2 below looks strange but it is
177 important to cope with the differences
178 between win2000 and win9x for this call
180 ret
= pull_string_talloc(ctx
,
187 if (ret
== (size_t)-1) {
188 return pdata_end
- base
;
191 return PTR_DIFF(p
, base
);
193 case SMB_FIND_EA_SIZE
: /* this is what OS/2 uses mostly */
194 /* these dates are converted to GMT by
196 if (pdata_end
- base
< 31) {
197 return pdata_end
- base
;
200 * What we're returning here as ctime_ts is
201 * actually the server create time.
203 finfo
->btime_ts
= convert_time_t_to_timespec(
205 smb1cli_conn_server_time_zone(
207 finfo
->ctime_ts
= convert_time_t_to_timespec(
208 make_unix_date2(p
+4, smb1cli_conn_server_time_zone(cli
->conn
)));
209 finfo
->atime_ts
= convert_time_t_to_timespec(
210 make_unix_date2(p
+8, smb1cli_conn_server_time_zone(cli
->conn
)));
211 finfo
->mtime_ts
= convert_time_t_to_timespec(
212 make_unix_date2(p
+12, smb1cli_conn_server_time_zone(cli
->conn
)));
213 finfo
->size
= IVAL(p
,16);
214 finfo
->attr
= SVAL(p
,24);
217 /* check for unisys! */
218 if (p
+ len
+ 1 > pdata_end
) {
219 return pdata_end
- base
;
221 ret
= pull_string_talloc(ctx
,
228 if (ret
== (size_t)-1) {
229 return pdata_end
- base
;
232 return PTR_DIFF(p
, base
) + 1;
234 case SMB_FIND_FILE_BOTH_DIRECTORY_INFO
: /* NT uses this, but also accepts 2 */
236 size_t namelen
, slen
;
238 if (pdata_end
- base
< 94) {
239 return pdata_end
- base
;
242 p
+= 4; /* next entry offset */
245 *p_resume_key
= IVAL(p
,0);
247 p
+= 4; /* fileindex */
249 /* Offset zero is "create time", not "change time". */
251 finfo
->atime_ts
= interpret_long_date(BVAL(p
, 0));
253 finfo
->mtime_ts
= interpret_long_date(BVAL(p
, 0));
255 finfo
->ctime_ts
= interpret_long_date(BVAL(p
, 0));
257 finfo
->size
= BVAL(p
,0);
259 p
+= 8; /* alloc size */
260 finfo
->attr
= IVAL(p
,0);
264 p
+= 4; /* EA size */
267 /* Bad short name length. */
268 return pdata_end
- base
;
271 ret
= pull_string_talloc(ctx
,
278 if (ret
== (size_t)-1) {
279 return pdata_end
- base
;
281 p
+= 24; /* short name? */
282 if (p
+ namelen
< p
|| p
+ namelen
> pdata_end
) {
283 return pdata_end
- base
;
285 ret
= pull_string_talloc(ctx
,
292 if (ret
== (size_t)-1) {
293 return pdata_end
- base
;
296 /* To be robust in the face of unicode conversion failures
297 we need to copy the raw bytes of the last name seen here.
298 Namelen doesn't include the terminating unicode null, so
301 *p_last_name_raw
= data_blob(NULL
, namelen
+ 2);
302 memcpy(p_last_name_raw
->data
, p
, namelen
);
303 SSVAL(p_last_name_raw
->data
, namelen
, 0);
305 return calc_next_entry_offset(base
, pdata_end
);
309 DEBUG(1,("Unknown long filename format %d\n",level
));
310 return calc_next_entry_offset(base
, pdata_end
);
313 /****************************************************************************
314 Interpret a short filename structure.
315 The length of the structure is returned.
316 ****************************************************************************/
318 static bool interpret_short_filename(TALLOC_CTX
*ctx
,
319 struct cli_state
*cli
,
321 struct file_info
*finfo
)
326 finfo
->attr
= CVAL(p
,21);
328 /* We don't get birth time. */
329 finfo
->btime_ts
.tv_sec
= 0;
330 finfo
->btime_ts
.tv_nsec
= 0;
331 /* this date is converted to GMT by make_unix_date */
332 finfo
->ctime_ts
.tv_sec
= make_unix_date(p
+22, smb1cli_conn_server_time_zone(cli
->conn
));
333 finfo
->ctime_ts
.tv_nsec
= 0;
334 finfo
->mtime_ts
.tv_sec
= finfo
->atime_ts
.tv_sec
= finfo
->ctime_ts
.tv_sec
;
335 finfo
->mtime_ts
.tv_nsec
= finfo
->atime_ts
.tv_nsec
= 0;
336 finfo
->size
= IVAL(p
,26);
337 ret
= pull_string_talloc(ctx
,
344 if (ret
== (size_t)-1) {
349 finfo
->short_name
= talloc_strdup(ctx
, finfo
->name
);
350 if (finfo
->short_name
== NULL
) {
357 struct cli_list_old_state
{
358 struct tevent_context
*ev
;
359 struct cli_state
*cli
;
364 uint8_t search_status
[23];
370 static void cli_list_old_done(struct tevent_req
*subreq
);
372 static struct tevent_req
*cli_list_old_send(TALLOC_CTX
*mem_ctx
,
373 struct tevent_context
*ev
,
374 struct cli_state
*cli
,
378 struct tevent_req
*req
, *subreq
;
379 struct cli_list_old_state
*state
;
381 static const uint16_t zero
= 0;
382 uint32_t usable_space
;
384 req
= tevent_req_create(mem_ctx
, &state
, struct cli_list_old_state
);
390 state
->attribute
= attribute
;
392 state
->mask
= talloc_strdup(state
, mask
);
393 if (tevent_req_nomem(state
->mask
, req
)) {
394 return tevent_req_post(req
, ev
);
396 state
->mask
= smb1_dfs_share_path(state
, cli
, state
->mask
);
397 if (tevent_req_nomem(state
->mask
, req
)) {
398 return tevent_req_post(req
, ev
);
400 usable_space
= cli_state_available_size(cli
, 100);
401 state
->num_asked
= usable_space
/ DIR_STRUCT_SIZE
;
403 SSVAL(state
->vwv
+ 0, 0, state
->num_asked
);
404 SSVAL(state
->vwv
+ 1, 0, state
->attribute
);
406 bytes
= talloc_array(state
, uint8_t, 1);
407 if (tevent_req_nomem(bytes
, req
)) {
408 return tevent_req_post(req
, ev
);
411 bytes
= smb_bytes_push_str(bytes
,
412 smbXcli_conn_use_unicode(cli
->conn
),
414 strlen(state
->mask
)+1,
417 bytes
= smb_bytes_push_bytes(bytes
, 5, (const uint8_t *)&zero
, 2);
418 if (tevent_req_nomem(bytes
, req
)) {
419 return tevent_req_post(req
, ev
);
422 subreq
= cli_smb_send(state
, state
->ev
, state
->cli
, SMBsearch
, 0, 0,
423 2, state
->vwv
, talloc_get_size(bytes
), bytes
);
424 if (tevent_req_nomem(subreq
, req
)) {
425 return tevent_req_post(req
, ev
);
427 tevent_req_set_callback(subreq
, cli_list_old_done
, req
);
431 static void cli_list_old_done(struct tevent_req
*subreq
)
433 struct tevent_req
*req
= tevent_req_callback_data(
434 subreq
, struct tevent_req
);
435 struct cli_list_old_state
*state
= tevent_req_data(
436 req
, struct cli_list_old_state
);
447 status
= cli_smb_recv(subreq
, state
, NULL
, 0, &wct
, &vwv
, &num_bytes
,
449 if (!NT_STATUS_IS_OK(status
)
450 && !NT_STATUS_EQUAL(status
, NT_STATUS_DOS(ERRDOS
, ERRnofiles
))
451 && !NT_STATUS_EQUAL(status
, STATUS_NO_MORE_FILES
)) {
453 tevent_req_nterror(req
, status
);
456 if (NT_STATUS_EQUAL(status
, NT_STATUS_DOS(ERRDOS
, ERRnofiles
))
457 || NT_STATUS_EQUAL(status
, STATUS_NO_MORE_FILES
)) {
463 req
, NT_STATUS_INVALID_NETWORK_RESPONSE
);
466 received
= SVAL(vwv
+ 0, 0);
471 * I don't think this can wrap. received is
472 * initialized from a 16-bit value.
474 if (num_bytes
< ((uint32_t)received
* DIR_STRUCT_SIZE
+ 3)) {
477 req
, NT_STATUS_INVALID_NETWORK_RESPONSE
);
481 dirlist_len
= talloc_get_size(state
->dirlist
);
483 tmp
= talloc_realloc(
484 state
, state
->dirlist
, uint8_t,
485 dirlist_len
+ received
* DIR_STRUCT_SIZE
);
486 if (tevent_req_nomem(tmp
, req
)) {
489 state
->dirlist
= tmp
;
490 memcpy(state
->dirlist
+ dirlist_len
, bytes
+ 3,
491 received
* DIR_STRUCT_SIZE
);
493 SSVAL(state
->search_status
, 0, 21);
494 memcpy(state
->search_status
+ 2,
495 bytes
+ 3 + (received
-1)*DIR_STRUCT_SIZE
, 21);
498 if (state
->first
|| state
->done
) {
499 tevent_req_done(req
);
503 state
->num_asked
= 0;
508 state
->first
= false;
510 SSVAL(state
->vwv
+ 0, 0, state
->num_asked
);
511 SSVAL(state
->vwv
+ 1, 0, state
->attribute
);
513 bytes
= talloc_array(state
, uint8_t, 1);
514 if (tevent_req_nomem(bytes
, req
)) {
518 bytes
= smb_bytes_push_str(bytes
, smbXcli_conn_use_unicode(state
->cli
->conn
), "",
520 bytes
= smb_bytes_push_bytes(bytes
, 5, state
->search_status
,
521 sizeof(state
->search_status
));
522 if (tevent_req_nomem(bytes
, req
)) {
525 subreq
= cli_smb_send(state
, state
->ev
, state
->cli
, cmd
, 0, 0,
526 2, state
->vwv
, talloc_get_size(bytes
), bytes
);
527 if (tevent_req_nomem(subreq
, req
)) {
530 tevent_req_set_callback(subreq
, cli_list_old_done
, req
);
533 static NTSTATUS
cli_list_old_recv(struct tevent_req
*req
, TALLOC_CTX
*mem_ctx
,
534 struct file_info
**pfinfo
)
536 struct cli_list_old_state
*state
= tevent_req_data(
537 req
, struct cli_list_old_state
);
539 size_t i
, num_received
;
540 struct file_info
*finfo
;
542 if (tevent_req_is_nterror(req
, &status
)) {
546 if (state
->dirlist
== NULL
) {
551 num_received
= talloc_array_length(state
->dirlist
) / DIR_STRUCT_SIZE
;
553 finfo
= talloc_array(mem_ctx
, struct file_info
, num_received
);
555 return NT_STATUS_NO_MEMORY
;
558 for (i
=0; i
<num_received
; i
++) {
559 if (!interpret_short_filename(
561 (char *)state
->dirlist
+ i
* DIR_STRUCT_SIZE
,
564 return NT_STATUS_NO_MEMORY
;
566 if (finfo
->name
== NULL
) {
568 return NT_STATUS_INVALID_NETWORK_RESPONSE
;
570 status
= is_bad_finfo_name(state
->cli
, finfo
);
571 if (!NT_STATUS_IS_OK(status
)) {
572 smbXcli_conn_disconnect(state
->cli
->conn
, status
);
577 TALLOC_FREE(state
->dirlist
);
582 NTSTATUS
cli_list_old(struct cli_state
*cli
, const char *mask
,
584 NTSTATUS (*fn
)(struct file_info
*,
585 const char *, void *), void *state
)
587 TALLOC_CTX
*frame
= talloc_stackframe();
588 struct tevent_context
*ev
;
589 struct tevent_req
*req
;
590 NTSTATUS status
= NT_STATUS_NO_MEMORY
;
591 struct file_info
*finfo
= NULL
;
594 if (smbXcli_conn_has_async_calls(cli
->conn
)) {
596 * Can't use sync call while an async call is in flight
598 status
= NT_STATUS_INVALID_PARAMETER
;
601 ev
= samba_tevent_context_init(frame
);
605 req
= cli_list_old_send(frame
, ev
, cli
, mask
, attribute
);
609 if (!tevent_req_poll_ntstatus(req
, ev
, &status
)) {
612 status
= cli_list_old_recv(req
, frame
, &finfo
);
613 if (!NT_STATUS_IS_OK(status
)) {
616 num_finfo
= talloc_array_length(finfo
);
617 for (i
=0; i
<num_finfo
; i
++) {
618 status
= fn(&finfo
[i
], mask
, state
);
619 if (!NT_STATUS_IS_OK(status
)) {
628 struct cli_list_trans_state
{
629 struct tevent_context
*ev
;
630 struct cli_state
*cli
;
637 uint16_t max_matches
;
646 struct file_info
*finfo
;
649 static void cli_list_trans_done(struct tevent_req
*subreq
);
651 static struct tevent_req
*cli_list_trans_send(TALLOC_CTX
*mem_ctx
,
652 struct tevent_context
*ev
,
653 struct cli_state
*cli
,
658 struct tevent_req
*req
, *subreq
;
659 struct cli_list_trans_state
*state
;
661 uint16_t additional_flags2
= 0;
663 req
= tevent_req_create(mem_ctx
, &state
,
664 struct cli_list_trans_state
);
670 state
->mask
= smb1_dfs_share_path(state
, cli
, mask
);
671 if (tevent_req_nomem(state
->mask
, req
)) {
672 return tevent_req_post(req
, ev
);
674 state
->attribute
= attribute
;
675 state
->info_level
= info_level
;
676 state
->loop_count
= 0;
679 state
->max_matches
= 1366; /* Match W2k */
681 SSVAL(&state
->setup
[0], 0, TRANSACT2_FINDFIRST
);
683 state
->param
= talloc_array(state
, uint8_t, 12);
684 if (tevent_req_nomem(state
->param
, req
)) {
685 return tevent_req_post(req
, ev
);
688 SSVAL(state
->param
, 0, state
->attribute
);
689 SSVAL(state
->param
, 2, state
->max_matches
);
690 SSVAL(state
->param
, 4,
691 FLAG_TRANS2_FIND_REQUIRE_RESUME
692 |FLAG_TRANS2_FIND_CLOSE_IF_END
693 |(cli
->backup_intent
? FLAG_TRANS2_FIND_BACKUP_INTENT
: 0));
694 SSVAL(state
->param
, 6, state
->info_level
);
695 SIVAL(state
->param
, 8, 0);
697 state
->param
= trans2_bytes_push_str(state
->param
, smbXcli_conn_use_unicode(cli
->conn
),
698 state
->mask
, strlen(state
->mask
)+1,
700 if (tevent_req_nomem(state
->param
, req
)) {
701 return tevent_req_post(req
, ev
);
704 if (clistr_is_previous_version_path(state
->mask
)) {
705 additional_flags2
= FLAGS2_REPARSE_PATH
;
708 param_len
= talloc_get_size(state
->param
);
710 subreq
= cli_trans_send(state
, state
->ev
, state
->cli
, additional_flags2
,
711 SMBtrans2
, NULL
, -1, 0, 0,
713 state
->param
, param_len
, 10,
714 NULL
, 0, CLI_BUFFER_SIZE
);
715 if (tevent_req_nomem(subreq
, req
)) {
716 return tevent_req_post(req
, ev
);
718 tevent_req_set_callback(subreq
, cli_list_trans_done
, req
);
722 static void cli_list_trans_done(struct tevent_req
*subreq
)
724 struct tevent_req
*req
= tevent_req_callback_data(
725 subreq
, struct tevent_req
);
726 struct cli_list_trans_state
*state
= tevent_req_data(
727 req
, struct cli_list_trans_state
);
735 struct file_info
*tmp
;
736 size_t old_num_finfo
;
737 uint16_t recv_flags2
;
741 uint32_t resume_key
= 0;
743 DATA_BLOB last_name_raw
;
744 struct file_info
*finfo
= NULL
;
746 uint16_t additional_flags2
= 0;
748 min_param
= (state
->first
? 6 : 4);
750 status
= cli_trans_recv(subreq
, talloc_tos(), &recv_flags2
,
752 ¶m
, min_param
, &num_param
,
753 &data
, 0, &num_data
);
755 if (!NT_STATUS_IS_OK(status
)) {
757 * TODO: retry, OS/2 nofiles
759 tevent_req_nterror(req
, status
);
764 state
->ff_dir_handle
= SVAL(param
, 0);
765 ff_searchcount
= SVAL(param
, 2);
766 ff_eos
= SVAL(param
, 4) != 0;
768 ff_searchcount
= SVAL(param
, 0);
769 ff_eos
= SVAL(param
, 2) != 0;
772 old_num_finfo
= talloc_array_length(state
->finfo
);
774 tmp
= talloc_realloc(state
, state
->finfo
, struct file_info
,
775 old_num_finfo
+ ff_searchcount
);
776 if (tevent_req_nomem(tmp
, req
)) {
781 p2
= p
= (char *)data
;
782 data_end
= (char *)data
+ num_data
;
783 last_name_raw
= data_blob_null
;
785 for (i
=0; i
<ff_searchcount
; i
++) {
786 if (p2
>= data_end
) {
790 if ((state
->info_level
== SMB_FIND_FILE_BOTH_DIRECTORY_INFO
)
791 && (i
== ff_searchcount
-1)) {
792 /* Last entry - fixup the last offset length. */
793 SIVAL(p2
, 0, PTR_DIFF((data
+ num_data
), p2
));
796 data_blob_free(&last_name_raw
);
798 finfo
= &state
->finfo
[old_num_finfo
+ i
];
800 p2
+= interpret_long_filename(
801 state
->finfo
, /* Stick fname to the array as such */
802 state
->cli
, state
->info_level
,
803 (char *)data
, recv_flags2
, p2
,
804 data_end
, finfo
, &resume_key
, &last_name_raw
);
806 if (finfo
->name
== NULL
) {
807 DEBUG(1, ("cli_list: Error: unable to parse name from "
808 "info level %d\n", state
->info_level
));
809 tevent_req_nterror(req
,
810 NT_STATUS_INVALID_NETWORK_RESPONSE
);
814 status
= is_bad_finfo_name(state
->cli
, finfo
);
815 if (!NT_STATUS_IS_OK(status
)) {
816 smbXcli_conn_disconnect(state
->cli
->conn
, status
);
817 tevent_req_nterror(req
, status
);
821 if (!state
->first
&& (state
->mask
[0] != '\0') &&
822 strcsequal(finfo
->name
, state
->mask
)) {
823 DEBUG(1, ("Error: Looping in FIND_NEXT as name %s has "
824 "already been seen?\n", finfo
->name
));
830 if (ff_searchcount
== 0) {
838 * Shrink state->finfo to the real length we received
840 tmp
= talloc_realloc(state
, state
->finfo
, struct file_info
,
842 if (tevent_req_nomem(tmp
, req
)) {
847 state
->first
= false;
850 data_blob_free(&last_name_raw
);
851 tevent_req_done(req
);
855 TALLOC_FREE(state
->mask
);
856 state
->mask
= talloc_strdup(state
, finfo
->name
);
857 if (tevent_req_nomem(state
->mask
, req
)) {
861 SSVAL(&state
->setup
[0], 0, TRANSACT2_FINDNEXT
);
863 param
= talloc_realloc(state
, state
->param
, uint8_t, 12);
864 if (tevent_req_nomem(param
, req
)) {
867 state
->param
= param
;
869 SSVAL(param
, 0, state
->ff_dir_handle
);
870 SSVAL(param
, 2, state
->max_matches
); /* max count */
871 SSVAL(param
, 4, state
->info_level
);
873 * For W2K servers serving out FAT filesystems we *must* set
874 * the resume key. If it's not FAT then it's returned as zero.
876 SIVAL(param
, 6, resume_key
); /* ff_resume_key */
878 * NB. *DON'T* use continue here. If you do it seems that W2K
879 * and brethren can miss filenames. Use last filename
880 * continue instead. JRA
882 SSVAL(param
, 10, (FLAG_TRANS2_FIND_REQUIRE_RESUME
883 |FLAG_TRANS2_FIND_CLOSE_IF_END
884 |(state
->cli
->backup_intent
? FLAG_TRANS2_FIND_BACKUP_INTENT
: 0)));
885 if (last_name_raw
.length
) {
886 state
->param
= trans2_bytes_push_bytes(state
->param
,
888 last_name_raw
.length
);
889 if (tevent_req_nomem(state
->param
, req
)) {
892 data_blob_free(&last_name_raw
);
894 state
->param
= trans2_bytes_push_str(state
->param
,
895 smbXcli_conn_use_unicode(state
->cli
->conn
),
897 strlen(state
->mask
)+1,
899 if (tevent_req_nomem(state
->param
, req
)) {
903 param_len
= talloc_get_size(state
->param
);
905 if (clistr_is_previous_version_path(state
->mask
)) {
906 additional_flags2
= FLAGS2_REPARSE_PATH
;
909 subreq
= cli_trans_send(state
, state
->ev
, state
->cli
, additional_flags2
,
910 SMBtrans2
, NULL
, -1, 0, 0,
912 state
->param
, param_len
, 10,
913 NULL
, 0, CLI_BUFFER_SIZE
);
914 if (tevent_req_nomem(subreq
, req
)) {
917 tevent_req_set_callback(subreq
, cli_list_trans_done
, req
);
920 static NTSTATUS
cli_list_trans_recv(struct tevent_req
*req
,
922 struct file_info
**finfo
)
924 struct cli_list_trans_state
*state
= tevent_req_data(
925 req
, struct cli_list_trans_state
);
928 if (tevent_req_is_nterror(req
, &status
)) {
931 *finfo
= talloc_move(mem_ctx
, &state
->finfo
);
935 struct cli_list_state
{
936 struct tevent_context
*ev
;
937 struct tevent_req
*subreq
;
938 NTSTATUS (*recv_fn
)(struct tevent_req
*req
, TALLOC_CTX
*mem_ctx
,
939 struct file_info
**finfo
);
940 struct file_info
*finfo
;
944 static void cli_list_done(struct tevent_req
*subreq
);
946 struct tevent_req
*cli_list_send(TALLOC_CTX
*mem_ctx
,
947 struct tevent_context
*ev
,
948 struct cli_state
*cli
,
953 struct tevent_req
*req
= NULL
;
954 struct cli_list_state
*state
;
955 enum protocol_types proto
= smbXcli_conn_protocol(cli
->conn
);
957 req
= tevent_req_create(mem_ctx
, &state
, struct cli_list_state
);
963 if (proto
>= PROTOCOL_SMB2_02
) {
964 state
->subreq
= cli_smb2_list_send(state
, ev
, cli
, mask
,
966 state
->recv_fn
= cli_smb2_list_recv
;
967 } else if (proto
>= PROTOCOL_LANMAN2
) {
968 state
->subreq
= cli_list_trans_send(
969 state
, ev
, cli
, mask
, attribute
, info_level
);
970 state
->recv_fn
= cli_list_trans_recv
;
972 state
->subreq
= cli_list_old_send(
973 state
, ev
, cli
, mask
, attribute
);
974 state
->recv_fn
= cli_list_old_recv
;
976 if (tevent_req_nomem(state
->subreq
, req
)) {
977 return tevent_req_post(req
, ev
);
979 tevent_req_set_callback(state
->subreq
, cli_list_done
, req
);
983 static void cli_list_done(struct tevent_req
*subreq
)
985 struct tevent_req
*req
= tevent_req_callback_data(
986 subreq
, struct tevent_req
);
987 struct cli_list_state
*state
= tevent_req_data(
988 req
, struct cli_list_state
);
991 SMB_ASSERT(subreq
== state
->subreq
);
994 * We don't want to be called by the lowerlevel routines
995 * from within state->recv_fn()
997 tevent_req_set_callback(subreq
, NULL
, NULL
);
999 status
= state
->recv_fn(subreq
, state
, &state
->finfo
);
1000 if (NT_STATUS_EQUAL(status
, NT_STATUS_RETRY
)) {
1001 /* We'll get back here */
1002 tevent_req_set_callback(subreq
, cli_list_done
, req
);
1006 if (tevent_req_nterror(req
, status
)) {
1009 tevent_req_notify_callback(req
);
1012 NTSTATUS
cli_list_recv(
1013 struct tevent_req
*req
,
1014 TALLOC_CTX
*mem_ctx
,
1015 struct file_info
**pfinfo
)
1017 struct cli_list_state
*state
= tevent_req_data(
1018 req
, struct cli_list_state
);
1020 struct file_info
*finfo
= NULL
;
1024 in_progress
= tevent_req_is_in_progress(req
);
1027 if (!tevent_req_is_nterror(req
, &status
)) {
1028 status
= NT_STATUS_NO_MORE_FILES
;
1033 if (state
->finfo
== NULL
) {
1034 status
= state
->recv_fn(state
->subreq
, state
, &state
->finfo
);
1036 if (NT_STATUS_EQUAL(status
, NT_STATUS_RETRY
)) {
1037 tevent_req_set_callback(
1038 state
->subreq
, cli_list_done
, req
);
1039 return NT_STATUS_RETRY
;
1042 if (NT_STATUS_IS_OK(status
) && (state
->finfo
== NULL
)) {
1043 status
= NT_STATUS_NO_MORE_FILES
;
1046 if (tevent_req_nterror(req
, status
)) {
1050 state
->num_received
= 0;
1053 num_results
= talloc_array_length(state
->finfo
);
1055 if (num_results
== 1) {
1056 finfo
= talloc_move(mem_ctx
, &state
->finfo
);
1058 struct file_info
*src_finfo
=
1059 &state
->finfo
[state
->num_received
];
1061 finfo
= talloc(mem_ctx
, struct file_info
);
1062 if (finfo
== NULL
) {
1063 return NT_STATUS_NO_MEMORY
;
1065 *finfo
= *src_finfo
;
1066 finfo
->name
= talloc_move(finfo
, &src_finfo
->name
);
1067 finfo
->short_name
= talloc_move(finfo
, &src_finfo
->short_name
);
1070 state
->num_received
+= 1;
1072 if (state
->num_received
== num_results
) {
1073 TALLOC_FREE(state
->finfo
);
1076 tevent_req_defer_callback(req
, state
->ev
);
1077 tevent_req_notify_callback(req
);
1080 return NT_STATUS_OK
;
1083 struct cli_list_sync_state
{
1086 NTSTATUS (*fn
)(struct file_info
*finfo
,
1088 void *private_data
);
1091 bool processed_file
;
1094 static void cli_list_sync_cb(struct tevent_req
*subreq
)
1096 struct cli_list_sync_state
*state
=
1097 tevent_req_callback_data_void(subreq
);
1098 struct file_info
*finfo
;
1101 state
->status
= cli_list_recv(subreq
, talloc_tos(), &finfo
);
1102 /* No TALLOC_FREE(subreq), we get here more than once */
1104 if (NT_STATUS_EQUAL(state
->status
, NT_STATUS_RETRY
)) {
1106 * The lowlevel SMB call was rearmed, we'll get back
1107 * here when it's done.
1109 state
->status
= NT_STATUS_OK
;
1113 if (!NT_STATUS_IS_OK(state
->status
)) {
1117 ok
= dir_check_ftype(finfo
->attr
, state
->attribute
);
1120 * Only process if attributes match. On SMB1 server
1121 * does this, so on SMB2 we need to emulate in the
1124 * https://bugzilla.samba.org/show_bug.cgi?id=10260
1129 state
->status
= state
->fn(finfo
, state
->mask
, state
->private_data
);
1131 state
->processed_file
= true;
1136 NTSTATUS
cli_list(struct cli_state
*cli
,
1139 NTSTATUS (*fn
)(struct file_info
*finfo
,
1141 void *private_data
),
1144 TALLOC_CTX
*frame
= NULL
;
1145 struct cli_list_sync_state state
= {
1147 .attribute
= attribute
,
1149 .private_data
= private_data
,
1151 struct tevent_context
*ev
;
1152 struct tevent_req
*req
;
1153 NTSTATUS status
= NT_STATUS_NO_MEMORY
;
1154 uint16_t info_level
;
1155 enum protocol_types proto
= smbXcli_conn_protocol(cli
->conn
);
1157 frame
= talloc_stackframe();
1159 if (smbXcli_conn_has_async_calls(cli
->conn
)) {
1161 * Can't use sync call while an async call is in flight
1163 status
= NT_STATUS_INVALID_PARAMETER
;
1166 ev
= samba_tevent_context_init(frame
);
1171 if (proto
>= PROTOCOL_SMB2_02
) {
1172 info_level
= SMB2_FIND_ID_BOTH_DIRECTORY_INFO
;
1174 info_level
= (smb1cli_conn_capabilities(cli
->conn
) & CAP_NT_SMBS
)
1175 ? SMB_FIND_FILE_BOTH_DIRECTORY_INFO
: SMB_FIND_INFO_STANDARD
;
1178 req
= cli_list_send(frame
, ev
, cli
, mask
, attribute
, info_level
);
1182 tevent_req_set_callback(req
, cli_list_sync_cb
, &state
);
1184 if (!tevent_req_poll_ntstatus(req
, ev
, &status
)) {
1188 status
= state
.status
;
1190 if (NT_STATUS_EQUAL(status
, NT_STATUS_NO_MORE_FILES
)) {
1191 status
= NT_STATUS_OK
;
1194 if (NT_STATUS_IS_OK(status
) && !state
.processed_file
) {
1195 status
= NT_STATUS_NO_SUCH_FILE
;