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