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