1 /*
2    Bacula(R) - The Network Backup Solution
3 
4    Copyright (C) 2000-2020 Kern Sibbald
5 
6    The original author of Bacula is Kern Sibbald, with contributions
7    from many others, a complete list can be found in the file AUTHORS.
8 
9    You may use this file and others of this release according to the
10    license defined in the LICENSE file, which includes the Affero General
11    Public License, v3.0 ("AGPLv3") and some additional permissions and
12    terms pursuant to its AGPLv3 Section 7.
13 
14    This notice must be preserved when any source code is
15    conveyed and/or propagated.
16 
17    Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19 /*
20 
21    This file was derived from GNU TAR source code. Except for a few key
22    ideas, it has been entirely rewritten for Bacula.
23 
24       Kern Sibbald, MM
25 
26    Thanks to the TAR programmers.
27 
28  */
29 
30 #include "bacula.h"
31 #include "find.h"
32 #ifdef HAVE_DARWIN_OS
33 #include <sys/param.h>
34 #include <sys/mount.h>
35 #include <sys/attr.h>
36 #endif
37 
38 int breaddir(DIR *dirp, POOLMEM *&d_name);
39 
40 extern int32_t name_max;              /* filename max length */
41 extern int32_t path_max;              /* path name max length */
42 
43 /*
44  * Structure for keeping track of hard linked files, we
45  *   keep an entry for each hardlinked file that we save,
46  *   which is the first one found. For all the other files that
47  *   are linked to this one, we save only the directory
48  *   entry so we can link it.
49  */
50 struct f_link {
51     struct f_link *next;
52     dev_t dev;                        /* device */
53     ino_t ino;                        /* inode with device is unique */
54     int32_t FileIndex;                /* Bacula FileIndex of this file */
55     int32_t digest_stream;            /* Digest type if needed */
56     uint32_t digest_len;              /* Digest len if needed */
57     char *digest;                     /* Checksum of the file if needed */
58     char name[1];                     /* The name */
59 };
60 
61 typedef struct f_link link_t;
62 #define LINK_HASHTABLE_BITS 16
63 #define LINK_HASHTABLE_SIZE (1<<LINK_HASHTABLE_BITS)
64 #define LINK_HASHTABLE_MASK (LINK_HASHTABLE_SIZE-1)
65 
LINKHASH(const struct stat & info)66 static inline int LINKHASH(const struct stat &info)
67 {
68     int hash = info.st_dev;
69     unsigned long long i = info.st_ino;
70     hash ^= i;
71     i >>= 16;
72     hash ^= i;
73     i >>= 16;
74     hash ^= i;
75     i >>= 16;
76     hash ^= i;
77     return hash & LINK_HASHTABLE_MASK;
78 }
79 
80 /*
81  * Create a new directory Find File packet, but copy
82  *   some of the essential info from the current packet.
83  *   However, be careful to zero out the rest of the
84  *   packet.
85  */
new_dir_ff_pkt(FF_PKT * ff_pkt)86 static FF_PKT *new_dir_ff_pkt(FF_PKT *ff_pkt)
87 {
88    FF_PKT *dir_ff_pkt = (FF_PKT *)bmalloc(sizeof(FF_PKT));
89    memcpy(dir_ff_pkt, ff_pkt, sizeof(FF_PKT));
90 
91    /* Do not duplicate pointers */
92    dir_ff_pkt->fname = bstrdup(ff_pkt->fname);
93    dir_ff_pkt->snap_fname = bstrdup(ff_pkt->snap_fname);
94    dir_ff_pkt->link = bstrdup(ff_pkt->link);
95 
96    if (ff_pkt->fname_save) {
97       dir_ff_pkt->fname_save = get_pool_memory(PM_FNAME);
98       pm_strcpy(dir_ff_pkt->fname_save, ff_pkt->fname_save);
99    }
100    if (ff_pkt->link_save) {
101       dir_ff_pkt->link_save = get_pool_memory(PM_FNAME);
102       pm_strcpy(dir_ff_pkt->link_save, ff_pkt->link_save);
103    }
104 
105    dir_ff_pkt->included_files_list = NULL;
106    dir_ff_pkt->excluded_files_list = NULL;
107    dir_ff_pkt->excluded_paths_list = NULL;
108    dir_ff_pkt->linkhash = NULL;
109    dir_ff_pkt->ignoredir_fname = NULL;
110    return dir_ff_pkt;
111 }
112 
113 /*
114  * Free the temp directory ff_pkt
115  */
free_dir_ff_pkt(FF_PKT * dir_ff_pkt)116 static void free_dir_ff_pkt(FF_PKT *dir_ff_pkt)
117 {
118    free(dir_ff_pkt->fname);
119    free(dir_ff_pkt->snap_fname);
120    free(dir_ff_pkt->link);
121    if (dir_ff_pkt->fname_save) {
122       free_pool_memory(dir_ff_pkt->fname_save);
123    }
124    if (dir_ff_pkt->link_save) {
125       free_pool_memory(dir_ff_pkt->link_save);
126    }
127    free(dir_ff_pkt);
128 }
129 
130 /*
131  * Check to see if we allow the file system type of a file or directory.
132  * If we do not have a list of file system types, we accept anything.
133  */
accept_fstype(FF_PKT * ff,void * dummy)134 static int accept_fstype(FF_PKT *ff, void *dummy) {
135    int i;
136    char fs[1000];
137    bool accept = true;
138 
139    if (ff->fstypes.size()) {
140       accept = false;
141       if (!fstype(ff, fs, sizeof(fs))) {
142          Dmsg1(50, "Cannot determine file system type for \"%s\"\n", ff->fname);
143       } else {
144          for (i = 0; i < ff->fstypes.size(); ++i) {
145             if (strcmp(fs, (char *)ff->fstypes.get(i)) == 0) {
146                Dmsg2(100, "Accepting fstype %s for \"%s\"\n", fs, ff->fname);
147                accept = true;
148                break;
149             }
150             Dmsg3(200, "fstype %s for \"%s\" does not match %s\n", fs,
151                   ff->fname, ff->fstypes.get(i));
152          }
153       }
154    }
155    return accept;
156 }
157 
158 /*
159  * Check to see if we allow the drive type of a file or directory.
160  * If we do not have a list of drive types, we accept anything.
161  */
accept_drivetype(FF_PKT * ff,void * dummy)162 static int accept_drivetype(FF_PKT *ff, void *dummy) {
163    int i;
164    char dt[100];
165    bool accept = true;
166 
167    if (ff->drivetypes.size()) {
168       accept = false;
169       /* fname is a better choice here than snap_fname */
170       if (!drivetype(ff->fname, dt, sizeof(dt))) {
171          Dmsg1(50, "Cannot determine drive type for \"%s\"\n", ff->fname);
172       } else {
173          for (i = 0; i < ff->drivetypes.size(); ++i) {
174             if (strcmp(dt, (char *)ff->drivetypes.get(i)) == 0) {
175                Dmsg2(100, "Accepting drive type %s for \"%s\"\n", dt, ff->fname);
176                accept = true;
177                break;
178             }
179             Dmsg3(200, "drive type %s for \"%s\" does not match %s\n", dt,
180                   ff->fname, ff->drivetypes.get(i));
181          }
182       }
183    }
184    return accept;
185 }
186 
187 /*
188  * This function determines whether we can use getattrlist()
189  * It's odd, but we have to use the function to determine that...
190  * Also, the man pages talk about things as if they were implemented.
191  *
192  * On Mac OS X, this succesfully differentiates between HFS+ and UFS
193  * volumes, which makes me trust it is OK for others, too.
194  */
volume_has_attrlist(const char * fname)195 static bool volume_has_attrlist(const char *fname)
196 {
197 #ifdef HAVE_DARWIN_OS
198    struct statfs st;
199    struct volinfo_struct {
200       unsigned long length;               /* Mandatory field */
201       vol_capabilities_attr_t info;       /* Volume capabilities */
202    } vol;
203    struct attrlist attrList;
204 
205    memset(&attrList, 0, sizeof(attrList));
206    attrList.bitmapcount = ATTR_BIT_MAP_COUNT;
207    attrList.volattr = ATTR_VOL_INFO | ATTR_VOL_CAPABILITIES;
208    if (statfs(fname, &st) == 0) {
209       /* We need to check on the mount point */
210       if (getattrlist(st.f_mntonname, &attrList, &vol, sizeof(vol), FSOPT_NOFOLLOW) == 0
211             && (vol.info.capabilities[VOL_CAPABILITIES_INTERFACES] & VOL_CAP_INT_ATTRLIST)
212             && (vol.info.valid[VOL_CAPABILITIES_INTERFACES] & VOL_CAP_INT_ATTRLIST)) {
213          return true;
214       }
215    }
216 #endif
217    return false;
218 }
219 
220 /*
221  * check for BSD nodump flag
222  */
no_dump(JCR * jcr,FF_PKT * ff_pkt)223 static bool no_dump(JCR *jcr, FF_PKT *ff_pkt)
224 {
225 #if defined(HAVE_CHFLAGS) && defined(UF_NODUMP)
226    if ( (ff_pkt->flags & FO_HONOR_NODUMP) &&
227         (ff_pkt->statp.st_flags & UF_NODUMP) ) {
228       Jmsg(jcr, M_INFO, 1, _("     NODUMP flag set - will not process %s\n"),
229            ff_pkt->fname);
230       return true;                    /* do not backup this file */
231    }
232 #endif
233    return false;                      /* do backup */
234 }
235 
236 /* check if a file have changed during backup and display an error */
has_file_changed(JCR * jcr,FF_PKT * ff_pkt)237 bool has_file_changed(JCR *jcr, FF_PKT *ff_pkt)
238 {
239    struct stat statp;
240    Dmsg1(500, "has_file_changed fname=%s\n",ff_pkt->fname);
241 
242    if (ff_pkt->snapshot_convert_fct) {
243       /* their is no reason to check for a file change inside a snapshot */
244       /* but this has already highlighted some bugs in the past */
245       /* if you want to optimize, uncomment the "return below" */
246       // return false;
247    }
248 
249    if (ff_pkt->type != FT_REG) { /* not a regular file */
250       return false;
251    }
252 
253    if (lstat(ff_pkt->snap_fname, &statp) != 0) {
254       berrno be;
255       Jmsg(jcr, M_WARNING, 0,
256            _("Cannot stat file %s: ERR=%s\n"),ff_pkt->fname,be.bstrerror());
257       return true;
258    }
259 
260    if (statp.st_mtime != ff_pkt->statp.st_mtime) {
261       Jmsg(jcr, M_ERROR, 0, _("%s mtime changed during backup.\n"), ff_pkt->fname);
262       Dmsg3(50, "%s mtime (%lld) changed during backup (%lld).\n", ff_pkt->fname,
263             (int64_t)ff_pkt->statp.st_mtime, (int64_t)statp.st_mtime);
264       return true;
265    }
266 
267    if (statp.st_ctime != ff_pkt->statp.st_ctime) {
268       Jmsg(jcr, M_ERROR, 0, _("%s ctime changed during backup.\n"), ff_pkt->fname);
269       Dmsg3(50, "%s ctime (%lld) changed during backup (%lld).\n", ff_pkt->fname,
270             (int64_t)ff_pkt->statp.st_ctime, (int64_t)statp.st_ctime);
271       return true;
272    }
273 
274    if ((int64_t)statp.st_size != (int64_t)ff_pkt->statp.st_size) {
275       Jmsg(jcr, M_ERROR, 0, _("%s size of %lld changed during backup to %lld.n"),ff_pkt->fname,
276          (int64_t)ff_pkt->statp.st_size, (int64_t)statp.st_size);
277       Dmsg3(50, "%s size (%lld) changed during backup (%lld).\n", ff_pkt->fname,
278             (int64_t)ff_pkt->statp.st_size, (int64_t)statp.st_size);
279       return true;
280    }
281 
282    return false;
283 }
284 
285 /*
286  * For incremental/differential or accurate backups, we
287  *   determine if the current file has changed.
288  */
check_changes(JCR * jcr,FF_PKT * ff_pkt)289 bool check_changes(JCR *jcr, FF_PKT *ff_pkt)
290 {
291    /* in special mode (like accurate backup), the programmer can
292     * choose his comparison function.
293     */
294    if (ff_pkt->check_fct) {
295       return ff_pkt->check_fct(jcr, ff_pkt);
296    }
297 
298    /* For normal backups (incr/diff), we use this default
299     * behaviour
300     */
301    if (ff_pkt->incremental &&
302        (ff_pkt->statp.st_mtime < ff_pkt->save_time &&
303         ((ff_pkt->flags & FO_MTIMEONLY) ||
304          ff_pkt->statp.st_ctime < ff_pkt->save_time)))
305    {
306       return false;
307    }
308 
309    return true;
310 }
311 
have_ignoredir(FF_PKT * ff_pkt)312 static bool have_ignoredir(FF_PKT *ff_pkt)
313 {
314    struct stat sb;
315    char *ignoredir;
316 
317    /* Ensure that pointers are defined */
318    if (!ff_pkt->fileset || !ff_pkt->fileset->incexe) {
319       return false;
320    }
321    ignoredir = ff_pkt->fileset->incexe->ignoredir;
322 
323    if (ignoredir) {
324       if (!ff_pkt->ignoredir_fname) {
325          ff_pkt->ignoredir_fname = get_pool_memory(PM_FNAME);
326       }
327       Mmsg(ff_pkt->ignoredir_fname, "%s/%s", ff_pkt->fname, ignoredir);
328       if (stat(ff_pkt->ignoredir_fname, &sb) == 0) {
329          Dmsg2(100, "Directory '%s' ignored (found %s)\n",
330                ff_pkt->fname, ignoredir);
331          return true;      /* Just ignore this directory */
332       }
333    }
334    return false;
335 }
336 
337 /*
338  * When the current file is a hardlink, the backup code can compute
339  * the checksum and store it into the link_t structure.
340  */
341 void
ff_pkt_set_link_digest(FF_PKT * ff_pkt,int32_t digest_stream,const char * digest,uint32_t len)342 ff_pkt_set_link_digest(FF_PKT *ff_pkt,
343                        int32_t digest_stream, const char *digest, uint32_t len)
344 {
345    if (ff_pkt->linked && !ff_pkt->linked->digest) {     /* is a hardlink */
346       ff_pkt->linked->digest = (char *) bmalloc(len);
347       memcpy(ff_pkt->linked->digest, digest, len);
348       ff_pkt->linked->digest_len = len;
349       ff_pkt->linked->digest_stream = digest_stream;
350    }
351 }
352 
353 /*
354  * Find a single file.
355  * handle_file is the callback for handling the file.
356  * p is the filename
357  * parent_device is the device we are currently on
358  * top_level is 1 when not recursing or 0 when
359  *  descending into a directory.
360  */
361 int
find_one_file(JCR * jcr,FF_PKT * ff_pkt,int handle_file (JCR * jcr,FF_PKT * ff,bool top_level),char * fname,char * snap_fname,dev_t parent_device,bool top_level)362 find_one_file(JCR *jcr, FF_PKT *ff_pkt,
363                int handle_file(JCR *jcr, FF_PKT *ff, bool top_level),
364                char *fname, char *snap_fname, dev_t parent_device, bool top_level)
365 {
366    struct utimbuf restore_times;
367    int rtn_stat;
368    int len;
369 
370    ff_pkt->fname = ff_pkt->link = fname;
371    ff_pkt->snap_fname = snap_fname;
372 
373    if (lstat(snap_fname, &ff_pkt->statp) != 0) {
374        /* Cannot stat file */
375        ff_pkt->type = FT_NOSTAT;
376        ff_pkt->ff_errno = errno;
377        return handle_file(jcr, ff_pkt, top_level);
378    }
379 
380    Dmsg1(300, "File ----: %s\n", fname);
381 
382    /* Save current times of this directory in case we need to
383     * reset them because the user doesn't want them changed.
384     */
385    restore_times.actime = ff_pkt->statp.st_atime;
386    restore_times.modtime = ff_pkt->statp.st_mtime;
387 
388    /*
389     * We check for allowed fstypes and drivetypes at top_level and fstype change (below).
390     */
391    if (top_level) {
392       if (!accept_fstype(ff_pkt, NULL)) {
393          ff_pkt->type = FT_INVALIDFS;
394          if (ff_pkt->flags & FO_KEEPATIME && !ff_pkt->snapshot_convert_fct) {
395             utime(snap_fname, &restore_times);  /* snap_fname == fname */
396          }
397 
398          char fs[100];
399 
400          if (!fstype(ff_pkt, fs, sizeof(fs))) {
401              bstrncpy(fs, "unknown", sizeof(fs));
402          }
403 
404          Jmsg(jcr, M_INFO, 0, _("Top level directory \"%s\" has unlisted fstype \"%s\"\n"), fname, fs);
405          return 1;      /* Just ignore this error - or the whole backup is cancelled */
406       }
407       if (!accept_drivetype(ff_pkt, NULL)) {
408          ff_pkt->type = FT_INVALIDDT;
409          if (ff_pkt->flags & FO_KEEPATIME && !ff_pkt->snapshot_convert_fct) {
410             utime(snap_fname, &restore_times); /* snap_fname == fname */
411          }
412 
413          char dt[100];
414 
415          if (!drivetype(ff_pkt->fname, dt, sizeof(dt))) {
416              bstrncpy(dt, "unknown", sizeof(dt));
417          }
418 
419          Jmsg(jcr, M_INFO, 0, _("Top level directory \"%s\" has an unlisted drive type \"%s\"\n"), fname, dt);
420          return 1;      /* Just ignore this error - or the whole backup is cancelled */
421       }
422       ff_pkt->volhas_attrlist = volume_has_attrlist(snap_fname);
423    }
424 
425    /*
426     * Ignore this entry if no_dump() returns true
427     */
428    if (no_dump(jcr, ff_pkt)) {
429            Dmsg1(100, "'%s' ignored (NODUMP flag set)\n",
430                  ff_pkt->fname);
431            return 1;
432    }
433 
434    /*
435     * If this is an Incremental backup, see if file was modified
436     * since our last "save_time", presumably the last Full save
437     * or Incremental.
438     */
439    if (   !S_ISDIR(ff_pkt->statp.st_mode)
440        && !check_changes(jcr, ff_pkt))
441    {
442       Dmsg1(500, "Non-directory incremental: %s\n", ff_pkt->fname);
443       ff_pkt->type = FT_NOCHG;
444       return handle_file(jcr, ff_pkt, top_level);
445    }
446 
447 #ifdef HAVE_DARWIN_OS
448    if (ff_pkt->flags & FO_HFSPLUS && ff_pkt->volhas_attrlist
449          && S_ISREG(ff_pkt->statp.st_mode)) {
450        /* TODO: initialise attrList once elsewhere? */
451        struct attrlist attrList;
452        memset(&attrList, 0, sizeof(attrList));
453        attrList.bitmapcount = ATTR_BIT_MAP_COUNT;
454        attrList.commonattr = ATTR_CMN_FNDRINFO;
455        attrList.fileattr = ATTR_FILE_RSRCLENGTH;
456        if (getattrlist(snap_fname, &attrList, &ff_pkt->hfsinfo,
457                 sizeof(ff_pkt->hfsinfo), FSOPT_NOFOLLOW) != 0) {
458           ff_pkt->type = FT_NOSTAT;
459           ff_pkt->ff_errno = errno;
460           return handle_file(jcr, ff_pkt, top_level);
461        }
462        return -1; /* ignore */
463    }
464 #endif
465 
466    ff_pkt->LinkFI = 0;
467    /*
468     * Handle hard linked files
469     *
470     * Maintain a list of hard linked files already backed up. This
471     *  allows us to ensure that the data of each file gets backed
472     *  up only once.
473     */
474    if (!(ff_pkt->flags & FO_NO_HARDLINK)
475        && ff_pkt->statp.st_nlink > 1
476        && (S_ISREG(ff_pkt->statp.st_mode)
477            || S_ISCHR(ff_pkt->statp.st_mode)
478            || S_ISBLK(ff_pkt->statp.st_mode)
479            || S_ISFIFO(ff_pkt->statp.st_mode)
480            || S_ISSOCK(ff_pkt->statp.st_mode))) {
481 
482        struct f_link *lp;
483        if (ff_pkt->linkhash == NULL) {
484            ff_pkt->linkhash = (link_t **)bmalloc(LINK_HASHTABLE_SIZE * sizeof(link_t *));
485            memset(ff_pkt->linkhash, 0, LINK_HASHTABLE_SIZE * sizeof(link_t *));
486        }
487        const int linkhash = LINKHASH(ff_pkt->statp);
488 
489       /* Search link list of hard linked files */
490        for (lp = ff_pkt->linkhash[linkhash]; lp; lp = lp->next)
491          if (lp->ino == (ino_t)ff_pkt->statp.st_ino &&
492              lp->dev == (dev_t)ff_pkt->statp.st_dev) {
493              /* If we have already backed up the hard linked file don't do it again */
494              if (strcmp(lp->name, fname) == 0) {
495                 Dmsg2(400, "== Name identical skip FI=%d file=%s\n", lp->FileIndex, fname);
496                 return 1;             /* ignore */
497              }
498              ff_pkt->link = lp->name;
499              ff_pkt->type = FT_LNKSAVED;       /* Handle link, file already saved */
500              ff_pkt->LinkFI = lp->FileIndex;
501              ff_pkt->linked = 0;
502              ff_pkt->digest = lp->digest;
503              ff_pkt->digest_stream = lp->digest_stream;
504              ff_pkt->digest_len = lp->digest_len;
505              rtn_stat = handle_file(jcr, ff_pkt, top_level);
506              Dmsg3(400, "FT_LNKSAVED FI=%d LinkFI=%d file=%s\n",
507                 ff_pkt->FileIndex, lp->FileIndex, lp->name);
508              return rtn_stat;
509          }
510 
511       /* File not previously dumped. Chain it into our list. */
512       len = strlen(fname) + 1;
513       lp = (struct f_link *)bmalloc(sizeof(struct f_link) + len);
514       lp->digest = NULL;                /* set later */
515       lp->digest_stream = 0;            /* set later */
516       lp->digest_len = 0;               /* set later */
517       lp->ino = ff_pkt->statp.st_ino;
518       lp->dev = ff_pkt->statp.st_dev;
519       lp->FileIndex = 0;                  /* set later */
520       bstrncpy(lp->name, fname, len);
521       lp->next = ff_pkt->linkhash[linkhash];
522       ff_pkt->linkhash[linkhash] = lp;
523       ff_pkt->linked = lp;            /* mark saved link */
524       Dmsg2(400, "added to hash FI=%d file=%s\n", ff_pkt->FileIndex, lp->name);
525    } else {
526       ff_pkt->linked = NULL;
527    }
528 
529    /* This is not a link to a previously dumped file, so dump it.  */
530    if (S_ISREG(ff_pkt->statp.st_mode)) {
531       boffset_t sizeleft;
532 
533       sizeleft = ff_pkt->statp.st_size;
534 
535       /* Don't bother opening empty, world readable files.  Also do not open
536          files when archive is meant for /dev/null.  */
537       if (ff_pkt->null_output_device || (sizeleft == 0
538               && MODE_RALL == (MODE_RALL & ff_pkt->statp.st_mode))) {
539          ff_pkt->type = FT_REGE;
540       } else {
541          ff_pkt->type = FT_REG;
542       }
543       rtn_stat = handle_file(jcr, ff_pkt, top_level);
544       if (ff_pkt->linked) {
545          ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
546       }
547       Dmsg3(400, "FT_REG FI=%d linked=%d file=%s\n", ff_pkt->FileIndex,
548          ff_pkt->linked ? 1 : 0, fname);
549       if (ff_pkt->flags & FO_KEEPATIME && !ff_pkt->snapshot_convert_fct) {
550          utime(snap_fname, &restore_times); /* snap_fname == fname */
551       }
552       return rtn_stat;
553 
554 
555    } else if (S_ISLNK(ff_pkt->statp.st_mode)) {  /* soft link */
556       int size;
557       char *buffer = (char *)alloca(path_max + name_max + 102);
558 
559       size = readlink(snap_fname, buffer, path_max + name_max + 101);
560       if (size < 0) {
561          /* Could not follow link */
562          ff_pkt->type = FT_NOFOLLOW;
563          ff_pkt->ff_errno = errno;
564          rtn_stat = handle_file(jcr, ff_pkt, top_level);
565          if (ff_pkt->linked) {
566             ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
567          }
568          return rtn_stat;
569       }
570       buffer[size] = 0;
571       ff_pkt->link = buffer;          /* point to link */
572       ff_pkt->type = FT_LNK;          /* got a real link */
573       rtn_stat = handle_file(jcr, ff_pkt, top_level);
574       if (ff_pkt->linked) {
575          ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
576       }
577       return rtn_stat;
578 
579    } else if (S_ISDIR(ff_pkt->statp.st_mode)) {
580       DIR *directory;
581       POOL_MEM dname(PM_FNAME);
582       char *link;
583       int link_len;
584       int len;
585       int status;
586       dev_t our_device = ff_pkt->statp.st_dev;
587       bool recurse = true;
588       bool volhas_attrlist = ff_pkt->volhas_attrlist;    /* Remember this if we recurse */
589 
590       /*
591        * Ignore this directory and everything below if the file .nobackup
592        * (or what is defined for IgnoreDir in this fileset) exists
593        */
594       if (have_ignoredir(ff_pkt)) {
595          return 1; /* Just ignore this directory */
596       }
597 
598       /* Build a canonical directory name with a trailing slash in link var */
599       len = strlen(fname);
600       link_len = len + 200;
601       link = (char *)bmalloc(link_len + 2);
602       bstrncpy(link, fname, link_len);
603       /* Strip all trailing slashes */
604       while (len >= 1 && IsPathSeparator(link[len - 1]))
605         len--;
606       link[len++] = '/';             /* add back one */
607       link[len] = 0;
608 
609       ff_pkt->link = link;
610       if (!check_changes(jcr, ff_pkt)) {
611          /* Incremental/Full+Base option, directory entry not changed */
612          ff_pkt->type = FT_DIRNOCHG;
613       } else {
614          ff_pkt->type = FT_DIRBEGIN;
615       }
616       /*
617        * We have set st_rdev to 1 if it is a reparse point, otherwise 0,
618        *  if st_rdev is 2, it is a mount point
619        */
620 #if defined(HAVE_WIN32)
621       /*
622        * A reparse point (WIN32_REPARSE_POINT)
623        *  is something special like one of the following:
624        *  IO_REPARSE_TAG_DFS              0x8000000A
625        *  IO_REPARSE_TAG_DFSR             0x80000012
626        *  IO_REPARSE_TAG_HSM              0xC0000004
627        *  IO_REPARSE_TAG_HSM2             0x80000006
628        *  IO_REPARSE_TAG_SIS              0x80000007
629        *  IO_REPARSE_TAG_SYMLINK          0xA000000C
630        *
631        * A junction point is a:
632        *  IO_REPARSE_TAG_MOUNT_POINT      0xA0000003
633        * which can be either a link to a Volume (WIN32_MOUNT_POINT)
634        * or a link to a directory (WIN32_JUNCTION_POINT)
635        *
636        * Ignore WIN32_REPARSE_POINT and WIN32_JUNCTION_POINT
637        */
638       if (ff_pkt->statp.st_rdev == WIN32_REPARSE_POINT) {
639          ff_pkt->type = FT_REPARSE;
640       } else if (ff_pkt->statp.st_rdev == WIN32_JUNCTION_POINT ||
641             (ff_pkt->statp.st_rdev == WIN32_SYMLINK_POINT && S_ISDIR(ff_pkt->statp.st_mode))) {
642          // Handle SYMLINK DIRECTORY like a Junction, BackupRead() does all the work
643          ff_pkt->type = FT_JUNCTION;
644       }
645 #endif
646       /*
647        * Note, we return the directory to the calling program (handle_file)
648        * when we first see the directory (FT_DIRBEGIN.
649        * This allows the program to apply matches and make a
650        * choice whether or not to accept it.  If it is accepted, we
651        * do not immediately save it, but do so only after everything
652        * in the directory is seen (i.e. the FT_DIREND).
653        */
654       rtn_stat = handle_file(jcr, ff_pkt, top_level);
655       if (rtn_stat < 1 || ff_pkt->type == FT_REPARSE ||
656           ff_pkt->type == FT_JUNCTION) {   /* ignore or error status */
657          free(link);
658          return rtn_stat;
659       }
660       /* Done with DIRBEGIN, next call will be DIREND */
661       if (ff_pkt->type == FT_DIRBEGIN) {
662          ff_pkt->type = FT_DIREND;
663       }
664 
665       /*
666        * Create a temporary ff packet for this directory
667        *   entry, and defer handling the directory until
668        *   we have recursed into it.  This saves the
669        *   directory after all files have been processed, and
670        *   during the restore, the directory permissions will
671        *   be reset after all the files have been restored.
672        */
673       Dmsg1(300, "Create temp ff packet for dir: %s\n", ff_pkt->fname);
674       FF_PKT *dir_ff_pkt = new_dir_ff_pkt(ff_pkt);
675 #if defined(HAVE_WIN32)
676       if (dir_ff_pkt->root_of_volume || (ff_pkt->fname[3]=='\0' && ff_pkt->fname[1]==':')) {
677          /* tell "restore" that this is a root directory and that it should not
678           * restore the Windows hidden and system attribute
679           */
680          dir_ff_pkt->statp.st_rdev = WIN32_ROOT_POINT;
681          // don't "propagate" the "root_of_volume" property to sub-directories
682          ff_pkt->root_of_volume = false;
683       }
684 #endif
685 
686       /*
687        * Do not descend into subdirectories (recurse) if the
688        * user has turned it off for this directory.
689        *
690        * If we are crossing file systems, we are either not allowed
691        * to cross, or we may be restricted by a list of permitted
692        * file systems.
693        */
694       bool is_win32_mount_point = false;
695 #if defined(HAVE_WIN32)
696       is_win32_mount_point = ff_pkt->statp.st_rdev == WIN32_MOUNT_POINT;
697 #endif
698       if (!top_level && ff_pkt->flags & FO_NO_RECURSION) {
699          ff_pkt->type = FT_NORECURSE;
700          recurse = false;
701       } else if (!top_level &&
702             (parent_device != ff_pkt->statp.st_dev || is_win32_mount_point)) {
703             /* Nested mountpoint in a Windows snapshot works like the original
704              * mountpoint and redirect to the Live filesystem (not a snapshot)
705              *
706              * In Linux the directory is not a mountpoint anymore and we backup
707              * the content of the directory that is normally empty.
708              */
709          if (is_win32_mount_point && ff_pkt->flags & FO_MULTIFS) {
710             return 1;           /* Ignore this dir, it will backed up later */
711 
712          } else if (is_win32_mount_point) {
713             recurse = false;    /* We backup the mount point itself, but nothing more */
714 
715          } else if(!(ff_pkt->flags & FO_MULTIFS)) {
716             ff_pkt->type = FT_NOFSCHG;
717             recurse = false;
718 
719          } else if (!accept_fstype(ff_pkt, NULL)) {
720             ff_pkt->type = FT_INVALIDFS;
721             recurse = false;
722 
723          } else {
724             ff_pkt->volhas_attrlist = volume_has_attrlist(snap_fname);
725          }
726       }
727       /* If not recursing, just backup dir and return */
728       if (!recurse) {
729          rtn_stat = handle_file(jcr, ff_pkt, top_level);
730          if (ff_pkt->linked) {
731             ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
732          }
733          free(link);
734          free_dir_ff_pkt(dir_ff_pkt);
735          ff_pkt->link = ff_pkt->fname;     /* reset "link" */
736          if (ff_pkt->flags & FO_KEEPATIME && !ff_pkt->snapshot_convert_fct) {
737             utime(snap_fname, &restore_times); /* snap_fname==fname */
738          }
739          return rtn_stat;
740       }
741 
742       ff_pkt->link = ff_pkt->fname;     /* reset "link" */
743 
744       /*
745        * Descend into or "recurse" into the directory to read
746        *   all the files in it.
747        */
748       errno = 0;
749       if ((directory = opendir(snap_fname)) == NULL) {
750          ff_pkt->type = FT_NOOPEN;
751          ff_pkt->ff_errno = errno;
752          rtn_stat = handle_file(jcr, ff_pkt, top_level);
753          if (ff_pkt->linked) {
754             ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
755          }
756          free(link);
757          free_dir_ff_pkt(dir_ff_pkt);
758          return rtn_stat;
759       }
760 
761       /* Build a canonical directory name of the file inside the snapshot,
762        * with a trailing slash in link var */
763       int slen = strlen(snap_fname);
764       int snap_len = slen + 200;
765       char *snap_link = (char *)bmalloc(snap_len + 2);
766       bstrncpy(snap_link, snap_fname, snap_len);
767       /* Strip all trailing slashes */
768       while (slen >= 1 && IsPathSeparator(snap_link[slen - 1]))
769         slen--;
770       snap_link[slen++] = '/';             /* add back one */
771       snap_link[slen] = 0;
772 
773       /*
774        * Process all files in this directory entry (recursing).
775        *    This would possibly run faster if we chdir to the directory
776        *    before traversing it.
777        */
778       rtn_stat = 1;
779       while (!job_canceled(jcr)) {
780          char *p, *q, *s;
781          int l;
782          int i;
783 
784          status = breaddir(directory, dname.addr());
785          if (status != 0) {
786             /* error or end of directory */
787 //          Dmsg1(99, "breaddir returned stat=%d\n", status);
788             break;
789          }
790          p = dname.c_str();
791          /* Skip `.', `..', and excluded file names.  */
792          if (p[0] == '\0' || (p[0] == '.' && (p[1] == '\0' ||
793              (p[1] == '.' && p[2] == '\0')))) {
794             continue;
795          }
796          l = strlen(dname.c_str());
797          if (l + len >= link_len) {
798              link_len = len + l + 1;
799              link = (char *)brealloc(link, link_len + 1);
800          }
801          if (l + slen >= snap_len) {
802              snap_len = slen + l + 1;
803              snap_link = (char *)brealloc(snap_link, snap_len + 1);
804          }
805 
806          q = link + len;
807          s = snap_link + slen;
808          for (i=0; i < l; i++) {
809             *q++ = *s++ =*p++;
810          }
811          *q = *s = 0;
812          if (!file_is_excluded(ff_pkt, link)) {
813             rtn_stat = find_one_file(jcr, ff_pkt, handle_file, link, snap_link, our_device, false);
814             if (ff_pkt->linked) {
815                ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
816             }
817          }
818 
819       }
820       closedir(directory);
821       free(link);
822       free(snap_link);
823 
824       /*
825        * Now that we have recursed through all the files in the
826        *  directory, we "save" the directory so that after all
827        *  the files are restored, this entry will serve to reset
828        *  the directory modes and dates.  Temp directory values
829        *  were used without this record.
830        */
831       handle_file(jcr, dir_ff_pkt, top_level);       /* handle directory entry */
832       if (ff_pkt->linked) {
833          ff_pkt->linked->FileIndex = dir_ff_pkt->FileIndex;
834       }
835       free_dir_ff_pkt(dir_ff_pkt);
836 
837       if (ff_pkt->flags & FO_KEEPATIME && !ff_pkt->snapshot_convert_fct) {
838          utime(snap_fname, &restore_times);
839       }
840       ff_pkt->volhas_attrlist = volhas_attrlist;      /* Restore value in case it changed. */
841       return rtn_stat;
842    } /* end check for directory */
843 
844    /*
845     * If it is explicitly mentioned (i.e. top_level) and is
846     *  a block device, we do a raw backup of it or if it is
847     *  a fifo, we simply read it.
848     */
849 #ifdef HAVE_FREEBSD_OS
850    /*
851     * On FreeBSD, all block devices are character devices, so
852     *   to be able to read a raw disk, we need the check for
853     *   a character device.
854     * crw-r----- 1 root  operator - 116, 0x00040002 Jun 9 19:32 /dev/ad0s3
855     * crw-r----- 1 root  operator - 116, 0x00040002 Jun 9 19:32 /dev/rad0s3
856     */
857    if (top_level && (S_ISBLK(ff_pkt->statp.st_mode) || S_ISCHR(ff_pkt->statp.st_mode))) {
858 #else
859    if (top_level && S_ISBLK(ff_pkt->statp.st_mode)) {
860 #endif
861       ff_pkt->type = FT_RAW;          /* raw partition */
862    } else if (top_level && S_ISFIFO(ff_pkt->statp.st_mode) &&
863               ff_pkt->flags & FO_READFIFO) {
864       ff_pkt->type = FT_FIFO;
865    } else {
866       /* The only remaining types are special (character, ...) files */
867       ff_pkt->type = FT_SPEC;
868    }
869    rtn_stat = handle_file(jcr, ff_pkt, top_level);
870    if (ff_pkt->linked) {
871       ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
872    }
873    return rtn_stat;
874 }
875 
876 int term_find_one(FF_PKT *ff)
877 {
878    struct f_link *lp, *lc;
879    int count = 0;
880    int i;
881 
882 
883    if (ff->linkhash == NULL) return 0;
884 
885    for (i =0 ; i < LINK_HASHTABLE_SIZE; i ++) {
886    /* Free up list of hard linked files */
887       lp = ff->linkhash[i];
888       while (lp) {
889          lc = lp;
890          lp = lp->next;
891          if (lc) {
892             if (lc->digest) {
893                free(lc->digest);
894             }
895             free(lc);
896             count++;
897          }
898       }
899       ff->linkhash[i] = NULL;
900    }
901    free(ff->linkhash);
902    ff->linkhash = NULL;
903    return count;
904 }
905