]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/findlib/find_one.c
kes Change IncludeDir to Exclude Dir Containing.
[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 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 /* check if a file have changed during backup and display an error */
215 bool has_file_changed(JCR *jcr, FF_PKT *ff_pkt)
216 {
217    struct stat statp;
218    Dmsg1(500, "has_file_changed fname=%s\n",ff_pkt->fname);
219
220    if (ff_pkt->type != FT_REG) { /* not a regular file */
221       return false;
222    }
223
224    if (lstat(ff_pkt->fname, &statp) != 0) {
225       berrno be;
226       Jmsg(jcr, M_WARNING, 0, 
227            _("Cannot stat file %s: ERR=%s\n"),ff_pkt->fname,be.bstrerror());
228       return true;
229    }
230
231    if (statp.st_mtime != ff_pkt->statp.st_mtime) {
232       /* TODO: add time of changes */
233       Jmsg(jcr, M_ERROR, 0, _("%s mtime changed during backup.\n"), ff_pkt->fname);
234       return true;
235    }
236
237    if (statp.st_ctime != ff_pkt->statp.st_ctime) {
238       /* TODO: add time of changes */
239       Jmsg(jcr, M_ERROR, 0, _("%s ctime changed during backup.\n"), ff_pkt->fname);
240       return true;
241    }
242   
243    if (statp.st_size != ff_pkt->statp.st_size) {
244       /* TODO: add size change */
245       Jmsg(jcr, M_ERROR, 0, _("%s size changed during backup.\n"),ff_pkt->fname);
246       return true;
247    }
248
249    if ((statp.st_blksize != ff_pkt->statp.st_blksize) ||
250        (statp.st_blocks  != ff_pkt->statp.st_blocks)) {
251       /* TODO: add size change */
252       Jmsg(jcr, M_ERROR, 0, _("%s size changed during backup.\n"),ff_pkt->fname);
253       return true;
254    }
255
256    return false;
257 }
258
259 /*
260  * For incremental/diffential or accurate backups, we
261  *   determine if the current file has changed.
262  */
263 static bool check_changes(JCR *jcr, FF_PKT *ff_pkt)
264 {
265    /* in special mode (like accurate backup), the programmer can 
266     * choose his comparison function.
267     */
268    if (ff_pkt->check_fct) {
269       return ff_pkt->check_fct(jcr, ff_pkt);
270    }
271
272    /* For normal backups (incr/diff), we use this default
273     * behaviour
274     */
275    if (ff_pkt->incremental &&
276        (ff_pkt->statp.st_mtime < ff_pkt->save_time &&
277         ((ff_pkt->flags & FO_MTIMEONLY) ||
278          ff_pkt->statp.st_ctime < ff_pkt->save_time))) 
279    {
280       return false;
281    } 
282
283    return true;
284 }
285
286 /*
287  * Find a single file.
288  * handle_file is the callback for handling the file.
289  * p is the filename
290  * parent_device is the device we are currently on
291  * top_level is 1 when not recursing or 0 when
292  *  descending into a directory.
293  */
294 int
295 find_one_file(JCR *jcr, FF_PKT *ff_pkt, 
296                int handle_file(JCR *jcr, FF_PKT *ff, bool top_level),
297                char *fname, dev_t parent_device, bool top_level)
298 {
299    struct utimbuf restore_times;
300    int rtn_stat;
301    int len;
302
303    ff_pkt->fname = ff_pkt->link = fname;
304
305    if (lstat(fname, &ff_pkt->statp) != 0) {
306        /* Cannot stat file */
307        ff_pkt->type = FT_NOSTAT;
308        ff_pkt->ff_errno = errno;
309        return handle_file(jcr, ff_pkt, top_level);
310    }
311
312    Dmsg1(300, "File ----: %s\n", fname);
313
314    /* Save current times of this directory in case we need to
315     * reset them because the user doesn't want them changed.
316     */
317    restore_times.actime = ff_pkt->statp.st_atime;
318    restore_times.modtime = ff_pkt->statp.st_mtime;
319
320    /*
321     * We check for allowed fstypes and drivetypes at top_level and fstype change (below).
322     */
323    if (top_level) {
324       if (!accept_fstype(ff_pkt, NULL)) {
325          ff_pkt->type = FT_INVALIDFS;
326          if (ff_pkt->flags & FO_KEEPATIME) {
327             utime(fname, &restore_times);
328          }
329
330          char fs[100];
331
332          if (!fstype(ff_pkt->fname, fs, sizeof(fs))) {
333              bstrncpy(fs, "unknown", sizeof(fs));
334          }
335
336          Jmsg(jcr, M_INFO, 0, _("Top level directory \"%s\" has unlisted fstype \"%s\"\n"), fname, fs);
337          return 1;      /* Just ignore this error - or the whole backup is cancelled */
338       }
339       if (!accept_drivetype(ff_pkt, NULL)) {
340          ff_pkt->type = FT_INVALIDDT;
341          if (ff_pkt->flags & FO_KEEPATIME) {
342             utime(fname, &restore_times);
343          }
344
345          char dt[100];
346
347          if (!drivetype(ff_pkt->fname, dt, sizeof(dt))) {
348              bstrncpy(dt, "unknown", sizeof(dt));
349          }
350
351          Jmsg(jcr, M_INFO, 0, _("Top level directory \"%s\" has an unlisted drive type \"%s\"\n"), fname, dt);
352          return 1;      /* Just ignore this error - or the whole backup is cancelled */
353       }
354       ff_pkt->volhas_attrlist = volume_has_attrlist(fname);
355    }
356    /*
357     * If this is an Incremental backup, see if file was modified
358     * since our last "save_time", presumably the last Full save
359     * or Incremental.
360     */
361    if (   ff_pkt->incremental 
362        && !S_ISDIR(ff_pkt->statp.st_mode) 
363        && !check_changes(jcr, ff_pkt)) 
364    {
365       Dmsg1(500, "Non-directory incremental: %s\n", ff_pkt->fname);
366       ff_pkt->type = FT_NOCHG;
367       return handle_file(jcr, ff_pkt, top_level);
368    }
369
370 #ifdef HAVE_DARWIN_OS
371    if (ff_pkt->flags & FO_HFSPLUS && ff_pkt->volhas_attrlist
372          && S_ISREG(ff_pkt->statp.st_mode)) {
373        /* TODO: initialise attrList once elsewhere? */
374        struct attrlist attrList;
375        memset(&attrList, 0, sizeof(attrList));
376        attrList.bitmapcount = ATTR_BIT_MAP_COUNT;
377        attrList.commonattr = ATTR_CMN_FNDRINFO;
378        attrList.fileattr = ATTR_FILE_RSRCLENGTH;
379        if (getattrlist(fname, &attrList, &ff_pkt->hfsinfo,
380                 sizeof(ff_pkt->hfsinfo), FSOPT_NOFOLLOW) != 0) {
381           ff_pkt->type = FT_NOSTAT;
382           ff_pkt->ff_errno = errno;
383           return handle_file(jcr, ff_pkt, top_level);
384        }
385    }
386 #endif
387
388    ff_pkt->LinkFI = 0;
389    /*
390     * Handle hard linked files
391     *
392     * Maintain a list of hard linked files already backed up. This
393     *  allows us to ensure that the data of each file gets backed
394     *  up only once.
395     */
396    if (!(ff_pkt->flags & FO_NO_HARDLINK)
397        && ff_pkt->statp.st_nlink > 1
398        && (S_ISREG(ff_pkt->statp.st_mode)
399            || S_ISCHR(ff_pkt->statp.st_mode)
400            || S_ISBLK(ff_pkt->statp.st_mode)
401            || S_ISFIFO(ff_pkt->statp.st_mode)
402            || S_ISSOCK(ff_pkt->statp.st_mode))) {
403
404        struct f_link *lp;
405        if (ff_pkt->linkhash == NULL) {
406            ff_pkt->linkhash = (link_t **)bmalloc(LINK_HASHTABLE_SIZE * sizeof(link_t *));
407            memset(ff_pkt->linkhash, 0, LINK_HASHTABLE_SIZE * sizeof(link_t *));
408        }
409        const int linkhash = LINKHASH(ff_pkt->statp);
410
411       /* Search link list of hard linked files */
412        for (lp = ff_pkt->linkhash[linkhash]; lp; lp = lp->next)
413          if (lp->ino == (ino_t)ff_pkt->statp.st_ino &&
414              lp->dev == (dev_t)ff_pkt->statp.st_dev) {
415              /* If we have already backed up the hard linked file don't do it again */
416              if (strcmp(lp->name, fname) == 0) {
417                 Dmsg2(400, "== Name identical skip FI=%d file=%s\n", lp->FileIndex, fname);
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              ff_pkt->linked = 0;
424              rtn_stat = handle_file(jcr, ff_pkt, top_level);
425              Dmsg3(400, "FT_LNKSAVED FI=%d LinkFI=%d file=%s\n", 
426                 ff_pkt->FileIndex, lp->FileIndex, lp->name);
427              return rtn_stat;
428          }
429
430       /* File not previously dumped. Chain it into our list. */
431       len = strlen(fname) + 1;
432       lp = (struct f_link *)bmalloc(sizeof(struct f_link) + len);
433       lp->ino = ff_pkt->statp.st_ino;
434       lp->dev = ff_pkt->statp.st_dev;
435       lp->FileIndex = 0;                  /* set later */
436       bstrncpy(lp->name, fname, len);
437       lp->next = ff_pkt->linkhash[linkhash];
438       ff_pkt->linkhash[linkhash] = lp;
439       ff_pkt->linked = lp;            /* mark saved link */
440       Dmsg2(400, "added to hash FI=%d file=%s\n", ff_pkt->FileIndex, lp->name);
441    } else {
442       ff_pkt->linked = NULL;
443    }
444
445    /* This is not a link to a previously dumped file, so dump it.  */
446    if (S_ISREG(ff_pkt->statp.st_mode)) {
447       boffset_t sizeleft;
448
449       sizeleft = ff_pkt->statp.st_size;
450
451       /* Don't bother opening empty, world readable files.  Also do not open
452          files when archive is meant for /dev/null.  */
453       if (ff_pkt->null_output_device || (sizeleft == 0
454               && MODE_RALL == (MODE_RALL & ff_pkt->statp.st_mode))) {
455          ff_pkt->type = FT_REGE;
456       } else {
457          ff_pkt->type = FT_REG;
458       }
459       rtn_stat = handle_file(jcr, ff_pkt, top_level);
460       if (ff_pkt->linked) {
461          ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
462       }
463       Dmsg3(400, "FT_REG FI=%d linked=%d file=%s\n", ff_pkt->FileIndex, 
464          ff_pkt->linked ? 1 : 0, fname);
465       if (ff_pkt->flags & FO_KEEPATIME) {
466          utime(fname, &restore_times);
467       }       
468       return rtn_stat;
469
470
471    } else if (S_ISLNK(ff_pkt->statp.st_mode)) {  /* soft link */
472       int size;
473       char *buffer = (char *)alloca(path_max + name_max + 102);
474
475       size = readlink(fname, buffer, path_max + name_max + 101);
476       if (size < 0) {
477          /* Could not follow link */
478          ff_pkt->type = FT_NOFOLLOW;
479          ff_pkt->ff_errno = errno;
480          rtn_stat = handle_file(jcr, ff_pkt, top_level);
481          if (ff_pkt->linked) {
482             ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
483          }
484          return rtn_stat;
485       }
486       buffer[size] = 0;
487       ff_pkt->link = buffer;          /* point to link */
488       ff_pkt->type = FT_LNK;          /* got a real link */
489       rtn_stat = handle_file(jcr, ff_pkt, top_level);
490       if (ff_pkt->linked) {
491          ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
492       }
493       return rtn_stat;
494
495    } else if (S_ISDIR(ff_pkt->statp.st_mode)) {
496       DIR *directory;
497       struct dirent *entry, *result;
498       char *link;
499       int link_len;
500       int len;
501       int status;
502       dev_t our_device = ff_pkt->statp.st_dev;
503       bool recurse = true;
504       bool volhas_attrlist = ff_pkt->volhas_attrlist;    /* Remember this if we recurse */
505
506       /*
507        * If we are using Win32 (non-portable) backup API, don't check
508        *  access as everything is more complicated, and
509        *  in principle, we should be able to access everything.
510        */
511       if (!have_win32_api() || (ff_pkt->flags & FO_PORTABLE)) {
512          if (access(fname, R_OK) == -1 && geteuid() != 0) {
513             /* Could not access() directory */
514             ff_pkt->type = FT_NOACCESS;
515             ff_pkt->ff_errno = errno;
516             rtn_stat = handle_file(jcr, ff_pkt, top_level);
517             if (ff_pkt->linked) {
518                ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
519             }
520             return rtn_stat;
521          }
522       }
523
524       /*
525        * Ignore this directory and everything below if the file .nobackup
526        * (or what is defined for IgnoreDir in this fileset) exists
527        */
528       if (ff_pkt->ignoredir != NULL) {
529          struct stat sb;
530          char fname[MAXPATHLEN];
531
532          if (strlen(ff_pkt->fname) + strlen("/") +
533             strlen(ff_pkt->ignoredir) + 1 > MAXPATHLEN)
534             return 1;   /* Is this wisdom? */
535
536          strcpy(fname, ff_pkt->fname);
537          strcat(fname, "/");
538          strcat(fname, ff_pkt->ignoredir);
539          if (stat(fname, &sb) == 0) {
540             Dmsg2(100, "Directory '%s' ignored (found %s)\n",
541                ff_pkt->fname, ff_pkt->ignoredir);
542             return 1;      /* Just ignore this directory */
543          }
544       }
545
546       /* Build a canonical directory name with a trailing slash in link var */
547       len = strlen(fname);
548       link_len = len + 200;
549       link = (char *)bmalloc(link_len + 2);
550       bstrncpy(link, fname, link_len);
551       /* Strip all trailing slashes */
552       while (len >= 1 && IsPathSeparator(link[len - 1]))
553         len--;
554       link[len++] = '/';             /* add back one */
555       link[len] = 0;
556
557       ff_pkt->link = link;
558       if (ff_pkt->incremental && !check_changes(jcr, ff_pkt)) {
559          /* Incremental option, directory entry not changed */
560          ff_pkt->type = FT_DIRNOCHG;
561       } else {
562          ff_pkt->type = FT_DIRBEGIN;
563       }
564       /*
565        * We have set st_rdev to 1 if it is a reparse point, otherwise 0,
566        *  if st_rdev is 2, it is a mount point 
567        */
568 #if defined(HAVE_WIN32)
569       if (ff_pkt->statp.st_rdev == WIN32_REPARSE_POINT) {
570          ff_pkt->type = FT_REPARSE;
571       }
572 #endif 
573       /*
574        * Note, we return the directory to the calling program (handle_file)
575        * when we first see the directory (FT_DIRBEGIN.
576        * This allows the program to apply matches and make a
577        * choice whether or not to accept it.  If it is accepted, we
578        * do not immediately save it, but do so only after everything
579        * in the directory is seen (i.e. the FT_DIREND).
580        */
581       rtn_stat = handle_file(jcr, ff_pkt, top_level);
582       if (rtn_stat < 1 || ff_pkt->type == FT_REPARSE) {   /* ignore or error status */
583          free(link);
584          return rtn_stat;
585       }
586       /* Done with DIRBEGIN, next call will be DIREND */
587       if (ff_pkt->type == FT_DIRBEGIN) {
588          ff_pkt->type = FT_DIREND;
589       }
590
591       /*
592        * Create a temporary ff packet for this directory
593        *   entry, and defer handling the directory until
594        *   we have recursed into it.  This saves the
595        *   directory after all files have been processed, and
596        *   during the restore, the directory permissions will
597        *   be reset after all the files have been restored.
598        */
599       Dmsg1(300, "Create temp ff packet for dir: %s\n", ff_pkt->fname);
600       FF_PKT *dir_ff_pkt = new_dir_ff_pkt(ff_pkt);
601
602       /*
603        * Do not descend into subdirectories (recurse) if the
604        * user has turned it off for this directory.
605        *
606        * If we are crossing file systems, we are either not allowed
607        * to cross, or we may be restricted by a list of permitted
608        * file systems.
609        */
610       bool is_win32_mount_point = false;
611 #if defined(HAVE_WIN32)
612       is_win32_mount_point = ff_pkt->statp.st_rdev == WIN32_MOUNT_POINT;
613 #endif
614       if (!top_level && ff_pkt->flags & FO_NO_RECURSION) {
615          ff_pkt->type = FT_NORECURSE;
616          recurse = false;
617       } else if (!top_level && (parent_device != ff_pkt->statp.st_dev ||
618                  is_win32_mount_point)) {
619          if(!(ff_pkt->flags & FO_MULTIFS)) {
620             ff_pkt->type = FT_NOFSCHG;
621             recurse = false;
622          } else if (!accept_fstype(ff_pkt, NULL)) {
623             ff_pkt->type = FT_INVALIDFS;
624             recurse = false;
625          } else {
626             ff_pkt->volhas_attrlist = volume_has_attrlist(fname);
627          }
628       }
629       /* If not recursing, just backup dir and return */
630       if (!recurse) {
631          rtn_stat = handle_file(jcr, ff_pkt, top_level);
632          if (ff_pkt->linked) {
633             ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
634          }
635          free(link);
636          free_dir_ff_pkt(dir_ff_pkt);
637          ff_pkt->link = ff_pkt->fname;     /* reset "link" */
638          if (ff_pkt->flags & FO_KEEPATIME) {
639             utime(fname, &restore_times);
640          }
641          return rtn_stat;
642       }
643
644       ff_pkt->link = ff_pkt->fname;     /* reset "link" */
645
646       /*
647        * Descend into or "recurse" into the directory to read
648        *   all the files in it.
649        */
650       errno = 0;
651       if ((directory = opendir(fname)) == NULL) {
652          ff_pkt->type = FT_NOOPEN;
653          ff_pkt->ff_errno = errno;
654          rtn_stat = handle_file(jcr, ff_pkt, top_level);
655          if (ff_pkt->linked) {
656             ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
657          }
658          free(link);
659          free_dir_ff_pkt(dir_ff_pkt);
660          return rtn_stat;
661       }
662
663       /*
664        * Process all files in this directory entry (recursing).
665        *    This would possibly run faster if we chdir to the directory
666        *    before traversing it.
667        */
668       rtn_stat = 1;
669       entry = (struct dirent *)malloc(sizeof(struct dirent) + name_max + 100);
670       for ( ; !job_canceled(jcr); ) {
671          char *p, *q;
672          int i;
673
674          status  = readdir_r(directory, entry, &result);
675          if (status != 0 || result == NULL) {
676 //          Dmsg2(99, "readdir returned stat=%d result=0x%x\n",
677 //             status, (long)result);
678             break;
679          }
680          ASSERT(name_max+1 > (int)sizeof(struct dirent) + (int)NAMELEN(entry));
681          p = entry->d_name;
682          /* Skip `.', `..', and excluded file names.  */
683          if (p[0] == '\0' || (p[0] == '.' && (p[1] == '\0' ||
684              (p[1] == '.' && p[2] == '\0')))) {
685             continue;
686          }
687
688          if ((int)NAMELEN(entry) + len >= link_len) {
689              link_len = len + NAMELEN(entry) + 1;
690              link = (char *)brealloc(link, link_len + 1);
691          }
692          q = link + len;
693          for (i=0; i < (int)NAMELEN(entry); i++) {
694             *q++ = *p++;
695          }
696          *q = 0;
697          if (!file_is_excluded(ff_pkt, link)) {
698             rtn_stat = find_one_file(jcr, ff_pkt, handle_file, link, our_device, false);
699             if (ff_pkt->linked) {
700                ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
701             }
702          }
703       }
704       closedir(directory);
705       free(link);
706       free(entry);
707
708       /*
709        * Now that we have recursed through all the files in the
710        *  directory, we "save" the directory so that after all
711        *  the files are restored, this entry will serve to reset
712        *  the directory modes and dates.  Temp directory values
713        *  were used without this record.
714        */
715       handle_file(jcr, dir_ff_pkt, top_level);       /* handle directory entry */
716       if (ff_pkt->linked) {
717          ff_pkt->linked->FileIndex = dir_ff_pkt->FileIndex;
718       }
719       free_dir_ff_pkt(dir_ff_pkt);
720
721       if (ff_pkt->flags & FO_KEEPATIME) {
722          utime(fname, &restore_times);
723       }
724       ff_pkt->volhas_attrlist = volhas_attrlist;      /* Restore value in case it changed. */
725       return rtn_stat;
726    } /* end check for directory */
727
728    /*
729     * If it is explicitly mentioned (i.e. top_level) and is
730     *  a block device, we do a raw backup of it or if it is
731     *  a fifo, we simply read it.
732     */
733 #ifdef HAVE_FREEBSD_OS
734    /*
735     * On FreeBSD, all block devices are character devices, so
736     *   to be able to read a raw disk, we need the check for
737     *   a character device.
738     * crw-r----- 1 root  operator - 116, 0x00040002 Jun 9 19:32 /dev/ad0s3
739     * crw-r----- 1 root  operator - 116, 0x00040002 Jun 9 19:32 /dev/rad0s3
740     */
741    if (top_level && (S_ISBLK(ff_pkt->statp.st_mode) || S_ISCHR(ff_pkt->statp.st_mode))) {
742 #else
743    if (top_level && S_ISBLK(ff_pkt->statp.st_mode)) {
744 #endif
745       ff_pkt->type = FT_RAW;          /* raw partition */
746    } else if (top_level && S_ISFIFO(ff_pkt->statp.st_mode) &&
747               ff_pkt->flags & FO_READFIFO) {
748       ff_pkt->type = FT_FIFO;
749    } else {
750       /* The only remaining types are special (character, ...) files */
751       ff_pkt->type = FT_SPEC;
752    }
753    rtn_stat = handle_file(jcr, ff_pkt, top_level);
754    if (ff_pkt->linked) {
755       ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
756    }
757    return rtn_stat;
758 }
759
760 int term_find_one(FF_PKT *ff)
761 {
762    struct f_link *lp, *lc;
763    int count = 0;
764    int i;
765
766    
767    if (ff->linkhash == NULL) return 0;
768
769    for (i =0 ; i < LINK_HASHTABLE_SIZE; i ++) {
770    /* Free up list of hard linked files */
771        lp = ff->linkhash[i];
772        while (lp) {
773       lc = lp;
774       lp = lp->next;
775       if (lc) {
776          free(lc);
777          count++;
778       }
779    }
780        ff->linkhash[i] = NULL;
781    }
782    free(ff->linkhash);
783    ff->linkhash = NULL;
784    return count;
785 }