FROMLIST: fuse: Definitions and ioctl for passthrough

Expose the FUSE_PASSTHROUGH interface to user space and declare all the
basic data structures and functions as the skeleton on top of which the
FUSE passthrough functionality will be built.

As part of this, introduce the new FUSE passthrough ioctl, which allows
the FUSE daemon to specify a direct connection between a FUSE file and a
lower file system file. Such ioctl requires user space to pass the file
descriptor of one of its opened files through the fuse_passthrough_out
data structure introduced in this patch. This structure includes extra
fields for possible future extensions.
Also, add the passthrough functions for the set-up and tear-down of the
data structures and locks that will be used both when fuse_conns and
fuse_files are created/deleted.

Bug: 179164095
Link: https://lore.kernel.org/lkml/20210125153057.3623715-4-balsini@android.com/
Signed-off-by: Alessio Balsini <balsini@android.com>
Change-Id: I732532581348adadda5b5048a9346c2b0868d539
Signed-off-by: Alessio Balsini <balsini@google.com>
This commit is contained in:
Alessio Balsini
2021-01-25 17:02:28 +00:00
committed by Hridaya Prajapati
parent 759f072297
commit 0d86dc020b
8 changed files with 93 additions and 3 deletions

View File

@@ -6,3 +6,4 @@ obj-$(CONFIG_FUSE_FS) += fuse.o
obj-$(CONFIG_CUSE) += cuse.o
fuse-objs := dev.o dir.o file.o inode.o control.o xattr.o acl.o
fuse-objs += passthrough.o

View File

@@ -2293,6 +2293,7 @@ static long fuse_dev_ioctl(struct file *file, unsigned int cmd,
int res;
int oldfd;
struct fuse_dev *fud = NULL;
struct fuse_passthrough_out pto;
if (_IOC_TYPE(cmd) != FUSE_DEV_IOC_MAGIC)
return -EINVAL;
@@ -2323,6 +2324,17 @@ static long fuse_dev_ioctl(struct file *file, unsigned int cmd,
}
}
break;
case _IOC_NR(FUSE_DEV_IOC_PASSTHROUGH_OPEN):
res = -EFAULT;
if (!copy_from_user(&pto,
(struct fuse_passthrough_out __user *)arg,
sizeof(pto))) {
res = -EINVAL;
fud = fuse_get_dev(file);
if (fud)
res = fuse_passthrough_open(fud, &pto);
}
break;
default:
res = -ENOTTY;
break;

View File

@@ -508,6 +508,7 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
ff->fh = outopen.fh;
ff->nodeid = outentry.nodeid;
ff->open_flags = outopen.open_flags;
fuse_passthrough_setup(fc, ff, &outopen);
inode = fuse_iget(dir->i_sb, outentry.nodeid, outentry.generation,
&outentry.attr, entry_attr_timeout(&outentry), 0);
if (!inode) {

View File

@@ -136,7 +136,7 @@ int fuse_do_open(struct fuse_conn *fc, u64 nodeid, struct file *file,
if (!err) {
ff->fh = outarg.fh;
ff->open_flags = outarg.open_flags;
fuse_passthrough_setup(fc, ff, &outarg);
} else if (err != -ENOSYS || isdir) {
fuse_file_free(ff);
return err;
@@ -264,6 +264,8 @@ void fuse_release_common(struct file *file, bool isdir)
struct fuse_req *req = ff->reserved_req;
int opcode = isdir ? FUSE_RELEASEDIR : FUSE_RELEASE;
fuse_passthrough_release(&ff->passthrough);
fuse_prepare_release(ff, file->f_flags, opcode);
if (ff->flock) {

View File

@@ -124,6 +124,14 @@ enum {
struct fuse_conn;
/**
* Reference to lower filesystem file for read/write operations handled in
* passthrough mode
*/
struct fuse_passthrough {
struct file *filp;
};
/** FUSE specific file data */
struct fuse_file {
/** Fuse connection for this file */
@@ -150,6 +158,9 @@ struct fuse_file {
/** Entry on inode's write_files list */
struct list_head write_entry;
/** Container for data related to the passthrough functionality */
struct fuse_passthrough passthrough;
/** RB node to be linked on fuse_conn->polled_files */
struct rb_node polled_node;
@@ -650,6 +661,9 @@ struct fuse_conn {
/** Allow other than the mounter user to access the filesystem ? */
unsigned allow_other:1;
/** Passthrough mode for read/write IO */
unsigned int passthrough:1;
/** The number of requests waiting for completion */
atomic_t num_waiting;
@@ -688,6 +702,12 @@ struct fuse_conn {
/** List of device instances belonging to this connection */
struct list_head devices;
/** IDR for passthrough requests */
struct idr passthrough_req;
/** Protects passthrough_req */
spinlock_t passthrough_req_lock;
};
static inline struct fuse_conn *get_fuse_conn_super(struct super_block *sb)
@@ -1019,4 +1039,11 @@ struct posix_acl;
struct posix_acl *fuse_get_acl(struct inode *inode, int type);
int fuse_set_acl(struct inode *inode, struct posix_acl *acl, int type);
/* passthrough.c */
int fuse_passthrough_open(struct fuse_dev *fud,
struct fuse_passthrough_out *pto);
int fuse_passthrough_setup(struct fuse_conn *fc, struct fuse_file *ff,
struct fuse_open_out *openarg);
void fuse_passthrough_release(struct fuse_passthrough *passthrough);
#endif /* _FS_FUSE_I_H */

View File

@@ -615,6 +615,7 @@ void fuse_conn_init(struct fuse_conn *fc, struct user_namespace *user_ns)
{
memset(fc, 0, sizeof(*fc));
spin_lock_init(&fc->lock);
spin_lock_init(&fc->passthrough_req_lock);
init_rwsem(&fc->killsb);
refcount_set(&fc->count, 1);
atomic_set(&fc->dev_count, 1);
@@ -624,6 +625,7 @@ void fuse_conn_init(struct fuse_conn *fc, struct user_namespace *user_ns)
INIT_LIST_HEAD(&fc->bg_queue);
INIT_LIST_HEAD(&fc->entry);
INIT_LIST_HEAD(&fc->devices);
idr_init(&fc->passthrough_req);
atomic_set(&fc->num_waiting, 0);
fc->max_background = FUSE_DEFAULT_MAX_BACKGROUND;
fc->congestion_threshold = FUSE_DEFAULT_CONGESTION_THRESHOLD;
@@ -936,6 +938,12 @@ static void process_init_reply(struct fuse_conn *fc, struct fuse_req *req)
}
if (arg->flags & FUSE_ABORT_ERROR)
fc->abort_err = 1;
if (arg->flags & FUSE_PASSTHROUGH) {
fc->passthrough = 1;
/* Prevent further stacking */
fc->sb->s_stack_depth =
FILESYSTEM_MAX_STACK_DEPTH;
}
} else {
ra_pages = fc->max_read / PAGE_SIZE;
fc->no_lock = 1;
@@ -967,7 +975,7 @@ static void fuse_send_init(struct fuse_conn *fc, struct fuse_req *req)
FUSE_DO_READDIRPLUS | FUSE_READDIRPLUS_AUTO | FUSE_ASYNC_DIO |
FUSE_WRITEBACK_CACHE | FUSE_NO_OPEN_SUPPORT |
FUSE_PARALLEL_DIROPS | FUSE_HANDLE_KILLPRIV | FUSE_POSIX_ACL |
FUSE_ABORT_ERROR;
FUSE_ABORT_ERROR | FUSE_PASSTHROUGH;
req->in.h.opcode = FUSE_INIT;
req->in.numargs = 1;
req->in.args[0].size = sizeof(*arg);
@@ -983,9 +991,16 @@ static void fuse_send_init(struct fuse_conn *fc, struct fuse_req *req)
fuse_request_send_background(fc, req);
}
static int free_fuse_passthrough(int id, void *p, void *data)
{
return 0;
}
static void fuse_free_conn(struct fuse_conn *fc)
{
WARN_ON(!list_empty(&fc->devices));
idr_for_each(&fc->passthrough_req, free_fuse_passthrough, NULL);
idr_destroy(&fc->passthrough_req);
kfree_rcu(fc, rcu);
}

21
fs/fuse/passthrough.c Normal file
View File

@@ -0,0 +1,21 @@
// SPDX-License-Identifier: GPL-2.0
#include "fuse_i.h"
#include <linux/fuse.h>
int fuse_passthrough_open(struct fuse_dev *fud,
struct fuse_passthrough_out *pto)
{
return -EINVAL;
}
int fuse_passthrough_setup(struct fuse_conn *fc, struct fuse_file *ff,
struct fuse_open_out *openarg)
{
return -EINVAL;
}
void fuse_passthrough_release(struct fuse_passthrough *passthrough)
{
}

View File

@@ -274,6 +274,7 @@ struct fuse_file_lock {
#define FUSE_HANDLE_KILLPRIV (1 << 19)
#define FUSE_POSIX_ACL (1 << 20)
#define FUSE_ABORT_ERROR (1 << 21)
#define FUSE_PASSTHROUGH (1 << 31)
/**
* CUSE INIT request/reply flags
@@ -506,7 +507,7 @@ struct fuse_create_in {
struct fuse_open_out {
uint64_t fh;
uint32_t open_flags;
uint32_t padding;
uint32_t passthrough_fh;
};
struct fuse_release_in {
@@ -707,6 +708,14 @@ struct fuse_in_header {
uint32_t padding;
};
/* fuse_passthrough_out for passthrough V1 */
struct fuse_passthrough_out {
uint32_t fd;
/* For future implementation */
uint32_t len;
void *vec;
};
struct fuse_out_header {
uint32_t len;
int32_t error;
@@ -784,6 +793,8 @@ struct fuse_notify_retrieve_in {
/* Device ioctls: */
#define FUSE_DEV_IOC_MAGIC 229
#define FUSE_DEV_IOC_CLONE _IOR(FUSE_DEV_IOC_MAGIC, 0, uint32_t)
/* 127 is reserved for the V1 interface implementation in Android */
#define FUSE_DEV_IOC_PASSTHROUGH_OPEN _IOW(FUSE_DEV_IOC_MAGIC, 127, struct fuse_passthrough_out)
struct fuse_lseek_in {
uint64_t fh;