1 /*
2    Bacula(R) - The Network Backup Solution
3 
4    Copyright (C) 2000-2018 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((void *)dir_ff_pkt, (void *)ff_pkt, sizeof(FF_PKT));
90    dir_ff_pkt->fname = bstrdup(ff_pkt->fname);
91    dir_ff_pkt->link = bstrdup(ff_pkt->link);
92    dir_ff_pkt->sys_fname = get_pool_memory(PM_FNAME);
93 
94    if (ff_pkt->strip_snap_path) {
95       dir_ff_pkt->fname_save = get_pool_memory(PM_FNAME);
96       dir_ff_pkt->link_save = get_pool_memory(PM_FNAME);
97       pm_strcpy(dir_ff_pkt->fname_save, ff_pkt->fname_save);
98       pm_strcpy(dir_ff_pkt->link_save, ff_pkt->link_save);
99 
100       // need its own "working" buffers, some pm_strcat or pm_strcopy
101       // will realloc these ones, no need to copy the current values
102       dir_ff_pkt->snap_top_fname = get_pool_memory(PM_FNAME);
103       dir_ff_pkt->snap_fname = get_pool_memory(PM_FNAME);
104    } else {
105       dir_ff_pkt->fname_save = NULL;
106       dir_ff_pkt->link_save = NULL;
107    }
108 
109    dir_ff_pkt->included_files_list = NULL;
110    dir_ff_pkt->excluded_files_list = NULL;
111    dir_ff_pkt->excluded_paths_list = NULL;
112    dir_ff_pkt->linkhash = NULL;
113    dir_ff_pkt->ignoredir_fname = NULL;
114    return dir_ff_pkt;
115 }
116 
117 /*
118  * Free the temp directory ff_pkt
119  */
free_dir_ff_pkt(FF_PKT * dir_ff_pkt)120 static void free_dir_ff_pkt(FF_PKT *dir_ff_pkt)
121 {
122    free(dir_ff_pkt->fname);
123    free(dir_ff_pkt->link);
124    free_pool_memory(dir_ff_pkt->sys_fname);
125    if (dir_ff_pkt->fname_save) {
126       free_pool_memory(dir_ff_pkt->fname_save);
127    }
128    if (dir_ff_pkt->link_save) {
129       free_pool_memory(dir_ff_pkt->link_save);
130    }
131    if (dir_ff_pkt->snap_top_fname) {
132       free_pool_memory(dir_ff_pkt->snap_top_fname);
133       free_pool_memory(dir_ff_pkt->snap_fname);
134    }
135    free(dir_ff_pkt);
136 }
137 
138 /*
139  * Check to see if we allow the file system type of a file or directory.
140  * If we do not have a list of file system types, we accept anything.
141  */
accept_fstype(FF_PKT * ff,void * dummy)142 static int accept_fstype(FF_PKT *ff, void *dummy) {
143    int i;
144    char fs[1000];
145    bool accept = true;
146 
147    if (ff->fstypes.size()) {
148       accept = false;
149       if (!fstype(ff, fs, sizeof(fs))) {
150          Dmsg1(50, "Cannot determine file system type for \"%s\"\n", ff->fname);
151       } else {
152          for (i = 0; i < ff->fstypes.size(); ++i) {
153             if (strcmp(fs, (char *)ff->fstypes.get(i)) == 0) {
154                Dmsg2(100, "Accepting fstype %s for \"%s\"\n", fs, ff->fname);
155                accept = true;
156                break;
157             }
158             Dmsg3(200, "fstype %s for \"%s\" does not match %s\n", fs,
159                   ff->fname, ff->fstypes.get(i));
160          }
161       }
162    }
163    return accept;
164 }
165 
166 /*
167  * Check to see if we allow the drive type of a file or directory.
168  * If we do not have a list of drive types, we accept anything.
169  */
accept_drivetype(FF_PKT * ff,void * dummy)170 static int accept_drivetype(FF_PKT *ff, void *dummy) {
171    int i;
172    char dt[100];
173    bool accept = true;
174 
175    if (ff->drivetypes.size()) {
176       accept = false;
177       if (!drivetype(ff->fname, dt, sizeof(dt))) {
178          Dmsg1(50, "Cannot determine drive type for \"%s\"\n", ff->fname);
179       } else {
180          for (i = 0; i < ff->drivetypes.size(); ++i) {
181             if (strcmp(dt, (char *)ff->drivetypes.get(i)) == 0) {
182                Dmsg2(100, "Accepting drive type %s for \"%s\"\n", dt, ff->fname);
183                accept = true;
184                break;
185             }
186             Dmsg3(200, "drive type %s for \"%s\" does not match %s\n", dt,
187                   ff->fname, ff->drivetypes.get(i));
188          }
189       }
190    }
191    return accept;
192 }
193 
194 /*
195  * This function determines whether we can use getattrlist()
196  * It's odd, but we have to use the function to determine that...
197  * Also, the man pages talk about things as if they were implemented.
198  *
199  * On Mac OS X, this succesfully differentiates between HFS+ and UFS
200  * volumes, which makes me trust it is OK for others, too.
201  */
volume_has_attrlist(const char * fname)202 static bool volume_has_attrlist(const char *fname)
203 {
204 #ifdef HAVE_DARWIN_OS
205    struct statfs st;
206    struct volinfo_struct {
207       unsigned long length;               /* Mandatory field */
208       vol_capabilities_attr_t info;       /* Volume capabilities */
209    } vol;
210    struct attrlist attrList;
211 
212    memset(&attrList, 0, sizeof(attrList));
213    attrList.bitmapcount = ATTR_BIT_MAP_COUNT;
214    attrList.volattr = ATTR_VOL_INFO | ATTR_VOL_CAPABILITIES;
215    if (statfs(fname, &st) == 0) {
216       /* We need to check on the mount point */
217       if (getattrlist(st.f_mntonname, &attrList, &vol, sizeof(vol), FSOPT_NOFOLLOW) == 0
218             && (vol.info.capabilities[VOL_CAPABILITIES_INTERFACES] & VOL_CAP_INT_ATTRLIST)
219             && (vol.info.valid[VOL_CAPABILITIES_INTERFACES] & VOL_CAP_INT_ATTRLIST)) {
220          return true;
221       }
222    }
223 #endif
224    return false;
225 }
226 
227 /*
228  * check for BSD nodump flag
229  */
no_dump(JCR * jcr,FF_PKT * ff_pkt)230 static bool no_dump(JCR *jcr, FF_PKT *ff_pkt)
231 {
232 #if defined(HAVE_CHFLAGS) && defined(UF_NODUMP)
233    if ( (ff_pkt->flags & FO_HONOR_NODUMP) &&
234         (ff_pkt->statp.st_flags & UF_NODUMP) ) {
235       Jmsg(jcr, M_INFO, 1, _("     NODUMP flag set - will not process %s\n"),
236            ff_pkt->fname);
237       return true;                    /* do not backup this file */
238    }
239 #endif
240    return false;                      /* do backup */
241 }
242 
243 /* check if a file have changed during backup and display an error */
has_file_changed(JCR * jcr,FF_PKT * ff_pkt)244 bool has_file_changed(JCR *jcr, FF_PKT *ff_pkt)
245 {
246    struct stat statp;
247    Dmsg1(500, "has_file_changed fname=%s\n",ff_pkt->fname);
248 
249    if (ff_pkt->type != FT_REG) { /* not a regular file */
250       return false;
251    }
252 
253    if (lstat(ff_pkt->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/diffential 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,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, 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 
372    if (lstat(fname, &ff_pkt->statp) != 0) {
373        /* Cannot stat file */
374        ff_pkt->type = FT_NOSTAT;
375        ff_pkt->ff_errno = errno;
376        return handle_file(jcr, ff_pkt, top_level);
377    }
378 
379    Dmsg1(300, "File ----: %s\n", fname);
380 
381    /* Save current times of this directory in case we need to
382     * reset them because the user doesn't want them changed.
383     */
384    restore_times.actime = ff_pkt->statp.st_atime;
385    restore_times.modtime = ff_pkt->statp.st_mtime;
386 
387    /*
388     * We check for allowed fstypes and drivetypes at top_level and fstype change (below).
389     */
390    if (top_level) {
391       if (!accept_fstype(ff_pkt, NULL)) {
392          ff_pkt->type = FT_INVALIDFS;
393          if (ff_pkt->flags & FO_KEEPATIME) {
394             utime(fname, &restore_times);
395          }
396 
397          char fs[100];
398 
399          if (!fstype(ff_pkt, fs, sizeof(fs))) {
400              bstrncpy(fs, "unknown", sizeof(fs));
401          }
402 
403          Jmsg(jcr, M_INFO, 0, _("Top level directory \"%s\" has unlisted fstype \"%s\"\n"), fname, fs);
404          return 1;      /* Just ignore this error - or the whole backup is cancelled */
405       }
406       if (!accept_drivetype(ff_pkt, NULL)) {
407          ff_pkt->type = FT_INVALIDDT;
408          if (ff_pkt->flags & FO_KEEPATIME) {
409             utime(fname, &restore_times);
410          }
411 
412          char dt[100];
413 
414          if (!drivetype(ff_pkt->fname, dt, sizeof(dt))) {
415              bstrncpy(dt, "unknown", sizeof(dt));
416          }
417 
418          Jmsg(jcr, M_INFO, 0, _("Top level directory \"%s\" has an unlisted drive type \"%s\"\n"), fname, dt);
419          return 1;      /* Just ignore this error - or the whole backup is cancelled */
420       }
421       ff_pkt->volhas_attrlist = volume_has_attrlist(fname);
422    }
423 
424    /*
425     * Ignore this entry if no_dump() returns true
426     */
427    if (no_dump(jcr, ff_pkt)) {
428            Dmsg1(100, "'%s' ignored (NODUMP flag set)\n",
429                  ff_pkt->fname);
430            return 1;
431    }
432 
433    /*
434     * If this is an Incremental backup, see if file was modified
435     * since our last "save_time", presumably the last Full save
436     * or Incremental.
437     */
438    if (   !S_ISDIR(ff_pkt->statp.st_mode)
439        && !check_changes(jcr, ff_pkt))
440    {
441       Dmsg1(500, "Non-directory incremental: %s\n", ff_pkt->fname);
442       ff_pkt->type = FT_NOCHG;
443       return handle_file(jcr, ff_pkt, top_level);
444    }
445 
446 #ifdef HAVE_DARWIN_OS
447    if (ff_pkt->flags & FO_HFSPLUS && ff_pkt->volhas_attrlist
448          && S_ISREG(ff_pkt->statp.st_mode)) {
449        /* TODO: initialise attrList once elsewhere? */
450        struct attrlist attrList;
451        memset(&attrList, 0, sizeof(attrList));
452        attrList.bitmapcount = ATTR_BIT_MAP_COUNT;
453        attrList.commonattr = ATTR_CMN_FNDRINFO;
454        attrList.fileattr = ATTR_FILE_RSRCLENGTH;
455        if (getattrlist(fname, &attrList, &ff_pkt->hfsinfo,
456                 sizeof(ff_pkt->hfsinfo), FSOPT_NOFOLLOW) != 0) {
457           ff_pkt->type = FT_NOSTAT;
458           ff_pkt->ff_errno = errno;
459           return handle_file(jcr, ff_pkt, top_level);
460        }
461        return -1; /* ignore */
462    }
463 #endif
464 
465    ff_pkt->LinkFI = 0;
466    /*
467     * Handle hard linked files
468     *
469     * Maintain a list of hard linked files already backed up. This
470     *  allows us to ensure that the data of each file gets backed
471     *  up only once.
472     */
473    if (!(ff_pkt->flags & FO_NO_HARDLINK)
474        && ff_pkt->statp.st_nlink > 1
475        && (S_ISREG(ff_pkt->statp.st_mode)
476            || S_ISCHR(ff_pkt->statp.st_mode)
477            || S_ISBLK(ff_pkt->statp.st_mode)
478            || S_ISFIFO(ff_pkt->statp.st_mode)
479            || S_ISSOCK(ff_pkt->statp.st_mode))) {
480 
481        struct f_link *lp;
482        if (ff_pkt->linkhash == NULL) {
483            ff_pkt->linkhash = (link_t **)bmalloc(LINK_HASHTABLE_SIZE * sizeof(link_t *));
484            memset(ff_pkt->linkhash, 0, LINK_HASHTABLE_SIZE * sizeof(link_t *));
485        }
486        const int linkhash = LINKHASH(ff_pkt->statp);
487 
488       /* Search link list of hard linked files */
489        for (lp = ff_pkt->linkhash[linkhash]; lp; lp = lp->next)
490          if (lp->ino == (ino_t)ff_pkt->statp.st_ino &&
491              lp->dev == (dev_t)ff_pkt->statp.st_dev) {
492              /* If we have already backed up the hard linked file don't do it again */
493              if (strcmp(lp->name, fname) == 0) {
494                 Dmsg2(400, "== Name identical skip FI=%d file=%s\n", lp->FileIndex, fname);
495                 return 1;             /* ignore */
496              }
497              ff_pkt->link = lp->name;
498              ff_pkt->type = FT_LNKSAVED;       /* Handle link, file already saved */
499              ff_pkt->LinkFI = lp->FileIndex;
500              ff_pkt->linked = 0;
501              ff_pkt->digest = lp->digest;
502              ff_pkt->digest_stream = lp->digest_stream;
503              ff_pkt->digest_len = lp->digest_len;
504              rtn_stat = handle_file(jcr, ff_pkt, top_level);
505              Dmsg3(400, "FT_LNKSAVED FI=%d LinkFI=%d file=%s\n",
506                 ff_pkt->FileIndex, lp->FileIndex, lp->name);
507              return rtn_stat;
508          }
509 
510       /* File not previously dumped. Chain it into our list. */
511       len = strlen(fname) + 1;
512       lp = (struct f_link *)bmalloc(sizeof(struct f_link) + len);
513       lp->digest = NULL;                /* set later */
514       lp->digest_stream = 0;            /* set later */
515       lp->digest_len = 0;               /* set later */
516       lp->ino = ff_pkt->statp.st_ino;
517       lp->dev = ff_pkt->statp.st_dev;
518       lp->FileIndex = 0;                  /* set later */
519       bstrncpy(lp->name, fname, len);
520       lp->next = ff_pkt->linkhash[linkhash];
521       ff_pkt->linkhash[linkhash] = lp;
522       ff_pkt->linked = lp;            /* mark saved link */
523       Dmsg2(400, "added to hash FI=%d file=%s\n", ff_pkt->FileIndex, lp->name);
524    } else {
525       ff_pkt->linked = NULL;
526    }
527 
528    /* This is not a link to a previously dumped file, so dump it.  */
529    if (S_ISREG(ff_pkt->statp.st_mode)) {
530       boffset_t sizeleft;
531 
532       sizeleft = ff_pkt->statp.st_size;
533 
534       /* Don't bother opening empty, world readable files.  Also do not open
535          files when archive is meant for /dev/null.  */
536       if (ff_pkt->null_output_device || (sizeleft == 0
537               && MODE_RALL == (MODE_RALL & ff_pkt->statp.st_mode))) {
538          ff_pkt->type = FT_REGE;
539       } else {
540          ff_pkt->type = FT_REG;
541       }
542       rtn_stat = handle_file(jcr, ff_pkt, top_level);
543       if (ff_pkt->linked) {
544          ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
545       }
546       Dmsg3(400, "FT_REG FI=%d linked=%d file=%s\n", ff_pkt->FileIndex,
547          ff_pkt->linked ? 1 : 0, fname);
548       if (ff_pkt->flags & FO_KEEPATIME) {
549          utime(fname, &restore_times);
550       }
551       return rtn_stat;
552 
553 
554    } else if (S_ISLNK(ff_pkt->statp.st_mode)) {  /* soft link */
555       int size;
556       char *buffer = (char *)alloca(path_max + name_max + 102);
557 
558       size = readlink(fname, buffer, path_max + name_max + 101);
559       if (size < 0) {
560          /* Could not follow link */
561          ff_pkt->type = FT_NOFOLLOW;
562          ff_pkt->ff_errno = errno;
563          rtn_stat = handle_file(jcr, ff_pkt, top_level);
564          if (ff_pkt->linked) {
565             ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
566          }
567          return rtn_stat;
568       }
569       buffer[size] = 0;
570       ff_pkt->link = buffer;          /* point to link */
571       ff_pkt->type = FT_LNK;          /* got a real link */
572       rtn_stat = handle_file(jcr, ff_pkt, top_level);
573       if (ff_pkt->linked) {
574          ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
575       }
576       return rtn_stat;
577 
578    } else if (S_ISDIR(ff_pkt->statp.st_mode)) {
579       DIR *directory;
580       POOL_MEM dname(PM_FNAME);
581       char *link;
582       int link_len;
583       int len;
584       int status;
585       dev_t our_device = ff_pkt->statp.st_dev;
586       bool recurse = true;
587       bool volhas_attrlist = ff_pkt->volhas_attrlist;    /* Remember this if we recurse */
588 
589       /*
590        * Ignore this directory and everything below if the file .nobackup
591        * (or what is defined for IgnoreDir in this fileset) exists
592        */
593       if (have_ignoredir(ff_pkt)) {
594          return 1; /* Just ignore this directory */
595       }
596 
597       /* Build a canonical directory name with a trailing slash in link var */
598       len = strlen(fname);
599       link_len = len + 200;
600       link = (char *)bmalloc(link_len + 2);
601       bstrncpy(link, fname, link_len);
602       /* Strip all trailing slashes */
603       while (len >= 1 && IsPathSeparator(link[len - 1]))
604         len--;
605       link[len++] = '/';             /* add back one */
606       link[len] = 0;
607 
608       ff_pkt->link = link;
609       if (!check_changes(jcr, ff_pkt)) {
610          /* Incremental/Full+Base option, directory entry not changed */
611          ff_pkt->type = FT_DIRNOCHG;
612       } else {
613          ff_pkt->type = FT_DIRBEGIN;
614       }
615       /*
616        * We have set st_rdev to 1 if it is a reparse point, otherwise 0,
617        *  if st_rdev is 2, it is a mount point
618        */
619 #if defined(HAVE_WIN32)
620       /*
621        * A reparse point (WIN32_REPARSE_POINT)
622        *  is something special like one of the following:
623        *  IO_REPARSE_TAG_DFS              0x8000000A
624        *  IO_REPARSE_TAG_DFSR             0x80000012
625        *  IO_REPARSE_TAG_HSM              0xC0000004
626        *  IO_REPARSE_TAG_HSM2             0x80000006
627        *  IO_REPARSE_TAG_SIS              0x80000007
628        *  IO_REPARSE_TAG_SYMLINK          0xA000000C
629        *
630        * A junction point is a:
631        *  IO_REPARSE_TAG_MOUNT_POINT      0xA0000003
632        * which can be either a link to a Volume (WIN32_MOUNT_POINT)
633        * or a link to a directory (WIN32_JUNCTION_POINT)
634        *
635        * Ignore WIN32_REPARSE_POINT and WIN32_JUNCTION_POINT
636        */
637       if (ff_pkt->statp.st_rdev == WIN32_REPARSE_POINT) {
638          ff_pkt->type = FT_REPARSE;
639       } else if (ff_pkt->statp.st_rdev == WIN32_JUNCTION_POINT) {
640          ff_pkt->type = FT_JUNCTION;
641       }
642 #endif
643       /*
644        * Note, we return the directory to the calling program (handle_file)
645        * when we first see the directory (FT_DIRBEGIN.
646        * This allows the program to apply matches and make a
647        * choice whether or not to accept it.  If it is accepted, we
648        * do not immediately save it, but do so only after everything
649        * in the directory is seen (i.e. the FT_DIREND).
650        */
651       rtn_stat = handle_file(jcr, ff_pkt, top_level);
652       if (rtn_stat < 1 || ff_pkt->type == FT_REPARSE ||
653           ff_pkt->type == FT_JUNCTION) {   /* ignore or error status */
654          free(link);
655          return rtn_stat;
656       }
657       /* Done with DIRBEGIN, next call will be DIREND */
658       if (ff_pkt->type == FT_DIRBEGIN) {
659          ff_pkt->type = FT_DIREND;
660       }
661 
662       /*
663        * Create a temporary ff packet for this directory
664        *   entry, and defer handling the directory until
665        *   we have recursed into it.  This saves the
666        *   directory after all files have been processed, and
667        *   during the restore, the directory permissions will
668        *   be reset after all the files have been restored.
669        */
670       Dmsg1(300, "Create temp ff packet for dir: %s\n", ff_pkt->fname);
671       FF_PKT *dir_ff_pkt = new_dir_ff_pkt(ff_pkt);
672 
673       /*
674        * Do not descend into subdirectories (recurse) if the
675        * user has turned it off for this directory.
676        *
677        * If we are crossing file systems, we are either not allowed
678        * to cross, or we may be restricted by a list of permitted
679        * file systems.
680        */
681       bool is_win32_mount_point = false;
682 #if defined(HAVE_WIN32)
683       is_win32_mount_point = ff_pkt->statp.st_rdev == WIN32_MOUNT_POINT;
684 #endif
685       if (!top_level && ff_pkt->flags & FO_NO_RECURSION) {
686          ff_pkt->type = FT_NORECURSE;
687          recurse = false;
688       } else if (!top_level && (parent_device != ff_pkt->statp.st_dev ||
689                  is_win32_mount_point)) {
690          if(!(ff_pkt->flags & FO_MULTIFS)) {
691             ff_pkt->type = FT_NOFSCHG;
692             recurse = false;
693          } else if (!accept_fstype(ff_pkt, NULL)) {
694             ff_pkt->type = FT_INVALIDFS;
695             recurse = false;
696          } else {
697             ff_pkt->volhas_attrlist = volume_has_attrlist(fname);
698          }
699       }
700       /* If not recursing, just backup dir and return */
701       if (!recurse) {
702          rtn_stat = handle_file(jcr, ff_pkt, top_level);
703          if (ff_pkt->linked) {
704             ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
705          }
706          free(link);
707          free_dir_ff_pkt(dir_ff_pkt);
708          ff_pkt->link = ff_pkt->fname;     /* reset "link" */
709          if (ff_pkt->flags & FO_KEEPATIME) {
710             utime(fname, &restore_times);
711          }
712          return rtn_stat;
713       }
714 
715       ff_pkt->link = ff_pkt->fname;     /* reset "link" */
716 
717       /*
718        * Descend into or "recurse" into the directory to read
719        *   all the files in it.
720        */
721       errno = 0;
722       if ((directory = opendir(fname)) == NULL) {
723          ff_pkt->type = FT_NOOPEN;
724          ff_pkt->ff_errno = errno;
725          rtn_stat = handle_file(jcr, ff_pkt, top_level);
726          if (ff_pkt->linked) {
727             ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
728          }
729          free(link);
730          free_dir_ff_pkt(dir_ff_pkt);
731          return rtn_stat;
732       }
733 
734       /*
735        * Process all files in this directory entry (recursing).
736        *    This would possibly run faster if we chdir to the directory
737        *    before traversing it.
738        */
739       rtn_stat = 1;
740       while (!job_canceled(jcr)) {
741          char *p, *q;
742          int l;
743          int i;
744 
745          status = breaddir(directory, dname.addr());
746          if (status != 0) {
747             /* error or end of directory */
748 //          Dmsg1(99, "breaddir returned stat=%d\n", status);
749             break;
750          }
751          p = dname.c_str();
752          /* Skip `.', `..', and excluded file names.  */
753          if (p[0] == '\0' || (p[0] == '.' && (p[1] == '\0' ||
754              (p[1] == '.' && p[2] == '\0')))) {
755             continue;
756          }
757          l = strlen(dname.c_str());
758          if (l + len >= link_len) {
759              link_len = len + l + 1;
760              link = (char *)brealloc(link, link_len + 1);
761          }
762          q = link + len;
763          for (i=0; i < l; i++) {
764             *q++ = *p++;
765          }
766          *q = 0;
767          if (!file_is_excluded(ff_pkt, link)) {
768             rtn_stat = find_one_file(jcr, ff_pkt, handle_file, link, our_device, false);
769             if (ff_pkt->linked) {
770                ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
771             }
772          }
773       }
774       closedir(directory);
775       free(link);
776 
777       /*
778        * Now that we have recursed through all the files in the
779        *  directory, we "save" the directory so that after all
780        *  the files are restored, this entry will serve to reset
781        *  the directory modes and dates.  Temp directory values
782        *  were used without this record.
783        */
784       handle_file(jcr, dir_ff_pkt, top_level);       /* handle directory entry */
785       if (ff_pkt->linked) {
786          ff_pkt->linked->FileIndex = dir_ff_pkt->FileIndex;
787       }
788       free_dir_ff_pkt(dir_ff_pkt);
789 
790       if (ff_pkt->flags & FO_KEEPATIME) {
791          utime(fname, &restore_times);
792       }
793       ff_pkt->volhas_attrlist = volhas_attrlist;      /* Restore value in case it changed. */
794       return rtn_stat;
795    } /* end check for directory */
796 
797    /*
798     * If it is explicitly mentioned (i.e. top_level) and is
799     *  a block device, we do a raw backup of it or if it is
800     *  a fifo, we simply read it.
801     */
802 #ifdef HAVE_FREEBSD_OS
803    /*
804     * On FreeBSD, all block devices are character devices, so
805     *   to be able to read a raw disk, we need the check for
806     *   a character device.
807     * crw-r----- 1 root  operator - 116, 0x00040002 Jun 9 19:32 /dev/ad0s3
808     * crw-r----- 1 root  operator - 116, 0x00040002 Jun 9 19:32 /dev/rad0s3
809     */
810    if (top_level && (S_ISBLK(ff_pkt->statp.st_mode) || S_ISCHR(ff_pkt->statp.st_mode))) {
811 #else
812    if (top_level && S_ISBLK(ff_pkt->statp.st_mode)) {
813 #endif
814       ff_pkt->type = FT_RAW;          /* raw partition */
815    } else if (top_level && S_ISFIFO(ff_pkt->statp.st_mode) &&
816               ff_pkt->flags & FO_READFIFO) {
817       ff_pkt->type = FT_FIFO;
818    } else {
819       /* The only remaining types are special (character, ...) files */
820       ff_pkt->type = FT_SPEC;
821    }
822    rtn_stat = handle_file(jcr, ff_pkt, top_level);
823    if (ff_pkt->linked) {
824       ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
825    }
826    return rtn_stat;
827 }
828 
829 int term_find_one(FF_PKT *ff)
830 {
831    struct f_link *lp, *lc;
832    int count = 0;
833    int i;
834 
835 
836    if (ff->linkhash == NULL) return 0;
837 
838    for (i =0 ; i < LINK_HASHTABLE_SIZE; i ++) {
839    /* Free up list of hard linked files */
840       lp = ff->linkhash[i];
841       while (lp) {
842          lc = lp;
843          lp = lp->next;
844          if (lc) {
845             if (lc->digest) {
846                free(lc->digest);
847             }
848             free(lc);
849             count++;
850          }
851       }
852       ff->linkhash[i] = NULL;
853    }
854    free(ff->linkhash);
855    ff->linkhash = NULL;
856    return count;
857 }
858