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);
|
ext4_fname_from_fscrypt_name(fname, &name);
|
||||||
|
|
||||||
#if IS_ENABLED(CONFIG_UNICODE)
|
|
||||||
err = ext4_fname_setup_ci_filename(dir, iname, fname);
|
err = ext4_fname_setup_ci_filename(dir, iname, fname);
|
||||||
if (err)
|
if (err)
|
||||||
ext4_fname_free_filename(fname);
|
ext4_fname_free_filename(fname);
|
||||||
#endif
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,11 +50,9 @@ int ext4_fname_prepare_lookup(struct inode *dir, struct dentry *dentry,
|
|||||||
|
|
||||||
ext4_fname_from_fscrypt_name(fname, &name);
|
ext4_fname_from_fscrypt_name(fname, &name);
|
||||||
|
|
||||||
#if IS_ENABLED(CONFIG_UNICODE)
|
|
||||||
err = ext4_fname_setup_ci_filename(dir, &dentry->d_name, fname);
|
err = ext4_fname_setup_ci_filename(dir, &dentry->d_name, fname);
|
||||||
if (err)
|
if (err)
|
||||||
ext4_fname_free_filename(fname);
|
ext4_fname_free_filename(fname);
|
||||||
#endif
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,10 +67,7 @@ void ext4_fname_free_filename(struct ext4_filename *fname)
|
|||||||
fname->usr_fname = NULL;
|
fname->usr_fname = NULL;
|
||||||
fname->disk_name.name = NULL;
|
fname->disk_name.name = NULL;
|
||||||
|
|
||||||
#if IS_ENABLED(CONFIG_UNICODE)
|
ext4_fname_free_ci_filename(fname);
|
||||||
kfree(fname->cf_name.name);
|
|
||||||
fname->cf_name.name = NULL;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool uuid_is_zero(__u8 u[16])
|
static bool uuid_is_zero(__u8 u[16])
|
||||||
|
@ -2511,7 +2511,7 @@ struct ext4_filename {
|
|||||||
struct fscrypt_str crypto_buf;
|
struct fscrypt_str crypto_buf;
|
||||||
#endif
|
#endif
|
||||||
#if IS_ENABLED(CONFIG_UNICODE)
|
#if IS_ENABLED(CONFIG_UNICODE)
|
||||||
struct fscrypt_str cf_name;
|
struct qstr cf_name;
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -2745,8 +2745,25 @@ ext4_fsblk_t ext4_inode_to_goal_block(struct inode *);
|
|||||||
|
|
||||||
#if IS_ENABLED(CONFIG_UNICODE)
|
#if IS_ENABLED(CONFIG_UNICODE)
|
||||||
extern int ext4_fname_setup_ci_filename(struct inode *dir,
|
extern int ext4_fname_setup_ci_filename(struct inode *dir,
|
||||||
const struct qstr *iname,
|
const struct qstr *iname,
|
||||||
struct ext4_filename *fname);
|
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
|
#endif
|
||||||
|
|
||||||
/* ext4 encryption related stuff goes here crypto.c */
|
/* ext4 encryption related stuff goes here crypto.c */
|
||||||
@ -2769,16 +2786,11 @@ static inline int ext4_fname_setup_filename(struct inode *dir,
|
|||||||
int lookup,
|
int lookup,
|
||||||
struct ext4_filename *fname)
|
struct ext4_filename *fname)
|
||||||
{
|
{
|
||||||
int err = 0;
|
|
||||||
fname->usr_fname = iname;
|
fname->usr_fname = iname;
|
||||||
fname->disk_name.name = (unsigned char *) iname->name;
|
fname->disk_name.name = (unsigned char *) iname->name;
|
||||||
fname->disk_name.len = iname->len;
|
fname->disk_name.len = iname->len;
|
||||||
|
|
||||||
#if IS_ENABLED(CONFIG_UNICODE)
|
return ext4_fname_setup_ci_filename(dir, iname, fname);
|
||||||
err = ext4_fname_setup_ci_filename(dir, iname, fname);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int ext4_fname_prepare_lookup(struct inode *dir,
|
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)
|
static inline void ext4_fname_free_filename(struct ext4_filename *fname)
|
||||||
{
|
{
|
||||||
#if IS_ENABLED(CONFIG_UNICODE)
|
ext4_fname_free_ci_filename(fname);
|
||||||
kfree(fname->cf_name.name);
|
|
||||||
fname->cf_name.name = NULL;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int ext4_ioctl_get_encryption_pwsalt(struct file *filp,
|
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)
|
#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,
|
int ext4_fname_setup_ci_filename(struct inode *dir, const struct qstr *iname,
|
||||||
struct ext4_filename *name)
|
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;
|
struct dx_hash_info *hinfo = &name->hinfo;
|
||||||
int len;
|
int len;
|
||||||
|
|
||||||
@ -1455,18 +1404,18 @@ int ext4_fname_setup_ci_filename(struct inode *dir, const struct qstr *iname,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
cf_name->name = kmalloc(EXT4_NAME_LEN, GFP_NOFS);
|
buf = kmalloc(EXT4_NAME_LEN, GFP_NOFS);
|
||||||
if (!cf_name->name)
|
if (!buf)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
len = utf8_casefold(dir->i_sb->s_encoding,
|
len = utf8_casefold(dir->i_sb->s_encoding, iname, buf, EXT4_NAME_LEN);
|
||||||
iname, cf_name->name,
|
|
||||||
EXT4_NAME_LEN);
|
|
||||||
if (len <= 0) {
|
if (len <= 0) {
|
||||||
kfree(cf_name->name);
|
kfree(buf);
|
||||||
cf_name->name = NULL;
|
buf = NULL;
|
||||||
}
|
}
|
||||||
|
cf_name->name = buf;
|
||||||
cf_name->len = (unsigned) len;
|
cf_name->len = (unsigned) len;
|
||||||
|
|
||||||
if (!IS_ENCRYPTED(dir))
|
if (!IS_ENCRYPTED(dir))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@ -1502,22 +1451,29 @@ static bool ext4_match(struct inode *parent,
|
|||||||
#if IS_ENABLED(CONFIG_UNICODE)
|
#if IS_ENABLED(CONFIG_UNICODE)
|
||||||
if (IS_CASEFOLDED(parent) &&
|
if (IS_CASEFOLDED(parent) &&
|
||||||
(!IS_ENCRYPTED(parent) || fscrypt_has_encryption_key(parent))) {
|
(!IS_ENCRYPTED(parent) || fscrypt_has_encryption_key(parent))) {
|
||||||
if (fname->cf_name.name) {
|
/*
|
||||||
struct qstr cf = {.name = fname->cf_name.name,
|
* Just checking IS_ENCRYPTED(parent) below is not
|
||||||
.len = fname->cf_name.len};
|
* sufficient to decide whether one can use the hash for
|
||||||
if (IS_ENCRYPTED(parent)) {
|
* skipping the string comparison, because the key might
|
||||||
if (fname->hinfo.hash != EXT4_DIRENT_HASH(de) ||
|
* have been added right after
|
||||||
fname->hinfo.minor_hash !=
|
* ext4_fname_setup_ci_filename(). In this case, a hash
|
||||||
EXT4_DIRENT_MINOR_HASH(de)) {
|
* 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 generic_ci_match(parent, fname->usr_fname,
|
||||||
}
|
&fname->cf_name, de->name,
|
||||||
}
|
de->name_len) > 0;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -1869,8 +1825,7 @@ static struct dentry *ext4_lookup(struct inode *dir, struct dentry *dentry, unsi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if IS_ENABLED(CONFIG_UNICODE)
|
if (IS_ENABLED(CONFIG_UNICODE) && !inode && IS_CASEFOLDED(dir)) {
|
||||||
if (!inode && IS_CASEFOLDED(dir)) {
|
|
||||||
/* Eventually we want to call d_add_ci(dentry, NULL)
|
/* Eventually we want to call d_add_ci(dentry, NULL)
|
||||||
* for negative dentries in the encoding case as
|
* for negative dentries in the encoding case as
|
||||||
* well. For now, prevent the negative dentry
|
* 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;
|
return NULL;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
return d_splice_alias(inode, dentry);
|
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);
|
ext4_fc_track_unlink(handle, dentry);
|
||||||
retval = ext4_mark_inode_dirty(handle, dir);
|
retval = ext4_mark_inode_dirty(handle, dir);
|
||||||
|
|
||||||
#if IS_ENABLED(CONFIG_UNICODE)
|
|
||||||
/* VFS negative dentries are incompatible with Encoding and
|
/* VFS negative dentries are incompatible with Encoding and
|
||||||
* Case-insensitiveness. Eventually we'll want avoid
|
* Case-insensitiveness. Eventually we'll want avoid
|
||||||
* invalidating the dentries here, alongside with returning the
|
* invalidating the dentries here, alongside with returning the
|
||||||
* negative dentries at ext4_lookup(), when it is better
|
* negative dentries at ext4_lookup(), when it is better
|
||||||
* supported by the VFS for the CI case.
|
* supported by the VFS for the CI case.
|
||||||
*/
|
*/
|
||||||
if (IS_CASEFOLDED(dir))
|
if (IS_ENABLED(CONFIG_UNICODE) && IS_CASEFOLDED(dir))
|
||||||
d_invalidate(dentry);
|
d_invalidate(dentry);
|
||||||
#endif
|
|
||||||
|
|
||||||
end_rmdir:
|
end_rmdir:
|
||||||
brelse(bh);
|
brelse(bh);
|
||||||
@ -3319,16 +3272,15 @@ static int ext4_unlink(struct inode *dir, struct dentry *dentry)
|
|||||||
goto out_trace;
|
goto out_trace;
|
||||||
|
|
||||||
retval = __ext4_unlink(dir, &dentry->d_name, d_inode(dentry), dentry);
|
retval = __ext4_unlink(dir, &dentry->d_name, d_inode(dentry), dentry);
|
||||||
#if IS_ENABLED(CONFIG_UNICODE)
|
|
||||||
/* VFS negative dentries are incompatible with Encoding and
|
/* VFS negative dentries are incompatible with Encoding and
|
||||||
* Case-insensitiveness. Eventually we'll want avoid
|
* Case-insensitiveness. Eventually we'll want avoid
|
||||||
* invalidating the dentries here, alongside with returning the
|
* invalidating the dentries here, alongside with returning the
|
||||||
* negative dentries at ext4_lookup(), when it is better
|
* negative dentries at ext4_lookup(), when it is better
|
||||||
* supported by the VFS for the CI case.
|
* supported by the VFS for the CI case.
|
||||||
*/
|
*/
|
||||||
if (IS_CASEFOLDED(dir))
|
if (IS_ENABLED(CONFIG_UNICODE) && IS_CASEFOLDED(dir))
|
||||||
d_invalidate(dentry);
|
d_invalidate(dentry);
|
||||||
#endif
|
|
||||||
|
|
||||||
out_trace:
|
out_trace:
|
||||||
trace_ext4_unlink_exit(dentry, retval);
|
trace_ext4_unlink_exit(dentry, retval);
|
||||||
|
@ -3586,14 +3586,12 @@ int ext4_feature_set_ok(struct super_block *sb, int readonly)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !IS_ENABLED(CONFIG_UNICODE)
|
if (!IS_ENABLED(CONFIG_UNICODE) && ext4_has_feature_casefold(sb)) {
|
||||||
if (ext4_has_feature_casefold(sb)) {
|
|
||||||
ext4_msg(sb, KERN_ERR,
|
ext4_msg(sb, KERN_ERR,
|
||||||
"Filesystem with casefold feature cannot be "
|
"Filesystem with casefold feature cannot be "
|
||||||
"mounted without CONFIG_UNICODE");
|
"mounted without CONFIG_UNICODE");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
if (readonly)
|
if (readonly)
|
||||||
return 1;
|
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;
|
return 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if IS_ENABLED(CONFIG_UNICODE)
|
||||||
/* If @dir is casefolded, initialize @fname->cf_name from @fname->usr_fname. */
|
/* If @dir is casefolded, initialize @fname->cf_name from @fname->usr_fname. */
|
||||||
int f2fs_init_casefolded_name(const struct inode *dir,
|
int f2fs_init_casefolded_name(const struct inode *dir,
|
||||||
struct f2fs_filename *fname)
|
struct f2fs_filename *fname)
|
||||||
{
|
{
|
||||||
#if IS_ENABLED(CONFIG_UNICODE)
|
|
||||||
struct super_block *sb = dir->i_sb;
|
struct super_block *sb = dir->i_sb;
|
||||||
|
unsigned char *buf;
|
||||||
|
int len;
|
||||||
|
|
||||||
if (IS_CASEFOLDED(dir) &&
|
if (IS_CASEFOLDED(dir) &&
|
||||||
!is_dot_dotdot(fname->usr_fname->name, fname->usr_fname->len)) {
|
!is_dot_dotdot(fname->usr_fname->name, fname->usr_fname->len)) {
|
||||||
fname->cf_name.name = f2fs_kmem_cache_alloc(f2fs_cf_name_slab,
|
buf = f2fs_kmem_cache_alloc(f2fs_cf_name_slab,
|
||||||
GFP_NOFS, false, F2FS_SB(sb));
|
GFP_NOFS, false, F2FS_SB(sb));
|
||||||
if (!fname->cf_name.name)
|
if (!buf)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
fname->cf_name.len = utf8_casefold(sb->s_encoding,
|
|
||||||
fname->usr_fname,
|
len = utf8_casefold(sb->s_encoding, fname->usr_fname,
|
||||||
fname->cf_name.name,
|
buf, F2FS_NAME_LEN);
|
||||||
F2FS_NAME_LEN);
|
if (len <= 0) {
|
||||||
if ((int)fname->cf_name.len <= 0) {
|
kmem_cache_free(f2fs_cf_name_slab, buf);
|
||||||
kmem_cache_free(f2fs_cf_name_slab, fname->cf_name.name);
|
|
||||||
fname->cf_name.name = NULL;
|
|
||||||
if (sb_has_strict_encoding(sb))
|
if (sb_has_strict_encoding(sb))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
/* fall back to treating name as opaque byte sequence */
|
/* fall back to treating name as opaque byte sequence */
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
fname->cf_name.name = buf;
|
||||||
|
fname->cf_name.len = len;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
return 0;
|
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,
|
static int __f2fs_setup_filename(const struct inode *dir,
|
||||||
const struct fscrypt_name *crypt_name,
|
const struct fscrypt_name *crypt_name,
|
||||||
struct f2fs_filename *fname)
|
struct f2fs_filename *fname)
|
||||||
@ -142,12 +156,7 @@ void f2fs_free_filename(struct f2fs_filename *fname)
|
|||||||
kfree(fname->crypto_buf.name);
|
kfree(fname->crypto_buf.name);
|
||||||
fname->crypto_buf.name = NULL;
|
fname->crypto_buf.name = NULL;
|
||||||
#endif
|
#endif
|
||||||
#if IS_ENABLED(CONFIG_UNICODE)
|
f2fs_free_casefolded_name(fname);
|
||||||
if (fname->cf_name.name) {
|
|
||||||
kmem_cache_free(f2fs_cf_name_slab, fname->cf_name.name);
|
|
||||||
fname->cf_name.name = NULL;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned long dir_block_index(unsigned int level,
|
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);
|
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,
|
static inline int f2fs_match_name(const struct inode *dir,
|
||||||
const struct f2fs_filename *fname,
|
const struct f2fs_filename *fname,
|
||||||
const u8 *de_name, u32 de_name_len)
|
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;
|
struct fscrypt_name f;
|
||||||
|
|
||||||
#if IS_ENABLED(CONFIG_UNICODE)
|
#if IS_ENABLED(CONFIG_UNICODE)
|
||||||
if (fname->cf_name.name) {
|
if (fname->cf_name.name)
|
||||||
struct qstr cf = FSTR_TO_QSTR(&fname->cf_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
|
#endif
|
||||||
f.usr_fname = fname->usr_fname;
|
f.usr_fname = fname->usr_fname;
|
||||||
f.disk_name = fname->disk_name;
|
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
|
* internal operation where usr_fname is also NULL. In all these cases
|
||||||
* we fall back to treating the name as an opaque byte sequence.
|
* we fall back to treating the name as an opaque byte sequence.
|
||||||
*/
|
*/
|
||||||
struct fscrypt_str cf_name;
|
struct qstr cf_name;
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -3533,8 +3533,22 @@ int f2fs_get_tmpfile(struct mnt_idmap *idmap, struct inode *dir,
|
|||||||
/*
|
/*
|
||||||
* dir.c
|
* dir.c
|
||||||
*/
|
*/
|
||||||
|
#if IS_ENABLED(CONFIG_UNICODE)
|
||||||
int f2fs_init_casefolded_name(const struct inode *dir,
|
int f2fs_init_casefolded_name(const struct inode *dir,
|
||||||
struct f2fs_filename *fname);
|
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 f2fs_setup_filename(struct inode *dir, const struct qstr *iname,
|
||||||
int lookup, struct f2fs_filename *fname);
|
int lookup, struct f2fs_filename *fname);
|
||||||
int f2fs_prepare_lookup(struct inode *dir, struct dentry *dentry,
|
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;
|
goto out_iput;
|
||||||
}
|
}
|
||||||
out_splice:
|
out_splice:
|
||||||
#if IS_ENABLED(CONFIG_UNICODE)
|
if (IS_ENABLED(CONFIG_UNICODE) && !inode && IS_CASEFOLDED(dir)) {
|
||||||
if (!inode && IS_CASEFOLDED(dir)) {
|
|
||||||
/* Eventually we want to call d_add_ci(dentry, NULL)
|
/* Eventually we want to call d_add_ci(dentry, NULL)
|
||||||
* for negative dentries in the encoding case as
|
* for negative dentries in the encoding case as
|
||||||
* well. For now, prevent the negative dentry
|
* well. For now, prevent the negative dentry
|
||||||
@ -586,7 +585,7 @@ out_splice:
|
|||||||
trace_f2fs_lookup_end(dir, dentry, ino, err);
|
trace_f2fs_lookup_end(dir, dentry, ino, err);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
new = d_splice_alias(inode, dentry);
|
new = d_splice_alias(inode, dentry);
|
||||||
trace_f2fs_lookup_end(dir, !IS_ERR_OR_NULL(new) ? new : dentry,
|
trace_f2fs_lookup_end(dir, !IS_ERR_OR_NULL(new) ? new : dentry,
|
||||||
ino, IS_ERR(new) ? PTR_ERR(new) : err);
|
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_delete_entry(de, page, dir, inode);
|
||||||
f2fs_unlock_op(sbi);
|
f2fs_unlock_op(sbi);
|
||||||
|
|
||||||
#if IS_ENABLED(CONFIG_UNICODE)
|
|
||||||
/* VFS negative dentries are incompatible with Encoding and
|
/* VFS negative dentries are incompatible with Encoding and
|
||||||
* Case-insensitiveness. Eventually we'll want avoid
|
* Case-insensitiveness. Eventually we'll want avoid
|
||||||
* invalidating the dentries here, alongside with returning the
|
* invalidating the dentries here, alongside with returning the
|
||||||
* negative dentries at f2fs_lookup(), when it is better
|
* negative dentries at f2fs_lookup(), when it is better
|
||||||
* supported by the VFS for the CI case.
|
* supported by the VFS for the CI case.
|
||||||
*/
|
*/
|
||||||
if (IS_CASEFOLDED(dir))
|
if (IS_ENABLED(CONFIG_UNICODE) && IS_CASEFOLDED(dir))
|
||||||
d_invalidate(dentry);
|
d_invalidate(dentry);
|
||||||
#endif
|
|
||||||
if (IS_DIRSYNC(dir))
|
if (IS_DIRSYNC(dir))
|
||||||
f2fs_sync_fs(sbi->sb, 1);
|
f2fs_sync_fs(sbi->sb, 1);
|
||||||
fail:
|
fail:
|
||||||
|
@ -46,10 +46,6 @@
|
|||||||
|
|
||||||
static struct kmem_cache *fsync_entry_slab;
|
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)
|
bool f2fs_space_for_roll_forward(struct f2fs_sb_info *sbi)
|
||||||
{
|
{
|
||||||
s64 nalloc = percpu_counter_sum_positive(&sbi->alloc_valid_block_count);
|
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)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
f2fs_hash_filename(dir, fname);
|
f2fs_hash_filename(dir, fname);
|
||||||
#if IS_ENABLED(CONFIG_UNICODE)
|
|
||||||
/* Case-sensitive match is fine for recovery */
|
/* Case-sensitive match is fine for recovery */
|
||||||
kmem_cache_free(f2fs_cf_name_slab, fname->cf_name.name);
|
f2fs_free_casefolded_name(fname);
|
||||||
fname->cf_name.name = NULL;
|
|
||||||
#endif
|
|
||||||
} else {
|
} else {
|
||||||
f2fs_hash_filename(dir, fname);
|
f2fs_hash_filename(dir, fname);
|
||||||
}
|
}
|
||||||
|
@ -321,7 +321,7 @@ struct kmem_cache *f2fs_cf_name_slab;
|
|||||||
static int __init f2fs_create_casefold_cache(void)
|
static int __init f2fs_create_casefold_cache(void)
|
||||||
{
|
{
|
||||||
f2fs_cf_name_slab = f2fs_kmem_cache_create("f2fs_casefolded_name",
|
f2fs_cf_name_slab = f2fs_kmem_cache_create("f2fs_casefolded_name",
|
||||||
F2FS_NAME_LEN);
|
F2FS_NAME_LEN);
|
||||||
return f2fs_cf_name_slab ? 0 : -ENOMEM;
|
return f2fs_cf_name_slab ? 0 : -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1326,13 +1326,13 @@ default_check:
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
#endif
|
#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,
|
f2fs_err(sbi,
|
||||||
"Filesystem with casefold feature cannot be mounted without CONFIG_UNICODE");
|
"Filesystem with casefold feature cannot be mounted without CONFIG_UNICODE");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
/*
|
/*
|
||||||
* The BLKZONED feature indicates that the drive was formatted with
|
* The BLKZONED feature indicates that the drive was formatted with
|
||||||
* zone alignment optimization. This is optional for host-aware
|
* 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,
|
.d_revalidate = fscrypt_d_revalidate,
|
||||||
#endif
|
#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
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_FS_ENCRYPTION
|
#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 int generic_check_addressable(unsigned, u64);
|
||||||
|
|
||||||
extern void generic_set_sb_d_ops(struct super_block *sb);
|
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)
|
static inline bool sb_has_encoding(const struct super_block *sb)
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user