ovl: check for emptiness of redirect dir
authorAmir Goldstein <amir73il@gmail.com>
Wed, 26 Oct 2016 09:34:06 +0000 (12:34 +0300)
committerMiklos Szeredi <mszeredi@redhat.com>
Fri, 16 Dec 2016 10:02:57 +0000 (11:02 +0100)
Before introducing redirect_dir feature, the condition
!ovl_lower_positive(dentry) for a directory, implied that it is a pure
upper directory, which may be removed if empty.

Now that directory can be redirect, it is possible that upper does not
cover any lower (i.e. !ovl_lower_positive(dentry)), but the directory is a
merge (with redirected path) and maybe non empty.

Check for this case in ovl_remove_upper().

This change fixes the following test case from rename-pop-dir.py
of unionmount-testsuite:

    """Remove dir and rename old name"""
    d = ctx.non_empty_dir()
    d2 = ctx.no_dir()

    ctx.rmdir(d, err=ENOTEMPTY)
    ctx.rename(d, d2)
    ctx.rmdir(d, err=ENOENT)
    ctx.rmdir(d2, err=ENOTEMPTY)

./run --ov rename-pop-dir
/mnt/a/no_dir103: Expected error (Directory not empty) was not produced

Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
fs/overlayfs/dir.c

index c1de84c..4257a4a 100644 (file)
@@ -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;
 }