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 /fs/overlayfs | |
| 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>
Diffstat (limited to 'fs/overlayfs')
| -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; |
