WHATSNEW: Start release notes for Samba 3.6.14.
[Samba.git] / source3 / libsmb / clilist.c
blob43fa7d7706b3e53acec27a5710f67b904d50fc7b
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"
26 /****************************************************************************
27 Calculate a safe next_entry_offset.
28 ****************************************************************************/
30 static size_t calc_next_entry_offset(const char *base, const char *pdata_end)
32 size_t next_entry_offset = (size_t)IVAL(base,0);
34 if (next_entry_offset == 0 ||
35 base + next_entry_offset < base ||
36 base + next_entry_offset > pdata_end) {
37 next_entry_offset = pdata_end - base;
39 return next_entry_offset;
42 /****************************************************************************
43 Interpret a long filename structure - this is mostly guesses at the moment.
44 The length of the structure is returned
45 The structure of a long filename depends on the info level.
46 SMB_FIND_FILE_BOTH_DIRECTORY_INFO is used
47 by NT and SMB_FIND_EA_SIZE is used by OS/2
48 ****************************************************************************/
50 static size_t interpret_long_filename(TALLOC_CTX *ctx,
51 struct cli_state *cli,
52 int level,
53 const char *base_ptr,
54 uint16_t recv_flags2,
55 const char *p,
56 const char *pdata_end,
57 struct file_info *finfo,
58 uint32 *p_resume_key,
59 DATA_BLOB *p_last_name_raw)
61 int len;
62 size_t ret;
63 const char *base = p;
65 data_blob_free(p_last_name_raw);
67 if (p_resume_key) {
68 *p_resume_key = 0;
70 ZERO_STRUCTP(finfo);
72 switch (level) {
73 case SMB_FIND_INFO_STANDARD: /* OS/2 understands this */
74 /* these dates are converted to GMT by
75 make_unix_date */
76 if (pdata_end - base < 27) {
77 return pdata_end - base;
79 finfo->ctime_ts = convert_time_t_to_timespec(
80 make_unix_date2(p+4, cli->serverzone));
81 finfo->atime_ts = convert_time_t_to_timespec(
82 make_unix_date2(p+8, cli->serverzone));
83 finfo->mtime_ts = convert_time_t_to_timespec(
84 make_unix_date2(p+12, cli->serverzone));
85 finfo->size = IVAL(p,16);
86 finfo->mode = CVAL(p,24);
87 len = CVAL(p, 26);
88 p += 27;
89 p += align_string(base_ptr, p, 0);
91 /* We can safely use len here (which is required by OS/2)
92 * and the NAS-BASIC server instead of +2 or +1 as the
93 * STR_TERMINATE flag below is
94 * actually used as the length calculation.
95 * The len is merely an upper bound.
96 * Due to the explicit 2 byte null termination
97 * in cli_receive_trans/cli_receive_nt_trans
98 * we know this is safe. JRA + kukks
101 if (p + len > pdata_end) {
102 return pdata_end - base;
105 /* the len+2 below looks strange but it is
106 important to cope with the differences
107 between win2000 and win9x for this call
108 (tridge) */
109 ret = clistr_pull_talloc(ctx,
110 base_ptr,
111 recv_flags2,
112 &finfo->name,
114 len+2,
115 STR_TERMINATE);
116 if (ret == (size_t)-1) {
117 return pdata_end - base;
119 p += ret;
120 return PTR_DIFF(p, base);
122 case SMB_FIND_EA_SIZE: /* this is what OS/2 uses mostly */
123 /* these dates are converted to GMT by
124 make_unix_date */
125 if (pdata_end - base < 31) {
126 return pdata_end - base;
128 finfo->ctime_ts = convert_time_t_to_timespec(
129 make_unix_date2(p+4, cli->serverzone));
130 finfo->atime_ts = convert_time_t_to_timespec(
131 make_unix_date2(p+8, cli->serverzone));
132 finfo->mtime_ts = convert_time_t_to_timespec(
133 make_unix_date2(p+12, cli->serverzone));
134 finfo->size = IVAL(p,16);
135 finfo->mode = CVAL(p,24);
136 len = CVAL(p, 30);
137 p += 31;
138 /* check for unisys! */
139 if (p + len + 1 > pdata_end) {
140 return pdata_end - base;
142 ret = clistr_pull_talloc(ctx,
143 base_ptr,
144 recv_flags2,
145 &finfo->name,
147 len,
148 STR_NOALIGN);
149 if (ret == (size_t)-1) {
150 return pdata_end - base;
152 p += ret;
153 return PTR_DIFF(p, base) + 1;
155 case SMB_FIND_FILE_BOTH_DIRECTORY_INFO: /* NT uses this, but also accepts 2 */
157 size_t namelen, slen;
159 if (pdata_end - base < 94) {
160 return pdata_end - base;
163 p += 4; /* next entry offset */
165 if (p_resume_key) {
166 *p_resume_key = IVAL(p,0);
168 p += 4; /* fileindex */
170 /* Offset zero is "create time", not "change time". */
171 p += 8;
172 finfo->atime_ts = interpret_long_date(p);
173 p += 8;
174 finfo->mtime_ts = interpret_long_date(p);
175 p += 8;
176 finfo->ctime_ts = interpret_long_date(p);
177 p += 8;
178 finfo->size = IVAL2_TO_SMB_BIG_UINT(p,0);
179 p += 8;
180 p += 8; /* alloc size */
181 finfo->mode = CVAL(p,0);
182 p += 4;
183 namelen = IVAL(p,0);
184 p += 4;
185 p += 4; /* EA size */
186 slen = SVAL(p, 0);
187 if (slen > 24) {
188 /* Bad short name length. */
189 return pdata_end - base;
191 p += 2;
193 /* stupid NT bugs. grr */
194 int flags = 0;
195 if (p[1] == 0 && namelen > 1) flags |= STR_UNICODE;
196 clistr_pull(base_ptr, finfo->short_name, p,
197 sizeof(finfo->short_name),
198 slen, flags);
200 p += 24; /* short name? */
201 if (p + namelen < p || p + namelen > pdata_end) {
202 return pdata_end - base;
204 ret = clistr_pull_talloc(ctx,
205 base_ptr,
206 recv_flags2,
207 &finfo->name,
209 namelen,
211 if (ret == (size_t)-1) {
212 return pdata_end - base;
215 /* To be robust in the face of unicode conversion failures
216 we need to copy the raw bytes of the last name seen here.
217 Namelen doesn't include the terminating unicode null, so
218 copy it here. */
220 if (p_last_name_raw) {
221 *p_last_name_raw = data_blob(NULL, namelen+2);
222 memcpy(p_last_name_raw->data, p, namelen);
223 SSVAL(p_last_name_raw->data, namelen, 0);
225 return calc_next_entry_offset(base, pdata_end);
229 DEBUG(1,("Unknown long filename format %d\n",level));
230 return calc_next_entry_offset(base, pdata_end);
233 /****************************************************************************
234 Interpret a short filename structure.
235 The length of the structure is returned.
236 ****************************************************************************/
238 static bool interpret_short_filename(TALLOC_CTX *ctx,
239 struct cli_state *cli,
240 char *p,
241 struct file_info *finfo)
243 size_t ret;
244 ZERO_STRUCTP(finfo);
246 finfo->mode = CVAL(p,21);
248 /* this date is converted to GMT by make_unix_date */
249 finfo->ctime_ts.tv_sec = make_unix_date(p+22, cli->serverzone);
250 finfo->ctime_ts.tv_nsec = 0;
251 finfo->mtime_ts.tv_sec = finfo->atime_ts.tv_sec = finfo->ctime_ts.tv_sec;
252 finfo->mtime_ts.tv_nsec = finfo->atime_ts.tv_nsec = 0;
253 finfo->size = IVAL(p,26);
254 ret = clistr_pull_talloc(ctx,
255 cli->inbuf,
256 SVAL(cli->inbuf, smb_flg2),
257 &finfo->name,
258 p+30,
260 STR_ASCII);
261 if (ret == (size_t)-1) {
262 return false;
265 if (finfo->name) {
266 strlcpy(finfo->short_name,
267 finfo->name,
268 sizeof(finfo->short_name));
270 return true;
273 struct cli_list_old_state {
274 struct tevent_context *ev;
275 struct cli_state *cli;
276 uint16_t vwv[2];
277 char *mask;
278 int num_asked;
279 uint16_t attribute;
280 uint8_t search_status[23];
281 bool first;
282 bool done;
283 uint8_t *dirlist;
286 static void cli_list_old_done(struct tevent_req *subreq);
288 static struct tevent_req *cli_list_old_send(TALLOC_CTX *mem_ctx,
289 struct tevent_context *ev,
290 struct cli_state *cli,
291 const char *mask,
292 uint16_t attribute)
294 struct tevent_req *req, *subreq;
295 struct cli_list_old_state *state;
296 uint8_t *bytes;
297 static const uint16_t zero = 0;
299 req = tevent_req_create(mem_ctx, &state, struct cli_list_old_state);
300 if (req == NULL) {
301 return NULL;
303 state->ev = ev;
304 state->cli = cli;
305 state->attribute = attribute;
306 state->first = true;
307 state->mask = talloc_strdup(state, mask);
308 if (tevent_req_nomem(state->mask, req)) {
309 return tevent_req_post(req, ev);
311 state->num_asked = (cli->max_xmit - 100) / DIR_STRUCT_SIZE;
313 SSVAL(state->vwv + 0, 0, state->num_asked);
314 SSVAL(state->vwv + 1, 0, state->attribute);
316 bytes = talloc_array(state, uint8_t, 1);
317 if (tevent_req_nomem(bytes, req)) {
318 return tevent_req_post(req, ev);
320 bytes[0] = 4;
321 bytes = smb_bytes_push_str(bytes, cli_ucs2(cli), mask,
322 strlen(mask)+1, NULL);
324 bytes = smb_bytes_push_bytes(bytes, 5, (uint8_t *)&zero, 2);
325 if (tevent_req_nomem(bytes, req)) {
326 return tevent_req_post(req, ev);
329 subreq = cli_smb_send(state, state->ev, state->cli, SMBsearch,
330 0, 2, state->vwv, talloc_get_size(bytes), bytes);
331 if (tevent_req_nomem(subreq, req)) {
332 return tevent_req_post(req, ev);
334 tevent_req_set_callback(subreq, cli_list_old_done, req);
335 return req;
338 static void cli_list_old_done(struct tevent_req *subreq)
340 struct tevent_req *req = tevent_req_callback_data(
341 subreq, struct tevent_req);
342 struct cli_list_old_state *state = tevent_req_data(
343 req, struct cli_list_old_state);
344 NTSTATUS status;
345 uint8_t cmd;
346 uint8_t wct;
347 uint16_t *vwv;
348 uint32_t num_bytes;
349 uint8_t *bytes;
350 uint16_t received;
351 size_t dirlist_len;
352 uint8_t *tmp;
354 status = cli_smb_recv(subreq, state, NULL, 0, &wct, &vwv, &num_bytes,
355 &bytes);
356 if (!NT_STATUS_IS_OK(status)
357 && !NT_STATUS_EQUAL(status, NT_STATUS_DOS(ERRDOS, ERRnofiles))
358 && !NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) {
359 TALLOC_FREE(subreq);
360 tevent_req_nterror(req, status);
361 return;
363 if (NT_STATUS_EQUAL(status, NT_STATUS_DOS(ERRDOS, ERRnofiles))
364 || NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) {
365 received = 0;
366 } else {
367 if (wct < 1) {
368 TALLOC_FREE(subreq);
369 tevent_req_nterror(
370 req, NT_STATUS_INVALID_NETWORK_RESPONSE);
371 return;
373 received = SVAL(vwv + 0, 0);
376 if (received > 0) {
378 * I don't think this can wrap. received is
379 * initialized from a 16-bit value.
381 if (num_bytes < (received * DIR_STRUCT_SIZE + 3)) {
382 TALLOC_FREE(subreq);
383 tevent_req_nterror(
384 req, NT_STATUS_INVALID_NETWORK_RESPONSE);
385 return;
388 dirlist_len = talloc_get_size(state->dirlist);
390 tmp = TALLOC_REALLOC_ARRAY(
391 state, state->dirlist, uint8_t,
392 dirlist_len + received * DIR_STRUCT_SIZE);
393 if (tevent_req_nomem(tmp, req)) {
394 return;
396 state->dirlist = tmp;
397 memcpy(state->dirlist + dirlist_len, bytes + 3,
398 received * DIR_STRUCT_SIZE);
400 SSVAL(state->search_status, 0, 21);
401 memcpy(state->search_status + 2,
402 bytes + 3 + (received-1)*DIR_STRUCT_SIZE, 21);
403 cmd = SMBsearch;
404 } else {
405 if (state->first || state->done) {
406 tevent_req_done(req);
407 return;
409 state->done = true;
410 state->num_asked = 0;
411 cmd = SMBfclose;
413 TALLOC_FREE(subreq);
415 state->first = false;
417 SSVAL(state->vwv + 0, 0, state->num_asked);
418 SSVAL(state->vwv + 1, 0, state->attribute);
420 bytes = talloc_array(state, uint8_t, 1);
421 if (tevent_req_nomem(bytes, req)) {
422 return;
424 bytes[0] = 4;
425 bytes = smb_bytes_push_str(bytes, cli_ucs2(state->cli), "",
426 1, NULL);
427 bytes = smb_bytes_push_bytes(bytes, 5, state->search_status,
428 sizeof(state->search_status));
429 if (tevent_req_nomem(bytes, req)) {
430 return;
432 subreq = cli_smb_send(state, state->ev, state->cli, cmd, 0,
433 2, state->vwv, talloc_get_size(bytes), bytes);
434 if (tevent_req_nomem(subreq, req)) {
435 return;
437 tevent_req_set_callback(subreq, cli_list_old_done, req);
440 static NTSTATUS cli_list_old_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
441 struct file_info **pfinfo)
443 struct cli_list_old_state *state = tevent_req_data(
444 req, struct cli_list_old_state);
445 NTSTATUS status;
446 size_t i, num_received;
447 struct file_info *finfo;
449 if (tevent_req_is_nterror(req, &status)) {
450 return status;
453 num_received = talloc_array_length(state->dirlist) / DIR_STRUCT_SIZE;
455 finfo = TALLOC_ARRAY(mem_ctx, struct file_info, num_received);
456 if (finfo == NULL) {
457 return NT_STATUS_NO_MEMORY;
460 for (i=0; i<num_received; i++) {
461 if (!interpret_short_filename(
462 finfo, state->cli,
463 (char *)state->dirlist + i * DIR_STRUCT_SIZE,
464 &finfo[i])) {
465 TALLOC_FREE(finfo);
466 return NT_STATUS_NO_MEMORY;
469 *pfinfo = finfo;
470 return NT_STATUS_OK;
473 NTSTATUS cli_list_old(struct cli_state *cli, const char *mask,
474 uint16 attribute,
475 NTSTATUS (*fn)(const char *, struct file_info *,
476 const char *, void *), void *state)
478 TALLOC_CTX *frame = talloc_stackframe();
479 struct event_context *ev;
480 struct tevent_req *req;
481 NTSTATUS status = NT_STATUS_NO_MEMORY;
482 struct file_info *finfo;
483 size_t i, num_finfo;
485 if (cli_has_async_calls(cli)) {
487 * Can't use sync call while an async call is in flight
489 status = NT_STATUS_INVALID_PARAMETER;
490 goto fail;
492 ev = event_context_init(frame);
493 if (ev == NULL) {
494 goto fail;
496 req = cli_list_old_send(frame, ev, cli, mask, attribute);
497 if (req == NULL) {
498 goto fail;
500 if (!tevent_req_poll(req, ev)) {
501 status = map_nt_error_from_unix(errno);
502 goto fail;
504 status = cli_list_old_recv(req, frame, &finfo);
505 if (!NT_STATUS_IS_OK(status)) {
506 goto fail;
508 num_finfo = talloc_array_length(finfo);
509 for (i=0; i<num_finfo; i++) {
510 status = fn(cli->dfs_mountpoint, &finfo[i], mask, state);
511 if (!NT_STATUS_IS_OK(status)) {
512 goto fail;
515 fail:
516 TALLOC_FREE(frame);
517 return status;
520 struct cli_list_trans_state {
521 struct tevent_context *ev;
522 struct cli_state *cli;
523 char *mask;
524 uint16_t attribute;
525 uint16_t info_level;
527 int loop_count;
528 int total_received;
529 uint16_t max_matches;
530 bool first;
532 int ff_eos;
533 int ff_dir_handle;
535 uint16_t setup[1];
536 uint8_t *param;
538 struct file_info *finfo;
541 static void cli_list_trans_done(struct tevent_req *subreq);
543 static struct tevent_req *cli_list_trans_send(TALLOC_CTX *mem_ctx,
544 struct tevent_context *ev,
545 struct cli_state *cli,
546 const char *mask,
547 uint16_t attribute,
548 uint16_t info_level)
550 struct tevent_req *req, *subreq;
551 struct cli_list_trans_state *state;
552 size_t nlen, param_len;
553 char *p;
555 req = tevent_req_create(mem_ctx, &state,
556 struct cli_list_trans_state);
557 if (req == NULL) {
558 return NULL;
560 state->ev = ev;
561 state->cli = cli;
562 state->mask = talloc_strdup(state, mask);
563 if (tevent_req_nomem(state->mask, req)) {
564 return tevent_req_post(req, ev);
566 state->attribute = attribute;
567 state->info_level = info_level;
568 state->loop_count = 0;
569 state->first = true;
571 state->max_matches = 1366; /* Match W2k */
573 SSVAL(&state->setup[0], 0, TRANSACT2_FINDFIRST);
575 nlen = 2*(strlen(mask)+1);
576 state->param = TALLOC_ARRAY(state, uint8_t, 12+nlen+2);
577 if (tevent_req_nomem(state->param, req)) {
578 return tevent_req_post(req, ev);
581 SSVAL(state->param, 0, state->attribute);
582 SSVAL(state->param, 2, state->max_matches);
583 SSVAL(state->param, 4,
584 FLAG_TRANS2_FIND_REQUIRE_RESUME
585 |FLAG_TRANS2_FIND_CLOSE_IF_END);
586 SSVAL(state->param, 6, state->info_level);
587 SIVAL(state->param, 8, 0);
589 p = ((char *)state->param)+12;
590 p += clistr_push(state->cli, p, state->mask, nlen,
591 STR_TERMINATE);
592 param_len = PTR_DIFF(p, state->param);
594 subreq = cli_trans_send(state, state->ev, state->cli,
595 SMBtrans2, NULL, -1, 0, 0,
596 state->setup, 1, 0,
597 state->param, param_len, 10,
598 NULL, 0, cli->max_xmit);
599 if (tevent_req_nomem(subreq, req)) {
600 return tevent_req_post(req, ev);
602 tevent_req_set_callback(subreq, cli_list_trans_done, req);
603 return req;
606 static void cli_list_trans_done(struct tevent_req *subreq)
608 struct tevent_req *req = tevent_req_callback_data(
609 subreq, struct tevent_req);
610 struct cli_list_trans_state *state = tevent_req_data(
611 req, struct cli_list_trans_state);
612 NTSTATUS status;
613 uint8_t *param;
614 uint32_t num_param;
615 uint8_t *data;
616 char *data_end;
617 uint32_t num_data;
618 uint32_t min_param;
619 struct file_info *tmp;
620 size_t old_num_finfo;
621 uint16_t recv_flags2;
622 int ff_searchcount;
623 bool ff_eos;
624 char *p, *p2;
625 uint32_t resume_key = 0;
626 int i;
627 DATA_BLOB last_name_raw;
628 struct file_info *finfo = NULL;
629 size_t nlen, param_len;
631 min_param = (state->first ? 6 : 4);
633 status = cli_trans_recv(subreq, talloc_tos(), &recv_flags2,
634 NULL, 0, NULL,
635 &param, min_param, &num_param,
636 &data, 0, &num_data);
637 TALLOC_FREE(subreq);
638 if (!NT_STATUS_IS_OK(status)) {
640 * TODO: retry, OS/2 nofiles
642 tevent_req_nterror(req, status);
643 return;
646 if (state->first) {
647 state->ff_dir_handle = SVAL(param, 0);
648 ff_searchcount = SVAL(param, 2);
649 ff_eos = SVAL(param, 4) != 0;
650 } else {
651 ff_searchcount = SVAL(param, 0);
652 ff_eos = SVAL(param, 2) != 0;
655 old_num_finfo = talloc_array_length(state->finfo);
657 tmp = TALLOC_REALLOC_ARRAY(state, state->finfo, struct file_info,
658 old_num_finfo + ff_searchcount);
659 if (tevent_req_nomem(tmp, req)) {
660 return;
662 state->finfo = tmp;
664 p2 = p = (char *)data;
665 data_end = (char *)data + num_data;
666 last_name_raw = data_blob_null;
668 for (i=0; i<ff_searchcount; i++) {
669 if (p2 >= data_end) {
670 ff_eos = true;
671 break;
673 if ((state->info_level == SMB_FIND_FILE_BOTH_DIRECTORY_INFO)
674 && (i == ff_searchcount-1)) {
675 /* Last entry - fixup the last offset length. */
676 SIVAL(p2, 0, PTR_DIFF((data + num_data), p2));
679 data_blob_free(&last_name_raw);
681 finfo = &state->finfo[old_num_finfo + i];
683 p2 += interpret_long_filename(
684 state->finfo, /* Stick fname to the array as such */
685 state->cli, state->info_level,
686 (char *)data, recv_flags2, p2,
687 data_end, finfo, &resume_key, &last_name_raw);
689 if (finfo->name == NULL) {
690 DEBUG(1, ("cli_list: Error: unable to parse name from "
691 "info level %d\n", state->info_level));
692 ff_eos = true;
693 break;
695 if (!state->first && (state->mask[0] != '\0') &&
696 strcsequal(finfo->name, state->mask)) {
697 DEBUG(1, ("Error: Looping in FIND_NEXT as name %s has "
698 "already been seen?\n", finfo->name));
699 ff_eos = true;
700 break;
704 if (ff_searchcount == 0) {
705 ff_eos = true;
708 TALLOC_FREE(param);
709 TALLOC_FREE(data);
712 * Shrink state->finfo to the real length we received
714 tmp = TALLOC_REALLOC_ARRAY(state, state->finfo, struct file_info,
715 old_num_finfo + i);
716 if (tevent_req_nomem(tmp, req)) {
717 return;
719 state->finfo = tmp;
721 state->first = false;
723 if (ff_eos) {
724 data_blob_free(&last_name_raw);
725 tevent_req_done(req);
726 return;
729 TALLOC_FREE(state->mask);
730 state->mask = talloc_strdup(state, finfo->name);
731 if (tevent_req_nomem(state->mask, req)) {
732 return;
735 SSVAL(&state->setup[0], 0, TRANSACT2_FINDNEXT);
737 nlen = 2*(strlen(state->mask) + 1);
739 param = TALLOC_REALLOC_ARRAY(state, state->param, uint8_t,
740 12 + nlen + last_name_raw.length + 2);
741 if (tevent_req_nomem(param, req)) {
742 return;
744 state->param = param;
746 SSVAL(param, 0, state->ff_dir_handle);
747 SSVAL(param, 2, state->max_matches); /* max count */
748 SSVAL(param, 4, state->info_level);
750 * For W2K servers serving out FAT filesystems we *must* set
751 * the resume key. If it's not FAT then it's returned as zero.
753 SIVAL(param, 6, resume_key); /* ff_resume_key */
755 * NB. *DON'T* use continue here. If you do it seems that W2K
756 * and bretheren can miss filenames. Use last filename
757 * continue instead. JRA
759 SSVAL(param, 10, (FLAG_TRANS2_FIND_REQUIRE_RESUME
760 |FLAG_TRANS2_FIND_CLOSE_IF_END));
761 p = ((char *)param)+12;
762 if (last_name_raw.length) {
763 memcpy(p, last_name_raw.data, last_name_raw.length);
764 p += last_name_raw.length;
765 data_blob_free(&last_name_raw);
766 } else {
767 p += clistr_push(state->cli, p, state->mask, nlen,
768 STR_TERMINATE);
771 param_len = PTR_DIFF(p, param);
773 subreq = cli_trans_send(state, state->ev, state->cli,
774 SMBtrans2, NULL, -1, 0, 0,
775 state->setup, 1, 0,
776 state->param, param_len, 10,
777 NULL, 0, state->cli->max_xmit);
778 if (tevent_req_nomem(subreq, req)) {
779 return;
781 tevent_req_set_callback(subreq, cli_list_trans_done, req);
784 static NTSTATUS cli_list_trans_recv(struct tevent_req *req,
785 TALLOC_CTX *mem_ctx,
786 struct file_info **finfo)
788 struct cli_list_trans_state *state = tevent_req_data(
789 req, struct cli_list_trans_state);
790 NTSTATUS status;
792 if (tevent_req_is_nterror(req, &status)) {
793 return status;
795 *finfo = talloc_move(mem_ctx, &state->finfo);
796 return NT_STATUS_OK;
799 NTSTATUS cli_list_trans(struct cli_state *cli, const char *mask,
800 uint16_t attribute, int info_level,
801 NTSTATUS (*fn)(const char *mnt, struct file_info *finfo,
802 const char *mask, void *private_data),
803 void *private_data)
805 TALLOC_CTX *frame = talloc_stackframe();
806 struct event_context *ev;
807 struct tevent_req *req;
808 int i, num_finfo;
809 struct file_info *finfo = NULL;
810 NTSTATUS status = NT_STATUS_NO_MEMORY;
812 if (cli_has_async_calls(cli)) {
814 * Can't use sync call while an async call is in flight
816 status = NT_STATUS_INVALID_PARAMETER;
817 goto fail;
819 ev = event_context_init(frame);
820 if (ev == NULL) {
821 goto fail;
823 req = cli_list_trans_send(frame, ev, cli, mask, attribute, info_level);
824 if (req == NULL) {
825 goto fail;
827 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
828 goto fail;
830 status = cli_list_trans_recv(req, frame, &finfo);
831 if (!NT_STATUS_IS_OK(status)) {
832 goto fail;
834 num_finfo = talloc_array_length(finfo);
835 for (i=0; i<num_finfo; i++) {
836 status = fn(cli->dfs_mountpoint, &finfo[i], mask, private_data);
837 if (!NT_STATUS_IS_OK(status)) {
838 goto fail;
841 fail:
842 TALLOC_FREE(frame);
843 return status;
846 struct cli_list_state {
847 NTSTATUS (*recv_fn)(struct tevent_req *req, TALLOC_CTX *mem_ctx,
848 struct file_info **finfo);
849 struct file_info *finfo;
852 static void cli_list_done(struct tevent_req *subreq);
854 struct tevent_req *cli_list_send(TALLOC_CTX *mem_ctx,
855 struct tevent_context *ev,
856 struct cli_state *cli,
857 const char *mask,
858 uint16_t attribute,
859 uint16_t info_level)
861 struct tevent_req *req, *subreq;
862 struct cli_list_state *state;
864 req = tevent_req_create(mem_ctx, &state, struct cli_list_state);
865 if (req == NULL) {
866 return NULL;
869 if (cli->protocol <= PROTOCOL_LANMAN1) {
870 subreq = cli_list_old_send(state, ev, cli, mask, attribute);
871 state->recv_fn = cli_list_old_recv;
872 } else {
873 subreq = cli_list_trans_send(state, ev, cli, mask, attribute,
874 info_level);
875 state->recv_fn = cli_list_trans_recv;
877 if (tevent_req_nomem(subreq, req)) {
878 return tevent_req_post(req, ev);
880 tevent_req_set_callback(subreq, cli_list_done, req);
881 return req;
884 static void cli_list_done(struct tevent_req *subreq)
886 struct tevent_req *req = tevent_req_callback_data(
887 subreq, struct tevent_req);
888 struct cli_list_state *state = tevent_req_data(
889 req, struct cli_list_state);
890 NTSTATUS status;
892 status = state->recv_fn(subreq, state, &state->finfo);
893 TALLOC_FREE(subreq);
894 if (!NT_STATUS_IS_OK(status)) {
895 tevent_req_nterror(req, status);
896 return;
898 tevent_req_done(req);
901 NTSTATUS cli_list_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
902 struct file_info **finfo, size_t *num_finfo)
904 struct cli_list_state *state = tevent_req_data(
905 req, struct cli_list_state);
906 NTSTATUS status;
908 if (tevent_req_is_nterror(req, &status)) {
909 return status;
911 *num_finfo = talloc_array_length(state->finfo);
912 *finfo = talloc_move(mem_ctx, &state->finfo);
913 return NT_STATUS_OK;
916 NTSTATUS cli_list(struct cli_state *cli, const char *mask, uint16 attribute,
917 NTSTATUS (*fn)(const char *, struct file_info *, const char *,
918 void *), void *state)
920 TALLOC_CTX *frame = talloc_stackframe();
921 struct event_context *ev;
922 struct tevent_req *req;
923 NTSTATUS status = NT_STATUS_NO_MEMORY;
924 struct file_info *finfo;
925 size_t i, num_finfo;
926 uint16_t info_level;
928 if (cli_has_async_calls(cli)) {
930 * Can't use sync call while an async call is in flight
932 status = NT_STATUS_INVALID_PARAMETER;
933 goto fail;
935 ev = event_context_init(frame);
936 if (ev == NULL) {
937 goto fail;
940 info_level = (cli->capabilities & CAP_NT_SMBS)
941 ? SMB_FIND_FILE_BOTH_DIRECTORY_INFO : SMB_FIND_INFO_STANDARD;
943 req = cli_list_send(frame, ev, cli, mask, attribute, info_level);
944 if (req == NULL) {
945 goto fail;
947 if (!tevent_req_poll(req, ev)) {
948 status = map_nt_error_from_unix(errno);
949 goto fail;
952 status = cli_list_recv(req, frame, &finfo, &num_finfo);
953 if (!NT_STATUS_IS_OK(status)) {
954 goto fail;
957 for (i=0; i<num_finfo; i++) {
958 status = fn(cli->dfs_mountpoint, &finfo[i], mask, state);
959 if (!NT_STATUS_IS_OK(status)) {
960 goto fail;
963 fail:
964 TALLOC_FREE(frame);
965 return status;