Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > 9383e745e23602bc45f9c92184feea59 > files > 33

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

commit 9107ed8993d8efc19be0c34e4874db3f229c1869
Author: Bob Peterson <bob@ganesha.peterson>
Date:   Fri Jan 22 13:41:44 2010 -0600

    fsck.gfs2: enforce consistency between bitmap and blockmap
    
    One of the big problems with fsck.gfs2 was that there was nothing
    to enforce that the incore bitmap from the rgrps corresponded
    one-to-one with the incore blockmaps.  This patch enforces that
    by making a central function that sets values into the blockmap
    and makes sure the bitmap has the correct corresponding bits.
    It also tracks all block references with debug messages so block
    map problems may be easily tracked down by running fsck.gfs2 with
    -vv.  I needed to add some support functions and modified how
    the metadata buffers are managed.
    
    rhbz#455300

diff --git a/gfs2/fsck/eattr.c b/gfs2/fsck/eattr.c
index b633d47..0eed835 100644
--- a/gfs2/fsck/eattr.c
+++ b/gfs2/fsck/eattr.c
@@ -20,14 +20,15 @@
 #include "eattr.h"
 #include "metawalk.h"
 
-static int clear_blk_nodup(struct gfs2_sbd *sbp, uint64_t block)
+static int clear_blk_nodup(struct gfs2_inode *ip, uint64_t block)
 {
 	if(is_duplicate(block)) {
 		log_debug( _("Not clearing block with marked as a duplicate\n"));
 		return 1;
 	}
 
-	gfs2_blockmap_set(bl, block, gfs2_block_free);
+	fsck_blockmap_set(ip, block, _("cleared eattr block"),
+			  gfs2_block_free);
 
 	return 0;
 
@@ -37,14 +38,14 @@ int clear_eattr_indir(struct gfs2_inode *ip, uint64_t block,
 		      uint64_t parent, struct gfs2_buffer_head **bh,
 		      void *private)
 {
-	return clear_blk_nodup(ip->i_sbd, block);
+	return clear_blk_nodup(ip, block);
 }
 
 int clear_eattr_leaf(struct gfs2_inode *ip, uint64_t block,
 		     uint64_t parent, struct gfs2_buffer_head **bh,
 		     void *private)
 {
-	return clear_blk_nodup(ip->i_sbd, block);
+	return clear_blk_nodup(ip, block);
 }
 
 int clear_eattr_entry (struct gfs2_inode *ip,
@@ -93,7 +94,7 @@ int clear_eattr_extentry(struct gfs2_inode *ip, uint64_t *ea_data_ptr,
 {
 	uint64_t block = be64_to_cpu(*ea_data_ptr);
 
-	return clear_blk_nodup(ip->i_sbd, block);
+	return clear_blk_nodup(ip, block);
 
 }
 
diff --git a/gfs2/fsck/lost_n_found.c b/gfs2/fsck/lost_n_found.c
index 90354d9..d9bed19 100644
--- a/gfs2/fsck/lost_n_found.c
+++ b/gfs2/fsck/lost_n_found.c
@@ -25,6 +25,7 @@
 #include "libgfs2.h"
 #include "lost_n_found.h"
 #include "link.h"
+#include "metawalk.h"
 #include "util.h"
 
 /* add_inode_to_lf - Add dir entry to lost+found for the inode
@@ -57,7 +58,8 @@ int add_inode_to_lf(struct gfs2_inode *ip){
 			 * directory or just found an old one, and we
 			 * used that instead of the block_type to run
 			 * this */
-			gfs2_blockmap_set(bl, lf_dip->i_di.di_num.no_addr,
+			fsck_blockmap_set(ip, lf_dip->i_di.di_num.no_addr,
+					  _("lost+found dinode"),
 					  gfs2_inode_dir);
 			/* root inode links to lost+found */
 			increment_link(ip->i_sbd->md.rooti->i_di.di_num.no_addr,
diff --git a/gfs2/fsck/main.c b/gfs2/fsck/main.c
index 44b8098..365e7e9 100644
--- a/gfs2/fsck/main.c
+++ b/gfs2/fsck/main.c
@@ -27,6 +27,7 @@
 #include "libgfs2.h"
 #include "fsck.h"
 #include "osi_list.h"
+#include "metawalk.h"
 #include "util.h"
 
 struct gfs2_options opts = {0};
@@ -181,9 +182,11 @@ static int check_system_inode(struct gfs2_inode *sysinode, const char *filename,
 		/* bitmap.  In that case, don't rebuild the inode.  */
 		/* Just reuse the inode and fix the bitmap.         */
 		if (ds.q == gfs2_block_free) {
-			log_info( _("The inode exists but the block is not marked 'in use'; fixing it.\n"));
-			gfs2_blockmap_set(bl,sysinode->i_di.di_num.no_addr,
-					  mark);
+			log_info( _("The inode exists but the block is not "
+				    "marked 'in use'; fixing it.\n"));
+			fsck_blockmap_set(sysinode,
+					  sysinode->i_di.di_num.no_addr,
+					  filename, mark);
 			ds.q = mark;
 			if (mark == gfs2_inode_dir)
 				dirtree_insert(sysinode->i_di.di_num.no_addr);
@@ -199,8 +202,9 @@ static int check_system_inode(struct gfs2_inode *sysinode, const char *filename,
 		log_err( _("Invalid or missing %s system inode.\n"), filename);
 		if (query(_("Create new %s system inode? (y/n) "), filename)) {
 			builder(sysinode->i_sbd);
-			gfs2_blockmap_set(bl, sysinode->i_di.di_num.no_addr,
-					  mark);
+			fsck_blockmap_set(sysinode,
+					  sysinode->i_di.di_num.no_addr,
+					  filename, mark);
 			ds.q = mark;
 			if (mark == gfs2_inode_dir)
 				dirtree_insert(sysinode->i_di.di_num.no_addr);
diff --git a/gfs2/fsck/metawalk.c b/gfs2/fsck/metawalk.c
index 4d70693..f14f59b 100644
--- a/gfs2/fsck/metawalk.c
+++ b/gfs2/fsck/metawalk.c
@@ -19,16 +19,155 @@
 #include <sys/stat.h>
 #include <unistd.h>
 #include <libintl.h>
+#include <ctype.h>
 #define _(String) gettext(String)
 
 #include "libgfs2.h"
+#include "osi_tree.h"
 #include "fsck.h"
 #include "util.h"
 #include "metawalk.h"
 #include "hash.h"
+#include "inode_hash.h"
 
 #define COMFORTABLE_BLKS 5242880 /* 20GB in 4K blocks */
 
+/* There are two bitmaps: (1) The "blockmap" that fsck uses to keep track of
+   what block type has been discovered, and (2) The rgrp bitmap.  Function
+   gfs2_blockmap_set is used to set the former and gfs2_set_bitmap
+   is used to set the latter.  The two must be kept in sync, otherwise
+   you'll get bitmap mismatches.  This function checks the status of the
+   bitmap whenever the blockmap changes, and fixes it accordingly. */
+int check_n_fix_bitmap(struct gfs2_sbd *sdp, uint64_t blk,
+		       enum gfs2_mark_block new_blockmap_state)
+{
+	int old_bitmap_state, new_bitmap_state;
+	struct rgrp_list *rgd;
+
+	rgd = gfs2_blk2rgrpd(sdp, blk);
+
+	old_bitmap_state = gfs2_get_bitmap(sdp, blk, rgd);
+	if (old_bitmap_state < 0) {
+		log_err( _("Block %lld (0x%llx) is not represented in the"
+			   "system bitmap; part of an rgrp or superblock.\n"),
+			 (unsigned long long)blk, (unsigned long long)blk);
+		return -1;
+	}
+	new_bitmap_state = blockmap_to_bitmap(new_blockmap_state);
+	if (old_bitmap_state != new_bitmap_state) {
+		const char *allocdesc[] = {"free space", "data", "unlinked",
+					   "inode", "reserved"};
+
+		log_err( _("Block %llu (0x%llx) seems to be %s, but is "
+			   "marked as %s in the bitmap.\n"),
+			 (unsigned long long)blk, (unsigned long long)blk,
+			 allocdesc[new_bitmap_state],
+			 allocdesc[old_bitmap_state]);
+		if(query( _("Okay to fix the bitmap? (y/n)"))) {
+			/* If the new bitmap state is free (and therefore the
+			   old state was not) we have to add to the free
+			   space in the rgrp. If the old bitmap state was
+			   free (and therefore it no longer is) we have to
+			   subtract to the free space.  If the type changed
+			   from dinode to data or data to dinode, no change in
+			   free space. */
+			gfs2_set_bitmap(sdp, blk, new_bitmap_state);
+			if (new_bitmap_state == GFS2_BLKST_FREE) {
+				/* If we're freeing a dinode, get rid of
+				   the hash table entries for it. */
+				if (old_bitmap_state == GFS2_BLKST_DINODE) {
+					struct dir_info *dt;
+					struct inode_info *ii;
+
+					dt = dirtree_find(blk);
+					if (dt)
+						dirtree_delete(dt);
+					ii = inodetree_find(blk);
+					if (ii)
+						inodetree_delete(ii);
+				}
+				rgd->rg.rg_free++;
+				gfs2_rgrp_out(&rgd->rg, rgd->bh[0]);
+			} else if (old_bitmap_state == GFS2_BLKST_FREE) {
+				rgd->rg.rg_free--;
+				gfs2_rgrp_out(&rgd->rg, rgd->bh[0]);
+			}
+			log_err( _("The bitmap was fixed.\n"));
+		} else {
+			log_err( _("The bitmap inconsistency was ignored.\n"));
+		}
+	}
+	return 0;
+}
+
+/*
+ * _fsck_blockmap_set - Mark a block in the 4-bit blockmap and the 2-bit
+ *                      bitmap, and adjust free space accordingly.
+ */
+int _fsck_blockmap_set(struct gfs2_inode *ip, uint64_t bblock,
+		       const char *btype, enum gfs2_mark_block mark,
+		       const char *caller, int fline)
+{
+	int error;
+
+	if (print_level >= MSG_DEBUG) {
+		const char *p;
+
+		p = strrchr(caller, '/');
+		if (p)
+			p++;
+		else
+			p = caller;
+		/* I'm circumventing the log levels here on purpose to make the
+		   output easier to debug. */
+		if (ip->i_di.di_num.no_addr == bblock) {
+			print_fsck_log(MSG_DEBUG, p, fline,
+				       _("%s inode found at block %lld "
+					 "(0x%llx): marking as '%s'\n"),
+				       btype, (unsigned long long)
+				       ip->i_di.di_num.no_addr,
+				       (unsigned long long)
+				       ip->i_di.di_num.no_addr,
+				       block_type_string(mark));
+		} else if (mark == gfs2_bad_block || mark == gfs2_meta_inval) {
+			print_fsck_log(MSG_DEBUG, p, fline,
+				       _("inode %lld (0x%llx) references "
+					 "%s block %lld (0x%llx): "
+					 "marking as '%s'\n"),
+				       (unsigned long long)
+				       ip->i_di.di_num.no_addr,
+				       (unsigned long long)
+				       ip->i_di.di_num.no_addr,
+				       btype, (unsigned long long)bblock,
+				       (unsigned long long)bblock,
+				       block_type_string(mark));
+		} else {
+			print_fsck_log(MSG_DEBUG, p, fline,
+				       _("inode %lld (0x%llx) references "
+					 "%s block %lld (0x%llx): "
+					 "marking as '%s'\n"),
+				       (unsigned long long)
+				       ip->i_di.di_num.no_addr,
+				       (unsigned long long)
+				       ip->i_di.di_num.no_addr, btype,
+				       (unsigned long long)bblock,
+				       (unsigned long long)bblock,
+				       block_type_string(mark));
+		}
+	}
+
+	/* First, check the rgrp bitmap against what we think it should be.
+	   If that fails, it's an invalid block--part of an rgrp. */
+	error = check_n_fix_bitmap(ip->i_sbd, bblock, mark);
+	if (error) {
+		log_err( _("This block is not represented in the bitmap.\n"));
+		return error;
+	}
+
+	error = gfs2_blockmap_set(bl, bblock, mark);
+	return error;
+}
+
 struct duptree *dupfind(uint64_t block)
 {
 	struct osi_node *node = dup_blocks.osi_node;
@@ -704,9 +843,10 @@ static int check_eattr_entries(struct gfs2_inode *ip,
 						/* Endianness doesn't matter
 						   in this case because it's
 						   a single byte. */
-						gfs2_blockmap_set(bl,
-							     ip->i_di.di_eattr,
-							     gfs2_meta_eattr);
+						fsck_blockmap_set(ip,
+						       ip->i_di.di_eattr,
+						       _("extended attribute"),
+						       gfs2_meta_eattr);
 						log_err( _("The EA was "
 							   "fixed.\n"));
 					} else {
@@ -855,10 +995,9 @@ static int check_indirect_eattr(struct gfs2_inode *ip, uint64_t indirect,
 							 pass->private);
 			}
 			if (leaf_pointer_errors == leaf_pointers) {
-				if (indirect_buf->b_modified)
-					gfs2_set_bitmap(sdp, indirect,
-							GFS2_BLKST_FREE);
-				gfs2_blockmap_set(bl, indirect,
+				fsck_blockmap_set(ip, indirect,
+						  _("indirect extended "
+						    "attribute"),
 						  gfs2_block_free);
 				error = 1;
 			}
@@ -901,6 +1040,29 @@ int check_inode_eattr(struct gfs2_inode *ip, struct metawalk_fxns *pass)
 }
 
 /**
+ * free_metalist - free all metadata on a multi-level metadata list
+ */
+static void free_metalist(struct gfs2_inode *ip, osi_list_t *mlp)
+{
+	int i;
+	struct gfs2_buffer_head *nbh;
+
+	for (i = 0; i < GFS2_MAX_META_HEIGHT; i++) {
+		osi_list_t *list;
+
+		list = &mlp[i];
+		while (!osi_list_empty(list)) {
+			nbh = osi_list_entry(list->next,
+					     struct gfs2_buffer_head, b_altlist);
+			if (nbh == ip->i_bh)
+				osi_list_del(&nbh->b_altlist);
+			else
+				brelse(nbh);
+		}
+	}
+}
+
+/**
  * build_and_check_metalist - check a bunch of indirect blocks
  *                            This includes hash table blocks for directories
  *                            which are technically "data" in the bitmap.
@@ -981,16 +1143,7 @@ static int build_and_check_metalist(struct gfs2_inode *ip, osi_list_t *mlp,
 	} /* for height */
 	return 0;
 fail:
-	for (i = 0; i < GFS2_MAX_META_HEIGHT; i++) {
-		osi_list_t *list;
-		list = &mlp[i];
-		while (!osi_list_empty(list)) {
-			nbh = osi_list_entry(list->next,
-					     struct gfs2_buffer_head, b_altlist);
-			brelse(nbh);
-			osi_list_del(&nbh->b_altlist);
-		}
-	}
+	free_metalist(ip, mlp);
 	return -1;
 }
 
@@ -1003,7 +1156,7 @@ fail:
 int check_metatree(struct gfs2_inode *ip, struct metawalk_fxns *pass)
 {
 	osi_list_t metalist[GFS2_MAX_META_HEIGHT];
-	osi_list_t *list, *tmp;
+	osi_list_t *list;
 	struct gfs2_buffer_head *bh;
 	uint64_t block, *ptr;
 	uint32_t height = ip->i_di.di_height;
@@ -1035,8 +1188,9 @@ int check_metatree(struct gfs2_inode *ip, struct metawalk_fxns *pass)
 	if (ip->i_di.di_blocks > COMFORTABLE_BLKS)
 		last_reported_fblock = -10000000;
 
-	for (tmp = list->next; tmp != list; tmp = tmp->next) {
-		bh = osi_list_entry(tmp, struct gfs2_buffer_head, b_altlist);
+	while (error >= 0 && !osi_list_empty(list)) {
+		bh = osi_list_entry(list->next, struct gfs2_buffer_head,
+				    b_altlist);
 
 		if (height > 1) {
 			if (gfs2_check_meta(bh, GFS2_METATYPE_IN)) {
@@ -1075,6 +1229,10 @@ int check_metatree(struct gfs2_inode *ip, struct metawalk_fxns *pass)
 			if (ip->i_di.di_blocks > COMFORTABLE_BLKS)
 				big_file_comfort(ip, blks_checked);
 		}
+		if (bh == ip->i_bh)
+			osi_list_del(&bh->b_altlist);
+		else
+			brelse(bh);
 	}
 	if (ip->i_di.di_blocks > COMFORTABLE_BLKS) {
 		log_notice( _("\rLarge file at %lld (0x%llx) - 100 percent "
@@ -1085,22 +1243,6 @@ int check_metatree(struct gfs2_inode *ip, struct metawalk_fxns *pass)
 		fflush(stdout);
 	}
 
-	/* free metalists */
-	for (i = 0; i < GFS2_MAX_META_HEIGHT; i++)
-	{
-		list = &metalist[i];
-		while (!osi_list_empty(list))
-		{
-			bh = osi_list_entry(list->next,
-					    struct gfs2_buffer_head, b_altlist);
-			if (bh == ip->i_bh)
-				osi_list_del(&bh->b_altlist);
-			else
-				brelse(bh);
-			osi_list_del(&bh->b_altlist);
-		}
-	}
-
 end:
         if (S_ISDIR(ip->i_di.di_mode)) {
 		/* check validity of leaf blocks and leaf chains */
@@ -1217,7 +1359,7 @@ int delete_blocks(struct gfs2_inode *ip, uint64_t block,
 				  (unsigned long long)block,
 				  (unsigned long long)ip->i_di.di_num.no_addr,
 				  (unsigned long long)ip->i_di.di_num.no_addr);
-			gfs2_blockmap_set(bl, block, gfs2_block_free);
+			fsck_blockmap_set(ip, block, btype, gfs2_block_free);
 			gfs2_free_block(ip->i_sbd, block);
 		}
 	}
diff --git a/gfs2/fsck/metawalk.h b/gfs2/fsck/metawalk.h
index 08fc9e1..f8feb2d 100644
--- a/gfs2/fsck/metawalk.h
+++ b/gfs2/fsck/metawalk.h
@@ -39,10 +39,18 @@ extern int delete_eattr_indir(struct gfs2_inode *ip, uint64_t block,
 extern int delete_eattr_leaf(struct gfs2_inode *ip, uint64_t block,
 			     uint64_t parent, struct gfs2_buffer_head **bh,
 			     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);
+extern int check_n_fix_bitmap(struct gfs2_sbd *sdp, uint64_t blk,
+		       enum gfs2_mark_block new_blockmap_state);
 extern struct duptree *dupfind(uint64_t block);
 
 #define is_duplicate(dblock) ((dupfind(dblock)) ? 1 : 0)
 
+#define fsck_blockmap_set(ip, b, bt, m) _fsck_blockmap_set(ip, b, bt, m, \
+							   __FILE__, __LINE__)
+
 /* metawalk_fxns: function pointers to check various parts of the fs
  *
  * The functions should return -1 on fatal errors, 1 if the block
diff --git a/gfs2/fsck/pass1.c b/gfs2/fsck/pass1.c
index 804522a..18bb3ae 100644
--- a/gfs2/fsck/pass1.c
+++ b/gfs2/fsck/pass1.c
@@ -88,12 +88,7 @@ static int leaf(struct gfs2_inode *ip, uint64_t block,
 {
 	struct block_count *bc = (struct block_count *) private;
 
-	log_debug( _("\tDinode %lld (0x%llx) references leaf block #%lld "
-		     "(0x%llx)\n"),
-		   (unsigned long long)ip->i_di.di_num.no_addr,
-		   (unsigned long long)ip->i_di.di_num.no_addr,
-		   (unsigned long long)block, (unsigned long long)block);
-	gfs2_blockmap_set(bl, block, gfs2_leaf_blk);
+	fsck_blockmap_set(ip, block, _("directory leaf"), gfs2_leaf_blk);
 	bc->indir_count++;
 	return 0;
 }
@@ -109,8 +104,8 @@ static int check_metalist(struct gfs2_inode *ip, uint64_t block,
 	*bh = NULL;
 
 	if (gfs2_check_range(ip->i_sbd, block)){ /* blk outside of FS */
-		gfs2_blockmap_set(bl, ip->i_di.di_num.no_addr,
-			       gfs2_bad_block);
+		fsck_blockmap_set(ip, ip->i_di.di_num.no_addr,
+				  _("itself"), gfs2_bad_block);
 		log_debug( _("Bad indirect block pointer (out of range).\n"));
 
 		return 1;
@@ -128,7 +123,8 @@ static int check_metalist(struct gfs2_inode *ip, uint64_t block,
 		log_debug( _("Bad indirect block pointer (points to "
 			     "something that is not an indirect block).\n"));
 		if(!found_dup) {
-			gfs2_blockmap_set(bl, block, gfs2_meta_inval);
+			fsck_blockmap_set(ip, block, _("bad indirect"),
+					  gfs2_meta_inval);
 			brelse(nbh);
 			return 1;
 		}
@@ -136,11 +132,9 @@ static int check_metalist(struct gfs2_inode *ip, uint64_t block,
 	} else /* blk check ok */
 		*bh = nbh;
 
-	if (!found_dup) {
-		log_debug( _("Setting %" PRIu64 " (0x%" PRIx64 ") to indirect "
-			     "block.\n"), block, block);
-		gfs2_blockmap_set(bl, block, gfs2_indir_blk);
-	}
+	if (!found_dup)
+		fsck_blockmap_set(ip, block, _("indirect"),
+				  gfs2_indir_blk);
 	bc->indir_count++;
 
 	return 0;
@@ -150,7 +144,7 @@ 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, btype;
+	int error = 0;
 
 	if (gfs2_check_range(ip->i_sbd, block)) {
 		log_err( _("inode %lld (0x%llx) has a bad data block pointer "
@@ -161,8 +155,9 @@ static int check_data(struct gfs2_inode *ip, uint64_t block, void *private)
 		/* Mark the owner of this block with the bad_block
 		 * designator so we know to check it for out of range
 		 * blocks later */
-		gfs2_blockmap_set(bl, ip->i_di.di_num.no_addr,
-			       gfs2_bad_block);
+		fsck_blockmap_set(ip, ip->i_di.di_num.no_addr,
+				  _("bad (out of range) data"),
+				  gfs2_bad_block);
 		return 1;
 	}
 	q = block_type(block);
@@ -190,41 +185,7 @@ static int check_data(struct gfs2_inode *ip, uint64_t block, void *private)
 		gfs2_block_unmark(ip->i_sbd, bl, block, gfs2_meta_inval);
 		gfs2_dup_set(block);
 	}
-	log_debug( _("Marking block %llu (0x%llx) as data block\n"),
-		   (unsigned long long)block, (unsigned long long)block);
-	gfs2_blockmap_set(bl, block, gfs2_block_used);
-
-	/* This is also confusing, so I'll clarify.  There are two bitmaps:
-	   (1) The gfs2_bmap that fsck uses to keep track of what block
-	   type has been discovered, and (2) The rgrp bitmap.  Function
-	   gfs2_blockmap_set is used to set the former and gfs2_set_bitmap
-	   is used to set the latter.  In this function we need to set both
-	   because we found a "data" block that could be "meta" in the rgrp
-	   bitmap.  If we don't we could run into the data block again as
-	   metadata when we're traversing the metadata with gfs2_next_rg_meta
-	   in func pass1().  If that happens, it will look at the block,
-	   say "hey this isn't metadata" and mark it incorrectly as an
-	   invalid metadata block and free it.  Ordinarily, fsck will wait
-	   until pass5 to sync (2) so that it agrees with (1).  However, in
-	   this case, it's better to do it upfront.  The duplicate solving
-	   code in pass1b.c is better at resolving metadata referencing a
-	   data block than it is at resolving a data block referencing a
-	   metadata block. */
-	btype = gfs2_get_bitmap(ip->i_sbd, block, NULL);
-	if (btype != GFS2_BLKST_USED) {
-		const char *allocdesc[] = {"free space", "data", "unlinked",
-					   "metadata", "reserved"};
-
-		log_err( _("Block %llu (0x%llx) seems to be data, but is "
-			   "marked as %s.\n"), (unsigned long long)block,
-			   (unsigned long long)block, allocdesc[btype]);
-		if(query( _("Okay to mark it as 'data'? (y/n)"))) {
-			gfs2_set_bitmap(ip->i_sbd, block, GFS2_BLKST_USED);
-			log_err( _("The block was reassigned as data.\n"));
-		} else {
-			log_err( _("The invalid block was ignored.\n"));
-		}
-	}
+	fsck_blockmap_set(ip, block, _("data"), gfs2_block_used);
 	bc->data_count++;
 	return error;
 }
@@ -232,11 +193,9 @@ static int check_data(struct gfs2_inode *ip, uint64_t block, void *private)
 static int remove_inode_eattr(struct gfs2_inode *ip, struct block_count *bc,
 			      int duplicate)
 {
-	if (!duplicate) {
-		gfs2_set_bitmap(ip->i_sbd, ip->i_di.di_eattr,
-				GFS2_BLKST_FREE);
-		gfs2_blockmap_set(bl, ip->i_di.di_eattr, gfs2_block_free);
-	}
+	if (!duplicate)
+		fsck_blockmap_set(ip, ip->i_di.di_eattr, _("deleted eattr"),
+				  gfs2_block_free);
 	ip->i_di.di_eattr = 0;
 	bc->ea_count = 0;
 	ip->i_di.di_blocks = 1 + bc->indir_count + bc->data_count;
@@ -276,8 +235,6 @@ static int ask_remove_inode_eattr(struct gfs2_inode *ip,
 static int clear_eas(struct gfs2_inode *ip, struct block_count *bc,
 		     uint64_t block, int duplicate, const char *emsg)
 {
-	struct gfs2_sbd *sdp = ip->i_sbd;
-
 	log_err( _("Inode #%llu (0x%llx): %s"),
 		(unsigned long long)ip->i_di.di_num.no_addr,
 		(unsigned long long)ip->i_di.di_num.no_addr, emsg);
@@ -289,8 +246,8 @@ static int clear_eas(struct gfs2_inode *ip, struct block_count *bc,
 			log_err( _("The bad extended attribute was "
 				   "removed.\n"));
 		} else if (!duplicate) {
-			gfs2_blockmap_set(bl, block, gfs2_block_free);
-			gfs2_set_bitmap(sdp, block, GFS2_BLKST_FREE);
+			fsck_blockmap_set(ip, block, _("removed eattr"),
+					  gfs2_block_free);
 			log_err( _("The bad Extended Attribute was "
 				   "removed.\n"));
 		}
@@ -355,10 +312,9 @@ static int check_eattr_indir(struct gfs2_inode *ip, uint64_t indirect,
 		bc->ea_count++;
 		ret = 1;
 	} else {
-		log_debug( _("Setting #%" PRIu64 " (0x%" PRIx64
-			  ") to indirect Extended Attribute block\n"),
-			   indirect, indirect);
-		gfs2_blockmap_set(bl, indirect, gfs2_indir_blk);
+		fsck_blockmap_set(ip, indirect,
+				  _("indirect Extended Attribute"),
+				  gfs2_indir_blk);
 		bc->ea_count++;
 	}
 	return ret;
@@ -440,12 +396,10 @@ static int check_leaf_block(struct gfs2_inode *ip, uint64_t block, int btype,
 		brelse(leaf_bh);
 		return 1;
 	}
-	log_debug( _("Setting block #%lld (0x%llx) to eattr block\n"),
-		   (unsigned long long)block, (unsigned long long)block);
 	/* Point of confusion: We've got to set the ea block itself to
 	   gfs2_meta_eattr here.  Elsewhere we mark the inode with
 	   gfs2_eattr_block meaning it contains an eattr for pass1c. */
-	gfs2_blockmap_set(bl, block, gfs2_meta_eattr);
+	fsck_blockmap_set(ip, block, _("Extended Attribute"), gfs2_meta_eattr);
 	bc->ea_count++;
 	*bh = leaf_bh;
 	return 0;
@@ -483,7 +437,9 @@ static int check_extended_leaf_eattr(struct gfs2_inode *ip, uint64_t *data_ptr,
 			 (unsigned long long)ip->i_di.di_eattr,
 			 (unsigned long long)el_blk,
 			 (unsigned long long)el_blk);
-		gfs2_blockmap_set(bl, ip->i_di.di_eattr, gfs2_bad_block);
+		fsck_blockmap_set(ip, ip->i_di.di_eattr,
+				  _("bad (out of range) Extended Attribute "),
+				  gfs2_bad_block);
 		return 1;
 	}
 	error = check_leaf_block(ip, el_blk, GFS2_METATYPE_ED, &bh, private);
@@ -514,7 +470,9 @@ static int check_eattr_leaf(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,
 			 (unsigned long long)block, (unsigned long long)block);
-		gfs2_blockmap_set(bl, ip->i_di.di_eattr, gfs2_bad_block);
+		fsck_blockmap_set(ip, ip->i_di.di_eattr,
+				  _("bad (out of range) Extended "
+				    "Attribute leaf"), gfs2_bad_block);
 		return 1;
 	}
 	return check_leaf_block(ip, block, GFS2_METATYPE_EA, bh, private);
@@ -566,38 +524,26 @@ static int clear_metalist(struct gfs2_inode *ip, uint64_t block,
 {
 	*bh = NULL;
 
-	if(!is_duplicate(block)) {
-		gfs2_blockmap_set(bl, block, gfs2_block_free);
-		return 0;
-	}
+	if(!is_duplicate(block))
+		fsck_blockmap_set(ip, block, _("cleared metadata"),
+				  gfs2_block_free);
 	return 0;
 }
 
 static int clear_data(struct gfs2_inode *ip, uint64_t block, void *private)
 {
-	if(!is_duplicate(block)) {
-		gfs2_blockmap_set(bl, block, gfs2_block_free);
-		return 0;
-	}
+	if(!is_duplicate(block))
+		fsck_blockmap_set(ip, block, _("cleared data"),
+				  gfs2_block_free);
 	return 0;
-
 }
 
 static int clear_leaf(struct gfs2_inode *ip, uint64_t block,
 	       struct gfs2_buffer_head *bh, void *private)
 {
-	log_crit( _("Clearing leaf #%" PRIu64 " (0x%" PRIx64 ")\n"),
-		  block, block);
-
-	if(!is_duplicate(block)) {
-		log_crit( _("Setting leaf #%" PRIu64 " (0x%" PRIx64 ") invalid\n"),
-				 block, block);
-		if(gfs2_blockmap_set(bl, block, gfs2_block_free)) {
-			stack;
-			return -1;
-		}
-		return 0;
-	}
+	if(!is_duplicate(block))
+		fsck_blockmap_set(ip, block, _("cleared directory leaf"),
+				  gfs2_block_free);
 	return 0;
 }
 
@@ -644,9 +590,8 @@ static int handle_di(struct gfs2_sbd *sdp, struct gfs2_buffer_head *bh,
 	switch(ip->i_di.di_mode & S_IFMT) {
 
 	case S_IFDIR:
-		log_debug( _("Setting %" PRIu64 " (0x%" PRIx64 ") to directory inode.\n"),
-				  block, block);
-		if(gfs2_blockmap_set(bl, block, gfs2_inode_dir)) {
+		if (fsck_blockmap_set(ip, block, _("directory"),
+				      gfs2_inode_dir)) {
 			stack;
 			fsck_inode_put(&ip);
 			return -1;
@@ -658,68 +603,60 @@ static int handle_di(struct gfs2_sbd *sdp, struct gfs2_buffer_head *bh,
 		}
 		break;
 	case S_IFREG:
-		log_debug( _("Setting %" PRIu64 " (0x%" PRIx64 ") to file inode.\n"),
-				  block, block);
-		if(gfs2_blockmap_set(bl, block, gfs2_inode_file)) {
+		if (fsck_blockmap_set(ip, block, _("file"),
+				      gfs2_inode_file)) {
 			stack;
 			fsck_inode_put(&ip);
 			return -1;
 		}
 		break;
 	case S_IFLNK:
-		log_debug( _("Setting %" PRIu64 " (0x%" PRIx64 ") to symlink inode.\n"),
-				  block, block);
-		if(gfs2_blockmap_set(bl, block, gfs2_inode_lnk)) {
+		if (fsck_blockmap_set(ip, block, _("symlink"),
+				      gfs2_inode_lnk)) {
 			stack;
 			fsck_inode_put(&ip);
 			return -1;
 		}
 		break;
 	case S_IFBLK:
-		log_debug( _("Setting %" PRIu64 " (0x%" PRIx64 ") to block dev inode.\n"),
-				  block, block);
-		if(gfs2_blockmap_set(bl, block, gfs2_inode_blk)) {
+		if (fsck_blockmap_set(ip, block, _("block device"),
+				      gfs2_inode_blk)) {
 			stack;
 			fsck_inode_put(&ip);
 			return -1;
 		}
 		break;
 	case S_IFCHR:
-		log_debug( _("Setting %" PRIu64 " (0x%" PRIx64 ") to char dev inode.\n"),
-				  block, block);
-		if(gfs2_blockmap_set(bl, block, gfs2_inode_chr)) {
+		if (fsck_blockmap_set(ip, block, _("character device"),
+				      gfs2_inode_chr)) {
 			stack;
 			fsck_inode_put(&ip);
 			return -1;
 		}
 		break;
 	case S_IFIFO:
-		log_debug( _("Setting %" PRIu64 " (0x%" PRIx64 ") to fifo inode.\n"),
-				  block, block);
-		if(gfs2_blockmap_set(bl, block, gfs2_inode_fifo)) {
+		if (fsck_blockmap_set(ip, block, _("fifo"),
+				      gfs2_inode_fifo)) {
 			stack;
 			fsck_inode_put(&ip);
 			return -1;
 		}
 		break;
 	case S_IFSOCK:
-		log_debug( _("Setting %" PRIu64 " (0x%" PRIx64 ") to socket inode.\n"),
-				  block, block);
-		if(gfs2_blockmap_set(bl, block, gfs2_inode_sock)) {
+		if (fsck_blockmap_set(ip, block, _("socket"),
+				      gfs2_inode_sock)) {
 			stack;
 			fsck_inode_put(&ip);
 			return -1;
 		}
 		break;
 	default:
-		log_debug( _("Setting %" PRIu64 " (0x%" PRIx64 ") to invalid.\n"),
-				  block, block);
-		if(gfs2_blockmap_set(bl, block, gfs2_meta_inval)) {
+		if (fsck_blockmap_set(ip, block, _("invalid mode"),
+				      gfs2_meta_inval)) {
 			stack;
 			fsck_inode_put(&ip);
 			return -1;
 		}
-		gfs2_set_bitmap(sdp, block, GFS2_BLKST_FREE);
 		fsck_inode_put(&ip);
 		return 0;
 	}
@@ -757,14 +694,12 @@ static int handle_di(struct gfs2_sbd *sdp, struct gfs2_buffer_head *bh,
 				 (unsigned long long)ip->i_di.di_num.no_addr,
 				 ip->i_di.di_depth,
 				 (1 >> (ip->i_di.di_size/sizeof(uint64_t))));
-			/* once implemented, remove continue statement */
-			log_warn( _("Marking inode invalid\n"));
-			if(gfs2_blockmap_set(bl, block, gfs2_meta_inval)) {
+			if(fsck_blockmap_set(ip, block, _("bad depth"),
+					     gfs2_meta_inval)) {
 				stack;
 				fsck_inode_put(&ip);
 				return -1;
 			}
-			gfs2_set_bitmap(sdp, block, GFS2_BLKST_FREE);
 			fsck_inode_put(&ip);
 			return 0;
 		}
@@ -783,9 +718,8 @@ static int handle_di(struct gfs2_sbd *sdp, struct gfs2_buffer_head *bh,
 			 (unsigned long long)ip->i_di.di_num.no_addr);
 		/* FIXME: Must set all leaves invalid as well */
 		check_metatree(ip, &invalidate_metatree);
-		gfs2_blockmap_set(bl, ip->i_di.di_num.no_addr,
-				  gfs2_meta_inval);
-		gfs2_set_bitmap(sdp, ip->i_di.di_num.no_addr, GFS2_BLKST_FREE);
+		fsck_blockmap_set(ip, ip->i_di.di_num.no_addr,
+				  _("corrupt"), gfs2_meta_inval);
 		fsck_inode_put(&ip);
 		return 0;
 	}
@@ -911,16 +845,14 @@ int pass1(struct gfs2_sbd *sbp)
 					   "%llu (0x%llx)\n"),
 					 (unsigned long long)block,
 					 (unsigned long long)block);
-				if(query( _("Okay to free the invalid block? "
-					    "(y/n)"))) {
-					gfs2_set_bitmap(sbp, block,
-							GFS2_BLKST_FREE);
-					log_err( _("The invalid block was "
-						   "freed.\n"));
-				} else {
-					log_err( _("The invalid block was "
-						   "ignored.\n"));
+				if (gfs2_blockmap_set(bl, block,
+						      gfs2_block_free)) {
+					stack;
+					brelse(bh);
+					return FSCK_ERROR;
 				}
+				check_n_fix_bitmap(sbp, block,
+						   gfs2_block_free);
 			} else if (handle_di(sbp, bh, block) < 0) {
 				stack;
 				brelse(bh);
diff --git a/gfs2/fsck/pass1b.c b/gfs2/fsck/pass1b.c
index c482806..0d030f0 100644
--- a/gfs2/fsck/pass1b.c
+++ b/gfs2/fsck/pass1b.c
@@ -180,8 +180,8 @@ static int clear_dup_metalist(struct gfs2_inode *ip, uint64_t block,
 			 dh->id->parent);
 		/* Setting the block to invalid means the inode is
 		 * cleared in pass2 */
-		gfs2_blockmap_set(bl, ip->i_di.di_num.no_addr,
-				  gfs2_meta_inval);
+		fsck_blockmap_set(ip, ip->i_di.di_num.no_addr,
+				  _("inode with duplicate"), gfs2_meta_inval);
 	}
 	return 0;
 }
@@ -212,7 +212,8 @@ static int clear_dup_eattr_indir(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);
-		gfs2_blockmap_set(bl, ip->i_di.di_eattr, gfs2_meta_inval);
+		fsck_blockmap_set(ip, ip->i_di.di_eattr,
+				  _("indirect eattr"), gfs2_meta_inval);
 	}
 
 	return 0;
@@ -238,7 +239,8 @@ static int clear_dup_eattr_leaf(struct gfs2_inode *ip, uint64_t block,
 				dh->id->name ? dh->id->name : "",
 				dh->id->parent, dh->id->parent);
 		/* mark the main eattr block invalid */
-		gfs2_blockmap_set(bl, ip->i_di.di_eattr, gfs2_meta_inval);
+		fsck_blockmap_set(ip, ip->i_di.di_eattr,
+				  _("indirect eattr leaf"), gfs2_meta_inval);
 	}
 
 	return 0;
@@ -308,7 +310,8 @@ static int clear_eattr_extentry(struct gfs2_inode *ip, uint64_t *ea_data_ptr,
 				dh->id->name ? dh->id->name : "",
 				dh->id->parent, dh->id->parent);
 		/* mark the main eattr block invalid */
-		gfs2_blockmap_set(bl, ip->i_di.di_eattr, gfs2_meta_inval);
+		fsck_blockmap_set(ip, ip->i_di.di_eattr,
+				  _("extended eattr leaf"), gfs2_meta_inval);
 	}
 
 	return 0;
@@ -430,9 +433,9 @@ static int handle_dup_blk(struct gfs2_sbd *sbp, struct duptree *b)
 					inodetree_delete(ii);
 				/* Setting the block to invalid means the inode
 				   is cleared in pass2 */
-				gfs2_blockmap_set(bl, ip->i_di.di_num.no_addr,
-						  gfs2_meta_inval);
-				bmodified(ip->i_bh);
+				fsck_blockmap_set(ip, ip->i_di.di_num.no_addr,
+						 _("inode with bad duplicate"),
+						 gfs2_meta_inval);
 				fsck_inode_put(&ip);
 			} else {
 				log_warn( _("The bad inode was not cleared."));
@@ -476,8 +479,8 @@ static int handle_dup_blk(struct gfs2_sbd *sbp, struct duptree *b)
 		if(!id->ea_only)
 			check_metatree(ip, &clear_dup_fxns);
 
-		gfs2_blockmap_set(bl, ip->i_di.di_num.no_addr,
-				  gfs2_meta_inval);
+		fsck_blockmap_set(ip, ip->i_di.di_num.no_addr,
+				  _("inode with duplicate"), gfs2_meta_inval);
 		fsck_inode_put(&ip); /* out, brelse, free */
 		dh.ref_inode_count--;
 		if(dh.ref_inode_count == 1)
diff --git a/gfs2/fsck/pass2.c b/gfs2/fsck/pass2.c
index d2eb61d..290edbb 100644
--- a/gfs2/fsck/pass2.c
+++ b/gfs2/fsck/pass2.c
@@ -225,7 +225,8 @@ static int check_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent,
 			log_err( _("Directory entry not fixed.\n"));
 			goto dentry_is_valid;
 		}
-		gfs2_blockmap_set(bl, ip->i_di.di_num.no_addr,
+		fsck_blockmap_set(ip, ip->i_di.di_num.no_addr,
+				  _("corrupt directory entry"),
 				  gfs2_meta_inval);
 		log_err( _("Bad directory entry deleted.\n"));
 		return 1;
@@ -274,7 +275,8 @@ static int check_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent,
 		check_metatree(entry_ip, &pass2_fxns_delete);
 		if (entry_ip != ip)
 			fsck_inode_put(&entry_ip);
-		gfs2_blockmap_set(bl, entryblock, gfs2_block_free);
+		fsck_blockmap_set(ip, entryblock,
+				  _("bad directory entry"), gfs2_block_free);
 		goto nuke_dentry;
 	}
 	if(q < gfs2_inode_dir || q > gfs2_inode_sock) {
@@ -550,7 +552,8 @@ int check_system_dir(struct gfs2_inode *sysinode, const char *dirname,
 		return -1;
 	}
 	if (error > 0)
-		gfs2_blockmap_set(bl, iblock, gfs2_meta_inval);
+		fsck_blockmap_set(sysinode, iblock, dirname,
+				  gfs2_meta_inval);
 
 	if(check_inode_eattr(sysinode, &pass2_fxns)) {
 		stack;
@@ -730,6 +733,8 @@ int pass2(struct gfs2_sbd *sbp)
 				} else
 					log_err( _("Directory entry to invalid inode remains.\n"));
 			}
+			/* Can't use fsck_blockmap_set here because we don't
+			   have an inode in memory. */
 			gfs2_blockmap_set(bl, dirblk, gfs2_meta_inval);
 		}
 		ip = fsck_load_inode(sbp, dirblk);
diff --git a/gfs2/fsck/pass3.c b/gfs2/fsck/pass3.c
index 719a665..0e3cfbf 100644
--- a/gfs2/fsck/pass3.c
+++ b/gfs2/fsck/pass3.c
@@ -214,9 +214,20 @@ int pass3(struct gfs2_sbd *sbp)
 					log_err( _("Found unlinked directory containing bad block\n"));
 					if(query(
 					   _("Clear unlinked directory with bad blocks? (y/n) "))) {
+						log_warn( _("inode %lld (0x%llx) is "
+						       "now marked as free\n"),
+							  (unsigned long long)
+							  di->dinode,
+							  (unsigned long long)
+							  di->dinode);
+						/* Can't use fsck_blockmap_set
+						   because we don't have ip */
 						gfs2_blockmap_set(bl,
-							       di->dinode,
-							       gfs2_block_free);
+								  di->dinode,
+							  gfs2_block_free);
+						check_n_fix_bitmap(sbp,
+								   di->dinode,
+							   gfs2_block_free);
 						break;
 					} else
 						log_err( _("Unlinked directory with bad block remains\n"));
@@ -229,9 +240,25 @@ int pass3(struct gfs2_sbd *sbp)
 				   q != gfs2_inode_fifo &&
 				   q != gfs2_inode_sock) {
 					log_err( _("Unlinked block marked as inode not an inode\n"));
+					if(!query(_("Clear the unlinked block?"
+						    " (y/n) "))) {
+						log_err( _("The block was not "
+							   "cleared\n"));
+						break;
+					}
+					log_warn( _("inode %lld (0x%llx) is now "
+						    "marked as free\n"),
+						  (unsigned long long)
+						  di->dinode,
+						  (unsigned long long)
+						  di->dinode);
+					/* Can't use fsck_blockmap_set
+					   because we don't have ip */
 					gfs2_blockmap_set(bl, di->dinode,
 							  gfs2_block_free);
-					log_err( _("Cleared\n"));
+					check_n_fix_bitmap(sbp, di->dinode,
+							   gfs2_block_free);
+					log_err( _("The block was cleared\n"));
 					break;
 				}
 
@@ -243,9 +270,10 @@ int pass3(struct gfs2_sbd *sbp)
 				if(!ip->i_di.di_size && !ip->i_di.di_eattr){
 					log_err( _("Unlinked directory has zero size.\n"));
 					if(query( _("Remove zero-size unlinked directory? (y/n) "))) {
-						gfs2_blockmap_set(bl,
-							       di->dinode,
-							       gfs2_block_free);
+						fsck_blockmap_set(ip,
+								  di->dinode,
+						_("zero-sized unlinked inode"),
+							  gfs2_block_free);
 						fsck_inode_put(&ip);
 						break;
 					} else {
diff --git a/gfs2/fsck/pass4.c b/gfs2/fsck/pass4.c
index 764b977..4140fbb 100644
--- a/gfs2/fsck/pass4.c
+++ b/gfs2/fsck/pass4.c
@@ -83,10 +83,10 @@ static int scan_inode_list(struct gfs2_sbd *sbp) {
 					check_inode_eattr(ip,
 							  &pass4_fxns_delete);
 					check_metatree(ip, &pass4_fxns_delete);
-					bmodified(ip->i_bh);
-					fsck_inode_put(&ip);
-					gfs2_blockmap_set(bl, ii->inode,
+					fsck_blockmap_set(ip, ii->inode,
+							  _("bad unlinked"),
 							  gfs2_block_free);
+					fsck_inode_put(&ip);
 					continue;
 				} else
 					log_err( _("Unlinked inode with bad blocks not cleared\n"));
@@ -108,9 +108,10 @@ static int scan_inode_list(struct gfs2_sbd *sbp) {
 					check_inode_eattr(ip,
 							  &pass4_fxns_delete);
 					check_metatree(ip, &pass4_fxns_delete);
-					bmodified(ip->i_bh);
-					gfs2_blockmap_set(bl, ii->inode,
+					fsck_blockmap_set(ip, ii->inode,
+						  _("invalid unlinked"),
 							  gfs2_block_free);
+					fsck_inode_put(&ip);
 					log_err( _("The inode was deleted\n"));
 				} else {
 					log_err( _("The inode was not "
@@ -127,7 +128,8 @@ static int scan_inode_list(struct gfs2_sbd *sbp) {
 			if(!ip->i_di.di_size && !ip->i_di.di_eattr){
 				log_err( _("Unlinked inode has zero size\n"));
 				if(query( _("Clear zero-size unlinked inode? (y/n) "))) {
-					gfs2_blockmap_set(bl, ii->inode,
+					fsck_blockmap_set(ip, ii->inode,
+						_("unlinked zero-length"),
 							  gfs2_block_free);
 					fsck_inode_put(&ip);
 					continue;
diff --git a/gfs2/libgfs2/libgfs2.h b/gfs2/libgfs2/libgfs2.h
index 8950c48..53c71d5 100644
--- a/gfs2/libgfs2/libgfs2.h
+++ b/gfs2/libgfs2/libgfs2.h
@@ -313,6 +313,34 @@ static const inline char *block_type_string(uint8_t q)
 	return blktyp[15];
 }
 
+/* Must be kept in sync with gfs2_mark_block enum above. Blocks marked as
+   invalid or bad are considered metadata until actually freed. */
+static inline int blockmap_to_bitmap(enum gfs2_mark_block m)
+{
+	static int bitmap_states[16] = {
+		GFS2_BLKST_FREE,
+		GFS2_BLKST_USED,
+		GFS2_BLKST_USED,
+		GFS2_BLKST_DINODE,
+		GFS2_BLKST_DINODE,
+
+		GFS2_BLKST_DINODE,
+		GFS2_BLKST_DINODE,
+		GFS2_BLKST_DINODE,
+		GFS2_BLKST_DINODE,
+		GFS2_BLKST_DINODE,
+
+		GFS2_BLKST_FREE,
+		GFS2_BLKST_FREE,
+		GFS2_BLKST_USED,
+		GFS2_BLKST_USED,
+		GFS2_BLKST_USED,
+
+		GFS2_BLKST_USED
+	};
+	return bitmap_states[m];
+}
+
 extern struct gfs2_bmap *gfs2_bmap_create(struct gfs2_sbd *sdp, uint64_t size,
 					  uint64_t *addl_mem_needed);
 extern struct special_blocks *blockfind(struct special_blocks *blist, uint64_t num);