cluster: RHEL511 - fsck: rebase from RHEL6

Bob Peterson rpeterso at fedoraproject.org
Tue Dec 3 13:18:55 UTC 2013


Gitweb:        http://git.fedorahosted.org/git/?p=cluster.git;a=commitdiff;h=40fb2e03b1df0bb5754c1c8e8d8c4bd6a411e88d
Commit:        40fb2e03b1df0bb5754c1c8e8d8c4bd6a411e88d
Parent:        be05457a5d3ff4a6190b30055c6c64fde8108998
Author:        Bob Peterson <rpeterso at redhat.com>
AuthorDate:    Wed Aug 21 08:28:44 2013 -0500
Committer:     Bob Peterson <rpeterso at redhat.com>
CommitterDate: Tue Dec 3 07:18:14 2013 -0600

fsck: rebase from RHEL6

This patch rebases the fsck.gfs2 tool from the latest RHEL6 version.
To keep the patch from affecting the other utils, many of the source
files from libgfs2 were copied to the fsck directory, and the Makefile
was adjusted accordingly.

rhbz#902918
---
 gfs2/fsck/Makefile          |   17 +-
 gfs2/fsck/block_list.c      |   65 ++
 gfs2/fsck/buf.c             |   89 +++
 gfs2/fsck/device_geometry.c |  126 +++
 gfs2/fsck/eattr.c           |   12 -
 gfs2/fsck/eattr.h           |   12 -
 gfs2/fsck/fs_bits.c         |  259 ++++++
 gfs2/fsck/fs_bits.h         |   14 -
 gfs2/fsck/fs_geometry.c     |  259 ++++++
 gfs2/fsck/fs_ops.c          | 1841 +++++++++++++++++++++++++++++++++++++++++++
 gfs2/fsck/fs_recovery.c     |  152 +++-
 gfs2/fsck/fs_recovery.h     |   13 -
 gfs2/fsck/fsck.h            |   23 +-
 gfs2/fsck/gfs1.c            |  429 ++++++++++
 gfs2/fsck/gfs2_disk_hash.c  |   72 ++
 gfs2/fsck/gfs2_log.c        |  192 +++++
 gfs2/fsck/hash.c            |   13 -
 gfs2/fsck/hash.h            |   13 -
 gfs2/fsck/initialize.c      |  456 +++++++----
 gfs2/fsck/inode_hash.c      |   16 +-
 gfs2/fsck/inode_hash.h      |   13 -
 gfs2/fsck/libgfs2.h         |  822 +++++++++++++++++++
 gfs2/fsck/link.c            |   13 -
 gfs2/fsck/link.h            |   14 -
 gfs2/fsck/lost_n_found.c    |  233 +++---
 gfs2/fsck/lost_n_found.h    |   14 +-
 gfs2/fsck/main.c            |   36 +-
 gfs2/fsck/metawalk.c        | 1095 ++++++++++++++++++--------
 gfs2/fsck/metawalk.h        |   96 ++-
 gfs2/fsck/misc.c            |  607 ++++++++++++++
 gfs2/fsck/ondisk.c          |  638 +++++++++++++++
 gfs2/fsck/pass1.c           |  881 +++++++++++----------
 gfs2/fsck/pass1b.c          |  825 ++++++++------------
 gfs2/fsck/pass1c.c          |   30 +-
 gfs2/fsck/pass2.c           | 1277 ++++++++++++++++++++++++++-----
 gfs2/fsck/pass3.c           |   57 +-
 gfs2/fsck/pass4.c           |   20 +-
 gfs2/fsck/pass5.c           |  115 +++-
 gfs2/fsck/recovery.c        |  244 ++++++
 gfs2/fsck/rgrepair.c        |  551 ++++++++++---
 gfs2/fsck/rgrp.c            |  228 ++++++
 gfs2/fsck/size.c            |   67 ++
 gfs2/fsck/structures.c      |  530 +++++++++++++
 gfs2/fsck/super.c           |  327 ++++++++
 gfs2/fsck/util.c            |  266 +++++--
 gfs2/fsck/util.h            |  134 +++-
 46 files changed, 10966 insertions(+), 2240 deletions(-)

diff --git a/gfs2/fsck/Makefile b/gfs2/fsck/Makefile
index 7fd06ca..2d201f0 100644
--- a/gfs2/fsck/Makefile
+++ b/gfs2/fsck/Makefile
@@ -17,21 +17,23 @@ include ${top_srcdir}/make/defines.mk
 TARGET1= fsck.gfs2
 TARGET2= gfs2_fsck
 
-INCLUDES= -I${top_srcdir}/include -I${top_srcdir}/config -I${top_srcdir}/libgfs2 -I${KERNEL_SRC}/fs/gfs2/ -I${KERNEL_SRC}/include/
-LIBS=${top_srcdir}/libgfs2
+INCLUDES= -I${top_srcdir}/include -I${top_srcdir}/config -I${KERNEL_SRC}/fs/gfs2/ -I${KERNEL_SRC}/include/
 
 .PHONY: all clean
 
 sources = main.c initialize.c pass1.c pass1b.c pass1c.c pass2.c pass3.c \
 	pass4.c pass5.c util.c fs_recovery.c rgrepair.c\
-	lost_n_found.c link.c eattr.c hash.c inode_hash.c metawalk.c
+	lost_n_found.c link.c eattr.c hash.c inode_hash.c metawalk.c \
+	fs_ops.c gfs1.c gfs2_log.c buf.c structures.c ondisk.c fs_bits.c \
+	rgrp.c gfs2_disk_hash.c block_list.c super.c misc.c recovery.c \
+	device_geometry.c fs_geometry.c size.c
 
-CFLAGS+=-D_FILE_OFFSET_BITS=64 -DHELPER_PROGRAM -DGFS_RELEASE_NAME=\"${RELEASE}\" -Wall -O2
+CFLAGS+=-D_FILE_OFFSET_BITS=64 -DHELPER_PROGRAM -D_GNU_SOURCE -DGFS_RELEASE_NAME=\"${RELEASE}\" -Wall -O2
 
-all: depends ${TARGET1} ${TARGET2}
+all: ${TARGET1} ${TARGET2}
 
 ${TARGET1}: $(sources:.c=.o)
-	$(CC) $(CFLAGS) -L$(LIBS) $^ -o $@ -lgfs2
+	$(CC) $(CFLAGS) $^ -o $@
 
 ${TARGET2}: ${TARGET1}
 	ln -f ${TARGET1} ${TARGET2}	
@@ -39,9 +41,6 @@ ${TARGET2}: ${TARGET1}
 %.o: %.c
 	$(CC) -MMD -c $(INCLUDES) $(CFLAGS) $< -o $@
 
-depends:
-	$(MAKE) -C ../libgfs2 all
-
 test_block_list: log.o test_block_list.o
 	$(CC) $(CFLAGS) $^ -o $@
 
diff --git a/gfs2/fsck/block_list.c b/gfs2/fsck/block_list.c
new file mode 100644
index 0000000..554a1ef
--- /dev/null
+++ b/gfs2/fsck/block_list.c
@@ -0,0 +1,65 @@
+#include <inttypes.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "libgfs2.h"
+
+void gfs2_special_free(struct special_blocks *blist)
+{
+	struct special_blocks *f;
+
+	while(!osi_list_empty(&blist->list)) {
+		f = osi_list_entry(blist->list.next, struct special_blocks,
+				   list);
+		osi_list_del(&f->list);
+		free(f);
+	}
+}
+
+struct special_blocks *blockfind(struct special_blocks *blist, uint64_t num)
+{
+	osi_list_t *head = &blist->list;
+	osi_list_t *tmp;
+	struct special_blocks *b;
+
+	for (tmp = head->next; tmp != head; tmp = tmp->next) {
+		b = osi_list_entry(tmp, struct special_blocks, list);
+		if (b->block == num)
+			return b;
+	}
+	return NULL;
+}
+
+void gfs2_special_add(struct special_blocks *blocklist, uint64_t block)
+{
+	struct special_blocks *b;
+
+	b = malloc(sizeof(struct special_blocks));
+	if (b) {
+		memset(b, 0, sizeof(*b));
+		b->block = block;
+		osi_list_add_prev(&b->list, &blocklist->list);
+	}
+}
+
+void gfs2_special_set(struct special_blocks *blocklist, uint64_t block)
+{
+	if (blockfind(blocklist, block))
+		return;
+	gfs2_special_add(blocklist, block);
+}
+
+void gfs2_special_clear(struct special_blocks *blocklist, uint64_t block)
+{
+	struct special_blocks *b;
+
+	b = blockfind(blocklist, block);
+	if (b) {
+		osi_list_del(&b->list);
+		free(b);
+	}
+}
diff --git a/gfs2/fsck/buf.c b/gfs2/fsck/buf.c
new file mode 100644
index 0000000..f5d013d
--- /dev/null
+++ b/gfs2/fsck/buf.c
@@ -0,0 +1,89 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <linux/types.h>
+
+#include "libgfs2.h"
+
+struct gfs2_buffer_head *__bget_generic(struct gfs2_sbd *sdp, uint64_t num,
+					int read_disk,
+					int line, const char *caller)
+{
+	struct gfs2_buffer_head *bh;
+
+	bh = calloc(1, sizeof(struct gfs2_buffer_head) + sdp->bsize);
+	if (bh == NULL)
+		return NULL;
+
+	bh->b_blocknr = num;
+	bh->sdp = sdp;
+	bh->b_data = (char *)bh + sizeof(struct gfs2_buffer_head);
+	if (read_disk) {
+		if (lseek(sdp->device_fd, num * sdp->bsize, SEEK_SET) !=
+		    num * sdp->bsize) {
+			fprintf(stderr, "bad seek: %s from %s:%d: block "
+				"%llu (0x%llx)\n", strerror(errno),
+				caller, line, (unsigned long long)num,
+				(unsigned long long)num);
+			exit(-1);
+		}
+		if (read(sdp->device_fd, bh->b_data, sdp->bsize) < 0) {
+			fprintf(stderr, "bad read: %s from %s:%d: block "
+				"%llu (0x%llx)\n", strerror(errno),
+				caller, line, (unsigned long long)num,
+				(unsigned long long)num);
+			exit(-1);
+		}
+	}
+	return bh;
+}
+
+struct gfs2_buffer_head *__bget(struct gfs2_sbd *sdp, uint64_t num, int line,
+				const char *caller)
+{
+	return __bget_generic(sdp, num, FALSE, line, caller);
+}
+
+struct gfs2_buffer_head *__bread(struct gfs2_sbd *sdp, uint64_t num, int line,
+				 const char *caller)
+{
+	return __bget_generic(sdp, num, TRUE, line, caller);
+}
+
+int bwrite(struct gfs2_buffer_head *bh)
+{
+	struct gfs2_sbd *sdp = bh->sdp;
+
+	if (lseek(sdp->device_fd, bh->b_blocknr * sdp->bsize, SEEK_SET) !=
+	    bh->b_blocknr * sdp->bsize) {
+		return -1;
+	}
+	if (write(sdp->device_fd, bh->b_data, sdp->bsize) != sdp->bsize)
+		return -1;
+	sdp->writes++;
+	bh->b_modified = 0;
+	return 0;
+}
+
+int brelse(struct gfs2_buffer_head *bh)
+{
+	int error = 0;
+
+	if (bh->b_blocknr == -1)
+		printf("Double free!\n");
+	if (bh->b_modified)
+		error = bwrite(bh);
+	bh->b_blocknr = -1;
+	if (bh->b_altlist.next && !osi_list_empty(&bh->b_altlist))
+		osi_list_del(&bh->b_altlist);
+	free(bh);
+	return error;
+}
diff --git a/gfs2/fsck/device_geometry.c b/gfs2/fsck/device_geometry.c
new file mode 100644
index 0000000..a05ae94
--- /dev/null
+++ b/gfs2/fsck/device_geometry.c
@@ -0,0 +1,126 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <linux/types.h>
+
+#include "libgfs2.h"
+
+#define BLKSSZGET _IO(0x12,104)   /* logical_block_size */
+#define BLKIOMIN _IO(0x12,120)    /* minimum_io_size */
+#define BLKIOOPT _IO(0x12,121)    /* optimal_io_size */
+#define BLKALIGNOFF _IO(0x12,122) /* alignment_offset */
+#define BLKPBSZGET _IO(0x12,123)  /* physical_block_size */
+
+/**
+ * device_topology - Get the device topology
+ * Values not fetched are returned as zero.
+ */
+int device_topology(struct gfs2_sbd *sdp)
+{
+	if (ioctl(sdp->device_fd, BLKSSZGET, &sdp->logical_block_size) < 0)
+		sdp->logical_block_size = 0;
+	if (ioctl(sdp->device_fd, BLKIOMIN, &sdp->minimum_io_size) < 0)
+		sdp->minimum_io_size = 0;
+	if (ioctl(sdp->device_fd, BLKALIGNOFF, &sdp->optimal_io_size) < 0)
+		sdp->optimal_io_size = 0;
+	if (ioctl(sdp->device_fd, BLKIOOPT, &sdp->alignment_offset) < 0)
+		sdp->alignment_offset = 0;
+	if (ioctl(sdp->device_fd, BLKPBSZGET, &sdp->physical_block_size) < 0)
+		sdp->physical_block_size = 0;
+	if (!sdp->debug)
+		return 0;
+
+	printf("\nDevice Topology:\n");
+	printf("  Logical block size: %u\n", sdp->logical_block_size);
+	printf("  Physical block size: %u\n", sdp->physical_block_size);
+	printf("  Minimum I/O size: %u\n", sdp->minimum_io_size);
+	printf("  Optimal I/O size: %u (0 means unknown)\n",
+	       sdp->optimal_io_size);
+	printf("  Alignment offset: %u\n", sdp->alignment_offset);
+	return 0;
+}
+
+/**
+ * device_geometry - Get the size of a device
+ * @w: the command line
+ *
+ */
+
+int device_geometry(struct gfs2_sbd *sdp)
+{
+	struct device *device = &sdp->device;
+	uint64_t bytes;
+	int error;
+
+	error = device_size(sdp->device_fd, &bytes);
+	if (error)
+		return error;
+
+	if (sdp->debug)
+		printf("\nPartition size = %"PRIu64"\n",
+		       bytes >> GFS2_BASIC_BLOCK_SHIFT);
+
+	device->start = 0;
+	device->length = bytes >> GFS2_BASIC_BLOCK_SHIFT;
+	return 0;
+}
+
+/**
+ * fix_device_geometry - round off address and lengths and convert to FS blocks
+ * @w: the command line
+ *
+ */
+
+int fix_device_geometry(struct gfs2_sbd *sdp)
+{
+	struct device *device = &sdp->device;
+	unsigned int bbsize = sdp->bsize >> GFS2_BASIC_BLOCK_SHIFT;
+	uint64_t start, length;
+	unsigned int remainder;
+
+	if (sdp->debug) {
+		printf("\nDevice Geometry:  (in basic blocks)\n");
+		printf("  start = %"PRIu64", length = %"PRIu64", rgf_flags = 0x%.8X\n",
+		       device->start,
+		       device->length,
+		       device->rgf_flags);
+	}
+
+	start = device->start;
+	length = device->length;
+
+	if (length < 1 << (20 - GFS2_BASIC_BLOCK_SHIFT)) {
+		errno = ENOSPC;
+		return -1;
+	}
+
+	remainder = start % bbsize;
+	if (remainder) {
+		length -= bbsize - remainder;
+		start += bbsize - remainder;
+	}
+
+	start /= bbsize;
+	length /= bbsize;
+
+	device->start = start;
+	device->length = length;
+	sdp->device_size = start + length;
+
+	if (sdp->debug) {
+		printf("\nDevice Geometry:  (in FS blocks)\n");
+		printf("  start = %"PRIu64", length = %"
+		       PRIu64", rgf_flags = 0x%.8X\n",
+		       device->start, device->length, device->rgf_flags);
+		printf("\nDevice Size: %"PRIu64"\n", sdp->device_size);
+	}
+	return 0;
+}
diff --git a/gfs2/fsck/eattr.c b/gfs2/fsck/eattr.c
index 1023f09..377c98c 100644
--- a/gfs2/fsck/eattr.c
+++ b/gfs2/fsck/eattr.c
@@ -1,15 +1,3 @@
-/******************************************************************************
-*******************************************************************************
-**
-**  Copyright (C) 2005 Red Hat, Inc.  All rights reserved.
-**
-**  This copyrighted material is made available to anyone wishing to use,
-**  modify, copy, or redistribute it subject to the terms and conditions
-**  of the GNU General Public License v.2.
-**
-*******************************************************************************
-******************************************************************************/
-
 #include <stdint.h>
 #include <string.h>
 #include <libintl.h>
diff --git a/gfs2/fsck/eattr.h b/gfs2/fsck/eattr.h
index fac8240..f4264b0 100644
--- a/gfs2/fsck/eattr.h
+++ b/gfs2/fsck/eattr.h
@@ -1,15 +1,3 @@
-/******************************************************************************
-*******************************************************************************
-**
-**  Copyright (C) 2005 Red Hat, Inc.  All rights reserved.
-**
-**  This copyrighted material is made available to anyone wishing to use,
-**  modify, copy, or redistribute it subject to the terms and conditions
-**  of the GNU General Public License v.2.
-**
-*******************************************************************************
-******************************************************************************/
-
 #ifndef _EATTR_H
 #define _EATTR_H
 
diff --git a/gfs2/fsck/fs_bits.c b/gfs2/fsck/fs_bits.c
new file mode 100644
index 0000000..f45aabe
--- /dev/null
+++ b/gfs2/fsck/fs_bits.c
@@ -0,0 +1,259 @@
+#include <inttypes.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "libgfs2.h"
+
+#if BITS_PER_LONG == 32
+#define LBITMASK   (0x55555555UL)
+#define LBITSKIP55 (0x55555555UL)
+#define LBITSKIP00 (0x00000000UL)
+#else
+#define LBITMASK   (0x5555555555555555UL)
+#define LBITSKIP55 (0x5555555555555555UL)
+#define LBITSKIP00 (0x0000000000000000UL)
+#endif
+
+#define ALIGN(x,a) (((x)+(a)-1)&~((a)-1))
+
+/**
+ * gfs2_bit_search
+ * @ptr: Pointer to bitmap data
+ * @mask: Mask to use (normally 0x55555.... but adjusted for search start)
+ * @state: The state we are searching for
+ *
+ * We xor the bitmap data with a patter which is the bitwise opposite
+ * of what we are looking for, this gives rise to a pattern of ones
+ * wherever there is a match. Since we have two bits per entry, we
+ * take this pattern, shift it down by one place and then and it with
+ * the original. All the even bit positions (0,2,4, etc) then represent
+ * successful matches, so we mask with 0x55555..... to remove the unwanted
+ * odd bit positions.
+ *
+ * This allows searching of a whole u64 at once (32 blocks) with a
+ * single test (on 64 bit arches).
+ */
+
+static inline uint64_t gfs2_bit_search(const unsigned long long *ptr,
+				       unsigned long long mask,
+				       uint8_t state)
+{
+	unsigned long long tmp;
+	static const unsigned long long search[] = {
+		[0] = 0xffffffffffffffffULL,
+		[1] = 0xaaaaaaaaaaaaaaaaULL,
+		[2] = 0x5555555555555555ULL,
+		[3] = 0x0000000000000000ULL,
+	};
+	tmp = le64_to_cpu(*ptr) ^ search[state];
+	tmp &= (tmp >> 1);
+	tmp &= mask;
+	return tmp;
+}
+
+/**
+ * gfs2_bitfit - Find a free block in the bitmaps
+ * @buffer: the buffer that holds the bitmaps
+ * @buflen: the length (in bytes) of the buffer
+ * @goal: the block to try to allocate
+ * @old_state: the state of the block we're looking for
+ *
+ * Return: the block number that was allocated
+ */
+unsigned long gfs2_bitfit(const unsigned char *buf, const unsigned int len,
+			  unsigned long goal, unsigned char state)
+{
+	unsigned long spoint = (goal << 1) & ((8 * sizeof(unsigned long long)) - 1);
+	const unsigned long long *ptr = ((unsigned long long *)buf) + (goal >> 5);
+	const unsigned long long *end = (unsigned long long *)
+		(buf + ALIGN(len, sizeof(unsigned long long)));
+	unsigned long long tmp;
+	unsigned long long mask = 0x5555555555555555ULL;
+	unsigned long bit;
+
+	if (state > 3)
+		return 0;
+
+	/* Mask off bits we don't care about at the start of the search */
+	mask <<= spoint;
+	tmp = gfs2_bit_search(ptr, mask, state);
+	ptr++;
+	while(tmp == 0 && ptr < end) {
+		tmp = gfs2_bit_search(ptr, 0x5555555555555555ULL, state);
+		ptr++;
+	}
+	/* Mask off any bits which are more than len bytes from the start */
+	if (ptr == end && (len & (sizeof(unsigned long long) - 1)))
+		tmp &= (((unsigned long long)~0) >>
+			(64 - 8 * (len & (sizeof(unsigned long long) - 1))));
+	/* Didn't find anything, so return */
+	if (tmp == 0)
+		return BFITNOENT;
+	ptr--;
+	bit = ffsll(tmp);
+	bit /= 2;	/* two bits per entry in the bitmap */
+	return (((const unsigned char *)ptr - buf) * GFS2_NBBY) + bit;
+}
+
+/**
+ * fs_bitcount - count the number of bits in a certain state
+ * @buffer: the buffer that holds the bitmaps
+ * @buflen: the length (in bytes) of the buffer
+ * @state: the state of the block we're looking for
+ *
+ * Returns: The number of bits
+ */
+uint32_t gfs2_bitcount(unsigned char *buffer, unsigned int buflen,
+		       unsigned char state)
+{
+	unsigned char *byte, *end;
+	unsigned int bit;
+	uint32_t count = 0;
+
+	byte = buffer;
+	bit = 0;
+	end = buffer + buflen;
+
+	while (byte < end){
+		if (((*byte >> bit) & GFS2_BIT_MASK) == state)
+			count++;
+
+		bit += GFS2_BIT_SIZE;
+		if (bit >= 8){
+			bit = 0;
+			byte++;
+		}
+	}
+	return count;
+}
+
+/*
+ * check_range - check if blkno is within FS limits
+ * @sdp: super block
+ * @blkno: block number
+ *
+ * Returns: 0 if ok, -1 if out of bounds
+ */
+int gfs2_check_range(struct gfs2_sbd *sdp, uint64_t blkno)
+{
+	if((blkno > sdp->fssize) || (blkno <= sdp->sb_addr))
+		return -1;
+	return 0;
+}
+
+/*
+ * valid_block - check if blkno is valid and not part of our rgrps or bitmaps
+ * @sdp: super block
+ * @blkno: block number
+ *
+ * Returns: 1 if ok, 0 if out of bounds
+ */
+int valid_block(struct gfs2_sbd *sdp, uint64_t blkno)
+{
+	if((blkno > sdp->fssize) || (blkno <= sdp->sb_addr))
+		return 0;
+	/* Check if the block is one of our rgrp or bitmap blocks */
+	if (gfs2_get_bitmap(sdp, blkno, NULL) < 0)
+		return 0;
+	return 1;
+}
+
+/*
+ * gfs2_set_bitmap
+ * @sdp: super block
+ * @blkno: block number relative to file system
+ * @state: one of three possible states
+ *
+ * This function sets the value of a bit of the
+ * file system bitmap.
+ *
+ * Returns: 0 on success, -1 on error
+ */
+int gfs2_set_bitmap(struct gfs2_sbd *sdp, uint64_t blkno, int state)
+{
+	int           buf;
+	uint32_t        rgrp_block;
+	struct gfs2_bitmap *bits = NULL;
+	struct rgrp_tree *rgd;
+	unsigned char *byte, cur_state;
+	unsigned int bit;
+
+	/* FIXME: should GFS2_BLKST_INVALID be allowed */
+	if ((state < GFS2_BLKST_FREE) || (state > GFS2_BLKST_DINODE))
+		return -1;
+
+	rgd = gfs2_blk2rgrpd(sdp, blkno);
+
+	if(!rgd || blkno < rgd->ri.ri_data0)
+		return -1;
+
+	rgrp_block = (uint32_t)(blkno - rgd->ri.ri_data0);
+	for(buf= 0; buf < rgd->ri.ri_length; buf++){
+		bits = &(rgd->bits[buf]);
+		if(rgrp_block < ((bits->bi_start + bits->bi_len)*GFS2_NBBY))
+			break;
+	}
+
+	byte = (unsigned char *)(rgd->bh[buf]->b_data + bits->bi_offset) +
+		(rgrp_block/GFS2_NBBY - bits->bi_start);
+	bit = (rgrp_block % GFS2_NBBY) * GFS2_BIT_SIZE;
+
+	cur_state = (*byte >> bit) & GFS2_BIT_MASK;
+	*byte ^= cur_state << bit;
+	*byte |= state << bit;
+
+	bmodified(rgd->bh[buf]);
+	return 0;
+}
+
+/*
+ * gfs2_get_bitmap - get value of FS bitmap
+ * @sdp: super block
+ * @blkno: block number relative to file system
+ *
+ * This function gets the value of a bit of the
+ * file system bitmap.
+ * Possible state values for a block in the bitmap are:
+ *  GFS_BLKST_FREE     (0)
+ *  GFS_BLKST_USED     (1)
+ *  GFS_BLKST_INVALID  (2)
+ *  GFS_BLKST_DINODE   (3)
+ *
+ * Returns: state on success, -1 on error
+ */
+int gfs2_get_bitmap(struct gfs2_sbd *sdp, uint64_t blkno,
+		    struct rgrp_tree *rgd)
+{
+	int           i, val;
+	uint32_t        rgrp_block;
+	struct gfs2_bitmap	*bits = NULL;
+	unsigned int  bit;
+	unsigned char *byte;
+
+	if (rgd == NULL) {
+		rgd = gfs2_blk2rgrpd(sdp, blkno);
+		if(rgd == NULL)
+			return -1;
+	}
+
+	rgrp_block = (uint32_t)(blkno - rgd->ri.ri_data0);
+
+	for (i = 0; i < rgd->ri.ri_length; i++) {
+		bits = &(rgd->bits[i]);
+		if(rgrp_block < ((bits->bi_start + bits->bi_len)*GFS2_NBBY))
+			break;
+	}
+
+	if (i >= rgd->ri.ri_length)
+		return -1;
+	if (!rgd->bh || !rgd->bh[i])
+		return 0;
+	byte = (unsigned char *)(rgd->bh[i]->b_data + bits->bi_offset) +
+		(rgrp_block/GFS2_NBBY - bits->bi_start);
+	bit = (rgrp_block % GFS2_NBBY) * GFS2_BIT_SIZE;
+
+	val = ((*byte >> bit) & GFS2_BIT_MASK);
+
+	return val;
+}
diff --git a/gfs2/fsck/fs_bits.h b/gfs2/fsck/fs_bits.h
index 2838785..d4f262e 100644
--- a/gfs2/fsck/fs_bits.h
+++ b/gfs2/fsck/fs_bits.h
@@ -1,20 +1,6 @@
-/******************************************************************************
-*******************************************************************************
-**
-**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
-**  Copyright (C) 2004-2005 Red Hat, Inc.  All rights reserved.
-**
-**  This copyrighted material is made available to anyone wishing to use,
-**  modify, copy, or redistribute it subject to the terms and conditions
-**  of the GNU General Public License v.2.
-**
-*******************************************************************************
-******************************************************************************/
-
 #ifndef __FS_BITS_H__
 #define __FS_BITS_H__
 
-/*#include "global.h" */
 #include "libgfs2.h"
 #include "fsck.h"
 
diff --git a/gfs2/fsck/fs_geometry.c b/gfs2/fsck/fs_geometry.c
new file mode 100644
index 0000000..c29543f
--- /dev/null
+++ b/gfs2/fsck/fs_geometry.c
@@ -0,0 +1,259 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <linux/types.h>
+#include "libgfs2.h"
+
+#define DIV_RU(x, y) (((x) + (y) - 1) / (y))
+
+/**
+ * how_many_rgrps - figure out how many RG to put in a subdevice
+ * @w: the command line
+ * @dev: the device
+ *
+ * Returns: the number of RGs
+ */
+
+uint64_t how_many_rgrps(struct gfs2_sbd *sdp, struct device *dev, int rgsize_specified)
+{
+	uint64_t nrgrp;
+	uint32_t rgblocks1, rgblocksn, bitblocks1, bitblocksn;
+	int bitmap_overflow = 0;
+
+	while (TRUE) {
+		nrgrp = DIV_RU(dev->length, (sdp->rgsize << 20) / sdp->bsize);
+
+		/* check to see if the rg length overflows max # bitblks */
+		rgblocksn = dev->length / nrgrp;
+		rgblocks2bitblocks(sdp->bsize, &rgblocksn, &bitblocksn);
+		/* calculate size of the first rgrp */
+		rgblocks1 = dev->length - (nrgrp - 1) * (dev->length / nrgrp);
+		rgblocks2bitblocks(sdp->bsize, &rgblocks1, &bitblocks1);
+		if (bitblocks1 > 2149 || bitblocksn > 2149) {
+			bitmap_overflow = 1;
+			if (sdp->rgsize <= GFS2_DEFAULT_RGSIZE) {
+				fprintf(stderr, "error: It is not possible "
+					"to use the entire device with "
+					"block size %u bytes.\n",
+					sdp->bsize);
+				exit(-1);
+			}
+			sdp->rgsize -= GFS2_DEFAULT_RGSIZE; /* smaller rgs */
+			continue;
+		}
+		if (bitmap_overflow ||
+		    rgsize_specified || /* If user specified an rg size or */
+		    nrgrp <= GFS2_EXCESSIVE_RGS || /* not an excessive # or  */
+		    sdp->rgsize >= 2048)   /* we reached the max rg size */
+			break;
+
+		sdp->rgsize += GFS2_DEFAULT_RGSIZE; /* bigger rgs */
+	}
+
+	if (sdp->debug)
+		printf("  rg sz = %"PRIu32"\n  nrgrp = %"PRIu64"\n",
+		       sdp->rgsize, nrgrp);
+
+	return nrgrp;
+}
+
+/**
+ * compute_rgrp_layout - figure out where the RG in a FS are
+ * @w: the command line
+ *
+ * Returns: a list of rgrp_list_t structures
+ */
+
+void compute_rgrp_layout(struct gfs2_sbd *sdp, struct osi_root *rgtree,
+			 int rgsize_specified)
+{
+	struct device *dev;
+	struct rgrp_tree *rl, *rlast = NULL, *rlast2 = NULL;
+	struct osi_node *n, *next = NULL;
+	unsigned int rgrp = 0, nrgrp, rglength;
+	uint64_t rgaddr;
+
+	sdp->new_rgrps = 0;
+	dev = &sdp->device;
+
+	/* Reserve space for the superblock */
+	dev->start += sdp->sb_addr + 1;
+
+	/* If this is a new file system, compute the length and number */
+	/* of rgs based on the size of the device.                     */
+	/* If we have existing RGs (i.e. gfs2_grow) find the last one. */
+	if (!rgtree->osi_node) {
+		dev->length -= sdp->sb_addr + 1;
+		nrgrp = how_many_rgrps(sdp, dev, rgsize_specified);
+		rglength = dev->length / nrgrp;
+		sdp->new_rgrps = nrgrp;
+	} else {
+		uint64_t old_length, new_chunk;
+
+		log_info("Existing resource groups:\n");
+		for (rgrp = 0, n = osi_first(rgtree); n; n = next, rgrp++) {
+			next = osi_next(n);
+			rl = (struct rgrp_tree *)n;
+
+			log_info("%d: start: %" PRIu64 " (0x%"
+				 PRIx64 "), length = %"PRIu64" (0x%"
+				 PRIx64 ")\n", rgrp + 1, rl->start, rl->start,
+				 rl->length, rl->length);
+			rlast2 = rlast;
+			rlast = rl;
+		}
+		rlast->start = rlast->ri.ri_addr;
+		rglength = rgrp_size(rlast);
+		rlast->length = rglength;
+		old_length = rlast->ri.ri_addr + rglength;
+		new_chunk = dev->length - old_length;
+		sdp->new_rgrps = new_chunk / rglength;
+		nrgrp = rgrp + sdp->new_rgrps;
+	}
+
+	if (rgrp < nrgrp)
+		log_info("\nNew resource groups:\n");
+	for (; rgrp < nrgrp; rgrp++) {
+		if (rgrp) {
+			rgaddr = rlast->start + rlast->length;
+			rl = rgrp_insert(rgtree, rgaddr);
+			rl->length = rglength;
+		} else {
+			rgaddr = dev->start;
+			rl = rgrp_insert(rgtree, rgaddr);
+			rl->length = dev->length -
+				(nrgrp - 1) * (dev->length / nrgrp);
+		}
+		rl->start = rgaddr;
+		log_info("%d: start: %" PRIu64 " (0x%"
+			 PRIx64 "), length = %"PRIu64" (0x%"
+			 PRIx64 ")\n", rgrp + 1, rl->start, rl->start,
+			 rl->length, rl->length);
+		rlast = rl;
+	}
+
+	sdp->rgrps = nrgrp;
+	if (sdp->debug) {
+		log_info("\n");
+
+		for (n = osi_first(rgtree); n; n = next) {
+			next = osi_next(n);
+			rl = (struct rgrp_tree *)n;
+			log_info("rg_o = %llu, rg_l = %llu\n",
+				 (unsigned long long)rl->start,
+				 (unsigned long long)rl->length);
+		}
+	}
+}
+
+/**
+ * rgblocks2bitblocks -
+ * @bsize:
+ * @rgblocks:
+ * @bitblocks:
+ *
+ * Given a number of blocks in a RG, figure out the number of blocks
+ * needed for bitmaps.
+ *
+ */
+
+void rgblocks2bitblocks(unsigned int bsize, uint32_t *rgblocks, uint32_t *bitblocks)
+{
+	unsigned int bitbytes_provided, last = 0;
+	unsigned int bitbytes_needed;
+
+	*bitblocks = 1;
+	bitbytes_provided = bsize - sizeof(struct gfs2_rgrp);
+
+	for (;;) {
+	        bitbytes_needed = (*rgblocks - *bitblocks) / GFS2_NBBY;
+
+		if (bitbytes_provided >= bitbytes_needed) {
+			if (last >= bitbytes_needed)
+				(*bitblocks)--;
+			break;
+		}
+
+		last = bitbytes_provided;
+		(*bitblocks)++;
+		bitbytes_provided += bsize - sizeof(struct gfs2_meta_header);
+	}
+
+	*rgblocks = bitbytes_needed * GFS2_NBBY;
+}
+
+/**
+ * build_rgrps - write a bunch of resource groups to disk.
+ * If fd > 0, write the data to the given file handle.
+ * Otherwise, use gfs2 buffering in buf.c.
+ */
+void build_rgrps(struct gfs2_sbd *sdp, int do_write)
+{
+	struct osi_node *n, *next = NULL;
+	struct rgrp_tree *rl;
+	uint32_t rgblocks, bitblocks;
+	struct gfs2_rindex *ri;
+	struct gfs2_meta_header mh;
+	unsigned int x;
+
+	mh.mh_magic = GFS2_MAGIC;
+	mh.mh_type = GFS2_METATYPE_RB;
+	mh.mh_format = GFS2_FORMAT_RB;
+	if (do_write)
+		n = osi_first(&sdp->rgtree);
+	else
+		n = osi_first(&sdp->rgcalc);
+
+	for (; n; n = next) {
+		next = osi_next(n);
+		rl = (struct rgrp_tree *)n;
+		ri = &rl->ri;
+
+		rgblocks = rl->length;
+		rgblocks2bitblocks(sdp->bsize, &rgblocks, &bitblocks);
+
+		ri->ri_addr = rl->start;
+		ri->ri_length = bitblocks;
+		ri->ri_data0 = rl->start + bitblocks;
+		ri->ri_data = rgblocks;
+		ri->ri_bitbytes = rgblocks / GFS2_NBBY;
+
+		memset(&rl->rg, 0, sizeof(rl->rg));
+		rl->rg.rg_header.mh_magic = GFS2_MAGIC;
+		rl->rg.rg_header.mh_type = GFS2_METATYPE_RG;
+		rl->rg.rg_header.mh_format = GFS2_FORMAT_RG;
+		rl->rg.rg_free = rgblocks;
+
+		if (gfs2_compute_bitstructs(sdp, rl)) {
+			fprintf(stderr, "%s: Unable to build resource groups "
+				"with these characteristics.\n", __FUNCTION__);
+			exit(-1);
+		}
+
+		if (do_write) {
+			for (x = 0; x < bitblocks; x++) {
+				rl->bh[x] = bget(sdp, rl->start + x);
+				if (x)
+					gfs2_meta_header_out(&mh, rl->bh[x]);
+				else
+					gfs2_rgrp_out(&rl->rg, rl->bh[x]);
+			}
+		}
+
+		if (sdp->debug) {
+			printf("\n");
+			gfs2_rindex_print(ri);
+		}
+
+		sdp->blks_total += rgblocks;
+		sdp->fssize = ri->ri_data0 + ri->ri_data;
+	}
+}
diff --git a/gfs2/fsck/fs_ops.c b/gfs2/fsck/fs_ops.c
new file mode 100644
index 0000000..2b0d528
--- /dev/null
+++ b/gfs2/fsck/fs_ops.c
@@ -0,0 +1,1841 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <linux/types.h>
+#include "libgfs2.h"
+
+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];
+}
+
+/* Detect directory is a stuffed inode */
+static int inode_is_stuffed(struct gfs2_inode *ip)
+{
+	return !ip->i_di.di_height;
+}
+
+struct gfs2_inode *inode_get(struct gfs2_sbd *sdp, struct gfs2_buffer_head *bh)
+{
+	struct gfs2_inode *ip;
+
+	ip = calloc(1, sizeof(struct gfs2_inode));
+	if (ip == NULL) {
+		fprintf(stderr, "Out of memory in %s\n", __FUNCTION__);
+		exit(-1);
+	}
+	gfs2_dinode_in(&ip->i_di, bh);
+	ip->i_bh = bh;
+	ip->i_sbd = sdp;
+	ip->bh_owned = 0; /* caller did the bread so we don't own the bh */
+	return ip;
+}
+
+struct gfs2_inode *inode_read(struct gfs2_sbd *sdp, uint64_t di_addr)
+{
+	struct gfs2_inode *ip;
+
+	ip = calloc(1, sizeof(struct gfs2_inode));
+	if (ip == NULL) {
+		fprintf(stderr, "Out of memory in %s\n", __FUNCTION__);
+		exit(-1);
+	}
+	ip->i_bh = bread(sdp, di_addr);
+	gfs2_dinode_in(&ip->i_di, ip->i_bh);
+	ip->i_sbd = sdp;
+	ip->bh_owned = 1; /* We did the bread so we own the bh */
+	return ip;
+}
+
+struct gfs2_inode *is_system_inode(struct gfs2_sbd *sdp, uint64_t block)
+{
+	int j;
+
+	if (sdp->md.inum && block == sdp->md.inum->i_di.di_num.no_addr)
+		return sdp->md.inum;
+	if (sdp->md.statfs && block == sdp->md.statfs->i_di.di_num.no_addr)
+		return sdp->md.statfs;
+	if (sdp->md.jiinode && block == sdp->md.jiinode->i_di.di_num.no_addr)
+		return sdp->md.jiinode;
+	if (sdp->md.riinode && block == sdp->md.riinode->i_di.di_num.no_addr)
+		return sdp->md.riinode;
+	if (sdp->md.qinode && block == sdp->md.qinode->i_di.di_num.no_addr)
+		return sdp->md.qinode;
+	if (sdp->md.pinode && block == sdp->md.pinode->i_di.di_num.no_addr)
+		return sdp->md.pinode;
+	if (sdp->md.rooti && block == sdp->md.rooti->i_di.di_num.no_addr)
+		return sdp->md.rooti;
+	if (sdp->master_dir && block == sdp->master_dir->i_di.di_num.no_addr)
+		return sdp->master_dir;
+	for (j = 0; j < sdp->md.journals; j++)
+		if (sdp->md.journal && sdp->md.journal[j] &&
+		    block == sdp->md.journal[j]->i_di.di_num.no_addr)
+			return sdp->md.journal[j];
+	return NULL;
+}
+
+void inode_put(struct gfs2_inode **ip_in)
+{
+	struct gfs2_inode *ip = *ip_in;
+	uint64_t block = ip->i_di.di_num.no_addr;
+	struct gfs2_sbd *sdp = ip->i_sbd;
+
+	if (ip->i_bh->b_modified) {
+		gfs2_dinode_out(&ip->i_di, ip->i_bh);
+		if (!ip->bh_owned && is_system_inode(sdp, block))
+			fprintf(stderr, "Warning: Change made to inode "
+				"were discarded.\n");
+		/* This is for debugging only: a convenient place to set
+		   a breakpoint. This means a system inode was modified but
+		   not written.  That's not fatal: some places like
+		   adjust_inode in gfs2_convert will do this on purpose.
+		   It can also point out a coding problem, but we don't
+		   want to raise alarm in the users either. */
+	}
+	if (ip->bh_owned)
+		brelse(ip->i_bh);
+	ip->i_bh = NULL;
+	free(ip);
+	*ip_in = NULL; /* make sure the memory isn't accessed again */
+}
+
+static uint64_t blk_alloc_i(struct gfs2_sbd *sdp, unsigned int type)
+{
+	struct osi_node *n, *next = NULL;
+	struct rgrp_tree *rl = NULL;
+	struct gfs2_rindex *ri;
+	struct gfs2_rgrp *rg;
+	unsigned int block, bn = 0, x = 0, y = 0;
+	unsigned int state;
+	struct gfs2_buffer_head *bh;
+
+	memset(&rg, 0, sizeof(rg));
+	for (n = osi_first(&sdp->rgtree); n; n = next) {
+		next = osi_next(n);
+		rl = (struct rgrp_tree *)n;
+		if (rl->rg.rg_free)
+			break;
+	}
+
+	if (n == NULL) {
+		fprintf(stderr, "Not enough space available on device\n");
+		exit(1);
+	}
+
+	ri = &rl->ri;
+	rg = &rl->rg;
+
+	for (block = 0; block < ri->ri_length; block++) {
+		bh = rl->bh[block];
+		x = (block) ? sizeof(struct gfs2_meta_header) : sizeof(struct gfs2_rgrp);
+
+		for (; x < sdp->bsize; x++)
+			for (y = 0; y < GFS2_NBBY; y++) {
+				state = (bh->b_data[x] >> (GFS2_BIT_SIZE * y)) & 0x03;
+				if (state == GFS2_BLKST_FREE)
+					goto found;
+				bn++;
+			}
+	}
+
+	die("allocation is broken (1): %"PRIu64" %u\n",
+	    (uint64_t)rl->ri.ri_addr, rl->rg.rg_free);
+
+found:
+	if (bn >= ri->ri_bitbytes * GFS2_NBBY)
+		die("allocation is broken (2): bn: %u %u rgrp: %"PRIu64
+		    " (0x%" PRIx64 ") Free:%u\n",
+		    bn, ri->ri_bitbytes * GFS2_NBBY, (uint64_t)rl->ri.ri_addr,
+		    (uint64_t)rl->ri.ri_addr, rl->rg.rg_free);
+
+	switch (type) {
+	case DATA:
+	case META:
+		state = GFS2_BLKST_USED;
+		break;
+	case DINODE:
+		state = GFS2_BLKST_DINODE;
+		rg->rg_dinodes++;
+		break;
+	default:
+		die("bad state\n");
+	}
+
+	bh->b_data[x] &= ~(0x03 << (GFS2_BIT_SIZE * y));
+	bh->b_data[x] |= state << (GFS2_BIT_SIZE * y);
+	rg->rg_free--;
+
+	bmodified(bh);
+	if (sdp->gfs1)
+		gfs_rgrp_out((struct gfs_rgrp *)rg, rl->bh[0]);
+	else
+		gfs2_rgrp_out(rg, rl->bh[0]);
+
+	sdp->blks_alloced++;
+	return ri->ri_data0 + bn;
+}
+
+uint64_t data_alloc(struct gfs2_inode *ip)
+{
+	uint64_t x;
+	x = blk_alloc_i(ip->i_sbd, DATA);
+	ip->i_di.di_goal_data = x;
+	bmodified(ip->i_bh);
+	return x;
+}
+
+uint64_t meta_alloc(struct gfs2_inode *ip)
+{
+	uint64_t x;
+	x = blk_alloc_i(ip->i_sbd, META);
+	ip->i_di.di_goal_meta = x;
+	bmodified(ip->i_bh);
+	return x;
+}
+
+uint64_t dinode_alloc(struct gfs2_sbd *sdp)
+{
+	sdp->dinodes_alloced++;
+	return blk_alloc_i(sdp, DINODE);
+}
+
+static __inline__ void buffer_clear_tail(struct gfs2_sbd *sdp,
+					 struct gfs2_buffer_head *bh, int head)
+{
+	memset(bh->b_data + head, 0, sdp->bsize - head);
+	bmodified(bh);
+}
+
+static __inline__ void
+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,
+	       sdp->bsize - from_head);
+	memset(to_bh->b_data + sdp->bsize + to_head - from_head, 0,
+	       from_head - to_head);
+	bmodified(to_bh);
+}
+
+void unstuff_dinode(struct gfs2_inode *ip)
+{
+	struct gfs2_sbd *sdp = ip->i_sbd;
+	struct gfs2_buffer_head *bh;
+	uint64_t block = 0;
+	int isdir = S_ISDIR(ip->i_di.di_mode) || is_gfs_dir(&ip->i_di);
+
+	if (ip->i_di.di_size) {
+		if (isdir) {
+			struct gfs2_meta_header mh;
+
+			block = meta_alloc(ip);
+			bh = bget(sdp, block);
+			mh.mh_magic = GFS2_MAGIC;
+			mh.mh_type = GFS2_METATYPE_JD;
+			mh.mh_format = GFS2_FORMAT_JD;
+			gfs2_meta_header_out(&mh, bh);
+
+			buffer_copy_tail(sdp, bh,
+					 sizeof(struct gfs2_meta_header),
+					 ip->i_bh, sizeof(struct gfs2_dinode));
+
+			brelse(bh);
+		} else {
+			block = data_alloc(ip);
+			bh = bget(sdp, block);
+
+			buffer_copy_tail(sdp, bh, 0,
+					 ip->i_bh, sizeof(struct gfs2_dinode));
+			brelse(bh);
+		}
+	}
+
+	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);
+		/* no need: bmodified(ip->i_bh); buffer_clear_tail does it */
+		ip->i_di.di_blocks++;
+	}
+
+	ip->i_di.di_height = 1;
+}
+
+unsigned int calc_tree_height(struct gfs2_inode *ip, uint64_t size)
+{
+	struct gfs2_sbd *sdp = ip->i_sbd;
+	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 = sdp->sd_jheightsize;
+		max = sdp->sd_max_jheight;
+	} else {
+		arr = sdp->sd_heightsize;
+		max = sdp->sd_max_height;
+	}
+
+	for (height = 0; height < max; height++)
+		if (arr[height] >= size)
+			break;
+
+	return height;
+}
+
+void build_height(struct gfs2_inode *ip, int height)
+{
+	struct gfs2_sbd *sdp = ip->i_sbd;
+	struct gfs2_buffer_head *bh;
+	uint64_t block = 0, *bp;
+	unsigned int x;
+	int new_block;
+
+	while (ip->i_di.di_height < height) {
+		new_block = FALSE;
+		bp = (uint64_t *)(ip->i_bh->b_data + sizeof(struct gfs2_dinode));
+		for (x = 0; x < sdp->sd_diptrs; x++, bp++)
+			if (*bp) {
+				new_block = TRUE;
+				break;
+			}
+
+		if (new_block) {
+			struct gfs2_meta_header mh;
+
+			block = meta_alloc(ip);
+			bh = bget(sdp, block);
+			mh.mh_magic = GFS2_MAGIC;
+			mh.mh_type = GFS2_METATYPE_IN;
+			mh.mh_format = GFS2_FORMAT_IN;
+			gfs2_meta_header_out(&mh, bh);
+			buffer_copy_tail(sdp, bh,
+					 sizeof(struct gfs2_meta_header),
+					 ip->i_bh, sizeof(struct gfs2_dinode));
+			brelse(bh);
+		}
+
+		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);
+			/* no need: bmodified(ip->i_bh);*/
+			ip->i_di.di_blocks++;
+		}
+
+		ip->i_di.di_height++;
+	}
+}
+
+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;
+
+	mp = calloc(1, sizeof(struct metapath));
+	if (mp == NULL) {
+		fprintf(stderr, "Out of memory in %s\n", __FUNCTION__);
+		exit(-1);
+	}
+	for (i = ip->i_di.di_height; i--;) {
+		mp->mp_list[i] = b % sdp->sd_inptrs;
+		b /= sdp->sd_inptrs;
+	}
+
+	return mp;
+}
+
+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);
+	bmodified(bh);
+	ip->i_di.di_blocks++;
+	bmodified(ip->i_bh);
+
+	*new = 1;
+}
+
+void 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;
+
+	*new = 0;
+	*dblock = 0;
+	if (extlen)
+		*extlen = 0;
+
+	if (inode_is_stuffed(ip)) {
+		if (!lblock) {
+			*dblock = ip->i_di.di_num.no_addr;
+			if (extlen)
+				*extlen = 1;
+		}
+		return;
+	}
+
+	bsize = (S_ISDIR(ip->i_di.di_mode)) ? 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 = ip->i_bh;
+
+	for (x = 0; x < end_of_metadata; x++) {
+		lookup_block(ip, bh, x, mp, create, new, dblock);
+		if (bh != ip->i_bh)
+			brelse(bh);
+		if (!*dblock)
+			goto out;
+
+		if (*new) {
+			struct gfs2_meta_header mh;
+			bh = bget(sdp, *dblock);
+			mh.mh_magic = GFS2_MAGIC;
+			mh.mh_type = GFS2_METATYPE_IN;
+			mh.mh_format = GFS2_FORMAT_IN;
+			gfs2_meta_header_out(&mh, bh);
+		} else {
+			if (*dblock == ip->i_di.di_num.no_addr)
+				bh = ip->i_bh;
+			else
+				bh = bread(sdp, *dblock);
+		}
+	}
+
+	if (!prealloc)
+		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) {
+				lookup_block(ip, bh, end_of_metadata, mp, FALSE, &tmp_new,
+							 &tmp_dblock);
+
+				if (*dblock + *extlen != tmp_dblock)
+					break;
+
+				(*extlen)++;
+			}
+		}
+	}
+
+	if (bh != ip->i_bh)
+		brelse(bh);
+
+ out:
+	free(mp);
+}
+
+static void
+copy2mem(struct gfs2_buffer_head *bh, void **buf, unsigned int offset,
+	 unsigned int size)
+{
+	char **p = (char **)buf;
+
+	if (bh)
+		memcpy(*p, bh->b_data + offset, size);
+	else
+		memset(*p, 0, size);
+
+	*p += size;
+}
+
+int gfs2_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;
+	unsigned int o;
+	uint32_t extlen = 0;
+	unsigned int amount;
+	int not_new = 0;
+	int isdir = !!(S_ISDIR(ip->i_di.di_mode));
+	int journaled = ip->i_di.di_flags & GFS2_DIF_JDATA;
+	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 ((sdp->gfs1 && journaled) || (!sdp->gfs1 && isdir)) {
+		lblock = offset;
+		o = lblock % sdp->sd_jbsize;
+		lblock /= sdp->sd_jbsize;
+	} else {
+		lblock = offset >> sdp->sd_sb.sb_bsize_shift;
+		o = offset & (sdp->bsize - 1);
+	}
+
+	if (inode_is_stuffed(ip))
+		o += sizeof(struct gfs2_dinode);
+	else if ((sdp->gfs1 && journaled) || (!sdp->gfs1 && isdir))
+		o += sizeof(struct gfs2_meta_header);
+
+	while (copied < size) {
+		amount = size - copied;
+		if (amount > sdp->bsize - o)
+			amount = sdp->bsize - o;
+
+		if (!extlen) {
+			if (sdp->gfs1)
+				gfs1_block_map(ip, lblock, &not_new, &dblock,
+					       &extlen, FALSE);
+			else
+				block_map(ip, lblock, &not_new, &dblock,
+					  &extlen, FALSE);
+		}
+
+		if (dblock) {
+			if (dblock == ip->i_di.di_num.no_addr)
+				bh = ip->i_bh;
+			else
+				bh = bread(sdp, dblock);
+			dblock++;
+			extlen--;
+		} else
+			bh = NULL;
+
+		copy2mem(bh, &buf, o, amount);
+		if (bh && bh != ip->i_bh)
+			brelse(bh);
+
+		copied += amount;
+		lblock++;
+
+		if (sdp->gfs1)
+			o = (journaled) ? sizeof(struct gfs2_meta_header) : 0;
+		else
+			o = (isdir) ? sizeof(struct gfs2_meta_header) : 0;
+	}
+
+	return copied;
+}
+
+static void copy_from_mem(struct gfs2_buffer_head *bh, void **buf,
+			  unsigned int offset, unsigned int size)
+{
+	char **p = (char **)buf;
+
+	memcpy(bh->b_data + offset, *p, size);
+	bmodified(bh);
+	*p += size;
+}
+
+int __gfs2_writei(struct gfs2_inode *ip, void *buf,
+		  uint64_t offset, unsigned int size, int resize)
+{
+	struct gfs2_sbd *sdp = ip->i_sbd;
+	struct gfs2_buffer_head *bh;
+	uint64_t lblock, dblock;
+	unsigned int o;
+	uint32_t extlen = 0;
+	unsigned int amount;
+	int new;
+	int isdir = !!(S_ISDIR(ip->i_di.di_mode));
+	const uint64_t start = offset;
+	int copied = 0;
+
+	if (!size)
+		return 0;
+
+	if (inode_is_stuffed(ip) &&
+	    ((start + size) > (sdp->bsize - sizeof(struct gfs2_dinode))))
+		unstuff_dinode(ip);
+
+	if (isdir) {
+		lblock = offset;
+		o = lblock % sdp->sd_jbsize;
+		lblock /= sdp->sd_jbsize;
+	} else {
+		lblock = offset >> sdp->sd_sb.sb_bsize_shift;
+		o = offset & (sdp->bsize - 1);
+	}
+
+	if (inode_is_stuffed(ip))
+		o += sizeof(struct gfs2_dinode);
+	else if (isdir)
+		o += sizeof(struct gfs2_meta_header);
+
+	while (copied < size) {
+		amount = size - copied;
+		if (amount > sdp->bsize - o)
+			amount = sdp->bsize - o;
+
+		if (!extlen) {
+			new = TRUE;
+			block_map(ip, lblock, &new, &dblock, &extlen, FALSE);
+		}
+
+		if (new) {
+			bh = bget(sdp, dblock);
+			if (isdir) {
+				struct gfs2_meta_header mh;
+				mh.mh_magic = GFS2_MAGIC;
+				mh.mh_type = GFS2_METATYPE_JD;
+				mh.mh_format = GFS2_FORMAT_JD;
+				gfs2_meta_header_out(&mh, bh);
+			}
+		} else {
+			if (dblock == ip->i_di.di_num.no_addr)
+				bh = ip->i_bh;
+			else
+				bh = bread(sdp, dblock);
+		}
+		copy_from_mem(bh, &buf, o, amount);
+		if (bh != ip->i_bh)
+			brelse(bh);
+
+		copied += amount;
+		lblock++;
+		dblock++;
+		extlen--;
+
+		o = (isdir) ? sizeof(struct gfs2_meta_header) : 0;
+	}
+
+	if (resize && ip->i_di.di_size < start + copied) {
+		bmodified(ip->i_bh);
+		ip->i_di.di_size = start + copied;
+	}
+
+	return copied;
+}
+
+struct gfs2_buffer_head *get_file_buf(struct gfs2_inode *ip, uint64_t lbn,
+				      int prealloc)
+{
+	struct gfs2_sbd *sdp = ip->i_sbd;
+	uint64_t dbn;
+	int new = TRUE;
+
+	if (inode_is_stuffed(ip))
+		unstuff_dinode(ip);
+
+	block_map(ip, lbn, &new, &dbn, NULL, prealloc);
+	if (!dbn)
+		die("get_file_buf\n");
+
+	if (!prealloc && new &&
+	    ip->i_di.di_size < (lbn + 1) << sdp->sd_sb.sb_bsize_shift) {
+		bmodified(ip->i_bh);
+		ip->i_di.di_size = (lbn + 1) << sdp->sd_sb.sb_bsize_shift;
+	}
+	if (dbn == ip->i_di.di_num.no_addr)
+		return ip->i_bh;
+	else
+		return bread(sdp, dbn);
+}
+
+int gfs2_dirent_first(struct gfs2_inode *dip, struct gfs2_buffer_head *bh,
+					  struct gfs2_dirent **dent)
+{
+	struct gfs2_meta_header *h = (struct gfs2_meta_header *)bh->b_data;
+
+	if (be32_to_cpu(h->mh_type) == GFS2_METATYPE_LF) {
+		*dent = (struct gfs2_dirent *)(bh->b_data + sizeof(struct gfs2_leaf));
+		return IS_LEAF;
+	} else {
+		*dent = (struct gfs2_dirent *)(bh->b_data + sizeof(struct gfs2_dinode));
+		return IS_DINODE;
+	}
+}
+
+int gfs2_dirent_next(struct gfs2_inode *dip, struct gfs2_buffer_head *bh,
+					 struct gfs2_dirent **dent)
+{
+	char *bh_end;
+	uint16_t cur_rec_len;
+
+	bh_end = bh->b_data + dip->i_sbd->bsize;
+	cur_rec_len = be16_to_cpu((*dent)->de_rec_len);
+
+	if (cur_rec_len == 0 || (char *)(*dent) + cur_rec_len >= bh_end)
+		return -ENOENT;
+
+	*dent = (struct gfs2_dirent *)((char *)(*dent) + cur_rec_len);
+
+	return 0;
+}
+
+/**
+ * Allocate a gfs2 dirent
+ * Returns 0 on success, with *dent_out pointing to the new dirent,
+ * or -1 on failure, with errno set
+ */
+static int dirent_alloc(struct gfs2_inode *dip, struct gfs2_buffer_head *bh,
+			int name_len, struct gfs2_dirent **dent_out)
+{
+	struct gfs2_dirent *dent, *new;
+	unsigned int rec_len = GFS2_DIRENT_SIZE(name_len);
+	unsigned int entries = 0, offset = 0;
+	int type;
+
+	type = gfs2_dirent_first(dip, bh, &dent);
+
+	if (type == IS_LEAF) {
+		struct gfs2_leaf *leaf = (struct gfs2_leaf *)bh->b_data;
+		entries = be16_to_cpu(leaf->lf_entries);
+		offset = sizeof(struct gfs2_leaf);
+	} else {
+		struct gfs2_dinode *dinode = (struct gfs2_dinode *)bh->b_data;
+		entries = be32_to_cpu(dinode->di_entries);
+		offset = sizeof(struct gfs2_dinode);
+	}
+
+	if (!entries) {
+		dent->de_rec_len = cpu_to_be16(dip->i_sbd->bsize - offset);
+		dent->de_name_len = cpu_to_be16(name_len);
+		bmodified(bh);
+		*dent_out = dent;
+		dip->i_di.di_entries++;
+		bmodified(dip->i_bh);
+		return 0;
+	}
+
+	do {
+		uint16_t cur_rec_len;
+		uint16_t cur_name_len;
+		uint16_t new_rec_len;
+
+		cur_rec_len = be16_to_cpu(dent->de_rec_len);
+		cur_name_len = be16_to_cpu(dent->de_name_len);
+
+		if ((!dent->de_inum.no_formal_ino && cur_rec_len >= rec_len) ||
+		    (cur_rec_len >= GFS2_DIRENT_SIZE(cur_name_len) + rec_len)) {
+
+			if (dent->de_inum.no_formal_ino) {
+				new = (struct gfs2_dirent *)((char *)dent +
+							    GFS2_DIRENT_SIZE(cur_name_len));
+				memset(new, 0, sizeof(struct gfs2_dirent));
+
+				new->de_rec_len = cpu_to_be16(cur_rec_len -
+					  GFS2_DIRENT_SIZE(cur_name_len));
+				new->de_name_len = cpu_to_be16(name_len);
+
+				new_rec_len = be16_to_cpu(new->de_rec_len);
+				dent->de_rec_len = cpu_to_be16(cur_rec_len - new_rec_len);
+
+				*dent_out = new;
+				bmodified(bh);
+				dip->i_di.di_entries++;
+				bmodified(dip->i_bh);
+				return 0;
+			}
+
+			dent->de_name_len = cpu_to_be16(name_len);
+
+			*dent_out = dent;
+			bmodified(bh);
+			dip->i_di.di_entries++;
+			bmodified(dip->i_bh);
+			return 0;
+		}
+	} while (gfs2_dirent_next(dip, bh, &dent) == 0);
+
+	errno = ENOSPC;
+	return -1;
+}
+
+void dirent2_del(struct gfs2_inode *dip, struct gfs2_buffer_head *bh,
+		 struct gfs2_dirent *prev, struct gfs2_dirent *cur)
+{
+	uint16_t cur_rec_len, prev_rec_len;
+
+	bmodified(bh);
+	if (gfs2_check_meta(bh, GFS2_METATYPE_LF) == 0) {
+		struct gfs2_leaf *lf = (struct gfs2_leaf *)bh->b_data;
+
+		lf->lf_entries = be16_to_cpu(lf->lf_entries) - 1;
+		lf->lf_entries = cpu_to_be16(lf->lf_entries);
+	}
+
+	if (dip->i_di.di_entries) {
+		bmodified(dip->i_bh);
+		dip->i_di.di_entries--;
+	}
+	if (!prev) {
+		cur->de_inum.no_formal_ino = 0;
+		return;
+	}
+
+	prev_rec_len = be16_to_cpu(prev->de_rec_len);
+	cur_rec_len = be16_to_cpu(cur->de_rec_len);
+
+	prev_rec_len += cur_rec_len;
+	prev->de_rec_len = cpu_to_be16(prev_rec_len);
+}
+
+void gfs2_get_leaf_nr(struct gfs2_inode *dip, uint32_t lindex,
+		      uint64_t *leaf_out)
+{
+	uint64_t leaf_no;
+	int count;
+
+	if (dip->i_sbd->gfs1)
+		return gfs_get_leaf_nr(dip, lindex, leaf_out);
+	count = gfs2_readi(dip, (char *)&leaf_no, lindex * sizeof(uint64_t),
+			   sizeof(uint64_t));
+	if (count != sizeof(uint64_t))
+		die("gfs2_get_leaf_nr:  Bad internal read.\n");
+
+	*leaf_out = be64_to_cpu(leaf_no);
+}
+
+void gfs2_put_leaf_nr(struct gfs2_inode *dip, uint32_t inx, uint64_t leaf_out)
+{
+	uint64_t leaf_no;
+	int count;
+
+	if (dip->i_sbd->gfs1) {
+		gfs_put_leaf_nr(dip, inx, leaf_out);
+		return;
+	}
+	leaf_no = cpu_to_be64(leaf_out);
+	count = gfs2_writei(dip, (char *)&leaf_no, inx * sizeof(uint64_t),
+			    sizeof(uint64_t));
+	if (count != sizeof(uint64_t))
+		die("gfs2_put_leaf_nr:  Bad internal write.\n");
+}
+
+void dir_split_leaf(struct gfs2_inode *dip, uint32_t start, uint64_t leaf_no,
+		    struct gfs2_buffer_head *obh)
+{
+	struct gfs2_buffer_head *nbh;
+	struct gfs2_leaf *nleaf, *oleaf;
+	struct gfs2_dirent *dent, *prev = NULL, *next = NULL, *new;
+	uint32_t len, half_len, divider;
+	uint64_t bn, *lp;
+	uint32_t name_len;
+	int x, moved = FALSE;
+	int count;
+
+	bn = meta_alloc(dip);
+	nbh = bget(dip->i_sbd, bn);
+	{
+		struct gfs2_meta_header mh;
+		mh.mh_magic = GFS2_MAGIC;
+		mh.mh_type = GFS2_METATYPE_LF;
+		mh.mh_format = GFS2_FORMAT_LF;
+		gfs2_meta_header_out(&mh, nbh);
+		buffer_clear_tail(dip->i_sbd, nbh,
+				  sizeof(struct gfs2_meta_header));
+	}
+
+	nleaf = (struct gfs2_leaf *)nbh->b_data;
+	nleaf->lf_dirent_format = cpu_to_be32(GFS2_FORMAT_DE);
+
+	oleaf = (struct gfs2_leaf *)obh->b_data;
+
+	len = 1 << (dip->i_di.di_depth - be16_to_cpu(oleaf->lf_depth));
+	half_len = len >> 1;
+
+	lp = calloc(1, half_len * sizeof(uint64_t));
+	if (lp == NULL) {
+		fprintf(stderr, "Out of memory in %s\n", __FUNCTION__);
+		exit(-1);
+	}
+	for (x = 0; x < half_len; x++)
+		lp[x] = cpu_to_be64(bn);
+
+	if (dip->i_sbd->gfs1)
+		count = gfs1_writei(dip, (char *)lp, start * sizeof(uint64_t),
+				    half_len * sizeof(uint64_t));
+	else
+		count = gfs2_writei(dip, (char *)lp, start * sizeof(uint64_t),
+				    half_len * sizeof(uint64_t));
+	if (count != half_len * sizeof(uint64_t))
+		die("dir_split_leaf (2)\n");
+
+	free(lp);
+
+	divider = (start + half_len) << (32 - dip->i_di.di_depth);
+
+	gfs2_dirent_first(dip, obh, &dent);
+
+	do {
+		next = dent;
+		if (gfs2_dirent_next(dip, obh, &next))
+			next = NULL;
+
+		if (dent->de_inum.no_formal_ino &&
+		    be32_to_cpu(dent->de_hash) < divider) {
+			name_len = be16_to_cpu(dent->de_name_len);
+
+			if (dirent_alloc(dip, nbh, name_len, &new))
+				die("dir_split_leaf (3)\n");
+
+			new->de_inum = dent->de_inum;
+			new->de_hash = dent->de_hash;
+			new->de_type = dent->de_type;
+			memcpy((char *)(new + 1), (char *)(dent + 1), name_len);
+
+			nleaf->lf_entries = be16_to_cpu(nleaf->lf_entries) + 1;
+			nleaf->lf_entries = cpu_to_be16(nleaf->lf_entries);
+
+			dirent2_del(dip, obh, prev, dent);
+
+			if (!prev)
+				prev = dent;
+
+			moved = TRUE;
+		} else
+			prev = dent;
+
+		dent = next;
+	} while (dent);
+
+	if (!moved) {
+		if (dirent_alloc(dip, nbh, 0, &new))
+			die("dir_split_leaf (4)\n");
+		new->de_inum.no_formal_ino = 0;
+		/* Don't count the sentinel dirent as an entry */
+		dip->i_di.di_entries--;
+	}
+
+	oleaf->lf_depth = be16_to_cpu(oleaf->lf_depth) + 1;
+	oleaf->lf_depth = cpu_to_be16(oleaf->lf_depth);
+	nleaf->lf_depth = oleaf->lf_depth;
+
+	dip->i_di.di_blocks++;
+	bmodified(dip->i_bh);
+
+	bmodified(obh); /* Need to do this in case nothing was moved */
+	bmodified(nbh);
+	brelse(nbh);
+}
+
+static void dir_double_exhash(struct gfs2_inode *dip)
+{
+	struct gfs2_sbd *sdp = dip->i_sbd;
+	uint64_t *buf;
+	uint64_t *from, *to;
+	uint64_t block;
+	int x;
+	int count;
+
+	buf = calloc(1, 3 * sdp->sd_hash_bsize);
+	if (buf == NULL) {
+		fprintf(stderr, "Out of memory in %s\n", __FUNCTION__);
+		exit(-1);
+	}
+
+	for (block = dip->i_di.di_size >> sdp->sd_hash_bsize_shift; block--;) {
+		count = gfs2_readi(dip, (char *)buf,
+			      block * sdp->sd_hash_bsize,
+			      sdp->sd_hash_bsize);
+		if (count != sdp->sd_hash_bsize)
+			die("dir_double_exhash (1)\n");
+
+		from = buf;
+		to = (uint64_t *)((char *)buf + sdp->sd_hash_bsize);
+
+		for (x = sdp->sd_hash_ptrs; x--; from++) {
+			*to++ = *from;
+			*to++ = *from;
+		}
+
+		if (sdp->gfs1)
+			count = gfs1_writei(dip, (char *)buf +
+					    sdp->sd_hash_bsize,
+					    block * sdp->bsize, sdp->bsize);
+		else
+			count = gfs2_writei(dip, (char *)buf +
+					    sdp->sd_hash_bsize,
+					    block * sdp->bsize, sdp->bsize);
+		if (count != sdp->bsize)
+			die("dir_double_exhash (2)\n");
+
+	}
+
+	free(buf);
+
+	dip->i_di.di_depth++;
+	bmodified(dip->i_bh);
+}
+
+/**
+ * get_leaf - Get leaf
+ * @dip:
+ * @leaf_no:
+ * @bh_out:
+ *
+ * Returns: 0 on success, error code otherwise
+ */
+
+int gfs2_get_leaf(struct gfs2_inode *dip, uint64_t leaf_no,
+				  struct gfs2_buffer_head **bhp)
+{
+	int error = 0;
+
+	*bhp = bread(dip->i_sbd, leaf_no);
+	error = gfs2_check_meta(*bhp, GFS2_METATYPE_LF);
+	if(error)
+		brelse(*bhp);
+	return error;
+}
+
+/**
+ * get_first_leaf - Get first leaf
+ * @dip: The GFS2 inode
+ * @index:
+ * @bh_out:
+ *
+ * Returns: 0 on success, error code otherwise
+ */
+
+static int get_first_leaf(struct gfs2_inode *dip, uint32_t lindex,
+			  struct gfs2_buffer_head **bh_out)
+{
+	uint64_t leaf_no;
+
+	gfs2_get_leaf_nr(dip, lindex, &leaf_no);
+	*bh_out = bread(dip->i_sbd, leaf_no);
+	return 0;
+}
+
+/**
+ * get_next_leaf - Get next leaf
+ * @dip: The GFS2 inode
+ * @bh_in: The buffer
+ * @bh_out:
+ *
+ * Returns: 0 on success, error code otherwise
+ */
+
+static int get_next_leaf(struct gfs2_inode *dip,struct gfs2_buffer_head *bh_in,
+						 struct gfs2_buffer_head **bh_out)
+{
+	struct gfs2_leaf *leaf;
+
+	leaf = (struct gfs2_leaf *)bh_in->b_data;
+
+	if (!leaf->lf_next)
+		return -1;
+	*bh_out = bread(dip->i_sbd, be64_to_cpu(leaf->lf_next));
+	return 0;
+}
+
+static int dir_e_add(struct gfs2_inode *dip, const char *filename, int len,
+		      struct gfs2_inum *inum, unsigned int type)
+{
+	struct gfs2_buffer_head *bh, *nbh;
+	struct gfs2_leaf *leaf, *nleaf;
+	struct gfs2_dirent *dent;
+	uint32_t lindex, llen;
+	uint32_t hash;
+	uint64_t leaf_no, bn;
+	int err = 0;
+
+	hash = gfs2_disk_hash(filename, len);
+restart:
+	/* Have to kludge because (hash >> 32) gives hash for some reason. */
+	if (dip->i_di.di_depth)
+		lindex = hash >> (32 - dip->i_di.di_depth);
+	else
+		lindex = 0;
+
+	gfs2_get_leaf_nr(dip, lindex, &leaf_no);
+
+	for (;;) {
+		bh = bread(dip->i_sbd, leaf_no);
+		leaf = (struct gfs2_leaf *)bh->b_data;
+
+		if (dirent_alloc(dip, bh, len, &dent)) {
+
+			if (be16_to_cpu(leaf->lf_depth) < dip->i_di.di_depth) {
+				llen = 1 << (dip->i_di.di_depth -
+					     be16_to_cpu(leaf->lf_depth));
+				dir_split_leaf(dip, lindex & ~(llen - 1),
+					       leaf_no, bh);
+				brelse(bh);
+				goto restart;
+
+			} else if (dip->i_di.di_depth < GFS2_DIR_MAX_DEPTH) {
+				brelse(bh);
+				dir_double_exhash(dip);
+				goto restart;
+
+			} else if (leaf->lf_next) {
+				leaf_no = be64_to_cpu(leaf->lf_next);
+				brelse(bh);
+				continue;
+
+			} else {
+				struct gfs2_meta_header mh;
+
+				bn = meta_alloc(dip);
+				nbh = bget(dip->i_sbd, bn);
+				mh.mh_magic = GFS2_MAGIC;
+				mh.mh_type = GFS2_METATYPE_LF;
+				mh.mh_format = GFS2_FORMAT_LF;
+				gfs2_meta_header_out(&mh, nbh);
+
+				leaf->lf_next = cpu_to_be64(bn);
+
+				nleaf = (struct gfs2_leaf *)nbh->b_data;
+				nleaf->lf_depth = leaf->lf_depth;
+				nleaf->lf_dirent_format = cpu_to_be32(GFS2_FORMAT_DE);
+
+				err = dirent_alloc(dip, nbh, len, &dent);
+				if (err)
+					return err;
+				dip->i_di.di_blocks++;
+				bmodified(dip->i_bh);
+				bmodified(bh);
+				brelse(bh);
+				bh = nbh;
+				leaf = nleaf;
+			}
+		}
+
+		gfs2_inum_out(inum, (char *)&dent->de_inum);
+		dent->de_hash = cpu_to_be32(hash);
+		dent->de_type = cpu_to_be16(type);
+		memcpy((char *)(dent + 1), filename, len);
+
+		leaf->lf_entries = be16_to_cpu(leaf->lf_entries) + 1;
+		leaf->lf_entries = cpu_to_be16(leaf->lf_entries);
+
+		bmodified(bh);
+		brelse(bh);
+		return err;
+	}
+}
+
+static void dir_make_exhash(struct gfs2_inode *dip)
+{
+	struct gfs2_sbd *sdp = dip->i_sbd;
+	struct gfs2_dirent *dent;
+	struct gfs2_buffer_head *bh;
+	struct gfs2_leaf *leaf;
+	int y;
+	uint32_t x;
+	uint64_t *lp, bn;
+
+	bn = meta_alloc(dip);
+	bh = bget(sdp, bn);
+	{
+		struct gfs2_meta_header mh;
+		mh.mh_magic = GFS2_MAGIC;
+		mh.mh_type = GFS2_METATYPE_LF;
+		mh.mh_format = GFS2_FORMAT_LF;
+		gfs2_meta_header_out(&mh, bh);
+	}
+
+	leaf = (struct gfs2_leaf *)bh->b_data;
+	leaf->lf_dirent_format = cpu_to_be32(GFS2_FORMAT_DE);
+	leaf->lf_entries = cpu_to_be16(dip->i_di.di_entries);
+
+	buffer_copy_tail(sdp, bh, sizeof(struct gfs2_leaf),
+			 dip->i_bh, sizeof(struct gfs2_dinode));
+
+	x = 0;
+	gfs2_dirent_first(dip, bh, &dent);
+
+	do {
+		if (!dent->de_inum.no_formal_ino)
+			continue;
+		if (++x == dip->i_di.di_entries)
+			break;
+	} while (gfs2_dirent_next(dip, bh, &dent) == 0);
+
+	dent->de_rec_len = be16_to_cpu(dent->de_rec_len);
+	dent->de_rec_len = cpu_to_be16(dent->de_rec_len +
+		sizeof(struct gfs2_dinode) - sizeof(struct gfs2_leaf));
+
+	/* no need to: bmodified(bh); (buffer_copy_tail does it) */
+	brelse(bh);
+
+	buffer_clear_tail(sdp, dip->i_bh, sizeof(struct gfs2_dinode));
+
+	lp = (uint64_t *)(dip->i_bh->b_data + sizeof(struct gfs2_dinode));
+
+	for (x = sdp->sd_hash_ptrs; x--; lp++)
+		*lp = cpu_to_be64(bn);
+
+	dip->i_di.di_size = sdp->bsize / 2;
+	dip->i_di.di_blocks++;
+	dip->i_di.di_flags |= GFS2_DIF_EXHASH;
+	dip->i_di.di_payload_format = 0;
+	/* no need: bmodified(dip->i_bh); buffer_clear_tail does it. */
+
+	for (x = sdp->sd_hash_ptrs, y = -1; x; x >>= 1, y++) ;
+	dip->i_di.di_depth = y;
+
+	gfs2_dinode_out(&dip->i_di, dip->i_bh);
+	bwrite(dip->i_bh);
+}
+
+static int dir_l_add(struct gfs2_inode *dip, const char *filename, int len,
+		      struct gfs2_inum *inum, unsigned int type)
+{
+	struct gfs2_dirent *dent;
+	int err = 0;
+
+	if (dirent_alloc(dip, dip->i_bh, len, &dent)) {
+		dir_make_exhash(dip);
+		err = dir_e_add(dip, filename, len, inum, type);
+		return err;
+	}
+
+	gfs2_inum_out(inum, (char *)&dent->de_inum);
+	dent->de_hash = gfs2_disk_hash(filename, len);
+	dent->de_hash = cpu_to_be32(dent->de_hash);
+	dent->de_type = cpu_to_be16(type);
+	memcpy((char *)(dent + 1), filename, len);
+	bmodified(dip->i_bh);
+	return err;
+}
+
+int dir_add(struct gfs2_inode *dip, const char *filename, int len,
+	     struct gfs2_inum *inum, unsigned int type)
+{
+	int err = 0;
+	if (dip->i_di.di_flags & GFS2_DIF_EXHASH)
+		err = dir_e_add(dip, filename, len, inum, type);
+	else
+		err = dir_l_add(dip, filename, len, inum, type);
+	return err;
+}
+
+static struct gfs2_buffer_head *__init_dinode(struct gfs2_sbd *sdp,
+					      struct gfs2_inum *inum,
+					      unsigned int mode,
+					      uint32_t flags,
+					      struct gfs2_inum *parent,
+					      int gfs1)
+{
+	struct gfs2_buffer_head *bh;
+	struct gfs2_dinode di;
+	int is_dir;
+
+	if (gfs1)
+		is_dir = (IF2DT(mode) == GFS_FILE_DIR);
+	else
+		is_dir = S_ISDIR(mode);
+	bh = bget(sdp, inum->no_addr);
+
+	memset(&di, 0, sizeof(struct gfs2_dinode));
+	di.di_header.mh_magic = GFS2_MAGIC;
+	di.di_header.mh_type = GFS2_METATYPE_DI;
+	di.di_header.mh_format = GFS2_FORMAT_DI;
+	di.di_num = *inum;
+	di.di_mode = mode;
+	di.di_nlink = 1;
+	di.di_blocks = 1;
+	di.di_atime = di.di_mtime = di.di_ctime = sdp->time;
+	di.di_goal_meta = di.di_goal_data = bh->b_blocknr;
+	di.di_flags = flags;
+
+	if (is_dir) {
+		struct gfs2_dirent de1, de2;
+
+		memset(&de1, 0, sizeof(struct gfs2_dirent));
+		de1.de_inum = di.di_num;
+		de1.de_hash = gfs2_disk_hash(".", 1);
+		de1.de_rec_len = GFS2_DIRENT_SIZE(1);
+		de1.de_name_len = 1;
+		de1.de_type = (gfs1 ? GFS_FILE_DIR : IF2DT(S_IFDIR));
+
+		memset(&de2, 0, sizeof(struct gfs2_dirent));
+		de2.de_inum = *parent;
+		de2.de_hash = gfs2_disk_hash("..", 2);
+		de2.de_rec_len = sdp->bsize - sizeof(struct gfs2_dinode) - de1.de_rec_len;
+		de2.de_name_len = 2;
+		de2.de_type = (gfs1 ? GFS_FILE_DIR : IF2DT(S_IFDIR));
+
+		gfs2_dirent_out(&de1, bh->b_data + sizeof(struct gfs2_dinode));
+		memcpy(bh->b_data +
+		       sizeof(struct gfs2_dinode) +
+		       sizeof(struct gfs2_dirent),
+		       ".", 1);
+		gfs2_dirent_out(&de2, bh->b_data + sizeof(struct gfs2_dinode) + de1.de_rec_len);
+		memcpy(bh->b_data +
+		       sizeof(struct gfs2_dinode) +
+		       de1.de_rec_len +
+		       sizeof(struct gfs2_dirent),
+		       "..", 2);
+
+		di.di_nlink = 2;
+		di.di_size = sdp->bsize - sizeof(struct gfs2_dinode);
+		di.di_flags |= GFS2_DIF_JDATA;
+		di.di_payload_format = GFS2_FORMAT_DE;
+		di.di_entries = 2;
+	}
+
+	gfs2_dinode_out(&di, bh);
+
+	return bh;
+}
+
+struct gfs2_buffer_head *init_dinode(struct gfs2_sbd *sdp,
+				     struct gfs2_inum *inum,
+				     unsigned int mode, uint32_t flags,
+				     struct gfs2_inum *parent)
+{
+	return __init_dinode(sdp, inum, mode, flags, parent, 0);
+}
+
+struct gfs2_buffer_head *init_gfs_dinode(struct gfs2_sbd *sdp,
+					 struct gfs2_inum *inum,
+					 unsigned int mode, uint32_t flags,
+					 struct gfs2_inum *parent)
+{
+	return __init_dinode(sdp, inum, mode, flags, parent, 1);
+}
+
+static struct gfs2_inode *__createi(struct gfs2_inode *dip,
+				    const char *filename, unsigned int mode,
+				    uint32_t flags, int if_gfs1)
+{
+	struct gfs2_sbd *sdp = dip->i_sbd;
+	uint64_t bn;
+	struct gfs2_inum inum;
+	struct gfs2_buffer_head *bh;
+	struct gfs2_inode *ip;
+	int err = 0;
+	int is_dir;
+
+	gfs2_lookupi(dip, filename, strlen(filename), &ip);
+	if (!ip) {
+		bn = dinode_alloc(sdp);
+
+		if (if_gfs1)
+			inum.no_formal_ino = bn;
+		else
+			inum.no_formal_ino = sdp->md.next_inum++;
+		inum.no_addr = bn;
+
+		err = dir_add(dip, filename, strlen(filename), &inum, IF2DT(mode));
+		if (err)
+			return NULL;
+
+		if (if_gfs1)
+			is_dir = (IF2DT(mode) == GFS_FILE_DIR);
+		else
+			is_dir = S_ISDIR(mode);
+		if (is_dir) {
+			bmodified(dip->i_bh);
+			dip->i_di.di_nlink++;
+		}
+
+		bh = __init_dinode(sdp, &inum, mode, flags, &dip->i_di.di_num,
+				   if_gfs1);
+		ip = inode_get(sdp, bh);
+		bmodified(bh);
+	}
+	ip->bh_owned = 1;
+	return ip;
+}
+
+struct gfs2_inode *createi(struct gfs2_inode *dip, const char *filename,
+			   unsigned int mode, uint32_t flags)
+{
+	return __createi(dip, filename, mode, flags, 0);
+}
+
+struct gfs2_inode *gfs_createi(struct gfs2_inode *dip, const char *filename,
+			   unsigned int mode, uint32_t flags)
+{
+	return __createi(dip, filename, mode, flags, 1);
+}
+
+/**
+ * gfs2_filecmp - Compare two filenames
+ * @file1: The first filename
+ * @file2: The second filename
+ * @len_of_file2: The length of the second file
+ *
+ * This routine compares two filenames and returns 1 if they are equal.
+ *
+ * Returns: 1 if the files are the same, otherwise 0.
+ */
+
+static int gfs2_filecmp(const char *file1, const char *file2, int len_of_file2)
+{
+	if (strlen(file1) != len_of_file2)
+		return 0;
+	if (memcmp(file1, file2, len_of_file2))
+		return 0;
+	return 1;
+}
+
+/**
+ * leaf_search
+ * @bh:
+ * @id:
+ * @dent_out:
+ * @dent_prev:
+ *
+ * Returns:
+ */
+static int leaf_search(struct gfs2_inode *dip, struct gfs2_buffer_head *bh,
+		       const char *filename, int len,
+		       struct gfs2_dirent **dent_out,
+		       struct gfs2_dirent **dent_prev)
+{
+	uint32_t hash;
+	struct gfs2_dirent *dent, *prev = NULL;
+	unsigned int entries = 0, x = 0;
+	int type;
+
+	type = gfs2_dirent_first(dip, bh, &dent);
+
+	if (type == IS_LEAF){
+		struct gfs2_leaf *leaf = (struct gfs2_leaf *)bh->b_data;
+		entries = be16_to_cpu(leaf->lf_entries);
+	} else if (type == IS_DINODE)
+		entries = dip->i_di.di_entries;
+	else
+		return -1;
+
+	hash = gfs2_disk_hash(filename, len);
+
+	do{
+		if (!dent->de_inum.no_formal_ino){
+			prev = dent;
+			continue;
+		}
+
+		if (be32_to_cpu(dent->de_hash) == hash &&
+			gfs2_filecmp(filename, (char *)(dent + 1),
+				     be16_to_cpu(dent->de_name_len))) {
+			*dent_out = dent;
+			if (dent_prev)
+				*dent_prev = prev;
+			return 0;
+		}
+
+		if(x >= entries)
+			return -1;
+		x++;
+		prev = dent;
+	} while (gfs2_dirent_next(dip, bh, &dent) == 0);
+
+	return -ENOENT;
+}
+
+/**
+ * linked_leaf_search - Linked leaf search
+ * @dip: The GFS2 inode
+ * @id:
+ * @dent_out:
+ * @dent_prev:
+ * @bh_out:
+ *
+ * Returns: 0 on sucess, error code otherwise
+ */
+
+static int linked_leaf_search(struct gfs2_inode *dip, const char *filename,
+			      int len, struct gfs2_dirent **dent_out,
+			      struct gfs2_buffer_head **bh_out)
+{
+	struct gfs2_buffer_head *bh = NULL, *bh_next;
+	uint32_t hsize, lindex;
+	uint32_t hash;
+	int error = 0;
+
+	hsize = 1 << dip->i_di.di_depth;
+	if(hsize * sizeof(uint64_t) != dip->i_di.di_size)
+		return -1;
+
+	/*  Figure out the address of the leaf node.  */
+
+	hash = gfs2_disk_hash(filename, len);
+	lindex = hash >> (32 - dip->i_di.di_depth);
+
+	error = get_first_leaf(dip, lindex, &bh_next);
+	if (error)
+		return error;
+	if (bh_next == NULL)
+		return errno;
+
+	/*  Find the entry  */
+	do{
+		if (bh && bh != dip->i_bh)
+			brelse(bh);
+
+		bh = bh_next;
+
+		error = leaf_search(dip, bh, filename, len, dent_out, NULL);
+		switch (error){
+		case 0:
+			*bh_out = bh;
+			return 0;
+
+		case -ENOENT:
+			break;
+
+		default:
+			if (bh && bh != dip->i_bh)
+				brelse(bh);
+			return error;
+		}
+
+		error = get_next_leaf(dip, bh, &bh_next);
+	} while (!error);
+
+	if (bh && bh != dip->i_bh)
+		brelse(bh);
+
+	return error;
+}
+
+/**
+ * dir_e_search -
+ * @dip: The GFS2 inode
+ * @id:
+ * @inode:
+ *
+ * Returns:
+ */
+static int dir_e_search(struct gfs2_inode *dip, const char *filename,
+			int len, unsigned int *type, struct gfs2_inum *inum)
+{
+	struct gfs2_buffer_head *bh = NULL;
+	struct gfs2_dirent *dent;
+	int error;
+
+	error = linked_leaf_search(dip, filename, len, &dent, &bh);
+	if (error)
+		return error;
+
+	gfs2_inum_in(inum, (char *)&dent->de_inum);
+	if (type)
+		*type = be16_to_cpu(dent->de_type);
+
+	brelse(bh);
+
+	return 0;
+}
+
+
+/**
+ * dir_l_search -
+ * @dip: The GFS2 inode
+ * @id:
+ * @inode:
+ *
+ * Returns:
+ */
+static int dir_l_search(struct gfs2_inode *dip, const char *filename,
+			int len, unsigned int *type, struct gfs2_inum *inum)
+{
+	struct gfs2_dirent *dent;
+	int error;
+
+	if(!inode_is_stuffed(dip))
+		return -1;
+
+	error = leaf_search(dip, dip->i_bh, filename, len, &dent, NULL);
+	if (!error) {
+		gfs2_inum_in(inum, (char *)&dent->de_inum);
+		if(type)
+			*type = be16_to_cpu(dent->de_type);
+	}
+	return error;
+}
+
+/**
+ * dir_search - Search a directory
+ * @dip: The GFS inode
+ * @id
+ * @type:
+ *
+ * This routine searches a directory for a file or another directory
+ * given its filename.  The component of the identifier that is
+ * not being used to search will be filled in and must be freed by
+ * the caller.
+ *
+ * Returns: 0 if found, -1 on failure, -ENOENT if not found.
+ */
+int dir_search(struct gfs2_inode *dip, const char *filename, int len,
+		      unsigned int *type, struct gfs2_inum *inum)
+{
+	int error;
+
+	if(!S_ISDIR(dip->i_di.di_mode) && !is_gfs_dir(&dip->i_di))
+		return -1;
+
+	if (dip->i_di.di_flags & GFS2_DIF_EXHASH)
+		error = dir_e_search(dip, filename, len, type, inum);
+	else
+		error = dir_l_search(dip, filename, len, type, inum);
+
+	return error;
+}
+
+static int dir_e_del(struct gfs2_inode *dip, const char *filename, int len)
+{
+	int lindex;
+	int error;
+	int found = 0;
+	uint64_t leaf_no;
+	struct gfs2_buffer_head *bh = NULL;
+	struct gfs2_dirent *cur, *prev;
+
+	lindex = (1 << (dip->i_di.di_depth))-1;
+
+	for(; (lindex >= 0) && !found; lindex--){
+		gfs2_get_leaf_nr(dip, lindex, &leaf_no);
+
+		while(leaf_no && !found){
+			bh = bread(dip->i_sbd, leaf_no);
+			error = leaf_search(dip, bh, filename, len, &cur, &prev);
+			if (error) {
+				if(error != -ENOENT){
+					brelse(bh);
+					return -1;
+				}
+				leaf_no = be64_to_cpu(((struct gfs2_leaf *)bh->b_data)->lf_next);
+				brelse(bh);
+			} else
+				found = 1;
+		}
+	}
+
+	if(!found)
+		return 1;
+
+	if (bh) {
+		dirent2_del(dip, bh, prev, cur);
+		brelse(bh);
+	}
+	return 0;
+}
+
+static int dir_l_del(struct gfs2_inode *dip, const char *filename, int len)
+{
+	int error=0;
+	struct gfs2_dirent *cur, *prev;
+
+	if(!inode_is_stuffed(dip))
+		return -1;
+
+	error = leaf_search(dip, dip->i_bh, filename, len, &cur, &prev);
+	if (error) {
+		if (error == -ENOENT)
+			return 1;
+		else
+			return -1;
+	}
+
+	dirent2_del(dip, dip->i_bh, prev, cur);
+	return 0;
+}
+
+
+/*
+ * gfs2_dirent_del
+ * @dip
+ * filename
+ *
+ * Delete a directory entry from a directory.  This _only_
+ * removes the directory entry - leaving the dinode in
+ * place.  (Likely without a link.)
+ *
+ * Returns: 0 on success (or if it doesn't already exist), -1 on failure
+ */
+int gfs2_dirent_del(struct gfs2_inode *dip, const char *filename, int len)
+{
+	int error;
+
+	if(!S_ISDIR(dip->i_di.di_mode) && !is_gfs_dir(&dip->i_di))
+		return -1;
+
+	if (dip->i_di.di_flags & GFS2_DIF_EXHASH)
+		error = dir_e_del(dip, filename, len);
+	else
+		error = dir_l_del(dip, filename, len);
+	bmodified(dip->i_bh);
+	return error;
+}
+
+/**
+ * gfs2_lookupi - Look up a filename in a directory and return its inode
+ * @dip: The directory to search
+ * @name: The name of the inode to look for
+ * @ipp: Used to return the found inode if any
+ *
+ * Returns: 0 on success, -EXXXX on failure
+ */
+int gfs2_lookupi(struct gfs2_inode *dip, const char *filename, int len,
+		 struct gfs2_inode **ipp)
+{
+	struct gfs2_sbd *sdp = dip->i_sbd;
+	int error = 0;
+	struct gfs2_inum inum;
+
+	*ipp = NULL;
+
+	if (!len || len > GFS2_FNAMESIZE)
+		return -ENAMETOOLONG;
+	if (gfs2_filecmp(filename, (char *)".", 1)) {
+		*ipp = dip;
+		return 0;
+	}
+	error = dir_search(dip, filename, len, NULL, &inum);
+	if (error) {
+		if (error == -ENOENT)
+			return 0;
+	}
+	else
+		*ipp = inode_read(sdp, inum.no_addr);
+
+	return error;
+}
+
+/**
+ * gfs2_free_block - free up a block given its block number
+ */
+void gfs2_free_block(struct gfs2_sbd *sdp, uint64_t block)
+{
+	struct rgrp_tree *rgd;
+
+	/* Adjust the free space count for the freed block */
+	rgd = gfs2_blk2rgrpd(sdp, block); /* find the rg for indir block */
+	if (rgd) {
+		gfs2_set_bitmap(sdp, block, GFS2_BLKST_FREE);
+		rgd->rg.rg_free++; /* adjust the free count */
+		if (sdp->gfs1)
+			gfs_rgrp_out((struct gfs_rgrp *)&rgd->rg, rgd->bh[0]);
+		else
+			gfs2_rgrp_out(&rgd->rg, rgd->bh[0]);
+		sdp->blks_alloced--;
+	}
+}
+
+/**
+ * gfs2_freedi - unlink a disk inode by block number.
+ * Note: currently only works for regular files.
+ */
+int gfs2_freedi(struct gfs2_sbd *sdp, uint64_t diblock)
+{
+	struct gfs2_inode *ip;
+	struct gfs2_buffer_head *bh, *nbh;
+	int h, head_size;
+	uint64_t *ptr, block;
+	struct rgrp_tree *rgd;
+	uint32_t height;
+	osi_list_t metalist[GFS2_MAX_META_HEIGHT];
+	osi_list_t *cur_list, *next_list, *tmp;
+
+	for (h = 0; h < GFS2_MAX_META_HEIGHT; h++)
+		osi_list_init(&metalist[h]);
+
+	bh = bread(sdp, diblock);
+	ip = inode_get(sdp, bh);
+	height = ip->i_di.di_height;
+	osi_list_add(&bh->b_altlist, &metalist[0]);
+
+	for (h = 0; h < height; h++){
+		cur_list = &metalist[h];
+		next_list = &metalist[h + 1];
+		head_size = (h > 0 ? sizeof(struct gfs2_meta_header) :
+			     sizeof(struct gfs2_dinode));
+
+		for (tmp = cur_list->next; tmp != cur_list; tmp = tmp->next){
+			bh = osi_list_entry(tmp, struct gfs2_buffer_head,
+					    b_altlist);
+
+			for (ptr = (uint64_t *)(bh->b_data + head_size);
+			     (char *)ptr < (bh->b_data + sdp->bsize); ptr++) {
+				if (!*ptr)
+					continue;
+
+				block = be64_to_cpu(*ptr);
+				gfs2_free_block(sdp, block);
+				if (h == height - 1) /* if not metadata */
+					continue; /* don't queue it up */
+				/* Read the next metadata block in the chain */
+				nbh = bread(sdp, block);
+				osi_list_add(&nbh->b_altlist, next_list);
+				brelse(nbh);
+			}
+		}
+	}
+	/* Set the bitmap type for inode to free space: */
+	gfs2_set_bitmap(sdp, ip->i_di.di_num.no_addr, GFS2_BLKST_FREE);
+	inode_put(&ip);
+	/* inode_put deallocated the extra block used by the disk inode, */
+	/* so adjust it in the superblock struct */
+	sdp->blks_alloced--;
+	/* Now we have to adjust the rg freespace count and inode count: */
+	rgd = gfs2_blk2rgrpd(sdp, diblock);
+	rgd->rg.rg_free++;
+	rgd->rg.rg_dinodes--; /* one less inode in use */
+	if (sdp->gfs1)
+		gfs_rgrp_out((struct gfs_rgrp *)&rgd->rg, rgd->bh[0]);
+	else
+		gfs2_rgrp_out(&rgd->rg, rgd->bh[0]);
+	sdp->dinodes_alloced--;
+	return 0;
+}
diff --git a/gfs2/fsck/fs_recovery.c b/gfs2/fsck/fs_recovery.c
index da80f5c..2e902ea 100644
--- a/gfs2/fsck/fs_recovery.c
+++ b/gfs2/fsck/fs_recovery.c
@@ -1,20 +1,5 @@
-/******************************************************************************
-*******************************************************************************
-**
-**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
-**  Copyright (C) 2004-2005 Red Hat, Inc.  All rights reserved.
-**
-**  This copyrighted material is made available to anyone wishing to use,
-**  modify, copy, or redistribute it subject to the terms and conditions
-**  of the GNU General Public License v.2.
-**
-*******************************************************************************
-******************************************************************************/
-
 #include <errno.h>
 #include <inttypes.h>
-#include <linux_endian.h>
-#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <time.h>
@@ -25,9 +10,9 @@
 #include "fsck.h"
 #include "fs_recovery.h"
 #include "libgfs2.h"
+#include "metawalk.h"
 #include "util.h"
 
-#define RANDOM(values) ((values) * (random() / (RAND_MAX + 1.0)))
 #define JOURNAL_NAME_SIZE 16
 
 unsigned int sd_found_jblocks = 0, sd_replayed_jblocks = 0;
@@ -127,7 +112,8 @@ static int buf_lo_scan_elements(struct gfs2_inode *ip, unsigned int start,
 
 		sd_found_metablocks++;
 
-		blkno = be64_to_cpu(*ptr++);
+		blkno = be64_to_cpu(*ptr);
+		ptr++;
 		if (gfs2_revoke_check(sdp, blkno, start))
 			continue;
 
@@ -233,8 +219,10 @@ static int databuf_lo_scan_elements(struct gfs2_inode *ip, unsigned int start,
 
 	gfs2_replay_incr_blk(ip, &start);
 	for (; blks; gfs2_replay_incr_blk(ip, &start), blks--) {
-		blkno = be64_to_cpu(*ptr++);
-		esc = be64_to_cpu(*ptr++);
+		blkno = be64_to_cpu(*ptr);
+		ptr++;
+		esc = be64_to_cpu(*ptr);
+		ptr++;
 
 		sd_found_jblocks++;
 
@@ -572,6 +560,63 @@ reinit:
 	return error;
 }
 
+/* We can't use the rangecheck function from pass1 because we haven't gone
+ * through initialization properly yet. */
+static int rangecheck_jblock(struct gfs2_inode *ip, uint64_t block)
+{
+	if((block > ip->i_sbd->fssize) || (block <= ip->i_sbd->sb_addr)) {
+		log_info( _("Bad block pointer (out of range) found in "
+			    "journal inode %lld (0x%llx).\n"),
+			  (unsigned long long)ip->i_di.di_num.no_addr,
+			  (unsigned long long)ip->i_di.di_num.no_addr);
+		return meta_error; /* Exits check_metatree quicker */
+	}
+	return meta_is_good;
+}
+
+static int rangecheck_jmeta(struct gfs2_inode *ip, uint64_t block,
+			       struct gfs2_buffer_head **bh, int h,
+			       int *is_valid, int *was_duplicate,
+			       void *private)
+{
+	int rc;
+
+	*bh = NULL;
+	*was_duplicate = 0;
+	*is_valid = 0;
+	rc = rangecheck_jblock(ip, block);
+	if (rc == meta_is_good) {
+		*bh = bread(ip->i_sbd, block);
+		*is_valid = (gfs2_check_meta(*bh, GFS2_METATYPE_IN) == 0);
+		if (!(*is_valid)) {
+			log_err( _("Journal at block %lld (0x%llx) has a bad "
+				   "indirect block pointer %lld (0x%llx) "
+				   "(points to something that is not an "
+				   "indirect block).\n"),
+				 (unsigned long long)ip->i_di.di_num.no_addr,
+				 (unsigned long long)ip->i_di.di_num.no_addr,
+				 (unsigned long long)block,
+				 (unsigned long long)block);
+			brelse(*bh);
+			return meta_skip_further;
+		}
+	}
+	return rc;
+}
+
+static int rangecheck_jdata(struct gfs2_inode *ip, uint64_t metablock,
+			   uint64_t block, void *private)
+{
+	return rangecheck_jblock(ip, block);
+}
+
+struct metawalk_fxns rangecheck_journal = {
+	.private = NULL,
+	.invalid_meta_is_fatal = 1,
+	.check_metalist = rangecheck_jmeta,
+	.check_data = rangecheck_jdata,
+};
+
 /*
  * replay_journals - replay the journals
  * sdp: the super block
@@ -596,9 +641,39 @@ int replay_journals(struct gfs2_sbd *sdp, int preen, int force_check,
 	sdp->jsize = GFS2_DEFAULT_JSIZE;
 
 	for(i = 0; i < sdp->md.journals; i++) {
+		if (sdp->md.journal[i]) {
+			struct rgrp_tree rgd;
+			struct gfs2_bitmap bits;
+
+			/* The real rgrp tree hasn't been built at this point,
+			 * so we need to dummy one up that covers the whole
+			 * file system so basic functions in check_metatree
+			 * don't segfault. */
+			rgd.start = sdp->sb_addr + 1;
+			rgd.length = 1;
+			rgd.bh = NULL;
+			bits.bi_start = 0;
+			bits.bi_len = sdp->fssize / GFS2_NBBY;
+			rgd.bits = &bits;
+			rgd.ri.ri_addr = sdp->sb_addr + 1;
+			rgd.ri.ri_length = 1;
+			rgd.ri.ri_data0 = sdp->sb_addr + 2;
+			rgd.ri.ri_data = sdp->fssize - (sdp->sb_addr + 2);
+
+			sdp->rgtree.osi_node = (struct osi_node *)&rgd;
+			error = check_metatree(sdp->md.journal[i],
+					       &rangecheck_journal);
+			sdp->rgtree.osi_node = NULL;
+			if (error)
+				/* Don't use fsck_inode_put here because it's a
+				   system file and we need to dismantle it. */
+				inode_put(&sdp->md.journal[i]);
+			error = 0; /* bad journal is non-fatal */
+		}
 		if (!sdp->md.journal[i]) {
 			log_err(_("File system journal \"journal%d\" is "
-				  "missing: pass1 will try to recreate it.\n"),
+				  "missing or corrupt: pass1 will try to "
+				  "recreate it.\n"),
 				i);
 			continue;
 		}
@@ -641,7 +716,9 @@ int ji_update(struct gfs2_sbd *sdp)
 {
 	struct gfs2_inode *jip, *ip = sdp->md.jiinode;
 	char journal_name[JOURNAL_NAME_SIZE];
-	int i;
+	int i, error;
+	char buf[sizeof(struct gfs_jindex)];
+	struct gfs_jindex ji;
 
 	if (!ip) {
 		log_crit("Journal index inode not found.\n");
@@ -652,24 +729,41 @@ int ji_update(struct gfs2_sbd *sdp)
 	   plus two for "." and "..".  So we subtract the 2 and divide by 3.
 	   If per_node is missing or damaged, we have to trust jindex has
 	   the correct number of entries. */
-	if (sdp->md.pinode) /* if per_node was read in properly */
+	if (sdp->gfs1)
+		sdp->md.journals = ip->i_di.di_size / sizeof(struct gfs_jindex);
+	else if (sdp->md.pinode) /* if per_node was read in properly */
 		sdp->md.journals = (sdp->md.pinode->i_di.di_entries - 2) / 3;
 	else
 		sdp->md.journals = ip->i_di.di_entries - 2;
 
 	if (!(sdp->md.journal = calloc(sdp->md.journals,
-				      sizeof(struct gfs2_inode *)))) {
+				       sizeof(struct gfs2_inode *)))) {
 		log_err("Unable to allocate journal index\n");
 		return -1;
 	}
 	memset(journal_name, 0, sizeof(*journal_name));
 	for (i = 0; i < sdp->md.journals; i++) {
-		/* FIXME check snprintf return code */
-		snprintf(journal_name, JOURNAL_NAME_SIZE, "journal%u", i);
-		gfs2_lookupi(sdp->md.jiinode, journal_name, strlen(journal_name),
-					 &jip);
-		sdp->md.journal[i] = jip;
+		if (sdp->gfs1) {
+			error = gfs2_readi(ip,
+					   buf, i * sizeof(struct gfs_jindex),
+					   sizeof(struct gfs_jindex));
+			if (!error)
+				break;
+			if (error != sizeof(struct gfs_jindex)){
+				log_err("An error occurred while reading the"
+					" journal index file.\n");
+				return -1;
+			}
+			gfs_jindex_in(&ji, buf);
+			sdp->md.journal[i] = inode_read(sdp, ji.ji_addr);
+		} else {
+			/* FIXME check snprintf return code */
+			snprintf(journal_name, JOURNAL_NAME_SIZE,
+				 "journal%u", i);
+			gfs2_lookupi(sdp->md.jiinode, journal_name,
+				     strlen(journal_name), &jip);
+			sdp->md.journal[i] = jip;
+		}
 	}
 	return 0;
-
 }
diff --git a/gfs2/fsck/fs_recovery.h b/gfs2/fsck/fs_recovery.h
index 8606712..1a7aae3 100644
--- a/gfs2/fsck/fs_recovery.h
+++ b/gfs2/fsck/fs_recovery.h
@@ -1,16 +1,3 @@
-/******************************************************************************
-*******************************************************************************
-**
-**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
-**  Copyright (C) 2004-2005 Red Hat, Inc.  All rights reserved.
-**
-**  This copyrighted material is made available to anyone wishing to use,
-**  modify, copy, or redistribute it subject to the terms and conditions
-**  of the GNU General Public License v.2.
-**
-*******************************************************************************
-******************************************************************************/
-
 #ifndef __FS_RECOVERY_H__
 #define __FS_RECOVERY_H__
 
diff --git a/gfs2/fsck/fsck.h b/gfs2/fsck/fsck.h
index b571d4a..ddabf79 100644
--- a/gfs2/fsck/fsck.h
+++ b/gfs2/fsck/fsck.h
@@ -1,15 +1,3 @@
-/*****************************************************************************
-*******************************************************************************
-**
-**  Copyright (C) 2005-2007 Red Hat, Inc.  All rights reserved.
-**
-**  This copyrighted material is made available to anyone wishing to use,
-**  modify, copy, or redistribute it subject to the terms and conditions
-**  of the GNU General Public License v.2.
-**
-*******************************************************************************
-******************************************************************************/
-
 #ifndef _FSCK_H
 #define _FSCK_H
 
@@ -99,9 +87,12 @@ enum rgindex_trust_level { /* how far can we trust our RG index? */
 	open_minded = 2, /* At least 1 RG is corrupt. Try to calculate what it
 			    should be, in a perfect world where our RGs are all
 			    on even boundaries. Blue sky. Chirping birds. */
-	distrust = 3   /* The world isn't perfect, our RGs are not on nice neat
+	distrust = 3,  /* The world isn't perfect, our RGs are not on nice neat
 			  boundaries.  The fs must have been messed with by
 			  gfs2_grow or something.  Count the RGs by hand. */
+	indignation = 4 /* Not only do we have corruption, but the rgrps
+			   aren't on even boundaries, so this file system
+			   must have been converted from gfs2_convert. */
 };
 
 extern struct gfs2_inode *fsck_load_inode(struct gfs2_sbd *sdp, uint64_t block);
@@ -121,11 +112,10 @@ extern int pass4(struct gfs2_sbd *sdp);
 extern int pass5(struct gfs2_sbd *sdp);
 extern int rg_repair(struct gfs2_sbd *sdp, int trust_lvl, int *rg_count,
 		     int *sane);
-extern void gfs2_dup_free(void);
 extern int fsck_query(const char *format, ...)
 	__attribute__((format(printf,1,2)));
 extern struct dir_info *dirtree_find(uint64_t block);
-extern void dup_delete(struct duptree *b);
+extern void dup_delete(struct duptree *dt);
 extern void dirtree_delete(struct dir_info *b);
 
 /* FIXME: Hack to get this going for pass2 - this should be pulled out
@@ -134,6 +124,7 @@ struct dir_info *dirtree_insert(struct gfs2_inum inum);
 
 extern struct gfs2_options opts;
 extern struct gfs2_inode *lf_dip; /* Lost and found directory inode */
+extern int lf_was_created;
 extern struct gfs2_bmap *bl;
 extern uint64_t last_fs_block, last_reported_block;
 extern int64_t last_reported_fblock;
@@ -147,4 +138,6 @@ extern struct osi_root inodetree;
 extern int dups_found; /* How many duplicate references have we found? */
 extern int dups_found_first; /* How many duplicates have we found the original
 				reference for? */
+extern struct gfs_sb *sbd1;
+
 #endif /* _FSCK_H */
diff --git a/gfs2/fsck/gfs1.c b/gfs2/fsck/gfs1.c
new file mode 100644
index 0000000..d1e8ab8
--- /dev/null
+++ b/gfs2/fsck/gfs1.c
@@ -0,0 +1,429 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <linux/types.h>
+#include <linux/gfs2_ondisk.h>
+
+#include "osi_list.h"
+#include "libgfs2.h"
+
+/* GFS1 compatibility functions - so that programs like gfs2_convert
+   and gfs2_edit can examine/manipulate GFS1 file systems. */
+
+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];
+}
+
+int is_gfs_dir(struct gfs2_dinode *dinode)
+{
+	if (dinode->__pad1 == GFS_FILE_DIR)
+		return 1;
+	return 0;
+}
+
+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);
+	bmodified(bh);
+	ip->i_di.di_blocks++;
+	bmodified(ip->i_bh);
+
+	*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;
+
+	*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 = ip->i_bh;
+
+	for (x = 0; x < end_of_metadata; x++) {
+		gfs1_lookup_block(ip, bh, x, mp, create, new, dblock);
+		if (bh != ip->i_bh)
+			brelse(bh);
+		if (!*dblock)
+			goto out;
+
+		if (*new) {
+			struct gfs2_meta_header mh;
+
+			bh = bget(sdp, *dblock);
+			mh.mh_magic = GFS2_MAGIC;
+			mh.mh_type = GFS2_METATYPE_IN;
+			mh.mh_format = GFS2_FORMAT_IN;
+			gfs2_meta_header_out(&mh, bh);
+		} else {
+			if (*dblock == ip->i_di.di_num.no_addr)
+				bh = ip->i_bh;
+			else
+				bh = bread(sdp, *dblock);
+		}
+	}
+
+	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)++;
+			}
+		}
+	}
+
+	if (bh != ip->i_bh)
+		brelse(bh);
+
+ out:
+	free(mp);
+}
+
+int gfs1_writei(struct gfs2_inode *ip, char *buf, uint64_t offset,
+		unsigned int size)
+{
+	struct gfs2_sbd *sdp = ip->i_sbd;
+	struct gfs2_buffer_head *bh;
+	uint64_t lblock, dblock;
+	uint32_t extlen = 0;
+	unsigned int amount;
+	int new;
+	int journaled = fs_is_jdata(ip);
+	const uint64_t start = offset;
+	int copied = 0;
+	int error = 0;
+
+	if (!size)
+		goto fail;  /*  Not really an error  */
+
+
+	if (!ip->i_di.di_height && /* stuffed */
+	    ((start + size) > (sdp->bsize - sizeof(struct gfs_dinode))))
+		unstuff_dinode(ip);
+
+	if (journaled) {
+		lblock = offset / sdp->sd_jbsize;
+		offset %= sdp->sd_jbsize;
+	} else {
+		lblock = offset >> sdp->sd_sb.sb_bsize_shift;
+		offset &= sdp->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){
+			new = TRUE;
+			gfs1_block_map(ip, lblock, &new, &dblock, &extlen, 0);
+			if (!dblock)
+				return -1;
+		}
+
+		if (dblock == ip->i_di.di_num.no_addr)
+			bh = ip->i_bh;
+		else
+			bh = bread(sdp, dblock);
+
+		if (journaled && dblock != ip->i_di.di_num.no_addr ) {
+			struct gfs2_meta_header mh;
+
+			mh.mh_magic = GFS2_MAGIC;
+			mh.mh_type = GFS2_METATYPE_JD;
+			mh.mh_format = GFS2_FORMAT_JD;
+			gfs2_meta_header_out(&mh, bh);
+		}
+
+		memcpy(bh->b_data + offset, buf + copied, amount);
+		bmodified(bh);
+		if (bh != ip->i_bh)
+			brelse(bh);
+
+		copied += amount;
+		lblock++;
+		dblock++;
+		extlen--;
+
+		offset = (journaled) ? sizeof(struct gfs2_meta_header) : 0;
+	}
+
+ out:
+	if (ip->i_di.di_size < start + copied) {
+		bmodified(ip->i_bh);
+		ip->i_di.di_size = start + copied;
+	}
+	ip->i_di.di_mtime = ip->i_di.di_ctime = time(NULL);
+
+	gfs2_dinode_out(&ip->i_di, ip->i_bh);
+
+	return copied;
+
+ fail:
+	if (copied)
+		goto out;
+
+	return error;
+}
+
+/* ------------------------------------------------------------------------ */
+/* gfs_dinode_in */
+/* ------------------------------------------------------------------------ */
+static void gfs_dinode_in(struct gfs_dinode *di, struct gfs2_buffer_head *bh)
+{
+	struct gfs_dinode *str = (struct gfs_dinode *)bh->b_data;
+
+	gfs2_meta_header_in(&di->di_header, bh);
+	gfs2_inum_in(&di->di_num, (char *)&str->di_num);
+
+	di->di_mode = be32_to_cpu(str->di_mode);
+	di->di_uid = be32_to_cpu(str->di_uid);
+	di->di_gid = be32_to_cpu(str->di_gid);
+	di->di_nlink = be32_to_cpu(str->di_nlink);
+	di->di_size = be64_to_cpu(str->di_size);
+	di->di_blocks = be64_to_cpu(str->di_blocks);
+	di->di_atime = be64_to_cpu(str->di_atime);
+	di->di_mtime = be64_to_cpu(str->di_mtime);
+	di->di_ctime = be64_to_cpu(str->di_ctime);
+	di->di_major = be32_to_cpu(str->di_major);
+	di->di_minor = be32_to_cpu(str->di_minor);
+	di->di_goal_dblk = be64_to_cpu(str->di_goal_dblk);
+	di->di_goal_mblk = be64_to_cpu(str->di_goal_mblk);
+	di->di_flags = be32_to_cpu(str->di_flags);
+	di->di_payload_format = be32_to_cpu(str->di_payload_format);
+	di->di_type = be16_to_cpu(str->di_type);
+	di->di_height = be16_to_cpu(str->di_height);
+	di->di_depth = be16_to_cpu(str->di_depth);
+	di->di_entries = be32_to_cpu(str->di_entries);
+	di->di_eattr = be64_to_cpu(str->di_eattr);
+}
+
+static struct gfs2_inode *__gfs_inode_get(struct gfs2_sbd *sdp,
+					  struct gfs2_buffer_head *bh,
+					  uint64_t di_addr)
+{
+	struct gfs_dinode gfs1_dinode;
+	struct gfs2_inode *ip;
+
+	ip = calloc(1, sizeof(struct gfs2_inode));
+	if (ip == NULL) {
+		fprintf(stderr, "Out of memory in %s\n", __FUNCTION__);
+		exit(-1);
+	}
+
+	ip->bh_owned = 0;
+	if (!bh) {
+		bh = bread(sdp, di_addr);
+		ip->bh_owned = 1;
+	}
+	gfs_dinode_in(&gfs1_dinode, bh);
+	memcpy(&ip->i_di.di_header, &gfs1_dinode.di_header,
+	       sizeof(struct gfs2_meta_header));
+	memcpy(&ip->i_di.di_num, &gfs1_dinode.di_num,
+	       sizeof(struct gfs2_inum));
+	ip->i_di.di_mode = gfs1_dinode.di_mode;
+	ip->i_di.di_uid = gfs1_dinode.di_uid;
+	ip->i_di.di_gid = gfs1_dinode.di_gid;
+	ip->i_di.di_nlink = gfs1_dinode.di_nlink;
+	ip->i_di.di_size = gfs1_dinode.di_size;
+	ip->i_di.di_blocks = gfs1_dinode.di_blocks;
+	ip->i_di.di_atime = gfs1_dinode.di_atime;
+	ip->i_di.di_mtime = gfs1_dinode.di_mtime;
+	ip->i_di.di_ctime = gfs1_dinode.di_ctime;
+	ip->i_di.di_major = gfs1_dinode.di_major;
+	ip->i_di.di_minor = gfs1_dinode.di_minor;
+	ip->i_di.di_goal_data = gfs1_dinode.di_goal_dblk;
+	ip->i_di.di_goal_meta = gfs1_dinode.di_goal_mblk;
+	ip->i_di.di_flags = gfs1_dinode.di_flags;
+	ip->i_di.di_payload_format = gfs1_dinode.di_payload_format;
+	ip->i_di.__pad1 = gfs1_dinode.di_type;
+	ip->i_di.di_height = gfs1_dinode.di_height;
+	ip->i_di.di_depth = gfs1_dinode.di_depth;
+	ip->i_di.di_entries = gfs1_dinode.di_entries;
+	ip->i_di.di_eattr = gfs1_dinode.di_eattr;
+	ip->i_bh = bh;
+	ip->i_sbd = sdp;
+	return ip;
+}
+
+struct gfs2_inode *gfs_inode_get(struct gfs2_sbd *sdp,
+				 struct gfs2_buffer_head *bh)
+{
+	return __gfs_inode_get(sdp, bh, 0);
+}
+
+struct gfs2_inode *gfs_inode_read(struct gfs2_sbd *sdp, uint64_t di_addr)
+{
+	return __gfs_inode_get(sdp, NULL, di_addr);
+}
+
+/* ------------------------------------------------------------------------ */
+/* gfs_jindex_in - read in a gfs1 jindex structure.                         */
+/* ------------------------------------------------------------------------ */
+void gfs_jindex_in(struct gfs_jindex *jindex, char *jbuf)
+{
+	struct gfs_jindex *str = (struct gfs_jindex *) jbuf;
+
+	jindex->ji_addr = be64_to_cpu(str->ji_addr);
+	jindex->ji_nsegment = be32_to_cpu(str->ji_nsegment);
+	jindex->ji_pad = be32_to_cpu(str->ji_pad);
+	memcpy(jindex->ji_reserved, str->ji_reserved, 64);
+}
+
+/* ------------------------------------------------------------------------ */
+/* gfs_rgrp_in - Read in a resource group header                            */
+/* ------------------------------------------------------------------------ */
+void gfs_rgrp_in(struct gfs_rgrp *rgrp, struct gfs2_buffer_head *rbh)
+{
+	struct gfs_rgrp *str = (struct gfs_rgrp *)rbh->b_data;
+
+	gfs2_meta_header_in(&rgrp->rg_header, rbh);
+	rgrp->rg_flags = be32_to_cpu(str->rg_flags);
+	rgrp->rg_free = be32_to_cpu(str->rg_free);
+	rgrp->rg_useddi = be32_to_cpu(str->rg_useddi);
+	rgrp->rg_freedi = be32_to_cpu(str->rg_freedi);
+	gfs2_inum_in(&rgrp->rg_freedi_list, (char *)&str->rg_freedi_list);
+	rgrp->rg_usedmeta = be32_to_cpu(str->rg_usedmeta);
+	rgrp->rg_freemeta = be32_to_cpu(str->rg_freemeta);
+
+	memcpy(rgrp->rg_reserved, str->rg_reserved, 64);
+}
+
+/* ------------------------------------------------------------------------ */
+/* gfs_rgrp_out */
+/* ------------------------------------------------------------------------ */
+void gfs_rgrp_out(struct gfs_rgrp *rgrp, struct gfs2_buffer_head *rbh)
+{
+	struct gfs_rgrp *str = (struct gfs_rgrp *)rbh->b_data;
+
+	gfs2_meta_header_out(&rgrp->rg_header, rbh);
+	str->rg_flags = cpu_to_be32(rgrp->rg_flags);
+	str->rg_free = cpu_to_be32(rgrp->rg_free);
+	str->rg_useddi = cpu_to_be32(rgrp->rg_useddi);
+	str->rg_freedi = cpu_to_be32(rgrp->rg_freedi);
+	gfs2_inum_out(&rgrp->rg_freedi_list, (char *)&str->rg_freedi_list);
+	str->rg_usedmeta = cpu_to_be32(rgrp->rg_usedmeta);
+	str->rg_freemeta = cpu_to_be32(rgrp->rg_freemeta);
+
+	memcpy(str->rg_reserved, rgrp->rg_reserved, 64);
+	bmodified(rbh);
+}
+
+void gfs_get_leaf_nr(struct gfs2_inode *dip, uint32_t lindex,
+		     uint64_t *leaf_out)
+{
+	uint64_t leaf_no;
+	int count;
+
+	count = gfs2_readi(dip, (char *)&leaf_no, lindex * sizeof(uint64_t),
+			   sizeof(uint64_t));
+	if (count != sizeof(uint64_t))
+		die("gfs_get_leaf_nr:  Bad internal read.\n");
+
+	*leaf_out = be64_to_cpu(leaf_no);
+}
+
+void gfs_put_leaf_nr(struct gfs2_inode *dip, uint32_t inx, uint64_t leaf_out)
+{
+	uint64_t leaf_no;
+	int count;
+
+	leaf_no = cpu_to_be64(leaf_out);
+	count = gfs1_writei(dip, (char *)&leaf_no, inx * sizeof(uint64_t),
+			   sizeof(uint64_t));
+	if (count != sizeof(uint64_t))
+		die("gfs_put_leaf_nr:  Bad internal write.\n");
+}
diff --git a/gfs2/fsck/gfs2_disk_hash.c b/gfs2/fsck/gfs2_disk_hash.c
new file mode 100644
index 0000000..5da6103
--- /dev/null
+++ b/gfs2/fsck/gfs2_disk_hash.c
@@ -0,0 +1,72 @@
+#include <stdio.h>
+#include "libgfs2.h"
+
+static const uint32_t crc_32_tab[] =
+{
+  0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
+  0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
+  0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+  0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
+  0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
+  0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+  0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
+  0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
+  0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+  0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
+  0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
+  0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+  0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
+  0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
+  0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+  0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
+  0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
+  0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+  0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
+  0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
+  0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+  0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
+  0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
+  0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+  0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
+  0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
+  0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+  0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
+  0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
+  0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+  0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
+  0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
+};
+
+/**
+ * gfs2_disk_hash - hash an array of data
+ * @data: the data to be hashed
+ * @len: the length of data to be hashed
+ *
+ * This function must produce the same results as the one in the kernel:
+ *   crc32_le(0xFFFFFFFF, data, len) ^ 0xFFFFFFFF
+ *
+ * Take some data and convert it to a 32-bit hash.
+ *
+ * The hash function is a 32-bit CRC of the data.  The algorithm uses
+ * the crc_32_tab table above.
+ *
+ * This may not be the fastest hash function, but it does a fair bit better
+ * at providing uniform results than the others I've looked at.  That's
+ * really important for efficient directories.
+ *
+ * Returns: the hash
+ */
+
+uint32_t gfs2_disk_hash(const char *data, int len)
+{
+	uint32_t hash = 0xFFFFFFFF;
+
+	for (; len--; data++)
+		hash = crc_32_tab[(hash ^ *data) & 0xFF] ^ (hash >> 8);
+
+	hash = ~hash;
+
+	return hash;
+}
+
+
diff --git a/gfs2/fsck/gfs2_log.c b/gfs2/fsck/gfs2_log.c
new file mode 100644
index 0000000..53a3027
--- /dev/null
+++ b/gfs2/fsck/gfs2_log.c
@@ -0,0 +1,192 @@
+#include <stdio.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <libintl.h>
+#include <sys/select.h>
+#include <signal.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "libgfs2.h"
+
+#define _(String) gettext(String)
+
+int print_level = MSG_NOTICE;
+
+void increase_verbosity(void)
+{
+	print_level++;
+}
+
+void decrease_verbosity(void)
+{
+	print_level--;
+}
+
+static __attribute__((format (printf, 4, 0)))
+void print_msg(int priority, const char *file, int line,
+	       const char *format, va_list args) {
+
+	switch (priority) {
+
+	case MSG_DEBUG:
+		printf("(%s:%d) ", file, line);
+		vprintf(format, args);
+		break;
+	case MSG_INFO:
+	case MSG_NOTICE:
+	case MSG_WARN:
+		vprintf(format, args);
+		fflush(NULL);
+		break;
+	case MSG_ERROR:
+	case MSG_CRITICAL:
+	default:
+		vfprintf(stderr, format, args);
+		break;
+	}
+}
+
+
+void print_fsck_log(int priority, const char *file, int line,
+		    const char *format, ...)
+{
+	va_list args;
+	const char *transform;
+
+	va_start(args, format);
+	transform = _(format);
+	print_msg(priority, file, line, transform, args);
+	va_end(args);
+}
+
+char gfs2_getch(void)
+{
+	struct termios termattr, savetermattr;
+	char ch;
+	ssize_t size;
+
+	tcgetattr (STDIN_FILENO, &termattr);
+	savetermattr = termattr;
+	termattr.c_lflag &= ~(ICANON | IEXTEN | ISIG);
+	termattr.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
+	termattr.c_cflag &= ~(CSIZE | PARENB);
+	termattr.c_cflag |= CS8;
+	termattr.c_oflag &= ~(OPOST);
+	termattr.c_cc[VMIN] = 0;
+	termattr.c_cc[VTIME] = 0;
+
+	tcsetattr (STDIN_FILENO, TCSANOW, &termattr);
+	do {
+		size = read(STDIN_FILENO, &ch, 1);
+		if (size)
+			break;
+		usleep(50000);
+	} while (!size);
+
+	tcsetattr (STDIN_FILENO, TCSANOW, &savetermattr);
+	return ch;
+}
+
+char generic_interrupt(const char *caller, const char *where,
+		       const char *progress, const char *question,
+		       const char *answers)
+{
+	fd_set rfds;
+	struct timeval tv;
+	char response;
+	int err, i;
+
+	FD_ZERO(&rfds);
+	FD_SET(STDIN_FILENO, &rfds);
+
+	tv.tv_sec = 0;
+	tv.tv_usec = 0;
+	/* Make sure there isn't extraneous input before asking the
+	 * user the question */
+	while((err = select(STDIN_FILENO + 1, &rfds, NULL, NULL, &tv))) {
+		if(err < 0) {
+			log_debug("Error in select() on stdin\n");
+			break;
+		}
+		if(read(STDIN_FILENO, &response, sizeof(char)) < 0) {
+			log_debug("Error in read() on stdin\n");
+			break;
+		}
+	}
+	while (TRUE) {
+		printf("\n%s interrupted during %s:  ", caller, where);
+		if (progress)
+			printf("%s.\n", progress);
+		printf("%s", question);
+
+		/* Make sure query is printed out */
+		fflush(NULL);
+		response = gfs2_getch();
+		printf("\n");
+		fflush(NULL);
+		if (strchr(answers, response))
+			break;
+		printf("Bad response, please type ");
+		for (i = 0; i < strlen(answers) - 1; i++)
+			printf("'%c', ", answers[i]);
+		printf(" or '%c'.\n", answers[i]);
+	}
+	return response;
+}
+
+int gfs2_query(int *setonabort, struct gfs2_options *opts,
+	       const char *format, ...)
+{
+	va_list args;
+	const char *transform;
+	char response;
+	int ret = 0;
+
+	*setonabort = 0;
+	if(opts->yes)
+		return 1;
+	if(opts->no)
+		return 0;
+
+	opts->query = TRUE;
+	while (1) {
+		va_start(args, format);
+		transform = _(format);
+		vprintf(transform, args);
+		va_end(args);
+
+		/* Make sure query is printed out */
+		fflush(NULL);
+		response = gfs2_getch();
+
+		printf("\n");
+		fflush(NULL);
+		if (response == 0x3) { /* if interrupted, by ctrl-c */
+			response = generic_interrupt("Question", "response",
+						     NULL,
+						     "Do you want to abort " \
+						     "or continue (a/c)?",
+						     "ac");
+			if (response == 'a') {
+				ret = 0;
+				*setonabort = 1;
+				break;
+			}
+			printf("Continuing.\n");
+		} else if(tolower(response) == 'y') {
+			ret = 1;
+			break;
+		} else if (tolower(response) == 'n') {
+			ret = 0;
+			break;
+		} else {
+			printf("Bad response %d, please type 'y' or 'n'.\n",
+			       response);
+		}
+	}
+
+	opts->query = FALSE;
+	return ret;
+}
diff --git a/gfs2/fsck/hash.c b/gfs2/fsck/hash.c
index 34367be..9f09111 100644
--- a/gfs2/fsck/hash.c
+++ b/gfs2/fsck/hash.c
@@ -1,16 +1,3 @@
-/******************************************************************************
-*******************************************************************************
-**
-**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
-**  Copyright (C) 2004-2005 Red Hat, Inc.  All rights reserved.
-**
-**  This copyrighted material is made available to anyone wishing to use,
-**  modify, copy, or redistribute it subject to the terms and conditions
-**  of the GNU General Public License v.2.
-**
-*******************************************************************************
-******************************************************************************/
-
 /* This is the same hash algorithm used by the glocks in gfs */
 
 #include <stdint.h>
diff --git a/gfs2/fsck/hash.h b/gfs2/fsck/hash.h
index ba5856a..d5fe8e9 100644
--- a/gfs2/fsck/hash.h
+++ b/gfs2/fsck/hash.h
@@ -1,16 +1,3 @@
-/******************************************************************************
-*******************************************************************************
-**
-**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
-**  Copyright (C) 2004-2005 Red Hat, Inc.  All rights reserved.
-**
-**  This copyrighted material is made available to anyone wishing to use,
-**  modify, copy, or redistribute it subject to the terms and conditions
-**  of the GNU General Public License v.2.
-**
-*******************************************************************************
-******************************************************************************/
-
 #ifndef _HASH_H
 #define _HASH_H
 
diff --git a/gfs2/fsck/initialize.c b/gfs2/fsck/initialize.c
index 7855db4..789af2c 100644
--- a/gfs2/fsck/initialize.c
+++ b/gfs2/fsck/initialize.c
@@ -1,17 +1,3 @@
-/******************************************************************************
-*******************************************************************************
-**
-**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
-**  Copyright (C) 2004-2007 Red Hat, Inc.  All rights reserved.
-**
-**  This copyrighted material is made available to anyone wishing to use,
-**  modify, copy, or redistribute it subject to the terms and conditions
-**  of the GNU General Public License v.2.
-**
-*******************************************************************************
-******************************************************************************/
-
-#include <linux_endian.h>
 #include <stdio.h>
 #include <stdint.h>
 #include <inttypes.h>
@@ -23,6 +9,7 @@
 #include <unistd.h>
 #include <libintl.h>
 #include <errno.h>
+#include <time.h>
 
 #define _(String) gettext(String)
 
@@ -43,6 +30,8 @@
 static int was_mounted_ro = 0;
 static uint64_t possible_root = HIGHEST_BLOCK;
 static struct master_dir fix_md;
+static unsigned long long blks_2free = 0;
+extern int sb_fixed;
 
 /**
  * block_mounters
@@ -76,7 +65,7 @@ static int block_mounters(struct gfs2_sbd *sdp, int block_em)
 	return 0;
 }
 
-void gfs2_dup_free(void)
+static void gfs2_dup_free(void)
 {
 	struct osi_node *n;
 	struct duptree *dt;
@@ -156,7 +145,8 @@ static int set_block_ranges(struct gfs2_sbd *sdp)
 		next = osi_next(n);
 		rgd = (struct rgrp_tree *)n;
 		ri = &rgd->ri;
-		if (ri->ri_data0 + ri->ri_data - 1 > rmax)
+		if (ri->ri_data0 + ri->ri_data &&
+		    ri->ri_data0 + ri->ri_data - 1 > rmax)
 			rmax = ri->ri_data0 + ri->ri_data - 1;
 		if (!rmin || ri->ri_data0 < rmin)
 			rmin = ri->ri_data0;
@@ -185,8 +175,9 @@ static int set_block_ranges(struct gfs2_sbd *sdp)
 	error = read(sdp->device_fd, buf, sdp->sd_sb.sb_bsize);
 	if (error != sdp->sd_sb.sb_bsize){
 		log_crit( _("Can't read last block in file system (error %u), "
-				 "last_fs_block: %"PRIu64" (0x%" PRIx64 ")\n"), error,
-				 last_fs_block, last_fs_block);
+			 "last_fs_block: %llu (0x%llx)\n"), error,
+			 (unsigned long long)last_fs_block,
+			 (unsigned long long)last_fs_block);
 		goto fail;
 	}
 
@@ -201,16 +192,21 @@ static int set_block_ranges(struct gfs2_sbd *sdp)
  */
 static void check_rgrp_integrity(struct gfs2_sbd *sdp, struct rgrp_tree *rgd,
 				 int *fixit, int *this_rg_fixed,
-				 int *this_rg_bad)
+				 int *this_rg_bad, int *this_rg_cleaned)
 {
-	uint32_t rg_free, rg_reclaimed;
+	uint32_t rg_free, rg_reclaimed, rg_unlinked;
 	int rgb, x, y, off, bytes_to_check, total_bytes_to_check, asked = 0;
 	unsigned int state;
+	struct gfs_rgrp *gfs1rg = (struct gfs_rgrp *)&rgd->rg;
+	uint64_t diblock;
+	struct gfs2_buffer_head *bh;
 
-	rg_free = rg_reclaimed = 0;
+	rg_free = rg_reclaimed = rg_unlinked = 0;
 	total_bytes_to_check = rgd->ri.ri_bitbytes;
-	*this_rg_fixed = *this_rg_bad = 0;
 
+	*this_rg_fixed = *this_rg_bad = *this_rg_cleaned = 0;
+
+	diblock = rgd->ri.ri_data0;
 	for (rgb = 0; rgb < rgd->ri.ri_length; rgb++){
 		/* Count up the free blocks in the bitmap */
 		off = (rgb) ? sizeof(struct gfs2_meta_header) :
@@ -224,64 +220,138 @@ static void check_rgrp_integrity(struct gfs2_sbd *sdp, struct rgrp_tree *rgd,
 			unsigned char *byte;
 
 			byte = (unsigned char *)&rgd->bh[rgb]->b_data[off + x];
-			if (*byte == 0x55)
+			if (*byte == 0x55) {
+				diblock += GFS2_NBBY;
 				continue;
+			}
 			if (*byte == 0x00) {
+				diblock += GFS2_NBBY;
 				rg_free += GFS2_NBBY;
 				continue;
 			}
 			for (y = 0; y < GFS2_NBBY; y++) {
-				if (fsck_abort) /* if asked to abort */
-					return;
 				state = (*byte >>
 					 (GFS2_BIT_SIZE * y)) & GFS2_BIT_MASK;
-				if (state == GFS2_BLKST_USED)
+				if (state == GFS2_BLKST_USED) {
+					diblock++;
 					continue;
-				if (state == GFS2_BLKST_DINODE)
+				}
+				if (state == GFS2_BLKST_DINODE) {
+					diblock++;
 					continue;
+				}
 				if (state == GFS2_BLKST_FREE) {
+					diblock++;
 					rg_free++;
 					continue;
 				}
 				/* GFS2_BLKST_UNLINKED */
-				*this_rg_bad = 1;
+				if (sdp->gfs1)
+					log_info(_("Free metadata block 0x%llx"
+						   " found.\n"),
+						 (unsigned long long)diblock);
+				else
+					log_info(_("Unlinked dinode 0x%llx "
+						   "found.\n"),
+						 (unsigned long long)diblock);
 				if (!asked) {
 					char msg[256];
 
 					asked = 1;
 					sprintf(msg,
-						_("Okay to reclaim unlinked "
-						  "inodes in resource group "
+						_("Okay to reclaim free "
+						  "metadata in resource group "
 						  "%lld (0x%llx)? (y/n)"),
 						(unsigned long long)rgd->ri.ri_addr,
 						(unsigned long long)rgd->ri.ri_addr);
 					if (query("%s", msg))
 						*fixit = 1;
 				}
-				if (!(*fixit))
+				if (!(*fixit)) {
+					rg_unlinked++;
+					diblock++;
 					continue;
+				}
 				*byte &= ~(GFS2_BIT_MASK <<
 					   (GFS2_BIT_SIZE * y));
 				bmodified(rgd->bh[rgb]);
 				rg_reclaimed++;
 				rg_free++;
-				*this_rg_fixed = 1;
+				rgd->rg.rg_free++;
+				if (sdp->gfs1 && gfs1rg->rg_freemeta)
+					gfs1rg->rg_freemeta--;
+				log_info(_("Free metadata block %lld (0x%llx) "
+					   "reclaimed.\n"),
+					 (unsigned long long)diblock,
+					 (unsigned long long)diblock);
+				bh = bread(sdp, diblock);
+				if (!gfs2_check_meta(bh, GFS2_METATYPE_DI)) {
+					struct gfs2_inode *ip =
+						fsck_inode_get(sdp, bh);
+					if (ip->i_di.di_blocks > 1) {
+						blks_2free +=
+							ip->i_di.di_blocks - 1;
+						log_info(_("%lld blocks "
+							   "(total) may need "
+							   "to be freed in "
+							   "pass 5.\n"),
+							 blks_2free);
+					}
+					fsck_inode_put(&ip);
+				}
+				brelse(bh);
+				diblock++;
 			}
 		}
 	}
+	/* The unlinked blocks we reclaim shouldn't be considered errors,
+	   since we're just reclaiming them as a courtesy. If we already
+	   got permission to reclaim them, we adjust the rgrp counts
+	   accordingly. That way, only "real" rgrp count inconsistencies
+	   will be reported. */
+	if (rg_reclaimed && *fixit) {
+		if (sdp->gfs1)
+			gfs_rgrp_out((struct gfs_rgrp *)&rgd->rg, rgd->bh[0]);
+		else
+			gfs2_rgrp_out(&rgd->rg, rgd->bh[0]);
+		*this_rg_cleaned = 1;
+		log_info( _("The rgrp at %lld (0x%llx) was cleaned of %d "
+			    "free metadata blocks.\n"),
+			  (unsigned long long)rgd->ri.ri_addr,
+			  (unsigned long long)rgd->ri.ri_addr,
+			  rg_reclaimed);
+	}
 	if (rgd->rg.rg_free != rg_free) {
 		*this_rg_bad = 1;
+		*this_rg_cleaned = 0;
 		log_err( _("Error: resource group %lld (0x%llx): "
 			   "free space (%d) does not match bitmap (%d)\n"),
 			 (unsigned long long)rgd->ri.ri_addr,
 			 (unsigned long long)rgd->ri.ri_addr,
 			 rgd->rg.rg_free, rg_free);
-		if (rg_reclaimed)
-			log_err( _("(%d blocks were reclaimed)\n"),
-				 rg_reclaimed);
 		if (query( _("Fix the rgrp free blocks count? (y/n)"))) {
 			rgd->rg.rg_free = rg_free;
-			gfs2_rgrp_out(&rgd->rg, rgd->bh[0]);
+			if (sdp->gfs1)
+				gfs_rgrp_out((struct gfs_rgrp *)&rgd->rg,
+					     rgd->bh[0]);
+			else
+				gfs2_rgrp_out(&rgd->rg, rgd->bh[0]);
+			*this_rg_fixed = 1;
+			log_err( _("The rgrp was fixed.\n"));
+		} else
+			log_err( _("The rgrp was not fixed.\n"));
+	}
+	if (sdp->gfs1 && gfs1rg->rg_freemeta != rg_unlinked) {
+		*this_rg_bad = 1;
+		*this_rg_cleaned = 0;
+		log_err( _("Error: resource group %lld (0x%llx): "
+			   "free meta  (%d) does not match bitmap (%d)\n"),
+			 (unsigned long long)rgd->ri.ri_addr,
+			 (unsigned long long)rgd->ri.ri_addr,
+			 gfs1rg->rg_freemeta, rg_unlinked);
+		if (query( _("Fix the rgrp free meta blocks count? (y/n)"))) {
+			gfs1rg->rg_freemeta = rg_unlinked;
+			gfs_rgrp_out((struct gfs_rgrp *)&rgd->rg, rgd->bh[0]);
 			*this_rg_fixed = 1;
 			log_err( _("The rgrp was fixed.\n"));
 		} else
@@ -299,14 +369,16 @@ static void check_rgrp_integrity(struct gfs2_sbd *sdp, struct rgrp_tree *rgd,
 
 /**
  * check_rgrps_integrity - verify rgrp consistency
+ * Note: We consider an rgrp "cleaned" if the unlinked meta blocks are
+ *       cleaned, so not quite "bad" and not quite "good" but rewritten anyway.
  *
  * Returns: 0 on success, 1 if errors were detected
  */
-static int check_rgrps_integrity(struct gfs2_sbd *sdp)
+static void check_rgrps_integrity(struct gfs2_sbd *sdp)
 {
 	struct osi_node *n, *next = NULL;
-	int rgs_good = 0, rgs_bad = 0, rgs_fixed = 0;
-	int was_bad = 0, was_fixed = 0, error = 0;
+	int rgs_good = 0, rgs_bad = 0, rgs_fixed = 0, rgs_cleaned = 0;
+	int was_bad = 0, was_fixed = 0, was_cleaned = 0;
 	struct rgrp_tree *rgd;
 	int reclaim_unlinked = 0;
 
@@ -315,22 +387,28 @@ static int check_rgrps_integrity(struct gfs2_sbd *sdp)
 		next = osi_next(n);
 		rgd = (struct rgrp_tree *)n;
 		if (fsck_abort)
-			return 0;
+			return;
 		check_rgrp_integrity(sdp, rgd, &reclaim_unlinked,
-				     &was_fixed, &was_bad);
+				     &was_fixed, &was_bad, &was_cleaned);
 		if (was_fixed)
 			rgs_fixed++;
-		if (was_bad) {
-			error = 1;
+		if (was_cleaned)
+			rgs_cleaned++;
+		else if (was_bad)
 			rgs_bad++;
-		} else
+		else
 			rgs_good++;
 	}
-	if (rgs_bad)
-		log_err( _("RGs: Consistent: %d   Inconsistent: %d   Fixed: %d"
-			   "   Total: %d\n"),
-			 rgs_good, rgs_bad, rgs_fixed, rgs_good + rgs_bad);
-	return error;
+	if (rgs_bad || rgs_cleaned) {
+		log_err( _("RGs: Consistent: %d   Cleaned: %d   Inconsistent: "
+			   "%d   Fixed: %d   Total: %d\n"),
+			 rgs_good, rgs_cleaned, rgs_bad, rgs_fixed,
+			 rgs_good + rgs_bad + rgs_cleaned);
+		if (rgs_cleaned && blks_2free)
+			log_err(_("%lld blocks may need to be freed in pass 5 "
+				  "due to the cleaned resource groups.\n"),
+				blks_2free);
+	}
 }
 
 /**
@@ -541,7 +619,7 @@ static int fetch_rgrps(struct gfs2_sbd *sdp)
 	 ********  Validate and read in resource group information  ********
 	 *******************************************************************/
 	log_warn( _("Validating Resource Group index.\n"));
-	for (trust_lvl = blind_faith; trust_lvl <= distrust; trust_lvl++) {
+	for (trust_lvl = blind_faith; trust_lvl <= indignation; trust_lvl++) {
 		int ret = 0;
 
 		log_warn( _("Level %d rgrp check: %s.\n"), trust_lvl + 1,
@@ -564,8 +642,9 @@ static int fetch_rgrps(struct gfs2_sbd *sdp)
 		if (fsck_abort)
 			break;
 	}
-	if (trust_lvl > distrust) {
-		log_err( _("RG recovery impossible; I can't fix this file system.\n"));
+	if (trust_lvl > indignation) {
+		log_err( _("Resource Group recovery impossible; I can't fix "
+			   "this file system.\n"));
 		return -1;
 	}
 	log_info( _("%u resource groups found.\n"), rgcount);
@@ -603,23 +682,9 @@ static int init_system_inodes(struct gfs2_sbd *sdp)
 	/*******************************************************************
 	 *****************  Initialize more system inodes  *****************
 	 *******************************************************************/
-	/* Look for "inum" entry in master dinode */
-	gfs2_lookupi(sdp->master_dir, "inum", 4, &sdp->md.inum);
-	if (!sdp->md.inum) {
-		if (!query( _("The gfs2 system inum inode is missing. "
-			      "Okay to rebuild it? (y/n) "))) {
-			log_err( _("fsck.gfs2 cannot continue without "
-				   "a valid inum file; aborting.\n"));
-			goto fail;
-		}
-		err = build_inum(sdp);
-		if (err) {
-			log_crit(_("Error rebuilding inum inode: %s\n"),
-				 strerror(err));
-			exit(FSCK_ERROR);
-		}
-		gfs2_lookupi(sdp->master_dir, "inum", 4,
-			     &sdp->md.inum);
+	if (!sdp->gfs1) {
+		/* Look for "inum" entry in master dinode */
+		gfs2_lookupi(sdp->master_dir, "inum", 4, &sdp->md.inum);
 		if (!sdp->md.inum) {
 			if (!query( _("The gfs2 system inum inode is missing. "
 				      "Okay to rebuild it? (y/n) "))) {
@@ -652,14 +717,12 @@ static int init_system_inodes(struct gfs2_sbd *sdp)
 		/* call gfs2_inum_range_in() to retrieve range */
 		sdp->md.next_inum = be64_to_cpu(inumbuf);
 	}
-	/* Read inum entry into buffer */
-	gfs2_readi(sdp->md.inum, &inumbuf, 0,
-		   sdp->md.inum->i_di.di_size);
-	/* call gfs2_inum_range_in() to retrieve range */
-	sdp->md.next_inum = be64_to_cpu(inumbuf);
-
-	gfs2_lookupi(sdp->master_dir, "statfs", 6, &sdp->md.statfs);
-	if (!sdp->md.statfs) {
+
+	if (sdp->gfs1)
+		sdp->md.statfs = inode_read(sdp, sbd1->sb_license_di.no_addr);
+	else
+		gfs2_lookupi(sdp->master_dir, "statfs", 6, &sdp->md.statfs);
+	if (!sdp->gfs1 && !sdp->md.statfs) {
 		if (!query( _("The gfs2 system statfs inode is missing. "
 			      "Okay to rebuild it? (y/n) "))) {
 			log_err( _("fsck.gfs2 cannot continue without a valid "
@@ -697,8 +760,11 @@ static int init_system_inodes(struct gfs2_sbd *sdp)
 		}
 	}
 
-	gfs2_lookupi(sdp->master_dir, "quota", 5, &sdp->md.qinode);
-	if (!sdp->md.qinode) {
+	if (sdp->gfs1)
+		sdp->md.qinode = inode_read(sdp, sbd1->sb_quota_di.no_addr);
+	else
+		gfs2_lookupi(sdp->master_dir, "quota", 5, &sdp->md.qinode);
+	if (!sdp->gfs1 && !sdp->md.qinode) {
 		if (!query( _("The gfs2 system quota inode is missing. "
 			      "Okay to rebuild it? (y/n) "))) {
 			log_crit("System quota inode was not "
@@ -720,7 +786,9 @@ static int init_system_inodes(struct gfs2_sbd *sdp)
 
 	/* Try to lookup the per_node inode.  If it was missing, it is now
 	   safe to rebuild it. */
-	lookup_per_node(sdp, 1);
+	if (!sdp->gfs1)
+		lookup_per_node(sdp, 1);
+
 	/*******************************************************************
 	 *******  Now, set boundary fields in the super block  *************
 	 *******************************************************************/
@@ -732,7 +800,7 @@ static int init_system_inodes(struct gfs2_sbd *sdp)
 
 	bl = gfs2_bmap_create(sdp, last_fs_block+1, &addl_mem_needed);
 	if (!bl) {
-		log_crit( _("This system doesn't have enough memory + swap space to fsck this file system.\n"));
+		log_crit( _("This system doesn't have enough memory and swap space to fsck this file system.\n"));
 		log_crit( _("Additional memory needed is approximately: %lluMB\n"),
 			 (unsigned long long)(addl_mem_needed / 1048576ULL));
 		log_crit( _("Please increase your swap space by that amount and run gfs2_fsck again.\n"));
@@ -745,58 +813,6 @@ static int init_system_inodes(struct gfs2_sbd *sdp)
 	return -1;
 }
 
-static int get_lockproto_table(struct gfs2_sbd *sdp)
-{
-	FILE *fp;
-	char line[PATH_MAX];
-	char *cluname = NULL, *end = NULL;
-	const char *fsname, *cfgfile = "/etc/cluster/cluster.conf";
-
-	memset(sdp->lockproto, 0, sizeof(sdp->lockproto));
-	memset(sdp->locktable, 0, sizeof(sdp->locktable));
-	fp = fopen(cfgfile, "rt");
-	if (!fp) {
-		/* no cluster.conf; must be a stand-alone file system */
-		strcpy(sdp->lockproto, "lock_nolock");
-		log_warn(_("Lock protocol determined to be: lock_nolock\n"));
-		log_warn(_("Stand-alone file system: No need for a lock "
-			   "table.\n"));
-		return 0;
-	}
-	/* We found a cluster.conf so assume it's a clustered file system */
-	log_warn(_("Lock protocol assumed to be: " GFS2_DEFAULT_LOCKPROTO
-		   "\n"));
-	strcpy(sdp->lockproto, GFS2_DEFAULT_LOCKPROTO);
-
-	while (fgets(line, sizeof(line) - 1, fp)) {
-		cluname = strstr(line,"<cluster name=");
-		if (cluname) {
-			cluname += 15;
-			end = strchr(cluname,'"');
-			if (end)
-				*end = '\0';
-			break;
-		}
-	}
-	if (cluname == NULL || end == NULL || end - cluname < 1) {
-		log_err(_("Error: Unable to determine cluster name from %s\n"),
-		                                                      cfgfile);
-	} else {
-		fsname = strrchr(opts.device, '/');
-		if (fsname)
-			fsname++;
-		else
-			fsname = "repaired";
-		snprintf(sdp->locktable, sizeof(sdp->locktable), "%.*s:%.16s",
-		         (int)(sizeof(sdp->locktable) - strlen(fsname) - 2),
-		         cluname, fsname);
-		log_warn(_("Lock table determined to be: %s\n"),
-			 sdp->locktable);
-	}
-	fclose(fp);
-	return 0;
-}
-
 /**
  * is_journal_copy - Is this a "real" dinode or a copy inside a journal?
  * A real dinode will be located at the block number in its no_addr.
@@ -835,13 +851,15 @@ static void peruse_system_dinode(struct gfs2_sbd *sdp, struct gfs2_dinode *di,
 		return;
 	}
 	ip = inode_read(sdp, di->di_num.no_addr);
-	if (di->di_num.no_formal_ino == 3) {
+	if ((!sdp->gfs1 && di->di_num.no_formal_ino == 3) ||
+	    (sdp->gfs1 && (di->di_flags & GFS2_DIF_JDATA) &&
+	     (di->di_size % sizeof(struct gfs_jindex) == 0))) {
 		if (fix_md.jiinode || is_journal_copy(ip, bh))
 			goto out_discard_ip;
 		log_warn(_("Found system jindex file at: 0x%llx\n"),
 			 di->di_num.no_addr);
 		fix_md.jiinode = ip;
-	} else if (S_ISDIR(di->di_mode)) {
+	} else if (!sdp->gfs1 && is_dir(di, sdp->gfs1)) {
 		/* Check for a jindex dir entry. Only one system dir has a
 		   jindex: master */
 		gfs2_lookupi(ip, "jindex", 6, &child_ip);
@@ -880,7 +898,7 @@ static void peruse_system_dinode(struct gfs2_sbd *sdp, struct gfs2_dinode *di,
 		log_debug(_("Unknown system directory at block 0x%llx\n"),
 			  di->di_num.no_addr);
 		goto out_discard_ip;
-	} else if (di->di_size == 8) {
+	} else if (!sdp->gfs1 && di->di_size == 8) {
 		if (fix_md.inum || is_journal_copy(ip, bh))
 			goto out_discard_ip;
 		fix_md.inum = ip;
@@ -926,7 +944,7 @@ static void peruse_user_dinode(struct gfs2_sbd *sdp, struct gfs2_dinode *di,
 
 	if (sdp->sd_sb.sb_root_dir.no_addr) /* if we know the root dinode */
 		return;             /* we don't need to find the root */
-	if (!S_ISDIR(di->di_mode))  /* if this isn't a directory */
+	if (!is_dir(di, sdp->gfs1))  /* if this isn't a directory */
 		return;             /* it can't lead us to the root anyway */
 
 	if (di->di_num.no_formal_ino == 1) {
@@ -961,7 +979,7 @@ static void peruse_user_dinode(struct gfs2_sbd *sdp, struct gfs2_dinode *di,
 		gfs2_lookupi(ip, "..", 2, &parent_ip);
 		if (parent_ip && parent_ip->i_di.di_num.no_addr ==
 		    ip->i_di.di_num.no_addr) {
-			log_warn(_("fsck found the root inode at: 0x%llx\n"),
+			log_warn(_("Found the root directory at: 0x%llx\n"),
 				 ip->i_di.di_num.no_addr);
 			sdp->sd_sb.sb_root_dir.no_addr =
 				ip->i_di.di_num.no_addr;
@@ -1061,7 +1079,6 @@ static int peruse_metadata(struct gfs2_sbd *sdp, uint64_t startblock)
 	uint64_t blk, max_rg_size;
 	struct gfs2_buffer_head *bh;
 	struct gfs2_dinode di;
-	int found_gfs2_dinodes = 0, possible_gfs1_dinodes = 0;
 
 	max_rg_size = 2147483648ull / sdp->bsize;
 	/* Max RG size is 2GB. 2G / bsize. */
@@ -1072,18 +1089,6 @@ static int peruse_metadata(struct gfs2_sbd *sdp, uint64_t startblock)
 			continue;
 		}
 		gfs2_dinode_in(&di, bh);
-		if (!found_gfs2_dinodes &&
-		    di.di_num.no_addr == di.di_num.no_formal_ino) {
-			possible_gfs1_dinodes++;
-			if (possible_gfs1_dinodes > 5) {
-				log_err(_("Found several gfs (version 1) "
-					  "dinodes; aborting.\n"));
-				brelse(bh);
-				return -1;
-			}
-		} else {
-			found_gfs2_dinodes++;
-		}
 		if (di.di_flags & GFS2_DIF_SYSTEM)
 			peruse_system_dinode(sdp, &di, bh);
 		else
@@ -1152,7 +1157,7 @@ static int sb_repair(struct gfs2_sbd *sdp)
 				  "be the root; using master - 1.\n"));
 			possible_root = sdp->sd_sb.sb_master_dir.no_addr - 1;
 		}
-		log_err(_("Found a root directory candidate at  0x%llx\n"),
+		log_err(_("Found a possible root at: 0x%llx\n"),
 			(unsigned long long)possible_root);
 		sdp->sd_sb.sb_root_dir.no_addr = possible_root;
 		sdp->md.rooti = inode_read(sdp, possible_root);
@@ -1178,9 +1183,10 @@ static int sb_repair(struct gfs2_sbd *sdp)
 		}
 	}
 	/* Step 3 - Rebuild the lock protocol and file system table name */
-	get_lockproto_table(sdp);
+	strcpy(sdp->lockproto, GFS2_DEFAULT_LOCKPROTO);
+	strcpy(sdp->locktable, "unknown");
 	if (query(_("Okay to fix the GFS2 superblock? (y/n)"))) {
-		log_info(_("Master system directory found at: 0x%llx\n"),
+		log_info(_("Found system master directory at: 0x%llx\n"),
 			 sdp->sd_sb.sb_master_dir.no_addr);
 		sdp->master_dir = inode_read(sdp,
 					     sdp->sd_sb.sb_master_dir.no_addr);
@@ -1194,6 +1200,7 @@ static int sb_repair(struct gfs2_sbd *sdp)
 		build_sb(sdp, uuid);
 		inode_put(&sdp->md.rooti);
 		inode_put(&sdp->master_dir);
+		sb_fixed = 1;
 	} else {
 		log_crit(_("GFS2 superblock not fixed; fsck cannot proceed "
 			   "without a valid superblock.\n"));
@@ -1232,15 +1239,116 @@ static int fill_super_block(struct gfs2_sbd *sdp)
 		return -1;
 	}
 
-	compute_constants(sdp);
-	ret = read_sb(sdp);
+	if (compute_constants(sdp)) {
+		log_crit(_("Bad constants (1)\n"));
+		exit(FSCK_ERROR);
+	}
+	ret = read_sb(sdp, 1);
 	if (ret < 0) {
 		if (sb_repair(sdp) != 0)
 			return -1; /* unrepairable, so exit */
 		/* Now that we've tried to repair it, re-read it. */
-		if (read_sb(sdp) < 0)
+		ret = read_sb(sdp, 1);
+		if (ret < 0)
+			return -1;
+	}
+	if (sdp->gfs1)
+		sbd1 = (struct gfs_sb *)&sdp->sd_sb;
+	return 0;
+}
+
+static void gfs_log_header_out(struct gfs_log_header *head, char *buf)
+{
+        struct gfs_log_header *str = (struct gfs_log_header *) buf;
+
+	str->lh_header.mh_magic = cpu_to_be32(head->lh_header.mh_magic);
+	str->lh_header.mh_type = cpu_to_be32(head->lh_header.mh_type);
+	str->lh_header.mh_format = cpu_to_be32(head->lh_header.mh_format);
+	str->lh_header.__pad0 = cpu_to_be32(head->lh_header.__pad0);
+
+	str->lh_flags = cpu_to_be32(head->lh_flags);
+	str->lh_pad = cpu_to_be32(head->lh_pad);
+	str->lh_first = cpu_to_be64(head->lh_first);
+	str->lh_sequence = cpu_to_be64(head->lh_sequence);
+	str->lh_tail = cpu_to_be64(head->lh_tail);
+	str->lh_last_dump = cpu_to_be64(head->lh_last_dump);
+}
+
+/*
+ * reconstruct_single_journal - write a fresh GFS1 journal
+ * @sdp: superblock
+ * @jnum: journal number
+ *
+ * This function will write a fresh journal over the top of
+ * the previous journal.  All journal information is lost.  This
+ * process is basically stolen from write_journals() in the mkfs code.
+ *
+ * Returns: -1 on error, 0 otherwise
+ */
+static int reconstruct_single_journal(struct gfs2_sbd *sdp, int jnum,
+				      uint32_t ji_nsegment)
+{
+	struct gfs_log_header lh;
+	uint32_t seg, sequence;
+	struct gfs2_buffer_head *bh;
+
+	srandom(time(NULL));
+	sequence = ji_nsegment / (RAND_MAX + 1.0) * random();
+
+	log_info("Clearing journal %d\n", jnum);
+
+	for (seg = 0; seg < ji_nsegment; seg++){
+		memset(&lh, 0, sizeof(struct gfs_log_header));
+
+		lh.lh_header.mh_magic = GFS2_MAGIC;
+		lh.lh_header.mh_type = GFS2_METATYPE_LH;
+		lh.lh_header.mh_format = GFS2_FORMAT_LH;
+		lh.lh_header.__pad0 = 0x101674; /* mh_generation */
+		lh.lh_flags = GFS2_LOG_HEAD_UNMOUNT;
+		lh.lh_first = sdp->md.journal[jnum]->i_di.di_num.no_addr +
+			(seg * sbd1->sb_seg_size);
+		lh.lh_sequence = sequence;
+
+		bh = bget(sdp, lh.lh_first * sdp->bsize);
+		memset(bh->b_data, 0, sdp->bsize);
+		gfs_log_header_out(&lh, bh->b_data);
+		gfs_log_header_out(&lh, bh->b_data + GFS2_BASIC_BLOCK -
+				   sizeof(struct gfs_log_header));
+		brelse(bh);
+
+		if (++sequence == ji_nsegment)
+			sequence = 0;
+	}
+	return 0;
+}
+
+
+/*
+ * reconstruct_journals - write fresh journals for GFS1 only
+ * sdp: the super block
+ *
+ * Returns: 0 on success, -1 on failure
+ */
+static int reconstruct_journals(struct gfs2_sbd *sdp)
+{
+	int i, count;
+	struct gfs_jindex ji;
+	char buf[sizeof(struct gfs_jindex)];
+
+	log_err("Clearing GFS journals (this may take a while)\n");
+	for (i = 0; i < sdp->md.journals; i++) {
+		count = gfs2_readi(sdp->md.jiinode, buf,
+				   i * sizeof(struct gfs_jindex),
+				   sizeof(struct gfs_jindex));
+		if (count != sizeof(struct gfs_jindex))
+			return 0;
+		gfs_jindex_in(&ji, buf);
+		if ((i % 2) == 0)
+			log_err(".");
+		if (reconstruct_single_journal(sdp, i, ji.ji_nsegment))
 			return -1;
 	}
+	log_err("\nJournals cleared.\n");
 	return 0;
 }
 
@@ -1251,7 +1359,10 @@ static int init_rindex(struct gfs2_sbd *sdp)
 {
 	int err;
 
-	gfs2_lookupi(sdp->master_dir, "rindex", 6, &sdp->md.riinode);
+	if (sdp->gfs1)
+		sdp->md.riinode = inode_read(sdp, sbd1->sb_rindex_di.no_addr);
+	else
+		gfs2_lookupi(sdp->master_dir, "rindex", 6, &sdp->md.riinode);
 
 	if (sdp->md.riinode)
 		return 0;
@@ -1279,7 +1390,10 @@ static int init_jindex(struct gfs2_sbd *sdp)
 
 	/* rgrepair requires the journals be read in in order to distinguish
 	   "real" rgrps from rgrps that are just copies left in journals. */
-	gfs2_lookupi(sdp->master_dir, "jindex", 6, &sdp->md.jiinode);
+	if (sdp->gfs1)
+		sdp->md.jiinode = inode_read(sdp, sbd1->sb_jindex_di.no_addr);
+	else
+		gfs2_lookupi(sdp->master_dir, "jindex", 6, &sdp->md.jiinode);
 
 	if (!sdp->md.jiinode) {
 		int err;
@@ -1334,7 +1448,8 @@ int initialize(struct gfs2_sbd *sdp, int force_check, int preen,
 		int is_mounted, ro;
 
 		if (open_flag == O_RDONLY || errno != EBUSY) {
-			log_crit( _("Unable to open device: %s\n"), opts.device);
+			log_crit( _("Unable to open device: %s\n"),
+				  opts.device);
 			return FSCK_USAGE;
 		}
 		/* We can't open it EXCL.  It may be already open rw (in which
@@ -1380,10 +1495,15 @@ int initialize(struct gfs2_sbd *sdp, int force_check, int preen,
 	}
 
 	/* Get master dinode */
-	sdp->master_dir = inode_read(sdp, sdp->sd_sb.sb_master_dir.no_addr);
-	if (sdp->master_dir->i_di.di_header.mh_magic != GFS2_MAGIC ||
-	    sdp->master_dir->i_di.di_header.mh_type != GFS2_METATYPE_DI ||
-	    !sdp->master_dir->i_di.di_size) {
+	if (sdp->gfs1)
+		sdp->master_dir = NULL;
+	else
+		sdp->master_dir = inode_read(sdp,
+					     sdp->sd_sb.sb_master_dir.no_addr);
+	if (!sdp->gfs1 &&
+	    (sdp->master_dir->i_di.di_header.mh_magic != GFS2_MAGIC ||
+	     sdp->master_dir->i_di.di_header.mh_type != GFS2_METATYPE_DI ||
+	     !sdp->master_dir->i_di.di_size)) {
 		inode_put(&sdp->master_dir);
 		rebuild_master(sdp);
 		sdp->master_dir = inode_read(sdp,
@@ -1393,7 +1513,8 @@ int initialize(struct gfs2_sbd *sdp, int force_check, int preen,
 	/* Look up the "per_node" inode.  If there are journals missing, we
 	   need to figure out what's missing from per_node. And we need all
 	   our journals to be there before we can replay them. */
-	lookup_per_node(sdp, 0);
+	if (!sdp->gfs1)
+		lookup_per_node(sdp, 0);
 
 	/* We need rindex first in case jindex is missing and needs to read
 	   in the rgrps before rebuilding it. */
@@ -1407,7 +1528,10 @@ int initialize(struct gfs2_sbd *sdp, int force_check, int preen,
 	/* If GFS, rebuild the journals.  If GFS2, replay them.  We don't have
 	   the smarts to replay GFS1 journals (neither did gfs_fsck). */
 
-	if (replay_journals(sdp, preen, force_check, &clean_journals)) {
+	if (sdp->gfs1) {
+		if (reconstruct_journals(sdp))
+			return FSCK_ERROR;
+	} else if (replay_journals(sdp, preen, force_check, &clean_journals)) {
 		if (!opts.no && preen_is_safe(sdp, preen, force_check))
 			block_mounters(sdp, 0);
 		stack;
diff --git a/gfs2/fsck/inode_hash.c b/gfs2/fsck/inode_hash.c
index 5532faf..56581b7 100644
--- a/gfs2/fsck/inode_hash.c
+++ b/gfs2/fsck/inode_hash.c
@@ -1,24 +1,10 @@
-/******************************************************************************
-*******************************************************************************
-**
-**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
-**  Copyright (C) 2004-2005 Red Hat, Inc.  All rights reserved.
-**
-**  This copyrighted material is made available to anyone wishing to use,
-**  modify, copy, or redistribute it subject to the terms and conditions
-**  of the GNU General Public License v.2.
-**
-*******************************************************************************
-******************************************************************************/
-
-#include <string.h>
 #include <stdint.h>
 #include <unistd.h>
 #include <libintl.h>
+#include <string.h>
 
 #include "libgfs2.h"
 #include "osi_list.h"
-#include "hash.h"
 #include "inode_hash.h"
 #include "fsck.h"
 #define _(String) gettext(String)
diff --git a/gfs2/fsck/inode_hash.h b/gfs2/fsck/inode_hash.h
index 6275d6d..ba18ab2 100644
--- a/gfs2/fsck/inode_hash.h
+++ b/gfs2/fsck/inode_hash.h
@@ -1,16 +1,3 @@
-/******************************************************************************
-*******************************************************************************
-**
-**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
-**  Copyright (C) 2004-2005 Red Hat, Inc.  All rights reserved.
-**
-**  This copyrighted material is made available to anyone wishing to use,
-**  modify, copy, or redistribute it subject to the terms and conditions
-**  of the GNU General Public License v.2.
-**
-*******************************************************************************
-******************************************************************************/
-
 #ifndef _INODE_HASH_H
 #define _INODE_HASH_H
 
diff --git a/gfs2/fsck/libgfs2.h b/gfs2/fsck/libgfs2.h
new file mode 100644
index 0000000..2edc04c
--- /dev/null
+++ b/gfs2/fsck/libgfs2.h
@@ -0,0 +1,822 @@
+#ifndef __LIBGFS2_DOT_H__
+#define __LIBGFS2_DOT_H__
+
+#include <features.h>
+#include <inttypes.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <linux/types.h>
+#include <linux/limits.h>
+#include <endian.h>
+#include <byteswap.h>
+
+#include <linux/gfs2_ondisk.h>
+#include "osi_list.h"
+#include "osi_tree.h"
+
+__BEGIN_DECLS
+
+#ifndef TRUE
+#define TRUE (1)
+#endif
+
+#ifndef FALSE
+#define FALSE (0)
+#endif
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+
+#define be16_to_cpu(x) (x)
+#define be32_to_cpu(x) (x)
+#define be64_to_cpu(x) (x)
+
+#define cpu_to_be16(x) (x)
+#define cpu_to_be32(x) (x)
+#define cpu_to_be64(x) (x)
+
+#define le16_to_cpu(x) (bswap_16((x)))
+#define le32_to_cpu(x) (bswap_32((x)))
+#define le64_to_cpu(x) (bswap_64((x)))
+
+#define cpu_to_le16(x) (bswap_16((x)))
+#define cpu_to_le32(x) (bswap_32((x)))
+#define cpu_to_le64(x) (bswap_64((x)))
+
+#endif  /*  __BYTE_ORDER == __BIG_ENDIAN  */
+
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+#define be16_to_cpu(x) (bswap_16((x)))
+#define be32_to_cpu(x) (bswap_32((x)))
+#define be64_to_cpu(x) (bswap_64((x)))
+
+#define cpu_to_be16(x) (bswap_16((x)))
+#define cpu_to_be32(x) (bswap_32((x)))
+#define cpu_to_be64(x) (bswap_64((x)))
+
+#define le16_to_cpu(x) (x)
+#define le32_to_cpu(x) (x)
+#define le64_to_cpu(x) (x)
+
+#define cpu_to_le16(x) (x)
+#define cpu_to_le32(x) (x)
+#define cpu_to_le64(x) (x)
+
+#endif  /*  __BYTE_ORDER == __LITTLE_ENDIAN  */
+
+#define BLOCKMAP_SIZE4(size) (size >> 1)
+#define BLOCKMAP_BYTE_OFFSET4(x) ((x & 0x0000000000000001) << 2)
+#define BLOCKMAP_MASK4 (0xf)
+
+static __inline__ __attribute__((noreturn, format (printf, 1, 2)))
+void die(const char *fmt, ...)
+{
+	va_list ap;
+	va_start(ap, fmt);
+	vfprintf(stderr, fmt, ap);
+	va_end(ap);
+	exit(-1);
+}
+
+struct device {
+	uint64_t start;
+	uint64_t length;
+	uint32_t rgf_flags;
+};
+
+struct gfs2_bitmap
+{
+	uint32_t   bi_offset;  /* The offset in the buffer of the first byte */
+	uint32_t   bi_start;   /* The position of the first byte in this block */
+	uint32_t   bi_len;     /* The number of bytes in this block */
+};
+
+struct rgrp_tree {
+	struct osi_node node;
+	uint64_t start;	   /* The offset of the beginning of this resource group */
+	uint64_t length;	/* The length of this resource group */
+
+	struct gfs2_rindex ri;
+	struct gfs2_rgrp rg;
+	struct gfs2_bitmap *bits;
+	struct gfs2_buffer_head **bh;
+};
+
+struct gfs2_buffer_head {
+	osi_list_t b_altlist; /* alternate list */
+	uint64_t b_blocknr;
+	int b_modified;
+	union {
+		struct iovec iov;
+		char *b_data;
+	};
+	struct gfs2_sbd *sdp;
+};
+
+struct special_blocks {
+	osi_list_t list;
+	uint64_t block;
+};
+
+struct gfs2_sbd;
+struct gfs2_inode {
+	int bh_owned; /* Is this bh owned, iow, should we release it later? */
+	struct gfs2_dinode i_di;
+	struct gfs2_buffer_head *i_bh;
+	struct gfs2_sbd *i_sbd;
+};
+
+#define BUF_HASH_SHIFT       (13)    /* # hash buckets = 8K */
+#define BUF_HASH_SIZE        (1 << BUF_HASH_SHIFT)
+#define BUF_HASH_MASK        (BUF_HASH_SIZE - 1)
+
+/* FIXME not sure that i want to keep a record of the inodes or the
+ * contents of them, or both ... if I need to write back to them, it
+ * would be easier to hold the inode as well  */
+struct per_node
+{
+	struct gfs2_inode *inum;
+	struct gfs2_inum_range inum_range;
+	struct gfs2_inode *statfs;
+	struct gfs2_statfs_change statfs_change;
+	struct gfs2_inode *unlinked;
+	struct gfs2_inode *quota;
+	struct gfs2_quota_change quota_change;
+};
+
+struct master_dir
+{
+	struct gfs2_inode *inum;
+	uint64_t next_inum;
+	struct gfs2_inode *statfs;
+	struct gfs2_statfs_change statfs_change;
+
+	struct gfs2_rindex rindex;
+	struct gfs2_inode *qinode;
+	struct gfs2_quota quotas;
+
+	struct gfs2_inode       *jiinode;
+	struct gfs2_inode       *riinode;
+	struct gfs2_inode       *rooti;
+	struct gfs2_inode       *pinode;
+
+	struct gfs2_inode **journal;      /* Array of journals */
+	uint32_t journals;                /* Journal count */
+	struct per_node *pn;              /* Array of per_node entries */
+};
+
+struct gfs2_sbd {
+	struct gfs2_sb sd_sb;    /* a copy of the ondisk structure */
+	char lockproto[GFS2_LOCKNAME_LEN];
+	char locktable[GFS2_LOCKNAME_LEN];
+
+	unsigned int bsize;	     /* The block size of the FS (in bytes) */
+	unsigned int jsize;	     /* Size of journals (in MB) */
+	unsigned int rgsize;     /* Size of resource groups (in MB) */
+	unsigned int utsize;     /* Size of unlinked tag files (in MB) */
+	unsigned int qcsize;     /* Size of quota change files (in MB) */
+
+	int debug;
+	int quiet;
+	int expert;
+	int override;
+
+	char device_name[PATH_MAX];
+	char *path_name;
+
+	/* Constants */
+
+	uint32_t sd_fsb2bb;
+	uint32_t sd_fsb2bb_shift;
+	uint32_t sd_diptrs;
+	uint32_t sd_inptrs;
+	uint32_t sd_jbsize;
+	uint32_t sd_hash_bsize;
+	uint32_t sd_hash_bsize_shift;
+	uint32_t sd_hash_ptrs;
+	uint32_t sd_max_dirres;
+	uint32_t sd_max_height;
+	uint64_t sd_heightsize[GFS2_MAX_META_HEIGHT];
+	uint32_t sd_max_jheight;
+	uint64_t sd_jheightsize[GFS2_MAX_META_HEIGHT];
+
+	/* Not specified on the command line, but... */
+
+	int64_t time;
+
+	struct device device;
+	uint64_t device_size;
+
+	int device_fd;
+	int path_fd;
+
+	uint64_t sb_addr;
+
+	uint64_t orig_fssize;
+	uint64_t fssize;
+	uint64_t blks_total;
+	uint64_t blks_alloced;
+	uint64_t dinodes_alloced;
+
+	uint64_t orig_rgrps;
+	uint64_t rgrps;
+	uint64_t new_rgrps;
+	struct osi_root rgtree;
+	struct osi_root rgcalc;
+
+	unsigned int orig_journals;
+
+	struct gfs2_inode *master_dir;
+	struct master_dir md;
+
+	unsigned int writes;
+	int metafs_fd;
+	char metafs_path[PATH_MAX]; /* where metafs is mounted */
+	struct special_blocks eattr_blocks;
+
+	/* device topology information: */
+	uint32_t logical_block_size;
+	uint32_t minimum_io_size;
+	uint32_t optimal_io_size;
+	uint32_t alignment_offset;
+	uint32_t physical_block_size;
+	uint64_t rg_one_length;
+	uint64_t rg_length;
+	int gfs1;
+};
+
+struct metapath {
+	unsigned int mp_list[GFS2_MAX_META_HEIGHT];
+};
+
+
+#define GFS2_DEFAULT_BSIZE          (4096)
+#define GFS2_DEFAULT_JSIZE          (128)
+#define GFS2_DEFAULT_RGSIZE         (256)
+#define GFS2_DEFAULT_UTSIZE         (1)
+#define GFS2_DEFAULT_QCSIZE         (1)
+#define GFS2_DEFAULT_LOCKPROTO      "lock_dlm"
+#define GFS2_MIN_GROW_SIZE          (10)
+#define GFS2_EXCESSIVE_RGS          (10000)
+
+#define DATA (1)
+#define META (2)
+#define DINODE (3)
+
+/* bitmap.c */
+struct gfs2_bmap {
+	uint64_t size;
+	uint64_t mapsize;
+	unsigned char *map;
+};
+
+/* block_list.c */
+
+extern struct special_blocks *blockfind(struct special_blocks *blist, uint64_t num);
+extern void gfs2_special_add(struct special_blocks *blocklist, uint64_t block);
+extern void gfs2_special_set(struct special_blocks *blocklist, uint64_t block);
+extern void gfs2_special_free(struct special_blocks *blist);
+extern void gfs2_special_clear(struct special_blocks *blocklist,
+			       uint64_t block);
+
+/* buf.c */
+extern struct gfs2_buffer_head *__bget_generic(struct gfs2_sbd *sdp,
+					       uint64_t num,
+					       int read_disk, int line,
+					       const char *caller);
+extern struct gfs2_buffer_head *__bget(struct gfs2_sbd *sdp, uint64_t num,
+				       int line, const char *caller);
+extern struct gfs2_buffer_head *__bread(struct gfs2_sbd *sdp, uint64_t num,
+					int line, const char *caller);
+extern struct gfs2_buffer_head *bget(struct gfs2_sbd *sdp, uint64_t num);
+extern int __breadm(struct gfs2_sbd *sdp, struct gfs2_buffer_head **bhs, size_t n, uint64_t block, int line, const char *caller);
+extern int bwrite(struct gfs2_buffer_head *bh);
+extern int brelse(struct gfs2_buffer_head *bh);
+
+#define bmodified(bh) do { bh->b_modified = 1; } while(0)
+
+#define bget_generic(bl, num, find, read) __bget_generic(bl, num, find, read, \
+							 __LINE__, \
+							 __FUNCTION__)
+#define bget(bl, num) __bget(bl, num, __LINE__, __FUNCTION__)
+#define bread(bl, num) __bread(bl, num, __LINE__, __FUNCTION__)
+#define breadm(bl, bhs, n, block) __breadm(bl, bhs, n, block, __LINE__, __FUNCTION__)
+#define bsync(bl) do { __bsync(bl, __LINE__, __FUNCTION__); } while(0)
+#define bcommit(bl) do { __bcommit(bl, __LINE__, __FUNCTION__); } while(0)
+
+/* device_geometry.c */
+extern int device_topology(struct gfs2_sbd *sdp);
+extern int device_geometry(struct gfs2_sbd *sdp);
+extern int fix_device_geometry(struct gfs2_sbd *sdp);
+
+/* fs_bits.c */
+#define BFITNOENT (0xFFFFFFFF)
+
+/* functions with blk #'s that are buffer relative */
+extern uint32_t gfs2_bitcount(unsigned char *buffer, unsigned int buflen,
+			      unsigned char state);
+extern unsigned long gfs2_bitfit(const unsigned char *buffer,
+				 const unsigned int buflen,
+				 unsigned long goal, unsigned char old_state);
+
+/* functions with blk #'s that are rgrp relative */
+extern uint32_t gfs2_blkalloc_internal(struct rgrp_tree *rgd, uint32_t goal,
+				       unsigned char old_state,
+				       unsigned char new_state, int do_it);
+extern int gfs2_check_range(struct gfs2_sbd *sdp, uint64_t blkno);
+
+/* functions with blk #'s that are file system relative */
+extern int valid_block(struct gfs2_sbd *sdp, uint64_t blkno);
+extern int gfs2_get_bitmap(struct gfs2_sbd *sdp, uint64_t blkno,
+			   struct rgrp_tree *rgd);
+extern int gfs2_set_bitmap(struct gfs2_sbd *sdp, uint64_t blkno, int state);
+
+/* fs_geometry.c */
+extern void rgblocks2bitblocks(unsigned int bsize, uint32_t *rgblocks,
+			       uint32_t *bitblocks);
+extern uint64_t how_many_rgrps(struct gfs2_sbd *sdp, struct device *dev,
+			       int rgsize_specified);
+extern void compute_rgrp_layout(struct gfs2_sbd *sdp, struct osi_root *rgtree,
+				int rgsize_specified);
+extern void build_rgrps(struct gfs2_sbd *sdp, int write);
+
+/* fs_ops.c */
+#define IS_LEAF     (1)
+#define IS_DINODE   (2)
+
+extern struct metapath *find_metapath(struct gfs2_inode *ip, uint64_t block);
+extern 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);
+extern struct gfs2_inode *inode_get(struct gfs2_sbd *sdp,
+				    struct gfs2_buffer_head *bh);
+extern struct gfs2_inode *inode_read(struct gfs2_sbd *sdp, uint64_t di_addr);
+extern struct gfs2_inode *is_system_inode(struct gfs2_sbd *sdp,
+					  uint64_t block);
+extern void inode_put(struct gfs2_inode **ip);
+extern uint64_t data_alloc(struct gfs2_inode *ip);
+extern uint64_t meta_alloc(struct gfs2_inode *ip);
+extern uint64_t dinode_alloc(struct gfs2_sbd *sdp);
+extern int gfs2_readi(struct gfs2_inode *ip, void *buf, uint64_t offset,
+		      unsigned int size);
+#define gfs2_writei(ip, buf, offset, size) \
+	__gfs2_writei(ip, buf, offset, size, 1)
+extern int __gfs2_writei(struct gfs2_inode *ip, void *buf, uint64_t offset,
+			 unsigned int size, int resize);
+extern struct gfs2_buffer_head *get_file_buf(struct gfs2_inode *ip,
+					     uint64_t lbn, int prealloc);
+extern struct gfs2_buffer_head *init_dinode(struct gfs2_sbd *sdp,
+					    struct gfs2_inum *inum,
+					    unsigned int mode, uint32_t flags,
+					    struct gfs2_inum *parent);
+extern struct gfs2_buffer_head *init_gfs_dinode(struct gfs2_sbd *sdp,
+						struct gfs2_inum *inum,
+						unsigned int mode,
+						uint32_t flags,
+						struct gfs2_inum *parent);
+extern struct gfs2_inode *createi(struct gfs2_inode *dip, const char *filename,
+				  unsigned int mode, uint32_t flags);
+extern struct gfs2_inode *gfs_createi(struct gfs2_inode *dip,
+				      const char *filename, unsigned int mode,
+				      uint32_t flags);
+extern void dirent2_del(struct gfs2_inode *dip, struct gfs2_buffer_head *bh,
+			struct gfs2_dirent *prev, struct gfs2_dirent *cur);
+extern int dir_search(struct gfs2_inode *dip, const char *filename, int len,
+		      unsigned int *type, struct gfs2_inum *inum);
+extern int gfs2_lookupi(struct gfs2_inode *dip, const char *filename, int len,
+			struct gfs2_inode **ipp);
+extern int dir_add(struct gfs2_inode *dip, const char *filename, int len,
+		    struct gfs2_inum *inum, unsigned int type);
+extern int gfs2_dirent_del(struct gfs2_inode *dip, const char *filename,
+			   int filename_len);
+extern void block_map(struct gfs2_inode *ip, uint64_t lblock, int *new,
+		      uint64_t *dblock, uint32_t *extlen, int prealloc);
+extern void gfs2_get_leaf_nr(struct gfs2_inode *dip, uint32_t index,
+			     uint64_t *leaf_out);
+extern void gfs2_put_leaf_nr(struct gfs2_inode *dip, uint32_t inx, uint64_t leaf_out);
+extern void dir_split_leaf(struct gfs2_inode *dip, uint32_t start,
+			   uint64_t leaf_no, struct gfs2_buffer_head *obh);
+extern void gfs2_free_block(struct gfs2_sbd *sdp, uint64_t block);
+extern int gfs2_freedi(struct gfs2_sbd *sdp, uint64_t block);
+extern int gfs2_get_leaf(struct gfs2_inode *dip, uint64_t leaf_no,
+			 struct gfs2_buffer_head **bhp);
+extern int gfs2_dirent_first(struct gfs2_inode *dip,
+			     struct gfs2_buffer_head *bh,
+			     struct gfs2_dirent **dent);
+extern int gfs2_dirent_next(struct gfs2_inode *dip, struct gfs2_buffer_head *bh,
+			    struct gfs2_dirent **dent);
+extern void build_height(struct gfs2_inode *ip, int height);
+extern void unstuff_dinode(struct gfs2_inode *ip);
+extern unsigned int calc_tree_height(struct gfs2_inode *ip, uint64_t size);
+extern int write_journal(struct gfs2_sbd *sdp, unsigned int j,
+			 unsigned int blocks);
+
+/**
+ * device_size - figure out a device's size
+ * @fd: the file descriptor of a device
+ * @bytes: the number of bytes the device holds
+ *
+ * Returns: -1 on error (with errno set), 0 on success (with @bytes set)
+ */
+
+extern int device_size(int fd, uint64_t *bytes);
+
+/* gfs1.c - GFS1 backward compatibility structures and functions */
+
+#define GFS_FORMAT_SB           (100)  /* Super-Block */
+#define GFS_METATYPE_SB         (1)    /* Super-Block */
+#define GFS_FORMAT_FS           (1309) /* Filesystem (all-encompassing) */
+#define GFS_FORMAT_MULTI        (1401) /* Multi-Host */
+/* GFS1 Dinode types  */
+#define GFS_FILE_NON            (0)
+#define GFS_FILE_REG            (1)    /* regular file */
+#define GFS_FILE_DIR            (2)    /* directory */
+#define GFS_FILE_LNK            (5)    /* link */
+#define GFS_FILE_BLK            (7)    /* block device node */
+#define GFS_FILE_CHR            (8)    /* character device node */
+#define GFS_FILE_FIFO           (101)  /* fifo/pipe */
+#define GFS_FILE_SOCK           (102)  /* socket */
+
+/* GFS 1 journal block types: */
+#define GFS_LOG_DESC_METADATA   (300)    /* metadata */
+#define GFS_LOG_DESC_IUL        (400)    /* unlinked inode */
+#define GFS_LOG_DESC_IDA        (401)    /* de-allocated inode */
+#define GFS_LOG_DESC_Q          (402)    /* quota */
+#define GFS_LOG_DESC_LAST       (500)    /* final in a logged transaction */
+
+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];
+};
+
+struct gfs_sb {
+	/*  Order is important; need to be able to read old superblocks
+	    in order to support on-disk version upgrades */
+	struct gfs2_meta_header sb_header;
+
+	uint32_t sb_fs_format;         /* GFS_FORMAT_FS (on-disk version) */
+	uint32_t sb_multihost_format;  /* GFS_FORMAT_MULTI */
+	uint32_t sb_flags;             /* ?? */
+
+	uint32_t sb_bsize;             /* fundamental FS block size in bytes */
+	uint32_t sb_bsize_shift;       /* log2(sb_bsize) */
+	uint32_t sb_seg_size;          /* Journal segment size in FS blocks */
+
+	/* These special inodes do not appear in any on-disk directory. */
+	struct gfs2_inum sb_jindex_di;  /* journal index inode */
+	struct gfs2_inum sb_rindex_di;  /* resource group index inode */
+	struct gfs2_inum sb_root_di;    /* root directory inode */
+
+	/* Default inter-node locking protocol (lock module) and namespace */
+	char sb_lockproto[GFS2_LOCKNAME_LEN]; /* lock protocol name */
+	char sb_locktable[GFS2_LOCKNAME_LEN]; /* unique name for this FS */
+
+	/* More special inodes */
+	struct gfs2_inum sb_quota_di;   /* quota inode */
+	struct gfs2_inum sb_license_di; /* license inode */
+
+	char sb_reserved[96];
+};
+
+struct gfs_rgrp {
+	struct gfs2_meta_header rg_header;
+
+	uint32_t rg_flags;      /* ?? */
+
+	uint32_t rg_free;       /* Number (qty) of free data blocks */
+
+	/* Dinodes are USEDMETA, but are handled separately from other METAs */
+	uint32_t rg_useddi;     /* Number (qty) of dinodes (used or free) */
+	uint32_t rg_freedi;     /* Number (qty) of unused (free) dinodes */
+	struct gfs2_inum rg_freedi_list; /* 1st block in chain of free dinodes */
+
+	/* These META statistics do not include dinodes (used or free) */
+	uint32_t rg_usedmeta;   /* Number (qty) of used metadata blocks */
+	uint32_t rg_freemeta;   /* Number (qty) of unused metadata blocks */
+
+	char rg_reserved[64];
+};
+
+struct gfs_log_header {
+	struct gfs2_meta_header lh_header;
+
+	uint32_t lh_flags;      /* GFS_LOG_HEAD_... */
+	uint32_t lh_pad;
+
+	uint64_t lh_first;     /* Block number of first header in this trans */
+	uint64_t lh_sequence;   /* Sequence number of this transaction */
+
+	uint64_t lh_tail;       /* Block number of log tail */
+	uint64_t lh_last_dump;  /* Block number of last dump */
+
+	char lh_reserved[64];
+};
+
+struct gfs_rindex {
+	uint64_t ri_addr;     /* block # of 1st block (header) in rgrp */
+	uint32_t ri_length;   /* # fs blocks containing rgrp header & bitmap */
+	uint32_t ri_pad;
+
+	uint64_t ri_data1;    /* block # of first data/meta block in rgrp */
+	uint32_t ri_data;     /* number (qty) of data/meta blocks in rgrp */
+
+	uint32_t ri_bitbytes; /* total # bytes used by block alloc bitmap */
+
+	char ri_reserved[64];
+};
+
+struct gfs_jindex {
+        uint64_t ji_addr;       /* starting block of the journal */
+        uint32_t ji_nsegment;   /* number (quantity) of segments in journal */
+        uint32_t ji_pad;
+
+        char ji_reserved[64];
+};
+
+struct gfs_log_descriptor {
+	struct gfs2_meta_header ld_header;
+
+	uint32_t ld_type;       /* GFS_LOG_DESC_... Type of this log chunk */
+	uint32_t ld_length;     /* Number of buffers in this chunk */
+	uint32_t ld_data1;      /* descriptor-specific field */
+	uint32_t ld_data2;      /* descriptor-specific field */
+	char ld_reserved[64];
+};
+
+extern int is_gfs_dir(struct gfs2_dinode *dinode);
+extern 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);
+extern void gfs1_block_map(struct gfs2_inode *ip, uint64_t lblock, int *new,
+			   uint64_t *dblock, uint32_t *extlen, int prealloc);
+extern int gfs1_writei(struct gfs2_inode *ip, char *buf, uint64_t offset,
+		       unsigned int size);
+extern int gfs1_ri_update(struct gfs2_sbd *sdp, int fd, int *rgcount, int quiet);
+extern struct gfs2_inode *gfs_inode_get(struct gfs2_sbd *sdp,
+					struct gfs2_buffer_head *bh);
+extern struct gfs2_inode *gfs_inode_read(struct gfs2_sbd *sdp,
+					 uint64_t di_addr);
+extern void gfs_jindex_in(struct gfs_jindex *jindex, char *buf);
+extern void gfs_rgrp_in(struct gfs_rgrp *rg, struct gfs2_buffer_head *bh);
+extern void gfs_rgrp_out(struct gfs_rgrp *rg, struct gfs2_buffer_head *bh);
+extern void gfs_get_leaf_nr(struct gfs2_inode *dip, uint32_t lindex,
+			    uint64_t *leaf_out);
+extern void gfs_put_leaf_nr(struct gfs2_inode *dip, uint32_t inx,
+			    uint64_t leaf_out);
+
+/* gfs2_log.c */
+struct gfs2_options {
+	char *device;
+	unsigned int yes:1;
+	unsigned int no:1;
+	unsigned int query:1;
+};
+
+extern int print_level;
+
+#define MSG_DEBUG       7
+#define MSG_INFO        6
+#define MSG_NOTICE      5
+#define MSG_WARN        4
+#define MSG_ERROR       3
+#define MSG_CRITICAL    2
+#define MSG_NULL        1
+
+#define print_log(priority, format...) \
+	do { print_fsck_log(priority, __FUNCTION__, __LINE__, ## format); } while(0)
+
+#define log_debug(format...) \
+	do { if(print_level >= MSG_DEBUG) print_log(MSG_DEBUG, format); } while(0)
+#define log_info(format...) \
+	do { if(print_level >= MSG_INFO) print_log(MSG_INFO, format); } while(0)
+
+#define log_notice(format...) \
+	do { if(print_level >= MSG_NOTICE) print_log(MSG_NOTICE, format); } while(0)
+
+#define log_warn(format...) \
+	do { if(print_level >= MSG_WARN) print_log(MSG_WARN, format); } while(0)
+
+#define log_err(format...) \
+	do { if(print_level >= MSG_ERROR) print_log(MSG_ERROR, format); } while(0)
+
+#define log_crit(format...) \
+	do { if(print_level >= MSG_CRITICAL) print_log(MSG_CRITICAL, format); } while(0)
+
+#define stack log_debug("<backtrace> - %s()\n", __func__)
+
+extern char gfs2_getch(void);
+extern void increase_verbosity(void);
+extern void decrease_verbosity(void);
+extern void print_fsck_log(int priority, const char *file, int line,
+			   const char *format, ...)
+	__attribute__((format(printf,4,5)));
+extern char generic_interrupt(const char *caller, const char *where,
+			      const char *progress, const char *question,
+			      const char *answers);
+extern int gfs2_query(int *setonabort, struct gfs2_options *opts,
+		      const char *format, ...)
+	__attribute__((format(printf,3,4)));
+
+/* misc.c */
+
+extern char *get_list(void);
+extern int compute_heightsize(struct gfs2_sbd *sdp, uint64_t *heightsize,
+			      uint32_t bsize1, int diptrs, int inptrs);
+extern int compute_constants(struct gfs2_sbd *sdp);
+extern int is_pathname_mounted(struct gfs2_sbd *sdp, int *ro_mount);
+extern int is_gfs2(struct gfs2_sbd *sdp);
+extern int find_gfs2_meta(struct gfs2_sbd *sdp);
+extern int dir_exists(const char *dir);
+extern int check_for_gfs2(struct gfs2_sbd *sdp);
+extern int mount_gfs2_meta(struct gfs2_sbd *sdp);
+extern void cleanup_metafs(struct gfs2_sbd *sdp);
+extern char *find_debugfs_mount(void);
+extern char *mp2fsname(char *mp);
+extern char *get_sysfs(const char *fsname, const char *filename);
+extern int get_sysfs_uint(const char *fsname, const char *filename, unsigned int *val);
+extern int set_sysfs(const char *fsname, const char *filename, const char *val);
+extern int is_fsname(char *name);
+extern void get_random_bytes(void *buf, int nbytes);
+
+/* recovery.c */
+extern void gfs2_replay_incr_blk(struct gfs2_inode *ip, unsigned int *blk);
+extern int gfs2_replay_read_block(struct gfs2_inode *ip, unsigned int blk,
+				  struct gfs2_buffer_head **bh);
+extern int gfs2_revoke_add(struct gfs2_sbd *sdp, uint64_t blkno, unsigned int where);
+extern int gfs2_revoke_check(struct gfs2_sbd *sdp, uint64_t blkno,
+			     unsigned int where);
+extern void gfs2_revoke_clean(struct gfs2_sbd *sdp);
+extern int get_log_header(struct gfs2_inode *ip, unsigned int blk,
+			  struct gfs2_log_header *head);
+extern int find_good_lh(struct gfs2_inode *ip, unsigned int *blk,
+			struct gfs2_log_header *head);
+extern int jhead_scan(struct gfs2_inode *ip, struct gfs2_log_header *head);
+extern int gfs2_find_jhead(struct gfs2_inode *ip, struct gfs2_log_header *head);
+extern int clean_journal(struct gfs2_inode *ip, struct gfs2_log_header *head);
+
+/* rgrp.c */
+extern int gfs2_compute_bitstructs(struct gfs2_sbd *sdp, struct rgrp_tree *rgd);
+extern struct rgrp_tree *gfs2_blk2rgrpd(struct gfs2_sbd *sdp, uint64_t blk);
+extern uint64_t gfs2_rgrp_read(struct gfs2_sbd *sdp, struct rgrp_tree *rgd);
+extern void gfs2_rgrp_relse(struct rgrp_tree *rgd);
+extern struct rgrp_tree *rgrp_insert(struct osi_root *rgtree,
+				     uint64_t rgblock);
+extern void gfs2_rgrp_free(struct osi_root *rgrp_tree);
+/* figure out the size of the given resource group, in blocks */
+static inline unsigned int rgrp_size(struct rgrp_tree *rgrp)
+{
+	return rgrp->ri.ri_data + rgrp->ri.ri_length;
+}
+
+/* structures.c */
+extern int build_master(struct gfs2_sbd *sdp);
+extern void build_sb(struct gfs2_sbd *sdp, const unsigned char *uuid);
+extern int build_journal(struct gfs2_sbd *sdp, int j,
+			 struct gfs2_inode *jindex);
+extern int build_jindex(struct gfs2_sbd *sdp);
+extern int build_per_node(struct gfs2_sbd *sdp);
+extern int build_inum(struct gfs2_sbd *sdp);
+extern int build_statfs(struct gfs2_sbd *sdp);
+extern int build_rindex(struct gfs2_sbd *sdp);
+extern int build_quota(struct gfs2_sbd *sdp);
+extern int build_root(struct gfs2_sbd *sdp);
+extern int do_init_inum(struct gfs2_sbd *sdp);
+extern int do_init_statfs(struct gfs2_sbd *sdp);
+extern int gfs2_check_meta(struct gfs2_buffer_head *bh, int type);
+extern int gfs2_next_rg_meta(struct rgrp_tree *rgd, uint64_t *block,
+			     int first);
+extern int gfs2_next_rg_metatype(struct gfs2_sbd *sdp, struct rgrp_tree *rgd,
+				 uint64_t *block, uint32_t type, int first);
+extern int gfs2_next_rg_freemeta(struct rgrp_tree *rgd, uint64_t *block,
+				 int first);
+
+/* super.c */
+extern int check_sb(struct gfs2_sb *sb, int allow_gfs);
+extern int read_sb(struct gfs2_sbd *sdp, int allow_gfs);
+extern int rindex_read(struct gfs2_sbd *sdp, int fd, int *count1, int *sane);
+extern int ri_update(struct gfs2_sbd *sdp, int fd, int *rgcount, int *sane);
+extern int write_sb(struct gfs2_sbd *sdp);
+
+/* ondisk.c */
+extern uint32_t gfs2_disk_hash(const char *data, int len);
+extern const char *str_uuid(const unsigned char *uuid);
+extern void gfs2_print_uuid(const unsigned char *uuid);
+extern void print_it(const char *label, const char *fmt, const char *fmt2, ...)
+	__attribute__((format(printf,2,4)));
+
+/* Translation functions */
+
+extern void gfs2_inum_in(struct gfs2_inum *no, char *buf);
+extern void gfs2_inum_out(struct gfs2_inum *no, char *buf);
+extern void gfs2_meta_header_in(struct gfs2_meta_header *mh,
+				struct gfs2_buffer_head *bh);
+extern void gfs2_meta_header_out(struct gfs2_meta_header *mh,
+				 struct gfs2_buffer_head *bh);
+extern void gfs2_sb_in(struct gfs2_sb *sb, struct gfs2_buffer_head *bh);
+extern void gfs2_sb_out(struct gfs2_sb *sb, struct gfs2_buffer_head *bh);
+extern void gfs2_rindex_in(struct gfs2_rindex *ri, char *buf);
+extern void gfs2_rindex_out(struct gfs2_rindex *ri, char *buf);
+extern void gfs2_rgrp_in(struct gfs2_rgrp *rg, struct gfs2_buffer_head *bh);
+extern void gfs2_rgrp_out(struct gfs2_rgrp *rg, struct gfs2_buffer_head *bh);
+extern void gfs2_quota_in(struct gfs2_quota *qu, char *buf);
+extern void gfs2_quota_out(struct gfs2_quota *qu, char *buf);
+extern void gfs2_dinode_in(struct gfs2_dinode *di,
+			   struct gfs2_buffer_head *bh);
+extern void gfs2_dinode_out(struct gfs2_dinode *di,
+			    struct gfs2_buffer_head *bh);
+extern void gfs2_dirent_in(struct gfs2_dirent *de, char *buf);
+extern void gfs2_dirent_out(struct gfs2_dirent *de, char *buf);
+extern void gfs2_leaf_in(struct gfs2_leaf *lf, struct gfs2_buffer_head *bh);
+extern void gfs2_leaf_out(struct gfs2_leaf *lf, struct gfs2_buffer_head *bh);
+extern void gfs2_ea_header_in(struct gfs2_ea_header *ea, char *buf);
+extern void gfs2_ea_header_out(struct gfs2_ea_header *ea, char *buf);
+extern void gfs2_log_header_in(struct gfs2_log_header *lh,
+			       struct gfs2_buffer_head *bh);
+extern void gfs2_log_header_out(struct gfs2_log_header *lh,
+				struct gfs2_buffer_head *bh);
+extern void gfs2_log_descriptor_in(struct gfs2_log_descriptor *ld,
+				   struct gfs2_buffer_head *bh);
+extern void gfs2_log_descriptor_out(struct gfs2_log_descriptor *ld,
+				    struct gfs2_buffer_head *bh);
+extern void gfs2_statfs_change_in(struct gfs2_statfs_change *sc, char *buf);
+extern void gfs2_statfs_change_out(struct gfs2_statfs_change *sc, char *buf);
+extern void gfs2_quota_change_in(struct gfs2_quota_change *qc,
+				 struct gfs2_buffer_head *bh);
+extern void gfs2_quota_change_out(struct gfs2_quota_change *qc,
+				  struct gfs2_buffer_head *bh);
+
+/* Printing functions */
+
+extern void gfs2_inum_print(struct gfs2_inum *no);
+extern void gfs2_meta_header_print(struct gfs2_meta_header *mh);
+extern void gfs2_sb_print(struct gfs2_sb *sb);
+extern void gfs2_rindex_print(struct gfs2_rindex *ri);
+extern void gfs2_rgrp_print(struct gfs2_rgrp *rg);
+extern void gfs2_quota_print(struct gfs2_quota *qu);
+extern void gfs2_dinode_print(struct gfs2_dinode *di);
+extern void gfs2_dirent_print(struct gfs2_dirent *de, char *name);
+extern void gfs2_leaf_print(struct gfs2_leaf *lf);
+extern void gfs2_ea_header_print(struct gfs2_ea_header *ea, char *name);
+extern void gfs2_log_header_print(struct gfs2_log_header *lh);
+extern void gfs2_log_descriptor_print(struct gfs2_log_descriptor *ld);
+extern void gfs2_statfs_change_print(struct gfs2_statfs_change *sc);
+extern void gfs2_quota_change_print(struct gfs2_quota_change *qc);
+
+__END_DECLS
+
+#endif /* __LIBGFS2_DOT_H__ */
diff --git a/gfs2/fsck/link.c b/gfs2/fsck/link.c
index 9482b34..c2b1a95 100644
--- a/gfs2/fsck/link.c
+++ b/gfs2/fsck/link.c
@@ -1,16 +1,3 @@
-/******************************************************************************
-*******************************************************************************
-**
-**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
-**  Copyright (C) 2004-2005 Red Hat, Inc.  All rights reserved.
-**
-**  This copyrighted material is made available to anyone wishing to use,
-**  modify, copy, or redistribute it subject to the terms and conditions
-**  of the GNU General Public License v.2.
-**
-*******************************************************************************
-******************************************************************************/
-
 #include <inttypes.h>
 #include <stdlib.h>
 #include <stdint.h>
diff --git a/gfs2/fsck/link.h b/gfs2/fsck/link.h
index befb534..842afb9 100644
--- a/gfs2/fsck/link.h
+++ b/gfs2/fsck/link.h
@@ -1,17 +1,3 @@
-/******************************************************************************
-*******************************************************************************
-**
-**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
-**  Copyright (C) 2004-2005 Red Hat, Inc.  All rights reserved.
-**
-**  This copyrighted material is made available to anyone wishing to use,
-**  modify, copy, or redistribute it subject to the terms and conditions
-**  of the GNU General Public License v.2.
-**
-*******************************************************************************
-******************************************************************************/
-
-
 #ifndef _LINK_H
 #define _LINK_H
 
diff --git a/gfs2/fsck/lost_n_found.c b/gfs2/fsck/lost_n_found.c
index f90c155..c8265a2 100644
--- a/gfs2/fsck/lost_n_found.c
+++ b/gfs2/fsck/lost_n_found.c
@@ -1,16 +1,3 @@
-/******************************************************************************
-*******************************************************************************
-**
-**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
-**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
-**
-**  This copyrighted material is made available to anyone wishing to use,
-**  modify, copy, or redistribute it subject to the terms and conditions
-**  of the GNU General Public License v.2.
-**
-*******************************************************************************
-******************************************************************************/
-
 #include <inttypes.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -19,7 +6,6 @@
 #include <unistd.h>
 #include <dirent.h>
 #include <libintl.h>
-#include <errno.h>
 #define _(String) gettext(String)
 
 #include "fsck.h"
@@ -46,52 +32,61 @@ static void add_dotdot(struct gfs2_inode *ip)
 	if (di && valid_block(sdp, di->dotdot_parent.no_addr)) {
 		struct gfs2_inode *dip;
 
-		log_debug(_("Directory %lld (0x%llx) already had a "
-			    "\"..\" link to %lld (0x%llx).\n"),
-			  (unsigned long long)ip->i_di.di_num.no_addr,
+		log_debug(_("Directory (0x%llx) already had a "
+			    "\"..\" link to (0x%llx).\n"),
 			  (unsigned long long)ip->i_di.di_num.no_addr,
-			  (unsigned long long)di->dotdot_parent.no_addr,
 			  (unsigned long long)di->dotdot_parent.no_addr);
-		decr_link_count(di->dotdot_parent.no_addr,
-				ip->i_di.di_num.no_addr,
-				_(".. unlinked, moving to lost+found"));
 		dip = fsck_load_inode(sdp, di->dotdot_parent.no_addr);
-		if (dip->i_di.di_nlink > 0) {
-			dip->i_di.di_nlink--;
-			set_di_nlink(dip); /* keep inode tree in sync */
-			log_debug(_("Decrementing its links to %d\n"),
-				  dip->i_di.di_nlink);
-			bmodified(dip->i_bh);
-		} else if (!dip->i_di.di_nlink) {
-			log_debug(_("Its link count is zero.\n"));
+		if (dip->i_di.di_num.no_formal_ino ==
+		    di->dotdot_parent.no_formal_ino) {
+			decr_link_count(di->dotdot_parent.no_addr,
+					ip->i_di.di_num.no_addr,
+					_(".. unlinked, moving to lost+found"));
+			if (dip->i_di.di_nlink > 0) {
+			  dip->i_di.di_nlink--;
+			  set_di_nlink(dip); /* keep inode tree in sync */
+			  log_debug(_("Decrementing its links to %d\n"),
+				    dip->i_di.di_nlink);
+			  bmodified(dip->i_bh);
+			} else if (!dip->i_di.di_nlink) {
+			  log_debug(_("Its link count is zero.\n"));
+			} else {
+			  log_debug(_("Its link count is %d!  Changing "
+				      "it to 0.\n"), dip->i_di.di_nlink);
+			  dip->i_di.di_nlink = 0;
+			  set_di_nlink(dip); /* keep inode tree in sync */
+			  bmodified(dip->i_bh);
+			}
 		} else {
-			log_debug(_("Its link count is %d!  Changing "
-				    "it to 0.\n"), dip->i_di.di_nlink);
-			dip->i_di.di_nlink = 0;
-			set_di_nlink(dip); /* keep inode tree in sync */
-			bmodified(dip->i_bh);
+			log_debug(_("Directory (0x%llx)'s link to parent "
+				    "(0x%llx) had a formal inode discrepancy: "
+				    "was 0x%llx, expected 0x%llx\n"),
+				  (unsigned long long)ip->i_di.di_num.no_addr,
+				  (unsigned long long)di->dotdot_parent.no_addr,
+				  di->dotdot_parent.no_formal_ino,
+				  dip->i_di.di_num.no_formal_ino);
+			log_debug(_("The parent directory was not changed.\n"));
 		}
 		fsck_inode_put(&dip);
 		di = NULL;
 	} else {
 		if (di)
 			log_debug(_("Couldn't find a valid \"..\" entry "
-				    "for orphan directory %lld (0x%llx): "
+				    "for orphan directory (0x%llx): "
 				    "'..' = 0x%llx\n"),
 				  (unsigned long long)ip->i_di.di_num.no_addr,
-				  (unsigned long long)ip->i_di.di_num.no_addr,
 				  (unsigned long long)di->dotdot_parent.no_addr);
 		else
-			log_debug(_("Couldn't find directory %lld (0x%llx) "
+			log_debug(_("Couldn't find directory (0x%llx) "
 				    "in directory tree.\n"),
-				  (unsigned long long)ip->i_di.di_num.no_addr,
 				  (unsigned long long)ip->i_di.di_num.no_addr);
 	}
 	if (gfs2_dirent_del(ip, "..", 2))
 		log_warn( _("add_inode_to_lf:  Unable to remove "
 			    "\"..\" directory entry.\n"));
 
-	err = dir_add(ip, "..", 2, &(lf_dip->i_di.di_num), DT_DIR);
+	err = dir_add(ip, "..", 2, &(lf_dip->i_di.di_num),
+		      (sdp->gfs1 ? GFS_FILE_DIR : DT_DIR));
 	if (err) {
 		log_crit(_("Error adding .. directory: %s\n"),
 			 strerror(errno));
@@ -99,6 +94,71 @@ static void add_dotdot(struct gfs2_inode *ip)
 	}
 }
 
+void make_sure_lf_exists(struct gfs2_inode *ip)
+{
+	uint8_t q;
+	struct dir_info *di;
+	struct gfs2_sbd *sdp = ip->i_sbd;
+	uint32_t mode;
+
+	if (lf_dip)
+		return;
+
+	log_info( _("Locating/Creating lost+found directory\n"));
+
+	/* if this is gfs1, we have to trick createi into using
+	   no_formal_ino = no_addr, so we set next_inum to the
+	   free block we're about to allocate. */
+	if (sdp->gfs1)
+		sdp->md.next_inum = find_free_blk(sdp);
+	mode = (sdp->gfs1 ? DT2IF(GFS_FILE_DIR) : S_IFDIR) | 0700;
+	if (sdp->gfs1)
+		lf_dip = gfs_createi(sdp->md.rooti, "lost+found", mode, 0);
+	else
+		lf_dip = createi(sdp->md.rooti, "lost+found",
+				 S_IFDIR | 0700, 0);
+	if (lf_dip == NULL) {
+		log_crit(_("Error creating lost+found: %s\n"),
+			 strerror(errno));
+		exit(FSCK_ERROR);
+	}
+
+	/* createi will have incremented the di_nlink link count for the root
+	   directory.  We must set the nlink value in the hash table to keep
+	   them in sync so that pass4 can detect and fix any descrepancies. */
+	set_di_nlink(sdp->md.rooti);
+
+	q = block_type(lf_dip->i_di.di_num.no_addr);
+	if (q != gfs2_inode_dir) {
+		lf_was_created = 1;
+		/* This is a new lost+found directory, so set its block type
+		   and increment link counts for the directories */
+		/* FIXME: i'd feel better about this if fs_mkdir returned
+		   whether it created a new directory or just found an old one,
+		   and we used that instead of the block_type to run this */
+		fsck_blockmap_set(ip, lf_dip->i_di.di_num.no_addr,
+				  _("lost+found dinode"), gfs2_inode_dir);
+		dirtree_insert(lf_dip->i_di.di_num);
+		/* root inode links to lost+found */
+		incr_link_count(sdp->md.rooti->i_di.di_num, lf_dip, _("root"));
+		/* lost+found link for '.' from itself */
+		incr_link_count(lf_dip->i_di.di_num, lf_dip, "\".\"");
+		/* lost+found link for '..' back to root */
+		incr_link_count(lf_dip->i_di.di_num, sdp->md.rooti, "\"..\"");
+		if (sdp->gfs1)
+			lf_dip->i_di.__pad1 = GFS_FILE_DIR;
+	}
+	log_info( _("lost+found directory is dinode %lld (0x%llx)\n"),
+		  (unsigned long long)lf_dip->i_di.di_num.no_addr,
+		  (unsigned long long)lf_dip->i_di.di_num.no_addr);
+	di = dirtree_find(lf_dip->i_di.di_num.no_addr);
+	if (di) {
+		log_info( _("Marking lost+found inode connected\n"));
+		di->checked = 1;
+		di = NULL;
+	}
+}
+
 /* add_inode_to_lf - Add dir entry to lost+found for the inode
  * @ip: inode to add to lost + found
  *
@@ -113,103 +173,62 @@ int add_inode_to_lf(struct gfs2_inode *ip){
 	__be32 inode_type;
 	uint64_t lf_blocks;
 	struct gfs2_sbd *sdp = ip->i_sbd;
-	struct dir_info *di;
 	int err = 0;
 	uint32_t mode;
 
-	if (!lf_dip) {
-		uint8_t q;
-
-		log_info( _("Locating/Creating lost+found directory\n"));
-
-		lf_dip = createi(sdp->md.rooti, "lost+found",
-				 S_IFDIR | 0700, 0);
-		if (lf_dip == NULL) {
-			log_crit(_("Error %d creating lost+found\n"), errno);
-			exit(FSCK_ERROR);
-		}
-
-		/* createi will have incremented the di_nlink link count for
-		   the root directory.  We must increment the nlink value
-		   in the hash table to keep them in sync so that pass4 can
-		   detect and fix any descrepancies. */
-		set_di_nlink(sdp->md.rooti);
-
-		q = block_type(lf_dip->i_di.di_num.no_addr);
-		if (q != gfs2_inode_dir) {
-			/* This is a new lost+found directory, so set its
-			 * block type and increment link counts for
-			 * the directories */
-			/* FIXME: i'd feel better about this if
-			 * fs_mkdir returned whether it created a new
-			 * directory or just found an old one, and we
-			 * used that instead of the block_type to run
-			 * this */
-			fsck_blockmap_set(ip, lf_dip->i_di.di_num.no_addr,
-					  _("lost+found dinode"),
-					  gfs2_inode_dir);
-			/* root inode links to lost+found */
-			incr_link_count(sdp->md.rooti->i_di.di_num,
-				       lf_dip, _("root"));
-			/* lost+found link for '.' from itself */
-			incr_link_count(lf_dip->i_di.di_num,
-					lf_dip, "\".\"");
-			/* lost+found link for '..' back to root */
-			incr_link_count(lf_dip->i_di.di_num, sdp->md.rooti,
-				       "\"..\"");
-		}
-		log_info( _("lost+found directory is dinode %lld (0x%llx)\n"),
-			  (unsigned long long)lf_dip->i_di.di_num.no_addr,
-			  (unsigned long long)lf_dip->i_di.di_num.no_addr);
-		di = dirtree_find(lf_dip->i_di.di_num.no_addr);
-		if (di) {
-			log_info( _("Marking lost+found inode connected\n"));
-			di->checked = 1;
-			di = NULL;
-		}
-	}
+	make_sure_lf_exists(ip);
 	if (ip->i_di.di_num.no_addr == lf_dip->i_di.di_num.no_addr) {
 		log_err( _("Trying to add lost+found to itself...skipping"));
 		return 0;
 	}
 	lf_blocks = lf_dip->i_di.di_blocks;
 
-	mode = ip->i_di.di_mode & S_IFMT;
+	if (sdp->gfs1)
+		mode = gfs_to_gfs2_mode(ip);
+	else
+		mode = ip->i_di.di_mode & S_IFMT;
 
 	switch (mode) {
 	case S_IFDIR:
 		add_dotdot(ip);
 		sprintf(tmp_name, "lost_dir_%llu",
 			(unsigned long long)ip->i_di.di_num.no_addr);
-		inode_type = DT_DIR;
+		inode_type = (sdp->gfs1 ? GFS_FILE_DIR : DT_DIR);
 		break;
 	case S_IFREG:
-		sprintf(tmp_name, "lost_file_%llu", ip->i_di.di_num.no_addr);
-		inode_type = DT_REG;
+		sprintf(tmp_name, "lost_file_%llu",
+			(unsigned long long)ip->i_di.di_num.no_addr);
+		inode_type = (sdp->gfs1 ? GFS_FILE_REG : DT_REG);
 		break;
 	case S_IFLNK:
-		sprintf(tmp_name, "lost_link_%llu", ip->i_di.di_num.no_addr);
-		inode_type = DT_LNK;
+		sprintf(tmp_name, "lost_link_%llu",
+			(unsigned long long)ip->i_di.di_num.no_addr);
+		inode_type = (sdp->gfs1 ? GFS_FILE_LNK : DT_LNK);
 		break;
 	case S_IFBLK:
-		sprintf(tmp_name, "lost_blkdev_%llu", ip->i_di.di_num.no_addr);
-		inode_type = DT_BLK;
+		sprintf(tmp_name, "lost_blkdev_%llu",
+			(unsigned long long)ip->i_di.di_num.no_addr);
+		inode_type = (sdp->gfs1 ? GFS_FILE_BLK : DT_BLK);
 		break;
 	case S_IFCHR:
-		sprintf(tmp_name, "lost_chrdev_%llu", ip->i_di.di_num.no_addr);
-		inode_type = DT_CHR;
+		sprintf(tmp_name, "lost_chrdev_%llu",
+			(unsigned long long)ip->i_di.di_num.no_addr);
+		inode_type = (sdp->gfs1 ? GFS_FILE_CHR : DT_CHR);
 		break;
 	case S_IFIFO:
-		sprintf(tmp_name, "lost_fifo_%llu", ip->i_di.di_num.no_addr);
-		inode_type = DT_FIFO;
+		sprintf(tmp_name, "lost_fifo_%llu",
+			(unsigned long long)ip->i_di.di_num.no_addr);
+		inode_type = (sdp->gfs1 ? GFS_FILE_FIFO : DT_FIFO);
 		break;
 	case S_IFSOCK:
-		sprintf(tmp_name, "lost_socket_%llu", ip->i_di.di_num.no_addr);
-		inode_type = DT_SOCK;
+		sprintf(tmp_name, "lost_socket_%llu",
+			(unsigned long long)ip->i_di.di_num.no_addr);
+		inode_type = (sdp->gfs1 ? GFS_FILE_SOCK : DT_SOCK);
 		break;
 	default:
-		sprintf(tmp_name, "lost_%llu", ip->i_di.di_num.no_addr);
-		inode_type = DT_REG;
+		sprintf(tmp_name, "lost_%llu",
+			(unsigned long long)ip->i_di.di_num.no_addr);
+		inode_type = (sdp->gfs1 ? GFS_FILE_REG : DT_REG);
 		break;
 	}
 
diff --git a/gfs2/fsck/lost_n_found.h b/gfs2/fsck/lost_n_found.h
index 44bfffb..2b76cc2 100644
--- a/gfs2/fsck/lost_n_found.h
+++ b/gfs2/fsck/lost_n_found.h
@@ -1,21 +1,9 @@
-/******************************************************************************
-*******************************************************************************
-**
-**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
-**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
-**
-**  This copyrighted material is made available to anyone wishing to use,
-**  modify, copy, or redistribute it subject to the terms and conditions
-**  of the GNU General Public License v.2.
-**
-*******************************************************************************
-******************************************************************************/
-
 #ifndef __LOST_N_FOUND_H__
 #define __LOST_N_FOUND_H__
 
 #include "libgfs2.h"
 
 int add_inode_to_lf(struct gfs2_inode *ip);
+void make_sure_lf_exists(struct gfs2_inode *ip);
 
 #endif /* __LOST_N_FOUND_H__ */
diff --git a/gfs2/fsck/main.c b/gfs2/fsck/main.c
index 40aaaab..3d9f79c 100644
--- a/gfs2/fsck/main.c
+++ b/gfs2/fsck/main.c
@@ -1,15 +1,3 @@
-/*****************************************************************************
-******************************************************************************
-**
-**  Copyright (C) 2005-2007 Red Hat, Inc.  All rights reserved.
-**
-**  This copyrighted material is made available to anyone wishing to use,
-**  modify, copy, or redistribute it subject to the terms and conditions
-**  of the GNU General Public License v.2.
-**
-******************************************************************************
-*****************************************************************************/
-
 #include <unistd.h>
 #include <stdio.h>
 #include <stdint.h>
@@ -32,6 +20,7 @@
 
 struct gfs2_options opts = {0};
 struct gfs2_inode *lf_dip = NULL; /* Lost and found directory inode */
+int lf_was_created = 0;
 struct gfs2_bmap *bl = NULL;
 uint64_t last_fs_block, last_reported_block = -1;
 int64_t last_reported_fblock = -1000000;
@@ -40,12 +29,15 @@ int errors_found = 0, errors_corrected = 0;
 const char *pass = "";
 uint64_t last_data_block;
 uint64_t first_data_block;
-const char *prog_name = "gfs2_fsck"; /* needed by libgfs2 */
 int preen = 0, force_check = 0;
 struct osi_root dup_blocks = (struct osi_root) { NULL, };
 struct osi_root dirtree = (struct osi_root) { NULL, };
 struct osi_root inodetree = (struct osi_root) { NULL, };
 int dups_found = 0, dups_found_first = 0;
+struct gfs_sb *sbd1 = NULL;
+int sb_fixed = 0;
+
+extern FILE *brdfp;
 
 /* This function is for libgfs2's sake.                                      */
 void print_it(const char *label, const char *fmt, const char *fmt2, ...)
@@ -166,6 +158,10 @@ static void check_statfs(struct gfs2_sbd *sdp)
 	char buf[sizeof(struct gfs2_statfs_change)];
 	int count;
 
+	if (sdp->gfs1 && !sdp->md.statfs->i_di.di_size) {
+		log_info("This GFS1 file system is not using fast_statfs.\n");
+		return;
+	}
 	/* Read the current statfs values */
 	count = gfs2_readi(sdp->md.statfs, buf, 0,
 			   sdp->md.statfs->i_di.di_size);
@@ -240,6 +236,8 @@ int main(int argc, char **argv)
 
 	memset(sdp, 0, sizeof(*sdp));
 
+/* 	brdfp = fopen("brd-log.txt", "w"); */
+
 	if ((error = read_cmdline(argc, argv, &opts)))
 		exit(error);
 	setbuf(stdout, NULL);
@@ -352,7 +350,8 @@ int main(int argc, char **argv)
 		check_statfs(sdp);
 
 	/* Free up our system inodes */
-	inode_put(&sdp->md.inum);
+	if (!sdp->gfs1)
+		inode_put(&sdp->md.inum);
 	inode_put(&sdp->md.statfs);
 	for (j = 0; j < sdp->md.journals; j++)
 		inode_put(&sdp->md.journal[j]);
@@ -361,9 +360,11 @@ int main(int argc, char **argv)
 	inode_put(&sdp->md.jiinode);
 	inode_put(&sdp->md.riinode);
 	inode_put(&sdp->md.qinode);
-	inode_put(&sdp->md.pinode);
+	if (!sdp->gfs1)
+		inode_put(&sdp->md.pinode);
 	inode_put(&sdp->md.rooti);
-	inode_put(&sdp->master_dir);
+	if (!sdp->gfs1)
+		inode_put(&sdp->master_dir);
 	if (lf_dip)
 		inode_put(&lf_dip);
 
@@ -371,6 +372,9 @@ int main(int argc, char **argv)
 		log_notice( _("Writing changes to disk\n"));
 	fsync(sdp->device_fd);
 	destroy(sdp);
+	if (sb_fixed)
+		log_warn(_("Superblock was reset. Use tunegfs2 to manually "
+		           "set lock table before mounting.\n"));
 	log_notice( _("gfs2_fsck complete\n"));
 
 	if (!error) {
diff --git a/gfs2/fsck/metawalk.c b/gfs2/fsck/metawalk.c
index 035fb34..d1c38d1 100644
--- a/gfs2/fsck/metawalk.c
+++ b/gfs2/fsck/metawalk.c
@@ -1,18 +1,4 @@
-/*****************************************************************************
-*******************************************************************************
-**
-**  Copyright (C) 2005-2007 Red Hat, Inc.  All rights reserved.
-**
-**  This copyrighted material is made available to anyone wishing to use,
-**  modify, copy, or redistribute it subject to the terms and conditions
-**  of the GNU General Public License v.2.
-**
-*******************************************************************************
-******************************************************************************/
-
-#include <errno.h>
 #include <inttypes.h>
-#include <linux_endian.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -21,6 +7,7 @@
 #include <unistd.h>
 #include <libintl.h>
 #include <ctype.h>
+#include <fcntl.h>
 #define _(String) gettext(String)
 
 #include "libgfs2.h"
@@ -28,7 +15,6 @@
 #include "fsck.h"
 #include "util.h"
 #include "metawalk.h"
-#include "hash.h"
 #include "inode_hash.h"
 
 #define COMFORTABLE_BLKS 5242880 /* 20GB in 4K blocks */
@@ -39,7 +25,7 @@
    is used to set the latter.  The two must be kept in sync, otherwise
    you'll get bitmap mismatches.  This function checks the status of the
    bitmap whenever the blockmap changes, and fixes it accordingly. */
-int check_n_fix_bitmap(struct gfs2_sbd *sdp, uint64_t blk,
+int check_n_fix_bitmap(struct gfs2_sbd *sdp, uint64_t blk, int error_on_dinode,
 		       enum gfs2_mark_block new_blockmap_state)
 {
 	int old_bitmap_state, new_bitmap_state;
@@ -49,22 +35,34 @@ int check_n_fix_bitmap(struct gfs2_sbd *sdp, uint64_t blk,
 
 	old_bitmap_state = gfs2_get_bitmap(sdp, blk, rgd);
 	if (old_bitmap_state < 0) {
-		log_err( _("Block %lld (0x%llx) is not represented in the"
+		log_err( _("Block %llu (0x%llx) is not represented in the "
 			   "system bitmap; part of an rgrp or superblock.\n"),
 			 (unsigned long long)blk, (unsigned long long)blk);
 		return -1;
 	}
-	new_bitmap_state = blockmap_to_bitmap(new_blockmap_state);
+	new_bitmap_state = blockmap_to_bitmap(new_blockmap_state, sdp->gfs1);
 	if (old_bitmap_state != new_bitmap_state) {
-		const char *allocdesc[] = {"free space", "data", "unlinked",
-					   "inode", "reserved"};
-
+		const char *allocdesc[2][5] = { /* gfs2 descriptions */
+			{"free", "data", "unlinked", "inode", "reserved"},
+			/* gfs1 descriptions: */
+			{"free", "data", "free meta", "metadata", "reserved"}};
+
+		if (error_on_dinode && old_bitmap_state == GFS2_BLKST_DINODE &&
+		    new_bitmap_state != GFS2_BLKST_FREE) {
+			log_debug(_("Reference as '%s' to block %llu (0x%llx) "
+				    "which was marked as dinode. Needs "
+				    "further investigation.\n"),
+				  allocdesc[sdp->gfs1][new_bitmap_state],
+				  (unsigned long long)blk,
+				  (unsigned long long)blk);
+			return 1;
+		}
 		/* Keep these messages as short as possible, or the output
 		   gets to be huge and unmanageable. */
 		log_err( _("Block %llu (0x%llx) was '%s', should be %s.\n"),
 			 (unsigned long long)blk, (unsigned long long)blk,
-			 allocdesc[new_bitmap_state],
-			 allocdesc[old_bitmap_state]);
+			 allocdesc[sdp->gfs1][old_bitmap_state],
+			 allocdesc[sdp->gfs1][new_bitmap_state]);
 		if (query( _("Fix the bitmap? (y/n)"))) {
 			/* If the new bitmap state is free (and therefore the
 			   old state was not) we have to add to the free
@@ -89,10 +87,18 @@ int check_n_fix_bitmap(struct gfs2_sbd *sdp, uint64_t blk,
 						inodetree_delete(ii);
 				}
 				rgd->rg.rg_free++;
-				gfs2_rgrp_out(&rgd->rg, rgd->bh[0]);
+				if (sdp->gfs1)
+					gfs_rgrp_out((struct gfs_rgrp *)
+						     &rgd->rg, rgd->bh[0]);
+				else
+					gfs2_rgrp_out(&rgd->rg, rgd->bh[0]);
 			} else if (old_bitmap_state == GFS2_BLKST_FREE) {
 				rgd->rg.rg_free--;
-				gfs2_rgrp_out(&rgd->rg, rgd->bh[0]);
+				if (sdp->gfs1)
+					gfs_rgrp_out((struct gfs_rgrp *)
+						     &rgd->rg, rgd->bh[0]);
+				else
+					gfs2_rgrp_out(&rgd->rg, rgd->bh[0]);
 			}
 			log_err( _("The bitmap was fixed.\n"));
 		} else {
@@ -108,6 +114,7 @@ int check_n_fix_bitmap(struct gfs2_sbd *sdp, uint64_t blk,
  */
 int _fsck_blockmap_set(struct gfs2_inode *ip, uint64_t bblock,
 		       const char *btype, enum gfs2_mark_block mark,
+		       int error_on_dinode,
 		       const char *caller, int fline)
 {
 	int error;
@@ -166,9 +173,11 @@ int _fsck_blockmap_set(struct gfs2_inode *ip, uint64_t bblock,
 
 	/* First, check the rgrp bitmap against what we think it should be.
 	   If that fails, it's an invalid block--part of an rgrp. */
-	error = check_n_fix_bitmap(ip->i_sbd, bblock, mark);
+	error = check_n_fix_bitmap(ip->i_sbd, bblock, error_on_dinode, mark);
 	if (error) {
-		log_err( _("This block is not represented in the bitmap.\n"));
+		if (error < 0)
+			log_err( _("This block is not represented in the "
+				   "bitmap.\n"));
 		return error;
 	}
 
@@ -181,23 +190,42 @@ struct duptree *dupfind(uint64_t block)
 	struct osi_node *node = dup_blocks.osi_node;
 
 	while (node) {
-		struct duptree *data = (struct duptree *)node;
+		struct duptree *dt = (struct duptree *)node;
 
-		if (block < data->block)
+		if (block < dt->block)
 			node = node->osi_left;
-		else if (block > data->block)
+		else if (block > dt->block)
 			node = node->osi_right;
 		else
-			return data;
+			return dt;
 	}
 	return NULL;
 }
 
 struct gfs2_inode *fsck_system_inode(struct gfs2_sbd *sdp, uint64_t block)
 {
+	int j;
+
 	if (lf_dip && lf_dip->i_di.di_num.no_addr == block)
 		return lf_dip;
-	return is_system_inode(sdp, block);
+	if (!sdp->gfs1)
+		return is_system_inode(sdp, block);
+
+	if (sdp->md.statfs && block == sdp->md.statfs->i_di.di_num.no_addr)
+		return sdp->md.statfs;
+	if (sdp->md.jiinode && block == sdp->md.jiinode->i_di.di_num.no_addr)
+		return sdp->md.jiinode;
+	if (sdp->md.riinode && block == sdp->md.riinode->i_di.di_num.no_addr)
+		return sdp->md.riinode;
+	if (sdp->md.qinode && block == sdp->md.qinode->i_di.di_num.no_addr)
+		return sdp->md.qinode;
+	if (sdp->md.rooti && block == sdp->md.rooti->i_di.di_num.no_addr)
+		return sdp->md.rooti;
+	for (j = 0; j < sdp->md.journals; j++)
+		if (sdp->md.journal && sdp->md.journal[j] &&
+		    block == sdp->md.journal[j]->i_di.di_num.no_addr)
+			return sdp->md.journal[j];
+	return NULL;
 }
 
 /* fsck_load_inode - same as gfs2_load_inode() in libgfs2 but system inodes
@@ -209,6 +237,8 @@ struct gfs2_inode *fsck_load_inode(struct gfs2_sbd *sdp, uint64_t block)
 	ip = fsck_system_inode(sdp, block);
 	if (ip)
 		return ip;
+	if (sdp->gfs1)
+		return gfs_inode_read(sdp, block);
 	return inode_read(sdp, block);
 }
 
@@ -223,6 +253,8 @@ struct gfs2_inode *fsck_inode_get(struct gfs2_sbd *sdp,
 	if (sysip)
 		return sysip;
 
+	if (sdp->gfs1)
+		return gfs_inode_get(sdp, bh);
 	return inode_get(sdp, bh);
 }
 
@@ -297,13 +329,11 @@ static void dirblk_truncate(struct gfs2_inode *ip, struct gfs2_dirent *fixb,
 {
 	char *bh_end;
 	struct gfs2_dirent de;
-	uint16_t old_rec_len;
 
 	bh_end = bh->b_data + ip->i_sbd->sd_sb.sb_bsize;
 	/* truncate the block to save the most dentries.  To do this we
 	   have to patch the previous dent. */
 	gfs2_dirent_in(&de, (char *)fixb);
-	old_rec_len = de.de_rec_len;
 	de.de_rec_len = bh_end - (char *)fixb;
 	gfs2_dirent_out(&de, (char *)fixb);
 	bmodified(bh);
@@ -316,15 +346,16 @@ static void dirblk_truncate(struct gfs2_inode *ip, struct gfs2_dirent *fixb,
  * bh - buffer for the leaf block
  * type - type of block this is (linear or exhash)
  * @count - set to the count entries
+ * @lindex - the last inde
  * @pass - structure pointing to pass-specific functions
  *
  * returns: 0 - good block or it was repaired to be good
  *         -1 - error occurred
  */
 static int check_entries(struct gfs2_inode *ip, struct gfs2_buffer_head *bh,
-		  int type, uint32_t *count, struct metawalk_fxns *pass)
+			 int type, uint32_t *count, int lindex,
+			 struct metawalk_fxns *pass)
 {
-	struct gfs2_leaf *leaf = NULL;
 	struct gfs2_dirent *dent;
 	struct gfs2_dirent de, *prev;
 	int error = 0;
@@ -336,14 +367,12 @@ static int check_entries(struct gfs2_inode *ip, struct gfs2_buffer_head *bh,
 
 	if (type == DIR_LINEAR) {
 		dent = (struct gfs2_dirent *)(bh->b_data + sizeof(struct gfs2_dinode));
-	}
-	else if (type == DIR_EXHASH) {
+	} else if (type == DIR_EXHASH) {
 		dent = (struct gfs2_dirent *)(bh->b_data + sizeof(struct gfs2_leaf));
-		leaf = (struct gfs2_leaf *)bh->b_data;
-		log_debug( _("Checking leaf %" PRIu64 " (0x%" PRIx64 ")\n"),
-				  bh->b_blocknr, bh->b_blocknr);
-	}
-	else {
+		log_debug( _("Checking leaf %llu (0x%llx)\n"),
+			  (unsigned long long)bh->b_blocknr,
+			  (unsigned long long)bh->b_blocknr);
+	} else {
 		log_err( _("Invalid directory type %d specified\n"), type);
 		return -1;
 	}
@@ -431,6 +460,7 @@ static int check_entries(struct gfs2_inode *ip, struct gfs2_buffer_head *bh,
 			} else {
 				error = pass->check_dentry(ip, dent, prev, bh,
 							   filename, count,
+							   lindex,
 							   pass->private);
 				if (error < 0) {
 					stack;
@@ -441,11 +471,12 @@ static int check_entries(struct gfs2_inode *ip, struct gfs2_buffer_head *bh,
 
 		if ((char *)dent + de.de_rec_len >= bh_end){
 			log_debug( _("Last entry processed for %lld->%lld "
-				     "(0x%llx->0x%llx).\n"),
+				     "(0x%llx->0x%llx), di_blocks=%llu.\n"),
 				   (unsigned long long)ip->i_di.di_num.no_addr,
 				   (unsigned long long)bh->b_blocknr,
 				   (unsigned long long)ip->i_di.di_num.no_addr,
-				   (unsigned long long)bh->b_blocknr);
+				   (unsigned long long)bh->b_blocknr,
+				   (unsigned long long)ip->i_di.di_blocks);
 			break;
 		}
 
@@ -459,49 +490,13 @@ static int check_entries(struct gfs2_inode *ip, struct gfs2_buffer_head *bh,
 	return 0;
 }
 
-/* Process a bad leaf pointer and ask to repair the first time.      */
-/* The repair process involves extending the previous leaf's entries */
-/* so that they replace the bad ones.  We have to hack up the old    */
-/* leaf a bit, but it's better than deleting the whole directory,    */
-/* which is what used to happen before.                              */
-static int warn_and_patch(struct gfs2_inode *ip, uint64_t *leaf_no,
-			  uint64_t *bad_leaf, uint64_t old_leaf,
-			  uint64_t first_ok_leaf, int pindex, const char *msg)
-{
-	int okay_to_fix = 0;
-
-	if (*bad_leaf != *leaf_no) {
-		log_err( _("Directory Inode %llu (0x%llx) points to leaf %llu"
-			" (0x%llx) %s.\n"),
-			(unsigned long long)ip->i_di.di_num.no_addr,
-			(unsigned long long)ip->i_di.di_num.no_addr,
-			(unsigned long long)*leaf_no,
-			(unsigned long long)*leaf_no, msg);
-	}
-	if (*leaf_no == *bad_leaf ||
-	    (okay_to_fix = query( _("Attempt to patch around it? (y/n) ")))) {
-		if (valid_block(ip->i_sbd, old_leaf))
-			gfs2_put_leaf_nr(ip, pindex, old_leaf);
-		else
-			gfs2_put_leaf_nr(ip, pindex, first_ok_leaf);
-		log_err( _("Directory Inode %llu (0x%llx) repaired.\n"),
-			 (unsigned long long)ip->i_di.di_num.no_addr,
-			 (unsigned long long)ip->i_di.di_num.no_addr);
-	} else
-		log_err( _("Bad leaf left in place.\n"));
-	*bad_leaf = *leaf_no;
-	*leaf_no = old_leaf;
-	return okay_to_fix;
-}
-
 /**
  * check_leaf - check a leaf block for errors
+ * Reads in the leaf block
+ * Leaves the buffer around for further analysis (caller must brelse)
  */
-static int check_leaf(struct gfs2_inode *ip, int lindex,
-		      struct metawalk_fxns *pass, int *ref_count,
-		      uint64_t *leaf_no, uint64_t old_leaf, uint64_t *bad_leaf,
-		      uint64_t first_ok_leaf, struct gfs2_leaf *leaf,
-		      struct gfs2_leaf *oldleaf)
+int check_leaf(struct gfs2_inode *ip, int lindex, struct metawalk_fxns *pass,
+	       uint64_t *leaf_no, struct gfs2_leaf *leaf, int *ref_count)
 {
 	int error = 0, fix;
 	struct gfs2_buffer_head *lbh = NULL;
@@ -509,17 +504,17 @@ static int check_leaf(struct gfs2_inode *ip, int lindex,
 	struct gfs2_sbd *sdp = ip->i_sbd;
 	const char *msg;
 
-	*ref_count = 1;
 	/* Make sure the block number is in range. */
 	if (!valid_block(ip->i_sbd, *leaf_no)) {
 		log_err( _("Leaf block #%llu (0x%llx) is out of range for "
-			   "directory #%llu (0x%llx).\n"),
+			   "directory #%llu (0x%llx) at index %d (0x%x).\n"),
 			 (unsigned long long)*leaf_no,
 			 (unsigned long long)*leaf_no,
 			 (unsigned long long)ip->i_di.di_num.no_addr,
-			 (unsigned long long)ip->i_di.di_num.no_addr);
+			 (unsigned long long)ip->i_di.di_num.no_addr,
+			 lindex, lindex);
 		msg = _("that is out of range");
-		goto out_copy_old_leaf;
+		goto bad_leaf;
 	}
 
 	/* Try to read in the leaf block. */
@@ -527,11 +522,14 @@ static int check_leaf(struct gfs2_inode *ip, int lindex,
 	/* Make sure it's really a valid leaf block. */
 	if (gfs2_check_meta(lbh, GFS2_METATYPE_LF)) {
 		msg = _("that is not really a leaf");
-		goto out_copy_old_leaf;
+		goto bad_leaf;
 	}
+	if (pass->check_leaf_depth)
+		error = pass->check_leaf_depth(ip, *leaf_no, *ref_count, lbh);
+
 	if (pass->check_leaf) {
 		error = pass->check_leaf(ip, *leaf_no, pass->private);
-		if (error) {
+		if (error == -EEXIST) {
 			log_info(_("Previous reference to leaf %lld (0x%llx) "
 				   "has already checked it; skipping.\n"),
 				 (unsigned long long)*leaf_no,
@@ -563,27 +561,30 @@ static int check_leaf(struct gfs2_inode *ip, int lindex,
 			 (unsigned long long)*leaf_no,
 			 (unsigned long long)*leaf_no);
 		msg = _("that is not a leaf");
-		goto out_copy_old_leaf;
+		goto bad_leaf;
 	}
 
-	if (pass->check_dentry && S_ISDIR(ip->i_di.di_mode)) {
-		error = check_entries(ip, lbh, DIR_EXHASH, &count, pass);
+	if (pass->check_dentry && is_dir(&ip->i_di, sdp->gfs1)) {
+		error = check_entries(ip, lbh, DIR_EXHASH, &count, lindex,
+				      pass);
 
 		if (skip_this_pass || fsck_abort)
 			goto out;
 
 		if (error < 0) {
 			stack;
-			goto out;
+			goto out; /* This seems wrong: needs investigation */
 		}
 
-		if (count != leaf->lf_entries) {
-			/* release and re-read the leaf in case check_entries
-			   changed it. */
-			brelse(lbh);
-			lbh = bread(sdp, *leaf_no);
-			gfs2_leaf_in(leaf, lbh);
+		if (count == leaf->lf_entries)
+			goto out;
 
+		/* release and re-read the leaf in case check_entries
+		   changed it. */
+		brelse(lbh);
+		lbh = bread(sdp, *leaf_no);
+		gfs2_leaf_in(leaf, lbh);
+		if (count != leaf->lf_entries) {
 			log_err( _("Leaf %llu (0x%llx) entry count in "
 				   "directory %llu (0x%llx) does not match "
 				   "number of entries found - is %u, found %u\n"),
@@ -605,39 +606,106 @@ out:
 	brelse(lbh);
 	return 0;
 
-out_copy_old_leaf:
-	/* The leaf we read in is bad.  So we'll copy the old leaf into the
-	 * new one.  However, that will make us shift our ref count. */
-	fix = warn_and_patch(ip, leaf_no, bad_leaf, old_leaf,
-			     first_ok_leaf, lindex, msg);
-	(*ref_count)++;
-	memcpy(leaf, oldleaf, sizeof(struct gfs2_leaf));
-	if (lbh) {
-		if (fix)
-			bmodified(lbh);
+bad_leaf:
+	if (lbh)
 		brelse(lbh);
+	if (pass->repair_leaf) {
+		/* The leaf we read in is bad so we need to repair it. */
+		fix = pass->repair_leaf(ip, leaf_no, lindex, *ref_count, msg,
+					pass->private);
+		if (fix < 0)
+			return fix;
+
 	}
 	return 1;
 }
 
+static int u64cmp(const void *p1, const void *p2)
+{
+	uint64_t a = *(uint64_t *)p1;
+	uint64_t b = *(uint64_t *)p2;
+
+	if (a > b)
+		return 1;
+	if (a < b)
+		return -1;
+
+	return 0;
+}
+
+static void dir_leaf_reada(struct gfs2_inode *ip, uint64_t *tbl, unsigned hsize)
+{
+	uint64_t *t = alloca(hsize * sizeof(uint64_t));
+	uint64_t leaf_no;
+	struct gfs2_sbd *sdp = ip->i_sbd;
+	unsigned n = 0;
+	unsigned i;
+
+	for (i = 0; i < hsize; i++) {
+		leaf_no = be64_to_cpu(tbl[i]);
+		if (valid_block(ip->i_sbd, leaf_no))
+			t[n++] = leaf_no * sdp->bsize;
+	}
+	qsort(t, n, sizeof(uint64_t), u64cmp);
+	for (i = 0; i < n; i++)
+		posix_fadvise(sdp->device_fd, t[i], sdp->bsize, POSIX_FADV_WILLNEED);
+}
+
 /* Checks exhash directory entries */
 static int check_leaf_blks(struct gfs2_inode *ip, struct metawalk_fxns *pass)
 {
-	int error;
-	struct gfs2_leaf leaf, oldleaf;
-	uint64_t leaf_no, old_leaf, bad_leaf = -1;
-	uint64_t first_ok_leaf;
+	int error = 0;
+	struct gfs2_leaf leaf;
+	unsigned hsize = (1 << ip->i_di.di_depth);
+	uint64_t leaf_no, leaf_next;
+	uint64_t first_ok_leaf, orig_di_blocks;
 	struct gfs2_buffer_head *lbh;
 	int lindex;
 	struct gfs2_sbd *sdp = ip->i_sbd;
-	int ref_count = 0, old_was_dup;
+	int ref_count, orig_ref_count, orig_di_depth, orig_di_height;
+	uint64_t *tbl;
+	int chained_leaf, tbl_valid;
+
+	tbl = get_dir_hash(ip);
+	if (tbl == NULL) {
+		perror("get_dir_hash");
+		return -1;
+	}
+	tbl_valid = 1;
+	orig_di_depth = ip->i_di.di_depth;
+	orig_di_height = ip->i_di.di_height;
+	orig_di_blocks = ip->i_di.di_blocks;
+
+	/* Turn off system readahead */
+	posix_fadvise(sdp->device_fd, 0, 0, POSIX_FADV_RANDOM);
+
+	/* Readahead */
+	dir_leaf_reada(ip, tbl, hsize);
+
+	if (pass->check_hash_tbl) {
+		error = pass->check_hash_tbl(ip, tbl, hsize, pass->private);
+		if (error < 0) {
+			free(tbl);
+			posix_fadvise(sdp->device_fd, 0, 0, POSIX_FADV_NORMAL);
+			return error;
+		}
+		/* If hash table changes were made, read it in again. */
+		if (error) {
+			free(tbl);
+			tbl = get_dir_hash(ip);
+			if (tbl == NULL) {
+				perror("get_dir_hash");
+				return -1;
+			}
+		}
+	}
 
 	/* Find the first valid leaf pointer in range and use it as our "old"
 	   leaf. That way, bad blocks at the beginning will be overwritten
 	   with the first valid leaf. */
 	first_ok_leaf = leaf_no = -1;
-	for (lindex = 0; lindex < (1 << ip->i_di.di_depth); lindex++) {
-		gfs2_get_leaf_nr(ip, lindex, &leaf_no);
+	for (lindex = 0; lindex < hsize; lindex++) {
+		leaf_no = be64_to_cpu(tbl[lindex]);
 		if (valid_block(ip->i_sbd, leaf_no)) {
 			lbh = bread(sdp, leaf_no);
 			/* Make sure it's really a valid leaf block. */
@@ -654,56 +722,90 @@ static int check_leaf_blks(struct gfs2_inode *ip, struct metawalk_fxns *pass)
 			   "blocks\n"),
 			 (unsigned long long)ip->i_di.di_num.no_addr,
 			 (unsigned long long)ip->i_di.di_num.no_addr);
+		free(tbl);
+		posix_fadvise(sdp->device_fd, 0, 0, POSIX_FADV_NORMAL);
 		return 1;
 	}
-	old_leaf = -1;
-	memset(&oldleaf, 0, sizeof(oldleaf));
-	old_was_dup = 0;
-	for (lindex = 0; lindex < (1 << ip->i_di.di_depth); lindex++) {
+	lindex = 0;
+	leaf_next = -1;
+	while (lindex < hsize) {
+		int l;
+
 		if (fsck_abort)
 			break;
-		gfs2_get_leaf_nr(ip, lindex, &leaf_no);
 
-		/* GFS has multiple indirect pointers to the same leaf
-		 * until those extra pointers are needed, so skip the dups */
-		if (leaf_no == bad_leaf) {
-			gfs2_put_leaf_nr(ip, lindex, old_leaf);
-			ref_count++;
-			continue;
-		} else if (old_leaf == leaf_no) {
+		if (!tbl_valid) {
+			free(tbl);
+			log_debug(_("Re-reading 0x%llx hash table.\n"),
+				  (unsigned long long)ip->i_di.di_num.no_addr);
+			tbl = get_dir_hash(ip);
+			if (tbl == NULL) {
+				perror("get_dir_hash");
+				return -1;
+			}
+			tbl_valid = 1;
+			orig_di_depth = ip->i_di.di_depth;
+			orig_di_height = ip->i_di.di_height;
+			orig_di_blocks = ip->i_di.di_blocks;
+		}
+		leaf_no = be64_to_cpu(tbl[lindex]);
+
+		/* count the number of block pointers to this leaf. We don't
+		   need to count the current lindex, because we already know
+		   it's a reference */
+		ref_count = 1;
+
+		for (l = lindex + 1; l < hsize; l++) {
+			leaf_next = be64_to_cpu(tbl[l]);
+			if (leaf_next != leaf_no)
+				break;
 			ref_count++;
-			continue;
 		}
+		orig_ref_count = ref_count;
 
+		chained_leaf = 0;
 		do {
-			if (fsck_abort)
+			if (fsck_abort) {
+				free(tbl);
+				posix_fadvise(sdp->device_fd, 0, 0, POSIX_FADV_NORMAL);
 				return 0;
-			/* If the old leaf was a duplicate referenced by a
-			   previous dinode, we can't check the number of
-			   pointers because the number of pointers may be for
-			   that other dinode's reference, not this one. */
-			if (pass->check_num_ptrs && !old_was_dup &&
-			    valid_block(ip->i_sbd, old_leaf)) {
-				error = pass->check_num_ptrs(ip, old_leaf,
-							     &ref_count,
-							     &lindex,
-							     &oldleaf);
-				if (error)
-					return error;
 			}
-			error = check_leaf(ip, lindex, pass, &ref_count,
-					   &leaf_no, old_leaf, &bad_leaf,
-					   first_ok_leaf, &leaf, &oldleaf);
-			old_was_dup = (error == -EEXIST);
-			old_leaf = leaf_no;
-			memcpy(&oldleaf, &leaf, sizeof(oldleaf));
+			error = check_leaf(ip, lindex, pass, &leaf_no, &leaf,
+					   &ref_count);
+			if (ref_count != orig_ref_count)
+				tbl_valid = 0;
 			if (!leaf.lf_next || error)
 				break;
 			leaf_no = leaf.lf_next;
-			log_debug( _("Leaf chain (0x%llx) detected.\n"),
-				   (unsigned long long)leaf_no);
+			chained_leaf++;
+			log_debug( _("Leaf chain #%d (0x%llx) detected.\n"),
+				   chained_leaf, (unsigned long long)leaf_no);
 		} while (1); /* while we have chained leaf blocks */
+		if (orig_di_depth != ip->i_di.di_depth) {
+			log_debug(_("Depth of 0x%llx changed from %d to %d\n"),
+				  (unsigned long long)ip->i_di.di_num.no_addr,
+				  orig_di_depth, ip->i_di.di_depth);
+			tbl_valid = 0;
+		}
+		if (orig_di_height != ip->i_di.di_height) {
+			log_debug(_("Height of 0x%llx changed from %d to "
+				    "%d\n"),
+				  (unsigned long long)ip->i_di.di_num.no_addr,
+				  orig_di_height, ip->i_di.di_height);
+			tbl_valid = 0;
+		}
+		if (orig_di_blocks != ip->i_di.di_blocks) {
+			log_debug(_("Block count of 0x%llx changed from %llu "
+				    "to %llu\n"),
+				  (unsigned long long)ip->i_di.di_num.no_addr,
+				  (unsigned long long)orig_di_blocks,
+				  (unsigned long long)ip->i_di.di_blocks);
+			tbl_valid = 0;
+		}
+		lindex += ref_count;
 	} /* for every leaf block */
+	free(tbl);
+	posix_fadvise(sdp->device_fd, 0, 0, POSIX_FADV_NORMAL);
 	return 0;
 }
 
@@ -860,55 +962,33 @@ int delete_block(struct gfs2_inode *ip, uint64_t block,
 
 /**
  * find_remove_dup - find out if this is a duplicate ref.  If so, remove it.
- * Returns: 0 if not a duplicate reference, 1 if it is.
+ *
+ * Returns: 1 if there are any remaining references to this block, else 0.
  */
 int find_remove_dup(struct gfs2_inode *ip, uint64_t block, const char *btype)
 {
-	struct duptree *d;
+	struct duptree *dt;
 	struct inode_with_dups *id;
 
-	d = dupfind(block);
-	if (!d)
+	dt = dupfind(block);
+	if (!dt)
 		return 0;
 
 	/* remove the inode reference id structure for this reference. */
-	id = find_dup_ref_inode(d, ip);
+	id = find_dup_ref_inode(dt, ip);
 	if (!id)
-		return 0;
+		goto more_refs;
 
-	dup_listent_delete(id);
-	log_err( _("Removing duplicate status of block %llu (0x%llx) "
-		   "referenced as %s by dinode %llu (0x%llx)\n"),
-		 (unsigned long long)block, (unsigned long long)block,
-		 btype, (unsigned long long)ip->i_di.di_num.no_addr,
-		 (unsigned long long)ip->i_di.di_num.no_addr);
-	d->refs--; /* one less reference */
-	if (d->refs == 1) {
-		log_info( _("This leaves only one reference: it's "
-			    "no longer a duplicate.\n"));
-		dup_delete(d); /* not duplicate now */
-	} else
-		log_info( _("%d block reference(s) remain.\n"),
-			  d->refs);
-	return 1; /* but the original ref still exists so do not free it. */
-}
-
-/**
- * free_block_if_notdup - free blocks associated with an inode, but if it's a
- *                        duplicate, just remove that designation instead.
- * Returns: 1 if the block was freed, 0 if a duplicate reference was removed
- * Note: The return code is handled this way because there are places in
- *       metawalk.c that assume "1" means "change was made" and "0" means
- *       change was not made.
- */
-int free_block_if_notdup(struct gfs2_inode *ip, uint64_t block,
-			 const char *btype)
-{
-	if (!find_remove_dup(ip, block, btype)) { /* not a dup */
-		fsck_blockmap_set(ip, block, btype, gfs2_block_free);
-		return 1;
+	dup_listent_delete(dt, id);
+	if (dt->refs == 0) {
+		log_info( _("This was the last reference: it's no longer a "
+			    "duplicate.\n"));
+		dup_delete(dt); /* not duplicate now */
+		return 0;
 	}
-	return 0;
+more_refs:
+	log_info( _("%d block reference(s) remain.\n"), dt->refs);
+	return 1; /* references still exist so do not free the block. */
 }
 
 /**
@@ -920,12 +1000,13 @@ int free_block_if_notdup(struct gfs2_inode *ip, uint64_t block,
  */
 static int delete_block_if_notdup(struct gfs2_inode *ip, uint64_t block,
 				  struct gfs2_buffer_head **bh,
-				  const char *btype, void *private)
+				  const char *btype, int *was_duplicate,
+				  void *private)
 {
 	uint8_t q;
 
 	if (!valid_block(ip->i_sbd, block))
-		return -EFAULT;
+		return meta_error;
 
 	q = block_type(block);
 	if (q == gfs2_block_free) {
@@ -935,9 +1016,21 @@ static int delete_block_if_notdup(struct gfs2_inode *ip, uint64_t block,
 			  (unsigned long long)block,
 			  (unsigned long long)ip->i_di.di_num.no_addr,
 			  (unsigned long long)ip->i_di.di_num.no_addr);
-		return 0;
+		return meta_is_good;
 	}
-	return free_block_if_notdup(ip, block, btype);
+	if (find_remove_dup(ip, block, btype)) { /* a dup */
+		if (was_duplicate)
+			*was_duplicate = 1;
+		log_err( _("Not clearing duplicate reference in inode "
+			   "at block #%llu (0x%llx) to block #%llu (0x%llx) "
+			   "because it's referenced by another inode.\n"),
+			 (unsigned long long)ip->i_di.di_num.no_addr,
+			 (unsigned long long)ip->i_di.di_num.no_addr,
+			 (unsigned long long)block, (unsigned long long)block);
+	} else {
+		fsck_blockmap_set(ip, block, btype, gfs2_block_free);
+	}
+	return meta_is_good;
 }
 
 /**
@@ -957,10 +1050,11 @@ static int check_indirect_eattr(struct gfs2_inode *ip, uint64_t indirect,
 	struct gfs2_sbd *sdp = ip->i_sbd;
 	int first_ea_is_bad = 0;
 	uint64_t di_eattr_save = ip->i_di.di_eattr;
-	uint64_t offset = sizeof(struct gfs2_meta_header);
+	uint64_t offset = ip->i_sbd->gfs1 ? sizeof(struct gfs_indirect) : sizeof(struct gfs2_meta_header);
 
-	log_debug( _("Checking EA indirect block #%"PRIu64" (0x%" PRIx64 ").\n"),
-			  indirect, indirect);
+	log_debug( _("Checking EA indirect block #%llu (0x%llx).\n"),
+		  (unsigned long long)indirect,
+		  (unsigned long long)indirect);
 
 	if (!pass->check_eattr_indir)
 		return 0;
@@ -1000,7 +1094,7 @@ static int check_indirect_eattr(struct gfs2_inode *ip, uint64_t indirect,
 			} else if (leaf_pointer_errors) {
 				/* This is a bit tricky.  We can't have eattr
 				   holes. So if we have 4 good eattrs, 1 bad
-				   eattr and 5 more good ones: GGGGBGGGGG, 
+				   eattr and 5 more good ones: GGGGBGGGGG,
 				   we need to tell check_leaf_eattr to delete
 				   all eattrs after the bad one.  So we want:
 				   GGGG when we finish.  To do that, we set
@@ -1092,6 +1186,9 @@ static void free_metalist(struct gfs2_inode *ip, osi_list_t *mlp)
  *                            This includes hash table blocks for directories
  *                            which are technically "data" in the bitmap.
  *
+ * Returns: 0 - all is well, process the blocks this metadata references
+ *          1 - something went wrong, but process the sub-blocks anyway
+ *         -1 - something went wrong, so don't process the sub-blocks
  * @ip:
  * @mlp:
  */
@@ -1103,7 +1200,7 @@ static int build_and_check_metalist(struct gfs2_inode *ip, osi_list_t *mlp,
 	osi_list_t *prev_list, *cur_list, *tmp;
 	int h, head_size, iblk_type;
 	uint64_t *ptr, block;
-	int error = 0, err;
+	int error, was_duplicate, is_valid;
 
 	osi_list_add(&metabh->b_altlist, &mlp[0]);
 
@@ -1112,20 +1209,23 @@ static int build_and_check_metalist(struct gfs2_inode *ip, osi_list_t *mlp,
 	   because it checks everything through the hash table using
 	   "depth" field calculations. However, we still have to check the
 	   indirect blocks, even if the height == 1.  */
-	if (S_ISDIR(ip->i_di.di_mode))
+	if (is_dir(&ip->i_di, ip->i_sbd->gfs1))
 		height++;
 
 	/* if (<there are no indirect blocks to check>) */
 	if (height < 2)
-		return 0;
+		return meta_is_good;
 	for (h = 1; h < height; h++) {
 		if (h > 1) {
-			if (S_ISDIR(ip->i_di.di_mode) &&
+			if (is_dir(&ip->i_di, ip->i_sbd->gfs1) &&
 			    h == ip->i_di.di_height + 1)
 				iblk_type = GFS2_METATYPE_JD;
 			else
 				iblk_type = GFS2_METATYPE_IN;
-			head_size = sizeof(struct gfs2_meta_header);
+			if (ip->i_sbd->gfs1)
+				head_size = sizeof(struct gfs_indirect);
+			else
+				head_size = sizeof(struct gfs2_meta_header);
 		} else {
 			iblk_type = GFS2_METATYPE_DI;
 			head_size = sizeof(struct gfs2_dinode);
@@ -1136,9 +1236,12 @@ static int build_and_check_metalist(struct gfs2_inode *ip, osi_list_t *mlp,
 		for (tmp = prev_list->next; tmp != prev_list; tmp = tmp->next){
 			bh = osi_list_entry(tmp, struct gfs2_buffer_head,
 					    b_altlist);
+			if (gfs2_check_meta(bh, iblk_type)) {
+				if (pass->invalid_meta_is_fatal)
+					return meta_error;
 
-			if (gfs2_check_meta(bh, iblk_type))
 				continue;
+			}
 
 			/* Now check the metadata itself */
 			for (ptr = (uint64_t *)(bh->b_data + head_size);
@@ -1146,7 +1249,7 @@ static int build_and_check_metalist(struct gfs2_inode *ip, osi_list_t *mlp,
 			     ptr++) {
 				if (skip_this_pass || fsck_abort) {
 					free_metalist(ip, mlp);
-					return FSCK_OK;
+					return meta_is_good;
 				}
 				nbh = NULL;
 
@@ -1154,21 +1257,46 @@ static int build_and_check_metalist(struct gfs2_inode *ip, osi_list_t *mlp,
 					continue;
 
 				block = be64_to_cpu(*ptr);
-				err = pass->check_metalist(ip, block, &nbh, h,
-							   pass->private);
+				was_duplicate = 0;
+				error = pass->check_metalist(ip, block, &nbh,
+							     h, &is_valid,
+							     &was_duplicate,
+							     pass->private);
 				/* check_metalist should hold any buffers
 				   it gets with "bread". */
-				if (err < 0) {
+				if (error == meta_error) {
 					stack;
-					error = err;
-					goto fail;
+					log_info(_("\nSerious metadata "
+						   "error on block %llu "
+						   "(0x%llx).\n"),
+						 (unsigned long long)block,
+						 (unsigned long long)block);
+					return error;
 				}
-				if (err > 0) {
-					if (!error)
-						error = err;
-					log_debug( _("Skipping block %" PRIu64
-						     " (0x%" PRIx64 ")\n"),
-						   block, block);
+				if (error == meta_skip_further) {
+					log_info(_("\nUnrecoverable metadata "
+						   "error on block %llu "
+						   "(0x%llx). Further metadata"
+						   " will be skipped.\n"),
+						 (unsigned long long)block,
+						 (unsigned long long)block);
+					return error;
+				}
+				if (!is_valid) {
+					log_debug( _("Skipping rejected block "
+						     "%llu (0x%llx)\n"),
+						   (unsigned long long)block,
+						   (unsigned long long)block);
+					if (pass->invalid_meta_is_fatal)
+						return meta_error;
+
+					continue;
+				}
+				if (was_duplicate) {
+					log_debug( _("Skipping duplicate %llu "
+						     "(0x%llx)\n"),
+						   (unsigned long long)block,
+						   (unsigned long long)block);
 					continue;
 				}
 				if (!valid_block(ip->i_sbd, block)) {
@@ -1176,18 +1304,18 @@ static int build_and_check_metalist(struct gfs2_inode *ip, osi_list_t *mlp,
 						     "%lld (0x%llx)\n"),
 						   (unsigned long long)block,
 						   (unsigned long long)block);
+					if (pass->invalid_meta_is_fatal)
+						return meta_error;
+
 					continue;
 				}
 				if (!nbh)
 					nbh = bread(ip->i_sbd, block);
-				osi_list_add(&nbh->b_altlist, cur_list);
+				osi_list_add_prev(&nbh->b_altlist, cur_list);
 			} /* for all data on the indirect block */
 		} /* for blocks at that height */
 	} /* for height */
-	return error;
-fail:
-	free_metalist(ip, mlp);
-	return error;
+	return 0;
 }
 
 /**
@@ -1204,11 +1332,14 @@ fail:
  *          2 (ENOENT) is there were too many bad pointers
  */
 static int check_data(struct gfs2_inode *ip, struct metawalk_fxns *pass,
-		      uint64_t *ptr_start, char *ptr_end,
-		      uint64_t *blks_checked)
+		      struct gfs2_buffer_head *bh, int head_size,
+		      uint64_t *blks_checked, uint64_t *error_blk)
 {
 	int error = 0, rc = 0;
 	uint64_t block, *ptr;
+	uint64_t *ptr_start = (uint64_t *)(bh->b_data + head_size);
+	char *ptr_end = (bh->b_data + ip->i_sbd->bsize);
+	uint64_t metablock = bh->b_blocknr;
 
 	/* If there isn't much pointer corruption check the pointers */
 	for (ptr = ptr_start ; (char *)ptr < ptr_end && !fsck_abort; ptr++) {
@@ -1218,38 +1349,92 @@ static int check_data(struct gfs2_inode *ip, struct metawalk_fxns *pass,
 		if (skip_this_pass || fsck_abort)
 			return error;
 		block =  be64_to_cpu(*ptr);
-		/* It's important that we don't call !valid_block and
+		/* It's important that we don't call valid_block() and
 		   bypass calling check_data on invalid blocks because that
 		   would defeat the rangecheck_block related functions in
 		   pass1. Therefore the individual check_data functions
 		   should do a range check. */
-		rc = pass->check_data(ip, block, pass->private);
+		rc = pass->check_data(ip, metablock, block, pass->private);
+		if (!error && rc) {
+			error = rc;
+			log_info("\n");
+			if (rc < 0) {
+				*error_blk = block;
+				log_info(_("Unrecoverable "));
+			}
+			log_info(_("data block error %d on block %llu "
+				   "(0x%llx).\n"), rc,
+				 (unsigned long long)block,
+				 (unsigned long long)block);
+		}
 		if (rc < 0)
 			return rc;
-		if (!error && rc)
-			error = rc;
 		(*blks_checked)++;
 	}
 	return error;
 }
 
+static int undo_check_data(struct gfs2_inode *ip, struct metawalk_fxns *pass,
+			   uint64_t *ptr_start, char *ptr_end,
+			   uint64_t error_blk)
+{
+	int rc = 0;
+	uint64_t block, *ptr;
+
+	/* If there isn't much pointer corruption check the pointers */
+	for (ptr = ptr_start ; (char *)ptr < ptr_end && !fsck_abort; ptr++) {
+		if (!*ptr)
+			continue;
+
+		if (skip_this_pass || fsck_abort)
+			return 1;
+		block =  be64_to_cpu(*ptr);
+		if (block == error_blk)
+			return 1;
+		rc = pass->undo_check_data(ip, block, pass->private);
+		if (rc < 0)
+			return rc;
+	}
+	return 0;
+}
+
+static int hdr_size(struct gfs2_buffer_head *bh, int height)
+{
+	if (height > 1) {
+		if (gfs2_check_meta(bh, GFS2_METATYPE_IN))
+			return 0;
+		if (bh->sdp->gfs1)
+			return sizeof(struct gfs_indirect);
+		else
+			return sizeof(struct gfs2_meta_header);
+	}
+	/* if this isn't really a dinode, skip it */
+	if (gfs2_check_meta(bh, GFS2_METATYPE_DI))
+		return 0;
+
+	return sizeof(struct gfs2_dinode);
+}
+
 /**
  * check_metatree
- * @ip:
- * @rgd:
+ * @ip: inode structure in memory
+ * @pass: structure passed in from caller to determine the sub-functions
  *
  */
 int check_metatree(struct gfs2_inode *ip, struct metawalk_fxns *pass)
 {
 	osi_list_t metalist[GFS2_MAX_META_HEIGHT];
-	osi_list_t *list;
+	osi_list_t *list, *tmp;
 	struct gfs2_buffer_head *bh;
 	uint32_t height = ip->i_di.di_height;
 	int  i, head_size;
 	uint64_t blks_checked = 0;
 	int error, rc;
+	int metadata_clean = 0;
+	uint64_t error_blk = 0;
+	int hit_error_blk = 0;
 
-	if (!height && !S_ISDIR(ip->i_di.di_mode))
+	if (!height && !is_dir(&ip->i_di, ip->i_sbd->gfs1))
 		return 0;
 
 	for (i = 0; i < GFS2_MAX_META_HEIGHT; i++)
@@ -1259,35 +1444,21 @@ int check_metatree(struct gfs2_inode *ip, struct metawalk_fxns *pass)
 	error = build_and_check_metalist(ip, &metalist[0], pass);
 	if (error) {
 		stack;
-		free_metalist(ip, &metalist[0]);
-		return error;
+		goto undo_metalist;
 	}
 
+	metadata_clean = 1;
 	/* For directories, we've already checked the "data" blocks which
 	 * comprise the directory hash table, so we perform the directory
 	 * checks and exit. */
-        if (S_ISDIR(ip->i_di.di_mode)) {
-		free_metalist(ip, &metalist[0]);
+        if (is_dir(&ip->i_di, ip->i_sbd->gfs1)) {
 		if (!(ip->i_di.di_flags & GFS2_DIF_EXHASH))
-			return 0;
+			goto out;
 		/* check validity of leaf blocks and leaf chains */
 		error = check_leaf_blks(ip, pass);
-		return error;
-	}
-
-	/* Free the metalist buffers from heights we don't need to check.
-	   For the rest we'll free as we check them to save time.
-	   metalist[0] will only have the dinode bh, so we can skip it. */
-	for (i = 1; i < height - 1; i++) {
-		list = &metalist[i];
-		while (!osi_list_empty(list)) {
-			bh = osi_list_entry(list->next,
-					    struct gfs2_buffer_head, b_altlist);
-			if (bh == ip->i_bh)
-				osi_list_del(&bh->b_altlist);
-			else
-				brelse(bh);
-		}
+		if (error)
+			goto undo_metalist;
+		goto out;
 	}
 
 	/* check data blocks */
@@ -1295,53 +1466,24 @@ int check_metatree(struct gfs2_inode *ip, struct metawalk_fxns *pass)
 	if (ip->i_di.di_blocks > COMFORTABLE_BLKS)
 		last_reported_fblock = -10000000;
 
-	while (error >= 0 && !osi_list_empty(list)) {
+	for (tmp = list->next; !error && tmp != list; tmp = tmp->next) {
 		if (fsck_abort) {
 			free_metalist(ip, &metalist[0]);
 			return 0;
 		}
-		bh = osi_list_entry(list->next, struct gfs2_buffer_head,
-				    b_altlist);
-
-		if (height > 1) {
-			if (gfs2_check_meta(bh, GFS2_METATYPE_IN)) {
-				if (bh == ip->i_bh)
-					osi_list_del(&bh->b_altlist);
-				else
-					brelse(bh);
-				continue;
-			}
-			head_size = sizeof(struct gfs2_meta_header);
-		} else {
-			/* if this isn't really a dinode, skip it */
-			if (gfs2_check_meta(bh, GFS2_METATYPE_DI)) {
-				if (bh == ip->i_bh)
-					osi_list_del(&bh->b_altlist);
-				else
-					brelse(bh);
-				continue;
-			}
-			head_size = sizeof(struct gfs2_dinode);
-		}
+		bh = osi_list_entry(tmp, struct gfs2_buffer_head, b_altlist);
+		head_size = hdr_size(bh, height);
+		if (!head_size)
+			continue;
 
 		if (pass->check_data)
-			rc = check_data(ip, pass, (uint64_t *)
-					(bh->b_data + head_size),
-					(bh->b_data + ip->i_sbd->bsize),
-					&blks_checked);
-		else
-			rc = 0;
+			error = check_data(ip, pass, bh, head_size,
+					   &blks_checked, &error_blk);
 
-		if (rc && (!error || rc < 0))
-			error = rc;
-		if (ip->i_di.di_blocks > COMFORTABLE_BLKS)
-			big_file_comfort(ip, blks_checked);
-		if (bh == ip->i_bh)
-			osi_list_del(&bh->b_altlist);
-		else
-			brelse(bh);
+		if (pass->big_file_msg && ip->i_di.di_blocks > COMFORTABLE_BLKS)
+			pass->big_file_msg(ip, blks_checked);
 	}
-	if (ip->i_di.di_blocks > COMFORTABLE_BLKS) {
+	if (pass->big_file_msg && ip->i_di.di_blocks > COMFORTABLE_BLKS) {
 		log_notice( _("\rLarge file at %lld (0x%llx) - 100 percent "
 			      "complete.                                   "
 			      "\n"),
@@ -1349,6 +1491,63 @@ int check_metatree(struct gfs2_inode *ip, struct metawalk_fxns *pass)
 			    (unsigned long long)ip->i_di.di_num.no_addr);
 		fflush(stdout);
 	}
+undo_metalist:
+	if (!error)
+		goto out;
+	log_err( _("Error: inode %llu (0x%llx) had unrecoverable errors.\n"),
+		 (unsigned long long)ip->i_di.di_num.no_addr,
+		 (unsigned long long)ip->i_di.di_num.no_addr);
+	if (!query( _("Remove the invalid inode? (y/n) "))) {
+		free_metalist(ip, &metalist[0]);
+		log_err(_("Invalid inode not deleted.\n"));
+		return error;
+	}
+	for (i = 0; pass->undo_check_meta && i < height; i++) {
+		while (!osi_list_empty(&metalist[i])) {
+			list = &metalist[i];
+			bh = osi_list_entry(list->next,
+					    struct gfs2_buffer_head,
+					    b_altlist);
+			log_err(_("Undoing metadata work for block %llu "
+				  "(0x%llx)\n"),
+				(unsigned long long)bh->b_blocknr,
+				(unsigned long long)bh->b_blocknr);
+			if (i)
+				rc = pass->undo_check_meta(ip, bh->b_blocknr,
+							   i, pass->private);
+			else
+				rc = 0;
+			if (metadata_clean && rc == 0 && i == height - 1 &&
+			    !hit_error_blk) {
+				head_size = hdr_size(bh, height);
+				if (head_size) {
+					rc = undo_check_data(ip, pass,
+							     (uint64_t *)
+					      (bh->b_data + head_size),
+					      (bh->b_data + ip->i_sbd->bsize),
+							     error_blk);
+					if (rc > 0) {
+						hit_error_blk = 1;
+						rc = 0;
+					}
+				}
+			}
+			if (bh == ip->i_bh)
+				osi_list_del(&bh->b_altlist);
+			else
+				brelse(bh);
+		}
+	}
+	/* There may be leftover duplicate records, so we need to delete them.
+	   For example, if a metadata block was found to be a duplicate, we
+	   may not have added it to the metalist, which means it's not there
+	   to undo. */
+	delete_all_dups(ip);
+	/* Set the dinode as "bad" so it gets deleted */
+	fsck_blockmap_set(ip, ip->i_di.di_num.no_addr,
+			  _("corrupt"), gfs2_block_free);
+	log_err(_("The corrupt inode was invalidated.\n"));
+out:
 	free_metalist(ip, &metalist[0]);
 	return error;
 }
@@ -1360,7 +1559,7 @@ int check_linear_dir(struct gfs2_inode *ip, struct gfs2_buffer_head *bh,
 	int error = 0;
 	uint32_t count = 0;
 
-	error = check_entries(ip, bh, DIR_LINEAR, &count, pass);
+	error = check_entries(ip, bh, DIR_LINEAR, &count, 0, pass);
 	if (error < 0) {
 		stack;
 		return -1;
@@ -1373,9 +1572,12 @@ int check_dir(struct gfs2_sbd *sdp, uint64_t block, struct metawalk_fxns *pass)
 {
 	struct gfs2_inode *ip;
 	int error = 0;
+	uint64_t cur_blks;
 
 	ip = fsck_load_inode(sdp, block);
 
+	cur_blks = ip->i_di.di_blocks;
+
 	if (ip->i_di.di_flags & GFS2_DIF_EXHASH)
 		error = check_leaf_blks(ip, pass);
 	else
@@ -1384,6 +1586,9 @@ int check_dir(struct gfs2_sbd *sdp, uint64_t block, struct metawalk_fxns *pass)
 	if (error < 0)
 		stack;
 
+	if (ip->i_di.di_blocks != cur_blks)
+		reprocess_inode(ip, _("Current"));
+
 	fsck_inode_put(&ip); /* does a brelse */
 	return error;
 }
@@ -1391,7 +1596,8 @@ int check_dir(struct gfs2_sbd *sdp, uint64_t block, struct metawalk_fxns *pass)
 static int remove_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent,
 			 struct gfs2_dirent *prev_de,
 			 struct gfs2_buffer_head *bh,
-			 char *filename, uint32_t *count, void *private)
+			 char *filename, uint32_t *count, int lindex,
+			 void *private)
 {
 	/* the metawalk_fxn's private field must be set to the dentry
 	 * block we want to clear */
@@ -1442,33 +1648,52 @@ int remove_dentry_from_dir(struct gfs2_sbd *sdp, uint64_t dir,
 }
 
 int delete_metadata(struct gfs2_inode *ip, uint64_t block,
-		    struct gfs2_buffer_head **bh, int h, void *private)
+		    struct gfs2_buffer_head **bh, int h, int *is_valid,
+		    int *was_duplicate, void *private)
 {
-	return delete_block_if_notdup(ip, block, bh, _("metadata"), private);
+	*is_valid = 1;
+	*was_duplicate = 0;
+	return delete_block_if_notdup(ip, block, bh, _("metadata"),
+				      was_duplicate, private);
 }
 
 int delete_leaf(struct gfs2_inode *ip, uint64_t block, void *private)
 {
-	return delete_block_if_notdup(ip, block, NULL, _("leaf"), private);
+	return delete_block_if_notdup(ip, block, NULL, _("leaf"), NULL,
+				      private);
 }
 
-int delete_data(struct gfs2_inode *ip, uint64_t block, void *private)
+int delete_data(struct gfs2_inode *ip, uint64_t metablock,
+		uint64_t block, void *private)
 {
-	return delete_block_if_notdup(ip, block, NULL, _("data"), private);
+	return delete_block_if_notdup(ip, block, NULL, _("data"), NULL,
+				      private);
 }
 
-int delete_eattr_indir(struct gfs2_inode *ip, uint64_t block, uint64_t parent,
-		       struct gfs2_buffer_head **bh, void *private)
+static int del_eattr_generic(struct gfs2_inode *ip, uint64_t block,
+			     uint64_t parent, struct gfs2_buffer_head **bh,
+			     void *private, const char *eatype)
 {
-	int ret;
+	int ret = 0;
+	int was_free = 0;
+	uint8_t q;
 
-	ret = delete_block_if_notdup(ip, block, NULL,
-				     _("indirect extended attribute"),
-				     private);
+	if (valid_block(ip->i_sbd, block)) {
+		q = block_type(block);
+		if (q == gfs2_block_free)
+			was_free = 1;
+		ret = delete_block_if_notdup(ip, block, NULL, eatype,
+					     NULL, private);
+		if (!ret) {
+			*bh = bread(ip->i_sbd, block);
+			if (!was_free)
+				ip->i_di.di_blocks--;
+			bmodified(ip->i_bh);
+		}
+	}
 	/* Even if it's a duplicate reference, we want to eliminate the
 	   reference itself, and adjust di_blocks accordingly. */
 	if (ip->i_di.di_eattr) {
-		ip->i_di.di_blocks--;
 		if (block == ip->i_di.di_eattr)
 			ip->i_di.di_eattr = 0;
 		bmodified(ip->i_bh);
@@ -1476,24 +1701,74 @@ int delete_eattr_indir(struct gfs2_inode *ip, uint64_t block, uint64_t parent,
 	return ret;
 }
 
+int delete_eattr_indir(struct gfs2_inode *ip, uint64_t block, uint64_t parent,
+		       struct gfs2_buffer_head **bh, void *private)
+{
+	return del_eattr_generic(ip, block, parent, bh, private,
+				 _("extended attribute"));
+}
+
 int delete_eattr_leaf(struct gfs2_inode *ip, uint64_t block, uint64_t parent,
 		      struct gfs2_buffer_head **bh, void *private)
 {
-	int ret;
+	return del_eattr_generic(ip, block, parent, bh, private,
+				 _("indirect extended attribute"));
+}
 
-	ret = delete_block_if_notdup(ip, block, NULL, _("extended attribute"),
-				     private);
-	if (ip->i_di.di_eattr) {
-		ip->i_di.di_blocks--;
-		if (block == ip->i_di.di_eattr)
-			ip->i_di.di_eattr = 0;
-		bmodified(ip->i_bh);
+int delete_eattr_entry(struct gfs2_inode *ip, struct gfs2_buffer_head *leaf_bh,
+		       struct gfs2_ea_header *ea_hdr,
+		       struct gfs2_ea_header *ea_hdr_prev, void *private)
+{
+	struct gfs2_sbd *sdp = ip->i_sbd;
+	char ea_name[256];
+	uint32_t avail_size;
+	int max_ptrs;
+
+	if (!ea_hdr->ea_name_len){
+		/* Skip this entry for now */
+		return 1;
 	}
-	return ret;
+
+	memset(ea_name, 0, sizeof(ea_name));
+	strncpy(ea_name, (char *)ea_hdr + sizeof(struct gfs2_ea_header),
+		ea_hdr->ea_name_len);
+
+	if (!GFS2_EATYPE_VALID(ea_hdr->ea_type) &&
+	   ((ea_hdr_prev) || (!ea_hdr_prev && ea_hdr->ea_type))){
+		/* Skip invalid entry */
+		return 1;
+	}
+
+	if (!ea_hdr->ea_num_ptrs)
+		return 0;
+
+	avail_size = sdp->sd_sb.sb_bsize - sizeof(struct gfs2_meta_header);
+	max_ptrs = (be32_to_cpu(ea_hdr->ea_data_len) + avail_size - 1) /
+		avail_size;
+
+	if (max_ptrs > ea_hdr->ea_num_ptrs)
+		return 1;
+
+	log_debug( _("  Pointers Required: %d\n  Pointers Reported: %d\n"),
+		   max_ptrs, ea_hdr->ea_num_ptrs);
+
+	return 0;
+}
+
+int delete_eattr_extentry(struct gfs2_inode *ip, uint64_t *ea_data_ptr,
+			  struct gfs2_buffer_head *leaf_bh,
+			  struct gfs2_ea_header *ea_hdr,
+			  struct gfs2_ea_header *ea_hdr_prev, void *private)
+{
+	uint64_t block = be64_to_cpu(*ea_data_ptr);
+
+	return delete_block_if_notdup(ip, block, NULL, _("extended attribute"),
+				      NULL, private);
 }
 
 static int alloc_metalist(struct gfs2_inode *ip, uint64_t block,
-			  struct gfs2_buffer_head **bh, int h, void *private)
+			  struct gfs2_buffer_head **bh, int h, int *is_valid,
+			  int *was_duplicate, void *private)
 {
 	uint8_t q;
 	const char *desc = (const char *)private;
@@ -1501,19 +1776,22 @@ static int alloc_metalist(struct gfs2_inode *ip, uint64_t block,
 	/* No need to range_check here--if it was added, it's in range. */
 	/* We can't check the bitmap here because this function is called
 	   after the bitmap has been set but before the blockmap has. */
+	*is_valid = 1;
+	*was_duplicate = 0;
 	*bh = bread(ip->i_sbd, block);
 	q = block_type(block);
-	if (blockmap_to_bitmap(q) == GFS2_BLKST_FREE) { /* If not marked yet */
+	if (blockmap_to_bitmap(q, ip->i_sbd->gfs1) == GFS2_BLKST_FREE) {
 		log_debug(_("%s reference to new metadata block "
 			    "%lld (0x%llx) is now marked as indirect.\n"),
 			  desc, (unsigned long long)block,
 			  (unsigned long long)block);
 		gfs2_blockmap_set(bl, block, gfs2_indir_blk);
 	}
-	return 0;
+	return meta_is_good;
 }
 
-static int alloc_data(struct gfs2_inode *ip, uint64_t block, void *private)
+static int alloc_data(struct gfs2_inode *ip, uint64_t metablock,
+		      uint64_t block, void *private)
 {
 	uint8_t q;
 	const char *desc = (const char *)private;
@@ -1522,7 +1800,7 @@ static int alloc_data(struct gfs2_inode *ip, uint64_t block, void *private)
 	/* We can't check the bitmap here because this function is called
 	   after the bitmap has been set but before the blockmap has. */
 	q = block_type(block);
-	if (blockmap_to_bitmap(q) == GFS2_BLKST_FREE) { /* If not marked yet */
+	if (blockmap_to_bitmap(q, ip->i_sbd->gfs1) == GFS2_BLKST_FREE) {
 		log_debug(_("%s reference to new data block "
 			    "%lld (0x%llx) is now marked as data.\n"),
 			  desc, (unsigned long long)block,
@@ -1540,7 +1818,7 @@ static int alloc_leaf(struct gfs2_inode *ip, uint64_t block, void *private)
 	/* We can't check the bitmap here because this function is called
 	   after the bitmap has been set but before the blockmap has. */
 	q = block_type(block);
-	if (blockmap_to_bitmap(q) == GFS2_BLKST_FREE) /* If not marked yet */
+	if (blockmap_to_bitmap(q, ip->i_sbd->gfs1) == GFS2_BLKST_FREE)
 		fsck_blockmap_set(ip, block, _("newly allocated leaf"),
 				  gfs2_leaf_blk);
 	return 0;
@@ -1581,10 +1859,183 @@ void reprocess_inode(struct gfs2_inode *ip, const char *desc)
 	int error;
 
 	alloc_fxns.private = (void *)desc;
-	log_info( _("%s had blocks added; reprocessing its metadata tree "
-		    "at height=%d.\n"), desc, ip->i_di.di_height);
+	log_info( _("%s inode %llu (0x%llx) had blocks added; reprocessing "
+		    "its metadata tree at height=%d.\n"), desc,
+		  (unsigned long long)ip->i_di.di_num.no_addr,
+		  (unsigned long long)ip->i_di.di_num.no_addr,
+		  ip->i_di.di_height);
 	error = check_metatree(ip, &alloc_fxns);
 	if (error)
 		log_err( _("Error %d reprocessing the %s metadata tree.\n"),
 			 error, desc);
 }
+
+/*
+ * write_new_leaf - allocate and write a new leaf to cover a gap in hash table
+ * @dip: the directory inode
+ * @start_lindex: where in the hash table to start writing
+ * @num_copies: number of copies of the pointer to write into hash table
+ * @before_or_after: desc. of whether this is being added before/after/etc.
+ * @bn: pointer to return the newly allocated leaf's block number
+ */
+int write_new_leaf(struct gfs2_inode *dip, int start_lindex, int num_copies,
+		   const char *before_or_after, uint64_t *bn)
+{
+	struct gfs2_buffer_head *nbh;
+	struct gfs2_leaf *leaf;
+	struct gfs2_dirent *dent;
+	int count, i;
+	int factor = 0, pad_size;
+	uint64_t *cpyptr;
+	char *padbuf;
+	int divisor = num_copies;
+	int end_lindex = start_lindex + num_copies;
+
+	padbuf = malloc(num_copies * sizeof(uint64_t));
+	/* calculate the depth needed for the new leaf */
+	while (divisor > 1) {
+		factor++;
+		divisor /= 2;
+	}
+	/* Make sure the number of copies is properly a factor of 2 */
+	if ((1 << factor) != num_copies) {
+		log_err(_("Program error: num_copies not a factor of 2.\n"));
+		log_err(_("num_copies=%d, dinode = %lld (0x%llx)\n"),
+			num_copies,
+			(unsigned long long)dip->i_di.di_num.no_addr,
+			(unsigned long long)dip->i_di.di_num.no_addr);
+		log_err(_("lindex = %d (0x%x)\n"), start_lindex, start_lindex);
+		stack;
+		free(padbuf);
+		return -1;
+	}
+
+	/* allocate and write out a new leaf block */
+	*bn = meta_alloc(dip);
+	fsck_blockmap_set(dip, *bn, _("directory leaf"), gfs2_leaf_blk);
+	log_err(_("A new directory leaf was allocated at block %lld "
+		  "(0x%llx) to fill the %d (0x%x) pointer gap %s the existing "
+		  "pointer at index %d (0x%x).\n"), (unsigned long long)*bn,
+		(unsigned long long)*bn, num_copies, num_copies,
+		before_or_after, start_lindex, start_lindex);
+	dip->i_di.di_blocks++;
+	bmodified(dip->i_bh);
+	nbh = bget(dip->i_sbd, *bn);
+	memset(nbh->b_data, 0, dip->i_sbd->bsize);
+	leaf = (struct gfs2_leaf *)nbh->b_data;
+	leaf->lf_header.mh_magic = cpu_to_be32(GFS2_MAGIC);
+	leaf->lf_header.mh_type = cpu_to_be32(GFS2_METATYPE_LF);
+	leaf->lf_header.mh_format = cpu_to_be32(GFS2_FORMAT_LF);
+	leaf->lf_depth = cpu_to_be16(dip->i_di.di_depth - factor);
+
+	/* initialize the first dirent on the new leaf block */
+	dent = (struct gfs2_dirent *)(nbh->b_data + sizeof(struct gfs2_leaf));
+	dent->de_rec_len = cpu_to_be16(dip->i_sbd->bsize -
+				       sizeof(struct gfs2_leaf));
+	bmodified(nbh);
+	brelse(nbh);
+
+	/* pad the hash table with the new leaf block */
+	cpyptr = (uint64_t *)padbuf;
+	for (i = start_lindex; i < end_lindex; i++) {
+		*cpyptr = cpu_to_be64(*bn);
+		cpyptr++;
+	}
+	pad_size = num_copies * sizeof(uint64_t);
+	log_err(_("Writing to the hash table of directory %lld "
+		  "(0x%llx) at index: 0x%x for 0x%lx pointers.\n"),
+		(unsigned long long)dip->i_di.di_num.no_addr,
+		(unsigned long long)dip->i_di.di_num.no_addr,
+		start_lindex, pad_size / sizeof(uint64_t));
+	if (dip->i_sbd->gfs1)
+		count = gfs1_writei(dip, padbuf, start_lindex *
+				    sizeof(uint64_t), pad_size);
+	else
+		count = gfs2_writei(dip, padbuf, start_lindex *
+				    sizeof(uint64_t), pad_size);
+	free(padbuf);
+	if (count != pad_size) {
+		log_err( _("Error: bad write while fixing directory leaf "
+			   "pointers.\n"));
+		return -1;
+	}
+	return 0;
+}
+
+/* repair_leaf - Warn the user of an error and ask permission to fix it
+ * Process a bad leaf pointer and ask to repair the first time.
+ * The repair process involves extending the previous leaf's entries
+ * so that they replace the bad ones.  We have to hack up the old
+ * leaf a bit, but it's better than deleting the whole directory,
+ * which is what used to happen before. */
+int repair_leaf(struct gfs2_inode *ip, uint64_t *leaf_no, int lindex,
+		int ref_count, const char *msg, int allow_alloc)
+{
+	int new_leaf_blks = 0, error, refs;
+	uint64_t bn = 0;
+
+	log_err( _("Directory Inode %llu (0x%llx) points to leaf %llu"
+		   " (0x%llx) %s.\n"),
+		 (unsigned long long)ip->i_di.di_num.no_addr,
+		 (unsigned long long)ip->i_di.di_num.no_addr,
+		 (unsigned long long)*leaf_no,
+		 (unsigned long long)*leaf_no, msg);
+	if (!query( _("Attempt to patch around it? (y/n) "))) {
+		log_err( _("Bad leaf left in place.\n"));
+		goto out;
+	}
+	if (!allow_alloc) {
+		uint64_t *cpyptr;
+		char *padbuf;
+		int pad_size, i;
+
+		padbuf = malloc(ref_count * sizeof(uint64_t));
+		cpyptr = (uint64_t *)padbuf;
+		for (i = 0; i < ref_count; i++) {
+			*cpyptr = 0;
+			cpyptr++;
+		}
+		pad_size = ref_count * sizeof(uint64_t);
+		log_err(_("Writing zeros to the hash table of directory %lld "
+			  "(0x%llx) at index: 0x%x for 0x%x pointers.\n"),
+			(unsigned long long)ip->i_di.di_num.no_addr,
+			(unsigned long long)ip->i_di.di_num.no_addr,
+			lindex, ref_count);
+		if (ip->i_sbd->gfs1)
+			gfs1_writei(ip, padbuf, lindex * sizeof(uint64_t),
+				    pad_size);
+		else
+			gfs2_writei(ip, padbuf, lindex * sizeof(uint64_t),
+				    pad_size);
+		free(padbuf);
+		log_err( _("Directory Inode %llu (0x%llx) patched.\n"),
+			 (unsigned long long)ip->i_di.di_num.no_addr,
+			 (unsigned long long)ip->i_di.di_num.no_addr);
+		goto out;
+	}
+	/* We can only write leafs in quantities that are factors of
+	   two, since leaves are doubled, not added sequentially.
+	   So if we have a hole that's not a factor of 2, we have to
+	   break it down into separate leaf blocks that are. */
+	while (ref_count) {
+		refs = 1;
+		while (refs <= ref_count) {
+			if (refs * 2 > ref_count)
+				break;
+			refs *= 2;
+		}
+		error = write_new_leaf(ip, lindex, refs, _("replacing"), &bn);
+		if (error)
+			return error;
+
+		new_leaf_blks++;
+		lindex += refs;
+		ref_count -= refs;
+	}
+	log_err( _("Directory Inode %llu (0x%llx) repaired.\n"),
+		 (unsigned long long)ip->i_di.di_num.no_addr,
+		 (unsigned long long)ip->i_di.di_num.no_addr);
+out:
+	*leaf_no = bn;
+	return new_leaf_blks;
+}
diff --git a/gfs2/fsck/metawalk.h b/gfs2/fsck/metawalk.h
index 7bebf1c..5e30bfe 100644
--- a/gfs2/fsck/metawalk.h
+++ b/gfs2/fsck/metawalk.h
@@ -1,15 +1,3 @@
-/*****************************************************************************
-*******************************************************************************
-**
-**  Copyright (C) 2005-2007 Red Hat, Inc.  All rights reserved.
-**
-**  This copyrighted material is made available to anyone wishing to use,
-**  modify, copy, or redistribute it subject to the terms and conditions
-**  of the GNU General Public License v.2.
-**
-*******************************************************************************
-******************************************************************************/
-
 #ifndef _METAWALK_H
 #define _METAWALK_H
 
@@ -27,37 +15,66 @@ extern int check_dir(struct gfs2_sbd *sdp, uint64_t block,
 		     struct metawalk_fxns *pass);
 extern int check_linear_dir(struct gfs2_inode *ip, struct gfs2_buffer_head *bh,
 			    struct metawalk_fxns *pass);
+extern int check_leaf(struct gfs2_inode *ip, int lindex,
+		      struct metawalk_fxns *pass, uint64_t *leaf_no,
+		      struct gfs2_leaf *leaf, int *ref_count);
 extern int remove_dentry_from_dir(struct gfs2_sbd *sdp, uint64_t dir,
 						   uint64_t dentryblock);
 extern int delete_block(struct gfs2_inode *ip, uint64_t block,
 		 struct gfs2_buffer_head **bh, const char *btype,
 		 void *private);
 extern int delete_metadata(struct gfs2_inode *ip, uint64_t block,
-			   struct gfs2_buffer_head **bh, int h, void *private);
+			   struct gfs2_buffer_head **bh, int h, int *is_valid,
+			   int *was_duplicate, void *private);
 extern int delete_leaf(struct gfs2_inode *ip, uint64_t block, void *private);
-extern int delete_data(struct gfs2_inode *ip, uint64_t block, void *private);
+extern int delete_data(struct gfs2_inode *ip, uint64_t metablock,
+		       uint64_t block, void *private);
 extern int delete_eattr_indir(struct gfs2_inode *ip, uint64_t block, uint64_t parent,
 		       struct gfs2_buffer_head **bh, void *private);
 extern int delete_eattr_leaf(struct gfs2_inode *ip, uint64_t block, uint64_t parent,
 		      struct gfs2_buffer_head **bh, void *private);
+extern int delete_eattr_entry(struct gfs2_inode *ip,
+			      struct gfs2_buffer_head *leaf_bh,
+			      struct gfs2_ea_header *ea_hdr,
+			      struct gfs2_ea_header *ea_hdr_prev,
+			      void *private);
+extern int delete_eattr_extentry(struct gfs2_inode *ip, uint64_t *ea_data_ptr,
+				 struct gfs2_buffer_head *leaf_bh,
+				 struct gfs2_ea_header *ea_hdr,
+				 struct gfs2_ea_header *ea_hdr_prev,
+				 void *private);
+
 extern int _fsck_blockmap_set(struct gfs2_inode *ip, uint64_t bblock,
-		       const char *btype, enum gfs2_mark_block mark,
-		       const char *caller, int line);
+			      const char *btype, enum gfs2_mark_block mark,
+			      int error_on_dinode,
+			      const char *caller, int line);
 extern int check_n_fix_bitmap(struct gfs2_sbd *sdp, uint64_t blk,
-		       enum gfs2_mark_block new_blockmap_state);
+			      int error_on_dinode,
+			      enum gfs2_mark_block new_blockmap_state);
 extern void reprocess_inode(struct gfs2_inode *ip, const char *desc);
 extern struct duptree *dupfind(uint64_t block);
 extern struct gfs2_inode *fsck_system_inode(struct gfs2_sbd *sdp,
 					    uint64_t block);
 extern int find_remove_dup(struct gfs2_inode *ip, uint64_t block,
 			   const char *btype);
-extern int free_block_if_notdup(struct gfs2_inode *ip, uint64_t block,
-				const char *btype);
+extern int write_new_leaf(struct gfs2_inode *dip, int start_lindex,
+			  int num_copies, const char *before_or_after,
+			  uint64_t *bn);
+extern int repair_leaf(struct gfs2_inode *ip, uint64_t *leaf_no, int lindex,
+		       int ref_count, const char *msg, int allow_alloc);
 
 #define is_duplicate(dblock) ((dupfind(dblock)) ? 1 : 0)
 
-#define fsck_blockmap_set(ip, b, bt, m) _fsck_blockmap_set(ip, b, bt, m, \
-							   __FUNCTION__, __LINE__)
+#define fsck_blockmap_set(ip, b, bt, m) \
+	_fsck_blockmap_set(ip, b, bt, m, 0, __FUNCTION__, __LINE__)
+#define fsck_blkmap_set_noino(ip, b, bt, m) \
+	_fsck_blockmap_set(ip, b, bt, m, 1, __FUNCTION__, __LINE__)
+
+enum meta_check_rc {
+	meta_error = -1,
+	meta_is_good = 0,
+	meta_skip_further = 1,
+};
 
 /* metawalk_fxns: function pointers to check various parts of the fs
  *
@@ -76,13 +93,31 @@ extern int free_block_if_notdup(struct gfs2_inode *ip, uint64_t block,
  */
 struct metawalk_fxns {
 	void *private;
+	int invalid_meta_is_fatal;
+	int (*check_leaf_depth) (struct gfs2_inode *ip, uint64_t leaf_no,
+				 int ref_count, struct gfs2_buffer_head *lbh);
 	int (*check_leaf) (struct gfs2_inode *ip, uint64_t block,
 			   void *private);
+	/* parameters to the check_metalist sub-functions:
+	   ip: incore inode pointer
+	   block: block number of the metadata block to be checked
+	   bh: buffer_head to be returned
+	   h: height
+	   is_valid: returned as 1 if the metadata block is valid and should
+	             be added to the metadata list for further processing.
+	   was_duplicate: returns as 1 if the metadata block was determined
+	             to be a duplicate reference, in which case we want to
+		     skip adding it to the metadata list.
+	   private: Pointer to pass-specific data
+	   returns: 0 - everything is good, but there may be duplicates
+	            1 - skip further processing
+	*/
 	int (*check_metalist) (struct gfs2_inode *ip, uint64_t block,
 			       struct gfs2_buffer_head **bh, int h,
+			       int *is_valid, int *was_duplicate,
 			       void *private);
-	int (*check_data) (struct gfs2_inode *ip, uint64_t block,
-			   void *private);
+	int (*check_data) (struct gfs2_inode *ip, uint64_t metablock,
+			   uint64_t block, void *private);
 	int (*check_eattr_indir) (struct gfs2_inode *ip, uint64_t block,
 				  uint64_t parent,
 				  struct gfs2_buffer_head **bh, void *private);
@@ -92,7 +127,8 @@ struct metawalk_fxns {
 	int (*check_dentry) (struct gfs2_inode *ip, struct gfs2_dirent *de,
 			     struct gfs2_dirent *prev,
 			     struct gfs2_buffer_head *bh,
-			     char *filename, uint32_t *count, void *private);
+			     char *filename, uint32_t *count,
+			     int lindex, void *private);
 	int (*check_eattr_entry) (struct gfs2_inode *ip,
 				  struct gfs2_buffer_head *leaf_bh,
 				  struct gfs2_ea_header *ea_hdr,
@@ -107,9 +143,15 @@ struct metawalk_fxns {
 	int (*finish_eattr_indir) (struct gfs2_inode *ip, int leaf_pointers,
 				   int leaf_pointer_errors, void *private);
 	void (*big_file_msg) (struct gfs2_inode *ip, uint64_t blks_checked);
-	int (*check_num_ptrs) (struct gfs2_inode *ip, uint64_t leafno,
-			       int *ref_count, int *lindex,
-			       struct gfs2_leaf *leaf);
+	int (*check_hash_tbl) (struct gfs2_inode *ip, uint64_t *tbl,
+			       unsigned hsize, void *private);
+	int (*repair_leaf) (struct gfs2_inode *ip, uint64_t *leaf_no,
+			    int lindex, int ref_count, const char *msg,
+			    void *private);
+	int (*undo_check_meta) (struct gfs2_inode *ip, uint64_t block,
+				int h, void *private);
+	int (*undo_check_data) (struct gfs2_inode *ip, uint64_t block,
+				void *private);
 };
 
 #endif /* _METAWALK_H */
diff --git a/gfs2/fsck/misc.c b/gfs2/fsck/misc.c
new file mode 100644
index 0000000..4c676ec
--- /dev/null
+++ b/gfs2/fsck/misc.c
@@ -0,0 +1,607 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <time.h>
+#include <errno.h>
+#include <sys/mount.h>
+#include <linux/types.h>
+#include <sys/file.h>
+#include <dirent.h>
+#include <linux/kdev_t.h>
+#include <sys/sysmacros.h>
+#include <mntent.h>
+#include <sys/time.h>
+#include <signal.h>
+
+#include "libgfs2.h"
+
+#define PAGE_SIZE (4096)
+#define SYS_BASE "/sys/fs/gfs2" /* FIXME: Look in /proc/mounts to find this */
+#define DIV_RU(x, y) (((x) + (y) - 1) / (y))
+
+static char sysfs_buf[PAGE_SIZE];
+int metafs_interrupted = 0;
+
+int 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;
+		m = space % inptrs;
+		d = space / inptrs;
+
+		if (d != heightsize[x - 1] || m)
+			break;
+		heightsize[x] = space;
+	}
+	if (x > GFS2_MAX_META_HEIGHT)
+		die("bad constants (1)\n");
+	return x;
+}
+
+int compute_constants(struct gfs2_sbd *sdp)
+{
+	uint32_t hash_blocks, ind_blocks, leaf_blocks;
+	uint32_t tmp_blocks;
+
+	sdp->md.next_inum = 1;
+
+	sdp->sd_sb.sb_bsize_shift = ffs(sdp->bsize) - 1;
+	sdp->sb_addr = GFS2_SB_ADDR * GFS2_BASIC_BLOCK / sdp->bsize;
+
+	sdp->sd_fsb2bb_shift = sdp->sd_sb.sb_bsize_shift -
+		GFS2_BASIC_BLOCK_SHIFT;
+	sdp->sd_fsb2bb = 1 << sdp->sd_fsb2bb_shift;
+	sdp->sd_diptrs = (sdp->bsize - sizeof(struct gfs2_dinode)) /
+		sizeof(uint64_t);
+	sdp->sd_inptrs = (sdp->bsize - sizeof(struct gfs2_meta_header)) /
+		sizeof(uint64_t);
+	sdp->sd_jbsize = sdp->bsize - sizeof(struct gfs2_meta_header);
+	sdp->sd_hash_bsize = sdp->bsize / 2;
+	sdp->sd_hash_bsize_shift = sdp->sd_sb.sb_bsize_shift - 1;
+	sdp->sd_hash_ptrs = sdp->sd_hash_bsize / sizeof(uint64_t);
+
+	/*  Compute maximum reservation required to add a entry to a directory  */
+
+	hash_blocks = DIV_RU(sizeof(uint64_t) * (1 << GFS2_DIR_MAX_DEPTH),
+			     sdp->sd_jbsize);
+
+	ind_blocks = 0;
+	for (tmp_blocks = hash_blocks; tmp_blocks > sdp->sd_diptrs;) {
+		tmp_blocks = DIV_RU(tmp_blocks, sdp->sd_inptrs);
+		ind_blocks += tmp_blocks;
+	}
+
+	leaf_blocks = 2 + GFS2_DIR_MAX_DEPTH;
+
+	sdp->sd_max_dirres = hash_blocks + ind_blocks + leaf_blocks;
+
+	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);
+	return 0;
+}
+
+int is_pathname_mounted(struct gfs2_sbd *sdp, int *ro_mount)
+{
+	FILE *fp;
+	struct mntent *mnt;
+	dev_t file_dev=0, file_rdev=0;
+	ino_t file_ino=0;
+	struct stat st_buf;
+
+	*ro_mount = 0;
+	if ((fp = setmntent("/proc/mounts", "r")) == NULL) {
+		perror("open: /proc/mounts");
+		return 0;
+	}
+	if (stat(sdp->path_name, &st_buf) == 0) {
+		if (S_ISBLK(st_buf.st_mode)) {
+			file_rdev = st_buf.st_rdev;
+		} else {
+			file_dev = st_buf.st_dev;
+			file_ino = st_buf.st_ino;
+		}
+	}
+	while ((mnt = getmntent (fp)) != NULL) {
+		/* Check if they specified the device instead of mnt point */
+		if (strcmp(sdp->device_name, mnt->mnt_fsname) == 0) {
+			strcpy(sdp->path_name, mnt->mnt_dir); /* fix it */
+			break;
+		}
+		if (strcmp(sdp->path_name, mnt->mnt_dir) == 0) {
+			strcpy(sdp->device_name, mnt->mnt_fsname); /* fix it */
+			break;
+		}
+		if (stat(mnt->mnt_fsname, &st_buf) != 0)
+			continue;
+
+		if (S_ISBLK(st_buf.st_mode)) {
+			if (file_rdev && (file_rdev == st_buf.st_rdev)) {
+				strcpy(sdp->device_name, mnt->mnt_fsname);
+				strcpy(sdp->path_name, mnt->mnt_dir);
+				break;
+			}
+		} else {
+			if (file_dev && ((file_dev == st_buf.st_dev) &&
+					 (file_ino == st_buf.st_ino))) {
+				strcpy(sdp->device_name, mnt->mnt_fsname);
+				strcpy(sdp->path_name, mnt->mnt_dir);
+				break;
+			}
+		}
+	}
+	endmntent (fp);
+	if (mnt == NULL)
+		return 0;
+	if (stat(mnt->mnt_dir, &st_buf) < 0) {
+		if (errno == ENOENT)
+			return 0;
+	}
+	/* Can't trust fstype because / has "rootfs". */
+	if (file_rdev && (st_buf.st_dev != file_rdev))
+		return 0;
+	if (hasmntopt(mnt, MNTOPT_RO))
+               *ro_mount = 1;
+	return 1; /* mounted */
+}
+
+int is_gfs2(struct gfs2_sbd *sdp)
+{
+	int fd, rc;
+	struct gfs2_sb sb;
+
+	fd = open(sdp->device_name, O_RDWR);
+	if (fd < 0)
+		return 0;
+
+	rc = 0;
+	if (lseek(fd, GFS2_SB_ADDR * GFS2_BASIC_BLOCK, SEEK_SET) >= 0 &&
+	    read(fd, &sb, sizeof(sb)) == sizeof(sb) &&
+	    be32_to_cpu(sb.sb_header.mh_magic) == GFS2_MAGIC &&
+	    be32_to_cpu(sb.sb_header.mh_type) == GFS2_METATYPE_SB)
+		rc = 1;
+	close(fd);
+	return rc;
+}
+
+int check_for_gfs2(struct gfs2_sbd *sdp)
+{
+	int ro;
+
+	if (!is_pathname_mounted(sdp, &ro))
+		return -1;
+	if (!is_gfs2(sdp))
+		return -1;
+	return 0;
+}
+
+static int lock_for_admin(struct gfs2_sbd *sdp)
+{
+	int error;
+
+	if (sdp->debug)
+		printf("\nTrying to get admin lock...\n");
+
+	sdp->metafs_fd = open(sdp->metafs_path, O_RDONLY | O_NOFOLLOW);
+	if (sdp->metafs_fd < 0)
+		return -1;
+
+	error = flock(sdp->metafs_fd, LOCK_EX);
+	if (error)
+		return -1;
+	if (sdp->debug)
+		printf("Got it.\n");
+	return 0;
+}
+
+static void remove_mtab_entry(struct gfs2_sbd *sdp)
+{
+	FILE *mtab, *mtabnew;
+	struct mntent *mountent;
+	char mtab_tmpfn[PATH_MAX];
+	int error, fd;
+	mode_t mask;
+
+	mtab = setmntent("/etc/mtab", "rt");
+	if (mtab == NULL)
+		die("Couldn't open /etc/mtab for writing: %s\n",
+		    strerror(errno));
+	strcpy(mtab_tmpfn, "/etc/mtab.XXXXXX");
+	mask = umask(S_IXUSR | S_IRWXG | S_IRWXO);
+	fd = mkstemp(mtab_tmpfn);
+	umask(mask);
+	if (fd < 0)
+		die("Couldn't open temporary mtab file for writing: %s\n",
+		    strerror(errno));
+
+	mtabnew = fdopen(fd, "wt");
+	if (mtabnew == NULL)
+		die("Couldn't open %s for writing : %s\n", mtab_tmpfn,
+		    strerror(errno));
+
+	while ((mountent = getmntent(mtab)) != NULL) {
+		if (!strcmp(mountent->mnt_dir, sdp->metafs_path))
+			continue;
+		error = addmntent(mtabnew, mountent);
+		if (error)
+			die("Unable to remove mount entry from mtab.\n");
+	}
+
+	endmntent(mtab);
+	fclose(mtabnew);
+	close(fd);
+	if (rename(mtab_tmpfn, "/etc/mtab"))
+		fprintf(stderr, "Unable to remove mount entry from mtab.\n");
+}
+
+void cleanup_metafs(struct gfs2_sbd *sdp)
+{
+	int ret;
+	struct sigaction sa = {	.sa_handler = SIG_DFL };
+
+	if (sdp->metafs_fd <= 0)
+		return;
+
+	fsync(sdp->metafs_fd);
+	close(sdp->metafs_fd);
+	ret = umount(sdp->metafs_path);
+	if (ret)
+		fprintf(stderr, "Couldn't unmount %s : %s\n",
+			sdp->metafs_path, strerror(errno));
+	else
+		rmdir(sdp->metafs_path);
+	sigaction(SIGINT, &sa, NULL);
+	sigaction(SIGILL, &sa, NULL);
+	sigaction(SIGTERM, &sa, NULL);
+	sigaction(SIGHUP, &sa, NULL);
+	sigaction(SIGABRT, &sa, NULL);
+	sigaction(SIGSEGV, &sa, NULL);
+	sigaction(SIGCONT, &sa, NULL);
+	sigaction(SIGUSR1, &sa, NULL);
+	sigaction(SIGUSR2, &sa, NULL);
+	metafs_interrupted = 0;
+	remove_mtab_entry(sdp);
+}
+
+static void sighandler(int error)
+{
+	metafs_interrupted = 1;
+}
+
+int mount_gfs2_meta(struct gfs2_sbd *sdp)
+{
+	int ret;
+	struct mntent ment;
+	char mnt_type[5] = "gfs2";
+	char mnt_opts[9] = "gfs2meta";
+	FILE *mtab;
+	struct sigaction sa = {	.sa_handler = &sighandler };
+
+	memset(sdp->metafs_path, 0, PATH_MAX);
+	snprintf(sdp->metafs_path, PATH_MAX - 1, "/tmp/.gfs2meta.XXXXXX");
+
+	if(!mkdtemp(sdp->metafs_path))
+		return -1;
+
+	mtab = setmntent("/etc/mtab", "at");
+	if (mtab == NULL) {
+		rmdir(sdp->metafs_path);
+		return -1;
+	}
+
+	sigaction(SIGINT, &sa, NULL);
+	sigaction(SIGILL, &sa, NULL);
+	sigaction(SIGTERM, &sa, NULL);
+	sigaction(SIGHUP, &sa, NULL);
+	sigaction(SIGABRT, &sa, NULL);
+	sigaction(SIGSEGV, &sa, NULL);
+	sigaction(SIGCONT, &sa, NULL);
+	sigaction(SIGUSR1, &sa, NULL);
+	sigaction(SIGUSR2, &sa, NULL);
+	ret = mount(sdp->path_name, sdp->metafs_path, "gfs2meta", 0, NULL);
+	if (ret) {
+		rmdir(sdp->metafs_path);
+		return -1;
+	}
+	ment.mnt_fsname = sdp->path_name;
+	ment.mnt_dir = sdp->metafs_path;
+	ment.mnt_type = mnt_type;
+	ment.mnt_opts = mnt_opts;
+	addmntent(mtab, &ment);
+	endmntent(mtab);
+	if (lock_for_admin(sdp))
+		return -1;
+	return 0;
+}
+
+static char *__get_sysfs(const char *fsname, const char *filename)
+{
+	char path[PATH_MAX];
+	int fd, rv;
+
+	memset(path, 0, PATH_MAX);
+	memset(sysfs_buf, 0, PAGE_SIZE);
+	snprintf(path, PATH_MAX - 1, "%s/%s/%s", SYS_BASE, fsname, filename);
+
+	fd = open(path, O_RDONLY);
+	if (fd < 0)
+		return NULL;
+	rv = read(fd, sysfs_buf, PAGE_SIZE);
+	if (rv < 0) {
+		close(fd);
+		return NULL;
+	}
+
+	close(fd);
+	return sysfs_buf;
+}
+
+char *get_sysfs(const char *fsname, const char *filename)
+{
+	char *s;
+	char *p;
+
+	s = __get_sysfs(fsname, filename);
+	if (!s)
+		return NULL;
+	p = strchr(s, '\n');
+	if (p)
+		*p = '\0';
+	return sysfs_buf;
+}
+
+int get_sysfs_uint(const char *fsname, const char *filename, unsigned int *val)
+{
+	char *s = __get_sysfs(fsname, filename);
+	int ret;
+	if (!s)
+		return -1;
+	ret = sscanf(s, "%u", val);
+	if (1 != ret) {
+		errno = ENOMSG;
+		return -1;
+	}
+	return 0;
+}
+
+int set_sysfs(const char *fsname, const char *filename, const char *val)
+{
+	char path[PATH_MAX];
+	int fd, rv, len;
+
+	len = strlen(val) + 1;
+	if (len > PAGE_SIZE) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	memset(path, 0, PATH_MAX);
+	snprintf(path, PATH_MAX - 1, "%s/%s/%s", SYS_BASE, fsname, filename);
+
+	fd = open(path, O_WRONLY);
+	if (fd < 0)
+		return -1;
+
+	rv = write(fd, val, len);
+	if (rv != len) {
+		close(fd);
+		return -1;
+	}
+	close(fd);
+	return 0;
+}
+
+/**
+ * get_list - Get the list of GFS2 filesystems
+ *
+ * Returns: a NULL terminated string
+ */
+
+#define LIST_SIZE 1048576
+
+char *
+get_list(void)
+{
+	char path[PATH_MAX];
+	char s_id[PATH_MAX];
+	char *list, *p;
+	int rv, fd, x = 0, total = 0;
+	DIR *d;
+	struct dirent *de;
+
+	list = malloc(LIST_SIZE);
+	if (!list)
+		die("out of memory\n");
+
+	memset(path, 0, PATH_MAX);
+	snprintf(path, PATH_MAX, "%s", SYS_BASE);
+
+	d = opendir(path);
+	if (!d)
+		die("can't open %s: %s\n", SYS_BASE, strerror(errno));
+
+	while ((de = readdir(d))) {
+		if (de->d_name[0] == '.')
+			continue;
+
+		memset(path, 0, PATH_MAX);
+		snprintf(path, PATH_MAX, "%s/%s/id", SYS_BASE, de->d_name);
+
+		fd = open(path, O_RDONLY);
+		if (fd < 0)
+			die("can't open %s: %s\n", path, strerror(errno));
+
+		memset(s_id, 0, PATH_MAX);
+
+		rv = read(fd, s_id, sizeof(s_id));
+		if (rv < 0)
+			die("can't read %s: %s\n", path, strerror(errno));
+
+		close(fd);
+
+		p = strstr(s_id, "\n");
+		if (p)
+			*p = '\0';
+
+		total += strlen(s_id) + strlen(de->d_name) + 2;
+		if (total > LIST_SIZE)
+			break;
+
+		x += sprintf(list + x, "%s %s\n", s_id, de->d_name);
+
+	}
+
+	closedir(d);
+
+	return list;
+}
+
+char *find_debugfs_mount(void)
+{
+	FILE *file;
+	char line[PATH_MAX];
+	char device[PATH_MAX], type[PATH_MAX];
+	char *path;
+
+	file = fopen("/proc/mounts", "rt");
+	if (!file)
+		return NULL;
+
+	path = malloc(PATH_MAX);
+	if (!path) {
+		fclose(file);
+		return NULL;
+	}
+	while (fgets(line, PATH_MAX, file)) {
+
+		if (sscanf(line, "%s %s %s", device, path, type) != 3)
+			continue;
+		if (!strcmp(type, "debugfs")) {
+			fclose(file);
+			return path;
+		}
+	}
+
+	free(path);
+	fclose(file);
+	return NULL;
+}
+
+/**
+ * mp2fsname - Find the name for a filesystem given its mountpoint
+ *
+ * We do this by iterating through gfs2 dirs in /sys/fs/gfs2/ looking for
+ * one where the "id" attribute matches the device id returned by stat for
+ * the mount point.  The reason we go through all this is simple: the
+ * kernel's sysfs is named after the VFS s_id, not the device name.
+ * So it's perfectly legal to do something like this to simulate user
+ * conditions without the proper hardware:
+ *    # rm /dev/sdb1
+ *    # mkdir /dev/cciss
+ *    # mknod /dev/cciss/c0d0p1 b 8 17
+ *    # mount -tgfs2 /dev/cciss/c0d0p1 /mnt/gfs2
+ *    # gfs2_tool gettune /mnt/gfs2
+ * In this example the tuning variables are in a directory named after the
+ * VFS s_id, which in this case would be /sys/fs/gfs2/sdb1/
+ *
+ * Returns: the fsname
+ */
+
+char *mp2fsname(char *mp)
+{
+	char device_id[PATH_MAX], *fsname = NULL;
+	struct stat statbuf;
+	DIR *d;
+	struct dirent *de;
+	char *id;
+
+	if (stat(mp, &statbuf))
+		return NULL;
+
+	memset(device_id, 0, sizeof(device_id));
+	sprintf(device_id, "%i:%i", major(statbuf.st_dev),
+		minor(statbuf.st_dev));
+
+	d = opendir(SYS_BASE);
+	if (!d)
+		return NULL;
+
+	while ((de = readdir(d))) {
+		if (de->d_name[0] == '.')
+			continue;
+
+		id = get_sysfs(de->d_name, "id");
+		if (!id)
+			continue;
+		if (strcmp(id, device_id) == 0) {
+			fsname = strdup(de->d_name);
+			break;
+		}
+	}
+
+	closedir(d);
+
+	return fsname;
+}
+
+/*
+ * get_random_bytes - Generate a series of random bytes using /dev/urandom.
+ *
+ * Modified from original code in gen_uuid.c in e2fsprogs/lib
+ */
+void get_random_bytes(void *buf, int nbytes)
+{
+	int i, n = nbytes, fd;
+	int lose_counter = 0;
+	unsigned char *cp = (unsigned char *) buf;
+	struct timeval	tv;
+
+	gettimeofday(&tv, 0);
+	fd = open("/dev/urandom", O_RDONLY);
+	srand((getpid() << 16) ^ getuid() ^ tv.tv_sec ^ tv.tv_usec);
+	/* Crank the random number generator a few times */
+	gettimeofday(&tv, 0);
+	for (i = (tv.tv_sec ^ tv.tv_usec) & 0x1F; i > 0; i--)
+		rand();
+	if (fd >= 0) {
+		while (n > 0) {
+			i = read(fd, cp, n);
+			if (i <= 0) {
+				if (lose_counter++ > 16)
+					break;
+				continue;
+			}
+			n -= i;
+			cp += i;
+			lose_counter = 0;
+		}
+		close(fd);
+	}
+
+	/*
+	 * We do this all the time, but this is the only source of
+	 * randomness if /dev/random/urandom is out to lunch.
+	 */
+	for (cp = buf, i = 0; i < nbytes; i++)
+		*cp++ ^= (rand() >> 7) & 0xFF;
+
+	return;
+}
diff --git a/gfs2/fsck/ondisk.c b/gfs2/fsck/ondisk.c
new file mode 100644
index 0000000..e43cd26
--- /dev/null
+++ b/gfs2/fsck/ondisk.c
@@ -0,0 +1,638 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <ctype.h>
+
+#include <linux/types.h>
+#include "libgfs2.h"
+
+#define pv(struct, member, fmt, fmt2) do {				\
+		print_it("  "#member, fmt, fmt2, struct->member);	\
+	} while (FALSE);
+#define pv2(struct, member, fmt, fmt2) do {				\
+		print_it("  ", fmt, fmt2, struct->member);		\
+	} while (FALSE);
+
+
+#define CPIN_08(s1, s2, member, count) {memcpy((s1->member), (s2->member), (count));}
+#define CPOUT_08(s1, s2, member, count) {memcpy((s2->member), (s1->member), (count));}
+#define CPIN_16(s1, s2, member) {(s1->member) = be16_to_cpu((s2->member));}
+#define CPOUT_16(s1, s2, member) {(s2->member) = cpu_to_be16((s1->member));}
+#define CPIN_32(s1, s2, member) {(s1->member) = be32_to_cpu((s2->member));}
+#define CPOUT_32(s1, s2, member) {(s2->member) = cpu_to_be32((s1->member));}
+#define CPIN_64(s1, s2, member) {(s1->member) = be64_to_cpu((s2->member));}
+#define CPOUT_64(s1, s2, member) {(s2->member) = cpu_to_be64((s1->member));}
+
+/*
+ * gfs2_xxx_in - read in an xxx struct
+ * first arg: the cpu-order structure
+ * bh: the disk-order buffer_head
+ *
+ * gfs2_xxx_out - write out an xxx struct
+ * first arg: the cpu-order structure
+ * bh: the disk-order buffer_head
+ *
+ * gfs2_xxx_print - print out an xxx struct
+ * first arg: the cpu-order structure
+ */
+
+void gfs2_inum_in(struct gfs2_inum *no, char *buf)
+{
+	struct gfs2_inum *str = (struct gfs2_inum *)buf;
+
+	CPIN_64(no, str, no_formal_ino);
+	CPIN_64(no, str, no_addr);
+}
+
+void gfs2_inum_out(struct gfs2_inum *no, char *buf)
+{
+	struct gfs2_inum *str = (struct gfs2_inum *)buf;
+
+	CPOUT_64(no, str, no_formal_ino);
+	CPOUT_64(no, str, no_addr);
+}
+
+void gfs2_inum_print(struct gfs2_inum *no)
+{
+	pv(no, no_formal_ino, "%llu", "0x%llx");
+	pv(no, no_addr, "%llu", "0x%llx");
+}
+
+void gfs2_meta_header_in(struct gfs2_meta_header *mh,
+			 struct gfs2_buffer_head *bh)
+{
+	struct gfs2_meta_header *str = (struct gfs2_meta_header *)bh->b_data;
+
+	CPIN_32(mh, str, mh_magic);
+	CPIN_32(mh, str, mh_type);
+	CPIN_32(mh, str, mh_format);
+}
+
+void gfs2_meta_header_out(struct gfs2_meta_header *mh,
+			  struct gfs2_buffer_head *bh)
+{
+	struct gfs2_meta_header *str = (struct gfs2_meta_header *)bh->b_data;
+
+	CPOUT_32(mh, str, mh_magic);
+	CPOUT_32(mh, str, mh_type);
+	CPOUT_32(mh, str, mh_format);
+	str->__pad0 = 0;
+	str->__pad1 = 0;
+	bmodified(bh);
+}
+
+void gfs2_meta_header_print(struct gfs2_meta_header *mh)
+{
+	pv(mh, mh_magic, "0x%08X", NULL);
+	pv(mh, mh_type, "%u", "0x%x");
+	pv(mh, mh_format, "%u", "0x%x");
+}
+
+void gfs2_sb_in(struct gfs2_sb *sb, struct gfs2_buffer_head *bh)
+{
+	struct gfs2_sb *str = (struct gfs2_sb *)bh->b_data;
+
+	gfs2_meta_header_in(&sb->sb_header, bh);
+
+	CPIN_32(sb, str, sb_fs_format);
+	CPIN_32(sb, str, sb_multihost_format);
+	CPIN_32(sb, str, __pad0);                        /* gfs sb_flags */
+
+	CPIN_32(sb, str, sb_bsize);
+	CPIN_32(sb, str, sb_bsize_shift);
+	CPIN_32(sb, str, __pad1);                        /* gfs sb_seg_size */
+
+	gfs2_inum_in(&sb->sb_master_dir, (char *)&str->sb_master_dir);
+	gfs2_inum_in(&sb->sb_root_dir, (char *)&str->sb_root_dir);
+
+	CPIN_08(sb, str, sb_lockproto, GFS2_LOCKNAME_LEN);
+	CPIN_08(sb, str, sb_locktable, GFS2_LOCKNAME_LEN);
+	gfs2_inum_in(&sb->__pad2, (char *)&str->__pad2); /* gfs rindex */
+	gfs2_inum_in(&sb->__pad3, (char *)&str->__pad3); /* gfs quota */
+	gfs2_inum_in(&sb->__pad4, (char *)&str->__pad4); /* gfs license */
+#ifdef GFS2_HAS_UUID
+	CPIN_08(sb, str, sb_uuid, sizeof(sb->sb_uuid));
+#endif
+}
+
+void gfs2_sb_out(struct gfs2_sb *sb, struct gfs2_buffer_head *bh)
+{
+	struct gfs2_sb *str = (struct gfs2_sb *)bh->b_data;
+
+	gfs2_meta_header_out(&sb->sb_header, bh);
+
+	CPOUT_32(sb, str, sb_fs_format);
+	CPOUT_32(sb, str, sb_multihost_format);
+
+	CPOUT_32(sb, str, sb_bsize);
+	CPOUT_32(sb, str, sb_bsize_shift);
+
+	gfs2_inum_out(&sb->sb_master_dir, (char *)&str->sb_master_dir);
+	gfs2_inum_out(&sb->sb_root_dir, (char *)&str->sb_root_dir);
+
+	CPOUT_08(sb, str, sb_lockproto, GFS2_LOCKNAME_LEN);
+	CPOUT_08(sb, str, sb_locktable, GFS2_LOCKNAME_LEN);
+#ifdef GFS2_HAS_UUID
+	memcpy(str->sb_uuid, sb->sb_uuid, 16);
+#endif
+	bmodified(bh);
+}
+
+const char *str_uuid(const unsigned char *uuid)
+{
+	static char str[64];
+	char *ch;
+	int i;
+
+	memset(str, 0, sizeof(str));
+	ch = str;
+	for (i = 0; i < 16; i++) {
+		sprintf(ch, "%02x", uuid[i]);
+		ch += 2;
+		if ((i == 3) || (i == 5) || (i == 7) || (i == 9)) {
+			*ch = '-';
+			ch++;
+		}
+	}
+	return str;
+}
+
+#ifdef GFS2_HAS_UUID
+void gfs2_print_uuid(const unsigned char *uuid)
+{
+	print_it("  uuid", "%s", NULL, str_uuid(uuid));
+}
+#endif
+
+void gfs2_sb_print(struct gfs2_sb *sb)
+{
+	gfs2_meta_header_print(&sb->sb_header);
+
+	pv(sb, sb_fs_format, "%u", "0x%x");
+	pv(sb, sb_multihost_format, "%u", "0x%x");
+
+	pv(sb, sb_bsize, "%u", "0x%x");
+	pv(sb, sb_bsize_shift, "%u", "0x%x");
+
+	gfs2_inum_print(&sb->sb_master_dir);
+	gfs2_inum_print(&sb->sb_root_dir);
+
+	pv(sb, sb_lockproto, "%s", NULL);
+	pv(sb, sb_locktable, "%s", NULL);
+
+#ifdef GFS2_HAS_UUID
+	gfs2_print_uuid(sb->sb_uuid);
+#endif
+}
+
+void gfs2_rindex_in(struct gfs2_rindex *ri, char *buf)
+{
+	struct gfs2_rindex *str = (struct gfs2_rindex *)buf;
+
+	CPIN_64(ri, str, ri_addr);
+	CPIN_32(ri, str, ri_length);
+
+	CPIN_64(ri, str, ri_data0);
+	CPIN_32(ri, str, ri_data);
+
+	CPIN_32(ri, str, ri_bitbytes);
+
+	CPIN_08(ri, str, ri_reserved, 64);
+}
+
+void gfs2_rindex_out(struct gfs2_rindex *ri, char *buf)
+{
+	struct gfs2_rindex *str = (struct gfs2_rindex *)buf;
+
+	CPOUT_64(ri, str, ri_addr);
+	CPOUT_32(ri, str, ri_length);
+	ri->__pad = 0;
+
+	CPOUT_64(ri, str, ri_data0);
+	CPOUT_32(ri, str, ri_data);
+
+	CPOUT_32(ri, str, ri_bitbytes);
+
+	CPOUT_08(ri, str, ri_reserved, 64);
+}
+
+void gfs2_rindex_print(struct gfs2_rindex *ri)
+{
+	pv(ri, ri_addr, "%llu", "0x%llx");
+	pv(ri, ri_length, "%u", "0x%x");
+
+	pv(ri, ri_data0, "%llu", "0x%llx");
+	pv(ri, ri_data, "%u", "0x%x");
+
+	pv(ri, ri_bitbytes, "%u", "0x%x");
+}
+
+void gfs2_rgrp_in(struct gfs2_rgrp *rg, struct gfs2_buffer_head *bh)
+{
+	struct gfs2_rgrp *str = (struct gfs2_rgrp *)bh->b_data;
+
+	gfs2_meta_header_in(&rg->rg_header, bh);
+	CPIN_32(rg, str, rg_flags);
+	CPIN_32(rg, str, rg_free);
+	CPIN_32(rg, str, rg_dinodes);
+
+	CPIN_08(rg, str, rg_reserved, 80);
+}
+
+void gfs2_rgrp_out(struct gfs2_rgrp *rg, struct gfs2_buffer_head *bh)
+{
+	struct gfs2_rgrp *str = (struct gfs2_rgrp *)bh->b_data;
+
+	gfs2_meta_header_out(&rg->rg_header, bh);
+	CPOUT_32(rg, str, rg_flags);
+	CPOUT_32(rg, str, rg_free);
+	CPOUT_32(rg, str, rg_dinodes);
+
+	CPOUT_08(rg, str, rg_reserved, 80);
+	bmodified(bh);
+}
+
+void gfs2_rgrp_print(struct gfs2_rgrp *rg)
+{
+	gfs2_meta_header_print(&rg->rg_header);
+	pv(rg, rg_flags, "%u", "0x%x");
+	pv(rg, rg_free, "%u", "0x%x");
+	pv(rg, rg_dinodes, "%u", "0x%x");
+}
+
+void gfs2_quota_in(struct gfs2_quota *qu, char *buf)
+{
+	struct gfs2_quota *str = (struct gfs2_quota *)buf;
+
+	CPIN_64(qu, str, qu_limit);
+	CPIN_64(qu, str, qu_warn);
+	CPIN_64(qu, str, qu_value);
+	CPIN_08(qu, str, qu_reserved, sizeof(qu->qu_reserved));
+}
+
+void gfs2_quota_out(struct gfs2_quota *qu, char *buf)
+{
+	struct gfs2_quota *str = (struct gfs2_quota *)buf;
+
+	CPOUT_64(qu, str, qu_limit);
+	CPOUT_64(qu, str, qu_warn);
+	CPOUT_64(qu, str, qu_value);
+	memset(qu->qu_reserved, 0, sizeof(qu->qu_reserved));
+}
+
+void gfs2_quota_print(struct gfs2_quota *qu)
+{
+	pv(qu, qu_limit, "%llu", "0x%llx");
+	pv(qu, qu_warn, "%llu", "0x%llx");
+	pv(qu, qu_value, "%lld", "0x%llx");
+}
+
+void gfs2_dinode_in(struct gfs2_dinode *di, struct gfs2_buffer_head *bh)
+{
+	struct gfs2_dinode *str = (struct gfs2_dinode *)bh->b_data;
+
+	gfs2_meta_header_in(&di->di_header, bh);
+	gfs2_inum_in(&di->di_num, (char *)&str->di_num);
+
+	CPIN_32(di, str, di_mode);
+	CPIN_32(di, str, di_uid);
+	CPIN_32(di, str, di_gid);
+	CPIN_32(di, str, di_nlink);
+	CPIN_64(di, str, di_size);
+	CPIN_64(di, str, di_blocks);
+	CPIN_64(di, str, di_atime);
+	CPIN_64(di, str, di_mtime);
+	CPIN_64(di, str, di_ctime);
+	CPIN_32(di, str, di_major);
+	CPIN_32(di, str, di_minor);
+
+	CPIN_64(di, str, di_goal_meta);
+	CPIN_64(di, str, di_goal_data);
+
+	CPIN_32(di, str, di_flags);
+	CPIN_32(di, str, di_payload_format);
+	CPIN_16(di, str, __pad1);
+	CPIN_16(di, str, di_height);
+
+	CPIN_16(di, str, di_depth);
+	CPIN_32(di, str, di_entries);
+
+	CPIN_64(di, str, di_eattr);
+
+	CPIN_08(di, str, di_reserved, 32);
+}
+
+void gfs2_dinode_out(struct gfs2_dinode *di, struct gfs2_buffer_head *bh)
+{
+	struct gfs2_dinode *str = (struct gfs2_dinode *)bh->b_data;
+
+	gfs2_meta_header_out(&di->di_header, bh);
+	gfs2_inum_out(&di->di_num, (char *)&str->di_num);
+
+	CPOUT_32(di, str, di_mode);
+	CPOUT_32(di, str, di_uid);
+	CPOUT_32(di, str, di_gid);
+	CPOUT_32(di, str, di_nlink);
+	CPOUT_64(di, str, di_size);
+	CPOUT_64(di, str, di_blocks);
+	CPOUT_64(di, str, di_atime);
+	CPOUT_64(di, str, di_mtime);
+	CPOUT_64(di, str, di_ctime);
+	CPOUT_32(di, str, di_major);
+	CPOUT_32(di, str, di_minor);
+
+	CPOUT_64(di, str, di_goal_meta);
+	CPOUT_64(di, str, di_goal_data);
+
+	CPOUT_32(di, str, di_flags);
+	CPOUT_32(di, str, di_payload_format);
+	CPOUT_16(di, str, __pad1);
+	CPOUT_16(di, str, di_height);
+
+	CPOUT_16(di, str, di_depth);
+	CPOUT_32(di, str, di_entries);
+
+	CPOUT_64(di, str, di_eattr);
+
+	CPOUT_08(di, str, di_reserved, 32);
+	bmodified(bh);
+}
+
+void gfs2_dinode_print(struct gfs2_dinode *di)
+{
+	gfs2_meta_header_print(&di->di_header);
+	gfs2_inum_print(&di->di_num);
+
+	pv(di, di_mode, "0%o", NULL);
+	pv(di, di_uid, "%u", "0x%x");
+	pv(di, di_gid, "%u", "0x%x");
+	pv(di, di_nlink, "%u", "0x%x");
+	pv(di, di_size, "%llu", "0x%llx");
+	pv(di, di_blocks, "%llu", "0x%llx");
+	pv(di, di_atime, "%lld", "0x%llx");
+	pv(di, di_mtime, "%lld", "0x%llx");
+	pv(di, di_ctime, "%lld", "0x%llx");
+	pv(di, di_major, "%u", "0x%llx");
+	pv(di, di_minor, "%u", "0x%llx");
+
+	pv(di, di_goal_meta, "%llu", "0x%llx");
+	pv(di, di_goal_data, "%llu", "0x%llx");
+
+	pv(di, di_flags, "0x%.8X", NULL);
+	pv(di, di_payload_format, "%u", "0x%x");
+	pv(di, di_height, "%u", "0x%x");
+
+	pv(di, di_depth, "%u", "0x%x");
+	pv(di, di_entries, "%u", "0x%x");
+
+	pv(di, di_eattr, "%llu", "0x%llx");
+}
+
+void gfs2_dirent_in(struct gfs2_dirent *de, char *buf)
+{
+	struct gfs2_dirent *str = (struct gfs2_dirent *)buf;
+
+	gfs2_inum_in(&de->de_inum, buf);
+	CPIN_32(de, str, de_hash);
+	CPIN_16(de, str, de_rec_len);
+	CPIN_16(de, str, de_name_len);
+	CPIN_16(de, str, de_type);
+}
+
+void gfs2_dirent_out(struct gfs2_dirent *de, char *buf)
+{
+	struct gfs2_dirent *str = (struct gfs2_dirent *)buf;
+
+	gfs2_inum_out(&de->de_inum, buf);
+	CPOUT_32(de, str, de_hash);
+	CPOUT_16(de, str, de_rec_len);
+	CPOUT_16(de, str, de_name_len);
+	CPOUT_16(de, str, de_type);
+	memset(str->__pad, 0, sizeof(str->__pad));
+}
+
+void gfs2_dirent_print(struct gfs2_dirent *de, char *name)
+{
+	char buf[GFS2_FNAMESIZE + 1];
+
+	gfs2_inum_print(&de->de_inum);
+	pv(de, de_hash, "0x%.8X", NULL);
+	pv(de, de_rec_len, "%u", "0x%x");
+	pv(de, de_name_len, "%u", "0x%x");
+	pv(de, de_type, "%u", "0x%x");
+
+	memset(buf, 0, GFS2_FNAMESIZE + 1);
+	memcpy(buf, name, de->de_name_len);
+	print_it("  name", "%s", NULL, buf);
+}
+
+void gfs2_leaf_in(struct gfs2_leaf *lf, struct gfs2_buffer_head *bh)
+{
+	struct gfs2_leaf *str = (struct gfs2_leaf *)bh->b_data;
+
+	gfs2_meta_header_in(&lf->lf_header, bh);
+	CPIN_16(lf, str, lf_depth);
+	CPIN_16(lf, str, lf_entries);
+	CPIN_32(lf, str, lf_dirent_format);
+	CPIN_64(lf, str, lf_next);
+
+	CPIN_08(lf, str, lf_reserved, 32);
+}
+
+void gfs2_leaf_out(struct gfs2_leaf *lf, struct gfs2_buffer_head *bh)
+{
+	struct gfs2_leaf *str = (struct gfs2_leaf *)bh->b_data;
+
+	gfs2_meta_header_out(&lf->lf_header, bh);
+	CPOUT_16(lf, str, lf_depth);
+	CPOUT_16(lf, str, lf_entries);
+	CPOUT_32(lf, str, lf_dirent_format);
+	CPOUT_64(lf, str, lf_next);
+
+	CPOUT_08(lf, str, lf_reserved, 32);
+	bmodified(bh);
+}
+
+void gfs2_leaf_print(struct gfs2_leaf *lf)
+{
+	gfs2_meta_header_print(&lf->lf_header);
+	pv(lf, lf_depth, "%u", "0x%x");
+	pv(lf, lf_entries, "%u", "0x%x");
+	pv(lf, lf_dirent_format, "%u", "0x%x");
+	pv(lf, lf_next, "%llu", "0x%llx");
+}
+
+void gfs2_ea_header_in(struct gfs2_ea_header *ea, char *buf)
+{
+	struct gfs2_ea_header *str = (struct gfs2_ea_header *)buf;
+
+	CPIN_32(ea, str, ea_rec_len);
+	CPIN_32(ea, str, ea_data_len);
+	ea->ea_name_len = str->ea_name_len;
+	ea->ea_type = str->ea_type;
+	ea->ea_flags = str->ea_flags;
+	ea->ea_num_ptrs = str->ea_num_ptrs;
+}
+
+void gfs2_ea_header_out(struct gfs2_ea_header *ea, char *buf)
+{
+	struct gfs2_ea_header *str = (struct gfs2_ea_header *)buf;
+
+	CPOUT_32(ea, str, ea_rec_len);
+	CPOUT_32(ea, str, ea_data_len);
+	str->ea_name_len = ea->ea_name_len;
+	str->ea_type = ea->ea_type;
+	str->ea_flags = ea->ea_flags;
+	str->ea_num_ptrs = ea->ea_num_ptrs;
+	str->__pad = 0;
+}
+
+void gfs2_ea_header_print(struct gfs2_ea_header *ea, char *name)
+{
+	char buf[GFS2_EA_MAX_NAME_LEN + 1];
+
+	pv(ea, ea_rec_len, "%u", "0x%x");
+	pv(ea, ea_data_len, "%u", "0x%x");
+	pv(ea, ea_name_len, "%u", "0x%x");
+	pv(ea, ea_type, "%u", "0x%x");
+	pv(ea, ea_flags, "%u", "0x%x");
+	pv(ea, ea_num_ptrs, "%u", "0x%x");
+
+	memset(buf, 0, GFS2_EA_MAX_NAME_LEN + 1);
+	memcpy(buf, name, ea->ea_name_len);
+	print_it("  name", "%s", NULL, buf);
+}
+
+void gfs2_log_header_in(struct gfs2_log_header *lh,
+			struct gfs2_buffer_head *bh)
+{
+	struct gfs2_log_header *str = (struct gfs2_log_header *)bh->b_data;
+
+	gfs2_meta_header_in(&lh->lh_header, bh);
+	CPIN_64(lh, str, lh_sequence);
+	CPIN_32(lh, str, lh_flags);
+	CPIN_32(lh, str, lh_tail);
+	CPIN_32(lh, str, lh_blkno);
+	CPIN_32(lh, str, lh_hash);
+}
+
+void gfs2_log_header_out(struct gfs2_log_header *lh,
+			 struct gfs2_buffer_head *bh)
+{
+	struct gfs2_log_header *str = (struct gfs2_log_header *)bh->b_data;
+
+	gfs2_meta_header_out(&lh->lh_header, bh);
+	CPOUT_64(lh, str, lh_sequence);
+	CPOUT_32(lh, str, lh_flags);
+	CPOUT_32(lh, str, lh_tail);
+	CPOUT_32(lh, str, lh_blkno);
+	CPOUT_32(lh, str, lh_hash);
+	bmodified(bh);
+}
+
+void gfs2_log_header_print(struct gfs2_log_header *lh)
+{
+	gfs2_meta_header_print(&lh->lh_header);
+	pv(lh, lh_sequence, "%llu", "0x%llx");
+	pv(lh, lh_flags, "0x%.8X", NULL);
+	pv(lh, lh_tail, "%u", "0x%x");
+	pv(lh, lh_blkno, "%u", "0x%x");
+	pv(lh, lh_hash, "0x%.8X", NULL);
+}
+
+void gfs2_log_descriptor_in(struct gfs2_log_descriptor *ld,
+			    struct gfs2_buffer_head *bh)
+{
+	struct gfs2_log_descriptor *str = (struct gfs2_log_descriptor *)bh->b_data;
+
+	gfs2_meta_header_in(&ld->ld_header, bh);
+	CPIN_32(ld, str, ld_type);
+	CPIN_32(ld, str, ld_length);
+	CPIN_32(ld, str, ld_data1);
+	CPIN_32(ld, str, ld_data2);
+
+	CPIN_08(ld, str, ld_reserved, 32);
+}
+
+void gfs2_log_descriptor_out(struct gfs2_log_descriptor *ld,
+			     struct gfs2_buffer_head *bh)
+{
+	struct gfs2_log_descriptor *str = (struct gfs2_log_descriptor *)bh->b_data;
+
+	gfs2_meta_header_out(&ld->ld_header, bh);
+	CPOUT_32(ld, str, ld_type);
+	CPOUT_32(ld, str, ld_length);
+	CPOUT_32(ld, str, ld_data1);
+	CPOUT_32(ld, str, ld_data2);
+
+	CPOUT_08(ld, str, ld_reserved, 32);
+	bmodified(bh);
+}
+
+void gfs2_log_descriptor_print(struct gfs2_log_descriptor *ld)
+{
+	gfs2_meta_header_print(&ld->ld_header);
+	pv(ld, ld_type, "%u", "0x%x");
+	pv(ld, ld_length, "%u", "0x%x");
+	pv(ld, ld_data1, "%u", "0x%x");
+	pv(ld, ld_data2, "%u", "0x%x");
+}
+
+void gfs2_statfs_change_in(struct gfs2_statfs_change *sc, char *buf)
+{
+	struct gfs2_statfs_change *str = (struct gfs2_statfs_change *)buf;
+
+	CPIN_64(sc, str, sc_total);
+	CPIN_64(sc, str, sc_free);
+	CPIN_64(sc, str, sc_dinodes);
+}
+
+void gfs2_statfs_change_out(struct gfs2_statfs_change *sc, char *buf)
+{
+	struct gfs2_statfs_change *str = (struct gfs2_statfs_change *)buf;
+
+	CPOUT_64(sc, str, sc_total);
+	CPOUT_64(sc, str, sc_free);
+	CPOUT_64(sc, str, sc_dinodes);
+}
+
+void gfs2_statfs_change_print(struct gfs2_statfs_change *sc)
+{
+	pv(sc, sc_total, "%lld", "0x%llx");
+	pv(sc, sc_free, "%lld", "0x%llx");
+	pv(sc, sc_dinodes, "%lld", "0x%llx");
+}
+
+void gfs2_quota_change_in(struct gfs2_quota_change *qc,
+			  struct gfs2_buffer_head *bh)
+{
+	struct gfs2_quota_change *str = (struct gfs2_quota_change *)(bh->b_data +
+		sizeof(struct gfs2_meta_header));
+
+	CPIN_64(qc, str, qc_change);
+	CPIN_32(qc, str, qc_flags);
+	CPIN_32(qc, str, qc_id);
+}
+
+void gfs2_quota_change_out(struct gfs2_quota_change *qc,
+			   struct gfs2_buffer_head *bh)
+{
+	struct gfs2_quota_change *str = (struct gfs2_quota_change *)(bh->b_data +
+		sizeof(struct gfs2_meta_header));
+
+	CPOUT_64(qc, str, qc_change);
+	CPOUT_32(qc, str, qc_flags);
+	CPOUT_32(qc, str, qc_id);
+	bmodified(bh);
+}
+
+void gfs2_quota_change_print(struct gfs2_quota_change *qc)
+{
+	pv(qc, qc_change, "%lld", "0x%llx");
+	pv(qc, qc_flags, "0x%.8X", NULL);
+	pv(qc, qc_id, "%u", "0x%x");
+}
+
+
diff --git a/gfs2/fsck/pass1.c b/gfs2/fsck/pass1.c
index c0369c5..bc865bc 100644
--- a/gfs2/fsck/pass1.c
+++ b/gfs2/fsck/pass1.c
@@ -1,16 +1,3 @@
-/******************************************************************************
-*******************************************************************************
-**
-**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
-**  Copyright (C) 2004-2007 Red Hat, Inc.  All rights reserved.
-**
-**  This copyrighted material is made available to anyone wishing to use,
-**  modify, copy, or redistribute it subject to the terms and conditions
-**  of the GNU General Public License v.2.
-**
-*******************************************************************************
-******************************************************************************/
-
 /* pass1 checks inodes for format & type, duplicate blocks, & incorrect
  * block count.
  *
@@ -30,7 +17,6 @@
 #include <sys/ioctl.h>
 #include <inttypes.h>
 #include <libintl.h>
-#include <errno.h>
 #define _(String) gettext(String)
 
 #include "libgfs2.h"
@@ -38,22 +24,24 @@
 #include "inode_hash.h"
 #include "util.h"
 #include "link.h"
-#include "linux_endian.h"
 #include "metawalk.h"
 
+struct special_blocks gfs1_rindex_blks;
+
 struct block_count {
 	uint64_t indir_count;
 	uint64_t data_count;
 	uint64_t ea_count;
 };
 
-static int check_leaf(struct gfs2_inode *ip, uint64_t block, void *private);
+static int p1check_leaf(struct gfs2_inode *ip, uint64_t block, void *private);
 static int check_metalist(struct gfs2_inode *ip, uint64_t block,
-			  struct gfs2_buffer_head **bh, int h, void *private);
+			  struct gfs2_buffer_head **bh, int h, int *is_valid,
+			  int *was_duplicate, void *private);
 static int undo_check_metalist(struct gfs2_inode *ip, uint64_t block,
-			       struct gfs2_buffer_head **bh, int h,
-			       void *private);
-static int check_data(struct gfs2_inode *ip, uint64_t block, void *private);
+			       int h, void *private);
+static int check_data(struct gfs2_inode *ip, uint64_t metablock,
+		      uint64_t block, void *private);
 static int undo_check_data(struct gfs2_inode *ip, uint64_t block,
 			   void *private);
 static int check_eattr_indir(struct gfs2_inode *ip, uint64_t indirect,
@@ -72,17 +60,16 @@ static int check_extended_leaf_eattr(struct gfs2_inode *ip, uint64_t *data_ptr,
 				     struct gfs2_ea_header *ea_hdr,
 				     struct gfs2_ea_header *ea_hdr_prev,
 				     void *private);
-static int check_num_ptrs(struct gfs2_inode *ip, uint64_t leafno,
-			  int *ref_count, int *lindex, struct gfs2_leaf *leaf);
 static int finish_eattr_indir(struct gfs2_inode *ip, int leaf_pointers,
 			      int leaf_pointer_errors, void *private);
 static int invalidate_metadata(struct gfs2_inode *ip, uint64_t block,
 			       struct gfs2_buffer_head **bh, int h,
+			       int *is_valid, int *was_duplicate,
 			       void *private);
 static int invalidate_leaf(struct gfs2_inode *ip, uint64_t block,
 			   void *private);
-static int invalidate_data(struct gfs2_inode *ip, uint64_t block,
-			   void *private);
+static int invalidate_data(struct gfs2_inode *ip, uint64_t metablock,
+			   uint64_t block, void *private);
 static int invalidate_eattr_indir(struct gfs2_inode *ip, uint64_t block,
 				  uint64_t parent,
 				  struct gfs2_buffer_head **bh,
@@ -92,9 +79,17 @@ static int invalidate_eattr_leaf(struct gfs2_inode *ip, uint64_t block,
 				 void *private);
 static int handle_ip(struct gfs2_sbd *sdp, struct gfs2_inode *ip);
 
+static int pass1_repair_leaf(struct gfs2_inode *ip, uint64_t *leaf_no,
+			     int lindex, int ref_count, const char *msg,
+			     void *private)
+{
+	repair_leaf(ip, leaf_no, lindex, ref_count, msg, 0);
+	return 0;
+}
+
 struct metawalk_fxns pass1_fxns = {
 	.private = NULL,
-	.check_leaf = check_leaf,
+	.check_leaf = p1check_leaf,
 	.check_metalist = check_metalist,
 	.check_data = check_data,
 	.check_eattr_indir = check_eattr_indir,
@@ -104,13 +99,9 @@ struct metawalk_fxns pass1_fxns = {
 	.check_eattr_extentry = check_extended_leaf_eattr,
 	.finish_eattr_indir = finish_eattr_indir,
 	.big_file_msg = big_file_comfort,
-	.check_num_ptrs = check_num_ptrs,
-};
-
-struct metawalk_fxns undo_fxns = {
-	.private = NULL,
-	.check_metalist = undo_check_metalist,
-	.check_data = undo_check_data,
+	.repair_leaf = pass1_repair_leaf,
+	.undo_check_meta = undo_check_metalist,
+	.undo_check_data = undo_check_data,
 };
 
 struct metawalk_fxns invalidate_fxns = {
@@ -132,10 +123,13 @@ struct metawalk_fxns invalidate_fxns = {
  */
 static int resuscitate_metalist(struct gfs2_inode *ip, uint64_t block,
 				struct gfs2_buffer_head **bh, int h,
+				int *is_valid, int *was_duplicate,
 				void *private)
 {
 	struct block_count *bc = (struct block_count *)private;
 
+	*is_valid = 1;
+	*was_duplicate = 0;
 	*bh = NULL;
 	if (!valid_block(ip->i_sbd, block)){ /* blk outside of FS */
 		fsck_blockmap_set(ip, ip->i_di.di_num.no_addr,
@@ -144,14 +138,15 @@ static int resuscitate_metalist(struct gfs2_inode *ip, uint64_t block,
 			   "range) found in system inode %lld (0x%llx).\n"),
 			 (unsigned long long)ip->i_di.di_num.no_addr,
 			 (unsigned long long)ip->i_di.di_num.no_addr);
-		return 1;
+		*is_valid = 0;
+		return meta_is_good;
 	}
 	if (fsck_system_inode(ip->i_sbd, block))
 		fsck_blockmap_set(ip, block, _("system file"), gfs2_indir_blk);
 	else
-		check_n_fix_bitmap(ip->i_sbd, block, gfs2_indir_blk);
+		check_n_fix_bitmap(ip->i_sbd, block, 0, gfs2_indir_blk);
 	bc->indir_count++;
-	return 0;
+	return meta_is_good;
 }
 
 /*
@@ -163,7 +158,7 @@ static int resuscitate_metalist(struct gfs2_inode *ip, uint64_t block,
 static int resuscitate_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent,
 			      struct gfs2_dirent *prev_de,
 			      struct gfs2_buffer_head *bh, char *filename,
-			      uint32_t *count, void *priv)
+			      uint32_t *count, int lindex, void *priv)
 {
 	struct gfs2_sbd *sdp = ip->i_sbd;
 	struct gfs2_dirent dentry, *de;
@@ -189,9 +184,10 @@ static int resuscitate_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent,
 			 (unsigned long long)ip->i_di.di_num.no_addr);
 		return 0;
 	}
-	if (block == sdp->md.jiinode->i_di.di_num.no_addr ||
-	    block == sdp->md.pinode->i_di.di_num.no_addr ||
-	    block == sdp->master_dir->i_di.di_num.no_addr)
+	if (block == sdp->md.jiinode->i_di.di_num.no_addr)
+		dinode_type = gfs2_inode_dir;
+	else if (!sdp->gfs1 && (block == sdp->md.pinode->i_di.di_num.no_addr ||
+				block == sdp->master_dir->i_di.di_num.no_addr))
 		dinode_type = gfs2_inode_dir;
 	else
 		dinode_type = gfs2_inode_file;
@@ -202,7 +198,7 @@ static int resuscitate_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent,
 	if (fsck_system_inode(sdp, block))
 		fsck_blockmap_set(ip, block, _("system file"), dinode_type);
 	else
-		check_n_fix_bitmap(sdp, block, dinode_type);
+		check_n_fix_bitmap(sdp, block, 0, dinode_type);
 	/* Return the number of leaf entries so metawalk doesn't flag this
 	   leaf as having none. */
 	*count = be16_to_cpu(((struct gfs2_leaf *)bh->b_data)->lf_entries);
@@ -215,138 +211,7 @@ struct metawalk_fxns sysdir_fxns = {
 	.check_dentry = resuscitate_dentry,
 };
 
-/*
- * fix_leaf_pointers - fix a directory dinode that has a number of pointers
- *                     that is not a multiple of 2.
- * dip - the directory inode having the problem
- * lindex - the index of the leaf right after the problem (need to back up)
- * cur_numleafs - current (incorrect) number of instances of the leaf block
- * correct_numleafs - the correct number instances of the leaf block
- */
-static int fix_leaf_pointers(struct gfs2_inode *dip, int *lindex,
-			     int cur_numleafs, int correct_numleafs)
-{
-	int count;
-	char *ptrbuf;
-	int start_lindex = *lindex - cur_numleafs; /* start of bad ptrs */
-	int tot_num_ptrs = (1 << dip->i_di.di_depth) - start_lindex;
-	int bufsize = tot_num_ptrs * sizeof(uint64_t);
-	int off_by = cur_numleafs - correct_numleafs;
-
-	ptrbuf = malloc(bufsize);
-	if (!ptrbuf) {
-		log_err( _("Error: Cannot allocate memory to fix the leaf "
-			   "pointers.\n"));
-		return -1;
-	}
-	/* Read all the pointers, starting with the first bad one */
-	count = gfs2_readi(dip, ptrbuf, start_lindex * sizeof(uint64_t),
-			   bufsize);
-	if (count != bufsize) {
-		log_err( _("Error: bad read while fixing leaf pointers.\n"));
-		free(ptrbuf);
-		return -1;
-	}
-
-	bufsize -= off_by * sizeof(uint64_t); /* We need to write fewer */
-	/* Write the same pointers, but offset them so they fit within the
-	   smaller factor of 2. So if we have 12 pointers, write out only
-	   the last 8 of them.  If we have 7, write the last 4, etc.
-	   We need to write these starting at the current lindex and adjust
-	   lindex accordingly. */
-	count = gfs2_writei(dip, ptrbuf + (off_by * sizeof(uint64_t)),
-			    start_lindex * sizeof(uint64_t), bufsize);
-	if (count != bufsize) {
-		log_err( _("Error: bad read while fixing leaf pointers.\n"));
-		free(ptrbuf);
-		return -1;
-	}
-	/* Now zero out the hole left at the end */
-	memset(ptrbuf, 0, off_by * sizeof(uint64_t));
-	gfs2_writei(dip, ptrbuf, (start_lindex * sizeof(uint64_t)) +
-		    bufsize, off_by * sizeof(uint64_t));
-	free(ptrbuf);
-	*lindex -= off_by; /* adjust leaf index to account for the change */
-	return 0;
-}
-
-/**
- * check_num_ptrs - check a previously processed leaf's pointer count in the
- *                  hash table.
- *
- * The number of pointers in a directory hash table that point to any given
- * leaf block should always be a factor of two.  The difference between the
- * leaf block's depth and the dinode's di_depth gives us the factor.
- * This function makes sure the leaf follows the rules properly.
- *
- * ip - pointer to the in-core inode structure
- * leafno - the leaf number we're operating on
- * ref_count - the number of pointers to this leaf we actually counted.
- * exp_count - the number of pointers to this leaf we expect based on
- *             ip depth minus leaf depth.
- * lindex - leaf index number
- * leaf - the leaf structure for the leaf block to check
- */
-static int check_num_ptrs(struct gfs2_inode *ip, uint64_t leafno,
-			  int *ref_count, int *lindex, struct gfs2_leaf *leaf)
-{
-	int factor = 0, divisor = *ref_count, multiple = 1, error = 0;
-	struct gfs2_buffer_head *lbh;
-	int exp_count;
-
-	/* Check to see if the number of pointers we found is a power of 2.
-	   It needs to be and if it's not we need to fix it.*/
-	while (divisor > 1) {
-		factor++;
-		divisor /= 2;
-		multiple = multiple << 1;
-	}
-	if (*ref_count != multiple) {
-		log_err( _("Directory #%llu (0x%llx) has an invalid number of "
-			   "pointers to leaf #%llu (0x%llx)\n\tFound: %u, "
-			   "which is not a factor of 2.\n"),
-			 (unsigned long long)ip->i_di.di_num.no_addr,
-			 (unsigned long long)ip->i_di.di_num.no_addr,
-			 (unsigned long long)leafno,
-			 (unsigned long long)leafno, *ref_count);
-		if (!query( _("Attempt to fix it? (y/n) "))) {
-			log_err( _("Directory inode was not fixed.\n"));
-			return 1;
-		}
-		error = fix_leaf_pointers(ip, lindex, *ref_count, multiple);
-		if (error)
-			return error;
-		*ref_count = multiple;
-		log_err( _("Directory inode was fixed.\n"));
-	}
-	/* Check to see if the counted number of leaf pointers is what we
-	   expect based on the leaf depth. */
-	exp_count = (1 << (ip->i_di.di_depth - leaf->lf_depth));
-	if (*ref_count != exp_count) {
-		log_err( _("Directory #%llu (0x%llx) has an incorrect number "
-			   "of pointers to leaf #%llu (0x%llx)\n\tFound: "
-			   "%u,  Expected: %u\n"),
-			 (unsigned long long)ip->i_di.di_num.no_addr,
-			 (unsigned long long)ip->i_di.di_num.no_addr,
-			 (unsigned long long)leafno,
-			 (unsigned long long)leafno, *ref_count, exp_count);
-		if (!query( _("Attempt to fix it? (y/n) "))) {
-			log_err( _("Directory leaf was not fixed.\n"));
-			return 1;
-		}
-		lbh = bread(ip->i_sbd, leafno);
-		gfs2_leaf_in(leaf, lbh);
-		log_err( _("Leaf depth was %d, changed to %d\n"),
-			 leaf->lf_depth, ip->i_di.di_depth - factor);
-		leaf->lf_depth = ip->i_di.di_depth - factor;
-		gfs2_leaf_out(leaf, lbh);
-		brelse(lbh);
-		log_err( _("Directory leaf was fixed.\n"));
-	}
-	return 0;
-}
-
-static int check_leaf(struct gfs2_inode *ip, uint64_t block, void *private)
+static int p1check_leaf(struct gfs2_inode *ip, uint64_t block, void *private)
 {
 	struct block_count *bc = (struct block_count *) private;
 	uint8_t q;
@@ -354,9 +219,10 @@ static int check_leaf(struct gfs2_inode *ip, uint64_t block, void *private)
 	/* Note if we've gotten this far, the block has already passed the
 	   check in metawalk: gfs2_check_meta(lbh, GFS2_METATYPE_LF).
 	   So we know it's a leaf block. */
+	bc->indir_count++;
 	q = block_type(block);
 	if (q != gfs2_block_free) {
-		log_err( _("Found duplicate block %llu (0x%llx) referenced "
+		log_err( _("Found duplicate block #%llu (0x%llx) referenced "
 			   "as a directory leaf in dinode "
 			   "%llu (0x%llx) - was marked %d (%s)\n"),
 			 (unsigned long long)block,
@@ -371,21 +237,23 @@ static int check_leaf(struct gfs2_inode *ip, uint64_t block, void *private)
 			return -EEXIST;
 	}
 	fsck_blockmap_set(ip, block, _("directory leaf"), gfs2_leaf_blk);
-	bc->indir_count++;
 	return 0;
 }
 
 static int check_metalist(struct gfs2_inode *ip, uint64_t block,
-			  struct gfs2_buffer_head **bh, int h, void *private)
+			  struct gfs2_buffer_head **bh, int h, int *is_valid,
+			  int *was_duplicate, void *private)
 {
 	uint8_t q;
-	int found_dup = 0, iblk_type;
+	int iblk_type;
 	struct gfs2_buffer_head *nbh;
 	struct block_count *bc = (struct block_count *)private;
 	const char *blktypedesc;
 
 	*bh = NULL;
 
+	*was_duplicate = 0;
+	*is_valid = 0;
 	if (!valid_block(ip->i_sbd, block)) { /* blk outside of FS */
 		/* The bad dinode should be invalidated later due to
 		   "unrecoverable" errors.  The inode itself should be
@@ -398,9 +266,9 @@ static int check_metalist(struct gfs2_inode *ip, uint64_t block,
 			   (unsigned long long)ip->i_di.di_num.no_addr,
 			   (unsigned long long)ip->i_di.di_num.no_addr);
 
-		return 1;
+		return meta_skip_further;
 	}
-	if (S_ISDIR(ip->i_di.di_mode) && h == ip->i_di.di_height) {
+	if (is_dir(&ip->i_di, ip->i_sbd->gfs1) && h == ip->i_di.di_height) {
 		iblk_type = GFS2_METATYPE_JD;
 		blktypedesc = _("a directory hash table block");
 	} else {
@@ -409,7 +277,7 @@ static int check_metalist(struct gfs2_inode *ip, uint64_t block,
 	}
 	q = block_type(block);
 	if (q != gfs2_block_free) {
-		log_err( _("Found duplicate block %llu (0x%llx) referenced "
+		log_err( _("Found duplicate block #%llu (0x%llx) referenced "
 			   "as metadata in indirect block for dinode "
 			   "%llu (0x%llx) - was marked %d (%s)\n"),
 			 (unsigned long long)block,
@@ -417,12 +285,13 @@ static int check_metalist(struct gfs2_inode *ip, uint64_t block,
 			 (unsigned long long)ip->i_di.di_num.no_addr,
 			 (unsigned long long)ip->i_di.di_num.no_addr, q,
 			 block_type_string(q));
-		add_duplicate_ref(ip, block, ref_as_meta, 0, INODE_VALID);
-		found_dup = 1;
+		*was_duplicate = 1;
 	}
 	nbh = bread(ip->i_sbd, block);
 
-	if (gfs2_check_meta(nbh, iblk_type)){
+	*is_valid = (gfs2_check_meta(nbh, iblk_type) == 0);
+
+	if (!(*is_valid)) {
 		log_err( _("Inode %lld (0x%llx) has a bad indirect block "
 			   "pointer %lld (0x%llx) (points to something "
 			   "that is not %s).\n"),
@@ -430,195 +299,230 @@ static int check_metalist(struct gfs2_inode *ip, uint64_t block,
 			 (unsigned long long)ip->i_di.di_num.no_addr,
 			 (unsigned long long)block,
 			 (unsigned long long)block, blktypedesc);
-		if (!found_dup) {
-			fsck_blockmap_set(ip, block, _("bad indirect"),
-					  gfs2_meta_inval);
-			brelse(nbh);
-			nbh = NULL;
-			return 1;
-		}
 		brelse(nbh);
-		nbh = NULL;
-	} else /* blk check ok */
-		*bh = nbh;
+		return meta_skip_further;
+	}
 
 	bc->indir_count++;
-	if (found_dup) {
-		if (nbh) {
-			brelse(nbh);
-			nbh = NULL;
-			*bh = NULL;
-		}
-		return 1; /* don't process the metadata again */
-	} else
-		fsck_blockmap_set(ip, block, _("indirect"),
-				  gfs2_indir_blk);
+	if (*was_duplicate) {
+		add_duplicate_ref(ip, block, ref_as_meta, 0,
+				  *is_valid ? INODE_VALID : INODE_INVALID);
+		brelse(nbh);
+	} else {
+		*bh = nbh;
+		fsck_blockmap_set(ip, block, _("indirect"), gfs2_indir_blk);
+	}
 
-	return 0;
+	if (*is_valid)
+		return meta_is_good;
+	return meta_skip_further;
 }
 
-static int undo_check_metalist(struct gfs2_inode *ip, uint64_t block,
-			       struct gfs2_buffer_head **bh, int h,
-			       void *private)
+/* undo_reference - undo previously processed data or metadata
+ * We've treated the metadata for this dinode as good so far, but not we
+ * realize it's bad. So we need to undo what we've done.
+ *
+ * Returns: 0 - We need to process the block as metadata. In other words,
+ *              we need to undo any blocks it refers to.
+ *          1 - We can't process the block as metadata.
+ */
+
+static int undo_reference(struct gfs2_inode *ip, uint64_t block, int meta,
+			  void *private)
 {
-	int found_dup = 0, iblk_type;
-	struct gfs2_buffer_head *nbh;
 	struct block_count *bc = (struct block_count *)private;
-
-	*bh = NULL;
+	struct duptree *dt;
+	struct inode_with_dups *id;
+	int old_bitmap_state = 0;
+	struct rgrp_tree *rgd;
 
 	if (!valid_block(ip->i_sbd, block)) { /* blk outside of FS */
 		fsck_blockmap_set(ip, ip->i_di.di_num.no_addr,
 				  _("bad block referencing"), gfs2_block_free);
 		return 1;
 	}
-	if (S_ISDIR(ip->i_di.di_mode) && h == ip->i_di.di_height)
-		iblk_type = GFS2_METATYPE_JD;
-	else
-		iblk_type = GFS2_METATYPE_IN;
 
-	found_dup = find_remove_dup(ip, block, _("Metadata"));
-	nbh = bread(ip->i_sbd, block);
+	if (meta)
+		bc->indir_count--;
+	dt = dupfind(block);
+	if (dt) {
+		/* remove all duplicate reference structures from this inode */
+		do {
+			id = find_dup_ref_inode(dt, ip);
+			if (!id)
+				break;
 
-	if (gfs2_check_meta(nbh, iblk_type)) {
-		if (!found_dup) {
-			fsck_blockmap_set(ip, block, _("bad indirect"),
-					  gfs2_block_free);
-			brelse(nbh);
+			dup_listent_delete(dt, id);
+		} while (id);
+
+		if (dt->refs) {
+			log_err(_("Block %llu (0x%llx) is still referenced "
+				  "from another inode; not freeing.\n"),
+				(unsigned long long)block,
+				(unsigned long long)block);
 			return 1;
 		}
-		brelse(nbh);
-		nbh = NULL;
-	} else /* blk check ok */
-		*bh = nbh;
-
-	bc->indir_count--;
-	if (found_dup) {
-		if (nbh)
-			brelse(nbh);
-		*bh = NULL;
-		return 1; /* don't process the metadata again */
-	} else
-		fsck_blockmap_set(ip, block, _("bad indirect"),
-				  gfs2_block_free);
+	}
+	if (!meta) {
+		rgd = gfs2_blk2rgrpd(ip->i_sbd, block);
+		old_bitmap_state = gfs2_get_bitmap(ip->i_sbd, block, rgd);
+		if (old_bitmap_state == GFS2_BLKST_DINODE)
+			return -1;
+	}
+	fsck_blockmap_set(ip, block,
+			  meta ? _("bad indirect") : _("referenced data"),
+			  gfs2_block_free);
 	return 0;
 }
 
-static int check_data(struct gfs2_inode *ip, uint64_t block, void *private)
+static int undo_check_metalist(struct gfs2_inode *ip, uint64_t block,
+			       int h, void *private)
+{
+	return undo_reference(ip, block, 1, private);
+}
+
+static int undo_check_data(struct gfs2_inode *ip, uint64_t block,
+			   void *private)
+{
+	return undo_reference(ip, block, 0, private);
+}
+
+/* blockmap_set_as_data - set block as 'data' in the blockmap, if not dinode
+ *
+ * This function tries to set a block that's referenced as data as 'data'
+ * in the fsck blockmap. But if that block is marked as 'dinode' in the
+ * rgrp bitmap, it does additional checks to see if it looks like a dinode.
+ * Note that previous checks were done for duplicate references, so this
+ * is checking for dinodes that we haven't processed yet.
+ */
+static int blockmap_set_as_data(struct gfs2_inode *ip, uint64_t block)
+{
+	int error;
+	struct gfs2_buffer_head *bh;
+	struct gfs2_dinode *di;
+
+	error = fsck_blkmap_set_noino(ip, block, _("data"),  gfs2_block_used);
+	if (!error)
+		return 0;
+
+	error = 0;
+	/* The bitmap says it's a dinode, but a block reference begs to differ.
+	   So which is it? */
+	bh = bread(ip->i_sbd, block);
+	if (gfs2_check_meta(bh, GFS2_METATYPE_DI) != 0)
+		goto out;
+
+	/* The meta header agrees it's a dinode. But it might be data in
+	   disguise, so do some extra checks. */
+	di = (struct gfs2_dinode *)bh->b_data;
+	if (be64_to_cpu(di->di_num.no_addr) != block)
+		goto out;
+
+	log_err(_("Inode %lld (0x%llx) has a reference to block %lld (0x%llx) "
+		  "as a data block, but it appears to be a dinode we "
+		  "haven't checked yet.\n"),
+		(unsigned long long)ip->i_di.di_num.no_addr,
+		(unsigned long long)ip->i_di.di_num.no_addr,
+		(unsigned long long)block, (unsigned long long)block);
+	error = -1;
+out:
+	if (!error)
+		fsck_blockmap_set(ip, block, _("data"),  gfs2_block_used);
+	brelse(bh);
+	return error;
+}
+
+static int check_data(struct gfs2_inode *ip, uint64_t metablock,
+		      uint64_t block, void *private)
 {
 	uint8_t q;
 	struct block_count *bc = (struct block_count *) private;
 
 	if (!valid_block(ip->i_sbd, block)) {
 		log_err( _("inode %lld (0x%llx) has a bad data block pointer "
-			   "%lld (invalid or out of range)\n"),
+			   "%lld (0x%llx) (invalid or out of range) "),
 			 (unsigned long long)ip->i_di.di_num.no_addr,
 			 (unsigned long long)ip->i_di.di_num.no_addr,
-			 (unsigned long long)block);
+			 (unsigned long long)block, (unsigned long long)block);
+		if (metablock == ip->i_di.di_num.no_addr)
+			log_err("\n");
+		else
+			log_err(_("from metadata block %llu (0x%llx)\n"),
+				(unsigned long long)metablock,
+				(unsigned long long)metablock);
 		/* Mark the owner of this block with the bad_block
 		 * designator so we know to check it for out of range
 		 * blocks later */
 		fsck_blockmap_set(ip, ip->i_di.di_num.no_addr,
 				  _("bad (out of range) data"),
 				  gfs2_bad_block);
-		return 1;
+		return -1;
 	}
+	bc->data_count++; /* keep the count sane anyway */
 	q = block_type(block);
 	if (q != gfs2_block_free) {
 		log_err( _("Found duplicate %s block %llu (0x%llx) "
-			   "referenced as data by dinode %llu (0x%llx)\n"),
+			   "referenced as data by dinode %llu (0x%llx) "),
 			 block_type_string(q),
 			 (unsigned long long)block,
 			 (unsigned long long)block,
 			 (unsigned long long)ip->i_di.di_num.no_addr,
 			 (unsigned long long)ip->i_di.di_num.no_addr);
+		if (metablock == ip->i_di.di_num.no_addr)
+			log_err("\n");
+		else
+			log_err(_("from metadata block %llu (0x%llx)\n"),
+				(unsigned long long)metablock,
+				(unsigned long long)metablock);
+
+		if (q >= gfs2_indir_blk && q <= gfs2_jdata) {
+			log_info(_("The block was processed earlier as valid "
+				   "metadata, so it can't possibly be "
+				   "data.\n"));
+			/* We still need to add a duplicate record here because
+			   when check_metatree tries to delete the inode, we
+			   can't have the "undo" functions freeing the block
+			   out from other the original referencing inode. */
+			add_duplicate_ref(ip, block, ref_as_data, 0,
+					  INODE_VALID);
+			return 1;
+		}
 		if (q != gfs2_meta_inval) {
 			log_info( _("Seems to be a normal duplicate; I'll "
 				    "sort it out in pass1b.\n"));
 			add_duplicate_ref(ip, block, ref_as_data, 0,
 					  INODE_VALID);
-			/* If the prev ref was as data, this is likely a data
-			   block, so keep the block count for both refs. */
-			if (q == gfs2_block_used)
-				bc->data_count++;
-			return 1;
+			/* This inode references the block as data. So if this
+			   all is validated, we want to keep this count. */
+			return 0;
 		}
 		log_info( _("The block was invalid as metadata but might be "
 			    "okay as data.  I'll sort it out in pass1b.\n"));
 		add_duplicate_ref(ip, block, ref_as_data, 0, INODE_VALID);
-		bc->data_count++;
-		return 1;
+		return 0;
 	}
-	fsck_blockmap_set(ip, block, _("data"), gfs2_block_used);
-	bc->data_count++;
+	/* In gfs1, rgrp indirect blocks are marked in the bitmap as "meta".
+	   In gfs2, "meta" is only for dinodes. So here we dummy up the
+	   blocks so that the bitmap isn't changed improperly. */
+	if (ip->i_sbd->gfs1 && ip == ip->i_sbd->md.riinode) {
+		log_info(_("Block %lld (0x%llx) is a GFS1 rindex block\n"),
+			 (unsigned long long)block, (unsigned long long)block);
+		gfs2_special_set(&gfs1_rindex_blks, block);
+		fsck_blockmap_set(ip, block, _("rgrp"), gfs2_indir_blk);
+		/*gfs2_meta_rgrp);*/
+	} else if (ip->i_sbd->gfs1 && ip->i_di.di_flags & GFS2_DIF_JDATA) {
+		log_info(_("Block %lld (0x%llx) is a GFS1 journaled data "
+			   "block\n"),
+			 (unsigned long long)block, (unsigned long long)block);
+		fsck_blockmap_set(ip, block, _("jdata"), gfs2_jdata);
+	} else
+		return blockmap_set_as_data(ip, block);
 	return 0;
 }
 
-static int undo_check_data(struct gfs2_inode *ip, uint64_t block,
-			   void *private)
-{
-	struct block_count *bc = (struct block_count *) private;
-
-	if (!valid_block(ip->i_sbd, block)) {
-		/* Mark the owner of this block with the bad_block
-		 * designator so we know to check it for out of range
-		 * blocks later */
-		fsck_blockmap_set(ip, ip->i_di.di_num.no_addr,
-				  _("bad (invalid or out of range) data"),
-				  gfs2_block_free);
-		return 1;
-	}
-	bc->data_count--;
-	return free_block_if_notdup(ip, block, _("data"));
-}
-
 static int remove_inode_eattr(struct gfs2_inode *ip, struct block_count *bc)
 {
-	struct duptree *dt;
-	struct inode_with_dups *id;
-	osi_list_t *ref;
-	int moved = 0;
-
-	/* If it's a duplicate reference to the block, we need to check
-	   if the reference is on the valid or invalid inodes list.
-	   If it's on the valid inode's list, move it to the invalid
-	   inodes list.  The reason is simple: This inode, although
-	   valid, has an now-invalid reference, so we should not give
-	   this reference preferential treatment over others. */
-	dt = dupfind(ip->i_di.di_eattr);
-	if (dt) {
-		osi_list_foreach(ref, &dt->ref_inode_list) {
-			id = osi_list_entry(ref, struct inode_with_dups, list);
-			if (id->block_no == ip->i_di.di_num.no_addr) {
-				log_debug( _("Moving inode %lld (0x%llx)'s "
-					     "duplicate reference to %lld "
-					     "(0x%llx) from the valid to the "
-					     "invalid reference list.\n"),
-					   (unsigned long long)
-					   ip->i_di.di_num.no_addr,
-					   (unsigned long long)
-					   ip->i_di.di_num.no_addr,
-					   (unsigned long long)
-					   ip->i_di.di_eattr,
-					   (unsigned long long)
-					   ip->i_di.di_eattr);
-				/* Move from the normal to the invalid list */
-				osi_list_del(&id->list);
-				osi_list_add_prev(&id->list,
-						  &dt->ref_invinode_list);
-				moved = 1;
-				break;
-			}
-		}
-		if (!moved)
-			log_debug( _("Duplicate reference to %lld "
-				     "(0x%llx) not moved.\n"),
-				   (unsigned long long)ip->i_di.di_eattr,
-				   (unsigned long long)ip->i_di.di_eattr);
-	} else {
-		delete_block(ip, ip->i_di.di_eattr, NULL,
-			     "extended attribute", NULL);
-	}
+	undo_reference(ip, ip->i_di.di_eattr, 0, bc);
 	ip->i_di.di_eattr = 0;
 	bc->ea_count = 0;
 	ip->i_di.di_blocks = 1 + bc->indir_count + bc->data_count;
@@ -630,6 +534,8 @@ static int remove_inode_eattr(struct gfs2_inode *ip, struct block_count *bc)
 static int ask_remove_inode_eattr(struct gfs2_inode *ip,
 				  struct block_count *bc)
 {
+	if (ip->i_di.di_eattr == 0)
+		return 0; /* eattr was removed prior to this call */
 	log_err( _("Inode %lld (0x%llx) has unrecoverable Extended Attribute "
 		   "errors.\n"), (unsigned long long)ip->i_di.di_num.no_addr,
 		 (unsigned long long)ip->i_di.di_num.no_addr);
@@ -772,7 +678,11 @@ static int finish_eattr_indir(struct gfs2_inode *ip, int leaf_pointers,
 		ip->i_di.di_blocks = 1 + bc->indir_count +
 			bc->data_count + bc->ea_count;
 		bmodified(ip->i_bh);
-		log_err( _("Block count fixed.\n"));
+		log_err(_("Block count fixed: 1+%lld+%lld+%lld = %lld.\n"),
+			(unsigned long long)bc->indir_count,
+			(unsigned long long)bc->data_count,
+			(unsigned long long)bc->ea_count,
+			(unsigned long long)ip->i_di.di_blocks);
 		return 1;
 	}
 	log_err( _("Block count not fixed.\n"));
@@ -968,7 +878,8 @@ static int check_eattr_entries(struct gfs2_inode *ip,
  * delete_block_if_notdup.
  */
 static int mark_block_invalid(struct gfs2_inode *ip, uint64_t block,
-			      enum dup_ref_type reftype, const char *btype)
+			      enum dup_ref_type reftype, const char *btype,
+			      int *is_valid, int *was_duplicate)
 {
 	uint8_t q;
 
@@ -979,11 +890,20 @@ static int mark_block_invalid(struct gfs2_inode *ip, uint64_t block,
 	 * referenced elsewhere (duplicates) won't be flagged as such,
 	 * and as a result, they'll be freed when this dinode is deleted,
 	 * despite being used by another dinode as a valid block. */
-	if (!valid_block(ip->i_sbd, block))
-		return 0;
+	if (is_valid)
+		*is_valid = 1;
+	if (was_duplicate)
+		*was_duplicate = 0;
+	if (!valid_block(ip->i_sbd, block)) {
+		if (is_valid)
+			*is_valid = 0;
+		return meta_is_good;
+	}
 
 	q = block_type(block);
 	if (q != gfs2_block_free) {
+		if (was_duplicate)
+			*was_duplicate = 1;
 		add_duplicate_ref(ip, block, reftype, 0, INODE_INVALID);
 		log_info( _("%s block %lld (0x%llx), part of inode "
 			    "%lld (0x%llx), was previously referenced so "
@@ -992,29 +912,35 @@ static int mark_block_invalid(struct gfs2_inode *ip, uint64_t block,
 			  (unsigned long long)block,
 			  (unsigned long long)ip->i_di.di_num.no_addr,
 			  (unsigned long long)ip->i_di.di_num.no_addr);
-		return 0;
+		return meta_is_good;
 	}
 	fsck_blockmap_set(ip, block, btype, gfs2_meta_inval);
-	return 0;
+	return meta_is_good;
 }
 
 static int invalidate_metadata(struct gfs2_inode *ip, uint64_t block,
 			       struct gfs2_buffer_head **bh, int h,
+			       int *is_valid, int *was_duplicate,
 			       void *private)
 {
-	return mark_block_invalid(ip, block, ref_as_meta, _("metadata"));
+	*is_valid = 1;
+	*was_duplicate = 0;
+	return mark_block_invalid(ip, block, ref_as_meta, _("metadata"),
+				  is_valid, was_duplicate);
 }
 
 static int invalidate_leaf(struct gfs2_inode *ip, uint64_t block,
 			   void *private)
 {
-	return mark_block_invalid(ip, block, ref_as_meta, _("leaf"));
+	return mark_block_invalid(ip, block, ref_as_meta, _("leaf"),
+				  NULL, NULL);
 }
 
-static int invalidate_data(struct gfs2_inode *ip, uint64_t block,
-			   void *private)
+static int invalidate_data(struct gfs2_inode *ip, uint64_t metablock,
+			   uint64_t block, void *private)
 {
-	return mark_block_invalid(ip, block, ref_as_data, _("data"));
+	return mark_block_invalid(ip, block, ref_as_data, _("data"),
+				  NULL, NULL);
 }
 
 static int invalidate_eattr_indir(struct gfs2_inode *ip, uint64_t block,
@@ -1022,7 +948,8 @@ static int invalidate_eattr_indir(struct gfs2_inode *ip, uint64_t block,
 				  struct gfs2_buffer_head **bh, void *private)
 {
 	return mark_block_invalid(ip, block, ref_as_ea,
-				  _("indirect extended attribute"));
+				  _("indirect extended attribute"),
+				  NULL, NULL);
 }
 
 static int invalidate_eattr_leaf(struct gfs2_inode *ip, uint64_t block,
@@ -1030,7 +957,8 @@ static int invalidate_eattr_leaf(struct gfs2_inode *ip, uint64_t block,
 				 void *private)
 {
 	return mark_block_invalid(ip, block, ref_as_ea,
-				  _("extended attribute"));
+				  _("extended attribute"),
+				  NULL, NULL);
 }
 
 /**
@@ -1043,6 +971,7 @@ static int invalidate_eattr_leaf(struct gfs2_inode *ip, uint64_t block,
  * messing with them because we don't want to mark a block as a
  * duplicate (for example) until we know if the pointers in general can
  * be trusted. Thus it needs to be in a separate loop.
+ * Returns: 0 if good range, otherwise != 0
  */
 enum b_types { btype_meta, btype_leaf, btype_data, btype_ieattr, btype_eattr};
 const char *btypes[5] = {
@@ -1064,9 +993,9 @@ static int rangecheck_block(struct gfs2_inode *ip, uint64_t block,
 			  (unsigned long long)ip->i_di.di_num.no_addr,
 			  (unsigned long long)ip->i_di.di_num.no_addr);
 		if ((*bad_pointers) <= BAD_POINTER_TOLERANCE)
-			return ENOENT;
+			return meta_is_good;
 		else
-			return -ENOENT; /* Exits check_metatree quicker */
+			return meta_error; /* Exits check_metatree quicker */
 	}
 	/* See how many duplicate blocks it has */
 	q = block_type(block);
@@ -1079,17 +1008,20 @@ static int rangecheck_block(struct gfs2_inode *ip, uint64_t block,
 			  (unsigned long long)ip->i_di.di_num.no_addr,
 			  (unsigned long long)ip->i_di.di_num.no_addr);
 		if ((*bad_pointers) <= BAD_POINTER_TOLERANCE)
-			return ENOENT;
+			return meta_is_good;
 		else
-			return -ENOENT; /* Exits check_metatree quicker */
+			return meta_error; /* Exits check_metatree quicker */
 	}
-	return 0;
+	return meta_is_good;
 }
 
 static int rangecheck_metadata(struct gfs2_inode *ip, uint64_t block,
 			       struct gfs2_buffer_head **bh, int h,
+			       int *is_valid, int *was_duplicate,
 			       void *private)
 {
+	*is_valid = 1;
+	*was_duplicate = 0;
 	return rangecheck_block(ip, block, bh, btype_meta, private);
 }
 
@@ -1099,8 +1031,8 @@ static int rangecheck_leaf(struct gfs2_inode *ip, uint64_t block,
 	return rangecheck_block(ip, block, NULL, btype_leaf, private);
 }
 
-static int rangecheck_data(struct gfs2_inode *ip, uint64_t block,
-			   void *private)
+static int rangecheck_data(struct gfs2_inode *ip, uint64_t metablock,
+			   uint64_t block, void *private)
 {
 	return rangecheck_block(ip, block, NULL, btype_data, private);
 }
@@ -1136,7 +1068,7 @@ static int handle_ip(struct gfs2_sbd *sdp, struct gfs2_inode *ip)
 	int error;
 	struct block_count bc = {0};
 	long bad_pointers;
-	uint64_t block = ip->i_bh->b_blocknr;
+	uint64_t lf_blks = 0;
 
 	bad_pointers = 0L;
 
@@ -1159,23 +1091,14 @@ static int handle_ip(struct gfs2_sbd *sdp, struct gfs2_inode *ip)
 
 	error = set_ip_blockmap(ip, 1);
 	if (error == -EINVAL) {
-		/* We found a dinode that has an invalid mode, so we can't
-		   tell if it's a data file, directory or a socket.
-		   Regardless, we have to invalidate its metadata in case there
-		   are duplicate blocks referenced.  If we don't call
-		   check_metatree, the blocks it references will be deleted
-		   wholesale by pass2, and if any of those blocks are
-		   duplicates--referenced by another dinode for some reason--
-		   we will mark it free, even though it's in use.  In other
-		   words, we would introduce file system corruption. So we
-		   need to keep track of the fact that it's invalid and
-		   skip parts that we can't be sure of based on dinode type. */
-		log_debug("Invalid mode dinode found at block %lld (0x%llx): "
-			  "Invalidating all its metadata.\n",
-			  (unsigned long long)ip->i_di.di_num.no_addr,
-			  (unsigned long long)ip->i_di.di_num.no_addr);
-		check_metatree(ip, &invalidate_fxns);
-		check_inode_eattr(ip, &invalidate_fxns);
+		/* We found a dinode that has an invalid mode. At this point
+		   set_ip_blockmap returned an error, which means it never
+		   got inserted into the inode tree. Since we haven't even
+		   processed its metadata with pass1_fxns, none of its
+		   metadata will be flagged as metadata or data blocks yet.
+		   Therefore, we don't need to invalidate anything. */
+		fsck_blockmap_set(ip, ip->i_di.di_num.no_addr,
+				  _("invalid mode"), gfs2_block_free);
 		return 0;
 	} else if (error)
 		goto bad_dinode;
@@ -1183,47 +1106,30 @@ static int handle_ip(struct gfs2_sbd *sdp, struct gfs2_inode *ip)
 	if (set_di_nlink(ip))
 		goto bad_dinode;
 
-	if (S_ISDIR(ip->i_di.di_mode) &&
-	    (ip->i_di.di_flags & GFS2_DIF_EXHASH)) {
-		if (((1 << ip->i_di.di_depth) * sizeof(uint64_t)) != ip->i_di.di_size){
-			log_warn( _("Directory dinode block #%llu (0x%llx"
-				 ") has bad depth.  Found %u, Expected %u\n"),
-				 (unsigned long long)ip->i_di.di_num.no_addr,
-				 (unsigned long long)ip->i_di.di_num.no_addr,
-				 ip->i_di.di_depth,
-				 (1 >> (ip->i_di.di_size/sizeof(uint64_t))));
-			if (fsck_blockmap_set(ip, block, _("bad depth"),
-					     gfs2_block_free))
-				goto bad_dinode;
-			return 0;
-		}
-	}
+	if (lf_dip)
+		lf_blks = lf_dip->i_di.di_blocks;
 
 	pass1_fxns.private = &bc;
 	error = check_metatree(ip, &pass1_fxns);
-	if (fsck_abort || error < 0)
-		return 0;
-	if (error > 0) {
-		log_err( _("Error: inode %llu (0x%llx) has unrecoverable "
-			   "errors; invalidating.\n"),
-			 (unsigned long long)ip->i_di.di_num.no_addr,
-			 (unsigned long long)ip->i_di.di_num.no_addr);
-		undo_fxns.private = &bc;
-		check_metatree(ip, &undo_fxns);
-		/* If we undo the metadata accounting, including metadatas
-		   duplicate block status, we need to make sure later passes
-		   don't try to free up the metadata referenced by this inode.
-		   Therefore we mark the inode as free space. */
-		fsck_blockmap_set(ip, ip->i_di.di_num.no_addr,
-				  _("corrupt"), gfs2_block_free);
+
+	/* Pass1 may have added some blocks to lost+found by virtue of leafs
+	   that were misplaced. If it did, we need to reprocess lost+found
+	   to correctly account for its blocks. */
+	if (lf_dip && lf_dip->i_di.di_blocks != lf_blks)
+		reprocess_inode(lf_dip, "lost+found");
+
+	/* We there was an error, we return 0 because we want fsck to continue
+	   and analyze the other dinodes as well. */
+	if (fsck_abort)
 		return 0;
-	}
 
-	error = check_inode_eattr(ip, &pass1_fxns);
+	if (!error) {
+		error = check_inode_eattr(ip, &pass1_fxns);
 
-	if (error &&
-	    !(ip->i_di.di_flags & GFS2_DIF_EA_INDIRECT))
-		ask_remove_inode_eattr(ip, &bc);
+		if (error &&
+		    !(ip->i_di.di_flags & GFS2_DIF_EA_INDIRECT))
+			ask_remove_inode_eattr(ip, &bc);
+	}
 
 	if (ip->i_di.di_blocks != 
 		(1 + bc.indir_count + bc.data_count + bc.ea_count)) {
@@ -1266,22 +1172,11 @@ bad_dinode:
  */
 static int handle_di(struct gfs2_sbd *sdp, struct gfs2_buffer_head *bh)
 {
-	uint8_t q;
 	int error = 0;
 	uint64_t block = bh->b_blocknr;
 	struct gfs2_inode *ip;
 
 	ip = fsck_inode_get(sdp, bh);
-	q = block_type(block);
-	if (q != gfs2_block_free) {
-		log_err( _("Found a duplicate inode block at #%llu"
-			   " (0x%llx) previously marked as a %s\n"),
-			 (unsigned long long)block,
-			 (unsigned long long)block, block_type_string(q));
-		add_duplicate_ref(ip, block, ref_as_meta, 0, INODE_VALID);
-		fsck_inode_put(&ip);
-		return 0;
-	}
 
 	if (ip->i_di.di_num.no_addr != block) {
 		log_err( _("Inode #%llu (0x%llx): Bad inode address found: %llu "
@@ -1298,6 +1193,23 @@ static int handle_di(struct gfs2_sbd *sdp, struct gfs2_buffer_head *bh)
 			log_err( _("Address in inode at block #%" PRIu64
 				 " (0x%" PRIx64 ") not fixed\n"), block, block);
 	}
+	if (sdp->gfs1 && ip->i_di.di_num.no_formal_ino != block) {
+		log_err( _("Inode #%llu (0x%llx): GFS1 formal inode number "
+			   "mismatch: was %llu (0x%llx)\n"),
+			 (unsigned long long)block, (unsigned long long)block,
+			 (unsigned long long)ip->i_di.di_num.no_formal_ino,
+			 (unsigned long long)ip->i_di.di_num.no_formal_ino);
+		if (query( _("Fix formal inode number in inode #%llu"
+			    " (0x%llx)? (y/n) "), (unsigned long long)block,
+			   (unsigned long long)block)) {
+			ip->i_di.di_num.no_formal_ino = block;
+			bmodified(ip->i_bh);
+		} else
+			log_err( _("Inode number in inode at block #%lld "
+				   "(0x%llx) not fixed\n"),
+				 (unsigned long long)block,
+				 (unsigned long long)block);
+	}
 	error = handle_ip(sdp, ip);
 	fsck_inode_put(&ip);
 	return error;
@@ -1319,6 +1231,8 @@ static int check_system_inode(struct gfs2_sbd *sdp,
 
 	log_info( _("Checking system inode '%s'\n"), filename);
 	if (*sysinode) {
+		/* Read in the system inode, look at its dentries, and start
+		 * reading through them */
 		iblock = (*sysinode)->i_di.di_num.no_addr;
 		log_info( _("System inode for '%s' is located at block %"
 			 PRIu64 " (0x%" PRIx64 ")\n"), filename,
@@ -1329,7 +1243,7 @@ static int check_system_inode(struct gfs2_sbd *sdp,
 				 (unsigned long long)iblock,
 				 (unsigned long long)iblock);
 			gfs2_blockmap_set(bl, iblock, gfs2_block_free);
-			check_n_fix_bitmap(sdp, iblock, gfs2_block_free);
+			check_n_fix_bitmap(sdp, iblock, 0, gfs2_block_free);
 			inode_put(sysinode);
 		}
 	}
@@ -1381,7 +1295,7 @@ static int check_system_inode(struct gfs2_sbd *sdp,
 			return -1;
 		}
 	}
-	if (S_ISDIR((*sysinode)->i_di.di_mode)) {
+	if (is_dir(&(*sysinode)->i_di, sdp->gfs1)) {
 		struct block_count bc = {0};
 
 		sysdir_fxns.private = &bc;
@@ -1405,12 +1319,17 @@ static int check_system_inode(struct gfs2_sbd *sdp,
 static int build_a_journal(struct gfs2_sbd *sdp)
 {
 	char name[256];
+	int err = 0;
 
 	/* First, try to delete the journal if it's in jindex */
 	sprintf(name, "journal%u", sdp->md.journals);
 	gfs2_dirent_del(sdp->md.jiinode, name, strlen(name));
 	/* Now rebuild it */
-	build_journal(sdp, sdp->md.journals, sdp->md.jiinode);
+	err = build_journal(sdp, sdp->md.journals, sdp->md.jiinode);
+	if (err) {
+		log_crit(_("Error %d building journal\n"), err);
+		exit(FSCK_ERROR);
+	}
 	return 0;
 }
 
@@ -1424,13 +1343,15 @@ static int check_system_inodes(struct gfs2_sbd *sdp)
 	/* Mark the master system dinode as a "dinode" in the block map.
 	   All other system dinodes in master will be taken care of by function
 	   resuscitate_metalist.  But master won't since it has no parent.*/
-	fsck_blockmap_set(sdp->master_dir,
-			  sdp->master_dir->i_di.di_num.no_addr,
-			  "master", gfs2_inode_dir);
-	if (check_system_inode(sdp, &sdp->master_dir, "master", build_master,
-			       gfs2_inode_dir)) {
-		stack;
-		return -1;
+	if (!sdp->gfs1) {
+		fsck_blockmap_set(sdp->master_dir,
+				  sdp->master_dir->i_di.di_num.no_addr,
+				  "master", gfs2_inode_dir);
+		if (check_system_inode(sdp, &sdp->master_dir, "master",
+				       build_master, gfs2_inode_dir)) {
+			stack;
+			return -1;
+		}
 	}
 	/* Mark the root dinode as a "dinode" in the block map as we did
 	   for master, since it has no parent. */
@@ -1441,7 +1362,8 @@ static int check_system_inodes(struct gfs2_sbd *sdp)
 		stack;
 		return -1;
 	}
-	if (check_system_inode(sdp, &sdp->md.inum, "inum", build_inum,
+	if (!sdp->gfs1 &&
+	    check_system_inode(sdp, &sdp->md.inum, "inum", build_inum,
 			       gfs2_inode_file)) {
 		stack;
 		return -1;
@@ -1452,7 +1374,7 @@ static int check_system_inodes(struct gfs2_sbd *sdp)
 		return -1;
 	}
 	if (check_system_inode(sdp, &sdp->md.jiinode, "jindex", build_jindex,
-			       gfs2_inode_dir)) {
+			       (sdp->gfs1 ? gfs2_inode_file : gfs2_inode_dir))) {
 		stack;
 		return -1;
 	}
@@ -1466,7 +1388,8 @@ static int check_system_inodes(struct gfs2_sbd *sdp)
 		stack;
 		return -1;
 	}
-	if (check_system_inode(sdp, &sdp->md.pinode, "per_node",
+	if (!sdp->gfs1 &&
+	    check_system_inode(sdp, &sdp->md.pinode, "per_node",
 			       build_per_node, gfs2_inode_dir)) {
 		stack;
 		return -1;
@@ -1474,6 +1397,21 @@ static int check_system_inodes(struct gfs2_sbd *sdp)
 	/* We have to play a trick on build_journal:  We swap md.journals
 	   in order to keep a count of which journal we need to build. */
 	journal_count = sdp->md.journals;
+	/* gfs1's journals aren't dinode, they're just a bunch of blocks. */
+	if (sdp->gfs1) {
+		/* gfs1 has four dinodes that are set in the superblock and
+		   therefore not linked to anything else. We need to adjust
+		   the link counts so pass4 doesn't get confused. */
+		incr_link_count(sdp->md.statfs->i_di.di_num, NULL,
+				_("gfs1 statfs inode"));
+		incr_link_count(sdp->md.jiinode->i_di.di_num, NULL,
+				_("gfs1 jindex inode"));
+		incr_link_count(sdp->md.riinode->i_di.di_num, NULL,
+				_("gfs1 rindex inode"));
+		incr_link_count(sdp->md.qinode->i_di.di_num, NULL,
+				_("gfs1 quota inode"));
+		return 0;
+	}
 	for (sdp->md.journals = 0; sdp->md.journals < journal_count;
 	     sdp->md.journals++) {
 		char jname[16];
@@ -1507,13 +1445,15 @@ int pass1(struct gfs2_sbd *sdp)
 {
 	struct osi_node *n, *next = NULL;
 	struct gfs2_buffer_head *bh;
-	uint64_t block;
+	struct gfs2_inode *ip;
+	uint64_t block = 0;
 	struct rgrp_tree *rgd;
 	int first;
 	uint64_t i;
-	uint64_t blk_count;
-	uint64_t offset;
 	uint64_t rg_count = 0;
+	uint8_t q;
+
+	osi_list_init(&gfs1_rindex_blks.list);
 
 	/* FIXME: In the gfs fsck, we had to mark things like the
 	 * journals and indices and such as 'other_meta' - in gfs2,
@@ -1545,28 +1485,42 @@ int pass1(struct gfs2_sbd *sdp)
 			if (gfs2_blockmap_set(bl, rgd->ri.ri_addr + i,
 					      gfs2_indir_blk)) {
 				stack;
+				gfs2_special_free(&gfs1_rindex_blks);
 				return FSCK_ERROR;
 			}
 			/* rgrps and bitmaps don't have bits to represent
 			   their blocks, so don't do this:
-			check_n_fix_bitmap(sdp, rgd->ri.ri_addr + i,
+			check_n_fix_bitmap(sdp, rgd->ri.ri_addr + i, 0,
 			gfs2_meta_rgrp);*/
 		}
 
-		offset = sizeof(struct gfs2_rgrp);
-		blk_count = 1;
 		first = 1;
 
 		while (1) {
+			int is_inode;
+			uint32_t check_magic;
+
 			/* "block" is relative to the entire file system */
 			/* Get the next dinode in the file system, according
-			   to the bitmap.  This should ONLY be dinodes. */
+			   to the bitmap.  This should ONLY be dinodes unless
+			   it's GFS1, in which case it can be any metadata. */
 			if (gfs2_next_rg_meta(rgd, &block, first))
 				break;
+			/* skip gfs1 rindex indirect blocks */
+			if (sdp->gfs1 && blockfind(&gfs1_rindex_blks, block)) {
+				log_debug(_("Skipping rindex indir block "
+					    "%lld (0x%llx)\n"),
+					  (unsigned long long)block,
+					  (unsigned long long)block);
+				first = 0;
+				continue;
+			}
 			warm_fuzzy_stuff(block);
 
-			if (fsck_abort) /* if asked to abort */
+			if (fsck_abort) { /* if asked to abort */
+				gfs2_special_free(&gfs1_rindex_blks);
 				return FSCK_OK;
+			}
 			if (skip_this_pass) {
 				printf( _("Skipping pass 1 is not a good idea.\n"));
 				skip_this_pass = FALSE;
@@ -1580,27 +1534,83 @@ int pass1(struct gfs2_sbd *sdp)
 				first = 0;
 				continue;
 			}
+
 			bh = bread(sdp, block);
 
+			is_inode = 0;
+			if (gfs2_check_meta(bh, GFS2_METATYPE_DI) == 0)
+				is_inode = 1;
+
+			check_magic = ((struct gfs2_meta_header *)
+				       (bh->b_data))->mh_magic;
+
+			q = block_type(block);
+			if (q != gfs2_block_free) {
+				if (be32_to_cpu(check_magic) == GFS2_MAGIC &&
+				    sdp->gfs1 && !is_inode) {
+					log_debug("Block 0x%llx assumed to be "
+						  "previously processed GFS1 "
+						  "non-dinode metadata.\n",
+						  (unsigned long long)block);
+					brelse(bh);
+					first = 0;
+					continue;
+				}
+				log_err( _("Found a duplicate inode block at "
+					   "#%llu (0x%llx) previously marked "
+					   "as a %s\n"),
+					 (unsigned long long)block,
+					 (unsigned long long)block,
+					 block_type_string(q));
+				ip = fsck_inode_get(sdp, bh);
+				if (is_inode &&
+				    ip->i_di.di_num.no_addr == block)
+					add_duplicate_ref(ip, block,
+							  ref_is_inode, 0,
+							  INODE_VALID);
+				else
+					log_info(_("dinum.no_addr is wrong, "
+						   "so I assume the bitmap is "
+						   "just wrong.\n"));
+				fsck_inode_put(&ip);
+				brelse(bh);
+				first = 0;
+				continue;
+			}
+
 			/*log_debug( _("Checking metadata block #%" PRIu64
 			  " (0x%" PRIx64 ")\n"), block, block);*/
 
-			if (gfs2_check_meta(bh, GFS2_METATYPE_DI)) {
-				log_err( _("Found invalid inode at block #"
-					   "%llu (0x%llx)\n"),
-					 (unsigned long long)block,
-					 (unsigned long long)block);
-				if (gfs2_blockmap_set(bl, block,
-						      gfs2_block_free)) {
-					stack;
-					brelse(bh);
-					return FSCK_ERROR;
+			if (!is_inode) {
+				if (be32_to_cpu(check_magic) == GFS2_MAGIC) {
+				/* In gfs2, a bitmap mark of 2 means an inode,
+				   but in gfs1 it means any metadata.  So if
+				   this is gfs1 and not an inode, it may be
+				   okay.  If it's non-dinode metadata, it will
+				   be referenced by an inode, so we need to
+				   skip it here and it will be sorted out
+				   when the referencing inode is checked. */
+					if (sdp->gfs1) {
+						log_debug( _("Deferring GFS1 "
+							     "metadata block #"
+							     "%" PRIu64" (0x%"
+							     PRIx64 ")\n"),
+							   block, block);
+						brelse(bh);
+						first = 0;
+						continue;
+					}
+					log_err( _("Found invalid inode at "
+						   "block #%llu (0x%llx)\n"),
+						 (unsigned long long)block,
+						 (unsigned long long)block);
+					check_n_fix_bitmap(sdp, block, 0,
+							   gfs2_block_free);
 				}
-				check_n_fix_bitmap(sdp, block,
-						   gfs2_block_free);
 			} else if (handle_di(sdp, bh) < 0) {
 				stack;
 				brelse(bh);
+				gfs2_special_free(&gfs1_rindex_blks);
 				return FSCK_ERROR;
 			}
 			/* Ignore everything else - they should be hit by the
@@ -1613,6 +1623,19 @@ int pass1(struct gfs2_sbd *sdp)
 			brelse(bh);
 			first = 0;
 		}
+		/*
+		  For GFS1, we have to count the "free meta" blocks in the
+		  resource group and mark them specially so we can count them
+		  properly in pass5.
+		 */
+		if (!sdp->gfs1)
+			continue;
+		first = 1;
+		while (gfs2_next_rg_freemeta(rgd, &block, first) == 0) {
+			gfs2_blockmap_set(bl, block, gfs2_freemeta);
+			first = 0;
+		}
 	}
+	gfs2_special_free(&gfs1_rindex_blks);
 	return FSCK_OK;
 }
diff --git a/gfs2/fsck/pass1b.c b/gfs2/fsck/pass1b.c
index 19b9f6a..401ee80 100644
--- a/gfs2/fsck/pass1b.c
+++ b/gfs2/fsck/pass1b.c
@@ -1,19 +1,4 @@
-/******************************************************************************
-*******************************************************************************
-**
-**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
-**  Copyright (C) 2004-2005 Red Hat, Inc.  All rights reserved.
-**
-**  This copyrighted material is made available to anyone wishing to use,
-**  modify, copy, or redistribute it subject to the terms and conditions
-**  of the GNU General Public License v.2.
-**
-*******************************************************************************
-******************************************************************************/
-
 #include <inttypes.h>
-#include <linux_endian.h>
-#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
@@ -35,382 +20,12 @@ struct fxn_info {
 };
 
 struct dup_handler {
-	struct duptree *b;
-	struct inode_with_dups *id;
+	struct duptree *dt;
 	int ref_inode_count;
 	int ref_count;
 };
 
-static int check_leaf(struct gfs2_inode *ip, uint64_t block, void *private);
-static int check_metalist(struct gfs2_inode *ip, uint64_t block,
-			  struct gfs2_buffer_head **bh, int h, void *private);
-static int check_data(struct gfs2_inode *ip, uint64_t block, void *private);
-static int check_eattr_indir(struct gfs2_inode *ip, uint64_t block,
-			     uint64_t parent, struct gfs2_buffer_head **bh,
-			     void *private);
-static int check_eattr_leaf(struct gfs2_inode *ip, uint64_t block,
-			    uint64_t parent, struct gfs2_buffer_head **bh,
-			    void *private);
-static int check_eattr_entry(struct gfs2_inode *ip,
-			     struct gfs2_buffer_head *leaf_bh,
-			     struct gfs2_ea_header *ea_hdr,
-			     struct gfs2_ea_header *ea_hdr_prev,
-			     void *private);
-static int check_eattr_extentry(struct gfs2_inode *ip, uint64_t *ea_data_ptr,
-				struct gfs2_buffer_head *leaf_bh,
-				struct gfs2_ea_header *ea_hdr,
-				struct gfs2_ea_header *ea_hdr_prev,
-				void *private);
-static int find_dentry(struct gfs2_inode *ip, struct gfs2_dirent *de,
-		       struct gfs2_dirent *prev, struct gfs2_buffer_head *bh,
-		       char *filename, uint32_t *count, void *priv);
-
-struct metawalk_fxns find_refs = {
-	.private = NULL,
-	.check_leaf = check_leaf,
-	.check_metalist = check_metalist,
-	.check_data = check_data,
-	.check_eattr_indir = check_eattr_indir,
-	.check_eattr_leaf = check_eattr_leaf,
-	.check_dentry = NULL,
-	.check_eattr_entry = check_eattr_entry,
-	.check_eattr_extentry = check_eattr_extentry,
-};
-
-struct metawalk_fxns find_dirents = {
-	.private = NULL,
-	.check_leaf = NULL,
-	.check_metalist = NULL,
-	.check_data = NULL,
-	.check_eattr_indir = NULL,
-	.check_eattr_leaf = NULL,
-	.check_dentry = find_dentry,
-	.check_eattr_entry = NULL,
-	.check_eattr_extentry = NULL,
-};
-
-static int check_leaf(struct gfs2_inode *ip, uint64_t block, void *private)
-{
-	return add_duplicate_ref(ip, block, ref_as_meta, 1, INODE_VALID);
-}
-
-static int check_metalist(struct gfs2_inode *ip, uint64_t block,
-			  struct gfs2_buffer_head **bh, int h, void *private)
-{
-	return add_duplicate_ref(ip, block, ref_as_meta, 1, INODE_VALID);
-}
-
-static int check_data(struct gfs2_inode *ip, uint64_t block, void *private)
-{
-	return add_duplicate_ref(ip, block, ref_as_data, 1, INODE_VALID);
-}
-
-static int check_eattr_indir(struct gfs2_inode *ip, uint64_t block,
-			     uint64_t parent, struct gfs2_buffer_head **bh,
-			     void *private)
-{
-	struct gfs2_sbd *sdp = ip->i_sbd;
-	int error;
-
-	error = add_duplicate_ref(ip, block, ref_as_ea, 1, INODE_VALID);
-	if (!error)
-		*bh = bread(sdp, block);
-
-	return error;
-}
-
-static int check_eattr_leaf(struct gfs2_inode *ip, uint64_t block,
-			    uint64_t parent, struct gfs2_buffer_head **bh,
-			    void *private)
-{
-	struct gfs2_sbd *sdp = ip->i_sbd;
-	int error;
-
-	error = add_duplicate_ref(ip, block, ref_as_ea, 1, INODE_VALID);
-	if (!error)
-		*bh = bread(sdp, block);
-	return error;
-}
-
-static int check_eattr_entry(struct gfs2_inode *ip,
-							 struct gfs2_buffer_head *leaf_bh,
-							 struct gfs2_ea_header *ea_hdr,
-							 struct gfs2_ea_header *ea_hdr_prev,
-							 void *private)
-{
-	return 0;
-}
-
-static int check_eattr_extentry(struct gfs2_inode *ip, uint64_t *ea_data_ptr,
-				struct gfs2_buffer_head *leaf_bh,
-				struct gfs2_ea_header *ea_hdr,
-				struct gfs2_ea_header *ea_hdr_prev,
-				void *private)
-{
-	uint64_t block = be64_to_cpu(*ea_data_ptr);
-
-	return add_duplicate_ref(ip, block, ref_as_ea, 1, INODE_VALID);
-}
-
-/*
- * check_dir_dup_ref - check for a directory entry duplicate reference
- *                     and if found, set the name into the id.
- * Returns: 1 if filename was found, otherwise 0
- */
-static int check_dir_dup_ref(struct gfs2_inode *ip,  struct gfs2_dirent *de,
-			     osi_list_t *tmp2, char *filename)
-{
-	struct inode_with_dups *id;
-
-	id = osi_list_entry(tmp2, struct inode_with_dups, list);
-	if (id->name)
-		/* We can only have one parent of inodes that contain duplicate
-		 * blocks...no need to keep looking for this one. */
-		return 1;
-	if (id->block_no == de->de_inum.no_addr) {
-		id->name = strdup(filename);
-		id->parent = ip->i_di.di_num.no_addr;
-		log_debug( _("Duplicate block %llu (0x%llx"
-			     ") is in file or directory %llu"
-			     " (0x%llx) named %s\n"),
-			   (unsigned long long)id->block_no,
-			   (unsigned long long)id->block_no,
-			   (unsigned long long)ip->i_di.di_num.no_addr,
-			   (unsigned long long)ip->i_di.di_num.no_addr,
-			   filename);
-		/* If there are duplicates of duplicates, I guess we'll miss
-		   them here. */
-		return 1;
-	}
-	return 0;
-}
-
-static int find_dentry(struct gfs2_inode *ip, struct gfs2_dirent *de,
-		       struct gfs2_dirent *prev,
-		       struct gfs2_buffer_head *bh, char *filename,
-		       uint32_t *count, void *priv)
-{
-	struct osi_node *n, *next = NULL;
-	osi_list_t *tmp2;
-	struct duptree *b;
-	int found;
-
-	for (n = osi_first(&dup_blocks); n; n = next) {
-		next = osi_next(n);
-		b = (struct duptree *)n;
-		found = 0;
-		osi_list_foreach(tmp2, &b->ref_invinode_list) {
-			if (check_dir_dup_ref(ip, de, tmp2, filename)) {
-				found = 1;
-				break;
-			}
-		}
-		if (!found) {
-			osi_list_foreach(tmp2, &b->ref_inode_list) {
-				if (check_dir_dup_ref(ip, de, tmp2, filename))
-					break;
-			}
-		}
-	}
-	/* Return the number of leaf entries so metawalk doesn't flag this
-	   leaf as having none. */
-	*count = be16_to_cpu(((struct gfs2_leaf *)bh->b_data)->lf_entries);
-	return 0;
-}
-
-static int clear_dup_metalist(struct gfs2_inode *ip, uint64_t block,
-			      struct gfs2_buffer_head **bh, int h,
-			      void *private)
-{
-	struct dup_handler *dh = (struct dup_handler *) private;
-	struct duptree *d;
-
-	if (!valid_block(ip->i_sbd, block))
-		return 0;
-
-	/* This gets tricky. We're traversing a metadata tree trying to
-	   delete an inode based on it having a duplicate block reference
-	   somewhere in its metadata.  We know this block is listed as data
-	   or metadata for this inode, but it may or may not be one of the
-	   actual duplicate references that caused the problem.  If it's not
-	   a duplicate, it's normal metadata that isn't referenced anywhere
-	   else, but we're deleting the inode out from under it, so we need
-	   to delete it altogether. If the block is a duplicate referenced
-	   block, we need to keep its type intact and let the caller sort
-	   it out once we're down to a single reference. */
-	d = dupfind(block);
-	if (!d) {
-		fsck_blockmap_set(ip, block, _("no longer valid"),
-				  gfs2_block_free);
-		return 0;
-	}
-	/* This block, having failed the above test, is duplicated somewhere */
-	if (block == dh->b->block) {
-		log_err( _("Not clearing duplicate reference in inode \"%s\" "
-			   "at block #%llu (0x%llx) to block #%llu (0x%llx) "
-			   "because it's valid for another inode.\n"),
-			 dh->id->name ? dh->id->name : _("unknown name"),
-			 (unsigned long long)ip->i_di.di_num.no_addr,
-			 (unsigned long long)ip->i_di.di_num.no_addr,
-			 (unsigned long long)block, (unsigned long long)block);
-		log_err( _("Inode %s is in directory %"PRIu64" (0x%" PRIx64 ")\n"),
-			 dh->id->name ? dh->id->name : "", dh->id->parent,
-			 dh->id->parent);
-	}
-	/* We return 1 not 0 because we need build_and_check_metalist to
-	   bypass adding the metadata below it to the metalist.  If that
-	   were to happen, all the indirect blocks pointed to by the
-	   duplicate block would be processed twice, which means it might
-	   be mistakenly freed as "no longer valid" (in this function above)
-	   even though it's valid metadata for a different inode. Returning
-	   1 ensures that the metadata isn't processed again. */
-	return 1;
-}
-
-static int clear_dup_data(struct gfs2_inode *ip, uint64_t block, void *private)
-{
-	return clear_dup_metalist(ip, block, NULL, 0, private);
-}
-
-static int clear_leaf(struct gfs2_inode *ip, uint64_t block, void *private)
-{
-	return clear_dup_metalist(ip, block, NULL, 0, private);
-}
-
-static int clear_dup_eattr_indir(struct gfs2_inode *ip, uint64_t block,
-				 uint64_t parent, struct gfs2_buffer_head **bh,
-				 void *private)
-{
-	return clear_dup_metalist(ip, block, NULL, 0, private);
-}
-
-static int clear_dup_eattr_leaf(struct gfs2_inode *ip, uint64_t block,
-				uint64_t parent, struct gfs2_buffer_head **bh,
-				void *private)
-{
-	return clear_dup_metalist(ip, block, NULL, 0, private);
-}
-
-static int clear_eattr_entry (struct gfs2_inode *ip,
-		       struct gfs2_buffer_head *leaf_bh,
-		       struct gfs2_ea_header *ea_hdr,
-		       struct gfs2_ea_header *ea_hdr_prev,
-		       void *private)
-{
-	struct gfs2_sbd *sdp = ip->i_sbd;
-	char ea_name[256];
-
-	if (!ea_hdr->ea_name_len){
-		/* Skip this entry for now */
-		return 1;
-	}
-
-	memset(ea_name, 0, sizeof(ea_name));
-	strncpy(ea_name, (char *)ea_hdr + sizeof(struct gfs2_ea_header),
-		ea_hdr->ea_name_len);
-
-	if (!GFS2_EATYPE_VALID(ea_hdr->ea_type) &&
-	   ((ea_hdr_prev) || (!ea_hdr_prev && ea_hdr->ea_type))){
-		/* Skip invalid entry */
-		return 1;
-	}
-
-	if (ea_hdr->ea_num_ptrs){
-		uint32_t avail_size;
-		int max_ptrs;
-
-		avail_size = sdp->sd_sb.sb_bsize - sizeof(struct gfs2_meta_header);
-		max_ptrs = (be32_to_cpu(ea_hdr->ea_data_len) + avail_size - 1) /
-			avail_size;
-
-		if (max_ptrs > ea_hdr->ea_num_ptrs)
-			return 1;
-		else {
-			log_debug( _("  Pointers Required: %d\n  Pointers Reported: %d\n"),
-					  max_ptrs, ea_hdr->ea_num_ptrs);
-		}
-	}
-	return 0;
-}
-
-static int clear_eattr_extentry(struct gfs2_inode *ip, uint64_t *ea_data_ptr,
-				struct gfs2_buffer_head *leaf_bh,
-				struct gfs2_ea_header *ea_hdr,
-				struct gfs2_ea_header *ea_hdr_prev,
-				void *private)
-{
-	uint64_t block = be64_to_cpu(*ea_data_ptr);
-
-	return clear_dup_metalist(ip, block, NULL, 0, private);
-}
-
-/* Finds all references to duplicate blocks in the metadata */
-static int find_block_ref(struct gfs2_sbd *sdp, uint64_t inode)
-{
-	struct gfs2_inode *ip;
-	int error = 0;
-
-	ip = fsck_load_inode(sdp, inode); /* bread, inode_get */
-	/* double-check the meta header just to be sure it's metadata */
-	if (ip->i_di.di_header.mh_magic != GFS2_MAGIC ||
-	    ip->i_di.di_header.mh_type != GFS2_METATYPE_DI) {
-		log_debug( _("Block %lld (0x%llx) is not gfs2 metadata.\n"),
-			     (unsigned long long)inode,
-			     (unsigned long long)inode);
-		return 1;
-	}
-	/* Check to see if this inode was referenced by another by mistake */
-	add_duplicate_ref(ip, inode, ref_is_inode, 1, INODE_VALID);
-
-	/* Check this dinode's metadata for references to known duplicates */
-	error = check_metatree(ip, &find_refs);
-	if (error < 0) {
-		stack;
-		fsck_inode_put(&ip); /* out, brelse, free */
-		return error;
-	}
-
-	/* Exhash dir leafs will be checked by check_metatree (right after
-	   the "end:" label.)  But if this is a linear directory we need to
-	   check the dir with check_linear_dir. */
-	if (S_ISDIR(ip->i_di.di_mode) && !(ip->i_di.di_flags & GFS2_DIF_EXHASH))
-		error = check_linear_dir(ip, ip->i_bh, &find_dirents);
-
-	/* Check for ea references in the inode */
-	if (!error)
-		error = check_inode_eattr(ip, &find_refs);
-
-	fsck_inode_put(&ip); /* out, brelse, free */
-
-	return error;
-}
-
-/* get_ref_type - figure out if all duplicate references from this inode
-   are the same type, and if so, return the type. */
-static enum dup_ref_type get_ref_type(struct inode_with_dups *id)
-{
-	enum dup_ref_type t, i;
-	int found_type_with_ref;
-	int found_other_types;
-
-	for (t = ref_as_data; t < ref_types; t++) {
-		found_type_with_ref = 0;
-		found_other_types = 0;
-		for (i = ref_as_data; i < ref_types; i++) {
-			if (id->reftypecount[i]) {
-				if (t == i)
-					found_type_with_ref = 1;
-				else
-					found_other_types = 1;
-			}
-		}
-		if (found_type_with_ref)
-			return found_other_types ? ref_types : t;
-	}
-	return ref_types;
-}
-
-static void log_inode_reference(struct duptree *b, osi_list_t *tmp, int inval)
+static void log_inode_reference(struct duptree *dt, osi_list_t *tmp, int inval)
 {
 	char reftypestring[32];
 	struct inode_with_dups *id;
@@ -430,15 +45,17 @@ static void log_inode_reference(struct duptree *b, osi_list_t *tmp, int inval)
 		    "block %llu (0x%llx) (%s)\n"), id->name,
 		  (unsigned long long)id->block_no,
 		  (unsigned long long)id->block_no, id->dup_count,
-		  (unsigned long long)b->block,
-		  (unsigned long long)b->block, reftypestring);
+		  (unsigned long long)dt->block,
+		  (unsigned long long)dt->block, reftypestring);
 }
+
+/* delete_all_dups - delete all duplicate records for a given inode */
 /*
  * resolve_dup_references - resolve all but the last dinode that has a
  *                          duplicate reference to a given block.
  *
  * @sdp - pointer to the superblock structure
- * @b - pointer to the duplicate reference rbtree to use
+ * @dt - pointer to the duplicate reference rbtree to use
  * @ref_list - list of duplicate references to be resolved (invalid or valid)
  * @dh - duplicate handler
  * inval - The references on this ref_list are invalid.  We prefer to delete
@@ -446,37 +63,41 @@ static void log_inode_reference(struct duptree *b, osi_list_t *tmp, int inval)
  * acceptable_ref - Delete dinodes that reference the given block as anything
  *                  _but_ this type.  Try to save references as this type.
  */
-static int resolve_dup_references(struct gfs2_sbd *sdp, struct duptree *b,
-				  osi_list_t *ref_list, struct dup_handler *dh,
-				  int inval, int acceptable_ref)
+static void resolve_dup_references(struct gfs2_sbd *sdp, struct duptree *dt,
+				   osi_list_t *ref_list,
+				   struct dup_handler *dh,
+				   int inval, int acceptable_ref)
 {
 	struct gfs2_inode *ip;
 	struct inode_with_dups *id;
 	osi_list_t *tmp, *x;
-	struct metawalk_fxns clear_dup_fxns = {
+	struct metawalk_fxns pass1b_fxns_delete = {
 		.private = NULL,
-		.check_leaf = clear_leaf,
-		.check_metalist = clear_dup_metalist,
-		.check_data = clear_dup_data,
-		.check_eattr_indir = clear_dup_eattr_indir,
-		.check_eattr_leaf = clear_dup_eattr_leaf,
-		.check_dentry = NULL,
-		.check_eattr_entry = clear_eattr_entry,
-		.check_eattr_extentry = clear_eattr_extentry,
+		.check_metalist = delete_metadata,
+		.check_data = delete_data,
+		.check_leaf = delete_leaf,
+		.check_eattr_indir = delete_eattr_indir,
+		.check_eattr_leaf = delete_eattr_leaf,
+		.check_eattr_entry = delete_eattr_entry,
+		.check_eattr_extentry = delete_eattr_extentry,
 	};
 	enum dup_ref_type this_ref;
 	struct inode_info *ii;
 	int found_good_ref = 0;
+	uint8_t q;
 
 	osi_list_foreach_safe(tmp, ref_list, x) {
+		if (skip_this_pass || fsck_abort)
+			return;
+
 		id = osi_list_entry(tmp, struct inode_with_dups, list);
-		dh->b = b;
-		dh->id = id;
+		dh->dt = dt;
 
 		if (dh->ref_inode_count == 1) /* down to the last reference */
-			return 1;
+			return;
 
 		this_ref = get_ref_type(id);
+		q = block_type(id->block_no);
 		if (inval)
 			log_warn( _("Invalid "));
 		/* FIXME: If we already found an acceptable reference to this
@@ -488,13 +109,19 @@ static int resolve_dup_references(struct gfs2_sbd *sdp, struct duptree *b,
 						      type and */
 		    this_ref == acceptable_ref && /* this ref is acceptable */
 		    !found_good_ref) { /* We haven't found a good reference */
-			uint8_t q;
-
 			/* If this is an invalid inode, but not on the invalid
 			   list, it's better to delete it. */
-			q = block_type(id->block_no);
 			if (q != gfs2_inode_invalid) {
 				found_good_ref = 1;
+				log_warn( _("Inode %s (%lld/0x%llx)'s "
+					    "reference to block %llu (0x%llx) "
+					    "as '%s' is acceptable.\n"),
+					  id->name,
+					  (unsigned long long)id->block_no,
+					  (unsigned long long)id->block_no,
+					  (unsigned long long)dt->block,
+					  (unsigned long long)dt->block,
+					  reftypes[this_ref]);
 				continue; /* don't delete the dinode */
 			}
 		}
@@ -511,8 +138,8 @@ static int resolve_dup_references(struct gfs2_sbd *sdp, struct duptree *b,
 			    "really %s.\n"),
 			  id->name, (unsigned long long)id->block_no,
 			  (unsigned long long)id->block_no,
-			  (unsigned long long)b->block,
-			  (unsigned long long)b->block,
+			  (unsigned long long)dt->block,
+			  (unsigned long long)dt->block,
 			  reftypes[this_ref], reftypes[acceptable_ref]);
 		if (!(query( _("Okay to delete %s inode %lld (0x%llx)? "
 			       "(y/n) "),
@@ -521,77 +148,135 @@ static int resolve_dup_references(struct gfs2_sbd *sdp, struct duptree *b,
 			     (unsigned long long)id->block_no))) {
 			log_warn( _("The bad inode was not cleared."));
 			/* delete the list entry so we don't leak memory but
-			   leave the reference count.  If the decrement the
+			   leave the reference count. If we decrement the
 			   ref count, we could get down to 1 and the dinode
 			   would be changed without a 'Yes' answer. */
 			/* (dh->ref_inode_count)--;*/
-			dup_listent_delete(id);
+			dup_listent_delete(dt, id);
 			continue;
 		}
-		log_warn( _("Clearing inode %lld (0x%llx)...\n"),
-			  (unsigned long long)id->block_no,
-			  (unsigned long long)id->block_no);
+		if (q == gfs2_block_free)
+			log_warn( _("Inode %lld (0x%llx) was previously "
+				    "deleted.\n"),
+				  (unsigned long long)id->block_no,
+				  (unsigned long long)id->block_no);
+		else
+			log_warn(_("Pass1b is deleting inode %lld (0x%llx).\n"),
+				 (unsigned long long)id->block_no,
+				 (unsigned long long)id->block_no);
 
 		ip = fsck_load_inode(sdp, id->block_no);
-		ii = inodetree_find(ip->i_di.di_num.no_addr);
-		if (ii)
-			inodetree_delete(ii);
-		clear_dup_fxns.private = (void *) dh;
-		/* Clear the EAs for the inode first */
-		check_inode_eattr(ip, &clear_dup_fxns);
-		/* If the dup was in data or metadata, clear the dinode */
-		if (id->reftypecount[ref_as_data] ||
-		    id->reftypecount[ref_as_meta]) {
-			check_metatree(ip, &clear_dup_fxns);
-			fsck_blockmap_set(ip, ip->i_di.di_num.no_addr,
-					  _("duplicate referencing bad"),
-					  gfs2_inode_invalid);
+		/* If we've already deleted this dinode, don't try to delete
+		   it again. That could free blocks that used to be duplicate
+		   references that are now resolved (and gone). */
+		if (q != gfs2_block_free) {
+			/* Clear the EAs for the inode first */
+			check_inode_eattr(ip, &pass1b_fxns_delete);
+			/* If the reference was as metadata or data, we've got
+			   a corrupt dinode that will be deleted. */
+			if (inval || id->reftypecount[ref_as_data] ||
+			    id->reftypecount[ref_as_meta]) {
+				/* Remove the inode from the inode tree */
+				ii = inodetree_find(ip->i_di.di_num.no_addr);
+				if (ii)
+					inodetree_delete(ii);
+				fsck_blockmap_set(ip, ip->i_di.di_num.no_addr,
+						_("duplicate referencing bad"),
+						  gfs2_inode_invalid);
+				/* We delete the dup_handler inode count and
+				   duplicate id BEFORE clearing the metadata,
+				   because if this is the last reference to
+				   this metadata block, we need to traverse the
+				   tree and free the data blocks it references.
+				   However, we don't want to delete other
+				   duplicates that may be used by other
+				   dinodes. */
+				(dh->ref_inode_count)--;
+				/* FIXME: other option should be to duplicate
+				   the block for each duplicate and point the
+				   metadata at the cloned blocks */
+				check_metatree(ip, &pass1b_fxns_delete);
+			}
 		}
+		/* Now we've got to go through an delete any other duplicate
+		   references from this dinode we're deleting. If we don't,
+		   pass1b will discover the other duplicate record, try to
+		   delete this dinode a second time, and this time its earlier
+		   duplicate references won't be seen as duplicates anymore
+		   (because they were eliminated earlier in pass1b). And so
+		   the blocks will be mistakenly freed, when, in fact, they're
+		   still being referenced by a valid dinode. */
+		delete_all_dups(ip);
 		fsck_inode_put(&ip); /* out, brelse, free */
-		(dh->ref_inode_count)--;
-		/* FIXME: other option should be to duplicate the
-		 * block for each duplicate and point the metadata at
-		 * the cloned blocks */
-		dup_listent_delete(id);
 	}
-	if (dh->ref_inode_count == 1) /* down to the last reference */
-		return 1;
-	return 0;
+	return;
 }
 
-static int handle_dup_blk(struct gfs2_sbd *sdp, struct duptree *b)
+/* revise_dup_handler - get current information about a duplicate reference
+ *
+ * Function resolve_dup_references can delete dinodes that reference blocks
+ * which may have duplicate references. Therefore, the duplicate tree is
+ * constantly being changed. This function revises the duplicate handler so
+ * that it accurately matches what's in the duplicate tree regarding this block
+ */
+static void revise_dup_handler(uint64_t dup_blk, struct dup_handler *dh)
 {
-	struct gfs2_inode *ip;
 	osi_list_t *tmp;
+	struct duptree *dt;
 	struct inode_with_dups *id;
-	struct dup_handler dh = {0};
-	int last_reference = 0;
-	struct gfs2_buffer_head *bh;
-	uint32_t cmagic, ctype;
-	enum dup_ref_type acceptable_ref;
 
+	dh->ref_inode_count = 0;
+	dh->ref_count = 0;
+	dh->dt = NULL;
+
+	dt = dupfind(dup_blk);
+	if (!dt)
+		return;
+
+	dh->dt = dt;
 	/* Count the duplicate references, both valid and invalid */
-	osi_list_foreach(tmp, &b->ref_invinode_list) {
+	osi_list_foreach(tmp, &dt->ref_invinode_list) {
 		id = osi_list_entry(tmp, struct inode_with_dups, list);
-		dh.ref_inode_count++;
-		dh.ref_count += id->dup_count;
+		dh->ref_inode_count++;
+		dh->ref_count += id->dup_count;
 	}
-	osi_list_foreach(tmp, &b->ref_inode_list) {
+	osi_list_foreach(tmp, &dt->ref_inode_list) {
 		id = osi_list_entry(tmp, struct inode_with_dups, list);
-		dh.ref_inode_count++;
-		dh.ref_count += id->dup_count;
+		dh->ref_inode_count++;
+		dh->ref_count += id->dup_count;
 	}
+}
+
+/* handle_dup_blk - handle a duplicate block reference.
+ *
+ * This function should resolve and delete the duplicate block reference given,
+ * iow dt.
+ */
+static int handle_dup_blk(struct gfs2_sbd *sdp, struct duptree *dt)
+{
+	osi_list_t *tmp;
+	struct gfs2_inode *ip;
+	struct inode_with_dups *id;
+	struct dup_handler dh = {0};
+	struct gfs2_buffer_head *bh;
+	uint32_t cmagic, ctype;
+	enum dup_ref_type acceptable_ref;
+	uint64_t dup_blk;
+
+	dup_blk = dt->block;
+	revise_dup_handler(dup_blk, &dh);
 
 	/* Log the duplicate references */
 	log_notice( _("Block %llu (0x%llx) has %d inodes referencing it"
 		   " for a total of %d duplicate references:\n"),
-		   (unsigned long long)b->block, (unsigned long long)b->block,
-		   dh.ref_inode_count, dh.ref_count);
+		    (unsigned long long)dt->block,
+		    (unsigned long long)dt->block,
+		    dh.ref_inode_count, dh.ref_count);
 
-	osi_list_foreach(tmp, &b->ref_invinode_list)
-		log_inode_reference(b, tmp, 1);
-	osi_list_foreach(tmp, &b->ref_inode_list)
-		log_inode_reference(b, tmp, 0);
+	osi_list_foreach(tmp, &dt->ref_invinode_list)
+		log_inode_reference(dt, tmp, 1);
+	osi_list_foreach(tmp, &dt->ref_inode_list)
+		log_inode_reference(dt, tmp, 0);
 
 	/* Figure out the block type to see if we can eliminate references
 	   to a different type. In other words, if the duplicate block looks
@@ -600,7 +285,7 @@ static int handle_dup_blk(struct gfs2_sbd *sdp, struct duptree *b)
 	   references to it as metadata.  Dinodes with such references are
 	   clearly corrupt and need to be deleted.
 	   And if we're left with a single reference, problem solved. */
-	bh = bread(sdp, b->block);
+	bh = bread(sdp, dt->block);
 	cmagic = ((struct gfs2_meta_header *)(bh->b_data))->mh_magic;
 	ctype = ((struct gfs2_meta_header *)(bh->b_data))->mh_type;
 	brelse(bh);
@@ -633,77 +318,67 @@ static int handle_dup_blk(struct gfs2_sbd *sdp, struct duptree *b)
 	   invalidated for other reasons, such as bad pointers.  So we need to
 	   make sure at this point that any inode deletes reverse out any
 	   duplicate reference before we get to this point. */
-	if (dh.ref_count == 1)
-		last_reference = 1;
 
 	/* Step 1 - eliminate references from inodes that are not valid.
 	 *          This may be because they were deleted due to corruption.
 	 *          All block types are unacceptable, so we use ref_types.
 	 */
-	if (!last_reference) {
+	if (dh.ref_count > 1) {
 		log_debug( _("----------------------------------------------\n"
 			     "Step 1: Eliminate references to block %llu "
 			     "(0x%llx) that were previously marked "
 			     "invalid.\n"),
-			   (unsigned long long)b->block,
-			   (unsigned long long)b->block);
-		last_reference = resolve_dup_references(sdp, b,
-							&b->ref_invinode_list,
-							&dh, 1, ref_types);
+			   (unsigned long long)dt->block,
+			   (unsigned long long)dt->block);
+		resolve_dup_references(sdp, dt, &dt->ref_invinode_list,
+				       &dh, 1, ref_types);
+		revise_dup_handler(dup_blk, &dh);
 	}
 	/* Step 2 - eliminate reference from inodes that reference it as the
 	 *          wrong type.  For example, a data file referencing it as
 	 *          a data block, but it's really a metadata block.  Or a
 	 *          directory inode referencing a data block as a leaf block.
 	 */
-	if (!last_reference) {
+	if (dh.ref_count > 1) {
 		log_debug( _("----------------------------------------------\n"
 			     "Step 2: Eliminate references to block %llu "
 			     "(0x%llx) that need the wrong block type.\n"),
-			   (unsigned long long)b->block,
-			   (unsigned long long)b->block);
-		last_reference = resolve_dup_references(sdp, b,
-							&b->ref_inode_list,
-							&dh, 0,
-							acceptable_ref);
+			   (unsigned long long)dt->block,
+			   (unsigned long long)dt->block);
+		resolve_dup_references(sdp, dt, &dt->ref_inode_list, &dh, 0,
+				       acceptable_ref);
+		revise_dup_handler(dup_blk, &dh);
 	}
 	/* Step 3 - We have multiple dinodes referencing it as the correct
 	 *          type.  Just blast one of them.
 	 *          All block types are fair game, so we use ref_types.
 	 */
-	if (!last_reference) {
+	if (dh.ref_count > 1) {
 		log_debug( _("----------------------------------------------\n"
 			     "Step 3: Choose one reference to block %llu "
 			     "(0x%llx) to keep.\n"),
-			   (unsigned long long)b->block,
-			   (unsigned long long)b->block);
-		last_reference = resolve_dup_references(sdp, b,
-							&b->ref_inode_list,
-							&dh, 0, ref_types);
+			   (unsigned long long)dt->block,
+			   (unsigned long long)dt->block);
+		resolve_dup_references(sdp, dt, &dt->ref_inode_list, &dh, 0,
+				       ref_types);
+		revise_dup_handler(dup_blk, &dh);
 	}
-	/* Now fix the block type of the block in question. */
-	if (osi_list_empty(&b->ref_inode_list)) {
-		log_notice( _("Block %llu (0x%llx) has no more references; "
-			      "Marking as 'free'.\n"),
-			    (unsigned long long)b->block,
-			    (unsigned long long)b->block);
-		gfs2_blockmap_set(bl, b->block, gfs2_block_free);
-		check_n_fix_bitmap(sdp, b->block, gfs2_block_free);
-		return 0;
-	}
-	if (last_reference) {
+	/* If there's still a last remaining reference, and it's a valid
+	   reference, use it to determine the correct block type for our
+	   blockmap and bitmap. */
+	if (dh.ref_inode_count == 1 && !osi_list_empty(&dt->ref_inode_list)) {
 		uint8_t q;
 
 		log_notice( _("Block %llu (0x%llx) has only one remaining "
-			      "reference.\n"),
-			    (unsigned long long)b->block,
-			    (unsigned long long)b->block);
+			      "valid inode referencing it.\n"),
+			    (unsigned long long)dup_blk,
+			    (unsigned long long)dup_blk);
 		/* If we're down to a single reference (and not all references
 		   deleted, which may be the case of an inode that has only
 		   itself and a reference), we need to reset the block type
 		   from invalid to data or metadata. Start at the first one
 		   in the list, not the structure's place holder. */
-		tmp = (&b->ref_inode_list)->next;
+		tmp = dt->ref_inode_list.next;
 		id = osi_list_entry(tmp, struct inode_with_dups, list);
 		log_debug( _("----------------------------------------------\n"
 			     "Step 4. Set block type based on the remaining "
@@ -719,47 +394,181 @@ static int handle_dup_blk(struct gfs2_sbd *sdp, struct duptree *b)
 				     "the block as free.\n"),
 				   (unsigned long long)id->block_no,
 				   (unsigned long long)id->block_no);
-			fsck_blockmap_set(ip, b->block,
+			fsck_blockmap_set(ip, dt->block,
 					  _("reference-repaired leaf"),
 					  gfs2_block_free);
 		} else if (id->reftypecount[ref_is_inode]) {
 			set_ip_blockmap(ip, 0); /* 0=do not add to dirtree */
 		} else if (id->reftypecount[ref_as_data]) {
-			fsck_blockmap_set(ip, b->block,
+			fsck_blockmap_set(ip, dt->block,
 					  _("reference-repaired data"),
 					  gfs2_block_used);
 		} else if (id->reftypecount[ref_as_meta]) {
-			if (S_ISDIR(ip->i_di.di_mode))
-				fsck_blockmap_set(ip, b->block,
+			if (is_dir(&ip->i_di, sdp->gfs1))
+				fsck_blockmap_set(ip, dt->block,
 						  _("reference-repaired leaf"),
 						  gfs2_leaf_blk);
 			else
-				fsck_blockmap_set(ip, b->block,
+				fsck_blockmap_set(ip, dt->block,
 						  _("reference-repaired "
 						    "indirect"),
 						  gfs2_indir_blk);
 		} else
-			fsck_blockmap_set(ip, b->block,
+			fsck_blockmap_set(ip, dt->block,
 					  _("reference-repaired extended "
 					    "attribute"),
 					  gfs2_meta_eattr);
 		fsck_inode_put(&ip); /* out, brelse, free */
+		log_debug(_("Done with duplicate reference to block 0x%llx\n"),
+			  (unsigned long long)dt->block);
+		dup_delete(dt);
 	} else {
 		/* They may have answered no and not fixed all references. */
-		log_debug( _("All duplicate references were processed.\n"));
+		log_debug( _("All duplicate references to block 0x%llx were "
+			     "processed.\n"), (unsigned long long)dup_blk);
+		if (dh.ref_count) {
+			log_debug(_("Done with duplicate reference to block "
+				    "0x%llx, but %d references remain.\n"),
+				  (unsigned long long)dup_blk, dh.ref_count);
+		} else {
+			log_notice( _("Block %llu (0x%llx) has no more "
+				      "references; Marking as 'free'.\n"),
+				    (unsigned long long)dup_blk,
+				    (unsigned long long)dup_blk);
+			if (dh.dt)
+				dup_delete(dh.dt);
+			/* Now fix the block type of the block in question. */
+			gfs2_blockmap_set(bl, dup_blk, gfs2_block_free);
+			check_n_fix_bitmap(sdp, dup_blk, 0, gfs2_block_free);
+		}
 	}
 	return 0;
 }
 
+static int check_leaf_refs(struct gfs2_inode *ip, uint64_t block,
+			   void *private)
+{
+	return add_duplicate_ref(ip, block, ref_as_meta, 1, INODE_VALID);
+}
+
+static int check_metalist_refs(struct gfs2_inode *ip, uint64_t block,
+			       struct gfs2_buffer_head **bh, int h,
+			       int *is_valid, int *was_duplicate,
+			       void *private)
+{
+	*was_duplicate = 0;
+	*is_valid = 1;
+	return add_duplicate_ref(ip, block, ref_as_meta, 1, INODE_VALID);
+}
+
+static int check_data_refs(struct gfs2_inode *ip, uint64_t metablock,
+			   uint64_t block, void *private)
+{
+	return add_duplicate_ref(ip, block, ref_as_data, 1, INODE_VALID);
+}
+
+static int check_eattr_indir_refs(struct gfs2_inode *ip, uint64_t block,
+				  uint64_t parent,
+				  struct gfs2_buffer_head **bh, void *private)
+{
+	struct gfs2_sbd *sdp = ip->i_sbd;
+	int error;
+
+	error = add_duplicate_ref(ip, block, ref_as_ea, 1, INODE_VALID);
+	if (!error)
+		*bh = bread(sdp, block);
+
+	return error;
+}
+
+static int check_eattr_leaf_refs(struct gfs2_inode *ip, uint64_t block,
+				 uint64_t parent, struct gfs2_buffer_head **bh,
+				 void *private)
+{
+	struct gfs2_sbd *sdp = ip->i_sbd;
+	int error;
+
+	error = add_duplicate_ref(ip, block, ref_as_ea, 1, INODE_VALID);
+	if (!error)
+		*bh = bread(sdp, block);
+	return error;
+}
+
+static int check_eattr_entry_refs(struct gfs2_inode *ip,
+				  struct gfs2_buffer_head *leaf_bh,
+				  struct gfs2_ea_header *ea_hdr,
+				  struct gfs2_ea_header *ea_hdr_prev,
+				  void *private)
+{
+	return 0;
+}
+
+static int check_eattr_extentry_refs(struct gfs2_inode *ip,
+				     uint64_t *ea_data_ptr,
+				     struct gfs2_buffer_head *leaf_bh,
+				     struct gfs2_ea_header *ea_hdr,
+				     struct gfs2_ea_header *ea_hdr_prev,
+				     void *private)
+{
+	uint64_t block = be64_to_cpu(*ea_data_ptr);
+
+	return add_duplicate_ref(ip, block, ref_as_ea, 1, INODE_VALID);
+}
+
+/* Finds all references to duplicate blocks in the metadata */
+/* Finds all references to duplicate blocks in the metadata */
+static int find_block_ref(struct gfs2_sbd *sdp, uint64_t inode)
+{
+	struct gfs2_inode *ip;
+	int error = 0;
+	struct metawalk_fxns find_refs = {
+		.private = NULL,
+		.check_leaf = check_leaf_refs,
+		.check_metalist = check_metalist_refs,
+		.check_data = check_data_refs,
+		.check_eattr_indir = check_eattr_indir_refs,
+		.check_eattr_leaf = check_eattr_leaf_refs,
+		.check_eattr_entry = check_eattr_entry_refs,
+		.check_eattr_extentry = check_eattr_extentry_refs,
+	};
+
+	ip = fsck_load_inode(sdp, inode); /* bread, inode_get */
+
+	/* double-check the meta header just to be sure it's metadata */
+	if (ip->i_di.di_header.mh_magic != GFS2_MAGIC ||
+	    ip->i_di.di_header.mh_type != GFS2_METATYPE_DI) {
+		log_debug( _("Block %lld (0x%llx) is not gfs2 metadata.\n"),
+			     (unsigned long long)inode,
+			     (unsigned long long)inode);
+		error = 1;
+		goto out;
+	}
+	/* Check to see if this inode was referenced by another by mistake */
+	add_duplicate_ref(ip, inode, ref_is_inode, 1, INODE_VALID);
+
+	/* Check this dinode's metadata for references to known duplicates */
+	error = check_metatree(ip, &find_refs);
+	if (error < 0)
+		stack;
+
+	/* Check for ea references in the inode */
+	if (!error)
+		error = check_inode_eattr(ip, &find_refs);
+
+out:
+	fsck_inode_put(&ip); /* out, brelse, free */
+	return error;
+}
+
 /* Pass 1b handles finding the previous inode for a duplicate block
  * When found, store the inodes pointing to the duplicate block for
  * use in pass2 */
 int pass1b(struct gfs2_sbd *sdp)
 {
-	struct duptree *b;
+	struct duptree *dt;
 	uint64_t i;
 	uint8_t q;
-	struct osi_node *n, *next = NULL;
+	struct osi_node *n;
 	int rc = FSCK_OK;
 
 	log_info( _("Looking for duplicate blocks...\n"));
@@ -810,17 +619,11 @@ int pass1b(struct gfs2_sbd *sdp)
 	 * it later */
 	log_info( _("Handling duplicate blocks\n"));
 out:
-        for (n = osi_first(&dup_blocks); n; n = next) {
-		next = osi_next(n);
-                b = (struct duptree *)n;
+	/* Resolve all duplicates by clearing out the dup tree */
+        while ((n = osi_first(&dup_blocks))) {
+                dt = (struct duptree *)n;
 		if (!skip_this_pass && !rc) /* no error & not asked to skip the rest */
-			handle_dup_blk(sdp, b);
-		/* Do not attempt to free the dup_blocks list or its parts
-		   here because any func that calls check_metatree needs
-		   to check duplicate status based on this linked list.
-		   This is especially true for pass2 where it may delete "bad"
-		   inodes, and we can't delete an inode's indirect block if
-		   it was a duplicate (therefore in use by another dinode). */
+			handle_dup_blk(sdp, dt);
 	}
 	return rc;
 }
diff --git a/gfs2/fsck/pass1c.c b/gfs2/fsck/pass1c.c
index 48ea430..16e3db3 100644
--- a/gfs2/fsck/pass1c.c
+++ b/gfs2/fsck/pass1c.c
@@ -1,17 +1,4 @@
-/******************************************************************************
-*******************************************************************************
-**
-**  Copyright (C) 2005 Red Hat, Inc.  All rights reserved.
-**
-**  This copyrighted material is made available to anyone wishing to use,
-**  modify, copy, or redistribute it subject to the terms and conditions
-**  of the GNU General Public License v.2.
-**
-*******************************************************************************
-******************************************************************************/
-
 #include <inttypes.h>
-#include <linux_endian.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
@@ -23,6 +10,12 @@
 #include "util.h"
 #include "metawalk.h"
 
+struct metawalk_fxns pass1c_fxns_delete = {
+	.private = NULL,
+	.check_eattr_indir = delete_eattr_indir,
+	.check_eattr_leaf = delete_eattr_leaf,
+};
+
 static int remove_eattr_entry(struct gfs2_sbd *sdp,
 			      struct gfs2_buffer_head *leaf_bh,
 			      struct gfs2_ea_header *curr,
@@ -37,9 +30,10 @@ static int remove_eattr_entry(struct gfs2_sbd *sdp,
 		if (curr->ea_flags & GFS2_EAFLAG_LAST)
 			prev->ea_flags |= GFS2_EAFLAG_LAST;	
 	}
-	log_err( _("Bad Extended Attribute at block #%"PRIu64
-		   " (0x%" PRIx64 ") removed.\n"),
-		 leaf_bh->b_blocknr, leaf_bh->b_blocknr);
+	log_err( _("Bad Extended Attribute at block #%llu"
+		   " (0x%llx) removed.\n"),
+		 (unsigned long long)leaf_bh->b_blocknr,
+		 (unsigned long long)leaf_bh->b_blocknr);
 	bmodified(leaf_bh);
 	return 0;
 }
@@ -72,7 +66,7 @@ static int ask_remove_eattr_entry(struct gfs2_sbd *sdp,
 static int ask_remove_eattr(struct gfs2_inode *ip)
 {
 	if (query( _("Remove the bad Extended Attribute? (y/n) "))) {
-		ip->i_di.di_eattr = 0;
+		check_inode_eattr(ip, &pass1c_fxns_delete);
 		bmodified(ip->i_bh);
 		log_err( _("Bad Extended Attribute removed.\n"));
 		return 1;
@@ -205,7 +199,7 @@ static int check_eattr_entry(struct gfs2_inode *ip,
 			return ask_remove_eattr_entry(sdp, leaf_bh, ea_hdr,
 						      ea_hdr_prev, 0, 0);
 		} else {
-			log_debug( _("  Pointers Required: %d\n  Pointers Reported: %d\n"),
+			log_debug( _(" Pointers Required: %d\n  Pointers Reported: %d\n"),
 					  max_ptrs, ea_hdr->ea_num_ptrs);
 		}
 	}
diff --git a/gfs2/fsck/pass2.c b/gfs2/fsck/pass2.c
index 1c0d721..ba5a71d 100644
--- a/gfs2/fsck/pass2.c
+++ b/gfs2/fsck/pass2.c
@@ -1,16 +1,3 @@
-/******************************************************************************
-*******************************************************************************
-**
-**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
-**  Copyright (C) 2004-2007 Red Hat, Inc.  All rights reserved.
-**
-**  This copyrighted material is made available to anyone wishing to use,
-**  modify, copy, or redistribute it subject to the terms and conditions
-**  of the GNU General Public License v.2.
-**
-*******************************************************************************
-******************************************************************************/
-
 #include <dirent.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -18,7 +5,6 @@
 #include <inttypes.h>
 #include <sys/stat.h>
 #include <libintl.h>
-#include <errno.h>
 #define _(String) gettext(String)
 
 #include "libgfs2.h"
@@ -27,10 +13,20 @@
 #include "eattr.h"
 #include "metawalk.h"
 #include "link.h"
+#include "lost_n_found.h"
 #include "inode_hash.h"
 
 #define MAX_FILENAME 256
 
+struct metawalk_fxns pass2_fxns;
+
+struct metawalk_fxns delete_eattrs = {
+	.check_eattr_indir = delete_eattr_indir,
+	.check_eattr_leaf = delete_eattr_leaf,
+	.check_eattr_entry = delete_eattr_entry,
+	.check_eattr_extentry = delete_eattr_extentry,
+};
+
 /* Set children's parent inode in dir_info structure - ext2 does not set
  * dotdot inode here, but instead in pass3 - should we? */
 static int set_parent_dir(struct gfs2_sbd *sdp, struct gfs2_inum child,
@@ -50,9 +46,9 @@ static int set_parent_dir(struct gfs2_sbd *sdp, struct gfs2_inum child,
 	if (di->dinode.no_addr == child.no_addr &&
 	    di->dinode.no_formal_ino == child.no_formal_ino) {
 		if (di->treewalk_parent) {
-			log_err( _("Another directory at block %llx (0x%llx) "
-				   "already contains this child %lld (%llx) - "
-				   "checking parent %llx (0x%llx)\n"),
+			log_err( _("Another directory at block %lld (0x%llx) "
+				   "already contains this child %lld (0x%llx)"
+				   " - checking parent %lld (0x%llx)\n"),
 				 (unsigned long long)di->treewalk_parent,
 				 (unsigned long long)di->treewalk_parent,
 				 (unsigned long long)child.no_addr,
@@ -137,31 +133,32 @@ static const char *de_type_string(uint8_t de_type)
 	return de_types[3]; /* invalid */
 }
 
-static int check_file_type(uint8_t de_type, uint8_t blk_type)
+static int check_file_type(uint8_t de_type, uint8_t blk_type, int gfs1)
 {
 	switch(blk_type) {
 	case gfs2_inode_dir:
-		if (de_type != DT_DIR)
+		if (de_type != (gfs1 ? GFS_FILE_DIR : DT_DIR))
 			return 1;
 		break;
 	case gfs2_inode_file:
-		if (de_type != DT_REG)
+		if (de_type != (gfs1 ? GFS_FILE_REG : DT_REG))
 			return 1;
 		break;
 	case gfs2_inode_lnk:
-		if (de_type != DT_LNK)
+		if (de_type != (gfs1 ? GFS_FILE_LNK : DT_LNK))
 			return 1;
 		break;
 	case gfs2_inode_device:
-		if (de_type != DT_BLK && de_type != DT_CHR)
+		if ((de_type != (gfs1 ? GFS_FILE_BLK : DT_BLK)) &&
+		    (de_type != (gfs1 ? GFS_FILE_CHR : DT_CHR)))
 			return 1;
 		break;
 	case gfs2_inode_fifo:
-		if (de_type != DT_FIFO)
+		if (de_type != (gfs1 ? GFS_FILE_FIFO : DT_FIFO))
 			return 1;
 		break;
 	case gfs2_inode_sock:
-		if (de_type != DT_SOCK)
+		if (de_type != (gfs1 ? GFS_FILE_SOCK : DT_SOCK))
 			return 1;
 		break;
 	default:
@@ -172,59 +169,6 @@ static int check_file_type(uint8_t de_type, uint8_t blk_type)
 	return 0;
 }
 
-static int delete_eattr_entry (struct gfs2_inode *ip,
-			       struct gfs2_buffer_head *leaf_bh,
-			       struct gfs2_ea_header *ea_hdr,
-			       struct gfs2_ea_header *ea_hdr_prev,
-			       void *private)
-{
-	struct gfs2_sbd *sdp = ip->i_sbd;
-	char ea_name[256];
-
-	if (!ea_hdr->ea_name_len){
-		/* Skip this entry for now */
-		return 1;
-	}
-
-	memset(ea_name, 0, sizeof(ea_name));
-	strncpy(ea_name, (char *)ea_hdr + sizeof(struct gfs2_ea_header),
-		ea_hdr->ea_name_len);
-
-	if (!GFS2_EATYPE_VALID(ea_hdr->ea_type) &&
-	   ((ea_hdr_prev) || (!ea_hdr_prev && ea_hdr->ea_type))){
-		/* Skip invalid entry */
-		return 1;
-	}
-
-	if (ea_hdr->ea_num_ptrs){
-		uint32_t avail_size;
-		int max_ptrs;
-
-		avail_size = sdp->sd_sb.sb_bsize - sizeof(struct gfs2_meta_header);
-		max_ptrs = (be32_to_cpu(ea_hdr->ea_data_len) + avail_size - 1) /
-			avail_size;
-
-		if (max_ptrs > ea_hdr->ea_num_ptrs)
-			return 1;
-		else {
-			log_debug( _("  Pointers Required: %d\n  Pointers Reported: %d\n"),
-					  max_ptrs, ea_hdr->ea_num_ptrs);
-		}
-	}
-	return 0;
-}
-
-static int delete_eattr_extentry(struct gfs2_inode *ip, uint64_t *ea_data_ptr,
-				 struct gfs2_buffer_head *leaf_bh,
-				 struct gfs2_ea_header *ea_hdr,
-				 struct gfs2_ea_header *ea_hdr_prev,
-				 void *private)
-{
-	uint64_t block = be64_to_cpu(*ea_data_ptr);
-
-	return delete_metadata(ip, block, NULL, 0, private);
-}
-
 struct metawalk_fxns pass2_fxns_delete = {
 	.private = NULL,
 	.check_metalist = delete_metadata,
@@ -263,7 +207,7 @@ static int bad_formal_ino(struct gfs2_inode *ip, struct gfs2_dirent *dent,
 		 (unsigned long long)entry.no_formal_ino,
 		 (unsigned long long)ii->di_num.no_formal_ino,
 		 (unsigned long long)ii->di_num.no_formal_ino);
-	if (q != gfs2_inode_dir) {
+	if (q != gfs2_inode_dir || !strcmp("..", tmp_name)) {
 		if (query( _("Remove the corrupt directory entry? (y/n) ")))
 			return 1;
 		log_err( _("Corrupt directory entry not removed.\n"));
@@ -299,51 +243,216 @@ static int bad_formal_ino(struct gfs2_inode *ip, struct gfs2_dirent *dent,
 	return 0;
 }
 
-/* FIXME: should maybe refactor this a bit - but need to deal with
- * FIXMEs internally first */
-static int check_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent,
-		 struct gfs2_dirent *prev_de,
-		 struct gfs2_buffer_head *bh, char *filename,
-		 uint32_t *count, void *priv)
+static int hash_table_index(uint32_t hash, struct gfs2_inode *ip)
 {
-	struct gfs2_sbd *sdp = ip->i_sbd;
-	uint8_t q = 0;
-	char tmp_name[MAX_FILENAME];
-	struct gfs2_inum entry;
-	struct dir_status *ds = (struct dir_status *) priv;
-	int error;
-	struct gfs2_inode *entry_ip = NULL;
-	struct metawalk_fxns clear_eattrs = {0};
-	struct gfs2_dirent dentry, *de;
-	uint32_t calculated_hash;
+	return hash >> (32 - ip->i_di.di_depth);
+}
 
-	memset(&dentry, 0, sizeof(struct gfs2_dirent));
-	gfs2_dirent_in(&dentry, (char *)dent);
-	de = &dentry;
+static int hash_table_max(int lindex, struct gfs2_inode *ip,
+		   struct gfs2_buffer_head *bh)
+{
+	struct gfs2_leaf *leaf = (struct gfs2_leaf *)bh->b_data;
+	return (1 << (ip->i_di.di_depth - be16_to_cpu(leaf->lf_depth))) +
+		lindex - 1;
+}
 
-	clear_eattrs.check_eattr_indir = delete_eattr_indir;
-	clear_eattrs.check_eattr_leaf = delete_eattr_leaf;
-	clear_eattrs.check_eattr_entry = clear_eattr_entry;
-	clear_eattrs.check_eattr_extentry = clear_eattr_extentry;
+static int check_leaf_depth(struct gfs2_inode *ip, uint64_t leaf_no,
+			    int ref_count, struct gfs2_buffer_head *lbh)
+{
+	struct gfs2_leaf *leaf = (struct gfs2_leaf *)lbh->b_data;
+	int cur_depth = be16_to_cpu(leaf->lf_depth);
+	int exp_count = 1 << (ip->i_di.di_depth - cur_depth);
+	int divisor;
+	int factor, correct_depth;
 
-	entry.no_addr = de->de_inum.no_addr;
-	entry.no_formal_ino = de->de_inum.no_formal_ino;
+	if (exp_count == ref_count)
+		return 0;
 
-	/* Start of checks */
-	memset(tmp_name, 0, MAX_FILENAME);
-	if (de->de_name_len < MAX_FILENAME)
-		strncpy(tmp_name, filename, de->de_name_len);
-	else
-		strncpy(tmp_name, filename, MAX_FILENAME - 1);
+	factor = 0;
+	divisor = ref_count;
+	while (divisor > 1) {
+		factor++;
+		divisor >>= 1;
+	}
+	if (ip->i_di.di_depth < factor) /* can't be fixed--leaf must be on the
+					   wrong dinode. */
+		return -1;
+	correct_depth = ip->i_di.di_depth - factor;
+	if (cur_depth == correct_depth)
+		return 0;
+
+	log_err(_("Leaf block %llu (0x%llx) in dinode %llu (0x%llx) has the "
+		  "wrong depth: is %d (length %d), should be %d (length "
+		  "%d).\n"),
+		(unsigned long long)leaf_no, (unsigned long long)leaf_no,
+		(unsigned long long)ip->i_di.di_num.no_addr,
+		(unsigned long long)ip->i_di.di_num.no_addr,
+		cur_depth, ref_count, correct_depth, exp_count);
+	if (!query( _("Fix the leaf block? (y/n)"))) {
+		log_err( _("The leaf block was not fixed.\n"));
+		return 0;
+	}
+
+	leaf->lf_depth = cpu_to_be16(correct_depth);
+	bmodified(lbh);
+	log_err( _("The leaf block depth was fixed.\n"));
+	return 1;
+}
+
+/* wrong_leaf: Deal with a dirent discovered to be on the wrong leaf block
+ *
+ * Returns: 1 if the dirent is to be removed, 0 if it needs to be kept,
+ *          or -1 on error
+ */
+static int wrong_leaf(struct gfs2_inode *ip, struct gfs2_inum *entry,
+		      const char *tmp_name, int lindex, int lindex_max,
+		      int hash_index, struct gfs2_buffer_head *bh,
+		      struct dir_status *ds, struct gfs2_dirent *dent,
+		      struct gfs2_dirent *de, struct gfs2_dirent *prev_de,
+		      uint32_t *count, uint8_t q)
+{
+	struct gfs2_sbd *sdp = ip->i_sbd;
+	struct gfs2_buffer_head *dest_lbh;
+	uint64_t planned_leaf, real_leaf;
+	int li, dest_ref, error;
+	uint64_t *tbl;
+
+	log_err(_("Directory entry '%s' at block %lld (0x%llx) is on the "
+		  "wrong leaf block.\n"), tmp_name,
+		(unsigned long long)entry->no_addr,
+		(unsigned long long)entry->no_addr);
+	log_err(_("Leaf index is: 0x%x. The range for this leaf block is "
+		  "0x%x - 0x%x\n"), hash_index, lindex, lindex_max);
+	if (!query( _("Move the misplaced directory entry to "
+		      "a valid leaf block? (y/n) "))) {
+		log_err( _("Misplaced directory entry not moved.\n"));
+		return 0;
+	}
+
+	/* check the destination leaf block's depth */
+	tbl = get_dir_hash(ip);
+	if (tbl == NULL) {
+		perror("get_dir_hash");
+		return -1;
+	}
+	planned_leaf = be64_to_cpu(tbl[hash_index]);
+	log_err(_("Moving it from leaf %llu (0x%llx) to %llu (0x%llx)\n"),
+		(unsigned long long)be64_to_cpu(tbl[lindex]),
+		(unsigned long long)be64_to_cpu(tbl[lindex]),
+		(unsigned long long)planned_leaf,
+		(unsigned long long)planned_leaf);
+	/* Can't trust lf_depth; we have to count */
+	dest_ref = 0;
+	for (li = 0; li < (1 << ip->i_di.di_depth); li++) {
+		if (be64_to_cpu(tbl[li]) == planned_leaf)
+			dest_ref++;
+		else if (dest_ref)
+			break;
+	}
+	dest_lbh = bread(sdp, planned_leaf);
+	check_leaf_depth(ip, planned_leaf, dest_ref, dest_lbh);
+	brelse(dest_lbh);
+	free(tbl);
+
+	/* check if it's already on the correct leaf block */
+	error = dir_search(ip, tmp_name, de->de_name_len, NULL, &de->de_inum);
+	if (!error) {
+		log_err(_("The misplaced directory entry already appears on "
+			  "the correct leaf block.\n"));
+		log_err( _("The bad duplicate directory entry "
+			   "'%s' was cleared.\n"), tmp_name);
+		return 1; /* nuke the dent upon return */
+	}
 
-	if (!valid_block(ip->i_sbd, entry.no_addr)) {
+	if (dir_add(ip, tmp_name, de->de_name_len, &de->de_inum,
+		    de->de_type) == 0) {
+		log_err(_("The misplaced directory entry was moved to a "
+			  "valid leaf block.\n"));
+		gfs2_get_leaf_nr(ip, hash_index, &real_leaf);
+		if (real_leaf != planned_leaf) {
+			log_err(_("The planned leaf was split. The new leaf "
+				  "is: %llu (0x%llx). di_blocks=%llu\n"),
+				(unsigned long long)real_leaf,
+				(unsigned long long)real_leaf,
+				(unsigned long long)ip->i_di.di_blocks);
+			fsck_blockmap_set(ip, real_leaf, _("split leaf"),
+					  gfs2_indir_blk);
+		}
+		/* If the misplaced dirent was supposed to be earlier in the
+		   hash table, we need to adjust our counts for the blocks
+		   that have already been processed. If it's supposed to
+		   appear later, we'll count it has part of our normal
+		   processing when we get to that leaf block later on in the
+		   hash table. */
+		if (hash_index > lindex) {
+			log_err(_("Accounting deferred.\n"));
+			return 1; /* nuke the dent upon return */
+		}
+		/* If we get here, it's because we moved a dent to another
+		   leaf, but that leaf has already been processed. So we have
+		   to nuke the dent from this leaf when we return, but we
+		   still need to do the "good dent" accounting. */
+		if (de->de_type == (sdp->gfs1 ? GFS_FILE_DIR : DT_DIR)) {
+			error = set_parent_dir(sdp, de->de_inum,
+					       ip->i_di.di_num);
+			if (error > 0)
+				/* This is a bit of a kludge, but returning 0
+				   in this case causes the caller to go through
+				   function set_parent_dir a second time and
+				   deal properly with the hard link. */
+				return 0;
+		}
+		error = incr_link_count(*entry, ip,
+					_("moved valid reference"));
+		if (error > 0 &&
+		    bad_formal_ino(ip, dent, *entry, tmp_name, q, de, bh) == 1)
+			return 1; /* nuke it */
+
+		/* You cannot do this:
+		   (*count)++;
+		   The reason is: *count is the count of dentries on the leaf,
+		   and we moved the dentry to a previous leaf within the same
+		   directory dinode. So the directory counts still get
+		   incremented, but not leaf entries. When we called dir_add
+		   above, it should have fixed that prev leaf's lf_entries. */
+		ds->entry_count++;
+		return 1;
+	} else {
+		log_err(_("Error moving directory entry.\n"));
+		return 1; /* nuke it */
+	}
+}
+
+/* basic_dentry_checks - fundamental checks for directory entries
+ *
+ * @ip: pointer to the incode inode structure
+ * @entry: pointer to the inum info
+ * @tmp_name: user-friendly file name
+ * @count: pointer to the entry count
+ * @de: pointer to the directory entry
+ *
+ * Returns: 1 means corruption, nuke the dentry, 0 means checks pass
+ */
+static int basic_dentry_checks(struct gfs2_inode *ip, struct gfs2_dirent *dent,
+			       struct gfs2_inum *entry, const char *tmp_name,
+			       uint32_t *count, struct gfs2_dirent *de,
+			       struct dir_status *ds, uint8_t *q,
+			       struct gfs2_buffer_head *bh)
+{
+	struct gfs2_sbd *sdp = ip->i_sbd;
+	uint32_t calculated_hash;
+	struct gfs2_inode *entry_ip = NULL;
+	int error;
+	struct inode_info *ii;
+
+	if (!valid_block(ip->i_sbd, entry->no_addr)) {
 		log_err( _("Block # referenced by directory entry %s in inode "
 			   "%lld (0x%llx) is invalid\n"),
 			 tmp_name, (unsigned long long)ip->i_di.di_num.no_addr,
 			 (unsigned long long)ip->i_di.di_num.no_addr);
 		if (query( _("Clear directory entry to out of range block? "
 			    "(y/n) "))) {
-			goto nuke_dentry;
+			return 1;
 		} else {
 			log_err( _("Directory entry to out of range block remains\n"));
 			(*count)++;
@@ -360,7 +469,7 @@ static int check_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent,
 			de->de_rec_len, de->de_name_len);
 		if (!query( _("Clear the directory entry? (y/n) "))) {
 			log_err( _("Directory entry not fixed.\n"));
-			goto dentry_is_valid;
+			return 0;
 		}
 		fsck_blockmap_set(ip, ip->i_di.di_num.no_addr,
 				  _("corrupt directory entry"),
@@ -382,7 +491,7 @@ static int check_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent,
 			   tmp_name)) {
 			log_err( _("Directory entry hash for %s not "
 				   "fixed.\n"), tmp_name);
-			goto dentry_is_valid;
+			return 0;
 		}
 		de->de_hash = calculated_hash;
 		gfs2_dirent_out(de, (char *)dent);
@@ -391,7 +500,7 @@ static int check_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent,
 			 tmp_name);
 	}
 
-	q = block_type(entry.no_addr);
+	*q = block_type(entry->no_addr);
 	/* Get the status of the directory inode */
 	/**
 	 * 1. Blocks marked "invalid" were invalidated due to duplicate
@@ -405,25 +514,25 @@ static int check_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent,
 	 * 2. Blocks marked "bad" need to have their entire
 	 * metadata tree deleted.
 	*/
-	if (q == gfs2_inode_invalid || q == gfs2_bad_block) {
+	if (*q == gfs2_inode_invalid || *q == gfs2_bad_block) {
 		/* This entry's inode has bad blocks in it */
 
 		/* Handle bad blocks */
 		log_err( _("Found directory entry '%s' pointing to invalid "
 			   "block %lld (0x%llx)\n"), tmp_name,
-			 (unsigned long long)entry.no_addr,
-			 (unsigned long long)entry.no_addr);
+			 (unsigned long long)entry->no_addr,
+			 (unsigned long long)entry->no_addr);
 
 		if (!query( _("Delete inode containing bad blocks? (y/n)"))) {
 			log_warn( _("Entry to inode containing bad blocks remains\n"));
-			goto dentry_is_valid;
+			return 0;
 		}
 
-		if (q == gfs2_bad_block) {
-			if (ip->i_di.di_num.no_addr == entry.no_addr)
+		if (*q == gfs2_bad_block) {
+			if (ip->i_di.di_num.no_addr == entry->no_addr)
 				entry_ip = ip;
 			else
-				entry_ip = fsck_load_inode(sdp, entry.no_addr);
+				entry_ip = fsck_load_inode(sdp, entry->no_addr);
 			if (ip->i_di.di_eattr) {
 				check_inode_eattr(entry_ip,
 						  &pass2_fxns_delete);
@@ -432,29 +541,29 @@ static int check_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent,
 			if (entry_ip != ip)
 				fsck_inode_put(&entry_ip);
 		}
-		fsck_blockmap_set(ip, entry.no_addr,
+		fsck_blockmap_set(ip, entry->no_addr,
 				  _("bad directory entry"), gfs2_block_free);
 		log_err( _("Inode %lld (0x%llx) was deleted.\n"),
-			 (unsigned long long)entry.no_addr,
-			 (unsigned long long)entry.no_addr);
-		goto nuke_dentry;
+			 (unsigned long long)entry->no_addr,
+			 (unsigned long long)entry->no_addr);
+		return 1;
 	}
-	if (q < gfs2_inode_dir || q > gfs2_inode_sock) {
+	if (*q < gfs2_inode_dir || *q > gfs2_inode_sock) {
 		log_err( _("Directory entry '%s' referencing inode %llu "
 			   "(0x%llx) in dir inode %llu (0x%llx) block type "
 			   "%d: %s.\n"), tmp_name,
-			 (unsigned long long)entry.no_addr,
-			 (unsigned long long)entry.no_addr,
+			 (unsigned long long)entry->no_addr,
+			 (unsigned long long)entry->no_addr,
 			 (unsigned long long)ip->i_di.di_num.no_addr,
 			 (unsigned long long)ip->i_di.di_num.no_addr,
-			 q, q == gfs2_inode_invalid ?
+			 *q, *q == gfs2_inode_invalid ?
 			 _("was previously marked invalid") :
 			 _("was deleted or is not an inode"));
 
 		if (!query( _("Clear directory entry to non-inode block? "
 			     "(y/n) "))) {
 			log_err( _("Directory entry to non-inode block remains\n"));
-			goto dentry_is_valid;
+			return 0;
 		}
 
 		/* Don't decrement the link here: Here in pass2, we increment
@@ -473,20 +582,20 @@ static int check_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent,
 		   count on "delete_block_if_notdup" knowing whether it's
 		   really a duplicate block if we never traversed the metadata
 		   tree for the invalid inode. */
-		goto nuke_dentry;
+		return 1;
 	}
 
-	error = check_file_type(de->de_type, q);
+	error = check_file_type(de->de_type, *q, sdp->gfs1);
 	if (error < 0) {
 		log_err( _("Error: directory entry type is "
 			   "incompatible with block type at block %lld "
 			   "(0x%llx) in directory inode %llu (0x%llx).\n"),
-			 (unsigned long long)entry.no_addr,
-			 (unsigned long long)entry.no_addr,
+			 (unsigned long long)entry->no_addr,
+			 (unsigned long long)entry->no_addr,
 			 (unsigned long long)ip->i_di.di_num.no_addr,
 			 (unsigned long long)ip->i_di.di_num.no_addr);
 		log_err( _("Directory entry type is %d, block type is %d.\n"),
-			 de->de_type, q);
+			 de->de_type, *q);
 		stack;
 		return -1;
 	}
@@ -494,22 +603,80 @@ static int check_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent,
 		log_err( _("Type '%s' in dir entry (%s, %llu/0x%llx) conflicts"
 			 " with type '%s' in dinode. (Dir entry is stale.)\n"),
 			 de_type_string(de->de_type), tmp_name,
-			 (unsigned long long)entry.no_addr,
-			 (unsigned long long)entry.no_addr,
-			 block_type_string(q));
+			 (unsigned long long)entry->no_addr,
+			 (unsigned long long)entry->no_addr,
+			 block_type_string(*q));
 		if (!query( _("Clear stale directory entry? (y/n) "))) {
 			log_err( _("Stale directory entry remains\n"));
-			goto dentry_is_valid;
+			return 0;
 		}
-		if (ip->i_di.di_num.no_addr == entry.no_addr)
+		if (ip->i_di.di_num.no_addr == entry->no_addr)
 			entry_ip = ip;
 		else
-			entry_ip = fsck_load_inode(sdp, entry.no_addr);
-		check_inode_eattr(entry_ip, &clear_eattrs);
+			entry_ip = fsck_load_inode(sdp, entry->no_addr);
+		check_inode_eattr(entry_ip, &delete_eattrs);
 		if (entry_ip != ip)
 			fsck_inode_put(&entry_ip);
-		goto nuke_dentry;
+		return 1;
 	}
+	/* We need to verify the formal inode number matches. If it doesn't,
+	   it needs to be deleted. */
+	ii = inodetree_find(entry->no_addr);
+	if (ii && ii->di_num.no_formal_ino != entry->no_formal_ino) {
+		log_err( _("Directory entry '%s' pointing to block %llu "
+			   "(0x%llx) in directory %llu (0x%llx) has the "
+			   "wrong 'formal' inode number.\n"), tmp_name,
+			 (unsigned long long)entry->no_addr,
+			 (unsigned long long)entry->no_addr,
+			 (unsigned long long)ip->i_di.di_num.no_addr,
+			 (unsigned long long)ip->i_di.di_num.no_addr);
+		log_err( _("The directory entry has %llu (0x%llx) but the "
+			   "inode has %llu (0x%llx)\n"),
+			 (unsigned long long)entry->no_formal_ino,
+			 (unsigned long long)entry->no_formal_ino,
+			 (unsigned long long)ii->di_num.no_formal_ino,
+			 (unsigned long long)ii->di_num.no_formal_ino);
+		return 1;
+	}
+	return 0;
+}
+
+/* FIXME: should maybe refactor this a bit - but need to deal with
+ * FIXMEs internally first */
+static int check_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent,
+			struct gfs2_dirent *prev_de,
+			struct gfs2_buffer_head *bh, char *filename,
+			uint32_t *count, int lindex, void *priv)
+{
+	struct gfs2_sbd *sdp = ip->i_sbd;
+	uint8_t q = 0;
+	char tmp_name[MAX_FILENAME];
+	struct gfs2_inum entry;
+	struct dir_status *ds = (struct dir_status *) priv;
+	int error;
+	struct gfs2_inode *entry_ip = NULL;
+	struct gfs2_dirent dentry, *de;
+	int hash_index; /* index into the hash table based on the hash */
+	int lindex_max; /* largest acceptable hash table index for hash */
+
+	memset(&dentry, 0, sizeof(struct gfs2_dirent));
+	gfs2_dirent_in(&dentry, (char *)dent);
+	de = &dentry;
+
+	entry.no_addr = de->de_inum.no_addr;
+	entry.no_formal_ino = de->de_inum.no_formal_ino;
+
+	/* Start of checks */
+	memset(tmp_name, 0, MAX_FILENAME);
+	if (de->de_name_len < MAX_FILENAME)
+		strncpy(tmp_name, filename, de->de_name_len);
+	else
+		strncpy(tmp_name, filename, MAX_FILENAME - 1);
+
+	error = basic_dentry_checks(ip, dent, &entry, tmp_name, count, de,
+				    ds, &q, bh);
+	if (error)
+		goto nuke_dentry;
 
 	if (!strcmp(".", tmp_name)) {
 		log_debug( _("Found . dentry in directory %lld (0x%llx)\n"),
@@ -531,7 +698,7 @@ static int check_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent,
 				entry_ip = ip;
 			else
 				entry_ip = fsck_load_inode(sdp, entry.no_addr);
-			check_inode_eattr(entry_ip, &clear_eattrs);
+			check_inode_eattr(entry_ip, &delete_eattrs);
 			if (entry_ip != ip)
 				fsck_inode_put(&entry_ip);
 			goto nuke_dentry;
@@ -562,7 +729,7 @@ static int check_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent,
 				entry_ip = ip;
 			else
 				entry_ip = fsck_load_inode(sdp, entry.no_addr);
-			check_inode_eattr(entry_ip, &clear_eattrs);
+			check_inode_eattr(entry_ip, &delete_eattrs);
 			if (entry_ip != ip)
 				fsck_inode_put(&entry_ip);
 			goto nuke_dentry;
@@ -592,7 +759,7 @@ static int check_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent,
 				entry_ip = ip;
 			else
 				entry_ip = fsck_load_inode(sdp, entry.no_addr);
-			check_inode_eattr(entry_ip, &clear_eattrs);
+			check_inode_eattr(entry_ip, &delete_eattrs);
 			if (entry_ip != ip)
 				fsck_inode_put(&entry_ip);
 
@@ -612,7 +779,7 @@ static int check_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent,
 				entry_ip = ip;
 			else
 				entry_ip = fsck_load_inode(sdp, entry.no_addr);
-			check_inode_eattr(entry_ip, &clear_eattrs);
+			check_inode_eattr(entry_ip, &delete_eattrs);
 			if (entry_ip != ip)
 				fsck_inode_put(&entry_ip);
 
@@ -631,6 +798,21 @@ static int check_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent,
 		ds->dotdotdir = 1;
 		goto dentry_is_valid;
 	}
+	/* If this is an exhash directory, make sure the dentries in the leaf
+	   block have a hash table index that fits */
+	if (ip->i_di.di_flags & GFS2_DIF_EXHASH) {
+		hash_index = hash_table_index(de->de_hash, ip);
+		lindex_max = hash_table_max(lindex, ip, bh);
+		if (hash_index < lindex || hash_index > lindex_max) {
+			int nuke_dent;
+
+			nuke_dent = wrong_leaf(ip, &entry, tmp_name, lindex,
+					       lindex_max, hash_index, bh, ds,
+					       dent, de, prev_de, count, q);
+			if (nuke_dent)
+				goto nuke_dentry;
+		}
+	}
 
 	/* After this point we're only concerned with directories */
 	if (q != gfs2_inode_dir) {
@@ -662,10 +844,9 @@ static int check_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent,
 dentry_is_valid:
 	/* This directory inode links to this inode via this dentry */
 	error = incr_link_count(entry, ip, _("valid reference"));
-	if (error > 0) {
-		if (bad_formal_ino(ip, dent, entry, tmp_name, q, de, bh) == 1)
-			goto nuke_dentry;
-	}
+	if (error > 0 &&
+	    bad_formal_ino(ip, dent, entry, tmp_name, q, de, bh) == 1)
+		goto nuke_dentry;
 
 	(*count)++;
 	ds->entry_count++;
@@ -678,9 +859,729 @@ nuke_dentry:
 	return 1;
 }
 
+/* pad_with_leafblks - pad a hash table with pointers to new leaf blocks
+ *
+ * @ip: pointer to the dinode structure
+ * @tbl: pointer to the hash table in memory
+ * @lindex: index location within the hash table to pad
+ * @len: number of pointers to be padded
+ */
+static void pad_with_leafblks(struct gfs2_inode *ip, uint64_t *tbl,
+			      int lindex, int len)
+{
+	int new_len, i;
+	uint32_t proper_start = lindex;
+	uint64_t new_leaf_blk;
+
+	log_err(_("Padding inode %llu (0x%llx) hash table at offset %d (0x%x) "
+		  "for %d pointers.\n"),
+		(unsigned long long)ip->i_di.di_num.no_addr,
+		(unsigned long long)ip->i_di.di_num.no_addr, lindex, lindex,
+		len);
+	while (len) {
+		new_len = 1;
+		/* Determine the next factor of 2 down from extras. We can't
+		   just write out a leaf block on a power-of-two boundary.
+		   We also need to make sure it has a length that will
+		   ensure a "proper start" block as well. */
+		while ((new_len << 1) <= len) {
+			/* Translation: If doubling the size of the new leaf
+			   will make its start boundary wrong, we have to
+			   settle for a smaller length (and iterate more). */
+			proper_start = (lindex & ~((new_len << 1) - 1));
+			if (lindex != proper_start)
+				break;
+			new_len <<= 1;
+		}
+		write_new_leaf(ip, lindex, new_len, "after", &new_leaf_blk);
+		log_err(_("New leaf block was allocated at %llu (0x%llx) for "
+			  "index %d (0x%x), length %d\n"),
+			(unsigned long long)new_leaf_blk,
+			(unsigned long long)new_leaf_blk,
+			lindex, lindex, new_len);
+		fsck_blockmap_set(ip, new_leaf_blk, _("pad leaf"),
+				  gfs2_leaf_blk);
+		/* Fix the hash table in memory to have the new leaf */
+		for (i = 0; i < new_len; i++)
+			tbl[lindex + i] = cpu_to_be64(new_leaf_blk);
+		len -= new_len;
+		lindex += new_len;
+	}
+}
+
+/* lost_leaf - repair a leaf block that's on the wrong directory inode
+ *
+ * If the correct index is less than the starting index, we have a problem.
+ * Since we process the index sequentially, the previous index has already
+ * been processed, fixed, and is now correct. But this leaf wants to overwrite
+ * a previously written good leaf. The only thing we can do is move all the
+ * directory entries to lost+found so we don't overwrite the good leaf. Then
+ * we need to pad the gap we leave.
+ */
+static int lost_leaf(struct gfs2_inode *ip, uint64_t *tbl, uint64_t leafno,
+		     int ref_count, int lindex, struct gfs2_buffer_head *bh)
+{
+	char *filename;
+	char *bh_end = bh->b_data + ip->i_sbd->bsize;
+	struct gfs2_dirent de, *dent;
+	int error;
+
+	log_err(_("Leaf block %llu (0x%llx) seems to be out of place and its "
+		  "contents need to be moved to lost+found.\n"),
+		(unsigned long long)leafno, (unsigned long long)leafno);
+	if (!query( _("Attempt to fix it? (y/n) "))) {
+		log_err( _("Directory leaf was not fixed.\n"));
+		return 0;
+	}
+	make_sure_lf_exists(ip);
+
+	dent = (struct gfs2_dirent *)(bh->b_data + sizeof(struct gfs2_leaf));
+	while (1) {
+		char tmp_name[PATH_MAX];
+
+		memset(&de, 0, sizeof(struct gfs2_dirent));
+		gfs2_dirent_in(&de, (char *)dent);
+		filename = (char *)dent + sizeof(struct gfs2_dirent);
+		memset(tmp_name, 0, sizeof(tmp_name));
+		if (de.de_name_len > sizeof(filename)) {
+			log_debug(_("Encountered bad filename length; "
+				    "stopped processing.\n"));
+			break;
+		}
+		memcpy(tmp_name, filename, de.de_name_len);
+		if ((de.de_name_len == 1 && filename[0] == '.')) {
+			log_debug(_("Skipping entry '.'\n"));
+		} else if (de.de_name_len == 2 && filename[0] == '.' &&
+			   filename[1] == '.') {
+			log_debug(_("Skipping entry '..'\n"));
+		} else if (!de.de_inum.no_formal_ino) { /* sentinel */
+			log_debug(_("Skipping sentinel '%s'\n"), tmp_name);
+		} else {
+			uint32_t count;
+			struct dir_status ds = {0};
+			uint8_t q = 0;
+
+			error = basic_dentry_checks(ip, dent, &de.de_inum,
+						    tmp_name, &count, &de,
+						    &ds, &q, bh);
+			if (error) {
+				log_err(_("Not relocating corrupt entry "
+					  "\"%s\".\n"), tmp_name);
+			} else {
+				error = dir_add(lf_dip, filename,
+						de.de_name_len, &de.de_inum,
+						de.de_type);
+				if (error && error != -EEXIST) {
+					log_err(_("Error %d encountered while "
+						  "trying to relocate \"%s\" "
+						  "to lost+found.\n"), error,
+						tmp_name);
+					return error;
+				}
+				/* This inode is linked from lost+found */
+				incr_link_count(de.de_inum, lf_dip,
+						_("from lost+found"));
+				/* If it's a directory, lost+found is
+				   back-linked to it via .. */
+				if (q == gfs2_inode_dir)
+					incr_link_count(lf_dip->i_di.di_num,
+							NULL,
+							_("to lost+found"));
+				log_err(_("Relocated \"%s\", block %llu "
+					  "(0x%llx) to lost+found.\n"),
+					tmp_name,
+					(unsigned long long)de.de_inum.no_addr,
+					(unsigned long long)de.de_inum.no_addr);
+			}
+		}
+		if ((char *)dent + de.de_rec_len >= bh_end)
+			break;
+		dent = (struct gfs2_dirent *)((char *)dent + de.de_rec_len);
+	}
+	log_err(_("Directory entries from misplaced leaf block were relocated "
+		  "to lost+found.\n"));
+	/* Free the lost leaf. */
+	fsck_blockmap_set(ip, leafno, _("lost leaf"), gfs2_block_free);
+	ip->i_di.di_blocks--;
+	bmodified(ip->i_bh);
+	/* Now we have to deal with the bad hash table entries pointing to the
+	   misplaced leaf block. But we can't just fill the gap with a single
+	   leaf. We have to write on nice power-of-two boundaries, and we have
+	   to pad out any extra pointers. */
+	pad_with_leafblks(ip, tbl, lindex, ref_count);
+	return 1;
+}
+
+static int basic_check_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent,
+			      struct gfs2_dirent *prev_de,
+			      struct gfs2_buffer_head *bh, char *filename,
+			      uint32_t *count, int lindex, void *priv)
+{
+	uint8_t q = 0;
+	char tmp_name[MAX_FILENAME];
+	struct gfs2_inum entry;
+	struct dir_status *ds = (struct dir_status *) priv;
+	struct gfs2_dirent dentry, *de;
+	int error;
+
+	memset(&dentry, 0, sizeof(struct gfs2_dirent));
+	gfs2_dirent_in(&dentry, (char *)dent);
+	de = &dentry;
+
+	entry.no_addr = de->de_inum.no_addr;
+	entry.no_formal_ino = de->de_inum.no_formal_ino;
+
+	/* Start of checks */
+	memset(tmp_name, 0, MAX_FILENAME);
+	if (de->de_name_len < MAX_FILENAME)
+		strncpy(tmp_name, filename, de->de_name_len);
+	else
+		strncpy(tmp_name, filename, MAX_FILENAME - 1);
+
+	error = basic_dentry_checks(ip, dent, &entry, tmp_name, count, de,
+				    ds, &q, bh);
+	if (error) {
+		dirent2_del(ip, bh, prev_de, dent);
+		log_err( _("Bad directory entry '%s' cleared.\n"), tmp_name);
+		return 1;
+	} else {
+		(*count)++;
+		return 0;
+	}
+}
+
+static int pass2_repair_leaf(struct gfs2_inode *ip, uint64_t *leaf_no,
+			     int lindex, int ref_count, const char *msg,
+			     void *private)
+{
+	return repair_leaf(ip, leaf_no, lindex, ref_count, msg, 1);
+}
+
+/* The purpose of leafck_fxns is to provide a means for function fix_hashtable
+ * to do basic sanity checks on leaf blocks before manipulating them, for
+ * example, splitting them. If they're corrupt, splitting them or trying to
+ * move their contents can cause a segfault. We can't really use the standard
+ * pass2_fxns because that will do things we don't want. For example, it will
+ * find '.' and '..' and increment the directory link count, which would be
+ * done a second time when the dirent is really checked in pass2_fxns.
+ * We don't want it to do the "wrong leaf" thing, or set_parent_dir either.
+ * We just want a basic sanity check on pointers and lengths.
+ */
+struct metawalk_fxns leafck_fxns = {
+	.check_leaf_depth = check_leaf_depth,
+	.check_dentry = basic_check_dentry,
+	.repair_leaf = pass2_repair_leaf,
+};
+
+/* fix_hashtable - fix a corrupt hash table
+ *
+ * The main intent of this function is to sort out hash table problems.
+ * That is, it needs to determine if leaf blocks are in the wrong place,
+ * if the count of pointers is wrong, and if there are extra pointers.
+ * Everything should be placed on correct power-of-two boundaries appropriate
+ * to their leaf depth, and extra pointers should be correctly padded with new
+ * leaf blocks.
+ *
+ * @ip: the directory dinode structure pointer
+ * @tbl: hash table that's already read into memory
+ * @hsize: hash table size, as dictated by the dinode's di_depth
+ * @leafblk: the leaf block number that appears at this lindex in the tbl
+ * @lindex: leaf index that has a problem
+ * @proper_start: where this leaf's pointers should start, as far as the
+ *                hash table is concerned (sight unseen; trusting the leaf
+ *                really belongs here).
+ * @len: count of pointers in the hash table to this leafblk
+ * @proper_len: pointer to return the proper number of pointers, as the kernel
+ *              calculates it, based on the leaf depth.
+ * @factor: the proper depth, given this number of pointers (rounded down).
+ *
+ * Returns: 0 - no changes made, or X if changes were made
+ */
+static int fix_hashtable(struct gfs2_inode *ip, uint64_t *tbl, unsigned hsize,
+			 uint64_t leafblk, int lindex, uint32_t proper_start,
+			 int len, int *proper_len, int factor)
+{
+	struct gfs2_buffer_head *lbh;
+	struct gfs2_leaf leaf;
+	struct gfs2_dirent dentry, *de;
+	int changes = 0, error, i, extras, hash_index;
+	uint64_t new_leaf_blk;
+	uint64_t leaf_no;
+	uint32_t leaf_proper_start;
+
+	*proper_len = len;
+	log_err(_("Dinode %llu (0x%llx) has a hash table error at index "
+		  "0x%x, length 0x%x: leaf block %llu (0x%llx)\n"),
+		(unsigned long long)ip->i_di.di_num.no_addr,
+		(unsigned long long)ip->i_di.di_num.no_addr, lindex, len,
+		(unsigned long long)leafblk, (unsigned long long)leafblk);
+	if (!query( _("Fix the hash table? (y/n) "))) {
+		log_err(_("Hash table not fixed.\n"));
+		return 0;
+	}
+
+	memset(&leaf, 0, sizeof(leaf));
+	leaf_no = leafblk;
+	error = check_leaf(ip, lindex, &leafck_fxns, &leaf_no, &leaf, &len);
+	if (error) {
+		log_debug("Leaf repaired while fixing the hash table.\n");
+		error = 0;
+	}
+	lbh = bread(ip->i_sbd, leafblk);
+	/* If the leaf's depth is out of range for this dinode, it's obviously
+	   attached to the wrong dinode. Move the dirents to lost+found. */
+	if (leaf.lf_depth > ip->i_di.di_depth) {
+		log_err(_("This leaf block's depth (%d) is too big for this "
+			  "dinode's depth (%d)\n"),
+			leaf.lf_depth, ip->i_di.di_depth);
+		error = lost_leaf(ip, tbl, leafblk, len, lindex, lbh);
+		brelse(lbh);
+		return error;
+	}
+
+	memset(&dentry, 0, sizeof(struct gfs2_dirent));
+	de = (struct gfs2_dirent *)(lbh->b_data + sizeof(struct gfs2_leaf));
+	gfs2_dirent_in(&dentry, (char *)de);
+
+	/* If this is an empty leaf, we can just delete it and pad. */
+	if ((dentry.de_rec_len == cpu_to_be16(ip->i_sbd->bsize -
+					      sizeof(struct gfs2_leaf))) &&
+	    (dentry.de_inum.no_formal_ino == 0)) {
+		brelse(lbh);
+		gfs2_free_block(ip->i_sbd, leafblk);
+		log_err(_("Out of place leaf block %llu (0x%llx) had no "
+			"entries, so it was deleted.\n"),
+			(unsigned long long)leafblk,
+			(unsigned long long)leafblk);
+		pad_with_leafblks(ip, tbl, lindex, len);
+		log_err(_("Reprocessing index 0x%x (case 1).\n"), lindex);
+		return 1;
+	}
+
+	/* Calculate the proper number of pointers based on the leaf depth. */
+	*proper_len = 1 << (ip->i_di.di_depth - leaf.lf_depth);
+
+	/* Look at the first dirent and check its hash value to see if it's
+	   at the proper starting offset. */
+	hash_index = hash_table_index(dentry.de_hash, ip);
+	/* Need to use len here, not *proper_len because the leaf block may
+	   be valid within the range, but starts too soon in the hash table. */
+	if (hash_index < lindex ||  hash_index > lindex + len) {
+		log_err(_("This leaf block has hash index %d, which is out of "
+			  "bounds for where it appears in the hash table "
+			  "(%d - %d)\n"),
+			hash_index, lindex, lindex + *proper_len);
+		error = lost_leaf(ip, tbl, leafblk, len, lindex, lbh);
+		brelse(lbh);
+		return error;
+	}
+
+	/* Now figure out where this leaf should start, and pad any pointers
+	   up to that point with new leaf blocks. */
+	leaf_proper_start = (hash_index & ~(*proper_len - 1));
+	if (lindex < leaf_proper_start) {
+		log_err(_("Leaf pointers start at %d (0x%x), should be %d "
+			  "(%x).\n"), lindex, lindex,
+			leaf_proper_start, leaf_proper_start);
+		pad_with_leafblks(ip, tbl, lindex, leaf_proper_start - lindex);
+		brelse(lbh);
+		return 1; /* reprocess the starting lindex */
+	}
+	/* If the proper start according to the leaf's hash index is later
+	   than the proper start according to the hash table, it's once
+	   again lost and we have to relocate it. The same applies if the
+	   leaf's hash index is prior to the proper state, but the leaf is
+	   already at its maximum depth. */
+	if ((leaf_proper_start < proper_start) ||
+	    ((*proper_len > len || lindex > leaf_proper_start) &&
+	     leaf.lf_depth == ip->i_di.di_depth)) {
+		log_err(_("Leaf block should start at 0x%x, but it appears at "
+			  "0x%x in the hash table.\n"), leaf_proper_start,
+			proper_start);
+		error = lost_leaf(ip, tbl, leafblk, len, lindex, lbh);
+		brelse(lbh);
+		return error;
+	}
+
+	/* If we SHOULD have more pointers than we do, we can solve the
+	   problem by splitting the block to a lower depth. Then we may have
+	   the right number of pointers. If the leaf block pointers start
+	   later than they should, we can split the leaf to give it a smaller
+	   footprint in the hash table. */
+	if ((*proper_len > len || lindex > leaf_proper_start) &&
+	    ip->i_di.di_depth > leaf.lf_depth) {
+		log_err(_("For depth %d, length %d, the proper start is: "
+			  "0x%x.\n"), factor, len, proper_start);
+		changes++;
+		new_leaf_blk = find_free_blk(ip->i_sbd);
+		dir_split_leaf(ip, lindex, leafblk, lbh);
+		/* re-read the leaf to pick up dir_split_leaf's changes */
+		gfs2_leaf_in(&leaf, lbh);
+		*proper_len = 1 << (ip->i_di.di_depth - leaf.lf_depth);
+		log_err(_("Leaf block %llu (0x%llx) was split from length "
+			  "%d to %d\n"), (unsigned long long)leafblk,
+			(unsigned long long)leafblk, len, *proper_len);
+		if (*proper_len < 0) {
+			log_err(_("Programming error: proper_len=%d, "
+				  "di_depth = %d, lf_depth = %d.\n"),
+				*proper_len, ip->i_di.di_depth, leaf.lf_depth);
+			exit(FSCK_ERROR);
+		}
+		log_err(_("New split-off leaf block was allocated at %lld "
+			  "(0x%llx) for index %d (0x%x)\n"),
+			(unsigned long long)new_leaf_blk,
+			(unsigned long long)new_leaf_blk, lindex, lindex);
+		fsck_blockmap_set(ip, new_leaf_blk, _("split leaf"),
+				  gfs2_leaf_blk);
+		log_err(_("Hash table repaired.\n"));
+		/* Fix up the hash table in memory to include the new leaf */
+		for (i = 0; i < *proper_len; i++)
+			tbl[lindex + i] = cpu_to_be64(new_leaf_blk);
+		if (*proper_len < (len >> 1)) {
+			log_err(_("One leaf split is not enough. The hash "
+				  "table will need to be reprocessed.\n"));
+			brelse(lbh);
+			return changes;
+		}
+		lindex += (*proper_len); /* skip the new leaf from the split */
+		len -= (*proper_len);
+	}
+	if (*proper_len < len) {
+		log_err(_("There are %d pointers, but leaf 0x%llx's "
+			  "depth, %d, only allows %d\n"),
+			len, (unsigned long long)leafblk, leaf.lf_depth,
+			*proper_len);
+	}
+	brelse(lbh);
+	/* At this point, lindex should be at the proper end of the pointers.
+	   Now we need to replace any extra duplicate pointers to the old
+	   (original) leafblk (that ran off the end) with new leaf blocks. */
+	lindex += (*proper_len); /* Skip past the normal good pointers */
+	len -= (*proper_len);
+	extras = 0;
+	for (i = 0; i < len; i++) {
+		if (be64_to_cpu(tbl[lindex + i]) == leafblk)
+			extras++;
+		else
+			break;
+	}
+	if (extras) {
+		log_err(_("Found %d extra pointers to leaf %llu (0x%llx)\n"),
+			extras, (unsigned long long)leafblk,
+			(unsigned long long)leafblk);
+		pad_with_leafblks(ip, tbl, lindex, extras);
+		log_err(_("Reprocessing index 0x%x (case 2).\n"), lindex);
+		return 1;
+	}
+	return changes;
+}
+
+/* check_hash_tbl_dups - check for the same leaf in multiple places */
+static int check_hash_tbl_dups(struct gfs2_inode *ip, uint64_t *tbl,
+			       unsigned hsize, int lindex, int len)
+{
+	int l, len2;
+	uint64_t leafblk, leaf_no;
+	struct gfs2_buffer_head *lbh;
+	struct gfs2_leaf leaf;
+	struct gfs2_dirent dentry, *de;
+	int hash_index; /* index into the hash table based on the hash */
+
+	leafblk = be64_to_cpu(tbl[lindex]);
+	for (l = 0; l < hsize; l++) {
+		if (l == lindex) { /* skip the valid reference */
+			l += len - 1;
+			continue;
+		}
+		if (be64_to_cpu(tbl[l]) != leafblk)
+			continue;
+
+		for (len2 = 0; l + len2 < hsize; len2++) {
+			if (l + len2 == lindex)
+				break;
+			if (be64_to_cpu(tbl[l + len2]) != leafblk)
+				break;
+		}
+		log_err(_("Dinode %llu (0x%llx) has duplicate leaf pointers "
+			  "to block %llu (0x%llx) at offsets %u (0x%x) "
+			  "(for 0x%x) and %u (0x%x) (for 0x%x)\n"),
+			(unsigned long long)ip->i_di.di_num.no_addr,
+			(unsigned long long)ip->i_di.di_num.no_addr,
+			(unsigned long long)leafblk,
+			(unsigned long long)leafblk, lindex, lindex, len,
+			l, l, len2);
+
+		/* See which set of references is valid: the one passed in
+		   or the duplicate we found. */
+		memset(&leaf, 0, sizeof(leaf));
+		leaf_no = leafblk;
+		if (!valid_block(ip->i_sbd, leaf_no)) /* Checked later */
+			continue;
+
+		lbh = bread(ip->i_sbd, leafblk);
+		if (gfs2_check_meta(lbh, GFS2_METATYPE_LF)) { /* Chked later */
+			brelse(lbh);
+			continue;
+		}
+
+		memset(&dentry, 0, sizeof(struct gfs2_dirent));
+		de = (struct gfs2_dirent *)(lbh->b_data +
+					    sizeof(struct gfs2_leaf));
+		gfs2_dirent_in(&dentry, (char *)de);
+		hash_index = hash_table_index(dentry.de_hash, ip);
+		brelse(lbh);
+		/* check the duplicate ref first */
+		if (hash_index < l ||  hash_index > l + len2) {
+			log_err(_("This leaf block has hash index %d, which "
+				  "is out of bounds for lindex (%d - %d)\n"),
+				hash_index, l, l + len2);
+			if (!query( _("Fix the hash table? (y/n) "))) {
+				log_err(_("Hash table not fixed.\n"));
+				return 0;
+			}
+			/* Adjust the ondisk block count. The original value
+			   may have been correct without the duplicates but
+			   pass1 would have counted them and adjusted the
+			   count to include them. So we must subtract them. */
+			ip->i_di.di_blocks--;
+			bmodified(ip->i_bh);
+			pad_with_leafblks(ip, tbl, l, len2);
+		} else {
+			log_debug(_("Hash index 0x%x is the proper "
+				    "reference to leaf 0x%llx.\n"),
+				  l, (unsigned long long)leafblk);
+		}
+		/* Check the original ref: both references might be bad.
+		   If both were bad, just return and if we encounter it
+		   again, we'll treat it as new. If the original ref is not
+		   bad, keep looking for (and fixing) other instances. */
+		if (hash_index < lindex ||  hash_index > lindex + len) {
+			log_err(_("This leaf block has hash index %d, which "
+				  "is out of bounds for lindex (%d - %d).\n"),
+				hash_index, lindex, lindex + len);
+			if (!query( _("Fix the hash table? (y/n) "))) {
+				log_err(_("Hash table not fixed.\n"));
+				return 0;
+			}
+			ip->i_di.di_blocks--;
+			bmodified(ip->i_bh);
+			pad_with_leafblks(ip, tbl, lindex, len);
+			/* At this point we know both copies are bad, so we
+			   return to start fresh */
+			return -EFAULT;
+		} else {
+			log_debug(_("Hash index 0x%x is the proper "
+				    "reference to leaf 0x%llx.\n"),
+				  lindex, (unsigned long long)leafblk);
+		}
+	}
+	return 0;
+}
+
+/* check_hash_tbl - check that the hash table is sane
+ *
+ * We've got to make sure the hash table is sane. Each leaf needs to
+ * be counted a proper power of 2. We can't just have 3 pointers to a leaf.
+ * The number of pointers must correspond to the proper leaf depth, and they
+ * must all fall on power-of-two boundaries. The leaf block pointers all need
+ * to fall properly on these boundaries, otherwise the kernel code's
+ * calculations will land it on the wrong leaf block while it's searching,
+ * and the result will be files you can see with ls, but can't open, delete
+ * or use them.
+ *
+ * The goal of this function is to check the hash table to make sure the
+ * boundaries and lengths all line up properly, and if not, to fix it.
+ *
+ * Note: There's a delicate balance here, because this function gets called
+ *       BEFORE leaf blocks are checked by function check_leaf from function
+ *       check_leaf_blks: the hash table has to be sane before we can start
+ *       checking all the leaf blocks. And yet if there's hash table corruption
+ *       we may need to reference leaf blocks to fix it, which means we need
+ *       to check and/or fix a leaf block along the way.
+ */
+static int check_hash_tbl(struct gfs2_inode *ip, uint64_t *tbl,
+			  unsigned hsize, void *private)
+{
+	int error = 0;
+	int lindex, len, proper_len, i, changes = 0;
+	uint64_t leafblk;
+	struct gfs2_leaf leaf;
+	struct gfs2_buffer_head *lbh;
+	int factor;
+	uint32_t proper_start;
+	uint32_t next_proper_start;
+	int anomaly;
+
+	lindex = 0;
+	while (lindex < hsize) {
+		if (fsck_abort)
+			return changes;
+		len = 1;
+		factor = 0;
+		leafblk = be64_to_cpu(tbl[lindex]);
+		next_proper_start = lindex;
+		anomaly = 0;
+		while (lindex + (len << 1) - 1 < hsize) {
+			if (be64_to_cpu(tbl[lindex + (len << 1) - 1]) !=
+			    leafblk)
+				break;
+			next_proper_start = (lindex & ~((len << 1) - 1));
+			if (lindex != next_proper_start)
+				anomaly = 1;
+			/* Check if there are other values written between
+			   here and the next factor. */
+			for (i = len; !anomaly && i + lindex < hsize &&
+				     i < (len << 1); i++)
+				if (be64_to_cpu(tbl[lindex + i]) != leafblk)
+					anomaly = 1;
+			if (anomaly)
+				break;
+			len <<= 1;
+			factor++;
+		}
+
+		/* Check for leftover pointers after the factor of two: */
+		proper_len = len; /* A factor of 2 that fits nicely */
+		while (lindex + len < hsize &&
+		       be64_to_cpu(tbl[lindex + len]) == leafblk)
+			len++;
+
+		/* See if that leaf block is valid. If not, write a new one
+		   that falls on a proper boundary. If it doesn't naturally,
+		   we may need more. */
+		if (!valid_block(ip->i_sbd, leafblk)) {
+			uint64_t new_leafblk;
+
+			log_err(_("Dinode %llu (0x%llx) has bad leaf pointers "
+				  "at offset %d for %d\n"),
+				(unsigned long long)ip->i_di.di_num.no_addr,
+				(unsigned long long)ip->i_di.di_num.no_addr,
+				lindex, len);
+			if (!query( _("Fix the hash table? (y/n) "))) {
+				log_err(_("Hash table not fixed.\n"));
+				lindex += len;
+				continue;
+			}
+			error = write_new_leaf(ip, lindex, proper_len,
+					       _("replacing"), &new_leafblk);
+			if (error)
+				return error;
+
+			for (i = lindex; i < lindex + proper_len; i++)
+				tbl[i] = cpu_to_be64(new_leafblk);
+			lindex += proper_len;
+			continue;
+		}
+
+		if (check_hash_tbl_dups(ip, tbl, hsize, lindex, len))
+			continue;
+
+		/* Make sure they call on proper leaf-split boundaries. This
+		   is the calculation used by the kernel, and dir_split_leaf */
+		proper_start = (lindex & ~(proper_len - 1));
+		if (lindex != proper_start) {
+			log_debug(_("lindex 0x%llx is not a proper starting "
+				    "point for leaf %llu (0x%llx): 0x%llx\n"),
+				  (unsigned long long)lindex,
+				  (unsigned long long)leafblk,
+				  (unsigned long long)leafblk,
+				  (unsigned long long)proper_start);
+			changes = fix_hashtable(ip, tbl, hsize, leafblk,
+						lindex, proper_start, len,
+						&proper_len, factor);
+			/* Check if we need to split more leaf blocks */
+			if (changes) {
+				if (proper_len < (len >> 1))
+					log_err(_("More leaf splits are "
+						  "needed; "));
+				log_err(_("Reprocessing index 0x%x (case 3).\n"),
+					lindex);
+				continue; /* Make it reprocess the lindex */
+			}
+		}
+		/* Check for extra pointers to this leaf. At this point, len
+		   is the number of pointers we have. proper_len is the proper
+		   number of pointers if the hash table is assumed correct.
+		   Function fix_hashtable will read in the leaf block and
+		   determine the "actual" proper length based on the leaf
+		   depth, and adjust the hash table accordingly. */
+		if (len != proper_len) {
+			log_err(_("Length %d (0x%x) is not a proper length "
+				  "for leaf %llu (0x%llx). Valid boundary "
+				  "assumed to be %d (0x%x).\n"), len, len,
+				(unsigned long long)leafblk,
+				(unsigned long long)leafblk,
+				proper_len, proper_len);
+			lbh = bread(ip->i_sbd, leafblk);
+			gfs2_leaf_in(&leaf, lbh);
+			if (gfs2_check_meta(lbh, GFS2_METATYPE_LF) ||
+			    leaf.lf_depth > ip->i_di.di_depth)
+				leaf.lf_depth = factor;
+			brelse(lbh);
+			changes = fix_hashtable(ip, tbl, hsize, leafblk,
+						lindex, lindex, len,
+						&proper_len, leaf.lf_depth);
+			/* If fixing the hash table made changes, we can no
+			   longer count on the leaf block pointers all pointing
+			   to the same leaf (which is checked below). To avoid
+			   flagging another error, reprocess the offset. */
+			if (changes) {
+				log_err(_("Reprocessing index 0x%x (case 4).\n"),
+					lindex);
+				continue; /* Make it reprocess the lindex */
+			}
+		}
+
+		/* Now make sure they're all the same pointer */
+		for (i = lindex; i < lindex + proper_len; i++) {
+			if (fsck_abort)
+				return changes;
+
+			if (be64_to_cpu(tbl[i]) == leafblk) /* No problem */
+				continue;
+
+			log_err(_("Dinode %llu (0x%llx) has a hash table "
+				  "inconsistency at index %d (0x%x) for %d\n"),
+				(unsigned long long)ip->i_di.di_num.no_addr,
+				(unsigned long long)ip->i_di.di_num.no_addr,
+				i, i, len);
+			if (!query( _("Fix the hash table? (y/n) "))) {
+				log_err(_("Hash table not fixed.\n"));
+				continue;
+			}
+			changes++;
+			/* Now we have to determine if the hash table is
+			   corrupt, or if the leaf has the wrong depth. */
+			lbh = bread(ip->i_sbd, leafblk);
+			gfs2_leaf_in(&leaf, lbh);
+			brelse(lbh);
+			/* Calculate the expected pointer count based on the
+			   leaf depth. */
+			proper_len = 1 << (ip->i_di.di_depth - leaf.lf_depth);
+			if (proper_len != len) {
+				log_debug(_("Length 0x%x is not proper for "
+					    "leaf %llu (0x%llx): 0x%x\n"),
+					  len, (unsigned long long)leafblk,
+					  (unsigned long long)leafblk,
+					  proper_len);
+				changes = fix_hashtable(ip, tbl, hsize,
+							leafblk, lindex,
+							lindex, len,
+							&proper_len,
+							leaf.lf_depth);
+				break;
+			}
+		}
+		lindex += proper_len;
+	}
+	if (!error && changes)
+		error = 1;
+	return error;
+}
 
 struct metawalk_fxns pass2_fxns = {
 	.private = NULL,
+	.check_leaf_depth = check_leaf_depth,
 	.check_leaf = NULL,
 	.check_metalist = NULL,
 	.check_data = NULL,
@@ -688,6 +1589,8 @@ struct metawalk_fxns pass2_fxns = {
 	.check_eattr_leaf = check_eattr_leaf,
 	.check_dentry = check_dentry,
 	.check_eattr_entry = NULL,
+	.check_hash_tbl = check_hash_tbl,
+	.repair_leaf = pass2_repair_leaf,
 };
 
 /* Check system directory inode                                           */
@@ -695,7 +1598,7 @@ struct metawalk_fxns pass2_fxns = {
 static int check_system_dir(struct gfs2_inode *sysinode, const char *dirname,
 		     int builder(struct gfs2_sbd *sdp))
 {
-	uint64_t iblock = 0;
+	uint64_t iblock = 0, cur_blks;
 	struct dir_status ds = {0};
 	char *filename;
 	int filename_len;
@@ -710,12 +1613,15 @@ static int check_system_dir(struct gfs2_inode *sysinode, const char *dirname,
 	}
 	pass2_fxns.private = (void *) &ds;
 	if (ds.q == gfs2_bad_block) {
+		cur_blks = sysinode->i_di.di_blocks;
 		/* First check that the directory's metatree is valid */
 		error = check_metatree(sysinode, &pass2_fxns);
 		if (error < 0) {
 			stack;
 			return error;
 		}
+		if (sysinode->i_di.di_blocks != cur_blks)
+			reprocess_inode(sysinode, _("System inode"));
 	}
 	error = check_dir(sysinode->i_sbd, iblock, &pass2_fxns);
 	if (skip_this_pass || fsck_abort) /* if asked to skip the rest */
@@ -735,8 +1641,7 @@ static int check_system_dir(struct gfs2_inode *sysinode, const char *dirname,
 	if (!ds.dotdir) {
 		log_err( _("No '.' entry found for %s directory.\n"), dirname);
 		if (query( _("Is it okay to add '.' entry? (y/n) "))) {
-			uint64_t cur_blks = sysinode->i_di.di_blocks;
-
+			cur_blks = sysinode->i_di.di_blocks;
 			sprintf(tmp_name, ".");
 			filename_len = strlen(tmp_name); /* no trailing NULL */
 			if (!(filename = malloc(sizeof(char) * filename_len))) {
@@ -748,15 +1653,19 @@ static int check_system_dir(struct gfs2_inode *sysinode, const char *dirname,
 				    filename_len))) {
 				log_err( _("Unable to zero name string\n"));
 				stack;
+				free(filename);
 				return -1;
 			}
 			memcpy(filename, tmp_name, filename_len);
 			log_warn( _("Adding '.' entry\n"));
 			error = dir_add(sysinode, filename, filename_len,
-					&(sysinode->i_di.di_num), DT_DIR);
+					&(sysinode->i_di.di_num),
+					(sysinode->i_sbd->gfs1 ?
+					 GFS_FILE_DIR : DT_DIR));
 			if (error) {
 				log_err(_("Error adding directory %s: %s\n"),
 				        filename, strerror(errno));
+				free(filename);
 				return -errno;
 			}
 			if (cur_blks != sysinode->i_di.di_blocks)
@@ -799,8 +1708,11 @@ static int check_system_dir(struct gfs2_inode *sysinode, const char *dirname,
  */
 static inline int is_system_dir(struct gfs2_sbd *sdp, uint64_t block)
 {
-	if (block == sdp->md.rooti->i_di.di_num.no_addr ||
-	    block == sdp->md.jiinode->i_di.di_num.no_addr ||
+	if (block == sdp->md.rooti->i_di.di_num.no_addr)
+		return TRUE;
+	if (sdp->gfs1)
+		return FALSE;
+	if (block == sdp->md.jiinode->i_di.di_num.no_addr ||
 	    block == sdp->md.pinode->i_di.di_num.no_addr ||
 	    block == sdp->master_dir->i_di.di_num.no_addr)
 		return TRUE;
@@ -820,7 +1732,7 @@ static inline int is_system_dir(struct gfs2_sbd *sdp, uint64_t block)
  */
 int pass2(struct gfs2_sbd *sdp)
 {
-	uint64_t dirblk;
+	uint64_t dirblk, cur_blks;
 	uint8_t q;
 	struct dir_status ds = {0};
 	struct gfs2_inode *ip;
@@ -830,19 +1742,22 @@ int pass2(struct gfs2_sbd *sdp)
 	int error = 0;
 
 	/* Check all the system directory inodes. */
-	if (check_system_dir(sdp->md.jiinode, "jindex", build_jindex)) {
+	if (!sdp->gfs1 &&
+	    check_system_dir(sdp->md.jiinode, "jindex", build_jindex)) {
 		stack;
 		return FSCK_ERROR;
 	}
 	if (skip_this_pass || fsck_abort) /* if asked to skip the rest */
 		return FSCK_OK;
-	if (check_system_dir(sdp->md.pinode, "per_node", build_per_node)) {
+	if (!sdp->gfs1 &&
+	    check_system_dir(sdp->md.pinode, "per_node", build_per_node)) {
 		stack;
 		return FSCK_ERROR;
 	}
 	if (skip_this_pass || fsck_abort) /* if asked to skip the rest */
 		return FSCK_OK;
-	if (check_system_dir(sdp->master_dir, "master", build_master)) {
+	if (!sdp->gfs1 &&
+	    check_system_dir(sdp->master_dir, "master", build_master)) {
 		stack;
 		return FSCK_ERROR;
 	}
@@ -852,6 +1767,8 @@ int pass2(struct gfs2_sbd *sdp)
 		stack;
 		return FSCK_ERROR;
 	}
+	if (skip_this_pass || fsck_abort) /* if asked to skip the rest */
+		return FSCK_OK;
 	log_info( _("Checking directory inodes.\n"));
 	/* Grab each directory inode, and run checks on it */
 	for (dirblk = 0; dirblk < last_fs_block; dirblk++) {
@@ -868,8 +1785,16 @@ int pass2(struct gfs2_sbd *sdp)
 		if (q != gfs2_inode_dir)
 			continue;
 
-		log_debug( _("Checking directory inode at block %"PRIu64" (0x%"
-				  PRIx64 ")\n"), dirblk, dirblk);
+		/* If we created lost+found, its links should have been
+		   properly adjusted, so don't check it. */
+		if (lf_was_created &&
+		    (dirblk == lf_dip->i_di.di_num.no_addr)) {
+			log_debug(_("Pass2 skipping the new lost+found.\n"));
+			continue;
+		}
+
+		log_debug( _("Checking directory inode at block %llu (0x%llx)\n"),
+			  (unsigned long long)dirblk, (unsigned long long)dirblk);
 
 		memset(&ds, 0, sizeof(ds));
 		pass2_fxns.private = (void *) &ds;
@@ -877,12 +1802,15 @@ int pass2(struct gfs2_sbd *sdp)
 			/* First check that the directory's metatree
 			 * is valid */
 			ip = fsck_load_inode(sdp, dirblk);
+			cur_blks = ip->i_di.di_blocks;
 			error = check_metatree(ip, &pass2_fxns);
 			fsck_inode_put(&ip);
 			if (error < 0) {
 				stack;
 				return error;
 			}
+			if (ip->i_di.di_blocks != cur_blks)
+				reprocess_inode(ip, "current");
 		}
 		error = check_dir(sdp, dirblk, &pass2_fxns);
 		if (skip_this_pass || fsck_abort) /* if asked to skip the rest */
@@ -931,17 +1859,16 @@ int pass2(struct gfs2_sbd *sdp)
 			/* Can't use fsck_blockmap_set here because we don't
 			   have an inode in memory. */
 			gfs2_blockmap_set(bl, dirblk, gfs2_inode_invalid);
-			check_n_fix_bitmap(sdp, dirblk, gfs2_inode_invalid);
+			check_n_fix_bitmap(sdp, dirblk, 0, gfs2_inode_invalid);
 		}
 		ip = fsck_load_inode(sdp, dirblk);
 		if (!ds.dotdir) {
 			log_err(_("No '.' entry found for directory inode at "
-				  "block %"PRIu64" (0x%" PRIx64 ")\n"),
-				dirblk, dirblk);
+				  "block %llu (0x%llx)\n"),
+				(unsigned long long)dirblk,
+				(unsigned long long)dirblk);
 
 			if (query( _("Is it okay to add '.' entry? (y/n) "))) {
-				uint64_t cur_blks;
-
 				sprintf(tmp_name, ".");
 				filename_len = strlen(tmp_name); /* no trailing
 								    NULL */
@@ -961,7 +1888,9 @@ int pass2(struct gfs2_sbd *sdp)
 
 				cur_blks = ip->i_di.di_blocks;
 				error = dir_add(ip, filename, filename_len,
-						&(ip->i_di.di_num), DT_DIR);
+						&(ip->i_di.di_num),
+						(sdp->gfs1 ? GFS_FILE_DIR :
+						 DT_DIR));
 				if (error) {
 					log_err(_("Error adding directory %s: %s\n"),
 					        filename, strerror(errno));
@@ -1002,15 +1931,5 @@ int pass2(struct gfs2_sbd *sdp)
 		}
 		fsck_inode_put(&ip); /* does a gfs2_dinode_out, brelse */
 	}
-	/* Now that we've deleted the inodes marked "bad" we can safely
-	   get rid of the duplicate block list.  If we do it any sooner,
-	   we won't discover that a given block is a duplicate and avoid
-	   deleting it from both inodes referencing it. Note: The other
-	   returns from this function are premature exits of the program
-	   and gfs2_block_list_destroy should get rid of the list for us. */
-	gfs2_dup_free();
 	return FSCK_OK;
 }
-
-
-
diff --git a/gfs2/fsck/pass3.c b/gfs2/fsck/pass3.c
index f8727d3..6a6305a 100644
--- a/gfs2/fsck/pass3.c
+++ b/gfs2/fsck/pass3.c
@@ -1,23 +1,9 @@
-/******************************************************************************
-*******************************************************************************
-**
-**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
-**  Copyright (C) 2004-2005 Red Hat, Inc.  All rights reserved.
-**
-**  This copyrighted material is made available to anyone wishing to use,
-**  modify, copy, or redistribute it subject to the terms and conditions
-**  of the GNU General Public License v.2.
-**
-*******************************************************************************
-******************************************************************************/
-
 #include <stdio.h>
 #include <stdlib.h>
 #include <inttypes.h>
 #include <string.h>
 #include <dirent.h>
 #include <libintl.h>
-#include <errno.h>
 #define _(String) gettext(String)
 
 #include "libgfs2.h"
@@ -66,7 +52,8 @@ static int attach_dotdot_to(struct gfs2_sbd *sdp, uint64_t newdotdot,
 	else
 		decr_link_count(olddotdot, block, _("old \"..\""));
 	cur_blks = ip->i_di.di_blocks;
-	err = dir_add(ip, filename, filename_len, &pip->i_di.di_num, DT_DIR);
+	err = dir_add(ip, filename, filename_len, &pip->i_di.di_num,
+		      (sdp->gfs1 ? GFS_FILE_DIR : DT_DIR));
 	if (err) {
 		log_err(_("Error adding directory %s: %s\n"),
 		        filename, strerror(errno));
@@ -211,10 +198,38 @@ int pass3(struct gfs2_sbd *sdp)
 		log_info( _("Marking root inode connected\n"));
 		di->checked = 1;
 	}
-	di = dirtree_find(sdp->master_dir->i_di.di_num.no_addr);
-	if (di) {
-		log_info( _("Marking master directory inode connected\n"));
-		di->checked = 1;
+	if (sdp->gfs1) {
+		di = dirtree_find(sdp->md.statfs->i_di.di_num.no_addr);
+		if (di) {
+			log_info( _("Marking GFS1 statfs file inode "
+				    "connected\n"));
+			di->checked = 1;
+		}
+		di = dirtree_find(sdp->md.jiinode->i_di.di_num.no_addr);
+		if (di) {
+			log_info( _("Marking GFS1 jindex file inode "
+				    "connected\n"));
+			di->checked = 1;
+		}
+		di = dirtree_find(sdp->md.riinode->i_di.di_num.no_addr);
+		if (di) {
+			log_info( _("Marking GFS1 rindex file inode "
+				    "connected\n"));
+			di->checked = 1;
+		}
+		di = dirtree_find(sdp->md.qinode->i_di.di_num.no_addr);
+		if (di) {
+			log_info( _("Marking GFS1 quota file inode "
+				    "connected\n"));
+			di->checked = 1;
+		}
+	} else {
+		di = dirtree_find(sdp->master_dir->i_di.di_num.no_addr);
+		if (di) {
+			log_info( _("Marking master directory inode "
+				    "connected\n"));
+			di->checked = 1;
+		}
 	}
 
 	/* Go through the directory list, working up through the parents
@@ -258,7 +273,7 @@ int pass3(struct gfs2_sbd *sdp)
 					gfs2_blockmap_set(bl, di->dinode.no_addr,
 							  gfs2_block_free);
 					check_n_fix_bitmap(sdp, di->dinode.no_addr,
-							   gfs2_block_free);
+							   0, gfs2_block_free);
 					break;
 				} else
 					log_err( _("Unlinked directory with bad block remains\n"));
@@ -282,7 +297,7 @@ int pass3(struct gfs2_sbd *sdp)
 				   because we don't have ip */
 				gfs2_blockmap_set(bl, di->dinode.no_addr,
 						  gfs2_block_free);
-				check_n_fix_bitmap(sdp, di->dinode.no_addr,
+				check_n_fix_bitmap(sdp, di->dinode.no_addr, 0,
 						   gfs2_block_free);
 				log_err( _("The block was cleared\n"));
 				break;
diff --git a/gfs2/fsck/pass4.c b/gfs2/fsck/pass4.c
index 502dd24..4605671 100644
--- a/gfs2/fsck/pass4.c
+++ b/gfs2/fsck/pass4.c
@@ -1,16 +1,3 @@
-/******************************************************************************
-*******************************************************************************
-**
-**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
-**  Copyright (C) 2004-2005 Red Hat, Inc.  All rights reserved.
-**
-**  This copyrighted material is made available to anyone wishing to use,
-**  modify, copy, or redistribute it subject to the terms and conditions
-**  of the GNU General Public License v.2.
-**
-*******************************************************************************
-******************************************************************************/
-
 #include <inttypes.h>
 #include <stdlib.h>
 #include <stdio.h>
@@ -69,6 +56,13 @@ static int scan_inode_list(struct gfs2_sbd *sdp) {
 			log_crit( _("osi_tree broken in scan_info_list!!\n"));
 			exit(FSCK_ERROR);
 		}
+		/* Don't check reference counts on the special gfs files */
+		if (sdp->gfs1 &&
+		    ((ii->di_num.no_addr == sdp->md.riinode->i_di.di_num.no_addr) ||
+		     (ii->di_num.no_addr == sdp->md.jiinode->i_di.di_num.no_addr) ||
+		     (ii->di_num.no_addr == sdp->md.qinode->i_di.di_num.no_addr) ||
+		     (ii->di_num.no_addr == sdp->md.statfs->i_di.di_num.no_addr)))
+			continue;
 		if (ii->counted_links == 0) {
 			log_err( _("Found unlinked inode at %llu (0x%llx)\n"),
 				(unsigned long long)ii->di_num.no_addr,
diff --git a/gfs2/fsck/pass5.c b/gfs2/fsck/pass5.c
index a811cdb..7e85b69 100644
--- a/gfs2/fsck/pass5.c
+++ b/gfs2/fsck/pass5.c
@@ -1,16 +1,3 @@
-/******************************************************************************
-*******************************************************************************
-**
-**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
-**  Copyright (C) 2004-2005 Red Hat, Inc.  All rights reserved.
-**
-**  This copyrighted material is made available to anyone wishing to use,
-**  modify, copy, or redistribute it subject to the terms and conditions
-**  of the GNU General Public License v.2.
-**
-*******************************************************************************
-******************************************************************************/
-
 #include <stdio.h>
 #include <string.h>
 #include <inttypes.h>
@@ -22,7 +9,51 @@
 #include "fsck.h"
 #include "util.h"
 
-static int convert_mark(uint8_t q, uint32_t *count)
+static int gfs1_convert_mark(uint8_t q, uint32_t *count)
+{
+	switch(q) {
+
+	case gfs2_meta_inval:
+	case gfs2_inode_invalid:
+		/* Convert invalid metadata to free blocks */
+	case gfs2_block_free:
+		count[0]++;
+		return GFS2_BLKST_FREE;
+
+	case gfs2_block_used:
+		count[2]++;
+		return GFS2_BLKST_USED;
+
+	case gfs2_inode_dir:
+	case gfs2_inode_file:
+	case gfs2_inode_lnk:
+	case gfs2_inode_device:
+	case gfs2_inode_fifo:
+	case gfs2_inode_sock:
+		count[1]++;
+		return GFS2_BLKST_DINODE;
+
+	case gfs2_indir_blk:
+	case gfs2_leaf_blk:
+	/*case gfs2_meta_rgrp:*/
+	case gfs2_jdata: /* gfs1 jdata blocks count as "metadata" and gfs1
+			    metadata is marked the same as gfs2 inode in the
+			    bitmap. */
+	case gfs2_meta_eattr:
+		count[3]++;
+		return GFS2_BLKST_DINODE;
+
+	case gfs2_freemeta:
+		count[4]++;
+		return GFS2_BLKST_UNLINKED;
+
+	default:
+		log_err( _("Invalid block type %d found\n"), q);
+	}
+	return -1;
+}
+
+static int gfs2_convert_mark(uint8_t q, uint32_t *count)
 {
 	switch(q) {
 
@@ -41,6 +72,9 @@ static int convert_mark(uint8_t q, uint32_t *count)
 	case gfs2_inode_file:
 	case gfs2_inode_lnk:
 	case gfs2_inode_device:
+	case gfs2_jdata: /* gfs1 jdata blocks count as "metadata" and gfs1
+			    metadata is marked the same as gfs2 inode in the
+			    bitmap. */
 	case gfs2_inode_fifo:
 	case gfs2_inode_sock:
 		count[1]++;
@@ -52,13 +86,17 @@ static int convert_mark(uint8_t q, uint32_t *count)
 		count[2]++;
 		return GFS2_BLKST_USED;
 
+	case gfs2_freemeta:
+		log_err( _("Invalid freemeta type %d found\n"), q);
+		count[4]++;
+		return -1;
+
 	default:
 		log_err( _("Invalid block type %d found\n"), q);
 	}
 	return -1;
 }
 
-
 static int check_block_status(struct gfs2_sbd *sdp, char *buffer,
 			      unsigned int buflen, uint64_t *rg_block,
 			      uint64_t rg_data, uint32_t *count)
@@ -84,7 +122,10 @@ static int check_block_status(struct gfs2_sbd *sdp, char *buffer,
 
 		q = block_type(block);
 
-		block_status = convert_mark(q, count);
+		if (sdp->gfs1)
+			block_status = gfs1_convert_mark(q, count);
+		else
+			block_status = gfs2_convert_mark(q, count);
 
 		if (block_status < 0) {
 			log_err( _("Invalid status for block %llu (0x%llx).\n"),
@@ -117,9 +158,10 @@ static int check_block_status(struct gfs2_sbd *sdp, char *buffer,
 						(unsigned long long)block,
 						(unsigned long long)block);
 			} else {
-				log_info( _("Unlinked block found at block %"
-					    PRIu64" (0x%" PRIx64 "), left "
-					    "unchanged.\n"), block, block);
+				log_info( _("Unlinked block found at block %llu"
+					    " (0x%llx), left unchanged.\n"),
+					(unsigned long long)block,
+					(unsigned long long)block);
 			}
 		} else if (rg_status != block_status) {
 			const char *blockstatus[] = {"Free", "Data",
@@ -143,8 +185,9 @@ static int check_block_status(struct gfs2_sbd *sdp, char *buffer,
 				else
 					log_err( _("Fixed.\n"));
 			} else
-				log_err( _("Bitmap at block %"PRIu64" (0x%" PRIx64
-						") left inconsistent\n"), block, block);
+				log_err( _("Bitmap at block %llu (0x%llx) left inconsistent\n"),
+					(unsigned long long)block,
+					(unsigned long long)block);
 		}
 		(*rg_block)++;
 		bit += GFS2_BIT_SIZE;
@@ -164,6 +207,7 @@ static void update_rgrp(struct gfs2_sbd *sdp, struct rgrp_tree *rgp,
 	struct gfs2_bitmap *bits;
 	uint64_t rg_block = 0;
 	int update = 0;
+	struct gfs_rgrp *gfs1rg = (struct gfs_rgrp *)&rgp->rg;
 
 	for(i = 0; i < rgp->ri.ri_length; i++) {
 		bits = &rgp->bits[i];
@@ -196,7 +240,25 @@ static void update_rgrp(struct gfs2_sbd *sdp, struct rgrp_tree *rgp,
 		rgp->rg.rg_dinodes = count[1];
 		update = 1;
 	}
-	if ((rgp->ri.ri_data - count[0] - count[1]) != count[2]) {
+	if (sdp->gfs1 && gfs1rg->rg_usedmeta != count[3]) {
+		log_err( _("RG #%llu (0x%llx) Used metadata count "
+			   "inconsistent: is %u should be %u\n"),
+			 (unsigned long long)rgp->ri.ri_addr,
+			 (unsigned long long)rgp->ri.ri_addr,
+			 gfs1rg->rg_usedmeta, count[3]);
+		gfs1rg->rg_usedmeta = count[3];
+		update = 1;
+	}
+	if (sdp->gfs1 && gfs1rg->rg_freemeta != count[4]) {
+		log_err( _("RG #%llu (0x%llx) Free metadata count "
+			   "inconsistent: is %u should be %u\n"),
+			 (unsigned long long)rgp->ri.ri_addr,
+			 (unsigned long long)rgp->ri.ri_addr,
+			 gfs1rg->rg_freemeta, count[4]);
+		gfs1rg->rg_freemeta = count[4];
+		update = 1;
+	}
+	if (!sdp->gfs1 && (rgp->ri.ri_data - count[0] - count[1]) != count[2]) {
 		/* FIXME not sure how to handle this case ATM - it
 		 * means that the total number of blocks we've counted
 		 * exceeds the blocks in the rg */
@@ -207,7 +269,10 @@ static void update_rgrp(struct gfs2_sbd *sdp, struct rgrp_tree *rgp,
 		if (query( _("Update resource group counts? (y/n) "))) {
 			log_warn( _("Resource group counts updated\n"));
 			/* write out the rgrp */
-			gfs2_rgrp_out(&rgp->rg, rgp->bh[0]);
+			if (sdp->gfs1)
+				gfs_rgrp_out(gfs1rg, rgp->bh[0]);
+			else
+				gfs2_rgrp_out(&rgp->rg, rgp->bh[0]);
 		} else
 			log_err( _("Resource group counts left inconsistent\n"));
 	}
@@ -223,7 +288,7 @@ int pass5(struct gfs2_sbd *sdp)
 {
 	struct osi_node *n, *next = NULL;
 	struct rgrp_tree *rgp = NULL;
-	uint32_t count[3];
+	uint32_t count[5];
 	uint64_t rg_count = 0;
 
 	/* Reconcile RG bitmaps with fsck bitmap */
@@ -231,7 +296,7 @@ int pass5(struct gfs2_sbd *sdp)
 		next = osi_next(n);
 		if (skip_this_pass || fsck_abort) /* if asked to skip the rest */
 			return FSCK_OK;
-		log_info( _("Verifying Resource Group #%" PRIu64 "\n"), rg_count);
+		log_info( _("Verifying Resource Group #%llu\n"), (unsigned long long)rg_count);
 		memset(count, 0, sizeof(count));
 		rgp = (struct rgrp_tree *)n;
 
diff --git a/gfs2/fsck/recovery.c b/gfs2/fsck/recovery.c
new file mode 100644
index 0000000..74f896e
--- /dev/null
+++ b/gfs2/fsck/recovery.c
@@ -0,0 +1,244 @@
+/*
+ * NOTE:
+ *
+ * This code was pilfered from the gfs2 kernel and adapted to userland.
+ * If you change this part, you should evaluate whether the upstream kernel
+ * version of recovery.c should be changed as well.  Likewise, if the
+ * upstream version changes, this part should be kept in sync.
+ * 
+ */
+
+#include <errno.h>
+#include <string.h>
+#include "libgfs2.h"
+
+void gfs2_replay_incr_blk(struct gfs2_inode *ip, unsigned int *blk)
+{
+	uint32_t jd_blocks = ip->i_di.di_size / ip->i_sbd->sd_sb.sb_bsize;
+
+        if (++*blk == jd_blocks)
+                *blk = 0;
+}
+
+int gfs2_replay_read_block(struct gfs2_inode *ip, unsigned int blk,
+			   struct gfs2_buffer_head **bh)
+{
+	int new = 0;
+	uint64_t dblock;
+	uint32_t extlen;
+
+	block_map(ip, blk, &new, &dblock, &extlen, FALSE);
+	if (!dblock)
+		return -EIO;
+
+	*bh = bread(ip->i_sbd, dblock);
+	return 0;
+}
+
+/**
+ * get_log_header - read the log header for a given segment
+ * @ip: the journal incore inode
+ * @blk: the block to look at
+ * @lh: the log header to return
+ *
+ * Read the log header for a given segement in a given journal.  Do a few
+ * sanity checks on it.
+ *
+ * Returns: 0 on success,
+ *          1 if the header was invalid or incomplete,
+ *          errno on error
+ */
+
+int get_log_header(struct gfs2_inode *ip, unsigned int blk,
+		   struct gfs2_log_header *head)
+{
+	struct gfs2_buffer_head *bh;
+	struct gfs2_log_header lh, *tmp;
+	uint32_t hash, saved_hash;
+	int error;
+
+	error = gfs2_replay_read_block(ip, blk, &bh);
+	if (error)
+		return error;
+
+	tmp = (struct gfs2_log_header *)bh->b_data;
+	saved_hash = tmp->lh_hash;
+	tmp->lh_hash = 0;
+	hash = gfs2_disk_hash(bh->b_data, sizeof(struct gfs2_log_header));
+	tmp->lh_hash = saved_hash;
+	gfs2_log_header_in(&lh, bh);
+	brelse(bh);
+
+	if (error || lh.lh_blkno != blk || lh.lh_hash != hash)
+		return 1;
+
+	*head = lh;
+
+	return 0;
+}
+
+/**
+ * find_good_lh - find a good log header
+ * @ip: the journal incore inode
+ * @blk: the segment to start searching from
+ * @lh: the log header to fill in
+ * @forward: if true search forward in the log, else search backward
+ *
+ * Call get_log_header() to get a log header for a segment, but if the
+ * segment is bad, either scan forward or backward until we find a good one.
+ *
+ * Returns: errno
+ */
+
+int find_good_lh(struct gfs2_inode *ip, unsigned int *blk,
+		 struct gfs2_log_header *head)
+{
+	unsigned int orig_blk = *blk;
+	int error;
+	uint32_t jd_blocks = ip->i_di.di_size / ip->i_sbd->sd_sb.sb_bsize;
+
+	for (;;) {
+		error = get_log_header(ip, *blk, head);
+		if (error <= 0)
+			return error;
+
+		if (++*blk == jd_blocks)
+			*blk = 0;
+
+		if (*blk == orig_blk)
+			return -EIO;
+	}
+}
+
+/**
+ * jhead_scan - make sure we've found the head of the log
+ * @jd: the journal
+ * @head: this is filled in with the log descriptor of the head
+ *
+ * At this point, seg and lh should be either the head of the log or just
+ * before.  Scan forward until we find the head.
+ *
+ * Returns: errno
+ */
+
+int jhead_scan(struct gfs2_inode *ip, struct gfs2_log_header *head)
+{
+	unsigned int blk = head->lh_blkno;
+	uint32_t jd_blocks = ip->i_di.di_size / ip->i_sbd->sd_sb.sb_bsize;
+	struct gfs2_log_header lh;
+	int error;
+
+	for (;;) {
+		if (++blk == jd_blocks)
+			blk = 0;
+
+		error = get_log_header(ip, blk, &lh);
+		if (error < 0)
+			return error;
+		if (error == 1)
+			continue;
+
+		if (lh.lh_sequence == head->lh_sequence)
+			return -EIO;
+		if (lh.lh_sequence < head->lh_sequence)
+			break;
+
+		*head = lh;
+	}
+
+	return 0;
+}
+
+/**
+ * gfs2_find_jhead - find the head of a log
+ * @jd: the journal
+ * @head: the log descriptor for the head of the log is returned here
+ *
+ * Do a binary search of a journal and find the valid log entry with the
+ * highest sequence number.  (i.e. the log head)
+ *
+ * Returns: errno
+ */
+
+int gfs2_find_jhead(struct gfs2_inode *ip, struct gfs2_log_header *head)
+{
+	struct gfs2_log_header lh_1, lh_m;
+	uint32_t blk_1, blk_2, blk_m;
+	uint32_t jd_blocks = ip->i_di.di_size / ip->i_sbd->sd_sb.sb_bsize;
+	int error;
+
+	blk_1 = 0;
+	blk_2 = jd_blocks - 1;
+
+	for (;;) {
+		blk_m = (blk_1 + blk_2) / 2;
+
+		error = find_good_lh(ip, &blk_1, &lh_1);
+		if (error)
+			return error;
+
+		error = find_good_lh(ip, &blk_m, &lh_m);
+		if (error)
+			return error;
+
+		if (blk_1 == blk_m || blk_m == blk_2)
+			break;
+
+		if (lh_1.lh_sequence <= lh_m.lh_sequence)
+			blk_1 = blk_m;
+		else
+			blk_2 = blk_m;
+	}
+
+	error = jhead_scan(ip, &lh_1);
+	if (error)
+		return error;
+
+	*head = lh_1;
+
+	return error;
+}
+
+/**
+ * clean_journal - mark a dirty journal as being clean
+ * @sdp: the filesystem
+ * @jd: the journal
+ * @head: the head journal to start from
+ *
+ * Returns: errno
+ */
+
+int clean_journal(struct gfs2_inode *ip, struct gfs2_log_header *head)
+{
+	unsigned int lblock;
+	struct gfs2_log_header *lh;
+	uint32_t hash, extlen;
+	struct gfs2_buffer_head *bh;
+	int new = 0;
+	uint64_t dblock;
+
+	lblock = head->lh_blkno;
+	gfs2_replay_incr_blk(ip, &lblock);
+	block_map(ip, lblock, &new, &dblock, &extlen, 0);
+	if (!dblock)
+		return -EIO;
+
+	bh = bread(ip->i_sbd, 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));
+	lh->lh_header.mh_magic = cpu_to_be32(GFS2_MAGIC);
+	lh->lh_header.mh_type = cpu_to_be32(GFS2_METATYPE_LH);
+	lh->lh_header.mh_format = cpu_to_be32(GFS2_FORMAT_LH);
+	lh->lh_sequence = cpu_to_be64(head->lh_sequence + 1);
+	lh->lh_flags = cpu_to_be32(GFS2_LOG_HEAD_UNMOUNT);
+	lh->lh_blkno = cpu_to_be32(lblock);
+	hash = gfs2_disk_hash((const char *)lh, sizeof(struct gfs2_log_header));
+	lh->lh_hash = cpu_to_be32(hash);
+	bmodified(bh);
+	brelse(bh);
+
+	return 0;
+}
+
diff --git a/gfs2/fsck/rgrepair.c b/gfs2/fsck/rgrepair.c
index fb4d092..908c3fa 100644
--- a/gfs2/fsck/rgrepair.c
+++ b/gfs2/fsck/rgrepair.c
@@ -1,16 +1,3 @@
-/******************************************************************************
-*******************************************************************************
-**
-**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
-**  Copyright (C) 2007 Red Hat, Inc.  All rights reserved.
-**
-**  This copyrighted material is made available to anyone wishing to use,
-**  modify, copy, or redistribute it subject to the terms and conditions
-**  of the GNU General Public License v.2.
-**
-*******************************************************************************
-******************************************************************************/
-
 #include <unistd.h>
 #include <inttypes.h>
 #include <stdio.h>
@@ -28,6 +15,9 @@
 int rindex_modified = FALSE;
 struct special_blocks false_rgrps;
 
+#define BAD_RG_PERCENT_TOLERANCE 11
+#define AWAY_FROM_BITMAPS 0x1000
+
 #define ri_equal(ondisk, expected, field) (ondisk.field == expected.field)
 
 #define ri_compare(rg, ondisk, expected, field, fmt)	\
@@ -60,7 +50,7 @@ static void find_journaled_rgs(struct gfs2_sbd *sdp)
 
 	osi_list_init(&false_rgrps.list);
 	for (j = 0; j < sdp->md.journals; j++) {
-		log_debug( _("Checking for RGs in journal%d.\n"), j);
+		log_debug( _("Checking for rgrps in journal%d.\n"), j);
 		ip = sdp->md.journal[j];
 		jblocks = ip->i_di.di_size / sdp->sd_sb.sb_bsize;
 		for (b = 0; b < jblocks; b++) {
@@ -69,7 +59,7 @@ static void find_journaled_rgs(struct gfs2_sbd *sdp)
 				break;
 			bh = bread(sdp, dblock);
 			if (!gfs2_check_meta(bh, GFS2_METATYPE_RG)) {
-				log_debug( _("False RG found at block "
+				log_debug( _("False rgrp found at block "
 					  "0x%" PRIx64 "\n"), dblock);
 				gfs2_special_set(&false_rgrps, dblock);
 			}
@@ -86,64 +76,37 @@ static int is_false_rg(uint64_t block)
 }
 
 /*
- * gfs2_rindex_rebuild - rebuild a corrupt Resource Group (RG) index manually
- *                        where trust_lvl == distrust
- *
- * If this routine is called, it means we have RGs in odd/unexpected places,
- * and there is a corrupt RG or RG index entry.  It also means we can't trust
- * the RG index to be sane, and the RGs don't agree with how mkfs would have
- * built them by default.  So we have no choice but to go through and count 
- * them by hand.  We've tried twice to recover the RGs and RG index, and
- * failed, so this is our last chance to remedy the situation.
- *
- * This routine tries to minimize performance impact by:
- * 1. Skipping through the filesystem at known increments when possible.
- * 2. Shuffle through every block when RGs are not found at the predicted
- *    locations.
- *
- * Note: A GFS2 filesystem differs from a GFS1 file system in that there will
- * only be ONE chunk (i.e. no artificial subdevices on either size of the
- * journals).  The journals and even the rindex are kept as part of the file
- * system, so we need to rebuild that information by hand.  Also, with GFS1,
- * the different chunks ("subdevices") could have different RG sizes, which
- * made for quite a mess when trying to recover RGs.  GFS2 always uses the 
- * same RG size determined by the original mkfs, so recovery is easier.
+ * find_shortest_rgdist - hunt and peck for the shortest distance between RGs.
  *
+ * Sample several of them because an RG that's been blasted may
+ * look like twice the distance.  If we can find 6 of them, that
+ * should be enough to figure out the correct layout.
+ * This also figures out first_rg_dist since that's always different.
  */
-static int gfs2_rindex_rebuild(struct gfs2_sbd *sdp, int *num_rgs)
+static uint64_t find_shortest_rgdist(struct gfs2_sbd *sdp,
+				     uint64_t *initial_first_rg_dist,
+				     uint64_t *first_rg_dist)
 {
-	struct osi_node *n, *next = NULL;
+	uint64_t blk, block_of_last_rg, shortest_dist_btwn_rgs;
 	struct gfs2_buffer_head *bh;
-	uint64_t shortest_dist_btwn_rgs;
-	uint64_t blk, block_of_last_rg;
-	uint64_t fwd_block, block_bump;
-	uint64_t first_rg_dist, initial_first_rg_dist;
-	struct rgrp_tree *calc_rgd, *prev_rgd;
-	int number_of_rgs, rgi;
+	int number_of_rgs = 0;
 	struct gfs2_rindex buf, tmpndx;
-	int rg_was_fnd = FALSE, corrupt_rgs = 0, bitmap_was_fnd;
 
 	/* Figure out if there are any RG-looking blocks in the journal we
 	   need to ignore. */
 	find_journaled_rgs(sdp);
-	number_of_rgs = 0;
-	initial_first_rg_dist = first_rg_dist = sdp->sb_addr + 1;
+
+	*initial_first_rg_dist = *first_rg_dist = sdp->sb_addr + 1;
 	block_of_last_rg = sdp->sb_addr + 1;
-	/* ------------------------------------------------------------- */
-	/* First, hunt and peck for the shortest distance between RGs.   */
-	/* Sample several of them because an RG that's been blasted may  */
-	/* look like twice the distance.  If we can find 6 of them, that */
-	/* should be enough to figure out the correct layout.            */
-	/* ------------------------------------------------------------- */
 	shortest_dist_btwn_rgs = sdp->device.length;
+
 	for (blk = sdp->sb_addr + 1;
-	     blk < sdp->device.length && number_of_rgs < 6;
-	     blk++) {
+	     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))) &&
 		    !is_false_rg(blk)) {
-			log_debug( _("RG found at block 0x%" PRIx64 "\n"), blk);
+			log_debug( _("rgrp found at block 0x%" PRIx64 "\n"), blk);
 			if (blk > sdp->sb_addr + 1) {
 				uint64_t rgdist;
 				
@@ -160,8 +123,8 @@ static int gfs2_rindex_rebuild(struct gfs2_sbd *sdp, int *num_rgs)
 				/* from the rest because of the superblock   */
 				/* and 64K dead space.                       */
 				/* ----------------------------------------- */
-				if (first_rg_dist == initial_first_rg_dist)
-					first_rg_dist = rgdist;
+				if (*first_rg_dist == *initial_first_rg_dist)
+					*first_rg_dist = rgdist;
 				if (rgdist < shortest_dist_btwn_rgs) {
 					shortest_dist_btwn_rgs = rgdist;
 					log_debug( _("(shortest so far)\n"));
@@ -175,18 +138,15 @@ static int gfs2_rindex_rebuild(struct gfs2_sbd *sdp, int *num_rgs)
 		}
 		brelse(bh);
 	}
-	number_of_rgs = 0;
-	gfs2_special_free(&false_rgrps);
-
 	/* -------------------------------------------------------------- */
 	/* Sanity-check our first_rg_dist. If RG #2 got nuked, the        */
 	/* first_rg_dist would measure from #1 to #3, which would be bad. */
 	/* We need to take remedial measures to fix it (from the index).  */
 	/* -------------------------------------------------------------- */
-	log_debug( _("First RG distance: 0x%" PRIx64 "\n"), first_rg_dist);
-	log_debug( _("Distance between RGs: 0x%" PRIx64 "\n"),
+	log_debug( _("First rgrp distance: 0x%" PRIx64 "\n"), *first_rg_dist);
+	log_debug( _("Distance between rgrps: 0x%" PRIx64 "\n"),
 		  shortest_dist_btwn_rgs);
-	if (first_rg_dist >= shortest_dist_btwn_rgs +
+	if (*first_rg_dist >= shortest_dist_btwn_rgs +
 	    (shortest_dist_btwn_rgs / 4)) {
 		/* read in the second RG index entry for this subd. */
 		gfs2_readi(sdp->md.riinode, (char *)&buf,
@@ -194,29 +154,307 @@ static int gfs2_rindex_rebuild(struct gfs2_sbd *sdp, int *num_rgs)
 			   sizeof(struct gfs2_rindex));
 		gfs2_rindex_in(&tmpndx, (char *)&buf);
 		if (tmpndx.ri_addr > sdp->sb_addr + 1) { /* sanity check */
-			log_warn( _("RG 2 is damaged: getting dist from index: "));
-			first_rg_dist = tmpndx.ri_addr - (sdp->sb_addr + 1);
-			log_warn("0x%" PRIx64 "\n", first_rg_dist);
+			log_warn( _("rgrp 2 is damaged: getting dist from index: "));
+			*first_rg_dist = tmpndx.ri_addr - (sdp->sb_addr + 1);
+			log_warn("0x%" PRIx64 "\n", *first_rg_dist);
 		}
 		else {
-			log_warn( _("RG index 2 is damaged: extrapolating dist: "));
-			first_rg_dist = sdp->device.length -
+			log_warn( _("rgrp index 2 is damaged: extrapolating dist: "));
+			*first_rg_dist = sdp->device.length -
 				(sdp->rgrps - 1) *
 				(sdp->device.length / sdp->rgrps);
-			log_warn("0x%" PRIx64 "\n", first_rg_dist);
+			log_warn("0x%" PRIx64 "\n", *first_rg_dist);
 		}
-		log_debug( _("Adjusted first RG distance: 0x%" PRIx64 "\n"),
-			  first_rg_dist);
+		log_debug( _("Adjusted first rgrp distance: 0x%" PRIx64 "\n"),
+			  *first_rg_dist);
 	} /* if first RG distance is within tolerance */
 
+	gfs2_special_free(&false_rgrps);
+	return shortest_dist_btwn_rgs;
+}
+
+/*
+ * count_usedspace - count the used bits in a rgrp bitmap buffer
+ */
+static uint64_t count_usedspace(struct gfs2_sbd *sdp, int first,
+				struct gfs2_buffer_head *bh)
+{
+	int off, x, y, bytes_to_check;
+	uint32_t rg_used = 0;
+	unsigned int state;
+
+	/* Count up the free blocks in the bitmap */
+	if (first) {
+		if (sdp->gfs1)
+			off = sizeof(struct gfs_rgrp);
+		else
+			off = sizeof(struct gfs2_rgrp);
+	} else
+		off = sizeof(struct gfs2_meta_header);
+	bytes_to_check = sdp->bsize - off;
+	for (x = 0; x < bytes_to_check; x++) {
+		unsigned char *byte;
+
+		byte = (unsigned char *)&bh->b_data[off + x];
+		if (*byte == 0x55) {
+			rg_used += GFS2_NBBY;
+			continue;
+		}
+		if (*byte == 0x00)
+			continue;
+		for (y = 0; y < GFS2_NBBY; y++) {
+			state = (*byte >> (GFS2_BIT_SIZE * y)) & GFS2_BIT_MASK;
+			if (state == GFS2_BLKST_FREE ||
+			    state == GFS2_BLKST_UNLINKED)
+				continue;
+			rg_used++;
+		}
+	}
+	return rg_used;
+}
+
+/*
+ * find_next_rgrp_dist - find the distance to the next rgrp
+ *
+ * This function is only called if the rgrps are determined to be on uneven
+ * boundaries.  In a normal gfs2 file system, after mkfs.gfs2, all the
+ * rgrps but the first and second one will be the same distance from the
+ * previous rgrp.  (The first rgrp will predictably be after the superblock
+ * and the second one will be adjusted based on the number 64KB skipped
+ * at the start of the file system.)  The only way we can deviate from that
+ * pattern is if the user did gfs_grow on a gfs1 file system, then converted
+ * it to gfs2 using gfs2_convert.
+ *
+ * This function finds the distance to the next rgrp for these cases.
+ */
+static uint64_t find_next_rgrp_dist(struct gfs2_sbd *sdp, uint64_t blk,
+				    struct rgrp_tree *prevrgd)
+{
+	struct osi_node *n, *next = NULL;
+	uint64_t rgrp_dist = 0, used_blocks, block, next_block, twogigs;
+	struct rgrp_tree *rgd = NULL, *next_rgd;
+	struct gfs2_buffer_head *bh;
+	struct gfs2_meta_header mh;
+	int first, length, b, found, mega_in_blocks;
+	uint32_t free_blocks = 0;
+
+	for (n = osi_first(&sdp->rgtree); n; n = next) {
+		next = osi_next(n);
+		rgd = (struct rgrp_tree *)n;
+		if (rgd->ri.ri_addr == blk)
+			break;
+	}
+	if (rgd && n && osi_next(n) && rgd->ri.ri_addr == blk) {
+		n = osi_next(n);
+		next_rgd = (struct rgrp_tree *)n;
+		rgrp_dist = next_rgd->ri.ri_addr - rgd->ri.ri_addr;
+		return rgrp_dist;
+	}
+	mega_in_blocks = (1024 * 1024)  / sdp->bsize;
+	twogigs = 2048 * mega_in_blocks;
+	/* Unfortunately, if we fall through to here we can't trust the
+	   rindex.  So we have to analyze the current rgrp to figure out
+	   the bare minimum block number where it ends. If we don't have
+	   rindex, all we know about this rgrp is what's on disk: its
+	   rg_free.  If we analyze the rgrp's bitmap and the bitmaps that
+	   follow, we can figure out how many bits are used.  If we add
+	   rg_free, we get the total number of blocks this rgrp
+	   represents.  After that should be the next rgrp, but it may
+	   skip a few blocks (hopefully no more than 4).  */
+	used_blocks = 0;
+	length = 0;
+	block = prevrgd->ri.ri_addr;
+	first = 1;
+	found = 0;
+	while (1) {
+		if (block >= sdp->device.length)
+			break;
+		if (block >= prevrgd->ri.ri_addr + twogigs)
+			break;
+		bh = bread(sdp, block);
+		gfs2_meta_header_in(&mh, bh);
+		if ((mh.mh_magic != GFS2_MAGIC) ||
+		    (first && mh.mh_type != GFS2_METATYPE_RG) ||
+		    (!first && mh.mh_type != GFS2_METATYPE_RB)) {
+			brelse(bh);
+			break;
+		}
+		if (first) {
+			struct gfs2_rgrp *rg;
+
+			rg = (struct gfs2_rgrp *)bh->b_data;
+			free_blocks = be32_to_cpu(rg->rg_free);
+		}
+		used_blocks += count_usedspace(sdp, first, bh);
+		first = 0;
+		block++;
+		length++;
+		brelse(bh);
+		/* Check if this distance points to an rgrp:
+		   We have to look for blocks that resemble rgrps and bitmaps.
+		   If they do, we need to count blocks used and free and see
+		   if adding that number of free blocks accounts for the
+		   next rgrp we find. Otherwise, you could have a length of
+		   6 with additional user blocks that just happen to look like
+		   bitmap blocks.  Count them all as bitmaps and you'll be
+		   hopelessly lost. */
+		rgrp_dist = used_blocks + free_blocks + length;
+		next_block = prevrgd->ri.ri_addr + rgrp_dist;
+		/* Now we account for block rounding done by mkfs.gfs2 */
+		for (b = 0; b <= length + GFS2_NBBY; b++) {
+			if (next_block >= sdp->device.length)
+				break;
+			bh = bread(sdp, next_block + b);
+			gfs2_meta_header_in(&mh, bh);
+			brelse(bh);
+			if (mh.mh_magic == GFS2_MAGIC) {
+				if (mh.mh_type == GFS2_METATYPE_RG) {
+					found = 1;
+					break;
+				}
+				/* if the first thing we find is a bitmap,
+				   there must be a damaged rgrp on the
+				   previous block. */
+				if (mh.mh_type == GFS2_METATYPE_RB) {
+					found = 1;
+					rgrp_dist--;
+					break;
+				}
+			}
+			rgrp_dist++;
+		}
+		if (found) {
+			block = next_block;
+			log_info( _("rgrp found at 0x%llx, length=%d, "
+				    "used=%llu, free=%d\n"),
+				  prevrgd->ri.ri_addr, length,
+				  (unsigned long long)used_blocks,
+				  free_blocks);
+			break;
+		}
+	}
+	return rgrp_dist;
+}
+
+/*
+ * hunt_and_peck - find the distance to the next rgrp
+ *
+ * This function is only called if the rgrps are determined to be on uneven
+ * boundaries, and also corrupt.  So we have to go out searching for one.
+ */
+static uint64_t hunt_and_peck(struct gfs2_sbd *sdp, uint64_t blk,
+			      struct rgrp_tree *prevrgd, uint64_t last_bump)
+{
+	uint64_t rgrp_dist = 0, block, twogigs, last_block, last_meg;
+	struct gfs2_buffer_head *bh;
+	struct gfs2_meta_header mh;
+	int b, mega_in_blocks;
+
+	/* Skip ahead the previous amount: we might get lucky.
+	   If we're close to the end of the device, take the rest. */
+	if (gfs2_check_range(sdp, blk + last_bump))
+		return sdp->fssize - blk;
+
+	bh = bread(sdp, blk + last_bump);
+	gfs2_meta_header_in(&mh, bh);
+	brelse(bh);
+	if (mh.mh_magic == GFS2_MAGIC && mh.mh_type == GFS2_METATYPE_RG) {
+		log_info( _("rgrp found at 0x%llx, length=%lld\n"),
+			  (unsigned long long)blk + last_bump,
+			  (unsigned long long)last_bump);
+		return last_bump;
+	}
+
+	rgrp_dist = AWAY_FROM_BITMAPS; /* Get away from any bitmaps
+					  associated with the previous rgrp */
+	block = prevrgd->ri.ri_addr + rgrp_dist;
+	/* Now we account for block rounding done by mkfs.gfs2.  A rgrp can
+	   be at most 2GB in size, so that's where we call it. We do somewhat
+	   obscure math here to avoid integer overflows. */
+	mega_in_blocks = (1024 * 1024)  / sdp->bsize;
+	twogigs = 2048 * mega_in_blocks;
+	if (block + twogigs <= sdp->fssize) {
+		last_block = twogigs;
+		last_meg = 0;
+	} else {
+		/* There won't be a rgrp in the last megabyte. */
+		last_block = sdp->fssize - block - mega_in_blocks;
+		last_meg = mega_in_blocks;
+	}
+	for (b = AWAY_FROM_BITMAPS; b < last_block; b++) {
+		bh = bread(sdp, block + b);
+		gfs2_meta_header_in(&mh, bh);
+		brelse(bh);
+		if (mh.mh_magic == GFS2_MAGIC) {
+			if (mh.mh_type == GFS2_METATYPE_RG)
+				break;
+			/* if the first thing we find is a bitmap, there must
+			   be a damaged rgrp on the previous block. */
+			if (mh.mh_type == GFS2_METATYPE_RB) {
+				rgrp_dist--;
+				break;
+			}
+		}
+		rgrp_dist++;
+	}
+	return rgrp_dist + last_meg;
+}
+
+/*
+ * gfs2_rindex_rebuild - rebuild a corrupt Resource Group (RG) index manually
+ *                        where trust_lvl == distrust
+ *
+ * If this routine is called, it means we have RGs in odd/unexpected places,
+ * and there is a corrupt RG or RG index entry.  It also means we can't trust
+ * the RG index to be sane, and the RGs don't agree with how mkfs would have
+ * built them by default.  So we have no choice but to go through and count
+ * them by hand.  We've tried twice to recover the RGs and RG index, and
+ * failed, so this is our last chance to remedy the situation.
+ *
+ * This routine tries to minimize performance impact by:
+ * 1. Skipping through the filesystem at known increments when possible.
+ * 2. Shuffle through every block when RGs are not found at the predicted
+ *    locations.
+ *
+ * Note: A GFS2 filesystem differs from a GFS1 file system in that there will
+ * only be ONE chunk (i.e. no artificial subdevices on either size of the
+ * journals).  The journals and even the rindex are kept as part of the file
+ * system, so we need to rebuild that information by hand.  Also, with GFS1,
+ * the different chunks ("subdevices") could have different RG sizes, which
+ * made for quite a mess when trying to recover RGs.  GFS2 always uses the
+ * same RG size determined by the original mkfs, so recovery is easier.
+ *
+ * If "gfs_grow" is specified the file system was most likely converted
+ * from gfs1 to gfs2 after a gfs_grow operation.  In that case, the rgrps
+ * will not be on predictable boundaries.
+ */
+static int gfs2_rindex_rebuild(struct gfs2_sbd *sdp, int *num_rgs,
+			       int gfs_grow)
+{
+	struct osi_node *n, *next = NULL;
+	struct gfs2_buffer_head *bh;
+	uint64_t shortest_dist_btwn_rgs;
+	uint64_t blk;
+	uint64_t fwd_block, block_bump;
+	uint64_t first_rg_dist, initial_first_rg_dist;
+	struct rgrp_tree *calc_rgd, *prev_rgd;
+	int number_of_rgs, rgi;
+	int rg_was_fnd = FALSE, corrupt_rgs = 0, bitmap_was_fnd;
+
+	sdp->rgcalc.osi_node = NULL;
+	initial_first_rg_dist = first_rg_dist = sdp->sb_addr + 1;
+	shortest_dist_btwn_rgs = find_shortest_rgdist(sdp,
+						      &initial_first_rg_dist,
+						      &first_rg_dist);
+	number_of_rgs = 0;
 	/* -------------------------------------------------------------- */
 	/* Now go through the RGs and verify their integrity, fixing as   */
 	/* needed when corruption is encountered.                         */
 	/* -------------------------------------------------------------- */
 	prev_rgd = NULL;
 	block_bump = first_rg_dist;
-	for (blk = sdp->sb_addr + 1; blk <= sdp->device.length;
-	     blk += block_bump) {
+	blk = sdp->sb_addr + 1;
+	while (blk <= sdp->device.length) {
 		log_debug( _("Block 0x%" PRIx64 "\n"), blk);
 		bh = bread(sdp, blk);
 		rg_was_fnd = (!gfs2_check_meta(bh, GFS2_METATYPE_RG));
@@ -224,7 +462,7 @@ static int gfs2_rindex_rebuild(struct gfs2_sbd *sdp, int *num_rgs)
 		/* Allocate a new RG and index. */
 		calc_rgd = rgrp_insert(&sdp->rgcalc, blk);
 		if (!calc_rgd) {
-			log_crit( _("Can't allocate memory for rg repair.\n"));
+			log_crit( _("Can't allocate memory for rgrp repair.\n"));
 			return -1;
 		}
 		calc_rgd->ri.ri_length = 1;
@@ -234,11 +472,13 @@ static int gfs2_rindex_rebuild(struct gfs2_sbd *sdp, int *num_rgs)
 			/* ------------------------------------------------- */
 			corrupt_rgs++;
 			if (corrupt_rgs < 5)
-				log_debug( _("Missing or damaged RG at block %" 
+				log_debug( _("Missing or damaged rgrp at block %"
 					  PRIu64 " (0x%" PRIx64 ")\n"),
 					  blk, blk);
 			else {
-				log_crit( _("Error: too many bad RGs.\n"));
+				log_crit( _("Error: too many missing or "
+					    "damaged rgrps using this method. "
+					    "Time to try another method.\n"));
 				return -1;
 			}
 		}
@@ -270,6 +510,8 @@ static int gfs2_rindex_rebuild(struct gfs2_sbd *sdp, int *num_rgs)
 
 			prev_rgd->ri.ri_length = bitblocks;
 			prev_rgd->ri.ri_data = rgblocks;
+			prev_rgd->ri.ri_data0 = prev_rgd->ri.ri_addr +
+				prev_rgd->ri.ri_length;
 			prev_rgd->ri.ri_data -= prev_rgd->ri.ri_data %
 				GFS2_NBBY;
 			prev_rgd->ri.ri_bitbytes = prev_rgd->ri.ri_data /
@@ -278,18 +520,42 @@ static int gfs2_rindex_rebuild(struct gfs2_sbd *sdp, int *num_rgs)
 				  prev_rgd->ri.ri_data);
 		}
 		number_of_rgs++;
-		log_warn( _("%c RG %d at block 0x%" PRIX64 " %s"),
-			 (rg_was_fnd ? ' ' : '*'), number_of_rgs, blk,
-			 (rg_was_fnd ? "intact" : "*** DAMAGED ***"));
+		if (rg_was_fnd)
+			log_info( _("  rgrp %d at block 0x%llx intact"),
+				  number_of_rgs, (unsigned long long)blk);
+		else
+			log_warn( _("* rgrp %d at block 0x%llx *** DAMAGED ***"),
+				  number_of_rgs, (unsigned long long)blk);
 		prev_rgd = calc_rgd;
-		block_of_last_rg = blk;
-
+		/*
+		 * Figure out where our next rgrp should be.
+		 */
 		if (blk == sdp->sb_addr + 1)
 			block_bump = first_rg_dist;
-		else
+		else if (!gfs_grow) {
 			block_bump = shortest_dist_btwn_rgs;
-		if (block_bump != 1)
-			log_warn( _(" [length 0x%" PRIx64 "]\n"), block_bump);
+			/* if we have uniformly-spaced rgrps, there may be
+			   some wasted space at the end of the device.
+			   Since we don't want to create a short rgrp and
+			   break our uniformity, just quit here. */
+			if (blk + (2 * block_bump) > sdp->device.length)
+				break;
+		} else if (rg_was_fnd)
+			block_bump = find_next_rgrp_dist(sdp, blk, prev_rgd);
+		else
+			block_bump = hunt_and_peck(sdp, blk, prev_rgd,
+						   block_bump);
+		if (block_bump != 1) {
+			if (rg_was_fnd)
+				log_info( _(" [length 0x%" PRIx64 "]\n"),
+					  block_bump);
+			else
+				log_warn( _(" [length 0x%" PRIx64 "]\n"),
+					  block_bump);
+		} else {
+			log_warn("\n");
+		}
+		blk += block_bump;
 	} /* for each rg block */
 	/* ----------------------------------------------------------------- */
 	/* If we got to the end of the fs, we still need to fix the          */
@@ -302,6 +568,8 @@ static int gfs2_rindex_rebuild(struct gfs2_sbd *sdp, int *num_rgs)
 		rgblocks2bitblocks(sdp->bsize, &rgblocks, &bitblocks);
 
 		prev_rgd->ri.ri_length = bitblocks;
+		prev_rgd->ri.ri_data0 = prev_rgd->ri.ri_addr +
+			prev_rgd->ri.ri_length;
 		prev_rgd->ri.ri_data = rgblocks;
 		prev_rgd->ri.ri_data -= prev_rgd->ri.ri_data % GFS2_NBBY;
 		prev_rgd->ri.ri_bitbytes = prev_rgd->ri.ri_data / GFS2_NBBY;
@@ -342,15 +610,8 @@ static int gfs2_rindex_rebuild(struct gfs2_sbd *sdp, int *num_rgs)
  */
 static int gfs2_rindex_calculate(struct gfs2_sbd *sdp, int *num_rgs)
 {
-	sdp->rgsize = GFS2_DEFAULT_RGSIZE; /* compute_rgrp_layout adjusts */
-	device_geometry(sdp);
-	fix_device_geometry(sdp);
-	/* Compute the default resource group layout as mkfs would have done */
-	compute_rgrp_layout(sdp, &sdp->rgcalc, FALSE);
-	build_rgrps(sdp, FALSE); /* FALSE = calc but don't write to disk. */
-	*num_rgs = 0;
-	log_debug( _("fs_total_size = 0x%" PRIX64 " blocks.\n"),
-		  sdp->device.length);
+	uint64_t num_rgrps = 0;
+
 	/* ----------------------------------------------------------------- */
 	/* Calculate how many RGs there are supposed to be based on the      */
 	/* rindex filesize.  Remember that our trust level is open-minded    */
@@ -361,6 +622,33 @@ static int gfs2_rindex_calculate(struct gfs2_sbd *sdp, int *num_rgs)
 	*num_rgs = sdp->md.riinode->i_di.di_size / sizeof(struct gfs2_rindex);
 
 	sdp->rgcalc.osi_node = NULL;
+	if (device_geometry(sdp)) {
+		fprintf(stderr, _("Geometry error\n"));
+		exit(-1);
+	}
+	if (fix_device_geometry(sdp)) {
+		fprintf(stderr, _("Device is too small (%"PRIu64" bytes)\n"),
+				sdp->device.length << GFS2_BASIC_BLOCK_SHIFT);
+		exit(-1);
+	}
+
+	/* Try all possible rgrp sizes: 2048, 1024, 512, 256, 128, 64, 32 */
+	for (sdp->rgsize = GFS2_DEFAULT_RGSIZE; sdp->rgsize >= 32;
+	     sdp->rgsize /= 2) {
+		num_rgrps = how_many_rgrps(sdp, &sdp->device, TRUE);
+		if (num_rgrps == *num_rgs) {
+			log_info(_("rgsize must be: %lld (0x%llx)\n"),
+				 (unsigned long long)sdp->rgsize,
+				 (unsigned long long)sdp->rgsize);
+			break;
+		}
+	}
+	/* Compute the default resource group layout as mkfs would have done */
+	compute_rgrp_layout(sdp, &sdp->rgcalc, TRUE);
+	build_rgrps(sdp, FALSE); /* FALSE = calc but don't write to disk. */
+	log_debug( _("fs_total_size = 0x%" PRIX64 " blocks.\n"),
+		  sdp->device.length);
+	log_warn( _("L3: number of rgs in the index = %d.\n"), *num_rgs);
 	return 0;
 }
 
@@ -372,14 +660,14 @@ static int rewrite_rg_block(struct gfs2_sbd *sdp, struct rgrp_tree *rg,
 			    uint64_t errblock)
 {
 	int x = errblock - rg->ri.ri_addr;
+	const char *typedesc = x ? "GFS2_METATYPE_RB" : "GFS2_METATYPE_RG";
 
-	log_err( _("Block #%lld (0x%llx) (%d of %d) is neither"
-		" GFS2_METATYPE_RB nor GFS2_METATYPE_RG.\n"),
+	log_err( _("Block #%lld (0x%llx) (%d of %d) is not %s.\n"),
 		 (unsigned long long)rg->ri.ri_addr + x,
 		 (unsigned long long)rg->ri.ri_addr + x,
-		 (int)x+1, (int)rg->ri.ri_length);
+		 (int)x+1, (int)rg->ri.ri_length, typedesc);
 	if (query( _("Fix the Resource Group? (y/n)"))) {
-		log_err( _("Attempting to repair the RG.\n"));
+		log_err( _("Attempting to repair the rgrp.\n"));
 		rg->bh[x] = bread(sdp, rg->ri.ri_addr + x);
 		if (x) {
 			struct gfs2_meta_header mh;
@@ -389,12 +677,19 @@ static int rewrite_rg_block(struct gfs2_sbd *sdp, struct rgrp_tree *rg,
 			mh.mh_format = GFS2_FORMAT_RB;
 			gfs2_meta_header_out(&mh, rg->bh[x]);
 		} else {
-			memset(&rg->rg, 0, sizeof(struct gfs2_rgrp));
+			if (sdp->gfs1)
+				memset(&rg->rg, 0, sizeof(struct gfs_rgrp));
+			else
+				memset(&rg->rg, 0, sizeof(struct gfs2_rgrp));
 			rg->rg.rg_header.mh_magic = GFS2_MAGIC;
 			rg->rg.rg_header.mh_type = GFS2_METATYPE_RG;
 			rg->rg.rg_header.mh_format = GFS2_FORMAT_RG;
 			rg->rg.rg_free = rg->ri.ri_data;
-			gfs2_rgrp_out(&rg->rg, rg->bh[x]);
+			if (sdp->gfs1)
+				gfs_rgrp_out((struct gfs_rgrp *)&rg->rg,
+					     rg->bh[x]);
+			else
+				gfs2_rgrp_out(&rg->rg, rg->bh[x]);
 		}
 		brelse(rg->bh[x]);
 		rg->bh[x] = NULL;
@@ -427,6 +722,7 @@ static int expect_rindex_sanity(struct gfs2_sbd *sdp, int *num_rgs)
 		memcpy(&exp->ri, &rgd->ri, sizeof(exp->ri));
 		memcpy(&exp->rg, &rgd->rg, sizeof(exp->rg));
 		exp->bits = NULL;
+		exp->bh = NULL;
 		gfs2_compute_bitstructs(sdp, exp);
 	}
 	sdp->rgrps = *num_rgs;
@@ -440,17 +736,23 @@ static int expect_rindex_sanity(struct gfs2_sbd *sdp, int *num_rgs)
  *             open_minded means it might be okay, but we should verify it.
  *             distrust means it's not to be trusted, so we should go to
  *             greater lengths to build it from scratch.
+ *             indignation means we have corruption, but the file system
+ *             was converted from GFS via gfs2_convert, and its rgrps are
+ *             not on nice boundaries thanks to previous gfs_grow ops. Lovely.
  */
 int rg_repair(struct gfs2_sbd *sdp, int trust_lvl, int *rg_count, int *sane)
 {
 	struct osi_node *n, *next = NULL, *e, *enext;
-	int error, discrepancies;
+	int error, discrepancies, percent;
 	int calc_rg_count = 0, rgcount_from_index, rg;
 	struct gfs2_rindex buf;
 
 	if (trust_lvl == blind_faith)
 		return 0;
-	else if (trust_lvl == ye_of_little_faith) { /* if rindex seems sane */
+	if (trust_lvl == ye_of_little_faith) { /* if rindex seems sane */
+		/* Don't free previous incarnations in memory, if any.
+		 * We need them to copy in the next function:
+		 * gfs2_rgrp_free(&sdp->rglist); */
 		if (!(*sane)) {
 			log_err(_("The rindex file does not meet our "
 				  "expectations.\n"));
@@ -476,7 +778,7 @@ int rg_repair(struct gfs2_sbd *sdp, int trust_lvl, int *rg_count, int *sane)
 		/* Free previous incarnations in memory, if any. */
 		gfs2_rgrp_free(&sdp->rgtree);
 
-		error = gfs2_rindex_rebuild(sdp, &calc_rg_count);
+		error = gfs2_rindex_rebuild(sdp, &calc_rg_count, 0);
 		if (error) {
 			log_crit( _("Error rebuilding rgrp list.\n"));
 			gfs2_rgrp_free(&sdp->rgcalc);
@@ -484,7 +786,18 @@ int rg_repair(struct gfs2_sbd *sdp, int trust_lvl, int *rg_count, int *sane)
 		}
 		sdp->rgrps = calc_rg_count;
 	}
+	else if (trust_lvl == indignation) { /* If we can't trust anything */
+		/* Free previous incarnations in memory, if any. */
+		gfs2_rgrp_free(&sdp->rgtree);
 
+		error = gfs2_rindex_rebuild(sdp, &calc_rg_count, 1);
+		if (error) {
+			log_crit( _("Error rebuilding rgrp list.\n"));
+			gfs2_rgrp_free(&sdp->rgcalc);
+			return -1;
+		}
+		sdp->rgrps = calc_rg_count;
+	}
 	/* Read in the rindex */
 	sdp->rgtree.osi_node = NULL; /* Just to be safe */
 	rindex_read(sdp, 0, &rgcount_from_index, sane);
@@ -542,18 +855,26 @@ int rg_repair(struct gfs2_sbd *sdp, int trust_lvl, int *rg_count, int *sane)
 		    !ri_equal(actual->ri, expected->ri, ri_data) ||
 		    !ri_equal(actual->ri, expected->ri, ri_bitbytes)) {
 			discrepancies++;
+			log_info(_("%d addr: 0x%llx 0x%llx * has mismatch\n"),
+				 rg + 1, actual->ri.ri_addr,
+				 expected->ri.ri_addr);
 		}
 		n = next;
 		e = enext;
 	}
-	if (trust_lvl < distrust && discrepancies > (trust_lvl * 8)) {
-		log_warn( _("Level %d didn't work.  Too many descepencies.\n"),
-			 trust_lvl + 1);
-		log_warn( _("%d out of %d RGs did not match what was expected.\n"),
-			 discrepancies, rg);
-		gfs2_rgrp_free(&sdp->rgcalc);
-		gfs2_rgrp_free(&sdp->rgtree);
-		return -1;
+	if (rg) {
+		/* Check to see if more than 2% of the rgrps are wrong.  */
+		percent = (discrepancies * 100) / rg;
+		if (percent > BAD_RG_PERCENT_TOLERANCE) {
+			log_warn( _("Level %d didn't work.  Too many "
+				    "discrepancies.\n"), trust_lvl + 1);
+			log_warn( _("%d out of %d rgrps (%d percent) did not "
+				    "match what was expected.\n"),
+				  discrepancies, rg, percent);
+			gfs2_rgrp_free(&sdp->rgcalc);
+			gfs2_rgrp_free(&sdp->rgtree);
+			return -1;
+		}
 	}
 	/* ------------------------------------------------------------- */
 	/* Now compare the rindex to what we think it should be.         */
@@ -611,11 +932,13 @@ int rg_repair(struct gfs2_sbd *sdp, int trust_lvl, int *rg_count, int *sane)
 				/* Therefore, gfs2_compute_bitstructs might  */
 				/* have malloced the wrong length for bitmap */
 				/* buffers.  So we have to redo it.          */
-				if (actual->bits)
+				if (actual->bits) {
 					free(actual->bits);
+					actual->bits = NULL;
+				}
 			}
 			else
-				log_err( _("RG index not fixed.\n"));
+				log_err( _("rindex not fixed.\n"));
 			gfs2_compute_bitstructs(sdp, actual);
 			rindex_modified = FALSE;
 		}
diff --git a/gfs2/fsck/rgrp.c b/gfs2/fsck/rgrp.c
new file mode 100644
index 0000000..7ae862a
--- /dev/null
+++ b/gfs2/fsck/rgrp.c
@@ -0,0 +1,228 @@
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libgfs2.h"
+
+#define RG_SYNC_TOLERANCE 1000
+
+/**
+ * gfs2_compute_bitstructs - Compute the bitmap sizes
+ * @rgd: The resource group descriptor
+ *
+ * Returns: 0 on success, -1 on error
+ */
+int gfs2_compute_bitstructs(struct gfs2_sbd *sdp, struct rgrp_tree *rgd)
+{
+	struct gfs2_bitmap *bits;
+	uint32_t length = rgd->ri.ri_length;
+	uint32_t bytes_left, bytes;
+	int x;
+
+	/* 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)))
+		return -1;
+
+	bytes_left = rgd->ri.ri_bitbytes;
+
+	for (x = 0; x < length; x++){
+		bits = &rgd->bits[x];
+
+		if (length == 1){
+			bytes = bytes_left;
+			bits->bi_offset = sizeof(struct gfs2_rgrp);
+			bits->bi_start = 0;
+			bits->bi_len = bytes;
+		}
+		else if (x == 0){
+			bytes = sdp->sd_sb.sb_bsize - sizeof(struct gfs2_rgrp);
+			bits->bi_offset = sizeof(struct gfs2_rgrp);
+			bits->bi_start = 0;
+			bits->bi_len = bytes;
+		}
+		else if (x + 1 == length){
+			bytes = bytes_left;
+			bits->bi_offset = sizeof(struct gfs2_meta_header);
+			bits->bi_start = rgd->ri.ri_bitbytes - bytes_left;
+			bits->bi_len = bytes;
+		}
+		else{
+			bytes = sdp->sd_sb.sb_bsize - sizeof(struct gfs2_meta_header);
+			bits->bi_offset = sizeof(struct gfs2_meta_header);
+			bits->bi_start = rgd->ri.ri_bitbytes - bytes_left;
+			bits->bi_len = bytes;
+		}
+
+		bytes_left -= bytes;
+	}
+
+	if(bytes_left)
+		return -1;
+
+	if((rgd->bits[length - 1].bi_start +
+	    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;
+	if(!memset(rgd->bh, 0, length * sizeof(struct gfs2_buffer_head *)))
+		return -1;
+
+	return 0;
+}
+
+
+/**
+ * blk2rgrpd - Find resource group for a given data block number
+ * @sdp: The GFS superblock
+ * @n: The data block number
+ *
+ * Returns: Ths resource group, or NULL if not found
+ */
+struct rgrp_tree *gfs2_blk2rgrpd(struct gfs2_sbd *sdp, uint64_t blk)
+{
+	struct osi_node *node = sdp->rgtree.osi_node;
+	struct gfs2_rindex *ri;
+
+	while (node) {
+		struct rgrp_tree *rgd = (struct rgrp_tree *)node;
+		ri = &rgd->ri;
+
+		if (blk < ri->ri_addr)
+			node = node->osi_left;
+		else if (blk > ri->ri_data0 + ri->ri_data)
+			node = node->osi_right;
+		else
+			return rgd;
+	}
+	return NULL;
+}
+
+/**
+ * gfs2_rgrp_read - read in the resource group information from disk.
+ * @rgd - resource group structure
+ * returns: 0 if no error, otherwise the block number that failed
+ */
+uint64_t gfs2_rgrp_read(struct gfs2_sbd *sdp, struct rgrp_tree *rgd)
+{
+	int x, length = rgd->ri.ri_length;
+	uint64_t max_rgrp_bitbytes, max_rgrp_len;
+
+	/* Max size of an rgrp is 2GB.  Figure out how many blocks that is: */
+	max_rgrp_bitbytes = ((2147483648 / sdp->bsize) / GFS2_NBBY);
+	max_rgrp_len = max_rgrp_bitbytes / sdp->bsize;
+	if (!length && length > max_rgrp_len)
+		return -1;
+	if (gfs2_check_range(sdp, rgd->ri.ri_addr))
+		return -1;
+	for (x = 0; x < length; x++){
+		rgd->bh[x] = bread(sdp, rgd->ri.ri_addr + x);
+		if(gfs2_check_meta(rgd->bh[x],
+				   (x) ? GFS2_METATYPE_RB : GFS2_METATYPE_RG))
+		{
+			uint64_t error;
+
+			error = rgd->ri.ri_addr + x;
+			for (; x >= 0; x--) {
+				brelse(rgd->bh[x]);
+				rgd->bh[x] = NULL;
+			}
+			return error;
+		}
+	}
+	if (rgd->bh && rgd->bh[0]) {
+		if (sdp->gfs1)
+			gfs_rgrp_in((struct gfs_rgrp *)&rgd->rg, rgd->bh[0]);
+		else
+			gfs2_rgrp_in(&rgd->rg, rgd->bh[0]);
+	}
+	return 0;
+}
+
+void gfs2_rgrp_relse(struct rgrp_tree *rgd)
+{
+	int x, length = rgd->ri.ri_length;
+
+	for (x = 0; x < length; x++) {
+		if (rgd->bh) {
+			if (rgd->bh[x])
+				brelse(rgd->bh[x]);
+			rgd->bh[x] = NULL;
+		}
+	}
+}
+
+struct rgrp_tree *rgrp_insert(struct osi_root *rgtree, uint64_t rgblock)
+{
+	struct osi_node **newn = &rgtree->osi_node, *parent = NULL;
+	struct rgrp_tree *data;
+
+	/* Figure out where to put new node */
+	while (*newn) {
+		struct rgrp_tree *cur = (struct rgrp_tree *)*newn;
+
+		parent = *newn;
+		if (rgblock < cur->ri.ri_addr)
+			newn = &((*newn)->osi_left);
+		else if (rgblock > cur->ri.ri_addr)
+			newn = &((*newn)->osi_right);
+		else
+			return cur;
+	}
+
+	data = malloc(sizeof(struct rgrp_tree));
+	if (!data)
+		return NULL;
+	if (!memset(data, 0, sizeof(struct rgrp_tree)))
+		return NULL;
+	/* Add new node and rebalance tree. */
+	data->ri.ri_addr = rgblock;
+	osi_link_node(&data->node, parent, newn);
+	osi_insert_color(&data->node, rgtree);
+
+	return data;
+}
+
+void gfs2_rgrp_free(struct osi_root *rgrp_tree)
+{
+	struct rgrp_tree *rgd;
+	int rgs_since_sync = 0;
+	struct osi_node *n;
+	struct gfs2_sbd *sdp = NULL;
+
+	while ((n = osi_first(rgrp_tree))) {
+		rgd = (struct rgrp_tree *)n;
+		if (rgd->bh && rgd->bh[0]) { /* if a buffer exists        */
+			rgs_since_sync++;
+			if (rgs_since_sync >= RG_SYNC_TOLERANCE) {
+				if (!sdp)
+					sdp = rgd->bh[0]->sdp;
+				fsync(sdp->device_fd);
+				rgs_since_sync = 0;
+			}
+			gfs2_rgrp_relse(rgd); /* free them all. */
+		}
+		if(rgd->bits)
+			free(rgd->bits);
+		if(rgd->bh) {
+			free(rgd->bh);
+			rgd->bh = NULL;
+		}
+		osi_erase(&rgd->node, rgrp_tree);
+		free(rgd);
+	}
+}
diff --git a/gfs2/fsck/size.c b/gfs2/fsck/size.c
new file mode 100644
index 0000000..d9d2c54
--- /dev/null
+++ b/gfs2/fsck/size.c
@@ -0,0 +1,67 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/mount.h>
+
+#include "libgfs2.h"
+
+#ifndef BLKGETSIZE64
+#define BLKGETSIZE64 _IOR(0x12, 114, size_t)
+#endif
+
+/**
+ * do_device_size - determine the size of a Linux block device
+ * @device: the path to the device node
+ *
+ * Returns: -1 on error (with errno set), 0 on success (with @bytes set)
+ */
+
+static int do_device_size(int fd, uint64_t *bytes)
+{
+	off_t off;
+
+	off = lseek(fd, 0, SEEK_END);
+	if (off >= 0) {
+		*bytes = off;
+		return 0;
+	}
+
+	return -1;
+}
+
+/**
+ * device_size - figure out a device's size
+ * @fd: the file descriptor of a device
+ * @bytes: the number of bytes the device holds
+ *
+ * Returns: -1 on error (with errno set), 0 on success (with @bytes set)
+ */
+
+int device_size(int fd, uint64_t *bytes)
+{
+	struct stat st;
+	int error;
+
+	error = fstat(fd, &st);
+	if (error)
+		return error;
+
+	if (S_ISREG(st.st_mode)) {
+		*bytes = st.st_size;
+		return 0;
+	} else if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode))
+		return do_device_size(fd, bytes);
+	else if (S_ISDIR(st.st_mode))
+		errno = EISDIR;
+	else
+		errno = EINVAL;
+
+	return -1;
+}
diff --git a/gfs2/fsck/structures.c b/gfs2/fsck/structures.c
new file mode 100644
index 0000000..0fe1153
--- /dev/null
+++ b/gfs2/fsck/structures.c
@@ -0,0 +1,530 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <linux/types.h>
+
+#include "libgfs2.h"
+
+int build_master(struct gfs2_sbd *sdp)
+{
+	struct gfs2_inum inum;
+	uint64_t bn;
+	struct gfs2_buffer_head *bh;
+
+	bn = dinode_alloc(sdp);
+	inum.no_formal_ino = sdp->md.next_inum++;
+	inum.no_addr = bn;
+
+	bh = init_dinode(sdp, &inum, S_IFDIR | 0755, GFS2_DIF_SYSTEM, &inum);
+
+	sdp->master_dir = inode_get(sdp, bh);
+
+	if (sdp->debug) {
+		printf("\nMaster dir:\n");
+		gfs2_dinode_print(&sdp->master_dir->i_di);
+	}
+	sdp->master_dir->bh_owned = 1;
+	return 0;
+}
+
+void build_sb(struct gfs2_sbd *sdp, const unsigned char *uuid)
+{
+	unsigned int x;
+	struct gfs2_buffer_head *bh;
+	struct gfs2_sb sb;
+
+	/* Zero out the beginning of the device up to the superblock */
+	for (x = 0; x < sdp->sb_addr; x++) {
+		bh = bget(sdp, x);
+		memset(bh->b_data, 0, sdp->bsize);
+		bmodified(bh);
+		brelse(bh);
+	}
+
+	memset(&sb, 0, sizeof(struct gfs2_sb));
+	sb.sb_header.mh_magic = GFS2_MAGIC;
+	sb.sb_header.mh_type = GFS2_METATYPE_SB;
+	sb.sb_header.mh_format = GFS2_FORMAT_SB;
+	sb.sb_fs_format = GFS2_FORMAT_FS;
+	sb.sb_multihost_format = GFS2_FORMAT_MULTI;
+	sb.sb_bsize = sdp->bsize;
+	sb.sb_bsize_shift = ffs(sdp->bsize) - 1;
+	sb.sb_master_dir = sdp->master_dir->i_di.di_num;
+	sb.sb_root_dir = sdp->md.rooti->i_di.di_num;
+	strcpy(sb.sb_lockproto, sdp->lockproto);
+	strcpy(sb.sb_locktable, sdp->locktable);
+#ifdef GFS2_HAS_UUID
+	memcpy(sb.sb_uuid, uuid, sizeof(sb.sb_uuid));
+#endif
+	bh = bget(sdp, sdp->sb_addr);
+	gfs2_sb_out(&sb, bh);
+	brelse(bh);
+
+	if (sdp->debug) {
+		printf("\nSuper Block:\n");
+		gfs2_sb_print(&sb);
+	}
+}
+
+int write_journal(struct gfs2_sbd *sdp, unsigned int j, unsigned int blocks)
+{
+	struct gfs2_log_header lh;
+	unsigned int x;
+	uint64_t seq = ((blocks) * (random() / (RAND_MAX + 1.0)));
+	uint32_t hash;
+	unsigned int height;
+
+	/* Build the height up so our journal blocks will be contiguous and */
+	/* not broken up by indirect block pages.                           */
+	height = calc_tree_height(sdp->md.journal[j],
+				  (blocks + 1) * sdp->bsize);
+	build_height(sdp->md.journal[j], height);
+
+	memset(&lh, 0, sizeof(struct gfs2_log_header));
+	lh.lh_header.mh_magic = GFS2_MAGIC;
+	lh.lh_header.mh_type = GFS2_METATYPE_LH;
+	lh.lh_header.mh_format = GFS2_FORMAT_LH;
+	lh.lh_flags = GFS2_LOG_HEAD_UNMOUNT;
+
+	for (x = 0; x < blocks; x++) {
+		struct gfs2_buffer_head *bh = get_file_buf(sdp->md.journal[j],
+							   x, TRUE);
+		if (!bh)
+			return -1;
+		bmodified(bh);
+		brelse(bh);
+	}
+	for (x = 0; x < blocks; x++) {
+		struct gfs2_buffer_head *bh = get_file_buf(sdp->md.journal[j],
+							   x, FALSE);
+		if (!bh)
+			return -1;
+
+		memset(bh->b_data, 0, sdp->bsize);
+		lh.lh_sequence = seq;
+		lh.lh_blkno = x;
+		gfs2_log_header_out(&lh, bh);
+		hash = gfs2_disk_hash(bh->b_data, sizeof(struct gfs2_log_header));
+		((struct gfs2_log_header *)bh->b_data)->lh_hash = cpu_to_be32(hash);
+
+		bmodified(bh);
+		brelse(bh);
+
+		if (++seq == blocks)
+			seq = 0;
+	}
+
+	if (sdp->debug) {
+		printf("\nJournal %u:\n", j);
+		gfs2_dinode_print(&sdp->md.journal[j]->i_di);
+	}
+	return 0;
+}
+
+int build_journal(struct gfs2_sbd *sdp, int j, struct gfs2_inode *jindex)
+{
+	char name[256];
+	int ret;
+
+	sprintf(name, "journal%u", j);
+	sdp->md.journal[j] = createi(jindex, name, S_IFREG | 0600,
+				     GFS2_DIF_SYSTEM);
+	ret = write_journal(sdp, j,
+			    sdp->jsize << 20 >> sdp->sd_sb.sb_bsize_shift);
+	return ret;
+}
+
+int build_jindex(struct gfs2_sbd *sdp)
+{
+	struct gfs2_inode *jindex;
+	unsigned int j;
+	int ret;
+
+	jindex = createi(sdp->master_dir, "jindex", S_IFDIR | 0700,
+			 GFS2_DIF_SYSTEM);
+	sdp->md.journal = malloc(sdp->md.journals *
+				 sizeof(struct gfs2_inode *));
+	for (j = 0; j < sdp->md.journals; j++) {
+		ret = build_journal(sdp, j, jindex);
+		if (ret)
+			return ret;
+		inode_put(&sdp->md.journal[j]);
+	}
+	if (sdp->debug) {
+		printf("\nJindex:\n");
+		gfs2_dinode_print(&jindex->i_di);
+	}
+
+	free(sdp->md.journal);
+	inode_put(&jindex);
+	return 0;
+}
+
+static int build_inum_range(struct gfs2_inode *per_node, unsigned int j)
+{
+	struct gfs2_sbd *sdp = per_node->i_sbd;
+	char name[256];
+	struct gfs2_inode *ip;
+
+	sprintf(name, "inum_range%u", j);
+	ip = createi(per_node, name, S_IFREG | 0600,
+		     GFS2_DIF_SYSTEM | GFS2_DIF_JDATA);
+	ip->i_di.di_size = sizeof(struct gfs2_inum_range);
+	gfs2_dinode_out(&ip->i_di, ip->i_bh);
+
+	if (sdp->debug) {
+		printf("\nInum Range %u:\n", j);
+		gfs2_dinode_print(&ip->i_di);
+	}
+
+	inode_put(&ip);
+	return 0;
+}
+
+static void build_statfs_change(struct gfs2_inode *per_node, unsigned int j)
+{
+	struct gfs2_sbd *sdp = per_node->i_sbd;
+	char name[256];
+	struct gfs2_inode *ip;
+
+	sprintf(name, "statfs_change%u", j);
+	ip = createi(per_node, name, S_IFREG | 0600,
+		     GFS2_DIF_SYSTEM | GFS2_DIF_JDATA);
+	ip->i_di.di_size = sizeof(struct gfs2_statfs_change);
+	gfs2_dinode_out(&ip->i_di, ip->i_bh);
+
+	if (sdp->debug) {
+		printf("\nStatFS Change %u:\n", j);
+		gfs2_dinode_print(&ip->i_di);
+	}
+
+	inode_put(&ip);
+}
+
+static int build_quota_change(struct gfs2_inode *per_node, unsigned int j)
+{
+	struct gfs2_sbd *sdp = per_node->i_sbd;
+	struct gfs2_meta_header mh;
+	char name[256];
+	struct gfs2_inode *ip;
+	unsigned int blocks = sdp->qcsize << (20 - sdp->sd_sb.sb_bsize_shift);
+	unsigned int x;
+	unsigned int hgt;
+	struct gfs2_buffer_head *bh;
+
+	memset(&mh, 0, sizeof(struct gfs2_meta_header));
+	mh.mh_magic = GFS2_MAGIC;
+	mh.mh_type = GFS2_METATYPE_QC;
+	mh.mh_format = GFS2_FORMAT_QC;
+
+	sprintf(name, "quota_change%u", j);
+	ip = createi(per_node, name, S_IFREG | 0600, GFS2_DIF_SYSTEM);
+
+	hgt = calc_tree_height(ip, (blocks + 1) * sdp->bsize);
+	build_height(ip, hgt);
+
+	for (x = 0; x < blocks; x++) {
+		bh = get_file_buf(ip, x, FALSE);
+		if (!bh)
+			return -1;
+
+		memset(bh->b_data, 0, sdp->bsize);
+		gfs2_meta_header_out(&mh, bh);
+		brelse(bh);
+	}
+
+	if (sdp->debug) {
+		printf("\nQuota Change %u:\n", j);
+		gfs2_dinode_print(&ip->i_di);
+	}
+
+	inode_put(&ip);
+	return 0;
+}
+
+int build_per_node(struct gfs2_sbd *sdp)
+{
+	struct gfs2_inode *per_node;
+	unsigned int j;
+
+	per_node = createi(sdp->master_dir, "per_node", S_IFDIR | 0700,
+			   GFS2_DIF_SYSTEM);
+
+	for (j = 0; j < sdp->md.journals; j++) {
+		build_inum_range(per_node, j);
+		build_statfs_change(per_node, j);
+		build_quota_change(per_node, j);
+	}
+
+	if (sdp->debug) {
+		printf("\nper_node:\n");
+		gfs2_dinode_print(&per_node->i_di);
+	}
+
+	inode_put(&per_node);
+	return 0;
+}
+
+int build_inum(struct gfs2_sbd *sdp)
+{
+	struct gfs2_inode *ip;
+
+	ip = createi(sdp->master_dir, "inum", S_IFREG | 0600,
+		     GFS2_DIF_SYSTEM | GFS2_DIF_JDATA);
+
+	if (sdp->debug) {
+		printf("\nInum Inode:\n");
+		gfs2_dinode_print(&ip->i_di);
+	}
+
+	inode_put(&ip);
+	return 0;
+}
+
+int build_statfs(struct gfs2_sbd *sdp)
+{
+	struct gfs2_inode *ip;
+
+	ip = createi(sdp->master_dir, "statfs", S_IFREG | 0600,
+		     GFS2_DIF_SYSTEM | GFS2_DIF_JDATA);
+
+	if (sdp->debug) {
+		printf("\nStatFS Inode:\n");
+		gfs2_dinode_print(&ip->i_di);
+	}
+
+	inode_put(&ip);
+	return 0;
+}
+
+int build_rindex(struct gfs2_sbd *sdp)
+{
+	struct gfs2_inode *ip;
+	struct osi_node *n, *next = NULL;
+	struct rgrp_tree *rl;
+	char buf[sizeof(struct gfs2_rindex)];
+	int count;
+
+	ip = createi(sdp->master_dir, "rindex", S_IFREG | 0600,
+		     GFS2_DIF_SYSTEM | GFS2_DIF_JDATA);
+	ip->i_di.di_payload_format = GFS2_FORMAT_RI;
+	bmodified(ip->i_bh);
+
+	for (n = osi_first(&sdp->rgtree); n; n = next) {
+		next = osi_next(n);
+		rl = (struct rgrp_tree *)n;
+
+		gfs2_rindex_out(&rl->ri, buf);
+
+		count = gfs2_writei(ip, buf, ip->i_di.di_size,
+				    sizeof(struct gfs2_rindex));
+		if (count != sizeof(struct gfs2_rindex))
+			return -1;
+	}
+	memset(buf, 0, sizeof(struct gfs2_rindex));
+	count = __gfs2_writei(ip, buf, ip->i_di.di_size,
+			      sizeof(struct gfs2_rindex), 0);
+	if (count != sizeof(struct gfs2_rindex))
+		return -1;
+
+	if (sdp->debug) {
+		printf("\nResource Index:\n");
+		gfs2_dinode_print(&ip->i_di);
+	}
+
+	inode_put(&ip);
+	return 0;
+}
+
+int build_quota(struct gfs2_sbd *sdp)
+{
+	struct gfs2_inode *ip;
+	struct gfs2_quota qu;
+	char buf[sizeof(struct gfs2_quota)];
+	int count;
+
+	ip = createi(sdp->master_dir, "quota", S_IFREG | 0600,
+		     GFS2_DIF_SYSTEM | GFS2_DIF_JDATA);
+	ip->i_di.di_payload_format = GFS2_FORMAT_QU;
+	bmodified(ip->i_bh);
+
+	memset(&qu, 0, sizeof(struct gfs2_quota));
+	qu.qu_value = 1;
+	gfs2_quota_out(&qu, buf);
+
+	count = gfs2_writei(ip, buf, ip->i_di.di_size, sizeof(struct gfs2_quota));
+	if (count != sizeof(struct gfs2_quota))
+		return -1;
+	count = gfs2_writei(ip, buf, ip->i_di.di_size, sizeof(struct gfs2_quota));
+	if (count != sizeof(struct gfs2_quota))
+		return -1;
+
+	if (sdp->debug) {
+		printf("\nRoot quota:\n");
+		gfs2_quota_print(&qu);
+	}
+
+	inode_put(&ip);
+	return 0;
+}
+
+int build_root(struct gfs2_sbd *sdp)
+{
+	struct gfs2_inum inum;
+	uint64_t bn;
+	struct gfs2_buffer_head *bh;
+
+	bn = dinode_alloc(sdp);
+	inum.no_formal_ino = sdp->md.next_inum++;
+	inum.no_addr = bn;
+
+	bh = init_dinode(sdp, &inum, S_IFDIR | 0755, 0, &inum);
+	sdp->md.rooti = inode_get(sdp, bh);
+
+	if (sdp->debug) {
+		printf("\nRoot directory:\n");
+		gfs2_dinode_print(&sdp->md.rooti->i_di);
+	}
+	sdp->md.rooti->bh_owned = 1;
+	return 0;
+}
+
+int do_init_inum(struct gfs2_sbd *sdp)
+{
+	struct gfs2_inode *ip = sdp->md.inum;
+	uint64_t buf;
+	int count;
+
+	buf = cpu_to_be64(sdp->md.next_inum);
+	count = gfs2_writei(ip, &buf, 0, sizeof(uint64_t));
+	if (count != sizeof(uint64_t))
+		return -1;
+
+	if (sdp->debug)
+		printf("\nNext Inum: %"PRIu64"\n",
+		       sdp->md.next_inum);
+	return 0;
+}
+
+int do_init_statfs(struct gfs2_sbd *sdp)
+{
+	struct gfs2_inode *ip = sdp->md.statfs;
+	struct gfs2_statfs_change sc;
+	char buf[sizeof(struct gfs2_statfs_change)];
+	int count;
+
+	sc.sc_total = sdp->blks_total;
+	sc.sc_free = sdp->blks_total - sdp->blks_alloced;
+	sc.sc_dinodes = sdp->dinodes_alloced;
+
+	gfs2_statfs_change_out(&sc, buf);
+	count = gfs2_writei(ip, buf, 0, sizeof(struct gfs2_statfs_change));
+	if (count != sizeof(struct gfs2_statfs_change))
+		return -1;
+
+	if (sdp->debug) {
+		printf("\nStatfs:\n");
+		gfs2_statfs_change_print(&sc);
+	}
+	return 0;
+}
+
+int gfs2_check_meta(struct gfs2_buffer_head *bh, int type)
+{
+	uint32_t check_magic = ((struct gfs2_meta_header *)(bh->b_data))->mh_magic;
+	uint32_t check_type = ((struct gfs2_meta_header *)(bh->b_data))->mh_type;
+
+	check_magic = be32_to_cpu(check_magic);
+	check_type = be32_to_cpu(check_type);
+	if((check_magic != GFS2_MAGIC) || (type && (check_type != type)))
+		return -1;
+	return 0;
+}
+
+/**
+ * gfs2_next_rg_meta
+ * @rgd:
+ * @block:
+ * @first: if set, start at zero and ignore block
+ *
+ * The position to start looking from is *block.  When a block
+ * is found, it is returned in block.
+ *
+ * Returns: 0 on success, -1 when finished
+ */
+static int __gfs2_next_rg_meta(struct rgrp_tree *rgd, uint64_t *block,
+			       int first, unsigned char state)
+{
+	struct gfs2_bitmap *bits = NULL;
+	uint32_t length = rgd->ri.ri_length;
+	uint32_t blk = (first)? 0: (uint32_t)((*block + 1) - rgd->ri.ri_data0);
+	int i;
+
+	if (!first && (*block < rgd->ri.ri_data0)) {
+		log_err("next_rg_meta:  Start block is outside rgrp bounds.\n");
+		exit(1);
+	}
+	for (i = 0; i < length; i++){
+		bits = &rgd->bits[i];
+		if (blk < bits->bi_len * GFS2_NBBY)
+			break;
+		blk -= bits->bi_len * GFS2_NBBY;
+	}
+	for (; i < length; i++){
+		bits = &rgd->bits[i];
+		blk = gfs2_bitfit((unsigned char *)rgd->bh[i]->b_data +
+				  bits->bi_offset, bits->bi_len, blk, state);
+		if(blk != BFITNOENT){
+			*block = blk + (bits->bi_start * GFS2_NBBY) +
+				rgd->ri.ri_data0;
+			break;
+		}
+		blk = 0;
+	}
+	if (i == length)
+		return -1;
+	return 0;
+}
+
+int gfs2_next_rg_meta(struct rgrp_tree *rgd, uint64_t *block, int first)
+{
+	return __gfs2_next_rg_meta(rgd, block, first, GFS2_BLKST_DINODE);
+}
+
+int gfs2_next_rg_freemeta(struct rgrp_tree *rgd, uint64_t *block, int first)
+{
+	return __gfs2_next_rg_meta(rgd, block, first, GFS2_BLKST_UNLINKED);
+}
+
+/**
+ * next_rg_metatype
+ * @rgd:
+ * @block:
+ * @type: the type of metadata we're looking for
+ * @first: if set we should start at block zero and block is ignored
+ *
+ * Returns: 0 on success, -1 on error or finished
+ */
+int gfs2_next_rg_metatype(struct gfs2_sbd *sdp, struct rgrp_tree *rgd,
+			  uint64_t *block, uint32_t type, int first)
+{
+	struct gfs2_buffer_head *bh = NULL;
+
+	do{
+		if (bh)
+			brelse(bh);
+		if (gfs2_next_rg_meta(rgd, block, first))
+			return -1;
+		bh = bread(sdp, *block);
+		first = 0;
+	} while(gfs2_check_meta(bh, type));
+	brelse(bh);
+	return 0;
+}
diff --git a/gfs2/fsck/super.c b/gfs2/fsck/super.c
new file mode 100644
index 0000000..8f49dff
--- /dev/null
+++ b/gfs2/fsck/super.c
@@ -0,0 +1,327 @@
+#include <unistd.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include "libgfs2.h"
+#include "osi_list.h"
+
+/**
+ * check_sb - Check superblock
+ * @sdp: the filesystem
+ * @sb: The superblock
+ *
+ * Checks the version code of the FS is one that we understand how to
+ * read and that the sizes of the various on-disk structures have not
+ * changed.
+ *
+ * Returns: -1 on failure, 1 if this is gfs (gfs1), 2 if this is gfs2
+ */
+int check_sb(struct gfs2_sb *sb, int allow_gfs)
+{
+	if (sb->sb_header.mh_magic != GFS2_MAGIC ||
+	    sb->sb_header.mh_type != GFS2_METATYPE_SB) {
+		log_crit("Either the super block is corrupted, or this "
+				 "is not a GFS2 filesystem\n");
+		log_debug("Header magic: %X Header Type: %X\n",
+				  sb->sb_header.mh_magic,
+				  sb->sb_header.mh_type);
+		return -EINVAL;
+	}
+	if (sb->sb_fs_format == GFS_FORMAT_FS &&
+	    sb->sb_header.mh_format == GFS_FORMAT_SB &&
+	    sb->sb_multihost_format == GFS_FORMAT_MULTI) {
+		if (allow_gfs)
+			return 1;
+
+		log_crit("Old gfs1 file system detected.\n");
+		return -EINVAL;
+	}
+	return 2;
+}
+
+
+/*
+ * read_sb: read the super block from disk
+ * sdp: in-core super block
+ *
+ * This function reads in the super block from disk and
+ * initializes various constants maintained in the super
+ * block
+ *
+ * allow_gfs - passed in as 1 if we're allowed to accept gfs1 file systems
+ *
+ * Returns: 0 on success, -1 on failure
+ * sdp->gfs1 will be set if this is gfs (gfs1)
+ */
+int read_sb(struct gfs2_sbd *sdp, int allow_gfs)
+{
+	struct gfs2_buffer_head *bh;
+	uint64_t space = 0;
+	unsigned int x;
+	int ret;
+
+	bh = bread(sdp, GFS2_SB_ADDR >> sdp->sd_fsb2bb_shift);
+	gfs2_sb_in(&sdp->sd_sb, bh);
+	brelse(bh);
+
+	ret = check_sb(&sdp->sd_sb, allow_gfs);
+	if (ret < 0)
+		return ret;
+	if (ret == 1)
+		sdp->gfs1 = 1;
+	sdp->sd_fsb2bb_shift = sdp->sd_sb.sb_bsize_shift - GFS2_BASIC_BLOCK_SHIFT;
+	sdp->bsize = sdp->sd_sb.sb_bsize;
+	if (sdp->gfs1) {
+		sdp->sd_diptrs = (sdp->sd_sb.sb_bsize -
+				  sizeof(struct gfs_dinode)) /
+			sizeof(uint64_t);
+		sdp->sd_inptrs = (sdp->sd_sb.sb_bsize -
+				  sizeof(struct gfs_indirect)) /
+			sizeof(uint64_t);
+	} else {
+		sdp->sd_diptrs = (sdp->sd_sb.sb_bsize -
+				  sizeof(struct gfs2_dinode)) /
+			sizeof(uint64_t);
+		sdp->sd_inptrs = (sdp->sd_sb.sb_bsize -
+				  sizeof(struct gfs2_meta_header)) /
+			sizeof(uint64_t);
+	}
+	sdp->sd_jbsize = sdp->sd_sb.sb_bsize - sizeof(struct gfs2_meta_header);
+	sdp->sd_hash_bsize = sdp->bsize / 2;
+	sdp->sd_hash_bsize_shift = sdp->sd_sb.sb_bsize_shift - 1;
+	sdp->sd_hash_ptrs = sdp->sd_hash_bsize / sizeof(uint64_t);
+	sdp->sd_heightsize[0] = sdp->sd_sb.sb_bsize - sizeof(struct gfs2_dinode);
+	sdp->sd_heightsize[1] = sdp->sd_sb.sb_bsize * sdp->sd_diptrs;
+	for (x = 2; ; x++){
+		space = sdp->sd_heightsize[x - 1] * sdp->sd_inptrs;
+		/* FIXME: Do we really need this first check?? */
+		if (space / sdp->sd_inptrs != sdp->sd_heightsize[x - 1] ||
+		    space % sdp->sd_inptrs != 0)
+			break;
+		sdp->sd_heightsize[x] = space;
+	}
+	if (x > GFS2_MAX_META_HEIGHT){
+		log_err("Bad max metadata height.\n");
+		return -1;
+	}
+
+	sdp->sd_jheightsize[0] = sdp->sd_sb.sb_bsize - sizeof(struct gfs2_dinode);
+	sdp->sd_jheightsize[1] = sdp->sd_jbsize * sdp->sd_diptrs;
+	for (x = 2; ; x++){
+		space = sdp->sd_jheightsize[x - 1] * sdp->sd_inptrs;
+		if (space / sdp->sd_inptrs != sdp->sd_jheightsize[x - 1] ||
+			space % sdp->sd_inptrs != 0)
+			break;
+		sdp->sd_jheightsize[x] = space;
+	}
+	sdp->sd_max_jheight = x;
+	if(sdp->sd_max_jheight > GFS2_MAX_META_HEIGHT) {
+		log_err("Bad max jheight.\n");
+		return -1;
+	}
+	sdp->fssize = lseek(sdp->device_fd, 0, SEEK_END) / sdp->sd_sb.sb_bsize;
+	sdp->sb_addr = GFS2_SB_ADDR * GFS2_BASIC_BLOCK / sdp->bsize;
+
+	return 0;
+}
+
+/**
+ * rindex_read - read in the rg index file
+ * @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.
+ * @sane: return whether rindex is consistent
+ *
+ * Returns: 0 on success, -1 on failure
+ */
+int rindex_read(struct gfs2_sbd *sdp, int fd, int *count1, int *sane)
+{
+	unsigned int rg;
+	int error;
+	union {
+		struct gfs_rindex bufgfs1;
+		struct gfs2_rindex bufgfs2;
+	} buf;
+	struct gfs2_rindex ri;
+	struct rgrp_tree *rgd = NULL, *prev_rgd = NULL;
+	uint64_t prev_length = 0;
+
+	*sane = 1;
+	*count1 = 0;
+	if (!fd && sdp->md.riinode->i_di.di_size % sizeof(struct gfs2_rindex))
+		*sane = 0; /* rindex file size must be a multiple of 96 */
+	for (rg = 0; ; rg++) {
+		if (fd > 0)
+			error = read(fd, &buf, sizeof(struct gfs2_rindex));
+		else
+			error = gfs2_readi(sdp->md.riinode,
+					   (char *)&buf.bufgfs2,
+					   rg * sizeof(struct gfs2_rindex),
+					   sizeof(struct gfs2_rindex));
+		if (!error)
+			break;
+		if (error != sizeof(struct gfs2_rindex))
+			return -1;
+
+		gfs2_rindex_in(&ri, (char *)&buf.bufgfs2);
+		rgd = rgrp_insert(&sdp->rgtree, ri.ri_addr);
+		memcpy(&rgd->ri, &ri, sizeof(struct gfs2_rindex));
+
+		rgd->start = rgd->ri.ri_addr;
+		if (prev_rgd) {
+			/* If rg addresses go backwards, it's not sane
+			   (or it's converted from gfs1). */
+			if (!sdp->gfs1) {
+				if (prev_rgd->start >= rgd->start)
+					*sane = 0;
+				/* If rg lengths are not consistent, it's not
+				   sane (or it's converted from gfs1).  The
+				   first RG will be a different length due to
+				   space allocated for the superblock, so we
+				   can't detect this until we check rgrp 3,
+				   when we can compare the distance between
+				   rgrp 1 and rgrp 2. */
+				if (rg > 2 && prev_length &&
+				    prev_length != rgd->start -
+				    prev_rgd->start)
+					*sane = 0;
+			}
+			prev_length = rgd->start - prev_rgd->start;
+			prev_rgd->length = rgrp_size(prev_rgd);
+		}
+
+		if(gfs2_compute_bitstructs(sdp, rgd))
+			*sane = 0;
+
+		(*count1)++;
+		prev_rgd = rgd;
+	}
+	if (prev_rgd)
+		prev_rgd->length = rgrp_size(prev_rgd);
+	return 0;
+}
+
+#define RA_WINDOW 32
+
+static unsigned gfs2_rgrp_reada(struct gfs2_sbd *sdp, unsigned cur_window,
+				struct osi_node *n)
+{
+	struct rgrp_tree *rgd;
+	unsigned i;
+	off_t start, len;
+
+	for (i = 0; i < RA_WINDOW; i++, n = osi_next(n)) {
+		if (n == NULL)
+			return i;
+		if (i < cur_window)
+			continue;
+		rgd = (struct rgrp_tree *)n;
+		start = rgd->ri.ri_addr * sdp->bsize;
+		len = rgd->ri.ri_length * sdp->bsize;
+		posix_fadvise(sdp->device_fd, start, len, POSIX_FADV_WILLNEED);
+	}
+
+	return i;
+}
+
+/**
+ * ri_update - attach rgrps to the super block
+ * @sdp: incore superblock data
+ * fd: optional file handle for rindex (through the meta_fs)
+ * @rgcount: returned count of rgs
+ *
+ * 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.
+ */
+static int __ri_update(struct gfs2_sbd *sdp, int fd, int *rgcount, int *sane,
+		       int quiet)
+{
+	struct rgrp_tree *rgd;
+	struct gfs2_rindex *ri;
+	int count1 = 0, count2 = 0;
+	uint64_t errblock = 0;
+	uint64_t rmax = 0;
+	struct osi_node *n, *next = NULL;
+	unsigned ra_window = 0;
+
+	/* Turn off generic readhead */
+	posix_fadvise(sdp->device_fd, 0, 0, POSIX_FADV_RANDOM);
+
+	if (rindex_read(sdp, fd, &count1, sane))
+		goto fail;
+	for (n = osi_first(&sdp->rgtree); n; n = next) {
+		next = osi_next(n);
+		rgd = (struct rgrp_tree *)n;
+		/* Readahead resource group headers */
+		if (ra_window < RA_WINDOW/2)
+			ra_window = gfs2_rgrp_reada(sdp, ra_window, n);
+		/* Read resource group header */
+		errblock = gfs2_rgrp_read(sdp, rgd);
+		if (errblock)
+			return errblock;
+		ra_window--;
+		count2++;
+		if (!quiet && count2 % 100 == 0) {
+			printf(".");
+			fflush(stdout);
+		}
+		ri = &rgd->ri;
+		if (ri->ri_data0 + ri->ri_data - 1 > rmax)
+			rmax = ri->ri_data0 + ri->ri_data - 1;
+	}
+
+	sdp->fssize = rmax;
+	*rgcount = count1;
+	if (count1 != count2)
+		goto fail;
+
+	posix_fadvise(sdp->device_fd, 0, 0, POSIX_FADV_NORMAL);
+	return 0;
+
+ fail:
+	posix_fadvise(sdp->device_fd, 0, 0, POSIX_FADV_NORMAL);
+	gfs2_rgrp_free(&sdp->rgtree);
+	return -1;
+}
+
+int ri_update(struct gfs2_sbd *sdp, int fd, int *rgcount, int *sane)
+{
+	return __ri_update(sdp, fd, rgcount, sane, 1);
+}
+
+/**
+ * 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, int quiet)
+{
+	int sane;
+
+	return __ri_update(sdp, fd, rgcount, &sane, quiet);
+}
+
+int write_sb(struct gfs2_sbd *sbp)
+{
+	struct gfs2_buffer_head *bh;
+
+	bh = bread(sbp, GFS2_SB_ADDR >> sbp->sd_fsb2bb_shift);
+	gfs2_sb_out(&sbp->sd_sb, bh);
+	brelse(bh);
+	fsync(sbp->device_fd); /* make sure the change gets to disk ASAP */
+	return 0;
+}
+
diff --git a/gfs2/fsck/util.c b/gfs2/fsck/util.c
index d95461b..05989fe 100644
--- a/gfs2/fsck/util.c
+++ b/gfs2/fsck/util.c
@@ -1,19 +1,4 @@
-/******************************************************************************
-*******************************************************************************
-**
-**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
-**  Copyright (C) 2004-2005 Red Hat, Inc.  All rights reserved.
-**
-**  This copyrighted material is made available to anyone wishing to use,
-**  modify, copy, or redistribute it subject to the terms and conditions
-**  of the GNU General Public License v.2.
-**
-*******************************************************************************
-******************************************************************************/
-
 #include <inttypes.h>
-#include <linux_endian.h>
-#include <stdarg.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
@@ -21,8 +6,6 @@
 #include <stdio.h>
 #include <libintl.h>
 #include <ctype.h>
-#include <errno.h>
-#include <sys/stat.h>
 #define _(String) gettext(String)
 
 #include "libgfs2.h"
@@ -47,7 +30,7 @@ void big_file_comfort(struct gfs2_inode *ip, uint64_t blks_checked)
 	if (blks_checked - last_reported_fblock < one_percent)
 		return;
 
-	last_reported_block = blks_checked;
+	last_reported_fblock = blks_checked;
 	gettimeofday(&tv, NULL);
 	if (!seconds)
 		seconds = tv.tv_sec;
@@ -82,7 +65,8 @@ void warm_fuzzy_stuff(uint64_t block)
 
 	if (!one_percent)
 		one_percent = last_fs_block / 100;
-	if (block - last_reported_block >= one_percent) {
+	if (!last_reported_block ||
+	    block - last_reported_block >= one_percent) {
 		last_reported_block = block;
 		gettimeofday(&tv, NULL);
 		if (!seconds)
@@ -93,7 +77,8 @@ void warm_fuzzy_stuff(uint64_t block)
 			seconds = tv.tv_sec;
 			if (last_fs_block) {
 				percent = (block * 100) / last_fs_block;
-				log_notice( _("\r%" PRIu64 " percent complete.\r"), percent);
+				log_notice( _("\r%llu percent complete.\r"),
+					   (unsigned long long)percent);
 				fflush(stdout);
 			}
 		}
@@ -171,7 +156,7 @@ int fsck_query(const char *format, ...)
 static struct duptree *gfs2_dup_set(uint64_t dblock, int create)
 {
 	struct osi_node **newn = &dup_blocks.osi_node, *parent = NULL;
-	struct duptree *data;
+	struct duptree *dt;
 
 	/* Figure out where to put new node */
 	while (*newn) {
@@ -188,20 +173,20 @@ static struct duptree *gfs2_dup_set(uint64_t dblock, int create)
 
 	if (!create)
 		return NULL;
-	data = malloc(sizeof(struct duptree));
+	dt = malloc(sizeof(struct duptree));
 	dups_found++;
-	memset(data, 0, sizeof(struct duptree));
+	memset(dt, 0, sizeof(struct duptree));
 	/* Add new node and rebalance tree. */
-	data->block = dblock;
-	data->refs = 1; /* reference 1 is actually the reference we need to
-			   discover in pass1b. */
-	data->first_ref_found = 0;
-	osi_list_init(&data->ref_inode_list);
-	osi_list_init(&data->ref_invinode_list);
-	osi_link_node(&data->node, parent, newn);
-	osi_insert_color(&data->node, &dup_blocks);
-
-	return data;
+	dt->block = dblock;
+	dt->refs = 1; /* reference 1 is actually the reference we need to
+			 discover in pass1b. */
+	dt->first_ref_found = 0;
+	osi_list_init(&dt->ref_inode_list);
+	osi_list_init(&dt->ref_invinode_list);
+	osi_link_node(&dt->node, parent, newn);
+	osi_insert_color(&dt->node, &dup_blocks);
+
+	return dt;
 }
 
 /**
@@ -249,19 +234,41 @@ int add_duplicate_ref(struct gfs2_inode *ip, uint64_t block,
 	struct duptree *dt;
 
 	if (!valid_block(ip->i_sbd, block))
-		return 0;
+		return meta_is_good;
 	/* If this is not the first reference (i.e. all calls from pass1) we
 	   need to create the duplicate reference. If this is pass1b, we want
 	   to ignore references that aren't found. */
 	dt = gfs2_dup_set(block, !first);
 	if (!dt)        /* If this isn't a duplicate */
-		return 0;
+		return meta_is_good;
 
 	/* If we found the duplicate reference but we've already discovered
 	   the first reference (in pass1b) and the other references in pass1,
 	   we don't need to count it, so just return. */
 	if (dt->first_ref_found)
-		return 0;
+		return meta_is_good;
+
+	/* Check for a previous reference to this duplicate */
+	id = find_dup_ref_inode(dt, ip);
+
+	/* We have to be careful here. The original referencing dinode may have
+	   deemed to be bad and deleted/freed in pass1. In that case, pass1b
+	   wouldn't discover the correct [deleted] original reference. In
+	   that case, we don't want to be confused and consider this second
+	   reference the same as the first. If we do, we'll never be able to
+	   resolve it. The first reference can't be the second reference. */
+	if (id && first && !dt->first_ref_found) {
+		log_info(_("Original reference to block %llu (0x%llx) was "
+			   "previously found to be bad and deleted.\n"),
+			 (unsigned long long)block,
+			 (unsigned long long)block);
+		log_info(_("I'll consider the reference from inode %llu "
+			   "(0x%llx) the first reference.\n"),
+			 (unsigned long long)ip->i_di.di_num.no_addr,
+			 (unsigned long long)ip->i_di.di_num.no_addr);
+		dt->first_ref_found = 1;
+		return meta_is_good;
+	}
 
 	/* The first time this is called from pass1 is actually the second
 	   reference.  When we go back in pass1b looking for the original
@@ -274,8 +281,6 @@ int add_duplicate_ref(struct gfs2_inode *ip, uint64_t block,
 		dt->refs++;
 	}
 
-	/* Check for a previous reference to this duplicate */
-	id = find_dup_ref_inode(dt, ip);
 	if (id == NULL) {
 		/* Check for the inode on the invalid inode reference list. */
 		uint8_t q;
@@ -283,12 +288,12 @@ int add_duplicate_ref(struct gfs2_inode *ip, uint64_t block,
 		if (!(id = malloc(sizeof(*id)))) {
 			log_crit( _("Unable to allocate "
 				    "inode_with_dups structure\n"));
-			return -1;
+			return meta_error;
 		}
 		if (!(memset(id, 0, sizeof(*id)))) {
 			log_crit( _("Unable to zero inode_with_dups "
 				    "structure\n"));
-			return -1;
+			return meta_error;
 		}
 		id->block_no = ip->i_di.di_num.no_addr;
 		q = block_type(ip->i_di.di_num.no_addr);
@@ -312,9 +317,10 @@ int add_duplicate_ref(struct gfs2_inode *ip, uint64_t block,
 	id->reftypecount[reftype]++;
 	id->dup_count++;
 	log_info( _("Found %d reference(s) to block %llu"
-		    " (0x%llx) as %s in inode #%llu (0x%llx)\n"),
+		    " (0x%llx) as %s in %s inode #%llu (0x%llx)\n"),
 		  id->dup_count, (unsigned long long)block,
 		  (unsigned long long)block, reftypes[reftype],
+		  inode_valid ? _("valid") : _("invalid"),
 		  (unsigned long long)ip->i_di.di_num.no_addr,
 		  (unsigned long long)ip->i_di.di_num.no_addr);
 	if (first)
@@ -322,7 +328,7 @@ int add_duplicate_ref(struct gfs2_inode *ip, uint64_t block,
 	else
 		log_info( _("This brings the total to: %d duplicate "
 			    "references\n"), dt->refs);
-	return 0;
+	return meta_is_good;
 }
 
 struct dir_info *dirtree_insert(struct gfs2_inum inum)
@@ -378,31 +384,62 @@ struct dir_info *dirtree_find(uint64_t block)
 	return NULL;
 }
 
-void dup_listent_delete(struct inode_with_dups *id)
+/* get_ref_type - figure out if all duplicate references from this inode
+   are the same type, and if so, return the type. */
+enum dup_ref_type get_ref_type(struct inode_with_dups *id)
+{
+	enum dup_ref_type t, i;
+	int found_type_with_ref;
+	int found_other_types;
+
+	for (t = ref_as_data; t < ref_types; t++) {
+		found_type_with_ref = 0;
+		found_other_types = 0;
+		for (i = ref_as_data; i < ref_types; i++) {
+			if (id->reftypecount[i]) {
+				if (t == i)
+					found_type_with_ref = 1;
+				else
+					found_other_types = 1;
+			}
+		}
+		if (found_type_with_ref)
+			return found_other_types ? ref_types : t;
+	}
+	return ref_types;
+}
+
+void dup_listent_delete(struct duptree *dt, struct inode_with_dups *id)
 {
+	log_err( _("Removing duplicate reference to block %llu (0x%llx) "
+		   "referenced as %s by dinode %llu (0x%llx)\n"),
+		 (unsigned long long)dt->block, (unsigned long long)dt->block,
+		 reftypes[get_ref_type(id)], (unsigned long long)id->block_no,
+		 (unsigned long long)id->block_no);
+	dt->refs--; /* one less reference */
 	if (id->name)
 		free(id->name);
 	osi_list_del(&id->list);
 	free(id);
 }
 
-void dup_delete(struct duptree *b)
+void dup_delete(struct duptree *dt)
 {
 	struct inode_with_dups *id;
 	osi_list_t *tmp;
 
-	while (!osi_list_empty(&b->ref_invinode_list)) {
-		tmp = (&b->ref_invinode_list)->next;
+	while (!osi_list_empty(&dt->ref_invinode_list)) {
+		tmp = (&dt->ref_invinode_list)->next;
 		id = osi_list_entry(tmp, struct inode_with_dups, list);
-		dup_listent_delete(id);
+		dup_listent_delete(dt, id);
 	}
-	while (!osi_list_empty(&b->ref_inode_list)) {
-		tmp = (&b->ref_inode_list)->next;
+	while (!osi_list_empty(&dt->ref_inode_list)) {
+		tmp = (&dt->ref_inode_list)->next;
 		id = osi_list_entry(tmp, struct inode_with_dups, list);
-		dup_listent_delete(id);
+		dup_listent_delete(dt, id);
 	}
-	osi_erase(&b->node, &dup_blocks);
-	free(b);
+	osi_erase(&dt->node, &dup_blocks);
+	free(dt);
 }
 
 void dirtree_delete(struct dir_info *b)
@@ -462,6 +499,8 @@ int gfs2_blockmap_set(struct gfs2_bmap *bmap, uint64_t bblock,
 	static unsigned char *byte;
 	static uint64_t b;
 
+	if (!bmap)
+		return 0;
 	if (bblock > bmap->size)
 		return -1;
 
@@ -492,9 +531,13 @@ void *gfs2_bmap_destroy(struct gfs2_sbd *sdp, struct gfs2_bmap *il)
 int set_ip_blockmap(struct gfs2_inode *ip, int instree)
 {
 	uint64_t block = ip->i_bh->b_blocknr;
+	struct gfs2_sbd *sdp = ip->i_sbd;
 	uint32_t mode;
 
-	mode = ip->i_di.di_mode & S_IFMT;
+	if (sdp->gfs1)
+		mode = gfs_to_gfs2_mode(ip);
+	else
+		mode = ip->i_di.di_mode & S_IFMT;
 
 	switch (mode) {
 	case S_IFDIR:
@@ -544,3 +587,120 @@ bad_dinode:
 	stack;
 	return -EPERM;
 }
+
+uint64_t find_free_blk(struct gfs2_sbd *sdp)
+{
+	struct osi_node *n, *next = NULL;
+	struct rgrp_tree *rl = NULL;
+	struct gfs2_rindex *ri;
+	struct gfs2_rgrp *rg;
+	unsigned int block, bn = 0, x = 0, y = 0;
+	unsigned int state;
+	struct gfs2_buffer_head *bh;
+
+	memset(&rg, 0, sizeof(rg));
+	for (n = osi_first(&sdp->rgtree); n; n = next) {
+		next = osi_next(n);
+		rl = (struct rgrp_tree *)n;
+		if (rl->rg.rg_free)
+			break;
+	}
+
+	if (n == NULL)
+		return 0;
+
+	ri = &rl->ri;
+	rg = &rl->rg;
+
+	for (block = 0; block < ri->ri_length; block++) {
+		bh = rl->bh[block];
+		x = (block) ? sizeof(struct gfs2_meta_header) : sizeof(struct gfs2_rgrp);
+
+		for (; x < sdp->bsize; x++)
+			for (y = 0; y < GFS2_NBBY; y++) {
+				state = (bh->b_data[x] >> (GFS2_BIT_SIZE * y)) & 0x03;
+				if (state == GFS2_BLKST_FREE)
+					return ri->ri_data0 + bn;
+				bn++;
+			}
+	}
+	return 0;
+}
+
+uint64_t *get_dir_hash(struct gfs2_inode *ip)
+{
+	unsigned hsize = (1 << ip->i_di.di_depth) * sizeof(uint64_t);
+	int ret;
+	uint64_t *tbl = malloc(hsize);
+
+	if (tbl == NULL)
+		return NULL;
+
+	ret = gfs2_readi(ip, tbl, 0, hsize);
+	if (ret != hsize) {
+		free(tbl);
+		return NULL;
+	}
+
+	return tbl;
+}
+
+void delete_all_dups(struct gfs2_inode *ip)
+{
+	struct osi_node *n, *next;
+	struct duptree *dt;
+	osi_list_t *tmp, *x;
+	struct inode_with_dups *id;
+	int found;
+
+	for (n = osi_first(&dup_blocks); n; n = next) {
+		next = osi_next(n);
+		dt = (struct duptree *)n;
+
+		found = 0;
+		id = NULL;
+
+		osi_list_foreach_safe(tmp, &dt->ref_invinode_list, x) {
+			id = osi_list_entry(tmp, struct inode_with_dups, list);
+			if (id->block_no == ip->i_di.di_num.no_addr) {
+				dup_listent_delete(dt, id);
+				found = 1;
+			}
+		}
+		osi_list_foreach_safe(tmp, &dt->ref_inode_list, x) {
+			id = osi_list_entry(tmp, struct inode_with_dups, list);
+			if (id->block_no == ip->i_di.di_num.no_addr) {
+				dup_listent_delete(dt, id);
+				found = 1;
+			}
+		}
+		if (!found)
+			continue;
+
+		if (dt->refs == 0) {
+			log_debug(_("This was the last reference: 0x%llx is "
+				    "no longer a duplicate.\n"),
+				  (unsigned long long)dt->block);
+			dup_delete(dt); /* not duplicate now */
+		} else {
+			log_debug(_("%d references remain to 0x%llx\n"),
+				  dt->refs, (unsigned long long)dt->block);
+			if (dt->refs > 1)
+				continue;
+
+			id = NULL;
+			osi_list_foreach(tmp, &dt->ref_invinode_list)
+				id = osi_list_entry(tmp,
+						    struct inode_with_dups,
+						    list);
+			osi_list_foreach(tmp, &dt->ref_inode_list)
+				id = osi_list_entry(tmp,
+						    struct inode_with_dups,
+						    list);
+			if (id)
+				log_debug("Last reference is from inode "
+					  "0x%llx\n",
+					  (unsigned long long)id->block_no);
+		}
+	}
+}
diff --git a/gfs2/fsck/util.h b/gfs2/fsck/util.h
index e4bee0a..fe3fd6a 100644
--- a/gfs2/fsck/util.h
+++ b/gfs2/fsck/util.h
@@ -1,19 +1,8 @@
-/******************************************************************************
-*******************************************************************************
-**
-**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
-**  Copyright (C) 2004-2005 Red Hat, Inc.  All rights reserved.
-**
-**  This copyrighted material is made available to anyone wishing to use,
-**  modify, copy, or redistribute it subject to the terms and conditions
-**  of the GNU General Public License v.2.
-**
-*******************************************************************************
-******************************************************************************/
-
 #ifndef __UTIL_H__
 #define __UTIL_H__
 
+#include <sys/stat.h>
+
 #include "fsck.h"
 #include "libgfs2.h"
 
@@ -23,7 +12,6 @@
 #define INODE_VALID 1
 #define INODE_INVALID 0
 
-int compute_height(struct gfs2_sbd *sdp, uint64_t sz);
 struct di_info *search_list(osi_list_t *list, uint64_t addr);
 void big_file_comfort(struct gfs2_inode *ip, uint64_t blks_checked);
 void warm_fuzzy_stuff(uint64_t block);
@@ -31,7 +19,7 @@ int add_duplicate_ref(struct gfs2_inode *ip, uint64_t block,
 		      enum dup_ref_type reftype, int first, int inode_valid);
 extern struct inode_with_dups *find_dup_ref_inode(struct duptree *dt,
 						  struct gfs2_inode *ip);
-extern void dup_listent_delete(struct inode_with_dups *id);
+extern void dup_listent_delete(struct duptree *dt, struct inode_with_dups *id);
 
 extern const char *reftypes[ref_types + 1];
 
@@ -66,7 +54,7 @@ enum gfs2_mark_block {
 	gfs2_jdata         = (0xa), /* gfs journaled data blocks */
 	gfs2_meta_inval    = (0xb),
 	gfs2_leaf_blk      = (0xc),
-
+	gfs2_freemeta      = (0xd), /* was: gfs2_meta_rgrp */
 	gfs2_meta_eattr    = (0xe),
 
 	gfs2_bad_block     = (0xf), /* Contains at least one bad block */
@@ -77,7 +65,7 @@ static const inline char *block_type_string(uint8_t q)
 	const char *blktyp[] = {
 		"free",
 		"data",
-		"indirect data",
+		"indirect meta",
 		"directory",
 		"file",
 
@@ -90,7 +78,7 @@ static const inline char *block_type_string(uint8_t q)
 		"journaled data",
 		"invalid meta",
 		"dir leaf",
-		"",
+		"free metadata",
 		"eattribute",
 
 		"bad"};
@@ -101,36 +89,100 @@ static const inline char *block_type_string(uint8_t q)
 
 /* Must be kept in sync with gfs2_mark_block enum above. Blocks marked as
    invalid or bad are considered metadata until actually freed. */
-static inline int blockmap_to_bitmap(enum gfs2_mark_block m)
+static inline int blockmap_to_bitmap(enum gfs2_mark_block m, int gfs1)
 {
-	static int bitmap_states[16] = {
-		GFS2_BLKST_FREE,  /* free */
-		GFS2_BLKST_USED,  /* data */
-		GFS2_BLKST_USED,  /* indirect data or rgrp meta */
-		GFS2_BLKST_DINODE,  /* directory */
-		GFS2_BLKST_DINODE,  /* file */
-
-		GFS2_BLKST_DINODE,  /* symlink */
-		GFS2_BLKST_DINODE,  /* block or char device */
-		GFS2_BLKST_DINODE,  /* fifo */
-		GFS2_BLKST_DINODE,  /* socket */
-		GFS2_BLKST_FREE,  /* invalid inode */
-
-		GFS2_BLKST_USED,    /* journaled data */
-		GFS2_BLKST_FREE,  /* invalid meta */
-		GFS2_BLKST_USED,  /* dir leaf */
-		GFS2_BLKST_UNLINKED,  /* GFS unlinked metadata */
-		GFS2_BLKST_USED,  /* eattribute */
-
-		GFS2_BLKST_DINODE}; /* bad */
-
-	return bitmap_states[m];
+	static int bitmap_states[2][16] = {
+		/* ---------------------- gfs2 ------------------------------*/
+		{GFS2_BLKST_FREE,  /* free */
+		 GFS2_BLKST_USED,  /* data */
+		 GFS2_BLKST_USED,  /* indirect data or rgrp meta */
+		 GFS2_BLKST_DINODE,  /* directory */
+		 GFS2_BLKST_DINODE,  /* file */
+
+		 GFS2_BLKST_DINODE,  /* symlink */
+		 GFS2_BLKST_DINODE,  /* block or char device */
+		 GFS2_BLKST_DINODE,  /* fifo */
+		 GFS2_BLKST_DINODE,  /* socket */
+		 GFS2_BLKST_FREE,  /* invalid inode */
+
+		 GFS2_BLKST_USED,    /* journaled data */
+		 GFS2_BLKST_FREE,  /* invalid meta */
+		 GFS2_BLKST_USED,  /* dir leaf */
+		 GFS2_BLKST_UNLINKED,  /* GFS unlinked metadata */
+		 GFS2_BLKST_USED,  /* eattribute */
+
+		 GFS2_BLKST_DINODE}, /* bad */
+		/* ---------------------- gfs1 ----------------------------- */
+		{GFS2_BLKST_FREE,  /* free */
+		 GFS2_BLKST_USED,  /* data */
+		 GFS2_BLKST_DINODE,  /* indirect data or rgrp meta*/
+		 GFS2_BLKST_DINODE,  /* directory */
+		 GFS2_BLKST_DINODE,  /* file */
+
+		 GFS2_BLKST_DINODE,  /* symlink */
+		 GFS2_BLKST_DINODE,  /* block or char device */
+		 GFS2_BLKST_DINODE,  /* fifo */
+		 GFS2_BLKST_DINODE,  /* socket */
+		 GFS2_BLKST_FREE,  /* invalid inode */
+
+		 GFS2_BLKST_DINODE,  /* journaled data */
+		 GFS2_BLKST_FREE,  /* invalid meta */
+		 GFS2_BLKST_DINODE,  /* dir leaf */
+		 GFS2_BLKST_UNLINKED, /* GFS unlinked metadata */
+		 GFS2_BLKST_DINODE,  /* eattribute */
+
+		 GFS2_BLKST_DINODE}}; /* bad */
+	return bitmap_states[gfs1][m];
 }
 
+static inline int is_dir(struct gfs2_dinode *dinode, int gfs1)
+{
+	if (gfs1 && is_gfs_dir(dinode))
+		return 1;
+	if (S_ISDIR(dinode->di_mode))
+		return 1;
+
+	return 0;
+}
+
+static inline uint32_t gfs_to_gfs2_mode(struct gfs2_inode *ip)
+{
+	uint16_t gfs1mode = ip->i_di.__pad1;
+
+	switch (gfs1mode) {
+	case GFS_FILE_DIR:
+		return S_IFDIR;
+	case GFS_FILE_REG:
+		return S_IFREG;
+	case GFS_FILE_LNK:
+		return S_IFLNK;
+	case GFS_FILE_BLK:
+		return S_IFBLK;
+	case GFS_FILE_CHR:
+		return S_IFCHR;
+	case GFS_FILE_FIFO:
+		return S_IFIFO;
+	case GFS_FILE_SOCK:
+		return S_IFSOCK;
+	default:
+		/* This could be an aborted gfs2_convert so look for both. */
+		if (ip->i_di.di_entries ||
+		    (ip->i_di.di_mode & S_IFMT) == S_IFDIR)
+			return S_IFDIR;
+		else
+			return S_IFREG;
+	}
+}
+
+extern enum dup_ref_type get_ref_type(struct inode_with_dups *id);
 extern struct gfs2_bmap *gfs2_bmap_create(struct gfs2_sbd *sdp, uint64_t size,
 					  uint64_t *addl_mem_needed);
 extern void *gfs2_bmap_destroy(struct gfs2_sbd *sdp, struct gfs2_bmap *il);
 extern int gfs2_blockmap_set(struct gfs2_bmap *il, uint64_t block,
 			     enum gfs2_mark_block mark);
 extern int set_ip_blockmap(struct gfs2_inode *ip, int instree);
+extern uint64_t find_free_blk(struct gfs2_sbd *sdp);
+extern uint64_t *get_dir_hash(struct gfs2_inode *ip);
+extern void delete_all_dups(struct gfs2_inode *ip);
+
 #endif /* __UTIL_H__ */


More information about the cluster-commits mailing list