diff options
Diffstat (limited to 'fs/overlayfs')
-rw-r--r-- | fs/overlayfs/export.c | 23 | ||||
-rw-r--r-- | fs/overlayfs/inode.c | 16 | ||||
-rw-r--r-- | fs/overlayfs/overlayfs.h | 1 |
3 files changed, 38 insertions, 2 deletions
diff --git a/fs/overlayfs/export.c b/fs/overlayfs/export.c index f475a10eec07..0bca38c79244 100644 --- a/fs/overlayfs/export.c +++ b/fs/overlayfs/export.c @@ -443,14 +443,22 @@ static struct dentry *ovl_lower_fh_to_d(struct super_block *sb, struct ovl_path *stack = &origin; struct dentry *dentry = NULL; struct dentry *index = NULL; + struct inode *inode = NULL; + bool is_deleted = false; int err; /* First lookup indexed upper by fh */ if (ofs->indexdir) { index = ovl_get_index_fh(ofs, fh); err = PTR_ERR(index); - if (IS_ERR(index)) - return ERR_PTR(err); + if (IS_ERR(index)) { + if (err != -ESTALE) + return ERR_PTR(err); + + /* Found a whiteout index - treat as deleted inode */ + is_deleted = true; + index = NULL; + } } /* Then lookup origin by fh */ @@ -461,6 +469,16 @@ static struct dentry *ovl_lower_fh_to_d(struct super_block *sb, err = ovl_verify_origin(index, origin.dentry, false); if (err) goto out_err; + } else if (is_deleted) { + /* Lookup deleted non-dir by origin inode */ + if (!d_is_dir(origin.dentry)) + inode = ovl_lookup_inode(sb, origin.dentry); + err = -ESTALE; + if (!inode || atomic_read(&inode->i_count) == 1) + goto out_err; + + /* Deleted but still open? */ + index = dget(ovl_i_dentry_upper(inode)); } dentry = ovl_get_dentry(sb, NULL, &origin, index); @@ -468,6 +486,7 @@ static struct dentry *ovl_lower_fh_to_d(struct super_block *sb, out: dput(origin.dentry); dput(index); + iput(inode); return dentry; out_err: diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c index bfd7c766b5cd..56ba015b9f5e 100644 --- a/fs/overlayfs/inode.c +++ b/fs/overlayfs/inode.c @@ -645,6 +645,22 @@ static bool ovl_verify_inode(struct inode *inode, struct dentry *lowerdentry, return true; } +struct inode *ovl_lookup_inode(struct super_block *sb, struct dentry *origin) +{ + struct inode *inode, *key = d_inode(origin); + + inode = ilookup5(sb, (unsigned long) key, ovl_inode_test, key); + if (!inode) + return NULL; + + if (!ovl_verify_inode(inode, origin, NULL)) { + iput(inode); + return ERR_PTR(-ESTALE); + } + + return inode; +} + struct inode *ovl_get_inode(struct super_block *sb, struct dentry *upperdentry, struct dentry *lowerdentry, struct dentry *index, unsigned int numlower) diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h index 40ba11e412b1..a47f9142b6be 100644 --- a/fs/overlayfs/overlayfs.h +++ b/fs/overlayfs/overlayfs.h @@ -322,6 +322,7 @@ int ovl_update_time(struct inode *inode, struct timespec *ts, int flags); bool ovl_is_private_xattr(const char *name); struct inode *ovl_new_inode(struct super_block *sb, umode_t mode, dev_t rdev); +struct inode *ovl_lookup_inode(struct super_block *sb, struct dentry *origin); struct inode *ovl_get_inode(struct super_block *sb, struct dentry *upperdentry, struct dentry *lowerdentry, struct dentry *index, unsigned int numlower); |