lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:	Sat, 02 Feb 2008 01:25:59 -0700
From:	Andreas Dilger <adilger@....com>
To:	"Theodore Ts'o" <tytso@....edu>, linux-ext4@...r.kernel.org
Subject: Re: [PATCH][0/28] e2fsprogs-extents.patch


Support for checking 32-bit extents format inodes and the INCOMPAT_EXTENTS
feature.

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 (when
INCOMPAT_64BIT support is added), marked "FIXME: 48-bit support".

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-1.40.5/e2fsck/Makefile.in
===================================================================
--- e2fsprogs-1.40.5.orig/e2fsck/Makefile.in
+++ e2fsprogs-1.40.5/e2fsck/Makefile.in
@@ -256,6 +256,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-1.40.5/e2fsck/e2fsck.h
===================================================================
--- e2fsprogs-1.40.5.orig/e2fsck/e2fsck.h
+++ e2fsprogs-1.40.5/e2fsck/e2fsck.h
@@ -328,6 +328,7 @@ struct e2fsck_struct {
 	__u32 large_files;
 	__u32 fs_ext_attr_inodes;
 	__u32 fs_ext_attr_blocks;
+	__u32 extent_files;
 
 	/* misc fields */
 	time_t now;
Index: e2fsprogs-1.40.5/e2fsck/pass1.c
===================================================================
--- e2fsprogs-1.40.5.orig/e2fsck/pass1.c
+++ e2fsprogs-1.40.5/e2fsck/pass1.c
@@ -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
 	 * If the index flag is set, then this is a bogus
 	 * device/fifo/socket
 	 */
-	if (inode->i_flags & EXT2_INDEX_FL)
+	if (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);
@@ -484,7 +488,9 @@ void e2fsck_pass1(e2fsck_t ctx)
 	int		imagic_fs;
 	int		busted_fs_time = 0;
 	int		inode_size;
-	
+	struct ext3_extent_header *eh;
+	int		extent_fs;
+
 #ifdef RESOURCE_TRACK
 	init_resource_track(&rtrack, ctx->fs->io);
 #endif
@@ -515,6 +521,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
@@ -891,8 +898,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);
@@ -904,21 +910,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;
@@ -1426,10 +1486,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;
@@ -1443,7 +1516,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)) &&
@@ -1480,6 +1554,135 @@ 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, high_bits_ok = 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;
+
+	if (ctx->fs->super->s_blocks_count_hi) /* FIXME: 48-bit support ??? */
+		high_bits_ok = 1;
+
+	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++) {
+			if (ex->ee_start_hi && !high_bits_ok &&
+			    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++) {
+			if (ix->ei_leaf_hi && !high_bits_ok &&
+			    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.
@@ -1499,9 +1701,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);
@@ -1509,6 +1713,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;
 
@@ -1530,10 +1736,27 @@ static void check_blocks(e2fsck_t ctx, s
 		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)
@@ -1697,6 +1920,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().
  */
@@ -1775,7 +2001,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;
 	}
@@ -1792,9 +2019,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;
@@ -1804,9 +2056,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;
@@ -1815,6 +2070,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-1.40.5/e2fsck/pass2.c
===================================================================
--- e2fsprogs-1.40.5.orig/e2fsck/pass2.c
+++ e2fsprogs-1.40.5/e2fsck/pass2.c
@@ -285,7 +285,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-1.40.5/e2fsck/problem.c
===================================================================
--- e2fsprogs-1.40.5.orig/e2fsck/problem.c
+++ e2fsprogs-1.40.5/e2fsck/problem.c
@@ -784,6 +784,46 @@ static struct e2fsck_problem problem_tab
 	  N_("@i %i is a %It but it looks like it is really a directory.\n"),
 	  PROMPT_FIX, 0 },
 
+	/* 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 */
@@ -1518,6 +1558,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-1.40.5/e2fsck/problem.h
===================================================================
--- e2fsprogs-1.40.5.orig/e2fsck/problem.h
+++ e2fsprogs-1.40.5/e2fsck/problem.h
@@ -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 extent high bits set */
 
 #define PR_LATCH(x)	((((x) & PR_LATCH_MASK) >> 4) - 1)
 
@@ -455,6 +456,33 @@ struct problem_context {
 /* inode appears to be a directory */
 #define PR_1_TREAT_AS_DIRECTORY		0x010055
 
+/* 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-1.40.5/lib/ext2fs/Makefile.in
===================================================================
--- e2fsprogs-1.40.5.orig/lib/ext2fs/Makefile.in
+++ e2fsprogs-1.40.5/lib/ext2fs/Makefile.in
@@ -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 \
@@ -127,6 +129,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 \
 	$(srcdir)/unix_io.c \
 	$(srcdir)/unlink.c \
@@ -394,6 +397,10 @@ ext_attr.o: $(srcdir)/ext_attr.c $(srcdi
  $(srcdir)/ext2fs.h $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.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 $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
Index: e2fsprogs-1.40.5/lib/ext2fs/block.c
===================================================================
--- e2fsprogs-1.40.5.orig/lib/ext2fs/block.c
+++ e2fsprogs-1.40.5/lib/ext2fs/block.c
@@ -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-1.40.5/lib/ext2fs/block.h
===================================================================
--- /dev/null
+++ e2fsprogs-1.40.5/lib/ext2fs/block.h
@@ -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-1.40.5/lib/ext2fs/bmap.c
===================================================================
--- e2fsprogs-1.40.5.orig/lib/ext2fs/bmap.c
+++ e2fsprogs-1.40.5/lib/ext2fs/bmap.c
@@ -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 support */
+				*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,
@@ -149,6 +209,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-1.40.5/lib/ext2fs/ext2_err.et.in
===================================================================
--- e2fsprogs-1.40.5.orig/lib/ext2fs/ext2_err.et.in
+++ e2fsprogs-1.40.5/lib/ext2fs/ext2_err.et.in
@@ -326,5 +326,17 @@ ec	EXT2_ET_TDB_ERR_NOEXIST,
 ec	EXT2_ET_TDB_ERR_RDONLY,
 	"TDB: Write not permitted"
 
+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-1.40.5/lib/ext2fs/ext2fs.h
===================================================================
--- e2fsprogs-1.40.5.orig/lib/ext2fs/ext2fs.h
+++ e2fsprogs-1.40.5/lib/ext2fs/ext2fs.h
@@ -436,12 +436,14 @@ typedef struct ext2_icount *ext2_icount_
 					 EXT3_FEATURE_INCOMPAT_JOURNAL_DEV|\
 					 EXT2_FEATURE_INCOMPAT_META_BG|\
 					 EXT3_FEATURE_INCOMPAT_RECOVER|\
+ 					 EXT3_FEATURE_INCOMPAT_EXTENTS|\
 					 EXT4_FEATURE_INCOMPAT_FLEX_BG)
 #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_EXTENTS|\
 					 EXT4_FEATURE_INCOMPAT_FLEX_BG)
 #endif
 #define EXT2_LIB_FEATURE_RO_COMPAT_SUPP	(EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER|\
@@ -722,6 +724,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,
@@ -810,6 +827,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,
@@ -984,6 +1003,9 @@ extern void ext2fs_swap_inode_full(ext2_
 				   int bufsize);
 extern void ext2fs_swap_inode(ext2_filsys fs,struct ext2_inode *t,
 			      struct ext2_inode *f, int hostorder);
+extern void ext2fs_swap_extent_header(struct ext3_extent_header *eh);
+extern void ext2fs_swap_extent_index(struct ext3_extent_idx *ix);
+extern void ext2fs_swap_extent(struct ext3_extent *ex);
 
 /* valid_blk.c */
 extern int ext2fs_inode_has_valid_blocks(struct ext2_inode *inode);
Index: e2fsprogs-1.40.5/lib/ext2fs/extents.c
===================================================================
--- /dev/null
+++ e2fsprogs-1.40.5/lib/ext2fs/extents.c
@@ -0,0 +1,475 @@
+/*
+ * extent.c --- iterate over all blocks in an extent-mapped inode
+ *
+ * Copyright (C) 2005 Alex Tomas <alex@...sterfs.com>
+ * Copyright (C) 2006 Andreas Dilger <adilger@...sterfs.com>
+ *
+ * %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;
+			/* FIXME: 48-bit support*/
+			ei->ei_leaf = new_block;
+
+			*eh_orig = eh_child;
+			*ex_orig = EXT_FIRST_EXTENT(eh_child) + 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 {
+						/* FIXME: 48-bit support */
+						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 support */
+					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) {
+					ret |= BLOCK_CHANGED;
+					/* index has no more block, remove it */
+					/* FIXME: 48-bit support */
+					ix->ei_leaf = block_address;
+					if (ix->ei_leaf == 0 &&
+					    ix->ei_leaf_hi == 0) {
+						if(ext2fs_extent_index_remove(eh, ix)) {
+							ret |= BLOCK_ABORT |BLOCK_ERROR;
+							goto free_buf;
+						} else {
+							--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 support */
+					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-1.40.5/lib/ext2fs/ind_block.c
===================================================================
--- e2fsprogs-1.40.5.orig/lib/ext2fs/ind_block.c
+++ e2fsprogs-1.40.5/lib/ext2fs/ind_block.c
@@ -22,9 +22,9 @@
 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;
+	int	limit = fs->blocksize >> 2;
+	blk_t	*block_nr = (blk_t *)buf;
+	int	i;
 
 	if ((fs->flags & EXT2_FLAG_IMAGE_FILE) &&
 	    (fs->io != fs->image_io))
@@ -35,7 +35,6 @@ errcode_t ext2fs_read_ind_block(ext2_fil
 			return retval;
 	}
 #ifdef WORDS_BIGENDIAN
-	block_nr = (blk_t *) buf;
 	for (i = 0; i < limit; i++, block_nr++)
 		*block_nr = ext2fs_swab32(*block_nr);
 #endif
@@ -60,3 +59,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-1.40.5/lib/ext2fs/swapfs.c
===================================================================
--- e2fsprogs-1.40.5.orig/lib/ext2fs/swapfs.c
+++ e2fsprogs-1.40.5/lib/ext2fs/swapfs.c
@@ -142,11 +142,33 @@ 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)
 {
-	unsigned i, has_data_blocks, extra_isize;
+	unsigned i, has_data_blocks, extra_isize, has_extents;
 	int islnk = 0;
 	__u32 *eaf, *eat;
 
@@ -164,18 +186,46 @@ void ext2fs_swap_inode_full(ext2_filsys 
 	t->i_gid = ext2fs_swab16(f->i_gid);
 	t->i_links_count = ext2fs_swab16(f->i_links_count);
 	t->i_file_acl = ext2fs_swab32(f->i_file_acl);
-	if (hostorder)
-		has_data_blocks = ext2fs_inode_data_blocks(fs, 
+	if (hostorder) {
+		has_data_blocks = ext2fs_inode_data_blocks(fs,
 					   (struct ext2_inode *) f);
-	t->i_blocks = ext2fs_swab32(f->i_blocks);
-	if (!hostorder)
-		has_data_blocks = ext2fs_inode_data_blocks(fs, 
+		t->i_blocks = ext2fs_swab32(f->i_blocks);
+		has_extents = (f->i_flags & EXT4_EXTENTS_FL);
+		t->i_flags = ext2fs_swab32(f->i_flags);
+	} else {
+		t->i_blocks = ext2fs_swab32(f->i_blocks);
+		has_data_blocks = ext2fs_inode_data_blocks(fs,
 					   (struct ext2_inode *) t);
+		t->i_flags = ext2fs_swab32(f->i_flags);
+		has_extents = (t->i_flags & EXT4_EXTENTS_FL);
+	}
 	t->i_flags = ext2fs_swab32(f->i_flags);
 	t->i_dir_acl = ext2fs_swab32(f->i_dir_acl);
-	if (!islnk || has_data_blocks ) {
-		for (i = 0; i < EXT2_N_BLOCKS; i++)
-			t->i_block[i] = ext2fs_swab32(f->i_block[i]);
+	if (!islnk || has_data_blocks) {
+	        if (has_extents) {
+			struct ext3_extent_header *eh;
+			int max = EXT2_N_BLOCKS * sizeof(__u32) - sizeof(*eh);
+
+			memcpy(t->i_block, f->i_block, sizeof(f->i_block));
+			eh = (struct ext3_extent_header *)t->i_block;
+			ext2fs_swap_extent_header(eh);
+
+			if (!eh->eh_depth) {
+				struct ext3_extent *ex = EXT_FIRST_EXTENT(eh);
+				max = max / sizeof(struct ext3_extent);
+				for (i = 0; i < max; i++, ex++)
+					ext2fs_swap_extent(ex);
+			} else {
+				struct ext3_extent_idx *ix =
+					EXT_FIRST_INDEX(eh);
+				max = max / sizeof(struct ext3_extent_idx);
+				for (i = 0; i < max; i++, ix++)
+					ext2fs_swap_extent_index(ix);
+			}
+		} else {
+			for (i = 0; i < EXT2_N_BLOCKS; i++)
+				t->i_block[i] = ext2fs_swab32(f->i_block[i]);
+		}
 	} else if (t != f) {
 		for (i = 0; i < EXT2_N_BLOCKS; i++)
 			t->i_block[i] = f->i_block[i];
@@ -218,11 +268,13 @@ void ext2fs_swap_inode_full(ext2_filsys 
 	if (bufsize < (int) (sizeof(struct ext2_inode) + sizeof(__u16)))
 		return; /* no i_extra_isize field */
 
-	if (hostorder)
+	if (hostorder) {
 		extra_isize = f->i_extra_isize;
-	t->i_extra_isize = ext2fs_swab16(f->i_extra_isize);
-	if (!hostorder)
+		t->i_extra_isize = ext2fs_swab16(f->i_extra_isize);
+	} else {
+		t->i_extra_isize = ext2fs_swab16(f->i_extra_isize);
 		extra_isize = t->i_extra_isize;
+	}
 	if (extra_isize > EXT2_INODE_SIZE(fs->super) -
 				sizeof(struct ext2_inode)) {
 		/* this is error case: i_extra_size is too large */
Index: e2fsprogs-1.40.5/lib/ext2fs/valid_blk.c
===================================================================
--- e2fsprogs-1.40.5.orig/lib/ext2fs/valid_blk.c
+++ e2fsprogs-1.40.5/lib/ext2fs/valid_blk.c
@@ -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-1.40.5/tests/f_bad_disconnected_inode/expect.1
===================================================================
--- e2fsprogs-1.40.5.orig/tests/f_bad_disconnected_inode/expect.1
+++ e2fsprogs-1.40.5/tests/f_bad_disconnected_inode/expect.1
@@ -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-1.40.5/tests/f_bbfile/expect.1
===================================================================
--- e2fsprogs-1.40.5.orig/tests/f_bbfile/expect.1
+++ e2fsprogs-1.40.5/tests/f_bbfile/expect.1
@@ -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-1.40.5/tests/f_bbfile/expect.2
===================================================================
--- e2fsprogs-1.40.5.orig/tests/f_bbfile/expect.2
+++ e2fsprogs-1.40.5/tests/f_bbfile/expect.2
@@ -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-1.40.5/tests/f_lotsbad/expect.1
===================================================================
--- e2fsprogs-1.40.5.orig/tests/f_lotsbad/expect.1
+++ e2fsprogs-1.40.5/tests/f_lotsbad/expect.1
@@ -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-1.40.5/tests/f_lotsbad/expect.2
===================================================================
--- e2fsprogs-1.40.5.orig/tests/f_lotsbad/expect.2
+++ e2fsprogs-1.40.5/tests/f_lotsbad/expect.2
@@ -1,7 +1,18 @@
 Pass 1: Checking inodes, blocks, and sizes
+Inode 13 is too big.  Truncate? yes
+
+Block #16580876 (37) causes directory to be too big.  CLEARED.
+Inode 13, i_size is 4093916160, should be 12288.  Fix? yes
+
+Inode 13, i_blocks is 32, should be 30.  Fix? yes
+
 Pass 2: Checking directory structure
+Directory inode 13 has an unallocated block #16580876.  Allocate? yes
+
 Pass 3: Checking directory connectivity
 Pass 4: Checking reference counts
 Pass 5: Checking group summary information
-test_filesys: 12/32 files (0.0% non-contiguous), 57/100 blocks
-Exit status is 0
+
+test_filesys: ***** FILE SYSTEM WAS MODIFIED *****
+test_filesys: 13/32 files (15.4% non-contiguous), 70/100 blocks
+Exit status is 1
Index: e2fsprogs-1.40.5/tests/f_messy_inode/expect.1
===================================================================
--- e2fsprogs-1.40.5.orig/tests/f_messy_inode/expect.1
+++ e2fsprogs-1.40.5/tests/f_messy_inode/expect.1
@@ -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-1.40.5/tests/f_messy_inode/expect.2
===================================================================
--- e2fsprogs-1.40.5.orig/tests/f_messy_inode/expect.2
+++ e2fsprogs-1.40.5/tests/f_messy_inode/expect.2
@@ -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

Cheers, Andreas
--
Andreas Dilger
Sr. Staff Engineer, Lustre Group
Sun Microsystems of Canada, Inc.

-
To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ