2 Unix SMB/Netbios implementation.
4 client directory list routines
5 Copyright (C) Andrew Tridgell 1994-1998
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27 /****************************************************************************
28 interpret a long filename structure - this is mostly guesses at the moment
29 The length of the structure is returned
30 The structure of a long filename depends on the info level. 260 is used
31 by NT and 2 is used by OS/2
32 ****************************************************************************/
33 static int interpret_long_filename(struct cli_state *cli,
34 int level,char *p,file_info *finfo)
36 extern file_info def_finfo;
41 if (!finfo) finfo = &finfo2;
43 memcpy(finfo,&def_finfo,sizeof(*finfo));
47 case 1: /* OS/2 understands this */
48 /* these dates are converted to GMT by
50 finfo->ctime = make_unix_date2(p+4);
51 finfo->atime = make_unix_date2(p+8);
52 finfo->mtime = make_unix_date2(p+12);
53 finfo->size = IVAL(p,16);
54 finfo->mode = CVAL(p,24);
57 p += clistr_align_in(cli, p, 0);
58 p += clistr_pull(cli, finfo->name, p,
62 return PTR_DIFF(p, base);
64 case 2: /* this is what OS/2 uses mostly */
65 /* these dates are converted to GMT by
67 finfo->ctime = make_unix_date2(p+4);
68 finfo->atime = make_unix_date2(p+8);
69 finfo->mtime = make_unix_date2(p+12);
70 finfo->size = IVAL(p,16);
71 finfo->mode = CVAL(p,24);
74 /* check for unisys! */
75 p += clistr_pull(cli, finfo->name, p,
79 return PTR_DIFF(p, base) + 1;
81 case 260: /* NT uses this, but also accepts 2 */
84 p += 4; /* next entry offset */
85 p += 4; /* fileindex */
87 /* these dates appear to arrive in a
88 weird way. It seems to be localtime
89 plus the serverzone given in the
90 initial connect. This is GMT when
91 DST is not in effect and one hour
92 from GMT otherwise. Can this really
95 I suppose this could be called
96 kludge-GMT. Is is the GMT you get
97 by using the current DST setting on
98 a different localtime. It will be
99 cheap to calculate, I suppose, as
100 no DST tables will be needed */
102 finfo->ctime = interpret_long_date(p); p += 8;
103 finfo->atime = interpret_long_date(p); p += 8;
104 finfo->mtime = interpret_long_date(p); p += 8; p += 8;
105 finfo->size = IVAL(p,0); p += 8;
106 p += 8; /* alloc size */
107 finfo->mode = CVAL(p,0); p += 4;
108 namelen = IVAL(p,0); p += 4;
109 p += 4; /* EA size */
113 /* stupid NT bugs. grr */
115 if (p[1] == 0 && namelen > 1) flags |= STR_UNICODE;
116 clistr_pull(cli, finfo->short_name, p,
117 sizeof(finfo->short_name),
120 p += 24; /* short name? */
121 clistr_pull(cli, finfo->name, p,
124 return SVAL(base, 0);
128 DEBUG(1,("Unknown long filename format %d\n",level));
133 /****************************************************************************
134 do a directory listing, calling fn on each file found
135 ****************************************************************************/
136 int cli_list_new(struct cli_state *cli,const char *Mask,uint16 attribute,
137 void (*fn)(file_info *, const char *, void *), void *state)
139 int max_matches = 512;
145 char *tdl, *dirlist = NULL;
147 int total_received = -1;
149 int ff_searchcount=0;
154 char *rparam=NULL, *rdata=NULL;
155 int param_len, data_len;
159 /* NT uses 260, OS/2 uses 2. Both accept 1. */
160 info_level = (cli->capabilities&CAP_NT_SMBS)?260:1;
164 while (ff_eos == 0) {
166 if (loop_count > 200) {
167 DEBUG(0,("Error: Looping in FIND_NEXT??\n"));
172 setup = TRANSACT2_FINDFIRST;
173 SSVAL(param,0,attribute); /* attribute */
174 SSVAL(param,2,max_matches); /* max count */
175 SSVAL(param,4,4+2); /* resume required + close on end */
176 SSVAL(param,6,info_level);
179 p += clistr_push(cli, param+12, mask, -1,
182 setup = TRANSACT2_FINDNEXT;
183 SSVAL(param,0,ff_dir_handle);
184 SSVAL(param,2,max_matches); /* max count */
185 SSVAL(param,4,info_level);
186 SIVAL(param,6,0); /* ff_resume_key */
187 SSVAL(param,10,8+4+2); /* continue + resume required + close on end */
189 p += clistr_push(cli, param+12, mask, -1,
193 param_len = PTR_DIFF(p, param);
195 if (!cli_send_trans(cli, SMBtrans2,
197 -1, 0, /* fid, flags */
198 &setup, 1, 0, /* setup, length, max */
199 param, param_len, 10, /* param, length, max */
201 cli->max_xmit /* data, length, max */
206 if (!cli_receive_trans(cli, SMBtrans2,
208 &rdata, &data_len) &&
209 cli_is_dos_error(cli)) {
210 /* we need to work around a Win95 bug - sometimes
211 it gives ERRSRV/ERRerror temprarily */
214 cli_dos_error(cli, &eclass, &ecode);
215 if (eclass != ERRSRV || ecode != ERRerror) break;
220 if (cli_is_error(cli) || !rdata || !rparam)
223 if (total_received == -1) total_received = 0;
225 /* parse out some important return info */
228 ff_dir_handle = SVAL(p,0);
229 ff_searchcount = SVAL(p,2);
231 ff_lastname = SVAL(p,8);
233 ff_searchcount = SVAL(p,0);
235 ff_lastname = SVAL(p,6);
238 if (ff_searchcount == 0)
241 /* point to the data bytes */
244 /* we might need the lastname for continuations */
245 if (ff_lastname > 0) {
249 clistr_pull(cli, mask, p+ff_lastname,
251 data_len-ff_lastname,
255 clistr_pull(cli, mask, p+ff_lastname+1,
265 /* and add them to the dirlist pool */
266 tdl = Realloc(dirlist,dirlist_len + data_len);
269 DEBUG(0,("cli_list_new: Failed to expand dirlist\n"));
274 /* put in a length for the last entry, to ensure we can chain entries
275 into the next packet */
276 for (p2=p,i=0;i<(ff_searchcount-1);i++)
277 p2 += interpret_long_filename(cli,info_level,p2,NULL);
278 SSVAL(p2,0,data_len - PTR_DIFF(p2,p));
280 /* grab the data for later use */
281 memcpy(dirlist+dirlist_len,p,data_len);
282 dirlist_len += data_len;
284 total_received += ff_searchcount;
286 if (rdata) free(rdata); rdata = NULL;
287 if (rparam) free(rparam); rparam = NULL;
289 DEBUG(3,("received %d entries (eos=%d)\n",
290 ff_searchcount,ff_eos));
292 if (ff_searchcount > 0) loop_count = 0;
297 for (p=dirlist,i=0;i<total_received;i++) {
298 p += interpret_long_filename(cli,info_level,p,&finfo);
299 fn(&finfo, Mask, state);
302 /* free up the dirlist buffer */
303 if (dirlist) free(dirlist);
304 return(total_received);
309 /****************************************************************************
310 interpret a short filename structure
311 The length of the structure is returned
312 ****************************************************************************/
313 static int interpret_short_filename(struct cli_state *cli, char *p,file_info *finfo)
315 extern file_info def_finfo;
319 finfo->mode = CVAL(p,21);
321 /* this date is converted to GMT by make_unix_date */
322 finfo->ctime = make_unix_date(p+22);
323 finfo->mtime = finfo->atime = finfo->ctime;
324 finfo->size = IVAL(p,26);
325 clistr_pull(cli, finfo->name, p+30, sizeof(finfo->name), 12, STR_ASCII);
326 if (strcmp(finfo->name, "..") && strcmp(finfo->name, "."))
327 fstrcpy(finfo->short_name,finfo->name);
329 return(DIR_STRUCT_SIZE);
333 /****************************************************************************
334 do a directory listing, calling fn on each file found
335 this uses the old SMBsearch interface. It is needed for testing Samba,
336 but should otherwise not be used
337 ****************************************************************************/
338 int cli_list_old(struct cli_state *cli,const char *Mask,uint16 attribute,
339 void (*fn)(file_info *, const char *, void *), void *state)
345 int num_asked = (cli->max_xmit - 100)/DIR_STRUCT_SIZE;
346 int num_received = 0;
348 char *tdl, *dirlist = NULL;
356 memset(cli->outbuf,'\0',smb_size);
357 memset(cli->inbuf,'\0',smb_size);
359 set_message(cli->outbuf,2,0,True);
361 CVAL(cli->outbuf,smb_com) = SMBsearch;
363 SSVAL(cli->outbuf,smb_tid,cli->cnum);
364 cli_setup_packet(cli);
366 SSVAL(cli->outbuf,smb_vwv0,num_asked);
367 SSVAL(cli->outbuf,smb_vwv1,attribute);
369 p = smb_buf(cli->outbuf);
372 p += clistr_push(cli, p, first?mask:"", -1, STR_TERMINATE);
384 cli_setup_bcc(cli, p);
386 if (!cli_receive_smb(cli)) break;
388 received = SVAL(cli->inbuf,smb_vwv0);
389 if (received <= 0) break;
393 tdl = Realloc(dirlist,(num_received + received)*DIR_STRUCT_SIZE);
396 DEBUG(0,("cli_list_old: failed to expand dirlist"));
397 if (dirlist) free(dirlist);
402 p = smb_buf(cli->inbuf) + 3;
404 memcpy(dirlist+num_received*DIR_STRUCT_SIZE,
405 p,received*DIR_STRUCT_SIZE);
407 memcpy(status,p + ((received-1)*DIR_STRUCT_SIZE),21);
409 num_received += received;
411 if (CVAL(cli->inbuf,smb_rcls) != 0) break;
415 memset(cli->outbuf,'\0',smb_size);
416 memset(cli->inbuf,'\0',smb_size);
418 set_message(cli->outbuf,2,0,True);
419 CVAL(cli->outbuf,smb_com) = SMBfclose;
420 SSVAL(cli->outbuf,smb_tid,cli->cnum);
421 cli_setup_packet(cli);
423 SSVAL(cli->outbuf, smb_vwv0, 0); /* find count? */
424 SSVAL(cli->outbuf, smb_vwv1, attribute);
426 p = smb_buf(cli->outbuf);
436 cli_setup_bcc(cli, p);
438 if (!cli_receive_smb(cli)) {
439 DEBUG(0,("Error closing search: %s\n",smb_errstr(cli->inbuf)));
443 for (p=dirlist,i=0;i<num_received;i++) {
445 p += interpret_short_filename(cli, p,&finfo);
446 fn(&finfo, Mask, state);
449 if (dirlist) free(dirlist);
450 return(num_received);
454 /****************************************************************************
455 do a directory listing, calling fn on each file found
456 this auto-switches between old and new style
457 ****************************************************************************/
458 int cli_list(struct cli_state *cli,const char *Mask,uint16 attribute,
459 void (*fn)(file_info *, const char *, void *), void *state)
461 if (cli->protocol <= PROTOCOL_LANMAN1) {
462 return cli_list_old(cli, Mask, attribute, fn, state);
464 return cli_list_new(cli, Mask, attribute, fn, state);