In order to make case-insentive mounting points work, overlayfs needs the casefolded version of its dentries so the search and insertion in the struct ovl_readdir_data's red-black compares the dentry names in a case-insentive fashion. If a dentry is casefolded, compute and store it's casefolded name and it's Unicode map. If utf8_casefold() fails, set it's name pointer as NULL so it can be ignored and fallback to the original name. Signed-off-by: André Almeida <andrealmeid@xxxxxxxxxx> --- fs/overlayfs/readdir.c | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/fs/overlayfs/readdir.c b/fs/overlayfs/readdir.c index b65cdfce31ce27172d28d879559f1008b9c87320..83bca1bcb0488461b08effa70b32ff2fefba134e 100644 --- a/fs/overlayfs/readdir.c +++ b/fs/overlayfs/readdir.c @@ -16,6 +16,8 @@ #include <linux/overflow.h> #include "overlayfs.h" +#define OVL_NAME_LEN 255 + struct ovl_cache_entry { unsigned int len; unsigned int type; @@ -27,6 +29,9 @@ struct ovl_cache_entry { bool is_upper; bool is_whiteout; bool check_xwhiteout; + struct unicode_map *map; + char *cf_name; + int cf_len; char name[]; }; @@ -50,6 +55,7 @@ struct ovl_readdir_data { bool is_upper; bool d_type_supported; bool in_xwhiteouts_dir; + struct unicode_map *map; }; struct ovl_dir_file { @@ -166,6 +172,29 @@ static struct ovl_cache_entry *ovl_cache_entry_new(struct ovl_readdir_data *rdd, p->is_whiteout = false; /* Defer check for overlay.whiteout to ovl_iterate() */ p->check_xwhiteout = rdd->in_xwhiteouts_dir && d_type == DT_REG; + p->map = rdd->map; + p->cf_name = NULL; + + if (p->map && !is_dot_dotdot(name, len)) { + const struct qstr str = { .name = name, .len = len }; + int ret; + + p->cf_name = kmalloc(OVL_NAME_LEN, GFP_KERNEL); + + if (!p->cf_name) { + kfree(p); + return NULL; + } + + ret = utf8_casefold(p->map, &str, p->cf_name, OVL_NAME_LEN); + + if (ret < 0) { + kfree(p->cf_name); + p->cf_name = NULL; + } else { + p->cf_len = ret; + } + } if (d_type == DT_CHR) { p->next_maybe_whiteout = rdd->first_maybe_whiteout; @@ -223,8 +252,10 @@ void ovl_cache_free(struct list_head *list) struct ovl_cache_entry *p; struct ovl_cache_entry *n; - list_for_each_entry_safe(p, n, list, l_node) + list_for_each_entry_safe(p, n, list, l_node) { + kfree(p->cf_name); kfree(p); + } INIT_LIST_HEAD(list); } @@ -357,12 +388,19 @@ static int ovl_dir_read_merged(struct dentry *dentry, struct list_head *list, .list = list, .root = root, .is_lowest = false, + .map = NULL, }; int idx, next; const struct ovl_layer *layer; for (idx = 0; idx != -1; idx = next) { next = ovl_path_next(idx, dentry, &realpath, &layer); + +#if IS_ENABLED(CONFIG_UNICODE) + if (ovl_dentry_casefolded(realpath.dentry)) + rdd.map = realpath.dentry->d_sb->s_encoding; +#endif + rdd.is_upper = ovl_dentry_upper(dentry) == realpath.dentry; rdd.in_xwhiteouts_dir = layer->has_xwhiteouts && ovl_dentry_has_xwhiteouts(dentry); -- 2.50.1