File gfs2_convert_corrupts_file_system_when_directory_has_di_height_3.patch of Package cluster

commit 3f305725249969841ca195f473c533a7d37b702b
Author: Abhijith Das <adas@redhat.com>
Date:   Thu Oct 14 10:47:56 2010 -0500

    gfs2_convert: corrupts file system when directory has di_height 3
    
    This patch fixes the conversion of directory inodes with height >= 2.
    It moves a lot of code around, mainly because two functions
    (adjust_indirect_blocks) and (adjust_jdata_inode) were doing a lot of the same
    things and the latter function was too long. Adding support for directory
    conversion would've added a third function that would duplicate a lot of the
    same code... so I consolidated the common things into two smaller functions,
    now called adjust_indirect_blocks() and get_metablocks(). The file-type
    specific functions are now in two separate functions fix_ind_jdata() and
    fix_ind_reg_or_dir().
    
    Resolves: rhbz#630005
    Signed-off-by: Abhi Das <adas@redhat.com>

diff --git a/gfs2/convert/gfs2_convert.c b/gfs2/convert/gfs2_convert.c
index f84de84..e71a00c 100644
--- a/gfs2/convert/gfs2_convert.c
+++ b/gfs2/convert/gfs2_convert.c
@@ -417,180 +417,6 @@ static void fix_metatree(struct gfs2_sbd *sbp, struct gfs2_inode *ip,
 /*                                                                           */
 /* Adapted from gfs2_fsck metawalk.c's build_and_check_metalist              */
 /* ------------------------------------------------------------------------- */
-static int adjust_indirect_blocks(struct gfs2_sbd *sbp, struct gfs2_inode *ip)
-{
-	uint32_t gfs2_hgt;
-	struct gfs2_buffer_head *bh;
-	osi_list_t *tmp, *x;
-	int h, header_size, bufsize, ptrnum;
-	uint64_t *ptr1, block;
-	uint64_t dinode_size;
-	int error = 0, di_height;
-	struct blocklist blocks, *blk, *newblk;
-	struct metapath gfs2mp;
-	struct gfs2_buffer_head *dibh = ip->i_bh;
-
-	/* if there are no indirect blocks to check */
-	if (ip->i_di.di_height <= 1)
-		return 0;
-
-	osi_list_init(&blocks.list);
-
-	/* Add the dinode block to the blocks list */
-	blk = malloc(sizeof(struct blocklist));
-	if (!blk) {
-		log_crit("Error: Can't allocate memory"
-			 " for indirect block fix.\n");
-		return -1;
-	}
-	memset(blk, 0, sizeof(*blk));
-	/* allocate a buffer to hold the pointers */
-	bufsize = sbp->sd_inptrs * sizeof(uint64_t);
-	blk->block = dibh->b_blocknr;
-	blk->ptrbuf = malloc(bufsize);
-	if (!blk->ptrbuf) {
-		log_crit("Error: Can't allocate memory"
-			 " for file conversion.\n");
-		free(blk);
-		return -1;
-	}
-	memset(blk->ptrbuf, 0, bufsize);
-	/* Fill in the pointers from the dinode buffer */
-	memcpy(blk->ptrbuf, dibh->b_data + sizeof(struct gfs_dinode),
-	       sbp->bsize - sizeof(struct gfs_dinode));
-	/* Zero out the pointers so we can fill them in later. */
-	memset(dibh->b_data + sizeof(struct gfs_dinode), 0,
-	       sbp->bsize - sizeof(struct gfs_dinode));
-	osi_list_add_prev(&blk->list, &blocks.list);
-
-	/* Now run the metadata chain and build lists of all metadata blocks */
-	osi_list_foreach(tmp, &blocks.list) {
-		blk = osi_list_entry(tmp, struct blocklist, list);
-
-		if (blk->height >= ip->i_di.di_height - 1)
-			continue;
-		header_size = (blk->height > 0 ? sizeof(struct gfs_indirect) :
-			       sizeof(struct gfs_dinode));
-		for (ptr1 = (uint64_t *)blk->ptrbuf, ptrnum = 0;
-		     ptrnum < sbp->sd_inptrs; ptr1++, ptrnum++) {
-			if (!*ptr1)
-				continue;
-
-			block = be64_to_cpu(*ptr1);
-
-			newblk = malloc(sizeof(struct blocklist));
-			if (!newblk) {
-				log_crit("Error: Can't allocate memory"
-					 " for indirect block fix.\n");
-				error = -1;
-				goto out;
-			}
-			memset(newblk, 0, sizeof(*newblk));
-			newblk->ptrbuf = malloc(bufsize);
-			if (!newblk->ptrbuf) {
-				log_crit("Error: Can't allocate memory"
-					 " for file conversion.\n");
-				free(newblk);
-				goto out;
-			}
-			memset(newblk->ptrbuf, 0, bufsize);
-			newblk->block = block;
-			newblk->height = blk->height + 1;
-			/* Build the metapointer list from our predecessors */
-			for (h = 0; h < blk->height; h++)
-				newblk->mp.mp_list[h] = blk->mp.mp_list[h];
-			newblk->mp.mp_list[h] = ptrnum;
-			/* Queue it to be processed later on in the loop. */
-			osi_list_add_prev(&newblk->list, &blocks.list);
-
-			/* read the new metadata block's pointers */
-			bh = bread(sbp, block);
-			memcpy(newblk->ptrbuf, bh->b_data +
-			       sizeof(struct gfs_indirect), bufsize);
-			/* Zero the buffer so we can fill it in later */
-			memset(bh->b_data + sizeof(struct gfs_indirect), 0,
-			       bufsize);
-			bmodified(bh);
-			brelse(bh);
-			/* Free the metadata block so we can reuse it.
-			   This allows us to convert a "full" file system. */
-			ip->i_di.di_blocks--;
-			gfs2_free_block(sbp, block);
-		}
-	}
-
-	/* The gfs2 height may be different.  We need to rebuild the
-	   metadata tree to the gfs2 height. */
-	gfs2_hgt = calc_gfs2_tree_height(ip, ip->i_di.di_size);
-	/* Save off the size because we're going to empty the contents
-	   and add the data blocks back in later. */
-	dinode_size = ip->i_di.di_size;
-	ip->i_di.di_size = 0ULL;
-	di_height = ip->i_di.di_height;
-	ip->i_di.di_height = 0;
-
-	/* Now run through the block list a second time.  If the block
-	   is the highest for metadata, rewrite the data to the gfs2
-	   offset. */
-	osi_list_foreach_safe(tmp, &blocks.list, x) {
-		unsigned int len;
-		uint64_t *ptr2;
-
-		blk = osi_list_entry(tmp, struct blocklist, list);
-		/* If it's not metadata that holds data block pointers
-		   (i.e. metadata pointing to other metadata) */
-		if (blk->height != di_height - 1) {
-			osi_list_del(tmp);
-			free(blk->ptrbuf);
-			free(blk);
-			continue;
-		}
-		/* Skip zero pointers at the start of the buffer.  This may
-		   seem pointless, but the gfs1 blocks won't align with the
-		   gfs2 blocks.  That means that a single block write of
-		   gfs1's pointers is likely to span two blocks on gfs2.
-		   That's a problem if the file system is full.
-		   So I'm trying to truncate the data at the start and end
-		   of the buffers (i.e. write only what we need to). */
-		len = bufsize;
-		for (ptr1 = (uint64_t *)blk->ptrbuf, ptrnum = 0;
-		     ptrnum < sbp->sd_inptrs; ptr1++, ptrnum++) {
-			if (*ptr1 != 0x00)
-				break;
-			len -= sizeof(uint64_t);
-		}
-		/* Skip zero bytes at the end of the buffer */
-		ptr2 = (uint64_t *)(blk->ptrbuf + bufsize) - 1;
-		while (len > 0 && *ptr2 == 0) {
-			ptr2--;
-			len -= sizeof(uint64_t);
-		}
-		blk->mp.mp_list[di_height - 1] = ptrnum;
-		mp_gfs1_to_gfs2(sbp, di_height, gfs2_hgt, &blk->mp, &gfs2mp);
-		memcpy(&blk->mp, &gfs2mp, sizeof(struct metapath));
-		blk->height -= di_height - gfs2_hgt;
-		if (len)
-			fix_metatree(sbp, ip, blk, ptr1, len);
-		osi_list_del(tmp);
-		free(blk->ptrbuf);
-		free(blk);
-	}
-	ip->i_di.di_size = dinode_size;
-
-	/* Set the new dinode height, which may or may not have changed.  */
-	/* The caller will take it from the ip and write it to the buffer */
-	ip->i_di.di_height = gfs2_hgt;
-	return 0;
-
-out:
-	while (!osi_list_empty(&blocks.list)) {
-		blk = osi_list_entry(tmp, struct blocklist, list);
-		osi_list_del(&blocks.list);
-		free(blk->ptrbuf);
-		free(blk);
-	}
-	return error;
-}
 
 static void jdata_mp_gfs1_to_gfs2(struct gfs2_sbd *sbp, int gfs1_h, int gfs2_h,
 			   struct metapath *gfs1mp, struct metapath *gfs2mp,
@@ -697,42 +523,23 @@ static void fix_jdatatree(struct gfs2_sbd *sbp, struct gfs2_inode *ip,
 	}
 }
 
-static int adjust_jdata_inode(struct gfs2_sbd *sbp, struct gfs2_inode *ip)
+static int get_inode_metablocks(struct gfs2_sbd *sbp, struct gfs2_inode *ip, struct blocklist *blocks)
 {
-	uint32_t gfs2_hgt;
-	struct gfs2_buffer_head *bh;
-	osi_list_t *tmp, *x;
-	int h, header_size, bufsize, ptrnum;
+	struct blocklist *blk, *newblk;
+	struct gfs2_buffer_head *bh, *dibh = ip->i_bh;
+	osi_list_t *tmp;
 	uint64_t *ptr1, block;
-	uint64_t dinode_size;
-	int error = 0, di_height;
-	struct blocklist blocks, *blk, *newblk;
-	struct metapath gfs2mp;
-	struct gfs2_buffer_head *dibh = ip->i_bh;
-
-	/* Don't have to worry about things with stuffed inodes */
-	if (ip->i_di.di_height == 0)
-		return 0;
+	int h, header_size, ptrnum;
+	int bufsize = sbp->bsize - sizeof(struct gfs_indirect);
 
-	osi_list_init(&blocks.list);
-
-	/* Add the dinode block to the blocks list */
+	/* Add dinode block to the list */
 	blk = malloc(sizeof(struct blocklist));
 	if (!blk) {
-		log_crit("Error: Can't allocate memory"
-			 " for indirect block fix.\n");
+		log_crit("Error: Can't allocate memory for indirect block fix\n");
 		return -1;
 	}
 	memset(blk, 0, sizeof(*blk));
-	/* allocate a buffer to hold the pointers or data */
-	bufsize = sbp->bsize - sizeof(struct gfs2_meta_header);
 	blk->block = dibh->b_blocknr;
-	/* 
-	 * blk->ptrbuf either contains 
-	 * a) diptrs (for height=0)
-	 * b) inptrs (for height=1 to di_height - 1)
-	 * c) data for height = di_height
-	 */
 	blk->ptrbuf = malloc(bufsize);
 	if (!blk->ptrbuf) {
 		log_crit("Error: Can't allocate memory"
@@ -747,10 +554,10 @@ static int adjust_jdata_inode(struct gfs2_sbd *sbp, struct gfs2_inode *ip)
 	/* Zero out the pointers so we can fill them in later. */
 	memset(dibh->b_data + sizeof(struct gfs_dinode), 0,
 	       sbp->bsize - sizeof(struct gfs_dinode));
-	osi_list_add_prev(&blk->list, &blocks.list);
+	osi_list_add_prev(&blk->list, &blocks->list);
 
 	/* Now run the metadata chain and build lists of all metadata blocks */
-	osi_list_foreach(tmp, &blocks.list) {
+	osi_list_foreach(tmp, &blocks->list) {
 		blk = osi_list_entry(tmp, struct blocklist, list);
 
 		if (blk->height >= ip->i_di.di_height - 1)
@@ -761,23 +568,19 @@ static int adjust_jdata_inode(struct gfs2_sbd *sbp, struct gfs2_inode *ip)
 		     ptrnum < sbp->sd_inptrs; ptr1++, ptrnum++) {
 			if (!*ptr1)
 				continue;
-
 			block = be64_to_cpu(*ptr1);
 
 			newblk = malloc(sizeof(struct blocklist));
 			if (!newblk) {
-				log_crit("Error: Can't allocate memory"
-					 " for indirect block fix.\n");
-				error = -1;
-				goto out;
+				log_crit("Error: Can't allocate memory for indirect block fix.\n");
+				return -1;
 			}
 			memset(newblk, 0, sizeof(*newblk));
 			newblk->ptrbuf = malloc(bufsize);
 			if (!newblk->ptrbuf) {
-				log_crit("Error: Can't allocate memory"
-					 " for file conversion.\n");
+				log_crit("Error: Can't allocate memory for file conversion.\n");
 				free(newblk);
-				goto out;
+				return -1;
 			}
 			memset(newblk->ptrbuf, 0, bufsize);
 			newblk->block = block;
@@ -787,15 +590,12 @@ static int adjust_jdata_inode(struct gfs2_sbd *sbp, struct gfs2_inode *ip)
 				newblk->mp.mp_list[h] = blk->mp.mp_list[h];
 			newblk->mp.mp_list[h] = ptrnum;
 			/* Queue it to be processed later on in the loop. */
-			osi_list_add_prev(&newblk->list, &blocks.list);
-
+			osi_list_add_prev(&newblk->list, &blocks->list);
 			/* read the new metadata block's pointers */
 			bh = bread(sbp, block);
-			memcpy(newblk->ptrbuf, bh->b_data + sizeof(struct gfs_indirect),
-			       sbp->bsize - sizeof(struct gfs_indirect));
+			memcpy(newblk->ptrbuf, bh->b_data + sizeof(struct gfs_indirect), bufsize);
 			/* Zero the buffer so we can fill it in later */
-			memset(bh->b_data + sizeof(struct gfs_indirect), 0,
-			       sbp->bsize - sizeof(struct gfs_indirect));
+			memset(bh->b_data + sizeof(struct gfs_indirect), 0, bufsize);
 			bmodified(bh);
 			brelse(bh);
 			/* Free the block so we can reuse it. This allows us to
@@ -804,6 +604,140 @@ static int adjust_jdata_inode(struct gfs2_sbd *sbp, struct gfs2_inode *ip)
 			gfs2_free_block(sbp, block);
 		}
 	}
+	return 0;
+}
+
+static int fix_ind_reg_or_dir(struct gfs2_sbd *sbp, struct gfs2_inode *ip, uint32_t di_height,
+		       uint32_t gfs2_hgt, struct blocklist *blk, struct blocklist *blocks)
+{
+	unsigned int len, bufsize;
+	uint64_t *ptr1, *ptr2;
+	int ptrnum;
+	struct metapath gfs2mp;
+
+	bufsize = sbp->bsize - sizeof(struct gfs_indirect);
+	len = bufsize;
+
+	/* Skip zero pointers at the start of the buffer.  This may 
+	   seem pointless, but the gfs1 blocks won't align with the 
+	   gfs2 blocks.  That means that a single block write of 
+	   gfs1's pointers is likely to span two blocks on gfs2. 
+	   That's a problem if the file system is full. 
+	   So I'm trying to truncate the data at the start and end 
+	   of the buffers (i.e. write only what we need to). */
+	for (ptr1 = (uint64_t *)blk->ptrbuf, ptrnum = 0;
+	     ptrnum < sbp->sd_inptrs; ptr1++, ptrnum++) {
+		if (*ptr1 != 0x00)
+			break;
+		len -= sizeof(uint64_t);
+	}
+	/* Skip zero bytes at the end of the buffer */
+	ptr2 = (uint64_t *)(blk->ptrbuf + bufsize) - 1;
+	while (len > 0 && *ptr2 == 0) {
+		ptr2--;
+		len -= sizeof(uint64_t);
+	}
+	blk->mp.mp_list[di_height - 1] = ptrnum;
+	mp_gfs1_to_gfs2(sbp, di_height, gfs2_hgt, &blk->mp, &gfs2mp);
+	memcpy(&blk->mp, &gfs2mp, sizeof(struct metapath));
+	blk->height -= di_height - gfs2_hgt;
+	if (len)
+		fix_metatree(sbp, ip, blk, ptr1, len);
+
+	return 0;
+}
+
+static int fix_ind_jdata(struct gfs2_sbd *sbp, struct gfs2_inode *ip, uint32_t di_height, 
+		  uint32_t gfs2_hgt, uint64_t dinode_size, struct blocklist *blk, 
+		  struct blocklist *blocks)
+{
+	struct blocklist *newblk;
+	unsigned int len, bufsize;
+	uint64_t *ptr1, block;
+	int ptrnum, h;
+	struct metapath gfs2mp;
+	struct gfs2_buffer_head *bh;
+
+	bufsize = sbp->bsize - sizeof(struct gfs2_meta_header);
+	/*
+	 * For each metadata block that holds jdata block pointers,
+	 * get the blk pointers and copy them block by block
+	 */
+	for (ptr1 = (uint64_t *) blk->ptrbuf, ptrnum = 0;
+	     ptrnum < sbp->sd_inptrs; ptr1++, ptrnum++) {
+		if (!*ptr1)
+			continue;
+		block = be64_to_cpu(*ptr1);
+
+		newblk = malloc(sizeof(struct blocklist));
+		if (!newblk) {
+			log_crit("Error: Can't allocate memory for indirect block fix.\n");
+			return -1;
+		}
+		memset(newblk, 0, sizeof(*newblk));
+		newblk->ptrbuf = malloc(bufsize); 
+		if (!newblk->ptrbuf) {
+			log_crit("Error: Can't allocate memory for file conversion.\n");
+			free(newblk);
+			return -1;
+		}
+		memset(newblk->ptrbuf, 0, bufsize);
+		newblk->block = block;
+		newblk->height = blk->height + 1;
+		/* Build the metapointer list from our predecessors */
+		for (h=0; h < blk->height; h++)
+			newblk->mp.mp_list[h] = blk->mp.mp_list[h];
+		newblk->mp.mp_list[h] = ptrnum;
+		bh = bread(sbp, block);
+		/* This is a data block. i.e newblk->height == ip->i_di.di_height */
+		/* read in the jdata block */
+		memcpy(newblk->ptrbuf, bh->b_data +
+		       sizeof(struct gfs2_meta_header), bufsize);
+		memset(bh->b_data + sizeof(struct gfs2_meta_header), 0, bufsize);
+		bmodified(bh);
+		brelse(bh);
+		/* Free the block so we can reuse it. This allows us to
+		   convert a "full" file system */
+		ip->i_di.di_blocks--;
+		gfs2_free_block(sbp, block);
+
+		len = bufsize;
+		jdata_mp_gfs1_to_gfs2(sbp, di_height, gfs2_hgt, &newblk->mp, &gfs2mp,
+				      &len, dinode_size);
+		memcpy(&newblk->mp, &gfs2mp, sizeof(struct metapath));
+		newblk->height -= di_height - gfs2_hgt;
+		if (len)
+			fix_jdatatree(sbp, ip, newblk, newblk->ptrbuf, len);
+		free(newblk->ptrbuf);
+		free(newblk);
+	}
+	return 0;
+}
+
+static int adjust_indirect_blocks(struct gfs2_sbd *sbp, struct gfs2_inode *ip)
+{
+	uint64_t dinode_size;
+	uint32_t gfs2_hgt, di_height;
+	osi_list_t *tmp=NULL, *x;
+	struct blocklist blocks, *blk;
+	int error = 0;
+
+	int isdir = S_ISDIR(ip->i_di.di_mode); /* is always jdata */
+	int isjdata = ((GFS2_DIF_JDATA & ip->i_di.di_flags) && !isdir);
+	int isreg = (!isjdata && !isdir);
+
+	/* regular files and dirs are same upto height=2
+	   jdata files (not dirs) are same only when height=0 */
+	if (((isreg||isdir) && ip->i_di.di_height <= 1) ||
+	    (isjdata && ip->i_di.di_height == 0))
+		return 0; /* nothing to do */
+
+	osi_list_init(&blocks.list);
+
+	error = get_inode_metablocks(sbp, ip, &blocks);
+	if (error)
+		goto out;
+
 	/* The gfs2 height may be different.  We need to rebuild the
 	   metadata tree to the gfs2 height. */
 	gfs2_hgt = calc_gfs2_tree_height(ip, ip->i_di.di_size);
@@ -817,82 +751,36 @@ static int adjust_jdata_inode(struct gfs2_sbd *sbp, struct gfs2_inode *ip)
 	/* Now run through the block list a second time.  If the block
 	   is a data block, rewrite the data to the gfs2 offset. */
 	osi_list_foreach_safe(tmp, &blocks.list, x) {
-		unsigned int len;
 
 		blk = osi_list_entry(tmp, struct blocklist, list);
-		/* If it's not a highest level indirect block */
+		/* If it's not metadata that holds data block pointers
+		   (i.e. metadata pointing to other metadata) */
 		if (blk->height != di_height - 1) {
 			osi_list_del(tmp);
 			free(blk->ptrbuf);
 			free(blk);
 			continue;
 		}
-		/*
-		 * For each metadata block that holds jdata block pointers,
-		 * get the blk pointers and copy them block by block
-		 */
-		for (ptr1 = (uint64_t *) blk->ptrbuf, ptrnum = 0;
-		     ptrnum < sbp->sd_inptrs; ptr1++, ptrnum++) {
-			if (!*ptr1)
-				continue;
-			block = be64_to_cpu(*ptr1);
-
-			newblk = malloc(sizeof(struct blocklist));
-			if (!newblk) {
-				log_crit("Error: Can't allocate memory"
-					 " for indirect block fix.\n");
-				error = -1;
-				goto out;
-			}
-			memset(newblk, 0, sizeof(*newblk));
-			newblk->ptrbuf = malloc(bufsize); 
-			if (!newblk->ptrbuf) {
-				log_crit("Error: Can't allocate memory"
-					 " for file conversion.\n");
-				free(newblk);
-				goto out;
-			}
-			memset(newblk->ptrbuf, 0, bufsize);
-			newblk->block = block;
-			newblk->height = blk->height + 1;
-			/* Build the metapointer list from our predecessors */
-			for (h=0; h < blk->height; h++)
-				newblk->mp.mp_list[h] = blk->mp.mp_list[h];
-			newblk->mp.mp_list[h] = ptrnum;
-			bh = bread(sbp, block);
-			/* This is a data block. i.e newblk->height == ip->i_di.di_height */
-			/* read in the jdata block */
-			memcpy(newblk->ptrbuf, bh->b_data +
-			       sizeof(struct gfs2_meta_header), bufsize);
-			memset(bh->b_data + sizeof(struct gfs2_meta_header), 0,
-			       bufsize);
-			bmodified(bh);
-			brelse(bh);
-			/* Free the block so we can reuse it. This allows us to
-			   convert a "full" file system */
-			ip->i_di.di_blocks--;
-			gfs2_free_block(sbp, block);
+		if (isreg || isdir) /* more or less same way to deal with either */
+			error = fix_ind_reg_or_dir(sbp, ip, di_height, 
+						   gfs2_hgt, blk, &blocks);
+		else if (isjdata)
+			error = fix_ind_jdata(sbp, ip, di_height, gfs2_hgt, 
+					      dinode_size, blk, &blocks);
+		if (error)
+			goto out;
 
-			len = bufsize;
-			jdata_mp_gfs1_to_gfs2(sbp, di_height, gfs2_hgt, &newblk->mp, &gfs2mp,
-					      &len, dinode_size);
-			memcpy(&newblk->mp, &gfs2mp, sizeof(struct metapath));
-			newblk->height -= di_height - gfs2_hgt;
-			if (len)
-				fix_jdatatree(sbp, ip, newblk, newblk->ptrbuf, len);
-			free(newblk->ptrbuf);
-			free(newblk);
-		}
 		osi_list_del(tmp);
 		free(blk->ptrbuf);
 		free(blk);
 	}
+
 	ip->i_di.di_size = dinode_size;
 
 	/* Set the new dinode height, which may or may not have changed.  */
 	/* The caller will take it from the ip and write it to the buffer */
 	ip->i_di.di_height = gfs2_hgt;
-	return 0;
+	return error;
 
 out:
 	while (!osi_list_empty(&blocks.list)) {
@@ -1057,12 +945,8 @@ static int adjust_inode(struct gfs2_sbd *sbp, struct gfs2_buffer_head *bh)
 		inode->i_di.di_goal_data = 0; /* make sure the upper 32b are 0 */
 		inode->i_di.di_goal_data = gfs1_dinode_struct->di_goal_dblk;
 		inode->i_di.di_generation = 0;
-		if (!(inode->i_di.di_mode & S_IFDIR) &&
-		    inode->i_di.di_flags & GFS2_DIF_JDATA)
-			ret = adjust_jdata_inode(sbp, inode);
-		else
-			ret = adjust_indirect_blocks(sbp, inode);
-		if (ret)
+		
+		if (adjust_indirect_blocks(sbp, inode))
 			return -1;
 		/* Check for cdpns */
 		if (inode->i_di.di_mode & S_IFLNK) {
@@ -1349,9 +1233,11 @@ static int fix_one_directory_exhash(struct gfs2_sbd *sbp, struct gfs2_inode *dip
 			leaf_block = be64_to_cpu(buf);
 			error = 0;
 		}
+	leaf_chain:
 		/* leaf blocks may be repeated, so skip the duplicates: */
 		if (leaf_block == prev_leaf_block) /* same block? */
 			continue;                      /* already converted */
+
 		prev_leaf_block = leaf_block;
 		/* read the leaf buffer in */
 		error = gfs2_get_leaf(dip, leaf_block, &bh_leaf);
@@ -1365,6 +1251,11 @@ static int fix_one_directory_exhash(struct gfs2_sbd *sbp, struct gfs2_inode *dip
 		brelse(bh_leaf);
 		if (dentmod && error == -EISDIR) /* dentmod was marked DT_DIR, break out */
 			break;
+		if (leaf.lf_next) { /* leaf has a leaf chain, process leaves in chain */
+			leaf_block = leaf.lf_next;
+			error = 0;
+			goto leaf_chain;
+		}
 	} /* for leaf_num */
 	return 0;
 }/* fix_one_directory_exhash */
@@ -1403,6 +1294,10 @@ static int fix_directory_info(struct gfs2_sbd *sbp, osi_list_t *dir_to_fix)
 	osi_list_t *tmp, *fix;
 	struct inode_block *dir_iblk;
 	uint64_t offset, dirblock;
+	uint32_t gfs1_inptrs = sbp->sd_inptrs;
+	/* Directory inodes have been converted to gfs2, use gfs2 inptrs */
+	sbp->sd_inptrs = (sbp->bsize - sizeof(struct gfs2_meta_header))
+		/ sizeof(uint64_t);
 
 	dirs_fixed = 0;
 	dirents_fixed = 0;
@@ -1433,6 +1328,7 @@ static int fix_directory_info(struct gfs2_sbd *sbp, osi_list_t *dir_to_fix)
 		osi_list_del(tmp);
 		free(tmp);
 	}
+	sbp->sd_inptrs = gfs1_inptrs;
 	return 0;
 }/* fix_directory_info */