Skip to contentSkip to conversation

vfs: add file_dentry()

PATCH In review Miklos Szeredi 2 participants 4 messages Mar 8, 2016

Subsystems (4)
lore
Share & tools
lore

Miklos Szeredi

author maintainer
#0 lore raw

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")
---
 fs/open.c              |   11 +++++++++++
 fs/overlayfs/super.c   |   16 ++++++++++++++++
 include/linux/dcache.h |    1 +
 include/linux/fs.h     |    2 ++
 4 files changed, 30 insertions(+)
include/linux/fs.h +2 -0
@@ -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);
fs/open.c +11 -0
@@ -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
fs/overlayfs/super.c +16 -0
@@ -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,
 };
include/linux/dcache.h +1 -0
@@ -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;
 
 /*
author maintainer
#1 lore raw

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")
Cc 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(-)
fs/nfs/dir.c +3 -3
@@ -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;
fs/nfs/inode.c +1 -1
@@ -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);
fs/nfs/nfs4file.c +2 -2
@@ -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;
Acked-by on v1 2/2
#2 lore raw

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

Quoted 5 lines · Miklos Szeredi

From: Miklos Szeredi <[email protected]>

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

Quoted 7 lines

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")
Cc Trond Myklebust <[email protected]>
Quoted 66 lines
---
 fs/nfs/dir.c      |    6 +++---
 fs/nfs/inode.c    |    2 +-
 fs/nfs/nfs4file.c |    4 ++--
 3 files changed, 6 insertions(+), 6 deletions(-)
fs/nfs/dir.c +3 -3
@@ -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;
fs/nfs/inode.c +1 -1
@@ -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);
fs/nfs/nfs4file.c +2 -2
@@ -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;
Reviewed-by on v1 1/2
#3 lore raw
In reply to Miklos Szeredi
Quoted 18 lines · Miklos Szeredi

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).

Quoted 5 lines

[*] 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")
Quoted 81 lines
---
 fs/open.c              |   11 +++++++++++
 fs/overlayfs/super.c   |   16 ++++++++++++++++
 include/linux/dcache.h |    1 +
 include/linux/fs.h     |    2 ++
 4 files changed, 30 insertions(+)
include/linux/fs.h +2 -0
@@ -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);
fs/open.c +11 -0
@@ -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
fs/overlayfs/super.c +16 -0
@@ -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,
 };
include/linux/dcache.h +1 -0
@@ -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;
 
 /*