From 39b681f8026c170a73972517269efc830db0d7ce Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Fri, 29 Jul 2016 12:05:24 +0200 Subject: [PATCH] ovl: store real inode pointer in ->i_private To get from overlay inode to real inode we currently use 'struct ovl_entry', which has lifetime connected to overlay dentry. This is okay, since each overlay dentry had a new overlay inode allocated. Following patch will break that assumption, so need to leave out ovl_entry. This patch stores the real inode directly in i_private, with the lowest bit used to indicate whether the inode is upper or lower. Lifetime rules remain, using ovl_inode_real() must only be done while caller holds ref on overlay dentry (and hence on real dentry), or within RCU protected regions. Signed-off-by: Miklos Szeredi --- fs/overlayfs/copy_up.c | 1 + fs/overlayfs/dir.c | 3 ++- fs/overlayfs/inode.c | 11 +++------- fs/overlayfs/overlayfs.h | 18 ++++++++++++---- fs/overlayfs/super.c | 44 ++++++++++++++++++---------------------- 5 files changed, 40 insertions(+), 37 deletions(-) diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c index 80aa6f1eb336..54e5d6681786 100644 --- a/fs/overlayfs/copy_up.c +++ b/fs/overlayfs/copy_up.c @@ -292,6 +292,7 @@ static int ovl_copy_up_locked(struct dentry *workdir, struct dentry *upperdir, goto out_cleanup; ovl_dentry_update(dentry, newdentry); + ovl_inode_update(d_inode(dentry), d_inode(newdentry)); newdentry = NULL; /* diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c index b4eac8173f93..96b1bdcf3674 100644 --- a/fs/overlayfs/dir.c +++ b/fs/overlayfs/dir.c @@ -167,6 +167,7 @@ static void ovl_instantiate(struct dentry *dentry, struct inode *inode, { ovl_dentry_version_inc(dentry->d_parent); ovl_dentry_update(dentry, newdentry); + ovl_inode_update(inode, d_inode(newdentry)); ovl_copyattr(newdentry->d_inode, inode); d_instantiate(dentry, inode); } @@ -416,7 +417,7 @@ static int ovl_create_or_link(struct dentry *dentry, int mode, dev_t rdev, }; err = -ENOMEM; - inode = ovl_new_inode(dentry->d_sb, mode, dentry->d_fsdata); + inode = ovl_new_inode(dentry->d_sb, mode); if (!inode) goto out; diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c index 0598c169ce41..2bdd3cae0f71 100644 --- a/fs/overlayfs/inode.c +++ b/fs/overlayfs/inode.c @@ -117,15 +117,12 @@ static int ovl_getattr(struct vfsmount *mnt, struct dentry *dentry, int ovl_permission(struct inode *inode, int mask) { - struct ovl_entry *oe = inode->i_private; bool is_upper; - struct dentry *realdentry = ovl_entry_real(oe, &is_upper); - struct inode *realinode; + struct inode *realinode = ovl_inode_real(inode, &is_upper); const struct cred *old_cred; int err; /* Careful in RCU walk mode */ - realinode = d_inode_rcu(realdentry); if (!realinode) { WARN_ON(!(mask & MAY_NOT_BLOCK)); return -ECHILD; @@ -312,7 +309,7 @@ int ovl_removexattr(struct dentry *dentry, const char *name) struct posix_acl *ovl_get_acl(struct inode *inode, int type) { - struct inode *realinode = ovl_inode_real(inode); + struct inode *realinode = ovl_inode_real(inode, NULL); const struct cred *old_cred; struct posix_acl *acl; @@ -412,8 +409,7 @@ static const struct inode_operations ovl_symlink_inode_operations = { .update_time = ovl_update_time, }; -struct inode *ovl_new_inode(struct super_block *sb, umode_t mode, - struct ovl_entry *oe) +struct inode *ovl_new_inode(struct super_block *sb, umode_t mode) { struct inode *inode; @@ -424,7 +420,6 @@ struct inode *ovl_new_inode(struct super_block *sb, umode_t mode, inode->i_ino = get_next_ino(); inode->i_mode = mode; inode->i_flags |= S_NOCMTIME; - inode->i_private = oe; mode &= S_IFMT; switch (mode) { diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h index fb73c09a84e7..6410209ea616 100644 --- a/fs/overlayfs/overlayfs.h +++ b/fs/overlayfs/overlayfs.h @@ -27,6 +27,8 @@ enum ovl_path_type { #define OVL_XATTR_PRE_LEN 16 #define OVL_XATTR_OPAQUE OVL_XATTR_PRE_NAME"opaque" +#define OVL_ISUPPER_MASK 1UL + static inline int ovl_do_rmdir(struct inode *dir, struct dentry *dentry) { int err = vfs_rmdir(dir, dentry); @@ -131,6 +133,16 @@ static inline int ovl_do_whiteout(struct inode *dir, struct dentry *dentry) return err; } +static inline struct inode *ovl_inode_real(struct inode *inode, bool *is_upper) +{ + unsigned long x = (unsigned long) READ_ONCE(inode->i_private); + + if (is_upper) + *is_upper = x & OVL_ISUPPER_MASK; + + return (struct inode *) (x & ~OVL_ISUPPER_MASK); +} + enum ovl_path_type ovl_path_type(struct dentry *dentry); u64 ovl_dentry_version_get(struct dentry *dentry); void ovl_dentry_version_inc(struct dentry *dentry); @@ -141,8 +153,6 @@ int ovl_path_next(int idx, struct dentry *dentry, struct path *path); struct dentry *ovl_dentry_upper(struct dentry *dentry); struct dentry *ovl_dentry_lower(struct dentry *dentry); struct dentry *ovl_dentry_real(struct dentry *dentry); -struct dentry *ovl_entry_real(struct ovl_entry *oe, bool *is_upper); -struct inode *ovl_inode_real(struct inode *inode); struct vfsmount *ovl_entry_mnt_real(struct ovl_entry *oe, struct inode *inode, bool is_upper); struct ovl_dir_cache *ovl_dir_cache(struct dentry *dentry); @@ -155,6 +165,7 @@ void ovl_dentry_set_opaque(struct dentry *dentry, bool opaque); bool ovl_is_whiteout(struct dentry *dentry); const struct cred *ovl_override_creds(struct super_block *sb); void ovl_dentry_update(struct dentry *dentry, struct dentry *upperdentry); +void ovl_inode_update(struct inode *inode, struct inode *upperinode); struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags); struct file *ovl_path_open(struct path *path, int flags); @@ -183,8 +194,7 @@ struct posix_acl *ovl_get_acl(struct inode *inode, int type); int ovl_open_maybe_copy_up(struct dentry *dentry, unsigned int file_flags); int ovl_update_time(struct inode *inode, struct timespec *ts, int flags); -struct inode *ovl_new_inode(struct super_block *sb, umode_t mode, - struct ovl_entry *oe); +struct inode *ovl_new_inode(struct super_block *sb, umode_t mode); static inline void ovl_copyattr(struct inode *from, struct inode *to) { to->i_uid = from->i_uid; diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index 058103c60f54..313f773652ff 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -145,25 +145,11 @@ struct dentry *ovl_dentry_real(struct dentry *dentry) return realdentry; } -struct dentry *ovl_entry_real(struct ovl_entry *oe, bool *is_upper) +static void ovl_inode_init(struct inode *inode, struct inode *realinode, + bool is_upper) { - struct dentry *realdentry; - - realdentry = ovl_upperdentry_dereference(oe); - if (realdentry) { - *is_upper = true; - } else { - realdentry = __ovl_dentry_lower(oe); - *is_upper = false; - } - return realdentry; -} - -struct inode *ovl_inode_real(struct inode *inode) -{ - bool tmp; - - return d_inode(ovl_entry_real(inode->i_private, &tmp)); + WRITE_ONCE(inode->i_private, (unsigned long) realinode | + (is_upper ? OVL_ISUPPER_MASK : 0)); } struct vfsmount *ovl_entry_mnt_real(struct ovl_entry *oe, struct inode *inode, @@ -235,7 +221,6 @@ void ovl_dentry_update(struct dentry *dentry, struct dentry *upperdentry) WARN_ON(!inode_is_locked(upperdentry->d_parent->d_inode)); WARN_ON(oe->__upperdentry); - BUG_ON(!upperdentry->d_inode); /* * Make sure upperdentry is consistent before making it visible to * ovl_upperdentry_dereference(). @@ -244,6 +229,13 @@ void ovl_dentry_update(struct dentry *dentry, struct dentry *upperdentry) oe->__upperdentry = upperdentry; } +void ovl_inode_update(struct inode *inode, struct inode *upperinode) +{ + WARN_ON(!upperinode); + WRITE_ONCE(inode->i_private, + (unsigned long) upperinode | OVL_ISUPPER_MASK); +} + void ovl_dentry_version_inc(struct dentry *dentry) { struct ovl_entry *oe = dentry->d_fsdata; @@ -574,14 +566,16 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, if (upperdentry || ctr) { struct dentry *realdentry; + struct inode *realinode; realdentry = upperdentry ? upperdentry : stack[0].dentry; + realinode = d_inode(realdentry); err = -ENOMEM; - inode = ovl_new_inode(dentry->d_sb, realdentry->d_inode->i_mode, - oe); + inode = ovl_new_inode(dentry->d_sb, realinode->i_mode); if (!inode) goto out_free_oe; + ovl_inode_init(inode, realinode, !!upperdentry); ovl_copyattr(realdentry->d_inode, inode); } @@ -969,6 +963,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) struct path upperpath = { NULL, NULL }; struct path workpath = { NULL, NULL }; struct dentry *root_dentry; + struct inode *realinode; struct ovl_entry *oe; struct ovl_fs *ufs; struct path *stack = NULL; @@ -1150,7 +1145,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) if (!oe) goto out_put_cred; - root_dentry = d_make_root(ovl_new_inode(sb, S_IFDIR, oe)); + root_dentry = d_make_root(ovl_new_inode(sb, S_IFDIR)); if (!root_dentry) goto out_free_oe; @@ -1169,8 +1164,9 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) root_dentry->d_fsdata = oe; - ovl_copyattr(ovl_dentry_real(root_dentry)->d_inode, - root_dentry->d_inode); + realinode = d_inode(ovl_dentry_real(root_dentry)); + ovl_inode_init(d_inode(root_dentry), realinode, !!upperpath.dentry); + ovl_copyattr(realinode, d_inode(root_dentry)); sb->s_magic = OVERLAYFS_SUPER_MAGIC; sb->s_op = &ovl_super_operations; -- GitLab