Re: [PATCH v7 6/8] f2fs: Handle casefolding with Encryption

From: Eric Biggers
Date: Wed Feb 12 2020 - 00:47:25 EST


On Fri, Feb 07, 2020 at 05:35:50PM -0800, Daniel Rosenberg wrote:
> @@ -173,24 +193,24 @@ static inline bool f2fs_match_name(struct f2fs_dentry_ptr *d,
> {
> #ifdef CONFIG_UNICODE
> struct inode *parent = d->inode;
> - struct super_block *sb = parent->i_sb;
> - struct qstr entry;
> + unsigned char *name;
> + int len;
> #endif
>
> if (de->hash_code != namehash)
> return false;
>
> #ifdef CONFIG_UNICODE
> - entry.name = d->filename[bit_pos];
> - entry.len = de->name_len;
> + name = d->filename[bit_pos];
> + len = de->name_len;

This is missing le16_to_cpu().

> int f2fs_add_regular_entry(struct inode *dir, const struct qstr *new_name,
> const struct qstr *orig_name,
> + f2fs_hash_t dentry_hash,
> struct inode *inode, nid_t ino, umode_t mode)
> {
> unsigned int bit_pos;
> unsigned int level;
> unsigned int current_depth;
> unsigned long bidx, block;
> - f2fs_hash_t dentry_hash;
> unsigned int nbucket, nblock;
> struct page *dentry_page = NULL;
> struct f2fs_dentry_block *dentry_blk = NULL;
> @@ -632,7 +652,6 @@ int f2fs_add_regular_entry(struct inode *dir, const struct qstr *new_name,
>
> level = 0;
> slots = GET_DENTRY_SLOTS(new_name->len);
> - dentry_hash = f2fs_dentry_hash(dir, new_name, NULL);

Why was the call to f2fs_dentry_hash() moved to the caller, but for
f2fs_add_inline_entry() a different approach was taken?

> @@ -718,17 +737,19 @@ int f2fs_add_dentry(struct inode *dir, struct fscrypt_name *fname,
> struct inode *inode, nid_t ino, umode_t mode)
> {
> struct qstr new_name;
> + f2fs_hash_t dentry_hash;
> int err = -EAGAIN;
>
> new_name.name = fname_name(fname);
> new_name.len = fname_len(fname);
>
> if (f2fs_has_inline_dentry(dir))
> - err = f2fs_add_inline_entry(dir, &new_name, fname->usr_fname,
> + err = f2fs_add_inline_entry(dir, &new_name, fname,
> inode, ino, mode);

I'm really confused. Why are you passing around both new_name and fname?
We already have new_name == fname.disk_name. So isn't just the
'struct fscrypt_name' sufficient?

> +static f2fs_hash_t __f2fs_dentry_hash(const struct inode *dir,
> + const struct qstr *name_info,
> + const struct fscrypt_name *fname)
> {
> __u32 hash;
> f2fs_hash_t f2fs_hash;
> @@ -85,6 +86,11 @@ static f2fs_hash_t __f2fs_dentry_hash(const struct qstr *name_info,
> if (is_dot_dotdot(name_info))
> return 0;
>
> + if (IS_CASEFOLDED(dir) && IS_ENCRYPTED(dir)) {
> + f2fs_hash = fscrypt_fname_siphash(dir, name_info);
> + return f2fs_hash;
> + }

This is missing cpu_to_le32().

Also, above we have:

/* encrypted bigname case */
if (fname && !fname->disk_name.name)
return cpu_to_le32(fname->hash);

That won't work with encrypted+casefolded directories without the key, because
now sometimes the hash from the no-key name is needed even when the disk_name is
available. This will cause a crash in fscrypt_fname_siphash() being called
without the key. I think you want:

if (fname && fname->is_ciphertext_name)
return cpu_to_le32(fname->hash);

Can you please write xfstests for encrypt+casefold?

- Eric