summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fs/overlayfs/dir.c31
1 files changed, 22 insertions, 9 deletions
diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c
index c1de84c1c5ec..4257a4a0ed72 100644
--- a/fs/overlayfs/dir.c
+++ b/fs/overlayfs/dir.c
@@ -674,8 +674,17 @@ 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;
+ struct dentry *opaquedir = NULL;
int err;
+ /* Redirect dir can be !ovl_lower_positive && OVL_TYPE_MERGE */
+ if (is_dir && ovl_dentry_get_redirect(dentry)) {
+ opaquedir = ovl_check_empty_and_clear(dentry);
+ err = PTR_ERR(opaquedir);
+ if (IS_ERR(opaquedir))
+ goto out;
+ }
+
inode_lock_nested(dir, I_MUTEX_PARENT);
upper = lookup_one_len(dentry->d_name.name, upperdir,
dentry->d_name.len);
@@ -684,14 +693,15 @@ static int ovl_remove_upper(struct dentry *dentry, bool is_dir)
goto out_unlock;
err = -ESTALE;
- if (upper == ovl_dentry_upper(dentry)) {
- if (is_dir)
- err = vfs_rmdir(dir, upper);
- else
- err = vfs_unlink(dir, upper, NULL);
- ovl_dentry_version_inc(dentry->d_parent);
- }
- dput(upper);
+ if ((opaquedir && upper != opaquedir) ||
+ (!opaquedir && upper != ovl_dentry_upper(dentry)))
+ goto out_dput_upper;
+
+ if (is_dir)
+ err = vfs_rmdir(dir, upper);
+ else
+ err = vfs_unlink(dir, upper, NULL);
+ ovl_dentry_version_inc(dentry->d_parent);
/*
* Keeping this dentry hashed would mean having to release
@@ -701,9 +711,12 @@ static int ovl_remove_upper(struct dentry *dentry, bool is_dir)
*/
if (!err)
d_drop(dentry);
+out_dput_upper:
+ dput(upper);
out_unlock:
inode_unlock(dir);
-
+ dput(opaquedir);
+out:
return err;
}