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