s3:tests: let modprinter.pl use $TMPDIR
[Samba.git] / source3 / libsmb / clilist.c
blobd8951482bacaf1e4675fc13b06c17dced9654a57
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 = BVAL(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 *p_last_name_raw = data_blob(NULL, namelen + 2);
302 memcpy(p_last_name_raw->data, p, namelen);
303 SSVAL(p_last_name_raw->data, namelen, 0);
305 return calc_next_entry_offset(base, pdata_end);
309 DEBUG(1,("Unknown long filename format %d\n",level));
310 return calc_next_entry_offset(base, pdata_end);
313 /****************************************************************************
314 Interpret a short filename structure.
315 The length of the structure is returned.
316 ****************************************************************************/
318 static bool interpret_short_filename(TALLOC_CTX *ctx,
319 struct cli_state *cli,
320 char *p,
321 struct file_info *finfo)
323 size_t ret;
324 ZERO_STRUCTP(finfo);
326 finfo->attr = CVAL(p,21);
328 /* We don't get birth time. */
329 finfo->btime_ts.tv_sec = 0;
330 finfo->btime_ts.tv_nsec = 0;
331 /* this date is converted to GMT by make_unix_date */
332 finfo->ctime_ts.tv_sec = make_unix_date(p+22, smb1cli_conn_server_time_zone(cli->conn));
333 finfo->ctime_ts.tv_nsec = 0;
334 finfo->mtime_ts.tv_sec = finfo->atime_ts.tv_sec = finfo->ctime_ts.tv_sec;
335 finfo->mtime_ts.tv_nsec = finfo->atime_ts.tv_nsec = 0;
336 finfo->size = IVAL(p,26);
337 ret = pull_string_talloc(ctx,
338 NULL,
340 &finfo->name,
341 p+30,
343 STR_ASCII);
344 if (ret == (size_t)-1) {
345 return false;
348 if (finfo->name) {
349 finfo->short_name = talloc_strdup(ctx, finfo->name);
350 if (finfo->short_name == NULL) {
351 return false;
354 return true;
357 struct cli_list_old_state {
358 struct tevent_context *ev;
359 struct cli_state *cli;
360 uint16_t vwv[2];
361 char *mask;
362 int num_asked;
363 uint32_t attribute;
364 uint8_t search_status[23];
365 bool first;
366 bool done;
367 uint8_t *dirlist;
370 static void cli_list_old_done(struct tevent_req *subreq);
372 static struct tevent_req *cli_list_old_send(TALLOC_CTX *mem_ctx,
373 struct tevent_context *ev,
374 struct cli_state *cli,
375 const char *mask,
376 uint32_t attribute)
378 struct tevent_req *req, *subreq;
379 struct cli_list_old_state *state;
380 uint8_t *bytes;
381 static const uint16_t zero = 0;
382 uint32_t usable_space;
384 req = tevent_req_create(mem_ctx, &state, struct cli_list_old_state);
385 if (req == NULL) {
386 return NULL;
388 state->ev = ev;
389 state->cli = cli;
390 state->attribute = attribute;
391 state->first = true;
392 state->mask = talloc_strdup(state, mask);
393 if (tevent_req_nomem(state->mask, req)) {
394 return tevent_req_post(req, ev);
396 state->mask = smb1_dfs_share_path(state, cli, state->mask);
397 if (tevent_req_nomem(state->mask, req)) {
398 return tevent_req_post(req, ev);
400 usable_space = cli_state_available_size(cli, 100);
401 state->num_asked = usable_space / DIR_STRUCT_SIZE;
403 SSVAL(state->vwv + 0, 0, state->num_asked);
404 SSVAL(state->vwv + 1, 0, state->attribute);
406 bytes = talloc_array(state, uint8_t, 1);
407 if (tevent_req_nomem(bytes, req)) {
408 return tevent_req_post(req, ev);
410 bytes[0] = 4;
411 bytes = smb_bytes_push_str(bytes,
412 smbXcli_conn_use_unicode(cli->conn),
413 state->mask,
414 strlen(state->mask)+1,
415 NULL);
417 bytes = smb_bytes_push_bytes(bytes, 5, (const uint8_t *)&zero, 2);
418 if (tevent_req_nomem(bytes, req)) {
419 return tevent_req_post(req, ev);
422 subreq = cli_smb_send(state, state->ev, state->cli, SMBsearch, 0, 0,
423 2, state->vwv, talloc_get_size(bytes), bytes);
424 if (tevent_req_nomem(subreq, req)) {
425 return tevent_req_post(req, ev);
427 tevent_req_set_callback(subreq, cli_list_old_done, req);
428 return req;
431 static void cli_list_old_done(struct tevent_req *subreq)
433 struct tevent_req *req = tevent_req_callback_data(
434 subreq, struct tevent_req);
435 struct cli_list_old_state *state = tevent_req_data(
436 req, struct cli_list_old_state);
437 NTSTATUS status;
438 uint8_t cmd;
439 uint8_t wct;
440 uint16_t *vwv;
441 uint32_t num_bytes;
442 uint8_t *bytes;
443 uint16_t received;
444 size_t dirlist_len;
445 uint8_t *tmp;
447 status = cli_smb_recv(subreq, state, NULL, 0, &wct, &vwv, &num_bytes,
448 &bytes);
449 if (!NT_STATUS_IS_OK(status)
450 && !NT_STATUS_EQUAL(status, NT_STATUS_DOS(ERRDOS, ERRnofiles))
451 && !NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) {
452 TALLOC_FREE(subreq);
453 tevent_req_nterror(req, status);
454 return;
456 if (NT_STATUS_EQUAL(status, NT_STATUS_DOS(ERRDOS, ERRnofiles))
457 || NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) {
458 received = 0;
459 } else {
460 if (wct < 1) {
461 TALLOC_FREE(subreq);
462 tevent_req_nterror(
463 req, NT_STATUS_INVALID_NETWORK_RESPONSE);
464 return;
466 received = SVAL(vwv + 0, 0);
469 if (received > 0) {
471 * I don't think this can wrap. received is
472 * initialized from a 16-bit value.
474 if (num_bytes < ((uint32_t)received * DIR_STRUCT_SIZE + 3)) {
475 TALLOC_FREE(subreq);
476 tevent_req_nterror(
477 req, NT_STATUS_INVALID_NETWORK_RESPONSE);
478 return;
481 dirlist_len = talloc_get_size(state->dirlist);
483 tmp = talloc_realloc(
484 state, state->dirlist, uint8_t,
485 dirlist_len + received * DIR_STRUCT_SIZE);
486 if (tevent_req_nomem(tmp, req)) {
487 return;
489 state->dirlist = tmp;
490 memcpy(state->dirlist + dirlist_len, bytes + 3,
491 received * DIR_STRUCT_SIZE);
493 SSVAL(state->search_status, 0, 21);
494 memcpy(state->search_status + 2,
495 bytes + 3 + (received-1)*DIR_STRUCT_SIZE, 21);
496 cmd = SMBsearch;
497 } else {
498 if (state->first || state->done) {
499 tevent_req_done(req);
500 return;
502 state->done = true;
503 state->num_asked = 0;
504 cmd = SMBfclose;
506 TALLOC_FREE(subreq);
508 state->first = false;
510 SSVAL(state->vwv + 0, 0, state->num_asked);
511 SSVAL(state->vwv + 1, 0, state->attribute);
513 bytes = talloc_array(state, uint8_t, 1);
514 if (tevent_req_nomem(bytes, req)) {
515 return;
517 bytes[0] = 4;
518 bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(state->cli->conn), "",
519 1, NULL);
520 bytes = smb_bytes_push_bytes(bytes, 5, state->search_status,
521 sizeof(state->search_status));
522 if (tevent_req_nomem(bytes, req)) {
523 return;
525 subreq = cli_smb_send(state, state->ev, state->cli, cmd, 0, 0,
526 2, state->vwv, talloc_get_size(bytes), bytes);
527 if (tevent_req_nomem(subreq, req)) {
528 return;
530 tevent_req_set_callback(subreq, cli_list_old_done, req);
533 static NTSTATUS cli_list_old_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
534 struct file_info **pfinfo)
536 struct cli_list_old_state *state = tevent_req_data(
537 req, struct cli_list_old_state);
538 NTSTATUS status;
539 size_t i, num_received;
540 struct file_info *finfo;
542 if (tevent_req_is_nterror(req, &status)) {
543 return status;
546 if (state->dirlist == NULL) {
547 *pfinfo = NULL;
548 return NT_STATUS_OK;
551 num_received = talloc_array_length(state->dirlist) / DIR_STRUCT_SIZE;
553 finfo = talloc_array(mem_ctx, struct file_info, num_received);
554 if (finfo == NULL) {
555 return NT_STATUS_NO_MEMORY;
558 for (i=0; i<num_received; i++) {
559 if (!interpret_short_filename(
560 finfo, state->cli,
561 (char *)state->dirlist + i * DIR_STRUCT_SIZE,
562 &finfo[i])) {
563 TALLOC_FREE(finfo);
564 return NT_STATUS_NO_MEMORY;
566 if (finfo->name == NULL) {
567 TALLOC_FREE(finfo);
568 return NT_STATUS_INVALID_NETWORK_RESPONSE;
570 status = is_bad_finfo_name(state->cli, finfo);
571 if (!NT_STATUS_IS_OK(status)) {
572 smbXcli_conn_disconnect(state->cli->conn, status);
573 TALLOC_FREE(finfo);
574 return status;
577 TALLOC_FREE(state->dirlist);
578 *pfinfo = finfo;
579 return NT_STATUS_OK;
582 NTSTATUS cli_list_old(struct cli_state *cli, const char *mask,
583 uint32_t attribute,
584 NTSTATUS (*fn)(struct file_info *,
585 const char *, void *), void *state)
587 TALLOC_CTX *frame = talloc_stackframe();
588 struct tevent_context *ev;
589 struct tevent_req *req;
590 NTSTATUS status = NT_STATUS_NO_MEMORY;
591 struct file_info *finfo = NULL;
592 size_t i, num_finfo;
594 if (smbXcli_conn_has_async_calls(cli->conn)) {
596 * Can't use sync call while an async call is in flight
598 status = NT_STATUS_INVALID_PARAMETER;
599 goto fail;
601 ev = samba_tevent_context_init(frame);
602 if (ev == NULL) {
603 goto fail;
605 req = cli_list_old_send(frame, ev, cli, mask, attribute);
606 if (req == NULL) {
607 goto fail;
609 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
610 goto fail;
612 status = cli_list_old_recv(req, frame, &finfo);
613 if (!NT_STATUS_IS_OK(status)) {
614 goto fail;
616 num_finfo = talloc_array_length(finfo);
617 for (i=0; i<num_finfo; i++) {
618 status = fn(&finfo[i], mask, state);
619 if (!NT_STATUS_IS_OK(status)) {
620 goto fail;
623 fail:
624 TALLOC_FREE(frame);
625 return status;
628 struct cli_list_trans_state {
629 struct tevent_context *ev;
630 struct cli_state *cli;
631 char *mask;
632 uint32_t attribute;
633 uint16_t info_level;
635 int loop_count;
636 int total_received;
637 uint16_t max_matches;
638 bool first;
640 int ff_eos;
641 int ff_dir_handle;
643 uint16_t setup[1];
644 uint8_t *param;
646 struct file_info *finfo;
649 static void cli_list_trans_done(struct tevent_req *subreq);
651 static struct tevent_req *cli_list_trans_send(TALLOC_CTX *mem_ctx,
652 struct tevent_context *ev,
653 struct cli_state *cli,
654 const char *mask,
655 uint32_t attribute,
656 uint16_t info_level)
658 struct tevent_req *req, *subreq;
659 struct cli_list_trans_state *state;
660 size_t param_len;
661 uint16_t additional_flags2 = 0;
663 req = tevent_req_create(mem_ctx, &state,
664 struct cli_list_trans_state);
665 if (req == NULL) {
666 return NULL;
668 state->ev = ev;
669 state->cli = cli;
670 state->mask = smb1_dfs_share_path(state, cli, mask);
671 if (tevent_req_nomem(state->mask, req)) {
672 return tevent_req_post(req, ev);
674 state->attribute = attribute;
675 state->info_level = info_level;
676 state->loop_count = 0;
677 state->first = true;
679 state->max_matches = 1366; /* Match W2k */
681 SSVAL(&state->setup[0], 0, TRANSACT2_FINDFIRST);
683 state->param = talloc_array(state, uint8_t, 12);
684 if (tevent_req_nomem(state->param, req)) {
685 return tevent_req_post(req, ev);
688 SSVAL(state->param, 0, state->attribute);
689 SSVAL(state->param, 2, state->max_matches);
690 SSVAL(state->param, 4,
691 FLAG_TRANS2_FIND_REQUIRE_RESUME
692 |FLAG_TRANS2_FIND_CLOSE_IF_END
693 |(cli->backup_intent ? FLAG_TRANS2_FIND_BACKUP_INTENT : 0));
694 SSVAL(state->param, 6, state->info_level);
695 SIVAL(state->param, 8, 0);
697 state->param = trans2_bytes_push_str(state->param, smbXcli_conn_use_unicode(cli->conn),
698 state->mask, strlen(state->mask)+1,
699 NULL);
700 if (tevent_req_nomem(state->param, req)) {
701 return tevent_req_post(req, ev);
704 if (clistr_is_previous_version_path(state->mask)) {
705 additional_flags2 = FLAGS2_REPARSE_PATH;
708 param_len = talloc_get_size(state->param);
710 subreq = cli_trans_send(state, state->ev, state->cli, additional_flags2,
711 SMBtrans2, NULL, -1, 0, 0,
712 state->setup, 1, 0,
713 state->param, param_len, 10,
714 NULL, 0, CLI_BUFFER_SIZE);
715 if (tevent_req_nomem(subreq, req)) {
716 return tevent_req_post(req, ev);
718 tevent_req_set_callback(subreq, cli_list_trans_done, req);
719 return req;
722 static void cli_list_trans_done(struct tevent_req *subreq)
724 struct tevent_req *req = tevent_req_callback_data(
725 subreq, struct tevent_req);
726 struct cli_list_trans_state *state = tevent_req_data(
727 req, struct cli_list_trans_state);
728 NTSTATUS status;
729 uint8_t *param;
730 uint32_t num_param;
731 uint8_t *data;
732 char *data_end;
733 uint32_t num_data;
734 uint32_t min_param;
735 struct file_info *tmp;
736 size_t old_num_finfo;
737 uint16_t recv_flags2;
738 int ff_searchcount;
739 bool ff_eos;
740 char *p, *p2;
741 uint32_t resume_key = 0;
742 int i;
743 DATA_BLOB last_name_raw;
744 struct file_info *finfo = NULL;
745 size_t param_len;
746 uint16_t additional_flags2 = 0;
748 min_param = (state->first ? 6 : 4);
750 status = cli_trans_recv(subreq, talloc_tos(), &recv_flags2,
751 NULL, 0, NULL,
752 &param, min_param, &num_param,
753 &data, 0, &num_data);
754 TALLOC_FREE(subreq);
755 if (!NT_STATUS_IS_OK(status)) {
757 * TODO: retry, OS/2 nofiles
759 tevent_req_nterror(req, status);
760 return;
763 if (state->first) {
764 state->ff_dir_handle = SVAL(param, 0);
765 ff_searchcount = SVAL(param, 2);
766 ff_eos = SVAL(param, 4) != 0;
767 } else {
768 ff_searchcount = SVAL(param, 0);
769 ff_eos = SVAL(param, 2) != 0;
772 old_num_finfo = talloc_array_length(state->finfo);
774 tmp = talloc_realloc(state, state->finfo, struct file_info,
775 old_num_finfo + ff_searchcount);
776 if (tevent_req_nomem(tmp, req)) {
777 return;
779 state->finfo = tmp;
781 p2 = p = (char *)data;
782 data_end = (char *)data + num_data;
783 last_name_raw = data_blob_null;
785 for (i=0; i<ff_searchcount; i++) {
786 if (p2 >= data_end) {
787 ff_eos = true;
788 break;
790 if ((state->info_level == SMB_FIND_FILE_BOTH_DIRECTORY_INFO)
791 && (i == ff_searchcount-1)) {
792 /* Last entry - fixup the last offset length. */
793 SIVAL(p2, 0, PTR_DIFF((data + num_data), p2));
796 data_blob_free(&last_name_raw);
798 finfo = &state->finfo[old_num_finfo + i];
800 p2 += interpret_long_filename(
801 state->finfo, /* Stick fname to the array as such */
802 state->cli, state->info_level,
803 (char *)data, recv_flags2, p2,
804 data_end, finfo, &resume_key, &last_name_raw);
806 if (finfo->name == NULL) {
807 DEBUG(1, ("cli_list: Error: unable to parse name from "
808 "info level %d\n", state->info_level));
809 tevent_req_nterror(req,
810 NT_STATUS_INVALID_NETWORK_RESPONSE);
811 return;
814 status = is_bad_finfo_name(state->cli, finfo);
815 if (!NT_STATUS_IS_OK(status)) {
816 smbXcli_conn_disconnect(state->cli->conn, status);
817 tevent_req_nterror(req, status);
818 return;
821 if (!state->first && (state->mask[0] != '\0') &&
822 strcsequal(finfo->name, state->mask)) {
823 DEBUG(1, ("Error: Looping in FIND_NEXT as name %s has "
824 "already been seen?\n", finfo->name));
825 ff_eos = true;
826 break;
830 if (ff_searchcount == 0) {
831 ff_eos = true;
834 TALLOC_FREE(param);
835 TALLOC_FREE(data);
838 * Shrink state->finfo to the real length we received
840 tmp = talloc_realloc(state, state->finfo, struct file_info,
841 old_num_finfo + i);
842 if (tevent_req_nomem(tmp, req)) {
843 return;
845 state->finfo = tmp;
847 state->first = false;
849 if (ff_eos) {
850 data_blob_free(&last_name_raw);
851 tevent_req_done(req);
852 return;
855 TALLOC_FREE(state->mask);
856 state->mask = talloc_strdup(state, finfo->name);
857 if (tevent_req_nomem(state->mask, req)) {
858 return;
861 SSVAL(&state->setup[0], 0, TRANSACT2_FINDNEXT);
863 param = talloc_realloc(state, state->param, uint8_t, 12);
864 if (tevent_req_nomem(param, req)) {
865 return;
867 state->param = param;
869 SSVAL(param, 0, state->ff_dir_handle);
870 SSVAL(param, 2, state->max_matches); /* max count */
871 SSVAL(param, 4, state->info_level);
873 * For W2K servers serving out FAT filesystems we *must* set
874 * the resume key. If it's not FAT then it's returned as zero.
876 SIVAL(param, 6, resume_key); /* ff_resume_key */
878 * NB. *DON'T* use continue here. If you do it seems that W2K
879 * and brethren can miss filenames. Use last filename
880 * continue instead. JRA
882 SSVAL(param, 10, (FLAG_TRANS2_FIND_REQUIRE_RESUME
883 |FLAG_TRANS2_FIND_CLOSE_IF_END
884 |(state->cli->backup_intent ? FLAG_TRANS2_FIND_BACKUP_INTENT : 0)));
885 if (last_name_raw.length) {
886 state->param = trans2_bytes_push_bytes(state->param,
887 last_name_raw.data,
888 last_name_raw.length);
889 if (tevent_req_nomem(state->param, req)) {
890 return;
892 data_blob_free(&last_name_raw);
893 } else {
894 state->param = trans2_bytes_push_str(state->param,
895 smbXcli_conn_use_unicode(state->cli->conn),
896 state->mask,
897 strlen(state->mask)+1,
898 NULL);
899 if (tevent_req_nomem(state->param, req)) {
900 return;
903 param_len = talloc_get_size(state->param);
905 if (clistr_is_previous_version_path(state->mask)) {
906 additional_flags2 = FLAGS2_REPARSE_PATH;
909 subreq = cli_trans_send(state, state->ev, state->cli, additional_flags2,
910 SMBtrans2, NULL, -1, 0, 0,
911 state->setup, 1, 0,
912 state->param, param_len, 10,
913 NULL, 0, CLI_BUFFER_SIZE);
914 if (tevent_req_nomem(subreq, req)) {
915 return;
917 tevent_req_set_callback(subreq, cli_list_trans_done, req);
920 static NTSTATUS cli_list_trans_recv(struct tevent_req *req,
921 TALLOC_CTX *mem_ctx,
922 struct file_info **finfo)
924 struct cli_list_trans_state *state = tevent_req_data(
925 req, struct cli_list_trans_state);
926 NTSTATUS status;
928 if (tevent_req_is_nterror(req, &status)) {
929 return status;
931 *finfo = talloc_move(mem_ctx, &state->finfo);
932 return NT_STATUS_OK;
935 struct cli_list_state {
936 struct tevent_context *ev;
937 struct tevent_req *subreq;
938 NTSTATUS (*recv_fn)(struct tevent_req *req, TALLOC_CTX *mem_ctx,
939 struct file_info **finfo);
940 struct file_info *finfo;
941 size_t num_received;
944 static void cli_list_done(struct tevent_req *subreq);
946 struct tevent_req *cli_list_send(TALLOC_CTX *mem_ctx,
947 struct tevent_context *ev,
948 struct cli_state *cli,
949 const char *mask,
950 uint32_t attribute,
951 uint16_t info_level)
953 struct tevent_req *req = NULL;
954 struct cli_list_state *state;
955 enum protocol_types proto = smbXcli_conn_protocol(cli->conn);
957 req = tevent_req_create(mem_ctx, &state, struct cli_list_state);
958 if (req == NULL) {
959 return NULL;
961 state->ev = ev;
963 if (proto >= PROTOCOL_SMB2_02) {
964 state->subreq = cli_smb2_list_send(state, ev, cli, mask,
965 info_level);
966 state->recv_fn = cli_smb2_list_recv;
967 } else if (proto >= PROTOCOL_LANMAN2) {
968 state->subreq = cli_list_trans_send(
969 state, ev, cli, mask, attribute, info_level);
970 state->recv_fn = cli_list_trans_recv;
971 } else {
972 state->subreq = cli_list_old_send(
973 state, ev, cli, mask, attribute);
974 state->recv_fn = cli_list_old_recv;
976 if (tevent_req_nomem(state->subreq, req)) {
977 return tevent_req_post(req, ev);
979 tevent_req_set_callback(state->subreq, cli_list_done, req);
980 return req;
983 static void cli_list_done(struct tevent_req *subreq)
985 struct tevent_req *req = tevent_req_callback_data(
986 subreq, struct tevent_req);
987 struct cli_list_state *state = tevent_req_data(
988 req, struct cli_list_state);
989 NTSTATUS status;
991 SMB_ASSERT(subreq == state->subreq);
994 * We don't want to be called by the lowerlevel routines
995 * from within state->recv_fn()
997 tevent_req_set_callback(subreq, NULL, NULL);
999 status = state->recv_fn(subreq, state, &state->finfo);
1000 if (NT_STATUS_EQUAL(status, NT_STATUS_RETRY)) {
1001 /* We'll get back here */
1002 tevent_req_set_callback(subreq, cli_list_done, req);
1003 return;
1006 if (tevent_req_nterror(req, status)) {
1007 return;
1009 tevent_req_notify_callback(req);
1012 NTSTATUS cli_list_recv(
1013 struct tevent_req *req,
1014 TALLOC_CTX *mem_ctx,
1015 struct file_info **pfinfo)
1017 struct cli_list_state *state = tevent_req_data(
1018 req, struct cli_list_state);
1019 size_t num_results;
1020 struct file_info *finfo = NULL;
1021 NTSTATUS status;
1022 bool in_progress;
1024 in_progress = tevent_req_is_in_progress(req);
1026 if (!in_progress) {
1027 if (!tevent_req_is_nterror(req, &status)) {
1028 status = NT_STATUS_NO_MORE_FILES;
1030 return status;
1033 if (state->finfo == NULL) {
1034 status = state->recv_fn(state->subreq, state, &state->finfo);
1036 if (NT_STATUS_EQUAL(status, NT_STATUS_RETRY)) {
1037 tevent_req_set_callback(
1038 state->subreq, cli_list_done, req);
1039 return NT_STATUS_RETRY;
1042 if (NT_STATUS_IS_OK(status) && (state->finfo == NULL)) {
1043 status = NT_STATUS_NO_MORE_FILES;
1046 if (tevent_req_nterror(req, status)) {
1047 return status;
1050 state->num_received = 0;
1053 num_results = talloc_array_length(state->finfo);
1055 if (num_results == 1) {
1056 finfo = talloc_move(mem_ctx, &state->finfo);
1057 } else {
1058 struct file_info *src_finfo =
1059 &state->finfo[state->num_received];
1061 finfo = talloc(mem_ctx, struct file_info);
1062 if (finfo == NULL) {
1063 return NT_STATUS_NO_MEMORY;
1065 *finfo = *src_finfo;
1066 finfo->name = talloc_move(finfo, &src_finfo->name);
1067 finfo->short_name = talloc_move(finfo, &src_finfo->short_name);
1070 state->num_received += 1;
1072 if (state->num_received == num_results) {
1073 TALLOC_FREE(state->finfo);
1076 tevent_req_defer_callback(req, state->ev);
1077 tevent_req_notify_callback(req);
1079 *pfinfo = finfo;
1080 return NT_STATUS_OK;
1083 struct cli_list_sync_state {
1084 const char *mask;
1085 uint32_t attribute;
1086 NTSTATUS (*fn)(struct file_info *finfo,
1087 const char *mask,
1088 void *private_data);
1089 void *private_data;
1090 NTSTATUS status;
1091 bool processed_file;
1094 static void cli_list_sync_cb(struct tevent_req *subreq)
1096 struct cli_list_sync_state *state =
1097 tevent_req_callback_data_void(subreq);
1098 struct file_info *finfo;
1099 bool ok;
1101 state->status = cli_list_recv(subreq, talloc_tos(), &finfo);
1102 /* No TALLOC_FREE(subreq), we get here more than once */
1104 if (NT_STATUS_EQUAL(state->status, NT_STATUS_RETRY)) {
1106 * The lowlevel SMB call was rearmed, we'll get back
1107 * here when it's done.
1109 state->status = NT_STATUS_OK;
1110 return;
1113 if (!NT_STATUS_IS_OK(state->status)) {
1114 return;
1117 ok = dir_check_ftype(finfo->attr, state->attribute);
1118 if (!ok) {
1120 * Only process if attributes match. On SMB1 server
1121 * does this, so on SMB2 we need to emulate in the
1122 * client.
1124 * https://bugzilla.samba.org/show_bug.cgi?id=10260
1126 return;
1129 state->status = state->fn(finfo, state->mask, state->private_data);
1131 state->processed_file = true;
1133 TALLOC_FREE(finfo);
1136 NTSTATUS cli_list(struct cli_state *cli,
1137 const char *mask,
1138 uint32_t attribute,
1139 NTSTATUS (*fn)(struct file_info *finfo,
1140 const char *mask,
1141 void *private_data),
1142 void *private_data)
1144 TALLOC_CTX *frame = NULL;
1145 struct cli_list_sync_state state = {
1146 .mask = mask,
1147 .attribute = attribute,
1148 .fn = fn,
1149 .private_data = private_data,
1151 struct tevent_context *ev;
1152 struct tevent_req *req;
1153 NTSTATUS status = NT_STATUS_NO_MEMORY;
1154 uint16_t info_level;
1155 enum protocol_types proto = smbXcli_conn_protocol(cli->conn);
1157 frame = talloc_stackframe();
1159 if (smbXcli_conn_has_async_calls(cli->conn)) {
1161 * Can't use sync call while an async call is in flight
1163 status = NT_STATUS_INVALID_PARAMETER;
1164 goto fail;
1166 ev = samba_tevent_context_init(frame);
1167 if (ev == NULL) {
1168 goto fail;
1171 if (proto >= PROTOCOL_SMB2_02) {
1172 info_level = SMB2_FIND_ID_BOTH_DIRECTORY_INFO;
1173 } else {
1174 info_level = (smb1cli_conn_capabilities(cli->conn) & CAP_NT_SMBS)
1175 ? SMB_FIND_FILE_BOTH_DIRECTORY_INFO : SMB_FIND_INFO_STANDARD;
1178 req = cli_list_send(frame, ev, cli, mask, attribute, info_level);
1179 if (req == NULL) {
1180 goto fail;
1182 tevent_req_set_callback(req, cli_list_sync_cb, &state);
1184 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
1185 goto fail;
1188 status = state.status;
1190 if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_FILES)) {
1191 status = NT_STATUS_OK;
1194 if (NT_STATUS_IS_OK(status) && !state.processed_file) {
1195 status = NT_STATUS_NO_SUCH_FILE;
1198 fail:
1199 TALLOC_FREE(frame);
1200 return status;