[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

[Cluster-devel] [gfs2-utils patch] fsck.gfs2: Add ability to detect journal inode indirect block corruption



Hi,

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.

Regards,

Bob Peterson
Red Hat File Systems

Signed-off-by: Bob Peterson <rpeterso redhat com> 
---
diff --git a/gfs2/fsck/fs_recovery.c b/gfs2/fsck/fs_recovery.c
index d07a515..8b82695 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,33 @@ 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;
+
+			/* 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;
+			rgd.ri.ri_addr = sdp->sb_addr + 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]);
+		}
 		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;
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]