]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/findlib/find_one.c
Restore win32 dir from Branch-5.2 and update it
[bacula/bacula] / bacula / src / findlib / find_one.c
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 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 #if defined(HAVE_WIN32)
610       /*
611        * A reparse point (WIN32_REPARSE_POINT)
612        *  is something special like one of the following:
613        *  IO_REPARSE_TAG_DFS              0x8000000A
614        *  IO_REPARSE_TAG_DFSR             0x80000012
615        *  IO_REPARSE_TAG_HSM              0xC0000004
616        *  IO_REPARSE_TAG_HSM2             0x80000006
617        *  IO_REPARSE_TAG_SIS              0x80000007
618        *  IO_REPARSE_TAG_SYMLINK          0xA000000C
619        *
620        * A junction point is a:
621        *  IO_REPARSE_TAG_MOUNT_POINT      0xA0000003
622        * which can be either a link to a Volume (WIN32_MOUNT_POINT)
623        * or a link to a directory (WIN32_JUNCTION_POINT)
624        *
625        * Ignore WIN32_REPARSE_POINT and WIN32_JUNCTION_POINT
626        */
627       if (ff_pkt->statp.st_rdev == WIN32_REPARSE_POINT) {
628          ff_pkt->type = FT_REPARSE;
629       } else if (ff_pkt->statp.st_rdev == WIN32_JUNCTION_POINT) {
630          ff_pkt->type = FT_JUNCTION;
631       }
632 #endif
633       /*
634        * Note, we return the directory to the calling program (handle_file)
635        * when we first see the directory (FT_DIRBEGIN.
636        * This allows the program to apply matches and make a
637        * choice whether or not to accept it.  If it is accepted, we
638        * do not immediately save it, but do so only after everything
639        * in the directory is seen (i.e. the FT_DIREND).
640        */
641       rtn_stat = handle_file(jcr, ff_pkt, top_level);
642       if (rtn_stat < 1 || ff_pkt->type == FT_REPARSE ||
643           ff_pkt->type == FT_JUNCTION) {   /* ignore or error status */
644          free(link);
645          return rtn_stat;
646       }
647       /* Done with DIRBEGIN, next call will be DIREND */
648       if (ff_pkt->type == FT_DIRBEGIN) {
649          ff_pkt->type = FT_DIREND;
650       }
651
652       /*
653        * Create a temporary ff packet for this directory
654        *   entry, and defer handling the directory until
655        *   we have recursed into it.  This saves the
656        *   directory after all files have been processed, and
657        *   during the restore, the directory permissions will
658        *   be reset after all the files have been restored.
659        */
660       Dmsg1(300, "Create temp ff packet for dir: %s\n", ff_pkt->fname);
661       FF_PKT *dir_ff_pkt = new_dir_ff_pkt(ff_pkt);
662
663       /*
664        * Do not descend into subdirectories (recurse) if the
665        * user has turned it off for this directory.
666        *
667        * If we are crossing file systems, we are either not allowed
668        * to cross, or we may be restricted by a list of permitted
669        * file systems.
670        */
671       bool is_win32_mount_point = false;
672 #if defined(HAVE_WIN32)
673       is_win32_mount_point = ff_pkt->statp.st_rdev == WIN32_MOUNT_POINT;
674 #endif
675       if (!top_level && ff_pkt->flags & FO_NO_RECURSION) {
676          ff_pkt->type = FT_NORECURSE;
677          recurse = false;
678       } else if (!top_level && (parent_device != ff_pkt->statp.st_dev ||
679                  is_win32_mount_point)) {
680          if(!(ff_pkt->flags & FO_MULTIFS)) {
681             ff_pkt->type = FT_NOFSCHG;
682             recurse = false;
683          } else if (!accept_fstype(ff_pkt, NULL)) {
684             ff_pkt->type = FT_INVALIDFS;
685             recurse = false;
686          } else {
687             ff_pkt->volhas_attrlist = volume_has_attrlist(fname);
688          }
689       }
690       /* If not recursing, just backup dir and return */
691       if (!recurse) {
692          rtn_stat = handle_file(jcr, ff_pkt, top_level);
693          if (ff_pkt->linked) {
694             ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
695          }
696          free(link);
697          free_dir_ff_pkt(dir_ff_pkt);
698          ff_pkt->link = ff_pkt->fname;     /* reset "link" */
699          if (ff_pkt->flags & FO_KEEPATIME) {
700             utime(fname, &restore_times);
701          }
702          return rtn_stat;
703       }
704
705       ff_pkt->link = ff_pkt->fname;     /* reset "link" */
706
707       /*
708        * Descend into or "recurse" into the directory to read
709        *   all the files in it.
710        */
711       errno = 0;
712       if ((directory = opendir(fname)) == NULL) {
713          ff_pkt->type = FT_NOOPEN;
714          ff_pkt->ff_errno = errno;
715          rtn_stat = handle_file(jcr, ff_pkt, top_level);
716          if (ff_pkt->linked) {
717             ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
718          }
719          free(link);
720          free_dir_ff_pkt(dir_ff_pkt);
721          return rtn_stat;
722       }
723
724       /*
725        * Process all files in this directory entry (recursing).
726        *    This would possibly run faster if we chdir to the directory
727        *    before traversing it.
728        */
729       rtn_stat = 1;
730       entry = (struct dirent *)malloc(sizeof(struct dirent) + name_max + 100);
731       for ( ; !job_canceled(jcr); ) {
732          char *p, *q;
733          int i;
734
735          status  = readdir_r(directory, entry, &result);
736          if (status != 0 || result == NULL) {
737 //          Dmsg2(99, "readdir returned stat=%d result=0x%x\n",
738 //             status, (long)result);
739             break;
740          }
741          ASSERT(name_max+1 > (int)sizeof(struct dirent) + (int)NAMELEN(entry));
742          p = entry->d_name;
743          /* Skip `.', `..', and excluded file names.  */
744          if (p[0] == '\0' || (p[0] == '.' && (p[1] == '\0' ||
745              (p[1] == '.' && p[2] == '\0')))) {
746             continue;
747          }
748
749          if ((int)NAMELEN(entry) + len >= link_len) {
750              link_len = len + NAMELEN(entry) + 1;
751              link = (char *)brealloc(link, link_len + 1);
752          }
753          q = link + len;
754          for (i=0; i < (int)NAMELEN(entry); i++) {
755             *q++ = *p++;
756          }
757          *q = 0;
758          if (!file_is_excluded(ff_pkt, link)) {
759             rtn_stat = find_one_file(jcr, ff_pkt, handle_file, link, our_device, false);
760             if (ff_pkt->linked) {
761                ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
762             }
763          }
764       }
765       closedir(directory);
766       free(link);
767       free(entry);
768
769       /*
770        * Now that we have recursed through all the files in the
771        *  directory, we "save" the directory so that after all
772        *  the files are restored, this entry will serve to reset
773        *  the directory modes and dates.  Temp directory values
774        *  were used without this record.
775        */
776       handle_file(jcr, dir_ff_pkt, top_level);       /* handle directory entry */
777       if (ff_pkt->linked) {
778          ff_pkt->linked->FileIndex = dir_ff_pkt->FileIndex;
779       }
780       free_dir_ff_pkt(dir_ff_pkt);
781
782       if (ff_pkt->flags & FO_KEEPATIME) {
783          utime(fname, &restore_times);
784       }
785       ff_pkt->volhas_attrlist = volhas_attrlist;      /* Restore value in case it changed. */
786       return rtn_stat;
787    } /* end check for directory */
788
789    /*
790     * If it is explicitly mentioned (i.e. top_level) and is
791     *  a block device, we do a raw backup of it or if it is
792     *  a fifo, we simply read it.
793     */
794 #ifdef HAVE_FREEBSD_OS
795    /*
796     * On FreeBSD, all block devices are character devices, so
797     *   to be able to read a raw disk, we need the check for
798     *   a character device.
799     * crw-r----- 1 root  operator - 116, 0x00040002 Jun 9 19:32 /dev/ad0s3
800     * crw-r----- 1 root  operator - 116, 0x00040002 Jun 9 19:32 /dev/rad0s3
801     */
802    if (top_level && (S_ISBLK(ff_pkt->statp.st_mode) || S_ISCHR(ff_pkt->statp.st_mode))) {
803 #else
804    if (top_level && S_ISBLK(ff_pkt->statp.st_mode)) {
805 #endif
806       ff_pkt->type = FT_RAW;          /* raw partition */
807    } else if (top_level && S_ISFIFO(ff_pkt->statp.st_mode) &&
808               ff_pkt->flags & FO_READFIFO) {
809       ff_pkt->type = FT_FIFO;
810    } else {
811       /* The only remaining types are special (character, ...) files */
812       ff_pkt->type = FT_SPEC;
813    }
814    rtn_stat = handle_file(jcr, ff_pkt, top_level);
815    if (ff_pkt->linked) {
816       ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
817    }
818    return rtn_stat;
819 }
820
821 int term_find_one(FF_PKT *ff)
822 {
823    struct f_link *lp, *lc;
824    int count = 0;
825    int i;
826
827
828    if (ff->linkhash == NULL) return 0;
829
830    for (i =0 ; i < LINK_HASHTABLE_SIZE; i ++) {
831    /* Free up list of hard linked files */
832       lp = ff->linkhash[i];
833       while (lp) {
834          lc = lp;
835          lp = lp->next;
836          if (lc) {
837             if (lc->digest) {
838                free(lc->digest);
839             }
840             free(lc);
841             count++;
842          }
843       }
844       ff->linkhash[i] = NULL;
845    }
846    free(ff->linkhash);
847    ff->linkhash = NULL;
848    return count;
849 }