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