Re: [PATCH v2 2/2] ext4: Optimize match for casefolded encrypted dirs

2021-04-05 Thread Theodore Ts'o
On Fri, Mar 19, 2021 at 07:34:14AM +, Daniel Rosenberg wrote:
> 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 

Thanks, applied.

- Ted


[PATCH v2 2/2] ext4: Optimize match for casefolded encrypted dirs

2021-03-19 Thread Daniel Rosenberg
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 
---
 fs/ext4/ext4.h  | 17 ---
 fs/ext4/namei.c | 55 ++---
 2 files changed, 38 insertions(+), 34 deletions(-)

diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index dafa528c4d9f..181d07791efb 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -2637,9 +2637,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
@@ -2670,9 +2670,9 @@ static inline int ext4_fname_setup_filename(struct inode 
*dir,
ext4_fname_from_fscrypt_name(fname, );
 
 #ifdef CONFIG_UNICODE
-   ext4_fname_setup_ci_filename(dir, iname, >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,
@@ -2689,9 +2689,9 @@ static inline int ext4_fname_prepare_lookup(struct inode 
*dir,
ext4_fname_from_fscrypt_name(fname, );
 
 #ifdef CONFIG_UNICODE
-   ext4_fname_setup_ci_filename(dir, >d_name, >cf_name);
+   err = ext4_fname_setup_ci_filename(dir, >d_name, fname);
 #endif
-   return 0;
+   return err;
 }
 
 static inline void ext4_fname_free_filename(struct ext4_filename *fname)
@@ -2716,15 +2716,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, >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,
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index 97d2755b9775..1fb7128220ce 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -816,7 +816,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;
 
@@ -1367,19 +1369,21 @@ static int ext4_ci_compare(const struct inode *parent, 
const struct qstr *name,
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 = >cf_name;
+   struct dx_hash_info *hinfo = >hinfo;
int len;
 
if (!IS_CASEFOLDED(dir) || !dir->i_sb->s_encoding) {
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,
@@ -1387,10 +1391,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
 
@@ -1420,16 +1432,12 @@ static bool ext4_match(struct inode *parent,
struct qstr cf = {.name = fname->cf_name.name,
  .len =