source: branches/samba-3.0/source/libsmb/clilist.c

Last change on this file was 124, checked in by Paul Smedley, 17 years ago

Update source to 3.0.28a

File size: 15.4 KB
Line 
1/*
2 Unix SMB/CIFS implementation.
3 client directory list routines
4 Copyright (C) Andrew Tridgell 1994-1998
5
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 2 of the License, or
9 (at your option) any later version.
10
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.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19*/
20
21#include "includes.h"
22
23extern file_info def_finfo;
24
25/****************************************************************************
26 Interpret a long filename structure - this is mostly guesses at the moment.
27 The length of the structure is returned
28 The structure of a long filename depends on the info level. 260 is used
29 by NT and 2 is used by OS/2
30****************************************************************************/
31
32static size_t interpret_long_filename(struct cli_state *cli, int level,char *p,file_info *finfo,
33 uint32 *p_resume_key, DATA_BLOB *p_last_name_raw, uint32 *p_last_name_raw_len)
34{
35 file_info finfo2;
36 int len;
37 char *base = p;
38
39 if (!finfo) {
40 finfo = &finfo2;
41 }
42
43 if (p_resume_key) {
44 *p_resume_key = 0;
45 }
46 memcpy(finfo,&def_finfo,sizeof(*finfo));
47 finfo->cli = cli;
48
49 switch (level) {
50 case 1: /* OS/2 understands this */
51 /* these dates are converted to GMT by
52 make_unix_date */
53 finfo->ctime_ts = convert_time_t_to_timespec(cli_make_unix_date2(cli, p+4));
54 finfo->atime_ts = convert_time_t_to_timespec(cli_make_unix_date2(cli, p+8));
55 finfo->mtime_ts = convert_time_t_to_timespec(cli_make_unix_date2(cli, p+12));
56 finfo->size = IVAL(p,16);
57 finfo->mode = CVAL(p,24);
58 len = CVAL(p, 26);
59 p += 27;
60 p += clistr_align_in(cli, p, 0);
61 /* the len+2 below looks strange but it is
62 important to cope with the differences
63 between win2000 and win9x for this call
64 (tridge) */
65 p += clistr_pull(cli, finfo->name, p,
66 sizeof(finfo->name),
67 len+2,
68 STR_TERMINATE);
69 return PTR_DIFF(p, base);
70
71 case 2: /* this is what OS/2 uses mostly */
72 /* these dates are converted to GMT by
73 make_unix_date */
74 finfo->ctime_ts = convert_time_t_to_timespec(cli_make_unix_date2(cli, p+4));
75 finfo->atime_ts = convert_time_t_to_timespec(cli_make_unix_date2(cli, p+8));
76 finfo->mtime_ts = convert_time_t_to_timespec(cli_make_unix_date2(cli, p+12));
77 finfo->size = IVAL(p,16);
78 finfo->mode = CVAL(p,24);
79 len = CVAL(p, 30);
80 p += 31;
81 /* check for unisys! */
82 p += clistr_pull(cli, finfo->name, p,
83 sizeof(finfo->name),
84 len,
85 STR_NOALIGN);
86 return PTR_DIFF(p, base) + 1;
87
88 case 260: /* NT uses this, but also accepts 2 */
89 {
90 size_t namelen, slen;
91 p += 4; /* next entry offset */
92
93 if (p_resume_key) {
94 *p_resume_key = IVAL(p,0);
95 }
96 p += 4; /* fileindex */
97
98 /* Offset zero is "create time", not "change time". */
99 p += 8;
100 finfo->atime_ts = interpret_long_date(p);
101 p += 8;
102 finfo->mtime_ts = interpret_long_date(p);
103 p += 8;
104 finfo->ctime_ts = interpret_long_date(p);
105 p += 8;
106 finfo->size = IVAL2_TO_SMB_BIG_UINT(p,0);
107 p += 8;
108 p += 8; /* alloc size */
109 finfo->mode = CVAL(p,0);
110 p += 4;
111 namelen = IVAL(p,0);
112 p += 4;
113 p += 4; /* EA size */
114 slen = SVAL(p, 0);
115 p += 2;
116 {
117 /* stupid NT bugs. grr */
118 int flags = 0;
119 if (p[1] == 0 && namelen > 1) flags |= STR_UNICODE;
120 clistr_pull(cli, finfo->short_name, p,
121 sizeof(finfo->short_name),
122 slen, flags);
123 }
124 p += 24; /* short name? */
125 clistr_pull(cli, finfo->name, p,
126 sizeof(finfo->name),
127 namelen, 0);
128
129 /* To be robust in the face of unicode conversion failures
130 we need to copy the raw bytes of the last name seen here.
131 Namelen doesn't include the terminating unicode null, so
132 copy it here. */
133
134 if (p_last_name_raw && p_last_name_raw_len) {
135 if (namelen + 2 > p_last_name_raw->length) {
136 memset(p_last_name_raw->data, '\0', sizeof(p_last_name_raw->length));
137 *p_last_name_raw_len = 0;
138 } else {
139 memcpy(p_last_name_raw->data, p, namelen);
140 SSVAL(p_last_name_raw->data, namelen, 0);
141 *p_last_name_raw_len = namelen + 2;
142 }
143 }
144 return (size_t)IVAL(base, 0);
145 }
146 }
147
148 DEBUG(1,("Unknown long filename format %d\n",level));
149 return (size_t)IVAL(base,0);
150}
151
152/****************************************************************************
153 Do a directory listing, calling fn on each file found.
154****************************************************************************/
155
156int cli_list_new(struct cli_state *cli,const char *Mask,uint16 attribute,
157 void (*fn)(const char *, file_info *, const char *, void *), void *state)
158{
159#if 1
160 int max_matches = 1366; /* Match W2k - was 512. */
161#else
162 int max_matches = 512;
163#endif
164 int info_level;
165 char *p, *p2;
166 pstring mask;
167 file_info finfo;
168 int i;
169 char *dirlist = NULL;
170 int dirlist_len = 0;
171 int total_received = -1;
172 BOOL First = True;
173 int ff_searchcount=0;
174 int ff_eos=0;
175 int ff_dir_handle=0;
176 int loop_count = 0;
177 char *rparam=NULL, *rdata=NULL;
178 unsigned int param_len, data_len;
179 uint16 setup;
180 pstring param;
181 const char *mnt;
182 uint32 resume_key = 0;
183 uint32 last_name_raw_len = 0;
184 DATA_BLOB last_name_raw = data_blob(NULL, 2*sizeof(pstring));
185
186 /* NT uses 260, OS/2 uses 2. Both accept 1. */
187 info_level = (cli->capabilities&CAP_NT_SMBS)?260:1;
188
189 pstrcpy(mask,Mask);
190
191 while (ff_eos == 0) {
192 loop_count++;
193 if (loop_count > 200) {
194 DEBUG(0,("Error: Looping in FIND_NEXT??\n"));
195 break;
196 }
197
198 if (First) {
199 setup = TRANSACT2_FINDFIRST;
200 SSVAL(param,0,attribute); /* attribute */
201 SSVAL(param,2,max_matches); /* max count */
202 SSVAL(param,4,(FLAG_TRANS2_FIND_REQUIRE_RESUME|FLAG_TRANS2_FIND_CLOSE_IF_END)); /* resume required + close on end */
203 SSVAL(param,6,info_level);
204 SIVAL(param,8,0);
205 p = param+12;
206 p += clistr_push(cli, param+12, mask, sizeof(param)-12,
207 STR_TERMINATE);
208 } else {
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 /* For W2K servers serving out FAT filesystems we *must* set the
214 resume key. If it's not FAT then it's returned as zero. */
215 SIVAL(param,6,resume_key); /* ff_resume_key */
216 /* NB. *DON'T* use continue here. If you do it seems that W2K and bretheren
217 can miss filenames. Use last filename continue instead. JRA */
218 SSVAL(param,10,(FLAG_TRANS2_FIND_REQUIRE_RESUME|FLAG_TRANS2_FIND_CLOSE_IF_END)); /* resume required + close on end */
219 p = param+12;
220 if (last_name_raw_len && (last_name_raw_len < (sizeof(param)-12))) {
221 memcpy(p, last_name_raw.data, last_name_raw_len);
222 p += last_name_raw_len;
223 } else {
224 p += clistr_push(cli, param+12, mask, sizeof(param)-12, STR_TERMINATE);
225 }
226 }
227
228 param_len = PTR_DIFF(p, param);
229
230 if (!cli_send_trans(cli, SMBtrans2,
231 NULL, /* Name */
232 -1, 0, /* fid, flags */
233 &setup, 1, 0, /* setup, length, max */
234 param, param_len, 10, /* param, length, max */
235 NULL, 0,
236#if 0
237 /* w2k value. */
238 MIN(16384,cli->max_xmit) /* data, length, max. */
239#else
240 cli->max_xmit /* data, length, max. */
241#endif
242 )) {
243 break;
244 }
245
246 if (!cli_receive_trans(cli, SMBtrans2,
247 &rparam, &param_len,
248 &rdata, &data_len) &&
249 cli_is_dos_error(cli)) {
250 /* We need to work around a Win95 bug - sometimes
251 it gives ERRSRV/ERRerror temprarily */
252 uint8 eclass;
253 uint32 ecode;
254
255 SAFE_FREE(rdata);
256 SAFE_FREE(rparam);
257
258 cli_dos_error(cli, &eclass, &ecode);
259
260 /*
261 * OS/2 might return "no more files",
262 * which just tells us, that searchcount is zero
263 * in this search.
264 * Guenter Kukkukk <linux@kukkukk.com>
265 */
266
267 if (eclass == ERRDOS && ecode == ERRnofiles) {
268 ff_searchcount = 0;
269 cli_reset_error(cli);
270 break;
271 }
272
273 if (eclass != ERRSRV || ecode != ERRerror)
274 break;
275 smb_msleep(100);
276 continue;
277 }
278
279 if (cli_is_error(cli) || !rdata || !rparam) {
280 SAFE_FREE(rdata);
281 SAFE_FREE(rparam);
282 break;
283 }
284
285 if (total_received == -1)
286 total_received = 0;
287
288 /* parse out some important return info */
289 p = rparam;
290 if (First) {
291 ff_dir_handle = SVAL(p,0);
292 ff_searchcount = SVAL(p,2);
293 ff_eos = SVAL(p,4);
294 } else {
295 ff_searchcount = SVAL(p,0);
296 ff_eos = SVAL(p,2);
297 }
298
299 if (ff_searchcount == 0) {
300 SAFE_FREE(rdata);
301 SAFE_FREE(rparam);
302 break;
303 }
304
305 /* point to the data bytes */
306 p = rdata;
307
308 /* we might need the lastname for continuations */
309 for (p2=p,i=0;i<ff_searchcount;i++) {
310 if ((info_level == 260) && (i == ff_searchcount-1)) {
311 /* Last entry - fixup the last offset length. */
312 SIVAL(p2,0,PTR_DIFF((rdata + data_len),p2));
313 }
314 p2 += interpret_long_filename(cli,info_level,p2,&finfo,
315 &resume_key,&last_name_raw,&last_name_raw_len);
316
317 if (!First && *mask && strcsequal(finfo.name, mask)) {
318 DEBUG(0,("Error: Looping in FIND_NEXT as name %s has already been seen?\n",
319 finfo.name));
320 ff_eos = 1;
321 break;
322 }
323 }
324
325 if (ff_searchcount > 0) {
326 pstrcpy(mask, finfo.name);
327 } else {
328 pstrcpy(mask,"");
329 }
330
331 /* grab the data for later use */
332 /* and add them to the dirlist pool */
333 dirlist = (char *)SMB_REALLOC(dirlist,dirlist_len + data_len);
334
335 if (!dirlist) {
336 DEBUG(0,("cli_list_new: Failed to expand dirlist\n"));
337 SAFE_FREE(rdata);
338 SAFE_FREE(rparam);
339 break;
340 }
341
342 memcpy(dirlist+dirlist_len,p,data_len);
343 dirlist_len += data_len;
344
345 total_received += ff_searchcount;
346
347 SAFE_FREE(rdata);
348 SAFE_FREE(rparam);
349
350 DEBUG(3,("received %d entries (eos=%d)\n",
351 ff_searchcount,ff_eos));
352
353 if (ff_searchcount > 0)
354 loop_count = 0;
355
356 First = False;
357 }
358
359 mnt = cli_cm_get_mntpoint( cli );
360
361 /* see if the server disconnected or the connection otherwise failed */
362 if (cli_is_error(cli)) {
363 total_received = -1;
364 } else {
365 /* no connection problem. let user function add each entry */
366 for (p=dirlist,i=0;i<total_received;i++) {
367 p += interpret_long_filename(cli, info_level, p,
368 &finfo,NULL,NULL,NULL);
369 fn( mnt,&finfo, Mask, state );
370 }
371 }
372
373 /* free up the dirlist buffer and last name raw blob */
374 SAFE_FREE(dirlist);
375 data_blob_free(&last_name_raw);
376 return(total_received);
377}
378
379/****************************************************************************
380 Interpret a short filename structure.
381 The length of the structure is returned.
382****************************************************************************/
383
384static int interpret_short_filename(struct cli_state *cli, char *p,file_info *finfo)
385{
386
387 *finfo = def_finfo;
388
389 finfo->cli = cli;
390 finfo->mode = CVAL(p,21);
391
392 /* this date is converted to GMT by make_unix_date */
393 finfo->ctime_ts.tv_sec = cli_make_unix_date(cli, p+22);
394 finfo->ctime_ts.tv_nsec = 0;
395 finfo->mtime_ts.tv_sec = finfo->atime_ts.tv_sec = finfo->ctime_ts.tv_sec;
396 finfo->mtime_ts.tv_nsec = finfo->atime_ts.tv_nsec = 0;
397 finfo->size = IVAL(p,26);
398 clistr_pull(cli, finfo->name, p+30, sizeof(finfo->name), 12, STR_ASCII);
399 if (strcmp(finfo->name, "..") && strcmp(finfo->name, ".")) {
400 strncpy(finfo->short_name,finfo->name, sizeof(finfo->short_name)-1);
401 finfo->short_name[sizeof(finfo->short_name)-1] = '\0';
402 }
403
404 return(DIR_STRUCT_SIZE);
405}
406
407
408/****************************************************************************
409 Do a directory listing, calling fn on each file found.
410 this uses the old SMBsearch interface. It is needed for testing Samba,
411 but should otherwise not be used.
412****************************************************************************/
413
414int cli_list_old(struct cli_state *cli,const char *Mask,uint16 attribute,
415 void (*fn)(const char *, file_info *, const char *, void *), void *state)
416{
417 char *p;
418 int received = 0;
419 BOOL first = True;
420 char status[21];
421 int num_asked = (cli->max_xmit - 100)/DIR_STRUCT_SIZE;
422 int num_received = 0;
423 int i;
424 char *dirlist = NULL;
425 pstring mask;
426
427 ZERO_ARRAY(status);
428
429 pstrcpy(mask,Mask);
430
431 while (1) {
432 memset(cli->outbuf,'\0',smb_size);
433 memset(cli->inbuf,'\0',smb_size);
434
435 set_message(cli->outbuf,2,0,True);
436
437 SCVAL(cli->outbuf,smb_com,SMBsearch);
438
439 SSVAL(cli->outbuf,smb_tid,cli->cnum);
440 cli_setup_packet(cli);
441
442 SSVAL(cli->outbuf,smb_vwv0,num_asked);
443 SSVAL(cli->outbuf,smb_vwv1,attribute);
444
445 p = smb_buf(cli->outbuf);
446 *p++ = 4;
447
448 p += clistr_push(cli, p, first?mask:"", -1, STR_TERMINATE);
449 *p++ = 5;
450 if (first) {
451 SSVAL(p,0,0);
452 p += 2;
453 } else {
454 SSVAL(p,0,21);
455 p += 2;
456 memcpy(p,status,21);
457 p += 21;
458 }
459
460 cli_setup_bcc(cli, p);
461 cli_send_smb(cli);
462 if (!cli_receive_smb(cli)) break;
463
464 received = SVAL(cli->inbuf,smb_vwv0);
465 if (received <= 0) break;
466
467 first = False;
468
469 dirlist = (char *)SMB_REALLOC(
470 dirlist,(num_received + received)*DIR_STRUCT_SIZE);
471 if (!dirlist) {
472 DEBUG(0,("cli_list_old: failed to expand dirlist"));
473 return 0;
474 }
475
476 p = smb_buf(cli->inbuf) + 3;
477
478 memcpy(dirlist+num_received*DIR_STRUCT_SIZE,
479 p,received*DIR_STRUCT_SIZE);
480
481 memcpy(status,p + ((received-1)*DIR_STRUCT_SIZE),21);
482
483 num_received += received;
484
485 if (cli_is_error(cli)) break;
486 }
487
488 if (!first) {
489 memset(cli->outbuf,'\0',smb_size);
490 memset(cli->inbuf,'\0',smb_size);
491
492 set_message(cli->outbuf,2,0,True);
493 SCVAL(cli->outbuf,smb_com,SMBfclose);
494 SSVAL(cli->outbuf,smb_tid,cli->cnum);
495 cli_setup_packet(cli);
496
497 SSVAL(cli->outbuf, smb_vwv0, 0); /* find count? */
498 SSVAL(cli->outbuf, smb_vwv1, attribute);
499
500 p = smb_buf(cli->outbuf);
501 *p++ = 4;
502 fstrcpy(p, "");
503 p += strlen(p) + 1;
504 *p++ = 5;
505 SSVAL(p, 0, 21);
506 p += 2;
507 memcpy(p,status,21);
508 p += 21;
509
510 cli_setup_bcc(cli, p);
511 cli_send_smb(cli);
512 if (!cli_receive_smb(cli)) {
513 DEBUG(0,("Error closing search: %s\n",cli_errstr(cli)));
514 }
515 }
516
517 for (p=dirlist,i=0;i<num_received;i++) {
518 file_info finfo;
519 p += interpret_short_filename(cli, p,&finfo);
520 fn("\\", &finfo, Mask, state);
521 }
522
523 SAFE_FREE(dirlist);
524 return(num_received);
525}
526
527/****************************************************************************
528 Do a directory listing, calling fn on each file found.
529 This auto-switches between old and new style.
530****************************************************************************/
531
532int cli_list(struct cli_state *cli,const char *Mask,uint16 attribute,
533 void (*fn)(const char *, file_info *, const char *, void *), void *state)
534{
535 if (cli->protocol <= PROTOCOL_LANMAN1)
536 return cli_list_old(cli, Mask, attribute, fn, state);
537 return cli_list_new(cli, Mask, attribute, fn, state);
538}
Note: See TracBrowser for help on using the repository browser.