s3:idmap_ad: add support for ADS_AUTH_SASL_{STARTTLS,LDAPS}
[Samba.git] / source3 / libsmb / clilist.c
blob54b46b09e0e2953d9d46ab5a4f6b25312724d2b8
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(BVAL(p, 0));
252 p += 8;
253 finfo->mtime_ts = interpret_long_date(BVAL(p, 0));
254 p += 8;
255 finfo->ctime_ts = interpret_long_date(BVAL(p, 0));
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 state->mask = smb1_dfs_share_path(state, cli, state->mask);
398 if (tevent_req_nomem(state->mask, req)) {
399 return tevent_req_post(req, ev);
401 usable_space = cli_state_available_size(cli, 100);
402 state->num_asked = usable_space / DIR_STRUCT_SIZE;
404 SSVAL(state->vwv + 0, 0, state->num_asked);
405 SSVAL(state->vwv + 1, 0, state->attribute);
407 bytes = talloc_array(state, uint8_t, 1);
408 if (tevent_req_nomem(bytes, req)) {
409 return tevent_req_post(req, ev);
411 bytes[0] = 4;
412 bytes = smb_bytes_push_str(bytes,
413 smbXcli_conn_use_unicode(cli->conn),
414 state->mask,
415 strlen(state->mask)+1,
416 NULL);
418 bytes = smb_bytes_push_bytes(bytes, 5, (const uint8_t *)&zero, 2);
419 if (tevent_req_nomem(bytes, req)) {
420 return tevent_req_post(req, ev);
423 subreq = cli_smb_send(state, state->ev, state->cli, SMBsearch, 0, 0,
424 2, state->vwv, talloc_get_size(bytes), bytes);
425 if (tevent_req_nomem(subreq, req)) {
426 return tevent_req_post(req, ev);
428 tevent_req_set_callback(subreq, cli_list_old_done, req);
429 return req;
432 static void cli_list_old_done(struct tevent_req *subreq)
434 struct tevent_req *req = tevent_req_callback_data(
435 subreq, struct tevent_req);
436 struct cli_list_old_state *state = tevent_req_data(
437 req, struct cli_list_old_state);
438 NTSTATUS status;
439 uint8_t cmd;
440 uint8_t wct;
441 uint16_t *vwv;
442 uint32_t num_bytes;
443 uint8_t *bytes;
444 uint16_t received;
445 size_t dirlist_len;
446 uint8_t *tmp;
448 status = cli_smb_recv(subreq, state, NULL, 0, &wct, &vwv, &num_bytes,
449 &bytes);
450 if (!NT_STATUS_IS_OK(status)
451 && !NT_STATUS_EQUAL(status, NT_STATUS_DOS(ERRDOS, ERRnofiles))
452 && !NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) {
453 TALLOC_FREE(subreq);
454 tevent_req_nterror(req, status);
455 return;
457 if (NT_STATUS_EQUAL(status, NT_STATUS_DOS(ERRDOS, ERRnofiles))
458 || NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) {
459 received = 0;
460 } else {
461 if (wct < 1) {
462 TALLOC_FREE(subreq);
463 tevent_req_nterror(
464 req, NT_STATUS_INVALID_NETWORK_RESPONSE);
465 return;
467 received = SVAL(vwv + 0, 0);
470 if (received > 0) {
472 * I don't think this can wrap. received is
473 * initialized from a 16-bit value.
475 if (num_bytes < ((uint32_t)received * DIR_STRUCT_SIZE + 3)) {
476 TALLOC_FREE(subreq);
477 tevent_req_nterror(
478 req, NT_STATUS_INVALID_NETWORK_RESPONSE);
479 return;
482 dirlist_len = talloc_get_size(state->dirlist);
484 tmp = talloc_realloc(
485 state, state->dirlist, uint8_t,
486 dirlist_len + received * DIR_STRUCT_SIZE);
487 if (tevent_req_nomem(tmp, req)) {
488 return;
490 state->dirlist = tmp;
491 memcpy(state->dirlist + dirlist_len, bytes + 3,
492 received * DIR_STRUCT_SIZE);
494 SSVAL(state->search_status, 0, 21);
495 memcpy(state->search_status + 2,
496 bytes + 3 + (received-1)*DIR_STRUCT_SIZE, 21);
497 cmd = SMBsearch;
498 } else {
499 if (state->first || state->done) {
500 tevent_req_done(req);
501 return;
503 state->done = true;
504 state->num_asked = 0;
505 cmd = SMBfclose;
507 TALLOC_FREE(subreq);
509 state->first = false;
511 SSVAL(state->vwv + 0, 0, state->num_asked);
512 SSVAL(state->vwv + 1, 0, state->attribute);
514 bytes = talloc_array(state, uint8_t, 1);
515 if (tevent_req_nomem(bytes, req)) {
516 return;
518 bytes[0] = 4;
519 bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(state->cli->conn), "",
520 1, NULL);
521 bytes = smb_bytes_push_bytes(bytes, 5, state->search_status,
522 sizeof(state->search_status));
523 if (tevent_req_nomem(bytes, req)) {
524 return;
526 subreq = cli_smb_send(state, state->ev, state->cli, cmd, 0, 0,
527 2, state->vwv, talloc_get_size(bytes), bytes);
528 if (tevent_req_nomem(subreq, req)) {
529 return;
531 tevent_req_set_callback(subreq, cli_list_old_done, req);
534 static NTSTATUS cli_list_old_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
535 struct file_info **pfinfo)
537 struct cli_list_old_state *state = tevent_req_data(
538 req, struct cli_list_old_state);
539 NTSTATUS status;
540 size_t i, num_received;
541 struct file_info *finfo;
543 if (tevent_req_is_nterror(req, &status)) {
544 return status;
547 if (state->dirlist == NULL) {
548 *pfinfo = NULL;
549 return NT_STATUS_OK;
552 num_received = talloc_array_length(state->dirlist) / DIR_STRUCT_SIZE;
554 finfo = talloc_array(mem_ctx, struct file_info, num_received);
555 if (finfo == NULL) {
556 return NT_STATUS_NO_MEMORY;
559 for (i=0; i<num_received; i++) {
560 if (!interpret_short_filename(
561 finfo, state->cli,
562 (char *)state->dirlist + i * DIR_STRUCT_SIZE,
563 &finfo[i])) {
564 TALLOC_FREE(finfo);
565 return NT_STATUS_NO_MEMORY;
567 if (finfo->name == NULL) {
568 TALLOC_FREE(finfo);
569 return NT_STATUS_INVALID_NETWORK_RESPONSE;
571 status = is_bad_finfo_name(state->cli, finfo);
572 if (!NT_STATUS_IS_OK(status)) {
573 smbXcli_conn_disconnect(state->cli->conn, status);
574 TALLOC_FREE(finfo);
575 return status;
578 TALLOC_FREE(state->dirlist);
579 *pfinfo = finfo;
580 return NT_STATUS_OK;
583 NTSTATUS cli_list_old(struct cli_state *cli, const char *mask,
584 uint32_t attribute,
585 NTSTATUS (*fn)(struct file_info *,
586 const char *, void *), void *state)
588 TALLOC_CTX *frame = talloc_stackframe();
589 struct tevent_context *ev;
590 struct tevent_req *req;
591 NTSTATUS status = NT_STATUS_NO_MEMORY;
592 struct file_info *finfo = NULL;
593 size_t i, num_finfo;
595 if (smbXcli_conn_has_async_calls(cli->conn)) {
597 * Can't use sync call while an async call is in flight
599 status = NT_STATUS_INVALID_PARAMETER;
600 goto fail;
602 ev = samba_tevent_context_init(frame);
603 if (ev == NULL) {
604 goto fail;
606 req = cli_list_old_send(frame, ev, cli, mask, attribute);
607 if (req == NULL) {
608 goto fail;
610 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
611 goto fail;
613 status = cli_list_old_recv(req, frame, &finfo);
614 if (!NT_STATUS_IS_OK(status)) {
615 goto fail;
617 num_finfo = talloc_array_length(finfo);
618 for (i=0; i<num_finfo; i++) {
619 status = fn(&finfo[i], mask, state);
620 if (!NT_STATUS_IS_OK(status)) {
621 goto fail;
624 fail:
625 TALLOC_FREE(frame);
626 return status;
629 struct cli_list_trans_state {
630 struct tevent_context *ev;
631 struct cli_state *cli;
632 char *mask;
633 uint32_t attribute;
634 uint16_t info_level;
636 int loop_count;
637 int total_received;
638 uint16_t max_matches;
639 bool first;
641 int ff_eos;
642 int ff_dir_handle;
644 uint16_t setup[1];
645 uint8_t *param;
647 struct file_info *finfo;
650 static void cli_list_trans_done(struct tevent_req *subreq);
652 static struct tevent_req *cli_list_trans_send(TALLOC_CTX *mem_ctx,
653 struct tevent_context *ev,
654 struct cli_state *cli,
655 const char *mask,
656 uint32_t attribute,
657 uint16_t info_level)
659 struct tevent_req *req, *subreq;
660 struct cli_list_trans_state *state;
661 size_t param_len;
662 uint16_t additional_flags2 = 0;
664 req = tevent_req_create(mem_ctx, &state,
665 struct cli_list_trans_state);
666 if (req == NULL) {
667 return NULL;
669 state->ev = ev;
670 state->cli = cli;
671 state->mask = talloc_strdup(state, mask);
672 if (tevent_req_nomem(state->mask, req)) {
673 return tevent_req_post(req, ev);
675 state->mask = smb1_dfs_share_path(state, cli, state->mask);
676 if (tevent_req_nomem(state->mask, req)) {
677 return tevent_req_post(req, ev);
679 state->attribute = attribute;
680 state->info_level = info_level;
681 state->loop_count = 0;
682 state->first = true;
684 state->max_matches = 1366; /* Match W2k */
686 SSVAL(&state->setup[0], 0, TRANSACT2_FINDFIRST);
688 state->param = talloc_array(state, uint8_t, 12);
689 if (tevent_req_nomem(state->param, req)) {
690 return tevent_req_post(req, ev);
693 SSVAL(state->param, 0, state->attribute);
694 SSVAL(state->param, 2, state->max_matches);
695 SSVAL(state->param, 4,
696 FLAG_TRANS2_FIND_REQUIRE_RESUME
697 |FLAG_TRANS2_FIND_CLOSE_IF_END
698 |(cli->backup_intent ? FLAG_TRANS2_FIND_BACKUP_INTENT : 0));
699 SSVAL(state->param, 6, state->info_level);
700 SIVAL(state->param, 8, 0);
702 state->param = trans2_bytes_push_str(state->param, smbXcli_conn_use_unicode(cli->conn),
703 state->mask, strlen(state->mask)+1,
704 NULL);
705 if (tevent_req_nomem(state->param, req)) {
706 return tevent_req_post(req, ev);
709 if (clistr_is_previous_version_path(state->mask)) {
710 additional_flags2 = FLAGS2_REPARSE_PATH;
713 param_len = talloc_get_size(state->param);
715 subreq = cli_trans_send(state, state->ev, state->cli, additional_flags2,
716 SMBtrans2, NULL, -1, 0, 0,
717 state->setup, 1, 0,
718 state->param, param_len, 10,
719 NULL, 0, CLI_BUFFER_SIZE);
720 if (tevent_req_nomem(subreq, req)) {
721 return tevent_req_post(req, ev);
723 tevent_req_set_callback(subreq, cli_list_trans_done, req);
724 return req;
727 static void cli_list_trans_done(struct tevent_req *subreq)
729 struct tevent_req *req = tevent_req_callback_data(
730 subreq, struct tevent_req);
731 struct cli_list_trans_state *state = tevent_req_data(
732 req, struct cli_list_trans_state);
733 NTSTATUS status;
734 uint8_t *param;
735 uint32_t num_param;
736 uint8_t *data;
737 char *data_end;
738 uint32_t num_data;
739 uint32_t min_param;
740 struct file_info *tmp;
741 size_t old_num_finfo;
742 uint16_t recv_flags2;
743 int ff_searchcount;
744 bool ff_eos;
745 char *p, *p2;
746 uint32_t resume_key = 0;
747 int i;
748 DATA_BLOB last_name_raw;
749 struct file_info *finfo = NULL;
750 size_t param_len;
751 uint16_t additional_flags2 = 0;
753 min_param = (state->first ? 6 : 4);
755 status = cli_trans_recv(subreq, talloc_tos(), &recv_flags2,
756 NULL, 0, NULL,
757 &param, min_param, &num_param,
758 &data, 0, &num_data);
759 TALLOC_FREE(subreq);
760 if (!NT_STATUS_IS_OK(status)) {
762 * TODO: retry, OS/2 nofiles
764 tevent_req_nterror(req, status);
765 return;
768 if (state->first) {
769 state->ff_dir_handle = SVAL(param, 0);
770 ff_searchcount = SVAL(param, 2);
771 ff_eos = SVAL(param, 4) != 0;
772 } else {
773 ff_searchcount = SVAL(param, 0);
774 ff_eos = SVAL(param, 2) != 0;
777 old_num_finfo = talloc_array_length(state->finfo);
779 tmp = talloc_realloc(state, state->finfo, struct file_info,
780 old_num_finfo + ff_searchcount);
781 if (tevent_req_nomem(tmp, req)) {
782 return;
784 state->finfo = tmp;
786 p2 = p = (char *)data;
787 data_end = (char *)data + num_data;
788 last_name_raw = data_blob_null;
790 for (i=0; i<ff_searchcount; i++) {
791 if (p2 >= data_end) {
792 ff_eos = true;
793 break;
795 if ((state->info_level == SMB_FIND_FILE_BOTH_DIRECTORY_INFO)
796 && (i == ff_searchcount-1)) {
797 /* Last entry - fixup the last offset length. */
798 SIVAL(p2, 0, PTR_DIFF((data + num_data), p2));
801 data_blob_free(&last_name_raw);
803 finfo = &state->finfo[old_num_finfo + i];
805 p2 += interpret_long_filename(
806 state->finfo, /* Stick fname to the array as such */
807 state->cli, state->info_level,
808 (char *)data, recv_flags2, p2,
809 data_end, finfo, &resume_key, &last_name_raw);
811 if (finfo->name == NULL) {
812 DEBUG(1, ("cli_list: Error: unable to parse name from "
813 "info level %d\n", state->info_level));
814 tevent_req_nterror(req,
815 NT_STATUS_INVALID_NETWORK_RESPONSE);
816 return;
819 status = is_bad_finfo_name(state->cli, finfo);
820 if (!NT_STATUS_IS_OK(status)) {
821 smbXcli_conn_disconnect(state->cli->conn, status);
822 tevent_req_nterror(req, status);
823 return;
826 if (!state->first && (state->mask[0] != '\0') &&
827 strcsequal(finfo->name, state->mask)) {
828 DEBUG(1, ("Error: Looping in FIND_NEXT as name %s has "
829 "already been seen?\n", finfo->name));
830 ff_eos = true;
831 break;
835 if (ff_searchcount == 0) {
836 ff_eos = true;
839 TALLOC_FREE(param);
840 TALLOC_FREE(data);
843 * Shrink state->finfo to the real length we received
845 tmp = talloc_realloc(state, state->finfo, struct file_info,
846 old_num_finfo + i);
847 if (tevent_req_nomem(tmp, req)) {
848 return;
850 state->finfo = tmp;
852 state->first = false;
854 if (ff_eos) {
855 data_blob_free(&last_name_raw);
856 tevent_req_done(req);
857 return;
860 TALLOC_FREE(state->mask);
861 state->mask = talloc_strdup(state, finfo->name);
862 if (tevent_req_nomem(state->mask, req)) {
863 return;
866 SSVAL(&state->setup[0], 0, TRANSACT2_FINDNEXT);
868 param = talloc_realloc(state, state->param, uint8_t, 12);
869 if (tevent_req_nomem(param, req)) {
870 return;
872 state->param = param;
874 SSVAL(param, 0, state->ff_dir_handle);
875 SSVAL(param, 2, state->max_matches); /* max count */
876 SSVAL(param, 4, state->info_level);
878 * For W2K servers serving out FAT filesystems we *must* set
879 * the resume key. If it's not FAT then it's returned as zero.
881 SIVAL(param, 6, resume_key); /* ff_resume_key */
883 * NB. *DON'T* use continue here. If you do it seems that W2K
884 * and brethren can miss filenames. Use last filename
885 * continue instead. JRA
887 SSVAL(param, 10, (FLAG_TRANS2_FIND_REQUIRE_RESUME
888 |FLAG_TRANS2_FIND_CLOSE_IF_END
889 |(state->cli->backup_intent ? FLAG_TRANS2_FIND_BACKUP_INTENT : 0)));
890 if (last_name_raw.length) {
891 state->param = trans2_bytes_push_bytes(state->param,
892 last_name_raw.data,
893 last_name_raw.length);
894 if (tevent_req_nomem(state->param, req)) {
895 return;
897 data_blob_free(&last_name_raw);
898 } else {
899 state->param = trans2_bytes_push_str(state->param,
900 smbXcli_conn_use_unicode(state->cli->conn),
901 state->mask,
902 strlen(state->mask)+1,
903 NULL);
904 if (tevent_req_nomem(state->param, req)) {
905 return;
908 param_len = talloc_get_size(state->param);
910 if (clistr_is_previous_version_path(state->mask)) {
911 additional_flags2 = FLAGS2_REPARSE_PATH;
914 subreq = cli_trans_send(state, state->ev, state->cli, additional_flags2,
915 SMBtrans2, NULL, -1, 0, 0,
916 state->setup, 1, 0,
917 state->param, param_len, 10,
918 NULL, 0, CLI_BUFFER_SIZE);
919 if (tevent_req_nomem(subreq, req)) {
920 return;
922 tevent_req_set_callback(subreq, cli_list_trans_done, req);
925 static NTSTATUS cli_list_trans_recv(struct tevent_req *req,
926 TALLOC_CTX *mem_ctx,
927 struct file_info **finfo)
929 struct cli_list_trans_state *state = tevent_req_data(
930 req, struct cli_list_trans_state);
931 NTSTATUS status;
933 if (tevent_req_is_nterror(req, &status)) {
934 return status;
936 *finfo = talloc_move(mem_ctx, &state->finfo);
937 return NT_STATUS_OK;
940 NTSTATUS cli_list_trans(struct cli_state *cli, const char *mask,
941 uint32_t attribute, int info_level,
942 NTSTATUS (*fn)(
943 struct file_info *finfo,
944 const char *mask,
945 void *private_data),
946 void *private_data)
948 TALLOC_CTX *frame = talloc_stackframe();
949 struct tevent_context *ev;
950 struct tevent_req *req;
951 int i, num_finfo;
952 struct file_info *finfo = NULL;
953 NTSTATUS status = NT_STATUS_NO_MEMORY;
955 if (smbXcli_conn_has_async_calls(cli->conn)) {
957 * Can't use sync call while an async call is in flight
959 status = NT_STATUS_INVALID_PARAMETER;
960 goto fail;
962 ev = samba_tevent_context_init(frame);
963 if (ev == NULL) {
964 goto fail;
966 req = cli_list_trans_send(frame, ev, cli, mask, attribute, info_level);
967 if (req == NULL) {
968 goto fail;
970 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
971 goto fail;
973 status = cli_list_trans_recv(req, frame, &finfo);
974 if (!NT_STATUS_IS_OK(status)) {
975 goto fail;
977 num_finfo = talloc_array_length(finfo);
978 for (i=0; i<num_finfo; i++) {
979 status = fn(&finfo[i], mask, private_data);
980 if (!NT_STATUS_IS_OK(status)) {
981 goto fail;
984 fail:
985 TALLOC_FREE(frame);
986 return status;
989 struct cli_list_state {
990 struct tevent_context *ev;
991 struct tevent_req *subreq;
992 NTSTATUS (*recv_fn)(struct tevent_req *req, TALLOC_CTX *mem_ctx,
993 struct file_info **finfo);
994 struct file_info *finfo;
995 size_t num_received;
998 static void cli_list_done(struct tevent_req *subreq);
1000 struct tevent_req *cli_list_send(TALLOC_CTX *mem_ctx,
1001 struct tevent_context *ev,
1002 struct cli_state *cli,
1003 const char *mask,
1004 uint32_t attribute,
1005 uint16_t info_level)
1007 struct tevent_req *req = NULL;
1008 struct cli_list_state *state;
1009 enum protocol_types proto = smbXcli_conn_protocol(cli->conn);
1011 req = tevent_req_create(mem_ctx, &state, struct cli_list_state);
1012 if (req == NULL) {
1013 return NULL;
1015 state->ev = ev;
1017 if (proto >= PROTOCOL_SMB2_02) {
1018 state->subreq = cli_smb2_list_send(state, ev, cli, mask,
1019 info_level);
1020 state->recv_fn = cli_smb2_list_recv;
1021 } else if (proto >= PROTOCOL_LANMAN2) {
1022 state->subreq = cli_list_trans_send(
1023 state, ev, cli, mask, attribute, info_level);
1024 state->recv_fn = cli_list_trans_recv;
1025 } else {
1026 state->subreq = cli_list_old_send(
1027 state, ev, cli, mask, attribute);
1028 state->recv_fn = cli_list_old_recv;
1030 if (tevent_req_nomem(state->subreq, req)) {
1031 return tevent_req_post(req, ev);
1033 tevent_req_set_callback(state->subreq, cli_list_done, req);
1034 return req;
1037 static void cli_list_done(struct tevent_req *subreq)
1039 struct tevent_req *req = tevent_req_callback_data(
1040 subreq, struct tevent_req);
1041 struct cli_list_state *state = tevent_req_data(
1042 req, struct cli_list_state);
1043 NTSTATUS status;
1045 SMB_ASSERT(subreq == state->subreq);
1048 * We don't want to be called by the lowerlevel routines
1049 * from within state->recv_fn()
1051 tevent_req_set_callback(subreq, NULL, NULL);
1053 status = state->recv_fn(subreq, state, &state->finfo);
1054 if (NT_STATUS_EQUAL(status, NT_STATUS_RETRY)) {
1055 /* We'll get back here */
1056 tevent_req_set_callback(subreq, cli_list_done, req);
1057 return;
1060 if (tevent_req_nterror(req, status)) {
1061 return;
1063 tevent_req_notify_callback(req);
1066 NTSTATUS cli_list_recv(
1067 struct tevent_req *req,
1068 TALLOC_CTX *mem_ctx,
1069 struct file_info **pfinfo)
1071 struct cli_list_state *state = tevent_req_data(
1072 req, struct cli_list_state);
1073 size_t num_results;
1074 struct file_info *finfo = NULL;
1075 NTSTATUS status;
1076 bool in_progress;
1078 in_progress = tevent_req_is_in_progress(req);
1080 if (!in_progress) {
1081 if (!tevent_req_is_nterror(req, &status)) {
1082 status = NT_STATUS_NO_MORE_FILES;
1084 return status;
1087 if (state->finfo == NULL) {
1088 status = state->recv_fn(state->subreq, state, &state->finfo);
1090 if (NT_STATUS_EQUAL(status, NT_STATUS_RETRY)) {
1091 tevent_req_set_callback(
1092 state->subreq, cli_list_done, req);
1093 return NT_STATUS_RETRY;
1096 if (NT_STATUS_IS_OK(status) && (state->finfo == NULL)) {
1097 status = NT_STATUS_NO_MORE_FILES;
1100 if (tevent_req_nterror(req, status)) {
1101 return status;
1104 state->num_received = 0;
1107 num_results = talloc_array_length(state->finfo);
1109 if (num_results == 1) {
1110 finfo = talloc_move(mem_ctx, &state->finfo);
1111 } else {
1112 struct file_info *src_finfo =
1113 &state->finfo[state->num_received];
1115 finfo = talloc(mem_ctx, struct file_info);
1116 if (finfo == NULL) {
1117 return NT_STATUS_NO_MEMORY;
1119 *finfo = *src_finfo;
1120 finfo->name = talloc_move(finfo, &src_finfo->name);
1121 finfo->short_name = talloc_move(finfo, &src_finfo->short_name);
1124 state->num_received += 1;
1126 if (state->num_received == num_results) {
1127 TALLOC_FREE(state->finfo);
1130 tevent_req_defer_callback(req, state->ev);
1131 tevent_req_notify_callback(req);
1133 *pfinfo = finfo;
1134 return NT_STATUS_OK;
1137 struct cli_list_sync_state {
1138 const char *mask;
1139 uint32_t attribute;
1140 NTSTATUS (*fn)(struct file_info *finfo,
1141 const char *mask,
1142 void *private_data);
1143 void *private_data;
1144 NTSTATUS status;
1145 bool processed_file;
1148 static void cli_list_sync_cb(struct tevent_req *subreq)
1150 struct cli_list_sync_state *state =
1151 tevent_req_callback_data_void(subreq);
1152 struct file_info *finfo;
1153 bool ok;
1155 state->status = cli_list_recv(subreq, talloc_tos(), &finfo);
1156 /* No TALLOC_FREE(subreq), we get here more than once */
1158 if (NT_STATUS_EQUAL(state->status, NT_STATUS_RETRY)) {
1160 * The lowlevel SMB call was rearmed, we'll get back
1161 * here when it's done.
1163 state->status = NT_STATUS_OK;
1164 return;
1167 if (!NT_STATUS_IS_OK(state->status)) {
1168 return;
1171 ok = dir_check_ftype(finfo->attr, state->attribute);
1172 if (!ok) {
1174 * Only process if attributes match. On SMB1 server
1175 * does this, so on SMB2 we need to emulate in the
1176 * client.
1178 * https://bugzilla.samba.org/show_bug.cgi?id=10260
1180 return;
1183 state->status = state->fn(finfo, state->mask, state->private_data);
1185 state->processed_file = true;
1187 TALLOC_FREE(finfo);
1190 NTSTATUS cli_list(struct cli_state *cli,
1191 const char *mask,
1192 uint32_t attribute,
1193 NTSTATUS (*fn)(struct file_info *finfo,
1194 const char *mask,
1195 void *private_data),
1196 void *private_data)
1198 TALLOC_CTX *frame = NULL;
1199 struct cli_list_sync_state state = {
1200 .mask = mask,
1201 .attribute = attribute,
1202 .fn = fn,
1203 .private_data = private_data,
1205 struct tevent_context *ev;
1206 struct tevent_req *req;
1207 NTSTATUS status = NT_STATUS_NO_MEMORY;
1208 uint16_t info_level;
1209 enum protocol_types proto = smbXcli_conn_protocol(cli->conn);
1211 frame = talloc_stackframe();
1213 if (smbXcli_conn_has_async_calls(cli->conn)) {
1215 * Can't use sync call while an async call is in flight
1217 status = NT_STATUS_INVALID_PARAMETER;
1218 goto fail;
1220 ev = samba_tevent_context_init(frame);
1221 if (ev == NULL) {
1222 goto fail;
1225 if (proto >= PROTOCOL_SMB2_02) {
1226 info_level = SMB2_FIND_ID_BOTH_DIRECTORY_INFO;
1227 } else {
1228 info_level = (smb1cli_conn_capabilities(cli->conn) & CAP_NT_SMBS)
1229 ? SMB_FIND_FILE_BOTH_DIRECTORY_INFO : SMB_FIND_INFO_STANDARD;
1232 req = cli_list_send(frame, ev, cli, mask, attribute, info_level);
1233 if (req == NULL) {
1234 goto fail;
1236 tevent_req_set_callback(req, cli_list_sync_cb, &state);
1238 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
1239 goto fail;
1242 status = state.status;
1244 if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_FILES)) {
1245 status = NT_STATUS_OK;
1248 if (NT_STATUS_IS_OK(status) && !state.processed_file) {
1249 status = NT_STATUS_NO_SUCH_FILE;
1252 fail:
1253 TALLOC_FREE(frame);
1254 return status;