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 */
127 if (p[1] == 0 && slen > 1) {
128 /* NT has stuffed up again */
129 unistr_to_dos(finfo->short_name, p, slen/2);
131 strncpy(finfo->short_name, p, 12);
132 finfo->short_name[12] = 0;
134 p += 24; /* short name? */
135 StrnCpy(finfo->name,p,MIN(sizeof(finfo->name)-1,namelen));
136 dos_to_unix(finfo->name,True);
142 DEBUG(1,("Unknown long filename format %d\n",level));
147 /****************************************************************************
148 do a directory listing, calling fn on each file found
149 ****************************************************************************/
150 int cli_list(struct cli_state *cli,const char *Mask,uint16 attribute,
151 void (*fn)(file_info *, const char *))
153 int max_matches = 512;
154 /* NT uses 260, OS/2 uses 2. Both accept 1. */
155 int info_level = cli->protocol<PROTOCOL_NT1?1:260;
160 char *dirlist = NULL;
162 int total_received = -1;
164 int ff_searchcount=0;
169 char *rparam=NULL, *rdata=NULL;
170 int param_len, data_len;
175 unix_to_dos(mask,True);
177 while (ff_eos == 0) {
179 if (loop_count > 200) {
180 DEBUG(0,("Error: Looping in FIND_NEXT??\n"));
184 param_len = 12+strlen(mask)+1;
187 setup = TRANSACT2_FINDFIRST;
188 SSVAL(param,0,attribute); /* attribute */
189 SSVAL(param,2,max_matches); /* max count */
190 SSVAL(param,4,4+2); /* resume required + close on end */
191 SSVAL(param,6,info_level);
193 pstrcpy(param+12,mask);
195 setup = TRANSACT2_FINDNEXT;
196 SSVAL(param,0,ff_dir_handle);
197 SSVAL(param,2,max_matches); /* max count */
198 SSVAL(param,4,info_level);
199 SIVAL(param,6,0); /* ff_resume_key */
200 SSVAL(param,10,8+4+2); /* continue + resume required + close on end */
201 pstrcpy(param+12,mask);
203 DEBUG(5,("hand=0x%X ff_lastname=%d mask=%s\n",
204 ff_dir_handle,ff_lastname,mask));
207 if (!cli_send_trans(cli, SMBtrans2,
208 NULL, 0, /* Name, length */
209 -1, 0, /* fid, flags */
210 &setup, 1, 0, /* setup, length, max */
211 param, param_len, 10, /* param, length, max */
213 cli->max_xmit /* data, length, max */
218 if (!cli_receive_trans(cli, SMBtrans2,
220 &rdata, &data_len)) {
221 /* we need to work around a Win95 bug - sometimes
222 it gives ERRSRV/ERRerror temprarily */
225 cli_error(cli, &eclass, &ecode, NULL);
226 if (eclass != ERRSRV || ecode != ERRerror) break;
231 if (total_received == -1) total_received = 0;
233 /* parse out some important return info */
236 ff_dir_handle = SVAL(p,0);
237 ff_searchcount = SVAL(p,2);
239 ff_lastname = SVAL(p,8);
241 ff_searchcount = SVAL(p,0);
243 ff_lastname = SVAL(p,6);
246 if (ff_searchcount == 0)
249 /* point to the data bytes */
252 /* we might need the lastname for continuations */
253 if (ff_lastname > 0) {
257 StrnCpy(mask,p+ff_lastname,
258 MIN(sizeof(mask)-1,data_len-ff_lastname));
261 pstrcpy(mask,p + ff_lastname + 1);
268 dos_to_unix(mask, True);
270 /* and add them to the dirlist pool */
271 dirlist = Realloc(dirlist,dirlist_len + data_len);
274 DEBUG(0,("Failed to expand dirlist\n"));
278 /* put in a length for the last entry, to ensure we can chain entries
279 into the next packet */
280 for (p2=p,i=0;i<(ff_searchcount-1);i++)
281 p2 += interpret_long_filename(info_level,p2,NULL);
282 SSVAL(p2,0,data_len - PTR_DIFF(p2,p));
284 /* grab the data for later use */
285 memcpy(dirlist+dirlist_len,p,data_len);
286 dirlist_len += data_len;
288 total_received += ff_searchcount;
290 if (rdata) free(rdata); rdata = NULL;
291 if (rparam) free(rparam); rparam = NULL;
293 DEBUG(3,("received %d entries (eos=%d)\n",
294 ff_searchcount,ff_eos));
296 if (ff_searchcount > 0) loop_count = 0;
301 for (p=dirlist,i=0;i<total_received;i++) {
302 p += interpret_long_filename(info_level,p,&finfo);
306 /* free up the dirlist buffer */
307 if (dirlist) free(dirlist);
308 return(total_received);
313 /****************************************************************************
314 interpret a short filename structure
315 The length of the structure is returned
316 ****************************************************************************/
317 static int interpret_short_filename(char *p,file_info *finfo)
319 extern file_info def_finfo;
323 finfo->mode = CVAL(p,21);
325 /* this date is converted to GMT by make_unix_date */
326 finfo->ctime = make_unix_date(p+22);
327 finfo->mtime = finfo->atime = finfo->ctime;
328 finfo->size = IVAL(p,26);
329 pstrcpy(finfo->name,p+30);
330 if (strcmp(finfo->name, "..") && strcmp(finfo->name, "."))
331 fstrcpy(finfo->short_name,finfo->name);
333 return(DIR_STRUCT_SIZE);
337 /****************************************************************************
338 do a directory listing, calling fn on each file found
339 this uses the old SMBsearch interface. It is needed for testing Samba,
340 but should otherwise not be used
341 ****************************************************************************/
342 int cli_list_old(struct cli_state *cli,const char *Mask,uint16 attribute,
343 void (*fn)(file_info *, const char *))
349 int num_asked = (cli->max_xmit - 100)/DIR_STRUCT_SIZE;
350 int num_received = 0;
352 char *dirlist = NULL;
360 memset(cli->outbuf,'\0',smb_size);
361 memset(cli->inbuf,'\0',smb_size);
364 set_message(cli->outbuf,2,5 + strlen(mask),True);
366 set_message(cli->outbuf,2,5 + 21,True);
368 CVAL(cli->outbuf,smb_com) = SMBffirst;
370 SSVAL(cli->outbuf,smb_tid,cli->cnum);
371 cli_setup_packet(cli);
373 SSVAL(cli->outbuf,smb_vwv0,num_asked);
374 SSVAL(cli->outbuf,smb_vwv1,attribute);
376 p = smb_buf(cli->outbuf);
395 if (!cli_receive_smb(cli)) break;
397 received = SVAL(cli->inbuf,smb_vwv0);
398 if (received <= 0) break;
402 dirlist = Realloc(dirlist,(num_received + received)*DIR_STRUCT_SIZE);
407 p = smb_buf(cli->inbuf) + 3;
409 memcpy(dirlist+num_received*DIR_STRUCT_SIZE,
410 p,received*DIR_STRUCT_SIZE);
412 memcpy(status,p + ((received-1)*DIR_STRUCT_SIZE),21);
414 num_received += received;
416 if (CVAL(cli->inbuf,smb_rcls) != 0) break;
420 memset(cli->outbuf,'\0',smb_size);
421 memset(cli->inbuf,'\0',smb_size);
423 set_message(cli->outbuf,2,5 + 21,True);
424 CVAL(cli->outbuf,smb_com) = SMBfclose;
425 SSVAL(cli->outbuf,smb_tid,cli->cnum);
426 cli_setup_packet(cli);
428 SSVAL(cli->outbuf, smb_vwv0, 0); /* find count? */
429 SSVAL(cli->outbuf, smb_vwv1, attribute);
431 p = smb_buf(cli->outbuf);
441 if (!cli_receive_smb(cli)) {
442 DEBUG(0,("Error closing search: %s\n",smb_errstr(cli->inbuf)));
446 for (p=dirlist,i=0;i<num_received;i++) {
448 p += interpret_short_filename(p,&finfo);
452 if (dirlist) free(dirlist);
453 return(num_received);