Skip to content

Commit c843843

Browse files
drosen-googleJaegeuk Kim
authored andcommitted
fs: 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. The new dentry operations are functionally equivalent to the existing operations in ext4 and f2fs, apart from the use of utf8_casefold_hash to avoid an allocation. By providing a common implementation, all users can benefit from any optimizations without needing to port over improvements. Signed-off-by: Daniel Rosenberg <drosen@google.com> Reviewed-by: Eric Biggers <ebiggers@google.com> Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
1 parent 3d7bfea commit c843843

File tree

2 files changed

+103
-0
lines changed

2 files changed

+103
-0
lines changed

fs/libfs.c

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
#include <linux/fs_context.h>
2121
#include <linux/pseudo_fs.h>
2222
#include <linux/fsnotify.h>
23+
#include <linux/unicode.h>
24+
#include <linux/fscrypt.h>
2325

2426
#include <linux/uaccess.h>
2527

@@ -1363,3 +1365,88 @@ bool is_empty_dir_inode(struct inode *inode)
13631365
return (inode->i_fop == &empty_dir_operations) &&
13641366
(inode->i_op == &empty_dir_inode_operations);
13651367
}
1368+
1369+
#ifdef CONFIG_UNICODE
1370+
/*
1371+
* Determine if the name of a dentry should be casefolded.
1372+
*
1373+
* Return: if names will need casefolding
1374+
*/
1375+
static bool needs_casefold(const struct inode *dir)
1376+
{
1377+
return IS_CASEFOLDED(dir) && dir->i_sb->s_encoding;
1378+
}
1379+
1380+
/**
1381+
* generic_ci_d_compare - generic d_compare implementation for casefolding filesystems
1382+
* @dentry: dentry whose name we are checking against
1383+
* @len: len of name of dentry
1384+
* @str: str pointer to name of dentry
1385+
* @name: Name to compare against
1386+
*
1387+
* Return: 0 if names match, 1 if mismatch, or -ERRNO
1388+
*/
1389+
int generic_ci_d_compare(const struct dentry *dentry, unsigned int len,
1390+
const char *str, const struct qstr *name)
1391+
{
1392+
const struct dentry *parent = READ_ONCE(dentry->d_parent);
1393+
const struct inode *dir = READ_ONCE(parent->d_inode);
1394+
const struct super_block *sb = dentry->d_sb;
1395+
const struct unicode_map *um = sb->s_encoding;
1396+
struct qstr qstr = QSTR_INIT(str, len);
1397+
char strbuf[DNAME_INLINE_LEN];
1398+
int ret;
1399+
1400+
if (!dir || !needs_casefold(dir))
1401+
goto fallback;
1402+
/*
1403+
* If the dentry name is stored in-line, then it may be concurrently
1404+
* modified by a rename. If this happens, the VFS will eventually retry
1405+
* the lookup, so it doesn't matter what ->d_compare() returns.
1406+
* However, it's unsafe to call utf8_strncasecmp() with an unstable
1407+
* string. Therefore, we have to copy the name into a temporary buffer.
1408+
*/
1409+
if (len <= DNAME_INLINE_LEN - 1) {
1410+
memcpy(strbuf, str, len);
1411+
strbuf[len] = 0;
1412+
qstr.name = strbuf;
1413+
/* prevent compiler from optimizing out the temporary buffer */
1414+
barrier();
1415+
}
1416+
ret = utf8_strncasecmp(um, name, &qstr);
1417+
if (ret >= 0)
1418+
return ret;
1419+
1420+
if (sb_has_strict_encoding(sb))
1421+
return -EINVAL;
1422+
fallback:
1423+
if (len != name->len)
1424+
return 1;
1425+
return !!memcmp(str, name->name, len);
1426+
}
1427+
EXPORT_SYMBOL(generic_ci_d_compare);
1428+
1429+
/**
1430+
* generic_ci_d_hash - generic d_hash implementation for casefolding filesystems
1431+
* @dentry: dentry of the parent directory
1432+
* @str: qstr of name whose hash we should fill in
1433+
*
1434+
* Return: 0 if hash was successful or unchanged, and -EINVAL on error
1435+
*/
1436+
int generic_ci_d_hash(const struct dentry *dentry, struct qstr *str)
1437+
{
1438+
const struct inode *dir = READ_ONCE(dentry->d_inode);
1439+
struct super_block *sb = dentry->d_sb;
1440+
const struct unicode_map *um = sb->s_encoding;
1441+
int ret = 0;
1442+
1443+
if (!dir || !needs_casefold(dir))
1444+
return 0;
1445+
1446+
ret = utf8_casefold_hash(um, dentry, str);
1447+
if (ret < 0 && sb_has_strict_encoding(sb))
1448+
return -EINVAL;
1449+
return 0;
1450+
}
1451+
EXPORT_SYMBOL(generic_ci_d_hash);
1452+
#endif

include/linux/fs.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1371,6 +1371,12 @@ extern int send_sigurg(struct fown_struct *fown);
13711371
#define SB_ACTIVE (1<<30)
13721372
#define SB_NOUSER (1<<31)
13731373

1374+
/* These flags relate to encoding and casefolding */
1375+
#define SB_ENC_STRICT_MODE_FL (1 << 0)
1376+
1377+
#define sb_has_strict_encoding(sb) \
1378+
(sb->s_encoding_flags & SB_ENC_STRICT_MODE_FL)
1379+
13741380
/*
13751381
* Umount options
13761382
*/
@@ -1440,6 +1446,10 @@ struct super_block {
14401446
#endif
14411447
#ifdef CONFIG_FS_VERITY
14421448
const struct fsverity_operations *s_vop;
1449+
#endif
1450+
#ifdef CONFIG_UNICODE
1451+
struct unicode_map *s_encoding;
1452+
__u16 s_encoding_flags;
14431453
#endif
14441454
struct hlist_bl_head s_roots; /* alternate root dentries for NFS */
14451455
struct list_head s_mounts; /* list of mounts; _not_ for fs use */
@@ -3262,6 +3272,12 @@ extern int generic_file_fsync(struct file *, loff_t, loff_t, int);
32623272

32633273
extern int generic_check_addressable(unsigned, u64);
32643274

3275+
#ifdef CONFIG_UNICODE
3276+
extern int generic_ci_d_hash(const struct dentry *dentry, struct qstr *str);
3277+
extern int generic_ci_d_compare(const struct dentry *dentry, unsigned int len,
3278+
const char *str, const struct qstr *name);
3279+
#endif
3280+
32653281
#ifdef CONFIG_MIGRATION
32663282
extern int buffer_migrate_page(struct address_space *,
32673283
struct page *, struct page *,

0 commit comments

Comments
 (0)