Line data Source code
1 : /*
2 : Bacula® - The Network Backup Solution
3 :
4 : Copyright (C) 2000-2010 Free Software Foundation Europe e.V.
5 :
6 : The main author of Bacula is Kern Sibbald, with contributions from
7 : many others, a complete list can be found in the file AUTHORS.
8 : This program is Free Software; you can redistribute it and/or
9 : modify it under the terms of version three of the GNU Affero General Public
10 : License as published by the Free Software Foundation and included
11 : in the file LICENSE.
12 :
13 : This program is distributed in the hope that it will be useful, but
14 : WITHOUT ANY WARRANTY; without even the implied warranty of
15 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 : General Public License for more details.
17 :
18 : You should have received a copy of the GNU Affero General Public License
19 : along with this program; if not, write to the Free Software
20 : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 : 02110-1301, USA.
22 :
23 : Bacula® is a registered trademark of Kern Sibbald.
24 : The licensor of Bacula is the Free Software Foundation Europe
25 : (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26 : Switzerland, email:ftf@fsfeurope.org.
27 : */
28 : /*
29 : This file was derived from GNU TAR source code. Except for a few key
30 : ideas, it has been entirely rewritten for Bacula.
31 :
32 : Kern Sibbald, MM
33 :
34 : Thanks to the TAR programmers.
35 : */
36 : /*
37 : This file was derived from the findlib code from bacula-5.0.3, and
38 : heavily modified. Therefore, I have retained the bacula copyright notice.
39 : The specific bacula files were:
40 : src/findlib/find.c
41 : src/findlib/find_one.c.
42 : The comment by Kern above, about TAR, was from find_one.c.
43 :
44 : Graham Keeling, 2014.
45 : */
46 :
47 : #include "../burp.h"
48 : #include "../alloc.h"
49 : #include "../conf.h"
50 : #include "../fsops.h"
51 : #include "../linkhash.h"
52 : #include "../log.h"
53 : #include "../pathcmp.h"
54 : #include "../prepend.h"
55 : #include "../regexp.h"
56 : #include "../strlist.h"
57 : #include "find.h"
58 :
59 : #ifdef HAVE_LINUX_OS
60 : #include <sys/statfs.h>
61 : #endif
62 :
63 : static int (*send_file)(struct asfd *, FF_PKT *, struct conf **);
64 :
65 : // Initialize the find files "global" variables
66 13 : FF_PKT *find_files_init(
67 : int callback(struct asfd *asfd, FF_PKT *ff, struct conf **confs))
68 : {
69 : FF_PKT *ff;
70 :
71 13 : if(!(ff=(FF_PKT *)calloc_w(1, sizeof(FF_PKT), __func__))
72 13 : || linkhash_init())
73 0 : return NULL;
74 13 : send_file=callback;
75 :
76 13 : return ff;
77 : }
78 :
79 13 : void find_files_free(FF_PKT **ff)
80 : {
81 13 : linkhash_free();
82 13 : free_v((void **)ff);
83 13 : }
84 :
85 : // Return 1 to include the file, 0 to exclude it.
86 52 : static int in_include_ext(struct strlist *incext, const char *fname)
87 : {
88 52 : int i=0;
89 : struct strlist *l;
90 52 : const char *cp=NULL;
91 : // If not doing include_ext, let the file get backed up.
92 52 : if(!incext) return 1;
93 :
94 : // The flag of the first item contains the maximum number of characters
95 : // that need to be checked.
96 : // FIX THIS: The next two functions do things very similar to this.
97 16 : for(cp=fname+strlen(fname)-1; i<incext->flag && cp>=fname; cp--, i++)
98 : {
99 14 : if(*cp!='.') continue;
100 8 : for(l=incext; l; l=l->next)
101 7 : if(!strcasecmp(l->path, cp+1))
102 4 : return 1;
103 : // If file has no extension, it cannot be included.
104 1 : return 0;
105 : }
106 2 : return 0;
107 : }
108 :
109 122 : static int in_exclude_ext(struct strlist *excext, const char *fname)
110 : {
111 122 : int i=0;
112 : struct strlist *l;
113 122 : const char *cp=NULL;
114 : // If not doing exclude_ext, let the file get backed up.
115 122 : if(!excext) return 0;
116 :
117 : // The flag of the first item contains the maximum number of characters
118 : // that need to be checked.
119 25 : for(cp=fname+strlen(fname)-1; i<excext->flag && cp>=fname; cp--, i++)
120 : {
121 20 : if(*cp!='.') continue;
122 10 : for(l=excext; l; l=l->next)
123 8 : if(!strcasecmp(l->path, cp+1))
124 3 : return 1;
125 : // If file has no extension, it is included.
126 2 : return 0;
127 : }
128 5 : return 0;
129 : }
130 :
131 : // Returns the level of compression.
132 0 : int in_exclude_comp(struct strlist *excom, const char *fname, int compression)
133 : {
134 0 : int i=0;
135 : struct strlist *l;
136 0 : const char *cp=NULL;
137 : // If not doing compression, or there are no excludes, return
138 : // straight away.
139 0 : if(!compression || !excom) return compression;
140 :
141 : // The flag of the first item contains the maximum number of characters
142 : // that need to be checked.
143 0 : for(cp=fname+strlen(fname)-1; i<excom->flag && cp>=fname; cp--, i++)
144 : {
145 0 : if(*cp!='.') continue;
146 0 : for(l=excom; l; l=l->next)
147 0 : if(!strcasecmp(l->path, cp+1))
148 0 : return 0;
149 0 : return compression;
150 : }
151 0 : return compression;
152 : }
153 :
154 : /* Return 1 to include the file, 0 to exclude it. */
155 : /* Currently not used - it needs more thinking about. */
156 : /*
157 : int in_include_regex(struct strlist *increg, const char *fname)
158 : {
159 : // If not doing include_regex, let the file get backed up.
160 : if(!increg) return 1;
161 : for(; increg; increg=increg->next)
162 : if(regex_check(increg->re, fname))
163 : return 1;
164 : return 0;
165 : }
166 : */
167 :
168 119 : int in_exclude_regex(struct strlist *excreg, const char *fname)
169 : {
170 : // If not doing exclude_regex, let the file get backed up.
171 139 : for(; excreg; excreg=excreg->next)
172 23 : if(regex_check(excreg->re, fname))
173 3 : return 1;
174 116 : return 0;
175 : }
176 :
177 : // When recursing into directories, do not want to check the include_ext list.
178 122 : static int file_is_included_no_incext(struct conf **confs, const char *fname)
179 : {
180 122 : int ret=0;
181 122 : int longest=0;
182 122 : int matching=0;
183 122 : struct strlist *l=NULL;
184 122 : struct strlist *best=NULL;
185 :
186 244 : if(in_exclude_ext(get_strlist(confs[OPT_EXCEXT]), fname)
187 122 : || in_exclude_regex(get_strlist(confs[OPT_EXCREG]), fname))
188 6 : return 0;
189 :
190 : // Check include/exclude directories.
191 296 : for(l=get_strlist(confs[OPT_INCEXCDIR]); l; l=l->next)
192 : {
193 180 : matching=is_subdir(l->path, fname);
194 180 : if(matching>longest)
195 : {
196 134 : longest=matching;
197 134 : best=l;
198 : }
199 : }
200 116 : if(!best) ret=0;
201 116 : else ret=best->flag;
202 :
203 116 : return ret;
204 : }
205 :
206 66 : static int file_is_included(struct conf **confs,
207 : const char *fname, bool top_level)
208 : {
209 : // Always save the top level directory.
210 : // This will help in the simulation of browsing backups because it
211 : // will mean that there is always a directory before any files:
212 : // d /home/graham
213 : // f /home/graham/somefile.txt
214 : // This means that we can use the stats of the directory (/home/graham
215 : // in this example) as the stats of the parent directories (/home,
216 : // for example). Trust me on this.
217 132 : if(!top_level
218 69 : && !in_include_ext(get_strlist(confs[OPT_INCEXT]), fname)) return 0;
219 :
220 63 : return file_is_included_no_incext(confs, fname);
221 : }
222 :
223 0 : static int fs_change_is_allowed(struct conf **confs, const char *fname)
224 : {
225 : struct strlist *l;
226 0 : if(get_int(confs[OPT_CROSS_ALL_FILESYSTEMS])) return 1;
227 0 : for(l=get_strlist(confs[OPT_FSCHGDIR]); l; l=l->next)
228 0 : if(!strcmp(l->path, fname)) return 1;
229 0 : return 0;
230 : }
231 :
232 6 : static int need_to_read_fifo(struct conf **confs, const char *fname)
233 : {
234 : struct strlist *l;
235 6 : if(get_int(confs[OPT_READ_ALL_FIFOS])) return 1;
236 7 : for(l=get_strlist(confs[OPT_FIFOS]); l; l=l->next)
237 5 : if(!strcmp(l->path, fname)) return 1;
238 2 : return 0;
239 : }
240 :
241 2 : static int need_to_read_blockdev(struct conf **confs, const char *fname)
242 : {
243 : struct strlist *l;
244 2 : if(get_int(confs[OPT_READ_ALL_BLOCKDEVS])) return 1;
245 3 : for(l=get_strlist(confs[OPT_BLOCKDEVS]); l; l=l->next)
246 3 : if(!strcmp(l->path, fname)) return 1;
247 0 : return 0;
248 : }
249 :
250 25 : static int nobackup_directory(struct strlist *nobackup, const char *path)
251 : {
252 : struct stat statp;
253 60 : for(; nobackup; nobackup=nobackup->next)
254 : {
255 7 : char *fullpath=NULL;
256 7 : if(!(fullpath=prepend_s(path, nobackup->path)))
257 2 : return -1;
258 7 : if(!lstat(fullpath, &statp))
259 : {
260 2 : free_w(&fullpath);
261 2 : return 1;
262 : }
263 5 : free_w(&fullpath);
264 : }
265 23 : return 0;
266 : }
267 :
268 29 : static int file_size_match(FF_PKT *ff_pkt, struct conf **confs)
269 : {
270 : uint64_t sizeleft;
271 29 : uint64_t min_file_size=get_uint64_t(confs[OPT_MIN_FILE_SIZE]);
272 29 : uint64_t max_file_size=get_uint64_t(confs[OPT_MAX_FILE_SIZE]);
273 29 : sizeleft=(uint64_t)ff_pkt->statp.st_size;
274 :
275 29 : if(min_file_size && sizeleft<min_file_size)
276 1 : return 0;
277 28 : if(max_file_size && sizeleft>max_file_size)
278 1 : return 0;
279 27 : return 1;
280 : }
281 :
282 : // Last checks before actually processing the file system entry.
283 66 : int send_file_w(struct asfd *asfd, FF_PKT *ff, bool top_level, struct conf **confs)
284 : {
285 66 : if(!file_is_included(confs, ff->fname, top_level)) return 0;
286 :
287 : // Doing the file size match here also catches hard links.
288 126 : if(S_ISREG(ff->statp.st_mode)
289 63 : && !file_size_match(ff, confs))
290 2 : return 0;
291 :
292 : /*
293 : * Handle hard linked files
294 : * Maintain a list of hard linked files already backed up. This
295 : * allows us to ensure that the data of each file gets backed
296 : * up only once.
297 : */
298 61 : if(ff->statp.st_nlink > 1
299 28 : && (S_ISREG(ff->statp.st_mode)
300 24 : || S_ISCHR(ff->statp.st_mode)
301 24 : || S_ISBLK(ff->statp.st_mode)
302 24 : || S_ISFIFO(ff->statp.st_mode)
303 24 : || S_ISSOCK(ff->statp.st_mode)))
304 : {
305 : struct f_link *lp;
306 4 : struct f_link **bucket=NULL;
307 :
308 4 : if((lp=linkhash_search(&ff->statp, &bucket)))
309 : {
310 2 : if(!strcmp(lp->name, ff->fname)) return 0;
311 2 : ff->link=lp->name;
312 : /* Handle link, file already saved */
313 2 : ff->type=FT_LNK_H;
314 : }
315 : else
316 : {
317 2 : if(linkhash_add(ff->fname,
318 2 : &ff->statp, bucket)) return -1;
319 : }
320 : }
321 :
322 61 : return send_file(asfd, ff, confs);
323 : }
324 :
325 31 : static int found_regular_file(struct asfd *asfd,
326 : FF_PKT *ff_pkt, struct conf **confs,
327 : char *fname, bool top_level)
328 : {
329 31 : ff_pkt->type=FT_REG;
330 31 : return send_file_w(asfd, ff_pkt, top_level, confs);
331 : }
332 :
333 1 : static int found_soft_link(struct asfd *asfd, FF_PKT *ff_pkt, struct conf **confs,
334 : char *fname, bool top_level)
335 : {
336 : ssize_t size;
337 1 : char *buffer=(char *)alloca(fs_full_path_max+102);
338 :
339 1 : if((size=readlink(fname, buffer, fs_full_path_max+101))<0)
340 : {
341 : /* Could not follow link */
342 0 : ff_pkt->type=FT_NOFOLLOW;
343 : }
344 : else
345 : {
346 1 : buffer[size]=0;
347 1 : ff_pkt->link=buffer; /* point to link */
348 1 : ff_pkt->type=FT_LNK_S; /* got a soft link */
349 : }
350 1 : return send_file_w(asfd, ff_pkt, top_level, confs);
351 : }
352 :
353 13 : static int fstype_excluded(struct asfd *asfd,
354 : struct conf **confs, const char *fname)
355 : {
356 : #if defined(HAVE_LINUX_OS)
357 : struct statfs buf;
358 : struct strlist *l;
359 13 : if(statfs(fname, &buf))
360 : {
361 : logw(asfd, get_cntr(confs), "Could not statfs %s: %s\n",
362 0 : fname, strerror(errno));
363 0 : return -1;
364 : }
365 13 : for(l=get_strlist(confs[OPT_EXCFS]); l; l=l->next)
366 0 : if(l->flag==buf.f_type)
367 0 : return -1;
368 : #endif
369 13 : return 0;
370 : }
371 :
372 : #if defined(HAVE_WIN32)
373 : static void windows_reparse_point_fiddling(FF_PKT *ff_pkt)
374 : {
375 : /*
376 : * We have set st_rdev to 1 if it is a reparse point, otherwise 0,
377 : * if st_rdev is 2, it is a mount point.
378 : */
379 : /*
380 : * A reparse point (WIN32_REPARSE_POINT)
381 : * is something special like one of the following:
382 : * IO_REPARSE_TAG_DFS 0x8000000A
383 : * IO_REPARSE_TAG_DFSR 0x80000012
384 : * IO_REPARSE_TAG_HSM 0xC0000004
385 : * IO_REPARSE_TAG_HSM2 0x80000006
386 : * IO_REPARSE_TAG_SIS 0x80000007
387 : * IO_REPARSE_TAG_SYMLINK 0xA000000C
388 : *
389 : * A junction point is a:
390 : * IO_REPARSE_TAG_MOUNT_POINT 0xA0000003
391 : * which can be either a link to a Volume (WIN32_MOUNT_POINT)
392 : * or a link to a directory (WIN32_JUNCTION_POINT)
393 : *
394 : * Ignore WIN32_REPARSE_POINT and WIN32_JUNCTION_POINT
395 : */
396 : if (ff_pkt->statp.st_rdev == WIN32_REPARSE_POINT) {
397 : ff_pkt->type = FT_REPARSE;
398 : } else if (ff_pkt->statp.st_rdev == WIN32_JUNCTION_POINT) {
399 : ff_pkt->type = FT_JUNCTION;
400 : }
401 : }
402 : #endif
403 :
404 : // Prototype because process_entries_in_directory() recurses using find_files().
405 : static int find_files(struct asfd *asfd, FF_PKT *ff_pkt, struct conf **confs,
406 : char *fname, dev_t parent_device, bool top_level);
407 :
408 17 : static int process_entries_in_directory(struct asfd *asfd, struct dirent **nl,
409 : int count, char **link, size_t len, size_t *link_len,
410 : struct conf **confs, FF_PKT *ff_pkt, dev_t our_device)
411 : {
412 17 : int m=0;
413 17 : int ret=0;
414 76 : for(m=0; m<count; m++)
415 : {
416 : size_t i;
417 59 : char *p=NULL;
418 59 : char *q=NULL;
419 :
420 59 : p=nl[m]->d_name;
421 :
422 59 : if(strlen(p)+len>=*link_len)
423 : {
424 0 : *link_len=len+strlen(p)+1;
425 0 : if(!(*link=(char *)
426 0 : realloc_w(*link, (*link_len)+1, __func__)))
427 0 : return -1;
428 : }
429 59 : q=(*link)+len;
430 159 : for(i=0; i<strlen(nl[m]->d_name); i++)
431 100 : *q++=*p++;
432 59 : *q=0;
433 59 : ff_pkt->flen=i;
434 :
435 59 : if(file_is_included_no_incext(confs, *link))
436 : {
437 : ret=find_files(asfd, ff_pkt,
438 50 : confs, *link, our_device, false /*top_level*/);
439 : }
440 : else
441 : {
442 : struct strlist *x;
443 : // Excluded, but there might be a subdirectory that is
444 : // included.
445 26 : for(x=get_strlist(confs[OPT_INCEXCDIR]); x; x=x->next)
446 : {
447 17 : if(x->flag
448 17 : && is_subdir(*link, x->path))
449 : {
450 : struct strlist *y;
451 4 : if((ret=find_files(asfd, ff_pkt,
452 : confs, x->path,
453 4 : our_device, false)))
454 0 : break;
455 : // Now need to skip subdirectories of
456 : // the thing that we just stuck in
457 : // find_one_file(), or we might get
458 : // some things backed up twice.
459 6 : for(y=x->next; y; y=y->next)
460 2 : if(y->next
461 2 : && is_subdir(x->path, y->path))
462 1 : y=y->next;
463 : }
464 : }
465 : }
466 59 : free_v((void **)&(nl[m]));
467 59 : if(ret) break;
468 : }
469 17 : return ret;
470 : }
471 :
472 25 : static int found_directory(struct asfd *asfd,
473 : FF_PKT *ff_pkt, struct conf **confs,
474 : char *fname, dev_t parent_device, bool top_level)
475 : {
476 25 : int ret=-1;
477 25 : char *link=NULL;
478 : size_t link_len;
479 : size_t len;
480 25 : int nbret=0;
481 25 : int count=0;
482 : dev_t our_device;
483 25 : struct dirent **nl=NULL;
484 :
485 25 : our_device=ff_pkt->statp.st_dev;
486 :
487 50 : if((nbret=nobackup_directory(get_strlist(confs[OPT_NOBACKUP]),
488 50 : ff_pkt->fname)))
489 : {
490 2 : if(nbret<0) goto end; // Error.
491 2 : ret=0; // Do not back it up.
492 2 : goto end;
493 : }
494 :
495 : /* Build a canonical directory name with a trailing slash in link var */
496 23 : len=strlen(fname);
497 23 : link_len=len+200;
498 23 : if(!(link=(char *)malloc_w(link_len+2, __func__)))
499 0 : goto end;
500 23 : snprintf(link, link_len, "%s", fname);
501 :
502 : /* Strip all trailing slashes */
503 23 : while(len >= 1 && IsPathSeparator(link[len - 1])) len--;
504 : /* add back one */
505 23 : link[len++]='/';
506 23 : link[len]=0;
507 :
508 23 : ff_pkt->link=link;
509 23 : ff_pkt->type=FT_DIR;
510 :
511 : #if defined(HAVE_WIN32)
512 : windows_reparse_point_fiddling(ff_pkt);
513 : #endif
514 :
515 23 : if(send_file_w(asfd, ff_pkt, top_level, confs))
516 0 : goto end;
517 23 : if(ff_pkt->type==FT_REPARSE || ff_pkt->type==FT_JUNCTION)
518 : {
519 : // Ignore.
520 0 : ret=0;
521 0 : goto end;
522 : }
523 :
524 23 : if(top_level
525 10 : || (parent_device!=ff_pkt->statp.st_dev
526 : #if defined(HAVE_WIN32)
527 : || ff_pkt->statp.st_rdev==WIN32_MOUNT_POINT
528 : #endif
529 : ))
530 : {
531 13 : if(fstype_excluded(asfd, confs, ff_pkt->fname))
532 : {
533 0 : ret=send_file_w(asfd, ff_pkt, top_level, confs);
534 0 : goto end;
535 : }
536 13 : if(!top_level && !fs_change_is_allowed(confs, ff_pkt->fname))
537 : {
538 0 : ff_pkt->type=FT_NOFSCHG;
539 : // Just backup the directory and return.
540 0 : ret=send_file_w(asfd, ff_pkt, top_level, confs);
541 0 : goto end;
542 : }
543 : }
544 :
545 23 : ff_pkt->link=ff_pkt->fname;
546 :
547 23 : errno=0;
548 23 : switch(entries_in_directory_alphasort(fname,
549 23 : &nl, &count, get_int(confs[OPT_ATIME])))
550 : {
551 23 : case 0: break;
552 : case 1:
553 0 : ff_pkt->type=FT_NOOPEN;
554 0 : ret=send_file_w(asfd, ff_pkt, top_level, confs);
555 : default:
556 0 : goto end;
557 : }
558 :
559 23 : if(nl)
560 : {
561 17 : if(process_entries_in_directory(asfd, nl, count,
562 17 : &link, len, &link_len, confs, ff_pkt, our_device))
563 0 : goto end;
564 : }
565 23 : ret=0;
566 : end:
567 25 : free_w(&link);
568 25 : free_v((void **)&nl);
569 25 : return ret;
570 : }
571 :
572 9 : static int found_other(struct asfd *asfd, FF_PKT *ff_pkt, struct conf **confs,
573 : char *fname, bool top_level)
574 : {
575 : #ifdef HAVE_FREEBSD_OS
576 : /*
577 : * On FreeBSD, all block devices are character devices, so
578 : * to be able to read a raw disk, we need the check for
579 : * a character device.
580 : * crw-r----- 1 root operator - 116, 0x00040002 Jun 9 19:32 /dev/ad0s3
581 : * crw-r----- 1 root operator - 116, 0x00040002 Jun 9 19:32 /dev/rad0s3
582 : */
583 : if((S_ISBLK(ff_pkt->statp.st_mode) || S_ISCHR(ff_pkt->statp.st_mode))
584 : && need_to_read_blockdev(confs, ff_pkt->fname))
585 : {
586 : #else
587 18 : if(S_ISBLK(ff_pkt->statp.st_mode)
588 9 : && need_to_read_blockdev(confs, ff_pkt->fname))
589 : {
590 : #endif
591 : /* raw partition */
592 2 : ff_pkt->type=FT_RAW;
593 : }
594 14 : else if(S_ISFIFO(ff_pkt->statp.st_mode)
595 7 : && need_to_read_fifo(confs, ff_pkt->fname))
596 : {
597 4 : ff_pkt->type=FT_FIFO;
598 : }
599 : else
600 : {
601 : /* The only remaining are special (character, ...) files */
602 3 : ff_pkt->type=FT_SPEC;
603 : }
604 9 : return send_file_w(asfd, ff_pkt, top_level, confs);
605 : }
606 :
607 68 : static int find_files(struct asfd *asfd, FF_PKT *ff_pkt, struct conf **confs,
608 : char *fname, dev_t parent_device, bool top_level)
609 : {
610 68 : ff_pkt->fname=fname;
611 68 : ff_pkt->link=fname;
612 :
613 : #ifdef HAVE_WIN32
614 : if(win32_lstat(fname, &ff_pkt->statp, &ff_pkt->winattr))
615 : #else
616 68 : if(lstat(fname, &ff_pkt->statp))
617 : #endif
618 : {
619 2 : ff_pkt->type=FT_NOSTAT;
620 2 : return send_file_w(asfd, ff_pkt, top_level, confs);
621 : }
622 :
623 66 : if(S_ISREG(ff_pkt->statp.st_mode))
624 31 : return found_regular_file(asfd, ff_pkt, confs, fname, top_level);
625 35 : else if(S_ISLNK(ff_pkt->statp.st_mode))
626 : {
627 : #ifdef S_IFLNK
628 : /* A symlink.
629 : If they have specified the symlink in a read_blockdev
630 : argument, treat it as a block device.
631 : */
632 : struct strlist *l;
633 4 : for(l=get_strlist(confs[OPT_BLOCKDEVS]); l; l=l->next)
634 : {
635 3 : if(!strcmp(l->path, fname))
636 : {
637 2 : ff_pkt->statp.st_mode ^= S_IFLNK;
638 2 : ff_pkt->statp.st_mode |= S_IFBLK;
639 : return found_other(asfd, ff_pkt, confs, fname,
640 2 : top_level);
641 : }
642 : }
643 : #endif
644 1 : return found_soft_link(asfd, ff_pkt, confs, fname, top_level);
645 : }
646 32 : else if(S_ISDIR(ff_pkt->statp.st_mode))
647 : return found_directory(asfd, ff_pkt, confs, fname,
648 25 : parent_device, top_level);
649 : else
650 7 : return found_other(asfd, ff_pkt, confs, fname, top_level);
651 : }
652 :
653 14 : int find_files_begin(struct asfd *asfd,
654 : FF_PKT *ff_pkt, struct conf **confs, char *fname)
655 : {
656 : return find_files(asfd, ff_pkt,
657 14 : confs, fname, (dev_t)-1, 1 /* top_level */);
658 : }
|