2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2015 Kern Sibbald
5 Copyright (C) 2000-2014 Free Software Foundation Europe e.V.
7 The original author of Bacula is Kern Sibbald, with contributions
8 from many others, a complete list can be found in the file AUTHORS.
10 You may use this file and others of this release according to the
11 license defined in the LICENSE file, which includes the Affero General
12 Public License, v3.0 ("AGPLv3") and some additional permissions and
13 terms pursuant to its AGPLv3 Section 7.
15 This notice must be preserved when any source code is
16 conveyed and/or propagated.
18 Bacula(R) is a registered trademark of Kern Sibbald.
22 This file was derived from GNU TAR source code. Except for a few key
23 ideas, it has been entirely rewritten for Bacula.
27 Thanks to the TAR programmers.
34 #include <sys/param.h>
35 #include <sys/mount.h>
39 extern int32_t name_max; /* filename max length */
40 extern int32_t path_max; /* path name max length */
43 * Structure for keeping track of hard linked files, we
44 * keep an entry for each hardlinked file that we save,
45 * which is the first one found. For all the other files that
46 * are linked to this one, we save only the directory
47 * entry so we can link it.
51 dev_t dev; /* device */
52 ino_t ino; /* inode with device is unique */
53 uint32_t FileIndex; /* Bacula FileIndex of this file */
54 int32_t digest_stream; /* Digest type if needed */
55 uint32_t digest_len; /* Digest len if needed */
56 char *digest; /* Checksum of the file if needed */
57 char name[1]; /* The name */
60 typedef struct f_link link_t;
61 #define LINK_HASHTABLE_BITS 16
62 #define LINK_HASHTABLE_SIZE (1<<LINK_HASHTABLE_BITS)
63 #define LINK_HASHTABLE_MASK (LINK_HASHTABLE_SIZE-1)
65 static inline int LINKHASH(const struct stat &info)
67 int hash = info.st_dev;
68 unsigned long long i = info.st_ino;
76 return hash & LINK_HASHTABLE_MASK;
80 * Create a new directory Find File packet, but copy
81 * some of the essential info from the current packet.
82 * However, be careful to zero out the rest of the
85 static FF_PKT *new_dir_ff_pkt(FF_PKT *ff_pkt)
87 FF_PKT *dir_ff_pkt = (FF_PKT *)bmalloc(sizeof(FF_PKT));
88 memcpy(dir_ff_pkt, ff_pkt, sizeof(FF_PKT));
89 dir_ff_pkt->fname = bstrdup(ff_pkt->fname);
90 dir_ff_pkt->link = bstrdup(ff_pkt->link);
91 dir_ff_pkt->sys_fname = get_pool_memory(PM_FNAME);
93 if (ff_pkt->strip_snap_path) {
94 dir_ff_pkt->fname_save = get_pool_memory(PM_FNAME);
95 dir_ff_pkt->link_save = get_pool_memory(PM_FNAME);
96 pm_strcpy(dir_ff_pkt->fname_save, ff_pkt->fname_save);
97 pm_strcpy(dir_ff_pkt->link_save, ff_pkt->link_save);
100 dir_ff_pkt->fname_save = NULL;
101 dir_ff_pkt->link_save = NULL;
104 dir_ff_pkt->included_files_list = NULL;
105 dir_ff_pkt->excluded_files_list = NULL;
106 dir_ff_pkt->excluded_paths_list = NULL;
107 dir_ff_pkt->linkhash = NULL;
108 dir_ff_pkt->ignoredir_fname = NULL;
113 * Free the temp directory ff_pkt
115 static void free_dir_ff_pkt(FF_PKT *dir_ff_pkt)
117 free(dir_ff_pkt->fname);
118 free(dir_ff_pkt->link);
119 free_pool_memory(dir_ff_pkt->sys_fname);
120 if (dir_ff_pkt->fname_save) {
121 free_pool_memory(dir_ff_pkt->fname_save);
123 if (dir_ff_pkt->link_save) {
124 free_pool_memory(dir_ff_pkt->link_save);
130 * Check to see if we allow the file system type of a file or directory.
131 * If we do not have a list of file system types, we accept anything.
133 static int accept_fstype(FF_PKT *ff, void *dummy) {
138 if (ff->fstypes.size()) {
140 if (!fstype(ff, fs, sizeof(fs))) {
141 Dmsg1(50, "Cannot determine file system type for \"%s\"\n", ff->fname);
143 for (i = 0; i < ff->fstypes.size(); ++i) {
144 if (strcmp(fs, (char *)ff->fstypes.get(i)) == 0) {
145 Dmsg2(100, "Accepting fstype %s for \"%s\"\n", fs, ff->fname);
149 Dmsg3(200, "fstype %s for \"%s\" does not match %s\n", fs,
150 ff->fname, ff->fstypes.get(i));
158 * Check to see if we allow the drive type of a file or directory.
159 * If we do not have a list of drive types, we accept anything.
161 static int accept_drivetype(FF_PKT *ff, void *dummy) {
166 if (ff->drivetypes.size()) {
168 if (!drivetype(ff->fname, dt, sizeof(dt))) {
169 Dmsg1(50, "Cannot determine drive type for \"%s\"\n", ff->fname);
171 for (i = 0; i < ff->drivetypes.size(); ++i) {
172 if (strcmp(dt, (char *)ff->drivetypes.get(i)) == 0) {
173 Dmsg2(100, "Accepting drive type %s for \"%s\"\n", dt, ff->fname);
177 Dmsg3(200, "drive type %s for \"%s\" does not match %s\n", dt,
178 ff->fname, ff->drivetypes.get(i));
186 * This function determines whether we can use getattrlist()
187 * It's odd, but we have to use the function to determine that...
188 * Also, the man pages talk about things as if they were implemented.
190 * On Mac OS X, this succesfully differentiates between HFS+ and UFS
191 * volumes, which makes me trust it is OK for others, too.
193 static bool volume_has_attrlist(const char *fname)
195 #ifdef HAVE_DARWIN_OS
197 struct volinfo_struct {
198 unsigned long length; /* Mandatory field */
199 vol_capabilities_attr_t info; /* Volume capabilities */
201 struct attrlist attrList;
203 memset(&attrList, 0, sizeof(attrList));
204 attrList.bitmapcount = ATTR_BIT_MAP_COUNT;
205 attrList.volattr = ATTR_VOL_INFO | ATTR_VOL_CAPABILITIES;
206 if (statfs(fname, &st) == 0) {
207 /* We need to check on the mount point */
208 if (getattrlist(st.f_mntonname, &attrList, &vol, sizeof(vol), FSOPT_NOFOLLOW) == 0
209 && (vol.info.capabilities[VOL_CAPABILITIES_INTERFACES] & VOL_CAP_INT_ATTRLIST)
210 && (vol.info.valid[VOL_CAPABILITIES_INTERFACES] & VOL_CAP_INT_ATTRLIST)) {
219 * check for BSD nodump flag
221 static bool no_dump(JCR *jcr, FF_PKT *ff_pkt)
223 #if defined(HAVE_CHFLAGS) && defined(UF_NODUMP)
224 if ( (ff_pkt->flags & FO_HONOR_NODUMP) &&
225 (ff_pkt->statp.st_flags & UF_NODUMP) ) {
226 Jmsg(jcr, M_INFO, 1, _(" NODUMP flag set - will not process %s\n"),
228 return true; /* do not backup this file */
231 return false; /* do backup */
234 /* check if a file have changed during backup and display an error */
235 bool has_file_changed(JCR *jcr, FF_PKT *ff_pkt)
238 Dmsg1(500, "has_file_changed fname=%s\n",ff_pkt->fname);
240 if (ff_pkt->type != FT_REG) { /* not a regular file */
244 if (lstat(ff_pkt->fname, &statp) != 0) {
246 Jmsg(jcr, M_WARNING, 0,
247 _("Cannot stat file %s: ERR=%s\n"),ff_pkt->fname,be.bstrerror());
251 if (statp.st_mtime != ff_pkt->statp.st_mtime) {
252 Jmsg(jcr, M_ERROR, 0, _("%s mtime changed during backup.\n"), ff_pkt->fname);
253 Dmsg3(50, "%s mtime (%lld) changed during backup (%lld).\n", ff_pkt->fname,
254 (int64_t)ff_pkt->statp.st_mtime, (int64_t)statp.st_mtime);
258 if (statp.st_ctime != ff_pkt->statp.st_ctime) {
259 Jmsg(jcr, M_ERROR, 0, _("%s ctime changed during backup.\n"), ff_pkt->fname);
260 Dmsg3(50, "%s ctime (%lld) changed during backup (%lld).\n", ff_pkt->fname,
261 (int64_t)ff_pkt->statp.st_ctime, (int64_t)statp.st_ctime);
265 if ((int64_t)statp.st_size != (int64_t)ff_pkt->statp.st_size) {
266 Jmsg(jcr, M_ERROR, 0, _("%s size of %lld changed during backup to %lld.n"),ff_pkt->fname,
267 (int64_t)ff_pkt->statp.st_size, (int64_t)statp.st_size);
268 Dmsg3(50, "%s size (%lld) changed during backup (%lld).\n", ff_pkt->fname,
269 (int64_t)ff_pkt->statp.st_size, (int64_t)statp.st_size);
277 * For incremental/diffential or accurate backups, we
278 * determine if the current file has changed.
280 bool check_changes(JCR *jcr, FF_PKT *ff_pkt)
282 /* in special mode (like accurate backup), the programmer can
283 * choose his comparison function.
285 if (ff_pkt->check_fct) {
286 return ff_pkt->check_fct(jcr, ff_pkt);
289 /* For normal backups (incr/diff), we use this default
292 if (ff_pkt->incremental &&
293 (ff_pkt->statp.st_mtime < ff_pkt->save_time &&
294 ((ff_pkt->flags & FO_MTIMEONLY) ||
295 ff_pkt->statp.st_ctime < ff_pkt->save_time)))
303 static bool have_ignoredir(FF_PKT *ff_pkt)
308 /* Ensure that pointers are defined */
309 if (!ff_pkt->fileset || !ff_pkt->fileset->incexe) {
312 ignoredir = ff_pkt->fileset->incexe->ignoredir;
315 if (!ff_pkt->ignoredir_fname) {
316 ff_pkt->ignoredir_fname = get_pool_memory(PM_FNAME);
318 Mmsg(ff_pkt->ignoredir_fname, "%s/%s", ff_pkt->fname, ignoredir);
319 if (stat(ff_pkt->ignoredir_fname, &sb) == 0) {
320 Dmsg2(100, "Directory '%s' ignored (found %s)\n",
321 ff_pkt->fname, ignoredir);
322 return true; /* Just ignore this directory */
329 * When the current file is a hardlink, the backup code can compute
330 * the checksum and store it into the link_t structure.
333 ff_pkt_set_link_digest(FF_PKT *ff_pkt,
334 int32_t digest_stream, const char *digest, uint32_t len)
336 if (ff_pkt->linked && !ff_pkt->linked->digest) { /* is a hardlink */
337 ff_pkt->linked->digest = (char *) bmalloc(len);
338 memcpy(ff_pkt->linked->digest, digest, len);
339 ff_pkt->linked->digest_len = len;
340 ff_pkt->linked->digest_stream = digest_stream;
345 * Find a single file.
346 * handle_file is the callback for handling the file.
348 * parent_device is the device we are currently on
349 * top_level is 1 when not recursing or 0 when
350 * descending into a directory.
353 find_one_file(JCR *jcr, FF_PKT *ff_pkt,
354 int handle_file(JCR *jcr, FF_PKT *ff, bool top_level),
355 char *fname, dev_t parent_device, bool top_level)
357 struct utimbuf restore_times;
361 ff_pkt->fname = ff_pkt->link = fname;
363 if (lstat(fname, &ff_pkt->statp) != 0) {
364 /* Cannot stat file */
365 ff_pkt->type = FT_NOSTAT;
366 ff_pkt->ff_errno = errno;
367 return handle_file(jcr, ff_pkt, top_level);
370 Dmsg1(300, "File ----: %s\n", fname);
372 /* Save current times of this directory in case we need to
373 * reset them because the user doesn't want them changed.
375 restore_times.actime = ff_pkt->statp.st_atime;
376 restore_times.modtime = ff_pkt->statp.st_mtime;
379 * We check for allowed fstypes and drivetypes at top_level and fstype change (below).
382 if (!accept_fstype(ff_pkt, NULL)) {
383 ff_pkt->type = FT_INVALIDFS;
384 if (ff_pkt->flags & FO_KEEPATIME) {
385 utime(fname, &restore_times);
390 if (!fstype(ff_pkt, fs, sizeof(fs))) {
391 bstrncpy(fs, "unknown", sizeof(fs));
394 Jmsg(jcr, M_INFO, 0, _("Top level directory \"%s\" has unlisted fstype \"%s\"\n"), fname, fs);
395 return 1; /* Just ignore this error - or the whole backup is cancelled */
397 if (!accept_drivetype(ff_pkt, NULL)) {
398 ff_pkt->type = FT_INVALIDDT;
399 if (ff_pkt->flags & FO_KEEPATIME) {
400 utime(fname, &restore_times);
405 if (!drivetype(ff_pkt->fname, dt, sizeof(dt))) {
406 bstrncpy(dt, "unknown", sizeof(dt));
409 Jmsg(jcr, M_INFO, 0, _("Top level directory \"%s\" has an unlisted drive type \"%s\"\n"), fname, dt);
410 return 1; /* Just ignore this error - or the whole backup is cancelled */
412 ff_pkt->volhas_attrlist = volume_has_attrlist(fname);
416 * Ignore this entry if no_dump() returns true
418 if (no_dump(jcr, ff_pkt)) {
419 Dmsg1(100, "'%s' ignored (NODUMP flag set)\n",
425 * If this is an Incremental backup, see if file was modified
426 * since our last "save_time", presumably the last Full save
429 if ( !S_ISDIR(ff_pkt->statp.st_mode)
430 && !check_changes(jcr, ff_pkt))
432 Dmsg1(500, "Non-directory incremental: %s\n", ff_pkt->fname);
433 ff_pkt->type = FT_NOCHG;
434 return handle_file(jcr, ff_pkt, top_level);
437 #ifdef HAVE_DARWIN_OS
438 if (ff_pkt->flags & FO_HFSPLUS && ff_pkt->volhas_attrlist
439 && S_ISREG(ff_pkt->statp.st_mode)) {
440 /* TODO: initialise attrList once elsewhere? */
441 struct attrlist attrList;
442 memset(&attrList, 0, sizeof(attrList));
443 attrList.bitmapcount = ATTR_BIT_MAP_COUNT;
444 attrList.commonattr = ATTR_CMN_FNDRINFO;
445 attrList.fileattr = ATTR_FILE_RSRCLENGTH;
446 if (getattrlist(fname, &attrList, &ff_pkt->hfsinfo,
447 sizeof(ff_pkt->hfsinfo), FSOPT_NOFOLLOW) != 0) {
448 ff_pkt->type = FT_NOSTAT;
449 ff_pkt->ff_errno = errno;
450 return handle_file(jcr, ff_pkt, top_level);
452 return -1; /* ignore */
458 * Handle hard linked files
460 * Maintain a list of hard linked files already backed up. This
461 * allows us to ensure that the data of each file gets backed
464 if (!(ff_pkt->flags & FO_NO_HARDLINK)
465 && ff_pkt->statp.st_nlink > 1
466 && (S_ISREG(ff_pkt->statp.st_mode)
467 || S_ISCHR(ff_pkt->statp.st_mode)
468 || S_ISBLK(ff_pkt->statp.st_mode)
469 || S_ISFIFO(ff_pkt->statp.st_mode)
470 || S_ISSOCK(ff_pkt->statp.st_mode))) {
473 if (ff_pkt->linkhash == NULL) {
474 ff_pkt->linkhash = (link_t **)bmalloc(LINK_HASHTABLE_SIZE * sizeof(link_t *));
475 memset(ff_pkt->linkhash, 0, LINK_HASHTABLE_SIZE * sizeof(link_t *));
477 const int linkhash = LINKHASH(ff_pkt->statp);
479 /* Search link list of hard linked files */
480 for (lp = ff_pkt->linkhash[linkhash]; lp; lp = lp->next)
481 if (lp->ino == (ino_t)ff_pkt->statp.st_ino &&
482 lp->dev == (dev_t)ff_pkt->statp.st_dev) {
483 /* If we have already backed up the hard linked file don't do it again */
484 if (strcmp(lp->name, fname) == 0) {
485 Dmsg2(400, "== Name identical skip FI=%d file=%s\n", lp->FileIndex, fname);
486 return 1; /* ignore */
488 ff_pkt->link = lp->name;
489 ff_pkt->type = FT_LNKSAVED; /* Handle link, file already saved */
490 ff_pkt->LinkFI = lp->FileIndex;
492 ff_pkt->digest = lp->digest;
493 ff_pkt->digest_stream = lp->digest_stream;
494 ff_pkt->digest_len = lp->digest_len;
495 rtn_stat = handle_file(jcr, ff_pkt, top_level);
496 Dmsg3(400, "FT_LNKSAVED FI=%d LinkFI=%d file=%s\n",
497 ff_pkt->FileIndex, lp->FileIndex, lp->name);
501 /* File not previously dumped. Chain it into our list. */
502 len = strlen(fname) + 1;
503 lp = (struct f_link *)bmalloc(sizeof(struct f_link) + len);
504 lp->digest = NULL; /* set later */
505 lp->digest_stream = 0; /* set later */
506 lp->digest_len = 0; /* set later */
507 lp->ino = ff_pkt->statp.st_ino;
508 lp->dev = ff_pkt->statp.st_dev;
509 lp->FileIndex = 0; /* set later */
510 bstrncpy(lp->name, fname, len);
511 lp->next = ff_pkt->linkhash[linkhash];
512 ff_pkt->linkhash[linkhash] = lp;
513 ff_pkt->linked = lp; /* mark saved link */
514 Dmsg2(400, "added to hash FI=%d file=%s\n", ff_pkt->FileIndex, lp->name);
516 ff_pkt->linked = NULL;
519 /* This is not a link to a previously dumped file, so dump it. */
520 if (S_ISREG(ff_pkt->statp.st_mode)) {
523 sizeleft = ff_pkt->statp.st_size;
525 /* Don't bother opening empty, world readable files. Also do not open
526 files when archive is meant for /dev/null. */
527 if (ff_pkt->null_output_device || (sizeleft == 0
528 && MODE_RALL == (MODE_RALL & ff_pkt->statp.st_mode))) {
529 ff_pkt->type = FT_REGE;
531 ff_pkt->type = FT_REG;
533 rtn_stat = handle_file(jcr, ff_pkt, top_level);
534 if (ff_pkt->linked) {
535 ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
537 Dmsg3(400, "FT_REG FI=%d linked=%d file=%s\n", ff_pkt->FileIndex,
538 ff_pkt->linked ? 1 : 0, fname);
539 if (ff_pkt->flags & FO_KEEPATIME) {
540 utime(fname, &restore_times);
545 } else if (S_ISLNK(ff_pkt->statp.st_mode)) { /* soft link */
547 char *buffer = (char *)alloca(path_max + name_max + 102);
549 size = readlink(fname, buffer, path_max + name_max + 101);
551 /* Could not follow link */
552 ff_pkt->type = FT_NOFOLLOW;
553 ff_pkt->ff_errno = errno;
554 rtn_stat = handle_file(jcr, ff_pkt, top_level);
555 if (ff_pkt->linked) {
556 ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
561 ff_pkt->link = buffer; /* point to link */
562 ff_pkt->type = FT_LNK; /* got a real link */
563 rtn_stat = handle_file(jcr, ff_pkt, top_level);
564 if (ff_pkt->linked) {
565 ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
569 } else if (S_ISDIR(ff_pkt->statp.st_mode)) {
571 struct dirent *entry, *result;
576 dev_t our_device = ff_pkt->statp.st_dev;
578 bool volhas_attrlist = ff_pkt->volhas_attrlist; /* Remember this if we recurse */
581 * Ignore this directory and everything below if the file .nobackup
582 * (or what is defined for IgnoreDir in this fileset) exists
584 if (have_ignoredir(ff_pkt)) {
585 return 1; /* Just ignore this directory */
588 /* Build a canonical directory name with a trailing slash in link var */
590 link_len = len + 200;
591 link = (char *)bmalloc(link_len + 2);
592 bstrncpy(link, fname, link_len);
593 /* Strip all trailing slashes */
594 while (len >= 1 && IsPathSeparator(link[len - 1]))
596 link[len++] = '/'; /* add back one */
600 if (!check_changes(jcr, ff_pkt)) {
601 /* Incremental/Full+Base option, directory entry not changed */
602 ff_pkt->type = FT_DIRNOCHG;
604 ff_pkt->type = FT_DIRBEGIN;
607 * We have set st_rdev to 1 if it is a reparse point, otherwise 0,
608 * if st_rdev is 2, it is a mount point
611 * Note, we return the directory to the calling program (handle_file)
612 * when we first see the directory (FT_DIRBEGIN.
613 * This allows the program to apply matches and make a
614 * choice whether or not to accept it. If it is accepted, we
615 * do not immediately save it, but do so only after everything
616 * in the directory is seen (i.e. the FT_DIREND).
618 rtn_stat = handle_file(jcr, ff_pkt, top_level);
619 if (rtn_stat < 1 || ff_pkt->type == FT_REPARSE ||
620 ff_pkt->type == FT_JUNCTION) { /* ignore or error status */
624 /* Done with DIRBEGIN, next call will be DIREND */
625 if (ff_pkt->type == FT_DIRBEGIN) {
626 ff_pkt->type = FT_DIREND;
630 * Create a temporary ff packet for this directory
631 * entry, and defer handling the directory until
632 * we have recursed into it. This saves the
633 * directory after all files have been processed, and
634 * during the restore, the directory permissions will
635 * be reset after all the files have been restored.
637 Dmsg1(300, "Create temp ff packet for dir: %s\n", ff_pkt->fname);
638 FF_PKT *dir_ff_pkt = new_dir_ff_pkt(ff_pkt);
641 * Do not descend into subdirectories (recurse) if the
642 * user has turned it off for this directory.
644 * If we are crossing file systems, we are either not allowed
645 * to cross, or we may be restricted by a list of permitted
648 bool is_win32_mount_point = false;
649 if (!top_level && ff_pkt->flags & FO_NO_RECURSION) {
650 ff_pkt->type = FT_NORECURSE;
652 } else if (!top_level && (parent_device != ff_pkt->statp.st_dev ||
653 is_win32_mount_point)) {
654 if(!(ff_pkt->flags & FO_MULTIFS)) {
655 ff_pkt->type = FT_NOFSCHG;
657 } else if (!accept_fstype(ff_pkt, NULL)) {
658 ff_pkt->type = FT_INVALIDFS;
661 ff_pkt->volhas_attrlist = volume_has_attrlist(fname);
664 /* If not recursing, just backup dir and return */
666 rtn_stat = handle_file(jcr, ff_pkt, top_level);
667 if (ff_pkt->linked) {
668 ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
671 free_dir_ff_pkt(dir_ff_pkt);
672 ff_pkt->link = ff_pkt->fname; /* reset "link" */
673 if (ff_pkt->flags & FO_KEEPATIME) {
674 utime(fname, &restore_times);
679 ff_pkt->link = ff_pkt->fname; /* reset "link" */
682 * Descend into or "recurse" into the directory to read
683 * all the files in it.
686 if ((directory = opendir(fname)) == NULL) {
687 ff_pkt->type = FT_NOOPEN;
688 ff_pkt->ff_errno = errno;
689 rtn_stat = handle_file(jcr, ff_pkt, top_level);
690 if (ff_pkt->linked) {
691 ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
694 free_dir_ff_pkt(dir_ff_pkt);
699 * Process all files in this directory entry (recursing).
700 * This would possibly run faster if we chdir to the directory
701 * before traversing it.
704 entry = (struct dirent *)malloc(sizeof(struct dirent) + name_max + 100);
705 for ( ; !job_canceled(jcr); ) {
709 status = readdir_r(directory, entry, &result);
710 if (status != 0 || result == NULL) {
711 // Dmsg2(99, "readdir returned stat=%d result=0x%x\n",
712 // status, (long)result);
715 ASSERT(name_max+1 > (int)sizeof(struct dirent) + (int)NAMELEN(entry));
717 /* Skip `.', `..', and excluded file names. */
718 if (p[0] == '\0' || (p[0] == '.' && (p[1] == '\0' ||
719 (p[1] == '.' && p[2] == '\0')))) {
723 if ((int)NAMELEN(entry) + len >= link_len) {
724 link_len = len + NAMELEN(entry) + 1;
725 link = (char *)brealloc(link, link_len + 1);
728 for (i=0; i < (int)NAMELEN(entry); i++) {
732 if (!file_is_excluded(ff_pkt, link)) {
733 rtn_stat = find_one_file(jcr, ff_pkt, handle_file, link, our_device, false);
734 if (ff_pkt->linked) {
735 ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
744 * Now that we have recursed through all the files in the
745 * directory, we "save" the directory so that after all
746 * the files are restored, this entry will serve to reset
747 * the directory modes and dates. Temp directory values
748 * were used without this record.
750 handle_file(jcr, dir_ff_pkt, top_level); /* handle directory entry */
751 if (ff_pkt->linked) {
752 ff_pkt->linked->FileIndex = dir_ff_pkt->FileIndex;
754 free_dir_ff_pkt(dir_ff_pkt);
756 if (ff_pkt->flags & FO_KEEPATIME) {
757 utime(fname, &restore_times);
759 ff_pkt->volhas_attrlist = volhas_attrlist; /* Restore value in case it changed. */
761 } /* end check for directory */
764 * If it is explicitly mentioned (i.e. top_level) and is
765 * a block device, we do a raw backup of it or if it is
766 * a fifo, we simply read it.
768 #if defined(HAVE_FREEBSD_OS) || defined(__FreeBSD_kernel__)
770 * On FreeBSD, all block devices are character devices, so
771 * to be able to read a raw disk, we need the check for
772 * a character device.
773 * crw-r----- 1 root operator - 116, 0x00040002 Jun 9 19:32 /dev/ad0s3
774 * crw-r----- 1 root operator - 116, 0x00040002 Jun 9 19:32 /dev/rad0s3
776 if (top_level && (S_ISBLK(ff_pkt->statp.st_mode) || S_ISCHR(ff_pkt->statp.st_mode))) {
778 if (top_level && S_ISBLK(ff_pkt->statp.st_mode)) {
780 ff_pkt->type = FT_RAW; /* raw partition */
781 } else if (top_level && S_ISFIFO(ff_pkt->statp.st_mode) &&
782 ff_pkt->flags & FO_READFIFO) {
783 ff_pkt->type = FT_FIFO;
785 /* The only remaining types are special (character, ...) files */
786 ff_pkt->type = FT_SPEC;
788 rtn_stat = handle_file(jcr, ff_pkt, top_level);
789 if (ff_pkt->linked) {
790 ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
795 int term_find_one(FF_PKT *ff)
797 struct f_link *lp, *lc;
802 if (ff->linkhash == NULL) return 0;
804 for (i =0 ; i < LINK_HASHTABLE_SIZE; i ++) {
805 /* Free up list of hard linked files */
806 lp = ff->linkhash[i];
818 ff->linkhash[i] = NULL;