* - 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)
static void alloc_bb_map(e2fsck_t ctx);
static void alloc_imagic_map(e2fsck_t ctx);
static void mark_inode_bad(e2fsck_t ctx, ino_t ino);
+static void add_casefolded_dir(e2fsck_t ctx, ino_t ino);
static void handle_fs_bad_blocks(e2fsck_t ctx);
static void process_inodes(e2fsck_t ctx, char *block_buf);
static EXT2_QSORT_TYPE process_inode_cmp(const void *a, const void *b);
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.
*
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) {
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);
add_encrypted_file(ctx, &pctx) < 0)
goto clear_inode;
+ if (casefold_fs && inode->i_flags & EXT4_CASEFOLD_FL)
+ ext2fs_mark_inode_bitmap2(ctx->inode_casefold_map, ino);
+
if (LINUX_S_ISDIR(inode->i_mode)) {
ext2fs_mark_inode_bitmap2(ctx->inode_dir_map, ino);
e2fsck_add_dir_info(ctx, ino, 0);
ctx->fs_directory_count++;
+ if (inode->i_flags & EXT4_CASEFOLD_FL)
+ add_casefolded_dir(ctx, ino);
} else if (LINUX_S_ISREG (inode->i_mode)) {
ext2fs_mark_inode_bitmap2(ctx->inode_reg_map, ino);
ctx->fs_regular_count++;
* 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);
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);
ext2fs_mark_inode_bitmap2(ctx->inode_bad_map, ino);
}
+static void add_casefolded_dir(e2fsck_t ctx, ino_t ino)
+{
+ struct problem_context pctx;
+
+ if (!ctx->casefolded_dirs) {
+ pctx.errcode = ext2fs_u32_list_create(&ctx->casefolded_dirs, 0);
+ if (pctx.errcode)
+ goto error;
+ }
+ pctx.errcode = ext2fs_u32_list_add(ctx->casefolded_dirs, ino);
+ if (pctx.errcode == 0)
+ return;
+error:
+ fix_problem(ctx, PR_1_ALLOCATE_CASEFOLDED_DIRLIST, &pctx);
+ /* Should never get here */
+ ctx->flags |= E2F_FLAG_ABORT;
+}
+
/*
* This procedure will allocate the inode "bb" (badblock) map table
*/
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;
}
(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;
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;
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;
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;
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;
bad_size = 2;
}
} else {
- size = EXT2_I_SIZE(inode);
if ((pb.last_init_lblock >= 0) &&
/* Do not allow initialized allocated blocks past i_size*/
(size < (__u64)pb.last_init_lblock * fs->blocksize) &&
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)) {
(unsigned long) pctx->ino, type,
(unsigned long) p->previous_block+1,
(unsigned long) blk,
- blockcnt);
+ (long long) blockcnt);
}
p->fragmented = 1;
}
}
if (p->is_dir && !ext2fs_has_feature_largedir(fs->super) &&
+ !pctx->inode->i_size_high &&
blockcnt > (1 << (21 - fs->super->s_log_block_size)))
problem = PR_1_TOOBIG_DIR;
if (p->is_dir && p->num_blocks + 1 >= p->max_blocks)