mirror of https://github.com/vitalif/e2fsprogs
e2fsck: verify htree root/node checksums
Check htree internal node checksums. If broken, ask user to clear the htree index and recreate it later. [ Move the check for not rehashing the lost+found directory to pass1 so that we don't end up truncating lost+found when the metadata checksum feature is enabled. -- TYT ] Signed-off-by: Darrick J. Wong <djwong@us.ibm.com> Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>pu
parent
dc96de09d7
commit
07307114de
|
@ -494,6 +494,8 @@ extern void region_free(region_t region);
|
|||
extern int region_allocate(region_t region, region_addr_t start, int n);
|
||||
|
||||
/* rehash.c */
|
||||
void e2fsck_rehash_dir_later(e2fsck_t ctx, ext2_ino_t ino);
|
||||
int e2fsck_dir_will_be_rehashed(e2fsck_t ctx, ext2_ino_t ino);
|
||||
errcode_t e2fsck_rehash_dir(e2fsck_t ctx, ext2_ino_t ino);
|
||||
void e2fsck_rehash_directories(e2fsck_t ctx);
|
||||
|
||||
|
|
|
@ -758,6 +758,9 @@ void e2fsck_pass1(e2fsck_t ctx)
|
|||
ext2fs_mark_block_bitmap2(ctx->block_found_map,
|
||||
fs->super->s_mmp_block);
|
||||
|
||||
/* Set up ctx->lost_and_found if possible */
|
||||
(void) e2fsck_get_lost_and_found(ctx, 0);
|
||||
|
||||
while (1) {
|
||||
if (ino % (fs->super->s_inodes_per_group * 4) == 1) {
|
||||
if (e2fsck_mmp_update(fs))
|
||||
|
@ -2225,9 +2228,10 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
|
|||
}
|
||||
|
||||
if (ctx->dirs_to_hash && pb.is_dir &&
|
||||
(ctx->lost_and_found == ino) &&
|
||||
!(inode->i_flags & EXT2_INDEX_FL) &&
|
||||
((inode->i_size / fs->blocksize) >= 3))
|
||||
ext2fs_u32_list_add(ctx->dirs_to_hash, ino);
|
||||
e2fsck_rehash_dir_later(ctx, ino);
|
||||
|
||||
out:
|
||||
if (dirty_inode)
|
||||
|
|
|
@ -527,7 +527,7 @@ static void parse_int_node(ext2_filsys fs,
|
|||
struct ext2_db_entry2 *db,
|
||||
struct check_dir_struct *cd,
|
||||
struct dx_dir_info *dx_dir,
|
||||
char *block_buf)
|
||||
char *block_buf, int failed_csum)
|
||||
{
|
||||
struct ext2_dx_root_info *root;
|
||||
struct ext2_dx_entry *ent;
|
||||
|
@ -538,6 +538,7 @@ static void parse_int_node(ext2_filsys fs,
|
|||
ext2_dirhash_t min_hash = 0xffffffff;
|
||||
ext2_dirhash_t max_hash = 0;
|
||||
ext2_dirhash_t hash = 0, prev_hash;
|
||||
int csum_size = 0;
|
||||
|
||||
if (db->blockcnt == 0) {
|
||||
root = (struct ext2_dx_root_info *) (block_buf + 24);
|
||||
|
@ -552,9 +553,22 @@ static void parse_int_node(ext2_filsys fs,
|
|||
#endif
|
||||
|
||||
ent = (struct ext2_dx_entry *) (block_buf + 24 + root->info_length);
|
||||
|
||||
if (failed_csum &&
|
||||
(e2fsck_dir_will_be_rehashed(cd->ctx, cd->pctx.ino) ||
|
||||
fix_problem(cd->ctx, PR_2_HTREE_ROOT_CSUM_INVALID,
|
||||
&cd->pctx)))
|
||||
goto clear_and_exit;
|
||||
} else {
|
||||
ent = (struct ext2_dx_entry *) (block_buf+8);
|
||||
|
||||
if (failed_csum &&
|
||||
(e2fsck_dir_will_be_rehashed(cd->ctx, cd->pctx.ino) ||
|
||||
fix_problem(cd->ctx, PR_2_HTREE_NODE_CSUM_INVALID,
|
||||
&cd->pctx)))
|
||||
goto clear_and_exit;
|
||||
}
|
||||
|
||||
limit = (struct ext2_dx_countlimit *) ent;
|
||||
|
||||
#ifdef DX_DEBUG
|
||||
|
@ -565,8 +579,12 @@ static void parse_int_node(ext2_filsys fs,
|
|||
#endif
|
||||
|
||||
count = ext2fs_le16_to_cpu(limit->count);
|
||||
expect_limit = (fs->blocksize - ((char *) ent - block_buf)) /
|
||||
sizeof(struct ext2_dx_entry);
|
||||
if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
|
||||
EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
|
||||
csum_size = sizeof(struct ext2_dx_tail);
|
||||
expect_limit = (fs->blocksize -
|
||||
(csum_size + ((char *) ent - block_buf))) /
|
||||
sizeof(struct ext2_dx_entry);
|
||||
if (ext2fs_le16_to_cpu(limit->limit) != expect_limit) {
|
||||
cd->pctx.num = ext2fs_le16_to_cpu(limit->limit);
|
||||
if (fix_problem(cd->ctx, PR_2_HTREE_BAD_LIMIT, &cd->pctx))
|
||||
|
@ -632,6 +650,7 @@ static void parse_int_node(ext2_filsys fs,
|
|||
clear_and_exit:
|
||||
clear_htree(cd->ctx, cd->pctx.ino);
|
||||
dx_dir->numblocks = 0;
|
||||
e2fsck_rehash_dir_later(cd->ctx, cd->pctx.ino);
|
||||
}
|
||||
#endif /* ENABLE_HTREE */
|
||||
|
||||
|
@ -734,6 +753,7 @@ static int check_dir_block(ext2_filsys fs,
|
|||
struct problem_context pctx;
|
||||
int dups_found = 0;
|
||||
int ret;
|
||||
int dx_csum_size = 0;
|
||||
|
||||
cd = (struct check_dir_struct *) priv_data;
|
||||
buf = cd->buf;
|
||||
|
@ -745,6 +765,10 @@ static int check_dir_block(ext2_filsys fs,
|
|||
if (ctx->progress && (ctx->progress)(ctx, 2, cd->count++, cd->max))
|
||||
return DIRENT_ABORT;
|
||||
|
||||
if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
|
||||
EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
|
||||
dx_csum_size = sizeof(struct ext2_dx_tail);
|
||||
|
||||
/*
|
||||
* Make sure the inode is still in use (could have been
|
||||
* deleted in the duplicate/bad blocks pass.
|
||||
|
@ -834,7 +858,7 @@ static int check_dir_block(ext2_filsys fs,
|
|||
(rec_len == fs->blocksize) &&
|
||||
(dirent->name_len == 0) &&
|
||||
(ext2fs_le16_to_cpu(limit->limit) ==
|
||||
((fs->blocksize-8) /
|
||||
((fs->blocksize - (8 + dx_csum_size)) /
|
||||
sizeof(struct ext2_dx_entry))))
|
||||
dx_db->type = DX_DIRBLOCK_NODE;
|
||||
}
|
||||
|
@ -1121,7 +1145,7 @@ out_htree:
|
|||
cd->pctx.dir = cd->pctx.ino;
|
||||
if ((dx_db->type == DX_DIRBLOCK_ROOT) ||
|
||||
(dx_db->type == DX_DIRBLOCK_NODE))
|
||||
parse_int_node(fs, db, cd, dx_dir, buf);
|
||||
parse_int_node(fs, db, cd, dx_dir, buf, 0);
|
||||
}
|
||||
#endif /* ENABLE_HTREE */
|
||||
if (offset != fs->blocksize) {
|
||||
|
|
|
@ -1393,6 +1393,16 @@ static struct e2fsck_problem problem_table[] = {
|
|||
N_("i_file_acl_hi @F %N, @s zero.\n"),
|
||||
PROMPT_CLEAR, PR_PREEN_OK },
|
||||
|
||||
/* htree root node fails checksum */
|
||||
{ PR_2_HTREE_ROOT_CSUM_INVALID,
|
||||
N_("@p @h %d: root node fails checksum\n"),
|
||||
PROMPT_CLEAR_HTREE, PR_PREEN_OK },
|
||||
|
||||
/* htree internal node fails checksum */
|
||||
{ PR_2_HTREE_NODE_CSUM_INVALID,
|
||||
N_("@p @h %d: internal node fails checksum\n"),
|
||||
PROMPT_CLEAR_HTREE, PR_PREEN_OK },
|
||||
|
||||
/* Pass 3 errors */
|
||||
|
||||
/* Pass 3: Checking directory connectivity */
|
||||
|
|
|
@ -835,6 +835,12 @@ struct problem_context {
|
|||
/* i_file_acl_hi should be zero */
|
||||
#define PR_2_I_FILE_ACL_HI_ZERO 0x020048
|
||||
|
||||
/* htree root node fails checksum */
|
||||
#define PR_2_HTREE_ROOT_CSUM_INVALID 0x020049
|
||||
|
||||
/* htree node fails checksum */
|
||||
#define PR_2_HTREE_NODE_CSUM_INVALID 0x02004A
|
||||
|
||||
/*
|
||||
* Pass 3 errors
|
||||
*/
|
||||
|
|
|
@ -52,6 +52,25 @@
|
|||
#include "e2fsck.h"
|
||||
#include "problem.h"
|
||||
|
||||
/* Schedule a dir to be rebuilt during pass 3A. */
|
||||
void e2fsck_rehash_dir_later(e2fsck_t ctx, ext2_ino_t ino)
|
||||
{
|
||||
if (!ctx->dirs_to_hash)
|
||||
ext2fs_u32_list_create(&ctx->dirs_to_hash, 50);
|
||||
if (ctx->dirs_to_hash)
|
||||
ext2fs_u32_list_add(ctx->dirs_to_hash, ino);
|
||||
}
|
||||
|
||||
/* Ask if a dir will be rebuilt during pass 3A. */
|
||||
int e2fsck_dir_will_be_rehashed(e2fsck_t ctx, ext2_ino_t ino)
|
||||
{
|
||||
if (ctx->options & E2F_OPT_COMPRESS_DIRS)
|
||||
return 1;
|
||||
if (!ctx->dirs_to_hash)
|
||||
return 0;
|
||||
return ext2fs_u32_list_test(ctx->dirs_to_hash, ino);
|
||||
}
|
||||
|
||||
struct fill_dir_struct {
|
||||
char *buf;
|
||||
struct ext2_inode *inode;
|
||||
|
@ -495,6 +514,7 @@ static struct ext2_dx_root_info *set_root_node(ext2_filsys fs, char *buf,
|
|||
struct ext2_dx_root_info *root;
|
||||
struct ext2_dx_countlimit *limits;
|
||||
int filetype = 0;
|
||||
int csum_size = 0;
|
||||
|
||||
if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE)
|
||||
filetype = EXT2_FT_DIR << 8;
|
||||
|
@ -519,8 +539,13 @@ static struct ext2_dx_root_info *set_root_node(ext2_filsys fs, char *buf,
|
|||
root->indirect_levels = 0;
|
||||
root->unused_flags = 0;
|
||||
|
||||
if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
|
||||
EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
|
||||
csum_size = sizeof(struct ext2_dx_tail);
|
||||
|
||||
limits = (struct ext2_dx_countlimit *) (buf+32);
|
||||
limits->limit = (fs->blocksize - 32) / sizeof(struct ext2_dx_entry);
|
||||
limits->limit = (fs->blocksize - (32 + csum_size)) /
|
||||
sizeof(struct ext2_dx_entry);
|
||||
limits->count = 0;
|
||||
|
||||
return root;
|
||||
|
@ -531,14 +556,20 @@ static struct ext2_dx_entry *set_int_node(ext2_filsys fs, char *buf)
|
|||
{
|
||||
struct ext2_dir_entry *dir;
|
||||
struct ext2_dx_countlimit *limits;
|
||||
int csum_size = 0;
|
||||
|
||||
memset(buf, 0, fs->blocksize);
|
||||
dir = (struct ext2_dir_entry *) buf;
|
||||
dir->inode = 0;
|
||||
(void) ext2fs_set_rec_len(fs, fs->blocksize, dir);
|
||||
|
||||
if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
|
||||
EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
|
||||
csum_size = sizeof(struct ext2_dx_tail);
|
||||
|
||||
limits = (struct ext2_dx_countlimit *) (buf+8);
|
||||
limits->limit = (fs->blocksize - 8) / sizeof(struct ext2_dx_entry);
|
||||
limits->limit = (fs->blocksize - (8 + csum_size)) /
|
||||
sizeof(struct ext2_dx_entry);
|
||||
limits->count = 0;
|
||||
|
||||
return (struct ext2_dx_entry *) limits;
|
||||
|
@ -836,8 +867,6 @@ void e2fsck_rehash_directories(e2fsck_t ctx)
|
|||
if (!ctx->dirs_to_hash && !all_dirs)
|
||||
return;
|
||||
|
||||
e2fsck_get_lost_and_found(ctx, 0);
|
||||
|
||||
clear_problem_context(&pctx);
|
||||
|
||||
dir_index = ctx->fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX;
|
||||
|
@ -865,8 +894,7 @@ void e2fsck_rehash_directories(e2fsck_t ctx)
|
|||
if (!ext2fs_u32_list_iterate(iter, &ino))
|
||||
break;
|
||||
}
|
||||
if (ino == ctx->lost_and_found)
|
||||
continue;
|
||||
|
||||
pctx.dir = ino;
|
||||
if (first) {
|
||||
fix_problem(ctx, PR_3A_PASS_HEADER, &pctx);
|
||||
|
|
Loading…
Reference in New Issue