public inbox for cluster-cvs@sourceware.org
help / color / mirror / Atom feed
* gfs2-utils: master - GFS2: fsck.gfs2 sometimes needs to be run twice
@ 2009-08-10 16:34 Bob Peterson
  0 siblings, 0 replies; 2+ messages in thread
From: Bob Peterson @ 2009-08-10 16:34 UTC (permalink / raw)
  To: cluster-cvs-relay

Gitweb:        http://git.fedorahosted.org/git/gfs2-utils.git?p=gfs2-utils.git;a=commitdiff;h=67839817dab9251c937bf10d2f01bef101395154
Commit:        67839817dab9251c937bf10d2f01bef101395154
Parent:        15e280c0799675d86ec33dcf5839b9f758ba90f9
Author:        Bob Peterson <rpeterso@redhat.com>
AuthorDate:    Mon Aug 10 11:23:47 2009 -0500
Committer:     Bob Peterson <rpeterso@redhat.com>
CommitterDate: Mon Aug 10 11:35:31 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/fsck/main.c     |    6 +-
 gfs2/fsck/metawalk.c |  286 +++++++++++++++++++++++++++++++--------
 gfs2/fsck/metawalk.h |   15 ++-
 gfs2/fsck/pass1.c    |  373 +++++++++++++++++++++++++++++---------------------
 gfs2/fsck/pass1b.c   |   31 +++--
 gfs2/fsck/pass1c.c   |  215 ++++++++++-------------------
 gfs2/fsck/pass2.c    |   63 ++++-----
 gfs2/fsck/pass4.c    |   38 +++++-
 gfs2/fsck/pass5.c    |   39 +++++-
 9 files changed, 650 insertions(+), 416 deletions(-)

diff --git a/gfs2/fsck/main.c b/gfs2/fsck/main.c
index c4778ad..b5e865a 100644
--- a/gfs2/fsck/main.c
+++ b/gfs2/fsck/main.c
@@ -191,9 +191,9 @@ static 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 da89c93..e6f0a9b 100644
--- a/gfs2/fsck/metawalk.c
+++ b/gfs2/fsck/metawalk.c
@@ -98,6 +98,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.
+ */
 static int dirent_repair(struct gfs2_inode *ip, struct gfs2_buffer_head *bh,
 		  struct gfs2_dirent *de, struct gfs2_dirent *dent,
 		  int type, int first)
@@ -113,8 +124,7 @@ static 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);
@@ -137,8 +147,40 @@ static 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
+ */
 static int check_entries(struct gfs2_inode *ip, struct gfs2_buffer_head *bh,
-		  int eindex, int type, enum update_flags *update,
+		  int type, enum update_flags *update,
 		  uint16_t *count, struct metawalk_fxns *pass)
 {
 	struct gfs2_leaf *leaf = NULL;
@@ -177,7 +219,8 @@ static int check_entries(struct gfs2_inode *ip, struct gfs2_buffer_head *bh,
 		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 %llu (0x%llx"
 				"), entry %d of directory %llu"
 				"(0x%llx) is corrupt.\n"),
@@ -189,18 +232,29 @@ static 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){
@@ -216,7 +270,19 @@ static int check_entries(struct gfs2_inode *ip, struct gfs2_buffer_head *bh,
 					(unsigned long long)bh->b_blocknr,
 					(unsigned long long)ip->i_di.di_num.no_addr,
 					(unsigned long long)ip->i_di.di_num.no_addr);
-				return 1;
+				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 */
@@ -238,9 +304,6 @@ static int check_entries(struct gfs2_inode *ip, struct gfs2_buffer_head *bh,
 					stack;
 					return -1;
 				}
-				/*if(error > 0) {
-				  return 1;
-				  }*/
 			}
 		}
 
@@ -295,8 +358,8 @@ static void warn_and_patch(struct gfs2_inode *ip, uint64_t *leaf_no,
 }
 
 /* Checks exhash directory entries */
-static int check_leaf(struct gfs2_inode *ip, enum update_flags *update,
-	       struct metawalk_fxns *pass)
+static int check_leaf_blks(struct gfs2_inode *ip, enum update_flags *update,
+			   struct metawalk_fxns *pass)
 {
 	int error;
 	struct gfs2_leaf leaf, oldleaf;
@@ -346,8 +409,8 @@ static int check_leaf(struct gfs2_inode *ip, enum update_flags *update,
 			ref_count++;
 			continue;
 		}
-		if(gfs2_check_range(ip->i_sbd, old_leaf) == 0 &&
-		   ref_count != exp_count){
+		if (gfs2_check_range(ip->i_sbd, old_leaf) == 0 &&
+		    ref_count != exp_count) {
 			log_err( _("Dir #%llu (0x%llx) has an incorrect "
 				   "number of pointers to leaf #%llu "
 				   " (0x%llx)\n\tFound: %u,  Expected: "
@@ -452,9 +515,8 @@ static 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, lindex,
-						      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,
@@ -467,9 +529,6 @@ static 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;
 
@@ -501,7 +560,7 @@ static 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)
@@ -509,10 +568,10 @@ static 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;
 }
 
@@ -536,12 +595,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;
@@ -566,7 +631,6 @@ static int check_eattr_entries(struct gfs2_inode *ip,
 							      update_it,
 							      pass->private)) {
 					errors_found++;
-					error = 1;
 					if (query(&opts, _("Repair the bad "
 							 "Extended Attribute? "
 							   "(y/n) "))) {
@@ -584,9 +648,11 @@ static int check_eattr_entries(struct gfs2_inode *ip,
 							     gfs2_meta_eattr);
 						log_err( _("The EA was "
 							   "fixed.\n"));
-					} else
+					} else {
+						error = 1;
 						log_err( _("The bad EA was "
 							   "not fixed.\n"));
+					}
 				}
 				tot_ealen += sdp->sd_sb.sb_bsize -
 					sizeof(struct gfs2_meta_header);
@@ -620,23 +686,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;
 	}
@@ -653,12 +729,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"),
@@ -673,27 +753,69 @@ 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);
@@ -704,7 +826,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;
 }
@@ -720,9 +842,8 @@ 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 #%llu (0x%llx).\n"),
 		  (unsigned long long)ip->i_di.di_num.no_addr,
@@ -733,9 +854,10 @@ int check_inode_eattr(struct gfs2_inode *ip, enum update_flags *want_updated,
 						 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;
 	}
 
@@ -790,7 +912,7 @@ static int build_and_check_metalist(struct gfs2_inode *ip,
 			     (char *)ptr < (bh->b_data + ip->i_sbd->bsize);
 			     ptr++) {
 				nbh = NULL;
-		
+
 				if (!*ptr)
 					continue;
 
@@ -825,6 +947,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);
 		}
 	}
@@ -919,7 +1042,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)
@@ -937,7 +1060,7 @@ static 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;
@@ -958,7 +1081,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); */
@@ -1106,3 +1229,56 @@ 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 fd8f1b7..0721ed1 100644
--- a/gfs2/fsck/metawalk.h
+++ b/gfs2/fsck/metawalk.h
@@ -16,6 +16,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
  *
@@ -66,7 +78,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 6c3d3c6..18592ed 100644
--- a/gfs2/fsck/pass1.c
+++ b/gfs2/fsck/pass1.c
@@ -55,7 +55,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 = {
@@ -107,20 +108,21 @@ static int check_metalist(struct gfs2_inode *ip, uint64_t block,
 	if(q.block_type != gfs2_block_free) {
 		log_err( _("Found duplicate block referenced as metadata in "
 			   "indirect block - was marked %d\n"), q.block_type);
-		gfs2_block_set(ip->i_sbd, bl, block, gfs2_dup_block);
+		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;
 
 	if (!found_dup) {
@@ -140,15 +142,22 @@ static int check_data(struct gfs2_inode *ip, uint64_t block, void *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) {
@@ -156,6 +165,10 @@ static int check_data(struct gfs2_inode *ip, uint64_t block, void *private)
 			   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
@@ -212,6 +225,51 @@ static int check_data(struct gfs2_inode *ip, uint64_t block, void *private)
 	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;
+}
+
 /* clear_eas - clear the extended attributes for an inode
  *
  * @ip       - in core inode pointer
@@ -231,27 +289,22 @@ static int clear_eas(struct gfs2_inode *ip, struct block_count *bc,
 	log_err( _("Inode #%llu (0x%llx): %s"),
 		(unsigned long long)ip->i_di.di_num.no_addr,
 		(unsigned long long)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 Extended "
-						"Attribute? (y/n) ")))) {
-		if (block == 0)
-			block = ip->i_di.di_eattr;
-		if (!duplicate) {
+	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;
-		log_err( _("The bad Extended Attribute was removed.\n"));
 		return 1;
 	} else {
 		log_err( _("The bad Extended Attribute was not fixed.\n"));
@@ -294,8 +347,8 @@ static int check_eattr_indir(struct gfs2_inode *ip, uint64_t indirect,
 			if (!clear_eas(ip, bc, indirect, 1, want_updated,
 				       _("Bad indirect Extended Attribute "
 					 "duplicate found"))) {
-				gfs2_block_set(sdp, bl, indirect,
-					       gfs2_dup_block);
+				gfs2_block_mark(sdp, bl, indirect,
+						gfs2_dup_block);
 				bc->ea_count++;
 			}
 			return 1;
@@ -313,7 +366,7 @@ static int check_eattr_indir(struct gfs2_inode *ip, uint64_t indirect,
 			 (unsigned long long)ip->i_di.di_num.no_addr,
 			 (unsigned long long)indirect,
 			 (unsigned long long)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 {
@@ -326,21 +379,99 @@ static int check_eattr_indir(struct gfs2_inode *ip, uint64_t indirect,
 	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 #%llu (0x%llx) with eattr 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);
+	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 #%llu (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;
+	}
+	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;
 	}
-	clear_eas(ip, (struct block_count *)private, 0, 0, want_updated,
-		  _("has unrecoverable indirect Extended Attribute errors"));
+	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;
 }
 
@@ -362,16 +493,14 @@ 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 #%llu (0x%llx): Extended Attribute block "
-			   "%llu (0x%llx) has an extemded leaf block #%llu "
+			   "%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,
@@ -382,46 +511,11 @@ static int check_extended_leaf_eattr(struct gfs2_inode *ip, uint64_t *data_ptr,
 		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 Extended Attribute "
-				    "duplicate"));
-		else
-			clear_eas(ip, bc, el_blk, 0, want_updated,
-				  _("Extended Attribute 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,
@@ -429,75 +523,28 @@ 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;
 
-	/* Figure out the parent's block type */
-	gfs2_block_check(sdp, bl, parent, &q);
 	/* 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*/
-	    q.block_type != gfs2_indir_blk) { /* parent isn't indirect block */
-		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)){
+	 * 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(s) attached.\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 Extended Attribute duplicate "
-					    "found"));
-			} else {
-				clear_eas(ip, bc, block, 0, want_updated,
-					  _("Extended Attribute 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,
@@ -579,7 +626,9 @@ static 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);
+
+	log_crit( _("Clearing leaf #%" PRIu64 " (0x%" PRIx64 ")\n"),
+		  block, block);
 
 	if(gfs2_block_check(ip->i_sbd, bl, block, &q)) {
 		stack;
@@ -595,7 +644,6 @@ static 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)
@@ -651,9 +699,10 @@ static int handle_di(struct gfs2_sbd *sdp, struct gfs2_buffer_head *bh,
 			(unsigned long long)ip->i_di.di_num.no_addr,
 			(unsigned long long)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;
@@ -809,7 +858,11 @@ static 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)) {
@@ -820,9 +873,15 @@ static int handle_di(struct gfs2_sbd *sdp, struct gfs2_buffer_head *bh,
 			(unsigned long long)ip->i_di.di_blocks,
 			(unsigned long long)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"),
+			  (unsigned long long)ip->i_di.di_blocks,
+			  (unsigned long long)bc.indir_count,
+			  (unsigned long long)bc.data_count,
+			  (unsigned long long)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);
@@ -873,12 +932,12 @@ static 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 c0181c1..2372dc8 100644
--- a/gfs2/fsck/pass1b.c
+++ b/gfs2/fsck/pass1b.c
@@ -168,7 +168,7 @@ static int clear_dup_metalist(struct gfs2_inode *ip, uint64_t block,
 		return 1;
 	if(block == dh->b->block_no) {
 		log_err( _("Found duplicate reference in inode \"%s\" at "
-			   "block #%llu (0x%llx) with block #%llu (0x%llx)\n"),
+			   "block #%llu (0x%llx) to block #%llu (0x%llx)\n"),
 			 dh->id->name ? dh->id->name : _("unknown name"),
 			 (unsigned long long)ip->i_di.di_num.no_addr,
 			 (unsigned long long)ip->i_di.di_num.no_addr,
@@ -428,9 +428,11 @@ static int handle_dup_blk(struct gfs2_sbd *sbp, struct dup_blocks *b)
 				  (unsigned long long)b->block_no,
 				  (unsigned long long)b->block_no);
 			errors_found++;
-			if ((errors_corrected +=
-			     query(&opts, _("Clear the inode? (y/n) ")))) {
-				log_warn( _("Clearing...\n"));
+			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);
@@ -461,13 +463,21 @@ static int handle_dup_blk(struct gfs2_sbd *sbp, struct dup_blocks *b)
 			  (unsigned long long)id->block_no,
 			  id->dup_count, (unsigned long long)b->block_no,
 			  (unsigned long long)b->block_no);
+	}
+	osi_list_foreach(tmp, &b->ref_inode_list) {
+		id = osi_list_entry(tmp, struct inode_with_dups, list);
 		errors_found++;
-		if (!(errors_corrected +=
-		      query(&opts, _("Clear the inode? (y/n) ")))) {
+		if (!(query(&opts, _("Okay to clear inode %lld (0x%llx)? "
+				     "(y/n) "),
+				     (unsigned long long)id->block_no,
+				     (unsigned long long)id->block_no))) {
 			log_warn( _("The bad inode was not cleared...\n"));
 			continue;
 		}
-		log_warn( _("Clearing...\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);
 		dh.b = b;
 		dh.id = id;
@@ -556,13 +566,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 8e2d01a..1d4d5ef 100644
--- a/gfs2/fsck/pass1c.c
+++ b/gfs2/fsck/pass1c.c
@@ -17,32 +17,72 @@ static int remove_eattr_entry(struct gfs2_sbd *sdp,
 			      struct gfs2_ea_header *curr,
 			      struct gfs2_ea_header *prev)
 {
-	log_err( _("Bad Extended Attribute at block #%"PRIu64
-		   " (0x%" PRIx64 ") removed.\n"),
-		 leaf_bh->b_blocknr, leaf_bh->b_blocknr);
-	if(!prev)
+	if (!prev)
 		curr->ea_type = GFS2_EATYPE_UNUSED;
 	else {
-		prev->ea_rec_len =
-			be32_to_cpu(curr->ea_rec_len);
+		uint32_t tmp32 = be32_to_cpu(curr->ea_rec_len) +
 			be32_to_cpu(prev->ea_rec_len);
-			cpu_to_be32(curr->ea_rec_len + 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;
 }
 
+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 #%llu"
 			" (0x%llx) for inode #%llu"
@@ -51,16 +91,7 @@ static int check_eattr_indir(struct gfs2_inode *ip, uint64_t 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);
-		errors_found++;
-		if ((errors_corrected +=
-		     query(&opts, _("Remove the bad Extended Attribute? "
-				    "(y/n) ")))) {
-			ip->i_di.di_eattr = 0;
-			*update = updated;
-			log_err( _("Bad Extended Attribute removed.\n"));
-		} else
-			log_err( _("Bad Extended Attribute not removed.\n"));
-		return 1;
+		return ask_remove_eattr(ip, need_update);
 	}
 	else if (gfs2_block_check(sbp, bl, block, &q)) {
 		stack;
@@ -74,17 +105,7 @@ static int check_eattr_indir(struct gfs2_inode *ip, uint64_t 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);
-		errors_found++;
-		if ((errors_corrected +=
-		     query(&opts, _("Remove the bad Extended Attribute? "
-				    "(y/n) ")))) {
-			ip->i_di.di_eattr = 0;
-			*update = updated;
-			log_err( _("Bad Extended Attribute removed.\n"));
-		} else
-			log_err( _("Bad Extended Attribute not removed.\n"));
-
-		return 1;
+		return ask_remove_eattr(ip, need_update);
 	}
 	else
 		indir_bh = bread(&sbp->buf_list, block);
@@ -95,28 +116,17 @@ static int check_eattr_indir(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 #%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);
-		errors_found++;
-		if ((errors_corrected +=
-		     query(&opts, _("Remove the bad Extended Attribute? "
-				    "(y/n) ")))) {
-			ip->i_di.di_eattr = 0;
-			*update = updated;
-			log_err( _("Bad Extended Attribute removed.\n"));
-		} else
-			log_err( _("Bad Extended Attribute not removed.\n"));
-
-		return 1;
+		return ask_remove_eattr(ip, need_update);
 	}
 	else if (gfs2_block_check(sbp, bl, block, &q)) {
 		stack;
@@ -127,17 +137,7 @@ static int check_eattr_leaf(struct gfs2_inode *ip, uint64_t block,
 			   " (0x%llx) invalid.\n"),
 			 (unsigned long long)ip->i_di.di_num.no_addr,
 			 (unsigned long long)ip->i_di.di_num.no_addr);
-		errors_found++;
-		if ((errors_corrected +=
-		     query(&opts, _("Remove the bad Extended Attribute? "
-				    "(y/n) ")))) {
-			ip->i_di.di_eattr = 0;
-			*update = updated;
-			log_err( _("Bad Extended Attribute removed.\n"));
-		} else
-			log_err( _("Bad Extended Attribute not removed.\n"));
-
-		return 1;
+		return ask_remove_eattr(ip, need_update);
 	}
 	else 
 		*bh = bread(&sbp->buf_list, block);
@@ -159,73 +159,24 @@ static int check_eattr_entry(struct gfs2_inode *ip,
 
 	if(!ea_hdr->ea_name_len){
 		log_err( _("EA has name length == 0\n"));
-		errors_found++;
-		if ((errors_corrected +=
-		     query(&opts, _("Remove the bad Extended Attribute? "
-				    "(y/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;
-			}
-		} else
-			log_err( _("Bad Extended Attribute not removed.\n"));
-
-		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"));
-		errors_found++;
-		if ((errors_corrected +=
-		     query(&opts, _("Remove the bad Extended Attribute? "
-				    "(y/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;
-			}
-		} else
-			log_err( _("Bad Extended Attribute not removed.\n"));
-
-		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"));
-		errors_found++;
-		if ((errors_corrected +=
-		     query(&opts, _("Remove the bad Extended Attribute? "
-				    "(y/n) ")))) {
-			ea_hdr->ea_flags |= GFS2_EAFLAG_LAST;
-			if(remove_eattr_entry(sdp, leaf_bh, ea_hdr,
-					      ea_hdr_prev)){
-				stack;
-				return -1;
-			}
-		} else
-			log_err( _("Bad Extended Attribute not removed.\n"));
-
-		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"));
-		errors_found++;
-		if ((errors_corrected +=
-		     query(&opts, _("Remove the bad Extended Attribute? "
-				    "(y/n) ")))) {
-			if(remove_eattr_entry(sdp, leaf_bh, ea_hdr,
-					      ea_hdr_prev)){
-				stack;
-				return -1;
-			}
-		} else
-			log_err( _("Bad Extended Attribute not removed.\n"));
-
-		return 1;
+		return ask_remove_eattr_entry(sdp, leaf_bh, ea_hdr,
+					      ea_hdr_prev, 0, 0);
 	}
 
 	memset(ea_name, 0, sizeof(ea_name));
@@ -236,19 +187,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);
-		errors_found++;
-		if ((errors_corrected +=
-		     query(&opts, _("Remove the bad Extended Attribute? "
-				    "(y/n) ")))) {
-			if(remove_eattr_entry(sdp, leaf_bh, ea_hdr,
-					      ea_hdr_prev)){
-				stack;
-				return -1;
-			}
-		} else
-			log_err( _("Bad Extended Attribute not removed.\n"));
-
-		return 1;
+		return ask_remove_eattr_entry(sdp, leaf_bh, ea_hdr,
+					      ea_hdr_prev, 0, 0);
 	}
 
 	if(ea_hdr->ea_num_ptrs){
@@ -260,23 +200,10 @@ static int check_eattr_entry(struct gfs2_inode *ip,
 
 		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"),
-				max_ptrs, ea_hdr->ea_num_ptrs);
-			errors_found++;
-			if ((errors_corrected +=
-			     query(&opts, _("Remove the bad Extended "
-					    "Attribute? (y/n) ")))) {
-				if(remove_eattr_entry(sdp, leaf_bh, ea_hdr,
-						      ea_hdr_prev)){
-					stack;
-					return -1;
-				}
-			} else
-				log_err( _("Bad Extended Attribute not "
-					   "removed.\n"));
-
-			return 1;
+			log_err( _("  Required:  %d\n  Reported:  %d\n"),
+				 max_ptrs, ea_hdr->ea_num_ptrs);
+			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);
@@ -294,7 +221,6 @@ static 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;
@@ -332,11 +258,12 @@ 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_unmark(sbp, bl, block_no, gfs2_eattr_block);
diff --git a/gfs2/fsck/pass2.c b/gfs2/fsck/pass2.c
index 849a96e..4f1f301 100644
--- a/gfs2/fsck/pass2.c
+++ b/gfs2/fsck/pass2.c
@@ -144,36 +144,12 @@ static int check_file_type(uint8_t de_type, uint8_t block_type)
 	return 0;
 }
 
-static int delete_blocks(struct gfs2_inode *ip, uint64_t block,
-			 struct gfs2_buffer_head **bh, void *private)
-{
-	struct gfs2_block_query q = {0};
-
-	if (gfs2_check_range(ip->i_sbd, block) == 0) {
-		log_info(_("Deleting block %lld (0x%llx) as part of inode "
-			   "%lld (0x%llx)\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);
-		if (gfs2_block_check(ip->i_sbd, bl, block, &q))
-			return 0;
-		if (!q.dup_block) {
-			gfs2_block_set(ip->i_sbd, bl, block, gfs2_block_free);
-			gfs2_free_block(ip->i_sbd, block);
-		}
-	}
-	return 0;
-}
-
-static int delete_data(struct gfs2_inode *ip, uint64_t block, void *private)
-{
-	return delete_blocks(ip, block, NULL, private);
-}
-
 struct metawalk_fxns pass2_fxns_delete = {
 	.private = NULL,
-	.check_metalist = delete_blocks,
+	.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
@@ -279,18 +255,19 @@ static int check_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent,
 		log_err( _("Found a bad directory entry: %s\n"), filename);
 
 		errors_found++;
-		if(query(&opts, _("Clear 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);
-
+			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"));
@@ -338,6 +315,8 @@ static int check_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent,
 				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,
@@ -723,12 +702,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)) {
@@ -750,6 +730,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;
@@ -777,8 +758,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);
@@ -880,6 +861,18 @@ int pass2(struct gfs2_sbd *sbp)
 		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/pass4.c b/gfs2/fsck/pass4.c
index fb0e0a5..95b6e9d 100644
--- a/gfs2/fsck/pass4.c
+++ b/gfs2/fsck/pass4.c
@@ -10,6 +10,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 */
@@ -64,8 +73,13 @@ static int scan_inode_list(struct gfs2_sbd *sbp, osi_list_t *list) {
 					(unsigned long long)ii->inode);
 				errors_found++;
 				if(query(&opts,
-						 _("Clear unlinked inode with bad blocks? (y/n) "))) {
+						 _("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;
@@ -79,10 +93,24 @@ static 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 bf774de..b8ec90f 100644
--- a/gfs2/fsck/pass5.c
+++ b/gfs2/fsck/pass5.c
@@ -63,6 +63,7 @@ static int check_block_status(struct gfs2_sbd *sbp, char *buffer, unsigned int b
 	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;
@@ -86,11 +87,41 @@ static int check_block_status(struct gfs2_sbd *sbp, char *buffer, unsigned int b
 		   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);


^ permalink raw reply	[flat|nested] 2+ messages in thread

* gfs2-utils: master - GFS2: fsck.gfs2 sometimes needs to be run twice
@ 2009-06-17 13:18 Bob Peterson
  0 siblings, 0 replies; 2+ messages in thread
From: Bob Peterson @ 2009-06-17 13:18 UTC (permalink / raw)
  To: cluster-cvs-relay

Gitweb:        http://git.fedorahosted.org/git/gfs2-utils.git?p=gfs2-utils.git;a=commitdiff;h=5572c03c5d09caf4b05747b1722bd75d64750298
Commit:        5572c03c5d09caf4b05747b1722bd75d64750298
Parent:        4b0aa4e27fee3750814fa2a7532bb2885dbaf87c
Author:        Bob Peterson <rpeterso@redhat.com>
AuthorDate:    Wed Jun 17 08:12:47 2009 -0500
Committer:     Bob Peterson <rpeterso@redhat.com>
CommitterDate: Wed Jun 17 08:12:47 2009 -0500

GFS2: fsck.gfs2 sometimes needs to be run twice

bz 500483

This patch fixes a lot of fsck.gfs2 bugs.  Most of these bugs
have to do with improper bitmap setting that resulted in needing
to run fsck.gfs2 a second time in order to fix the file system.
There were also cases where changes were not written out and
cases where the user was not asked for permission to make changes.
There were also fixes related to extended attributes.
---
 gfs2/fsck/metawalk.c      |  157 +++++++++++++++++++++++------------
 gfs2/fsck/pass1.c         |  134 ++++++++++++++++++++----------
 gfs2/fsck/pass1b.c        |  114 ++++++++++++++++---------
 gfs2/fsck/pass1c.c        |  169 ++++++++++++++++++++++++++++----------
 gfs2/fsck/pass2.c         |  203 +++++++++++++++++++++++++++++++--------------
 gfs2/fsck/pass3.c         |    3 +-
 gfs2/libgfs2/block_list.c |   28 +++++--
 gfs2/libgfs2/fs_ops.c     |    2 +
 gfs2/libgfs2/libgfs2.h    |    6 +-
 9 files changed, 564 insertions(+), 252 deletions(-)

diff --git a/gfs2/fsck/metawalk.c b/gfs2/fsck/metawalk.c
index 8abb0bd..7088d8f 100644
--- a/gfs2/fsck/metawalk.c
+++ b/gfs2/fsck/metawalk.c
@@ -168,6 +168,8 @@ static 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);
@@ -261,8 +263,8 @@ static 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.                              */
 static void warn_and_patch(struct gfs2_inode *ip, uint64_t *leaf_no, 
-		    uint64_t *bad_leaf, uint64_t old_leaf, int pindex,
-		    const char *msg)
+			   uint64_t *bad_leaf, uint64_t old_leaf,
+			   uint64_t first_ok_leaf, int pindex, const char *msg)
 {
 	if (*bad_leaf != *leaf_no) {
 		log_err( _("Directory Inode %llu (0x%llx) points to leaf %llu"
@@ -276,7 +278,13 @@ static 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, pindex, old_leaf);
+		if (gfs2_check_range(ip->i_sbd, old_leaf) == 0)
+			gfs2_put_leaf_nr(ip, pindex, old_leaf);
+		else
+			gfs2_put_leaf_nr(ip, pindex, first_ok_leaf);
+		log_err( _("Directory Inode %llu (0x%llx) repaired.\n"),
+			 (unsigned long long)ip->i_di.di_num.no_addr,
+			 (unsigned long long)ip->i_di.di_num.no_addr);
 	}
 	else
 		log_err( _("Bad leaf left in place.\n"));
@@ -291,15 +299,36 @@ static int check_leaf(struct gfs2_inode *ip, enum update_flags *update,
 	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 lindex;
 	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(lindex = 0; lindex < (1 << ip->i_di.di_depth); lindex++) {
+		gfs2_get_leaf_nr(ip, lindex, &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(lindex = 0; lindex < (1 << ip->i_di.di_depth); lindex++) {
+		if (fsck_abort)
+			break;
 		gfs2_get_leaf_nr(ip, lindex, &leaf_no);
 
 		/* GFS has multiple indirect pointers to the same leaf
@@ -314,42 +343,43 @@ static 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 #%llu (0x%llx) has an incorrect "
-					"number of pointers to leaf #%llu "
-					" (0x%llx)\n\tFound: %u,  Expected: "
-					"%u\n"), (unsigned long long)
-					ip->i_di.di_num.no_addr,
-					(unsigned long long)
-					ip->i_di.di_num.no_addr,
-					(unsigned long long)old_leaf,
-					(unsigned long long)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 #%llu (0x%llx) has an incorrect "
+				   "number of pointers to leaf #%llu "
+				   " (0x%llx)\n\tFound: %u,  Expected: "
+				   "%u\n"), (unsigned long long)
+				 ip->i_di.di_num.no_addr,
+				 (unsigned long long)
+				 ip->i_di.di_num.no_addr,
+				 (unsigned long long)old_leaf,
+				 (unsigned long long)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 #%llu (0x%llx) is out "
@@ -361,7 +391,7 @@ static int check_leaf(struct gfs2_inode *ip, enum update_flags *update,
 					(unsigned long long)
 					ip->i_di.di_num.no_addr);
 				warn_and_patch(ip, &leaf_no, &bad_leaf,
-					       old_leaf, lindex,
+					       old_leaf, first_ok_leaf, lindex,
 					       _("that is out of range"));
 				memcpy(&leaf, &oldleaf, sizeof(oldleaf));
 				break;
@@ -373,10 +403,10 @@ static 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, lindex,
+					       old_leaf, first_ok_leaf, lindex,
 					       _("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);
@@ -534,8 +564,10 @@ static int check_eattr_entries(struct gfs2_inode *ip,
 							      update_it,
 							      pass->private)) {
 					errors_found++;
-					if (query(&opts, _("Repair the bad EA? "
-						  "(y/n) "))) {
+					error = 1;
+					if (query(&opts, _("Repair the bad "
+							 "Extended Attribute? "
+							   "(y/n) "))) {
 						errors_corrected++;
 						ea_hdr->ea_num_ptrs = i;
 						ea_hdr->ea_data_len =
@@ -545,9 +577,14 @@ static int check_eattr_entries(struct gfs2_inode *ip,
 						/* Endianness doesn't matter
 						   in this case because it's
 						   a single byte. */
-						return -1;
-					}
-					log_err( _("The bad EA was not fixed.\n"));
+						gfs2_block_set(sdp, bl,
+							     ip->i_di.di_eattr,
+							     gfs2_meta_eattr);
+						log_err( _("The EA was "
+							   "fixed.\n"));
+					} else
+						log_err( _("The bad EA was "
+							   "not fixed.\n"));
 				}
 				tot_ealen += sdp->sd_sb.sb_bsize -
 					sizeof(struct gfs2_meta_header);
@@ -565,7 +602,7 @@ static int check_eattr_entries(struct gfs2_inode *ip,
 			 be32_to_cpu(ea_hdr->ea_rec_len));
 	}
 
-	return 0;
+	return error;
 }
 
 /**
@@ -573,7 +610,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,
@@ -595,9 +632,10 @@ static int check_leaf_eattr(struct gfs2_inode *ip, uint64_t block,
 		if(error > 0) {
 			return 1;
 		}
-		check_eattr_entries(ip, bh, pass, want_updated);
+		error = check_eattr_entries(ip, bh, pass, want_updated);
 		if (bh)
 			brelse(bh, *want_updated);
+		return error;
 	}
 
 	return 0;
@@ -656,8 +694,6 @@ static int check_indirect_eattr(struct gfs2_inode *ip, uint64_t indirect,
 				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;
@@ -735,9 +771,17 @@ 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);
@@ -828,8 +872,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++) {
diff --git a/gfs2/fsck/pass1.c b/gfs2/fsck/pass1.c
index d3cf70c..e53b666 100644
--- a/gfs2/fsck/pass1.c
+++ b/gfs2/fsck/pass1.c
@@ -103,9 +103,9 @@ 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);
-		gfs2_block_mark(ip->i_sbd, bl, block, gfs2_dup_block);
+		log_err( _("Found duplicate block referenced as metadata in "
+			   "indirect block - was marked %d\n"), q.block_type);
+		gfs2_block_set(ip->i_sbd, bl, block, gfs2_dup_block);
 		found_dup = 1;
 	}
 	nbh = bread(&ip->i_sbd->buf_list, block);
@@ -121,9 +121,11 @@ static int check_metalist(struct gfs2_inode *ip, uint64_t block,
 	}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;
@@ -133,6 +135,7 @@ 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;
 
 	if (gfs2_check_range(ip->i_sbd, block)) {
 		log_err( _("Bad data block pointer (out of range)\n"));
@@ -147,17 +150,46 @@ static int check_data(struct gfs2_inode *ip, uint64_t block, void *private)
 		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);
+			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;
+		log_err( _("Block %" PRIu64 " (0x%"PRIx64 ") previous "
+			   "reference was invalid, so it likely is a data "
+			   "block.\n"),
+			block, block);
+		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);
+	gfs2_block_mark(ip->i_sbd, bl, block, gfs2_block_used);
+	/* This is 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. */
+	gfs2_set_bitmap(ip->i_sbd, block, GFS2_BLKST_USED);
 	bc->data_count++;
-	return 0;
+	return error;
 }
 
 /* clear_eas - clear the extended attributes for an inode
@@ -184,12 +216,11 @@ static int clear_eas(struct gfs2_inode *ip, struct block_count *bc,
 			block, block);
 	log_err(".\n");
 	errors_found++;
-	if ((errors_corrected += query(&opts, _("Clear the bad EA? (y/n) ")))) {
+	if ((errors_corrected += query(&opts, _("Clear the bad Extended "
+						"Attribute? (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);
 			gfs2_block_set(sdp, bl, block, gfs2_block_free);
 			gfs2_set_bitmap(sdp, block, GFS2_BLKST_FREE);
 		}
@@ -200,9 +231,10 @@ static int clear_eas(struct gfs2_inode *ip, struct block_count *bc,
 		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;
+		log_err( _("The bad Extended Attribute was removed.\n"));
 		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;
 	}
@@ -240,28 +272,34 @@ 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")))
+				       _("Bad indirect Extended Attribute "
+					 "duplicate found"))) {
 				gfs2_block_set(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 #%llu (0x%llx): Duplicate EA indirect "
-			"block found 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,
-			(unsigned long long)indirect,
-			(unsigned long long)indirect);
+		log_err( _("Inode #%llu (0x%llx): Duplicate Extended "
+			   "Attribute indirect block found 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,
+			 (unsigned long long)indirect,
+			 (unsigned long long)indirect);
 		gfs2_block_set(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++;
 	}
@@ -282,7 +320,7 @@ static int finish_eattr_indir(struct gfs2_inode *ip, int indir_ok,
 		return 0;
 	}
 	clear_eas(ip, (struct block_count *)private, 0, 0, want_updated,
-		  _("has unrecoverable indirect EA errors"));
+		  _("has unrecoverable indirect Extended Attribute errors"));
 	return 0;
 }
 
@@ -312,12 +350,15 @@ static int check_extended_leaf_eattr(struct gfs2_inode *ip, uint64_t *data_ptr,
 	int ret = 0;
 
 	if(gfs2_check_range(sdp, el_blk)){
-		log_err( _("Inode #%llu (0x%llx): EA extended "
-			"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)el_blk,
-			(unsigned long long)el_blk);
+		log_err( _("Inode #%llu (0x%llx): Extended Attribute block "
+			   "%llu (0x%llx) has an extemded 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;
 	}
@@ -335,10 +376,12 @@ static int check_extended_leaf_eattr(struct gfs2_inode *ip, uint64_t *data_ptr,
 	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"));
+				  _("has bad extended Extended Attribute "
+				    "duplicate"));
 		else
 			clear_eas(ip, bc, el_blk, 0, want_updated,
-				  _("EA extended leaf block has incorrect type"));
+				  _("Extended Attribute extended leaf block "
+				    "has incorrect type"));
 		ret = 1;
 	} else { /* If this looks like an EA */
 		if(q.block_type != gfs2_block_free) { /* Duplicate? */
@@ -371,16 +414,19 @@ static int check_eattr_leaf(struct gfs2_inode *ip, uint64_t block,
 	struct gfs2_block_query q = {0};
 	struct block_count *bc = (struct block_count *) private;
 
+	/* Figure out the parent's block type */
+	gfs2_block_check(sdp, bl, parent, &q);
 	/* 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 */
+	if (parent != ip->i_di.di_num.no_addr && /* if parent isn't the inode*/
+	    q.block_type != gfs2_indir_blk) { /* parent isn't indirect block */
 		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 #%llu (0x%llx): EA leaf block #%llu (0x%llx"
-			 ") is out of range.\n"),
+		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);
@@ -400,10 +446,12 @@ static int check_eattr_leaf(struct gfs2_inode *ip, uint64_t 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"));
+					  _("Bad Extended Attribute duplicate "
+					    "found"));
 			} else {
 				clear_eas(ip, bc, block, 0, want_updated,
-					  _("EA leaf block has incorrect type"));
+					  _("Extended Attribute leaf block "
+					    "has incorrect type"));
 			}
 			ret = 1;
 			brelse(leaf_bh, not_updated);
@@ -600,8 +648,8 @@ static 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);
@@ -724,7 +772,7 @@ static 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;
 	}
@@ -774,7 +822,7 @@ static 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"),
+		log_info( _("Found invalid metadata at #%" PRIu64 " (0x%" PRIx64 ")\n"),
 				  block, block);
 		if(gfs2_block_set(sdp, bl, block, gfs2_meta_inval)) {
 			stack;
diff --git a/gfs2/fsck/pass1b.c b/gfs2/fsck/pass1b.c
index 4e24611..c02317b 100644
--- a/gfs2/fsck/pass1b.c
+++ b/gfs2/fsck/pass1b.c
@@ -165,14 +165,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 #%llu"
-			") with block #%llu\n"),
-			dh->id->name ? dh->id->name : _("unknown name"),
-			(unsigned long long)ip->i_di.di_num.no_addr,
-			(unsigned long long)block);
+		log_err( _("Found duplicate reference in inode \"%s\" at "
+			   "block #%llu (0x%llx) with block #%llu (0x%llx)\n"),
+			 dh->id->name ? dh->id->name : _("unknown name"),
+			 (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);
 		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 */
@@ -184,31 +185,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 #%llu"
-			" (0x%llx) at block #%llu (0x%llx)\n"),
-			dh->id->name ? dh->id->name : _("unknown name"),
-			(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);
-		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 +340,15 @@ static int find_block_ref(struct gfs2_sbd *sbp, uint64_t inode, struct dup_block
 	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,6 +400,51 @@ static 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 %llu (0x%llx), "
+				    "but the block is really metadata.\n"),
+				  id->name, (unsigned long long)id->block_no,
+				  (unsigned long long)id->block_no,
+				  (unsigned long long)b->block_no,
+				  (unsigned long long)b->block_no);
+			errors_found++;
+			if ((errors_corrected +=
+			     query(&opts, _("Clear the inode? (y/n) ")))) {
+				log_warn( _("Clearing...\n"));
+				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 %llu (0x%llx) has %d inodes referencing it"
 		   " for a total of %d duplicate references\n"),
 		   (unsigned long long)b->block_no,
@@ -429,10 +453,18 @@ static int handle_dup_blk(struct gfs2_sbd *sbp, struct dup_blocks *b)
 
 	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( _("Inode %s (%lld/0x%llx) has %d reference(s) to "
+			    "block %llu (0x%llx)\n"), id->name,
+			  (unsigned long long)id->block_no,
+			  (unsigned long long)id->block_no,
+			  id->dup_count, (unsigned long long)b->block_no,
+			  (unsigned long long)b->block_no);
+		errors_found++;
+		if (!(errors_corrected +=
+		      query(&opts, _("Clear the inode? (y/n) ")))) {
+			log_warn( _("The bad inode was not cleared...\n"));
+			continue;
+		}
 		log_warn( _("Clearing...\n"));
 		ip = fsck_load_inode(sbp, id->block_no);
 		dh.b = b;
@@ -444,7 +476,9 @@ static 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;
diff --git a/gfs2/fsck/pass1c.c b/gfs2/fsck/pass1c.c
index f3b7985..e0f4576 100644
--- a/gfs2/fsck/pass1c.c
+++ b/gfs2/fsck/pass1c.c
@@ -15,7 +15,8 @@ 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"),
+	log_err( _("Bad Extended Attribute at block #%"PRIu64
+		   " (0x%" PRIx64 ") removed.\n"),
 		 leaf_bh->b_blocknr, leaf_bh->b_blocknr);
 	if(!prev)
 		curr->ea_type = GFS2_EATYPE_UNUSED;
@@ -48,8 +49,15 @@ static int check_eattr_indir(struct gfs2_inode *ip, uint64_t 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);
-		ip->i_di.di_eattr = 0;
-		*update = (opts.no ? not_updated : updated);
+		errors_found++;
+		if ((errors_corrected +=
+		     query(&opts, _("Remove the bad Extended Attribute? "
+				    "(y/n) ")))) {
+			ip->i_di.di_eattr = 0;
+			*update = updated;
+			log_err( _("Bad Extended Attribute removed.\n"));
+		} else
+			log_err( _("Bad Extended Attribute not removed.\n"));
 		return 1;
 	}
 	else if (gfs2_block_check(sbp, bl, block, &q)) {
@@ -59,13 +67,21 @@ static int check_eattr_indir(struct gfs2_inode *ip, uint64_t block,
 	else if(q.block_type != gfs2_indir_blk) {
 		log_err( _("Extended attributes indirect block #%llu"
 			" (0x%llx) for inode #%llu"
-			" (0x%llx) invalid...removing\n"),
+			" (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);
-		ip->i_di.di_eattr = 0;
-		*update = (opts.no ? not_updated : updated);
+		errors_found++;
+		if ((errors_corrected +=
+		     query(&opts, _("Remove the bad Extended Attribute? "
+				    "(y/n) ")))) {
+			ip->i_di.di_eattr = 0;
+			*update = updated;
+			log_err( _("Bad Extended Attribute removed.\n"));
+		} else
+			log_err( _("Bad Extended Attribute not removed.\n"));
+
 		return 1;
 	}
 	else
@@ -85,11 +101,19 @@ static int check_eattr_leaf(struct gfs2_inode *ip, uint64_t block,
 	*update = not_updated;
 	if(gfs2_check_range(sbp, block)) {
 		log_err( _("Extended attributes block for inode #%llu"
-			" (0x%llx) out of range...removing\n"),
+			" (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);
-		ip->i_di.di_eattr = 0;
-		*update = (opts.no ? not_updated : updated);
+		errors_found++;
+		if ((errors_corrected +=
+		     query(&opts, _("Remove the bad Extended Attribute? "
+				    "(y/n) ")))) {
+			ip->i_di.di_eattr = 0;
+			*update = updated;
+			log_err( _("Bad Extended Attribute removed.\n"));
+		} else
+			log_err( _("Bad Extended Attribute not removed.\n"));
+
 		return 1;
 	}
 	else if (gfs2_block_check(sbp, bl, block, &q)) {
@@ -98,11 +122,19 @@ static int check_eattr_leaf(struct gfs2_inode *ip, uint64_t block,
 	}
 	else if(q.block_type != gfs2_meta_eattr) {
 		log_err( _("Extended attributes block for inode #%llu"
-			" (0x%llx) invalid...removing\n"),
-			(unsigned long long)ip->i_di.di_num.no_addr,
-			(unsigned long long)ip->i_di.di_num.no_addr);
-		ip->i_di.di_eattr = 0;
-		*update = (opts.no ? not_updated : updated);
+			   " (0x%llx) invalid.\n"),
+			 (unsigned long long)ip->i_di.di_num.no_addr,
+			 (unsigned long long)ip->i_di.di_num.no_addr);
+		errors_found++;
+		if ((errors_corrected +=
+		     query(&opts, _("Remove the bad Extended Attribute? "
+				    "(y/n) ")))) {
+			ip->i_di.di_eattr = 0;
+			*update = updated;
+			log_err( _("Bad Extended Attribute removed.\n"));
+		} else
+			log_err( _("Bad Extended Attribute not removed.\n"));
+
 		return 1;
 	}
 	else 
@@ -125,40 +157,72 @@ 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;
-		}
+		errors_found++;
+		if ((errors_corrected +=
+		     query(&opts, _("Remove the bad Extended Attribute? "
+				    "(y/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;
+			}
+		} else
+			log_err( _("Bad Extended Attribute not removed.\n"));
+
 		return 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;
-		}
+		errors_found++;
+		if ((errors_corrected +=
+		     query(&opts, _("Remove the bad Extended Attribute? "
+				    "(y/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;
+			}
+		} else
+			log_err( _("Bad Extended Attribute not removed.\n"));
+
 		return 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;
-		}
+		errors_found++;
+		if ((errors_corrected +=
+		     query(&opts, _("Remove the bad Extended Attribute? "
+				    "(y/n) ")))) {
+			ea_hdr->ea_flags |= GFS2_EAFLAG_LAST;
+			if(remove_eattr_entry(sdp, leaf_bh, ea_hdr,
+					      ea_hdr_prev)){
+				stack;
+				return -1;
+			}
+		} else
+			log_err( _("Bad Extended Attribute not removed.\n"));
+
 		return 1;
 	}
 	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;
-		}
+		errors_found++;
+		if ((errors_corrected +=
+		     query(&opts, _("Remove the bad Extended Attribute? "
+				    "(y/n) ")))) {
+			if(remove_eattr_entry(sdp, leaf_bh, ea_hdr,
+					      ea_hdr_prev)){
+				stack;
+				return -1;
+			}
+		} else
+			log_err( _("Bad Extended Attribute not removed.\n"));
+
 		return 1;
 	}
 
@@ -170,10 +234,18 @@ 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;
-		}
+		errors_found++;
+		if ((errors_corrected +=
+		     query(&opts, _("Remove the bad Extended Attribute? "
+				    "(y/n) ")))) {
+			if(remove_eattr_entry(sdp, leaf_bh, ea_hdr,
+					      ea_hdr_prev)){
+				stack;
+				return -1;
+			}
+		} else
+			log_err( _("Bad Extended Attribute not removed.\n"));
+
 		return 1;
 	}
 
@@ -189,10 +261,19 @@ static int check_eattr_entry(struct gfs2_inode *ip,
 			log_err( _("  Required:  %d\n"
 				"  Reported:  %d\n"),
 				max_ptrs, ea_hdr->ea_num_ptrs);
-			if(remove_eattr_entry(sdp, leaf_bh, ea_hdr, ea_hdr_prev)){
-				stack;
-				return -1;
-			}
+			errors_found++;
+			if ((errors_corrected +=
+			     query(&opts, _("Remove the bad Extended "
+					    "Attribute? (y/n) ")))) {
+				if(remove_eattr_entry(sdp, leaf_bh, ea_hdr,
+						      ea_hdr_prev)){
+					stack;
+					return -1;
+				}
+			} else
+				log_err( _("Bad Extended Attribute not "
+					   "removed.\n"));
+
 			return 1;
 		} else {
 			log_debug( _("  Pointers Required: %d\n  Pointers Reported: %d\n"),
@@ -256,7 +337,7 @@ int pass1c(struct gfs2_sbd *sbp)
 		if (gfs2_check_meta(bh, GFS2_METATYPE_IN)) { /* 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 %llu (0x%llx)\n"),
diff --git a/gfs2/fsck/pass2.c b/gfs2/fsck/pass2.c
index 31390c2..c6961b7 100644
--- a/gfs2/fsck/pass2.c
+++ b/gfs2/fsck/pass2.c
@@ -142,6 +142,38 @@ static int check_file_type(uint8_t de_type, uint8_t block_type)
 	return 0;
 }
 
+static int delete_blocks(struct gfs2_inode *ip, uint64_t block,
+			 struct gfs2_buffer_head **bh, void *private)
+{
+	struct gfs2_block_query q = {0};
+
+	if (gfs2_check_range(ip->i_sbd, block) == 0) {
+		log_info(_("Deleting block %lld (0x%llx) as part of inode "
+			   "%lld (0x%llx)\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);
+		if (gfs2_block_check(ip->i_sbd, bl, block, &q))
+			return 0;
+		if (!q.dup_block) {
+			gfs2_block_set(ip->i_sbd, bl, block, gfs2_block_free);
+			gfs2_free_block(ip->i_sbd, block);
+		}
+	}
+	return 0;
+}
+
+static int delete_data(struct gfs2_inode *ip, uint64_t block, void *private)
+{
+	return delete_blocks(ip, block, NULL, private);
+}
+
+struct metawalk_fxns pass2_fxns_delete = {
+	.private = NULL,
+	.check_metalist = delete_blocks,
+	.check_data = delete_data,
+};
+
 /* FIXME: should maybe refactor this a bit - but need to deal with
  * FIXMEs internally first */
 static int check_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent,
@@ -160,7 +192,6 @@ static 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;
@@ -242,22 +273,17 @@ static 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, _("Clear 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_metatree(entry_ip, &pass2_fxns_delete);
 			dirent2_del(ip, bh, prev_de, dent);
 
 			gfs2_block_set(sbp, bl, de->de_inum.no_addr,
@@ -277,22 +303,44 @@ static int check_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent,
 	   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 %llu (0x%llx"
-			") in dir inode %llu (0x%llx"
-			") has an invalid block type: %d.\n"), tmp_name,
-			(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,
-			q.block_type);
+			   ") in dir inode %llu (0x%llx"
+			   ") block type %d: %s.\n"), tmp_name,
+			 (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,
+			 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_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"));
@@ -323,6 +371,7 @@ static 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"));
@@ -585,27 +634,33 @@ static 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 %llu (0x%llx"
@@ -671,6 +726,7 @@ int pass2(struct gfs2_sbd *sbp)
 	int filename_len;
 	char tmp_name[256];
 	int error = 0;
+	enum update_flags need_update = NOT_UPDATED;
 
 	/* Check all the system directory inodes. */
 	if (check_system_dir(sbp->md.jiinode, "jindex", build_jindex)) {
@@ -766,42 +822,61 @@ 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 %llu (0x%llx)\n"),
 				ip->i_di.di_entries, ds.entry_count,
 				(unsigned long long)ip->i_di.di_num.no_addr,
 				(unsigned long long)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 */
+			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 */
 	}
 	return FSCK_OK;
 }
diff --git a/gfs2/fsck/pass3.c b/gfs2/fsck/pass3.c
index 83f2e50..9bb7cb4 100644
--- a/gfs2/fsck/pass3.c
+++ b/gfs2/fsck/pass3.c
@@ -272,10 +272,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/libgfs2/block_list.c b/gfs2/libgfs2/block_list.c
index 2bea6aa..571c737 100644
--- a/gfs2/libgfs2/block_list.c
+++ b/gfs2/libgfs2/block_list.c
@@ -293,12 +293,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;
@@ -316,12 +317,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;
@@ -335,15 +349,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_ops.c b/gfs2/libgfs2/fs_ops.c
index 2624c24..fc898ce 100644
--- a/gfs2/libgfs2/fs_ops.c
+++ b/gfs2/libgfs2/fs_ops.c
@@ -705,6 +705,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 a2a6a9b..b64edd5 100644
--- a/gfs2/libgfs2/libgfs2.h
+++ b/gfs2/libgfs2/libgfs2.h
@@ -371,8 +371,12 @@ extern int gfs2_block_mark(struct gfs2_sbd *sdp, struct gfs2_block_list *il,
 	 		   uint64_t block, enum gfs2_mark_block mark);
 extern 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 */
+extern 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 */
 extern int gfs2_block_clear(struct gfs2_sbd *sdp, struct gfs2_block_list *il,
-			    uint64_t block, enum gfs2_mark_block m);
+			    uint64_t block);
 extern int gfs2_block_check(struct gfs2_sbd *sdp, struct gfs2_block_list *il,
 			    uint64_t block, struct gfs2_block_query *val);
 extern void *gfs2_block_list_destroy(struct gfs2_sbd *sdp,


^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2009-08-10 16:34 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-08-10 16:34 gfs2-utils: master - GFS2: fsck.gfs2 sometimes needs to be run twice Bob Peterson
  -- strict thread matches above, loose matches on Subject: below --
2009-06-17 13:18 Bob Peterson

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