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 /* we need to work around a Win95 bug - sometimes
209 it gives ERRSRV/ERRerror temprarily */
212 cli_error(cli, &eclass, &ecode, NULL);
213 if (eclass != ERRSRV || ecode != ERRerror) break;
218 if (total_received == -1) total_received = 0;
220 /* parse out some important return info */
223 ff_dir_handle = SVAL(p,0);
224 ff_searchcount = SVAL(p,2);
226 ff_lastname = SVAL(p,8);
228 ff_searchcount = SVAL(p,0);
230 ff_lastname = SVAL(p,6);
233 if (ff_searchcount == 0)
236 /* point to the data bytes */
239 /* we might need the lastname for continuations */
240 if (ff_lastname > 0) {
244 clistr_pull(cli, mask, p+ff_lastname,
246 data_len-ff_lastname,
250 clistr_pull(cli, mask, p+ff_lastname+1,
260 /* and add them to the dirlist pool */
261 dirlist = Realloc(dirlist,dirlist_len + data_len);
264 DEBUG(0,("Failed to expand dirlist\n"));
268 /* put in a length for the last entry, to ensure we can chain entries
269 into the next packet */
270 for (p2=p,i=0;i<(ff_searchcount-1);i++)
271 p2 += interpret_long_filename(cli,info_level,p2,NULL);
272 SSVAL(p2,0,data_len - PTR_DIFF(p2,p));
274 /* grab the data for later use */
275 memcpy(dirlist+dirlist_len,p,data_len);
276 dirlist_len += data_len;
278 total_received += ff_searchcount;
280 if (rdata) free(rdata); rdata = NULL;
281 if (rparam) free(rparam); rparam = NULL;
283 DEBUG(3,("received %d entries (eos=%d)\n",
284 ff_searchcount,ff_eos));
286 if (ff_searchcount > 0) loop_count = 0;
291 for (p=dirlist,i=0;i<total_received;i++) {
292 p += interpret_long_filename(cli,info_level,p,&finfo);
293 fn(&finfo, Mask, state);
296 /* free up the dirlist buffer */
297 if (dirlist) free(dirlist);
298 return(total_received);
303 /****************************************************************************
304 interpret a short filename structure
305 The length of the structure is returned
306 ****************************************************************************/
307 static int interpret_short_filename(struct cli_state *cli, char *p,file_info *finfo)
309 extern file_info def_finfo;
313 finfo->mode = CVAL(p,21);
315 /* this date is converted to GMT by make_unix_date */
316 finfo->ctime = make_unix_date(p+22);
317 finfo->mtime = finfo->atime = finfo->ctime;
318 finfo->size = IVAL(p,26);
319 clistr_pull(cli, finfo->name, p+30, sizeof(finfo->name), 12, STR_ASCII);
320 if (strcmp(finfo->name, "..") && strcmp(finfo->name, "."))
321 fstrcpy(finfo->short_name,finfo->name);
323 return(DIR_STRUCT_SIZE);
327 /****************************************************************************
328 do a directory listing, calling fn on each file found
329 this uses the old SMBsearch interface. It is needed for testing Samba,
330 but should otherwise not be used
331 ****************************************************************************/
332 int cli_list_old(struct cli_state *cli,const char *Mask,uint16 attribute,
333 void (*fn)(file_info *, const char *, void *), void *state)
339 int num_asked = (cli->max_xmit - 100)/DIR_STRUCT_SIZE;
340 int num_received = 0;
342 char *dirlist = NULL;
350 memset(cli->outbuf,'\0',smb_size);
351 memset(cli->inbuf,'\0',smb_size);
353 set_message(cli->outbuf,2,0,True);
355 CVAL(cli->outbuf,smb_com) = SMBsearch;
357 SSVAL(cli->outbuf,smb_tid,cli->cnum);
358 cli_setup_packet(cli);
360 SSVAL(cli->outbuf,smb_vwv0,num_asked);
361 SSVAL(cli->outbuf,smb_vwv1,attribute);
363 p = smb_buf(cli->outbuf);
366 p += clistr_push(cli, p, first?mask:"", -1, STR_TERMINATE);
378 cli_setup_bcc(cli, p);
380 if (!cli_receive_smb(cli)) break;
382 received = SVAL(cli->inbuf,smb_vwv0);
383 if (received <= 0) break;
387 dirlist = Realloc(dirlist,(num_received + received)*DIR_STRUCT_SIZE);
392 p = smb_buf(cli->inbuf) + 3;
394 memcpy(dirlist+num_received*DIR_STRUCT_SIZE,
395 p,received*DIR_STRUCT_SIZE);
397 memcpy(status,p + ((received-1)*DIR_STRUCT_SIZE),21);
399 num_received += received;
401 if (CVAL(cli->inbuf,smb_rcls) != 0) break;
405 memset(cli->outbuf,'\0',smb_size);
406 memset(cli->inbuf,'\0',smb_size);
408 set_message(cli->outbuf,2,0,True);
409 CVAL(cli->outbuf,smb_com) = SMBfclose;
410 SSVAL(cli->outbuf,smb_tid,cli->cnum);
411 cli_setup_packet(cli);
413 SSVAL(cli->outbuf, smb_vwv0, 0); /* find count? */
414 SSVAL(cli->outbuf, smb_vwv1, attribute);
416 p = smb_buf(cli->outbuf);
426 cli_setup_bcc(cli, p);
428 if (!cli_receive_smb(cli)) {
429 DEBUG(0,("Error closing search: %s\n",smb_errstr(cli->inbuf)));
433 for (p=dirlist,i=0;i<num_received;i++) {
435 p += interpret_short_filename(cli, p,&finfo);
436 fn(&finfo, Mask, state);
439 if (dirlist) free(dirlist);
440 return(num_received);
444 /****************************************************************************
445 do a directory listing, calling fn on each file found
446 this auto-switches between old and new style
447 ****************************************************************************/
448 int cli_list(struct cli_state *cli,const char *Mask,uint16 attribute,
449 void (*fn)(file_info *, const char *, void *), void *state)
451 if (cli->protocol <= PROTOCOL_LANMAN1) {
452 return cli_list_old(cli, Mask, attribute, fn, state);
454 return cli_list_new(cli, Mask, attribute, fn, state);