Commit 11f37104 authored by Miklos Szeredi's avatar Miklos Szeredi Committed by Miklos Szeredi

ovl: verify upper dentry before unlink and rename

Unlink and rename in overlayfs checked the upper dentry for staleness by
verifying upper->d_parent against upperdir.  However the dentry can go
stale also by being unhashed, for example.

Expand the verification to actually look up the name again (under parent
lock) and check if it matches the upper dentry.  This matches what the VFS
does before passing the dentry to filesytem's unlink/rename methods, which
excludes any inconsistency caused by overlayfs.
Signed-off-by: default avatarMiklos Szeredi <mszeredi@redhat.com>
parent 3c2de27d
...@@ -596,21 +596,25 @@ static int ovl_remove_upper(struct dentry *dentry, bool is_dir) ...@@ -596,21 +596,25 @@ static int ovl_remove_upper(struct dentry *dentry, bool is_dir)
{ {
struct dentry *upperdir = ovl_dentry_upper(dentry->d_parent); struct dentry *upperdir = ovl_dentry_upper(dentry->d_parent);
struct inode *dir = upperdir->d_inode; struct inode *dir = upperdir->d_inode;
struct dentry *upper = ovl_dentry_upper(dentry); struct dentry *upper;
int err; int err;
inode_lock_nested(dir, I_MUTEX_PARENT); inode_lock_nested(dir, I_MUTEX_PARENT);
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; err = -ESTALE;
if (upper->d_parent == upperdir) { if (upper == ovl_dentry_upper(dentry)) {
/* Don't let d_delete() think it can reset d_inode */
dget(upper);
if (is_dir) if (is_dir)
err = vfs_rmdir(dir, upper); err = vfs_rmdir(dir, upper);
else else
err = vfs_unlink(dir, upper, NULL); err = vfs_unlink(dir, upper, NULL);
dput(upper);
ovl_dentry_version_inc(dentry->d_parent); ovl_dentry_version_inc(dentry->d_parent);
} }
dput(upper);
/* /*
* Keeping this dentry hashed would mean having to release * Keeping this dentry hashed would mean having to release
...@@ -620,6 +624,7 @@ static int ovl_remove_upper(struct dentry *dentry, bool is_dir) ...@@ -620,6 +624,7 @@ static int ovl_remove_upper(struct dentry *dentry, bool is_dir)
*/ */
if (!err) if (!err)
d_drop(dentry); d_drop(dentry);
out_unlock:
inode_unlock(dir); inode_unlock(dir);
return err; return err;
...@@ -840,29 +845,39 @@ static int ovl_rename2(struct inode *olddir, struct dentry *old, ...@@ -840,29 +845,39 @@ static int ovl_rename2(struct inode *olddir, struct dentry *old,
trap = lock_rename(new_upperdir, old_upperdir); trap = lock_rename(new_upperdir, old_upperdir);
olddentry = ovl_dentry_upper(old);
newdentry = ovl_dentry_upper(new); olddentry = lookup_one_len(old->d_name.name, old_upperdir,
if (newdentry) { old->d_name.len);
err = PTR_ERR(olddentry);
if (IS_ERR(olddentry))
goto out_unlock;
err = -ESTALE;
if (olddentry != ovl_dentry_upper(old))
goto out_dput_old;
newdentry = lookup_one_len(new->d_name.name, new_upperdir,
new->d_name.len);
err = PTR_ERR(newdentry);
if (IS_ERR(newdentry))
goto out_dput_old;
err = -ESTALE;
if (ovl_dentry_upper(new)) {
if (opaquedir) { if (opaquedir) {
newdentry = opaquedir; if (newdentry != opaquedir)
opaquedir = NULL; goto out_dput;
} else { } else {
dget(newdentry); if (newdentry != ovl_dentry_upper(new))
goto out_dput;
} }
} else { } else {
new_create = true; new_create = true;
newdentry = lookup_one_len(new->d_name.name, new_upperdir, if (!d_is_negative(newdentry) &&
new->d_name.len); (!new_opaque || !ovl_is_whiteout(newdentry)))
err = PTR_ERR(newdentry); goto out_dput;
if (IS_ERR(newdentry))
goto out_unlock;
} }
err = -ESTALE;
if (olddentry->d_parent != old_upperdir)
goto out_dput;
if (newdentry->d_parent != new_upperdir)
goto out_dput;
if (olddentry == trap) if (olddentry == trap)
goto out_dput; goto out_dput;
if (newdentry == trap) if (newdentry == trap)
...@@ -925,6 +940,8 @@ static int ovl_rename2(struct inode *olddir, struct dentry *old, ...@@ -925,6 +940,8 @@ static int ovl_rename2(struct inode *olddir, struct dentry *old,
out_dput: out_dput:
dput(newdentry); dput(newdentry);
out_dput_old:
dput(olddentry);
out_unlock: out_unlock:
unlock_rename(new_upperdir, old_upperdir); unlock_rename(new_upperdir, old_upperdir);
out_revert_creds: out_revert_creds:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment