Re: [PATCH FOR REVIEW] fsck_ffs: Recover from catastrophic damage

From: Xin LI <delphij_at_delphij.net>
Date: Thu, 21 Feb 2008 14:52:06 -0800
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Here is a revised version of patch.

It adds a new '-C' flag to fsck_ffs(8), which causes fsck_ffs(8) to run
in a new 'catastrophic recovery' mode, where more aggressive operations
are done.

All cg clearing operations are now hidden in '-C' mode, and a prompt is
provided so that sysadmin can choose whether to clear the cg.

Other changes:
 - Be more careful while resetting a cg.  Set more fields;
 - Sanity check d_ino in pass2check().  Give fsck_ffs(8) an opportunity
to recover from insane inode number provided by damaged directory entry.

Cheers,
- --
Xin LI <delphij_at_delphij.net>	http://www.delphij.net/
FreeBSD - The Power to Serve!
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.4 (FreeBSD)

iD8DBQFHvgCWi+vbBBjt66ARAjrmAKCvDR/4wWiNp/k+9Jhz6YEhp9fFpgCeLyus
/1BXVmFBI9S0fBdc3YOXmMw=
=sE9G
-----END PGP SIGNATURE-----

Index: fsck.h
===================================================================
RCS file: /home/ncvs/src/sbin/fsck_ffs/fsck.h,v
retrieving revision 1.37
diff -u -p -r1.37 fsck.h
--- fsck.h	31 Oct 2006 22:06:56 -0000	1.37
+++ fsck.h	21 Feb 2008 22:01:44 -0000
_at__at_ -270,6 +270,7 _at__at_ char	yflag;			/* assume a yes response *
 int	bkgrdflag;		/* use a snapshot to run on an active system */
 int	bflag;			/* location of alternate super block */
 int	debug;			/* output debugging info */
+char	catastrophicflag;	/* run in catastrophic mode */
 int	cvtlevel;		/* convert to newer file system format */
 int	bkgrdcheck;		/* determine if background check is possible */
 int	bkgrdsumadj;		/* whether the kernel have ability to adjust superblock summary */
_at__at_ -335,6 +336,7 _at__at_ void		cacheino(union dinode *dp, ino_t i
 void		catch(int);
 void		catchquit(int);
 int		changeino(ino_t dir, const char *name, ino_t newnum);
+void		check_cgmagic(int cg, struct cg *cgp);
 int		chkrange(ufs2_daddr_t blk, int cnt);
 void		ckfini(int markclean);
 int		ckinode(union dinode *dp, struct inodesc *);
Index: fsck_ffs.8
===================================================================
RCS file: /home/ncvs/src/sbin/fsck_ffs/fsck_ffs.8,v
retrieving revision 1.34
diff -u -p -r1.34 fsck_ffs.8
--- fsck_ffs.8	20 Sep 2005 08:02:38 -0000	1.34
+++ fsck_ffs.8	21 Feb 2008 22:26:08 -0000
_at__at_ -38,7 +38,7 _at__at_
 .Nd file system consistency check and interactive repair
 .Sh SYNOPSIS
 .Nm
-.Op Fl BFpfny
+.Op Fl BCFpfny
 .Op Fl b Ar block
 .Op Fl c Ar level
 .Op Fl m Ar mode
_at__at_ -175,6 +175,26 _at__at_ Use the block specified immediately afte
 the super block for the file system.
 An alternate super block is usually located at block 32 for UFS1,
 and block 160 for UFS2.
+.It Fl C
+Run
+.Nm
+in 'catastrophic recovery' mode, which will enable certain aggressive
+operations that can make
+.Nm
+to survive with file systems that has very serious data damage, which
+is an useful last resort when on disk data damage is very serious
+and causes
+.Nm
+to crash otherwise.  Be
+.Em very careful
+using this flag, is dangerous if there are data transmission hazards
+because a false positive cylinder group magic number mismatch could
+cause
+.Em irrevertible data loss!
+.Pp
+This option implies the
+.Fl f
+flag.
 .It Fl c
 Convert the file system to the specified level.
 Note that the level of a file system can only be raised.
Index: fsutil.c
===================================================================
RCS file: /home/ncvs/src/sbin/fsck_ffs/fsutil.c,v
retrieving revision 1.26
diff -u -p -r1.26 fsutil.c
--- fsutil.c	31 Oct 2006 22:06:56 -0000	1.26
+++ fsutil.c	21 Feb 2008 22:47:08 -0000
_at__at_ -301,7 +301,7 _at__at_ ckfini(int markclean)
 	if (havesb && cursnapshot == 0 && sblock.fs_magic == FS_UFS2_MAGIC &&
 	    sblk.b_bno != sblock.fs_sblockloc / dev_bsize &&
 	    !preen && reply("UPDATE STANDARD SUPERBLOCK")) {
-		sblk.b_bno = sblock.fs_sblockloc / dev_bsize;
+		sblk.b_bno = SBLOCK_UFS2 / dev_bsize;
 		sbdirty();
 		flush(fswritefd, &sblk);
 	}
_at__at_ -418,6 +418,32 _at__at_ blwrite(int fd, char *buf, ufs2_daddr_t 
 }
 
 /*
+ * Check cg's magic number.  If catastrophic mode is enabled and the cg's
+ * magic number is bad, offer an option to clear the whole cg.
+ */
+void
+check_cgmagic(int cg, struct cg *cgp)
+{
+
+	if (!cg_chkmagic(cgp)) {
+	    pwarn("CG %d: BAD MAGIC NUMBER\n", cg);
+	    if (catastrophicflag) {
+		if (reply("CLEAR CG")) {
+			memset(cgp, 0, (size_t)sblock.fs_cgsize);
+			cgp->cg_initediblk = sblock.fs_ipg;
+			cgp->cg_old_niblk = sblock.fs_ipg;
+			cgp->cg_old_ncyl = sblock.fs_old_cpg;
+			cgp->cg_cgx = cg;
+			cgp->cg_niblk = sblock.fs_ipg;
+			cgp->cg_ndblk = sblock.fs_size - cgbase(&sblock, cg);
+			cgp->cg_magic = CG_MAGIC;
+		}
+	    } else
+		printf("YOU MAY NEED TO RERUN FSCK WITH -C IF IT CRASHED.\n");
+	}
+}
+
+/*
  * allocate a data block with the specified number of fragments
  */
 ufs2_daddr_t
_at__at_ -441,8 +467,7 _at__at_ allocblk(long frags)
 			}
 			cg = dtog(&sblock, i + j);
 			getblk(&cgblk, cgtod(&sblock, cg), sblock.fs_cgsize);
-			if (!cg_chkmagic(cgp))
-				pfatal("CG %d: BAD MAGIC NUMBER\n", cg);
+			check_cgmagic(cg, cgp);
 			baseblk = dtogd(&sblock, i + j);
 			for (k = 0; k < frags; k++) {
 				setbmap(i + j + k);
Index: inode.c
===================================================================
RCS file: /home/ncvs/src/sbin/fsck_ffs/inode.c,v
retrieving revision 1.38
diff -u -p -r1.38 inode.c
--- inode.c	31 Oct 2006 22:06:56 -0000	1.38
+++ inode.c	21 Feb 2008 21:56:27 -0000
_at__at_ -617,8 +617,7 _at__at_ allocino(ino_t request, int type)
 		return (0);
 	cg = ino_to_cg(&sblock, ino);
 	getblk(&cgblk, cgtod(&sblock, cg), sblock.fs_cgsize);
-	if (!cg_chkmagic(cgp))
-		pfatal("CG %d: BAD MAGIC NUMBER\n", cg);
+	check_cgmagic(cg, cgp);
 	setbit(cg_inosused(cgp), ino % sblock.fs_ipg);
 	cgp->cg_cs.cs_nifree--;
 	switch (type & IFMT) {
Index: main.c
===================================================================
RCS file: /home/ncvs/src/sbin/fsck_ffs/main.c,v
retrieving revision 1.47
diff -u -p -r1.47 main.c
--- main.c	19 Sep 2007 01:24:19 -0000	1.47
+++ main.c	21 Feb 2008 22:02:42 -0000
_at__at_ -81,7 +81,8 _at__at_ main(int argc, char *argv[])
 
 	sync();
 	skipclean = 1;
-	while ((ch = getopt(argc, argv, "b:Bc:dfFm:npy")) != -1) {
+	catastrophicflag = 0;
+	while ((ch = getopt(argc, argv, "b:Bc:CdfFm:npy")) != -1) {
 		switch (ch) {
 		case 'b':
 			skipclean = 0;
_at__at_ -105,6 +106,10 _at__at_ main(int argc, char *argv[])
 			debug++;
 			break;
 
+		case 'C':
+			catastrophicflag = 1;
+			/* FALLTHROUGH */
+
 		case 'f':
 			skipclean = 0;
 			break;
Index: pass1.c
===================================================================
RCS file: /home/ncvs/src/sbin/fsck_ffs/pass1.c,v
retrieving revision 1.43
diff -u -p -r1.43 pass1.c
--- pass1.c	8 Oct 2004 20:44:47 -0000	1.43
+++ pass1.c	20 Feb 2008 07:13:53 -0000
_at__at_ -93,9 +93,11 _at__at_ pass1(void)
 		inumber = c * sblock.fs_ipg;
 		setinodebuf(inumber);
 		getblk(&cgblk, cgtod(&sblock, c), sblock.fs_cgsize);
-		if (sblock.fs_magic == FS_UFS2_MAGIC)
+		if (sblock.fs_magic == FS_UFS2_MAGIC) {
 			inosused = cgrp.cg_initediblk;
-		else
+			if (inosused > sblock.fs_ipg)
+				inosused = sblock.fs_ipg;
+		} else
 			inosused = sblock.fs_ipg;
 		if (got_siginfo) {
 			printf("%s: phase 1: cyl group %d of %d (%d%%)\n",
Index: pass2.c
===================================================================
RCS file: /home/ncvs/src/sbin/fsck_ffs/pass2.c,v
retrieving revision 1.26
diff -u -p -r1.26 pass2.c
--- pass2.c	8 Oct 2004 20:44:47 -0000	1.26
+++ pass2.c	21 Feb 2008 22:31:03 -0000
_at__at_ -242,6 +242,8 _at__at_ pass2check(struct inodesc *idesc)
 	/*
 	 * check for "."
 	 */
+	if (dirp->d_ino > maxino)
+		goto chk2;
 	if (idesc->id_entryno != 0)
 		goto chk1;
 	if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) {
Index: setup.c
===================================================================
RCS file: /home/ncvs/src/sbin/fsck_ffs/setup.c,v
retrieving revision 1.50
diff -u -p -r1.50 setup.c
--- setup.c	31 Oct 2006 22:06:56 -0000	1.50
+++ setup.c	20 Feb 2008 07:13:27 -0000
_at__at_ -349,7 +349,7 _at__at_ readsb(int listerr)
 			      sblock.fs_sblockloc == sblock_try[i])) &&
 			    sblock.fs_ncg >= 1 &&
 			    sblock.fs_bsize >= MINBSIZE &&
-			    sblock.fs_bsize >= sizeof(struct fs))
+			    sblock.fs_sbsize >= roundup(sizeof(struct fs), dev_bsize))
 				break;
 		}
 		if (sblock_try[i] == -1) {
Received on Thu Feb 21 2008 - 21:52:18 UTC

This archive was generated by hypermail 2.4.0 : Wed May 19 2021 - 11:39:27 UTC