From 7dce0c06e56d0f661126bd1ee1e5d70c174c2500 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Sat, 15 Mar 2014 11:36:34 -0400 Subject: [PATCH 1/9] libext2fs: fix parents when modifying extents In ext2fs_extent_set_bmap() and ext2fs_punch_extent(), fix the parents when altering either end of an extent so that the parent nodes reflect the added mapping. There's a slight complication to using fix_parents: if there are two mappings to an lblk in the tree, the value of handle->path->curr can point to either extent afterwards), which is documented in a comment. Some additional color commentary from Darrick: In the _set_bmap() case, I noticed that the "remapping last block in extent" case would produce symptoms if we are trying to remap a block from "extent" to "next_extent", and the two extents are pointed to by different index nodes. _extent_replace(..., next_extent) updates e_lblk in the leaf extent, but because there's no _extent_fix_parents() call, the index nodes never get updated. In the _punch_extent() case, we conclude that we need to split an extent into two pieces since we're punching out the middle. If the extent is the last extent in the block, the second extent will be inserted into a new leaf node block. Without _fix_parents(), the index node doesn't seem to get updated. Signed-off-by: Darrick J. Wong Signed-off-by: "Theodore Ts'o" --- lib/ext2fs/extent.c | 30 ++++++++++++++++++++++++------ lib/ext2fs/punch.c | 14 ++++++++++---- 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/lib/ext2fs/extent.c b/lib/ext2fs/extent.c index c12bc930..d3d54451 100644 --- a/lib/ext2fs/extent.c +++ b/lib/ext2fs/extent.c @@ -734,7 +734,14 @@ errcode_t ext2fs_extent_goto(ext2_extent_handle_t handle, * and so on. * * Safe to call for any position in node; if not at the first entry, - * will simply return. + * it will simply return. + * + * Note a subtlety of this function -- if there happen to be two extents + * mapping the same lblk and someone calls fix_parents on the second of the two + * extents, the position of the extent handle after the call will be the second + * extent if nothing happened, or the first extent if something did. A caller + * in this situation must use ext2fs_extent_goto() after calling this function. + * Or simply don't map the same lblk with two extents, ever. */ errcode_t ext2fs_extent_fix_parents(ext2_extent_handle_t handle) { @@ -1424,17 +1431,25 @@ errcode_t ext2fs_extent_set_bmap(ext2_extent_handle_t handle, &next_extent); if (retval) goto done; - retval = ext2fs_extent_fix_parents(handle); - if (retval) - goto done; } else retval = ext2fs_extent_insert(handle, EXT2_EXTENT_INSERT_AFTER, &newextent); if (retval) goto done; - /* Now pointing at inserted extent; move back to prev */ + retval = ext2fs_extent_fix_parents(handle); + if (retval) + goto done; + /* + * Now pointing at inserted extent; move back to prev. + * + * We cannot use EXT2_EXTENT_PREV to go back; note the + * subtlety in the comment for fix_parents(). + */ + retval = ext2fs_extent_goto(handle, logical); + if (retval) + goto done; retval = ext2fs_extent_get(handle, - EXT2_EXTENT_PREV_LEAF, + EXT2_EXTENT_CURRENT, &extent); if (retval) goto done; @@ -1465,6 +1480,9 @@ errcode_t ext2fs_extent_set_bmap(ext2_extent_handle_t handle, } else retval = ext2fs_extent_insert(handle, 0, &newextent); + if (retval) + goto done; + retval = ext2fs_extent_fix_parents(handle); if (retval) goto done; retval = ext2fs_extent_get(handle, diff --git a/lib/ext2fs/punch.c b/lib/ext2fs/punch.c index a3d020ec..2a2cf102 100644 --- a/lib/ext2fs/punch.c +++ b/lib/ext2fs/punch.c @@ -343,10 +343,16 @@ static errcode_t ext2fs_punch_extent(ext2_filsys fs, ext2_ino_t ino, EXT2_EXTENT_INSERT_AFTER, &newex); if (retval) goto errout; - /* Now pointing at inserted extent; so go back */ - retval = ext2fs_extent_get(handle, - EXT2_EXTENT_PREV_LEAF, - &newex); + retval = ext2fs_extent_fix_parents(handle); + if (retval) + goto errout; + /* + * Now pointing at inserted extent; so go back. + * + * We cannot use EXT2_EXTENT_PREV to go back; note the + * subtlety in the comment for fix_parents(). + */ + retval = ext2fs_extent_goto(handle, extent.e_lblk); if (retval) goto errout; } From db3d8718be6ad3bdd252b242827fa54914b8ec2e Mon Sep 17 00:00:00 2001 From: Andreas Dilger Date: Fri, 13 Nov 2015 18:10:27 -0700 Subject: [PATCH 2/9] e2fsck: skip quota update when interrupted There is a bug in how e2fsck handles being interrupted by CTRL-C. If CTRL-C is pressed to kill e2fsck rather than e.g. kill -9, then the interrupt handler sets E2F_FLAG_CANCEL in the context but doesn't actually kill the process. Instead, e2fsck_pass1() checks this flag before processing the next inode. If a filesystem is running in fix mode (e2fsck -fy) is interrupted, and the quota feature is enabled, then the quota file will still be written to disk even though the inode scan was not complete and the quota information is totally inaccurate. Even worse, if the Pass 1 inode and block scan was not finished, then the in-memory block bitmaps (which are used for block allocation during e2fsck) are also invalid, so any blocks allocated to the quota files may corrupt other files if those blocks were actually used. e2fsck 1.42.13.wc3 (28-Aug-2015) Pass 1: Checking inodes, blocks, and sizes ^C[QUOTA WARNING] Usage inconsistent for ID 0: actual (6455296, 168) != expected (8568832, 231) [QUOTA WARNING] Usage inconsistent for ID 695: actual (614932320256, 63981) != expected (2102405386240, 176432) Update quota info for quota type 0? yes [QUOTA WARNING] Usage inconsistent for ID 0: actual (6455296, 168) != expected (8568832, 231) [QUOTA WARNING] Usage inconsistent for ID 538: actual (614932320256, 63981) != expected (2102405386240, 176432) Update quota info for quota type 1? yes myth-OST0001: e2fsck canceled. myth-OST0001: ***** FILE SYSTEM WAS MODIFIED ***** There may be a desire to flush out modified inodes and such that have been repaired, so that restarting an interrupted e2fsck will make progress, but the quota file update is plain wrong unless at least pass1 has finished, and the journal recreation is also dangerous if the block bitmaps have not been fully updated. Signed-off-by: Andreas Dilger Signed-off-by: Theodore Ts'o --- e2fsck/e2fsck.c | 2 -- e2fsck/e2fsck.h | 4 ++-- e2fsck/pass1.c | 4 +++- e2fsck/pass2.c | 10 +++++----- e2fsck/unix.c | 18 ++++++++++-------- 5 files changed, 20 insertions(+), 18 deletions(-) diff --git a/e2fsck/e2fsck.c b/e2fsck/e2fsck.c index 0ec15404..2002dc00 100644 --- a/e2fsck/e2fsck.c +++ b/e2fsck/e2fsck.c @@ -203,8 +203,6 @@ static pass_t e2fsck_passes[] = { e2fsck_pass1, e2fsck_pass2, e2fsck_pass3, e2fsck_pass4, e2fsck_pass5, 0 }; -#define E2F_FLAG_RUN_RETURN (E2F_FLAG_SIGNAL_MASK|E2F_FLAG_RESTART) - int e2fsck_run(e2fsck_t ctx) { int i; diff --git a/e2fsck/e2fsck.h b/e2fsck/e2fsck.h index f904026f..810030e2 100644 --- a/e2fsck/e2fsck.h +++ b/e2fsck/e2fsck.h @@ -173,10 +173,10 @@ struct resource_track { */ #define E2F_FLAG_ABORT 0x0001 /* Abort signaled */ #define E2F_FLAG_CANCEL 0x0002 /* Cancel signaled */ -#define E2F_FLAG_SIGNAL_MASK 0x0003 +#define E2F_FLAG_SIGNAL_MASK (E2F_FLAG_ABORT | E2F_FLAG_CANCEL) #define E2F_FLAG_RESTART 0x0004 /* Restart signaled */ +#define E2F_FLAG_RUN_RETURN (E2F_FLAG_SIGNAL_MASK | E2F_FLAG_RESTART) #define E2F_FLAG_RESTART_LATER 0x0008 /* Restart after all iterations done */ - #define E2F_FLAG_SETJMP_OK 0x0010 /* Setjmp valid for abort */ #define E2F_FLAG_PROG_BAR 0x0020 /* Progress bar on screen */ diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c index 3bf481f3..ac3ea4eb 100644 --- a/e2fsck/pass1.c +++ b/e2fsck/pass1.c @@ -766,7 +766,7 @@ void e2fsck_pass1(e2fsck_t ctx) inode, inode_size); ehandler_operation(old_op); if (ctx->flags & E2F_FLAG_SIGNAL_MASK) - return; + goto endit; if (pctx.errcode == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE) { if (!ctx->inode_bb_map) alloc_bb_map(ctx); @@ -1277,6 +1277,8 @@ endit: if ((ctx->flags & E2F_FLAG_SIGNAL_MASK) == 0) print_resource_track(ctx, _("Pass 1"), &rtrack, ctx->fs->io); + else + ctx->invalid_bitmaps++; } /* diff --git a/e2fsck/pass2.c b/e2fsck/pass2.c index 4acddaee..b8f7e33a 100644 --- a/e2fsck/pass2.c +++ b/e2fsck/pass2.c @@ -148,14 +148,14 @@ void e2fsck_pass2(e2fsck_t ctx) cd.pctx.errcode = ext2fs_dblist_iterate2(fs->dblist, check_dir_block, &cd); - if (ctx->flags & E2F_FLAG_SIGNAL_MASK || ctx->flags & E2F_FLAG_RESTART) - return; - if (ctx->flags & E2F_FLAG_RESTART_LATER) { ctx->flags |= E2F_FLAG_RESTART; - return; + ctx->flags &= ~E2F_FLAG_RESTART_LATER; } + if (ctx->flags & E2F_FLAG_RUN_RETURN) + return; + if (cd.pctx.errcode) { fix_problem(ctx, PR_2_DBLIST_ITERATE, &cd.pctx); ctx->flags |= E2F_FLAG_ABORT; @@ -739,7 +739,7 @@ static int check_dir_block(ext2_filsys fs, buf = cd->buf; ctx = cd->ctx; - if (ctx->flags & E2F_FLAG_SIGNAL_MASK || ctx->flags & E2F_FLAG_RESTART) + if (ctx->flags & E2F_FLAG_RUN_RETURN) return DIRENT_ABORT; if (ctx->progress && (ctx->progress)(ctx, 2, cd->count++, cd->max)) diff --git a/e2fsck/unix.c b/e2fsck/unix.c index 10036e76..9d9ba04f 100644 --- a/e2fsck/unix.c +++ b/e2fsck/unix.c @@ -1667,8 +1667,15 @@ print_unsupp_features: } no_journal: - if (ctx->qctx) { + if (run_result & E2F_FLAG_ABORT) { + fatal_error(ctx, _("aborted")); + } else if (run_result & E2F_FLAG_CANCEL) { + log_out(ctx, _("%s: e2fsck canceled.\n"), ctx->device_name ? + ctx->device_name : ctx->filesystem_name); + exit_value |= FSCK_CANCELED; + } else if (ctx->qctx && !ctx->invalid_bitmaps) { int i, needs_writeout; + for (i = 0; i < MAXQUOTAS; i++) { if (qtype != -1 && qtype != i) continue; @@ -1695,18 +1702,13 @@ no_journal: ext2fs_close_free(&ctx->fs); goto restart; } - if (run_result & E2F_FLAG_ABORT) - fatal_error(ctx, _("aborted")); #ifdef MTRACE mtrace_print("Cleanup"); #endif was_changed = ext2fs_test_changed(fs); - if (run_result & E2F_FLAG_CANCEL) { - log_out(ctx, _("%s: e2fsck canceled.\n"), ctx->device_name ? - ctx->device_name : ctx->filesystem_name); - exit_value |= FSCK_CANCELED; - } else if (!(ctx->options & E2F_OPT_READONLY)) { + if (!(ctx->flags & E2F_FLAG_RUN_RETURN) && + !(ctx->options & E2F_OPT_READONLY)) { if (ext2fs_test_valid(fs)) { if (!(sb->s_state & EXT2_VALID_FS)) exit_value |= FSCK_NONDESTRUCT; From 5cb290e233759f4eba7567f76dbc2f30506725f3 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Mon, 30 Nov 2015 11:56:55 -0500 Subject: [PATCH 3/9] chattr: fix typo in a manpage Signed-off-by: Jan Kara Signed-off-by: Theodore Ts'o --- misc/chattr.1.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/chattr.1.in b/misc/chattr.1.in index 75b3ed8c..65e09b34 100644 --- a/misc/chattr.1.in +++ b/misc/chattr.1.in @@ -93,7 +93,7 @@ set on new or empty files. If it is set on a file which already has data blocks, it is undefined when the blocks assigned to the file will be fully stable. If the 'C' flag is set on a directory, it will have no effect on the directory, but new files created in that directory will -the No_COW attribute.) +have the No_COW attribute set.) .PP A file with the 'd' attribute set is not candidate for backup when the .BR dump (8) From 2b833c9aae3323745af3fdc998d8b3255f40511e Mon Sep 17 00:00:00 2001 From: Artemiy Volkov Date: Mon, 30 Nov 2015 12:03:52 -0500 Subject: [PATCH 4/9] e2fsck: do not read EA header beyond the end of an inode In check_inode_extra_space(), if we attempt to read an EA header at the end of the extra space, in a corrupted filesystem it may result in a read beyond the bounds of the inode. Add a check to prevent this. Reproduced by running ./test_one --valgrind f_write_ea_toobig_extra_isize. Signed-off-by: Artemiy Volkov Signed-off-by: Theodore Ts'o Acked-by: Darrick J. Wong --- e2fsck/pass1.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c index ac3ea4eb..66ff69a3 100644 --- a/e2fsck/pass1.c +++ b/e2fsck/pass1.c @@ -382,6 +382,10 @@ static void check_inode_extra_space(e2fsck_t ctx, struct problem_context *pctx) return; } + /* check if there is no place for an EA header */ + if (inode->i_extra_isize >= max - sizeof(__u32)) + return; + eamagic = (__u32 *) (((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE + inode->i_extra_isize); if (*eamagic == EXT2_EXT_ATTR_MAGIC) { From f449486d631987983b4275d246b7bbbb551f3235 Mon Sep 17 00:00:00 2001 From: Andreas Dilger Date: Mon, 30 Nov 2015 12:09:44 -0500 Subject: [PATCH 5/9] libext2fs: fix tst_badblocks buffer overrun The test2[] array is not 0-terminated and the create_test_list() for loop does not terminate properly at the end of this array, but continues until it hits the 0 at the end of test3[]. Reported-by: Hanno Boeck Addresses: https://bugzilla.kernel.org/show_bug.cgi?id=104311 Signed-off-by: Andreas Dilger Signed-off-by: Theodore Ts'o --- lib/ext2fs/tst_badblocks.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/ext2fs/tst_badblocks.c b/lib/ext2fs/tst_badblocks.c index 3b39ef13..c685f33c 100644 --- a/lib/ext2fs/tst_badblocks.c +++ b/lib/ext2fs/tst_badblocks.c @@ -30,11 +30,11 @@ #define DEL_BLK 0x0002 blk_t test1[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0 }; -blk_t test2[] = { 11, 10, 9, 8, 7, 6, 5, 4, 3, 3, 2, 1 }; +blk_t test2[] = { 11, 10, 9, 8, 7, 6, 5, 4, 3, 3, 2, 1, 0 }; blk_t test3[] = { 3, 1, 4, 5, 9, 2, 7, 10, 5, 6, 10, 8, 0 }; blk_t test4[] = { 20, 50, 12, 17, 13, 2, 66, 23, 56, 0 }; blk_t test4a[] = { - 20, 1, + 20, 1, 50, 1, 3, 0, 17, 1, From c5b3ae7fb5d58dd12a1e02c2443bad32c8a76150 Mon Sep 17 00:00:00 2001 From: Jim Garlick Date: Mon, 30 Nov 2015 12:13:39 -0500 Subject: [PATCH 6/9] tune2fs: warn if the filesystem journal is dirty Running tune2fs on a filesystem with an unrecovered journal can cause the tune2fs settings changes in the superblock to be reverted when the journal is replayed if it contains an uncommitted copy of the superblock. Print a warning if this is detected so that the user isn't surprised if it happens. Signed-off-by: Jim Garlick Updated message printed to include steps to replay journal. Signed-off-by: Andreas Dilger Signed-off-by: Theodore Ts'o --- misc/tune2fs.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/misc/tune2fs.c b/misc/tune2fs.c index cd1d17f5..aed0a35a 100644 --- a/misc/tune2fs.c +++ b/misc/tune2fs.c @@ -2406,6 +2406,18 @@ retry_open: ext_mount_opts); free(ext_mount_opts); } + + /* Warn if file system needs recovery and it is opened for writing. */ + if ((open_flag & EXT2_FLAG_RW) && !(mount_flags & EXT2_MF_MOUNTED) && + (sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL) && + (sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER)) { + fprintf(stderr, +_("Warning: The journal is dirty. You may wish to replay the journal like:\n\n" + "\te2fsck -E journal_only %s\n\n" + "then rerun this command. Otherwise, any changes made may be overwritten\n" + "by journal recovery.\n"), device_name); + } + free(device_name); remove_error_table(&et_ext2_error_table); From f680db654b2f095712f2528dc178eea5cfd6401f Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 30 Nov 2015 15:22:07 -0500 Subject: [PATCH 7/9] e2fsck: zap extent-format inode with no extent header The kernel requires all inodes with the extent flag set to have a valid extent tree header in i_block. The ext2fs_extent_open2 prefers to initialize the header if i_block is zeroed, but e2fsck never writes the new header to disk. Since the kernel won't create inodes with the flag and no header anyway, zap such files. Reported-by: Bo Branten Signed-off-by: Darrick J. Wong Signed-off-by: Theodore Ts'o --- e2fsck/pass1.c | 13 ++++++++++ e2fsck/problem.c | 5 ++++ e2fsck/problem.h | 39 +++++++++++++++++++++++++++++ tests/f_extents/expect.1 | 3 +-- tests/f_zeroed_ext_header/expect.1 | 22 ++++++++++++++++ tests/f_zeroed_ext_header/expect.2 | 7 ++++++ tests/f_zeroed_ext_header/image.gz | Bin 0 -> 1447 bytes tests/f_zeroed_ext_header/name | 1 + 8 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 tests/f_zeroed_ext_header/expect.1 create mode 100644 tests/f_zeroed_ext_header/expect.2 create mode 100644 tests/f_zeroed_ext_header/image.gz create mode 100644 tests/f_zeroed_ext_header/name diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c index 66ff69a3..2370bb0e 100644 --- a/e2fsck/pass1.c +++ b/e2fsck/pass1.c @@ -2138,7 +2138,20 @@ static void check_blocks_extents(e2fsck_t ctx, struct problem_context *pctx, ext2_ino_t ino = pctx->ino; errcode_t retval; blk64_t eof_lblk; + struct ext3_extent_header *eh; + /* Check for a proper extent header... */ + eh = (struct ext3_extent_header *) &inode->i_block[0]; + retval = ext2fs_extent_header_verify(eh, sizeof(inode->i_block)); + if (retval) { + if (fix_problem(ctx, PR_1_MISSING_EXTENT_HEADER, pctx)) + e2fsck_clear_inode(ctx, ino, inode, 0, + "check_blocks_extents"); + pctx->errcode = 0; + return; + } + + /* ...since this function doesn't fail if i_block is zeroed. */ pctx->errcode = ext2fs_extent_open2(fs, ino, inode, &ehandle); if (pctx->errcode) { if (fix_problem(ctx, PR_1_READ_EXTENT, pctx)) diff --git a/e2fsck/problem.c b/e2fsck/problem.c index f442a330..e7831481 100644 --- a/e2fsck/problem.c +++ b/e2fsck/problem.c @@ -987,6 +987,11 @@ static struct e2fsck_problem problem_table[] = { N_("@i %i logical @b %b (physical @b %c) violates cluster allocation rules.\nWill fix in pass 1B.\n"), PROMPT_NONE, 0 }, + /* Inode has corrupt extent header */ + { PR_1_MISSING_EXTENT_HEADER, + N_("@i %i has corrupt @x header. "), + PROMPT_CLEAR_INODE, 0 }, + /* Pass 1b errors */ /* Pass 1B: Rescan for duplicate/bad blocks */ diff --git a/e2fsck/problem.h b/e2fsck/problem.h index 212ed350..85014f9c 100644 --- a/e2fsck/problem.h +++ b/e2fsck/problem.h @@ -587,6 +587,45 @@ struct problem_context { /* Inode logical block is misaligned */ #define PR_1_MISALIGNED_CLUSTER 0x010074 +/* Inode has INLINE_DATA_FL flag but extended attribute not found */ +#define PR_1_INLINE_DATA_NO_ATTR 0x010075 + +/* extents/inlinedata set on fifo/socket/device */ +#define PR_1_SPECIAL_EXTENTS_IDATA 0x010076 + +/* idata/extent flag set and extent header found, clear idata flag */ +#define PR_1_CLEAR_INLINE_DATA_FOR_EXTENT 0x010077 + +/* inlinedata/extent set and no extent header found, clear extent flag */ +#define PR_1_CLEAR_EXTENT_FOR_INLINE_DATA 0x010078 + +/* inlinedata/extent set, clear both flags */ +#define PR_1_CLEAR_EXTENT_INLINE_DATA_FLAGS 0x010079 + +/* inlinedata/extent set, clear inode */ +#define PR_1_CLEAR_EXTENT_INLINE_DATA_INODE 0x01007A + +/* badblocks is in badblocks */ +#define PR_1_BADBLOCKS_IN_BADBLOCKS 0x01007B + +/* can't allocate extent region */ +#define PR_1_EXTENT_ALLOC_REGION_ABORT 0x01007C + +/* leaf extent collision */ +#define PR_1_EXTENT_COLLISION 0x01007D + +/* Error allocating memory for encrypted directory list */ +#define PR_1_ALLOCATE_ENCRYPTED_DIRLIST 0x01007E + +/* extent tree max depth too big */ +#define PR_1_EXTENT_BAD_MAX_DEPTH 0x01007F + +/* bigalloc fs cannot have blockmap files */ +#define PR_1_NO_BIGALLOC_BLOCKMAP_FILES 0x010080 + +/* Missing extent header */ +#define PR_1_MISSING_EXTENT_HEADER 0x010081 + /* * Pass 1b errors */ diff --git a/tests/f_extents/expect.1 b/tests/f_extents/expect.1 index 2abe32e3..882c321b 100644 --- a/tests/f_extents/expect.1 +++ b/tests/f_extents/expect.1 @@ -17,8 +17,7 @@ Clear? yes Inode 17, i_blocks is 32, should be 0. Fix? yes -Error while reading over extent tree in inode 18: Corrupt extent header -Clear inode? yes +Inode 18 has corrupt extent header. Clear inode? yes Inode 18, i_blocks is 2, should be 0. Fix? yes diff --git a/tests/f_zeroed_ext_header/expect.1 b/tests/f_zeroed_ext_header/expect.1 new file mode 100644 index 00000000..2613e9ff --- /dev/null +++ b/tests/f_zeroed_ext_header/expect.1 @@ -0,0 +1,22 @@ +Pass 1: Checking inodes, blocks, and sizes +Inode 12 has corrupt extent header. Clear inode? yes + +Pass 2: Checking directory structure +Entry 'testa' in / (2) has deleted/unused inode 12. Clear? yes + +Pass 3: Checking directory connectivity +Pass 4: Checking reference counts +Pass 5: Checking group summary information +Inode bitmap differences: -12 +Fix? yes + +Free inodes count wrong for group #0 (115, counted=116). +Fix? yes + +Free inodes count wrong (115, counted=116). +Fix? yes + + +test_filesys: ***** FILE SYSTEM WAS MODIFIED ***** +test_filesys: 12/128 files (0.0% non-contiguous), 15/256 blocks +Exit status is 1 diff --git a/tests/f_zeroed_ext_header/expect.2 b/tests/f_zeroed_ext_header/expect.2 new file mode 100644 index 00000000..177288fb --- /dev/null +++ b/tests/f_zeroed_ext_header/expect.2 @@ -0,0 +1,7 @@ +Pass 1: Checking inodes, blocks, and sizes +Pass 2: Checking directory structure +Pass 3: Checking directory connectivity +Pass 4: Checking reference counts +Pass 5: Checking group summary information +test_filesys: 12/128 files (0.0% non-contiguous), 15/256 blocks +Exit status is 0 diff --git a/tests/f_zeroed_ext_header/image.gz b/tests/f_zeroed_ext_header/image.gz new file mode 100644 index 0000000000000000000000000000000000000000..67a43341680f578cddb53f54ff7702887dbff384 GIT binary patch literal 1447 zcmb2|=3o$(j|yXAetXw2TR2dL;X`?N>kKz(Z^1b^>YCHe^o6qAm0?+$7xp+r+H2hf zM}xmSuWtD6Znm-}_HRKy-ZmaoZi!TNvhq z%-!SRwBqR5?GkT4{oT@2*X(yw&+5mHGIQR&_vWtmtc&;|^Sb%G*QvKTl{c8B|G!S}ojuij{=4(_6}z?;$vm%m_;mN@?bq`1_e9)yWfLD&IlumX)Xw}LHpet4 zCNVJhOxUu=-u~+T|M}wI|1dBx90+-7`}=SmkoDky`NO6@!K3qL21sUwe_X-#-yz*x?Dy*SC-tAMTb7qC$zQNVyUkCW-EF({tw@Mber7qz1@bd?%w}dE}|TNum0ZUBFk@j~R19 literal 0 HcmV?d00001 diff --git a/tests/f_zeroed_ext_header/name b/tests/f_zeroed_ext_header/name new file mode 100644 index 00000000..31394cf5 --- /dev/null +++ b/tests/f_zeroed_ext_header/name @@ -0,0 +1 @@ +zap inode with zeroed extent header From e158db537754ae1ffb3ec6184051bd1a4226937a Mon Sep 17 00:00:00 2001 From: Andreas Dilger Date: Mon, 30 Nov 2015 15:26:21 -0500 Subject: [PATCH 8/9] libext2fs: fix block-mapped file punch If ext2fs_punch() was called with "end = ~0ULL" to indicate truncate to the end of file it tried to compute "count" for ext2fs_punch_ind() based on "start" and "end", but incorrectly passed "count = ~0U" even when "start" was non-zero, causing an overflow in some cases. The calling convention for ext2fs_punch_ind() was also gratuitously different from ext2fs_punch() and ext2fs_punch_extent(), passing "count" instead of "end" as the last parameter. Fix this by passing it "end" like the other functions, and handle "count" internally. Add checks to ext2fs_punch_ind() if "end" is at or beyond the 2^32 indirect block limit so the 32-bit internal variables don't overflow. Signed-off-by: Andreas Dilger Signed-off-by: Theodore Ts'o --- lib/ext2fs/punch.c | 48 ++++++++++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/lib/ext2fs/punch.c b/lib/ext2fs/punch.c index 2a2cf102..62ddd50a 100644 --- a/lib/ext2fs/punch.c +++ b/lib/ext2fs/punch.c @@ -47,7 +47,7 @@ static int check_zero_block(char *buf, int blocksize) */ static errcode_t ind_punch(ext2_filsys fs, struct ext2_inode *inode, char *block_buf, blk_t *p, int level, - blk_t start, blk_t count, int max) + blk64_t start, blk64_t count, int max) { errcode_t retval; blk_t b; @@ -56,11 +56,11 @@ static errcode_t ind_punch(ext2_filsys fs, struct ext2_inode *inode, int freed = 0; #ifdef PUNCH_DEBUG - printf("Entering ind_punch, level %d, start %u, count %u, " + printf("Entering ind_punch, level %d, start %llu, count %llu, " "max %d\n", level, start, count, max); #endif - incr = 1ULL << ((EXT2_BLOCK_SIZE_BITS(fs->super)-2)*level); - for (i=0, offset=0; i < max; i++, p++, offset += incr) { + incr = 1ULL << ((EXT2_BLOCK_SIZE_BITS(fs->super) - 2) * level); + for (i = 0, offset = 0; i < max; i++, p++, offset += incr) { if (offset >= start + count) break; if (*p == 0 || (offset+incr) <= start) @@ -100,8 +100,9 @@ static errcode_t ind_punch(ext2_filsys fs, struct ext2_inode *inode, return ext2fs_iblk_sub_blocks(fs, inode, freed); } +#define BLK_T_MAX ((blk_t)~0ULL) static errcode_t ext2fs_punch_ind(ext2_filsys fs, struct ext2_inode *inode, - char *block_buf, blk_t start, blk_t count) + char *block_buf, blk64_t start, blk64_t end) { errcode_t retval; char *buf = 0; @@ -110,6 +111,15 @@ static errcode_t ext2fs_punch_ind(ext2_filsys fs, struct ext2_inode *inode, blk_t *bp = inode->i_block; blk_t addr_per_block; blk64_t max = EXT2_NDIR_BLOCKS; + blk_t count; + + /* Check start/end don't overflow the 2^32-1 indirect block limit */ + if (start > BLK_T_MAX) + return 0; + if (end >= BLK_T_MAX || end - start + 1 >= BLK_T_MAX) + count = BLK_T_MAX - start; + else + count = end - start + 1; if (!block_buf) { retval = ext2fs_get_array(3, fs->blocksize, &buf); @@ -118,11 +128,11 @@ static errcode_t ext2fs_punch_ind(ext2_filsys fs, struct ext2_inode *inode, block_buf = buf; } - addr_per_block = (blk_t) fs->blocksize >> 2; + addr_per_block = (blk_t)fs->blocksize >> 2; for (level = 0; level < 4; level++, max *= (blk64_t)addr_per_block) { #ifdef PUNCH_DEBUG - printf("Main loop level %d, start %u count %u " + printf("Main loop level %d, start %llu count %u " "max %llu num %d\n", level, start, count, max, num); #endif if (start < max) { @@ -149,6 +159,7 @@ errout: ext2fs_free_mem(&buf); return retval; } +#undef BLK_T_MAX #ifdef PUNCH_DEBUG @@ -428,10 +439,10 @@ errout: ext2fs_extent_free(handle); return retval; } - + /* - * Deallocate all logical blocks starting at start to end, inclusive. - * If end is ~0, then this is effectively truncate. + * Deallocate all logical _blocks_ starting at start to end, inclusive. + * If end is ~0ULL, then this is effectively truncate. */ errcode_t ext2fs_punch(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode, @@ -453,19 +464,14 @@ errcode_t ext2fs_punch(ext2_filsys fs, ext2_ino_t ino, } if (inode->i_flags & EXT4_EXTENTS_FL) retval = ext2fs_punch_extent(fs, ino, inode, start, end); - else { - blk_t count; - - if (start > ~0U) - return 0; - if (end > ~0U) - end = ~0U; - count = ((end - start + 1) < ~0U) ? (end - start + 1) : ~0U; - retval = ext2fs_punch_ind(fs, inode, block_buf, - (blk_t) start, count); - } + else + retval = ext2fs_punch_ind(fs, inode, block_buf, start, end); if (retval) return retval; +#ifdef PUNCH_DEBUG + printf("%u: write inode size now %u blocks %u\n", + ino, inode->i_size, inode->i_blocks); +#endif return ext2fs_write_inode(fs, ino, inode); } From 19961cd0003564c63c33ec14e69dfec6d81a2238 Mon Sep 17 00:00:00 2001 From: Andreas Dilger Date: Mon, 30 Nov 2015 15:26:35 -0500 Subject: [PATCH 9/9] e2fsck: fix e2fsck -fD directory truncation When an extent-mapped directory is compacted by "e2fsck -fD" and frees enough leaf blocks that it loses an extent tree index block, the old e2fsck_rehash_dir->ext2fs_block_iterate3->write_dir_block() code would not free the extent block, which would result in the extent tree becoming corrupted when it is written out. Pass 1: Checking inodes, blocks, and sizes Inode 17825800, end of extent exceeds allowed value (logical block 710, physical block 570459684, len 1019) This results in loss of a whole index block of directory leaf blocks and maybe thousands or millions of files in lost+found. Fix e2fsck_rehash_dir() to call ext2fs_punch() to free the blocks at the end of the directory instead of trying to handle this itself while writing out the directory. That properly handles all of the cases of updating the extent tree as well as accounting for blocks that are released (both leaf blocks and index blocks). Add a test case for compacting the directory to be smaller than the index block that originally caused the corruption. Signed-off-by: Andreas Dilger Signed-off-by: Theodore Ts'o --- e2fsck/rehash.c | 74 ++++++++++++++++++---------------- tests/f_extent_htree/expect.1 | 29 +++++++++++++ tests/f_extent_htree/expect.2 | 7 ++++ tests/f_extent_htree/image.gz | Bin 0 -> 10101 bytes tests/f_extent_htree/name | 1 + tests/f_extent_htree/script | 69 +++++++++++++++++++++++++++++++ tests/f_h_badnode/expect.1 | 2 +- tests/f_h_badnode/expect.2 | 2 +- 8 files changed, 148 insertions(+), 36 deletions(-) create mode 100644 tests/f_extent_htree/expect.1 create mode 100644 tests/f_extent_htree/expect.2 create mode 100644 tests/f_extent_htree/image.gz create mode 100644 tests/f_extent_htree/name create mode 100644 tests/f_extent_htree/script diff --git a/e2fsck/rehash.c b/e2fsck/rehash.c index 8ff48838..52d99a3c 100644 --- a/e2fsck/rehash.c +++ b/e2fsck/rehash.c @@ -52,10 +52,13 @@ #include "e2fsck.h" #include "problem.h" +#undef REHASH_DEBUG + struct fill_dir_struct { char *buf; struct ext2_inode *inode; errcode_t err; + ext2_ino_t ino; e2fsck_t ctx; struct hash_entry *harray; int max_array, num_array; @@ -625,8 +628,8 @@ static errcode_t calculate_tree(ext2_filsys fs, struct write_dir_struct { struct out_dir *outdir; errcode_t err; + ext2_ino_t ino; e2fsck_t ctx; - blk64_t cleared; }; /* @@ -643,28 +646,35 @@ static int write_dir_block(ext2_filsys fs, blk64_t blk; char *dir; - if (*block_nr == 0) +#ifdef REHASH_DEBUG + printf("%u: write_dir_block %lld:%lld", wd->ino, blockcnt, *block_nr); +#endif + if (*block_nr == 0) { +#ifdef REHASH_DEBUG + printf(" - skip\n"); +#endif return 0; - if (blockcnt >= wd->outdir->num) { - e2fsck_read_bitmaps(wd->ctx); - blk = *block_nr; - /* - * In theory, we only release blocks from the end of the - * directory file, so it's fine to clobber a whole cluster at - * once. - */ - if (blk % EXT2FS_CLUSTER_RATIO(fs) == 0) { - ext2fs_block_alloc_stats2(fs, blk, -1); - wd->cleared++; - } - *block_nr = 0; - return BLOCK_CHANGED; } - if (blockcnt < 0) + /* Don't free blocks at the end of the directory, they will be + * truncated by the caller. */ + if (blockcnt >= wd->outdir->num) { +#ifdef REHASH_DEBUG + printf(" - not freed\n"); +#endif return 0; + } + if (blockcnt < 0) { +#ifdef REHASH_DEBUG + printf(" - skip\n"); +#endif + return 0; + } dir = wd->outdir->buf + (blockcnt * fs->blocksize); wd->err = ext2fs_write_dir_block3(fs, *block_nr, dir, 0); +#ifdef REHASH_DEBUG + printf(" - write (%d)\n", wd->err); +#endif if (wd->err) return BLOCK_ABORT; return 0; @@ -684,10 +694,10 @@ static errcode_t write_directory(e2fsck_t ctx, ext2_filsys fs, wd.outdir = outdir; wd.err = 0; + wd.ino = ino; wd.ctx = ctx; - wd.cleared = 0; - retval = ext2fs_block_iterate3(fs, ino, 0, 0, + retval = ext2fs_block_iterate3(fs, ino, 0, NULL, write_dir_block, &wd); if (retval) return retval; @@ -699,14 +709,17 @@ static errcode_t write_directory(e2fsck_t ctx, ext2_filsys fs, inode.i_flags &= ~EXT2_INDEX_FL; else inode.i_flags |= EXT2_INDEX_FL; - retval = ext2fs_inode_size_set(fs, &inode, - outdir->num * fs->blocksize); +#ifdef REHASH_DEBUG + printf("%u: set inode size to %u blocks = %u bytes\n", + ino, outdir->num, outdir->num * fs->blocksize); +#endif + retval = ext2fs_inode_size_set(fs, &inode, (ext2_off64_t)outdir->num * + fs->blocksize); if (retval) return retval; - ext2fs_iblk_sub_blocks(fs, &inode, wd.cleared); - e2fsck_write_inode(ctx, ino, &inode, "rehash_dir"); - return 0; + /* ext2fs_punch() calls ext2fs_write_inode() which writes the size */ + return ext2fs_punch(fs, ino, &inode, NULL, outdir->num, ~0ULL); } errcode_t e2fsck_rehash_dir(e2fsck_t ctx, ext2_ino_t ino) @@ -715,32 +728,25 @@ errcode_t e2fsck_rehash_dir(e2fsck_t ctx, ext2_ino_t ino) errcode_t retval; struct ext2_inode inode; char *dir_buf = 0; - struct fill_dir_struct fd; - struct out_dir outdir; + struct fill_dir_struct fd = { NULL }; + struct out_dir outdir = { 0 }; - outdir.max = outdir.num = 0; - outdir.buf = 0; - outdir.hashes = 0; e2fsck_read_inode(ctx, ino, &inode, "rehash_dir"); retval = ENOMEM; - fd.harray = 0; dir_buf = malloc(inode.i_size); if (!dir_buf) goto errout; fd.max_array = inode.i_size / 32; - fd.num_array = 0; fd.harray = malloc(fd.max_array * sizeof(struct hash_entry)); if (!fd.harray) goto errout; + fd.ino = ino; fd.ctx = ctx; fd.buf = dir_buf; fd.inode = &inode; - fd.err = 0; - fd.dir_size = 0; - fd.compress = 0; if (!(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) || (inode.i_size / fs->blocksize) < 2) fd.compress = 1; diff --git a/tests/f_extent_htree/expect.1 b/tests/f_extent_htree/expect.1 new file mode 100644 index 00000000..223ca697 --- /dev/null +++ b/tests/f_extent_htree/expect.1 @@ -0,0 +1,29 @@ +Pass 1: Checking inodes, blocks, and sizes +Pass 2: Checking directory structure +Pass 3: Checking directory connectivity +Pass 3A: Optimizing directories +Pass 4: Checking reference counts +Pass 5: Checking group summary information + +test_filesys: ***** FILE SYSTEM WAS MODIFIED ***** + + 352 inodes used (41.12%, out of 856) + 0 non-contiguous files (0.0%) + 1 non-contiguous directory (0.3%) + # of inodes with ind/dind/tind blocks: 0/0/0 + Extent depth histogram: 342/1 + 586 blocks used (68.94%, out of 850) + 0 bad blocks + 0 large files + + 340 regular files + 3 directories + 0 character device files + 0 block device files + 0 fifos + 0 links + 0 symbolic links (0 fast symbolic links) + 0 sockets +------------ + 343 files +Exit status is 1 diff --git a/tests/f_extent_htree/expect.2 b/tests/f_extent_htree/expect.2 new file mode 100644 index 00000000..860b491e --- /dev/null +++ b/tests/f_extent_htree/expect.2 @@ -0,0 +1,7 @@ +Pass 1: Checking inodes, blocks, and sizes +Pass 2: Checking directory structure +Pass 3: Checking directory connectivity +Pass 4: Checking reference counts +Pass 5: Checking group summary information +test_filesys: 352/856 files (0.3% non-contiguous), 586/850 blocks +Exit status is 0 diff --git a/tests/f_extent_htree/image.gz b/tests/f_extent_htree/image.gz new file mode 100644 index 0000000000000000000000000000000000000000..284207efb12a04136c9f26c33f2197fed97cbd99 GIT binary patch literal 10101 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 S1hD`^CoQ(LFj+Co /dev/null) +TMPFILE=${TMPFILE:-"$TMPDIR/image"} +BSIZE=1024 + +> $OUT +mkdir -p $SRC/$SUB +# calculate the number of files needed to create the directory extent tree +# deep enough to exceed the in-inode index and spill into an index block. +# +# dirents per block * extents per block * (index blocks > i_blocks) +NUM=$(((BSIZE / (NAMELEN + 8)) * (BSIZE / 12) * 2)) +# Create source files. Unfortunately hard links will be copied as links, +# and blocks with only NULs will be turned into holes. +if [ ! -f $BASE.1 ]; then + for N in $(seq $NUM); do + echo "foo" > $BASE.$N + done >> $OUT +fi + +# make filesystem with enough inodes and blocks to hold all the test files +> $TMPFILE +NUM=$((NUM * 5 / 3)) +echo "mke2fs -b $BSIZE -O dir_index,extent -d$SRC -N$NUM $TMPFILE $NUM" >> $OUT +$MKE2FS -b $BSIZE -O dir_index,extent -d$SRC -N$NUM $TMPFILE $NUM >> $OUT 2>&1 +rm -r $SRC + +# Run e2fsck to convert dir to htree before deleting the files, as mke2fs +# doesn't do this. Run second e2fsck to verify there is no corruption yet. +( + EXP1=$test_dir/expect.pre.1 + EXP2=$test_dir/expect.pre.2 + OUT1=$test_name.pre.1.log + OUT2=$test_name.pre.2.log + DESCRIPTION="$(cat $test_dir/name) setup" + . $cmd_dir/run_e2fsck +) + +# generate a list of filenames for debugfs to delete, one from each leaf block +DELETE_LIST=$TMPDIR/delete.$$ +$DEBUGFS -c -R "htree subdir" $TMPFILE 2>> $OUT | + grep -A2 "Reading directory block" | + awk '/yyyyy/ { print "rm '$SUB'/"$4 }' > $DELETE_LIST +$DEBUGFS -w -f $DELETE_LIST $TMPFILE >> $OUT 2>&1 +rm $DELETE_LIST +cp $TMPFILE $TMPFILE.sav + +. $cmd_dir/run_e2fsck diff --git a/tests/f_h_badnode/expect.1 b/tests/f_h_badnode/expect.1 index ce2adb3f..95b1cee8 100644 --- a/tests/f_h_badnode/expect.1 +++ b/tests/f_h_badnode/expect.1 @@ -14,5 +14,5 @@ Pass 4: Checking reference counts Pass 5: Checking group summary information test_filesys: ***** FILE SYSTEM WAS MODIFIED ***** -test_filesys: 47730/100192 files (0.0% non-contiguous), 13551/31745 blocks +test_filesys: 47730/100192 files (0.0% non-contiguous), 13550/31745 blocks Exit status is 1 diff --git a/tests/f_h_badnode/expect.2 b/tests/f_h_badnode/expect.2 index b9dadb73..65985d14 100644 --- a/tests/f_h_badnode/expect.2 +++ b/tests/f_h_badnode/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: 47730/100192 files (0.0% non-contiguous), 13551/31745 blocks +test_filesys: 47730/100192 files (0.0% non-contiguous), 13550/31745 blocks Exit status is 0