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