Skip to content

Commit 4a051e4

Browse files
committed
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
2 parents 7d15687 + 28add38 commit 4a051e4

File tree

11 files changed

+195
-202
lines changed

11 files changed

+195
-202
lines changed

fs/ext4/crypto.c

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,10 @@ int ext4_fname_setup_filename(struct inode *dir, const struct qstr *iname,
3131

3232
ext4_fname_from_fscrypt_name(fname, &name);
3333

34-
#if IS_ENABLED(CONFIG_UNICODE)
3534
err = ext4_fname_setup_ci_filename(dir, iname, fname);
3635
if (err)
3736
ext4_fname_free_filename(fname);
38-
#endif
37+
3938
return err;
4039
}
4140

@@ -51,11 +50,9 @@ int ext4_fname_prepare_lookup(struct inode *dir, struct dentry *dentry,
5150

5251
ext4_fname_from_fscrypt_name(fname, &name);
5352

54-
#if IS_ENABLED(CONFIG_UNICODE)
5553
err = ext4_fname_setup_ci_filename(dir, &dentry->d_name, fname);
5654
if (err)
5755
ext4_fname_free_filename(fname);
58-
#endif
5956
return err;
6057
}
6158

@@ -70,10 +67,7 @@ void ext4_fname_free_filename(struct ext4_filename *fname)
7067
fname->usr_fname = NULL;
7168
fname->disk_name.name = NULL;
7269

73-
#if IS_ENABLED(CONFIG_UNICODE)
74-
kfree(fname->cf_name.name);
75-
fname->cf_name.name = NULL;
76-
#endif
70+
ext4_fname_free_ci_filename(fname);
7771
}
7872

7973
static bool uuid_is_zero(__u8 u[16])

fs/ext4/ext4.h

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2511,7 +2511,7 @@ struct ext4_filename {
25112511
struct fscrypt_str crypto_buf;
25122512
#endif
25132513
#if IS_ENABLED(CONFIG_UNICODE)
2514-
struct fscrypt_str cf_name;
2514+
struct qstr cf_name;
25152515
#endif
25162516
};
25172517

@@ -2745,8 +2745,25 @@ ext4_fsblk_t ext4_inode_to_goal_block(struct inode *);
27452745

27462746
#if IS_ENABLED(CONFIG_UNICODE)
27472747
extern int ext4_fname_setup_ci_filename(struct inode *dir,
2748-
const struct qstr *iname,
2749-
struct ext4_filename *fname);
2748+
const struct qstr *iname,
2749+
struct ext4_filename *fname);
2750+
2751+
static inline void ext4_fname_free_ci_filename(struct ext4_filename *fname)
2752+
{
2753+
kfree(fname->cf_name.name);
2754+
fname->cf_name.name = NULL;
2755+
}
2756+
#else
2757+
static inline int ext4_fname_setup_ci_filename(struct inode *dir,
2758+
const struct qstr *iname,
2759+
struct ext4_filename *fname)
2760+
{
2761+
return 0;
2762+
}
2763+
2764+
static inline void ext4_fname_free_ci_filename(struct ext4_filename *fname)
2765+
{
2766+
}
27502767
#endif
27512768

27522769
/* ext4 encryption related stuff goes here crypto.c */
@@ -2769,16 +2786,11 @@ static inline int ext4_fname_setup_filename(struct inode *dir,
27692786
int lookup,
27702787
struct ext4_filename *fname)
27712788
{
2772-
int err = 0;
27732789
fname->usr_fname = iname;
27742790
fname->disk_name.name = (unsigned char *) iname->name;
27752791
fname->disk_name.len = iname->len;
27762792

2777-
#if IS_ENABLED(CONFIG_UNICODE)
2778-
err = ext4_fname_setup_ci_filename(dir, iname, fname);
2779-
#endif
2780-
2781-
return err;
2793+
return ext4_fname_setup_ci_filename(dir, iname, fname);
27822794
}
27832795

27842796
static inline int ext4_fname_prepare_lookup(struct inode *dir,
@@ -2790,10 +2802,7 @@ static inline int ext4_fname_prepare_lookup(struct inode *dir,
27902802

27912803
static inline void ext4_fname_free_filename(struct ext4_filename *fname)
27922804
{
2793-
#if IS_ENABLED(CONFIG_UNICODE)
2794-
kfree(fname->cf_name.name);
2795-
fname->cf_name.name = NULL;
2796-
#endif
2805+
ext4_fname_free_ci_filename(fname);
27972806
}
27982807

27992808
static inline int ext4_ioctl_get_encryption_pwsalt(struct file *filp,

fs/ext4/namei.c

Lines changed: 37 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1390,62 +1390,11 @@ static void dx_insert_block(struct dx_frame *frame, u32 hash, ext4_lblk_t block)
13901390
}
13911391

13921392
#if IS_ENABLED(CONFIG_UNICODE)
1393-
/*
1394-
* Test whether a case-insensitive directory entry matches the filename
1395-
* being searched for. If quick is set, assume the name being looked up
1396-
* is already in the casefolded form.
1397-
*
1398-
* Returns: 0 if the directory entry matches, more than 0 if it
1399-
* doesn't match or less than zero on error.
1400-
*/
1401-
static int ext4_ci_compare(const struct inode *parent, const struct qstr *name,
1402-
u8 *de_name, size_t de_name_len, bool quick)
1403-
{
1404-
const struct super_block *sb = parent->i_sb;
1405-
const struct unicode_map *um = sb->s_encoding;
1406-
struct fscrypt_str decrypted_name = FSTR_INIT(NULL, de_name_len);
1407-
struct qstr entry = QSTR_INIT(de_name, de_name_len);
1408-
int ret;
1409-
1410-
if (IS_ENCRYPTED(parent)) {
1411-
const struct fscrypt_str encrypted_name =
1412-
FSTR_INIT(de_name, de_name_len);
1413-
1414-
decrypted_name.name = kmalloc(de_name_len, GFP_KERNEL);
1415-
if (!decrypted_name.name)
1416-
return -ENOMEM;
1417-
ret = fscrypt_fname_disk_to_usr(parent, 0, 0, &encrypted_name,
1418-
&decrypted_name);
1419-
if (ret < 0)
1420-
goto out;
1421-
entry.name = decrypted_name.name;
1422-
entry.len = decrypted_name.len;
1423-
}
1424-
1425-
if (quick)
1426-
ret = utf8_strncasecmp_folded(um, name, &entry);
1427-
else
1428-
ret = utf8_strncasecmp(um, name, &entry);
1429-
if (ret < 0) {
1430-
/* Handle invalid character sequence as either an error
1431-
* or as an opaque byte sequence.
1432-
*/
1433-
if (sb_has_strict_encoding(sb))
1434-
ret = -EINVAL;
1435-
else if (name->len != entry.len)
1436-
ret = 1;
1437-
else
1438-
ret = !!memcmp(name->name, entry.name, entry.len);
1439-
}
1440-
out:
1441-
kfree(decrypted_name.name);
1442-
return ret;
1443-
}
1444-
14451393
int ext4_fname_setup_ci_filename(struct inode *dir, const struct qstr *iname,
14461394
struct ext4_filename *name)
14471395
{
1448-
struct fscrypt_str *cf_name = &name->cf_name;
1396+
struct qstr *cf_name = &name->cf_name;
1397+
unsigned char *buf;
14491398
struct dx_hash_info *hinfo = &name->hinfo;
14501399
int len;
14511400

@@ -1455,18 +1404,18 @@ int ext4_fname_setup_ci_filename(struct inode *dir, const struct qstr *iname,
14551404
return 0;
14561405
}
14571406

1458-
cf_name->name = kmalloc(EXT4_NAME_LEN, GFP_NOFS);
1459-
if (!cf_name->name)
1407+
buf = kmalloc(EXT4_NAME_LEN, GFP_NOFS);
1408+
if (!buf)
14601409
return -ENOMEM;
14611410

1462-
len = utf8_casefold(dir->i_sb->s_encoding,
1463-
iname, cf_name->name,
1464-
EXT4_NAME_LEN);
1411+
len = utf8_casefold(dir->i_sb->s_encoding, iname, buf, EXT4_NAME_LEN);
14651412
if (len <= 0) {
1466-
kfree(cf_name->name);
1467-
cf_name->name = NULL;
1413+
kfree(buf);
1414+
buf = NULL;
14681415
}
1416+
cf_name->name = buf;
14691417
cf_name->len = (unsigned) len;
1418+
14701419
if (!IS_ENCRYPTED(dir))
14711420
return 0;
14721421

@@ -1502,22 +1451,29 @@ static bool ext4_match(struct inode *parent,
15021451
#if IS_ENABLED(CONFIG_UNICODE)
15031452
if (IS_CASEFOLDED(parent) &&
15041453
(!IS_ENCRYPTED(parent) || fscrypt_has_encryption_key(parent))) {
1505-
if (fname->cf_name.name) {
1506-
struct qstr cf = {.name = fname->cf_name.name,
1507-
.len = fname->cf_name.len};
1508-
if (IS_ENCRYPTED(parent)) {
1509-
if (fname->hinfo.hash != EXT4_DIRENT_HASH(de) ||
1510-
fname->hinfo.minor_hash !=
1511-
EXT4_DIRENT_MINOR_HASH(de)) {
1512-
1513-
return false;
1514-
}
1515-
}
1516-
return !ext4_ci_compare(parent, &cf, de->name,
1517-
de->name_len, true);
1518-
}
1519-
return !ext4_ci_compare(parent, fname->usr_fname, de->name,
1520-
de->name_len, false);
1454+
/*
1455+
* Just checking IS_ENCRYPTED(parent) below is not
1456+
* sufficient to decide whether one can use the hash for
1457+
* skipping the string comparison, because the key might
1458+
* have been added right after
1459+
* ext4_fname_setup_ci_filename(). In this case, a hash
1460+
* mismatch will be a false negative. Therefore, make
1461+
* sure cf_name was properly initialized before
1462+
* considering the calculated hash.
1463+
*/
1464+
if (IS_ENCRYPTED(parent) && fname->cf_name.name &&
1465+
(fname->hinfo.hash != EXT4_DIRENT_HASH(de) ||
1466+
fname->hinfo.minor_hash != EXT4_DIRENT_MINOR_HASH(de)))
1467+
return false;
1468+
/*
1469+
* Treat comparison errors as not a match. The
1470+
* only case where it happens is on a disk
1471+
* corruption or ENOMEM.
1472+
*/
1473+
1474+
return generic_ci_match(parent, fname->usr_fname,
1475+
&fname->cf_name, de->name,
1476+
de->name_len) > 0;
15211477
}
15221478
#endif
15231479

@@ -1869,16 +1825,15 @@ static struct dentry *ext4_lookup(struct inode *dir, struct dentry *dentry, unsi
18691825
}
18701826
}
18711827

1872-
#if IS_ENABLED(CONFIG_UNICODE)
1873-
if (!inode && IS_CASEFOLDED(dir)) {
1828+
if (IS_ENABLED(CONFIG_UNICODE) && !inode && IS_CASEFOLDED(dir)) {
18741829
/* Eventually we want to call d_add_ci(dentry, NULL)
18751830
* for negative dentries in the encoding case as
18761831
* well. For now, prevent the negative dentry
18771832
* from being cached.
18781833
*/
18791834
return NULL;
18801835
}
1881-
#endif
1836+
18821837
return d_splice_alias(inode, dentry);
18831838
}
18841839

@@ -3208,16 +3163,14 @@ static int ext4_rmdir(struct inode *dir, struct dentry *dentry)
32083163
ext4_fc_track_unlink(handle, dentry);
32093164
retval = ext4_mark_inode_dirty(handle, dir);
32103165

3211-
#if IS_ENABLED(CONFIG_UNICODE)
32123166
/* VFS negative dentries are incompatible with Encoding and
32133167
* Case-insensitiveness. Eventually we'll want avoid
32143168
* invalidating the dentries here, alongside with returning the
32153169
* negative dentries at ext4_lookup(), when it is better
32163170
* supported by the VFS for the CI case.
32173171
*/
3218-
if (IS_CASEFOLDED(dir))
3172+
if (IS_ENABLED(CONFIG_UNICODE) && IS_CASEFOLDED(dir))
32193173
d_invalidate(dentry);
3220-
#endif
32213174

32223175
end_rmdir:
32233176
brelse(bh);
@@ -3319,16 +3272,15 @@ static int ext4_unlink(struct inode *dir, struct dentry *dentry)
33193272
goto out_trace;
33203273

33213274
retval = __ext4_unlink(dir, &dentry->d_name, d_inode(dentry), dentry);
3322-
#if IS_ENABLED(CONFIG_UNICODE)
3275+
33233276
/* VFS negative dentries are incompatible with Encoding and
33243277
* Case-insensitiveness. Eventually we'll want avoid
33253278
* invalidating the dentries here, alongside with returning the
33263279
* negative dentries at ext4_lookup(), when it is better
33273280
* supported by the VFS for the CI case.
33283281
*/
3329-
if (IS_CASEFOLDED(dir))
3282+
if (IS_ENABLED(CONFIG_UNICODE) && IS_CASEFOLDED(dir))
33303283
d_invalidate(dentry);
3331-
#endif
33323284

33333285
out_trace:
33343286
trace_ext4_unlink_exit(dentry, retval);

fs/ext4/super.c

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3586,14 +3586,12 @@ int ext4_feature_set_ok(struct super_block *sb, int readonly)
35863586
return 0;
35873587
}
35883588

3589-
#if !IS_ENABLED(CONFIG_UNICODE)
3590-
if (ext4_has_feature_casefold(sb)) {
3589+
if (!IS_ENABLED(CONFIG_UNICODE) && ext4_has_feature_casefold(sb)) {
35913590
ext4_msg(sb, KERN_ERR,
35923591
"Filesystem with casefold feature cannot be "
35933592
"mounted without CONFIG_UNICODE");
35943593
return 0;
35953594
}
3596-
#endif
35973595

35983596
if (readonly)
35993597
return 1;

0 commit comments

Comments
 (0)