git: d22531d57282 - main - Identify each UFS/FFS superblock integrity check as a warning or fatal error.

From: Kirk McKusick <mckusick_at_FreeBSD.org>
Date: Mon, 01 Aug 2022 05:12:59 UTC
The branch main has been updated by mckusick:

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

commit d22531d5728298deda1ce9f7cdebcd4fd8d9ddb2
Author:     Kirk McKusick <mckusick@FreeBSD.org>
AuthorDate: 2022-08-01 05:07:20 +0000
Commit:     Kirk McKusick <mckusick@FreeBSD.org>
CommitDate: 2022-08-01 05:07:20 +0000

    Identify each UFS/FFS superblock integrity check as a warning or fatal error.
    
    Identify each of the superblock validation checks as either a
    warning or a fatal error. Any integrity check that can cause a
    system hang or crash is marked as fatal. Those that may simply
    lead to poor file layoutor other less good operating conditions
    are marked as warning.
    
    Normally both fatal and warning are treated as errors and prevent
    the superblock from being loaded. A new flag, UFS_NOWARNFAIL, is
    added. When passed to ffs_sbget() it will note warnings that it
    finds, but will still proceed with loading the superblock. Note
    that when UFS_NOWARNFAIL is used, it also includes UFS_NOHASHFAIL.
    
    No legitimate superblocks should fail as a result of these changes.
---
 sys/ufs/ffs/ffs_subr.c | 182 +++++++++++++++++++++++++++----------------------
 sys/ufs/ffs/fs.h       |   5 ++
 2 files changed, 106 insertions(+), 81 deletions(-)

diff --git a/sys/ufs/ffs/ffs_subr.c b/sys/ufs/ffs/ffs_subr.c
index 0bcc43fae4d4..8768c5588def 100644
--- a/sys/ufs/ffs/ffs_subr.c
+++ b/sys/ufs/ffs/ffs_subr.c
@@ -324,8 +324,7 @@ readsuper(void *devfd, struct fs **fsp, off_t sblockloc, int flags,
  */
 #define ILOG2(num)	(fls(num) - 1)
 #define MPRINT		if (prtmsg) printf
-#undef CHK
-#define CHK(lhs, op, rhs, fmt)						\
+#define FCHK(lhs, op, rhs, fmt)						\
 	if (lhs op rhs) {						\
 		MPRINT("UFS%d superblock failed: %s (" #fmt ") %s %s ("	\
 		    #fmt ")\n", fs->fs_magic == FS_UFS1_MAGIC ? 1 : 2,	\
@@ -333,124 +332,145 @@ readsuper(void *devfd, struct fs **fsp, off_t sblockloc, int flags,
 		if (error == 0)						\
 			error = ENOENT;					\
 	}
-#define CHK2(lhs1, op1, rhs1, lhs2, op2, rhs2, fmt)			\
+#define WCHK(lhs, op, rhs, fmt)						\
+	if (lhs op rhs) {						\
+		MPRINT("UFS%d superblock failed: %s (" #fmt ") %s %s ("	\
+		    #fmt ")%s\n", fs->fs_magic == FS_UFS1_MAGIC ? 1 : 2,\
+		    #lhs, (intmax_t)lhs, #op, #rhs, (intmax_t)rhs, wmsg);\
+		if (error == 0)						\
+			error = warnerr;				\
+	}
+#define FCHK2(lhs1, op1, rhs1, lhs2, op2, rhs2, fmt)			\
 	if (lhs1 op1 rhs1 && lhs2 op2 rhs2) {				\
 		MPRINT("UFS%d superblock failed: %s (" #fmt ") %s %s ("	\
 		    #fmt ") && %s (" #fmt ") %s %s (" #fmt ")\n",	\
-		    fs->fs_magic == FS_UFS1_MAGIC ? 1 : 2, #lhs1, 	\
+		    fs->fs_magic == FS_UFS1_MAGIC ? 1 : 2, #lhs1,	\
 		    (intmax_t)lhs1, #op1, #rhs1, (intmax_t)rhs1, #lhs2,	\
 		    (intmax_t)lhs2, #op2, #rhs2, (intmax_t)rhs2);	\
 		if (error == 0)						\
 			error = ENOENT;					\
 	}
+#define WCHK2(lhs1, op1, rhs1, lhs2, op2, rhs2, fmt)			\
+	if (lhs1 op1 rhs1 && lhs2 op2 rhs2) {				\
+		MPRINT("UFS%d superblock failed: %s (" #fmt ") %s %s ("	\
+		    #fmt ") && %s (" #fmt ") %s %s (" #fmt ")%s\n",	\
+		    fs->fs_magic == FS_UFS1_MAGIC ? 1 : 2, #lhs1,	\
+		    (intmax_t)lhs1, #op1, #rhs1, (intmax_t)rhs1, #lhs2,	\
+		    (intmax_t)lhs2, #op2, #rhs2, (intmax_t)rhs2, wmsg);	\
+		if (error == 0)						\
+			error = warnerr;				\
+	}
 
 static int
 validate_sblock(struct fs *fs, int flags)
 {
 	u_long i, sectorsize;
 	u_int64_t maxfilesize, sizepb;
-	int error, prtmsg;
+	int error, prtmsg, warnerr;
+	char *wmsg;
 
 	error = 0;
 	sectorsize = dbtob(1);
 	prtmsg = ((flags & UFS_NOMSG) == 0);
+	warnerr = (flags & UFS_NOWARNFAIL) == UFS_NOWARNFAIL ? 0 : ENOENT;
+	wmsg = warnerr ? "" : " (Ignored)";
 	if (fs->fs_magic == FS_UFS2_MAGIC) {
 		if ((flags & UFS_ALTSBLK) == 0)
-			CHK2(fs->fs_sblockactualloc, !=, SBLOCK_UFS2,
+			FCHK2(fs->fs_sblockactualloc, !=, SBLOCK_UFS2,
 			    fs->fs_sblockactualloc, !=, 0, %jd);
-		CHK(fs->fs_sblockloc, !=, SBLOCK_UFS2, %#jx);
-		CHK(fs->fs_maxsymlinklen, !=, ((UFS_NDADDR + UFS_NIADDR) *
+		FCHK(fs->fs_sblockloc, !=, SBLOCK_UFS2, %#jx);
+		FCHK(fs->fs_maxsymlinklen, !=, ((UFS_NDADDR + UFS_NIADDR) *
 			sizeof(ufs2_daddr_t)), %jd);
-		CHK(fs->fs_nindir, !=, fs->fs_bsize / sizeof(ufs2_daddr_t),
+		FCHK(fs->fs_nindir, !=, fs->fs_bsize / sizeof(ufs2_daddr_t),
 		    %jd);
-		CHK(fs->fs_inopb, !=,
+		FCHK(fs->fs_inopb, !=,
 		    fs->fs_bsize / sizeof(struct ufs2_dinode), %jd);
 	} else if (fs->fs_magic == FS_UFS1_MAGIC) {
 		if ((flags & UFS_ALTSBLK) == 0)
-			CHK(fs->fs_sblockactualloc, >, SBLOCK_UFS1, %jd);
-		CHK(fs->fs_sblockloc, <, 0, %jd);
-		CHK(fs->fs_sblockloc, >, SBLOCK_UFS1, %jd);
-		CHK(fs->fs_nindir, !=, fs->fs_bsize / sizeof(ufs1_daddr_t),
+			FCHK(fs->fs_sblockactualloc, >, SBLOCK_UFS1, %jd);
+		FCHK(fs->fs_sblockloc, <, 0, %jd);
+		FCHK(fs->fs_sblockloc, >, SBLOCK_UFS1, %jd);
+		FCHK(fs->fs_nindir, !=, fs->fs_bsize / sizeof(ufs1_daddr_t),
 		    %jd);
-		CHK(fs->fs_inopb, !=,
+		FCHK(fs->fs_inopb, !=,
 		    fs->fs_bsize / sizeof(struct ufs1_dinode), %jd);
-		CHK(fs->fs_maxsymlinklen, !=, ((UFS_NDADDR + UFS_NIADDR) *
+		FCHK(fs->fs_maxsymlinklen, !=, ((UFS_NDADDR + UFS_NIADDR) *
 			sizeof(ufs1_daddr_t)), %jd);
-		CHK(fs->fs_old_inodefmt, !=, FS_44INODEFMT, %jd);
-		CHK(fs->fs_old_rotdelay, !=, 0, %jd);
-		CHK(fs->fs_old_rps, !=, 60, %jd);
-		CHK(fs->fs_old_nspf, !=, fs->fs_fsize / sectorsize, %jd);
-		CHK(fs->fs_old_cpg, !=, 1, %jd);
-		CHK(fs->fs_old_interleave, !=, 1, %jd);
-		CHK(fs->fs_old_trackskew, !=, 0, %jd);
-		CHK(fs->fs_old_cpc, !=, 0, %jd);
-		CHK(fs->fs_old_postblformat, !=, 1, %jd);
-		CHK(fs->fs_old_nrpos, !=, 1, %jd);
-		CHK(fs->fs_old_spc, !=, fs->fs_fpg * fs->fs_old_nspf, %jd);
-		CHK(fs->fs_old_nsect, !=, fs->fs_old_spc, %jd);
-		CHK(fs->fs_old_npsect, !=, fs->fs_old_spc, %jd);
-		CHK(fs->fs_old_ncyl, !=, fs->fs_ncg, %jd);
+		WCHK(fs->fs_old_inodefmt, !=, FS_44INODEFMT, %jd);
+		WCHK(fs->fs_old_rotdelay, !=, 0, %jd);
+		WCHK(fs->fs_old_rps, !=, 60, %jd);
+		WCHK(fs->fs_old_nspf, !=, fs->fs_fsize / sectorsize, %jd);
+		WCHK(fs->fs_old_cpg, !=, 1, %jd);
+		WCHK(fs->fs_old_interleave, !=, 1, %jd);
+		WCHK(fs->fs_old_trackskew, !=, 0, %jd);
+		WCHK(fs->fs_old_cpc, !=, 0, %jd);
+		WCHK(fs->fs_old_postblformat, !=, 1, %jd);
+		WCHK(fs->fs_old_nrpos, !=, 1, %jd);
+		WCHK(fs->fs_old_spc, !=, fs->fs_fpg * fs->fs_old_nspf, %jd);
+		WCHK(fs->fs_old_nsect, !=, fs->fs_old_spc, %jd);
+		WCHK(fs->fs_old_npsect, !=, fs->fs_old_spc, %jd);
+		FCHK(fs->fs_old_ncyl, !=, fs->fs_ncg, %jd);
 	} else {
 		/* Bad magic number, so assume not a superblock */
 		return (ENOENT);
 	}
-	CHK(fs->fs_bsize, <, MINBSIZE, %jd);
-	CHK(fs->fs_bsize, >, MAXBSIZE, %jd);
-	CHK(fs->fs_bsize, <, roundup(sizeof(struct fs), DEV_BSIZE), %jd);
-	CHK(powerof2(fs->fs_bsize), ==, 0, %jd);
-	CHK(fs->fs_frag, <, 1, %jd);
-	CHK(fs->fs_frag, >, MAXFRAG, %jd);
-	CHK(fs->fs_frag, !=, numfrags(fs, fs->fs_bsize), %jd);
-	CHK(fs->fs_fsize, <, sectorsize, %jd);
-	CHK(fs->fs_fsize * fs->fs_frag, !=, fs->fs_bsize, %jd);
-	CHK(powerof2(fs->fs_fsize), ==, 0, %jd);
-	CHK(fs->fs_fpg, <, 3 * fs->fs_frag, %jd);
-	CHK(fs->fs_ncg, <, 1, %jd);
-	CHK(fs->fs_ipg, <, 1, %jd);
-	CHK(fs->fs_ipg * fs->fs_ncg, >, (((int64_t)(1)) << 32) - INOPB(fs),
+	FCHK(fs->fs_bsize, <, MINBSIZE, %jd);
+	FCHK(fs->fs_bsize, >, MAXBSIZE, %jd);
+	FCHK(fs->fs_bsize, <, roundup(sizeof(struct fs), DEV_BSIZE), %jd);
+	FCHK(powerof2(fs->fs_bsize), ==, 0, %jd);
+	FCHK(fs->fs_frag, <, 1, %jd);
+	FCHK(fs->fs_frag, >, MAXFRAG, %jd);
+	FCHK(fs->fs_frag, !=, numfrags(fs, fs->fs_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_ipg, <, 1, %jd);
+	FCHK(fs->fs_ipg * fs->fs_ncg, >, (((int64_t)(1)) << 32) - INOPB(fs),
 	    %jd);
-	CHK(fs->fs_sbsize, >, SBLOCKSIZE, %jd);
-	CHK(fs->fs_maxbsize, <, fs->fs_bsize, %jd);
-	CHK(powerof2(fs->fs_maxbsize), ==, 0, %jd);
-	CHK(fs->fs_maxbsize, >, FS_MAXCONTIG * fs->fs_bsize, %jd);
-	CHK(fs->fs_bmask, !=, ~(fs->fs_bsize - 1), %#jx);
-	CHK(fs->fs_fmask, !=, ~(fs->fs_fsize - 1), %#jx);
-	CHK(fs->fs_qbmask, !=, ~fs->fs_bmask, %#jx);
-	CHK(fs->fs_qfmask, !=, ~fs->fs_fmask, %#jx);
-	CHK(fs->fs_bshift, !=, ILOG2(fs->fs_bsize), %jd);
-	CHK(fs->fs_fshift, !=, ILOG2(fs->fs_fsize), %jd);
-	CHK(fs->fs_fragshift, !=, ILOG2(fs->fs_frag), %jd);
-	CHK(fs->fs_fsbtodb, !=, ILOG2(fs->fs_fsize / sectorsize), %jd);
-	CHK(fs->fs_old_cgoffset, <, 0, %jd);
-	CHK2(fs->fs_old_cgoffset, >, 0, ~fs->fs_old_cgmask, <, 0, %jd);
-	CHK(fs->fs_old_cgoffset * (~fs->fs_old_cgmask), >, fs->fs_fpg, %jd);
-	CHK(fs->fs_sblkno, !=, roundup(
+	FCHK(fs->fs_sbsize, >, SBLOCKSIZE, %jd);
+	FCHK(fs->fs_maxbsize, <, fs->fs_bsize, %jd);
+	FCHK(powerof2(fs->fs_maxbsize), ==, 0, %jd);
+	FCHK(fs->fs_maxbsize, >, FS_MAXCONTIG * fs->fs_bsize, %jd);
+	FCHK(fs->fs_bmask, !=, ~(fs->fs_bsize - 1), %#jx);
+	FCHK(fs->fs_fmask, !=, ~(fs->fs_fsize - 1), %#jx);
+	FCHK(fs->fs_qbmask, !=, ~fs->fs_bmask, %#jx);
+	FCHK(fs->fs_qfmask, !=, ~fs->fs_fmask, %#jx);
+	FCHK(fs->fs_bshift, !=, ILOG2(fs->fs_bsize), %jd);
+	FCHK(fs->fs_fshift, !=, ILOG2(fs->fs_fsize), %jd);
+	FCHK(fs->fs_fragshift, !=, ILOG2(fs->fs_frag), %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);
-	CHK(fs->fs_cblkno, !=, fs->fs_sblkno +
+	FCHK(fs->fs_cblkno, !=, fs->fs_sblkno +
 	    roundup(howmany(SBLOCKSIZE, fs->fs_fsize), fs->fs_frag), %jd);
-	CHK(fs->fs_iblkno, !=, fs->fs_cblkno + fs->fs_frag, %jd);
-	CHK(fs->fs_dblkno, !=, fs->fs_iblkno + fs->fs_ipg / INOPF(fs), %jd);
-	CHK(fs->fs_cgsize, >, fs->fs_bsize, %jd);
+	FCHK(fs->fs_iblkno, !=, fs->fs_cblkno + fs->fs_frag, %jd);
+	FCHK(fs->fs_dblkno, !=, fs->fs_iblkno + fs->fs_ipg / INOPF(fs), %jd);
+	FCHK(fs->fs_cgsize, >, fs->fs_bsize, %jd);
 	/*
 	 * This test is valid, however older versions of growfs failed
 	 * to correctly update fs_dsize so will fail this test. Thus we
 	 * exclude it from the requirements.
 	 */
 #ifdef notdef
-	CHK(fs->fs_dsize, !=, fs->fs_size - fs->fs_sblkno -
+	WCHK(fs->fs_dsize, !=, fs->fs_size - fs->fs_sblkno -
 		fs->fs_ncg * (fs->fs_dblkno - fs->fs_sblkno) -
 		howmany(fs->fs_cssize, fs->fs_fsize), %jd);
 #endif
-	CHK(fs->fs_metaspace, <, 0, %jd);
-	CHK(fs->fs_metaspace, >, fs->fs_fpg / 2, %jd);
-	CHK(fs->fs_minfree, >, 99, %jd%%);
+	WCHK(fs->fs_metaspace, <, 0, %jd);
+	WCHK(fs->fs_metaspace, >, fs->fs_fpg / 2, %jd);
+	WCHK(fs->fs_minfree, >, 99, %jd%%);
 	maxfilesize = fs->fs_bsize * UFS_NDADDR - 1;
 	for (sizepb = fs->fs_bsize, i = 0; i < UFS_NIADDR; i++) {
 		sizepb *= NINDIR(fs);
 		maxfilesize += sizepb;
 	}
-	CHK(fs->fs_maxfilesize, !=, maxfilesize, %jd);
+	WCHK(fs->fs_maxfilesize, !=, maxfilesize, %jd);
 	/*
 	 * These values have a tight interaction with each other that
 	 * makes it hard to tightly bound them. So we can only check
@@ -462,9 +482,9 @@ validate_sblock(struct fs *fs, int flags)
 	 * that the summary information size is correct and that it starts
 	 * and ends in the data area of the same cylinder group.
 	 */
-	CHK(fs->fs_size, <, 8 * fs->fs_frag, %jd);
-	CHK(fs->fs_size, <=, (fs->fs_ncg - 1) * fs->fs_fpg, %jd);
-	CHK(fs->fs_size, >, fs->fs_ncg * fs->fs_fpg, %jd);
+	FCHK(fs->fs_size, <, 8 * fs->fs_frag, %jd);
+	WCHK(fs->fs_size, <=, (fs->fs_ncg - 1) * fs->fs_fpg, %jd);
+	WCHK(fs->fs_size, >, fs->fs_ncg * fs->fs_fpg, %jd);
 	/*
 	 * If we are not requested to read in the csum data stop here
 	 * as the correctness of the remaining values is only important
@@ -472,12 +492,12 @@ validate_sblock(struct fs *fs, int flags)
 	 */
 	if ((flags & UFS_NOCSUM) != 0)
 		return (error);
-	CHK(fs->fs_csaddr, <, 0, %jd);
-	CHK(fs->fs_cssize, !=,
+	FCHK(fs->fs_csaddr, <, 0, %jd);
+	FCHK(fs->fs_cssize, !=,
 	    fragroundup(fs, fs->fs_ncg * sizeof(struct csum)), %jd);
-	CHK(dtog(fs, fs->fs_csaddr), >, fs->fs_ncg, %jd);
-	CHK(fs->fs_csaddr, <, cgdmin(fs, dtog(fs, fs->fs_csaddr)), %jd);
-	CHK(dtog(fs, fs->fs_csaddr + howmany(fs->fs_cssize, fs->fs_fsize)), >,
+	FCHK(dtog(fs, fs->fs_csaddr), >, fs->fs_ncg, %jd);
+	FCHK(fs->fs_csaddr, <, cgdmin(fs, dtog(fs, fs->fs_csaddr)), %jd);
+	FCHK(dtog(fs, fs->fs_csaddr + howmany(fs->fs_cssize, fs->fs_fsize)), >,
 	    dtog(fs, fs->fs_csaddr), %jd);
 	/*
 	 * With file system clustering it is possible to allocate
@@ -498,10 +518,10 @@ validate_sblock(struct fs *fs, int flags)
 	 * those (mostly 32-bit machines) can (very slowly) handle I/O
 	 * requests that exceed maxphys.
 	 */
-	CHK(fs->fs_maxcontig, <, 0, %jd);
-	CHK(fs->fs_maxcontig, >, MAX(256, maxphys / fs->fs_bsize), %jd);
-	CHK2(fs->fs_maxcontig, ==, 0, fs->fs_contigsumsize, !=, 0, %jd);
-	CHK2(fs->fs_maxcontig, >, 1, fs->fs_contigsumsize, !=,
+	WCHK(fs->fs_maxcontig, <, 0, %jd);
+	WCHK(fs->fs_maxcontig, >, MAX(256, maxphys / fs->fs_bsize), %jd);
+	WCHK2(fs->fs_maxcontig, ==, 0, fs->fs_contigsumsize, !=, 0, %jd);
+	WCHK2(fs->fs_maxcontig, >, 1, fs->fs_contigsumsize, !=,
 	    MIN(fs->fs_maxcontig, FS_MAXCONTIG), %jd);
 	return (error);
 }
diff --git a/sys/ufs/ffs/fs.h b/sys/ufs/ffs/fs.h
index 81cb416b383d..0a546e40e82a 100644
--- a/sys/ufs/ffs/fs.h
+++ b/sys/ufs/ffs/fs.h
@@ -98,8 +98,13 @@
  *    return the superblock. This is used by the bootstrap code to
  *    give the system a chance to come up so that fsck can be run to
  *    correct the problem.
+ *
+ * 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.
  */
 #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_ALTSBLK	0x1000	/* Flag used internally */