diff options
author | Miklos Szeredi <mszeredi@redhat.com> | 2017-07-04 22:03:17 +0200 |
---|---|---|
committer | Miklos Szeredi <mszeredi@redhat.com> | 2017-07-04 22:03:17 +0200 |
commit | b9ac5c274b8c9d642567022c0e319bca4db31956 (patch) | |
tree | b28e97ec8acea6f9f752bb27c4bd8d3b55c36427 /fs/overlayfs/inode.c | |
parent | ovl: cleanup bad and stale index entries on mount (diff) | |
download | linux-b9ac5c274b8c9d642567022c0e319bca4db31956.tar.xz linux-b9ac5c274b8c9d642567022c0e319bca4db31956.zip |
ovl: hash overlay non-dir inodes by copy up origin
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
Diffstat (limited to 'fs/overlayfs/inode.c')
-rw-r--r-- | fs/overlayfs/inode.c | 46 |
1 files changed, 41 insertions, 5 deletions
diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c index 35bb956af8e8..d9fe07defca3 100644 --- a/fs/overlayfs/inode.c +++ b/fs/overlayfs/inode.c @@ -467,6 +467,25 @@ static int ovl_inode_set(struct inode *inode, void *data) return 0; } +static bool ovl_verify_inode(struct inode *inode, struct dentry *lowerdentry, + struct dentry *upperdentry) +{ + struct inode *lowerinode = lowerdentry ? d_inode(lowerdentry) : NULL; + + /* Lower (origin) inode must match, even if NULL */ + if (ovl_inode_lower(inode) != lowerinode) + return false; + + /* + * Allow non-NULL __upperdentry in inode even if upperdentry is NULL. + * This happens when finding a lower alias for a copied up hard link. + */ + if (upperdentry && ovl_inode_upper(inode) != d_inode(upperdentry)) + return false; + + return true; +} + struct inode *ovl_get_inode(struct dentry *dentry, struct dentry *upperdentry) { struct dentry *lowerdentry = ovl_dentry_lower(dentry); @@ -476,12 +495,25 @@ struct inode *ovl_get_inode(struct dentry *dentry, struct dentry *upperdentry) if (!realinode) realinode = d_inode(lowerdentry); - if (upperdentry && !d_is_dir(upperdentry)) { - inode = iget5_locked(dentry->d_sb, (unsigned long) realinode, - ovl_inode_test, ovl_inode_set, realinode); + if (!S_ISDIR(realinode->i_mode) && + (upperdentry || (lowerdentry && ovl_indexdir(dentry->d_sb)))) { + struct inode *key = d_inode(lowerdentry ?: upperdentry); + + inode = iget5_locked(dentry->d_sb, (unsigned long) key, + ovl_inode_test, ovl_inode_set, key); if (!inode) - goto out; + goto out_nomem; if (!(inode->i_state & I_NEW)) { + /* + * Verify that the underlying files stored in the inode + * match those in the dentry. + */ + if (!ovl_verify_inode(inode, lowerdentry, upperdentry)) { + iput(inode); + inode = ERR_PTR(-ESTALE); + goto out; + } + dput(upperdentry); goto out; } @@ -490,7 +522,7 @@ struct inode *ovl_get_inode(struct dentry *dentry, struct dentry *upperdentry) } else { inode = new_inode(dentry->d_sb); if (!inode) - goto out; + goto out_nomem; } ovl_fill_inode(inode, realinode->i_mode, realinode->i_rdev); ovl_inode_init(inode, upperdentry, lowerdentry); @@ -502,4 +534,8 @@ struct inode *ovl_get_inode(struct dentry *dentry, struct dentry *upperdentry) unlock_new_inode(inode); out: return inode; + +out_nomem: + inode = ERR_PTR(-ENOMEM); + goto out; } |