mirror of
https://github.com/torvalds/linux.git
synced 2026-05-29 17:43:52 +02:00
ovl: Create ovl_casefold() to support casefolded strncmp()
To add overlayfs support casefold layers, create a new function ovl_casefold(), to be able to do case-insensitive strncmp(). ovl_casefold() allocates a new buffer and stores the casefolded version of the string on it. If the allocation or the casefold operation fails, fallback to use the original string. The case-insentive name is then used in the rb-tree search/insertion operation. If the name is found in the rb-tree, the name can be discarded and the buffer is freed. If the name isn't found, it's then stored at struct ovl_cache_entry to be used later. Reviewed-by: Amir Goldstein <amir73il@gmail.com> Signed-off-by: André Almeida <andrealmeid@igalia.com> Signed-off-by: Amir Goldstein <amir73il@gmail.com>
This commit is contained in:
parent
5fbf73c7f1
commit
ee95c5fc86
|
|
@ -27,6 +27,8 @@ struct ovl_cache_entry {
|
|||
bool is_upper;
|
||||
bool is_whiteout;
|
||||
bool check_xwhiteout;
|
||||
const char *c_name;
|
||||
int c_len;
|
||||
char name[];
|
||||
};
|
||||
|
||||
|
|
@ -45,6 +47,7 @@ struct ovl_readdir_data {
|
|||
struct list_head *list;
|
||||
struct list_head middle;
|
||||
struct ovl_cache_entry *first_maybe_whiteout;
|
||||
struct unicode_map *map;
|
||||
int count;
|
||||
int err;
|
||||
bool is_upper;
|
||||
|
|
@ -66,6 +69,31 @@ static struct ovl_cache_entry *ovl_cache_entry_from_node(struct rb_node *n)
|
|||
return rb_entry(n, struct ovl_cache_entry, node);
|
||||
}
|
||||
|
||||
static int ovl_casefold(struct ovl_readdir_data *rdd, const char *str, int len,
|
||||
char **dst)
|
||||
{
|
||||
const struct qstr qstr = { .name = str, .len = len };
|
||||
char *cf_name;
|
||||
int cf_len;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_UNICODE) || !rdd->map || is_dot_dotdot(str, len))
|
||||
return 0;
|
||||
|
||||
cf_name = kmalloc(NAME_MAX, GFP_KERNEL);
|
||||
if (!cf_name) {
|
||||
rdd->err = -ENOMEM;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
cf_len = utf8_casefold(rdd->map, &qstr, cf_name, NAME_MAX);
|
||||
if (cf_len > 0)
|
||||
*dst = cf_name;
|
||||
else
|
||||
kfree(cf_name);
|
||||
|
||||
return cf_len;
|
||||
}
|
||||
|
||||
static bool ovl_cache_entry_find_link(const char *name, int len,
|
||||
struct rb_node ***link,
|
||||
struct rb_node **parent)
|
||||
|
|
@ -79,10 +107,10 @@ static bool ovl_cache_entry_find_link(const char *name, int len,
|
|||
|
||||
*parent = *newp;
|
||||
tmp = ovl_cache_entry_from_node(*newp);
|
||||
cmp = strncmp(name, tmp->name, len);
|
||||
cmp = strncmp(name, tmp->c_name, len);
|
||||
if (cmp > 0)
|
||||
newp = &tmp->node.rb_right;
|
||||
else if (cmp < 0 || len < tmp->len)
|
||||
else if (cmp < 0 || len < tmp->c_len)
|
||||
newp = &tmp->node.rb_left;
|
||||
else
|
||||
found = true;
|
||||
|
|
@ -101,10 +129,10 @@ static struct ovl_cache_entry *ovl_cache_entry_find(struct rb_root *root,
|
|||
while (node) {
|
||||
struct ovl_cache_entry *p = ovl_cache_entry_from_node(node);
|
||||
|
||||
cmp = strncmp(name, p->name, len);
|
||||
cmp = strncmp(name, p->c_name, len);
|
||||
if (cmp > 0)
|
||||
node = p->node.rb_right;
|
||||
else if (cmp < 0 || len < p->len)
|
||||
else if (cmp < 0 || len < p->c_len)
|
||||
node = p->node.rb_left;
|
||||
else
|
||||
return p;
|
||||
|
|
@ -145,6 +173,7 @@ static bool ovl_calc_d_ino(struct ovl_readdir_data *rdd,
|
|||
|
||||
static struct ovl_cache_entry *ovl_cache_entry_new(struct ovl_readdir_data *rdd,
|
||||
const char *name, int len,
|
||||
const char *c_name, int c_len,
|
||||
u64 ino, unsigned int d_type)
|
||||
{
|
||||
struct ovl_cache_entry *p;
|
||||
|
|
@ -167,6 +196,14 @@ static struct ovl_cache_entry *ovl_cache_entry_new(struct ovl_readdir_data *rdd,
|
|||
/* Defer check for overlay.whiteout to ovl_iterate() */
|
||||
p->check_xwhiteout = rdd->in_xwhiteouts_dir && d_type == DT_REG;
|
||||
|
||||
if (c_name && c_name != name) {
|
||||
p->c_name = c_name;
|
||||
p->c_len = c_len;
|
||||
} else {
|
||||
p->c_name = p->name;
|
||||
p->c_len = len;
|
||||
}
|
||||
|
||||
if (d_type == DT_CHR) {
|
||||
p->next_maybe_whiteout = rdd->first_maybe_whiteout;
|
||||
rdd->first_maybe_whiteout = p;
|
||||
|
|
@ -174,48 +211,62 @@ static struct ovl_cache_entry *ovl_cache_entry_new(struct ovl_readdir_data *rdd,
|
|||
return p;
|
||||
}
|
||||
|
||||
static bool ovl_cache_entry_add_rb(struct ovl_readdir_data *rdd,
|
||||
const char *name, int len, u64 ino,
|
||||
/* Return 0 for found, 1 for added, <0 for error */
|
||||
static int ovl_cache_entry_add_rb(struct ovl_readdir_data *rdd,
|
||||
const char *name, int len,
|
||||
const char *c_name, int c_len,
|
||||
u64 ino,
|
||||
unsigned int d_type)
|
||||
{
|
||||
struct rb_node **newp = &rdd->root->rb_node;
|
||||
struct rb_node *parent = NULL;
|
||||
struct ovl_cache_entry *p;
|
||||
|
||||
if (ovl_cache_entry_find_link(name, len, &newp, &parent))
|
||||
return true;
|
||||
if (ovl_cache_entry_find_link(c_name, c_len, &newp, &parent))
|
||||
return 0;
|
||||
|
||||
p = ovl_cache_entry_new(rdd, name, len, ino, d_type);
|
||||
p = ovl_cache_entry_new(rdd, name, len, c_name, c_len, ino, d_type);
|
||||
if (p == NULL) {
|
||||
rdd->err = -ENOMEM;
|
||||
return false;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
list_add_tail(&p->l_node, rdd->list);
|
||||
rb_link_node(&p->node, parent, newp);
|
||||
rb_insert_color(&p->node, rdd->root);
|
||||
|
||||
return true;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool ovl_fill_lowest(struct ovl_readdir_data *rdd,
|
||||
/* Return 0 for found, 1 for added, <0 for error */
|
||||
static int ovl_fill_lowest(struct ovl_readdir_data *rdd,
|
||||
const char *name, int namelen,
|
||||
const char *c_name, int c_len,
|
||||
loff_t offset, u64 ino, unsigned int d_type)
|
||||
{
|
||||
struct ovl_cache_entry *p;
|
||||
|
||||
p = ovl_cache_entry_find(rdd->root, name, namelen);
|
||||
p = ovl_cache_entry_find(rdd->root, c_name, c_len);
|
||||
if (p) {
|
||||
list_move_tail(&p->l_node, &rdd->middle);
|
||||
return 0;
|
||||
} else {
|
||||
p = ovl_cache_entry_new(rdd, name, namelen, ino, d_type);
|
||||
p = ovl_cache_entry_new(rdd, name, namelen, c_name, c_len,
|
||||
ino, d_type);
|
||||
if (p == NULL)
|
||||
rdd->err = -ENOMEM;
|
||||
else
|
||||
list_add_tail(&p->l_node, &rdd->middle);
|
||||
}
|
||||
|
||||
return rdd->err == 0;
|
||||
return rdd->err ?: 1;
|
||||
}
|
||||
|
||||
static void ovl_cache_entry_free(struct ovl_cache_entry *p)
|
||||
{
|
||||
if (p->c_name != p->name)
|
||||
kfree(p->c_name);
|
||||
kfree(p);
|
||||
}
|
||||
|
||||
void ovl_cache_free(struct list_head *list)
|
||||
|
|
@ -224,7 +275,7 @@ void ovl_cache_free(struct list_head *list)
|
|||
struct ovl_cache_entry *n;
|
||||
|
||||
list_for_each_entry_safe(p, n, list, l_node)
|
||||
kfree(p);
|
||||
ovl_cache_entry_free(p);
|
||||
|
||||
INIT_LIST_HEAD(list);
|
||||
}
|
||||
|
|
@ -260,12 +311,39 @@ static bool ovl_fill_merge(struct dir_context *ctx, const char *name,
|
|||
{
|
||||
struct ovl_readdir_data *rdd =
|
||||
container_of(ctx, struct ovl_readdir_data, ctx);
|
||||
struct ovl_fs *ofs = OVL_FS(rdd->dentry->d_sb);
|
||||
const char *c_name = NULL;
|
||||
char *cf_name = NULL;
|
||||
int c_len = 0, ret;
|
||||
|
||||
if (ofs->casefold)
|
||||
c_len = ovl_casefold(rdd, name, namelen, &cf_name);
|
||||
|
||||
if (rdd->err)
|
||||
return false;
|
||||
|
||||
if (c_len <= 0) {
|
||||
c_name = name;
|
||||
c_len = namelen;
|
||||
} else {
|
||||
c_name = cf_name;
|
||||
}
|
||||
|
||||
rdd->count++;
|
||||
if (!rdd->is_lowest)
|
||||
return ovl_cache_entry_add_rb(rdd, name, namelen, ino, d_type);
|
||||
ret = ovl_cache_entry_add_rb(rdd, name, namelen, c_name, c_len, ino, d_type);
|
||||
else
|
||||
return ovl_fill_lowest(rdd, name, namelen, offset, ino, d_type);
|
||||
ret = ovl_fill_lowest(rdd, name, namelen, c_name, c_len, offset, ino, d_type);
|
||||
|
||||
/*
|
||||
* If ret == 1, that means that c_name is being used as part of struct
|
||||
* ovl_cache_entry and will be freed at ovl_cache_free(). Otherwise,
|
||||
* c_name was found in the rb-tree so we can free it here.
|
||||
*/
|
||||
if (ret != 1 && c_name != name)
|
||||
kfree(c_name);
|
||||
|
||||
return ret >= 0;
|
||||
}
|
||||
|
||||
static int ovl_check_whiteouts(const struct path *path, struct ovl_readdir_data *rdd)
|
||||
|
|
@ -357,12 +435,18 @@ 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;
|
||||
struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
|
||||
|
||||
for (idx = 0; idx != -1; idx = next) {
|
||||
next = ovl_path_next(idx, dentry, &realpath, &layer);
|
||||
|
||||
if (ofs->casefold)
|
||||
rdd.map = sb_encoding(realpath.dentry->d_sb);
|
||||
|
||||
rdd.is_upper = ovl_dentry_upper(dentry) == realpath.dentry;
|
||||
rdd.in_xwhiteouts_dir = layer->has_xwhiteouts &&
|
||||
ovl_dentry_has_xwhiteouts(dentry);
|
||||
|
|
@ -555,7 +639,7 @@ static bool ovl_fill_plain(struct dir_context *ctx, const char *name,
|
|||
container_of(ctx, struct ovl_readdir_data, ctx);
|
||||
|
||||
rdd->count++;
|
||||
p = ovl_cache_entry_new(rdd, name, namelen, ino, d_type);
|
||||
p = ovl_cache_entry_new(rdd, name, namelen, NULL, 0, ino, d_type);
|
||||
if (p == NULL) {
|
||||
rdd->err = -ENOMEM;
|
||||
return false;
|
||||
|
|
@ -595,7 +679,7 @@ static int ovl_dir_read_impure(const struct path *path, struct list_head *list,
|
|||
}
|
||||
if (p->ino == p->real_ino) {
|
||||
list_del(&p->l_node);
|
||||
kfree(p);
|
||||
ovl_cache_entry_free(p);
|
||||
} else {
|
||||
struct rb_node **newp = &root->rb_node;
|
||||
struct rb_node *parent = NULL;
|
||||
|
|
@ -1023,7 +1107,7 @@ int ovl_check_empty_dir(struct dentry *dentry, struct list_head *list)
|
|||
|
||||
del_entry:
|
||||
list_del(&p->l_node);
|
||||
kfree(p);
|
||||
ovl_cache_entry_free(p);
|
||||
}
|
||||
|
||||
return err;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user