Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6e29e14f authored by Daniel Rosenberg's avatar Daniel Rosenberg
Browse files

FROMLIST: Add standard casefolding support



This adds general supporting functions for filesystems that use
utf8 casefolding. It provides standard dentry_operations and adds the
necessary structures in struct super_block to allow this standardization.

Ext4 and F2fs are switch to these implementations.

Signed-off-by: default avatarDaniel Rosenberg <drosen@google.com>
Note: Fixed issue with non-strictly enforced fallback hash
Test: Boots, /data/media is case insensitive
Bug: 138322712
Link: https://lore.kernel.org/linux-f2fs-devel/20200208013552.241832-1-drosen@google.com/T/#t
Change-Id: I81b5fb5d3ce0259a60712ae2505c1e4b03dbafde
parent 73f57a09
Loading
Loading
Loading
Loading
+61 −0
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@
#include <linux/exportfs.h>
#include <linux/writeback.h>
#include <linux/buffer_head.h> /* sync_mapping_buffers */
#include <linux/unicode.h>
#include <linux/fscrypt.h>

#include <linux/uaccess.h>

@@ -1258,3 +1260,62 @@ bool is_empty_dir_inode(struct inode *inode)
	return (inode->i_fop == &empty_dir_operations) &&
		(inode->i_op == &empty_dir_inode_operations);
}

#ifdef CONFIG_UNICODE
bool needs_casefold(const struct inode *dir)
{
	return IS_CASEFOLDED(dir) && dir->i_sb->s_encoding &&
			(!IS_ENCRYPTED(dir) || fscrypt_has_encryption_key(dir));
}
EXPORT_SYMBOL(needs_casefold);

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 *inode = READ_ONCE(parent->d_inode);
	const struct super_block *sb = dentry->d_sb;
	const struct unicode_map *um = sb->s_encoding;
	struct qstr entry = QSTR_INIT(str, len);
	int ret;

	if (!inode || !needs_casefold(inode))
		goto fallback;

	ret = utf8_strncasecmp(um, name, &entry);
	if (ret >= 0)
		return ret;

	if (sb_has_enc_strict_mode(sb))
		return -EINVAL;
fallback:
	if (len != name->len)
		return 1;
	return !!memcmp(str, name->name, len);
}
EXPORT_SYMBOL(generic_ci_d_compare);

int generic_ci_d_hash(const struct dentry *dentry, struct qstr *str)
{
	const struct inode *inode = READ_ONCE(dentry->d_inode);
	struct super_block *sb = dentry->d_sb;
	const struct unicode_map *um = sb->s_encoding;
	int ret = 0;

	if (!inode || !needs_casefold(inode))
		return 0;

	ret = utf8_casefold_hash(um, dentry, str);
	if (ret < 0)
		goto err;

	return 0;
err:
	if (sb_has_enc_strict_mode(sb))
		ret = -EINVAL;
	else
		ret = 0;
	return ret;
}
EXPORT_SYMBOL(generic_ci_d_hash);
#endif
+22 −0
Original line number Diff line number Diff line
@@ -1333,6 +1333,12 @@ extern int send_sigurg(struct fown_struct *fown);
#define SB_ACTIVE	(1<<30)
#define SB_NOUSER	(1<<31)

/* These flags relate to encoding and casefolding */
#define SB_ENC_STRICT_MODE_FL	(1 << 0)

#define sb_has_enc_strict_mode(sb) \
	(sb->s_encoding_flags & SB_ENC_STRICT_MODE_FL)

/*
 *	Umount options
 */
@@ -1400,6 +1406,10 @@ struct super_block {
#endif
#ifdef CONFIG_FS_VERITY
	const struct fsverity_operations *s_vop;
#endif
#ifdef CONFIG_UNICODE
	struct unicode_map *s_encoding;
	__u16 s_encoding_flags;
#endif
	struct hlist_bl_head	s_roots;	/* alternate root dentries for NFS */
	struct list_head	s_mounts;	/* list of mounts; _not_ for fs use */
@@ -3261,6 +3271,18 @@ extern int generic_file_fsync(struct file *, loff_t, loff_t, int);

extern int generic_check_addressable(unsigned, u64);

#ifdef CONFIG_UNICODE
extern int generic_ci_d_hash(const struct dentry *dentry, struct qstr *str);
extern int generic_ci_d_compare(const struct dentry *dentry, unsigned int len,
				const char *str, const struct qstr *name);
extern bool needs_casefold(const struct inode *dir);
#else
static inline bool needs_casefold(const struct inode *dir)
{
	return 0;
}
#endif

#ifdef CONFIG_MIGRATION
extern int buffer_migrate_page(struct address_space *,
				struct page *, struct page *,