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.
+2002-08-31 Theodore Ts'o <tytso@valinux.com>
+
+ * 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 <tytso@mit.edu>
* problem.c: Fix PR_1_RELOC_BLOCK_ALLOCATE message to explain that
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;
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);
}
/*
+ * 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.
*/
char *buf;
e2fsck_t ctx;
int problem;
+ struct ext2_dx_root_info *root;
cd = (struct check_dir_struct *) priv_data;
buf = cd->buf;
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;
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 */
/* 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
*/
+2002-08-31 Theodore Ts'o <tytso@valinux.com>
+
+ * 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 <tytso@mit.edu>
* valid_blk.c (ext2fs_inode_has_valid_blocks): Fix bug which
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
*/
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)
return 0;
}
-
static EXT2_QSORT_TYPE dir_block_cmp(const void *a, const void *b)
{
const struct ext2_db_entry *db_a =
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),