public inbox for cluster-cvs@sourceware.org
help / color / mirror / Atom feed
* cluster: RHEL52 - gfs2_convert results in GFS2 File System Corruption
@ 2009-03-25 22:03 Bob Peterson
  0 siblings, 0 replies; only message in thread
From: Bob Peterson @ 2009-03-25 22:03 UTC (permalink / raw)
  To: cluster-cvs-relay

Gitweb:        http://git.fedorahosted.org/git/cluster.git?p=cluster.git;a=commitdiff;h=e716488ac3bb60ddf7111030b975d852151f6bb6
Commit:        e716488ac3bb60ddf7111030b975d852151f6bb6
Parent:        21fa92bcc567386ffb548ce7a1a7efe42266d889
Author:        Bob Peterson <rpeterso@redhat.com>
AuthorDate:    Wed Mar 25 16:54:36 2009 -0500
Committer:     Bob Peterson <rpeterso@redhat.com>
CommitterDate: Wed Mar 25 16:54:36 2009 -0500

gfs2_convert results in GFS2 File System Corruption

bz 490136 / 491988

The gfs2_convert tool was taking an unreasonable amount of
time and memory, and if your files were big, the could be
corrupted as well.  This patch reworks the functions that
convert dinode data to rearrange the indirect block pointer
data so that it does it quickly, correctly and with minimum
memory usage.
---
 gfs2/convert/gfs2_convert.c |  876 +++++++++++++++++++++++++++++++++++++++----
 gfs2/edit/gfs2hex.c         |    2 +-
 gfs2/edit/hexedit.c         |   39 +-
 gfs2/edit/hexedit.h         |   10 -
 gfs2/edit/savemeta.c        |   81 +----
 gfs2/fsck/fs_recovery.c     |   11 +-
 gfs2/fsck/initialize.c      |    8 +-
 gfs2/fsck/main.c            |    5 +-
 gfs2/fsck/metawalk.c        |   26 +-
 gfs2/fsck/pass1.c           |   14 +-
 gfs2/fsck/pass1b.c          |    4 +-
 gfs2/fsck/pass1c.c          |    6 +-
 gfs2/fsck/pass2.c           |    8 +-
 gfs2/fsck/rgrepair.c        |   12 +-
 gfs2/libgfs2/buf.c          |  116 ++++---
 gfs2/libgfs2/fs_geometry.c  |    2 +-
 gfs2/libgfs2/fs_ops.c       |  145 ++++----
 gfs2/libgfs2/libgfs2.h      |  107 +++++-
 gfs2/libgfs2/misc.c         |   67 ++--
 gfs2/libgfs2/recovery.c     |    6 +-
 gfs2/libgfs2/rgrp.c         |   18 +-
 gfs2/libgfs2/structures.c   |    8 +-
 gfs2/libgfs2/super.c        |   11 +-
 gfs2/mkfs/main_grow.c       |   10 +-
 gfs2/mkfs/main_mkfs.c       |   12 +-
 gfs2/tool/df.c              |    8 +-
 26 files changed, 1199 insertions(+), 413 deletions(-)

diff --git a/gfs2/convert/gfs2_convert.c b/gfs2/convert/gfs2_convert.c
index b0cca07..f388ea8 100644
--- a/gfs2/convert/gfs2_convert.c
+++ b/gfs2/convert/gfs2_convert.c
@@ -119,57 +119,19 @@ struct gfs1_sb {
 	char sb_reserved[96];
 };
 
-struct gfs1_dinode {
-	struct gfs2_meta_header di_header;
-
-	struct gfs2_inum di_num; /* formal inode # and block address */
-
-	uint32_t di_mode;	/* mode of file */
-	uint32_t di_uid;	/* owner's user id */
-	uint32_t di_gid;	/* owner's group id */
-	uint32_t di_nlink;	/* number (qty) of links to this file */
-	uint64_t di_size;	/* number (qty) of bytes in file */
-	uint64_t di_blocks;	/* number (qty) of blocks in file */
-	int64_t di_atime;	/* time last accessed */
-	int64_t di_mtime;	/* time last modified */
-	int64_t di_ctime;	/* time last changed */
-
-	/*  Non-zero only for character or block device nodes  */
-	uint32_t di_major;	/* device major number */
-	uint32_t di_minor;	/* device minor number */
-
-	/*  Block allocation strategy  */
-	uint64_t di_rgrp;	/* dinode rgrp block number */
-	uint64_t di_goal_rgrp;	/* rgrp to alloc from next */
-	uint32_t di_goal_dblk;	/* data block goal */
-	uint32_t di_goal_mblk;	/* metadata block goal */
-
-	uint32_t di_flags;	/* GFS_DIF_... */
-
-	/*  struct gfs_rindex, struct gfs_jindex, or struct gfs_dirent */
-	uint32_t di_payload_format;  /* GFS_FORMAT_... */
-	uint16_t di_type;	/* GFS_FILE_... type of file */
-	uint16_t di_height;	/* height of metadata (0 == stuffed) */
-	uint32_t di_incarn;	/* incarnation (unused, see gfs_meta_header) */
-	uint16_t di_pad;
-
-	/*  These only apply to directories  */
-	uint16_t di_depth;	/* Number of bits in the table */
-	uint32_t di_entries;	/* The # (qty) of entries in the directory */
-
-	/*  This formed an on-disk chain of unused dinodes  */
-	struct gfs2_inum di_next_unused;  /* used in old versions only */
-
-	uint64_t di_eattr;	/* extended attribute block number */
-
-	char di_reserved[56];
-};
-
 struct inode_block {
 	osi_list_t list;
 	uint64_t di_addr;
 };
 
+struct blocklist {
+	osi_list_t list;
+	uint64_t block;
+	struct metapath mp;
+	int height;
+	char *ptrbuf;
+};
+
 struct gfs1_sb  raw_gfs1_ondisk_sb;
 struct gfs2_sbd sb2;
 char device[256];
@@ -180,6 +142,11 @@ uint64_t dirs_fixed;
 uint64_t dirents_fixed;
 char *prog_name = "gfs2_convert"; /* needed by libgfs2 */
 struct gfs1_jindex *sd_jindex = NULL;    /* gfs1 journal index in memory */
+int gfs2_inptrs;
+uint64_t gfs2_heightsize[GFS2_MAX_META_HEIGHT];
+uint64_t gfs2_jheightsize[GFS2_MAX_META_HEIGHT];
+int gfs2_max_height;
+int gfs2_max_jheight;
 
 /* ------------------------------------------------------------------------- */
 /* This function is for libgfs's sake.                                       */
@@ -209,9 +176,13 @@ void convert_bitmaps(struct gfs2_sbd *sdp, struct rgrp_list *rgd2,
 	struct gfs2_buffer_head *bh;
 
 	ri = &rgd2->ri;
-	gfs2_compute_bitstructs(sdp, rgd2); /* mallocs bh as array */
+	if (gfs2_compute_bitstructs(sdp, rgd2)) { /* mallocs bh as array */
+		log_crit("gfs2_convert: Error converting bitmaps.\n");
+		exit(-1);
+	}
 	for (blk = 0; blk < ri->ri_length; blk++) {
-		bh = bget_generic(sdp, ri->ri_addr + blk, read_disk, read_disk);
+		bh = bget_generic(&sdp->nvbuf_list, ri->ri_addr + blk,
+				  read_disk, read_disk);
 		if (!rgd2->bh[blk])
 			rgd2->bh[blk] = bh;
 		x = (blk) ? sizeof(struct gfs2_meta_header) : sizeof(struct gfs2_rgrp);
@@ -223,6 +194,7 @@ void convert_bitmaps(struct gfs2_sbd *sdp, struct rgrp_list *rgd2,
 				if (state == 0x02) /* unallocated metadata state invalid */
 					rgd2->bh[blk]->b_data[x] &= ~(0x02 << (GFS2_BIT_SIZE * y));
 			}
+		brelse(bh, updated);
 	}
 }/* convert_bitmaps */
 
@@ -236,6 +208,7 @@ static int convert_rgs(struct gfs2_sbd *sbp)
 	osi_list_t *tmp;
 	struct gfs2_buffer_head *bh;
 	struct gfs1_rgrp *rgd1;
+	int rgs = 0;
 
 	/* --------------------------------- */
 	/* Now convert its rgs into gfs2 rgs */
@@ -250,17 +223,381 @@ static int convert_rgs(struct gfs2_sbd *sbp)
 		sbp->blks_total += rgd->ri.ri_data;
 		sbp->blks_alloced += (rgd->ri.ri_data - rgd->rg.rg_free);
 		sbp->dinodes_alloced += rgd1->rg_useddi;
-
 		convert_bitmaps(sbp, rgd, TRUE);
 		/* Write the updated rgrp to the gfs2 buffer */
-		bh = bget(sbp, rgd->ri.ri_addr); /* get a gfs2 buffer for the rg */
+		bh = bget(&sbp->nvbuf_list,
+			  rgd->ri.ri_addr); /* get a gfs2 buffer for the rg */
 		gfs2_rgrp_out(&rgd->rg, rgd->bh[0]->b_data);
 		brelse(bh, updated); /* release the buffer */
+		rgs++;
+		if (rgs % 100 == 0) {
+			printf(".");
+			fflush(stdout);
+		}
 	}
 	return 0;
 }/* superblock_cvt */
 
 /* ------------------------------------------------------------------------- */
+/* calc_gfs2_tree_height - calculate new dinode height as if this is gfs2    */
+/*                                                                           */
+/* This is similar to calc_tree_height in libgfs2 but at the point this      */
+/* function is called, I have the wrong (gfs1 not gfs2) constants in place.  */
+/* ------------------------------------------------------------------------- */
+unsigned int calc_gfs2_tree_height(struct gfs2_inode *ip, uint64_t size)
+{
+	uint64_t *arr;
+	unsigned int max, height;
+
+	if (ip->i_di.di_size > size)
+		size = ip->i_di.di_size;
+
+	if (S_ISDIR(ip->i_di.di_mode)) {
+		arr = gfs2_jheightsize;
+		max = gfs2_max_jheight;
+	} else {
+		arr = gfs2_heightsize;
+		max = gfs2_max_height;
+	}
+
+	for (height = 0; height < max; height++)
+		if (arr[height] >= size)
+			break;
+
+	return height;
+}
+
+/* ------------------------------------------------------------------------- */
+/* mp_gfs1_to_gfs2 - convert a gfs1 metapath to a gfs2 metapath.             */
+/* ------------------------------------------------------------------------- */
+void mp_gfs1_to_gfs2(struct gfs2_sbd *sbp, int gfs1_h, int gfs2_h,
+		     struct metapath *gfs1mp, struct metapath *gfs2mp)
+{
+	uint64_t lblock;
+	int h;
+	uint64_t gfs1factor[GFS2_MAX_META_HEIGHT];
+	uint64_t gfs2factor[GFS2_MAX_META_HEIGHT];
+
+	/* figure out multiplication factors for each height - gfs1 */
+	memset(&gfs1factor, 0, sizeof(gfs1factor));
+	gfs1factor[gfs1_h - 1] = 1ull;
+	for (h = gfs1_h - 1; h > 0; h--)
+		gfs1factor[h - 1] = gfs1factor[h] * sbp->sd_inptrs;
+
+	/* figure out multiplication factors for each height - gfs2 */
+	memset(&gfs2factor, 0, sizeof(gfs2factor));
+	gfs2factor[gfs1_h - 1] = 1ull;
+	for (h = gfs2_h - 1; h > 0; h--)
+		gfs2factor[h - 1] = gfs2factor[h] * gfs2_inptrs;
+
+	/* Convert from gfs1 to a logical block */
+	lblock = 0;
+	for (h = 0; h < gfs1_h; h++)
+		lblock += (gfs1mp->mp_list[h] * gfs1factor[h]);
+
+	/* Convert from a logical block back to gfs2 */
+	memset(gfs2mp, 0, sizeof(*gfs2mp));
+	for (h = 0; h < gfs2_h; h++) {
+		/* Can't use do_div here because the factors are too large. */
+		gfs2mp->mp_list[h] = lblock / gfs2factor[h];
+		lblock %= gfs2factor[h];
+	}
+}
+
+/* ------------------------------------------------------------------------- */
+/* fix_metatree - Fix up the metatree to match the gfs2 metapath info        */
+/*                Similar to gfs2_writei in libgfs2 but we're only           */
+/*                interested in rearranging the metadata while leaving the   */
+/*                actual data blocks intact.                                 */
+/* ------------------------------------------------------------------------- */
+void fix_metatree(struct gfs2_sbd *sbp, struct gfs2_inode *ip,
+		  struct blocklist *blk, uint64_t *first_nonzero_ptr,
+		  unsigned int size)
+{
+	uint64_t block;
+	struct gfs2_buffer_head *bh;
+	unsigned int amount, ptramt;
+	int hdrsize, h, copied = 0, new;
+	struct gfs2_meta_header mh;
+	char *srcptr = (char *)first_nonzero_ptr;
+
+	mh.mh_magic = GFS2_MAGIC;
+	mh.mh_type = GFS2_METATYPE_IN;
+	mh.mh_format = GFS2_FORMAT_IN;
+	if (!ip->i_di.di_height)
+		unstuff_dinode(ip);
+
+	ptramt = blk->mp.mp_list[blk->height] * sizeof(uint64_t);
+	amount = size;
+
+	while (copied < size) {
+		bh = bhold(ip->i_bh);
+
+		/* First, build up the metatree */
+		for (h = 0; h < blk->height; h++) {
+			lookup_block(ip, bh, h, &blk->mp, 1, &new, &block);
+			brelse(bh, updated);
+			if (!block)
+				break;
+
+			bh = bread(&sbp->buf_list, block);
+			if (new)
+				memset(bh->b_data, 0, sbp->bsize);
+			gfs2_meta_header_out(&mh, bh->b_data);
+		}
+
+		hdrsize = sizeof(struct gfs2_meta_header);
+
+		if (amount > sbp->bsize - hdrsize - ptramt)
+			amount = sbp->bsize - hdrsize - ptramt;
+
+		memcpy(bh->b_data + hdrsize + ptramt,
+		       (char *)srcptr, amount);
+		srcptr += amount;
+		brelse(bh, updated);
+
+		copied += amount;
+
+		if (hdrsize + ptramt + amount >= sbp->bsize) {
+			/* advance to the next metablock */
+			blk->mp.mp_list[blk->height] +=
+				(amount / sizeof(uint64_t));
+			for (h = blk->height; h > 0; h--) {
+				if (blk->mp.mp_list[h] >= gfs2_inptrs) {
+					blk->mp.mp_list[h] = 0;
+					blk->mp.mp_list[h - 1]++;
+					continue;
+				}
+				break;
+			}
+		}
+		amount = size - copied;
+		ptramt = 0;
+	}
+}
+
+/* ------------------------------------------------------------------------- */
+/* adjust_indirect_blocks - convert all gfs_indirect blocks to gfs2.         */
+/*                                                                           */
+/* This function converts all gfs_indirect blocks to GFS2.  The difference   */
+/* is that gfs1 indirect block has a 64-byte chunk of reserved space that    */
+/* gfs2 does not.  Since GFS block locations (relative to the start of the   */
+/* file have their locations defined by the offset from the end of the       */
+/* structure, all block pointers must be shifted.                            */
+/*                                                                           */
+/* Stuffed inodes don't need to be shifted at since there are no indirect    */
+/* blocks.  Inodes with height 1 don't need to be shifted either, because    */
+/* the dinode size is the same between gfs and gfs2 (232 bytes), and         */
+/* therefore you can fit the same number of block pointers after the dinode  */
+/* structure.  For the normal 4K block size, that's 483 pointers.  For 1K    */
+/* blocks, it's 99 pointers.                                                 */
+/*                                                                           */
+/* At height 2 things get complex.  GFS1 reserves an area of 64 (0x40) bytes */
+/* at the start of the indirect block, so for 4K blocks, you can fit 501     */
+/* pointers.  GFS2 doesn't reserve that space, so you can fit 509 pointers.  */
+/* For 1K blocks, it's 117 pointers in GFS1 and 125 in GFS2.                 */
+/*                                                                           */
+/* That means, for example, that if you have 4K blocks, a 946MB file will    */
+/* require a height of 3 for GFS, but only a height of 2 for GFS2.           */
+/* There isn't a good way to shift the pointers around from one height to    */
+/* another, so the only way to do it is to rebuild all those indirect blocks */
+/* from empty ones.                                                          */
+/*                                                                           */
+/* For example, with a 1K block size, if you do:                             */
+/*                                                                           */
+/* dd if=/mnt/gfs/big of=/tmp/tocompare skip=496572346368 bs=1024 count=1    */
+/*                                                                           */
+/* the resulting metadata paths will look vastly different for the data:     */
+/*                                                                           */
+/* height    0     1     2     3     4     5                                 */
+/* GFS1:  0x16  0x4b  0x70  0x11  0x5e  0x48                                 */
+/* GFS2:  0x10  0x21  0x78  0x05  0x14  0x76                                 */
+/*                                                                           */
+/* To complicate matters, we can't really require free space.  A user might  */
+/* be trying to migrate a "full" gfs1 file system to GFS2.  After we         */
+/* convert the journals to GFS2, we might have more free space, so we can    */
+/* allocate blocks at that time.                                             */
+/*                                                                           */
+/* Assumes: GFS1 values are in place for diptrs and inptrs.                  */
+/*                                                                           */
+/* Returns: 0 on success, -1 on failure                                      */
+/*                                                                           */
+/* Adapted from gfs2_fsck metawalk.c's build_and_check_metalist              */
+/* ------------------------------------------------------------------------- */
+int adjust_indirect_blocks(struct gfs2_sbd *sbp, struct gfs2_buffer_head *dibh,
+			   struct gfs2_inode *ip)
+{
+	uint32_t gfs2_hgt;
+	struct gfs2_buffer_head *bh;
+	osi_list_t *tmp, *x;
+	int h, header_size, bufsize, ptrnum;
+	uint64_t *ptr1, block;
+	uint64_t dinode_size;
+	int error = 0, di_height;
+	struct blocklist blocks, *blk, *newblk;
+	struct metapath gfs2mp;
+
+	/* if there are no indirect blocks to check */
+	if (ip->i_di.di_height <= 1)
+		return 0;
+
+	osi_list_init(&blocks.list);
+
+	/* Add the dinode block to the blocks list */
+	blk = malloc(sizeof(struct blocklist));
+	if (!blk) {
+		log_crit("Error: Can't allocate memory"
+			 " for indirect block fix.\n");
+		return -1;
+	}
+	memset(blk, 0, sizeof(*blk));
+	/* allocate a buffer to hold the pointers */
+	bufsize = sbp->sd_inptrs * sizeof(uint64_t);
+	blk->block = dibh->b_blocknr;
+	blk->ptrbuf = malloc(bufsize);
+	if (!blk->ptrbuf) {
+		log_crit("Error: Can't allocate memory"
+			 " for file conversion.\n");
+		free(blk);
+		return -1;
+	}
+	memset(blk->ptrbuf, 0, bufsize);
+	/* Fill in the pointers from the dinode buffer */
+	memcpy(blk->ptrbuf, dibh->b_data + sizeof(struct gfs_dinode),
+	       sbp->bsize - sizeof(struct gfs_dinode));
+	/* Zero out the pointers so we can fill them in later. */
+	memset(dibh->b_data + sizeof(struct gfs_dinode), 0,
+	       sbp->bsize - sizeof(struct gfs_dinode));
+	osi_list_add_prev(&blk->list, &blocks.list);
+
+	/* Now run the metadata chain and build lists of all metadata blocks */
+	osi_list_foreach(tmp, &blocks.list) {
+		blk = osi_list_entry(tmp, struct blocklist, list);
+
+		if (blk->height >= ip->i_di.di_height - 1)
+			continue;
+		header_size = (blk->height > 0 ? sizeof(struct gfs_indirect) :
+			       sizeof(struct gfs_dinode));
+		for (ptr1 = (uint64_t *)blk->ptrbuf, ptrnum = 0;
+		     ptrnum < sbp->sd_inptrs; ptr1++, ptrnum++) {
+			if (!*ptr1)
+				continue;
+
+			block = be64_to_cpu(*ptr1);
+
+			newblk = malloc(sizeof(struct blocklist));
+			if (!newblk) {
+				log_crit("Error: Can't allocate memory"
+					 " for indirect block fix.\n");
+				error = -1;
+				goto out;
+			}
+			memset(newblk, 0, sizeof(*newblk));
+			newblk->ptrbuf = malloc(bufsize);
+			if (!newblk->ptrbuf) {
+				log_crit("Error: Can't allocate memory"
+					 " for file conversion.\n");
+				free(newblk);
+				goto out;
+			}
+			memset(newblk->ptrbuf, 0, bufsize);
+			newblk->block = block;
+			newblk->height = blk->height + 1;
+			/* Build the metapointer list from our predecessors */
+			for (h = 0; h < blk->height; h++)
+				newblk->mp.mp_list[h] = blk->mp.mp_list[h];
+			newblk->mp.mp_list[h] = ptrnum;
+			/* Queue it to be processed later on in the loop. */
+			osi_list_add_prev(&newblk->list, &blocks.list);
+
+			/* read the new metadata block's pointers */
+			bh = bread(&sbp->buf_list, block);
+			memcpy(newblk->ptrbuf, bh->b_data +
+			       sizeof(struct gfs_indirect), bufsize);
+			/* Zero the buffer so we can fill it in later */
+			memset(bh->b_data + sizeof(struct gfs_indirect), 0,
+			       bufsize);
+			brelse(bh, updated);
+			/* Free the metadata block so we can reuse it.
+			   This allows us to convert a "full" file system. */
+			ip->i_di.di_blocks--;
+			gfs2_free_block(sbp, block);
+		}
+	}
+
+	/* The gfs2 height may be different.  We need to rebuild the
+	   metadata tree to the gfs2 height. */
+	gfs2_hgt = calc_gfs2_tree_height(ip, ip->i_di.di_size);
+	/* Save off the size because we're going to empty the contents
+	   and add the data blocks back in later. */
+	dinode_size = ip->i_di.di_size;
+	ip->i_di.di_size = 0ULL;
+	di_height = ip->i_di.di_height;
+	ip->i_di.di_height = 0;
+
+	/* Now run through the block list a second time.  If the block
+	   is the highest for metadata, rewrite the data to the gfs2
+	   offset. */
+	osi_list_foreach_safe(tmp, &blocks.list, x) {
+		unsigned int len;
+		uint64_t *ptr2;
+
+		blk = osi_list_entry(tmp, struct blocklist, list);
+		/* If it's not metadata that holds data block pointers
+		   (i.e. metadata pointing to other metadata) */
+		if (blk->height != di_height - 1) {
+			osi_list_del(tmp);
+			free(blk->ptrbuf);
+			free(blk);
+			continue;
+		}
+		/* Skip zero pointers at the start of the buffer.  This may
+		   seem pointless, but the gfs1 blocks won't align with the
+		   gfs2 blocks.  That means that a single block write of
+		   gfs1's pointers is likely to span two blocks on gfs2.
+		   That's a problem if the file system is full.
+		   So I'm trying to truncate the data at the start and end
+		   of the buffers (i.e. write only what we need to). */
+		len = bufsize;
+		for (ptr1 = (uint64_t *)blk->ptrbuf, ptrnum = 0;
+		     ptrnum < sbp->sd_inptrs; ptr1++, ptrnum++) {
+			if (*ptr1 != 0x00)
+				break;
+			len -= sizeof(uint64_t);
+		}
+		/* Skip zero bytes at the end of the buffer */
+		ptr2 = (uint64_t *)(blk->ptrbuf + bufsize) - 1;
+		while (len > 0 && *ptr2 == 0) {
+			ptr2--;
+			len -= sizeof(uint64_t);
+		}
+		blk->mp.mp_list[di_height - 1] = ptrnum;
+		mp_gfs1_to_gfs2(sbp, di_height, gfs2_hgt, &blk->mp, &gfs2mp);
+		memcpy(&blk->mp, &gfs2mp, sizeof(struct metapath));
+		if (len)
+			fix_metatree(sbp, ip, blk, ptr1, len);
+		osi_list_del(tmp);
+		free(blk->ptrbuf);
+		free(blk);
+	}
+	ip->i_di.di_size = dinode_size;
+
+	/* Set the new dinode height, which may or may not have changed.  */
+	/* The caller will take it from the ip and write it to the buffer */
+	ip->i_di.di_height = gfs2_hgt;
+	return 0;
+
+out:
+	while (!osi_list_empty(&blocks.list)) {
+		blk = osi_list_entry(tmp, struct blocklist, list);
+		osi_list_del(&blocks.list);
+		free(blk->ptrbuf);
+		free(blk);
+	}
+	return error;
+}
+
+/* ------------------------------------------------------------------------- */
 /* adjust_inode - change an inode from gfs1 to gfs2                          */
 /*                                                                           */
 /* Returns: 0 on success, -1 on failure                                      */
@@ -330,13 +667,15 @@ int adjust_inode(struct gfs2_sbd *sbp, struct gfs2_buffer_head *bh)
 	/*       don't want to shift the data around.                  */
 	/* ----------------------------------------------------------- */
 	if (inode_was_gfs1) {
-		struct gfs1_dinode *gfs1_dinode_struct;
+		struct gfs_dinode *gfs1_dinode_struct;
 
-		gfs1_dinode_struct = (struct gfs1_dinode *)&inode->i_di;
+		gfs1_dinode_struct = (struct gfs_dinode *)&inode->i_di;
 		inode->i_di.di_goal_meta = inode->i_di.di_goal_data;
 		inode->i_di.di_goal_data = 0; /* make sure the upper 32b are 0 */
 		inode->i_di.di_goal_data = gfs1_dinode_struct->di_goal_dblk;
 		inode->i_di.di_generation = 0;
+		if (adjust_indirect_blocks(sbp, bh, inode))
+			return -1;
 	}
 	
 	gfs2_dinode_out(&inode->i_di, bh->b_data);
@@ -360,6 +699,7 @@ int inode_renumber(struct gfs2_sbd *sbp, uint64_t root_inode_addr)
 	struct gfs2_buffer_head *bh;
 	int first;
 	int error = 0;
+	int rgs_processed = 0;
 
 	log_notice("Converting inodes.\n");
 	sbp->md.next_inum = 1; /* starting inode numbering */
@@ -370,6 +710,7 @@ int inode_renumber(struct gfs2_sbd *sbp, uint64_t root_inode_addr)
 	/* Traverse the resource groups to figure out where the inodes are. */
 	/* ---------------------------------------------------------------- */
 	osi_list_foreach(tmp, &sbp->rglist) {
+		rgs_processed++;
 		rgd = osi_list_entry(tmp, struct rgrp_list, list);
 		first = 1;
 		if (gfs2_rgrp_read(sbp, rgd)) {
@@ -382,8 +723,9 @@ int inode_renumber(struct gfs2_sbd *sbp, uint64_t root_inode_addr)
 			/* doesn't think we hung.  (This may take a long time).       */
 			if (tv.tv_sec - seconds) {
 				seconds = tv.tv_sec;
-				log_notice("\r%" PRIu64" inodes converted.",
-						   sbp->md.next_inum);
+				log_notice("\r%" PRIu64" inodes from %d rgs "
+					   "converted.", sbp->md.next_inum,
+					   rgs_processed);
 				fflush(stdout);
 			}
 			/* Get the next metadata block.  Break out if we reach the end. */
@@ -398,7 +740,7 @@ int inode_renumber(struct gfs2_sbd *sbp, uint64_t root_inode_addr)
 				sbp->sd_sb.sb_root_dir.no_addr = block;
 				sbp->sd_sb.sb_root_dir.no_formal_ino = sbp->md.next_inum;
 			}
-			bh = bread(sbp, block);
+			bh = bread(&sbp->buf_list, block);
 			if (!gfs2_check_meta(bh, GFS_METATYPE_DI)) /* if it is an dinode */
 				error = adjust_inode(sbp, bh);
 			else { /* It's metadata, but not an inode, so fix the bitmap. */
@@ -431,7 +773,8 @@ int inode_renumber(struct gfs2_sbd *sbp, uint64_t root_inode_addr)
 		} /* while 1 */
 		gfs2_rgrp_relse(rgd, updated);
 	} /* for all rgs */
-	log_notice("\r%" PRIu64" inodes converted.", sbp->md.next_inum);
+	log_notice("\r%" PRIu64" inodes from %d rgs converted.",
+		   sbp->md.next_inum, rgs_processed);
 	fflush(stdout);
 	return 0;
 }/* inode_renumber */
@@ -445,7 +788,7 @@ int fetch_inum(struct gfs2_sbd *sbp, uint64_t iblock,
 	struct gfs2_buffer_head *bh_fix;
 	struct gfs2_inode *fix_inode;
 
-	bh_fix = bread(sbp, iblock);
+	bh_fix = bread(&sbp->buf_list, iblock);
 	fix_inode = inode_get(sbp, bh_fix);
 	inum->no_formal_ino = fix_inode->i_di.di_num.no_formal_ino;
 	inum->no_addr = fix_inode->i_di.di_num.no_addr;
@@ -609,6 +952,7 @@ int fix_directory_info(struct gfs2_sbd *sbp, osi_list_t *dirs_to_fix)
 	gettimeofday(&tv, NULL);
 	seconds = tv.tv_sec;
 	log_notice("\nFixing file and directory information.\n");
+	fflush(stdout);
 	offset = 0;
 	tmp = NULL;
 	/* for every directory in the list */
@@ -623,7 +967,7 @@ int fix_directory_info(struct gfs2_sbd *sbp, osi_list_t *dirs_to_fix)
 		dir_iblk = (struct inode_block *)fix;
 		dirblock = dir_iblk->di_addr; /* addr of dir inode */
 		/* read in the directory inode */
-		bh_dir = bread(sbp, dirblock);
+		bh_dir = bread(&sbp->buf_list, dirblock);
 		dip = inode_get(sbp, bh_dir);
 		/* fix the directory: either exhash (leaves) or linear (stuffed) */
 		if (dip->i_di.di_flags & GFS2_DIF_EXHASH) {
@@ -701,6 +1045,7 @@ int read_gfs1_jiindex(struct gfs2_sbd *sdp)
 		}
 		journ = sd_jindex + j;
 		gfs1_jindex_in(journ, buf);
+		sdp->jsize = (journ->ji_nsegment * 16 * sdp->bsize) >> 20;
 	}
 	if(j * sizeof(struct gfs1_jindex) != ip->i_di.di_size){
 		log_crit("journal inode size invalid\n");
@@ -714,6 +1059,315 @@ int read_gfs1_jiindex(struct gfs2_sbd *sdp)
 	return -1;
 }
 
+static __inline__ int fs_is_jdata(struct gfs2_inode *ip)
+{
+        return ip->i_di.di_flags & GFS2_DIF_JDATA;
+}
+
+static __inline__ uint64_t *
+gfs1_metapointer(struct gfs2_buffer_head *bh, unsigned int height,
+		 struct metapath *mp)
+{
+	unsigned int head_size = (height > 0) ?
+		sizeof(struct gfs_indirect) : sizeof(struct gfs_dinode);
+
+	return ((uint64_t *)(bh->b_data + head_size)) + mp->mp_list[height];
+}
+
+void gfs1_lookup_block(struct gfs2_inode *ip, struct gfs2_buffer_head *bh,
+		  unsigned int height, struct metapath *mp,
+		  int create, int *new, uint64_t *block)
+{
+	uint64_t *ptr = gfs1_metapointer(bh, height, mp);
+
+	if (*ptr) {
+		*block = be64_to_cpu(*ptr);
+		return;
+	}
+
+	*block = 0;
+
+	if (!create)
+		return;
+
+	if (height == ip->i_di.di_height - 1&&
+	    !(S_ISDIR(ip->i_di.di_mode)))
+		*block = data_alloc(ip);
+	else
+		*block = meta_alloc(ip);
+
+	*ptr = cpu_to_be64(*block);
+	ip->i_di.di_blocks++;
+
+	*new = 1;
+}
+
+void gfs1_block_map(struct gfs2_inode *ip, uint64_t lblock, int *new,
+		    uint64_t *dblock, uint32_t *extlen, int prealloc)
+{
+	struct gfs2_sbd *sdp = ip->i_sbd;
+	struct gfs2_buffer_head *bh;
+	struct metapath *mp;
+	int create = *new;
+	unsigned int bsize;
+	unsigned int height;
+	unsigned int end_of_metadata;
+	unsigned int x;
+	enum update_flags f;
+
+	f = not_updated;
+	*new = 0;
+	*dblock = 0;
+	if (extlen)
+		*extlen = 0;
+
+	if (!ip->i_di.di_height) { /* stuffed */
+		if (!lblock) {
+			*dblock = ip->i_di.di_num.no_addr;
+			if (extlen)
+				*extlen = 1;
+		}
+		return;
+	}
+
+	bsize = (fs_is_jdata(ip)) ? sdp->sd_jbsize : sdp->bsize;
+
+	height = calc_tree_height(ip, (lblock + 1) * bsize);
+	if (ip->i_di.di_height < height) {
+		if (!create)
+			return;
+
+		build_height(ip, height);
+	}
+
+	mp = find_metapath(ip, lblock);
+	end_of_metadata = ip->i_di.di_height - 1;
+
+	bh = bhold(ip->i_bh);
+
+	for (x = 0; x < end_of_metadata; x++) {
+		gfs1_lookup_block(ip, bh, x, mp, create, new, dblock);
+		brelse(bh, f);
+		if (!*dblock)
+			goto out;
+
+		if (*new) {
+			struct gfs2_meta_header mh;
+
+			bh = bget(&sdp->buf_list, *dblock);
+			mh.mh_magic = GFS2_MAGIC;
+			mh.mh_type = GFS2_METATYPE_IN;
+			mh.mh_format = GFS2_FORMAT_IN;
+			gfs2_meta_header_out(&mh, bh->b_data);
+			f = updated;
+		} else {
+			bh = bread(&sdp->buf_list, *dblock);
+			f = not_updated;
+		}
+	}
+
+	if (!prealloc)
+		gfs1_lookup_block(ip, bh, end_of_metadata, mp, create, new,
+				  dblock);
+
+	if (extlen && *dblock) {
+		*extlen = 1;
+
+		if (!*new) {
+			uint64_t tmp_dblock;
+			int tmp_new;
+			unsigned int nptrs;
+
+			nptrs = (end_of_metadata) ? sdp->sd_inptrs : sdp->sd_diptrs;
+
+			while (++mp->mp_list[end_of_metadata] < nptrs) {
+				gfs1_lookup_block(ip, bh, end_of_metadata, mp,
+						  FALSE, &tmp_new,
+						  &tmp_dblock);
+
+				if (*dblock + *extlen != tmp_dblock)
+					break;
+
+				(*extlen)++;
+			}
+		}
+	}
+
+	brelse(bh, f);
+
+ out:
+	free(mp);
+}
+
+int gfs1_readi(struct gfs2_inode *ip, void *buf,
+	       uint64_t offset, unsigned int size)
+{
+	struct gfs2_sbd *sdp = ip->i_sbd;
+	struct gfs2_buffer_head *bh;
+	uint64_t lblock, dblock = 0;
+	uint32_t extlen = 0;
+	unsigned int amount;
+	int not_new = 0;
+	int journaled = fs_is_jdata(ip);
+	int copied = 0;
+
+	if (offset >= ip->i_di.di_size)
+		return 0;
+
+	if ((offset + size) > ip->i_di.di_size)
+		size = ip->i_di.di_size - offset;
+
+	if (!size)
+		return 0;
+
+	if (journaled) {
+		lblock = offset / sdp->sd_jbsize;
+		offset %= sdp->sd_jbsize;
+	} else {
+		lblock = offset >> sdp->sd_sb.sb_bsize_shift;
+		offset &= sdp->sd_sb.sb_bsize - 1;
+	}
+
+	if (!ip->i_di.di_height) /* stuffed */
+		offset += sizeof(struct gfs_dinode);
+	else if (journaled)
+		offset += sizeof(struct gfs2_meta_header);
+
+	while (copied < size) {
+		amount = size - copied;
+		if (amount > sdp->bsize - offset)
+			amount = sdp->bsize - offset;
+
+		if (!extlen)
+			gfs1_block_map(ip, lblock, &not_new, &dblock,
+				       &extlen, FALSE);
+
+		if (dblock) {
+			bh = bread(&sdp->buf_list, dblock);
+			dblock++;
+			extlen--;
+		} else
+			bh = NULL;
+
+
+		if (bh) {
+			memcpy(buf+copied, bh->b_data + offset, amount);
+			brelse(bh, not_updated);
+		} else
+			memset(buf+copied, 0, amount);
+		copied += amount;
+		lblock++;
+
+		offset = (journaled) ? sizeof(struct gfs2_meta_header) : 0;
+	}
+
+	return copied;
+}
+
+/**
+ * gfs1_rindex_read - read in the rg index file
+ *                  Stolen from libgfs2/super.c, but modified to handle gfs1.
+ * @sdp: the incore superblock pointer
+ * fd: optional file handle for rindex file (if meta_fs file system is mounted)
+ *     (if fd is <= zero, it will read from raw device)
+ * @count1: return count of the rgs.
+ *
+ * Returns: 0 on success, -1 on failure
+ */
+int gfs1_rindex_read(struct gfs2_sbd *sdp, int fd, int *count1)
+{
+	unsigned int rg;
+	int error;
+	struct gfs2_rindex buf;
+	struct rgrp_list *rgd, *prev_rgd;
+	uint64_t prev_length = 0;
+
+	*count1 = 0;
+	prev_rgd = NULL;
+	for (rg = 0; ; rg++) {
+		if (fd > 0)
+			error = read(fd, &buf, sizeof(struct gfs2_rindex));
+		else
+			error = gfs1_readi(sdp->md.riinode, (char *)&buf,
+					   (rg * sizeof(struct gfs2_rindex)),
+					   sizeof(struct gfs2_rindex));
+		if (!error)
+			break;
+		if (error != sizeof(struct gfs2_rindex))
+			return -1;
+
+		rgd = (struct rgrp_list *)malloc(sizeof(struct rgrp_list));
+		if (!rgd) {
+			log_crit("Cannot allocate memory for rindex.\n");
+			exit(-1);
+		}
+		memset(rgd, 0, sizeof(struct rgrp_list));
+		osi_list_add_prev(&rgd->list, &sdp->rglist);
+
+		gfs2_rindex_in(&rgd->ri, (char *)&buf);
+
+		rgd->start = rgd->ri.ri_addr;
+		if (prev_rgd) {
+			prev_length = rgd->start - prev_rgd->start;
+			prev_rgd->length = prev_length;
+		}
+
+		if(gfs2_compute_bitstructs(sdp, rgd))
+			return -1;
+
+		(*count1)++;
+		prev_rgd = rgd;
+	}
+	if (prev_rgd)
+		prev_rgd->length = prev_length;
+	return 0;
+}
+
+/**
+ * gfs1_ri_update - attach rgrps to the super block
+ *                  Stolen from libgfs2/super.c, but modified to handle gfs1.
+ * @sdp:
+ *
+ * Given the rgrp index inode, link in all rgrps into the super block
+ * and be sure that they can be read.
+ *
+ * Returns: 0 on success, -1 on failure.
+ */
+int gfs1_ri_update(struct gfs2_sbd *sdp, int fd, int *rgcount)
+{
+	struct rgrp_list *rgd;
+	osi_list_t *tmp;
+	int count1 = 0, count2 = 0;
+	uint64_t errblock = 0;
+
+	if (gfs1_rindex_read(sdp, fd, &count1))
+	    goto fail;
+	for (tmp = sdp->rglist.next; tmp != &sdp->rglist; tmp = tmp->next) {
+		enum update_flags f;
+
+		f = not_updated;
+		rgd = osi_list_entry(tmp, struct rgrp_list, list);
+		errblock = gfs2_rgrp_read(sdp, rgd);
+		if (errblock)
+			return errblock;
+		count2++;
+		if (count2 % 100 == 0) {
+			printf(".");
+			fflush(stdout);
+		}
+	}
+
+	*rgcount = count1;
+	if (count1 != count2)
+		goto fail;
+
+	return 0;
+
+ fail:
+	gfs2_rgrp_free(&sdp->rglist, not_updated);
+	return -1;
+}
+
 /* ------------------------------------------------------------------------- */
 /* init - initialization code                                                */
 /* Returns: 0 on success, -1 on failure                                      */
@@ -721,7 +1375,7 @@ int read_gfs1_jiindex(struct gfs2_sbd *sdp)
 static int init(struct gfs2_sbd *sbp)
 {
 	struct gfs2_buffer_head *bh;
-	int rgcount, i;
+	int rgcount;
 	struct gfs2_inum inum;
 
 	memset(sbp, 0, sizeof(struct gfs2_sbd));
@@ -751,16 +1405,42 @@ static int init(struct gfs2_sbd *sbp)
 	sbp->sd_sb.sb_bsize = GFS2_DEFAULT_BSIZE;
 	sbp->bsize = sbp->sd_sb.sb_bsize;
 	osi_list_init(&sbp->rglist);
-	osi_list_init(&sbp->buf_list);
-	for(i = 0; i < BUF_HASH_SIZE; i++)
-		osi_list_init(&sbp->buf_hash[i]);
+	init_buf_list(sbp, &sbp->buf_list, 128 << 20);
+	init_buf_list(sbp, &sbp->nvbuf_list, 0xffffffff);
 	compute_constants(sbp);
 
-	bh = bread(sbp, GFS2_SB_ADDR >> sbp->sd_fsb2bb_shift);
+	bh = bread(&sbp->buf_list, GFS2_SB_ADDR >> sbp->sd_fsb2bb_shift);
 	memcpy(&raw_gfs1_ondisk_sb, (struct gfs1_sb *)bh->b_data,
 		   sizeof(struct gfs1_sb));
 	gfs2_sb_in(&sbp->sd_sb, bh->b_data);
+	sbp->bsize = sbp->sd_sb.sb_bsize;
+	sbp->sd_inptrs = (sbp->bsize - sizeof(struct gfs_indirect)) /
+		sizeof(uint64_t);
+	sbp->sd_diptrs = (sbp->bsize - sizeof(struct gfs_dinode)) /
+		sizeof(uint64_t);
+	sbp->sd_jbsize = sbp->bsize - sizeof(struct gfs2_meta_header);
 	brelse(bh, not_updated);
+	sbp->sd_max_height = compute_heightsize(sbp, sbp->sd_heightsize,
+						sbp->bsize, sbp->sd_diptrs,
+						sbp->sd_inptrs);
+	sbp->sd_max_jheight = compute_heightsize(sbp, sbp->sd_jheightsize,
+						 sbp->sd_jbsize,
+						 sbp->sd_diptrs,
+						 sbp->sd_inptrs);
+	/* -------------------------------------------------------- */
+	/* Our constants are for gfs1.  Need some for gfs2 as well. */
+	/* -------------------------------------------------------- */
+	gfs2_inptrs = (sbp->bsize - sizeof(struct gfs2_meta_header)) /
+                sizeof(uint64_t); /* How many ptrs can we fit on a block? */
+	memset(gfs2_heightsize, 0, sizeof(gfs2_heightsize));
+	gfs2_max_height = compute_heightsize(sbp, gfs2_heightsize,
+					     sbp->bsize,
+					     sbp->sd_diptrs, gfs2_inptrs);
+	memset(gfs2_jheightsize, 0, sizeof(gfs2_jheightsize));
+	gfs2_max_jheight = compute_heightsize(sbp, gfs2_jheightsize,
+					      sbp->sd_jbsize,
+					      sbp->sd_diptrs, gfs2_inptrs);
+
 	/* ---------------------------------------------- */
 	/* Make sure we're really gfs1                    */
 	/* ---------------------------------------------- */
@@ -789,11 +1469,14 @@ static int init(struct gfs2_sbd *sbp)
 	/* so that it adjusts for the metaheader by faking out the inode to  */
 	/* look like a directory, temporarily.                               */
 	sbp->md.riinode->i_di.di_mode &= ~S_IFMT;
-	sbp->md.riinode->i_di.di_mode |= S_IFDIR; 
-	if (ri_update(sbp, 0, &rgcount)){
+	sbp->md.riinode->i_di.di_mode |= S_IFDIR;
+	printf("Examining file system");
+	if (gfs1_ri_update(sbp, 0, &rgcount)){
 		log_crit("Unable to fill in resource group information.\n");
 		return -1;
 	}
+	printf("\n");
+	fflush(stdout);
 	inode_put(sbp->md.riinode, updated);
 	inode_put(sbp->md.jiinode, updated);
 	log_debug("%d rgs found.\n", rgcount);
@@ -996,6 +1679,7 @@ int journ_space_to_rg(struct gfs2_sbd *sdp)
 		rgd->ri.ri_bitbytes = rgd->ri.ri_data / GFS2_NBBY;
 		convert_bitmaps(sdp, rgd, FALSE); /* allocates rgd2->bh */
 		for (x = 0; x < rgd->ri.ri_length; x++) {
+			rgd->bh[x]->b_count++;
 			if (x)
 				gfs2_meta_header_out(&mh, rgd->bh[x]->b_data);
 			else
@@ -1052,7 +1736,7 @@ void remove_obsolete_gfs1(struct gfs2_sbd *sbp)
 {
 	struct gfs2_inum inum;
 
-	log_notice("Removing obsolete gfs1 structures.\n");
+	log_notice("Removing obsolete GFS1 file system structures.\n");
 	fflush(stdout);
 	/* Delete the old gfs1 Journal index: */
 	gfs2_inum_in(&inum, (char *)&raw_gfs1_ondisk_sb.sb_jindex_di);
@@ -1072,6 +1756,40 @@ void remove_obsolete_gfs1(struct gfs2_sbd *sbp)
 }
 
 /* ------------------------------------------------------------------------- */
+/* lifted from libgfs2/structures.c                                          */
+/* ------------------------------------------------------------------------- */
+void conv_build_jindex(struct gfs2_sbd *sdp)
+{
+	struct gfs2_inode *jindex;
+	unsigned int j;
+
+	jindex = createi(sdp->master_dir, "jindex", S_IFDIR | 0700,
+			 GFS2_DIF_SYSTEM);
+
+	for (j = 0; j < sdp->md.journals; j++) {
+		char name[256];
+		struct gfs2_inode *ip;
+
+		printf("Writing journal #%d...", j + 1);
+		fflush(stdout);
+		sprintf(name, "journal%u", j);
+		ip = createi(jindex, name, S_IFREG | 0600, GFS2_DIF_SYSTEM);
+		write_journal(sdp, ip, j,
+			      sdp->jsize << 20 >> sdp->sd_sb.sb_bsize_shift);
+		inode_put(ip, updated);
+		printf("done.\n");
+		fflush(stdout);
+	}
+
+	if (sdp->debug) {
+		printf("\nJindex:\n");
+		gfs2_dinode_print(&jindex->i_di);
+	}
+
+	inode_put(jindex, updated);
+}
+
+/* ------------------------------------------------------------------------- */
 /* main - mainline code                                                      */
 /* ------------------------------------------------------------------------- */
 int main(int argc, char **argv)
@@ -1103,12 +1821,14 @@ int main(int argc, char **argv)
 	/* Convert incore gfs1 sb to gfs2 sb              */
 	/* ---------------------------------------------- */
 	if (!error) {
-		log_notice("Converting resource groups.\n");
+		log_notice("Converting resource groups.");
+		fflush(stdout);
 		error = convert_rgs(&sb2);
+		log_notice("\n");
 		if (error)
 			log_crit("%s: Unable to convert resource groups.\n",
 					device);
-		bcommit(&sb2); /* write the buffers to disk */
+		bcommit(&sb2.nvbuf_list); /* write the buffers to disk */
 	}
 	/* ---------------------------------------------- */
 	/* Renumber the inodes consecutively.             */
@@ -1117,7 +1837,7 @@ int main(int argc, char **argv)
 		error = inode_renumber(&sb2, sb2.sd_sb.sb_root_dir.no_addr);
 		if (error)
 			log_crit("\n%s: Error renumbering inodes.\n", device);
-		bcommit(&sb2); /* write the buffers to disk */
+		bcommit(&sb2.buf_list); /* write the buffers to disk */
 	}
 	/* ---------------------------------------------- */
 	/* Fix the directories to match the new numbers.  */
@@ -1138,19 +1858,21 @@ int main(int argc, char **argv)
 		error = journ_space_to_rg(&sb2);
 		if (error)
 			log_crit("%s: Error converting journal space.\n", device);
-		bcommit(&sb2); /* write the buffers to disk */
+		bcommit(&sb2.buf_list); /* write the buffers to disk */
 	}
 	/* ---------------------------------------------- */
 	/* Create our system files and directories.       */
 	/* ---------------------------------------------- */
 	if (!error) {
-		log_notice("Building system structures.\n");
+		/* Now we've got to treat it as a gfs2 file system */
+		compute_constants(&sb2);
 		/* Build the master subdirectory. */
 		build_master(&sb2); /* Does not do inode_put */
 		sb2.sd_sb.sb_master_dir = sb2.master_dir->i_di.di_num;
 		/* Build empty journal index file. */
-		build_jindex(&sb2);
-		/* Build out per-node directories */
+		conv_build_jindex(&sb2);
+		log_notice("Building GFS2 file system structures.\n");
+		/* Build the per-node directories */
 		build_per_node(&sb2);
 		/* Create the empty inode number file */
 		build_inum(&sb2); /* Does not do inode_put */
@@ -1169,7 +1891,8 @@ int main(int argc, char **argv)
 		inode_put(sb2.md.inum, updated);
 		inode_put(sb2.md.statfs, updated);
 
-		bcommit(&sb2); /* write the buffers to disk */
+		bcommit(&sb2.buf_list); /* write the buffers to disk */
+		bcommit(&sb2.nvbuf_list); /* write the buffers to disk */
 
 		/* Now delete the now-obsolete gfs1 files: */
 		remove_obsolete_gfs1(&sb2);
@@ -1181,13 +1904,14 @@ int main(int argc, char **argv)
 		/* end because if the tool is interrupted in the middle, we want */
 		/* it to not reject the partially converted fs as already done   */
 		/* when it's run a second time.                                  */
-		bh = bread(&sb2, sb2.sb_addr);
+		bh = bread(&sb2.buf_list, sb2.sb_addr);
 		sb2.sd_sb.sb_fs_format = GFS2_FORMAT_FS;
 		sb2.sd_sb.sb_multihost_format = GFS2_FORMAT_MULTI;
 		gfs2_sb_out(&sb2.sd_sb, bh->b_data);
 		brelse(bh, updated);
 
-		bsync(&sb2); /* write the buffers to disk */
+		bsync(&sb2.buf_list); /* write the buffers to disk */
+		bsync(&sb2.nvbuf_list); /* write the buffers to disk */
 		error = fsync(sb2.device_fd);
 		if (error)
 			perror(device);
diff --git a/gfs2/edit/gfs2hex.c b/gfs2/edit/gfs2hex.c
index a56b4ee..347ac1d 100644
--- a/gfs2/edit/gfs2hex.c
+++ b/gfs2/edit/gfs2hex.c
@@ -272,7 +272,7 @@ void do_dinode_extended(struct gfs2_dinode *di, char *buf)
 
 				if (last >= max_block)
 					break;
-				tmp_bh = bread(&sbd, last);
+				tmp_bh = bread(&sbd.buf_list, last);
 				gfs2_leaf_in(&leaf, tmp_bh->b_data);
 				indirect->ii[indirect_blocks].dirents = 0;
 				for (direntcount = 0, bufoffset = sizeof(struct gfs2_leaf);
diff --git a/gfs2/edit/hexedit.c b/gfs2/edit/hexedit.c
index df94600..251efbd 100644
--- a/gfs2/edit/hexedit.c
+++ b/gfs2/edit/hexedit.c
@@ -630,7 +630,7 @@ void rgcount(void)
 		block = sbd1->sb_rindex_di.no_addr;
 	else
 		block = masterblock("rindex");
-	ribh = bread(&sbd, block);
+	ribh = bread(&sbd.buf_list, block);
 	riinode = inode_get(&sbd, ribh);
 	printf("%lld RGs in this file system.\n",
 	       riinode->i_di.di_size / sizeof(struct gfs2_rindex));
@@ -674,7 +674,7 @@ void set_rgrp_flags(int rgnum, uint32_t new_flags, int modify, int full)
 		block = sbd1->sb_rindex_di.no_addr;
 	else
 		block = masterblock("rindex");
-	ribh = bread(&sbd, block);
+	ribh = bread(&sbd.buf_list, block);
 	riinode = inode_get(&sbd, ribh);
 	if (rgnum >= riinode->i_di.di_size / sizeof(struct gfs2_rindex)) {
 		fprintf(stderr, "Error: File system only has %lld RGs.\n",
@@ -684,7 +684,7 @@ void set_rgrp_flags(int rgnum, uint32_t new_flags, int modify, int full)
 		return;
 	}
 	rgblk = find_rgrp_block(riinode, rgnum);
-	bh = bread(&sbd, rgblk);
+	bh = bread(&sbd.buf_list, rgblk);
 	gfs2_rgrp_in(&rg, bh->b_data);
 	if (modify) {
 		printf("RG #%d (block %llu / 0x%llx) rg_flags changed from 0x%08x to 0x%08x\n",
@@ -708,7 +708,7 @@ void set_rgrp_flags(int rgnum, uint32_t new_flags, int modify, int full)
 	}
 	inode_put(riinode, not_updated);
 	if (modify)
-		bsync(&sbd);
+		bsync(&sbd.buf_list);
 }
 
 /* ------------------------------------------------------------------------ */
@@ -756,7 +756,7 @@ int parse_rindex(struct gfs2_inode *di, int print_rindex)
 				struct gfs2_rgrp rg;
 				struct gfs2_buffer_head *tmp_bh;
 
-				tmp_bh = bread(&sbd, ri.ri_addr);
+				tmp_bh = bread(&sbd.buf_list, ri.ri_addr);
 				gfs2_rgrp_in(&rg, tmp_bh->b_data);
 				gfs2_rgrp_print(&rg);
 				brelse(tmp_bh, not_updated);
@@ -1272,7 +1272,7 @@ int display_extended(void)
 
 	/* Display any indirect pointers that we have. */
 	if (block_is_rindex()) {
-		tmp_bh = bread(&sbd, block);
+		tmp_bh = bread(&sbd.buf_list, block);
 		tmp_inode = inode_get(&sbd, tmp_bh);
 		parse_rindex(tmp_inode, TRUE);
 		brelse(tmp_bh, not_updated);
@@ -1283,31 +1283,35 @@ int display_extended(void)
 	else if (display_indirect(indirect, indirect_blocks, 0, 0) == 0)
 		return -1;
 	else if (block_is_rglist()) {
-		tmp_bh = bread(&sbd, masterblock("rindex"));
+		if (gfs1)
+			tmp_bh = bread(&sbd.buf_list,
+				       sbd1->sb_rindex_di.no_addr);
+		else
+			tmp_bh = bread(&sbd.buf_list, masterblock("rindex"));
 		tmp_inode = inode_get(&sbd, tmp_bh);
 		parse_rindex(tmp_inode, FALSE);
 		brelse(tmp_bh, not_updated);
 	}
 	else if (block_is_jindex()) {
-		tmp_bh = bread(&sbd, block);
+		tmp_bh = bread(&sbd.buf_list, block);
 		tmp_inode = inode_get(&sbd, tmp_bh);
 		print_jindex(tmp_inode);
 		brelse(tmp_bh, not_updated);
 	}
 	else if (block_is_inum_file()) {
-		tmp_bh = bread(&sbd, block);
+		tmp_bh = bread(&sbd.buf_list, block);
 		tmp_inode = inode_get(&sbd, tmp_bh);
 		print_inum(tmp_inode);
 		brelse(tmp_bh, not_updated);
 	}
 	else if (block_is_statfs_file()) {
-		tmp_bh = bread(&sbd, block);
+		tmp_bh = bread(&sbd.buf_list, block);
 		tmp_inode = inode_get(&sbd, tmp_bh);
 		print_statfs(tmp_inode);
 		brelse(tmp_bh, not_updated);
 	}
 	else if (block_is_quota_file()) {
-		tmp_bh = bread(&sbd, block);
+		tmp_bh = bread(&sbd.buf_list, block);
 		tmp_inode = inode_get(&sbd, tmp_bh);
 		print_quota(tmp_inode);
 		brelse(tmp_bh, not_updated);
@@ -1320,8 +1324,6 @@ int display_extended(void)
 /* ------------------------------------------------------------------------ */
 void read_superblock(int fd)
 {
-	int x;
-
 	sbd1 = (struct gfs_sb *)&sbd.sd_sb;
 	ioctl(fd, BLKFLSBUF, 0);
 	do_lseek(fd, 0x10 * 4096);
@@ -1335,9 +1337,8 @@ void read_superblock(int fd)
 	sbd.qcsize = GFS2_DEFAULT_QCSIZE;
 	sbd.time = time(NULL);
 	osi_list_init(&sbd.rglist);
-	osi_list_init(&sbd.buf_list);
-	for (x = 0; x < BUF_HASH_SIZE; x++)
-		osi_list_init(&sbd.buf_hash[x]);
+	init_buf_list(&sbd, &sbd.buf_list, 128 << 20);
+	init_buf_list(&sbd, &sbd.nvbuf_list, 0xffffffff);
 	compute_constants(&sbd);
 	gfs2_sb_in(&sbd.sd_sb, buf); /* parse it out into the sb structure */
 	/* Check to see if this is really gfs1 */
@@ -2048,7 +2049,7 @@ void dump_journal(const char *journal)
 	else
 		jindex_block = masterblock("jindex");
 	/* read in the block */
-	jindex_bh = bread(&sbd, jindex_block);
+	jindex_bh = bread(&sbd.buf_list, jindex_block);
 	/* get the dinode data from it. */
 	gfs2_dinode_in(&di, jindex_bh->b_data); /* parse disk inode into structure */
 
@@ -2071,7 +2072,7 @@ void dump_journal(const char *journal)
 		j_size = ji.ji_nsegment * 0x10;
 	} else {
 		jblock = indirect->ii[0].dirent[journal_num + 2].block;
-		j_bh = bread(&sbd, jblock);
+		j_bh = bread(&sbd.buf_list, jblock);
 		j_inode = inode_get(&sbd, j_bh);
 		gfs2_dinode_in(&jdi, j_bh->b_data);/* parse dinode to struct */
 		j_size = jdi.di_size;
@@ -2081,7 +2082,7 @@ void dump_journal(const char *journal)
 		if (gfs1) {
 			if (j_bh)
 				brelse(j_bh, not_updated);
-			j_bh = bread(&sbd, jblock + jb);
+			j_bh = bread(&sbd.buf_list, jblock + jb);
 			memcpy(jbuf, j_bh->b_data, bufsize);
 		}
 		else
diff --git a/gfs2/edit/hexedit.h b/gfs2/edit/hexedit.h
index 4778ab1..b16783d 100644
--- a/gfs2/edit/hexedit.h
+++ b/gfs2/edit/hexedit.h
@@ -162,12 +162,6 @@ struct iinfo {
 	struct indirect_info ii[512];
 };
 
-struct gfs_indirect {
-	struct gfs2_meta_header in_header;
-
-	char in_reserved[64];
-};
-
 struct blkstack_info {
 	uint64_t block;
 	int start_row[DMODES];
@@ -179,10 +173,6 @@ struct blkstack_info {
 	int gfs2_struct_type;
 };
 
-struct metapath {
-	uint64_t mp_list[GFS2_MAX_META_HEIGHT];
-};
-
 struct gfs_sb {
 	/*  Order is important; need to be able to read old superblocks
 	    in order to support on-disk version upgrades */
diff --git a/gfs2/edit/savemeta.c b/gfs2/edit/savemeta.c
index c73dc09..c4e2efb 100644
--- a/gfs2/edit/savemeta.c
+++ b/gfs2/edit/savemeta.c
@@ -59,59 +59,6 @@ static __inline__ int fs_is_jdata(struct gfs2_inode *ip)
         return ip->i_di.di_flags & GFS2_DIF_JDATA;
 }
 
-static struct metapath *find_metapath(struct gfs2_inode *ip, uint64_t block)
-{
-	struct gfs2_sbd *sdp = ip->i_sbd;
-	struct metapath *mp;
-	uint64_t b = block;
-	unsigned int i;
-
-	zalloc(mp, sizeof(struct metapath));
-
-	for (i = ip->i_di.di_height; i--;)
-		mp->mp_list[i] = do_div(b, sdp->sd_inptrs);
-
-	return mp;
-}
-
-static __inline__ uint64_t *
-metapointer(struct gfs2_buffer_head *bh, unsigned int height,
-			struct metapath *mp)
-{
-	unsigned int head_size = (height > 0) ?
-		sizeof(struct gfs2_meta_header) : sizeof(struct gfs2_dinode);
-
-	return ((uint64_t *)(bh->b_data + head_size)) + mp->mp_list[height];
-}
-
-static void lookup_block(struct gfs2_inode *ip,
-	     struct gfs2_buffer_head *bh, unsigned int height, struct metapath *mp,
-	     int create, int *new, uint64_t *block)
-{
-	uint64_t *ptr = metapointer(bh, height, mp);
-
-	if (*ptr) {
-		*block = be64_to_cpu(*ptr);
-		return;
-	}
-
-	*block = 0;
-
-	if (!create)
-		return;
-
-	if (height == ip->i_di.di_height - 1&&
-	    !(S_ISDIR(ip->i_di.di_mode)))
-		*block = data_alloc(ip);
-	else
-		*block = meta_alloc(ip);
-
-	*ptr = cpu_to_be64(*block);
-	ip->i_di.di_blocks++;
-
-	*new = 1;
-}
-
 void gfs1_block_map(struct gfs2_inode *ip, uint64_t lblock, int *new,
 		    uint64_t *dblock, uint32_t *extlen, int prealloc)
 {
@@ -164,14 +111,14 @@ void gfs1_block_map(struct gfs2_inode *ip, uint64_t lblock, int *new,
 		if (*new) {
 			struct gfs2_meta_header mh;
 
-			bh = bget(sdp, *dblock);
+			bh = bget(&sdp->buf_list, *dblock);
 			mh.mh_magic = GFS2_MAGIC;
 			mh.mh_type = GFS2_METATYPE_IN;
 			mh.mh_format = GFS2_FORMAT_IN;
 			gfs2_meta_header_out(&mh, bh->b_data);
 			f = updated;
 		} else
-			bh = bread(sdp, *dblock);
+			bh = bread(&sdp->buf_list, *dblock);
 	}
 
 	if (!prealloc)
@@ -249,7 +196,7 @@ int gfs1_readi(struct gfs2_inode *ip, void *buf,
 				       &extlen, FALSE);
 
 		if (dblock) {
-			bh = bread(sdp, dblock);
+			bh = bread(&sdp->buf_list, dblock);
 			dblock++;
 			extlen--;
 		} else
@@ -562,7 +509,7 @@ void save_indirect_blocks(int out_fd, osi_list_t *cur_list,
 		old_block = indir_block;
 		save_block(sbd.device_fd, out_fd, indir_block);
 		if (height != hgt) { /* If not at max height */
-			nbh = bread(&sbd, indir_block);
+			nbh = bread(&sbd.buf_list, indir_block);
 			osi_list_add_prev(&nbh->b_altlist,
 					  cur_list);
 			brelse(nbh, not_updated);
@@ -597,8 +544,8 @@ void save_inode_data(int out_fd)
 
 	for (i = 0; i < GFS2_MAX_META_HEIGHT; i++)
 		osi_list_init(&metalist[i]);
-	buf = malloc(bufsize);
-	metabh = bread(&sbd, block);
+	buf = malloc(sbd.bsize);
+	metabh = bread(&sbd.buf_list, block);
 	inode = inode_get(&sbd, metabh);
 	height = inode->i_di.di_height;
 	/* If this is a user inode, we don't follow to the file height.
@@ -640,7 +587,7 @@ void save_inode_data(int out_fd)
 		struct gfs2_meta_header mh;
 		int e;
 
-		metabh = bread(&sbd, inode->i_di.di_eattr);
+		metabh = bread(&sbd.buf_list, inode->i_di.di_eattr);
 		save_block(sbd.device_fd, out_fd, inode->i_di.di_eattr);
 		gfs2_meta_header_in(&mh, metabh->b_data);
 		if (mh.mh_magic == GFS2_MAGIC) {
@@ -714,7 +661,7 @@ void get_journal_inode_blocks(void)
 			if (journal > indirect->ii[0].dirents - 3)
 				break;
 			jblock = indirect->ii[0].dirent[journal + 2].block;
-			bh = bread(&sbd, jblock);
+			bh = bread(&sbd.buf_list, jblock);
 			j_inode = inode_get(&sbd, bh);
 			gfs2_dinode_in(&jdi, bh->b_data);
 			inode_put(j_inode, not_updated);
@@ -750,15 +697,13 @@ void savemeta(const char *out_fn, int slow)
 	blks_saved = total_out = last_reported_block = 0;
 	bufsize = BUFSIZE;
 	if (!slow) {
-		int i;
-
 		device_geometry(&sbd);
 		fix_device_geometry(&sbd);
 		osi_list_init(&sbd.rglist);
-		osi_list_init(&sbd.buf_list);
-		for(i = 0; i < BUF_HASH_SIZE; i++)
-			osi_list_init(&sbd.buf_hash[i]);
-		sbd.sd_sb.sb_bsize = GFS2_DEFAULT_BSIZE;
+		init_buf_list(&sbd, &sbd.buf_list, 128 << 20);
+		init_buf_list(&sbd, &sbd.nvbuf_list, 0xffffffff);
+		if (!gfs1)
+			sbd.sd_sb.sb_bsize = GFS2_DEFAULT_BSIZE;
 		compute_constants(&sbd);
 		if(read_sb(&sbd) < 0)
 			slow = TRUE;
@@ -783,7 +728,7 @@ void savemeta(const char *out_fn, int slow)
 					    &sbd.md.riinode);
 			jindex_block = masterblock("jindex");
 		}
-		bh = bread(&sbd, jindex_block);
+		bh = bread(&sbd.buf_list, jindex_block);
 		gfs2_dinode_in(&di, bh->b_data);
 		if (!gfs1)
 			do_dinode_extended(&di, bh->b_data);
diff --git a/gfs2/fsck/fs_recovery.c b/gfs2/fsck/fs_recovery.c
index 3b25dc6..bab427d 100644
--- a/gfs2/fsck/fs_recovery.c
+++ b/gfs2/fsck/fs_recovery.c
@@ -133,8 +133,8 @@ static int buf_lo_scan_elements(struct gfs2_inode *ip, unsigned int start,
 		if (error)
 			return error;
 
-		bh_ip = bget(sdp, blkno);
-		memcpy(bh_ip->b_data, bh_log->b_data, bh_log->b_size);
+		bh_ip = bget(&sdp->buf_list, blkno);
+		memcpy(bh_ip->b_data, bh_log->b_data, sdp->bsize);
 
 		check_magic = ((struct gfs2_meta_header *)
 			       (bh_ip->b_data))->mh_magic;
@@ -230,8 +230,8 @@ static int databuf_lo_scan_elements(struct gfs2_inode *ip, unsigned int start,
 		if (error)
 			return error;
 
-		bh_ip = bget(sdp, blkno);
-		memcpy(bh_ip->b_data, bh_log->b_data, bh_log->b_size);
+		bh_ip = bget(&sdp->buf_list, blkno);
+		memcpy(bh_ip->b_data, bh_log->b_data, sdp->bsize);
 
 		/* Unescape */
 		if (esc) {
@@ -434,6 +434,7 @@ int replay_journals(struct gfs2_sbd *sdp){
 	inode_put(sdp->master_dir, not_updated);
 	inode_put(sdp->md.jiinode, not_updated);
 	/* Sync the buffers to disk so we get a fresh start. */
-	bsync(sdp);
+	bsync(&sdp->buf_list);
+	bsync(&sdp->nvbuf_list);
 	return 0;
 }
diff --git a/gfs2/fsck/initialize.c b/gfs2/fsck/initialize.c
index d759b09..c14a835 100644
--- a/gfs2/fsck/initialize.c
+++ b/gfs2/fsck/initialize.c
@@ -308,11 +308,11 @@ static int fill_super_block(struct gfs2_sbd *sdp)
 	 ********************************************************************/
 	log_info("Initializing lists...\n");
 	osi_list_init(&sdp->rglist);
-	osi_list_init(&sdp->buf_list);
+	init_buf_list(sdp, &sdp->buf_list, 128 << 20);
+	init_buf_list(sdp, &sdp->nvbuf_list, 0xffffffff);
 	for(i = 0; i < BUF_HASH_SIZE; i++) {
 		osi_list_init(&dir_hash[i]);
 		osi_list_init(&inode_hash[i]);
-		osi_list_init(&sdp->buf_hash[i]);
 	}
 
 	/********************************************************************
@@ -385,8 +385,8 @@ static void destroy_sbp(struct gfs2_sbd *sbp)
 {
 	if(!opts.no) {
 		if(block_mounters(sbp, 0)) {
-			log_warn("Unable to unblock other mounters - manual intevention required\n");
-			log_warn("Use 'gfs_tool sb <device> proto' to fix\n");
+			log_warn("Unable to unblock other mounters - manual intervention required\n");
+			log_warn("Use 'gfs2_tool sb <device> proto' to fix\n");
 		}
 		log_info("Syncing the device.\n");
 		fsync(sbp->device_fd);
diff --git a/gfs2/fsck/main.c b/gfs2/fsck/main.c
index 8ee1de3..b20ab11 100644
--- a/gfs2/fsck/main.c
+++ b/gfs2/fsck/main.c
@@ -149,7 +149,7 @@ void interrupt(int sig)
 			PRIu64 "\n", last_reported_block, last_fs_block);
 	
 	response = generic_interrupt("gfs2_fsck", pass, progress,
-				     "Do you want to abort gfs_fsck, skip " \
+				     "Do you want to abort gfs2_fsck, skip " \
 				     "the rest of this pass or continue " \
 				     "(a/s/c)?", "asc");
 	if(tolower(response) == 's') {
@@ -406,7 +406,8 @@ int main(int argc, char **argv)
 
 	if (!opts.no)
 		log_notice("Writing changes to disk\n");
-	bsync(sbp);
+	bsync(&sbp->buf_list);
+	bsync(&sbp->nvbuf_list);
 	destroy(sbp);
 	log_notice("gfs2_fsck complete    \n");
 
diff --git a/gfs2/fsck/metawalk.c b/gfs2/fsck/metawalk.c
index ac7e8f0..7eb1619 100644
--- a/gfs2/fsck/metawalk.c
+++ b/gfs2/fsck/metawalk.c
@@ -113,13 +113,14 @@ int dirent_repair(struct gfs2_inode *ip, struct gfs2_buffer_head *bh,
 	/* If this is a sentinel, just fix the length and move on */
 	if (first && !de->de_inum.no_formal_ino) { /* Is it a sentinel? */
 		if (type == DIR_LINEAR)
-			de->de_rec_len = bh->b_size -
+			de->de_rec_len = ip->i_sbd->bsize -
 				sizeof(struct gfs2_dinode);
 		else
-			de->de_rec_len = bh->b_size - sizeof(struct gfs2_leaf);
+			de->de_rec_len = ip->i_sbd->bsize -
+				sizeof(struct gfs2_leaf);
 	}
 	else {
-		bh_end = bh->b_data + bh->b_size;
+		bh_end = bh->b_data + ip->i_sbd->bsize;
 		/* first, figure out a probable name length */
 		p = (char *)dent + sizeof(struct gfs2_dirent);
 		while (*p &&         /* while there's a non-zero char and */
@@ -153,7 +154,7 @@ int check_entries(struct gfs2_inode *ip, struct gfs2_buffer_head *bh,
 	char *filename;
 	int first = 1;
 
-	bh_end = bh->b_data + bh->b_size;
+	bh_end = bh->b_data + ip->i_sbd->bsize;
 
 	if(type == DIR_LINEAR) {
 		dent = (struct gfs2_dirent *)(bh->b_data + sizeof(struct gfs2_dinode));
@@ -320,7 +321,7 @@ int check_leaf(struct gfs2_inode *ip, int *update, struct metawalk_fxns *pass)
 				{
 					int factor = 0, divisor = ref_count;
 
-					lbh = bread(sbp, old_leaf);
+					lbh = bread(&sbp->buf_list, old_leaf);
 					while (divisor > 1) {
 						factor++;
 						divisor /= 2;
@@ -355,7 +356,7 @@ int check_leaf(struct gfs2_inode *ip, int *update, struct metawalk_fxns *pass)
 
 			*update = not_updated;
 			/* Try to read in the leaf block. */
-			lbh = bread(sbp, leaf_no);
+			lbh = bread(&sbp->buf_list, leaf_no);
 			/* Make sure it's really a valid leaf block. */
 			if (gfs2_check_meta(lbh, GFS2_METATYPE_LF)) {
 				warn_and_patch(ip, &leaf_no, &bad_leaf,
@@ -421,7 +422,7 @@ int check_leaf(struct gfs2_inode *ip, int *update, struct metawalk_fxns *pass)
 				if(update && (count != leaf.lf_entries)) {
 					enum update_flags f = not_updated;
 
-					lbh = bread(sbp, leaf_no);
+					lbh = bread(&sbp->buf_list, leaf_no);
 					gfs2_leaf_in(&leaf, lbh->b_data);
 
 					log_err("Leaf %"PRIu64" (0x%" PRIx64
@@ -636,7 +637,7 @@ static int build_and_check_metalist(struct gfs2_inode *ip,
 	uint64_t *ptr, block;
 	int err;
 
-	metabh = bread(ip->i_sbd, ip->i_di.di_num.no_addr);
+	metabh = bread(&ip->i_sbd->buf_list, ip->i_di.di_num.no_addr);
 
 	osi_list_add(&metabh->b_altlist, &mlp[0]);
 
@@ -656,7 +657,7 @@ static int build_and_check_metalist(struct gfs2_inode *ip,
 				     sizeof(struct gfs2_dinode));
 
 			for (ptr = (uint64_t *)(bh->b_data + head_size);
-			     (char *)ptr < (bh->b_data + bh->b_size);
+			     (char *)ptr < (bh->b_data + ip->i_sbd->bsize);
 			     ptr++) {
 				nbh = NULL;
 		
@@ -679,7 +680,8 @@ static int build_and_check_metalist(struct gfs2_inode *ip,
 					continue;
 				}
 				if(!nbh)
-					nbh = bread(ip->i_sbd, block);
+					nbh = bread(&ip->i_sbd->buf_list,
+						    block);
 
 				osi_list_add(&nbh->b_altlist, cur_list);
 			} /* for all data on the indirect block */
@@ -746,7 +748,7 @@ int check_metatree(struct gfs2_inode *ip, struct metawalk_fxns *pass)
 			     sizeof(struct gfs2_dinode));
 		ptr = (uint64_t *)(bh->b_data + head_size);
 
-		for ( ; (char *)ptr < (bh->b_data + bh->b_size); ptr++)	{
+		for ( ; (char *)ptr < (bh->b_data + ip->i_sbd->bsize); ptr++) {
 			if (!*ptr)
 				continue;
 
@@ -812,7 +814,7 @@ int check_dir(struct gfs2_sbd *sbp, uint64_t block, struct metawalk_fxns *pass)
 	int update = 0;
 	int error = 0;
 
-	bh = bread(sbp, block);
+	bh = bread(&sbp->buf_list, block);
 	ip = fsck_inode_get(sbp, bh);
 
 	if(ip->i_di.di_flags & GFS2_DIF_EXHASH) {
diff --git a/gfs2/fsck/pass1.c b/gfs2/fsck/pass1.c
index b00331d..47dd8f2 100644
--- a/gfs2/fsck/pass1.c
+++ b/gfs2/fsck/pass1.c
@@ -81,7 +81,7 @@ static int check_metalist(struct gfs2_inode *ip, uint64_t block,
 		gfs2_block_mark(bl, block, gfs2_dup_block);
 		found_dup = 1;
 	}
-	nbh = bread(ip->i_sbd, block);
+	nbh = bread(&ip->i_sbd->buf_list, block);
 
 	if (gfs2_check_meta(nbh, GFS2_METATYPE_IN)){
 		log_debug("Bad indirect block pointer "
@@ -170,7 +170,7 @@ static int check_eattr_indir(struct gfs2_inode *ip, uint64_t indirect,
 		   so pass1c can check it. */
 		gfs2_block_mark(bl, ip->i_di.di_num.no_addr, gfs2_eattr_block);
 
-		*bh = bread(sdp, indirect);
+		*bh = bread(&sdp->buf_list, indirect);
 		if(gfs2_check_meta(*bh, GFS2_METATYPE_IN)) {
 			log_warn("EA indirect block %" PRIu64 " (0x%" PRIx64
 				 ") has incorrect type.\n",
@@ -230,7 +230,7 @@ static int check_extended_leaf_eattr(struct gfs2_inode *ip, uint64_t *data_ptr,
 		return 1;
 	}
 
-	el_buf = bread(sdp, el_blk);
+	el_buf = bread(&sdp->buf_list, el_blk);
 
 	if(gfs2_check_meta(el_buf, GFS2_METATYPE_ED)) {
 		log_err("EA extended leaf block %" PRIu64 " (0x%" PRIx64
@@ -285,7 +285,11 @@ static int check_eattr_leaf(struct gfs2_inode *ip, uint64_t block,
 		ret = 1;
 	}
 	else {
-		leaf_bh = bread(sdp, block);
+		/* Special duplicate processing:  If we have an EA block,
+		   check if it really is an EA.  If it is, let duplicate
+		   handling sort it out.  If it isn't, clear it but don't
+		   count it as a duplicate. */
+		leaf_bh = bread(&sdp->buf_list, block);
 		if(gfs2_check_meta(leaf_bh, GFS2_METATYPE_EA)) {
 			log_warn("EA leaf block %"PRIu64" (0x%" 
 				 PRIx64") for inode %"PRIu64" (0x%"
@@ -776,7 +780,7 @@ int pass1(struct gfs2_sbd *sbp)
 				skip_this_pass = FALSE;
 				fflush(stdout);
 			}
-			bh = bread(sbp, block);
+			bh = bread(&sbp->buf_list, block);
 
 			if (scan_meta(sbp, bh, block)) {
 				stack;
diff --git a/gfs2/fsck/pass1b.c b/gfs2/fsck/pass1b.c
index 2b0ab50..ec9413e 100644
--- a/gfs2/fsck/pass1b.c
+++ b/gfs2/fsck/pass1b.c
@@ -83,7 +83,7 @@ static int check_eattr_indir(struct gfs2_inode *ip, uint64_t block,
 	struct gfs2_buffer_head *indir_bh = NULL;
 
 	inc_if_found(block, 0, private);
-	indir_bh = bread(sbp, block);
+	indir_bh = bread(&sbp->buf_list, block);
 	*bh = indir_bh;
 
 	return 0;
@@ -96,7 +96,7 @@ static int check_eattr_leaf(struct gfs2_inode *ip, uint64_t block,
 	struct gfs2_buffer_head *leaf_bh = NULL;
 
 	inc_if_found(block, 0, private);
-	leaf_bh = bread(sbp, block);
+	leaf_bh = bread(&sbp->buf_list, block);
 
 	*bh = leaf_bh;
 	return 0;
diff --git a/gfs2/fsck/pass1c.c b/gfs2/fsck/pass1c.c
index 12a0cc8..b6d085f 100644
--- a/gfs2/fsck/pass1c.c
+++ b/gfs2/fsck/pass1c.c
@@ -66,7 +66,7 @@ int check_eattr_indir(struct gfs2_inode *ip, uint64_t block,
 		return 1;
 	}
 	else
-		indir_bh = bread(sbp, block);
+		indir_bh = bread(&sbp->buf_list, block);
 
 	*bh = indir_bh;
 	return 0;
@@ -95,7 +95,7 @@ int check_eattr_leaf(struct gfs2_inode *ip, uint64_t block,
 		return 1;
 	}
 	else 
-		*bh = bread(sbp, block);
+		*bh = bread(&sbp->buf_list, block);
 
 	return 0;
 }
@@ -235,7 +235,7 @@ int pass1c(struct gfs2_sbd *sbp)
 
 		if (skip_this_pass || fsck_abort) /* if asked to skip the rest */
 			return 0;
-		bh = bread(sbp, block_no);
+		bh = bread(&sbp->buf_list, block_no);
 		if (gfs2_check_meta(bh, GFS2_METATYPE_IN)) { /* if a dinode */
 			log_info("EA in inode %"PRIu64" (0x%" PRIx64 ")\n",
 				 block_no, block_no);
diff --git a/gfs2/fsck/pass2.c b/gfs2/fsck/pass2.c
index c15b0a8..03c80d1 100644
--- a/gfs2/fsck/pass2.c
+++ b/gfs2/fsck/pass2.c
@@ -89,13 +89,13 @@ int set_dotdot_dir(struct gfs2_sbd *sbp, uint64_t childblock,
 static int check_eattr_indir(struct gfs2_inode *ip, uint64_t block,
 			    uint64_t parent, struct gfs2_buffer_head **bh, void *private)
 {
-	*bh = bread(ip->i_sbd, block);
+	*bh = bread(&ip->i_sbd->buf_list, block);
 	return 0;
 }
 static int check_eattr_leaf(struct gfs2_inode *ip, uint64_t block,
 			    uint64_t parent, struct gfs2_buffer_head **bh, void *private)
 {
-	*bh = bread(ip->i_sbd, block);
+	*bh = bread(&ip->i_sbd->buf_list, block);
 	return 0;
 }
 
@@ -729,7 +729,7 @@ int pass2(struct gfs2_sbd *sbp)
 			}
 			gfs2_block_set(bl, i, gfs2_meta_inval);
 		}
-		bh = bread(sbp, i);
+		bh = bread(&sbp->buf_list, i);
 		ip = fsck_inode_get(sbp, bh);
 		if(!ds.dotdir) {
 			log_err("No '.' entry found\n");
@@ -755,7 +755,7 @@ int pass2(struct gfs2_sbd *sbp)
 		}
 		fsck_inode_put(ip, not_updated); /* does a brelse */
 
-		bh = bread(sbp, i);
+		bh = bread(&sbp->buf_list, i);
 		ip = fsck_inode_get(sbp, bh);
 		if(ip->i_di.di_entries != ds.entry_count) {
 			log_err("Entries is %d - should be %d for inode block %" PRIu64
diff --git a/gfs2/fsck/rgrepair.c b/gfs2/fsck/rgrepair.c
index 8ba16bf..0b6e499 100644
--- a/gfs2/fsck/rgrepair.c
+++ b/gfs2/fsck/rgrepair.c
@@ -89,9 +89,9 @@ int gfs2_rindex_rebuild(struct gfs2_sbd *sdp, osi_list_t *ret_list,
 	for (blk = sdp->sb_addr + 1;
 	     blk < sdp->device.length && number_of_rgs < 6;
 	     blk++) {
-		bh = bread(sdp, blk);
-		if ((blk == sdp->sb_addr + 1) ||
-		    (!gfs2_check_meta(bh, GFS2_METATYPE_RG))) {
+		bh = bread(&sdp->nvbuf_list, blk);
+		if (((blk == sdp->sb_addr + 1) ||
+		    (!gfs2_check_meta(bh, GFS2_METATYPE_RG)))) {
 			log_debug("RG found at block 0x%" PRIx64 "\n", blk);
 			if (blk > sdp->sb_addr + 1) {
 				uint64_t rgdist;
@@ -163,7 +163,7 @@ int gfs2_rindex_rebuild(struct gfs2_sbd *sdp, osi_list_t *ret_list,
 	for (blk = sdp->sb_addr + 1; blk <= sdp->device.length;
 	     blk += block_bump) {
 		log_debug("Block 0x%" PRIx64 "\n", blk);
-		bh = bread(sdp, blk);
+		bh = bread(&sdp->nvbuf_list, blk);
 		rg_was_fnd = (!gfs2_check_meta(bh, GFS2_METATYPE_RG));
 		brelse(bh, not_updated);
 		/* Allocate a new RG and index. */
@@ -197,7 +197,7 @@ int gfs2_rindex_rebuild(struct gfs2_sbd *sdp, osi_list_t *ret_list,
 		for (fwd_block = blk + 1;
 		     fwd_block < sdp->device.length; 
 		     fwd_block++) {
-			bh = bread(sdp, fwd_block);
+			bh = bread(&sdp->nvbuf_list, fwd_block);
 			bitmap_was_fnd =
 				(!gfs2_check_meta(bh, GFS2_METATYPE_RB));
 			brelse(bh, not_updated);
@@ -334,7 +334,7 @@ int rewrite_rg_block(struct gfs2_sbd *sdp, struct rgrp_list *rg,
 	if (query(&opts, "Fix the RG? (y/n)")) {
 
 		log_err("Attempting to repair the RG.\n");
-		rg->bh[x] = bread(sdp, rg->ri.ri_addr + x);
+		rg->bh[x] = bread(&sdp->nvbuf_list, rg->ri.ri_addr + x);
 		if (x) {
 			struct gfs2_meta_header mh;
 
diff --git a/gfs2/libgfs2/buf.c b/gfs2/libgfs2/buf.c
index 562e965..4680daa 100644
--- a/gfs2/libgfs2/buf.c
+++ b/gfs2/libgfs2/buf.c
@@ -26,17 +26,19 @@
 #include "libgfs2.h"
 
 static __inline__ osi_list_t *
-blkno2head(struct gfs2_sbd *sdp, uint64_t blkno)
+blkno2head(struct buf_list *bl, uint64_t blkno)
 {
-	return sdp->buf_hash +
+	return bl->buf_hash +
 		(gfs2_disk_hash((char *)&blkno, sizeof(uint64_t)) & BUF_HASH_MASK);
 }
 
-void write_buffer(struct gfs2_sbd *sdp, struct gfs2_buffer_head *bh)
+static void write_buffer(struct buf_list *bl, struct gfs2_buffer_head *bh)
 {
+	struct gfs2_sbd *sdp = bl->sbp;
+
 	osi_list_del(&bh->b_list);
 	osi_list_del(&bh->b_hash);
-	sdp->num_bufs--;
+	bl->num_bufs--;
 	if (bh->b_changed) {
 		do_lseek(sdp->device_fd, bh->b_blocknr * sdp->bsize);
 		do_write(sdp->device_fd, bh->b_data, sdp->bsize);
@@ -45,31 +47,50 @@ void write_buffer(struct gfs2_sbd *sdp, struct gfs2_buffer_head *bh)
 	free(bh);
 }
 
+void init_buf_list(struct gfs2_sbd *sdp, struct buf_list *bl, uint32_t limit)
+{
+	int i;
+
+	bl->num_bufs = 0;
+	bl->spills = 0;
+	bl->limit = limit;
+	bl->sbp = sdp;
+	osi_list_init(&bl->list);
+	for(i = 0; i < BUF_HASH_SIZE; i++)
+		osi_list_init(&bl->buf_hash[i]);
+}
+
 static void
-add_buffer(struct gfs2_sbd *sdp, struct gfs2_buffer_head *bh)
+add_buffer(struct buf_list *bl, struct gfs2_buffer_head *bh)
 {
-	osi_list_t *head = blkno2head(sdp, bh->b_blocknr);
+	osi_list_t *head = blkno2head(bl, bh->b_blocknr);
 
-	osi_list_add(&bh->b_list, &sdp->buf_list);
+	osi_list_add(&bh->b_list, &bl->list);
 	osi_list_add(&bh->b_hash, head);
-	sdp->num_bufs++;
-
-	while (sdp->num_bufs * sdp->bsize > 128 << 20) {
-		bh = osi_list_entry(sdp->buf_list.prev, struct gfs2_buffer_head,
-							b_list);
-		if (bh->b_count) {
-			osi_list_del(&bh->b_list);
-			osi_list_add(&bh->b_list, &sdp->buf_list);
-			continue;
+	bl->num_bufs++;
+
+	if (bl->num_bufs * bl->sbp->bsize > bl->limit) {
+		int found = 0;
+		osi_list_t *tmp, *x;
+
+		for (tmp = bl->list.prev, x = tmp->prev; tmp != &bl->list;
+		     tmp = x, x = x->prev) {
+			bh = osi_list_entry(tmp, struct gfs2_buffer_head,
+					    b_list);
+			if (!bh->b_count) {
+				write_buffer(bl, bh);
+				found++;
+				if (found >= 10)
+					break;
+			}
 		}
-		write_buffer(sdp, bh);
-		sdp->spills++;
-	} 
+		bl->spills++;
+	}
 }
 
-struct gfs2_buffer_head *bfind(struct gfs2_sbd *sdp, uint64_t num)
+struct gfs2_buffer_head *bfind(struct buf_list *bl, uint64_t num)
 {
-	osi_list_t *head = blkno2head(sdp, num);
+	osi_list_t *head = blkno2head(bl, num);
 	osi_list_t *tmp;
 	struct gfs2_buffer_head *bh;
 
@@ -77,7 +98,7 @@ struct gfs2_buffer_head *bfind(struct gfs2_sbd *sdp, uint64_t num)
 		bh = osi_list_entry(tmp, struct gfs2_buffer_head, b_hash);
 		if (bh->b_blocknr == num) {
 			osi_list_del(&bh->b_list);
-			osi_list_add(&bh->b_list, &sdp->buf_list);
+			osi_list_add(&bh->b_list, &bl->list);
 			osi_list_del(&bh->b_hash);
 			osi_list_add(&bh->b_hash, head);
 			bh->b_count++;
@@ -88,13 +109,14 @@ struct gfs2_buffer_head *bfind(struct gfs2_sbd *sdp, uint64_t num)
 	return NULL;
 }
 
-struct gfs2_buffer_head *bget_generic(struct gfs2_sbd *sdp, uint64_t num,
-									  int find_existing, int read_disk)
+struct gfs2_buffer_head *bget_generic(struct buf_list *bl, uint64_t num,
+				      int find_existing, int read_disk)
 {
 	struct gfs2_buffer_head *bh;
+	struct gfs2_sbd *sdp = bl->sbp;
 
 	if (find_existing) {
-		bh = bfind(sdp, num);
+		bh = bfind(bl, num);
 		if (bh)
 			return bh;
 	}
@@ -108,32 +130,34 @@ struct gfs2_buffer_head *bget_generic(struct gfs2_sbd *sdp, uint64_t num,
 		do_lseek(sdp->device_fd, num * sdp->bsize);
 		do_read(sdp->device_fd, bh->b_data, sdp->bsize);
 	}
-	add_buffer(sdp, bh);
+	add_buffer(bl, bh);
 	bh->b_changed = FALSE;
 
 	return bh;
 }
 
-struct gfs2_buffer_head *bget(struct gfs2_sbd *sdp, uint64_t num)
+struct gfs2_buffer_head *bget(struct buf_list *bl, uint64_t num)
 {
-	return bget_generic(sdp, num, TRUE, FALSE);
+	return bget_generic(bl, num, TRUE, FALSE);
 }
 
-struct gfs2_buffer_head *bread(struct gfs2_sbd *sdp, uint64_t num)
+struct gfs2_buffer_head *bread(struct buf_list *bl, uint64_t num)
 {
-	return bget_generic(sdp, num, TRUE, TRUE);
+	return bget_generic(bl, num, TRUE, TRUE);
 }
 
-struct gfs2_buffer_head *bget_zero(struct gfs2_sbd *sdp, uint64_t num)
+struct gfs2_buffer_head *bget_zero(struct buf_list *bl, uint64_t num)
 {
-	return bget_generic(sdp, num, FALSE, FALSE);
+	return bget_generic(bl, num, FALSE, FALSE);
 }
 
 struct gfs2_buffer_head *bhold(struct gfs2_buffer_head *bh)
 {
-	if (!bh->b_count)
+	if (!bh->b_count) {
+		stack;
 		die("buffer hold error for block %" PRIu64 " (0x%" PRIx64")\n",
 			bh->b_blocknr, bh->b_blocknr);
+	}
 	bh->b_count++;
 	return bh;
 }
@@ -144,51 +168,55 @@ void brelse(struct gfs2_buffer_head *bh, enum update_flags updated)
 	/* set it FALSE if it's TRUE until we write the changed data to disk. */
 	if (updated)
 		bh->b_changed = TRUE;
-	if (!bh->b_count)
+	if (!bh->b_count) {
+		stack;
 		die("buffer count underflow for block %" PRIu64 " (0x%" PRIx64")\n",
 			bh->b_blocknr, bh->b_blocknr);
+	}
 	bh->b_count--;
 }
 
-void bsync(struct gfs2_sbd *sdp)
+void bsync(struct buf_list *bl)
 {
 	struct gfs2_buffer_head *bh;
 
-	while (!osi_list_empty(&sdp->buf_list)) {
-		bh = osi_list_entry(sdp->buf_list.prev, struct gfs2_buffer_head,
+	while (!osi_list_empty(&bl->list)) {
+		bh = osi_list_entry(bl->list.prev, struct gfs2_buffer_head,
 							b_list);
 		if (bh->b_count)
 			die("buffer still held for block: %" PRIu64 " (0x%" PRIx64")\n",
 				bh->b_blocknr, bh->b_blocknr);
-		write_buffer(sdp, bh);
+		write_buffer(bl, bh);
 	}
 }
 
 /* commit buffers to disk but do not discard */
-void bcommit(struct gfs2_sbd *sdp)
+void bcommit(struct buf_list *bl)
 {
 	osi_list_t *tmp, *x;
 	struct gfs2_buffer_head *bh;
+	struct gfs2_sbd *sdp = bl->sbp;
 
-	osi_list_foreach_safe(tmp, &sdp->buf_list, x) {
+	osi_list_foreach_safe(tmp, &bl->list, x) {
 		bh = osi_list_entry(tmp, struct gfs2_buffer_head, b_list);
 		if (!bh->b_count)             /* if not reserved for later */
-			write_buffer(sdp, bh);    /* write the data, free the memory */
+			write_buffer(bl, bh);/* write the data & free memory */
 		else if (bh->b_changed) {     /* if buffer has changed */
 			do_lseek(sdp->device_fd, bh->b_blocknr * sdp->bsize);
-			do_write(sdp->device_fd, bh->b_data, sdp->bsize); /* write it out */
+			do_write(sdp->device_fd, bh->b_data, sdp->bsize);
 			bh->b_changed = FALSE;    /* no longer changed */
 		}
 	}
+	fsync(sdp->device_fd);
 }
 
 /* Check for unreleased buffers */
-void bcheck(struct gfs2_sbd *sdp)
+void bcheck(struct buf_list *bl)
 {
 	osi_list_t *tmp;
 	struct gfs2_buffer_head *bh;
 
-	osi_list_foreach(tmp, &sdp->buf_list) {
+	osi_list_foreach(tmp, &bl->list) {
 		bh = osi_list_entry(tmp, struct gfs2_buffer_head, b_list);
 		if (bh->b_count)
 			die("buffer still held: %"PRIu64"\n", bh->b_blocknr);
diff --git a/gfs2/libgfs2/fs_geometry.c b/gfs2/libgfs2/fs_geometry.c
index c6d275c..4501a84 100644
--- a/gfs2/libgfs2/fs_geometry.c
+++ b/gfs2/libgfs2/fs_geometry.c
@@ -226,7 +226,7 @@ void build_rgrps(struct gfs2_sbd *sdp, int write)
 
 		if (write) {
 			for (x = 0; x < bitblocks; x++) {
-				bh = bget(sdp, rl->start + x);
+				bh = bget(&sdp->nvbuf_list, rl->start + x);
 				if (x)
 					gfs2_meta_header_out(&mh, bh->b_data);
 				else
diff --git a/gfs2/libgfs2/fs_ops.c b/gfs2/libgfs2/fs_ops.c
index a48d2ff..343c162 100644
--- a/gfs2/libgfs2/fs_ops.c
+++ b/gfs2/libgfs2/fs_ops.c
@@ -75,7 +75,7 @@ uint64_t blk_alloc_i(struct gfs2_sbd *sdp, unsigned int type)
 	rg = &rl->rg;
 
 	for (block = 0; block < ri->ri_length; block++) {
-		bh = bread(sdp, ri->ri_addr + block);
+		bh = bread(&sdp->nvbuf_list, ri->ri_addr + block);
 		x = (block) ? sizeof(struct gfs2_meta_header) : sizeof(struct gfs2_rgrp);
 
 		for (; x < sdp->bsize; x++)
@@ -117,7 +117,7 @@ uint64_t blk_alloc_i(struct gfs2_sbd *sdp, unsigned int type)
 
 	brelse(bh, updated);
 
-	bh = bread(sdp, ri->ri_addr);
+	bh = bread(&sdp->nvbuf_list, ri->ri_addr);
 	gfs2_rgrp_out(rg, bh->b_data);
 	brelse(bh, updated);
 
@@ -148,24 +148,24 @@ uint64_t dinode_alloc(struct gfs2_sbd *sdp)
 	return blk_alloc_i(sdp, DINODE);
 }
 
-static __inline__ void buffer_clear_tail(struct gfs2_buffer_head *bh, int head)
+static __inline__ void buffer_clear_tail(struct gfs2_sbd *sdp,
+					 struct gfs2_buffer_head *bh, int head)
 {
-	memset(bh->b_data + head, 0, bh->b_size - head);
+	memset(bh->b_data + head, 0, sdp->bsize - head);
 }
 
 static __inline__ void
-buffer_copy_tail(struct gfs2_buffer_head *to_bh, int to_head,
-				 struct gfs2_buffer_head *from_bh, int from_head)
+buffer_copy_tail(struct gfs2_sbd *sdp,
+		 struct gfs2_buffer_head *to_bh, int to_head,
+		 struct gfs2_buffer_head *from_bh, int from_head)
 {
-	memcpy(to_bh->b_data + to_head,
-	       from_bh->b_data + from_head,
-	       from_bh->b_size - from_head);
-	memset(to_bh->b_data + to_bh->b_size + to_head - from_head,
-	       0,
+	memcpy(to_bh->b_data + to_head, from_bh->b_data + from_head,
+	       sdp->bsize - from_head);
+	memset(to_bh->b_data + sdp->bsize + to_head - from_head, 0,
 	       from_head - to_head);
 }
 
-static void unstuff_dinode(struct gfs2_inode *ip)
+void unstuff_dinode(struct gfs2_inode *ip)
 {
 	struct gfs2_sbd *sdp = ip->i_sbd;
 	struct gfs2_buffer_head *bh;
@@ -175,7 +175,7 @@ static void unstuff_dinode(struct gfs2_inode *ip)
 	if (ip->i_di.di_size) {
 		if (isdir) {
 			block = meta_alloc(ip);
-			bh = bget(sdp, block);
+			bh = bget(&sdp->buf_list, block);
 			{
 				struct gfs2_meta_header mh;
 				mh.mh_magic = GFS2_MAGIC;
@@ -184,22 +184,23 @@ static void unstuff_dinode(struct gfs2_inode *ip)
 				gfs2_meta_header_out(&mh, bh->b_data);
 			}
 
-			buffer_copy_tail(bh, sizeof(struct gfs2_meta_header),
+			buffer_copy_tail(sdp, bh,
+					 sizeof(struct gfs2_meta_header),
 					 ip->i_bh, sizeof(struct gfs2_dinode));
 
 			brelse(bh, updated);
 		} else {
 			block = data_alloc(ip);
-			bh = bget(sdp, block);
+			bh = bget(&sdp->buf_list, block);
 
-			buffer_copy_tail(bh, 0,
+			buffer_copy_tail(sdp, bh, 0,
 					 ip->i_bh, sizeof(struct gfs2_dinode));
 
 			brelse(bh, updated);
 		}
 	}
 
-	buffer_clear_tail(ip->i_bh, sizeof(struct gfs2_dinode));
+	buffer_clear_tail(sdp, ip->i_bh, sizeof(struct gfs2_dinode));
 
 	if (ip->i_di.di_size) {
 		*(uint64_t *)(ip->i_bh->b_data + sizeof(struct gfs2_dinode)) = cpu_to_be64(block);
@@ -252,7 +253,7 @@ void build_height(struct gfs2_inode *ip, int height)
 
 		if (new_block) {
 			block = meta_alloc(ip);
-			bh = bget(sdp, block);
+			bh = bget(&sdp->buf_list, block);
 			{
 				struct gfs2_meta_header mh;
 				mh.mh_magic = GFS2_MAGIC;
@@ -260,13 +261,14 @@ void build_height(struct gfs2_inode *ip, int height)
 				mh.mh_format = GFS2_FORMAT_IN;
 				gfs2_meta_header_out(&mh, bh->b_data);
 			}
-			buffer_copy_tail(bh, sizeof(struct gfs2_meta_header),
+			buffer_copy_tail(sdp, bh,
+					 sizeof(struct gfs2_meta_header),
 					 ip->i_bh, sizeof(struct gfs2_dinode));
 
 			brelse(bh, updated);
 		}
 
-		buffer_clear_tail(ip->i_bh, sizeof(struct gfs2_dinode));
+		buffer_clear_tail(sdp, ip->i_bh, sizeof(struct gfs2_dinode));
 
 		if (new_block) {
 			*(uint64_t *)(ip->i_bh->b_data + sizeof(struct gfs2_dinode)) = cpu_to_be64(block);
@@ -277,11 +279,7 @@ void build_height(struct gfs2_inode *ip, int height)
 	}
 }
 
-struct metapath {
-	unsigned int mp_list[GFS2_MAX_META_HEIGHT];
-};
-
-static struct metapath *find_metapath(struct gfs2_inode *ip, uint64_t block)
+struct metapath *find_metapath(struct gfs2_inode *ip, uint64_t block)
 {
 	struct gfs2_sbd *sdp = ip->i_sbd;
 	struct metapath *mp;
@@ -296,19 +294,9 @@ static struct metapath *find_metapath(struct gfs2_inode *ip, uint64_t block)
 	return mp;
 }
 
-static __inline__ uint64_t *
-metapointer(struct gfs2_buffer_head *bh, unsigned int height,
-			struct metapath *mp)
-{
-	unsigned int head_size = (height > 0) ?
-		sizeof(struct gfs2_meta_header) : sizeof(struct gfs2_dinode);
-
-	return ((uint64_t *)(bh->b_data + head_size)) + mp->mp_list[height];
-}
-
-static void lookup_block(struct gfs2_inode *ip,
-	     struct gfs2_buffer_head *bh, unsigned int height, struct metapath *mp,
-	     int create, int *new, uint64_t *block)
+void lookup_block(struct gfs2_inode *ip, struct gfs2_buffer_head *bh,
+		  unsigned int height, struct metapath *mp,
+		  int create, int *new, uint64_t *block)
 {
 	uint64_t *ptr = metapointer(bh, height, mp);
 
@@ -384,13 +372,13 @@ void block_map(struct gfs2_inode *ip, uint64_t lblock, int *new,
 
 		if (*new) {
 			struct gfs2_meta_header mh;
-			bh = bget(sdp, *dblock);
+			bh = bget(&sdp->buf_list, *dblock);
 			mh.mh_magic = GFS2_MAGIC;
 			mh.mh_type = GFS2_METATYPE_IN;
 			mh.mh_format = GFS2_FORMAT_IN;
 			gfs2_meta_header_out(&mh, bh->b_data);
 		} else
-			bh = bread(sdp, *dblock);
+			bh = bread(&sdp->buf_list, *dblock);
 	}
 
 	if (!prealloc)
@@ -483,7 +471,7 @@ int gfs2_readi(struct gfs2_inode *ip, void *buf,
 				  FALSE, not_updated);
 
 		if (dblock) {
-			bh = bread(sdp, dblock);
+			bh = bread(&sdp->buf_list, dblock);
 			dblock++;
 			extlen--;
 		} else
@@ -521,7 +509,7 @@ int gfs2_writei(struct gfs2_inode *ip, void *buf,
 	uint32_t extlen = 0;
 	unsigned int amount;
 	int new;
-	int isdir = !!(S_ISDIR(ip->i_di.di_flags));
+	int isdir = !!(S_ISDIR(ip->i_di.di_mode));
 	const uint64_t start = offset;
 	int copied = 0;
 
@@ -557,7 +545,7 @@ int gfs2_writei(struct gfs2_inode *ip, void *buf,
 		}
 
 		if (new) {
-			bh = bget(sdp, dblock);
+			bh = bget(&sdp->buf_list, dblock);
 			if (isdir) {
 				struct gfs2_meta_header mh;
 				mh.mh_magic = GFS2_MAGIC;
@@ -566,7 +554,7 @@ int gfs2_writei(struct gfs2_inode *ip, void *buf,
 				gfs2_meta_header_out(&mh, bh->b_data);
 			}
 		} else
-			bh = bread(sdp, dblock);
+			bh = bread(&sdp->buf_list, dblock);
 		copy_from_mem(bh, &buf, o, amount);
 		brelse(bh, updated);
 
@@ -603,9 +591,9 @@ struct gfs2_buffer_head *get_file_buf(struct gfs2_inode *ip, uint64_t lbn,
 		ip->i_di.di_size = (lbn + 1) << sdp->sd_sb.sb_bsize_shift;
 
 	if (new)
-		return bget(sdp, dbn);
+		return bget(&sdp->buf_list, dbn);
 	else
-		return bread(sdp, dbn);
+		return bread(&sdp->buf_list, dbn);
 }
 
 int gfs2_dirent_first(struct gfs2_inode *dip, struct gfs2_buffer_head *bh,
@@ -628,7 +616,7 @@ int gfs2_dirent_next(struct gfs2_inode *dip, struct gfs2_buffer_head *bh,
 	char *bh_end;
 	uint16_t cur_rec_len;
 
-	bh_end = bh->b_data + bh->b_size;
+	bh_end = bh->b_data + dip->i_sbd->bsize;
 	cur_rec_len = be16_to_cpu((*dent)->de_rec_len);
 
 	if ((char *)(*dent) + cur_rec_len >= bh_end)
@@ -661,7 +649,7 @@ dirent_alloc(struct gfs2_inode *dip, struct gfs2_buffer_head *bh, int name_len,
 	}
 
 	if (!entries) {
-		dent->de_rec_len = cpu_to_be16(bh->b_size - offset);
+		dent->de_rec_len = cpu_to_be16(dip->i_sbd->bsize - offset);
 		dent->de_name_len = cpu_to_be16(name_len);
 
 		*dent_out = dent;
@@ -759,7 +747,7 @@ dir_split_leaf(struct gfs2_inode *dip, uint32_t index, uint64_t leaf_no)
 	int count;
 
 	bn = meta_alloc(dip);
-	nbh = bget(dip->i_sbd, bn);
+	nbh = bget(&dip->i_sbd->buf_list, bn);
 	{
 		struct gfs2_meta_header mh;
 		mh.mh_magic = GFS2_MAGIC;
@@ -771,7 +759,7 @@ dir_split_leaf(struct gfs2_inode *dip, uint32_t index, uint64_t leaf_no)
 	nleaf = (struct gfs2_leaf *)nbh->b_data;
 	nleaf->lf_dirent_format = cpu_to_be32(GFS2_FORMAT_DE);
 
-	obh = bread(dip->i_sbd, leaf_no);
+	obh = bread(&dip->i_sbd->buf_list, leaf_no);
 	oleaf = (struct gfs2_leaf *)obh->b_data;
 
 	len = 1 << (dip->i_di.di_depth - be16_to_cpu(oleaf->lf_depth));
@@ -902,7 +890,7 @@ int gfs2_get_leaf(struct gfs2_inode *dip, uint64_t leaf_no,
 {
 	int error = 0;
 
-	*bhp = bread(dip->i_sbd, leaf_no);
+	*bhp = bread(&dip->i_sbd->buf_list, leaf_no);
 	if (error)
 		return error;
 	error = gfs2_check_meta(*bhp, GFS2_METATYPE_LF);
@@ -926,7 +914,7 @@ static int get_first_leaf(struct gfs2_inode *dip, uint32_t index,
 	uint64_t leaf_no;
 
 	gfs2_get_leaf_nr(dip, index, &leaf_no);
-	*bh_out = bget(dip->i_sbd, leaf_no);
+	*bh_out = bread(&dip->i_sbd->buf_list, leaf_no);
 	return 0;
 }
 
@@ -948,7 +936,7 @@ static int get_next_leaf(struct gfs2_inode *dip,struct gfs2_buffer_head *bh_in,
 
 	if (!leaf->lf_next)
 		return -1;
-	*bh_out = bget(dip->i_sbd, be64_to_cpu(leaf->lf_next));
+	*bh_out = bread(&dip->i_sbd->buf_list, be64_to_cpu(leaf->lf_next));
 	return 0;
 }
 
@@ -974,7 +962,7 @@ dir_e_add(struct gfs2_inode *dip, char *filename, int len,
 	gfs2_get_leaf_nr(dip, index, &leaf_no);
 
 	for (;;) {
-		bh = bread(dip->i_sbd, leaf_no);
+		bh = bread(&dip->i_sbd->buf_list, leaf_no);
 		leaf = (struct gfs2_leaf *)bh->b_data;
 
 		if (dirent_alloc(dip, bh, len, &dent)) {
@@ -996,7 +984,7 @@ dir_e_add(struct gfs2_inode *dip, char *filename, int len,
 
 			} else {
 				bn = meta_alloc(dip);
-				nbh = bget(dip->i_sbd, bn);
+				nbh = bget(&dip->i_sbd->buf_list, bn);
 				{
 					struct gfs2_meta_header mh;
 					mh.mh_magic = GFS2_MAGIC;
@@ -1047,7 +1035,7 @@ dir_make_exhash(struct gfs2_inode *dip)
 	uint64_t *lp, bn;
 
 	bn = meta_alloc(dip);
-	bh = bget(sdp, bn);
+	bh = bget(&sdp->buf_list, bn);
 	{
 		struct gfs2_meta_header mh;
 		mh.mh_magic = GFS2_MAGIC;
@@ -1060,7 +1048,7 @@ dir_make_exhash(struct gfs2_inode *dip)
 	leaf->lf_dirent_format = cpu_to_be32(GFS2_FORMAT_DE);
 	leaf->lf_entries = cpu_to_be16(dip->i_di.di_entries);
 
-	buffer_copy_tail(bh, sizeof(struct gfs2_leaf),
+	buffer_copy_tail(sdp, bh, sizeof(struct gfs2_leaf),
 			 dip->i_bh, sizeof(struct gfs2_dinode));
 
 	x = 0;
@@ -1078,7 +1066,7 @@ dir_make_exhash(struct gfs2_inode *dip)
 
 	brelse(bh, updated);
 
-	buffer_clear_tail(dip->i_bh, sizeof(struct gfs2_dinode));
+	buffer_clear_tail(sdp, dip->i_bh, sizeof(struct gfs2_dinode));
 
 	lp = (uint64_t *)(dip->i_bh->b_data + sizeof(struct gfs2_dinode));
 
@@ -1131,7 +1119,7 @@ init_dinode(struct gfs2_sbd *sdp, struct gfs2_inum *inum,
 	struct gfs2_buffer_head *bh;
 	struct gfs2_dinode di;
 
-	bh = bget(sdp, inum->no_addr);
+	bh = bget(&sdp->buf_list, inum->no_addr);
 
 	memset(&di, 0, sizeof(struct gfs2_dinode));
 	di.di_header.mh_magic = GFS2_MAGIC;
@@ -1399,7 +1387,7 @@ static int dir_l_search(struct gfs2_inode *dip, const char *filename,
 	if(!inode_is_stuffed(dip))
 		return -1;
 
-	dibh = bread(dip->i_sbd, dip->i_di.di_num.no_addr);
+	dibh = bread(&dip->i_sbd->buf_list, dip->i_di.di_num.no_addr);
 	error = leaf_search(dip, dibh, filename, len, &dent, NULL);
 	if (!error) {
 		gfs2_inum_in(inum, (char *)&dent->de_inum);
@@ -1454,7 +1442,7 @@ static int dir_e_del(struct gfs2_inode *dip, const char *filename, int len)
 		gfs2_get_leaf_nr(dip, index, &leaf_no);
 
 		while(leaf_no && !found){
-			bh = bget(dip->i_sbd, leaf_no);
+			bh = bread(&dip->i_sbd->buf_list, leaf_no);
 			error = leaf_search(dip, bh, filename, len, &cur, &prev);
 			if (error) {
 				if(error != -ENOENT){
@@ -1488,7 +1476,7 @@ static int dir_l_del(struct gfs2_inode *dip, struct gfs2_buffer_head *dibh,
 		return -1;
 
 	if(!dibh) {
-		dibh = bread(dip->i_sbd, dip->i_di.di_num.no_addr);
+		dibh = bread(&dip->i_sbd->buf_list, dip->i_di.di_num.no_addr);
 		if (error)
 			return -1;
 		got_buf = 1;
@@ -1572,6 +1560,23 @@ int gfs2_lookupi(struct gfs2_inode *dip, const char *filename, int len,
 }
 
 /**
+ * gfs2_free_block - free up a block given its block number
+ */
+void gfs2_free_block(struct gfs2_sbd *sdp, uint64_t block)
+{
+	struct gfs2_buffer_head *bh;
+	struct rgrp_list *rgd;
+
+	gfs2_set_bitmap(sdp, block, GFS2_BLKST_FREE);
+	/* Adjust the free space count for the freed block */
+	rgd = gfs2_blk2rgrpd(sdp, block); /* find the rg for indir block */
+	bh = bget(&sdp->nvbuf_list, rgd->ri.ri_addr); /* get the rg buffer */
+	rgd->rg.rg_free++; /* adjust the free count */
+	gfs2_rgrp_out(&rgd->rg, bh->b_data); /* back to the buffer */
+	brelse(bh, updated); /* release the buffer */
+}
+
+/**
  * gfs2_freedi - unlink a disk inode by block number.
  * Note: currently only works for regular files.
  */
@@ -1584,7 +1589,7 @@ int gfs2_freedi(struct gfs2_sbd *sdp, uint64_t block)
 	unsigned char *buf;
 	struct rgrp_list *rgd;
 	
-	bh = bread(sdp, block);
+	bh = bread(&sdp->buf_list, block);
 	ip = inode_get(sdp, bh);
 	if (ip->i_di.di_height > 0) {
 		buf = (unsigned char *)bh->b_data;
@@ -1592,16 +1597,8 @@ int gfs2_freedi(struct gfs2_sbd *sdp, uint64_t block)
 		for (x = sizeof(struct gfs2_dinode); x < sdp->bsize;
 			 x += sizeof(uint64_t)) {
 			p = be64_to_cpu(*(uint64_t *)(buf + x));
-			if (p) {
-				gfs2_set_bitmap(sdp, p, GFS2_BLKST_FREE);
-				/* We need to adjust the free space count for the freed */
-                /* indirect block. */
-				rgd = gfs2_blk2rgrpd(sdp, p); /* find the rg for indir block */
-				bh = bget(sdp, rgd->ri.ri_addr); /* get the buffer its rg */
-				rgd->rg.rg_free++; /* adjust the free count */
-				gfs2_rgrp_out(&rgd->rg, bh->b_data); /* back to the buffer */
-				brelse(bh, updated); /* release the buffer */
-			}
+			if (p)
+				gfs2_free_block(sdp, p);
 		}
 	}
 	/* Set the bitmap type for inode to free space: */
@@ -1612,7 +1609,7 @@ int gfs2_freedi(struct gfs2_sbd *sdp, uint64_t block)
 	/* The rg itself is in memory as rgd->rg, but there's most likely a  */
 	/* buffer in memory for the rg on disk because we used it to fix the */
 	/* bitmaps, some of which are on the same block on disk.             */
-	bh = bread(sdp, rgd->ri.ri_addr); /* get the buffer */
+	bh = bread(&sdp->nvbuf_list, rgd->ri.ri_addr); /* get the buffer */
 	rgd->rg.rg_free++;
 	rgd->rg.rg_dinodes--; /* one less inode in use */
 	gfs2_rgrp_out(&rgd->rg, bh->b_data);
diff --git a/gfs2/libgfs2/libgfs2.h b/gfs2/libgfs2/libgfs2.h
index 7f8e393..98b3396 100644
--- a/gfs2/libgfs2/libgfs2.h
+++ b/gfs2/libgfs2/libgfs2.h
@@ -170,6 +170,15 @@ struct master_dir
 	struct per_node *pn;              /* Array of per_node entries */
 };
 
+struct buf_list {
+	unsigned int num_bufs;
+	unsigned int spills;
+	uint32_t limit;
+	osi_list_t list;
+	struct gfs2_sbd *sbp;
+	osi_list_t buf_hash[BUF_HASH_SIZE];
+};
+
 struct gfs2_sbd {
 	struct gfs2_sb sd_sb;    /* a copy of the ondisk structure */
 	char lockproto[GFS2_LOCKNAME_LEN];
@@ -230,20 +239,22 @@ struct gfs2_sbd {
 
 	unsigned int orig_journals;
 
-	unsigned int num_bufs;
-	osi_list_t buf_list;
-	osi_list_t buf_hash[BUF_HASH_SIZE];
+	struct buf_list buf_list;   /* transient buffer list */
+	struct buf_list nvbuf_list; /* non-volatile buffer list */
 
 	struct gfs2_inode *master_dir;
 	struct master_dir md;
 
-	unsigned int spills;
 	unsigned int writes;
 	int metafs_fd;
 	int metafs_mounted; /* If metafs was already mounted */
 	char metafs_path[PATH_MAX]; /* where metafs is mounted */
 };
 
+struct metapath {
+	unsigned int mp_list[GFS2_MAX_META_HEIGHT];
+};
+
 extern char *prog_name;
 
 #define GFS2_DEFAULT_BSIZE          (4096)
@@ -352,17 +363,17 @@ int gfs2_find_next_block_type(struct gfs2_block_list *il,
 			      enum gfs2_mark_block m, uint64_t *b);
 
 /* buf.c */
-struct gfs2_buffer_head *bget_generic(struct gfs2_sbd *sdp, uint64_t num,
+void init_buf_list(struct gfs2_sbd *sdp, struct buf_list *bl, uint32_t limit);
+struct gfs2_buffer_head *bget_generic(struct buf_list *bl, uint64_t num,
 				      int find_existing, int read_disk);
-struct gfs2_buffer_head *bget(struct gfs2_sbd *sdp, uint64_t num);
-struct gfs2_buffer_head *bread(struct gfs2_sbd *sdp, uint64_t num);
-struct gfs2_buffer_head *bget_zero(struct gfs2_sbd *sdp, uint64_t num);
+struct gfs2_buffer_head *bget(struct buf_list *bl, uint64_t num);
+struct gfs2_buffer_head *bread(struct buf_list *bl, uint64_t num);
+struct gfs2_buffer_head *bget_zero(struct buf_list *bl, uint64_t num);
 struct gfs2_buffer_head *bhold(struct gfs2_buffer_head *bh);
 void brelse(struct gfs2_buffer_head *bh, enum update_flags updated);
-void bsync(struct gfs2_sbd *sdp);
-void bcommit(struct gfs2_sbd *sdp);
-void bcheck(struct gfs2_sbd *sdp);
-void write_buffer(struct gfs2_sbd *sdp, struct gfs2_buffer_head *bh);
+void bsync(struct buf_list *bl);
+void bcommit(struct buf_list *bl);
+void bcheck(struct buf_list *bl);
 
 /* device_geometry.c */
 void device_geometry(struct gfs2_sbd *sdp);
@@ -400,6 +411,20 @@ void build_rgrps(struct gfs2_sbd *sdp, int write);
 #define IS_LEAF     (1)
 #define IS_DINODE   (2)
 
+static __inline__ uint64_t *
+metapointer(struct gfs2_buffer_head *bh, unsigned int height,
+	    struct metapath *mp)
+{
+	unsigned int head_size = (height > 0) ?
+		sizeof(struct gfs2_meta_header) : sizeof(struct gfs2_dinode);
+
+	return ((uint64_t *)(bh->b_data + head_size)) + mp->mp_list[height];
+}
+
+struct metapath *find_metapath(struct gfs2_inode *ip, uint64_t block);
+void lookup_block(struct gfs2_inode *ip, struct gfs2_buffer_head *bh,
+		  unsigned int height, struct metapath *mp,
+		  int create, int *new, uint64_t *block);
 struct gfs2_inode *inode_get(struct gfs2_sbd *sdp,
 			     struct gfs2_buffer_head *bh);
 void inode_put(struct gfs2_inode *ip, enum update_flags updated);
@@ -433,7 +458,7 @@ void block_map(struct gfs2_inode *ip, uint64_t lblock, int *new,
 void gfs2_get_leaf_nr(struct gfs2_inode *dip, uint32_t index,
 					  uint64_t *leaf_out);
 void gfs2_put_leaf_nr(struct gfs2_inode *dip, uint32_t inx, uint64_t leaf_out);
-
+void gfs2_free_block(struct gfs2_sbd *sdp, uint64_t block);
 int gfs2_freedi(struct gfs2_sbd *sdp, uint64_t block);
 int gfs2_get_leaf(struct gfs2_inode *dip, uint64_t leaf_no,
 				  struct gfs2_buffer_head **bhp);
@@ -442,6 +467,7 @@ int gfs2_dirent_first(struct gfs2_inode *dip, struct gfs2_buffer_head *bh,
 int gfs2_dirent_next(struct gfs2_inode *dip, struct gfs2_buffer_head *bh,
 					 struct gfs2_dirent **dent);
 void build_height(struct gfs2_inode *ip, int height);
+void unstuff_dinode(struct gfs2_inode *ip);
 unsigned int calc_tree_height(struct gfs2_inode *ip, uint64_t size);
 void write_journal(struct gfs2_sbd *sdp, struct gfs2_inode *ip, unsigned int j,
 				   unsigned int blocks);
@@ -456,6 +482,59 @@ void write_journal(struct gfs2_sbd *sdp, struct gfs2_inode *ip, unsigned int j,
 
 int device_size(int fd, uint64_t *bytes);
 
+/* gfs1.c - GFS1 backward compatibility functions */
+struct gfs_indirect {
+	struct gfs2_meta_header in_header;
+
+	char in_reserved[64];
+};
+
+struct gfs_dinode {
+	struct gfs2_meta_header di_header;
+
+	struct gfs2_inum di_num; /* formal inode # and block address */
+
+	uint32_t di_mode;	/* mode of file */
+	uint32_t di_uid;	/* owner's user id */
+	uint32_t di_gid;	/* owner's group id */
+	uint32_t di_nlink;	/* number (qty) of links to this file */
+	uint64_t di_size;	/* number (qty) of bytes in file */
+	uint64_t di_blocks;	/* number (qty) of blocks in file */
+	int64_t di_atime;	/* time last accessed */
+	int64_t di_mtime;	/* time last modified */
+	int64_t di_ctime;	/* time last changed */
+
+	/*  Non-zero only for character or block device nodes  */
+	uint32_t di_major;	/* device major number */
+	uint32_t di_minor;	/* device minor number */
+
+	/*  Block allocation strategy  */
+	uint64_t di_rgrp;	/* dinode rgrp block number */
+	uint64_t di_goal_rgrp;	/* rgrp to alloc from next */
+	uint32_t di_goal_dblk;	/* data block goal */
+	uint32_t di_goal_mblk;	/* metadata block goal */
+
+	uint32_t di_flags;	/* GFS_DIF_... */
+
+	/*  struct gfs_rindex, struct gfs_jindex, or struct gfs_dirent */
+	uint32_t di_payload_format;  /* GFS_FORMAT_... */
+	uint16_t di_type;	/* GFS_FILE_... type of file */
+	uint16_t di_height;	/* height of metadata (0 == stuffed) */
+	uint32_t di_incarn;	/* incarnation (unused, see gfs_meta_header) */
+	uint16_t di_pad;
+
+	/*  These only apply to directories  */
+	uint16_t di_depth;	/* Number of bits in the table */
+	uint32_t di_entries;	/* The # (qty) of entries in the directory */
+
+	/*  This formed an on-disk chain of unused dinodes  */
+	struct gfs2_inum di_next_unused;  /* used in old versions only */
+
+	uint64_t di_eattr;	/* extended attribute block number */
+
+	char di_reserved[56];
+};
+
 /* locking.c */
 void test_locking(char *lockproto, char *locktable);
 
@@ -530,6 +609,8 @@ int gfs2_query(int *setonabort, struct gfs2_options *opts,
 /* misc.c */
 #define SYS_BASE "/sys/fs/gfs2"
 
+uint32_t compute_heightsize(struct gfs2_sbd *sdp, uint64_t *heightsize,
+			    uint32_t bsize1, int diptrs, int inptrs);
 void compute_constants(struct gfs2_sbd *sdp);
 int find_gfs2_meta(struct gfs2_sbd *sdp);
 int dir_exists(const char *dir);
diff --git a/gfs2/libgfs2/misc.c b/gfs2/libgfs2/misc.c
index b6948dd..cf5794a 100644
--- a/gfs2/libgfs2/misc.c
+++ b/gfs2/libgfs2/misc.c
@@ -34,12 +34,35 @@
 
 static char sysfs_buf[PAGE_SIZE];
 
+uint32_t compute_heightsize(struct gfs2_sbd *sdp, uint64_t *heightsize,
+			    uint32_t bsize1, int diptrs, int inptrs)
+{
+	int x;
+
+	heightsize[0] = sdp->bsize - sizeof(struct gfs2_dinode);
+	heightsize[1] = bsize1 * diptrs;
+	for (x = 2;; x++) {
+		uint64_t space, d;
+		uint32_t m;
+
+		space = heightsize[x - 1] * inptrs;
+		d = space;
+		m = do_div(d, inptrs);
+
+		if (d != heightsize[x - 1] || m)
+			break;
+		heightsize[x] = space;
+	}
+	if (x > GFS2_MAX_META_HEIGHT)
+		die("bad constants (1)\n");
+	return x;
+}
+
 void
 compute_constants(struct gfs2_sbd *sdp)
 {
 	uint32_t hash_blocks, ind_blocks, leaf_blocks;
 	uint32_t tmp_blocks;
-	unsigned int x;
 
 	sdp->md.next_inum = 1;
 
@@ -73,41 +96,13 @@ compute_constants(struct gfs2_sbd *sdp)
 
 	sdp->sd_max_dirres = hash_blocks + ind_blocks + leaf_blocks;
 
-	sdp->sd_heightsize[0] = sdp->bsize - sizeof(struct gfs2_dinode);
-	sdp->sd_heightsize[1] = sdp->bsize * sdp->sd_diptrs;
-	for (x = 2;; x++) {
-		uint64_t space, d;
-		uint32_t m;
-
-		space = sdp->sd_heightsize[x - 1] * sdp->sd_inptrs;
-		d = space;
-		m = do_div(d, sdp->sd_inptrs);
-
-		if (d != sdp->sd_heightsize[x - 1] || m)
-			break;
-		sdp->sd_heightsize[x] = space;
-	}
-	sdp->sd_max_height = x;
-	if (sdp->sd_max_height > GFS2_MAX_META_HEIGHT)
-		die("bad constants (1)\n");
-
-	sdp->sd_jheightsize[0] = sdp->bsize - sizeof(struct gfs2_dinode);
-	sdp->sd_jheightsize[1] = sdp->sd_jbsize * sdp->sd_diptrs;
-	for (x = 2;; x++) {
-		uint64_t space, d;
-		uint32_t m;
-
-		space = sdp->sd_jheightsize[x - 1] * sdp->sd_inptrs;
-		d = space;
-		m = do_div(d, sdp->sd_inptrs);
-
-		if (d != sdp->sd_jheightsize[x - 1] || m)
-			break;
-		sdp->sd_jheightsize[x] = space;
-	}
-	sdp->sd_max_jheight = x;
-	if (sdp->sd_max_jheight > GFS2_MAX_META_HEIGHT)
-		die("bad constants (2)\n");
+	sdp->sd_max_height = compute_heightsize(sdp, sdp->sd_heightsize,
+						sdp->bsize, sdp->sd_diptrs,
+						sdp->sd_inptrs);
+	sdp->sd_max_jheight = compute_heightsize(sdp, sdp->sd_jheightsize,
+						 sdp->sd_jbsize,
+						 sdp->sd_diptrs,
+						 sdp->sd_inptrs);
 }
 
 int 
diff --git a/gfs2/libgfs2/recovery.c b/gfs2/libgfs2/recovery.c
index 387d4fa..0d2ba7d 100644
--- a/gfs2/libgfs2/recovery.c
+++ b/gfs2/libgfs2/recovery.c
@@ -40,7 +40,7 @@ int gfs2_replay_read_block(struct gfs2_inode *ip, unsigned int blk,
 	if (!dblock)
 		return -EIO;
 
-	*bh = bread(ip->i_sbd, dblock);
+	*bh = bread(&ip->i_sbd->buf_list, dblock);
 	return 0;
 }
 
@@ -232,8 +232,8 @@ int clean_journal(struct gfs2_inode *ip, struct gfs2_log_header *head)
 	if (!dblock)
 		return -EIO;
 
-	bh = bread(ip->i_sbd, dblock);
-	memset(bh->b_data, 0, bh->b_size);
+	bh = bread(&ip->i_sbd->buf_list, dblock);
+	memset(bh->b_data, 0, ip->i_sbd->bsize);
 
 	lh = (struct gfs2_log_header *)bh->b_data;
 	memset(lh, 0, sizeof(struct gfs2_log_header));
diff --git a/gfs2/libgfs2/rgrp.c b/gfs2/libgfs2/rgrp.c
index 5728144..800efcb 100644
--- a/gfs2/libgfs2/rgrp.c
+++ b/gfs2/libgfs2/rgrp.c
@@ -31,7 +31,15 @@ int gfs2_compute_bitstructs(struct gfs2_sbd *sdp, struct rgrp_list *rgd)
 	uint32_t bytes_left, bytes;
 	int x;
 
-	if(!(rgd->bits = (struct gfs2_bitmap *)
+	/* Max size of an rg is 2GB.  A 2GB RG with (minimum) 512-byte blocks
+	   has 4194304 blocks.  We can represent 4 blocks in one bitmap byte.
+	   Therefore, all 4194304 blocks can be represented in 1048576 bytes.
+	   Subtract a metadata header for each 512-byte block and we get
+	   488 bytes of bitmap per block.  Divide 1048576 by 488 and we can
+	   be assured we should never have more than 2149 of them. */
+	if (length > 2149 || length == 0)
+		return -1;
+	if(rgd->bits == NULL && !(rgd->bits = (struct gfs2_bitmap *)
 		 malloc(length * sizeof(struct gfs2_bitmap))))
 		return -1;
 	if(!memset(rgd->bits, 0, length * sizeof(struct gfs2_bitmap)))
@@ -77,6 +85,8 @@ int gfs2_compute_bitstructs(struct gfs2_sbd *sdp, struct rgrp_list *rgd)
 	    rgd->bits[length - 1].bi_len) * GFS2_NBBY != rgd->ri.ri_data)
 		return -1;
 
+	if (rgd->bh)      /* If we already have a bh allocated */
+		return 0; /* don't want to allocate another */
 	if(!(rgd->bh = (struct gfs2_buffer_head **)
 		 malloc(length * sizeof(struct gfs2_buffer_head *))))
 		return -1;
@@ -122,7 +132,7 @@ uint64_t gfs2_rgrp_read(struct gfs2_sbd *sdp, struct rgrp_list *rgd)
 	int x, length = rgd->ri.ri_length;
 
 	for (x = 0; x < length; x++){
-		rgd->bh[x] = bread(sdp, rgd->ri.ri_addr + x);
+		rgd->bh[x] = bread(&sdp->nvbuf_list, rgd->ri.ri_addr + x);
 		if(gfs2_check_meta(rgd->bh[x],
 				   (x) ? GFS2_METATYPE_RB : GFS2_METATYPE_RG))
 		{
@@ -158,8 +168,10 @@ void gfs2_rgrp_free(osi_list_t *rglist, enum update_flags updated)
 			gfs2_rgrp_relse(rgd, updated); /* free them all. */
 		if(rgd->bits)
 			free(rgd->bits);
-		if(rgd->bh)
+		if(rgd->bh) {
 			free(rgd->bh);
+			rgd->bh = NULL;
+		}
 		osi_list_del(&rgd->list);
 		free(rgd);
 	}
diff --git a/gfs2/libgfs2/structures.c b/gfs2/libgfs2/structures.c
index c733b39..09c4a70 100644
--- a/gfs2/libgfs2/structures.c
+++ b/gfs2/libgfs2/structures.c
@@ -54,7 +54,7 @@ build_sb(struct gfs2_sbd *sdp)
 
 	/* Zero out the beginning of the device up to the superblock */
 	for (x = 0; x < sdp->sb_addr; x++) {
-		bh = bget(sdp, x);
+		bh = bget(&sdp->buf_list, x);
 		memset(bh->b_data, 0, sdp->bsize);
 		brelse(bh, updated);
 	}
@@ -72,7 +72,7 @@ build_sb(struct gfs2_sbd *sdp)
 	strcpy(sb.sb_lockproto, sdp->lockproto);
 	strcpy(sb.sb_locktable, sdp->locktable);
 
-	bh = bget(sdp, sdp->sb_addr);
+	bh = bget(&sdp->buf_list, sdp->sb_addr);
 	gfs2_sb_out(&sb, bh->b_data);
 	brelse(bh, updated);
 
@@ -421,7 +421,7 @@ struct gfs2_inode *gfs2_load_inode(struct gfs2_sbd *sbp, uint64_t block)
 	struct gfs2_buffer_head *bh;
 	struct gfs2_inode *ip;
 
-	bh = bread(sbp, block);
+	bh = bread(&sbp->buf_list, block);
 	ip = inode_get(sbp, bh);
 	return ip;
 }
@@ -592,7 +592,7 @@ int gfs2_next_rg_metatype(struct gfs2_sbd *sdp, struct rgrp_list *rgd,
 			brelse(bh, not_updated);
 		if (gfs2_next_rg_meta(rgd, block, first))
 			return -1;
-		bh = bread(sdp, *block);
+		bh = bread(&sdp->buf_list, *block);
 		first = 0;
 	} while(gfs2_check_meta(bh, type));
 	brelse(bh, not_updated);
diff --git a/gfs2/libgfs2/super.c b/gfs2/libgfs2/super.c
index 1af87c1..b32c5b4 100644
--- a/gfs2/libgfs2/super.c
+++ b/gfs2/libgfs2/super.c
@@ -71,7 +71,7 @@ int read_sb(struct gfs2_sbd *sdp)
 	unsigned int x;
 	int error;
 
-	bh = bread(sdp, GFS2_SB_ADDR >> sdp->sd_fsb2bb_shift);
+	bh = bread(&sdp->buf_list, GFS2_SB_ADDR >> sdp->sd_fsb2bb_shift);
 	gfs2_sb_in(&sdp->sd_sb, bh->b_data);
 	brelse(bh, not_updated);
 
@@ -197,6 +197,10 @@ int rindex_read(struct gfs2_sbd *sdp, int fd, int *count1)
 			return -1;
 
 		rgd = (struct rgrp_list *)malloc(sizeof(struct rgrp_list));
+		if (!rgd) {
+			log_crit("Cannot allocate memory for rindex.\n");
+			exit(-1);
+		}
 		memset(rgd, 0, sizeof(struct rgrp_list));
 		osi_list_add_prev(&rgd->list, &sdp->rglist);
 
@@ -267,10 +271,11 @@ int write_sb(struct gfs2_sbd *sbp)
 {
 	struct gfs2_buffer_head *bh;
 
-	bh = bread(sbp, GFS2_SB_ADDR >> sbp->sd_fsb2bb_shift);
+	bh = bread(&sbp->buf_list, GFS2_SB_ADDR >> sbp->sd_fsb2bb_shift);
 	gfs2_sb_out(&sbp->sd_sb, bh->b_data);
 	brelse(bh, updated);
-	bcommit(sbp); /* make sure the change gets to disk ASAP */
+	bcommit(&sbp->buf_list); /* make sure the change gets to disk ASAP */
+	bcommit(&sbp->nvbuf_list); /* make sure the change gets to disk ASAP */
 	return 0;
 }
 
diff --git a/gfs2/mkfs/main_grow.c b/gfs2/mkfs/main_grow.c
index 6b2603b..2ac6e78 100644
--- a/gfs2/mkfs/main_grow.c
+++ b/gfs2/mkfs/main_grow.c
@@ -184,7 +184,8 @@ void initialize_new_portion(struct gfs2_sbd *sdp, int *old_rg_count)
 	inode_put(sdp->master_dir, not_updated);
 
 	/* We're done with the libgfs portion, so commit it to disk.      */
-	bsync(sdp);
+	bsync(&sdp->buf_list);
+	bsync(&sdp->nvbuf_list);
 }
 
 /**
@@ -255,7 +256,7 @@ void
 main_grow(int argc, char *argv[])
 {
 	struct gfs2_sbd sbd, *sdp = &sbd;
-	int rgcount, i, rindex_fd;
+	int rgcount, rindex_fd;
 	char rindex_name[PATH_MAX];
 	osi_list_t *head = &sdp->rglist;
 
@@ -284,9 +285,8 @@ main_grow(int argc, char *argv[])
 		fix_device_geometry(sdp);
 		log_info("Initializing lists...\n");
 		osi_list_init(&sdp->rglist);
-		osi_list_init(&sdp->buf_list);
-		for(i = 0; i < BUF_HASH_SIZE; i++)
-			osi_list_init(&sdp->buf_hash[i]);
+		init_buf_list(sdp, &sdp->buf_list, 128 << 20);
+		init_buf_list(sdp, &sdp->nvbuf_list, 0xffffffff);
 
 		sdp->sd_sb.sb_bsize = GFS2_DEFAULT_BSIZE;
 		sdp->bsize = sdp->sd_sb.sb_bsize;
diff --git a/gfs2/mkfs/main_mkfs.c b/gfs2/mkfs/main_mkfs.c
index f66a990..944ef63 100644
--- a/gfs2/mkfs/main_mkfs.c
+++ b/gfs2/mkfs/main_mkfs.c
@@ -345,7 +345,8 @@ print_results(struct gfs2_sbd *sdp)
 
 	if (sdp->debug) {
 		printf("\n");
-		printf("Spills:                    %u\n", sdp->spills);
+		printf("Spills:                    %u\n",
+		       sdp->buf_list.spills);
 		printf("Writes:                    %u\n", sdp->writes);
 	}
 
@@ -363,7 +364,6 @@ void
 main_mkfs(int argc, char *argv[])
 {
 	struct gfs2_sbd sbd, *sdp = &sbd;
-	unsigned int x;
 	int error;
 	int rgsize_specified = 0;
 
@@ -376,9 +376,8 @@ main_mkfs(int argc, char *argv[])
 	strcpy(sdp->lockproto, GFS2_DEFAULT_LOCKPROTO);
 	sdp->time = time(NULL);
 	osi_list_init(&sdp->rglist);
-	osi_list_init(&sdp->buf_list);
-	for (x = 0; x < BUF_HASH_SIZE; x++)
-		osi_list_init(&sdp->buf_hash[x]);
+	init_buf_list(sdp, &sdp->buf_list, 128 << 20);
+	init_buf_list(sdp, &sdp->nvbuf_list, 0xffffffff);
 
 	decode_arguments(argc, argv, sdp);
 	if (sdp->rgsize == -1)                 /* if rg size not specified */
@@ -430,7 +429,8 @@ main_mkfs(int argc, char *argv[])
 	inode_put(sdp->master_dir, updated);
 	inode_put(sdp->md.inum, updated);
 	inode_put(sdp->md.statfs, updated);
-	bsync(sdp);
+	bsync(&sdp->buf_list);
+	bsync(&sdp->nvbuf_list);
 
 	error = fsync(sdp->device_fd);
 	if (error)
diff --git a/gfs2/tool/df.c b/gfs2/tool/df.c
index f6b203e..ec39313 100644
--- a/gfs2/tool/df.c
+++ b/gfs2/tool/df.c
@@ -45,7 +45,7 @@ do_df_one(char *path)
 	uint64_t rgrps;
 	unsigned int flags;
 	char *value, *fs;
- 	int x, statfs_fd;
+	int statfs_fd;
 	struct gfs2_sbd sbd;
 	char buf[GFS2_DEFAULT_BSIZE], statfs_fn[PATH_MAX];
 	struct gfs2_statfs_change sc;
@@ -65,9 +65,9 @@ do_df_one(char *path)
 	sbd.utsize = GFS2_DEFAULT_UTSIZE;
 	sbd.qcsize = GFS2_DEFAULT_QCSIZE;
 	osi_list_init(&sbd.rglist);
-	osi_list_init(&sbd.buf_list);
-	for (x = 0; x < BUF_HASH_SIZE; x++)
-		osi_list_init(&sbd.buf_hash[x]);
+	init_buf_list(&sbd, &sbd.buf_list, 128 << 20);
+	init_buf_list(&sbd, &sbd.nvbuf_list, 0xffffffff);
+
 	do_lseek(sbd.device_fd, 0x10 * sbd.bsize);
 	do_read(sbd.device_fd, buf, sbd.bsize); /* read in the superblock */
 


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2009-03-25 22:03 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-03-25 22:03 cluster: RHEL52 - gfs2_convert results in GFS2 File System Corruption Bob Peterson

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