Skip to content

Commit

Permalink
ovl: verify upper dentry before unlink and rename
Browse files Browse the repository at this point in the history
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: Miklos Szeredi <mszeredi@redhat.com>
  • Loading branch information
Miklos Szeredi authored and Miklos Szeredi committed Mar 21, 2016
1 parent 3c2de27 commit 11f3710
Showing 1 changed file with 38 additions and 21 deletions.
59 changes: 38 additions & 21 deletions fs/overlayfs/dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 inode *dir = upperdir->d_inode;
struct dentry *upper = ovl_dentry_upper(dentry);
struct dentry *upper;
int err;

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;
if (upper->d_parent == upperdir) {
/* Don't let d_delete() think it can reset d_inode */
dget(upper);
if (upper == ovl_dentry_upper(dentry)) {
if (is_dir)
err = vfs_rmdir(dir, upper);
else
err = vfs_unlink(dir, upper, NULL);
dput(upper);
ovl_dentry_version_inc(dentry->d_parent);
}
dput(upper);

/*
* Keeping this dentry hashed would mean having to release
Expand All @@ -620,6 +624,7 @@ static int ovl_remove_upper(struct dentry *dentry, bool is_dir)
*/
if (!err)
d_drop(dentry);
out_unlock:
inode_unlock(dir);

return err;
Expand Down Expand Up @@ -840,29 +845,39 @@ static int ovl_rename2(struct inode *olddir, struct dentry *old,

trap = lock_rename(new_upperdir, old_upperdir);

olddentry = ovl_dentry_upper(old);
newdentry = ovl_dentry_upper(new);
if (newdentry) {

olddentry = lookup_one_len(old->d_name.name, old_upperdir,
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) {
newdentry = opaquedir;
opaquedir = NULL;
if (newdentry != opaquedir)
goto out_dput;
} else {
dget(newdentry);
if (newdentry != ovl_dentry_upper(new))
goto out_dput;
}
} else {
new_create = true;
newdentry = lookup_one_len(new->d_name.name, new_upperdir,
new->d_name.len);
err = PTR_ERR(newdentry);
if (IS_ERR(newdentry))
goto out_unlock;
if (!d_is_negative(newdentry) &&
(!new_opaque || !ovl_is_whiteout(newdentry)))
goto out_dput;
}

err = -ESTALE;
if (olddentry->d_parent != old_upperdir)
goto out_dput;
if (newdentry->d_parent != new_upperdir)
goto out_dput;
if (olddentry == trap)
goto out_dput;
if (newdentry == trap)
Expand Down Expand Up @@ -925,6 +940,8 @@ static int ovl_rename2(struct inode *olddir, struct dentry *old,

out_dput:
dput(newdentry);
out_dput_old:
dput(olddentry);
out_unlock:
unlock_rename(new_upperdir, old_upperdir);
out_revert_creds:
Expand Down

0 comments on commit 11f3710

Please sign in to comment.