|
@@ -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
|
|
@@ -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;
|
|
@@ -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)
|
|
@@ -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:
|