git: efd949aaba74 - stable/13 - Have fsck_ffs(8) properly correct superblock check-hash failures.

From: Kirk McKusick <mckusick_at_FreeBSD.org>
Date: Wed, 16 Feb 2022 01:14:38 UTC
The branch stable/13 has been updated by mckusick:

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

commit efd949aaba7449ae994fe5052178c2ba3efd2e7b
Author:     Kirk McKusick <mckusick@FreeBSD.org>
AuthorDate: 2022-02-04 19:46:36 +0000
Commit:     Kirk McKusick <mckusick@FreeBSD.org>
CommitDate: 2022-02-16 01:14:22 +0000

    Have fsck_ffs(8) properly correct superblock check-hash failures.
    
    (cherry picked from commit c0bfa109b942659f609b7e2bf3ba042ec0cb3f9d)
    
    PR:           245916
---
 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 e78e2d206499..f7224b48a9a4 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;
 		}
 	}
 
@@ -646,6 +570,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 "