Add support for opening and closing the backing file on a passthrough directory. * Add backing file to fuse file * Add fuse_inode_has_backing - this is how we will detect whether to use traditional fuse operations or the added backing file operations * Add backing operations to open, flush and close Signed-off-by: Paul Lawrence <paullawrence@xxxxxxxxxx> --- fs/fuse/backing.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++ fs/fuse/dir.c | 16 ----------- fs/fuse/file.c | 34 ++++++++++++++++-------- fs/fuse/fuse_i.h | 39 +++++++++++++++++++++++++++ 4 files changed, 130 insertions(+), 27 deletions(-) diff --git a/fs/fuse/backing.c b/fs/fuse/backing.c index 1dcc617bf660..04265bd06695 100644 --- a/fs/fuse/backing.c +++ b/fs/fuse/backing.c @@ -6,6 +6,64 @@ #include "fuse_i.h" +#include <linux/backing-file.h> + +int fuse_open_backing(struct inode *inode, struct file *file, bool isdir) +{ + struct fuse_mount *fm = get_fuse_mount(inode); + struct fuse_file *ff; + int retval; + int mask; + union fuse_dentry *fd = get_fuse_dentry(file->f_path.dentry); + struct file *backing_file; + uint32_t flags = file->f_flags & ~(O_CREAT | O_EXCL | O_NOCTTY); + + ff = fuse_file_alloc(fm, true); + if (!ff) + return -ENOMEM; + + switch (flags & O_ACCMODE) { + case O_RDONLY: + mask = MAY_READ; + break; + + case O_WRONLY: + mask = MAY_WRITE; + break; + + case O_RDWR: + mask = MAY_READ | MAY_WRITE; + break; + + default: + retval = -EINVAL; + goto outerr; + } + + retval = inode_permission(&nop_mnt_idmap, + get_fuse_inode(inode)->backing_inode, mask); + if (retval) + goto outerr; + + backing_file = backing_file_open(&file->f_path, file->f_flags, + &fd->backing_path, current_cred()); + + if (IS_ERR(backing_file)) { + retval = PTR_ERR(backing_file); + goto outerr; + } + + ff->backing_file = backing_file; + ff->nodeid = get_fuse_inode(inode)->nodeid; + file->private_data = ff; + return 0; + +outerr: + if (retval) + fuse_file_free(ff); + return retval; +} + int fuse_handle_backing(struct fuse_entry_backing *feb, struct inode **backing_inode, struct path *backing_path) { @@ -27,3 +85,13 @@ int fuse_handle_backing(struct fuse_entry_backing *feb, return 0; } + +int fuse_flush_backing(struct file *file, fl_owner_t id) +{ + struct fuse_file *fuse_file = file->private_data; + struct file *backing_file = fuse_file->backing_file; + + if (backing_file->f_op->flush) + return backing_file->f_op->flush(backing_file, id); + return 0; +} diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 909463fae94d..658898f324b5 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -44,18 +44,7 @@ static inline u64 fuse_dentry_time(const struct dentry *entry) { return (u64)entry->d_fsdata; } - #else -union fuse_dentry { - struct { - u64 time; -#ifdef CONFIG_FUSE_PASSTHROUGH_DIR - struct path backing_path; -#endif - }; - struct rcu_head rcu; -}; - static inline void __fuse_dentry_settime(struct dentry *dentry, u64 time) { ((union fuse_dentry *) dentry->d_fsdata)->time = time; @@ -65,11 +54,6 @@ static inline u64 fuse_dentry_time(const struct dentry *entry) { return ((union fuse_dentry *) entry->d_fsdata)->time; } - -static inline union fuse_dentry *get_fuse_dentry(const struct dentry *entry) -{ - return entry->d_fsdata; -} #endif static void fuse_dentry_settime(struct dentry *dentry, u64 time) diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 754378dd9f71..0cd0a94073c7 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -21,6 +21,7 @@ #include <linux/filelock.h> #include <linux/splice.h> #include <linux/task_io_accounting_ops.h> +#include <linux/file.h> static int fuse_send_open(struct fuse_mount *fm, u64 nodeid, unsigned int open_flags, int opcode, @@ -105,19 +106,24 @@ static void fuse_file_put(struct fuse_file *ff, bool sync) struct fuse_release_args *ra = &ff->args->release_args; struct fuse_args *args = (ra ? &ra->args : NULL); - if (ra && ra->inode) - fuse_file_io_release(ff, ra->inode); - - if (!args) { - /* Do nothing when server does not implement 'open' */ - } else if (sync) { - fuse_simple_request(ff->fm, args); + if (ff->backing_file) { fuse_release_end(ff->fm, args, 0); + fput(ff->backing_file); } else { - args->end = fuse_release_end; - if (fuse_simple_background(ff->fm, args, - GFP_KERNEL | __GFP_NOFAIL)) - fuse_release_end(ff->fm, args, -ENOTCONN); + if (ra && ra->inode) + fuse_file_io_release(ff, ra->inode); + + if (!args) { + /* Do nothing when server does not implement 'open' */ + } else if (sync) { + fuse_simple_request(ff->fm, args); + fuse_release_end(ff->fm, args, 0); + } else { + args->end = fuse_release_end; + if (fuse_simple_background(ff->fm, args, + GFP_KERNEL | __GFP_NOFAIL)) + fuse_release_end(ff->fm, args, -ENOTCONN); + } } kfree(ff); } @@ -248,6 +254,9 @@ static int fuse_open(struct inode *inode, struct file *file) if (err) return err; + if (fuse_inode_has_backing(inode)) + return fuse_open_backing(inode, file, false); + if (is_wb_truncate || dax_truncate) inode_lock(inode); @@ -522,6 +531,9 @@ static int fuse_flush(struct file *file, fl_owner_t id) FUSE_ARGS(args); int err; + if (fuse_inode_has_backing(file->f_inode)) + return fuse_flush_backing(file, id); + if (fuse_is_bad(inode)) return -EIO; diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 1dc04bc6ac49..a27c05810bab 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -96,6 +96,25 @@ struct fuse_submount_lookup { struct fuse_forget_link *forget; }; + +/** FUSE specific dentry data */ +#if BITS_PER_LONG < 64 || defined(CONFIG_FUSE_PASSTHROUGH_DIR) +union fuse_dentry { + struct { + u64 time; +#ifdef CONFIG_FUSE_PASSTHROUGH_DIR + struct path backing_path; +#endif + }; + struct rcu_head rcu; +}; + +static inline union fuse_dentry *get_fuse_dentry(const struct dentry *entry) +{ + return entry->d_fsdata; +} +#endif + /** Container for data related to mapping to backing file */ struct fuse_backing { struct file *file; @@ -287,6 +306,14 @@ struct fuse_file { } readdir; +#ifdef CONFIG_FUSE_PASSTHROUGH_DIR + /** + * TODO: Reconcile with passthrough file + * backing file when in bpf mode + */ + struct file *backing_file; +#endif + /** RB node to be linked on fuse_conn->polled_files */ struct rb_node polled_node; @@ -1597,4 +1624,16 @@ extern void fuse_sysctl_unregister(void); /* backing.c */ int fuse_handle_backing(struct fuse_entry_backing *feb, struct inode **backing_inode, struct path *backing_path); + + +static inline bool fuse_inode_has_backing(struct inode *inode) +{ + struct fuse_inode *fuse_inode = get_fuse_inode(inode); + + return fuse_inode && fuse_inode->backing_inode; +} + +int fuse_open_backing(struct inode *inode, struct file *file, bool isdir); +int fuse_flush_backing(struct file *file, fl_owner_t id); + #endif /* _FS_FUSE_I_H */ -- 2.49.0.1112.g889b7c5bd8-goog