Commit b9ac5c27 authored by Miklos Szeredi's avatar Miklos Szeredi

ovl: hash overlay non-dir inodes by copy up origin

Signed-off-by: default avatarMiklos Szeredi <mszeredi@redhat.com>
parent 415543d5
...@@ -467,6 +467,25 @@ static int ovl_inode_set(struct inode *inode, void *data) ...@@ -467,6 +467,25 @@ static int ovl_inode_set(struct inode *inode, void *data)
return 0; 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 inode *ovl_get_inode(struct dentry *dentry, struct dentry *upperdentry)
{ {
struct dentry *lowerdentry = ovl_dentry_lower(dentry); struct dentry *lowerdentry = ovl_dentry_lower(dentry);
...@@ -476,12 +495,25 @@ struct inode *ovl_get_inode(struct dentry *dentry, struct dentry *upperdentry) ...@@ -476,12 +495,25 @@ struct inode *ovl_get_inode(struct dentry *dentry, struct dentry *upperdentry)
if (!realinode) if (!realinode)
realinode = d_inode(lowerdentry); realinode = d_inode(lowerdentry);
if (upperdentry && !d_is_dir(upperdentry)) { if (!S_ISDIR(realinode->i_mode) &&
inode = iget5_locked(dentry->d_sb, (unsigned long) realinode, (upperdentry || (lowerdentry && ovl_indexdir(dentry->d_sb)))) {
ovl_inode_test, ovl_inode_set, realinode); 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) if (!inode)
goto out; goto out_nomem;
if (!(inode->i_state & I_NEW)) { 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); dput(upperdentry);
goto out; goto out;
} }
...@@ -490,7 +522,7 @@ struct inode *ovl_get_inode(struct dentry *dentry, struct dentry *upperdentry) ...@@ -490,7 +522,7 @@ struct inode *ovl_get_inode(struct dentry *dentry, struct dentry *upperdentry)
} else { } else {
inode = new_inode(dentry->d_sb); inode = new_inode(dentry->d_sb);
if (!inode) if (!inode)
goto out; goto out_nomem;
} }
ovl_fill_inode(inode, realinode->i_mode, realinode->i_rdev); ovl_fill_inode(inode, realinode->i_mode, realinode->i_rdev);
ovl_inode_init(inode, upperdentry, lowerdentry); ovl_inode_init(inode, upperdentry, lowerdentry);
...@@ -502,4 +534,8 @@ struct inode *ovl_get_inode(struct dentry *dentry, struct dentry *upperdentry) ...@@ -502,4 +534,8 @@ struct inode *ovl_get_inode(struct dentry *dentry, struct dentry *upperdentry)
unlock_new_inode(inode); unlock_new_inode(inode);
out: out:
return inode; return inode;
out_nomem:
inode = ERR_PTR(-ENOMEM);
goto out;
} }
...@@ -678,9 +678,9 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, ...@@ -678,9 +678,9 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
upperdentry = dget(index); upperdentry = dget(index);
if (upperdentry || ctr) { if (upperdentry || ctr) {
err = -ENOMEM;
inode = ovl_get_inode(dentry, upperdentry); inode = ovl_get_inode(dentry, upperdentry);
if (!inode) err = PTR_ERR(inode);
if (IS_ERR(inode))
goto out_free_oe; goto out_free_oe;
OVL_I(inode)->redirect = upperredirect; OVL_I(inode)->redirect = upperredirect;
......
...@@ -236,7 +236,6 @@ void ovl_inode_update(struct inode *inode, struct dentry *upperdentry) ...@@ -236,7 +236,6 @@ void ovl_inode_update(struct inode *inode, struct dentry *upperdentry)
{ {
struct inode *upperinode = d_inode(upperdentry); struct inode *upperinode = d_inode(upperdentry);
WARN_ON(!inode_unhashed(inode));
WARN_ON(!inode_is_locked(upperdentry->d_parent->d_inode)); WARN_ON(!inode_is_locked(upperdentry->d_parent->d_inode));
WARN_ON(OVL_I(inode)->__upperdentry); WARN_ON(OVL_I(inode)->__upperdentry);
...@@ -245,7 +244,7 @@ void ovl_inode_update(struct inode *inode, struct dentry *upperdentry) ...@@ -245,7 +244,7 @@ void ovl_inode_update(struct inode *inode, struct dentry *upperdentry)
*/ */
smp_wmb(); smp_wmb();
OVL_I(inode)->__upperdentry = upperdentry; OVL_I(inode)->__upperdentry = upperdentry;
if (!S_ISDIR(upperinode->i_mode)) { if (!S_ISDIR(upperinode->i_mode) && inode_unhashed(inode)) {
inode->i_private = upperinode; inode->i_private = upperinode;
__insert_inode_hash(inode, (unsigned long) upperinode); __insert_inode_hash(inode, (unsigned long) upperinode);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment