+#ifdef ENABLE_HTREE
+static void parse_int_node(ext2_filsys fs,
+ struct ext2_db_entry *db,
+ struct check_dir_struct *cd,
+ struct dx_dir_info *dx_dir,
+ char *block_buf)
+{
+ struct ext2_dx_root_info *root;
+ struct ext2_dx_entry *ent;
+ struct ext2_dx_countlimit *limit;
+ struct dx_dirblock_info *dx_db;
+ int i, expect_limit, count;
+ blk_t blk;
+ ext2_dirhash_t min_hash = 0xffffffff;
+ ext2_dirhash_t max_hash = 0;
+ ext2_dirhash_t hash = 0, prev_hash;
+
+ if (db->blockcnt == 0) {
+ root = (struct ext2_dx_root_info *) (block_buf + 24);
+
+#ifdef DX_DEBUG
+ printf("Root node dump:\n");
+ printf("\t Reserved zero: %d\n", root->reserved_zero);
+ printf("\t Hash Version: %d\n", root->hash_version);
+ printf("\t Info length: %d\n", root->info_length);
+ printf("\t Indirect levels: %d\n", root->indirect_levels);
+ printf("\t Flags: %d\n", root->unused_flags);
+#endif
+
+ ent = (struct ext2_dx_entry *) (block_buf + 24 + root->info_length);
+ } else {
+ ent = (struct ext2_dx_entry *) (block_buf+8);
+ }
+ limit = (struct ext2_dx_countlimit *) ent;
+
+#ifdef DX_DEBUG
+ printf("Number of entries (count): %d\n",
+ ext2fs_le16_to_cpu(limit->count));
+ printf("Number of entries (limit): %d\n",
+ ext2fs_le16_to_cpu(limit->limit));
+#endif
+
+ count = ext2fs_le16_to_cpu(limit->count);
+ expect_limit = (fs->blocksize - ((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))
+ goto clear_and_exit;
+ }
+ if (count > expect_limit) {
+ cd->pctx.num = count;
+ if (fix_problem(cd->ctx, PR_2_HTREE_BAD_COUNT, &cd->pctx))
+ goto clear_and_exit;
+ count = expect_limit;
+ }
+
+ for (i=0; i < count; i++) {
+ prev_hash = hash;
+ hash = i ? (ext2fs_le32_to_cpu(ent[i].hash) & ~1) : 0;
+#ifdef DX_DEBUG
+ printf("Entry #%d: Hash 0x%08x, block %d\n", i,
+ hash, ext2fs_le32_to_cpu(ent[i].block));
+#endif
+ blk = ext2fs_le32_to_cpu(ent[i].block) & 0x0ffffff;
+ /* Check to make sure the block is valid */
+ if (blk > (blk_t) dx_dir->numblocks) {
+ cd->pctx.blk = blk;
+ if (fix_problem(cd->ctx, PR_2_HTREE_BADBLK,
+ &cd->pctx))
+ goto clear_and_exit;
+ }
+ if (hash < prev_hash &&
+ fix_problem(cd->ctx, PR_2_HTREE_HASH_ORDER, &cd->pctx))
+ goto clear_and_exit;
+ dx_db = &dx_dir->dx_block[blk];
+ if (dx_db->flags & DX_FLAG_REFERENCED) {
+ dx_db->flags |= DX_FLAG_DUP_REF;
+ } else {
+ dx_db->flags |= DX_FLAG_REFERENCED;
+ dx_db->parent = db->blockcnt;
+ }
+ if (hash < min_hash)
+ min_hash = hash;
+ if (hash > max_hash)
+ max_hash = hash;
+ dx_db->node_min_hash = hash;
+ if ((i+1) < count)
+ dx_db->node_max_hash =
+ ext2fs_le32_to_cpu(ent[i+1].hash) & ~1;
+ else {
+ dx_db->node_max_hash = 0xfffffffe;
+ dx_db->flags |= DX_FLAG_LAST;
+ }
+ if (i == 0)
+ dx_db->flags |= DX_FLAG_FIRST;
+ }
+#ifdef DX_DEBUG
+ printf("Blockcnt = %d, min hash 0x%08x, max hash 0x%08x\n",
+ db->blockcnt, min_hash, max_hash);
+#endif
+ dx_db = &dx_dir->dx_block[db->blockcnt];
+ dx_db->min_hash = min_hash;
+ dx_db->max_hash = max_hash;
+ return;
+
+clear_and_exit:
+ clear_htree(cd->ctx, cd->pctx.ino);
+ dx_dir->numblocks = 0;
+}
+#endif /* ENABLE_HTREE */
+
+/*
+ * Given a busted directory, try to salvage it somehow.
+ *
+ */
+static void salvage_directory(ext2_filsys fs,
+ struct ext2_dir_entry *dirent,
+ struct ext2_dir_entry *prev,
+ unsigned int *offset)
+{
+ char *cp = (char *) dirent;
+ int left = fs->blocksize - *offset - dirent->rec_len;
+ int name_len = dirent->name_len & 0xFF;
+
+ /*
+ * Special case of directory entry of size 8: copy what's left
+ * of the directory block up to cover up the invalid hole.
+ */
+ if ((left >= 12) && (dirent->rec_len == 8)) {
+ memmove(cp, cp+8, left);
+ memset(cp + left, 0, 8);
+ return;
+ }
+ /*
+ * If the directory entry overruns the end of the directory
+ * block, and the name is small enough to fit, then adjust the
+ * record length.
+ */
+ if ((left < 0) &&
+ (name_len + 8 <= dirent->rec_len + left) &&
+ dirent->inode <= fs->super->s_inodes_count &&
+ strnlen(dirent->name, name_len) == name_len) {
+ dirent->rec_len += left;
+ return;
+ }
+ /*
+ * If the directory entry is a multiple of four, so it is
+ * valid, let the previous directory entry absorb the invalid
+ * one.
+ */
+ if (prev && dirent->rec_len && (dirent->rec_len % 4) == 0) {
+ prev->rec_len += dirent->rec_len;
+ *offset += dirent->rec_len;
+ return;
+ }
+ /*
+ * Default salvage method --- kill all of the directory
+ * entries for the rest of the block. We will either try to
+ * absorb it into the previous directory entry, or create a
+ * new empty directory entry the rest of the directory block.
+ */
+ if (prev) {
+ prev->rec_len += fs->blocksize - *offset;
+ *offset = fs->blocksize;
+ } else {
+ dirent->rec_len = fs->blocksize - *offset;
+ dirent->name_len = 0;
+ dirent->inode = 0;
+ }
+}