s3-libsmb: move protos to libsmb/proto.h
[ira/wip.git] / source3 / libsmb / clilist.c
1 /*
2    Unix SMB/CIFS implementation.
3    client directory list routines
4    Copyright (C) Andrew Tridgell 1994-1998
5
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.
10
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.
15
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/>.
18 */
19
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
26 /****************************************************************************
27  Calculate a safe next_entry_offset.
28 ****************************************************************************/
29
30 static size_t calc_next_entry_offset(const char *base, const char *pdata_end)
31 {
32         size_t next_entry_offset = (size_t)IVAL(base,0);
33
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;
38         }
39         return next_entry_offset;
40 }
41
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 ****************************************************************************/
49
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)
60 {
61         int len;
62         size_t ret;
63         const char *base = p;
64
65         data_blob_free(p_last_name_raw);
66
67         if (p_resume_key) {
68                 *p_resume_key = 0;
69         }
70         ZERO_STRUCTP(finfo);
71
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;
78                         }
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);
90
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
99                          */
100
101                         if (p + len > pdata_end) {
102                                 return pdata_end - base;
103                         }
104
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,
113                                                 p,
114                                                 len+2,
115                                                 STR_TERMINATE);
116                         if (ret == (size_t)-1) {
117                                 return pdata_end - base;
118                         }
119                         p += ret;
120                         return PTR_DIFF(p, base);
121
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;
127                         }
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;
141                         }
142                         ret = clistr_pull_talloc(ctx,
143                                                 base_ptr,
144                                                 recv_flags2,
145                                                 &finfo->name,
146                                                 p,
147                                                 len,
148                                                 STR_NOALIGN);
149                         if (ret == (size_t)-1) {
150                                 return pdata_end - base;
151                         }
152                         p += ret;
153                         return PTR_DIFF(p, base) + 1;
154
155                 case SMB_FIND_FILE_BOTH_DIRECTORY_INFO: /* NT uses this, but also accepts 2 */
156                 {
157                         size_t namelen, slen;
158
159                         if (pdata_end - base < 94) {
160                                 return pdata_end - base;
161                         }
162
163                         p += 4; /* next entry offset */
164
165                         if (p_resume_key) {
166                                 *p_resume_key = IVAL(p,0);
167                         }
168                         p += 4; /* fileindex */
169
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;
190                         }
191                         p += 2;
192                         {
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);
199                         }
200                         p += 24; /* short name? */
201                         if (p + namelen < p || p + namelen > pdata_end) {
202                                 return pdata_end - base;
203                         }
204                         ret = clistr_pull_talloc(ctx,
205                                                 base_ptr,
206                                                 recv_flags2,
207                                                 &finfo->name,
208                                                 p,
209                                                 namelen,
210                                                 0);
211                         if (ret == (size_t)-1) {
212                                 return pdata_end - base;
213                         }
214
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. */
219
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);
224                         }
225                         return calc_next_entry_offset(base, pdata_end);
226                 }
227         }
228
229         DEBUG(1,("Unknown long filename format %d\n",level));
230         return calc_next_entry_offset(base, pdata_end);
231 }
232
233 /****************************************************************************
234  Interpret a short filename structure.
235  The length of the structure is returned.
236 ****************************************************************************/
237
238 static bool interpret_short_filename(TALLOC_CTX *ctx,
239                                 struct cli_state *cli,
240                                 char *p,
241                                 struct file_info *finfo)
242 {
243         size_t ret;
244         ZERO_STRUCTP(finfo);
245
246         finfo->mode = CVAL(p,21);
247
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,
259                         12,
260                         STR_ASCII);
261         if (ret == (size_t)-1) {
262                 return false;
263         }
264
265         if (finfo->name) {
266                 strlcpy(finfo->short_name,
267                         finfo->name,
268                         sizeof(finfo->short_name));
269         }
270         return true;
271 }
272
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;
284 };
285
286 static void cli_list_old_done(struct tevent_req *subreq);
287
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)
293 {
294         struct tevent_req *req, *subreq;
295         struct cli_list_old_state *state;
296         uint8_t *bytes;
297         static const uint16_t zero = 0;
298
299         req = tevent_req_create(mem_ctx, &state, struct cli_list_old_state);
300         if (req == NULL) {
301                 return NULL;
302         }
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);
310         }
311         state->num_asked = (cli->max_xmit - 100) / DIR_STRUCT_SIZE;
312
313         SSVAL(state->vwv + 0, 0, state->num_asked);
314         SSVAL(state->vwv + 1, 0, state->attribute);
315
316         bytes = talloc_array(state, uint8_t, 1);
317         if (tevent_req_nomem(bytes, req)) {
318                 return tevent_req_post(req, ev);
319         }
320         bytes[0] = 4;
321         bytes = smb_bytes_push_str(bytes, cli_ucs2(cli), mask,
322                                    strlen(mask)+1, NULL);
323
324         bytes = smb_bytes_push_bytes(bytes, 5, (const uint8_t *)&zero, 2);
325         if (tevent_req_nomem(bytes, req)) {
326                 return tevent_req_post(req, ev);
327         }
328
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);
333         }
334         tevent_req_set_callback(subreq, cli_list_old_done, req);
335         return req;
336 }
337
338 static void cli_list_old_done(struct tevent_req *subreq)
339 {
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;
353
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;
362         }
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;
372                 }
373                 received = SVAL(vwv + 0, 0);
374         }
375
376         if (received > 0) {
377                 /*
378                  * I don't think this can wrap. received is
379                  * initialized from a 16-bit value.
380                  */
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;
386                 }
387
388                 dirlist_len = talloc_get_size(state->dirlist);
389
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;
395                 }
396                 state->dirlist = tmp;
397                 memcpy(state->dirlist + dirlist_len, bytes + 3,
398                        received * DIR_STRUCT_SIZE);
399
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;
408                 }
409                 state->done = true;
410                 state->num_asked = 0;
411                 cmd = SMBfclose;
412         }
413         TALLOC_FREE(subreq);
414
415         state->first = false;
416
417         SSVAL(state->vwv + 0, 0, state->num_asked);
418         SSVAL(state->vwv + 1, 0, state->attribute);
419
420         bytes = talloc_array(state, uint8_t, 1);
421         if (tevent_req_nomem(bytes, req)) {
422                 return;
423         }
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;
431         }
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;
436         }
437         tevent_req_set_callback(subreq, cli_list_old_done, req);
438 }
439
440 static NTSTATUS cli_list_old_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
441                                   struct file_info **pfinfo)
442 {
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;
448
449         if (tevent_req_is_nterror(req, &status)) {
450                 return status;
451         }
452
453         num_received = talloc_array_length(state->dirlist) / DIR_STRUCT_SIZE;
454
455         finfo = TALLOC_ARRAY(mem_ctx, struct file_info, num_received);
456         if (finfo == NULL) {
457                 return NT_STATUS_NO_MEMORY;
458         }
459
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;
467                 }
468         }
469         *pfinfo = finfo;
470         return NT_STATUS_OK;
471 }
472
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)
477 {
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;
484
485         if (cli_has_async_calls(cli)) {
486                 /*
487                  * Can't use sync call while an async call is in flight
488                  */
489                 status = NT_STATUS_INVALID_PARAMETER;
490                 goto fail;
491         }
492         ev = event_context_init(frame);
493         if (ev == NULL) {
494                 goto fail;
495         }
496         req = cli_list_old_send(frame, ev, cli, mask, attribute);
497         if (req == NULL) {
498                 goto fail;
499         }
500         if (!tevent_req_poll(req, ev)) {
501                 status = map_nt_error_from_unix(errno);
502                 goto fail;
503         }
504         status = cli_list_old_recv(req, frame, &finfo);
505         if (!NT_STATUS_IS_OK(status)) {
506                 goto fail;
507         }
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;
513                 }
514         }
515  fail:
516         TALLOC_FREE(frame);
517         if (!NT_STATUS_IS_OK(status)) {
518                 cli_set_error(cli, status);
519         }
520         return status;
521 }
522
523 struct cli_list_trans_state {
524         struct tevent_context *ev;
525         struct cli_state *cli;
526         char *mask;
527         uint16_t attribute;
528         uint16_t info_level;
529
530         int loop_count;
531         int total_received;
532         uint16_t max_matches;
533         bool first;
534
535         int ff_eos;
536         int ff_dir_handle;
537
538         uint16_t setup[1];
539         uint8_t *param;
540
541         struct file_info *finfo;
542 };
543
544 static void cli_list_trans_done(struct tevent_req *subreq);
545
546 static struct tevent_req *cli_list_trans_send(TALLOC_CTX *mem_ctx,
547                                               struct tevent_context *ev,
548                                               struct cli_state *cli,
549                                               const char *mask,
550                                               uint16_t attribute,
551                                               uint16_t info_level)
552 {
553         struct tevent_req *req, *subreq;
554         struct cli_list_trans_state *state;
555         size_t nlen, param_len;
556         char *p;
557
558         req = tevent_req_create(mem_ctx, &state,
559                                 struct cli_list_trans_state);
560         if (req == NULL) {
561                 return NULL;
562         }
563         state->ev = ev;
564         state->cli = cli;
565         state->mask = talloc_strdup(state, mask);
566         if (tevent_req_nomem(state->mask, req)) {
567                 return tevent_req_post(req, ev);
568         }
569         state->attribute = attribute;
570         state->info_level = info_level;
571         state->loop_count = 0;
572         state->first = true;
573
574         state->max_matches = 1366; /* Match W2k */
575
576         state->setup[0] = TRANSACT2_FINDFIRST;
577
578         nlen = 2*(strlen(mask)+1);
579         state->param = TALLOC_ARRAY(state, uint8_t, 12+nlen+2);
580         if (tevent_req_nomem(state->param, req)) {
581                 return tevent_req_post(req, ev);
582         }
583
584         SSVAL(state->param, 0, state->attribute);
585         SSVAL(state->param, 2, state->max_matches);
586         SSVAL(state->param, 4,
587               FLAG_TRANS2_FIND_REQUIRE_RESUME
588               |FLAG_TRANS2_FIND_CLOSE_IF_END);
589         SSVAL(state->param, 6, state->info_level);
590         SIVAL(state->param, 8, 0);
591
592         p = ((char *)state->param)+12;
593         p += clistr_push(state->cli, p, state->mask, nlen,
594                          STR_TERMINATE);
595         param_len = PTR_DIFF(p, state->param);
596
597         subreq = cli_trans_send(state, state->ev, state->cli,
598                                 SMBtrans2, NULL, -1, 0, 0,
599                                 state->setup, 1, 0,
600                                 state->param, param_len, 10,
601                                 NULL, 0, cli->max_xmit);
602         if (tevent_req_nomem(subreq, req)) {
603                 return tevent_req_post(req, ev);
604         }
605         tevent_req_set_callback(subreq, cli_list_trans_done, req);
606         return req;
607 }
608
609 static void cli_list_trans_done(struct tevent_req *subreq)
610 {
611         struct tevent_req *req = tevent_req_callback_data(
612                 subreq, struct tevent_req);
613         struct cli_list_trans_state *state = tevent_req_data(
614                 req, struct cli_list_trans_state);
615         NTSTATUS status;
616         uint8_t *param;
617         uint32_t num_param;
618         uint8_t *data;
619         char *data_end;
620         uint32_t num_data;
621         uint32_t min_param;
622         struct file_info *tmp;
623         size_t old_num_finfo;
624         uint16_t recv_flags2;
625         int ff_searchcount;
626         bool ff_eos;
627         char *p, *p2;
628         uint32_t resume_key = 0;
629         int i;
630         DATA_BLOB last_name_raw;
631         struct file_info *finfo = NULL;
632         size_t nlen, param_len;
633
634         min_param = (state->first ? 6 : 4);
635
636         status = cli_trans_recv(subreq, talloc_tos(), &recv_flags2,
637                                 NULL, 0, NULL,
638                                 &param, min_param, &num_param,
639                                 &data, 0, &num_data);
640         TALLOC_FREE(subreq);
641         if (!NT_STATUS_IS_OK(status)) {
642                 /*
643                  * TODO: retry, OS/2 nofiles
644                  */
645                 tevent_req_nterror(req, status);
646                 return;
647         }
648
649         if (state->first) {
650                 state->ff_dir_handle = SVAL(param, 0);
651                 ff_searchcount = SVAL(param, 2);
652                 ff_eos = SVAL(param, 4) != 0;
653         } else {
654                 ff_searchcount = SVAL(param, 0);
655                 ff_eos = SVAL(param, 2) != 0;
656         }
657
658         old_num_finfo = talloc_array_length(state->finfo);
659
660         tmp = TALLOC_REALLOC_ARRAY(state, state->finfo, struct file_info,
661                                    old_num_finfo + ff_searchcount);
662         if (tevent_req_nomem(tmp, req)) {
663                 return;
664         }
665         state->finfo = tmp;
666
667         p2 = p = (char *)data;
668         data_end = (char *)data + num_data;
669         last_name_raw = data_blob_null;
670
671         for (i=0; i<ff_searchcount; i++) {
672                 if (p2 >= data_end) {
673                         ff_eos = true;
674                         break;
675                 }
676                 if ((state->info_level == SMB_FIND_FILE_BOTH_DIRECTORY_INFO)
677                     && (i == ff_searchcount-1)) {
678                         /* Last entry - fixup the last offset length. */
679                         SIVAL(p2, 0, PTR_DIFF((data + num_data), p2));
680                 }
681
682                 data_blob_free(&last_name_raw);
683
684                 finfo = &state->finfo[old_num_finfo + i];
685
686                 p2 += interpret_long_filename(
687                         state->finfo, /* Stick fname to the array as such */
688                         state->cli, state->info_level,
689                         (char *)data, recv_flags2, p2,
690                         data_end, finfo, &resume_key, &last_name_raw);
691
692                 if (finfo->name == NULL) {
693                         DEBUG(1, ("cli_list: Error: unable to parse name from "
694                                   "info level %d\n", state->info_level));
695                         ff_eos = true;
696                         break;
697                 }
698                 if (!state->first && (state->mask[0] != '\0') &&
699                     strcsequal(finfo->name, state->mask)) {
700                         DEBUG(1, ("Error: Looping in FIND_NEXT as name %s has "
701                                   "already been seen?\n", finfo->name));
702                         ff_eos = true;
703                         break;
704                 }
705         }
706
707         if (ff_searchcount == 0) {
708                 ff_eos = true;
709         }
710
711         TALLOC_FREE(param);
712         TALLOC_FREE(data);
713
714         /*
715          * Shrink state->finfo to the real length we received
716          */
717         tmp = TALLOC_REALLOC_ARRAY(state, state->finfo, struct file_info,
718                                    old_num_finfo + i);
719         if (tevent_req_nomem(tmp, req)) {
720                 return;
721         }
722         state->finfo = tmp;
723
724         state->first = false;
725
726         if (ff_eos) {
727                 data_blob_free(&last_name_raw);
728                 tevent_req_done(req);
729                 return;
730         }
731
732         TALLOC_FREE(state->mask);
733         state->mask = talloc_strdup(state, finfo->name);
734         if (tevent_req_nomem(state->mask, req)) {
735                 return;
736         }
737
738         state->setup[0] = TRANSACT2_FINDNEXT;
739
740         nlen = 2*(strlen(state->mask) + 1);
741
742         param = TALLOC_REALLOC_ARRAY(state, state->param, uint8_t,
743                                      12 + nlen + last_name_raw.length + 2);
744         if (tevent_req_nomem(param, req)) {
745                 return;
746         }
747         state->param = param;
748
749         SSVAL(param, 0, state->ff_dir_handle);
750         SSVAL(param, 2, state->max_matches); /* max count */
751         SSVAL(param, 4, state->info_level);
752         /*
753          * For W2K servers serving out FAT filesystems we *must* set
754          * the resume key. If it's not FAT then it's returned as zero.
755          */
756         SIVAL(param, 6, resume_key); /* ff_resume_key */
757         /*
758          * NB. *DON'T* use continue here. If you do it seems that W2K
759          * and bretheren can miss filenames. Use last filename
760          * continue instead. JRA
761          */
762         SSVAL(param, 10, (FLAG_TRANS2_FIND_REQUIRE_RESUME
763                           |FLAG_TRANS2_FIND_CLOSE_IF_END));
764         p = ((char *)param)+12;
765         if (last_name_raw.length) {
766                 memcpy(p, last_name_raw.data, last_name_raw.length);
767                 p += last_name_raw.length;
768                 data_blob_free(&last_name_raw);
769         } else {
770                 p += clistr_push(state->cli, p, state->mask, nlen,
771                                  STR_TERMINATE);
772         }
773
774         param_len = PTR_DIFF(p, param);
775
776         subreq = cli_trans_send(state, state->ev, state->cli,
777                                 SMBtrans2, NULL, -1, 0, 0,
778                                 state->setup, 1, 0,
779                                 state->param, param_len, 10,
780                                 NULL, 0, state->cli->max_xmit);
781         if (tevent_req_nomem(subreq, req)) {
782                 return;
783         }
784         tevent_req_set_callback(subreq, cli_list_trans_done, req);
785 }
786
787 static NTSTATUS cli_list_trans_recv(struct tevent_req *req,
788                                     TALLOC_CTX *mem_ctx,
789                                     struct file_info **finfo)
790 {
791         struct cli_list_trans_state *state = tevent_req_data(
792                 req, struct cli_list_trans_state);
793         NTSTATUS status;
794
795         if (tevent_req_is_nterror(req, &status)) {
796                 return status;
797         }
798         *finfo = talloc_move(mem_ctx, &state->finfo);
799         return NT_STATUS_OK;
800 }
801
802 NTSTATUS cli_list_trans(struct cli_state *cli, const char *mask,
803                         uint16_t attribute, int info_level,
804                         NTSTATUS (*fn)(const char *mnt, struct file_info *finfo,
805                                    const char *mask, void *private_data),
806                         void *private_data)
807 {
808         TALLOC_CTX *frame = talloc_stackframe();
809         struct event_context *ev;
810         struct tevent_req *req;
811         int i, num_finfo;
812         struct file_info *finfo = NULL;
813         NTSTATUS status = NT_STATUS_NO_MEMORY;
814
815         if (cli_has_async_calls(cli)) {
816                 /*
817                  * Can't use sync call while an async call is in flight
818                  */
819                 status = NT_STATUS_INVALID_PARAMETER;
820                 goto fail;
821         }
822         ev = event_context_init(frame);
823         if (ev == NULL) {
824                 goto fail;
825         }
826         req = cli_list_trans_send(frame, ev, cli, mask, attribute, info_level);
827         if (req == NULL) {
828                 goto fail;
829         }
830         if (!tevent_req_poll_ntstatus(req, ev, &status)) {
831                 goto fail;
832         }
833         status = cli_list_trans_recv(req, frame, &finfo);
834         if (!NT_STATUS_IS_OK(status)) {
835                 goto fail;
836         }
837         num_finfo = talloc_array_length(finfo);
838         for (i=0; i<num_finfo; i++) {
839                 status = fn(cli->dfs_mountpoint, &finfo[i], mask, private_data);
840                 if (!NT_STATUS_IS_OK(status)) {
841                         goto fail;
842                 }
843         }
844  fail:
845         TALLOC_FREE(frame);
846         if (!NT_STATUS_IS_OK(status)) {
847                 cli_set_error(cli, status);
848         }
849         return status;
850 }
851
852 struct cli_list_state {
853         NTSTATUS (*recv_fn)(struct tevent_req *req, TALLOC_CTX *mem_ctx,
854                             struct file_info **finfo);
855         struct file_info *finfo;
856 };
857
858 static void cli_list_done(struct tevent_req *subreq);
859
860 struct tevent_req *cli_list_send(TALLOC_CTX *mem_ctx,
861                                  struct tevent_context *ev,
862                                  struct cli_state *cli,
863                                  const char *mask,
864                                  uint16_t attribute,
865                                  uint16_t info_level)
866 {
867         struct tevent_req *req, *subreq;
868         struct cli_list_state *state;
869
870         req = tevent_req_create(mem_ctx, &state, struct cli_list_state);
871         if (req == NULL) {
872                 return NULL;
873         }
874
875         if (cli->protocol <= PROTOCOL_LANMAN1) {
876                 subreq = cli_list_old_send(state, ev, cli, mask, attribute);
877                 state->recv_fn = cli_list_old_recv;
878         } else {
879                 subreq = cli_list_trans_send(state, ev, cli, mask, attribute,
880                                              info_level);
881                 state->recv_fn = cli_list_trans_recv;
882         }
883         if (tevent_req_nomem(subreq, req)) {
884                 return tevent_req_post(req, ev);
885         }
886         tevent_req_set_callback(subreq, cli_list_done, req);
887         return req;
888 }
889
890 static void cli_list_done(struct tevent_req *subreq)
891 {
892         struct tevent_req *req = tevent_req_callback_data(
893                 subreq, struct tevent_req);
894         struct cli_list_state *state = tevent_req_data(
895                 req, struct cli_list_state);
896         NTSTATUS status;
897
898         status = state->recv_fn(subreq, state, &state->finfo);
899         TALLOC_FREE(subreq);
900         if (!NT_STATUS_IS_OK(status)) {
901                 tevent_req_nterror(req, status);
902                 return;
903         }
904         tevent_req_done(req);
905 }
906
907 NTSTATUS cli_list_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
908                        struct file_info **finfo, size_t *num_finfo)
909 {
910         struct cli_list_state *state = tevent_req_data(
911                 req, struct cli_list_state);
912         NTSTATUS status;
913
914         if (tevent_req_is_nterror(req, &status)) {
915                 return status;
916         }
917         *num_finfo = talloc_array_length(state->finfo);
918         *finfo = talloc_move(mem_ctx, &state->finfo);
919         return NT_STATUS_OK;
920 }
921
922 NTSTATUS cli_list(struct cli_state *cli, const char *mask, uint16 attribute,
923                   NTSTATUS (*fn)(const char *, struct file_info *, const char *,
924                              void *), void *state)
925 {
926         TALLOC_CTX *frame = talloc_stackframe();
927         struct event_context *ev;
928         struct tevent_req *req;
929         NTSTATUS status = NT_STATUS_NO_MEMORY;
930         struct file_info *finfo;
931         size_t i, num_finfo;
932         uint16_t info_level;
933
934         if (cli_has_async_calls(cli)) {
935                 /*
936                  * Can't use sync call while an async call is in flight
937                  */
938                 status = NT_STATUS_INVALID_PARAMETER;
939                 goto fail;
940         }
941         ev = event_context_init(frame);
942         if (ev == NULL) {
943                 goto fail;
944         }
945
946         info_level = (cli->capabilities & CAP_NT_SMBS)
947                 ? SMB_FIND_FILE_BOTH_DIRECTORY_INFO : SMB_FIND_INFO_STANDARD;
948
949         req = cli_list_send(frame, ev, cli, mask, attribute, info_level);
950         if (req == NULL) {
951                 goto fail;
952         }
953         if (!tevent_req_poll(req, ev)) {
954                 status = map_nt_error_from_unix(errno);
955                 goto fail;
956         }
957
958         status = cli_list_recv(req, frame, &finfo, &num_finfo);
959         if (!NT_STATUS_IS_OK(status)) {
960                 goto fail;
961         }
962
963         for (i=0; i<num_finfo; i++) {
964                 status = fn(cli->dfs_mountpoint, &finfo[i], mask, state);
965                 if (!NT_STATUS_IS_OK(status)) {
966                         goto fail;
967                 }
968         }
969  fail:
970         TALLOC_FREE(frame);
971         if (!NT_STATUS_IS_OK(status)) {
972                 cli_set_error(cli, status);
973         }
974         return status;
975 }