1 ext3: Add support for non-native signed/unsigned htree hash algorithms
3 From: Theodore Ts'o <tytso@mit.edu>
5 The original ext3 hash algorithms assumed that variables of type char
6 were signed, as God and K&R intended. Unfortunately, this assumption
7 is not true on some architectures. Userspace support for marking
8 filesystems with non-native signed/unsigned chars was added two years
9 ago, but the kernel-side support was never added (until now).
11 Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
12 Cc: akpm@linux-foundation.org
13 Cc: linux-kernel@vger.kernel.org
15 fs/ext3/hash.c | 77 ++++++++++++++++++++++++++++++++++++++------
16 fs/ext3/namei.c | 7 ++++
17 fs/ext3/super.c | 12 +++++++
18 include/linux/ext3_fs.h | 28 +++++++++++++++-
19 include/linux/ext3_fs_sb.h | 1 +
20 5 files changed, 114 insertions(+), 11 deletions(-)
22 diff --git a/fs/ext3/hash.c b/fs/ext3/hash.c
23 index c30e149..7d215b4 100644
26 @@ -35,23 +35,71 @@ static void TEA_transform(__u32 buf[4], __u32 const in[])
29 /* The old legacy hash */
30 -static __u32 dx_hack_hash (const char *name, int len)
31 +static __u32 dx_hack_hash_unsigned(const char *name, int len)
33 - __u32 hash0 = 0x12a3fe2d, hash1 = 0x37abe8f9;
34 + __u32 hash, hash0 = 0x12a3fe2d, hash1 = 0x37abe8f9;
35 + const unsigned char *ucp = (const unsigned char *) name;
38 + hash = hash1 + (hash0 ^ (((int) *ucp++) * 7152373));
40 + if (hash & 0x80000000)
48 +static __u32 dx_hack_hash_signed(const char *name, int len)
50 + __u32 hash, hash0 = 0x12a3fe2d, hash1 = 0x37abe8f9;
51 + const signed char *scp = (const signed char *) name;
54 - __u32 hash = hash1 + (hash0 ^ (*name++ * 7152373));
55 + hash = hash1 + (hash0 ^ (((int) *scp++) * 7152373));
57 - if (hash & 0x80000000) hash -= 0x7fffffff;
58 + if (hash & 0x80000000)
63 - return (hash0 << 1);
67 -static void str2hashbuf(const char *msg, int len, __u32 *buf, int num)
68 +static void str2hashbuf_signed(const char *msg, int len, __u32 *buf, int num)
72 + const signed char *scp = (const signed char *) msg;
74 + pad = (__u32)len | ((__u32)len << 8);
80 + for (i = 0; i < len; i++) {
83 + val = ((int) scp[i]) + (val << 8);
96 +static void str2hashbuf_unsigned(const char *msg, int len, __u32 *buf, int num)
100 + const unsigned char *ucp = (const unsigned char *) msg;
102 pad = (__u32)len | ((__u32)len << 8);
104 @@ -62,7 +110,7 @@ static void str2hashbuf(const char *msg, int len, __u32 *buf, int num)
105 for (i=0; i < len; i++) {
108 - val = msg[i] + (val << 8);
109 + val = ((int) ucp[i]) + (val << 8);
113 @@ -95,6 +143,8 @@ int ext3fs_dirhash(const char *name, int len, struct dx_hash_info *hinfo)
117 + void (*str2hashbuf)(const char *, int, __u32 *, int) =
118 + str2hashbuf_signed;
120 /* Initialize the default seed for the hash checksum functions */
122 @@ -113,13 +163,18 @@ int ext3fs_dirhash(const char *name, int len, struct dx_hash_info *hinfo)
125 switch (hinfo->hash_version) {
126 + case DX_HASH_LEGACY_UNSIGNED:
127 + hash = dx_hack_hash_unsigned(name, len);
130 - hash = dx_hack_hash(name, len);
131 + hash = dx_hack_hash_signed(name, len);
133 + case DX_HASH_HALF_MD4_UNSIGNED:
134 + str2hashbuf = str2hashbuf_unsigned;
135 case DX_HASH_HALF_MD4:
138 - str2hashbuf(p, len, in, 8);
139 + (*str2hashbuf)(p, len, in, 8);
140 half_md4_transform(buf, in);
143 @@ -127,10 +182,12 @@ int ext3fs_dirhash(const char *name, int len, struct dx_hash_info *hinfo)
147 + case DX_HASH_TEA_UNSIGNED:
148 + str2hashbuf = str2hashbuf_unsigned;
152 - str2hashbuf(p, len, in, 4);
153 + (*str2hashbuf)(p, len, in, 4);
154 TEA_transform(buf, in);
157 diff --git a/fs/ext3/namei.c b/fs/ext3/namei.c
158 index 3e5edc9..4fb0493 100644
159 --- a/fs/ext3/namei.c
160 +++ b/fs/ext3/namei.c
161 @@ -368,6 +368,8 @@ dx_probe(struct qstr *entry, struct inode *dir,
164 hinfo->hash_version = root->info.hash_version;
165 + if (hinfo->hash_version <= DX_HASH_TEA)
166 + hinfo->hash_version += EXT3_SB(dir->i_sb)->s_hash_unsigned;
167 hinfo->seed = EXT3_SB(dir->i_sb)->s_hash_seed;
169 ext3fs_dirhash(entry->name, entry->len, hinfo);
170 @@ -636,6 +638,9 @@ int ext3_htree_fill_tree(struct file *dir_file, __u32 start_hash,
171 dir = dir_file->f_path.dentry->d_inode;
172 if (!(EXT3_I(dir)->i_flags & EXT3_INDEX_FL)) {
173 hinfo.hash_version = EXT3_SB(dir->i_sb)->s_def_hash_version;
174 + if (hinfo.hash_version <= DX_HASH_TEA)
175 + hinfo.hash_version +=
176 + EXT3_SB(dir->i_sb)->s_hash_unsigned;
177 hinfo.seed = EXT3_SB(dir->i_sb)->s_hash_seed;
178 count = htree_dirblock_to_tree(dir_file, dir, 0, &hinfo,
179 start_hash, start_minor_hash);
180 @@ -1398,6 +1403,8 @@ static int make_indexed_dir(handle_t *handle, struct dentry *dentry,
182 /* Initialize as for dx_probe */
183 hinfo.hash_version = root->info.hash_version;
184 + if (hinfo.hash_version <= DX_HASH_TEA)
185 + hinfo.hash_version += EXT3_SB(dir->i_sb)->s_hash_unsigned;
186 hinfo.seed = EXT3_SB(dir->i_sb)->s_hash_seed;
187 ext3fs_dirhash(name, namelen, &hinfo);
189 diff --git a/fs/ext3/super.c b/fs/ext3/super.c
190 index e5717a4..f175396 100644
191 --- a/fs/ext3/super.c
192 +++ b/fs/ext3/super.c
193 @@ -1744,6 +1744,18 @@ static int ext3_fill_super (struct super_block *sb, void *data, int silent)
194 for (i=0; i < 4; i++)
195 sbi->s_hash_seed[i] = le32_to_cpu(es->s_hash_seed[i]);
196 sbi->s_def_hash_version = es->s_def_hash_version;
197 + i = le32_to_cpu(es->s_flags);
198 + if (i & EXT2_FLAGS_UNSIGNED_HASH)
199 + sbi->s_hash_unsigned = 3;
200 + else if ((i & EXT2_FLAGS_SIGNED_HASH) == 0) {
201 +#ifdef __CHAR_UNSIGNED__
202 + es->s_flags |= cpu_to_le32(EXT2_FLAGS_UNSIGNED_HASH);
203 + sbi->s_hash_unsigned = 3;
205 + es->s_flags |= cpu_to_le32(EXT2_FLAGS_SIGNED_HASH);
210 if (sbi->s_blocks_per_group > blocksize * 8) {
212 diff --git a/include/linux/ext3_fs.h b/include/linux/ext3_fs.h
213 index d14f029..9004794 100644
214 --- a/include/linux/ext3_fs.h
215 +++ b/include/linux/ext3_fs.h
216 @@ -354,6 +354,13 @@ struct ext3_inode {
217 #define EXT3_ORPHAN_FS 0x0004 /* Orphans being recovered */
220 + * Misc. filesystem flags
222 +#define EXT2_FLAGS_SIGNED_HASH 0x0001 /* Signed dirhash in use */
223 +#define EXT2_FLAGS_UNSIGNED_HASH 0x0002 /* Unsigned dirhash in use */
224 +#define EXT2_FLAGS_TEST_FILESYS 0x0004 /* to test development code */
229 #define EXT3_MOUNT_CHECK 0x00001 /* Do mount-time checks */
230 @@ -489,7 +496,23 @@ struct ext3_super_block {
231 __u16 s_reserved_word_pad;
232 __le32 s_default_mount_opts;
233 __le32 s_first_meta_bg; /* First metablock block group */
234 - __u32 s_reserved[190]; /* Padding to the end of the block */
235 + __le32 s_mkfs_time; /* When the filesystem was created */
236 + __le32 s_jnl_blocks[17]; /* Backup of the journal inode */
237 + /* 64bit support valid if EXT4_FEATURE_COMPAT_64BIT */
238 +/*150*/ __le32 s_blocks_count_hi; /* Blocks count */
239 + __le32 s_r_blocks_count_hi; /* Reserved blocks count */
240 + __le32 s_free_blocks_count_hi; /* Free blocks count */
241 + __le16 s_min_extra_isize; /* All inodes have at least # bytes */
242 + __le16 s_want_extra_isize; /* New inodes should reserve # bytes */
243 + __le32 s_flags; /* Miscellaneous flags */
244 + __le16 s_raid_stride; /* RAID stride */
245 + __le16 s_mmp_interval; /* # seconds to wait in MMP checking */
246 + __le64 s_mmp_block; /* Block for multi-mount protection */
247 + __le32 s_raid_stripe_width; /* blocks on all data disks (N*stride)*/
248 + __u8 s_log_groups_per_flex; /* FLEX_BG group size */
249 + __u8 s_reserved_char_pad2;
250 + __le16 s_reserved_pad;
251 + __u32 s_reserved[162]; /* Padding to the end of the block */
255 @@ -694,6 +717,9 @@ static inline __le16 ext3_rec_len_to_disk(unsigned len)
256 #define DX_HASH_LEGACY 0
257 #define DX_HASH_HALF_MD4 1
258 #define DX_HASH_TEA 2
259 +#define DX_HASH_LEGACY_UNSIGNED 3
260 +#define DX_HASH_HALF_MD4_UNSIGNED 4
261 +#define DX_HASH_TEA_UNSIGNED 5
265 diff --git a/include/linux/ext3_fs_sb.h b/include/linux/ext3_fs_sb.h
266 index b65f028..50dc80a 100644
267 --- a/include/linux/ext3_fs_sb.h
268 +++ b/include/linux/ext3_fs_sb.h
269 @@ -57,6 +57,7 @@ struct ext3_sb_info {
270 u32 s_next_generation;
272 int s_def_hash_version;
273 + int s_hash_unsigned; /* 3 if hash should be signed, 0 if not */
274 struct percpu_counter s_freeblocks_counter;
275 struct percpu_counter s_freeinodes_counter;
276 struct percpu_counter s_dirs_counter;