s3:libsmb: make use of cli_state_server_time_zone()
[obnox/samba/samba-obnox.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_state_server_time_zone(cli)));
81                         finfo->atime_ts = convert_time_t_to_timespec(
82                                 make_unix_date2(p+8, cli_state_server_time_zone(cli)));
83                         finfo->mtime_ts = convert_time_t_to_timespec(
84                                 make_unix_date2(p+12, cli_state_server_time_zone(cli)));
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_state_server_time_zone(cli)));
130                         finfo->atime_ts = convert_time_t_to_timespec(
131                                 make_unix_date2(p+8, cli_state_server_time_zone(cli)));
132                         finfo->mtime_ts = convert_time_t_to_timespec(
133                                 make_unix_date2(p+12, cli_state_server_time_zone(cli)));
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                         ret = clistr_pull_talloc(ctx,
193                                                 base_ptr,
194                                                 recv_flags2,
195                                                 &finfo->short_name,
196                                                 p,
197                                                 slen,
198                                                 STR_UNICODE);
199                         if (ret == (size_t)-1) {
200                                 return pdata_end - base;
201                         }
202                         p += 24; /* short name? */
203                         if (p + namelen < p || p + namelen > pdata_end) {
204                                 return pdata_end - base;
205                         }
206                         ret = clistr_pull_talloc(ctx,
207                                                 base_ptr,
208                                                 recv_flags2,
209                                                 &finfo->name,
210                                                 p,
211                                                 namelen,
212                                                 0);
213                         if (ret == (size_t)-1) {
214                                 return pdata_end - base;
215                         }
216
217                         /* To be robust in the face of unicode conversion failures
218                            we need to copy the raw bytes of the last name seen here.
219                            Namelen doesn't include the terminating unicode null, so
220                            copy it here. */
221
222                         if (p_last_name_raw) {
223                                 *p_last_name_raw = data_blob(NULL, namelen+2);
224                                 memcpy(p_last_name_raw->data, p, namelen);
225                                 SSVAL(p_last_name_raw->data, namelen, 0);
226                         }
227                         return calc_next_entry_offset(base, pdata_end);
228                 }
229         }
230
231         DEBUG(1,("Unknown long filename format %d\n",level));
232         return calc_next_entry_offset(base, pdata_end);
233 }
234
235 /****************************************************************************
236  Interpret a short filename structure.
237  The length of the structure is returned.
238 ****************************************************************************/
239
240 static bool interpret_short_filename(TALLOC_CTX *ctx,
241                                 struct cli_state *cli,
242                                 char *p,
243                                 struct file_info *finfo)
244 {
245         size_t ret;
246         ZERO_STRUCTP(finfo);
247
248         finfo->mode = CVAL(p,21);
249
250         /* this date is converted to GMT by make_unix_date */
251         finfo->ctime_ts.tv_sec = make_unix_date(p+22, cli_state_server_time_zone(cli));
252         finfo->ctime_ts.tv_nsec = 0;
253         finfo->mtime_ts.tv_sec = finfo->atime_ts.tv_sec = finfo->ctime_ts.tv_sec;
254         finfo->mtime_ts.tv_nsec = finfo->atime_ts.tv_nsec = 0;
255         finfo->size = IVAL(p,26);
256         ret = clistr_pull_talloc(ctx,
257                         NULL,
258                         0,
259                         &finfo->name,
260                         p+30,
261                         12,
262                         STR_ASCII);
263         if (ret == (size_t)-1) {
264                 return false;
265         }
266
267         if (finfo->name) {
268                 finfo->short_name = talloc_strdup(ctx, finfo->name);
269                 if (finfo->short_name == NULL) {
270                         return false;
271                 }
272         }
273         return true;
274 }
275
276 struct cli_list_old_state {
277         struct tevent_context *ev;
278         struct cli_state *cli;
279         uint16_t vwv[2];
280         char *mask;
281         int num_asked;
282         uint16_t attribute;
283         uint8_t search_status[23];
284         bool first;
285         bool done;
286         uint8_t *dirlist;
287 };
288
289 static void cli_list_old_done(struct tevent_req *subreq);
290
291 static struct tevent_req *cli_list_old_send(TALLOC_CTX *mem_ctx,
292                                             struct tevent_context *ev,
293                                             struct cli_state *cli,
294                                             const char *mask,
295                                             uint16_t attribute)
296 {
297         struct tevent_req *req, *subreq;
298         struct cli_list_old_state *state;
299         uint8_t *bytes;
300         static const uint16_t zero = 0;
301         uint32_t usable_space;
302
303         req = tevent_req_create(mem_ctx, &state, struct cli_list_old_state);
304         if (req == NULL) {
305                 return NULL;
306         }
307         state->ev = ev;
308         state->cli = cli;
309         state->attribute = attribute;
310         state->first = true;
311         state->mask = talloc_strdup(state, mask);
312         if (tevent_req_nomem(state->mask, req)) {
313                 return tevent_req_post(req, ev);
314         }
315         usable_space = cli_state_available_size(cli, 100);
316         state->num_asked = usable_space / DIR_STRUCT_SIZE;
317
318         SSVAL(state->vwv + 0, 0, state->num_asked);
319         SSVAL(state->vwv + 1, 0, state->attribute);
320
321         bytes = talloc_array(state, uint8_t, 1);
322         if (tevent_req_nomem(bytes, req)) {
323                 return tevent_req_post(req, ev);
324         }
325         bytes[0] = 4;
326         bytes = smb_bytes_push_str(bytes, cli_ucs2(cli), mask,
327                                    strlen(mask)+1, NULL);
328
329         bytes = smb_bytes_push_bytes(bytes, 5, (const uint8_t *)&zero, 2);
330         if (tevent_req_nomem(bytes, req)) {
331                 return tevent_req_post(req, ev);
332         }
333
334         subreq = cli_smb_send(state, state->ev, state->cli, SMBsearch,
335                               0, 2, state->vwv, talloc_get_size(bytes), bytes);
336         if (tevent_req_nomem(subreq, req)) {
337                 return tevent_req_post(req, ev);
338         }
339         tevent_req_set_callback(subreq, cli_list_old_done, req);
340         return req;
341 }
342
343 static void cli_list_old_done(struct tevent_req *subreq)
344 {
345         struct tevent_req *req = tevent_req_callback_data(
346                 subreq, struct tevent_req);
347         struct cli_list_old_state *state = tevent_req_data(
348                 req, struct cli_list_old_state);
349         NTSTATUS status;
350         uint8_t cmd;
351         uint8_t wct;
352         uint16_t *vwv;
353         uint32_t num_bytes;
354         uint8_t *bytes;
355         uint16_t received;
356         size_t dirlist_len;
357         uint8_t *tmp;
358
359         status = cli_smb_recv(subreq, state, NULL, 0, &wct, &vwv, &num_bytes,
360                               &bytes);
361         if (!NT_STATUS_IS_OK(status)
362             && !NT_STATUS_EQUAL(status, NT_STATUS_DOS(ERRDOS, ERRnofiles))
363             && !NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) {
364                 TALLOC_FREE(subreq);
365                 tevent_req_nterror(req, status);
366                 return;
367         }
368         if (NT_STATUS_EQUAL(status, NT_STATUS_DOS(ERRDOS, ERRnofiles))
369             || NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) {
370                 received = 0;
371         } else {
372                 if (wct < 1) {
373                         TALLOC_FREE(subreq);
374                         tevent_req_nterror(
375                                 req, NT_STATUS_INVALID_NETWORK_RESPONSE);
376                         return;
377                 }
378                 received = SVAL(vwv + 0, 0);
379         }
380
381         if (received > 0) {
382                 /*
383                  * I don't think this can wrap. received is
384                  * initialized from a 16-bit value.
385                  */
386                 if (num_bytes < (received * DIR_STRUCT_SIZE + 3)) {
387                         TALLOC_FREE(subreq);
388                         tevent_req_nterror(
389                                 req, NT_STATUS_INVALID_NETWORK_RESPONSE);
390                         return;
391                 }
392
393                 dirlist_len = talloc_get_size(state->dirlist);
394
395                 tmp = talloc_realloc(
396                         state, state->dirlist, uint8_t,
397                         dirlist_len + received * DIR_STRUCT_SIZE);
398                 if (tevent_req_nomem(tmp, req)) {
399                         return;
400                 }
401                 state->dirlist = tmp;
402                 memcpy(state->dirlist + dirlist_len, bytes + 3,
403                        received * DIR_STRUCT_SIZE);
404
405                 SSVAL(state->search_status, 0, 21);
406                 memcpy(state->search_status + 2,
407                        bytes + 3 + (received-1)*DIR_STRUCT_SIZE, 21);
408                 cmd = SMBsearch;
409         } else {
410                 if (state->first || state->done) {
411                         tevent_req_done(req);
412                         return;
413                 }
414                 state->done = true;
415                 state->num_asked = 0;
416                 cmd = SMBfclose;
417         }
418         TALLOC_FREE(subreq);
419
420         state->first = false;
421
422         SSVAL(state->vwv + 0, 0, state->num_asked);
423         SSVAL(state->vwv + 1, 0, state->attribute);
424
425         bytes = talloc_array(state, uint8_t, 1);
426         if (tevent_req_nomem(bytes, req)) {
427                 return;
428         }
429         bytes[0] = 4;
430         bytes = smb_bytes_push_str(bytes, cli_ucs2(state->cli), "",
431                                    1, NULL);
432         bytes = smb_bytes_push_bytes(bytes, 5, state->search_status,
433                                      sizeof(state->search_status));
434         if (tevent_req_nomem(bytes, req)) {
435                 return;
436         }
437         subreq = cli_smb_send(state, state->ev, state->cli, cmd, 0,
438                               2, state->vwv, talloc_get_size(bytes), bytes);
439         if (tevent_req_nomem(subreq, req)) {
440                 return;
441         }
442         tevent_req_set_callback(subreq, cli_list_old_done, req);
443 }
444
445 static NTSTATUS cli_list_old_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
446                                   struct file_info **pfinfo)
447 {
448         struct cli_list_old_state *state = tevent_req_data(
449                 req, struct cli_list_old_state);
450         NTSTATUS status;
451         size_t i, num_received;
452         struct file_info *finfo;
453
454         if (tevent_req_is_nterror(req, &status)) {
455                 return status;
456         }
457
458         num_received = talloc_array_length(state->dirlist) / DIR_STRUCT_SIZE;
459
460         finfo = talloc_array(mem_ctx, struct file_info, num_received);
461         if (finfo == NULL) {
462                 return NT_STATUS_NO_MEMORY;
463         }
464
465         for (i=0; i<num_received; i++) {
466                 if (!interpret_short_filename(
467                             finfo, state->cli,
468                             (char *)state->dirlist + i * DIR_STRUCT_SIZE,
469                             &finfo[i])) {
470                         TALLOC_FREE(finfo);
471                         return NT_STATUS_NO_MEMORY;
472                 }
473         }
474         *pfinfo = finfo;
475         return NT_STATUS_OK;
476 }
477
478 NTSTATUS cli_list_old(struct cli_state *cli, const char *mask,
479                       uint16 attribute,
480                       NTSTATUS (*fn)(const char *, struct file_info *,
481                                  const char *, void *), void *state)
482 {
483         TALLOC_CTX *frame = talloc_stackframe();
484         struct event_context *ev;
485         struct tevent_req *req;
486         NTSTATUS status = NT_STATUS_NO_MEMORY;
487         struct file_info *finfo;
488         size_t i, num_finfo;
489
490         if (cli_has_async_calls(cli)) {
491                 /*
492                  * Can't use sync call while an async call is in flight
493                  */
494                 status = NT_STATUS_INVALID_PARAMETER;
495                 goto fail;
496         }
497         ev = event_context_init(frame);
498         if (ev == NULL) {
499                 goto fail;
500         }
501         req = cli_list_old_send(frame, ev, cli, mask, attribute);
502         if (req == NULL) {
503                 goto fail;
504         }
505         if (!tevent_req_poll(req, ev)) {
506                 status = map_nt_error_from_unix(errno);
507                 goto fail;
508         }
509         status = cli_list_old_recv(req, frame, &finfo);
510         if (!NT_STATUS_IS_OK(status)) {
511                 goto fail;
512         }
513         num_finfo = talloc_array_length(finfo);
514         for (i=0; i<num_finfo; i++) {
515                 status = fn(cli->dfs_mountpoint, &finfo[i], mask, state);
516                 if (!NT_STATUS_IS_OK(status)) {
517                         goto fail;
518                 }
519         }
520  fail:
521         TALLOC_FREE(frame);
522         return status;
523 }
524
525 struct cli_list_trans_state {
526         struct tevent_context *ev;
527         struct cli_state *cli;
528         char *mask;
529         uint16_t attribute;
530         uint16_t info_level;
531
532         int loop_count;
533         int total_received;
534         uint16_t max_matches;
535         bool first;
536
537         int ff_eos;
538         int ff_dir_handle;
539
540         uint16_t setup[1];
541         uint8_t *param;
542
543         struct file_info *finfo;
544 };
545
546 static void cli_list_trans_done(struct tevent_req *subreq);
547
548 static struct tevent_req *cli_list_trans_send(TALLOC_CTX *mem_ctx,
549                                               struct tevent_context *ev,
550                                               struct cli_state *cli,
551                                               const char *mask,
552                                               uint16_t attribute,
553                                               uint16_t info_level)
554 {
555         struct tevent_req *req, *subreq;
556         struct cli_list_trans_state *state;
557         size_t param_len;
558
559         req = tevent_req_create(mem_ctx, &state,
560                                 struct cli_list_trans_state);
561         if (req == NULL) {
562                 return NULL;
563         }
564         state->ev = ev;
565         state->cli = cli;
566         state->mask = talloc_strdup(state, mask);
567         if (tevent_req_nomem(state->mask, req)) {
568                 return tevent_req_post(req, ev);
569         }
570         state->attribute = attribute;
571         state->info_level = info_level;
572         state->loop_count = 0;
573         state->first = true;
574
575         state->max_matches = 1366; /* Match W2k */
576
577         SSVAL(&state->setup[0], 0, TRANSACT2_FINDFIRST);
578
579         state->param = talloc_array(state, uint8_t, 12);
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         state->param = trans2_bytes_push_str(state->param, cli_ucs2(cli),
593                                              state->mask, strlen(state->mask)+1,
594                                              NULL);
595         if (tevent_req_nomem(state->param, req)) {
596                 return tevent_req_post(req, ev);
597         }
598         param_len = talloc_get_size(state->param);
599
600         subreq = cli_trans_send(state, state->ev, state->cli,
601                                 SMBtrans2, NULL, -1, 0, 0,
602                                 state->setup, 1, 0,
603                                 state->param, param_len, 10,
604                                 NULL, 0, CLI_BUFFER_SIZE);
605         if (tevent_req_nomem(subreq, req)) {
606                 return tevent_req_post(req, ev);
607         }
608         tevent_req_set_callback(subreq, cli_list_trans_done, req);
609         return req;
610 }
611
612 static void cli_list_trans_done(struct tevent_req *subreq)
613 {
614         struct tevent_req *req = tevent_req_callback_data(
615                 subreq, struct tevent_req);
616         struct cli_list_trans_state *state = tevent_req_data(
617                 req, struct cli_list_trans_state);
618         NTSTATUS status;
619         uint8_t *param;
620         uint32_t num_param;
621         uint8_t *data;
622         char *data_end;
623         uint32_t num_data;
624         uint32_t min_param;
625         struct file_info *tmp;
626         size_t old_num_finfo;
627         uint16_t recv_flags2;
628         int ff_searchcount;
629         bool ff_eos;
630         char *p, *p2;
631         uint32_t resume_key = 0;
632         int i;
633         DATA_BLOB last_name_raw;
634         struct file_info *finfo = NULL;
635         size_t param_len;
636
637         min_param = (state->first ? 6 : 4);
638
639         status = cli_trans_recv(subreq, talloc_tos(), &recv_flags2,
640                                 NULL, 0, NULL,
641                                 &param, min_param, &num_param,
642                                 &data, 0, &num_data);
643         TALLOC_FREE(subreq);
644         if (!NT_STATUS_IS_OK(status)) {
645                 /*
646                  * TODO: retry, OS/2 nofiles
647                  */
648                 tevent_req_nterror(req, status);
649                 return;
650         }
651
652         if (state->first) {
653                 state->ff_dir_handle = SVAL(param, 0);
654                 ff_searchcount = SVAL(param, 2);
655                 ff_eos = SVAL(param, 4) != 0;
656         } else {
657                 ff_searchcount = SVAL(param, 0);
658                 ff_eos = SVAL(param, 2) != 0;
659         }
660
661         old_num_finfo = talloc_array_length(state->finfo);
662
663         tmp = talloc_realloc(state, state->finfo, struct file_info,
664                                    old_num_finfo + ff_searchcount);
665         if (tevent_req_nomem(tmp, req)) {
666                 return;
667         }
668         state->finfo = tmp;
669
670         p2 = p = (char *)data;
671         data_end = (char *)data + num_data;
672         last_name_raw = data_blob_null;
673
674         for (i=0; i<ff_searchcount; i++) {
675                 if (p2 >= data_end) {
676                         ff_eos = true;
677                         break;
678                 }
679                 if ((state->info_level == SMB_FIND_FILE_BOTH_DIRECTORY_INFO)
680                     && (i == ff_searchcount-1)) {
681                         /* Last entry - fixup the last offset length. */
682                         SIVAL(p2, 0, PTR_DIFF((data + num_data), p2));
683                 }
684
685                 data_blob_free(&last_name_raw);
686
687                 finfo = &state->finfo[old_num_finfo + i];
688
689                 p2 += interpret_long_filename(
690                         state->finfo, /* Stick fname to the array as such */
691                         state->cli, state->info_level,
692                         (char *)data, recv_flags2, p2,
693                         data_end, finfo, &resume_key, &last_name_raw);
694
695                 if (finfo->name == NULL) {
696                         DEBUG(1, ("cli_list: Error: unable to parse name from "
697                                   "info level %d\n", state->info_level));
698                         ff_eos = true;
699                         break;
700                 }
701                 if (!state->first && (state->mask[0] != '\0') &&
702                     strcsequal(finfo->name, state->mask)) {
703                         DEBUG(1, ("Error: Looping in FIND_NEXT as name %s has "
704                                   "already been seen?\n", finfo->name));
705                         ff_eos = true;
706                         break;
707                 }
708         }
709
710         if (ff_searchcount == 0) {
711                 ff_eos = true;
712         }
713
714         TALLOC_FREE(param);
715         TALLOC_FREE(data);
716
717         /*
718          * Shrink state->finfo to the real length we received
719          */
720         tmp = talloc_realloc(state, state->finfo, struct file_info,
721                                    old_num_finfo + i);
722         if (tevent_req_nomem(tmp, req)) {
723                 return;
724         }
725         state->finfo = tmp;
726
727         state->first = false;
728
729         if (ff_eos) {
730                 data_blob_free(&last_name_raw);
731                 tevent_req_done(req);
732                 return;
733         }
734
735         TALLOC_FREE(state->mask);
736         state->mask = talloc_strdup(state, finfo->name);
737         if (tevent_req_nomem(state->mask, req)) {
738                 return;
739         }
740
741         SSVAL(&state->setup[0], 0, TRANSACT2_FINDNEXT);
742
743         param = talloc_realloc(state, state->param, uint8_t, 12);
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         if (last_name_raw.length) {
765                 state->param = trans2_bytes_push_bytes(state->param,
766                                                        last_name_raw.data,
767                                                        last_name_raw.length);
768                 if (tevent_req_nomem(state->param, req)) {
769                         return;
770                 }
771                 data_blob_free(&last_name_raw);
772         } else {
773                 state->param = trans2_bytes_push_str(state->param,
774                                                      cli_ucs2(state->cli),
775                                                      state->mask,
776                                                      strlen(state->mask)+1,
777                                                      NULL);
778                 if (tevent_req_nomem(state->param, req)) {
779                         return;
780                 }
781         }
782         param_len = talloc_get_size(state->param);
783
784         subreq = cli_trans_send(state, state->ev, state->cli,
785                                 SMBtrans2, NULL, -1, 0, 0,
786                                 state->setup, 1, 0,
787                                 state->param, param_len, 10,
788                                 NULL, 0, CLI_BUFFER_SIZE);
789         if (tevent_req_nomem(subreq, req)) {
790                 return;
791         }
792         tevent_req_set_callback(subreq, cli_list_trans_done, req);
793 }
794
795 static NTSTATUS cli_list_trans_recv(struct tevent_req *req,
796                                     TALLOC_CTX *mem_ctx,
797                                     struct file_info **finfo)
798 {
799         struct cli_list_trans_state *state = tevent_req_data(
800                 req, struct cli_list_trans_state);
801         NTSTATUS status;
802
803         if (tevent_req_is_nterror(req, &status)) {
804                 return status;
805         }
806         *finfo = talloc_move(mem_ctx, &state->finfo);
807         return NT_STATUS_OK;
808 }
809
810 NTSTATUS cli_list_trans(struct cli_state *cli, const char *mask,
811                         uint16_t attribute, int info_level,
812                         NTSTATUS (*fn)(const char *mnt, struct file_info *finfo,
813                                    const char *mask, void *private_data),
814                         void *private_data)
815 {
816         TALLOC_CTX *frame = talloc_stackframe();
817         struct event_context *ev;
818         struct tevent_req *req;
819         int i, num_finfo;
820         struct file_info *finfo = NULL;
821         NTSTATUS status = NT_STATUS_NO_MEMORY;
822
823         if (cli_has_async_calls(cli)) {
824                 /*
825                  * Can't use sync call while an async call is in flight
826                  */
827                 status = NT_STATUS_INVALID_PARAMETER;
828                 goto fail;
829         }
830         ev = event_context_init(frame);
831         if (ev == NULL) {
832                 goto fail;
833         }
834         req = cli_list_trans_send(frame, ev, cli, mask, attribute, info_level);
835         if (req == NULL) {
836                 goto fail;
837         }
838         if (!tevent_req_poll_ntstatus(req, ev, &status)) {
839                 goto fail;
840         }
841         status = cli_list_trans_recv(req, frame, &finfo);
842         if (!NT_STATUS_IS_OK(status)) {
843                 goto fail;
844         }
845         num_finfo = talloc_array_length(finfo);
846         for (i=0; i<num_finfo; i++) {
847                 status = fn(cli->dfs_mountpoint, &finfo[i], mask, private_data);
848                 if (!NT_STATUS_IS_OK(status)) {
849                         goto fail;
850                 }
851         }
852  fail:
853         TALLOC_FREE(frame);
854         return status;
855 }
856
857 struct cli_list_state {
858         NTSTATUS (*recv_fn)(struct tevent_req *req, TALLOC_CTX *mem_ctx,
859                             struct file_info **finfo);
860         struct file_info *finfo;
861 };
862
863 static void cli_list_done(struct tevent_req *subreq);
864
865 struct tevent_req *cli_list_send(TALLOC_CTX *mem_ctx,
866                                  struct tevent_context *ev,
867                                  struct cli_state *cli,
868                                  const char *mask,
869                                  uint16_t attribute,
870                                  uint16_t info_level)
871 {
872         struct tevent_req *req, *subreq;
873         struct cli_list_state *state;
874
875         req = tevent_req_create(mem_ctx, &state, struct cli_list_state);
876         if (req == NULL) {
877                 return NULL;
878         }
879
880         if (cli_state_protocol(cli) <= PROTOCOL_LANMAN1) {
881                 subreq = cli_list_old_send(state, ev, cli, mask, attribute);
882                 state->recv_fn = cli_list_old_recv;
883         } else {
884                 subreq = cli_list_trans_send(state, ev, cli, mask, attribute,
885                                              info_level);
886                 state->recv_fn = cli_list_trans_recv;
887         }
888         if (tevent_req_nomem(subreq, req)) {
889                 return tevent_req_post(req, ev);
890         }
891         tevent_req_set_callback(subreq, cli_list_done, req);
892         return req;
893 }
894
895 static void cli_list_done(struct tevent_req *subreq)
896 {
897         struct tevent_req *req = tevent_req_callback_data(
898                 subreq, struct tevent_req);
899         struct cli_list_state *state = tevent_req_data(
900                 req, struct cli_list_state);
901         NTSTATUS status;
902
903         status = state->recv_fn(subreq, state, &state->finfo);
904         TALLOC_FREE(subreq);
905         if (!NT_STATUS_IS_OK(status)) {
906                 tevent_req_nterror(req, status);
907                 return;
908         }
909         tevent_req_done(req);
910 }
911
912 NTSTATUS cli_list_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
913                        struct file_info **finfo, size_t *num_finfo)
914 {
915         struct cli_list_state *state = tevent_req_data(
916                 req, struct cli_list_state);
917         NTSTATUS status;
918
919         if (tevent_req_is_nterror(req, &status)) {
920                 return status;
921         }
922         *num_finfo = talloc_array_length(state->finfo);
923         *finfo = talloc_move(mem_ctx, &state->finfo);
924         return NT_STATUS_OK;
925 }
926
927 NTSTATUS cli_list(struct cli_state *cli, const char *mask, uint16 attribute,
928                   NTSTATUS (*fn)(const char *, struct file_info *, const char *,
929                              void *), void *state)
930 {
931         TALLOC_CTX *frame = talloc_stackframe();
932         struct event_context *ev;
933         struct tevent_req *req;
934         NTSTATUS status = NT_STATUS_NO_MEMORY;
935         struct file_info *finfo;
936         size_t i, num_finfo;
937         uint16_t info_level;
938
939         if (cli_has_async_calls(cli)) {
940                 /*
941                  * Can't use sync call while an async call is in flight
942                  */
943                 status = NT_STATUS_INVALID_PARAMETER;
944                 goto fail;
945         }
946         ev = event_context_init(frame);
947         if (ev == NULL) {
948                 goto fail;
949         }
950
951         info_level = (cli_state_capabilities(cli) & CAP_NT_SMBS)
952                 ? SMB_FIND_FILE_BOTH_DIRECTORY_INFO : SMB_FIND_INFO_STANDARD;
953
954         req = cli_list_send(frame, ev, cli, mask, attribute, info_level);
955         if (req == NULL) {
956                 goto fail;
957         }
958         if (!tevent_req_poll(req, ev)) {
959                 status = map_nt_error_from_unix(errno);
960                 goto fail;
961         }
962
963         status = cli_list_recv(req, frame, &finfo, &num_finfo);
964         if (!NT_STATUS_IS_OK(status)) {
965                 goto fail;
966         }
967
968         for (i=0; i<num_finfo; i++) {
969                 status = fn(cli->dfs_mountpoint, &finfo[i], mask, state);
970                 if (!NT_STATUS_IS_OK(status)) {
971                         goto fail;
972                 }
973         }
974  fail:
975         TALLOC_FREE(frame);
976         return status;
977 }