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 p += clistr_pull(cli, finfo->name, p,
78 return PTR_DIFF(p, base) + 1;
80 case 260: /* NT uses this, but also accepts 2 */
83 p += 4; /* next entry offset */
84 p += 4; /* fileindex */
86 /* these dates appear to arrive in a
87 weird way. It seems to be localtime
88 plus the serverzone given in the
89 initial connect. This is GMT when
90 DST is not in effect and one hour
91 from GMT otherwise. Can this really
94 I suppose this could be called
95 kludge-GMT. Is is the GMT you get
96 by using the current DST setting on
97 a different localtime. It will be
98 cheap to calculate, I suppose, as
99 no DST tables will be needed */
101 finfo->ctime = interpret_long_date(p); p += 8;
102 finfo->atime = interpret_long_date(p); p += 8;
103 finfo->mtime = interpret_long_date(p); p += 8; p += 8;
104 finfo->size = IVAL(p,0); p += 8;
105 p += 8; /* alloc size */
106 finfo->mode = CVAL(p,0); p += 4;
107 namelen = IVAL(p,0); p += 4;
108 p += 4; /* EA size */
112 /* stupid NT bugs. grr */
114 if (p[1] == 0 && namelen > 1) flags |= STR_UNICODE;
115 clistr_pull(cli, finfo->short_name, p,
116 sizeof(finfo->short_name),
119 p += 24; /* short name? */
120 clistr_pull(cli, finfo->name, p,
123 return SVAL(base, 0);
127 DEBUG(1,("Unknown long filename format %d\n",level));
132 /****************************************************************************
133 do a directory listing, calling fn on each file found
134 ****************************************************************************/
135 int cli_list_new(struct cli_state *cli,const char *Mask,uint16 attribute,
136 void (*fn)(file_info *, const char *, void *), void *state)
138 int max_matches = 512;
144 char *tdl, *dirlist = NULL;
146 int total_received = -1;
148 int ff_searchcount=0;
153 char *rparam=NULL, *rdata=NULL;
154 int param_len, data_len;
158 /* NT uses 260, OS/2 uses 2. Both accept 1. */
159 info_level = (cli->capabilities&CAP_NT_SMBS)?260:1;
163 while (ff_eos == 0) {
165 if (loop_count > 200) {
166 DEBUG(0,("Error: Looping in FIND_NEXT??\n"));
171 setup = TRANSACT2_FINDFIRST;
172 SSVAL(param,0,attribute); /* attribute */
173 SSVAL(param,2,max_matches); /* max count */
174 SSVAL(param,4,4+2); /* resume required + close on end */
175 SSVAL(param,6,info_level);
178 p += clistr_push(cli, param+12, mask, -1,
181 setup = TRANSACT2_FINDNEXT;
182 SSVAL(param,0,ff_dir_handle);
183 SSVAL(param,2,max_matches); /* max count */
184 SSVAL(param,4,info_level);
185 SIVAL(param,6,0); /* ff_resume_key */
186 SSVAL(param,10,8+4+2); /* continue + resume required + close on end */
188 p += clistr_push(cli, param+12, mask, -1,
192 param_len = PTR_DIFF(p, param);
194 if (!cli_send_trans(cli, SMBtrans2,
196 -1, 0, /* fid, flags */
197 &setup, 1, 0, /* setup, length, max */
198 param, param_len, 10, /* param, length, max */
200 cli->max_xmit /* data, length, max */
205 if (!cli_receive_trans(cli, SMBtrans2,
207 &rdata, &data_len) &&
208 cli_is_dos_error(cli)) {
209 /* we need to work around a Win95 bug - sometimes
210 it gives ERRSRV/ERRerror temprarily */
213 cli_dos_error(cli, &eclass, &ecode);
214 if (eclass != ERRSRV || ecode != ERRerror) break;
219 if (cli_is_error(cli) || !rdata || !rparam)
222 if (total_received == -1) total_received = 0;
224 /* parse out some important return info */
227 ff_dir_handle = SVAL(p,0);
228 ff_searchcount = SVAL(p,2);
230 ff_lastname = SVAL(p,8);
232 ff_searchcount = SVAL(p,0);
234 ff_lastname = SVAL(p,6);
237 if (ff_searchcount == 0)
240 /* point to the data bytes */
243 /* we might need the lastname for continuations */
244 if (ff_lastname > 0) {
248 clistr_pull(cli, mask, p+ff_lastname,
250 data_len-ff_lastname,
254 clistr_pull(cli, mask, p+ff_lastname+1,
264 /* and add them to the dirlist pool */
265 tdl = Realloc(dirlist,dirlist_len + data_len);
268 DEBUG(0,("cli_list_new: Failed to expand dirlist\n"));
273 /* put in a length for the last entry, to ensure we can chain entries
274 into the next packet */
275 for (p2=p,i=0;i<(ff_searchcount-1);i++)
276 p2 += interpret_long_filename(cli,info_level,p2,NULL);
277 SSVAL(p2,0,data_len - PTR_DIFF(p2,p));
279 /* grab the data for later use */
280 memcpy(dirlist+dirlist_len,p,data_len);
281 dirlist_len += data_len;
283 total_received += ff_searchcount;
285 if (rdata) free(rdata); rdata = NULL;
286 if (rparam) free(rparam); rparam = NULL;
288 DEBUG(3,("received %d entries (eos=%d)\n",
289 ff_searchcount,ff_eos));
291 if (ff_searchcount > 0) loop_count = 0;
296 for (p=dirlist,i=0;i<total_received;i++) {
297 p += interpret_long_filename(cli,info_level,p,&finfo);
298 fn(&finfo, Mask, state);
301 /* free up the dirlist buffer */
302 if (dirlist) free(dirlist);
303 return(total_received);
308 /****************************************************************************
309 interpret a short filename structure
310 The length of the structure is returned
311 ****************************************************************************/
312 static int interpret_short_filename(struct cli_state *cli, char *p,file_info *finfo)
314 extern file_info def_finfo;
318 finfo->mode = CVAL(p,21);
320 /* this date is converted to GMT by make_unix_date */
321 finfo->ctime = make_unix_date(p+22);
322 finfo->mtime = finfo->atime = finfo->ctime;
323 finfo->size = IVAL(p,26);
324 clistr_pull(cli, finfo->name, p+30, sizeof(finfo->name), 12, STR_ASCII);
325 if (strcmp(finfo->name, "..") && strcmp(finfo->name, "."))
326 fstrcpy(finfo->short_name,finfo->name);
328 return(DIR_STRUCT_SIZE);
332 /****************************************************************************
333 do a directory listing, calling fn on each file found
334 this uses the old SMBsearch interface. It is needed for testing Samba,
335 but should otherwise not be used
336 ****************************************************************************/
337 int cli_list_old(struct cli_state *cli,const char *Mask,uint16 attribute,
338 void (*fn)(file_info *, const char *, void *), void *state)
344 int num_asked = (cli->max_xmit - 100)/DIR_STRUCT_SIZE;
345 int num_received = 0;
347 char *tdl, *dirlist = NULL;
355 memset(cli->outbuf,'\0',smb_size);
356 memset(cli->inbuf,'\0',smb_size);
358 set_message(cli->outbuf,2,0,True);
360 CVAL(cli->outbuf,smb_com) = SMBsearch;
362 SSVAL(cli->outbuf,smb_tid,cli->cnum);
363 cli_setup_packet(cli);
365 SSVAL(cli->outbuf,smb_vwv0,num_asked);
366 SSVAL(cli->outbuf,smb_vwv1,attribute);
368 p = smb_buf(cli->outbuf);
371 p += clistr_push(cli, p, first?mask:"", -1, STR_TERMINATE);
383 cli_setup_bcc(cli, p);
385 if (!cli_receive_smb(cli)) break;
387 received = SVAL(cli->inbuf,smb_vwv0);
388 if (received <= 0) break;
392 tdl = Realloc(dirlist,(num_received + received)*DIR_STRUCT_SIZE);
395 DEBUG(0,("cli_list_old: failed to expand dirlist"));
396 if (dirlist) free(dirlist);
401 p = smb_buf(cli->inbuf) + 3;
403 memcpy(dirlist+num_received*DIR_STRUCT_SIZE,
404 p,received*DIR_STRUCT_SIZE);
406 memcpy(status,p + ((received-1)*DIR_STRUCT_SIZE),21);
408 num_received += received;
410 if (CVAL(cli->inbuf,smb_rcls) != 0) break;
414 memset(cli->outbuf,'\0',smb_size);
415 memset(cli->inbuf,'\0',smb_size);
417 set_message(cli->outbuf,2,0,True);
418 CVAL(cli->outbuf,smb_com) = SMBfclose;
419 SSVAL(cli->outbuf,smb_tid,cli->cnum);
420 cli_setup_packet(cli);
422 SSVAL(cli->outbuf, smb_vwv0, 0); /* find count? */
423 SSVAL(cli->outbuf, smb_vwv1, attribute);
425 p = smb_buf(cli->outbuf);
435 cli_setup_bcc(cli, p);
437 if (!cli_receive_smb(cli)) {
438 DEBUG(0,("Error closing search: %s\n",smb_errstr(cli->inbuf)));
442 for (p=dirlist,i=0;i<num_received;i++) {
444 p += interpret_short_filename(cli, p,&finfo);
445 fn(&finfo, Mask, state);
448 if (dirlist) free(dirlist);
449 return(num_received);
453 /****************************************************************************
454 do a directory listing, calling fn on each file found
455 this auto-switches between old and new style
456 ****************************************************************************/
457 int cli_list(struct cli_state *cli,const char *Mask,uint16 attribute,
458 void (*fn)(file_info *, const char *, void *), void *state)
460 if (cli->protocol <= PROTOCOL_LANMAN1) {
461 return cli_list_old(cli, Mask, attribute, fn, state);
463 return cli_list_new(cli, Mask, attribute, fn, state);