diff --git a/lib/libufs/Makefile b/lib/libufs/Makefile --- a/lib/libufs/Makefile +++ b/lib/libufs/Makefile @@ -20,6 +20,8 @@ MLINKS+= getinode.3 putinode.3 MLINKS+= sbread.3 sbwrite.3 MLINKS+= sbread.3 sbget.3 +MLINKS+= sbread.3 sbsearch.3 +MLINKS+= sbread.3 sbfind.3 MLINKS+= sbread.3 sbput.3 MLINKS+= ufs_disk_close.3 ufs_disk_fillout.3 MLINKS+= ufs_disk_close.3 ufs_disk_fillout_blank.3 diff --git a/lib/libufs/libufs.h b/lib/libufs/libufs.h --- a/lib/libufs/libufs.h +++ b/lib/libufs/libufs.h @@ -111,6 +111,8 @@ void ffs_fragacct(struct fs *, int, int32_t [], int); int ffs_isblock(struct fs *, u_char *, ufs1_daddr_t); int ffs_isfreeblock(struct fs *, u_char *, ufs1_daddr_t); +int ffs_sbsearch(void *, struct fs **, int, char *, + int (*)(void *, off_t, void **, int)); void ffs_setblock(struct fs *, u_char *, ufs1_daddr_t); int ffs_sbget(void *, struct fs **, off_t, int, char *, int (*)(void *, off_t, void **, int)); @@ -149,9 +151,11 @@ * sblock.c */ int sbread(struct uufsd *); +int sbfind(struct uufsd *, int); int sbwrite(struct uufsd *, int); /* low level superblock read/write functions */ int sbget(int, struct fs **, off_t, int); +int sbsearch(int, struct fs **, int); int sbput(int, struct fs *, int); /* diff --git a/lib/libufs/sblock.c b/lib/libufs/sblock.c --- a/lib/libufs/sblock.c +++ b/lib/libufs/sblock.c @@ -73,6 +73,30 @@ return (handle_disk_read(disk, fs, error)); } +/* + * Make an extensive search to find a superblock. If the superblock + * in the standard place cannot be used, try looking for one of the + * backup superblocks. + * + * The flags parameter is made up of the following or'ed together options: + * + * UFS_NOMSG indicates that superblock inconsistency error messages + * should not be printed. + * + * UFS_NOCSUM causes only the superblock itself to be returned, but does + * not read in any auxillary data structures like the cylinder group + * summary information. + */ +int +sbfind(struct uufsd *disk, int flags) +{ + struct fs *fs; + int error; + + error = sbsearch(disk->d_fd, &fs, flags); + return (handle_disk_read(disk, fs, error)); +} + static int handle_disk_read(struct uufsd *disk, struct fs *fs, int error) { @@ -174,8 +198,27 @@ int sbget(int devfd, struct fs **fsp, off_t sblockloc, int flags) { + int error; + + error = ffs_sbget(&devfd, fsp, sblockloc, flags, "user", use_pread); + fflush(NULL); /* flush any messages */ + return (error); +} + +/* + * Make an extensive search of the devfd device to find a superblock. + * If the superblock in the standard place cannot be used, try looking + * for one of the backup superblocks. If found, memory is allocated and + * returned in fsp. + */ +int +sbsearch(int devfd, struct fs **fsp, int flags) +{ + int error; - return (ffs_sbget(&devfd, fsp, sblockloc, flags, "user", use_pread)); + error = ffs_sbsearch(&devfd, fsp, flags, "user", use_pread); + fflush(NULL); /* flush any messages */ + return (error); } /* @@ -209,11 +252,10 @@ off_t savedactualloc; int i, error; - if ((error = ffs_sbput(&devfd, fs, fs->fs_sblockactualloc, - use_pwrite)) != 0) + error = ffs_sbput(&devfd, fs, fs->fs_sblockactualloc, use_pwrite); + fflush(NULL); /* flush any messages */ + if (error != 0 || numaltwrite == 0) return (error); - if (numaltwrite == 0) - return (0); savedactualloc = fs->fs_sblockactualloc; if (fs->fs_si != NULL) { savedcsp = fs->fs_csp; @@ -223,6 +265,7 @@ fs->fs_sblockactualloc = dbtob(fsbtodb(fs, cgsblock(fs, i))); if ((error = ffs_sbput(&devfd, fs, fs->fs_sblockactualloc, use_pwrite)) != 0) { + fflush(NULL); /* flush any messages */ fs->fs_sblockactualloc = savedactualloc; fs->fs_csp = savedcsp; return (error); @@ -231,6 +274,7 @@ fs->fs_sblockactualloc = savedactualloc; if (fs->fs_si != NULL) fs->fs_csp = savedcsp; + fflush(NULL); /* flush any messages */ return (0); } diff --git a/lib/libufs/sbread.3 b/lib/libufs/sbread.3 --- a/lib/libufs/sbread.3 +++ b/lib/libufs/sbread.3 @@ -3,19 +3,21 @@ .\" Description: .\" Manual page for libufs functions: .\" sbget(3) +.\" sbsearch(3) .\" sbput(3) .\" sbread(3) +.\" sbfind(3) .\" sbwrite(3) .\" .\" This file is in the public domain. .\" .\" $FreeBSD$ .\" -.Dd September 2, 2020 +.Dd August 8, 2022 .Dt SBREAD 3 .Os .Sh NAME -.Nm sbget , sbput , sbread , sbwrite +.Nm sbget , sbsearch , sbput , sbread , sbfind , sbwrite .Nd read and write superblocks of a UFS file system .Sh LIBRARY .Lb libufs @@ -29,16 +31,22 @@ .Ft int .Fn sbget "int devfd" "struct fs **fsp" "off_t sblockloc" "int flags" .Ft int +.Fn sbsearch "int devfd" "struct fs **fsp" "int flags" +.Ft int .Fn sbput "int devfd" "struct fs *fs" "int numaltwrite" .Ft int .Fn sbread "struct uufsd *disk" .Ft int +.Fn sbfind "struct uufsd *disk" "int flags" +.Ft int .Fn sbwrite "struct uufsd *disk" "int all" .Sh DESCRIPTION The -.Fn sbget +.Fn sbget , +.Fn sbsearch , +.Fn sbread , and -.Fn sbread +.Fn sbfind functions provide superblock reads for .Xr libufs 3 consumers. @@ -52,7 +60,9 @@ .Pp The .Fn sbget -function first allocates a buffer to hold the superblock. +and +.Fn sbsearch +functions first allocate a buffer to hold the superblock. Using the .Va devfd file descriptor that references the filesystem disk, @@ -65,26 +75,38 @@ may be specified for .Va sblockloc to request that the standard location for the superblock be read. +The +.Fn sbsearch +function uses the +.Va devfd +file descriptor that references the filesystem disk, +to search first for the superblock at the standard location. +If it is not found or is too damaged to use +.Fn sbsearch +will attempt to find one of the filesystem's alternate superblocks. Flags are specified by .Em or Ns 'ing the following values: .Pp -.Bl -tag -width UFS_NOHASHFAIL -.It Cm UFS_NOHASHFAIL -Will note if the check hash is wrong but will still return the superblock. +.Bl -tag -width UFS_NOCSUM +.It Cm UFS_NOCSUM +Causes only the superblock itself to be returned, but does not read in any +auxiliary data structures like the cylinder group summary information. .It Cm UFS_NOMSG Indicates that superblock inconsistency error messages should not be printed. -.It Cm UFS_NOCSUM -Causes only the superblock itself to be returned, but does not read in any auxiliary data structures like the cylinder group summary information. .El .Pp If successful, .Fn sbget -returns a pointer to the buffer containing the superblock in +and +.Fn sbsearch +functions return a pointer to the buffer containing the superblock in .Va fsp . The .Fn sbget -function is safe to use in threaded applications. +and +.Fn sbsearch +functions are safe to use in threaded applications. .Pp The .Fn sbput @@ -113,7 +135,19 @@ .Pp The .Fn sbread -function reads the standard filesystem superblock into the +function reads the standard filesystem superblock. +The +.Fn sbfind +function tries to find a usable superblock. +It searchs first for the superblock at the standard location. +If it is not found or is too damaged to use +.Fn sbfind +will attempt to find one of the filesystem's alternate superblocks. +If successful +.Fn sbread +and +.Fn sbfind +return a superblock in the .Va d_sb , structure embedded in the given user-land UFS disk structure. .Pp @@ -131,16 +165,19 @@ .Sh RETURN VALUES .Rv -std sbread sbwrite The -.Fn sbget +.Fn sbget , +.Fn sbsearch , and .Fn sbput functions return the value 0 if successful; otherwise they return one of the errors described below. .Sh ERRORS The errors returned by -.Fn sbget +.Fn sbget , +.Fn sbsearch , +.Fn sbread , and -.Fn sbread +.Fn sbfind , include any of the errors specified for the library function .Xr bread 3 . Additionally, they may follow the diff --git a/sbin/dumpfs/dumpfs.c b/sbin/dumpfs/dumpfs.c --- a/sbin/dumpfs/dumpfs.c +++ b/sbin/dumpfs/dumpfs.c @@ -130,13 +130,8 @@ usage(); while ((name = *argv++) != NULL) { - if (ufs_disk_fillout_blank(&disk, name) == -1) { - ufserr(name); - eval |= 1; - continue; - } - disk.d_lookupflags |= UFS_NOHASHFAIL; - if (sbread(&disk) == -1) { + if (ufs_disk_fillout_blank(&disk, name) == -1 || + sbfind(&disk, 0) == -1) { ufserr(name); eval |= 1; continue; diff --git a/sbin/ffsinfo/ffsinfo.c b/sbin/ffsinfo/ffsinfo.c --- a/sbin/ffsinfo/ffsinfo.c +++ b/sbin/ffsinfo/ffsinfo.c @@ -223,8 +223,9 @@ device = special; } - if (ufs_disk_fillout(&disk, device) == -1) - err(1, "ufs_disk_fillout(%s) failed: %s", device, disk.d_error); + if (ufs_disk_fillout_blank(&disk, device) == -1 || + sbfind(&disk, 0) == -1) + err(1, "superblock fetch(%s) failed: %s", device, disk.d_error); DBG_OPEN(out_file); /* already here we need a superblock */ diff --git a/sbin/fsck_ffs/fsck.h b/sbin/fsck_ffs/fsck.h --- a/sbin/fsck_ffs/fsck.h +++ b/sbin/fsck_ffs/fsck.h @@ -363,7 +363,6 @@ extern char rerun; /* rerun fsck. Only used in non-preen mode */ extern char resolved; /* cleared if unresolved changes => not clean */ extern int returntosingle; /* 1 => return to single user mode on exit */ -extern int sbhashfailed; /* when reading superblock check hash failed */ extern long secsize; /* actual disk sector size */ extern char skipclean; /* skip clean file systems if preening */ extern char snapname[BUFSIZ]; /* when doing snapshots, the name of the file */ @@ -502,7 +501,7 @@ void propagate(void); void prtinode(struct inode *); void pwarn(const char *fmt, ...) __printflike(1, 2); -int readsb(int listerr); +int readsb(void); int reply(const char *question); void rwerror(const char *mesg, ufs2_daddr_t blk); void sblock_init(void); diff --git a/sbin/fsck_ffs/globs.c b/sbin/fsck_ffs/globs.c --- a/sbin/fsck_ffs/globs.c +++ b/sbin/fsck_ffs/globs.c @@ -96,7 +96,6 @@ char rerun; /* rerun fsck. Only used in non-preen mode */ int returntosingle; /* 1 => return to single user mode on exit */ char resolved; /* cleared if unresolved changes => not clean */ -int sbhashfailed; /* when reading superblock check hash failed */ char havesb; /* superblock has been read */ char skipclean; /* skip clean file systems if preening */ int fsmodified; /* 1 => write done to file system */ @@ -156,7 +155,6 @@ resolved = 0; havesb = 0; fsmodified = 0; - sbhashfailed = 0; fsreadfd = -1; fswritefd = -1; maxfsblock = 0; diff --git a/sbin/fsck_ffs/main.c b/sbin/fsck_ffs/main.c --- a/sbin/fsck_ffs/main.c +++ b/sbin/fsck_ffs/main.c @@ -271,7 +271,7 @@ */ sblock_init(); sbreadfailed = 0; - if (openfilesys(filesys) == 0 || readsb(0) == 0) + if (openfilesys(filesys) == 0 || readsb() == 0) sbreadfailed = 1; if (bkgrdcheck) { if (sbreadfailed) diff --git a/sbin/fsck_ffs/setup.c b/sbin/fsck_ffs/setup.c --- a/sbin/fsck_ffs/setup.c +++ b/sbin/fsck_ffs/setup.c @@ -60,6 +60,7 @@ struct inoinfo **inphead, **inpsort; /* info about all inodes */ +static int sbhashfailed; #define POWEROF2(num) (((num) & ((num) - 1)) == 0) static int calcsb(char *dev, int devfd, struct fs *fs); @@ -74,39 +75,15 @@ int setup(char *dev) { - long cg, bmapsize; - struct fs proto; + long bmapsize; /* - * We are expected to have an open file descriptor + * We are expected to have an open file descriptor and a superblock. */ - if (fsreadfd < 0) + if (fsreadfd < 0 || havesb == 0) { + if (debug) + printf("setup: bad fsreadfd or missing superblock\n"); return (0); - /* - * If we do not yet have a superblock, read it in looking - * for alternates if necessary. - */ - if (havesb == 0 && readsb(1) == 0) { - skipclean = 0; - if (bflag || preen || calcsb(dev, fsreadfd, &proto) == 0) - return(0); - if (reply("LOOK FOR ALTERNATE SUPERBLOCKS") == 0) - return (0); - for (cg = 0; cg < proto.fs_ncg; cg++) { - bflag = fsbtodb(&proto, cgsblock(&proto, cg)); - if (readsb(0) != 0) - break; - } - if (cg >= proto.fs_ncg) { - printf("SEARCH FOR ALTERNATE SUPER-BLOCK FAILED. " - "YOU MUST USE THE\n-b OPTION TO FSCK TO SPECIFY " - "THE LOCATION OF AN ALTERNATE\nSUPER-BLOCK TO " - "SUPPLY NEEDED INFORMATION; SEE fsck_ffs(8).\n"); - bflag = 0; - return(0); - } - pwarn("USING ALTERNATE SUPERBLOCK AT %jd\n", bflag); - bflag = 0; } if (preen == 0) printf("** %s", dev); @@ -243,40 +220,64 @@ * Read in the super block and its summary info. */ int -readsb(int listerr) +readsb(void) { - off_t super; - int ret, flags; struct fs *fs; - super = bflag ? bflag * dev_bsize : UFS_STDSB; - flags = sbhashfailed ? UFS_NOHASHFAIL | UFS_NOMSG : UFS_NOMSG; + sbhashfailed = 0; readcnt[sblk.b_type]++; - while ((ret = sbget(fsreadfd, &fs, super, flags)) != 0) { - switch (ret) { + /* + * If bflag is given, then check just that superblock. + */ + if (bflag) { + switch (sbget(fsreadfd, &fs, bflag * dev_bsize, UFS_NOMSG)) { + case 0: + goto goodsb; case EINTEGRITY: - if (bflag || (super == UFS_STDSB && - flags == (UFS_NOHASHFAIL | UFS_NOMSG))) - return (0); - super = UFS_STDSB; - flags = UFS_NOHASHFAIL | UFS_NOMSG; - sbhashfailed = 1; - continue; + printf("Check hash failed for superblock at %jd\n", + bflag); + return (0); case ENOENT: - if (bflag) - printf("%jd is not a file system " - "superblock\n", super / dev_bsize); - else - printf("Cannot find file system " - "superblock\n"); + printf("%jd is not a file system superblock\n", bflag); return (0); case EIO: default: - printf("I/O error reading %jd\n", - super / dev_bsize); + printf("I/O error reading %jd\n", bflag); return (0); } } + /* + * Check for the standard superblock and use it if good. + */ + if (sbget(fsreadfd, &fs, UFS_STDSB, UFS_NOMSG) == 0) + goto goodsb; + /* + * Check if the only problem is a check-hash failure. + */ + skipclean = 0; + if (sbget(fsreadfd, &fs, UFS_STDSB, UFS_NOMSG | UFS_NOHASHFAIL) == 0) { + sbhashfailed = 1; + goto goodsb; + } + /* + * Do an exhaustive search for a usable superblock. + */ + switch (sbsearch(fsreadfd, &fs, 0)) { + case 0: + goto goodsb; + case ENOENT: + printf("SEARCH FOR ALTERNATE SUPER-BLOCK FAILED. " + "YOU MUST USE THE\n-b OPTION TO FSCK TO SPECIFY " + "THE LOCATION OF AN ALTERNATE\nSUPER-BLOCK TO " + "SUPPLY NEEDED INFORMATION; SEE fsck_ffs(8).\n"); + return (0); + case EIO: + default: + printf("I/O error reading a usable superblock\n"); + return (0); + } + +goodsb: memcpy(&sblock, fs, fs->fs_sbsize); free(fs); /* diff --git a/sbin/fsdb/fsdb.c b/sbin/fsdb/fsdb.c --- a/sbin/fsdb/fsdb.c +++ b/sbin/fsdb/fsdb.c @@ -111,7 +111,7 @@ fsys = argv[0]; sblock_init(); - if (openfilesys(fsys) == 0 || readsb(0) == 0 || setup(fsys) == 0) + if (openfilesys(fsys) == 0 || readsb() == 0 || setup(fsys) == 0) errx(1, "cannot set up file system `%s'", fsys); if (fswritefd < 0) nflag++; diff --git a/stand/libsa/ufs.c b/stand/libsa/ufs.c --- a/stand/libsa/ufs.c +++ b/stand/libsa/ufs.c @@ -151,7 +151,7 @@ static int ufs_use_sa_read(void *, off_t, void **, int); /* from ffs_subr.c */ -int ffs_sbget(void *, struct fs **, off_t, int, char *, +int ffs_sbsearch(void *, struct fs **, int, char *, int (*)(void *, off_t, void **, int)); /* @@ -529,8 +529,8 @@ if (mnt == NULL) { /* read super block */ twiddle(1); - if ((rc = ffs_sbget(f, &fs, UFS_STDSB, UFS_NOHASHFAIL, "stand", - ufs_use_sa_read)) != 0) { + if ((rc = ffs_sbsearch(f, &fs, 0, "stand", ufs_use_sa_read)) + != 0) { goto out; } } else { diff --git a/sys/ufs/ffs/ffs_extern.h b/sys/ufs/ffs/ffs_extern.h --- a/sys/ufs/ffs/ffs_extern.h +++ b/sys/ufs/ffs/ffs_extern.h @@ -86,6 +86,8 @@ int ffs_isfreeblock(struct fs *, u_char *, ufs1_daddr_t); void ffs_oldfscompat_write(struct fs *, struct ufsmount *); int ffs_own_mount(const struct mount *mp); +int ffs_sbsearch(void *, struct fs **, int, struct malloc_type *, + int (*)(void *, off_t, void **, int)); int ffs_reallocblks(struct vop_reallocblks_args *); int ffs_realloccg(struct inode *, ufs2_daddr_t, ufs2_daddr_t, ufs2_daddr_t, int, int, int, struct ucred *, struct buf **); diff --git a/sys/ufs/ffs/ffs_subr.c b/sys/ufs/ffs/ffs_subr.c --- a/sys/ufs/ffs/ffs_subr.c +++ b/sys/ufs/ffs/ffs_subr.c @@ -378,6 +378,40 @@ prtmsg = ((flags & UFS_NOMSG) == 0); warnerr = (flags & UFS_NOWARNFAIL) == UFS_NOWARNFAIL ? 0 : ENOENT; wmsg = warnerr ? "" : " (Ignored)"; + /* + * If just validating for recovery, then do just the minimal + * checks needed for the superblock fields needed to find + * alternate superblocks. + */ + if ((flags & UFS_FSRONLY) == UFS_FSRONLY && + (fs->fs_magic == FS_UFS1_MAGIC || fs->fs_magic == FS_UFS2_MAGIC)) { + if (fs->fs_magic == FS_UFS2_MAGIC) { + FCHK(fs->fs_sblockloc, !=, SBLOCK_UFS2, %#jx); + } else if (fs->fs_magic == FS_UFS1_MAGIC) { + FCHK(fs->fs_sblockloc, <, 0, %jd); + FCHK(fs->fs_sblockloc, >, SBLOCK_UFS1, %jd); + } + FCHK(fs->fs_frag, <, 1, %jd); + FCHK(fs->fs_frag, >, MAXFRAG, %jd); + FCHK(fs->fs_bsize, <, MINBSIZE, %jd); + FCHK(fs->fs_bsize, >, MAXBSIZE, %jd); + FCHK(fs->fs_bsize, <, roundup(sizeof(struct fs), DEV_BSIZE), + %jd); + FCHK(fs->fs_fsize, <, sectorsize, %jd); + FCHK(fs->fs_fsize * fs->fs_frag, !=, fs->fs_bsize, %jd); + FCHK(powerof2(fs->fs_fsize), ==, 0, %jd); + FCHK(fs->fs_fpg, <, 3 * fs->fs_frag, %jd); + FCHK(fs->fs_ncg, <, 1, %jd); + FCHK(fs->fs_fsbtodb, !=, ILOG2(fs->fs_fsize / sectorsize), %jd); + FCHK(fs->fs_old_cgoffset, <, 0, %jd); + FCHK2(fs->fs_old_cgoffset, >, 0, ~fs->fs_old_cgmask, <, 0, %jd); + FCHK(fs->fs_old_cgoffset * (~fs->fs_old_cgmask), >, fs->fs_fpg, + %jd); + FCHK(fs->fs_sblkno, !=, roundup( + howmany(fs->fs_sblockloc + SBLOCKSIZE, fs->fs_fsize), + fs->fs_frag), %jd); + return (error); + } if (fs->fs_magic == FS_UFS2_MAGIC) { if ((flags & UFS_ALTSBLK) == 0) FCHK2(fs->fs_sblockactualloc, !=, SBLOCK_UFS2, @@ -530,6 +564,146 @@ return (error); } +/* + * Make an extensive search to find a superblock. If the superblock + * in the standard place cannot be used, try looking for one of the + * backup superblocks. + * + * Flags are made up of the following or'ed together options: + * + * UFS_NOMSG indicates that superblock inconsistency error messages + * should not be printed. + * + * UFS_NOCSUM causes only the superblock itself to be returned, but does + * not read in any auxillary data structures like the cylinder group + * summary information. + */ +int +ffs_sbsearch(void *devfd, struct fs **fsp, int reqflags, + struct malloc_type *filltype, + int (*readfunc)(void *devfd, off_t loc, void **bufp, int size)) +{ + struct fsrecovery *fsr; + struct fs *protofs; + void *fsrbuf; + char *cp; + long nocsum, flags, msg, cg; + off_t sblk, secsize; + int error; + + msg = (reqflags & UFS_NOMSG) == 0; + nocsum = reqflags & UFS_NOCSUM; + /* + * Try normal superblock read and return it if it works. + * + * Suppress messages if it fails until we find out if + * failure can be avoided. + */ + flags = UFS_NOMSG | nocsum; + if (ffs_sbget(devfd, fsp, UFS_STDSB, flags, filltype, readfunc) == 0) + return (0); + /* + * First try: ignoring hash failures. + */ + flags |= UFS_NOHASHFAIL; + if (msg) + flags &= ~UFS_NOMSG; + if (ffs_sbget(devfd, fsp, UFS_STDSB, flags, filltype, readfunc) == 0) + return (0); + /* + * Next up is to check if fields of the superblock that are + * needed to find backup superblocks are usable. + */ + if (msg) + printf("Attempted recovery for standard superblock: failed\n"); + flags = UFS_FSRONLY | UFS_NOHASHFAIL | UFS_NOMSG; + if (ffs_sbget(devfd, &protofs, UFS_STDSB, flags, filltype, + readfunc) == 0) { + if (msg) + printf("Attempted extraction of recovery data from " + "standard superblock: "); + } else { + /* + * Final desperation is to see if alternate superblock + * parameters have been saved in the boot area. + */ + if (msg) + printf("Attempted extraction of recovery data from " + "standard superblock: failed\nAttempt to find " + "boot zone recovery data: "); + /* + * Look to see if recovery information has been saved. + * If so we can generate a prototype superblock based + * on that information. + * + * We need fragments-per-group, number of cylinder groups, + * location of the superblock within the cylinder group, and + * the conversion from filesystem fragments to disk blocks. + * + * When building a UFS2 filesystem, newfs(8) stores these + * details at the end of the boot block area at the start + * of the filesystem partition. If they have been overwritten + * by a boot block, we fail. But usually they are there + * and we can use them. + * + * We could ask the underlying device for its sector size, + * but some devices lie. So we just try a plausible range. + */ + error = ENOENT; + for (secsize = dbtob(1); secsize <= SBLOCKSIZE; secsize *= 2) + if ((error = (*readfunc)(devfd, (SBLOCK_UFS2 - secsize), + &fsrbuf, secsize)) == 0) + break; + if (error != 0) + goto trynowarn; + cp = fsrbuf; /* type change to keep compiler happy */ + fsr = (struct fsrecovery *)&cp[secsize - sizeof *fsr]; + if (fsr->fsr_magic != FS_UFS2_MAGIC || + (protofs = UFS_MALLOC(SBLOCKSIZE, filltype, M_NOWAIT)) + == NULL) { + UFS_FREE(fsrbuf, filltype); + goto trynowarn; + } + memset(protofs, 0, sizeof(struct fs)); + protofs->fs_fpg = fsr->fsr_fpg; + protofs->fs_fsbtodb = fsr->fsr_fsbtodb; + protofs->fs_sblkno = fsr->fsr_sblkno; + protofs->fs_magic = fsr->fsr_magic; + protofs->fs_ncg = fsr->fsr_ncg; + UFS_FREE(fsrbuf, filltype); + } + /* + * Scan looking for alternative superblocks. + */ + for (cg = 0; cg < protofs->fs_ncg; cg++) { + sblk = dbtob(fsbtodb(protofs, cgsblock(protofs, cg))); + if (ffs_sbget(devfd, fsp, sblk, UFS_NOMSG | nocsum, filltype, + readfunc) == 0) { + if (msg) + printf("succeeded with alternate superblock " + "at %jd\n", (intmax_t)btodb(sblk)); + UFS_FREE(protofs, filltype); + return (0); + } + } + UFS_FREE(protofs, filltype); + /* + * Our alternate superblock strategies failed. Our last ditch effort + * is to see if the standard superblock has only non-critical errors. + */ +trynowarn: + flags = UFS_NOWARNFAIL | UFS_NOMSG | nocsum; + if (msg) { + printf("failed\n"); + flags &= ~UFS_NOMSG; + } + if (ffs_sbget(devfd, fsp, UFS_STDSB, flags, filltype, readfunc) != 0) + return (ENOENT); + if (msg) + printf("Using standard superblock with non-critical errors.\n"); + return (0); +} + /* * Write a superblock to the devfd device from the memory pointed to by fs. * Write out the superblock summary information if it is present. diff --git a/sys/ufs/ffs/ffs_vfsops.c b/sys/ufs/ffs/ffs_vfsops.c --- a/sys/ufs/ffs/ffs_vfsops.c +++ b/sys/ufs/ffs/ffs_vfsops.c @@ -913,8 +913,7 @@ struct g_consumer *cp; struct mount *nmp; struct vnode *devvp; - int candelete, canspeedup, flags; - off_t loc; + int candelete, canspeedup; fs = NULL; ump = NULL; @@ -958,12 +957,12 @@ goto out; } /* fetch the superblock and summary information */ - loc = UFS_STDSB; - flags = 0; if ((mp->mnt_flag & (MNT_ROOTFS | MNT_FORCE)) != 0) - flags = UFS_NOHASHFAIL; - if ((error = ffs_sbget(devvp, &fs, loc, flags, M_UFSMNT, ffs_use_bread)) - != 0) + error = ffs_sbsearch(devvp, &fs, 0, M_UFSMNT, ffs_use_bread); + else + error = ffs_sbget(devvp, &fs, UFS_STDSB, 0, M_UFSMNT, + ffs_use_bread); + if (error != 0) goto out; fs->fs_flags &= ~FS_UNCLEAN; if (fs->fs_clean == 0) { diff --git a/sys/ufs/ffs/fs.h b/sys/ufs/ffs/fs.h --- a/sys/ufs/ffs/fs.h +++ b/sys/ufs/ffs/fs.h @@ -102,11 +102,19 @@ * UFS_NOWARNFAIL will warn about inconsistencies but still return the * superblock. It includes UFS_NOHASHFAIL. UFS_NOWARNFAIL is used by * programs like fsck_ffs(8) to debug broken filesystems. + * + * UFS_FSRONLY will only validate the superblock fields needed to + * calculate where the backup filesystem superblocks are located. + * If these values pass their validation tests, then the superblock + * is returned. This flag is used as part of the attempt to find + * alternate superblocks when using ffs_sbsearch(). */ #define UFS_NOHASHFAIL 0x0001 /* Ignore check-hash failure */ #define UFS_NOWARNFAIL 0x0003 /* Ignore non-fatal inconsistencies */ #define UFS_NOMSG 0x0004 /* Print no error message */ #define UFS_NOCSUM 0x0008 /* Read just the superblock without csum */ +#define UFS_FSRONLY 0x0010 /* Validate only values needed for recovery + of alternate superblocks */ #define UFS_ALTSBLK 0x1000 /* Flag used internally */ /* diff --git a/tools/diag/prtblknos/main.c b/tools/diag/prtblknos/main.c --- a/tools/diag/prtblknos/main.c +++ b/tools/diag/prtblknos/main.c @@ -56,7 +56,6 @@ char ibuf[64]; char *fsname, *filename; ino_t inonum; - int error; filename = NULL; if (argc == 2) { @@ -83,7 +82,8 @@ fsname = *++argv; /* get the superblock. */ - if ((error = ufs_disk_fillout(&disk, fsname)) < 0) + if (ufs_disk_fillout_blank(&disk, fsname) == -1 || + sbfind(&disk, 0) == -1) err(1, "Cannot access file system superblock on %s", fsname); fs = (struct fs *)&disk.d_sb; @@ -99,7 +99,7 @@ (void)printf("%s (inode #%jd): ", filename, (intmax_t)inonum); - if ((error = getinode(&disk, &dp, inonum)) < 0) + if (getinode(&disk, &dp, inonum) < 0) warn("Read of inode %jd on %s failed: %s", (intmax_t)inonum, fsname, disk.d_error);