2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2010 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 three of the GNU Affero 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 Affero 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.
42 #include <sys/param.h>
43 #include <sys/mount.h>
47 extern int32_t name_max; /* filename max length */
48 extern int32_t path_max; /* path name max length */
51 * Structure for keeping track of hard linked files, we
52 * keep an entry for each hardlinked file that we save,
53 * which is the first one found. For all the other files that
54 * are linked to this one, we save only the directory
55 * entry so we can link it.
59 dev_t dev; /* device */
60 ino_t ino; /* inode with device is unique */
61 uint32_t FileIndex; /* Bacula FileIndex of this file */
62 char name[1]; /* The name */
65 typedef struct f_link link_t;
66 #define LINK_HASHTABLE_BITS 16
67 #define LINK_HASHTABLE_SIZE (1<<LINK_HASHTABLE_BITS)
68 #define LINK_HASHTABLE_MASK (LINK_HASHTABLE_SIZE-1)
70 static inline int LINKHASH(const struct stat &info)
72 int hash = info.st_dev;
73 unsigned long long i = info.st_ino;
81 return hash & LINK_HASHTABLE_MASK;
85 * Create a new directory Find File packet, but copy
86 * some of the essential info from the current packet.
87 * However, be careful to zero out the rest of the
90 static FF_PKT *new_dir_ff_pkt(FF_PKT *ff_pkt)
92 FF_PKT *dir_ff_pkt = (FF_PKT *)bmalloc(sizeof(FF_PKT));
93 memcpy(dir_ff_pkt, ff_pkt, sizeof(FF_PKT));
94 dir_ff_pkt->fname = bstrdup(ff_pkt->fname);
95 dir_ff_pkt->link = bstrdup(ff_pkt->link);
96 dir_ff_pkt->sys_fname = get_pool_memory(PM_FNAME);
97 dir_ff_pkt->included_files_list = NULL;
98 dir_ff_pkt->excluded_files_list = NULL;
99 dir_ff_pkt->excluded_paths_list = NULL;
100 dir_ff_pkt->linkhash = NULL;
101 dir_ff_pkt->fname_save = NULL;
102 dir_ff_pkt->link_save = NULL;
107 * Free the temp directory ff_pkt
109 static void free_dir_ff_pkt(FF_PKT *dir_ff_pkt)
111 free(dir_ff_pkt->fname);
112 free(dir_ff_pkt->link);
113 free_pool_memory(dir_ff_pkt->sys_fname);
114 if (dir_ff_pkt->fname_save) {
115 free_pool_memory(dir_ff_pkt->fname_save);
117 if (dir_ff_pkt->link_save) {
118 free_pool_memory(dir_ff_pkt->link_save);
124 * Check to see if we allow the file system type of a file or directory.
125 * If we do not have a list of file system types, we accept anything.
127 static int accept_fstype(FF_PKT *ff, void *dummy) {
132 if (ff->fstypes.size()) {
134 if (!fstype(ff->fname, fs, sizeof(fs))) {
135 Dmsg1(50, "Cannot determine file system type for \"%s\"\n", ff->fname);
137 for (i = 0; i < ff->fstypes.size(); ++i) {
138 if (strcmp(fs, (char *)ff->fstypes.get(i)) == 0) {
139 Dmsg2(100, "Accepting fstype %s for \"%s\"\n", fs, ff->fname);
143 Dmsg3(200, "fstype %s for \"%s\" does not match %s\n", fs,
144 ff->fname, ff->fstypes.get(i));
152 * Check to see if we allow the drive type of a file or directory.
153 * If we do not have a list of drive types, we accept anything.
155 static int accept_drivetype(FF_PKT *ff, void *dummy) {
160 if (ff->drivetypes.size()) {
162 if (!drivetype(ff->fname, dt, sizeof(dt))) {
163 Dmsg1(50, "Cannot determine drive type for \"%s\"\n", ff->fname);
165 for (i = 0; i < ff->drivetypes.size(); ++i) {
166 if (strcmp(dt, (char *)ff->drivetypes.get(i)) == 0) {
167 Dmsg2(100, "Accepting drive type %s for \"%s\"\n", dt, ff->fname);
171 Dmsg3(200, "drive type %s for \"%s\" does not match %s\n", dt,
172 ff->fname, ff->drivetypes.get(i));
180 * This function determines whether we can use getattrlist()
181 * It's odd, but we have to use the function to determine that...
182 * Also, the man pages talk about things as if they were implemented.
184 * On Mac OS X, this succesfully differentiates between HFS+ and UFS
185 * volumes, which makes me trust it is OK for others, too.
187 static bool volume_has_attrlist(const char *fname)
189 #ifdef HAVE_DARWIN_OS
191 struct volinfo_struct {
192 unsigned long length; /* Mandatory field */
193 vol_capabilities_attr_t info; /* Volume capabilities */
195 struct attrlist attrList;
197 memset(&attrList, 0, sizeof(attrList));
198 attrList.bitmapcount = ATTR_BIT_MAP_COUNT;
199 attrList.volattr = ATTR_VOL_INFO | ATTR_VOL_CAPABILITIES;
200 if (statfs(fname, &st) == 0) {
201 /* We need to check on the mount point */
202 if (getattrlist(st.f_mntonname, &attrList, &vol, sizeof(vol), FSOPT_NOFOLLOW) == 0
203 && (vol.info.capabilities[VOL_CAPABILITIES_INTERFACES] & VOL_CAP_INT_ATTRLIST)
204 && (vol.info.valid[VOL_CAPABILITIES_INTERFACES] & VOL_CAP_INT_ATTRLIST)) {
213 * check for BSD nodump flag
215 static bool no_dump(JCR *jcr, FF_PKT *ff_pkt)
217 #if defined(HAVE_CHFLAGS) && defined(UF_NODUMP)
218 if ( (ff_pkt->flags & FO_HONOR_NODUMP) &&
219 (ff_pkt->statp.st_flags & UF_NODUMP) ) {
220 Jmsg(jcr, M_INFO, 1, _(" NODUMP flag set - will not process %s\n"),
222 return true; /* do not backup this file */
225 return false; /* do backup */
228 /* check if a file have changed during backup and display an error */
229 bool has_file_changed(JCR *jcr, FF_PKT *ff_pkt)
232 Dmsg1(500, "has_file_changed fname=%s\n",ff_pkt->fname);
234 if (ff_pkt->type != FT_REG) { /* not a regular file */
238 if (lstat(ff_pkt->fname, &statp) != 0) {
240 Jmsg(jcr, M_WARNING, 0,
241 _("Cannot stat file %s: ERR=%s\n"),ff_pkt->fname,be.bstrerror());
245 if (statp.st_mtime != ff_pkt->statp.st_mtime) {
246 /* TODO: add time of changes */
247 Jmsg(jcr, M_ERROR, 0, _("%s mtime changed during backup.\n"), ff_pkt->fname);
251 if (statp.st_ctime != ff_pkt->statp.st_ctime) {
252 /* TODO: add time of changes */
253 Jmsg(jcr, M_ERROR, 0, _("%s ctime changed during backup.\n"), ff_pkt->fname);
257 if (statp.st_size != ff_pkt->statp.st_size) {
258 /* TODO: add size change */
259 Jmsg(jcr, M_ERROR, 0, _("%s size changed during backup.\n"),ff_pkt->fname);
263 if ((statp.st_blksize != ff_pkt->statp.st_blksize) ||
264 (statp.st_blocks != ff_pkt->statp.st_blocks)) {
265 /* TODO: add size change */
266 Jmsg(jcr, M_ERROR, 0, _("%s size changed during backup.\n"),ff_pkt->fname);
274 * For incremental/diffential or accurate backups, we
275 * determine if the current file has changed.
277 static bool check_changes(JCR *jcr, FF_PKT *ff_pkt)
279 /* in special mode (like accurate backup), the programmer can
280 * choose his comparison function.
282 if (ff_pkt->check_fct) {
283 return ff_pkt->check_fct(jcr, ff_pkt);
286 /* For normal backups (incr/diff), we use this default
289 if (ff_pkt->incremental &&
290 (ff_pkt->statp.st_mtime < ff_pkt->save_time &&
291 ((ff_pkt->flags & FO_MTIMEONLY) ||
292 ff_pkt->statp.st_ctime < ff_pkt->save_time)))
300 static bool have_ignoredir(FF_PKT *ff_pkt)
303 char tmp_name[MAXPATHLEN];
306 /* Ensure that pointers are defined */
307 if (!ff_pkt->fileset || !ff_pkt->fileset->incexe) {
310 ignoredir = ff_pkt->fileset->incexe->ignoredir;
313 if (strlen(ff_pkt->fname) + strlen(ignoredir) + 2 > MAXPATHLEN) {
317 strcpy(tmp_name, ff_pkt->fname);
318 strcat(tmp_name, "/");
319 strcat(tmp_name, ignoredir);
320 if (stat(tmp_name, &sb) == 0) {
321 Dmsg2(100, "Directory '%s' ignored (found %s)\n",
322 ff_pkt->fname, ignoredir);
323 return true; /* Just ignore this directory */
330 * Find a single file.
331 * handle_file is the callback for handling the file.
333 * parent_device is the device we are currently on
334 * top_level is 1 when not recursing or 0 when
335 * descending into a directory.
338 find_one_file(JCR *jcr, FF_PKT *ff_pkt,
339 int handle_file(JCR *jcr, FF_PKT *ff, bool top_level),
340 char *fname, dev_t parent_device, bool top_level)
342 struct utimbuf restore_times;
346 ff_pkt->fname = ff_pkt->link = fname;
348 if (lstat(fname, &ff_pkt->statp) != 0) {
349 /* Cannot stat file */
350 ff_pkt->type = FT_NOSTAT;
351 ff_pkt->ff_errno = errno;
352 return handle_file(jcr, ff_pkt, top_level);
355 Dmsg1(300, "File ----: %s\n", fname);
357 /* Save current times of this directory in case we need to
358 * reset them because the user doesn't want them changed.
360 restore_times.actime = ff_pkt->statp.st_atime;
361 restore_times.modtime = ff_pkt->statp.st_mtime;
364 * We check for allowed fstypes and drivetypes at top_level and fstype change (below).
367 if (!accept_fstype(ff_pkt, NULL)) {
368 ff_pkt->type = FT_INVALIDFS;
369 if (ff_pkt->flags & FO_KEEPATIME) {
370 utime(fname, &restore_times);
375 if (!fstype(ff_pkt->fname, fs, sizeof(fs))) {
376 bstrncpy(fs, "unknown", sizeof(fs));
379 Jmsg(jcr, M_INFO, 0, _("Top level directory \"%s\" has unlisted fstype \"%s\"\n"), fname, fs);
380 return 1; /* Just ignore this error - or the whole backup is cancelled */
382 if (!accept_drivetype(ff_pkt, NULL)) {
383 ff_pkt->type = FT_INVALIDDT;
384 if (ff_pkt->flags & FO_KEEPATIME) {
385 utime(fname, &restore_times);
390 if (!drivetype(ff_pkt->fname, dt, sizeof(dt))) {
391 bstrncpy(dt, "unknown", sizeof(dt));
394 Jmsg(jcr, M_INFO, 0, _("Top level directory \"%s\" has an unlisted drive type \"%s\"\n"), fname, dt);
395 return 1; /* Just ignore this error - or the whole backup is cancelled */
397 ff_pkt->volhas_attrlist = volume_has_attrlist(fname);
401 * Ignore this entry if no_dump() returns true
403 if (no_dump(jcr, ff_pkt)) {
404 Dmsg1(100, "'%s' ignored (NODUMP flag set)\n",
410 * If this is an Incremental backup, see if file was modified
411 * since our last "save_time", presumably the last Full save
414 if ( !S_ISDIR(ff_pkt->statp.st_mode)
415 && !check_changes(jcr, ff_pkt))
417 Dmsg1(500, "Non-directory incremental: %s\n", ff_pkt->fname);
418 ff_pkt->type = FT_NOCHG;
419 return handle_file(jcr, ff_pkt, top_level);
422 #ifdef HAVE_DARWIN_OS
423 if (ff_pkt->flags & FO_HFSPLUS && ff_pkt->volhas_attrlist
424 && S_ISREG(ff_pkt->statp.st_mode)) {
425 /* TODO: initialise attrList once elsewhere? */
426 struct attrlist attrList;
427 memset(&attrList, 0, sizeof(attrList));
428 attrList.bitmapcount = ATTR_BIT_MAP_COUNT;
429 attrList.commonattr = ATTR_CMN_FNDRINFO;
430 attrList.fileattr = ATTR_FILE_RSRCLENGTH;
431 if (getattrlist(fname, &attrList, &ff_pkt->hfsinfo,
432 sizeof(ff_pkt->hfsinfo), FSOPT_NOFOLLOW) != 0) {
433 ff_pkt->type = FT_NOSTAT;
434 ff_pkt->ff_errno = errno;
435 return handle_file(jcr, ff_pkt, top_level);
442 * Handle hard linked files
444 * Maintain a list of hard linked files already backed up. This
445 * allows us to ensure that the data of each file gets backed
448 if (!(ff_pkt->flags & FO_NO_HARDLINK)
449 && ff_pkt->statp.st_nlink > 1
450 && (S_ISREG(ff_pkt->statp.st_mode)
451 || S_ISCHR(ff_pkt->statp.st_mode)
452 || S_ISBLK(ff_pkt->statp.st_mode)
453 || S_ISFIFO(ff_pkt->statp.st_mode)
454 || S_ISSOCK(ff_pkt->statp.st_mode))) {
457 if (ff_pkt->linkhash == NULL) {
458 ff_pkt->linkhash = (link_t **)bmalloc(LINK_HASHTABLE_SIZE * sizeof(link_t *));
459 memset(ff_pkt->linkhash, 0, LINK_HASHTABLE_SIZE * sizeof(link_t *));
461 const int linkhash = LINKHASH(ff_pkt->statp);
463 /* Search link list of hard linked files */
464 for (lp = ff_pkt->linkhash[linkhash]; lp; lp = lp->next)
465 if (lp->ino == (ino_t)ff_pkt->statp.st_ino &&
466 lp->dev == (dev_t)ff_pkt->statp.st_dev) {
467 /* If we have already backed up the hard linked file don't do it again */
468 if (strcmp(lp->name, fname) == 0) {
469 Dmsg2(400, "== Name identical skip FI=%d file=%s\n", lp->FileIndex, fname);
470 return 1; /* ignore */
472 ff_pkt->link = lp->name;
473 ff_pkt->type = FT_LNKSAVED; /* Handle link, file already saved */
474 ff_pkt->LinkFI = lp->FileIndex;
476 rtn_stat = handle_file(jcr, ff_pkt, top_level);
477 Dmsg3(400, "FT_LNKSAVED FI=%d LinkFI=%d file=%s\n",
478 ff_pkt->FileIndex, lp->FileIndex, lp->name);
482 /* File not previously dumped. Chain it into our list. */
483 len = strlen(fname) + 1;
484 lp = (struct f_link *)bmalloc(sizeof(struct f_link) + len);
485 lp->ino = ff_pkt->statp.st_ino;
486 lp->dev = ff_pkt->statp.st_dev;
487 lp->FileIndex = 0; /* set later */
488 bstrncpy(lp->name, fname, len);
489 lp->next = ff_pkt->linkhash[linkhash];
490 ff_pkt->linkhash[linkhash] = lp;
491 ff_pkt->linked = lp; /* mark saved link */
492 Dmsg2(400, "added to hash FI=%d file=%s\n", ff_pkt->FileIndex, lp->name);
494 ff_pkt->linked = NULL;
497 /* This is not a link to a previously dumped file, so dump it. */
498 if (S_ISREG(ff_pkt->statp.st_mode)) {
501 sizeleft = ff_pkt->statp.st_size;
503 /* Don't bother opening empty, world readable files. Also do not open
504 files when archive is meant for /dev/null. */
505 if (ff_pkt->null_output_device || (sizeleft == 0
506 && MODE_RALL == (MODE_RALL & ff_pkt->statp.st_mode))) {
507 ff_pkt->type = FT_REGE;
509 ff_pkt->type = FT_REG;
511 rtn_stat = handle_file(jcr, ff_pkt, top_level);
512 if (ff_pkt->linked) {
513 ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
515 Dmsg3(400, "FT_REG FI=%d linked=%d file=%s\n", ff_pkt->FileIndex,
516 ff_pkt->linked ? 1 : 0, fname);
517 if (ff_pkt->flags & FO_KEEPATIME) {
518 utime(fname, &restore_times);
523 } else if (S_ISLNK(ff_pkt->statp.st_mode)) { /* soft link */
525 char *buffer = (char *)alloca(path_max + name_max + 102);
527 size = readlink(fname, buffer, path_max + name_max + 101);
529 /* Could not follow link */
530 ff_pkt->type = FT_NOFOLLOW;
531 ff_pkt->ff_errno = errno;
532 rtn_stat = handle_file(jcr, ff_pkt, top_level);
533 if (ff_pkt->linked) {
534 ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
539 ff_pkt->link = buffer; /* point to link */
540 ff_pkt->type = FT_LNK; /* got a real link */
541 rtn_stat = handle_file(jcr, ff_pkt, top_level);
542 if (ff_pkt->linked) {
543 ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
547 } else if (S_ISDIR(ff_pkt->statp.st_mode)) {
549 struct dirent *entry, *result;
554 dev_t our_device = ff_pkt->statp.st_dev;
556 bool volhas_attrlist = ff_pkt->volhas_attrlist; /* Remember this if we recurse */
559 * Ignore this directory and everything below if the file .nobackup
560 * (or what is defined for IgnoreDir in this fileset) exists
562 if (have_ignoredir(ff_pkt)) {
563 return 1; /* Just ignore this directory */
566 /* Build a canonical directory name with a trailing slash in link var */
568 link_len = len + 200;
569 link = (char *)bmalloc(link_len + 2);
570 bstrncpy(link, fname, link_len);
571 /* Strip all trailing slashes */
572 while (len >= 1 && IsPathSeparator(link[len - 1]))
574 link[len++] = '/'; /* add back one */
578 if (!check_changes(jcr, ff_pkt)) {
579 /* Incremental/Full+Base option, directory entry not changed */
580 ff_pkt->type = FT_DIRNOCHG;
582 ff_pkt->type = FT_DIRBEGIN;
585 * We have set st_rdev to 1 if it is a reparse point, otherwise 0,
586 * if st_rdev is 2, it is a mount point
588 #if defined(HAVE_WIN32)
589 if (ff_pkt->statp.st_rdev == WIN32_REPARSE_POINT) {
590 ff_pkt->type = FT_REPARSE;
594 * Note, we return the directory to the calling program (handle_file)
595 * when we first see the directory (FT_DIRBEGIN.
596 * This allows the program to apply matches and make a
597 * choice whether or not to accept it. If it is accepted, we
598 * do not immediately save it, but do so only after everything
599 * in the directory is seen (i.e. the FT_DIREND).
601 rtn_stat = handle_file(jcr, ff_pkt, top_level);
602 if (rtn_stat < 1 || ff_pkt->type == FT_REPARSE) { /* ignore or error status */
606 /* Done with DIRBEGIN, next call will be DIREND */
607 if (ff_pkt->type == FT_DIRBEGIN) {
608 ff_pkt->type = FT_DIREND;
612 * Create a temporary ff packet for this directory
613 * entry, and defer handling the directory until
614 * we have recursed into it. This saves the
615 * directory after all files have been processed, and
616 * during the restore, the directory permissions will
617 * be reset after all the files have been restored.
619 Dmsg1(300, "Create temp ff packet for dir: %s\n", ff_pkt->fname);
620 FF_PKT *dir_ff_pkt = new_dir_ff_pkt(ff_pkt);
623 * Do not descend into subdirectories (recurse) if the
624 * user has turned it off for this directory.
626 * If we are crossing file systems, we are either not allowed
627 * to cross, or we may be restricted by a list of permitted
630 bool is_win32_mount_point = false;
631 #if defined(HAVE_WIN32)
632 is_win32_mount_point = ff_pkt->statp.st_rdev == WIN32_MOUNT_POINT;
634 if (!top_level && ff_pkt->flags & FO_NO_RECURSION) {
635 ff_pkt->type = FT_NORECURSE;
637 } else if (!top_level && (parent_device != ff_pkt->statp.st_dev ||
638 is_win32_mount_point)) {
639 if(!(ff_pkt->flags & FO_MULTIFS)) {
640 ff_pkt->type = FT_NOFSCHG;
642 } else if (!accept_fstype(ff_pkt, NULL)) {
643 ff_pkt->type = FT_INVALIDFS;
646 ff_pkt->volhas_attrlist = volume_has_attrlist(fname);
649 /* If not recursing, just backup dir and return */
651 rtn_stat = handle_file(jcr, ff_pkt, top_level);
652 if (ff_pkt->linked) {
653 ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
656 free_dir_ff_pkt(dir_ff_pkt);
657 ff_pkt->link = ff_pkt->fname; /* reset "link" */
658 if (ff_pkt->flags & FO_KEEPATIME) {
659 utime(fname, &restore_times);
664 ff_pkt->link = ff_pkt->fname; /* reset "link" */
667 * Descend into or "recurse" into the directory to read
668 * all the files in it.
671 if ((directory = opendir(fname)) == NULL) {
672 ff_pkt->type = FT_NOOPEN;
673 ff_pkt->ff_errno = errno;
674 rtn_stat = handle_file(jcr, ff_pkt, top_level);
675 if (ff_pkt->linked) {
676 ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
679 free_dir_ff_pkt(dir_ff_pkt);
684 * Process all files in this directory entry (recursing).
685 * This would possibly run faster if we chdir to the directory
686 * before traversing it.
689 entry = (struct dirent *)malloc(sizeof(struct dirent) + name_max + 100);
690 for ( ; !job_canceled(jcr); ) {
694 status = readdir_r(directory, entry, &result);
695 if (status != 0 || result == NULL) {
696 // Dmsg2(99, "readdir returned stat=%d result=0x%x\n",
697 // status, (long)result);
700 ASSERT(name_max+1 > (int)sizeof(struct dirent) + (int)NAMELEN(entry));
702 /* Skip `.', `..', and excluded file names. */
703 if (p[0] == '\0' || (p[0] == '.' && (p[1] == '\0' ||
704 (p[1] == '.' && p[2] == '\0')))) {
708 if ((int)NAMELEN(entry) + len >= link_len) {
709 link_len = len + NAMELEN(entry) + 1;
710 link = (char *)brealloc(link, link_len + 1);
713 for (i=0; i < (int)NAMELEN(entry); i++) {
717 if (!file_is_excluded(ff_pkt, link)) {
718 rtn_stat = find_one_file(jcr, ff_pkt, handle_file, link, our_device, false);
719 if (ff_pkt->linked) {
720 ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
729 * Now that we have recursed through all the files in the
730 * directory, we "save" the directory so that after all
731 * the files are restored, this entry will serve to reset
732 * the directory modes and dates. Temp directory values
733 * were used without this record.
735 handle_file(jcr, dir_ff_pkt, top_level); /* handle directory entry */
736 if (ff_pkt->linked) {
737 ff_pkt->linked->FileIndex = dir_ff_pkt->FileIndex;
739 free_dir_ff_pkt(dir_ff_pkt);
741 if (ff_pkt->flags & FO_KEEPATIME) {
742 utime(fname, &restore_times);
744 ff_pkt->volhas_attrlist = volhas_attrlist; /* Restore value in case it changed. */
746 } /* end check for directory */
749 * If it is explicitly mentioned (i.e. top_level) and is
750 * a block device, we do a raw backup of it or if it is
751 * a fifo, we simply read it.
753 #ifdef HAVE_FREEBSD_OS
755 * On FreeBSD, all block devices are character devices, so
756 * to be able to read a raw disk, we need the check for
757 * a character device.
758 * crw-r----- 1 root operator - 116, 0x00040002 Jun 9 19:32 /dev/ad0s3
759 * crw-r----- 1 root operator - 116, 0x00040002 Jun 9 19:32 /dev/rad0s3
761 if (top_level && (S_ISBLK(ff_pkt->statp.st_mode) || S_ISCHR(ff_pkt->statp.st_mode))) {
763 if (top_level && S_ISBLK(ff_pkt->statp.st_mode)) {
765 ff_pkt->type = FT_RAW; /* raw partition */
766 } else if (top_level && S_ISFIFO(ff_pkt->statp.st_mode) &&
767 ff_pkt->flags & FO_READFIFO) {
768 ff_pkt->type = FT_FIFO;
770 /* The only remaining types are special (character, ...) files */
771 ff_pkt->type = FT_SPEC;
773 rtn_stat = handle_file(jcr, ff_pkt, top_level);
774 if (ff_pkt->linked) {
775 ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
780 int term_find_one(FF_PKT *ff)
782 struct f_link *lp, *lc;
787 if (ff->linkhash == NULL) return 0;
789 for (i =0 ; i < LINK_HASHTABLE_SIZE; i ++) {
790 /* Free up list of hard linked files */
791 lp = ff->linkhash[i];
800 ff->linkhash[i] = NULL;