diff options
author | Amir Goldstein <amir73il@gmail.com> | 2016-10-26 12:34:06 +0300 |
---|---|---|
committer | Miklos Szeredi <mszeredi@redhat.com> | 2016-12-16 11:02:57 +0100 |
commit | d15951198eaccb92c6b49e62cb72f5ff62da2236 (patch) | |
tree | 9a93dbee0a4b62d70761993e5f9e949444868bed /fs/overlayfs/dir.c | |
parent | a6c6065511411c57167a6cdae0c33263fb662b51 (diff) |
ovl: check for emptiness of redirect dir
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>
Diffstat (limited to 'fs/overlayfs/dir.c')
-rw-r--r-- | fs/overlayfs/dir.c | 31 |
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; } |