smbd: set fsp_flags.is_fsa to true on printer file handles
[Samba.git] / source3 / libsmb / clilist.c
blob46d6d95b9819c28b6f157863e7f92b3c3a0ed5f3
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 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,
1006 bool posix)
1008 struct tevent_req *req = NULL;
1009 struct cli_list_state *state;
1010 enum protocol_types proto = smbXcli_conn_protocol(cli->conn);
1012 req = tevent_req_create(mem_ctx, &state, struct cli_list_state);
1013 if (req == NULL) {
1014 return NULL;
1016 state->ev = ev;
1018 if (proto >= PROTOCOL_SMB2_02) {
1019 state->subreq = cli_smb2_list_send(state, ev, cli, mask,
1020 info_level, posix);
1021 state->recv_fn = cli_smb2_list_recv;
1022 } else if (proto >= PROTOCOL_LANMAN2) {
1023 state->subreq = cli_list_trans_send(
1024 state, ev, cli, mask, attribute, info_level);
1025 state->recv_fn = cli_list_trans_recv;
1026 } else {
1027 state->subreq = cli_list_old_send(
1028 state, ev, cli, mask, attribute);
1029 state->recv_fn = cli_list_old_recv;
1031 if (tevent_req_nomem(state->subreq, req)) {
1032 return tevent_req_post(req, ev);
1034 tevent_req_set_callback(state->subreq, cli_list_done, req);
1035 return req;
1038 static void cli_list_done(struct tevent_req *subreq)
1040 struct tevent_req *req = tevent_req_callback_data(
1041 subreq, struct tevent_req);
1042 struct cli_list_state *state = tevent_req_data(
1043 req, struct cli_list_state);
1044 NTSTATUS status;
1046 SMB_ASSERT(subreq == state->subreq);
1049 * We don't want to be called by the lowerlevel routines
1050 * from within state->recv_fn()
1052 tevent_req_set_callback(subreq, NULL, NULL);
1054 status = state->recv_fn(subreq, state, &state->finfo);
1055 if (NT_STATUS_EQUAL(status, NT_STATUS_RETRY)) {
1056 /* We'll get back here */
1057 tevent_req_set_callback(subreq, cli_list_done, req);
1058 return;
1061 if (tevent_req_nterror(req, status)) {
1062 return;
1064 tevent_req_notify_callback(req);
1067 NTSTATUS cli_list_recv(
1068 struct tevent_req *req,
1069 TALLOC_CTX *mem_ctx,
1070 struct file_info **pfinfo)
1072 struct cli_list_state *state = tevent_req_data(
1073 req, struct cli_list_state);
1074 size_t num_results;
1075 struct file_info *finfo = NULL;
1076 NTSTATUS status;
1077 bool in_progress;
1079 in_progress = tevent_req_is_in_progress(req);
1081 if (!in_progress) {
1082 if (!tevent_req_is_nterror(req, &status)) {
1083 status = NT_STATUS_NO_MORE_FILES;
1085 return status;
1088 if (state->finfo == NULL) {
1089 status = state->recv_fn(state->subreq, state, &state->finfo);
1091 if (NT_STATUS_EQUAL(status, NT_STATUS_RETRY)) {
1092 tevent_req_set_callback(
1093 state->subreq, cli_list_done, req);
1094 return NT_STATUS_RETRY;
1097 if (NT_STATUS_IS_OK(status) && (state->finfo == NULL)) {
1098 status = NT_STATUS_NO_MORE_FILES;
1101 if (tevent_req_nterror(req, status)) {
1102 return status;
1105 state->num_received = 0;
1108 num_results = talloc_array_length(state->finfo);
1110 if (num_results == 1) {
1111 finfo = talloc_move(mem_ctx, &state->finfo);
1112 } else {
1113 struct file_info *src_finfo =
1114 &state->finfo[state->num_received];
1116 finfo = talloc(mem_ctx, struct file_info);
1117 if (finfo == NULL) {
1118 return NT_STATUS_NO_MEMORY;
1120 *finfo = *src_finfo;
1121 finfo->name = talloc_move(finfo, &src_finfo->name);
1122 finfo->short_name = talloc_move(finfo, &src_finfo->short_name);
1125 state->num_received += 1;
1127 if (state->num_received == num_results) {
1128 TALLOC_FREE(state->finfo);
1131 tevent_req_defer_callback(req, state->ev);
1132 tevent_req_notify_callback(req);
1134 *pfinfo = finfo;
1135 return NT_STATUS_OK;
1138 struct cli_list_sync_state {
1139 const char *mask;
1140 uint32_t attribute;
1141 NTSTATUS (*fn)(struct file_info *finfo,
1142 const char *mask,
1143 void *private_data);
1144 void *private_data;
1145 NTSTATUS status;
1146 bool processed_file;
1149 static void cli_list_sync_cb(struct tevent_req *subreq)
1151 struct cli_list_sync_state *state =
1152 tevent_req_callback_data_void(subreq);
1153 struct file_info *finfo;
1154 bool ok;
1156 state->status = cli_list_recv(subreq, talloc_tos(), &finfo);
1157 /* No TALLOC_FREE(subreq), we get here more than once */
1159 if (NT_STATUS_EQUAL(state->status, NT_STATUS_RETRY)) {
1161 * The lowlevel SMB call was rearmed, we'll get back
1162 * here when it's done.
1164 state->status = NT_STATUS_OK;
1165 return;
1168 if (!NT_STATUS_IS_OK(state->status)) {
1169 return;
1172 ok = dir_check_ftype(finfo->attr, state->attribute);
1173 if (!ok) {
1175 * Only process if attributes match. On SMB1 server
1176 * does this, so on SMB2 we need to emulate in the
1177 * client.
1179 * https://bugzilla.samba.org/show_bug.cgi?id=10260
1181 return;
1184 state->status = state->fn(finfo, state->mask, state->private_data);
1186 state->processed_file = true;
1188 TALLOC_FREE(finfo);
1191 NTSTATUS cli_list(struct cli_state *cli,
1192 const char *mask,
1193 uint32_t attribute,
1194 NTSTATUS (*fn)(struct file_info *finfo,
1195 const char *mask,
1196 void *private_data),
1197 void *private_data)
1199 TALLOC_CTX *frame = NULL;
1200 struct cli_list_sync_state state = {
1201 .mask = mask,
1202 .attribute = attribute,
1203 .fn = fn,
1204 .private_data = private_data,
1206 struct tevent_context *ev;
1207 struct tevent_req *req;
1208 NTSTATUS status = NT_STATUS_NO_MEMORY;
1209 uint16_t info_level;
1210 enum protocol_types proto = smbXcli_conn_protocol(cli->conn);
1212 frame = talloc_stackframe();
1214 if (smbXcli_conn_has_async_calls(cli->conn)) {
1216 * Can't use sync call while an async call is in flight
1218 status = NT_STATUS_INVALID_PARAMETER;
1219 goto fail;
1221 ev = samba_tevent_context_init(frame);
1222 if (ev == NULL) {
1223 goto fail;
1226 if (proto >= PROTOCOL_SMB2_02) {
1227 info_level = SMB2_FIND_ID_BOTH_DIRECTORY_INFO;
1228 } else {
1229 info_level = (smb1cli_conn_capabilities(cli->conn) & CAP_NT_SMBS)
1230 ? SMB_FIND_FILE_BOTH_DIRECTORY_INFO : SMB_FIND_INFO_STANDARD;
1233 req = cli_list_send(frame, ev, cli, mask, attribute, info_level, false);
1234 if (req == NULL) {
1235 goto fail;
1237 tevent_req_set_callback(req, cli_list_sync_cb, &state);
1239 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
1240 goto fail;
1243 status = state.status;
1245 if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_FILES)) {
1246 status = NT_STATUS_OK;
1249 if (NT_STATUS_IS_OK(status) && !state.processed_file) {
1250 status = NT_STATUS_NO_SUCH_FILE;
1253 fail:
1254 TALLOC_FREE(frame);
1255 return status;