2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2014 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from many
7 others, a complete list can be found in the file AUTHORS.
9 You may use this file and others of this release according to the
10 license defined in the LICENSE file, which includes the Affero General
11 Public License, v3.0 ("AGPLv3") and some additional permissions and
12 terms pursuant to its AGPLv3 Section 7.
14 Bacula® is a registered trademark of Kern Sibbald.
18 This file was derived from GNU TAR source code. Except for a few key
19 ideas, it has been entirely rewritten for Bacula.
23 Thanks to the TAR programmers.
30 #include <sys/param.h>
31 #include <sys/mount.h>
35 extern int32_t name_max; /* filename max length */
36 extern int32_t path_max; /* path name max length */
39 * Structure for keeping track of hard linked files, we
40 * keep an entry for each hardlinked file that we save,
41 * which is the first one found. For all the other files that
42 * are linked to this one, we save only the directory
43 * entry so we can link it.
47 dev_t dev; /* device */
48 ino_t ino; /* inode with device is unique */
49 uint32_t FileIndex; /* Bacula FileIndex of this file */
50 int32_t digest_stream; /* Digest type if needed */
51 uint32_t digest_len; /* Digest len if needed */
52 char *digest; /* Checksum of the file if needed */
53 char name[1]; /* The name */
56 typedef struct f_link link_t;
57 #define LINK_HASHTABLE_BITS 16
58 #define LINK_HASHTABLE_SIZE (1<<LINK_HASHTABLE_BITS)
59 #define LINK_HASHTABLE_MASK (LINK_HASHTABLE_SIZE-1)
61 static inline int LINKHASH(const struct stat &info)
63 int hash = info.st_dev;
64 unsigned long long i = info.st_ino;
72 return hash & LINK_HASHTABLE_MASK;
76 * Create a new directory Find File packet, but copy
77 * some of the essential info from the current packet.
78 * However, be careful to zero out the rest of the
81 static FF_PKT *new_dir_ff_pkt(FF_PKT *ff_pkt)
83 FF_PKT *dir_ff_pkt = (FF_PKT *)bmalloc(sizeof(FF_PKT));
84 memcpy(dir_ff_pkt, ff_pkt, sizeof(FF_PKT));
85 dir_ff_pkt->fname = bstrdup(ff_pkt->fname);
86 dir_ff_pkt->link = bstrdup(ff_pkt->link);
87 dir_ff_pkt->sys_fname = get_pool_memory(PM_FNAME);
88 dir_ff_pkt->included_files_list = NULL;
89 dir_ff_pkt->excluded_files_list = NULL;
90 dir_ff_pkt->excluded_paths_list = NULL;
91 dir_ff_pkt->linkhash = NULL;
92 dir_ff_pkt->fname_save = NULL;
93 dir_ff_pkt->link_save = NULL;
94 dir_ff_pkt->ignoredir_fname = NULL;
99 * Free the temp directory ff_pkt
101 static void free_dir_ff_pkt(FF_PKT *dir_ff_pkt)
103 free(dir_ff_pkt->fname);
104 free(dir_ff_pkt->link);
105 free_pool_memory(dir_ff_pkt->sys_fname);
106 if (dir_ff_pkt->fname_save) {
107 free_pool_memory(dir_ff_pkt->fname_save);
109 if (dir_ff_pkt->link_save) {
110 free_pool_memory(dir_ff_pkt->link_save);
112 if (dir_ff_pkt->ignoredir_fname) {
113 free_pool_memory(dir_ff_pkt->ignoredir_fname);
119 * Check to see if we allow the file system type of a file or directory.
120 * If we do not have a list of file system types, we accept anything.
122 static int accept_fstype(FF_PKT *ff, void *dummy) {
127 if (ff->fstypes.size()) {
129 if (!fstype(ff->fname, fs, sizeof(fs))) {
130 Dmsg1(50, "Cannot determine file system type for \"%s\"\n", ff->fname);
132 for (i = 0; i < ff->fstypes.size(); ++i) {
133 if (strcmp(fs, (char *)ff->fstypes.get(i)) == 0) {
134 Dmsg2(100, "Accepting fstype %s for \"%s\"\n", fs, ff->fname);
138 Dmsg3(200, "fstype %s for \"%s\" does not match %s\n", fs,
139 ff->fname, ff->fstypes.get(i));
147 * Check to see if we allow the drive type of a file or directory.
148 * If we do not have a list of drive types, we accept anything.
150 static int accept_drivetype(FF_PKT *ff, void *dummy) {
155 if (ff->drivetypes.size()) {
157 if (!drivetype(ff->fname, dt, sizeof(dt))) {
158 Dmsg1(50, "Cannot determine drive type for \"%s\"\n", ff->fname);
160 for (i = 0; i < ff->drivetypes.size(); ++i) {
161 if (strcmp(dt, (char *)ff->drivetypes.get(i)) == 0) {
162 Dmsg2(100, "Accepting drive type %s for \"%s\"\n", dt, ff->fname);
166 Dmsg3(200, "drive type %s for \"%s\" does not match %s\n", dt,
167 ff->fname, ff->drivetypes.get(i));
175 * This function determines whether we can use getattrlist()
176 * It's odd, but we have to use the function to determine that...
177 * Also, the man pages talk about things as if they were implemented.
179 * On Mac OS X, this succesfully differentiates between HFS+ and UFS
180 * volumes, which makes me trust it is OK for others, too.
182 static bool volume_has_attrlist(const char *fname)
184 #ifdef HAVE_DARWIN_OS
186 struct volinfo_struct {
187 unsigned long length; /* Mandatory field */
188 vol_capabilities_attr_t info; /* Volume capabilities */
190 struct attrlist attrList;
192 memset(&attrList, 0, sizeof(attrList));
193 attrList.bitmapcount = ATTR_BIT_MAP_COUNT;
194 attrList.volattr = ATTR_VOL_INFO | ATTR_VOL_CAPABILITIES;
195 if (statfs(fname, &st) == 0) {
196 /* We need to check on the mount point */
197 if (getattrlist(st.f_mntonname, &attrList, &vol, sizeof(vol), FSOPT_NOFOLLOW) == 0
198 && (vol.info.capabilities[VOL_CAPABILITIES_INTERFACES] & VOL_CAP_INT_ATTRLIST)
199 && (vol.info.valid[VOL_CAPABILITIES_INTERFACES] & VOL_CAP_INT_ATTRLIST)) {
208 * check for BSD nodump flag
210 static bool no_dump(JCR *jcr, FF_PKT *ff_pkt)
212 #if defined(HAVE_CHFLAGS) && defined(UF_NODUMP)
213 if ( (ff_pkt->flags & FO_HONOR_NODUMP) &&
214 (ff_pkt->statp.st_flags & UF_NODUMP) ) {
215 Jmsg(jcr, M_INFO, 1, _(" NODUMP flag set - will not process %s\n"),
217 return true; /* do not backup this file */
220 return false; /* do backup */
223 /* check if a file have changed during backup and display an error */
224 bool has_file_changed(JCR *jcr, FF_PKT *ff_pkt)
227 Dmsg1(500, "has_file_changed fname=%s\n",ff_pkt->fname);
229 if (ff_pkt->type != FT_REG) { /* not a regular file */
233 if (lstat(ff_pkt->fname, &statp) != 0) {
235 Jmsg(jcr, M_WARNING, 0,
236 _("Cannot stat file %s: ERR=%s\n"),ff_pkt->fname,be.bstrerror());
240 if (statp.st_mtime != ff_pkt->statp.st_mtime) {
241 Jmsg(jcr, M_ERROR, 0, _("%s mtime changed during backup.\n"), ff_pkt->fname);
242 Dmsg3(50, "%s mtime (%lld) changed during backup (%lld).\n", ff_pkt->fname,
243 (int64_t)ff_pkt->statp.st_mtime, (int64_t)statp.st_mtime);
247 if (statp.st_ctime != ff_pkt->statp.st_ctime) {
248 Jmsg(jcr, M_ERROR, 0, _("%s ctime changed during backup.\n"), ff_pkt->fname);
249 Dmsg3(50, "%s ctime (%lld) changed during backup (%lld).\n", ff_pkt->fname,
250 (int64_t)ff_pkt->statp.st_ctime, (int64_t)statp.st_ctime);
254 if ((int64_t)statp.st_size != (int64_t)ff_pkt->statp.st_size) {
255 Jmsg(jcr, M_ERROR, 0, _("%s size of %lld changed during backup to %lld.n"),ff_pkt->fname,
256 (int64_t)ff_pkt->statp.st_size, (int64_t)statp.st_size);
257 Dmsg3(50, "%s size (%lld) changed during backup (%lld).\n", ff_pkt->fname,
258 (int64_t)ff_pkt->statp.st_size, (int64_t)statp.st_size);
266 * For incremental/diffential or accurate backups, we
267 * determine if the current file has changed.
269 bool check_changes(JCR *jcr, FF_PKT *ff_pkt)
271 /* in special mode (like accurate backup), the programmer can
272 * choose his comparison function.
274 if (ff_pkt->check_fct) {
275 return ff_pkt->check_fct(jcr, ff_pkt);
278 /* For normal backups (incr/diff), we use this default
281 if (ff_pkt->incremental &&
282 (ff_pkt->statp.st_mtime < ff_pkt->save_time &&
283 ((ff_pkt->flags & FO_MTIMEONLY) ||
284 ff_pkt->statp.st_ctime < ff_pkt->save_time)))
292 static bool have_ignoredir(FF_PKT *ff_pkt)
297 /* Ensure that pointers are defined */
298 if (!ff_pkt->fileset || !ff_pkt->fileset->incexe) {
301 ignoredir = ff_pkt->fileset->incexe->ignoredir;
304 if (!ff_pkt->ignoredir_fname) {
305 ff_pkt->ignoredir_fname = get_pool_memory(PM_FNAME);
307 Mmsg(ff_pkt->ignoredir_fname, "%s/%s", ff_pkt->fname, ignoredir);
308 if (stat(ff_pkt->ignoredir_fname, &sb) == 0) {
309 Dmsg2(100, "Directory '%s' ignored (found %s)\n",
310 ff_pkt->fname, ignoredir);
311 return true; /* Just ignore this directory */
318 * When the current file is a hardlink, the backup code can compute
319 * the checksum and store it into the link_t structure.
322 ff_pkt_set_link_digest(FF_PKT *ff_pkt,
323 int32_t digest_stream, const char *digest, uint32_t len)
325 if (ff_pkt->linked && !ff_pkt->linked->digest) { /* is a hardlink */
326 ff_pkt->linked->digest = (char *) bmalloc(len);
327 memcpy(ff_pkt->linked->digest, digest, len);
328 ff_pkt->linked->digest_len = len;
329 ff_pkt->linked->digest_stream = digest_stream;
334 * Find a single file.
335 * handle_file is the callback for handling the file.
337 * parent_device is the device we are currently on
338 * top_level is 1 when not recursing or 0 when
339 * descending into a directory.
342 find_one_file(JCR *jcr, FF_PKT *ff_pkt,
343 int handle_file(JCR *jcr, FF_PKT *ff, bool top_level),
344 char *fname, dev_t parent_device, bool top_level)
346 struct utimbuf restore_times;
350 ff_pkt->fname = ff_pkt->link = fname;
352 if (lstat(fname, &ff_pkt->statp) != 0) {
353 /* Cannot stat file */
354 ff_pkt->type = FT_NOSTAT;
355 ff_pkt->ff_errno = errno;
356 return handle_file(jcr, ff_pkt, top_level);
359 Dmsg1(300, "File ----: %s\n", fname);
361 /* Save current times of this directory in case we need to
362 * reset them because the user doesn't want them changed.
364 restore_times.actime = ff_pkt->statp.st_atime;
365 restore_times.modtime = ff_pkt->statp.st_mtime;
368 * We check for allowed fstypes and drivetypes at top_level and fstype change (below).
371 if (!accept_fstype(ff_pkt, NULL)) {
372 ff_pkt->type = FT_INVALIDFS;
373 if (ff_pkt->flags & FO_KEEPATIME) {
374 utime(fname, &restore_times);
379 if (!fstype(ff_pkt->fname, fs, sizeof(fs))) {
380 bstrncpy(fs, "unknown", sizeof(fs));
383 Jmsg(jcr, M_INFO, 0, _("Top level directory \"%s\" has unlisted fstype \"%s\"\n"), fname, fs);
384 return 1; /* Just ignore this error - or the whole backup is cancelled */
386 if (!accept_drivetype(ff_pkt, NULL)) {
387 ff_pkt->type = FT_INVALIDDT;
388 if (ff_pkt->flags & FO_KEEPATIME) {
389 utime(fname, &restore_times);
394 if (!drivetype(ff_pkt->fname, dt, sizeof(dt))) {
395 bstrncpy(dt, "unknown", sizeof(dt));
398 Jmsg(jcr, M_INFO, 0, _("Top level directory \"%s\" has an unlisted drive type \"%s\"\n"), fname, dt);
399 return 1; /* Just ignore this error - or the whole backup is cancelled */
401 ff_pkt->volhas_attrlist = volume_has_attrlist(fname);
405 * Ignore this entry if no_dump() returns true
407 if (no_dump(jcr, ff_pkt)) {
408 Dmsg1(100, "'%s' ignored (NODUMP flag set)\n",
414 * If this is an Incremental backup, see if file was modified
415 * since our last "save_time", presumably the last Full save
418 if ( !S_ISDIR(ff_pkt->statp.st_mode)
419 && !check_changes(jcr, ff_pkt))
421 Dmsg1(500, "Non-directory incremental: %s\n", ff_pkt->fname);
422 ff_pkt->type = FT_NOCHG;
423 return handle_file(jcr, ff_pkt, top_level);
426 #ifdef HAVE_DARWIN_OS
427 if (ff_pkt->flags & FO_HFSPLUS && ff_pkt->volhas_attrlist
428 && S_ISREG(ff_pkt->statp.st_mode)) {
429 /* TODO: initialise attrList once elsewhere? */
430 struct attrlist attrList;
431 memset(&attrList, 0, sizeof(attrList));
432 attrList.bitmapcount = ATTR_BIT_MAP_COUNT;
433 attrList.commonattr = ATTR_CMN_FNDRINFO;
434 attrList.fileattr = ATTR_FILE_RSRCLENGTH;
435 if (getattrlist(fname, &attrList, &ff_pkt->hfsinfo,
436 sizeof(ff_pkt->hfsinfo), FSOPT_NOFOLLOW) != 0) {
437 ff_pkt->type = FT_NOSTAT;
438 ff_pkt->ff_errno = errno;
439 return handle_file(jcr, ff_pkt, top_level);
441 return -1; /* ignore */
447 * Handle hard linked files
449 * Maintain a list of hard linked files already backed up. This
450 * allows us to ensure that the data of each file gets backed
453 if (!(ff_pkt->flags & FO_NO_HARDLINK)
454 && ff_pkt->statp.st_nlink > 1
455 && (S_ISREG(ff_pkt->statp.st_mode)
456 || S_ISCHR(ff_pkt->statp.st_mode)
457 || S_ISBLK(ff_pkt->statp.st_mode)
458 || S_ISFIFO(ff_pkt->statp.st_mode)
459 || S_ISSOCK(ff_pkt->statp.st_mode))) {
462 if (ff_pkt->linkhash == NULL) {
463 ff_pkt->linkhash = (link_t **)bmalloc(LINK_HASHTABLE_SIZE * sizeof(link_t *));
464 memset(ff_pkt->linkhash, 0, LINK_HASHTABLE_SIZE * sizeof(link_t *));
466 const int linkhash = LINKHASH(ff_pkt->statp);
468 /* Search link list of hard linked files */
469 for (lp = ff_pkt->linkhash[linkhash]; lp; lp = lp->next)
470 if (lp->ino == (ino_t)ff_pkt->statp.st_ino &&
471 lp->dev == (dev_t)ff_pkt->statp.st_dev) {
472 /* If we have already backed up the hard linked file don't do it again */
473 if (strcmp(lp->name, fname) == 0) {
474 Dmsg2(400, "== Name identical skip FI=%d file=%s\n", lp->FileIndex, fname);
475 return 1; /* ignore */
477 ff_pkt->link = lp->name;
478 ff_pkt->type = FT_LNKSAVED; /* Handle link, file already saved */
479 ff_pkt->LinkFI = lp->FileIndex;
481 ff_pkt->digest = lp->digest;
482 ff_pkt->digest_stream = lp->digest_stream;
483 ff_pkt->digest_len = lp->digest_len;
484 rtn_stat = handle_file(jcr, ff_pkt, top_level);
485 Dmsg3(400, "FT_LNKSAVED FI=%d LinkFI=%d file=%s\n",
486 ff_pkt->FileIndex, lp->FileIndex, lp->name);
490 /* File not previously dumped. Chain it into our list. */
491 len = strlen(fname) + 1;
492 lp = (struct f_link *)bmalloc(sizeof(struct f_link) + len);
493 lp->digest = NULL; /* set later */
494 lp->digest_stream = 0; /* set later */
495 lp->digest_len = 0; /* set later */
496 lp->ino = ff_pkt->statp.st_ino;
497 lp->dev = ff_pkt->statp.st_dev;
498 lp->FileIndex = 0; /* set later */
499 bstrncpy(lp->name, fname, len);
500 lp->next = ff_pkt->linkhash[linkhash];
501 ff_pkt->linkhash[linkhash] = lp;
502 ff_pkt->linked = lp; /* mark saved link */
503 Dmsg2(400, "added to hash FI=%d file=%s\n", ff_pkt->FileIndex, lp->name);
505 ff_pkt->linked = NULL;
508 /* This is not a link to a previously dumped file, so dump it. */
509 if (S_ISREG(ff_pkt->statp.st_mode)) {
512 sizeleft = ff_pkt->statp.st_size;
514 /* Don't bother opening empty, world readable files. Also do not open
515 files when archive is meant for /dev/null. */
516 if (ff_pkt->null_output_device || (sizeleft == 0
517 && MODE_RALL == (MODE_RALL & ff_pkt->statp.st_mode))) {
518 ff_pkt->type = FT_REGE;
520 ff_pkt->type = FT_REG;
522 rtn_stat = handle_file(jcr, ff_pkt, top_level);
523 if (ff_pkt->linked) {
524 ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
526 Dmsg3(400, "FT_REG FI=%d linked=%d file=%s\n", ff_pkt->FileIndex,
527 ff_pkt->linked ? 1 : 0, fname);
528 if (ff_pkt->flags & FO_KEEPATIME) {
529 utime(fname, &restore_times);
534 } else if (S_ISLNK(ff_pkt->statp.st_mode)) { /* soft link */
536 char *buffer = (char *)alloca(path_max + name_max + 102);
538 size = readlink(fname, buffer, path_max + name_max + 101);
540 /* Could not follow link */
541 ff_pkt->type = FT_NOFOLLOW;
542 ff_pkt->ff_errno = errno;
543 rtn_stat = handle_file(jcr, ff_pkt, top_level);
544 if (ff_pkt->linked) {
545 ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
550 ff_pkt->link = buffer; /* point to link */
551 ff_pkt->type = FT_LNK; /* got a real link */
552 rtn_stat = handle_file(jcr, ff_pkt, top_level);
553 if (ff_pkt->linked) {
554 ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
558 } else if (S_ISDIR(ff_pkt->statp.st_mode)) {
560 struct dirent *entry, *result;
565 dev_t our_device = ff_pkt->statp.st_dev;
567 bool volhas_attrlist = ff_pkt->volhas_attrlist; /* Remember this if we recurse */
570 * Ignore this directory and everything below if the file .nobackup
571 * (or what is defined for IgnoreDir in this fileset) exists
573 if (have_ignoredir(ff_pkt)) {
574 return 1; /* Just ignore this directory */
577 /* Build a canonical directory name with a trailing slash in link var */
579 link_len = len + 200;
580 link = (char *)bmalloc(link_len + 2);
581 bstrncpy(link, fname, link_len);
582 /* Strip all trailing slashes */
583 while (len >= 1 && IsPathSeparator(link[len - 1]))
585 link[len++] = '/'; /* add back one */
589 if (!check_changes(jcr, ff_pkt)) {
590 /* Incremental/Full+Base option, directory entry not changed */
591 ff_pkt->type = FT_DIRNOCHG;
593 ff_pkt->type = FT_DIRBEGIN;
596 * We have set st_rdev to 1 if it is a reparse point, otherwise 0,
597 * if st_rdev is 2, it is a mount point
600 * Note, we return the directory to the calling program (handle_file)
601 * when we first see the directory (FT_DIRBEGIN.
602 * This allows the program to apply matches and make a
603 * choice whether or not to accept it. If it is accepted, we
604 * do not immediately save it, but do so only after everything
605 * in the directory is seen (i.e. the FT_DIREND).
607 rtn_stat = handle_file(jcr, ff_pkt, top_level);
608 if (rtn_stat < 1 || ff_pkt->type == FT_REPARSE ||
609 ff_pkt->type == FT_JUNCTION) { /* ignore or error status */
613 /* Done with DIRBEGIN, next call will be DIREND */
614 if (ff_pkt->type == FT_DIRBEGIN) {
615 ff_pkt->type = FT_DIREND;
619 * Create a temporary ff packet for this directory
620 * entry, and defer handling the directory until
621 * we have recursed into it. This saves the
622 * directory after all files have been processed, and
623 * during the restore, the directory permissions will
624 * be reset after all the files have been restored.
626 Dmsg1(300, "Create temp ff packet for dir: %s\n", ff_pkt->fname);
627 FF_PKT *dir_ff_pkt = new_dir_ff_pkt(ff_pkt);
630 * Do not descend into subdirectories (recurse) if the
631 * user has turned it off for this directory.
633 * If we are crossing file systems, we are either not allowed
634 * to cross, or we may be restricted by a list of permitted
637 if (!top_level && ff_pkt->flags & FO_NO_RECURSION) {
638 ff_pkt->type = FT_NORECURSE;
640 } else if (!top_level && (parent_device != ff_pkt->statp.st_dev)) {
641 if(!(ff_pkt->flags & FO_MULTIFS)) {
642 ff_pkt->type = FT_NOFSCHG;
644 } else if (!accept_fstype(ff_pkt, NULL)) {
645 ff_pkt->type = FT_INVALIDFS;
648 ff_pkt->volhas_attrlist = volume_has_attrlist(fname);
651 /* If not recursing, just backup dir and return */
653 rtn_stat = handle_file(jcr, ff_pkt, top_level);
654 if (ff_pkt->linked) {
655 ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
658 free_dir_ff_pkt(dir_ff_pkt);
659 ff_pkt->link = ff_pkt->fname; /* reset "link" */
660 if (ff_pkt->flags & FO_KEEPATIME) {
661 utime(fname, &restore_times);
666 ff_pkt->link = ff_pkt->fname; /* reset "link" */
669 * Descend into or "recurse" into the directory to read
670 * all the files in it.
673 if ((directory = opendir(fname)) == NULL) {
674 ff_pkt->type = FT_NOOPEN;
675 ff_pkt->ff_errno = errno;
676 rtn_stat = handle_file(jcr, ff_pkt, top_level);
677 if (ff_pkt->linked) {
678 ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
681 free_dir_ff_pkt(dir_ff_pkt);
686 * Process all files in this directory entry (recursing).
687 * This would possibly run faster if we chdir to the directory
688 * before traversing it.
691 entry = (struct dirent *)malloc(sizeof(struct dirent) + name_max + 100);
692 for ( ; !job_canceled(jcr); ) {
696 status = readdir_r(directory, entry, &result);
697 if (status != 0 || result == NULL) {
698 // Dmsg2(99, "readdir returned stat=%d result=0x%x\n",
699 // status, (long)result);
702 ASSERT(name_max+1 > (int)sizeof(struct dirent) + (int)NAMELEN(entry));
704 /* Skip `.', `..', and excluded file names. */
705 if (p[0] == '\0' || (p[0] == '.' && (p[1] == '\0' ||
706 (p[1] == '.' && p[2] == '\0')))) {
710 if ((int)NAMELEN(entry) + len >= link_len) {
711 link_len = len + NAMELEN(entry) + 1;
712 link = (char *)brealloc(link, link_len + 1);
715 for (i=0; i < (int)NAMELEN(entry); i++) {
719 if (!file_is_excluded(ff_pkt, link)) {
720 rtn_stat = find_one_file(jcr, ff_pkt, handle_file, link, our_device, false);
721 if (ff_pkt->linked) {
722 ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
731 * Now that we have recursed through all the files in the
732 * directory, we "save" the directory so that after all
733 * the files are restored, this entry will serve to reset
734 * the directory modes and dates. Temp directory values
735 * were used without this record.
737 handle_file(jcr, dir_ff_pkt, top_level); /* handle directory entry */
738 if (ff_pkt->linked) {
739 ff_pkt->linked->FileIndex = dir_ff_pkt->FileIndex;
741 free_dir_ff_pkt(dir_ff_pkt);
743 if (ff_pkt->flags & FO_KEEPATIME) {
744 utime(fname, &restore_times);
746 ff_pkt->volhas_attrlist = volhas_attrlist; /* Restore value in case it changed. */
748 } /* end check for directory */
751 * If it is explicitly mentioned (i.e. top_level) and is
752 * a block device, we do a raw backup of it or if it is
753 * a fifo, we simply read it.
755 #ifdef HAVE_FREEBSD_OS
757 * On FreeBSD, all block devices are character devices, so
758 * to be able to read a raw disk, we need the check for
759 * a character device.
760 * crw-r----- 1 root operator - 116, 0x00040002 Jun 9 19:32 /dev/ad0s3
761 * crw-r----- 1 root operator - 116, 0x00040002 Jun 9 19:32 /dev/rad0s3
763 if (top_level && (S_ISBLK(ff_pkt->statp.st_mode) || S_ISCHR(ff_pkt->statp.st_mode))) {
765 if (top_level && S_ISBLK(ff_pkt->statp.st_mode)) {
767 ff_pkt->type = FT_RAW; /* raw partition */
768 } else if (top_level && S_ISFIFO(ff_pkt->statp.st_mode) &&
769 ff_pkt->flags & FO_READFIFO) {
770 ff_pkt->type = FT_FIFO;
772 /* The only remaining types are special (character, ...) files */
773 ff_pkt->type = FT_SPEC;
775 rtn_stat = handle_file(jcr, ff_pkt, top_level);
776 if (ff_pkt->linked) {
777 ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
782 int term_find_one(FF_PKT *ff)
784 struct f_link *lp, *lc;
789 if (ff->linkhash == NULL) return 0;
791 for (i =0 ; i < LINK_HASHTABLE_SIZE; i ++) {
792 /* Free up list of hard linked files */
793 lp = ff->linkhash[i];
805 ff->linkhash[i] = NULL;