From 55acc6618259c8ff0a400a131f0f4b613e96010a Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Tue, 4 Jul 2017 22:03:18 +0200 Subject: [PATCH] ovl: add flag for upper in ovl_entry For rename, we need to ensure that an upper alias exists for hard links before attempting the operation. Introduce a flag in ovl_entry to track the state of the upper alias. Signed-off-by: Miklos Szeredi --- fs/overlayfs/copy_up.c | 1 + fs/overlayfs/dir.c | 1 + fs/overlayfs/namei.c | 4 +++- fs/overlayfs/overlayfs.h | 2 ++ fs/overlayfs/ovl_entry.h | 5 ++++- fs/overlayfs/super.c | 1 + fs/overlayfs/util.c | 19 +++++++++++++++++++ 7 files changed, 31 insertions(+), 2 deletions(-) diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c index 8f9e26e91386..58c06bd58a96 100644 --- a/fs/overlayfs/copy_up.c +++ b/fs/overlayfs/copy_up.c @@ -472,6 +472,7 @@ static int ovl_copy_up_locked(struct ovl_copy_up_ctx *c) if (err) goto out_cleanup; + ovl_dentry_set_upper_alias(c->dentry); ovl_inode_update(d_inode(c->dentry), newdentry); out: dput(temp); diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c index a072c27e03bc..8b2b23181b19 100644 --- a/fs/overlayfs/dir.c +++ b/fs/overlayfs/dir.c @@ -156,6 +156,7 @@ static void ovl_instantiate(struct dentry *dentry, struct inode *inode, struct dentry *newdentry, bool hardlink) { ovl_dentry_version_inc(dentry->d_parent); + ovl_dentry_set_upper_alias(dentry); if (!hardlink) { ovl_inode_update(inode, newdentry); ovl_copyattr(newdentry->d_inode, inode); diff --git a/fs/overlayfs/namei.c b/fs/overlayfs/namei.c index f7fb0c919419..2d8b6292fe21 100644 --- a/fs/overlayfs/namei.c +++ b/fs/overlayfs/namei.c @@ -674,7 +674,9 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, memcpy(oe->lowerstack, stack, sizeof(struct path) * ctr); dentry->d_fsdata = oe; - if (index && !upperdentry) + if (upperdentry) + ovl_dentry_set_upper_alias(dentry); + else if (index) upperdentry = dget(index); if (upperdentry || ctr) { diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h index f3e49cf34517..751b36a5c22f 100644 --- a/fs/overlayfs/overlayfs.h +++ b/fs/overlayfs/overlayfs.h @@ -206,6 +206,8 @@ void ovl_set_dir_cache(struct dentry *dentry, struct ovl_dir_cache *cache); bool ovl_dentry_is_opaque(struct dentry *dentry); bool ovl_dentry_is_whiteout(struct dentry *dentry); void ovl_dentry_set_opaque(struct dentry *dentry); +bool ovl_dentry_has_upper_alias(struct dentry *dentry); +void ovl_dentry_set_upper_alias(struct dentry *dentry); bool ovl_redirect_dir(struct super_block *sb); const char *ovl_dentry_get_redirect(struct dentry *dentry); void ovl_dentry_set_redirect(struct dentry *dentry, const char *redirect); diff --git a/fs/overlayfs/ovl_entry.h b/fs/overlayfs/ovl_entry.h index 9642ec64467b..878a750986dd 100644 --- a/fs/overlayfs/ovl_entry.h +++ b/fs/overlayfs/ovl_entry.h @@ -42,7 +42,10 @@ struct ovl_fs { /* private information held for every overlayfs dentry */ struct ovl_entry { union { - bool opaque; + struct { + unsigned long has_upper; + bool opaque; + }; struct rcu_head rcu; }; unsigned numlower; diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index 791581c370f5..f29ee08cf99f 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -1119,6 +1119,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) kfree(lowertmp); if (upperpath.dentry) { + oe->has_upper = true; if (ovl_is_impuredir(upperpath.dentry)) ovl_set_flag(OVL_IMPURE, d_inode(root_dentry)); } diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c index c80b4bf1e64f..38fa75228c66 100644 --- a/fs/overlayfs/util.c +++ b/fs/overlayfs/util.c @@ -201,6 +201,25 @@ void ovl_dentry_set_opaque(struct dentry *dentry) oe->opaque = true; } +/* + * For hard links it's possible for ovl_dentry_upper() to return positive, while + * there's no actual upper alias for the inode. Copy up code needs to know + * about the existence of the upper alias, so it can't use ovl_dentry_upper(). + */ +bool ovl_dentry_has_upper_alias(struct dentry *dentry) +{ + struct ovl_entry *oe = dentry->d_fsdata; + + return oe->has_upper; +} + +void ovl_dentry_set_upper_alias(struct dentry *dentry) +{ + struct ovl_entry *oe = dentry->d_fsdata; + + oe->has_upper = true; +} + bool ovl_redirect_dir(struct super_block *sb) { struct ovl_fs *ofs = sb->s_fs_info; -- 2.20.1