Merge tag 'for-next-6.9' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/krisman/unicode into vfs.misc
Merge case-insensitive updates from Gabriel Krisman Bertazi: - Patch case-insensitive lookup by trying the case-exact comparison first, before falling back to costly utf8 casefolded comparison. - Fix to forbid using a case-insensitive directory as part of an overlayfs mount. - Patchset to ensure d_op are set at d_alloc time for fscrypt and casefold volumes, ensuring filesystem dentries will all have the correct ops, whether they come from a lookup or not. * tag 'for-next-6.9' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/krisman/unicode: libfs: Drop generic_set_encrypted_ci_d_ops ubifs: Configure dentry operations at dentry-creation time f2fs: Configure dentry operations at dentry-creation time ext4: Configure dentry operations at dentry-creation time libfs: Add helper to choose dentry operations at mount-time libfs: Merge encrypted_ci_dentry_ops and ci_dentry_ops fscrypt: Drop d_revalidate once the key is added fscrypt: Drop d_revalidate for valid dentries during lookup fscrypt: Factor out a helper to configure the lookup dentry ovl: Always reject mounting over case-insensitive directories libfs: Attempt exact-match comparison first during casefolded lookup Signed-off-by: Christian Brauner <brauner@kernel.org>
This commit is contained in:
+40
-62
@@ -1738,16 +1738,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
|
||||
@@ -1758,20 +1770,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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1800,6 +1806,9 @@ static int generic_ci_d_hash(const struct dentry *dentry, struct qstr *str)
|
||||
static const struct dentry_operations generic_ci_dentry_ops = {
|
||||
.d_hash = generic_ci_d_hash,
|
||||
.d_compare = generic_ci_d_compare,
|
||||
#ifdef CONFIG_FS_ENCRYPTION
|
||||
.d_revalidate = fscrypt_d_revalidate,
|
||||
#endif
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -1809,64 +1818,33 @@ static const struct dentry_operations generic_encrypted_dentry_ops = {
|
||||
};
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_FS_ENCRYPTION) && IS_ENABLED(CONFIG_UNICODE)
|
||||
static const struct dentry_operations generic_encrypted_ci_dentry_ops = {
|
||||
.d_hash = generic_ci_d_hash,
|
||||
.d_compare = generic_ci_d_compare,
|
||||
.d_revalidate = fscrypt_d_revalidate,
|
||||
};
|
||||
#endif
|
||||
|
||||
/**
|
||||
* generic_set_encrypted_ci_d_ops - helper for setting d_ops for given dentry
|
||||
* @dentry: dentry to set ops on
|
||||
* generic_set_sb_d_ops - helper for choosing the set of
|
||||
* filesystem-wide dentry operations for the enabled features
|
||||
* @sb: superblock to be configured
|
||||
*
|
||||
* Casefolded directories need d_hash and d_compare set, so that the dentries
|
||||
* contained in them are handled case-insensitively. Note that these operations
|
||||
* are needed on the parent directory rather than on the dentries in it, and
|
||||
* while the casefolding flag can be toggled on and off on an empty directory,
|
||||
* dentry_operations can't be changed later. As a result, if the filesystem has
|
||||
* casefolding support enabled at all, we have to give all dentries the
|
||||
* casefolding operations even if their inode doesn't have the casefolding flag
|
||||
* currently (and thus the casefolding ops would be no-ops for now).
|
||||
*
|
||||
* Encryption works differently in that the only dentry operation it needs is
|
||||
* d_revalidate, which it only needs on dentries that have the no-key name flag.
|
||||
* The no-key flag can't be set "later", so we don't have to worry about that.
|
||||
*
|
||||
* Finally, to maximize compatibility with overlayfs (which isn't compatible
|
||||
* with certain dentry operations) and to avoid taking an unnecessary
|
||||
* performance hit, we use custom dentry_operations for each possible
|
||||
* combination rather than always installing all operations.
|
||||
* Filesystems supporting casefolding and/or fscrypt can call this
|
||||
* helper at mount-time to configure sb->s_d_op to best set of dentry
|
||||
* operations required for the enabled features. The helper must be
|
||||
* called after these have been configured, but before the root dentry
|
||||
* is created.
|
||||
*/
|
||||
void generic_set_encrypted_ci_d_ops(struct dentry *dentry)
|
||||
void generic_set_sb_d_ops(struct super_block *sb)
|
||||
{
|
||||
#ifdef CONFIG_FS_ENCRYPTION
|
||||
bool needs_encrypt_ops = dentry->d_flags & DCACHE_NOKEY_NAME;
|
||||
#endif
|
||||
#if IS_ENABLED(CONFIG_UNICODE)
|
||||
bool needs_ci_ops = dentry->d_sb->s_encoding;
|
||||
#endif
|
||||
#if defined(CONFIG_FS_ENCRYPTION) && IS_ENABLED(CONFIG_UNICODE)
|
||||
if (needs_encrypt_ops && needs_ci_ops) {
|
||||
d_set_d_op(dentry, &generic_encrypted_ci_dentry_ops);
|
||||
if (sb->s_encoding) {
|
||||
sb->s_d_op = &generic_ci_dentry_ops;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#ifdef CONFIG_FS_ENCRYPTION
|
||||
if (needs_encrypt_ops) {
|
||||
d_set_d_op(dentry, &generic_encrypted_dentry_ops);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#if IS_ENABLED(CONFIG_UNICODE)
|
||||
if (needs_ci_ops) {
|
||||
d_set_d_op(dentry, &generic_ci_dentry_ops);
|
||||
if (sb->s_cop) {
|
||||
sb->s_d_op = &generic_encrypted_dentry_ops;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
EXPORT_SYMBOL(generic_set_encrypted_ci_d_ops);
|
||||
EXPORT_SYMBOL(generic_set_sb_d_ops);
|
||||
|
||||
/**
|
||||
* inode_maybe_inc_iversion - increments i_version
|
||||
|
||||
Reference in New Issue
Block a user