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 *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 (total_received == -1) total_received = 0;
221 /* parse out some important return info */
224 ff_dir_handle = SVAL(p,0);
225 ff_searchcount = SVAL(p,2);
227 ff_lastname = SVAL(p,8);
229 ff_searchcount = SVAL(p,0);
231 ff_lastname = SVAL(p,6);
234 if (ff_searchcount == 0)
237 /* point to the data bytes */
240 /* we might need the lastname for continuations */
241 if (ff_lastname > 0) {
245 clistr_pull(cli, mask, p+ff_lastname,
247 data_len-ff_lastname,
251 clistr_pull(cli, mask, p+ff_lastname+1,
261 /* and add them to the dirlist pool */
262 dirlist = Realloc(dirlist,dirlist_len + data_len);
265 DEBUG(0,("Failed to expand dirlist\n"));
269 /* put in a length for the last entry, to ensure we can chain entries
270 into the next packet */
271 for (p2=p,i=0;i<(ff_searchcount-1);i++)
272 p2 += interpret_long_filename(cli,info_level,p2,NULL);
273 SSVAL(p2,0,data_len - PTR_DIFF(p2,p));
275 /* grab the data for later use */
276 memcpy(dirlist+dirlist_len,p,data_len);
277 dirlist_len += data_len;
279 total_received += ff_searchcount;
281 if (rdata) free(rdata); rdata = NULL;
282 if (rparam) free(rparam); rparam = NULL;
284 DEBUG(3,("received %d entries (eos=%d)\n",
285 ff_searchcount,ff_eos));
287 if (ff_searchcount > 0) loop_count = 0;
292 for (p=dirlist,i=0;i<total_received;i++) {
293 p += interpret_long_filename(cli,info_level,p,&finfo);
294 fn(&finfo, Mask, state);
297 /* free up the dirlist buffer */
298 if (dirlist) free(dirlist);
299 return(total_received);
304 /****************************************************************************
305 interpret a short filename structure
306 The length of the structure is returned
307 ****************************************************************************/
308 static int interpret_short_filename(struct cli_state *cli, char *p,file_info *finfo)
310 extern file_info def_finfo;
314 finfo->mode = CVAL(p,21);
316 /* this date is converted to GMT by make_unix_date */
317 finfo->ctime = make_unix_date(p+22);
318 finfo->mtime = finfo->atime = finfo->ctime;
319 finfo->size = IVAL(p,26);
320 clistr_pull(cli, finfo->name, p+30, sizeof(finfo->name), 12, STR_ASCII);
321 if (strcmp(finfo->name, "..") && strcmp(finfo->name, "."))
322 fstrcpy(finfo->short_name,finfo->name);
324 return(DIR_STRUCT_SIZE);
328 /****************************************************************************
329 do a directory listing, calling fn on each file found
330 this uses the old SMBsearch interface. It is needed for testing Samba,
331 but should otherwise not be used
332 ****************************************************************************/
333 int cli_list_old(struct cli_state *cli,const char *Mask,uint16 attribute,
334 void (*fn)(file_info *, const char *, void *), void *state)
340 int num_asked = (cli->max_xmit - 100)/DIR_STRUCT_SIZE;
341 int num_received = 0;
343 char *dirlist = NULL;
351 memset(cli->outbuf,'\0',smb_size);
352 memset(cli->inbuf,'\0',smb_size);
354 set_message(cli->outbuf,2,0,True);
356 CVAL(cli->outbuf,smb_com) = SMBsearch;
358 SSVAL(cli->outbuf,smb_tid,cli->cnum);
359 cli_setup_packet(cli);
361 SSVAL(cli->outbuf,smb_vwv0,num_asked);
362 SSVAL(cli->outbuf,smb_vwv1,attribute);
364 p = smb_buf(cli->outbuf);
367 p += clistr_push(cli, p, first?mask:"", -1, STR_TERMINATE);
379 cli_setup_bcc(cli, p);
381 if (!cli_receive_smb(cli)) break;
383 received = SVAL(cli->inbuf,smb_vwv0);
384 if (received <= 0) break;
388 dirlist = Realloc(dirlist,(num_received + received)*DIR_STRUCT_SIZE);
393 p = smb_buf(cli->inbuf) + 3;
395 memcpy(dirlist+num_received*DIR_STRUCT_SIZE,
396 p,received*DIR_STRUCT_SIZE);
398 memcpy(status,p + ((received-1)*DIR_STRUCT_SIZE),21);
400 num_received += received;
402 if (CVAL(cli->inbuf,smb_rcls) != 0) break;
406 memset(cli->outbuf,'\0',smb_size);
407 memset(cli->inbuf,'\0',smb_size);
409 set_message(cli->outbuf,2,0,True);
410 CVAL(cli->outbuf,smb_com) = SMBfclose;
411 SSVAL(cli->outbuf,smb_tid,cli->cnum);
412 cli_setup_packet(cli);
414 SSVAL(cli->outbuf, smb_vwv0, 0); /* find count? */
415 SSVAL(cli->outbuf, smb_vwv1, attribute);
417 p = smb_buf(cli->outbuf);
427 cli_setup_bcc(cli, p);
429 if (!cli_receive_smb(cli)) {
430 DEBUG(0,("Error closing search: %s\n",smb_errstr(cli->inbuf)));
434 for (p=dirlist,i=0;i<num_received;i++) {
436 p += interpret_short_filename(cli, p,&finfo);
437 fn(&finfo, Mask, state);
440 if (dirlist) free(dirlist);
441 return(num_received);
445 /****************************************************************************
446 do a directory listing, calling fn on each file found
447 this auto-switches between old and new style
448 ****************************************************************************/
449 int cli_list(struct cli_state *cli,const char *Mask,uint16 attribute,
450 void (*fn)(file_info *, const char *, void *), void *state)
452 if (cli->protocol <= PROTOCOL_LANMAN1) {
453 return cli_list_old(cli, Mask, attribute, fn, state);
455 return cli_list_new(cli, Mask, attribute, fn, state);