diff options
author | Miklos Szeredi <mszeredi@suse.cz> | 2014-12-12 18:59:45 -0500 |
---|---|---|
committer | Miklos Szeredi <mszeredi@suse.cz> | 2014-12-12 18:59:45 -0500 |
commit | 3e01cee3b980f96463cb6f378ab05303a99903d9 (patch) | |
tree | 59ad7b720642aeeaa4c6706703125b8332b44454 | |
parent | 3d3c6b89399a1b5e8a59ffbb8cb2a7797a9ef154 (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.c | 79 | ||||
-rw-r--r-- | fs/overlayfs/super.c | 27 |
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 | ||
83 | static struct ovl_cache_entry *ovl_cache_entry_new(const char *name, int len, | 83 | static 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 | * |