summaryrefslogtreecommitdiff
path: root/e2fsck
diff options
context:
space:
mode:
authorTheodore Ts'o <tytso@mit.edu>2006-11-11 22:32:35 -0500
committerTheodore Ts'o <tytso@mit.edu>2006-11-11 22:32:35 -0500
commitf77704e416fca7dbe4cc91abba674d2ae3c14f6f (patch)
tree1a927102301da898881d0dc788d493381f61f463 /e2fsck
parent52325593b1979509dc3f18232b50359e311268cc (diff)
downloade2fsprogs-f77704e416fca7dbe4cc91abba674d2ae3c14f6f.tar.gz
Add directory hashed signed/unsigned hint to superblock
The e2fsprogs and kernel implementation of directory hash tree has a bug which causes the implementation to be dependent on whether characters are signed or unsigned. Platforms such as the PowerPC, Arm, and S/390 have signed characters by default, which means that hash directories on those systems are incompatible with hash directories on other systems, such as the x86. To fix this we add a new flags field to the superblock, and define two new bits in that field to indicate whether or not the directory should be signed or unsigned. If the bits are not set, e2fsck and fixed kernels will set them to the signed/unsigned value of the currently running platform, and then respect those bits when calculating the directory hash. This allows compatibility with current filesystems, as well as allowing cross-architectural compatibility. Addresses Debian Bug: #389772 Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
Diffstat (limited to 'e2fsck')
-rw-r--r--e2fsck/ChangeLog11
-rw-r--r--e2fsck/pass2.c5
-rw-r--r--e2fsck/problem.c5
-rw-r--r--e2fsck/problem.h3
-rw-r--r--e2fsck/rehash.c17
-rw-r--r--e2fsck/super.c35
6 files changed, 71 insertions, 5 deletions
diff --git a/e2fsck/ChangeLog b/e2fsck/ChangeLog
index 2f11bd75..24288fee 100644
--- a/e2fsck/ChangeLog
+++ b/e2fsck/ChangeLog
@@ -1,5 +1,16 @@
2006-11-11 Theodore Tso <tytso@mit.edu>
+ * super.c (e2fsck_fix_dirhash_hint, check_super_block): If neither
+ the signed or unsigned dirhash hint, set it based on
+ default signed vs. unsigned character type in use by the
+ platform.
+
+ * problem.c, problem.h (PR_0_DIRHASH_HINT): Add new problem code.
+
+ * pass2.c (check_dir_block), rehash.c (fill_dir_block): Check the
+ superblock flags to determine whether to use the signed or
+ unsigned version of the hash should be used.
+
* problem.c, problem.h (PR_2_BLOCKS_HI_ZERO): Add new problem code.
* pass1.c (e2fsck_pass1), pass2.c (e2fsck_process_bad_inode):
diff --git a/e2fsck/pass2.c b/e2fsck/pass2.c
index 8bcb3d18..6dc5e5ff 100644
--- a/e2fsck/pass2.c
+++ b/e2fsck/pass2.c
@@ -801,8 +801,11 @@ static int check_dir_block(ext2_filsys fs,
clear_htree(ctx, ino);
dx_dir->numblocks = 0;
dx_db = 0;
- }
+ }
dx_dir->hashversion = root->hash_version;
+ if ((dx_dir->hashversion <= EXT2_HASH_TEA) &&
+ (fs->super->s_flags & EXT2_FLAGS_UNSIGNED_HASH))
+ dx_dir->hashversion += 3;
dx_dir->depth = root->indirect_levels + 1;
} else if ((dirent->inode == 0) &&
(dirent->rec_len == fs->blocksize) &&
diff --git a/e2fsck/problem.c b/e2fsck/problem.c
index 7f6add40..cb535ad0 100644
--- a/e2fsck/problem.c
+++ b/e2fsck/problem.c
@@ -346,6 +346,11 @@ static struct e2fsck_problem problem_table[] = {
N_("@S hint for external superblock @s %X. "),
PROMPT_FIX, PR_PREEN_OK },
+ /* Adding dirhash hint */
+ { PR_0_DIRHASH_HINT,
+ N_("Adding dirhash hint to @f.\n\n"),
+ PROMPT_NONE, 0 },
+
/* Pass 1 errors */
/* Pass 1: Checking inodes, blocks, and sizes */
diff --git a/e2fsck/problem.h b/e2fsck/problem.h
index 4561c618..e5070b05 100644
--- a/e2fsck/problem.h
+++ b/e2fsck/problem.h
@@ -193,6 +193,9 @@ struct problem_context {
/* Superblock hint for external journal incorrect */
#define PR_0_EXTERNAL_JOURNAL_HINT 0x000033
+/* Superblock hint for external journal incorrect */
+#define PR_0_DIRHASH_HINT 0x000034
+
/*
* Pass 1 errors
*/
diff --git a/e2fsck/rehash.c b/e2fsck/rehash.c
index 727e08c2..4a73ddf5 100644
--- a/e2fsck/rehash.c
+++ b/e2fsck/rehash.c
@@ -88,6 +88,7 @@ static int fill_dir_block(ext2_filsys fs,
struct ext2_dir_entry *dirent;
char *dir;
unsigned int offset, dir_offset;
+ int hash_alg;
if (blockcnt < 0)
return 0;
@@ -107,6 +108,10 @@ static int fill_dir_block(ext2_filsys fs,
if (fd->err)
return BLOCK_ABORT;
}
+ hash_alg = fs->super->s_def_hash_version;
+ if ((hash_alg <= EXT2_HASH_TEA) &&
+ (fs->super->s_flags & EXT2_FLAGS_UNSIGNED_HASH))
+ hash_alg += 3;
/* While the directory block is "hot", index it. */
dir_offset = 0;
while (dir_offset < fs->blocksize) {
@@ -145,8 +150,7 @@ static int fill_dir_block(ext2_filsys fs,
if (fd->compress)
ent->hash = ent->minor_hash = 0;
else {
- fd->err = ext2fs_dirhash(fs->super->s_def_hash_version,
- dirent->name,
+ fd->err = ext2fs_dirhash(hash_alg, dirent->name,
dirent->name_len & 0xFF,
fs->super->s_hash_seed,
&ent->hash, &ent->minor_hash);
@@ -323,10 +327,16 @@ static int duplicate_search_and_fix(e2fsck_t ctx, ext2_filsys fs,
int fixed = 0;
char new_name[256];
__u16 new_len;
+ int hash_alg;
clear_problem_context(&pctx);
pctx.ino = ino;
+ hash_alg = fs->super->s_def_hash_version;
+ if ((hash_alg <= EXT2_HASH_TEA) &&
+ (fs->super->s_flags & EXT2_FLAGS_UNSIGNED_HASH))
+ hash_alg += 3;
+
for (i=1; i < fd->num_array; i++) {
ent = fd->harray + i;
prev = ent - 1;
@@ -363,8 +373,7 @@ static int duplicate_search_and_fix(e2fsck_t ctx, ext2_filsys fs,
if (fix_problem(ctx, PR_2_NON_UNIQUE_FILE, &pctx)) {
memcpy(ent->dir->name, new_name, new_len & 0xFF);
ent->dir->name_len = new_len;
- ext2fs_dirhash(fs->super->s_def_hash_version,
- ent->dir->name,
+ ext2fs_dirhash(hash_alg, ent->dir->name,
ent->dir->name_len & 0xFF,
fs->super->s_hash_seed,
&ent->hash, &ent->minor_hash);
diff --git a/e2fsck/super.c b/e2fsck/super.c
index ae40531c..62e03a5b 100644
--- a/e2fsck/super.c
+++ b/e2fsck/super.c
@@ -436,6 +436,36 @@ cleanup:
}
+/*
+ * This function checks the dirhash signed/unsigned hint if necessary.
+ */
+void e2fsck_fix_dirhash_hint(e2fsck_t ctx)
+{
+ struct ext2_super_block *sb = ctx->fs->super;
+ struct problem_context pctx;
+ problem_t problem;
+ int retval;
+ char c;
+
+ if ((ctx->options & E2F_OPT_READONLY) ||
+ !(sb->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) ||
+ (sb->s_flags & (EXT2_FLAGS_SIGNED_HASH|EXT2_FLAGS_UNSIGNED_HASH)))
+ return;
+
+ c = (char) 255;
+
+ clear_problem_context(&pctx);
+ if (fix_problem(ctx, PR_0_DIRHASH_HINT, &pctx)) {
+ if (((int) c) == -1) {
+ sb->s_flags |= EXT2_FLAGS_SIGNED_HASH;
+ } else {
+ sb->s_flags |= EXT2_FLAGS_UNSIGNED_HASH;
+ }
+ ext2fs_mark_super_dirty(ctx->fs);
+ }
+}
+
+
void check_super_block(e2fsck_t ctx)
{
ext2_filsys fs = ctx->fs;
@@ -732,5 +762,10 @@ void check_super_block(e2fsck_t ctx)
*/
e2fsck_fix_ext3_journal_hint(ctx);
+ /*
+ * Add dirhash hint if necessary
+ */
+ e2fsck_fix_dirhash_hint(ctx);
+
return;
}