diff options
Diffstat (limited to 'fs/overlayfs/dir.c')
-rw-r--r-- | fs/overlayfs/dir.c | 921 |
1 files changed, 921 insertions, 0 deletions
diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c new file mode 100644 index 000000000000..15cd91ad9940 --- /dev/null +++ b/fs/overlayfs/dir.c | |||
@@ -0,0 +1,921 @@ | |||
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 | enum ovl_path_type type) | ||
289 | { | ||
290 | int err; | ||
291 | struct dentry *ret = NULL; | ||
292 | LIST_HEAD(list); | ||
293 | |||
294 | err = ovl_check_empty_dir(dentry, &list); | ||
295 | if (err) | ||
296 | ret = ERR_PTR(err); | ||
297 | else if (type == OVL_PATH_MERGE) | ||
298 | ret = ovl_clear_empty(dentry, &list); | ||
299 | |||
300 | ovl_cache_free(&list); | ||
301 | |||
302 | return ret; | ||
303 | } | ||
304 | |||
305 | static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode, | ||
306 | struct kstat *stat, const char *link, | ||
307 | struct dentry *hardlink) | ||
308 | { | ||
309 | struct dentry *workdir = ovl_workdir(dentry); | ||
310 | struct inode *wdir = workdir->d_inode; | ||
311 | struct dentry *upperdir = ovl_dentry_upper(dentry->d_parent); | ||
312 | struct inode *udir = upperdir->d_inode; | ||
313 | struct dentry *upper; | ||
314 | struct dentry *newdentry; | ||
315 | int err; | ||
316 | |||
317 | err = ovl_lock_rename_workdir(workdir, upperdir); | ||
318 | if (err) | ||
319 | goto out; | ||
320 | |||
321 | newdentry = ovl_lookup_temp(workdir, dentry); | ||
322 | err = PTR_ERR(newdentry); | ||
323 | if (IS_ERR(newdentry)) | ||
324 | goto out_unlock; | ||
325 | |||
326 | upper = lookup_one_len(dentry->d_name.name, upperdir, | ||
327 | dentry->d_name.len); | ||
328 | err = PTR_ERR(upper); | ||
329 | if (IS_ERR(upper)) | ||
330 | goto out_dput; | ||
331 | |||
332 | err = ovl_create_real(wdir, newdentry, stat, link, hardlink, true); | ||
333 | if (err) | ||
334 | goto out_dput2; | ||
335 | |||
336 | if (S_ISDIR(stat->mode)) { | ||
337 | err = ovl_set_opaque(newdentry); | ||
338 | if (err) | ||
339 | goto out_cleanup; | ||
340 | |||
341 | err = ovl_do_rename(wdir, newdentry, udir, upper, | ||
342 | RENAME_EXCHANGE); | ||
343 | if (err) | ||
344 | goto out_cleanup; | ||
345 | |||
346 | ovl_cleanup(wdir, upper); | ||
347 | } else { | ||
348 | err = ovl_do_rename(wdir, newdentry, udir, upper, 0); | ||
349 | if (err) | ||
350 | goto out_cleanup; | ||
351 | } | ||
352 | ovl_dentry_version_inc(dentry->d_parent); | ||
353 | ovl_dentry_update(dentry, newdentry); | ||
354 | ovl_copyattr(newdentry->d_inode, inode); | ||
355 | d_instantiate(dentry, inode); | ||
356 | newdentry = NULL; | ||
357 | out_dput2: | ||
358 | dput(upper); | ||
359 | out_dput: | ||
360 | dput(newdentry); | ||
361 | out_unlock: | ||
362 | unlock_rename(workdir, upperdir); | ||
363 | out: | ||
364 | return err; | ||
365 | |||
366 | out_cleanup: | ||
367 | ovl_cleanup(wdir, newdentry); | ||
368 | goto out_dput2; | ||
369 | } | ||
370 | |||
371 | static int ovl_create_or_link(struct dentry *dentry, int mode, dev_t rdev, | ||
372 | const char *link, struct dentry *hardlink) | ||
373 | { | ||
374 | int err; | ||
375 | struct inode *inode; | ||
376 | struct kstat stat = { | ||
377 | .mode = mode, | ||
378 | .rdev = rdev, | ||
379 | }; | ||
380 | |||
381 | err = -ENOMEM; | ||
382 | inode = ovl_new_inode(dentry->d_sb, mode, dentry->d_fsdata); | ||
383 | if (!inode) | ||
384 | goto out; | ||
385 | |||
386 | err = ovl_copy_up(dentry->d_parent); | ||
387 | if (err) | ||
388 | goto out_iput; | ||
389 | |||
390 | if (!ovl_dentry_is_opaque(dentry)) { | ||
391 | err = ovl_create_upper(dentry, inode, &stat, link, hardlink); | ||
392 | } else { | ||
393 | const struct cred *old_cred; | ||
394 | struct cred *override_cred; | ||
395 | |||
396 | err = -ENOMEM; | ||
397 | override_cred = prepare_creds(); | ||
398 | if (!override_cred) | ||
399 | goto out_iput; | ||
400 | |||
401 | /* | ||
402 | * CAP_SYS_ADMIN for setting opaque xattr | ||
403 | * CAP_DAC_OVERRIDE for create in workdir, rename | ||
404 | * CAP_FOWNER for removing whiteout from sticky dir | ||
405 | */ | ||
406 | cap_raise(override_cred->cap_effective, CAP_SYS_ADMIN); | ||
407 | cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE); | ||
408 | cap_raise(override_cred->cap_effective, CAP_FOWNER); | ||
409 | old_cred = override_creds(override_cred); | ||
410 | |||
411 | err = ovl_create_over_whiteout(dentry, inode, &stat, link, | ||
412 | hardlink); | ||
413 | |||
414 | revert_creds(old_cred); | ||
415 | put_cred(override_cred); | ||
416 | } | ||
417 | |||
418 | if (!err) | ||
419 | inode = NULL; | ||
420 | out_iput: | ||
421 | iput(inode); | ||
422 | out: | ||
423 | return err; | ||
424 | } | ||
425 | |||
426 | static int ovl_create_object(struct dentry *dentry, int mode, dev_t rdev, | ||
427 | const char *link) | ||
428 | { | ||
429 | int err; | ||
430 | |||
431 | err = ovl_want_write(dentry); | ||
432 | if (!err) { | ||
433 | err = ovl_create_or_link(dentry, mode, rdev, link, NULL); | ||
434 | ovl_drop_write(dentry); | ||
435 | } | ||
436 | |||
437 | return err; | ||
438 | } | ||
439 | |||
440 | static int ovl_create(struct inode *dir, struct dentry *dentry, umode_t mode, | ||
441 | bool excl) | ||
442 | { | ||
443 | return ovl_create_object(dentry, (mode & 07777) | S_IFREG, 0, NULL); | ||
444 | } | ||
445 | |||
446 | static int ovl_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) | ||
447 | { | ||
448 | return ovl_create_object(dentry, (mode & 07777) | S_IFDIR, 0, NULL); | ||
449 | } | ||
450 | |||
451 | static int ovl_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, | ||
452 | dev_t rdev) | ||
453 | { | ||
454 | /* Don't allow creation of "whiteout" on overlay */ | ||
455 | if (S_ISCHR(mode) && rdev == WHITEOUT_DEV) | ||
456 | return -EPERM; | ||
457 | |||
458 | return ovl_create_object(dentry, mode, rdev, NULL); | ||
459 | } | ||
460 | |||
461 | static int ovl_symlink(struct inode *dir, struct dentry *dentry, | ||
462 | const char *link) | ||
463 | { | ||
464 | return ovl_create_object(dentry, S_IFLNK, 0, link); | ||
465 | } | ||
466 | |||
467 | static int ovl_link(struct dentry *old, struct inode *newdir, | ||
468 | struct dentry *new) | ||
469 | { | ||
470 | int err; | ||
471 | struct dentry *upper; | ||
472 | |||
473 | err = ovl_want_write(old); | ||
474 | if (err) | ||
475 | goto out; | ||
476 | |||
477 | err = ovl_copy_up(old); | ||
478 | if (err) | ||
479 | goto out_drop_write; | ||
480 | |||
481 | upper = ovl_dentry_upper(old); | ||
482 | err = ovl_create_or_link(new, upper->d_inode->i_mode, 0, NULL, upper); | ||
483 | |||
484 | out_drop_write: | ||
485 | ovl_drop_write(old); | ||
486 | out: | ||
487 | return err; | ||
488 | } | ||
489 | |||
490 | static int ovl_remove_and_whiteout(struct dentry *dentry, | ||
491 | enum ovl_path_type type, bool is_dir) | ||
492 | { | ||
493 | struct dentry *workdir = ovl_workdir(dentry); | ||
494 | struct inode *wdir = workdir->d_inode; | ||
495 | struct dentry *upperdir = ovl_dentry_upper(dentry->d_parent); | ||
496 | struct inode *udir = upperdir->d_inode; | ||
497 | struct dentry *whiteout; | ||
498 | struct dentry *upper; | ||
499 | struct dentry *opaquedir = NULL; | ||
500 | int err; | ||
501 | |||
502 | if (is_dir) { | ||
503 | opaquedir = ovl_check_empty_and_clear(dentry, type); | ||
504 | err = PTR_ERR(opaquedir); | ||
505 | if (IS_ERR(opaquedir)) | ||
506 | goto out; | ||
507 | } | ||
508 | |||
509 | err = ovl_lock_rename_workdir(workdir, upperdir); | ||
510 | if (err) | ||
511 | goto out_dput; | ||
512 | |||
513 | whiteout = ovl_whiteout(workdir, dentry); | ||
514 | err = PTR_ERR(whiteout); | ||
515 | if (IS_ERR(whiteout)) | ||
516 | goto out_unlock; | ||
517 | |||
518 | if (type == OVL_PATH_LOWER) { | ||
519 | upper = lookup_one_len(dentry->d_name.name, upperdir, | ||
520 | dentry->d_name.len); | ||
521 | err = PTR_ERR(upper); | ||
522 | if (IS_ERR(upper)) | ||
523 | goto kill_whiteout; | ||
524 | |||
525 | err = ovl_do_rename(wdir, whiteout, udir, upper, 0); | ||
526 | dput(upper); | ||
527 | if (err) | ||
528 | goto kill_whiteout; | ||
529 | } else { | ||
530 | int flags = 0; | ||
531 | |||
532 | upper = ovl_dentry_upper(dentry); | ||
533 | if (opaquedir) | ||
534 | upper = opaquedir; | ||
535 | err = -ESTALE; | ||
536 | if (upper->d_parent != upperdir) | ||
537 | goto kill_whiteout; | ||
538 | |||
539 | if (is_dir) | ||
540 | flags |= RENAME_EXCHANGE; | ||
541 | |||
542 | err = ovl_do_rename(wdir, whiteout, udir, upper, flags); | ||
543 | if (err) | ||
544 | goto kill_whiteout; | ||
545 | |||
546 | if (is_dir) | ||
547 | ovl_cleanup(wdir, upper); | ||
548 | } | ||
549 | ovl_dentry_version_inc(dentry->d_parent); | ||
550 | out_d_drop: | ||
551 | d_drop(dentry); | ||
552 | dput(whiteout); | ||
553 | out_unlock: | ||
554 | unlock_rename(workdir, upperdir); | ||
555 | out_dput: | ||
556 | dput(opaquedir); | ||
557 | out: | ||
558 | return err; | ||
559 | |||
560 | kill_whiteout: | ||
561 | ovl_cleanup(wdir, whiteout); | ||
562 | goto out_d_drop; | ||
563 | } | ||
564 | |||
565 | static int ovl_remove_upper(struct dentry *dentry, bool is_dir) | ||
566 | { | ||
567 | struct dentry *upperdir = ovl_dentry_upper(dentry->d_parent); | ||
568 | struct inode *dir = upperdir->d_inode; | ||
569 | struct dentry *upper = ovl_dentry_upper(dentry); | ||
570 | int err; | ||
571 | |||
572 | mutex_lock_nested(&dir->i_mutex, I_MUTEX_PARENT); | ||
573 | err = -ESTALE; | ||
574 | if (upper->d_parent == upperdir) { | ||
575 | /* Don't let d_delete() think it can reset d_inode */ | ||
576 | dget(upper); | ||
577 | if (is_dir) | ||
578 | err = vfs_rmdir(dir, upper); | ||
579 | else | ||
580 | err = vfs_unlink(dir, upper, NULL); | ||
581 | dput(upper); | ||
582 | ovl_dentry_version_inc(dentry->d_parent); | ||
583 | } | ||
584 | |||
585 | /* | ||
586 | * Keeping this dentry hashed would mean having to release | ||
587 | * upperpath/lowerpath, which could only be done if we are the | ||
588 | * sole user of this dentry. Too tricky... Just unhash for | ||
589 | * now. | ||
590 | */ | ||
591 | d_drop(dentry); | ||
592 | mutex_unlock(&dir->i_mutex); | ||
593 | |||
594 | return err; | ||
595 | } | ||
596 | |||
597 | static inline int ovl_check_sticky(struct dentry *dentry) | ||
598 | { | ||
599 | struct inode *dir = ovl_dentry_real(dentry->d_parent)->d_inode; | ||
600 | struct inode *inode = ovl_dentry_real(dentry)->d_inode; | ||
601 | |||
602 | if (check_sticky(dir, inode)) | ||
603 | return -EPERM; | ||
604 | |||
605 | return 0; | ||
606 | } | ||
607 | |||
608 | static int ovl_do_remove(struct dentry *dentry, bool is_dir) | ||
609 | { | ||
610 | enum ovl_path_type type; | ||
611 | int err; | ||
612 | |||
613 | err = ovl_check_sticky(dentry); | ||
614 | if (err) | ||
615 | goto out; | ||
616 | |||
617 | err = ovl_want_write(dentry); | ||
618 | if (err) | ||
619 | goto out; | ||
620 | |||
621 | err = ovl_copy_up(dentry->d_parent); | ||
622 | if (err) | ||
623 | goto out_drop_write; | ||
624 | |||
625 | type = ovl_path_type(dentry); | ||
626 | if (type == OVL_PATH_PURE_UPPER) { | ||
627 | err = ovl_remove_upper(dentry, is_dir); | ||
628 | } else { | ||
629 | const struct cred *old_cred; | ||
630 | struct cred *override_cred; | ||
631 | |||
632 | err = -ENOMEM; | ||
633 | override_cred = prepare_creds(); | ||
634 | if (!override_cred) | ||
635 | goto out_drop_write; | ||
636 | |||
637 | /* | ||
638 | * CAP_SYS_ADMIN for setting xattr on whiteout, opaque dir | ||
639 | * CAP_DAC_OVERRIDE for create in workdir, rename | ||
640 | * CAP_FOWNER for removing whiteout from sticky dir | ||
641 | * CAP_FSETID for chmod of opaque dir | ||
642 | * CAP_CHOWN for chown of opaque dir | ||
643 | */ | ||
644 | cap_raise(override_cred->cap_effective, CAP_SYS_ADMIN); | ||
645 | cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE); | ||
646 | cap_raise(override_cred->cap_effective, CAP_FOWNER); | ||
647 | cap_raise(override_cred->cap_effective, CAP_FSETID); | ||
648 | cap_raise(override_cred->cap_effective, CAP_CHOWN); | ||
649 | old_cred = override_creds(override_cred); | ||
650 | |||
651 | err = ovl_remove_and_whiteout(dentry, type, is_dir); | ||
652 | |||
653 | revert_creds(old_cred); | ||
654 | put_cred(override_cred); | ||
655 | } | ||
656 | out_drop_write: | ||
657 | ovl_drop_write(dentry); | ||
658 | out: | ||
659 | return err; | ||
660 | } | ||
661 | |||
662 | static int ovl_unlink(struct inode *dir, struct dentry *dentry) | ||
663 | { | ||
664 | return ovl_do_remove(dentry, false); | ||
665 | } | ||
666 | |||
667 | static int ovl_rmdir(struct inode *dir, struct dentry *dentry) | ||
668 | { | ||
669 | return ovl_do_remove(dentry, true); | ||
670 | } | ||
671 | |||
672 | static int ovl_rename2(struct inode *olddir, struct dentry *old, | ||
673 | struct inode *newdir, struct dentry *new, | ||
674 | unsigned int flags) | ||
675 | { | ||
676 | int err; | ||
677 | enum ovl_path_type old_type; | ||
678 | enum ovl_path_type new_type; | ||
679 | struct dentry *old_upperdir; | ||
680 | struct dentry *new_upperdir; | ||
681 | struct dentry *olddentry; | ||
682 | struct dentry *newdentry; | ||
683 | struct dentry *trap; | ||
684 | bool old_opaque; | ||
685 | bool new_opaque; | ||
686 | bool new_create = false; | ||
687 | bool cleanup_whiteout = false; | ||
688 | bool overwrite = !(flags & RENAME_EXCHANGE); | ||
689 | bool is_dir = S_ISDIR(old->d_inode->i_mode); | ||
690 | bool new_is_dir = false; | ||
691 | struct dentry *opaquedir = NULL; | ||
692 | const struct cred *old_cred = NULL; | ||
693 | struct cred *override_cred = NULL; | ||
694 | |||
695 | err = -EINVAL; | ||
696 | if (flags & ~(RENAME_EXCHANGE | RENAME_NOREPLACE)) | ||
697 | goto out; | ||
698 | |||
699 | flags &= ~RENAME_NOREPLACE; | ||
700 | |||
701 | err = ovl_check_sticky(old); | ||
702 | if (err) | ||
703 | goto out; | ||
704 | |||
705 | /* Don't copy up directory trees */ | ||
706 | old_type = ovl_path_type(old); | ||
707 | err = -EXDEV; | ||
708 | if ((old_type == OVL_PATH_LOWER || old_type == OVL_PATH_MERGE) && is_dir) | ||
709 | goto out; | ||
710 | |||
711 | if (new->d_inode) { | ||
712 | err = ovl_check_sticky(new); | ||
713 | if (err) | ||
714 | goto out; | ||
715 | |||
716 | if (S_ISDIR(new->d_inode->i_mode)) | ||
717 | new_is_dir = true; | ||
718 | |||
719 | new_type = ovl_path_type(new); | ||
720 | err = -EXDEV; | ||
721 | if (!overwrite && (new_type == OVL_PATH_LOWER || new_type == OVL_PATH_MERGE) && new_is_dir) | ||
722 | goto out; | ||
723 | |||
724 | err = 0; | ||
725 | if (new_type == OVL_PATH_LOWER && old_type == OVL_PATH_LOWER) { | ||
726 | if (ovl_dentry_lower(old)->d_inode == | ||
727 | ovl_dentry_lower(new)->d_inode) | ||
728 | goto out; | ||
729 | } | ||
730 | if (new_type != OVL_PATH_LOWER && old_type != OVL_PATH_LOWER) { | ||
731 | if (ovl_dentry_upper(old)->d_inode == | ||
732 | ovl_dentry_upper(new)->d_inode) | ||
733 | goto out; | ||
734 | } | ||
735 | } else { | ||
736 | if (ovl_dentry_is_opaque(new)) | ||
737 | new_type = OVL_PATH_UPPER; | ||
738 | else | ||
739 | new_type = OVL_PATH_PURE_UPPER; | ||
740 | } | ||
741 | |||
742 | err = ovl_want_write(old); | ||
743 | if (err) | ||
744 | goto out; | ||
745 | |||
746 | err = ovl_copy_up(old); | ||
747 | if (err) | ||
748 | goto out_drop_write; | ||
749 | |||
750 | err = ovl_copy_up(new->d_parent); | ||
751 | if (err) | ||
752 | goto out_drop_write; | ||
753 | if (!overwrite) { | ||
754 | err = ovl_copy_up(new); | ||
755 | if (err) | ||
756 | goto out_drop_write; | ||
757 | } | ||
758 | |||
759 | old_opaque = old_type != OVL_PATH_PURE_UPPER; | ||
760 | new_opaque = new_type != OVL_PATH_PURE_UPPER; | ||
761 | |||
762 | if (old_opaque || new_opaque) { | ||
763 | err = -ENOMEM; | ||
764 | override_cred = prepare_creds(); | ||
765 | if (!override_cred) | ||
766 | goto out_drop_write; | ||
767 | |||
768 | /* | ||
769 | * CAP_SYS_ADMIN for setting xattr on whiteout, opaque dir | ||
770 | * CAP_DAC_OVERRIDE for create in workdir | ||
771 | * CAP_FOWNER for removing whiteout from sticky dir | ||
772 | * CAP_FSETID for chmod of opaque dir | ||
773 | * CAP_CHOWN for chown of opaque dir | ||
774 | */ | ||
775 | cap_raise(override_cred->cap_effective, CAP_SYS_ADMIN); | ||
776 | cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE); | ||
777 | cap_raise(override_cred->cap_effective, CAP_FOWNER); | ||
778 | cap_raise(override_cred->cap_effective, CAP_FSETID); | ||
779 | cap_raise(override_cred->cap_effective, CAP_CHOWN); | ||
780 | old_cred = override_creds(override_cred); | ||
781 | } | ||
782 | |||
783 | if (overwrite && (new_type == OVL_PATH_LOWER || new_type == OVL_PATH_MERGE) && new_is_dir) { | ||
784 | opaquedir = ovl_check_empty_and_clear(new, new_type); | ||
785 | err = PTR_ERR(opaquedir); | ||
786 | if (IS_ERR(opaquedir)) { | ||
787 | opaquedir = NULL; | ||
788 | goto out_revert_creds; | ||
789 | } | ||
790 | } | ||
791 | |||
792 | if (overwrite) { | ||
793 | if (old_opaque) { | ||
794 | if (new->d_inode || !new_opaque) { | ||
795 | /* Whiteout source */ | ||
796 | flags |= RENAME_WHITEOUT; | ||
797 | } else { | ||
798 | /* Switch whiteouts */ | ||
799 | flags |= RENAME_EXCHANGE; | ||
800 | } | ||
801 | } else if (is_dir && !new->d_inode && new_opaque) { | ||
802 | flags |= RENAME_EXCHANGE; | ||
803 | cleanup_whiteout = true; | ||
804 | } | ||
805 | } | ||
806 | |||
807 | old_upperdir = ovl_dentry_upper(old->d_parent); | ||
808 | new_upperdir = ovl_dentry_upper(new->d_parent); | ||
809 | |||
810 | trap = lock_rename(new_upperdir, old_upperdir); | ||
811 | |||
812 | olddentry = ovl_dentry_upper(old); | ||
813 | newdentry = ovl_dentry_upper(new); | ||
814 | if (newdentry) { | ||
815 | if (opaquedir) { | ||
816 | newdentry = opaquedir; | ||
817 | opaquedir = NULL; | ||
818 | } else { | ||
819 | dget(newdentry); | ||
820 | } | ||
821 | } else { | ||
822 | new_create = true; | ||
823 | newdentry = lookup_one_len(new->d_name.name, new_upperdir, | ||
824 | new->d_name.len); | ||
825 | err = PTR_ERR(newdentry); | ||
826 | if (IS_ERR(newdentry)) | ||
827 | goto out_unlock; | ||
828 | } | ||
829 | |||
830 | err = -ESTALE; | ||
831 | if (olddentry->d_parent != old_upperdir) | ||
832 | goto out_dput; | ||
833 | if (newdentry->d_parent != new_upperdir) | ||
834 | goto out_dput; | ||
835 | if (olddentry == trap) | ||
836 | goto out_dput; | ||
837 | if (newdentry == trap) | ||
838 | goto out_dput; | ||
839 | |||
840 | if (is_dir && !old_opaque && new_opaque) { | ||
841 | err = ovl_set_opaque(olddentry); | ||
842 | if (err) | ||
843 | goto out_dput; | ||
844 | } | ||
845 | if (!overwrite && new_is_dir && old_opaque && !new_opaque) { | ||
846 | err = ovl_set_opaque(newdentry); | ||
847 | if (err) | ||
848 | goto out_dput; | ||
849 | } | ||
850 | |||
851 | if (old_opaque || new_opaque) { | ||
852 | err = ovl_do_rename(old_upperdir->d_inode, olddentry, | ||
853 | new_upperdir->d_inode, newdentry, | ||
854 | flags); | ||
855 | } else { | ||
856 | /* No debug for the plain case */ | ||
857 | BUG_ON(flags & ~RENAME_EXCHANGE); | ||
858 | err = vfs_rename(old_upperdir->d_inode, olddentry, | ||
859 | new_upperdir->d_inode, newdentry, | ||
860 | NULL, flags); | ||
861 | } | ||
862 | |||
863 | if (err) { | ||
864 | if (is_dir && !old_opaque && new_opaque) | ||
865 | ovl_remove_opaque(olddentry); | ||
866 | if (!overwrite && new_is_dir && old_opaque && !new_opaque) | ||
867 | ovl_remove_opaque(newdentry); | ||
868 | goto out_dput; | ||
869 | } | ||
870 | |||
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 | |||
876 | if (old_opaque != new_opaque) { | ||
877 | ovl_dentry_set_opaque(old, new_opaque); | ||
878 | if (!overwrite) | ||
879 | ovl_dentry_set_opaque(new, old_opaque); | ||
880 | } | ||
881 | |||
882 | if (cleanup_whiteout) | ||
883 | ovl_cleanup(old_upperdir->d_inode, newdentry); | ||
884 | |||
885 | ovl_dentry_version_inc(old->d_parent); | ||
886 | ovl_dentry_version_inc(new->d_parent); | ||
887 | |||
888 | out_dput: | ||
889 | dput(newdentry); | ||
890 | out_unlock: | ||
891 | unlock_rename(new_upperdir, old_upperdir); | ||
892 | out_revert_creds: | ||
893 | if (old_opaque || new_opaque) { | ||
894 | revert_creds(old_cred); | ||
895 | put_cred(override_cred); | ||
896 | } | ||
897 | out_drop_write: | ||
898 | ovl_drop_write(old); | ||
899 | out: | ||
900 | dput(opaquedir); | ||
901 | return err; | ||
902 | } | ||
903 | |||
904 | const struct inode_operations ovl_dir_inode_operations = { | ||
905 | .lookup = ovl_lookup, | ||
906 | .mkdir = ovl_mkdir, | ||
907 | .symlink = ovl_symlink, | ||
908 | .unlink = ovl_unlink, | ||
909 | .rmdir = ovl_rmdir, | ||
910 | .rename2 = ovl_rename2, | ||
911 | .link = ovl_link, | ||
912 | .setattr = ovl_setattr, | ||
913 | .create = ovl_create, | ||
914 | .mknod = ovl_mknod, | ||
915 | .permission = ovl_permission, | ||
916 | .getattr = ovl_dir_getattr, | ||
917 | .setxattr = ovl_setxattr, | ||
918 | .getxattr = ovl_getxattr, | ||
919 | .listxattr = ovl_listxattr, | ||
920 | .removexattr = ovl_removexattr, | ||
921 | }; | ||