Changes from RFC v1: - Handle errors from charset_normalize/casefold (Olaf Weber) - Send length of both strings to comparison functions. - Cast length type to size_t Signed-off-by: Gabriel Krisman Bertazi <krisman@xxxxxxxxxxxxxxx> --- fs/ext4/dir.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ fs/ext4/ext4.h | 2 ++ fs/ext4/super.c | 2 ++ 3 files changed, 52 insertions(+) diff --git a/fs/ext4/dir.c b/fs/ext4/dir.c index da87cf757f7d..02911f2681ab 100644 --- a/fs/ext4/dir.c +++ b/fs/ext4/dir.c @@ -26,6 +26,7 @@ #include <linux/buffer_head.h> #include <linux/slab.h> #include <linux/iversion.h> +#include <linux/nls.h> #include "ext4.h" #include "xattr.h" @@ -662,3 +663,50 @@ const struct file_operations ext4_dir_operations = { .open = ext4_dir_open, .release = ext4_release_dir, }; + +static int ext4_d_compare(const struct dentry *dentry, unsigned int len, + const char *str, const struct qstr *name) +{ + struct nls_table *charset = EXT4_SB(dentry->d_sb)->encoding; + size_t nlen = strlen(name->name); + + return nls_strncmp(charset, str, len, name->name, nlen); +} + +static int ext4_d_compare_ci(const struct dentry *dentry, unsigned int len, + const char *str, const struct qstr *name) +{ + struct nls_table *charset = EXT4_SB(dentry->d_sb)->encoding; + size_t nlen = strlen(name->name); + + return nls_strncasecmp(charset, str, len, name->name, nlen); +} + +static int ext4_d_ci_hash(const struct dentry *dentry, struct qstr *q) +{ + unsigned long hash; + int i, len; + unsigned char *folded = NULL; + const struct nls_table *charset = EXT4_SB(dentry->d_sb)->encoding; + + len = nls_casefold(charset, q->name, q->len, &folded); + + if (len < 0) { + kfree(folded); + return -EINVAL; + } + + hash = init_name_hash(dentry); + for (i = 0; i < len; i++) + hash = partial_name_hash(folded[i], hash); + q->hash = end_name_hash(hash); + + kfree(folded); + return 0; +} + +const struct dentry_operations ext4_dentry_ops = { + .d_hash = ext4_d_ci_hash, + .d_compare = ext4_d_compare, + .d_compare_ci = ext4_d_compare_ci, +}; diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index ebda06e4cb24..eb193d66565a 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -2939,6 +2939,8 @@ static inline void ext4_unlock_group(struct super_block *sb, /* dir.c */ extern const struct file_operations ext4_dir_operations; +extern const struct dentry_operations ext4_dentry_ops; +extern const struct dentry_operations ext4_ci_dentry_ops; /* file.c */ extern const struct inode_operations ext4_file_inode_operations; diff --git a/fs/ext4/super.c b/fs/ext4/super.c index bbf0a5c4104b..e73976986dcb 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -4241,6 +4241,8 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) iput(root); goto failed_mount4; } + + sb->s_d_op = &ext4_dentry_ops; sb->s_root = d_make_root(root); if (!sb->s_root) { ext4_msg(sb, KERN_ERR, "get root dentry failed"); -- 2.17.0