2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2008 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)) {
214 /* check if a file have changed during backup and display an error */
215 bool has_file_changed(JCR *jcr, FF_PKT *ff_pkt)
218 Dmsg1(500, "has_file_changed fname=%s\n",ff_pkt->fname);
220 if (ff_pkt->type != FT_REG) { /* not a regular file */
224 if (lstat(ff_pkt->fname, &statp) != 0) {
226 Jmsg(jcr, M_WARNING, 0,
227 _("Cannot stat file %s: ERR=%s\n"),ff_pkt->fname,be.bstrerror());
231 if (statp.st_mtime != ff_pkt->statp.st_mtime) {
232 /* TODO: add time of changes */
233 Jmsg(jcr, M_ERROR, 0, _("%s mtime changed during backup.\n"), ff_pkt->fname);
237 if (statp.st_ctime != ff_pkt->statp.st_ctime) {
238 /* TODO: add time of changes */
239 Jmsg(jcr, M_ERROR, 0, _("%s ctime changed during backup.\n"), ff_pkt->fname);
243 if (statp.st_size != ff_pkt->statp.st_size) {
244 /* TODO: add size change */
245 Jmsg(jcr, M_ERROR, 0, _("%s size changed during backup.\n"),ff_pkt->fname);
249 if ((statp.st_blksize != ff_pkt->statp.st_blksize) ||
250 (statp.st_blocks != ff_pkt->statp.st_blocks)) {
251 /* TODO: add size change */
252 Jmsg(jcr, M_ERROR, 0, _("%s size changed during backup.\n"),ff_pkt->fname);
260 * For incremental/diffential or accurate backups, we
261 * determine if the current file has changed.
263 static bool check_changes(JCR *jcr, FF_PKT *ff_pkt)
265 /* in special mode (like accurate backup), the programmer can
266 * choose his comparison function.
268 if (ff_pkt->check_fct) {
269 return ff_pkt->check_fct(jcr, ff_pkt);
272 /* For normal backups (incr/diff), we use this default
275 if (ff_pkt->incremental &&
276 (ff_pkt->statp.st_mtime < ff_pkt->save_time &&
277 ((ff_pkt->flags & FO_MTIMEONLY) ||
278 ff_pkt->statp.st_ctime < ff_pkt->save_time)))
287 * Find a single file.
288 * handle_file is the callback for handling the file.
290 * parent_device is the device we are currently on
291 * top_level is 1 when not recursing or 0 when
292 * descending into a directory.
295 find_one_file(JCR *jcr, FF_PKT *ff_pkt,
296 int handle_file(JCR *jcr, FF_PKT *ff, bool top_level),
297 char *fname, dev_t parent_device, bool top_level)
299 struct utimbuf restore_times;
303 ff_pkt->fname = ff_pkt->link = fname;
305 if (lstat(fname, &ff_pkt->statp) != 0) {
306 /* Cannot stat file */
307 ff_pkt->type = FT_NOSTAT;
308 ff_pkt->ff_errno = errno;
309 return handle_file(jcr, ff_pkt, top_level);
312 Dmsg1(300, "File ----: %s\n", fname);
314 /* Save current times of this directory in case we need to
315 * reset them because the user doesn't want them changed.
317 restore_times.actime = ff_pkt->statp.st_atime;
318 restore_times.modtime = ff_pkt->statp.st_mtime;
321 * We check for allowed fstypes and drivetypes at top_level and fstype change (below).
324 if (!accept_fstype(ff_pkt, NULL)) {
325 ff_pkt->type = FT_INVALIDFS;
326 if (ff_pkt->flags & FO_KEEPATIME) {
327 utime(fname, &restore_times);
332 if (!fstype(ff_pkt->fname, fs, sizeof(fs))) {
333 bstrncpy(fs, "unknown", sizeof(fs));
336 Jmsg(jcr, M_INFO, 0, _("Top level directory \"%s\" has unlisted fstype \"%s\"\n"), fname, fs);
337 return 1; /* Just ignore this error - or the whole backup is cancelled */
339 if (!accept_drivetype(ff_pkt, NULL)) {
340 ff_pkt->type = FT_INVALIDDT;
341 if (ff_pkt->flags & FO_KEEPATIME) {
342 utime(fname, &restore_times);
347 if (!drivetype(ff_pkt->fname, dt, sizeof(dt))) {
348 bstrncpy(dt, "unknown", sizeof(dt));
351 Jmsg(jcr, M_INFO, 0, _("Top level directory \"%s\" has an unlisted drive type \"%s\"\n"), fname, dt);
352 return 1; /* Just ignore this error - or the whole backup is cancelled */
354 ff_pkt->volhas_attrlist = volume_has_attrlist(fname);
357 * If this is an Incremental backup, see if file was modified
358 * since our last "save_time", presumably the last Full save
361 if ( ff_pkt->incremental
362 && !S_ISDIR(ff_pkt->statp.st_mode)
363 && !check_changes(jcr, ff_pkt))
365 Dmsg1(500, "Non-directory incremental: %s\n", ff_pkt->fname);
366 ff_pkt->type = FT_NOCHG;
367 return handle_file(jcr, ff_pkt, top_level);
370 #ifdef HAVE_DARWIN_OS
371 if (ff_pkt->flags & FO_HFSPLUS && ff_pkt->volhas_attrlist
372 && S_ISREG(ff_pkt->statp.st_mode)) {
373 /* TODO: initialise attrList once elsewhere? */
374 struct attrlist attrList;
375 memset(&attrList, 0, sizeof(attrList));
376 attrList.bitmapcount = ATTR_BIT_MAP_COUNT;
377 attrList.commonattr = ATTR_CMN_FNDRINFO;
378 attrList.fileattr = ATTR_FILE_RSRCLENGTH;
379 if (getattrlist(fname, &attrList, &ff_pkt->hfsinfo,
380 sizeof(ff_pkt->hfsinfo), FSOPT_NOFOLLOW) != 0) {
381 ff_pkt->type = FT_NOSTAT;
382 ff_pkt->ff_errno = errno;
383 return handle_file(jcr, ff_pkt, top_level);
390 * Handle hard linked files
392 * Maintain a list of hard linked files already backed up. This
393 * allows us to ensure that the data of each file gets backed
396 if (!(ff_pkt->flags & FO_NO_HARDLINK)
397 && ff_pkt->statp.st_nlink > 1
398 && (S_ISREG(ff_pkt->statp.st_mode)
399 || S_ISCHR(ff_pkt->statp.st_mode)
400 || S_ISBLK(ff_pkt->statp.st_mode)
401 || S_ISFIFO(ff_pkt->statp.st_mode)
402 || S_ISSOCK(ff_pkt->statp.st_mode))) {
405 if (ff_pkt->linkhash == NULL) {
406 ff_pkt->linkhash = (link_t **)bmalloc(LINK_HASHTABLE_SIZE * sizeof(link_t *));
407 memset(ff_pkt->linkhash, 0, LINK_HASHTABLE_SIZE * sizeof(link_t *));
409 const int linkhash = LINKHASH(ff_pkt->statp);
411 /* Search link list of hard linked files */
412 for (lp = ff_pkt->linkhash[linkhash]; lp; lp = lp->next)
413 if (lp->ino == (ino_t)ff_pkt->statp.st_ino &&
414 lp->dev == (dev_t)ff_pkt->statp.st_dev) {
415 /* If we have already backed up the hard linked file don't do it again */
416 if (strcmp(lp->name, fname) == 0) {
417 Dmsg2(400, "== Name identical skip FI=%d file=%s\n", lp->FileIndex, fname);
418 return 1; /* ignore */
420 ff_pkt->link = lp->name;
421 ff_pkt->type = FT_LNKSAVED; /* Handle link, file already saved */
422 ff_pkt->LinkFI = lp->FileIndex;
424 rtn_stat = handle_file(jcr, ff_pkt, top_level);
425 Dmsg3(400, "FT_LNKSAVED FI=%d LinkFI=%d file=%s\n",
426 ff_pkt->FileIndex, lp->FileIndex, lp->name);
430 /* File not previously dumped. Chain it into our list. */
431 len = strlen(fname) + 1;
432 lp = (struct f_link *)bmalloc(sizeof(struct f_link) + len);
433 lp->ino = ff_pkt->statp.st_ino;
434 lp->dev = ff_pkt->statp.st_dev;
435 lp->FileIndex = 0; /* set later */
436 bstrncpy(lp->name, fname, len);
437 lp->next = ff_pkt->linkhash[linkhash];
438 ff_pkt->linkhash[linkhash] = lp;
439 ff_pkt->linked = lp; /* mark saved link */
440 Dmsg2(400, "added to hash FI=%d file=%s\n", ff_pkt->FileIndex, lp->name);
442 ff_pkt->linked = NULL;
445 /* This is not a link to a previously dumped file, so dump it. */
446 if (S_ISREG(ff_pkt->statp.st_mode)) {
449 sizeleft = ff_pkt->statp.st_size;
451 /* Don't bother opening empty, world readable files. Also do not open
452 files when archive is meant for /dev/null. */
453 if (ff_pkt->null_output_device || (sizeleft == 0
454 && MODE_RALL == (MODE_RALL & ff_pkt->statp.st_mode))) {
455 ff_pkt->type = FT_REGE;
457 ff_pkt->type = FT_REG;
459 rtn_stat = handle_file(jcr, ff_pkt, top_level);
460 if (ff_pkt->linked) {
461 ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
463 Dmsg3(400, "FT_REG FI=%d linked=%d file=%s\n", ff_pkt->FileIndex,
464 ff_pkt->linked ? 1 : 0, fname);
465 if (ff_pkt->flags & FO_KEEPATIME) {
466 utime(fname, &restore_times);
471 } else if (S_ISLNK(ff_pkt->statp.st_mode)) { /* soft link */
473 char *buffer = (char *)alloca(path_max + name_max + 102);
475 size = readlink(fname, buffer, path_max + name_max + 101);
477 /* Could not follow link */
478 ff_pkt->type = FT_NOFOLLOW;
479 ff_pkt->ff_errno = errno;
480 rtn_stat = handle_file(jcr, ff_pkt, top_level);
481 if (ff_pkt->linked) {
482 ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
487 ff_pkt->link = buffer; /* point to link */
488 ff_pkt->type = FT_LNK; /* got a real link */
489 rtn_stat = handle_file(jcr, ff_pkt, top_level);
490 if (ff_pkt->linked) {
491 ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
495 } else if (S_ISDIR(ff_pkt->statp.st_mode)) {
497 struct dirent *entry, *result;
502 dev_t our_device = ff_pkt->statp.st_dev;
504 bool volhas_attrlist = ff_pkt->volhas_attrlist; /* Remember this if we recurse */
507 * If we are using Win32 (non-portable) backup API, don't check
508 * access as everything is more complicated, and
509 * in principle, we should be able to access everything.
511 if (!have_win32_api() || (ff_pkt->flags & FO_PORTABLE)) {
512 if (access(fname, R_OK) == -1 && geteuid() != 0) {
513 /* Could not access() directory */
514 ff_pkt->type = FT_NOACCESS;
515 ff_pkt->ff_errno = errno;
516 rtn_stat = handle_file(jcr, ff_pkt, top_level);
517 if (ff_pkt->linked) {
518 ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
525 * Ignore this directory and everything below if the file .nobackup
526 * (or what is defined for IgnoreDir in this fileset) exists
528 if (ff_pkt->ignoredir != NULL) {
530 char fname[MAXPATHLEN];
532 if (strlen(ff_pkt->fname) + strlen("/") +
533 strlen(ff_pkt->ignoredir) + 1 > MAXPATHLEN)
534 return 1; /* Is this wisdom? */
536 strcpy(fname, ff_pkt->fname);
538 strcat(fname, ff_pkt->ignoredir);
539 if (stat(fname, &sb) == 0) {
540 Dmsg2(100, "Directory '%s' ignored (found %s)\n",
541 ff_pkt->fname, ff_pkt->ignoredir);
542 return 1; /* Just ignore this directory */
546 /* Build a canonical directory name with a trailing slash in link var */
548 link_len = len + 200;
549 link = (char *)bmalloc(link_len + 2);
550 bstrncpy(link, fname, link_len);
551 /* Strip all trailing slashes */
552 while (len >= 1 && IsPathSeparator(link[len - 1]))
554 link[len++] = '/'; /* add back one */
558 if (ff_pkt->incremental && !check_changes(jcr, ff_pkt)) {
559 /* Incremental option, directory entry not changed */
560 ff_pkt->type = FT_DIRNOCHG;
562 ff_pkt->type = FT_DIRBEGIN;
565 * We have set st_rdev to 1 if it is a reparse point, otherwise 0,
566 * if st_rdev is 2, it is a mount point
568 #if defined(HAVE_WIN32)
569 if (ff_pkt->statp.st_rdev == WIN32_REPARSE_POINT) {
570 ff_pkt->type = FT_REPARSE;
574 * Note, we return the directory to the calling program (handle_file)
575 * when we first see the directory (FT_DIRBEGIN.
576 * This allows the program to apply matches and make a
577 * choice whether or not to accept it. If it is accepted, we
578 * do not immediately save it, but do so only after everything
579 * in the directory is seen (i.e. the FT_DIREND).
581 rtn_stat = handle_file(jcr, ff_pkt, top_level);
582 if (rtn_stat < 1 || ff_pkt->type == FT_REPARSE) { /* ignore or error status */
586 /* Done with DIRBEGIN, next call will be DIREND */
587 if (ff_pkt->type == FT_DIRBEGIN) {
588 ff_pkt->type = FT_DIREND;
592 * Create a temporary ff packet for this directory
593 * entry, and defer handling the directory until
594 * we have recursed into it. This saves the
595 * directory after all files have been processed, and
596 * during the restore, the directory permissions will
597 * be reset after all the files have been restored.
599 Dmsg1(300, "Create temp ff packet for dir: %s\n", ff_pkt->fname);
600 FF_PKT *dir_ff_pkt = new_dir_ff_pkt(ff_pkt);
603 * Do not descend into subdirectories (recurse) if the
604 * user has turned it off for this directory.
606 * If we are crossing file systems, we are either not allowed
607 * to cross, or we may be restricted by a list of permitted
610 bool is_win32_mount_point = false;
611 #if defined(HAVE_WIN32)
612 is_win32_mount_point = ff_pkt->statp.st_rdev == WIN32_MOUNT_POINT;
614 if (!top_level && ff_pkt->flags & FO_NO_RECURSION) {
615 ff_pkt->type = FT_NORECURSE;
617 } else if (!top_level && (parent_device != ff_pkt->statp.st_dev ||
618 is_win32_mount_point)) {
619 if(!(ff_pkt->flags & FO_MULTIFS)) {
620 ff_pkt->type = FT_NOFSCHG;
622 } else if (!accept_fstype(ff_pkt, NULL)) {
623 ff_pkt->type = FT_INVALIDFS;
626 ff_pkt->volhas_attrlist = volume_has_attrlist(fname);
629 /* If not recursing, just backup dir and return */
631 rtn_stat = handle_file(jcr, ff_pkt, top_level);
632 if (ff_pkt->linked) {
633 ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
636 free_dir_ff_pkt(dir_ff_pkt);
637 ff_pkt->link = ff_pkt->fname; /* reset "link" */
638 if (ff_pkt->flags & FO_KEEPATIME) {
639 utime(fname, &restore_times);
644 ff_pkt->link = ff_pkt->fname; /* reset "link" */
647 * Descend into or "recurse" into the directory to read
648 * all the files in it.
651 if ((directory = opendir(fname)) == NULL) {
652 ff_pkt->type = FT_NOOPEN;
653 ff_pkt->ff_errno = errno;
654 rtn_stat = handle_file(jcr, ff_pkt, top_level);
655 if (ff_pkt->linked) {
656 ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
659 free_dir_ff_pkt(dir_ff_pkt);
664 * Process all files in this directory entry (recursing).
665 * This would possibly run faster if we chdir to the directory
666 * before traversing it.
669 entry = (struct dirent *)malloc(sizeof(struct dirent) + name_max + 100);
670 for ( ; !job_canceled(jcr); ) {
674 status = readdir_r(directory, entry, &result);
675 if (status != 0 || result == NULL) {
676 // Dmsg2(99, "readdir returned stat=%d result=0x%x\n",
677 // status, (long)result);
680 ASSERT(name_max+1 > (int)sizeof(struct dirent) + (int)NAMELEN(entry));
682 /* Skip `.', `..', and excluded file names. */
683 if (p[0] == '\0' || (p[0] == '.' && (p[1] == '\0' ||
684 (p[1] == '.' && p[2] == '\0')))) {
688 if ((int)NAMELEN(entry) + len >= link_len) {
689 link_len = len + NAMELEN(entry) + 1;
690 link = (char *)brealloc(link, link_len + 1);
693 for (i=0; i < (int)NAMELEN(entry); i++) {
697 if (!file_is_excluded(ff_pkt, link)) {
698 rtn_stat = find_one_file(jcr, ff_pkt, handle_file, link, our_device, false);
699 if (ff_pkt->linked) {
700 ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
709 * Now that we have recursed through all the files in the
710 * directory, we "save" the directory so that after all
711 * the files are restored, this entry will serve to reset
712 * the directory modes and dates. Temp directory values
713 * were used without this record.
715 handle_file(jcr, dir_ff_pkt, top_level); /* handle directory entry */
716 if (ff_pkt->linked) {
717 ff_pkt->linked->FileIndex = dir_ff_pkt->FileIndex;
719 free_dir_ff_pkt(dir_ff_pkt);
721 if (ff_pkt->flags & FO_KEEPATIME) {
722 utime(fname, &restore_times);
724 ff_pkt->volhas_attrlist = volhas_attrlist; /* Restore value in case it changed. */
726 } /* end check for directory */
729 * If it is explicitly mentioned (i.e. top_level) and is
730 * a block device, we do a raw backup of it or if it is
731 * a fifo, we simply read it.
733 #ifdef HAVE_FREEBSD_OS
735 * On FreeBSD, all block devices are character devices, so
736 * to be able to read a raw disk, we need the check for
737 * a character device.
738 * crw-r----- 1 root operator - 116, 0x00040002 Jun 9 19:32 /dev/ad0s3
739 * crw-r----- 1 root operator - 116, 0x00040002 Jun 9 19:32 /dev/rad0s3
741 if (top_level && (S_ISBLK(ff_pkt->statp.st_mode) || S_ISCHR(ff_pkt->statp.st_mode))) {
743 if (top_level && S_ISBLK(ff_pkt->statp.st_mode)) {
745 ff_pkt->type = FT_RAW; /* raw partition */
746 } else if (top_level && S_ISFIFO(ff_pkt->statp.st_mode) &&
747 ff_pkt->flags & FO_READFIFO) {
748 ff_pkt->type = FT_FIFO;
750 /* The only remaining types are special (character, ...) files */
751 ff_pkt->type = FT_SPEC;
753 rtn_stat = handle_file(jcr, ff_pkt, top_level);
754 if (ff_pkt->linked) {
755 ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
760 int term_find_one(FF_PKT *ff)
762 struct f_link *lp, *lc;
767 if (ff->linkhash == NULL) return 0;
769 for (i =0 ; i < LINK_HASHTABLE_SIZE; i ++) {
770 /* Free up list of hard linked files */
771 lp = ff->linkhash[i];
780 ff->linkhash[i] = NULL;