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