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