Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > 35adedb8830cf948b43b86231991124b > files > 51

gfs2-utils-0.1.62-39.el5.src.rpm

commit 424bf2626e6b4f6b935d200452774de6d898cbc4
Author: Bob Peterson <bob@ganesha.peterson>
Date:   Mon Jan 25 13:39:24 2010 -0600

    fsck.gfs2: Overhaul duplicate reference processing
    
    This patch is a major overhaul of the duplicate reference processing
    that had so many problems.  Before this patch, pass1 would flag a
    block as a duplicate reference, then pass1b would go to great lengths
    to find all dinodes that reference the block in question.  Then it
    tried to figure out which reference to keep and which to discard.
    This was slow and prone to errors.  With this patch, when pass1
    discovers a block that's already got a blocktype, it keeps tabs on
    that block and what dinodes reference it from then on.  That means
    pass1b only needs to find one reference: The original reference that
    caused it to become not free.  Once that is found, it can stop
    looking.  Furthermore, this patch introduces two linked lists of
    inode references: valid references and invalid references.  When
    an inode is determined to be bad or invalid, its duplicate block
    references are moved from the valid to the invalid reference list.
    That way, pass1b can favor the most valid references over the invalid
    references, and it's more likely to keep good data and discard trash.
    
    rhbz#455300

diff --git a/gfs2/fsck/fsck.h b/gfs2/fsck/fsck.h
index abcc476..ddfee17 100644
--- a/gfs2/fsck/fsck.h
+++ b/gfs2/fsck/fsck.h
@@ -83,7 +83,7 @@ struct inode_with_dups {
 	osi_list_t list;
 	uint64_t block_no;
 	int dup_count;
-	int ea_only;
+	int reftypecount[ref_types];
 	uint64_t parent;
 	char *name;
 };
@@ -138,5 +138,7 @@ extern uint64_t first_data_block;
 extern struct osi_root dup_blocks;
 extern struct osi_root dirtree;
 extern struct osi_root inodetree;
-
+extern int dups_found; /* How many duplicate references have we found? */
+extern int dups_found_first; /* How many duplicates have we found the original
+				reference for? */
 #endif /* _FSCK_H */
diff --git a/gfs2/fsck/main.c b/gfs2/fsck/main.c
index c1035da..1a06f53 100644
--- a/gfs2/fsck/main.c
+++ b/gfs2/fsck/main.c
@@ -45,6 +45,7 @@ int preen = 0, force_check = 0;
 struct osi_root dup_blocks = (struct osi_root) { NULL, };
 struct osi_root dirtree = (struct osi_root) { NULL, };
 struct osi_root inodetree = (struct osi_root) { NULL, };
+int dups_found = 0, dups_found_first = 0;
 
 /* This function is for libgfs2's sake.                                      */
 void print_it(const char *label, const char *fmt, const char *fmt2, ...)
diff --git a/gfs2/fsck/pass1.c b/gfs2/fsck/pass1.c
index 108a82e..5216519 100644
--- a/gfs2/fsck/pass1.c
+++ b/gfs2/fsck/pass1.c
@@ -142,7 +142,7 @@ 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));
-		gfs2_dup_set(block);
+		add_duplicate_ref(ip, block, ref_as_meta, 0, INODE_VALID);
 		found_dup = 1;
 	}
 	nbh = bread(ip->i_sbd, block);
@@ -165,10 +165,12 @@ static int check_metalist(struct gfs2_inode *ip, uint64_t block,
 	} else /* blk check ok */
 		*bh = nbh;
 
-	if (!found_dup)
+	bc->indir_count++;
+	if (found_dup)
+		return 1; /* don't process the metadata again */
+	else
 		fsck_blockmap_set(ip, block, _("indirect"),
 				  gfs2_indir_blk);
-	bc->indir_count++;
 
 	return 0;
 }
@@ -233,7 +235,6 @@ static int check_data(struct gfs2_inode *ip, uint64_t block, void *private)
 {
 	uint8_t q;
 	struct block_count *bc = (struct block_count *) private;
-	int error = 0;
 
 	if (gfs2_check_range(ip->i_sbd, block)) {
 		log_err( _("inode %lld (0x%llx) has a bad data block pointer "
@@ -251,32 +252,33 @@ static int check_data(struct gfs2_inode *ip, uint64_t block, void *private)
 	}
 	q = block_type(block);
 	if (q != gfs2_block_free) {
-		log_err( _("Found duplicate block referenced as data at %"
-			   PRIu64 " (0x%"PRIx64 ")\n"), block, block);
+		log_err( _("Found duplicate %s block %llu (0x%llx) "
+			   "referenced as data by dinode %llu (0x%llx)\n"),
+			 block_type_string(q),
+			 (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 (q != gfs2_meta_inval) {
-			gfs2_dup_set(block);
+			log_info( _("Seems to be a normal duplicate; I'll "
+				    "sort it out in pass1b.\n"));
+			add_duplicate_ref(ip, block, ref_as_data, 0,
+					  INODE_VALID);
 			/* If the prev ref was as data, this is likely a data
 			   block, so keep the block count for both refs. */
 			if (q == gfs2_block_used)
 				bc->data_count++;
 			return 1;
 		}
-		/* An explanation is in order here.  At this point we found
-		   a duplicate block, a block that was already referenced
-		   somewhere else.  We'll resolve those duplicates in pass1b.
-		   However, if the block is marked "invalid" that's a special
-		   case.  It's likely that the block was discovered to be
-		   invalid metadata--i.e. doesn't have a metadata header.
-		   However, it still may be a valid data block, since they
-		   won't have metadata headers.  In that case, the block is
-		   marked as duplicate, but also as a data block. */
-		error = 1;
-		gfs2_block_unmark(ip->i_sbd, bl, block, gfs2_meta_inval);
-		gfs2_dup_set(block);
+		log_info( _("The block was invalid as metadata but might be "
+			    "okay as data.  I'll sort it out in pass1b.\n"));
+		add_duplicate_ref(ip, block, ref_as_data, 0, INODE_VALID);
+		bc->data_count++;
+		return 1;
 	}
 	fsck_blockmap_set(ip, block, _("data"), gfs2_block_used);
 	bc->data_count++;
-	return error;
+	return 0;
 }
 
 static int undo_check_data(struct gfs2_inode *ip, uint64_t block,
@@ -448,12 +450,12 @@ static int check_eattr_indir(struct gfs2_inode *ip, uint64_t indirect,
 	*bh = bread(sdp, indirect);
 	if(gfs2_check_meta(*bh, GFS2_METATYPE_IN)) {
 		if(q != gfs2_block_free) { /* Duplicate? */
+			add_duplicate_ref(ip, indirect, ref_as_ea, 0,
+					  INODE_VALID);
 			if (!clear_eas(ip, bc, indirect, 1,
 				       _("Bad indirect Extended Attribute "
-					 "duplicate found"))) {
-				gfs2_dup_set(indirect);
+					 "duplicate found")))
 				bc->ea_count++;
-			}
 			return 1;
 		}
 		clear_eas(ip, bc, indirect, 0,
@@ -469,7 +471,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_dup_set(indirect);
+		add_duplicate_ref(ip, indirect, ref_as_ea, 0, INODE_VALID);
 		bc->ea_count++;
 		ret = 1;
 	} else {
@@ -527,6 +529,8 @@ static int check_leaf_block(struct gfs2_inode *ip, uint64_t block, int btype,
 	leaf_bh = bread(sdp, block);
 	if(gfs2_check_meta(leaf_bh, btype)) {
 		if(q != gfs2_block_free) { /* Duplicate? */
+			add_duplicate_ref(ip, block, ref_as_ea, 0,
+					  INODE_VALID);
 			clear_eas(ip, bc, block, 1,
 				  _("Bad Extended Attribute duplicate found"));
 		} else {
@@ -541,7 +545,7 @@ static int check_leaf_block(struct gfs2_inode *ip, uint64_t block, int btype,
 		log_debug( _("Duplicate block found at #%lld (0x%llx).\n"),
 			   (unsigned long long)block,
 			   (unsigned long long)block);
-		gfs2_dup_set(block);
+		add_duplicate_ref(ip, block, ref_as_data, 0, INODE_VALID);
 		bc->ea_count++;
 		brelse(leaf_bh);
 		return 1;
@@ -781,10 +785,10 @@ static int handle_di(struct gfs2_sbd *sdp, struct gfs2_buffer_head *bh,
 
 	q = block_type(block);
 	if(q != gfs2_block_free) {
-		log_err( _("Found duplicate block referenced as an inode at "
-			   "#%" PRIu64 " (0x%" PRIx64 ")\n"), block, block);
-		gfs2_dup_set(block);
-		fsck_inode_put(&ip);
+		log_err( _("Found a duplicate inode block at #%" PRIu64
+			   " (0x%" PRIx64 ") previously marked as a %s\n"),
+			 block, block, block_type_string(q));
+		add_duplicate_ref(ip, block, ref_as_meta, 0, INODE_VALID);
 		return 0;
 	}
 
diff --git a/gfs2/fsck/pass1b.c b/gfs2/fsck/pass1b.c
index a78a7a5..5ad1e08 100644
--- a/gfs2/fsck/pass1b.c
+++ b/gfs2/fsck/pass1b.c
@@ -17,6 +17,7 @@
 #include <string.h>
 #include <unistd.h>
 #include <libintl.h>
+#include <sys/stat.h>
 #define _(String) gettext(String)
 
 #include "libgfs2.h"
@@ -39,28 +40,62 @@ struct dup_handler {
 	int ref_count;
 };
 
-static inline void inc_if_found(uint64_t block, int not_ea, void *private) {
-	struct fxn_info *fi = (struct fxn_info *) private;
-	if(block == fi->block) {
-		(fi->found)++;
-		if(not_ea)
-			fi->ea_only = 0;
-	}
-}
+static int check_metalist(struct gfs2_inode *ip, uint64_t block,
+			  struct gfs2_buffer_head **bh, void *private);
+static int check_data(struct gfs2_inode *ip, 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, uint16_t *count, void *priv);
+
+struct metawalk_fxns find_refs = {
+	.private = NULL,
+	.check_leaf = NULL,
+	.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_metalist(struct gfs2_inode *ip, uint64_t block,
 			  struct gfs2_buffer_head **bh, void *private)
 {
-	inc_if_found(block, 1, private);
-
-	return 0;
+	return add_duplicate_ref(ip, block, ref_as_meta, 1, INODE_VALID);
 }
 
 static int check_data(struct gfs2_inode *ip, uint64_t block, void *private)
 {
-	inc_if_found(block, 1, private);
-
-	return 0;
+	return add_duplicate_ref(ip, block, ref_as_data, 1, INODE_VALID);
 }
 
 static int check_eattr_indir(struct gfs2_inode *ip, uint64_t block,
@@ -68,13 +103,13 @@ static int check_eattr_indir(struct gfs2_inode *ip, uint64_t block,
 			     void *private)
 {
 	struct gfs2_sbd *sbp = ip->i_sbd;
-	struct gfs2_buffer_head *indir_bh = NULL;
+	int error;
 
-	inc_if_found(block, 0, private);
-	indir_bh = bread(sbp, block);
-	*bh = indir_bh;
+	error = add_duplicate_ref(ip, block, ref_as_ea, 1, INODE_VALID);
+	if (!error)
+		*bh = bread(sbp, block);
 
-	return 0;
+	return error;
 }
 
 static int check_eattr_leaf(struct gfs2_inode *ip, uint64_t block,
@@ -82,13 +117,12 @@ static int check_eattr_leaf(struct gfs2_inode *ip, uint64_t block,
 			    void *private)
 {
 	struct gfs2_sbd *sbp = ip->i_sbd;
-	struct gfs2_buffer_head *leaf_bh = NULL;
-
-	inc_if_found(block, 0, private);
-	leaf_bh = bread(sbp, block);
+	int error;
 
-	*bh = leaf_bh;
-	return 0;
+	error = add_duplicate_ref(ip, block, ref_as_ea, 1, INODE_VALID);
+	if (!error)
+		*bh = bread(sbp, block);
+	return error;
 }
 
 static int check_eattr_entry(struct gfs2_inode *ip,
@@ -108,8 +142,39 @@ static int check_eattr_extentry(struct gfs2_inode *ip, uint64_t *ea_data_ptr,
 {
 	uint64_t block = be64_to_cpu(*ea_data_ptr);
 
-	inc_if_found(block, 0, private);
+	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;
 }
 
@@ -121,43 +186,27 @@ static int find_dentry(struct gfs2_inode *ip, struct gfs2_dirent *de,
 	struct osi_node *n;
 	osi_list_t *tmp2;
 	struct duptree *b;
-	struct inode_with_dups *id;
-	struct gfs2_leaf leaf;
+	int found;
 
 	for (n = osi_first(&dup_blocks); n; n = osi_next(n)) {
 		b = (struct duptree *)n;
-		osi_list_foreach(tmp2, &b->ref_inode_list) {
-			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... */
-				continue;
-			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 */
+		found = 0;
+		osi_list_foreach(tmp2, &b->ref_invinode_list) {
+			if (check_dir_dup_ref(ip, de, tmp2, filename)) {
+				found = 1;
 				break;
 			}
 		}
+		if (!found) {
+			osi_list_foreach(tmp2, &b->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. */
-	gfs2_leaf_in(&leaf, bh);
-	*count = leaf.lf_entries;
+	*count = be16_to_cpu(((struct gfs2_leaf *)bh->b_data)->lf_entries);
 	return 0;
 }
 
@@ -165,12 +214,32 @@ static int clear_dup_metalist(struct gfs2_inode *ip, uint64_t block,
 			      struct gfs2_buffer_head **bh, void *private)
 {
 	struct dup_handler *dh = (struct dup_handler *) private;
-
-	if(dh->ref_count == 1)
-		return 1;
+	struct duptree *d;
+
+	if (gfs2_check_range(ip->i_sbd, block) != 0)
+		return 0;
+
+	/* 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. */
+	d = dupfind(block);
+	if (!d) {
+		fsck_blockmap_set(ip, block, _("no longer valid"),
+				  gfs2_block_free);
+		return 0;
+	}
+	/* This block, having failed the above test, is duplicated somewhere */
 	if(block == dh->b->block) {
-		log_err( _("Found duplicate reference in inode \"%s\" at "
-			   "block #%llu (0x%llx) to block #%llu (0x%llx)\n"),
+		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,
@@ -178,13 +247,15 @@ static int clear_dup_metalist(struct gfs2_inode *ip, uint64_t block,
 		log_err( _("Inode %s is in directory %"PRIu64" (0x%" PRIx64 ")\n"),
 			 dh->id->name ? dh->id->name : "", dh->id->parent,
 			 dh->id->parent);
-		/* Setting the block to invalid means the inode is
-		 * cleared in pass2 */
-		fsck_blockmap_set(ip, ip->i_di.di_num.no_addr,
-				  _("inode with duplicate"),
-				  gfs2_inode_invalid);
 	}
-	return 0;
+	/* 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 1;
 }
 
 static int clear_dup_data(struct gfs2_inode *ip, uint64_t block, void *private)
@@ -196,55 +267,14 @@ static int clear_dup_eattr_indir(struct gfs2_inode *ip, uint64_t block,
 				 uint64_t parent, struct gfs2_buffer_head **bh,
 				 void *private)
 {
-	struct dup_handler *dh = (struct dup_handler *) private;
-	/* Can't use fxns from eattr.c since we need to check the ref
-	 * count */
-	*bh = NULL;
-	if(dh->ref_count == 1)
-		return 1;
-	if(block == dh->b->block) {
-		log_err( _("Found dup in inode \"%s\" with address #%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);
-		fsck_blockmap_set(ip, ip->i_di.di_eattr,
-				  _("indirect eattr"), gfs2_meta_inval);
-	}
-
-	return 0;
+	return clear_dup_metalist(ip, block, NULL, private);
 }
 
 static int clear_dup_eattr_leaf(struct gfs2_inode *ip, uint64_t block,
 				uint64_t parent, struct gfs2_buffer_head **bh,
 				void *private)
 {
-	struct dup_handler *dh = (struct dup_handler *) private;
-
-	if(dh->ref_count == 1)
-		return 1;
-	if(block == dh->b->block) {
-		log_err( _("Found dup in inode \"%s\" with address #%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);
-		/* mark the main eattr block invalid */
-		fsck_blockmap_set(ip, ip->i_di.di_eattr,
-				  _("indirect eattr leaf"), gfs2_meta_inval);
-	}
-
-	return 0;
+	return clear_dup_metalist(ip, block, NULL, private);
 }
 
 static int clear_eattr_entry (struct gfs2_inode *ip,
@@ -296,88 +326,86 @@ static int clear_eattr_extentry(struct gfs2_inode *ip, uint64_t *ea_data_ptr,
 				void *private)
 {
 	uint64_t block = be64_to_cpu(*ea_data_ptr);
-	struct dup_handler *dh = (struct dup_handler *) private;
-
-	if(dh->ref_count == 1)
-		return 1;
-	if(block == dh->b->block) {
-		log_err( _("Found dup in inode \"%s\" with address #%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);
-		/* mark the main eattr block invalid */
-		fsck_blockmap_set(ip, ip->i_di.di_eattr,
-				  _("extended eattr leaf"), gfs2_meta_inval);
-	}
-
-	return 0;
 
+	return clear_dup_metalist(ip, block, NULL, private);
 }
 
 /* Finds all references to duplicate blocks in the metadata */
-static int find_block_ref(struct gfs2_sbd *sbp, uint64_t inode, struct duptree *b)
+static int find_block_ref(struct gfs2_sbd *sbp, uint64_t inode)
 {
 	struct gfs2_inode *ip;
-	struct fxn_info myfi = {b->block, 0, 1};
-	struct inode_with_dups *id = NULL;
-	struct metawalk_fxns find_refs = {
-		.private = (void*) &myfi,
-		.check_leaf = NULL,
-		.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,
-	};
+	int error = 0;
 
 	ip = fsck_load_inode(sbp, inode); /* bread, inode_get */
-	log_debug( _("Checking inode %" PRIu64 " (0x%" PRIx64 ")'s "
-		     "metatree for references to block %" PRIu64 " (0x%" PRIx64
-		     ")\n"), inode, inode, b->block, b->block);
-	if(check_metatree(ip, &find_refs)) {
-		stack;
-		fsck_inode_put(&ip); /* out, brelse, free */
-		return -1;
+	/*log_debug( _("Checking inode %" PRIu64 " (0x%" PRIx64 ")'s "
+		     "metatree for references to duplicate blocks)\n"),
+		     inode, inode);*/
+	/* 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);
+		return 1;
 	}
-	log_debug( _("Done checking metatree\n"));
-	/* Check for ea references in the inode */
-	if(check_inode_eattr(ip, &find_refs) < 0){
+	error = check_metatree(ip, &find_refs);
+	if (error < 0) {
 		stack;
 		fsck_inode_put(&ip); /* out, brelse, free */
-		return -1;
-	}
-	if (myfi.found) {
-		if(!(id = malloc(sizeof(*id)))) {
-			log_crit( _("Unable to allocate inode_with_dups structure\n"));
-			return -1;
-		}
-		if(!(memset(id, 0, sizeof(*id)))) {
-			log_crit( _("Unable to zero inode_with_dups structure\n"));
-			return -1;
-		}
-		log_debug( _("Found %d entries with block %" PRIu64
-				  " (0x%" PRIx64 ") in inode #%" PRIu64 " (0x%" PRIx64 ")\n"),
-				  myfi.found, b->block, b->block, inode, inode);
-		id->dup_count = myfi.found;
-		id->block_no = inode;
-		id->ea_only = myfi.ea_only;
-		osi_list_add_prev(&id->list, &b->ref_inode_list);
+		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(S_ISDIR(ip->i_di.di_mode) && !(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 0;
+
+	return error;
 }
 
-static int handle_dup_blk(struct gfs2_sbd *sbp, struct duptree *b)
+static void log_inode_reference(struct duptree *b, osi_list_t *tmp, int inval)
 {
-	osi_list_t *tmp;
+	char reftypestring[32];
+	struct inode_with_dups *id;
+
+	id = osi_list_entry(tmp, struct inode_with_dups, list);
+	if (id->dup_count == 1) {
+		if (id->reftypecount[ref_as_data])
+			strcpy(reftypestring, "as data");
+		else if (id->reftypecount[ref_as_meta])
+			strcpy(reftypestring, "as metadata");
+		else
+			strcpy(reftypestring, "as extended attribute");
+	} else {
+		sprintf(reftypestring, "%d/%d/%d",
+			id->reftypecount[ref_as_data],
+			id->reftypecount[ref_as_meta],
+			id->reftypecount[ref_as_ea]);
+	}
+	if (inval)
+		log_warn( _("Invalid "));
+	log_warn( _("Inode %s (%lld/0x%llx) has %d reference(s) to "
+		    "block %llu (0x%llx) (%s)\n"), id->name,
+		  (unsigned long long)id->block_no,
+		  (unsigned long long)id->block_no, id->dup_count,
+		  (unsigned long long)b->block,
+		  (unsigned long long)b->block, reftypestring);
+}
+
+static int clear_a_reference(struct gfs2_sbd *sbp, struct duptree *b,
+			     osi_list_t *ref_list, struct dup_handler *dh,
+			     int inval)
+{
+	struct gfs2_inode *ip;
 	struct inode_with_dups *id;
+	osi_list_t *tmp, *x;
 	struct metawalk_fxns clear_dup_fxns = {
 		.private = NULL,
 		.check_leaf = NULL,
@@ -389,9 +417,60 @@ static int handle_dup_blk(struct gfs2_sbd *sbp, struct duptree *b)
 		.check_eattr_entry = clear_eattr_entry,
 		.check_eattr_extentry = clear_eattr_extentry,
 	};
+
+	osi_list_foreach_safe(tmp, ref_list, x) {
+		id = osi_list_entry(tmp, struct inode_with_dups, list);
+		dh->b = b;
+		dh->id = id;
+		if(dh->ref_inode_count == 1) /* down to the last reference */
+			return 1;
+		if (!(query( _("Okay to clear %s inode %lld (0x%llx)? (y/n) "),
+			     (inval ? _("invalidated") : ""),
+			     (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 inode %lld (0x%llx)....\n"),
+			  (unsigned long long)id->block_no,
+			  (unsigned long long)id->block_no);
+		clear_dup_fxns.private = (void *) dh;
+		/* Clear the EAs for the inode first */
+		ip = fsck_load_inode(sbp, id->block_no);
+		check_inode_eattr(ip, &clear_dup_fxns);
+		/* If the dup wasn't only in the EA, clear the inode */
+		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,
+				  _("bad"), gfs2_inode_invalid);
+		fsck_inode_put(&ip); /* out, brelse, free */
+		(dh->ref_inode_count)--;
+		/* Inode is marked invalid and is removed in pass2 */
+		/* FIXME: other option should be to duplicate the
+		 * block for each duplicate and point the metadata at
+		 * the cloned blocks */
+	}
+	if(dh->ref_inode_count == 1) /* down to the last reference */
+		return 1;
+	return 0;
+}
+
+static int handle_dup_blk(struct gfs2_sbd *sbp, struct duptree *b)
+{
 	struct gfs2_inode *ip;
+	osi_list_t *tmp;
+	struct inode_with_dups *id;
 	struct dup_handler dh = {0};
+	int last_reference, ref_in_invalid_inode = 0;
 
+	osi_list_foreach(tmp, &b->ref_invinode_list) {
+		id = osi_list_entry(tmp, struct inode_with_dups, list);
+		dh.ref_inode_count++;
+		dh.ref_count += id->dup_count;
+		ref_in_invalid_inode = 1;
+	}
 	osi_list_foreach(tmp, &b->ref_inode_list) {
 		id = osi_list_entry(tmp, struct inode_with_dups, list);
 		dh.ref_inode_count++;
@@ -405,6 +484,11 @@ static int handle_dup_blk(struct gfs2_sbd *sbp, struct duptree *b)
 	   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. */
+	/* Another possibility here is that there is a single reference
+	   because all the other metadata references were in inodes that got
+	   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) {
 		struct gfs2_buffer_head *bh;
 		uint32_t cmagic;
@@ -413,7 +497,10 @@ static int handle_dup_blk(struct gfs2_sbd *sbp, struct duptree *b)
 		cmagic = ((struct gfs2_meta_header *)(bh->b_data))->mh_magic;
 		brelse(bh);
 		if (be32_to_cpu(cmagic) == GFS2_MAGIC) {
-			tmp = b->ref_inode_list.next;
+			if (ref_in_invalid_inode)
+				tmp = b->ref_invinode_list.next;
+			else
+				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), "
@@ -443,6 +530,13 @@ static int handle_dup_blk(struct gfs2_sbd *sbp, struct duptree *b)
 			}
 			return 0;
 		}
+		/* The other references may have been discredited due to
+		   invalid metadata or something.  Use the last remaining. */
+		log_notice( _("Block %llu (0x%llx) has only one remaining "
+			      "reference.\n"),
+			    (unsigned long long)b->block,
+			    (unsigned long long)b->block);
+		return 0;
 	}
 
 	log_notice( _("Block %llu (0x%llx) has %d inodes referencing it"
@@ -450,46 +544,65 @@ static int handle_dup_blk(struct gfs2_sbd *sbp, struct duptree *b)
 		   (unsigned long long)b->block, (unsigned long long)b->block,
 		   dh.ref_inode_count, dh.ref_count);
 
-	osi_list_foreach(tmp, &b->ref_inode_list) {
-		id = osi_list_entry(tmp, struct inode_with_dups, list);
-		log_warn( _("Inode %s (%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,
-			  (unsigned long long)b->block);
-	}
-	osi_list_foreach(tmp, &b->ref_inode_list) {
+	osi_list_foreach(tmp, &b->ref_invinode_list)
+		log_inode_reference(b, tmp, 1);
+	osi_list_foreach(tmp, &b->ref_inode_list)
+		log_inode_reference(b, tmp, 0);
+	
+	last_reference = clear_a_reference(sbp, b, &b->ref_invinode_list,
+					   &dh, 1);
+	if (!last_reference)
+		last_reference = clear_a_reference(sbp, b, &b->ref_inode_list,
+						   &dh, 0);
+
+	if (last_reference) {
+		uint8_t q;
+
+		/* 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 = (&b->ref_inode_list)->next;
 		id = osi_list_entry(tmp, struct inode_with_dups, list);
-		if (!(query( _("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 inode %lld (0x%llx)...\n"),
-			  (unsigned long long)id->block_no,
-			  (unsigned long long)id->block_no);
+		log_debug( _("Resetting the type based on the remaining "
+			     "reference in 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;
-		clear_dup_fxns.private = (void *) &dh;
-		/* Clear the EAs for the inode first */
-		check_inode_eattr(ip, &clear_dup_fxns);
-		/* If the dup wasn't only in the EA, clear the inode */
-		if(!id->ea_only)
-			check_metatree(ip, &clear_dup_fxns);
 
-		fsck_blockmap_set(ip, ip->i_di.di_num.no_addr,
-				  _("bad"), gfs2_inode_invalid);
+		q = block_type(id->block_no);
+		if (q == gfs2_inode_invalid) {
+			log_debug( _("The remaining reference inode %lld "
+				     "(0x%llx) is marked invalid: Marking "
+				     "the block as free.\n"),
+				   (unsigned long long)id->block_no,
+				   (unsigned long long)id->block_no);
+			fsck_blockmap_set(ip, b->block,
+					  _("reference-repaired leaf"),
+					  gfs2_block_free);
+		} else if (id->reftypecount[ref_as_data]) {
+			fsck_blockmap_set(ip, b->block,
+					  _("reference-repaired data"),
+					  gfs2_block_used);
+		} else if (id->reftypecount[ref_as_meta]) {
+			if (S_ISDIR(ip->i_di.di_mode))
+				fsck_blockmap_set(ip, b->block,
+						  _("reference-repaired leaf"),
+						  gfs2_leaf_blk);
+			else
+				fsck_blockmap_set(ip, b->block,
+						  _("reference-repaired "
+						    "indirect"),
+						  gfs2_indir_blk);
+		} else
+			fsck_blockmap_set(ip, b->block,
+					  _("reference-repaired extended "
+					    "attribute"),
+					  gfs2_meta_eattr);
 		fsck_inode_put(&ip); /* out, brelse, free */
-		dh.ref_inode_count--;
-		if(dh.ref_inode_count == 1)
-			break;
-		/* Inode is marked invalid and is removed in pass2 */
-		/* FIXME: other option should be to duplicate the
-		 * block for each duplicate and point the metadata at
-		 * the cloned blocks */
+	} else {
+		log_debug( _("All duplicate references were resolved.\n"));
 	}
 	return 0;
 
@@ -504,9 +617,7 @@ int pass1b(struct gfs2_sbd *sbp)
 	uint64_t i;
 	uint8_t q;
 	struct osi_node *n;
-	struct metawalk_fxns find_dirents = {0};
 	int rc = FSCK_OK;
-	find_dirents.check_dentry = &find_dentry;
 
 	log_info( _("Looking for duplicate blocks...\n"));
 
@@ -521,29 +632,33 @@ int pass1b(struct gfs2_sbd *sbp)
 	log_info( _("Scanning filesystem for inodes containing duplicate blocks...\n"));
 	log_debug( _("Filesystem has %"PRIu64" (0x%" PRIx64 ") blocks total\n"),
 			  last_fs_block, last_fs_block);
-	for(i = 0; i < last_fs_block; i += 1) {
-		warm_fuzzy_stuff(i);
+	for(i = 0; i < last_fs_block; i++) {
 		if (skip_this_pass || fsck_abort) /* if asked to skip the rest */
 			goto out;
-		log_debug( _("Scanning block %" PRIu64 " (0x%" PRIx64 ") for inodes\n"),
-				  i, i);
+
+		if (dups_found_first == dups_found) {
+			log_debug(_("Found all %d original references to "
+				    "duplicates.\n"), dups_found);
+			break;
+		}
 		q = block_type(i);
 
 		if (q < gfs2_inode_dir)
 			continue;
-		if (q > gfs2_inode_sock)
+		if (q > gfs2_inode_invalid)
 			continue;
 
-		for (n = osi_first(&dup_blocks); n; n = osi_next(n)) {
-			b = (struct duptree *)n;
-			if(find_block_ref(sbp, i, b)) {
-				stack;
-				rc = FSCK_ERROR;
-				goto out;
-			}
-		}
-		if(q == gfs2_inode_dir) {
-			check_dir(sbp, i, &find_dirents);
+		if (q == gfs2_inode_invalid)
+			log_debug( _("Checking invalidated duplicate dinode "
+				     "%lld (0x%llx)\n"),
+				   (unsigned long long)i,
+				   (unsigned long long)i);
+
+		warm_fuzzy_stuff(i);
+		if (find_block_ref(sbp, i) < 0) {
+			stack;
+			rc = FSCK_ERROR;
+			goto out;
 		}
 	}
 
diff --git a/gfs2/fsck/util.c b/gfs2/fsck/util.c
index 394d1e0..a9dce7b 100644
--- a/gfs2/fsck/util.c
+++ b/gfs2/fsck/util.c
@@ -25,9 +25,10 @@
 
 #include "libgfs2.h"
 #include "fs_bits.h"
-#include "metawalk.h"
 #include "util.h"
 
+const char *reftypes[3] = {"data", "metadata", "extended attribute"};
+
 /**
  * compute_height
  * @sdp:
@@ -185,7 +186,15 @@ int fsck_query(const char *format, ...)
 	return ret;
 }
 
-struct duptree *gfs2_dup_set(uint64_t dblock)
+/*
+ * gfs2_dup_set - Flag a block as a duplicate
+ * We keep the references in a red/black tree.  We can't keep track of every
+ * single inode in the file system, so the first time this function is called
+ * will actually be for the second reference to the duplicated block.
+ * This will return the number of references to the block.
+ *
+ * create - will be set if the call is supposed to create the reference. */
+static struct duptree *gfs2_dup_set(uint64_t dblock, int create)
 {
 	struct osi_node **newn = &dup_blocks.osi_node, *parent = NULL;
 	struct duptree *data;
@@ -203,18 +212,129 @@ struct duptree *gfs2_dup_set(uint64_t dblock)
 			return cur;
 	}
 
+	if (!create)
+		return NULL;
 	data = malloc(sizeof(struct duptree));
+	dups_found++;
 	memset(data, 0, sizeof(struct duptree));
 	/* Add new node and rebalance tree. */
 	data->block = dblock;
-	data->refs = 2; /* first call is the second reference to the block */
+	data->refs = 1; /* reference 1 is actually the reference we need to
+			   discover in pass1b. */
+	data->first_ref_found = 0;
 	osi_list_init(&data->ref_inode_list);
+	osi_list_init(&data->ref_invinode_list);
 	osi_link_node(&data->node, parent, newn);
 	osi_insert_color(&data->node, &dup_blocks);
 
 	return data;
 }
 
+/*
+ * add_duplicate_ref - Add a duplicate reference to the duplicates tree list
+ * A new element of the tree will be created as needed
+ * When the first reference is discovered in pass1, it realizes it's a
+ * duplicate but it has already forgotten where the first reference was.
+ * So we need to recreate the duplicate reference structure if it's not there.
+ * Later, in pass1b, it has to go back through the file system
+ * and figure out those original references in order to resolve them.
+ */
+int add_duplicate_ref(struct gfs2_inode *ip, uint64_t block,
+		      enum dup_ref_type reftype, int first, int inode_valid)
+{
+	osi_list_t *ref;
+	struct inode_with_dups *id, *found_id;
+	struct duptree *dt;
+
+	if (gfs2_check_range(ip->i_sbd, block) != 0)
+		return 0;
+	/* If this is not the first reference (i.e. all calls from pass1) we
+	   need to create the duplicate reference. If this is pass1b, we want
+	   to ignore references that aren't found. */
+	dt = gfs2_dup_set(block, !first);
+	if (!dt)        /* If this isn't a duplicate */
+		return 0;
+
+	/* If we found the duplicate reference but we've already discovered
+	   the first reference (in pass1b) and the other references in pass1,
+	   we don't need to count it, so just return. */
+	if (dt->first_ref_found)
+		return 0;
+
+	/* The first time this is called from pass1 is actually the second
+	   reference.  When we go back in pass1b looking for the original
+	   reference, we don't want to increment the reference count because
+	   it's already accounted for. */
+	if (first) {
+		if (!dt->first_ref_found) {
+			dt->first_ref_found = 1;
+			dups_found_first++; /* We found another first ref. */
+		}
+	} else {
+		dt->refs++;
+	}
+
+	/* Check for a previous reference to this duplicate on the "invalid
+	   inode" reference list. */
+	found_id = NULL;
+	osi_list_foreach(ref, &dt->ref_invinode_list) {
+		id = osi_list_entry(ref, struct inode_with_dups, list);
+
+		if (id->block_no == ip->i_di.di_num.no_addr) {
+			found_id = id;
+			break;
+		}
+	}
+	if (found_id == NULL) {
+		osi_list_foreach(ref, &dt->ref_inode_list) {
+			id = osi_list_entry(ref, struct inode_with_dups, list);
+
+			if (id->block_no == ip->i_di.di_num.no_addr) {
+				found_id = id;
+				break;
+			}
+		}
+	}
+	if (found_id == NULL) {
+		/* Check for the inode on the invalid inode reference list. */
+		uint8_t q;
+
+		if(!(found_id = malloc(sizeof(*found_id)))) {
+			log_crit( _("Unable to allocate "
+				    "inode_with_dups structure\n"));
+			return -1;
+		}
+		if(!(memset(found_id, 0, sizeof(*found_id)))) {
+			log_crit( _("Unable to zero inode_with_dups "
+				    "structure\n"));
+			return -1;
+		}
+		found_id->block_no = ip->i_di.di_num.no_addr;
+		q = block_type(ip->i_di.di_num.no_addr);
+		/* If it's an invalid dinode, put it first on the invalid
+		   inode reference list otherwise put it on the normal list. */
+		if (!inode_valid || q == gfs2_inode_invalid)
+			osi_list_add_prev(&found_id->list,
+					  &dt->ref_invinode_list);
+		else
+			osi_list_add_prev(&found_id->list,
+					  &dt->ref_inode_list);
+	}
+	found_id->reftypecount[reftype]++;
+	found_id->dup_count++;
+	log_info( _("Found %d reference(s) to block %llu"
+		    " (0x%llx) as %s in inode #%llu (0x%llx)\n"),
+		  found_id->dup_count, (unsigned long long)block,
+		  (unsigned long long)block, reftypes[reftype],
+		  (unsigned long long)ip->i_di.di_num.no_addr,
+		  (unsigned long long)ip->i_di.di_num.no_addr);
+	if (first)
+		log_info( _("This is the original reference.\n"));
+	else
+		log_info( _("This brings the total to: %d\n"), dt->refs);
+	return 0;
+}
+
 struct dir_info *dirtree_insert(uint64_t dblock)
 {
 	struct osi_node **newn = &dirtree.osi_node, *parent = NULL;
@@ -272,6 +392,14 @@ void dup_delete(struct duptree *b)
 	struct inode_with_dups *id;
 	osi_list_t *tmp;
 
+	while (!osi_list_empty(&b->ref_invinode_list)) {
+		tmp = (&b->ref_invinode_list)->next;
+		id = osi_list_entry(tmp, struct inode_with_dups, list);
+		if (id->name)
+			free(id->name);
+		osi_list_del(&id->list);
+		free(id);
+	}
 	while (!osi_list_empty(&b->ref_inode_list)) {
 		tmp = (&b->ref_inode_list)->next;
 		id = osi_list_entry(tmp, struct inode_with_dups, list);
diff --git a/gfs2/fsck/util.h b/gfs2/fsck/util.h
index 0113bdb..2c46e09 100644
--- a/gfs2/fsck/util.h
+++ b/gfs2/fsck/util.h
@@ -20,12 +20,16 @@
 #define fsck_lseek(fd, off) \
   ((lseek((fd), (off), SEEK_SET) == (off)) ? 0 : -1)
 
+#define INODE_VALID 1
+#define INODE_INVALID 0
+
 int compute_height(struct gfs2_sbd *sdp, uint64_t sz);
 struct di_info *search_list(osi_list_t *list, uint64_t addr);
 void big_file_comfort(struct gfs2_inode *ip, uint64_t blks_checked);
 void warm_fuzzy_stuff(uint64_t block);
-const char *block_type_string(uint8_t q);
-struct duptree *gfs2_dup_set(uint64_t dblock);
+int add_duplicate_ref(struct gfs2_inode *ip, uint64_t block,
+		      enum dup_ref_type reftype, int first, int inode_valid);
+extern const char *reftypes[3];
 
 static inline uint8_t block_type(uint64_t bblock)
 {