git: c0bfa109b942 - main - Have fsck_ffs(8) properly correct superblock check-hash failures.

From: Kirk McKusick <mckusick_at_FreeBSD.org>
Date: Fri, 04 Feb 2022 19:48:05 UTC
The branch main has been updated by mckusick:

URL: https://cgit.FreeBSD.org/src/commit/?id=c0bfa109b942659f609b7e2bf3ba042ec0cb3f9d

commit c0bfa109b942659f609b7e2bf3ba042ec0cb3f9d
Author:     Kirk McKusick <mckusick@FreeBSD.org>
AuthorDate: 2022-02-04 19:46:36 +0000
Commit:     Kirk McKusick <mckusick@FreeBSD.org>
CommitDate: 2022-02-04 19:47:48 +0000

    Have fsck_ffs(8) properly correct superblock check-hash failures.
    
    Part of the problem was that fsck_ffs would read the superblock
    multiple times complaining and repairing the superblock check hash
    each time and then at the end failing to write out the superblock
    with the corrected check hash. This fix reads the superblock just
    once and if the check hash is corrected ensures that the fixed
    superblock gets written.
    
    Tested by:    Peter Holm
    PR:           245916
    MFC after:    1 week
    Sponsored by: Netflix
---
 sbin/fsck_ffs/fsck.h   |   1 +
 sbin/fsck_ffs/fsutil.c |   4 +-
 sbin/fsck_ffs/globs.c  |   6 +-
 sbin/fsck_ffs/main.c   | 309 +++++++++++++++++++++++++++++++++----------------
 sbin/fsck_ffs/setup.c  | 182 +++++++----------------------
 5 files changed, 258 insertions(+), 244 deletions(-)

diff --git a/sbin/fsck_ffs/fsck.h b/sbin/fsck_ffs/fsck.h
index 9ecc5793e644..690a98038884 100644
--- a/sbin/fsck_ffs/fsck.h
+++ b/sbin/fsck_ffs/fsck.h
@@ -356,6 +356,7 @@ extern char	preen;			/* just fix normal inconsistencies */
 extern char	rerun;			/* rerun fsck. Only used in non-preen mode */
 extern int	returntosingle;		/* 1 => return to single user mode on exit */
 extern char	resolved;		/* cleared if unresolved changes => not clean */
+extern int	sbhashfailed;		/* when reading superblock check hash failed */
 extern char	havesb;			/* superblock has been read */
 extern char	skipclean;		/* skip clean file systems if preening */
 extern int	fsmodified;		/* 1 => write done to file system */
diff --git a/sbin/fsck_ffs/fsutil.c b/sbin/fsck_ffs/fsutil.c
index db22ee5b20cf..711c9bb63549 100644
--- a/sbin/fsck_ffs/fsutil.c
+++ b/sbin/fsck_ffs/fsutil.c
@@ -250,6 +250,7 @@ cglookup(int cg)
 	if (cgp == NULL) {
 		if (sujrecovery)
 			errx(EEXIT,"Ran out of memory during journal recovery");
+		flush(fswritefd, &cgblk);
 		getblk(&cgblk, cgtod(&sblock, cg), sblock.fs_cgsize);
 		return (&cgblk);
 	}
@@ -564,7 +565,7 @@ ckfini(int markclean)
 			cmd.size = markclean ? -1 : 1;
 			if (sysctlbyname("vfs.ffs.setflags", 0, 0,
 			    &cmd, sizeof cmd) == -1)
-				rwerror("SET FILE SYSTEM FLAGS", FS_UNCLEAN);
+				pwarn("CANNOT SET FILE SYSTEM DIRTY FLAG\n");
 			if (!preen) {
 				printf("\n***** FILE SYSTEM MARKED %s *****\n",
 				    markclean ? "CLEAN" : "DIRTY");
@@ -575,6 +576,7 @@ ckfini(int markclean)
 			printf("\n***** FILE SYSTEM STILL DIRTY *****\n");
 			rerun = 1;
 		}
+		bkgrdflag = 0;
 	}
 	if (debug && cachelookups > 0)
 		printf("cache with %d buffers missed %d of %d (%d%%)\n",
diff --git a/sbin/fsck_ffs/globs.c b/sbin/fsck_ffs/globs.c
index be4434ce38ca..09dbcc6694a2 100644
--- a/sbin/fsck_ffs/globs.c
+++ b/sbin/fsck_ffs/globs.c
@@ -96,6 +96,7 @@ char	preen;			/* just fix normal inconsistencies */
 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 */
@@ -155,8 +156,9 @@ fsckinit(void)
 	resolved = 0;
 	havesb = 0;
 	fsmodified = 0;
-	fsreadfd = 0;
-	fswritefd = 0;
+	sbhashfailed = 0;
+	fsreadfd = -1;
+	fswritefd = -1;
 	maxfsblock = 0;
 	maxino = 0;
 	lfdir = 0;
diff --git a/sbin/fsck_ffs/main.c b/sbin/fsck_ffs/main.c
index acf26d9fc558..e776add4a115 100644
--- a/sbin/fsck_ffs/main.c
+++ b/sbin/fsck_ffs/main.c
@@ -75,6 +75,8 @@ static int	restarts;
 static void usage(void) __dead2;
 static intmax_t argtoimax(int flag, const char *req, const char *str, int base);
 static int checkfilesys(char *filesys);
+static int setup_bkgrdchk(struct statfs *mntp, int sbrdfailed, char **filesys);
+static int openfilesys(char *dev);
 static int chkdoreload(struct statfs *mntp);
 static struct statfs *getmntpt(const char *);
 
@@ -181,6 +183,11 @@ main(int argc, char *argv[])
 	if (!argc)
 		usage();
 
+	if (bkgrdflag && cvtlevel > 0) {
+		pfatal("CANNOT CONVERT A SNAPSHOT\n");
+		exit(EEXIT);
+	}
+
 	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
 		(void)signal(SIGINT, catch);
 	if (ckclean)
@@ -237,18 +244,10 @@ checkfilesys(char *filesys)
 	ufs2_daddr_t n_ffree, n_bfree;
 	struct dups *dp;
 	struct statfs *mntp;
-	struct stat snapdir;
-	struct group *grp;
-	struct iovec *iov;
-	char errmsg[255];
-	int ofsmodified;
-	int iovlen;
 	intmax_t blks, files;
 	size_t size;
+	int sbreadfailed, ofsmodified;
 
-	iov = NULL;
-	iovlen = 0;
-	errmsg[0] = '\0';
 	fsutilinit();
 	fsckinit();
 
@@ -272,10 +271,12 @@ checkfilesys(char *filesys)
 	 * exit status will cause a foreground check to be run.
 	 */
 	sblock_init();
+	sbreadfailed = 0;
+	if (openfilesys(filesys) == 0 || readsb(0) == 0)
+		sbreadfailed = 1;
 	if (bkgrdcheck) {
-		if ((fsreadfd = open(filesys, O_RDONLY)) < 0 || readsb(0) == 0)
+		if (sbreadfailed)
 			exit(3);	/* Cannot read superblock */
-		close(fsreadfd);
 		/* Earlier background failed or journaled */
 		if (sblock.fs_flags & (FS_NEEDSFSCK | FS_SUJ))
 			exit(4);
@@ -293,7 +294,7 @@ checkfilesys(char *filesys)
 		/*
 		 * If file system is gjournaled, check it here.
 		 */
-		if ((fsreadfd = open(filesys, O_RDONLY)) < 0 || readsb(0) == 0)
+		if (sbreadfailed)
 			exit(3);	/* Cannot read superblock */
 		if (bkgrdflag == 0 &&
 		    (nflag || (fswritefd = open(filesys, O_WRONLY)) < 0)) {
@@ -307,107 +308,30 @@ checkfilesys(char *filesys)
 				pwarn("FILE SYSTEM CLEAN; SKIPPING CHECKS\n");
 				exit(0);
 			}
-			if ((sblock.fs_flags & (FS_UNCLEAN | FS_NEEDSFSCK)) == 0) {
+			if ((sblock.fs_flags &
+			    (FS_UNCLEAN | FS_NEEDSFSCK)) == 0) {
 				bufinit();
 				gjournal_check(filesys);
 				if (chkdoreload(mntp) == 0)
 					exit(0);
 				exit(4);
 			} else {
-				pfatal("UNEXPECTED INCONSISTENCY, CANNOT RUN "
-				    "FAST FSCK\n");
+				pfatal("FULL FSCK NEEDED, CANNOT RUN FAST "
+				    "FSCK\n");
 			}
 		}
-		close(fsreadfd);
 		close(fswritefd);
+		fswritefd = -1;
 	}
-	/*
-	 * If we are to do a background check:
-	 *	Get the mount point information of the file system
-	 *	create snapshot file
-	 *	return created snapshot file
-	 *	if not found, clear bkgrdflag and proceed with normal fsck
-	 */
 	if (bkgrdflag) {
-		/* Get the mount point information of the file system */
-		if (mntp == NULL) {
-			bkgrdflag = 0;
-			pfatal("NOT MOUNTED, CANNOT RUN IN BACKGROUND\n");
-		} else if ((mntp->f_flags & MNT_SOFTDEP) == 0) {
-			bkgrdflag = 0;
-			pfatal("NOT USING SOFT UPDATES, CANNOT RUN IN "
-			    "BACKGROUND\n");
-		} else if ((mntp->f_flags & MNT_RDONLY) != 0) {
-			bkgrdflag = 0;
-			pfatal("MOUNTED READ-ONLY, CANNOT RUN IN BACKGROUND\n");
-		} else if ((fsreadfd = open(filesys, O_RDONLY)) >= 0) {
-			if (readsb(0) != 0) {
-				if (sblock.fs_flags & (FS_NEEDSFSCK | FS_SUJ)) {
-					bkgrdflag = 0;
-					pfatal(
-			"UNEXPECTED INCONSISTENCY, CANNOT RUN IN BACKGROUND\n");
-				}
-				if ((sblock.fs_flags & FS_UNCLEAN) == 0 &&
-				    skipclean && ckclean) {
-					/*
-					 * file system is clean;
-					 * skip snapshot and report it clean
-					 */
-					pwarn(
-					"FILE SYSTEM CLEAN; SKIPPING CHECKS\n");
-					goto clean;
-				}
-			}
-			close(fsreadfd);
-		}
-		if (bkgrdflag) {
-			snprintf(snapname, sizeof snapname, "%s/.snap",
-			    mntp->f_mntonname);
-			if (stat(snapname, &snapdir) < 0) {
-				if (errno != ENOENT) {
-					bkgrdflag = 0;
-					pfatal(
-	"CANNOT FIND SNAPSHOT DIRECTORY %s: %s, CANNOT RUN IN BACKGROUND\n",
-					    snapname, strerror(errno));
-				} else if ((grp = getgrnam("operator")) == NULL ||
-					   mkdir(snapname, 0770) < 0 ||
-					   chown(snapname, -1, grp->gr_gid) < 0 ||
-					   chmod(snapname, 0770) < 0) {
-					bkgrdflag = 0;
-					pfatal(
-	"CANNOT CREATE SNAPSHOT DIRECTORY %s: %s, CANNOT RUN IN BACKGROUND\n",
-					    snapname, strerror(errno));
-				}
-			} else if (!S_ISDIR(snapdir.st_mode)) {
-				bkgrdflag = 0;
-				pfatal(
-			"%s IS NOT A DIRECTORY, CANNOT RUN IN BACKGROUND\n",
-				    snapname);
-			}
-		}
-		if (bkgrdflag) {
-			snprintf(snapname, sizeof snapname,
-			    "%s/.snap/fsck_snapshot", mntp->f_mntonname);
-			build_iovec(&iov, &iovlen, "fstype", "ffs", 4);
-			build_iovec(&iov, &iovlen, "from", snapname,
-			    (size_t)-1);
-			build_iovec(&iov, &iovlen, "fspath", mntp->f_mntonname,
-			    (size_t)-1);
-			build_iovec(&iov, &iovlen, "errmsg", errmsg,
-			    sizeof(errmsg));
-			build_iovec(&iov, &iovlen, "update", NULL, 0);
-			build_iovec(&iov, &iovlen, "snapshot", NULL, 0);
-
-			while (nmount(iov, iovlen, mntp->f_flags) < 0) {
-				if (errno == EEXIST && unlink(snapname) == 0)
-					continue;
-				bkgrdflag = 0;
-				pfatal("CANNOT CREATE SNAPSHOT %s: %s %s\n",
-				    snapname, strerror(errno), errmsg);
-				break;
-			}
-			if (bkgrdflag != 0)
-				filesys = snapname;
+		switch (setup_bkgrdchk(mntp, sbreadfailed, &filesys)) {
+		case -1: /* filesystem clean */
+			goto clean;
+		case 0: /* cannot do background, give up */
+			exit(EEXIT);
+		case 1: /* doing background check, preen rules apply */
+			preen = 1;
+			break;
 		}
 	}
 
@@ -648,6 +572,187 @@ checkfilesys(char *filesys)
 	return (rerun ? ERERUN : 0);
 }
 
+/*
+ * If we are to do a background check:
+ *	Get the mount point information of the file system
+ *	If already clean, return -1
+ *	Check that kernel supports background fsck
+ *	Find or create the snapshot directory
+ *	Create the snapshot file
+ *	Open snapshot
+ *	If anything fails print reason and return 0 which exits
+ */
+static int
+setup_bkgrdchk(struct statfs *mntp, int sbreadfailed, char **filesys)
+{
+	struct stat snapdir;
+	struct group *grp;
+	struct iovec *iov;
+	char errmsg[255];
+	int iovlen;
+	long size;
+
+	/* Get the mount point information of the file system */
+	if (mntp == NULL) {
+		pwarn("NOT MOUNTED, CANNOT RUN IN BACKGROUND\n");
+		return (0);
+	}
+	if ((mntp->f_flags & MNT_RDONLY) != 0) {
+		pwarn("MOUNTED READ-ONLY, CANNOT RUN IN BACKGROUND\n");
+		return (0);
+	}
+	if ((mntp->f_flags & MNT_SOFTDEP) == 0) {
+		pwarn("NOT USING SOFT UPDATES, CANNOT RUN IN BACKGROUND\n");
+		return (0);
+	}
+	if (sbreadfailed) {
+		pwarn("SUPERBLOCK READ FAILED, CANNOT RUN IN BACKGROUND\n");
+		return (0);
+	}
+	if ((sblock.fs_flags & FS_NEEDSFSCK) != 0) {
+		pwarn("FULL FSCK NEEDED, CANNOT RUN IN BACKGROUND\n");
+		return (0);
+	}
+	if ((sblock.fs_flags & FS_SUJ) != 0) {
+		pwarn("JOURNALED FILESYSTEM, CANNOT RUN IN BACKGROUND\n");
+		return (0);
+	}
+	if (skipclean && ckclean &&
+	   (sblock.fs_flags & (FS_UNCLEAN|FS_NEEDSFSCK)) == 0) {
+		/*
+		 * file system is clean;
+		 * skip snapshot and report it clean
+		 */
+		pwarn("FILE SYSTEM CLEAN; SKIPPING CHECKS\n");
+		return (-1);
+	}
+	/* Check that kernel supports background fsck */
+	size = MIBSIZE;
+	if (sysctlnametomib("vfs.ffs.adjrefcnt", adjrefcnt, &size) < 0||
+	    sysctlnametomib("vfs.ffs.adjblkcnt", adjblkcnt, &size) < 0||
+	    sysctlnametomib("vfs.ffs.setsize", setsize, &size) < 0 ||
+	    sysctlnametomib("vfs.ffs.freefiles", freefiles, &size) < 0||
+	    sysctlnametomib("vfs.ffs.freedirs", freedirs, &size) < 0 ||
+	    sysctlnametomib("vfs.ffs.freeblks", freeblks, &size) < 0) {
+		pwarn("KERNEL LACKS BACKGROUND FSCK SUPPORT\n");
+		return (0);
+	}
+	/*
+	 * When kernel lacks runtime bgfsck superblock summary
+	 * adjustment functionality, it does not mean we can not
+	 * continue, as old kernels will recompute the summary at
+	 * mount time. However, it will be an unexpected softupdates
+	 * inconsistency if it turns out that the summary is still
+	 * incorrect. Set a flag so subsequent operation can know this.
+	 */
+	bkgrdsumadj = 1;
+	if (sysctlnametomib("vfs.ffs.adjndir", adjndir, &size) < 0 ||
+	   sysctlnametomib("vfs.ffs.adjnbfree", adjnbfree, &size) < 0 ||
+	   sysctlnametomib("vfs.ffs.adjnifree", adjnifree, &size) < 0 ||
+	   sysctlnametomib("vfs.ffs.adjnffree", adjnffree, &size) < 0 ||
+	   sysctlnametomib("vfs.ffs.adjnumclusters", adjnumclusters,
+	   &size) < 0) {
+		bkgrdsumadj = 0;
+		pwarn("KERNEL LACKS RUNTIME SUPERBLOCK SUMMARY ADJUSTMENT "
+		    "SUPPORT\n");
+	}
+	/* Find or create the snapshot directory */
+	snprintf(snapname, sizeof snapname, "%s/.snap",
+	    mntp->f_mntonname);
+	if (stat(snapname, &snapdir) < 0) {
+		if (errno != ENOENT) {
+			pwarn("CANNOT FIND SNAPSHOT DIRECTORY %s: %s, CANNOT "
+			    "RUN IN BACKGROUND\n", snapname, strerror(errno));
+			return (0);
+		}
+		if ((grp = getgrnam("operator")) == NULL ||
+			   mkdir(snapname, 0770) < 0 ||
+			   chown(snapname, -1, grp->gr_gid) < 0 ||
+			   chmod(snapname, 0770) < 0) {
+			pwarn("CANNOT CREATE SNAPSHOT DIRECTORY %s: %s, "
+			    "CANNOT RUN IN BACKGROUND\n", snapname,
+			    strerror(errno));
+			return (0);
+		}
+	} else if (!S_ISDIR(snapdir.st_mode)) {
+		pwarn("%s IS NOT A DIRECTORY, CANNOT RUN IN BACKGROUND\n",
+		    snapname);
+		return (0);
+	}
+	/* Create the snapshot file */
+	iov = NULL;
+	iovlen = 0;
+	errmsg[0] = '\0';
+	snprintf(snapname, sizeof snapname, "%s/.snap/fsck_snapshot",
+	    mntp->f_mntonname);
+	build_iovec(&iov, &iovlen, "fstype", "ffs", 4);
+	build_iovec(&iov, &iovlen, "from", snapname, (size_t)-1);
+	build_iovec(&iov, &iovlen, "fspath", mntp->f_mntonname, (size_t)-1);
+	build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg));
+	build_iovec(&iov, &iovlen, "update", NULL, 0);
+	build_iovec(&iov, &iovlen, "snapshot", NULL, 0);
+	/* Create snapshot, removing old snapshot it it exists */
+	while (nmount(iov, iovlen, mntp->f_flags) < 0) {
+		if (errno == EEXIST && unlink(snapname) == 0)
+			continue;
+		pwarn("CANNOT CREATE SNAPSHOT %s: %s %s\n", snapname,
+		    strerror(errno), errmsg);
+		return (0);
+	}
+	/* Open snapshot */
+	if (openfilesys(snapname) == 0) {
+		unlink(snapname);
+		pwarn("CANNOT OPEN SNAPSHOT %s: %s, CANNOT RUN IN "
+		    "BACKGROUND\n", snapname, strerror(errno));
+		return (0);
+	}
+	free(sblock.fs_csp);
+	free(sblock.fs_si);
+	havesb = 0;
+	*filesys = snapname;
+	cmd.version = FFS_CMD_VERSION;
+	cmd.handle = fsreadfd;
+	return (1);
+}
+
+/*
+ * Open a device or file to be checked by fsck.
+ */
+static int
+openfilesys(char *dev)
+{
+	struct stat statb;
+	int saved_fsreadfd;
+
+	if (stat(dev, &statb) < 0) {
+		pfatal("CANNOT STAT %s: %s\n", dev, strerror(errno));
+		return (0);
+	}
+	if ((statb.st_mode & S_IFMT) != S_IFCHR &&
+	    (statb.st_mode & S_IFMT) != S_IFBLK) {
+		if (bkgrdflag != 0 && (statb.st_flags & SF_SNAPSHOT) == 0) {
+			pfatal("BACKGROUND FSCK LACKS A SNAPSHOT\n");
+			exit(EEXIT);
+		}
+		if (bkgrdflag != 0) {
+			cursnapshot = statb.st_ino;
+		} else {
+			pfatal("%s IS NOT A DISK DEVICE\n", dev);
+			if (reply("CONTINUE") == 0)
+				return (0);
+		}
+	}
+	saved_fsreadfd = fsreadfd;
+	if ((fsreadfd = open(dev, O_RDONLY)) < 0) {
+		fsreadfd = saved_fsreadfd;
+		pfatal("CANNOT OPEN %s: %s\n", dev, strerror(errno));
+		return (0);
+	}
+	if (saved_fsreadfd != -1)
+		close(saved_fsreadfd);
+	return (1);
+}
+
 static int
 chkdoreload(struct statfs *mntp)
 {
diff --git a/sbin/fsck_ffs/setup.c b/sbin/fsck_ffs/setup.c
index 0ae7f1bbb28f..506a027f40ac 100644
--- a/sbin/fsck_ffs/setup.c
+++ b/sbin/fsck_ffs/setup.c
@@ -76,114 +76,19 @@ static int chkrecovery(int devfd);
 int
 setup(char *dev)
 {
-	long cg, asked, i, j;
-	long bmapsize;
-	struct stat statb;
+	long cg, bmapsize;
 	struct fs proto;
-	size_t size;
 
-	havesb = 0;
-	fswritefd = -1;
-	cursnapshot = 0;
-	if (stat(dev, &statb) < 0) {
-		printf("Can't stat %s: %s\n", dev, strerror(errno));
-		if (bkgrdflag) {
-			unlink(snapname);
-			bkgrdflag = 0;
-		}
-		return (0);
-	}
-	if ((statb.st_mode & S_IFMT) != S_IFCHR &&
-	    (statb.st_mode & S_IFMT) != S_IFBLK) {
-		if (bkgrdflag != 0 && (statb.st_flags & SF_SNAPSHOT) == 0) {
-			unlink(snapname);
-			printf("background fsck lacks a snapshot\n");
-			exit(EEXIT);
-		}
-		if ((statb.st_flags & SF_SNAPSHOT) != 0 && cvtlevel == 0) {
-			cursnapshot = statb.st_ino;
-		} else {
-			if (cvtlevel == 0 ||
-			    (statb.st_flags & SF_SNAPSHOT) == 0) {
-				if (preen && bkgrdflag) {
-					unlink(snapname);
-					bkgrdflag = 0;
-				}
-				pfatal("%s is not a disk device", dev);
-				if (reply("CONTINUE") == 0) {
-					if (bkgrdflag) {
-						unlink(snapname);
-						bkgrdflag = 0;
-					}
-					return (0);
-				}
-			} else {
-				if (bkgrdflag) {
-					unlink(snapname);
-					bkgrdflag = 0;
-				}
-				pfatal("cannot convert a snapshot");
-				exit(EEXIT);
-			}
-		}
-	}
-	if ((fsreadfd = open(dev, O_RDONLY)) < 0) {
-		if (bkgrdflag) {
-			unlink(snapname);
-			bkgrdflag = 0;
-		}
-		printf("Can't open %s: %s\n", dev, strerror(errno));
+	/*
+	 * We are expected to have an open file descriptor
+	 */
+	if (fsreadfd < 0)
 		return (0);
-	}
-	if (bkgrdflag) {
-		unlink(snapname);
-		size = MIBSIZE;
-		if (sysctlnametomib("vfs.ffs.adjrefcnt", adjrefcnt, &size) < 0||
-		    sysctlnametomib("vfs.ffs.adjblkcnt", adjblkcnt, &size) < 0||
-		    sysctlnametomib("vfs.ffs.setsize", setsize, &size) < 0 ||
-		    sysctlnametomib("vfs.ffs.freefiles", freefiles, &size) < 0||
-		    sysctlnametomib("vfs.ffs.freedirs", freedirs, &size) < 0 ||
-		    sysctlnametomib("vfs.ffs.freeblks", freeblks, &size) < 0) {
-			pfatal("kernel lacks background fsck support\n");
-			exit(EEXIT);
-		}
-		/*
-		 * When kernel is lack of runtime bgfsck superblock summary
-		 * adjustment functionality, it does not mean we can not
-		 * continue, as old kernels will recompute the summary at
-		 * mount time.  However, it will be an unexpected softupdates
-		 * inconsistency if it turns out that the summary is still
-		 * incorrect.  Set a flag so subsequent operation can know
-		 * this.
-		 */
-		bkgrdsumadj = 1;
-		if (sysctlnametomib("vfs.ffs.adjndir", adjndir, &size) < 0 ||
-		    sysctlnametomib("vfs.ffs.adjnbfree", adjnbfree, &size) < 0 ||
-		    sysctlnametomib("vfs.ffs.adjnifree", adjnifree, &size) < 0 ||
-		    sysctlnametomib("vfs.ffs.adjnffree", adjnffree, &size) < 0 ||
-		    sysctlnametomib("vfs.ffs.adjnumclusters", adjnumclusters, &size) < 0) {
-			bkgrdsumadj = 0;
-			pwarn("kernel lacks runtime superblock summary adjustment support");
-		}
-		cmd.version = FFS_CMD_VERSION;
-		cmd.handle = fsreadfd;
-		fswritefd = -1;
-	}
-	if (preen == 0)
-		printf("** %s", dev);
-	if (bkgrdflag == 0 &&
-	    (nflag || (fswritefd = open(dev, O_WRONLY)) < 0)) {
-		fswritefd = -1;
-		if (preen)
-			pfatal("NO WRITE ACCESS");
-		printf(" (NO WRITE)");
-	}
-	if (preen == 0)
-		printf("\n");
 	/*
-	 * Read in the superblock, looking for alternates if necessary
+	 * If we do not yet have a superblock, read it in looking
+	 * for alternates if necessary.
 	 */
-	if (readsb(1) == 0) {
+	if (havesb == 0 && readsb(1) == 0) {
 		skipclean = 0;
 		if (bflag || preen || calcsb(dev, fsreadfd, &proto) == 0)
 			return(0);
@@ -195,19 +100,38 @@ setup(char *dev)
 				break;
 		}
 		if (cg >= proto.fs_ncg) {
-			printf("%s %s\n%s %s\n%s %s\n",
-				"SEARCH FOR ALTERNATE SUPER-BLOCK",
-				"FAILED. YOU MUST USE THE",
-				"-b OPTION TO FSCK TO SPECIFY THE",
-				"LOCATION OF AN ALTERNATE",
-				"SUPER-BLOCK TO SUPPLY NEEDED",
-				"INFORMATION; SEE fsck_ffs(8).");
+			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);
+	if (bkgrdflag == 0 &&
+	    (nflag || (fswritefd = open(dev, O_WRONLY)) < 0)) {
+		fswritefd = -1;
+		if (preen)
+			pfatal("NO WRITE ACCESS");
+		printf(" (NO WRITE)");
+	}
+	if (preen == 0)
+		printf("\n");
+	if (sbhashfailed != 0) {
+		pwarn("SUPERBLOCK CHECK HASH FAILED");
+		if (fswritefd == -1)
+			pwarn("OPENED READONLY SO CANNOT CORRECT CHECK HASH\n");
+		else if (preen || reply("CORRECT CHECK HASH") != 0) {
+			if (preen)
+				printf(" (CORRECTED)\n");
+			sblock.fs_clean = 0;
+			sbdirty();
+		}
+	}
 	if (skipclean && ckclean && sblock.fs_clean) {
 		pwarn("FILE SYSTEM CLEAN; SKIPPING CHECKS\n");
 		return (-1);
@@ -247,30 +171,6 @@ setup(char *dev)
 	    fswritefd != -1 && chkrecovery(fsreadfd) == 0 &&
 	    reply("SAVE DATA TO FIND ALTERNATE SUPERBLOCKS") != 0)
 		saverecovery(fsreadfd, fswritefd);
-	/*
-	 * read in the summary info.
-	 */
-	asked = 0;
-	sblock.fs_csp = Calloc(1, sblock.fs_cssize);
-	if (sblock.fs_csp == NULL) {
-		printf("cannot alloc %u bytes for cg summary info\n",
-		    (unsigned)sblock.fs_cssize);
-		goto badsb;
-	}
-	for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) {
-		size = MIN(sblock.fs_cssize - i, sblock.fs_bsize);
-		readcnt[sblk.b_type]++;
-		if (blread(fsreadfd, (char *)sblock.fs_csp + i,
-		    fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag),
-		    size) != 0 && !asked) {
-			pfatal("BAD SUMMARY INFORMATION");
-			if (reply("CONTINUE") == 0) {
-				ckfini(0);
-				exit(EEXIT);
-			}
-			asked++;
-		}
-	}
 	/*
 	 * allocate and initialize the necessary maps
 	 */
@@ -320,13 +220,17 @@ readsb(int listerr)
 	int bad, ret;
 	struct fs *fs;
 
-	super = bflag ? bflag * dev_bsize : STDSB_NOHASHFAIL;
+	super = bflag ? bflag * dev_bsize :
+	    sbhashfailed ? STDSB_NOHASHFAIL_NOMSG : STDSB_NOMSG;
 	readcnt[sblk.b_type]++;
-	if ((ret = sbget(fsreadfd, &fs, super)) != 0) {
+	while ((ret = sbget(fsreadfd, &fs, super)) != 0) {
 		switch (ret) {
-		case EINVAL:
-			/* Superblock check-hash failed */
-			return (0);
+		case EINTEGRITY:
+			if (bflag || super == STDSB_NOHASHFAIL_NOMSG)
+				return (0);
+			super = STDSB_NOHASHFAIL_NOMSG;
+			sbhashfailed = 1;
+			continue;
 		case ENOENT:
 			if (bflag)
 				printf("%jd is not a file system "