BACKPORT: ext4: optimize match for casefolded encrypted dirs
[ Upstream commit 1ae98e295fa2577fb5e492200c58d10230e00e99 ] Matching names with casefolded encrypting directories requires decrypting entries to confirm case since we are case preserving. We can avoid needing to decrypt if our hash values don't match. Signed-off-by: Daniel Rosenberg <drosen@google.com> Link: https://lore.kernel.org/r/20210319073414.1381041-3-drosen@google.com Signed-off-by: Theodore Ts'o <tytso@mit.edu> Change-Id: Ic74307e53dc59ec47575f33c56e291ccd137520d
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
ce3b26019c
commit
7f4133a55e
@@ -2406,9 +2406,9 @@ extern unsigned ext4_free_clusters_after_init(struct super_block *sb,
|
||||
ext4_fsblk_t ext4_inode_to_goal_block(struct inode *);
|
||||
|
||||
#ifdef CONFIG_UNICODE
|
||||
extern void ext4_fname_setup_ci_filename(struct inode *dir,
|
||||
extern int ext4_fname_setup_ci_filename(struct inode *dir,
|
||||
const struct qstr *iname,
|
||||
struct fscrypt_str *fname);
|
||||
struct ext4_filename *fname);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_FS_ENCRYPTION
|
||||
@@ -2439,9 +2439,9 @@ static inline int ext4_fname_setup_filename(struct inode *dir,
|
||||
ext4_fname_from_fscrypt_name(fname, &name);
|
||||
|
||||
#ifdef CONFIG_UNICODE
|
||||
ext4_fname_setup_ci_filename(dir, iname, &fname->cf_name);
|
||||
err = ext4_fname_setup_ci_filename(dir, iname, fname);
|
||||
#endif
|
||||
return 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline int ext4_fname_prepare_lookup(struct inode *dir,
|
||||
@@ -2458,9 +2458,9 @@ static inline int ext4_fname_prepare_lookup(struct inode *dir,
|
||||
ext4_fname_from_fscrypt_name(fname, &name);
|
||||
|
||||
#ifdef CONFIG_UNICODE
|
||||
ext4_fname_setup_ci_filename(dir, &dentry->d_name, &fname->cf_name);
|
||||
err = ext4_fname_setup_ci_filename(dir, &dentry->d_name, fname);
|
||||
#endif
|
||||
return 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline void ext4_fname_free_filename(struct ext4_filename *fname)
|
||||
@@ -2485,15 +2485,16 @@ static inline int ext4_fname_setup_filename(struct inode *dir,
|
||||
int lookup,
|
||||
struct ext4_filename *fname)
|
||||
{
|
||||
int err = 0;
|
||||
fname->usr_fname = iname;
|
||||
fname->disk_name.name = (unsigned char *) iname->name;
|
||||
fname->disk_name.len = iname->len;
|
||||
|
||||
#ifdef CONFIG_UNICODE
|
||||
ext4_fname_setup_ci_filename(dir, iname, &fname->cf_name);
|
||||
err = ext4_fname_setup_ci_filename(dir, iname, fname);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline int ext4_fname_prepare_lookup(struct inode *dir,
|
||||
|
||||
@@ -799,7 +799,9 @@ dx_probe(struct ext4_filename *fname, struct inode *dir,
|
||||
if (hinfo->hash_version <= DX_HASH_TEA)
|
||||
hinfo->hash_version += EXT4_SB(dir->i_sb)->s_hash_unsigned;
|
||||
hinfo->seed = EXT4_SB(dir->i_sb)->s_hash_seed;
|
||||
if (fname && fname_name(fname))
|
||||
/* hash is already computed for encrypted casefolded directory */
|
||||
if (fname && fname_name(fname) &&
|
||||
!(IS_ENCRYPTED(dir) && IS_CASEFOLDED(dir)))
|
||||
ext4fs_dirhash(dir, fname_name(fname), fname_len(fname), hinfo);
|
||||
hash = hinfo->hash;
|
||||
|
||||
@@ -1380,19 +1382,21 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ext4_fname_setup_ci_filename(struct inode *dir, const struct qstr *iname,
|
||||
struct fscrypt_str *cf_name)
|
||||
int ext4_fname_setup_ci_filename(struct inode *dir, const struct qstr *iname,
|
||||
struct ext4_filename *name)
|
||||
{
|
||||
struct fscrypt_str *cf_name = &name->cf_name;
|
||||
struct dx_hash_info *hinfo = &name->hinfo;
|
||||
int len;
|
||||
|
||||
if (!needs_casefold(dir)) {
|
||||
cf_name->name = NULL;
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
cf_name->name = kmalloc(EXT4_NAME_LEN, GFP_NOFS);
|
||||
if (!cf_name->name)
|
||||
return;
|
||||
return -ENOMEM;
|
||||
|
||||
len = utf8_casefold(dir->i_sb->s_encoding,
|
||||
iname, cf_name->name,
|
||||
@@ -1400,10 +1404,18 @@ void ext4_fname_setup_ci_filename(struct inode *dir, const struct qstr *iname,
|
||||
if (len <= 0) {
|
||||
kfree(cf_name->name);
|
||||
cf_name->name = NULL;
|
||||
return;
|
||||
}
|
||||
cf_name->len = (unsigned) len;
|
||||
if (!IS_ENCRYPTED(dir))
|
||||
return 0;
|
||||
|
||||
hinfo->hash_version = DX_HASH_SIPHASH;
|
||||
hinfo->seed = NULL;
|
||||
if (cf_name->name)
|
||||
ext4fs_dirhash(dir, cf_name->name, cf_name->len, hinfo);
|
||||
else
|
||||
ext4fs_dirhash(dir, iname->name, iname->len, hinfo);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1433,16 +1445,12 @@ static bool ext4_match(struct inode *parent,
|
||||
struct qstr cf = {.name = fname->cf_name.name,
|
||||
.len = fname->cf_name.len};
|
||||
if (IS_ENCRYPTED(parent)) {
|
||||
struct dx_hash_info hinfo;
|
||||
if (fname->hinfo.hash != EXT4_DIRENT_HASH(de) ||
|
||||
fname->hinfo.minor_hash !=
|
||||
EXT4_DIRENT_MINOR_HASH(de)) {
|
||||
|
||||
hinfo.hash_version = DX_HASH_SIPHASH;
|
||||
hinfo.seed = NULL;
|
||||
ext4fs_dirhash(parent, fname->cf_name.name,
|
||||
fname_len(fname), &hinfo);
|
||||
if (hinfo.hash != EXT4_DIRENT_HASH(de) ||
|
||||
hinfo.minor_hash !=
|
||||
EXT4_DIRENT_MINOR_HASH(de))
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return !ext4_ci_compare(parent, &cf, de->name,
|
||||
de->name_len, true);
|
||||
@@ -2070,15 +2078,11 @@ void ext4_insert_dentry(struct inode *dir,
|
||||
de->name_len = fname_len(fname);
|
||||
memcpy(de->name, fname_name(fname), fname_len(fname));
|
||||
if (ext4_hash_in_dirent(dir)) {
|
||||
struct dx_hash_info hinfo;
|
||||
struct dx_hash_info *hinfo = &fname->hinfo;
|
||||
|
||||
hinfo.hash_version = DX_HASH_SIPHASH;
|
||||
hinfo.seed = NULL;
|
||||
ext4fs_dirhash(dir, fname_usr_name(fname),
|
||||
fname_len(fname), &hinfo);
|
||||
EXT4_DIRENT_HASHES(de)->hash = cpu_to_le32(hinfo.hash);
|
||||
EXT4_DIRENT_HASHES(de)->hash = cpu_to_le32(hinfo->hash);
|
||||
EXT4_DIRENT_HASHES(de)->minor_hash =
|
||||
cpu_to_le32(hinfo.minor_hash);
|
||||
cpu_to_le32(hinfo->minor_hash);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2228,10 +2232,9 @@ static int make_indexed_dir(handle_t *handle, struct ext4_filename *fname,
|
||||
if (fname->hinfo.hash_version <= DX_HASH_TEA)
|
||||
fname->hinfo.hash_version += EXT4_SB(dir->i_sb)->s_hash_unsigned;
|
||||
fname->hinfo.seed = EXT4_SB(dir->i_sb)->s_hash_seed;
|
||||
if (ext4_hash_in_dirent(dir))
|
||||
ext4fs_dirhash(dir, fname_usr_name(fname),
|
||||
fname_len(fname), &fname->hinfo);
|
||||
else
|
||||
|
||||
/* casefolded encrypted hashes are computed on fname setup */
|
||||
if (!ext4_hash_in_dirent(dir))
|
||||
ext4fs_dirhash(dir, fname_name(fname),
|
||||
fname_len(fname), &fname->hinfo);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user