* [RESEND PATCH v9 1/3] libfs: Introduce case-insensitive string comparison helper
2024-02-08 6:43 [RESEND PATCH v9 0/3] Introduce case-insensitive string comparison helper Eugen Hristev
@ 2024-02-08 6:43 ` Eugen Hristev
2024-02-08 18:38 ` Gabriel Krisman Bertazi
2024-02-08 6:43 ` [RESEND PATCH v9 2/3] ext4: Reuse generic_ci_match for ci comparisons Eugen Hristev
` (2 subsequent siblings)
3 siblings, 1 reply; 11+ messages in thread
From: Eugen Hristev @ 2024-02-08 6:43 UTC (permalink / raw)
To: tytso, adilger.kernel, jaegeuk, chao, viro, brauner, linux-ext4,
linux-f2fs-devel
Cc: jack, linux-kernel, linux-fsdevel, kernel, eugen.hristev,
Gabriel Krisman Bertazi, Eric Biggers
From: Gabriel Krisman Bertazi <krisman@collabora.com>
generic_ci_match can be used by case-insensitive filesystems to compare
strings under lookup with dirents in a case-insensitive way. This
function is currently reimplemented by each filesystem supporting
casefolding, so this reduces code duplication in filesystem-specific
code.
Reviewed-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Gabriel Krisman Bertazi <krisman@collabora.com>
Signed-off-by: Eugen Hristev <eugen.hristev@collabora.com>
---
fs/libfs.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++
include/linux/fs.h | 4 +++
2 files changed, 72 insertions(+)
diff --git a/fs/libfs.c b/fs/libfs.c
index bb18884ff20e..f80cb982ac89 100644
--- a/fs/libfs.c
+++ b/fs/libfs.c
@@ -1773,6 +1773,74 @@ static const struct dentry_operations generic_ci_dentry_ops = {
.d_hash = generic_ci_d_hash,
.d_compare = generic_ci_d_compare,
};
+
+/**
+ * generic_ci_match() - Match a name (case-insensitively) with a dirent.
+ * @parent: Inode of the parent of the dirent under comparison
+ * @name: name under lookup.
+ * @folded_name: Optional pre-folded name under lookup
+ * @de_name: Dirent name.
+ * @de_name_len: dirent name length.
+ *
+ *
+ * Test whether a case-insensitive directory entry matches the filename
+ * being searched. If @folded_name is provided, it is used instead of
+ * recalculating the casefold of @name.
+ *
+ * Return: > 0 if the directory entry matches, 0 if it doesn't match, or
+ * < 0 on error.
+ */
+int generic_ci_match(const struct inode *parent,
+ const struct qstr *name,
+ const struct qstr *folded_name,
+ const u8 *de_name, u32 de_name_len)
+{
+ const struct super_block *sb = parent->i_sb;
+ const struct unicode_map *um = sb->s_encoding;
+ struct fscrypt_str decrypted_name = FSTR_INIT(NULL, de_name_len);
+ struct qstr dirent = QSTR_INIT(de_name, de_name_len);
+ int res, match = false;
+
+ if (IS_ENCRYPTED(parent)) {
+ const struct fscrypt_str encrypted_name =
+ FSTR_INIT((u8 *) de_name, de_name_len);
+
+ if (WARN_ON_ONCE(!fscrypt_has_encryption_key(parent)))
+ return -EINVAL;
+
+ decrypted_name.name = kmalloc(de_name_len, GFP_KERNEL);
+ if (!decrypted_name.name)
+ return -ENOMEM;
+ res = fscrypt_fname_disk_to_usr(parent, 0, 0, &encrypted_name,
+ &decrypted_name);
+ if (res < 0)
+ goto out;
+ dirent.name = decrypted_name.name;
+ dirent.len = decrypted_name.len;
+ }
+
+ if (folded_name->name)
+ res = utf8_strncasecmp_folded(um, folded_name, &dirent);
+ else
+ res = utf8_strncasecmp(um, name, &dirent);
+
+ if (!res)
+ match = true;
+ else if (res < 0 && !sb_has_strict_encoding(sb)) {
+ /*
+ * In non-strict mode, fallback to a byte comparison if
+ * the names have invalid characters.
+ */
+ res = 0;
+ match = ((name->len == dirent.len) &&
+ !memcmp(name->name, dirent.name, dirent.len));
+ }
+
+out:
+ kfree(decrypted_name.name);
+ return (res >= 0) ? match : res;
+}
+EXPORT_SYMBOL(generic_ci_match);
#endif
#ifdef CONFIG_FS_ENCRYPTION
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 820b93b2917f..7af691ff8d44 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -3296,6 +3296,10 @@ extern int generic_file_fsync(struct file *, loff_t, loff_t, int);
extern int generic_check_addressable(unsigned, u64);
extern void generic_set_encrypted_ci_d_ops(struct dentry *dentry);
+extern int generic_ci_match(const struct inode *parent,
+ const struct qstr *name,
+ const struct qstr *folded_name,
+ const u8 *de_name, u32 de_name_len);
static inline bool sb_has_encoding(const struct super_block *sb)
{
--
2.34.1
^ permalink raw reply related [flat|nested] 11+ messages in thread* Re: [RESEND PATCH v9 1/3] libfs: Introduce case-insensitive string comparison helper
2024-02-08 6:43 ` [RESEND PATCH v9 1/3] libfs: " Eugen Hristev
@ 2024-02-08 18:38 ` Gabriel Krisman Bertazi
2024-02-09 10:30 ` Eugen Hristev
0 siblings, 1 reply; 11+ messages in thread
From: Gabriel Krisman Bertazi @ 2024-02-08 18:38 UTC (permalink / raw)
To: Eugen Hristev
Cc: tytso, adilger.kernel, jaegeuk, chao, viro, brauner, linux-ext4,
linux-f2fs-devel, jack, linux-kernel, linux-fsdevel, kernel,
Gabriel Krisman Bertazi, Eric Biggers
Eugen Hristev <eugen.hristev@collabora.com> writes:
> From: Gabriel Krisman Bertazi <krisman@collabora.com>
>
> generic_ci_match can be used by case-insensitive filesystems to compare
> strings under lookup with dirents in a case-insensitive way. This
> function is currently reimplemented by each filesystem supporting
> casefolding, so this reduces code duplication in filesystem-specific
> code.
>
> Reviewed-by: Eric Biggers <ebiggers@google.com>
> Signed-off-by: Gabriel Krisman Bertazi <krisman@collabora.com>
> Signed-off-by: Eugen Hristev <eugen.hristev@collabora.com>
Hi Eugen,
Thanks for picking this up. Please, CC me in future versions.
> ---
> fs/libfs.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++
> include/linux/fs.h | 4 +++
> 2 files changed, 72 insertions(+)
>
> diff --git a/fs/libfs.c b/fs/libfs.c
> index bb18884ff20e..f80cb982ac89 100644
> --- a/fs/libfs.c
> +++ b/fs/libfs.c
> @@ -1773,6 +1773,74 @@ static const struct dentry_operations generic_ci_dentry_ops = {
> .d_hash = generic_ci_d_hash,
> .d_compare = generic_ci_d_compare,
> };
> +
> +/**
> + * generic_ci_match() - Match a name (case-insensitively) with a dirent.
> + * @parent: Inode of the parent of the dirent under comparison
> + * @name: name under lookup.
> + * @folded_name: Optional pre-folded name under lookup
> + * @de_name: Dirent name.
> + * @de_name_len: dirent name length.
> + *
> + *
> + * Test whether a case-insensitive directory entry matches the filename
> + * being searched. If @folded_name is provided, it is used instead of
> + * recalculating the casefold of @name.
Can we add a note that this is a filesystem helper for comparison with
directory entries, and VFS' ->d_compare should use generic_ci_d_compare.
> + *
> + * Return: > 0 if the directory entry matches, 0 if it doesn't match, or
> + * < 0 on error.
> + */
> +int generic_ci_match(const struct inode *parent,
> + const struct qstr *name,
> + const struct qstr *folded_name,
> + const u8 *de_name, u32 de_name_len)
> +{
> + const struct super_block *sb = parent->i_sb;
> + const struct unicode_map *um = sb->s_encoding;
> + struct fscrypt_str decrypted_name = FSTR_INIT(NULL, de_name_len);
> + struct qstr dirent = QSTR_INIT(de_name, de_name_len);
> + int res, match = false;
I know I originally wrote it this way, but match is an integer, so
let's use integers instead of false/true.
> +
> + if (IS_ENCRYPTED(parent)) {
> + const struct fscrypt_str encrypted_name =
> + FSTR_INIT((u8 *) de_name, de_name_len);
> +
> + if (WARN_ON_ONCE(!fscrypt_has_encryption_key(parent)))
> + return -EINVAL;
> +
> + decrypted_name.name = kmalloc(de_name_len, GFP_KERNEL);
> + if (!decrypted_name.name)
> + return -ENOMEM;
> + res = fscrypt_fname_disk_to_usr(parent, 0, 0, &encrypted_name,
> + &decrypted_name);
> + if (res < 0)
> + goto out;
> + dirent.name = decrypted_name.name;
> + dirent.len = decrypted_name.len;
> + }
> +
> + if (folded_name->name)
> + res = utf8_strncasecmp_folded(um, folded_name, &dirent);
> + else
> + res = utf8_strncasecmp(um, name, &dirent);
Similar to
https://git.kernel.org/pub/scm/linux/kernel/git/krisman/unicode.git/commit/?h=for-next&id=367122c529f35b4655acbe33c0cc4d6d3b32ba71
We should be checking for an exact-match first to avoid the utf8
comparison cost unnecessarily. The only problem is that we need to
ensure we fail for an invalid utf-8 de_name in "strict mode".
Fortunately, if folded_name->name exists, we know the name-under-lookup
was validated when initialized, so an exact-match of de_name must also
be valid. If folded_name is NULL, though, we might either have an
invalid utf-8 dentry-under-lookup or the allocation itself failed, so we
need to utf8_validate it.
Honestly, I don't care much about this !folded_name->name case, since
utf8_strncasecmp will do the right thing and an invalid utf8 on
case-insensitive directories should be an exception, not the norm. but
the code might get simpler if we do both:
(untested)
if (folded_name->name) {
if (dirent.len == folded_name->len &&
!memcmp(folded_name->name, dirent.name, dirent.len)) {
res = 1;
goto out;
}
res = utf8_strncasecmp_folded(um, folded_name, &dirent);
} else {
if (dirent.len == name->len &&
!memcmp(name->name, dirent.name, dirent.len) &&
(!sb_has_strict_encoding(sb) || !utf8_validate(um, name))) {
res = 1;
goto out;
}
res = utf8_strncasecmp(um, name, &dirent);
}
> +
> + if (!res)
> + match = true;
> + else if (res < 0 && !sb_has_strict_encoding(sb)) {
> + /*
> + * In non-strict mode, fallback to a byte comparison if
> + * the names have invalid characters.
> + */
> + res = 0;
> + match = ((name->len == dirent.len) &&
> + !memcmp(name->name, dirent.name, dirent.len));
> + }
This goes away entirely.
> +
> +out:
> + kfree(decrypted_name.name);
> + return (res >= 0) ? match : res;
and this becomes:
return res;
--
Gabriel Krisman Bertazi
^ permalink raw reply [flat|nested] 11+ messages in thread* Re: [RESEND PATCH v9 1/3] libfs: Introduce case-insensitive string comparison helper
2024-02-08 18:38 ` Gabriel Krisman Bertazi
@ 2024-02-09 10:30 ` Eugen Hristev
2024-02-09 14:40 ` Gabriel Krisman Bertazi
0 siblings, 1 reply; 11+ messages in thread
From: Eugen Hristev @ 2024-02-09 10:30 UTC (permalink / raw)
To: Gabriel Krisman Bertazi
Cc: tytso, adilger.kernel, jaegeuk, chao, viro, brauner, linux-ext4,
linux-f2fs-devel, jack, linux-kernel, linux-fsdevel, kernel,
Gabriel Krisman Bertazi, Eric Biggers
On 2/8/24 20:38, Gabriel Krisman Bertazi wrote:
> Eugen Hristev <eugen.hristev@collabora.com> writes:
>
>> From: Gabriel Krisman Bertazi <krisman@collabora.com>
>>
>> generic_ci_match can be used by case-insensitive filesystems to compare
>> strings under lookup with dirents in a case-insensitive way. This
>> function is currently reimplemented by each filesystem supporting
>> casefolding, so this reduces code duplication in filesystem-specific
>> code.
>>
>> Reviewed-by: Eric Biggers <ebiggers@google.com>
>> Signed-off-by: Gabriel Krisman Bertazi <krisman@collabora.com>
>> Signed-off-by: Eugen Hristev <eugen.hristev@collabora.com>
>
> Hi Eugen,
>
> Thanks for picking this up. Please, CC me in future versions.
Hello Gabriel,
Thanks for reviewing :)
>
>> ---
>> fs/libfs.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++
>> include/linux/fs.h | 4 +++
>> 2 files changed, 72 insertions(+)
>>
>> diff --git a/fs/libfs.c b/fs/libfs.c
>> index bb18884ff20e..f80cb982ac89 100644
>> --- a/fs/libfs.c
>> +++ b/fs/libfs.c
>> @@ -1773,6 +1773,74 @@ static const struct dentry_operations generic_ci_dentry_ops = {
>> .d_hash = generic_ci_d_hash,
>> .d_compare = generic_ci_d_compare,
>> };
>> +
>> +/**
>> + * generic_ci_match() - Match a name (case-insensitively) with a dirent.
>> + * @parent: Inode of the parent of the dirent under comparison
>> + * @name: name under lookup.
>> + * @folded_name: Optional pre-folded name under lookup
>> + * @de_name: Dirent name.
>> + * @de_name_len: dirent name length.
>> + *
>> + *
>> + * Test whether a case-insensitive directory entry matches the filename
>> + * being searched. If @folded_name is provided, it is used instead of
>> + * recalculating the casefold of @name.
>
> Can we add a note that this is a filesystem helper for comparison with
> directory entries, and VFS' ->d_compare should use generic_ci_d_compare.
>
>> + *
>> + * Return: > 0 if the directory entry matches, 0 if it doesn't match, or
>> + * < 0 on error.
>> + */
>> +int generic_ci_match(const struct inode *parent,
>> + const struct qstr *name,
>> + const struct qstr *folded_name,
>> + const u8 *de_name, u32 de_name_len)
>> +{
>> + const struct super_block *sb = parent->i_sb;
>> + const struct unicode_map *um = sb->s_encoding;
>> + struct fscrypt_str decrypted_name = FSTR_INIT(NULL, de_name_len);
>> + struct qstr dirent = QSTR_INIT(de_name, de_name_len);
>> + int res, match = false;
>
> I know I originally wrote it this way, but match is an integer, so
> let's use integers instead of false/true.
With the changes below, 'match' is no longer needed
>
>> +
>> + if (IS_ENCRYPTED(parent)) {
>> + const struct fscrypt_str encrypted_name =
>> + FSTR_INIT((u8 *) de_name, de_name_len);
>> +
>> + if (WARN_ON_ONCE(!fscrypt_has_encryption_key(parent)))
>> + return -EINVAL;
>> +
>> + decrypted_name.name = kmalloc(de_name_len, GFP_KERNEL);
>> + if (!decrypted_name.name)
>> + return -ENOMEM;
>> + res = fscrypt_fname_disk_to_usr(parent, 0, 0, &encrypted_name,
>> + &decrypted_name);
>> + if (res < 0)
>> + goto out;
>> + dirent.name = decrypted_name.name;
>> + dirent.len = decrypted_name.len;
>> + }
>> +
>> + if (folded_name->name)
>> + res = utf8_strncasecmp_folded(um, folded_name, &dirent);
>> + else
>> + res = utf8_strncasecmp(um, name, &dirent);
>
> Similar to
>
> https://git.kernel.org/pub/scm/linux/kernel/git/krisman/unicode.git/commit/?h=for-next&id=367122c529f35b4655acbe33c0cc4d6d3b32ba71
>
> We should be checking for an exact-match first to avoid the utf8
> comparison cost unnecessarily. The only problem is that we need to
> ensure we fail for an invalid utf-8 de_name in "strict mode".
>
> Fortunately, if folded_name->name exists, we know the name-under-lookup
> was validated when initialized, so an exact-match of de_name must also
> be valid. If folded_name is NULL, though, we might either have an
> invalid utf-8 dentry-under-lookup or the allocation itself failed, so we
> need to utf8_validate it.
>
> Honestly, I don't care much about this !folded_name->name case, since
> utf8_strncasecmp will do the right thing and an invalid utf8 on
> case-insensitive directories should be an exception, not the norm. but
> the code might get simpler if we do both:
>
> (untested)
I implemented your suggestion, but any idea about testing ? I ran smoke on xfstests
and it appears to be fine, but maybe some specific test case might try the
different paths here ?
Let me know,
Eugen
>
> if (folded_name->name) {
> if (dirent.len == folded_name->len &&
> !memcmp(folded_name->name, dirent.name, dirent.len)) {
> res = 1;
> goto out;
> }
> res = utf8_strncasecmp_folded(um, folded_name, &dirent);
> } else {
> if (dirent.len == name->len &&
> !memcmp(name->name, dirent.name, dirent.len) &&
> (!sb_has_strict_encoding(sb) || !utf8_validate(um, name))) {
> res = 1;
> goto out;
> }
> res = utf8_strncasecmp(um, name, &dirent);
> }
>
>> +
>> + if (!res)
>> + match = true;
>> + else if (res < 0 && !sb_has_strict_encoding(sb)) {
>> + /*
>> + * In non-strict mode, fallback to a byte comparison if
>> + * the names have invalid characters.
>> + */
>> + res = 0;
>> + match = ((name->len == dirent.len) &&
>> + !memcmp(name->name, dirent.name, dirent.len));
>> + }
>
> This goes away entirely.
>
>> +
>> +out:
>> + kfree(decrypted_name.name);
>> + return (res >= 0) ? match : res;
>
> and this becomes:
>
> return res;
>
^ permalink raw reply [flat|nested] 11+ messages in thread* Re: [RESEND PATCH v9 1/3] libfs: Introduce case-insensitive string comparison helper
2024-02-09 10:30 ` Eugen Hristev
@ 2024-02-09 14:40 ` Gabriel Krisman Bertazi
2024-02-13 4:44 ` Eugen Hristev
0 siblings, 1 reply; 11+ messages in thread
From: Gabriel Krisman Bertazi @ 2024-02-09 14:40 UTC (permalink / raw)
To: Eugen Hristev
Cc: tytso, adilger.kernel, jaegeuk, chao, viro, brauner, linux-ext4,
linux-f2fs-devel, jack, linux-kernel, linux-fsdevel, kernel,
Gabriel Krisman Bertazi, Eric Biggers
Eugen Hristev <eugen.hristev@collabora.com> writes:
> On 2/8/24 20:38, Gabriel Krisman Bertazi wrote:
>> (untested)
>
> I implemented your suggestion, but any idea about testing ? I ran smoke on xfstests
> and it appears to be fine, but maybe some specific test case might try the
> different paths here ?
Other than running the fstests quick group for each affected filesystems
looking for regressions, the way I'd do it is create a few files and
look them up with exact and inexact name matches. While doing that,
observe through bpftrace which functions got called and what they
returned.
Here, since you are testing the uncached lookup, you want to make sure
to drop the cached version prior to each lookup.
--
Gabriel Krisman Bertazi
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [RESEND PATCH v9 1/3] libfs: Introduce case-insensitive string comparison helper
2024-02-09 14:40 ` Gabriel Krisman Bertazi
@ 2024-02-13 4:44 ` Eugen Hristev
2024-02-13 16:09 ` Gabriel Krisman Bertazi
0 siblings, 1 reply; 11+ messages in thread
From: Eugen Hristev @ 2024-02-13 4:44 UTC (permalink / raw)
To: Gabriel Krisman Bertazi
Cc: tytso, adilger.kernel, jaegeuk, chao, viro, brauner, linux-ext4,
linux-f2fs-devel, jack, linux-kernel, linux-fsdevel, kernel,
Gabriel Krisman Bertazi, Eric Biggers
On 2/9/24 16:40, Gabriel Krisman Bertazi wrote:
> Eugen Hristev <eugen.hristev@collabora.com> writes:
>
>> On 2/8/24 20:38, Gabriel Krisman Bertazi wrote:
>
>>> (untested)
>>
>> I implemented your suggestion, but any idea about testing ? I ran smoke on xfstests
>> and it appears to be fine, but maybe some specific test case might try the
>> different paths here ?
>
> Other than running the fstests quick group for each affected filesystems
> looking for regressions, the way I'd do it is create a few files and
> look them up with exact and inexact name matches. While doing that,
> observe through bpftrace which functions got called and what they
> returned.
>
> Here, since you are testing the uncached lookup, you want to make sure
> to drop the cached version prior to each lookup.
>
Hello Gabriel,
With the changes you suggested, I get these errors now :
[ 107.409410] EXT4-fs error (device sda1): ext4_lookup:1816: inode #521217: comm
ls: 'CUC' linked to parent dir
ls: cannot access '/media/CI_dir/CUC': Structure needs cleaning
total 8
drwxr-xr-x 2 root root 4096 Feb 12 11:51 .
drwxr-xr-x 4 root root 4096 Feb 12 11:47 ..
-????????? ? ? ? ? ? CUC
Do you have any idea about what is wrong ?
Thanks,
Eugen
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [RESEND PATCH v9 1/3] libfs: Introduce case-insensitive string comparison helper
2024-02-13 4:44 ` Eugen Hristev
@ 2024-02-13 16:09 ` Gabriel Krisman Bertazi
0 siblings, 0 replies; 11+ messages in thread
From: Gabriel Krisman Bertazi @ 2024-02-13 16:09 UTC (permalink / raw)
To: Eugen Hristev
Cc: tytso, adilger.kernel, jaegeuk, chao, viro, brauner, linux-ext4,
linux-f2fs-devel, jack, linux-kernel, linux-fsdevel, kernel,
Gabriel Krisman Bertazi, Eric Biggers
Eugen Hristev <eugen.hristev@collabora.com> writes:
> On 2/9/24 16:40, Gabriel Krisman Bertazi wrote:
>> Eugen Hristev <eugen.hristev@collabora.com> writes:
> With the changes you suggested, I get these errors now :
>
> [ 107.409410] EXT4-fs error (device sda1): ext4_lookup:1816: inode #521217: comm
> ls: 'CUC' linked to parent dir
> ls: cannot access '/media/CI_dir/CUC': Structure needs cleaning
> total 8
> drwxr-xr-x 2 root root 4096 Feb 12 11:51 .
> drwxr-xr-x 4 root root 4096 Feb 12 11:47 ..
> -????????? ? ? ? ? ? CUC
>
> Do you have any idea about what is wrong ?
Hm, there's a bug somewhere. The lookup got broken and ls got an error.
Did you debug it a bit? can you share the code and a reproducer?
From a quick look at the example I suggested, utf8_strncasecmp* return 0
on match, but ext4_match should return true when matched. So remember to
negate the output:
...
res = !utf8_strncasecmp(um, name, &dirent);
...
--
Gabriel Krisman Bertazi
^ permalink raw reply [flat|nested] 11+ messages in thread
* [RESEND PATCH v9 2/3] ext4: Reuse generic_ci_match for ci comparisons
2024-02-08 6:43 [RESEND PATCH v9 0/3] Introduce case-insensitive string comparison helper Eugen Hristev
2024-02-08 6:43 ` [RESEND PATCH v9 1/3] libfs: " Eugen Hristev
@ 2024-02-08 6:43 ` Eugen Hristev
2024-02-08 18:42 ` Gabriel Krisman Bertazi
2024-02-08 6:43 ` [RESEND PATCH v9 3/3] f2fs: " Eugen Hristev
2024-07-24 2:16 ` [f2fs-dev] [RESEND PATCH v9 0/3] Introduce case-insensitive string comparison helper patchwork-bot+f2fs
3 siblings, 1 reply; 11+ messages in thread
From: Eugen Hristev @ 2024-02-08 6:43 UTC (permalink / raw)
To: tytso, adilger.kernel, jaegeuk, chao, viro, brauner, linux-ext4,
linux-f2fs-devel
Cc: jack, linux-kernel, linux-fsdevel, kernel, eugen.hristev,
Gabriel Krisman Bertazi
From: Gabriel Krisman Bertazi <krisman@collabora.com>
Instead of reimplementing ext4_match_ci, use the new libfs helper.
It also adds a comment explaining why fname->cf_name.name must be
checked prior to the encryption hash optimization, because that tripped
me before.
Signed-off-by: Gabriel Krisman Bertazi <krisman@collabora.com>
Signed-off-by: Eugen Hristev <eugen.hristev@collabora.com>
---
fs/ext4/namei.c | 91 +++++++++++++++----------------------------------
1 file changed, 27 insertions(+), 64 deletions(-)
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index e554c5a62ba9..6e7af8dc4dde 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -1390,58 +1390,6 @@ static void dx_insert_block(struct dx_frame *frame, u32 hash, ext4_lblk_t block)
}
#if IS_ENABLED(CONFIG_UNICODE)
-/*
- * Test whether a case-insensitive directory entry matches the filename
- * being searched for. If quick is set, assume the name being looked up
- * is already in the casefolded form.
- *
- * Returns: 0 if the directory entry matches, more than 0 if it
- * doesn't match or less than zero on error.
- */
-static int ext4_ci_compare(const struct inode *parent, const struct qstr *name,
- u8 *de_name, size_t de_name_len, bool quick)
-{
- const struct super_block *sb = parent->i_sb;
- const struct unicode_map *um = sb->s_encoding;
- struct fscrypt_str decrypted_name = FSTR_INIT(NULL, de_name_len);
- struct qstr entry = QSTR_INIT(de_name, de_name_len);
- int ret;
-
- if (IS_ENCRYPTED(parent)) {
- const struct fscrypt_str encrypted_name =
- FSTR_INIT(de_name, de_name_len);
-
- decrypted_name.name = kmalloc(de_name_len, GFP_KERNEL);
- if (!decrypted_name.name)
- return -ENOMEM;
- ret = fscrypt_fname_disk_to_usr(parent, 0, 0, &encrypted_name,
- &decrypted_name);
- if (ret < 0)
- goto out;
- entry.name = decrypted_name.name;
- entry.len = decrypted_name.len;
- }
-
- if (quick)
- ret = utf8_strncasecmp_folded(um, name, &entry);
- else
- ret = utf8_strncasecmp(um, name, &entry);
- if (ret < 0) {
- /* Handle invalid character sequence as either an error
- * or as an opaque byte sequence.
- */
- if (sb_has_strict_encoding(sb))
- ret = -EINVAL;
- else if (name->len != entry.len)
- ret = 1;
- else
- ret = !!memcmp(name->name, entry.name, entry.len);
- }
-out:
- kfree(decrypted_name.name);
- return ret;
-}
-
int ext4_fname_setup_ci_filename(struct inode *dir, const struct qstr *iname,
struct ext4_filename *name)
{
@@ -1503,20 +1451,35 @@ static bool ext4_match(struct inode *parent,
#if IS_ENABLED(CONFIG_UNICODE)
if (IS_CASEFOLDED(parent) &&
(!IS_ENCRYPTED(parent) || fscrypt_has_encryption_key(parent))) {
- if (fname->cf_name.name) {
- if (IS_ENCRYPTED(parent)) {
- if (fname->hinfo.hash != EXT4_DIRENT_HASH(de) ||
- fname->hinfo.minor_hash !=
- EXT4_DIRENT_MINOR_HASH(de)) {
+ int ret;
- return false;
- }
- }
- return !ext4_ci_compare(parent, &fname->cf_name,
- de->name, de->name_len, true);
+ /*
+ * Just checking IS_ENCRYPTED(parent) below is not
+ * sufficient to decide whether one can use the hash for
+ * skipping the string comparison, because the key might
+ * have been added right after
+ * ext4_fname_setup_ci_filename(). In this case, a hash
+ * mismatch will be a false negative. Therefore, make
+ * sure cf_name was properly initialized before
+ * considering the calculated hash.
+ */
+ if (IS_ENCRYPTED(parent) && fname->cf_name.name &&
+ (fname->hinfo.hash != EXT4_DIRENT_HASH(de) ||
+ fname->hinfo.minor_hash != EXT4_DIRENT_MINOR_HASH(de)))
+ return false;
+
+ ret = generic_ci_match(parent, fname->usr_fname,
+ &fname->cf_name, de->name,
+ de->name_len);
+ if (ret < 0) {
+ /*
+ * Treat comparison errors as not a match. The
+ * only case where it happens is on a disk
+ * corruption or ENOMEM.
+ */
+ return false;
}
- return !ext4_ci_compare(parent, fname->usr_fname, de->name,
- de->name_len, false);
+ return ret;
}
#endif
--
2.34.1
^ permalink raw reply related [flat|nested] 11+ messages in thread* Re: [RESEND PATCH v9 2/3] ext4: Reuse generic_ci_match for ci comparisons
2024-02-08 6:43 ` [RESEND PATCH v9 2/3] ext4: Reuse generic_ci_match for ci comparisons Eugen Hristev
@ 2024-02-08 18:42 ` Gabriel Krisman Bertazi
0 siblings, 0 replies; 11+ messages in thread
From: Gabriel Krisman Bertazi @ 2024-02-08 18:42 UTC (permalink / raw)
To: Eugen Hristev
Cc: tytso, adilger.kernel, jaegeuk, chao, viro, brauner, linux-ext4,
linux-f2fs-devel, jack, linux-kernel, linux-fsdevel, kernel,
Gabriel Krisman Bertazi
Eugen Hristev <eugen.hristev@collabora.com> writes:
> int ext4_fname_setup_ci_filename(struct inode *dir, const struct qstr *iname,
> struct ext4_filename *name)
> {
> @@ -1503,20 +1451,35 @@ static bool ext4_match(struct inode *parent,
> #if IS_ENABLED(CONFIG_UNICODE)
> if (IS_CASEFOLDED(parent) &&
> (!IS_ENCRYPTED(parent) || fscrypt_has_encryption_key(parent))) {
> - if (fname->cf_name.name) {
> - if (IS_ENCRYPTED(parent)) {
> - if (fname->hinfo.hash != EXT4_DIRENT_HASH(de) ||
> - fname->hinfo.minor_hash !=
> - EXT4_DIRENT_MINOR_HASH(de)) {
> + int ret;
>
> - return false;
> - }
> - }
> - return !ext4_ci_compare(parent, &fname->cf_name,
> - de->name, de->name_len, true);
> + /*
> + * Just checking IS_ENCRYPTED(parent) below is not
> + * sufficient to decide whether one can use the hash for
> + * skipping the string comparison, because the key might
> + * have been added right after
> + * ext4_fname_setup_ci_filename(). In this case, a hash
> + * mismatch will be a false negative. Therefore, make
> + * sure cf_name was properly initialized before
> + * considering the calculated hash.
> + */
> + if (IS_ENCRYPTED(parent) && fname->cf_name.name &&
> + (fname->hinfo.hash != EXT4_DIRENT_HASH(de) ||
> + fname->hinfo.minor_hash != EXT4_DIRENT_MINOR_HASH(de)))
> + return false;
> +
> + ret = generic_ci_match(parent, fname->usr_fname,
> + &fname->cf_name, de->name,
> + de->name_len);
> + if (ret < 0) {
> + /*
> + * Treat comparison errors as not a match. The
> + * only case where it happens is on a disk
> + * corruption or ENOMEM.
> + */
> + return false;
A minor problem with splitting the series as you did is that "ext4: Log
error when lookup of encoded dentry fails" conflicts with this change
and you get a merge conflict if it is applied in the wrong order.
--
Gabriel Krisman Bertazi
^ permalink raw reply [flat|nested] 11+ messages in thread
* [RESEND PATCH v9 3/3] f2fs: Reuse generic_ci_match for ci comparisons
2024-02-08 6:43 [RESEND PATCH v9 0/3] Introduce case-insensitive string comparison helper Eugen Hristev
2024-02-08 6:43 ` [RESEND PATCH v9 1/3] libfs: " Eugen Hristev
2024-02-08 6:43 ` [RESEND PATCH v9 2/3] ext4: Reuse generic_ci_match for ci comparisons Eugen Hristev
@ 2024-02-08 6:43 ` Eugen Hristev
2024-07-24 2:16 ` [f2fs-dev] [RESEND PATCH v9 0/3] Introduce case-insensitive string comparison helper patchwork-bot+f2fs
3 siblings, 0 replies; 11+ messages in thread
From: Eugen Hristev @ 2024-02-08 6:43 UTC (permalink / raw)
To: tytso, adilger.kernel, jaegeuk, chao, viro, brauner, linux-ext4,
linux-f2fs-devel
Cc: jack, linux-kernel, linux-fsdevel, kernel, eugen.hristev,
Gabriel Krisman Bertazi, Eric Biggers
From: Gabriel Krisman Bertazi <krisman@collabora.com>
Now that ci_match is part of libfs, make f2fs reuse it instead of having
a different implementation.
Reviewed-by: Chao Yu <chao@kernel.org>
Reviewed-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Gabriel Krisman Bertazi <krisman@collabora.com>
Signed-off-by: Eugen Hristev <eugen.hristev@collabora.com>
---
fs/f2fs/dir.c | 58 ++++-----------------------------------------------
1 file changed, 4 insertions(+), 54 deletions(-)
diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c
index f5b65cf36393..7953322b9b9e 100644
--- a/fs/f2fs/dir.c
+++ b/fs/f2fs/dir.c
@@ -185,58 +185,6 @@ static struct f2fs_dir_entry *find_in_block(struct inode *dir,
return f2fs_find_target_dentry(&d, fname, max_slots);
}
-#if IS_ENABLED(CONFIG_UNICODE)
-/*
- * Test whether a case-insensitive directory entry matches the filename
- * being searched for.
- *
- * Returns 1 for a match, 0 for no match, and -errno on an error.
- */
-static int f2fs_match_ci_name(const struct inode *dir, const struct qstr *name,
- const u8 *de_name, u32 de_name_len)
-{
- const struct super_block *sb = dir->i_sb;
- const struct unicode_map *um = sb->s_encoding;
- struct fscrypt_str decrypted_name = FSTR_INIT(NULL, de_name_len);
- struct qstr entry = QSTR_INIT(de_name, de_name_len);
- int res;
-
- if (IS_ENCRYPTED(dir)) {
- const struct fscrypt_str encrypted_name =
- FSTR_INIT((u8 *)de_name, de_name_len);
-
- if (WARN_ON_ONCE(!fscrypt_has_encryption_key(dir)))
- return -EINVAL;
-
- decrypted_name.name = kmalloc(de_name_len, GFP_KERNEL);
- if (!decrypted_name.name)
- return -ENOMEM;
- res = fscrypt_fname_disk_to_usr(dir, 0, 0, &encrypted_name,
- &decrypted_name);
- if (res < 0)
- goto out;
- entry.name = decrypted_name.name;
- entry.len = decrypted_name.len;
- }
-
- res = utf8_strncasecmp_folded(um, name, &entry);
- /*
- * In strict mode, ignore invalid names. In non-strict mode,
- * fall back to treating them as opaque byte sequences.
- */
- if (res < 0 && !sb_has_strict_encoding(sb)) {
- res = name->len == entry.len &&
- memcmp(name->name, entry.name, name->len) == 0;
- } else {
- /* utf8_strncasecmp_folded returns 0 on match */
- res = (res == 0);
- }
-out:
- kfree(decrypted_name.name);
- return res;
-}
-#endif /* CONFIG_UNICODE */
-
static inline int f2fs_match_name(const struct inode *dir,
const struct f2fs_filename *fname,
const u8 *de_name, u32 de_name_len)
@@ -245,8 +193,10 @@ static inline int f2fs_match_name(const struct inode *dir,
#if IS_ENABLED(CONFIG_UNICODE)
if (fname->cf_name.name)
- return f2fs_match_ci_name(dir, &fname->cf_name,
- de_name, de_name_len);
+ return generic_ci_match(dir, fname->usr_fname,
+ &fname->cf_name,
+ de_name, de_name_len);
+
#endif
f.usr_fname = fname->usr_fname;
f.disk_name = fname->disk_name;
--
2.34.1
^ permalink raw reply related [flat|nested] 11+ messages in thread* Re: [f2fs-dev] [RESEND PATCH v9 0/3] Introduce case-insensitive string comparison helper
2024-02-08 6:43 [RESEND PATCH v9 0/3] Introduce case-insensitive string comparison helper Eugen Hristev
` (2 preceding siblings ...)
2024-02-08 6:43 ` [RESEND PATCH v9 3/3] f2fs: " Eugen Hristev
@ 2024-07-24 2:16 ` patchwork-bot+f2fs
3 siblings, 0 replies; 11+ messages in thread
From: patchwork-bot+f2fs @ 2024-07-24 2:16 UTC (permalink / raw)
To: Eugen Hristev
Cc: tytso, adilger.kernel, jaegeuk, chao, viro, brauner, linux-ext4,
linux-f2fs-devel, linux-fsdevel, kernel, jack, linux-kernel
Hello:
This series was applied to jaegeuk/f2fs.git (dev)
by Christian Brauner <brauner@kernel.org>:
On Thu, 8 Feb 2024 08:43:31 +0200 you wrote:
> Hello,
>
> I am trying to respin the series here :
> https://www.spinics.net/lists/linux-ext4/msg85081.html
>
> To make it easier to apply I split it into smaller chunks which address
> one single thing.
> This series is based on top of the first series here:
> https://marc.info/?l=linux-ext4&m=170728813912935&w=2
>
> [...]
Here is the summary with links:
- [f2fs-dev,RESEND,v9,1/3] libfs: Introduce case-insensitive string comparison helper
(no matching commit)
- [f2fs-dev,RESEND,v9,2/3] ext4: Reuse generic_ci_match for ci comparisons
(no matching commit)
- [f2fs-dev,RESEND,v9,3/3] f2fs: Reuse generic_ci_match for ci comparisons
https://git.kernel.org/jaegeuk/f2fs/c/d66858eb0c72
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply [flat|nested] 11+ messages in thread