Skip to content

Commit 0906fbb

Browse files
committed
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>
1 parent 052d534 commit 0906fbb

File tree

1 file changed

+23
-17
lines changed

1 file changed

+23
-17
lines changed

fs/libfs.c

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1704,16 +1704,28 @@ bool is_empty_dir_inode(struct inode *inode)
17041704
static int generic_ci_d_compare(const struct dentry *dentry, unsigned int len,
17051705
const char *str, const struct qstr *name)
17061706
{
1707-
const struct dentry *parent = READ_ONCE(dentry->d_parent);
1708-
const struct inode *dir = READ_ONCE(parent->d_inode);
1709-
const struct super_block *sb = dentry->d_sb;
1710-
const struct unicode_map *um = sb->s_encoding;
1711-
struct qstr qstr = QSTR_INIT(str, len);
1707+
const struct dentry *parent;
1708+
const struct inode *dir;
17121709
char strbuf[DNAME_INLINE_LEN];
1713-
int ret;
1710+
struct qstr qstr;
1711+
1712+
/*
1713+
* Attempt a case-sensitive match first. It is cheaper and
1714+
* should cover most lookups, including all the sane
1715+
* applications that expect a case-sensitive filesystem.
1716+
*
1717+
* This comparison is safe under RCU because the caller
1718+
* guarantees the consistency between str and len. See
1719+
* __d_lookup_rcu_op_compare() for details.
1720+
*/
1721+
if (len == name->len && !memcmp(str, name->name, len))
1722+
return 0;
17141723

1724+
parent = READ_ONCE(dentry->d_parent);
1725+
dir = READ_ONCE(parent->d_inode);
17151726
if (!dir || !IS_CASEFOLDED(dir))
1716-
goto fallback;
1727+
return 1;
1728+
17171729
/*
17181730
* If the dentry name is stored in-line, then it may be concurrently
17191731
* 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,
17241736
if (len <= DNAME_INLINE_LEN - 1) {
17251737
memcpy(strbuf, str, len);
17261738
strbuf[len] = 0;
1727-
qstr.name = strbuf;
1739+
str = strbuf;
17281740
/* prevent compiler from optimizing out the temporary buffer */
17291741
barrier();
17301742
}
1731-
ret = utf8_strncasecmp(um, name, &qstr);
1732-
if (ret >= 0)
1733-
return ret;
1743+
qstr.len = len;
1744+
qstr.name = str;
17341745

1735-
if (sb_has_strict_encoding(sb))
1736-
return -EINVAL;
1737-
fallback:
1738-
if (len != name->len)
1739-
return 1;
1740-
return !!memcmp(str, name->name, len);
1746+
return utf8_strncasecmp(dentry->d_sb->s_encoding, name, &qstr);
17411747
}
17421748

17431749
/**

0 commit comments

Comments
 (0)