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