VERSION: Disable GIT_SNAPSHOT for the 4.11.8 release.
[Samba.git] / source3 / libsmb / clilist.c
blob4f518339e2bdb83d99a93cea9ad5ef48f828845f
1 /*
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/>.
20 #include "includes.h"
21 #include "libsmb/libsmb.h"
22 #include "../lib/util/tevent_ntstatus.h"
23 #include "async_smb.h"
24 #include "trans2.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;
43 if (windows_names) {
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;
53 return NT_STATUS_OK;
56 /****************************************************************************
57 Check if a returned directory name is safe. Disconnect if server is
58 sending bad names.
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");
74 return status;
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");
81 return status;
84 return NT_STATUS_OK;
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,
113 int level,
114 const char *base_ptr,
115 uint16_t recv_flags2,
116 const char *p,
117 const char *pdata_end,
118 struct file_info *finfo,
119 uint32_t *p_resume_key,
120 DATA_BLOB *p_last_name_raw)
122 int len;
123 size_t ret;
124 const char *base = p;
126 data_blob_free(p_last_name_raw);
128 if (p_resume_key) {
129 *p_resume_key = 0;
131 ZERO_STRUCTP(finfo);
133 switch (level) {
134 case SMB_FIND_INFO_STANDARD: /* OS/2 understands this */
135 /* these dates are converted to GMT by
136 make_unix_date */
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(
145 make_unix_date2(p+4,
146 smb1cli_conn_server_time_zone(
147 cli->conn)));
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->mode = CVAL(p,24);
156 len = CVAL(p, 26);
157 p += 27;
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
179 (tridge) */
180 ret = clistr_pull_talloc(ctx,
181 base_ptr,
182 recv_flags2,
183 &finfo->name,
185 len+2,
186 STR_TERMINATE);
187 if (ret == (size_t)-1) {
188 return pdata_end - base;
190 p += ret;
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
195 make_unix_date */
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(
204 make_unix_date2(p+4,
205 smb1cli_conn_server_time_zone(
206 cli->conn)));
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->mode = CVAL(p,24);
215 len = CVAL(p, 30);
216 p += 31;
217 /* check for unisys! */
218 if (p + len + 1 > pdata_end) {
219 return pdata_end - base;
221 ret = clistr_pull_talloc(ctx,
222 base_ptr,
223 recv_flags2,
224 &finfo->name,
226 len,
227 STR_NOALIGN);
228 if (ret == (size_t)-1) {
229 return pdata_end - base;
231 p += ret;
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 */
244 if (p_resume_key) {
245 *p_resume_key = IVAL(p,0);
247 p += 4; /* fileindex */
249 /* Offset zero is "create time", not "change time". */
250 p += 8;
251 finfo->atime_ts = interpret_long_date(p);
252 p += 8;
253 finfo->mtime_ts = interpret_long_date(p);
254 p += 8;
255 finfo->ctime_ts = interpret_long_date(p);
256 p += 8;
257 finfo->size = IVAL2_TO_SMB_BIG_UINT(p,0);
258 p += 8;
259 p += 8; /* alloc size */
260 finfo->mode = CVAL(p,0);
261 p += 4;
262 namelen = IVAL(p,0);
263 p += 4;
264 p += 4; /* EA size */
265 slen = CVAL(p, 0);
266 if (slen > 24) {
267 /* Bad short name length. */
268 return pdata_end - base;
270 p += 2;
271 ret = clistr_pull_talloc(ctx,
272 base_ptr,
273 recv_flags2,
274 &finfo->short_name,
276 slen,
277 STR_UNICODE);
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 = clistr_pull_talloc(ctx,
286 base_ptr,
287 recv_flags2,
288 &finfo->name,
290 namelen,
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
299 copy it here. */
301 if (p_last_name_raw) {
302 *p_last_name_raw = data_blob(NULL, namelen+2);
303 memcpy(p_last_name_raw->data, p, namelen);
304 SSVAL(p_last_name_raw->data, namelen, 0);
306 return calc_next_entry_offset(base, pdata_end);
310 DEBUG(1,("Unknown long filename format %d\n",level));
311 return calc_next_entry_offset(base, pdata_end);
314 /****************************************************************************
315 Interpret a short filename structure.
316 The length of the structure is returned.
317 ****************************************************************************/
319 static bool interpret_short_filename(TALLOC_CTX *ctx,
320 struct cli_state *cli,
321 char *p,
322 struct file_info *finfo)
324 size_t ret;
325 ZERO_STRUCTP(finfo);
327 finfo->mode = CVAL(p,21);
329 /* We don't get birth time. */
330 finfo->btime_ts.tv_sec = 0;
331 finfo->btime_ts.tv_nsec = 0;
332 /* this date is converted to GMT by make_unix_date */
333 finfo->ctime_ts.tv_sec = make_unix_date(p+22, smb1cli_conn_server_time_zone(cli->conn));
334 finfo->ctime_ts.tv_nsec = 0;
335 finfo->mtime_ts.tv_sec = finfo->atime_ts.tv_sec = finfo->ctime_ts.tv_sec;
336 finfo->mtime_ts.tv_nsec = finfo->atime_ts.tv_nsec = 0;
337 finfo->size = IVAL(p,26);
338 ret = clistr_pull_talloc(ctx,
339 NULL,
341 &finfo->name,
342 p+30,
344 STR_ASCII);
345 if (ret == (size_t)-1) {
346 return false;
349 if (finfo->name) {
350 finfo->short_name = talloc_strdup(ctx, finfo->name);
351 if (finfo->short_name == NULL) {
352 return false;
355 return true;
358 struct cli_list_old_state {
359 struct tevent_context *ev;
360 struct cli_state *cli;
361 uint16_t vwv[2];
362 char *mask;
363 int num_asked;
364 uint16_t attribute;
365 uint8_t search_status[23];
366 bool first;
367 bool done;
368 uint8_t *dirlist;
371 static void cli_list_old_done(struct tevent_req *subreq);
373 static struct tevent_req *cli_list_old_send(TALLOC_CTX *mem_ctx,
374 struct tevent_context *ev,
375 struct cli_state *cli,
376 const char *mask,
377 uint16_t attribute)
379 struct tevent_req *req, *subreq;
380 struct cli_list_old_state *state;
381 uint8_t *bytes;
382 static const uint16_t zero = 0;
383 uint32_t usable_space;
385 req = tevent_req_create(mem_ctx, &state, struct cli_list_old_state);
386 if (req == NULL) {
387 return NULL;
389 state->ev = ev;
390 state->cli = cli;
391 state->attribute = attribute;
392 state->first = true;
393 state->mask = talloc_strdup(state, mask);
394 if (tevent_req_nomem(state->mask, req)) {
395 return tevent_req_post(req, ev);
397 usable_space = cli_state_available_size(cli, 100);
398 state->num_asked = usable_space / DIR_STRUCT_SIZE;
400 SSVAL(state->vwv + 0, 0, state->num_asked);
401 SSVAL(state->vwv + 1, 0, state->attribute);
403 bytes = talloc_array(state, uint8_t, 1);
404 if (tevent_req_nomem(bytes, req)) {
405 return tevent_req_post(req, ev);
407 bytes[0] = 4;
408 bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), mask,
409 strlen(mask)+1, NULL);
411 bytes = smb_bytes_push_bytes(bytes, 5, (const uint8_t *)&zero, 2);
412 if (tevent_req_nomem(bytes, req)) {
413 return tevent_req_post(req, ev);
416 subreq = cli_smb_send(state, state->ev, state->cli, SMBsearch, 0, 0,
417 2, state->vwv, talloc_get_size(bytes), bytes);
418 if (tevent_req_nomem(subreq, req)) {
419 return tevent_req_post(req, ev);
421 tevent_req_set_callback(subreq, cli_list_old_done, req);
422 return req;
425 static void cli_list_old_done(struct tevent_req *subreq)
427 struct tevent_req *req = tevent_req_callback_data(
428 subreq, struct tevent_req);
429 struct cli_list_old_state *state = tevent_req_data(
430 req, struct cli_list_old_state);
431 NTSTATUS status;
432 uint8_t cmd;
433 uint8_t wct;
434 uint16_t *vwv;
435 uint32_t num_bytes;
436 uint8_t *bytes;
437 uint16_t received;
438 size_t dirlist_len;
439 uint8_t *tmp;
441 status = cli_smb_recv(subreq, state, NULL, 0, &wct, &vwv, &num_bytes,
442 &bytes);
443 if (!NT_STATUS_IS_OK(status)
444 && !NT_STATUS_EQUAL(status, NT_STATUS_DOS(ERRDOS, ERRnofiles))
445 && !NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) {
446 TALLOC_FREE(subreq);
447 tevent_req_nterror(req, status);
448 return;
450 if (NT_STATUS_EQUAL(status, NT_STATUS_DOS(ERRDOS, ERRnofiles))
451 || NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) {
452 received = 0;
453 } else {
454 if (wct < 1) {
455 TALLOC_FREE(subreq);
456 tevent_req_nterror(
457 req, NT_STATUS_INVALID_NETWORK_RESPONSE);
458 return;
460 received = SVAL(vwv + 0, 0);
463 if (received > 0) {
465 * I don't think this can wrap. received is
466 * initialized from a 16-bit value.
468 if (num_bytes < (received * DIR_STRUCT_SIZE + 3)) {
469 TALLOC_FREE(subreq);
470 tevent_req_nterror(
471 req, NT_STATUS_INVALID_NETWORK_RESPONSE);
472 return;
475 dirlist_len = talloc_get_size(state->dirlist);
477 tmp = talloc_realloc(
478 state, state->dirlist, uint8_t,
479 dirlist_len + received * DIR_STRUCT_SIZE);
480 if (tevent_req_nomem(tmp, req)) {
481 return;
483 state->dirlist = tmp;
484 memcpy(state->dirlist + dirlist_len, bytes + 3,
485 received * DIR_STRUCT_SIZE);
487 SSVAL(state->search_status, 0, 21);
488 memcpy(state->search_status + 2,
489 bytes + 3 + (received-1)*DIR_STRUCT_SIZE, 21);
490 cmd = SMBsearch;
491 } else {
492 if (state->first || state->done) {
493 tevent_req_done(req);
494 return;
496 state->done = true;
497 state->num_asked = 0;
498 cmd = SMBfclose;
500 TALLOC_FREE(subreq);
502 state->first = false;
504 SSVAL(state->vwv + 0, 0, state->num_asked);
505 SSVAL(state->vwv + 1, 0, state->attribute);
507 bytes = talloc_array(state, uint8_t, 1);
508 if (tevent_req_nomem(bytes, req)) {
509 return;
511 bytes[0] = 4;
512 bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(state->cli->conn), "",
513 1, NULL);
514 bytes = smb_bytes_push_bytes(bytes, 5, state->search_status,
515 sizeof(state->search_status));
516 if (tevent_req_nomem(bytes, req)) {
517 return;
519 subreq = cli_smb_send(state, state->ev, state->cli, cmd, 0, 0,
520 2, state->vwv, talloc_get_size(bytes), bytes);
521 if (tevent_req_nomem(subreq, req)) {
522 return;
524 tevent_req_set_callback(subreq, cli_list_old_done, req);
527 static NTSTATUS cli_list_old_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
528 struct file_info **pfinfo)
530 struct cli_list_old_state *state = tevent_req_data(
531 req, struct cli_list_old_state);
532 NTSTATUS status;
533 size_t i, num_received;
534 struct file_info *finfo;
536 if (tevent_req_is_nterror(req, &status)) {
537 return status;
540 num_received = talloc_array_length(state->dirlist) / DIR_STRUCT_SIZE;
542 finfo = talloc_array(mem_ctx, struct file_info, num_received);
543 if (finfo == NULL) {
544 return NT_STATUS_NO_MEMORY;
547 for (i=0; i<num_received; i++) {
548 if (!interpret_short_filename(
549 finfo, state->cli,
550 (char *)state->dirlist + i * DIR_STRUCT_SIZE,
551 &finfo[i])) {
552 TALLOC_FREE(finfo);
553 return NT_STATUS_NO_MEMORY;
556 status = is_bad_finfo_name(state->cli, finfo);
557 if (!NT_STATUS_IS_OK(status)) {
558 smbXcli_conn_disconnect(state->cli->conn, status);
559 TALLOC_FREE(finfo);
560 return status;
563 *pfinfo = finfo;
564 return NT_STATUS_OK;
567 NTSTATUS cli_list_old(struct cli_state *cli, const char *mask,
568 uint16_t attribute,
569 NTSTATUS (*fn)(const char *, struct file_info *,
570 const char *, void *), void *state)
572 TALLOC_CTX *frame = talloc_stackframe();
573 struct tevent_context *ev;
574 struct tevent_req *req;
575 NTSTATUS status = NT_STATUS_NO_MEMORY;
576 struct file_info *finfo;
577 size_t i, num_finfo;
579 if (smbXcli_conn_has_async_calls(cli->conn)) {
581 * Can't use sync call while an async call is in flight
583 status = NT_STATUS_INVALID_PARAMETER;
584 goto fail;
586 ev = samba_tevent_context_init(frame);
587 if (ev == NULL) {
588 goto fail;
590 req = cli_list_old_send(frame, ev, cli, mask, attribute);
591 if (req == NULL) {
592 goto fail;
594 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
595 goto fail;
597 status = cli_list_old_recv(req, frame, &finfo);
598 if (!NT_STATUS_IS_OK(status)) {
599 goto fail;
601 num_finfo = talloc_array_length(finfo);
602 for (i=0; i<num_finfo; i++) {
603 status = fn(cli->dfs_mountpoint, &finfo[i], mask, state);
604 if (!NT_STATUS_IS_OK(status)) {
605 goto fail;
608 fail:
609 TALLOC_FREE(frame);
610 return status;
613 struct cli_list_trans_state {
614 struct tevent_context *ev;
615 struct cli_state *cli;
616 char *mask;
617 uint16_t attribute;
618 uint16_t info_level;
620 int loop_count;
621 int total_received;
622 uint16_t max_matches;
623 bool first;
625 int ff_eos;
626 int ff_dir_handle;
628 uint16_t setup[1];
629 uint8_t *param;
631 struct file_info *finfo;
634 static void cli_list_trans_done(struct tevent_req *subreq);
636 static struct tevent_req *cli_list_trans_send(TALLOC_CTX *mem_ctx,
637 struct tevent_context *ev,
638 struct cli_state *cli,
639 const char *mask,
640 uint16_t attribute,
641 uint16_t info_level)
643 struct tevent_req *req, *subreq;
644 struct cli_list_trans_state *state;
645 size_t param_len;
646 uint16_t additional_flags2 = 0;
648 req = tevent_req_create(mem_ctx, &state,
649 struct cli_list_trans_state);
650 if (req == NULL) {
651 return NULL;
653 state->ev = ev;
654 state->cli = cli;
655 state->mask = talloc_strdup(state, mask);
656 if (tevent_req_nomem(state->mask, req)) {
657 return tevent_req_post(req, ev);
659 state->attribute = attribute;
660 state->info_level = info_level;
661 state->loop_count = 0;
662 state->first = true;
664 state->max_matches = 1366; /* Match W2k */
666 SSVAL(&state->setup[0], 0, TRANSACT2_FINDFIRST);
668 state->param = talloc_array(state, uint8_t, 12);
669 if (tevent_req_nomem(state->param, req)) {
670 return tevent_req_post(req, ev);
673 SSVAL(state->param, 0, state->attribute);
674 SSVAL(state->param, 2, state->max_matches);
675 SSVAL(state->param, 4,
676 FLAG_TRANS2_FIND_REQUIRE_RESUME
677 |FLAG_TRANS2_FIND_CLOSE_IF_END
678 |(cli->backup_intent ? FLAG_TRANS2_FIND_BACKUP_INTENT : 0));
679 SSVAL(state->param, 6, state->info_level);
680 SIVAL(state->param, 8, 0);
682 state->param = trans2_bytes_push_str(state->param, smbXcli_conn_use_unicode(cli->conn),
683 state->mask, strlen(state->mask)+1,
684 NULL);
685 if (tevent_req_nomem(state->param, req)) {
686 return tevent_req_post(req, ev);
689 if (clistr_is_previous_version_path(state->mask, NULL, NULL, NULL)) {
690 additional_flags2 = FLAGS2_REPARSE_PATH;
693 param_len = talloc_get_size(state->param);
695 subreq = cli_trans_send(state, state->ev, state->cli, additional_flags2,
696 SMBtrans2, NULL, -1, 0, 0,
697 state->setup, 1, 0,
698 state->param, param_len, 10,
699 NULL, 0, CLI_BUFFER_SIZE);
700 if (tevent_req_nomem(subreq, req)) {
701 return tevent_req_post(req, ev);
703 tevent_req_set_callback(subreq, cli_list_trans_done, req);
704 return req;
707 static void cli_list_trans_done(struct tevent_req *subreq)
709 struct tevent_req *req = tevent_req_callback_data(
710 subreq, struct tevent_req);
711 struct cli_list_trans_state *state = tevent_req_data(
712 req, struct cli_list_trans_state);
713 NTSTATUS status;
714 uint8_t *param;
715 uint32_t num_param;
716 uint8_t *data;
717 char *data_end;
718 uint32_t num_data;
719 uint32_t min_param;
720 struct file_info *tmp;
721 size_t old_num_finfo;
722 uint16_t recv_flags2;
723 int ff_searchcount;
724 bool ff_eos;
725 char *p, *p2;
726 uint32_t resume_key = 0;
727 int i;
728 DATA_BLOB last_name_raw;
729 struct file_info *finfo = NULL;
730 size_t param_len;
731 uint16_t additional_flags2 = 0;
733 min_param = (state->first ? 6 : 4);
735 status = cli_trans_recv(subreq, talloc_tos(), &recv_flags2,
736 NULL, 0, NULL,
737 &param, min_param, &num_param,
738 &data, 0, &num_data);
739 TALLOC_FREE(subreq);
740 if (!NT_STATUS_IS_OK(status)) {
742 * TODO: retry, OS/2 nofiles
744 tevent_req_nterror(req, status);
745 return;
748 if (state->first) {
749 state->ff_dir_handle = SVAL(param, 0);
750 ff_searchcount = SVAL(param, 2);
751 ff_eos = SVAL(param, 4) != 0;
752 } else {
753 ff_searchcount = SVAL(param, 0);
754 ff_eos = SVAL(param, 2) != 0;
757 old_num_finfo = talloc_array_length(state->finfo);
759 tmp = talloc_realloc(state, state->finfo, struct file_info,
760 old_num_finfo + ff_searchcount);
761 if (tevent_req_nomem(tmp, req)) {
762 return;
764 state->finfo = tmp;
766 p2 = p = (char *)data;
767 data_end = (char *)data + num_data;
768 last_name_raw = data_blob_null;
770 for (i=0; i<ff_searchcount; i++) {
771 if (p2 >= data_end) {
772 ff_eos = true;
773 break;
775 if ((state->info_level == SMB_FIND_FILE_BOTH_DIRECTORY_INFO)
776 && (i == ff_searchcount-1)) {
777 /* Last entry - fixup the last offset length. */
778 SIVAL(p2, 0, PTR_DIFF((data + num_data), p2));
781 data_blob_free(&last_name_raw);
783 finfo = &state->finfo[old_num_finfo + i];
785 p2 += interpret_long_filename(
786 state->finfo, /* Stick fname to the array as such */
787 state->cli, state->info_level,
788 (char *)data, recv_flags2, p2,
789 data_end, finfo, &resume_key, &last_name_raw);
791 if (finfo->name == NULL) {
792 DEBUG(1, ("cli_list: Error: unable to parse name from "
793 "info level %d\n", state->info_level));
794 ff_eos = true;
795 break;
798 status = is_bad_finfo_name(state->cli, finfo);
799 if (!NT_STATUS_IS_OK(status)) {
800 smbXcli_conn_disconnect(state->cli->conn, status);
801 tevent_req_nterror(req, status);
802 return;
805 if (!state->first && (state->mask[0] != '\0') &&
806 strcsequal(finfo->name, state->mask)) {
807 DEBUG(1, ("Error: Looping in FIND_NEXT as name %s has "
808 "already been seen?\n", finfo->name));
809 ff_eos = true;
810 break;
814 if (ff_searchcount == 0) {
815 ff_eos = true;
818 TALLOC_FREE(param);
819 TALLOC_FREE(data);
822 * Shrink state->finfo to the real length we received
824 tmp = talloc_realloc(state, state->finfo, struct file_info,
825 old_num_finfo + i);
826 if (tevent_req_nomem(tmp, req)) {
827 return;
829 state->finfo = tmp;
831 state->first = false;
833 if (ff_eos) {
834 data_blob_free(&last_name_raw);
835 tevent_req_done(req);
836 return;
839 TALLOC_FREE(state->mask);
840 state->mask = talloc_strdup(state, finfo->name);
841 if (tevent_req_nomem(state->mask, req)) {
842 return;
845 SSVAL(&state->setup[0], 0, TRANSACT2_FINDNEXT);
847 param = talloc_realloc(state, state->param, uint8_t, 12);
848 if (tevent_req_nomem(param, req)) {
849 return;
851 state->param = param;
853 SSVAL(param, 0, state->ff_dir_handle);
854 SSVAL(param, 2, state->max_matches); /* max count */
855 SSVAL(param, 4, state->info_level);
857 * For W2K servers serving out FAT filesystems we *must* set
858 * the resume key. If it's not FAT then it's returned as zero.
860 SIVAL(param, 6, resume_key); /* ff_resume_key */
862 * NB. *DON'T* use continue here. If you do it seems that W2K
863 * and bretheren can miss filenames. Use last filename
864 * continue instead. JRA
866 SSVAL(param, 10, (FLAG_TRANS2_FIND_REQUIRE_RESUME
867 |FLAG_TRANS2_FIND_CLOSE_IF_END
868 |(state->cli->backup_intent ? FLAG_TRANS2_FIND_BACKUP_INTENT : 0)));
869 if (last_name_raw.length) {
870 state->param = trans2_bytes_push_bytes(state->param,
871 last_name_raw.data,
872 last_name_raw.length);
873 if (tevent_req_nomem(state->param, req)) {
874 return;
876 data_blob_free(&last_name_raw);
877 } else {
878 state->param = trans2_bytes_push_str(state->param,
879 smbXcli_conn_use_unicode(state->cli->conn),
880 state->mask,
881 strlen(state->mask)+1,
882 NULL);
883 if (tevent_req_nomem(state->param, req)) {
884 return;
887 param_len = talloc_get_size(state->param);
889 if (clistr_is_previous_version_path(state->mask, NULL, NULL, NULL)) {
890 additional_flags2 = FLAGS2_REPARSE_PATH;
893 subreq = cli_trans_send(state, state->ev, state->cli, additional_flags2,
894 SMBtrans2, NULL, -1, 0, 0,
895 state->setup, 1, 0,
896 state->param, param_len, 10,
897 NULL, 0, CLI_BUFFER_SIZE);
898 if (tevent_req_nomem(subreq, req)) {
899 return;
901 tevent_req_set_callback(subreq, cli_list_trans_done, req);
904 static NTSTATUS cli_list_trans_recv(struct tevent_req *req,
905 TALLOC_CTX *mem_ctx,
906 struct file_info **finfo)
908 struct cli_list_trans_state *state = tevent_req_data(
909 req, struct cli_list_trans_state);
910 NTSTATUS status;
912 if (tevent_req_is_nterror(req, &status)) {
913 return status;
915 *finfo = talloc_move(mem_ctx, &state->finfo);
916 return NT_STATUS_OK;
919 NTSTATUS cli_list_trans(struct cli_state *cli, const char *mask,
920 uint16_t attribute, int info_level,
921 NTSTATUS (*fn)(const char *mnt, struct file_info *finfo,
922 const char *mask, void *private_data),
923 void *private_data)
925 TALLOC_CTX *frame = talloc_stackframe();
926 struct tevent_context *ev;
927 struct tevent_req *req;
928 int i, num_finfo;
929 struct file_info *finfo = NULL;
930 NTSTATUS status = NT_STATUS_NO_MEMORY;
932 if (smbXcli_conn_has_async_calls(cli->conn)) {
934 * Can't use sync call while an async call is in flight
936 status = NT_STATUS_INVALID_PARAMETER;
937 goto fail;
939 ev = samba_tevent_context_init(frame);
940 if (ev == NULL) {
941 goto fail;
943 req = cli_list_trans_send(frame, ev, cli, mask, attribute, info_level);
944 if (req == NULL) {
945 goto fail;
947 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
948 goto fail;
950 status = cli_list_trans_recv(req, frame, &finfo);
951 if (!NT_STATUS_IS_OK(status)) {
952 goto fail;
954 num_finfo = talloc_array_length(finfo);
955 for (i=0; i<num_finfo; i++) {
956 status = fn(cli->dfs_mountpoint, &finfo[i], mask, private_data);
957 if (!NT_STATUS_IS_OK(status)) {
958 goto fail;
961 fail:
962 TALLOC_FREE(frame);
963 return status;
966 struct cli_list_state {
967 NTSTATUS (*recv_fn)(struct tevent_req *req, TALLOC_CTX *mem_ctx,
968 struct file_info **finfo);
969 struct file_info *finfo;
972 static void cli_list_done(struct tevent_req *subreq);
974 struct tevent_req *cli_list_send(TALLOC_CTX *mem_ctx,
975 struct tevent_context *ev,
976 struct cli_state *cli,
977 const char *mask,
978 uint16_t attribute,
979 uint16_t info_level)
981 struct tevent_req *req, *subreq;
982 struct cli_list_state *state;
984 req = tevent_req_create(mem_ctx, &state, struct cli_list_state);
985 if (req == NULL) {
986 return NULL;
989 if (smbXcli_conn_protocol(cli->conn) <= PROTOCOL_LANMAN1) {
990 subreq = cli_list_old_send(state, ev, cli, mask, attribute);
991 state->recv_fn = cli_list_old_recv;
992 } else {
993 subreq = cli_list_trans_send(state, ev, cli, mask, attribute,
994 info_level);
995 state->recv_fn = cli_list_trans_recv;
997 if (tevent_req_nomem(subreq, req)) {
998 return tevent_req_post(req, ev);
1000 tevent_req_set_callback(subreq, cli_list_done, req);
1001 return req;
1004 static void cli_list_done(struct tevent_req *subreq)
1006 struct tevent_req *req = tevent_req_callback_data(
1007 subreq, struct tevent_req);
1008 struct cli_list_state *state = tevent_req_data(
1009 req, struct cli_list_state);
1010 NTSTATUS status;
1012 status = state->recv_fn(subreq, state, &state->finfo);
1013 TALLOC_FREE(subreq);
1014 if (!NT_STATUS_IS_OK(status)) {
1015 tevent_req_nterror(req, status);
1016 return;
1018 tevent_req_done(req);
1021 NTSTATUS cli_list_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
1022 struct file_info **finfo, size_t *num_finfo)
1024 struct cli_list_state *state = tevent_req_data(
1025 req, struct cli_list_state);
1026 NTSTATUS status;
1028 if (tevent_req_is_nterror(req, &status)) {
1029 return status;
1031 *num_finfo = talloc_array_length(state->finfo);
1032 *finfo = talloc_move(mem_ctx, &state->finfo);
1033 return NT_STATUS_OK;
1036 NTSTATUS cli_list(struct cli_state *cli, const char *mask, uint16_t attribute,
1037 NTSTATUS (*fn)(const char *, struct file_info *, const char *,
1038 void *), void *state)
1040 TALLOC_CTX *frame = NULL;
1041 struct tevent_context *ev;
1042 struct tevent_req *req;
1043 NTSTATUS status = NT_STATUS_NO_MEMORY;
1044 struct file_info *finfo;
1045 size_t i, num_finfo;
1046 uint16_t info_level;
1048 if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
1049 return cli_smb2_list(cli, mask, attribute, fn, state);
1052 frame = talloc_stackframe();
1054 if (smbXcli_conn_has_async_calls(cli->conn)) {
1056 * Can't use sync call while an async call is in flight
1058 status = NT_STATUS_INVALID_PARAMETER;
1059 goto fail;
1061 ev = samba_tevent_context_init(frame);
1062 if (ev == NULL) {
1063 goto fail;
1066 info_level = (smb1cli_conn_capabilities(cli->conn) & CAP_NT_SMBS)
1067 ? SMB_FIND_FILE_BOTH_DIRECTORY_INFO : SMB_FIND_INFO_STANDARD;
1069 req = cli_list_send(frame, ev, cli, mask, attribute, info_level);
1070 if (req == NULL) {
1071 goto fail;
1073 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
1074 goto fail;
1077 status = cli_list_recv(req, frame, &finfo, &num_finfo);
1078 if (!NT_STATUS_IS_OK(status)) {
1079 goto fail;
1082 for (i=0; i<num_finfo; i++) {
1083 status = fn(cli->dfs_mountpoint, &finfo[i], mask, state);
1084 if (!NT_STATUS_IS_OK(status)) {
1085 goto fail;
1088 fail:
1089 TALLOC_FREE(frame);
1090 return status;