mirror of https://github.com/vitalif/e2fsprogs
Add initial support for htree directories.
parent
88372d5c4b
commit
8fdc9985c1
|
@ -1,4 +1,8 @@
|
|||
2002-05-24 <tytso@snap.thunk.org>
|
||||
2002-06-25 Theodore Ts'o <tytso@mit.edu>
|
||||
|
||||
* configure.in, configure: Add --enable-htree and --enable-clear-htree
|
||||
|
||||
2002-05-24 Theodore Ts'o <tytso@mit.edu>
|
||||
|
||||
* configure.in: Add makefile for lib/evms for the EVMS FSIM
|
||||
plugin. Add --enable-old-evms configure option which uses
|
||||
|
|
39
configure.in
39
configure.in
|
@ -125,6 +125,45 @@ fi
|
|||
echo "Disabling compression support by default"
|
||||
)
|
||||
dnl
|
||||
dnl handle --enable-htree
|
||||
dnl
|
||||
AC_ARG_ENABLE([htree],
|
||||
[ --enable-htree enable EXPERIMENTAL htree directory support],
|
||||
if test "$enableval" = "no"
|
||||
then
|
||||
HTREE_CMT=#
|
||||
echo "Disabling htree directory support"
|
||||
else
|
||||
HTREE_CMT=
|
||||
AC_DEFINE(ENABLE_HTREE)
|
||||
echo "Enabling htree directory support"
|
||||
echo "WARNING: htree support is experimental"
|
||||
fi
|
||||
,
|
||||
HTREE_CMT=#
|
||||
echo "Disabling htree directory support by default"
|
||||
)
|
||||
AC_SUBST(HTREE_CMT)
|
||||
dnl
|
||||
dnl handle --enable-clear-htree
|
||||
dnl
|
||||
AC_ARG_ENABLE([htree-clear],
|
||||
[ --enable-htree-clear clear htree because we don't trust e2fsck],
|
||||
if test "$enableval" = "no"
|
||||
then
|
||||
HTREE_CLR_CMT=#
|
||||
echo "Disabling htree clearing"
|
||||
else
|
||||
HTREE_CLR_CMT=
|
||||
AC_DEFINE(ENABLE_HTREE_CLEAR)
|
||||
echo "Enabling htree clearing"
|
||||
fi
|
||||
,
|
||||
HTREE_CLR_CMT=#
|
||||
echo "Disabling htree clearing by default"
|
||||
)
|
||||
AC_SUBST(HTREE_CLR_CMT)
|
||||
dnl
|
||||
dnl handle --enable-old-evms
|
||||
dnl
|
||||
AC_ARG_ENABLE([old-evms],
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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 */
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
@ -207,6 +241,13 @@ struct e2fsck_struct {
|
|||
int dir_info_size;
|
||||
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
|
||||
*/
|
||||
|
@ -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);
|
||||
|
|
|
@ -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"),
|
||||
|
|
|
@ -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++;
|
||||
|
||||
|
|
256
e2fsck/pass2.c
256
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)
|
||||
{
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,3 +1,12 @@
|
|||
2002-06-25 Theodore Ts'o <tytso@mit.edu>
|
||||
|
||||
* Makefile.in (test_script): Add pass in the state of
|
||||
--enable-htree and --enable-clear-htree to the test
|
||||
script.
|
||||
|
||||
* f_h_normal, f_h_badnode: New test cases to test the htree
|
||||
directory code.
|
||||
|
||||
2002-06-09 Andreas Dilger <adilger@clusterfs.com>
|
||||
|
||||
* f_8192_block, f_16384_block: Basic tests of 8192-byte block
|
||||
|
|
|
@ -16,6 +16,8 @@ all:: @DO_TEST_SUITE@
|
|||
test_script: test_script.in Makefile
|
||||
@echo "Creating test_script..."
|
||||
@echo "#!/bin/sh" > test_script
|
||||
@HTREE_CMT@ @echo "HTREE=y" >> test_script
|
||||
@HTREE_CLR_CMT@ @echo "HTREE_CLR=y" >> test_script
|
||||
@echo "SRCDIR=@srcdir@" >> test_script
|
||||
@cat $(srcdir)/test_script.in >> test_script
|
||||
@chmod +x test_script
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
Pass 1: Checking inodes, blocks, and sizes
|
||||
Pass 2: Checking directory structure
|
||||
Problem in HTREE directory inode 12929: node (531) has bad max hash
|
||||
Problem in HTREE directory inode 12929: node (993) referenced twice
|
||||
Problem in HTREE directory inode 12929: node (1061) has bad min hash
|
||||
Problem in HTREE directory inode 12929: node (1062) has bad max hash
|
||||
Problem in HTREE directory inode 12929: node (1062) not referenced
|
||||
Invalid HTREE directory inode 12929 (/test2). Clear? yes
|
||||
|
||||
Pass 3: Checking directory connectivity
|
||||
Pass 4: Checking reference counts
|
||||
Pass 5: Checking group summary information
|
||||
|
||||
test_filesys: ***** FILE SYSTEM WAS MODIFIED *****
|
||||
test_filesys: 47730/100192 files (0.0% non-contiguous), 13689/31745 blocks
|
||||
Exit status is 1
|
|
@ -0,0 +1,7 @@
|
|||
Pass 1: Checking inodes, blocks, and sizes
|
||||
Pass 2: Checking directory structure
|
||||
Pass 3: Checking directory connectivity
|
||||
Pass 4: Checking reference counts
|
||||
Pass 5: Checking group summary information
|
||||
test_filesys: 47730/100192 files (0.0% non-contiguous), 13689/31745 blocks
|
||||
Exit status is 0
|
Binary file not shown.
|
@ -0,0 +1 @@
|
|||
hash directory with bad HTREE nodes
|
|
@ -0,0 +1,6 @@
|
|||
if test "$HTREE"x = yx -a "$HTREE_CLR"x = x; then
|
||||
. $cmd_dir/run_e2fsck
|
||||
else
|
||||
rm -f $test_name.ok $test_name.failed
|
||||
echo "skipped"
|
||||
fi
|
|
@ -0,0 +1,7 @@
|
|||
Pass 1: Checking inodes, blocks, and sizes
|
||||
Pass 2: Checking directory structure
|
||||
Pass 3: Checking directory connectivity
|
||||
Pass 4: Checking reference counts
|
||||
Pass 5: Checking group summary information
|
||||
test_filesys: 47729/100192 files (0.0% non-contiguous), 13687/31745 blocks
|
||||
Exit status is 0
|
|
@ -0,0 +1,7 @@
|
|||
Pass 1: Checking inodes, blocks, and sizes
|
||||
Pass 2: Checking directory structure
|
||||
Pass 3: Checking directory connectivity
|
||||
Pass 4: Checking reference counts
|
||||
Pass 5: Checking group summary information
|
||||
test_filesys: 47729/100192 files (0.0% non-contiguous), 13687/31745 blocks
|
||||
Exit status is 0
|
Binary file not shown.
|
@ -0,0 +1 @@
|
|||
Normal HTREE directory
|
|
@ -0,0 +1,6 @@
|
|||
if test "$HTREE"x = yx -a "$HTREE_CLR"x = x; then
|
||||
. $cmd_dir/run_e2fsck
|
||||
else
|
||||
rm -f $test_name.ok $test_name.failed
|
||||
echo "skipped"
|
||||
fi
|
|
@ -0,0 +1,3 @@
|
|||
if test "$HTREE"x = yx ; then
|
||||
. $cmd_dir/run_e2fsck
|
||||
fi
|
Loading…
Reference in New Issue