diff options
Diffstat (limited to 'fs/overlayfs/dir.c')
-rw-r--r-- | fs/overlayfs/dir.c | 928 |
1 files changed, 928 insertions, 0 deletions
diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c new file mode 100644 index 000000000000..8ffc4b980f1b --- /dev/null +++ b/fs/overlayfs/dir.c | |||
@@ -0,0 +1,928 @@ | |||
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/namei.h> | ||
12 | #include <linux/xattr.h> | ||
13 | #include <linux/security.h> | ||
14 | #include <linux/cred.h> | ||
15 | #include "overlayfs.h" | ||
16 | |||
17 | void ovl_cleanup(struct inode *wdir, struct dentry *wdentry) | ||
18 | { | ||
19 | int err; | ||
20 | |||
21 | dget(wdentry); | ||
22 | if (S_ISDIR(wdentry->d_inode->i_mode)) | ||
23 | err = ovl_do_rmdir(wdir, wdentry); | ||
24 | else | ||
25 | err = ovl_do_unlink(wdir, wdentry); | ||
26 | dput(wdentry); | ||
27 | |||
28 | if (err) { | ||
29 | pr_err("overlayfs: cleanup of '%pd2' failed (%i)\n", | ||
30 | wdentry, err); | ||
31 | } | ||
32 | } | ||
33 | |||
34 | struct dentry *ovl_lookup_temp(struct dentry *workdir, struct dentry *dentry) | ||
35 | { | ||
36 | struct dentry *temp; | ||
37 | char name[20]; | ||
38 | |||
39 | snprintf(name, sizeof(name), "#%lx", (unsigned long) dentry); | ||
40 | |||
41 | temp = lookup_one_len(name, workdir, strlen(name)); | ||
42 | if (!IS_ERR(temp) && temp->d_inode) { | ||
43 | pr_err("overlayfs: workdir/%s already exists\n", name); | ||
44 | dput(temp); | ||
45 | temp = ERR_PTR(-EIO); | ||
46 | } | ||
47 | |||
48 | return temp; | ||
49 | } | ||
50 | |||
51 | /* caller holds i_mutex on workdir */ | ||
52 | static struct dentry *ovl_whiteout(struct dentry *workdir, | ||
53 | struct dentry *dentry) | ||
54 | { | ||
55 | int err; | ||
56 | struct dentry *whiteout; | ||
57 | struct inode *wdir = workdir->d_inode; | ||
58 | |||
59 | whiteout = ovl_lookup_temp(workdir, dentry); | ||
60 | if (IS_ERR(whiteout)) | ||
61 | return whiteout; | ||
62 | |||
63 | err = ovl_do_whiteout(wdir, whiteout); | ||
64 | if (err) { | ||
65 | dput(whiteout); | ||
66 | whiteout = ERR_PTR(err); | ||
67 | } | ||
68 | |||
69 | return whiteout; | ||
70 | } | ||
71 | |||
72 | int ovl_create_real(struct inode *dir, struct dentry *newdentry, | ||
73 | struct kstat *stat, const char *link, | ||
74 | struct dentry *hardlink, bool debug) | ||
75 | { | ||
76 | int err; | ||
77 | |||
78 | if (newdentry->d_inode) | ||
79 | return -ESTALE; | ||
80 | |||
81 | if (hardlink) { | ||
82 | err = ovl_do_link(hardlink, dir, newdentry, debug); | ||
83 | } else { | ||
84 | switch (stat->mode & S_IFMT) { | ||
85 | case S_IFREG: | ||
86 | err = ovl_do_create(dir, newdentry, stat->mode, debug); | ||
87 | break; | ||
88 | |||
89 | case S_IFDIR: | ||
90 | err = ovl_do_mkdir(dir, newdentry, stat->mode, debug); | ||
91 | break; | ||
92 | |||
93 | case S_IFCHR: | ||
94 | case S_IFBLK: | ||
95 | case S_IFIFO: | ||
96 | case S_IFSOCK: | ||
97 | err = ovl_do_mknod(dir, newdentry, | ||
98 | stat->mode, stat->rdev, debug); | ||
99 | break; | ||
100 | |||
101 | case S_IFLNK: | ||
102 | err = ovl_do_symlink(dir, newdentry, link, debug); | ||
103 | break; | ||
104 | |||
105 | default: | ||
106 | err = -EPERM; | ||
107 | } | ||
108 | } | ||
109 | if (!err && WARN_ON(!newdentry->d_inode)) { | ||
110 | /* | ||
111 | * Not quite sure if non-instantiated dentry is legal or not. | ||
112 | * VFS doesn't seem to care so check and warn here. | ||
113 | */ | ||
114 | err = -ENOENT; | ||
115 | } | ||
116 | return err; | ||
117 | } | ||
118 | |||
119 | static int ovl_set_opaque(struct dentry *upperdentry) | ||
120 | { | ||
121 | return ovl_do_setxattr(upperdentry, ovl_opaque_xattr, "y", 1, 0); | ||
122 | } | ||
123 | |||
124 | static void ovl_remove_opaque(struct dentry *upperdentry) | ||
125 | { | ||
126 | int err; | ||
127 | |||
128 | err = ovl_do_removexattr(upperdentry, ovl_opaque_xattr); | ||
129 | if (err) { | ||
130 | pr_warn("overlayfs: failed to remove opaque from '%s' (%i)\n", | ||
131 | upperdentry->d_name.name, err); | ||
132 | } | ||
133 | } | ||
134 | |||
135 | static int ovl_dir_getattr(struct vfsmount *mnt, struct dentry *dentry, | ||
136 | struct kstat *stat) | ||
137 | { | ||
138 | int err; | ||
139 | enum ovl_path_type type; | ||
140 | struct path realpath; | ||
141 | |||
142 | type = ovl_path_real(dentry, &realpath); | ||
143 | err = vfs_getattr(&realpath, stat); | ||
144 | if (err) | ||
145 | return err; | ||
146 | |||
147 | stat->dev = dentry->d_sb->s_dev; | ||
148 | stat->ino = dentry->d_inode->i_ino; | ||
149 | |||
150 | /* | ||
151 | * It's probably not worth it to count subdirs to get the | ||
152 | * correct link count. nlink=1 seems to pacify 'find' and | ||
153 | * other utilities. | ||
154 | */ | ||
155 | if (type == OVL_PATH_MERGE) | ||
156 | stat->nlink = 1; | ||
157 | |||
158 | return 0; | ||
159 | } | ||
160 | |||
161 | static int ovl_create_upper(struct dentry *dentry, struct inode *inode, | ||
162 | struct kstat *stat, const char *link, | ||
163 | struct dentry *hardlink) | ||
164 | { | ||
165 | struct dentry *upperdir = ovl_dentry_upper(dentry->d_parent); | ||
166 | struct inode *udir = upperdir->d_inode; | ||
167 | struct dentry *newdentry; | ||
168 | int err; | ||
169 | |||
170 | mutex_lock_nested(&udir->i_mutex, I_MUTEX_PARENT); | ||
171 | newdentry = lookup_one_len(dentry->d_name.name, upperdir, | ||
172 | dentry->d_name.len); | ||
173 | err = PTR_ERR(newdentry); | ||
174 | if (IS_ERR(newdentry)) | ||
175 | goto out_unlock; | ||
176 | err = ovl_create_real(udir, newdentry, stat, link, hardlink, false); | ||
177 | if (err) | ||
178 | goto out_dput; | ||
179 | |||
180 | ovl_dentry_version_inc(dentry->d_parent); | ||
181 | ovl_dentry_update(dentry, newdentry); | ||
182 | ovl_copyattr(newdentry->d_inode, inode); | ||
183 | d_instantiate(dentry, inode); | ||
184 | newdentry = NULL; | ||
185 | out_dput: | ||
186 | dput(newdentry); | ||
187 | out_unlock: | ||
188 | mutex_unlock(&udir->i_mutex); | ||
189 | return err; | ||
190 | } | ||
191 | |||
192 | static int ovl_lock_rename_workdir(struct dentry *workdir, | ||
193 | struct dentry *upperdir) | ||
194 | { | ||
195 | /* Workdir should not be the same as upperdir */ | ||
196 | if (workdir == upperdir) | ||
197 | goto err; | ||
198 | |||
199 | /* Workdir should not be subdir of upperdir and vice versa */ | ||
200 | if (lock_rename(workdir, upperdir) != NULL) | ||
201 | goto err_unlock; | ||
202 | |||
203 | return 0; | ||
204 | |||
205 | err_unlock: | ||
206 | unlock_rename(workdir, upperdir); | ||
207 | err: | ||
208 | pr_err("overlayfs: failed to lock workdir+upperdir\n"); | ||
209 | return -EIO; | ||
210 | } | ||
211 | |||
212 | static struct dentry *ovl_clear_empty(struct dentry *dentry, | ||
213 | struct list_head *list) | ||
214 | { | ||
215 | struct dentry *workdir = ovl_workdir(dentry); | ||
216 | struct inode *wdir = workdir->d_inode; | ||
217 | struct dentry *upperdir = ovl_dentry_upper(dentry->d_parent); | ||
218 | struct inode *udir = upperdir->d_inode; | ||
219 | struct path upperpath; | ||
220 | struct dentry *upper; | ||
221 | struct dentry *opaquedir; | ||
222 | struct kstat stat; | ||
223 | int err; | ||
224 | |||
225 | err = ovl_lock_rename_workdir(workdir, upperdir); | ||
226 | if (err) | ||
227 | goto out; | ||
228 | |||
229 | ovl_path_upper(dentry, &upperpath); | ||
230 | err = vfs_getattr(&upperpath, &stat); | ||
231 | if (err) | ||
232 | goto out_unlock; | ||
233 | |||
234 | err = -ESTALE; | ||
235 | if (!S_ISDIR(stat.mode)) | ||
236 | goto out_unlock; | ||
237 | upper = upperpath.dentry; | ||
238 | if (upper->d_parent->d_inode != udir) | ||
239 | goto out_unlock; | ||
240 | |||
241 | opaquedir = ovl_lookup_temp(workdir, dentry); | ||
242 | err = PTR_ERR(opaquedir); | ||
243 | if (IS_ERR(opaquedir)) | ||
244 | goto out_unlock; | ||
245 | |||
246 | err = ovl_create_real(wdir, opaquedir, &stat, NULL, NULL, true); | ||
247 | if (err) | ||
248 | goto out_dput; | ||
249 | |||
250 | err = ovl_copy_xattr(upper, opaquedir); | ||
251 | if (err) | ||
252 | goto out_cleanup; | ||
253 | |||
254 | err = ovl_set_opaque(opaquedir); | ||
255 | if (err) | ||
256 | goto out_cleanup; | ||
257 | |||
258 | mutex_lock(&opaquedir->d_inode->i_mutex); | ||
259 | err = ovl_set_attr(opaquedir, &stat); | ||
260 | mutex_unlock(&opaquedir->d_inode->i_mutex); | ||
261 | if (err) | ||
262 | goto out_cleanup; | ||
263 | |||
264 | err = ovl_do_rename(wdir, opaquedir, udir, upper, RENAME_EXCHANGE); | ||
265 | if (err) | ||
266 | goto out_cleanup; | ||
267 | |||
268 | ovl_cleanup_whiteouts(upper, list); | ||
269 | ovl_cleanup(wdir, upper); | ||
270 | unlock_rename(workdir, upperdir); | ||
271 | |||
272 | /* dentry's upper doesn't match now, get rid of it */ | ||
273 | d_drop(dentry); | ||
274 | |||
275 | return opaquedir; | ||
276 | |||
277 | out_cleanup: | ||
278 | ovl_cleanup(wdir, opaquedir); | ||
279 | out_dput: | ||
280 | dput(opaquedir); | ||
281 | out_unlock: | ||
282 | unlock_rename(workdir, upperdir); | ||
283 | out: | ||
284 | return ERR_PTR(err); | ||
285 | } | ||
286 | |||
287 | static struct dentry *ovl_check_empty_and_clear(struct dentry *dentry) | ||
288 | { | ||
289 | int err; | ||
290 | struct dentry *ret = NULL; | ||
291 | LIST_HEAD(list); | ||
292 | |||
293 | err = ovl_check_empty_dir(dentry, &list); | ||
294 | if (err) | ||
295 | ret = ERR_PTR(err); | ||
296 | else { | ||
297 | /* | ||
298 | * If no upperdentry then skip clearing whiteouts. | ||
299 | * | ||
300 | * Can race with copy-up, since we don't hold the upperdir | ||
301 | * mutex. Doesn't matter, since copy-up can't create a | ||
302 | * non-empty directory from an empty one. | ||
303 | */ | ||
304 | if (ovl_dentry_upper(dentry)) | ||
305 | ret = ovl_clear_empty(dentry, &list); | ||
306 | } | ||
307 | |||
308 | ovl_cache_free(&list); | ||
309 | |||
310 | return ret; | ||
311 | } | ||
312 | |||
313 | static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode, | ||
314 | struct kstat *stat, const char *link, | ||
315 | struct dentry *hardlink) | ||
316 | { | ||
317 | struct dentry *workdir = ovl_workdir(dentry); | ||
318 | struct inode *wdir = workdir->d_inode; | ||
319 | struct dentry *upperdir = ovl_dentry_upper(dentry->d_parent); | ||
320 | struct inode *udir = upperdir->d_inode; | ||
321 | struct dentry *upper; | ||
322 | struct dentry *newdentry; | ||
323 | int err; | ||
324 | |||
325 | err = ovl_lock_rename_workdir(workdir, upperdir); | ||
326 | if (err) | ||
327 | goto out; | ||
328 | |||
329 | newdentry = ovl_lookup_temp(workdir, dentry); | ||
330 | err = PTR_ERR(newdentry); | ||
331 | if (IS_ERR(newdentry)) | ||
332 | goto out_unlock; | ||
333 | |||
334 | upper = lookup_one_len(dentry->d_name.name, upperdir, | ||
335 | dentry->d_name.len); | ||
336 | err = PTR_ERR(upper); | ||
337 | if (IS_ERR(upper)) | ||
338 | goto out_dput; | ||
339 | |||
340 | err = ovl_create_real(wdir, newdentry, stat, link, hardlink, true); | ||
341 | if (err) | ||
342 | goto out_dput2; | ||
343 | |||
344 | if (S_ISDIR(stat->mode)) { | ||
345 | err = ovl_set_opaque(newdentry); | ||
346 | if (err) | ||
347 | goto out_cleanup; | ||
348 | |||
349 | err = ovl_do_rename(wdir, newdentry, udir, upper, | ||
350 | RENAME_EXCHANGE); | ||
351 | if (err) | ||
352 | goto out_cleanup; | ||
353 | |||
354 | ovl_cleanup(wdir, upper); | ||
355 | } else { | ||
356 | err = ovl_do_rename(wdir, newdentry, udir, upper, 0); | ||
357 | if (err) | ||
358 | goto out_cleanup; | ||
359 | } | ||
360 | ovl_dentry_version_inc(dentry->d_parent); | ||
361 | ovl_dentry_update(dentry, newdentry); | ||
362 | ovl_copyattr(newdentry->d_inode, inode); | ||
363 | d_instantiate(dentry, inode); | ||
364 | newdentry = NULL; | ||
365 | out_dput2: | ||
366 | dput(upper); | ||
367 | out_dput: | ||
368 | dput(newdentry); | ||
369 | out_unlock: | ||
370 | unlock_rename(workdir, upperdir); | ||
371 | out: | ||
372 | return err; | ||
373 | |||
374 | out_cleanup: | ||
375 | ovl_cleanup(wdir, newdentry); | ||
376 | goto out_dput2; | ||
377 | } | ||
378 | |||
379 | static int ovl_create_or_link(struct dentry *dentry, int mode, dev_t rdev, | ||
380 | const char *link, struct dentry *hardlink) | ||
381 | { | ||
382 | int err; | ||
383 | struct inode *inode; | ||
384 | struct kstat stat = { | ||
385 | .mode = mode, | ||
386 | .rdev = rdev, | ||
387 | }; | ||
388 | |||
389 | err = -ENOMEM; | ||
390 | inode = ovl_new_inode(dentry->d_sb, mode, dentry->d_fsdata); | ||
391 | if (!inode) | ||
392 | goto out; | ||
393 | |||
394 | err = ovl_copy_up(dentry->d_parent); | ||
395 | if (err) | ||
396 | goto out_iput; | ||
397 | |||
398 | if (!ovl_dentry_is_opaque(dentry)) { | ||
399 | err = ovl_create_upper(dentry, inode, &stat, link, hardlink); | ||
400 | } else { | ||
401 | const struct cred *old_cred; | ||
402 | struct cred *override_cred; | ||
403 | |||
404 | err = -ENOMEM; | ||
405 | override_cred = prepare_creds(); | ||
406 | if (!override_cred) | ||
407 | goto out_iput; | ||
408 | |||
409 | /* | ||
410 | * CAP_SYS_ADMIN for setting opaque xattr | ||
411 | * CAP_DAC_OVERRIDE for create in workdir, rename | ||
412 | * CAP_FOWNER for removing whiteout from sticky dir | ||
413 | */ | ||
414 | cap_raise(override_cred->cap_effective, CAP_SYS_ADMIN); | ||
415 | cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE); | ||
416 | cap_raise(override_cred->cap_effective, CAP_FOWNER); | ||
417 | old_cred = override_creds(override_cred); | ||
418 | |||
419 | err = ovl_create_over_whiteout(dentry, inode, &stat, link, | ||
420 | hardlink); | ||
421 | |||
422 | revert_creds(old_cred); | ||
423 | put_cred(override_cred); | ||
424 | } | ||
425 | |||
426 | if (!err) | ||
427 | inode = NULL; | ||
428 | out_iput: | ||
429 | iput(inode); | ||
430 | out: | ||
431 | return err; | ||
432 | } | ||
433 | |||
434 | static int ovl_create_object(struct dentry *dentry, int mode, dev_t rdev, | ||
435 | const char *link) | ||
436 | { | ||
437 | int err; | ||
438 | |||
439 | err = ovl_want_write(dentry); | ||
440 | if (!err) { | ||
441 | err = ovl_create_or_link(dentry, mode, rdev, link, NULL); | ||
442 | ovl_drop_write(dentry); | ||
443 | } | ||
444 | |||
445 | return err; | ||
446 | } | ||
447 | |||
448 | static int ovl_create(struct inode *dir, struct dentry *dentry, umode_t mode, | ||
449 | bool excl) | ||
450 | { | ||
451 | return ovl_create_object(dentry, (mode & 07777) | S_IFREG, 0, NULL); | ||
452 | } | ||
453 | |||
454 | static int ovl_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) | ||
455 | { | ||
456 | return ovl_create_object(dentry, (mode & 07777) | S_IFDIR, 0, NULL); | ||
457 | } | ||
458 | |||
459 | static int ovl_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, | ||
460 | dev_t rdev) | ||
461 | { | ||
462 | /* Don't allow creation of "whiteout" on overlay */ | ||
463 | if (S_ISCHR(mode) && rdev == WHITEOUT_DEV) | ||
464 | return -EPERM; | ||
465 | |||
466 | return ovl_create_object(dentry, mode, rdev, NULL); | ||
467 | } | ||
468 | |||
469 | static int ovl_symlink(struct inode *dir, struct dentry *dentry, | ||
470 | const char *link) | ||
471 | { | ||
472 | return ovl_create_object(dentry, S_IFLNK, 0, link); | ||
473 | } | ||
474 | |||
475 | static int ovl_link(struct dentry *old, struct inode *newdir, | ||
476 | struct dentry *new) | ||
477 | { | ||
478 | int err; | ||
479 | struct dentry *upper; | ||
480 | |||
481 | err = ovl_want_write(old); | ||
482 | if (err) | ||
483 | goto out; | ||
484 | |||
485 | err = ovl_copy_up(old); | ||
486 | if (err) | ||
487 | goto out_drop_write; | ||
488 | |||
489 | upper = ovl_dentry_upper(old); | ||
490 | err = ovl_create_or_link(new, upper->d_inode->i_mode, 0, NULL, upper); | ||
491 | |||
492 | out_drop_write: | ||
493 | ovl_drop_write(old); | ||
494 | out: | ||
495 | return err; | ||
496 | } | ||
497 | |||
498 | static int ovl_remove_and_whiteout(struct dentry *dentry, bool is_dir) | ||
499 | { | ||
500 | struct dentry *workdir = ovl_workdir(dentry); | ||
501 | struct inode *wdir = workdir->d_inode; | ||
502 | struct dentry *upperdir = ovl_dentry_upper(dentry->d_parent); | ||
503 | struct inode *udir = upperdir->d_inode; | ||
504 | struct dentry *whiteout; | ||
505 | struct dentry *upper; | ||
506 | struct dentry *opaquedir = NULL; | ||
507 | int err; | ||
508 | |||
509 | if (is_dir) { | ||
510 | opaquedir = ovl_check_empty_and_clear(dentry); | ||
511 | err = PTR_ERR(opaquedir); | ||
512 | if (IS_ERR(opaquedir)) | ||
513 | goto out; | ||
514 | } | ||
515 | |||
516 | err = ovl_lock_rename_workdir(workdir, upperdir); | ||
517 | if (err) | ||
518 | goto out_dput; | ||
519 | |||
520 | whiteout = ovl_whiteout(workdir, dentry); | ||
521 | err = PTR_ERR(whiteout); | ||
522 | if (IS_ERR(whiteout)) | ||
523 | goto out_unlock; | ||
524 | |||
525 | upper = ovl_dentry_upper(dentry); | ||
526 | if (!upper) { | ||
527 | upper = lookup_one_len(dentry->d_name.name, upperdir, | ||
528 | dentry->d_name.len); | ||
529 | err = PTR_ERR(upper); | ||
530 | if (IS_ERR(upper)) | ||
531 | goto kill_whiteout; | ||
532 | |||
533 | err = ovl_do_rename(wdir, whiteout, udir, upper, 0); | ||
534 | dput(upper); | ||
535 | if (err) | ||
536 | goto kill_whiteout; | ||
537 | } else { | ||
538 | int flags = 0; | ||
539 | |||
540 | if (opaquedir) | ||
541 | upper = opaquedir; | ||
542 | err = -ESTALE; | ||
543 | if (upper->d_parent != upperdir) | ||
544 | goto kill_whiteout; | ||
545 | |||
546 | if (is_dir) | ||
547 | flags |= RENAME_EXCHANGE; | ||
548 | |||
549 | err = ovl_do_rename(wdir, whiteout, udir, upper, flags); | ||
550 | if (err) | ||
551 | goto kill_whiteout; | ||
552 | |||
553 | if (is_dir) | ||
554 | ovl_cleanup(wdir, upper); | ||
555 | } | ||
556 | ovl_dentry_version_inc(dentry->d_parent); | ||
557 | out_d_drop: | ||
558 | d_drop(dentry); | ||
559 | dput(whiteout); | ||
560 | out_unlock: | ||
561 | unlock_rename(workdir, upperdir); | ||
562 | out_dput: | ||
563 | dput(opaquedir); | ||
564 | out: | ||
565 | return err; | ||
566 | |||
567 | kill_whiteout: | ||
568 | ovl_cleanup(wdir, whiteout); | ||
569 | goto out_d_drop; | ||
570 | } | ||
571 | |||
572 | static int ovl_remove_upper(struct dentry *dentry, bool is_dir) | ||
573 | { | ||
574 | struct dentry *upperdir = ovl_dentry_upper(dentry->d_parent); | ||
575 | struct inode *dir = upperdir->d_inode; | ||
576 | struct dentry *upper = ovl_dentry_upper(dentry); | ||
577 | int err; | ||
578 | |||
579 | mutex_lock_nested(&dir->i_mutex, I_MUTEX_PARENT); | ||
580 | err = -ESTALE; | ||
581 | if (upper->d_parent == upperdir) { | ||
582 | /* Don't let d_delete() think it can reset d_inode */ | ||
583 | dget(upper); | ||
584 | if (is_dir) | ||
585 | err = vfs_rmdir(dir, upper); | ||
586 | else | ||
587 | err = vfs_unlink(dir, upper, NULL); | ||
588 | dput(upper); | ||
589 | ovl_dentry_version_inc(dentry->d_parent); | ||
590 | } | ||
591 | |||
592 | /* | ||
593 | * Keeping this dentry hashed would mean having to release | ||
594 | * upperpath/lowerpath, which could only be done if we are the | ||
595 | * sole user of this dentry. Too tricky... Just unhash for | ||
596 | * now. | ||
597 | */ | ||
598 | d_drop(dentry); | ||
599 | mutex_unlock(&dir->i_mutex); | ||
600 | |||
601 | return err; | ||
602 | } | ||
603 | |||
604 | static inline int ovl_check_sticky(struct dentry *dentry) | ||
605 | { | ||
606 | struct inode *dir = ovl_dentry_real(dentry->d_parent)->d_inode; | ||
607 | struct inode *inode = ovl_dentry_real(dentry)->d_inode; | ||
608 | |||
609 | if (check_sticky(dir, inode)) | ||
610 | return -EPERM; | ||
611 | |||
612 | return 0; | ||
613 | } | ||
614 | |||
615 | static int ovl_do_remove(struct dentry *dentry, bool is_dir) | ||
616 | { | ||
617 | enum ovl_path_type type; | ||
618 | int err; | ||
619 | |||
620 | err = ovl_check_sticky(dentry); | ||
621 | if (err) | ||
622 | goto out; | ||
623 | |||
624 | err = ovl_want_write(dentry); | ||
625 | if (err) | ||
626 | goto out; | ||
627 | |||
628 | err = ovl_copy_up(dentry->d_parent); | ||
629 | if (err) | ||
630 | goto out_drop_write; | ||
631 | |||
632 | type = ovl_path_type(dentry); | ||
633 | if (type == OVL_PATH_PURE_UPPER) { | ||
634 | err = ovl_remove_upper(dentry, is_dir); | ||
635 | } else { | ||
636 | const struct cred *old_cred; | ||
637 | struct cred *override_cred; | ||
638 | |||
639 | err = -ENOMEM; | ||
640 | override_cred = prepare_creds(); | ||
641 | if (!override_cred) | ||
642 | goto out_drop_write; | ||
643 | |||
644 | /* | ||
645 | * CAP_SYS_ADMIN for setting xattr on whiteout, opaque dir | ||
646 | * CAP_DAC_OVERRIDE for create in workdir, rename | ||
647 | * CAP_FOWNER for removing whiteout from sticky dir | ||
648 | * CAP_FSETID for chmod of opaque dir | ||
649 | * CAP_CHOWN for chown of opaque dir | ||
650 | */ | ||
651 | cap_raise(override_cred->cap_effective, CAP_SYS_ADMIN); | ||
652 | cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE); | ||
653 | cap_raise(override_cred->cap_effective, CAP_FOWNER); | ||
654 | cap_raise(override_cred->cap_effective, CAP_FSETID); | ||
655 | cap_raise(override_cred->cap_effective, CAP_CHOWN); | ||
656 | old_cred = override_creds(override_cred); | ||
657 | |||
658 | err = ovl_remove_and_whiteout(dentry, is_dir); | ||
659 | |||
660 | revert_creds(old_cred); | ||
661 | put_cred(override_cred); | ||
662 | } | ||
663 | out_drop_write: | ||
664 | ovl_drop_write(dentry); | ||
665 | out: | ||
666 | return err; | ||
667 | } | ||
668 | |||
669 | static int ovl_unlink(struct inode *dir, struct dentry *dentry) | ||
670 | { | ||
671 | return ovl_do_remove(dentry, false); | ||
672 | } | ||
673 | |||
674 | static int ovl_rmdir(struct inode *dir, struct dentry *dentry) | ||
675 | { | ||
676 | return ovl_do_remove(dentry, true); | ||
677 | } | ||
678 | |||
679 | static int ovl_rename2(struct inode *olddir, struct dentry *old, | ||
680 | struct inode *newdir, struct dentry *new, | ||
681 | unsigned int flags) | ||
682 | { | ||
683 | int err; | ||
684 | enum ovl_path_type old_type; | ||
685 | enum ovl_path_type new_type; | ||
686 | struct dentry *old_upperdir; | ||
687 | struct dentry *new_upperdir; | ||
688 | struct dentry *olddentry; | ||
689 | struct dentry *newdentry; | ||
690 | struct dentry *trap; | ||
691 | bool old_opaque; | ||
692 | bool new_opaque; | ||
693 | bool new_create = false; | ||
694 | bool cleanup_whiteout = false; | ||
695 | bool overwrite = !(flags & RENAME_EXCHANGE); | ||
696 | bool is_dir = S_ISDIR(old->d_inode->i_mode); | ||
697 | bool new_is_dir = false; | ||
698 | struct dentry *opaquedir = NULL; | ||
699 | const struct cred *old_cred = NULL; | ||
700 | struct cred *override_cred = NULL; | ||
701 | |||
702 | err = -EINVAL; | ||
703 | if (flags & ~(RENAME_EXCHANGE | RENAME_NOREPLACE)) | ||
704 | goto out; | ||
705 | |||
706 | flags &= ~RENAME_NOREPLACE; | ||
707 | |||
708 | err = ovl_check_sticky(old); | ||
709 | if (err) | ||
710 | goto out; | ||
711 | |||
712 | /* Don't copy up directory trees */ | ||
713 | old_type = ovl_path_type(old); | ||
714 | err = -EXDEV; | ||
715 | if ((old_type == OVL_PATH_LOWER || old_type == OVL_PATH_MERGE) && is_dir) | ||
716 | goto out; | ||
717 | |||
718 | if (new->d_inode) { | ||
719 | err = ovl_check_sticky(new); | ||
720 | if (err) | ||
721 | goto out; | ||
722 | |||
723 | if (S_ISDIR(new->d_inode->i_mode)) | ||
724 | new_is_dir = true; | ||
725 | |||
726 | new_type = ovl_path_type(new); | ||
727 | err = -EXDEV; | ||
728 | if (!overwrite && (new_type == OVL_PATH_LOWER || new_type == OVL_PATH_MERGE) && new_is_dir) | ||
729 | goto out; | ||
730 | |||
731 | err = 0; | ||
732 | if (new_type == OVL_PATH_LOWER && old_type == OVL_PATH_LOWER) { | ||
733 | if (ovl_dentry_lower(old)->d_inode == | ||
734 | ovl_dentry_lower(new)->d_inode) | ||
735 | goto out; | ||
736 | } | ||
737 | if (new_type != OVL_PATH_LOWER && old_type != OVL_PATH_LOWER) { | ||
738 | if (ovl_dentry_upper(old)->d_inode == | ||
739 | ovl_dentry_upper(new)->d_inode) | ||
740 | goto out; | ||
741 | } | ||
742 | } else { | ||
743 | if (ovl_dentry_is_opaque(new)) | ||
744 | new_type = OVL_PATH_UPPER; | ||
745 | else | ||
746 | new_type = OVL_PATH_PURE_UPPER; | ||
747 | } | ||
748 | |||
749 | err = ovl_want_write(old); | ||
750 | if (err) | ||
751 | goto out; | ||
752 | |||
753 | err = ovl_copy_up(old); | ||
754 | if (err) | ||
755 | goto out_drop_write; | ||
756 | |||
757 | err = ovl_copy_up(new->d_parent); | ||
758 | if (err) | ||
759 | goto out_drop_write; | ||
760 | if (!overwrite) { | ||
761 | err = ovl_copy_up(new); | ||
762 | if (err) | ||
763 | goto out_drop_write; | ||
764 | } | ||
765 | |||
766 | old_opaque = old_type != OVL_PATH_PURE_UPPER; | ||
767 | new_opaque = new_type != OVL_PATH_PURE_UPPER; | ||
768 | |||
769 | if (old_opaque || new_opaque) { | ||
770 | err = -ENOMEM; | ||
771 | override_cred = prepare_creds(); | ||
772 | if (!override_cred) | ||
773 | goto out_drop_write; | ||
774 | |||
775 | /* | ||
776 | * CAP_SYS_ADMIN for setting xattr on whiteout, opaque dir | ||
777 | * CAP_DAC_OVERRIDE for create in workdir | ||
778 | * CAP_FOWNER for removing whiteout from sticky dir | ||
779 | * CAP_FSETID for chmod of opaque dir | ||
780 | * CAP_CHOWN for chown of opaque dir | ||
781 | */ | ||
782 | cap_raise(override_cred->cap_effective, CAP_SYS_ADMIN); | ||
783 | cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE); | ||
784 | cap_raise(override_cred->cap_effective, CAP_FOWNER); | ||
785 | cap_raise(override_cred->cap_effective, CAP_FSETID); | ||
786 | cap_raise(override_cred->cap_effective, CAP_CHOWN); | ||
787 | old_cred = override_creds(override_cred); | ||
788 | } | ||
789 | |||
790 | if (overwrite && (new_type == OVL_PATH_LOWER || new_type == OVL_PATH_MERGE) && new_is_dir) { | ||
791 | opaquedir = ovl_check_empty_and_clear(new); | ||
792 | err = PTR_ERR(opaquedir); | ||
793 | if (IS_ERR(opaquedir)) { | ||
794 | opaquedir = NULL; | ||
795 | goto out_revert_creds; | ||
796 | } | ||
797 | } | ||
798 | |||
799 | if (overwrite) { | ||
800 | if (old_opaque) { | ||
801 | if (new->d_inode || !new_opaque) { | ||
802 | /* Whiteout source */ | ||
803 | flags |= RENAME_WHITEOUT; | ||
804 | } else { | ||
805 | /* Switch whiteouts */ | ||
806 | flags |= RENAME_EXCHANGE; | ||
807 | } | ||
808 | } else if (is_dir && !new->d_inode && new_opaque) { | ||
809 | flags |= RENAME_EXCHANGE; | ||
810 | cleanup_whiteout = true; | ||
811 | } | ||
812 | } | ||
813 | |||
814 | old_upperdir = ovl_dentry_upper(old->d_parent); | ||
815 | new_upperdir = ovl_dentry_upper(new->d_parent); | ||
816 | |||
817 | trap = lock_rename(new_upperdir, old_upperdir); | ||
818 | |||
819 | olddentry = ovl_dentry_upper(old); | ||
820 | newdentry = ovl_dentry_upper(new); | ||
821 | if (newdentry) { | ||
822 | if (opaquedir) { | ||
823 | newdentry = opaquedir; | ||
824 | opaquedir = NULL; | ||
825 | } else { | ||
826 | dget(newdentry); | ||
827 | } | ||
828 | } else { | ||
829 | new_create = true; | ||
830 | newdentry = lookup_one_len(new->d_name.name, new_upperdir, | ||
831 | new->d_name.len); | ||
832 | err = PTR_ERR(newdentry); | ||
833 | if (IS_ERR(newdentry)) | ||
834 | goto out_unlock; | ||
835 | } | ||
836 | |||
837 | err = -ESTALE; | ||
838 | if (olddentry->d_parent != old_upperdir) | ||
839 | goto out_dput; | ||
840 | if (newdentry->d_parent != new_upperdir) | ||
841 | goto out_dput; | ||
842 | if (olddentry == trap) | ||
843 | goto out_dput; | ||
844 | if (newdentry == trap) | ||
845 | goto out_dput; | ||
846 | |||
847 | if (is_dir && !old_opaque && new_opaque) { | ||
848 | err = ovl_set_opaque(olddentry); | ||
849 | if (err) | ||
850 | goto out_dput; | ||
851 | } | ||
852 | if (!overwrite && new_is_dir && old_opaque && !new_opaque) { | ||
853 | err = ovl_set_opaque(newdentry); | ||
854 | if (err) | ||
855 | goto out_dput; | ||
856 | } | ||
857 | |||
858 | if (old_opaque || new_opaque) { | ||
859 | err = ovl_do_rename(old_upperdir->d_inode, olddentry, | ||
860 | new_upperdir->d_inode, newdentry, | ||
861 | flags); | ||
862 | } else { | ||
863 | /* No debug for the plain case */ | ||
864 | BUG_ON(flags & ~RENAME_EXCHANGE); | ||
865 | err = vfs_rename(old_upperdir->d_inode, olddentry, | ||
866 | new_upperdir->d_inode, newdentry, | ||
867 | NULL, flags); | ||
868 | } | ||
869 | |||
870 | if (err) { | ||
871 | if (is_dir && !old_opaque && new_opaque) | ||
872 | ovl_remove_opaque(olddentry); | ||
873 | if (!overwrite && new_is_dir && old_opaque && !new_opaque) | ||
874 | ovl_remove_opaque(newdentry); | ||
875 | goto out_dput; | ||
876 | } | ||
877 | |||
878 | if (is_dir && old_opaque && !new_opaque) | ||
879 | ovl_remove_opaque(olddentry); | ||
880 | if (!overwrite && new_is_dir && !old_opaque && new_opaque) | ||
881 | ovl_remove_opaque(newdentry); | ||
882 | |||
883 | if (old_opaque != new_opaque) { | ||
884 | ovl_dentry_set_opaque(old, new_opaque); | ||
885 | if (!overwrite) | ||
886 | ovl_dentry_set_opaque(new, old_opaque); | ||
887 | } | ||
888 | |||
889 | if (cleanup_whiteout) | ||
890 | ovl_cleanup(old_upperdir->d_inode, newdentry); | ||
891 | |||
892 | ovl_dentry_version_inc(old->d_parent); | ||
893 | ovl_dentry_version_inc(new->d_parent); | ||
894 | |||
895 | out_dput: | ||
896 | dput(newdentry); | ||
897 | out_unlock: | ||
898 | unlock_rename(new_upperdir, old_upperdir); | ||
899 | out_revert_creds: | ||
900 | if (old_opaque || new_opaque) { | ||
901 | revert_creds(old_cred); | ||
902 | put_cred(override_cred); | ||
903 | } | ||
904 | out_drop_write: | ||
905 | ovl_drop_write(old); | ||
906 | out: | ||
907 | dput(opaquedir); | ||
908 | return err; | ||
909 | } | ||
910 | |||
911 | const struct inode_operations ovl_dir_inode_operations = { | ||
912 | .lookup = ovl_lookup, | ||
913 | .mkdir = ovl_mkdir, | ||
914 | .symlink = ovl_symlink, | ||
915 | .unlink = ovl_unlink, | ||
916 | .rmdir = ovl_rmdir, | ||
917 | .rename2 = ovl_rename2, | ||
918 | .link = ovl_link, | ||
919 | .setattr = ovl_setattr, | ||
920 | .create = ovl_create, | ||
921 | .mknod = ovl_mknod, | ||
922 | .permission = ovl_permission, | ||
923 | .getattr = ovl_dir_getattr, | ||
924 | .setxattr = ovl_setxattr, | ||
925 | .getxattr = ovl_getxattr, | ||
926 | .listxattr = ovl_listxattr, | ||
927 | .removexattr = ovl_removexattr, | ||
928 | }; | ||