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