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;
39 memcpy(finfo,&def_finfo,sizeof(*finfo));
43 case 1: /* OS/2 understands this */
45 /* these dates are converted to GMT by make_unix_date */
46 finfo->ctime = make_unix_date2(p+4);
47 finfo->atime = make_unix_date2(p+8);
48 finfo->mtime = make_unix_date2(p+12);
49 finfo->size = IVAL(p,16);
50 finfo->mode = CVAL(p,24);
51 clistr_pull(cli, finfo->name, p+27,
54 CLISTR_TERMINATE | CLISTR_CONVERT);
56 return(28 + CVAL(p,26));
58 case 2: /* this is what OS/2 uses mostly */
60 /* these dates are converted to GMT by make_unix_date */
61 finfo->ctime = make_unix_date2(p+4);
62 finfo->atime = make_unix_date2(p+8);
63 finfo->mtime = make_unix_date2(p+12);
64 finfo->size = IVAL(p,16);
65 finfo->mode = CVAL(p,24);
66 clistr_pull(cli, finfo->name, p+31,
69 CLISTR_TERMINATE | CLISTR_CONVERT);
71 return(32 + CVAL(p,30));
73 /* levels 3 and 4 are untested */
76 /* these dates are probably like the other ones */
77 finfo->ctime = make_unix_date2(p+8);
78 finfo->atime = make_unix_date2(p+12);
79 finfo->mtime = make_unix_date2(p+16);
80 finfo->size = IVAL(p,20);
81 finfo->mode = CVAL(p,28);
82 clistr_pull(cli, finfo->name, p+33,
85 CLISTR_TERMINATE | CLISTR_CONVERT);
91 /* these dates are probably like the other ones */
92 finfo->ctime = make_unix_date2(p+8);
93 finfo->atime = make_unix_date2(p+12);
94 finfo->mtime = make_unix_date2(p+16);
95 finfo->size = IVAL(p,20);
96 finfo->mode = CVAL(p,28);
97 clistr_pull(cli, finfo->name, p+37,
100 CLISTR_TERMINATE | CLISTR_CONVERT);
104 case 260: /* NT uses this, but also accepts 2 */
108 p += 4; /* next entry offset */
109 p += 4; /* fileindex */
111 /* these dates appear to arrive in a
112 weird way. It seems to be localtime
113 plus the serverzone given in the
114 initial connect. This is GMT when
115 DST is not in effect and one hour
116 from GMT otherwise. Can this really
119 I suppose this could be called
120 kludge-GMT. Is is the GMT you get
121 by using the current DST setting on
122 a different localtime. It will be
123 cheap to calculate, I suppose, as
124 no DST tables will be needed */
126 finfo->ctime = interpret_long_date(p); p += 8;
127 finfo->atime = interpret_long_date(p); p += 8;
128 finfo->mtime = interpret_long_date(p); p += 8; p += 8;
129 finfo->size = IVAL(p,0); p += 8;
130 p += 8; /* alloc size */
131 finfo->mode = CVAL(p,0); p += 4;
132 namelen = IVAL(p,0); p += 4;
133 p += 4; /* EA size */
136 clistr_pull(cli, finfo->short_name, p,
137 sizeof(finfo->short_name),
139 CLISTR_TERMINATE | CLISTR_CONVERT);
140 p += 24; /* short name? */
141 clistr_pull(cli, finfo->name, p,
150 DEBUG(1,("Unknown long filename format %d\n",level));
155 /****************************************************************************
156 do a directory listing, calling fn on each file found
157 ****************************************************************************/
158 int cli_list(struct cli_state *cli,const char *Mask,uint16 attribute,
159 void (*fn)(file_info *, const char *, void *), void *state)
161 int max_matches = 512;
162 /* NT uses 260, OS/2 uses 2. Both accept 1. */
163 int info_level = cli->protocol<PROTOCOL_NT1?1:260;
168 char *dirlist = NULL;
170 int total_received = -1;
172 int ff_searchcount=0;
177 char *rparam=NULL, *rdata=NULL;
178 int param_len, data_len;
182 if (cli->protocol <= PROTOCOL_LANMAN1) {
183 return cli_list_old(cli, Mask, attribute, fn, state);
188 while (ff_eos == 0) {
190 if (loop_count > 200) {
191 DEBUG(0,("Error: Looping in FIND_NEXT??\n"));
195 param_len = 12+clistr_push_size(cli, NULL, mask, -1,
200 setup = TRANSACT2_FINDFIRST;
201 SSVAL(param,0,attribute); /* attribute */
202 SSVAL(param,2,max_matches); /* max count */
203 SSVAL(param,4,4+2); /* resume required + close on end */
204 SSVAL(param,6,info_level);
206 clistr_push(cli, param+12, mask, -1,
207 CLISTR_TERMINATE | CLISTR_CONVERT);
209 setup = TRANSACT2_FINDNEXT;
210 SSVAL(param,0,ff_dir_handle);
211 SSVAL(param,2,max_matches); /* max count */
212 SSVAL(param,4,info_level);
213 SIVAL(param,6,0); /* ff_resume_key */
214 SSVAL(param,10,8+4+2); /* continue + resume required + close on end */
215 clistr_push(cli, param+12, mask, -1,
216 CLISTR_TERMINATE | CLISTR_CONVERT);
219 if (!cli_send_trans(cli, SMBtrans2,
221 -1, 0, /* fid, flags */
222 &setup, 1, 0, /* setup, length, max */
223 param, param_len, 10, /* param, length, max */
225 cli->max_xmit /* data, length, max */
230 if (!cli_receive_trans(cli, SMBtrans2,
232 &rdata, &data_len)) {
233 /* we need to work around a Win95 bug - sometimes
234 it gives ERRSRV/ERRerror temprarily */
237 cli_error(cli, &eclass, &ecode, NULL);
238 if (eclass != ERRSRV || ecode != ERRerror) break;
243 if (total_received == -1) total_received = 0;
245 /* parse out some important return info */
248 ff_dir_handle = SVAL(p,0);
249 ff_searchcount = SVAL(p,2);
251 ff_lastname = SVAL(p,8);
253 ff_searchcount = SVAL(p,0);
255 ff_lastname = SVAL(p,6);
258 if (ff_searchcount == 0)
261 /* point to the data bytes */
264 /* we might need the lastname for continuations */
265 if (ff_lastname > 0) {
269 clistr_pull(cli, mask, p+ff_lastname,
271 data_len-ff_lastname,
276 clistr_pull(cli, mask, p+ff_lastname+1,
287 /* and add them to the dirlist pool */
288 dirlist = Realloc(dirlist,dirlist_len + data_len);
291 DEBUG(0,("Failed to expand dirlist\n"));
295 /* put in a length for the last entry, to ensure we can chain entries
296 into the next packet */
297 for (p2=p,i=0;i<(ff_searchcount-1);i++)
298 p2 += interpret_long_filename(cli,info_level,p2,NULL);
299 SSVAL(p2,0,data_len - PTR_DIFF(p2,p));
301 /* grab the data for later use */
302 memcpy(dirlist+dirlist_len,p,data_len);
303 dirlist_len += data_len;
305 total_received += ff_searchcount;
307 if (rdata) free(rdata); rdata = NULL;
308 if (rparam) free(rparam); rparam = NULL;
310 DEBUG(3,("received %d entries (eos=%d)\n",
311 ff_searchcount,ff_eos));
313 if (ff_searchcount > 0) loop_count = 0;
318 for (p=dirlist,i=0;i<total_received;i++) {
319 p += interpret_long_filename(cli,info_level,p,&finfo);
320 fn(&finfo, Mask, state);
323 /* free up the dirlist buffer */
324 if (dirlist) free(dirlist);
325 return(total_received);
330 /****************************************************************************
331 interpret a short filename structure
332 The length of the structure is returned
333 ****************************************************************************/
334 static int interpret_short_filename(char *p,file_info *finfo)
336 extern file_info def_finfo;
340 finfo->mode = CVAL(p,21);
342 /* this date is converted to GMT by make_unix_date */
343 finfo->ctime = make_unix_date(p+22);
344 finfo->mtime = finfo->atime = finfo->ctime;
345 finfo->size = IVAL(p,26);
346 pstrcpy(finfo->name,p+30);
347 if (strcmp(finfo->name, "..") && strcmp(finfo->name, "."))
348 fstrcpy(finfo->short_name,finfo->name);
350 return(DIR_STRUCT_SIZE);
354 /****************************************************************************
355 do a directory listing, calling fn on each file found
356 this uses the old SMBsearch interface. It is needed for testing Samba,
357 but should otherwise not be used
358 ****************************************************************************/
359 int cli_list_old(struct cli_state *cli,const char *Mask,uint16 attribute,
360 void (*fn)(file_info *, const char *, void *), void *state)
366 int num_asked = (cli->max_xmit - 100)/DIR_STRUCT_SIZE;
367 int num_received = 0;
369 char *dirlist = NULL;
377 memset(cli->outbuf,'\0',smb_size);
378 memset(cli->inbuf,'\0',smb_size);
380 set_message(cli->outbuf,2,0,True);
382 CVAL(cli->outbuf,smb_com) = SMBsearch;
384 SSVAL(cli->outbuf,smb_tid,cli->cnum);
385 cli_setup_packet(cli);
387 SSVAL(cli->outbuf,smb_vwv0,num_asked);
388 SSVAL(cli->outbuf,smb_vwv1,attribute);
390 p = smb_buf(cli->outbuf);
393 p += clistr_push(cli, p, first?mask:"", -1, CLISTR_TERMINATE|CLISTR_CONVERT);
405 cli_setup_bcc(cli, p);
407 if (!cli_receive_smb(cli)) break;
409 received = SVAL(cli->inbuf,smb_vwv0);
410 if (received <= 0) break;
414 dirlist = Realloc(dirlist,(num_received + received)*DIR_STRUCT_SIZE);
419 p = smb_buf(cli->inbuf) + 3;
421 memcpy(dirlist+num_received*DIR_STRUCT_SIZE,
422 p,received*DIR_STRUCT_SIZE);
424 memcpy(status,p + ((received-1)*DIR_STRUCT_SIZE),21);
426 num_received += received;
428 if (CVAL(cli->inbuf,smb_rcls) != 0) break;
432 memset(cli->outbuf,'\0',smb_size);
433 memset(cli->inbuf,'\0',smb_size);
435 set_message(cli->outbuf,2,0,True);
436 CVAL(cli->outbuf,smb_com) = SMBfclose;
437 SSVAL(cli->outbuf,smb_tid,cli->cnum);
438 cli_setup_packet(cli);
440 SSVAL(cli->outbuf, smb_vwv0, 0); /* find count? */
441 SSVAL(cli->outbuf, smb_vwv1, attribute);
443 p = smb_buf(cli->outbuf);
453 cli_setup_bcc(cli, p);
455 if (!cli_receive_smb(cli)) {
456 DEBUG(0,("Error closing search: %s\n",smb_errstr(cli->inbuf)));
460 for (p=dirlist,i=0;i<num_received;i++) {
462 p += interpret_short_filename(p,&finfo);
463 fn(&finfo, Mask, state);
466 if (dirlist) free(dirlist);
467 return(num_received);