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