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];
306 char *ignoredir = ff_pkt->fileset->incexe->ignoredir;
309 if (strlen(ff_pkt->fname) + strlen(ignoredir) + 2 > MAXPATHLEN) {
313 strcpy(tmp_name, ff_pkt->fname);
314 strcat(tmp_name, "/");
315 strcat(tmp_name, ignoredir);
316 if (stat(tmp_name, &sb) == 0) {
317 Dmsg2(100, "Directory '%s' ignored (found %s)\n",
318 ff_pkt->fname, ignoredir);
319 return true; /* Just ignore this directory */
326 * Find a single file.
327 * handle_file is the callback for handling the file.
329 * parent_device is the device we are currently on
330 * top_level is 1 when not recursing or 0 when
331 * descending into a directory.
334 find_one_file(JCR *jcr, FF_PKT *ff_pkt,
335 int handle_file(JCR *jcr, FF_PKT *ff, bool top_level),
336 char *fname, dev_t parent_device, bool top_level)
338 struct utimbuf restore_times;
342 ff_pkt->fname = ff_pkt->link = fname;
344 if (lstat(fname, &ff_pkt->statp) != 0) {
345 /* Cannot stat file */
346 ff_pkt->type = FT_NOSTAT;
347 ff_pkt->ff_errno = errno;
348 return handle_file(jcr, ff_pkt, top_level);
351 Dmsg1(300, "File ----: %s\n", fname);
353 /* Save current times of this directory in case we need to
354 * reset them because the user doesn't want them changed.
356 restore_times.actime = ff_pkt->statp.st_atime;
357 restore_times.modtime = ff_pkt->statp.st_mtime;
360 * We check for allowed fstypes and drivetypes at top_level and fstype change (below).
363 if (!accept_fstype(ff_pkt, NULL)) {
364 ff_pkt->type = FT_INVALIDFS;
365 if (ff_pkt->flags & FO_KEEPATIME) {
366 utime(fname, &restore_times);
371 if (!fstype(ff_pkt->fname, fs, sizeof(fs))) {
372 bstrncpy(fs, "unknown", sizeof(fs));
375 Jmsg(jcr, M_INFO, 0, _("Top level directory \"%s\" has unlisted fstype \"%s\"\n"), fname, fs);
376 return 1; /* Just ignore this error - or the whole backup is cancelled */
378 if (!accept_drivetype(ff_pkt, NULL)) {
379 ff_pkt->type = FT_INVALIDDT;
380 if (ff_pkt->flags & FO_KEEPATIME) {
381 utime(fname, &restore_times);
386 if (!drivetype(ff_pkt->fname, dt, sizeof(dt))) {
387 bstrncpy(dt, "unknown", sizeof(dt));
390 Jmsg(jcr, M_INFO, 0, _("Top level directory \"%s\" has an unlisted drive type \"%s\"\n"), fname, dt);
391 return 1; /* Just ignore this error - or the whole backup is cancelled */
393 ff_pkt->volhas_attrlist = volume_has_attrlist(fname);
397 * Ignore this entry if no_dump() returns true
399 if (no_dump(jcr, ff_pkt)) {
400 Dmsg1(100, "'%s' ignored (NODUMP flag set)\n",
406 * If this is an Incremental backup, see if file was modified
407 * since our last "save_time", presumably the last Full save
410 if ( ff_pkt->incremental
411 && !S_ISDIR(ff_pkt->statp.st_mode)
412 && !check_changes(jcr, ff_pkt))
414 Dmsg1(500, "Non-directory incremental: %s\n", ff_pkt->fname);
415 ff_pkt->type = FT_NOCHG;
416 return handle_file(jcr, ff_pkt, top_level);
419 #ifdef HAVE_DARWIN_OS
420 if (ff_pkt->flags & FO_HFSPLUS && ff_pkt->volhas_attrlist
421 && S_ISREG(ff_pkt->statp.st_mode)) {
422 /* TODO: initialise attrList once elsewhere? */
423 struct attrlist attrList;
424 memset(&attrList, 0, sizeof(attrList));
425 attrList.bitmapcount = ATTR_BIT_MAP_COUNT;
426 attrList.commonattr = ATTR_CMN_FNDRINFO;
427 attrList.fileattr = ATTR_FILE_RSRCLENGTH;
428 if (getattrlist(fname, &attrList, &ff_pkt->hfsinfo,
429 sizeof(ff_pkt->hfsinfo), FSOPT_NOFOLLOW) != 0) {
430 ff_pkt->type = FT_NOSTAT;
431 ff_pkt->ff_errno = errno;
432 return handle_file(jcr, ff_pkt, top_level);
439 * Handle hard linked files
441 * Maintain a list of hard linked files already backed up. This
442 * allows us to ensure that the data of each file gets backed
445 if (!(ff_pkt->flags & FO_NO_HARDLINK)
446 && ff_pkt->statp.st_nlink > 1
447 && (S_ISREG(ff_pkt->statp.st_mode)
448 || S_ISCHR(ff_pkt->statp.st_mode)
449 || S_ISBLK(ff_pkt->statp.st_mode)
450 || S_ISFIFO(ff_pkt->statp.st_mode)
451 || S_ISSOCK(ff_pkt->statp.st_mode))) {
454 if (ff_pkt->linkhash == NULL) {
455 ff_pkt->linkhash = (link_t **)bmalloc(LINK_HASHTABLE_SIZE * sizeof(link_t *));
456 memset(ff_pkt->linkhash, 0, LINK_HASHTABLE_SIZE * sizeof(link_t *));
458 const int linkhash = LINKHASH(ff_pkt->statp);
460 /* Search link list of hard linked files */
461 for (lp = ff_pkt->linkhash[linkhash]; lp; lp = lp->next)
462 if (lp->ino == (ino_t)ff_pkt->statp.st_ino &&
463 lp->dev == (dev_t)ff_pkt->statp.st_dev) {
464 /* If we have already backed up the hard linked file don't do it again */
465 if (strcmp(lp->name, fname) == 0) {
466 Dmsg2(400, "== Name identical skip FI=%d file=%s\n", lp->FileIndex, fname);
467 return 1; /* ignore */
469 ff_pkt->link = lp->name;
470 ff_pkt->type = FT_LNKSAVED; /* Handle link, file already saved */
471 ff_pkt->LinkFI = lp->FileIndex;
473 rtn_stat = handle_file(jcr, ff_pkt, top_level);
474 Dmsg3(400, "FT_LNKSAVED FI=%d LinkFI=%d file=%s\n",
475 ff_pkt->FileIndex, lp->FileIndex, lp->name);
479 /* File not previously dumped. Chain it into our list. */
480 len = strlen(fname) + 1;
481 lp = (struct f_link *)bmalloc(sizeof(struct f_link) + len);
482 lp->ino = ff_pkt->statp.st_ino;
483 lp->dev = ff_pkt->statp.st_dev;
484 lp->FileIndex = 0; /* set later */
485 bstrncpy(lp->name, fname, len);
486 lp->next = ff_pkt->linkhash[linkhash];
487 ff_pkt->linkhash[linkhash] = lp;
488 ff_pkt->linked = lp; /* mark saved link */
489 Dmsg2(400, "added to hash FI=%d file=%s\n", ff_pkt->FileIndex, lp->name);
491 ff_pkt->linked = NULL;
494 /* This is not a link to a previously dumped file, so dump it. */
495 if (S_ISREG(ff_pkt->statp.st_mode)) {
498 sizeleft = ff_pkt->statp.st_size;
500 /* Don't bother opening empty, world readable files. Also do not open
501 files when archive is meant for /dev/null. */
502 if (ff_pkt->null_output_device || (sizeleft == 0
503 && MODE_RALL == (MODE_RALL & ff_pkt->statp.st_mode))) {
504 ff_pkt->type = FT_REGE;
506 ff_pkt->type = FT_REG;
508 rtn_stat = handle_file(jcr, ff_pkt, top_level);
509 if (ff_pkt->linked) {
510 ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
512 Dmsg3(400, "FT_REG FI=%d linked=%d file=%s\n", ff_pkt->FileIndex,
513 ff_pkt->linked ? 1 : 0, fname);
514 if (ff_pkt->flags & FO_KEEPATIME) {
515 utime(fname, &restore_times);
520 } else if (S_ISLNK(ff_pkt->statp.st_mode)) { /* soft link */
522 char *buffer = (char *)alloca(path_max + name_max + 102);
524 size = readlink(fname, buffer, path_max + name_max + 101);
526 /* Could not follow link */
527 ff_pkt->type = FT_NOFOLLOW;
528 ff_pkt->ff_errno = errno;
529 rtn_stat = handle_file(jcr, ff_pkt, top_level);
530 if (ff_pkt->linked) {
531 ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
536 ff_pkt->link = buffer; /* point to link */
537 ff_pkt->type = FT_LNK; /* got a real link */
538 rtn_stat = handle_file(jcr, ff_pkt, top_level);
539 if (ff_pkt->linked) {
540 ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
544 } else if (S_ISDIR(ff_pkt->statp.st_mode)) {
546 struct dirent *entry, *result;
551 dev_t our_device = ff_pkt->statp.st_dev;
553 bool volhas_attrlist = ff_pkt->volhas_attrlist; /* Remember this if we recurse */
556 * If we are using Win32 (non-portable) backup API, don't check
557 * access as everything is more complicated, and
558 * in principle, we should be able to access everything.
560 if (!have_win32_api() || (ff_pkt->flags & FO_PORTABLE)) {
561 if (access(fname, R_OK) == -1 && geteuid() != 0) {
562 /* Could not access() directory */
563 ff_pkt->type = FT_NOACCESS;
564 ff_pkt->ff_errno = errno;
565 rtn_stat = handle_file(jcr, ff_pkt, top_level);
566 if (ff_pkt->linked) {
567 ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
574 * Ignore this directory and everything below if the file .nobackup
575 * (or what is defined for IgnoreDir in this fileset) exists
577 if (have_ignoredir(ff_pkt)) {
578 return 1; /* Just ignore this directory */
581 /* Build a canonical directory name with a trailing slash in link var */
583 link_len = len + 200;
584 link = (char *)bmalloc(link_len + 2);
585 bstrncpy(link, fname, link_len);
586 /* Strip all trailing slashes */
587 while (len >= 1 && IsPathSeparator(link[len - 1]))
589 link[len++] = '/'; /* add back one */
593 if (ff_pkt->incremental && !check_changes(jcr, ff_pkt)) {
594 /* Incremental option, directory entry not changed */
595 ff_pkt->type = FT_DIRNOCHG;
597 ff_pkt->type = FT_DIRBEGIN;
600 * We have set st_rdev to 1 if it is a reparse point, otherwise 0,
601 * if st_rdev is 2, it is a mount point
603 #if defined(HAVE_WIN32)
604 if (ff_pkt->statp.st_rdev == WIN32_REPARSE_POINT) {
605 ff_pkt->type = FT_REPARSE;
609 * Note, we return the directory to the calling program (handle_file)
610 * when we first see the directory (FT_DIRBEGIN.
611 * This allows the program to apply matches and make a
612 * choice whether or not to accept it. If it is accepted, we
613 * do not immediately save it, but do so only after everything
614 * in the directory is seen (i.e. the FT_DIREND).
616 rtn_stat = handle_file(jcr, ff_pkt, top_level);
617 if (rtn_stat < 1 || ff_pkt->type == FT_REPARSE) { /* ignore or error status */
621 /* Done with DIRBEGIN, next call will be DIREND */
622 if (ff_pkt->type == FT_DIRBEGIN) {
623 ff_pkt->type = FT_DIREND;
627 * Create a temporary ff packet for this directory
628 * entry, and defer handling the directory until
629 * we have recursed into it. This saves the
630 * directory after all files have been processed, and
631 * during the restore, the directory permissions will
632 * be reset after all the files have been restored.
634 Dmsg1(300, "Create temp ff packet for dir: %s\n", ff_pkt->fname);
635 FF_PKT *dir_ff_pkt = new_dir_ff_pkt(ff_pkt);
638 * Do not descend into subdirectories (recurse) if the
639 * user has turned it off for this directory.
641 * If we are crossing file systems, we are either not allowed
642 * to cross, or we may be restricted by a list of permitted
645 bool is_win32_mount_point = false;
646 #if defined(HAVE_WIN32)
647 is_win32_mount_point = ff_pkt->statp.st_rdev == WIN32_MOUNT_POINT;
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 #ifdef HAVE_FREEBSD_OS
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];
815 ff->linkhash[i] = NULL;