mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-25 02:09:42 +00:00
ovl: hash overlay non-dir inodes by copy up origin
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
This commit is contained in:
parent
415543d5c6
commit
b9ac5c274b
3 changed files with 44 additions and 9 deletions
|
@ -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)
|
||||||
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)
|
||||||
} 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)
|
||||||
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,
|
||||||
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)
|
||||||
{
|
{
|
||||||
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)
|
||||||
*/
|
*/
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue