vfs-6.11.casefold
-----BEGIN PGP SIGNATURE----- iHUEABYKAB0WIQRAhzRXHqcMeLMyaSiRxhvAZXjcogUCZpEGnAAKCRCRxhvAZXjc om4UAP4xnLigNBMHqoWTvfbBjxfoPcWTq53M9CM/T2t2iz4AsQD+NiUQLmNO/LDJ 8aZ0K5TI2+uWYUHbyLabmKWwAm33UQ0= =ZeeN -----END PGP SIGNATURE----- Merge tag 'vfs-6.11.casefold' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs Pull vfs casefolding updates from Christian Brauner: "This contains some work to simplify the handling of casefolded names: - Simplify the handling of casefolded names in f2fs and ext4 by keeping the names as a qstr to avoiding unnecessary conversions - Introduce a new generic_ci_match() libfs case-insensitive lookup helper and use it in both f2fs and ext4 allowing to remove the filesystem specific implementations - Remove a bunch of ifdefs by making the unicode build checks part of the code flow" * tag 'vfs-6.11.casefold' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs: f2fs: Move CONFIG_UNICODE defguards into the code flow ext4: Move CONFIG_UNICODE defguards into the code flow f2fs: Reuse generic_ci_match for ci comparisons ext4: Reuse generic_ci_match for ci comparisons libfs: Introduce case-insensitive string comparison helper f2fs: Simplify the handling of cached casefolded names ext4: Simplify the handling of cached casefolded names
This commit is contained in:
commit
4a051e4c21
@ -31,11 +31,10 @@ int ext4_fname_setup_filename(struct inode *dir, const struct qstr *iname,
|
||||
|
||||
ext4_fname_from_fscrypt_name(fname, &name);
|
||||
|
||||
#if IS_ENABLED(CONFIG_UNICODE)
|
||||
err = ext4_fname_setup_ci_filename(dir, iname, fname);
|
||||
if (err)
|
||||
ext4_fname_free_filename(fname);
|
||||
#endif
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -51,11 +50,9 @@ int ext4_fname_prepare_lookup(struct inode *dir, struct dentry *dentry,
|
||||
|
||||
ext4_fname_from_fscrypt_name(fname, &name);
|
||||
|
||||
#if IS_ENABLED(CONFIG_UNICODE)
|
||||
err = ext4_fname_setup_ci_filename(dir, &dentry->d_name, fname);
|
||||
if (err)
|
||||
ext4_fname_free_filename(fname);
|
||||
#endif
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -70,10 +67,7 @@ void ext4_fname_free_filename(struct ext4_filename *fname)
|
||||
fname->usr_fname = NULL;
|
||||
fname->disk_name.name = NULL;
|
||||
|
||||
#if IS_ENABLED(CONFIG_UNICODE)
|
||||
kfree(fname->cf_name.name);
|
||||
fname->cf_name.name = NULL;
|
||||
#endif
|
||||
ext4_fname_free_ci_filename(fname);
|
||||
}
|
||||
|
||||
static bool uuid_is_zero(__u8 u[16])
|
||||
|
@ -2511,7 +2511,7 @@ struct ext4_filename {
|
||||
struct fscrypt_str crypto_buf;
|
||||
#endif
|
||||
#if IS_ENABLED(CONFIG_UNICODE)
|
||||
struct fscrypt_str cf_name;
|
||||
struct qstr cf_name;
|
||||
#endif
|
||||
};
|
||||
|
||||
@ -2745,8 +2745,25 @@ ext4_fsblk_t ext4_inode_to_goal_block(struct inode *);
|
||||
|
||||
#if IS_ENABLED(CONFIG_UNICODE)
|
||||
extern int ext4_fname_setup_ci_filename(struct inode *dir,
|
||||
const struct qstr *iname,
|
||||
struct ext4_filename *fname);
|
||||
const struct qstr *iname,
|
||||
struct ext4_filename *fname);
|
||||
|
||||
static inline void ext4_fname_free_ci_filename(struct ext4_filename *fname)
|
||||
{
|
||||
kfree(fname->cf_name.name);
|
||||
fname->cf_name.name = NULL;
|
||||
}
|
||||
#else
|
||||
static inline int ext4_fname_setup_ci_filename(struct inode *dir,
|
||||
const struct qstr *iname,
|
||||
struct ext4_filename *fname)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void ext4_fname_free_ci_filename(struct ext4_filename *fname)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
/* ext4 encryption related stuff goes here crypto.c */
|
||||
@ -2769,16 +2786,11 @@ static inline int ext4_fname_setup_filename(struct inode *dir,
|
||||
int lookup,
|
||||
struct ext4_filename *fname)
|
||||
{
|
||||
int err = 0;
|
||||
fname->usr_fname = iname;
|
||||
fname->disk_name.name = (unsigned char *) iname->name;
|
||||
fname->disk_name.len = iname->len;
|
||||
|
||||
#if IS_ENABLED(CONFIG_UNICODE)
|
||||
err = ext4_fname_setup_ci_filename(dir, iname, fname);
|
||||
#endif
|
||||
|
||||
return err;
|
||||
return ext4_fname_setup_ci_filename(dir, iname, fname);
|
||||
}
|
||||
|
||||
static inline int ext4_fname_prepare_lookup(struct inode *dir,
|
||||
@ -2790,10 +2802,7 @@ static inline int ext4_fname_prepare_lookup(struct inode *dir,
|
||||
|
||||
static inline void ext4_fname_free_filename(struct ext4_filename *fname)
|
||||
{
|
||||
#if IS_ENABLED(CONFIG_UNICODE)
|
||||
kfree(fname->cf_name.name);
|
||||
fname->cf_name.name = NULL;
|
||||
#endif
|
||||
ext4_fname_free_ci_filename(fname);
|
||||
}
|
||||
|
||||
static inline int ext4_ioctl_get_encryption_pwsalt(struct file *filp,
|
||||
|
120
fs/ext4/namei.c
120
fs/ext4/namei.c
@ -1390,62 +1390,11 @@ static void dx_insert_block(struct dx_frame *frame, u32 hash, ext4_lblk_t block)
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_UNICODE)
|
||||
/*
|
||||
* Test whether a case-insensitive directory entry matches the filename
|
||||
* being searched for. If quick is set, assume the name being looked up
|
||||
* is already in the casefolded form.
|
||||
*
|
||||
* Returns: 0 if the directory entry matches, more than 0 if it
|
||||
* doesn't match or less than zero on error.
|
||||
*/
|
||||
static int ext4_ci_compare(const struct inode *parent, const struct qstr *name,
|
||||
u8 *de_name, size_t de_name_len, bool quick)
|
||||
{
|
||||
const struct super_block *sb = parent->i_sb;
|
||||
const struct unicode_map *um = sb->s_encoding;
|
||||
struct fscrypt_str decrypted_name = FSTR_INIT(NULL, de_name_len);
|
||||
struct qstr entry = QSTR_INIT(de_name, de_name_len);
|
||||
int ret;
|
||||
|
||||
if (IS_ENCRYPTED(parent)) {
|
||||
const struct fscrypt_str encrypted_name =
|
||||
FSTR_INIT(de_name, de_name_len);
|
||||
|
||||
decrypted_name.name = kmalloc(de_name_len, GFP_KERNEL);
|
||||
if (!decrypted_name.name)
|
||||
return -ENOMEM;
|
||||
ret = fscrypt_fname_disk_to_usr(parent, 0, 0, &encrypted_name,
|
||||
&decrypted_name);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
entry.name = decrypted_name.name;
|
||||
entry.len = decrypted_name.len;
|
||||
}
|
||||
|
||||
if (quick)
|
||||
ret = utf8_strncasecmp_folded(um, name, &entry);
|
||||
else
|
||||
ret = utf8_strncasecmp(um, name, &entry);
|
||||
if (ret < 0) {
|
||||
/* Handle invalid character sequence as either an error
|
||||
* or as an opaque byte sequence.
|
||||
*/
|
||||
if (sb_has_strict_encoding(sb))
|
||||
ret = -EINVAL;
|
||||
else if (name->len != entry.len)
|
||||
ret = 1;
|
||||
else
|
||||
ret = !!memcmp(name->name, entry.name, entry.len);
|
||||
}
|
||||
out:
|
||||
kfree(decrypted_name.name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ext4_fname_setup_ci_filename(struct inode *dir, const struct qstr *iname,
|
||||
struct ext4_filename *name)
|
||||
{
|
||||
struct fscrypt_str *cf_name = &name->cf_name;
|
||||
struct qstr *cf_name = &name->cf_name;
|
||||
unsigned char *buf;
|
||||
struct dx_hash_info *hinfo = &name->hinfo;
|
||||
int len;
|
||||
|
||||
@ -1455,18 +1404,18 @@ int ext4_fname_setup_ci_filename(struct inode *dir, const struct qstr *iname,
|
||||
return 0;
|
||||
}
|
||||
|
||||
cf_name->name = kmalloc(EXT4_NAME_LEN, GFP_NOFS);
|
||||
if (!cf_name->name)
|
||||
buf = kmalloc(EXT4_NAME_LEN, GFP_NOFS);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
len = utf8_casefold(dir->i_sb->s_encoding,
|
||||
iname, cf_name->name,
|
||||
EXT4_NAME_LEN);
|
||||
len = utf8_casefold(dir->i_sb->s_encoding, iname, buf, EXT4_NAME_LEN);
|
||||
if (len <= 0) {
|
||||
kfree(cf_name->name);
|
||||
cf_name->name = NULL;
|
||||
kfree(buf);
|
||||
buf = NULL;
|
||||
}
|
||||
cf_name->name = buf;
|
||||
cf_name->len = (unsigned) len;
|
||||
|
||||
if (!IS_ENCRYPTED(dir))
|
||||
return 0;
|
||||
|
||||
@ -1502,22 +1451,29 @@ static bool ext4_match(struct inode *parent,
|
||||
#if IS_ENABLED(CONFIG_UNICODE)
|
||||
if (IS_CASEFOLDED(parent) &&
|
||||
(!IS_ENCRYPTED(parent) || fscrypt_has_encryption_key(parent))) {
|
||||
if (fname->cf_name.name) {
|
||||
struct qstr cf = {.name = fname->cf_name.name,
|
||||
.len = fname->cf_name.len};
|
||||
if (IS_ENCRYPTED(parent)) {
|
||||
if (fname->hinfo.hash != EXT4_DIRENT_HASH(de) ||
|
||||
fname->hinfo.minor_hash !=
|
||||
EXT4_DIRENT_MINOR_HASH(de)) {
|
||||
/*
|
||||
* Just checking IS_ENCRYPTED(parent) below is not
|
||||
* sufficient to decide whether one can use the hash for
|
||||
* skipping the string comparison, because the key might
|
||||
* have been added right after
|
||||
* ext4_fname_setup_ci_filename(). In this case, a hash
|
||||
* mismatch will be a false negative. Therefore, make
|
||||
* sure cf_name was properly initialized before
|
||||
* considering the calculated hash.
|
||||
*/
|
||||
if (IS_ENCRYPTED(parent) && fname->cf_name.name &&
|
||||
(fname->hinfo.hash != EXT4_DIRENT_HASH(de) ||
|
||||
fname->hinfo.minor_hash != EXT4_DIRENT_MINOR_HASH(de)))
|
||||
return false;
|
||||
/*
|
||||
* Treat comparison errors as not a match. The
|
||||
* only case where it happens is on a disk
|
||||
* corruption or ENOMEM.
|
||||
*/
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return !ext4_ci_compare(parent, &cf, de->name,
|
||||
de->name_len, true);
|
||||
}
|
||||
return !ext4_ci_compare(parent, fname->usr_fname, de->name,
|
||||
de->name_len, false);
|
||||
return generic_ci_match(parent, fname->usr_fname,
|
||||
&fname->cf_name, de->name,
|
||||
de->name_len) > 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -1869,8 +1825,7 @@ static struct dentry *ext4_lookup(struct inode *dir, struct dentry *dentry, unsi
|
||||
}
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_UNICODE)
|
||||
if (!inode && IS_CASEFOLDED(dir)) {
|
||||
if (IS_ENABLED(CONFIG_UNICODE) && !inode && IS_CASEFOLDED(dir)) {
|
||||
/* Eventually we want to call d_add_ci(dentry, NULL)
|
||||
* for negative dentries in the encoding case as
|
||||
* well. For now, prevent the negative dentry
|
||||
@ -1878,7 +1833,7 @@ static struct dentry *ext4_lookup(struct inode *dir, struct dentry *dentry, unsi
|
||||
*/
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
return d_splice_alias(inode, dentry);
|
||||
}
|
||||
|
||||
@ -3208,16 +3163,14 @@ static int ext4_rmdir(struct inode *dir, struct dentry *dentry)
|
||||
ext4_fc_track_unlink(handle, dentry);
|
||||
retval = ext4_mark_inode_dirty(handle, dir);
|
||||
|
||||
#if IS_ENABLED(CONFIG_UNICODE)
|
||||
/* VFS negative dentries are incompatible with Encoding and
|
||||
* Case-insensitiveness. Eventually we'll want avoid
|
||||
* invalidating the dentries here, alongside with returning the
|
||||
* negative dentries at ext4_lookup(), when it is better
|
||||
* supported by the VFS for the CI case.
|
||||
*/
|
||||
if (IS_CASEFOLDED(dir))
|
||||
if (IS_ENABLED(CONFIG_UNICODE) && IS_CASEFOLDED(dir))
|
||||
d_invalidate(dentry);
|
||||
#endif
|
||||
|
||||
end_rmdir:
|
||||
brelse(bh);
|
||||
@ -3319,16 +3272,15 @@ static int ext4_unlink(struct inode *dir, struct dentry *dentry)
|
||||
goto out_trace;
|
||||
|
||||
retval = __ext4_unlink(dir, &dentry->d_name, d_inode(dentry), dentry);
|
||||
#if IS_ENABLED(CONFIG_UNICODE)
|
||||
|
||||
/* VFS negative dentries are incompatible with Encoding and
|
||||
* Case-insensitiveness. Eventually we'll want avoid
|
||||
* invalidating the dentries here, alongside with returning the
|
||||
* negative dentries at ext4_lookup(), when it is better
|
||||
* supported by the VFS for the CI case.
|
||||
*/
|
||||
if (IS_CASEFOLDED(dir))
|
||||
if (IS_ENABLED(CONFIG_UNICODE) && IS_CASEFOLDED(dir))
|
||||
d_invalidate(dentry);
|
||||
#endif
|
||||
|
||||
out_trace:
|
||||
trace_ext4_unlink_exit(dentry, retval);
|
||||
|
@ -3586,14 +3586,12 @@ int ext4_feature_set_ok(struct super_block *sb, int readonly)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if !IS_ENABLED(CONFIG_UNICODE)
|
||||
if (ext4_has_feature_casefold(sb)) {
|
||||
if (!IS_ENABLED(CONFIG_UNICODE) && ext4_has_feature_casefold(sb)) {
|
||||
ext4_msg(sb, KERN_ERR,
|
||||
"Filesystem with casefold feature cannot be "
|
||||
"mounted without CONFIG_UNICODE");
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (readonly)
|
||||
return 1;
|
||||
|
105
fs/f2fs/dir.c
105
fs/f2fs/dir.c
@ -42,35 +42,49 @@ static unsigned int bucket_blocks(unsigned int level)
|
||||
return 4;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_UNICODE)
|
||||
/* If @dir is casefolded, initialize @fname->cf_name from @fname->usr_fname. */
|
||||
int f2fs_init_casefolded_name(const struct inode *dir,
|
||||
struct f2fs_filename *fname)
|
||||
{
|
||||
#if IS_ENABLED(CONFIG_UNICODE)
|
||||
struct super_block *sb = dir->i_sb;
|
||||
unsigned char *buf;
|
||||
int len;
|
||||
|
||||
if (IS_CASEFOLDED(dir) &&
|
||||
!is_dot_dotdot(fname->usr_fname->name, fname->usr_fname->len)) {
|
||||
fname->cf_name.name = f2fs_kmem_cache_alloc(f2fs_cf_name_slab,
|
||||
GFP_NOFS, false, F2FS_SB(sb));
|
||||
if (!fname->cf_name.name)
|
||||
buf = f2fs_kmem_cache_alloc(f2fs_cf_name_slab,
|
||||
GFP_NOFS, false, F2FS_SB(sb));
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
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) {
|
||||
kmem_cache_free(f2fs_cf_name_slab, fname->cf_name.name);
|
||||
fname->cf_name.name = NULL;
|
||||
|
||||
len = utf8_casefold(sb->s_encoding, fname->usr_fname,
|
||||
buf, F2FS_NAME_LEN);
|
||||
if (len <= 0) {
|
||||
kmem_cache_free(f2fs_cf_name_slab, buf);
|
||||
if (sb_has_strict_encoding(sb))
|
||||
return -EINVAL;
|
||||
/* fall back to treating name as opaque byte sequence */
|
||||
return 0;
|
||||
}
|
||||
fname->cf_name.name = buf;
|
||||
fname->cf_name.len = len;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void f2fs_free_casefolded_name(struct f2fs_filename *fname)
|
||||
{
|
||||
unsigned char *buf = (unsigned char *)fname->cf_name.name;
|
||||
|
||||
if (buf) {
|
||||
kmem_cache_free(f2fs_cf_name_slab, buf);
|
||||
fname->cf_name.name = NULL;
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_UNICODE */
|
||||
|
||||
static int __f2fs_setup_filename(const struct inode *dir,
|
||||
const struct fscrypt_name *crypt_name,
|
||||
struct f2fs_filename *fname)
|
||||
@ -142,12 +156,7 @@ void f2fs_free_filename(struct f2fs_filename *fname)
|
||||
kfree(fname->crypto_buf.name);
|
||||
fname->crypto_buf.name = NULL;
|
||||
#endif
|
||||
#if IS_ENABLED(CONFIG_UNICODE)
|
||||
if (fname->cf_name.name) {
|
||||
kmem_cache_free(f2fs_cf_name_slab, fname->cf_name.name);
|
||||
fname->cf_name.name = NULL;
|
||||
}
|
||||
#endif
|
||||
f2fs_free_casefolded_name(fname);
|
||||
}
|
||||
|
||||
static unsigned long dir_block_index(unsigned int level,
|
||||
@ -176,58 +185,6 @@ static struct f2fs_dir_entry *find_in_block(struct inode *dir,
|
||||
return f2fs_find_target_dentry(&d, fname, max_slots);
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_UNICODE)
|
||||
/*
|
||||
* Test whether a case-insensitive directory entry matches the filename
|
||||
* being searched for.
|
||||
*
|
||||
* Returns 1 for a match, 0 for no match, and -errno on an error.
|
||||
*/
|
||||
static int f2fs_match_ci_name(const struct inode *dir, const struct qstr *name,
|
||||
const u8 *de_name, u32 de_name_len)
|
||||
{
|
||||
const struct super_block *sb = dir->i_sb;
|
||||
const struct unicode_map *um = sb->s_encoding;
|
||||
struct fscrypt_str decrypted_name = FSTR_INIT(NULL, de_name_len);
|
||||
struct qstr entry = QSTR_INIT(de_name, de_name_len);
|
||||
int res;
|
||||
|
||||
if (IS_ENCRYPTED(dir)) {
|
||||
const struct fscrypt_str encrypted_name =
|
||||
FSTR_INIT((u8 *)de_name, de_name_len);
|
||||
|
||||
if (WARN_ON_ONCE(!fscrypt_has_encryption_key(dir)))
|
||||
return -EINVAL;
|
||||
|
||||
decrypted_name.name = kmalloc(de_name_len, GFP_KERNEL);
|
||||
if (!decrypted_name.name)
|
||||
return -ENOMEM;
|
||||
res = fscrypt_fname_disk_to_usr(dir, 0, 0, &encrypted_name,
|
||||
&decrypted_name);
|
||||
if (res < 0)
|
||||
goto out;
|
||||
entry.name = decrypted_name.name;
|
||||
entry.len = decrypted_name.len;
|
||||
}
|
||||
|
||||
res = utf8_strncasecmp_folded(um, name, &entry);
|
||||
/*
|
||||
* In strict mode, ignore invalid names. In non-strict mode,
|
||||
* fall back to treating them as opaque byte sequences.
|
||||
*/
|
||||
if (res < 0 && !sb_has_strict_encoding(sb)) {
|
||||
res = name->len == entry.len &&
|
||||
memcmp(name->name, entry.name, name->len) == 0;
|
||||
} else {
|
||||
/* utf8_strncasecmp_folded returns 0 on match */
|
||||
res = (res == 0);
|
||||
}
|
||||
out:
|
||||
kfree(decrypted_name.name);
|
||||
return res;
|
||||
}
|
||||
#endif /* CONFIG_UNICODE */
|
||||
|
||||
static inline int f2fs_match_name(const struct inode *dir,
|
||||
const struct f2fs_filename *fname,
|
||||
const u8 *de_name, u32 de_name_len)
|
||||
@ -235,11 +192,11 @@ static inline int f2fs_match_name(const struct inode *dir,
|
||||
struct fscrypt_name f;
|
||||
|
||||
#if IS_ENABLED(CONFIG_UNICODE)
|
||||
if (fname->cf_name.name) {
|
||||
struct qstr cf = FSTR_TO_QSTR(&fname->cf_name);
|
||||
if (fname->cf_name.name)
|
||||
return generic_ci_match(dir, fname->usr_fname,
|
||||
&fname->cf_name,
|
||||
de_name, de_name_len);
|
||||
|
||||
return f2fs_match_ci_name(dir, &cf, de_name, de_name_len);
|
||||
}
|
||||
#endif
|
||||
f.usr_fname = fname->usr_fname;
|
||||
f.disk_name = fname->disk_name;
|
||||
|
@ -531,7 +531,7 @@ struct f2fs_filename {
|
||||
* internal operation where usr_fname is also NULL. In all these cases
|
||||
* we fall back to treating the name as an opaque byte sequence.
|
||||
*/
|
||||
struct fscrypt_str cf_name;
|
||||
struct qstr cf_name;
|
||||
#endif
|
||||
};
|
||||
|
||||
@ -3533,8 +3533,22 @@ int f2fs_get_tmpfile(struct mnt_idmap *idmap, struct inode *dir,
|
||||
/*
|
||||
* dir.c
|
||||
*/
|
||||
#if IS_ENABLED(CONFIG_UNICODE)
|
||||
int f2fs_init_casefolded_name(const struct inode *dir,
|
||||
struct f2fs_filename *fname);
|
||||
void f2fs_free_casefolded_name(struct f2fs_filename *fname);
|
||||
#else
|
||||
static inline int f2fs_init_casefolded_name(const struct inode *dir,
|
||||
struct f2fs_filename *fname)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void f2fs_free_casefolded_name(struct f2fs_filename *fname)
|
||||
{
|
||||
}
|
||||
#endif /* CONFIG_UNICODE */
|
||||
|
||||
int f2fs_setup_filename(struct inode *dir, const struct qstr *iname,
|
||||
int lookup, struct f2fs_filename *fname);
|
||||
int f2fs_prepare_lookup(struct inode *dir, struct dentry *dentry,
|
||||
|
@ -576,8 +576,7 @@ static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry,
|
||||
goto out_iput;
|
||||
}
|
||||
out_splice:
|
||||
#if IS_ENABLED(CONFIG_UNICODE)
|
||||
if (!inode && IS_CASEFOLDED(dir)) {
|
||||
if (IS_ENABLED(CONFIG_UNICODE) && !inode && IS_CASEFOLDED(dir)) {
|
||||
/* Eventually we want to call d_add_ci(dentry, NULL)
|
||||
* for negative dentries in the encoding case as
|
||||
* well. For now, prevent the negative dentry
|
||||
@ -586,7 +585,7 @@ out_splice:
|
||||
trace_f2fs_lookup_end(dir, dentry, ino, err);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
new = d_splice_alias(inode, dentry);
|
||||
trace_f2fs_lookup_end(dir, !IS_ERR_OR_NULL(new) ? new : dentry,
|
||||
ino, IS_ERR(new) ? PTR_ERR(new) : err);
|
||||
@ -639,16 +638,15 @@ static int f2fs_unlink(struct inode *dir, struct dentry *dentry)
|
||||
f2fs_delete_entry(de, page, dir, inode);
|
||||
f2fs_unlock_op(sbi);
|
||||
|
||||
#if IS_ENABLED(CONFIG_UNICODE)
|
||||
/* VFS negative dentries are incompatible with Encoding and
|
||||
* Case-insensitiveness. Eventually we'll want avoid
|
||||
* invalidating the dentries here, alongside with returning the
|
||||
* negative dentries at f2fs_lookup(), when it is better
|
||||
* supported by the VFS for the CI case.
|
||||
*/
|
||||
if (IS_CASEFOLDED(dir))
|
||||
if (IS_ENABLED(CONFIG_UNICODE) && IS_CASEFOLDED(dir))
|
||||
d_invalidate(dentry);
|
||||
#endif
|
||||
|
||||
if (IS_DIRSYNC(dir))
|
||||
f2fs_sync_fs(sbi->sb, 1);
|
||||
fail:
|
||||
|
@ -46,10 +46,6 @@
|
||||
|
||||
static struct kmem_cache *fsync_entry_slab;
|
||||
|
||||
#if IS_ENABLED(CONFIG_UNICODE)
|
||||
extern struct kmem_cache *f2fs_cf_name_slab;
|
||||
#endif
|
||||
|
||||
bool f2fs_space_for_roll_forward(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
s64 nalloc = percpu_counter_sum_positive(&sbi->alloc_valid_block_count);
|
||||
@ -153,11 +149,8 @@ static int init_recovered_filename(const struct inode *dir,
|
||||
if (err)
|
||||
return err;
|
||||
f2fs_hash_filename(dir, fname);
|
||||
#if IS_ENABLED(CONFIG_UNICODE)
|
||||
/* Case-sensitive match is fine for recovery */
|
||||
kmem_cache_free(f2fs_cf_name_slab, fname->cf_name.name);
|
||||
fname->cf_name.name = NULL;
|
||||
#endif
|
||||
f2fs_free_casefolded_name(fname);
|
||||
} else {
|
||||
f2fs_hash_filename(dir, fname);
|
||||
}
|
||||
|
@ -321,7 +321,7 @@ struct kmem_cache *f2fs_cf_name_slab;
|
||||
static int __init f2fs_create_casefold_cache(void)
|
||||
{
|
||||
f2fs_cf_name_slab = f2fs_kmem_cache_create("f2fs_casefolded_name",
|
||||
F2FS_NAME_LEN);
|
||||
F2FS_NAME_LEN);
|
||||
return f2fs_cf_name_slab ? 0 : -ENOMEM;
|
||||
}
|
||||
|
||||
@ -1326,13 +1326,13 @@ default_check:
|
||||
return -EINVAL;
|
||||
}
|
||||
#endif
|
||||
#if !IS_ENABLED(CONFIG_UNICODE)
|
||||
if (f2fs_sb_has_casefold(sbi)) {
|
||||
|
||||
if (!IS_ENABLED(CONFIG_UNICODE) && f2fs_sb_has_casefold(sbi)) {
|
||||
f2fs_err(sbi,
|
||||
"Filesystem with casefold feature cannot be mounted without CONFIG_UNICODE");
|
||||
return -EINVAL;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The BLKZONED feature indicates that the drive was formatted with
|
||||
* zone alignment optimization. This is optional for host-aware
|
||||
|
74
fs/libfs.c
74
fs/libfs.c
@ -1854,6 +1854,80 @@ static const struct dentry_operations generic_ci_dentry_ops = {
|
||||
.d_revalidate = fscrypt_d_revalidate,
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
* generic_ci_match() - Match a name (case-insensitively) with a dirent.
|
||||
* This is a filesystem helper for comparison with directory entries.
|
||||
* generic_ci_d_compare should be used in VFS' ->d_compare instead.
|
||||
*
|
||||
* @parent: Inode of the parent of the dirent under comparison
|
||||
* @name: name under lookup.
|
||||
* @folded_name: Optional pre-folded name under lookup
|
||||
* @de_name: Dirent name.
|
||||
* @de_name_len: dirent name length.
|
||||
*
|
||||
* Test whether a case-insensitive directory entry matches the filename
|
||||
* being searched. If @folded_name is provided, it is used instead of
|
||||
* recalculating the casefold of @name.
|
||||
*
|
||||
* Return: > 0 if the directory entry matches, 0 if it doesn't match, or
|
||||
* < 0 on error.
|
||||
*/
|
||||
int generic_ci_match(const struct inode *parent,
|
||||
const struct qstr *name,
|
||||
const struct qstr *folded_name,
|
||||
const u8 *de_name, u32 de_name_len)
|
||||
{
|
||||
const struct super_block *sb = parent->i_sb;
|
||||
const struct unicode_map *um = sb->s_encoding;
|
||||
struct fscrypt_str decrypted_name = FSTR_INIT(NULL, de_name_len);
|
||||
struct qstr dirent = QSTR_INIT(de_name, de_name_len);
|
||||
int res = 0;
|
||||
|
||||
if (IS_ENCRYPTED(parent)) {
|
||||
const struct fscrypt_str encrypted_name =
|
||||
FSTR_INIT((u8 *) de_name, de_name_len);
|
||||
|
||||
if (WARN_ON_ONCE(!fscrypt_has_encryption_key(parent)))
|
||||
return -EINVAL;
|
||||
|
||||
decrypted_name.name = kmalloc(de_name_len, GFP_KERNEL);
|
||||
if (!decrypted_name.name)
|
||||
return -ENOMEM;
|
||||
res = fscrypt_fname_disk_to_usr(parent, 0, 0, &encrypted_name,
|
||||
&decrypted_name);
|
||||
if (res < 0) {
|
||||
kfree(decrypted_name.name);
|
||||
return res;
|
||||
}
|
||||
dirent.name = decrypted_name.name;
|
||||
dirent.len = decrypted_name.len;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
if (dirent.len == name->len &&
|
||||
!memcmp(name->name, dirent.name, dirent.len))
|
||||
goto out;
|
||||
|
||||
if (folded_name->name)
|
||||
res = utf8_strncasecmp_folded(um, folded_name, &dirent);
|
||||
else
|
||||
res = utf8_strncasecmp(um, name, &dirent);
|
||||
|
||||
out:
|
||||
kfree(decrypted_name.name);
|
||||
if (res < 0 && sb_has_strict_encoding(sb)) {
|
||||
pr_err_ratelimited("Directory contains filename that is invalid UTF-8");
|
||||
return 0;
|
||||
}
|
||||
return !res;
|
||||
}
|
||||
EXPORT_SYMBOL(generic_ci_match);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_FS_ENCRYPTION
|
||||
|
@ -3369,6 +3369,10 @@ extern int generic_file_fsync(struct file *, loff_t, loff_t, int);
|
||||
extern int generic_check_addressable(unsigned, u64);
|
||||
|
||||
extern void generic_set_sb_d_ops(struct super_block *sb);
|
||||
extern int generic_ci_match(const struct inode *parent,
|
||||
const struct qstr *name,
|
||||
const struct qstr *folded_name,
|
||||
const u8 *de_name, u32 de_name_len);
|
||||
|
||||
static inline bool sb_has_encoding(const struct super_block *sb)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user