From ea1959f01523ffc105747d660ccc5b7f02805928 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Sat, 31 Aug 2002 02:32:41 -0400 Subject: [PATCH] Fix a bug which caused e2fsck to fail to correctly check filesystems using a non-zero hash version (i.e., half MD4 or TEA hash). The hash version wasn't getting copied into dx_dir->hashversion and this caused the kernel to treat all directories if they were using the legacy hash, which was Bad. --- e2fsck/ChangeLog | 14 +++++++++++++ e2fsck/pass2.c | 48 ++++++++++++++++++++++++++++++++++++++++---- e2fsck/problem.c | 5 +++++ e2fsck/problem.h | 3 +++ lib/ext2fs/ChangeLog | 5 +++++ lib/ext2fs/dblist.c | 19 ++++++++++++------ lib/ext2fs/ext2fs.h | 3 +++ 7 files changed, 87 insertions(+), 10 deletions(-) diff --git a/e2fsck/ChangeLog b/e2fsck/ChangeLog index 658128291..14733dc8c 100644 --- a/e2fsck/ChangeLog +++ b/e2fsck/ChangeLog @@ -1,3 +1,17 @@ +2002-08-31 Theodore Ts'o + + * pass2.c (e2fsck_pass2): If this is a HTREE directory, sort the + dblist so that the first block of all of the directories + is handled first so we can read the hash version + information. + (check_dir_block): Examine the root node for correctness, + and offer to clear it if it is not correct. Also copy the + hash version to the dx_dir structure, so that the proper + hash function can be used for other blocks in the + directory. + + * problem.c, problem.h (PR_2_HTREE_BAD_ROOT): Add new problem code. + 2002-08-21 Theodore Ts'o * problem.c: Fix PR_1_RELOC_BLOCK_ALLOCATE message to explain that diff --git a/e2fsck/pass2.c b/e2fsck/pass2.c index e61a6a270..329107bfb 100644 --- a/e2fsck/pass2.c +++ b/e2fsck/pass2.c @@ -69,6 +69,7 @@ static int update_dir_block(ext2_filsys fs, int ref_offset, void *priv_data); static void clear_htree(e2fsck_t ctx, ext2_ino_t ino); +static EXT2_QSORT_TYPE special_dir_block_cmp(const void *a, const void *b); struct check_dir_struct { char *buf; @@ -135,6 +136,9 @@ void e2fsck_pass2(e2fsck_t ctx) if (ctx->progress) (void) (ctx->progress)(ctx, 2, 0, cd.max); + + if (fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) + ext2fs_dblist_sort(fs->dblist, special_dir_block_cmp); cd.pctx.errcode = ext2fs_dblist_iterate(fs->dblist, check_dir_block, &cd); @@ -278,6 +282,35 @@ void e2fsck_pass2(e2fsck_t ctx) #endif } +/* + * This is special sort function that makes sure that directory blocks + * with a dirblock of zero are sorted to the beginning of the list. + * This guarantees that the root node of the htree directories are + * processed first, so we know what hash version to use. + */ +static EXT2_QSORT_TYPE special_dir_block_cmp(const void *a, const void *b) +{ + const struct ext2_db_entry *db_a = + (const struct ext2_db_entry *) a; + const struct ext2_db_entry *db_b = + (const struct ext2_db_entry *) b; + + if (db_a->blockcnt && !db_b->blockcnt) + return 1; + + if (!db_a->blockcnt && db_b->blockcnt) + return -1; + + if (db_a->blk != db_b->blk) + return (int) (db_a->blk - db_b->blk); + + if (db_a->ino != db_b->ino) + return (int) (db_a->ino - db_b->ino); + + return (int) (db_a->blockcnt - db_b->blockcnt); +} + + /* * Make sure the first entry in the directory is '.', and that the * directory entry is sane. @@ -562,6 +595,7 @@ static int check_dir_block(ext2_filsys fs, char *buf; e2fsck_t ctx; int problem; + struct ext2_dx_root_info *root; cd = (struct check_dir_struct *) priv_data; buf = cd->buf; @@ -627,13 +661,19 @@ static int check_dir_block(ext2_filsys fs, dx_db->max_hash = 0; dirent = (struct ext2_dir_entry *) buf; - /* - * XXX we need to check to make sure the root - * directory block is actually valid! - */ if (db->blockcnt == 0) { + root = (struct ext2_dx_root_info *) (buf + 24); dx_db->type = DX_DIRBLOCK_ROOT; dx_db->flags |= DX_FLAG_FIRST | DX_FLAG_LAST; + if ((root->reserved_zero || + root->info_length < 8 || + root->indirect_levels > 1) && + fix_problem(ctx, PR_2_HTREE_BAD_ROOT, &cd->pctx)) { + clear_htree(ctx, ino); + dx_dir->numblocks = 0; + dx_db = 0; + } + dx_dir->hashversion = root->hash_version; } else if ((dirent->inode == 0) && (dirent->rec_len == fs->blocksize)) dx_db->type = DX_DIRBLOCK_NODE; diff --git a/e2fsck/problem.c b/e2fsck/problem.c index d56ec2d49..7571ae7c8 100644 --- a/e2fsck/problem.c +++ b/e2fsck/problem.c @@ -1062,6 +1062,11 @@ static const struct e2fsck_problem problem_table[] = { N_("Error addjusting refcount for @a @b %b (@i %i): %m\n"), PROMPT_NONE, PR_FATAL }, + /* Invalid HTREE root node */ + { PR_2_HTREE_BAD_ROOT, + N_("@p @h %d: root node is invalid\n"), + PROMPT_CLEAR_HTREE, 0 }, + /* Pass 3 errors */ /* Pass 3: Checking directory connectivity */ diff --git a/e2fsck/problem.h b/e2fsck/problem.h index 2196a33b1..cc7a01b47 100644 --- a/e2fsck/problem.h +++ b/e2fsck/problem.h @@ -630,6 +630,9 @@ struct problem_context { /* Error adjusting EA refcount */ #define PR_2_ADJ_EA_REFCOUNT 0x02003B +/* Invalid HTREE root node */ +#define PR_2_HTREE_BAD_ROOT 0x02003C + /* * Pass 3 errors */ diff --git a/lib/ext2fs/ChangeLog b/lib/ext2fs/ChangeLog index 7337c53f4..ecabd3d3c 100644 --- a/lib/ext2fs/ChangeLog +++ b/lib/ext2fs/ChangeLog @@ -1,3 +1,8 @@ +2002-08-31 Theodore Ts'o + + * dblist.c (ext2fs_dblist_sort): New function which allows the + caller to pass in a special sort comparison function. + 2002-08-20 Theodore Ts'o * valid_blk.c (ext2fs_inode_has_valid_blocks): Fix bug which diff --git a/lib/ext2fs/dblist.c b/lib/ext2fs/dblist.c index a195b5bcd..a3f99ebe6 100644 --- a/lib/ext2fs/dblist.c +++ b/lib/ext2fs/dblist.c @@ -204,6 +204,17 @@ errcode_t ext2fs_set_dir_block(ext2_dblist dblist, ext2_ino_t ino, blk_t blk, return EXT2_ET_DB_NOT_FOUND; } +void ext2fs_dblist_sort(ext2_dblist dblist, + EXT2_QSORT_TYPE (*sortfunc)(const void *, + const void *)) +{ + if (!sortfunc) + sortfunc = dir_block_cmp; + qsort(dblist->list, (size_t) dblist->count, + sizeof(struct ext2_db_entry), sortfunc); + dblist->sorted = 1; +} + /* * This function iterates over the directory block list */ @@ -218,11 +229,8 @@ errcode_t ext2fs_dblist_iterate(ext2_dblist dblist, EXT2_CHECK_MAGIC(dblist, EXT2_ET_MAGIC_DBLIST); - if (!dblist->sorted) { - qsort(dblist->list, (size_t) dblist->count, - sizeof(struct ext2_db_entry), dir_block_cmp); - dblist->sorted = 1; - } + if (!dblist->sorted) + ext2fs_dblist_sort(dblist, 0); for (i=0; i < dblist->count; i++) { ret = (*func)(dblist->fs, &dblist->list[(int)i], priv_data); if (ret & DBLIST_ABORT) @@ -231,7 +239,6 @@ errcode_t ext2fs_dblist_iterate(ext2_dblist dblist, return 0; } - static EXT2_QSORT_TYPE dir_block_cmp(const void *a, const void *b) { const struct ext2_db_entry *db_a = diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h index e871f5862..fa3eb63f4 100644 --- a/lib/ext2fs/ext2fs.h +++ b/lib/ext2fs/ext2fs.h @@ -589,6 +589,9 @@ extern errcode_t ext2fs_get_num_dirs(ext2_filsys fs, ext2_ino_t *ret_num_dirs); extern errcode_t ext2fs_init_dblist(ext2_filsys fs, ext2_dblist *ret_dblist); extern errcode_t ext2fs_add_dir_block(ext2_dblist dblist, ext2_ino_t ino, blk_t blk, int blockcnt); +extern void ext2fs_dblist_sort(ext2_dblist dblist, + EXT2_QSORT_TYPE (*sortfunc)(const void *, + const void *)); extern errcode_t ext2fs_dblist_iterate(ext2_dblist dblist, int (*func)(ext2_filsys fs, struct ext2_db_entry *db_info, void *priv_data), -- 2.47.2