2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2009 Free Software Foundation Europe e.V.
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
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.
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
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.
30 This file was derived from GNU TAR source code. Except for a few key
31 ideas, it has been entirely rewritten for Bacula.
35 Thanks to the TAR programmers.
44 #include <sys/param.h>
45 #include <sys/mount.h>
49 extern int32_t name_max; /* filename max length */
50 extern int32_t path_max; /* path name max length */
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.
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 */
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)
72 static inline int LINKHASH(const struct stat &info)
74 int hash = info.st_dev;
75 unsigned long long i = info.st_ino;
83 return hash & LINK_HASHTABLE_MASK;
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
92 static FF_PKT *new_dir_ff_pkt(FF_PKT *ff_pkt)
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;
109 * Free the temp directory ff_pkt
111 static void free_dir_ff_pkt(FF_PKT *dir_ff_pkt)
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);
119 if (dir_ff_pkt->link_save) {
120 free_pool_memory(dir_ff_pkt->link_save);
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.
129 static int accept_fstype(FF_PKT *ff, void *dummy) {
134 if (ff->fstypes.size()) {
136 if (!fstype(ff->fname, fs, sizeof(fs))) {
137 Dmsg1(50, "Cannot determine file system type for \"%s\"\n", ff->fname);
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);
145 Dmsg3(200, "fstype %s for \"%s\" does not match %s\n", fs,
146 ff->fname, ff->fstypes.get(i));
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.
157 static int accept_drivetype(FF_PKT *ff, void *dummy) {
162 if (ff->drivetypes.size()) {
164 if (!drivetype(ff->fname, dt, sizeof(dt))) {
165 Dmsg1(50, "Cannot determine drive type for \"%s\"\n", ff->fname);
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);
173 Dmsg3(200, "drive type %s for \"%s\" does not match %s\n", dt,
174 ff->fname, ff->drivetypes.get(i));
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.
186 * On Mac OS X, this succesfully differentiates between HFS+ and UFS
187 * volumes, which makes me trust it is OK for others, too.
189 static bool volume_has_attrlist(const char *fname)
191 #ifdef HAVE_DARWIN_OS
193 struct volinfo_struct {
194 unsigned long length; /* Mandatory field */
195 vol_capabilities_attr_t info; /* Volume capabilities */
197 struct attrlist attrList;
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)) {
215 * check for BSD nodump flag
217 static bool no_dump(JCR *jcr, FF_PKT *ff_pkt)
219 #if defined(HAVE_CHFLAGS) && defined(UF_NODUMP)
220 if ( (ff_pkt->flags & FO_HONOR_NODUMP) &&
221 (ff_pkt->statp.st_flags & UF_NODUMP) ) {
222 Jmsg(jcr, M_INFO, 1, _(" NODUMP flag set - will not process %s\n"),
224 return true; /* do not backup this file */
227 return false; /* do backup */
230 /* check if a file have changed during backup and display an error */
231 bool has_file_changed(JCR *jcr, FF_PKT *ff_pkt)
234 Dmsg1(500, "has_file_changed fname=%s\n",ff_pkt->fname);
236 if (ff_pkt->type != FT_REG) { /* not a regular file */
240 if (lstat(ff_pkt->fname, &statp) != 0) {
242 Jmsg(jcr, M_WARNING, 0,
243 _("Cannot stat file %s: ERR=%s\n"),ff_pkt->fname,be.bstrerror());
247 if (statp.st_mtime != ff_pkt->statp.st_mtime) {
248 /* TODO: add time of changes */
249 Jmsg(jcr, M_ERROR, 0, _("%s mtime changed during backup.\n"), ff_pkt->fname);
253 if (statp.st_ctime != ff_pkt->statp.st_ctime) {
254 /* TODO: add time of changes */
255 Jmsg(jcr, M_ERROR, 0, _("%s ctime changed during backup.\n"), ff_pkt->fname);
259 if (statp.st_size != ff_pkt->statp.st_size) {
260 /* TODO: add size change */
261 Jmsg(jcr, M_ERROR, 0, _("%s size changed during backup.\n"),ff_pkt->fname);
265 if ((statp.st_blksize != ff_pkt->statp.st_blksize) ||
266 (statp.st_blocks != ff_pkt->statp.st_blocks)) {
267 /* TODO: add size change */
268 Jmsg(jcr, M_ERROR, 0, _("%s size changed during backup.\n"),ff_pkt->fname);
276 * For incremental/diffential or accurate backups, we
277 * determine if the current file has changed.
279 static bool check_changes(JCR *jcr, FF_PKT *ff_pkt)
281 /* in special mode (like accurate backup), the programmer can
282 * choose his comparison function.
284 if (ff_pkt->check_fct) {
285 return ff_pkt->check_fct(jcr, ff_pkt);
288 /* For normal backups (incr/diff), we use this default
291 if (ff_pkt->incremental &&
292 (ff_pkt->statp.st_mtime < ff_pkt->save_time &&
293 ((ff_pkt->flags & FO_MTIMEONLY) ||
294 ff_pkt->statp.st_ctime < ff_pkt->save_time)))
302 static bool have_ignoredir(FF_PKT *ff_pkt)
305 char tmp_name[MAXPATHLEN];
308 /* Ensure that pointers are defined */
309 if (!ff_pkt->fileset || !ff_pkt->fileset->incexe) {
312 ignoredir = ff_pkt->fileset->incexe->ignoredir;
315 if (strlen(ff_pkt->fname) + strlen(ignoredir) + 2 > MAXPATHLEN) {
319 strcpy(tmp_name, ff_pkt->fname);
320 strcat(tmp_name, "/");
321 strcat(tmp_name, ignoredir);
322 if (stat(tmp_name, &sb) == 0) {
323 Dmsg2(100, "Directory '%s' ignored (found %s)\n",
324 ff_pkt->fname, ignoredir);
325 return true; /* Just ignore this directory */
332 * Find a single file.
333 * handle_file is the callback for handling the file.
335 * parent_device is the device we are currently on
336 * top_level is 1 when not recursing or 0 when
337 * descending into a directory.
340 find_one_file(JCR *jcr, FF_PKT *ff_pkt,
341 int handle_file(JCR *jcr, FF_PKT *ff, bool top_level),
342 char *fname, dev_t parent_device, bool top_level)
344 struct utimbuf restore_times;
348 ff_pkt->fname = ff_pkt->link = fname;
350 if (lstat(fname, &ff_pkt->statp) != 0) {
351 /* Cannot stat file */
352 ff_pkt->type = FT_NOSTAT;
353 ff_pkt->ff_errno = errno;
354 return handle_file(jcr, ff_pkt, top_level);
357 Dmsg1(300, "File ----: %s\n", fname);
359 /* Save current times of this directory in case we need to
360 * reset them because the user doesn't want them changed.
362 restore_times.actime = ff_pkt->statp.st_atime;
363 restore_times.modtime = ff_pkt->statp.st_mtime;
366 * We check for allowed fstypes and drivetypes at top_level and fstype change (below).
369 if (!accept_fstype(ff_pkt, NULL)) {
370 ff_pkt->type = FT_INVALIDFS;
371 if (ff_pkt->flags & FO_KEEPATIME) {
372 utime(fname, &restore_times);
377 if (!fstype(ff_pkt->fname, fs, sizeof(fs))) {
378 bstrncpy(fs, "unknown", sizeof(fs));
381 Jmsg(jcr, M_INFO, 0, _("Top level directory \"%s\" has unlisted fstype \"%s\"\n"), fname, fs);
382 return 1; /* Just ignore this error - or the whole backup is cancelled */
384 if (!accept_drivetype(ff_pkt, NULL)) {
385 ff_pkt->type = FT_INVALIDDT;
386 if (ff_pkt->flags & FO_KEEPATIME) {
387 utime(fname, &restore_times);
392 if (!drivetype(ff_pkt->fname, dt, sizeof(dt))) {
393 bstrncpy(dt, "unknown", sizeof(dt));
396 Jmsg(jcr, M_INFO, 0, _("Top level directory \"%s\" has an unlisted drive type \"%s\"\n"), fname, dt);
397 return 1; /* Just ignore this error - or the whole backup is cancelled */
399 ff_pkt->volhas_attrlist = volume_has_attrlist(fname);
403 * Ignore this entry if no_dump() returns true
405 if (no_dump(jcr, ff_pkt)) {
406 Dmsg1(100, "'%s' ignored (NODUMP flag set)\n",
412 * If this is an Incremental backup, see if file was modified
413 * since our last "save_time", presumably the last Full save
416 if ( ff_pkt->incremental
417 && !S_ISDIR(ff_pkt->statp.st_mode)
418 && !check_changes(jcr, ff_pkt))
420 Dmsg1(500, "Non-directory incremental: %s\n", ff_pkt->fname);
421 ff_pkt->type = FT_NOCHG;
422 return handle_file(jcr, ff_pkt, top_level);
425 #ifdef HAVE_DARWIN_OS
426 if (ff_pkt->flags & FO_HFSPLUS && ff_pkt->volhas_attrlist
427 && S_ISREG(ff_pkt->statp.st_mode)) {
428 /* TODO: initialise attrList once elsewhere? */
429 struct attrlist attrList;
430 memset(&attrList, 0, sizeof(attrList));
431 attrList.bitmapcount = ATTR_BIT_MAP_COUNT;
432 attrList.commonattr = ATTR_CMN_FNDRINFO;
433 attrList.fileattr = ATTR_FILE_RSRCLENGTH;
434 if (getattrlist(fname, &attrList, &ff_pkt->hfsinfo,
435 sizeof(ff_pkt->hfsinfo), FSOPT_NOFOLLOW) != 0) {
436 ff_pkt->type = FT_NOSTAT;
437 ff_pkt->ff_errno = errno;
438 return handle_file(jcr, ff_pkt, top_level);
445 * Handle hard linked files
447 * Maintain a list of hard linked files already backed up. This
448 * allows us to ensure that the data of each file gets backed
451 if (!(ff_pkt->flags & FO_NO_HARDLINK)
452 && ff_pkt->statp.st_nlink > 1
453 && (S_ISREG(ff_pkt->statp.st_mode)
454 || S_ISCHR(ff_pkt->statp.st_mode)
455 || S_ISBLK(ff_pkt->statp.st_mode)
456 || S_ISFIFO(ff_pkt->statp.st_mode)
457 || S_ISSOCK(ff_pkt->statp.st_mode))) {
460 if (ff_pkt->linkhash == NULL) {
461 ff_pkt->linkhash = (link_t **)bmalloc(LINK_HASHTABLE_SIZE * sizeof(link_t *));
462 memset(ff_pkt->linkhash, 0, LINK_HASHTABLE_SIZE * sizeof(link_t *));
464 const int linkhash = LINKHASH(ff_pkt->statp);
466 /* Search link list of hard linked files */
467 for (lp = ff_pkt->linkhash[linkhash]; lp; lp = lp->next)
468 if (lp->ino == (ino_t)ff_pkt->statp.st_ino &&
469 lp->dev == (dev_t)ff_pkt->statp.st_dev) {
470 /* If we have already backed up the hard linked file don't do it again */
471 if (strcmp(lp->name, fname) == 0) {
472 Dmsg2(400, "== Name identical skip FI=%d file=%s\n", lp->FileIndex, fname);
473 return 1; /* ignore */
475 ff_pkt->link = lp->name;
476 ff_pkt->type = FT_LNKSAVED; /* Handle link, file already saved */
477 ff_pkt->LinkFI = lp->FileIndex;
479 rtn_stat = handle_file(jcr, ff_pkt, top_level);
480 Dmsg3(400, "FT_LNKSAVED FI=%d LinkFI=%d file=%s\n",
481 ff_pkt->FileIndex, lp->FileIndex, lp->name);
485 /* File not previously dumped. Chain it into our list. */
486 len = strlen(fname) + 1;
487 lp = (struct f_link *)bmalloc(sizeof(struct f_link) + len);
488 lp->ino = ff_pkt->statp.st_ino;
489 lp->dev = ff_pkt->statp.st_dev;
490 lp->FileIndex = 0; /* set later */
491 bstrncpy(lp->name, fname, len);
492 lp->next = ff_pkt->linkhash[linkhash];
493 ff_pkt->linkhash[linkhash] = lp;
494 ff_pkt->linked = lp; /* mark saved link */
495 Dmsg2(400, "added to hash FI=%d file=%s\n", ff_pkt->FileIndex, lp->name);
497 ff_pkt->linked = NULL;
500 /* This is not a link to a previously dumped file, so dump it. */
501 if (S_ISREG(ff_pkt->statp.st_mode)) {
504 sizeleft = ff_pkt->statp.st_size;
506 /* Don't bother opening empty, world readable files. Also do not open
507 files when archive is meant for /dev/null. */
508 if (ff_pkt->null_output_device || (sizeleft == 0
509 && MODE_RALL == (MODE_RALL & ff_pkt->statp.st_mode))) {
510 ff_pkt->type = FT_REGE;
512 ff_pkt->type = FT_REG;
514 rtn_stat = handle_file(jcr, ff_pkt, top_level);
515 if (ff_pkt->linked) {
516 ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
518 Dmsg3(400, "FT_REG FI=%d linked=%d file=%s\n", ff_pkt->FileIndex,
519 ff_pkt->linked ? 1 : 0, fname);
520 if (ff_pkt->flags & FO_KEEPATIME) {
521 utime(fname, &restore_times);
526 } else if (S_ISLNK(ff_pkt->statp.st_mode)) { /* soft link */
528 char *buffer = (char *)alloca(path_max + name_max + 102);
530 size = readlink(fname, buffer, path_max + name_max + 101);
532 /* Could not follow link */
533 ff_pkt->type = FT_NOFOLLOW;
534 ff_pkt->ff_errno = errno;
535 rtn_stat = handle_file(jcr, ff_pkt, top_level);
536 if (ff_pkt->linked) {
537 ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
542 ff_pkt->link = buffer; /* point to link */
543 ff_pkt->type = FT_LNK; /* got a real link */
544 rtn_stat = handle_file(jcr, ff_pkt, top_level);
545 if (ff_pkt->linked) {
546 ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
550 } else if (S_ISDIR(ff_pkt->statp.st_mode)) {
552 struct dirent *entry, *result;
557 dev_t our_device = ff_pkt->statp.st_dev;
559 bool volhas_attrlist = ff_pkt->volhas_attrlist; /* Remember this if we recurse */
562 * If we are using Win32 (non-portable) backup API, don't check
563 * access as everything is more complicated, and
564 * in principle, we should be able to access everything.
566 if (!have_win32_api() || (ff_pkt->flags & FO_PORTABLE)) {
567 if (access(fname, R_OK) == -1 && geteuid() != 0) {
568 /* Could not access() directory */
569 ff_pkt->type = FT_NOACCESS;
570 ff_pkt->ff_errno = errno;
571 rtn_stat = handle_file(jcr, ff_pkt, top_level);
572 if (ff_pkt->linked) {
573 ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
580 * Ignore this directory and everything below if the file .nobackup
581 * (or what is defined for IgnoreDir in this fileset) exists
583 if (have_ignoredir(ff_pkt)) {
584 return 1; /* Just ignore this directory */
587 /* Build a canonical directory name with a trailing slash in link var */
589 link_len = len + 200;
590 link = (char *)bmalloc(link_len + 2);
591 bstrncpy(link, fname, link_len);
592 /* Strip all trailing slashes */
593 while (len >= 1 && IsPathSeparator(link[len - 1]))
595 link[len++] = '/'; /* add back one */
599 if (ff_pkt->incremental && !check_changes(jcr, ff_pkt)) {
600 /* Incremental option, directory entry not changed */
601 ff_pkt->type = FT_DIRNOCHG;
603 ff_pkt->type = FT_DIRBEGIN;
606 * We have set st_rdev to 1 if it is a reparse point, otherwise 0,
607 * if st_rdev is 2, it is a mount point
609 #if defined(HAVE_WIN32)
610 if (ff_pkt->statp.st_rdev == WIN32_REPARSE_POINT) {
611 ff_pkt->type = FT_REPARSE;
615 * Note, we return the directory to the calling program (handle_file)
616 * when we first see the directory (FT_DIRBEGIN.
617 * This allows the program to apply matches and make a
618 * choice whether or not to accept it. If it is accepted, we
619 * do not immediately save it, but do so only after everything
620 * in the directory is seen (i.e. the FT_DIREND).
622 rtn_stat = handle_file(jcr, ff_pkt, top_level);
623 if (rtn_stat < 1 || ff_pkt->type == FT_REPARSE) { /* ignore or error status */
627 /* Done with DIRBEGIN, next call will be DIREND */
628 if (ff_pkt->type == FT_DIRBEGIN) {
629 ff_pkt->type = FT_DIREND;
633 * Create a temporary ff packet for this directory
634 * entry, and defer handling the directory until
635 * we have recursed into it. This saves the
636 * directory after all files have been processed, and
637 * during the restore, the directory permissions will
638 * be reset after all the files have been restored.
640 Dmsg1(300, "Create temp ff packet for dir: %s\n", ff_pkt->fname);
641 FF_PKT *dir_ff_pkt = new_dir_ff_pkt(ff_pkt);
644 * Do not descend into subdirectories (recurse) if the
645 * user has turned it off for this directory.
647 * If we are crossing file systems, we are either not allowed
648 * to cross, or we may be restricted by a list of permitted
651 bool is_win32_mount_point = false;
652 #if defined(HAVE_WIN32)
653 is_win32_mount_point = ff_pkt->statp.st_rdev == WIN32_MOUNT_POINT;
655 if (!top_level && ff_pkt->flags & FO_NO_RECURSION) {
656 ff_pkt->type = FT_NORECURSE;
658 } else if (!top_level && (parent_device != ff_pkt->statp.st_dev ||
659 is_win32_mount_point)) {
660 if(!(ff_pkt->flags & FO_MULTIFS)) {
661 ff_pkt->type = FT_NOFSCHG;
663 } else if (!accept_fstype(ff_pkt, NULL)) {
664 ff_pkt->type = FT_INVALIDFS;
667 ff_pkt->volhas_attrlist = volume_has_attrlist(fname);
670 /* If not recursing, just backup dir and return */
672 rtn_stat = handle_file(jcr, ff_pkt, top_level);
673 if (ff_pkt->linked) {
674 ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
677 free_dir_ff_pkt(dir_ff_pkt);
678 ff_pkt->link = ff_pkt->fname; /* reset "link" */
679 if (ff_pkt->flags & FO_KEEPATIME) {
680 utime(fname, &restore_times);
685 ff_pkt->link = ff_pkt->fname; /* reset "link" */
688 * Descend into or "recurse" into the directory to read
689 * all the files in it.
692 if ((directory = opendir(fname)) == NULL) {
693 ff_pkt->type = FT_NOOPEN;
694 ff_pkt->ff_errno = errno;
695 rtn_stat = handle_file(jcr, ff_pkt, top_level);
696 if (ff_pkt->linked) {
697 ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
700 free_dir_ff_pkt(dir_ff_pkt);
705 * Process all files in this directory entry (recursing).
706 * This would possibly run faster if we chdir to the directory
707 * before traversing it.
710 entry = (struct dirent *)malloc(sizeof(struct dirent) + name_max + 100);
711 for ( ; !job_canceled(jcr); ) {
715 status = readdir_r(directory, entry, &result);
716 if (status != 0 || result == NULL) {
717 // Dmsg2(99, "readdir returned stat=%d result=0x%x\n",
718 // status, (long)result);
721 ASSERT(name_max+1 > (int)sizeof(struct dirent) + (int)NAMELEN(entry));
723 /* Skip `.', `..', and excluded file names. */
724 if (p[0] == '\0' || (p[0] == '.' && (p[1] == '\0' ||
725 (p[1] == '.' && p[2] == '\0')))) {
729 if ((int)NAMELEN(entry) + len >= link_len) {
730 link_len = len + NAMELEN(entry) + 1;
731 link = (char *)brealloc(link, link_len + 1);
734 for (i=0; i < (int)NAMELEN(entry); i++) {
738 if (!file_is_excluded(ff_pkt, link)) {
739 rtn_stat = find_one_file(jcr, ff_pkt, handle_file, link, our_device, false);
740 if (ff_pkt->linked) {
741 ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
750 * Now that we have recursed through all the files in the
751 * directory, we "save" the directory so that after all
752 * the files are restored, this entry will serve to reset
753 * the directory modes and dates. Temp directory values
754 * were used without this record.
756 handle_file(jcr, dir_ff_pkt, top_level); /* handle directory entry */
757 if (ff_pkt->linked) {
758 ff_pkt->linked->FileIndex = dir_ff_pkt->FileIndex;
760 free_dir_ff_pkt(dir_ff_pkt);
762 if (ff_pkt->flags & FO_KEEPATIME) {
763 utime(fname, &restore_times);
765 ff_pkt->volhas_attrlist = volhas_attrlist; /* Restore value in case it changed. */
767 } /* end check for directory */
770 * If it is explicitly mentioned (i.e. top_level) and is
771 * a block device, we do a raw backup of it or if it is
772 * a fifo, we simply read it.
774 #ifdef HAVE_FREEBSD_OS
776 * On FreeBSD, all block devices are character devices, so
777 * to be able to read a raw disk, we need the check for
778 * a character device.
779 * crw-r----- 1 root operator - 116, 0x00040002 Jun 9 19:32 /dev/ad0s3
780 * crw-r----- 1 root operator - 116, 0x00040002 Jun 9 19:32 /dev/rad0s3
782 if (top_level && (S_ISBLK(ff_pkt->statp.st_mode) || S_ISCHR(ff_pkt->statp.st_mode))) {
784 if (top_level && S_ISBLK(ff_pkt->statp.st_mode)) {
786 ff_pkt->type = FT_RAW; /* raw partition */
787 } else if (top_level && S_ISFIFO(ff_pkt->statp.st_mode) &&
788 ff_pkt->flags & FO_READFIFO) {
789 ff_pkt->type = FT_FIFO;
791 /* The only remaining types are special (character, ...) files */
792 ff_pkt->type = FT_SPEC;
794 rtn_stat = handle_file(jcr, ff_pkt, top_level);
795 if (ff_pkt->linked) {
796 ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
801 int term_find_one(FF_PKT *ff)
803 struct f_link *lp, *lc;
808 if (ff->linkhash == NULL) return 0;
810 for (i =0 ; i < LINK_HASHTABLE_SIZE; i ++) {
811 /* Free up list of hard linked files */
812 lp = ff->linkhash[i];
821 ff->linkhash[i] = NULL;