ovl: check whiteout on lowest layer as well
Not checking whiteouts on lowest layer was an optimization (there's nothing to white out there), but it could result in inconsitent behavior when a layer previously used as upper/middle is later used as lowest. Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
This commit is contained in:
Родитель
3d3c6b8939
Коммит
3e01cee3b9
|
@ -80,23 +80,50 @@ static struct ovl_cache_entry *ovl_cache_entry_find(struct rb_root *root,
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct ovl_cache_entry *ovl_cache_entry_new(const char *name, int len,
|
static struct ovl_cache_entry *ovl_cache_entry_new(struct dentry *dir,
|
||||||
|
const char *name, int len,
|
||||||
u64 ino, unsigned int d_type)
|
u64 ino, unsigned int d_type)
|
||||||
{
|
{
|
||||||
struct ovl_cache_entry *p;
|
struct ovl_cache_entry *p;
|
||||||
size_t size = offsetof(struct ovl_cache_entry, name[len + 1]);
|
size_t size = offsetof(struct ovl_cache_entry, name[len + 1]);
|
||||||
|
|
||||||
p = kmalloc(size, GFP_KERNEL);
|
p = kmalloc(size, GFP_KERNEL);
|
||||||
if (p) {
|
if (!p)
|
||||||
memcpy(p->name, name, len);
|
return NULL;
|
||||||
p->name[len] = '\0';
|
|
||||||
p->len = len;
|
|
||||||
p->type = d_type;
|
|
||||||
p->ino = ino;
|
|
||||||
p->is_whiteout = false;
|
|
||||||
p->is_cursor = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
memcpy(p->name, name, len);
|
||||||
|
p->name[len] = '\0';
|
||||||
|
p->len = len;
|
||||||
|
p->type = d_type;
|
||||||
|
p->ino = ino;
|
||||||
|
p->is_whiteout = false;
|
||||||
|
p->is_cursor = false;
|
||||||
|
|
||||||
|
if (d_type == DT_CHR) {
|
||||||
|
struct dentry *dentry;
|
||||||
|
const struct cred *old_cred;
|
||||||
|
struct cred *override_cred;
|
||||||
|
|
||||||
|
override_cred = prepare_creds();
|
||||||
|
if (!override_cred) {
|
||||||
|
kfree(p);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CAP_DAC_OVERRIDE for lookup
|
||||||
|
*/
|
||||||
|
cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE);
|
||||||
|
old_cred = override_creds(override_cred);
|
||||||
|
|
||||||
|
dentry = lookup_one_len(name, dir, len);
|
||||||
|
if (!IS_ERR(dentry)) {
|
||||||
|
p->is_whiteout = ovl_is_whiteout(dentry);
|
||||||
|
dput(dentry);
|
||||||
|
}
|
||||||
|
revert_creds(old_cred);
|
||||||
|
put_cred(override_cred);
|
||||||
|
}
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,36 +150,10 @@ static int ovl_cache_entry_add_rb(struct ovl_readdir_data *rdd,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
p = ovl_cache_entry_new(name, len, ino, d_type);
|
p = ovl_cache_entry_new(rdd->dir, name, len, ino, d_type);
|
||||||
if (p == NULL)
|
if (p == NULL)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
if (d_type == DT_CHR) {
|
|
||||||
struct dentry *dentry;
|
|
||||||
const struct cred *old_cred;
|
|
||||||
struct cred *override_cred;
|
|
||||||
|
|
||||||
override_cred = prepare_creds();
|
|
||||||
if (!override_cred) {
|
|
||||||
kfree(p);
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* CAP_DAC_OVERRIDE for lookup
|
|
||||||
*/
|
|
||||||
cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE);
|
|
||||||
old_cred = override_creds(override_cred);
|
|
||||||
|
|
||||||
dentry = lookup_one_len(name, rdd->dir, len);
|
|
||||||
if (!IS_ERR(dentry)) {
|
|
||||||
p->is_whiteout = ovl_is_whiteout(dentry);
|
|
||||||
dput(dentry);
|
|
||||||
}
|
|
||||||
revert_creds(old_cred);
|
|
||||||
put_cred(override_cred);
|
|
||||||
}
|
|
||||||
|
|
||||||
list_add_tail(&p->l_node, rdd->list);
|
list_add_tail(&p->l_node, rdd->list);
|
||||||
rb_link_node(&p->node, parent, newp);
|
rb_link_node(&p->node, parent, newp);
|
||||||
rb_insert_color(&p->node, &rdd->root);
|
rb_insert_color(&p->node, &rdd->root);
|
||||||
|
@ -170,7 +171,7 @@ static int ovl_fill_lower(struct ovl_readdir_data *rdd,
|
||||||
if (p) {
|
if (p) {
|
||||||
list_move_tail(&p->l_node, &rdd->middle);
|
list_move_tail(&p->l_node, &rdd->middle);
|
||||||
} else {
|
} else {
|
||||||
p = ovl_cache_entry_new(name, namelen, ino, d_type);
|
p = ovl_cache_entry_new(rdd->dir, name, namelen, ino, d_type);
|
||||||
if (p == NULL)
|
if (p == NULL)
|
||||||
rdd->err = -ENOMEM;
|
rdd->err = -ENOMEM;
|
||||||
else
|
else
|
||||||
|
@ -229,6 +230,7 @@ static inline int ovl_dir_read(struct path *realpath,
|
||||||
if (IS_ERR(realfile))
|
if (IS_ERR(realfile))
|
||||||
return PTR_ERR(realfile);
|
return PTR_ERR(realfile);
|
||||||
|
|
||||||
|
rdd->dir = realpath->dentry;
|
||||||
rdd->ctx.pos = 0;
|
rdd->ctx.pos = 0;
|
||||||
do {
|
do {
|
||||||
rdd->count = 0;
|
rdd->count = 0;
|
||||||
|
@ -274,7 +276,6 @@ static int ovl_dir_read_merged(struct dentry *dentry, struct list_head *list)
|
||||||
next = ovl_path_next(idx, dentry, &realpath);
|
next = ovl_path_next(idx, dentry, &realpath);
|
||||||
|
|
||||||
if (next != -1) {
|
if (next != -1) {
|
||||||
rdd.dir = realpath.dentry;
|
|
||||||
err = ovl_dir_read(&realpath, &rdd);
|
err = ovl_dir_read(&realpath, &rdd);
|
||||||
if (err)
|
if (err)
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -350,16 +350,12 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
|
||||||
if (IS_ERR(this))
|
if (IS_ERR(this))
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
/*
|
if (this) {
|
||||||
* If this is not the lowermost layer, check whiteout and opaque
|
|
||||||
* directory.
|
|
||||||
*/
|
|
||||||
if (poe->numlower && this) {
|
|
||||||
if (ovl_is_whiteout(this)) {
|
if (ovl_is_whiteout(this)) {
|
||||||
dput(this);
|
dput(this);
|
||||||
this = NULL;
|
this = NULL;
|
||||||
upperopaque = true;
|
upperopaque = true;
|
||||||
} else if (ovl_is_opaquedir(this)) {
|
} else if (poe->numlower && ovl_is_opaquedir(this)) {
|
||||||
upperopaque = true;
|
upperopaque = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -384,19 +380,16 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
|
||||||
goto out_put;
|
goto out_put;
|
||||||
if (!this)
|
if (!this)
|
||||||
continue;
|
continue;
|
||||||
|
if (ovl_is_whiteout(this)) {
|
||||||
/*
|
dput(this);
|
||||||
* If this is not the lowermost layer, check whiteout and opaque
|
break;
|
||||||
* directory.
|
|
||||||
*/
|
|
||||||
if (i < poe->numlower - 1) {
|
|
||||||
if (ovl_is_whiteout(this)) {
|
|
||||||
dput(this);
|
|
||||||
break;
|
|
||||||
} else if (ovl_is_opaquedir(this)) {
|
|
||||||
opaque = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
* Only makes sense to check opaque dir if this is not the
|
||||||
|
* lowermost layer.
|
||||||
|
*/
|
||||||
|
if (i < poe->numlower - 1 && ovl_is_opaquedir(this))
|
||||||
|
opaque = true;
|
||||||
/*
|
/*
|
||||||
* If this is a non-directory then stop here.
|
* If this is a non-directory then stop here.
|
||||||
*
|
*
|
||||||
|
|
Загрузка…
Ссылка в новой задаче