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