aboutsummaryrefslogtreecommitdiffstats
path: root/e2fsck
diff options
context:
space:
mode:
authorTheodore Ts'o <tytso@mit.edu>2002-06-25 23:26:34 -0400
committerTheodore Ts'o <tytso@mit.edu>2002-06-25 23:26:34 -0400
commit8fdc9985c1e2a4467630b33719b7feb281b7b33b (patch)
tree62568b09c49c9df3d49fe5e3cec0f6b6b446695e /e2fsck
parent88372d5c4b2ebd0405446b42de65bf2b0ebb2408 (diff)
downloade2fsprogs-8fdc9985c1e2a4467630b33719b7feb281b7b33b.tar.gz
e2fsprogs-8fdc9985c1e2a4467630b33719b7feb281b7b33b.tar.xz
e2fsprogs-8fdc9985c1e2a4467630b33719b7feb281b7b33b.zip
Add initial support for htree directories.
Diffstat (limited to 'e2fsck')
-rw-r--r--e2fsck/ChangeLog27
-rw-r--r--e2fsck/Makefile.in14
-rw-r--r--e2fsck/dx_dirinfo.c150
-rw-r--r--e2fsck/e2fsck.c3
-rw-r--r--e2fsck/e2fsck.h49
-rw-r--r--e2fsck/message.c4
-rw-r--r--e2fsck/pass1.c16
-rw-r--r--e2fsck/pass2.c256
-rw-r--r--e2fsck/problem.c39
-rw-r--r--e2fsck/problem.h24
-rw-r--r--e2fsck/unix.c11
11 files changed, 583 insertions, 10 deletions
diff --git a/e2fsck/ChangeLog b/e2fsck/ChangeLog
index c2e7e8d5..ca623e95 100644
--- a/e2fsck/ChangeLog
+++ b/e2fsck/ChangeLog
@@ -1,3 +1,30 @@
+2002-06-25 Theodore Ts'o <tytso@mit.edu>
+
+ * e2fsck.c (e2fsck_reset_context): Free the dx_dirinfo structure.
+
+ * message.c: Add new abbrevations @h and @p, "HTREE directory
+ inode" and "problem in".
+
+ * pass1.c (check_blocks): If the inode has the INDEX_FL flag,
+ register the block into the indexed directory data
+ structures. Or if the filesystem doesn't have the
+ DIR_INDEX flag, offer to clear the INDEX_FL.
+
+ * pass2.c (e2fsck_pass2, parse_int_node): Add support check htree
+ directories (we don't check all possible corruptions yet).
+
+ * problem.h, problem.h (PR_1_HTREE_SET, PR_2_HTREE_NOTREF,
+ PR_2_HTREE_DUPREF, PR_2_HTREE_MIN_HASH, PR_2_HTREE_MAX_HASH,
+ PR_2_HTREE_CLEAR, PR_2_HTREE_FCLR, PR_2_HTREE_BADBLK): Add
+ new problem codes.
+
+ * unix.c (main): If ENABLE_HTREE is not defined, complain if the
+ filesystem has the dir_index feature.
+
+ * Makefile.in, e2fsck.h, dx_dirinfo.c: New file (and group of
+ functions) which keeps track of blocks in HTREE directory
+ blocks.
+
2002-05-22 Andreas Dilger <adilger@clusterfs.com>
* super.c (check_superblock): Check that the number of inodes and
diff --git a/e2fsck/Makefile.in b/e2fsck/Makefile.in
index 1dd26aef..51c08212 100644
--- a/e2fsck/Makefile.in
+++ b/e2fsck/Makefile.in
@@ -55,17 +55,18 @@ PROFILED_DEPLIBS= $(PROFILED_LIBEXT2FS) $(PROFILED_LIBCOM_ERR) \
#MCHECK= -DMCHECK
OBJS= unix.o e2fsck.o super.o pass1.o pass1b.o pass2.o pass3.o pass4.o \
- pass5.o journal.o swapfs.o badblocks.o util.o dirinfo.o ehandler.o \
- problem.o message.o recovery.o region.o revoke.o ea_refcount.o \
- $(MTRACE_OBJ)
+ pass5.o journal.o swapfs.o badblocks.o util.o dirinfo.o dx_dirinfo.o \
+ ehandler.o problem.o message.o recovery.o region.o revoke.o \
+ ea_refcount.o $(MTRACE_OBJ)
PROFILED_OBJS= profiled/unix.o profiled/e2fsck.o profiled/super.o \
profiled/pass1.o profiled/pass1b.o \
profiled/pass2.o profiled/pass3.o profiled/pass4.o profiled/pass5.o \
profiled/journal.o profiled/badblocks.o profiled/util.o \
- profiled/dirinfo.o profiled/ehandler.o profiled/message.o \
- profiled/problem.o profiled/swapfs.o profiled/recovery.o \
- profiled/region.o profiled/revoke.o profiled/ea_refcount.o
+ profiled/dirinfo.o profiled/dx_dirinfo.o profiled/ehandler.o \
+ profiled/message.o profiled/problem.o profiled/swapfs.o \
+ profiled/recovery.o profiled/region.o profiled/revoke.o \
+ profiled/ea_refcount.o
SRCS= $(srcdir)/e2fsck.c \
$(srcdir)/super.c \
@@ -82,6 +83,7 @@ SRCS= $(srcdir)/e2fsck.c \
$(srcdir)/util.c \
$(srcdir)/unix.c \
$(srcdir)/dirinfo.c \
+ $(srcdir)/dx_dirinfo.c \
$(srcdir)/ehandler.c \
$(srcdir)/problem.c \
$(srcdir)/message.c \
diff --git a/e2fsck/dx_dirinfo.c b/e2fsck/dx_dirinfo.c
new file mode 100644
index 00000000..ff90e99f
--- /dev/null
+++ b/e2fsck/dx_dirinfo.c
@@ -0,0 +1,150 @@
+/*
+ * dirinfo.c --- maintains the directory information table for e2fsck.
+ *
+ * Copyright (C) 1993 Theodore Ts'o. This file may be redistributed
+ * under the terms of the GNU Public License.
+ */
+
+#include "e2fsck.h"
+#ifdef ENABLE_HTREE
+
+/*
+ * This subroutine is called during pass1 to create a directory info
+ * entry. During pass1, the passed-in parent is 0; it will get filled
+ * in during pass2.
+ */
+void e2fsck_add_dx_dir(e2fsck_t ctx, ext2_ino_t ino, int num_blocks)
+{
+ struct dx_dir_info *dir;
+ int i, j;
+ errcode_t retval;
+ unsigned long old_size;
+
+#if 0
+ printf("add_dx_dir_info for inode %lu...\n", ino);
+#endif
+ if (!ctx->dx_dir_info) {
+ ctx->dx_dir_info_count = 0;
+ ctx->dx_dir_info_size = 100; /* Guess */
+ ctx->dx_dir_info = (struct dx_dir_info *)
+ e2fsck_allocate_memory(ctx, ctx->dx_dir_info_size
+ * sizeof (struct dx_dir_info),
+ "directory map");
+ }
+
+ if (ctx->dx_dir_info_count >= ctx->dx_dir_info_size) {
+ old_size = ctx->dx_dir_info_size * sizeof(struct dx_dir_info);
+ ctx->dx_dir_info_size += 10;
+ retval = ext2fs_resize_mem(old_size, ctx->dx_dir_info_size *
+ sizeof(struct dx_dir_info),
+ (void **) &ctx->dx_dir_info);
+ if (retval) {
+ ctx->dx_dir_info_size -= 10;
+ return;
+ }
+ }
+
+ /*
+ * Normally, add_dx_dir_info is called with each inode in
+ * sequential order; but once in a while (like when pass 3
+ * needs to recreate the root directory or lost+found
+ * directory) it is called out of order. In those cases, we
+ * need to move the dx_dir_info entries down to make room, since
+ * the dx_dir_info array needs to be sorted by inode number for
+ * get_dx_dir_info()'s sake.
+ */
+ if (ctx->dx_dir_info_count &&
+ ctx->dx_dir_info[ctx->dx_dir_info_count-1].ino >= ino) {
+ for (i = ctx->dx_dir_info_count-1; i > 0; i--)
+ if (ctx->dx_dir_info[i-1].ino < ino)
+ break;
+ dir = &ctx->dx_dir_info[i];
+ if (dir->ino != ino)
+ for (j = ctx->dx_dir_info_count++; j > i; j--)
+ ctx->dx_dir_info[j] = ctx->dx_dir_info[j-1];
+ } else
+ dir = &ctx->dx_dir_info[ctx->dx_dir_info_count++];
+
+ dir->ino = ino;
+ dir->numblocks = num_blocks;
+ dir->hashversion = 0;
+ dir->dx_block = e2fsck_allocate_memory(ctx, num_blocks
+ * sizeof (struct dx_dirblock_info),
+ "dx_block info array");
+
+}
+
+/*
+ * get_dx_dir_info() --- given an inode number, try to find the directory
+ * information entry for it.
+ */
+struct dx_dir_info *e2fsck_get_dx_dir_info(e2fsck_t ctx, ext2_ino_t ino)
+{
+ int low, high, mid;
+
+ low = 0;
+ high = ctx->dx_dir_info_count-1;
+ if (!ctx->dx_dir_info)
+ return 0;
+ if (ino == ctx->dx_dir_info[low].ino)
+ return &ctx->dx_dir_info[low];
+ if (ino == ctx->dx_dir_info[high].ino)
+ return &ctx->dx_dir_info[high];
+
+ while (low < high) {
+ mid = (low+high)/2;
+ if (mid == low || mid == high)
+ break;
+ if (ino == ctx->dx_dir_info[mid].ino)
+ return &ctx->dx_dir_info[mid];
+ if (ino < ctx->dx_dir_info[mid].ino)
+ high = mid;
+ else
+ low = mid;
+ }
+ return 0;
+}
+
+/*
+ * Free the dx_dir_info structure when it isn't needed any more.
+ */
+void e2fsck_free_dx_dir_info(e2fsck_t ctx)
+{
+ int i;
+ struct dx_dir_info *dir;
+
+ if (ctx->dx_dir_info) {
+ dir = ctx->dx_dir_info;
+ for (i=0; i < ctx->dx_dir_info_count; i++) {
+ if (dir->dx_block) {
+ ext2fs_free_mem((void **) &dir->dx_block);
+ dir->dx_block = 0;
+ }
+ }
+ ext2fs_free_mem((void **) &ctx->dx_dir_info);
+ ctx->dx_dir_info = 0;
+ }
+ ctx->dx_dir_info_size = 0;
+ ctx->dx_dir_info_count = 0;
+}
+
+/*
+ * Return the count of number of directories in the dx_dir_info structure
+ */
+int e2fsck_get_num_dx_dirinfo(e2fsck_t ctx)
+{
+ return ctx->dx_dir_info_count;
+}
+
+/*
+ * A simple interator function
+ */
+struct dx_dir_info *e2fsck_dx_dir_info_iter(e2fsck_t ctx, int *control)
+{
+ if (*control >= ctx->dx_dir_info_count)
+ return 0;
+
+ return(ctx->dx_dir_info + (*control)++);
+}
+
+#endif /* ENABLE_HTREE */
diff --git a/e2fsck/e2fsck.c b/e2fsck/e2fsck.c
index 1ec8ffad..0abae190 100644
--- a/e2fsck/e2fsck.c
+++ b/e2fsck/e2fsck.c
@@ -72,6 +72,9 @@ errcode_t e2fsck_reset_context(e2fsck_t ctx)
ctx->fs->dblist = 0;
}
e2fsck_free_dir_info(ctx);
+#ifdef ENABLE_HTREE
+ e2fsck_free_dx_dir_info(ctx);
+#endif
if (ctx->refcount) {
ea_refcount_free(ctx->refcount);
ctx->refcount = 0;
diff --git a/e2fsck/e2fsck.h b/e2fsck/e2fsck.h
index 88490aa1..49097fe6 100644
--- a/e2fsck/e2fsck.h
+++ b/e2fsck/e2fsck.h
@@ -77,6 +77,40 @@ struct dir_info {
ext2_ino_t parent; /* Parent according to treewalk */
};
+
+/*
+ * The indexed directory information structure; stores information for
+ * directories which contain a hash tree index.
+ */
+struct dx_dir_info {
+ ext2_ino_t ino; /* Inode number */
+ int numblocks; /* number of blocks */
+ int hashversion;
+ struct dx_dirblock_info *dx_block; /* Array of size numblocks */
+};
+
+#define DX_DIRBLOCK_ROOT 1
+#define DX_DIRBLOCK_LEAF 2
+#define DX_DIRBLOCK_NODE 3
+#define DX_DIRBLOCK_CORRUPT 4
+#define DX_DIRBLOCK_CLEARED 8
+
+struct dx_dirblock_info {
+ int type;
+ blk_t phys;
+ int flags;
+ blk_t parent;
+ ext2_dirhash_t min_hash;
+ ext2_dirhash_t max_hash;
+ ext2_dirhash_t node_min_hash;
+ ext2_dirhash_t node_max_hash;
+};
+
+#define DX_FLAG_REFERENCED 1
+#define DX_FLAG_DUP_REF 2
+#define DX_FLAG_FIRST 4
+#define DX_FLAG_LAST 8
+
#ifdef RESOURCE_TRACK
/*
* This structure is used for keeping track of how much resources have
@@ -208,6 +242,13 @@ struct e2fsck_struct {
struct dir_info *dir_info;
/*
+ * Indexed directory information
+ */
+ int dx_dir_info_count;
+ int dx_dir_info_size;
+ struct dx_dir_info *dx_dir_info;
+
+ /*
* Tuning parameters
*/
int process_inode_size;
@@ -292,10 +333,16 @@ extern void test_disk(e2fsck_t ctx);
extern void e2fsck_add_dir_info(e2fsck_t ctx, ext2_ino_t ino, ext2_ino_t parent);
extern struct dir_info *e2fsck_get_dir_info(e2fsck_t ctx, ext2_ino_t ino);
extern void e2fsck_free_dir_info(e2fsck_t ctx);
-extern int e2fsck_get_num_dirs(e2fsck_t ctx);
extern int e2fsck_get_num_dirinfo(e2fsck_t ctx);
extern struct dir_info *e2fsck_dir_info_iter(e2fsck_t ctx, int *control);
+/* dx_dirinfo.c */
+extern void e2fsck_add_dx_dir(e2fsck_t ctx, ext2_ino_t ino, int num_blocks);
+extern struct dx_dir_info *e2fsck_get_dx_dir_info(e2fsck_t ctx, ext2_ino_t ino);
+extern void e2fsck_free_dx_dir_info(e2fsck_t ctx);
+extern int e2fsck_get_num_dx_dirinfo(e2fsck_t ctx);
+extern struct dx_dir_info *e2fsck_dx_dir_info_iter(e2fsck_t ctx, int *control);
+
/* ea_refcount.c */
extern errcode_t ea_refcount_create(int size, ext2_refcount_t *ret);
extern void ea_refcount_free(ext2_refcount_t refcount);
diff --git a/e2fsck/message.c b/e2fsck/message.c
index 991bbee8..6c81eeb2 100644
--- a/e2fsck/message.c
+++ b/e2fsck/message.c
@@ -63,12 +63,14 @@
* @f filesystem
* @F for @i %i (%Q) is
* @g group
+ * @h HTREE directory inode
* @i inode
* @I illegal
* @j journal
* @l lost+found
* @L is a link
* @o orphaned
+ * @p problem in
* @r root inode
* @s should be
* @S superblock
@@ -116,9 +118,11 @@ static const char *abbrevs[] = {
N_("ffilesystem"),
N_("Ffor @i %i (%Q) is"),
N_("ggroup"),
+ N_("hHTREE @d @i"),
N_("llost+found"),
N_("Lis a link"),
N_("oorphaned"),
+ N_("pproblem in"),
N_("rroot @i"),
N_("sshould be"),
N_("Ssuper@b"),
diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index b15e120b..5bb99bcc 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -1208,6 +1208,22 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
ctx->flags |= E2F_FLAG_RESTART;
return;
}
+
+ if (inode->i_flags & EXT2_INDEX_FL) {
+ if (fs->super->s_feature_compat &
+ EXT2_FEATURE_COMPAT_DIR_INDEX) {
+#ifdef ENABLE_HTREE
+ e2fsck_add_dx_dir(ctx, ino, pb.last_block+1);
+#endif
+ } else {
+ if (fix_problem(ctx, PR_1_HTREE_SET, pctx)) {
+ inode->i_flags &= ~EXT2_INDEX_FL;
+ e2fsck_write_inode(ctx, ino, inode,
+ "check_blocks");
+ }
+ }
+ }
+
if (inode->i_file_acl && check_ext_attr(ctx, pctx, block_buf))
pb.num_blocks++;
diff --git a/e2fsck/pass2.c b/e2fsck/pass2.c
index 34c253d4..5ad52968 100644
--- a/e2fsck/pass2.c
+++ b/e2fsck/pass2.c
@@ -50,6 +50,8 @@
#define _INLINE_ inline
#endif
+#undef DX_DEBUG
+
/*
* Keeps track of how many times an inode is referenced.
*/
@@ -66,6 +68,7 @@ static int update_dir_block(ext2_filsys fs,
blk_t ref_block,
int ref_offset,
void *priv_data);
+static void clear_htree(e2fsck_t ctx, ext2_ino_t ino);
struct check_dir_struct {
char *buf;
@@ -85,7 +88,13 @@ void e2fsck_pass2(e2fsck_t ctx)
#endif
struct dir_info *dir;
struct check_dir_struct cd;
-
+ struct dx_dir_info *dx_dir;
+ struct dx_dirblock_info *dx_db, *dx_parent;
+ blk_t b;
+ int i;
+ problem_t code;
+ int bad_dir;
+
#ifdef RESOURCE_TRACK
init_resource_track(&rtrack);
#endif
@@ -136,7 +145,93 @@ void e2fsck_pass2(e2fsck_t ctx)
ctx->flags |= E2F_FLAG_ABORT;
return;
}
-
+
+#ifdef ENABLE_HTREE
+ for (i=0; (dx_dir = e2fsck_dx_dir_info_iter(ctx, &i)) != 0;) {
+ if (dx_dir->ino == 0)
+ continue;
+ clear_problem_context(&pctx);
+ bad_dir = 0;
+ pctx.dir = dx_dir->ino;
+ dx_db = dx_dir->dx_block;
+ if (dx_db->flags & DX_FLAG_REFERENCED)
+ dx_db->flags |= DX_FLAG_DUP_REF;
+ else
+ dx_db->flags |= DX_FLAG_REFERENCED;
+ /*
+ * Find all of the first and last leaf blocks, and
+ * update their parent's min and max hash values
+ */
+ for (b=0, dx_db = dx_dir->dx_block;
+ b < dx_dir->numblocks;
+ b++, dx_db++) {
+ if ((dx_db->type != DX_DIRBLOCK_LEAF) ||
+ !(dx_db->flags & (DX_FLAG_FIRST | DX_FLAG_LAST)))
+ continue;
+ dx_parent = &dx_dir->dx_block[dx_db->parent];
+ /*
+ * XXX Make sure dx_parent->min_hash > dx_db->min_hash
+ */
+ if (dx_db->flags & DX_FLAG_FIRST)
+ dx_parent->min_hash = dx_db->min_hash;
+ /*
+ * XXX Make sure dx_parent->max_hash < dx_db->max_hash
+ */
+ if (dx_db->flags & DX_FLAG_LAST)
+ dx_parent->max_hash = dx_db->max_hash;
+ }
+
+ for (b=0, dx_db = dx_dir->dx_block;
+ b < dx_dir->numblocks;
+ b++, dx_db++) {
+ pctx.blkcount = b;
+ pctx.group = dx_db->parent;
+ code = 0;
+ if (!(dx_db->flags & DX_FLAG_FIRST) &&
+ (dx_db->min_hash < dx_db->node_min_hash)) {
+ pctx.blk = dx_db->min_hash;
+ pctx.blk2 = dx_db->node_min_hash;
+ code = PR_2_HTREE_MIN_HASH;
+ fix_problem(ctx, code, &pctx);
+ bad_dir++;
+ }
+ /*
+ * This test doesn't apply for the root block
+ * at block #0
+ */
+ if (b &&
+ (dx_db->max_hash > dx_db->node_max_hash)) {
+ pctx.blk = dx_db->max_hash;
+ pctx.blk2 = dx_db->node_max_hash;
+ code = PR_2_HTREE_MAX_HASH;
+ fix_problem(ctx, code, &pctx);
+ }
+ if (!(dx_db->flags & DX_FLAG_REFERENCED)) {
+ code = PR_2_HTREE_NOTREF;
+ fix_problem(ctx, code, &pctx);
+ bad_dir++;
+ } else if (dx_db->flags & DX_FLAG_DUP_REF) {
+ code = PR_2_HTREE_DUPREF;
+ fix_problem(ctx, code, &pctx);
+ bad_dir++;
+ }
+ if (code == 0)
+ continue;
+ }
+ if (bad_dir && fix_problem(ctx, PR_2_HTREE_CLEAR, &pctx)) {
+ clear_htree(ctx, dx_dir->ino);
+ dx_dir->ino = 0;
+ break;
+ }
+#ifdef ENABLE_HTREE_CLEAR
+ if (dx_dir->ino) {
+ fix_problem(ctx, PR_2_HTREE_FCLR, &pctx);
+ clear_htree(ctx, dx_dir->ino);
+ dx_dir->ino = 0;
+ }
+#endif
+ }
+#endif
ext2fs_free_mem((void **) &buf);
ext2fs_free_dblist(fs->dblist);
@@ -353,13 +448,107 @@ static _INLINE_ int check_filetype(e2fsck_t ctx,
return 1;
}
+#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;
+ blk_t blk;
+ ext2_dirhash_t min_hash = 0xffffffff;
+ ext2_dirhash_t max_hash = 0;
+ ext2_dirhash_t hash = 0;
+
+ 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", limit->count);
+ printf("Number of entries (limit): %d\n", limit->limit);
+#endif
+
+ for (i=0; i < limit->count; i++) {
+ hash = i ? (ent[i].hash & ~1) : 0;
+ /*
+ * XXX Check to make make sure the hash[i] < hash[i+1]
+ */
+#ifdef DX_DEBUG
+ printf("Entry #%d: Hash 0x%08x, block %d\n", i,
+ hash, ent[i].block);
+#endif
+ blk = ent[i].block & 0x0ffffff;
+ /* Check to make sure the block is valid */
+ if (blk > dx_dir->numblocks) {
+ if (fix_problem(cd->ctx, PR_2_HTREE_BADBLK,
+ cd->pctx)) {
+ clear_htree(cd->ctx, cd->pctx.ino);
+ dx_dir->ino = 0;
+ return;
+ }
+ }
+ 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) < limit->count)
+ dx_db->node_max_hash = (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;
+}
+#endif /* ENABLE_HTREE */
static int check_dir_block(ext2_filsys fs,
struct ext2_db_entry *db,
void *priv_data)
{
struct dir_info *subdir, *dir;
+ struct dx_dir_info *dx_dir;
+#ifdef ENABLE_HTREE
+ struct dx_dirblock_info *dx_db = 0;
+#endif /* ENABLE_HTREE */
struct ext2_dir_entry *dirent;
+ ext2_dirhash_t hash;
int offset = 0;
int dir_modified = 0;
int dot_state;
@@ -419,6 +608,32 @@ static int check_dir_block(ext2_filsys fs,
}
memset(buf, 0, fs->blocksize);
}
+#ifdef ENABLE_HTREE
+ dx_dir = e2fsck_get_dx_dir_info(ctx, ino);
+ if (dx_dir && dx_dir->ino) {
+ if (db->blockcnt >= dx_dir->numblocks) {
+ printf("XXX should never happen!!!\n");
+ abort();
+ }
+ dx_db = &dx_dir->dx_block[db->blockcnt];
+ dx_db->type = DX_DIRBLOCK_LEAF;
+ dx_db->phys = block_nr;
+ dx_db->min_hash = ~0;
+ 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) {
+ dx_db->type = DX_DIRBLOCK_ROOT;
+ dx_db->flags |= DX_FLAG_FIRST | DX_FLAG_LAST;
+ } else if ((dirent->inode == 0) &&
+ (dirent->rec_len == fs->blocksize))
+ dx_db->type = DX_DIRBLOCK_NODE;
+ }
+#endif /* ENABLE_HTREE */
do {
dot_state++;
@@ -563,6 +778,17 @@ static int check_dir_block(ext2_filsys fs,
if (check_filetype(ctx, dirent, ino, &cd->pctx))
dir_modified++;
+#ifdef ENABLE_HTREE
+ if (dx_db) {
+ ext2fs_dirhash(dx_dir->hashversion, dirent->name,
+ (dirent->name_len & 0xFF), &hash);
+ if (hash < dx_db->min_hash)
+ dx_db->min_hash = hash;
+ if (hash > dx_db->max_hash)
+ dx_db->max_hash = hash;
+ }
+#endif
+
/*
* If this is a directory, then mark its parent in its
* dir_info structure. If the parent field is already
@@ -604,6 +830,18 @@ static int check_dir_block(ext2_filsys fs,
#if 0
printf("\n");
#endif
+#ifdef ENABLE_HTREE
+ if (dx_db) {
+#ifdef DX_DEBUG
+ printf("db_block %d, type %d, min_hash 0x%0x, max_hash 0x%0x\n",
+ db->blockcnt, dx_db->type,
+ dx_db->min_hash, dx_db->max_hash);
+#endif
+ if ((dx_db->type == DX_DIRBLOCK_ROOT) ||
+ (dx_db->type == DX_DIRBLOCK_NODE))
+ parse_int_node(fs, db, cd, dx_dir, buf);
+ }
+#endif /* ENABLE_HTREE */
if (offset != fs->blocksize) {
cd->pctx.num = dirent->rec_len - fs->blocksize + offset;
if (fix_problem(ctx, PR_2_FINAL_RECLEN, &cd->pctx)) {
@@ -696,6 +934,20 @@ static void deallocate_inode(e2fsck_t ctx, ext2_ino_t ino, char* block_buf)
}
}
+/*
+ * This fuction clears the htree flag on an inode
+ */
+static void clear_htree(e2fsck_t ctx, ext2_ino_t ino)
+{
+ struct ext2_inode inode;
+ struct problem_context pctx;
+
+ e2fsck_read_inode(ctx, ino, &inode, "clear_htree");
+ inode.i_flags = inode.i_flags & ~EXT2_INDEX_FL;
+ e2fsck_write_inode(ctx, ino, &inode, "clear_htree");
+}
+
+
extern int e2fsck_process_bad_inode(e2fsck_t ctx, ext2_ino_t dir,
ext2_ino_t ino, char *buf)
{
diff --git a/e2fsck/problem.c b/e2fsck/problem.c
index e786fa2b..1d4ffb16 100644
--- a/e2fsck/problem.c
+++ b/e2fsck/problem.c
@@ -665,6 +665,11 @@ static const struct e2fsck_problem problem_table[] = {
N_("@b #%B (%b) causes symlink to be too big. "),
PROMPT_CLEAR, PR_LATCH_TOOBIG },
+ /* INDEX_FL flag set on a non-HTREE filesystem */
+ { PR_1_HTREE_SET,
+ N_("@i %i has INDEX_FL flag set on @f without htree support.\n"),
+ PROMPT_CLEAR, 0 },
+
/* Pass 1b errors */
/* Pass 1B: Rescan for duplicate/bad blocks */
@@ -984,6 +989,40 @@ static const struct e2fsck_problem problem_table[] = {
N_("@f contains large files, but lacks LARGE_FILE flag in @S.\n"),
PROMPT_FIX, 0 },
+ /* Node in HTREE directory not referenced */
+ { PR_2_HTREE_NOTREF,
+ N_("@p @h %d: node (%B) not referenced\n"),
+ PROMPT_NONE, 0 },
+
+ /* Node in HTREE directory referenced twice */
+ { PR_2_HTREE_DUPREF,
+ N_("@p @h %d: node (%B) referenced twice\n"),
+ PROMPT_NONE, 0 },
+
+ /* Node in HTREE directory has bad min hash */
+ { PR_2_HTREE_MIN_HASH,
+ N_("@p @h %d: node (%B) has bad min hash\n"),
+ PROMPT_NONE, 0 },
+
+ /* Node in HTREE directory has bad max hash */
+ { PR_2_HTREE_MAX_HASH,
+ N_("@p @h %d: node (%B) has bad max hash\n"),
+ PROMPT_NONE, 0 },
+
+ /* Clear invalid HTREE directory */
+ { PR_2_HTREE_CLEAR,
+ N_("Invalid @h %d (%q). "), PROMPT_CLEAR, 0 },
+
+ /* Clear the htree flag forcibly */
+ { PR_2_HTREE_FCLR,
+ N_("Forcibly clearing HTREE flag on @i %d (%q). (Beta test code)\n"),
+ PROMPT_NONE, 0 },
+
+ /* Bad block in htree interior node */
+ { PR_2_HTREE_BADBLK,
+ N_("@p @h %d (%q): bad @b number %B.\n"),
+ PROMPT_CLEAR, 0 },
+
/* Pass 3 errors */
/* Pass 3: Checking directory connectivity */
diff --git a/e2fsck/problem.h b/e2fsck/problem.h
index 3c4b1620..1e511a3a 100644
--- a/e2fsck/problem.h
+++ b/e2fsck/problem.h
@@ -385,6 +385,9 @@ struct problem_context {
/* Symlink too big */
#define PR_1_TOOBIG_SYMLINK 0x010046
+/* INDEX_FL flag set on a non-HTREE filesystem */
+#define PR_1_HTREE_SET 0x010047
+
/*
* Pass 1b errors
*/
@@ -584,6 +587,27 @@ struct problem_context {
/* Filesystem contains large files, but has no such flag in sb */
#define PR_2_FEATURE_LARGE_FILES 0x020033
+/* Node in HTREE directory not referenced */
+#define PR_2_HTREE_NOTREF 0x020034
+
+/* Node in HTREE directory referenced twice */
+#define PR_2_HTREE_DUPREF 0x020035
+
+/* Node in HTREE directory has bad min hash */
+#define PR_2_HTREE_MIN_HASH 0x020036
+
+/* Node in HTREE directory has bad max hash */
+#define PR_2_HTREE_MAX_HASH 0x020037
+
+/* Clear invalid HTREE directory */
+#define PR_2_HTREE_CLEAR 0x020038
+
+/* Clear the htree flag forcibly */
+#define PR_2_HTREE_FCLR 0x020039
+
+/* Bad block in htree interior node */
+#define PR_2_HTREE_BADBLK 0x02003A
+
/*
* Pass 3 errors
*/
diff --git a/e2fsck/unix.c b/e2fsck/unix.c
index e231888d..18f2b2c7 100644
--- a/e2fsck/unix.c
+++ b/e2fsck/unix.c
@@ -876,7 +876,16 @@ restart:
com_err(ctx->program_name, 0,
_("Warning: compression support is experimental.\n"));
#endif
-
+#ifndef ENABLE_HTREE
+ if (sb->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) {
+ com_err(ctx->program_name, 0,
+ _("E2fsck not compiled with HTREE support,\n\t"
+ "but filesystem %s has HTREE directories.\n"),
+ ctx->device_name);
+ goto get_newer;
+ }
+#endif
+
/*
* If the user specified a specific superblock, presumably the
* master superblock has been trashed. So we mark the