[PATCH] 1/19 e2fsprogs improved extent checking

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



The following patch improves the handling of corrupted extents, including
checking of the extent/index headers, index depth, overlap, duplicate
blocks, etc.  It is based atop the current 1.39-WIP e2fsprogs Mercurial tree.
It does NOT include the 64-bit fixups from Bull as we haven't rebased onto
an e2fsprogs patch series that supports the requirements for this.

There is still one case that is known not to be handled - if there
are duplicate blocks in an extent stored in an external index block,
but the index block is full and it needs a split of the index block
(which may need more splits up the tree).  This is very complex to fix
within the framework of the current block iterator, and we rationalized
to ourselves that this won't happen very often.  The more common case
(duplicate block with in-inode index) is handled correctly.

Following this patch will be a series of test cases for extents.  Two of
them are known to fail (f_extents_eh_depth and f_extents_shrd_blks) but
are useful nonetheless for Ted's work in rewriting the extents code.

Signed-off-by: Andreas Dilger <adilger@xxxxxxxxxxxxx>
Signed-off-by: Girish Shilamkar <girish@xxxxxxxxxxxxx>

Cheers, Andreas
--
Andreas Dilger
Principal Software Engineer
Cluster File Systems, Inc.

Support for checking 32-bit extents format inodes.  Note that this
patch depends on the upstream e2fsprogs including ext3_extents.h already.

Clear the high 16 bits of extents and index entries, since the
extents patches did not do this explicitly.  Some parts of this
code need fixing for checking > 32-bit block filesystems,
marked "XXX: 48-bit".

Verify extent headers in blocks, logical ordering of extents,
logical ordering of indexes.

Add explicit checking of {d,t,}indirect and index blocks to detect
corruption instead of implicitly doing this by checking the referred
blocks and only block-at-a-time correctness.  This avoids incorrectly
invoking the very lengthy duplicate blocks pass for bad indirect/index
blocks.  We may want to tune the "threshold" for how many errors make
a "bad" indirect/index block.

Add ability to split or remove extents in order to allow extent
reallocation during the duplicate blocks pass.

Index: e2fsprogs/e2fsck/Makefile.in
===================================================================
--- e2fsprogs.orig/e2fsck/Makefile.in	2007-02-15 11:31:42.000000000 -0700
+++ e2fsprogs/e2fsck/Makefile.in	2007-02-15 11:33:06.000000000 -0700
@@ -261,6 +261,7 @@ super.o: $(srcdir)/super.c $(top_srcdir)
 pass1.o: $(srcdir)/pass1.c $(srcdir)/e2fsck.h \
  $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_builddir)/lib/ext2fs/ext2_types.h \
  $(top_srcdir)/lib/ext2fs/ext2fs.h $(top_srcdir)/lib/ext2fs/ext2_fs.h \
+ $(top_srcdir)/lib/ext2fs/ext3_extents.h \
  $(top_srcdir)/lib/et/com_err.h $(top_srcdir)/lib/ext2fs/ext2_io.h \
  $(top_builddir)/lib/ext2fs/ext2_err.h $(top_srcdir)/lib/ext2fs/bitops.h \
  $(top_srcdir)/lib/blkid/blkid.h $(top_builddir)/lib/blkid/blkid_types.h \
Index: e2fsprogs/e2fsck/e2fsck.h
===================================================================
--- e2fsprogs.orig/e2fsck/e2fsck.h	2007-02-15 11:31:42.000000000 -0700
+++ e2fsprogs/e2fsck/e2fsck.h	2007-02-15 14:45:50.000000000 -0700
@@ -327,6 +327,7 @@ struct e2fsck_struct {
 	__u32 large_files;
 	__u32 fs_ext_attr_inodes;
 	__u32 fs_ext_attr_blocks;
+	__u32 extent_files;
 
 	time_t now;
 
Index: e2fsprogs/e2fsck/pass1.c
===================================================================
--- e2fsprogs.orig/e2fsck/pass1.c	2007-02-15 11:32:58.000000000 -0700
+++ e2fsprogs/e2fsck/pass1.c	2007-02-15 15:49:27.000000000 -0700
@@ -46,6 +46,7 @@
 
 #include "e2fsck.h"
 #include <ext2fs/ext2_ext_attr.h>
+#include <ext2fs/ext3_extents.h>
 
 #include "problem.h"
 
@@ -79,16 +80,19 @@ static void adjust_extattr_refcount(e2fs
 struct process_block_struct {
 	ext2_ino_t	ino;
 	unsigned	is_dir:1, is_reg:1, clear:1, suppress:1,
-				fragmented:1, compressed:1, bbcheck:1;
+			fragmented:1, compressed:1, bbcheck:1, extent:1;
 	blk_t		num_blocks;
 	blk_t		max_blocks;
 	e2_blkcnt_t	last_block;
 	int		num_illegal_blocks;
+	int		last_illegal_blocks;
 	blk_t		previous_block;
 	struct ext2_inode *inode;
 	struct problem_context *pctx;
 	ext2fs_block_bitmap fs_meta_blocks;
 	e2fsck_t	ctx;
+	struct ext3_extent_header *eh_prev;
+	void		*block_buf;
 };
 
 struct process_inode_block {
@@ -137,7 +141,7 @@ int e2fsck_pass1_check_device_inode(ext2
 	 * this is a bogus device/fifo/socket
 	 */
 	if ((ext2fs_inode_data_blocks(fs, inode) != 0) ||
-	    (inode->i_flags & EXT2_INDEX_FL))
+	    (inode->i_flags & (EXT2_INDEX_FL | EXT4_EXTENTS_FL)))
 		return 0;
 
 	/*
@@ -171,7 +175,7 @@ int e2fsck_pass1_check_symlink(ext2_fils
 	blk_t	blocks;
 
 	if ((inode->i_size_high || inode->i_size == 0) ||
-	    (inode->i_flags & EXT2_INDEX_FL))
+	    (inode->i_flags & (EXT2_INDEX_FL | EXT4_EXTENTS_FL)))
 		return 0;
 
 	blocks = ext2fs_inode_data_blocks(fs, inode);
@@ -389,7 +393,8 @@ void e2fsck_pass1(e2fsck_t ctx)
 	struct		problem_context pctx;
 	struct		scan_callback_struct scan_struct;
 	struct ext2_super_block *sb = ctx->fs->super;
-	int		imagic_fs;
+	struct ext3_extent_header *eh;
+	int		imagic_fs, extent_fs;
 	int		busted_fs_time = 0;
 	int		inode_size;
 	
@@ -423,6 +428,7 @@ void e2fsck_pass1(e2fsck_t ctx)
 #undef EXT2_BPP
 
 	imagic_fs = (sb->s_feature_compat & EXT2_FEATURE_COMPAT_IMAGIC_INODES);
+	extent_fs = (sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_EXTENTS);
 
 	/*
 	 * Allocate bitmaps structures
@@ -797,8 +803,7 @@ void e2fsck_pass1(e2fsck_t ctx)
 				check_blocks(ctx, &pctx, block_buf);
 				continue;
 			}
-		}
-		else if (LINUX_S_ISFIFO (inode->i_mode) &&
+		} else if (LINUX_S_ISFIFO (inode->i_mode) &&
 			 e2fsck_pass1_check_device_inode(fs, inode)) {
 			check_immutable(ctx, &pctx);
 			check_size(ctx, &pctx);
@@ -810,21 +815,75 @@ void e2fsck_pass1(e2fsck_t ctx)
 			ctx->fs_sockets_count++;
 		} else
 			mark_inode_bad(ctx, ino);
-		if (inode->i_block[EXT2_IND_BLOCK])
-			ctx->fs_ind_count++;
-		if (inode->i_block[EXT2_DIND_BLOCK])
-			ctx->fs_dind_count++;
-		if (inode->i_block[EXT2_TIND_BLOCK])
-			ctx->fs_tind_count++;
-		if (inode->i_block[EXT2_IND_BLOCK] ||
-		    inode->i_block[EXT2_DIND_BLOCK] ||
-		    inode->i_block[EXT2_TIND_BLOCK] ||
-		    inode->i_file_acl) {
-			inodes_to_process[process_inode_count].ino = ino;
-			inodes_to_process[process_inode_count].inode = *inode;
-			process_inode_count++;
-		} else
-			check_blocks(ctx, &pctx, block_buf);
+
+		eh = (struct ext3_extent_header *)inode->i_block;
+		if ((inode->i_flags & EXT4_EXTENTS_FL)) {
+			if ((LINUX_S_ISREG(inode->i_mode) ||
+			     LINUX_S_ISDIR(inode->i_mode)) &&
+			    ext2fs_extent_header_verify(eh, EXT2_N_BLOCKS *
+							sizeof(__u32)) == 0) {
+				if (!extent_fs &&
+				    fix_problem(ctx,PR_1_EXTENT_FEATURE,&pctx)){
+					sb->s_feature_incompat |=
+						EXT3_FEATURE_INCOMPAT_EXTENTS;
+					ext2fs_mark_super_dirty(fs);
+					extent_fs = 1;
+				}
+			} else if (fix_problem(ctx, PR_1_SET_EXTENT_FL, &pctx)){
+				inode->i_flags &= ~EXT4_EXTENTS_FL;
+				e2fsck_write_inode(ctx, ino, inode, "pass1");
+				goto check_ind_inode;
+			}
+		} else if (extent_fs &&
+			   (LINUX_S_ISREG(inode->i_mode) ||
+			    LINUX_S_ISDIR(inode->i_mode)) &&
+			   ext2fs_extent_header_verify(eh, EXT2_N_BLOCKS *
+						       sizeof(__u32)) == 0 &&
+			   fix_problem(ctx, PR_1_UNSET_EXTENT_FL, &pctx)) {
+			inode->i_flags |= EXT4_EXTENTS_FL;
+			e2fsck_write_inode(ctx, ino, inode, "pass1");
+		}
+		if (extent_fs && inode->i_flags & EXT4_EXTENTS_FL) {
+			ctx->extent_files++;
+			switch(eh->eh_depth) {
+			case 0:
+				break;
+			case 1:
+				ctx->fs_ind_count++;
+				break;
+			case 2:
+				ctx->fs_dind_count++;
+				break;
+			default:
+				ctx->fs_tind_count++;
+				break;
+			}
+			if (eh->eh_depth > 0) {
+				inodes_to_process[process_inode_count].ino = ino;
+				inodes_to_process[process_inode_count].inode = *inode;
+				process_inode_count++;
+			} else {
+				check_blocks(ctx, &pctx, block_buf);
+			}
+		} else {
+		check_ind_inode:
+			if (inode->i_block[EXT2_IND_BLOCK])
+				ctx->fs_ind_count++;
+			if (inode->i_block[EXT2_DIND_BLOCK])
+				ctx->fs_dind_count++;
+			if (inode->i_block[EXT2_TIND_BLOCK])
+				ctx->fs_tind_count++;
+			if (inode->i_block[EXT2_IND_BLOCK] ||
+			    inode->i_block[EXT2_DIND_BLOCK] ||
+			    inode->i_block[EXT2_TIND_BLOCK] ||
+			    inode->i_file_acl) {
+				inodes_to_process[process_inode_count].ino = ino;
+				inodes_to_process[process_inode_count].inode = *inode;
+				process_inode_count++;
+			} else {
+				check_blocks(ctx, &pctx, block_buf);
+			}
+		}
 
 		if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
 			return;
@@ -1319,10 +1378,23 @@ clear_extattr:
 	return 0;
 }
 
+static int htree_blk_iter_cb(ext2_filsys fs EXT2FS_ATTR((unused)),
+			     blk_t *blocknr,
+			     e2_blkcnt_t blockcnt EXT2FS_ATTR((unused)),
+			     blk_t ref_blk EXT2FS_ATTR((unused)),
+			     int ref_offset EXT2FS_ATTR((unused)),
+			     void *priv_data)
+{
+	blk_t *blk = priv_data;
+
+	*blk = *blocknr;
+
+	return BLOCK_ABORT;
+}
+
 /* Returns 1 if bad htree, 0 if OK */
 static int handle_htree(e2fsck_t ctx, struct problem_context *pctx,
-			ext2_ino_t ino EXT2FS_ATTR((unused)),
-			struct ext2_inode *inode,
+			ext2_ino_t ino, struct ext2_inode *inode,
 			char *block_buf)
 {
 	struct ext2_dx_root_info	*root;
@@ -1336,7 +1408,8 @@ static int handle_htree(e2fsck_t ctx, st
 	     fix_problem(ctx, PR_1_HTREE_SET, pctx)))
 		return 1;
 
-	blk = inode->i_block[0];
+	ext2fs_block_iterate2(fs, ino, BLOCK_FLAG_DATA_ONLY | BLOCK_FLAG_HOLE,
+			      block_buf, htree_blk_iter_cb, &blk);
 	if (((blk == 0) ||
 	     (blk < fs->super->s_first_data_block) ||
 	     (blk >= fs->super->s_blocks_count)) &&
@@ -1373,6 +1446,134 @@ static int handle_htree(e2fsck_t ctx, st
 	return 0;
 }
 
+/* sort 0 to the end of the list so we can exit early */
+static EXT2_QSORT_TYPE verify_ind_cmp(const void *a, const void *b)
+{
+	const __u32 blk_a = *(__u32 *)a - 1, blk_b = *(__u32 *)b - 1;
+
+	return blk_b > blk_a ? -1 : blk_a - blk_b;
+}
+
+/* Verify whether an indirect block is sane.  If it has multiple references
+ * to the same block, or if it has a large number of bad or duplicate blocks
+ * chances are that it is corrupt and we should just clear it instead of
+ * trying to salvage it.
+ * NOTE: this needs to get a copy of the blocks, since it reorders them */
+static int e2fsck_ind_block_verify(struct process_block_struct *p,
+				   void *block_buf, int buflen)
+{
+	__u32 blocks[EXT2_N_BLOCKS], *indir = block_buf;
+	int num_indir = buflen / sizeof(*indir);
+	int i, bad = 0;
+
+	if (num_indir == EXT2_N_BLOCKS) {
+		memcpy(blocks, block_buf, buflen);
+		indir = blocks;
+	}
+	qsort(indir, num_indir, sizeof(*indir), verify_ind_cmp);
+
+	for (i = 0; i < num_indir; i++) {
+		if (indir[i] == 0)
+			break;
+
+		/* bad block number, or duplicate block */
+		if (indir[i] < p->ctx->fs->super->s_first_data_block ||
+		    indir[i] > p->ctx->fs->super->s_blocks_count ||
+		    ext2fs_fast_test_block_bitmap(p->ctx->block_found_map,
+						  indir[i]))
+			bad++;
+
+		/* shouldn't reference the same block twice within a block */
+		if (i > 0 && indir[i] == indir[i - 1])
+			bad++;
+	}
+
+	if ((num_indir <= EXT2_N_BLOCKS && bad > 4) || bad > 8)
+		return PR_1_INDIRECT_BAD;
+
+#if DEBUG_E2FSCK
+	/* For debugging, clobber buffer to ensure it doesn't appear sane */
+	memset(indir, 0xca, buflen);
+#endif
+	return 0;
+}
+
+static int e2fsck_ext_block_verify(struct process_block_struct *p,
+				   void *block_buf, int buflen)
+{
+	struct ext3_extent_header *eh = block_buf, *eh_sav;
+	e2fsck_t ctx = p->ctx;
+	struct problem_context *pctx = p->pctx;
+	int i, problem = 0;
+
+	if (ext2fs_extent_header_verify(eh, buflen))
+		return PR_1_EXTENT_IDX_BAD;
+
+	if (p->eh_prev && p->eh_prev->eh_depth != eh->eh_depth + 1)
+		return PR_1_EXTENT_IDX_BAD;
+
+	eh_sav = p->eh_prev;
+	p->eh_prev = eh;
+
+	if (eh->eh_depth == 0) {
+		struct ext3_extent *ex = EXT_FIRST_EXTENT(eh), *ex_prev = NULL;
+
+		for (i = 0; i < eh->eh_entries; i++, ex++) {
+			/* FIXME: 48-bit check for s_blocks_count_hi */
+			if (ex->ee_start_hi && fix_problem(ctx, PR_1_EXTENT_HI,
+							   pctx)) {
+				ex->ee_start_hi = 0;
+				problem = PR_1_EXTENT_CHANGED;
+			}
+
+			if (ext2fs_extent_verify(ctx->fs, ex, ex_prev, NULL,0)){
+				p->num_illegal_blocks++;
+				pctx->blkcount = ex->ee_start;
+				pctx->num = ex->ee_len;
+				pctx->blk = ex->ee_block;
+				if (fix_problem(ctx, PR_1_EXTENT_BAD, pctx)) {
+					ext2fs_extent_remove(eh, ex);
+					i--; ex--; /* check next (moved) item */
+					problem = PR_1_EXTENT_CHANGED;
+					continue;
+				}
+			}
+
+			ex_prev = ex;
+		}
+	} else {
+		struct ext3_extent_idx *ix =EXT_FIRST_INDEX(eh), *ix_prev =NULL;
+
+		for (i = 0; i < eh->eh_entries; i++, ix++) {
+			/* FIXME: 48-bit check for s_blocks_count_hi */
+			if (ix->ei_leaf_hi && fix_problem(ctx, PR_1_EXTENT_HI,
+							  pctx)) {
+				ix->ei_leaf_hi = ix->ei_unused = 0;
+				problem = PR_1_EXTENT_CHANGED;
+			}
+
+			if (ext2fs_extent_index_verify(ctx->fs, ix, ix_prev)) {
+				p->num_illegal_blocks++;
+				pctx->blkcount = ix->ei_leaf;;
+				pctx->num = i;
+				pctx->blk = ix->ei_block;
+				if (fix_problem(ctx, PR_1_EXTENT_IDX_BAD,pctx)){
+					ext2fs_extent_index_remove(eh, ix);
+					i--; ix--; /* check next (moved) item */
+					problem = PR_1_EXTENT_CHANGED;
+					continue;
+				}
+			}
+
+			ix_prev = ix;
+		}
+	}
+
+	p->eh_prev = eh_sav;
+
+	return problem;
+}
+
 /*
  * This subroutine is called on each inode to account for all of the
  * blocks used by that inode.
@@ -1392,9 +1595,11 @@ static void check_blocks(e2fsck_t ctx, s
 	pb.num_blocks = 0;
 	pb.last_block = -1;
 	pb.num_illegal_blocks = 0;
+	pb.last_illegal_blocks = 0;
 	pb.suppress = 0; pb.clear = 0;
 	pb.fragmented = 0;
 	pb.compressed = 0;
+	pb.extent = !!(inode->i_flags & EXT4_EXTENTS_FL);
 	pb.previous_block = 0;
 	pb.is_dir = LINUX_S_ISDIR(inode->i_mode);
 	pb.is_reg = LINUX_S_ISREG(inode->i_mode);
@@ -1402,6 +1607,8 @@ static void check_blocks(e2fsck_t ctx, s
 	pb.inode = inode;
 	pb.pctx = pctx;
 	pb.ctx = ctx;
+	pb.eh_prev = NULL;
+	pb.block_buf = block_buf;
 	pctx->ino = ino;
 	pctx->errcode = 0;
 
@@ -1420,10 +1627,27 @@ static void check_blocks(e2fsck_t ctx, s
 	if (inode->i_file_acl && check_ext_attr(ctx, pctx, block_buf))
 		pb.num_blocks++;
 
-	if (ext2fs_inode_has_valid_blocks(inode))
-		pctx->errcode = ext2fs_block_iterate2(fs, ino,
-				       pb.is_dir ? BLOCK_FLAG_HOLE : 0,
-				       block_buf, process_block, &pb);
+	if (ext2fs_inode_has_valid_blocks(inode)) {
+		int problem = 0;
+
+		if (pb.extent)
+			problem = e2fsck_ext_block_verify(&pb, inode->i_block,
+							sizeof(inode->i_block));
+		else
+			problem = e2fsck_ind_block_verify(&pb, inode->i_block,
+							sizeof(inode->i_block));
+		if (problem == PR_1_EXTENT_CHANGED) {
+			dirty_inode++;
+			problem = 0;
+		}
+
+		if (problem && fix_problem(ctx, problem, pctx))
+			pb.clear = 1;
+		else
+			pctx->errcode = ext2fs_block_iterate2(fs, ino,
+						pb.is_dir ? BLOCK_FLAG_HOLE : 0,
+						block_buf, process_block, &pb);
+	}
 	end_problem_latch(ctx, PR_LATCH_BLOCK);
 	end_problem_latch(ctx, PR_LATCH_TOOBIG);
 	if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
@@ -1587,6 +1811,9 @@ static char *describe_illegal_block(ext2
 }
 #endif
 
+#define IND_BLKCNT(_b) ((_b) == BLOCK_COUNT_IND || (_b) == BLOCK_COUNT_DIND ||\
+			(_b) == BLOCK_COUNT_TIND)
+
 /*
  * This is a helper function for check_blocks().
  */
@@ -1665,7 +1892,8 @@ static int process_block(ext2_filsys fs,
 	 * file be contiguous.  (Which can never be true for really
 	 * big files that are greater than a block group.)
 	 */
-	if (!HOLE_BLKADDR(p->previous_block)) {
+	if (!HOLE_BLKADDR(p->previous_block) &&
+	    !(p->extent && IND_BLKCNT(blockcnt))) {
 		if (p->previous_block+1 != blk)
 			p->fragmented = 1;
 	}
@@ -1682,9 +1910,34 @@ static int process_block(ext2_filsys fs,
 	    blk >= fs->super->s_blocks_count)
 		problem = PR_1_ILLEGAL_BLOCK_NUM;
 
+	if (!problem && IND_BLKCNT(blockcnt) && p->ino != EXT2_RESIZE_INO) {
+		if (p->extent) {
+			if (ext2fs_read_ext_block(ctx->fs, blk, p->block_buf))
+				problem = PR_1_BLOCK_ITERATE;
+			else
+				problem = e2fsck_ext_block_verify(p,
+								 p->block_buf,
+								 fs->blocksize);
+			if (problem == PR_1_EXTENT_CHANGED) {
+				if (ext2fs_write_ext_block(ctx->fs, blk,
+							   p->block_buf))
+					problem = PR_1_BLOCK_ITERATE;
+			}
+
+		} else {
+			if (ext2fs_read_ind_block(ctx->fs, blk, p->block_buf))
+				problem = PR_1_BLOCK_ITERATE;
+			else
+				problem = e2fsck_ind_block_verify(p,
+								 p->block_buf,
+								 fs->blocksize);
+		}
+	}
+
 	if (problem) {
 		p->num_illegal_blocks++;
-		if (!p->suppress && (p->num_illegal_blocks % 12) == 0) {
+		if (!p->suppress &&
+		    p->num_illegal_blocks - p->last_illegal_blocks > 12) {
 			if (fix_problem(ctx, PR_1_TOO_MANY_BAD_BLOCKS, pctx)) {
 				p->clear = 1;
 				return BLOCK_ABORT;
@@ -1694,9 +1947,12 @@ static int process_block(ext2_filsys fs,
 				set_latch_flags(PR_LATCH_BLOCK,
 						PRL_SUPPRESS, 0);
 			}
+			p->last_illegal_blocks = p->num_illegal_blocks;
 		}
 		pctx->blk = blk;
 		pctx->blkcount = blockcnt;
+		if (problem == PR_1_EXTENT_CHANGED)
+			goto mark_used;
 		if (fix_problem(ctx, problem, pctx)) {
 			blk = *block_nr = 0;
 			ret_code = BLOCK_CHANGED;
@@ -1705,6 +1961,7 @@ static int process_block(ext2_filsys fs,
 			return 0;
 	}
 
+mark_used:
 	if (p->ino == EXT2_RESIZE_INO) {
 		/* 
 		 * The resize inode has already be sanity checked
Index: e2fsprogs/e2fsck/pass2.c
===================================================================
--- e2fsprogs.orig/e2fsck/pass2.c	2007-02-15 11:31:42.000000000 -0700
+++ e2fsprogs/e2fsck/pass2.c	2007-02-15 11:33:06.000000000 -0700
@@ -280,7 +280,16 @@ void e2fsck_pass2(e2fsck_t ctx)
 			ext2fs_mark_super_dirty(fs);
 		}
 	}
-	
+
+	if (!ctx->extent_files &&
+	    (sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_EXTENTS)) {
+		if (fs->flags & EXT2_FLAG_RW) {
+			sb->s_feature_incompat &=
+				~EXT3_FEATURE_INCOMPAT_EXTENTS;
+			ext2fs_mark_super_dirty(fs);
+		}
+	}
+
 #ifdef RESOURCE_TRACK
 	if (ctx->options & E2F_OPT_TIME2) {
 		e2fsck_clear_progbar(ctx);
Index: e2fsprogs/e2fsck/problem.c
===================================================================
--- e2fsprogs.orig/e2fsck/problem.c	2007-02-15 11:31:42.000000000 -0700
+++ e2fsprogs/e2fsck/problem.c	2007-02-15 13:32:14.000000000 -0700
@@ -779,6 +779,46 @@ static struct e2fsck_problem problem_tab
 	  N_("@a in @i %i has a hash (%N) which is @n (must be 0)\n"),
 	  PROMPT_CLEAR, PR_PREEN_OK },
 
+	/* indirect block corrupt */
+	{ PR_1_INDIRECT_BAD,
+	  N_("@i %i has corrupt indirect block\n"),
+	  PROMPT_CLEAR, PR_PREEN_OK },
+
+	/* inode has extents, superblock missing INCOMPAT_EXTENTS feature */
+	{ PR_1_EXTENT_FEATURE,
+	  N_("@i %i is in extent format, but @S is missing EXTENTS feature\n"),
+	  PROMPT_FIX, PR_PREEN_OK },
+
+	/* inode has EXTENTS_FL set, but is not an extent inode */
+	{ PR_1_SET_EXTENT_FL,
+	  N_("@i %i has EXTENT_FL set, but is not in extents format\n"),
+	  PROMPT_FIX, PR_PREEN_OK },
+
+	/* inode missing EXTENTS_FL, but is an extent inode */
+	{ PR_1_UNSET_EXTENT_FL,
+	  N_("@i %i missing EXTENT_FL, but is in extents format\n"),
+	  PROMPT_FIX, PR_PREEN_OK },
+
+	/* extent index corrupt */
+	{ PR_1_EXTENT_BAD,
+	  N_("@i %i has corrupt extent at @b %b (logical %B) length %N\n"),
+	  PROMPT_CLEAR, PR_PREEN_OK },
+
+	/* extent index corrupt */
+	{ PR_1_EXTENT_IDX_BAD,
+	  N_("@i %i has corrupt extent index at @b %b (logical %B) entry %N\n"),
+	  PROMPT_CLEAR, PR_PREEN_OK },
+
+	/* extent has high 16 bits set */
+	{ PR_1_EXTENT_HI,
+	  N_("High 16 bits of extent/index @b set\n"),
+	  PROMPT_CLEAR, PR_LATCH_EXTENT_HI|PR_PREEN_OK|PR_NO_OK|PR_PREEN_NOMSG},
+
+	/* extent has high 16 bits set header */
+	{ PR_1_EXTENT_HI_LATCH,
+	  N_("@i %i has high 16 bits of extent/index @b set\n"),
+	  PROMPT_CLEAR, PR_PREEN_OK | PR_NO_OK | PR_PREEN_NOMSG },
+
 	/* Pass 1b errors */
 
 	/* Pass 1B: Rescan for duplicate/bad blocks */
@@ -1508,6 +1548,7 @@ static struct latch_descr pr_latch_info[
 	{ PR_LATCH_LOW_DTIME, PR_1_ORPHAN_LIST_REFUGEES, 0 },
 	{ PR_LATCH_TOOBIG, PR_1_INODE_TOOBIG, 0 },
 	{ PR_LATCH_OPTIMIZE_DIR, PR_3A_OPTIMIZE_DIR_HEADER, PR_3A_OPTIMIZE_DIR_END },
+	{ PR_LATCH_EXTENT_HI, PR_1_EXTENT_HI_LATCH, 0 },
 	{ -1, 0, 0 },
 };
 
Index: e2fsprogs/e2fsck/problem.h
===================================================================
--- e2fsprogs.orig/e2fsck/problem.h	2007-02-15 11:31:42.000000000 -0700
+++ e2fsprogs/e2fsck/problem.h	2007-02-15 15:14:02.000000000 -0700
@@ -38,6 +38,7 @@ struct problem_context {
 #define PR_LATCH_LOW_DTIME 0x0070 /* Latch for pass1 orphaned list refugees */
 #define PR_LATCH_TOOBIG	0x0080	/* Latch for file to big errors */
 #define PR_LATCH_OPTIMIZE_DIR 0x0090 /* Latch for optimize directories */
+#define PR_LATCH_EXTENT_HI 0x00A0 /* Latch for optimize directories */
 
 #define PR_LATCH(x)	((((x) & PR_LATCH_MASK) >> 4) - 1)
 
@@ -452,6 +453,33 @@ struct problem_context {
 /* wrong EA hash value */
 #define PR_1_ATTR_HASH			0x010054
 
+/* indirect block corrupt */
+#define PR_1_INDIRECT_BAD		0x010059
+
+/* wrong EXT3_FEATURE_INCOMPAT_EXTENTS flag */
+#define PR_1_EXTENT_FEATURE		0x010060
+
+/* EXT4_EXTENT_FL flag set on non-extent file */
+#define PR_1_SET_EXTENT_FL		0x010061
+
+/* EXT4_EXTENT_FL flag not set extent file */
+#define PR_1_UNSET_EXTENT_FL		0x010062
+
+/* extent index corrupt */
+#define PR_1_EXTENT_BAD			0x010063
+
+/* extent index corrupt */
+#define PR_1_EXTENT_IDX_BAD		0x010064
+
+/* extent/index has high 16 bits set - header */
+#define PR_1_EXTENT_HI			0x010065
+
+/* extent/index has high 16 bits set */
+#define PR_1_EXTENT_HI_LATCH		0x010066
+
+/* extent/index was modified & repaired - not really a problem */
+#define PR_1_EXTENT_CHANGED		0x010067
+
 /*
  * Pass 1b errors
  */
Index: e2fsprogs/lib/ext2fs/Makefile.in
===================================================================
--- e2fsprogs.orig/lib/ext2fs/Makefile.in	2007-02-15 11:31:42.000000000 -0700
+++ e2fsprogs/lib/ext2fs/Makefile.in	2007-02-15 11:33:06.000000000 -0700
@@ -35,6 +35,7 @@ OBJS= $(DEBUGFS_LIB_OBJS) $(RESIZE_LIB_O
 	dir_iterate.o \
 	expanddir.o \
 	ext_attr.o \
+	extents.o \
 	finddev.o \
 	flushb.o \
 	freefs.o \
@@ -90,6 +91,7 @@ SRCS= ext2_err.c \
 	$(srcdir)/dupfs.c \
 	$(srcdir)/expanddir.c \
 	$(srcdir)/ext_attr.c \
+	$(srcdir)/extents.c \
 	$(srcdir)/fileio.c \
 	$(srcdir)/finddev.c \
 	$(srcdir)/flushb.c \
@@ -132,6 +134,7 @@ SRCS= ext2_err.c \
 	$(srcdir)/tst_bitops.c \
 	$(srcdir)/tst_byteswap.c \
 	$(srcdir)/tst_getsize.c \
+	$(srcdir)/tst_types.c \
 	$(srcdir)/tst_iscan.c
 
 HFILES= bitops.h ext2fs.h ext2_io.h ext2_fs.h ext2_ext_attr.h ext3_extents.h
@@ -372,6 +375,10 @@ ext_attr.o: $(srcdir)/ext_attr.c $(srcdi
  $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2_ext_attr.h \
  $(srcdir)/ext2fs.h $(srcdir)/ext2_fs.h $(top_srcdir)/lib/et/com_err.h \
  $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/bitops.h
+extents.o: $(srcdir)/extents.c $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext3_extents.h \
+ $(srcdir)/ext2fs.h $(srcdir)/ext2_fs.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/bitops.h
 fileio.o: $(srcdir)/fileio.c $(srcdir)/ext2_fs.h \
  $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
  $(srcdir)/ext2_fs.h $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_io.h \
@@ -516,6 +523,7 @@ unlink.o: $(srcdir)/unlink.c $(srcdir)/e
  $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/bitops.h
 valid_blk.o: $(srcdir)/valid_blk.c $(srcdir)/ext2_fs.h \
  $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext3_extents.h \
  $(srcdir)/ext2_fs.h $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_io.h \
  $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/bitops.h
 version.o: $(srcdir)/version.c $(srcdir)/ext2_fs.h \
Index: e2fsprogs/lib/ext2fs/block.c
===================================================================
--- e2fsprogs.orig/lib/ext2fs/block.c	2006-07-04 16:46:27.000000000 -0600
+++ e2fsprogs/lib/ext2fs/block.c	2007-02-15 15:51:01.000000000 -0700
@@ -17,24 +17,17 @@
 
 #include "ext2_fs.h"
 #include "ext2fs.h"
+#include "block.h"
 
-struct block_context {
-	ext2_filsys	fs;
-	int (*func)(ext2_filsys	fs,
-		    blk_t	*blocknr,
-		    e2_blkcnt_t	bcount,
-		    blk_t	ref_blk,
-		    int		ref_offset,
-		    void	*priv_data);
-	e2_blkcnt_t	bcount;
-	int		bsize;
-	int		flags;
-	errcode_t	errcode;
-	char	*ind_buf;
-	char	*dind_buf;
-	char	*tind_buf;
-	void	*priv_data;
-};
+#ifdef EXT_DEBUG
+void ext_show_inode(struct ext2_inode *inode, ext2_ino_t ino)
+{
+	printf("inode: %u blocks: %u\n",
+	       ino, inode->i_blocks);
+}
+#else
+#define ext_show_inode(inode, ino) do { } while (0)
+#endif
 
 static int block_iterate_ind(blk_t *ind_block, blk_t ref_block,
 			     int ref_offset, struct block_context *ctx)
@@ -276,7 +269,6 @@ errcode_t ext2fs_block_iterate2(ext2_fil
 				void *priv_data)
 {
 	int	i;
-	int	got_inode = 0;
 	int	ret = 0;
 	blk_t	blocks[EXT2_N_BLOCKS];	/* directory data blocks */
 	struct ext2_inode inode;
@@ -286,19 +278,20 @@ errcode_t ext2fs_block_iterate2(ext2_fil
 
 	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
 
+	ctx.errcode = ext2fs_read_inode(fs, ino, &inode);
+	if (ctx.errcode)
+		return ctx.errcode;
+
 	/*
 	 * Check to see if we need to limit large files
 	 */
 	if (flags & BLOCK_FLAG_NO_LARGE) {
-		ctx.errcode = ext2fs_read_inode(fs, ino, &inode);
-		if (ctx.errcode)
-			return ctx.errcode;
-		got_inode = 1;
 		if (!LINUX_S_ISDIR(inode.i_mode) &&
 		    (inode.i_size_high != 0))
 			return EXT2_ET_FILE_TOO_BIG;
 	}
 
+	/* The in-memory inode may have been changed by e2fsck */
 	retval = ext2fs_get_blocks(fs, ino, blocks);
 	if (retval)
 		return retval;
@@ -325,10 +318,6 @@ errcode_t ext2fs_block_iterate2(ext2_fil
 	 */
 	if ((fs->super->s_creator_os == EXT2_OS_HURD) &&
 	    !(flags & BLOCK_FLAG_DATA_ONLY)) {
-		ctx.errcode = ext2fs_read_inode(fs, ino, &inode);
-		if (ctx.errcode)
-			goto abort_exit;
-		got_inode = 1;
 		if (inode.osd1.hurd1.h_i_translator) {
 			ret |= (*ctx.func)(fs,
 					   &inode.osd1.hurd1.h_i_translator,
@@ -338,7 +327,16 @@ errcode_t ext2fs_block_iterate2(ext2_fil
 				goto abort_exit;
 		}
 	}
-	
+
+	/* Iterate over normal data blocks with extents.
+	 * We can't do any fixing here because this gets called by other
+	 * callers than e2fsck_pass1->check_blocks(). */
+	if (inode.i_flags & EXT4_EXTENTS_FL) {
+		ext_show_inode(&inode, ino);
+		ret |= block_iterate_extents(blocks, sizeof(blocks), 0, 0,&ctx);
+		goto abort_exit;
+	}
+
 	/*
 	 * Iterate over normal data blocks
 	 */
@@ -373,11 +371,6 @@ errcode_t ext2fs_block_iterate2(ext2_fil
 
 abort_exit:
 	if (ret & BLOCK_CHANGED) {
-		if (!got_inode) {
-			retval = ext2fs_read_inode(fs, ino, &inode);
-			if (retval)
-				return retval;
-		}
 		for (i=0; i < EXT2_N_BLOCKS; i++)
 			inode.i_block[i] = blocks[i];
 		retval = ext2fs_write_inode(fs, ino, &inode);
Index: e2fsprogs/lib/ext2fs/block.h
===================================================================
--- e2fsprogs.orig/lib/ext2fs/block.h	2007-02-14 10:49:32.414157760 -0700
+++ e2fsprogs/lib/ext2fs/block.h	2007-02-15 11:33:06.000000000 -0700
@@ -0,0 +1,33 @@
+/*
+ * block.h --- header for block iteration in block.c, extent.c
+ * 
+ * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+struct block_context {
+	ext2_filsys	fs;
+	int (*func)(ext2_filsys	fs,
+		    blk_t	*blocknr,
+		    e2_blkcnt_t	bcount,
+		    blk_t	ref_blk,
+		    int		ref_offset,
+		    void	*priv_data);
+	e2_blkcnt_t	bcount;
+	int		bsize;
+	int		flags;
+	errcode_t	errcode;
+	char	*ind_buf;
+	char	*dind_buf;
+	char	*tind_buf;
+	void	*priv_data;
+};
+
+/* libext2fs nternal function, in extent.c */
+extern int block_iterate_extents(void *eh_buf, unsigned bufsize,blk_t ref_block,
+				 int ref_offset EXT2FS_ATTR((unused)),
+				 struct block_context *ctx);
Index: e2fsprogs/lib/ext2fs/bmap.c
===================================================================
--- e2fsprogs.orig/lib/ext2fs/bmap.c	2006-07-04 16:46:27.000000000 -0600
+++ e2fsprogs/lib/ext2fs/bmap.c	2007-02-15 11:33:06.000000000 -0700
@@ -17,6 +17,7 @@
 
 #include "ext2_fs.h"
 #include "ext2fs.h"
+#include "ext3_extents.h"
 
 #if defined(__GNUC__) && !defined(NO_INLINE_FUNCS)
 #define _BMAP_INLINE_	__inline__
@@ -31,6 +32,65 @@ extern errcode_t ext2fs_bmap(ext2_filsys
 
 #define inode_bmap(inode, nr) ((inode)->i_block[(nr)])
 
+/* see also block_iterate_extents() */
+static errcode_t block_bmap_extents(void *eh_buf, unsigned bufsize,
+				    ext2_filsys fs, blk_t block,blk_t *phys_blk)
+{
+	struct ext3_extent_header *eh = eh_buf;
+	struct ext3_extent *ex;
+	errcode_t ret = 0;
+	int i;
+
+	ret = ext2fs_extent_header_verify(eh, bufsize);
+	if (ret)
+		return ret;
+
+	if (eh->eh_depth == 0) {
+		ex = EXT_FIRST_EXTENT(eh);
+		for (i = 0; i < eh->eh_entries; i++, ex++) {
+			if (block < ex->ee_block)
+				continue;
+
+			if (block < ex->ee_block + ex->ee_len)
+				/* FIXME: 48-bit */
+				*phys_blk = ex->ee_start + block - ex->ee_block;
+
+			/* only the first extent > block could hold the block
+			 * otherwise the extents would overlap */
+			break;
+		}
+	} else {
+		struct ext3_extent_idx *ix;
+		char *block_buf;
+
+		ret = ext2fs_get_mem(fs->blocksize, &block_buf);
+		if (ret)
+			return ret;
+
+		ix = EXT_FIRST_INDEX(eh);
+		for (i = 0; i < eh->eh_entries; i++, ix++) {
+			if (block < ix->ei_block)
+				continue;
+
+			ret = io_channel_read_blk(fs->io, ix->ei_leaf, 1,
+						  block_buf);
+			if (ret)
+				goto free_buf;
+
+			ret = block_bmap_extents(block_buf, fs->blocksize,
+						 fs, block, phys_blk);
+
+			/* only the first extent > block could hold the block
+			 * otherwise the extents would overlap */
+			break;
+		}
+
+	free_buf:
+		ext2fs_free_mem(&block_buf);
+	}
+	return ret;
+}
+
 static _BMAP_INLINE_ errcode_t block_ind_bmap(ext2_filsys fs, int flags, 
 					      blk_t ind, char *block_buf, 
 					      int *blocks_alloc,
@@ -155,6 +215,16 @@ errcode_t ext2fs_bmap(ext2_filsys fs, ex
 			return retval;
 		inode = &inode_buf;
 	}
+
+	if (inode->i_flags & EXT4_EXTENTS_FL) {
+		if (bmap_flags) /* unsupported as yet */
+			return EXT2_ET_BLOCK_ALLOC_FAIL;
+		retval = block_bmap_extents(inode->i_block,
+					    sizeof(inode->i_block),
+					    fs, block, phys_blk);
+		goto done;
+	}
+
 	addr_per_block = (blk_t) fs->blocksize >> 2;
 
 	if (!block_buf) {
Index: e2fsprogs/lib/ext2fs/ext2_err.et.in
===================================================================
--- e2fsprogs.orig/lib/ext2fs/ext2_err.et.in	2006-07-14 08:46:05.000000000 -0600
+++ e2fsprogs/lib/ext2fs/ext2_err.et.in	2007-02-15 11:33:06.000000000 -0700
@@ -296,5 +296,17 @@ ec	EXT2_ET_RESIZE_INODE_CORRUPT,
 ec	EXT2_ET_SET_BMAP_NO_IND,
 	"Missing indirect block not present"
 
+ec	EXT2_ET_EXTENT_HEADER_BAD,
+	"Corrupt extent header"
+
+ec	EXT2_ET_EXTENT_INDEX_BAD,
+	"Corrupt extent index"
+
+ec	EXT2_ET_EXTENT_LEAF_BAD,
+	"Corrupt extent"
+
+ec	EXT2_ET_EXTENT_NO_SPACE,
+	"No free space in extent map"
+
 	end
 
Index: e2fsprogs/lib/ext2fs/ext2fs.h
===================================================================
--- e2fsprogs.orig/lib/ext2fs/ext2fs.h	2007-02-15 11:31:42.000000000 -0700
+++ e2fsprogs/lib/ext2fs/ext2fs.h	2007-02-15 15:43:56.000000000 -0700
@@ -452,12 +452,14 @@ typedef struct ext2_icount *ext2_icount_
 					 EXT2_FEATURE_INCOMPAT_COMPRESSION|\
 					 EXT3_FEATURE_INCOMPAT_JOURNAL_DEV|\
 					 EXT2_FEATURE_INCOMPAT_META_BG|\
-					 EXT3_FEATURE_INCOMPAT_RECOVER)
+					 EXT3_FEATURE_INCOMPAT_RECOVER|\
+					 EXT3_FEATURE_INCOMPAT_EXTENTS)
 #else
 #define EXT2_LIB_FEATURE_INCOMPAT_SUPP	(EXT2_FEATURE_INCOMPAT_FILETYPE|\
 					 EXT3_FEATURE_INCOMPAT_JOURNAL_DEV|\
 					 EXT2_FEATURE_INCOMPAT_META_BG|\
-					 EXT3_FEATURE_INCOMPAT_RECOVER)
+					 EXT3_FEATURE_INCOMPAT_RECOVER|\
+					 EXT3_FEATURE_INCOMPAT_EXTENTS)
 #endif
 #define EXT2_LIB_FEATURE_RO_COMPAT_SUPP	(EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER|\
 					 EXT2_FEATURE_RO_COMPAT_LARGE_FILE)
@@ -721,6 +723,21 @@ extern errcode_t ext2fs_adjust_ea_refcou
 					   char *block_buf,
 					   int adjust, __u32 *newcount);
 
+/* extent.c */
+errcode_t ext2fs_extent_header_verify(struct ext3_extent_header *eh, int size);
+errcode_t ext2fs_extent_verify(ext2_filsys fs, struct ext3_extent *ex,
+			       struct ext3_extent *ex_prev,
+			       struct ext3_extent_idx *ix, int ix_len);
+errcode_t ext2fs_extent_index_verify(ext2_filsys fs,
+				     struct ext3_extent_idx *ix,
+				     struct ext3_extent_idx *ix_prev);
+errcode_t ext2fs_extent_remove(struct ext3_extent_header *eh,
+			       struct ext3_extent *ex);
+errcode_t ext2fs_extent_split(ext2_filsys fs, struct ext3_extent_header **eh,
+			      struct ext3_extent **ex, int count, int *flag);
+errcode_t ext2fs_extent_index_remove(struct ext3_extent_header *eh,
+				     struct ext3_extent_idx *ix);
+
 /* fileio.c */
 extern errcode_t ext2fs_file_open2(ext2_filsys fs, ext2_ino_t ino,
 				   struct ext2_inode *inode,
@@ -775,6 +792,8 @@ extern errcode_t ext2fs_image_bitmap_rea
 /* ind_block.c */
 errcode_t ext2fs_read_ind_block(ext2_filsys fs, blk_t blk, void *buf);
 errcode_t ext2fs_write_ind_block(ext2_filsys fs, blk_t blk, void *buf);
+errcode_t ext2fs_read_ext_block(ext2_filsys fs, blk_t blk, void *buf);
+errcode_t ext2fs_write_ext_block(ext2_filsys fs, blk_t blk, void *buf);
 
 /* initialize.c */
 extern errcode_t ext2fs_initialize(const char *name, int flags,
Index: e2fsprogs/lib/ext2fs/extents.c
===================================================================
--- e2fsprogs.orig/lib/ext2fs/extents.c	2007-02-14 10:49:32.414157760 -0700
+++ e2fsprogs/lib/ext2fs/extents.c	2007-02-15 16:56:49.000000000 -0700
@@ -0,0 +1,478 @@
+/*
+ * extent.c --- iterate over all blocks in an extent-mapped inode
+ *
+ * Copyright (C) 2005 Alex Tomas <alex@xxxxxxxxxxxxx>
+ * Copyright (C) 2006 Andreas Dilger <adilger@xxxxxxxxxxxxx>
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+#include "block.h"
+
+#ifdef EXT_DEBUG
+void ext_show_header(struct ext3_extent_header *eh)
+{
+	printf("header: magic=%x entries=%u max=%u depth=%u generation=%u\n",
+	       eh->eh_magic, eh->eh_entries, eh->eh_max, eh->eh_depth,
+	       eh->eh_generation);
+}
+
+void ext_show_index(struct ext3_extent_idx *ix)
+{
+	printf("index: block=%u leaf=%u leaf_hi=%u unused=%u\n",
+	       ix->ei_block, ix->ei_leaf, ix->ei_leaf_hi, ix->ei_unused);
+}
+
+void ext_show_extent(struct ext3_extent *ex)
+{
+	printf("extent: block=%u-%u len=%u start=%u start_hi=%u\n",
+	       ex->ee_block, ex->ee_block + ex->ee_len - 1,
+	       ex->ee_len, ex->ee_start, ex->ee_start_hi);
+}
+
+#define ext_printf(fmt, args...) printf(fmt, ## args)
+#else
+#define ext_show_header(eh) do { } while (0)
+#define ext_show_index(ix) do { } while (0)
+#define ext_show_extent(ex) do { } while (0)
+#define ext_printf(fmt, args...) do { } while (0)
+#endif
+
+errcode_t ext2fs_extent_header_verify(struct ext3_extent_header *eh, int size)
+{
+	int eh_max, entry_size;
+
+	ext_show_header(eh);
+	if (eh->eh_magic != EXT3_EXT_MAGIC)
+		return EXT2_ET_EXTENT_HEADER_BAD;
+	if (eh->eh_entries > eh->eh_max)
+		return EXT2_ET_EXTENT_HEADER_BAD;
+	if (eh->eh_depth == 0)
+		entry_size = sizeof(struct ext3_extent);
+	else
+		entry_size = sizeof(struct ext3_extent_idx);
+
+	eh_max = (size - sizeof(*eh)) / entry_size;
+	/* Allow two extent-sized items at the end of the block, for
+	 * ext4_extent_tail with checksum in the future. */
+	if (eh->eh_max > eh_max || eh->eh_max < eh_max - 2)
+		return EXT2_ET_EXTENT_HEADER_BAD;
+
+	return 0;
+}
+
+/* Verify that a single extent @ex is valid.  If @ex_prev is passed in,
+ * then this was the previous logical extent in this block and we can
+ * do additional sanity checking (though in case of error we don't know
+ * which of the two extents is bad).  Similarly, if @ix is passed in
+ * we can check that this extent is logically part of the index that
+ * refers to it (though again we can't know which of the two is bad). */
+errcode_t ext2fs_extent_verify(ext2_filsys fs, struct ext3_extent *ex,
+			       struct ext3_extent *ex_prev,
+			       struct ext3_extent_idx *ix, int ix_len)
+{
+	ext_show_extent(ex);
+	/* FIXME: 48-bit support */
+	if (ex->ee_start > fs->super->s_blocks_count)
+		return EXT2_ET_EXTENT_LEAF_BAD;
+
+	if (ex->ee_len == 0)
+		return EXT2_ET_EXTENT_LEAF_BAD;
+
+	if (ex->ee_len >= fs->super->s_blocks_per_group)
+		return EXT2_ET_EXTENT_LEAF_BAD;
+
+	if (ex_prev) {
+		/* We can't have a zero logical block except for first index */
+		if (ex->ee_block == 0)
+			return EXT2_ET_EXTENT_LEAF_BAD;
+
+		/* FIXME: 48-bit support */
+		/* extents must be in logical offset order */
+		if (ex->ee_block < ex_prev->ee_block + ex_prev->ee_len)
+			return EXT2_ET_EXTENT_LEAF_BAD;
+
+		/* extents must not overlap physical blocks */
+		if ((ex->ee_start < ex_prev->ee_start + ex_prev->ee_len) &&
+		    (ex->ee_start + ex->ee_len > ex_prev->ee_start))
+			return EXT2_ET_EXTENT_LEAF_BAD;
+	}
+
+	if (ix) {
+		/* FIXME: 48-bit support */
+		if (ex->ee_block < ix->ei_block)
+			return EXT2_ET_EXTENT_LEAF_BAD;
+
+		if (ix_len && ex->ee_block + ex->ee_len > ix->ei_block + ix_len)
+			return EXT2_ET_EXTENT_LEAF_BAD;
+	}
+
+	return 0;
+}
+
+errcode_t ext2fs_extent_index_verify(ext2_filsys fs, struct ext3_extent_idx *ix,
+				     struct ext3_extent_idx *ix_prev)
+{
+	ext_show_index(ix);
+	/* FIXME: 48-bit support */
+	if (ix->ei_leaf > fs->super->s_blocks_count)
+		return EXT2_ET_EXTENT_INDEX_BAD;
+
+	if (ix_prev == NULL)
+		return 0;
+
+	/* We can't have a zero logical block except for first index */
+	if (ix->ei_block == 0)
+		return EXT2_ET_EXTENT_INDEX_BAD;
+
+	if (ix->ei_block <= ix_prev->ei_block)
+		return EXT2_ET_EXTENT_INDEX_BAD;
+
+	return 0;
+}
+
+errcode_t ext2fs_extent_remove(struct ext3_extent_header *eh,
+			       struct ext3_extent *ex)
+{
+	int offs = ex - EXT_FIRST_EXTENT(eh);
+
+	if (offs < 0 || offs > eh->eh_entries)
+		return EXT2_ET_EXTENT_LEAF_BAD;
+
+	ext_printf("remove extent: offset %u\n", offs);
+
+	memmove(ex, ex + 1, (eh->eh_entries - offs - 1) * sizeof(*ex));
+	--eh->eh_entries;
+
+	return 0;
+}
+
+static errcode_t ext2fs_extent_split_internal(struct ext3_extent_header *eh,
+					      struct ext3_extent *ex, int offs)
+{
+	int entry = ex - EXT_FIRST_EXTENT(eh);
+	struct ext3_extent *ex_new = ex + 1;
+
+	ext_printf("split: ee_len: %u ee_block: %u ee_start: %u offset: %u\n",
+		   ex->ee_len, ex->ee_block, ex->ee_start, offs);
+	memmove(ex_new, ex, (eh->eh_entries - entry) * sizeof(*ex));
+	++eh->eh_entries;
+
+	ex->ee_len = offs;
+	/* FIXME: 48-bit support */
+	ex_new->ee_len -= offs;
+	ex_new->ee_block += offs;
+	ex_new->ee_start += offs;
+
+	return 0;
+}
+
+errcode_t ext2fs_extent_split(ext2_filsys fs,
+			      struct ext3_extent_header **eh_orig,
+			      struct ext3_extent **ex_orig, int offs, int *flag)
+{
+	struct ext3_extent_header *eh_parent = *eh_orig;
+	int retval, entry = *ex_orig - EXT_FIRST_EXTENT(eh_parent);
+	blk_t new_block;
+	char *buf;
+	struct ext3_extent_idx *ei = EXT_FIRST_INDEX(eh_parent);
+
+	if (entry < 0 || entry > (*eh_orig)->eh_entries)
+		return EXT2_ET_EXTENT_LEAF_BAD;
+
+	if (offs > (*ex_orig)->ee_len)
+		return EXT2_ET_EXTENT_LEAF_BAD;
+
+	if (eh_parent->eh_entries >= eh_parent->eh_max) {
+		ext_printf("split: eh_entries: %u eh_max: %u\n",
+			   eh_parent->eh_entries, eh_parent->eh_max);
+		if (eh_parent->eh_max == 4) {
+			struct ext3_extent_header *eh_child;
+			struct ext3_extent *ex_child;
+
+			retval = ext2fs_get_mem(fs->blocksize, &buf);
+
+			if (retval)
+				return EXT2_ET_EXTENT_NO_SPACE;
+
+			memset(buf, 0, fs->blocksize);
+			memcpy(buf, eh_parent, sizeof(*eh_parent) +
+			       eh_parent->eh_entries * sizeof(*ex_child));
+			eh_child = (struct ext3_extent_header *)buf;
+
+			eh_child->eh_max = (fs->blocksize -
+					    sizeof(struct ext3_extent_header)) /
+					   sizeof(struct ext3_extent);
+			retval = ext2fs_new_block(fs, (*ex_orig)->ee_block, 0,
+						  &new_block);
+			if (retval)
+				return EXT2_ET_EXTENT_NO_SPACE;
+
+			retval = io_channel_write_blk(fs->io, new_block, 1,buf);
+			if (retval)
+				return EXT2_ET_EXTENT_NO_SPACE;
+
+			eh_parent->eh_entries = 1;
+			eh_parent->eh_depth = 1;
+
+			ex_child = EXT_FIRST_EXTENT(eh_child);
+			ei->ei_block = ex_child->ee_block;
+			/*XXX 48 bit support*/
+			ei->ei_leaf = new_block;
+
+			*eh_orig = eh_child;
+			*ex_orig = EXT_FIRST_EXTENT(eh_child)  +
+				sizeof(struct ext3_extent) * entry;
+
+			*flag = BLOCK_CHANGED;
+		} else {
+			return EXT2_ET_EXTENT_NO_SPACE;
+		}
+	}
+
+	return ext2fs_extent_split_internal(*eh_orig, *ex_orig, offs);
+}
+
+errcode_t ext2fs_extent_index_remove(struct ext3_extent_header *eh,
+				     struct ext3_extent_idx *ix)
+{
+	struct ext3_extent_idx *first = EXT_FIRST_INDEX(eh);
+	int offs = ix - first;
+
+	ext_printf("remove index: offset %u\n", offs);
+
+	memmove(ix, ix + 1, (eh->eh_entries - offs - 1) * sizeof(*ix));
+	--eh->eh_entries;
+
+	return 0;
+}
+
+/* Internal function for ext2fs_block_iterate2() to recursively walk the
+ * extent tree, with a callback function for each block.  We also call the
+ * callback function on index blocks unless BLOCK_FLAG_DATA_ONLY is given.
+ * We traverse the tree in-order (internal nodes before their children)
+ * unless BLOCK_FLAG_DEPTH_FIRST is given.
+ *
+ * See also block_bmap_extents(). */
+int block_iterate_extents(void *eh_buf, unsigned bufsize, blk_t ref_block,
+			  int ref_offset EXT2FS_ATTR((unused)),
+			  struct block_context *ctx)
+{
+	struct ext3_extent_header *orig_eh, *eh;
+	struct ext3_extent *ex, *ex_prev = NULL;
+	int ret = 0;
+	int item, offs, flags, split_flag = 0;
+	blk_t  block_address;
+
+	orig_eh = eh = eh_buf;
+
+	if (ext2fs_extent_header_verify(eh, bufsize))
+		return BLOCK_ERROR;
+
+	if (eh->eh_depth == 0) {
+		ex = EXT_FIRST_EXTENT(eh);
+		for (item = 0; item < eh->eh_entries; item++, ex++) {
+			ext_show_extent(ex);
+			for (offs = 0; offs < ex->ee_len; offs++) {
+				block_address = ex->ee_start + offs;
+				flags = (*ctx->func)(ctx->fs, &block_address,
+						     (ex->ee_block + offs),
+						     ref_block, item,
+						     ctx->priv_data);
+				if (flags & (BLOCK_ABORT | BLOCK_ERROR)) {
+					ret |= flags &(BLOCK_ABORT|BLOCK_ERROR);
+					return ret;
+				}
+				if (!(flags & BLOCK_CHANGED))
+					continue;
+
+				ext_printf("extent leaf changed: "
+					   "block was %u+%u = %u, now %u\n",
+					   ex->ee_start, offs,
+					   ex->ee_start + offs, block_address);
+
+				/* FIXME: 48-bit support */
+				if (ex_prev &&
+				    block_address ==
+				    ex_prev->ee_start + ex_prev->ee_len &&
+				    ex->ee_block + offs ==
+				    ex_prev->ee_block + ex_prev->ee_len) {
+					/* can merge block with prev extent */
+					ex_prev->ee_len++;
+					ex->ee_len--;
+					ret |= BLOCK_CHANGED;
+
+					if (ex->ee_len == 0) {
+						/* no blocks left in this one */
+						ext2fs_extent_remove(eh, ex);
+						item--; ex--;
+						break;
+					} else {
+						/* XXX 48-bit */
+						ex->ee_start++;
+						ex->ee_block++;
+						offs--;
+					}
+
+				} else if (offs > 0 && /* implies ee_len > 1 */
+					   (ctx->errcode =
+					    ext2fs_extent_split(ctx->fs, &eh,
+								&ex, offs,
+								&split_flag)
+					    /* advance ex past newly split item,
+					     * comparison is bogus to make sure
+					     * increment doesn't change logic */
+					    || (offs > 0 && ex++ == NULL))) {
+					/* split before new block failed */
+					ret |= BLOCK_ABORT | BLOCK_ERROR;
+					return ret;
+
+				} else if (ex->ee_len > 1 &&
+					   (ctx->errcode =
+					    ext2fs_extent_split(ctx->fs, &eh,
+								&ex, 1,
+								&split_flag))) {
+					/* split after new block failed */
+					ret |= BLOCK_ABORT | BLOCK_ERROR;
+					return ret;
+
+				} else {
+					if (ex->ee_len != 1) {
+						/* this is an internal error */
+						ctx->errcode =
+						       EXT2_ET_EXTENT_INDEX_BAD;
+						ret |= BLOCK_ABORT |BLOCK_ERROR;
+						return ret;
+					}
+					/* FIXME: 48-bit */
+					ex->ee_start = block_address;
+					ret |= BLOCK_CHANGED;
+				}
+			}
+			ex_prev = ex;
+		}
+		/* Multi level split at depth == 0.
+		 * ex has been changed to point to  newly allocated block
+		 * buffer. And after returning  in this scenario, only inode is
+		 * updated with changed i_block. Hence explicitly write to the
+		 * block is required. */
+		if (split_flag == BLOCK_CHANGED) {
+			struct ext3_extent_idx *ix = EXT_FIRST_INDEX(orig_eh);
+			ctx->errcode = ext2fs_write_ext_block(ctx->fs,
+							      ix->ei_leaf, eh);
+		}
+	} else {
+		char *block_buf;
+		struct ext3_extent_idx *ix;
+
+		ret = ext2fs_get_mem(ctx->fs->blocksize, &block_buf);
+		if (ret)
+			return ret;
+
+		ext_show_header(eh);
+		ix = EXT_FIRST_INDEX(eh);
+		for (item = 0; item < eh->eh_entries; item++, ix++) {
+			ext_show_index(ix);
+			/* index is processed first in e2fsck case */
+			if (!(ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) &&
+			    !(ctx->flags & BLOCK_FLAG_DATA_ONLY)) {
+				block_address = ix->ei_leaf;
+				flags = (*ctx->func)(ctx->fs, &block_address,
+						     BLOCK_COUNT_IND, ref_block,
+						     item, ctx->priv_data);
+				if (flags & (BLOCK_ABORT | BLOCK_ERROR)) {
+					ret |= flags &(BLOCK_ABORT|BLOCK_ERROR);
+					goto free_buf;
+				}
+				if (flags & BLOCK_CHANGED) {
+					/* index has no more block, remove it */
+					/* FIXME: 48-bit */
+					ix->ei_leaf = block_address;
+					if (ix->ei_leaf == 0 &&
+					    ix->ei_leaf_hi == 0 &&
+					    ext2fs_extent_index_remove(eh, ix)){
+						ret |= BLOCK_ABORT |BLOCK_ERROR;
+						goto free_buf;
+					}
+
+					ret |= BLOCK_CHANGED;
+					if (ix->ei_leaf == 0 &&
+					    ix->ei_leaf_hi == 0) {
+						--item; --ix;
+						continue;
+					}
+					/* remapped? */
+				}
+			}
+			ctx->errcode = ext2fs_read_ext_block(ctx->fs,
+							     ix->ei_leaf,
+							     block_buf);
+			if (ctx->errcode) {
+				ret |= BLOCK_ERROR;
+				goto free_buf;
+			}
+			flags = block_iterate_extents(block_buf,
+						      ctx->fs->blocksize,
+						      ix->ei_leaf, item, ctx);
+			if (flags & BLOCK_CHANGED) {
+				struct ext3_extent_header *nh;
+				ctx->errcode =
+					ext2fs_write_ext_block(ctx->fs,
+							       ix->ei_leaf,
+							       block_buf);
+
+				nh = (struct ext3_extent_header *)block_buf;
+				if (nh->eh_entries == 0)
+					ix->ei_leaf = ix->ei_leaf_hi = 0;
+			}
+			if (flags & (BLOCK_ABORT | BLOCK_ERROR)) {
+				ret |= flags & (BLOCK_ABORT | BLOCK_ERROR);
+				goto free_buf;
+			}
+			if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) &&
+			    !(ctx->flags & BLOCK_FLAG_DATA_ONLY)) {
+				flags = (*ctx->func)(ctx->fs, &block_address,
+						     BLOCK_COUNT_IND, ref_block,
+						     item, ctx->priv_data);
+				if (flags & (BLOCK_ABORT | BLOCK_ERROR)) {
+					ret |= flags &(BLOCK_ABORT|BLOCK_ERROR);
+					goto free_buf;
+				}
+				if (flags & BLOCK_CHANGED)
+					/* FIXME: 48-bit */
+					ix->ei_leaf = block_address;
+			}
+
+			if (flags & BLOCK_CHANGED) {
+				/* index has no more block, remove it */
+				if (ix->ei_leaf == 0 && ix->ei_leaf_hi == 0 &&
+				    ext2fs_extent_index_remove(eh, ix)) {
+					ret |= BLOCK_ABORT |BLOCK_ERROR;
+					goto free_buf;
+				}
+
+				ret |= BLOCK_CHANGED;
+				if (ref_block == 0) {
+					--item; --ix;
+					continue;
+				}
+				/* remapped? */
+			}
+		}
+
+	free_buf:
+		ext2fs_free_mem(&block_buf);
+	}
+	return ret;
+}
Index: e2fsprogs/lib/ext2fs/ind_block.c
===================================================================
--- e2fsprogs.orig/lib/ext2fs/ind_block.c	2006-02-09 14:08:13.000000000 -0700
+++ e2fsprogs/lib/ext2fs/ind_block.c	2007-02-15 11:33:06.000000000 -0700
@@ -22,9 +22,6 @@
 errcode_t ext2fs_read_ind_block(ext2_filsys fs, blk_t blk, void *buf)
 {
 	errcode_t	retval;
-	blk_t		*block_nr;
-	int		i;
-	int		limit = fs->blocksize >> 2;
 
 	if ((fs->flags & EXT2_FLAG_IMAGE_FILE) &&
 	    (fs->io != fs->image_io))
@@ -36,7 +33,10 @@ errcode_t ext2fs_read_ind_block(ext2_fil
 	}
 #ifdef EXT2FS_ENABLE_SWAPFS
 	if (fs->flags & (EXT2_FLAG_SWAP_BYTES | EXT2_FLAG_SWAP_BYTES_READ)) {
-		block_nr = (blk_t *) buf;
+		int	limit = fs->blocksize >> 2;
+		blk_t	*block_nr = (blk_t *)buf;
+		int	i;
+
 		for (i = 0; i < limit; i++, block_nr++)
 			*block_nr = ext2fs_swab32(*block_nr);
 	}
@@ -46,16 +46,15 @@ errcode_t ext2fs_read_ind_block(ext2_fil
 
 errcode_t ext2fs_write_ind_block(ext2_filsys fs, blk_t blk, void *buf)
 {
-	blk_t		*block_nr;
-	int		i;
-	int		limit = fs->blocksize >> 2;
-
 	if (fs->flags & EXT2_FLAG_IMAGE_FILE)
 		return 0;
 
 #ifdef EXT2FS_ENABLE_SWAPFS
 	if (fs->flags & (EXT2_FLAG_SWAP_BYTES | EXT2_FLAG_SWAP_BYTES_WRITE)) {
-		block_nr = (blk_t *) buf;
+		int	limit = fs->blocksize >> 2;
+		blk_t	*block_nr = (blk_t *)buf;
+		int	i;
+
 		for (i = 0; i < limit; i++, block_nr++)
 			*block_nr = ext2fs_swab32(*block_nr);
 	}
@@ -64,3 +63,82 @@ errcode_t ext2fs_write_ind_block(ext2_fi
 }
 
 
+errcode_t ext2fs_read_ext_block(ext2_filsys fs, blk_t blk, void *buf)
+{
+	errcode_t	retval;
+
+	if ((fs->flags & EXT2_FLAG_IMAGE_FILE) &&
+	    (fs->io != fs->image_io))
+		memset(buf, 0, fs->blocksize);
+	else {
+		retval = io_channel_read_blk(fs->io, blk, 1, buf);
+		if (retval)
+			return retval;
+	}
+#ifdef EXT2FS_ENABLE_SWAPFS
+	if (fs->flags & (EXT2_FLAG_SWAP_BYTES | EXT2_FLAG_SWAP_BYTES_READ)) {
+		struct ext3_extent_header *eh = buf;
+		int i, limit;
+
+		ext2fs_swap_extent_header(eh);
+
+		if (eh->eh_depth == 0) {
+			struct ext3_extent *ex = EXT_FIRST_EXTENT(eh);
+
+			limit = (fs->blocksize - sizeof(*eh)) / sizeof(*ex);
+			if (eh->eh_entries < limit)
+				limit = eh->eh_entries;
+
+			for (i = 0; i < limit; i++, ex++)
+				ext2fs_swap_extent(ex);
+		} else {
+			struct ext3_extent_idx *ix = EXT_FIRST_INDEX(eh);
+
+			limit = (fs->blocksize - sizeof(*eh)) / sizeof(*ix);
+			if (eh->eh_entries < limit)
+				limit = eh->eh_entries;
+
+			for (i = 0; i < limit; i++, ix++)
+				ext2fs_swap_extent_index(ix);
+		}
+	}
+#endif
+	return 0;
+}
+
+errcode_t ext2fs_write_ext_block(ext2_filsys fs, blk_t blk, void *buf)
+{
+	if (fs->flags & EXT2_FLAG_IMAGE_FILE)
+		return 0;
+
+#ifdef EXT2FS_ENABLE_SWAPFS
+	if (fs->flags & (EXT2_FLAG_SWAP_BYTES | EXT2_FLAG_SWAP_BYTES_WRITE)) {
+		struct ext3_extent_header *eh = buf;
+		int i, limit;
+
+		if (eh->eh_depth == 0) {
+			struct ext3_extent *ex = EXT_FIRST_EXTENT(eh);
+
+			limit = (fs->blocksize - sizeof(*eh)) / sizeof(*ex);
+			if (eh->eh_entries < limit)
+				limit = eh->eh_entries;
+
+			for (i = 0; i < limit; i++, ex++)
+				ext2fs_swap_extent(ex);
+		} else {
+			struct ext3_extent_idx *ix = EXT_FIRST_INDEX(eh);
+
+			limit = (fs->blocksize - sizeof(*eh)) / sizeof(*ix);
+			if (eh->eh_entries < limit)
+				limit = eh->eh_entries;
+
+			for (i = 0; i < limit; i++, ix++)
+				ext2fs_swap_extent_index(ix);
+		}
+
+		ext2fs_swap_extent_header(eh);
+	}
+#endif
+	return io_channel_write_blk(fs->io, blk, 1, buf);
+}
+
Index: e2fsprogs/lib/ext2fs/swapfs.c
===================================================================
--- e2fsprogs.orig/lib/ext2fs/swapfs.c	2007-02-15 11:31:42.000000000 -0700
+++ e2fsprogs/lib/ext2fs/swapfs.c	2007-02-15 11:33:06.000000000 -0700
@@ -129,6 +129,28 @@ void ext2fs_swap_ext_attr(char *to, char
 	}
 }
 
+void ext2fs_swap_extent_header(struct ext3_extent_header *eh) {
+	eh->eh_magic = ext2fs_swab16(eh->eh_magic);
+	eh->eh_entries = ext2fs_swab16(eh->eh_entries);
+	eh->eh_max = ext2fs_swab16(eh->eh_max);
+	eh->eh_depth = ext2fs_swab16(eh->eh_depth);
+	eh->eh_generation = ext2fs_swab32(eh->eh_generation);
+}
+
+void ext2fs_swap_extent_index(struct ext3_extent_idx *ix) {
+	ix->ei_block = ext2fs_swab32(ix->ei_block);
+	ix->ei_leaf = ext2fs_swab32(ix->ei_leaf);
+	ix->ei_leaf_hi = ext2fs_swab16(ix->ei_leaf_hi);
+	ix->ei_unused = ext2fs_swab16(ix->ei_unused);
+}
+
+void ext2fs_swap_extent(struct ext3_extent *ex) {
+	ex->ee_block = ext2fs_swab32(ex->ee_block);
+	ex->ee_len = ext2fs_swab16(ex->ee_len);
+	ex->ee_start_hi =ext2fs_swab16(ex->ee_start_hi);
+	ex->ee_start = ext2fs_swab32(ex->ee_start);
+}
+
 void ext2fs_swap_inode_full(ext2_filsys fs, struct ext2_inode_large *t,
 			    struct ext2_inode_large *f, int hostorder,
 			    int bufsize)
Index: e2fsprogs/lib/ext2fs/valid_blk.c
===================================================================
--- e2fsprogs.orig/lib/ext2fs/valid_blk.c	2006-06-23 04:55:05.000000000 -0600
+++ e2fsprogs/lib/ext2fs/valid_blk.c	2007-02-15 11:33:06.000000000 -0700
@@ -19,6 +19,7 @@
 
 #include "ext2_fs.h"
 #include "ext2fs.h"
+#include "ext3_extents.h"
 
 /*
  * This function returns 1 if the inode's block entries actually
@@ -41,12 +42,23 @@ int ext2fs_inode_has_valid_blocks(struct
 	if (LINUX_S_ISLNK (inode->i_mode)) {
 		if (inode->i_file_acl == 0) {
 			/* With no EA block, we can rely on i_blocks */
-			if (inode->i_blocks == 0)
-				return 0;
+			if (inode->i_flags & EXT4_EXTENTS_FL) {
+				struct ext3_extent_header *eh;
+				eh = (struct ext3_extent_header *)inode->i_block;
+				if (eh->eh_entries == 0) 
+					return 0;
+			} else {
+				if (inode->i_blocks == 0)
+					return 0;
+			}
 		} else {
 			/* With an EA block, life gets more tricky */
 			if (inode->i_size >= EXT2_N_BLOCKS*4)
 				return 1; /* definitely using i_block[] */
+			/* 
+			 * we cant have EA + extents, so assume we aren't
+			 * using extents
+			 */
 			if (inode->i_size > 4 && inode->i_block[1] == 0)
 				return 1; /* definitely using i_block[] */
 			return 0; /* Probably a fast symlink */
Index: e2fsprogs/tests/f_bad_disconnected_inode/expect.1
===================================================================
--- e2fsprogs.orig/tests/f_bad_disconnected_inode/expect.1	2007-02-15 11:31:42.000000000 -0700
+++ e2fsprogs/tests/f_bad_disconnected_inode/expect.1	2007-02-15 11:33:06.000000000 -0700
@@ -1,4 +1,10 @@
 Pass 1: Checking inodes, blocks, and sizes
+Inode 15 has EXTENT_FL set, but is not in extents format
+Fix? yes
+
+Inode 16 has EXTENT_FL set, but is not in extents format
+Fix? yes
+
 Pass 2: Checking directory structure
 Pass 3: Checking directory connectivity
 /lost+found not found.  Create? yes
Index: e2fsprogs/tests/f_bbfile/expect.1
===================================================================
--- e2fsprogs.orig/tests/f_bbfile/expect.1	2006-02-09 14:08:13.000000000 -0700
+++ e2fsprogs/tests/f_bbfile/expect.1	2007-02-15 11:33:06.000000000 -0700
@@ -3,46 +3,60 @@ Filesystem did not have a UUID; generati
 Pass 1: Checking inodes, blocks, and sizes
 Group 0's inode bitmap (4) is bad.  Relocate? yes
 
+Inode 11 has corrupt indirect block
+Clear? yes
+
 Relocating group 0's inode bitmap from 4 to 43...
+Restarting e2fsck from the beginning...
+Pass 1: Checking inodes, blocks, and sizes
 
 Running additional passes to resolve blocks claimed by more than one inode...
 Pass 1B: Rescanning for multiply-claimed blocks
 Multiply-claimed block(s) in inode 2: 21
-Multiply-claimed block(s) in inode 11: 9 10 11 12 13 14 15 16 17 18 19 20
 Multiply-claimed block(s) in inode 12: 25 26
 Pass 1C: Scanning directories for inodes with multiply-claimed blocks
 Pass 1D: Reconciling multiply-claimed blocks
-(There are 3 inodes containing multiply-claimed blocks.)
+(There are 2 inodes containing multiply-claimed blocks.)
 
 File / (inode #2, mod time Sun Jan  2 08:29:13 1994) 
   has 1 multiply-claimed block(s), shared with 1 file(s):
 	<The bad blocks inode> (inode #1, mod time Sun Jul 17 00:47:58 1994)
 Clone multiply-claimed blocks? yes
 
-File /lost+found (inode #11, mod time Sun Jan  2 08:28:40 1994) 
-  has 12 multiply-claimed block(s), shared with 1 file(s):
-	<The bad blocks inode> (inode #1, mod time Sun Jul 17 00:47:58 1994)
-Clone multiply-claimed blocks? yes
-
 File /termcap (inode #12, mod time Sun Jan  2 08:29:13 1994) 
   has 2 multiply-claimed block(s), shared with 1 file(s):
 	<The bad blocks inode> (inode #1, mod time Sun Jul 17 00:47:58 1994)
 Clone multiply-claimed blocks? yes
 
 Pass 2: Checking directory structure
+Entry 'lost+found' in / (2) has deleted/unused inode 11.  Clear? yes
+
 Pass 3: Checking directory connectivity
+/lost+found not found.  Create? yes
+
 Pass 4: Checking reference counts
+Inode 2 ref count is 4, should be 3.  Fix? yes
+
 Pass 5: Checking group summary information
 Block bitmap differences:  +43
 Fix? yes
 
-Free blocks count wrong for group #0 (57, counted=41).
+Free blocks count wrong for group #0 (56, counted=52).
+Fix? yes
+
+Free blocks count wrong (56, counted=52).
+Fix? yes
+
+Free inodes count wrong for group #0 (19, counted=20).
+Fix? yes
+
+Directories count wrong for group #0 (3, counted=2).
 Fix? yes
 
-Free blocks count wrong (57, counted=41).
+Free inodes count wrong (19, counted=20).
 Fix? yes
 
 
 test_filesys: ***** FILE SYSTEM WAS MODIFIED *****
-test_filesys: 12/32 files (0.0% non-contiguous), 59/100 blocks
+test_filesys: 12/32 files (0.0% non-contiguous), 48/100 blocks
 Exit status is 1
Index: e2fsprogs/tests/f_bbfile/expect.2
===================================================================
--- e2fsprogs.orig/tests/f_bbfile/expect.2	2006-02-09 14:08:13.000000000 -0700
+++ e2fsprogs/tests/f_bbfile/expect.2	2007-02-15 11:33:06.000000000 -0700
@@ -3,5 +3,5 @@ Pass 2: Checking directory structure
 Pass 3: Checking directory connectivity
 Pass 4: Checking reference counts
 Pass 5: Checking group summary information
-test_filesys: 12/32 files (8.3% non-contiguous), 59/100 blocks
+test_filesys: 12/32 files (8.3% non-contiguous), 48/100 blocks
 Exit status is 0
Index: e2fsprogs/tests/f_lotsbad/expect.1
===================================================================
--- e2fsprogs.orig/tests/f_lotsbad/expect.1	2006-02-09 14:08:13.000000000 -0700
+++ e2fsprogs/tests/f_lotsbad/expect.1	2007-02-15 11:33:06.000000000 -0700
@@ -8,54 +8,41 @@ Inode 13, i_size is 15360, should be 122
 
 Inode 13, i_blocks is 32, should be 30.  Fix? yes
 
-Inode 12 has illegal block(s).  Clear? yes
+Inode 12 has corrupt indirect block
+Clear? yes
 
-Illegal block #12 (778398818) in inode 12.  CLEARED.
-Illegal block #13 (1768444960) in inode 12.  CLEARED.
-Illegal block #14 (1752375411) in inode 12.  CLEARED.
-Illegal block #15 (1684829551) in inode 12.  CLEARED.
-Illegal block #16 (1886349344) in inode 12.  CLEARED.
-Illegal block #17 (1819633253) in inode 12.  CLEARED.
-Illegal block #18 (1663072620) in inode 12.  CLEARED.
-Illegal block #19 (1735287144) in inode 12.  CLEARED.
-Illegal block #20 (1310731877) in inode 12.  CLEARED.
-Illegal block #21 (560297071) in inode 12.  CLEARED.
-Illegal block #22 (543512352) in inode 12.  CLEARED.
-Too many illegal blocks in inode 12.
-Clear inode? yes
+Inode 12, i_blocks is 34, should be 24.  Fix? yes
 
-Restarting e2fsck from the beginning...
-Pass 1: Checking inodes, blocks, and sizes
 Pass 2: Checking directory structure
-Entry 'termcap' in / (2) has deleted/unused inode 12.  Clear? yes
+Directory inode 13 has an unallocated block #16580876.  Allocate? yes
 
 Pass 3: Checking directory connectivity
 Pass 4: Checking reference counts
 Inode 2 ref count is 5, should be 4.  Fix? yes
 
 Pass 5: Checking group summary information
-Block bitmap differences:  -(27--41) -(44--45) -(74--90)
+Block bitmap differences:  -(38--41) -(74--90)
 Fix? yes
 
-Free blocks count wrong for group #0 (9, counted=43).
+Free blocks count wrong for group #0 (9, counted=30).
 Fix? yes
 
-Free blocks count wrong (9, counted=43).
+Free blocks count wrong (9, counted=30).
 Fix? yes
 
-Inode bitmap differences:  -12 -14
+Inode bitmap differences:  -14
 Fix? yes
 
-Free inodes count wrong for group #0 (18, counted=20).
+Free inodes count wrong for group #0 (18, counted=19).
 Fix? yes
 
 Directories count wrong for group #0 (4, counted=3).
 Fix? yes
 
-Free inodes count wrong (18, counted=20).
+Free inodes count wrong (18, counted=19).
 Fix? yes
 
 
 test_filesys: ***** FILE SYSTEM WAS MODIFIED *****
-test_filesys: 12/32 files (0.0% non-contiguous), 57/100 blocks
+test_filesys: 13/32 files (7.7% non-contiguous), 70/100 blocks
 Exit status is 1
Index: e2fsprogs/tests/f_messy_inode/expect.1
===================================================================
--- e2fsprogs.orig/tests/f_messy_inode/expect.1	2006-07-08 01:12:19.000000000 -0600
+++ e2fsprogs/tests/f_messy_inode/expect.1	2007-02-15 11:33:06.000000000 -0700
@@ -1,38 +1,36 @@
 Filesystem did not have a UUID; generating one.
 
 Pass 1: Checking inodes, blocks, and sizes
-Inode 14 has illegal block(s).  Clear? yes
-
-Illegal block #2 (4294901760) in inode 14.  CLEARED.
-Illegal block #3 (4294901760) in inode 14.  CLEARED.
-Illegal block #4 (4294901760) in inode 14.  CLEARED.
-Illegal block #5 (4294901760) in inode 14.  CLEARED.
-Illegal block #6 (4294901760) in inode 14.  CLEARED.
-Illegal block #7 (4294901760) in inode 14.  CLEARED.
-Illegal block #8 (4294901760) in inode 14.  CLEARED.
-Illegal block #9 (4294901760) in inode 14.  CLEARED.
-Illegal block #10 (4294901760) in inode 14.  CLEARED.
-Inode 14, i_size is 18446462598732849291, should be 2048.  Fix? yes
-
-Inode 14, i_blocks is 18, should be 4.  Fix? yes
+Inode 14 has corrupt indirect block
+Clear? yes
 
+Restarting e2fsck from the beginning...
+Pass 1: Checking inodes, blocks, and sizes
 Pass 2: Checking directory structure
-i_file_acl for inode 14 (/MAKEDEV) is 4294901760, should be zero.
-Clear? yes
+Entry 'MAKEDEV' in / (2) has deleted/unused inode 14.  Clear? yes
 
 Pass 3: Checking directory connectivity
 Pass 4: Checking reference counts
 Pass 5: Checking group summary information
-Block bitmap differences:  -(43--49)
+Block bitmap differences:  -(41--49)
+Fix? yes
+
+Free blocks count wrong for group #0 (68, counted=77).
+Fix? yes
+
+Free blocks count wrong (68, counted=77).
+Fix? yes
+
+Inode bitmap differences:  -14
 Fix? yes
 
-Free blocks count wrong for group #0 (68, counted=75).
+Free inodes count wrong for group #0 (3, counted=4).
 Fix? yes
 
-Free blocks count wrong (68, counted=75).
+Free inodes count wrong (3, counted=4).
 Fix? yes
 
 
 test_filesys: ***** FILE SYSTEM WAS MODIFIED *****
-test_filesys: 29/32 files (3.4% non-contiguous), 25/100 blocks
+test_filesys: 28/32 files (0.0% non-contiguous), 23/100 blocks
 Exit status is 1
Index: e2fsprogs/tests/f_messy_inode/expect.2
===================================================================
--- e2fsprogs.orig/tests/f_messy_inode/expect.2	2006-02-09 14:08:13.000000000 -0700
+++ e2fsprogs/tests/f_messy_inode/expect.2	2007-02-15 11:33:06.000000000 -0700
@@ -3,5 +3,5 @@ Pass 2: Checking directory structure
 Pass 3: Checking directory connectivity
 Pass 4: Checking reference counts
 Pass 5: Checking group summary information
-test_filesys: 29/32 files (0.0% non-contiguous), 25/100 blocks
+test_filesys: 28/32 files (0.0% non-contiguous), 23/100 blocks
 Exit status is 0

[Index of Archives]     [Reiser Filesystem Development]     [Ceph FS]     [Kernel Newbies]     [Security]     [Netfilter]     [Bugtraq]     [Linux FS]     [Yosemite National Park]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Device Mapper]     [Linux Media]

  Powered by Linux