From patchwork Mon Oct 26 12:50:12 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alessio Balsini X-Patchwork-Id: 11857063 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 771D5697 for ; Mon, 26 Oct 2020 12:51:23 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 4867822409 for ; Mon, 26 Oct 2020 12:51:23 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=android.com header.i=@android.com header.b="k6+CO/eM" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1775577AbgJZMvV (ORCPT ); Mon, 26 Oct 2020 08:51:21 -0400 Received: from mail-wm1-f66.google.com ([209.85.128.66]:39419 "EHLO mail-wm1-f66.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1775568AbgJZMvU (ORCPT ); Mon, 26 Oct 2020 08:51:20 -0400 Received: by mail-wm1-f66.google.com with SMTP id d3so12345351wma.4 for ; Mon, 26 Oct 2020 05:51:17 -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=60C4XPt+sm3xWxPQ15yIvj05ijHp+1cr7XJSkVPOdlQ=; b=k6+CO/eMVpf7nk0Z+zyT2QD0McsXDMJKq6/h8JxvmT9qbp9YNrJPBSeW1pVPXd4JDr rcPi9pyCGfJ1WSGjPglHY/5puvAN2XPNXWfbPBBOp0Q1i6wqbMhZrF39VsB2VpTErspz sQMCaN6mXrPEu7QqJxUM1ekcAEs1AhsiZSwN6YEDLHJVoNV9XnkQcXabe/BJJkWoZQ/m vUFI2d4FV1x5EUiC8rIPFGcdnkUgHW2yjvAZDUVpW7tEgmI8Kru4jPnLP5cV/QsUMVRF ofEVqpsXT/eMMIqFE3icTEfj5eFfTcXNLcIlabICCA7bZwtalOCa+JxxNI2QkdrLJJBp kq4g== 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=60C4XPt+sm3xWxPQ15yIvj05ijHp+1cr7XJSkVPOdlQ=; b=MWiZwt5vGArvZ7Cu7H0Sx8jApZob9zPcdPj5PUD44aevEqyH+4Rm3btTB+HHJc6IVj aZJsr6NAsErH/XZE6a4n9zw5cjLnzVPH2FoaU/kPuxRLdesU1+OHxpx0g2ildJcZg53+ fX+YHo5dbLGHpwwMYKLeHyZzxA7lIrZYp+L1ZMKacm79wHtgJWEoA+nzz55AxyKGLQWy kxr2Q2+hrPMdyVi8oqJRhuOAlzBH2/tEbyZrXKGoZ1mpxdweS2CjkhY6+wGHX9877p9t UF9Fj2zBIOAb1sBOI3OhUXrO0/RcWfYAGgx5xQm6fHUmYz35CX5IWturkm4eC3oycySf E6ZQ== X-Gm-Message-State: AOAM5315ydrKb7uyJ6vAHwYtFsHaV2PlBVwxJtOVu2wsWWx3jnehu3hy j+PbxVjnSlZgr8kjliVShQQePQ== X-Google-Smtp-Source: ABdhPJxaQQilFc2jTausbEDQ5JIAOX+eUlm5CpkN1Wkq14ZjblcaExyxD6wTiSK9X2jMx4WBvkki8w== X-Received: by 2002:a1c:740e:: with SMTP id p14mr16055994wmc.34.1603716676810; Mon, 26 Oct 2020 05:51:16 -0700 (PDT) Received: from balsini.lon.corp.google.com ([2a00:79e0:d:210:7220:84ff:fe09:7d5c]) by smtp.gmail.com with ESMTPSA id r1sm24423262wro.18.2020.10.26.05.51.16 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 26 Oct 2020 05:51:16 -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 V10 1/5] fuse: Definitions and ioctl() for passthrough Date: Mon, 26 Oct 2020 12:50:12 +0000 Message-Id: <20201026125016.1905945-2-balsini@android.com> X-Mailer: git-send-email 2.29.0.rc1.297.gfa9743e501-goog In-Reply-To: <20201026125016.1905945-1-balsini@android.com> References: <20201026125016.1905945-1-balsini@android.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org Expose the FUSE_PASSTHROUGH interface to userspace 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 userspace 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. Signed-off-by: Alessio Balsini --- fs/fuse/Makefile | 1 + fs/fuse/dev.c | 40 ++++++++++++++++++++++++++++----------- fs/fuse/dir.c | 1 + fs/fuse/file.c | 4 +++- fs/fuse/fuse_i.h | 26 +++++++++++++++++++++++++ fs/fuse/inode.c | 18 +++++++++++++++++- fs/fuse/passthrough.c | 21 ++++++++++++++++++++ include/uapi/linux/fuse.h | 13 +++++++++++-- 8 files changed, 109 insertions(+), 15 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..bcf1da0260bc 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -2222,37 +2222,55 @@ static int fuse_device_clone(struct fuse_conn *fc, struct file *new) 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 res; + int oldfd; + struct fuse_dev *fud; + struct fuse_passthrough_out pto; - err = -EFAULT; - if (!get_user(oldfd, (__u32 __user *) arg)) { + switch (cmd) { + case FUSE_DEV_IOC_CLONE: + res = -EFAULT; + if (!get_user(oldfd, (__u32 __user *)arg)) { struct file *old = fget(oldfd); - err = -EINVAL; + res = -EINVAL; if (old) { - struct fuse_dev *fud = NULL; + fud = NULL; /* * Check against file->f_op because CUSE * uses the same ioctl handler. */ if (old->f_op == file->f_op && - old->f_cred->user_ns == file->f_cred->user_ns) + old->f_cred->user_ns == + file->f_cred->user_ns) fud = fuse_get_dev(old); if (fud) { mutex_lock(&fuse_mutex); - err = fuse_device_clone(fud->fc, file); + res = fuse_device_clone(fud->fc, file); mutex_unlock(&fuse_mutex); } fput(old); } } + break; + case 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; } - return err; + return res; } const struct file_operations fuse_dev_operations = { diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 26f028bc760b..875799959e33 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -489,6 +489,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) { diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 83d917f7e542..84daaf084197 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -151,7 +151,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) { fuse_file_free(ff); return err; @@ -281,6 +281,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->passthrough); + 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..32da45ce86e0 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -163,6 +163,14 @@ enum { struct fuse_conn; struct fuse_release_args; +/** + * 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 */ @@ -208,6 +216,9 @@ struct fuse_file { } readdir; + /** 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; @@ -720,6 +731,9 @@ struct fuse_conn { /* Do not show mount options */ unsigned int no_mount_options:1; + /** Passthrough mode for read/write IO */ + unsigned int passthrough:1; + /** The number of requests waiting for completion */ atomic_t num_waiting; @@ -755,6 +769,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) @@ -1093,4 +1113,10 @@ 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_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 */ diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index bba747520e9b..6738dd5ff5d2 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -621,6 +621,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->bg_lock); + spin_lock_init(&fc->passthrough_req_lock); init_rwsem(&fc->killsb); refcount_set(&fc->count, 1); atomic_set(&fc->dev_count, 1); @@ -629,6 +630,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; @@ -965,6 +967,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 +1010,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); @@ -1023,9 +1032,16 @@ void fuse_send_init(struct fuse_conn *fc) } EXPORT_SYMBOL_GPL(fuse_send_init); +static int free_fuse_passthrough(int id, void *p, void *data) +{ + return 0; +} + 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); } EXPORT_SYMBOL_GPL(fuse_free_conn); diff --git a/fs/fuse/passthrough.c b/fs/fuse/passthrough.c new file mode 100644 index 000000000000..594060c654f8 --- /dev/null +++ b/fs/fuse/passthrough.c @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include "fuse_i.h" + +#include + +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) +{ +} diff --git a/include/uapi/linux/fuse.h b/include/uapi/linux/fuse.h index 373cada89815..ae06efb25d18 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 @@ -591,7 +592,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 { @@ -794,6 +795,13 @@ struct fuse_in_header { uint32_t padding; }; +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; @@ -869,7 +877,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 Mon Oct 26 12:50:13 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alessio Balsini X-Patchwork-Id: 11857065 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 A212092C for ; Mon, 26 Oct 2020 12:51:36 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 7FD4022409 for ; Mon, 26 Oct 2020 12:51:36 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=android.com header.i=@android.com header.b="k7bQnsf6" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1775590AbgJZMve (ORCPT ); Mon, 26 Oct 2020 08:51:34 -0400 Received: from mail-wr1-f65.google.com ([209.85.221.65]:43567 "EHLO mail-wr1-f65.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1775581AbgJZMvd (ORCPT ); Mon, 26 Oct 2020 08:51:33 -0400 Received: by mail-wr1-f65.google.com with SMTP id g12so12356713wrp.10 for ; Mon, 26 Oct 2020 05:51:29 -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=8U6Qxx953w4L4tpJflBCmGVShrqbgz4wqR0nsj2i66M=; b=k7bQnsf63vkCKsp8Rvxr7QOrch0TfaeXCi6nD1OZg5KP7PnOCGqEYtVxrvjHPIJECi ikDfclmimdvLua7YBaaUzumilE+yGMsAKKo99Zbi33Y4OQoWQZS3BnEcMXHE1S+yIUKy tAbfSzQlJfUVBtiehjICl3umaBVwuQ89Pb/D3wfWOrDWi/C0plwRi9n7ot4fnnUH2qi4 hbzZk0VJ+KsbnquuJijziGPftFUsY2gIJnzzvPOL6/lTiatBoEL1+wZfsRfDtIMZd6JO 8dSM3ET/8MNL2HSvDgQFZ66I31+HGAH0oAkb0fb3nEps9/8mbKcZzde4dvmKYRVUd1et wlnw== 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=8U6Qxx953w4L4tpJflBCmGVShrqbgz4wqR0nsj2i66M=; b=mbXHlloFClPuslMtBbskJw8RUIKDpTczCeG5AZA2IgUXAVsWMg7PZ3MXJf0HLojiMe cus+fqfd3edizyK6Yzm1l8GzLPLvZaIyvxXzeDdMQrED+m5NQRXdj7BkVh0RB98J8Ask S9f8dbb4rcKhDGryQ25cABelu154w2MrFeLtR87lVI56JxBohlSUXMToQTv6g60OdkDM TThpPSRIj8zIbSNWSxWqqYceaCnlzQdP52Ya+UWi/DjiDNwUoBkSo3cLWCcUDixgXajs hTMEqgkFKqOq4r6uCa5rrM27XU3HzyxIGjBpCn5XVCuUtIt5/Tcnqz8oelx+NqqC1P4p 2x4A== X-Gm-Message-State: AOAM532mEJdp2JKInKTnOGKuATXLhZ7qpWVRWPCRn6PwrBubl/LWUh7+ YsNg/nKJXQloV9wq3K+9/eC9KQ== X-Google-Smtp-Source: ABdhPJxjAqwKYi0Nh7e0kUODbADs1aQzhY1OUhWjvVLG7utC+bT1UdOvnc/p69OAAc0DjO1EJYo2QQ== X-Received: by 2002:adf:ef06:: with SMTP id e6mr17579382wro.397.1603716688727; Mon, 26 Oct 2020 05:51:28 -0700 (PDT) Received: from balsini.lon.corp.google.com ([2a00:79e0:d:210:7220:84ff:fe09:7d5c]) by smtp.gmail.com with ESMTPSA id r1sm24423262wro.18.2020.10.26.05.51.28 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 26 Oct 2020 05:51:28 -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 V10 2/5] fuse: Passthrough initialization and release Date: Mon, 26 Oct 2020 12:50:13 +0000 Message-Id: <20201026125016.1905945-3-balsini@android.com> X-Mailer: git-send-email 2.29.0.rc1.297.gfa9743e501-goog In-Reply-To: <20201026125016.1905945-1-balsini@android.com> References: <20201026125016.1905945-1-balsini@android.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org Implement the FUSE passthrough ioctl() that associates the lower (passthrough) file system file with the fuse_file. The file descriptor passed to the ioctl() by the FUSE daemon is used to access the relative file pointer, that will be copied to the fuse_file data structure to consolidate the link between the FUSE and lower file system. To enable the passthrough mode, userspace triggers the FUSE_DEV_IOC_PASSTHROUGH_OPEN ioctl() and, if the call succeeds, receives back an identifier that will be used at open/create response time in the fuse_open_out field to associate the FUSE file to the lower file system file. The value returned by the ioctl() to userspace can be: - > 0: success, the identifier can be used as part of an open/create reply. - < 0: an error occurred. The value 0 has been left unused for backward compatibility: the fuse_open_out field that is used to pass the passthrough_fh back to the kernel uses the same bits that were previously as struct padding, zero-initialized in the common libfuse implementation. Removing the 0 value fixes the ambiguity between the case in which 0 corresponds to a real passthrough_fh or a missing implementation, simplifying the userspace implementation. 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 pseudo files to be targeted for this feature. Passthrough comes with another limitation: no further file system stacking is allowed for those FUSE file systems using passthrough. Signed-off-by: Alessio Balsini --- fs/fuse/inode.c | 5 +++ fs/fuse/passthrough.c | 80 +++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 83 insertions(+), 2 deletions(-) diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 6738dd5ff5d2..1e94c54d1455 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -1034,6 +1034,11 @@ EXPORT_SYMBOL_GPL(fuse_send_init); static int free_fuse_passthrough(int id, void *p, void *data) { + struct fuse_passthrough *passthrough = (struct fuse_passthrough *)p; + + fuse_passthrough_release(passthrough); + kfree(p); + return 0; } diff --git a/fs/fuse/passthrough.c b/fs/fuse/passthrough.c index 594060c654f8..a135c955cc33 100644 --- a/fs/fuse/passthrough.c +++ b/fs/fuse/passthrough.c @@ -3,19 +3,95 @@ #include "fuse_i.h" #include +#include int fuse_passthrough_open(struct fuse_dev *fud, struct fuse_passthrough_out *pto) { - return -EINVAL; + int res; + struct file *passthrough_filp; + struct fuse_conn *fc = fud->fc; + struct fuse_passthrough *passthrough; + + if (!fc->passthrough) + return -EPERM; + + /* This field is reserved for future implementation */ + if (pto->len != 0) + return -EINVAL; + + passthrough_filp = fget(pto->fd); + if (!passthrough_filp) { + pr_err("FUSE: invalid file descriptor for passthrough.\n"); + return -EBADF; + } + + if (!passthrough_filp->f_op->read_iter || + !passthrough_filp->f_op->write_iter) { + pr_err("FUSE: passthrough file misses file operations.\n"); + return -EBADF; + } + + passthrough = kmalloc(sizeof(struct fuse_passthrough), GFP_KERNEL); + if (!passthrough) + return -ENOMEM; + + passthrough->filp = passthrough_filp; + + idr_preload(GFP_KERNEL); + spin_lock(&fc->passthrough_req_lock); + res = idr_alloc(&fc->passthrough_req, passthrough, 1, 0, GFP_ATOMIC); + spin_unlock(&fc->passthrough_req_lock); + idr_preload_end(); + if (res <= 0) { + fuse_passthrough_release(passthrough); + kfree(passthrough); + } + + return res; } int fuse_passthrough_setup(struct fuse_conn *fc, struct fuse_file *ff, struct fuse_open_out *openarg) { - return -EINVAL; + struct inode *passthrough_inode; + struct super_block *passthrough_sb; + struct fuse_passthrough *passthrough; + int passthrough_fh = openarg->passthrough_fh; + + if (!fc->passthrough) + return -EPERM; + + /* Default case, passthrough is not requested */ + if (passthrough_fh <= 0) + return -EINVAL; + + spin_lock(&fc->passthrough_req_lock); + passthrough = idr_remove(&fc->passthrough_req, passthrough_fh); + spin_unlock(&fc->passthrough_req_lock); + + if (!passthrough) + return -EINVAL; + + passthrough_inode = file_inode(passthrough->filp); + passthrough_sb = passthrough_inode->i_sb; + if (passthrough_sb->s_stack_depth >= FILESYSTEM_MAX_STACK_DEPTH) { + pr_err("FUSE: fs stacking depth exceeded for passthrough\n"); + fuse_passthrough_release(passthrough); + kfree(passthrough); + return -EINVAL; + } + + ff->passthrough = *passthrough; + kfree(passthrough); + + return 0; } void fuse_passthrough_release(struct fuse_passthrough *passthrough) { + if (passthrough->filp) { + fput(passthrough->filp); + passthrough->filp = NULL; + } } From patchwork Mon Oct 26 12:50:14 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alessio Balsini X-Patchwork-Id: 11857067 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 B9AFE697 for ; Mon, 26 Oct 2020 12:51:41 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 962D02242A for ; Mon, 26 Oct 2020 12:51:41 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=android.com header.i=@android.com header.b="os91uLp5" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1775594AbgJZMvl (ORCPT ); Mon, 26 Oct 2020 08:51:41 -0400 Received: from mail-wm1-f45.google.com ([209.85.128.45]:38550 "EHLO mail-wm1-f45.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1775592AbgJZMvk (ORCPT ); Mon, 26 Oct 2020 08:51:40 -0400 Received: by mail-wm1-f45.google.com with SMTP id l15so12354258wmi.3 for ; Mon, 26 Oct 2020 05:51:39 -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=UlQHDovrNDKdm6IMWfQLoIWvimboiAorH8hQQgAJgW0=; b=os91uLp57kt4nqat5HhkoFKu6bst0Qcip5dCGs+GvojC0bpuwZ87xH7J6IBws0VczG XPofmJhdzL0jGnAl6RZjzKbExzR7Rc/VWc5QjqsGdhTHsX4SVHpZZX9ocf/p9DCdsrhj FJEONn2hG6iPGGKJY98/OheZt1U7MlZX1Jwh+yQN55tcnKyxt4Krtpny80nszreQBFMP dAcu9csc7hrNClTue7rOb3KSe7sVhPMhdBb1zF5D2KCaKh/0PzPqHqg7etkCNtu41qt/ 8A57UamCKtKZYxYbvHxGZpgy7thVmw6xRGgHQVYUrmOhT7ST0eVjfOq/tusBWiLQI0Cw gR0Q== 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=UlQHDovrNDKdm6IMWfQLoIWvimboiAorH8hQQgAJgW0=; b=jEqi9NXg4cme9iOFNiYkqOm+4dwluZJ8QieEaltLeUegGazJZtGWR4NtRRyWP3zi+v dTngcfE6tOKoU5goGheday7+iwgr7I+TAck3iaZh64qCmWcA4n6WUBN4Q2roeXYn5AR7 k2qjmZHfAsj4iVYYy5n6V9Egpn90DhICBTcIJiXWYYR9UZS482fM/GSQXaopr3qzkdlD Px6HdUdZxzZ01DE6LZmbhWYdx7rrer0VxbX4tmeBvLlXtAukVV8rZAnY2s4jf03SyFMa OiFj9uzRCBtsxLN4/Mqi/RO+1V2/xuGN0n1e5LahUGGVW/C/QCA1r93u1nWZb7Yj8DhK R7jg== X-Gm-Message-State: AOAM5322E7t//V+Fn9NdRlsdhbEgL7X17qIOBAKCfEdogILPCM19z+4b BtiW+ayrmMdWnGE5mfKf7r7xXQ== X-Google-Smtp-Source: ABdhPJz7wg2jt+vpyty1PDUJG2zT0AyXcJOSKop7mt8WbVzDGhWyUwqVGRB0zA/hvDJyZKOuEY00Eg== X-Received: by 2002:a1c:2c02:: with SMTP id s2mr16656471wms.66.1603716698330; Mon, 26 Oct 2020 05:51:38 -0700 (PDT) Received: from balsini.lon.corp.google.com ([2a00:79e0:d:210:7220:84ff:fe09:7d5c]) by smtp.gmail.com with ESMTPSA id r1sm24423262wro.18.2020.10.26.05.51.37 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 26 Oct 2020 05:51:37 -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 V10 3/5] fuse: Introduce synchronous read and write for passthrough Date: Mon, 26 Oct 2020 12:50:14 +0000 Message-Id: <20201026125016.1905945-4-balsini@android.com> X-Mailer: git-send-email 2.29.0.rc1.297.gfa9743e501-goog In-Reply-To: <20201026125016.1905945-1-balsini@android.com> References: <20201026125016.1905945-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 with 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 allow 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 change only implements synchronous requests in passthrough, returning an error in the case of asynchronous 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 | 70 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 78 insertions(+), 2 deletions(-) diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 84daaf084197..f7a12489c0ef 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -1545,7 +1545,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); @@ -1559,7 +1561,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 32da45ce86e0..a888d3df5877 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -1118,5 +1118,7 @@ int fuse_passthrough_open(struct fuse_dev *fud, 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); +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 a135c955cc33..5a78cb336db4 100644 --- a/fs/fuse/passthrough.c +++ b/fs/fuse/passthrough.c @@ -4,6 +4,76 @@ #include #include +#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 inline rwf_t iocb_to_rw_flags(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; +} + +ssize_t fuse_passthrough_read_iter(struct kiocb *iocb_fuse, + struct iov_iter *iter) +{ + ssize_t ret; + 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; + + ret = vfs_iter_read(passthrough_filp, iter, &iocb_fuse->ki_pos, + iocb_to_rw_flags(iocb_fuse->ki_flags)); + + return ret; +} + +ssize_t fuse_passthrough_write_iter(struct kiocb *iocb_fuse, + struct iov_iter *iter) +{ + ssize_t ret; + 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); + + file_start_write(passthrough_filp); + ret = vfs_iter_write(passthrough_filp, iter, &iocb_fuse->ki_pos, + iocb_to_rw_flags(iocb_fuse->ki_flags)); + file_end_write(passthrough_filp); + if (ret > 0) + fuse_copyattr(fuse_filp, passthrough_filp); + + inode_unlock(fuse_inode); + + return ret; +} int fuse_passthrough_open(struct fuse_dev *fud, struct fuse_passthrough_out *pto) From patchwork Mon Oct 26 12:50: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: 11857069 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 1482214B7 for ; Mon, 26 Oct 2020 12:51:56 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id E4CF724640 for ; Mon, 26 Oct 2020 12:51:55 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=android.com header.i=@android.com header.b="JDXJgSQJ" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1775621AbgJZMvw (ORCPT ); Mon, 26 Oct 2020 08:51:52 -0400 Received: from mail-wr1-f67.google.com ([209.85.221.67]:45368 "EHLO mail-wr1-f67.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1775615AbgJZMvw (ORCPT ); Mon, 26 Oct 2020 08:51:52 -0400 Received: by mail-wr1-f67.google.com with SMTP id e17so12349362wru.12 for ; Mon, 26 Oct 2020 05:51:49 -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=9eIClT5rENq9kBsjIFF+DaoCpFvJQstMih7FfG4bpQI=; b=JDXJgSQJ80djfaAPRIOkY7qkWmZDRbS9SFL476HVIgjsAc6wSRP1/i/v1IV8BMMYkp B0EXwBBKYxl9+AGFZ4g/QiSgQ2Qjwh8a1W9uCMGhSrkaoV9bhAc8vQKI0pMz4TpbVugL l9LBQsJCbYz7EZZ3ceGl/XYGw6QQ69Pz/gzHcm3PBAtl2mbGYSdGHgRqvLdZ6SALcNBG 50tu3ZWj0GYObEaxLQ9JTZMCL1rHqgAMqIR4CCbY8sNXhD2dsInDHE5FmQQG9PozEd5f tT1UeQYxanLapsSaHir/m/ta4wr1rRd6ZAPhzMXeNtNrey4ICU9KsVoXmPtM9rHi8zlt JCTQ== 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=9eIClT5rENq9kBsjIFF+DaoCpFvJQstMih7FfG4bpQI=; b=G6w7hU438lzrKyYF3K7Qetj/+Vk3DS1d9qaXVSkS7e9UlLaFXqyVcSx3Cb+BxVr8jx 9RloRldfd3fcDAWcabXM6alEk469u1OMXKIIHogXV/ujZOC12TkWEFBl6Yvka8vlDdlZ ZViB6Qas8QITdkFOwg9QU2y9TvOFZRx60mHTwKDcYVqnBAl0uJEwlUH2cM9cTY3DcNd9 Z6VKR8Tc5VCbCQqmSvcx2e30c7WIyrcFl33EQiHuqlBba1XeeqwNg6LGUHFwD6Oec2Hz dadiD96NWdjRcn2JhCXcsLwhe8y9DzQYXXRUBqBQRpBHneeUbXIfMDJdcn/pfBttPYVT F8Aw== X-Gm-Message-State: AOAM5322UPowTon5pr9+o22umjOSgfy18FWEz5bWF6wYgqZSz/A0+F25 KIp1G6fwW7TYCyfeAZpS5+/tQg== X-Google-Smtp-Source: ABdhPJxnjjlEsdDTWZ5ovaMmfaaPyRK980t1YQWpq0a/B0eAQqt2YNlOhDcO6C7LQR2UDpxp+/8bJw== X-Received: by 2002:a5d:410a:: with SMTP id l10mr17506394wrp.274.1603716709164; Mon, 26 Oct 2020 05:51:49 -0700 (PDT) Received: from balsini.lon.corp.google.com ([2a00:79e0:d:210:7220:84ff:fe09:7d5c]) by smtp.gmail.com with ESMTPSA id r1sm24423262wro.18.2020.10.26.05.51.48 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 26 Oct 2020 05:51:48 -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 V10 4/5] fuse: Handle asynchronous read and write in passthrough Date: Mon, 26 Oct 2020 12:50:15 +0000 Message-Id: <20201026125016.1905945-5-balsini@android.com> X-Mailer: git-send-email 2.29.0.rc1.297.gfa9743e501-goog In-Reply-To: <20201026125016.1905945-1-balsini@android.com> References: <20201026125016.1905945-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 | 85 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 76 insertions(+), 9 deletions(-) diff --git a/fs/fuse/passthrough.c b/fs/fuse/passthrough.c index 5a78cb336db4..10b6872cdaa7 100644 --- a/fs/fuse/passthrough.c +++ b/fs/fuse/passthrough.c @@ -6,6 +6,11 @@ #include #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); @@ -32,6 +37,32 @@ static inline rwf_t iocb_to_rw_flags(int ifl) return flags; } +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) { @@ -43,8 +74,23 @@ ssize_t fuse_passthrough_read_iter(struct kiocb *iocb_fuse, if (!iov_iter_count(iter)) return 0; - ret = vfs_iter_read(passthrough_filp, iter, &iocb_fuse->ki_pos, - iocb_to_rw_flags(iocb_fuse->ki_flags)); + if (is_sync_kiocb(iocb_fuse)) { + ret = vfs_iter_read(passthrough_filp, iter, &iocb_fuse->ki_pos, + iocb_to_rw_flags(iocb_fuse->ki_flags)); + } else { + 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); + } return ret; } @@ -57,19 +103,40 @@ 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; inode_lock(fuse_inode); - file_start_write(passthrough_filp); - ret = vfs_iter_write(passthrough_filp, iter, &iocb_fuse->ki_pos, - iocb_to_rw_flags(iocb_fuse->ki_flags)); - file_end_write(passthrough_filp); - if (ret > 0) - fuse_copyattr(fuse_filp, passthrough_filp); - + if (is_sync_kiocb(iocb_fuse)) { + file_start_write(passthrough_filp); + ret = vfs_iter_write(passthrough_filp, iter, &iocb_fuse->ki_pos, + iocb_to_rw_flags(iocb_fuse->ki_flags)); + file_end_write(passthrough_filp); + if (ret > 0) + fuse_copyattr(fuse_filp, passthrough_filp); + } else { + 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: inode_unlock(fuse_inode); return ret; From patchwork Mon Oct 26 12:50: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: 11857071 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 453E292C for ; Mon, 26 Oct 2020 12:52:08 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 242B12463C for ; Mon, 26 Oct 2020 12:52:08 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=android.com header.i=@android.com header.b="mlOM4XI1" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1773110AbgJZMwD (ORCPT ); Mon, 26 Oct 2020 08:52:03 -0400 Received: from mail-wr1-f68.google.com ([209.85.221.68]:35086 "EHLO mail-wr1-f68.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1775629AbgJZMwD (ORCPT ); Mon, 26 Oct 2020 08:52:03 -0400 Received: by mail-wr1-f68.google.com with SMTP id n15so12395509wrq.2 for ; Mon, 26 Oct 2020 05:52:01 -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=gcgBSno187aQNdhvd4N5jly+A7zB4BHcwgXiG2B//N8=; b=mlOM4XI1khkSgoo5fH4K5JNpXT7Awm84134FjHcN081ek0aPls57KS+kLhzx5mzoNA jk5ZZDTsJkFJ4DUS9zSIUxZ+BszBTOnGFBu2KystEnePEFTkdq1kGQa68LyTowdZ5Rxg mxikb7wM3mKXfzDd/OUcGPAqZrP0Cm7Rv6NqeR+HMZhmB6/qQc1HDRwRu48kyK241O95 Mz6srSYgICO7m4lizZwqLRet3eJfCUFM/4NYsI0926uB5UWNy5fCXdhXUVk+oNQJz3VH ue7yFrl1oSHq3I0kcnIRTUWgzAOQFp65k12+zKjaGkhTCOUtgoYiQD44PDlAN3LXwUL4 B+oA== 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=gcgBSno187aQNdhvd4N5jly+A7zB4BHcwgXiG2B//N8=; b=pOj8b41nGjbaCnHROAefmo5bTTbZpu6jMGCy8bu4zaT3Cx6oHDfY7YO0GQur07fVcn L7cwBljv1gTAn0fJbjt+1ZS40B4SelsicNRqdubvzNIKKOkzrZUnp8Lc4L2kjnkLdE5B AM0cpCw4QrKJubh/o6Nh80psonjQUgH4172s5L8aWR/tqoEcW63gozZrJOJGRlUOxuka jiuxt9OO8EzW0mH14EMzGjEok5+FIKbUjJtbzT1yEw+raJ8Y14rINTJdKcnseZlYgN45 xANel1VSL7IQz9qvQ8JSk2ptKPHAASCOyMC0KbXtdyBGhZh6EyaJciYKUD68MhDDiF1W Bfmg== X-Gm-Message-State: AOAM530BLVIrx/areLMZRtdp0XwT1LIxss37Y96KmilUaqxecdITk76+ lSmLb96D/tgnxRMLmRnTETjhbQ== X-Google-Smtp-Source: ABdhPJyFOMU6T+owZA0xINIT11Tj46q+KH+V135ZjsSHI/oDFrAOsbqBfE4aDhI18cgmWjR1husmGw== X-Received: by 2002:adf:e849:: with SMTP id d9mr19065260wrn.25.1603716720303; Mon, 26 Oct 2020 05:52:00 -0700 (PDT) Received: from balsini.lon.corp.google.com ([2a00:79e0:d:210:7220:84ff:fe09:7d5c]) by smtp.gmail.com with ESMTPSA id r1sm24423262wro.18.2020.10.26.05.51.59 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 26 Oct 2020 05:51:59 -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 V10 5/5] fuse: Use daemon creds in passthrough mode Date: Mon, 26 Oct 2020 12:50:16 +0000 Message-Id: <20201026125016.1905945-6-balsini@android.com> X-Mailer: git-send-email 2.29.0.rc1.297.gfa9743e501-goog In-Reply-To: <20201026125016.1905945-1-balsini@android.com> References: <20201026125016.1905945-1-balsini@android.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org When using FUSE passthrough, read/write operations are directly forwarded to the lower file system file through VFS, but there is no guarantee that the process that is triggering the request has the right permissions to access the lower file system. This would cause the read/write access to fail. In passthrough file systems, where the FUSE daemon is responsible for the enforcement of the lower file system access policies, often happens that the process dealing with the FUSE file system doesn't have access to the lower file system. Being the FUSE daemon in charge of implementing the FUSE file operations, that in the case of read/write operations usually simply results in the copy of memory buffers from/to the lower file system respectively, these operations are executed with the FUSE daemon privileges. This patch adds a reference to the FUSE daemon credentials, referenced at FUSE_DEV_IOC_PASSTHROUGH_OPEN ioctl() time so that they can be used to temporarily raise the user credentials when accessing lower file system files in passthrough. The process accessing the FUSE file with passthrough enabled temporarily receives the privileges of the FUSE daemon while performing read/write operations. Similar behavior is implemented 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 is 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. Signed-off-by: Alessio Balsini --- fs/fuse/fuse_i.h | 5 ++++- fs/fuse/passthrough.c | 11 +++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index a888d3df5877..59e033a59551 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -165,10 +165,13 @@ struct fuse_release_args; /** * Reference to lower filesystem file for read/write operations handled in - * passthrough mode + * passthrough mode. + * This struct also tracks the credentials to be used for handling read/write + * operations. */ struct fuse_passthrough { struct file *filp; + struct cred *cred; }; /** FUSE specific file data */ diff --git a/fs/fuse/passthrough.c b/fs/fuse/passthrough.c index 10b6872cdaa7..ab81dd8f010b 100644 --- a/fs/fuse/passthrough.c +++ b/fs/fuse/passthrough.c @@ -67,6 +67,7 @@ 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; @@ -74,6 +75,7 @@ ssize_t fuse_passthrough_read_iter(struct kiocb *iocb_fuse, if (!iov_iter_count(iter)) return 0; + old_cred = override_creds(ff->passthrough.cred); if (is_sync_kiocb(iocb_fuse)) { ret = vfs_iter_read(passthrough_filp, iter, &iocb_fuse->ki_pos, iocb_to_rw_flags(iocb_fuse->ki_flags)); @@ -91,6 +93,7 @@ ssize_t fuse_passthrough_read_iter(struct kiocb *iocb_fuse, if (ret != -EIOCBQUEUED) fuse_aio_cleanup_handler(aio_req); } + revert_creds(old_cred); return ret; } @@ -99,6 +102,7 @@ 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); @@ -110,6 +114,7 @@ ssize_t fuse_passthrough_write_iter(struct kiocb *iocb_fuse, inode_lock(fuse_inode); + old_cred = override_creds(ff->passthrough.cred); if (is_sync_kiocb(iocb_fuse)) { file_start_write(passthrough_filp); ret = vfs_iter_write(passthrough_filp, iter, &iocb_fuse->ki_pos, @@ -137,6 +142,7 @@ ssize_t fuse_passthrough_write_iter(struct kiocb *iocb_fuse, fuse_aio_cleanup_handler(aio_req); } out: + revert_creds(old_cred); inode_unlock(fuse_inode); return ret; @@ -174,6 +180,7 @@ int fuse_passthrough_open(struct fuse_dev *fud, return -ENOMEM; passthrough->filp = passthrough_filp; + passthrough->cred = prepare_creds(); idr_preload(GFP_KERNEL); spin_lock(&fc->passthrough_req_lock); @@ -231,4 +238,8 @@ void fuse_passthrough_release(struct fuse_passthrough *passthrough) fput(passthrough->filp); passthrough->filp = NULL; } + if (passthrough->cred) { + put_cred(passthrough->cred); + passthrough->cred = NULL; + } }