gfs2-utils: master - fsck.gfs2: Add ability to detect journal inode indirect block corruption

Bob Peterson rpeterso at fedoraproject.org
Fri Aug 2 15:47:08 UTC 2013


Gitweb:        http://git.fedorahosted.org/git/?p=gfs2-utils.git;a=commitdiff;h=8deeb914420558e90a6ec27e97af7980f6a973ae
Commit:        8deeb914420558e90a6ec27e97af7980f6a973ae
Parent:        9962ab3034e6dfc981d9f6232e2af689d7dc3257
Author:        Bob Peterson <rpeterso at redhat.com>
AuthorDate:    Tue Jul 30 14:01:40 2013 -0500
Committer:     Bob Peterson <rpeterso at redhat.com>
CommitterDate: Thu Aug 1 20:29:31 2013 -0500

fsck.gfs2: Add ability to detect journal inode indirect block corruption

This patch gives the journal replay portion of fsck.gfs2 the ability to
do basic sanity checks on the indirect blocks associated with journals.
If corrupt indirect blocks are found, the journal is considered damaged,
and should be rebuilt by pass1.
---
 gfs2/fsck/fs_recovery.c |   90 ++++++++++++++++++++++++++++++++++++++++++++++-
 gfs2/fsck/metawalk.c    |   11 +++++-
 gfs2/fsck/metawalk.h    |    1 +
 gfs2/fsck/util.c        |    2 +
 4 files changed, 102 insertions(+), 2 deletions(-)

diff --git a/gfs2/fsck/fs_recovery.c b/gfs2/fsck/fs_recovery.c
index 71d9909..41d575a 100644
--- a/gfs2/fsck/fs_recovery.c
+++ b/gfs2/fsck/fs_recovery.c
@@ -12,6 +12,7 @@
 #include "fsck.h"
 #include "fs_recovery.h"
 #include "libgfs2.h"
+#include "metawalk.h"
 #include "util.h"
 
 #define JOURNAL_NAME_SIZE 16
@@ -559,6 +560,63 @@ reinit:
 	return error;
 }
 
+/* We can't use the rangecheck function from pass1 because we haven't gone
+ * through initialization properly yet. */
+static int rangecheck_jblock(struct gfs2_inode *ip, uint64_t block)
+{
+	if((block > ip->i_sbd->fssize) || (block <= ip->i_sbd->sb_addr)) {
+		log_info( _("Bad block pointer (out of range) found in "
+			    "journal inode %lld (0x%llx).\n"),
+			  (unsigned long long)ip->i_di.di_num.no_addr,
+			  (unsigned long long)ip->i_di.di_num.no_addr);
+		return meta_error; /* Exits check_metatree quicker */
+	}
+	return meta_is_good;
+}
+
+static int rangecheck_jmeta(struct gfs2_inode *ip, uint64_t block,
+			       struct gfs2_buffer_head **bh, int h,
+			       int *is_valid, int *was_duplicate,
+			       void *private)
+{
+	int rc;
+
+	*bh = NULL;
+	*was_duplicate = 0;
+	*is_valid = 0;
+	rc = rangecheck_jblock(ip, block);
+	if (rc == meta_is_good) {
+		*bh = bread(ip->i_sbd, block);
+		*is_valid = (gfs2_check_meta(*bh, GFS2_METATYPE_IN) == 0);
+		if (!(*is_valid)) {
+			log_err( _("Journal at block %lld (0x%llx) has a bad "
+				   "indirect block pointer %lld (0x%llx) "
+				   "(points to something that is not an "
+				   "indirect block).\n"),
+				 (unsigned long long)ip->i_di.di_num.no_addr,
+				 (unsigned long long)ip->i_di.di_num.no_addr,
+				 (unsigned long long)block,
+				 (unsigned long long)block);
+			brelse(*bh);
+			return meta_skip_further;
+		}
+	}
+	return rc;
+}
+
+static int rangecheck_jdata(struct gfs2_inode *ip, uint64_t metablock,
+			   uint64_t block, void *private)
+{
+	return rangecheck_jblock(ip, block);
+}
+
+struct metawalk_fxns rangecheck_journal = {
+	.private = NULL,
+	.invalid_meta_is_fatal = 1,
+	.check_metalist = rangecheck_jmeta,
+	.check_data = rangecheck_jdata,
+};
+
 /*
  * replay_journals - replay the journals
  * sdp: the super block
@@ -583,9 +641,39 @@ int replay_journals(struct gfs2_sbd *sdp, int preen, int force_check,
 	sdp->jsize = GFS2_DEFAULT_JSIZE;
 
 	for(i = 0; i < sdp->md.journals; i++) {
+		if (sdp->md.journal[i]) {
+			struct rgrp_tree rgd;
+			struct gfs2_bitmap bits;
+
+			/* The real rgrp tree hasn't been built at this point,
+			 * so we need to dummy one up that covers the whole
+			 * file system so basic functions in check_metatree
+			 * don't segfault. */
+			rgd.start = sdp->sb_addr + 1;
+			rgd.length = 1;
+			rgd.bh = NULL;
+			bits.bi_start = 0;
+			bits.bi_len = sdp->fssize / GFS2_NBBY;
+			rgd.bits = &bits;
+			rgd.ri.ri_addr = sdp->sb_addr + 1;
+			rgd.ri.ri_length = 1;
+			rgd.ri.ri_data0 = sdp->sb_addr + 2;
+			rgd.ri.ri_data = sdp->fssize - (sdp->sb_addr + 2);
+
+			sdp->rgtree.osi_node = (struct osi_node *)&rgd;
+			error = check_metatree(sdp->md.journal[i],
+					       &rangecheck_journal);
+			sdp->rgtree.osi_node = NULL;
+			if (error)
+				/* Don't use fsck_inode_put here because it's a
+				   system file and we need to dismantle it. */
+				inode_put(&sdp->md.journal[i]);
+			error = 0; /* bad journal is non-fatal */
+		}
 		if (!sdp->md.journal[i]) {
 			log_err(_("File system journal \"journal%d\" is "
-				  "missing: pass1 will try to recreate it.\n"),
+				  "missing or corrupt: pass1 will try to "
+				  "recreate it.\n"),
 				i);
 			continue;
 		}
diff --git a/gfs2/fsck/metawalk.c b/gfs2/fsck/metawalk.c
index 22f3334..b8cef87 100644
--- a/gfs2/fsck/metawalk.c
+++ b/gfs2/fsck/metawalk.c
@@ -1238,9 +1238,12 @@ static int build_and_check_metalist(struct gfs2_inode *ip, osi_list_t *mlp,
 		for (tmp = prev_list->next; tmp != prev_list; tmp = tmp->next){
 			bh = osi_list_entry(tmp, struct gfs2_buffer_head,
 					    b_altlist);
+			if (gfs2_check_meta(bh, iblk_type)) {
+				if (pass->invalid_meta_is_fatal)
+					return meta_error;
 
-			if (gfs2_check_meta(bh, iblk_type))
 				continue;
+			}
 
 			/* Now check the metadata itself */
 			for (ptr = (uint64_t *)(bh->b_data + head_size);
@@ -1286,6 +1289,9 @@ static int build_and_check_metalist(struct gfs2_inode *ip, osi_list_t *mlp,
 						     "%llu (0x%llx)\n"),
 						   (unsigned long long)block,
 						   (unsigned long long)block);
+					if (pass->invalid_meta_is_fatal)
+						return meta_error;
+
 					continue;
 				}
 				if (was_duplicate) {
@@ -1300,6 +1306,9 @@ static int build_and_check_metalist(struct gfs2_inode *ip, osi_list_t *mlp,
 						     "%lld (0x%llx)\n"),
 						   (unsigned long long)block,
 						   (unsigned long long)block);
+					if (pass->invalid_meta_is_fatal)
+						return meta_error;
+
 					continue;
 				}
 				if (!nbh)
diff --git a/gfs2/fsck/metawalk.h b/gfs2/fsck/metawalk.h
index a5a51c2..5e30bfe 100644
--- a/gfs2/fsck/metawalk.h
+++ b/gfs2/fsck/metawalk.h
@@ -93,6 +93,7 @@ enum meta_check_rc {
  */
 struct metawalk_fxns {
 	void *private;
+	int invalid_meta_is_fatal;
 	int (*check_leaf_depth) (struct gfs2_inode *ip, uint64_t leaf_no,
 				 int ref_count, struct gfs2_buffer_head *lbh);
 	int (*check_leaf) (struct gfs2_inode *ip, uint64_t block,
diff --git a/gfs2/fsck/util.c b/gfs2/fsck/util.c
index a701d82..408d89a 100644
--- a/gfs2/fsck/util.c
+++ b/gfs2/fsck/util.c
@@ -579,6 +579,8 @@ int gfs2_blockmap_set(struct gfs2_bmap *bmap, uint64_t bblock,
 	static unsigned char *byte;
 	static uint64_t b;
 
+	if (!bmap)
+		return 0;
 	if (bblock > bmap->size)
 		return -1;
 


More information about the cluster-commits mailing list