Skip to contentSkip to conversation

Misc patches regarding corrupt rindex handling

PATCH New Bob Peterson 2 participants 15 messages Feb 25, 2016 – Mar 21, 2016

lore
Share & tools
lore
#0 lore raw

cover Misc patches regarding corrupt rindex handling

Hi,

These patches are probably best handled as separate items, rather than as a set, but I didn't want to send out 11 separate emails.

This is a collection of patches I developed while debugging some rather complex problems involving the rebuilding of corrupt or missing rindex system files by fsck.gfs2. It also fixes some problems whereby gfs2_edit or other utils crash and burn due to corrupt rindex files.

Regards,

Bob Peterson
Red Hat File Systems

---
Bob Peterson (11):
libgfs2: Backport rbtree fixes from upstream
libgfs2: Check for obvious corruption reading rindex
libgfs2: Change rgrp counts to be uint64_t
gfs2_edit: Don't reference an empty rgrp tree
fsck.gfs2: Read jindex before making rindex repairs fsck.gfs2: better reporting of false positive rgrp identification fsck.gfs2: Minor reformatting
fsck.gfs2: Ditch variable rgcount_from_index
fsck.gfs2: Add ability to fix rindex file size
fsck.gfs2: Do not try to overrun the max rgrps that fit inside rindex fsck.gfs2: Detect multiple rgrp grow segments

 gfs2/edit/hexedit.c     |   9 +-
 gfs2/fsck/fs_recovery.c | 138 +++++++++++++++++++
 gfs2/fsck/fs_recovery.h |   1 +
 gfs2/fsck/initialize.c  | 142 +-------------------
 gfs2/fsck/rgrepair.c    | 349 ++++++++++++++++++++++++++++++++++--------------
 gfs2/include/osi_tree.h |  51 ++++---
 gfs2/libgfs2/gfs2l.c    |   2 +-
 gfs2/libgfs2/libgfs2.h  |   3 +-
 gfs2/libgfs2/rgrp.c     |  30 +++--
 gfs2/libgfs2/super.c    |   8 +-
 10 files changed, 457 insertions(+), 276 deletions(-)
Signature · 1 line
2.5.0
#1 lore raw

PATCH 1/11 libgfs2: Backport rbtree fixes from upstream

This patch changes the osi_tree.h include to more closely match the current rbtree implementation in e2fsprogs.

---
 gfs2/include/osi_tree.h | 51 +++++++++++++++++++++++++++++--------------------
 1 file changed, 30 insertions(+), 21 deletions(-)
gfs2/include/osi_tree.h +30 -21
index f0d0768..eca04a0 100644
@@ -12,7 +12,6 @@ struct osi_node {
 #define	OSI_BLACK	1
 	struct osi_node *osi_left;
 	struct osi_node *osi_right;
-	struct osi_node *osi_parent;
 };
 
 #define osi_parent(r)   ((struct osi_node *)((r)->osi_parent_color & ~3))
@@ -21,22 +20,25 @@ struct osi_node {
 #define osi_is_black(r) osi_color(r)
 #define osi_set_red(r)  do { (r)->osi_parent_color &= ~1; } while (0)
 #define osi_set_black(r)  do { (r)->osi_parent_color |= 1; } while (0)
+#define OSI_EMPTY_NODE(node) (osi_parent(node) == node)
 
 struct osi_root
 {
 	struct osi_node *osi_node;
 };
 
-static inline void osi_set_color(struct osi_node *rb, int color)
-{
-	rb->osi_parent_color = (rb->osi_parent_color & ~1) | color;
-}
+#define OSI_EMPTY_ROOT(root)  ((root)->osi_node == NULL)
 
 static inline void osi_set_parent(struct osi_node *rb, struct osi_node *p)
 {
         rb->osi_parent_color = (rb->osi_parent_color & 3) | (unsigned long)p;
 }
 
+static inline void osi_set_color(struct osi_node *rb, int color)
+{
+	rb->osi_parent_color = (rb->osi_parent_color & ~1) | color;
+}
+
 static inline void osi_link_node(struct osi_node *node,
 				 struct osi_node *parent,
 				 struct osi_node **osi_link)
@@ -245,33 +247,34 @@ static inline void osi_erase(struct osi_node *node, struct osi_root *root)
 		node = node->osi_right;
 		while ((left = node->osi_left) != NULL)
 			node = left;
+
+		if (osi_parent(old)) {
+			if (osi_parent(old)->osi_left == old)
+				osi_parent(old)->osi_left = node;
+			else
+				osi_parent(old)->osi_right = node;
+		} else
+			root->osi_node = node;
+
 		child = node->osi_right;
 		parent = osi_parent(node);
 		color = osi_color(node);
 
-		if (child)
-			osi_set_parent(child, parent);
 		if (parent == old) {
-			parent->osi_right = child;
 			parent = node;
-		} else
+		} else {
+			if (child)
+				osi_set_parent(child, parent);
 			parent->osi_left = child;
 
+			node->osi_right = old->osi_right;
+			osi_set_parent(old->osi_right, node);
+		}
+
 		node->osi_parent_color = old->osi_parent_color;
-		node->osi_right = old->osi_right;
 		node->osi_left = old->osi_left;
-
-		if (osi_parent(old)) {
-			if (osi_parent(old)->osi_left == old)
-				osi_parent(old)->osi_left = node;
-			else
-				osi_parent(old)->osi_right = node;
-		} else
-			root->osi_node = node;
-
 		osi_set_parent(old->osi_left, node);
-		if (old->osi_right)
-			osi_set_parent(old->osi_right, node);
+
 		goto color;
 	}
 
@@ -326,6 +329,9 @@ static inline struct osi_node *osi_next(struct osi_node *node)
 {
 	struct osi_node *parent;
 
+	if (OSI_EMPTY_NODE(node))
+		return NULL;
+
 	/* If we have a right-hand child, go down and then left as far
 	   as we can. */
 	if (node->osi_right) {
@@ -351,6 +357,9 @@ static inline struct osi_node *osi_prev(struct osi_node *node)
 {
 	struct osi_node *parent;
 
+	if (OSI_EMPTY_NODE(node))
+		return NULL;
+
 	/* If we have a left-hand child, go down and then right as far
 	   as we can. */
 	if (node->osi_left) {
Signature · 1 line
2.5.0
#2 lore raw

PATCH 2/11 libgfs2: Check for obvious corruption reading rindex

In reply to Bob Peterson

This patch changes function rindex_read so that it can detect and bypass obvious major corruption in the file. Trying to insert highly corrupted entries into the rgrp tree is counterproductive and only gets us into trouble later.

---
 gfs2/libgfs2/rgrp.c  | 10 ++++++++--
 gfs2/libgfs2/super.c |  4 +++-
 2 files changed, 11 insertions(+), 3 deletions(-)
gfs2/libgfs2/rgrp.c +8 -2
index c20aa1f..c5671f0 100644
@@ -71,8 +71,10 @@ int gfs2_compute_bitstructs(const uint32_t bsize, struct rgrp_tree *rgd)
 
 	return 0;
 errbits:
-	if (ownbits)
+	if (ownbits) {
 		free(rgd->bits);
+		rgd->bits = NULL;
+	}
 	return -1;
 }
 
@@ -197,8 +199,10 @@ void gfs2_rgrp_relse(struct rgrp_tree *rgd)
 {
 	int x, length = rgd->ri.ri_length;
 
+	if (rgd->bits == NULL)
+		return;
 	for (x = 0; x < length; x++) {
-		if (rgd->bits[x].bi_bh) {
+		if (rgd->bits[x].bi_bh && rgd->bits[x].bi_bh->b_data) {
 			brelse(rgd->bits[x].bi_bh);
 			rgd->bits[x].bi_bh = NULL;
 		}
@@ -241,6 +245,8 @@ void gfs2_rgrp_free(struct osi_root *rgrp_tree)
 	struct osi_node *n;
 	struct gfs2_sbd *sdp = NULL;
 
+	if (OSI_EMPTY_ROOT(rgrp_tree))
+		return;
 	while ((n = osi_first(rgrp_tree))) {
 		rgd = (struct rgrp_tree *)n;
 
gfs2/libgfs2/super.c +3 -1
index 73354ff..9b26cf4 100644
@@ -169,7 +169,7 @@ int rindex_read(struct gfs2_sbd *sdp, int fd, int *count1, int *sane)
 		if (gfs2_check_range(sdp, ri.ri_addr) != 0) {
 			*sane = 0;
 			if (prev_rgd == NULL)
-				return -1;
+				continue;
 			ri.ri_addr = prev_rgd->ri.ri_addr + prev_rgd->length;
 		}
 		rgd = rgrp_insert(&sdp->rgtree, ri.ri_addr);
@@ -206,6 +206,8 @@ int rindex_read(struct gfs2_sbd *sdp, int fd, int *count1, int *sane)
 	}
 	if (prev_rgd)
 		prev_rgd->length = rgrp_size(prev_rgd);
+	if (*count1 == 0)
+		return -1;
 	return 0;
 }
 
Signature · 1 line
2.5.0
#3 lore raw

PATCH 3/11 libgfs2: Change rgrp counts to be uint64_t

In reply to Bob Peterson

This patch changes our rgrp counts from int to uint64_t.

---
 gfs2/edit/hexedit.c    | 3 ++-
 gfs2/libgfs2/gfs2l.c   | 2 +-
 gfs2/libgfs2/libgfs2.h | 3 ++-
 gfs2/libgfs2/super.c   | 4 ++--
 4 files changed, 7 insertions(+), 5 deletions(-)
gfs2/edit/hexedit.c +2 -1
index 5adf7c5..606bb5a 100644
@@ -1018,7 +1018,8 @@ static void read_superblock(int fd)
 static int read_rindex(void)
 {
 	struct gfs2_rindex *ri;
-	int count, sane;
+	uint64_t count;
+	int sane;
 
 	sbd.fssize = sbd.device.length;
 	if (sbd.md.riinode) /* If we found the rindex */
gfs2/libgfs2/gfs2l.c +1 -1
index 5d9afd8..9c63ec4 100644
@@ -101,7 +101,7 @@ static int openfs(const char *path, struct gfs2_sbd *sdp)
 	int fd;
 	int ret;
 	int sane;
-	int count;
+	uint64_t count;
 
 	fd = open(path, O_RDWR);
 	if (fd < 0) {
gfs2/libgfs2/libgfs2.h +2 -1
index 12d57c7..7c87884 100644
@@ -701,7 +701,8 @@ extern int build_quota_change(struct gfs2_inode *per_node, unsigned int j);
 /* super.c */
 extern int check_sb(struct gfs2_sb *sb);
 extern int read_sb(struct gfs2_sbd *sdp);
-extern int rindex_read(struct gfs2_sbd *sdp, int fd, int *count1, int *sane);
+extern int rindex_read(struct gfs2_sbd *sdp, int fd, uint64_t *count1,
+		       int *sane);
 extern int ri_update(struct gfs2_sbd *sdp, int fd, int *rgcount, int *sane);
 extern int write_sb(struct gfs2_sbd *sdp);
 
gfs2/libgfs2/super.c +2 -2
index 9b26cf4..81834b3 100644
@@ -137,7 +137,7 @@ int read_sb(struct gfs2_sbd *sdp)
  *
  * Returns: 0 on success, -1 on failure
  */
-int rindex_read(struct gfs2_sbd *sdp, int fd, int *count1, int *sane)
+int rindex_read(struct gfs2_sbd *sdp, int fd, uint64_t *count1, int *sane)
 {
 	unsigned int rg;
 	int error;
@@ -250,7 +250,7 @@ static int __ri_update(struct gfs2_sbd *sdp, int fd, int *rgcount, int *sane,
 {
 	struct rgrp_tree *rgd;
 	struct gfs2_rindex *ri;
-	int count1 = 0, count2 = 0;
+	uint64_t count1 = 0, count2 = 0;
 	uint64_t errblock = 0;
 	uint64_t rmax = 0;
 	struct osi_node *n, *next = NULL;
Signature · 1 line
2.5.0
#4 lore raw

PATCH 4/11 gfs2_edit: Don't reference an empty rgrp tree

In reply to Bob Peterson

This patch adds a check to function read_rindex in gfs2_edit which makes sure the rgrp rb_tree is not empty before dereferencing it.

---
 gfs2/edit/hexedit.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)
gfs2/edit/hexedit.c +4 -2
index 606bb5a..1fea017 100644
@@ -1025,8 +1025,10 @@ static int read_rindex(void)
 	if (sbd.md.riinode) /* If we found the rindex */
 		rindex_read(&sbd, 0, &count, &sane);
 
-	ri = &((struct rgrp_tree *)osi_last(&sbd.rgtree))->ri;
-	sbd.fssize = ri->ri_data0 + ri->ri_data;
+	if (!OSI_EMPTY_ROOT(&sbd.rgtree)) {
+		ri = &((struct rgrp_tree *)osi_last(&sbd.rgtree))->ri;
+		sbd.fssize = ri->ri_data0 + ri->ri_data;
+	}
 	return 0;
 }
 
Signature · 1 line
2.5.0
#5 lore raw

PATCH 5/11 fsck.gfs2: Read jindex before making rindex repairs

In reply to Bob Peterson

In most cases, the rindex needs to be read into memory in case the journals or jindex are corrupt and need repairs. However, in some rare cases, the rindex needs repairs, and in the rindex repair code it needs to read in the jindex and journals in order to filter out rgrp records that appear in the journals. This prevents the rgrp records inside journals from being treated as real rgrps, rather than false-positives.

This patch also fixes a segfault in the rgrp code for cases of extremely corrupt rindex files where the rgrp has no buffers.

---
 gfs2/fsck/fs_recovery.c | 138 +++++++++++++++++++++++++++++++++++++++++++++++
 gfs2/fsck/fs_recovery.h |   1 +
 gfs2/fsck/initialize.c  | 140 ++----------------------------------------------
 gfs2/fsck/rgrepair.c    |  38 ++++++++++---
 gfs2/libgfs2/rgrp.c     |  20 +++----
 5 files changed, 186 insertions(+), 151 deletions(-)
gfs2/fsck/fs_recovery.c +138 -0
index ce11fb4..17b2a3a 100644
@@ -812,3 +812,141 @@ int ji_update(struct gfs2_sbd *sdp)
 	}
 	return 0;
 }
+
+static void bad_journalname(const char *filename, int len)
+{
+	if (len >= 64)
+		len = 63;
+	log_debug(_("Journal index entry '%.*s' has an invalid filename.\n"),
+	          len, filename);
+}
+
+/**
+ * check_jindex_dent - check the jindex directory entries
+ *
+ * This function makes sure the directory entries of the jindex are valid.
+ * If they're not '.' or '..' they better have the form journalXXX.
+ */
+static int check_jindex_dent(struct gfs2_inode *ip, struct gfs2_dirent *dent,
+			     struct gfs2_dirent *prev_de,
+			     struct gfs2_buffer_head *bh, char *filename,
+			     uint32_t *count, int *lindex, void *priv)
+{
+	struct gfs2_dirent dentry, *de;
+	int i;
+
+	memset(&dentry, 0, sizeof(struct gfs2_dirent));
+	gfs2_dirent_in(&dentry, (char *)dent);
+	de = &dentry;
+
+	if (de->de_name_len == 1 && filename[0] == '.')
+		goto dirent_good;
+	if (de->de_name_len == 2 && filename[0] == '.' && filename[1] == '.')
+		goto dirent_good;
+
+	if ((de->de_name_len >= 11) || /* "journal9999" */
+	    (de->de_name_len <= 7) ||
+	    (strncmp(filename, "journal", 7))) {
+		bad_journalname(filename, de->de_name_len);
+		return -1;
+	}
+	for (i = 7; i < de->de_name_len; i++) {
+		if (filename[i] < '0' || filename[i] > '9') {
+			bad_journalname(filename, de->de_name_len);
+			return -2;
+		}
+	}
+
+dirent_good:
+	/* Return the number of leaf entries so metawalk doesn't flag this
+	   leaf as having none. */
+	*count = be16_to_cpu(((struct gfs2_leaf *)bh->b_data)->lf_entries);
+	return 0;
+}
+
+struct metawalk_fxns jindex_check_fxns = {
+	.private = NULL,
+	.check_dentry = check_jindex_dent,
+};
+
+/**
+ * init_jindex - read in the rindex file
+ */
+int init_jindex(struct gfs2_sbd *sdp, int allow_ji_rebuild)
+{
+	/*******************************************************************
+	 ******************  Fill in journal information  ******************
+	 *******************************************************************/
+
+	log_debug(_("Validating the journal index.\n"));
+	/* rgrepair requires the journals be read in in order to distinguish
+	   "real" rgrps from rgrps that are just copies left in journals. */
+	if (sdp->gfs1)
+		sdp->md.jiinode = lgfs2_inode_read(sdp, sbd1->sb_jindex_di.no_addr);
+	else
+		gfs2_lookupi(sdp->master_dir, "jindex", 6, &sdp->md.jiinode);
+
+	if (!sdp->md.jiinode) {
+		int err;
+
+		if (!allow_ji_rebuild) {
+			log_crit(_("Error: jindex and rindex files are both "
+				   "corrupt.\n"));
+			return -1;
+		}
+		if (!query( _("The gfs2 system jindex inode is missing. "
+			      "Okay to rebuild it? (y/n) "))) {
+			log_crit(_("Error: cannot proceed without a valid "
+				   "jindex file.\n"));
+			return -1;
+		}
+
+		err = build_jindex(sdp);
+		if (err) {
+			log_crit(_("Error %d rebuilding jindex\n"), err);
+			return err;
+		}
+		gfs2_lookupi(sdp->master_dir, "jindex", 6, &sdp->md.jiinode);
+	}
+
+	/* check for irrelevant entries in jindex. Can't use check_dir because
+	   that creates and destroys the inode, which we don't want. */
+	if (!sdp->gfs1) {
+		int error;
+
+		log_debug(_("Checking the integrity of the journal index.\n"));
+		if (sdp->md.jiinode->i_di.di_flags & GFS2_DIF_EXHASH)
+			error = check_leaf_blks(sdp->md.jiinode,
+						&jindex_check_fxns);
+		else
+			error = check_linear_dir(sdp->md.jiinode,
+						 sdp->md.jiinode->i_bh,
+						 &jindex_check_fxns);
+		if (error) {
+			log_err(_("The system journal index is damaged.\n"));
+			if (!query( _("Okay to rebuild it? (y/n) "))) {
+				log_crit(_("Error: cannot proceed without a "
+					   "valid jindex file.\n"));
+				return -1;
+			}
+			inode_put(&sdp->md.jiinode);
+			gfs2_dirent_del(sdp->master_dir, "jindex", 6);
+			log_err(_("Corrupt journal index was removed.\n"));
+			error = build_jindex(sdp);
+			if (error) {
+				log_err(_("Error rebuilding journal "
+					  "index: Cannot continue.\n"));
+				return error;
+			}
+			gfs2_lookupi(sdp->master_dir, "jindex", 6,
+				     &sdp->md.jiinode);
+		}
+	}
+
+	/* read in the ji data */
+	if (ji_update(sdp)){
+		log_err( _("Unable to read jindex inode.\n"));
+		return -1;
+	}
+	return 0;
+}
gfs2/fsck/fs_recovery.h +1 -0
index 1a7aae3..d687627 100644
@@ -8,5 +8,6 @@ extern int replay_journals(struct gfs2_sbd *sdp, int preen, int force_check,
 extern int preen_is_safe(struct gfs2_sbd *sdp, int preen, int force_check);
 
 extern int ji_update(struct gfs2_sbd *sdp);
+extern int init_jindex(struct gfs2_sbd *sdp, int allow_ji_rebuild);
 #endif /* __FS_RECOVERY_H__ */
 
gfs2/fsck/initialize.c +5 -135

158 lines not on this page · raw message

gfs2/fsck/rgrepair.c +31 -7
index 12e474b..9ef97b8 100644
@@ -14,6 +14,7 @@
 #include "libgfs2.h"
 #include "osi_list.h"
 #include "fsck.h"
+#include "fs_recovery.h"
 
 int rindex_modified = FALSE;
 struct special_blocks false_rgrps;
@@ -41,6 +42,10 @@ struct special_blocks false_rgrps;
  * case, we don't want to mistake these blocks that look just a real RG
  * for a real RG block.  These are "fake" RGs that need to be ignored for
  * the purposes of finding where things are.
+ *
+ * NOTE: This function assumes that the jindex and journals have been read in,
+ *       which isn't often the case. Normally the rindex needs to be read in
+ *       first. If the rindex is damaged, that's not an option.
  */
 static void find_journaled_rgs(struct gfs2_sbd *sdp)
 {
@@ -62,8 +67,7 @@ static void find_journaled_rgs(struct gfs2_sbd *sdp)
 				break;
 			bh = bread(sdp, dblock);
 			if (!gfs2_check_meta(bh, GFS2_METATYPE_RG)) {
-				log_debug( _("False rgrp found at block 0x%llx\n"),
-					  (unsigned long long)dblock);
+				/* False rgrp found at block dblock */
 				gfs2_special_set(&false_rgrps, dblock);
 			}
 			brelse(bh);
@@ -443,6 +447,21 @@ static int gfs2_rindex_rebuild(struct gfs2_sbd *sdp, int *num_rgs,
 	struct rgrp_tree *calc_rgd, *prev_rgd;
 	int number_of_rgs, rgi;
 	int rg_was_fnd = FALSE, corrupt_rgs = 0;
+	int error = -1, j;
+
+	/*
+	 * In order to continue, we need to initialize the jindex. We need
+	 * the journals in order to correctly eliminate false positives during
+	 * rgrp repair. IOW, we need to properly ignore rgrps that appear in
+	 * the journals, and we can only do that if we have the journals.
+	 * To make matters worse, journals may span several (small) rgrps,
+	 * so we can't go by the rgrps.
+	 */
+	if (init_jindex(sdp, 0) != 0) {
+		log_crit(_("Error: Can't read jindex required for rindex "
+			   "repairs.\n"));
+		return -1;
+	}
 
 	sdp->rgcalc.osi_node = NULL;
 	initial_first_rg_dist = first_rg_dist = sdp->sb_addr + 1;
@@ -466,7 +485,7 @@ static int gfs2_rindex_rebuild(struct gfs2_sbd *sdp, int *num_rgs,
 		calc_rgd = rgrp_insert(&sdp->rgcalc, blk);
 		if (!calc_rgd) {
 			log_crit( _("Can't allocate memory for rgrp repair.\n"));
-			return -1;
+			goto out;
 		}
 		calc_rgd->ri.ri_length = 1;
 		if (!rg_was_fnd) { /* if not an RG */
@@ -483,7 +502,7 @@ static int gfs2_rindex_rebuild(struct gfs2_sbd *sdp, int *num_rgs,
 				log_crit( _("Error: too many missing or "
 					    "damaged rgrps using this method. "
 					    "Time to try another method.\n"));
-				return -1;
+				goto out;
 			}
 		}
 		/* ------------------------------------------------ */
@@ -519,10 +538,10 @@ static int gfs2_rindex_rebuild(struct gfs2_sbd *sdp, int *num_rgs,
 		}
 		number_of_rgs++;
 		if (rg_was_fnd)
-			log_info( _("  rgrp %d at block 0x%llx intact"),
+			log_info( _("  rgrp %d at block 0x%llx intact\n"),
 				  number_of_rgs, (unsigned long long)blk);
 		else
-			log_warn( _("* rgrp %d at block 0x%llx *** DAMAGED ***"),
+			log_warn( _("* rgrp %d at block 0x%llx *** DAMAGED ***\n"),
 				  number_of_rgs, (unsigned long long)blk);
 		prev_rgd = calc_rgd;
 		/*
@@ -587,7 +606,12 @@ static int gfs2_rindex_rebuild(struct gfs2_sbd *sdp, int *num_rgs,
 			  calc_rgd->ri.ri_bitbytes);
         }
 	*num_rgs = number_of_rgs;
-	return 0;
+	error = 0;
+out:
+	for (j = 0; j < sdp->md.journals; j++)
+		inode_put(&sdp->md.journal[j]);
+	free(sdp->md.journal);
+	return error;
 }
 
 #define DIV_RU(x, y) (((x) + (y) - 1) / (y))
gfs2/libgfs2/rgrp.c +11 -9
index c5671f0..766bdfc 100644
@@ -250,17 +250,19 @@ void gfs2_rgrp_free(struct osi_root *rgrp_tree)
 	while ((n = osi_first(rgrp_tree))) {
 		rgd = (struct rgrp_tree *)n;
 
-		if (rgd->bits && rgd->bits[0].bi_bh) { /* if a buffer exists */
-			rgs_since_sync++;
-			if (rgs_since_sync >= RG_SYNC_TOLERANCE) {
-				if (!sdp)
-					sdp = rgd->bits[0].bi_bh->sdp;
-				fsync(sdp->device_fd);
-				rgs_since_sync = 0;
+		if (rgd->bits) {
+			if (rgd->bits[0].bi_bh) { /* if a buffer exists */
+				rgs_since_sync++;
+				if (rgs_since_sync >= RG_SYNC_TOLERANCE) {
+					if (!sdp)
+						sdp = rgd->bits[0].bi_bh->sdp;
+					fsync(sdp->device_fd);
+					rgs_since_sync = 0;
+				}
+				gfs2_rgrp_relse(rgd); /* free them all. */
 			}
-			gfs2_rgrp_relse(rgd); /* free them all. */
+			free(rgd->bits);
 		}
-		free(rgd->bits);
 		osi_erase(&rgd->node, rgrp_tree);
 		free(rgd);
 	}
Signature · 1 line
2.5.0
#6 lore raw

PATCH 6/11 fsck.gfs2: better reporting of false positive rgrp identification

In reply to Bob Peterson

This patch adds some more information to the debug messages related to the identification of "false positive" rgrps. That is, rgrps that aren't really rgrps because they reside inside a journal.

---
 gfs2/fsck/rgrepair.c | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)
gfs2/fsck/rgrepair.c +7 -1
index 9ef97b8..f0759c1 100644
@@ -55,12 +55,16 @@ static void find_journaled_rgs(struct gfs2_sbd *sdp)
 	uint32_t extlen;
 	struct gfs2_inode *ip;
 	struct gfs2_buffer_head *bh;
+	int false_count;
 
 	osi_list_init(&false_rgrps.list);
 	for (j = 0; j < sdp->md.journals; j++) {
-		log_debug( _("Checking for rgrps in journal%d.\n"), j);
 		ip = sdp->md.journal[j];
+		log_debug(_("Checking for rgrps in journal%d which starts "
+			    "at block 0x%llx.\n"), j,
+			  (unsigned long long)ip->i_di.di_num.no_addr);
 		jblocks = ip->i_di.di_size / sdp->sd_sb.sb_bsize;
+		false_count = 0;
 		for (b = 0; b < jblocks; b++) {
 			block_map(ip, b, &new, &dblock, &extlen, 0);
 			if (!dblock)
@@ -68,10 +72,12 @@ static void find_journaled_rgs(struct gfs2_sbd *sdp)
 			bh = bread(sdp, dblock);
 			if (!gfs2_check_meta(bh, GFS2_METATYPE_RG)) {
 				/* False rgrp found@block dblock */
+				false_count++;
 				gfs2_special_set(&false_rgrps, dblock);
 			}
 			brelse(bh);
 		}
+		log_debug("\n%d false positives identified.\n", false_count);
 	}
 }
 
Signature · 1 line
2.5.0
#7 lore raw

PATCH 7/11 fsck.gfs2: Minor reformatting

In reply to Bob Peterson

This patch just changes some formatting inside the code and eliminates a message that doesn't need to be there.

---
 gfs2/fsck/rgrepair.c | 20 +++++++++-----------
 1 file changed, 9 insertions(+), 11 deletions(-)
gfs2/fsck/rgrepair.c +9 -11
index f0759c1..5bf82db 100644
@@ -171,8 +171,7 @@ static uint64_t find_shortest_rgdist(struct gfs2_sbd *sdp,
 			log_warn( _("rgrp 2 is damaged: getting dist from index: "));
 			*first_rg_dist = tmpndx.ri_addr - (sdp->sb_addr + 1);
 			log_warn("0x%llx\n", (unsigned long long)*first_rg_dist);
-		}
-		else {
+		} else {
 			log_warn( _("rgrp index 2 is damaged: extrapolating dist: "));
 			*first_rg_dist = sdp->device.length -
 				(sdp->rgrps - 1) *
@@ -500,8 +499,8 @@ static int gfs2_rindex_rebuild(struct gfs2_sbd *sdp, int *num_rgs,
 			/* ------------------------------------------------- */
 			corrupt_rgs++;
 			if (corrupt_rgs < 5)
-				log_debug( _("Missing or damaged rgrp at block "
-					  "%llu (0x%llx)\n"),
+				log_debug(_("Missing or damaged rgrp@block "
+					    "%llu (0x%llx)\n"),
 					  (unsigned long long)blk,
 					  (unsigned long long)blk);
 			else {
@@ -664,7 +663,8 @@ static uint64_t how_many_rgrps(struct gfs2_sbd *sdp, struct device *dev, int rgs
 		sdp->rgsize += GFS2_DEFAULT_RGSIZE; /* bigger rgs */
 	}
 
-	log_debug("  rg sz = %"PRIu32"\n  nrgrp = %"PRIu64"\n", sdp->rgsize, nrgrp);
+	log_debug("  rg sz = %"PRIu32"\n  nrgrp = %"PRIu64"\n", sdp->rgsize,
+		  nrgrp);
 
 	return nrgrp;
 }
@@ -728,10 +728,10 @@ static void compute_rgrp_layout(struct gfs2_sbd *sdp, struct osi_root *rgtree, i
 				(nrgrp - 1) * (dev->length / nrgrp);
 		}
 		rl->start = rgaddr;
-		printf("%d: start: %" PRIu64 " (0x%"
+		/* printf("%d: start: %" PRIu64 " (0x%"
 			 PRIx64 "), length = %"PRIu64" (0x%"
 			 PRIx64 ")\n", rgrp + 1, rl->start, rl->start,
-			 rl->length, rl->length);
+			 rl->length, rl->length);*/
 		rlast = rl;
 	}
 
@@ -909,8 +909,7 @@ int rg_repair(struct gfs2_sbd *sdp, int trust_lvl, int *rg_count, int *sane)
 			gfs2_rgrp_free(&sdp->rgcalc);
 			return -1;
 		}
-	}
-	else if (trust_lvl == distrust) { /* If we can't trust RG index */
+	} else if (trust_lvl == distrust) { /* If we can't trust RG index */
 		/* Free previous incarnations in memory, if any. */
 		gfs2_rgrp_free(&sdp->rgtree);
 
@@ -921,8 +920,7 @@ int rg_repair(struct gfs2_sbd *sdp, int trust_lvl, int *rg_count, int *sane)
 			return -1;
 		}
 		sdp->rgrps = calc_rg_count;
-	}
-	else if (trust_lvl == indignation) { /* If we can't trust anything */
+	} else if (trust_lvl == indignation) { /* If we can't trust anything */
 		/* Free previous incarnations in memory, if any. */
 		gfs2_rgrp_free(&sdp->rgtree);
 
Signature · 1 line
2.5.0
#8 lore raw

PATCH 8/11 fsck.gfs2: Ditch variable rgcount_from_index

In reply to Bob Peterson

This patch eliminates variable rgcount_from_index from function rg_repair. It wasn't really being used anyway. The important count of resource groups from the rindex is sdp->rgrps, so use that instead. Also, eliminate setting that from the calculated number of rgrps. We've already got calc_rg_count for that.

---
 gfs2/fsck/rgrepair.c | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)
gfs2/fsck/rgrepair.c +2 -4
index 5bf82db..6e56f91 100644
@@ -880,7 +880,7 @@ int rg_repair(struct gfs2_sbd *sdp, int trust_lvl, int *rg_count, int *sane)
 {
 	struct osi_node *n, *next = NULL, *e, *enext;
 	int error, discrepancies, percent;
-	int calc_rg_count = 0, rgcount_from_index, rg;
+	int calc_rg_count = 0, rg;
 	struct gfs2_rindex buf;
 
 	if (trust_lvl == blind_faith)
@@ -919,7 +919,6 @@ int rg_repair(struct gfs2_sbd *sdp, int trust_lvl, int *rg_count, int *sane)
 			gfs2_rgrp_free(&sdp->rgcalc);
 			return -1;
 		}
-		sdp->rgrps = calc_rg_count;
 	} else if (trust_lvl == indignation) { /* If we can't trust anything */
 		/* Free previous incarnations in memory, if any. */
 		gfs2_rgrp_free(&sdp->rgtree);
@@ -930,11 +929,10 @@ int rg_repair(struct gfs2_sbd *sdp, int trust_lvl, int *rg_count, int *sane)
 			gfs2_rgrp_free(&sdp->rgcalc);
 			return -1;
 		}
-		sdp->rgrps = calc_rg_count;
 	}
 	/* Read in the rindex */
 	sdp->rgtree.osi_node = NULL; /* Just to be safe */
-	rindex_read(sdp, 0, &rgcount_from_index, sane);
+	rindex_read(sdp, 0, &sdp->rgrps, sane);
 	if (sdp->md.riinode->i_di.di_size % sizeof(struct gfs2_rindex)) {
 		log_warn( _("WARNING: rindex file is corrupt.\n"));
 		gfs2_rgrp_free(&sdp->rgcalc);
Signature · 1 line
2.5.0
#9 lore raw

PATCH 9/11 fsck.gfs2: Add ability to fix rindex file size

In reply to Bob Peterson

Before this patch, fsck.gfs2 checked the size of rindex, and if it wasn't evenly divisible by the size of an rindex entry, it threw up its hands and gave up. This patch allows fsck.gfs2 to repair the rindex size, basically rounding down to the largest number of rgrps that will fit.

---
 gfs2/fsck/rgrepair.c | 16 ++++++++++++----
 1 file changed, 12 insertions(+), 4 deletions(-)
gfs2/fsck/rgrepair.c +12 -4
index 6e56f91..1fefc34 100644
@@ -934,10 +934,18 @@ int rg_repair(struct gfs2_sbd *sdp, int trust_lvl, int *rg_count, int *sane)
 	sdp->rgtree.osi_node = NULL; /* Just to be safe */
 	rindex_read(sdp, 0, &sdp->rgrps, sane);
 	if (sdp->md.riinode->i_di.di_size % sizeof(struct gfs2_rindex)) {
-		log_warn( _("WARNING: rindex file is corrupt.\n"));
-		gfs2_rgrp_free(&sdp->rgcalc);
-		gfs2_rgrp_free(&sdp->rgtree);
-		return -1;
+		log_warn( _("WARNING: rindex file has an invalid size.\n"));
+		if (!query( _("Truncate the rindex size? (y/n)"))) {
+			log_err(_("The rindex was not repaired.\n"));
+			gfs2_rgrp_free(&sdp->rgcalc);
+			gfs2_rgrp_free(&sdp->rgtree);
+			return -1;
+		}
+		sdp->md.riinode->i_di.di_size /= sizeof(struct gfs2_rindex);
+		sdp->md.riinode->i_di.di_size *= sizeof(struct gfs2_rindex);
+		bmodified(sdp->md.riinode->i_bh);
+		log_err(_("Changing rindex size to %lld.\n"),
+			(unsigned long long)sdp->md.riinode->i_di.di_size);
 	}
 	log_warn( _("L%d: number of rgs expected     = %lld.\n"), trust_lvl + 1,
 		 (unsigned long long)sdp->rgrps);
Signature · 1 line
2.5.0
#10 lore raw

PATCH 10/11 fsck.gfs2: Do not try to overrun the max rgrps that fit inside rindex

In reply to Bob Peterson

When rindex is being repaired, it may calculate a new set of rgrps, then replace the old rgrps with the new ones. The trouble is, if we've calculated more rgrps than will fit inside rindex, it will try to write past the end of rindex. That's unacceptable because we cannot allow block allocations until after pass1 has finished, and rg repair happens well before pass1 starts. The failing situation is for cases where, for example, a file system was extended via lvextend, thus allowing for many more rgrps to be calculated than originally fit, but the rindex is still in a pre-gfs2_grow state where its size can only fit a smaller number of rgrps. In that case, we want fsck.gfs2 to repair what rgrps it can, but stop then it reaches the limits of the rindex file size.

---
 gfs2/fsck/rgrepair.c | 37 ++++++++++++++++++++++++++++++-------
 1 file changed, 30 insertions(+), 7 deletions(-)
gfs2/fsck/rgrepair.c +30 -7
index 1fefc34..e80a7af 100644
@@ -950,13 +950,34 @@ int rg_repair(struct gfs2_sbd *sdp, int trust_lvl, int *rg_count, int *sane)
 	log_warn( _("L%d: number of rgs expected     = %lld.\n"), trust_lvl + 1,
 		 (unsigned long long)sdp->rgrps);
 	if (calc_rg_count != sdp->rgrps) {
+		int most_that_fit;
+
 		log_warn( _("L%d: They don't match; either (1) the fs was "
 			    "extended, (2) an odd\n"), trust_lvl + 1);
 		log_warn( _("L%d: rgrp size was used, or (3) we have a corrupt "
 			    "rg index.\n"), trust_lvl + 1);
-		gfs2_rgrp_free(&sdp->rgcalc);
-		gfs2_rgrp_free(&sdp->rgtree);
-		return -1;
+		/* If the trust level is open_minded, we would have calculated
+		   the rindex based on the device size. If it's not the same
+		   number, don't trust it. Complain about the discrepancy,
+		   then try again with a little more distrust. */
+		if ((trust_lvl < distrust) ||
+		    !query( _("Attempt to use what rgrps we can? (y/n)"))) {
+			gfs2_rgrp_free(&sdp->rgcalc);
+			gfs2_rgrp_free(&sdp->rgtree);
+			log_err(_("The rindex was not repaired.\n"));
+			return -1;
+		}
+		/* We cannot grow rindex at this point. Since pass1 has not
+		   yet run, we can't allocate blocks. Therefore we must use
+		   whatever will fix in the space given. */
+		most_that_fit = sdp->md.riinode->i_di.di_size /
+			sizeof(struct gfs2_rindex);
+		log_debug(_("The most we can fit is %d rgrps\n"),
+			  most_that_fit);
+		if (most_that_fit < calc_rg_count)
+			calc_rg_count = most_that_fit;
+		log_err(_("Attempting to fix rindex with %d rgrps.\n"),
+			calc_rg_count);
 	}
 	/* ------------------------------------------------------------- */
 	/* Now compare the rindex to what we think it should be.         */
@@ -967,7 +988,7 @@ int rg_repair(struct gfs2_sbd *sdp, int trust_lvl, int *rg_count, int *sane)
 	/* ------------------------------------------------------------- */
 	discrepancies = 0;
 	for (rg = 0, n = osi_first(&sdp->rgtree), e = osi_first(&sdp->rgcalc);
-	     n && e && !fsck_abort; rg++) {
+	     n && e && !fsck_abort && rg < calc_rg_count; rg++) {
 		struct rgrp_tree *expected, *actual;
 
 		next = osi_next(n);
@@ -1016,13 +1037,15 @@ int rg_repair(struct gfs2_sbd *sdp, int trust_lvl, int *rg_count, int *sane)
 			return -1;
 		}
 	}
+	log_debug("Calculated %d rgrps: Total: %d Match: %d Mismatch: %d\n",
+		  calc_rg_count, rg, rg - discrepancies, discrepancies);
 	/* ------------------------------------------------------------- */
 	/* Now compare the rindex to what we think it should be.         */
 	/* Our rindex should be pretty predictable unless we've grown    */
 	/* so look for index problems first before looking@the rgs.   */
 	/* ------------------------------------------------------------- */
 	for (rg = 0, n = osi_first(&sdp->rgtree), e = osi_first(&sdp->rgcalc);
-	     e && !fsck_abort; rg++) {
+	     e && !fsck_abort && rg < calc_rg_count; rg++) {
 		struct rgrp_tree *expected, *actual;
 
 		if (n)
@@ -1091,8 +1114,8 @@ int rg_repair(struct gfs2_sbd *sdp, int trust_lvl, int *rg_count, int *sane)
 	/* Now we can somewhat trust the rindex and the RG addresses,    */
 	/* so let's read them in, check them and optionally fix them.    */
 	/* ------------------------------------------------------------- */
-	for (rg = 0, n = osi_first(&sdp->rgtree); n && !fsck_abort;
-	     n = next, rg++) {
+	for (rg = 0, n = osi_first(&sdp->rgtree); n && !fsck_abort &&
+		     rg < calc_rg_count; n = next, rg++) {
 		struct rgrp_tree *rgd;
 		uint64_t prev_err = 0, errblock;
 		int i;
Signature · 1 line
2.5.0
#11 lore raw

PATCH 11/11 fsck.gfs2: Detect multiple rgrp grow segments

In reply to Bob Peterson

This patch gives fsck.gfs2's rgrepair code the capability to detect multiple gfs2_grow segments and repair them accordingly.

---
 gfs2/fsck/initialize.c |   2 +-
 gfs2/fsck/rgrepair.c   | 226 ++++++++++++++++++++++++++++++++++---------------
 2 files changed, 160 insertions(+), 68 deletions(-)
gfs2/fsck/initialize.c +1 -1
index 09ad660..84d39cd 100644
@@ -640,7 +640,7 @@ static int fetch_rgrps(struct gfs2_sbd *sdp)
 			log_notice(_("(level %d passed)\n"), trust_lvl + 1);
 			break;
 		} else {
-			if (ret < 0)
+			if (ret == -1)
 				log_err( _("(level %d failed: %s)\n"),
 					 trust_lvl + 1, fail_desc[trust_lvl]);
 			else
gfs2/fsck/rgrepair.c +159 -67

310 lines not on this page · raw message

Signature · 1 line
2.5.0
#12 lore raw

cover Misc patches regarding corrupt rindex handling

In reply to Bob Peterson

Hi Bob,

Quoted 16 lines · Bob Peterson

Hi,

These patches are probably best handled as separate items, rather than as a set, but I didn't want to send out 11 separate emails.

This is a collection of patches I developed while debugging some rather complex problems involving the rebuilding of corrupt or missing rindex system files by fsck.gfs2. It also fixes some problems whereby gfs2_edit or other utils crash and burn due to corrupt rindex files.

Regards,

Bob Peterson
Red Hat File Systems

Quoted 13 lines

---
Bob Peterson (11):
libgfs2: Backport rbtree fixes from upstream
libgfs2: Check for obvious corruption reading rindex
libgfs2: Change rgrp counts to be uint64_t
gfs2_edit: Don't reference an empty rgrp tree
fsck.gfs2: Read jindex before making rindex repairs fsck.gfs2: better reporting of false positive rgrp identification fsck.gfs2: Minor reformatting
fsck.gfs2: Ditch variable rgcount_from_index
fsck.gfs2: Add ability to fix rindex file size
fsck.gfs2: Do not try to overrun the max rgrps that fit inside rindex fsck.gfs2: Detect multiple rgrp grow segments

Consider these ACKed. I've taken a look through them and they look fine, though I'm not hugely familiar with that part of the fsck code. Coverity is happy with them too.

Outside of the scope of this set, I noticed in patch 11 that parts of the existing fsck.gfs2 code assume that the first rgrp lies in the first block after the superblock. That's likely to get confused when the file system is RAID stripe aligned so that's something that may need addressing at some point (mkfs.gfs2 -b 4096 -o swidth=16k,sunit=8k should mock it up for a test).

In general it would be good to have more fsck.gfs2 tests in the testsuite so if there's a simple way to test these patches without mounting (scribbling over some rindex entries?) then I'll get that added.

Cheers,
Andy

Quoted 13 lines
  gfs2/edit/hexedit.c     |   9 +-
  gfs2/fsck/fs_recovery.c | 138 +++++++++++++++++++
  gfs2/fsck/fs_recovery.h |   1 +
  gfs2/fsck/initialize.c  | 142 +-------------------
  gfs2/fsck/rgrepair.c    | 349 ++++++++++++++++++++++++++++++++++--------------
  gfs2/include/osi_tree.h |  51 ++++---
  gfs2/libgfs2/gfs2l.c    |   2 +-
  gfs2/libgfs2/libgfs2.h  |   3 +-
  gfs2/libgfs2/rgrp.c     |  30 +++--
  gfs2/libgfs2/super.c    |   8 +-
  10 files changed, 457 insertions(+), 276 deletions(-)
#13 lore raw

cover Misc patches regarding corrupt rindex handling

Hi Andy,

----- Original Message -----

Quoted 2 lines

Hi Bob,

(snip)

Quoted 3 lines

Consider these ACKed. I've taken a look through them and they look fine, though I'm not hugely familiar with that part of the fsck code. Coverity is happy with them too.

Does that mean I can/should push them? I thought you said you had some suggestions for them.

Quoted 6 lines

Outside of the scope of this set, I noticed in patch 11 that parts of the existing fsck.gfs2 code assume that the first rgrp lies in the first block after the superblock. That's likely to get confused when the file system is RAID stripe aligned so that's something that may need addressing at some point (mkfs.gfs2 -b 4096 -o swidth=16k,sunit=8k should mock it up for a test).

Good to know. This could be a problem for even today's rindex repair code. We may need to do some testing to figure out what's broken and what's not.

Quoted 5 lines

In general it would be good to have more fsck.gfs2 tests in the testsuite so if there's a simple way to test these patches without mounting (scribbling over some rindex entries?) then I'll get that added. Cheers,
Andy

This is an excellent idea. A very long time ago, I used to have a different fsck "nightmare" test that used a little "nukerg2.c" program (source attached) to nuke an rgrp or an rindex entry. Then I'd force fsck to fix it. I may have originally written it for gfs1 rather than gfs2, and I know I was especially targeting post-gfs2_grow rindexes.

I can no longer find the original script, but the original sequence went something like:

mkfs.gfs2 <lv>
./nukerg2 -rg X <lv>
fsck.gfs2 <lv>

# make sure $? is 1
fsck.gfs2 <lv>
# make sure $? is 0
mount <lv> <mntpt>
lvextend <lv>
gfs2_grow <mntpt>
./nukerg2 -rg X <lv>
fsck.gfs2 <lv>
# make sure $? is 1
fsck.gfs2 <lv>
# make sure $? is 0

For X, I'd systematically do:
1. First rgrp
2. Second rgrp
3. Third rgrp
4. Half-way point
5. Last rgrp
6. I'd also nuke two and three rgrps in a row IIRC.

There's also an "-rgindex" parameter to kill the rindex entries themselves, so I'd repeat each of the above steps, but killing the rindex entry rather than the rgrp itself.

This test really ought to be resurrected and added to the test suite.

Regards,

Bob Peterson
Red Hat File Systems
-------------- next part --------------
A non-text attachment was scrubbed...
Name: nukerg2.c
Type: text/x-c++src
Size: 6058 bytes
Desc: not available
URL: <http://listman.redhat.com/archives/cluster-devel/attachments/20160321/04df123c/attachment.bin>

#14 lore raw

cover Misc patches regarding corrupt rindex handling

Quoted 11 lines · Bob Peterson

Hi Andy,

----- Original Message -----

Quoted 2 lines

Hi Bob,

(snip)

Quoted 3 lines

Consider these ACKed. I've taken a look through them and they look fine, though I'm not hugely familiar with that part of the fsck code. Coverity is happy with them too.

Does that mean I can/should push them?

If you're happy with them, go right ahead. We can add tests and any tweaks that might be needed on top.

Quoted 2 lines

I thought you said you had some
suggestions for them.

Well I thought there might be an issue with using an int for the counter in patch 10 but that seems like it should be sufficient on closer inspection. There was also one piece of code that was commented out in patch 7 instead of removed which seemed odd but ultimately not a bug.

Quoted 9 lines
Quoted 6 lines

Outside of the scope of this set, I noticed in patch 11 that parts of the existing fsck.gfs2 code assume that the first rgrp lies in the first block after the superblock. That's likely to get confused when the file system is RAID stripe aligned so that's something that may need addressing at some point (mkfs.gfs2 -b 4096 -o swidth=16k,sunit=8k should mock it up for a test).

Good to know. This could be a problem for even today's rindex repair code. We may need to do some testing to figure out what's broken and what's not.

As I understand it, the rg repair code is just using the layout assumptions for quickly discovering where the rgrps are in order to rebuild the rindex, so in most cases it shouldn't be a problem (in theory), but we'll need to add heuristics based on the new layout strategy if it gets confused. Which is where the testing comes in...

Quoted 8 lines
Quoted 5 lines

In general it would be good to have more fsck.gfs2 tests in the testsuite so if there's a simple way to test these patches without mounting (scribbling over some rindex entries?) then I'll get that added. Cheers,
Andy

This is an excellent idea. A very long time ago, I used to have a different fsck "nightmare" test that used a little "nukerg2.c" program (source attached)

Thanks, it could live in the tests directory to make use of in tests/fsck.at and perhaps gfs2l can be extended to obsolete it later.

Quoted 30 lines

to nuke an rgrp or an rindex entry. Then I'd force fsck to fix it. I may have originally written it for gfs1 rather than gfs2, and I know I was especially targeting post-gfs2_grow rindexes.

I can no longer find the original script, but the original sequence went something like:

mkfs.gfs2 <lv>
./nukerg2 -rg X <lv>
fsck.gfs2 <lv>

# make sure $? is 1
fsck.gfs2 <lv>
# make sure $? is 0
mount <lv> <mntpt>
lvextend <lv>
gfs2_grow <mntpt>
./nukerg2 -rg X <lv>
fsck.gfs2 <lv>
# make sure $? is 1
fsck.gfs2 <lv>
# make sure $? is 0

For X, I'd systematically do:
1. First rgrp
2. Second rgrp
3. Third rgrp
4. Half-way point
5. Last rgrp
6. I'd also nuke two and three rgrps in a row IIRC.

Quoted 5 lines

There's also an "-rgindex" parameter to kill the rindex entries themselves, so I'd repeat each of the above steps, but killing the rindex entry rather than the rgrp itself.

This test really ought to be resurrected and added to the test suite.

Ok, sounds like a plan. We can do everything up to the mount in the gfs2-utils testsuite. Perhaps we can expand on it with some gfs2-specific xfstests tests for the mount+grow cases in future.

Cheers,
Andy