mirror of
https://github.com/torvalds/linux.git
synced 2024-11-10 14:11:52 +00:00
libfs: Attempt exact-match comparison first during casefolded lookup
Casefolded comparisons are (obviously) way more costly than a simple memcmp. Try the case-sensitive comparison first, falling-back to the case-insensitive lookup only when needed. This allows any exact-match lookup to complete without having to walk the utf8 trie. Note that, for strict mode, generic_ci_d_compare used to reject an invalid UTF-8 string, which would now be considered valid if it exact-matches the disk-name. But, if that is the case, the filesystem is corrupt. More than that, it really doesn't matter in practice, because the name-under-lookup will have already been rejected by generic_ci_d_hash and we won't even get here. The memcmp is safe under RCU because we are operating on str/len instead of dentry->d_name directly, and the caller guarantees their consistency between each other in __d_lookup_rcu_op_compare. Link: https://lore.kernel.org/r/87ttn2sip7.fsf_-_@mailhost.krisman.be Suggested-by: Linus Torvalds <torvalds@linux-foundation.org> Signed-off-by: Gabriel Krisman Bertazi <krisman@suse.de>
This commit is contained in:
parent
052d534373
commit
0906fbb2f7
40
fs/libfs.c
40
fs/libfs.c
@ -1704,16 +1704,28 @@ bool is_empty_dir_inode(struct inode *inode)
|
||||
static int generic_ci_d_compare(const struct dentry *dentry, unsigned int len,
|
||||
const char *str, const struct qstr *name)
|
||||
{
|
||||
const struct dentry *parent = READ_ONCE(dentry->d_parent);
|
||||
const struct inode *dir = READ_ONCE(parent->d_inode);
|
||||
const struct super_block *sb = dentry->d_sb;
|
||||
const struct unicode_map *um = sb->s_encoding;
|
||||
struct qstr qstr = QSTR_INIT(str, len);
|
||||
const struct dentry *parent;
|
||||
const struct inode *dir;
|
||||
char strbuf[DNAME_INLINE_LEN];
|
||||
int ret;
|
||||
struct qstr qstr;
|
||||
|
||||
/*
|
||||
* Attempt a case-sensitive match first. It is cheaper and
|
||||
* should cover most lookups, including all the sane
|
||||
* applications that expect a case-sensitive filesystem.
|
||||
*
|
||||
* This comparison is safe under RCU because the caller
|
||||
* guarantees the consistency between str and len. See
|
||||
* __d_lookup_rcu_op_compare() for details.
|
||||
*/
|
||||
if (len == name->len && !memcmp(str, name->name, len))
|
||||
return 0;
|
||||
|
||||
parent = READ_ONCE(dentry->d_parent);
|
||||
dir = READ_ONCE(parent->d_inode);
|
||||
if (!dir || !IS_CASEFOLDED(dir))
|
||||
goto fallback;
|
||||
return 1;
|
||||
|
||||
/*
|
||||
* If the dentry name is stored in-line, then it may be concurrently
|
||||
* modified by a rename. If this happens, the VFS will eventually retry
|
||||
@ -1724,20 +1736,14 @@ static int generic_ci_d_compare(const struct dentry *dentry, unsigned int len,
|
||||
if (len <= DNAME_INLINE_LEN - 1) {
|
||||
memcpy(strbuf, str, len);
|
||||
strbuf[len] = 0;
|
||||
qstr.name = strbuf;
|
||||
str = strbuf;
|
||||
/* prevent compiler from optimizing out the temporary buffer */
|
||||
barrier();
|
||||
}
|
||||
ret = utf8_strncasecmp(um, name, &qstr);
|
||||
if (ret >= 0)
|
||||
return ret;
|
||||
qstr.len = len;
|
||||
qstr.name = str;
|
||||
|
||||
if (sb_has_strict_encoding(sb))
|
||||
return -EINVAL;
|
||||
fallback:
|
||||
if (len != name->len)
|
||||
return 1;
|
||||
return !!memcmp(str, name->name, len);
|
||||
return utf8_strncasecmp(dentry->d_sb->s_encoding, name, &qstr);
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user