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(int level,char *p,file_info *finfo)
35 extern file_info def_finfo;
38 memcpy(finfo,&def_finfo,sizeof(*finfo));
42 case 1: /* OS/2 understands this */
44 /* these dates are converted to GMT by make_unix_date */
45 finfo->ctime = make_unix_date2(p+4);
46 finfo->atime = make_unix_date2(p+8);
47 finfo->mtime = make_unix_date2(p+12);
48 finfo->size = IVAL(p,16);
49 finfo->mode = CVAL(p,24);
50 pstrcpy(finfo->name,p+27);
51 dos_to_unix(finfo->name,True);
53 return(28 + CVAL(p,26));
55 case 2: /* this is what OS/2 uses mostly */
57 /* these dates are converted to GMT by make_unix_date */
58 finfo->ctime = make_unix_date2(p+4);
59 finfo->atime = make_unix_date2(p+8);
60 finfo->mtime = make_unix_date2(p+12);
61 finfo->size = IVAL(p,16);
62 finfo->mode = CVAL(p,24);
63 pstrcpy(finfo->name,p+31);
64 dos_to_unix(finfo->name,True);
66 return(32 + CVAL(p,30));
68 /* levels 3 and 4 are untested */
71 /* these dates are probably like the other ones */
72 finfo->ctime = make_unix_date2(p+8);
73 finfo->atime = make_unix_date2(p+12);
74 finfo->mtime = make_unix_date2(p+16);
75 finfo->size = IVAL(p,20);
76 finfo->mode = CVAL(p,28);
77 pstrcpy(finfo->name,p+33);
78 dos_to_unix(finfo->name,True);
84 /* these dates are probably like the other ones */
85 finfo->ctime = make_unix_date2(p+8);
86 finfo->atime = make_unix_date2(p+12);
87 finfo->mtime = make_unix_date2(p+16);
88 finfo->size = IVAL(p,20);
89 finfo->mode = CVAL(p,28);
90 pstrcpy(finfo->name,p+37);
91 dos_to_unix(finfo->name,True);
95 case 260: /* NT uses this, but also accepts 2 */
99 p += 4; /* next entry offset */
100 p += 4; /* fileindex */
102 /* these dates appear to arrive in a
103 weird way. It seems to be localtime
104 plus the serverzone given in the
105 initial connect. This is GMT when
106 DST is not in effect and one hour
107 from GMT otherwise. Can this really
110 I suppose this could be called
111 kludge-GMT. Is is the GMT you get
112 by using the current DST setting on
113 a different localtime. It will be
114 cheap to calculate, I suppose, as
115 no DST tables will be needed */
117 finfo->ctime = interpret_long_date(p); p += 8;
118 finfo->atime = interpret_long_date(p); p += 8;
119 finfo->mtime = interpret_long_date(p); p += 8; p += 8;
120 finfo->size = IVAL(p,0); p += 8;
121 p += 8; /* alloc size */
122 finfo->mode = CVAL(p,0); p += 4;
123 namelen = IVAL(p,0); p += 4;
124 p += 4; /* EA size */
125 p += 2; /* short name len? */
126 unistr_to_ascii(finfo->short_name, p, 12);
127 p += 24; /* short name? */
128 StrnCpy(finfo->name,p,MIN(sizeof(finfo->name)-1,namelen));
129 dos_to_unix(finfo->name,True);
135 DEBUG(1,("Unknown long filename format %d\n",level));
140 /****************************************************************************
141 do a directory listing, calling fn on each file found
142 ****************************************************************************/
143 int cli_list(struct cli_state *cli,const char *Mask,uint16 attribute,
144 void (*fn)(file_info *, const char *))
146 int max_matches = 512;
147 /* NT uses 260, OS/2 uses 2. Both accept 1. */
148 int info_level = cli->protocol<PROTOCOL_NT1?1:260;
153 char *dirlist = NULL;
155 int total_received = -1;
157 int ff_searchcount=0;
162 char *rparam=NULL, *rdata=NULL;
163 int param_len, data_len;
168 unix_to_dos(mask,True);
170 while (ff_eos == 0) {
172 if (loop_count > 200) {
173 DEBUG(0,("Error: Looping in FIND_NEXT??\n"));
177 param_len = 12+strlen(mask)+1;
180 setup = TRANSACT2_FINDFIRST;
181 SSVAL(param,0,attribute); /* attribute */
182 SSVAL(param,2,max_matches); /* max count */
183 SSVAL(param,4,4+2); /* resume required + close on end */
184 SSVAL(param,6,info_level);
186 pstrcpy(param+12,mask);
188 setup = TRANSACT2_FINDNEXT;
189 SSVAL(param,0,ff_dir_handle);
190 SSVAL(param,2,max_matches); /* max count */
191 SSVAL(param,4,info_level);
192 SIVAL(param,6,0); /* ff_resume_key */
193 SSVAL(param,10,8+4+2); /* continue + resume required + close on end */
194 pstrcpy(param+12,mask);
196 DEBUG(5,("hand=0x%X ff_lastname=%d mask=%s\n",
197 ff_dir_handle,ff_lastname,mask));
200 if (!cli_send_trans(cli, SMBtrans2,
201 NULL, 0, /* Name, length */
202 -1, 0, /* fid, flags */
203 &setup, 1, 0, /* setup, length, max */
204 param, param_len, 10, /* param, length, max */
206 cli->max_xmit /* data, length, max */
211 if (!cli_receive_trans(cli, SMBtrans2,
213 &rdata, &data_len)) {
214 /* we need to work around a Win95 bug - sometimes
215 it gives ERRSRV/ERRerror temprarily */
218 cli_error(cli, &eclass, &ecode, NULL);
219 if (eclass != ERRSRV || ecode != ERRerror) break;
224 if (total_received == -1) total_received = 0;
226 /* parse out some important return info */
229 ff_dir_handle = SVAL(p,0);
230 ff_searchcount = SVAL(p,2);
232 ff_lastname = SVAL(p,8);
234 ff_searchcount = SVAL(p,0);
236 ff_lastname = SVAL(p,6);
239 if (ff_searchcount == 0)
242 /* point to the data bytes */
245 /* we might need the lastname for continuations */
246 if (ff_lastname > 0) {
250 StrnCpy(mask,p+ff_lastname,
251 MIN(sizeof(mask)-1,data_len-ff_lastname));
254 pstrcpy(mask,p + ff_lastname + 1);
261 dos_to_unix(mask, True);
263 /* and add them to the dirlist pool */
264 dirlist = Realloc(dirlist,dirlist_len + data_len);
267 DEBUG(0,("Failed to expand dirlist\n"));
271 /* put in a length for the last entry, to ensure we can chain entries
272 into the next packet */
273 for (p2=p,i=0;i<(ff_searchcount-1);i++)
274 p2 += interpret_long_filename(info_level,p2,NULL);
275 SSVAL(p2,0,data_len - PTR_DIFF(p2,p));
277 /* grab the data for later use */
278 memcpy(dirlist+dirlist_len,p,data_len);
279 dirlist_len += data_len;
281 total_received += ff_searchcount;
283 if (rdata) free(rdata); rdata = NULL;
284 if (rparam) free(rparam); rparam = NULL;
286 DEBUG(3,("received %d entries (eos=%d)\n",
287 ff_searchcount,ff_eos));
289 if (ff_searchcount > 0) loop_count = 0;
294 for (p=dirlist,i=0;i<total_received;i++) {
295 p += interpret_long_filename(info_level,p,&finfo);
299 /* free up the dirlist buffer */
300 if (dirlist) free(dirlist);
301 return(total_received);
306 /****************************************************************************
307 interpret a short filename structure
308 The length of the structure is returned
309 ****************************************************************************/
310 static int interpret_short_filename(char *p,file_info *finfo)
312 extern file_info def_finfo;
316 finfo->mode = CVAL(p,21);
318 /* this date is converted to GMT by make_unix_date */
319 finfo->ctime = make_unix_date(p+22);
320 finfo->mtime = finfo->atime = finfo->ctime;
321 finfo->size = IVAL(p,26);
322 pstrcpy(finfo->name,p+30);
323 if (strcmp(finfo->name, "..") && strcmp(finfo->name, "."))
324 fstrcpy(finfo->short_name,finfo->name);
326 return(DIR_STRUCT_SIZE);
330 /****************************************************************************
331 do a directory listing, calling fn on each file found
332 this uses the old SMBsearch interface. It is needed for testing Samba,
333 but should otherwise not be used
334 ****************************************************************************/
335 int cli_list_old(struct cli_state *cli,const char *Mask,uint16 attribute,
336 void (*fn)(file_info *, const char *))
342 int num_asked = (cli->max_xmit - 100)/DIR_STRUCT_SIZE;
343 int num_received = 0;
345 char *dirlist = NULL;
353 memset(cli->outbuf,'\0',smb_size);
354 memset(cli->inbuf,'\0',smb_size);
357 set_message(cli->outbuf,2,5 + strlen(mask),True);
359 set_message(cli->outbuf,2,5 + 21,True);
361 CVAL(cli->outbuf,smb_com) = SMBffirst;
363 SSVAL(cli->outbuf,smb_tid,cli->cnum);
364 cli_setup_packet(cli);
366 SSVAL(cli->outbuf,smb_vwv0,num_asked);
367 SSVAL(cli->outbuf,smb_vwv1,attribute);
369 p = smb_buf(cli->outbuf);
388 if (!cli_receive_smb(cli)) break;
390 received = SVAL(cli->inbuf,smb_vwv0);
391 if (received <= 0) break;
395 dirlist = Realloc(dirlist,(num_received + received)*DIR_STRUCT_SIZE);
400 p = smb_buf(cli->inbuf) + 3;
402 memcpy(dirlist+num_received*DIR_STRUCT_SIZE,
403 p,received*DIR_STRUCT_SIZE);
405 memcpy(status,p + ((received-1)*DIR_STRUCT_SIZE),21);
407 num_received += received;
409 if (CVAL(cli->inbuf,smb_rcls) != 0) break;
413 memset(cli->outbuf,'\0',smb_size);
414 memset(cli->inbuf,'\0',smb_size);
416 set_message(cli->outbuf,0,6,True);
417 CVAL(cli->outbuf,smb_com) = SMBfclose;
418 SSVAL(cli->outbuf,smb_tid,cli->cnum);
419 cli_setup_packet(cli);
421 p = smb_buf(cli->outbuf);
433 if (!cli_receive_smb(cli)) {
434 DEBUG(0,("Error closing search: %s\n",smb_errstr(cli->inbuf)));
438 for (p=dirlist,i=0;i<num_received;i++) {
440 p += interpret_short_filename(p,&finfo);
444 if (dirlist) free(dirlist);
445 return(num_received);