2 Unix SMB/CIFS implementation.
3 client directory list routines
4 Copyright (C) Andrew Tridgell 1994-1998
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.
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.
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.
26 /****************************************************************************
27 interpret a long filename structure - this is mostly guesses at the moment
28 The length of the structure is returned
29 The structure of a long filename depends on the info level. 260 is used
30 by NT and 2 is used by OS/2
31 ****************************************************************************/
32 static int interpret_long_filename(struct cli_state *cli,
33 int level,char *p,file_info *finfo)
35 extern file_info def_finfo;
40 if (!finfo) finfo = &finfo2;
42 memcpy(finfo,&def_finfo,sizeof(*finfo));
46 case 1: /* OS/2 understands this */
47 /* these dates are converted to GMT by
49 finfo->ctime = make_unix_date2(p+4);
50 finfo->atime = make_unix_date2(p+8);
51 finfo->mtime = make_unix_date2(p+12);
52 finfo->size = IVAL(p,16);
53 finfo->mode = CVAL(p,24);
56 p += clistr_align_in(cli, p, 0);
57 /* the len+2 below looks strange but it is
58 important to cope with the differences
59 between win2000 and win9x for this call
61 p += clistr_pull(cli, finfo->name, p,
65 return PTR_DIFF(p, base);
67 case 2: /* this is what OS/2 uses mostly */
68 /* these dates are converted to GMT by
70 finfo->ctime = make_unix_date2(p+4);
71 finfo->atime = make_unix_date2(p+8);
72 finfo->mtime = make_unix_date2(p+12);
73 finfo->size = IVAL(p,16);
74 finfo->mode = CVAL(p,24);
77 /* check for unisys! */
78 p += clistr_pull(cli, finfo->name, p,
82 return PTR_DIFF(p, base) + 1;
84 case 260: /* NT uses this, but also accepts 2 */
87 p += 4; /* next entry offset */
88 p += 4; /* fileindex */
90 /* these dates appear to arrive in a
91 weird way. It seems to be localtime
92 plus the serverzone given in the
93 initial connect. This is GMT when
94 DST is not in effect and one hour
95 from GMT otherwise. Can this really
98 I suppose this could be called
99 kludge-GMT. Is is the GMT you get
100 by using the current DST setting on
101 a different localtime. It will be
102 cheap to calculate, I suppose, as
103 no DST tables will be needed */
105 finfo->ctime = interpret_long_date(p); p += 8;
106 finfo->atime = interpret_long_date(p); p += 8;
107 finfo->mtime = interpret_long_date(p); p += 8; p += 8;
108 finfo->size = IVAL(p,0); p += 8;
109 p += 8; /* alloc size */
110 finfo->mode = CVAL(p,0); p += 4;
111 namelen = IVAL(p,0); p += 4;
112 p += 4; /* EA size */
116 /* stupid NT bugs. grr */
118 if (p[1] == 0 && namelen > 1) flags |= STR_UNICODE;
119 clistr_pull(cli, finfo->short_name, p,
120 sizeof(finfo->short_name),
123 p += 24; /* short name? */
124 clistr_pull(cli, finfo->name, p,
127 return SVAL(base, 0);
131 DEBUG(1,("Unknown long filename format %d\n",level));
136 /****************************************************************************
137 do a directory listing, calling fn on each file found
138 ****************************************************************************/
139 int cli_list_new(struct cli_state *cli,const char *Mask,uint16 attribute,
140 void (*fn)(file_info *, const char *, void *), void *state)
142 int max_matches = 512;
148 char *tdl, *dirlist = NULL;
150 int total_received = -1;
152 int ff_searchcount=0;
157 char *rparam=NULL, *rdata=NULL;
158 int param_len, data_len;
162 /* NT uses 260, OS/2 uses 2. Both accept 1. */
163 info_level = (cli->capabilities&CAP_NT_SMBS)?260:1;
167 while (ff_eos == 0) {
169 if (loop_count > 200) {
170 DEBUG(0,("Error: Looping in FIND_NEXT??\n"));
175 setup = TRANSACT2_FINDFIRST;
176 SSVAL(param,0,attribute); /* attribute */
177 SSVAL(param,2,max_matches); /* max count */
178 SSVAL(param,4,4+2); /* resume required + close on end */
179 SSVAL(param,6,info_level);
182 p += clistr_push(cli, param+12, mask, -1,
185 setup = TRANSACT2_FINDNEXT;
186 SSVAL(param,0,ff_dir_handle);
187 SSVAL(param,2,max_matches); /* max count */
188 SSVAL(param,4,info_level);
189 SIVAL(param,6,0); /* ff_resume_key */
190 SSVAL(param,10,8+4+2); /* continue + resume required + close on end */
192 p += clistr_push(cli, param+12, mask, -1,
196 param_len = PTR_DIFF(p, param);
198 if (!cli_send_trans(cli, SMBtrans2,
200 -1, 0, /* fid, flags */
201 &setup, 1, 0, /* setup, length, max */
202 param, param_len, 10, /* param, length, max */
204 cli->max_xmit /* data, length, max */
209 if (!cli_receive_trans(cli, SMBtrans2,
211 &rdata, &data_len) &&
212 cli_is_dos_error(cli)) {
213 /* we need to work around a Win95 bug - sometimes
214 it gives ERRSRV/ERRerror temprarily */
217 cli_dos_error(cli, &eclass, &ecode);
218 if (eclass != ERRSRV || ecode != ERRerror) break;
223 if (cli_is_error(cli) || !rdata || !rparam)
226 if (total_received == -1) total_received = 0;
228 /* parse out some important return info */
231 ff_dir_handle = SVAL(p,0);
232 ff_searchcount = SVAL(p,2);
234 ff_lastname = SVAL(p,8);
236 ff_searchcount = SVAL(p,0);
238 ff_lastname = SVAL(p,6);
241 if (ff_searchcount == 0)
244 /* point to the data bytes */
247 /* we might need the lastname for continuations */
248 if (ff_lastname > 0) {
252 clistr_pull(cli, mask, p+ff_lastname,
254 data_len-ff_lastname,
258 clistr_pull(cli, mask, p+ff_lastname+1,
268 /* and add them to the dirlist pool */
269 tdl = Realloc(dirlist,dirlist_len + data_len);
272 DEBUG(0,("cli_list_new: Failed to expand dirlist\n"));
277 /* put in a length for the last entry, to ensure we can chain entries
278 into the next packet */
279 for (p2=p,i=0;i<(ff_searchcount-1);i++)
280 p2 += interpret_long_filename(cli,info_level,p2,NULL);
281 SSVAL(p2,0,data_len - PTR_DIFF(p2,p));
283 /* grab the data for later use */
284 memcpy(dirlist+dirlist_len,p,data_len);
285 dirlist_len += data_len;
287 total_received += ff_searchcount;
292 DEBUG(3,("received %d entries (eos=%d)\n",
293 ff_searchcount,ff_eos));
295 if (ff_searchcount > 0) loop_count = 0;
300 for (p=dirlist,i=0;i<total_received;i++) {
301 p += interpret_long_filename(cli,info_level,p,&finfo);
302 fn(&finfo, Mask, state);
305 /* free up the dirlist buffer */
307 return(total_received);
312 /****************************************************************************
313 interpret a short filename structure
314 The length of the structure is returned
315 ****************************************************************************/
316 static int interpret_short_filename(struct cli_state *cli, char *p,file_info *finfo)
318 extern file_info def_finfo;
322 finfo->mode = CVAL(p,21);
324 /* this date is converted to GMT by make_unix_date */
325 finfo->ctime = make_unix_date(p+22);
326 finfo->mtime = finfo->atime = finfo->ctime;
327 finfo->size = IVAL(p,26);
328 clistr_pull(cli, finfo->name, p+30, sizeof(finfo->name), 12, STR_ASCII);
329 if (strcmp(finfo->name, "..") && strcmp(finfo->name, "."))
330 fstrcpy(finfo->short_name,finfo->name);
332 return(DIR_STRUCT_SIZE);
336 /****************************************************************************
337 do a directory listing, calling fn on each file found
338 this uses the old SMBsearch interface. It is needed for testing Samba,
339 but should otherwise not be used
340 ****************************************************************************/
341 int cli_list_old(struct cli_state *cli,const char *Mask,uint16 attribute,
342 void (*fn)(file_info *, const char *, void *), void *state)
348 int num_asked = (cli->max_xmit - 100)/DIR_STRUCT_SIZE;
349 int num_received = 0;
351 char *tdl, *dirlist = NULL;
359 memset(cli->outbuf,'\0',smb_size);
360 memset(cli->inbuf,'\0',smb_size);
362 set_message(cli->outbuf,2,0,True);
364 SCVAL(cli->outbuf,smb_com,SMBsearch);
366 SSVAL(cli->outbuf,smb_tid,cli->cnum);
367 cli_setup_packet(cli);
369 SSVAL(cli->outbuf,smb_vwv0,num_asked);
370 SSVAL(cli->outbuf,smb_vwv1,attribute);
372 p = smb_buf(cli->outbuf);
375 p += clistr_push(cli, p, first?mask:"", -1, STR_TERMINATE);
387 cli_setup_bcc(cli, p);
389 if (!cli_receive_smb(cli)) break;
391 received = SVAL(cli->inbuf,smb_vwv0);
392 if (received <= 0) break;
396 tdl = Realloc(dirlist,(num_received + received)*DIR_STRUCT_SIZE);
399 DEBUG(0,("cli_list_old: failed to expand dirlist"));
405 p = smb_buf(cli->inbuf) + 3;
407 memcpy(dirlist+num_received*DIR_STRUCT_SIZE,
408 p,received*DIR_STRUCT_SIZE);
410 memcpy(status,p + ((received-1)*DIR_STRUCT_SIZE),21);
412 num_received += received;
414 if (cli_is_error(cli)) break;
418 memset(cli->outbuf,'\0',smb_size);
419 memset(cli->inbuf,'\0',smb_size);
421 set_message(cli->outbuf,2,0,True);
422 SCVAL(cli->outbuf,smb_com,SMBfclose);
423 SSVAL(cli->outbuf,smb_tid,cli->cnum);
424 cli_setup_packet(cli);
426 SSVAL(cli->outbuf, smb_vwv0, 0); /* find count? */
427 SSVAL(cli->outbuf, smb_vwv1, attribute);
429 p = smb_buf(cli->outbuf);
439 cli_setup_bcc(cli, p);
441 if (!cli_receive_smb(cli)) {
442 DEBUG(0,("Error closing search: %s\n",cli_errstr(cli)));
446 for (p=dirlist,i=0;i<num_received;i++) {
448 p += interpret_short_filename(cli, p,&finfo);
449 fn(&finfo, Mask, state);
453 return(num_received);
457 /****************************************************************************
458 do a directory listing, calling fn on each file found
459 this auto-switches between old and new style
460 ****************************************************************************/
461 int cli_list(struct cli_state *cli,const char *Mask,uint16 attribute,
462 void (*fn)(file_info *, const char *, void *), void *state)
464 if (cli->protocol <= PROTOCOL_LANMAN1) {
465 return cli_list_old(cli, Mask, attribute, fn, state);
467 return cli_list_new(cli, Mask, attribute, fn, state);