Initial import
[samba] / source / 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         int max_matches = 1366;
173         int info_level;
174         char *p, *p2;
175         pstring mask;
176         file_info finfo;
177         int i;
178         char *tdl, *dirlist = NULL;
179         int dirlist_len = 0;
180         int total_received = -1;
181         BOOL First = True;
182         int ff_searchcount=0;
183         int ff_eos=0;
184         int ff_lastname=0;
185         int ff_dir_handle=0;
186         int loop_count = 0;
187         char *rparam=NULL, *rdata=NULL;
188         unsigned int param_len, data_len;       
189         uint16 setup;
190         pstring param;
191         const char *mnt;
192         uint32 resume_key = 0;
193         uint32 last_name_raw_len = 0;
194         DATA_BLOB last_name_raw = data_blob(NULL, 2*sizeof(pstring));
195
196         /* NT uses 260, OS/2 uses 2. Both accept 1. */
197         info_level = (cli->capabilities&CAP_NT_SMBS)?260:1;
198         
199         /* when getting a directory listing from a 2k dfs root share, 
200            we have to include the full path (\server\share\mask) here */
201            
202         if ( cli->dfsroot )
203                 pstr_sprintf( mask, "\\%s\\%s\\%s", cli->desthost, cli->share, Mask );
204         else
205                 pstrcpy(mask,Mask);
206         
207         while (ff_eos == 0) {
208                 loop_count++;
209                 if (loop_count > 200) {
210                         DEBUG(0,("Error: Looping in FIND_NEXT??\n"));
211                         break;
212                 }
213
214                 if (First) {
215                         setup = TRANSACT2_FINDFIRST;
216                         SSVAL(param,0,attribute); /* attribute */
217                         SSVAL(param,2,max_matches); /* max count */
218                         SSVAL(param,4,(FLAG_TRANS2_FIND_REQUIRE_RESUME|FLAG_TRANS2_FIND_CLOSE_IF_END)); /* resume required + close on end */
219                         SSVAL(param,6,info_level); 
220                         SIVAL(param,8,0);
221                         p = param+12;
222                         p += clistr_push(cli, param+12, mask, sizeof(param)-12, 
223                                          STR_TERMINATE);
224                 } else {
225                         setup = TRANSACT2_FINDNEXT;
226                         SSVAL(param,0,ff_dir_handle);
227                         SSVAL(param,2,max_matches); /* max count */
228                         SSVAL(param,4,info_level); 
229                         /* For W2K servers serving out FAT filesystems we *must* set the
230                            resume key. If it's not FAT then it's returned as zero. */
231                         SIVAL(param,6,resume_key); /* ff_resume_key */
232                         /* NB. *DON'T* use continue here. If you do it seems that W2K and bretheren
233                            can miss filenames. Use last filename continue instead. JRA */
234                         SSVAL(param,10,(FLAG_TRANS2_FIND_REQUIRE_RESUME|FLAG_TRANS2_FIND_CLOSE_IF_END));        /* resume required + close on end */
235                         p = param+12;
236                         if (last_name_raw_len && (last_name_raw_len < (sizeof(param)-12))) {
237                                 memcpy(p, last_name_raw.data, last_name_raw_len);
238                                 p += last_name_raw_len;
239                         } else {
240                                 p += clistr_push(cli, param+12, mask, sizeof(param)-12, STR_TERMINATE);
241                         }
242                 }
243
244                 param_len = PTR_DIFF(p, param);
245
246                 if (!cli_send_trans(cli, SMBtrans2, 
247                                     NULL,                   /* Name */
248                                     -1, 0,                  /* fid, flags */
249                                     &setup, 1, 0,           /* setup, length, max */
250                                     param, param_len, 10,   /* param, length, max */
251                                     NULL, 0, 
252 #if 0
253                                     /* w2k value. */
254                                     MIN(16384,cli->max_xmit) /* data, length, max. */
255 #else
256                                     cli->max_xmit           /* data, length, max. */
257 #endif
258                                     )) {
259                         break;
260                 }
261
262                 if (!cli_receive_trans(cli, SMBtrans2, 
263                                        &rparam, &param_len,
264                                        &rdata, &data_len) &&
265                     cli_is_dos_error(cli)) {
266                         /* we need to work around a Win95 bug - sometimes
267                            it gives ERRSRV/ERRerror temprarily */
268                         uint8 eclass;
269                         uint32 ecode;
270
271                         SAFE_FREE(rdata);
272                         SAFE_FREE(rparam);
273
274                         cli_dos_error(cli, &eclass, &ecode);
275                         if (eclass != ERRSRV || ecode != ERRerror)
276                                 break;
277                         smb_msleep(100);
278                         continue;
279                 }
280
281                 if (cli_is_error(cli) || !rdata || !rparam) {
282                         SAFE_FREE(rdata);
283                         SAFE_FREE(rparam);
284                         break;
285                 }
286
287                 if (total_received == -1)
288                         total_received = 0;
289
290                 /* parse out some important return info */
291                 p = rparam;
292                 if (First) {
293                         ff_dir_handle = SVAL(p,0);
294                         ff_searchcount = SVAL(p,2);
295                         ff_eos = SVAL(p,4);
296                         ff_lastname = SVAL(p,8);
297                 } else {
298                         ff_searchcount = SVAL(p,0);
299                         ff_eos = SVAL(p,2);
300                         ff_lastname = SVAL(p,6);
301                 }
302
303                 if (ff_searchcount == 0) {
304                         SAFE_FREE(rdata);
305                         SAFE_FREE(rparam);
306                         break;
307                 }
308
309                 /* point to the data bytes */
310                 p = rdata;
311
312                 /* we might need the lastname for continuations */
313                 for (p2=p,i=0;i<ff_searchcount;i++) {
314                         if ((info_level == 260) && (i == ff_searchcount-1)) {
315                                 /* Last entry - fixup the last offset length. */
316                                 SIVAL(p2,0,PTR_DIFF((rdata + data_len),p2));
317                         }
318                         p2 += interpret_long_filename(cli,info_level,p2,&finfo,
319                                                         &resume_key,&last_name_raw,&last_name_raw_len);
320
321                         if (!First && *mask && strcsequal(finfo.name, mask)) {
322                                 DEBUG(0,("Error: Looping in FIND_NEXT as name %s has already been seen?\n",
323                                         finfo.name));
324                                 ff_eos = 1;
325                                 break;
326                         }
327                 }
328
329                 if (ff_lastname > 0) {
330                         pstrcpy(mask, finfo.name);
331                 } else {
332                         pstrcpy(mask,"");
333                 }
334
335                 /* grab the data for later use */
336                 /* and add them to the dirlist pool */
337                 tdl = SMB_REALLOC(dirlist,dirlist_len + data_len);
338
339                 if (!tdl) {
340                         DEBUG(0,("cli_list_new: Failed to expand dirlist\n"));
341                         SAFE_FREE(rdata);
342                         SAFE_FREE(rparam);
343                         break;
344                 } else {
345                         dirlist = tdl;
346                 }
347
348                 memcpy(dirlist+dirlist_len,p,data_len);
349                 dirlist_len += data_len;
350
351                 total_received += ff_searchcount;
352
353                 SAFE_FREE(rdata);
354                 SAFE_FREE(rparam);
355
356                 DEBUG(3,("received %d entries (eos=%d)\n",
357                          ff_searchcount,ff_eos));
358
359                 if (ff_searchcount > 0)
360                         loop_count = 0;
361
362                 First = False;
363         }
364
365         mnt = cli_cm_get_mntpoint( cli );
366
367         for (p=dirlist,i=0;i<total_received;i++) {
368                 p += interpret_long_filename(cli,info_level,p,&finfo,NULL,NULL,NULL);
369                 fn( mnt,&finfo, Mask, state );
370         }
371
372         /* free up the dirlist buffer and last name raw blob */
373         SAFE_FREE(dirlist);
374         data_blob_free(&last_name_raw);
375         return(total_received);
376 }
377
378 /****************************************************************************
379  Interpret a short filename structure.
380  The length of the structure is returned.
381 ****************************************************************************/
382
383 static int interpret_short_filename(struct cli_state *cli, char *p,file_info *finfo)
384 {
385
386         *finfo = def_finfo;
387
388         finfo->mode = CVAL(p,21);
389         
390         /* this date is converted to GMT by make_unix_date */
391         finfo->ctime = cli_make_unix_date(cli, p+22);
392         finfo->mtime = finfo->atime = finfo->ctime;
393         finfo->size = IVAL(p,26);
394         clistr_pull(cli, finfo->name, p+30, sizeof(finfo->name), 12, STR_ASCII);
395         if (strcmp(finfo->name, "..") && strcmp(finfo->name, ".")) {
396                 strncpy(finfo->short_name,finfo->name, sizeof(finfo->short_name)-1);
397                 finfo->short_name[sizeof(finfo->short_name)-1] = '\0';
398         }
399
400         return(DIR_STRUCT_SIZE);
401 }
402
403
404 /****************************************************************************
405  Do a directory listing, calling fn on each file found.
406  this uses the old SMBsearch interface. It is needed for testing Samba,
407  but should otherwise not be used.
408 ****************************************************************************/
409
410 int cli_list_old(struct cli_state *cli,const char *Mask,uint16 attribute, 
411                  void (*fn)(const char *, file_info *, const char *, void *), void *state)
412 {
413         char *p;
414         int received = 0;
415         BOOL first = True;
416         char status[21];
417         int num_asked = (cli->max_xmit - 100)/DIR_STRUCT_SIZE;
418         int num_received = 0;
419         int i;
420         char *tdl, *dirlist = NULL;
421         pstring mask;
422         
423         ZERO_ARRAY(status);
424
425         pstrcpy(mask,Mask);
426   
427         while (1) {
428                 memset(cli->outbuf,'\0',smb_size);
429                 memset(cli->inbuf,'\0',smb_size);
430
431                 set_message(cli->outbuf,2,0,True);
432
433                 SCVAL(cli->outbuf,smb_com,SMBsearch);
434
435                 SSVAL(cli->outbuf,smb_tid,cli->cnum);
436                 cli_setup_packet(cli);
437
438                 SSVAL(cli->outbuf,smb_vwv0,num_asked);
439                 SSVAL(cli->outbuf,smb_vwv1,attribute);
440   
441                 p = smb_buf(cli->outbuf);
442                 *p++ = 4;
443       
444                 p += clistr_push(cli, p, first?mask:"", -1, STR_TERMINATE);
445                 *p++ = 5;
446                 if (first) {
447                         SSVAL(p,0,0);
448                         p += 2;
449                 } else {
450                         SSVAL(p,0,21);
451                         p += 2;
452                         memcpy(p,status,21);
453                         p += 21;
454                 }
455
456                 cli_setup_bcc(cli, p);
457                 cli_send_smb(cli);
458                 if (!cli_receive_smb(cli)) break;
459
460                 received = SVAL(cli->inbuf,smb_vwv0);
461                 if (received <= 0) break;
462
463                 first = False;
464
465                 tdl = SMB_REALLOC(dirlist,(num_received + received)*DIR_STRUCT_SIZE);
466
467                 if (!tdl) {
468                         DEBUG(0,("cli_list_old: failed to expand dirlist"));
469                         SAFE_FREE(dirlist);
470                         return 0;
471                 }
472                 else dirlist = tdl;
473
474                 p = smb_buf(cli->inbuf) + 3;
475
476                 memcpy(dirlist+num_received*DIR_STRUCT_SIZE,
477                        p,received*DIR_STRUCT_SIZE);
478                 
479                 memcpy(status,p + ((received-1)*DIR_STRUCT_SIZE),21);
480                 
481                 num_received += received;
482                 
483                 if (cli_is_error(cli)) break;
484         }
485
486         if (!first) {
487                 memset(cli->outbuf,'\0',smb_size);
488                 memset(cli->inbuf,'\0',smb_size);
489
490                 set_message(cli->outbuf,2,0,True);
491                 SCVAL(cli->outbuf,smb_com,SMBfclose);
492                 SSVAL(cli->outbuf,smb_tid,cli->cnum);
493                 cli_setup_packet(cli);
494
495                 SSVAL(cli->outbuf, smb_vwv0, 0); /* find count? */
496                 SSVAL(cli->outbuf, smb_vwv1, attribute);
497
498                 p = smb_buf(cli->outbuf);
499                 *p++ = 4;
500                 fstrcpy(p, "");
501                 p += strlen(p) + 1;
502                 *p++ = 5;
503                 SSVAL(p, 0, 21);
504                 p += 2;
505                 memcpy(p,status,21);
506                 p += 21;
507                 
508                 cli_setup_bcc(cli, p);
509                 cli_send_smb(cli);
510                 if (!cli_receive_smb(cli)) {
511                         DEBUG(0,("Error closing search: %s\n",cli_errstr(cli)));
512                 }
513         }
514
515         for (p=dirlist,i=0;i<num_received;i++) {
516                 file_info finfo;
517                 p += interpret_short_filename(cli, p,&finfo);
518                 fn("\\", &finfo, Mask, state);
519         }
520
521         SAFE_FREE(dirlist);
522         return(num_received);
523 }
524
525 /****************************************************************************
526  Do a directory listing, calling fn on each file found.
527  This auto-switches between old and new style.
528 ****************************************************************************/
529
530 int cli_list(struct cli_state *cli,const char *Mask,uint16 attribute, 
531              void (*fn)(const char *, file_info *, const char *, void *), void *state)
532 {
533         if (cli->protocol <= PROTOCOL_LANMAN1)
534                 return cli_list_old(cli, Mask, attribute, fn, state);
535         return cli_list_new(cli, Mask, attribute, fn, state);
536 }