aboutsummaryrefslogtreecommitdiffstats
path: root/fs/overlayfs/super.c
diff options
context:
space:
mode:
authorMiklos Szeredi <mszeredi@redhat.com>2016-07-29 12:05:24 +0200
committerMiklos Szeredi <mszeredi@redhat.com>2016-07-29 12:05:24 +0200
commit51f7e52dc943468c6929fa0a82d4afac3c8e9636 (patch)
treef5eb45b46ae8b7103ef9b77bb1b711472b400c19 /fs/overlayfs/super.c
parentovl: store real inode pointer in ->i_private (diff)
downloadlinux-dev-51f7e52dc943468c6929fa0a82d4afac3c8e9636.tar.xz
linux-dev-51f7e52dc943468c6929fa0a82d4afac3c8e9636.zip
ovl: share inode for hard link
Inode attributes are copied up to overlay inode (uid, gid, mode, atime, mtime, ctime) so generic code using these fields works correcty. If a hard link is created in overlayfs separate inodes are allocated for each link. If chmod/chown/etc. is performed on one of the links then the inode belonging to the other ones won't be updated. This patch attempts to fix this by sharing inodes for hard links. Use inode hash (with real inode pointer as a key) to make sure overlay inodes are shared for hard links on upper. Hard links on lower are still split (which is not user observable until the copy-up happens, see Documentation/filesystems/overlayfs.txt under "Non-standard behavior"). The inode is only inserted in the hash if it is non-directoy and upper. Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
Diffstat (limited to '')
-rw-r--r--fs/overlayfs/super.c12
1 files changed, 10 insertions, 2 deletions
diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c
index 313f773652ff..44c4510f5adf 100644
--- a/fs/overlayfs/super.c
+++ b/fs/overlayfs/super.c
@@ -232,8 +232,11 @@ void ovl_dentry_update(struct dentry *dentry, struct dentry *upperdentry)
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))
+ __insert_inode_hash(inode, (unsigned long) upperinode);
}
void ovl_dentry_version_inc(struct dentry *dentry)
@@ -572,10 +575,15 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
realinode = d_inode(realdentry);
err = -ENOMEM;
- inode = ovl_new_inode(dentry->d_sb, realinode->i_mode);
+ if (upperdentry && !d_is_dir(upperdentry)) {
+ inode = ovl_get_inode(dentry->d_sb, realinode);
+ } else {
+ inode = ovl_new_inode(dentry->d_sb, realinode->i_mode);
+ if (inode)
+ ovl_inode_init(inode, realinode, !!upperdentry);
+ }
if (!inode)
goto out_free_oe;
- ovl_inode_init(inode, realinode, !!upperdentry);
ovl_copyattr(realdentry->d_inode, inode);
}