r23784: use the GPLv3 boilerplate as recommended by the FSF and the license text
[Samba/bb.git] / source / libsmb / clilist.c
blob31012e6011de236e727d9100d38801c8fc2acc70
1 /*
2 Unix SMB/CIFS implementation.
3 client directory list routines
4 Copyright (C) Andrew Tridgell 1994-1998
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "includes.h"
22 extern file_info def_finfo;
24 /****************************************************************************
25 Interpret a long filename structure - this is mostly guesses at the moment.
26 The length of the structure is returned
27 The structure of a long filename depends on the info level. 260 is used
28 by NT and 2 is used by OS/2
29 ****************************************************************************/
31 static size_t interpret_long_filename(struct cli_state *cli, int level,char *p,file_info *finfo,
32 uint32 *p_resume_key, DATA_BLOB *p_last_name_raw, uint32 *p_last_name_raw_len)
34 file_info finfo2;
35 int len;
36 char *base = p;
38 if (!finfo) {
39 finfo = &finfo2;
42 if (p_resume_key) {
43 *p_resume_key = 0;
45 memcpy(finfo,&def_finfo,sizeof(*finfo));
46 finfo->cli = cli;
48 switch (level) {
49 case 1: /* OS/2 understands this */
50 /* these dates are converted to GMT by
51 make_unix_date */
52 finfo->ctime_ts = convert_time_t_to_timespec(cli_make_unix_date2(cli, p+4));
53 finfo->atime_ts = convert_time_t_to_timespec(cli_make_unix_date2(cli, p+8));
54 finfo->mtime_ts = convert_time_t_to_timespec(cli_make_unix_date2(cli, p+12));
55 finfo->size = IVAL(p,16);
56 finfo->mode = CVAL(p,24);
57 len = CVAL(p, 26);
58 p += 27;
59 p += clistr_align_in(cli, p, 0);
60 /* the len+2 below looks strange but it is
61 important to cope with the differences
62 between win2000 and win9x for this call
63 (tridge) */
64 p += clistr_pull(cli, finfo->name, p,
65 sizeof(finfo->name),
66 len+2,
67 STR_TERMINATE);
68 return PTR_DIFF(p, base);
70 case 2: /* this is what OS/2 uses mostly */
71 /* these dates are converted to GMT by
72 make_unix_date */
73 finfo->ctime_ts = convert_time_t_to_timespec(cli_make_unix_date2(cli, p+4));
74 finfo->atime_ts = convert_time_t_to_timespec(cli_make_unix_date2(cli, p+8));
75 finfo->mtime_ts = convert_time_t_to_timespec(cli_make_unix_date2(cli, p+12));
76 finfo->size = IVAL(p,16);
77 finfo->mode = CVAL(p,24);
78 len = CVAL(p, 30);
79 p += 31;
80 /* check for unisys! */
81 p += clistr_pull(cli, finfo->name, p,
82 sizeof(finfo->name),
83 len,
84 STR_NOALIGN);
85 return PTR_DIFF(p, base) + 1;
87 case 260: /* NT uses this, but also accepts 2 */
89 size_t namelen, slen;
90 p += 4; /* next entry offset */
92 if (p_resume_key) {
93 *p_resume_key = IVAL(p,0);
95 p += 4; /* fileindex */
97 /* Offset zero is "create time", not "change time". */
98 p += 8;
99 finfo->atime_ts = interpret_long_date(p);
100 p += 8;
101 finfo->mtime_ts = interpret_long_date(p);
102 p += 8;
103 finfo->ctime_ts = interpret_long_date(p);
104 p += 8;
105 finfo->size = IVAL2_TO_SMB_BIG_UINT(p,0);
106 p += 8;
107 p += 8; /* alloc size */
108 finfo->mode = CVAL(p,0);
109 p += 4;
110 namelen = IVAL(p,0);
111 p += 4;
112 p += 4; /* EA size */
113 slen = SVAL(p, 0);
114 p += 2;
116 /* stupid NT bugs. grr */
117 int flags = 0;
118 if (p[1] == 0 && namelen > 1) flags |= STR_UNICODE;
119 clistr_pull(cli, finfo->short_name, p,
120 sizeof(finfo->short_name),
121 slen, flags);
123 p += 24; /* short name? */
124 clistr_pull(cli, finfo->name, p,
125 sizeof(finfo->name),
126 namelen, 0);
128 /* To be robust in the face of unicode conversion failures
129 we need to copy the raw bytes of the last name seen here.
130 Namelen doesn't include the terminating unicode null, so
131 copy it here. */
133 if (p_last_name_raw && p_last_name_raw_len) {
134 if (namelen + 2 > p_last_name_raw->length) {
135 memset(p_last_name_raw->data, '\0', sizeof(p_last_name_raw->length));
136 *p_last_name_raw_len = 0;
137 } else {
138 memcpy(p_last_name_raw->data, p, namelen);
139 SSVAL(p_last_name_raw->data, namelen, 0);
140 *p_last_name_raw_len = namelen + 2;
143 return (size_t)IVAL(base, 0);
147 DEBUG(1,("Unknown long filename format %d\n",level));
148 return (size_t)IVAL(base,0);
151 /****************************************************************************
152 Do a directory listing, calling fn on each file found.
153 ****************************************************************************/
155 int cli_list_new(struct cli_state *cli,const char *Mask,uint16 attribute,
156 void (*fn)(const char *, file_info *, const char *, void *), void *state)
158 #if 1
159 int max_matches = 1366; /* Match W2k - was 512. */
160 #else
161 int max_matches = 512;
162 #endif
163 int info_level;
164 char *p, *p2;
165 pstring mask;
166 file_info finfo;
167 int i;
168 char *dirlist = NULL;
169 int dirlist_len = 0;
170 int total_received = -1;
171 BOOL First = True;
172 int ff_searchcount=0;
173 int ff_eos=0;
174 int ff_dir_handle=0;
175 int loop_count = 0;
176 char *rparam=NULL, *rdata=NULL;
177 unsigned int param_len, data_len;
178 uint16 setup;
179 pstring param;
180 const char *mnt;
181 uint32 resume_key = 0;
182 uint32 last_name_raw_len = 0;
183 DATA_BLOB last_name_raw = data_blob(NULL, 2*sizeof(pstring));
185 /* NT uses 260, OS/2 uses 2. Both accept 1. */
186 info_level = (cli->capabilities&CAP_NT_SMBS)?260:1;
188 pstrcpy(mask,Mask);
190 while (ff_eos == 0) {
191 loop_count++;
192 if (loop_count > 200) {
193 DEBUG(0,("Error: Looping in FIND_NEXT??\n"));
194 break;
197 if (First) {
198 setup = TRANSACT2_FINDFIRST;
199 SSVAL(param,0,attribute); /* attribute */
200 SSVAL(param,2,max_matches); /* max count */
201 SSVAL(param,4,(FLAG_TRANS2_FIND_REQUIRE_RESUME|FLAG_TRANS2_FIND_CLOSE_IF_END)); /* resume required + close on end */
202 SSVAL(param,6,info_level);
203 SIVAL(param,8,0);
204 p = param+12;
205 p += clistr_push(cli, param+12, mask, sizeof(param)-12,
206 STR_TERMINATE);
207 } else {
208 setup = TRANSACT2_FINDNEXT;
209 SSVAL(param,0,ff_dir_handle);
210 SSVAL(param,2,max_matches); /* max count */
211 SSVAL(param,4,info_level);
212 /* For W2K servers serving out FAT filesystems we *must* set the
213 resume key. If it's not FAT then it's returned as zero. */
214 SIVAL(param,6,resume_key); /* ff_resume_key */
215 /* NB. *DON'T* use continue here. If you do it seems that W2K and bretheren
216 can miss filenames. Use last filename continue instead. JRA */
217 SSVAL(param,10,(FLAG_TRANS2_FIND_REQUIRE_RESUME|FLAG_TRANS2_FIND_CLOSE_IF_END)); /* resume required + close on end */
218 p = param+12;
219 if (last_name_raw_len && (last_name_raw_len < (sizeof(param)-12))) {
220 memcpy(p, last_name_raw.data, last_name_raw_len);
221 p += last_name_raw_len;
222 } else {
223 p += clistr_push(cli, param+12, mask, sizeof(param)-12, STR_TERMINATE);
227 param_len = PTR_DIFF(p, param);
229 if (!cli_send_trans(cli, SMBtrans2,
230 NULL, /* Name */
231 -1, 0, /* fid, flags */
232 &setup, 1, 0, /* setup, length, max */
233 param, param_len, 10, /* param, length, max */
234 NULL, 0,
235 #if 0
236 /* w2k value. */
237 MIN(16384,cli->max_xmit) /* data, length, max. */
238 #else
239 cli->max_xmit /* data, length, max. */
240 #endif
241 )) {
242 break;
245 if (!cli_receive_trans(cli, SMBtrans2,
246 &rparam, &param_len,
247 &rdata, &data_len) &&
248 cli_is_dos_error(cli)) {
249 /* we need to work around a Win95 bug - sometimes
250 it gives ERRSRV/ERRerror temprarily */
251 uint8 eclass;
252 uint32 ecode;
254 SAFE_FREE(rdata);
255 SAFE_FREE(rparam);
257 cli_dos_error(cli, &eclass, &ecode);
258 if (eclass != ERRSRV || ecode != ERRerror)
259 break;
260 smb_msleep(100);
261 continue;
264 if (cli_is_error(cli) || !rdata || !rparam) {
265 SAFE_FREE(rdata);
266 SAFE_FREE(rparam);
267 break;
270 if (total_received == -1)
271 total_received = 0;
273 /* parse out some important return info */
274 p = rparam;
275 if (First) {
276 ff_dir_handle = SVAL(p,0);
277 ff_searchcount = SVAL(p,2);
278 ff_eos = SVAL(p,4);
279 } else {
280 ff_searchcount = SVAL(p,0);
281 ff_eos = SVAL(p,2);
284 if (ff_searchcount == 0) {
285 SAFE_FREE(rdata);
286 SAFE_FREE(rparam);
287 break;
290 /* point to the data bytes */
291 p = rdata;
293 /* we might need the lastname for continuations */
294 for (p2=p,i=0;i<ff_searchcount;i++) {
295 if ((info_level == 260) && (i == ff_searchcount-1)) {
296 /* Last entry - fixup the last offset length. */
297 SIVAL(p2,0,PTR_DIFF((rdata + data_len),p2));
299 p2 += interpret_long_filename(cli,info_level,p2,&finfo,
300 &resume_key,&last_name_raw,&last_name_raw_len);
302 if (!First && *mask && strcsequal(finfo.name, mask)) {
303 DEBUG(0,("Error: Looping in FIND_NEXT as name %s has already been seen?\n",
304 finfo.name));
305 ff_eos = 1;
306 break;
310 if (ff_searchcount > 0) {
311 pstrcpy(mask, finfo.name);
312 } else {
313 pstrcpy(mask,"");
316 /* grab the data for later use */
317 /* and add them to the dirlist pool */
318 dirlist = (char *)SMB_REALLOC(dirlist,dirlist_len + data_len);
320 if (!dirlist) {
321 DEBUG(0,("cli_list_new: Failed to expand dirlist\n"));
322 SAFE_FREE(rdata);
323 SAFE_FREE(rparam);
324 break;
327 memcpy(dirlist+dirlist_len,p,data_len);
328 dirlist_len += data_len;
330 total_received += ff_searchcount;
332 SAFE_FREE(rdata);
333 SAFE_FREE(rparam);
335 DEBUG(3,("received %d entries (eos=%d)\n",
336 ff_searchcount,ff_eos));
338 if (ff_searchcount > 0)
339 loop_count = 0;
341 First = False;
344 mnt = cli_cm_get_mntpoint( cli );
346 /* see if the server disconnected or the connection otherwise failed */
347 if (cli_is_error(cli)) {
348 total_received = -1;
349 } else {
350 /* no connection problem. let user function add each entry */
351 for (p=dirlist,i=0;i<total_received;i++) {
352 p += interpret_long_filename(cli, info_level, p,
353 &finfo,NULL,NULL,NULL);
354 fn( mnt,&finfo, Mask, state );
358 /* free up the dirlist buffer and last name raw blob */
359 SAFE_FREE(dirlist);
360 data_blob_free(&last_name_raw);
361 return(total_received);
364 /****************************************************************************
365 Interpret a short filename structure.
366 The length of the structure is returned.
367 ****************************************************************************/
369 static int interpret_short_filename(struct cli_state *cli, char *p,file_info *finfo)
372 *finfo = def_finfo;
374 finfo->cli = cli;
375 finfo->mode = CVAL(p,21);
377 /* this date is converted to GMT by make_unix_date */
378 finfo->ctime_ts.tv_sec = cli_make_unix_date(cli, p+22);
379 finfo->ctime_ts.tv_nsec = 0;
380 finfo->mtime_ts.tv_sec = finfo->atime_ts.tv_sec = finfo->ctime_ts.tv_sec;
381 finfo->mtime_ts.tv_nsec = finfo->atime_ts.tv_nsec = 0;
382 finfo->size = IVAL(p,26);
383 clistr_pull(cli, finfo->name, p+30, sizeof(finfo->name), 12, STR_ASCII);
384 if (strcmp(finfo->name, "..") && strcmp(finfo->name, ".")) {
385 strncpy(finfo->short_name,finfo->name, sizeof(finfo->short_name)-1);
386 finfo->short_name[sizeof(finfo->short_name)-1] = '\0';
389 return(DIR_STRUCT_SIZE);
393 /****************************************************************************
394 Do a directory listing, calling fn on each file found.
395 this uses the old SMBsearch interface. It is needed for testing Samba,
396 but should otherwise not be used.
397 ****************************************************************************/
399 int cli_list_old(struct cli_state *cli,const char *Mask,uint16 attribute,
400 void (*fn)(const char *, file_info *, const char *, void *), void *state)
402 char *p;
403 int received = 0;
404 BOOL first = True;
405 char status[21];
406 int num_asked = (cli->max_xmit - 100)/DIR_STRUCT_SIZE;
407 int num_received = 0;
408 int i;
409 char *dirlist = NULL;
410 pstring mask;
412 ZERO_ARRAY(status);
414 pstrcpy(mask,Mask);
416 while (1) {
417 memset(cli->outbuf,'\0',smb_size);
418 memset(cli->inbuf,'\0',smb_size);
420 set_message(NULL,cli->outbuf,2,0,True);
422 SCVAL(cli->outbuf,smb_com,SMBsearch);
424 SSVAL(cli->outbuf,smb_tid,cli->cnum);
425 cli_setup_packet(cli);
427 SSVAL(cli->outbuf,smb_vwv0,num_asked);
428 SSVAL(cli->outbuf,smb_vwv1,attribute);
430 p = smb_buf(cli->outbuf);
431 *p++ = 4;
433 p += clistr_push(cli, p, first?mask:"", -1, STR_TERMINATE);
434 *p++ = 5;
435 if (first) {
436 SSVAL(p,0,0);
437 p += 2;
438 } else {
439 SSVAL(p,0,21);
440 p += 2;
441 memcpy(p,status,21);
442 p += 21;
445 cli_setup_bcc(cli, p);
446 cli_send_smb(cli);
447 if (!cli_receive_smb(cli)) break;
449 received = SVAL(cli->inbuf,smb_vwv0);
450 if (received <= 0) break;
452 first = False;
454 dirlist = (char *)SMB_REALLOC(
455 dirlist,(num_received + received)*DIR_STRUCT_SIZE);
456 if (!dirlist) {
457 DEBUG(0,("cli_list_old: failed to expand dirlist"));
458 return 0;
461 p = smb_buf(cli->inbuf) + 3;
463 memcpy(dirlist+num_received*DIR_STRUCT_SIZE,
464 p,received*DIR_STRUCT_SIZE);
466 memcpy(status,p + ((received-1)*DIR_STRUCT_SIZE),21);
468 num_received += received;
470 if (cli_is_error(cli)) break;
473 if (!first) {
474 memset(cli->outbuf,'\0',smb_size);
475 memset(cli->inbuf,'\0',smb_size);
477 set_message(NULL,cli->outbuf,2,0,True);
478 SCVAL(cli->outbuf,smb_com,SMBfclose);
479 SSVAL(cli->outbuf,smb_tid,cli->cnum);
480 cli_setup_packet(cli);
482 SSVAL(cli->outbuf, smb_vwv0, 0); /* find count? */
483 SSVAL(cli->outbuf, smb_vwv1, attribute);
485 p = smb_buf(cli->outbuf);
486 *p++ = 4;
487 fstrcpy(p, "");
488 p += strlen(p) + 1;
489 *p++ = 5;
490 SSVAL(p, 0, 21);
491 p += 2;
492 memcpy(p,status,21);
493 p += 21;
495 cli_setup_bcc(cli, p);
496 cli_send_smb(cli);
497 if (!cli_receive_smb(cli)) {
498 DEBUG(0,("Error closing search: %s\n",cli_errstr(cli)));
502 for (p=dirlist,i=0;i<num_received;i++) {
503 file_info finfo;
504 p += interpret_short_filename(cli, p,&finfo);
505 fn("\\", &finfo, Mask, state);
508 SAFE_FREE(dirlist);
509 return(num_received);
512 /****************************************************************************
513 Do a directory listing, calling fn on each file found.
514 This auto-switches between old and new style.
515 ****************************************************************************/
517 int cli_list(struct cli_state *cli,const char *Mask,uint16 attribute,
518 void (*fn)(const char *, file_info *, const char *, void *), void *state)
520 if (cli->protocol <= PROTOCOL_LANMAN1)
521 return cli_list_old(cli, Mask, attribute, fn, state);
522 return cli_list_new(cli, Mask, attribute, fn, state);