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,
144 CLISTR_TERMINATE | CLISTR_CONVERT);
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;
184 while (ff_eos == 0) {
186 if (loop_count > 200) {
187 DEBUG(0,("Error: Looping in FIND_NEXT??\n"));
191 param_len = 12+clistr_push_size(cli, NULL, mask, -1,
196 setup = TRANSACT2_FINDFIRST;
197 SSVAL(param,0,attribute); /* attribute */
198 SSVAL(param,2,max_matches); /* max count */
199 SSVAL(param,4,4+2); /* resume required + close on end */
200 SSVAL(param,6,info_level);
202 clistr_push(cli, param+12, mask, -1,
203 CLISTR_TERMINATE | CLISTR_CONVERT);
205 setup = TRANSACT2_FINDNEXT;
206 SSVAL(param,0,ff_dir_handle);
207 SSVAL(param,2,max_matches); /* max count */
208 SSVAL(param,4,info_level);
209 SIVAL(param,6,0); /* ff_resume_key */
210 SSVAL(param,10,8+4+2); /* continue + resume required + close on end */
211 clistr_push(cli, param+12, mask, -1,
212 CLISTR_TERMINATE | CLISTR_CONVERT);
215 if (!cli_send_trans(cli, SMBtrans2,
217 -1, 0, /* fid, flags */
218 &setup, 1, 0, /* setup, length, max */
219 param, param_len, 10, /* param, length, max */
221 cli->max_xmit /* data, length, max */
226 if (!cli_receive_trans(cli, SMBtrans2,
228 &rdata, &data_len)) {
229 /* we need to work around a Win95 bug - sometimes
230 it gives ERRSRV/ERRerror temprarily */
233 cli_error(cli, &eclass, &ecode, NULL);
234 if (eclass != ERRSRV || ecode != ERRerror) break;
239 if (total_received == -1) total_received = 0;
241 /* parse out some important return info */
244 ff_dir_handle = SVAL(p,0);
245 ff_searchcount = SVAL(p,2);
247 ff_lastname = SVAL(p,8);
249 ff_searchcount = SVAL(p,0);
251 ff_lastname = SVAL(p,6);
254 if (ff_searchcount == 0)
257 /* point to the data bytes */
260 /* we might need the lastname for continuations */
261 if (ff_lastname > 0) {
265 clistr_pull(cli, mask, p+ff_lastname,
267 data_len-ff_lastname,
272 clistr_pull(cli, mask, p+ff_lastname+1,
283 /* and add them to the dirlist pool */
284 dirlist = Realloc(dirlist,dirlist_len + data_len);
287 DEBUG(0,("Failed to expand dirlist\n"));
291 /* put in a length for the last entry, to ensure we can chain entries
292 into the next packet */
293 for (p2=p,i=0;i<(ff_searchcount-1);i++)
294 p2 += interpret_long_filename(cli,info_level,p2,NULL);
295 SSVAL(p2,0,data_len - PTR_DIFF(p2,p));
297 /* grab the data for later use */
298 memcpy(dirlist+dirlist_len,p,data_len);
299 dirlist_len += data_len;
301 total_received += ff_searchcount;
303 if (rdata) free(rdata); rdata = NULL;
304 if (rparam) free(rparam); rparam = NULL;
306 DEBUG(3,("received %d entries (eos=%d)\n",
307 ff_searchcount,ff_eos));
309 if (ff_searchcount > 0) loop_count = 0;
314 for (p=dirlist,i=0;i<total_received;i++) {
315 p += interpret_long_filename(cli,info_level,p,&finfo);
316 fn(&finfo, Mask, state);
319 /* free up the dirlist buffer */
320 if (dirlist) free(dirlist);
321 return(total_received);
326 /****************************************************************************
327 interpret a short filename structure
328 The length of the structure is returned
329 ****************************************************************************/
330 static int interpret_short_filename(char *p,file_info *finfo)
332 extern file_info def_finfo;
336 finfo->mode = CVAL(p,21);
338 /* this date is converted to GMT by make_unix_date */
339 finfo->ctime = make_unix_date(p+22);
340 finfo->mtime = finfo->atime = finfo->ctime;
341 finfo->size = IVAL(p,26);
342 pstrcpy(finfo->name,p+30);
343 if (strcmp(finfo->name, "..") && strcmp(finfo->name, "."))
344 fstrcpy(finfo->short_name,finfo->name);
346 return(DIR_STRUCT_SIZE);
350 /****************************************************************************
351 do a directory listing, calling fn on each file found
352 this uses the old SMBsearch interface. It is needed for testing Samba,
353 but should otherwise not be used
354 ****************************************************************************/
355 int cli_list_old(struct cli_state *cli,const char *Mask,uint16 attribute,
356 void (*fn)(file_info *, const char *))
362 int num_asked = (cli->max_xmit - 100)/DIR_STRUCT_SIZE;
363 int num_received = 0;
365 char *dirlist = NULL;
373 memset(cli->outbuf,'\0',smb_size);
374 memset(cli->inbuf,'\0',smb_size);
377 set_message(cli->outbuf,2,5 + strlen(mask),True);
379 set_message(cli->outbuf,2,5 + 21,True);
381 CVAL(cli->outbuf,smb_com) = SMBffirst;
383 SSVAL(cli->outbuf,smb_tid,cli->cnum);
384 cli_setup_packet(cli);
386 SSVAL(cli->outbuf,smb_vwv0,num_asked);
387 SSVAL(cli->outbuf,smb_vwv1,attribute);
389 p = smb_buf(cli->outbuf);
408 if (!cli_receive_smb(cli)) break;
410 received = SVAL(cli->inbuf,smb_vwv0);
411 if (received <= 0) break;
415 dirlist = Realloc(dirlist,(num_received + received)*DIR_STRUCT_SIZE);
420 p = smb_buf(cli->inbuf) + 3;
422 memcpy(dirlist+num_received*DIR_STRUCT_SIZE,
423 p,received*DIR_STRUCT_SIZE);
425 memcpy(status,p + ((received-1)*DIR_STRUCT_SIZE),21);
427 num_received += received;
429 if (CVAL(cli->inbuf,smb_rcls) != 0) break;
433 memset(cli->outbuf,'\0',smb_size);
434 memset(cli->inbuf,'\0',smb_size);
436 set_message(cli->outbuf,2,5 + 21,True);
437 CVAL(cli->outbuf,smb_com) = SMBfclose;
438 SSVAL(cli->outbuf,smb_tid,cli->cnum);
439 cli_setup_packet(cli);
441 SSVAL(cli->outbuf, smb_vwv0, 0); /* find count? */
442 SSVAL(cli->outbuf, smb_vwv1, attribute);
444 p = smb_buf(cli->outbuf);
454 if (!cli_receive_smb(cli)) {
455 DEBUG(0,("Error closing search: %s\n",smb_errstr(cli->inbuf)));
459 for (p=dirlist,i=0;i<num_received;i++) {
461 p += interpret_short_filename(p,&finfo);
465 if (dirlist) free(dirlist);
466 return(num_received);