e6047254930a8adefcf76f9113922643758f2f30
[jra/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
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. 260 is used
42  by NT and 2 is used by OS/2
43 ****************************************************************************/
44
45 static size_t interpret_long_filename(TALLOC_CTX *ctx,
46                                         struct cli_state *cli,
47                                         int level,
48                                         const char *p,
49                                         const char *pdata_end,
50                                         file_info *finfo,
51                                         uint32 *p_resume_key,
52                                         DATA_BLOB *p_last_name_raw)
53 {
54         int len;
55         size_t ret;
56         const char *base = p;
57
58         data_blob_free(p_last_name_raw);
59
60         if (p_resume_key) {
61                 *p_resume_key = 0;
62         }
63         ZERO_STRUCTP(finfo);
64         finfo->cli = cli;
65
66         switch (level) {
67                 case 1: /* OS/2 understands this */
68                         /* these dates are converted to GMT by
69                            make_unix_date */
70                         if (pdata_end - base < 27) {
71                                 return pdata_end - base;
72                         }
73                         finfo->ctime_ts = convert_time_t_to_timespec(cli_make_unix_date2(cli, p+4));
74                         finfo->atime_ts = convert_time_t_to_timespec(cli_make_unix_date2(cli, p+8));
75                         finfo->mtime_ts = convert_time_t_to_timespec(cli_make_unix_date2(cli, p+12));
76                         finfo->size = IVAL(p,16);
77                         finfo->mode = CVAL(p,24);
78                         len = CVAL(p, 26);
79                         p += 27;
80                         p += clistr_align_in(cli, p, 0);
81
82                         /* We can safely use len here (which is required by OS/2)
83                          * and the NAS-BASIC server instead of +2 or +1 as the
84                          * STR_TERMINATE flag below is
85                          * actually used as the length calculation.
86                          * The len is merely an upper bound.
87                          * Due to the explicit 2 byte null termination
88                          * in cli_receive_trans/cli_receive_nt_trans
89                          * we know this is safe. JRA + kukks
90                          */
91
92                         if (p + len > pdata_end) {
93                                 return pdata_end - base;
94                         }
95
96                         /* the len+2 below looks strange but it is
97                            important to cope with the differences
98                            between win2000 and win9x for this call
99                            (tridge) */
100                         ret = clistr_pull_talloc(ctx,
101                                                 cli->inbuf,
102                                                 &finfo->name,
103                                                 p,
104                                                 len+2,
105                                                 STR_TERMINATE);
106                         if (ret == (size_t)-1) {
107                                 return pdata_end - base;
108                         }
109                         p += ret;
110                         return PTR_DIFF(p, base);
111
112                 case 2: /* this is what OS/2 uses mostly */
113                         /* these dates are converted to GMT by
114                            make_unix_date */
115                         if (pdata_end - base < 31) {
116                                 return pdata_end - base;
117                         }
118                         finfo->ctime_ts = convert_time_t_to_timespec(cli_make_unix_date2(cli, p+4));
119                         finfo->atime_ts = convert_time_t_to_timespec(cli_make_unix_date2(cli, p+8));
120                         finfo->mtime_ts = convert_time_t_to_timespec(cli_make_unix_date2(cli, p+12));
121                         finfo->size = IVAL(p,16);
122                         finfo->mode = CVAL(p,24);
123                         len = CVAL(p, 30);
124                         p += 31;
125                         /* check for unisys! */
126                         if (p + len + 1 > pdata_end) {
127                                 return pdata_end - base;
128                         }
129                         ret = clistr_pull_talloc(ctx,
130                                                 cli->inbuf,
131                                                 &finfo->name,
132                                                 p,
133                                                 len,
134                                                 STR_NOALIGN);
135                         if (ret == (size_t)-1) {
136                                 return pdata_end - base;
137                         }
138                         p += ret;
139                         return PTR_DIFF(p, base) + 1;
140
141                 case 260: /* NT uses this, but also accepts 2 */
142                 {
143                         size_t namelen, slen;
144
145                         if (pdata_end - base < 94) {
146                                 return pdata_end - base;
147                         }
148
149                         p += 4; /* next entry offset */
150
151                         if (p_resume_key) {
152                                 *p_resume_key = IVAL(p,0);
153                         }
154                         p += 4; /* fileindex */
155
156                         /* Offset zero is "create time", not "change time". */
157                         p += 8;
158                         finfo->atime_ts = interpret_long_date(p);
159                         p += 8;
160                         finfo->mtime_ts = interpret_long_date(p);
161                         p += 8;
162                         finfo->ctime_ts = interpret_long_date(p);
163                         p += 8;
164                         finfo->size = IVAL2_TO_SMB_BIG_UINT(p,0);
165                         p += 8;
166                         p += 8; /* alloc size */
167                         finfo->mode = CVAL(p,0);
168                         p += 4;
169                         namelen = IVAL(p,0);
170                         p += 4;
171                         p += 4; /* EA size */
172                         slen = SVAL(p, 0);
173                         if (slen > 24) {
174                                 /* Bad short name length. */
175                                 return pdata_end - base;
176                         }
177                         p += 2;
178                         {
179                                 /* stupid NT bugs. grr */
180                                 int flags = 0;
181                                 if (p[1] == 0 && namelen > 1) flags |= STR_UNICODE;
182                                 clistr_pull(cli->inbuf, finfo->short_name, p,
183                                             sizeof(finfo->short_name),
184                                             slen, flags);
185                         }
186                         p += 24; /* short name? */
187                         if (p + namelen < p || p + namelen > pdata_end) {
188                                 return pdata_end - base;
189                         }
190                         ret = clistr_pull_talloc(ctx,
191                                                 cli->inbuf,
192                                                 &finfo->name,
193                                                 p,
194                                                 namelen,
195                                                 0);
196                         if (ret == (size_t)-1) {
197                                 return pdata_end - base;
198                         }
199
200                         /* To be robust in the face of unicode conversion failures
201                            we need to copy the raw bytes of the last name seen here.
202                            Namelen doesn't include the terminating unicode null, so
203                            copy it here. */
204
205                         if (p_last_name_raw) {
206                                 *p_last_name_raw = data_blob(NULL, namelen+2);
207                                 memcpy(p_last_name_raw->data, p, namelen);
208                                 SSVAL(p_last_name_raw->data, namelen, 0);
209                         }
210                         return calc_next_entry_offset(base, pdata_end);
211                 }
212         }
213
214         DEBUG(1,("Unknown long filename format %d\n",level));
215         return calc_next_entry_offset(base, pdata_end);
216 }
217
218 /****************************************************************************
219  Do a directory listing, calling fn on each file found.
220 ****************************************************************************/
221
222 int cli_list_new(struct cli_state *cli,const char *Mask,uint16 attribute,
223                  void (*fn)(const char *, file_info *, const char *, void *), void *state)
224 {
225 #if 1
226         int max_matches = 1366; /* Match W2k - was 512. */
227 #else
228         int max_matches = 512;
229 #endif
230         int info_level;
231         char *p, *p2, *rdata_end;
232         char *mask = NULL;
233         file_info finfo;
234         int i;
235         char *dirlist = NULL;
236         int dirlist_len = 0;
237         int total_received = -1;
238         bool First = True;
239         int ff_searchcount=0;
240         int ff_eos=0;
241         int ff_dir_handle=0;
242         int loop_count = 0;
243         char *rparam=NULL, *rdata=NULL;
244         unsigned int param_len, data_len;
245         uint16 setup;
246         char *param;
247         const char *mnt;
248         uint32 resume_key = 0;
249         TALLOC_CTX *frame = talloc_stackframe();
250         DATA_BLOB last_name_raw = data_blob(NULL, 0);
251
252         /* NT uses 260, OS/2 uses 2. Both accept 1. */
253         info_level = (cli->capabilities&CAP_NT_SMBS)?260:1;
254
255         mask = SMB_STRDUP(Mask);
256         if (!mask) {
257                 TALLOC_FREE(frame);
258                 return -1;
259         }
260
261         while (ff_eos == 0) {
262                 size_t nlen = 2*(strlen(mask)+1);
263
264                 loop_count++;
265                 if (loop_count > 200) {
266                         DEBUG(0,("Error: Looping in FIND_NEXT??\n"));
267                         break;
268                 }
269
270                 param = SMB_MALLOC_ARRAY(char, 12+nlen+last_name_raw.length+2);
271                 if (!param) {
272                         break;
273                 }
274
275                 if (First) {
276                         setup = TRANSACT2_FINDFIRST;
277                         SSVAL(param,0,attribute); /* attribute */
278                         SSVAL(param,2,max_matches); /* max count */
279                         SSVAL(param,4,(FLAG_TRANS2_FIND_REQUIRE_RESUME|FLAG_TRANS2_FIND_CLOSE_IF_END)); /* resume required + close on end */
280                         SSVAL(param,6,info_level);
281                         SIVAL(param,8,0);
282                         p = param+12;
283                         p += clistr_push(cli, param+12, mask,
284                                          nlen, STR_TERMINATE);
285                 } else {
286                         setup = TRANSACT2_FINDNEXT;
287                         SSVAL(param,0,ff_dir_handle);
288                         SSVAL(param,2,max_matches); /* max count */
289                         SSVAL(param,4,info_level);
290                         /* For W2K servers serving out FAT filesystems we *must* set the
291                            resume key. If it's not FAT then it's returned as zero. */
292                         SIVAL(param,6,resume_key); /* ff_resume_key */
293                         /* NB. *DON'T* use continue here. If you do it seems that W2K and bretheren
294                            can miss filenames. Use last filename continue instead. JRA */
295                         SSVAL(param,10,(FLAG_TRANS2_FIND_REQUIRE_RESUME|FLAG_TRANS2_FIND_CLOSE_IF_END));        /* resume required + close on end */
296                         p = param+12;
297                         if (last_name_raw.length) {
298                                 memcpy(p, last_name_raw.data, last_name_raw.length);
299                                 p += last_name_raw.length;
300                         } else {
301                                 p += clistr_push(cli, param+12, mask,
302                                                 nlen, STR_TERMINATE);
303                         }
304                 }
305
306                 param_len = PTR_DIFF(p, param);
307
308                 if (!cli_send_trans(cli, SMBtrans2,
309                                     NULL,                   /* Name */
310                                     -1, 0,                  /* fid, flags */
311                                     &setup, 1, 0,           /* setup, length, max */
312                                     param, param_len, 10,   /* param, length, max */
313                                     NULL, 0,
314 #if 0
315                                     /* w2k value. */
316                                     MIN(16384,cli->max_xmit) /* data, length, max. */
317 #else
318                                     cli->max_xmit           /* data, length, max. */
319 #endif
320                                     )) {
321                         SAFE_FREE(param);
322                         TALLOC_FREE(frame);
323                         break;
324                 }
325
326                 SAFE_FREE(param);
327
328                 if (!cli_receive_trans(cli, SMBtrans2,
329                                        &rparam, &param_len,
330                                        &rdata, &data_len) &&
331                     cli_is_dos_error(cli)) {
332                         /* We need to work around a Win95 bug - sometimes
333                            it gives ERRSRV/ERRerror temprarily */
334                         uint8 eclass;
335                         uint32 ecode;
336
337                         SAFE_FREE(rdata);
338                         SAFE_FREE(rparam);
339
340                         cli_dos_error(cli, &eclass, &ecode);
341
342                         /*
343                          * OS/2 might return "no more files",
344                          * which just tells us, that searchcount is zero
345                          * in this search.
346                          * Guenter Kukkukk <linux@kukkukk.com>
347                          */
348
349                         if (eclass == ERRDOS && ecode == ERRnofiles) {
350                                 ff_searchcount = 0;
351                                 cli_reset_error(cli);
352                                 break;
353                         }
354
355                         if (eclass != ERRSRV || ecode != ERRerror)
356                                 break;
357                         smb_msleep(100);
358                         continue;
359                 }
360
361                 if (cli_is_error(cli) || !rdata || !rparam) {
362                         SAFE_FREE(rdata);
363                         SAFE_FREE(rparam);
364                         break;
365                 }
366
367                 if (total_received == -1)
368                         total_received = 0;
369
370                 /* parse out some important return info */
371                 p = rparam;
372                 if (First) {
373                         ff_dir_handle = SVAL(p,0);
374                         ff_searchcount = SVAL(p,2);
375                         ff_eos = SVAL(p,4);
376                 } else {
377                         ff_searchcount = SVAL(p,0);
378                         ff_eos = SVAL(p,2);
379                 }
380
381                 if (ff_searchcount == 0) {
382                         SAFE_FREE(rdata);
383                         SAFE_FREE(rparam);
384                         break;
385                 }
386
387                 /* point to the data bytes */
388                 p = rdata;
389                 rdata_end = rdata + data_len;
390
391                 /* we might need the lastname for continuations */
392                 for (p2=p,i=0;i<ff_searchcount && p2 < rdata_end;i++) {
393                         if ((info_level == 260) && (i == ff_searchcount-1)) {
394                                 /* Last entry - fixup the last offset length. */
395                                 SIVAL(p2,0,PTR_DIFF((rdata + data_len),p2));
396                         }
397                         p2 += interpret_long_filename(frame,
398                                                         cli,
399                                                         info_level,
400                                                         p2,
401                                                         rdata_end,
402                                                         &finfo,
403                                                         &resume_key,
404                                                         &last_name_raw);
405
406                         if (!finfo.name) {
407                                 DEBUG(0,("cli_list_new: Error: unable to parse name from info level %d\n",
408                                         info_level));
409                                 ff_eos = 1;
410                                 break;
411                         }
412                         if (!First && *mask && strcsequal(finfo.name, mask)) {
413                                 DEBUG(0,("Error: Looping in FIND_NEXT as name %s has already been seen?\n",
414                                         finfo.name));
415                                 ff_eos = 1;
416                                 break;
417                         }
418                 }
419
420                 SAFE_FREE(mask);
421                 if (ff_searchcount > 0 && ff_eos == 0 && finfo.name) {
422                         mask = SMB_STRDUP(finfo.name);
423                 } else {
424                         mask = SMB_STRDUP("");
425                 }
426                 if (!mask) {
427                         SAFE_FREE(rdata);
428                         SAFE_FREE(rparam);
429                         break;
430                 }
431
432                 /* grab the data for later use */
433                 /* and add them to the dirlist pool */
434                 dirlist = (char *)SMB_REALLOC(dirlist,dirlist_len + data_len);
435
436                 if (!dirlist) {
437                         DEBUG(0,("cli_list_new: Failed to expand dirlist\n"));
438                         SAFE_FREE(rdata);
439                         SAFE_FREE(rparam);
440                         break;
441                 }
442
443                 memcpy(dirlist+dirlist_len,p,data_len);
444                 dirlist_len += data_len;
445
446                 total_received += ff_searchcount;
447
448                 SAFE_FREE(rdata);
449                 SAFE_FREE(rparam);
450
451                 DEBUG(3,("received %d entries (eos=%d)\n",
452                          ff_searchcount,ff_eos));
453
454                 if (ff_searchcount > 0)
455                         loop_count = 0;
456
457                 First = False;
458         }
459
460         mnt = cli_cm_get_mntpoint( cli );
461
462         /* see if the server disconnected or the connection otherwise failed */
463         if (cli_is_error(cli)) {
464                 total_received = -1;
465         } else {
466                 /* no connection problem.  let user function add each entry */
467                 rdata_end = dirlist + dirlist_len;
468                 for (p=dirlist,i=0;i<total_received;i++) {
469                         p += interpret_long_filename(frame,
470                                                         cli,
471                                                         info_level,
472                                                         p,
473                                                         rdata_end,
474                                                         &finfo,
475                                                         NULL,
476                                                         NULL);
477                         if (!finfo.name) {
478                                 DEBUG(0,("cli_list_new: unable to parse name from info level %d\n",
479                                         info_level));
480                                 break;
481                         }
482                         fn(mnt,&finfo, Mask, state);
483                 }
484         }
485
486         /* free up the dirlist buffer and last name raw blob */
487         SAFE_FREE(dirlist);
488         data_blob_free(&last_name_raw);
489         SAFE_FREE(mask);
490         TALLOC_FREE(frame);
491         return(total_received);
492 }
493
494 /****************************************************************************
495  Interpret a short filename structure.
496  The length of the structure is returned.
497 ****************************************************************************/
498
499 static bool interpret_short_filename(TALLOC_CTX *ctx,
500                                 struct cli_state *cli,
501                                 char *p,
502                                 file_info *finfo)
503 {
504         size_t ret;
505         ZERO_STRUCTP(finfo);
506
507         finfo->cli = cli;
508         finfo->mode = CVAL(p,21);
509
510         /* this date is converted to GMT by make_unix_date */
511         finfo->ctime_ts.tv_sec = cli_make_unix_date(cli, p+22);
512         finfo->ctime_ts.tv_nsec = 0;
513         finfo->mtime_ts.tv_sec = finfo->atime_ts.tv_sec = finfo->ctime_ts.tv_sec;
514         finfo->mtime_ts.tv_nsec = finfo->atime_ts.tv_nsec = 0;
515         finfo->size = IVAL(p,26);
516         ret = clistr_pull_talloc(ctx,
517                         cli->inbuf,
518                         &finfo->name,
519                         p+30,
520                         12,
521                         STR_ASCII);
522         if (ret == (size_t)-1) {
523                 return false;
524         }
525
526         if (finfo->name) {
527                 strlcpy(finfo->short_name,
528                         finfo->name,
529                         sizeof(finfo->short_name));
530         }
531         return true;
532         return(DIR_STRUCT_SIZE);
533 }
534
535 /****************************************************************************
536  Do a directory listing, calling fn on each file found.
537  this uses the old SMBsearch interface. It is needed for testing Samba,
538  but should otherwise not be used.
539 ****************************************************************************/
540
541 int cli_list_old(struct cli_state *cli,const char *Mask,uint16 attribute,
542                  void (*fn)(const char *, file_info *, const char *, void *), void *state)
543 {
544         char *p;
545         int received = 0;
546         bool first = True;
547         char status[21];
548         int num_asked = (cli->max_xmit - 100)/DIR_STRUCT_SIZE;
549         int num_received = 0;
550         int i;
551         char *dirlist = NULL;
552         char *mask = NULL;
553         TALLOC_CTX *frame = NULL;
554
555         ZERO_ARRAY(status);
556
557         mask = SMB_STRDUP(Mask);
558         if (!mask) {
559                 return -1;
560         }
561
562         while (1) {
563                 memset(cli->outbuf,'\0',smb_size);
564                 memset(cli->inbuf,'\0',smb_size);
565
566                 cli_set_message(cli->outbuf,2,0,True);
567
568                 SCVAL(cli->outbuf,smb_com,SMBsearch);
569
570                 SSVAL(cli->outbuf,smb_tid,cli->cnum);
571                 cli_setup_packet(cli);
572
573                 SSVAL(cli->outbuf,smb_vwv0,num_asked);
574                 SSVAL(cli->outbuf,smb_vwv1,attribute);
575
576                 p = smb_buf(cli->outbuf);
577                 *p++ = 4;
578
579                 p += clistr_push(cli, p, first?mask:"",
580                                 cli->bufsize - PTR_DIFF(p,cli->outbuf),
581                                 STR_TERMINATE);
582                 *p++ = 5;
583                 if (first) {
584                         SSVAL(p,0,0);
585                         p += 2;
586                 } else {
587                         SSVAL(p,0,21);
588                         p += 2;
589                         memcpy(p,status,21);
590                         p += 21;
591                 }
592
593                 cli_setup_bcc(cli, p);
594                 cli_send_smb(cli);
595                 if (!cli_receive_smb(cli)) break;
596
597                 received = SVAL(cli->inbuf,smb_vwv0);
598                 if (received <= 0) break;
599
600                 /* Ensure we received enough data. */
601                 if ((cli->inbuf+4+smb_len(cli->inbuf) - (smb_buf(cli->inbuf)+3)) <
602                                 received*DIR_STRUCT_SIZE) {
603                         break;
604                 }
605
606                 first = False;
607
608                 dirlist = (char *)SMB_REALLOC(
609                         dirlist,(num_received + received)*DIR_STRUCT_SIZE);
610                 if (!dirlist) {
611                         DEBUG(0,("cli_list_old: failed to expand dirlist"));
612                         SAFE_FREE(mask);
613                         return 0;
614                 }
615
616                 p = smb_buf(cli->inbuf) + 3;
617
618                 memcpy(dirlist+num_received*DIR_STRUCT_SIZE,
619                        p,received*DIR_STRUCT_SIZE);
620
621                 memcpy(status,p + ((received-1)*DIR_STRUCT_SIZE),21);
622
623                 num_received += received;
624
625                 if (cli_is_error(cli)) break;
626         }
627
628         if (!first) {
629                 memset(cli->outbuf,'\0',smb_size);
630                 memset(cli->inbuf,'\0',smb_size);
631
632                 cli_set_message(cli->outbuf,2,0,True);
633                 SCVAL(cli->outbuf,smb_com,SMBfclose);
634                 SSVAL(cli->outbuf,smb_tid,cli->cnum);
635                 cli_setup_packet(cli);
636
637                 SSVAL(cli->outbuf, smb_vwv0, 0); /* find count? */
638                 SSVAL(cli->outbuf, smb_vwv1, attribute);
639
640                 p = smb_buf(cli->outbuf);
641                 *p++ = 4;
642                 fstrcpy(p, "");
643                 p += strlen(p) + 1;
644                 *p++ = 5;
645                 SSVAL(p, 0, 21);
646                 p += 2;
647                 memcpy(p,status,21);
648                 p += 21;
649
650                 cli_setup_bcc(cli, p);
651                 cli_send_smb(cli);
652                 if (!cli_receive_smb(cli)) {
653                         DEBUG(0,("Error closing search: %s\n",cli_errstr(cli)));
654                 }
655         }
656
657         frame = talloc_stackframe();
658         for (p=dirlist,i=0;i<num_received;i++) {
659                 file_info finfo;
660                 if (!interpret_short_filename(frame, cli, p, &finfo)) {
661                         break;
662                 }
663                 p += DIR_STRUCT_SIZE;
664                 fn("\\", &finfo, Mask, state);
665         }
666         TALLOC_FREE(frame);
667
668         SAFE_FREE(mask);
669         SAFE_FREE(dirlist);
670         return(num_received);
671 }
672
673 /****************************************************************************
674  Do a directory listing, calling fn on each file found.
675  This auto-switches between old and new style.
676 ****************************************************************************/
677
678 int cli_list(struct cli_state *cli,const char *Mask,uint16 attribute,
679              void (*fn)(const char *, file_info *, const char *, void *), void *state)
680 {
681         if (cli->protocol <= PROTOCOL_LANMAN1)
682                 return cli_list_old(cli, Mask, attribute, fn, state);
683         return cli_list_new(cli, Mask, attribute, fn, state);
684 }