fs: add file_dentry()
This series fixes bugs in nfs and ext4 due to 4bacc9c923
("overlayfs:
Make f_path always point to the overlay and f_inode to the underlay").
Regular files opened on overlayfs will result in the file being opened on
the underlying filesystem, while f_path points to the overlayfs
mount/dentry.
This confuses filesystems which get the dentry from struct file and assume
it's theirs.
Add a new helper, file_dentry() [*], to get the filesystem's own dentry
from the file. This checks file->f_path.dentry->d_flags against
DCACHE_OP_REAL, and returns file->f_path.dentry if DCACHE_OP_REAL is not
set (this is the common, non-overlayfs case).
In the uncommon case it will call into overlayfs's ->d_real() to get the
underlying dentry, matching file_inode(file).
The reason we need to check against the inode is that if the file is copied
up while being open, d_real() would return the upper dentry, while the open
file comes from the lower dentry.
[*] If possible, it's better simply to use file_inode() instead.
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Tested-by: Goldwyn Rodrigues <rgoldwyn@suse.com>
Reviewed-by: Trond Myklebust <trond.myklebust@primarydata.com>
Cc: <stable@vger.kernel.org> # v4.2
Cc: David Howells <dhowells@redhat.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Daniel Axtens <dja@axtens.net>
This commit is contained in:
@@ -295,6 +295,37 @@ static void ovl_dentry_release(struct dentry *dentry)
|
||||
}
|
||||
}
|
||||
|
||||
static struct dentry *ovl_d_real(struct dentry *dentry, struct inode *inode)
|
||||
{
|
||||
struct dentry *real;
|
||||
|
||||
if (d_is_dir(dentry)) {
|
||||
if (!inode || inode == d_inode(dentry))
|
||||
return dentry;
|
||||
goto bug;
|
||||
}
|
||||
|
||||
real = ovl_dentry_upper(dentry);
|
||||
if (real && (!inode || inode == d_inode(real)))
|
||||
return real;
|
||||
|
||||
real = ovl_dentry_lower(dentry);
|
||||
if (!real)
|
||||
goto bug;
|
||||
|
||||
if (!inode || inode == d_inode(real))
|
||||
return real;
|
||||
|
||||
/* Handle recursion */
|
||||
if (real->d_flags & DCACHE_OP_REAL)
|
||||
return real->d_op->d_real(real, inode);
|
||||
|
||||
bug:
|
||||
WARN(1, "ovl_d_real(%pd4, %s:%lu\n): real dentry not found\n", dentry,
|
||||
inode ? inode->i_sb->s_id : "NULL", inode ? inode->i_ino : 0);
|
||||
return dentry;
|
||||
}
|
||||
|
||||
static int ovl_dentry_revalidate(struct dentry *dentry, unsigned int flags)
|
||||
{
|
||||
struct ovl_entry *oe = dentry->d_fsdata;
|
||||
@@ -339,11 +370,13 @@ static int ovl_dentry_weak_revalidate(struct dentry *dentry, unsigned int flags)
|
||||
static const struct dentry_operations ovl_dentry_operations = {
|
||||
.d_release = ovl_dentry_release,
|
||||
.d_select_inode = ovl_d_select_inode,
|
||||
.d_real = ovl_d_real,
|
||||
};
|
||||
|
||||
static const struct dentry_operations ovl_reval_dentry_operations = {
|
||||
.d_release = ovl_dentry_release,
|
||||
.d_select_inode = ovl_d_select_inode,
|
||||
.d_real = ovl_d_real,
|
||||
.d_revalidate = ovl_dentry_revalidate,
|
||||
.d_weak_revalidate = ovl_dentry_weak_revalidate,
|
||||
};
|
||||
|
Reference in New Issue
Block a user