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