diff options
Diffstat (limited to 'fs/overlayfs/readdir.c')
-rw-r--r-- | fs/overlayfs/readdir.c | 590 |
1 files changed, 590 insertions, 0 deletions
diff --git a/fs/overlayfs/readdir.c b/fs/overlayfs/readdir.c new file mode 100644 index 000000000000..910553f37aca --- /dev/null +++ b/fs/overlayfs/readdir.c | |||
@@ -0,0 +1,590 @@ | |||
1 | /* | ||
2 | * | ||
3 | * Copyright (C) 2011 Novell Inc. | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of the GNU General Public License version 2 as published by | ||
7 | * the Free Software Foundation. | ||
8 | */ | ||
9 | |||
10 | #include <linux/fs.h> | ||
11 | #include <linux/slab.h> | ||
12 | #include <linux/namei.h> | ||
13 | #include <linux/file.h> | ||
14 | #include <linux/xattr.h> | ||
15 | #include <linux/rbtree.h> | ||
16 | #include <linux/security.h> | ||
17 | #include <linux/cred.h> | ||
18 | #include "overlayfs.h" | ||
19 | |||
20 | struct ovl_cache_entry { | ||
21 | unsigned int len; | ||
22 | unsigned int type; | ||
23 | u64 ino; | ||
24 | bool is_whiteout; | ||
25 | struct list_head l_node; | ||
26 | struct rb_node node; | ||
27 | char name[]; | ||
28 | }; | ||
29 | |||
30 | struct ovl_dir_cache { | ||
31 | long refcount; | ||
32 | u64 version; | ||
33 | struct list_head entries; | ||
34 | }; | ||
35 | |||
36 | struct ovl_readdir_data { | ||
37 | struct dir_context ctx; | ||
38 | bool is_merge; | ||
39 | struct rb_root root; | ||
40 | struct list_head *list; | ||
41 | struct list_head middle; | ||
42 | int count; | ||
43 | int err; | ||
44 | }; | ||
45 | |||
46 | struct ovl_dir_file { | ||
47 | bool is_real; | ||
48 | bool is_upper; | ||
49 | struct ovl_dir_cache *cache; | ||
50 | struct ovl_cache_entry cursor; | ||
51 | struct file *realfile; | ||
52 | struct file *upperfile; | ||
53 | }; | ||
54 | |||
55 | static struct ovl_cache_entry *ovl_cache_entry_from_node(struct rb_node *n) | ||
56 | { | ||
57 | return container_of(n, struct ovl_cache_entry, node); | ||
58 | } | ||
59 | |||
60 | static struct ovl_cache_entry *ovl_cache_entry_find(struct rb_root *root, | ||
61 | const char *name, int len) | ||
62 | { | ||
63 | struct rb_node *node = root->rb_node; | ||
64 | int cmp; | ||
65 | |||
66 | while (node) { | ||
67 | struct ovl_cache_entry *p = ovl_cache_entry_from_node(node); | ||
68 | |||
69 | cmp = strncmp(name, p->name, len); | ||
70 | if (cmp > 0) | ||
71 | node = p->node.rb_right; | ||
72 | else if (cmp < 0 || len < p->len) | ||
73 | node = p->node.rb_left; | ||
74 | else | ||
75 | return p; | ||
76 | } | ||
77 | |||
78 | return NULL; | ||
79 | } | ||
80 | |||
81 | static struct ovl_cache_entry *ovl_cache_entry_new(const char *name, int len, | ||
82 | u64 ino, unsigned int d_type) | ||
83 | { | ||
84 | struct ovl_cache_entry *p; | ||
85 | size_t size = offsetof(struct ovl_cache_entry, name[len + 1]); | ||
86 | |||
87 | p = kmalloc(size, GFP_KERNEL); | ||
88 | if (p) { | ||
89 | memcpy(p->name, name, len); | ||
90 | p->name[len] = '\0'; | ||
91 | p->len = len; | ||
92 | p->type = d_type; | ||
93 | p->ino = ino; | ||
94 | p->is_whiteout = false; | ||
95 | } | ||
96 | |||
97 | return p; | ||
98 | } | ||
99 | |||
100 | static int ovl_cache_entry_add_rb(struct ovl_readdir_data *rdd, | ||
101 | const char *name, int len, u64 ino, | ||
102 | unsigned int d_type) | ||
103 | { | ||
104 | struct rb_node **newp = &rdd->root.rb_node; | ||
105 | struct rb_node *parent = NULL; | ||
106 | struct ovl_cache_entry *p; | ||
107 | |||
108 | while (*newp) { | ||
109 | int cmp; | ||
110 | struct ovl_cache_entry *tmp; | ||
111 | |||
112 | parent = *newp; | ||
113 | tmp = ovl_cache_entry_from_node(*newp); | ||
114 | cmp = strncmp(name, tmp->name, len); | ||
115 | if (cmp > 0) | ||
116 | newp = &tmp->node.rb_right; | ||
117 | else if (cmp < 0 || len < tmp->len) | ||
118 | newp = &tmp->node.rb_left; | ||
119 | else | ||
120 | return 0; | ||
121 | } | ||
122 | |||
123 | p = ovl_cache_entry_new(name, len, ino, d_type); | ||
124 | if (p == NULL) | ||
125 | return -ENOMEM; | ||
126 | |||
127 | list_add_tail(&p->l_node, rdd->list); | ||
128 | rb_link_node(&p->node, parent, newp); | ||
129 | rb_insert_color(&p->node, &rdd->root); | ||
130 | |||
131 | return 0; | ||
132 | } | ||
133 | |||
134 | static int ovl_fill_lower(struct ovl_readdir_data *rdd, | ||
135 | const char *name, int namelen, | ||
136 | loff_t offset, u64 ino, unsigned int d_type) | ||
137 | { | ||
138 | struct ovl_cache_entry *p; | ||
139 | |||
140 | p = ovl_cache_entry_find(&rdd->root, name, namelen); | ||
141 | if (p) { | ||
142 | list_move_tail(&p->l_node, &rdd->middle); | ||
143 | } else { | ||
144 | p = ovl_cache_entry_new(name, namelen, ino, d_type); | ||
145 | if (p == NULL) | ||
146 | rdd->err = -ENOMEM; | ||
147 | else | ||
148 | list_add_tail(&p->l_node, &rdd->middle); | ||
149 | } | ||
150 | |||
151 | return rdd->err; | ||
152 | } | ||
153 | |||
154 | void ovl_cache_free(struct list_head *list) | ||
155 | { | ||
156 | struct ovl_cache_entry *p; | ||
157 | struct ovl_cache_entry *n; | ||
158 | |||
159 | list_for_each_entry_safe(p, n, list, l_node) | ||
160 | kfree(p); | ||
161 | |||
162 | INIT_LIST_HEAD(list); | ||
163 | } | ||
164 | |||
165 | static void ovl_cache_put(struct ovl_dir_file *od, struct dentry *dentry) | ||
166 | { | ||
167 | struct ovl_dir_cache *cache = od->cache; | ||
168 | |||
169 | list_del(&od->cursor.l_node); | ||
170 | WARN_ON(cache->refcount <= 0); | ||
171 | cache->refcount--; | ||
172 | if (!cache->refcount) { | ||
173 | if (ovl_dir_cache(dentry) == cache) | ||
174 | ovl_set_dir_cache(dentry, NULL); | ||
175 | |||
176 | ovl_cache_free(&cache->entries); | ||
177 | kfree(cache); | ||
178 | } | ||
179 | } | ||
180 | |||
181 | static int ovl_fill_merge(void *buf, const char *name, int namelen, | ||
182 | loff_t offset, u64 ino, unsigned int d_type) | ||
183 | { | ||
184 | struct ovl_readdir_data *rdd = buf; | ||
185 | |||
186 | rdd->count++; | ||
187 | if (!rdd->is_merge) | ||
188 | return ovl_cache_entry_add_rb(rdd, name, namelen, ino, d_type); | ||
189 | else | ||
190 | return ovl_fill_lower(rdd, name, namelen, offset, ino, d_type); | ||
191 | } | ||
192 | |||
193 | static inline int ovl_dir_read(struct path *realpath, | ||
194 | struct ovl_readdir_data *rdd) | ||
195 | { | ||
196 | struct file *realfile; | ||
197 | int err; | ||
198 | |||
199 | realfile = ovl_path_open(realpath, O_RDONLY | O_DIRECTORY); | ||
200 | if (IS_ERR(realfile)) | ||
201 | return PTR_ERR(realfile); | ||
202 | |||
203 | rdd->ctx.pos = 0; | ||
204 | do { | ||
205 | rdd->count = 0; | ||
206 | rdd->err = 0; | ||
207 | err = iterate_dir(realfile, &rdd->ctx); | ||
208 | if (err >= 0) | ||
209 | err = rdd->err; | ||
210 | } while (!err && rdd->count); | ||
211 | fput(realfile); | ||
212 | |||
213 | return err; | ||
214 | } | ||
215 | |||
216 | static void ovl_dir_reset(struct file *file) | ||
217 | { | ||
218 | struct ovl_dir_file *od = file->private_data; | ||
219 | struct ovl_dir_cache *cache = od->cache; | ||
220 | struct dentry *dentry = file->f_path.dentry; | ||
221 | enum ovl_path_type type = ovl_path_type(dentry); | ||
222 | |||
223 | if (cache && ovl_dentry_version_get(dentry) != cache->version) { | ||
224 | ovl_cache_put(od, dentry); | ||
225 | od->cache = NULL; | ||
226 | } | ||
227 | WARN_ON(!od->is_real && type != OVL_PATH_MERGE); | ||
228 | if (od->is_real && type == OVL_PATH_MERGE) | ||
229 | od->is_real = false; | ||
230 | } | ||
231 | |||
232 | static int ovl_dir_mark_whiteouts(struct dentry *dir, | ||
233 | struct ovl_readdir_data *rdd) | ||
234 | { | ||
235 | struct ovl_cache_entry *p; | ||
236 | struct dentry *dentry; | ||
237 | const struct cred *old_cred; | ||
238 | struct cred *override_cred; | ||
239 | |||
240 | override_cred = prepare_creds(); | ||
241 | if (!override_cred) { | ||
242 | ovl_cache_free(rdd->list); | ||
243 | return -ENOMEM; | ||
244 | } | ||
245 | |||
246 | /* | ||
247 | * CAP_DAC_OVERRIDE for lookup | ||
248 | */ | ||
249 | cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE); | ||
250 | old_cred = override_creds(override_cred); | ||
251 | |||
252 | mutex_lock(&dir->d_inode->i_mutex); | ||
253 | list_for_each_entry(p, rdd->list, l_node) { | ||
254 | if (!p->name) | ||
255 | continue; | ||
256 | |||
257 | if (p->type != DT_CHR) | ||
258 | continue; | ||
259 | |||
260 | dentry = lookup_one_len(p->name, dir, p->len); | ||
261 | if (IS_ERR(dentry)) | ||
262 | continue; | ||
263 | |||
264 | p->is_whiteout = ovl_is_whiteout(dentry); | ||
265 | dput(dentry); | ||
266 | } | ||
267 | mutex_unlock(&dir->d_inode->i_mutex); | ||
268 | |||
269 | revert_creds(old_cred); | ||
270 | put_cred(override_cred); | ||
271 | |||
272 | return 0; | ||
273 | } | ||
274 | |||
275 | static inline int ovl_dir_read_merged(struct path *upperpath, | ||
276 | struct path *lowerpath, | ||
277 | struct list_head *list) | ||
278 | { | ||
279 | int err; | ||
280 | struct ovl_readdir_data rdd = { | ||
281 | .ctx.actor = ovl_fill_merge, | ||
282 | .list = list, | ||
283 | .root = RB_ROOT, | ||
284 | .is_merge = false, | ||
285 | }; | ||
286 | |||
287 | if (upperpath->dentry) { | ||
288 | err = ovl_dir_read(upperpath, &rdd); | ||
289 | if (err) | ||
290 | goto out; | ||
291 | |||
292 | if (lowerpath->dentry) { | ||
293 | err = ovl_dir_mark_whiteouts(upperpath->dentry, &rdd); | ||
294 | if (err) | ||
295 | goto out; | ||
296 | } | ||
297 | } | ||
298 | if (lowerpath->dentry) { | ||
299 | /* | ||
300 | * Insert lowerpath entries before upperpath ones, this allows | ||
301 | * offsets to be reasonably constant | ||
302 | */ | ||
303 | list_add(&rdd.middle, rdd.list); | ||
304 | rdd.is_merge = true; | ||
305 | err = ovl_dir_read(lowerpath, &rdd); | ||
306 | list_del(&rdd.middle); | ||
307 | } | ||
308 | out: | ||
309 | return err; | ||
310 | |||
311 | } | ||
312 | |||
313 | static void ovl_seek_cursor(struct ovl_dir_file *od, loff_t pos) | ||
314 | { | ||
315 | struct ovl_cache_entry *p; | ||
316 | loff_t off = 0; | ||
317 | |||
318 | list_for_each_entry(p, &od->cache->entries, l_node) { | ||
319 | if (!p->name) | ||
320 | continue; | ||
321 | if (off >= pos) | ||
322 | break; | ||
323 | off++; | ||
324 | } | ||
325 | list_move_tail(&od->cursor.l_node, &p->l_node); | ||
326 | } | ||
327 | |||
328 | static struct ovl_dir_cache *ovl_cache_get(struct dentry *dentry) | ||
329 | { | ||
330 | int res; | ||
331 | struct path lowerpath; | ||
332 | struct path upperpath; | ||
333 | struct ovl_dir_cache *cache; | ||
334 | |||
335 | cache = ovl_dir_cache(dentry); | ||
336 | if (cache && ovl_dentry_version_get(dentry) == cache->version) { | ||
337 | cache->refcount++; | ||
338 | return cache; | ||
339 | } | ||
340 | ovl_set_dir_cache(dentry, NULL); | ||
341 | |||
342 | cache = kzalloc(sizeof(struct ovl_dir_cache), GFP_KERNEL); | ||
343 | if (!cache) | ||
344 | return ERR_PTR(-ENOMEM); | ||
345 | |||
346 | cache->refcount = 1; | ||
347 | INIT_LIST_HEAD(&cache->entries); | ||
348 | |||
349 | ovl_path_lower(dentry, &lowerpath); | ||
350 | ovl_path_upper(dentry, &upperpath); | ||
351 | |||
352 | res = ovl_dir_read_merged(&upperpath, &lowerpath, &cache->entries); | ||
353 | if (res) { | ||
354 | ovl_cache_free(&cache->entries); | ||
355 | kfree(cache); | ||
356 | return ERR_PTR(res); | ||
357 | } | ||
358 | |||
359 | cache->version = ovl_dentry_version_get(dentry); | ||
360 | ovl_set_dir_cache(dentry, cache); | ||
361 | |||
362 | return cache; | ||
363 | } | ||
364 | |||
365 | static int ovl_iterate(struct file *file, struct dir_context *ctx) | ||
366 | { | ||
367 | struct ovl_dir_file *od = file->private_data; | ||
368 | struct dentry *dentry = file->f_path.dentry; | ||
369 | |||
370 | if (!ctx->pos) | ||
371 | ovl_dir_reset(file); | ||
372 | |||
373 | if (od->is_real) | ||
374 | return iterate_dir(od->realfile, ctx); | ||
375 | |||
376 | if (!od->cache) { | ||
377 | struct ovl_dir_cache *cache; | ||
378 | |||
379 | cache = ovl_cache_get(dentry); | ||
380 | if (IS_ERR(cache)) | ||
381 | return PTR_ERR(cache); | ||
382 | |||
383 | od->cache = cache; | ||
384 | ovl_seek_cursor(od, ctx->pos); | ||
385 | } | ||
386 | |||
387 | while (od->cursor.l_node.next != &od->cache->entries) { | ||
388 | struct ovl_cache_entry *p; | ||
389 | |||
390 | p = list_entry(od->cursor.l_node.next, struct ovl_cache_entry, l_node); | ||
391 | /* Skip cursors */ | ||
392 | if (p->name) { | ||
393 | if (!p->is_whiteout) { | ||
394 | if (!dir_emit(ctx, p->name, p->len, p->ino, p->type)) | ||
395 | break; | ||
396 | } | ||
397 | ctx->pos++; | ||
398 | } | ||
399 | list_move(&od->cursor.l_node, &p->l_node); | ||
400 | } | ||
401 | return 0; | ||
402 | } | ||
403 | |||
404 | static loff_t ovl_dir_llseek(struct file *file, loff_t offset, int origin) | ||
405 | { | ||
406 | loff_t res; | ||
407 | struct ovl_dir_file *od = file->private_data; | ||
408 | |||
409 | mutex_lock(&file_inode(file)->i_mutex); | ||
410 | if (!file->f_pos) | ||
411 | ovl_dir_reset(file); | ||
412 | |||
413 | if (od->is_real) { | ||
414 | res = vfs_llseek(od->realfile, offset, origin); | ||
415 | file->f_pos = od->realfile->f_pos; | ||
416 | } else { | ||
417 | res = -EINVAL; | ||
418 | |||
419 | switch (origin) { | ||
420 | case SEEK_CUR: | ||
421 | offset += file->f_pos; | ||
422 | break; | ||
423 | case SEEK_SET: | ||
424 | break; | ||
425 | default: | ||
426 | goto out_unlock; | ||
427 | } | ||
428 | if (offset < 0) | ||
429 | goto out_unlock; | ||
430 | |||
431 | if (offset != file->f_pos) { | ||
432 | file->f_pos = offset; | ||
433 | if (od->cache) | ||
434 | ovl_seek_cursor(od, offset); | ||
435 | } | ||
436 | res = offset; | ||
437 | } | ||
438 | out_unlock: | ||
439 | mutex_unlock(&file_inode(file)->i_mutex); | ||
440 | |||
441 | return res; | ||
442 | } | ||
443 | |||
444 | static int ovl_dir_fsync(struct file *file, loff_t start, loff_t end, | ||
445 | int datasync) | ||
446 | { | ||
447 | struct ovl_dir_file *od = file->private_data; | ||
448 | struct dentry *dentry = file->f_path.dentry; | ||
449 | struct file *realfile = od->realfile; | ||
450 | |||
451 | /* | ||
452 | * Need to check if we started out being a lower dir, but got copied up | ||
453 | */ | ||
454 | if (!od->is_upper && ovl_path_type(dentry) == OVL_PATH_MERGE) { | ||
455 | struct inode *inode = file_inode(file); | ||
456 | |||
457 | realfile = od->upperfile; | ||
458 | if (!realfile) { | ||
459 | struct path upperpath; | ||
460 | |||
461 | ovl_path_upper(dentry, &upperpath); | ||
462 | realfile = ovl_path_open(&upperpath, O_RDONLY); | ||
463 | mutex_lock(&inode->i_mutex); | ||
464 | if (!od->upperfile) { | ||
465 | if (IS_ERR(realfile)) { | ||
466 | mutex_unlock(&inode->i_mutex); | ||
467 | return PTR_ERR(realfile); | ||
468 | } | ||
469 | od->upperfile = realfile; | ||
470 | } else { | ||
471 | /* somebody has beaten us to it */ | ||
472 | if (!IS_ERR(realfile)) | ||
473 | fput(realfile); | ||
474 | realfile = od->upperfile; | ||
475 | } | ||
476 | mutex_unlock(&inode->i_mutex); | ||
477 | } | ||
478 | } | ||
479 | |||
480 | return vfs_fsync_range(realfile, start, end, datasync); | ||
481 | } | ||
482 | |||
483 | static int ovl_dir_release(struct inode *inode, struct file *file) | ||
484 | { | ||
485 | struct ovl_dir_file *od = file->private_data; | ||
486 | |||
487 | if (od->cache) { | ||
488 | mutex_lock(&inode->i_mutex); | ||
489 | ovl_cache_put(od, file->f_path.dentry); | ||
490 | mutex_unlock(&inode->i_mutex); | ||
491 | } | ||
492 | fput(od->realfile); | ||
493 | if (od->upperfile) | ||
494 | fput(od->upperfile); | ||
495 | kfree(od); | ||
496 | |||
497 | return 0; | ||
498 | } | ||
499 | |||
500 | static int ovl_dir_open(struct inode *inode, struct file *file) | ||
501 | { | ||
502 | struct path realpath; | ||
503 | struct file *realfile; | ||
504 | struct ovl_dir_file *od; | ||
505 | enum ovl_path_type type; | ||
506 | |||
507 | od = kzalloc(sizeof(struct ovl_dir_file), GFP_KERNEL); | ||
508 | if (!od) | ||
509 | return -ENOMEM; | ||
510 | |||
511 | type = ovl_path_real(file->f_path.dentry, &realpath); | ||
512 | realfile = ovl_path_open(&realpath, file->f_flags); | ||
513 | if (IS_ERR(realfile)) { | ||
514 | kfree(od); | ||
515 | return PTR_ERR(realfile); | ||
516 | } | ||
517 | INIT_LIST_HEAD(&od->cursor.l_node); | ||
518 | od->realfile = realfile; | ||
519 | od->is_real = (type != OVL_PATH_MERGE); | ||
520 | od->is_upper = (type != OVL_PATH_LOWER); | ||
521 | file->private_data = od; | ||
522 | |||
523 | return 0; | ||
524 | } | ||
525 | |||
526 | const struct file_operations ovl_dir_operations = { | ||
527 | .read = generic_read_dir, | ||
528 | .open = ovl_dir_open, | ||
529 | .iterate = ovl_iterate, | ||
530 | .llseek = ovl_dir_llseek, | ||
531 | .fsync = ovl_dir_fsync, | ||
532 | .release = ovl_dir_release, | ||
533 | }; | ||
534 | |||
535 | int ovl_check_empty_dir(struct dentry *dentry, struct list_head *list) | ||
536 | { | ||
537 | int err; | ||
538 | struct path lowerpath; | ||
539 | struct path upperpath; | ||
540 | struct ovl_cache_entry *p; | ||
541 | |||
542 | ovl_path_upper(dentry, &upperpath); | ||
543 | ovl_path_lower(dentry, &lowerpath); | ||
544 | |||
545 | err = ovl_dir_read_merged(&upperpath, &lowerpath, list); | ||
546 | if (err) | ||
547 | return err; | ||
548 | |||
549 | err = 0; | ||
550 | |||
551 | list_for_each_entry(p, list, l_node) { | ||
552 | if (p->is_whiteout) | ||
553 | continue; | ||
554 | |||
555 | if (p->name[0] == '.') { | ||
556 | if (p->len == 1) | ||
557 | continue; | ||
558 | if (p->len == 2 && p->name[1] == '.') | ||
559 | continue; | ||
560 | } | ||
561 | err = -ENOTEMPTY; | ||
562 | break; | ||
563 | } | ||
564 | |||
565 | return err; | ||
566 | } | ||
567 | |||
568 | void ovl_cleanup_whiteouts(struct dentry *upper, struct list_head *list) | ||
569 | { | ||
570 | struct ovl_cache_entry *p; | ||
571 | |||
572 | mutex_lock_nested(&upper->d_inode->i_mutex, I_MUTEX_PARENT); | ||
573 | list_for_each_entry(p, list, l_node) { | ||
574 | struct dentry *dentry; | ||
575 | |||
576 | if (!p->is_whiteout) | ||
577 | continue; | ||
578 | |||
579 | dentry = lookup_one_len(p->name, upper, p->len); | ||
580 | if (IS_ERR(dentry)) { | ||
581 | pr_err("overlayfs: lookup '%s/%.*s' failed (%i)\n", | ||
582 | upper->d_name.name, p->len, p->name, | ||
583 | (int) PTR_ERR(dentry)); | ||
584 | continue; | ||
585 | } | ||
586 | ovl_cleanup(upper->d_inode, dentry); | ||
587 | dput(dentry); | ||
588 | } | ||
589 | mutex_unlock(&upper->d_inode->i_mutex); | ||
590 | } | ||