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