]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/findlib/find_one.c
Detect mount/junction points and ignore junctions in Windows
[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 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       /*
590        * A reparse point (WIN32_REPARSE_POINT)
591        *  is something special like one of the following: 
592        *  IO_REPARSE_TAG_DFS              0x8000000A
593        *  IO_REPARSE_TAG_DFSR             0x80000012
594        *  IO_REPARSE_TAG_HSM              0xC0000004
595        *  IO_REPARSE_TAG_HSM2             0x80000006
596        *  IO_REPARSE_TAG_SIS              0x80000007
597        *  IO_REPARSE_TAG_SYMLINK          0xA000000C
598        *
599        * A junction point is a:
600        *  IO_REPARSE_TAG_MOUNT_POINT      0xA0000003
601        * which can be either a link to a Volume (WIN32_MOUNT_POINT)
602        * or a link to a directory (WIN32_JUNCTION_POINT)
603        *
604        * Ignore WIN32_REPARSE_POINT and WIN32_JUNCTION_POINT
605        */
606       if (ff_pkt->statp.st_rdev == WIN32_REPARSE_POINT) {
607          ff_pkt->type = FT_REPARSE;
608       } else if (ff_pkt->statp.st_rdev == WIN32_JUNCTION_POINT) {
609          ff_pkt->type = FT_JUNCTION;
610       }
611 #endif 
612       /*
613        * Note, we return the directory to the calling program (handle_file)
614        * when we first see the directory (FT_DIRBEGIN.
615        * This allows the program to apply matches and make a
616        * choice whether or not to accept it.  If it is accepted, we
617        * do not immediately save it, but do so only after everything
618        * in the directory is seen (i.e. the FT_DIREND).
619        */
620       rtn_stat = handle_file(jcr, ff_pkt, top_level);
621       if (rtn_stat < 1 || ff_pkt->type == FT_REPARSE ||
622           ff_pkt->type == FT_JUNCTION) {   /* ignore or error status */
623          free(link);
624          return rtn_stat;
625       }
626       /* Done with DIRBEGIN, next call will be DIREND */
627       if (ff_pkt->type == FT_DIRBEGIN) {
628          ff_pkt->type = FT_DIREND;
629       }
630
631       /*
632        * Create a temporary ff packet for this directory
633        *   entry, and defer handling the directory until
634        *   we have recursed into it.  This saves the
635        *   directory after all files have been processed, and
636        *   during the restore, the directory permissions will
637        *   be reset after all the files have been restored.
638        */
639       Dmsg1(300, "Create temp ff packet for dir: %s\n", ff_pkt->fname);
640       FF_PKT *dir_ff_pkt = new_dir_ff_pkt(ff_pkt);
641
642       /*
643        * Do not descend into subdirectories (recurse) if the
644        * user has turned it off for this directory.
645        *
646        * If we are crossing file systems, we are either not allowed
647        * to cross, or we may be restricted by a list of permitted
648        * file systems.
649        */
650       bool is_win32_mount_point = false;
651 #if defined(HAVE_WIN32)
652       is_win32_mount_point = ff_pkt->statp.st_rdev == WIN32_MOUNT_POINT;
653 #endif
654       if (!top_level && ff_pkt->flags & FO_NO_RECURSION) {
655          ff_pkt->type = FT_NORECURSE;
656          recurse = false;
657       } else if (!top_level && (parent_device != ff_pkt->statp.st_dev ||
658                  is_win32_mount_point)) {
659          if(!(ff_pkt->flags & FO_MULTIFS)) {
660             ff_pkt->type = FT_NOFSCHG;
661             recurse = false;
662          } else if (!accept_fstype(ff_pkt, NULL)) {
663             ff_pkt->type = FT_INVALIDFS;
664             recurse = false;
665          } else {
666             ff_pkt->volhas_attrlist = volume_has_attrlist(fname);
667          }
668       }
669       /* If not recursing, just backup dir and return */
670       if (!recurse) {
671          rtn_stat = handle_file(jcr, ff_pkt, top_level);
672          if (ff_pkt->linked) {
673             ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
674          }
675          free(link);
676          free_dir_ff_pkt(dir_ff_pkt);
677          ff_pkt->link = ff_pkt->fname;     /* reset "link" */
678          if (ff_pkt->flags & FO_KEEPATIME) {
679             utime(fname, &restore_times);
680          }
681          return rtn_stat;
682       }
683
684       ff_pkt->link = ff_pkt->fname;     /* reset "link" */
685
686       /*
687        * Descend into or "recurse" into the directory to read
688        *   all the files in it.
689        */
690       errno = 0;
691       if ((directory = opendir(fname)) == NULL) {
692          ff_pkt->type = FT_NOOPEN;
693          ff_pkt->ff_errno = errno;
694          rtn_stat = handle_file(jcr, ff_pkt, top_level);
695          if (ff_pkt->linked) {
696             ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
697          }
698          free(link);
699          free_dir_ff_pkt(dir_ff_pkt);
700          return rtn_stat;
701       }
702
703       /*
704        * Process all files in this directory entry (recursing).
705        *    This would possibly run faster if we chdir to the directory
706        *    before traversing it.
707        */
708       rtn_stat = 1;
709       entry = (struct dirent *)malloc(sizeof(struct dirent) + name_max + 100);
710       for ( ; !job_canceled(jcr); ) {
711          char *p, *q;
712          int i;
713
714          status  = readdir_r(directory, entry, &result);
715          if (status != 0 || result == NULL) {
716 //          Dmsg2(99, "readdir returned stat=%d result=0x%x\n",
717 //             status, (long)result);
718             break;
719          }
720          ASSERT(name_max+1 > (int)sizeof(struct dirent) + (int)NAMELEN(entry));
721          p = entry->d_name;
722          /* Skip `.', `..', and excluded file names.  */
723          if (p[0] == '\0' || (p[0] == '.' && (p[1] == '\0' ||
724              (p[1] == '.' && p[2] == '\0')))) {
725             continue;
726          }
727
728          if ((int)NAMELEN(entry) + len >= link_len) {
729              link_len = len + NAMELEN(entry) + 1;
730              link = (char *)brealloc(link, link_len + 1);
731          }
732          q = link + len;
733          for (i=0; i < (int)NAMELEN(entry); i++) {
734             *q++ = *p++;
735          }
736          *q = 0;
737          if (!file_is_excluded(ff_pkt, link)) {
738             rtn_stat = find_one_file(jcr, ff_pkt, handle_file, link, our_device, false);
739             if (ff_pkt->linked) {
740                ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
741             }
742          }
743       }
744       closedir(directory);
745       free(link);
746       free(entry);
747
748       /*
749        * Now that we have recursed through all the files in the
750        *  directory, we "save" the directory so that after all
751        *  the files are restored, this entry will serve to reset
752        *  the directory modes and dates.  Temp directory values
753        *  were used without this record.
754        */
755       handle_file(jcr, dir_ff_pkt, top_level);       /* handle directory entry */
756       if (ff_pkt->linked) {
757          ff_pkt->linked->FileIndex = dir_ff_pkt->FileIndex;
758       }
759       free_dir_ff_pkt(dir_ff_pkt);
760
761       if (ff_pkt->flags & FO_KEEPATIME) {
762          utime(fname, &restore_times);
763       }
764       ff_pkt->volhas_attrlist = volhas_attrlist;      /* Restore value in case it changed. */
765       return rtn_stat;
766    } /* end check for directory */
767
768    /*
769     * If it is explicitly mentioned (i.e. top_level) and is
770     *  a block device, we do a raw backup of it or if it is
771     *  a fifo, we simply read it.
772     */
773 #ifdef HAVE_FREEBSD_OS
774    /*
775     * On FreeBSD, all block devices are character devices, so
776     *   to be able to read a raw disk, we need the check for
777     *   a character device.
778     * crw-r----- 1 root  operator - 116, 0x00040002 Jun 9 19:32 /dev/ad0s3
779     * crw-r----- 1 root  operator - 116, 0x00040002 Jun 9 19:32 /dev/rad0s3
780     */
781    if (top_level && (S_ISBLK(ff_pkt->statp.st_mode) || S_ISCHR(ff_pkt->statp.st_mode))) {
782 #else
783    if (top_level && S_ISBLK(ff_pkt->statp.st_mode)) {
784 #endif
785       ff_pkt->type = FT_RAW;          /* raw partition */
786    } else if (top_level && S_ISFIFO(ff_pkt->statp.st_mode) &&
787               ff_pkt->flags & FO_READFIFO) {
788       ff_pkt->type = FT_FIFO;
789    } else {
790       /* The only remaining types are special (character, ...) files */
791       ff_pkt->type = FT_SPEC;
792    }
793    rtn_stat = handle_file(jcr, ff_pkt, top_level);
794    if (ff_pkt->linked) {
795       ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
796    }
797    return rtn_stat;
798 }
799
800 int term_find_one(FF_PKT *ff)
801 {
802    struct f_link *lp, *lc;
803    int count = 0;
804    int i;
805
806    
807    if (ff->linkhash == NULL) return 0;
808
809    for (i =0 ; i < LINK_HASHTABLE_SIZE; i ++) {
810    /* Free up list of hard linked files */
811        lp = ff->linkhash[i];
812        while (lp) {
813       lc = lp;
814       lp = lp->next;
815       if (lc) {
816          free(lc);
817          count++;
818       }
819    }
820        ff->linkhash[i] = NULL;
821    }
822    free(ff->linkhash);
823    ff->linkhash = NULL;
824    return count;
825 }