X-Git-Url: https://git.whamcloud.com/?a=blobdiff_plain;f=e2fsck%2Fpass1.c;h=9a5dac7a5508029fef7b1f19130a0f1661cc0c6a;hb=1b977a0fad7be60d444228b5802683ff6b78baf2;hp=c6aae6ec8fb67bc00c2c38134bf5e5e8aa4b86c3;hpb=9b01faa8b24909fca2d220efbdc989285796f6c4;p=tools%2Fe2fsprogs.git diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c index c6aae6e..9a5dac7 100644 --- a/e2fsck/pass1.c +++ b/e2fsck/pass1.c @@ -274,16 +274,15 @@ static void check_ea_in_inode(e2fsck_t ctx, struct problem_context *pctx) struct ext2_super_block *sb = ctx->fs->super; struct ext2_inode_large *inode; struct ext2_ext_attr_entry *entry; - char *start, *end; + char *start; unsigned int storage_size, remain; - int problem = 0; + problem_t problem = 0; inode = (struct ext2_inode_large *) pctx->inode; storage_size = EXT2_INODE_SIZE(ctx->fs->super) - EXT2_GOOD_OLD_INODE_SIZE - inode->i_extra_isize; start = ((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE + inode->i_extra_isize + sizeof(__u32); - end = (char *) inode + EXT2_INODE_SIZE(ctx->fs->super); entry = (struct ext2_ext_attr_entry *) start; /* scan all entry's headers first */ @@ -308,7 +307,7 @@ static void check_ea_in_inode(e2fsck_t ctx, struct problem_context *pctx) remain -= EXT2_EXT_ATTR_SIZE(entry->e_name_len); /* check value size */ - if (entry->e_value_size == 0 || entry->e_value_size > remain) { + if (entry->e_value_size > remain) { pctx->num = entry->e_value_size; problem = PR_1_ATTR_VALUE_SIZE; goto fix; @@ -474,7 +473,7 @@ static void check_is_really_dir(e2fsck_t ctx, struct problem_context *pctx, /* read the first block */ ehandler_operation(_("reading directory block")); - retval = ext2fs_read_dir_block3(ctx->fs, blk, buf, 0); + retval = ext2fs_read_dir_block4(ctx->fs, blk, buf, 0, pctx->ino); ehandler_operation(0); if (retval) return; @@ -483,7 +482,7 @@ static void check_is_really_dir(e2fsck_t ctx, struct problem_context *pctx, retval = ext2fs_get_rec_len(ctx->fs, dirent, &rec_len); if (retval) return; - if (((dirent->name_len & 0xFF) != 1) || + if ((ext2fs_dirent_name_len(dirent) != 1) || (dirent->name[0] != '.') || (dirent->inode != pctx->ino) || (rec_len < 12) || @@ -495,7 +494,7 @@ static void check_is_really_dir(e2fsck_t ctx, struct problem_context *pctx, retval = ext2fs_get_rec_len(ctx->fs, dirent, &rec_len); if (retval) return; - if (((dirent->name_len & 0xFF) != 2) || + if ((ext2fs_dirent_name_len(dirent) != 2) || (dirent->name[0] != '.') || (dirent->name[1] != '.') || (rec_len < 12) || @@ -541,6 +540,40 @@ extern void e2fsck_setup_tdb_icount(e2fsck_t ctx, int flags, *ret = 0; } +static errcode_t recheck_bad_inode_checksum(ext2_filsys fs, ext2_ino_t ino, + e2fsck_t ctx, + struct problem_context *pctx) +{ + errcode_t retval; + struct ext2_inode_large inode; + + /* + * Reread inode. If we don't see checksum error, then this inode + * has been fixed elsewhere. + */ + retval = ext2fs_read_inode_full(fs, ino, (struct ext2_inode *)&inode, + sizeof(inode)); + if (retval && retval != EXT2_ET_INODE_CSUM_INVALID) + return retval; + if (!retval) + return 0; + + /* + * Checksum still doesn't match. That implies that the inode passes + * all the sanity checks, so maybe the checksum is simply corrupt. + * See if the user will go for fixing that. + */ + if (!fix_problem(ctx, PR_1_INODE_ONLY_CSUM_INVALID, pctx)) + return 0; + + retval = ext2fs_write_inode_full(fs, ino, (struct ext2_inode *)&inode, + sizeof(inode)); + if (retval) + return retval; + + return 0; +} + void e2fsck_pass1(e2fsck_t ctx) { int i; @@ -562,6 +595,7 @@ void e2fsck_pass1(e2fsck_t ctx) int imagic_fs, extent_fs; int busted_fs_time = 0; int inode_size; + int failed_csum = 0; init_resource_track(&rtrack, ctx->fs->io); clear_problem_context(&pctx); @@ -692,7 +726,8 @@ void e2fsck_pass1(e2fsck_t ctx) } block_buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize * 3, "block interate buffer"); - e2fsck_use_inode_shortcuts(ctx, 1); + if (EXT2_INODE_SIZE(fs->super) == EXT2_GOOD_OLD_INODE_SIZE) + e2fsck_use_inode_shortcuts(ctx, 1); old_op = ehandler_operation(_("opening inode scan")); pctx.errcode = ext2fs_open_inode_scan(fs, ctx->inode_buffer_blocks, &scan); @@ -717,11 +752,14 @@ void e2fsck_pass1(e2fsck_t ctx) busted_fs_time = 1; if ((fs->super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_MMP) && - !(fs->super->s_mmp_block <= fs->super->s_first_data_block || - fs->super->s_mmp_block >= fs->super->s_blocks_count)) + fs->super->s_mmp_block > fs->super->s_first_data_block && + fs->super->s_mmp_block < ext2fs_blocks_count(fs->super)) ext2fs_mark_block_bitmap2(ctx->block_found_map, fs->super->s_mmp_block); + /* Set up ctx->lost_and_found if possible */ + (void) e2fsck_get_lost_and_found(ctx, 0); + while (1) { if (ino % (fs->super->s_inodes_per_group * 4) == 1) { if (e2fsck_mmp_update(fs)) @@ -740,7 +778,8 @@ void e2fsck_pass1(e2fsck_t ctx) ext2fs_mark_inode_bitmap2(ctx->inode_used_map, ino); continue; } - if (pctx.errcode) { + if (pctx.errcode && + pctx.errcode != EXT2_ET_INODE_CSUM_INVALID) { fix_problem(ctx, PR_1_ISCAN_ERROR, &pctx); ctx->flags |= E2F_FLAG_ABORT; return; @@ -750,6 +789,14 @@ void e2fsck_pass1(e2fsck_t ctx) pctx.ino = ino; pctx.inode = inode; ctx->stashed_ino = ino; + + /* Clear corrupt inode? */ + if (pctx.errcode == EXT2_ET_INODE_CSUM_INVALID) { + if (fix_problem(ctx, PR_1_INODE_CSUM_INVALID, &pctx)) + goto clear_inode; + failed_csum = 1; + } + if (inode->i_links_count) { pctx.errcode = ext2fs_icount_store(ctx->inode_link_info, ino, inode->i_links_count); @@ -951,7 +998,7 @@ void e2fsck_pass1(e2fsck_t ctx) inode_size, "pass1"); } } else if (ino < EXT2_FIRST_INODE(fs->super)) { - int problem = 0; + problem_t problem = 0; ext2fs_mark_inode_bitmap2(ctx->inode_used_map, ino); if (ino == EXT2_BOOT_LOADER_INO) { @@ -1146,6 +1193,20 @@ void e2fsck_pass1(e2fsck_t ctx) } else check_blocks(ctx, &pctx, block_buf); + /* + * If the inode failed the checksum and the user didn't + * clear the inode, test the checksum again -- if it still + * fails, ask the user if the checksum should be corrected. + */ + if (failed_csum) { + pctx.errcode = recheck_bad_inode_checksum(fs, ino, ctx, + &pctx); + if (pctx.errcode) { + ctx->flags |= E2F_FLAG_ABORT; + return; + } + } + if (ctx->flags & E2F_FLAG_SIGNAL_MASK) return; @@ -1237,6 +1298,12 @@ endit: ext2fs_free_mem(&block_buf); ext2fs_free_mem(&inode); + /* + * The l+f inode may have been cleared, so zap it now and + * later passes will recalculate it if necessary + */ + ctx->lost_and_found = 0; + print_resource_track(ctx, _("Pass 1"), &rtrack, ctx->fs->io); } @@ -1432,6 +1499,16 @@ static _INLINE_ void mark_block_used(e2fsck_t ctx, blk64_t block) } } +static _INLINE_ void mark_blocks_used(e2fsck_t ctx, blk64_t block, + unsigned int num) +{ + if (ext2fs_test_block_bitmap_range2(ctx->block_found_map, block, num)) + ext2fs_mark_block_bitmap_range2(ctx->block_found_map, block, num); + else + while (num--) + mark_block_used(ctx, block++); +} + /* * Adjust the extended attribute block's reference counts at the end * of pass 1, either by subtracting out references for EA blocks that @@ -1456,7 +1533,8 @@ static void adjust_extattr_refcount(e2fsck_t ctx, ext2_refcount_t refcount, if ((blk = ea_refcount_intr_next(refcount, &count)) == 0) break; pctx.blk = blk; - pctx.errcode = ext2fs_read_ext_attr2(fs, blk, block_buf); + pctx.errcode = ext2fs_read_ext_attr3(fs, blk, block_buf, + pctx.ino); if (pctx.errcode) { fix_problem(ctx, PR_1_EXTATTR_READ_ABORT, &pctx); return; @@ -1467,8 +1545,9 @@ static void adjust_extattr_refcount(e2fsck_t ctx, ext2_refcount_t refcount, pctx.num = should_be; if (fix_problem(ctx, PR_1_EXTATTR_REFCOUNT, &pctx)) { header->h_refcount = should_be; - pctx.errcode = ext2fs_write_ext_attr2(fs, blk, - block_buf); + pctx.errcode = ext2fs_write_ext_attr3(fs, blk, + block_buf, + pctx.ino); if (pctx.errcode) { fix_problem(ctx, PR_1_EXTATTR_WRITE_ABORT, &pctx); @@ -1493,6 +1572,7 @@ static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx, struct ext2_ext_attr_entry *entry; int count; region_t region = 0; + int failed_csum = 0; blk = ext2fs_file_acl_block(fs, inode); if (blk == 0) @@ -1566,7 +1646,12 @@ static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx, * validate it */ pctx->blk = blk; - pctx->errcode = ext2fs_read_ext_attr2(fs, blk, block_buf); + pctx->errcode = ext2fs_read_ext_attr3(fs, blk, block_buf, pctx->ino); + if (pctx->errcode == EXT2_ET_EXT_ATTR_CSUM_INVALID) { + if (fix_problem(ctx, PR_1_EA_BLOCK_CSUM_INVALID, pctx)) + goto clear_extattr; + failed_csum = 1; + } if (pctx->errcode && fix_problem(ctx, PR_1_READ_EA_BLOCK, pctx)) goto clear_extattr; header = (struct ext2_ext_attr_header *) block_buf; @@ -1648,6 +1733,18 @@ static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx, } region_free(region); + /* + * We only get here if there was no other errors that were fixed. + * If there was a checksum fail, ask to correct it. + */ + if (failed_csum && + fix_problem(ctx, PR_1_EA_BLOCK_ONLY_CSUM_INVALID, pctx)) { + pctx->errcode = ext2fs_write_ext_attr3(fs, blk, block_buf, + pctx->ino); + if (pctx->errcode) + return 0; + } + count = header->h_refcount - 1; if (count) ea_refcount_store(ctx->refcount, blk, count); @@ -1751,16 +1848,18 @@ void e2fsck_clear_inode(e2fsck_t ctx, ext2_ino_t ino, static void scan_extent_node(e2fsck_t ctx, struct problem_context *pctx, struct process_block_struct *pb, - blk64_t start_block, + blk64_t start_block, blk64_t end_block, + blk64_t eof_block, ext2_extent_handle_t ehandle) { struct ext2fs_extent extent; - blk64_t blk; + blk64_t blk, last_lblk; e2_blkcnt_t blockcnt; unsigned int i; int is_dir, is_leaf; - errcode_t problem; + problem_t problem; struct ext2_extent_info info; + int failed_csum; pctx->errcode = ext2fs_extent_get_info(ehandle, &info); if (pctx->errcode) @@ -1768,30 +1867,66 @@ static void scan_extent_node(e2fsck_t ctx, struct problem_context *pctx, pctx->errcode = ext2fs_extent_get(ehandle, EXT2_EXTENT_FIRST_SIB, &extent); - while (!pctx->errcode && info.num_entries-- > 0) { + while ((pctx->errcode == 0 || + pctx->errcode == EXT2_ET_EXTENT_CSUM_INVALID) && + info.num_entries-- > 0) { + failed_csum = 0; is_leaf = extent.e_flags & EXT2_EXTENT_FLAGS_LEAF; is_dir = LINUX_S_ISDIR(pctx->inode->i_mode); + last_lblk = extent.e_lblk + extent.e_len - 1; problem = 0; + /* Ask to clear a corrupt extent block */ + if (pctx->errcode == EXT2_ET_EXTENT_CSUM_INVALID) { + pctx->blk = extent.e_pblk; + pctx->blk2 = extent.e_lblk; + pctx->num = extent.e_len; + problem = PR_1_EXTENT_CSUM_INVALID; + if (fix_problem(ctx, problem, pctx)) + goto fix_problem_now; + failed_csum = 1; + } + if (extent.e_pblk == 0 || extent.e_pblk < ctx->fs->super->s_first_data_block || extent.e_pblk >= ext2fs_blocks_count(ctx->fs->super)) problem = PR_1_EXTENT_BAD_START_BLK; else if (extent.e_lblk < start_block) problem = PR_1_OUT_OF_ORDER_EXTENTS; - else if (extent.e_len == 0) + else if ((end_block && last_lblk > end_block) && + (!(extent.e_flags & EXT2_EXTENT_FLAGS_UNINIT && + last_lblk > eof_block))) + problem = PR_1_EXTENT_END_OUT_OF_BOUNDS; + else if (is_leaf && extent.e_len == 0) problem = PR_1_EXTENT_LENGTH_ZERO; else if (is_leaf && (extent.e_pblk + extent.e_len) > ext2fs_blocks_count(ctx->fs->super)) problem = PR_1_EXTENT_ENDS_BEYOND; + else if (is_leaf && is_dir && + ((extent.e_lblk + extent.e_len) > + (1 << (21 - ctx->fs->super->s_log_block_size)))) + problem = PR_1_TOOBIG_DIR; + + /* Corrupt but passes checks? Ask to fix checksum. */ + if (failed_csum) { + pctx->blk = extent.e_pblk; + pctx->blk2 = extent.e_lblk; + pctx->num = extent.e_len; + problem = 0; + if (fix_problem(ctx, PR_1_EXTENT_ONLY_CSUM_INVALID, + pctx)) + ext2fs_extent_replace(ehandle, 0, &extent); + } if (problem) { - report_problem: +report_problem: pctx->blk = extent.e_pblk; pctx->blk2 = extent.e_lblk; pctx->num = extent.e_len; + pctx->blkcount = extent.e_lblk + extent.e_len; if (fix_problem(ctx, problem, pctx)) { +fix_problem_now: e2fsck_read_bitmaps(ctx); pctx->errcode = ext2fs_extent_delete(ehandle, 0); @@ -1799,6 +1934,7 @@ static void scan_extent_node(e2fsck_t ctx, struct problem_context *pctx, pctx->str = "ext2fs_extent_delete"; return; } + ext2fs_extent_fix_parents(ehandle); pctx->errcode = ext2fs_extent_get(ehandle, EXT2_EXTENT_CURRENT, &extent); @@ -1812,17 +1948,35 @@ static void scan_extent_node(e2fsck_t ctx, struct problem_context *pctx, } if (!is_leaf) { + blk64_t lblk = extent.e_lblk; + blk = extent.e_pblk; pctx->errcode = ext2fs_extent_get(ehandle, EXT2_EXTENT_DOWN, &extent); if (pctx->errcode) { pctx->str = "EXT2_EXTENT_DOWN"; problem = PR_1_EXTENT_HEADER_INVALID; - if (pctx->errcode == EXT2_ET_EXTENT_HEADER_BAD) + if (pctx->errcode == + EXT2_ET_EXTENT_HEADER_BAD || + pctx->errcode == + EXT2_ET_EXTENT_CSUM_INVALID) goto report_problem; return; } - scan_extent_node(ctx, pctx, pb, extent.e_lblk, ehandle); + /* The next extent should match this index's logical start */ + if (extent.e_lblk != lblk) { + struct ext2_extent_info e_info; + + ext2fs_extent_get_info(ehandle, &e_info); + pctx->blk = lblk; + pctx->blk2 = extent.e_lblk; + pctx->num = e_info.curr_level - 1; + problem = PR_1_EXTENT_INDEX_START_INVALID; + if (fix_problem(ctx, problem, pctx)) + ext2fs_extent_fix_parents(ehandle); + } + scan_extent_node(ctx, pctx, pb, extent.e_lblk, + last_lblk, eof_block, ehandle); if (pctx->errcode) return; pctx->errcode = ext2fs_extent_get(ehandle, @@ -1857,7 +2011,8 @@ static void scan_extent_node(e2fsck_t ctx, struct problem_context *pctx, } pb->fragmented = 1; } - while (is_dir && ++pb->last_db_block < extent.e_lblk) { + while (is_dir && (++pb->last_db_block < + (e2_blkcnt_t) extent.e_lblk)) { pctx->errcode = ext2fs_add_dir_block2(ctx->fs->dblist, pb->ino, 0, pb->last_db_block); @@ -1867,15 +2022,19 @@ static void scan_extent_node(e2fsck_t ctx, struct problem_context *pctx, goto failed_add_dir_block; } } + if (!ctx->fs->cluster_ratio_bits) { + mark_blocks_used(ctx, extent.e_pblk, extent.e_len); + pb->num_blocks += extent.e_len; + } for (blk = extent.e_pblk, blockcnt = extent.e_lblk, i = 0; i < extent.e_len; blk++, blockcnt++, i++) { - if (!(ctx->fs->cluster_ratio_bits && - pb->previous_block && + if (ctx->fs->cluster_ratio_bits && + !(pb->previous_block && (EXT2FS_B2C(ctx->fs, blk) == EXT2FS_B2C(ctx->fs, pb->previous_block)) && (blk & EXT2FS_CLUSTER_MASK(ctx->fs)) == - (blockcnt & EXT2FS_CLUSTER_MASK(ctx->fs)))) { + ((unsigned) blockcnt & EXT2FS_CLUSTER_MASK(ctx->fs)))) { mark_block_used(ctx, blk); pb->num_blocks++; } @@ -1898,10 +2057,10 @@ static void scan_extent_node(e2fsck_t ctx, struct problem_context *pctx, if (is_dir && extent.e_len > 0) pb->last_db_block = blockcnt - 1; pb->previous_block = extent.e_pblk + extent.e_len - 1; - start_block = pb->last_block = extent.e_lblk + extent.e_len - 1; + start_block = pb->last_block = last_lblk; if (is_leaf && !is_dir && !(extent.e_flags & EXT2_EXTENT_FLAGS_UNINIT)) - pb->last_init_lblock = extent.e_lblk + extent.e_len - 1; + pb->last_init_lblock = last_lblk; next: pctx->errcode = ext2fs_extent_get(ehandle, EXT2_EXTENT_NEXT_SIB, @@ -1920,6 +2079,7 @@ static void check_blocks_extents(e2fsck_t ctx, struct problem_context *pctx, ext2_filsys fs = ctx->fs; ext2_ino_t ino = pctx->ino; errcode_t retval; + blk64_t eof_lblk; pctx->errcode = ext2fs_extent_open2(fs, ino, inode, &ehandle); if (pctx->errcode) { @@ -1937,7 +2097,9 @@ static void check_blocks_extents(e2fsck_t ctx, struct problem_context *pctx, ctx->extent_depth_count[info.max_depth]++; } - scan_extent_node(ctx, pctx, pb, 0, ehandle); + eof_lblk = ((EXT2_I_SIZE(inode) + fs->blocksize - 1) >> + EXT2_BLOCK_SIZE_BITS(fs->super)) - 1; + scan_extent_node(ctx, pctx, pb, 0, 0, eof_lblk, ehandle); if (pctx->errcode && fix_problem(ctx, PR_1_EXTENT_ITERATE_FAILURE, pctx)) { pb->num_blocks = 0; @@ -1960,7 +2122,7 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx, struct process_block_struct pb; ext2_ino_t ino = pctx->ino; struct ext2_inode *inode = pctx->inode; - int bad_size = 0; + unsigned bad_size = 0; int dirty_inode = 0; int extent_fs; __u64 size; @@ -2136,9 +2298,10 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx, } if (ctx->dirs_to_hash && pb.is_dir && + !(ctx->lost_and_found && ctx->lost_and_found == ino) && !(inode->i_flags & EXT2_INDEX_FL) && ((inode->i_size / fs->blocksize) >= 3)) - ext2fs_u32_list_add(ctx->dirs_to_hash, ino); + e2fsck_rehash_dir_later(ctx, ino); out: if (dirty_inode) @@ -2211,7 +2374,7 @@ static int process_block(ext2_filsys fs, struct problem_context *pctx; blk64_t blk = *block_nr; int ret_code = 0; - int problem = 0; + problem_t problem = 0; e2fsck_t ctx; p = (struct process_block_struct *) priv_data; @@ -2324,7 +2487,7 @@ static int process_block(ext2_filsys fs, (EXT2FS_B2C(ctx->fs, blk) == EXT2FS_B2C(ctx->fs, p->previous_block)) && (blk & EXT2FS_CLUSTER_MASK(ctx->fs)) == - (blockcnt & EXT2FS_CLUSTER_MASK(ctx->fs)))) { + ((unsigned) blockcnt & EXT2FS_CLUSTER_MASK(ctx->fs)))) { mark_block_used(ctx, blk); p->num_blocks++; } @@ -2522,14 +2685,16 @@ static int process_bad_block(ext2_filsys fs, return 0; } -static void new_table_block(e2fsck_t ctx, blk_t first_block, int group, +static void new_table_block(e2fsck_t ctx, blk64_t first_block, dgrp_t group, const char *name, int num, blk64_t *new_block) { ext2_filsys fs = ctx->fs; dgrp_t last_grp; blk64_t old_block = *new_block; blk64_t last_block; - int i, is_flexbg, flexbg, flexbg_size; + dgrp_t flexbg; + unsigned flexbg_size; + int i, is_flexbg; char *buf; struct problem_context pctx; @@ -2655,7 +2820,7 @@ static void mark_table_blocks(e2fsck_t ctx) ext2_filsys fs = ctx->fs; blk64_t b; dgrp_t i; - int j; + unsigned int j; struct problem_context pctx; clear_problem_context(&pctx); @@ -2822,11 +2987,11 @@ static void e2fsck_block_alloc_stats(ext2_filsys fs, blk64_t blk, int inuse) } } -void e2fsck_use_inode_shortcuts(e2fsck_t ctx, int bool) +void e2fsck_use_inode_shortcuts(e2fsck_t ctx, int use_shortcuts) { ext2_filsys fs = ctx->fs; - if (bool) { + if (use_shortcuts) { fs->get_blocks = pass1_get_blocks; fs->check_directory = pass1_check_directory; fs->read_inode = pass1_read_inode;