Commit d101a125 authored by Miklos Szeredi's avatar Miklos Szeredi Committed by Theodore Ts'o

fs: add file_dentry()

This series fixes bugs in nfs and ext4 due to 4bacc9c9 ("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: default avatarMiklos Szeredi <mszeredi@redhat.com>
Signed-off-by: default avatarTheodore Ts'o <tytso@mit.edu>
Tested-by: default avatarGoldwyn Rodrigues <rgoldwyn@suse.com>
Reviewed-by: default avatarTrond 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>
parent c9af28fd
...@@ -1667,7 +1667,8 @@ void d_set_d_op(struct dentry *dentry, const struct dentry_operations *op) ...@@ -1667,7 +1667,8 @@ void d_set_d_op(struct dentry *dentry, const struct dentry_operations *op)
DCACHE_OP_REVALIDATE | DCACHE_OP_REVALIDATE |
DCACHE_OP_WEAK_REVALIDATE | DCACHE_OP_WEAK_REVALIDATE |
DCACHE_OP_DELETE | DCACHE_OP_DELETE |
DCACHE_OP_SELECT_INODE)); DCACHE_OP_SELECT_INODE |
DCACHE_OP_REAL));
dentry->d_op = op; dentry->d_op = op;
if (!op) if (!op)
return; return;
...@@ -1685,6 +1686,8 @@ void d_set_d_op(struct dentry *dentry, const struct dentry_operations *op) ...@@ -1685,6 +1686,8 @@ void d_set_d_op(struct dentry *dentry, const struct dentry_operations *op)
dentry->d_flags |= DCACHE_OP_PRUNE; dentry->d_flags |= DCACHE_OP_PRUNE;
if (op->d_select_inode) if (op->d_select_inode)
dentry->d_flags |= DCACHE_OP_SELECT_INODE; dentry->d_flags |= DCACHE_OP_SELECT_INODE;
if (op->d_real)
dentry->d_flags |= DCACHE_OP_REAL;
} }
EXPORT_SYMBOL(d_set_d_op); EXPORT_SYMBOL(d_set_d_op);
......
...@@ -295,6 +295,37 @@ static void ovl_dentry_release(struct dentry *dentry) ...@@ -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) static int ovl_dentry_revalidate(struct dentry *dentry, unsigned int flags)
{ {
struct ovl_entry *oe = dentry->d_fsdata; struct ovl_entry *oe = dentry->d_fsdata;
...@@ -339,11 +370,13 @@ static int ovl_dentry_weak_revalidate(struct dentry *dentry, unsigned int flags) ...@@ -339,11 +370,13 @@ static int ovl_dentry_weak_revalidate(struct dentry *dentry, unsigned int flags)
static const struct dentry_operations ovl_dentry_operations = { static const struct dentry_operations ovl_dentry_operations = {
.d_release = ovl_dentry_release, .d_release = ovl_dentry_release,
.d_select_inode = ovl_d_select_inode, .d_select_inode = ovl_d_select_inode,
.d_real = ovl_d_real,
}; };
static const struct dentry_operations ovl_reval_dentry_operations = { static const struct dentry_operations ovl_reval_dentry_operations = {
.d_release = ovl_dentry_release, .d_release = ovl_dentry_release,
.d_select_inode = ovl_d_select_inode, .d_select_inode = ovl_d_select_inode,
.d_real = ovl_d_real,
.d_revalidate = ovl_dentry_revalidate, .d_revalidate = ovl_dentry_revalidate,
.d_weak_revalidate = ovl_dentry_weak_revalidate, .d_weak_revalidate = ovl_dentry_weak_revalidate,
}; };
......
...@@ -161,6 +161,7 @@ struct dentry_operations { ...@@ -161,6 +161,7 @@ struct dentry_operations {
struct vfsmount *(*d_automount)(struct path *); struct vfsmount *(*d_automount)(struct path *);
int (*d_manage)(struct dentry *, bool); int (*d_manage)(struct dentry *, bool);
struct inode *(*d_select_inode)(struct dentry *, unsigned); struct inode *(*d_select_inode)(struct dentry *, unsigned);
struct dentry *(*d_real)(struct dentry *, struct inode *);
} ____cacheline_aligned; } ____cacheline_aligned;
/* /*
...@@ -229,6 +230,7 @@ struct dentry_operations { ...@@ -229,6 +230,7 @@ struct dentry_operations {
#define DCACHE_OP_SELECT_INODE 0x02000000 /* Unioned entry: dcache op selects inode */ #define DCACHE_OP_SELECT_INODE 0x02000000 /* Unioned entry: dcache op selects inode */
#define DCACHE_ENCRYPTED_WITH_KEY 0x04000000 /* dir is encrypted with a valid key */ #define DCACHE_ENCRYPTED_WITH_KEY 0x04000000 /* dir is encrypted with a valid key */
#define DCACHE_OP_REAL 0x08000000
extern seqlock_t rename_lock; extern seqlock_t rename_lock;
...@@ -555,4 +557,12 @@ static inline struct dentry *d_backing_dentry(struct dentry *upper) ...@@ -555,4 +557,12 @@ static inline struct dentry *d_backing_dentry(struct dentry *upper)
return upper; return upper;
} }
static inline struct dentry *d_real(struct dentry *dentry)
{
if (unlikely(dentry->d_flags & DCACHE_OP_REAL))
return dentry->d_op->d_real(dentry, NULL);
else
return dentry;
}
#endif /* __LINUX_DCACHE_H */ #endif /* __LINUX_DCACHE_H */
...@@ -1241,6 +1241,16 @@ static inline struct inode *file_inode(const struct file *f) ...@@ -1241,6 +1241,16 @@ static inline struct inode *file_inode(const struct file *f)
return f->f_inode; return f->f_inode;
} }
static inline struct dentry *file_dentry(const struct file *file)
{
struct dentry *dentry = file->f_path.dentry;
if (unlikely(dentry->d_flags & DCACHE_OP_REAL))
return dentry->d_op->d_real(dentry, file_inode(file));
else
return dentry;
}
static inline int locks_lock_file_wait(struct file *filp, struct file_lock *fl) static inline int locks_lock_file_wait(struct file *filp, struct file_lock *fl)
{ {
return locks_lock_inode_wait(file_inode(filp), fl); return locks_lock_inode_wait(file_inode(filp), fl);
......
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