From: Theodore Ts'o Date: Tue, 21 Aug 2007 01:31:11 +0000 (-0400) Subject: e2fsck: Add support for extents X-Git-Tag: v1.41-WIP-0427~92 X-Git-Url: https://git.whamcloud.com/?a=commitdiff_plain;h=15d482ba6e46b6979d8d9e9b3b28e0942b626c2e;p=tools%2Fe2fsprogs.git e2fsck: Add support for extents Signed-off-by: "Theodore Ts'o" --- diff --git a/e2fsck/message.c b/e2fsck/message.c index b2e3e0f..8a3705e 100644 --- a/e2fsck/message.c +++ b/e2fsck/message.c @@ -80,6 +80,7 @@ * @S superblock * @u unattached * @v device + * @x extent * @z zero-length */ @@ -134,6 +135,7 @@ static const char *abbrevs[] = { N_("Ssuper@b"), N_("uunattached"), N_("vdevice"), + N_("xextent"), N_("zzero-length"), "@@", 0 @@ -388,7 +390,11 @@ static _INLINE_ void expand_percent_expression(ext2_filsys fs, char ch, fputc('%', stdout); break; case 'b': - printf("%u", ctx->blk); +#ifdef EXT2_NO_64_TYPE + printf("%u", (unsigned long) ctx->blk); +#else + printf("%llu", (unsigned long long) ctx->blk); +#endif break; case 'B': #ifdef EXT2_NO_64_TYPE @@ -398,7 +404,11 @@ static _INLINE_ void expand_percent_expression(ext2_filsys fs, char ch, #endif break; case 'c': - printf("%u", ctx->blk2); +#ifdef EXT2_NO_64_TYPE + printf("%u", (unsigned long) ctx->blk2); +#else + printf("%llu", (unsigned long long) ctx->blk2); +#endif break; case 'd': printf("%u", ctx->dir); diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c index e1aa029..03232e5 100644 --- a/e2fsck/pass1.c +++ b/e2fsck/pass1.c @@ -480,7 +480,7 @@ void e2fsck_pass1(e2fsck_t ctx) struct scan_callback_struct scan_struct; struct ext2_super_block *sb = ctx->fs->super; const char *old_op; - int imagic_fs; + int imagic_fs, extent_fs; int busted_fs_time = 0; int inode_size; @@ -514,6 +514,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 @@ -653,6 +654,39 @@ void e2fsck_pass1(e2fsck_t ctx) return; } } + + if ((inode->i_flags & EXT4_EXTENTS_FL) && !extent_fs && + (inode->i_links_count || (ino == EXT2_BAD_INO) || + (ino == EXT2_ROOT_INO) || (ino == EXT2_JOURNAL_INO))) { + if ((ext2fs_extent_header_verify(inode->i_block, + sizeof(inode->i_block)) == 0) && + 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_EXTENTS_SET, &pctx)) { + clear_inode: + e2fsck_clear_inode(ctx, ino, inode, 0, "pass1"); + if (ino == EXT2_BAD_INO) + ext2fs_mark_inode_bitmap(ctx->inode_used_map, + ino); + continue; + } + } + + if (extent_fs && !(inode->i_flags & EXT4_EXTENTS_FL) && + (inode->i_links_count || (ino == EXT2_BAD_INO) || + (ino == EXT2_ROOT_INO) || (ino == EXT2_JOURNAL_INO)) && + (LINUX_S_ISREG(inode->i_mode) || + LINUX_S_ISDIR(inode->i_mode)) && + (ext2fs_extent_header_verify(inode->i_block, + sizeof(inode->i_block)) == 0)) { + if (fix_problem(ctx, PR_1_UNSET_EXTENT_FL, &pctx)) { + inode->i_flags |= EXT4_EXTENTS_FL; + e2fsck_write_inode(ctx, ino, inode, "pass1"); + } + } + if (ino == EXT2_BAD_INO) { struct process_block_struct pb; @@ -695,10 +729,8 @@ void e2fsck_pass1(e2fsck_t ctx) * regnerated in pass #3. */ if (!LINUX_S_ISDIR(inode->i_mode)) { - if (fix_problem(ctx, PR_1_ROOT_NO_DIR, &pctx)) { - e2fsck_clear_inode(ctx, ino, inode, - 0, "pass1"); - } + if (fix_problem(ctx, PR_1_ROOT_NO_DIR, &pctx)) + goto clear_inode; } /* * If dtime is set, offer to clear it. mke2fs @@ -904,10 +936,11 @@ void e2fsck_pass1(e2fsck_t ctx) 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) { + if (!(inode->i_flags & EXT4_EXTENTS_FL) && + (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++; @@ -1407,8 +1440,7 @@ clear_extattr: /* 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; @@ -1422,12 +1454,17 @@ static int handle_htree(e2fsck_t ctx, struct problem_context *pctx, fix_problem(ctx, PR_1_HTREE_SET, pctx))) return 1; - blk = inode->i_block[0]; - if (((blk == 0) || - (blk < fs->super->s_first_data_block) || - (blk >= fs->super->s_blocks_count)) && - fix_problem(ctx, PR_1_HTREE_BADROOT, pctx)) - return 1; + pctx->errcode = ext2fs_bmap(fs, ino, inode, 0, 0, 0, &blk); + + if ((pctx->errcode) || + (blk == 0) || + (blk < fs->super->s_first_data_block) || + (blk >= fs->super->s_blocks_count)) { + if (fix_problem(ctx, PR_1_HTREE_BADROOT, pctx)) + return 1; + else + return 0; + } retval = io_channel_read_blk(fs->io, blk, 1, block_buf); if (retval && fix_problem(ctx, PR_1_HTREE_BADROOT, pctx)) @@ -1463,7 +1500,7 @@ void e2fsck_clear_inode(e2fsck_t ctx, ext2_ino_t ino, struct ext2_inode *inode, int restart_flag, const char *source) { - + inode->i_flags = 0; inode->i_links_count = 0; ext2fs_icount_store(ctx->inode_link_info, ino, 0); inode->i_dtime = ctx->now; @@ -1484,6 +1521,119 @@ void e2fsck_clear_inode(e2fsck_t ctx, ext2_ino_t ino, e2fsck_write_inode(ctx, ino, inode, source); } +static void scan_extent_node(e2fsck_t ctx, struct problem_context *pctx, + struct process_block_struct *pb, + ext2_extent_handle_t ehandle) +{ + struct ext2fs_extent extent; + blk_t blk; + e2_blkcnt_t blockcnt; + int i; + int is_dir, is_leaf; + errcode_t problem; + + + pctx->errcode = ext2fs_extent_get(ehandle, EXT2_EXTENT_FIRST_SIB, + &extent); + while (!pctx->errcode) { + is_leaf = extent.e_flags & EXT2_EXTENT_FLAGS_LEAF; + is_dir = LINUX_S_ISDIR(pctx->inode->i_mode); + + problem = 0; + if (extent.e_pblk < ctx->fs->super->s_first_data_block || + extent.e_pblk >= ctx->fs->super->s_blocks_count) + problem = PR_1_EXTENT_BAD_START_BLK; + else if ((extent.e_pblk + extent.e_len) > + ctx->fs->super->s_blocks_count) + problem = PR_1_EXTENT_ENDS_BEYOND; + + if (problem) { + pctx->blk = extent.e_pblk; + pctx->blk2 = extent.e_lblk; + pctx->num = extent.e_len; + if (fix_problem(ctx, problem, pctx)) { + pctx->errcode = + ext2fs_extent_delete(ehandle, 0); + if (pctx->errcode) { + fix_problem(ctx, + PR_1_EXTENT_DELETE_FAIL, + pctx); + /* Should never get here */ + ctx->flags |= E2F_FLAG_ABORT; + return; + } + } + goto next; + } + + if (!is_leaf) { + mark_block_used(ctx, extent.e_pblk); + pctx->errcode = ext2fs_extent_get(ehandle, + EXT2_EXTENT_DOWN, &extent); + if (pctx->errcode) { + printf("Error1: %s\n", error_message(pctx->errcode)); + abort(); + } + scan_extent_node(ctx, pctx, pb, ehandle); + pctx->errcode = ext2fs_extent_get(ehandle, + EXT2_EXTENT_UP, &extent); + if (pctx->errcode) { + printf("Error1: %s\n", error_message(pctx->errcode)); + abort(); + } + goto next; + } + + for (blk = extent.e_pblk, blockcnt = extent.e_lblk, i = 0; + i < extent.e_len; + blk++, blockcnt++, i++) { + mark_block_used(ctx, blk); + + if (is_dir) { + pctx->errcode = ext2fs_add_dir_block(ctx->fs->dblist, pctx->ino, blk, blockcnt); + if (pctx->errcode) { + pctx->blk = blk; + pctx->num = blockcnt; + fix_problem(ctx, PR_1_ADD_DBLOCK, pctx); + /* Should never get here */ + ctx->flags |= E2F_FLAG_ABORT; + return; + } + } + } + pb->num_blocks += extent.e_len; + pb->last_block = extent.e_lblk + extent.e_len - 1; + next: + pctx->errcode = ext2fs_extent_get(ehandle, + EXT2_EXTENT_NEXT_SIB, + &extent); + } + if (pctx->errcode == EXT2_ET_EXTENT_NO_NEXT) + pctx->errcode = 0; +} + +static void check_blocks_extents(e2fsck_t ctx, struct problem_context *pctx, + struct process_block_struct *pb, + char *block_buf) +{ + struct ext2_inode *inode = pctx->inode; + ext2_extent_handle_t ehandle; + ext2_filsys fs = ctx->fs; + ext2_ino_t ino = pctx->ino; + + pctx->errcode = ext2fs_extent_open(fs, ino, &ehandle); + if (pctx->errcode && + fix_problem(ctx, PR_1_READ_EXTENT, pctx)) { + e2fsck_clear_inode(ctx, ino, inode, 0, "check_blocks_extents"); + pctx->errcode = 0; + return; + } + + scan_extent_node(ctx, pctx, pb, ehandle); + + ext2fs_extent_free(ehandle); +} + /* * This subroutine is called on each inode to account for all of the * blocks used by that inode. @@ -1531,10 +1681,16 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx, if (inode->i_file_acl && check_ext_attr(ctx, pctx, block_buf)) pb.num_blocks++; - if (ext2fs_inode_has_valid_blocks(inode)) - pctx->errcode = ext2fs_block_iterate2(fs, ino, - pb.is_dir ? BLOCK_FLAG_HOLE : 0, - block_buf, process_block, &pb); + if (ext2fs_inode_has_valid_blocks(inode)) { + if ((ctx->fs->super->s_feature_incompat & + EXT3_FEATURE_INCOMPAT_EXTENTS) && + (inode->i_flags & EXT4_EXTENTS_FL)) + check_blocks_extents(ctx, pctx, &pb, block_buf); + 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) diff --git a/e2fsck/pass1b.c b/e2fsck/pass1b.c index 5d062ca..6956d23 100644 --- a/e2fsck/pass1b.c +++ b/e2fsck/pass1b.c @@ -294,7 +294,8 @@ static void pass1b(e2fsck_t ctx, char *block_buf) if (ext2fs_inode_has_valid_blocks(&inode) || (ino == EXT2_BAD_INO)) pctx.errcode = ext2fs_block_iterate2(fs, ino, - 0, block_buf, process_pass1b_block, &pb); + BLOCK_FLAG_READ_ONLY, block_buf, + process_pass1b_block, &pb); if (inode.i_file_acl) process_pass1b_block(fs, &inode.i_file_acl, BLOCK_COUNT_EXTATTR, 0, 0, &pb); @@ -590,8 +591,8 @@ static void delete_file(e2fsck_t ctx, ext2_ino_t ino, e2fsck_read_inode(ctx, ino, &inode, "delete_file"); if (ext2fs_inode_has_valid_blocks(&inode)) - pctx.errcode = ext2fs_block_iterate2(fs, ino, 0, block_buf, - delete_file_block, &pb); + pctx.errcode = ext2fs_block_iterate2(fs, ino, BLOCK_FLAG_READ_ONLY, + block_buf, delete_file_block, &pb); if (pctx.errcode) fix_problem(ctx, PR_1B_BLOCK_ITERATE, &pctx); if (ctx->inode_bad_map) diff --git a/e2fsck/problem.c b/e2fsck/problem.c index 7c3ebea..6a127c6 100644 --- a/e2fsck/problem.c +++ b/e2fsck/problem.c @@ -784,6 +784,41 @@ static struct e2fsck_problem problem_table[] = { N_("@i %i is a %It but it looks like it is really a directory.\n"), PROMPT_FIX, 0 }, + /* Error while reading extent tree */ + { PR_1_READ_EXTENT, + N_("Error while reading over @x tree in @i %i: %m\n"), + PROMPT_CLEAR_INODE, 0 }, + + /* Error deleting a bogus extent */ + { PR_1_EXTENT_DELETE_FAIL, + N_("Error while deleting extent: %m\n"), + PROMPT_ABORT, 0 }, + + /* Bad starting block in extent */ + { PR_1_EXTENT_BAD_START_BLK, + N_("@i %i has an @n extent\n\t(logical @b %c, @n physical @b %b, len %N)\n"), + PROMPT_CLEAR, 0 }, + + /* Extent ends beyond filesystem */ + { PR_1_EXTENT_ENDS_BEYOND, + N_("@i %i has an @n extent\n\t(logical @b %c, physical @b %b, @n len %N)\n"), + PROMPT_CLEAR, 0 }, + + /* EXTENTS_FL flag set on a non-extents filesystem */ + { PR_1_EXTENTS_SET, + N_("@i %i has EXTENTS_FL flag set on @f without extents support.\n"), + PROMPT_CLEAR, 0 }, + + /* 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, 0 }, + + /* 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 }, + /* Pass 1b errors */ /* Pass 1B: Rescan for duplicate/bad blocks */ diff --git a/e2fsck/problem.h b/e2fsck/problem.h index f5f7212..55a197d 100644 --- a/e2fsck/problem.h +++ b/e2fsck/problem.h @@ -16,7 +16,7 @@ struct problem_context { ext2_ino_t ino, ino2, dir; struct ext2_inode *inode; struct ext2_dir_entry *dirent; - blk_t blk, blk2; + blk64_t blk, blk2; e2_blkcnt_t blkcount; int group; __u64 num; @@ -455,6 +455,27 @@ struct problem_context { /* inode appears to be a directory */ #define PR_1_TREAT_AS_DIRECTORY 0x010055 +/* Error while reading extent tree */ +#define PR_1_READ_EXTENT 0x010056 + +/* Error deleting a bogus extent */ +#define PR_1_EXTENT_DELETE_FAIL 0x010057 + +/* Bad starting block in extent */ +#define PR_1_EXTENT_BAD_START_BLK 0x010058 + +/* Extent ends beyond filesystem */ +#define PR_1_EXTENT_ENDS_BEYOND 0x010059 + +/* EXTENTS_FL flag set on a non-extents capable filesystem */ +#define PR_1_EXTENTS_SET 0x01005A + +/* inode has extents, superblock missing INCOMPAT_EXTENTS feature */ +#define PR_1_EXTENT_FEATURE 0x01005B + +/* inode missing EXTENTS_FL, but is an extent inode */ +#define PR_1_UNSET_EXTENT_FL 0x01005C + /* * Pass 1b errors */ diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h index 02d2b42..b8c316c 100644 --- a/lib/ext2fs/ext2fs.h +++ b/lib/ext2fs/ext2fs.h @@ -502,12 +502,14 @@ typedef struct ext2_icount *ext2_icount_t; 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|\ diff --git a/tests/f_bad_disconnected_inode/expect.1 b/tests/f_bad_disconnected_inode/expect.1 index b4851f0..11862f6 100644 --- a/tests/f_bad_disconnected_inode/expect.1 +++ b/tests/f_bad_disconnected_inode/expect.1 @@ -1,4 +1,13 @@ Pass 1: Checking inodes, blocks, and sizes +Inode 1 has EXTENTS_FL flag set on filesystem without extents support. +Clear? yes + +Inode 15 has EXTENTS_FL flag set on filesystem without extents support. +Clear? yes + +Inode 16 has EXTENTS_FL flag set on filesystem without extents support. +Clear? yes + Pass 2: Checking directory structure Pass 3: Checking directory connectivity /lost+found not found. Create? yes @@ -18,26 +27,6 @@ Clear? yes Inode 14 (...) has invalid mode (0154247). Clear? yes -i_file_acl for inode 15 (...) is 1143674715, should be zero. -Clear? yes - -Inode 15 (...) has invalid mode (074044). -Clear? yes - -i_file_acl for inode 16 (...) is 2007517039, should be zero. -Clear? yes - -i_faddr for inode 16 (...) is 1003914917, should be zero. -Clear? yes - -i_blocks_hi for inode 16 (...) is 62762, should be zero. -Clear? yes - -Unattached inode 16 -Connect to /lost+found? yes - -Inode 16 ref count is 5925, should be 1. Fix? yes - Pass 5: Checking group summary information Block bitmap differences: -(9--19) Fix? yes @@ -48,19 +37,16 @@ Fix? yes Free blocks count wrong (79, counted=91). Fix? yes -Inode bitmap differences: +16 -Fix? yes - -Free inodes count wrong for group #0 (7, counted=4). +Free inodes count wrong for group #0 (6, counted=5). Fix? yes Directories count wrong for group #0 (3, counted=2). Fix? yes -Free inodes count wrong (7, counted=4). +Free inodes count wrong (6, counted=5). Fix? yes test_filesys: ***** FILE SYSTEM WAS MODIFIED ***** -test_filesys: 12/16 files (0.0% non-contiguous), 9/100 blocks +test_filesys: 11/16 files (0.0% non-contiguous), 9/100 blocks Exit status is 1 diff --git a/tests/f_bad_disconnected_inode/expect.2 b/tests/f_bad_disconnected_inode/expect.2 index 1739210..8dfeb70 100644 --- a/tests/f_bad_disconnected_inode/expect.2 +++ b/tests/f_bad_disconnected_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: 12/16 files (0.0% non-contiguous), 9/100 blocks +test_filesys: 11/16 files (0.0% non-contiguous), 9/100 blocks Exit status is 0