X-Git-Url: https://git.whamcloud.com/?a=blobdiff_plain;f=e2fsck%2Fpass1.c;h=dde862a821767bd05720c8c1c7b68c69b7fca455;hb=ece580372624113bf0ce9e291952801c2bb1884f;hp=23d2e99f7dd74dc92ed59a7eaa8885255decfcfa;hpb=25f291c9b32d8017e6969c72a75e37d354c0570b;p=tools%2Fe2fsprogs.git diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c index 23d2e99..dde862a 100644 --- a/e2fsck/pass1.c +++ b/e2fsck/pass1.c @@ -23,13 +23,16 @@ * - 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 - * during the normal course of a filesystem check. (Althogh if an + * during the normal course of a filesystem check. (Although if an * inconsistency is detected, other passes may need to read in an * inode to fix it.) * @@ -47,6 +50,7 @@ #include "e2fsck.h" #include +#include #include "problem.h" @@ -58,6 +62,11 @@ #undef DEBUG +struct ea_quota { + blk64_t blocks; + __u64 inodes; +}; + static int process_block(ext2_filsys fs, blk64_t *blocknr, e2_blkcnt_t blockcnt, blk64_t ref_blk, int ref_offset, void *priv_data); @@ -65,12 +74,13 @@ static int process_bad_block(ext2_filsys fs, blk64_t *block_nr, e2_blkcnt_t blockcnt, blk64_t ref_blk, int ref_offset, void *priv_data); static void check_blocks(e2fsck_t ctx, struct problem_context *pctx, - char *block_buf); + char *block_buf, + const struct ea_quota *ea_ibody_quota); 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); @@ -87,7 +97,7 @@ struct process_block_struct { inode_modified:1; blk64_t num_blocks; blk64_t max_blocks; - e2_blkcnt_t last_block; + blk64_t last_block; e2_blkcnt_t last_init_lblock; e2_blkcnt_t last_db_block; int num_illegal_blocks; @@ -96,13 +106,14 @@ struct process_block_struct { struct problem_context *pctx; ext2fs_block_bitmap fs_meta_blocks; e2fsck_t ctx; - region_t region; + blk64_t next_lblock; struct extent_tree_info eti; }; struct process_inode_block { ext2_ino_t ino; - struct ext2_inode inode; + struct ea_quota ea_ibody_quota; + struct ext2_inode_large inode; }; struct scan_callback_struct { @@ -120,16 +131,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. * @@ -143,10 +144,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; /* @@ -175,45 +176,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; - blk64_t blocks; - ext2_extent_handle_t handle; - struct ext2_extent_info info; - struct ext2fs_extent extent; - int encrypted = 0; 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) @@ -222,62 +196,64 @@ int e2fsck_pass1_check_symlink(ext2_filsys fs, ext2_ino_t ino, return 1; } - blocks = ext2fs_inode_data_blocks2(fs, inode); - if (blocks) { - if (inode->i_flags & EXT4_INLINE_DATA_FL) - return 0; - if ((inode->i_size >= fs->blocksize) || - (blocks != fs->blocksize >> 9) || - (inode->i_block[0] < fs->super->s_first_data_block) || - (inode->i_block[0] >= ext2fs_blocks_count(fs->super))) + if (ext2fs_is_fast_symlink(inode)) { + if (inode->i_flags & EXT4_EXTENTS_FL) return 0; + buf = (char *)inode->i_block; + buflen = sizeof(inode->i_block); + } else { + 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 (io_channel_read_blk64(fs->io, inode->i_block[0], 1, buf)) - return 0; - - if (inode->i_flags & EXT4_ENCRYPT_FL) { - len = ext2fs_le32_to_cpu(*((__u32 *)buf)) + 4; + 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 { - len = strnlen(buf, fs->blocksize); + blk = inode->i_block[0]; + + for (i = 1; i < EXT2_N_BLOCKS; i++) + if (inode->i_block[i]) + return 0; } - if (len == fs->blocksize) - return 0; - } else if (inode->i_flags & EXT4_INLINE_DATA_FL) { - char *inline_buf = NULL; - size_t inline_sz = 0; - if (ext2fs_inline_data_size(fs, ino, &inline_sz)) - return 0; - if (inode->i_size != inline_sz) - return 0; - if (ext2fs_get_mem(inline_sz + 1, &inline_buf)) - return 0; - i = 0; - if (ext2fs_inline_data_get(fs, ino, inode, inline_buf, NULL)) - goto exit_inline; - inline_buf[inline_sz] = 0; - len = strnlen(inline_buf, inline_sz); - if (len != inline_sz) - goto exit_inline; - i = 1; -exit_inline: - ext2fs_free_mem(&inline_buf); - return i; - } else { - if (inode->i_size >= sizeof(inode->i_block)) + if (blk < fs->super->s_first_data_block || + blk >= ext2fs_blocks_count(fs->super)) return 0; - len = strnlen((char *)inode->i_block, sizeof(inode->i_block)); - if (len == sizeof(inode->i_block)) + 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; } @@ -334,21 +310,127 @@ static void check_size(e2fsck_t ctx, struct problem_context *pctx) e2fsck_write_inode(ctx, pctx->ino, pctx->inode, "pass1"); } -static void check_ea_in_inode(e2fsck_t ctx, struct problem_context *pctx) +/* + * For a given size, calculate how many blocks would be charged towards quota. + */ +static blk64_t size_to_quota_blocks(ext2_filsys fs, size_t size) +{ + blk64_t clusters; + + clusters = DIV_ROUND_UP(size, fs->blocksize << fs->cluster_ratio_bits); + return EXT2FS_C2B(fs, clusters); +} + +/* + * Check validity of EA inode. Return 0 if EA inode is valid, otherwise return + * the problem code. + */ +static problem_t check_large_ea_inode(e2fsck_t ctx, + struct ext2_ext_attr_entry *entry, + struct problem_context *pctx, + blk64_t *quota_blocks) +{ + struct ext2_inode inode; + __u32 hash; + errcode_t retval; + + /* Check if inode is within valid range */ + if ((entry->e_value_inum < EXT2_FIRST_INODE(ctx->fs->super)) || + (entry->e_value_inum > ctx->fs->super->s_inodes_count)) { + pctx->num = entry->e_value_inum; + return PR_1_ATTR_VALUE_EA_INODE; + } + + e2fsck_read_inode(ctx, entry->e_value_inum, &inode, "pass1"); + + retval = ext2fs_ext_attr_hash_entry2(ctx->fs, entry, NULL, &hash); + if (retval) { + com_err("check_large_ea_inode", retval, + _("while hashing entry with e_value_inum = %u"), + entry->e_value_inum); + fatal_error(ctx, 0); + } + + if (hash == entry->e_hash) { + *quota_blocks = size_to_quota_blocks(ctx->fs, + entry->e_value_size); + } else { + /* This might be an old Lustre-style ea_inode reference. */ + if (inode.i_mtime == pctx->ino && + inode.i_generation == pctx->inode->i_generation) { + *quota_blocks = 0; + } else { + /* If target inode is also missing EA_INODE flag, + * this is likely to be a bad reference. + */ + if (!(inode.i_flags & EXT4_EA_INODE_FL)) { + pctx->num = entry->e_value_inum; + return PR_1_ATTR_VALUE_EA_INODE; + } else { + pctx->num = entry->e_hash; + return PR_1_ATTR_HASH; + } + } + } + + if (!(inode.i_flags & EXT4_EA_INODE_FL)) { + pctx->num = entry->e_value_inum; + if (fix_problem(ctx, PR_1_ATTR_SET_EA_INODE_FL, pctx)) { + inode.i_flags |= EXT4_EA_INODE_FL; + ext2fs_write_inode(ctx->fs, entry->e_value_inum, + &inode); + } else { + return PR_1_ATTR_NO_EA_INODE_FL; + } + } + return 0; +} + +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; + + for (entry = first; + (void *)entry < end && !EXT2_EXT_IS_LAST_ENTRY(entry); + entry = EXT2_EXT_ATTR_NEXT(entry)) { + if (!entry->e_value_inum) + continue; + if (!ctx->ea_inode_refs) { + pctx->errcode = ea_refcount_create(0, + &ctx->ea_inode_refs); + if (pctx->errcode) { + pctx->num = 4; + fix_problem(ctx, PR_1_ALLOCATE_REFCOUNT, pctx); + ctx->flags |= E2F_FLAG_ABORT; + return; + } + } + ea_refcount_increment(ctx->ea_inode_refs, entry->e_value_inum, + 0); + } +} + +static void check_ea_in_inode(e2fsck_t ctx, struct problem_context *pctx, + struct ea_quota *ea_ibody_quota) { struct ext2_super_block *sb = ctx->fs->super; struct ext2_inode_large *inode; struct ext2_ext_attr_entry *entry; - char *start, *header; + char *start, *header, *end; unsigned int storage_size, remain; problem_t problem = 0; region_t region = 0; + ea_ibody_quota->blocks = 0; + ea_ibody_quota->inodes = 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; header = ((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE + inode->i_extra_isize; + end = header + storage_size; start = header + sizeof(__u32); entry = (struct ext2_ext_attr_entry *) start; @@ -392,38 +474,48 @@ static void check_ea_in_inode(e2fsck_t ctx, struct problem_context *pctx) /* attribute len eats this space */ remain -= EXT2_EXT_ATTR_SIZE(entry->e_name_len); - /* check value size */ - if (entry->e_value_size > remain) { - pctx->num = entry->e_value_size; - problem = PR_1_ATTR_VALUE_SIZE; - goto fix; - } + if (entry->e_value_inum == 0) { + /* check value size */ + if (entry->e_value_size > remain) { + pctx->num = entry->e_value_size; + problem = PR_1_ATTR_VALUE_SIZE; + goto fix; + } - /* e_value_block must be 0 in inode's ea */ - if (entry->e_value_block != 0) { - pctx->num = entry->e_value_block; - problem = PR_1_ATTR_VALUE_BLOCK; - goto fix; - } + if (entry->e_value_size && + region_allocate(region, + sizeof(__u32) + entry->e_value_offs, + EXT2_EXT_ATTR_SIZE( + entry->e_value_size))) { + problem = PR_1_INODE_EA_ALLOC_COLLISION; + goto fix; + } - if (entry->e_value_size && - region_allocate(region, sizeof(__u32) + entry->e_value_offs, - EXT2_EXT_ATTR_SIZE(entry->e_value_size))) { - problem = PR_1_INODE_EA_ALLOC_COLLISION; - goto fix; - } + hash = ext2fs_ext_attr_hash_entry(entry, + start + entry->e_value_offs); - hash = ext2fs_ext_attr_hash_entry(entry, - start + entry->e_value_offs); + /* e_hash may be 0 in older inode's ea */ + if (entry->e_hash != 0 && entry->e_hash != hash) { + pctx->num = entry->e_hash; + problem = PR_1_ATTR_HASH; + goto fix; + } + } else { + blk64_t quota_blocks; - /* e_hash may be 0 in older inode's ea */ - if (entry->e_hash != 0 && entry->e_hash != hash) { - pctx->num = entry->e_hash; - problem = PR_1_ATTR_HASH; - goto fix; + problem = check_large_ea_inode(ctx, entry, pctx, + "a_blocks); + if (problem != 0) + goto fix; + + ea_ibody_quota->blocks += quota_blocks; + ea_ibody_quota->inodes++; } - remain -= entry->e_value_size; + /* If EA value is stored in external inode then it does not + * consume space here */ + if (entry->e_value_inum == 0) + remain -= entry->e_value_size; entry = EXT2_EXT_ATTR_NEXT(entry); } @@ -440,22 +532,46 @@ fix: * it seems like a corruption. it's very unlikely we could repair * EA(s) in automatic fashion -bzzz */ - if (problem == 0 || !fix_problem(ctx, problem, pctx)) + if (problem == 0 || !fix_problem(ctx, problem, pctx)) { + inc_ea_inode_refs(ctx, pctx, + (struct ext2_ext_attr_entry *)start, end); return; + } /* simply remove all possible EA(s) */ *((__u32 *)header) = 0UL; e2fsck_write_inode_full(ctx, pctx->ino, pctx->inode, EXT2_INODE_SIZE(sb), "pass1"); + ea_ibody_quota->blocks = 0; + ea_ibody_quota->inodes = 0; +} + +static int check_inode_extra_negative_epoch(__u32 xtime, __u32 extra) { + return (xtime & (1U << 31)) != 0 && + (extra & EXT4_EPOCH_MASK) == EXT4_EPOCH_MASK; } -static void check_inode_extra_space(e2fsck_t ctx, struct problem_context *pctx) +#define CHECK_INODE_EXTRA_NEGATIVE_EPOCH(inode, xtime) \ + check_inode_extra_negative_epoch(inode->i_##xtime, \ + inode->i_##xtime##_extra) + +/* When today's date is earlier than 2242, we assume that atimes, + * ctimes, crtimes, and mtimes with years in the range 2310..2378 are + * actually pre-1970 dates mis-encoded. + */ +#define EXT4_EXTRA_NEGATIVE_DATE_CUTOFF 2 * (1LL << 32) + +static void check_inode_extra_space(e2fsck_t ctx, struct problem_context *pctx, + struct ea_quota *ea_ibody_quota) { struct ext2_super_block *sb = ctx->fs->super; struct ext2_inode_large *inode; __u32 *eamagic; int min, max; + ea_ibody_quota->blocks = 0; + ea_ibody_quota->inodes = 0; + inode = (struct ext2_inode_large *) pctx->inode; if (EXT2_INODE_SIZE(sb) == EXT2_GOOD_OLD_INODE_SIZE) { /* this isn't large inode. so, nothing to check */ @@ -474,21 +590,56 @@ static void check_inode_extra_space(e2fsck_t ctx, struct problem_context *pctx) * implementations should never allow i_extra_isize to be 0 */ if (inode->i_extra_isize && - (inode->i_extra_isize < min || inode->i_extra_isize > max)) { + (inode->i_extra_isize < min || inode->i_extra_isize > max || + inode->i_extra_isize & 3)) { if (!fix_problem(ctx, PR_1_EXTRA_ISIZE, pctx)) return; - inode->i_extra_isize = min; + if (inode->i_extra_isize < min || inode->i_extra_isize > max) + inode->i_extra_isize = sb->s_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"); - return; } + /* check if there is no place for an EA header */ + if (inode->i_extra_isize >= max - sizeof(__u32)) + return; + eamagic = (__u32 *) (((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE + inode->i_extra_isize); if (*eamagic == EXT2_EXT_ATTR_MAGIC) { /* it seems inode has an extended attribute(s) in body */ - check_ea_in_inode(ctx, pctx); + check_ea_in_inode(ctx, pctx, ea_ibody_quota); + } + + /* + * If the inode's extended atime (ctime, crtime, mtime) is stored in + * the old, invalid format, repair it. + */ + if (((sizeof(time_t) <= 4) || + (((sizeof(time_t) > 4) && + ctx->now < EXT4_EXTRA_NEGATIVE_DATE_CUTOFF))) && + (CHECK_INODE_EXTRA_NEGATIVE_EPOCH(inode, atime) || + CHECK_INODE_EXTRA_NEGATIVE_EPOCH(inode, ctime) || + 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)) + return; + + if (CHECK_INODE_EXTRA_NEGATIVE_EPOCH(inode, atime)) + inode->i_atime_extra &= ~EXT4_EPOCH_MASK; + if (CHECK_INODE_EXTRA_NEGATIVE_EPOCH(inode, ctime)) + inode->i_ctime_extra &= ~EXT4_EPOCH_MASK; + if (CHECK_INODE_EXTRA_NEGATIVE_EPOCH(inode, crtime)) + 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"); } + } /* @@ -541,14 +692,12 @@ static void check_is_really_dir(e2fsck_t ctx, struct problem_context *pctx, * data. If it's true, we will treat it as a directory. */ - extent_fs = (ctx->fs->super->s_feature_incompat & - EXT3_FEATURE_INCOMPAT_EXTENTS); - inlinedata_fs = (ctx->fs->super->s_feature_incompat & - EXT4_FEATURE_INCOMPAT_INLINE_DATA); + extent_fs = ext2fs_has_feature_extents(ctx->fs->super); + inlinedata_fs = ext2fs_has_feature_inline_data(ctx->fs->super); if (inlinedata_fs && (inode->i_flags & EXT4_INLINE_DATA_FL)) { size_t size; __u32 dotdot; - unsigned int rec_len; + unsigned int rec_len2; struct ext2_dir_entry de; if (ext2fs_inline_data_size(ctx->fs, pctx->ino, &size)) @@ -569,14 +718,14 @@ static void check_is_really_dir(e2fsck_t ctx, struct problem_context *pctx, dotdot = ext2fs_le32_to_cpu(dotdot); de.inode = ext2fs_le32_to_cpu(de.inode); de.rec_len = ext2fs_le16_to_cpu(de.rec_len); - ext2fs_get_rec_len(ctx->fs, &de, &rec_len); + ext2fs_get_rec_len(ctx->fs, &de, &rec_len2); if (dotdot >= ctx->fs->super->s_inodes_count || (dotdot < EXT2_FIRST_INO(ctx->fs->super) && dotdot != EXT2_ROOT_INO) || de.inode >= ctx->fs->super->s_inodes_count || (de.inode < EXT2_FIRST_INO(ctx->fs->super) && de.inode != 0) || - rec_len > EXT4_MIN_INLINE_DATA_SIZE - + rec_len2 > EXT4_MIN_INLINE_DATA_SIZE - EXT4_INLINE_DATA_DOTDOT_SIZE) return; /* device files never have a "system.data" entry */ @@ -653,10 +802,12 @@ isdir: } } -void e2fsck_setup_tdb_icount(e2fsck_t ctx, int flags, - ext2_icount_t *ret) +extern errcode_t e2fsck_setup_icount(e2fsck_t ctx, const char *icount_name, + int flags, ext2_icount_t hint, + ext2_icount_t *ret) { unsigned int threshold; + unsigned int save_type; ext2_ino_t num_dirs; errcode_t retval; char *tdb_dir; @@ -675,13 +826,20 @@ void e2fsck_setup_tdb_icount(e2fsck_t ctx, int flags, if (retval) num_dirs = 1024; /* Guess */ - if (!enable || !tdb_dir || access(tdb_dir, W_OK) || - (threshold && num_dirs <= threshold)) - return; - - retval = ext2fs_create_icount_tdb(ctx->fs, tdb_dir, flags, ret); - if (retval) - *ret = 0; + if (enable && tdb_dir && !access(tdb_dir, W_OK) && + (!threshold || num_dirs > threshold)) { + retval = ext2fs_create_icount_tdb(ctx->fs, tdb_dir, + flags, ret); + if (retval == 0) + return 0; + } + e2fsck_set_bitmap_type(ctx->fs, EXT2FS_BMAP64_RBTREE, icount_name, + &save_type); + if (ctx->options & E2F_OPT_ICOUNT_FULLMAP) + flags |= EXT2_ICOUNT_OPT_FULLMAP; + retval = ext2fs_create_icount2(ctx->fs, flags, 0, hint, ret); + ctx->fs->default_bitmap_type = save_type; + return retval; } static errcode_t recheck_bad_inode_checksum(ext2_filsys fs, ext2_ino_t ino, @@ -831,10 +989,8 @@ static int fix_inline_data_extents_file(e2fsck_t ctx, int dirty = 0; /* Both feature flags not set? Just run the regular checks */ - if (!EXT2_HAS_INCOMPAT_FEATURE(fs->super, - EXT3_FEATURE_INCOMPAT_EXTENTS) && - !EXT2_HAS_INCOMPAT_FEATURE(fs->super, - EXT4_FEATURE_INCOMPAT_INLINE_DATA)) + if (!ext2fs_has_feature_extents(fs->super) && + !ext2fs_has_feature_inline_data(fs->super)) return 0; /* Clear both flags if it's a special file */ @@ -955,6 +1111,41 @@ out: } } +/* + * Check if the passed ino is one of the used superblock quota inodes. + * + * Before the quota inodes were journaled, older superblock quota inodes + * were just regular files in the filesystem and not reserved inodes. This + * checks if the passed ino is one of the s_*_quota_inum superblock fields, + * which may not always be the same as the EXT4_*_QUOTA_INO fields. + */ +static int quota_inum_is_super(struct ext2_super_block *sb, ext2_ino_t ino) +{ + enum quota_type qtype; + + for (qtype = 0; qtype < MAXQUOTAS; qtype++) + if (*quota_sb_inump(sb, qtype) == ino) + return 1; + + return 0; +} + +/* + * Check if the passed ino is one of the reserved quota inodes. + * This checks if the inode number is one of the reserved EXT4_*_QUOTA_INO + * inodes. These inodes may or may not be in use by the quota feature. + */ +static int quota_inum_is_reserved(ext2_filsys fs, ext2_ino_t ino) +{ + enum quota_type qtype; + + for (qtype = 0; qtype < MAXQUOTAS; qtype++) + if (quota_type2inum(qtype, fs->super) == ino) + return 1; + + return 0; +} + void e2fsck_pass1(e2fsck_t ctx) { int i; @@ -972,13 +1163,15 @@ void e2fsck_pass1(e2fsck_t ctx) struct scan_callback_struct scan_struct; struct ext2_super_block *sb = ctx->fs->super; const char *old_op; - unsigned int save_type; - 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); + 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; + struct ea_quota ea_ibody_quota; init_resource_track(&rtrack, ctx->fs->io); clear_problem_context(&pctx); @@ -993,7 +1186,7 @@ void e2fsck_pass1(e2fsck_t ctx) if (!(ctx->options & E2F_OPT_PREEN)) fix_problem(ctx, PR_1_PASS_HEADER, &pctx); - if ((fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) && + if (ext2fs_has_feature_dir_index(fs->super) && !(ctx->options & E2F_OPT_NO)) { if (ext2fs_u32_list_create(&ctx->dirs_to_hash, 50)) ctx->dirs_to_hash = 0; @@ -1014,10 +1207,10 @@ void e2fsck_pass1(e2fsck_t ctx) } #undef EXT2_BPP - imagic_fs = (sb->s_feature_compat & EXT2_FEATURE_COMPAT_IMAGIC_INODES); - extent_fs = (sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_EXTENTS); - inlinedata_fs = (sb->s_feature_incompat & - EXT4_FEATURE_INCOMPAT_INLINE_DATA); + 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 @@ -1069,22 +1262,32 @@ void e2fsck_pass1(e2fsck_t ctx) ctx->flags |= E2F_FLAG_ABORT; return; } - e2fsck_setup_tdb_icount(ctx, 0, &ctx->inode_link_info); - if (!ctx->inode_link_info) { - e2fsck_set_bitmap_type(fs, EXT2FS_BMAP64_RBTREE, - "inode_link_info", &save_type); - pctx.errcode = ext2fs_create_icount2(fs, 0, 0, 0, - &ctx->inode_link_info); - fs->default_bitmap_type = save_type; + 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) { fix_problem(ctx, PR_1_ALLOCATE_ICOUNT, &pctx); ctx->flags |= E2F_FLAG_ABORT; return; } + bufsize = inode_size; + if (bufsize < sizeof(struct ext2_inode_large)) + bufsize = sizeof(struct ext2_inode_large); inode = (struct ext2_inode *) - e2fsck_allocate_memory(ctx, inode_size, "scratch inode"); + e2fsck_allocate_memory(ctx, bufsize, "scratch inode"); inodes_to_process = (struct process_inode_block *) e2fsck_allocate_memory(ctx, @@ -1145,13 +1348,15 @@ 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; - if ((fs->super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_MMP) && + if (ext2fs_has_feature_mmp(fs->super) && 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, @@ -1165,14 +1370,14 @@ 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) - return; + goto endit; if (pctx.errcode == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE) { /* * If badblocks says badblocks is bad, offer to clear @@ -1193,17 +1398,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); @@ -1233,6 +1430,34 @@ void e2fsck_pass1(e2fsck_t ctx) } failed_csum = pctx.errcode != 0; + /* + * Check for inodes who might have been part of the + * orphaned list linked list. They should have gotten + * dealt with by now, unless the list had somehow been + * corrupted. + * + * FIXME: In the future, inodes which are still in use + * (and which are therefore) pending truncation should + * be handled specially. Right now we just clear the + * dtime field, and the normal e2fsck handling of + * inodes where i_size and the inode blocks are + * inconsistent is to fix i_size, instead of releasing + * the extra blocks. This won't catch the inodes that + * was at the end of the orphan list, but it's better + * than nothing. The right answer is that there + * shouldn't be any bugs in the orphan list handling. :-) + */ + if (inode->i_dtime && low_dtime_check && + inode->i_dtime < ctx->fs->super->s_inodes_count) { + if (fix_problem(ctx, PR_1_LOW_DTIME, &pctx)) { + inode->i_dtime = inode->i_links_count ? + 0 : ctx->now; + e2fsck_write_inode(ctx, ino, inode, + "pass1"); + failed_csum = 0; + } + } + if (inode->i_links_count) { pctx.errcode = ext2fs_icount_store(ctx->inode_link_info, ino, inode->i_links_count); @@ -1242,6 +1467,28 @@ void e2fsck_pass1(e2fsck_t ctx) ctx->flags |= E2F_FLAG_ABORT; goto endit; } + } else if ((ino >= EXT2_FIRST_INODE(fs->super)) && + !quota_inum_is_reserved(fs, ino)) { + if (!inode->i_dtime && inode->i_mode) { + if (fix_problem(ctx, + PR_1_ZERO_DTIME, &pctx)) { + inode->i_dtime = ctx->now; + e2fsck_write_inode(ctx, ino, inode, + "pass1"); + failed_csum = 0; + } + } + FINISH_INODE_LOOP(ctx, ino, &pctx, failed_csum); + 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? */ @@ -1261,11 +1508,10 @@ 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)) { - sb->s_feature_incompat |= - EXT4_FEATURE_INCOMPAT_INLINE_DATA; + ext2fs_set_feature_inline_data(sb); ext2fs_mark_super_dirty(fs); inlinedata_fs = 1; } else if (fix_problem(ctx, PR_1_INLINE_DATA_SET, &pctx)) { @@ -1277,7 +1523,6 @@ void e2fsck_pass1(e2fsck_t ctx) /* Test for inline data flag but no attr */ if ((inode->i_flags & EXT4_INLINE_DATA_FL) && inlinedata_fs && - EXT2_I_SIZE(inode) > EXT4_MIN_INLINE_DATA_SIZE && (ino >= EXT2_FIRST_INODE(fs->super))) { size_t size = 0; errcode_t err; @@ -1308,18 +1553,19 @@ 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)) { - err = ext2fs_inode_size_set(fs, inode, - sizeof(inode->i_block)); + err = ext2fs_inode_size_set(fs, inode, 0); if (err) { pctx.errcode = err; ctx->flags |= E2F_FLAG_ABORT; goto endit; } - if (LINUX_S_ISLNK(inode->i_mode)) - inode->i_flags &= ~EXT4_INLINE_DATA_FL; + inode->i_flags &= ~EXT4_INLINE_DATA_FL; + memset(&inode->i_block, 0, + sizeof(inode->i_block)); e2fsck_write_inode(ctx, ino, inode, "pass1"); failed_csum = 0; @@ -1354,7 +1600,7 @@ void e2fsck_pass1(e2fsck_t ctx) if ((ext2fs_extent_header_verify(inode->i_block, sizeof(inode->i_block)) == 0) && fix_problem(ctx, PR_1_EXTENT_FEATURE, &pctx)) { - sb->s_feature_incompat |= EXT3_FEATURE_INCOMPAT_EXTENTS; + ext2fs_set_feature_extents(sb); ext2fs_mark_super_dirty(fs); extent_fs = 1; } else if (fix_problem(ctx, PR_1_EXTENTS_SET, &pctx)) { @@ -1456,7 +1702,7 @@ void e2fsck_pass1(e2fsck_t ctx) /* * Make sure the root inode is a directory; if * not, offer to clear it. It will be - * regnerated in pass #3. + * regenerated in pass #3. */ if (!LINUX_S_ISDIR(inode->i_mode)) { if (fix_problem(ctx, PR_1_ROOT_NO_DIR, &pctx)) @@ -1490,7 +1736,7 @@ void e2fsck_pass1(e2fsck_t ctx) "pass1"); failed_csum = 0; } - check_blocks(ctx, &pctx, block_buf); + check_blocks(ctx, &pctx, block_buf, NULL); FINISH_INODE_LOOP(ctx, ino, &pctx, failed_csum); continue; } @@ -1505,13 +1751,10 @@ void e2fsck_pass1(e2fsck_t ctx) inode_size, "pass1"); failed_csum = 0; } - } else if ((ino == EXT4_USR_QUOTA_INO) || - (ino == EXT4_GRP_QUOTA_INO)) { + } else if (quota_inum_is_reserved(fs, ino)) { ext2fs_mark_inode_bitmap2(ctx->inode_used_map, ino); - if ((fs->super->s_feature_ro_compat & - EXT4_FEATURE_RO_COMPAT_QUOTA) && - ((fs->super->s_usr_quota_inum == ino) || - (fs->super->s_grp_quota_inum == ino))) { + if (ext2fs_has_feature_quota(fs->super) && + quota_inum_is_super(fs->super, ino)) { if (!LINUX_S_ISREG(inode->i_mode) && fix_problem(ctx, PR_1_QUOTA_BAD_MODE, &pctx)) { @@ -1520,7 +1763,7 @@ void e2fsck_pass1(e2fsck_t ctx) "pass1"); failed_csum = 0; } - check_blocks(ctx, &pctx, block_buf); + check_blocks(ctx, &pctx, block_buf, NULL); FINISH_INODE_LOOP(ctx, ino, &pctx, failed_csum); continue; } @@ -1558,53 +1801,12 @@ void e2fsck_pass1(e2fsck_t ctx) failed_csum = 0; } } - check_blocks(ctx, &pctx, block_buf); + check_blocks(ctx, &pctx, block_buf, NULL); FINISH_INODE_LOOP(ctx, ino, &pctx, failed_csum); continue; } - /* - * Check for inodes who might have been part of the - * orphaned list linked list. They should have gotten - * dealt with by now, unless the list had somehow been - * corrupted. - * - * FIXME: In the future, inodes which are still in use - * (and which are therefore) pending truncation should - * be handled specially. Right now we just clear the - * dtime field, and the normal e2fsck handling of - * inodes where i_size and the inode blocks are - * inconsistent is to fix i_size, instead of releasing - * the extra blocks. This won't catch the inodes that - * was at the end of the orphan list, but it's better - * than nothing. The right answer is that there - * shouldn't be any bugs in the orphan list handling. :-) - */ - if (inode->i_dtime && low_dtime_check && - inode->i_dtime < ctx->fs->super->s_inodes_count) { - if (fix_problem(ctx, PR_1_LOW_DTIME, &pctx)) { - inode->i_dtime = inode->i_links_count ? - 0 : ctx->now; - e2fsck_write_inode(ctx, ino, inode, - "pass1"); - failed_csum = 0; - } - } - - /* - * This code assumes that deleted inodes have - * i_links_count set to 0. - */ if (!inode->i_links_count) { - if (!inode->i_dtime && inode->i_mode) { - if (fix_problem(ctx, - PR_1_ZERO_DTIME, &pctx)) { - inode->i_dtime = ctx->now; - e2fsck_write_inode(ctx, ino, inode, - "pass1"); - failed_csum = 0; - } - } FINISH_INODE_LOOP(ctx, ino, &pctx, failed_csum); continue; } @@ -1637,16 +1839,15 @@ void e2fsck_pass1(e2fsck_t ctx) } if (inode->i_faddr || frag || fsize || - (LINUX_S_ISDIR(inode->i_mode) && inode->i_dir_acl)) + (!ext2fs_has_feature_largedir(fs->super) && + (LINUX_S_ISDIR(inode->i_mode) && inode->i_size_high))) mark_inode_bad(ctx, ino); - if ((fs->super->s_creator_os == EXT2_OS_LINUX) && - !(fs->super->s_feature_incompat & - EXT4_FEATURE_INCOMPAT_64BIT) && + 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); - if ((fs->super->s_creator_os == EXT2_OS_LINUX) && - !(fs->super->s_feature_ro_compat & - EXT4_FEATURE_RO_COMPAT_HUGE_FILE) && + 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); if (inode->i_flags & EXT2_IMAGIC_FL) { @@ -1665,7 +1866,7 @@ void e2fsck_pass1(e2fsck_t ctx) } } - check_inode_extra_space(ctx, &pctx); + check_inode_extra_space(ctx, &pctx, &ea_ibody_quota); check_is_really_dir(ctx, &pctx, block_buf); /* @@ -1681,12 +1882,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++; @@ -1710,9 +1918,10 @@ void e2fsck_pass1(e2fsck_t ctx) if (inode->i_flags & EXT4_INLINE_DATA_FL) { FINISH_INODE_LOOP(ctx, ino, &pctx, failed_csum); continue; - } else if (ext2fs_inode_data_blocks(fs, inode) == 0) { + } else if (ext2fs_is_fast_symlink(inode)) { ctx->fs_fast_symlinks_count++; - check_blocks(ctx, &pctx, block_buf); + check_blocks(ctx, &pctx, block_buf, + &ea_ibody_quota); FINISH_INODE_LOOP(ctx, ino, &pctx, failed_csum); continue; } @@ -1746,11 +1955,18 @@ void e2fsck_pass1(e2fsck_t ctx) inode->i_block[EXT2_DIND_BLOCK] || inode->i_block[EXT2_TIND_BLOCK] || ext2fs_file_acl_block(fs, inode))) { - inodes_to_process[process_inode_count].ino = ino; - inodes_to_process[process_inode_count].inode = *inode; + struct process_inode_block *itp; + + itp = &inodes_to_process[process_inode_count]; + itp->ino = ino; + itp->ea_ibody_quota = ea_ibody_quota; + if (inode_size < sizeof(struct ext2_inode_large)) + memcpy(&itp->inode, inode, inode_size); + else + memcpy(&itp->inode, inode, sizeof(itp->inode)); process_inode_count++; } else - check_blocks(ctx, &pctx, block_buf); + check_blocks(ctx, &pctx, block_buf, &ea_ibody_quota); FINISH_INODE_LOOP(ctx, ino, &pctx, failed_csum); @@ -1788,6 +2004,16 @@ void e2fsck_pass1(e2fsck_t ctx) ctx->refcount_extra = 0; } + if (ctx->ea_block_quota_blocks) { + ea_refcount_free(ctx->ea_block_quota_blocks); + ctx->ea_block_quota_blocks = 0; + } + + if (ctx->ea_block_quota_inodes) { + ea_refcount_free(ctx->ea_block_quota_inodes); + ctx->ea_block_quota_inodes = 0; + } + if (ctx->invalid_bitmaps) handle_fs_bad_blocks(ctx); @@ -1797,6 +2023,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); @@ -1826,10 +2055,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); @@ -1838,9 +2079,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); @@ -1857,6 +2099,8 @@ endit: if ((ctx->flags & E2F_FLAG_SIGNAL_MASK) == 0) print_resource_track(ctx, _("Pass 1"), &rtrack, ctx->fs->io); + else + ctx->invalid_bitmaps++; } #undef FINISH_INODE_LOOP @@ -1908,7 +2152,8 @@ static void process_inodes(e2fsck_t ctx, char *block_buf) sizeof(struct process_inode_block), process_inode_cmp); clear_problem_context(&pctx); for (i=0; i < process_inode_count; i++) { - pctx.inode = ctx->stashed_inode = &inodes_to_process[i].inode; + pctx.inode = ctx->stashed_inode = + (struct ext2_inode *) &inodes_to_process[i].inode; pctx.ino = ctx->stashed_ino = inodes_to_process[i].ino; #if 0 @@ -1917,7 +2162,8 @@ static void process_inodes(e2fsck_t ctx, char *block_buf) sprintf(buf, _("reading indirect blocks of inode %u"), pctx.ino); ehandler_operation(buf); - check_blocks(ctx, &pctx, block_buf); + check_blocks(ctx, &pctx, block_buf, + &inodes_to_process[i].ea_ibody_quota); if (ctx->flags & E2F_FLAG_SIGNAL_MASK) break; } @@ -1946,8 +2192,8 @@ static EXT2_QSORT_TYPE process_inode_cmp(const void *a, const void *b) * inodes, so it's OK to pass NULL to * ext2fs_file_acl_block() here. */ - ret = ext2fs_file_acl_block(0, &(ib_a->inode)) - - ext2fs_file_acl_block(0, &(ib_b->inode)); + ret = ext2fs_file_acl_block(0, ext2fs_const_inode(&ib_a->inode)) - + ext2fs_file_acl_block(0, ext2fs_const_inode(&ib_b->inode)); if (ret == 0) ret = ib_a->ino - ib_b->ino; return ret; @@ -1977,20 +2223,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; } @@ -2049,6 +2295,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"), @@ -2069,14 +2319,21 @@ static _INLINE_ void mark_block_used(e2fsck_t ctx, blk64_t block) } } +/* + * When cluster size is greater than one block, it is caller's responsibility + * to make sure block parameter starts at a cluster boundary. + */ 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++); + else { + unsigned int i; + + for (i = 0; i < num; i += EXT2FS_CLUSTER_RATIO(ctx->fs)) + mark_block_used(ctx, block + i); + } } /* @@ -2094,7 +2351,7 @@ static void adjust_extattr_refcount(e2fsck_t ctx, ext2_refcount_t refcount, ext2_filsys fs = ctx->fs; blk64_t blk; __u32 should_be; - int count; + ea_value_t count; clear_problem_context(&pctx); @@ -2111,7 +2368,7 @@ static void adjust_extattr_refcount(e2fsck_t ctx, ext2_refcount_t refcount, } header = (struct ext2_ext_attr_header *) block_buf; pctx.blkcount = header->h_refcount; - should_be = header->h_refcount + adjust_sign * count; + should_be = header->h_refcount + adjust_sign * (int)count; pctx.num = should_be; if (fix_problem(ctx, PR_1_EXTATTR_REFCOUNT, &pctx)) { header->h_refcount = should_be; @@ -2131,7 +2388,7 @@ static void adjust_extattr_refcount(e2fsck_t ctx, ext2_refcount_t refcount, * Handle processing the extended attribute blocks */ static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx, - char *block_buf) + char *block_buf, struct ea_quota *ea_block_quota) { ext2_filsys fs = ctx->fs; ext2_ino_t ino = pctx->ino; @@ -2139,11 +2396,15 @@ static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx, blk64_t blk; char * end; struct ext2_ext_attr_header *header; - struct ext2_ext_attr_entry *entry; - int count; + struct ext2_ext_attr_entry *first, *entry; + blk64_t quota_blocks = EXT2FS_C2B(fs, 1); + __u64 quota_inodes = 0; region_t region = 0; int failed_csum = 0; + ea_block_quota->blocks = 0; + ea_block_quota->inodes = 0; + blk = ext2fs_file_acl_block(fs, inode); if (blk == 0) return 0; @@ -2155,7 +2416,7 @@ static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx, * Or if the extended attribute block is an invalid block, * then the inode is also corrupted. */ - if (!(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR) || + 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); @@ -2194,6 +2455,20 @@ static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx, /* Have we seen this EA block before? */ if (ext2fs_fast_test_block_bitmap2(ctx->block_ea_map, blk)) { + ea_block_quota->blocks = EXT2FS_C2B(fs, 1); + ea_block_quota->inodes = 0; + + if (ctx->ea_block_quota_blocks) { + ea_refcount_fetch(ctx->ea_block_quota_blocks, blk, + "a_blocks); + if (quota_blocks) + ea_block_quota->blocks = quota_blocks; + } + + if (ctx->ea_block_quota_inodes) + ea_refcount_fetch(ctx->ea_block_quota_inodes, blk, + &ea_block_quota->inodes); + if (ea_refcount_decrement(ctx->refcount, blk, 0) == 0) return 1; /* Ooops, this EA was referenced more than it stated */ @@ -2257,8 +2532,9 @@ static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx, goto clear_extattr; } - entry = (struct ext2_ext_attr_entry *)(header+1); + first = (struct ext2_ext_attr_entry *)(header+1); end = block_buf + fs->blocksize; + entry = first; while ((char *)entry < end && *(__u32 *)entry) { __u32 hash; @@ -2276,30 +2552,41 @@ static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx, goto clear_extattr; break; } - if (entry->e_value_block != 0) { - if (fix_problem(ctx, PR_1_EA_BAD_VALUE, pctx)) - goto clear_extattr; - } - if (entry->e_value_offs + entry->e_value_size > fs->blocksize) { - if (fix_problem(ctx, PR_1_EA_BAD_VALUE, pctx)) - goto clear_extattr; - break; - } - if (entry->e_value_size && - region_allocate(region, entry->e_value_offs, - EXT2_EXT_ATTR_SIZE(entry->e_value_size))) { - if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION, pctx)) - goto clear_extattr; - } + if (entry->e_value_inum == 0) { + if (entry->e_value_offs + entry->e_value_size > + fs->blocksize) { + if (fix_problem(ctx, PR_1_EA_BAD_VALUE, pctx)) + goto clear_extattr; + break; + } + if (entry->e_value_size && + region_allocate(region, entry->e_value_offs, + EXT2_EXT_ATTR_SIZE(entry->e_value_size))) { + if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION, + pctx)) + goto clear_extattr; + } - hash = ext2fs_ext_attr_hash_entry(entry, block_buf + - entry->e_value_offs); + hash = ext2fs_ext_attr_hash_entry(entry, block_buf + + entry->e_value_offs); + + if (entry->e_hash != hash) { + pctx->num = entry->e_hash; + if (fix_problem(ctx, PR_1_ATTR_HASH, pctx)) + goto clear_extattr; + entry->e_hash = hash; + } + } else { + problem_t problem; + blk64_t entry_quota_blocks; - if (entry->e_hash != hash) { - pctx->num = entry->e_hash; - if (fix_problem(ctx, PR_1_ATTR_HASH, pctx)) + problem = check_large_ea_inode(ctx, entry, pctx, + &entry_quota_blocks); + if (problem && fix_problem(ctx, problem, pctx)) goto clear_extattr; - entry->e_hash = hash; + + quota_blocks += entry_quota_blocks; + quota_inodes++; } entry = EXT2_EXT_ATTR_NEXT(entry); @@ -2322,9 +2609,40 @@ static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx, return 0; } - count = header->h_refcount - 1; - if (count) - ea_refcount_store(ctx->refcount, blk, count); + if (quota_blocks != EXT2FS_C2B(fs, 1U)) { + if (!ctx->ea_block_quota_blocks) { + pctx->errcode = ea_refcount_create(0, + &ctx->ea_block_quota_blocks); + if (pctx->errcode) { + pctx->num = 3; + goto refcount_fail; + } + } + ea_refcount_store(ctx->ea_block_quota_blocks, blk, + quota_blocks); + } + + if (quota_inodes) { + if (!ctx->ea_block_quota_inodes) { + pctx->errcode = ea_refcount_create(0, + &ctx->ea_block_quota_inodes); + if (pctx->errcode) { + pctx->num = 4; +refcount_fail: + fix_problem(ctx, PR_1_ALLOCATE_REFCOUNT, pctx); + ctx->flags |= E2F_FLAG_ABORT; + return 0; + } + } + + ea_refcount_store(ctx->ea_block_quota_inodes, blk, + quota_inodes); + } + ea_block_quota->blocks = quota_blocks; + ea_block_quota->inodes = quota_inodes; + + inc_ea_inode_refs(ctx, pctx, first, end); + ea_refcount_store(ctx->refcount, blk, header->h_refcount - 1); mark_block_used(ctx, blk); ext2fs_fast_mark_block_bitmap2(ctx->block_ea_map, blk); return 1; @@ -2349,7 +2667,7 @@ static int handle_htree(e2fsck_t ctx, struct problem_context *pctx, if ((!LINUX_S_ISDIR(inode->i_mode) && fix_problem(ctx, PR_1_HTREE_NODIR, pctx)) || - (!(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) && + (!ext2fs_has_feature_dir_index(fs->super) && fix_problem(ctx, PR_1_HTREE_SET, pctx))) return 1; @@ -2380,18 +2698,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 > 1) && + /* 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; } @@ -2440,7 +2789,7 @@ void e2fsck_clear_inode(e2fsck_t ctx, ext2_ino_t ino, * line up. */ static int has_unaligned_cluster_map(e2fsck_t ctx, - blk64_t last_pblk, e2_blkcnt_t last_lblk, + blk64_t last_pblk, blk64_t last_lblk, blk64_t pblk, blk64_t lblk) { blk64_t cluster_mask; @@ -2476,8 +2825,7 @@ static void scan_extent_node(e2fsck_t ctx, struct problem_context *pctx, { struct ext2fs_extent extent; blk64_t blk, last_lblk; - e2_blkcnt_t blockcnt; - unsigned int i; + unsigned int i, n; int is_dir, is_leaf; problem_t problem; struct ext2_extent_info info; @@ -2529,8 +2877,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; @@ -2538,14 +2887,25 @@ 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) > - (1 << (21 - ctx->fs->super->s_log_block_size)))) + (1U << (21 - ctx->fs->super->s_log_block_size)))) problem = PR_1_TOOBIG_DIR; - if (is_leaf && problem == 0 && extent.e_len > 0 && - region_allocate(pb->region, extent.e_lblk, extent.e_len)) - problem = PR_1_EXTENT_COLLISION; + 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, + (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; + else if (extent.e_lblk + extent.e_len > pb->next_lblock) + pb->next_lblock = extent.e_lblk + extent.e_len; + } /* * Uninitialized blocks in a directory? Clear the flag and @@ -2562,7 +2922,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)) { @@ -2625,6 +2998,7 @@ report_problem: * will reallocate the block; then we can try again. */ if (pb->ino != EXT2_RESIZE_INO && + extent.e_pblk < ctx->fs->super->s_blocks_count && ext2fs_test_block_bitmap2(ctx->block_metadata_map, extent.e_pblk)) { next_try_repairs = 0; @@ -2632,7 +3006,8 @@ report_problem: fix_problem(ctx, PR_1_CRITICAL_METADATA_COLLISION, pctx); - ctx->flags |= E2F_FLAG_RESTART_LATER; + if ((ctx->options & E2F_OPT_NO) == 0) + ctx->flags |= E2F_FLAG_RESTART_LATER; } pctx->errcode = ext2fs_extent_get(ehandle, EXT2_EXTENT_DOWN, &extent); @@ -2650,7 +3025,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; @@ -2709,7 +3089,7 @@ report_problem: * pass 3 allocating empty directory blocks to fill the hole. */ if (try_repairs && is_dir && - pb->last_block + 1 < (e2_blkcnt_t)extent.e_lblk) { + pb->last_block + 1 < extent.e_lblk) { blk64_t new_lblk; new_lblk = pb->last_block + 1; @@ -2742,50 +3122,29 @@ report_problem: } } alloc_later: - 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); - if (pctx->errcode) { - pctx->blk = 0; - pctx->num = pb->last_db_block; - 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 && - (EXT2FS_B2C(ctx->fs, blk) == - EXT2FS_B2C(ctx->fs, pb->previous_block)) && - (blk & EXT2FS_CLUSTER_MASK(ctx->fs)) == - ((unsigned) blockcnt & EXT2FS_CLUSTER_MASK(ctx->fs)))) { - mark_block_used(ctx, blk); - pb->num_blocks++; - } - if (has_unaligned_cluster_map(ctx, pb->previous_block, - pb->last_block, blk, - blockcnt)) { - pctx->blk = blockcnt; - pctx->blk2 = blk; - fix_problem(ctx, PR_1_MISALIGNED_CLUSTER, pctx); - mark_block_used(ctx, blk); - mark_block_used(ctx, blk); + if (is_dir) { + while (++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); + if (pctx->errcode) { + pctx->blk = 0; + pctx->num = pb->last_db_block; + goto failed_add_dir_block; + } } - pb->last_block = blockcnt; - pb->previous_block = blk; - if (is_dir) { - pctx->errcode = ext2fs_add_dir_block2(ctx->fs->dblist, pctx->ino, blk, blockcnt); + for (i = 0; i < extent.e_len; i++) { + pctx->errcode = ext2fs_add_dir_block2( + ctx->fs->dblist, + pctx->ino, + extent.e_pblk + i, + extent.e_lblk + i); if (pctx->errcode) { - pctx->blk = blk; - pctx->num = blockcnt; + pctx->blk = extent.e_pblk + i; + pctx->num = extent.e_lblk + i; failed_add_dir_block: fix_problem(ctx, PR_1_ADD_DBLOCK, pctx); /* Should never get here */ @@ -2793,9 +3152,46 @@ alloc_later: return; } } + if (extent.e_len > 0) + pb->last_db_block = extent.e_lblk + extent.e_len - 1; + } + if (has_unaligned_cluster_map(ctx, pb->previous_block, + pb->last_block, + extent.e_pblk, + extent.e_lblk)) { + for (i = 0; i < extent.e_len; i++) { + pctx->blk = extent.e_lblk + i; + pctx->blk2 = extent.e_pblk + i; + fix_problem(ctx, PR_1_MISALIGNED_CLUSTER, pctx); + mark_block_used(ctx, extent.e_pblk + i); + mark_block_used(ctx, extent.e_pblk + i); + } + } + + /* + * Check whether first cluster got marked in previous iteration. + */ + if (ctx->fs->cluster_ratio_bits && + pb->previous_block && + (EXT2FS_B2C(ctx->fs, extent.e_pblk) == + EXT2FS_B2C(ctx->fs, pb->previous_block))) + /* Set blk to the beginning of next cluster. */ + blk = EXT2FS_C2B( + ctx->fs, + EXT2FS_B2C(ctx->fs, extent.e_pblk) + 1); + else + /* Set blk to the beginning of current cluster. */ + blk = EXT2FS_C2B(ctx->fs, + EXT2FS_B2C(ctx->fs, extent.e_pblk)); + + if (blk < extent.e_pblk + extent.e_len) { + mark_blocks_used(ctx, blk, + extent.e_pblk + extent.e_len - blk); + n = DIV_ROUND_UP(extent.e_pblk + extent.e_len - blk, + EXT2FS_CLUSTER_RATIO(ctx->fs)); + pb->num_blocks += n; } - if (is_dir && extent.e_len > 0) - pb->last_db_block = blockcnt - 1; + pb->last_block = extent.e_lblk + extent.e_len - 1; pb->previous_block = extent.e_pblk + extent.e_len - 1; start_block = pb->last_block = last_lblk; if (is_leaf && !is_dir && @@ -2830,7 +3226,20 @@ static void check_blocks_extents(e2fsck_t ctx, struct problem_context *pctx, ext2_ino_t ino = pctx->ino; errcode_t retval; blk64_t eof_lblk; + struct ext3_extent_header *eh; + /* Check for a proper extent header... */ + eh = (struct ext3_extent_header *) &inode->i_block[0]; + retval = ext2fs_extent_header_verify(eh, sizeof(inode->i_block)); + if (retval) { + if (fix_problem(ctx, PR_1_MISSING_EXTENT_HEADER, pctx)) + e2fsck_clear_inode(ctx, ino, inode, 0, + "check_blocks_extents"); + pctx->errcode = 0; + return; + } + + /* ...since this function doesn't fail if i_block is zeroed. */ pctx->errcode = ext2fs_extent_open2(fs, ino, inode, &ehandle); if (pctx->errcode) { if (fix_problem(ctx, PR_1_READ_EXTENT, pctx)) @@ -2863,13 +3272,7 @@ static void check_blocks_extents(e2fsck_t ctx, struct problem_context *pctx, memset(pb->eti.ext_info, 0, sizeof(pb->eti.ext_info)); pb->eti.ino = pb->ino; - pb->region = region_create(0, info.max_lblk); - if (!pb->region) { - ext2fs_extent_free(ehandle); - fix_problem(ctx, PR_1_EXTENT_ALLOC_REGION_ABORT, pctx); - ctx->flags |= E2F_FLAG_ABORT; - return; - } + pb->next_lblock = 0; eof_lblk = ((EXT2_I_SIZE(inode) + fs->blocksize - 1) >> EXT2_BLOCK_SIZE_BITS(fs->super)) - 1; @@ -2882,8 +3285,6 @@ static void check_blocks_extents(e2fsck_t ctx, struct problem_context *pctx, "check_blocks_extents"); pctx->errcode = 0; } - region_free(pb->region); - pb->region = NULL; ext2fs_extent_free(ehandle); /* Rebuild unless it's a dir and we're rehashing it */ @@ -2950,7 +3351,7 @@ err: * blocks used by that inode. */ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx, - char *block_buf) + char *block_buf, const struct ea_quota *ea_ibody_quota) { ext2_filsys fs = ctx->fs; struct process_block_struct pb; @@ -2961,10 +3362,12 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx, int extent_fs; int inlinedata_fs; __u64 size; + struct ea_quota ea_block_quota; pb.ino = ino; - pb.num_blocks = 0; - pb.last_block = -1; + pb.num_blocks = EXT2FS_B2C(ctx->fs, + ea_ibody_quota ? ea_ibody_quota->blocks : 0); + pb.last_block = ~0; pb.last_init_lblock = -1; pb.last_db_block = -1; pb.num_illegal_blocks = 0; @@ -2974,7 +3377,7 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx, pb.previous_block = 0; pb.is_dir = LINUX_S_ISDIR(inode->i_mode); pb.is_reg = LINUX_S_ISREG(inode->i_mode); - pb.max_blocks = 1 << (31 - fs->super->s_log_block_size); + pb.max_blocks = 1U << (31 - fs->super->s_log_block_size); pb.inode = inode; pb.pctx = pctx; pb.ctx = ctx; @@ -2983,15 +3386,13 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx, pctx->ino = ino; pctx->errcode = 0; - extent_fs = (ctx->fs->super->s_feature_incompat & - EXT3_FEATURE_INCOMPAT_EXTENTS); - inlinedata_fs = (ctx->fs->super->s_feature_incompat & - EXT4_FEATURE_INCOMPAT_INLINE_DATA); + extent_fs = ext2fs_has_feature_extents(ctx->fs->super); + inlinedata_fs = ext2fs_has_feature_inline_data(ctx->fs->super); - if (check_ext_attr(ctx, pctx, block_buf)) { + if (check_ext_attr(ctx, pctx, block_buf, &ea_block_quota)) { if (ctx->flags & E2F_FLAG_SIGNAL_MASK) goto out; - pb.num_blocks++; + pb.num_blocks += EXT2FS_B2C(ctx->fs, ea_block_quota.blocks); } if (inlinedata_fs && (inode->i_flags & EXT4_INLINE_DATA_FL)) @@ -3067,7 +3468,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); } } @@ -3080,43 +3481,49 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx, } } - if (ino == EXT2_ROOT_INO || ino >= EXT2_FIRST_INODE(ctx->fs->super)) { - quota_data_add(ctx->qctx, inode, ino, - pb.num_blocks * fs->blocksize); - quota_data_inodes(ctx->qctx, inode, ino, +1); + if (ino != quota_type2inum(PRJQUOTA, fs->super) && + (ino == EXT2_ROOT_INO || ino >= EXT2_FIRST_INODE(ctx->fs->super)) && + !(inode->i_flags & EXT4_EA_INODE_FL)) { + quota_data_add(ctx->qctx, (struct ext2_inode_large *) inode, + ino, + pb.num_blocks * EXT2_CLUSTER_SIZE(fs->super)); + quota_data_inodes(ctx->qctx, (struct ext2_inode_large *) inode, + ino, (ea_ibody_quota ? + ea_ibody_quota->inodes : 0) + + ea_block_quota.inodes + 1); } - if (!(fs->super->s_feature_ro_compat & - EXT4_FEATURE_RO_COMPAT_HUGE_FILE) || + if (!ext2fs_has_feature_huge_file(fs->super) || !(inode->i_flags & EXT4_HUGE_FILE_FL)) pb.num_blocks *= (fs->blocksize / 512); pb.num_blocks *= EXT2FS_CLUSTER_RATIO(fs); #if 0 - printf("inode %u, i_size = %u, last_block = %lld, i_blocks=%llu, num_blocks = %llu\n", - ino, inode->i_size, pb.last_block, ext2fs_inode_i_blocks(fs, inode), - pb.num_blocks); + printf("inode %u, i_size = %u, last_block = %llu, i_blocks=%llu, num_blocks = %llu\n", + 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) { - int 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 size; + size_t sz = 0; errcode_t err; - size = 0; flags = ctx->fs->flags; ctx->fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS; err = ext2fs_inline_data_size(ctx->fs, pctx->ino, - &size); + &sz); ctx->fs->flags = (flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) | (ctx->fs->flags & ~EXT2_FLAG_IGNORE_CSUM_ERRORS); - if (err || size != inode->i_size) { + if (err || sz != size) { bad_size = 7; - pctx->num = size; + 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; @@ -3126,15 +3533,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]) @@ -3153,8 +3555,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)) { @@ -3169,10 +3569,9 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx, if (LINUX_S_ISREG(inode->i_mode) && ext2fs_needs_large_file_feature(EXT2_I_SIZE(inode))) ctx->large_files++; - if ((fs->super->s_creator_os == EXT2_OS_LINUX) && + if ((fs->super->s_creator_os != EXT2_OS_HURD) && ((pb.num_blocks != ext2fs_inode_i_blocks(fs, inode)) || - ((fs->super->s_feature_ro_compat & - EXT4_FEATURE_RO_COMPAT_HUGE_FILE) && + (ext2fs_has_feature_huge_file(fs->super) && (inode->i_flags & EXT4_HUGE_FILE_FL) && (inode->osd2.linux2.l_i_blocks_hi != 0)))) { pctx->num = pb.num_blocks; @@ -3189,8 +3588,7 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx, * a block mapped file, so rebuild it as an extent file. We can skip * symlinks because they're never rewritten. */ - if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, - EXT4_FEATURE_RO_COMPAT_BIGALLOC) && + if (ext2fs_has_feature_bigalloc(fs->super) && (LINUX_S_ISREG(inode->i_mode) || LINUX_S_ISDIR(inode->i_mode)) && ext2fs_inode_data_blocks2(fs, inode) > 0 && (ino == EXT2_ROOT_INO || ino >= EXT2_FIRST_INO(fs->super)) && @@ -3328,15 +3726,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_dir && p->num_blocks + 1 >= p->max_blocks) problem = PR_1_TOOBIG_DIR; - if (p->is_reg && p->num_blocks+1 >= p->max_blocks) + 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; @@ -3353,10 +3755,12 @@ static int process_block(ext2_filsys fs, */ if (blockcnt < 0 && p->ino != EXT2_RESIZE_INO && + 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); - ctx->flags |= E2F_FLAG_RESTART_LATER; + if ((ctx->options & E2F_OPT_NO) == 0) + ctx->flags |= E2F_FLAG_RESTART_LATER; } if (problem) { @@ -3656,8 +4060,7 @@ static void new_table_block(e2fsck_t ctx, blk64_t first_block, dgrp_t group, * within the flex_bg, and if that fails then try finding the * space anywhere in the filesystem. */ - is_flexbg = EXT2_HAS_INCOMPAT_FEATURE(fs->super, - EXT4_FEATURE_INCOMPAT_FLEX_BG); + is_flexbg = ext2fs_has_feature_flex_bg(fs->super); if (is_flexbg) { flexbg_size = 1 << fs->super->s_log_groups_per_flex; flexbg = group / flexbg_size; @@ -3846,7 +4249,7 @@ static void mark_table_blocks(e2fsck_t ctx) } /* - * Thes subroutines short circuits ext2fs_get_blocks and + * These subroutines short circuits ext2fs_get_blocks and * ext2fs_check_directory; we use them since we already have the inode * structure, so there's no point in letting the ext2fs library read * the inode again.