Commit 3e01cee3 authored by Miklos Szeredi's avatar Miklos Szeredi

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: default avatarMiklos Szeredi <mszeredi@suse.cz>
parent 3d3c6b89
...@@ -80,23 +80,50 @@ static struct ovl_cache_entry *ovl_cache_entry_find(struct rb_root *root, ...@@ -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; memcpy(p->name, name, len);
p->type = d_type; p->name[len] = '\0';
p->ino = ino; p->len = len;
p->is_whiteout = false; p->type = d_type;
p->is_cursor = false; 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, ...@@ -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, ...@@ -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, ...@@ -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) ...@@ -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, ...@@ -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, ...@@ -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);
break;
}
/* /*
* If this is not the lowermost layer, check whiteout and opaque * Only makes sense to check opaque dir if this is not the
* directory. * lowermost layer.
*/ */
if (i < poe->numlower - 1) { if (i < poe->numlower - 1 && ovl_is_opaquedir(this))
if (ovl_is_whiteout(this)) { opaque = true;
dput(this);
break;
} else if (ovl_is_opaquedir(this)) {
opaque = true;
}
}
/* /*
* If this is a non-directory then stop here. * If this is a non-directory then stop here.
* *
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment