git: da86e7a20dc4 - main - Skip Pass 5 in fsck_ffs(8) when corrupt cylinder groups remain unfixed.

From: Kirk McKusick <mckusick_at_FreeBSD.org>
Date: Tue, 18 Apr 2023 23:14:01 UTC
The branch main has been updated by mckusick:

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

commit da86e7a20dc4a4b17e8d9e7630ed9b675cf71702
Author:     Kirk McKusick <mckusick@FreeBSD.org>
AuthorDate: 2023-04-18 23:10:49 +0000
Commit:     Kirk McKusick <mckusick@FreeBSD.org>
CommitDate: 2023-04-18 23:13:26 +0000

    Skip Pass 5 in fsck_ffs(8) when corrupt cylinder groups remain unfixed.
    
    Pass 1 of fsck_ffs checks the integrity of all the cylinder groups.
    If any are found to have been corrupted it offers to rebuild them.
    Pass 5 then makes a second pass over the cylinder groups to validate
    their block and inode maps. Pass 5 assumes that the cylinder groups
    are not corrupted and can segment fault if they are corrupted. Rather
    than rerunning the corruption checks a second time in pass 5, this
    fix keeps track whether any corrupt cylinder groups were found but not
    fixed in pass 1 either due to running with the -n flag or by explicitly
    answering `no' when asked whether to fix a corrupted cylinder group.
    If any corrupted cylinder groups remain after pass 1, fsck_ffs will
    decline to run pass 5. Instead it marks the filesystem as unclean
    so that fsck_ffs will need to be run again before the filesystem can
    be mounted.
    
    This patch cleans up and documents the return value from check_cgmagic().
    It also renames the variable / parameter "rebuildcg" to "rebuiltcg".
    This parameter describes whether the cylinder group has been rebuilt
    rather than whether it should be rebuilt.
    
    Reported by: Chuck Silvers
    Reviewed by: Chuck Silvers
    MFC after:   1 week
---
 sbin/fsck_ffs/fsck.h   |  3 ++-
 sbin/fsck_ffs/fsutil.c |  8 ++++++--
 sbin/fsck_ffs/globs.c  |  2 ++
 sbin/fsck_ffs/inode.c  |  4 ++--
 sbin/fsck_ffs/main.c   |  9 +++++++--
 sbin/fsck_ffs/pass1.c  | 30 +++++++++++++++++-------------
 6 files changed, 36 insertions(+), 20 deletions(-)

diff --git a/sbin/fsck_ffs/fsck.h b/sbin/fsck_ffs/fsck.h
index 43e826e13b77..95dccf0d672d 100644
--- a/sbin/fsck_ffs/fsck.h
+++ b/sbin/fsck_ffs/fsck.h
@@ -346,6 +346,7 @@ extern off_t bflag;		/* location of alternate super block */
 extern int bkgrdflag;		/* use a snapshot to run on an active system */
 extern char *blockmap;		/* ptr to primary blk allocation map */
 extern char *cdevname;		/* name of device being checked */
+extern int cgheader_corrupt;	/* one or more CG headers are corrupt */
 extern char ckclean;		/* only do work if not cleanly unmounted */
 extern int ckhashadd;		/* check hashes to be added */
 extern char *copybuf;		/* buffer to copy snapshot blocks */
@@ -493,7 +494,7 @@ int		ftypeok(union dinode *dp);
 void		getblk(struct bufarea *bp, ufs2_daddr_t blk, long size);
 struct bufarea *getdatablk(ufs2_daddr_t blkno, long size, int type);
 struct inoinfo *getinoinfo(ino_t inumber);
-union dinode   *getnextinode(ino_t inumber, int rebuildcg);
+union dinode   *getnextinode(ino_t inumber, int rebuiltcg);
 void		getpathname(char *namebuf, ino_t curdir, ino_t ino);
 void		ginode(ino_t, struct inode *);
 void		gjournal_check(const char *filesys);
diff --git a/sbin/fsck_ffs/fsutil.c b/sbin/fsck_ffs/fsutil.c
index 2b915d28aa26..1df52b7cfdc9 100644
--- a/sbin/fsck_ffs/fsutil.c
+++ b/sbin/fsck_ffs/fsutil.c
@@ -988,6 +988,10 @@ blzero(int fd, ufs2_daddr_t blk, long size)
 /*
  * Verify cylinder group's magic number and other parameters.  If the
  * test fails, offer an option to rebuild the whole cylinder group.
+ *
+ * Return 1 if the cylinder group is good or if repair is requested
+ * and is completed successfully. Return 0 if it is bad or if a repair
+ * has been requested but is not completed successfully.
  */
 #undef CHK
 #define CHK(lhs, op, rhs, fmt)						\
@@ -1080,7 +1084,7 @@ check_cgmagic(int cg, struct bufarea *cgbp, int request_rebuild)
 	if (!reply("REBUILD CYLINDER GROUP")) {
 		printf("YOU WILL NEED TO RERUN FSCK.\n");
 		rerun = 1;
-		return (1);
+		return (0);
 	}
 	/*
 	 * Zero out the cylinder group and then initialize critical fields.
@@ -1123,7 +1127,7 @@ check_cgmagic(int cg, struct bufarea *cgbp, int request_rebuild)
 	}
 	cgp->cg_ckhash = calculate_crc32c(~0L, (void *)cgp, sblock.fs_cgsize);
 	cgdirty(cgbp);
-	return (0);
+	return (1);
 }
 
 /*
diff --git a/sbin/fsck_ffs/globs.c b/sbin/fsck_ffs/globs.c
index 306944fa95b9..bce499754301 100644
--- a/sbin/fsck_ffs/globs.c
+++ b/sbin/fsck_ffs/globs.c
@@ -110,6 +110,7 @@ ino_t	lfdir;			/* lost & found directory inode number */
 const char *lfname;		/* lost & found directory name */
 int	lfmode;			/* lost & found directory creation mode */
 ufs2_daddr_t n_blks;		/* number of blocks in use */
+int	cgheader_corrupt;	/* one or more CG headers are corrupt */
 ino_t n_files;			/* number of files in use */
 volatile sig_atomic_t	got_siginfo;	/* received a SIGINFO */
 volatile sig_atomic_t	got_sigalarm;	/* received a SIGALRM */
@@ -164,6 +165,7 @@ fsckinit(void)
 	lfmode = 0700;
 	n_blks = 0;
 	n_files = 0;
+	cgheader_corrupt = 0;
 	got_siginfo = 0;
 	got_sigalarm = 0;
 	bzero(&zino.dp1, sizeof(struct ufs1_dinode));
diff --git a/sbin/fsck_ffs/inode.c b/sbin/fsck_ffs/inode.c
index 37b0f9ebc1b1..2d563f617a56 100644
--- a/sbin/fsck_ffs/inode.c
+++ b/sbin/fsck_ffs/inode.c
@@ -510,7 +510,7 @@ static ino_t nextinum, lastvalidinum;
 static long readcount, readpercg, fullcnt, inobufsize, partialcnt, partialsize;
 
 union dinode *
-getnextinode(ino_t inumber, int rebuildcg)
+getnextinode(ino_t inumber, int rebuiltcg)
 {
 	int j;
 	long size;
@@ -569,7 +569,7 @@ getnextinode(ino_t inumber, int rebuildcg)
 			dirty(&inobuf);
 		}
 	}
-	if (rebuildcg && (char *)dp == inobuf.b_un.b_buf) {
+	if (rebuiltcg && (char *)dp == inobuf.b_un.b_buf) {
 		/*
 		 * Try to determine if we have reached the end of the
 		 * allocated inodes.
diff --git a/sbin/fsck_ffs/main.c b/sbin/fsck_ffs/main.c
index f7d995a54d1b..2bd61dda7030 100644
--- a/sbin/fsck_ffs/main.c
+++ b/sbin/fsck_ffs/main.c
@@ -490,8 +490,13 @@ checkfilesys(char *filesys)
 	if (preen == 0)
 		printf("** Phase 5 - Check Cyl groups\n");
 	snapflush(std_checkblkavail);
-	pass5();
-	IOstats("Pass5");
+	if (cgheader_corrupt) {
+		printf("PHASE 5 SKIPPED DUE TO CORRUPT CYLINDER GROUP "
+		    "HEADER(S)\n");
+	} else {
+		pass5();
+		IOstats("Pass5");
+	}
 
 	/*
 	 * print out summary statistics
diff --git a/sbin/fsck_ffs/pass1.c b/sbin/fsck_ffs/pass1.c
index afe9f7f2b789..55233e0bc4f4 100644
--- a/sbin/fsck_ffs/pass1.c
+++ b/sbin/fsck_ffs/pass1.c
@@ -56,7 +56,7 @@ static ufs2_daddr_t badblk;
 static ufs2_daddr_t dupblk;
 static ino_t lastino;		/* last inode in use */
 
-static int checkinode(ino_t inumber, struct inodesc *, int rebuildcg);
+static int checkinode(ino_t inumber, struct inodesc *, int rebuiltcg);
 
 void
 pass1(void)
@@ -68,7 +68,7 @@ pass1(void)
 	ino_t inumber, inosused, mininos;
 	ufs2_daddr_t i, cgd;
 	u_int8_t *cp;
-	int c, rebuildcg;
+	int c, rebuiltcg;
 
 	badblk = dupblk = lastino = 0;
 
@@ -99,10 +99,14 @@ pass1(void)
 		inumber = c * sblock.fs_ipg;
 		cgbp = cglookup(c);
 		cgp = cgbp->b_un.b_cg;
-		rebuildcg = 0;
-		if (!check_cgmagic(c, cgbp, 1))
-			rebuildcg = 1;
-		if (!rebuildcg && sblock.fs_magic == FS_UFS2_MAGIC) {
+		rebuiltcg = 0;
+		if (!check_cgmagic(c, cgbp, 0)) {
+			if (!check_cgmagic(c, cgbp, 1))
+				cgheader_corrupt = 1;
+			else
+				rebuiltcg = 1;
+		}
+		if (!rebuiltcg && sblock.fs_magic == FS_UFS2_MAGIC) {
 			inosused = cgp->cg_initediblk;
 			if (inosused > sblock.fs_ipg) {
 				pfatal("Too many initialized inodes (%ju > %d) "
@@ -132,7 +136,7 @@ pass1(void)
 		 * to find the inodes that are really in use, and then
 		 * read only those inodes in from disk.
 		 */
-		if ((preen || inoopt) && usedsoftdep && !rebuildcg) {
+		if ((preen || inoopt) && usedsoftdep && !rebuiltcg) {
 			cp = &cg_inosused(cgp)[(inosused - 1) / CHAR_BIT];
 			for ( ; inosused != 0; cp--) {
 				if (*cp == 0) {
@@ -169,7 +173,7 @@ pass1(void)
 		setinodebuf(c, inosused);
 		for (i = 0; i < inosused; i++, inumber++) {
 			if (inumber < UFS_ROOTINO) {
-				(void)getnextinode(inumber, rebuildcg);
+				(void)getnextinode(inumber, rebuiltcg);
 				continue;
 			}
 			/*
@@ -178,7 +182,7 @@ pass1(void)
 			 * We always keep trying until we get to the minimum
 			 * valid number for this cylinder group.
 			 */
-			if (checkinode(inumber, &idesc, rebuildcg) == 0 &&
+			if (checkinode(inumber, &idesc, rebuiltcg) == 0 &&
 			    i > cgp->cg_initediblk)
 				break;
 		}
@@ -189,7 +193,7 @@ pass1(void)
 		 * fewer in use.
 		 */
 		mininos = roundup(inosused + INOPB(&sblock), INOPB(&sblock));
-		if (inoopt && !preen && !rebuildcg &&
+		if (inoopt && !preen && !rebuiltcg &&
 		    sblock.fs_magic == FS_UFS2_MAGIC &&
 		    cgp->cg_initediblk > 2 * INOPB(&sblock) &&
 		    mininos < cgp->cg_initediblk) {
@@ -209,7 +213,7 @@ pass1(void)
 			inosused = 0;
 		else
 			inosused = lastino - (c * sblock.fs_ipg);
-		if (rebuildcg && inosused > cgp->cg_initediblk &&
+		if (rebuiltcg && inosused > cgp->cg_initediblk &&
 		    sblock.fs_magic == FS_UFS2_MAGIC) {
 			cgp->cg_initediblk = roundup(inosused, INOPB(&sblock));
 			pwarn("CYLINDER GROUP %d: FOUND %d VALID INODES\n", c,
@@ -242,7 +246,7 @@ pass1(void)
 }
 
 static int
-checkinode(ino_t inumber, struct inodesc *idesc, int rebuildcg)
+checkinode(ino_t inumber, struct inodesc *idesc, int rebuiltcg)
 {
 	struct inode ip;
 	union dinode *dp;
@@ -252,7 +256,7 @@ checkinode(ino_t inumber, struct inodesc *idesc, int rebuildcg)
 	intmax_t size, fixsize;
 	int j, ret, offset;
 
-	if ((dp = getnextinode(inumber, rebuildcg)) == NULL) {
+	if ((dp = getnextinode(inumber, rebuiltcg)) == NULL) {
 		pfatal("INVALID INODE");
 		goto unknown;
 	}