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

Bob Peterson rpeterso at fedoraproject.org
Fri Aug 2 16:07:28 UTC 2013


Gitweb:        http://git.fedorahosted.org/git/?p=cluster.git;a=commitdiff;h=364c3cca8a4ea092b49af95c9babb15e10271661
Commit:        364c3cca8a4ea092b49af95c9babb15e10271661
Parent:        20f19daca1f21428748249e7982134b7a9728777
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:27:40 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.

rhbz#989647
---
 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 d07a515..2e902ea 100644
--- a/gfs2/fsck/fs_recovery.c
+++ b/gfs2/fsck/fs_recovery.c
@@ -10,6 +10,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 84366a2..79ee241 100644
--- a/gfs2/fsck/metawalk.c
+++ b/gfs2/fsck/metawalk.c
@@ -1236,9 +1236,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);
@@ -1284,6 +1287,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) {
@@ -1298,6 +1304,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 7ee49be..05989fe 100644
--- a/gfs2/fsck/util.c
+++ b/gfs2/fsck/util.c
@@ -499,6 +499,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