diff options
author | Miklos Szeredi <mszeredi@suse.cz> | 2014-11-20 10:39:59 -0500 |
---|---|---|
committer | Miklos Szeredi <mszeredi@suse.cz> | 2014-11-20 10:39:59 -0500 |
commit | a105d685a8483985a01776411de191a726b48132 (patch) | |
tree | 4296cad5216bb6b71ee417041198db32e7abad14 | |
parent | ef94b1864d1ed5be54376404bb23d22ed0481feb (diff) |
ovl: fix remove/copy-up race
ovl_remove_and_whiteout() needs to check if upper dentry exists or not
after having locked upper parent directory.
Previously we used a "type" value computed before locking the upper parent
directory, which is susceptible to racing with copy-up.
There's a similar check in ovl_check_empty_and_clear(). This one is not
actually racy, since copy-up doesn't change the "emptyness" property of a
directory. Add a comment to this effect, and check the existence of upper
dentry locally to make the code cleaner.
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
-rw-r--r-- | fs/overlayfs/dir.c | 31 |
1 files changed, 19 insertions, 12 deletions
diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c index 15cd91ad9940..8ffc4b980f1b 100644 --- a/fs/overlayfs/dir.c +++ b/fs/overlayfs/dir.c | |||
@@ -284,8 +284,7 @@ out: | |||
284 | return ERR_PTR(err); | 284 | return ERR_PTR(err); |
285 | } | 285 | } |
286 | 286 | ||
287 | static struct dentry *ovl_check_empty_and_clear(struct dentry *dentry, | 287 | static struct dentry *ovl_check_empty_and_clear(struct dentry *dentry) |
288 | enum ovl_path_type type) | ||
289 | { | 288 | { |
290 | int err; | 289 | int err; |
291 | struct dentry *ret = NULL; | 290 | struct dentry *ret = NULL; |
@@ -294,8 +293,17 @@ static struct dentry *ovl_check_empty_and_clear(struct dentry *dentry, | |||
294 | err = ovl_check_empty_dir(dentry, &list); | 293 | err = ovl_check_empty_dir(dentry, &list); |
295 | if (err) | 294 | if (err) |
296 | ret = ERR_PTR(err); | 295 | ret = ERR_PTR(err); |
297 | else if (type == OVL_PATH_MERGE) | 296 | else { |
298 | ret = ovl_clear_empty(dentry, &list); | 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 | } | ||
299 | 307 | ||
300 | ovl_cache_free(&list); | 308 | ovl_cache_free(&list); |
301 | 309 | ||
@@ -487,8 +495,7 @@ out: | |||
487 | return err; | 495 | return err; |
488 | } | 496 | } |
489 | 497 | ||
490 | static int ovl_remove_and_whiteout(struct dentry *dentry, | 498 | static int ovl_remove_and_whiteout(struct dentry *dentry, bool is_dir) |
491 | enum ovl_path_type type, bool is_dir) | ||
492 | { | 499 | { |
493 | struct dentry *workdir = ovl_workdir(dentry); | 500 | struct dentry *workdir = ovl_workdir(dentry); |
494 | struct inode *wdir = workdir->d_inode; | 501 | struct inode *wdir = workdir->d_inode; |
@@ -500,7 +507,7 @@ static int ovl_remove_and_whiteout(struct dentry *dentry, | |||
500 | int err; | 507 | int err; |
501 | 508 | ||
502 | if (is_dir) { | 509 | if (is_dir) { |
503 | opaquedir = ovl_check_empty_and_clear(dentry, type); | 510 | opaquedir = ovl_check_empty_and_clear(dentry); |
504 | err = PTR_ERR(opaquedir); | 511 | err = PTR_ERR(opaquedir); |
505 | if (IS_ERR(opaquedir)) | 512 | if (IS_ERR(opaquedir)) |
506 | goto out; | 513 | goto out; |
@@ -515,9 +522,10 @@ static int ovl_remove_and_whiteout(struct dentry *dentry, | |||
515 | if (IS_ERR(whiteout)) | 522 | if (IS_ERR(whiteout)) |
516 | goto out_unlock; | 523 | goto out_unlock; |
517 | 524 | ||
518 | if (type == OVL_PATH_LOWER) { | 525 | upper = ovl_dentry_upper(dentry); |
526 | if (!upper) { | ||
519 | upper = lookup_one_len(dentry->d_name.name, upperdir, | 527 | upper = lookup_one_len(dentry->d_name.name, upperdir, |
520 | dentry->d_name.len); | 528 | dentry->d_name.len); |
521 | err = PTR_ERR(upper); | 529 | err = PTR_ERR(upper); |
522 | if (IS_ERR(upper)) | 530 | if (IS_ERR(upper)) |
523 | goto kill_whiteout; | 531 | goto kill_whiteout; |
@@ -529,7 +537,6 @@ static int ovl_remove_and_whiteout(struct dentry *dentry, | |||
529 | } else { | 537 | } else { |
530 | int flags = 0; | 538 | int flags = 0; |
531 | 539 | ||
532 | upper = ovl_dentry_upper(dentry); | ||
533 | if (opaquedir) | 540 | if (opaquedir) |
534 | upper = opaquedir; | 541 | upper = opaquedir; |
535 | err = -ESTALE; | 542 | err = -ESTALE; |
@@ -648,7 +655,7 @@ static int ovl_do_remove(struct dentry *dentry, bool is_dir) | |||
648 | cap_raise(override_cred->cap_effective, CAP_CHOWN); | 655 | cap_raise(override_cred->cap_effective, CAP_CHOWN); |
649 | old_cred = override_creds(override_cred); | 656 | old_cred = override_creds(override_cred); |
650 | 657 | ||
651 | err = ovl_remove_and_whiteout(dentry, type, is_dir); | 658 | err = ovl_remove_and_whiteout(dentry, is_dir); |
652 | 659 | ||
653 | revert_creds(old_cred); | 660 | revert_creds(old_cred); |
654 | put_cred(override_cred); | 661 | put_cred(override_cred); |
@@ -781,7 +788,7 @@ static int ovl_rename2(struct inode *olddir, struct dentry *old, | |||
781 | } | 788 | } |
782 | 789 | ||
783 | if (overwrite && (new_type == OVL_PATH_LOWER || new_type == OVL_PATH_MERGE) && new_is_dir) { | 790 | 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); | 791 | opaquedir = ovl_check_empty_and_clear(new); |
785 | err = PTR_ERR(opaquedir); | 792 | err = PTR_ERR(opaquedir); |
786 | if (IS_ERR(opaquedir)) { | 793 | if (IS_ERR(opaquedir)) { |
787 | opaquedir = NULL; | 794 | opaquedir = NULL; |