X-Git-Url: https://git.whamcloud.com/?a=blobdiff_plain;f=e2fsck%2Fpass1.c;h=b48b89311e55555485ea0335d0bf10eb4a9e03d8;hb=e1abb3cd819986e1d5caa53bf91c0759de722ec8;hp=5015d9382c8b9a460f476aea709c999bd0a1445d;hpb=69ab815dbe7981902847dfefd4f4c2d2cc855ef3;p=tools%2Fe2fsprogs.git diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c index 5015d93..b48b893 100644 --- a/e2fsck/pass1.c +++ b/e2fsck/pass1.c @@ -23,10 +23,12 @@ * - 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) * - Ref counts for ea_inodes. (ea_inode_refs) + * - The encryption policy ID of each encrypted inode. (encrypted_files) * * Pass 1 is designed to stash away enough information so that the * other passes should not need to read in the inode information @@ -45,9 +47,13 @@ #ifdef HAVE_ERRNO_H #include #endif +#include #include "e2fsck.h" #include +/* todo remove this finally */ +#include +#include #include "problem.h" @@ -77,7 +83,7 @@ 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_encrypted_dir(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); @@ -128,16 +134,6 @@ static __u64 ext2_max_sizes[EXT2_MAX_BLOCK_LOG_SIZE - EXT2_MIN_BLOCK_LOG_SIZE + 1]; /* - * Free all memory allocated by pass1 in preparation for restarting - * things. - */ -static void unwind_pass1(ext2_filsys fs EXT2FS_ATTR((unused))) -{ - ext2fs_free_mem(&inodes_to_process); - inodes_to_process = 0; -} - -/* * Check to make sure a device inode is real. Returns 1 if the device * checks out, 0 if not. * @@ -151,10 +147,10 @@ int e2fsck_pass1_check_device_inode(ext2_filsys fs EXT2FS_ATTR((unused)), int i; /* - * If the index flag is set, then this is a bogus + * If the index or extents flag is set, then this is a bogus * device/fifo/socket */ - if (inode->i_flags & EXT2_INDEX_FL) + if (inode->i_flags & (EXT2_INDEX_FL | EXT4_EXTENTS_FL)) return 0; /* @@ -183,43 +179,18 @@ int e2fsck_pass1_check_device_inode(ext2_filsys fs EXT2FS_ATTR((unused)), int e2fsck_pass1_check_symlink(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode, char *buf) { + unsigned int buflen; unsigned int len; - int i; - ext2_extent_handle_t handle; - struct ext2_extent_info info; - struct ext2fs_extent extent; if ((inode->i_size_high || inode->i_size == 0) || (inode->i_flags & EXT2_INDEX_FL)) return 0; - if (inode->i_flags & EXT4_EXTENTS_FL) { - if (inode->i_flags & EXT4_INLINE_DATA_FL) - return 0; - if (inode->i_size > fs->blocksize) - return 0; - if (ext2fs_extent_open2(fs, ino, inode, &handle)) - return 0; - i = 0; - if (ext2fs_extent_get_info(handle, &info) || - (info.num_entries != 1) || - (info.max_depth != 0)) - goto exit_extent; - if (ext2fs_extent_get(handle, EXT2_EXTENT_ROOT, &extent) || - (extent.e_lblk != 0) || - (extent.e_len != 1) || - (extent.e_pblk < fs->super->s_first_data_block) || - (extent.e_pblk >= ext2fs_blocks_count(fs->super))) - goto exit_extent; - i = 1; - exit_extent: - ext2fs_extent_free(handle); - return i; - } - if (inode->i_flags & EXT4_INLINE_DATA_FL) { size_t inline_size; + if (inode->i_flags & EXT4_EXTENTS_FL) + return 0; if (ext2fs_inline_data_size(fs, ino, &inline_size)) return 0; if (inode->i_size != inline_size) @@ -229,36 +200,63 @@ int e2fsck_pass1_check_symlink(ext2_filsys fs, ext2_ino_t ino, } if (ext2fs_is_fast_symlink(inode)) { - if (inode->i_size >= sizeof(inode->i_block)) - return 0; - - len = strnlen((char *)inode->i_block, sizeof(inode->i_block)); - if (len == sizeof(inode->i_block)) + if (inode->i_flags & EXT4_EXTENTS_FL) return 0; + buf = (char *)inode->i_block; + buflen = sizeof(inode->i_block); } else { - if ((inode->i_size >= fs->blocksize) || - (inode->i_block[0] < fs->super->s_first_data_block) || - (inode->i_block[0] >= ext2fs_blocks_count(fs->super))) - return 0; + ext2_extent_handle_t handle; + struct ext2_extent_info info; + struct ext2fs_extent extent; + blk64_t blk; + int i; - for (i = 1; i < EXT2_N_BLOCKS; i++) - if (inode->i_block[i]) + if (inode->i_flags & EXT4_EXTENTS_FL) { + if (ext2fs_extent_open2(fs, ino, inode, &handle)) return 0; + if (ext2fs_extent_get_info(handle, &info) || + (info.num_entries != 1) || + (info.max_depth != 0)) { + ext2fs_extent_free(handle); + return 0; + } + if (ext2fs_extent_get(handle, EXT2_EXTENT_ROOT, + &extent) || + (extent.e_lblk != 0) || + (extent.e_len != 1)) { + ext2fs_extent_free(handle); + return 0; + } + blk = extent.e_pblk; + ext2fs_extent_free(handle); + } else { + blk = inode->i_block[0]; + + for (i = 1; i < EXT2_N_BLOCKS; i++) + if (inode->i_block[i]) + return 0; + } - if (io_channel_read_blk64(fs->io, inode->i_block[0], 1, buf)) + if (blk < fs->super->s_first_data_block || + blk >= ext2fs_blocks_count(fs->super)) return 0; - if (inode->i_flags & EXT4_ENCRYPT_FL) { - len = ext2fs_le32_to_cpu(*((__u32 *)buf)) + 4; - } else { - len = strnlen(buf, fs->blocksize); - } - if (len == fs->blocksize) + if (io_channel_read_blk64(fs->io, blk, 1, buf)) return 0; + + buflen = fs->blocksize; } + + if (inode->i_flags & EXT4_ENCRYPT_FL) + len = ext2fs_le16_to_cpu(*(__u16 *)buf) + 2; + else + len = strnlen(buf, buflen); + + if (len >= buflen) + return 0; + if (len != inode->i_size) - if ((inode->i_flags & EXT4_ENCRYPT_FL) == 0) - return 0; + return 0; return 1; } @@ -394,13 +392,13 @@ static problem_t check_large_ea_inode(e2fsck_t ctx, static void inc_ea_inode_refs(e2fsck_t ctx, struct problem_context *pctx, struct ext2_ext_attr_entry *first, void *end) { - struct ext2_ext_attr_entry *entry; + struct ext2_ext_attr_entry *entry = first; + struct ext2_ext_attr_entry *np = EXT2_EXT_ATTR_NEXT(entry); - for (entry = first; - (void *)entry < end && !EXT2_EXT_IS_LAST_ENTRY(entry); - entry = EXT2_EXT_ATTR_NEXT(entry)) { + while ((void *) entry < end && (void *) np < end && + !EXT2_EXT_IS_LAST_ENTRY(entry)) { if (!entry->e_value_inum) - continue; + goto next; if (!ctx->ea_inode_refs) { pctx->errcode = ea_refcount_create(0, &ctx->ea_inode_refs); @@ -413,6 +411,9 @@ static void inc_ea_inode_refs(e2fsck_t ctx, struct problem_context *pctx, } ea_refcount_increment(ctx->ea_inode_refs, entry->e_value_inum, 0); + next: + entry = np; + np = EXT2_EXT_ATTR_NEXT(entry); } } @@ -817,7 +818,6 @@ extern errcode_t e2fsck_setup_icount(e2fsck_t ctx, const char *icount_name, errcode_t retval; char *tdb_dir; int enable; - int full_map; *ret = 0; @@ -1152,7 +1152,22 @@ 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_thread(e2fsck_t ctx) { int i; __u64 max_sizes; @@ -1169,10 +1184,11 @@ void e2fsck_pass1(e2fsck_t ctx) struct scan_callback_struct scan_struct; struct ext2_super_block *sb = ctx->fs->super; const char *old_op; - int imagic_fs, extent_fs, inlinedata_fs; + const char *eop_next_inode = _("getting next inode from scan"); + int imagic_fs, extent_fs, inlinedata_fs, casefold_fs; int low_dtime_check = 1; - int inode_size = EXT2_INODE_SIZE(fs->super); - int bufsize; + unsigned int inode_size = EXT2_INODE_SIZE(fs->super); + unsigned int bufsize; int failed_csum = 0; ext2_ino_t ino_threshold = 0; dgrp_t ra_group = 0; @@ -1215,6 +1231,7 @@ void e2fsck_pass1(e2fsck_t ctx) imagic_fs = ext2fs_has_feature_imagic_inodes(sb); extent_fs = ext2fs_has_feature_extents(sb); inlinedata_fs = ext2fs_has_feature_inline_data(sb); + casefold_fs = ext2fs_has_feature_casefold(sb); /* * Allocate bitmaps structures @@ -1266,6 +1283,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) { @@ -1338,8 +1369,10 @@ void e2fsck_pass1(e2fsck_t ctx) if (ctx->progress && ((ctx->progress)(ctx, 1, 0, ctx->fs->group_desc_count))) goto endit; - if ((fs->super->s_wtime < fs->super->s_inodes_count) || - (fs->super->s_mtime < fs->super->s_inodes_count) || + if ((fs->super->s_wtime && + fs->super->s_wtime < fs->super->s_inodes_count) || + (fs->super->s_mtime && + fs->super->s_mtime < fs->super->s_inodes_count) || (fs->super->s_mkfs_time && fs->super->s_mkfs_time < fs->super->s_inodes_count)) low_dtime_check = 0; @@ -1358,13 +1391,13 @@ void e2fsck_pass1(e2fsck_t ctx) if (e2fsck_mmp_update(fs)) fatal_error(ctx, 0); } - old_op = ehandler_operation(_("getting next inode from scan")); + old_op = ehandler_operation(eop_next_inode); pctx.errcode = ext2fs_get_next_inode_full(scan, &ino, inode, inode_size); 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) { /* @@ -1386,17 +1419,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); @@ -1478,6 +1503,15 @@ void e2fsck_pass1(e2fsck_t ctx) continue; } + if ((inode->i_flags & EXT4_CASEFOLD_FL) && + ((!LINUX_S_ISDIR(inode->i_mode) && + fix_problem(ctx, PR_1_CASEFOLD_NONDIR, &pctx)) || + (!casefold_fs && + fix_problem(ctx, PR_1_CASEFOLD_FEATURE, &pctx)))) { + inode->i_flags &= ~EXT4_CASEFOLD_FL; + e2fsck_write_inode(ctx, ino, inode, "pass1"); + } + /* Conflicting inlinedata/extents inode flags? */ if ((inode->i_flags & EXT4_INLINE_DATA_FL) && (inode->i_flags & EXT4_EXTENTS_FL)) { @@ -1495,8 +1529,8 @@ void e2fsck_pass1(e2fsck_t ctx) (ino >= EXT2_FIRST_INODE(fs->super))) { size_t size = 0; - pctx.errcode = ext2fs_inline_data_size(fs, ino, &size); - if (!pctx.errcode && size && + pctx.errcode = get_inline_data_ea_size(fs, ino, &size); + if (!pctx.errcode && fix_problem(ctx, PR_1_INLINE_DATA_FEATURE, &pctx)) { ext2fs_set_feature_inline_data(sb); ext2fs_mark_super_dirty(fs); @@ -1540,6 +1574,7 @@ void e2fsck_pass1(e2fsck_t ctx) case EXT2_ET_NO_INLINE_DATA: case EXT2_ET_EXT_ATTR_CSUM_INVALID: case EXT2_ET_EA_BAD_VALUE_OFFSET: + case EXT2_ET_EA_INODE_CORRUPTED: /* broken EA or no system.data EA; truncate */ if (fix_problem(ctx, PR_1_INLINE_DATA_NO_ATTR, &pctx)) { @@ -1868,12 +1903,19 @@ void e2fsck_pass1(e2fsck_t ctx) failed_csum = 0; } + if ((inode->i_flags & EXT4_ENCRYPT_FL) && + 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_ENCRYPT_FL) - add_encrypted_dir(ctx, ino); + 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++; @@ -1955,7 +1997,7 @@ void e2fsck_pass1(e2fsck_t ctx) if (process_inode_count >= ctx->process_inode_size) { process_inodes(ctx, block_buf); - if (ctx->flags & E2F_FLAG_SIGNAL_MASK) + if (e2fsck_should_abort(ctx)) goto endit; } } @@ -2002,6 +2044,9 @@ void e2fsck_pass1(e2fsck_t ctx) ctx->block_ea_map = 0; } + /* We don't need the encryption policy => ID map any more */ + destroy_encryption_policy_map(ctx); + if (ctx->flags & E2F_FLAG_RESIZE_INODE) { clear_problem_context(&pctx); pctx.errcode = ext2fs_create_resize_inode(fs); @@ -2031,10 +2076,22 @@ void e2fsck_pass1(e2fsck_t ctx) * master superblock. */ ctx->use_superblock = 0; - unwind_pass1(fs); 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); @@ -2043,9 +2100,10 @@ void e2fsck_pass1(e2fsck_t ctx) e2fsck_pass1_dupblocks(ctx, block_buf); } ctx->flags |= E2F_FLAG_ALLOC_OK; - ext2fs_free_mem(&inodes_to_process); endit: e2fsck_use_inode_shortcuts(ctx, 0); + ext2fs_free_mem(&inodes_to_process); + inodes_to_process = 0; if (scan) ext2fs_close_inode_scan(scan); @@ -2065,6 +2123,164 @@ endit: else ctx->invalid_bitmaps++; } + +static errcode_t e2fsck_pass1_copy_fs(ext2_filsys dest, ext2_filsys src) +{ + errcode_t retval; + + memcpy(dest, src, sizeof(struct struct_ext2_filsys)); + if (dest->dblist) + dest->dblist->fs = dest; + if (dest->inode_map) + dest->inode_map->fs = dest; + if (dest->block_map) + dest->block_map->fs = dest; + + /* icache will be rebuilt if needed, so do not copy from @src */ + src->icache = NULL; + return 0; +} + +static void e2fsck_pass1_merge_fs(ext2_filsys dest, ext2_filsys src) +{ + struct ext2_inode_cache *icache = dest->icache; + + memcpy(dest, src, sizeof(struct struct_ext2_filsys)); + if (dest->dblist) + dest->dblist->fs = dest; + if (dest->inode_map) + dest->inode_map->fs = dest; + if (dest->block_map) + dest->block_map->fs = dest; + dest->icache = icache; + + if (src->icache) { + ext2fs_free_inode_cache(src->icache); + src->icache = NULL; + } +} + +static errcode_t e2fsck_pass1_thread_prepare(e2fsck_t global_ctx, e2fsck_t *thread_ctx) +{ + errcode_t retval; + e2fsck_t thread_context; + ext2_filsys thread_fs; + ext2_filsys global_fs = global_ctx->fs; + + 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; + } + + retval = e2fsck_pass1_copy_fs(thread_fs, 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->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 int e2fsck_pass1_thread_join(e2fsck_t global_ctx, e2fsck_t thread_ctx) +{ + int flags = global_ctx->flags; + ext2_filsys thread_fs = thread_ctx->fs; + ext2_filsys global_fs = global_ctx->fs; +#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 + /* Keep the global singal flags*/ + global_ctx->flags |= (flags & E2F_FLAG_SIGNAL_MASK) | + (global_ctx->flags & E2F_FLAG_SIGNAL_MASK); + + e2fsck_pass1_merge_fs(global_fs, thread_fs); + global_fs->priv_data = global_ctx; + global_ctx->fs = global_fs; + + ext2fs_free_mem(&thread_ctx->fs); + ext2fs_free_mem(&thread_ctx); + return 0; +} + +void e2fsck_pass1_multithread(e2fsck_t ctx) +{ + errcode_t retval; + e2fsck_t thread_ctx; + + retval = e2fsck_pass1_thread_prepare(ctx, &thread_ctx); + if (retval) { + com_err(ctx->program_name, 0, + _("while preparing pass1 thread\n")); + ctx->flags |= E2F_FLAG_ABORT; + return; + } + +#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; + e2fsck_pass1_thread_join(ctx, thread_ctx); + return; + } + thread_ctx->flags |= E2F_FLAG_SETJMP_OK; +#endif + + e2fsck_pass1_thread(thread_ctx); + retval = e2fsck_pass1_thread_join(ctx, thread_ctx); + if (retval) { + com_err(ctx->program_name, 0, + _("while joining pass1 thread\n")); + ctx->flags |= E2F_FLAG_ABORT; + return; + } +} + +void e2fsck_pass1(e2fsck_t ctx) +{ + e2fsck_pass1_multithread(ctx); +} + #undef FINISH_INODE_LOOP /* @@ -2127,7 +2343,7 @@ 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; @@ -2186,20 +2402,20 @@ static void mark_inode_bad(e2fsck_t ctx, ino_t ino) ext2fs_mark_inode_bitmap2(ctx->inode_bad_map, ino); } -static void add_encrypted_dir(e2fsck_t ctx, ino_t ino) +static void add_casefolded_dir(e2fsck_t ctx, ino_t ino) { struct problem_context pctx; - if (!ctx->encrypted_dirs) { - pctx.errcode = ext2fs_u32_list_create(&ctx->encrypted_dirs, 0); + 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->encrypted_dirs, ino); + pctx.errcode = ext2fs_u32_list_add(ctx->casefolded_dirs, ino); if (pctx.errcode == 0) return; error: - fix_problem(ctx, PR_1_ALLOCATE_ENCRYPTED_DIRLIST, &pctx); + fix_problem(ctx, PR_1_ALLOCATE_CASEFOLDED_DIRLIST, &pctx); /* Should never get here */ ctx->flags |= E2F_FLAG_ABORT; } @@ -2258,6 +2474,10 @@ static _INLINE_ void mark_block_used(e2fsck_t ctx, blk64_t block) clear_problem_context(&pctx); if (ext2fs_fast_test_block_bitmap2(ctx->block_found_map, block)) { + if (ext2fs_has_feature_shared_blocks(ctx->fs->super) && + !(ctx->options & E2F_OPT_UNSHARE_BLOCKS)) { + return; + } if (!ctx->block_dup_map) { pctx.errcode = e2fsck_allocate_block_bitmap(ctx->fs, _("multiply claimed block map"), @@ -2288,7 +2508,8 @@ static _INLINE_ void mark_blocks_used(e2fsck_t ctx, blk64_t block, if (ext2fs_test_block_bitmap_range2(ctx->block_found_map, block, num)) ext2fs_mark_block_bitmap_range2(ctx->block_found_map, block, num); else { - int i; + unsigned int i; + for (i = 0; i < num; i += EXT2FS_CLUSTER_RATIO(ctx->fs)) mark_block_used(ctx, block + i); } @@ -2511,8 +2732,9 @@ static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx, break; } if (entry->e_value_inum == 0) { - if (entry->e_value_offs + entry->e_value_size > - fs->blocksize) { + if (entry->e_value_size > EXT2_XATTR_SIZE_MAX || + (entry->e_value_offs + entry->e_value_size > + fs->blocksize)) { if (fix_problem(ctx, PR_1_EA_BAD_VALUE, pctx)) goto clear_extattr; break; @@ -2567,7 +2789,7 @@ static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx, return 0; } - if (quota_blocks != EXT2FS_C2B(fs, 1)) { + if (quota_blocks != EXT2FS_C2B(fs, 1U)) { if (!ctx->ea_block_quota_blocks) { pctx->errcode = ea_refcount_create(0, &ctx->ea_block_quota_blocks); @@ -2656,18 +2878,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; } @@ -2765,7 +3018,8 @@ static void scan_extent_node(e2fsck_t ctx, struct problem_context *pctx, if (pctx->errcode) return; if (!(ctx->options & E2F_OPT_FIXES_ONLY) && - !pb->eti.force_rebuild) { + !pb->eti.force_rebuild && + info.curr_level < MAX_EXTENT_DEPTH_COUNT) { struct extent_tree_level *etl; etl = pb->eti.ext_info + info.curr_level; @@ -2804,8 +3058,9 @@ static void scan_extent_node(e2fsck_t ctx, struct problem_context *pctx, else if (extent.e_lblk < start_block) problem = PR_1_OUT_OF_ORDER_EXTENTS; else if ((end_block && last_lblk > end_block) && - (!(extent.e_flags & EXT2_EXTENT_FLAGS_UNINIT && - last_lblk > eof_block))) + !(last_lblk > eof_block && + ((extent.e_flags & EXT2_EXTENT_FLAGS_UNINIT) || + (pctx->inode->i_flags & EXT4_VERITY_FL)))) problem = PR_1_EXTENT_END_OUT_OF_BOUNDS; else if (is_leaf && extent.e_len == 0) problem = PR_1_EXTENT_LENGTH_ZERO; @@ -2813,7 +3068,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; @@ -2821,9 +3077,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; @@ -2846,7 +3103,20 @@ static void scan_extent_node(e2fsck_t ctx, struct problem_context *pctx, return; failed_csum = 0; } - +#ifdef CONFIG_DEVELOPER_FEATURES + if (try_repairs && !is_dir && problem == 0 && + (ctx->options & E2F_OPT_CLEAR_UNINIT) && + (extent.e_flags & EXT2_EXTENT_FLAGS_UNINIT) && + fix_problem(ctx, PR_1_CLEAR_UNINIT_EXTENT, pctx)) { + extent.e_flags &= ~EXT2_EXTENT_FLAGS_UNINIT; + pb->inode_modified = 1; + pctx->errcode = ext2fs_extent_replace(ehandle, 0, + &extent); + if (pctx->errcode) + return; + failed_csum = 0; + } +#endif if (try_repairs && problem) { report_problem: if (fix_problem(ctx, problem, pctx)) { @@ -2936,7 +3206,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; @@ -3296,7 +3571,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); } @@ -3351,7 +3626,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); @@ -3374,7 +3649,7 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx, inode->i_flags &= ~EXT2_INDEX_FL; dirty_inode++; } else { - e2fsck_add_dx_dir(ctx, ino, pb.last_block+1); + e2fsck_add_dx_dir(ctx, ino, inode, pb.last_block+1); } } @@ -3405,11 +3680,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; @@ -3423,11 +3700,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; @@ -3437,15 +3714,10 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx, bad_size = 2; } } else { - e2_blkcnt_t blkpg = ctx->blocks_per_page; - - size = EXT2_I_SIZE(inode); if ((pb.last_init_lblock >= 0) && - /* allow allocated blocks to end of PAGE_SIZE */ + /* Do not allow initialized allocated blocks past i_size*/ (size < (__u64)pb.last_init_lblock * fs->blocksize) && - (pb.last_init_lblock / blkpg * blkpg != pb.last_init_lblock || - size < (__u64)(pb.last_init_lblock & ~(blkpg-1)) * - fs->blocksize)) + !(inode->i_flags & EXT4_VERITY_FL)) bad_size = 3; else if (!(extent_fs && (inode->i_flags & EXT4_EXTENTS_FL)) && size > ext2_max_sizes[fs->super->s_log_block_size]) @@ -3464,8 +3736,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)) { @@ -3637,15 +3907,19 @@ 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 && blockcnt > (1 << (21 - fs->super->s_log_block_size))) + 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_reg && p->num_blocks+1 >= p->max_blocks) + if (p->is_dir && p->num_blocks + 1 >= p->max_blocks) + problem = PR_1_TOOBIG_DIR; + if (p->is_reg && p->num_blocks + 1 >= p->max_blocks) problem = PR_1_TOOBIG_REG; if (!p->is_dir && !p->is_reg && blockcnt > 0) problem = PR_1_TOOBIG_SYMLINK; @@ -3833,7 +4107,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); @@ -3930,7 +4204,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; } @@ -3969,7 +4243,7 @@ static void new_table_block(e2fsck_t ctx, blk64_t first_block, dgrp_t group, */ is_flexbg = ext2fs_has_feature_flex_bg(fs->super); if (is_flexbg) { - flexbg_size = 1 << fs->super->s_log_groups_per_flex; + flexbg_size = 1U << fs->super->s_log_groups_per_flex; flexbg = group / flexbg_size; first_block = ext2fs_group_first_block2(fs, flexbg_size * flexbg);