X-Git-Url: https://git.whamcloud.com/?a=blobdiff_plain;f=e2fsck%2Fpass1.c;h=922de3dadb62e24d87e58e53aa7bfb4b34345d47;hb=7d6322473386e6b0f3586bb3d3ce6a97fb1bfb3c;hp=ed85431daf63bf0d032188ed29acbf80f4094779;hpb=f8e4f457d30ccff19833ddaf6514632f054a4418;p=tools%2Fe2fsprogs.git diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c index ed85431..922de3d 100644 --- a/e2fsck/pass1.c +++ b/e2fsck/pass1.c @@ -23,6 +23,7 @@ * - A bitmap of which inodes have bad fields. (inode_bad_map) * - 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) * - A bitmap of which blocks are in use. (block_found_map) * - A bitmap of which blocks are in use by two inodes (block_dup_map) * - The data blocks of the directory inodes. (dir_map) @@ -46,9 +47,15 @@ #ifdef HAVE_ERRNO_H #include #endif +#include +#ifdef HAVE_PTHREAD +#include +#endif #include "e2fsck.h" #include +/* todo remove this finally */ +#include #include #include "problem.h" @@ -79,8 +86,8 @@ 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 void process_inodes(e2fsck_t ctx, char *block_buf); static EXT2_QSORT_TYPE process_inode_cmp(const void *a, const void *b); static errcode_t scan_callback(ext2_filsys fs, ext2_inode_scan scan, dgrp_t group, void * priv_data); @@ -115,15 +122,15 @@ struct process_inode_block { }; struct scan_callback_struct { - e2fsck_t ctx; - char *block_buf; + e2fsck_t ctx; + char *block_buf; + struct process_inode_block *inodes_to_process; + int *process_inode_count; }; -/* - * For the inodes to process list. - */ -static struct process_inode_block *inodes_to_process; -static int process_inode_count; +static void process_inodes(e2fsck_t ctx, char *block_buf, + struct process_inode_block *inodes_to_process, + int *process_inode_count); static __u64 ext2_max_sizes[EXT2_MAX_BLOCK_LOG_SIZE - EXT2_MIN_BLOCK_LOG_SIZE + 1]; @@ -1144,10 +1151,24 @@ static int quota_inum_is_reserved(ext2_filsys fs, ext2_ino_t ino) return 0; } -void e2fsck_pass1(e2fsck_t ctx) +static int e2fsck_should_abort(e2fsck_t ctx) +{ + e2fsck_t global_ctx; + + if (ctx->flags & E2F_FLAG_SIGNAL_MASK) + return 1; + + if (ctx->global_ctx) { + global_ctx = ctx->global_ctx; + if (global_ctx->flags & E2F_FLAG_SIGNAL_MASK) + return 1; + } + return 0; +} + +void e2fsck_pass1_run(e2fsck_t ctx) { int i; - __u64 max_sizes; ext2_filsys fs = ctx->fs; ext2_ino_t ino = 0; struct ext2_inode *inode = NULL; @@ -1170,6 +1191,8 @@ void e2fsck_pass1(e2fsck_t ctx) ext2_ino_t ino_threshold = 0; dgrp_t ra_group = 0; struct ea_quota ea_ibody_quota; + struct process_inode_block *inodes_to_process; + int process_inode_count; init_resource_track(&rtrack, ctx->fs->io); clear_problem_context(&pctx); @@ -1194,17 +1217,6 @@ void e2fsck_pass1(e2fsck_t ctx) mtrace_print("Pass 1"); #endif -#define EXT2_BPP(bits) (1ULL << ((bits) - 2)) - - for (i = EXT2_MIN_BLOCK_LOG_SIZE; i <= EXT2_MAX_BLOCK_LOG_SIZE; i++) { - max_sizes = EXT2_NDIR_BLOCKS + EXT2_BPP(i); - max_sizes = max_sizes + EXT2_BPP(i) * EXT2_BPP(i); - max_sizes = max_sizes + EXT2_BPP(i) * EXT2_BPP(i) * EXT2_BPP(i); - max_sizes = (max_sizes * (1UL << i)); - ext2_max_sizes[i - EXT2_MIN_BLOCK_LOG_SIZE] = max_sizes; - } -#undef EXT2_BPP - imagic_fs = ext2fs_has_feature_imagic_inodes(sb); extent_fs = ext2fs_has_feature_extents(sb); inlinedata_fs = ext2fs_has_feature_inline_data(sb); @@ -1225,6 +1237,7 @@ void e2fsck_pass1(e2fsck_t ctx) } pctx.errcode = e2fsck_allocate_inode_bitmap(fs, _("directory inode map"), + ctx->global_ctx ? EXT2FS_BMAP64_RBTREE : EXT2FS_BMAP64_AUTODIR, "inode_dir_map", &ctx->inode_dir_map); if (pctx.errcode) { @@ -1260,6 +1273,20 @@ void e2fsck_pass1(e2fsck_t ctx) ctx->flags |= E2F_FLAG_ABORT; return; } + if (casefold_fs) { + pctx.errcode = + e2fsck_allocate_inode_bitmap(fs, + _("inode casefold map"), + EXT2FS_BMAP64_RBTREE, + "inode_casefold_map", + &ctx->inode_casefold_map); + if (pctx.errcode) { + pctx.num = 1; + fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx); + ctx->flags |= E2F_FLAG_ABORT; + return; + } + } pctx.errcode = e2fsck_setup_icount(ctx, "inode_link_info", 0, NULL, &ctx->inode_link_info); if (pctx.errcode) { @@ -1328,6 +1355,8 @@ void e2fsck_pass1(e2fsck_t ctx) ctx->stashed_inode = inode; scan_struct.ctx = ctx; scan_struct.block_buf = block_buf; + scan_struct.inodes_to_process = inodes_to_process; + scan_struct.process_inode_count = &process_inode_count; ext2fs_set_inode_callback(scan, scan_callback, &scan_struct); if (ctx->progress && ((ctx->progress)(ctx, 1, 0, ctx->fs->group_desc_count))) @@ -1349,6 +1378,23 @@ void e2fsck_pass1(e2fsck_t ctx) /* Set up ctx->lost_and_found if possible */ (void) e2fsck_get_lost_and_found(ctx, 0); +#ifdef HAVE_PTHREAD + if (ctx->global_ctx) { + if (ctx->options & E2F_OPT_DEBUG && + ctx->options & E2F_OPT_MULTITHREAD) + fprintf(stderr, "thread %d jumping to group %d\n", + ctx->thread_info.et_thread_index, + ctx->thread_info.et_group_start); + pctx.errcode = ext2fs_inode_scan_goto_blockgroup(scan, + ctx->thread_info.et_group_start); + if (pctx.errcode) { + fix_problem(ctx, PR_1_PASS_HEADER, &pctx); + ctx->flags |= E2F_FLAG_ABORT; + goto endit; + } + } +#endif + while (1) { if (ino % (fs->super->s_inodes_per_group * 4) == 1) { if (e2fsck_mmp_update(fs)) @@ -1360,7 +1406,7 @@ void e2fsck_pass1(e2fsck_t ctx) if (ino > ino_threshold) pass1_readahead(ctx, &ra_group, &ino_threshold); ehandler_operation(old_op); - if (ctx->flags & E2F_FLAG_SIGNAL_MASK) + if (e2fsck_should_abort(ctx)) goto endit; if (pctx.errcode == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE) { /* @@ -1382,17 +1428,9 @@ void e2fsck_pass1(e2fsck_t ctx) fix_problem(ctx, PR_1_ISCAN_ERROR, &pctx); ctx->flags |= E2F_FLAG_ABORT; - goto endit; - } - err = ext2fs_inode_scan_goto_blockgroup(scan, - 0); - if (err) { - fix_problem(ctx, PR_1_ISCAN_ERROR, - &pctx); - ctx->flags |= E2F_FLAG_ABORT; - goto endit; - } - continue; + } else + ctx->flags |= E2F_FLAG_RESTART; + goto endit; } if (!ctx->inode_bb_map) alloc_bb_map(ctx); @@ -1400,6 +1438,8 @@ void e2fsck_pass1(e2fsck_t ctx) ext2fs_mark_inode_bitmap2(ctx->inode_used_map, ino); continue; } + if (pctx.errcode == EXT2_ET_SCAN_FINISHED) + break; if (pctx.errcode && pctx.errcode != EXT2_ET_INODE_CSUM_INVALID && pctx.errcode != EXT2_ET_INODE_IS_GARBAGE) { @@ -1409,6 +1449,10 @@ void e2fsck_pass1(e2fsck_t ctx) } if (!ino) break; +#ifdef HAVE_PTHREAD + if (ctx->global_ctx) + ctx->thread_info.et_inode_number++; +#endif pctx.ino = ino; pctx.inode = inode; ctx->stashed_ino = ino; @@ -1878,10 +1922,15 @@ void e2fsck_pass1(e2fsck_t ctx) add_encrypted_file(ctx, &pctx) < 0) goto clear_inode; + if (casefold_fs && inode->i_flags & EXT4_CASEFOLD_FL) + ext2fs_mark_inode_bitmap2(ctx->inode_casefold_map, ino); + if (LINUX_S_ISDIR(inode->i_mode)) { ext2fs_mark_inode_bitmap2(ctx->inode_dir_map, ino); e2fsck_add_dir_info(ctx, ino, 0); ctx->fs_directory_count++; + if (inode->i_flags & EXT4_CASEFOLD_FL) + add_casefolded_dir(ctx, ino); } else if (LINUX_S_ISREG (inode->i_mode)) { ext2fs_mark_inode_bitmap2(ctx->inode_reg_map, ino); ctx->fs_regular_count++; @@ -1961,13 +2010,15 @@ void e2fsck_pass1(e2fsck_t ctx) goto endit; if (process_inode_count >= ctx->process_inode_size) { - process_inodes(ctx, block_buf); + process_inodes(ctx, block_buf, inodes_to_process, + &process_inode_count); - if (ctx->flags & E2F_FLAG_SIGNAL_MASK) + if (e2fsck_should_abort(ctx)) goto endit; } } - process_inodes(ctx, block_buf); + process_inodes(ctx, block_buf, inodes_to_process, + &process_inode_count); ext2fs_close_inode_scan(scan); scan = NULL; @@ -2045,6 +2096,19 @@ void e2fsck_pass1(e2fsck_t ctx) goto endit; } + if (ctx->large_dirs && !ext2fs_has_feature_largedir(fs->super)) { + if (fix_problem(ctx, PR_2_FEATURE_LARGE_DIRS, &pctx)) { + ext2fs_set_feature_largedir(fs->super); + fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY; + ext2fs_mark_super_dirty(fs); + } + if (fs->super->s_rev_level == EXT2_GOOD_OLD_REV && + fix_problem(ctx, PR_1_FS_REV_LEVEL, &pctx)) { + ext2fs_update_dynamic_rev(fs); + ext2fs_mark_super_dirty(fs); + } + } + if (ctx->block_dup_map) { if (ctx->options & E2F_OPT_PREEN) { clear_problem_context(&pctx); @@ -2076,6 +2140,827 @@ endit: else ctx->invalid_bitmaps++; } + +static void init_ext2_max_sizes() +{ + int i; + __u64 max_sizes; + + /* + * Init ext2_max_sizes which will be immutable and shared between + * threads + */ +#define EXT2_BPP(bits) (1ULL << ((bits) - 2)) + + for (i = EXT2_MIN_BLOCK_LOG_SIZE; i <= EXT2_MAX_BLOCK_LOG_SIZE; i++) { + max_sizes = EXT2_NDIR_BLOCKS + EXT2_BPP(i); + max_sizes = max_sizes + EXT2_BPP(i) * EXT2_BPP(i); + max_sizes = max_sizes + EXT2_BPP(i) * EXT2_BPP(i) * EXT2_BPP(i); + max_sizes = (max_sizes * (1UL << i)); + ext2_max_sizes[i - EXT2_MIN_BLOCK_LOG_SIZE] = max_sizes; + } +#undef EXT2_BPP +} + +#ifdef HAVE_PTHREAD +static errcode_t e2fsck_pass1_copy_bitmap(ext2_filsys fs, ext2fs_generic_bitmap *src, + ext2fs_generic_bitmap *dest) +{ + errcode_t ret; + + ret = ext2fs_copy_bitmap(*src, dest); + if (ret) + return ret; + + (*dest)->fs = fs; + + return 0; +} + +static void e2fsck_pass1_free_bitmap(ext2fs_generic_bitmap *bitmap) +{ + if (*bitmap) { + ext2fs_free_generic_bmap(*bitmap); + *bitmap = NULL; + } + +} + +static errcode_t e2fsck_pass1_merge_bitmap(ext2_filsys fs, ext2fs_generic_bitmap *src, + ext2fs_generic_bitmap *dest) +{ + errcode_t ret = 0; + + if (*src) { + if (*dest == NULL) { + *dest = *src; + *src = NULL; + } else { + ret = ext2fs_merge_bitmap(*src, *dest, NULL, NULL); + if (ret) + return ret; + } + (*dest)->fs = fs; + } + + return 0; +} + +static errcode_t e2fsck_pass1_copy_fs(ext2_filsys dest, e2fsck_t src_context, + ext2_filsys src) +{ + errcode_t retval; + + memcpy(dest, src, sizeof(struct struct_ext2_filsys)); + dest->inode_map = NULL; + dest->block_map = NULL; + dest->badblocks = NULL; + if (dest->dblist) + dest->dblist->fs = dest; + if (src->block_map) { + retval = e2fsck_pass1_copy_bitmap(dest, &src->block_map, + &dest->block_map); + if (retval) + return retval; + } + if (src->inode_map) { + retval = e2fsck_pass1_copy_bitmap(dest, &src->inode_map, + &dest->inode_map); + if (retval) + return retval; + } + + if (src->badblocks) { + retval = ext2fs_badblocks_copy(src->badblocks, + &dest->badblocks); + if (retval) + return retval; + } + + /* disable it for now */ + src_context->openfs_flags &= ~EXT2_FLAG_EXCLUSIVE; + retval = ext2fs_open_channel(dest, src_context->io_options, + src_context->io_manager, + src_context->openfs_flags, + src->io->block_size); + if (retval) + return retval; + + /* Block size might not be default */ + io_channel_set_blksize(dest->io, src->io->block_size); + ehandler_init(dest->io); + + assert(dest->io->magic == src->io->magic); + assert(dest->io->manager == src->io->manager); + assert(strcmp(dest->io->name, src->io->name) == 0); + assert(dest->io->block_size == src->io->block_size); + assert(dest->io->read_error == src->io->read_error); + assert(dest->io->write_error == src->io->write_error); + assert(dest->io->refcount == src->io->refcount); + assert(dest->io->flags == src->io->flags); + assert(dest->io->app_data == dest); + assert(src->io->app_data == src); + assert(dest->io->align == src->io->align); + + /* The data should be written to disk immediately */ + dest->io->flags |= CHANNEL_FLAGS_WRITETHROUGH; + /* icache will be rebuilt if needed, so do not copy from @src */ + src->icache = NULL; + return 0; +} + +static int e2fsck_pass1_merge_fs(ext2_filsys dest, ext2_filsys src) +{ + struct ext2_inode_cache *icache = dest->icache; + errcode_t retval = 0; + io_channel dest_io; + io_channel dest_image_io; + ext2fs_inode_bitmap inode_map; + ext2fs_block_bitmap block_map; + ext2_badblocks_list badblocks; + ext2_dblist dblist; + int flags; + + dest_io = dest->io; + dest_image_io = dest->image_io; + inode_map = dest->inode_map; + block_map = dest->block_map; + badblocks = dest->badblocks; + dblist = dest->dblist; + flags = dest->flags; + + memcpy(dest, src, sizeof(struct struct_ext2_filsys)); + dest->io = dest_io; + dest->image_io = dest_image_io; + dest->icache = icache; + dest->inode_map = inode_map; + dest->block_map = block_map; + dest->badblocks = badblocks; + dest->dblist = dblist; + if (dest->dblist) + dest->dblist->fs = dest; + dest->flags = src->flags | flags; + if (!(src->flags & EXT2_FLAG_VALID) || !(flags & EXT2_FLAG_VALID)) + ext2fs_unmark_valid(dest); + + if (src->icache) { + ext2fs_free_inode_cache(src->icache); + src->icache = NULL; + } + + retval = e2fsck_pass1_merge_bitmap(dest, &src->inode_map, + &dest->inode_map); + if (retval) + goto out; + + retval = e2fsck_pass1_merge_bitmap(dest, &src->block_map, + &dest->block_map); + if (retval) + goto out; + + if (src->dblist) { + if (dest->dblist) { + retval = ext2fs_merge_dblist(src->dblist, + dest->dblist); + if (retval) + goto out; + } else { + dest->dblist = src->dblist; + dest->dblist->fs = dest; + src->dblist = NULL; + } + } + + if (src->badblocks) { + if (dest->badblocks == NULL) + retval = ext2fs_badblocks_copy(src->badblocks, + &dest->badblocks); + else + retval = ext2fs_badblocks_merge(src->badblocks, + dest->badblocks); + } +out: + io_channel_close(src->io); + if (src->inode_map) + ext2fs_free_generic_bmap(src->inode_map); + if (src->block_map) + ext2fs_free_generic_bmap(src->block_map); + if (src->badblocks) + ext2fs_badblocks_list_free(src->badblocks); + if (src->dblist) + ext2fs_free_dblist(src->dblist); + + return retval; +} + +static errcode_t e2fsck_pass1_thread_prepare(e2fsck_t global_ctx, e2fsck_t *thread_ctx, + int thread_index, int num_threads) +{ + errcode_t retval; + e2fsck_t thread_context; + ext2_filsys thread_fs; + ext2_filsys global_fs = global_ctx->fs; + struct e2fsck_thread *tinfo; + dgrp_t average_group; + + assert(global_ctx->inode_used_map == NULL); + assert(global_ctx->inode_dir_map == NULL); + assert(global_ctx->inode_bb_map == NULL); + assert(global_ctx->inode_imagic_map == NULL); + assert(global_ctx->inode_reg_map == NULL); + assert(global_ctx->inodes_to_rebuild == NULL); + + assert(global_ctx->block_found_map == NULL); + assert(global_ctx->block_dup_map == NULL); + assert(global_ctx->block_ea_map == NULL); + assert(global_ctx->block_metadata_map == NULL); + assert(global_ctx->fs->dblist == NULL); + + retval = ext2fs_get_mem(sizeof(struct e2fsck_struct), &thread_context); + if (retval) { + com_err(global_ctx->program_name, retval, "while allocating memory"); + return retval; + } + memcpy(thread_context, global_ctx, sizeof(struct e2fsck_struct)); + thread_context->global_ctx = global_ctx; + + retval = ext2fs_get_mem(sizeof(struct struct_ext2_filsys), &thread_fs); + if (retval) { + com_err(global_ctx->program_name, retval, "while allocating memory"); + goto out_context; + } + + io_channel_flush_cleanup(global_fs->io); + retval = e2fsck_pass1_copy_fs(thread_fs, global_ctx, global_fs); + if (retval) { + com_err(global_ctx->program_name, retval, "while copying fs"); + goto out_fs; + } + thread_fs->priv_data = thread_context; + + thread_context->thread_info.et_thread_index = thread_index; + set_up_logging(thread_context); + + /* + * Distribute work to multiple threads: + * Each thread work on fs->group_desc_count / nthread groups. + */ + tinfo = &thread_context->thread_info; + average_group = thread_fs->group_desc_count / num_threads; + if (average_group == 0) + average_group = 1; + tinfo->et_group_start = average_group * thread_index; + if (thread_index == num_threads - 1) + tinfo->et_group_end = thread_fs->group_desc_count; + else + tinfo->et_group_end = average_group * (thread_index + 1); + tinfo->et_group_next = tinfo->et_group_start; + tinfo->et_inode_number = 0; + tinfo->et_log_buf[0] = '\0'; + tinfo->et_log_length = 0; + if (thread_context->options & E2F_OPT_MULTITHREAD) + log_out(thread_context, _("Scan group range [%d, %d)\n"), + tinfo->et_group_start, tinfo->et_group_end); + thread_context->fs = thread_fs; + *thread_ctx = thread_context; + return 0; +out_fs: + ext2fs_free_mem(&thread_fs); +out_context: + ext2fs_free_mem(&thread_context); + return retval; +} + +static void e2fsck_pass1_merge_dir_info(e2fsck_t global_ctx, e2fsck_t thread_ctx) +{ + if (thread_ctx->dir_info == NULL) + return; + + if (global_ctx->dir_info == NULL) { + global_ctx->dir_info = thread_ctx->dir_info; + thread_ctx->dir_info = NULL; + return; + } + + e2fsck_merge_dir_info(global_ctx, thread_ctx->dir_info, + global_ctx->dir_info); +} + +static void e2fsck_pass1_merge_dx_dir(e2fsck_t global_ctx, e2fsck_t thread_ctx) +{ + if (thread_ctx->dx_dir_info == NULL) + return; + + if (global_ctx->dx_dir_info == NULL) { + global_ctx->dx_dir_info = thread_ctx->dx_dir_info; + global_ctx->dx_dir_info_size = thread_ctx->dx_dir_info_size; + global_ctx->dx_dir_info_count = thread_ctx->dx_dir_info_count; + thread_ctx->dx_dir_info = NULL; + return; + } + + e2fsck_merge_dx_dir(global_ctx, thread_ctx); +} + +static inline errcode_t +e2fsck_pass1_merge_icount(ext2_icount_t *dest_icount, + ext2_icount_t *src_icount) +{ + if (*src_icount) { + if (*dest_icount == NULL) { + *dest_icount = *src_icount; + *src_icount = NULL; + } else { + errcode_t ret; + + ret = ext2fs_icount_merge(*src_icount, + *dest_icount); + if (ret) + return ret; + } + } + + return 0; +} + +static errcode_t e2fsck_pass1_merge_icounts(e2fsck_t global_ctx, e2fsck_t thread_ctx) +{ + errcode_t ret; + + ret = e2fsck_pass1_merge_icount(&global_ctx->inode_count, + &thread_ctx->inode_count); + if (ret) + return ret; + ret = e2fsck_pass1_merge_icount(&global_ctx->inode_link_info, + &thread_ctx->inode_link_info); + + return ret; +} + +static errcode_t e2fsck_pass1_merge_dirs_to_hash(e2fsck_t global_ctx, + e2fsck_t thread_ctx) +{ + errcode_t retval = 0; + + if (!thread_ctx->dirs_to_hash) + return 0; + + if (!global_ctx->dirs_to_hash) + retval = ext2fs_badblocks_copy(thread_ctx->dirs_to_hash, + &global_ctx->dirs_to_hash); + else + retval = ext2fs_badblocks_merge(thread_ctx->dirs_to_hash, + global_ctx->dirs_to_hash); + + return retval; +} + + +static int e2fsck_pass1_thread_join_one(e2fsck_t global_ctx, e2fsck_t thread_ctx) +{ + errcode_t retval; + int flags = global_ctx->flags; + ext2_filsys thread_fs = thread_ctx->fs; + ext2_filsys global_fs = global_ctx->fs; + FILE *global_logf = global_ctx->logf; + FILE *global_problem_logf = global_ctx->problem_logf; + ext2fs_inode_bitmap inode_bad_map = global_ctx->inode_bad_map; + struct dir_info_db *dir_info = global_ctx->dir_info; + struct dx_dir_info *dx_dir_info = global_ctx->dx_dir_info; + ext2fs_inode_bitmap inode_used_map = global_ctx->inode_used_map; + ext2fs_inode_bitmap inode_dir_map = global_ctx->inode_dir_map; + ext2fs_inode_bitmap inode_bb_map = global_ctx->inode_bb_map; + ext2fs_inode_bitmap inode_imagic_map = global_ctx->inode_imagic_map; + ext2fs_inode_bitmap inode_reg_map = global_ctx->inode_reg_map; + ext2fs_block_bitmap block_found_map = global_ctx->block_found_map; + ext2fs_block_bitmap block_dup_map = global_ctx->block_dup_map; + ext2fs_block_bitmap block_ea_map = global_ctx->block_ea_map; + ext2fs_block_bitmap block_metadata_map = global_ctx->block_metadata_map; + ext2fs_block_bitmap inodes_to_rebuild = global_ctx->inodes_to_rebuild; + ext2_icount_t inode_count = global_ctx->inode_count; + ext2_icount_t inode_link_info = global_ctx->inode_link_info; + __u32 fs_directory_count = global_ctx->fs_directory_count; + __u32 fs_regular_count = global_ctx->fs_regular_count; + __u32 fs_blockdev_count = global_ctx->fs_blockdev_count; + __u32 fs_chardev_count = global_ctx->fs_chardev_count; + __u32 fs_links_count = global_ctx->fs_links_count; + __u32 fs_symlinks_count = global_ctx->fs_symlinks_count; + __u32 fs_fast_symlinks_count = global_ctx->fs_fast_symlinks_count; + __u32 fs_fifo_count = global_ctx->fs_fifo_count; + __u32 fs_total_count = global_ctx->fs_total_count; + __u32 fs_badblocks_count = global_ctx->fs_badblocks_count; + __u32 fs_sockets_count = global_ctx->fs_sockets_count; + __u32 fs_ind_count = global_ctx->fs_ind_count; + __u32 fs_dind_count = global_ctx->fs_dind_count; + __u32 fs_tind_count = global_ctx->fs_tind_count; + __u32 fs_fragmented = global_ctx->fs_fragmented; + __u32 fs_fragmented_dir = global_ctx->fs_fragmented_dir; + __u32 large_files = global_ctx->large_files; + ext2_ino_t dx_dir_info_size = global_ctx->dx_dir_info_size; + ext2_ino_t dx_dir_info_count = global_ctx->dx_dir_info_count; + ext2_u32_list dirs_to_hash = global_ctx->dirs_to_hash; + +#ifdef HAVE_SETJMP_H + jmp_buf old_jmp; + + memcpy(old_jmp, global_ctx->abort_loc, sizeof(jmp_buf)); +#endif + memcpy(global_ctx, thread_ctx, sizeof(struct e2fsck_struct)); +#ifdef HAVE_SETJMP_H + memcpy(global_ctx->abort_loc, old_jmp, sizeof(jmp_buf)); +#endif + + global_ctx->inode_used_map = inode_used_map; + global_ctx->inode_bad_map = inode_bad_map; + global_ctx->inode_dir_map = inode_dir_map; + global_ctx->inode_bb_map = inode_bb_map; + global_ctx->inode_imagic_map = inode_imagic_map; + global_ctx->inodes_to_rebuild = inodes_to_rebuild; + global_ctx->inode_reg_map = inode_reg_map; + global_ctx->block_found_map = block_found_map; + global_ctx->block_dup_map = block_dup_map; + global_ctx->block_ea_map = block_ea_map; + global_ctx->block_metadata_map = block_metadata_map; + global_ctx->dir_info = dir_info; + e2fsck_pass1_merge_dir_info(global_ctx, thread_ctx); + global_ctx->dx_dir_info = dx_dir_info; + global_ctx->dx_dir_info_count = dx_dir_info_count; + global_ctx->dx_dir_info_size = dx_dir_info_size; + e2fsck_pass1_merge_dx_dir(global_ctx, thread_ctx); + global_ctx->inode_count = inode_count; + global_ctx->inode_link_info = inode_link_info; + global_ctx->fs_directory_count += fs_directory_count; + global_ctx->fs_regular_count += fs_regular_count; + global_ctx->fs_blockdev_count += fs_blockdev_count; + global_ctx->fs_chardev_count += fs_chardev_count; + global_ctx->fs_links_count += fs_links_count; + global_ctx->fs_symlinks_count += fs_symlinks_count; + global_ctx->fs_fast_symlinks_count += fs_fast_symlinks_count; + global_ctx->fs_fifo_count += fs_fifo_count; + global_ctx->fs_total_count += fs_total_count; + global_ctx->fs_badblocks_count += fs_badblocks_count; + global_ctx->fs_sockets_count += fs_sockets_count; + global_ctx->fs_ind_count += fs_ind_count; + global_ctx->fs_dind_count += fs_dind_count; + global_ctx->fs_tind_count += fs_tind_count; + global_ctx->fs_fragmented += fs_fragmented; + global_ctx->fs_fragmented_dir += fs_fragmented_dir; + global_ctx->large_files += large_files; + + /* Keep the global singal flags*/ + global_ctx->flags |= (flags & E2F_FLAG_SIGNAL_MASK) | + (global_ctx->flags & E2F_FLAG_SIGNAL_MASK); + + retval = e2fsck_pass1_merge_fs(global_fs, thread_fs); + if (retval) { + com_err(global_ctx->program_name, 0, _("while merging fs\n")); + return retval; + } + global_fs->priv_data = global_ctx; + global_ctx->fs = global_fs; + global_ctx->logf = global_logf; + global_ctx->problem_logf = global_problem_logf; + global_ctx->global_ctx = NULL; + retval = e2fsck_pass1_merge_icounts(global_ctx, thread_ctx); + if (retval) { + com_err(global_ctx->program_name, 0, + _("while merging icounts\n")); + return retval; + } + + global_ctx->dirs_to_hash = dirs_to_hash; + retval = e2fsck_pass1_merge_dirs_to_hash(global_ctx, thread_ctx); + if (retval) { + com_err(global_ctx->program_name, 0, + _("while merging dirs to hash\n")); + return retval; + } + + retval = e2fsck_pass1_merge_bitmap(global_fs, + &thread_ctx->inode_used_map, + &global_ctx->inode_used_map); + if (retval) + 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) + return retval; + retval = e2fsck_pass1_merge_bitmap(global_fs, + &thread_ctx->inode_bb_map, + &global_ctx->inode_bb_map); + if (retval) + return retval; + retval = e2fsck_pass1_merge_bitmap(global_fs, + &thread_ctx->inode_imagic_map, + &global_ctx->inode_imagic_map); + if (retval) + return retval; + retval = e2fsck_pass1_merge_bitmap(global_fs, + &thread_ctx->inode_reg_map, + &global_ctx->inode_reg_map); + if (retval) + return retval; + retval = e2fsck_pass1_merge_bitmap(global_fs, + &thread_ctx->inodes_to_rebuild, + &global_ctx->inodes_to_rebuild); + if (retval) + return retval; + retval = e2fsck_pass1_merge_bitmap(global_fs, + &thread_ctx->block_found_map, + &global_ctx->block_found_map); + if (retval) + return retval; + retval = e2fsck_pass1_merge_bitmap(global_fs, + &thread_ctx->block_dup_map, + &global_ctx->block_dup_map); + if (retval) + return retval; + retval = e2fsck_pass1_merge_bitmap(global_fs, + &thread_ctx->block_ea_map, + &global_ctx->block_ea_map); + if (retval) + return retval; + retval = e2fsck_pass1_merge_bitmap(global_fs, + &thread_ctx->block_metadata_map, + &global_ctx->block_metadata_map); + if (retval) + return retval; + + return 0; +} + +static int e2fsck_pass1_thread_join(e2fsck_t global_ctx, e2fsck_t thread_ctx) +{ + errcode_t retval; + + retval = e2fsck_pass1_thread_join_one(global_ctx, thread_ctx); + ext2fs_free_mem(&thread_ctx->fs); + if (thread_ctx->logf) + fclose(thread_ctx->logf); + if (thread_ctx->problem_logf) { + fputs("\n", thread_ctx->problem_logf); + fclose(thread_ctx->problem_logf); + } + e2fsck_pass1_free_bitmap(&thread_ctx->inode_used_map); + e2fsck_pass1_free_bitmap(&thread_ctx->inode_bad_map); + e2fsck_pass1_free_bitmap(&thread_ctx->inode_dir_map); + e2fsck_pass1_free_bitmap(&thread_ctx->inode_bb_map); + e2fsck_pass1_free_bitmap(&thread_ctx->inode_imagic_map); + e2fsck_pass1_free_bitmap(&thread_ctx->inode_reg_map); + e2fsck_pass1_free_bitmap(&thread_ctx->inodes_to_rebuild); + e2fsck_pass1_free_bitmap(&thread_ctx->block_found_map); + e2fsck_pass1_free_bitmap(&thread_ctx->block_dup_map); + e2fsck_pass1_free_bitmap(&thread_ctx->block_ea_map); + e2fsck_pass1_free_bitmap(&thread_ctx->block_metadata_map); + e2fsck_free_dir_info(thread_ctx); + ext2fs_free_icount(thread_ctx->inode_count); + ext2fs_free_icount(thread_ctx->inode_link_info); + if (thread_ctx->dirs_to_hash) + ext2fs_badblocks_list_free(thread_ctx->dirs_to_hash); + ext2fs_free_mem(&thread_ctx); + + return retval; +} + +static int e2fsck_pass1_threads_join(struct e2fsck_thread_info *infos, + int num_threads, e2fsck_t global_ctx) +{ + errcode_t rc; + errcode_t ret = 0; + int i; + struct e2fsck_thread_info *pinfo; + + for (i = 0; i < num_threads; i++) { + pinfo = &infos[i]; + + if (!pinfo->eti_started) + continue; + + rc = pthread_join(pinfo->eti_thread_id, NULL); + if (rc) { + com_err(global_ctx->program_name, rc, + _("while joining thread\n")); + if (ret == 0) + ret = rc; + } + rc = e2fsck_pass1_thread_join(global_ctx, infos[i].eti_thread_ctx); + if (rc) { + com_err(global_ctx->program_name, rc, + _("while joining pass1 thread\n")); + if (ret == 0) + ret = rc; + } + } + free(infos); + + return ret; +} + +static void *e2fsck_pass1_thread(void *arg) +{ + struct e2fsck_thread_info *info = arg; + e2fsck_t thread_ctx = info->eti_thread_ctx; +#ifdef DEBUG_THREADS + struct e2fsck_thread_debug *thread_debug = info->eti_debug; +#endif + +#ifdef DEBUG_THREADS + pthread_mutex_lock(&thread_debug->etd_mutex); + while (info->eti_thread_index > thread_debug->etd_finished_threads) { + pthread_cond_wait(&thread_debug->etd_cond, + &thread_debug->etd_mutex); + } + pthread_mutex_unlock(&thread_debug->etd_mutex); +#endif + +#ifdef HAVE_SETJMP_H + /* + * When fatal_error() happens, jump to here. The thread + * context's flags will be saved, but its abort_loc will + * be overwritten by original jump buffer for the later + * tests. + */ + if (setjmp(thread_ctx->abort_loc)) { + thread_ctx->flags &= ~E2F_FLAG_SETJMP_OK; + goto out; + } + thread_ctx->flags |= E2F_FLAG_SETJMP_OK; +#endif + + e2fsck_pass1_run(thread_ctx); + +out: + if (thread_ctx->options & E2F_OPT_MULTITHREAD) + log_out(thread_ctx, + _("Scanned group range [%lu, %lu), inodes %lu\n"), + thread_ctx->thread_info.et_group_start, + thread_ctx->thread_info.et_group_end, + thread_ctx->thread_info.et_inode_number); + +#ifdef DEBUG_THREADS + pthread_mutex_lock(&thread_debug->etd_mutex); + thread_debug->etd_finished_threads++; + pthread_cond_broadcast(&thread_debug->etd_cond); + pthread_mutex_unlock(&thread_debug->etd_mutex); +#endif + + return NULL; +} + +static int e2fsck_pass1_threads_start(struct e2fsck_thread_info **pinfo, + int num_threads, e2fsck_t global_ctx) +{ + struct e2fsck_thread_info *infos; + pthread_attr_t attr; + errcode_t retval; + errcode_t ret; + struct e2fsck_thread_info *tmp_pinfo; + int i; + e2fsck_t thread_ctx; +#ifdef DEBUG_THREADS + struct e2fsck_thread_debug thread_debug = + {PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER, 0}; + + thread_debug.etd_finished_threads = 0; +#endif + + retval = pthread_attr_init(&attr); + if (retval) { + com_err(global_ctx->program_name, retval, + _("while setting pthread attribute\n")); + return retval; + } + + infos = calloc(num_threads, sizeof(struct e2fsck_thread_info)); + if (infos == NULL) { + retval = -ENOMEM; + com_err(global_ctx->program_name, retval, + _("while allocating memory for threads\n")); + pthread_attr_destroy(&attr); + return retval; + } + + for (i = 0; i < num_threads; i++) { + tmp_pinfo = &infos[i]; + tmp_pinfo->eti_thread_index = i; +#ifdef DEBUG_THREADS + tmp_pinfo->eti_debug = &thread_debug; +#endif + retval = e2fsck_pass1_thread_prepare(global_ctx, &thread_ctx, + i, num_threads); + if (retval) { + com_err(global_ctx->program_name, retval, + _("while preparing pass1 thread\n")); + break; + } + tmp_pinfo->eti_thread_ctx = thread_ctx; + + retval = pthread_create(&tmp_pinfo->eti_thread_id, &attr, + &e2fsck_pass1_thread, tmp_pinfo); + if (retval) { + com_err(global_ctx->program_name, retval, + _("while creating thread\n")); + e2fsck_pass1_thread_join(global_ctx, thread_ctx); + break; + } + + tmp_pinfo->eti_started = 1; + } + + /* destroy the thread attribute object, since it is no longer needed */ + ret = pthread_attr_destroy(&attr); + if (ret) { + com_err(global_ctx->program_name, ret, + _("while destroying thread attribute\n")); + if (retval == 0) + retval = ret; + } + + if (retval) { + e2fsck_pass1_threads_join(infos, num_threads, global_ctx); + return retval; + } + *pinfo = infos; + return 0; +} + +static void e2fsck_pass1_multithread(e2fsck_t global_ctx) +{ + struct e2fsck_thread_info *infos = NULL; + int num_threads = 1; + errcode_t retval; + + retval = e2fsck_pass1_threads_start(&infos, num_threads, global_ctx); + if (retval) { + com_err(global_ctx->program_name, retval, + _("while starting pass1 threads\n")); + goto out_abort; + } + + retval = e2fsck_pass1_threads_join(infos, num_threads, global_ctx); + if (retval) { + com_err(global_ctx->program_name, retval, + _("while joining pass1 threads\n")); + goto out_abort; + } + return; +out_abort: + global_ctx->flags |= E2F_FLAG_ABORT; + return; +} +#endif + +/* TODO: tdb needs to be handled properly for multiple threads*/ +static int multiple_threads_supported(e2fsck_t ctx) +{ +#ifdef CONFIG_TDB + unsigned int threshold; + ext2_ino_t num_dirs; + errcode_t retval; + char *tdb_dir; + int enable; + + profile_get_string(ctx->profile, "scratch_files", "directory", 0, 0, + &tdb_dir); + profile_get_uint(ctx->profile, "scratch_files", + "numdirs_threshold", 0, 0, &threshold); + profile_get_boolean(ctx->profile, "scratch_files", + "icount", 0, 1, &enable); + + retval = ext2fs_get_num_dirs(ctx->fs, &num_dirs); + if (retval) + num_dirs = 1024; /* Guess */ + + /* tdb is unsupported now */ + if (enable && tdb_dir && !access(tdb_dir, W_OK) && + (!threshold || num_dirs > threshold)) + return 0; + #endif + return 1; +} + +void e2fsck_pass1(e2fsck_t ctx) +{ + + init_ext2_max_sizes(); +#ifdef HAVE_PTHREAD + if (multiple_threads_supported(ctx)) { + e2fsck_pass1_multithread(ctx); + return; + } + fprintf(stderr, "Fall through single thread for pass1 " + "because tdb could not handle properly\n"); +#endif + e2fsck_pass1_run(ctx); +} + #undef FINISH_INODE_LOOP /* @@ -2088,24 +2973,42 @@ static errcode_t scan_callback(ext2_filsys fs, { struct scan_callback_struct *scan_struct; e2fsck_t ctx; + struct e2fsck_thread *tinfo; scan_struct = (struct scan_callback_struct *) priv_data; ctx = scan_struct->ctx; - process_inodes((e2fsck_t) fs->priv_data, scan_struct->block_buf); + process_inodes((e2fsck_t) fs->priv_data, scan_struct->block_buf, + scan_struct->inodes_to_process, + scan_struct->process_inode_count); if (ctx->progress) if ((ctx->progress)(ctx, 1, group+1, ctx->fs->group_desc_count)) return EXT2_ET_CANCEL_REQUESTED; +#ifdef HAVE_PTHREAD + if (ctx->global_ctx) { + tinfo = &ctx->thread_info; + tinfo->et_group_next++; + if (ctx->options & E2F_OPT_DEBUG && + ctx->options & E2F_OPT_MULTITHREAD) + log_out(ctx, _("group %d finished\n"), + tinfo->et_group_next); + if (tinfo->et_group_next >= tinfo->et_group_end) + return EXT2_ET_SCAN_FINISHED; + } +#endif + return 0; } /* * Process the inodes in the "inodes to process" list. */ -static void process_inodes(e2fsck_t ctx, char *block_buf) +static void process_inodes(e2fsck_t ctx, char *block_buf, + struct process_inode_block *inodes_to_process, + int *process_inode_count) { int i; struct ext2_inode *old_stashed_inode; @@ -2117,15 +3020,15 @@ static void process_inodes(e2fsck_t ctx, char *block_buf) #if 0 printf("begin process_inodes: "); #endif - if (process_inode_count == 0) + if (*process_inode_count == 0) return; old_operation = ehandler_operation(0); old_stashed_inode = ctx->stashed_inode; old_stashed_ino = ctx->stashed_ino; - qsort(inodes_to_process, process_inode_count, + qsort(inodes_to_process, *process_inode_count, sizeof(struct process_inode_block), process_inode_cmp); clear_problem_context(&pctx); - for (i=0; i < process_inode_count; i++) { + for (i=0; i < *process_inode_count; i++) { pctx.inode = ctx->stashed_inode = (struct ext2_inode *) &inodes_to_process[i].inode; pctx.ino = ctx->stashed_ino = inodes_to_process[i].ino; @@ -2138,12 +3041,12 @@ static void process_inodes(e2fsck_t ctx, char *block_buf) ehandler_operation(buf); check_blocks(ctx, &pctx, block_buf, &inodes_to_process[i].ea_ibody_quota); - if (ctx->flags & E2F_FLAG_SIGNAL_MASK) + if (e2fsck_should_abort(ctx)) break; } ctx->stashed_inode = old_stashed_inode; ctx->stashed_ino = old_stashed_ino; - process_inode_count = 0; + *process_inode_count = 0; #if 0 printf("end process inodes\n"); #endif @@ -2197,6 +3100,24 @@ static void mark_inode_bad(e2fsck_t ctx, ino_t ino) ext2fs_mark_inode_bitmap2(ctx->inode_bad_map, ino); } +static void add_casefolded_dir(e2fsck_t ctx, ino_t ino) +{ + struct problem_context pctx; + + if (!ctx->casefolded_dirs) { + pctx.errcode = ext2fs_u32_list_create(&ctx->casefolded_dirs, 0); + if (pctx.errcode) + goto error; + } + pctx.errcode = ext2fs_u32_list_add(ctx->casefolded_dirs, ino); + if (pctx.errcode == 0) + return; +error: + fix_problem(ctx, PR_1_ALLOCATE_CASEFOLDED_DIRLIST, &pctx); + /* Should never get here */ + ctx->flags |= E2F_FLAG_ABORT; +} + /* * This procedure will allocate the inode "bb" (badblock) map table */ @@ -2654,18 +3575,49 @@ static int handle_htree(e2fsck_t ctx, struct problem_context *pctx, if ((root->hash_version != EXT2_HASH_LEGACY) && (root->hash_version != EXT2_HASH_HALF_MD4) && (root->hash_version != EXT2_HASH_TEA) && + (root->hash_version != EXT2_HASH_SIPHASH) && fix_problem(ctx, PR_1_HTREE_HASHV, pctx)) return 1; + if (ext4_hash_in_dirent(inode)) { + if (root->hash_version != EXT2_HASH_SIPHASH && + fix_problem(ctx, PR_1_HTREE_NEEDS_SIPHASH, pctx)) + return 1; + } else { + if (root->hash_version == EXT2_HASH_SIPHASH && + fix_problem(ctx, PR_1_HTREE_CANNOT_SIPHASH, pctx)) + return 1; + } + if ((root->unused_flags & EXT2_HASH_FLAG_INCOMPAT) && fix_problem(ctx, PR_1_HTREE_INCOMPAT, pctx)) return 1; pctx->num = root->indirect_levels; - if ((root->indirect_levels > ext2_dir_htree_level(fs)) && + /* if htree level is clearly too high, consider it to be broken */ + if (root->indirect_levels > EXT4_HTREE_LEVEL && fix_problem(ctx, PR_1_HTREE_DEPTH, pctx)) return 1; + /* if level is only maybe too high, LARGE_DIR feature could be unset */ + if (root->indirect_levels > ext2_dir_htree_level(fs) && + !ext2fs_has_feature_largedir(fs->super)) { + int blockbits = EXT2_BLOCK_SIZE_BITS(fs->super) + 10; + unsigned idx_pb = 1 << (blockbits - 3); + + /* compare inode size/blocks vs. max-sized 2-level htree */ + if (EXT2_I_SIZE(pctx->inode) < + (idx_pb - 1) * (idx_pb - 2) << blockbits && + pctx->inode->i_blocks < + (idx_pb - 1) * (idx_pb - 2) << (blockbits - 9) && + fix_problem(ctx, PR_1_HTREE_DEPTH, pctx)) + return 1; + } + + if (root->indirect_levels > EXT4_HTREE_LEVEL_COMPAT || + ext2fs_needs_large_file_feature(EXT2_I_SIZE(inode))) + ctx->large_dirs++; + return 0; } @@ -2812,7 +3764,8 @@ static void scan_extent_node(e2fsck_t ctx, struct problem_context *pctx, (extent.e_pblk + extent.e_len) > ext2fs_blocks_count(ctx->fs->super)) problem = PR_1_EXTENT_ENDS_BEYOND; - else if (is_leaf && is_dir && + else if (is_leaf && is_dir && !pctx->inode->i_size_high && + !ext2fs_has_feature_largedir(ctx->fs->super) && ((extent.e_lblk + extent.e_len) > (1U << (21 - ctx->fs->super->s_log_block_size)))) problem = PR_1_TOOBIG_DIR; @@ -2820,9 +3773,10 @@ static void scan_extent_node(e2fsck_t ctx, struct problem_context *pctx, if (is_leaf && problem == 0 && extent.e_len > 0) { #if 0 printf("extent_region(ino=%u, expect=%llu, " - "lblk=%llu, len=%u)\n", - pb->ino, pb->next_lblock, - extent.e_lblk, extent.e_len); + "lblk=%llu, len=%u)\n", pb->ino, + (unsigned long long) pb->next_lblock, + (unsigned long long) extent.e_lblk, + extent.e_len); #endif if (extent.e_lblk < pb->next_lblock) problem = PR_1_EXTENT_COLLISION; @@ -2948,7 +3902,12 @@ report_problem: if (extent.e_lblk != lblk) { struct ext2_extent_info e_info; - ext2fs_extent_get_info(ehandle, &e_info); + pctx->errcode = ext2fs_extent_get_info(ehandle, + &e_info); + if (pctx->errcode) { + pctx->str = "ext2fs_extent_get_info"; + return; + } pctx->blk = lblk; pctx->blk2 = extent.e_lblk; pctx->num = e_info.curr_level - 1; @@ -3308,7 +4267,7 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx, inlinedata_fs = ext2fs_has_feature_inline_data(ctx->fs->super); if (check_ext_attr(ctx, pctx, block_buf, &ea_block_quota)) { - if (ctx->flags & E2F_FLAG_SIGNAL_MASK) + if (e2fsck_should_abort(ctx)) goto out; pb.num_blocks += EXT2FS_B2C(ctx->fs, ea_block_quota.blocks); } @@ -3363,7 +4322,7 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx, } end_problem_latch(ctx, PR_LATCH_BLOCK); end_problem_latch(ctx, PR_LATCH_TOOBIG); - if (ctx->flags & E2F_FLAG_SIGNAL_MASK) + if (e2fsck_should_abort(ctx)) goto out; if (pctx->errcode) fix_problem(ctx, PR_1_BLOCK_ITERATE, pctx); @@ -3417,11 +4376,13 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx, pb.num_blocks *= EXT2FS_CLUSTER_RATIO(fs); #if 0 printf("inode %u, i_size = %u, last_block = %llu, i_blocks=%llu, num_blocks = %llu\n", - ino, inode->i_size, pb.last_block, ext2fs_inode_i_blocks(fs, inode), - pb.num_blocks); + ino, inode->i_size, (unsigned long long) pb.last_block, + (unsigned long long) ext2fs_inode_i_blocks(fs, inode), + (unsigned long long) pb.num_blocks); #endif + size = EXT2_I_SIZE(inode); if (pb.is_dir) { - unsigned nblock = inode->i_size >> EXT2_BLOCK_SIZE_BITS(fs->super); + unsigned nblock = size >> EXT2_BLOCK_SIZE_BITS(fs->super); if (inode->i_flags & EXT4_INLINE_DATA_FL) { int flags; size_t sz = 0; @@ -3435,11 +4396,11 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx, EXT2_FLAG_IGNORE_CSUM_ERRORS) | (ctx->fs->flags & ~EXT2_FLAG_IGNORE_CSUM_ERRORS); - if (err || sz != inode->i_size) { + if (err || sz != size) { bad_size = 7; pctx->num = sz; } - } else if (inode->i_size & (fs->blocksize - 1)) + } else if (size & (fs->blocksize - 1)) bad_size = 5; else if (nblock > (pb.last_block + 1)) bad_size = 1; @@ -3449,7 +4410,6 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx, bad_size = 2; } } else { - size = EXT2_I_SIZE(inode); if ((pb.last_init_lblock >= 0) && /* Do not allow initialized allocated blocks past i_size*/ (size < (__u64)pb.last_init_lblock * fs->blocksize) && @@ -3472,8 +4432,6 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx, pctx->num = (pb.last_block + 1) * fs->blocksize; pctx->group = bad_size; if (fix_problem(ctx, PR_1_BAD_I_SIZE, pctx)) { - if (LINUX_S_ISDIR(inode->i_mode)) - pctx->num &= 0xFFFFFFFFULL; ext2fs_inode_size_set(fs, inode, pctx->num); if (EXT2_I_SIZE(inode) == 0 && (inode->i_flags & EXT4_INLINE_DATA_FL)) { @@ -3645,13 +4603,14 @@ static int process_block(ext2_filsys fs, (unsigned long) pctx->ino, type, (unsigned long) p->previous_block+1, (unsigned long) blk, - blockcnt); + (long long) blockcnt); } p->fragmented = 1; } } if (p->is_dir && !ext2fs_has_feature_largedir(fs->super) && + !pctx->inode->i_size_high && blockcnt > (1 << (21 - fs->super->s_log_block_size))) problem = PR_1_TOOBIG_DIR; if (p->is_dir && p->num_blocks + 1 >= p->max_blocks) @@ -3844,7 +4803,7 @@ static int process_bad_block(ext2_filsys fs, *block_nr = 0; return BLOCK_CHANGED; } - if (ctx->flags & E2F_FLAG_SIGNAL_MASK) + if (e2fsck_should_abort(ctx)) return BLOCK_ABORT; } else mark_block_used(ctx, blk); @@ -3941,7 +4900,7 @@ static int process_bad_block(ext2_filsys fs, *block_nr = 0; return BLOCK_CHANGED; } - if (ctx->flags & E2F_FLAG_SIGNAL_MASK) + if (e2fsck_should_abort(ctx)) return BLOCK_ABORT; return 0; }