summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiklos Szeredi <mszeredi@suse.cz>2014-12-13 00:59:45 +0100
committerMiklos Szeredi <mszeredi@suse.cz>2014-12-13 00:59:45 +0100
commit3e01cee3b980f96463cb6f378ab05303a99903d9 (patch)
tree59ad7b720642aeeaa4c6706703125b8332b44454
parent3d3c6b89399a1b5e8a59ffbb8cb2a7797a9ef154 (diff)
downloadlinux-3e01cee3b980f96463cb6f378ab05303a99903d9.tar.gz
linux-3e01cee3b980f96463cb6f378ab05303a99903d9.tar.xz
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>
-rw-r--r--fs/overlayfs/readdir.c79
-rw-r--r--fs/overlayfs/super.c27
2 files changed, 50 insertions, 56 deletions
diff --git a/fs/overlayfs/readdir.c b/fs/overlayfs/readdir.c
index dfef6ca53dfe..9df848f2e622 100644
--- a/fs/overlayfs/readdir.c
+++ b/fs/overlayfs/readdir.c
@@ -80,23 +80,50 @@ static struct ovl_cache_entry *ovl_cache_entry_find(struct rb_root *root,
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)
{
struct ovl_cache_entry *p;
size_t size = offsetof(struct ovl_cache_entry, name[len + 1]);
p = kmalloc(size, GFP_KERNEL);
- if (p) {
- 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 (!p)
+ return NULL;
+
+ 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;
}
@@ -123,36 +150,10 @@ static int ovl_cache_entry_add_rb(struct ovl_readdir_data *rdd,
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)
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);
rb_link_node(&p->node, parent, newp);
rb_insert_color(&p->node, &rdd->root);
@@ -170,7 +171,7 @@ static int ovl_fill_lower(struct ovl_readdir_data *rdd,
if (p) {
list_move_tail(&p->l_node, &rdd->middle);
} 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)
rdd->err = -ENOMEM;
else
@@ -229,6 +230,7 @@ static inline int ovl_dir_read(struct path *realpath,
if (IS_ERR(realfile))
return PTR_ERR(realfile);
+ rdd->dir = realpath->dentry;
rdd->ctx.pos = 0;
do {
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);
if (next != -1) {
- rdd.dir = realpath.dentry;
err = ovl_dir_read(&realpath, &rdd);
if (err)
break;
diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c
index f72b82fdc1e6..5dbc6789fd5f 100644
--- a/fs/overlayfs/super.c
+++ b/fs/overlayfs/super.c
@@ -350,16 +350,12 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
if (IS_ERR(this))
goto out;
- /*
- * If this is not the lowermost layer, check whiteout and opaque
- * directory.
- */
- if (poe->numlower && this) {
+ if (this) {
if (ovl_is_whiteout(this)) {
dput(this);
this = NULL;
upperopaque = true;
- } else if (ovl_is_opaquedir(this)) {
+ } else if (poe->numlower && ovl_is_opaquedir(this)) {
upperopaque = true;
}
}
@@ -384,19 +380,16 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
goto out_put;
if (!this)
continue;
-
+ if (ovl_is_whiteout(this)) {
+ dput(this);
+ break;
+ }
/*
- * If this is not the lowermost layer, check whiteout and opaque
- * directory.
+ * Only makes sense to check opaque dir if this is not the
+ * lowermost layer.
*/
- if (i < poe->numlower - 1) {
- if (ovl_is_whiteout(this)) {
- dput(this);
- break;
- } else if (ovl_is_opaquedir(this)) {
- opaque = true;
- }
- }
+ if (i < poe->numlower - 1 && ovl_is_opaquedir(this))
+ opaque = true;
/*
* If this is a non-directory then stop here.
*