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