From: Andreas Dilger Subject: Re: [PATCH][0/28] e2fsprogs-extents.patch Date: Sat, 02 Feb 2008 01:25:59 -0700 Message-ID: <20080202082559.GG31694@webber.adilger.int> References: <20080202075943.GB23836@webber.adilger.int> Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: 7BIT To: "Theodore Ts'o" , linux-ext4@vger.kernel.org Return-path: Received: from sca-es-mail-2.Sun.COM ([192.18.43.133]:58318 "EHLO sca-es-mail-2.sun.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1757016AbYBBI0I (ORCPT ); Sat, 2 Feb 2008 03:26:08 -0500 Received: from fe-sfbay-09.sun.com ([192.18.43.129]) by sca-es-mail-2.sun.com (8.13.7+Sun/8.12.9) with ESMTP id m128Q5fl016512 for ; Sat, 2 Feb 2008 00:26:05 -0800 (PST) Received: from conversion-daemon.fe-sfbay-09.sun.com by fe-sfbay-09.sun.com (Sun Java System Messaging Server 6.2-8.04 (built Feb 28 2007)) id <0JVL00001R3JH300@fe-sfbay-09.sun.com> (original mail from adilger@sun.com) for linux-ext4@vger.kernel.org; Sat, 02 Feb 2008 00:26:05 -0800 (PST) In-reply-to: <20080202075943.GB23836@webber.adilger.int> Content-disposition: inline Sender: linux-ext4-owner@vger.kernel.org List-ID: 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 +#include #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 + * Copyright (C) 2006 Andreas Dilger + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#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): (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): - (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): (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.