From 8533d955f884f7b61d011a7c7d183a6c99db636b Mon Sep 17 00:00:00 2001 From: Andreas Dilger Date: Fri, 13 Apr 2012 01:13:58 -0600 Subject: [PATCH] e2fsck: track errors/badness found for each inode The present e2fsck code checks the inode, per field basis. It doesn't take into consideration to total sanity of the inode. This may cause e2fsck turning a garbage inode into an apparently sane inode ("It is a vessel of fertilizer, and none may abide its strength."). The following patch adds a heuristics to detect the degree of badness of an inode. icount mechanism is used to keep track of the badness of every inode. The badness is increased as various fields in inode are found to be corrupt. Badness above a certain threshold value results in deletion of the inode. The default badness threshold value is 7, it can be specified to e2fsck using "-E inode_badness_threshold=" This can avoid lengthy pass1b shared block processing, where a corrupt chunk of the inode table has resulted in a bunch of garbage inodes suddenly having shared blocks with a lot of good inodes (or each other). LU-11882 e2fsck: zero date is not inode badness Lustre FS OST has some precreated objects which have zeroed creation time. e2fsck adds badness points for such inodes. If OST has many precreated objects, e2fsck spends too much time during phase2, because processes each of such inode. Let's allow Lustre FS inode has zeroed time field for precreated objects. Change-Id: I171a0fe741449ca99b29b5af51032a7b4c716344 Cray-bug-id: LUS-6857 Signed-off-by: Artem Blagodarenko LU-8465 e2fsck: merge inode_badness after threads finish inode_badness is only specific for master-lustre branch, seperate it and fix a bug that inode_badness is not freed properly Signed-off-by: Wang Shilong Change-Id: I75a54adc088cb5b636171e4ee7929496858fedf9 Reviewed-on: https://review.whamcloud.com/39843 Reviewed-by: Andreas Dilger Tested-by: jenkins Tested-by: Maloo LU-5949 e2fsck: simplify inode badness handling Move the badness increment into fix_problem() so that code does not need to be spread across the code and explicitly set. Instead, take advantage of the fact that pctx->ino is almost always pointing at the bad inode and increment badness whenever fix_problem() is called on the inode. This also handles new problems in the future. That leaves only a handful of places in the code that need special handling to either set a higher badness (e.g. inode blocks that are directly referencing filesystem metadata or have wildly incorrect timestamps), or should not contribute to inode badness at all. The main exceptions are when reprocessing duplicate blocks in the inode (badness already set), and cleaning bad entries in a directory leaf block in check_dir_block(), which uses pctx->ino to reference the parent directory inode number. In such cases, fix_problem_bad() is called with 0 badness, since the leaf block was been repaired, and clearing the directory inode in this case is unnecessary. This patch should be merged into original badness patch on rebase. Fixes: 92a85a3ced2c ("e2fsck: track errors/badness found for each inode") Signed-off-by: Andreas Dilger Change-Id: I96cd21b5976991f0bb1c63fc99857c80e23ebbe5 Reviewed-on: https://review.whamcloud.com/41328 Tested-by: jenkins Tested-by: Maloo Reviewed-by: Wang Shilong Reviewed-by: Artem Blagodarenko LU-5949 e2fsck: call delete_inode() properly Fix incorrect argument for delete_inode() in pass1b(). Fixes: 8725134d2757 ("LU-5949 e2fsck: simplify inode badness handling") Signed-off-by: Andreas Dilger Change-Id: Ib90d87055e2c9359005034d06d88b083423ebbe5 Reviewed-on: https://review.whamcloud.com/41450 Tested-by: jenkins Reviewed-by: Artem Blagodarenko Reviewed-by: Wang Shilong Reviewed-by: Li Dongyang Tested-by: Maloo Change-Id: I9f5f48d979afebb9c953d9fb2777ebf570f30c15 Signed-off-by: Girish Shilamkar Signed-off-by: Andreas Dilger --- e2fsck/e2fsck.8.in | 10 +++ e2fsck/e2fsck.c | 8 +- e2fsck/e2fsck.h | 28 +++++- e2fsck/pass1.c | 206 ++++++++++++++++++++++++++++++++----------- e2fsck/pass1b.c | 16 ++-- e2fsck/pass2.c | 109 +++++++++++++++-------- e2fsck/pass3.c | 2 +- e2fsck/problem.c | 34 ++++++- e2fsck/problem.h | 19 +++- e2fsck/super.c | 6 +- e2fsck/unix.c | 18 ++++ lib/ext2fs/ext2fs.h | 1 + lib/ext2fs/icount.c | 20 ++++- tests/f_messy_inode/expect.1 | 10 ++- tests/f_messy_inode/expect.2 | 2 +- tests/f_messy_inode/script | 5 ++ 16 files changed, 384 insertions(+), 110 deletions(-) create mode 100644 tests/f_messy_inode/script diff --git a/e2fsck/e2fsck.8.in b/e2fsck/e2fsck.8.in index ab8cf82..9a8aaa6 100644 --- a/e2fsck/e2fsck.8.in +++ b/e2fsck/e2fsck.8.in @@ -221,6 +221,16 @@ be 1 or 2. The default extended attribute version format is 2. Only replay the journal if required, but do not perform any further checks or repairs. .TP +.BI inode_badness_threshold= threshold_value +A badness counter is associated with every inode, which determines the degree +of inode corruption. Each error found in the inode will increase the badness +by 1 or 2, and inodes with a badness at or above +.I threshold_value +will be prompted for deletion. The default +.I threshold_value +is 12, and must either be 0 (disabled), or between 3 and 200, as some valid +inode states may set a badness of 1 or 2 that should not clear the inode. +.TP .BI fragcheck During pass 1, print a detailed report of any discontiguous blocks for files in the filesystem. diff --git a/e2fsck/e2fsck.c b/e2fsck/e2fsck.c index b79c600..14fd2bb 100644 --- a/e2fsck/e2fsck.c +++ b/e2fsck/e2fsck.c @@ -87,6 +87,10 @@ errcode_t e2fsck_reset_context(e2fsck_t ctx) ext2fs_free_icount(ctx->inode_link_info); ctx->inode_link_info = 0; } + if (ctx->inode_badness) { + ext2fs_free_icount(ctx->inode_badness); + ctx->inode_badness = 0; + } if (ctx->journal_io) { if (ctx->fs && ctx->fs->io != ctx->journal_io) io_channel_close(ctx->journal_io); @@ -138,10 +142,6 @@ errcode_t e2fsck_reset_context(e2fsck_t ctx) ext2fs_free_inode_bitmap(ctx->inode_bb_map); ctx->inode_bb_map = 0; } - if (ctx->inode_bad_map) { - ext2fs_free_inode_bitmap(ctx->inode_bad_map); - ctx->inode_bad_map = 0; - } if (ctx->inode_imagic_map) { ext2fs_free_inode_bitmap(ctx->inode_imagic_map); ctx->inode_imagic_map = 0; diff --git a/e2fsck/e2fsck.h b/e2fsck/e2fsck.h index ab59be9..ed2410d 100644 --- a/e2fsck/e2fsck.h +++ b/e2fsck/e2fsck.h @@ -11,6 +11,7 @@ #include #include +#include #ifdef HAVE_UNISTD_H #include #endif @@ -230,6 +231,22 @@ enum clone_opt { E2F_CLONE_ZERO }; +#define EXT4_FITS_IN_INODE(ext4_inode, einode, field) \ + ((offsetof(typeof(*ext4_inode), field) + \ + sizeof(ext4_inode->field)) <= \ + (EXT2_GOOD_OLD_INODE_SIZE + \ + (einode)->i_extra_isize)) \ + +#define EXT4_XTIME_FUTURE(ctx, sb, xtime, margin) \ + (!((ctx)->flags & E2F_FLAG_TIME_INSANE) && \ + (xtime) > (ctx)->now + (margin)) +#define EXT4_XTIME_ANCIENT(ctx, sb, xtime, margin) \ + ((sb)->s_mkfs_time > (margin) && (xtime) < (sb)->s_mkfs_time - (margin)) + +#define BADNESS_THRESHOLD 12 +#define BADNESS_BAD_MODE 0x8000 +#define BADNESS_MAX 0x7fff + /* * Define the extended attribute refcount structure */ @@ -324,7 +341,6 @@ struct e2fsck_struct { /* The following inode bitmaps are separately used in thread_ctx Pass1*/ ext2fs_inode_bitmap inode_used_map; /* Inodes which are in use */ - ext2fs_inode_bitmap inode_bad_map; /* Inodes which are bad somehow */ ext2fs_inode_bitmap inode_dir_map; /* Inodes which are directories */ ext2fs_inode_bitmap inode_bb_map; /* Inodes which are in bad blocks */ ext2fs_inode_bitmap inode_imagic_map; /* AFS inodes */ @@ -342,6 +358,8 @@ struct e2fsck_struct { */ ext2_icount_t inode_count; ext2_icount_t inode_link_info; + ext2_icount_t inode_badness; + unsigned int inode_badness_threshold; ext2_refcount_t refcount; ext2_refcount_t refcount_extra; @@ -700,6 +718,14 @@ extern int e2fsck_pass1_check_symlink(ext2_filsys fs, ext2_ino_t ino, extern void e2fsck_clear_inode(e2fsck_t ctx, ext2_ino_t ino, struct ext2_inode *inode, int restart_flag, const char *source); +#define e2fsck_mark_inode_bad(ctx, pctx, code) \ + e2fsck_mark_inode_bad_loc(ctx, pctx, code, 1, __func__, __LINE__) +#define e2fsck_mark_inode_badder(ctx, pctx, code) \ + e2fsck_mark_inode_bad_loc(ctx, pctx, code, 2, __func__, __LINE__) +extern void e2fsck_mark_inode_bad_loc(e2fsck_t ctx, + struct problem_context *pctx, __u32 code, + int count, const char *func, const int line); +extern int e2fsck_fix_bad_inode(e2fsck_t ctx, struct problem_context *pctx); extern void e2fsck_intercept_block_allocations(e2fsck_t ctx); /* pass2.c */ diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c index de7f3ec..dc22fcd 100644 --- a/e2fsck/pass1.c +++ b/e2fsck/pass1.c @@ -20,7 +20,8 @@ * - A bitmap of which inodes are in use. (inode_used_map) * - A bitmap of which inodes are directories. (inode_dir_map) * - A bitmap of which inodes are regular files. (inode_reg_map) - * - A bitmap of which inodes have bad fields. (inode_bad_map) + * - An icount mechanism is used to keep track of + * inodes with bad fields and its badness (ctx->inode_badness) * - A bitmap of which inodes are in bad blocks. (inode_bb_map) * - A bitmap of which inodes are imagic inodes. (inode_imagic_map) * - A bitmap of which inodes are casefolded. (inode_casefold_map) @@ -86,7 +87,6 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx, static void mark_table_blocks(e2fsck_t ctx); static void alloc_bb_map(e2fsck_t ctx); static void alloc_imagic_map(e2fsck_t ctx); -static void mark_inode_bad(e2fsck_t ctx, ino_t ino); static void add_casefolded_dir(e2fsck_t ctx, ino_t ino); static void handle_fs_bad_blocks(e2fsck_t ctx); static EXT2_QSORT_TYPE process_inode_cmp(const void *a, const void *b); @@ -575,7 +575,7 @@ static void check_inode_extra_space(e2fsck_t ctx, struct problem_context *pctx, struct ext2_super_block *sb = ctx->fs->super; struct ext2_inode_large *inode; __u32 *eamagic; - int min, max; + int min, max, dirty = 0; ea_ibody_quota->blocks = 0; ea_ibody_quota->inodes = 0; @@ -606,8 +606,9 @@ static void check_inode_extra_space(e2fsck_t ctx, struct problem_context *pctx, inode->i_extra_isize = ctx->want_extra_isize; else inode->i_extra_isize = (inode->i_extra_isize + 3) & ~3; - e2fsck_write_inode_full(ctx, pctx->ino, pctx->inode, - EXT2_INODE_SIZE(sb), "pass1"); + dirty = 1; + + goto out; } /* check if there is no place for an EA header */ @@ -622,10 +623,7 @@ static void check_inode_extra_space(e2fsck_t ctx, struct problem_context *pctx, memset((char *)inode + EXT2_GOOD_OLD_INODE_SIZE, 0, EXT2_INODE_SIZE(sb) - EXT2_GOOD_OLD_INODE_SIZE); inode->i_extra_isize = ctx->want_extra_isize; - e2fsck_write_inode_full(ctx, pctx->ino, - (struct ext2_inode *)inode, - EXT2_INODE_SIZE(sb), - "check_inode_extra_space"); + dirty = 1; if (inode->i_extra_isize < ctx->min_extra_isize) ctx->min_extra_isize = inode->i_extra_isize; } @@ -633,6 +631,10 @@ static void check_inode_extra_space(e2fsck_t ctx, struct problem_context *pctx, if (*eamagic == EXT2_EXT_ATTR_MAGIC) check_ea_in_inode(ctx, pctx, ea_ibody_quota); + if (EXT4_XTIME_FUTURE(ctx, sb, inode->i_crtime, ctx->time_fudge)) + e2fsck_mark_inode_bad(ctx, pctx, PR_1_CRTIME_BAD); + else if (EXT4_XTIME_ANCIENT(ctx, sb, inode->i_crtime, ctx->time_fudge)) + e2fsck_mark_inode_bad(ctx, pctx, PR_1_CRTIME_BAD); /* * If the inode's extended atime (ctime, crtime, mtime) is stored in * the old, invalid format, repair it. @@ -645,7 +647,7 @@ static void check_inode_extra_space(e2fsck_t ctx, struct problem_context *pctx, CHECK_INODE_EXTRA_NEGATIVE_EPOCH(inode, crtime) || CHECK_INODE_EXTRA_NEGATIVE_EPOCH(inode, mtime))) { - if (!fix_problem(ctx, PR_1_EA_TIME_OUT_OF_RANGE, pctx)) + if (!fix_problem_bad(ctx, PR_1_EA_TIME_OUT_OF_RANGE, pctx, 2)) return; if (CHECK_INODE_EXTRA_NEGATIVE_EPOCH(inode, atime)) @@ -656,10 +658,13 @@ static void check_inode_extra_space(e2fsck_t ctx, struct problem_context *pctx, inode->i_crtime_extra &= ~EXT4_EPOCH_MASK; if (CHECK_INODE_EXTRA_NEGATIVE_EPOCH(inode, mtime)) inode->i_mtime_extra &= ~EXT4_EPOCH_MASK; - e2fsck_write_inode_full(ctx, pctx->ino, pctx->inode, - EXT2_INODE_SIZE(sb), "pass1"); + dirty = 1; } +out: + if (dirty) + e2fsck_write_inode_full(ctx, pctx->ino, pctx->inode, + EXT2_INODE_SIZE(sb), "pass1"); } static _INLINE_ int is_blocks_used(e2fsck_t ctx, blk64_t block, @@ -1127,6 +1132,28 @@ err: return retval; } +int e2fsck_fix_bad_inode(e2fsck_t ctx, struct problem_context *pctx) +{ + __u16 badness; + int rc = 0; + + if (!ctx->inode_badness) + return 0; + + if (ext2fs_icount_fetch(ctx->inode_badness, pctx->ino, &badness)) + return 0; + + if ((badness & ~BADNESS_BAD_MODE) > ctx->inode_badness_threshold) { + __u64 pctx_num_sav = pctx->num; + + pctx->num = badness; + rc = fix_problem_bad(ctx, PR_1B_INODE_TOOBAD, pctx, 0); + pctx->num = pctx_num_sav; + } + + return rc; +} + static void finish_processing_inode(e2fsck_t ctx, ext2_ino_t ino, struct problem_context *pctx, int failed_csum) @@ -1146,7 +1173,7 @@ static void finish_processing_inode(e2fsck_t ctx, ext2_ino_t ino, #define FINISH_INODE_LOOP(ctx, ino, pctx, failed_csum) \ do { \ finish_processing_inode((ctx), (ino), (pctx), (failed_csum)); \ - if ((ctx)->flags & E2F_FLAG_ABORT) { \ + if (e2fsck_should_abort(ctx)) { \ e2fsck_pass1_check_unlock(ctx); \ return; \ } \ @@ -1634,6 +1661,23 @@ static void e2fsck_pass1_post(e2fsck_t ctx) } +/* + * Lustre FS creates special inodes - precreated objects. + * They are zero-sized and have special attributes: + * mode |= S_ISUID | S_ISGID; + * valid |= LA_ATIME | LA_MTIME | LA_CTIME; + * atime = 0; + * mtime = 0; + * ctime = 0; + */ +static int precreated_object(struct ext2_inode *inode) +{ + if (((inode->i_mode & (S_ISUID | S_ISGID)) == (S_ISUID | S_ISGID)) && + inode->i_ctime == 0) + return 1; + return 0; +} + void e2fsck_pass1_run(e2fsck_t ctx) { int i; @@ -1811,9 +1855,8 @@ void e2fsck_pass1_run(e2fsck_t ctx) if (ctx->global_ctx) { if (ctx->options & E2F_OPT_DEBUG && ctx->options & E2F_OPT_MULTITHREAD) - fprintf(stderr, "thread %d jumping to group %u\n", - ctx->thread_info.et_thread_index, - ctx->thread_info.et_group_start); + log_out(ctx, "jumping to group %u\n", + ctx->thread_info.et_group_start); pctx.errcode = ext2fs_inode_scan_goto_blockgroup(scan, ctx->thread_info.et_group_start); if (pctx.errcode) { @@ -2368,18 +2411,21 @@ void e2fsck_pass1_run(e2fsck_t ctx) frag = fsize = 0; } + /* Fixed in pass2, e2fsck_process_bad_inode(). */ if (inode->i_faddr || frag || fsize || (!ext2fs_has_feature_largedir(fs->super) && - (LINUX_S_ISDIR(inode->i_mode) && inode->i_size_high))) - mark_inode_bad(ctx, ino); + LINUX_S_ISDIR(inode->i_mode) && inode->i_size_high)) + e2fsck_mark_inode_bad(ctx, &pctx, + PR_2_DIR_SIZE_HIGH_ZERO); if ((fs->super->s_creator_os != EXT2_OS_HURD) && !ext2fs_has_feature_64bit(fs->super) && inode->osd2.linux2.l_i_file_acl_high != 0) - mark_inode_bad(ctx, ino); + e2fsck_mark_inode_bad(ctx, &pctx, + PR_2_I_FILE_ACL_HI_ZERO); if ((fs->super->s_creator_os != EXT2_OS_HURD) && !ext2fs_has_feature_huge_file(fs->super) && (inode->osd2.linux2.l_i_blocks_hi != 0)) - mark_inode_bad(ctx, ino); + e2fsck_mark_inode_bad(ctx, &pctx, PR_2_BLOCKS_HI_ZERO); if (inode->i_flags & EXT2_IMAGIC_FL) { if (imagic_fs) { if (!ctx->inode_imagic_map) @@ -2470,8 +2516,37 @@ void e2fsck_pass1_run(e2fsck_t ctx) check_immutable(ctx, &pctx); check_size(ctx, &pctx); ctx->fs_sockets_count++; - } else - mark_inode_bad(ctx, ino); + } else { + e2fsck_mark_inode_bad(ctx, &pctx, PR_2_BAD_MODE); + } + + /* Future atime/mtime may be valid in rare cases, but are more + * likely to indicate corruption. Don't try to fix timestamps, + * but take into consideration whether inode is corrupted. If + * no other problems with the inode, probably it is OK. */ + if (EXT4_XTIME_FUTURE(ctx, sb, inode->i_atime, ctx->time_fudge)) + e2fsck_mark_inode_bad(ctx, &pctx, PR_1_INODE_BAD_TIME); + if (EXT4_XTIME_FUTURE(ctx, sb, inode->i_mtime, ctx->time_fudge)) + e2fsck_mark_inode_bad(ctx, &pctx, PR_1_INODE_BAD_TIME); + + /* Since ctime cannot be set directly from userspace, consider + * very old/future values worse than a bad atime/mtime. Same for + * crtime, but it is checked in check_inode_extra_space(). */ + if (EXT4_XTIME_FUTURE(ctx, sb, inode->i_ctime, ctx->time_fudge)) + e2fsck_mark_inode_badder(ctx, &pctx, + PR_1_INODE_BAD_TIME); + else if (!precreated_object(inode) && + EXT4_XTIME_ANCIENT(ctx, sb, inode->i_ctime, + ctx->time_fudge)) + e2fsck_mark_inode_badder(ctx, &pctx, + PR_1_INODE_BAD_TIME); + + /* no restart if clearing bad inode before block processing */ + if (e2fsck_fix_bad_inode(ctx, &pctx)) { + e2fsck_clear_inode(ctx, ino, inode, 0, "pass1"); + goto next_unlock; + } + if (!(inode->i_flags & EXT4_EXTENTS_FL) && !(inode->i_flags & EXT4_INLINE_DATA_FL)) { if (inode->i_block[EXT2_IND_BLOCK]) @@ -2532,6 +2607,7 @@ void e2fsck_pass1_run(e2fsck_t ctx) goto endit; } } + next_unlock: e2fsck_pass1_check_unlock(ctx); } process_inodes(ctx, block_buf, inodes_to_process, @@ -3009,6 +3085,11 @@ static errcode_t e2fsck_pass1_merge_icounts(e2fsck_t global_ctx, e2fsck_t thread return ret; ret = e2fsck_pass1_merge_icount(&global_ctx->inode_link_info, &thread_ctx->inode_link_info); + if (ret) + return ret; + + ret = e2fsck_pass1_merge_icount(&global_ctx->inode_badness, + &thread_ctx->inode_badness); return ret; } @@ -3256,11 +3337,6 @@ static errcode_t e2fsck_pass1_merge_context(e2fsck_t global_ctx, return retval; retval = e2fsck_pass1_merge_bitmap(global_fs, - &thread_ctx->inode_bad_map, - &global_ctx->inode_bad_map); - if (retval) - return retval; - retval = e2fsck_pass1_merge_bitmap(global_fs, &thread_ctx->inode_dir_map, &global_ctx->inode_dir_map); if (retval) @@ -3668,27 +3744,44 @@ static EXT2_QSORT_TYPE process_inode_cmp(const void *a, const void *b) } /* - * Mark an inode as being bad in some what + * Mark an inode as being bad and increment its badness counter. */ -static void mark_inode_bad(e2fsck_t ctx, ino_t ino) +void e2fsck_mark_inode_bad_loc(e2fsck_t ctx, struct problem_context *pctx, + __u32 code, int badness, const char *func, + const int line) { - struct problem_context pctx; + __u16 badness_before, badness_after; + __u64 pctx_num_sav = pctx->num; - if (!ctx->inode_bad_map) { - clear_problem_context(&pctx); + if (!ctx->inode_badness_threshold) /* badness is disabled */ + return; - pctx.errcode = e2fsck_allocate_inode_bitmap(ctx->fs, - _("bad inode map"), EXT2FS_BMAP64_RBTREE, - "inode_bad_map", &ctx->inode_bad_map); - if (pctx.errcode) { - pctx.num = 3; - fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx); - /* Should never get here */ + if (!ctx->inode_badness) { + errcode_t retval; + + retval = ext2fs_create_icount2(ctx->fs, 0, 0, NULL, + &ctx->inode_badness); + if (retval) { + pctx->errcode = retval; + fix_problem(ctx, PR_1_ALLOCATE_ICOUNT, pctx); ctx->flags |= E2F_FLAG_ABORT; return; } } - ext2fs_mark_inode_bitmap2(ctx->inode_bad_map, ino); + ext2fs_icount_fetch(ctx->inode_badness, pctx->ino, &badness_before); + if (badness + badness_before > BADNESS_MAX) + badness_after = BADNESS_MAX; + else if (badness < 0 && badness_before < -badness) + badness_after = 0; + else + badness_after = badness_before + badness; + ext2fs_icount_store(ctx->inode_badness, pctx->ino, badness_after); + + if (ctx->options & E2F_OPT_DEBUG) + log_out(ctx, + "%s:%d: increase inode %lu badness %u to %u for %x\n", + func, line, (unsigned long)pctx->ino, badness_before, + badness_after, code); } static void add_casefolded_dir(e2fsck_t ctx, ino_t ino) @@ -3895,7 +3988,8 @@ static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx, if (!ext2fs_has_feature_xattr(fs->super) || (blk < fs->super->s_first_data_block) || (blk >= ext2fs_blocks_count(fs->super))) { - mark_inode_bad(ctx, ino); + /* Fixed in pass2, e2fsck_process_bad_inode(). */ + e2fsck_mark_inode_bad(ctx, pctx, PR_2_FILE_ACL_ZERO); return 0; } @@ -4175,8 +4269,10 @@ static int handle_htree(e2fsck_t ctx, struct problem_context *pctx, } retval = io_channel_read_blk64(fs->io, blk, 1, block_buf); - if (retval && fix_problem(ctx, PR_1_HTREE_BADROOT, pctx)) - return 1; + if (retval) { + if (fix_problem(ctx, PR_1_HTREE_BADROOT, pctx)) + return 1; + } /* XXX should check that beginning matches a directory */ root = (struct ext2_dx_root_info *) (block_buf + 24); @@ -4258,8 +4354,8 @@ void e2fsck_clear_inode(e2fsck_t ctx, ext2_ino_t ino, ext2fs_unmark_inode_bitmap2(ctx->inode_used_map, ino); if (ctx->inode_reg_map) ext2fs_unmark_inode_bitmap2(ctx->inode_reg_map, ino); - if (ctx->inode_bad_map) - ext2fs_unmark_inode_bitmap2(ctx->inode_bad_map, ino); + if (ctx->inode_badness) + ext2fs_icount_store(ctx->inode_badness, ino, 0); /* * If the inode was partially accounted for before processing @@ -4431,7 +4527,9 @@ static void scan_extent_node(e2fsck_t ctx, struct problem_context *pctx, #endif if (try_repairs && problem) { report_problem: - if (fix_problem(ctx, problem, pctx)) { + /* Record badness only if extent is within inode */ + if (fix_problem_bad(ctx, problem, pctx, + info.curr_level == 0)) { if (ctx->invalid_bitmaps) { /* * If fsck knows the bitmaps are bad, @@ -4500,9 +4598,9 @@ report_problem: extent.e_pblk)) { next_try_repairs = 0; pctx->blk = blk; - fix_problem(ctx, + fix_problem_bad(ctx, PR_1_CRITICAL_METADATA_COLLISION, - pctx); + pctx, 2); if ((ctx->options & E2F_OPT_NO) == 0) ctx->flags |= E2F_FLAG_RESTART_LATER; } @@ -4979,6 +5077,10 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx, if (!pb.num_blocks && pb.is_dir && !(inode->i_flags & EXT4_INLINE_DATA_FL)) { + /* + * The mode might be in-correct. Increasing the badness by + * small amount won't hurt much. + */ if (fix_problem(ctx, PR_1_ZERO_LENGTH_DIR, pctx)) { e2fsck_clear_inode(ctx, ino, inode, 0, "check_blocks"); ctx->fs_directory_count--; @@ -5111,7 +5213,11 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx, e2fsck_rehash_dir_later(ctx, ino); out: - if (dirty_inode) + /* need restart if clearing bad inode after block processing */ + if (e2fsck_fix_bad_inode(ctx, pctx)) + e2fsck_clear_inode(ctx, ino, inode, E2F_FLAG_RESTART, + "check_blocks_bad"); + else if (dirty_inode) e2fsck_write_inode(ctx, ino, inode, "check_blocks"); } @@ -5263,7 +5369,7 @@ static int process_block(ext2_filsys fs, blk < ctx->fs->super->s_blocks_count && ext2fs_test_block_bitmap2(ctx->block_metadata_map, blk)) { pctx->blk = blk; - fix_problem(ctx, PR_1_CRITICAL_METADATA_COLLISION, pctx); + fix_problem_bad(ctx, PR_1_CRITICAL_METADATA_COLLISION, pctx, 2); if ((ctx->options & E2F_OPT_NO) == 0) ctx->flags |= E2F_FLAG_RESTART_LATER; } diff --git a/e2fsck/pass1b.c b/e2fsck/pass1b.c index e873ac0..1c8b01f 100644 --- a/e2fsck/pass1b.c +++ b/e2fsck/pass1b.c @@ -340,6 +340,14 @@ static void pass1b(e2fsck_t ctx, char *block_buf) pb.last_blk = 0; pb.pctx->blk = pb.pctx->blk2 = 0; + if (e2fsck_fix_bad_inode(ctx, &pctx)) { + struct dup_inode dp = { .inode = inode }; + + /* delete_file only uses dp.inode */ + delete_file(ctx, ino, &dp, block_buf); + continue; + } + if (ext2fs_inode_has_valid_blocks2(fs, EXT2_INODE(&inode)) || (ino == EXT2_BAD_INO)) pctx.errcode = ext2fs_block_iterate3(fs, ino, @@ -575,7 +583,7 @@ static void pass1d(e2fsck_t ctx, char *block_buf) pctx.dir = p->dir; pctx.blkcount = p->num_dupblocks; pctx.num = meta_data ? shared_len+1 : shared_len; - fix_problem(ctx, PR_1D_DUP_FILE, &pctx); + fix_problem_bad(ctx, PR_1D_DUP_FILE, &pctx, pctx.blkcount / 2); pctx.blkcount = 0; pctx.num = 0; @@ -593,14 +601,14 @@ static void pass1d(e2fsck_t ctx, char *block_buf) pctx.inode = EXT2_INODE(&t->inode); pctx.ino = shared[i]; pctx.dir = t->dir; - fix_problem(ctx, PR_1D_DUP_FILE_LIST, &pctx); + fix_problem_bad(ctx, PR_1D_DUP_FILE_LIST, &pctx, 0); } /* * Even if the file shares blocks with itself, we still need to * clone the blocks. */ if (file_ok && (meta_data ? shared_len+1 : shared_len) != 0) { - fix_problem(ctx, PR_1D_DUP_BLOCKS_DEALT, &pctx); + fix_problem_bad(ctx, PR_1D_DUP_BLOCKS_DEALT, &pctx, 0); continue; } if (ctx->shared != E2F_SHARED_DELETE && @@ -720,8 +728,6 @@ static void delete_file(e2fsck_t ctx, ext2_ino_t ino, delete_file_block, &pb); if (pctx.errcode) fix_problem(ctx, PR_1B_BLOCK_ITERATE, &pctx); - if (ctx->inode_bad_map) - ext2fs_unmark_inode_bitmap2(ctx->inode_bad_map, ino); if (ctx->inode_reg_map) ext2fs_unmark_inode_bitmap2(ctx->inode_reg_map, ino); ext2fs_unmark_inode_bitmap2(ctx->inode_dir_map, ino); diff --git a/e2fsck/pass2.c b/e2fsck/pass2.c index e504b30..34db6fd 100644 --- a/e2fsck/pass2.c +++ b/e2fsck/pass2.c @@ -33,16 +33,16 @@ * Pass 2 relies on the following information from previous passes: * - The directory information collected in pass 1. * - The inode_used_map bitmap - * - The inode_bad_map bitmap + * - The inode_badness bitmap * - The inode_dir_map bitmap * - The encrypted_file_info * - The inode_casefold_map bitmap * * Pass 2 frees the following data structures - * - The inode_bad_map bitmap * - The inode_reg_map bitmap * - The encrypted_file_info * - The inode_casefold_map bitmap + * - The inode_badness bitmap */ #define _GNU_SOURCE 1 /* get strnlen() */ @@ -281,10 +281,6 @@ void e2fsck_pass2(e2fsck_t ctx) ext2fs_free_mem(&buf); ext2fs_free_dblist(fs->dblist); - if (ctx->inode_bad_map) { - ext2fs_free_inode_bitmap(ctx->inode_bad_map); - ctx->inode_bad_map = 0; - } if (ctx->inode_reg_map) { ext2fs_free_inode_bitmap(ctx->inode_reg_map); ctx->inode_reg_map = 0; @@ -298,6 +294,10 @@ void e2fsck_pass2(e2fsck_t ctx) ext2fs_u32_list_free(ctx->casefolded_dirs); ctx->casefolded_dirs = 0; } + if (ctx->inode_badness) { + ext2fs_free_icount(ctx->inode_badness); + ctx->inode_badness = 0; + } clear_problem_context(&pctx); if (ctx->large_files) { @@ -574,6 +574,7 @@ static _INLINE_ int check_filetype(e2fsck_t ctx, { int filetype = ext2fs_dirent_file_type(dirent); int should_be = EXT2_FT_UNKNOWN; + __u16 badness = 0; struct ext2_inode inode; if (!ext2fs_has_feature_filetype(ctx->fs->super)) { @@ -584,16 +585,18 @@ static _INLINE_ int check_filetype(e2fsck_t ctx, return 1; } + if (ctx->inode_badness) + ext2fs_icount_fetch(ctx->inode_badness, dirent->inode, + &badness); + if (ext2fs_test_inode_bitmap2(ctx->inode_dir_map, dirent->inode)) { should_be = EXT2_FT_DIR; } else if (ext2fs_test_inode_bitmap2(ctx->inode_reg_map, dirent->inode)) { should_be = EXT2_FT_REG_FILE; - } else if (ctx->inode_bad_map && - ext2fs_test_inode_bitmap2(ctx->inode_bad_map, - dirent->inode)) + } else if (badness & BADNESS_BAD_MODE) { should_be = 0; - else { + } else { e2fsck_read_inode(ctx, dirent->inode, &inode, "check_filetype"); should_be = ext2_file_type(inode.i_mode); @@ -1474,26 +1477,6 @@ skip_checksum: } } - /* - * If the inode was marked as having bad fields in - * pass1, process it and offer to fix/clear it. - * (We wait until now so that we can display the - * pathname to the user.) - */ - if (ctx->inode_bad_map && - ext2fs_test_inode_bitmap2(ctx->inode_bad_map, - dirent->inode)) { - if (e2fsck_process_bad_inode(ctx, ino, - dirent->inode, - buf + fs->blocksize)) { - dirent->inode = 0; - dir_modified++; - goto next; - } - if (ctx->flags & E2F_FLAG_SIGNAL_MASK) - return DIRENT_ABORT; - } - group = ext2fs_group_of_ino(fs, dirent->inode); first_unused_inode = group * fs->super->s_inodes_per_group + 1 + fs->super->s_inodes_per_group - @@ -1534,6 +1517,25 @@ skip_checksum: } } + /* + * If the inode was marked as having bad fields in + * pass1, process it and offer to fix/clear it. + * (We wait until now so that we can display the + * pathname to the user.) + */ + if (!(ctx->flags & E2F_FLAG_RESTART_LATER) && + ctx->inode_badness && + ext2fs_icount_is_set(ctx->inode_badness, dirent->inode)) { + if (e2fsck_process_bad_inode(ctx, ino, dirent->inode, + buf + fs->blocksize)) { + dirent->inode = 0; + dir_modified++; + goto next; + } + if (ctx->flags & E2F_FLAG_SIGNAL_MASK) + return DIRENT_ABORT; + } + /* * Offer to clear unused inodes; if we are going to be * restarting the scan due to bg_itable_unused being @@ -1548,15 +1550,19 @@ skip_checksum: problem = PR_2_UNUSED_INODE; if (problem) { - if (fix_problem(ctx, problem, &cd->pctx)) { + int next = 0; + + if (fix_problem_bad(ctx, problem, &cd->pctx, 0)) { dirent->inode = 0; dir_modified++; - goto next; + next = 1; } else { ext2fs_unmark_valid(fs); if (problem == PR_2_BAD_INO) - goto next; + next = 1; } + if (next) + goto next; } if (check_filetype(ctx, dirent, ino, &cd->pctx)) @@ -1821,8 +1827,17 @@ static void deallocate_inode(e2fsck_t ctx, ext2_ino_t ino, char* block_buf) struct problem_context pctx; __u32 count; struct del_block del_block; + int extent_fs = 0; e2fsck_read_inode(ctx, ino, &inode, "deallocate_inode"); + /* ext2fs_block_iterate2() depends on the extents flags */ + if (inode.i_flags & EXT4_EXTENTS_FL) + extent_fs = 1; + e2fsck_clear_inode(ctx, ino, &inode, 0, "deallocate_inode"); + if (extent_fs) { + inode.i_flags |= EXT4_EXTENTS_FL; + e2fsck_write_inode(ctx, ino, &inode, "deallocate_inode"); + } clear_problem_context(&pctx); pctx.ino = ino; @@ -1851,6 +1866,8 @@ static void deallocate_inode(e2fsck_t ctx, ext2_ino_t ino, char* block_buf) ext2fs_block_alloc_stats2(fs, ext2fs_file_acl_block(fs, &inode), -1); } + if (ctx->inode_badness) + ext2fs_icount_store(ctx->inode_badness, ino, 0); ext2fs_file_acl_block_set(fs, &inode, 0); } @@ -1910,8 +1927,14 @@ int e2fsck_process_bad_inode(e2fsck_t ctx, ext2_ino_t dir, unsigned char *frag, *fsize; struct problem_context pctx; problem_t problem = 0; + __u16 badness = 0; + unsigned int flags = ctx->fs->flags; + flags = ctx->fs->flags; + ctx->fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS; e2fsck_read_inode(ctx, ino, &inode, "process_bad_inode"); + ctx->fs->flags = (flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) | + (ctx->fs->flags & ~EXT2_FLAG_IGNORE_CSUM_ERRORS); clear_problem_context(&pctx); pctx.ino = ino; @@ -1958,6 +1981,12 @@ int e2fsck_process_bad_inode(e2fsck_t ctx, ext2_ino_t dir, } else not_fixed++; problem = 0; + /* + * A large value is associated with bad mode in order to detect + * that mode was corrupt in check_filetype() + */ + e2fsck_mark_inode_bad_loc(ctx, &pctx, problem, BADNESS_BAD_MODE, + __func__, __LINE__); } if (inode.i_faddr) { @@ -2035,10 +2064,18 @@ int e2fsck_process_bad_inode(e2fsck_t ctx, ext2_ino_t dir, not_fixed++; } + /* The high value from BADNESS_BAD_MODE should not delete the inode */ + if (e2fsck_fix_bad_inode(ctx, &pctx)) { + deallocate_inode(ctx, ino, 0); + if (ctx->flags & E2F_FLAG_SIGNAL_MASK) + return 0; + inode_modified = 0; + } else { + not_fixed++; + } + if (inode_modified) e2fsck_write_inode(ctx, ino, &inode, "process_bad_inode"); - if (!not_fixed && ctx->inode_bad_map) - ext2fs_unmark_inode_bitmap2(ctx->inode_bad_map, ino); return 0; } @@ -2058,7 +2095,7 @@ static int allocate_dir_block(e2fsck_t ctx, char *block; struct ext2_inode inode; - if (fix_problem(ctx, PR_2_DIRECTORY_HOLE, pctx) == 0) + if (fix_problem_bad(ctx, PR_2_DIRECTORY_HOLE, pctx, 0) == 0) return 1; /* diff --git a/e2fsck/pass3.c b/e2fsck/pass3.c index cedaaf5..ace3b8b 100644 --- a/e2fsck/pass3.c +++ b/e2fsck/pass3.c @@ -464,7 +464,7 @@ unlink: pctx.errcode = retval; fix_problem(ctx, PR_3_ERR_FIND_LPF, &pctx); } - if (!fix_problem(ctx, PR_3_NO_LF_DIR, 0)) + if (!fix_problem(ctx, PR_3_NO_LF_DIR, &pctx)) return 0; /* diff --git a/e2fsck/problem.c b/e2fsck/problem.c index 10713a2..ae9cbba 100644 --- a/e2fsck/problem.c +++ b/e2fsck/problem.c @@ -1318,6 +1318,11 @@ static struct e2fsck_problem problem_table[] = { "without deletion of an EA.\n"), PROMPT_FIX, 0 }, + /* invalid inode creation time */ + { PR_1_CRTIME_BAD, + N_("@i %i creation time (%t) invalid.\n"), + PROMPT_CLEAR, PR_PREEN_OK | PR_NO_OK }, + /* Failed to goto block group */ { PR_1_SCAN_GOTO, N_("failed to goto block group"), @@ -1375,6 +1380,11 @@ static struct e2fsck_problem problem_table[] = { " %b--%c", PROMPT_NONE, PR_LATCH_DBLOCK | PR_PREEN_NOHDR, 0, 0, 0 }, + /* Inode is badly corrupt (badness value = ) */ + { PR_1B_INODE_TOOBAD, + N_("@i %i is badly corrupt (badness value = %N). "), + PROMPT_CLEAR, PR_PREEN_OK }, + /* Pass 1C: Scan directories for inodes with multiply-claimed blocks. */ { PR_1C_PASS_HEADER, N_("Pass 1C: Scanning directories for @is with @m @bs\n"), @@ -1882,6 +1892,10 @@ static struct e2fsck_problem problem_table[] = { N_("Duplicate filename @E found. "), PROMPT_CLEAR, 0, 0, 0, 0 }, + /* Inode is badly corrupt (badness value = ) */ + { PR_2_INODE_TOOBAD, + N_("@i %i is badly corrupt (badness value = %N). "), + PROMPT_CLEAR, PR_PREEN_OK }, /* Pass 3 errors */ @@ -2462,7 +2476,8 @@ static void print_problem(FILE *f, problem_t code, int answer, int fixed, fputs("/>\n", f); } -int fix_problem(e2fsck_t ctx, problem_t code, struct problem_context *pctx) +int fix_problem_loc(e2fsck_t ctx, problem_t code, struct problem_context *pctx, + int badness, const char *func, const int line) { ext2_filsys fs = ctx->fs; struct e2fsck_problem *ptr; @@ -2473,6 +2488,10 @@ int fix_problem(e2fsck_t ctx, problem_t code, struct problem_context *pctx) int suppress = 0; int fixed = 0; + /* ino is always set in pass1, where we will hit most badness */ + if (pctx && pctx->ino != 0 && badness && code < PR_3_PASS_HEADER) + e2fsck_mark_inode_bad_loc(ctx, pctx, code, badness, func, line); + ptr = find_problem(code); if (!ptr) { printf(_("Unhandled error code (0x%x)!\n"), code); @@ -2520,7 +2539,8 @@ int fix_problem(e2fsck_t ctx, problem_t code, struct problem_context *pctx) if (ptr->flags & PR_LATCH_MASK) { ldesc = find_latch(ptr->flags & PR_LATCH_MASK); if (ldesc->question && !(ldesc->flags & PRL_LATCHED)) { - ans = fix_problem(ctx, ldesc->question, pctx); + ans = fix_problem_loc(ctx, ldesc->question, pctx, + 0, func, line); if (ans == 1) ldesc->flags |= PRL_YES; if (ans == 0) @@ -2623,7 +2643,8 @@ int fix_problem(e2fsck_t ctx, problem_t code, struct problem_context *pctx) fatal_error(ctx, 0); if (ptr->flags & PR_AFTER_CODE) - answer = fix_problem(ctx, ptr->second_code, pctx); + answer = fix_problem_loc(ctx, ptr->second_code, pctx, + 0, func, line); if (answer && (ptr->prompt != PROMPT_NONE) && !(ptr->flags & PR_NOT_A_FIX)) { @@ -2674,6 +2695,13 @@ void preenhalt(e2fsck_t ctx) return; } +void e2fsck_mark_inode_bad_loc(e2fsck_t ctx, + struct problem_context *pctx, __u32 code, + int count, const char *func, const int line) +{ + return; +} + errcode_t profile_get_string(profile_t profile, const char *name, const char *subname, const char *subsubname, const char *def_val, diff --git a/e2fsck/problem.h b/e2fsck/problem.h index beaac1c..ffb1c49 100644 --- a/e2fsck/problem.h +++ b/e2fsck/problem.h @@ -694,6 +694,9 @@ struct problem_context { /* Inode has illegal EA value inode */ #define PR_1_ATTR_VALUE_EA_INODE 0x010083 +/* Inode has bad timestamp */ +#define PR_1_INODE_BAD_TIME 0x010084 + /* Parent inode has invalid EA entry. EA inode does not have * EXT4_EA_INODE_FL flag. Delete EA entry? */ #define PR_1_ATTR_NO_EA_INODE_FL 0x010085 @@ -744,6 +747,9 @@ struct problem_context { */ #define PR_1_CLEAR_EXTRA_ISIZE 0x010093 +/* invalid inode creation time */ +#define PR_1_CRTIME_BAD 0x010094 + /* Failed to goto block group */ #define PR_1_SCAN_GOTO 0x0100A0 @@ -778,6 +784,9 @@ struct problem_context { /* Duplicate/bad block range in inode */ #define PR_1B_DUP_RANGE 0x011008 +/* Inode is badly corrupt (badness value = ) */ +#define PR_1B_INODE_TOOBAD 0x011009 + /* Pass 1C: Scan directories for inodes with dup blocks. */ #define PR_1C_PASS_HEADER 0x012000 @@ -1081,6 +1090,9 @@ struct problem_context { /* Non-unique filename found, but can't rename */ #define PR_2_NON_UNIQUE_FILE_NO_RENAME 0x020054 +/* Inode is badly corrupt (badness value = ) */ +#define PR_2_INODE_TOOBAD 0x020055 + /* * Pass 3 errors */ @@ -1337,7 +1349,12 @@ struct problem_context { /* * Function declarations */ -int fix_problem(e2fsck_t ctx, problem_t code, struct problem_context *pctx); +#define fix_problem(ctx, code, pctx) \ + fix_problem_bad(ctx, code, pctx, 1) +#define fix_problem_bad(ctx, code, pctx, badness) \ + fix_problem_loc(ctx, code, pctx, badness, __func__, __LINE__) +int fix_problem_loc(e2fsck_t ctx, problem_t code, struct problem_context *pctx, + int badness, const char *func, const int line); int end_problem_latch(e2fsck_t ctx, int mask); int set_latch_flags(int mask, int setflags, int clearflags); int get_latch_flags(int mask, int *value); diff --git a/e2fsck/super.c b/e2fsck/super.c index e1c3f93..59da481 100644 --- a/e2fsck/super.c +++ b/e2fsck/super.c @@ -1039,8 +1039,7 @@ void check_super_block(e2fsck_t ctx) * write time is in the future. */ if (!broken_system_clock && fs->super->s_checkinterval && - !(ctx->flags & E2F_FLAG_TIME_INSANE) && - fs->super->s_mtime > (__u32) ctx->now) { + EXT4_XTIME_FUTURE(ctx, fs->super, fs->super->s_mtime, 0)) { pctx.num = fs->super->s_mtime; problem = PR_0_FUTURE_SB_LAST_MOUNT; if (fs->super->s_mtime <= (__u32) ctx->now + ctx->time_fudge) @@ -1051,8 +1050,7 @@ void check_super_block(e2fsck_t ctx) } } if (!broken_system_clock && fs->super->s_checkinterval && - !(ctx->flags & E2F_FLAG_TIME_INSANE) && - fs->super->s_wtime > (__u32) ctx->now) { + EXT4_XTIME_FUTURE(ctx, fs->super, fs->super->s_wtime, 0)) { pctx.num = fs->super->s_wtime; problem = PR_0_FUTURE_SB_LAST_WRITE; if (fs->super->s_wtime <= (__u32) ctx->now + ctx->time_fudge) diff --git a/e2fsck/unix.c b/e2fsck/unix.c index fe88356..d0e12d9 100644 --- a/e2fsck/unix.c +++ b/e2fsck/unix.c @@ -791,6 +791,22 @@ static void parse_extended_opts(e2fsck_t ctx, const char *opts) extended_usage++; continue; } + /* -E inode_badness_threshold= */ + } else if (strcmp(token, "inode_badness_threshold") == 0) { + unsigned int val; + + if (!arg) { + extended_usage++; + continue; + } + val = strtoul(arg, &p, 0); + if (*p != '\0' || (val < 3 && val != 0) || val > 200) { + fprintf(stderr, _("Invalid badness '%s'\n"), + arg); + extended_usage++; + continue; + } + ctx->inode_badness_threshold = val; } else if (strcmp(token, "journal_only") == 0) { if (arg) { extended_usage++; @@ -866,6 +882,7 @@ static void parse_extended_opts(e2fsck_t ctx, const char *opts) fputs(("\tshared=\n"), stderr); fputs(("\tclone=\n"), stderr); fputs(("\texpand_extra_isize\n"), stderr); + fputs(("\tinode_badness_threhold=(value)\n"), stderr); fputs("\toptimize_extents\n", stderr); fputs("\tno_optimize_extents\n", stderr); fputs("\tinode_count_fullmap\n", stderr); @@ -949,6 +966,7 @@ static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx) phys_mem_kb = get_memory_size() / 1024; ctx->readahead_kb = ~0ULL; + ctx->inode_badness_threshold = BADNESS_THRESHOLD; #ifdef HAVE_PTHREAD while ((c = getopt(argc, argv, "pam:nyrcC:B:dE:fvtFVM:b:I:j:P:l:L:N:SsDkz:")) != EOF) diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h index fcc8d2d..6d1c2e0 100644 --- a/lib/ext2fs/ext2fs.h +++ b/lib/ext2fs/ext2fs.h @@ -1533,6 +1533,7 @@ extern errcode_t ext2fs_calculate_summary_stats(ext2_filsys fs, int super_only); /* icount.c */ extern void ext2fs_free_icount(ext2_icount_t icount); +extern int ext2fs_icount_is_set(ext2_icount_t icount, ext2_ino_t ino); extern errcode_t ext2fs_create_icount_tdb(ext2_filsys fs, char *tdb_dir, int flags, ext2_icount_t *ret); extern errcode_t ext2fs_create_icount2(ext2_filsys fs, int flags, diff --git a/lib/ext2fs/icount.c b/lib/ext2fs/icount.c index 48665c7..093f51b 100644 --- a/lib/ext2fs/icount.c +++ b/lib/ext2fs/icount.c @@ -137,8 +137,9 @@ static errcode_t alloc_icount(ext2_filsys fs, int flags, ext2_icount_t *ret) &icount->multiple); if (retval) goto errout; - } else + } else { icount->multiple = 0; + } *ret = icount; return 0; @@ -508,6 +509,23 @@ static errcode_t get_inode_count(ext2_icount_t icount, ext2_ino_t ino, return 0; } +int ext2fs_icount_is_set(ext2_icount_t icount, ext2_ino_t ino) +{ + __u16 result; + + if (ext2fs_test_inode_bitmap2(icount->single, ino)) + return 1; + else if (icount->multiple) { + if (ext2fs_test_inode_bitmap2(icount->multiple, ino)) + return 1; + return 0; + } + ext2fs_icount_fetch(icount, ino, &result); + if (result) + return 1; + return 0; +} + errcode_t ext2fs_icount_validate(ext2_icount_t icount, FILE *out) { errcode_t ret = 0; diff --git a/tests/f_messy_inode/expect.1 b/tests/f_messy_inode/expect.1 index 708f1da..67cca25 100644 --- a/tests/f_messy_inode/expect.1 +++ b/tests/f_messy_inode/expect.1 @@ -20,19 +20,23 @@ Pass 2: Checking directory structure i_file_acl for inode 14 (/MAKEDEV) is 4294901760, should be zero. Clear? yes +Inode 14 is badly corrupt (badness value = 13). 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) Fix? yes -Free blocks count wrong for group #0 (68, counted=75). +Free blocks count wrong for group #0 (70, counted=77). Fix? yes -Free blocks count wrong (68, counted=75). +Free blocks count wrong (70, counted=77). 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 (3.6% non-contiguous), 23/100 blocks Exit status is 1 diff --git a/tests/f_messy_inode/expect.2 b/tests/f_messy_inode/expect.2 index 1fffb02..fb4e83a 100644 --- a/tests/f_messy_inode/expect.2 +++ b/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 diff --git a/tests/f_messy_inode/script b/tests/f_messy_inode/script new file mode 100644 index 0000000..3277356 --- /dev/null +++ b/tests/f_messy_inode/script @@ -0,0 +1,5 @@ +FSCK_OPT="-fy" +OUT1=$test_name.1.log +AFTER_CMD="sed -i -e 's/:[0-9]\{4\}:/::/' $OUT1" + +. $cmd_dir/run_e2fsck -- 1.8.3.1