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