From 76bfcb2dc2f3bf6bc5d5d15f73c756f58e0d2892 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 21 Sep 2020 16:10:07 -0700 Subject: [PATCH] ANDROID: sync generic casefolding code with patches going upstream Get the generic casefolding code in sync with the patches that are queued in f2fs.git#dev for 5.10. Equivalently, this reverts the patch "ANDROID-fs-adjust-casefolding-support-to-match-android-mainline.patch" from the android-mainline quilt series, with the following conflicts: Conflicts: fs/ext4/hash.c # due to "ANDROID: ext4: Handle casefolding with encryption" fs/ext4/namei.c # due to "ANDROID: ext4: Handle casefolding with encryption" fs/f2fs/dir.c # due to "ANDROID: f2fs: Handle casefolding with Encryption" Bug: 161184936 Cc: Daniel Rosenberg Cc: Paul Lawrence Cc: Jaegeuk Kim Change-Id: I0ae169f0f5f413fb21e4be7a163213aef3fa6756 Signed-off-by: Eric Biggers --- fs/ext4/hash.c | 2 +- fs/ext4/namei.c | 8 +++--- fs/f2fs/dir.c | 9 ++++--- fs/libfs.c | 56 +++++++++++++++++++++++++----------------- fs/unicode/utf8-core.c | 2 +- include/linux/fs.h | 8 +----- 6 files changed, 45 insertions(+), 40 deletions(-) diff --git a/fs/ext4/hash.c b/fs/ext4/hash.c index 6c0b85b345ae..6b04bf2a6a9d 100644 --- a/fs/ext4/hash.c +++ b/fs/ext4/hash.c @@ -296,7 +296,7 @@ int ext4fs_dirhash(const struct inode *dir, const char *name, int len, unsigned char *buff; struct qstr qstr = {.name = name, .len = len }; - if (len && needs_casefold(dir) && um) { + if (len && IS_CASEFOLDED(dir) && um) { buff = kzalloc(sizeof(char) * PATH_MAX, GFP_KERNEL); if (!buff) return -ENOMEM; diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index 6fb7e8ad0533..d95d83532ae0 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -1359,7 +1359,7 @@ static int ext4_ci_compare(const struct inode *parent, const struct qstr *name, /* Handle invalid character sequence as either an error * or as an opaque byte sequence. */ - if (sb_has_enc_strict_mode(sb)) + if (sb_has_strict_encoding(sb)) ret = -EINVAL; else if (name->len != entry.len) ret = 1; @@ -1378,7 +1378,7 @@ int ext4_fname_setup_ci_filename(struct inode *dir, const struct qstr *iname, struct dx_hash_info *hinfo = &name->hinfo; int len; - if (!needs_casefold(dir)) { + if (!IS_CASEFOLDED(dir) || !dir->i_sb->s_encoding) { cf_name->name = NULL; return 0; } @@ -1429,7 +1429,7 @@ static bool ext4_match(struct inode *parent, #endif #ifdef CONFIG_UNICODE - if (needs_casefold(parent)) { + if (parent->i_sb->s_encoding && IS_CASEFOLDED(parent)) { if (fname->cf_name.name) { struct qstr cf = {.name = fname->cf_name.name, .len = fname->cf_name.len}; @@ -2302,7 +2302,7 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry, return -EINVAL; #ifdef CONFIG_UNICODE - if (sb_has_enc_strict_mode(sb) && IS_CASEFOLDED(dir) && + if (sb_has_strict_encoding(sb) && IS_CASEFOLDED(dir) && sb->s_encoding && utf8_validate(sb->s_encoding, &dentry->d_name)) return -EINVAL; #endif diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index f1beb18e80fa..07004eb6edf8 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -76,21 +76,22 @@ int f2fs_init_casefolded_name(const struct inode *dir, struct f2fs_filename *fname) { #ifdef CONFIG_UNICODE - struct f2fs_sb_info *sbi = F2FS_SB(dir->i_sb); + struct super_block *sb = dir->i_sb; + struct f2fs_sb_info *sbi = F2FS_SB(sb); if (IS_CASEFOLDED(dir)) { fname->cf_name.name = f2fs_kmalloc(sbi, F2FS_NAME_LEN, GFP_NOFS); if (!fname->cf_name.name) return -ENOMEM; - fname->cf_name.len = utf8_casefold(sbi->sb->s_encoding, + fname->cf_name.len = utf8_casefold(sb->s_encoding, fname->usr_fname, fname->cf_name.name, F2FS_NAME_LEN); if ((int)fname->cf_name.len <= 0) { kfree(fname->cf_name.name); fname->cf_name.name = NULL; - if (sb_has_enc_strict_mode(dir->i_sb)) + if (sb_has_strict_encoding(sb)) return -EINVAL; /* fall back to treating name as opaque byte sequence */ } @@ -246,7 +247,7 @@ static bool f2fs_match_ci_name(const struct inode *dir, const struct qstr *name, * In strict mode, ignore invalid names. In non-strict mode, * fall back to treating them as opaque byte sequences. */ - if (sb_has_enc_strict_mode(sb) || name->len != entry.len) + if (sb_has_strict_encoding(sb) || name->len != entry.len) res = 1; else res = memcmp(name->name, entry.name, name->len); diff --git a/fs/libfs.c b/fs/libfs.c index 332319be3627..efc35efbcbe2 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -1367,27 +1367,38 @@ bool is_empty_dir_inode(struct inode *inode) } #ifdef CONFIG_UNICODE -bool needs_casefold(const struct inode *dir) +/* + * Determine if the name of a dentry should be casefolded. + * + * Return: if names will need casefolding + */ +static bool needs_casefold(const struct inode *dir) { - return IS_CASEFOLDED(dir) && dir->i_sb->s_encoding && - (!IS_ENCRYPTED(dir) || fscrypt_has_encryption_key(dir)); + return IS_CASEFOLDED(dir) && dir->i_sb->s_encoding; } -EXPORT_SYMBOL(needs_casefold); +/** + * generic_ci_d_compare - generic d_compare implementation for casefolding filesystems + * @dentry: dentry whose name we are checking against + * @len: len of name of dentry + * @str: str pointer to name of dentry + * @name: Name to compare against + * + * Return: 0 if names match, 1 if mismatch, or -ERRNO + */ 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 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 entry = QSTR_INIT(str, len); + struct qstr qstr = QSTR_INIT(str, len); char strbuf[DNAME_INLINE_LEN]; int ret; - if (!inode || !needs_casefold(inode)) + if (!dir || !needs_casefold(dir)) goto fallback; - /* * 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 @@ -1398,16 +1409,15 @@ 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; - entry.name = strbuf; + qstr.name = strbuf; /* prevent compiler from optimizing out the temporary buffer */ barrier(); } - - ret = utf8_strncasecmp(um, name, &entry); + ret = utf8_strncasecmp(um, name, &qstr); if (ret >= 0) return ret; - if (sb_has_enc_strict_mode(sb)) + if (sb_has_strict_encoding(sb)) return -EINVAL; fallback: if (len != name->len) @@ -1416,27 +1426,27 @@ fallback: } EXPORT_SYMBOL(generic_ci_d_compare); +/** + * generic_ci_d_hash - generic d_hash implementation for casefolding filesystems + * @dentry: dentry of the parent directory + * @str: qstr of name whose hash we should fill in + * + * Return: 0 if hash was successful or unchanged, and -EINVAL on error + */ int generic_ci_d_hash(const struct dentry *dentry, struct qstr *str) { - const struct inode *inode = READ_ONCE(dentry->d_inode); + const struct inode *dir = 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)) + if (!dir || !needs_casefold(dir)) return 0; ret = utf8_casefold_hash(um, dentry, str); - if (ret < 0) - goto err; - + if (ret < 0 && sb_has_strict_encoding(sb)) + return -EINVAL; return 0; -err: - if (sb_has_enc_strict_mode(sb)) - ret = -EINVAL; - else - ret = 0; - return ret; } EXPORT_SYMBOL(generic_ci_d_hash); diff --git a/fs/unicode/utf8-core.c b/fs/unicode/utf8-core.c index 90656b998072..dc25823bfed9 100644 --- a/fs/unicode/utf8-core.c +++ b/fs/unicode/utf8-core.c @@ -138,7 +138,7 @@ int utf8_casefold_hash(const struct unicode_map *um, const void *salt, while ((c = utf8byte(&cur))) { if (c < 0) - return c; + return -EINVAL; hash = partial_name_hash((unsigned char)c, hash); } str->hash = end_name_hash(hash); diff --git a/include/linux/fs.h b/include/linux/fs.h index 555be0778d0a..7010ff2aa11e 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1374,7 +1374,7 @@ extern int send_sigurg(struct fown_struct *fown); /* These flags relate to encoding and casefolding */ #define SB_ENC_STRICT_MODE_FL (1 << 0) -#define sb_has_enc_strict_mode(sb) \ +#define sb_has_strict_encoding(sb) \ (sb->s_encoding_flags & SB_ENC_STRICT_MODE_FL) /* @@ -3276,12 +3276,6 @@ extern int generic_check_addressable(unsigned, u64); 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 extern void generic_set_encrypted_ci_d_ops(struct inode *dir, struct dentry *dentry);