From patchwork Thu Sep 24 13:13:15 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alessio Balsini X-Patchwork-Id: 11797255 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 0D658139F for ; Thu, 24 Sep 2020 13:13:28 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id CE733206B8 for ; Thu, 24 Sep 2020 13:13:27 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=android.com header.i=@android.com header.b="uJnlAv/a" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727970AbgIXNN0 (ORCPT ); Thu, 24 Sep 2020 09:13:26 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:46142 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727891AbgIXNNZ (ORCPT ); Thu, 24 Sep 2020 09:13:25 -0400 Received: from mail-wm1-x342.google.com (mail-wm1-x342.google.com [IPv6:2a00:1450:4864:20::342]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 582CCC0613CE for ; Thu, 24 Sep 2020 06:13:25 -0700 (PDT) Received: by mail-wm1-x342.google.com with SMTP id w2so3494396wmi.1 for ; Thu, 24 Sep 2020 06:13:25 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=android.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=OBqvHPVRVJk3ZG/oqmQk5EP9lqOerQ2Xy+/srUo/wiA=; b=uJnlAv/aL1XmuplMzUvcuFvtfX8FJcXsfcQ0tGZX5elBjleB4pW0N8kFqc3qOKw2id JEeg85Lwp9w1EPvA0JfS56hn9SQMsYvE1X14ESMEGHkPbUzHLbVbXaixJUeBFZfHs5eV k8U+rIkjiFWQ6zVCP6SFBqwzdohotrPG6AuwsUxlehabP/SGYB1QJNAcERujhcGhmeGY RMNqS+yh+CoDUjTEikr7hnOxCG2UPmLaikRbZ5JDAMCBBiloi404F73zE/CoAJO7Le1S Fq2TUXHu5HKYnkD24iYibBVdYsHfh5jD8fAuapB34ZzKcXpScU85FC1wKY7isHOL0ASw Cnig== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=OBqvHPVRVJk3ZG/oqmQk5EP9lqOerQ2Xy+/srUo/wiA=; b=ckkbxNUL6l+C3EP318MB1r94X3S1O+GOAFVMIMADjtIVLXI35lbuH9luvOqTAxkcs+ 0ZXdv77RKOQ/rDRoG9AsaQTb8kjqO+wDPMHNzZ7y4gIC8Qv3f9IQN2w2RdwDKP2TuqIQ 45cz1m27yw3Em2zWjwV9g7dKFPog9hTLU/VPkl/ZJbUbCt+MAtf924Dprnlqo9Wyjcfx SmI+2WeGHpjONG2xpkQnmjLBLangy4fjvTENMcz6JMBubvgXj3oAL4JUNftbtaxJluD/ tvK7juEz7yJ7fq3FxVOS94i47TsMEL212xOoyRQl+s1YrEu3FuDGNLGp0dpYtUb22bMu bovA== X-Gm-Message-State: AOAM5312SOYToD6KkrtJj84OBlzrZdXd0yLu0jVcCTB0ZcWd7nQxGTOj hsfTC4QrRUp36tT0k+D4bpICjQ== X-Google-Smtp-Source: ABdhPJxye7QzFtLjQWsUXkCBuiHSS0YdQllP6GH1279i9l5TAnnWnhbtL6m7W4HYsW+eDiM445QJ2Q== X-Received: by 2002:a7b:c92c:: with SMTP id h12mr4725630wml.121.1600953203881; Thu, 24 Sep 2020 06:13:23 -0700 (PDT) Received: from balsini.lon.corp.google.com ([2a00:79e0:d:210:7220:84ff:fe09:7d5c]) by smtp.gmail.com with ESMTPSA id k22sm3805044wrd.29.2020.09.24.06.13.23 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 24 Sep 2020 06:13:23 -0700 (PDT) From: Alessio Balsini To: Miklos Szeredi Cc: Akilesh Kailash , Amir Goldstein , Antonio SJ Musumeci , David Anderson , Giuseppe Scrivano , Jann Horn , Jens Axboe , Martijn Coenen , Palmer Dabbelt , Paul Lawrence , Stefano Duo , Zimuzo Ezeozue , fuse-devel@lists.sourceforge.net, kernel-team@android.com, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH V9 1/4] fuse: Definitions and ioctl() for passthrough Date: Thu, 24 Sep 2020 14:13:15 +0100 Message-Id: <20200924131318.2654747-2-balsini@android.com> X-Mailer: git-send-email 2.28.0.681.g6f77f65b4e-goog In-Reply-To: <20200924131318.2654747-1-balsini@android.com> References: <20200924131318.2654747-1-balsini@android.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org Introduce the new FUSE passthrough ioctl(), which allows userspace to specify a direct connection between a FUSE file and a lower file system file. Such ioctl() requires userspace to specify: - the file descriptor of one of its opened files, - the unique identifier of the FUSE request associated with a pending open/create operation, both encapsulated into a fuse_passthrough_out data structure. The ioctl() will search for the pending FUSE request matching the unique identifier, and update the passthrough file pointer of the request with the file pointer referenced by the passed file descriptor. When that pending FUSE request is handled, the passthrough file pointer is copied to the fuse_file data structure, so that the link between FUSE and lower file system is consolidated. In order for the passthrough mode to be successfully activated, the lower file system file must implement both read_ and write_iter file operations. This extra check avoids special pseudofiles to be targets for this feature. An additional enforced limitation is that when FUSE passthrough is enabled, no further file system stacking is allowed. Signed-off-by: Alessio Balsini --- fs/fuse/Makefile | 1 + fs/fuse/dev.c | 57 +++++++++++++++++++++++++++++++++++---- fs/fuse/dir.c | 2 ++ fs/fuse/file.c | 17 +++++++++--- fs/fuse/fuse_i.h | 14 ++++++++++ fs/fuse/inode.c | 9 ++++++- fs/fuse/passthrough.c | 55 +++++++++++++++++++++++++++++++++++++ include/uapi/linux/fuse.h | 12 ++++++++- 8 files changed, 156 insertions(+), 11 deletions(-) create mode 100644 fs/fuse/passthrough.c diff --git a/fs/fuse/Makefile b/fs/fuse/Makefile index 3e8cebfb59b7..6971454a2bdf 100644 --- a/fs/fuse/Makefile +++ b/fs/fuse/Makefile @@ -8,4 +8,5 @@ obj-$(CONFIG_CUSE) += cuse.o obj-$(CONFIG_VIRTIO_FS) += virtiofs.o fuse-objs := dev.o dir.o file.o inode.o control.o xattr.o acl.o readdir.o +fuse-objs += passthrough.o virtiofs-y += virtio_fs.o diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 02b3c36b3676..c31e6c30fabf 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -2219,21 +2219,53 @@ static int fuse_device_clone(struct fuse_conn *fc, struct file *new) return 0; } +static int fuse_passthrough_open(struct fuse_dev *fud, + struct fuse_passthrough_out *pto) +{ + int ret; + struct fuse_req *req; + struct fuse_pqueue *fpq = &fud->pq; + struct fuse_conn *fc = fud->fc; + + if (!fc->passthrough) + return -EPERM; + + /* This field is reserved for future use */ + if (pto->len != 0) + return -EINVAL; + + spin_lock(&fpq->lock); + req = request_find(fpq, pto->unique & ~FUSE_INT_REQ_BIT); + if (!req) { + spin_unlock(&fpq->lock); + return -ENOENT; + } + __fuse_get_request(req); + spin_unlock(&fpq->lock); + + ret = fuse_passthrough_setup(req, pto->fd); + + fuse_put_request(fc, req); + return ret; +} + static long fuse_dev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { - int err = -ENOTTY; - - if (cmd == FUSE_DEV_IOC_CLONE) { - int oldfd; + int err; + int oldfd; + struct fuse_dev *fud; + struct fuse_passthrough_out pto; + switch (cmd) { + case FUSE_DEV_IOC_CLONE: err = -EFAULT; if (!get_user(oldfd, (__u32 __user *) arg)) { struct file *old = fget(oldfd); err = -EINVAL; if (old) { - struct fuse_dev *fud = NULL; + fud = NULL; /* * Check against file->f_op because CUSE @@ -2251,6 +2283,21 @@ static long fuse_dev_ioctl(struct file *file, unsigned int cmd, fput(old); } } + break; + case FUSE_DEV_IOC_PASSTHROUGH_OPEN: + err = -EFAULT; + if (!copy_from_user(&pto, + (struct fuse_passthrough_out __user *)arg, + sizeof(pto))) { + err = -EINVAL; + fud = fuse_get_dev(file); + if (fud) + err = fuse_passthrough_open(fud, &pto); + } + break; + default: + err = -ENOTTY; + break; } return err; } diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 26f028bc760b..531de0c5c9e8 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -477,6 +477,7 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, args.out_args[0].value = &outentry; args.out_args[1].size = sizeof(outopen); args.out_args[1].value = &outopen; + args.passthrough_filp = NULL; err = fuse_simple_request(fc, &args); if (err) goto out_free_ff; @@ -489,6 +490,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; + ff->passthrough_filp = args.passthrough_filp; inode = fuse_iget(dir->i_sb, outentry.nodeid, outentry.generation, &outentry.attr, entry_attr_timeout(&outentry), 0); if (!inode) { diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 83d917f7e542..6c0ec742ce74 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -33,10 +33,12 @@ static struct page **fuse_pages_alloc(unsigned int npages, gfp_t flags, } static int fuse_send_open(struct fuse_conn *fc, u64 nodeid, struct file *file, - int opcode, struct fuse_open_out *outargp) + int opcode, struct fuse_open_out *outargp, + struct file **passthrough_filp) { struct fuse_open_in inarg; FUSE_ARGS(args); + int ret; memset(&inarg, 0, sizeof(inarg)); inarg.flags = file->f_flags & ~(O_CREAT | O_EXCL | O_NOCTTY); @@ -51,7 +53,10 @@ static int fuse_send_open(struct fuse_conn *fc, u64 nodeid, struct file *file, args.out_args[0].size = sizeof(*outargp); args.out_args[0].value = outargp; - return fuse_simple_request(fc, &args); + ret = fuse_simple_request(fc, &args); + *passthrough_filp = args.passthrough_filp; + + return ret; } struct fuse_release_args { @@ -144,14 +149,16 @@ int fuse_do_open(struct fuse_conn *fc, u64 nodeid, struct file *file, /* Default for no-open */ ff->open_flags = FOPEN_KEEP_CACHE | (isdir ? FOPEN_CACHE_DIR : 0); if (isdir ? !fc->no_opendir : !fc->no_open) { + struct file *passthrough_filp; struct fuse_open_out outarg; int err; - err = fuse_send_open(fc, nodeid, file, opcode, &outarg); + err = fuse_send_open(fc, nodeid, file, opcode, &outarg, + &passthrough_filp); if (!err) { ff->fh = outarg.fh; ff->open_flags = outarg.open_flags; - + ff->passthrough_filp = passthrough_filp; } else if (err != -ENOSYS) { fuse_file_free(ff); return err; @@ -281,6 +288,8 @@ void fuse_release_common(struct file *file, bool isdir) struct fuse_release_args *ra = ff->release_args; int opcode = isdir ? FUSE_RELEASEDIR : FUSE_RELEASE; + fuse_passthrough_release(ff); + fuse_prepare_release(fi, ff, file->f_flags, opcode); if (ff->flock) { diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 740a8a7d7ae6..6c5166447905 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -208,6 +208,12 @@ struct fuse_file { } readdir; + /** + * Reference to lower filesystem file for read/write operations + * handled in pass-through mode + */ + struct file *passthrough_filp; + /** RB node to be linked on fuse_conn->polled_files */ struct rb_node polled_node; @@ -250,6 +256,8 @@ struct fuse_args { bool page_zeroing:1; bool page_replace:1; bool may_block:1; + /** Lower filesystem file pointer used in pass-through mode */ + struct file *passthrough_filp; struct fuse_in_arg in_args[3]; struct fuse_arg out_args[2]; void (*end)(struct fuse_conn *fc, struct fuse_args *args, int error); @@ -720,6 +728,9 @@ struct fuse_conn { /* Do not show mount options */ unsigned int no_mount_options:1; + /** Pass-through mode for read/write IO */ + unsigned int passthrough:1; + /** The number of requests waiting for completion */ atomic_t num_waiting; @@ -1093,4 +1104,7 @@ unsigned int fuse_len_args(unsigned int numargs, struct fuse_arg *args); u64 fuse_get_unique(struct fuse_iqueue *fiq); void fuse_free_conn(struct fuse_conn *fc); +int fuse_passthrough_setup(struct fuse_req *req, unsigned int fd); +void fuse_passthrough_release(struct fuse_file *ff); + #endif /* _FS_FUSE_I_H */ diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index bba747520e9b..eb223130a917 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -965,6 +965,12 @@ static void process_init_reply(struct fuse_conn *fc, struct fuse_args *args, min_t(unsigned int, FUSE_MAX_MAX_PAGES, max_t(unsigned int, arg->max_pages, 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; @@ -1002,7 +1008,8 @@ void fuse_send_init(struct fuse_conn *fc) FUSE_WRITEBACK_CACHE | FUSE_NO_OPEN_SUPPORT | FUSE_PARALLEL_DIROPS | FUSE_HANDLE_KILLPRIV | FUSE_POSIX_ACL | FUSE_ABORT_ERROR | FUSE_MAX_PAGES | FUSE_CACHE_SYMLINKS | - FUSE_NO_OPENDIR_SUPPORT | FUSE_EXPLICIT_INVAL_DATA; + FUSE_NO_OPENDIR_SUPPORT | FUSE_EXPLICIT_INVAL_DATA | + FUSE_PASSTHROUGH; ia->args.opcode = FUSE_INIT; ia->args.in_numargs = 1; ia->args.in_args[0].size = sizeof(ia->in); diff --git a/fs/fuse/passthrough.c b/fs/fuse/passthrough.c new file mode 100644 index 000000000000..86ab4eafa7bf --- /dev/null +++ b/fs/fuse/passthrough.c @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include "fuse_i.h" + +int fuse_passthrough_setup(struct fuse_req *req, unsigned int fd) +{ + int ret; + int fs_stack_depth; + struct file *passthrough_filp; + struct inode *passthrough_inode; + struct super_block *passthrough_sb; + + /* Passthrough mode can only be enabled at file open/create time */ + if (req->in.h.opcode != FUSE_OPEN && req->in.h.opcode != FUSE_CREATE) { + pr_err("FUSE: invalid OPCODE for request.\n"); + return -EINVAL; + } + + passthrough_filp = fget(fd); + if (!passthrough_filp) { + pr_err("FUSE: invalid file descriptor for passthrough.\n"); + return -EINVAL; + } + + ret = -EINVAL; + if (!passthrough_filp->f_op->read_iter || + !passthrough_filp->f_op->write_iter) { + pr_err("FUSE: passthrough file misses file operations.\n"); + goto out; + } + + passthrough_inode = file_inode(passthrough_filp); + passthrough_sb = passthrough_inode->i_sb; + fs_stack_depth = passthrough_sb->s_stack_depth + 1; + ret = -EEXIST; + if (fs_stack_depth > FILESYSTEM_MAX_STACK_DEPTH) { + pr_err("FUSE: maximum fs stacking depth exceeded for passthrough\n"); + goto out; + } + + req->args->passthrough_filp = passthrough_filp; + return 0; +out: + fput(passthrough_filp); + return ret; +} + +void fuse_passthrough_release(struct fuse_file *ff) +{ + if (!ff->passthrough_filp) + return; + + fput(ff->passthrough_filp); + ff->passthrough_filp = NULL; +} diff --git a/include/uapi/linux/fuse.h b/include/uapi/linux/fuse.h index 373cada89815..0cd9fd83374a 100644 --- a/include/uapi/linux/fuse.h +++ b/include/uapi/linux/fuse.h @@ -342,6 +342,7 @@ struct fuse_file_lock { #define FUSE_NO_OPENDIR_SUPPORT (1 << 24) #define FUSE_EXPLICIT_INVAL_DATA (1 << 25) #define FUSE_MAP_ALIGNMENT (1 << 26) +#define FUSE_PASSTHROUGH (1 << 27) /** * CUSE INIT request/reply flags @@ -794,6 +795,14 @@ struct fuse_in_header { uint32_t padding; }; +struct fuse_passthrough_out { + uint64_t unique; + uint32_t fd; + /* For future implementation */ + uint32_t len; + void *vec; +}; + struct fuse_out_header { uint32_t len; int32_t error; @@ -869,7 +878,8 @@ struct fuse_notify_retrieve_in { }; /* Device ioctls: */ -#define FUSE_DEV_IOC_CLONE _IOR(229, 0, uint32_t) +#define FUSE_DEV_IOC_CLONE _IOR(229, 0, uint32_t) +#define FUSE_DEV_IOC_PASSTHROUGH_OPEN _IOW(229, 1, struct fuse_passthrough_out) struct fuse_lseek_in { uint64_t fh; From patchwork Thu Sep 24 13:13:16 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alessio Balsini X-Patchwork-Id: 11797257 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id A8F4C618 for ; Thu, 24 Sep 2020 13:13:31 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 84D02206B8 for ; Thu, 24 Sep 2020 13:13:31 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=android.com header.i=@android.com header.b="aKDNhQ9x" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727985AbgIXNNa (ORCPT ); Thu, 24 Sep 2020 09:13:30 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:46148 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727966AbgIXNN0 (ORCPT ); Thu, 24 Sep 2020 09:13:26 -0400 Received: from mail-wm1-x342.google.com (mail-wm1-x342.google.com [IPv6:2a00:1450:4864:20::342]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id DC5ACC0613D4 for ; Thu, 24 Sep 2020 06:13:25 -0700 (PDT) Received: by mail-wm1-x342.google.com with SMTP id s13so3473482wmh.4 for ; Thu, 24 Sep 2020 06:13:25 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=android.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=JdyGUVuKv4uLTP1IH/WHroPyoizsYQ5O3xwWtSel+KA=; b=aKDNhQ9xaE7F0uUzW0kf0/mOfK+rPpwk1pTpxTAV+GegmPnu6q9Sp/YzeyQ9/JqSkW qxG/SjGu/Q1DYNKG96m6maPT41AKHfM0OV+VSrQwU1RLax+WIEUcHti50uK38USvhHOM PTI5qSc479kGMTxO98fo9wmzsSq02dH7ythqEBhAl0i9RjBPMDLorlKbFRZ5r7yedky0 cJCXVSiD75rndCAuuYnDOBO9WY6oWIzDGBBcnLCXR5OND/3OmZkelzRlOqXaJHeG59A9 i6OYktIcGLrZshWEsQkLuW/QBhJ6SllxuoIlbBA+oLM0rQ9qklTLCT1brH3Y5Pu6Fgi+ 3zug== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=JdyGUVuKv4uLTP1IH/WHroPyoizsYQ5O3xwWtSel+KA=; b=fo3mPoUMXaywAlgVzII9HpvwZOhBk/YSaPvDIVGPQORsI+deTmiD0Qg3Bsc7RbK0bw 6qpBrg8R4QWFzkNYfsrZ4QlmRdXI/EWnhf+vwBiOO5xQDW2Q2+g9zm7eTGK6D/xjPB/B LmYFXYnPHanZzP1vJJx09VcIVsAvsn17eI/tO0HbdAM0HVccrL7B8BT1uh65ZsL3dHLr gwSU8F3G52AJmmQC2Mm9NbvBaFD5HSVAQw+bxKUoeNrQL5U3Tb6aYAr7zPFoHDm1rvhh YCgIPjDIKiD3X/Ie8n9BqFtjSCFU08SaoLXAsbkYIHZpVsSINP7e0DPmzAEfoOm+VGPN aUzA== X-Gm-Message-State: AOAM532jMo3BPkE4VTgR9PI4O1zkEsa529pUpcZig/Ijd/VvU2StDryi SS5TfnTRGcMxLS178UO2ega2Vw== X-Google-Smtp-Source: ABdhPJyxhZcl5xyjMu94IAzrj7E5CrK+jN97dKuaDSE5wtPHiMTH4wRVOq+ZSf1afDxWj/RtxHzsKg== X-Received: by 2002:a1c:2e4b:: with SMTP id u72mr4758638wmu.69.1600953204589; Thu, 24 Sep 2020 06:13:24 -0700 (PDT) Received: from balsini.lon.corp.google.com ([2a00:79e0:d:210:7220:84ff:fe09:7d5c]) by smtp.gmail.com with ESMTPSA id k22sm3805044wrd.29.2020.09.24.06.13.23 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 24 Sep 2020 06:13:24 -0700 (PDT) From: Alessio Balsini To: Miklos Szeredi Cc: Akilesh Kailash , Amir Goldstein , Antonio SJ Musumeci , David Anderson , Giuseppe Scrivano , Jann Horn , Jens Axboe , Martijn Coenen , Palmer Dabbelt , Paul Lawrence , Stefano Duo , Zimuzo Ezeozue , fuse-devel@lists.sourceforge.net, kernel-team@android.com, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH V9 2/4] fuse: Trace daemon creds Date: Thu, 24 Sep 2020 14:13:16 +0100 Message-Id: <20200924131318.2654747-3-balsini@android.com> X-Mailer: git-send-email 2.28.0.681.g6f77f65b4e-goog In-Reply-To: <20200924131318.2654747-1-balsini@android.com> References: <20200924131318.2654747-1-balsini@android.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org Add a reference to the FUSE daemon credentials, so that they can be used to temporarily raise the user credentials when accessing lower file system files in passthrough. When using FUSE passthrough, read/write operations are directly forwarded to the lower file system file, but there is no guarantee that the process that is triggering the request has the right permissions to access the lower file system. By default, in the non-passthrough use case, it is the daemon that handles the read/write operations, that can be performed to the lower file system with the daemon privileges. When passthrough is active, instead, the read/write operation is directly applied to the lower file system, so to keep the same behavior as before, the calling process temporarily receives the same credentials as the daemon, that should be removed as soon as the operation completes. Signed-off-by: Alessio Balsini --- fs/fuse/fuse_i.h | 3 +++ fs/fuse/inode.c | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 6c5166447905..67bf5919f8d6 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -524,6 +524,9 @@ struct fuse_conn { /** The group id for this mount */ kgid_t group_id; + /** Creds of process which created this mount point */ + const struct cred *creator_cred; + /** The pid namespace for this mount */ struct pid_namespace *pid_ns; diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index eb223130a917..d22407bfa959 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -654,6 +654,8 @@ void fuse_conn_put(struct fuse_conn *fc) fiq->ops->release(fiq); put_pid_ns(fc->pid_ns); put_user_ns(fc->user_ns); + if (fc->creator_cred) + put_cred(fc->creator_cred); fc->release(fc); } } @@ -1203,6 +1205,12 @@ int fuse_fill_super_common(struct super_block *sb, struct fuse_fs_context *ctx) fc->allow_other = ctx->allow_other; fc->user_id = ctx->user_id; fc->group_id = ctx->group_id; + fc->creator_cred = prepare_creds(); + if (!fc->creator_cred) { + err = -ENOMEM; + goto err_dev_free; + } + fc->max_read = max_t(unsigned, 4096, ctx->max_read); fc->destroy = ctx->destroy; fc->no_control = ctx->no_control; From patchwork Thu Sep 24 13:13:17 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alessio Balsini X-Patchwork-Id: 11797261 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 2367E139F for ; Thu, 24 Sep 2020 13:13:41 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id E8F17235FD for ; Thu, 24 Sep 2020 13:13:40 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=android.com header.i=@android.com header.b="E8qt9biX" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727942AbgIXNNk (ORCPT ); Thu, 24 Sep 2020 09:13:40 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:46156 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727974AbgIXNN1 (ORCPT ); Thu, 24 Sep 2020 09:13:27 -0400 Received: from mail-wr1-x443.google.com (mail-wr1-x443.google.com [IPv6:2a00:1450:4864:20::443]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id DC39DC0613CE for ; Thu, 24 Sep 2020 06:13:26 -0700 (PDT) Received: by mail-wr1-x443.google.com with SMTP id z4so3784284wrr.4 for ; Thu, 24 Sep 2020 06:13:26 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=android.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=sZZlks3iSx8s9lDyxs+csuRoO2CrDNiUR/cHMJe+SwI=; b=E8qt9biXDu4ZdSAEs5GUe2aO7q54VUVhM0P1BmTOJAm3tD9KdSwYME2DhFbsXBPnB+ skXPmvnOOCmdEJi46WORrGtsiNXzdPCs2zeg+hQ9RoOxA8a203tRl6r8fVgx2Mfu4bvS w7YYX3EFnRHoahvfhZGayGTwavSsCLPTis1qf+gexSfHtwM59xkvveX5a6ztWw5XAEd6 UVY1nA4fmcjim2G9dX25m6yyTwTumbkDtbJ5mO7v0FC9DnQJuKajp2TTlq2p8WJHHiTI Ww/A4DvFG6900AA7f+FGDYBn9XaFcTZjIe91GJTfNbYPe5kHDoxXlPewJ31aJgQDIH+L xvVw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=sZZlks3iSx8s9lDyxs+csuRoO2CrDNiUR/cHMJe+SwI=; b=kWcJqQr1LzdLTjK27S04Fa/Z/xaDafqznAOUaARUziLWxptbIuf2vKS4qVpjg6wrgC RNCCx2tn+Trm/wEX359juD+kDNDPwsg5q9oV/6pSXC2HeOlMgFJOa0BrPYH36rWnHfsx yAlL20umUex5J7EFO+/7MK7isn7s7+tExIecdJIi83sCBODAgScS+wNoqGkLER+NjBo7 EmlEDFOfQuuyAa1C7wAXa6oSLPWlejomKrqAIoowIaWPjSk6YQT3UxlrjXSd+SP/qWYW zrstBYIovJg5vK+elfK/Izvdo13D3l9Bz5PftAhMaRiu+Seew8IkjbeZrN/hBBbPz6X5 t7uA== X-Gm-Message-State: AOAM53219sQccoJaSTlcded+KZTrzrTEHlsY7Z0b/LlmpR5M2m8E7q1+ rlo97rhXSwcrAmg0pyssDjktGg== X-Google-Smtp-Source: ABdhPJw36n6GgKPH4j0DYfUuTobv8Z6hBaPcKQAuxiE2b2hTjyQ4XL4FjapbfpSV/bkAXI9GEzJcxg== X-Received: by 2002:adf:fc0a:: with SMTP id i10mr5034984wrr.111.1600953205562; Thu, 24 Sep 2020 06:13:25 -0700 (PDT) Received: from balsini.lon.corp.google.com ([2a00:79e0:d:210:7220:84ff:fe09:7d5c]) by smtp.gmail.com with ESMTPSA id k22sm3805044wrd.29.2020.09.24.06.13.24 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 24 Sep 2020 06:13:25 -0700 (PDT) From: Alessio Balsini To: Miklos Szeredi Cc: Akilesh Kailash , Amir Goldstein , Antonio SJ Musumeci , David Anderson , Giuseppe Scrivano , Jann Horn , Jens Axboe , Martijn Coenen , Palmer Dabbelt , Paul Lawrence , Stefano Duo , Zimuzo Ezeozue , fuse-devel@lists.sourceforge.net, kernel-team@android.com, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH V9 3/4] fuse: Introduce synchronous read and write for passthrough Date: Thu, 24 Sep 2020 14:13:17 +0100 Message-Id: <20200924131318.2654747-4-balsini@android.com> X-Mailer: git-send-email 2.28.0.681.g6f77f65b4e-goog In-Reply-To: <20200924131318.2654747-1-balsini@android.com> References: <20200924131318.2654747-1-balsini@android.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org All the read and write operations performed on fuse_files which have the passthrough feature enabled are forwarded to the associated lower file system file via VFS. Sending the request directly to the lower file system avoids the userspace round-trip that, because of possible context switches and additional operations might reduce the overall performance, especially in those cases where caching doesn't help, for example in reads at random offsets. Verifying if a fuse_file has a lower file system file associated for passthrough can be done by checking the validity of its passthrough_filp pointer. This pointer is not NULL only if passthrough has been successfully enabled via the appropriate ioctl(). When a read/write operation is requested for a FUSE file with passthrough enabled, a new equivalent VFS request is generated, which instead targets the lower file system file. The VFS layer performs additional checks that allows for safer operations, but may cause the operation to fail if the process accessing the FUSE file system does not have access to the lower file system. This often happens in passthrough file systems, where the FUSE daemon is responsible for the enforcement of the lower file system access policies. In order to preserve this behavior, the current process accessing the FUSE file with passthrough enabled receives the privileges of the FUSE daemon while performing the read/write operation, emulating a behavior used in overlayfs. These privileges will be reverted as soon as the IO operation completes. This feature does not provide any higher security privileges to those processes accessing the FUSE file system with passthrough enabled. This because it is still the FUSE daemon responsible for enabling or not the passthrough feature at file open time, and should enable the feature only after appropriate access policy checks. This change only implements synchronous requests in passthrough, returning an error in the case of ansynchronous operations, yet covering the majority of the use cases. Signed-off-by: Alessio Balsini --- fs/fuse/file.c | 8 +++- fs/fuse/fuse_i.h | 2 + fs/fuse/passthrough.c | 93 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 101 insertions(+), 2 deletions(-) diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 6c0ec742ce74..c3289ff0cd33 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -1552,7 +1552,9 @@ static ssize_t fuse_file_read_iter(struct kiocb *iocb, struct iov_iter *to) if (is_bad_inode(file_inode(file))) return -EIO; - if (!(ff->open_flags & FOPEN_DIRECT_IO)) + if (ff->passthrough_filp) + return fuse_passthrough_read_iter(iocb, to); + else if (!(ff->open_flags & FOPEN_DIRECT_IO)) return fuse_cache_read_iter(iocb, to); else return fuse_direct_read_iter(iocb, to); @@ -1566,7 +1568,9 @@ static ssize_t fuse_file_write_iter(struct kiocb *iocb, struct iov_iter *from) if (is_bad_inode(file_inode(file))) return -EIO; - if (!(ff->open_flags & FOPEN_DIRECT_IO)) + if (ff->passthrough_filp) + return fuse_passthrough_write_iter(iocb, from); + else if (!(ff->open_flags & FOPEN_DIRECT_IO)) return fuse_cache_write_iter(iocb, from); else return fuse_direct_write_iter(iocb, from); diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 67bf5919f8d6..b0764ca4c4fd 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -1109,5 +1109,7 @@ void fuse_free_conn(struct fuse_conn *fc); int fuse_passthrough_setup(struct fuse_req *req, unsigned int fd); void fuse_passthrough_release(struct fuse_file *ff); +ssize_t fuse_passthrough_read_iter(struct kiocb *iocb, struct iov_iter *to); +ssize_t fuse_passthrough_write_iter(struct kiocb *iocb, struct iov_iter *from); #endif /* _FS_FUSE_I_H */ diff --git a/fs/fuse/passthrough.c b/fs/fuse/passthrough.c index 86ab4eafa7bf..f70c0ef6945b 100644 --- a/fs/fuse/passthrough.c +++ b/fs/fuse/passthrough.c @@ -2,6 +2,99 @@ #include "fuse_i.h" +#include + +static void fuse_copyattr(struct file *dst_file, struct file *src_file) +{ + struct inode *dst = file_inode(dst_file); + struct inode *src = file_inode(src_file); + + i_size_write(dst, i_size_read(src)); +} + +static rwf_t iocbflags_to_rwf(int ifl) +{ + rwf_t flags = 0; + + if (ifl & IOCB_APPEND) + flags |= RWF_APPEND; + if (ifl & IOCB_DSYNC) + flags |= RWF_DSYNC; + if (ifl & IOCB_HIPRI) + flags |= RWF_HIPRI; + if (ifl & IOCB_NOWAIT) + flags |= RWF_NOWAIT; + if (ifl & IOCB_SYNC) + flags |= RWF_SYNC; + + return flags; +} + +static const struct cred * +fuse_passthrough_override_creds(const struct file *fuse_filp) +{ + struct inode *fuse_inode = file_inode(fuse_filp); + struct fuse_conn *fc = fuse_inode->i_sb->s_fs_info; + + return override_creds(fc->creator_cred); +} + +ssize_t fuse_passthrough_read_iter(struct kiocb *iocb_fuse, + struct iov_iter *iter) +{ + ssize_t ret; + const struct cred *old_cred; + struct file *fuse_filp = iocb_fuse->ki_filp; + struct fuse_file *ff = fuse_filp->private_data; + struct file *passthrough_filp = ff->passthrough_filp; + + if (!iov_iter_count(iter)) + return 0; + + old_cred = fuse_passthrough_override_creds(fuse_filp); + if (is_sync_kiocb(iocb_fuse)) { + ret = vfs_iter_read(passthrough_filp, iter, &iocb_fuse->ki_pos, + iocbflags_to_rwf(iocb_fuse->ki_flags)); + } else { + ret = -EIO; + } + revert_creds(old_cred); + + return ret; +} + +ssize_t fuse_passthrough_write_iter(struct kiocb *iocb_fuse, + struct iov_iter *iter) +{ + ssize_t ret; + const struct cred *old_cred; + struct file *fuse_filp = iocb_fuse->ki_filp; + struct fuse_file *ff = fuse_filp->private_data; + struct inode *fuse_inode = file_inode(fuse_filp); + struct file *passthrough_filp = ff->passthrough_filp; + + if (!iov_iter_count(iter)) + return 0; + + inode_lock(fuse_inode); + + old_cred = fuse_passthrough_override_creds(fuse_filp); + if (is_sync_kiocb(iocb_fuse)) { + file_start_write(passthrough_filp); + ret = vfs_iter_write(passthrough_filp, iter, &iocb_fuse->ki_pos, + iocbflags_to_rwf(iocb_fuse->ki_flags)); + file_end_write(passthrough_filp); + if (ret > 0) + fuse_copyattr(fuse_filp, passthrough_filp); + } else { + ret = -EIO; + } + revert_creds(old_cred); + inode_unlock(fuse_inode); + + return ret; +} + int fuse_passthrough_setup(struct fuse_req *req, unsigned int fd) { int ret; From patchwork Thu Sep 24 13:13:18 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alessio Balsini X-Patchwork-Id: 11797263 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 56E30618 for ; Thu, 24 Sep 2020 13:13:49 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 3097C2311B for ; Thu, 24 Sep 2020 13:13:49 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=android.com header.i=@android.com header.b="c49kIoCN" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728000AbgIXNNk (ORCPT ); Thu, 24 Sep 2020 09:13:40 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:46160 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727983AbgIXNN2 (ORCPT ); Thu, 24 Sep 2020 09:13:28 -0400 Received: from mail-wr1-x441.google.com (mail-wr1-x441.google.com [IPv6:2a00:1450:4864:20::441]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id C6415C0613D4 for ; Thu, 24 Sep 2020 06:13:27 -0700 (PDT) Received: by mail-wr1-x441.google.com with SMTP id a17so3772224wrn.6 for ; Thu, 24 Sep 2020 06:13:27 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=android.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=SnFurKpeBkbwLq0KOJ5lvNPVyL4t0UDoYrGfHs7nv0A=; b=c49kIoCNMCWuGzzxSiHgueczTg5CkgrwLkGuWWq0FAGajSgKan0mFscWOzZVZ+m05d RbXYXA5FxFJCeXX7mCyh/aiJnyvqxapJeon+80154c1bUNnQb82F4gdw7Ej5UktyMO7k rdobOrqsNJFec7D8/br7wA47DdV9NURPNYsPThcpOki9m0thIqLiLbuRY4FgjXMsR04N SgpRRL5PtFgFAYE/gC7RVtUpmBRrctaosbCu9mkSBwDACxmcy70fQW+z6IZaqNo6orLr KL9ZHtgqVaorBff9YMt60zaVL854011zyCBngVeDEpHIO6pUMJdPr0mg9vOQxDHTy24j Fobg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=SnFurKpeBkbwLq0KOJ5lvNPVyL4t0UDoYrGfHs7nv0A=; b=tFkpah3VYbg2rpwS2rYwxFR80aCT9zMpYBGPQJ/SdFX+B60N7yBV73cY/x38cBOmuB hs5L88Sr/oCeYs8zXg5nIA4e5742Z/4hIIgnarf5QcxHqJgjsn4XgfV6PO3lDV49x+GD +eTOnYUBnTWGrTFUDTNBPG8cnS13qllDokh49znAnb6qz43DdSNHkcOfdVatAfvNSImz +72T8IQT9ME++OcoYwXCDj3WvP7N2As/y2azdadkCvZMcMzyD8Y0Q41t6ZuvQZvIt4nf fDvBjSjPRcc2Y5qTxZAqxd4xi8pqUzEAyB6G1qFt2YHJgJ8jQdZx2u+FqWH43nyPAGiQ WtgA== X-Gm-Message-State: AOAM531ZbIn0ln4uotQHmHlITqx6WHgyCLILGzmaIsBPbg/lI0jXhs26 QlD6RjDyQarHJNnE6q5a63vXCA== X-Google-Smtp-Source: ABdhPJwK71YGbcO6RW1+2r9ehrDaUmk/rj/VmGPTl16ohLV3Ibabs4NOTPjOmXGkw+O1V+VCBcxg2g== X-Received: by 2002:adf:cd0e:: with SMTP id w14mr5608662wrm.0.1600953206542; Thu, 24 Sep 2020 06:13:26 -0700 (PDT) Received: from balsini.lon.corp.google.com ([2a00:79e0:d:210:7220:84ff:fe09:7d5c]) by smtp.gmail.com with ESMTPSA id k22sm3805044wrd.29.2020.09.24.06.13.25 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 24 Sep 2020 06:13:25 -0700 (PDT) From: Alessio Balsini To: Miklos Szeredi Cc: Akilesh Kailash , Amir Goldstein , Antonio SJ Musumeci , David Anderson , Giuseppe Scrivano , Jann Horn , Jens Axboe , Martijn Coenen , Palmer Dabbelt , Paul Lawrence , Stefano Duo , Zimuzo Ezeozue , fuse-devel@lists.sourceforge.net, kernel-team@android.com, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH V9 4/4] fuse: Handle asynchronous read and write in passthrough Date: Thu, 24 Sep 2020 14:13:18 +0100 Message-Id: <20200924131318.2654747-5-balsini@android.com> X-Mailer: git-send-email 2.28.0.681.g6f77f65b4e-goog In-Reply-To: <20200924131318.2654747-1-balsini@android.com> References: <20200924131318.2654747-1-balsini@android.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org Extend the passthrough feature by handling asynchronous IO both for read and write operations. When an AIO request is received, if the request targets a FUSE file with the passthrough functionality enabled, a new identical AIO request is created. The new request targets the lower file system file, and gets assigned a special FUSE passthrough AIO completion callback. When the lower file system AIO request is completed, the FUSE passthrough AIO completion callback is executed and propagates the completion signal to the FUSE AIO request by triggering its completion callback as well. Signed-off-by: Alessio Balsini --- fs/fuse/passthrough.c | 64 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 62 insertions(+), 2 deletions(-) diff --git a/fs/fuse/passthrough.c b/fs/fuse/passthrough.c index f70c0ef6945b..b7d1a5517ffd 100644 --- a/fs/fuse/passthrough.c +++ b/fs/fuse/passthrough.c @@ -4,6 +4,11 @@ #include +struct fuse_aio_req { + struct kiocb iocb; + struct kiocb *iocb_fuse; +}; + static void fuse_copyattr(struct file *dst_file, struct file *src_file) { struct inode *dst = file_inode(dst_file); @@ -39,6 +44,32 @@ fuse_passthrough_override_creds(const struct file *fuse_filp) return override_creds(fc->creator_cred); } +static void fuse_aio_cleanup_handler(struct fuse_aio_req *aio_req) +{ + struct kiocb *iocb = &aio_req->iocb; + struct kiocb *iocb_fuse = aio_req->iocb_fuse; + + if (iocb->ki_flags & IOCB_WRITE) { + __sb_writers_acquired(file_inode(iocb->ki_filp)->i_sb, + SB_FREEZE_WRITE); + file_end_write(iocb->ki_filp); + fuse_copyattr(iocb_fuse->ki_filp, iocb->ki_filp); + } + + iocb_fuse->ki_pos = iocb->ki_pos; + kfree(aio_req); +} + +static void fuse_aio_rw_complete(struct kiocb *iocb, long res, long res2) +{ + struct fuse_aio_req *aio_req = + container_of(iocb, struct fuse_aio_req, iocb); + struct kiocb *iocb_fuse = aio_req->iocb_fuse; + + fuse_aio_cleanup_handler(aio_req); + iocb_fuse->ki_complete(iocb_fuse, res, res2); +} + ssize_t fuse_passthrough_read_iter(struct kiocb *iocb_fuse, struct iov_iter *iter) { @@ -56,7 +87,18 @@ ssize_t fuse_passthrough_read_iter(struct kiocb *iocb_fuse, ret = vfs_iter_read(passthrough_filp, iter, &iocb_fuse->ki_pos, iocbflags_to_rwf(iocb_fuse->ki_flags)); } else { - ret = -EIO; + struct fuse_aio_req *aio_req; + + aio_req = kmalloc(sizeof(struct fuse_aio_req), GFP_KERNEL); + if (!aio_req) + return -ENOMEM; + + aio_req->iocb_fuse = iocb_fuse; + kiocb_clone(&aio_req->iocb, iocb_fuse, passthrough_filp); + aio_req->iocb.ki_complete = fuse_aio_rw_complete; + ret = call_read_iter(passthrough_filp, &aio_req->iocb, iter); + if (ret != -EIOCBQUEUED) + fuse_aio_cleanup_handler(aio_req); } revert_creds(old_cred); @@ -72,6 +114,7 @@ ssize_t fuse_passthrough_write_iter(struct kiocb *iocb_fuse, struct fuse_file *ff = fuse_filp->private_data; struct inode *fuse_inode = file_inode(fuse_filp); struct file *passthrough_filp = ff->passthrough_filp; + struct inode *passthrough_inode = file_inode(passthrough_filp); if (!iov_iter_count(iter)) return 0; @@ -87,8 +130,25 @@ ssize_t fuse_passthrough_write_iter(struct kiocb *iocb_fuse, if (ret > 0) fuse_copyattr(fuse_filp, passthrough_filp); } else { - ret = -EIO; + struct fuse_aio_req *aio_req; + + aio_req = kmalloc(sizeof(struct fuse_aio_req), GFP_KERNEL); + if (!aio_req) { + ret = -ENOMEM; + goto out; + } + + file_start_write(passthrough_filp); + __sb_writers_release(passthrough_inode->i_sb, SB_FREEZE_WRITE); + + aio_req->iocb_fuse = iocb_fuse; + kiocb_clone(&aio_req->iocb, iocb_fuse, passthrough_filp); + aio_req->iocb.ki_complete = fuse_aio_rw_complete; + ret = call_write_iter(passthrough_filp, &aio_req->iocb, iter); + if (ret != -EIOCBQUEUED) + fuse_aio_cleanup_handler(aio_req); } +out: revert_creds(old_cred); inode_unlock(fuse_inode);