2016-03-08 15:46:08

by Miklos Szeredi

[permalink] [raw]
Subject: [PATCH 1/2] vfs: add file_dentry()

From: Miklos Szeredi <[email protected]>

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 simply compares file_inode(file->f_path.dentry) to
file_inode(file) and if they are equal returns file->f_path.dentry (this is
the common, non-overlayfs case).

In the uncommon case (regular file on overlayfs) it will call into
overlayfs's ->d_native_dentry() to get the underlying dentry matching
file_inode(file).

[*] If possible, it's better simply to use file_inode() instead.

Fixes: 4bacc9c9234c ("overlayfs: Make f_path always point to the overlay and f_inode to the underlay")
Signed-off-by: Miklos Szeredi <[email protected]>
Tested-by: Goldwyn Rodrigues <[email protected]>
---
fs/open.c | 11 +++++++++++
fs/overlayfs/super.c | 16 ++++++++++++++++
include/linux/dcache.h | 1 +
include/linux/fs.h | 2 ++
4 files changed, 30 insertions(+)

--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1234,6 +1234,8 @@ static inline struct inode *file_inode(c
return f->f_inode;
}

+extern struct dentry *file_dentry(const struct file *file);
+
static inline int locks_lock_file_wait(struct file *filp, struct file_lock *fl)
{
return locks_lock_inode_wait(file_inode(filp), fl);
--- a/fs/open.c
+++ b/fs/open.c
@@ -831,6 +831,17 @@ char *file_path(struct file *filp, char
}
EXPORT_SYMBOL(file_path);

+struct dentry *file_dentry(const struct file *file)
+{
+ struct dentry *dentry = file->f_path.dentry;
+
+ if (likely(d_inode(dentry) == file_inode(file)))
+ return dentry;
+ else
+ return dentry->d_op->d_native_dentry(dentry, file_inode(file));
+}
+EXPORT_SYMBOL(file_dentry);
+
/**
* vfs_open - open the file at the given path
* @path: path to open
--- a/fs/overlayfs/super.c
+++ b/fs/overlayfs/super.c
@@ -336,14 +336,30 @@ static int ovl_dentry_weak_revalidate(st
return ret;
}

+static struct dentry *ovl_d_native_dentry(struct dentry *dentry,
+ struct inode *inode)
+{
+ struct ovl_entry *oe = dentry->d_fsdata;
+ struct dentry *realentry = ovl_upperdentry_dereference(oe);
+
+ if (realentry && inode == d_inode(realentry))
+ return realentry;
+ realentry = __ovl_dentry_lower(oe);
+ if (realentry && inode == d_inode(realentry))
+ return realentry;
+ BUG();
+}
+
static const struct dentry_operations ovl_dentry_operations = {
.d_release = ovl_dentry_release,
.d_select_inode = ovl_d_select_inode,
+ .d_native_dentry = ovl_d_native_dentry,
};

static const struct dentry_operations ovl_reval_dentry_operations = {
.d_release = ovl_dentry_release,
.d_select_inode = ovl_d_select_inode,
+ .d_native_dentry = ovl_d_native_dentry,
.d_revalidate = ovl_dentry_revalidate,
.d_weak_revalidate = ovl_dentry_weak_revalidate,
};
--- a/include/linux/dcache.h
+++ b/include/linux/dcache.h
@@ -161,6 +161,7 @@ struct dentry_operations {
struct vfsmount *(*d_automount)(struct path *);
int (*d_manage)(struct dentry *, bool);
struct inode *(*d_select_inode)(struct dentry *, unsigned);
+ struct dentry *(*d_native_dentry)(struct dentry *, struct inode *);
} ____cacheline_aligned;

/*


2016-03-08 15:47:55

by Miklos Szeredi

[permalink] [raw]
Subject: [PATCH 2/2] nfs: use file_dentry()

From: Miklos Szeredi <[email protected]>

NFS may be used as lower layer of overlayfs and accessing f_path.dentry can
lead to a crash.

Fix by replacing direct access of file->f_path.dentry with the
file_dentry() accessor, which will always return a native object.

Fixes: 4bacc9c9234c ("overlayfs: Make f_path always point to the overlay and f_inode to the underlay")
Signed-off-by: Miklos Szeredi <[email protected]>
Cc: Trond Myklebust <[email protected]>
Tested-by: Goldwyn Rodrigues <[email protected]>
---
fs/nfs/dir.c | 6 +++---
fs/nfs/inode.c | 2 +-
fs/nfs/nfs4file.c | 4 ++--
3 files changed, 6 insertions(+), 6 deletions(-)

--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -377,7 +377,7 @@ int nfs_readdir_xdr_filler(struct page *
again:
timestamp = jiffies;
gencount = nfs_inc_attr_generation_counter();
- error = NFS_PROTO(inode)->readdir(file->f_path.dentry, cred, entry->cookie, pages,
+ error = NFS_PROTO(inode)->readdir(file_dentry(file), cred, entry->cookie, pages,
NFS_SERVER(inode)->dtsize, desc->plus);
if (error < 0) {
/* We requested READDIRPLUS, but the server doesn't grok it */
@@ -560,7 +560,7 @@ int nfs_readdir_page_filler(nfs_readdir_
count++;

if (desc->plus != 0)
- nfs_prime_dcache(desc->file->f_path.dentry, entry);
+ nfs_prime_dcache(file_dentry(desc->file), entry);

status = nfs_readdir_add_to_array(entry, page);
if (status != 0)
@@ -864,7 +864,7 @@ static bool nfs_dir_mapping_need_revalid
*/
static int nfs_readdir(struct file *file, struct dir_context *ctx)
{
- struct dentry *dentry = file->f_path.dentry;
+ struct dentry *dentry = file_dentry(file);
struct inode *inode = d_inode(dentry);
nfs_readdir_descriptor_t my_desc,
*desc = &my_desc;
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -940,7 +940,7 @@ int nfs_open(struct inode *inode, struct
{
struct nfs_open_context *ctx;

- ctx = alloc_nfs_open_context(filp->f_path.dentry, filp->f_mode);
+ ctx = alloc_nfs_open_context(file_dentry(filp), filp->f_mode);
if (IS_ERR(ctx))
return PTR_ERR(ctx);
nfs_file_set_open_context(filp, ctx);
--- a/fs/nfs/nfs4file.c
+++ b/fs/nfs/nfs4file.c
@@ -26,7 +26,7 @@ static int
nfs4_file_open(struct inode *inode, struct file *filp)
{
struct nfs_open_context *ctx;
- struct dentry *dentry = filp->f_path.dentry;
+ struct dentry *dentry = file_dentry(filp);
struct dentry *parent = NULL;
struct inode *dir;
unsigned openflags = filp->f_flags;
@@ -57,7 +57,7 @@ nfs4_file_open(struct inode *inode, stru
parent = dget_parent(dentry);
dir = d_inode(parent);

- ctx = alloc_nfs_open_context(filp->f_path.dentry, filp->f_mode);
+ ctx = alloc_nfs_open_context(file_dentry(filp), filp->f_mode);
err = PTR_ERR(ctx);
if (IS_ERR(ctx))
goto out;

2016-03-08 15:49:00

by Trond Myklebust

[permalink] [raw]
Subject: Re: [PATCH 1/2] vfs: add file_dentry()

On Tue, Mar 8, 2016 at 10:47 AM, Miklos Szeredi <[email protected]> wrote:
> From: Miklos Szeredi <[email protected]>
>
> 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 simply compares file_inode(file->f_path.dentry) to
> file_inode(file) and if they are equal returns file->f_path.dentry (this is
> the common, non-overlayfs case).
>
> In the uncommon case (regular file on overlayfs) it will call into
> overlayfs's ->d_native_dentry() to get the underlying dentry matching
> file_inode(file).
>
> [*] If possible, it's better simply to use file_inode() instead.
>
> Fixes: 4bacc9c9234c ("overlayfs: Make f_path always point to the overlay and f_inode to the underlay")
> Signed-off-by: Miklos Szeredi <[email protected]>
> Tested-by: Goldwyn Rodrigues <[email protected]>

Reviewed-by: Trond Myklebust <[email protected]>

> ---
> fs/open.c | 11 +++++++++++
> fs/overlayfs/super.c | 16 ++++++++++++++++
> include/linux/dcache.h | 1 +
> include/linux/fs.h | 2 ++
> 4 files changed, 30 insertions(+)
>
> --- a/include/linux/fs.h
> +++ b/include/linux/fs.h
> @@ -1234,6 +1234,8 @@ static inline struct inode *file_inode(c
> return f->f_inode;
> }
>
> +extern struct dentry *file_dentry(const struct file *file);
> +
> static inline int locks_lock_file_wait(struct file *filp, struct file_lock *fl)
> {
> return locks_lock_inode_wait(file_inode(filp), fl);
> --- a/fs/open.c
> +++ b/fs/open.c
> @@ -831,6 +831,17 @@ char *file_path(struct file *filp, char
> }
> EXPORT_SYMBOL(file_path);
>
> +struct dentry *file_dentry(const struct file *file)
> +{
> + struct dentry *dentry = file->f_path.dentry;
> +
> + if (likely(d_inode(dentry) == file_inode(file)))
> + return dentry;
> + else
> + return dentry->d_op->d_native_dentry(dentry, file_inode(file));
> +}
> +EXPORT_SYMBOL(file_dentry);
> +
> /**
> * vfs_open - open the file at the given path
> * @path: path to open
> --- a/fs/overlayfs/super.c
> +++ b/fs/overlayfs/super.c
> @@ -336,14 +336,30 @@ static int ovl_dentry_weak_revalidate(st
> return ret;
> }
>
> +static struct dentry *ovl_d_native_dentry(struct dentry *dentry,
> + struct inode *inode)
> +{
> + struct ovl_entry *oe = dentry->d_fsdata;
> + struct dentry *realentry = ovl_upperdentry_dereference(oe);
> +
> + if (realentry && inode == d_inode(realentry))
> + return realentry;
> + realentry = __ovl_dentry_lower(oe);
> + if (realentry && inode == d_inode(realentry))
> + return realentry;
> + BUG();
> +}
> +
> static const struct dentry_operations ovl_dentry_operations = {
> .d_release = ovl_dentry_release,
> .d_select_inode = ovl_d_select_inode,
> + .d_native_dentry = ovl_d_native_dentry,
> };
>
> static const struct dentry_operations ovl_reval_dentry_operations = {
> .d_release = ovl_dentry_release,
> .d_select_inode = ovl_d_select_inode,
> + .d_native_dentry = ovl_d_native_dentry,
> .d_revalidate = ovl_dentry_revalidate,
> .d_weak_revalidate = ovl_dentry_weak_revalidate,
> };
> --- a/include/linux/dcache.h
> +++ b/include/linux/dcache.h
> @@ -161,6 +161,7 @@ struct dentry_operations {
> struct vfsmount *(*d_automount)(struct path *);
> int (*d_manage)(struct dentry *, bool);
> struct inode *(*d_select_inode)(struct dentry *, unsigned);
> + struct dentry *(*d_native_dentry)(struct dentry *, struct inode *);
> } ____cacheline_aligned;
>
> /*

2016-03-08 15:49:46

by Trond Myklebust

[permalink] [raw]
Subject: Re: [PATCH 2/2] nfs: use file_dentry()

On Tue, Mar 8, 2016 at 10:49 AM, Miklos Szeredi <[email protected]> wrote:
> From: Miklos Szeredi <[email protected]>
>
> NFS may be used as lower layer of overlayfs and accessing f_path.dentry can
> lead to a crash.
>
> Fix by replacing direct access of file->f_path.dentry with the
> file_dentry() accessor, which will always return a native object.
>
> Fixes: 4bacc9c9234c ("overlayfs: Make f_path always point to the overlay and f_inode to the underlay")
> Signed-off-by: Miklos Szeredi <[email protected]>
> Cc: Trond Myklebust <[email protected]>
> Tested-by: Goldwyn Rodrigues <[email protected]>

Acked-by: Trond Myklebust <[email protected]>

> ---
> fs/nfs/dir.c | 6 +++---
> fs/nfs/inode.c | 2 +-
> fs/nfs/nfs4file.c | 4 ++--
> 3 files changed, 6 insertions(+), 6 deletions(-)
>
> --- a/fs/nfs/dir.c
> +++ b/fs/nfs/dir.c
> @@ -377,7 +377,7 @@ int nfs_readdir_xdr_filler(struct page *
> again:
> timestamp = jiffies;
> gencount = nfs_inc_attr_generation_counter();
> - error = NFS_PROTO(inode)->readdir(file->f_path.dentry, cred, entry->cookie, pages,
> + error = NFS_PROTO(inode)->readdir(file_dentry(file), cred, entry->cookie, pages,
> NFS_SERVER(inode)->dtsize, desc->plus);
> if (error < 0) {
> /* We requested READDIRPLUS, but the server doesn't grok it */
> @@ -560,7 +560,7 @@ int nfs_readdir_page_filler(nfs_readdir_
> count++;
>
> if (desc->plus != 0)
> - nfs_prime_dcache(desc->file->f_path.dentry, entry);
> + nfs_prime_dcache(file_dentry(desc->file), entry);
>
> status = nfs_readdir_add_to_array(entry, page);
> if (status != 0)
> @@ -864,7 +864,7 @@ static bool nfs_dir_mapping_need_revalid
> */
> static int nfs_readdir(struct file *file, struct dir_context *ctx)
> {
> - struct dentry *dentry = file->f_path.dentry;
> + struct dentry *dentry = file_dentry(file);
> struct inode *inode = d_inode(dentry);
> nfs_readdir_descriptor_t my_desc,
> *desc = &my_desc;
> --- a/fs/nfs/inode.c
> +++ b/fs/nfs/inode.c
> @@ -940,7 +940,7 @@ int nfs_open(struct inode *inode, struct
> {
> struct nfs_open_context *ctx;
>
> - ctx = alloc_nfs_open_context(filp->f_path.dentry, filp->f_mode);
> + ctx = alloc_nfs_open_context(file_dentry(filp), filp->f_mode);
> if (IS_ERR(ctx))
> return PTR_ERR(ctx);
> nfs_file_set_open_context(filp, ctx);
> --- a/fs/nfs/nfs4file.c
> +++ b/fs/nfs/nfs4file.c
> @@ -26,7 +26,7 @@ static int
> nfs4_file_open(struct inode *inode, struct file *filp)
> {
> struct nfs_open_context *ctx;
> - struct dentry *dentry = filp->f_path.dentry;
> + struct dentry *dentry = file_dentry(filp);
> struct dentry *parent = NULL;
> struct inode *dir;
> unsigned openflags = filp->f_flags;
> @@ -57,7 +57,7 @@ nfs4_file_open(struct inode *inode, stru
> parent = dget_parent(dentry);
> dir = d_inode(parent);
>
> - ctx = alloc_nfs_open_context(filp->f_path.dentry, filp->f_mode);
> + ctx = alloc_nfs_open_context(file_dentry(filp), filp->f_mode);
> err = PTR_ERR(ctx);
> if (IS_ERR(ctx))
> goto out;