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