1 /*
2    BAREOS® - Backup Archiving REcovery Open Sourced
3 
4    Copyright (C) 2000-2010 Free Software Foundation Europe e.V.
5    Copyright (C) 2011-2012 Planets Communications B.V.
6    Copyright (C) 2013-2016 Bareos GmbH & Co. KG
7 
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    Affero 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 /*
24  * Kern Sibbald, MM
25  */
26 /**
27  * @file
28  * This file was derived from GNU TAR source code. Except for a few key
29  * ideas, it has been entirely rewritten for Bareos.
30  *
31  * Thanks to the TAR programmers.
32  */
33 
34 #include "include/bareos.h"
35 #include "include/jcr.h"
36 #include "find.h"
37 #include "findlib/match.h"
38 #include "findlib/find_one.h"
39 #include "findlib/hardlink.h"
40 #include "findlib/fstype.h"
41 #include "findlib/drivetype.h"
42 
43 #ifdef HAVE_DARWIN_OS
44 #include <sys/param.h>
45 #include <sys/mount.h>
46 #include <sys/attr.h>
47 #endif
48 
49 extern int32_t name_max;              /* filename max length */
50 extern int32_t path_max;              /* path name max length */
51 
52 /**
53  * Create a new directory Find File packet, but copy
54  * some of the essential info from the current packet.
55  * However, be careful to zero out the rest of the
56  * packet.
57  */
new_dir_ff_pkt(FindFilesPacket * ff_pkt)58 static inline FindFilesPacket *new_dir_ff_pkt(FindFilesPacket *ff_pkt)
59 {
60    FindFilesPacket *dir_ff_pkt;
61 
62    dir_ff_pkt = (FindFilesPacket *)bmalloc(sizeof(FindFilesPacket));
63    memcpy(dir_ff_pkt, ff_pkt, sizeof(FindFilesPacket));
64    dir_ff_pkt->fname = bstrdup(ff_pkt->fname);
65    dir_ff_pkt->link = bstrdup(ff_pkt->link);
66    dir_ff_pkt->sys_fname = GetPoolMemory(PM_FNAME);
67    dir_ff_pkt->included_files_list = NULL;
68    dir_ff_pkt->excluded_files_list = NULL;
69    dir_ff_pkt->excluded_paths_list = NULL;
70    dir_ff_pkt->linkhash = NULL;
71    dir_ff_pkt->fname_save = NULL;
72    dir_ff_pkt->link_save = NULL;
73    dir_ff_pkt->ignoredir_fname = NULL;
74 
75    return dir_ff_pkt;
76 }
77 
78 /**
79  * Free the temp directory ff_pkt
80  */
FreeDirFfPkt(FindFilesPacket * dir_ff_pkt)81 static void FreeDirFfPkt(FindFilesPacket *dir_ff_pkt)
82 {
83    free(dir_ff_pkt->fname);
84    free(dir_ff_pkt->link);
85    FreePoolMemory(dir_ff_pkt->sys_fname);
86    if (dir_ff_pkt->fname_save) {
87       FreePoolMemory(dir_ff_pkt->fname_save);
88    }
89    if (dir_ff_pkt->link_save) {
90       FreePoolMemory(dir_ff_pkt->link_save);
91    }
92    if (dir_ff_pkt->ignoredir_fname) {
93       FreePoolMemory(dir_ff_pkt->ignoredir_fname);
94    }
95    free(dir_ff_pkt);
96 }
97 
98 /**
99  * Check to see if we allow the file system type of a file or directory.
100  * If we do not have a list of file system types, we accept anything.
101  */
102 #if defined(HAVE_WIN32)
AcceptFstype(FindFilesPacket * ff,void * dummy)103 static bool AcceptFstype(FindFilesPacket *ff, void *dummy)
104 {
105    return true;
106 }
107 #else
AcceptFstype(FindFilesPacket * ff,void * dummy)108 static bool AcceptFstype(FindFilesPacket *ff, void *dummy)
109 {
110    int i;
111    char fs[1000];
112    bool accept = true;
113 
114    if (ff->fstypes.size()) {
115       accept = false;
116       if (!fstype(ff->fname, fs, sizeof(fs))) {
117          Dmsg1(50, "Cannot determine file system type for \"%s\"\n", ff->fname);
118       } else {
119          for (i = 0; i < ff->fstypes.size(); ++i) {
120             if (bstrcmp(fs, (char *)ff->fstypes.get(i))) {
121                Dmsg2(100, "Accepting fstype %s for \"%s\"\n", fs, ff->fname);
122                accept = true;
123                break;
124             }
125             Dmsg3(200, "fstype %s for \"%s\" does not match %s\n", fs,
126                   ff->fname, ff->fstypes.get(i));
127          }
128       }
129    }
130 
131    return accept;
132 }
133 #endif
134 
135 /**
136  * Check to see if we allow the drive type of a file or directory.
137  * If we do not have a list of drive types, we accept anything.
138  */
139 #if defined(HAVE_WIN32)
AcceptDrivetype(FindFilesPacket * ff,void * dummy)140 static inline bool AcceptDrivetype(FindFilesPacket *ff, void *dummy)
141 {
142    int i;
143    char dt[100];
144    bool accept = true;
145 
146    if (ff->drivetypes.size()) {
147       accept = false;
148       if (!Drivetype(ff->fname, dt, sizeof(dt))) {
149          Dmsg1(50, "Cannot determine drive type for \"%s\"\n", ff->fname);
150       } else {
151          for (i = 0; i < ff->drivetypes.size(); ++i) {
152             if (bstrcmp(dt, (char *)ff->drivetypes.get(i))) {
153                Dmsg2(100, "Accepting drive type %s for \"%s\"\n", dt, ff->fname);
154                accept = true;
155                break;
156             }
157             Dmsg3(200, "drive type %s for \"%s\" does not match %s\n", dt,
158                   ff->fname, ff->drivetypes.get(i));
159          }
160       }
161    }
162 
163    return accept;
164 }
165 #else
AcceptDrivetype(FindFilesPacket * ff,void * dummy)166 static inline bool AcceptDrivetype(FindFilesPacket *ff, void *dummy)
167 {
168    return true;
169 }
170 #endif
171 
172 /**
173  * This function determines whether we can use getattrlist()
174  * It's odd, but we have to use the function to determine that...
175  * Also, the man pages talk about things as if they were implemented.
176  *
177  * On Mac OS X, this succesfully differentiates between HFS+ and UFS
178  * volumes, which makes me trust it is OK for others, too.
179  */
VolumeHasAttrlist(const char * fname)180 static bool VolumeHasAttrlist(const char *fname)
181 {
182 #ifdef HAVE_DARWIN_OS
183    struct statfs st;
184    struct volinfo_struct {
185       unsigned long length;               /* Mandatory field */
186       vol_capabilities_attr_t info;       /* Volume capabilities */
187    } vol;
188    struct attrlist attrList;
189 
190    memset(&attrList, 0, sizeof(attrList));
191    attrList.bitmapcount = ATTR_BIT_MAP_COUNT;
192    attrList.volattr = ATTR_VOL_INFO | ATTR_VOL_CAPABILITIES;
193    if (statfs(fname, &st) == 0) {
194       /*
195        * We need to check on the mount point
196        */
197       if (getattrlist(st.f_mntonname, &attrList, &vol, sizeof(vol), FSOPT_NOFOLLOW) == 0 &&
198          (vol.info.capabilities[VOL_CAPABILITIES_INTERFACES] & VOL_CAP_INT_ATTRLIST) &&
199          (vol.info.valid[VOL_CAPABILITIES_INTERFACES] & VOL_CAP_INT_ATTRLIST)) {
200          return true;
201       }
202    }
203 #endif
204    return false;
205 }
206 
207 /**
208  * check for BSD nodump flag
209  */
no_dump(JobControlRecord * jcr,FindFilesPacket * ff_pkt)210 static inline bool no_dump(JobControlRecord *jcr, FindFilesPacket *ff_pkt)
211 {
212 #if defined(HAVE_CHFLAGS) && defined(UF_NODUMP)
213    if (BitIsSet(FO_HONOR_NODUMP, ff_pkt->flags) &&
214        (ff_pkt->statp.st_flags & UF_NODUMP) ) {
215       Jmsg(jcr, M_INFO, 1, _("     NODUMP flag set - will not process %s\n"),
216            ff_pkt->fname);
217       return true;                    /* do not backup this file */
218    }
219 #endif
220    return false;                      /* do backup */
221 }
222 
223 /**
224  * check for sizes
225  */
CheckSizeMatching(JobControlRecord * jcr,FindFilesPacket * ff_pkt)226 static inline bool CheckSizeMatching(JobControlRecord *jcr, FindFilesPacket *ff_pkt)
227 {
228    int64_t begin_size, end_size, difference;
229 
230    /*
231     * See if size matching is turned on.
232     */
233    if (!ff_pkt->size_match) {
234       return true;
235    }
236 
237    /*
238     * Loose the unsigned bits to keep the compiler from warning
239     * about comparing signed and unsigned. As a size of a file
240     * can only be positive the unsigned is not really to interesting.
241     */
242    begin_size = ff_pkt->size_match->begin_size;
243    end_size = ff_pkt->size_match->end_size;
244 
245    /*
246     * See what kind of matching should be done.
247     */
248    switch (ff_pkt->size_match->type) {
249    case size_match_approx:
250       /*
251        * Calculate the fraction this size is of the wanted size.
252        */
253       if ((int64_t)ff_pkt->statp.st_size > begin_size) {
254          difference = ff_pkt->statp.st_size - begin_size;
255       } else {
256          difference = begin_size - ff_pkt->statp.st_size;
257       }
258 
259       /*
260        * See if the difference is less then 1% of the total.
261        */
262       return (difference < (begin_size / 100));
263    case size_match_smaller:
264       return (int64_t)ff_pkt->statp.st_size < begin_size;
265    case size_match_greater:
266       return (int64_t)ff_pkt->statp.st_size > begin_size;
267    case size_match_range:
268       return ((int64_t)ff_pkt->statp.st_size >= begin_size) &&
269              ((int64_t)ff_pkt->statp.st_size <= end_size);
270    default:
271       return true;
272    }
273 }
274 
275 /**
276  * Check if a file have changed during backup and display an error
277  */
HasFileChanged(JobControlRecord * jcr,FindFilesPacket * ff_pkt)278 bool HasFileChanged(JobControlRecord *jcr, FindFilesPacket *ff_pkt)
279 {
280    struct stat statp;
281    Dmsg1(500, "HasFileChanged fname=%s\n",ff_pkt->fname);
282 
283    if (ff_pkt->type != FT_REG) { /* not a regular file */
284       return false;
285    }
286 
287    if (lstat(ff_pkt->fname, &statp) != 0) {
288       BErrNo be;
289       Jmsg(jcr, M_WARNING, 0,
290            _("Cannot stat file %s: ERR=%s\n"),ff_pkt->fname,be.bstrerror());
291       return true;
292    }
293 
294    if (statp.st_mtime != ff_pkt->statp.st_mtime) {
295       Jmsg(jcr, M_ERROR, 0, _("%s: mtime changed during backup.\n"), ff_pkt->fname);
296       Dmsg3(50, "%s mtime (%lld) changed during backup (%lld).\n", ff_pkt->fname,
297             (int64_t)ff_pkt->statp.st_mtime, (int64_t)statp.st_mtime);
298       return true;
299    }
300 
301    if (statp.st_ctime != ff_pkt->statp.st_ctime) {
302       Jmsg(jcr, M_ERROR, 0, _("%s: ctime changed during backup.\n"), ff_pkt->fname);
303       Dmsg3(50, "%s ctime (%lld) changed during backup (%lld).\n", ff_pkt->fname,
304             (int64_t)ff_pkt->statp.st_ctime, (int64_t)statp.st_ctime);
305       return true;
306    }
307 
308    if (statp.st_size != ff_pkt->statp.st_size) {
309       /* TODO: add size change */
310       Jmsg(jcr, M_ERROR, 0, _("%s: size changed during backup.\n"),ff_pkt->fname);
311       Dmsg3(50, "%s size (%lld) changed during backup (%lld).\n", ff_pkt->fname,
312             (int64_t)ff_pkt->statp.st_size, (int64_t)statp.st_size);
313       return true;
314    }
315 
316    if ((statp.st_blksize != ff_pkt->statp.st_blksize) ||
317        (statp.st_blocks  != ff_pkt->statp.st_blocks)) {
318       Jmsg(jcr, M_ERROR, 0, _("%s: size changed during backup.\n"),ff_pkt->fname);
319       Dmsg3(50, "%s size (%lld) changed during backup (%lld).\n", ff_pkt->fname,
320             (int64_t)ff_pkt->statp.st_blocks, (int64_t)statp.st_blocks);
321       return true;
322    }
323 
324    return false;
325 }
326 
327 /**
328  * For incremental/diffential or accurate backups, we
329  * determine if the current file has changed.
330  */
CheckChanges(JobControlRecord * jcr,FindFilesPacket * ff_pkt)331 bool CheckChanges(JobControlRecord *jcr, FindFilesPacket *ff_pkt)
332 {
333    /*
334     * In special mode (like accurate backup), the programmer can
335     * choose his comparison function.
336     */
337    if (ff_pkt->CheckFct) {
338       return ff_pkt->CheckFct(jcr, ff_pkt);
339    }
340 
341    /*
342     * For normal backups (incr/diff), we use this default behaviour
343     */
344    if (ff_pkt->incremental &&
345       (ff_pkt->statp.st_mtime < ff_pkt->save_time &&
346       (BitIsSet(FO_MTIMEONLY, ff_pkt->flags) ||
347        ff_pkt->statp.st_ctime < ff_pkt->save_time))) {
348       return false;
349    }
350 
351    return true;
352 }
353 
HaveIgnoredir(FindFilesPacket * ff_pkt)354 static inline bool HaveIgnoredir(FindFilesPacket *ff_pkt)
355 {
356    struct stat sb;
357    char *ignoredir;
358 
359    /*
360     * Ensure that pointers are defined
361     */
362    if (!ff_pkt->fileset || !ff_pkt->fileset->incexe) {
363       return false;
364    }
365 
366    for (int i = 0; i < ff_pkt->fileset->incexe->ignoredir.size(); i++) {
367       ignoredir = (char *)ff_pkt->fileset->incexe->ignoredir.get(i);
368 
369       if (ignoredir) {
370          if (!ff_pkt->ignoredir_fname) {
371             ff_pkt->ignoredir_fname = GetPoolMemory(PM_FNAME);
372          }
373          Mmsg(ff_pkt->ignoredir_fname, "%s/%s", ff_pkt->fname, ignoredir);
374          if (stat(ff_pkt->ignoredir_fname, &sb) == 0) {
375             Dmsg2(100, "Directory '%s' ignored (found %s)\n",
376                   ff_pkt->fname, ignoredir);
377             return true;      /* Just ignore this directory */
378          }
379       }
380    }
381 
382    return false;
383 }
384 
385 /**
386  * Restore file times.
387  */
RestoreFileTimes(FindFilesPacket * ff_pkt,char * fname)388 static inline void RestoreFileTimes(FindFilesPacket *ff_pkt, char *fname)
389 {
390 #if defined(HAVE_LUTIMES)
391    struct timeval restore_times[2];
392 
393    restore_times[0].tv_sec = ff_pkt->statp.st_atime;
394    restore_times[0].tv_usec = 0;
395    restore_times[1].tv_sec = ff_pkt->statp.st_mtime;
396    restore_times[1].tv_usec = 0;
397 
398    lutimes(fname, restore_times);
399 #elif defined(HAVE_UTIMES)
400    struct timeval restore_times[2];
401 
402    restore_times[0].tv_sec = ff_pkt->statp.st_atime;
403    restore_times[0].tv_usec = 0;
404    restore_times[1].tv_sec = ff_pkt->statp.st_mtime;
405    restore_times[1].tv_usec = 0;
406 
407    utimes(fname, restore_times);
408 #else
409    struct utimbuf restore_times;
410 
411    restore_times.actime = ff_pkt->statp.st_atime;
412    restore_times.modtime = ff_pkt->statp.st_mtime;
413 
414    utime(fname, &restore_times);
415 #endif
416 }
417 
418 #ifdef HAVE_DARWIN_OS
419 /**
420  * Handling of a HFS+ attributes.
421  */
process_hfsattributes(JobControlRecord * jcr,FindFilesPacket * ff_pkt,int HandleFile (JobControlRecord * jcr,FindFilesPacket * ff,bool top_level),char * fname,bool top_level)422 static inline int process_hfsattributes(JobControlRecord *jcr, FindFilesPacket *ff_pkt,
423                                         int HandleFile(JobControlRecord *jcr, FindFilesPacket *ff, bool top_level),
424                                         char *fname, bool top_level)
425 {
426     /*
427      * TODO: initialise attrList once elsewhere?
428      */
429     struct attrlist attrList;
430 
431     memset(&attrList, 0, sizeof(attrList));
432     attrList.bitmapcount = ATTR_BIT_MAP_COUNT;
433     attrList.commonattr = ATTR_CMN_FNDRINFO;
434     attrList.fileattr = ATTR_FILE_RSRCLENGTH;
435     if (getattrlist(fname, &attrList, &ff_pkt->hfsinfo,
436                     sizeof(ff_pkt->hfsinfo), FSOPT_NOFOLLOW) != 0) {
437        ff_pkt->type = FT_NOSTAT;
438        ff_pkt->ff_errno = errno;
439        return HandleFile(jcr, ff_pkt, top_level);
440     }
441 
442     return -1;
443 }
444 #endif
445 
446 /**
447  * Handling of a hardlinked file.
448  */
process_hardlink(JobControlRecord * jcr,FindFilesPacket * ff_pkt,int HandleFile (JobControlRecord * jcr,FindFilesPacket * ff,bool top_level),char * fname,bool top_level,bool * done)449 static inline int process_hardlink(JobControlRecord *jcr, FindFilesPacket *ff_pkt,
450                                    int HandleFile(JobControlRecord *jcr, FindFilesPacket *ff, bool top_level),
451                                    char *fname, bool top_level, bool *done)
452 {
453    int rtn_stat = 0;
454    CurLink *hl;
455 
456    hl = lookup_hardlink(jcr, ff_pkt, ff_pkt->statp.st_ino, ff_pkt->statp.st_dev);
457    if (hl) {
458       /*
459        * If we have already backed up the hard linked file don't do it again
460        */
461       if (bstrcmp(hl->name, fname)) {
462          Dmsg2(400, "== Name identical skip FI=%d file=%s\n", hl->FileIndex, fname);
463          *done = true;
464          return 1; /* ignore */
465       }
466 
467       ff_pkt->link = hl->name;
468       ff_pkt->type = FT_LNKSAVED; /* Handle link, file already saved */
469       ff_pkt->LinkFI = hl->FileIndex;
470       ff_pkt->linked = NULL;
471       ff_pkt->digest = hl->digest;
472       ff_pkt->digest_stream = hl->digest_stream;
473       ff_pkt->digest_len = hl->digest_len;
474 
475       rtn_stat = HandleFile(jcr, ff_pkt, top_level);
476       Dmsg3(400, "FT_LNKSAVED FI=%d LinkFI=%d file=%s\n", ff_pkt->FileIndex, hl->FileIndex, hl->name);
477       *done = true;
478    } else {
479       /*
480        * File not previously dumped. Chain it into our list.
481        */
482       hl = new_hardlink(jcr, ff_pkt, fname, ff_pkt->statp.st_ino, ff_pkt->statp.st_dev);
483       ff_pkt->linked = hl;              /* Mark saved link */
484       Dmsg2(400, "Added to hash FI=%d file=%s\n", ff_pkt->FileIndex, hl->name);
485       *done = false;
486    }
487 
488    return rtn_stat;
489 }
490 
491 /**
492  * Handling of a regular file.
493  */
process_regular_file(JobControlRecord * jcr,FindFilesPacket * ff_pkt,int HandleFile (JobControlRecord * jcr,FindFilesPacket * ff,bool top_level),char * fname,bool top_level)494 static inline int process_regular_file(JobControlRecord *jcr, FindFilesPacket *ff_pkt,
495                                        int HandleFile(JobControlRecord *jcr, FindFilesPacket *ff, bool top_level),
496                                        char *fname, bool top_level)
497 {
498    int rtn_stat;
499    boffset_t sizeleft;
500 
501    sizeleft = ff_pkt->statp.st_size;
502 
503    /*
504     * Don't bother opening empty, world readable files. Also do not open
505     * files when archive is meant for /dev/null.
506     */
507    if (ff_pkt->null_output_device || (sizeleft == 0 &&
508        MODE_RALL == (MODE_RALL & ff_pkt->statp.st_mode))) {
509       ff_pkt->type = FT_REGE;
510    } else {
511       ff_pkt->type = FT_REG;
512    }
513 
514    rtn_stat = HandleFile(jcr, ff_pkt, top_level);
515    if (ff_pkt->linked) {
516       ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
517    }
518 
519    Dmsg3(400, "FT_REG FI=%d linked=%d file=%s\n",
520          ff_pkt->FileIndex, ff_pkt->linked ? 1 : 0, fname);
521 
522    if (BitIsSet(FO_KEEPATIME, ff_pkt->flags)) {
523       RestoreFileTimes(ff_pkt, fname);
524    }
525 
526    return rtn_stat;
527 }
528 
529 /**
530  * Handling of a symlink.
531  */
process_symlink(JobControlRecord * jcr,FindFilesPacket * ff_pkt,int HandleFile (JobControlRecord * jcr,FindFilesPacket * ff,bool top_level),char * fname,bool top_level)532 static inline int process_symlink(JobControlRecord *jcr, FindFilesPacket *ff_pkt,
533                                   int HandleFile(JobControlRecord *jcr, FindFilesPacket *ff, bool top_level),
534                                   char *fname, bool top_level)
535 {
536    int rtn_stat;
537    int size;
538    char *buffer = (char *)alloca(path_max + name_max + 102);
539 
540    size = readlink(fname, buffer, path_max + name_max + 101);
541    if (size < 0) {
542       /*
543        * Could not follow link
544        */
545       ff_pkt->type = FT_NOFOLLOW;
546       ff_pkt->ff_errno = errno;
547       rtn_stat = HandleFile(jcr, ff_pkt, top_level);
548       if (ff_pkt->linked) {
549          ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
550       }
551       return rtn_stat;
552    }
553 
554    buffer[size] = 0;
555    ff_pkt->link = buffer;          /* point to link */
556    ff_pkt->type = FT_LNK;          /* got a real link */
557 
558    rtn_stat = HandleFile(jcr, ff_pkt, top_level);
559    if (ff_pkt->linked) {
560       ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
561    }
562 
563    return rtn_stat;
564 }
565 
566 /**
567  * Handling of a directory.
568  */
process_directory(JobControlRecord * jcr,FindFilesPacket * ff_pkt,int HandleFile (JobControlRecord * jcr,FindFilesPacket * ff,bool top_level),char * fname,dev_t parent_device,bool top_level)569 static inline int process_directory(JobControlRecord *jcr, FindFilesPacket *ff_pkt,
570                                     int HandleFile(JobControlRecord *jcr, FindFilesPacket *ff, bool top_level),
571                                     char *fname, dev_t parent_device, bool top_level)
572 {
573    int rtn_stat;
574    DIR *directory;
575    struct dirent *result;
576 #ifdef USE_READDIR_R
577    struct dirent *entry;
578 #endif
579    char *link;
580    int link_len;
581    int len;
582    dev_t our_device = ff_pkt->statp.st_dev;
583    bool recurse = true;
584    bool volhas_attrlist = ff_pkt->volhas_attrlist;    /* Remember this if we recurse */
585 
586    /*
587     * Ignore this directory and everything below if the file .nobackup
588     * (or what is defined for IgnoreDir in this fileset) exists
589     */
590    if (HaveIgnoredir(ff_pkt)) {
591       return 1; /* Just ignore this directory */
592    }
593 
594    /*
595     * Build a canonical directory name with a trailing slash in link var
596     */
597    len = strlen(fname);
598    link_len = len + 200;
599    link = (char *)bmalloc(link_len + 2);
600    bstrncpy(link, fname, link_len);
601 
602    /*
603     * Strip all trailing slashes
604     */
605    while (len >= 1 && IsPathSeparator(link[len - 1])) {
606      len--;
607    }
608    link[len++] = '/';             /* add back one */
609    link[len] = 0;
610 
611    ff_pkt->link = link;
612    if (!CheckChanges(jcr, ff_pkt)) {
613       /*
614        * Incremental/Full+Base option, directory entry not changed
615        */
616       ff_pkt->type = FT_DIRNOCHG;
617    } else {
618       ff_pkt->type = FT_DIRBEGIN;
619    }
620 
621    bool is_win32_mount_point = false;
622 #if defined(HAVE_WIN32)
623    is_win32_mount_point = ff_pkt->statp.st_rdev & FILE_ATTRIBUTE_VOLUME_MOUNT_POINT;
624 
625    if (ff_pkt->statp.st_rdev & FILE_ATTRIBUTE_REPARSE_POINT) {
626       ff_pkt->type = FT_REPARSE;
627    }
628 
629    /* treat win32 mount points (Volume Mount Points) as directories */
630    if (is_win32_mount_point) {
631       ff_pkt->type = FT_DIRBEGIN;
632    }
633 #endif
634 
635    /*
636     * Note, we return the directory to the calling program (HandleFile)
637     * when we first see the directory (FT_DIRBEGIN.
638     * This allows the program to apply matches and make a
639     * choice whether or not to accept it.  If it is accepted, we
640     * do not immediately save it, but do so only after everything
641     * in the directory is seen (i.e. the FT_DIREND).
642     */
643    rtn_stat = HandleFile(jcr, ff_pkt, top_level);
644    if (rtn_stat < 1 ||
645        ff_pkt->type == FT_REPARSE)  {   /* ignore or error status */
646       free(link);
647 
648       return rtn_stat;
649    }
650 
651    /*
652     * Done with DIRBEGIN, next call will be DIREND
653     */
654    if (ff_pkt->type == FT_DIRBEGIN) {
655       ff_pkt->type = FT_DIREND;
656    }
657 
658    /*
659     * Create a temporary ff packet for this directory
660     * entry, and defer handling the directory until
661     * we have recursed into it.  This saves the
662     * directory after all files have been processed, and
663     * during the restore, the directory permissions will
664     * be reset after all the files have been restored.
665     */
666    Dmsg1(300, "Create temp ff packet for dir: %s\n", ff_pkt->fname);
667    FindFilesPacket *dir_ff_pkt = new_dir_ff_pkt(ff_pkt);
668 
669    /*
670     * Do not descend into subdirectories (recurse) if the
671     * user has turned it off for this directory.
672     *
673     * If we are crossing file systems, we are either not allowed
674     * to cross, or we may be restricted by a list of permitted
675     * file systems.
676     */
677    if (!top_level && BitIsSet(FO_NO_RECURSION, ff_pkt->flags)) {
678       ff_pkt->type = FT_NORECURSE;
679       recurse = false;
680    } else if (!top_level && (parent_device != ff_pkt->statp.st_dev ||
681               is_win32_mount_point)) {
682       if(!BitIsSet(FO_MULTIFS, ff_pkt->flags)) {
683          ff_pkt->type = FT_NOFSCHG;
684          recurse = false;
685       } else if (!AcceptFstype(ff_pkt, NULL)) {
686          ff_pkt->type = FT_INVALIDFS;
687          recurse = false;
688       } else {
689          ff_pkt->volhas_attrlist = VolumeHasAttrlist(fname);
690       }
691    }
692 
693    /*
694     * If not recursing, just backup dir and return
695     */
696    if (!recurse) {
697       rtn_stat = HandleFile(jcr, ff_pkt, top_level);
698       if (ff_pkt->linked) {
699          ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
700       }
701       free(link);
702       FreeDirFfPkt(dir_ff_pkt);
703       ff_pkt->link = ff_pkt->fname;     /* reset "link" */
704       if (BitIsSet(FO_KEEPATIME, ff_pkt->flags)) {
705          RestoreFileTimes(ff_pkt, fname);
706       }
707       return rtn_stat;
708    }
709 
710    ff_pkt->link = ff_pkt->fname;     /* reset "link" */
711 
712    /*
713     * Descend into or "recurse" into the directory to read all the files in it.
714     */
715    errno = 0;
716    if ((directory = opendir(fname)) == NULL) {
717       ff_pkt->type = FT_NOOPEN;
718       ff_pkt->ff_errno = errno;
719       rtn_stat = HandleFile(jcr, ff_pkt, top_level);
720       if (ff_pkt->linked) {
721          ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
722       }
723       free(link);
724       FreeDirFfPkt(dir_ff_pkt);
725       return rtn_stat;
726    }
727 
728    /*
729     * Process all files in this directory entry (recursing).
730     * This would possibly run faster if we chdir to the directory
731     * before traversing it.
732     */
733    rtn_stat = 1;
734 
735    /*
736     * Allocate some extra room so an overflow of the d_name with more then
737     * name_max bytes doesn't kill us right away. We check in the loop if
738     * an overflow has not happened.
739     */
740 #ifdef USE_READDIR_R
741    int status;
742 
743    entry = (struct dirent *)malloc(sizeof(struct dirent) + name_max + 100);
744    while (!JobCanceled(jcr)) {
745       int name_length;
746 
747       status = Readdir_r(directory, entry, &result);
748       if (status != 0 || result == NULL) {
749          break;
750       }
751 
752       name_length = (int)NAMELEN(entry);
753 
754       /*
755        * Some filesystems violate against the rules and return filenames
756        * longer than _PC_NAME_MAX. Log the error and continue.
757        */
758       if ((name_max + 1) <= ((int)sizeof(struct dirent) + name_length)) {
759          Jmsg2(jcr, M_ERROR, 0, _("%s: File name too long [%d]\n"), entry->d_name, name_length);
760          continue;
761       }
762 
763       /*
764        * Skip `.', `..', and excluded file names.
765        */
766       if (entry->d_name[0] == '\0' ||
767          (entry->d_name[0] == '.' && (entry->d_name[1] == '\0' ||
768          (entry->d_name[1] == '.' && entry->d_name[2] == '\0')))) {
769          continue;
770       }
771 
772       /*
773        * Make sure there is enough room to store the whole name.
774        */
775       if (name_length + len >= link_len) {
776          link_len = len + name_length + 1;
777          link = (char *)brealloc(link, link_len + 1);
778       }
779 
780       memcpy(link + len, entry->d_name, name_length);
781       link[len + name_length] = '\0';
782 
783       if (!FileIsExcluded(ff_pkt, link)) {
784          rtn_stat = FindOneFile(jcr, ff_pkt, HandleFile, link, our_device, false);
785          if (ff_pkt->linked) {
786             ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
787          }
788       }
789    }
790 
791    closedir(directory);
792    free(link);
793    free(entry);
794 
795 #else
796 
797    while (!JobCanceled(jcr)) {
798       int name_length;
799       result = readdir(directory);
800       if (result == NULL) {
801          break;
802       }
803 
804       name_length = (int)NAMELEN(result);
805 
806       /*
807        * Some filesystems violate against the rules and return filenames
808        * longer than _PC_NAME_MAX. Log the error and continue.
809        */
810       if ((name_max + 1) <= ((int)sizeof(struct dirent) + name_length)) {
811          Jmsg2(jcr, M_ERROR, 0, _("%s: File name too long [%d]\n"), result->d_name, name_length);
812          continue;
813       }
814 
815       /*
816        * Skip `.', `..', and excluded file names.
817        */
818       if (result->d_name[0] == '\0' ||
819          (result->d_name[0] == '.' && (result->d_name[1] == '\0' ||
820          (result->d_name[1] == '.' && result->d_name[2] == '\0')))) {
821          continue;
822       }
823 
824       /*
825        * Make sure there is enough room to store the whole name.
826        */
827       if (name_length + len >= link_len) {
828          link_len = len + name_length + 1;
829          link = (char *)brealloc(link, link_len + 1);
830       }
831 
832       memcpy(link + len, result->d_name, name_length);
833       link[len + name_length] = '\0';
834 
835       if (!FileIsExcluded(ff_pkt, link)) {
836          rtn_stat = FindOneFile(jcr, ff_pkt, HandleFile, link, our_device, false);
837          if (ff_pkt->linked) {
838             ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
839          }
840       }
841    }
842 
843    closedir(directory);
844    free(link);
845 #endif
846    /*
847     * Now that we have recursed through all the files in the
848     * directory, we "save" the directory so that after all
849     * the files are restored, this entry will serve to reset
850     * the directory modes and dates.  Temp directory values
851     * were used without this record.
852     */
853    HandleFile(jcr, dir_ff_pkt, top_level);       /* handle directory entry */
854    if (ff_pkt->linked) {
855       ff_pkt->linked->FileIndex = dir_ff_pkt->FileIndex;
856    }
857    FreeDirFfPkt(dir_ff_pkt);
858 
859    if (BitIsSet(FO_KEEPATIME, ff_pkt->flags)) {
860       RestoreFileTimes(ff_pkt, fname);
861    }
862    ff_pkt->volhas_attrlist = volhas_attrlist;      /* Restore value in case it changed. */
863 
864    return rtn_stat;
865 }
866 
867 /**
868  * Handling of a special file.
869  */
process_special_file(JobControlRecord * jcr,FindFilesPacket * ff_pkt,int HandleFile (JobControlRecord * jcr,FindFilesPacket * ff,bool top_level),char * fname,bool top_level)870 static inline int process_special_file(JobControlRecord *jcr, FindFilesPacket *ff_pkt,
871                                        int HandleFile(JobControlRecord *jcr, FindFilesPacket *ff, bool top_level),
872                                        char *fname, bool top_level)
873 {
874    int rtn_stat;
875 
876    /*
877     * If it is explicitly mentioned (i.e. top_level) and is
878     * a block device, we do a raw backup of it or if it is
879     * a fifo, we simply read it.
880     */
881 #ifdef HAVE_FREEBSD_OS
882    /*
883     * On FreeBSD, all block devices are character devices, so
884     * to be able to read a raw disk, we need the check for
885     * a character device.
886     *
887     * crw-r----- 1 root  operator - 116, 0x00040002 Jun 9 19:32 /dev/ad0s3
888     * crw-r----- 1 root  operator - 116, 0x00040002 Jun 9 19:32 /dev/rad0s3
889     */
890    if (top_level && (S_ISBLK(ff_pkt->statp.st_mode) || S_ISCHR(ff_pkt->statp.st_mode))) {
891 #else
892    if (top_level && S_ISBLK(ff_pkt->statp.st_mode)) {
893 #endif
894       ff_pkt->type = FT_RAW;          /* raw partition */
895    } else if (top_level &&
896               S_ISFIFO(ff_pkt->statp.st_mode) &&
897               BitIsSet(FO_READFIFO, ff_pkt->flags)) {
898       ff_pkt->type = FT_FIFO;
899    } else {
900       /*
901        * The only remaining types are special (character, ...) files
902        */
903       ff_pkt->type = FT_SPEC;
904    }
905 
906    rtn_stat = HandleFile(jcr, ff_pkt, top_level);
907    if (ff_pkt->linked) {
908      ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
909    }
910 
911    return rtn_stat;
912 }
913 
914 /**
915  * See if we need to perform any processing for a given file.
916  */
917 static inline bool NeedsProcessing(JobControlRecord *jcr, FindFilesPacket *ff_pkt, char *fname)
918 {
919    int loglevel = M_INFO;
920 
921    if (!AcceptFstype(ff_pkt, NULL)) {
922       char fs[100];
923 
924       ff_pkt->type = FT_INVALIDFS;
925       if (BitIsSet(FO_KEEPATIME, ff_pkt->flags)) {
926          RestoreFileTimes(ff_pkt, fname);
927       }
928 
929       if (!fstype(ff_pkt->fname, fs, sizeof(fs))) {
930          bstrncpy(fs, "unknown", sizeof(fs));
931          loglevel = M_WARNING;
932       }
933 
934       Jmsg(jcr, loglevel, 0, _("Top level directory \"%s\" has unlisted fstype \"%s\"\n"), fname, fs);
935       return false;  /* Just ignore this error - or the whole backup is cancelled */
936    }
937 
938    if (!AcceptDrivetype(ff_pkt, NULL)) {
939       char dt[100];
940 
941       ff_pkt->type = FT_INVALIDDT;
942       if (BitIsSet(FO_KEEPATIME, ff_pkt->flags)) {
943          RestoreFileTimes(ff_pkt, fname);
944       }
945 
946       if (!Drivetype(ff_pkt->fname, dt, sizeof(dt))) {
947          bstrncpy(dt, "unknown", sizeof(dt));
948          loglevel = M_WARNING;
949       }
950 
951       Jmsg(jcr, loglevel, 0, _("Top level directory \"%s\" has an unlisted drive type \"%s\"\n"), fname, dt);
952       return false;  /* Just ignore this error - or the whole backup is cancelled */
953    }
954 
955    ff_pkt->volhas_attrlist = VolumeHasAttrlist(fname);
956 
957    return true;
958 }
959 
960 /**
961  * Find a single file.
962  *
963  * HandleFile is the callback for handling the file.
964  *    p is the filename
965  *    parent_device is the device we are currently on
966  *    top_level is 1 when not recursing or 0 when descending into a directory.
967  */
968 int FindOneFile(JobControlRecord *jcr, FindFilesPacket *ff_pkt,
969                   int HandleFile(JobControlRecord *jcr, FindFilesPacket *ff, bool top_level),
970                   char *fname, dev_t parent_device, bool top_level)
971 {
972    int rtn_stat;
973    bool done = false;
974 
975    ff_pkt->fname = ff_pkt->link = fname;
976    ff_pkt->type = FT_UNSET;
977    if (lstat(fname, &ff_pkt->statp) != 0) {
978        /*
979         * Cannot stat file
980         */
981        ff_pkt->type = FT_NOSTAT;
982        ff_pkt->ff_errno = errno;
983        return HandleFile(jcr, ff_pkt, top_level);
984    }
985 
986    Dmsg1(300, "File ----: %s\n", fname);
987 
988    /*
989     * We check for allowed fstypes and drivetypes at top_level and fstype change (below).
990     */
991    if (top_level) {
992       if (!NeedsProcessing(jcr, ff_pkt, fname)) {
993          return 1;
994       }
995    }
996 
997    /*
998     * Ignore this entry if no_dump() returns true
999     */
1000    if (no_dump(jcr, ff_pkt)) {
1001       Dmsg1(100, "'%s' ignored (NODUMP flag set)\n", ff_pkt->fname);
1002       return 1;
1003    }
1004 
1005    switch (ff_pkt->statp.st_mode & S_IFMT) {
1006    case S_IFDIR:
1007       break;
1008    case S_IFREG:
1009       if (!CheckSizeMatching(jcr, ff_pkt)) {
1010          Dmsg1(100, "'%s' ignored (Size doesn't match\n", ff_pkt->fname);
1011          return 1;
1012       }
1013       /*
1014        * Fall Through
1015        */
1016    default:
1017       /*
1018        * If this is an Incremental backup, see if file was modified
1019        * since our last "save_time", presumably the last Full save
1020        * or Incremental.
1021        */
1022       if (!CheckChanges(jcr, ff_pkt)) {
1023          Dmsg1(500, "Non-directory incremental: %s\n", ff_pkt->fname);
1024          ff_pkt->type = FT_NOCHG;
1025          return HandleFile(jcr, ff_pkt, top_level);
1026       }
1027       break;
1028    }
1029 
1030 #ifdef HAVE_DARWIN_OS
1031    if (BitIsSet(FO_HFSPLUS, ff_pkt->flags) &&
1032        ff_pkt->volhas_attrlist &&
1033        S_ISREG(ff_pkt->statp.st_mode)) {
1034       rtn_stat = process_hfsattributes(jcr, ff_pkt, HandleFile, fname, top_level);
1035       if (rtn_stat != -1) {
1036          return rtn_stat;
1037       }
1038    }
1039 #endif
1040 
1041    ff_pkt->LinkFI = 0;
1042    /*
1043     * Handle hard linked files
1044     *
1045     * Maintain a list of hard linked files already backed up. This
1046     * allows us to ensure that the data of each file gets backed
1047     * up only once.
1048     */
1049    if (!BitIsSet(FO_NO_HARDLINK, ff_pkt->flags) && ff_pkt->statp.st_nlink > 1) {
1050       switch (ff_pkt->statp.st_mode & S_IFMT) {
1051       case S_IFREG:
1052       case S_IFCHR:
1053       case S_IFBLK:
1054       case S_IFIFO:
1055 #ifdef S_IFSOCK
1056       case S_IFSOCK:
1057 #endif
1058          /*
1059           * Via the done variable the process_hardlink function returns
1060           * if file processing is done. If done is set to false we continue
1061           * with the normal processing of the file.
1062           */
1063          rtn_stat = process_hardlink(jcr, ff_pkt, HandleFile, fname, top_level, &done);
1064          if (done) {
1065             return rtn_stat;
1066          }
1067          break;
1068       default:
1069          ff_pkt->linked = NULL;
1070          break;
1071       }
1072    } else {
1073       ff_pkt->linked = NULL;
1074    }
1075 
1076    /*
1077     * Based on the type of file call the correct function.
1078     * This is not a link to a previously dumped file, so dump it.
1079     */
1080    switch (ff_pkt->statp.st_mode & S_IFMT) {
1081    case S_IFREG:
1082       return process_regular_file(jcr, ff_pkt, HandleFile, fname, top_level);
1083 #ifdef S_IFLNK
1084    case S_IFLNK:
1085       return process_symlink(jcr, ff_pkt, HandleFile, fname, top_level);
1086 #endif
1087    case S_IFDIR:
1088       return process_directory(jcr, ff_pkt, HandleFile, fname, parent_device, top_level);
1089    default:
1090       return process_special_file(jcr, ff_pkt, HandleFile, fname, top_level);
1091    }
1092 }
1093 
1094 int TermFindOne(FindFilesPacket *ff)
1095 {
1096    int count;
1097 
1098    if (ff->linkhash == NULL) {
1099       return 0;
1100    }
1101 
1102    count = ff->linkhash->size();
1103    ff->linkhash->destroy();
1104    free(ff->linkhash);
1105    ff->linkhash = NULL;
1106 
1107    return count;
1108 }
1109