tests/krb5: Add more encryption type constants
[Samba.git] / source3 / libsmb / clilist.c
blob25040438d9e8d93bc211142e803a3479a1dfd3a0
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->attr = SVAL(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 = pull_string_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->attr = SVAL(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 = pull_string_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->attr = IVAL(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 = pull_string_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 = pull_string_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->attr = 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 = pull_string_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 uint32_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 uint32_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 < ((uint32_t)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;
555 if (finfo->name == NULL) {
556 TALLOC_FREE(finfo);
557 return NT_STATUS_INVALID_NETWORK_RESPONSE;
559 status = is_bad_finfo_name(state->cli, finfo);
560 if (!NT_STATUS_IS_OK(status)) {
561 smbXcli_conn_disconnect(state->cli->conn, status);
562 TALLOC_FREE(finfo);
563 return status;
566 *pfinfo = finfo;
567 return NT_STATUS_OK;
570 NTSTATUS cli_list_old(struct cli_state *cli, const char *mask,
571 uint32_t attribute,
572 NTSTATUS (*fn)(struct file_info *,
573 const char *, void *), void *state)
575 TALLOC_CTX *frame = talloc_stackframe();
576 struct tevent_context *ev;
577 struct tevent_req *req;
578 NTSTATUS status = NT_STATUS_NO_MEMORY;
579 struct file_info *finfo = NULL;
580 size_t i, num_finfo;
582 if (smbXcli_conn_has_async_calls(cli->conn)) {
584 * Can't use sync call while an async call is in flight
586 status = NT_STATUS_INVALID_PARAMETER;
587 goto fail;
589 ev = samba_tevent_context_init(frame);
590 if (ev == NULL) {
591 goto fail;
593 req = cli_list_old_send(frame, ev, cli, mask, attribute);
594 if (req == NULL) {
595 goto fail;
597 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
598 goto fail;
600 status = cli_list_old_recv(req, frame, &finfo);
601 if (!NT_STATUS_IS_OK(status)) {
602 goto fail;
604 num_finfo = talloc_array_length(finfo);
605 for (i=0; i<num_finfo; i++) {
606 status = fn(&finfo[i], mask, state);
607 if (!NT_STATUS_IS_OK(status)) {
608 goto fail;
611 fail:
612 TALLOC_FREE(frame);
613 return status;
616 struct cli_list_trans_state {
617 struct tevent_context *ev;
618 struct cli_state *cli;
619 char *mask;
620 uint32_t attribute;
621 uint16_t info_level;
623 int loop_count;
624 int total_received;
625 uint16_t max_matches;
626 bool first;
628 int ff_eos;
629 int ff_dir_handle;
631 uint16_t setup[1];
632 uint8_t *param;
634 struct file_info *finfo;
637 static void cli_list_trans_done(struct tevent_req *subreq);
639 static struct tevent_req *cli_list_trans_send(TALLOC_CTX *mem_ctx,
640 struct tevent_context *ev,
641 struct cli_state *cli,
642 const char *mask,
643 uint32_t attribute,
644 uint16_t info_level)
646 struct tevent_req *req, *subreq;
647 struct cli_list_trans_state *state;
648 size_t param_len;
649 uint16_t additional_flags2 = 0;
651 req = tevent_req_create(mem_ctx, &state,
652 struct cli_list_trans_state);
653 if (req == NULL) {
654 return NULL;
656 state->ev = ev;
657 state->cli = cli;
658 state->mask = talloc_strdup(state, mask);
659 if (tevent_req_nomem(state->mask, req)) {
660 return tevent_req_post(req, ev);
662 state->attribute = attribute;
663 state->info_level = info_level;
664 state->loop_count = 0;
665 state->first = true;
667 state->max_matches = 1366; /* Match W2k */
669 SSVAL(&state->setup[0], 0, TRANSACT2_FINDFIRST);
671 state->param = talloc_array(state, uint8_t, 12);
672 if (tevent_req_nomem(state->param, req)) {
673 return tevent_req_post(req, ev);
676 SSVAL(state->param, 0, state->attribute);
677 SSVAL(state->param, 2, state->max_matches);
678 SSVAL(state->param, 4,
679 FLAG_TRANS2_FIND_REQUIRE_RESUME
680 |FLAG_TRANS2_FIND_CLOSE_IF_END
681 |(cli->backup_intent ? FLAG_TRANS2_FIND_BACKUP_INTENT : 0));
682 SSVAL(state->param, 6, state->info_level);
683 SIVAL(state->param, 8, 0);
685 state->param = trans2_bytes_push_str(state->param, smbXcli_conn_use_unicode(cli->conn),
686 state->mask, strlen(state->mask)+1,
687 NULL);
688 if (tevent_req_nomem(state->param, req)) {
689 return tevent_req_post(req, ev);
692 if (clistr_is_previous_version_path(state->mask, NULL, NULL, NULL)) {
693 additional_flags2 = FLAGS2_REPARSE_PATH;
696 param_len = talloc_get_size(state->param);
698 subreq = cli_trans_send(state, state->ev, state->cli, additional_flags2,
699 SMBtrans2, NULL, -1, 0, 0,
700 state->setup, 1, 0,
701 state->param, param_len, 10,
702 NULL, 0, CLI_BUFFER_SIZE);
703 if (tevent_req_nomem(subreq, req)) {
704 return tevent_req_post(req, ev);
706 tevent_req_set_callback(subreq, cli_list_trans_done, req);
707 return req;
710 static void cli_list_trans_done(struct tevent_req *subreq)
712 struct tevent_req *req = tevent_req_callback_data(
713 subreq, struct tevent_req);
714 struct cli_list_trans_state *state = tevent_req_data(
715 req, struct cli_list_trans_state);
716 NTSTATUS status;
717 uint8_t *param;
718 uint32_t num_param;
719 uint8_t *data;
720 char *data_end;
721 uint32_t num_data;
722 uint32_t min_param;
723 struct file_info *tmp;
724 size_t old_num_finfo;
725 uint16_t recv_flags2;
726 int ff_searchcount;
727 bool ff_eos;
728 char *p, *p2;
729 uint32_t resume_key = 0;
730 int i;
731 DATA_BLOB last_name_raw;
732 struct file_info *finfo = NULL;
733 size_t param_len;
734 uint16_t additional_flags2 = 0;
736 min_param = (state->first ? 6 : 4);
738 status = cli_trans_recv(subreq, talloc_tos(), &recv_flags2,
739 NULL, 0, NULL,
740 &param, min_param, &num_param,
741 &data, 0, &num_data);
742 TALLOC_FREE(subreq);
743 if (!NT_STATUS_IS_OK(status)) {
745 * TODO: retry, OS/2 nofiles
747 tevent_req_nterror(req, status);
748 return;
751 if (state->first) {
752 state->ff_dir_handle = SVAL(param, 0);
753 ff_searchcount = SVAL(param, 2);
754 ff_eos = SVAL(param, 4) != 0;
755 } else {
756 ff_searchcount = SVAL(param, 0);
757 ff_eos = SVAL(param, 2) != 0;
760 old_num_finfo = talloc_array_length(state->finfo);
762 tmp = talloc_realloc(state, state->finfo, struct file_info,
763 old_num_finfo + ff_searchcount);
764 if (tevent_req_nomem(tmp, req)) {
765 return;
767 state->finfo = tmp;
769 p2 = p = (char *)data;
770 data_end = (char *)data + num_data;
771 last_name_raw = data_blob_null;
773 for (i=0; i<ff_searchcount; i++) {
774 if (p2 >= data_end) {
775 ff_eos = true;
776 break;
778 if ((state->info_level == SMB_FIND_FILE_BOTH_DIRECTORY_INFO)
779 && (i == ff_searchcount-1)) {
780 /* Last entry - fixup the last offset length. */
781 SIVAL(p2, 0, PTR_DIFF((data + num_data), p2));
784 data_blob_free(&last_name_raw);
786 finfo = &state->finfo[old_num_finfo + i];
788 p2 += interpret_long_filename(
789 state->finfo, /* Stick fname to the array as such */
790 state->cli, state->info_level,
791 (char *)data, recv_flags2, p2,
792 data_end, finfo, &resume_key, &last_name_raw);
794 if (finfo->name == NULL) {
795 DEBUG(1, ("cli_list: Error: unable to parse name from "
796 "info level %d\n", state->info_level));
797 tevent_req_nterror(req,
798 NT_STATUS_INVALID_NETWORK_RESPONSE);
799 return;
802 status = is_bad_finfo_name(state->cli, finfo);
803 if (!NT_STATUS_IS_OK(status)) {
804 smbXcli_conn_disconnect(state->cli->conn, status);
805 tevent_req_nterror(req, status);
806 return;
809 if (!state->first && (state->mask[0] != '\0') &&
810 strcsequal(finfo->name, state->mask)) {
811 DEBUG(1, ("Error: Looping in FIND_NEXT as name %s has "
812 "already been seen?\n", finfo->name));
813 ff_eos = true;
814 break;
818 if (ff_searchcount == 0) {
819 ff_eos = true;
822 TALLOC_FREE(param);
823 TALLOC_FREE(data);
826 * Shrink state->finfo to the real length we received
828 tmp = talloc_realloc(state, state->finfo, struct file_info,
829 old_num_finfo + i);
830 if (tevent_req_nomem(tmp, req)) {
831 return;
833 state->finfo = tmp;
835 state->first = false;
837 if (ff_eos) {
838 data_blob_free(&last_name_raw);
839 tevent_req_done(req);
840 return;
843 TALLOC_FREE(state->mask);
844 state->mask = talloc_strdup(state, finfo->name);
845 if (tevent_req_nomem(state->mask, req)) {
846 return;
849 SSVAL(&state->setup[0], 0, TRANSACT2_FINDNEXT);
851 param = talloc_realloc(state, state->param, uint8_t, 12);
852 if (tevent_req_nomem(param, req)) {
853 return;
855 state->param = param;
857 SSVAL(param, 0, state->ff_dir_handle);
858 SSVAL(param, 2, state->max_matches); /* max count */
859 SSVAL(param, 4, state->info_level);
861 * For W2K servers serving out FAT filesystems we *must* set
862 * the resume key. If it's not FAT then it's returned as zero.
864 SIVAL(param, 6, resume_key); /* ff_resume_key */
866 * NB. *DON'T* use continue here. If you do it seems that W2K
867 * and bretheren can miss filenames. Use last filename
868 * continue instead. JRA
870 SSVAL(param, 10, (FLAG_TRANS2_FIND_REQUIRE_RESUME
871 |FLAG_TRANS2_FIND_CLOSE_IF_END
872 |(state->cli->backup_intent ? FLAG_TRANS2_FIND_BACKUP_INTENT : 0)));
873 if (last_name_raw.length) {
874 state->param = trans2_bytes_push_bytes(state->param,
875 last_name_raw.data,
876 last_name_raw.length);
877 if (tevent_req_nomem(state->param, req)) {
878 return;
880 data_blob_free(&last_name_raw);
881 } else {
882 state->param = trans2_bytes_push_str(state->param,
883 smbXcli_conn_use_unicode(state->cli->conn),
884 state->mask,
885 strlen(state->mask)+1,
886 NULL);
887 if (tevent_req_nomem(state->param, req)) {
888 return;
891 param_len = talloc_get_size(state->param);
893 if (clistr_is_previous_version_path(state->mask, NULL, NULL, NULL)) {
894 additional_flags2 = FLAGS2_REPARSE_PATH;
897 subreq = cli_trans_send(state, state->ev, state->cli, additional_flags2,
898 SMBtrans2, NULL, -1, 0, 0,
899 state->setup, 1, 0,
900 state->param, param_len, 10,
901 NULL, 0, CLI_BUFFER_SIZE);
902 if (tevent_req_nomem(subreq, req)) {
903 return;
905 tevent_req_set_callback(subreq, cli_list_trans_done, req);
908 static NTSTATUS cli_list_trans_recv(struct tevent_req *req,
909 TALLOC_CTX *mem_ctx,
910 struct file_info **finfo)
912 struct cli_list_trans_state *state = tevent_req_data(
913 req, struct cli_list_trans_state);
914 NTSTATUS status;
916 if (tevent_req_is_nterror(req, &status)) {
917 return status;
919 *finfo = talloc_move(mem_ctx, &state->finfo);
920 return NT_STATUS_OK;
923 NTSTATUS cli_list_trans(struct cli_state *cli, const char *mask,
924 uint32_t attribute, int info_level,
925 NTSTATUS (*fn)(
926 struct file_info *finfo,
927 const char *mask,
928 void *private_data),
929 void *private_data)
931 TALLOC_CTX *frame = talloc_stackframe();
932 struct tevent_context *ev;
933 struct tevent_req *req;
934 int i, num_finfo;
935 struct file_info *finfo = NULL;
936 NTSTATUS status = NT_STATUS_NO_MEMORY;
938 if (smbXcli_conn_has_async_calls(cli->conn)) {
940 * Can't use sync call while an async call is in flight
942 status = NT_STATUS_INVALID_PARAMETER;
943 goto fail;
945 ev = samba_tevent_context_init(frame);
946 if (ev == NULL) {
947 goto fail;
949 req = cli_list_trans_send(frame, ev, cli, mask, attribute, info_level);
950 if (req == NULL) {
951 goto fail;
953 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
954 goto fail;
956 status = cli_list_trans_recv(req, frame, &finfo);
957 if (!NT_STATUS_IS_OK(status)) {
958 goto fail;
960 num_finfo = talloc_array_length(finfo);
961 for (i=0; i<num_finfo; i++) {
962 status = fn(&finfo[i], mask, private_data);
963 if (!NT_STATUS_IS_OK(status)) {
964 goto fail;
967 fail:
968 TALLOC_FREE(frame);
969 return status;
972 struct cli_list_state {
973 struct tevent_context *ev;
974 struct tevent_req *subreq;
975 NTSTATUS (*recv_fn)(struct tevent_req *req, TALLOC_CTX *mem_ctx,
976 struct file_info **finfo);
977 struct file_info *finfo;
978 size_t num_received;
981 static void cli_list_done(struct tevent_req *subreq);
983 struct tevent_req *cli_list_send(TALLOC_CTX *mem_ctx,
984 struct tevent_context *ev,
985 struct cli_state *cli,
986 const char *mask,
987 uint32_t attribute,
988 uint16_t info_level)
990 struct tevent_req *req = NULL;
991 struct cli_list_state *state;
992 enum protocol_types proto = smbXcli_conn_protocol(cli->conn);
994 req = tevent_req_create(mem_ctx, &state, struct cli_list_state);
995 if (req == NULL) {
996 return NULL;
998 state->ev = ev;
1000 if (proto >= PROTOCOL_SMB2_02) {
1001 state->subreq = cli_smb2_list_send(state, ev, cli, mask);
1002 state->recv_fn = cli_smb2_list_recv;
1003 } else if (proto >= PROTOCOL_LANMAN2) {
1004 state->subreq = cli_list_trans_send(
1005 state, ev, cli, mask, attribute, info_level);
1006 state->recv_fn = cli_list_trans_recv;
1007 } else {
1008 state->subreq = cli_list_old_send(
1009 state, ev, cli, mask, attribute);
1010 state->recv_fn = cli_list_old_recv;
1012 if (tevent_req_nomem(state->subreq, req)) {
1013 return tevent_req_post(req, ev);
1015 tevent_req_set_callback(state->subreq, cli_list_done, req);
1016 return req;
1019 static void cli_list_done(struct tevent_req *subreq)
1021 struct tevent_req *req = tevent_req_callback_data(
1022 subreq, struct tevent_req);
1023 struct cli_list_state *state = tevent_req_data(
1024 req, struct cli_list_state);
1025 NTSTATUS status;
1027 SMB_ASSERT(subreq == state->subreq);
1030 * We don't want to be called by the lowerlevel routines
1031 * from within state->recv_fn()
1033 tevent_req_set_callback(subreq, NULL, NULL);
1035 status = state->recv_fn(subreq, state, &state->finfo);
1036 if (NT_STATUS_EQUAL(status, NT_STATUS_RETRY)) {
1037 /* We'll get back here */
1038 tevent_req_set_callback(subreq, cli_list_done, req);
1039 return;
1042 if (tevent_req_nterror(req, status)) {
1043 return;
1045 tevent_req_notify_callback(req);
1048 NTSTATUS cli_list_recv(
1049 struct tevent_req *req,
1050 TALLOC_CTX *mem_ctx,
1051 struct file_info **pfinfo)
1053 struct cli_list_state *state = tevent_req_data(
1054 req, struct cli_list_state);
1055 size_t num_results;
1056 struct file_info *finfo = NULL;
1057 NTSTATUS status;
1058 bool in_progress;
1060 in_progress = tevent_req_is_in_progress(req);
1062 if (!in_progress) {
1063 if (!tevent_req_is_nterror(req, &status)) {
1064 status = NT_STATUS_NO_MORE_FILES;
1066 return status;
1069 if (state->finfo == NULL) {
1070 status = state->recv_fn(state->subreq, state, &state->finfo);
1072 if (NT_STATUS_EQUAL(status, NT_STATUS_RETRY)) {
1073 tevent_req_set_callback(
1074 state->subreq, cli_list_done, req);
1075 return NT_STATUS_RETRY;
1078 if (NT_STATUS_IS_OK(status) && (state->finfo == NULL)) {
1079 status = NT_STATUS_NO_MORE_FILES;
1082 if (tevent_req_nterror(req, status)) {
1083 return status;
1086 state->num_received = 0;
1089 num_results = talloc_array_length(state->finfo);
1091 if (num_results == 1) {
1092 finfo = talloc_move(mem_ctx, &state->finfo);
1093 } else {
1094 struct file_info *src_finfo =
1095 &state->finfo[state->num_received];
1097 finfo = talloc(mem_ctx, struct file_info);
1098 if (finfo == NULL) {
1099 return NT_STATUS_NO_MEMORY;
1101 *finfo = *src_finfo;
1102 finfo->name = talloc_move(finfo, &src_finfo->name);
1103 finfo->short_name = talloc_move(finfo, &src_finfo->short_name);
1106 state->num_received += 1;
1108 if (state->num_received == num_results) {
1109 TALLOC_FREE(state->finfo);
1112 tevent_req_defer_callback(req, state->ev);
1113 tevent_req_notify_callback(req);
1115 *pfinfo = finfo;
1116 return NT_STATUS_OK;
1119 struct cli_list_sync_state {
1120 const char *mask;
1121 uint32_t attribute;
1122 NTSTATUS (*fn)(struct file_info *finfo,
1123 const char *mask,
1124 void *private_data);
1125 void *private_data;
1126 NTSTATUS status;
1127 bool processed_file;
1130 static void cli_list_sync_cb(struct tevent_req *subreq)
1132 struct cli_list_sync_state *state =
1133 tevent_req_callback_data_void(subreq);
1134 struct file_info *finfo;
1135 bool ok;
1137 state->status = cli_list_recv(subreq, talloc_tos(), &finfo);
1138 /* No TALLOC_FREE(subreq), we get here more than once */
1140 if (NT_STATUS_EQUAL(state->status, NT_STATUS_RETRY)) {
1142 * The lowlevel SMB call was rearmed, we'll get back
1143 * here when it's done.
1145 state->status = NT_STATUS_OK;
1146 return;
1149 if (!NT_STATUS_IS_OK(state->status)) {
1150 return;
1153 ok = dir_check_ftype(finfo->attr, state->attribute);
1154 if (!ok) {
1156 * Only process if attributes match. On SMB1 server
1157 * does this, so on SMB2 we need to emulate in the
1158 * client.
1160 * https://bugzilla.samba.org/show_bug.cgi?id=10260
1162 return;
1165 state->status = state->fn(finfo, state->mask, state->private_data);
1167 state->processed_file = true;
1169 TALLOC_FREE(finfo);
1172 NTSTATUS cli_list(struct cli_state *cli,
1173 const char *mask,
1174 uint32_t attribute,
1175 NTSTATUS (*fn)(struct file_info *finfo,
1176 const char *mask,
1177 void *private_data),
1178 void *private_data)
1180 TALLOC_CTX *frame = NULL;
1181 struct cli_list_sync_state state = {
1182 .mask = mask,
1183 .attribute = attribute,
1184 .fn = fn,
1185 .private_data = private_data,
1187 struct tevent_context *ev;
1188 struct tevent_req *req;
1189 NTSTATUS status = NT_STATUS_NO_MEMORY;
1190 uint16_t info_level;
1192 frame = talloc_stackframe();
1194 if (smbXcli_conn_has_async_calls(cli->conn)) {
1196 * Can't use sync call while an async call is in flight
1198 status = NT_STATUS_INVALID_PARAMETER;
1199 goto fail;
1201 ev = samba_tevent_context_init(frame);
1202 if (ev == NULL) {
1203 goto fail;
1206 info_level = (smb1cli_conn_capabilities(cli->conn) & CAP_NT_SMBS)
1207 ? SMB_FIND_FILE_BOTH_DIRECTORY_INFO : SMB_FIND_INFO_STANDARD;
1209 req = cli_list_send(frame, ev, cli, mask, attribute, info_level);
1210 if (req == NULL) {
1211 goto fail;
1213 tevent_req_set_callback(req, cli_list_sync_cb, &state);
1215 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
1216 goto fail;
1219 status = state.status;
1221 if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_FILES)) {
1222 status = NT_STATUS_OK;
1225 if (NT_STATUS_IS_OK(status) && !state.processed_file) {
1226 status = NT_STATUS_NO_SUCH_FILE;
1229 fail:
1230 TALLOC_FREE(frame);
1231 return status;