public inbox for cluster-cvs@sourceware.org
help / color / mirror / Atom feed
From: Bob Peterson <rpeterso@fedoraproject.org>
To: cluster-cvs-relay@redhat.com
Subject: cluster: RHEL5 - GFS2: fsck.gfs2 sometimes needs to be run twice
Date: Mon, 10 Aug 2009 16:54:00 -0000	[thread overview]
Message-ID: <20090810165256.746DC1201F3@lists.fedorahosted.org> (raw)

Gitweb:        http://git.fedorahosted.org/git/cluster.git?p=cluster.git;a=commitdiff;h=726f9de7c9483aa0b797f1c2482cea37d73ed0cc
Commit:        726f9de7c9483aa0b797f1c2482cea37d73ed0cc
Parent:        8bae5c5c8121dea9bd80382851020d17be3d8552
Author:        Bob Peterson <rpeterso@redhat.com>
AuthorDate:    Mon Aug 10 11:47:02 2009 -0500
Committer:     Bob Peterson <rpeterso@redhat.com>
CommitterDate: Mon Aug 10 11:52:24 2009 -0500

GFS2: fsck.gfs2 sometimes needs to be run twice

bz 500483

This patch fixes numerous bugs whereby fsck.gfs2 was not "following
through" with its changes, and therefore a second run was often
needed to completely clean things up.
---
 gfs2/edit/hexedit.c       |   63 ------
 gfs2/fsck/main.c          |    6 +-
 gfs2/fsck/metawalk.c      |  471 ++++++++++++++++++++++++++++++++-----------
 gfs2/fsck/metawalk.h      |   15 ++-
 gfs2/fsck/pass1.c         |  493 ++++++++++++++++++++++++++++-----------------
 gfs2/fsck/pass1b.c        |  127 ++++++++----
 gfs2/fsck/pass1c.c        |  191 ++++++++++--------
 gfs2/fsck/pass2.c         |  238 ++++++++++++++--------
 gfs2/fsck/pass3.c         |    3 +-
 gfs2/fsck/pass4.c         |   43 ++++-
 gfs2/fsck/pass5.c         |   51 ++++-
 gfs2/fsck/rgrepair.c      |    2 +-
 gfs2/libgfs2/block_list.c |   28 ++-
 gfs2/libgfs2/fs_bits.c    |   61 ++++++
 gfs2/libgfs2/fs_ops.c     |    2 +
 gfs2/libgfs2/libgfs2.h    |    8 +-
 16 files changed, 1196 insertions(+), 606 deletions(-)

diff --git a/gfs2/edit/hexedit.c b/gfs2/edit/hexedit.c
index 5937663..3f932da 100644
--- a/gfs2/edit/hexedit.c
+++ b/gfs2/edit/hexedit.c
@@ -438,8 +438,6 @@ void print_usage(void)
 	Erase();
 }
 
-
-
 /* ------------------------------------------------------------------------ */
 /* get_block_type                                                           */
 /* returns: metatype if block is a GFS2 structure block type                */
@@ -457,67 +455,6 @@ int get_block_type(const char *lpBuffer)
 	return ret_type;
 }
 
-/*
- * fs_get_bitmap - get value of FS bitmap
- * @sdp: super block
- * @blkno: block number relative to file system
- *
- * This function gets the value of a bit of the
- * file system bitmap.
- * Possible state values for a block in the bitmap are:
- *  GFS_BLKST_FREE     (0)
- *  GFS_BLKST_USED     (1)
- *  GFS_BLKST_INVALID  (2)
- *  GFS_BLKST_DINODE   (3)
- *
- * Returns: state on success, -1 on error
- */
-int gfs2_get_bitmap(struct gfs2_sbd *sdp, uint64_t blkno,
-					struct rgrp_list *rgd)
-{
-	int           buf, val;
-	uint32_t        rgrp_block;
-	struct gfs2_bitmap	*bits = NULL;
-	unsigned int  bit;
-	unsigned char *byte;
-	int local_rgd = 0;
-
-	if(gfs2_check_range(sdp, blkno))
-		return -1;
-	if(rgd == NULL) {
-		local_rgd = 1;
-		rgd = gfs2_blk2rgrpd(sdp, blkno);
-	}
-	if(rgd == NULL)
-		return -1;
-	if(gfs2_rgrp_read(sdp, rgd))
-		return -1;
-
-	rgrp_block = (uint32_t)(blkno - rgd->ri.ri_data0);
-
-	for(buf= 0; buf < rgd->ri.ri_length; buf++){
-		bits = &(rgd->bits[buf]);
-		if(rgrp_block < ((bits->bi_start + bits->bi_len)*GFS2_NBBY)){
-			break;
-		}
-	}
-
-	if(buf >= rgd->ri.ri_length){
-		gfs2_rgrp_relse(rgd, not_updated);
-		return -1;
-	}
-
-	byte = (unsigned char *)(rgd->bh[buf]->b_data + bits->bi_offset) +
-		(rgrp_block/GFS2_NBBY - bits->bi_start);
-	bit = (rgrp_block % GFS2_NBBY) * GFS2_BIT_SIZE;
-
-	val = ((*byte >> bit) & GFS2_BIT_MASK);
-	if(local_rgd)
-		gfs2_rgrp_relse(rgd, not_updated);
-
-	return val;
-}
-
 /* ------------------------------------------------------------------------ */
 /* display_block_type                                                       */
 /* returns: metatype if block is a GFS2 structure block type                */
diff --git a/gfs2/fsck/main.c b/gfs2/fsck/main.c
index 8c85c8d..0b76790 100644
--- a/gfs2/fsck/main.c
+++ b/gfs2/fsck/main.c
@@ -199,9 +199,9 @@ int check_system_inode(struct gfs2_inode *sysinode, const char *filename,
 	if(!sysinode || ds.q.block_type != mark) {
 		log_err("Invalid or missing %s system inode.\n", filename);
 		errors_found++;
-		if ((errors_corrected +=
-		    query(&opts, "Create new %s system inode? (y/n) ",
-			  filename))) {
+		if (query(&opts, "Create new %s system inode? (y/n) ",
+			  filename)) {
+			errors_corrected++;
 			builder(sysinode->i_sbd);
 			gfs2_block_set(sysinode->i_sbd, bl,
 				       sysinode->i_di.di_num.no_addr,
diff --git a/gfs2/fsck/metawalk.c b/gfs2/fsck/metawalk.c
index 0730835..be62cfa 100644
--- a/gfs2/fsck/metawalk.c
+++ b/gfs2/fsck/metawalk.c
@@ -103,6 +103,17 @@ void fsck_inode_put(struct gfs2_inode *ip, enum update_flags update)
 	}
 }
 
+/**
+ * dirent_repair - attempt to repair a corrupt directory entry.
+ * @bh - The buffer header that contains the bad dirent
+ * @de - The directory entry in native format
+ * @dent - The directory entry in on-disk format
+ * @type - Type of directory (DIR_LINEAR or DIR_EXHASH)
+ * @first - TRUE if this is the first dirent in the buffer
+ *
+ * This function tries to repair a corrupt directory entry.  All we
+ * know at this point is that the length field is wrong.
+ */
 int dirent_repair(struct gfs2_inode *ip, struct gfs2_buffer_head *bh,
 		  struct gfs2_dirent *de, struct gfs2_dirent *dent,
 		  int type, int first)
@@ -118,8 +129,7 @@ int dirent_repair(struct gfs2_inode *ip, struct gfs2_buffer_head *bh,
 		else
 			de->de_rec_len = ip->i_sbd->bsize -
 				sizeof(struct gfs2_leaf);
-	}
-	else {
+	} else {
 		bh_end = bh->b_data + ip->i_sbd->bsize;
 		/* first, figure out a probable name length */
 		p = (char *)dent + sizeof(struct gfs2_dirent);
@@ -142,8 +152,40 @@ int dirent_repair(struct gfs2_inode *ip, struct gfs2_buffer_head *bh,
 	return 0;
 }
 
+/**
+ * dirblk_truncate - truncate a directory block
+ */
+static void dirblk_truncate(struct gfs2_inode *ip, struct gfs2_dirent *fixb,
+			    struct gfs2_buffer_head *bh)
+{
+	char *bh_end;
+	struct gfs2_dirent de;
+	uint16_t old_rec_len;
+
+	bh_end = bh->b_data + ip->i_sbd->sd_sb.sb_bsize;
+	/* truncate the block to save the most dentries.  To do this we
+	   have to patch the previous dent. */
+	gfs2_dirent_in(&de, (char *)fixb);
+	old_rec_len = de.de_rec_len;
+	de.de_rec_len = bh_end - (char *)fixb;
+	gfs2_dirent_out(&de, (char *)fixb);
+}
+
+/*
+ * 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
+ */
 int check_entries(struct gfs2_inode *ip, struct gfs2_buffer_head *bh,
-		  int index, int type, enum update_flags *update,
+		  int type, enum update_flags *update,
 		  uint16_t *count, struct metawalk_fxns *pass)
 {
 	struct gfs2_leaf *leaf = NULL;
@@ -175,12 +217,15 @@ int check_entries(struct gfs2_inode *ip, struct gfs2_buffer_head *bh,
 		return 0;
 
 	while(1) {
+		if (skip_this_pass || fsck_abort)
+			return FSCK_OK;
 		memset(&de, 0, sizeof(struct gfs2_dirent));
 		gfs2_dirent_in(&de, (char *)dent);
 		filename = (char *)dent + sizeof(struct gfs2_dirent);
 
 		if (de.de_rec_len < sizeof(struct gfs2_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 "(0x%"
 				PRIx64 "), entry %d of directory %"
 				PRIu64 "(0x%" PRIx64 ") is corrupt.\n",
@@ -190,18 +235,28 @@ int check_entries(struct gfs2_inode *ip, struct gfs2_buffer_head *bh,
 			errors_found++;
 			if (query(&opts, "Attempt to repair it? (y/n) ")) {
 				if (dirent_repair(ip, bh, &de, dent, type,
-						  first))
-					break;
-				else {
+						  first)) {
+					if (first) /* make a new sentinel */
+						dirblk_truncate(ip, dent, bh);
+					else
+						dirblk_truncate(ip, prev, bh);
+					*update = updated;
+					log_err("Unable to repair corrupt "
+						"directory entry; the entry "
+						"was removed instead.\n");
+					return 0;
+				} else {
+					log_err("Corrupt directory entry "
+						"repaired.\n");
 					errors_corrected++;
 					*update = updated;
+					/* keep looping through dentries */
 				}
-			}
-			else {
+			} else {
 				log_err("Corrupt directory entry ignored, "
 					"stopped after checking %d entries.\n",
 					*count);
-				break;
+				return 0;
 			}
 		}
 		if (!de.de_inum.no_formal_ino){
@@ -209,12 +264,26 @@ int check_entries(struct gfs2_inode *ip, struct gfs2_buffer_head *bh,
 				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 "(0x%" PRIx64 ") of directory %" PRIu64
-						" (0x%" PRIx64 ")!\n", bh->b_blocknr, bh->b_blocknr,
-						ip->i_di.di_num.no_addr, ip->i_di.di_num.no_addr);
-				return 1;
+				log_err("Directory entry with inode number of "
+					"zero in leaf %" PRIu64 "(0x%" PRIx64
+					") of directory %" PRIu64 " (0x%"
+					PRIx64 ")!\n", bh->b_blocknr,
+					bh->b_blocknr,
+					ip->i_di.di_num.no_addr,
+					ip->i_di.di_num.no_addr);
+				if (query(&opts,
+					  "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 {
 			if (!de.de_inum.no_addr && first) { /* reverse sentinel */
@@ -236,9 +305,6 @@ int check_entries(struct gfs2_inode *ip, struct gfs2_buffer_head *bh,
 					stack;
 					return -1;
 				}
-				/*if(error > 0) {
-				  return 1;
-				  }*/
 			}
 		}
 
@@ -263,12 +329,12 @@ int check_entries(struct gfs2_inode *ip, struct gfs2_buffer_head *bh,
 /* leaf a bit, but it's better than deleting the whole directory,    */
 /* which is what used to happen before.                              */
 void warn_and_patch(struct gfs2_inode *ip, uint64_t *leaf_no, 
-		    uint64_t *bad_leaf, uint64_t old_leaf, int index,
-		    const char *msg)
+		    uint64_t *bad_leaf, uint64_t old_leaf,
+		    uint64_t first_ok_leaf, int index, const char *msg)
 {
 	if (*bad_leaf != *leaf_no) {
 		log_err("Directory Inode %" PRIu64 "(0x%"
-			PRIx64 ") points to leaf %" PRIu64 "(0x%"
+			PRIx64 ") points to leaf %" PRIu64 " (0x%"
 			PRIx64 ") %s.\n", ip->i_di.di_num.no_addr,
 			ip->i_di.di_num.no_addr, *leaf_no, *leaf_no, msg);
 	}
@@ -276,7 +342,13 @@ void warn_and_patch(struct gfs2_inode *ip, uint64_t *leaf_no,
 	if (*leaf_no == *bad_leaf ||
 	    query(&opts, "Attempt to patch around it? (y/n) ")) {
 		errors_corrected++;
-		gfs2_put_leaf_nr(ip, index, old_leaf);
+		if (gfs2_check_range(ip->i_sbd, old_leaf) == 0)
+			gfs2_put_leaf_nr(ip, index, old_leaf);
+		else
+			gfs2_put_leaf_nr(ip, index, first_ok_leaf);
+		log_err("Directory Inode %" PRIu64 "(0x%" PRIx64
+			") repaired.\n", ip->i_di.di_num.no_addr,
+			ip->i_di.di_num.no_addr);
 	}
 	else
 		log_err("Bad leaf left in place.\n");
@@ -285,21 +357,42 @@ void warn_and_patch(struct gfs2_inode *ip, uint64_t *leaf_no,
 }
 
 /* Checks exhash directory entries */
-int check_leaf(struct gfs2_inode *ip, enum update_flags *update,
-	       struct metawalk_fxns *pass)
+int check_leaf_blks(struct gfs2_inode *ip, enum update_flags *update,
+		    struct metawalk_fxns *pass)
 {
 	int error;
 	struct gfs2_leaf leaf, oldleaf;
 	uint64_t leaf_no, old_leaf, bad_leaf = -1;
+	uint64_t first_leaf_ptr = -1, first_ok_leaf = -1;
 	struct gfs2_buffer_head *lbh;
 	int index;
 	struct gfs2_sbd *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(index = 0; index < (1 << ip->i_di.di_depth); index++) {
+		gfs2_get_leaf_nr(ip, index, &first_ok_leaf);
+		if (first_leaf_ptr == -1)
+			first_leaf_ptr = first_ok_leaf;
+		if(gfs2_check_range(ip->i_sbd, first_ok_leaf) == 0) {
+			lbh = bread(&sbp->buf_list, first_ok_leaf);
+			/* Make sure it's really a valid leaf block. */
+			if (gfs2_check_meta(lbh, GFS2_METATYPE_LF) == 0) {
+				brelse(lbh, not_updated);
+				break;
+			}
+			brelse(lbh, not_updated);
+		}
+	}
+	old_leaf = -1;
 	memset(&oldleaf, 0, sizeof(oldleaf));
 	for(index = 0; index < (1 << ip->i_di.di_depth); index++) {
+		if (fsck_abort)
+			break;
 		gfs2_get_leaf_nr(ip, index, &leaf_no);
 
 		/* GFS has multiple indirect pointers to the same leaf
@@ -314,41 +407,39 @@ int check_leaf(struct gfs2_inode *ip, enum update_flags *update,
 		else if(old_leaf == leaf_no) {
 			ref_count++;
 			continue;
-		} else {
-			if(ref_count != exp_count){
-				log_err("Dir #%" PRIu64 " (0x%"
-					PRIx64 ") has an incorrect "
-					"number of pointers to leaf #%"
-					PRIu64 " (0x%" PRIx64
-					")\n\tFound: %u,  Expected: %u\n",
-					ip->i_di.di_num.no_addr,
-					ip->i_di.di_num.no_addr,
-					old_leaf, old_leaf, ref_count,
-					exp_count);
-				errors_found++;
-				if (query(&opts, "Attempt to fix it? (y/n) "))
-				{
-					int factor = 0, divisor = ref_count;
-
-					errors_corrected++;
-					lbh = bread(&sbp->buf_list, old_leaf);
-					while (divisor > 1) {
-						factor++;
-						divisor /= 2;
-					}
-					oldleaf.lf_depth = ip->i_di.di_depth -
-						factor;
-					gfs2_leaf_out(&oldleaf, lbh->b_data);
-					brelse(lbh, updated);
+		}
+		if (gfs2_check_range(ip->i_sbd, old_leaf) == 0 &&
+		    ref_count != exp_count) {
+			log_err("Dir #%" PRIu64 " (0x%" PRIx64 ") has an "
+				"incorrect number of pointers to leaf #%"
+				PRIu64 " (0x%" PRIx64 ")\n\tFound: %u,  "
+				"Expected: %u\n", ip->i_di.di_num.no_addr,
+				ip->i_di.di_num.no_addr, old_leaf, old_leaf,
+				ref_count, exp_count);
+			errors_found++;
+			if (query(&opts, "Attempt to fix it? (y/n) ")) {
+				int factor = 0, divisor = ref_count;
+
+				errors_corrected++;
+				lbh = bread(&sbp->buf_list, old_leaf);
+				while (divisor > 1) {
+					factor++;
+					divisor /= 2;
 				}
-				else
-					return 1;
+				gfs2_leaf_in(&oldleaf, lbh->b_data);
+				oldleaf.lf_depth = ip->i_di.di_depth - factor;
+				gfs2_leaf_out(&oldleaf, lbh->b_data);
+				brelse(lbh, updated);
 			}
-			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(gfs2_check_range(ip->i_sbd, leaf_no)){
 				log_err("Leaf block #%" PRIu64 " (0x%"
@@ -358,8 +449,8 @@ int check_leaf(struct gfs2_inode *ip, enum update_flags *update,
 					ip->i_di.di_num.no_addr,
 					ip->i_di.di_num.no_addr);
 				warn_and_patch(ip, &leaf_no, &bad_leaf,
-					       old_leaf, index,
-					       "that is out of range");
+					       old_leaf, first_ok_leaf,
+					       index, "that is out of range");
 				memcpy(&leaf, &oldleaf, sizeof(oldleaf));
 				break;
 			}
@@ -370,10 +461,10 @@ int check_leaf(struct gfs2_inode *ip, enum update_flags *update,
 			/* Make sure it's really a valid leaf block. */
 			if (gfs2_check_meta(lbh, GFS2_METATYPE_LF)) {
 				warn_and_patch(ip, &leaf_no, &bad_leaf,
-					       old_leaf, index,
+					       old_leaf, first_ok_leaf, index,
 					       "that is not really a leaf");
 				memcpy(&leaf, &oldleaf, sizeof(oldleaf));
-				brelse(lbh, (opts.no ? not_updated : updated));
+				brelse(lbh, not_updated);
 				break;
 			}
 			gfs2_leaf_in(&leaf, lbh->b_data);
@@ -402,7 +493,9 @@ int check_leaf(struct gfs2_inode *ip, enum update_flags *update,
 				log_err("Inode %" PRIu64 " (0x%"
 					PRIx64 ") points to bad leaf "
 					PRIu64 " (0x%" PRIx64 ").\n",
-					ip->i_di.di_num.no_addr, leaf_no);
+					ip->i_di.di_num.no_addr,
+					ip->i_di.di_num.no_addr,
+					leaf_no, leaf_no);
 				brelse(lbh, *update);
 				break;
 			}
@@ -412,9 +505,8 @@ int check_leaf(struct gfs2_inode *ip, enum update_flags *update,
 
 			if(pass->check_dentry &&
 			   S_ISDIR(ip->i_di.di_mode)) {
-				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,
@@ -427,20 +519,24 @@ int check_leaf(struct gfs2_inode *ip, enum update_flags *update,
 					return -1;
 				}
 
-				if(error > 0)
-					return 1;
-
 				if(update && (count != leaf.lf_entries)) {
 					enum update_flags f = not_updated;
 
 					lbh = bread(&sbp->buf_list, leaf_no);
 					gfs2_leaf_in(&leaf, lbh->b_data);
 
-					log_err("Leaf %"PRIu64" (0x%" PRIx64
-							") entry count in directory %" PRIu64
-							" doesn't match number of entries found - is %u, found %u\n",
-							leaf_no, leaf_no, ip->i_di.di_num.no_addr,
-							leaf.lf_entries, count);
+					log_err( "Leaf %llu (0x%llx) entry "
+						"count in directory %llu"
+						" (0x%llx) doesn't match "
+						"number of entries found "
+						"- is %u, found %u\n",
+						(unsigned long long)leaf_no,
+						(unsigned long long)leaf_no,
+						(unsigned long long)
+						ip->i_di.di_num.no_addr,
+						(unsigned long long)
+						ip->i_di.di_num.no_addr,
+						leaf.lf_entries, count);
 					errors_found++;
 					if(query(&opts, "Update leaf entry count? (y/n) ")) {
 						errors_corrected++;
@@ -454,7 +550,7 @@ int check_leaf(struct gfs2_inode *ip, enum update_flags *update,
 				}
 				/* FIXME: Need to get entry count and
 				 * compare it against leaf->lf_entries */
-				break;
+				break; /* not a chain; go back to outer loop */
 			} else {
 				brelse(lbh, *update);
 				if(!leaf.lf_next)
@@ -462,10 +558,10 @@ int check_leaf(struct gfs2_inode *ip, enum update_flags *update,
 				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;
 }
 
@@ -489,12 +585,18 @@ static int check_eattr_entries(struct gfs2_inode *ip,
 					  sizeof(struct gfs2_meta_header));
 
 	while(1){
-		error = pass->check_eattr_entry(ip, bh, ea_hdr, ea_hdr_prev,
-						pass->private);
+		if (ea_hdr->ea_type == GFS2_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;
 		}
+		if (error > 0)
+			*update_it = updated;
 		if(error == 0 && pass->check_eattr_extentry &&
 		   ea_hdr->ea_num_ptrs) {
 			uint32_t tot_ealen = 0;
@@ -519,7 +621,8 @@ static int check_eattr_entries(struct gfs2_inode *ip,
 							      update_it,
 							      pass->private)) {
 					errors_found++;
-					if (query(&opts, "Repair the bad EA? "
+					if (query(&opts, "Repair the bad "
+						  "Extended Attribute? "
 						  "(y/n) ")) {
 						errors_corrected++;
 						ea_hdr->ea_num_ptrs = i;
@@ -530,9 +633,15 @@ static int check_eattr_entries(struct gfs2_inode *ip,
 						/* Endianness doesn't matter
 						   in this case because it's
 						   a single byte. */
-						return -1;
+						gfs2_block_set(sdp, bl,
+							     ip->i_di.di_eattr,
+							     gfs2_meta_eattr);
+						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->sd_sb.sb_bsize -
 					sizeof(struct gfs2_meta_header);
@@ -541,7 +650,9 @@ static int check_eattr_entries(struct gfs2_inode *ip,
 		}
 		offset += be32_to_cpu(ea_hdr->ea_rec_len);
 		if(ea_hdr->ea_flags & GFS2_EAFLAG_LAST ||
-		   offset >= ip->i_sbd->sd_sb.sb_bsize || ea_hdr->ea_rec_len == 0){
+		   offset >= ip->i_sbd->sd_sb.sb_bsize ||
+		   ea_hdr->ea_rec_len == 0) {
+			
 			break;
 		}
 		ea_hdr_prev = ea_hdr;
@@ -550,7 +661,7 @@ static int check_eattr_entries(struct gfs2_inode *ip,
 			 be32_to_cpu(ea_hdr->ea_rec_len));
 	}
 
-	return 0;
+	return error;
 }
 
 /**
@@ -558,7 +669,7 @@ static int check_eattr_entries(struct gfs2_inode *ip,
  * @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 gfs2_inode *ip, uint64_t block,
 			    uint64_t parent, enum update_flags *want_updated,
@@ -566,23 +677,33 @@ static int check_leaf_eattr(struct gfs2_inode *ip, uint64_t block,
 {
 	struct gfs2_buffer_head *bh = NULL;
 	int error = 0;
+	enum update_flags updated_this_leaf = not_updated;
 
 	log_debug("Checking EA leaf block #%"PRIu64" (0x%" PRIx64 ").\n",
 			  block, block);
 
 	if(pass->check_eattr_leaf) {
 		error = pass->check_eattr_leaf(ip, block, parent, &bh,
-					       want_updated, pass->private);
+					       &updated_this_leaf,
+					       pass->private);
+		if (updated_this_leaf) /* if this leaf was updated */
+			*want_updated = updated; /* signal it for the parent */
 		if(error < 0) {
 			stack;
 			return -1;
 		}
 		if(error > 0) {
+			if (bh)
+				brelse(bh, updated_this_leaf);
 			return 1;
 		}
 		if (bh) {
-			error = check_eattr_entries(ip, bh, pass, want_updated);
-			brelse(bh, *want_updated);
+			error = check_eattr_entries(ip, bh, pass,
+						    &updated_this_leaf);
+			brelse(bh, updated_this_leaf);
+			if (updated_this_leaf) /* if this leaf was updated */
+				*want_updated = updated; /* signal it for
+							    the parent */
 		}
 		return error;
 	}
@@ -599,12 +720,16 @@ static int check_leaf_eattr(struct gfs2_inode *ip, uint64_t block,
  */
 static int check_indirect_eattr(struct gfs2_inode *ip, uint64_t indirect,
 				enum update_flags *want_updated,
-				struct metawalk_fxns *pass){
+				struct metawalk_fxns *pass)
+{
 	int error = 0;
 	uint64_t *ea_leaf_ptr, *end;
 	uint64_t block;
 	struct gfs2_buffer_head *indirect_buf = NULL;
 	struct gfs2_sbd *sdp = ip->i_sbd;
+	enum update_flags update_indir_block = not_updated;
+	int first_ea_is_bad = 0;
+	uint64_t di_eattr_save = ip->i_di.di_eattr;
 
 	*want_updated = not_updated;
 	log_debug("Checking EA indirect block #%"PRIu64" (0x%" PRIx64 ").\n",
@@ -619,32 +744,72 @@ static int check_indirect_eattr(struct gfs2_inode *ip, uint64_t indirect,
 		int leaf_pointers = 0, leaf_pointer_errors = 0;
 
 		ea_leaf_ptr = (uint64_t *)(indirect_buf->b_data
-								   + sizeof(struct gfs2_meta_header));
+					   + sizeof(struct gfs2_meta_header));
 		end = ea_leaf_ptr + ((sdp->sd_sb.sb_bsize
-							  - sizeof(struct gfs2_meta_header)) / 8);
+				      - sizeof(struct gfs2_meta_header)) / 8);
 
 		while(*ea_leaf_ptr && (ea_leaf_ptr < end)){
 			block = be64_to_cpu(*ea_leaf_ptr);
 			leaf_pointers++;
 			error = check_leaf_eattr(ip, block, indirect,
 						 want_updated, pass);
-			if (error)
+			if (error) {
 				leaf_pointer_errors++;
+				if (update_indir_block == not_updated) {
+					errors_found++;
+					if (query(&opts, "Fix the indirect "
+						  "block too? (y/n) ")) {
+						update_indir_block = updated;
+						errors_corrected++;
+						*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,
+							want_updated,
+							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, want_updated,
-						 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,
+							 want_updated,
+							 pass->private);
+			}
+			if (leaf_pointer_errors == leaf_pointers) {
 				if (*want_updated)
 					gfs2_set_bitmap(sdp, indirect,
 							GFS2_BLKST_FREE);
-				gfs2_block_clear(sdp, bl, indirect,
-						 gfs2_indir_blk);
 				gfs2_block_set(sdp, bl, indirect,
 					       gfs2_block_free);
 				error = 1;
@@ -652,7 +817,7 @@ static int check_indirect_eattr(struct gfs2_inode *ip, uint64_t indirect,
 		}
 	}
 	if (indirect_buf)
-		brelse(indirect_buf, not_updated);
+		brelse(indirect_buf, update_indir_block);
 
 	return error;
 }
@@ -668,21 +833,22 @@ int check_inode_eattr(struct gfs2_inode *ip, enum update_flags *want_updated,
 {
 	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 " (0x%" PRIx64
-		  ").\n", ip->i_di.di_num.no_addr, ip->i_di.di_num.no_addr);
+	log_debug("Extended attributes exist for inode #%llu (0x%llx).\n",
+		  (unsigned long long)ip->i_di.di_num.no_addr,
+		  (unsigned long long)ip->i_di.di_num.no_addr);
 
 	if(ip->i_di.di_flags & GFS2_DIF_EA_INDIRECT){
 		if((error = check_indirect_eattr(ip, ip->i_di.di_eattr,
 						 want_updated, pass)))
 			stack;
 	} else {
-		if((error = check_leaf_eattr(ip, ip->i_di.di_eattr,
-					     ip->i_di.di_num.no_addr,
-					     want_updated, pass)))
+		error = check_leaf_eattr(ip, ip->i_di.di_eattr,
+					 ip->i_di.di_num.no_addr,
+					 want_updated, pass);
+		if (error)
 			stack;
 	}
 
@@ -721,15 +887,23 @@ static int build_and_check_metalist(struct gfs2_inode *ip,
 			bh = osi_list_entry(tmp, struct gfs2_buffer_head,
 					    b_altlist);
 
-			head_size = (i > 1 ?
-				     sizeof(struct gfs2_meta_header) :
-				     sizeof(struct gfs2_dinode));
+			if (i > 1) {
+				/* if this isn't really a block list skip it */
+				if (gfs2_check_meta(bh, GFS2_METATYPE_IN))
+					continue;
+				head_size = sizeof(struct gfs2_meta_header);
+			} else {
+				/* if this isn't really a dinode, skip it */
+				if (gfs2_check_meta(bh, GFS2_METATYPE_DI))
+					continue;
+				head_size = sizeof(struct gfs2_dinode);
+			}
 
 			for (ptr = (uint64_t *)(bh->b_data + head_size);
 			     (char *)ptr < (bh->b_data + ip->i_sbd->bsize);
 			     ptr++) {
 				nbh = NULL;
-		
+
 				if (!*ptr)
 					continue;
 
@@ -764,6 +938,7 @@ fail:
 		while (!osi_list_empty(list)) {
 			nbh = osi_list_entry(list->next,
 					     struct gfs2_buffer_head, b_altlist);
+			brelse(nbh, not_updated);
 			osi_list_del(&nbh->b_altlist);
 		}
 	}
@@ -804,8 +979,9 @@ int check_metatree(struct gfs2_inode *ip, struct metawalk_fxns *pass)
 	/* We don't need to record directory blocks - they will be
 	 * recorded later...i think... */
         if (S_ISDIR(ip->i_di.di_mode))
-		log_debug("Directory with height > 0 at %"PRIu64"\n",
-			  ip->i_di.di_num.no_addr);
+		log_debug( "Directory with height > 0 at %llu (0x%llx)\n",
+			  (unsigned long long)ip->i_di.di_num.no_addr,
+			  (unsigned long long)ip->i_di.di_num.no_addr);
 
 	/* check data blocks */
 	list = &metalist[height - 1];
@@ -813,8 +989,17 @@ int check_metatree(struct gfs2_inode *ip, struct metawalk_fxns *pass)
 	for (tmp = list->next; tmp != list; tmp = tmp->next) {
 		bh = osi_list_entry(tmp, struct gfs2_buffer_head, b_altlist);
 
-		head_size = (height != 1 ? sizeof(struct gfs2_meta_header) :
-			     sizeof(struct gfs2_dinode));
+		if (height > 1) {
+			/* if this isn't really a block list skip it */
+			if (gfs2_check_meta(bh, GFS2_METATYPE_IN))
+				continue;
+			head_size = sizeof(struct gfs2_meta_header);
+		} else {
+			/* if this isn't really a dinode, skip it */
+			if (gfs2_check_meta(bh, GFS2_METATYPE_DI))
+				continue;
+			head_size = sizeof(struct gfs2_dinode);
+		}
 		ptr = (uint64_t *)(bh->b_data + head_size);
 
 		for ( ; (char *)ptr < (bh->b_data + ip->i_sbd->bsize); ptr++) {
@@ -848,7 +1033,7 @@ end:
         if (S_ISDIR(ip->i_di.di_mode)) {
 		/* check validity of leaf blocks and leaf chains */
 		if (ip->i_di.di_flags & GFS2_DIF_EXHASH) {
-			error = check_leaf(ip, &update, pass);
+			error = check_leaf_blks(ip, &update, pass);
 			if(error < 0)
 				return -1;
 			if(error > 0)
@@ -866,7 +1051,7 @@ int check_linear_dir(struct gfs2_inode *ip, struct gfs2_buffer_head *bh,
 	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;
@@ -887,7 +1072,7 @@ int check_dir(struct gfs2_sbd *sbp, uint64_t block, struct metawalk_fxns *pass)
 	ip = fsck_inode_get(sbp, bh);
 
 	if(ip->i_di.di_flags & GFS2_DIF_EXHASH) {
-		error = check_leaf(ip, &update, pass);
+		error = check_leaf_blks(ip, &update, pass);
 		if(error < 0) {
 			stack;
 			fsck_inode_put(ip, not_updated); /* does brelse(bh); */
@@ -1035,3 +1220,55 @@ int dinode_hash_remove(osi_list_t *buckets, uint64_t key)
 	}
 	return -1;
 }
+
+/**
+ * delete_blocks - delete blocks associated with an inode
+ */
+int delete_blocks(struct gfs2_inode *ip, uint64_t block,
+		  struct gfs2_buffer_head **bh, const char *btype,
+		  void *private)
+{
+	struct gfs2_block_query q = {0};
+
+	if (gfs2_check_range(ip->i_sbd, block) == 0) {
+		if (gfs2_block_check(ip->i_sbd, bl, block, &q))
+			return 0;
+		if (!q.dup_block) {
+			log_info("Deleting %s block %lld (0x%llx) as part "
+				 "of inode %lld (0x%llx)\n", btype,
+				 (unsigned long long)block,
+				 (unsigned long long)block,
+				 (unsigned long long)ip->i_di.di_num.no_addr,
+				 (unsigned long long)ip->i_di.di_num.no_addr);
+			gfs2_block_set(ip->i_sbd, bl, block, gfs2_block_free);
+			gfs2_free_block(ip->i_sbd, block);
+		}
+	}
+	return 0;
+}
+
+int delete_metadata(struct gfs2_inode *ip, uint64_t block,
+		    struct gfs2_buffer_head **bh, void *private)
+{
+	return delete_blocks(ip, block, bh, "metadata", private);
+}
+
+int delete_data(struct gfs2_inode *ip, uint64_t block, void *private)
+{
+	return delete_blocks(ip, block, NULL, "data", private);
+}
+
+int delete_eattr_indir(struct gfs2_inode *ip, uint64_t block, uint64_t parent,
+		       struct gfs2_buffer_head **bh,
+		       enum update_flags *want_updated, void *private)
+{
+	return delete_blocks(ip, block, NULL, "indirect extended attribute",
+			     private);
+}
+
+int delete_eattr_leaf(struct gfs2_inode *ip, uint64_t block, uint64_t parent,
+		      struct gfs2_buffer_head **bh,
+		      enum update_flags *want_updated, void *private)
+{
+	return delete_blocks(ip, block, NULL, "extended attribute", private);
+}
diff --git a/gfs2/fsck/metawalk.h b/gfs2/fsck/metawalk.h
index 44ac31a..6568bba 100644
--- a/gfs2/fsck/metawalk.h
+++ b/gfs2/fsck/metawalk.h
@@ -28,6 +28,18 @@ int remove_dentry_from_dir(struct gfs2_sbd *sbp, uint64_t dir,
 int find_di(struct gfs2_sbd *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);
+int delete_blocks(struct gfs2_inode *ip, uint64_t block,
+		  struct gfs2_buffer_head **bh, const char *btype,
+		  void *private);
+int delete_metadata(struct gfs2_inode *ip, uint64_t block,
+		    struct gfs2_buffer_head **bh, void *private);
+int delete_data(struct gfs2_inode *ip, uint64_t block, void *private);
+int delete_eattr_indir(struct gfs2_inode *ip, uint64_t block, uint64_t parent,
+		       struct gfs2_buffer_head **bh,
+		       enum update_flags *want_updated, void *private);
+int delete_eattr_leaf(struct gfs2_inode *ip, uint64_t block, uint64_t parent,
+		      struct gfs2_buffer_head **bh,
+		      enum update_flags *want_updated, void *private);
 
 /* metawalk_fxns: function pointers to check various parts of the fs
  *
@@ -78,7 +90,8 @@ struct metawalk_fxns {
 				     struct gfs2_ea_header *ea_hdr_prev,
 				     enum update_flags *want_updated,
 				     void *private);
-	int (*finish_eattr_indir) (struct gfs2_inode *ip, int indir_ok,
+	int (*finish_eattr_indir) (struct gfs2_inode *ip, int leaf_pointers,
+				   int leaf_pointer_errors,
 				   enum update_flags *want_updated,
 				   void *private);
 };
diff --git a/gfs2/fsck/pass1.c b/gfs2/fsck/pass1.c
index 329e95f..c03bb51 100644
--- a/gfs2/fsck/pass1.c
+++ b/gfs2/fsck/pass1.c
@@ -65,7 +65,8 @@ static int check_extended_leaf_eattr(struct gfs2_inode *ip, uint64_t *data_ptr,
 				     struct gfs2_ea_header *ea_hdr_prev,
 				     enum update_flags *want_updated,
 				     void *private);
-static int finish_eattr_indir(struct gfs2_inode *ip, int indir_ok,
+static int finish_eattr_indir(struct gfs2_inode *ip, int leaf_pointers,
+			      int leaf_pointer_errors,
 			      enum update_flags *want_updated, void *private);
 
 struct metawalk_fxns pass1_fxns = {
@@ -115,27 +116,30 @@ static int check_metalist(struct gfs2_inode *ip, uint64_t block,
 		return -1;
 	}
 	if(q.block_type != gfs2_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);
 		gfs2_block_mark(ip->i_sbd, bl, block, gfs2_dup_block);
 		found_dup = 1;
 	}
 	nbh = bread(&ip->i_sbd->buf_list, block);
 
 	if (gfs2_check_meta(nbh, GFS2_METATYPE_IN)){
-		log_debug("Bad indirect block pointer "
-				  "(points to something that is not an indirect block).\n");
+		log_debug("Bad indirect block pointer (points to "
+			  "something that is not an indirect block).\n");
 		if(!found_dup) {
 			gfs2_block_set(ip->i_sbd, bl, block, gfs2_meta_inval);
 			brelse(nbh, not_updated);
 			return 1;
 		}
-	}else  /* blk check ok */
+		brelse(nbh, not_updated);
+	} else /* blk check ok */
 		*bh = nbh;
 
-	log_debug("Setting %" PRIu64 " (0x%" PRIx64 ") to indirect block.\n",
-			  block, block);
-	gfs2_block_set(ip->i_sbd, bl, block, gfs2_indir_blk);
+	if (!found_dup) {
+		log_debug("Setting %" PRIu64 " (0x%" PRIx64 ") to indirect "
+			  "block.\n", block, block);
+		gfs2_block_set(ip->i_sbd, bl, block, gfs2_indir_blk);
+	}
 	bc->indir_count++;
 
 	return 0;
@@ -145,30 +149,134 @@ static int check_data(struct gfs2_inode *ip, uint64_t block, void *private)
 {
 	struct gfs2_block_query q = {0};
 	struct block_count *bc = (struct block_count *) private;
+	int error = 0, btype;
 
 	if (gfs2_check_range(ip->i_sbd, block)) {
-		log_err( "Bad data block pointer (out of range)\n");
+		log_err("inode %lld (0x%llx) has a bad data block pointer "
+			"%lld (out of range)\n",
+			(unsigned long long)ip->i_di.di_num.no_addr,
+			(unsigned long long)ip->i_di.di_num.no_addr,
+			(unsigned long long)block);
 		/* Mark the owner of this block with the bad_block
-		 * designator so we know to check it for out of range blocks later */
+		 * designator so we know to check it for out of range
+		 * blocks later */
 		gfs2_block_set(ip->i_sbd, bl, ip->i_di.di_num.no_addr,
 			       gfs2_bad_block);
 		return 1;
 	}
 	if(gfs2_block_check(ip->i_sbd, bl, block, &q)) {
 		stack;
+		log_err("Found bad block referenced as data at %"
+			PRIu64 " (0x%"PRIx64 ")\n", block, block);
 		return -1;
 	}
 	if(q.block_type != gfs2_block_free) {
-		log_debug("Found duplicate block at %" PRIu64 " (0x%"PRIx64 ")\n",
-				  block, block);
+		log_err("Found duplicate block referenced as data at %"
+			 PRIu64 " (0x%"PRIx64 ")\n", block, block);
+		if (q.block_type != gfs2_meta_inval) {
+			gfs2_block_mark(ip->i_sbd, bl, block, gfs2_dup_block);
+			/* 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 == gfs2_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 data block. */
+		error = 1;
+		gfs2_block_unmark(ip->i_sbd, bl, block, gfs2_meta_inval);
 		gfs2_block_mark(ip->i_sbd, bl, block, gfs2_dup_block);
-		bc->data_count++;
-		return 1;
 	}
-	log_debug("Setting %" PRIu64 " (0x%" PRIx64 ") to data block\n", block,
-			  block);
-	gfs2_block_set(ip->i_sbd, bl, block, gfs2_block_used);
+	log_debug("Marking block %llu (0x%llx) as data block\n",
+		  (unsigned long long)block, (unsigned long long)block);
+	gfs2_block_mark(ip->i_sbd, bl, block, gfs2_block_used);
+
+	/* This is also confusing, so I'll clarify.  There are two bitmaps:
+	   (1) The gfs2_bmap that fsck uses to keep track of what block
+	   type has been discovered, and (2) The rgrp bitmap.  Function
+	   gfs2_block_set is used to set the former and gfs2_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 gfs2_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 = gfs2_get_bitmap(ip->i_sbd, block, NULL);
+	if (btype != GFS2_BLKST_USED) {
+		const char *allocdesc[] = {"free space", "data", "unlinked",
+					   "metadata", "reserved"};
+
+		errors_found++;
+		log_err("Block #%llu (0x%llx) seems to be data, but is marked "
+			"as %s.\n", (unsigned long long)block,
+			(unsigned long long)block, allocdesc[btype]);
+		if(query(&opts, "Okay to mark it as 'data'? (y/n)")) {
+			errors_corrected++;
+			gfs2_set_bitmap(ip->i_sbd, block, GFS2_BLKST_USED);
+			log_err("The block was reassigned as data.\n");
+		} else {
+			log_err("The invalid block was ignored.\n");
+		}
+	}
 	bc->data_count++;
+	return error;
+}
+
+static int remove_inode_eattr(struct gfs2_inode *ip, struct block_count *bc,
+			      int duplicate, enum update_flags *want_updated)
+{
+	if (!duplicate) {
+		gfs2_set_bitmap(ip->i_sbd, ip->i_di.di_eattr,
+				GFS2_BLKST_FREE);
+		gfs2_block_set(ip->i_sbd, bl, ip->i_di.di_eattr,
+			       gfs2_block_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 &= ~GFS2_DIF_EA_INDIRECT;
+	*want_updated = updated;
+	return 0;
+}
+
+static int ask_remove_inode_eattr(struct gfs2_inode *ip,
+				  struct block_count *bc,
+				  enum update_flags *want_updated)
+{
+	log_err("Inode %lld (0x%llx) has unrecoverable Extended Attribute "
+		"errors.\n", (unsigned long long)ip->i_di.di_num.no_addr,
+		(unsigned long long)ip->i_di.di_num.no_addr);
+	errors_found++;
+	if (query(&opts, "Clear all Extended Attributes from the "
+		  "inode? (y/n) ")) {
+		struct gfs2_block_query q;
+
+		errors_corrected++;
+		if(gfs2_block_check(ip->i_sbd, bl, ip->i_di.di_eattr, &q)) {
+			stack;
+			return -1;
+		}
+		if (!remove_inode_eattr(ip, bc, q.dup_block, want_updated))
+			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;
 }
 
@@ -190,30 +298,24 @@ static int clear_eas(struct gfs2_inode *ip, struct block_count *bc,
 	*want_updated = not_updated;
 	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");
+	log_err(" at block #%lld (0x%llx).\n",
+		(unsigned long long)block, (unsigned long long)block);
 	errors_found++;
-	if ((errors_corrected += query(&opts, "Clear the bad EA? (y/n) "))) {
-		if (block == 0)
-			block = ip->i_di.di_eattr;
-		gfs2_block_clear(sdp, bl, block, gfs2_eattr_block);
-		if (!duplicate) {
-			gfs2_block_clear(sdp, bl, block, gfs2_indir_blk);
+	if (query(&opts, "Clear the bad Extended Attribute? (y/n) ")) {
+		errors_corrected++;
+		if (block == ip->i_di.di_eattr) {
+			remove_inode_eattr(ip, bc, duplicate, want_updated);
+			log_err("The bad extended attribute was removed.\n");
+		} else if (!duplicate) {
 			gfs2_block_set(sdp, bl, block, gfs2_block_free);
 			gfs2_set_bitmap(sdp, block, GFS2_BLKST_FREE);
+			log_err("The bad Extended Attribute was "
+				"removed.\n");
 		}
-		ip->i_di.di_flags &= ~GFS2_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;
-		gfs2_dinode_out(&ip->i_di, ip->i_bh->b_data);
 		*want_updated = updated;
 		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;
 	}
@@ -251,48 +353,131 @@ static int check_eattr_indir(struct gfs2_inode *ip, uint64_t indirect,
 	if(gfs2_check_meta(*bh, GFS2_METATYPE_IN)) {
 		if(q.block_type != gfs2_block_free) { /* Duplicate? */
 			if (!clear_eas(ip, bc, indirect, 1, want_updated,
-				       "Bad indirect EA duplicate found"))
-				gfs2_block_set(sdp, bl, indirect,
-					       gfs2_dup_block);
+				       "Bad indirect Extended Attribute "
+				       "duplicate found")) {
+				gfs2_block_mark(sdp, bl, indirect,
+						gfs2_dup_block);
+				bc->ea_count++;
+			}
 			return 1;
 		}
 		clear_eas(ip, bc, indirect, 0, want_updated,
-			  "EA indirect block has incorrect type");
+			  "Extended Attribute indirect block has incorrect "
+			  "type");
 		return 1;
 	}
 	if(q.block_type != gfs2_block_free) { /* Duplicate? */
 		log_err("Inode #%" PRIu64 " (0x%" PRIx64
-			"): Duplicate EA indirect block found at #%" PRIu64
-			" (0x%" PRIx64 ").\n",
+			"): Duplicate Extended Attribute indirect block "
+			"found at #%" PRIu64 " (0x%" PRIx64 ").\n",
 			ip->i_di.di_num.no_addr, ip->i_di.di_num.no_addr,
 			indirect, indirect);
-		gfs2_block_set(sdp, bl, indirect, gfs2_dup_block);
+		gfs2_block_mark(sdp, bl, indirect, gfs2_dup_block);
 		bc->ea_count++;
 		ret = 1;
 	} else {
 		log_debug("Setting #%" PRIu64 " (0x%" PRIx64
-			  ") to indirect EA block\n", indirect, indirect);
+			  ") to indirect Extended Attribute block\n",
+			  indirect, indirect);
 		gfs2_block_set(sdp, bl, indirect, gfs2_indir_blk);
 		bc->ea_count++;
 	}
 	return ret;
 }
 
-static int finish_eattr_indir(struct gfs2_inode *ip, int indir_ok,
+static int finish_eattr_indir(struct gfs2_inode *ip, int leaf_pointers,
+			      int leaf_pointer_errors,
 			      enum update_flags *want_updated, void *private)
 {
-	if (indir_ok) {
-		log_debug("Marking inode #%" PRIu64 " (0x%"
-			  PRIx64 ") with eattr block\n",
-			  ip->i_di.di_num.no_addr, ip->i_di.di_num.no_addr);
-		/* Mark the inode as having an eattr in the block map
-		   so pass1c can check it. */
-		gfs2_block_mark(ip->i_sbd, bl, ip->i_di.di_num.no_addr,
-				gfs2_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, want_updated);
+	log_debug("Marking inode #%lld (0x%llx) with extended "
+		  "attribute block\n",
+		  (unsigned long long)ip->i_di.di_num.no_addr,
+		  (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. */
+	gfs2_block_mark(ip->i_sbd, bl, ip->i_di.di_num.no_addr,
+			gfs2_eattr_block);
+	bc->ea_count++;
+	if (!leaf_pointer_errors)
 		return 0;
+	log_err("Inode %lld (0x%llx) has recoverable indirect "
+		"Extended Attribute errors.\n",
+		(unsigned long long)ip->i_di.di_num.no_addr,
+		(unsigned long long)ip->i_di.di_num.no_addr);
+	errors_found++;
+	if (query(&opts, "Okay to fix the block count for the inode? "
+		  "(y/n) ")) {
+		errors_corrected++;
+		ip->i_di.di_blocks = 1 + bc->indir_count +
+			bc->data_count + bc->ea_count;
+		*want_updated = updated;
+		log_err("Block count fixed.\n");
+		return 1;
+	}
+	log_err("Block count not fixed.\n");
+	return 1;
+}
+
+static int check_leaf_block(struct gfs2_inode *ip, uint64_t block, int btype,
+			    struct gfs2_buffer_head **bh,
+			    enum update_flags *want_updated, void *private)
+{
+	struct gfs2_buffer_head *leaf_bh = NULL;
+	struct gfs2_sbd *sdp = ip->i_sbd;
+	struct gfs2_block_query q = {0};
+	struct block_count *bc = (struct block_count *) private;
+
+	if(gfs2_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. */
+	leaf_bh = bread(&sdp->buf_list, block);
+	if(gfs2_check_meta(leaf_bh, btype)) {
+		if(q.block_type != gfs2_block_free) { /* Duplicate? */
+			clear_eas(ip, bc, block, 1, want_updated,
+				  "Bad Extended Attribute duplicate found");
+		} else {
+			clear_eas(ip, bc, block, 0, want_updated,
+				  "Extended Attribute leaf block "
+				  "has incorrect type");
+		}
+		brelse(leaf_bh, *want_updated);
+		return 1;
 	}
-	clear_eas(ip, (struct block_count *)private, 0, 0, want_updated,
-		  "has unrecoverable indirect EA errors");
+	if(q.block_type != gfs2_block_free) { /* Duplicate? */
+		log_debug("Duplicate block found at #%lld (0x%llx).\n",
+			  (unsigned long long)block,
+			  (unsigned long long)block);
+		gfs2_block_mark(sdp, bl, block, gfs2_dup_block);
+		bc->ea_count++;
+		brelse(leaf_bh, not_updated);
+		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, want_updated,
+			  "Extended Attribute block removed due to "
+			  "previous errors.\n");
+		brelse(leaf_bh, *want_updated);
+		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. */
+	gfs2_block_set(sdp, bl, block, gfs2_meta_eattr);
+	bc->ea_count++;
+	*bh = leaf_bh;
 	return 0;
 }
 
@@ -314,60 +499,29 @@ static int check_extended_leaf_eattr(struct gfs2_inode *ip, uint64_t *data_ptr,
 				     enum update_flags *want_updated,
 				     void *private)
 {
-	struct gfs2_buffer_head *el_buf;
-	struct gfs2_sbd *sdp = ip->i_sbd;
-	struct gfs2_block_query q;
 	uint64_t el_blk = be64_to_cpu(*data_ptr);
-	struct block_count *bc = (struct block_count *) private;
-	int ret = 0;
+	struct gfs2_sbd *sdp = ip->i_sbd;
+	struct gfs2_buffer_head *bh = NULL;
+	int error;
 
 	if(gfs2_check_range(sdp, el_blk)){
-		log_err("Inode #%" PRIu64 " (0x%" PRIx64 "): EA extended "
-			"leaf block #%" PRIu64 " (0x%" PRIx64
-			") is out of range.\n",
-			ip->i_di.di_num.no_addr, ip->i_di.di_num.no_addr,
-			el_blk, el_blk);
+		log_err("Inode #%llu (0x%llx): Extended Attribute block "
+			"%llu (0x%llx) has an extended leaf block #%llu "
+			"(0x%llx) that is out of range.\n",
+			(unsigned long long)ip->i_di.di_num.no_addr,
+			(unsigned long long)ip->i_di.di_num.no_addr,
+			(unsigned long long)ip->i_di.di_eattr,
+			(unsigned long long)ip->i_di.di_eattr,
+			(unsigned long long)el_blk,
+			(unsigned long long)el_blk);
 		gfs2_block_set(sdp, bl, ip->i_di.di_eattr, gfs2_bad_block);
 		return 1;
 	}
-
-	if(gfs2_block_check(sdp, bl, el_blk, &q)) {
-		stack;
-		return -1;
-	}
-	el_buf = bread(&sdp->buf_list, el_blk);
-
-	/* 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(gfs2_check_meta(el_buf, GFS2_METATYPE_ED)) {
-		if(q.block_type != gfs2_block_free) /* Duplicate? */
-			clear_eas(ip, bc, el_blk, 1, want_updated,
-				  "has bad extended EA duplicate");
-		else
-			clear_eas(ip, bc, el_blk, 0, want_updated,
-				  "EA extended leaf block has incorrect type");
-		ret = 1;
-	} else { /* If this looks like an EA */
-		if(q.block_type != gfs2_block_free) { /* Duplicate? */
-			log_debug("Duplicate block found at #%" PRIu64
-				  " (0x%" PRIx64 ").\n",
-				  el_blk, el_blk);
-			gfs2_block_set(sdp, bl, el_blk, gfs2_dup_block);
-			bc->ea_count++;
-			ret = 1;
-		} else {
-			log_debug("Setting block #%" PRIu64
-				  " (0x%" PRIx64 ") to eattr block\n",
-				  el_blk, el_blk);
-			gfs2_block_set(sdp, bl, el_blk, gfs2_meta_eattr);
-			bc->ea_count++;
-		}
-	}
-
-	brelse(el_buf, not_updated);
-	return ret;
+	error = check_leaf_block(ip, el_blk, GFS2_METATYPE_ED, &bh,
+				 want_updated, private);
+	if (bh)
+		brelse(bh, not_updated);
+	return error;
 }
 
 static int check_eattr_leaf(struct gfs2_inode *ip, uint64_t block,
@@ -375,69 +529,27 @@ static int check_eattr_leaf(struct gfs2_inode *ip, uint64_t block,
 			    enum update_flags *want_updated, void *private)
 {
 	struct gfs2_sbd *sdp = ip->i_sbd;
-	struct gfs2_buffer_head *leaf_bh = NULL;
-	int ret = 0;
-	struct gfs2_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 " (0x%" PRIx64 ") to eattr block\n",
-				  parent, parent);
-		gfs2_block_set(sdp, bl, parent, gfs2_eattr_block);
-	}
-	if(gfs2_check_range(sdp, block)){
-		log_warn("Inode #%" PRIu64 " (0x%" PRIx64 "): EA leaf block "
-			 "#%" PRIu64 " (0x%" PRIx64 ") is out of range.\n",
-			 ip->i_di.di_num.no_addr, ip->i_di.di_num.no_addr,
-			 block, 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 %lld (0x%llx) as having eattr "
+		  "block\n", (unsigned long long)ip->i_di.di_num.no_addr,
+		  (unsigned long long)ip->i_di.di_num.no_addr);
+	gfs2_block_mark(sdp, bl, ip->i_di.di_num.no_addr, gfs2_eattr_block);
+	if(gfs2_check_range(sdp, block)) {
+		log_warn("Inode #%llu (0x%llx): Extended Attribute leaf "
+			 "block #%llu (0x%llx) is out of range.\n",
+			 (unsigned long long)ip->i_di.di_num.no_addr,
+			 (unsigned long long)ip->i_di.di_num.no_addr,
+			 (unsigned long long)block, (unsigned long long)block);
 		gfs2_block_set(sdp, bl, ip->i_di.di_eattr, gfs2_bad_block);
-		ret = 1;
-	}
-	else if(gfs2_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. */
-		leaf_bh = bread(&sdp->buf_list, block);
-		if(gfs2_check_meta(leaf_bh, GFS2_METATYPE_EA)) {
-			if(q.block_type != gfs2_block_free) { /* Duplicate? */
-				clear_eas(ip, bc, block, 1, want_updated,
-					  "Bad EA duplicate found");
-			} else {
-				clear_eas(ip, bc, block, 0, want_updated,
-					  "EA leaf block has incorrect type");
-			}
-			ret = 1;
-			brelse(leaf_bh, not_updated);
-		} else { /* If this looks like an EA */
-			if(q.block_type != gfs2_block_free) { /* Duplicate? */
-				log_debug("Duplicate block found at #%" PRIu64
-					  " (0x%" PRIx64 ").\n",
-					  block, block);
-				gfs2_block_set(sdp, bl, block, gfs2_dup_block);
-				bc->ea_count++;
-				ret = 1;
-				brelse(leaf_bh, not_updated);
-			} else {
-				log_debug("Setting block #%" PRIu64
-					  " (0x%" PRIx64 ") to eattr block\n",
-					  block, block);
-				gfs2_block_set(sdp, bl, block,
-					       gfs2_meta_eattr);
-				bc->ea_count++;
-			}
-		}
+		return 1;
 	}
-	if (!ret)
-		*bh = leaf_bh;
-
-	return ret;
+	return check_leaf_block(ip, block, GFS2_METATYPE_EA, bh, want_updated,
+				private);
 }
 
 static int check_eattr_entries(struct gfs2_inode *ip,
@@ -519,6 +631,7 @@ int clear_leaf(struct gfs2_inode *ip, uint64_t block,
 	       struct gfs2_buffer_head *bh, void *private)
 {
 	struct gfs2_block_query q = {0};
+
 	log_crit("Clearing leaf #%" PRIu64 " (0x%" PRIx64 ")\n", block, block);
 
 	if(gfs2_block_check(ip->i_sbd, bl, block, &q)) {
@@ -535,7 +648,6 @@ int clear_leaf(struct gfs2_inode *ip, uint64_t block,
 		return 0;
 	}
 	return 0;
-
 }
 
 int add_to_dir_list(struct gfs2_sbd *sbp, uint64_t block)
@@ -590,9 +702,9 @@ int handle_di(struct gfs2_sbd *sdp, struct gfs2_buffer_head *bh,
 				block, block, ip->i_di.di_num.no_addr,
 				ip->i_di.di_num.no_addr);
 		errors_found++;
-		if((errors_corrected +=
-		    query(&opts, "Fix address in inode at block #%"
-			  PRIu64 " (0x%" PRIx64 ")? (y/n) ", block, block))) {
+		if(query(&opts, "Fix address in inode at block #%"
+			 PRIu64 " (0x%" PRIx64 ")? (y/n) ", block, block)) {
+			errors_corrected++;
 			ip->i_di.di_num.no_addr = ip->i_di.di_num.no_formal_ino = block;
 			gfs2_dinode_out(&ip->i_di, ip->i_bh->b_data);
 			f = updated;
@@ -607,8 +719,8 @@ int handle_di(struct gfs2_sbd *sdp, struct gfs2_buffer_head *bh,
 		return -1;
 	}
 	if(q.block_type != gfs2_block_free) {
-		log_debug("Found duplicate block at #%" PRIu64 " (0x%" PRIx64 ")\n",
-				  block, block);
+		log_err("Found duplicate block referenced as an inode at #%"
+			PRIu64 " (0x%" PRIx64 ")\n", block, block);
 		if(gfs2_block_mark(sdp, bl, block, gfs2_dup_block)) {
 			stack;
 			fsck_inode_put(ip, f);
@@ -749,13 +861,14 @@ int handle_di(struct gfs2_sbd *sdp, struct gfs2_buffer_head *bh,
 	pass1_fxns.private = &bc;
 
 	error = check_metatree(ip, &pass1_fxns);
-	if(error < 0) {
+	if (fsck_abort || error < 0) {
 		fsck_inode_put(ip, f);
 		return 0;
 	}
 	if(error > 0) {
-		log_warn("Marking inode #%" PRIu64 " (0x%" PRIx64 ") invalid\n",
-			 ip->i_di.di_num.no_addr, ip->i_di.di_num.no_addr);
+		log_warn("Marking inode #%lld (0x%llx) invalid\n",
+			 (unsigned long long)ip->i_di.di_num.no_addr,
+			 ip->i_di.di_num.no_addr);
 		/* FIXME: Must set all leaves invalid as well */
 		check_metatree(ip, &invalidate_metatree);
 		gfs2_block_set(sdp, bl, ip->i_di.di_num.no_addr,
@@ -765,7 +878,11 @@ int handle_di(struct gfs2_sbd *sdp, struct gfs2_buffer_head *bh,
 		return 0;
 	}
 
-	check_inode_eattr(ip, &f, &pass1_fxns);
+	error = check_inode_eattr(ip, &f, &pass1_fxns);
+
+	if (error && f == updated &&
+	    !(ip->i_di.di_flags & GFS2_DIF_EA_INDIRECT))
+		ask_remove_inode_eattr(ip, &bc, &f);
 
 	if (ip->i_di.di_blocks != 
 		(1 + bc.indir_count + bc.data_count + bc.ea_count)) {
@@ -774,9 +891,13 @@ int handle_di(struct gfs2_sbd *sdp, struct gfs2_buffer_head *bh,
 				ip->i_di.di_num.no_addr, ip->i_di.di_num.no_addr,
 				ip->i_di.di_blocks,
 				1 + bc.indir_count + bc.data_count + bc.ea_count);
+		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);
 		errors_found++;
-		if ((errors_corrected +=
-		     query(&opts, "Fix ondisk block count? (y/n) "))) {
+		if (query(&opts, "Fix ondisk block count? (y/n) ")) {
+			errors_corrected++;
 			ip->i_di.di_blocks = 1 + bc.indir_count + bc.data_count +
 				bc.ea_count;
 			gfs2_dinode_out(&ip->i_di, ip->i_bh->b_data);
@@ -795,13 +916,21 @@ int scan_meta(struct gfs2_sbd *sdp, struct gfs2_buffer_head *bh,
 			  uint64_t block)
 {
 	if (gfs2_check_meta(bh, 0)) {
-		log_debug("Found invalid metadata at #%" PRIu64 " (0x%" PRIx64 ")\n",
-				  block, block);
+		errors_found++;
+
+		log_err("Found invalid metadata block at #%llu (0x%llx)\n",
+			(unsigned long long)block, (unsigned long long)block);
 		if(gfs2_block_set(sdp, bl, block, gfs2_meta_inval)) {
 			stack;
 			return -1;
 		}
-		gfs2_set_bitmap(sdp, block, GFS2_BLKST_FREE);
+		if(query(&opts, "Okay to free the invalid block? (y/n)")) {
+			errors_corrected++;
+			gfs2_set_bitmap(sdp, block, GFS2_BLKST_FREE);
+			log_err("The invalid block was freed.\n");
+		} else {
+			log_err("The invalid block was ignored.\n");
+		}
 		return 0;
 	}
 
@@ -817,12 +946,12 @@ int scan_meta(struct gfs2_sbd *sdp, struct gfs2_buffer_head *bh,
 			return -1;
 		}
 	}
-	/* Ignore everything else - they should be hit by the handle_di step. */
-	/* Don't check NONE either, because check_meta passes everything if   */
-	/* GFS2_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.                                         */
+	/* Ignore everything else - they should be hit by the handle_di step.
+	 * Don't check NONE either, because check_meta passes everything if
+	 * GFS2_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;
 }
 
diff --git a/gfs2/fsck/pass1b.c b/gfs2/fsck/pass1b.c
index 0ca9191..9b11cab 100644
--- a/gfs2/fsck/pass1b.c
+++ b/gfs2/fsck/pass1b.c
@@ -173,13 +173,15 @@ static int clear_dup_metalist(struct gfs2_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",
-				dh->id->name ? dh->id->name : "unknown name",
-				ip->i_di.di_num.no_addr, block);
+		log_err("Found duplicate reference in inode \"%s\" at block #%"
+			PRIu64 " (0x%" PRIx64 ") to block #%" PRIu64 " (0x%"
+			PRIx64 ")\n",
+			dh->id->name ? dh->id->name : "unknown name",
+			ip->i_di.di_num.no_addr, ip->i_di.di_num.no_addr,
+			block, block);
 		log_err("Inode %s is in directory %"PRIu64" (0x%" PRIx64 ")\n",
-				dh->id->name ? dh->id->name : "",
-				dh->id->parent, dh->id->parent);
+			dh->id->name ? dh->id->name : "", dh->id->parent,
+			dh->id->parent);
 		inode_hash_remove(inode_hash, ip->i_di.di_num.no_addr);
 		/* Setting the block to invalid means the inode is
 		 * cleared in pass2 */
@@ -191,29 +193,9 @@ static int clear_dup_metalist(struct gfs2_inode *ip, uint64_t block,
 
 static int clear_dup_data(struct gfs2_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\" for block #%" PRIu64
-				" (0x%" PRIx64 ") at block #%" PRIu64 " (0x%" PRIx64 ")\n",
-				dh->id->name ? dh->id->name : "unknown name",
-				ip->i_di.di_num.no_addr, ip->i_di.di_num.no_addr, block,
-				block);
-		log_err("Inode %s is in directory %"PRIu64" (0x%" PRIx64 ")\n",
-				dh->id->name ? dh->id->name : "", dh->id->parent,
-				dh->id->parent);
-		inode_hash_remove(inode_hash, ip->i_di.di_num.no_addr);
-		/* Setting the block to invalid means the inode is
-		 * cleared in pass2 */
-		gfs2_block_set(ip->i_sbd, bl, ip->i_di.di_num.no_addr,
-			       gfs2_meta_inval);
-	}
-
-	return 0;
+	return clear_dup_metalist(ip, block, NULL, private);
 }
+
 static int clear_dup_eattr_indir(struct gfs2_inode *ip, uint64_t block,
 				 uint64_t parent, struct gfs2_buffer_head **bh,
 				 enum update_flags *want_updated,
@@ -361,15 +343,15 @@ int find_block_ref(struct gfs2_sbd *sbp, uint64_t inode, struct dup_blocks *b)
 	enum update_flags update;
 
 	ip = fsck_load_inode(sbp, inode); /* bread, inode_get */
-	log_info("Checking inode %" PRIu64 " (0x%" PRIx64
-			 ")'s metatree for references to block %" PRIu64 " (0x%" PRIx64
-			 ")\n", inode, inode, b->block_no, b->block_no);
+	log_debug("Checking inode %" PRIu64 " (0x%" PRIx64 ")'s "
+		  "metatree for references to block %" PRIu64" (0x% " PRIx64
+		  ")\n", inode, inode, b->block_no, b->block_no);
 	if(check_metatree(ip, &find_refs)) {
 		stack;
 		fsck_inode_put(ip, not_updated); /* out, brelse, free */
 		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, &update, &find_refs) < 0){
 		stack;
@@ -421,18 +403,74 @@ int handle_dup_blk(struct gfs2_sbd *sbp, struct dup_blocks *b)
 		dh.ref_inode_count++;
 		dh.ref_count += id->dup_count;
 	}
+	/* 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) {
+		struct gfs2_buffer_head *bh;
+		uint32_t cmagic;
+
+		bh = bread(&sbp->buf_list, b->block_no);
+		cmagic = ((struct gfs2_meta_header *)(bh->b_data))->mh_magic;
+		brelse(bh, not_updated);
+		if (be32_to_cpu(cmagic) == GFS2_MAGIC) {
+			tmp = b->ref_inode_list.next;
+			id = osi_list_entry(tmp, struct inode_with_dups, list);
+			log_warn("Inode %s (%lld/0x%llx) has a reference to "
+				 "data block %"PRIu64" (0x%" PRIx64 "), but "
+				 "the block is really metadata.\n",
+				 id->name, id->block_no, id->block_no,
+				 b->block_no, b->block_no);
+			errors_found++;
+			if (query(&opts, "Clear the inode? (y/n) ")) {
+				errors_corrected++;
+				log_warn("Clearing inode %lld (0x%llx)...\n",
+					 (unsigned long long)id->block_no,
+					 (unsigned long long)id->block_no);
+				ip = fsck_load_inode(sbp, id->block_no);
+				inode_hash_remove(inode_hash,
+						  ip->i_di.di_num.no_addr);
+				/* Setting the block to invalid means the inode
+				   is cleared in pass2 */
+				gfs2_block_set(ip->i_sbd, bl,
+					       ip->i_di.di_num.no_addr,
+					       gfs2_meta_inval);
+				fsck_inode_put(ip, updated);
+			} else {
+				log_warn("The bad inode was not cleared.");
+			}
+			return 0;
+		}
+	}
+
 	log_notice("Block %" PRIu64 " (0x%" PRIx64 ") has %d inodes referencing it"
 			   " for a total of %d duplicate references\n",
 			   b->block_no, b->block_no, dh.ref_inode_count,
-			   dh.ref_inode_count, dh.ref_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 (%lld/0x%llx) has %d reference(s) to "
+			 "block %"PRIu64" (0x%" PRIx64 ")\n",
+			 id->name, id->block_no, id->block_no,
+			 id->dup_count, b->block_no, b->block_no);
+	}
 	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
-				 " (0x%" PRIx64 ")\n", id->name, id->dup_count, b->block_no,
-				 b->block_no);
-		/* FIXME: User input */
-		log_warn("Clearing...\n");
+		errors_found++;
+		if (!(query(&opts, "Okay to clear inode %lld (0x%llx)? (y/n) ",
+			    id->block_no, id->block_no))) {
+			log_warn("The bad inode was not cleared...\n");
+			continue;
+		}
+		errors_corrected++;
+		log_warn("Clearing inode %lld (0x%llx)...\n",
+			 (unsigned long long)id->block_no,
+			 (unsigned long long)id->block_no);
 		ip = fsck_load_inode(sbp, id->block_no);
 		dh.b = b;
 		dh.id = id;
@@ -443,7 +481,9 @@ int handle_dup_blk(struct gfs2_sbd *sbp, struct dup_blocks *b)
 		if(!id->ea_only)
 			check_metatree(ip, &clear_dup_fxns);
 
-		fsck_inode_put(ip, not_updated); /* out, brelse, free */
+		gfs2_block_set(ip->i_sbd, bl, ip->i_di.di_num.no_addr,
+			       gfs2_meta_inval);
+		fsck_inode_put(ip, updated); /* out, brelse, free */
 		dh.ref_inode_count--;
 		if(dh.ref_inode_count == 1)
 			break;
@@ -519,13 +559,10 @@ int pass1b(struct gfs2_sbd *sbp)
 	 * it later */
 	log_info("Handling duplicate blocks\n");
 out:
-	while (!osi_list_empty(&sbp->dup_blocks.list)) {
-		b = osi_list_entry(sbp->dup_blocks.list.next,
-				   struct dup_blocks, list);
+        osi_list_foreach_safe(tmp, &sbp->dup_blocks.list, x) {
+                b = osi_list_entry(tmp, struct dup_blocks, list);
 		if (!skip_this_pass && !rc) /* no error & not asked to skip the rest */
 			handle_dup_blk(sbp, b);
-		osi_list_del(&b->list);
-		free(b);
 	}
 	return rc;
 }
diff --git a/gfs2/fsck/pass1c.c b/gfs2/fsck/pass1c.c
index ac3ed27..4736433 100644
--- a/gfs2/fsck/pass1c.c
+++ b/gfs2/fsck/pass1c.c
@@ -26,52 +26,95 @@ static int remove_eattr_entry(struct gfs2_sbd *sdp,
 			      struct gfs2_ea_header *curr,
 			      struct gfs2_ea_header *prev)
 {
-	log_warn("Removing EA located in block #%"PRIu64" (0x%" PRIx64 ").\n",
-		 leaf_bh->b_blocknr, leaf_bh->b_blocknr);
-	if(!prev)
+	if (!prev)
 		curr->ea_type = GFS2_EATYPE_UNUSED;
 	else {
-		prev->ea_rec_len =
-			cpu_to_be32(be32_to_cpu(curr->ea_rec_len) +
-						be32_to_cpu(prev->ea_rec_len));
+		uint32_t tmp32 = be32_to_cpu(curr->ea_rec_len) +
+			be32_to_cpu(prev->ea_rec_len);
+		prev->ea_rec_len = cpu_to_be32(tmp32);
 		if (curr->ea_flags & GFS2_EAFLAG_LAST)
 			prev->ea_flags |= GFS2_EAFLAG_LAST;	
 	}
+	log_err("Bad Extended Attribute at block #%"PRIu64
+		" (0x%" PRIx64 ") removed.\n",
+		leaf_bh->b_blocknr, leaf_bh->b_blocknr);
 	return 0;
 }
 
-int check_eattr_indir(struct gfs2_inode *ip, uint64_t block,
+static int ask_remove_eattr_entry(struct gfs2_sbd *sdp,
+				  struct gfs2_buffer_head *leaf_bh,
+				  struct gfs2_ea_header *curr,
+				  struct gfs2_ea_header *prev,
+				  int fix_curr, int fix_curr_len)
+{
+	errors_found++;
+	if (query(&opts, "Remove the bad Extended Attribute entry? "
+		  "(y/n) ")) {
+		errors_corrected++;
+		if (fix_curr)
+			curr->ea_flags |= GFS2_EAFLAG_LAST;
+		if (fix_curr_len) {
+			uint32_t max_size = sdp->sd_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 gfs2_inode *ip,
+			    enum update_flags *need_update)
+{
+	errors_found++;
+	if (query(&opts, "Remove the bad Extended Attribute? (y/n) ")) {
+		errors_corrected++;
+		ip->i_di.di_eattr = 0;
+		*need_update = updated;
+		log_err("Bad Extended Attribute removed.\n");
+	} else
+		log_err("Bad Extended Attribute not removed.\n");
+	return 1;
+}
+
+static int check_eattr_indir(struct gfs2_inode *ip, uint64_t block,
 		      uint64_t parent, struct gfs2_buffer_head **bh,
-		      enum update_flags *update, void *private)
+		      enum update_flags *need_update, void *private)
 {
 	struct gfs2_sbd *sbp = ip->i_sbd;
 	struct gfs2_block_query q;
 	struct gfs2_buffer_head *indir_bh = NULL;
 
-	*update = not_updated;
+	*need_update = not_updated;
 	if(gfs2_check_range(sbp, block)) {
-		log_err("Extended attributes indirect block #%"PRIu64
-			" (0x%" PRIx64 ") for inode #%" PRIu64
-			" (0x%" PRIx64 ") out of range...removing\n",
-			block, block, ip->i_di.di_num.no_addr,
-			ip->i_di.di_num.no_addr);
-		ip->i_di.di_eattr = 0;
-		*update = (opts.no ? not_updated : updated);
-		return 1;
+		log_err("Extended attributes indirect block #%llu"
+			" (0x%llx) for inode #%llu"
+			" (0x%llx) out of range...removing\n",
+			(unsigned long long)block,
+			(unsigned long long)block,
+			(unsigned long long)ip->i_di.di_num.no_addr,
+			(unsigned long long)ip->i_di.di_num.no_addr);
+		return ask_remove_eattr(ip, need_update);
 	}
 	else if (gfs2_block_check(sbp, bl, block, &q)) {
 		stack;
 		return -1;
 	}
 	else if(q.block_type != gfs2_indir_blk) {
-		log_err("Extended attributes indirect block #%"PRIu64
-			" (0x%" PRIx64 ") for inode #%" PRIu64
-			" (0x%" PRIx64 ") invalid...removing\n",
-			block, block, ip->i_di.di_num.no_addr,
-			ip->i_di.di_num.no_addr);
-		ip->i_di.di_eattr = 0;
-		*update = (opts.no ? not_updated : updated);
-		return 1;
+		log_err("Extended attributes indirect block #%llu"
+			" (0x%llx) for inode #%llu"
+			" (0x%llx) invalid.\n",
+			(unsigned long long)block,
+			(unsigned long long)block,
+			(unsigned long long)ip->i_di.di_num.no_addr,
+			(unsigned long long)ip->i_di.di_num.no_addr);
+		return ask_remove_eattr(ip, need_update);
 	}
 	else
 		indir_bh = bread(&sbp->buf_list, block);
@@ -80,33 +123,30 @@ int check_eattr_indir(struct gfs2_inode *ip, uint64_t block,
 	return 0;
 }
 
-int check_eattr_leaf(struct gfs2_inode *ip, uint64_t block,
+static int check_eattr_leaf(struct gfs2_inode *ip, uint64_t block,
 		     uint64_t parent, struct gfs2_buffer_head **bh,
-		     enum update_flags *update, void *private)
+		     enum update_flags *need_update, void *private)
 {
 	struct gfs2_sbd *sbp = ip->i_sbd;
 	struct gfs2_block_query q;
 
-	*update = not_updated;
 	if(gfs2_check_range(sbp, block)) {
-		log_err("Extended attributes block for inode #%" PRIu64
-			" (0x%" PRIx64 ") out of range...removing\n",
-			ip->i_di.di_num.no_addr, ip->i_di.di_num.no_addr);
-		ip->i_di.di_eattr = 0;
-		*update = (opts.no ? not_updated : updated);
-		return 1;
+		log_err("Extended attributes block for inode #%llu"
+			" (0x%llx) out of range.\n",
+			(unsigned long long)ip->i_di.di_num.no_addr,
+			(unsigned long long)ip->i_di.di_num.no_addr);
+		return ask_remove_eattr(ip, need_update);
 	}
 	else if (gfs2_block_check(sbp, bl, block, &q)) {
 		stack;
 		return -1;
 	}
 	else if(q.block_type != gfs2_meta_eattr) {
-		log_err("Extended attributes block for inode #%"PRIu64
-			" (0x%" PRIx64 ") invalid...removing\n",
-			ip->i_di.di_num.no_addr, ip->i_di.di_num.no_addr);
-		ip->i_di.di_eattr = 0;
-		*update = (opts.no ? not_updated : updated);
-		return 1;
+		log_err("Extended attributes block for inode #%llu"
+			" (0x%llx) invalid.\n",
+			 (unsigned long long)ip->i_di.di_num.no_addr,
+			 (unsigned long long)ip->i_di.di_num.no_addr);
+		return ask_remove_eattr(ip, need_update);
 	}
 	else 
 		*bh = bread(&sbp->buf_list, block);
@@ -128,41 +168,24 @@ static int check_eattr_entry(struct gfs2_inode *ip,
 
 	if(!ea_hdr->ea_name_len){
 		log_err("EA has name length == 0\n");
-		ea_hdr->ea_flags |= GFS2_EAFLAG_LAST;
-		ea_hdr->ea_rec_len = cpu_to_be32(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 + be32_to_cpu(ea_hdr->ea_rec_len) > max_size){
 		log_err("EA rec length too long\n");
-		ea_hdr->ea_flags |= GFS2_EAFLAG_LAST;
-		ea_hdr->ea_rec_len = cpu_to_be32(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 + be32_to_cpu(ea_hdr->ea_rec_len) == max_size &&
 	   (ea_hdr->ea_flags & GFS2_EAFLAG_LAST) == 0){
 		log_err("last EA has no last entry flag\n");
-		ea_hdr->ea_flags |= GFS2_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));
@@ -173,11 +196,8 @@ static int check_eattr_entry(struct gfs2_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, GFS2_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){
@@ -188,24 +208,22 @@ static int check_eattr_entry(struct gfs2_inode *ip,
 		max_ptrs = (be32_to_cpu(ea_hdr->ea_data_len)+avail_size-1)/avail_size;
 
 		if(max_ptrs > ea_hdr->ea_num_ptrs){
-			log_err("EA (%s) has incorrect number of pointers.\n", ea_name);
-			log_err("  Required:  %d\n"
-				"  Reported:  %d\n",
+			log_err("EA (%s) has incorrect number of pointers.\n",
+				ea_name);
+			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);
+			log_debug("  Pointers Required: %d\n"
+				  "Pointers Reported: %d\n",
+				  max_ptrs, ea_hdr->ea_num_ptrs);
 		}
 	}
 	return 0;
 }
 
-int check_eattr_extentry(struct gfs2_inode *ip, uint64_t *ea_ptr,
+static int check_eattr_extentry(struct gfs2_inode *ip, uint64_t *ea_ptr,
 			 struct gfs2_buffer_head *leaf_bh,
 			 struct gfs2_ea_header *ea_hdr,
 			 struct gfs2_ea_header *ea_hdr_prev,
@@ -214,7 +232,6 @@ int check_eattr_extentry(struct gfs2_inode *ip, uint64_t *ea_ptr,
 	struct gfs2_block_query q;
 	struct gfs2_sbd *sbp = ip->i_sbd;
 
-	*want_updated = not_updated;
 	if(gfs2_block_check(sbp, bl, be64_to_cpu(*ea_ptr), &q)) {
 		stack;
 		return -1;
@@ -252,18 +269,20 @@ int pass1c(struct gfs2_sbd *sbp)
 	osi_list_foreach_safe(tmp, &sbp->eattr_blocks.list, x) {
 		ea_block = osi_list_entry(tmp, struct special_blocks, list);
 		block_no = ea_block->block;
+		warm_fuzzy_stuff(block_no);
 
 		if (skip_this_pass || fsck_abort) /* if asked to skip the rest */
 			return FSCK_OK;
 		bh = bread(&sbp->buf_list, block_no);
-		if (gfs2_check_meta(bh, GFS2_METATYPE_IN)) { /* if a dinode */
+		if (!gfs2_check_meta(bh, GFS2_METATYPE_DI)) { /* if a dinode */
 			log_info("EA in inode %"PRIu64" (0x%" PRIx64 ")\n",
 				 block_no, block_no);
-			gfs2_block_clear(sbp, bl, block_no, gfs2_eattr_block);
+			gfs2_block_unmark(sbp, bl, block_no, gfs2_eattr_block);
 			ip = fsck_inode_get(sbp, bh);
 
-			log_debug("Found eattr at %"PRIu64" (0x%" PRIx64 ")\n",
-				  ip->i_di.di_eattr, ip->i_di.di_eattr);
+			log_debug("Found eattr at %llu (0x%llx)\n",
+				  (unsigned long long)ip->i_di.di_eattr,
+				  (unsigned long long)ip->i_di.di_eattr);
 			/* FIXME: Handle walking the eattr here */
 			error = check_inode_eattr(ip, &want_updated,
 						  &pass1c_fxns);
diff --git a/gfs2/fsck/pass2.c b/gfs2/fsck/pass2.c
index 307b6ff..c664226 100644
--- a/gfs2/fsck/pass2.c
+++ b/gfs2/fsck/pass2.c
@@ -105,10 +105,11 @@ static int check_eattr_leaf(struct gfs2_inode *ip, uint64_t block,
 
 const char *de_type_string(uint8_t de_type)
 {
-	const char *de_types[15] = {"unknown", "fifo", "chrdev", "invalid",
-								"directory", "invalid", "blkdev", "invalid",
-								"file", "invalid", "symlink", "invalid",
-								"socket", "invalid", "wht"};
+	const char *de_types[15] =
+		{"unknown", "fifo", "chrdev", "invalid",
+		 "directory", "invalid", "blkdev", "invalid",
+		 "file", "invalid", "symlink", "invalid",
+		 "socket", "invalid", "wht"};
 	if (de_type < 15)
 		return de_types[de_type];
 	return de_types[3]; /* invalid */
@@ -153,6 +154,14 @@ static int check_file_type(uint8_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 gfs2_inode *ip, struct gfs2_dirent *dent,
@@ -171,7 +180,6 @@ int check_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent,
 	struct gfs2_dirent dentry, *de;
 	uint32_t calculated_hash;
 
-	*update = not_updated;
 	memset(&dentry, 0, sizeof(struct gfs2_dirent));
 	gfs2_dirent_in(&dentry, (char *)dent);
 	de = &dentry;
@@ -253,27 +261,23 @@ int check_dentry(struct gfs2_inode *ip, struct gfs2_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);
 
 		errors_found++;
-		if(query(&opts, "Clear entry to inode containing bad blocks? (y/n)")) {
-
+		if(query(&opts, "Delete inode containing bad blocks? (y/n)")) {
 			errors_corrected++;
 			entry_ip = fsck_load_inode(sbp, de->de_inum.no_addr);
-			check_inode_eattr(entry_ip, update, &clear_eattrs);
-			fsck_inode_put(entry_ip, not_updated);
-
-			/* FIXME: make sure all blocks referenced by
-			 * this inode are cleared in the bitmap */
-
+			check_inode_eattr(entry_ip, update,
+					  &pass2_fxns_delete);
+			check_metatree(entry_ip, &pass2_fxns_delete);
+			fsck_inode_put(entry_ip, updated);
 			dirent2_del(ip, bh, prev_de, dent);
-
 			gfs2_block_set(sbp, bl, de->de_inum.no_addr,
-				       gfs2_meta_inval);
+				       gfs2_block_free);
 			*update = updated;
+			log_warn("The inode containing bad blocks was "
+				 "deleted.\n");
 			return 1;
 		} else {
 			log_warn("Entry to inode containing bad blocks remains\n");
@@ -287,21 +291,45 @@ int check_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent,
 	   q.block_type != gfs2_inode_lnk && q.block_type != gfs2_inode_blk &&
 	   q.block_type != gfs2_inode_chr && q.block_type != gfs2_inode_fifo &&
 	   q.block_type != gfs2_inode_sock) {
-		log_err("Directory entry '%s' at block %" PRIu64 " (0x%" PRIx64
-			") in dir inode %" PRIu64 " (0x%" PRIx64
-			") has an invalid block type: %d.\n", tmp_name,
+		log_err("Directory entry '%s' for block %" PRIu64
+			" (0x%" PRIx64 ") in dir inode %" PRIu64 " (0x%" PRIx64
+			") block type %d: %s.\n", tmp_name,
 			de->de_inum.no_addr, de->de_inum.no_addr,
 			ip->i_di.di_num.no_addr, ip->i_di.di_num.no_addr,
-			q.block_type);
+			q.block_type, q.block_type == gfs2_meta_inval ?
+			"previously marked invalid" : "is not an inode");
 
 		errors_found++;
-		if(query(&opts, "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(&opts, "Clear directory entry to non-inode block? "
+			 "(y/n) ")) {
+			struct gfs2_buffer_head *bhi;
+
 			errors_corrected++;
 			dirent2_del(ip, bh, prev_de, dent);
 			*update = updated;
 			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 != gfs2_meta_inval)
+				return 1;
+
+			/* Now try to clear the dinode, if it is an dinode */
+			bhi = bread(&sbp->buf_list, de->de_inum.no_addr);
+			error = gfs2_check_meta(bhi, GFS2_METATYPE_DI);
+			brelse(bhi, updated);
+			if (error)
+				return 1; /* not a dinode: nothing to delete */
+
+			entry_ip = fsck_load_inode(sbp, de->de_inum.no_addr);
+			check_inode_eattr(entry_ip, update,
+					  &pass2_fxns_delete);
+			check_metatree(entry_ip, &pass2_fxns_delete);
+			fsck_inode_put(entry_ip, updated);
+			gfs2_block_set(sbp, bl, de->de_inum.no_addr,
+				       gfs2_block_free);
+
 			return 1;
 		} else {
 			log_err("Directory entry to non-inode block remains\n");
@@ -331,6 +359,7 @@ int check_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent,
 
 			dirent2_del(ip, bh, prev_de, dent);
 			*update = updated;
+			log_err("Stale directory entry deleted\n");
 			return 1;
 		} else {
 			log_err("Stale directory entry remains\n");
@@ -376,12 +405,16 @@ int check_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent,
 
 		/* check that '.' refers to this inode */
 		if(de->de_inum.no_addr != ip->i_di.di_num.no_addr) {
-			log_err("'.' entry's value incorrect in directory %" PRIu64
-					" (0x%" PRIx64 ").  Points to %"PRIu64
-					" (0x%" PRIx64 ") when it should point to %" PRIu64
-					" (0x%" PRIx64 ").\n",
-					de->de_inum.no_addr, de->de_inum.no_addr,
-					ip->i_di.di_num.no_addr, ip->i_di.di_num.no_addr);
+			log_err("'.' entry's value incorrect in directory %llu"
+				" (0x%llx).  Points to %llu"
+				" (0x%llx) when it should point to %llu"
+				" (0x%llx).\n",
+				(unsigned long long)de->de_inum.no_addr,
+				(unsigned long long)de->de_inum.no_addr,
+				(unsigned long long)de->de_inum.no_addr,
+				(unsigned long long)de->de_inum.no_addr,
+				(unsigned long long)ip->i_di.di_num.no_addr,
+				(unsigned long long)ip->i_di.di_num.no_addr);
 			errors_found++;
 			if(query(&opts, "Remove '.' reference? (y/n) ")) {
 				errors_corrected++;
@@ -587,27 +620,33 @@ int check_system_dir(struct gfs2_inode *sysinode, const char *dirname,
 	}
 	if(!ds.dotdir) {
 		log_err("No '.' entry found for %s directory.\n", dirname);
-		sprintf(tmp_name, ".");
-		filename_len = strlen(tmp_name);  /* no trailing NULL */
-		if(!(filename = malloc(sizeof(char) * filename_len))) {
-			log_err("Unable to allocate name string\n");
-			stack;
-			return -1;
-		}
-		if(!(memset(filename, 0, sizeof(char) * filename_len))) {
-			log_err("Unable to zero name string\n");
-			stack;
-			return -1;
-		}
-		memcpy(filename, tmp_name, filename_len);
-		log_warn("Adding '.' entry\n");
-		dir_add(sysinode, filename, filename_len,
+		errors_found++;
+		if (query(&opts, "Is it okay to add '.' entry? (y/n) ")) {
+			errors_corrected++;
+			sprintf(tmp_name, ".");
+			filename_len = strlen(tmp_name); /* no trailing NULL */
+			if(!(filename = malloc(sizeof(char) * filename_len))) {
+				log_err("Unable to allocate name string\n");
+				stack;
+				return -1;
+			}
+			if(!(memset(filename, 0, sizeof(char) *
+				    filename_len))) {
+				log_err("Unable to zero name string\n");
+				stack;
+				return -1;
+			}
+			memcpy(filename, tmp_name, filename_len);
+			log_warn("Adding '.' entry\n");
+			dir_add(sysinode, filename, filename_len,
 				&(sysinode->i_di.di_num), DT_DIR);
-		increment_link(sysinode->i_sbd,
-					   sysinode->i_di.di_num.no_addr);
-		ds.entry_count++;
-		free(filename);
-		update = 1;
+			increment_link(sysinode->i_sbd,
+				       sysinode->i_di.di_num.no_addr);
+			ds.entry_count++;
+			free(filename);
+			update = 1;
+		} else
+			log_err("The directory was not fixed.\n");
 	}
 	if(sysinode->i_di.di_entries != ds.entry_count) {
 		log_err("%s inode %" PRIu64 " (0x%" PRIx64
@@ -666,11 +705,13 @@ int pass2(struct gfs2_sbd *sbp)
 	struct gfs2_block_query q;
 	struct dir_status ds = {0};
 	struct gfs2_inode *ip;
-	struct gfs2_buffer_head b, *bh = &b;
+	struct gfs2_buffer_head *bh = NULL;
 	char *filename;
 	int filename_len;
 	char tmp_name[256];
 	int error = 0;
+	enum update_flags need_update = NOT_UPDATED;
+	struct dup_blocks *b;
 
 	/* Check all the system directory inodes. */
 	if (check_system_dir(sbp->md.jiinode, "jindex", build_jindex)) {
@@ -692,6 +733,7 @@ int pass2(struct gfs2_sbd *sbp)
 	log_info("Checking directory inodes.\n");
 	/* Grab each directory inode, and run checks on it */
 	for(i = 0; i < last_fs_block; i++) {
+		need_update = 0;
 		warm_fuzzy_stuff(i);
 		if (skip_this_pass || fsck_abort) /* if asked to skip the rest */
 			return FSCK_OK;
@@ -719,8 +761,8 @@ int pass2(struct gfs2_sbd *sbp)
 			 * is valid */
 			ip = fsck_load_inode(sbp, i);
 			if(check_metatree(ip, &pass2_fxns)) {
+				fsck_inode_put(ip, not_updated);
 				stack;
-				free(ip);
 				return FSCK_ERROR;
 			}
 			fsck_inode_put(ip, not_updated);
@@ -766,41 +808,73 @@ int pass2(struct gfs2_sbd *sbp)
 		bh = bread(&sbp->buf_list, i);
 		ip = fsck_inode_get(sbp, bh);
 		if(!ds.dotdir) {
-			log_err("No '.' entry found\n");
-			sprintf(tmp_name, ".");
-			filename_len = strlen(tmp_name);  /* no trailing NULL */
-			if(!(filename = malloc(sizeof(char) * filename_len))) {
-				log_err("Unable to allocate name string\n");
-				stack;
-				return FSCK_ERROR;
-			}
-			if(!memset(filename, 0, sizeof(char) * filename_len)) {
-				log_err("Unable to zero name string\n");
-				stack;
-				return FSCK_ERROR;
-			}
-			memcpy(filename, tmp_name, filename_len);
-
-			dir_add(ip, filename, filename_len, &(ip->i_di.di_num), DT_DIR);
-			increment_link(ip->i_sbd, ip->i_di.di_num.no_addr);
-			ds.entry_count++;
-			free(filename);
+			log_err("No '.' entry found for directory inode at "
+				"block %"PRIu64" (0x%" PRIx64 ")\n", i, i);
 
+			errors_found++;
+			if (query(&opts,
+				  "Is it okay to add '.' entry? (y/n) ")) {
+				errors_corrected++;
+				sprintf(tmp_name, ".");
+				filename_len = strlen(tmp_name); /* no trailing
+								    NULL */
+				if(!(filename = malloc(sizeof(char) *
+						       filename_len))) {
+					log_err("Unable to allocate name\n");
+					stack;
+					return FSCK_ERROR;
+				}
+				if(!memset(filename, 0, sizeof(char) *
+					   filename_len)) {
+					log_err("Unable to zero name\n");
+					stack;
+					return FSCK_ERROR;
+				}
+				memcpy(filename, tmp_name, filename_len);
+
+				dir_add(ip, filename, filename_len,
+					&(ip->i_di.di_num), DT_DIR);
+				increment_link(ip->i_sbd,
+					       ip->i_di.di_num.no_addr);
+				ds.entry_count++;
+				free(filename);
+				log_err("The directory was fixed.\n");
+				need_update = UPDATED;
+			} else {
+				log_err("The directory was not fixed.\n");
+			}
 		}
-		fsck_inode_put(ip, not_updated); /* does a brelse */
 
-		bh = bread(&sbp->buf_list, i);
-		ip = fsck_inode_get(sbp, bh);
 		if(ip->i_di.di_entries != ds.entry_count) {
-			log_err("Entries is %d - should be %d for inode block %" PRIu64
-					" (0x%" PRIx64 ")\n",
-					ip->i_di.di_entries, ds.entry_count,
-					ip->i_di.di_num.no_addr, ip->i_di.di_num.no_addr);
-			ip->i_di.di_entries = ds.entry_count;
-			fsck_inode_put(ip, updated); /* does a gfs2_dinode_out, brelse */
+			log_err("Entries is %d - should be %d for inode "
+				"block %" PRIu64 " (0x%" PRIx64 ")\n",
+				ip->i_di.di_entries, ds.entry_count,
+				ip->i_di.di_num.no_addr,
+				ip->i_di.di_num.no_addr);
+			errors_found++;
+			if (query(&opts,
+				  "Fix the entry count? (y/n) ")) {
+				errors_corrected++;
+				ip->i_di.di_entries = ds.entry_count;
+				need_update = UPDATED;
+			} else {
+				log_err("The entry count was not fixed.\n");
+			}
 		}
-		else
-			fsck_inode_put(ip, not_updated); /* does a brelse */
+		fsck_inode_put(ip, need_update); /* does a gfs2_dinode_out,
+						    brelse */
+	}
+	/* Now that we've deleted the inodes marked "bad" we can safely
+	   get rid of the duplicate block list.  If we do it any sooner,
+	   we won't discover that a given block is a duplicate and avoid
+	   deleting it from both inodes referencing it. Note: The other
+	   returns from this function are premature exits of the program
+	   and gfs2_block_list_destroy should get rid of the list for us. */
+	while (!osi_list_empty(&sbp->dup_blocks.list)) {
+		b = osi_list_entry(sbp->dup_blocks.list.next,
+				   struct dup_blocks, list);
+		osi_list_del(&b->list);
+		free(b);
 	}
 	return FSCK_OK;
 }
diff --git a/gfs2/fsck/pass3.c b/gfs2/fsck/pass3.c
index b61cc8d..a78d81e 100644
--- a/gfs2/fsck/pass3.c
+++ b/gfs2/fsck/pass3.c
@@ -283,10 +283,11 @@ int pass3(struct gfs2_sbd *sbp)
 						return FSCK_ERROR;
 					}
 					log_warn("Directory relinked to lost+found\n");
+					fsck_inode_put(ip, updated);
 				} else {
 					log_err("Unlinked directory remains unlinked\n");
+					fsck_inode_put(ip, not_updated);
 				}
-				fsck_inode_put(ip, not_updated);
 				break;
 			}
 			else {
diff --git a/gfs2/fsck/pass4.c b/gfs2/fsck/pass4.c
index ce0e562..a06647b 100644
--- a/gfs2/fsck/pass4.c
+++ b/gfs2/fsck/pass4.c
@@ -19,6 +19,15 @@
 #include "fsck.h"
 #include "lost_n_found.h"
 #include "inode_hash.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 */
@@ -65,11 +74,19 @@ int scan_inode_list(struct gfs2_sbd *sbp, osi_list_t *list) {
 				return -1;
 			}
 			if(q.bad_block) {
-				log_err("Unlinked inode contains bad blocks\n", ii->inode);
+				log_err("Unlinked inode %llu (0x%llx) contains"
+					"bad blocks\n",
+					(unsigned long long)ii->inode,
+					(unsigned long long)ii->inode);
 				errors_found++;
-				if(query(&opts,
-						 "Clear unlinked inode with bad blocks? (y/n) ")) {
+				if(query(&opts, "Delete unlinked inode with "
+					 "bad blocks? (y/n) ")) {
 					errors_corrected++;
+					ip = fsck_load_inode(sbp, ii->inode);
+					check_inode_eattr(ip, &f,
+							  &pass4_fxns_delete);
+					check_metatree(ip, &pass4_fxns_delete);
+					fsck_inode_put(ip, updated);
 					gfs2_block_set(sbp, bl, ii->inode,
 						       gfs2_block_free);
 					continue;
@@ -83,10 +100,22 @@ int scan_inode_list(struct gfs2_sbd *sbp, osi_list_t *list) {
 			   q.block_type != gfs2_inode_chr &&
 			   q.block_type != gfs2_inode_fifo &&
 			   q.block_type != gfs2_inode_sock) {
-				log_err("Unlinked block marked as inode not an inode\n");
-				gfs2_block_set(sbp, bl, ii->inode,
-					       gfs2_block_free);
-				log_err("Cleared\n");
+				log_err("Unlinked block marked as inode is "
+					"not an inode (%d)\n", q.block_type);
+				ip = fsck_load_inode(sbp, ii->inode);
+				if(query(&opts, "Delete unlinked inode "
+					 "? (y/n) ")) {
+					check_inode_eattr(ip, &f,
+							  &pass4_fxns_delete);
+					check_metatree(ip, &pass4_fxns_delete);
+					fsck_inode_put(ip, updated);
+					gfs2_block_set(sbp, bl, ii->inode,
+						       gfs2_block_free);
+					log_err("The inode was deleted\n");
+				} else {
+					log_err("The inode was not deleted\n");
+					fsck_inode_put(ip, not_updated);
+				}
 				continue;
 			}
 			ip = fsck_load_inode(sbp, ii->inode);
diff --git a/gfs2/fsck/pass5.c b/gfs2/fsck/pass5.c
index 27f7251..ec89adf 100644
--- a/gfs2/fsck/pass5.c
+++ b/gfs2/fsck/pass5.c
@@ -72,6 +72,7 @@ int check_block_status(struct gfs2_sbd *sbp, char *buffer, unsigned int buflen,
 	unsigned char rg_status, block_status;
 	struct gfs2_block_query q;
 	uint64_t block;
+	static int free_unlinked = -1;
 
 	/* FIXME verify cast */
 	byte = (unsigned char *) buffer;
@@ -95,19 +96,51 @@ int check_block_status(struct gfs2_sbd *sbp, char *buffer, unsigned int buflen,
 		   So we ignore it. */
 		if (rg_status == GFS2_BLKST_UNLINKED &&
 		    block_status == GFS2_BLKST_FREE) {
-			log_warn("Unlinked block found at block %"
-				 PRIu64" (0x%" PRIx64 "), left unchanged.\n",
-				 block, block);
+			errors_found++;
+			if (free_unlinked == -1) {
+				log_err("Unlinked inode block found at block %"
+					"llu (0x%llx).\n",
+					(unsigned long long)block,
+					(unsigned long long)block);
+				if(query(&opts, "Do you want me to fix the "
+					 "bitmap for all unlinked blocks? "
+					 "(y/n) "))
+					free_unlinked = 1;
+				else
+					free_unlinked = 0;
+			}
+			if (free_unlinked) {
+				if(gfs2_set_bitmap(sbp, block, block_status))
+					log_err("Unlinked block %llu (0x%llx)"
+						" bitmap not fixed.\n",
+						(unsigned long long)block,
+						(unsigned long long)block);
+				else {
+					log_err("Unlinked block %llu (0x%llx)"
+						" bitmap fixed.\n",
+						(unsigned long long)block,
+						(unsigned long long)block);
+					errors_corrected++;
+				}
+			} else {
+				log_info("Unlinked block found at block %"
+					 PRIu64" (0x%" PRIx64 "), left "
+					 "unchanged.\n", block, block);
+			}
 		} else if (rg_status != block_status) {
-			const char *blockstatus[] = {"Free", "Data", "Unlinked", "inode"};
+			const char *blockstatus[] = {"Free", "Data",
+						     "Unlinked", "inode"};
 
 			log_err("Ondisk and fsck bitmaps differ at"
-					" block %"PRIu64" (0x%" PRIx64 ") \n", block, 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]);
+				" block %"PRIu64" (0x%" PRIx64 ") \n",
+				block, 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));
+				block_type_string(&q));
 
 			errors_found++;
 			if(query(&opts, "Fix bitmap for block %"
diff --git a/gfs2/fsck/rgrepair.c b/gfs2/fsck/rgrepair.c
index e11b72b..e8429a6 100644
--- a/gfs2/fsck/rgrepair.c
+++ b/gfs2/fsck/rgrepair.c
@@ -429,7 +429,7 @@ int rg_repair(struct gfs2_sbd *sdp, int trust_lvl, int *rg_count)
 {
 	int error, descrepencies;
 	osi_list_t expected_rglist;
-	int calc_rg_count, rgcount_from_index, rg;
+	int calc_rg_count = 0, rgcount_from_index, rg;
 	osi_list_t *exp, *act; /* expected, actual */
 	struct gfs2_rindex buf;
 
diff --git a/gfs2/libgfs2/block_list.c b/gfs2/libgfs2/block_list.c
index 67de5e1..9fc3ac5 100644
--- a/gfs2/libgfs2/block_list.c
+++ b/gfs2/libgfs2/block_list.c
@@ -174,12 +174,13 @@ int gfs2_block_mark(struct gfs2_sbd *sdp, struct gfs2_block_list *il,
 	return err;
 }
 
-int gfs2_block_clear(struct gfs2_sbd *sdp, struct gfs2_block_list *il,
-		     uint64_t block, enum gfs2_mark_block m)
+/* gfs2_block_unmark clears ONE mark for the given block */
+int gfs2_block_unmark(struct gfs2_sbd *sdp, struct gfs2_block_list *il,
+		      uint64_t block, enum gfs2_mark_block mark)
 {
 	int err = 0;
 
-	switch (m) {
+	switch (mark) {
 	case gfs2_dup_block:
 		gfs2_dup_clear(&sdp->dup_blocks, block);
 		break;
@@ -197,12 +198,25 @@ int gfs2_block_clear(struct gfs2_sbd *sdp, struct gfs2_block_list *il,
 	return err;
 }
 
+/* gfs2_block_clear clears all the marks for the given block */
+int gfs2_block_clear(struct gfs2_sbd *sdp, struct gfs2_block_list *il,
+		     uint64_t block)
+{
+	int err = 0;
+
+	gfs2_dup_clear(&sdp->dup_blocks, block);
+	gfs2_special_clear(&sdp->bad_blocks, block);
+	gfs2_special_clear(&sdp->eattr_blocks, block);
+	err = gfs2_bitmap_clear(&il->list.gbmap.group_map, block);
+	return err;
+}
+
 int gfs2_block_set(struct gfs2_sbd *sdp, struct gfs2_block_list *il,
 		   uint64_t block, enum gfs2_mark_block mark)
 {
 	int err;
 
-	err = gfs2_block_clear(sdp, il, block, mark);
+	err = gfs2_block_clear(sdp, il, block); /* clear all block status */
 	if(!err)
 		err = gfs2_block_mark(sdp, il, block, mark);
 	return err;
@@ -216,15 +230,15 @@ int gfs2_block_check(struct gfs2_sbd *sdp, struct gfs2_block_list *il,
 	val->bad_block = 0;
 	val->dup_block = 0;
 	val->eattr_block = 0;
-	if((err = gfs2_bitmap_get(&il->list.gbmap.group_map, block,
-				  &val->block_type)))
-		return err;
 	if (blockfind(&sdp->bad_blocks, block))
 		val->bad_block = 1;
 	if (dupfind(&sdp->dup_blocks, block))
 		val->dup_block = 1;
 	if (blockfind(&sdp->eattr_blocks, block))
 		val->eattr_block = 1;
+	if((err = gfs2_bitmap_get(&il->list.gbmap.group_map, block,
+				  &val->block_type)))
+		return err;
 	return 0;
 }
 
diff --git a/gfs2/libgfs2/fs_bits.c b/gfs2/libgfs2/fs_bits.c
index 4cd41b4..b295261 100644
--- a/gfs2/libgfs2/fs_bits.c
+++ b/gfs2/libgfs2/fs_bits.c
@@ -201,3 +201,64 @@ int gfs2_set_bitmap(struct gfs2_sbd *sdp, uint64_t blkno, int state)
 	gfs2_rgrp_relse(rgd, updated);
 	return 0;
 }
+
+/*
+ * fs_get_bitmap - get value of FS bitmap
+ * @sdp: super block
+ * @blkno: block number relative to file system
+ *
+ * This function gets the value of a bit of the
+ * file system bitmap.
+ * Possible state values for a block in the bitmap are:
+ *  GFS_BLKST_FREE     (0)
+ *  GFS_BLKST_USED     (1)
+ *  GFS_BLKST_INVALID  (2)
+ *  GFS_BLKST_DINODE   (3)
+ *
+ * Returns: state on success, -1 on error
+ */
+int gfs2_get_bitmap(struct gfs2_sbd *sdp, uint64_t blkno,
+		    struct rgrp_list *rgd)
+{
+	int           buf, val;
+	uint32_t        rgrp_block;
+	struct gfs2_bitmap	*bits = NULL;
+	unsigned int  bit;
+	unsigned char *byte;
+	int local_rgd = 0;
+
+	if(gfs2_check_range(sdp, blkno))
+		return -1;
+	if(rgd == NULL) {
+		local_rgd = 1;
+		rgd = gfs2_blk2rgrpd(sdp, blkno);
+	}
+	if(rgd == NULL)
+		return -1;
+	if(gfs2_rgrp_read(sdp, rgd))
+		return -1;
+
+	rgrp_block = (uint32_t)(blkno - rgd->ri.ri_data0);
+
+	for(buf= 0; buf < rgd->ri.ri_length; buf++){
+		bits = &(rgd->bits[buf]);
+		if(rgrp_block < ((bits->bi_start + bits->bi_len)*GFS2_NBBY)){
+			break;
+		}
+	}
+
+	if(buf >= rgd->ri.ri_length){
+		gfs2_rgrp_relse(rgd, not_updated);
+		return -1;
+	}
+
+	byte = (unsigned char *)(rgd->bh[buf]->b_data + bits->bi_offset) +
+		(rgrp_block/GFS2_NBBY - bits->bi_start);
+	bit = (rgrp_block % GFS2_NBBY) * GFS2_BIT_SIZE;
+
+	val = ((*byte >> bit) & GFS2_BIT_MASK);
+	if(local_rgd)
+		gfs2_rgrp_relse(rgd, not_updated);
+
+	return val;
+}
diff --git a/gfs2/libgfs2/fs_ops.c b/gfs2/libgfs2/fs_ops.c
index 3ccc9c8..9cc6dea 100644
--- a/gfs2/libgfs2/fs_ops.c
+++ b/gfs2/libgfs2/fs_ops.c
@@ -695,6 +695,8 @@ void dirent2_del(struct gfs2_inode *dip, struct gfs2_buffer_head *bh,
 {
 	uint16_t cur_rec_len, prev_rec_len;
 
+	if (dip->i_di.di_entries)
+		dip->i_di.di_entries--;
 	if (!prev) {
 		cur->de_inum.no_formal_ino = 0;
 		return;
diff --git a/gfs2/libgfs2/libgfs2.h b/gfs2/libgfs2/libgfs2.h
index a458feb..64d36d0 100644
--- a/gfs2/libgfs2/libgfs2.h
+++ b/gfs2/libgfs2/libgfs2.h
@@ -387,8 +387,12 @@ int gfs2_block_mark(struct gfs2_sbd *sdp, struct gfs2_block_list *il,
 		    uint64_t block, enum gfs2_mark_block mark);
 int gfs2_block_set(struct gfs2_sbd *sdp, struct gfs2_block_list *il,
 		   uint64_t block, enum gfs2_mark_block mark);
+/* gfs2_block_unmark clears ONE mark for the given block */
+int gfs2_block_unmark(struct gfs2_sbd *sdp, struct gfs2_block_list *il,
+		      uint64_t block, enum gfs2_mark_block m);
+/* gfs2_block_clear clears all the marks for the given block */
 int gfs2_block_clear(struct gfs2_sbd *sdp, struct gfs2_block_list *il,
-		     uint64_t block, enum gfs2_mark_block m);
+		     uint64_t block);
 int gfs2_block_check(struct gfs2_sbd *sdp, struct gfs2_block_list *il,
 		     uint64_t block, struct gfs2_block_query *val);
 void *gfs2_block_list_destroy(struct gfs2_sbd *sdp,
@@ -429,7 +433,7 @@ int gfs2_check_range(struct gfs2_sbd *sdp, uint64_t blkno);
 
 /* functions with blk #'s that are file system relative */
 int gfs2_get_bitmap(struct gfs2_sbd *sdp, uint64_t blkno,
-                                        struct rgrp_list *rgd);
+		    struct rgrp_list *rgd);
 int gfs2_set_bitmap(struct gfs2_sbd *sdp, uint64_t blkno, int state);
 
 /* fs_geometry.c */


                 reply	other threads:[~2009-08-10 16:54 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20090810165256.746DC1201F3@lists.fedorahosted.org \
    --to=rpeterso@fedoraproject.org \
    --cc=cluster-cvs-relay@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).