| From cfc9fde0b07c3b44b570057c5f93dda59dca1c94 Mon Sep 17 00:00:00 2001 |
| From: Maxim Patlasov <mpatlasov@virtuozzo.com> |
| Date: Thu, 21 Jul 2016 18:24:26 -0700 |
| Subject: ovl: verify upper dentry in ovl_remove_and_whiteout() |
| |
| From: Maxim Patlasov <mpatlasov@virtuozzo.com> |
| |
| commit cfc9fde0b07c3b44b570057c5f93dda59dca1c94 upstream. |
| |
| The upper dentry may become stale before we call ovl_lock_rename_workdir. |
| For example, someone could (mistakenly or maliciously) manually unlink(2) |
| it directly from upperdir. |
| |
| To ensure it is not stale, let's lookup it after ovl_lock_rename_workdir |
| and and check if it matches the upper dentry. |
| |
| Essentially, it is the same problem and similar solution as in |
| commit 11f3710417d0 ("ovl: verify upper dentry before unlink and rename"). |
| |
| Signed-off-by: Maxim Patlasov <mpatlasov@virtuozzo.com> |
| Signed-off-by: Miklos Szeredi <mszeredi@redhat.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| fs/overlayfs/dir.c | 56 +++++++++++++++++++++++------------------------------ |
| 1 file changed, 25 insertions(+), 31 deletions(-) |
| |
| --- a/fs/overlayfs/dir.c |
| +++ b/fs/overlayfs/dir.c |
| @@ -511,6 +511,7 @@ static int ovl_remove_and_whiteout(struc |
| struct dentry *upper; |
| struct dentry *opaquedir = NULL; |
| int err; |
| + int flags = 0; |
| |
| if (WARN_ON(!workdir)) |
| return -EROFS; |
| @@ -540,46 +541,39 @@ static int ovl_remove_and_whiteout(struc |
| if (err) |
| goto out_dput; |
| |
| + upper = lookup_one_len(dentry->d_name.name, upperdir, |
| + dentry->d_name.len); |
| + err = PTR_ERR(upper); |
| + if (IS_ERR(upper)) |
| + goto out_unlock; |
| + |
| + err = -ESTALE; |
| + if ((opaquedir && upper != opaquedir) || |
| + (!opaquedir && ovl_dentry_upper(dentry) && |
| + upper != ovl_dentry_upper(dentry))) { |
| + goto out_dput_upper; |
| + } |
| + |
| whiteout = ovl_whiteout(workdir, dentry); |
| err = PTR_ERR(whiteout); |
| if (IS_ERR(whiteout)) |
| - goto out_unlock; |
| + goto out_dput_upper; |
| |
| - upper = ovl_dentry_upper(dentry); |
| - if (!upper) { |
| - upper = lookup_one_len(dentry->d_name.name, upperdir, |
| - dentry->d_name.len); |
| - err = PTR_ERR(upper); |
| - if (IS_ERR(upper)) |
| - goto kill_whiteout; |
| - |
| - err = ovl_do_rename(wdir, whiteout, udir, upper, 0); |
| - dput(upper); |
| - if (err) |
| - goto kill_whiteout; |
| - } else { |
| - int flags = 0; |
| - |
| - if (opaquedir) |
| - upper = opaquedir; |
| - err = -ESTALE; |
| - if (upper->d_parent != upperdir) |
| - goto kill_whiteout; |
| - |
| - if (is_dir) |
| - flags |= RENAME_EXCHANGE; |
| - |
| - err = ovl_do_rename(wdir, whiteout, udir, upper, flags); |
| - if (err) |
| - goto kill_whiteout; |
| + if (d_is_dir(upper)) |
| + flags = RENAME_EXCHANGE; |
| + |
| + err = ovl_do_rename(wdir, whiteout, udir, upper, flags); |
| + if (err) |
| + goto kill_whiteout; |
| + if (flags) |
| + ovl_cleanup(wdir, upper); |
| |
| - if (is_dir) |
| - ovl_cleanup(wdir, upper); |
| - } |
| ovl_dentry_version_inc(dentry->d_parent); |
| out_d_drop: |
| d_drop(dentry); |
| dput(whiteout); |
| +out_dput_upper: |
| + dput(upper); |
| out_unlock: |
| unlock_rename(workdir, upperdir); |
| out_dput: |