public inbox for cluster-cvs@sourceware.org
help / color / mirror / Atom feed
* cluster: STABLE2 - GFS: gfs_fsck sometimes needs to be run twice
@ 2009-08-10 19:21 Bob Peterson
  0 siblings, 0 replies; only message in thread
From: Bob Peterson @ 2009-08-10 19:21 UTC (permalink / raw)
  To: cluster-cvs-relay

Gitweb:        http://git.fedorahosted.org/git/cluster.git?p=cluster.git;a=commitdiff;h=1028257becdaca26b9b14fa5abfb78280804d627
Commit:        1028257becdaca26b9b14fa5abfb78280804d627
Parent:        92cec48f73496aeb7bc6fb480a9decb6dc951a6a
Author:        Bob Peterson <rpeterso@redhat.com>
AuthorDate:    Mon Aug 10 14:01:22 2009 -0500
Committer:     Bob Peterson <rpeterso@redhat.com>
CommitterDate: Mon Aug 10 14:02:02 2009 -0500

GFS: gfs_fsck sometimes needs to be run twice

bz 509225

This patch fixes numerous bugs whereby gfs_fsck was not "following
through" with its changes, and therefore a second run was often
needed to completely clean things up.
---
 gfs/gfs_fsck/block_list.c |   24 ++-
 gfs/gfs_fsck/block_list.h |    3 +-
 gfs/gfs_fsck/fs_dir.c     |   23 ++-
 gfs/gfs_fsck/fs_dir.h     |    2 +
 gfs/gfs_fsck/metawalk.c   |  471 +++++++++++++++++++++++-----------
 gfs/gfs_fsck/metawalk.h   |   13 +-
 gfs/gfs_fsck/pass1.c      |  640 +++++++++++++++++++++++++--------------------
 gfs/gfs_fsck/pass1b.c     |  124 ++++++----
 gfs/gfs_fsck/pass1c.c     |  177 +++++++------
 gfs/gfs_fsck/pass2.c      |  285 ++++++++++++---------
 gfs/gfs_fsck/pass3.c      |   23 +-
 gfs/gfs_fsck/pass4.c      |   51 +++-
 gfs/gfs_fsck/pass5.c      |   44 +++-
 gfs/gfs_fsck/rgrp.c       |    3 +-
 gfs/gfs_fsck/super.c      |    2 +-
 15 files changed, 1160 insertions(+), 725 deletions(-)

diff --git a/gfs/gfs_fsck/block_list.c b/gfs/gfs_fsck/block_list.c
index ecc9dbd..23b8087 100644
--- a/gfs/gfs_fsck/block_list.c
+++ b/gfs/gfs_fsck/block_list.c
@@ -116,13 +116,13 @@ int block_mark(struct block_list *il, uint64_t block, enum mark_block mark)
 int block_set(struct block_list *il, uint64_t block, enum mark_block mark)
 {
 	int err = 0;
-	err = block_clear(il, block, mark);
+	err = block_clear(il, block);
 	if(!err)
 		err = block_mark(il, block, mark);
 	return err;
 }
 
-int block_clear(struct block_list *il, uint64_t block, enum mark_block m)
+int block_unmark(struct block_list *il, uint64_t block, enum mark_block m)
 {
 	int err = 0;
 
@@ -154,6 +154,26 @@ int block_clear(struct block_list *il, uint64_t block, enum mark_block m)
 	return err;
 }
 
+int block_clear(struct block_list *il, uint64_t block)
+{
+	int err = 0;
+
+	switch(il->type) {
+	case gbmap:
+		err = bitmap_clear(&il->list.gbmap.dup_map, block);
+		err = bitmap_clear(&il->list.gbmap.bad_map, block);
+		err = bitmap_clear(&il->list.gbmap.eattr_map, block);
+		err = bitmap_clear(&il->list.gbmap.group_map, block);
+		break;
+	default:
+		log_err("block list type %d not implemented\n",
+			il->type);
+		err = -1;
+		break;
+	}
+	return err;
+}
+
 int block_check(struct block_list *il, uint64_t block, struct block_query *val)
 {
 	int err = 0;
diff --git a/gfs/gfs_fsck/block_list.h b/gfs/gfs_fsck/block_list.h
index ca1524b..71c5642 100644
--- a/gfs/gfs_fsck/block_list.h
+++ b/gfs/gfs_fsck/block_list.h
@@ -77,7 +77,8 @@ struct block_list {
 struct block_list *block_list_create(uint64_t size, enum block_list_type type);
 int block_mark(struct block_list *il, uint64_t block, enum mark_block mark);
 int block_set(struct block_list *il, uint64_t block, enum mark_block mark);
-int block_clear(struct block_list *il, uint64_t block, enum mark_block m);
+int block_unmark(struct block_list *il, uint64_t block, enum mark_block m);
+int block_clear(struct block_list *il, uint64_t block);
 int block_check(struct block_list *il, uint64_t block,
 		struct block_query *val);
 int block_check_for_mark(struct block_list *il, uint64_t block,
diff --git a/gfs/gfs_fsck/fs_dir.c b/gfs/gfs_fsck/fs_dir.c
index 399575e..c826e87 100644
--- a/gfs/gfs_fsck/fs_dir.c
+++ b/gfs/gfs_fsck/fs_dir.c
@@ -1726,8 +1726,7 @@ int dirent_repair(struct fsck_inode *ip, osi_buf_t *bh, struct gfs_dirent *de,
 				sizeof(struct gfs_dinode);
 		else
 			de->de_rec_len = BH_SIZE(bh) - sizeof(struct gfs_leaf);
-	}
-	else {
+	} else {
 		bh_end = BH_DATA(bh) + BH_SIZE(bh);
 		/* first, figure out a probable name length */
 		p = (char *)dent + sizeof(struct gfs_dirent);
@@ -1750,3 +1749,23 @@ int dirent_repair(struct fsck_inode *ip, osi_buf_t *bh, struct gfs_dirent *de,
 	write_buf(ip->i_sbd, bh, 0);
 	return 0;
 }
+
+/**
+ * dirblk_truncate - truncate a directory block
+ */
+void dirblk_truncate(struct fsck_inode *ip, struct gfs_dirent *fixb,
+		     osi_buf_t *bh)
+{
+	char *bh_end;
+	struct gfs_dirent de;
+	uint16_t old_rec_len;
+
+	bh_end = BH_DATA(bh) + BH_SIZE(bh);
+	/* truncate the block to save the most dentries.  To do this we
+	   have to patch the previous dent. */
+	gfs_dirent_in(&de, (char *)fixb);
+	old_rec_len = de.de_rec_len;
+	de.de_rec_len = bh_end - (char *)fixb;
+	gfs_dirent_out(&de, (char *)fixb);
+	write_buf(ip->i_sbd, bh, 0);
+}
diff --git a/gfs/gfs_fsck/fs_dir.h b/gfs/gfs_fsck/fs_dir.h
index c0e514b..f741723 100644
--- a/gfs/gfs_fsck/fs_dir.h
+++ b/gfs/gfs_fsck/fs_dir.h
@@ -29,5 +29,7 @@ int fs_dirent_alloc(struct fsck_inode *dip, osi_buf_t *bh,
 int fs_dir_search(struct fsck_inode *dip, identifier_t *id, unsigned int *type);
 int dirent_repair(struct fsck_inode *ip, osi_buf_t *bh, struct gfs_dirent *de, 
 		  struct gfs_dirent *dent, int type, int first);
+void dirblk_truncate(struct fsck_inode *ip, struct gfs_dirent *fixb,
+		     osi_buf_t *bh);
 
 #endif /* __FS_DIR_H__ */
diff --git a/gfs/gfs_fsck/metawalk.c b/gfs/gfs_fsck/metawalk.c
index 8667897..135d200 100644
--- a/gfs/gfs_fsck/metawalk.c
+++ b/gfs/gfs_fsck/metawalk.c
@@ -8,9 +8,22 @@
 
 #include "metawalk.h"
 
-int check_entries(struct fsck_inode *ip, osi_buf_t *bh, int index,
-		  int type, int *update, uint16_t *count,
-		  struct metawalk_fxns *pass)
+/*
+ * check_entries - check directory entries for a given block
+ *
+ * @ip - dinode associated with this leaf block
+ * bh - buffer for the leaf block
+ * type - type of block this is (linear or exhash)
+ * @update - set to 1 if the block was updated
+ * @count - set to the count entries
+ * @pass - structure pointing to pass-specific functions
+ *
+ * returns: 0 - good block or it was repaired to be good
+ *         -1 - error occurred
+ */
+static int check_entries(struct fsck_inode *ip, osi_buf_t *bh, int type,
+			 int *update, uint16_t *count,
+			 struct metawalk_fxns *pass)
 {
 	struct gfs_leaf *leaf = NULL;
 	struct gfs_dirent *dent;
@@ -38,31 +51,46 @@ int check_entries(struct fsck_inode *ip, osi_buf_t *bh, int index,
 	}
 
 	prev = NULL;
-	if(!pass->check_dentry) {
+	if(!pass->check_dentry)
 		return 0;
-	}
 
 	while(1) {
+		if (skip_this_pass || fsck_abort)
+			return 0;
 		memset(&de, 0, sizeof(struct gfs_dirent));
 		gfs_dirent_in(&de, (char *)dent);
 		filename = (char *)dent + sizeof(struct gfs_dirent);
 
 		if (de.de_rec_len < sizeof(struct gfs_dirent) +
-		    de.de_name_len || !de.de_name_len) {
+		    de.de_name_len ||
+		    (de.de_inum.no_formal_ino && !de.de_name_len && !first)) {
 			log_err("Directory block %"
 				PRIu64 ", entry %d of directory %"
 				PRIu64 " is corrupt.\n", BH_BLKNO(bh),
 				(*count) + 1, ip->i_di.di_num.no_addr);
 			if (query(ip->i_sbd, "Attempt to repair it? (y/n) ")) {
 				if (dirent_repair(ip, bh, &de, dent, type,
-						  first))
-					break;
-			}
-			else {
-				log_err("Corrupt directory entry %d ignored, "
+						  first)) {
+					if (first) /* make a new sentinel */
+						dirblk_truncate(ip, dent, bh);
+					else
+						dirblk_truncate(ip, prev, bh);
+					*update = 1;
+					log_err("Unable to repair corrupt "
+						"directory entry; the entry "
+						"was removed instead.\n");
+					return 0;
+				} else {
+					log_err("Corrupt directory entry "
+						"repaired.\n");
+					*update = 1;
+					/* keep looping through dentries */
+				}
+			} else {
+				log_err("Corrupt directory entry ignored, "
 					"stopped after checking %d entries.\n",
 					*count);
-				break;
+				return 0;
 			}
 		}
 		if (!de.de_inum.no_formal_ino){
@@ -70,9 +98,23 @@ int check_entries(struct fsck_inode *ip, osi_buf_t *bh, int index,
 				log_debug("First dirent is a sentinel (place holder).\n");
 				first = 0;
 			} else {
-				/* FIXME: Do something about this */
-				log_err("Directory entry with inode number of zero in leaf %"PRIu64" of directory %"PRIu64"!\n", BH_BLKNO(bh), ip->i_di.di_num.no_addr);
-				return 1;
+				log_err("Directory entry with inode number of "
+					"zero in leaf %"PRIu64" of directory "
+					"%"PRIu64"!\n", BH_BLKNO(bh),
+					ip->i_di.di_num.no_addr);
+				if (query(ip->i_sbd,
+					  "Attempt to remove it? (y/n) ")) {
+					dirblk_truncate(ip, prev, bh);
+					*update = 1;
+					log_err("The corrupt directory entry "
+						"was removed.\n");
+				} else {
+					log_err("Corrupt directory entry "
+						"ignored, stopped after "
+						"checking %d entries.\n",
+						*count);
+				}
+				return 0;
 			}
 		} else {
 
@@ -84,9 +126,6 @@ int check_entries(struct fsck_inode *ip, osi_buf_t *bh, int index,
 				stack;
 				return -1;
 			}
-			/*if(error > 0) {
-			  return 1;
-			  }*/
 		}
 
 		if ((char *)dent + de.de_rec_len >= bh_end){
@@ -96,27 +135,22 @@ int check_entries(struct fsck_inode *ip, osi_buf_t *bh, int index,
 
 		/* If we didn't clear the dentry, or if we did, but it
 		 * was the first dentry, set prev  */
-		if(!error || first) {
+		if(!error || first)
 			prev = dent;
-		}
-
 		first = 0;
-
-
 		dent = (struct gfs_dirent *)((char *)dent + de.de_rec_len);
 	}
 	return 0;
 }
 
-
 /* Process a bad leaf pointer and ask to repair the first time.      */
 /* The repair process involves extending the previous leaf's entries */
 /* so that they replace the bad ones.  We have to hack up the old    */
 /* leaf a bit, but it's better than deleting the whole directory,    */
 /* which is what used to happen before.                              */
-void warn_and_patch(struct fsck_inode *ip, uint64_t *leaf_no, 
-		    uint64_t *bad_leaf, uint64_t old_leaf, int index,
-		    const char *msg)
+static void warn_and_patch(struct fsck_inode *ip, uint64_t *leaf_no,
+		    uint64_t *bad_leaf, uint64_t old_leaf,
+		    uint64_t first_ok_leaf, int lindex, const char *msg)
 {
 	if (*bad_leaf != *leaf_no) {
 		log_err("Directory Inode %" PRIu64 " points to leaf %"
@@ -125,7 +159,12 @@ void warn_and_patch(struct fsck_inode *ip, uint64_t *leaf_no,
 	}
 	if (*leaf_no == *bad_leaf ||
 	    query(ip->i_sbd, "Attempt to patch around it? (y/n) ")) {
-		put_leaf_nr(ip, index, old_leaf);
+		if (check_range(ip->i_sbd, old_leaf) == 0)
+			put_leaf_nr(ip, lindex, old_leaf);
+		else
+			put_leaf_nr(ip, lindex, first_ok_leaf);
+		log_err("Directory Inode %" PRIu64 ") repaired.\n",
+			ip->i_di.di_num.no_addr);
 	}
 	else
 		log_err("Bad leaf left in place.\n");
@@ -133,29 +172,51 @@ void warn_and_patch(struct fsck_inode *ip, uint64_t *leaf_no,
 	*leaf_no = old_leaf;
 }
 
-/* Checks exthash directory entries */
-int check_leaf(struct fsck_inode *ip, int *update, struct metawalk_fxns *pass)
+/* Checks all exhash directory leaf blocks (and their directory entries) */
+static int check_leaf_blks(struct fsck_inode *ip, int *update,
+			   struct metawalk_fxns *pass)
 {
 	int error;
 	struct gfs_leaf leaf, oldleaf;
 	uint64_t leaf_no, old_leaf, bad_leaf = -1;
+	uint64_t first_leaf_ptr = -1, first_ok_leaf = -1;
 	osi_buf_t *lbh;
-	int index;
+	int lindex;
 	struct fsck_sb *sbp = ip->i_sbd;
 	uint16_t count;
 	int ref_count = 0, exp_count = 0;
 
-	old_leaf = 0;
+	/* Find the first valid leaf pointer in range and use it as our "old"
+	   leaf. That way, bad blocks at the beginning will be overwritten
+	   with the first valid leaf. */
+	first_ok_leaf = -1;
+	for(lindex = 0; lindex < (1 << ip->i_di.di_depth); lindex++) {
+		get_leaf_nr(ip, lindex, &first_ok_leaf);
+		if (first_leaf_ptr == -1)
+			first_leaf_ptr = first_ok_leaf;
+		if(check_range(sbp, first_ok_leaf) == 0) {
+			get_and_read_buf(sbp, first_ok_leaf, &lbh, 0);
+			/* Make sure it's really a valid leaf block. */
+			if (check_meta(lbh, GFS_METATYPE_LF) == 0) {
+				relse_buf(sbp, lbh);
+				break;
+			}
+			relse_buf(sbp, lbh);
+		}
+	}
+	old_leaf = -1;
 	memset(&oldleaf, 0, sizeof(oldleaf));
-	for(index = 0; index < (1 << ip->i_di.di_depth); index++) {
-		if(get_leaf_nr(ip, index, &leaf_no)) {
+	for(lindex = 0; lindex < (1 << ip->i_di.di_depth); lindex++) {
+		if (fsck_abort)
+			break;
+		if(get_leaf_nr(ip, lindex, &leaf_no)) {
 			log_err("Unable to get leaf block number in dir %"
 				PRIu64"\n"
 				"\tDepth = %u\n"
-				"\tindex = %u\n",
+				"\tlindex = %u\n",
 				ip->i_num.no_addr,
 				ip->i_di.di_depth,
-				index);
+				lindex);
 			return -1;
 		}
 
@@ -163,56 +224,55 @@ int check_leaf(struct fsck_inode *ip, int *update, struct metawalk_fxns *pass)
 		 * until those extra pointers are needed, so skip the
 		 * dups */
 		if (leaf_no == bad_leaf) {
-			put_leaf_nr(ip, index, old_leaf); /* fill w/old leaf */
+			put_leaf_nr(ip, lindex, old_leaf); /* fill w/old leaf */
 			ref_count++;
 			continue;
 		}
 		else if(old_leaf == leaf_no) {
 			ref_count++;
 			continue;
-		} else {
-			if(ref_count != exp_count){
-				log_err("Dir #%"PRIu64" has an incorrect number "
-					 "of pointers to leaf #%"PRIu64"\n"
-					 "\tFound: %u,  Expected: %u\n",
-					 ip->i_num.no_addr,
-					 old_leaf,
-					 ref_count,
-					 exp_count);
-				if (query(ip->i_sbd, "Attempt to fix it? (y/n) ")) {
-					int factor = 0, divisor = ref_count;
-
-					get_and_read_buf(sbp, old_leaf, &lbh,
-							 0);
-					while (divisor > 1) {
-						factor++;
-						divisor /= 2;
-					}
-					oldleaf.lf_depth = ip->i_di.di_depth -
-						factor;
-					gfs_leaf_out(&oldleaf, BH_DATA(lbh));
-					write_buf(sbp, lbh, 0);
-					relse_buf(sbp, lbh);
+		}
+		if(check_range(sbp, old_leaf) == 0 && ref_count != exp_count){
+			log_err("Dir #%"PRIu64" has an incorrect number "
+				"of pointers to leaf #%"PRIu64"\n"
+				"\tFound: %u,  Expected: %u\n",
+				ip->i_num.no_addr, old_leaf, ref_count,
+				exp_count);
+			if (query(ip->i_sbd, "Attempt to fix it? (y/n) ")) {
+				int factor = 0, divisor = ref_count;
+
+				get_and_read_buf(sbp, old_leaf, &lbh, 0);
+				while (divisor > 1) {
+					factor++;
+					divisor /= 2;
 				}
-				else
-					return 1;
+				gfs_leaf_in(&oldleaf, BH_DATA(lbh));
+				oldleaf.lf_depth = ip->i_di.di_depth - factor;
+				gfs_leaf_out(&oldleaf, BH_DATA(lbh));
+				write_buf(sbp, lbh, 0);
+				relse_buf(sbp, lbh);
 			}
-			ref_count = 1;
+			else
+				return 1;
 		}
+		ref_count = 1;
 
 		count = 0;
 		do {
+			if (fsck_abort)
+				break;
 			/* Make sure the block number is in range. */
 			if(check_range(ip->i_sbd, leaf_no)){
 				log_err("Leaf block #%"PRIu64" is out of "
 					"range for directory #%"PRIu64".\n",
 					leaf_no, ip->i_di.di_num.no_addr);
 				warn_and_patch(ip, &leaf_no, &bad_leaf,
-					       old_leaf, index,
+					       old_leaf,  first_ok_leaf, lindex,
 					       "that is out of range");
 				memcpy(&leaf, &oldleaf, sizeof(oldleaf));
 				break;
 			}
+			*update = 0;
 			/* Try to read in the leaf block. */
 			if(get_and_read_buf(sbp, leaf_no, &lbh, 0)){
 				log_err("Unable to read leaf block #%"
@@ -220,7 +280,7 @@ int check_leaf(struct fsck_inode *ip, int *update, struct metawalk_fxns *pass)
 					"directory #%"PRIu64".\n",
 					leaf_no, ip->i_di.di_num.no_addr);
 				warn_and_patch(ip, &leaf_no, &bad_leaf,
-					       old_leaf, index,
+					       old_leaf, first_ok_leaf, lindex,
 					       "that cannot be read");
 				memcpy(&leaf, &oldleaf, sizeof(oldleaf));
 				relse_buf(sbp, lbh);
@@ -229,7 +289,7 @@ int check_leaf(struct fsck_inode *ip, int *update, struct metawalk_fxns *pass)
 			/* Make sure it's really a valid leaf block. */
 			if (check_meta(lbh, GFS_METATYPE_LF)) {
 				warn_and_patch(ip, &leaf_no, &bad_leaf,
-					       old_leaf, index,
+					       old_leaf, first_ok_leaf, lindex,
 					       "that is not really a leaf");
 				memcpy(&leaf, &oldleaf, sizeof(oldleaf));
 				relse_buf(sbp, lbh);
@@ -241,29 +301,34 @@ int check_leaf(struct fsck_inode *ip, int *update, struct metawalk_fxns *pass)
 							 pass->private);
 			}
 
+			/* Make sure it's really a leaf. */
+			if (leaf.lf_header.mh_type != GFS_METATYPE_LF) {
+				log_err("Inode %" PRIu64 " points to bad leaf "
+					PRIu64 ".\n", ip->i_di.di_num.no_addr,
+					leaf_no);
+				relse_buf(sbp, lbh);
+				break;
+			}
+
 			exp_count = (1 << (ip->i_di.di_depth - leaf.lf_depth));
 			log_debug("expected count %u - %u %u\n", exp_count,
 				  ip->i_di.di_depth, leaf.lf_depth);
 			if(pass->check_dentry && 
 			   ip->i_di.di_type == GFS_FILE_DIR) {
-				error = check_entries(ip, lbh, index,
-						      DIR_EXHASH, update,
-						      &count,
-						      pass);
+				error = check_entries(ip, lbh, DIR_EXHASH,
+						      update, &count, pass);
 
 				/* Since the buffer possibly got
-				   updated directly, release it now,
-				   and grab it again later if we need it */
+				 * updated directly, release it now,
+				 * and grab it again later if we need it. */
+
 				relse_buf(sbp, lbh);
+
 				if(error < 0) {
 					stack;
 					return -1;
 				}
 
-				if(error > 0) {
-					return 1;
-				}
-
 				if(update && (count != leaf.lf_entries)) {
 
 					if(get_and_read_buf(sbp, leaf_no,
@@ -289,22 +354,19 @@ int check_leaf(struct fsck_inode *ip, int *update, struct metawalk_fxns *pass)
 					relse_buf(sbp, lbh);
 				}
 				/* FIXME: Need to get entry count and
-				 * compare it against
-				 * leaf->lf_entries */
-
-				break;
+				 * compare it against leaf->lf_entries */
+				break; /* not a chain; go back to outer loop */
 			} else {
 				relse_buf(sbp, lbh);
-				if(!leaf.lf_next) {
+				if(!leaf.lf_next)
 					break;
-				}
 				leaf_no = leaf.lf_next;
 				log_debug("Leaf chain detected.\n");
 			}
-		} while(1);
+		} while(1); /* while we have chained leaf blocks */
 		old_leaf = leaf_no;
 		memcpy(&oldleaf, &leaf, sizeof(oldleaf));
-	}
+	} /* for every leaf block */
 	return 0;
 }
 
@@ -325,8 +387,12 @@ static int check_eattr_entries(struct fsck_inode *ip, osi_buf_t *bh,
 					  sizeof(struct gfs_meta_header));
 
 	while(1){
-		error = pass->check_eattr_entry(ip, bh, ea_hdr, ea_hdr_prev,
-						pass->private);
+		if (ea_hdr->ea_type == GFS_EATYPE_UNUSED)
+			error = 0;
+		else
+			error = pass->check_eattr_entry(ip, bh, ea_hdr,
+							ea_hdr_prev,
+							pass->private);
 		if(error < 0) {
 			stack;
 			return -1;
@@ -346,6 +412,7 @@ static int check_eattr_entries(struct fsck_inode *ip, osi_buf_t *bh,
 			** In this case, the EA ** code leaves
 			** the blocks ** there for **
 			** reuse...........  */
+
 			for(i = 0; i < ea_hdr->ea_num_ptrs; i++){
 				if(pass->check_eattr_extentry(ip,
 							      ea_data_ptr,
@@ -353,8 +420,8 @@ static int check_eattr_entries(struct fsck_inode *ip, osi_buf_t *bh,
 							      ea_hdr_prev,
 							      pass->private)) {
 					if (query(ip->i_sbd,
-						  "Repair the bad EA? "
-						  "(y/n) ")) {
+						  "Repair the bad Extended "
+						  "Attribute? (y/n) ")) {
 						ea_hdr->ea_num_ptrs = i;
 						ea_hdr->ea_data_len =
 							cpu_to_be32(tot_ealen);
@@ -362,10 +429,16 @@ static int check_eattr_entries(struct fsck_inode *ip, osi_buf_t *bh,
 						/* Endianness doesn't matter
 						   in this case because it's
 						   a single byte. */
+						block_set(sdp->bl,
+							  ip->i_di.di_eattr,
+							  meta_eattr);
 						write_buf(sdp, bh, 0);
-						return -1;
+						log_err("The EA was fixed.\n");
+					} else {
+						error = 1;
+						log_err("The bad EA was not "
+							"fixed.\n");
 					}
-					log_err("The bad EA was not fixed.\n");
 				}
 				tot_ealen += sdp->sb.sb_bsize -
 					sizeof(struct gfs_meta_header);
@@ -383,7 +456,7 @@ static int check_eattr_entries(struct fsck_inode *ip, osi_buf_t *bh,
 			 gfs32_to_cpu(ea_hdr->ea_rec_len));
 	}
 
-	return 0;
+	return error;
 }
 
 /**
@@ -391,7 +464,7 @@ static int check_eattr_entries(struct fsck_inode *ip, osi_buf_t *bh,
  * @ip: the inode the eattr comes from
  * @block: block number of the leaf
  *
- * Returns: 0 on success, -1 if removal is needed
+ * Returns: 0 on success, 1 if removal is needed, -1 on error
  */
 static int check_leaf_eattr(struct fsck_inode *ip, uint64_t block,
 			    uint64_t parent, struct metawalk_fxns *pass)
@@ -408,7 +481,8 @@ static int check_leaf_eattr(struct fsck_inode *ip, uint64_t block,
 			return -1;
 		}
 		if(error > 0) {
-			relse_buf(ip->i_sbd, bh);
+			if (bh)
+				relse_buf(ip->i_sbd, bh);
 			return 1;
 		}
 		if (bh) {
@@ -421,10 +495,6 @@ static int check_leaf_eattr(struct fsck_inode *ip, uint64_t block,
 	return 0;
 }
 
-
-
-
-
 /**
  * check_indirect_eattr
  * @ip: the inode the eattr comes from
@@ -433,12 +503,16 @@ static int check_leaf_eattr(struct fsck_inode *ip, uint64_t block,
  * Returns: 0 on success -1 on error
  */
 static int check_indirect_eattr(struct fsck_inode *ip, uint64_t indirect,
-				struct metawalk_fxns *pass){
+				struct metawalk_fxns *pass)
+{
 	int error = 0;
 	uint64_t *ea_leaf_ptr, *end;
 	uint64_t block;
 	osi_buf_t *indirect_buf = NULL;
 	struct fsck_sb *sdp = ip->i_sbd;
+	int update_indir_block = 0;
+	int first_ea_is_bad = 0;
+	uint64_t di_eattr_save = ip->i_di.di_eattr;
 
 	log_debug("Checking EA indirect block #%"PRIu64".\n", indirect);
 
@@ -451,41 +525,75 @@ static int check_indirect_eattr(struct fsck_inode *ip, uint64_t indirect,
 
 		ea_leaf_ptr = (uint64 *)(BH_DATA(indirect_buf)
 					 + sizeof(struct gfs_indirect));
-		end = ea_leaf_ptr
-			+ ((sdp->sb.sb_bsize
-			    - sizeof(struct gfs_indirect)) / 8);
+		end = ea_leaf_ptr + ((sdp->sb.sb_bsize
+				      - sizeof(struct gfs_indirect)) / 8);
 
 		while(*ea_leaf_ptr && (ea_leaf_ptr < end)){
 			block = gfs64_to_cpu(*ea_leaf_ptr);
 			leaf_pointers++;
 			error = check_leaf_eattr(ip, block, indirect, pass);
-			if (error)
+			if (error) {
 				leaf_pointer_errors++;
+				if (!update_indir_block) {
+					if (query(sdp, "Fix the indirect "
+						  "block too? (y/n) ")) {
+						update_indir_block = 1;
+						*ea_leaf_ptr = 0;
+					}
+				} else
+					*ea_leaf_ptr = 0;
+			}
+			/* If the first eattr lead is bad, we can't have
+			   a hole, so we have to treat this as an unrecoverable
+			   eattr error and delete all eattr info. Calling
+			   finish_eattr_indir here causes ip->i_di.di_eattr = 0
+			   and that ensures that subsequent calls to
+			   check_leaf_eattr result in the eattr
+			   check_leaf_block nuking them all "due to previous
+			   errors" */
+			if (leaf_pointers == 1 && leaf_pointer_errors == 1) {
+				first_ea_is_bad = 1;
+				if (pass->finish_eattr_indir)
+					pass->finish_eattr_indir(ip,
+							leaf_pointers,
+							leaf_pointer_errors,
+							pass->private);
+			} else if (leaf_pointer_errors) {
+				/* This is a bit tricky.  We can't have eattr
+				   holes. So if we have 4 good eattrs, 1 bad
+				   eattr and 5 more good ones: GGGGBGGGGG,
+				   we need to tell check_leaf_eattr to delete
+				   all eattrs after the bad one.  So we want:
+				   GGGG when we finish.  To do that, we set
+				   di_eattr to 0 temporarily. */
+				ip->i_di.di_eattr = 0;
+			}
 			ea_leaf_ptr++;
 		}
 		if (pass->finish_eattr_indir) {
-			int indir_ok = 1;
-
-			if (leaf_pointer_errors == leaf_pointers)
-				indir_ok = 0;
-			pass->finish_eattr_indir(ip, indir_ok, pass->private);
-			if (!indir_ok) {
+			if (!first_ea_is_bad) {
+				/* If the first ea is good but subsequent ones
+				   were bad and deleted, we need to restore
+				   the saved di_eattr block. */
+				if (leaf_pointer_errors)
+					ip->i_di.di_eattr = di_eattr_save;
+				pass->finish_eattr_indir(ip, leaf_pointers,
+							 leaf_pointer_errors,
+							 pass->private);
+			}
+			if (leaf_pointer_errors == leaf_pointers) {
 				fs_set_bitmap(sdp, indirect, GFS_BLKST_FREE);
-				block_clear(sdp->bl, indirect, indir_blk);
 				block_set(sdp->bl, indirect, block_free);
 				error = 1;
 			}
 		}
 	}
-
 	if (indirect_buf)
 		relse_buf(sdp, indirect_buf);
+
 	return error;
 }
 
-
-
-
 /**
  * check_inode_eattr - check the EA's for a single inode
  * @ip: the inode whose EA to check
@@ -496,9 +604,8 @@ int check_inode_eattr(struct fsck_inode *ip, struct metawalk_fxns *pass)
 {
 	int error = 0;
 
-	if(!ip->i_di.di_eattr){
+	if(!ip->i_di.di_eattr)
 		return 0;
-	}
 
 	log_debug("Extended attributes exist for inode #%"PRIu64".\n",
 		ip->i_num.no_formal_ino);
@@ -507,8 +614,9 @@ int check_inode_eattr(struct fsck_inode *ip, struct metawalk_fxns *pass)
 		if((error = check_indirect_eattr(ip, ip->i_di.di_eattr, pass)))
 			stack;
 	} else {
-		if((error = check_leaf_eattr(ip, ip->i_di.di_eattr,
-					     ip->i_di.di_num.no_addr, pass)))
+		error = check_leaf_eattr(ip, ip->i_di.di_eattr,
+					 ip->i_di.di_num.no_addr, pass);
+		if (error)
 			stack;
 	}
 
@@ -519,31 +627,27 @@ int check_inode_eattr(struct fsck_inode *ip, struct metawalk_fxns *pass)
  * build_metalist
  * @ip:
  * @mlp:
- *
  */
-
 static int build_metalist(struct fsck_inode *ip, osi_list_t *mlp,
 			  struct metawalk_fxns *pass)
 {
 	uint32 height = ip->i_di.di_height;
-	osi_buf_t *bh, *nbh;
+	osi_buf_t *bh, *nbh, *metabh;
 	osi_list_t *prev_list, *cur_list, *tmp;
 	int i, head_size;
 	uint64 *ptr, block;
 	int err;
 
-	if(get_and_read_buf(ip->i_sbd, ip->i_di.di_num.no_addr, &bh, 0)) {
+	if(get_and_read_buf(ip->i_sbd, ip->i_di.di_num.no_addr, &metabh, 0)) {
 		stack;
 		return -1;
 	}
 
-	osi_list_add(&bh->b_list, &mlp[0]);
+	osi_list_add(&metabh->b_list, &mlp[0]);
 
 	/* if(<there are no indirect blocks to check>) */
-	if (height < 2) {
+	if (height < 2)
 		return 0;
-	}
-
 	for (i = 1; i < height; i++){
 		prev_list = &mlp[i - 1];
 		cur_list = &mlp[i];
@@ -551,9 +655,17 @@ static int build_metalist(struct fsck_inode *ip, osi_list_t *mlp,
 		for (tmp = prev_list->next; tmp != prev_list; tmp = tmp->next){
 			bh = osi_list_entry(tmp, osi_buf_t, b_list);
 
-			head_size = (i > 1 ?
-				     sizeof(struct gfs_indirect) :
-				     sizeof(struct gfs_dinode));
+			if (i > 1) {
+				/* if this isn't really a block list skip it */
+				if (check_meta(bh, GFS_METATYPE_IN))
+					continue;
+				head_size = sizeof(struct gfs_indirect);
+			} else {
+				/* if this isn't really a dinode, skip it */
+				if (check_meta(bh, GFS_METATYPE_DI))
+					continue;
+				head_size = sizeof(struct gfs_dinode);
+			}
 
 			for (ptr = (uint64 *)(bh->b_data + head_size);
 			     (char *)ptr < (bh->b_data + bh->b_size);
@@ -564,7 +676,6 @@ static int build_metalist(struct fsck_inode *ip, osi_list_t *mlp,
 					continue;
 
 				block = gfs64_to_cpu(*ptr);
-
 				err = pass->check_metalist(ip, block, &nbh,
 							   pass->private);
 				if(err < 0) {
@@ -590,8 +701,7 @@ static int build_metalist(struct fsck_inode *ip, osi_list_t *mlp,
 	return 0;
 
  fail:
-	for (i = 0; i < GFS_MAX_META_HEIGHT; i++)
-	{
+	for (i = 0; i < GFS_MAX_META_HEIGHT; i++) {
 		osi_list_t *list;
 		list = &mlp[i];
 		while (!osi_list_empty(list))
@@ -601,6 +711,8 @@ static int build_metalist(struct fsck_inode *ip, osi_list_t *mlp,
 			relse_buf(ip->i_sbd, bh);
 		}
 	}
+	/* This is an error path, so we need to release the buffer here: */
+	relse_buf(ip->i_sbd, metabh);
 	return -1;
 }
 
@@ -624,7 +736,6 @@ int check_metatree(struct fsck_inode *ip, struct metawalk_fxns *pass)
 	if (!height)
 		goto end;
 
-
 	for (i = 0; i < GFS_MAX_META_HEIGHT; i++)
 		osi_list_init(&metalist[i]);
 
@@ -639,21 +750,28 @@ int check_metatree(struct fsck_inode *ip, struct metawalk_fxns *pass)
 	if (ip->i_di.di_type == GFS_FILE_DIR) {
 		log_debug("Directory with height > 0 at %"PRIu64"\n",
 			  ip->i_di.di_num.no_addr);
-
 	}
 
 	/* check data blocks */
 	list = &metalist[height - 1];
 
-	for (tmp = list->next; tmp != list; tmp = tmp->next)
-	{
+	for (tmp = list->next; tmp != list; tmp = tmp->next) {
 		bh = osi_list_entry(tmp, osi_buf_t, b_list);
 
-		head_size = (height != 1 ? sizeof(struct gfs_indirect) : sizeof(struct gfs_dinode));
+		if (height > 1) {
+			/* if this isn't really a block list skip it */
+			if (check_meta(bh, GFS_METATYPE_IN))
+				continue;
+			head_size = sizeof(struct gfs_indirect);
+		} else {
+			/* if this isn't really a dinode, skip it */
+			if (check_meta(bh, GFS_METATYPE_DI))
+				continue;
+			head_size = sizeof(struct gfs_dinode);
+		}
 		ptr = (uint64 *)(bh->b_data + head_size);
 
-		for ( ; (char *)ptr < (bh->b_data + bh->b_size); ptr++)
-		{
+		for ( ; (char *)ptr < (bh->b_data + bh->b_size); ptr++) {
 			if (!*ptr)
 				continue;
 
@@ -667,7 +785,6 @@ int check_metatree(struct fsck_inode *ip, struct metawalk_fxns *pass)
 		}
 	}
 
-
 	/* free metalists */
 	for (i = 0; i < GFS_MAX_META_HEIGHT; i++)
 	{
@@ -684,7 +801,7 @@ end:
 	if (ip->i_di.di_type == GFS_FILE_DIR) {
 		/* check validity of leaf blocks and leaf chains */
 		if (ip->i_di.di_flags & GFS_DIF_EXHASH) {
-			error = check_leaf(ip, &update, pass);
+			error = check_leaf_blks(ip, &update, pass);
 			if(error < 0)
 				return -1;
 			if(error > 0)
@@ -695,15 +812,14 @@ end:
 	return 0;
 }
 
-
 /* Checks stuffed inode directories */
-int check_linear_dir(struct fsck_inode *ip, osi_buf_t *bh, int *update,
+static int check_linear_dir(struct fsck_inode *ip, osi_buf_t *bh, int *update,
 		     struct metawalk_fxns *pass)
 {
 	int error = 0;
 	uint16_t count = 0;
 
-	error = check_entries(ip, bh, 0, DIR_LINEAR, update, &count, pass);
+	error = check_entries(ip, bh, DIR_LINEAR, update, &count, pass);
 	if(error < 0) {
 		stack;
 		return -1;
@@ -734,8 +850,7 @@ int check_dir(struct fsck_sb *sbp, uint64_t block, struct metawalk_fxns *pass)
 	}
 
 	if(ip->i_di.di_flags & GFS_DIF_EXHASH) {
-
-		error = check_leaf(ip, &update, pass);
+		error = check_leaf_blks(ip, &update, pass);
 		if(error < 0) {
 			stack;
 			free_inode(&ip);
@@ -757,10 +872,8 @@ int check_dir(struct fsck_sb *sbp, uint64_t block, struct metawalk_fxns *pass)
 	relse_buf(sbp, bh);
 
 	return error;
-
 }
 
-
 static int remove_dentry(struct fsck_inode *ip, struct gfs_dirent *dent,
 		  struct gfs_dirent *prev_de,
 		  osi_buf_t *bh, char *filename, int *update,
@@ -877,7 +990,6 @@ int dinode_hash_insert(osi_list_t *buckets, uint64_t key, struct dir_info *di)
 	return 0;
 }
 
-
 int dinode_hash_remove(osi_list_t *buckets, uint64_t key)
 {
 	osi_list_t *tmp;
@@ -896,3 +1008,66 @@ int dinode_hash_remove(osi_list_t *buckets, uint64_t key)
 	}
 	return -1;
 }
+
+/**
+ * free_block - free up a block given its block number
+ */
+void free_block(struct fsck_sb *sdp, uint64_t block)
+{
+	osi_buf_t *bh;
+	struct fsck_rgrp *rgd;
+
+	fs_set_bitmap(sdp, block, GFS_BLKST_FREE);
+	/* Adjust the free space count for the freed block */
+	rgd = fs_blk2rgrpd(sdp, block); /* find the rg for indir block */
+	get_and_read_buf(sdp, rgd->rd_ri.ri_addr, &bh, 0);
+	rgd->rd_rg.rg_free++; /* adjust the free count */
+	gfs_rgrp_out(&rgd->rd_rg, bh->b_data); /* back to the buffer */
+	relse_buf(sdp, bh); /* release the buffer */
+}
+
+/**
+ * delete_blocks - delete blocks associated with an inode
+ */
+int delete_blocks(struct fsck_inode *ip, uint64_t block, osi_buf_t **bh,
+		  const char *btype, void *private)
+{
+	struct block_query q = {0};
+
+	if (check_range(ip->i_sbd, block) == 0) {
+		if (block_check(ip->i_sbd->bl, block, &q))
+			return 0;
+		if (!q.dup_block) {
+			log_info("Deleting %s block %lld as part "
+				 "of inode %lld \n",
+				 btype, block, ip->i_di.di_num.no_addr);
+			block_set(ip->i_sbd->bl, block, block_free);
+			free_block(ip->i_sbd, block);
+		}
+	}
+	return 0;
+}
+
+int delete_metadata(struct fsck_inode *ip, uint64_t block, osi_buf_t **bh,
+		    void *private)
+{
+	return delete_blocks(ip, block, bh, "metadata", private);
+}
+
+int delete_data(struct fsck_inode *ip, uint64_t block, void *private)
+{
+	return delete_blocks(ip, block, NULL, "data", private);
+}
+
+int delete_eattr_indir(struct fsck_inode *ip, uint64_t block, uint64_t parent,
+		       osi_buf_t **bh, void *private)
+{
+	return delete_blocks(ip, block, NULL, "indirect extended attribute",
+			     private);
+}
+
+int delete_eattr_leaf(struct fsck_inode *ip, uint64_t block, uint64_t parent,
+		      osi_buf_t **bh, void *private)
+{
+	return delete_blocks(ip, block, NULL, "extended attribute", private);
+}
diff --git a/gfs/gfs_fsck/metawalk.h b/gfs/gfs_fsck/metawalk.h
index e5e6bd5..2d4eed2 100644
--- a/gfs/gfs_fsck/metawalk.h
+++ b/gfs/gfs_fsck/metawalk.h
@@ -14,6 +14,16 @@ int remove_dentry_from_dir(struct fsck_sb *sbp, uint64_t dir,
 int find_di(struct fsck_sb *sbp, uint64_t childblock, struct dir_info **dip);
 int dinode_hash_insert(osi_list_t *buckets, uint64_t key, struct dir_info *di);
 int dinode_hash_remove(osi_list_t *buckets, uint64_t key);
+void free_block(struct fsck_sb *sdp, uint64_t block);
+int delete_blocks(struct fsck_inode *ip, uint64_t block, osi_buf_t **bh,
+		  const char *btype, void *private);
+int delete_metadata(struct fsck_inode *ip, uint64_t block, osi_buf_t **bh,
+		    void *private);
+int delete_data(struct fsck_inode *ip, uint64_t block, void *private);
+int delete_eattr_indir(struct fsck_inode *ip, uint64_t block, uint64_t parent,
+		       osi_buf_t **bh, void *private);
+int delete_eattr_leaf(struct fsck_inode *ip, uint64_t block, uint64_t parent,
+		      osi_buf_t **bh, void *private);
 
 /* metawalk_fxns: function pointers to check various parts of the fs
  *
@@ -60,7 +70,8 @@ struct metawalk_fxns {
 				     struct gfs_ea_header *ea_hdr,
 				     struct gfs_ea_header *ea_hdr_prev,
 				     void *private);
-	int (*finish_eattr_indir) (struct fsck_inode *ip, int indir_ok,
+	int (*finish_eattr_indir) (struct fsck_inode *ip, int leaf_pointers,
+				   int leaf_pointer_errors,
 				   void *private);
 };
 
diff --git a/gfs/gfs_fsck/pass1.c b/gfs/gfs_fsck/pass1.c
index 3768ebf..8569db5 100644
--- a/gfs/gfs_fsck/pass1.c
+++ b/gfs/gfs_fsck/pass1.c
@@ -48,8 +48,8 @@ static int check_extended_leaf_eattr(struct fsck_inode *ip, uint64_t *data_ptr,
 				     struct gfs_ea_header *ea_hdr,
 				     struct gfs_ea_header *ea_hdr_prev,
 				     void *private);
-static int finish_eattr_indir(struct fsck_inode *ip, int indir_ok,
-			      void *private);
+static int finish_eattr_indir(struct fsck_inode *ip, int leaf_pointers,
+			      int leaf_pointer_errors, void *private);
 
 struct metawalk_fxns pass1_fxns = {
 	.private = NULL,
@@ -73,7 +73,6 @@ static int leaf(struct fsck_inode *ip, uint64_t block, osi_buf_t *bh,
 	log_debug("\tLeaf block at %15"PRIu64"\n", BH_BLKNO(bh));
 	block_set(sdp->bl, BH_BLKNO(bh), leaf_blk);
 	bc->indir_count++;
-
 	return 0;
 }
 
@@ -93,23 +92,22 @@ static int check_metalist(struct fsck_inode *ip, uint64_t block,
 		log_debug("Bad indirect block pointer (out of range).\n");
 
 		return 1;
-        }
+	}
 	if(block_check(sdp->bl, block, &q)) {
 		stack;
 		return -1;
 	}
 	if(q.block_type != block_free) {
-		log_debug("Found duplicate block in indirect block -"
-			  " was marked %d\n", q.block_type);
+		log_err("Found duplicate block referenced as metadata in "
+			"indirect block - was marked %d\n", q.block_type);
 		block_mark(sdp->bl, block, dup_block);
 		found_dup = 1;
 	}
         get_and_read_buf(ip->i_sbd, block, &nbh, 0);
 
-        /** Attention -- experimental code **/
-        if (check_meta(nbh, GFS_METATYPE_IN)){
-		log_debug("Bad indirect block pointer "
-			"(points to something that is not an indirect block).\n");
+	if (check_meta(nbh, GFS_METATYPE_IN)){
+		log_debug("Bad indirect block pointer (points to "
+			"something that is not an indirect block).\n");
 		if(!found_dup) {
 			block_set(sdp->bl, block, meta_inval);
 			relse_buf(ip->i_sbd, nbh);
@@ -117,29 +115,30 @@ static int check_metalist(struct fsck_inode *ip, uint64_t block,
 		}
 
 		relse_buf(ip->i_sbd, nbh);
-        }else{  /* blk check ok */
+	} else /* blk check ok */
 		*bh = nbh;
-        }
-        /** Attention -- experimental code end **/
 
-	block_set(sdp->bl, block, indir_blk);
+	if (!found_dup) {
+		log_debug("Setting %" PRIu64 " to indirect block.\n", block);
+		block_set(sdp->bl, block, indir_blk);
+	}
 	bc->indir_count++;
 
 	return 0;
 }
 
-
-
 static int check_data(struct fsck_inode *ip, uint64_t block, void *private)
 {
 	struct fsck_sb *sdp = ip->i_sbd;
 	struct block_query q = {0};
 	osi_buf_t *data_bh;
 	struct block_count *bc = (struct block_count *) private;
+	int error = 0, btype;
 
 	if (check_range(ip->i_sbd, block)) {
-
-		log_err( "Bad data block pointer (out of range)\n");
+		log_err("inode %lld has a bad data block pointer "
+			"%lld (out of range)\n",
+			ip->i_di.di_num.no_addr, block);
 		/* Mark the owner of this block with the bad_block
 		 * designator so we know to check it for out of range
 		 * blocks later */
@@ -148,60 +147,186 @@ static int check_data(struct fsck_inode *ip, uint64_t block, void *private)
 		return 1;
 	}
 
+	if(get_and_read_buf(ip->i_sbd, block, &data_bh, 0)) {
+		stack;
+		block_set(sdp->bl, ip->i_di.di_num.no_addr, meta_inval);
+		return 1;
+	}
+	if(block_check(sdp->bl, block, &q)) {
+		stack;
+		log_err("Found bad block referenced as data at %"
+			PRIu64 " (0x%"PRIx64 ")\n", block, block);
+		relse_buf(sdp, data_bh);
+		return -1;
+	}
 	if (ip->i_di.di_flags & GFS_DIF_JDATA){
-		/* Journaled data *is* metadata */
-		if(get_and_read_buf(ip->i_sbd, block, &data_bh, 0)) {
-			stack;
-			block_set(sdp->bl, ip->i_di.di_num.no_addr, meta_inval);
-			return 1;
-		}
 		if(check_meta(data_bh, GFS_METATYPE_JD)) {
 			log_err("Block #%"PRIu64" in inode %"PRIu64" does not have "
-				"correct meta header. is %u should be %u\n",
+				"correct meta header. Is %u should be %u\n",
 				block, ip->i_di.di_num.no_addr,
 				gfs32_to_cpu(((struct gfs_meta_header *)
 					      BH_DATA((data_bh)))->mh_type),
 				GFS_METATYPE_JD);
+			block_set(sdp->bl, ip->i_di.di_num.no_addr,
+				  meta_inval);
 			relse_buf(sdp, data_bh);
-			block_set(sdp->bl, ip->i_di.di_num.no_addr, meta_inval);
 			return 1;
 		}
 
-		if(block_check(sdp->bl, block, &q)) {
-			stack;
-			relse_buf(sdp, data_bh);
-			return -1;
+		if(q.block_type != block_free) {
+			log_err("Found duplicate block referenced as "
+				"journaled data at %" PRIu64"\n", block);
+			if (q.block_type != meta_inval) {
+				block_mark(sdp->bl, block, dup_block);
+				relse_buf(sdp, data_bh);
+				/* If the prev ref was as data, this is likely
+				   a data block, so keep the block count for
+				   both refs. */
+				if (q.block_type == block_used)
+					bc->data_count++;
+				return 1;
+			}
+			/* An explanation is in order here.  At this point we
+			   found a duplicate block, a block that was already
+			   referenced somewhere else.  We'll resolve those
+			   duplicates in pass1b. However, if the block is
+			   marked "invalid" that's a special case.  It's likely
+			   that the block was discovered to be invalid
+			   metadata--i.e. doesn't have a metadata header.
+			   However, it still may be a valid data block, since
+			   they won't have metadata headers.  In that case,
+			   the block is marked as duplicate, but also as a
+			   journaled data block. */
+			error = 1;
+			log_debug("Marking %" PRIu64 " as journaled data "
+				  "block\n", block);
+			block_unmark(sdp->bl, block, meta_inval);
+			block_mark(sdp->bl, block, dup_block);
 		}
+		block_mark(sdp->bl, block, journal_blk);
+	} else {
 		if(q.block_type != block_free) {
-			log_debug("Found duplicate block at %"
-				  PRIu64"\n", block);
+			log_err("Found duplicate block referenced as data "
+				"at %" PRIu64"\n", block);
+			if (q.block_type != meta_inval) {
+				block_mark(sdp->bl, block, dup_block);
+				bc->data_count++;
+				relse_buf(sdp, data_bh);
+				return 1;
+			}
+			error = 1;
+			log_debug("Marking %"PRIu64 " as data block\n", block);
+			block_unmark(sdp->bl, block, meta_inval);
 			block_mark(sdp->bl, block, dup_block);
-			bc->data_count++;
-			relse_buf(sdp, data_bh);
-			return 1;
 		}
-		log_debug("Setting %"PRIu64 " to journal block\n", block);
-		block_set(sdp->bl, block, journal_blk);
-		bc->data_count++;
-		relse_buf(sdp, data_bh);
+		block_mark(sdp->bl, block, block_used);
+	}
+	relse_buf(sdp, data_bh);
+	/* This is also confusing, so I'll clarify.  There are two bitmaps:
+	   (1) The bitmap that fsck uses to keep track of what block
+	   type has been discovered, and (2) The rgrp bitmap.  Function
+	   gfs_block_set is used to set the former and set_bitmap
+	   is used to set the latter.  In this function we need to set both
+	   because we found a "data" block that could be "meta" in the rgrp
+	   bitmap.  If we don't we could run into the data block again as
+	   metadata when we're traversing the metadata with next_rg_meta
+	   in func pass1().  If that happens, it will look at the block,
+	   say "hey this isn't metadata" and mark it incorrectly as an
+	   invalid metadata block and free it.  Ordinarily, fsck will wait
+	   until pass5 to sync (2) so that it agrees with (1).  However, in
+	   this case, it's better to do it upfront.  The duplicate solving
+	   code in pass1b.c is better at resolving metadata referencing a
+	   data block than it is at resolving a data block referencing a
+	   metadata block. */
+	btype = fs_get_bitmap(ip->i_sbd, block, NULL);
+	if (btype != GFS_BLKST_USED && btype != GFS_BLKST_USEDMETA) {
+		const char *allocdesc[] = {"free space", "data",
+					   "free metadata", "metadata",
+					   "reserved"};
+
+		if (ip->i_di.di_flags & GFS_DIF_JDATA) {
+			log_err("Block #%llu seems to be journaled data, but "
+				"is marked as %s.\n",
+				(unsigned long long)block, allocdesc[btype]);
+			if(query(sdp, "Okay to mark it as 'journaled data'? "
+				 "(y/n)")) {
+				fs_set_bitmap(sdp, block, GFS_BLKST_USEDMETA);
+				log_err("The block was reassigned as "
+					"journaled data.\n");
+			} else {
+				log_err("The invalid block was ignored.\n");
+			}
+		} else {
+			log_err("Block #%llu seems to be data, but "
+				"is marked as %s.\n",
+				(unsigned long long)block, allocdesc[btype]);
+			if(query(sdp, "Okay to mark it as 'data'? "
+				 "(y/n)")) {
+				fs_set_bitmap(sdp, block, GFS_BLKST_USED);
+				log_err("The block was reassigned as "
+					"journaled data.\n");
+			} else {
+				log_err("The invalid block was ignored.\n");
+			}
+		}
+	}
+	bc->data_count++;
+	return error;
+}
+
+static int remove_inode_eattr(struct fsck_inode *ip, struct block_count *bc,
+			      int duplicate)
+{
+	osi_buf_t *dibh = NULL;
+
+	if (!duplicate) {
+		fs_set_bitmap(ip->i_sbd, ip->i_di.di_eattr,
+				GFS_BLKST_FREEMETA);
+		block_set(ip->i_sbd->bl, ip->i_di.di_eattr, meta_free);
+	}
+	ip->i_di.di_eattr = 0;
+	bc->ea_count = 0;
+	ip->i_di.di_blocks = 1 + bc->indir_count + bc->data_count;
+	ip->i_di.di_flags &= ~GFS_DIF_EA_INDIRECT;
+	if (get_and_read_buf(ip->i_sbd, ip->i_num.no_addr, &dibh, 0)) {
+		log_err("The bad Extended Attribute could not be fixed.\n");
+		bc->ea_count++;
+		return 1;
+	}
+	gfs_dinode_out(&ip->i_di, BH_DATA(dibh));
+	if (write_buf(ip->i_sbd, dibh, 0) < 0) {
+		stack;
+		log_crit("The bad Extended Attribute remains.\n");
+		relse_buf(ip->i_sbd, dibh);
+		return -1;
+	} else {
+		log_warn("The bad Extended Attribute was removed.\n");
 	}
-	else {
-		if(block_check(sdp->bl, block, &q)) {
+	relse_buf(ip->i_sbd, dibh);
+	return 0;
+}
+
+static int ask_remove_inode_eattr(struct fsck_inode *ip,
+				  struct block_count *bc)
+{
+	log_err("Inode %lld has unrecoverable Extended Attribute "
+		"errors.\n", (unsigned long long)ip->i_di.di_num.no_addr);
+	if (query(ip->i_sbd, "Clear all Extended Attributes from the "
+		  "inode? (y/n) ")) {
+		struct block_query q;
+
+		if(block_check(ip->i_sbd->bl, ip->i_di.di_eattr, &q)) {
 			stack;
 			return -1;
 		}
-		if(q.block_type != block_free) {
-			log_debug("Found duplicate block at %"
-				  PRIu64"\n", block);
-			block_mark(sdp->bl, block, dup_block);
-			bc->data_count++;
-			return 1;
-		}
-		log_debug("Setting %"PRIu64 " to data block\n", block);
-		block_set(sdp->bl, block, block_used);
-		bc->data_count++;
+		if (!remove_inode_eattr(ip, bc, q.dup_block))
+			log_err("Extended attributes were removed.\n");
+		else
+			log_err("Unable to remove inode eattr pointer; "
+				"the error remains.\n");
+	} else {
+		log_err("Extended attributes were not removed.\n");
 	}
-
 	return 0;
 }
 
@@ -218,44 +343,23 @@ static int clear_eas(struct fsck_inode *ip, struct block_count *bc,
 		     uint64_t block, int duplicate, const char *emsg)
 {
 	struct fsck_sb *sdp = ip->i_sbd;
-	osi_buf_t *dibh = NULL;
 
 	log_err("Inode #%" PRIu64 " (0x%" PRIx64 "): %s",
 		ip->i_di.di_num.no_addr, ip->i_di.di_num.no_addr, emsg);
-	if (block)
-		log_err(" at block #%" PRIu64 " (0x%" PRIx64 ")",
-			block, block);
-	log_err(".\n");
-	if (query(sdp, "Clear the bad EA? (y/n) ")) {
-		if (block == 0)
-			block = ip->i_di.di_eattr;
-		block_clear(sdp->bl, block, eattr_block);
-		if (!duplicate) {
-			block_clear(sdp->bl, block, indir_blk);
-			block_set(sdp->bl, block, block_free);
-			fs_set_bitmap(sdp, block, GFS_BLKST_FREE);
-		}
-		ip->i_di.di_flags &= ~GFS_DIF_EA_INDIRECT;
-		if (block == ip->i_di.di_eattr)
-			ip->i_di.di_eattr = 0;
-		bc->ea_count = 0;
-		ip->i_di.di_blocks = 1 + bc->indir_count + bc->data_count;
-		if (get_and_read_buf(sdp, ip->i_num.no_addr, &dibh, 0)) {
-			log_err("The bad EA could not be fixed.\n");
-			bc->ea_count++;
-			return 0;
+	log_err(" at block #%" PRIu64 ".\n", block);
+	if (query(sdp, "Clear the bad Extended Attribute? (y/n) ")) {
+		if (block == ip->i_di.di_eattr) {
+			remove_inode_eattr(ip, bc, duplicate);
+			log_warn("The bad Extended Attribute was removed.\n");
+		} else if (!duplicate) {
+			block_set(sdp->bl, block, meta_free);
+			fs_set_bitmap(sdp, block, GFS_BLKST_FREEMETA);
+			log_err("The bad Extended Attribute was "
+				"removed.\n");
 		}
-		gfs_dinode_out(&ip->i_di, BH_DATA(dibh));
-		if (write_buf(sdp, dibh, 0) < 0) {
-			stack;
-			log_crit("Bad EA reference remains.\n");
-		} else {
-			log_warn("Bad EA reference cleared.\n");
-		}
-		relse_buf(sdp, dibh);
 		return 1;
 	} else {
-		log_err("The bad EA was not fixed.\n");
+		log_err("The bad Extended Attribute was not fixed.\n");
 		bc->ea_count++;
 		return 0;
 	}
@@ -279,7 +383,7 @@ static int check_eattr_indir(struct fsck_inode *ip, uint64_t indirect,
 		 * in pass1c */
 		return 1;
 	}
-	else if(block_check(sdp->bl, indirect, &q)) {
+	if(block_check(sdp->bl, indirect, &q)) {
 		stack;
 		return -1;
 	}
@@ -289,52 +393,129 @@ static int check_eattr_indir(struct fsck_inode *ip, uint64_t indirect,
 	   handling sort it out.  If it isn't, clear it but don't
 	   count it as a duplicate. */
 	if(get_and_read_buf(sdp, indirect, bh, 0)) {
-		log_warn("Unable to read EA indirect block #%"PRIu64".\n",
-			indirect);
+		log_warn("Unable to read Extended Attribute indirect block "
+			 "#%"PRIu64".\n", indirect);
 		block_set(sdp->bl, indirect, meta_inval);
 		return 1;
 	}
 	if(check_meta(*bh, GFS_METATYPE_IN)) {
 		if(q.block_type != block_free) {
 			if (!clear_eas(ip, bc, indirect, 1,
-				       "Bad indirect EA duplicate found"))
-				block_set(sdp->bl, indirect, dup_block);
+				       "Bad indirect Extended Attribute "
+				       "duplicate found")) {
+				block_mark(sdp->bl, indirect, dup_block);
+				bc->ea_count++;
+			}
 			return 1;
 		}
 		clear_eas(ip, bc, indirect, 0,
-			  "EA indirect block has incorrect type");
+			  "Extended Attribute indirect block has incorrect "
+			  "type");
 		return 1;
 	}
 	if(q.block_type != block_free) { /* Duplicate? */
-		log_err("Inode #%" PRIu64 "Duplicate block found at #%"PRIu64".\n",
-			indirect);
-		block_set(sdp->bl, indirect, dup_block);
+		log_err("Inode #%" PRIu64 "Duplicate Extended Attribute "
+			"indirect block found at #%"PRIu64".\n",
+			ip->i_di.di_num.no_addr, indirect);
+		block_mark(sdp->bl, indirect, dup_block);
 		bc->ea_count++;
 		ret = 1;
-	}
-	else {
+	} else {
 		log_debug("Setting #%" PRIu64
-			  ") to indirect EA block\n", indirect);
+			  ") to indirect Extended Attribute block\n",
+			  indirect);
 		block_set(sdp->bl, BH_BLKNO(*bh), indir_blk);
 		bc->ea_count++;
 	}
 	return ret;
 }
 
-static int finish_eattr_indir(struct fsck_inode *ip, int indir_ok,
-			      void *private)
+static int finish_eattr_indir(struct fsck_inode *ip, int leaf_pointers,
+			      int leaf_pointer_errors, void *private)
 {
-	if (indir_ok) {
-		log_debug("Marking inode #%" PRIu64 ") with eattr block\n",
-			  ip->i_di.di_num.no_addr);
-		/* Mark the inode as having an eattr in the block map
-		   so pass1c can check it. */
-		block_mark(ip->i_sbd->bl, ip->i_di.di_num.no_addr,
-			   eattr_block);
+	struct block_count *bc = (struct block_count *) private;
+
+	if (leaf_pointer_errors == leaf_pointers) /* All eas were bad */
+		return ask_remove_inode_eattr(ip, bc);
+	log_debug("Marking inode #%lld with extended attribute block\n",
+		  (unsigned long long)ip->i_di.di_num.no_addr);
+	/* Mark the inode as having an eattr in the block map
+	   so pass1c can check it. */
+	block_mark(ip->i_sbd->bl, ip->i_di.di_num.no_addr, eattr_block);
+	bc->ea_count++;
+	if (!leaf_pointer_errors)
 		return 0;
+	log_err("Inode %lld has recoverable indirect "
+		"Extended Attribute errors.\n",
+		(unsigned long long)ip->i_di.di_num.no_addr);
+	if (query(ip->i_sbd, "Okay to fix the block count for the inode? "
+		  "(y/n) ")) {
+		ip->i_di.di_blocks = 1 + bc->indir_count +
+			bc->data_count + bc->ea_count;
+		log_err("Block count fixed.\n");
+		return 1;
 	}
-	clear_eas(ip, (struct block_count *)private, 0, 0,
-		  "has unrecoverable indirect EA errors");
+	log_err("Block count not fixed.\n");
+	return 1;
+}
+
+static int check_leaf_block(struct fsck_inode *ip, uint64_t block, int btype,
+			    osi_buf_t **bh, void *private)
+{
+	osi_buf_t *leaf_bh = NULL;
+	struct fsck_sb *sdp = ip->i_sbd;
+	struct block_query q = {0};
+	struct block_count *bc = (struct block_count *) private;
+
+	if(block_check(sdp->bl, block, &q)) {
+		stack;
+		return -1;
+	}
+	/* Special duplicate processing:  If we have an EA block, check if it
+	   really is an EA.  If it is, let duplicate handling sort it out.
+	   If it isn't, clear it but don't count it as a duplicate. */
+	if(get_and_read_buf(sdp, block, &leaf_bh, 0)){
+		log_err("Unable to read leaf block %lld.\n", block);
+		return -1;
+	}
+	if(check_meta(leaf_bh, btype)) {
+		if(q.block_type != block_free) { /* Duplicate? */
+			clear_eas(ip, bc, block, 1,
+				  "Bad Extended Attribute duplicate found");
+		} else {
+			clear_eas(ip, bc, block, 0,
+				  "Extended Attribute leaf block "
+				  "has incorrect type");
+		}
+		relse_buf(sdp, leaf_bh);
+		return 1;
+	}
+	if(q.block_type != block_free) { /* Duplicate? */
+		log_debug("Duplicate block found at #%lld.\n",
+			  (unsigned long long)block);
+		block_mark(sdp->bl, block, dup_block);
+		bc->ea_count++;
+		relse_buf(sdp, leaf_bh);
+		return 1;
+	}
+	if (ip->i_di.di_eattr == 0) {
+		/* Can only get in here if there were unrecoverable ea
+		   errors that caused clear_eas to be called.  What we
+		   need to do here is remove the subsequent ea blocks. */
+		clear_eas(ip, bc, block, 0,
+			  "Extended Attribute block removed due to "
+			  "previous errors.\n");
+		relse_buf(sdp, leaf_bh);
+		return 1;
+	}
+	log_debug("Setting block #%lld (0x%llx) to eattr block\n",
+		  (unsigned long long)block, (unsigned long long)block);
+	/* Point of confusion: We've got to set the ea block itself to
+	   gfs2_meta_eattr here.  Elsewhere we mark the inode with
+	   gfs2_eattr_block meaning it contains an eattr for pass1c. */
+	block_set(sdp->bl, block, meta_eattr);
+	bc->ea_count++;
+	*bh = leaf_bh;
 	return 0;
 }
 
@@ -355,132 +536,46 @@ static int check_extended_leaf_eattr(struct fsck_inode *ip, uint64_t *data_ptr,
 				     struct gfs_ea_header *ea_hdr_prev,
 				     void *private)
 {
-	osi_buf_t *el_buf;
-	struct fsck_sb *sdp = ip->i_sbd;
-	struct block_query q;
 	uint64_t el_blk = gfs64_to_cpu(*data_ptr);
-	struct block_count *bc = (struct block_count *) private;
-	int ret = 0;
+	struct fsck_sb *sdp = ip->i_sbd;
+	osi_buf_t *bh = NULL;
+	int error;
 
 	if(check_range(sdp, el_blk)){
-		log_err("EA extended leaf block #%"PRIu64" "
-			"is out of range.\n",
-			el_blk);
+		log_err("Inode #%" PRIu64 ": Extended Attribute block %" PRIu64
+			" has an extended leaf block #%" PRIu64 " that is out "
+			"of range.\n", ip->i_di.di_num.no_addr,
+			ip->i_di.di_eattr, el_blk);
 		block_set(sdp->bl, ip->i_di.di_eattr, bad_block);
 		return 1;
 	}
-
-	if(block_check(sdp->bl, el_blk, &q)) {
-		stack;
-		return -1;
-	}
-	if(get_and_read_buf(sdp, el_blk, &el_buf, 0)){
-		log_err("Unable to check extended leaf block.\n");
-		block_set(sdp->bl, el_blk, meta_inval);
-		return 1;
-	}
-
-	/* Special duplicate processing:  If we have an EA block,
-	   check if it really is an EA.  If it is, let duplicate
-	   handling sort it out.  If it isn't, clear it but don't
-	   count it as a duplicate. */
-	if(check_meta(el_buf, GFS_METATYPE_ED)) {
-		if(q.block_type != block_free) /* Duplicate? */
-			clear_eas(ip, bc, el_blk, 1,
-				  "has bad extended EA duplicate");
-		else
-			clear_eas(ip, bc, el_blk, 0,
-				  "EA extended leaf block has incorrect type");
-		ret = 1;
-	} else { /* If this looks like an EA */
-		if(q.block_type != block_free) { /* Duplicate? */
-			log_debug("Duplicate block found at #%" PRIu64").\n",
-				  el_blk);
-			block_set(sdp->bl, el_blk, dup_block);
-			bc->ea_count++;
-			ret = 1;
-		} else {
-			log_debug("Setting block #%" PRIu64 ") to eattr block\n",
-				  el_blk);
-			block_set(sdp->bl, el_blk, meta_eattr);
-			bc->ea_count++;
-		}
-	}
-
-	relse_buf(sdp, el_buf);
-	return 0;
+	error = check_leaf_block(ip, el_blk, GFS_METATYPE_ED, &bh, private);
+	if (bh)
+		relse_buf(sdp, bh);
+	return error;
 }
 
 static int check_eattr_leaf(struct fsck_inode *ip, uint64_t block,
 			    uint64_t parent, osi_buf_t **bh, void *private)
 {
 	struct fsck_sb *sdp = ip->i_sbd;
-	osi_buf_t *leaf_bh;
-	int ret = 0;
-	struct block_query q = {0};
-	struct block_count *bc = (struct block_count *) private;
 
 	/* This inode contains an eattr - it may be invalid, but the
-	 * eattr attributes points to a non-zero block */
-	if (parent != ip->i_di.di_num.no_addr) { /* if parent isn't the inode */
-		log_debug("Setting %" PRIu64 ") to eattr block\n", parent);
-		block_set(sdp->bl, ip->i_num.no_addr, eattr_block);
-	}
-
+	 * eattr attributes points to a non-zero block.
+	 * Clarification: If we're here we're checking a leaf block, and the
+	 * source dinode needs to be marked as having extended attributes.
+	 * That instructs pass1c to check the contents of the ea blocks. */
+	log_debug("Setting inode %" PRIu64 ") as having eattr "
+		  "block\n", ip->i_num.no_addr);
+	block_mark(sdp->bl, ip->i_num.no_addr, eattr_block);
 	if(check_range(sdp, block)){
-		log_warn("EA leaf block #%"PRIu64" in inode %"PRIu64
-			 " is out of range.\n",
+		log_warn("Inode #%" PRIu64 " Extended Attribute leaf block "
+			 "#%" PRIu64 " is out of range.\n",
 			 ip->i_num.no_addr, block);
 		block_set(sdp->bl, ip->i_di.di_eattr, bad_block);
-		ret = 1;
-	}
-	else if(block_check(sdp->bl, block, &q)) {
-		stack;
-		return -1;
-	}
-	else {
-		/* Special duplicate processing:  If we have an EA block,
-		   check if it really is an EA.  If it is, let duplicate
-		   handling sort it out.  If it isn't, clear it but don't
-		   count it as a duplicate. */
-		if(get_and_read_buf(sdp, block, &leaf_bh, 0)){
-			log_warn("Unable to read EA leaf block #%"PRIu64".\n",
-				 block);
-			block_set(sdp->bl, block, meta_inval);
-			return 1;
-		}
-		if (check_meta(leaf_bh, GFS_METATYPE_EA)) {
-			if (q.block_type != block_free) { /* Duplicate? */
-				clear_eas(ip, bc, block, 1,
-					  "Bad EA duplicate found");
-			} else {
-				clear_eas(ip, bc, block, 0,
-					  "EA leaf block has incorrect type");
-			}
-			ret = 1;
-			relse_buf(sdp, leaf_bh);
-		} else { /* If this looks like an EA */
-			if (q.block_type != block_free) { /* Duplicate? */
-				log_debug("Duplicate block found at #%"PRIu64
-					  ".\n", block);
-				block_set(sdp->bl, block, dup_block);
-				bc->ea_count++;
-				ret = 1;
-				relse_buf(sdp, leaf_bh);
-			} else {
-				log_debug("Setting block #%" PRIu64
-					  " to eattr block\n", block);
-				block_set(sdp->bl, BH_BLKNO(leaf_bh),
-					  meta_eattr);
-				bc->ea_count++;
-			}
-		}
+		return 1;
 	}
-
-	if (!ret)
-		*bh = leaf_bh;
-
-	return ret;
+	return check_leaf_block(ip, block, GFS_METATYPE_EA, bh, private);
 }
 
 static int check_eattr_entries(struct fsck_inode *ip,
@@ -508,7 +603,7 @@ static int check_eattr_entries(struct fsck_inode *ip,
 	}
 
 	if(ea_hdr->ea_num_ptrs){
-		uint32 avail_size;
+		uint32_t avail_size;
 		int max_ptrs;
 
 		avail_size = sdp->sb.sb_bsize - sizeof(struct gfs_meta_header);
@@ -519,16 +614,13 @@ static int check_eattr_entries(struct fsck_inode *ip,
 		} else {
 			log_debug("  Pointers Required: %d\n"
 				  "  Pointers Reported: %d\n",
-				  max_ptrs,
-				  ea_hdr->ea_num_ptrs);
+				  max_ptrs, ea_hdr->ea_num_ptrs);
 		}
-
 	}
-
 	return 0;
 }
 
-int clear_metalist(struct fsck_inode *ip, uint64_t block,
+static int clear_metalist(struct fsck_inode *ip, uint64_t block,
 		   osi_buf_t **bh, void *private)
 {
 	struct fsck_sb *sdp = ip->i_sbd;
@@ -547,7 +639,7 @@ int clear_metalist(struct fsck_inode *ip, uint64_t block,
 	return 0;
 }
 
-int clear_data(struct fsck_inode *ip, uint64_t block, void *private)
+static int clear_data(struct fsck_inode *ip, uint64_t block, void *private)
 {
 	struct fsck_sb *sdp = ip->i_sbd;
 	struct block_query q = {0};
@@ -564,12 +656,12 @@ int clear_data(struct fsck_inode *ip, uint64_t block, void *private)
 
 }
 
-int clear_leaf(struct fsck_inode *ip, uint64_t block,
+static int clear_leaf(struct fsck_inode *ip, uint64_t block,
 	       osi_buf_t *bh, void *private)
 {
-
-	struct fsck_sb *sdp = ip->i_sbd;
 	struct block_query q = {0};
+	struct fsck_sb *sdp = ip->i_sbd;
+
 	log_crit("Clearing leaf %"PRIu64"\n", block);
 
 	if(block_check(sdp->bl, block, &q)) {
@@ -577,7 +669,7 @@ int clear_leaf(struct fsck_inode *ip, uint64_t block,
 		return -1;
 	}
 	if(!q.dup_block) {
-		log_crit("Setting leaf invalid\n");
+		log_crit("Setting leaf #%" PRIu64 " invalid\n", block);
 		if(block_set(sdp->bl, block, block_free)) {
 			stack;
 			return -1;
@@ -585,7 +677,6 @@ int clear_leaf(struct fsck_inode *ip, uint64_t block,
 		return 0;
 	}
 	return 0;
-
 }
 
 int add_to_dir_list(struct fsck_sb *sbp, uint64_t block)
@@ -609,19 +700,17 @@ int add_to_dir_list(struct fsck_sb *sbp, uint64_t block)
 		return -1;
 	}
 	if(!memset(newdi, 0, sizeof(*newdi))) {
-		log_crit("error while zeroing dir_info structure\n");
+		log_crit("Error while zeroing dir_info structure\n");
 		return -1;
 	}
 
 	newdi->dinode = block;
-
 	dinode_hash_insert(sbp->dir_hash, block, newdi);
-
 	return 0;
 }
 
 
-int handle_di(struct fsck_sb *sdp, osi_buf_t *bh, uint64_t block, int mfree)
+static int handle_di(struct fsck_sb *sdp, osi_buf_t *bh, uint64_t block, int mfree)
 {
 	struct block_query q = {0};
 	struct fsck_inode *ip;
@@ -696,8 +785,8 @@ int handle_di(struct fsck_sb *sdp, osi_buf_t *bh, uint64_t block, int mfree)
 		goto fail;
 	}
 	if(q.block_type != block_free) {
-		log_debug("Found duplicate block at %"PRIu64"\n",
-			  block);
+		log_err("Found duplicate block referenced as an inode at #%"
+			PRIu64"\n", block);
 		if(block_mark(sdp->bl, block, dup_block)) {
 			stack;
 			goto fail;
@@ -775,7 +864,8 @@ int handle_di(struct fsck_sb *sdp, osi_buf_t *bh, uint64_t block, int mfree)
 			 ip->i_di.di_num.no_addr, ip->i_di.di_height,
 			 compute_height(sdp, ip->i_di.di_size));
 			/* once implemented, remove continue statement */
-		log_warn("Marking inode invalid\n");
+		log_warn("Marking inode %lld invalid\n",
+			 ip->i_di.di_num.no_addr);
 		if(block_set(sdp->bl, block, meta_inval)) {
 			stack;
 			goto fail;
@@ -785,17 +875,16 @@ int handle_di(struct fsck_sb *sdp, osi_buf_t *bh, uint64_t block, int mfree)
 	}
 
 	if (ip->i_di.di_type == (GFS_FILE_DIR &&
-				 (ip->i_di.di_flags & GFS_DIF_EXHASH)))
-	{
+				 (ip->i_di.di_flags & GFS_DIF_EXHASH))) {
 		if (((1 << ip->i_di.di_depth) * sizeof(uint64_t)) !=
-		    ip->i_di.di_size)
-		{
+		    ip->i_di.di_size) {
 			log_warn("Directory dinode #%"PRIu64" has bad depth.  "
 				 "Found %u, Expected %u\n",
 				 ip->i_di.di_num.no_addr, ip->i_di.di_depth,
 				 (1 >> (ip->i_di.di_size/sizeof(uint64))));
 			/* once implemented, remove continue statement */
-			log_warn("Marking inode invalid\n");
+			log_warn("Marking inode %lld invalid\n",
+				 ip->i_di.di_num.no_addr);
 			if(block_set(sdp->bl, block, meta_inval)) {
 				stack;
 				goto fail;
@@ -808,45 +897,38 @@ int handle_di(struct fsck_sb *sdp, osi_buf_t *bh, uint64_t block, int mfree)
 	pass1_fxns.private = &bc;
 
 	error = check_metatree(ip, &pass1_fxns);
-	if(error < 0) {
+	if(fsck_abort || error < 0) {
 		return 0;
 	}
 	if(error > 0) {
-		log_warn("Marking inode invalid\n");
+		log_warn("Marking inode %lld invalid\n",
+			 ip->i_di.di_num.no_addr);
 		/* FIXME: Must set all leaves invalid as well */
 		check_metatree(ip, &invalidate_metatree);
 		block_set(ip->i_sbd->bl, ip->i_di.di_num.no_addr, meta_inval);
 		fs_set_bitmap(sdp, block, GFS_BLKST_FREE);
+		free(ip);
 		return 0;
 	}
 
-	/* FIXME: is this correct? */
-	if(check_inode_eattr(ip, &pass1_fxns) < 0){
-		osi_buf_t	*di_bh;
-		ip->i_di.di_eattr = 0;
-		if(get_and_read_buf(sdp, ip->i_di.di_num.no_addr, &di_bh, 0)){
-			stack;
-			log_crit("Bad EA reference remains.\n");
-		} else {
-			gfs_dinode_out(&ip->i_di, BH_DATA(di_bh));
-			if(write_buf(ip->i_sbd, di_bh, 0) < 0){
-				stack;
-				log_crit("Bad EA reference remains.\n");
-			} else {
-				log_warn("Bad EA reference cleared.\n");
-			}
-			relse_buf(sdp, di_bh);
-		}
-	}
+	error = check_inode_eattr(ip, &pass1_fxns);
 
-	if(ip->i_di.di_blocks != (1 + bc.indir_count + bc.data_count + bc.ea_count)) {
+	if (error && !(ip->i_di.di_flags & GFS_DIF_EA_INDIRECT))
+		ask_remove_inode_eattr(ip, &bc);
+
+	if (ip->i_di.di_blocks !=
+	    (1 + bc.indir_count + bc.data_count + bc.ea_count)) {
 		osi_buf_t	*di_bh;
+
 		log_err("Ondisk block count does not match what fsck"
 			" found for inode %"PRIu64"\n", ip->i_di.di_num.no_addr);
+		log_info("inode has: %lld, but fsck counts: Dinode:1 + indir:"
+			 "%lld + data: %lld + ea: %lld\n",
+			 ip->i_di.di_blocks, bc.indir_count, bc.data_count,
+			 bc.ea_count);
 		if(query(sdp, "Fix ondisk block count? (y/n) ")) {
 			ip->i_di.di_blocks = 1 + bc.indir_count +
-				bc.data_count +
-				bc.ea_count;
+				bc.data_count + bc.ea_count;
 			if(get_and_read_buf(sdp, ip->i_di.di_num.no_addr,
 					    &di_bh, 0)){
 				stack;
@@ -878,16 +960,20 @@ int handle_di(struct fsck_sb *sdp, osi_buf_t *bh, uint64_t block, int mfree)
 }
 
 
-int scan_meta(struct fsck_sb *sdp, osi_buf_t *bh, uint64_t block, int mfree)
+static int scan_meta(struct fsck_sb *sdp, osi_buf_t *bh, uint64_t block, int mfree)
 {
-
 	if (check_meta(bh, 0)) {
-		log_debug("Found invalid metadata at %"PRIu64"\n", block);
+		log_err("Found invalid metadata block at %"PRIu64"\n", block);
 		if(block_set(sdp->bl, block, meta_inval)) {
 			stack;
 			return -1;
 		}
-		fs_set_bitmap(sdp, block, GFS_BLKST_FREE);
+		if(query(sdp, "Okay to free the invalid block? (y/n)")) {
+			fs_set_bitmap(sdp, block, GFS_BLKST_FREE);
+			log_err("The invalid block was freed.\n");
+		} else {
+			log_err("The invalid block was ignored.\n");
+		}
 		return 0;
 	}
 
@@ -899,19 +985,12 @@ int scan_meta(struct fsck_sb *sdp, osi_buf_t *bh, uint64_t block, int mfree)
 			return -1;
 		}
 	}
-	else if (!check_type(bh, GFS_METATYPE_NONE)) {
-		if(block_set(sdp->bl, block, meta_free)) {
-			stack;
-			return -1;
-		}
-	} else {
-		log_debug("Metadata block %"PRIu64
-			  " not an inode or free metadata\n",
-			  block);
-	}
-	/* Ignore everything else - they should be hit by the
-	 * handle_di step */
-
+	/* Ignore everything else - they should be hit by the handle_di step.
+	 * Don't check NONE either, because check_meta passes everything if
+	 * GFS_METATYPE_NONE is specified.
+	 * Hopefully, other metadata types such as indirect blocks will be
+	 * handled when the inode itself is processed, and if it's not, it
+	 * should be caught in pass5. */
 	return 0;
 }
 
@@ -990,15 +1069,12 @@ int pass1(struct fsck_sb *sbp)
 
 		offset = sizeof(struct gfs_rgrp);
 		blk_count = 1;
-
 		first = 1;
 
 		while (1) {
-
 			/* "block" is relative to the entire file system */
 			if(next_rg_meta_free(rgd, &block, first, &mfree))
 				break;
-
 			warm_fuzzy_stuff(block);
 			if (fsck_abort) /* if asked to abort */
 				return 0;
diff --git a/gfs/gfs_fsck/pass1b.c b/gfs/gfs_fsck/pass1b.c
index 9f32aa6..301f7d0 100644
--- a/gfs/gfs_fsck/pass1b.c
+++ b/gfs/gfs_fsck/pass1b.c
@@ -87,7 +87,7 @@ static int check_eattr_leaf(struct fsck_inode *ip, uint64_t block,
 
 	inc_if_found(block, 0, private);
 	if(get_and_read_buf(sbp, block, &leaf_bh, 0)){
-		log_warn("Unable to read EA leaf block #%"PRIu64".\n",
+		log_err("Unable to read EA leaf block #%"PRIu64".\n",
 			 block);
 		return 1;
 	}
@@ -166,11 +166,11 @@ static int clear_dup_metalist(struct fsck_inode *ip, uint64_t block,
 	if(dh->ref_count == 1)
 		return 1;
 	if(block == dh->b->block_no) {
-		log_err("Found dup in inode \"%s\" (block #%"PRIu64
-			") with block #%"PRIu64"\n",
+		log_err("Found duplicate reference in inode \"%s\" at block #%"
+			PRIu64 " to block #%"PRIu64"\n",
 			dh->id->name ? dh->id->name : "unknown name",
 			ip->i_di.di_num.no_addr, block);
-		log_err("inode %s is in directory %"PRIu64"\n",
+		log_err("Inode %s is in directory %"PRIu64"\n",
 			dh->id->name ? dh->id->name : "",
 			dh->id->parent);
 		inode_hash_remove(ip->i_sbd->inode_hash, ip->i_di.di_num.no_addr);
@@ -182,26 +182,7 @@ static int clear_dup_metalist(struct fsck_inode *ip, uint64_t block,
 }
 static int clear_dup_data(struct fsck_inode *ip, uint64_t block, void *private)
 {
-	struct dup_handler *dh = (struct dup_handler *) private;
-
-	if(dh->ref_count == 1) {
-		return 1;
-	}
-	if(block == dh->b->block_no) {
-		log_err("Found dup in inode \"%s\" (block #%"PRIu64
-			") with block #%"PRIu64"\n",
-			dh->id->name ? dh->id->name : "unknown name",
-			ip->i_di.di_num.no_addr, block);
-		log_err("inode %s is in directory %"PRIu64"\n",
-			dh->id->name ? dh->id->name : "",
-			dh->id->parent);
-		inode_hash_remove(ip->i_sbd->inode_hash, ip->i_di.di_num.no_addr);
-		/* Setting the block to invalid means the inode is
-		 * cleared in pass2 */
-		block_set(ip->i_sbd->bl, ip->i_di.di_num.no_addr, meta_inval);
-	}
-
-	return 0;
+	return clear_dup_metalist(ip, block, NULL, private);
 }
 static int clear_dup_eattr_indir(struct fsck_inode *ip, uint64_t block,
 				 uint64_t parent, osi_buf_t **bh,
@@ -210,6 +191,7 @@ static int clear_dup_eattr_indir(struct fsck_inode *ip, uint64_t block,
 	struct dup_handler *dh = (struct dup_handler *) private;
 	/* Can't use fxns from eattr.c since we need to check the ref
 	 * count */
+	*bh = NULL;
 	if(dh->ref_count == 1)
 		return 1;
 	if(block == dh->b->block_no) {
@@ -286,8 +268,6 @@ static int clear_eattr_entry (struct fsck_inode *ip,
 				  max_ptrs,
 				  ea_hdr->ea_num_ptrs);
 		}
-
-
 	}
 	return 0;
 }
@@ -317,7 +297,7 @@ static int clear_eattr_extentry(struct fsck_inode *ip, uint64_t *ea_data_ptr,
 }
 
 /* Finds all references to duplicate blocks in the metadata */
-int find_block_ref(struct fsck_sb *sbp, uint64_t inode, struct blocks *b)
+static int find_block_ref(struct fsck_sb *sbp, uint64_t inode, struct blocks *b)
 {
 	struct fsck_inode *ip;
 	struct fxn_info myfi = {b->block_no, 0, 1};
@@ -338,15 +318,20 @@ int find_block_ref(struct fsck_sb *sbp, uint64_t inode, struct blocks *b)
 		stack;
 		return -1;
 	}
-	log_info("Checking inode %"PRIu64"'s metatree for references to block %"PRIu64"\n",
-		 inode, b->block_no);
+	log_debug("Checking inode %"PRIu64"'s metatree for references to "
+		  "block %"PRIu64"\n", inode, b->block_no);
 	if(check_metatree(ip, &find_refs)) {
 		stack;
 		free_inode(&ip);
 		return -1;
 	}
-	log_info("Done checking metatree\n");
-
+	log_debug("Done checking metatree\n");
+	/* Check for ea references in the inode */
+	if(check_inode_eattr(ip, &find_refs) < 0){
+		stack;
+		free_inode(&ip);
+		return -1;
+	}
 	if (myfi.found) {
 		if(!(id = malloc(sizeof(*id)))) {
 			log_crit("Unable to allocate inode_with_dups structure\n");
@@ -363,15 +348,13 @@ int find_block_ref(struct fsck_sb *sbp, uint64_t inode, struct blocks *b)
 		id->block_no = inode;
 		id->ea_only = myfi.ea_only;
 		osi_list_add_prev(&id->list, &b->ref_inode_list);
-		free_inode(&ip);
-		return 0;
 	}
 	free_inode(&ip);
 	return 0;
 }
 
 /* Finds all blocks marked in the duplicate block bitmap */
-int find_dup_blocks(struct fsck_sb *sbp)
+static int find_dup_blocks(struct fsck_sb *sbp)
 {
 	uint64_t block_no = 0;
 	struct blocks *b;
@@ -394,9 +377,7 @@ int find_dup_blocks(struct fsck_sb *sbp)
 	return 0;
 }
 
-
-
-int handle_dup_blk(struct fsck_sb *sbp, struct blocks *b)
+static int handle_dup_blk(struct fsck_sb *sbp, struct blocks *b)
 {
 	osi_list_t *tmp;
 	struct inode_with_dups *id;
@@ -419,16 +400,65 @@ int handle_dup_blk(struct fsck_sb *sbp, struct blocks *b)
 		dh.ref_inode_count++;
 		dh.ref_count += id->dup_count;
 	}
-	log_notice("Block %"PRIu64" has %d inodes referencing it for"
-		   "a total of %d duplicate references\n",
+	/* A single reference to the block implies a possible situation where
+	   a data pointer points to a metadata block.  In other words, the
+	   duplicate reference in the file system is (1) Metadata block X and
+	   (2) A dinode reference such as a data pointer pointing to block X.
+	   We can't really check for that in pass1 because user data might
+	   just _look_ like metadata by coincidence, and at the time we're
+	   checking, we might not have processed the referenced block.
+	   Here in pass1b we're sure. */
+	if (dh.ref_count == 1) {
+		osi_buf_t *bh;
+		uint32_t cmagic;
+
+		get_and_read_buf(sbp, b->block_no, &bh, 0);
+		cmagic = ((struct gfs_meta_header *)(bh->b_data))->mh_magic;
+		relse_buf(sbp, bh);
+		if (be32_to_cpu(cmagic) == GFS_MAGIC) {
+			tmp = b->ref_inode_list.next;
+			id = osi_list_entry(tmp, struct inode_with_dups, list);
+			log_warn("Inode %s (%lld) has a reference to "
+				 "data block %"PRIu64", but "
+				 "the block is really metadata.\n",
+				 id->name, id->block_no, b->block_no);
+			if (query(sbp, "Clear the inode? (y/n) ")) {
+				log_warn("Clearing inode %lld...\n",
+					 id->block_no);
+				load_inode(sbp, id->block_no, &ip);
+				inode_hash_remove(ip->i_sbd->inode_hash,
+						  ip->i_di.di_num.no_addr);
+				/* Setting the block to invalid means the inode
+				   is cleared in pass2 */
+				block_set(sbp->bl, ip->i_di.di_num.no_addr,
+					  meta_inval);
+				free_inode(&ip);
+			} else {
+				log_warn("The bad inode was not cleared.");
+			}
+			return 0;
+		}
+	}
+
+	log_notice("Block %"PRIu64" has %d inodes referencing it for "
+		   "a total of %d duplicate references.\n",
 		   b->block_no, dh.ref_inode_count, dh.ref_count);
 
 	osi_list_foreach(tmp, &b->ref_inode_list) {
 		id = osi_list_entry(tmp, struct inode_with_dups, list);
-		log_warn("Inode %s has %d reference(s) to block %"PRIu64
-			 "\n", id->name, id->dup_count, b->block_no);
-		/* FIXME: User input */
-		log_warn("Clearing...\n");
+		log_warn("Inode %s (%lld) has %d reference(s) to block %"PRIu64
+			 "\n", id->name, id->block_no, id->dup_count,
+			 b->block_no);
+	}
+	osi_list_foreach(tmp, &b->ref_inode_list) {
+		id = osi_list_entry(tmp, struct inode_with_dups, list);
+		if (!query(sbp, "Okay to clear inode %lld? (y/n) ",
+			   id->block_no)) {
+			log_warn("The inode %lld was not cleared...\n",
+				 id->block_no);
+			continue;
+		}
+		log_warn("Clearing inode %lld...\n", id->block_no);
 		load_inode(sbp, id->block_no, &ip);
 		dh.b = b;
 		dh.id = id;
@@ -439,6 +469,7 @@ int handle_dup_blk(struct fsck_sb *sbp, struct blocks *b)
 		if(!id->ea_only)
 			check_metatree(ip, &clear_dup_fxns);
 
+		block_set(sbp->bl, id->block_no, meta_inval);
 		free_inode(&ip);
 		dh.ref_inode_count--;
 		if(dh.ref_inode_count == 1)
@@ -460,11 +491,12 @@ int pass1b(struct fsck_sb *sbp)
 	struct blocks *b;
 	uint64_t i;
 	struct block_query q;
-	osi_list_t *tmp;
+	osi_list_t *tmp = NULL, *x;
 	struct metawalk_fxns find_dirents = {0};
-	find_dirents.check_dentry = &find_dentry;
 	int rc = 0;
 
+	find_dirents.check_dentry = &find_dentry;
+
 	osi_list_init(&sbp->dup_list);
 	/* Shove all blocks marked as duplicated into a list */
 	log_info("Looking for duplicate blocks...\n");
@@ -497,7 +529,7 @@ int pass1b(struct fsck_sb *sbp)
 		   (q.block_type == inode_chr) ||
 		   (q.block_type == inode_fifo) ||
 		   (q.block_type == inode_sock)) {
-			osi_list_foreach(tmp, &sbp->dup_list) {
+			osi_list_foreach_safe(tmp, &sbp->dup_list, x) {
 				b = osi_list_entry(tmp, struct blocks, list);
 				if(find_block_ref(sbp, i, b)) {
 					stack;
diff --git a/gfs/gfs_fsck/pass1c.c b/gfs/gfs_fsck/pass1c.c
index e65b862..d07d7cf 100644
--- a/gfs/gfs_fsck/pass1c.c
+++ b/gfs/gfs_fsck/pass1c.c
@@ -1,5 +1,6 @@
 #include "fsck.h"
 #include "fsck_incore.h"
+#include "fs_inode.h"
 #include "bio.h"
 #include "inode.h"
 #include "util.h"
@@ -10,14 +11,12 @@ static int remove_eattr_entry(struct fsck_sb *sdp, osi_buf_t *leaf_bh,
 			struct gfs_ea_header *curr,
 			struct gfs_ea_header *prev)
 {
-	log_warn("Removing EA located in block #%"PRIu64".\n",
-		 BH_BLKNO(leaf_bh));
-	if(!prev){
+	if(!prev)
 		curr->ea_type = GFS_EATYPE_UNUSED;
-	} else {
-		prev->ea_rec_len =
-			cpu_to_gfs32(gfs32_to_cpu(curr->ea_rec_len) +
-				     gfs32_to_cpu(prev->ea_rec_len));
+	else {
+		uint32_t tmp32 = gfs32_to_cpu(curr->ea_rec_len) +
+			gfs32_to_cpu(prev->ea_rec_len);
+		prev->ea_rec_len = cpu_to_gfs32(tmp32);
 		if (curr->ea_flags & GFS_EAFLAG_LAST)
 			prev->ea_flags |= GFS_EAFLAG_LAST;	
 	}
@@ -26,84 +25,120 @@ static int remove_eattr_entry(struct fsck_sb *sdp, osi_buf_t *leaf_bh,
 		log_err("EA removal failed.\n");
 		return -1;
 	}
+	log_err("Bad Extended Attribute at block #%"PRIu64" removed.\n",
+		 BH_BLKNO(leaf_bh));
 	return 0;
 }
 
-int check_eattr_indir(struct fsck_inode *ip, uint64_t block,
-		      uint64_t parent, osi_buf_t **bh,
-		      void *private)
+static int ask_remove_eattr_entry(struct fsck_sb *sdp, osi_buf_t *leaf_bh,
+				  struct gfs_ea_header *curr,
+				  struct gfs_ea_header *prev,
+				  int fix_curr, int fix_curr_len)
+{
+	if (query(sdp, "Remove the bad Extended Attribute? (y/n) ")) {
+		if (fix_curr)
+			curr->ea_flags |= GFS_EAFLAG_LAST;
+		if (fix_curr_len) {
+			uint32_t max_size = sdp->sb.sb_bsize;
+			uint32_t offset = (uint32_t)(((unsigned long)curr) -
+					     ((unsigned long)leaf_bh->b_data));
+			curr->ea_rec_len = cpu_to_be32(max_size - offset);
+		}
+		if (remove_eattr_entry(sdp, leaf_bh, curr, prev)) {
+			stack;
+			return -1;
+		}
+	} else {
+		log_err("Bad Extended Attribute not removed.\n");
+	}
+	return 1;
+}
+
+static int ask_remove_eattr(struct fsck_inode *ip)
+{
+	if (query(ip->i_sbd, "Remove the bad Extended Attribute? (y/n) ")) {
+		ip->i_di.di_eattr = 0;
+		if (fs_copyout_dinode(ip))
+			log_err("Bad Extended Attribute could not be "
+				"removed.\n");
+		else
+			log_err("Bad Extended Attribute removed.\n");
+	} else
+		log_err("Bad Extended Attribute not removed.\n");
+	return 1;
+}
+
+static int check_eattr_indir(struct fsck_inode *ip, uint64_t block,
+			     uint64_t parent, osi_buf_t **bh,
+			     void *private)
 {
 	int *update = (int *) private;
 	struct fsck_sb *sbp = ip->i_sbd;
 	struct block_query q;
-	osi_buf_t *indir_bh;
+	osi_buf_t *indir_bh = NULL;
 
+	*update = 0;
 	if(check_range(sbp, block)) {
-		log_err("Extended attributes indirect block out of range...removing\n");
-		ip->i_di.di_eattr = 0;
-		*update = 1;
-		return 1;
+		log_err("Extended attributes indirect block #%llu"
+			" for inode #%llu is out of range.\n",
+			(unsigned long long)block,
+			(unsigned long long)ip->i_num.no_addr);
+		return ask_remove_eattr(ip);
 	}
 	else if (block_check(sbp->bl, block, &q)) {
 		stack;
 		return -1;
 	}
 	else if(q.block_type != indir_blk) {
-		log_err("Extended attributes indirect block invalid...removing\n");
-		ip->i_di.di_eattr = 0;
-		*update = 1;
-		return 1;
+		log_err("Extended attributes indirect block #%llu"
+			" for inode #%llu is invalid.\n",
+			(unsigned long long)block,
+			(unsigned long long)ip->i_num.no_addr);
+		return ask_remove_eattr(ip);
 	}
 	else if(get_and_read_buf(sbp, block, &indir_bh, 0)){
-		log_warn("Unable to read EA leaf block #%"PRIu64".\n",
-			 block);
-		ip->i_di.di_eattr = 0;
-		*update = 1;
-		return 1;
+		log_warn("Unable to read Extended Attribute leaf block "
+			 "#%"PRIu64".\n", block);
+		return ask_remove_eattr(ip);
 	}
 
 	*bh = indir_bh;
 	return 0;
 }
-int check_eattr_leaf(struct fsck_inode *ip, uint64_t block,
-		     uint64_t parent, osi_buf_t **bh, void *private)
+
+static int check_eattr_leaf(struct fsck_inode *ip, uint64_t block,
+			    uint64_t parent, osi_buf_t **bh, void *private)
 {
-	int *update = (int *) private;
 	struct fsck_sb *sbp = ip->i_sbd;
 	struct block_query q;
 	osi_buf_t *leaf_bh;
 
 	if(check_range(sbp, block)) {
-		log_err("Extended attributes block out of range...removing\n");
-		ip->i_di.di_eattr = 0;
-		*update = 1;
-		return 1;
+		log_err("Extended attributes leaf block #%"PRIu64
+			" for inode #%" PRIu64 " is out of range.\n",
+			block, ip->i_num.no_addr);
+		return ask_remove_eattr(ip);
 	}
 	else if (block_check(sbp->bl, block, &q)) {
 		stack;
 		return -1;
 	}
 	else if(q.block_type != meta_eattr) {
-		log_err("Extended attributes block invalid...removing\n");
-		ip->i_di.di_eattr = 0;
-		*update = 1;
-		return 1;
+		log_err("Extended attributes leaf block #%"PRIu64
+			" for inode #%" PRIu64 " is invalid.\n",
+			block, ip->i_num.no_addr);
+		return ask_remove_eattr(ip);
 	}
 	else if(get_and_read_buf(sbp, block, &leaf_bh, 0)){
-		log_warn("Unable to read EA leaf block #%"PRIu64".\n",
-			 block);
-		ip->i_di.di_eattr = 0;
-		*update = 1;
-		return 1;
+		log_warn("Unable to read Extended attributes leaf block "
+			 "#%"PRIu64".\n", block);
+		return ask_remove_eattr(ip);
 	}
 
 	*bh = leaf_bh;
-
 	return 0;
-
 }
 
-
 static int check_eattr_entry(struct fsck_inode *ip,
 			     osi_buf_t *leaf_bh,
 			     struct gfs_ea_header *ea_hdr,
@@ -117,41 +152,24 @@ static int check_eattr_entry(struct fsck_inode *ip,
 	uint32_t max_size = sdp->sb.sb_bsize;
 	if(!ea_hdr->ea_rec_len){
 		log_err("EA has rec length == 0\n");
-		ea_hdr->ea_flags |= GFS_EAFLAG_LAST;
-		ea_hdr->ea_rec_len = cpu_to_gfs32(max_size - offset);
-		if(remove_eattr_entry(sdp, leaf_bh, ea_hdr, ea_hdr_prev)){
-			stack;
-			return -1;
-		}
-		return 1;
+		return ask_remove_eattr_entry(sdp, leaf_bh, ea_hdr,
+					      ea_hdr_prev, 1, 1);
 	}
 	if(offset + gfs32_to_cpu(ea_hdr->ea_rec_len) > max_size){
 		log_err("EA rec length too long\n");
-		ea_hdr->ea_flags |= GFS_EAFLAG_LAST;
-		ea_hdr->ea_rec_len = cpu_to_gfs32(max_size - offset);
-		if(remove_eattr_entry(sdp, leaf_bh, ea_hdr, ea_hdr_prev)){
-			stack;
-			return -1;
-		}
-		return 1;
+		return ask_remove_eattr_entry(sdp, leaf_bh, ea_hdr,
+					      ea_hdr_prev, 1, 1);
 	}
 	if(offset + gfs32_to_cpu(ea_hdr->ea_rec_len) == max_size &&
 	   (ea_hdr->ea_flags & GFS_EAFLAG_LAST) == 0){
 		log_err("last EA has no last entry flag\n");
-		ea_hdr->ea_flags |= GFS_EAFLAG_LAST;
-		if(remove_eattr_entry(sdp, leaf_bh, ea_hdr, ea_hdr_prev)){
-			stack;
-			return -1;
-		}
-		return 1;
+		return ask_remove_eattr_entry(sdp, leaf_bh, ea_hdr,
+					      ea_hdr_prev, 0, 0);
 	}
 	if(!ea_hdr->ea_name_len){
 		log_err("EA has name length == 0\n");
-		if(remove_eattr_entry(sdp, leaf_bh, ea_hdr, ea_hdr_prev)){
-			stack;
-			return -1;
-		}
-		return 1;
+		return ask_remove_eattr_entry(sdp, leaf_bh, ea_hdr,
+					      ea_hdr_prev, 0, 0);
 	}
 
 	memset(ea_name, 0, sizeof(ea_name));
@@ -162,11 +180,8 @@ static int check_eattr_entry(struct fsck_inode *ip,
 	   ((ea_hdr_prev) || (!ea_hdr_prev && ea_hdr->ea_type))){
 		log_err("EA (%s) type is invalid (%d > %d).\n",
 			ea_name, ea_hdr->ea_type, GFS_EATYPE_LAST);
-		if(remove_eattr_entry(sdp, leaf_bh, ea_hdr, ea_hdr_prev)){
-			stack;
-			return -1;
-		}
-		return 1;
+		return ask_remove_eattr_entry(sdp, leaf_bh, ea_hdr,
+					      ea_hdr_prev, 0, 0);
 	}
 
 	if(ea_hdr->ea_num_ptrs){
@@ -181,24 +196,18 @@ static int check_eattr_entry(struct fsck_inode *ip,
 			log_err("  Required:  %d\n"
 				"  Reported:  %d\n",
 				max_ptrs, ea_hdr->ea_num_ptrs);
-			if(remove_eattr_entry(sdp, leaf_bh, ea_hdr, ea_hdr_prev)){
-				stack;
-				return -1;
-			}
-			return 1;
+			return ask_remove_eattr_entry(sdp, leaf_bh, ea_hdr,
+						      ea_hdr_prev, 0, 0);
 		} else {
 			log_debug("  Pointers Required: %d\n"
 				  "  Pointers Reported: %d\n",
-				  max_ptrs,
-				  ea_hdr->ea_num_ptrs);
+				  max_ptrs, ea_hdr->ea_num_ptrs);
 		}
-
 	}
-
 	return 0;
 }
 
-int check_eattr_extentry(struct fsck_inode *ip, uint64_t *ea_ptr,
+static int check_eattr_extentry(struct fsck_inode *ip, uint64_t *ea_ptr,
 			 osi_buf_t *leaf_bh,
 			 struct gfs_ea_header *ea_hdr,
 			 struct gfs_ea_header *ea_hdr_prev,
@@ -206,6 +215,7 @@ int check_eattr_extentry(struct fsck_inode *ip, uint64_t *ea_ptr,
 {
 	struct block_query q;
 	struct fsck_sb *sbp = ip->i_sbd;
+
 	if(block_check(sbp->bl, gfs64_to_cpu(*ea_ptr), &q)) {
 		stack;
 		return -1;
@@ -252,6 +262,7 @@ int pass1c(struct fsck_sb *sbp)
 			return -1;
 		}
 
+		block_unmark(sbp->bl, block_no, eattr_block);
 		log_debug("Found eattr at %"PRIu64"\n", ip->i_di.di_eattr);
 		/* FIXME: Handle walking the eattr here */
 		error = check_inode_eattr(ip, &pass1c_fxns);
diff --git a/gfs/gfs_fsck/pass2.c b/gfs/gfs_fsck/pass2.c
index 450f26f..9c4a298 100644
--- a/gfs/gfs_fsck/pass2.c
+++ b/gfs/gfs_fsck/pass2.c
@@ -25,7 +25,7 @@ struct dir_status {
 
 /* Set children's parent inode in dir_info structure - ext2 does not set
  * dotdot inode here, but instead in pass3 - should we? */
-int set_parent_dir(struct fsck_sb *sbp, uint64_t childblock,
+static int set_parent_dir(struct fsck_sb *sbp, uint64_t childblock,
 		   uint64_t parentblock)
 {
 	struct dir_info *di;
@@ -51,7 +51,7 @@ int set_parent_dir(struct fsck_sb *sbp, uint64_t childblock,
 }
 
 /* Set's the child's '..' directory inode number in dir_info structure */
-int set_dotdot_dir(struct fsck_sb *sbp, uint64_t childblock,
+static int set_dotdot_dir(struct fsck_sb *sbp, uint64_t childblock,
 		   uint64_t parentblock)
 {
 	struct dir_info *di;
@@ -84,12 +84,22 @@ static int check_eattr_indir(struct fsck_inode *ip, uint64_t block,
 			    uint64_t parent, osi_buf_t **bh, void *private)
 {
 
+	osi_buf_t *indir_bh;
+
+	if(get_and_read_buf(ip->i_sbd, block, &indir_bh, 0)){
+		log_warn("Unable to read EA indir block #%"PRIu64".\n",
+			 block);
+		block_set(ip->i_sbd->bl, block, meta_inval);
+		return 1;
+	}
+	*bh = indir_bh;
+
 	return 0;
 }
 static int check_eattr_leaf(struct fsck_inode *ip, uint64_t block,
 			    uint64_t parent, osi_buf_t **bh, void *private)
 {
-	osi_buf_t *leaf_bh;
+	osi_buf_t *leaf_bh = NULL;
 
 	if(get_and_read_buf(ip->i_sbd, block, &leaf_bh, 0)){
 		log_warn("Unable to read EA leaf block #%"PRIu64".\n",
@@ -97,12 +107,13 @@ static int check_eattr_leaf(struct fsck_inode *ip, uint64_t block,
 		block_set(ip->i_sbd->bl, block, meta_inval);
 		return 1;
 	}
-
+	*bh = leaf_bh;
 
 	return 0;
 }
 
-static int check_file_type(uint16_t de_type, uint8_t block_type) {
+static int check_file_type(uint16_t de_type, uint8_t block_type)
+{
 	switch(block_type) {
 	case inode_dir:
 		if(de_type != GFS_FILE_DIR)
@@ -140,9 +151,17 @@ static int check_file_type(uint16_t de_type, uint8_t block_type) {
 	return 0;
 }
 
+struct metawalk_fxns pass2_fxns_delete = {
+	.private = NULL,
+	.check_metalist = delete_metadata,
+	.check_data = delete_data,
+	.check_eattr_indir = delete_eattr_indir,
+	.check_eattr_leaf = delete_eattr_leaf,
+};
+
 /* FIXME: should maybe refactor this a bit - but need to deal with
  * FIXMEs internally first */
-int check_dentry(struct fsck_inode *ip, struct gfs_dirent *dent,
+static int check_dentry(struct fsck_inode *ip, struct gfs_dirent *dent,
 		 struct gfs_dirent *prev_de,
 		 osi_buf_t *bh, char *filename, int *update,
 		 uint16_t *count, void *priv)
@@ -196,11 +215,10 @@ int check_dentry(struct fsck_inode *ip, struct gfs_dirent *dent,
 	/* FIXME: This should probably go to the top of the fxn, and
 	 * references to filename should be replaced with tmp_name */
 	memset(tmp_name, 0, MAX_FILENAME);
-	if(de->de_name_len < MAX_FILENAME){
+	if(de->de_name_len < MAX_FILENAME)
 		strncpy(tmp_name, filename, de->de_name_len);
-	} else {
+	else
 		strncpy(tmp_name, filename, MAX_FILENAME - 1);
-	}
 
 	if(check_range(ip->i_sbd, entryblock)) {
 		log_err("Block # referenced by directory entry %s is out of range\n",
@@ -210,10 +228,10 @@ int check_dentry(struct fsck_inode *ip, struct gfs_dirent *dent,
 			if(dirent_del(ip, bh, prev_de, dent))
 				log_err("Error encountered while removing bad "
 					"directory entry.  Skipping.\n");
+			*update = 1;
 			return 1;
 		} else {
 			log_err("Directory entry to out of range block remains\n");
-			*update = 1;
 			(*count)++;
 			ds->entry_count++;
 			return 0;
@@ -227,27 +245,26 @@ int check_dentry(struct fsck_inode *ip, struct gfs_dirent *dent,
 	if(q.bad_block) {
 		/* This entry's inode has bad blocks in it */
 
-		/* FIXME: user interface */
-		/* FIXME: do i want to kill the inode here? */
 		/* Handle bad blocks */
-		log_err("Found a bad directory entry: %s\n", filename);
-
-		if(query(sbp, "Clear entry to inode containing bad blocks? (y/n)")) {
-
-			load_inode(sbp, de->de_inum.no_addr, &entry_ip);
-			check_inode_eattr(entry_ip, &clear_eattrs);
-			free_inode(&entry_ip);
-
-			/* FIXME: make sure all blocks referenced by
-			 * this inode are cleared in the bitmap */
-
+		log_err("Found a bad directory entry: %s at block %lld.\n",
+			filename, de->de_inum.no_addr);
+
+		if(query(sbp, "Delete the inode containing bad blocks? "
+			 "(y/n)")) {
+			if (!load_inode(sbp, de->de_inum.no_addr, &entry_ip)) {
+				check_inode_eattr(entry_ip,
+						  &pass2_fxns_delete);
+				check_metatree(entry_ip, &pass2_fxns_delete);
+				free_inode(&entry_ip);
+			}
 			dirent_del(ip, bh, prev_de, dent);
-
-			block_set(sbp->bl, de->de_inum.no_addr, meta_inval);
+			block_set(sbp->bl, de->de_inum.no_addr, meta_free);
+			*update = 1;
+			log_warn("The inode containing bad blocks was "
+				 "deleted.\n");
 			return 1;
 		} else {
 			log_warn("Entry to inode containing bad blocks remains\n");
-			*update = 1;
 			(*count)++;
 			ds->entry_count++;
 			return 0;
@@ -258,32 +275,53 @@ int check_dentry(struct fsck_inode *ip, struct gfs_dirent *dent,
 	   q.block_type != inode_lnk && q.block_type != inode_blk &&
 	   q.block_type != inode_chr && q.block_type != inode_fifo &&
 	   q.block_type != inode_sock) {
-		log_err("Found directory entry '%s' in block %"
-			PRIu64" to something"
-			" not a file or directory!\n", tmp_name,
-			ip->i_num.no_addr);
-		log_debug("block #%"PRIu64" in %"PRIu64"\n",
-			  de->de_inum.no_addr, ip->i_num.no_addr);
+		log_err("Directory entry '%s' for block %" PRIu64
+			" in dir inode %" PRIu64 " block type %d: %s.\n",
+			tmp_name, de->de_inum.no_addr, ip->i_num.no_addr,
+			q.block_type, q.block_type == meta_inval ?
+			"previously marked invalid" : "is not an inode");
 
-		if(query(sbp, "Clear directory entry to non-inode block? (y/n) ")) {
-			/* FIXME: make sure all blocks referenced by
-			 * this inode are cleared in the bitmap */
+		if(query(sbp, "Clear directory entry to non-inode block? "
+			 "(y/n) ")) {
+			osi_buf_t *bhi;
 
-			if(dirent_del(ip, bh, prev_de, dent))
+			if(dirent_del(ip, bh, prev_de, dent)) {
 				log_err("Error encountered while removing bad "
 					"directory entry.  Skipping.\n");
+				return -1;
+			}
+			*update = 1;
 			log_warn("Directory entry '%s' cleared\n", tmp_name);
+
+			/* If it was previously marked invalid (i.e. known
+			   to be bad, not just a free block, etc.) then
+			   delete any metadata it holds.  If not, return. */
+			if (q.block_type != meta_inval)
+				return 1;
+
+			/* Now try to clear the dinode, if it is an dinode */
+			get_and_read_buf(sbp, de->de_inum.no_addr, &bhi, 0);
+			error = check_meta(bhi, GFS_METATYPE_DI);
+			relse_buf(sbp, bhi);
+			if (error)
+				return 1; /* not a dinode: nothing to delete */
+
+			if (!load_inode(sbp, de->de_inum.no_addr, &entry_ip)) {
+				check_inode_eattr(entry_ip,
+						  &pass2_fxns_delete);
+				check_metatree(entry_ip, &pass2_fxns_delete);
+				free_inode(&entry_ip);
+			}
+			block_set(sbp->bl, de->de_inum.no_addr, block_free);
 			return 1;
 		} else {
 			log_err("Directory entry to non-inode block remains\n");
-			*update = 1;
 			(*count)++;
 			ds->entry_count++;
 			return 0;
 		}
 	}
 
-
 	error = check_file_type(de->de_type, q.block_type);
 	if(error < 0) {
 		stack;
@@ -301,17 +339,17 @@ int check_dentry(struct fsck_inode *ip, struct gfs_dirent *dent,
 			if(dirent_del(ip, bh, prev_de, dent))
 				log_err("Error encountered while removing bad "
 					"directory entry.  Skipping.\n");
+			*update = 1;
+			log_err("Stale directory entry deleted\n");
 			return 1;
 		} else {
 			log_err("Stale directory entry remains\n");
-			*update  = 1;
 			(*count)++;
 			ds->entry_count++;
 			return 0;
 		}
 	}
 
-
 	if(!strcmp(".", tmp_name)) {
 		log_debug("Found . dentry\n");
 
@@ -324,6 +362,7 @@ int check_dentry(struct fsck_inode *ip, struct gfs_dirent *dent,
 				free_inode(&entry_ip);
 
 				dirent_del(ip, bh, prev_de, dent);
+				*update  = 1;
 				return 1;
 			} else {
 				log_err("Duplicate '.' entry remains\n");
@@ -331,7 +370,6 @@ int check_dentry(struct fsck_inode *ip, struct gfs_dirent *dent,
 				 * and check the rest of the '.'
 				 * entry? */
 				increment_link(sbp, de->de_inum.no_addr);
-				*update  = 1;
 				(*count)++;
 				ds->entry_count++;
 				return 0;
@@ -355,6 +393,7 @@ int check_dentry(struct fsck_inode *ip, struct gfs_dirent *dent,
 				free_inode(&entry_ip);
 
 				dirent_del(ip, bh, prev_de, dent);
+				*update = 1;
 				return 1;
 
 			} else {
@@ -362,7 +401,6 @@ int check_dentry(struct fsck_inode *ip, struct gfs_dirent *dent,
 				/* Not setting ds->dotdir here since
 				 * this '.' entry is invalid */
 				increment_link(sbp, de->de_inum.no_addr);
-				*update = 1;
 				(*count)++;
 				ds->entry_count++;
 				return 0;
@@ -371,7 +409,6 @@ int check_dentry(struct fsck_inode *ip, struct gfs_dirent *dent,
 
 		ds->dotdir = 1;
 		increment_link(sbp, de->de_inum.no_addr);
-		*update = 1;
 		(*count)++;
 		ds->entry_count++;
 
@@ -396,7 +433,6 @@ int check_dentry(struct fsck_inode *ip, struct gfs_dirent *dent,
 				 * and check the rest of the '..'
 				 * entry? */
 				increment_link(sbp, de->de_inum.no_addr);
-				*update  = 1;
 				(*count)++;
 				ds->entry_count++;
 				return 0;
@@ -417,7 +453,6 @@ int check_dentry(struct fsck_inode *ip, struct gfs_dirent *dent,
 			} else {
 				log_err("Bad '..' directory entry remains\n");
 				increment_link(sbp, de->de_inum.no_addr);
-				*update  = 1;
 				(*count)++;
 				ds->entry_count++;
 				return 0;
@@ -437,7 +472,7 @@ int check_dentry(struct fsck_inode *ip, struct gfs_dirent *dent,
 
 		ds->dotdotdir = 1;
 		increment_link(sbp, de->de_inum.no_addr);
-		*update = 1;
+		*update = (sbp->opts->no ? 0 : 1);
 		(*count)++;
 		ds->entry_count++;
 		return 0;
@@ -448,7 +483,7 @@ int check_dentry(struct fsck_inode *ip, struct gfs_dirent *dent,
 	if(q.block_type != inode_dir) {
 		log_debug("Found non-dir inode dentry\n");
 		increment_link(sbp, de->de_inum.no_addr);
-		*update = 1;
+		*update = (sbp->opts->no ? 0 : 1);
 		(*count)++;
 		ds->entry_count++;
 		return 0;
@@ -468,7 +503,6 @@ int check_dentry(struct fsck_inode *ip, struct gfs_dirent *dent,
 			return 1;
 		} else {
 			log_err("Hard link to directory remains\n");
-			*update = 1;
 			(*count)++;
 			ds->entry_count++;
 			return 0;
@@ -479,7 +513,7 @@ int check_dentry(struct fsck_inode *ip, struct gfs_dirent *dent,
 		return -1;
 	}
 	increment_link(sbp, de->de_inum.no_addr);
-	*update = 1;
+	*update = (sbp->opts->no ? 0 : 1);
 	(*count)++;
 	ds->entry_count++;
 	/* End of checks */
@@ -501,7 +535,7 @@ struct metawalk_fxns pass2_fxns = {
 
 
 
-int build_rooti(struct fsck_sb *sbp)
+static int build_rooti(struct fsck_sb *sbp)
 {
 	struct fsck_inode *ip;
 	osi_buf_t *bh;
@@ -553,7 +587,7 @@ int build_rooti(struct fsck_sb *sbp)
 }
 
 /* Check root inode and verify it's in the bitmap */
-int check_root_dir(struct fsck_sb *sbp)
+static int check_root_dir(struct fsck_sb *sbp)
 {
 	uint64_t rootblock;
 	struct dir_status ds = {0};
@@ -647,30 +681,35 @@ int check_root_dir(struct fsck_sb *sbp)
 
 	if(!ds.dotdir) {
 		log_err("No '.' entry found\n");
-		sprintf(tmp_name, ".");
-		filename.len = strlen(tmp_name);  /* no trailing NULL */
-		if(!(filename.name = malloc(sizeof(char) * filename.len))) {
-			log_err("Unable to allocate name string\n");
-			stack;
-			return -1;
-		}
-		if(!(memset(filename.name, 0, sizeof(char) * filename.len))) {
-			log_err("Unable to zero name string\n");
-			stack;
-			return -1;
-		}
-		memcpy(filename.name, tmp_name, filename.len);
-		log_warn("Adding '.' entry\n");
-		if(fs_dir_add(ip, &filename, &(ip->i_num),
-			      ip->i_di.di_type)){
-			log_err("Failed to link \".\" entry to directory.\n");
-			return -1;
-		}
-
-		increment_link(ip->i_sbd, ip->i_num.no_addr);
-		ds.entry_count++;
-		free(filename.name);
-		update = 1;
+		if (query(sbp, "Is it okay to add '.' entry? (y/n) ")) {
+			sprintf(tmp_name, ".");
+			filename.len = strlen(tmp_name); /* no trailing NULL */
+			if(!(filename.name =
+			     malloc(sizeof(char) * filename.len))) {
+				log_err("Unable to allocate name string\n");
+				stack;
+				return -1;
+			}
+			if(!(memset(filename.name, 0, sizeof(char) *
+				    filename.len))) {
+				log_err("Unable to zero name string\n");
+				stack;
+				return -1;
+			}
+			memcpy(filename.name, tmp_name, filename.len);
+			log_warn("Adding '.' entry\n");
+			if(fs_dir_add(ip, &filename, &(ip->i_num),
+				      ip->i_di.di_type)){
+				log_err("Failed to link \".\" entry to "
+					"directory.\n");
+				return -1;
+			}
+			increment_link(ip->i_sbd, ip->i_num.no_addr);
+			ds.entry_count++;
+			free(filename.name);
+			update = 1;
+		} else
+			log_err("The directory was not fixed.\n");
 	}
 	free_inode(&ip);
 	if(get_and_read_buf(sbp, rootblock, &bh, 0)){
@@ -730,16 +769,18 @@ int pass2(struct fsck_sb *sbp, struct options *opts)
 	osi_buf_t b, *bh = &b;
 	osi_filename_t filename;
 	char tmp_name[256];
+	int error = 0;
+	int need_update = 0;
+
 	if(check_root_dir(sbp)) {
 		stack;
 		return -1;
 	}
-	int error = 0;
 
 	log_info("Checking directory inodes.\n");
 	/* Grab each directory inode, and run checks on it */
 	for(i = 0; i < sbp->last_fs_block; i++) {
-
+		need_update = 0;
 		warm_fuzzy_stuff(i);
 		if (skip_this_pass || fsck_abort) /* if asked to skip the rest */
 			return 0;
@@ -823,63 +864,63 @@ int pass2(struct fsck_sb *sbp, struct options *opts)
 			relse_buf(sbp, bh);
 			return -1;
 		}
-		/* FIXME: Should not have to do this here - fs_dir_add reads
-		 * the buffer too though, and commits the change to disk, so I
-		 * have to reread the buffer after calling it if I'm going to
-		 * make more changes */
-		relse_buf(sbp, bh);
 
 		if(!ds.dotdir) {
-			log_err("No '.' entry found\n");
-			sprintf(tmp_name, ".");
-			filename.len = strlen(tmp_name);  /* no trailing NULL */
-			if(!(filename.name = malloc(sizeof(char) * filename.len))) {
-				log_err("Unable to allocate name string\n");
-				stack;
-				return -1;
-			}
-			if(!memset(filename.name, 0, sizeof(char) * filename.len)) {
-				log_err("Unable to zero name string\n");
-				stack;
-				return -1;
-			}
-			memcpy(filename.name, tmp_name, filename.len);
+			log_err("No '.' entry found for directory inode at "
+				"block %" PRIu64 "\n", i);
+			if (query(sbp,
+				  "Is it okay to add '.' entry? (y/n) ")) {
+				sprintf(tmp_name, ".");
+				filename.len = strlen(tmp_name); /* no trailing
+								    NULL */
+				if(!(filename.name = malloc(sizeof(char) *
+							    filename.len))) {
+					log_err("Unable to allocate name\n");
+					stack;
+					return -1;
+				}
+				if(!memset(filename.name, 0, sizeof(char) *
+					   filename.len)) {
+					log_err("Unable to zero name\n");
+					stack;
+					return -1;
+				}
+				memcpy(filename.name, tmp_name, filename.len);
 
-			if(fs_dir_add(ip, &filename, &(ip->i_num),
-				      ip->i_di.di_type)){
-				log_err("Failed to link \".\" entry to directory.\n");
-				return -1;
+				if(fs_dir_add(ip, &filename, &(ip->i_num),
+					      ip->i_di.di_type)){
+					log_err("Failed to link \".\" entry "
+						"to directory.\n");
+					return -1;
+				}
+				increment_link(ip->i_sbd, ip->i_num.no_addr);
+				ds.entry_count++;
+				free(filename.name);
+				log_err("The directory was fixed.\n");
+				need_update = 1;
+			} else {
+				log_err("The directory was not fixed.\n");
 			}
-
-			increment_link(ip->i_sbd, ip->i_num.no_addr);
-			ds.entry_count++;
-			free(filename.name);
-
-		}
-		free_inode(&ip);
-
-		if(get_and_read_buf(sbp, i, &bh, 0)){
-			log_err("Unable to retrieve block #%"PRIu64"\n",
-				i);
-			block_set(sbp->bl, i, meta_inval);
-			return -1;
 		}
 
-		if(copyin_inode(sbp, bh, &ip)) {
-			stack;
-			relse_buf(sbp, bh);
-			return -1;
-		}
 		if(ip->i_di.di_entries != ds.entry_count) {
-			log_err("Entries is %d - should be %d for %"PRIu64"\n",
+			log_err("Entries is %d - should be %d for inode "
+				"block %" PRIu64 "\n",
 				ip->i_di.di_entries, ds.entry_count,
 				ip->i_di.di_num.no_addr);
-			ip->i_di.di_entries = ds.entry_count;
+			if (query(sbp, "Fix the entry count? (y/n) ")) {
+				ip->i_di.di_entries = ds.entry_count;
+				need_update = 1;
+			} else {
+				log_err("The entry count was not fixed.\n");
+			}
+		}
+		if (need_update) {
 			gfs_dinode_out(&ip->i_di, BH_DATA(bh));
 			write_buf(sbp, bh, 0);
+			free_inode(&ip);
+			relse_buf(sbp, bh);
 		}
-		free_inode(&ip);
-		relse_buf(sbp, bh);
 	}
 	return 0;
 }
diff --git a/gfs/gfs_fsck/pass3.c b/gfs/gfs_fsck/pass3.c
index 3991147..4f6781b 100644
--- a/gfs/gfs_fsck/pass3.c
+++ b/gfs/gfs_fsck/pass3.c
@@ -25,23 +25,25 @@ static int attach_dotdot_to(struct fsck_sb *sbp, uint64_t newdotdot,
 	 * this case? */
 
 	filename.len = strlen("..");
-	if(!(filename.name = malloc(sizeof(char) * filename.len))) {
+	if(!(filename.name = malloc(sizeof(char) * filename.len + 1))) {
 		log_err("Unable to allocate name\n");
+		free_inode(&ip);
+		free_inode(&pip);
 		stack;
 		return -1;
 	}
-	if(!memset(filename.name, 0, sizeof(char) * filename.len)) {
+	if(!memset(filename.name, 0, (sizeof(char) * filename.len + 1))) {
 		log_err("Unable to zero name\n");
+		free_inode(&ip);
+		free_inode(&pip);
 		stack;
 		return -1;
 	}
 	memcpy(filename.name, "..", filename.len);
-	if(fs_dirent_del(ip, NULL, &filename)){
+	if(fs_dirent_del(ip, NULL, &filename))
 		log_warn("Unable to remove \"..\" directory entry.\n");
-	}
-	else {
+	else
 		decrement_link(sbp, olddotdot);
-	}
 	if(fs_dir_add(ip, &filename, &pip->i_num,
 		      pip->i_di.di_type)){
 		log_err("Failed to link \"..\" entry to directory.\n");
@@ -56,7 +58,7 @@ static int attach_dotdot_to(struct fsck_sb *sbp, uint64_t newdotdot,
 	return 0;
 }
 
-struct dir_info *mark_and_return_parent(struct fsck_sb *sbp,
+static struct dir_info *mark_and_return_parent(struct fsck_sb *sbp,
 					struct dir_info *di)
 {
 	struct dir_info *pdi;
@@ -65,9 +67,8 @@ struct dir_info *mark_and_return_parent(struct fsck_sb *sbp,
 
 	di->checked = 1;
 
-	if(!di->treewalk_parent) {
+	if(!di->treewalk_parent)
 		return NULL;
-	}
 
 	if(di->dotdot_parent != di->treewalk_parent) {
 		log_warn(".. and treewalk conections are not the same for %"PRIu64
@@ -104,7 +105,6 @@ struct dir_info *mark_and_return_parent(struct fsck_sb *sbp,
 				attach_dotdot_to(sbp, di->treewalk_parent,
 						 di->dotdot_parent, di->dinode);
 				di->dotdot_parent = di->treewalk_parent;
-
 			}
 		}
 		else {
@@ -145,7 +145,6 @@ struct dir_info *mark_and_return_parent(struct fsck_sb *sbp,
 				attach_dotdot_to(sbp, di->treewalk_parent,
 						 di->dotdot_parent, di->dinode);
 				di->dotdot_parent = di->treewalk_parent;
-
 			}
 		}
 	}
@@ -165,7 +164,6 @@ struct dir_info *mark_and_return_parent(struct fsck_sb *sbp,
 	find_di(sbp, di->dotdot_parent, &pdi);
 
 	return pdi;
-
 }
 
 /**
@@ -250,6 +248,7 @@ int pass3(struct fsck_sb *sbp, struct options *opts)
 				if(query(sbp, "Add unlinked directory to l+f? (y/n) ")) {
 					if(add_inode_to_lf(ip)) {
 						stack;
+						free_inode(&ip);
 						return -1;
 					}
 					log_warn("Directory relinked to l+f\n");
diff --git a/gfs/gfs_fsck/pass4.c b/gfs/gfs_fsck/pass4.c
index bb9d61d..4455fc6 100644
--- a/gfs/gfs_fsck/pass4.c
+++ b/gfs/gfs_fsck/pass4.c
@@ -6,10 +6,19 @@
 #include "inode_hash.h"
 #include "inode.h"
 #include "lost_n_found.h"
+#include "metawalk.h"
+
+struct metawalk_fxns pass4_fxns_delete = {
+	.private = NULL,
+	.check_metalist = delete_metadata,
+	.check_data = delete_data,
+	.check_eattr_indir = delete_eattr_indir,
+	.check_eattr_leaf = delete_eattr_leaf,
+};
 
 /* Updates the link count of an inode to what the fsck has seen for
  * link count */
-int fix_inode_count(struct fsck_sb *sbp, struct inode_info *ii,
+static int fix_inode_count(struct fsck_sb *sbp, struct inode_info *ii,
 		    struct fsck_inode *ip)
 {
 	log_info("Fixing inode count for %"PRIu64"\n",
@@ -26,7 +35,7 @@ int fix_inode_count(struct fsck_sb *sbp, struct inode_info *ii,
 	return 0;
 }
 
-int scan_inode_list(struct fsck_sb *sbp, osi_list_t *list) {
+static int scan_inode_list(struct fsck_sb *sbp, osi_list_t *list) {
 	osi_list_t *tmp;
 	struct inode_info *ii;
 	struct fsck_inode *ip;
@@ -61,12 +70,18 @@ int scan_inode_list(struct fsck_sb *sbp, osi_list_t *list) {
 				log_err("Unlinked inode contains"
 					"bad blocks\n",
 					ii->inode);
-				if(query(sbp, "Clear unlinked inode with bad blocks? (y/n) ")) {
-					block_set(sbp->bl, ii->inode, block_free);
+				if(query(sbp, "Delete unlinked inode with bad "
+					 "blocks? (y/n) ")) {
+					load_inode(sbp, ii->inode, &ip);
+					check_inode_eattr(ip,
+							  &pass4_fxns_delete);
+					check_metatree(ip, &pass4_fxns_delete);
+					free_inode(&ip);
+					block_set(sbp->bl, ii->inode,
+						  block_free);
 					continue;
-				} else {
+				} else
 					log_err("Unlinked inode with bad blocks not cleared\n");
-				}
 			}
 			if(q.block_type != inode_dir &&
 			   q.block_type != inode_file &&
@@ -75,9 +90,23 @@ int scan_inode_list(struct fsck_sb *sbp, osi_list_t *list) {
 			   q.block_type != inode_chr &&
 			   q.block_type != inode_fifo &&
 			   q.block_type != inode_sock) {
-				log_err("Unlinked block marked as inode not an inode\n");
-				block_set(sbp->bl, ii->inode, block_free);
-				log_err("Cleared\n");
+				log_err("Unlinked block marked as an inode is "
+					"not an inode (%d)\n", q.block_type);
+				if(query(sbp, "Delete unlinked inode"
+					 "? (y/n) ")) {
+					if (!load_inode(sbp, ii->inode, &ip)) {
+						check_inode_eattr(ip,
+							   &pass4_fxns_delete);
+						check_metatree(ip,
+							   &pass4_fxns_delete);
+					}
+					block_set(sbp->bl, ii->inode,
+						  block_free);
+					free_inode(&ip);
+					log_err("The inode was deleted\n");
+				} else
+					log_err("The inode was not deleted\n");
+
 				continue;
 			}
 			if(load_inode(sbp, ii->inode, &ip)) {
@@ -106,9 +135,8 @@ int scan_inode_list(struct fsck_sb *sbp, osi_list_t *list) {
 					fix_inode_count(sbp, ii, ip);
 					lf_addition = 1;
 				}
-			} else {
+			} else
 				log_err("Unlinked inode left unlinked\n");
-			}
 			free_inode(&ip);
 		}
 		else if(ii->link_count != ii->counted_links) {
@@ -143,7 +171,6 @@ int scan_inode_list(struct fsck_sb *sbp, osi_list_t *list) {
 		}
 	}
 
-
 	return 0;
 }
 
diff --git a/gfs/gfs_fsck/pass5.c b/gfs/gfs_fsck/pass5.c
index 86c9cc3..b40ab59 100644
--- a/gfs/gfs_fsck/pass5.c
+++ b/gfs/gfs_fsck/pass5.c
@@ -119,7 +119,7 @@ int count_bmaps(struct fsck_rgrp *rgp)
 }
 #endif /* DEBUG */
 
-int convert_mark(enum mark_block mark, uint32_t *count)
+static int convert_mark(enum mark_block mark, uint32_t *count)
 {
 	switch(mark) {
 
@@ -162,8 +162,19 @@ int convert_mark(enum mark_block mark, uint32_t *count)
 	return -1;
 }
 
+static const char *block_type_string(struct block_query *q)
+{
+	const char *blktyp[] = {"free", "used", "indirect data", "directory",
+				"file", "symlink", "block dev", "char dev",
+				"fifo", "socket", "dir leaf", "journ data",
+				"other meta", "free meta", "meta eattr",
+				"bad blk", "dup block", "eattr", "invalid"};
+	if (q->block_type < 18)
+		return (blktyp[q->block_type]);
+	return blktyp[18];
+}
 
-int check_block_status(struct fsck_sb *sbp, char *buffer, unsigned int buflen,
+static int check_block_status(struct fsck_sb *sbp, char *buffer, unsigned int buflen,
 		       uint64_t *rg_block, uint64_t rg_data, uint32_t *count)
 {
 	unsigned char *byte, *end;
@@ -179,7 +190,6 @@ int check_block_status(struct fsck_sb *sbp, char *buffer, unsigned int buflen,
 	while(byte < end) {
 		rg_status = ((*byte >> bit) & GFS_BIT_MASK);
 		block = rg_data + *rg_block;
-		log_debug("Checking block %" PRIu64 "\n", block);
 		warm_fuzzy_stuff(block);
 		if (skip_this_pass || fsck_abort) /* if asked to skip the rest */
 			return 0;
@@ -196,7 +206,11 @@ int check_block_status(struct fsck_sb *sbp, char *buffer, unsigned int buflen,
 					 PRIu64" to a free data block\n", block);
 				if(!sbp->opts->no) {
 					if(fs_set_bitmap(sbp, block, block_status)) {
-						log_warn("Failed to convert free metadata block to free data block at %PRIu64.\n", block);
+						log_warn("Failed to convert "
+							 "free metadata block "
+							 "to free data block "
+							 "at %"PRIu64".\n",
+							 block);
 					}
 					else {
 						log_info("Succeeded.\n");
@@ -204,22 +218,30 @@ int check_block_status(struct fsck_sb *sbp, char *buffer, unsigned int buflen,
 				}
 			}
 			else {
+				const char *blockstatus[] = {"Free", "Data",
+							     "Free Meta",
+							     "Metadata"};
 
 				log_err("ondisk and fsck bitmaps differ at"
 					" block %"PRIu64"\n", block);
+				log_err("Ondisk status is %u (%s) but FSCK "
+					"thinks it should be ",
+					rg_status, blockstatus[rg_status]);
+				log_err("%u (%s)\n", block_status,
+					blockstatus[block_status]);
+				log_err("Metadata type is %u (%s)\n",
+					q.block_type,
+					block_type_string(&q));
 
 				if(query(sbp, "Fix bitmap for block %"
 					 PRIu64"? (y/n) ", block)) {
-					if(fs_set_bitmap(sbp, block, block_status)) {
+					if(fs_set_bitmap(sbp, block, block_status))
 						log_err("Failed.\n");
-					}
-					else {
+					else
 						log_err("Succeeded.\n");
-					}
-				} else {
+				} else
 					log_err("Bitmap at block %"PRIu64
 						" left inconsistent\n", block);
-				}
 			}
 		}
 		(*rg_block)++;
@@ -240,7 +262,7 @@ int check_block_status(struct fsck_sb *sbp, char *buffer, unsigned int buflen,
 #define FREE_META_COUNT  16
 #define CONVERT_FREEMETA_TO_FREE (FREE_COUNT | FREE_META_COUNT)
 
-int update_rgrp(struct fsck_rgrp *rgp, uint32_t *count, int rgcount)
+static int update_rgrp(struct fsck_rgrp *rgp, uint32_t *count, int rgcount)
 {
 	uint32_t i;
 	fs_bitmap_t *bits;
diff --git a/gfs/gfs_fsck/rgrp.c b/gfs/gfs_fsck/rgrp.c
index dcbb2cc..89a7c39 100644
--- a/gfs/gfs_fsck/rgrp.c
+++ b/gfs/gfs_fsck/rgrp.c
@@ -155,7 +155,6 @@ int fs_rgrp_read(struct fsck_rgrp *rgd, int repair_if_corrupted)
 	int error;
 
 	if(rgd->rd_open_count){
-		log_debug("rgrp already read...\n");
 		rgd->rd_open_count++;
 		return 0;
 	}
@@ -236,7 +235,7 @@ void fs_rgrp_relse(struct fsck_rgrp *rgd)
 
 	rgd->rd_open_count--;
 	if(rgd->rd_open_count){
-		log_debug("rgrp still held...\n");
+		;
 	} else {
 		for (x = 0; x < length; x++){
 			if (rgd->rd_bh[x]) {
diff --git a/gfs/gfs_fsck/super.c b/gfs/gfs_fsck/super.c
index 507cf3f..f2ada28 100644
--- a/gfs/gfs_fsck/super.c
+++ b/gfs/gfs_fsck/super.c
@@ -1142,7 +1142,7 @@ int ri_update(struct fsck_sb *sdp)
 	osi_list_t expected_rglist; /* List of expected resource groups */
 	osi_list_t *tmp;
 	struct gfs_rindex buf;
-	unsigned int rg, calc_rg_count;
+	unsigned int rg, calc_rg_count = 0;
 	int error, count1 = 0, count2 = 0;
 	int fix_grow_problems = 0, grow_problems = 0;
 	enum rgindex_trust_level { /* how far can we trust our RG index? */


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2009-08-10 19:21 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-08-10 19:21 cluster: STABLE2 - GFS: gfs_fsck sometimes needs to be run twice Bob Peterson

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).