diff options
author | Miklos Szeredi <mszeredi@redhat.com> | 2016-12-16 11:02:55 +0100 |
---|---|---|
committer | Miklos Szeredi <mszeredi@redhat.com> | 2016-12-16 11:02:55 +0100 |
commit | 3ee23ff1025a18796607cf4110a8ffa8e2d163fd (patch) | |
tree | 1b5173c39a66443e5665efaf82e7fe1b4c40119e /fs/overlayfs | |
parent | 370e55ace59c2d3ed8f0ca933155030b9652e04f (diff) |
ovl: check lower existence of rename target
Check if something exists on the lower layer(s) under the target or rename
to decide if directory needs to be marked "opaque".
Marking opaque is done before the rename, and on failure the marking was
undone. Also the opaque xattr was removed if the target didn't cover
anything.
This patch changes behavior so that removal of "opaque" is not done in
either of the above cases. This means that directory may have the opaque
flag even if it doesn't cover anything. However this shouldn't affect the
performance or semantics of the overalay, while simplifying the code.
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
Diffstat (limited to 'fs/overlayfs')
-rw-r--r-- | fs/overlayfs/dir.c | 63 |
1 files changed, 11 insertions, 52 deletions
diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c index 69c3971a9992..f24b6b967901 100644 --- a/fs/overlayfs/dir.c +++ b/fs/overlayfs/dir.c @@ -126,17 +126,6 @@ static int ovl_set_opaque(struct dentry *upperdentry) return ovl_do_setxattr(upperdentry, OVL_XATTR_OPAQUE, "y", 1, 0); } -static void ovl_remove_opaque(struct dentry *upperdentry) -{ - int err; - - err = ovl_do_removexattr(upperdentry, OVL_XATTR_OPAQUE); - if (err) { - pr_warn("overlayfs: failed to remove opaque from '%s' (%i)\n", - upperdentry->d_name.name, err); - } -} - static int ovl_dir_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) { @@ -817,9 +806,6 @@ static int ovl_rename(struct inode *olddir, struct dentry *old, goto out_drop_write; } - old_opaque = ovl_dentry_is_opaque(old); - new_opaque = ovl_dentry_is_opaque(new); - old_cred = ovl_override_creds(old->d_sb); if (overwrite && new_is_dir && ovl_type_merge_or_lower(new)) { @@ -868,6 +854,9 @@ static int ovl_rename(struct inode *olddir, struct dentry *old, if (IS_ERR(newdentry)) goto out_dput_old; + old_opaque = ovl_dentry_is_opaque(old); + new_opaque = ovl_dentry_is_opaque(new); + err = -ESTALE; if (ovl_dentry_upper(new)) { if (opaquedir) { @@ -891,54 +880,24 @@ static int ovl_rename(struct inode *olddir, struct dentry *old, if (WARN_ON(olddentry->d_inode == newdentry->d_inode)) goto out_dput; - if (is_dir && !old_opaque && new_opaque) { + if (is_dir && !old_opaque && ovl_lower_positive(new)) { err = ovl_set_opaque(olddentry); if (err) goto out_dput; + ovl_dentry_set_opaque(old, true); } - if (!overwrite && new_is_dir && old_opaque && !new_opaque) { + if (!overwrite && + new_is_dir && !new_opaque && ovl_lower_positive(old)) { err = ovl_set_opaque(newdentry); if (err) goto out_dput; + ovl_dentry_set_opaque(new, true); } - if (old_opaque || new_opaque) { - err = ovl_do_rename(old_upperdir->d_inode, olddentry, - new_upperdir->d_inode, newdentry, - flags); - } else { - /* No debug for the plain case */ - BUG_ON(flags & ~RENAME_EXCHANGE); - err = vfs_rename(old_upperdir->d_inode, olddentry, - new_upperdir->d_inode, newdentry, - NULL, flags); - } - - if (err) { - if (is_dir && !old_opaque && new_opaque) - ovl_remove_opaque(olddentry); - if (!overwrite && new_is_dir && old_opaque && !new_opaque) - ovl_remove_opaque(newdentry); + err = ovl_do_rename(old_upperdir->d_inode, olddentry, + new_upperdir->d_inode, newdentry, flags); + if (err) goto out_dput; - } - - if (is_dir && old_opaque && !new_opaque) - ovl_remove_opaque(olddentry); - if (!overwrite && new_is_dir && !old_opaque && new_opaque) - ovl_remove_opaque(newdentry); - - /* - * Old dentry now lives in different location. Dentries in - * lowerstack are stale. We cannot drop them here because - * access to them is lockless. This could be only pure upper - * or opaque directory - numlower is zero. Or upper non-dir - * entry - its pureness is tracked by flag opaque. - */ - if (old_opaque != new_opaque) { - ovl_dentry_set_opaque(old, new_opaque); - if (!overwrite) - ovl_dentry_set_opaque(new, old_opaque); - } if (cleanup_whiteout) ovl_cleanup(old_upperdir->d_inode, newdentry); |