diff options
Diffstat (limited to 'fs/overlayfs/util.c')
-rw-r--r-- | fs/overlayfs/util.c | 35 |
1 files changed, 29 insertions, 6 deletions
diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c index f4847eff3284..fc7b1447435b 100644 --- a/fs/overlayfs/util.c +++ b/fs/overlayfs/util.c @@ -155,6 +155,22 @@ struct dentry *ovl_dentry_real(struct dentry *dentry) return realdentry; } +struct inode *ovl_inode_real(struct inode *inode, bool *is_upper) +{ + struct inode *realinode = lockless_dereference(OVL_I(inode)->upper); + bool isup = false; + + if (!realinode) + realinode = OVL_I(inode)->lower; + else + isup = true; + + if (is_upper) + *is_upper = isup; + + return realinode; +} + struct ovl_dir_cache *ovl_dir_cache(struct dentry *dentry) { struct ovl_entry *oe = dentry->d_fsdata; @@ -233,10 +249,11 @@ void ovl_dentry_update(struct dentry *dentry, struct dentry *upperdentry) void ovl_inode_init(struct inode *inode, struct dentry *dentry) { struct inode *realinode = d_inode(ovl_dentry_real(dentry)); - bool is_upper = ovl_dentry_upper(dentry); - WRITE_ONCE(inode->i_private, (unsigned long) realinode | - (is_upper ? OVL_ISUPPER_MASK : 0)); + if (ovl_dentry_upper(dentry)) + OVL_I(inode)->upper = realinode; + else + OVL_I(inode)->lower = realinode; ovl_copyattr(realinode, inode); } @@ -245,10 +262,16 @@ void ovl_inode_update(struct inode *inode, struct inode *upperinode) { WARN_ON(!upperinode); WARN_ON(!inode_unhashed(inode)); - WRITE_ONCE(inode->i_private, - (unsigned long) upperinode | OVL_ISUPPER_MASK); - if (!S_ISDIR(upperinode->i_mode)) + /* + * Make sure upperinode is consistent before making it visible to + * ovl_inode_real(); + */ + smp_wmb(); + OVL_I(inode)->upper = upperinode; + if (!S_ISDIR(upperinode->i_mode)) { + inode->i_private = upperinode; __insert_inode_hash(inode, (unsigned long) upperinode); + } } void ovl_dentry_version_inc(struct dentry *dentry) |