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