This is only possible with FUSE_PASSTHROUGH_V2 enabled.

Mark the inode with S_DAX if FUSE_LOOKUP returns with FUSE_ATTR_DAX set.

This patch does not yet provide a way actually use the dax dev backing:
when such a backing ID is provided in reply to FUSE_OPEN with
FOPEN_PASSTHROUGH flag set, an error will be returned.

Signed-off-by: Miklos Szeredi <[email protected]>
---
 fs/fuse/backing.c     | 121 ++++++++++++++++++++++++++++++------------
 fs/fuse/file.c        |   2 +-
 fs/fuse/fuse_i.h      |  28 ++++++++--
 fs/fuse/inode.c       |  11 +++-
 fs/fuse/passthrough.c |   6 ++-
 5 files changed, 126 insertions(+), 42 deletions(-)

diff --git a/fs/fuse/backing.c b/fs/fuse/backing.c
index 3c879df7989c..c852f0498961 100644
--- a/fs/fuse/backing.c
+++ b/fs/fuse/backing.c
@@ -9,6 +9,7 @@
 #include "fuse_i.h"
 
 #include <linux/file.h>
+#include <linux/dax.h>
 #include <linux/rhashtable.h>
 
 static struct fuse_backing *fuse_backing_get(struct fuse_backing *fb)
@@ -22,9 +23,16 @@ static void fuse_backing_free(struct fuse_backing *fb)
 {
        pr_debug("%s: fb=0x%p\n", __func__, fb);
 
-       if (fb->file)
-               fput(fb->file);
-       put_cred(fb->cred);
+       switch (fb->type) {
+       case FUSE_BACKING_PATH:
+               path_put(&fb->path);
+               put_cred(fb->cred);
+               break;
+
+       case FUSE_BACKING_DAXDEV:
+               fs_put_dax(fb->dax_dev, fb);
+               break;
+       }
        kfree_rcu(fb, rcu);
 }
 
@@ -103,39 +111,83 @@ int fuse_backing_close_64(struct fuse_conn *fc, u64 
backing_id)
        return 0;
 }
 
-static struct fuse_backing *fuse_backing_new(struct fuse_conn *fc, int fd)
+static int fuse_dax_notify_failure(struct dax_device *daxdev, u64 offset, u64 
len, int mf_flags)
 {
-       struct fuse_backing *fb;
-       struct super_block *backing_sb;
-       struct file *file;
+       struct fuse_backing *fb = dax_holder(daxdev);
 
-       /* TODO: relax CAP_SYS_ADMIN once backing files are visible to lsof */
-       if (!fc->passthrough || !capable(CAP_SYS_ADMIN))
-               return ERR_PTR(-EPERM);
+       fb->dax_error = true;
 
-       CLASS(fd_raw, f)(fd);
-       if (fd_empty(f))
-               return ERR_PTR(-EBADF);
+       return 0;
+}
 
-       file = fd_file(f);
+static const struct dax_holder_operations fuse_dax_holder_ops = {
+       .notify_failure         = fuse_dax_notify_failure,
+};
+
+static int fuse_backing_open_file(struct fuse_conn *fc, struct fuse_backing 
*fb, struct file *file)
+{
+       struct inode *inode = file_inode(file);
+       struct dax_device *daxdev;
+       int err;
 
-       /* read/write/splice/mmap passthrough only relevant for regular files */
-       if (!d_is_reg(file->f_path.dentry))
-               return d_is_dir(file->f_path.dentry) ? ERR_PTR(-EISDIR) : 
ERR_PTR(-EINVAL);
+       switch (inode->i_mode & S_IFMT) {
+       case S_IFREG:
+               /* TODO: relax CAP_SYS_ADMIN once backing files are visible to 
lsof */
+               if (!fc->passthrough || !capable(CAP_SYS_ADMIN))
+                       return -EPERM;
+
+               if (inode->i_sb->s_stack_depth >= fc->max_stack_depth)
+                       return -ELOOP;
+
+               fb->type = FUSE_BACKING_PATH;
+               fb->path = file->f_path;
+               path_get(&fb->path);
+               fb->cred = get_current_cred();
+               return 0;
+
+       case S_IFCHR:
+               daxdev = dax_dev_find(inode->i_rdev);
+               if (!daxdev)
+                       return -EINVAL;
+
+               err = -EPERM;
+               if (capable(CAP_SYS_RAWIO)) {
+                       err = fs_dax_get(daxdev, fb, &fuse_dax_holder_ops);
+                       if (!err) {
+                               fb->type = FUSE_BACKING_DAXDEV;
+                               fb->dax_dev = daxdev;
+                       }
+               }
+               put_dax(daxdev);
+               return err;
 
-       backing_sb = file_inode(file)->i_sb;
-       if (backing_sb->s_stack_depth >= fc->max_stack_depth)
-               return ERR_PTR(-ELOOP);
+       case S_IFDIR:
+               return -EISDIR;
+
+       default:
+               return -EINVAL;
+       }
+}
+
+static struct fuse_backing *fuse_backing_new(struct fuse_conn *fc, int fd)
+{
+       struct fuse_backing *fb __free(kfree) = kmalloc_obj(*fb);
+       int err;
 
-       fb = kmalloc_obj(struct fuse_backing);
        if (!fb)
                return ERR_PTR(-ENOMEM);
 
-       fb->file = get_file(file);
-       fb->cred = get_current_cred();
+       CLASS(fd_raw, f)(fd);
+       if (fd_empty(f))
+               return ERR_PTR(-EBADF);
+
+       err = fuse_backing_open_file(fc, fb, fd_file(f));
+       if (err)
+               return ERR_PTR(err);
+
        refcount_set(&fb->count, 1);
 
-       return fb;
+       return_ptr(fb);
 }
 
 int fuse_backing_open_64(struct fuse_conn *fc, struct fuse_backing_create_in 
*map)
@@ -143,22 +195,21 @@ int fuse_backing_open_64(struct fuse_conn *fc, struct 
fuse_backing_create_in *ma
        struct fuse_backing *fb;
        int res;
 
-       res = -EINVAL;
        if (map->padding || map->spare[0] || map->spare[1])
-               goto out;
+               return -EINVAL;
 
        if (!fc->backing_id_64)
-               goto out;
+               return -EINVAL;
 
        fb = fuse_backing_new(fc, map->fd);
-       res = PTR_ERR(fb);
-       if (!IS_ERR(fb)) {
-               fb->backing_id = map->backing_id;
-               res = fuse_backing_add_64(fc, fb);
-               if (res < 0)
-                       fuse_backing_free(fb);
-       }
-out:
+       if (IS_ERR(fb))
+               return PTR_ERR(fb);
+
+       fb->backing_id = map->backing_id;
+       res = fuse_backing_add_64(fc, fb);
+       if (res < 0)
+               fuse_backing_free(fb);
+
        return res;
 }
 
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index 1036c06fe35a..fb8e0a9fa4f6 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -297,7 +297,7 @@ static int fuse_open(struct inode *inode, struct file *file)
        if (!err) {
                if (is_truncate)
                        truncate_pagecache(inode, 0);
-               else if (!(ff->open_flags & FOPEN_KEEP_CACHE))
+               else if (!(ff->open_flags & FOPEN_KEEP_CACHE) && !IS_DAX(inode))
                        invalidate_inode_pages2(inode->i_mapping);
        }
 out_unlock:
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index 59afebc3dd2b..3e7ffb9bb2c3 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -89,10 +89,25 @@ struct fuse_submount_lookup {
        struct fuse_forget_link *forget;
 };
 
+enum fuse_backing_type {
+       FUSE_BACKING_PATH,
+       FUSE_BACKING_DAXDEV,
+};
+
 /* Container for data related to mapping to backing file */
 struct fuse_backing {
-       struct file *file;
-       const struct cred *cred;
+       enum fuse_backing_type type;
+
+       union {
+               struct {
+                       struct path path;
+                       const struct cred *cred;
+               };
+               struct {
+                       struct dax_device *dax_dev;
+                       bool dax_error;
+               };
+       };
        u64 backing_id;
        struct rhash_head hash_node;
        /* refcount */
@@ -1239,7 +1254,14 @@ void fuse_free_conn(struct fuse_conn *fc);
 
 /* dax.c */
 
-#define FUSE_IS_VDAX(inode) (IS_ENABLED(CONFIG_FUSE_VDAX) && IS_DAX(inode))
+static inline bool FUSE_IS_VDAX(struct inode *inode)
+{
+#ifdef CONFIG_FUSE_VDAX
+       return get_fuse_inode(inode)->vdax && IS_DAX(inode);
+#else
+       return false;
+#endif
+}
 
 ssize_t fuse_vdax_read_iter(struct kiocb *iocb, struct iov_iter *to);
 ssize_t fuse_vdax_write_iter(struct kiocb *iocb, struct iov_iter *from);
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index bb76bddfc241..d42c824fd6af 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -148,7 +148,7 @@ static void fuse_evict_inode(struct inode *inode)
        /* Will write inode on close/munmap and in all other dirtiers */
        WARN_ON(inode_state_read_once(inode) & I_DIRTY_INODE);
 
-       if (FUSE_IS_VDAX(inode))
+       if (IS_DAX(inode))
                dax_break_layout_final(inode);
 
        truncate_inode_pages_final(&inode->i_data);
@@ -403,6 +403,10 @@ static void fuse_init_submount_lookup(struct 
fuse_submount_lookup *sl,
        refcount_set(&sl->count, 1);
 }
 
+static const struct address_space_operations fuse_dax_aops = {
+       .dirty_folio    = noop_dirty_folio,
+};
+
 static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr,
                            struct fuse_conn *fc)
 {
@@ -430,6 +434,11 @@ static void fuse_init_inode(struct inode *inode, struct 
fuse_attr *attr,
         */
        if (!fc->posix_acl)
                inode->i_acl = inode->i_default_acl = ACL_DONT_CACHE;
+
+       if ((attr->flags & FUSE_ATTR_DAX) && !fc->vdax) {
+               inode->i_flags |= S_DAX;
+               inode->i_data.a_ops = &fuse_dax_aops;
+       }
 }
 
 static int fuse_inode_eq(struct inode *inode, void *_nodeidp)
diff --git a/fs/fuse/passthrough.c b/fs/fuse/passthrough.c
index 4894842ad6d0..e9ab1aea34e2 100644
--- a/fs/fuse/passthrough.c
+++ b/fs/fuse/passthrough.c
@@ -156,9 +156,11 @@ int fuse_passthrough_open(struct file *file, struct 
fuse_backing *fb)
        struct fuse_file *ff = file->private_data;
        struct file *backing_file;
 
+       if (fb->type != FUSE_BACKING_PATH)
+               return fuse_EIO("invalid backing type");
+
        /* Allocate backing file per fuse file to store fuse path */
-       backing_file = backing_file_open(file, file->f_flags,
-                                        &fb->file->f_path, fb->cred);
+       backing_file = backing_file_open(file, file->f_flags, &fb->path, 
fb->cred);
        if (IS_ERR(backing_file))
                return fuse_EIO("failed to open backing file (%ld)", 
PTR_ERR(backing_file));
 
-- 
2.54.0


Reply via email to