diff options
Diffstat (limited to 'fs/ext4/hash.c')
| -rw-r--r-- | fs/ext4/hash.c | 59 |
1 files changed, 57 insertions, 2 deletions
diff --git a/fs/ext4/hash.c b/fs/ext4/hash.c index e22dcfab308b..48483cd015d3 100644 --- a/fs/ext4/hash.c +++ b/fs/ext4/hash.c @@ -6,6 +6,7 @@ */ #include <linux/fs.h> +#include <linux/unicode.h> #include <linux/compiler.h> #include <linux/bitops.h> #include "ext4.h" @@ -196,7 +197,8 @@ static void str2hashbuf_unsigned(const char *msg, int len, __u32 *buf, int num) * represented, and whether or not the returned hash is 32 bits or 64 * bits. 32 bit hashes will return 0 for the minor hash. */ -int ext4fs_dirhash(const char *name, int len, struct dx_hash_info *hinfo) +static int __ext4fs_dirhash(const struct inode *dir, const char *name, int len, + struct dx_hash_info *hinfo) { __u32 hash; __u32 minor_hash = 0; @@ -231,6 +233,7 @@ int ext4fs_dirhash(const char *name, int len, struct dx_hash_info *hinfo) break; case DX_HASH_HALF_MD4_UNSIGNED: str2hashbuf = str2hashbuf_unsigned; + fallthrough; case DX_HASH_HALF_MD4: p = name; while (len > 0) { @@ -244,6 +247,7 @@ int ext4fs_dirhash(const char *name, int len, struct dx_hash_info *hinfo) break; case DX_HASH_TEA_UNSIGNED: str2hashbuf = str2hashbuf_unsigned; + fallthrough; case DX_HASH_TEA: p = name; while (len > 0) { @@ -255,9 +259,29 @@ int ext4fs_dirhash(const char *name, int len, struct dx_hash_info *hinfo) hash = buf[0]; minor_hash = buf[1]; break; + case DX_HASH_SIPHASH: + { + struct qstr qname = QSTR_INIT(name, len); + __u64 combined_hash; + + if (fscrypt_has_encryption_key(dir)) { + combined_hash = fscrypt_fname_siphash(dir, &qname); + } else { + ext4_warning_inode(dir, "Siphash requires key"); + return -EINVAL; + } + + hash = (__u32)(combined_hash >> 32); + minor_hash = (__u32)combined_hash; + break; + } default: hinfo->hash = 0; - return -1; + hinfo->minor_hash = 0; + ext4_warning(dir->i_sb, + "invalid/unsupported hash tree version %u", + hinfo->hash_version); + return -EINVAL; } hash = hash & ~1; if (hash == (EXT4_HTREE_EOF_32BIT << 1)) @@ -266,3 +290,34 @@ int ext4fs_dirhash(const char *name, int len, struct dx_hash_info *hinfo) hinfo->minor_hash = minor_hash; return 0; } + +int ext4fs_dirhash(const struct inode *dir, const char *name, int len, + struct dx_hash_info *hinfo) +{ +#if IS_ENABLED(CONFIG_UNICODE) + const struct unicode_map *um = dir->i_sb->s_encoding; + int r, dlen; + unsigned char *buff; + struct qstr qstr = {.name = name, .len = len }; + + if (len && IS_CASEFOLDED(dir) && + (!IS_ENCRYPTED(dir) || fscrypt_has_encryption_key(dir))) { + buff = kzalloc(PATH_MAX, GFP_KERNEL); + if (!buff) + return -ENOMEM; + + dlen = utf8_casefold(um, &qstr, buff, PATH_MAX); + if (dlen < 0) { + kfree(buff); + goto opaque_seq; + } + + r = __ext4fs_dirhash(dir, buff, dlen, hinfo); + + kfree(buff); + return r; + } +opaque_seq: +#endif + return __ext4fs_dirhash(dir, name, len, hinfo); +} |
