aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiklos Szeredi <mszeredi@suse.cz>2014-12-12 18:59:45 -0500
committerMiklos Szeredi <mszeredi@suse.cz>2014-12-12 18:59:45 -0500
commit3e01cee3b980f96463cb6f378ab05303a99903d9 (patch)
tree59ad7b720642aeeaa4c6706703125b8332b44454
parent3d3c6b89399a1b5e8a59ffbb8cb2a7797a9ef154 (diff)
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,
80 return NULL; 80 return NULL;
81} 81}
82 82
83static struct ovl_cache_entry *ovl_cache_entry_new(const char *name, int len, 83static struct ovl_cache_entry *ovl_cache_entry_new(struct dentry *dir,
84 const char *name, int len,
84 u64 ino, unsigned int d_type) 85 u64 ino, unsigned int d_type)
85{ 86{
86 struct ovl_cache_entry *p; 87 struct ovl_cache_entry *p;
87 size_t size = offsetof(struct ovl_cache_entry, name[len + 1]); 88 size_t size = offsetof(struct ovl_cache_entry, name[len + 1]);
88 89
89 p = kmalloc(size, GFP_KERNEL); 90 p = kmalloc(size, GFP_KERNEL);
90 if (p) { 91 if (!p)
91 memcpy(p->name, name, len); 92 return NULL;
92 p->name[len] = '\0'; 93
93 p->len = len; 94 memcpy(p->name, name, len);
94 p->type = d_type; 95 p->name[len] = '\0';
95 p->ino = ino; 96 p->len = len;
96 p->is_whiteout = false; 97 p->type = d_type;
97 p->is_cursor = false; 98 p->ino = ino;
98 } 99 p->is_whiteout = false;
100 p->is_cursor = false;
101
102 if (d_type == DT_CHR) {
103 struct dentry *dentry;
104 const struct cred *old_cred;
105 struct cred *override_cred;
106
107 override_cred = prepare_creds();
108 if (!override_cred) {
109 kfree(p);
110 return NULL;
111 }
99 112
113 /*
114 * CAP_DAC_OVERRIDE for lookup
115 */
116 cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE);
117 old_cred = override_creds(override_cred);
118
119 dentry = lookup_one_len(name, dir, len);
120 if (!IS_ERR(dentry)) {
121 p->is_whiteout = ovl_is_whiteout(dentry);
122 dput(dentry);
123 }
124 revert_creds(old_cred);
125 put_cred(override_cred);
126 }
100 return p; 127 return p;
101} 128}
102 129
@@ -123,36 +150,10 @@ static int ovl_cache_entry_add_rb(struct ovl_readdir_data *rdd,
123 return 0; 150 return 0;
124 } 151 }
125 152
126 p = ovl_cache_entry_new(name, len, ino, d_type); 153 p = ovl_cache_entry_new(rdd->dir, name, len, ino, d_type);
127 if (p == NULL) 154 if (p == NULL)
128 return -ENOMEM; 155 return -ENOMEM;
129 156
130 if (d_type == DT_CHR) {
131 struct dentry *dentry;
132 const struct cred *old_cred;
133 struct cred *override_cred;
134
135 override_cred = prepare_creds();
136 if (!override_cred) {
137 kfree(p);
138 return -ENOMEM;
139 }
140
141 /*
142 * CAP_DAC_OVERRIDE for lookup
143 */
144 cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE);
145 old_cred = override_creds(override_cred);
146
147 dentry = lookup_one_len(name, rdd->dir, len);
148 if (!IS_ERR(dentry)) {
149 p->is_whiteout = ovl_is_whiteout(dentry);
150 dput(dentry);
151 }
152 revert_creds(old_cred);
153 put_cred(override_cred);
154 }
155
156 list_add_tail(&p->l_node, rdd->list); 157 list_add_tail(&p->l_node, rdd->list);
157 rb_link_node(&p->node, parent, newp); 158 rb_link_node(&p->node, parent, newp);
158 rb_insert_color(&p->node, &rdd->root); 159 rb_insert_color(&p->node, &rdd->root);
@@ -170,7 +171,7 @@ static int ovl_fill_lower(struct ovl_readdir_data *rdd,
170 if (p) { 171 if (p) {
171 list_move_tail(&p->l_node, &rdd->middle); 172 list_move_tail(&p->l_node, &rdd->middle);
172 } else { 173 } else {
173 p = ovl_cache_entry_new(name, namelen, ino, d_type); 174 p = ovl_cache_entry_new(rdd->dir, name, namelen, ino, d_type);
174 if (p == NULL) 175 if (p == NULL)
175 rdd->err = -ENOMEM; 176 rdd->err = -ENOMEM;
176 else 177 else
@@ -229,6 +230,7 @@ static inline int ovl_dir_read(struct path *realpath,
229 if (IS_ERR(realfile)) 230 if (IS_ERR(realfile))
230 return PTR_ERR(realfile); 231 return PTR_ERR(realfile);
231 232
233 rdd->dir = realpath->dentry;
232 rdd->ctx.pos = 0; 234 rdd->ctx.pos = 0;
233 do { 235 do {
234 rdd->count = 0; 236 rdd->count = 0;
@@ -274,7 +276,6 @@ static int ovl_dir_read_merged(struct dentry *dentry, struct list_head *list)
274 next = ovl_path_next(idx, dentry, &realpath); 276 next = ovl_path_next(idx, dentry, &realpath);
275 277
276 if (next != -1) { 278 if (next != -1) {
277 rdd.dir = realpath.dentry;
278 err = ovl_dir_read(&realpath, &rdd); 279 err = ovl_dir_read(&realpath, &rdd);
279 if (err) 280 if (err)
280 break; 281 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,
350 if (IS_ERR(this)) 350 if (IS_ERR(this))
351 goto out; 351 goto out;
352 352
353 /* 353 if (this) {
354 * If this is not the lowermost layer, check whiteout and opaque
355 * directory.
356 */
357 if (poe->numlower && this) {
358 if (ovl_is_whiteout(this)) { 354 if (ovl_is_whiteout(this)) {
359 dput(this); 355 dput(this);
360 this = NULL; 356 this = NULL;
361 upperopaque = true; 357 upperopaque = true;
362 } else if (ovl_is_opaquedir(this)) { 358 } else if (poe->numlower && ovl_is_opaquedir(this)) {
363 upperopaque = true; 359 upperopaque = true;
364 } 360 }
365 } 361 }
@@ -384,19 +380,16 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
384 goto out_put; 380 goto out_put;
385 if (!this) 381 if (!this)
386 continue; 382 continue;
387 383 if (ovl_is_whiteout(this)) {
384 dput(this);
385 break;
386 }
388 /* 387 /*
389 * If this is not the lowermost layer, check whiteout and opaque 388 * Only makes sense to check opaque dir if this is not the
390 * directory. 389 * lowermost layer.
391 */ 390 */
392 if (i < poe->numlower - 1) { 391 if (i < poe->numlower - 1 && ovl_is_opaquedir(this))
393 if (ovl_is_whiteout(this)) { 392 opaque = true;
394 dput(this);
395 break;
396 } else if (ovl_is_opaquedir(this)) {
397 opaque = true;
398 }
399 }
400 /* 393 /*
401 * If this is a non-directory then stop here. 394 * If this is a non-directory then stop here.
402 * 395 *