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