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
Darrick J. Wong 2012-08-02 17:27:30 -04:00 committed by Theodore Ts'o
parent dc96de09d7
commit 07307114de
6 changed files with 86 additions and 12 deletions

View File

@ -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);

View File

@ -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)

View File

@ -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) {

View File

@ -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 */

View File

@ -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
*/

View File

@ -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);