[Cluster-devel] [gfs2-utils PATCH 40/47] fsck.gfs2: major duplicate reference reform

Bob Peterson rpeterso at redhat.com
Tue May 14 16:22:03 UTC 2013


This patch is a large set of changes designed to rework how pass1b
resolves duplicate block references. There are basically two major
changes with this patch:

First, the metawalk functions were trying to attribute too much
information to the return codes of its callback functions: (1) Was
there an error? (2) Was the inode valid? (3) Was a duplicate block
reference encountered? (4) Should we keep going and process more of
its metadata? This often led to bad decisions made by metawalk:
For example, it would stop processing metadata when it should have
continued, thereby forgetting to mark blocks free that were no longer
in use. This patch introduces two new variables to the metatree
functions, *is_valid and *was_duplicate. The first one indicates
whether the dinode was valid or whether there is good cause to
delete it. The second indicates whether a duplicate block reference
was encountered. With this patch, the return code indicates simply
whether metadata processing should be skipped or not, and nothing
more. This is especially useful in pass1. For example, if it
encounters major corruption in a dinode, it doesn't do any good to
mark all its blocks as duplicates and have the undo functions try
to reverse all those decisions.

The second major change with this patch has to do with the
philosophy of how duplicate references are resolved. Before, pass1
would flag the duplicates and pass1b would try to resolve them all,
marking dinodes that should be deleted as "bad", and pass2 would
delete the bad dinodes. This becomes very problematic and messy
in pass1b, especially in cases where you have a number of duplicate
references that are common between multiple dinodes. For example,
suppose files A, B and C share some of the same blocks, but not
others:

A - 0x3000 0x3001 0x1233 0x1234 0x3004
B - 0x4000 0x4001 0x4002 0x1234 0x1235
C - 0x1231 0x1232 0x1233 0x1234 0x1235

The old strategy that got us into trouble was to log the three
duplicate blocks, delete invalid dinodes A and B, but leave the
duplicate reference structure around for 0x1233, 0x1234 and 0x1235
so that C would be left intact with the only references to all five
blocks. But in cleaning up the leftover duplicate structure often
led to bad decisions where C wouldn't have all its blocks marked
as referenced. Often, you would end up with blocks that were marked
as free which were still in use, and blocks that were marked as
in use that should have been freed, and it was all due to the
existence of those duplicate structures that were still on the list
until pass2.

The new strategy is to resolve-as-you-go. In other words, pass1b
considers the three duplicate blocks, but when it decides that
file A should be deleted, it removes all its references from the
list, thereby making the decision between B and C easier: it no
longer has to worry about block 1233, and there's only one thing
to consider about block 0x1234 and 0x1235. When B is deleted, it
removes all its duplicate references, so block 0x1235 is no longer
considered to be in conflict. Once a file is deleted, all its
duplicate reference structures are removed so as not to confuse
other duplicates being resolved. The duplicate handler structure,
struct dup_handler, is revised with every reference that's resolved
so it's not working off a long list of possibles, most of
which were already taken care of by previous actions.

rhbz#902920

Conflicts:
	gfs2/fsck/pass1b.c
---
 gfs2/fsck/fsck.h       |   2 -
 gfs2/fsck/initialize.c |   2 +-
 gfs2/fsck/metawalk.c   | 219 ++++++++-----
 gfs2/fsck/metawalk.h   |  31 +-
 gfs2/fsck/pass1.c      | 101 +++---
 gfs2/fsck/pass1b.c     | 810 ++++++++++++++++++++-----------------------------
 gfs2/fsck/pass2.c      |  60 ----
 gfs2/fsck/util.c       |  37 ++-
 gfs2/fsck/util.h       |   3 +-
 9 files changed, 612 insertions(+), 653 deletions(-)

diff --git a/gfs2/fsck/fsck.h b/gfs2/fsck/fsck.h
index b21a670..6d888af 100644
--- a/gfs2/fsck/fsck.h
+++ b/gfs2/fsck/fsck.h
@@ -112,11 +112,9 @@ extern int pass4(struct gfs2_sbd *sdp);
 extern int pass5(struct gfs2_sbd *sdp);
 extern int rg_repair(struct gfs2_sbd *sdp, int trust_lvl, int *rg_count,
 		     int *sane);
-extern void gfs2_dup_free(void);
 extern int fsck_query(const char *format, ...)
 	__attribute__((format(printf,1,2)));
 extern struct dir_info *dirtree_find(uint64_t block);
-extern void dup_listent_delete(struct inode_with_dups *id);
 extern void dup_delete(struct duptree *dt);
 extern void dirtree_delete(struct dir_info *b);
 
diff --git a/gfs2/fsck/initialize.c b/gfs2/fsck/initialize.c
index 7d64b0a..b01b240 100644
--- a/gfs2/fsck/initialize.c
+++ b/gfs2/fsck/initialize.c
@@ -66,7 +66,7 @@ static int block_mounters(struct gfs2_sbd *sdp, int block_em)
 	return 0;
 }
 
-void gfs2_dup_free(void)
+static void gfs2_dup_free(void)
 {
 	struct osi_node *n;
 	struct duptree *dt;
diff --git a/gfs2/fsck/metawalk.c b/gfs2/fsck/metawalk.c
index d285ee5..19593f3 100644
--- a/gfs2/fsck/metawalk.c
+++ b/gfs2/fsck/metawalk.c
@@ -950,7 +950,8 @@ int delete_block(struct gfs2_inode *ip, uint64_t block,
 
 /**
  * find_remove_dup - find out if this is a duplicate ref.  If so, remove it.
- * Returns: 0 if not a duplicate reference, 1 if it is.
+ *
+ * Returns: 1 if there are any remaining references to this block, else 0.
  */
 int find_remove_dup(struct gfs2_inode *ip, uint64_t block, const char *btype)
 {
@@ -964,41 +965,18 @@ int find_remove_dup(struct gfs2_inode *ip, uint64_t block, const char *btype)
 	/* remove the inode reference id structure for this reference. */
 	id = find_dup_ref_inode(dt, ip);
 	if (!id)
-		return 0;
-
-	dup_listent_delete(id);
-	log_err( _("Removing duplicate status of block %llu (0x%llx) "
-		   "referenced as %s by dinode %llu (0x%llx)\n"),
-		 (unsigned long long)block, (unsigned long long)block,
-		 btype, (unsigned long long)ip->i_di.di_num.no_addr,
-		 (unsigned long long)ip->i_di.di_num.no_addr);
-	dt->refs--; /* one less reference */
-	if (dt->refs == 1) {
-		log_info( _("This leaves only one reference: it's "
-			    "no longer a duplicate.\n"));
+		goto more_refs;
+	
+	dup_listent_delete(dt, id);
+	if (dt->refs == 0) {
+		log_info( _("This was the last reference: it's no longer a "
+			    "duplicate.\n"));
 		dup_delete(dt); /* not duplicate now */
-	} else
-		log_info( _("%d block reference(s) remain.\n"),
-			  dt->refs);
-	return 1; /* but the original ref still exists so do not free it. */
-}
-
-/**
- * free_block_if_notdup - free blocks associated with an inode, but if it's a
- *                        duplicate, just remove that designation instead.
- * Returns: 1 if the block was freed, 0 if a duplicate reference was removed
- * Note: The return code is handled this way because there are places in
- *       metawalk.c that assume "1" means "change was made" and "0" means
- *       change was not made.
- */
-int free_block_if_notdup(struct gfs2_inode *ip, uint64_t block,
-			 const char *btype)
-{
-	if (!find_remove_dup(ip, block, btype)) { /* not a dup */
-		fsck_blockmap_set(ip, block, btype, gfs2_block_free);
-		return meta_skip_further;
+		return 0;
 	}
-	return meta_is_good;
+more_refs:
+	log_info( _("%d block reference(s) remain.\n"), dt->refs);
+	return 1; /* references still exist so do not free the block. */
 }
 
 /**
@@ -1010,7 +988,8 @@ int free_block_if_notdup(struct gfs2_inode *ip, uint64_t block,
  */
 static int delete_block_if_notdup(struct gfs2_inode *ip, uint64_t block,
 				  struct gfs2_buffer_head **bh,
-				  const char *btype, void *private)
+				  const char *btype, int *was_duplicate,
+				  void *private)
 {
 	uint8_t q;
 
@@ -1027,7 +1006,19 @@ static int delete_block_if_notdup(struct gfs2_inode *ip, uint64_t block,
 			  (unsigned long long)ip->i_di.di_num.no_addr);
 		return meta_is_good;
 	}
-	return free_block_if_notdup(ip, block, btype);
+	if (find_remove_dup(ip, block, btype)) { /* a dup */
+		if (was_duplicate)
+			*was_duplicate = 1;
+		log_err( _("Not clearing duplicate reference in inode "
+			   "at block #%llu (0x%llx) to block #%llu (0x%llx) "
+			   "because it's referenced by another inode.\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);
+	} else {
+		fsck_blockmap_set(ip, block, btype, gfs2_block_free);
+	}
+	return meta_is_good;
 }
 
 /**
@@ -1197,7 +1188,7 @@ static int build_and_check_metalist(struct gfs2_inode *ip, osi_list_t *mlp,
 	osi_list_t *prev_list, *cur_list, *tmp;
 	int h, head_size, iblk_type;
 	uint64_t *ptr, block;
-	int error = 0, err;
+	int error, was_duplicate, is_valid;
 
 	osi_list_add(&metabh->b_altlist, &mlp[0]);
 
@@ -1211,7 +1202,7 @@ static int build_and_check_metalist(struct gfs2_inode *ip, osi_list_t *mlp,
 
 	/* if (<there are no indirect blocks to check>) */
 	if (height < 2)
-		return 0;
+		return meta_is_good;
 	for (h = 1; h < height; h++) {
 		if (h > 1) {
 			if (is_dir(&ip->i_di, ip->i_sbd->gfs1) &&
@@ -1243,7 +1234,7 @@ static int build_and_check_metalist(struct gfs2_inode *ip, osi_list_t *mlp,
 			     ptr++) {
 				if (skip_this_pass || fsck_abort) {
 					free_metalist(ip, mlp);
-					return FSCK_OK;
+					return meta_is_good;
 				}
 				nbh = NULL;
 
@@ -1251,19 +1242,41 @@ static int build_and_check_metalist(struct gfs2_inode *ip, osi_list_t *mlp,
 					continue;
 
 				block = be64_to_cpu(*ptr);
-				err = pass->check_metalist(ip, block, &nbh, h,
-							   pass->private);
+				was_duplicate = 0;
+				error = pass->check_metalist(ip, block, &nbh,
+							     h, &is_valid,
+							     &was_duplicate,
+							     pass->private);
 				/* check_metalist should hold any buffers
 				   it gets with "bread". */
-				if (err == meta_error) {
+				if (error == meta_error) {
 					stack;
-					error = err;
+					log_info(_("\nSerious metadata "
+						   "error on block %llu "
+						   "(0x%llx).\n"),
+						 (unsigned long long)block,
+						 (unsigned long long)block);
+					return error;
+				}
+				if (error == meta_skip_further) {
+					log_info(_("\nUnrecoverable metadata "
+						   "error on block %llu "
+						   "(0x%llx). Further metadata"
+						   " will be skipped.\n"),
+						 (unsigned long long)block,
+						 (unsigned long long)block);
 					return error;
 				}
-				if (err == meta_skip_further) {
-					if (!error)
-						error = err;
-					log_debug( _("Skipping block %llu (0x%llx)\n"),
+				if (!is_valid) {
+					log_debug( _("Skipping rejected block "
+						     "%llu (0x%llx)\n"),
+						   (unsigned long long)block,
+						   (unsigned long long)block);
+					continue;
+				}
+				if (was_duplicate) {
+					log_debug( _("Skipping duplicate %llu "
+						     "(0x%llx)\n"),
 						   (unsigned long long)block,
 						   (unsigned long long)block);
 					continue;
@@ -1597,34 +1610,52 @@ int remove_dentry_from_dir(struct gfs2_sbd *sdp, uint64_t dir,
 }
 
 int delete_metadata(struct gfs2_inode *ip, uint64_t block,
-		    struct gfs2_buffer_head **bh, int h, void *private)
+		    struct gfs2_buffer_head **bh, int h, int *is_valid,
+		    int *was_duplicate, void *private)
 {
-	return delete_block_if_notdup(ip, block, bh, _("metadata"), private);
+	*is_valid = 1;
+	*was_duplicate = 0;
+	return delete_block_if_notdup(ip, block, bh, _("metadata"),
+				      was_duplicate, private);
 }
 
 int delete_leaf(struct gfs2_inode *ip, uint64_t block, void *private)
 {
-	return delete_block_if_notdup(ip, block, NULL, _("leaf"), private);
+	return delete_block_if_notdup(ip, block, NULL, _("leaf"), NULL,
+				      private);
 }
 
 int delete_data(struct gfs2_inode *ip, uint64_t metablock,
 		uint64_t block, void *private)
 {
-	return delete_block_if_notdup(ip, block, NULL, _("data"), private);
+	return delete_block_if_notdup(ip, block, NULL, _("data"), NULL,
+				      private);
 }
 
-int delete_eattr_indir(struct gfs2_inode *ip, uint64_t block, uint64_t parent,
-		       struct gfs2_buffer_head **bh, void *private)
+static int del_eattr_generic(struct gfs2_inode *ip, uint64_t block,
+			     uint64_t parent, struct gfs2_buffer_head **bh,
+			     void *private, const char *eatype)
 {
-	int ret;
+	int ret = 0;
+	int was_free = 0;
+	uint8_t q;
 
-	ret = delete_block_if_notdup(ip, block, NULL,
-				     _("indirect extended attribute"),
-				     private);
+	if (valid_block(ip->i_sbd, block)) {
+		q = block_type(block);
+		if (q == gfs2_block_free)
+			was_free = 1;
+		ret = delete_block_if_notdup(ip, block, NULL, eatype,
+					     NULL, private);
+		if (!ret) {
+			*bh = bread(ip->i_sbd, block);
+			if (!was_free)
+				ip->i_di.di_blocks--;
+			bmodified(ip->i_bh);
+		}
+	}
 	/* Even if it's a duplicate reference, we want to eliminate the
 	   reference itself, and adjust di_blocks accordingly. */
 	if (ip->i_di.di_eattr) {
-		ip->i_di.di_blocks--;
 		if (block == ip->i_di.di_eattr)
 			ip->i_di.di_eattr = 0;
 		bmodified(ip->i_bh);
@@ -1632,24 +1663,74 @@ int delete_eattr_indir(struct gfs2_inode *ip, uint64_t block, uint64_t parent,
 	return ret;
 }
 
+int delete_eattr_indir(struct gfs2_inode *ip, uint64_t block, uint64_t parent,
+		       struct gfs2_buffer_head **bh, void *private)
+{
+	return del_eattr_generic(ip, block, parent, bh, private,
+				 _("extended attribute"));
+}
+
 int delete_eattr_leaf(struct gfs2_inode *ip, uint64_t block, uint64_t parent,
 		      struct gfs2_buffer_head **bh, void *private)
 {
-	int ret;
+	return del_eattr_generic(ip, block, parent, bh, private,
+				 _("indirect extended attribute"));
+}
 
-	ret = delete_block_if_notdup(ip, block, NULL, _("extended attribute"),
-				     private);
-	if (ip->i_di.di_eattr) {
-		ip->i_di.di_blocks--;
-		if (block == ip->i_di.di_eattr)
-			ip->i_di.di_eattr = 0;
-		bmodified(ip->i_bh);
+int delete_eattr_entry(struct gfs2_inode *ip, struct gfs2_buffer_head *leaf_bh,
+		       struct gfs2_ea_header *ea_hdr,
+		       struct gfs2_ea_header *ea_hdr_prev, void *private)
+{
+	struct gfs2_sbd *sdp = ip->i_sbd;
+	char ea_name[256];
+	uint32_t avail_size;
+	int max_ptrs;
+
+	if (!ea_hdr->ea_name_len){
+		/* Skip this entry for now */
+		return 1;
 	}
-	return ret;
+
+	memset(ea_name, 0, sizeof(ea_name));
+	strncpy(ea_name, (char *)ea_hdr + sizeof(struct gfs2_ea_header),
+		ea_hdr->ea_name_len);
+
+	if (!GFS2_EATYPE_VALID(ea_hdr->ea_type) &&
+	   ((ea_hdr_prev) || (!ea_hdr_prev && ea_hdr->ea_type))){
+		/* Skip invalid entry */
+		return 1;
+	}
+
+	if (!ea_hdr->ea_num_ptrs)
+		return 0;
+
+	avail_size = sdp->sd_sb.sb_bsize - sizeof(struct gfs2_meta_header);
+	max_ptrs = (be32_to_cpu(ea_hdr->ea_data_len) + avail_size - 1) /
+		avail_size;
+
+	if (max_ptrs > ea_hdr->ea_num_ptrs)
+		return 1;
+
+	log_debug( _("  Pointers Required: %d\n  Pointers Reported: %d\n"),
+		   max_ptrs, ea_hdr->ea_num_ptrs);
+
+	return 0;
+}
+
+int delete_eattr_extentry(struct gfs2_inode *ip, uint64_t *ea_data_ptr,
+			  struct gfs2_buffer_head *leaf_bh,
+			  struct gfs2_ea_header *ea_hdr,
+			  struct gfs2_ea_header *ea_hdr_prev, void *private)
+{
+	uint64_t block = be64_to_cpu(*ea_data_ptr);
+
+	return delete_block_if_notdup(ip, block, NULL, _("extended attribute"),
+				      NULL, private);
 }
 
 static int alloc_metalist(struct gfs2_inode *ip, uint64_t block,
-			  struct gfs2_buffer_head **bh, int h, void *private)
+			  struct gfs2_buffer_head **bh, int h, int *is_valid,
+			  int *was_duplicate, void *private)
 {
 	uint8_t q;
 	const char *desc = (const char *)private;
@@ -1657,6 +1738,8 @@ static int alloc_metalist(struct gfs2_inode *ip, uint64_t block,
 	/* No need to range_check here--if it was added, it's in range. */
 	/* We can't check the bitmap here because this function is called
 	   after the bitmap has been set but before the blockmap has. */
+	*is_valid = 1;
+	*was_duplicate = 0;
 	*bh = bread(ip->i_sbd, block);
 	q = block_type(block);
 	if (blockmap_to_bitmap(q, ip->i_sbd->gfs1) == GFS2_BLKST_FREE) {
diff --git a/gfs2/fsck/metawalk.h b/gfs2/fsck/metawalk.h
index 49217cc..56f57d9 100644
--- a/gfs2/fsck/metawalk.h
+++ b/gfs2/fsck/metawalk.h
@@ -24,7 +24,8 @@ extern int delete_block(struct gfs2_inode *ip, uint64_t block,
 		 struct gfs2_buffer_head **bh, const char *btype,
 		 void *private);
 extern int delete_metadata(struct gfs2_inode *ip, uint64_t block,
-			   struct gfs2_buffer_head **bh, int h, void *private);
+			   struct gfs2_buffer_head **bh, int h, int *is_valid,
+			   int *was_duplicate, void *private);
 extern int delete_leaf(struct gfs2_inode *ip, uint64_t block, void *private);
 extern int delete_data(struct gfs2_inode *ip, uint64_t metablock,
 		       uint64_t block, void *private);
@@ -32,6 +33,17 @@ extern int delete_eattr_indir(struct gfs2_inode *ip, uint64_t block, uint64_t pa
 		       struct gfs2_buffer_head **bh, void *private);
 extern int delete_eattr_leaf(struct gfs2_inode *ip, uint64_t block, uint64_t parent,
 		      struct gfs2_buffer_head **bh, void *private);
+extern int delete_eattr_entry(struct gfs2_inode *ip,
+			      struct gfs2_buffer_head *leaf_bh,
+			      struct gfs2_ea_header *ea_hdr,
+			      struct gfs2_ea_header *ea_hdr_prev,
+			      void *private);
+extern int delete_eattr_extentry(struct gfs2_inode *ip, uint64_t *ea_data_ptr,
+				 struct gfs2_buffer_head *leaf_bh,
+				 struct gfs2_ea_header *ea_hdr,
+				 struct gfs2_ea_header *ea_hdr_prev,
+				 void *private);
+
 extern int _fsck_blockmap_set(struct gfs2_inode *ip, uint64_t bblock,
 		       const char *btype, enum gfs2_mark_block mark,
 		       const char *caller, int line);
@@ -48,8 +60,6 @@ extern int write_new_leaf(struct gfs2_inode *dip, int start_lindex,
 			  uint64_t *bn);
 extern int repair_leaf(struct gfs2_inode *ip, uint64_t *leaf_no, int lindex,
 		       int ref_count, const char *msg);
-extern int free_block_if_notdup(struct gfs2_inode *ip, uint64_t block,
-				const char *btype);
 
 #define is_duplicate(dblock) ((dupfind(dblock)) ? 1 : 0)
 
@@ -83,8 +93,23 @@ struct metawalk_fxns {
 				 int ref_count, struct gfs2_buffer_head *lbh);
 	int (*check_leaf) (struct gfs2_inode *ip, uint64_t block,
 			   void *private);
+	/* parameters to the check_metalist sub-functions:
+	   ip: incore inode pointer
+	   block: block number of the metadata block to be checked
+	   bh: buffer_head to be returned
+	   h: height
+	   is_valid: returned as 1 if the metadata block is valid and should
+	             be added to the metadata list for further processing.
+	   was_duplicate: returns as 1 if the metadata block was determined
+	             to be a duplicate reference, in which case we want to
+		     skip adding it to the metadata list.
+	   private: Pointer to pass-specific data
+	   returns: 0 - everything is good, but there may be duplicates
+	            1 - skip further processing
+	*/
 	int (*check_metalist) (struct gfs2_inode *ip, uint64_t block,
 			       struct gfs2_buffer_head **bh, int h,
+			       int *is_valid, int *was_duplicate,
 			       void *private);
 	int (*check_data) (struct gfs2_inode *ip, uint64_t metablock,
 			   uint64_t block, void *private);
diff --git a/gfs2/fsck/pass1.c b/gfs2/fsck/pass1.c
index df10089..ee7e2c5 100644
--- a/gfs2/fsck/pass1.c
+++ b/gfs2/fsck/pass1.c
@@ -37,7 +37,8 @@ struct block_count {
 
 static int p1check_leaf(struct gfs2_inode *ip, uint64_t block, void *private);
 static int check_metalist(struct gfs2_inode *ip, uint64_t block,
-			  struct gfs2_buffer_head **bh, int h, void *private);
+			  struct gfs2_buffer_head **bh, int h, int *is_valid,
+			  int *was_duplicate, void *private);
 static int undo_check_metalist(struct gfs2_inode *ip, uint64_t block,
 			       int h, void *private);
 static int check_data(struct gfs2_inode *ip, uint64_t metablock,
@@ -64,6 +65,7 @@ static int finish_eattr_indir(struct gfs2_inode *ip, int leaf_pointers,
 			      int leaf_pointer_errors, void *private);
 static int invalidate_metadata(struct gfs2_inode *ip, uint64_t block,
 			       struct gfs2_buffer_head **bh, int h,
+			       int *is_valid, int *was_duplicate,
 			       void *private);
 static int invalidate_leaf(struct gfs2_inode *ip, uint64_t block,
 			   void *private);
@@ -127,10 +129,13 @@ struct metawalk_fxns invalidate_fxns = {
  */
 static int resuscitate_metalist(struct gfs2_inode *ip, uint64_t block,
 				struct gfs2_buffer_head **bh, int h,
+				int *is_valid, int *was_duplicate,
 				void *private)
 {
 	struct block_count *bc = (struct block_count *)private;
 
+	*is_valid = 1;
+	*was_duplicate = 0;
 	*bh = NULL;
 	if (!valid_block(ip->i_sbd, block)){ /* blk outside of FS */
 		fsck_blockmap_set(ip, ip->i_di.di_num.no_addr,
@@ -139,7 +144,8 @@ static int resuscitate_metalist(struct gfs2_inode *ip, uint64_t block,
 			   "range) found in system inode %lld (0x%llx).\n"),
 			 (unsigned long long)ip->i_di.di_num.no_addr,
 			 (unsigned long long)ip->i_di.di_num.no_addr);
-		return meta_skip_further;
+		*is_valid = 0;
+		return meta_is_good;
 	}
 	if (fsck_system_inode(ip->i_sbd, block))
 		fsck_blockmap_set(ip, block, _("system file"), gfs2_indir_blk);
@@ -241,16 +247,19 @@ static int p1check_leaf(struct gfs2_inode *ip, uint64_t block, void *private)
 }
 
 static int check_metalist(struct gfs2_inode *ip, uint64_t block,
-			  struct gfs2_buffer_head **bh, int h, void *private)
+			  struct gfs2_buffer_head **bh, int h, int *is_valid,
+			  int *was_duplicate, void *private)
 {
 	uint8_t q;
-	int found_dup = 0, iblk_type;
+	int iblk_type;
 	struct gfs2_buffer_head *nbh;
 	struct block_count *bc = (struct block_count *)private;
 	const char *blktypedesc;
 
 	*bh = NULL;
 
+	*was_duplicate = 0;
+	*is_valid = 0;
 	if (!valid_block(ip->i_sbd, block)) { /* blk outside of FS */
 		/* The bad dinode should be invalidated later due to
 		   "unrecoverable" errors.  The inode itself should be
@@ -282,12 +291,13 @@ static int check_metalist(struct gfs2_inode *ip, uint64_t block,
 			 (unsigned long long)ip->i_di.di_num.no_addr,
 			 (unsigned long long)ip->i_di.di_num.no_addr, q,
 			 block_type_string(q));
-		add_duplicate_ref(ip, block, ref_as_meta, 0, INODE_VALID);
-		found_dup = 1;
+		*was_duplicate = 1;
 	}
 	nbh = bread(ip->i_sbd, block);
 
-	if (gfs2_check_meta(nbh, iblk_type)){
+	*is_valid = (gfs2_check_meta(nbh, iblk_type) == 0);
+
+	if (!(*is_valid)) {
 		log_err( _("Inode %lld (0x%llx) has a bad indirect block "
 			   "pointer %lld (0x%llx) (points to something "
 			   "that is not %s).\n"),
@@ -295,31 +305,23 @@ static int check_metalist(struct gfs2_inode *ip, uint64_t block,
 			 (unsigned long long)ip->i_di.di_num.no_addr,
 			 (unsigned long long)block,
 			 (unsigned long long)block, blktypedesc);
-		if (!found_dup) {
-			fsck_blockmap_set(ip, block, _("bad indirect"),
-					  gfs2_meta_inval);
-			brelse(nbh);
-			nbh = NULL;
-			return meta_skip_further;
-		}
 		brelse(nbh);
-		nbh = NULL;
-	} else /* blk check ok */
-		*bh = nbh;
+		return meta_skip_further;
+	}
 
 	bc->indir_count++;
-	if (found_dup) {
-		if (nbh) {
-			brelse(nbh);
-			nbh = NULL;
-			*bh = NULL;
-		}
-		return meta_skip_further; /* don't process the metadata again */
-	} else
-		fsck_blockmap_set(ip, block, _("indirect"),
-				  gfs2_indir_blk);
+	if (*was_duplicate) {
+		add_duplicate_ref(ip, block, ref_as_meta, 0,
+				  *is_valid ? INODE_VALID : INODE_INVALID);
+		brelse(nbh);
+	} else {
+		*bh = nbh;
+		fsck_blockmap_set(ip, block, _("indirect"), gfs2_indir_blk);
+	}
 
-	return meta_is_good;
+	if (*is_valid)
+		return meta_is_good;
+	return meta_skip_further;
 }
 
 /* undo_reference - undo previously processed data or metadata
@@ -354,7 +356,7 @@ static int undo_reference(struct gfs2_inode *ip, uint64_t block, int meta,
 			if (!id)
 				break;
 
-			dup_listent_delete(id);
+			dup_listent_delete(dt, id);
 		} while (id);
 
 		if (dt->refs) {
@@ -827,7 +829,8 @@ static int check_eattr_entries(struct gfs2_inode *ip,
  * delete_block_if_notdup.
  */
 static int mark_block_invalid(struct gfs2_inode *ip, uint64_t block,
-			      enum dup_ref_type reftype, const char *btype)
+			      enum dup_ref_type reftype, const char *btype,
+			      int *is_valid, int *was_duplicate)
 {
 	uint8_t q;
 
@@ -838,11 +841,20 @@ static int mark_block_invalid(struct gfs2_inode *ip, uint64_t block,
 	 * referenced elsewhere (duplicates) won't be flagged as such,
 	 * and as a result, they'll be freed when this dinode is deleted,
 	 * despite being used by another dinode as a valid block. */
-	if (!valid_block(ip->i_sbd, block))
+	if (is_valid)
+		*is_valid = 1;
+	if (was_duplicate)
+		*was_duplicate = 0;
+	if (!valid_block(ip->i_sbd, block)) {
+		if (is_valid)
+			*is_valid = 0;
 		return meta_is_good;
+	}
 
 	q = block_type(block);
 	if (q != gfs2_block_free) {
+		if (was_duplicate)
+			*was_duplicate = 1;
 		add_duplicate_ref(ip, block, reftype, 0, INODE_INVALID);
 		log_info( _("%s block %lld (0x%llx), part of inode "
 			    "%lld (0x%llx), was previously referenced so "
@@ -859,21 +871,27 @@ static int mark_block_invalid(struct gfs2_inode *ip, uint64_t block,
 
 static int invalidate_metadata(struct gfs2_inode *ip, uint64_t block,
 			       struct gfs2_buffer_head **bh, int h,
+			       int *is_valid, int *was_duplicate,
 			       void *private)
 {
-	return mark_block_invalid(ip, block, ref_as_meta, _("metadata"));
+	*is_valid = 1;
+	*was_duplicate = 0;
+	return mark_block_invalid(ip, block, ref_as_meta, _("metadata"),
+				  is_valid, was_duplicate);
 }
 
 static int invalidate_leaf(struct gfs2_inode *ip, uint64_t block,
 			   void *private)
 {
-	return mark_block_invalid(ip, block, ref_as_meta, _("leaf"));
+	return mark_block_invalid(ip, block, ref_as_meta, _("leaf"),
+				  NULL, NULL);
 }
 
 static int invalidate_data(struct gfs2_inode *ip, uint64_t metablock,
 			   uint64_t block, void *private)
 {
-	return mark_block_invalid(ip, block, ref_as_data, _("data"));
+	return mark_block_invalid(ip, block, ref_as_data, _("data"),
+				  NULL, NULL);
 }
 
 static int invalidate_eattr_indir(struct gfs2_inode *ip, uint64_t block,
@@ -881,7 +899,8 @@ static int invalidate_eattr_indir(struct gfs2_inode *ip, uint64_t block,
 				  struct gfs2_buffer_head **bh, void *private)
 {
 	return mark_block_invalid(ip, block, ref_as_ea,
-				  _("indirect extended attribute"));
+				  _("indirect extended attribute"),
+				  NULL, NULL);
 }
 
 static int invalidate_eattr_leaf(struct gfs2_inode *ip, uint64_t block,
@@ -889,7 +908,8 @@ static int invalidate_eattr_leaf(struct gfs2_inode *ip, uint64_t block,
 				 void *private)
 {
 	return mark_block_invalid(ip, block, ref_as_ea,
-				  _("extended attribute"));
+				  _("extended attribute"),
+				  NULL, NULL);
 }
 
 /**
@@ -924,7 +944,7 @@ static int rangecheck_block(struct gfs2_inode *ip, uint64_t block,
 			  (unsigned long long)ip->i_di.di_num.no_addr,
 			  (unsigned long long)ip->i_di.di_num.no_addr);
 		if ((*bad_pointers) <= BAD_POINTER_TOLERANCE)
-			return meta_skip_further;
+			return meta_is_good;
 		else
 			return meta_error; /* Exits check_metatree quicker */
 	}
@@ -939,7 +959,7 @@ static int rangecheck_block(struct gfs2_inode *ip, uint64_t block,
 			  (unsigned long long)ip->i_di.di_num.no_addr,
 			  (unsigned long long)ip->i_di.di_num.no_addr);
 		if ((*bad_pointers) <= BAD_POINTER_TOLERANCE)
-			return meta_skip_further;
+			return meta_is_good;
 		else
 			return meta_error; /* Exits check_metatree quicker */
 	}
@@ -948,8 +968,11 @@ static int rangecheck_block(struct gfs2_inode *ip, uint64_t block,
 
 static int rangecheck_metadata(struct gfs2_inode *ip, uint64_t block,
 			       struct gfs2_buffer_head **bh, int h,
+			       int *is_valid, int *was_duplicate,
 			       void *private)
 {
+	*is_valid = 1;
+	*was_duplicate = 0;
 	return rangecheck_block(ip, block, bh, btype_meta, private);
 }
 
@@ -1048,7 +1071,7 @@ static int handle_ip(struct gfs2_sbd *sdp, struct gfs2_inode *ip)
 
 	/* We there was an error, we return 0 because we want fsck to continue
 	   and analyze the other dinodes as well. */
-	if (fsck_abort || error != 0)
+	if (fsck_abort)
 		return 0;
 
 	error = check_inode_eattr(ip, &pass1_fxns);
diff --git a/gfs2/fsck/pass1b.c b/gfs2/fsck/pass1b.c
index b5da200..15a3f3a 100644
--- a/gfs2/fsck/pass1b.c
+++ b/gfs2/fsck/pass1b.c
@@ -23,386 +23,10 @@ struct fxn_info {
 
 struct dup_handler {
 	struct duptree *dt;
-	struct inode_with_dups *id;
 	int ref_inode_count;
 	int ref_count;
 };
 
-static int check_leaf_refs(struct gfs2_inode *ip, uint64_t block, void *private);
-static int check_metalist(struct gfs2_inode *ip, uint64_t block,
-			  struct gfs2_buffer_head **bh, int h, void *private);
-static int check_data(struct gfs2_inode *ip, uint64_t metablock,
-		      uint64_t block, void *private);
-static int check_eattr_indir(struct gfs2_inode *ip, uint64_t block,
-			     uint64_t parent, struct gfs2_buffer_head **bh,
-			     void *private);
-static int check_eattr_leaf(struct gfs2_inode *ip, uint64_t block,
-			    uint64_t parent, struct gfs2_buffer_head **bh,
-			    void *private);
-static int check_eattr_entry(struct gfs2_inode *ip,
-			     struct gfs2_buffer_head *leaf_bh,
-			     struct gfs2_ea_header *ea_hdr,
-			     struct gfs2_ea_header *ea_hdr_prev,
-			     void *private);
-static int check_eattr_extentry(struct gfs2_inode *ip, uint64_t *ea_data_ptr,
-				struct gfs2_buffer_head *leaf_bh,
-				struct gfs2_ea_header *ea_hdr,
-				struct gfs2_ea_header *ea_hdr_prev,
-				void *private);
-static int find_dentry(struct gfs2_inode *ip, struct gfs2_dirent *de,
-		       struct gfs2_dirent *prev, struct gfs2_buffer_head *bh,
-		       char *filename, uint32_t *count, int lindex,
-		       void *priv);
-
-struct metawalk_fxns find_refs = {
-	.private = NULL,
-	.check_leaf = check_leaf_refs,
-	.check_metalist = check_metalist,
-	.check_data = check_data,
-	.check_eattr_indir = check_eattr_indir,
-	.check_eattr_leaf = check_eattr_leaf,
-	.check_dentry = NULL,
-	.check_eattr_entry = check_eattr_entry,
-	.check_eattr_extentry = check_eattr_extentry,
-};
-
-struct metawalk_fxns find_dirents = {
-	.private = NULL,
-	.check_leaf = NULL,
-	.check_metalist = NULL,
-	.check_data = NULL,
-	.check_eattr_indir = NULL,
-	.check_eattr_leaf = NULL,
-	.check_dentry = find_dentry,
-	.check_eattr_entry = NULL,
-	.check_eattr_extentry = NULL,
-};
-
-static int check_leaf_refs(struct gfs2_inode *ip, uint64_t block, void *private)
-{
-	return add_duplicate_ref(ip, block, ref_as_meta, 1, INODE_VALID);
-}
-
-static int check_metalist(struct gfs2_inode *ip, uint64_t block,
-			  struct gfs2_buffer_head **bh, int h, void *private)
-{
-	return add_duplicate_ref(ip, block, ref_as_meta, 1, INODE_VALID);
-}
-
-static int check_data(struct gfs2_inode *ip, uint64_t metablock,
-		      uint64_t block, void *private)
-{
-	return add_duplicate_ref(ip, block, ref_as_data, 1, INODE_VALID);
-}
-
-static int check_eattr_indir(struct gfs2_inode *ip, uint64_t block,
-			     uint64_t parent, struct gfs2_buffer_head **bh,
-			     void *private)
-{
-	struct gfs2_sbd *sdp = ip->i_sbd;
-	int error;
-
-	error = add_duplicate_ref(ip, block, ref_as_ea, 1, INODE_VALID);
-	if (!error)
-		*bh = bread(sdp, block);
-
-	return error;
-}
-
-static int check_eattr_leaf(struct gfs2_inode *ip, uint64_t block,
-			    uint64_t parent, struct gfs2_buffer_head **bh,
-			    void *private)
-{
-	struct gfs2_sbd *sdp = ip->i_sbd;
-	int error;
-
-	error = add_duplicate_ref(ip, block, ref_as_ea, 1, INODE_VALID);
-	if (!error)
-		*bh = bread(sdp, block);
-	return error;
-}
-
-static int check_eattr_entry(struct gfs2_inode *ip,
-			     struct gfs2_buffer_head *leaf_bh,
-			     struct gfs2_ea_header *ea_hdr,
-			     struct gfs2_ea_header *ea_hdr_prev, void *private)
-{
-	return 0;
-}
-
-static int check_eattr_extentry(struct gfs2_inode *ip, uint64_t *ea_data_ptr,
-				struct gfs2_buffer_head *leaf_bh,
-				struct gfs2_ea_header *ea_hdr,
-				struct gfs2_ea_header *ea_hdr_prev,
-				void *private)
-{
-	uint64_t block = be64_to_cpu(*ea_data_ptr);
-
-	return add_duplicate_ref(ip, block, ref_as_ea, 1, INODE_VALID);
-}
-
-/*
- * check_dir_dup_ref - check for a directory entry duplicate reference
- *                     and if found, set the name into the id.
- * Returns: 1 if filename was found, otherwise 0
- */
-static int check_dir_dup_ref(struct gfs2_inode *ip,  struct gfs2_dirent *de,
-			     osi_list_t *tmp2, char *filename)
-{
-	struct inode_with_dups *id;
-
-	id = osi_list_entry(tmp2, struct inode_with_dups, list);
-	if (id->name)
-		/* We can only have one parent of inodes that contain duplicate
-		 * blocks...no need to keep looking for this one. */
-		return 1;
-	if (id->block_no == de->de_inum.no_addr) {
-		id->name = strdup(filename);
-		id->parent = ip->i_di.di_num.no_addr;
-		log_debug( _("Duplicate block %llu (0x%llx"
-			     ") is in file or directory %llu"
-			     " (0x%llx) named %s\n"),
-			   (unsigned long long)id->block_no,
-			   (unsigned long long)id->block_no,
-			   (unsigned long long)ip->i_di.di_num.no_addr,
-			   (unsigned long long)ip->i_di.di_num.no_addr,
-			   filename);
-		/* If there are duplicates of duplicates, I guess we'll miss
-		   them here. */
-		return 1;
-	}
-	return 0;
-}
-
-static int find_dentry(struct gfs2_inode *ip, struct gfs2_dirent *de,
-		       struct gfs2_dirent *prev,
-		       struct gfs2_buffer_head *bh, char *filename,
-		       uint32_t *count, int lindex, void *priv)
-{
-	struct osi_node *n, *next = NULL;
-	osi_list_t *tmp2;
-	struct duptree *dt;
-	int found;
-
-	for (n = osi_first(&dup_blocks); n; n = next) {
-		next = osi_next(n);
-		dt = (struct duptree *)n;
-		found = 0;
-		osi_list_foreach(tmp2, &dt->ref_invinode_list) {
-			if (check_dir_dup_ref(ip, de, tmp2, filename)) {
-				found = 1;
-				break;
-			}
-		}
-		if (!found) {
-			osi_list_foreach(tmp2, &dt->ref_inode_list) {
-				if (check_dir_dup_ref(ip, de, tmp2, filename))
-					break;
-			}
-		}
-	}
-	/* Return the number of leaf entries so metawalk doesn't flag this
-	   leaf as having none. */
-	*count = be16_to_cpu(((struct gfs2_leaf *)bh->b_data)->lf_entries);
-	return 0;
-}
-
-static int clear_dup_metalist(struct gfs2_inode *ip, uint64_t block,
-			      struct gfs2_buffer_head **bh, int h,
-			      void *private)
-{
-	struct dup_handler *dh = (struct dup_handler *) private;
-	struct duptree *dt;
-
-	if (!valid_block(ip->i_sbd, block))
-		return meta_is_good;
-
-	/* This gets tricky. We're traversing a metadata tree trying to
-	   delete an inode based on it having a duplicate block reference
-	   somewhere in its metadata.  We know this block is listed as data
-	   or metadata for this inode, but it may or may not be one of the
-	   actual duplicate references that caused the problem.  If it's not
-	   a duplicate, it's normal metadata that isn't referenced anywhere
-	   else, but we're deleting the inode out from under it, so we need
-	   to delete it altogether. If the block is a duplicate referenced
-	   block, we need to keep its type intact and let the caller sort
-	   it out once we're down to a single reference. */
-	dt = dupfind(block);
-	if (!dt) {
-		fsck_blockmap_set(ip, block, _("no longer valid"),
-				  gfs2_block_free);
-		return meta_is_good;
-	}
-	/* This block, having failed the above test, is duplicated somewhere */
-	if (block == dh->dt->block) {
-		log_err( _("Not clearing duplicate reference in inode \"%s\" "
-			   "at block #%llu (0x%llx) to block #%llu (0x%llx) "
-			   "because it's valid for another inode.\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 %llu (0x%llx)\n"),
-			 dh->id->name ? dh->id->name : "",
-			 (unsigned long long)dh->id->parent,
-			 (unsigned long long)dh->id->parent);
-	}
-	/* We return 1 not 0 because we need build_and_check_metalist to
-	   bypass adding the metadata below it to the metalist.  If that
-	   were to happen, all the indirect blocks pointed to by the
-	   duplicate block would be processed twice, which means it might
-	   be mistakenly freed as "no longer valid" (in this function above)
-	   even though it's valid metadata for a different inode. Returning
-	   1 ensures that the metadata isn't processed again. */
-	return meta_skip_further;
-}
-
-static int clear_dup_data(struct gfs2_inode *ip, uint64_t metablock,
-			  uint64_t block, void *private)
-{
-	return clear_dup_metalist(ip, block, NULL, 0, private);
-}
-
-static int clear_leaf(struct gfs2_inode *ip, uint64_t block, void *private)
-{
-	return clear_dup_metalist(ip, block, NULL, 0, private);
-}
-
-static int clear_dup_eattr_indir(struct gfs2_inode *ip, uint64_t block,
-				 uint64_t parent, struct gfs2_buffer_head **bh,
-				 void *private)
-{
-	return clear_dup_metalist(ip, block, NULL, 0, private);
-}
-
-static int clear_dup_eattr_leaf(struct gfs2_inode *ip, uint64_t block,
-				uint64_t parent, struct gfs2_buffer_head **bh,
-				void *private)
-{
-	return clear_dup_metalist(ip, block, NULL, 0, private);
-}
-
-static int clear_eattr_entry (struct gfs2_inode *ip,
-		       struct gfs2_buffer_head *leaf_bh,
-		       struct gfs2_ea_header *ea_hdr,
-		       struct gfs2_ea_header *ea_hdr_prev,
-		       void *private)
-{
-	struct gfs2_sbd *sdp = ip->i_sbd;
-	char ea_name[256];
-
-	if (!ea_hdr->ea_name_len){
-		/* Skip this entry for now */
-		return 1;
-	}
-
-	memset(ea_name, 0, sizeof(ea_name));
-	strncpy(ea_name, (char *)ea_hdr + sizeof(struct gfs2_ea_header),
-		ea_hdr->ea_name_len);
-
-	if (!GFS2_EATYPE_VALID(ea_hdr->ea_type) &&
-	   ((ea_hdr_prev) || (!ea_hdr_prev && ea_hdr->ea_type))){
-		/* Skip invalid entry */
-		return 1;
-	}
-
-	if (ea_hdr->ea_num_ptrs){
-		uint32_t avail_size;
-		int max_ptrs;
-
-		avail_size = sdp->sd_sb.sb_bsize - sizeof(struct gfs2_meta_header);
-		max_ptrs = (be32_to_cpu(ea_hdr->ea_data_len) + avail_size - 1) /
-			avail_size;
-
-		if (max_ptrs > ea_hdr->ea_num_ptrs)
-			return 1;
-		else {
-			log_debug( _("  Pointers Required: %d\n  Pointers Reported: %d\n"),
-					  max_ptrs, ea_hdr->ea_num_ptrs);
-		}
-	}
-	return 0;
-}
-
-static int clear_eattr_extentry(struct gfs2_inode *ip, uint64_t *ea_data_ptr,
-				struct gfs2_buffer_head *leaf_bh,
-				struct gfs2_ea_header *ea_hdr,
-				struct gfs2_ea_header *ea_hdr_prev,
-				void *private)
-{
-	uint64_t block = be64_to_cpu(*ea_data_ptr);
-
-	return clear_dup_metalist(ip, block, NULL, 0, private);
-}
-
-/* Finds all references to duplicate blocks in the metadata */
-static int find_block_ref(struct gfs2_sbd *sdp, uint64_t inode)
-{
-	struct gfs2_inode *ip;
-	int error = 0;
-
-	ip = fsck_load_inode(sdp, inode); /* bread, inode_get */
-	/* double-check the meta header just to be sure it's metadata */
-	if (ip->i_di.di_header.mh_magic != GFS2_MAGIC ||
-	    ip->i_di.di_header.mh_type != GFS2_METATYPE_DI) {
-		log_debug( _("Block %lld (0x%llx) is not gfs2 metadata.\n"),
-			     (unsigned long long)inode,
-			     (unsigned long long)inode);
-		fsck_inode_put(&ip);
-		return 1;
-	}
-	/* Check to see if this inode was referenced by another by mistake */
-	add_duplicate_ref(ip, inode, ref_is_inode, 1, INODE_VALID);
-
-	/* Check this dinode's metadata for references to known duplicates */
-	error = check_metatree(ip, &find_refs);
-	if (error < 0) {
-		stack;
-		fsck_inode_put(&ip); /* out, brelse, free */
-		return error;
-	}
-
-	/* Exhash dir leafs will be checked by check_metatree (right after
-	   the "end:" label.)  But if this is a linear directory we need to
-	   check the dir with check_linear_dir. */
-	if (is_dir(&ip->i_di, sdp->gfs1) &&
-	   !(ip->i_di.di_flags & GFS2_DIF_EXHASH))
-		error = check_linear_dir(ip, ip->i_bh, &find_dirents);
-
-	/* Check for ea references in the inode */
-	if (!error)
-		error = check_inode_eattr(ip, &find_refs);
-
-	fsck_inode_put(&ip); /* out, brelse, free */
-
-	return error;
-}
-
-/* get_ref_type - figure out if all duplicate references from this inode
-   are the same type, and if so, return the type. */
-static enum dup_ref_type get_ref_type(struct inode_with_dups *id)
-{
-	enum dup_ref_type t, i;
-	int found_type_with_ref;
-	int found_other_types;
-
-	for (t = ref_as_data; t < ref_types; t++) {
-		found_type_with_ref = 0;
-		found_other_types = 0;
-		for (i = ref_as_data; i < ref_types; i++) {
-			if (id->reftypecount[i]) {
-				if (t == i)
-					found_type_with_ref = 1;
-				else
-					found_other_types = 1;
-			}
-		}
-		if (found_type_with_ref)
-			return found_other_types ? ref_types : t;
-	}
-	return ref_types;
-}
-
 static void log_inode_reference(struct duptree *dt, osi_list_t *tmp, int inval)
 {
 	char reftypestring[32];
@@ -426,12 +50,74 @@ static void log_inode_reference(struct duptree *dt, osi_list_t *tmp, int inval)
 		  (unsigned long long)dt->block,
 		  (unsigned long long)dt->block, reftypestring);
 }
+
+/* delete_all_dups - delete all duplicate records for a given inode */
+static void delete_all_dups(struct gfs2_inode *ip)
+{
+	struct osi_node *n, *next;
+	struct duptree *dt;
+	osi_list_t *tmp, *x;
+	struct inode_with_dups *id;
+	int found;
+
+	for (n = osi_first(&dup_blocks); n; n = next) {
+		next = osi_next(n);
+		dt = (struct duptree *)n;
+
+		found = 0;
+		id = NULL;
+
+		osi_list_foreach_safe(tmp, &dt->ref_invinode_list, x) {
+			id = osi_list_entry(tmp, struct inode_with_dups, list);
+			if (id->block_no == ip->i_di.di_num.no_addr) {
+				dup_listent_delete(dt, id);
+				found = 1;
+			}
+		}
+		osi_list_foreach_safe(tmp, &dt->ref_inode_list, x) {
+			id = osi_list_entry(tmp, struct inode_with_dups, list);
+			if (id->block_no == ip->i_di.di_num.no_addr) {
+				dup_listent_delete(dt, id);
+				found = 1;
+			}
+		}
+		if (!found)
+			continue;
+
+		if (dt->refs == 0) {
+			log_debug(_("This was the last reference: 0x%llx is "
+				    "no longer a duplicate.\n"),
+				  (unsigned long long)dt->block);
+			dup_delete(dt); /* not duplicate now */
+		} else {
+			log_debug(_("%d references remain to 0x%llx\n"),
+				  dt->refs, (unsigned long long)dt->block);
+			if (dt->refs > 1)
+				continue;
+
+			id = NULL;
+			osi_list_foreach(tmp, &dt->ref_invinode_list)
+				id = osi_list_entry(tmp,
+						    struct inode_with_dups,
+						    list);
+			osi_list_foreach(tmp, &dt->ref_inode_list)
+				id = osi_list_entry(tmp,
+						    struct inode_with_dups,
+						    list);
+			if (id)
+				log_debug("Last reference is from inode "
+					  "0x%llx\n",
+					  (unsigned long long)id->block_no);
+		}
+	}
+}
+
 /*
  * resolve_dup_references - resolve all but the last dinode that has a
  *                          duplicate reference to a given block.
  *
  * @sdp - pointer to the superblock structure
- * @b - pointer to the duplicate reference rbtree to use
+ * @dt - pointer to the duplicate reference rbtree to use
  * @ref_list - list of duplicate references to be resolved (invalid or valid)
  * @dh - duplicate handler
  * inval - The references on this ref_list are invalid.  We prefer to delete
@@ -439,40 +125,42 @@ static void log_inode_reference(struct duptree *dt, osi_list_t *tmp, int inval)
  * acceptable_ref - Delete dinodes that reference the given block as anything
  *                  _but_ this type.  Try to save references as this type.
  */
-static int resolve_dup_references(struct gfs2_sbd *sdp, struct duptree *dt,
-				  osi_list_t *ref_list, struct dup_handler *dh,
-				  int inval, int acceptable_ref)
+static void resolve_dup_references(struct gfs2_sbd *sdp, struct duptree *dt,
+				   osi_list_t *ref_list,
+				   struct dup_handler *dh,
+				   int inval, int acceptable_ref)
 {
 	struct gfs2_inode *ip;
 	struct inode_with_dups *id;
 	osi_list_t *tmp, *x;
-	struct metawalk_fxns clear_dup_fxns = {
+	struct metawalk_fxns pass1b_fxns_delete = {
 		.private = NULL,
-		.check_leaf = clear_leaf,
-		.check_metalist = clear_dup_metalist,
-		.check_data = clear_dup_data,
-		.check_eattr_indir = clear_dup_eattr_indir,
-		.check_eattr_leaf = clear_dup_eattr_leaf,
-		.check_dentry = NULL,
-		.check_eattr_entry = clear_eattr_entry,
-		.check_eattr_extentry = clear_eattr_extentry,
+		.check_metalist = delete_metadata,
+		.check_data = delete_data,
+		.check_leaf = delete_leaf,
+		.check_eattr_indir = delete_eattr_indir,
+		.check_eattr_leaf = delete_eattr_leaf,
+		.check_eattr_entry = delete_eattr_entry,
+		.check_eattr_extentry = delete_eattr_extentry,
 	};
 	enum dup_ref_type this_ref;
 	struct inode_info *ii;
 	int found_good_ref = 0;
+	uint64_t dup_block;
+	uint8_t q;
 
 	osi_list_foreach_safe(tmp, ref_list, x) {
 		if (skip_this_pass || fsck_abort)
-			return FSCK_OK;
+			return;
 
 		id = osi_list_entry(tmp, struct inode_with_dups, list);
 		dh->dt = dt;
-		dh->id = id;
 
 		if (dh->ref_inode_count == 1) /* down to the last reference */
-			return 1;
+			return;
 
 		this_ref = get_ref_type(id);
+		q = block_type(id->block_no);
 		if (inval)
 			log_warn( _("Invalid "));
 		/* FIXME: If we already found an acceptable reference to this
@@ -484,11 +172,8 @@ static int resolve_dup_references(struct gfs2_sbd *sdp, struct duptree *dt,
 						      type and */
 		    this_ref == acceptable_ref && /* this ref is acceptable */
 		    !found_good_ref) { /* We haven't found a good reference */
-			uint8_t q;
-
 			/* If this is an invalid inode, but not on the invalid
 			   list, it's better to delete it. */
-			q = block_type(id->block_no);
 			if (q != gfs2_inode_invalid) {
 				found_good_ref = 1;
 				log_warn( _("Inode %s (%lld/0x%llx)'s "
@@ -526,69 +211,124 @@ static int resolve_dup_references(struct gfs2_sbd *sdp, struct duptree *dt,
 			     (unsigned long long)id->block_no))) {
 			log_warn( _("The bad inode was not cleared."));
 			/* delete the list entry so we don't leak memory but
-			   leave the reference count.  If the decrement the
+			   leave the reference count. If we decrement the
 			   ref count, we could get down to 1 and the dinode
 			   would be changed without a 'Yes' answer. */
 			/* (dh->ref_inode_count)--;*/
-			dup_listent_delete(id);
+			dup_listent_delete(dt, id);
 			continue;
 		}
-		log_warn( _("Clearing inode %lld (0x%llx)...\n"),
-			  (unsigned long long)id->block_no,
-			  (unsigned long long)id->block_no);
-
+		if (q == gfs2_block_free)
+			log_warn( _("Inode %lld (0x%llx) was previously "
+				    "deleted.\n"),
+				  (unsigned long long)id->block_no,
+				  (unsigned long long)id->block_no);
+		else
+			log_warn(_("Pass1b is deleting inode %lld (0x%llx).\n"),
+				 (unsigned long long)id->block_no,
+				 (unsigned long long)id->block_no);
+
+		dup_block = id->block_no;
 		ip = fsck_load_inode(sdp, id->block_no);
-		if (id->reftypecount[ref_as_data] ||
-		    id->reftypecount[ref_as_meta]) {
-			ii = inodetree_find(ip->i_di.di_num.no_addr);
-			if (ii)
-				inodetree_delete(ii);
-		}
-		clear_dup_fxns.private = (void *) dh;
-		/* Clear the EAs for the inode first */
-		check_inode_eattr(ip, &clear_dup_fxns);
-		/* If the dup was in data or metadata, clear the dinode */
-		if (id->reftypecount[ref_as_data] ||
-		    id->reftypecount[ref_as_meta]) {
-			check_metatree(ip, &clear_dup_fxns);
-			fsck_blockmap_set(ip, ip->i_di.di_num.no_addr,
-					  _("duplicate referencing bad"),
-					  gfs2_inode_invalid);
+		/* If we've already deleted this dinode, don't try to delete
+		   it again. That could free blocks that used to be duplicate
+		   references that are now resolved (and gone). */
+		if (q != gfs2_block_free) {
+			/* Clear the EAs for the inode first */
+			check_inode_eattr(ip, &pass1b_fxns_delete);
+			/* If the reference was as metadata or data, we've got
+			   a corrupt dinode that will be deleted. */
+			if (inval || id->reftypecount[ref_as_data] ||
+			    id->reftypecount[ref_as_meta]) {
+				/* Remove the inode from the inode tree */
+				ii = inodetree_find(ip->i_di.di_num.no_addr);
+				if (ii)
+					inodetree_delete(ii);
+				fsck_blockmap_set(ip, ip->i_di.di_num.no_addr,
+						_("duplicate referencing bad"),
+						  gfs2_inode_invalid);
+				/* We delete the dup_handler inode count and
+				   duplicate id BEFORE clearing the metadata,
+				   because if this is the last reference to
+				   this metadata block, we need to traverse the
+				   tree and free the data blocks it references.
+				   However, we don't want to delete other
+				   duplicates that may be used by other
+				   dinodes. */
+				(dh->ref_inode_count)--;
+				/* FIXME: other option should be to duplicate
+				   the block for each duplicate and point the
+				   metadata at the cloned blocks */
+				check_metatree(ip, &pass1b_fxns_delete);
+			}
 		}
+		/* Now we've got to go through an delete any other duplicate
+		   references from this dinode we're deleting. If we don't,
+		   pass1b will discover the other duplicate record, try to
+		   delete this dinode a second time, and this time its earlier
+		   duplicate references won't be seen as duplicates anymore
+		   (because they were eliminated earlier in pass1b). And so
+		   the blocks will be mistakenly freed, when, in fact, they're
+		   still being referenced by a valid dinode. */
+		delete_all_dups(ip);
 		fsck_inode_put(&ip); /* out, brelse, free */
-		(dh->ref_inode_count)--;
-		/* FIXME: other option should be to duplicate the
-		 * block for each duplicate and point the metadata at
-		 * the cloned blocks */
-		dup_listent_delete(id);
 	}
-	if (dh->ref_inode_count == 1) /* down to the last reference */
-		return 1;
-	return 0;
+	return;
 }
 
-static int handle_dup_blk(struct gfs2_sbd *sdp, struct duptree *dt)
+/* revise_dup_handler - get current information about a duplicate reference
+ *
+ * Function resolve_dup_references can delete dinodes that reference blocks
+ * which may have duplicate references. Therefore, the duplicate tree is
+ * constantly being changed. This function revises the duplicate handler so
+ * that it accurately matches what's in the duplicate tree regarding this block
+ */
+static void revise_dup_handler(uint64_t dup_blk, struct dup_handler *dh)
 {
-	struct gfs2_inode *ip;
 	osi_list_t *tmp;
+	struct duptree *dt;
 	struct inode_with_dups *id;
-	struct dup_handler dh = {0};
-	int last_reference = 0;
-	struct gfs2_buffer_head *bh;
-	uint32_t cmagic, ctype;
-	enum dup_ref_type acceptable_ref;
 
+	dh->ref_inode_count = 0;
+	dh->ref_count = 0;
+	dh->dt = NULL;
+
+	dt = dupfind(dup_blk);
+	if (!dt)
+		return;
+
+	dh->dt = dt;
 	/* Count the duplicate references, both valid and invalid */
 	osi_list_foreach(tmp, &dt->ref_invinode_list) {
 		id = osi_list_entry(tmp, struct inode_with_dups, list);
-		dh.ref_inode_count++;
-		dh.ref_count += id->dup_count;
+		dh->ref_inode_count++;
+		dh->ref_count += id->dup_count;
 	}
 	osi_list_foreach(tmp, &dt->ref_inode_list) {
 		id = osi_list_entry(tmp, struct inode_with_dups, list);
-		dh.ref_inode_count++;
-		dh.ref_count += id->dup_count;
+		dh->ref_inode_count++;
+		dh->ref_count += id->dup_count;
 	}
+}
+
+/* handle_dup_blk - handle a duplicate block reference.
+ *
+ * This function should resolve and delete the duplicate block reference given,
+ * iow dt.
+ */
+static int handle_dup_blk(struct gfs2_sbd *sdp, struct duptree *dt)
+{
+	osi_list_t *tmp;
+	struct gfs2_inode *ip;
+	struct inode_with_dups *id;
+	struct dup_handler dh = {0};
+	struct gfs2_buffer_head *bh;
+	uint32_t cmagic, ctype;
+	enum dup_ref_type acceptable_ref;
+	uint64_t dup_blk;
+
+	dup_blk = dt->block;
+	revise_dup_handler(dup_blk, &dh);
 
 	/* Log the duplicate references */
 	log_notice( _("Block %llu (0x%llx) has %d inodes referencing it"
@@ -642,77 +382,67 @@ static int handle_dup_blk(struct gfs2_sbd *sdp, struct duptree *dt)
 	   invalidated for other reasons, such as bad pointers.  So we need to
 	   make sure at this point that any inode deletes reverse out any
 	   duplicate reference before we get to this point. */
-	if (dh.ref_count == 1)
-		last_reference = 1;
 
 	/* Step 1 - eliminate references from inodes that are not valid.
 	 *          This may be because they were deleted due to corruption.
 	 *          All block types are unacceptable, so we use ref_types.
 	 */
-	if (!last_reference) {
+	if (dh.ref_count > 1) {
 		log_debug( _("----------------------------------------------\n"
 			     "Step 1: Eliminate references to block %llu "
 			     "(0x%llx) that were previously marked "
 			     "invalid.\n"),
 			   (unsigned long long)dt->block,
 			   (unsigned long long)dt->block);
-		last_reference = resolve_dup_references(sdp, dt,
-							&dt->ref_invinode_list,
-							&dh, 1, ref_types);
+		resolve_dup_references(sdp, dt, &dt->ref_invinode_list,
+				       &dh, 1, ref_types);
+		revise_dup_handler(dup_blk, &dh);
 	}
 	/* Step 2 - eliminate reference from inodes that reference it as the
 	 *          wrong type.  For example, a data file referencing it as
 	 *          a data block, but it's really a metadata block.  Or a
 	 *          directory inode referencing a data block as a leaf block.
 	 */
-	if (!last_reference) {
+	if (dh.ref_count > 1) {
 		log_debug( _("----------------------------------------------\n"
 			     "Step 2: Eliminate references to block %llu "
 			     "(0x%llx) that need the wrong block type.\n"),
 			   (unsigned long long)dt->block,
 			   (unsigned long long)dt->block);
-		last_reference = resolve_dup_references(sdp, dt,
-							&dt->ref_inode_list,
-							&dh, 0,
-							acceptable_ref);
+		resolve_dup_references(sdp, dt, &dt->ref_inode_list, &dh, 0,
+				       acceptable_ref);
+		revise_dup_handler(dup_blk, &dh);
 	}
 	/* Step 3 - We have multiple dinodes referencing it as the correct
 	 *          type.  Just blast one of them.
 	 *          All block types are fair game, so we use ref_types.
 	 */
-	if (!last_reference) {
+	if (dh.ref_count > 1) {
 		log_debug( _("----------------------------------------------\n"
 			     "Step 3: Choose one reference to block %llu "
 			     "(0x%llx) to keep.\n"),
 			   (unsigned long long)dt->block,
 			   (unsigned long long)dt->block);
-		last_reference = resolve_dup_references(sdp, dt,
-							&dt->ref_inode_list,
-							&dh, 0, ref_types);
-	}
-	/* Now fix the block type of the block in question. */
-	if (osi_list_empty(&dt->ref_inode_list)) {
-		log_notice( _("Block %llu (0x%llx) has no more references; "
-			      "Marking as 'free'.\n"),
-			    (unsigned long long)dt->block,
-			    (unsigned long long)dt->block);
-		gfs2_blockmap_set(bl, dt->block, gfs2_block_free);
-		check_n_fix_bitmap(sdp, dt->block, gfs2_block_free);
-		return 0;
+		resolve_dup_references(sdp, dt, &dt->ref_inode_list, &dh, 0,
+				       ref_types);
+		revise_dup_handler(dup_blk, &dh);
 	}
-	if (last_reference) {
+	/* If there's still a last remaining reference, and it's a valid
+	   reference, use it to determine the correct block type for our
+	   blockmap and bitmap. */
+	if (dh.ref_count == 1 && !osi_list_empty(&dt->ref_inode_list)) {
 		uint8_t q;
 
 		log_notice( _("Block %llu (0x%llx) has only one remaining "
-			      "reference.\n"),
-			    (unsigned long long)dt->block,
-			    (unsigned long long)dt->block);
+			      "valid reference.\n"),
+			    (unsigned long long)dup_blk,
+			    (unsigned long long)dup_blk);
 		/* If we're down to a single reference (and not all references
 		   deleted, which may be the case of an inode that has only
 		   itself and a reference), we need to reset the block type
 		   from invalid to data or metadata. Start at the first one
 		   in the list, not the structure's place holder. */
-		tmp = (&dt->ref_inode_list)->next;
+		tmp = dt->ref_inode_list.next;
 		id = osi_list_entry(tmp, struct inode_with_dups, list);
 		log_debug( _("----------------------------------------------\n"
 			     "Step 4. Set block type based on the remaining "
@@ -753,13 +483,147 @@ static int handle_dup_blk(struct gfs2_sbd *sdp, struct duptree *dt)
 					    "attribute"),
 					  gfs2_meta_eattr);
 		fsck_inode_put(&ip); /* out, brelse, free */
+		log_debug(_("Done with duplicate reference to block 0x%llx\n"),
+			  (unsigned long long)dt->block);
+		dup_delete(dt);
 	} else {
 		/* They may have answered no and not fixed all references. */
-		log_debug( _("All duplicate references were processed.\n"));
+		log_debug( _("All duplicate references to block 0x%llx were "
+			     "processed.\n"), (unsigned long long)dup_blk);
+		if (dh.ref_count) {
+			log_debug(_("Done with duplicate reference to block "
+				    "0x%llx, but %d references remain.\n"),
+				  (unsigned long long)dup_blk, dh.ref_count);
+		} else {
+			log_notice( _("Block %llu (0x%llx) has no more "
+				      "references; Marking as 'free'.\n"),
+				    (unsigned long long)dup_blk,
+				    (unsigned long long)dup_blk);
+			if (dh.dt)
+				dup_delete(dh.dt);
+			/* Now fix the block type of the block in question. */
+			gfs2_blockmap_set(bl, dup_blk, gfs2_block_free);
+			check_n_fix_bitmap(sdp, dup_blk, gfs2_block_free);
+		}
 	}
 	return 0;
 }
 
+static int check_leaf_refs(struct gfs2_inode *ip, uint64_t block,
+			   void *private)
+{
+	return add_duplicate_ref(ip, block, ref_as_meta, 1, INODE_VALID);
+}
+
+static int check_metalist_refs(struct gfs2_inode *ip, uint64_t block,
+			       struct gfs2_buffer_head **bh, int h,
+			       int *is_valid, int *was_duplicate,
+			       void *private)
+{
+	*was_duplicate = 0;
+	*is_valid = 1;
+	return add_duplicate_ref(ip, block, ref_as_meta, 1, INODE_VALID);
+}
+
+static int check_data_refs(struct gfs2_inode *ip, uint64_t metablock,
+			   uint64_t block, void *private)
+{
+	return add_duplicate_ref(ip, block, ref_as_data, 1, INODE_VALID);
+}
+
+static int check_eattr_indir_refs(struct gfs2_inode *ip, uint64_t block,
+				  uint64_t parent,
+				  struct gfs2_buffer_head **bh, void *private)
+{
+	struct gfs2_sbd *sdp = ip->i_sbd;
+	int error;
+
+	error = add_duplicate_ref(ip, block, ref_as_ea, 1, INODE_VALID);
+	if (!error)
+		*bh = bread(sdp, block);
+
+	return error;
+}
+
+static int check_eattr_leaf_refs(struct gfs2_inode *ip, uint64_t block,
+				 uint64_t parent, struct gfs2_buffer_head **bh,
+				 void *private)
+{
+	struct gfs2_sbd *sdp = ip->i_sbd;
+	int error;
+
+	error = add_duplicate_ref(ip, block, ref_as_ea, 1, INODE_VALID);
+	if (!error)
+		*bh = bread(sdp, block);
+	return error;
+}
+
+static int check_eattr_entry_refs(struct gfs2_inode *ip,
+				  struct gfs2_buffer_head *leaf_bh,
+				  struct gfs2_ea_header *ea_hdr,
+				  struct gfs2_ea_header *ea_hdr_prev,
+				  void *private)
+{
+	return 0;
+}
+
+static int check_eattr_extentry_refs(struct gfs2_inode *ip,
+				     uint64_t *ea_data_ptr,
+				     struct gfs2_buffer_head *leaf_bh,
+				     struct gfs2_ea_header *ea_hdr,
+				     struct gfs2_ea_header *ea_hdr_prev,
+				     void *private)
+{
+	uint64_t block = be64_to_cpu(*ea_data_ptr);
+
+	return add_duplicate_ref(ip, block, ref_as_ea, 1, INODE_VALID);
+}
+
+/* Finds all references to duplicate blocks in the metadata */
+/* Finds all references to duplicate blocks in the metadata */
+static int find_block_ref(struct gfs2_sbd *sdp, uint64_t inode)
+{
+	struct gfs2_inode *ip;
+	int error = 0;
+	struct metawalk_fxns find_refs = {
+		.private = NULL,
+		.check_leaf = check_leaf_refs,
+		.check_metalist = check_metalist_refs,
+		.check_data = check_data_refs,
+		.check_eattr_indir = check_eattr_indir_refs,
+		.check_eattr_leaf = check_eattr_leaf_refs,
+		.check_eattr_entry = check_eattr_entry_refs,
+		.check_eattr_extentry = check_eattr_extentry_refs,
+	};
+
+	ip = fsck_load_inode(sdp, inode); /* bread, inode_get */
+
+	/* double-check the meta header just to be sure it's metadata */
+	if (ip->i_di.di_header.mh_magic != GFS2_MAGIC ||
+	    ip->i_di.di_header.mh_type != GFS2_METATYPE_DI) {
+		log_debug( _("Block %lld (0x%llx) is not gfs2 metadata.\n"),
+			     (unsigned long long)inode,
+			     (unsigned long long)inode);
+		error = 1;
+		goto out;
+	}
+	/* Check to see if this inode was referenced by another by mistake */
+	add_duplicate_ref(ip, inode, ref_is_inode, 1, INODE_VALID);
+
+	/* Check this dinode's metadata for references to known duplicates */
+	error = check_metatree(ip, &find_refs);
+	if (error < 0)
+		stack;
+
+	/* Check for ea references in the inode */
+	if (!error)
+		error = check_inode_eattr(ip, &find_refs);
+
+out:
+	fsck_inode_put(&ip); /* out, brelse, free */
+	return error;
+}
+
 /* Pass 1b handles finding the previous inode for a duplicate block
  * When found, store the inodes pointing to the duplicate block for
  * use in pass2 */
@@ -768,7 +632,7 @@ int pass1b(struct gfs2_sbd *sdp)
 	struct duptree *dt;
 	uint64_t i;
 	uint8_t q;
-	struct osi_node *n, *next = NULL;
+	struct osi_node *n;
 	int rc = FSCK_OK;
 
 	log_info( _("Looking for duplicate blocks...\n"));
@@ -819,17 +683,11 @@ int pass1b(struct gfs2_sbd *sdp)
 	 * it later */
 	log_info( _("Handling duplicate blocks\n"));
 out:
-        for (n = osi_first(&dup_blocks); n; n = next) {
-		next = osi_next(n);
+	/* Resolve all duplicates by clearing out the dup tree */
+        while ((n = osi_first(&dup_blocks))) {
                 dt = (struct duptree *)n;
 		if (!skip_this_pass && !rc) /* no error & not asked to skip the rest */
 			handle_dup_blk(sdp, dt);
-		/* Do not attempt to free the dup_blocks list or its parts
-		   here because any func that calls check_metatree needs
-		   to check duplicate status based on this linked list.
-		   This is especially true for pass2 where it may delete "bad"
-		   inodes, and we can't delete an inode's indirect block if
-		   it was a duplicate (therefore in use by another dinode). */
 	}
 	return rc;
 }
diff --git a/gfs2/fsck/pass2.c b/gfs2/fsck/pass2.c
index e38841e..8b38b43 100644
--- a/gfs2/fsck/pass2.c
+++ b/gfs2/fsck/pass2.c
@@ -169,59 +169,6 @@ static int check_file_type(uint8_t de_type, uint8_t blk_type, int gfs1)
 	return 0;
 }
 
-static int delete_eattr_entry (struct gfs2_inode *ip,
-			       struct gfs2_buffer_head *leaf_bh,
-			       struct gfs2_ea_header *ea_hdr,
-			       struct gfs2_ea_header *ea_hdr_prev,
-			       void *private)
-{
-	struct gfs2_sbd *sdp = ip->i_sbd;
-	char ea_name[256];
-
-	if (!ea_hdr->ea_name_len){
-		/* Skip this entry for now */
-		return 1;
-	}
-
-	memset(ea_name, 0, sizeof(ea_name));
-	strncpy(ea_name, (char *)ea_hdr + sizeof(struct gfs2_ea_header),
-		ea_hdr->ea_name_len);
-
-	if (!GFS2_EATYPE_VALID(ea_hdr->ea_type) &&
-	   ((ea_hdr_prev) || (!ea_hdr_prev && ea_hdr->ea_type))){
-		/* Skip invalid entry */
-		return 1;
-	}
-
-	if (ea_hdr->ea_num_ptrs){
-		uint32_t avail_size;
-		int max_ptrs;
-
-		avail_size = sdp->sd_sb.sb_bsize - sizeof(struct gfs2_meta_header);
-		max_ptrs = (be32_to_cpu(ea_hdr->ea_data_len) + avail_size - 1) /
-			avail_size;
-
-		if (max_ptrs > ea_hdr->ea_num_ptrs)
-			return 1;
-		else {
-			log_debug( _("  Pointers Required: %d\n  Pointers Reported: %d\n"),
-					  max_ptrs, ea_hdr->ea_num_ptrs);
-		}
-	}
-	return 0;
-}
-
-static int delete_eattr_extentry(struct gfs2_inode *ip, uint64_t *ea_data_ptr,
-				 struct gfs2_buffer_head *leaf_bh,
-				 struct gfs2_ea_header *ea_hdr,
-				 struct gfs2_ea_header *ea_hdr_prev,
-				 void *private)
-{
-	uint64_t block = be64_to_cpu(*ea_data_ptr);
-
-	return delete_metadata(ip, block, NULL, 0, private);
-}
-
 struct metawalk_fxns pass2_fxns_delete = {
 	.private = NULL,
 	.check_metalist = delete_metadata,
@@ -1836,12 +1783,5 @@ int pass2(struct gfs2_sbd *sdp)
 		}
 		fsck_inode_put(&ip); /* 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. */
-	gfs2_dup_free();
 	return FSCK_OK;
 }
diff --git a/gfs2/fsck/util.c b/gfs2/fsck/util.c
index ef59e6e..9d6f163 100644
--- a/gfs2/fsck/util.c
+++ b/gfs2/fsck/util.c
@@ -466,8 +466,39 @@ struct dir_info *dirtree_find(uint64_t block)
 	return NULL;
 }
 
-void dup_listent_delete(struct inode_with_dups *id)
+/* get_ref_type - figure out if all duplicate references from this inode
+   are the same type, and if so, return the type. */
+enum dup_ref_type get_ref_type(struct inode_with_dups *id)
 {
+	enum dup_ref_type t, i;
+	int found_type_with_ref;
+	int found_other_types;
+
+	for (t = ref_as_data; t < ref_types; t++) {
+		found_type_with_ref = 0;
+		found_other_types = 0;
+		for (i = ref_as_data; i < ref_types; i++) {
+			if (id->reftypecount[i]) {
+				if (t == i)
+					found_type_with_ref = 1;
+				else
+					found_other_types = 1;
+			}
+		}
+		if (found_type_with_ref)
+			return found_other_types ? ref_types : t;
+	}
+	return ref_types;
+}
+
+void dup_listent_delete(struct duptree *dt, struct inode_with_dups *id)
+{
+	log_err( _("Removing duplicate reference to block %llu (0x%llx) "
+		   "referenced as %s by dinode %llu (0x%llx)\n"),
+		 (unsigned long long)dt->block, (unsigned long long)dt->block,
+		 reftypes[get_ref_type(id)], (unsigned long long)id->block_no,
+		 (unsigned long long)id->block_no);
+	dt->refs--; /* one less reference */
 	if (id->name)
 		free(id->name);
 	osi_list_del(&id->list);
@@ -482,12 +513,12 @@ void dup_delete(struct duptree *dt)
 	while (!osi_list_empty(&dt->ref_invinode_list)) {
 		tmp = (&dt->ref_invinode_list)->next;
 		id = osi_list_entry(tmp, struct inode_with_dups, list);
-		dup_listent_delete(id);
+		dup_listent_delete(dt, id);
 	}
 	while (!osi_list_empty(&dt->ref_inode_list)) {
 		tmp = (&dt->ref_inode_list)->next;
 		id = osi_list_entry(tmp, struct inode_with_dups, list);
-		dup_listent_delete(id);
+		dup_listent_delete(dt, id);
 	}
 	osi_erase(&dt->node, &dup_blocks);
 	free(dt);
diff --git a/gfs2/fsck/util.h b/gfs2/fsck/util.h
index 00c2239..361b1a2 100644
--- a/gfs2/fsck/util.h
+++ b/gfs2/fsck/util.h
@@ -19,7 +19,7 @@ int add_duplicate_ref(struct gfs2_inode *ip, uint64_t block,
 		      enum dup_ref_type reftype, int first, int inode_valid);
 extern struct inode_with_dups *find_dup_ref_inode(struct duptree *dt,
 						  struct gfs2_inode *ip);
-extern void dup_listent_delete(struct inode_with_dups *id);
+extern void dup_listent_delete(struct duptree *dt, struct inode_with_dups *id);
 
 extern const char *reftypes[ref_types + 1];
 
@@ -174,6 +174,7 @@ static inline uint32_t gfs_to_gfs2_mode(struct gfs2_inode *ip)
 	}
 }
 
+extern enum dup_ref_type get_ref_type(struct inode_with_dups *id);
 extern struct gfs2_bmap *gfs2_bmap_create(struct gfs2_sbd *sdp, uint64_t size,
 					  uint64_t *addl_mem_needed);
 extern void *gfs2_bmap_destroy(struct gfs2_sbd *sdp, struct gfs2_bmap *il);
-- 
1.7.11.7




More information about the Cluster-devel mailing list