r16541: Fix #3862 reported by jason@ncac.gwu.edu.
[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 2 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, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 #include "includes.h"
22
23 extern file_info def_finfo;
24
25 /****************************************************************************
26  Interpret a long filename structure - this is mostly guesses at the moment.
27  The length of the structure is returned
28  The structure of a long filename depends on the info level. 260 is used
29  by NT and 2 is used by OS/2
30 ****************************************************************************/
31
32 static size_t interpret_long_filename(struct cli_state *cli, int level,char *p,file_info *finfo,
33                                         uint32 *p_resume_key, DATA_BLOB *p_last_name_raw, uint32 *p_last_name_raw_len)
34 {
35         file_info finfo2;
36         int len;
37         char *base = p;
38
39         if (!finfo) {
40                 finfo = &finfo2;
41         }
42
43         if (p_resume_key) {
44                 *p_resume_key = 0;
45         }
46         memcpy(finfo,&def_finfo,sizeof(*finfo));
47
48         switch (level) {
49                 case 1: /* OS/2 understands this */
50                         /* these dates are converted to GMT by
51                            make_unix_date */
52                         finfo->ctime = cli_make_unix_date2(cli, p+4);
53                         finfo->atime = cli_make_unix_date2(cli, p+8);
54                         finfo->mtime = cli_make_unix_date2(cli, p+12);
55                         finfo->size = IVAL(p,16);
56                         finfo->mode = CVAL(p,24);
57                         len = CVAL(p, 26);
58                         p += 27;
59                         p += clistr_align_in(cli, p, 0);
60                         /* the len+2 below looks strange but it is
61                            important to cope with the differences
62                            between win2000 and win9x for this call
63                            (tridge) */
64                         p += clistr_pull(cli, finfo->name, p,
65                                          sizeof(finfo->name),
66                                          len+2, 
67                                          STR_TERMINATE);
68                         return PTR_DIFF(p, base);
69
70                 case 2: /* this is what OS/2 uses mostly */
71                         /* these dates are converted to GMT by
72                            make_unix_date */
73                         finfo->ctime = cli_make_unix_date2(cli, p+4);
74                         finfo->atime = cli_make_unix_date2(cli, p+8);
75                         finfo->mtime = cli_make_unix_date2(cli, p+12);
76                         finfo->size = IVAL(p,16);
77                         finfo->mode = CVAL(p,24);
78                         len = CVAL(p, 30);
79                         p += 31;
80                         /* check for unisys! */
81                         p += clistr_pull(cli, finfo->name, p,
82                                          sizeof(finfo->name),
83                                          len, 
84                                          STR_NOALIGN);
85                         return PTR_DIFF(p, base) + 1;
86                         
87                 case 260: /* NT uses this, but also accepts 2 */
88                 {
89                         size_t namelen, slen;
90                         p += 4; /* next entry offset */
91
92                         if (p_resume_key) {
93                                 *p_resume_key = IVAL(p,0);
94                         }
95                         p += 4; /* fileindex */
96                                 
97                         /* these dates appear to arrive in a
98                            weird way. It seems to be localtime
99                            plus the serverzone given in the
100                            initial connect. This is GMT when
101                            DST is not in effect and one hour
102                            from GMT otherwise. Can this really
103                            be right??
104                            
105                            I suppose this could be called
106                            kludge-GMT. Is is the GMT you get
107                            by using the current DST setting on
108                            a different localtime. It will be
109                            cheap to calculate, I suppose, as
110                            no DST tables will be needed */
111                         
112                         finfo->ctime = interpret_long_date(p);
113                         p += 8;
114                         finfo->atime = interpret_long_date(p);
115                         p += 8;
116                         finfo->mtime = interpret_long_date(p);
117                         p += 8;
118                         p += 8;
119                         finfo->size = IVAL2_TO_SMB_BIG_UINT(p,0);
120                         p += 8;
121                         p += 8; /* alloc size */
122                         finfo->mode = CVAL(p,0);
123                         p += 4;
124                         namelen = IVAL(p,0);
125                         p += 4;
126                         p += 4; /* EA size */
127                         slen = SVAL(p, 0);
128                         p += 2; 
129                         {
130                                 /* stupid NT bugs. grr */
131                                 int flags = 0;
132                                 if (p[1] == 0 && namelen > 1) flags |= STR_UNICODE;
133                                 clistr_pull(cli, finfo->short_name, p,
134                                             sizeof(finfo->short_name),
135                                             slen, flags);
136                         }
137                         p += 24; /* short name? */        
138                         clistr_pull(cli, finfo->name, p,
139                                     sizeof(finfo->name),
140                                     namelen, 0);
141
142                         /* To be robust in the face of unicode conversion failures
143                            we need to copy the raw bytes of the last name seen here.
144                            Namelen doesn't include the terminating unicode null, so
145                            copy it here. */
146
147                         if (p_last_name_raw && p_last_name_raw_len) {
148                                 if (namelen + 2 > p_last_name_raw->length) {
149                                         memset(p_last_name_raw->data, '\0', sizeof(p_last_name_raw->length));
150                                         *p_last_name_raw_len = 0;
151                                 } else {
152                                         memcpy(p_last_name_raw->data, p, namelen);
153                                         SSVAL(p_last_name_raw->data, namelen, 0);
154                                         *p_last_name_raw_len = namelen + 2;
155                                 }
156                         }
157                         return (size_t)IVAL(base, 0);
158                 }
159         }
160         
161         DEBUG(1,("Unknown long filename format %d\n",level));
162         return (size_t)IVAL(base,0);
163 }
164
165 /****************************************************************************
166  Do a directory listing, calling fn on each file found.
167 ****************************************************************************/
168
169 int cli_list_new(struct cli_state *cli,const char *Mask,uint16 attribute, 
170                  void (*fn)(const char *, file_info *, const char *, void *), void *state)
171 {
172 #if 1
173         int max_matches = 1366; /* Match W2k - was 512. */
174 #else
175         int max_matches = 512;
176 #endif
177         int info_level;
178         char *p, *p2;
179         pstring mask;
180         file_info finfo;
181         int i;
182         char *dirlist = NULL;
183         int dirlist_len = 0;
184         int total_received = -1;
185         BOOL First = True;
186         int ff_searchcount=0;
187         int ff_eos=0;
188         int ff_dir_handle=0;
189         int loop_count = 0;
190         char *rparam=NULL, *rdata=NULL;
191         unsigned int param_len, data_len;       
192         uint16 setup;
193         pstring param;
194         const char *mnt;
195         uint32 resume_key = 0;
196         uint32 last_name_raw_len = 0;
197         DATA_BLOB last_name_raw = data_blob(NULL, 2*sizeof(pstring));
198
199         /* NT uses 260, OS/2 uses 2. Both accept 1. */
200         info_level = (cli->capabilities&CAP_NT_SMBS)?260:1;
201         
202         /* when getting a directory listing from a 2k dfs root share, 
203            we have to include the full path (\server\share\mask) here */
204            
205         if ( cli->dfsroot )
206                 pstr_sprintf( mask, "\\%s\\%s\\%s", cli->desthost, cli->share, Mask );
207         else
208                 pstrcpy(mask,Mask);
209         
210         while (ff_eos == 0) {
211                 loop_count++;
212                 if (loop_count > 200) {
213                         DEBUG(0,("Error: Looping in FIND_NEXT??\n"));
214                         break;
215                 }
216
217                 if (First) {
218                         setup = TRANSACT2_FINDFIRST;
219                         SSVAL(param,0,attribute); /* attribute */
220                         SSVAL(param,2,max_matches); /* max count */
221                         SSVAL(param,4,(FLAG_TRANS2_FIND_REQUIRE_RESUME|FLAG_TRANS2_FIND_CLOSE_IF_END)); /* resume required + close on end */
222                         SSVAL(param,6,info_level); 
223                         SIVAL(param,8,0);
224                         p = param+12;
225                         p += clistr_push(cli, param+12, mask, sizeof(param)-12, 
226                                          STR_TERMINATE);
227                 } else {
228                         setup = TRANSACT2_FINDNEXT;
229                         SSVAL(param,0,ff_dir_handle);
230                         SSVAL(param,2,max_matches); /* max count */
231                         SSVAL(param,4,info_level); 
232                         /* For W2K servers serving out FAT filesystems we *must* set the
233                            resume key. If it's not FAT then it's returned as zero. */
234                         SIVAL(param,6,resume_key); /* ff_resume_key */
235                         /* NB. *DON'T* use continue here. If you do it seems that W2K and bretheren
236                            can miss filenames. Use last filename continue instead. JRA */
237                         SSVAL(param,10,(FLAG_TRANS2_FIND_REQUIRE_RESUME|FLAG_TRANS2_FIND_CLOSE_IF_END));        /* resume required + close on end */
238                         p = param+12;
239                         if (last_name_raw_len && (last_name_raw_len < (sizeof(param)-12))) {
240                                 memcpy(p, last_name_raw.data, last_name_raw_len);
241                                 p += last_name_raw_len;
242                         } else {
243                                 p += clistr_push(cli, param+12, mask, sizeof(param)-12, STR_TERMINATE);
244                         }
245                 }
246
247                 param_len = PTR_DIFF(p, param);
248
249                 if (!cli_send_trans(cli, SMBtrans2, 
250                                     NULL,                   /* Name */
251                                     -1, 0,                  /* fid, flags */
252                                     &setup, 1, 0,           /* setup, length, max */
253                                     param, param_len, 10,   /* param, length, max */
254                                     NULL, 0, 
255 #if 0
256                                     /* w2k value. */
257                                     MIN(16384,cli->max_xmit) /* data, length, max. */
258 #else
259                                     cli->max_xmit           /* data, length, max. */
260 #endif
261                                     )) {
262                         break;
263                 }
264
265                 if (!cli_receive_trans(cli, SMBtrans2, 
266                                        &rparam, &param_len,
267                                        &rdata, &data_len) &&
268                     cli_is_dos_error(cli)) {
269                         /* we need to work around a Win95 bug - sometimes
270                            it gives ERRSRV/ERRerror temprarily */
271                         uint8 eclass;
272                         uint32 ecode;
273
274                         SAFE_FREE(rdata);
275                         SAFE_FREE(rparam);
276
277                         cli_dos_error(cli, &eclass, &ecode);
278                         if (eclass != ERRSRV || ecode != ERRerror)
279                                 break;
280                         smb_msleep(100);
281                         continue;
282                 }
283
284                 if (cli_is_error(cli) || !rdata || !rparam) {
285                         SAFE_FREE(rdata);
286                         SAFE_FREE(rparam);
287                         break;
288                 }
289
290                 if (total_received == -1)
291                         total_received = 0;
292
293                 /* parse out some important return info */
294                 p = rparam;
295                 if (First) {
296                         ff_dir_handle = SVAL(p,0);
297                         ff_searchcount = SVAL(p,2);
298                         ff_eos = SVAL(p,4);
299                 } else {
300                         ff_searchcount = SVAL(p,0);
301                         ff_eos = SVAL(p,2);
302                 }
303
304                 if (ff_searchcount == 0) {
305                         SAFE_FREE(rdata);
306                         SAFE_FREE(rparam);
307                         break;
308                 }
309
310                 /* point to the data bytes */
311                 p = rdata;
312
313                 /* we might need the lastname for continuations */
314                 for (p2=p,i=0;i<ff_searchcount;i++) {
315                         if ((info_level == 260) && (i == ff_searchcount-1)) {
316                                 /* Last entry - fixup the last offset length. */
317                                 SIVAL(p2,0,PTR_DIFF((rdata + data_len),p2));
318                         }
319                         p2 += interpret_long_filename(cli,info_level,p2,&finfo,
320                                                         &resume_key,&last_name_raw,&last_name_raw_len);
321
322                         if (!First && *mask && strcsequal(finfo.name, mask)) {
323                                 DEBUG(0,("Error: Looping in FIND_NEXT as name %s has already been seen?\n",
324                                         finfo.name));
325                                 ff_eos = 1;
326                                 break;
327                         }
328                 }
329
330                 if (ff_searchcount > 0) {
331                         pstrcpy(mask, finfo.name);
332                 } else {
333                         pstrcpy(mask,"");
334                 }
335
336                 /* grab the data for later use */
337                 /* and add them to the dirlist pool */
338                 dirlist = SMB_REALLOC(dirlist,dirlist_len + data_len);
339
340                 if (!dirlist) {
341                         DEBUG(0,("cli_list_new: Failed to expand dirlist\n"));
342                         SAFE_FREE(rdata);
343                         SAFE_FREE(rparam);
344                         break;
345                 }
346
347                 memcpy(dirlist+dirlist_len,p,data_len);
348                 dirlist_len += data_len;
349
350                 total_received += ff_searchcount;
351
352                 SAFE_FREE(rdata);
353                 SAFE_FREE(rparam);
354
355                 DEBUG(3,("received %d entries (eos=%d)\n",
356                          ff_searchcount,ff_eos));
357
358                 if (ff_searchcount > 0)
359                         loop_count = 0;
360
361                 First = False;
362         }
363
364         mnt = cli_cm_get_mntpoint( cli );
365
366         for (p=dirlist,i=0;i<total_received;i++) {
367                 p += interpret_long_filename(cli,info_level,p,&finfo,NULL,NULL,NULL);
368                 fn( mnt,&finfo, Mask, state );
369         }
370
371         /* free up the dirlist buffer and last name raw blob */
372         SAFE_FREE(dirlist);
373         data_blob_free(&last_name_raw);
374         return(total_received);
375 }
376
377 /****************************************************************************
378  Interpret a short filename structure.
379  The length of the structure is returned.
380 ****************************************************************************/
381
382 static int interpret_short_filename(struct cli_state *cli, char *p,file_info *finfo)
383 {
384
385         *finfo = def_finfo;
386
387         finfo->mode = CVAL(p,21);
388         
389         /* this date is converted to GMT by make_unix_date */
390         finfo->ctime = cli_make_unix_date(cli, p+22);
391         finfo->mtime = finfo->atime = finfo->ctime;
392         finfo->size = IVAL(p,26);
393         clistr_pull(cli, finfo->name, p+30, sizeof(finfo->name), 12, STR_ASCII);
394         if (strcmp(finfo->name, "..") && strcmp(finfo->name, ".")) {
395                 strncpy(finfo->short_name,finfo->name, sizeof(finfo->short_name)-1);
396                 finfo->short_name[sizeof(finfo->short_name)-1] = '\0';
397         }
398
399         return(DIR_STRUCT_SIZE);
400 }
401
402
403 /****************************************************************************
404  Do a directory listing, calling fn on each file found.
405  this uses the old SMBsearch interface. It is needed for testing Samba,
406  but should otherwise not be used.
407 ****************************************************************************/
408
409 int cli_list_old(struct cli_state *cli,const char *Mask,uint16 attribute, 
410                  void (*fn)(const char *, file_info *, const char *, void *), void *state)
411 {
412         char *p;
413         int received = 0;
414         BOOL first = True;
415         char status[21];
416         int num_asked = (cli->max_xmit - 100)/DIR_STRUCT_SIZE;
417         int num_received = 0;
418         int i;
419         char *dirlist = NULL;
420         pstring mask;
421         
422         ZERO_ARRAY(status);
423
424         pstrcpy(mask,Mask);
425   
426         while (1) {
427                 memset(cli->outbuf,'\0',smb_size);
428                 memset(cli->inbuf,'\0',smb_size);
429
430                 set_message(cli->outbuf,2,0,True);
431
432                 SCVAL(cli->outbuf,smb_com,SMBsearch);
433
434                 SSVAL(cli->outbuf,smb_tid,cli->cnum);
435                 cli_setup_packet(cli);
436
437                 SSVAL(cli->outbuf,smb_vwv0,num_asked);
438                 SSVAL(cli->outbuf,smb_vwv1,attribute);
439   
440                 p = smb_buf(cli->outbuf);
441                 *p++ = 4;
442       
443                 p += clistr_push(cli, p, first?mask:"", -1, STR_TERMINATE);
444                 *p++ = 5;
445                 if (first) {
446                         SSVAL(p,0,0);
447                         p += 2;
448                 } else {
449                         SSVAL(p,0,21);
450                         p += 2;
451                         memcpy(p,status,21);
452                         p += 21;
453                 }
454
455                 cli_setup_bcc(cli, p);
456                 cli_send_smb(cli);
457                 if (!cli_receive_smb(cli)) break;
458
459                 received = SVAL(cli->inbuf,smb_vwv0);
460                 if (received <= 0) break;
461
462                 first = False;
463
464                 dirlist = SMB_REALLOC(dirlist,(num_received + received)*DIR_STRUCT_SIZE);
465                 if (!dirlist) {
466                         DEBUG(0,("cli_list_old: failed to expand dirlist"));
467                         return 0;
468                 }
469
470                 p = smb_buf(cli->inbuf) + 3;
471
472                 memcpy(dirlist+num_received*DIR_STRUCT_SIZE,
473                        p,received*DIR_STRUCT_SIZE);
474                 
475                 memcpy(status,p + ((received-1)*DIR_STRUCT_SIZE),21);
476                 
477                 num_received += received;
478                 
479                 if (cli_is_error(cli)) break;
480         }
481
482         if (!first) {
483                 memset(cli->outbuf,'\0',smb_size);
484                 memset(cli->inbuf,'\0',smb_size);
485
486                 set_message(cli->outbuf,2,0,True);
487                 SCVAL(cli->outbuf,smb_com,SMBfclose);
488                 SSVAL(cli->outbuf,smb_tid,cli->cnum);
489                 cli_setup_packet(cli);
490
491                 SSVAL(cli->outbuf, smb_vwv0, 0); /* find count? */
492                 SSVAL(cli->outbuf, smb_vwv1, attribute);
493
494                 p = smb_buf(cli->outbuf);
495                 *p++ = 4;
496                 fstrcpy(p, "");
497                 p += strlen(p) + 1;
498                 *p++ = 5;
499                 SSVAL(p, 0, 21);
500                 p += 2;
501                 memcpy(p,status,21);
502                 p += 21;
503                 
504                 cli_setup_bcc(cli, p);
505                 cli_send_smb(cli);
506                 if (!cli_receive_smb(cli)) {
507                         DEBUG(0,("Error closing search: %s\n",cli_errstr(cli)));
508                 }
509         }
510
511         for (p=dirlist,i=0;i<num_received;i++) {
512                 file_info finfo;
513                 p += interpret_short_filename(cli, p,&finfo);
514                 fn("\\", &finfo, Mask, state);
515         }
516
517         SAFE_FREE(dirlist);
518         return(num_received);
519 }
520
521 /****************************************************************************
522  Do a directory listing, calling fn on each file found.
523  This auto-switches between old and new style.
524 ****************************************************************************/
525
526 int cli_list(struct cli_state *cli,const char *Mask,uint16 attribute, 
527              void (*fn)(const char *, file_info *, const char *, void *), void *state)
528 {
529         if (cli->protocol <= PROTOCOL_LANMAN1)
530                 return cli_list_old(cli, Mask, attribute, fn, state);
531         return cli_list_new(cli, Mask, attribute, fn, state);
532 }