fuse:Porting fuse passthrough feature
Android common kernel already support fuse passthrough Backport to qcom sm2020 baseline Change-Id: Icd1791f5a4cc07a8af080426f69124764e0872a2 Signed-off-by: a17671 <a17671@motorola.com> Reviewed-on: https://gerrit.mot.com/2283027 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Xiangpo Zhao <zhaoxp3@motorola.com> Submit-Approved: Jira Key
This commit is contained in:
1 parent
dc9d750ffe
commit
e90e7499c7
9 files changed
+458
-16
No files matched your search
@@ -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
|
||||
@@ -621,6 +621,8 @@ static int __init cuse_init(void)
|
||||
cuse_channel_fops.owner = THIS_MODULE;
|
||||
cuse_channel_fops.open = cuse_channel_open;
|
||||
cuse_channel_fops.release = cuse_channel_release;
|
||||
/* CUSE is not prepared for FUSE_DEV_IOC_CLONE */
|
||||
cuse_channel_fops.unlocked_ioctl = NULL;
|
||||
|
||||
cuse_class = class_create(THIS_MODULE, "cuse");
|
||||
if (IS_ERR(cuse_class))
|
||||
|
||||
+30
-11
@@ -2261,37 +2261,56 @@ 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;
|
||||
int res;
|
||||
int oldfd;
|
||||
struct fuse_dev *fud = NULL;
|
||||
struct fuse_passthrough_out pto;
|
||||
|
||||
if (cmd == FUSE_DEV_IOC_CLONE) {
|
||||
int oldfd;
|
||||
if (_IOC_TYPE(cmd) != FUSE_DEV_IOC_MAGIC)
|
||||
return -EINVAL;
|
||||
|
||||
err = -EFAULT;
|
||||
if (!get_user(oldfd, (__u32 __user *) arg)) {
|
||||
switch (_IOC_NR(cmd)) {
|
||||
case _IOC_NR(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;
|
||||
|
||||
/*
|
||||
* 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 _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;
|
||||
}
|
||||
return err;
|
||||
return res;
|
||||
}
|
||||
|
||||
const struct file_operations fuse_dev_operations = {
|
||||
|
||||
@@ -505,6 +505,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) {
|
||||
|
||||
+33
-2
@@ -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;
|
||||
@@ -258,6 +258,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) {
|
||||
@@ -927,6 +929,10 @@ static ssize_t fuse_file_read_iter(struct kiocb *iocb, struct iov_iter *to)
|
||||
{
|
||||
struct inode *inode = iocb->ki_filp->f_mapping->host;
|
||||
struct fuse_conn *fc = get_fuse_conn(inode);
|
||||
struct fuse_file *ff = iocb->ki_filp->private_data;
|
||||
|
||||
if (is_bad_inode(inode))
|
||||
return -EIO;
|
||||
|
||||
/*
|
||||
* In auto invalidate mode, always update attributes on read.
|
||||
@@ -941,7 +947,10 @@ static ssize_t fuse_file_read_iter(struct kiocb *iocb, struct iov_iter *to)
|
||||
return err;
|
||||
}
|
||||
|
||||
return generic_file_read_iter(iocb, to);
|
||||
if (ff->passthrough.filp)
|
||||
return fuse_passthrough_read_iter(iocb, to);
|
||||
else
|
||||
return generic_file_read_iter(iocb, to);
|
||||
}
|
||||
|
||||
static void fuse_write_fill(struct fuse_req *req, struct fuse_file *ff,
|
||||
@@ -1179,12 +1188,16 @@ static ssize_t fuse_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
|
||||
{
|
||||
struct file *file = iocb->ki_filp;
|
||||
struct address_space *mapping = file->f_mapping;
|
||||
struct fuse_file *ff = file->private_data;
|
||||
ssize_t written = 0;
|
||||
ssize_t written_buffered = 0;
|
||||
struct inode *inode = mapping->host;
|
||||
ssize_t err;
|
||||
loff_t endbyte = 0;
|
||||
|
||||
if (ff->passthrough.filp)
|
||||
return fuse_passthrough_write_iter(iocb, from);
|
||||
|
||||
if (get_fuse_conn(inode)->writeback_cache) {
|
||||
/* Update size (EOF optimization) and mode (SUID clearing) */
|
||||
err = fuse_update_attributes(mapping->host, file);
|
||||
@@ -1420,10 +1433,14 @@ static ssize_t __fuse_direct_read(struct fuse_io_priv *io,
|
||||
{
|
||||
ssize_t res;
|
||||
struct inode *inode = file_inode(io->iocb->ki_filp);
|
||||
struct fuse_file *ff = io->iocb->ki_filp->private_data;
|
||||
|
||||
if (is_bad_inode(inode))
|
||||
return -EIO;
|
||||
|
||||
if (ff->passthrough.filp)
|
||||
return fuse_passthrough_read_iter(io->iocb, iter);
|
||||
|
||||
res = fuse_direct_io(io, iter, ppos, 0);
|
||||
|
||||
fuse_invalidate_attr(inode);
|
||||
@@ -1441,11 +1458,15 @@ static ssize_t fuse_direct_write_iter(struct kiocb *iocb, struct iov_iter *from)
|
||||
{
|
||||
struct inode *inode = file_inode(iocb->ki_filp);
|
||||
struct fuse_io_priv io = FUSE_IO_PRIV_SYNC(iocb);
|
||||
struct fuse_file *ff = iocb->ki_filp->private_data;
|
||||
ssize_t res;
|
||||
|
||||
if (is_bad_inode(inode))
|
||||
return -EIO;
|
||||
|
||||
if (ff->passthrough.filp)
|
||||
return fuse_passthrough_write_iter(iocb, from);
|
||||
|
||||
/* Don't allow parallel writes to the same file */
|
||||
inode_lock(inode);
|
||||
res = generic_write_checks(iocb, from);
|
||||
@@ -2095,6 +2116,11 @@ static const struct vm_operations_struct fuse_file_vm_ops = {
|
||||
|
||||
static int fuse_file_mmap(struct file *file, struct vm_area_struct *vma)
|
||||
{
|
||||
struct fuse_file *ff = file->private_data;
|
||||
|
||||
if (ff->passthrough.filp)
|
||||
return fuse_passthrough_mmap(file, vma);
|
||||
|
||||
if ((vma->vm_flags & VM_SHARED) && (vma->vm_flags & VM_MAYWRITE))
|
||||
fuse_link_write_file(file);
|
||||
|
||||
@@ -2105,6 +2131,11 @@ static int fuse_file_mmap(struct file *file, struct vm_area_struct *vma)
|
||||
|
||||
static int fuse_direct_mmap(struct file *file, struct vm_area_struct *vma)
|
||||
{
|
||||
struct fuse_file *ff = file->private_data;
|
||||
|
||||
if (ff->passthrough.filp)
|
||||
return fuse_passthrough_mmap(file, vma);
|
||||
|
||||
/* Can't provide the coherency needed for MAP_SHARED */
|
||||
if (vma->vm_flags & VM_MAYSHARE)
|
||||
return -ENODEV;
|
||||
|
||||
@@ -122,6 +122,17 @@ enum {
|
||||
|
||||
struct fuse_conn;
|
||||
|
||||
/**
|
||||
* Reference to lower filesystem file for read/write operations handled in
|
||||
* 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 */
|
||||
struct fuse_file {
|
||||
/** Fuse connection for this file */
|
||||
@@ -148,6 +159,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;
|
||||
|
||||
@@ -646,6 +660,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;
|
||||
|
||||
@@ -684,6 +701,12 @@ struct fuse_conn {
|
||||
|
||||
/** List of device instances belonging to this connection */
|
||||
struct list_head devices;
|
||||
|
||||
/** IDR for passthrough requests */
|
||||
struct idr passthrough_req;
|
||||
|
||||
/** Proctects passthrough_req */
|
||||
spinlock_t passthrough_req_lock;
|
||||
};
|
||||
|
||||
static inline struct fuse_conn *get_fuse_conn_super(struct super_block *sb)
|
||||
@@ -1004,4 +1027,14 @@ 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);
|
||||
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);
|
||||
ssize_t fuse_passthrough_mmap(struct file *file, struct vm_area_struct *vma);
|
||||
|
||||
#endif /* _FS_FUSE_I_H */
|
||||
+21
-1
@@ -609,6 +609,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);
|
||||
@@ -618,6 +619,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;
|
||||
@@ -930,6 +932,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;
|
||||
@@ -961,7 +969,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);
|
||||
@@ -977,9 +985,21 @@ 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)
|
||||
{
|
||||
struct fuse_passthrough *passthrough = (struct fuse_passthrough *)p;
|
||||
|
||||
fuse_passthrough_release(passthrough);
|
||||
kfree(p);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,323 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#include "fuse_i.h"
|
||||
|
||||
#include <linux/file.h>
|
||||
#include <linux/fuse.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/uio.h>
|
||||
|
||||
struct fuse_aio_req {
|
||||
struct kiocb iocb;
|
||||
struct kiocb *iocb_fuse;
|
||||
};
|
||||
|
||||
static void fuse_file_accessed(struct file *dst_file, struct file *src_file)
|
||||
{
|
||||
struct inode *dst_inode;
|
||||
struct inode *src_inode;
|
||||
|
||||
if (dst_file->f_flags & O_NOATIME)
|
||||
return;
|
||||
|
||||
dst_inode = file_inode(dst_file);
|
||||
src_inode = file_inode(src_file);
|
||||
|
||||
if ((!timespec64_equal(&dst_inode->i_mtime, &src_inode->i_mtime) ||
|
||||
!timespec64_equal(&dst_inode->i_ctime, &src_inode->i_ctime))) {
|
||||
dst_inode->i_mtime = src_inode->i_mtime;
|
||||
dst_inode->i_ctime = src_inode->i_ctime;
|
||||
}
|
||||
|
||||
touch_atime(&dst_file->f_path);
|
||||
}
|
||||
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);
|
||||
|
||||
dst->i_atime = src->i_atime;
|
||||
dst->i_mtime = src->i_mtime;
|
||||
dst->i_ctime = src->i_ctime;
|
||||
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;
|
||||
}
|
||||
|
||||
static inline void kiocb_clone(struct kiocb *kiocb, struct kiocb *kiocb_src,
|
||||
struct file *filp)
|
||||
{
|
||||
*kiocb = (struct kiocb){
|
||||
.ki_filp = filp,
|
||||
.ki_flags = kiocb_src->ki_flags,
|
||||
.ki_hint = kiocb_src->ki_hint,
|
||||
.ki_ioprio = kiocb_src->ki_ioprio,
|
||||
.ki_pos = kiocb_src->ki_pos,
|
||||
};
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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 = 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));
|
||||
} else {
|
||||
struct fuse_aio_req *aio_req;
|
||||
|
||||
aio_req = kmalloc(sizeof(struct fuse_aio_req), GFP_KERNEL);
|
||||
if (!aio_req) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
out:
|
||||
revert_creds(old_cred);
|
||||
|
||||
fuse_file_accessed(fuse_filp, passthrough_filp);
|
||||
|
||||
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;
|
||||
struct inode *passthrough_inode = file_inode(passthrough_filp);
|
||||
|
||||
if (!iov_iter_count(iter))
|
||||
return 0;
|
||||
|
||||
inode_lock(fuse_inode);
|
||||
|
||||
fuse_copyattr(fuse_filp, passthrough_filp);
|
||||
|
||||
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,
|
||||
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:
|
||||
revert_creds(old_cred);
|
||||
inode_unlock(fuse_inode);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
ssize_t fuse_passthrough_mmap(struct file *file, struct vm_area_struct *vma)
|
||||
{
|
||||
int ret;
|
||||
const struct cred *old_cred;
|
||||
struct fuse_file *ff = file->private_data;
|
||||
struct file *passthrough_filp = ff->passthrough.filp;
|
||||
|
||||
if (!passthrough_filp->f_op->mmap)
|
||||
return -ENODEV;
|
||||
|
||||
if (WARN_ON(file != vma->vm_file))
|
||||
return -EIO;
|
||||
|
||||
vma->vm_file = get_file(passthrough_filp);
|
||||
|
||||
old_cred = override_creds(ff->passthrough.cred);
|
||||
ret = call_mmap(vma->vm_file, vma);
|
||||
revert_creds(old_cred);
|
||||
|
||||
if (ret)
|
||||
fput(passthrough_filp);
|
||||
else
|
||||
fput(file);
|
||||
|
||||
fuse_file_accessed(file, passthrough_filp);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int fuse_passthrough_open(struct fuse_dev *fud,
|
||||
struct fuse_passthrough_out *pto)
|
||||
{
|
||||
int res;
|
||||
struct file *passthrough_filp;
|
||||
struct fuse_conn *fc = fud->fc;
|
||||
struct inode *passthrough_inode;
|
||||
struct super_block *passthrough_sb;
|
||||
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");
|
||||
res = -EBADF;
|
||||
goto err_free_file;
|
||||
}
|
||||
|
||||
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");
|
||||
res = -EINVAL;
|
||||
goto err_free_file;
|
||||
}
|
||||
|
||||
passthrough = kmalloc(sizeof(struct fuse_passthrough), GFP_KERNEL);
|
||||
if (!passthrough) {
|
||||
res = -ENOMEM;
|
||||
goto err_free_file;
|
||||
}
|
||||
|
||||
passthrough->filp = passthrough_filp;
|
||||
passthrough->cred = prepare_creds();
|
||||
|
||||
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)
|
||||
return res;
|
||||
|
||||
fuse_passthrough_release(passthrough);
|
||||
kfree(passthrough);
|
||||
|
||||
err_free_file:
|
||||
fput(passthrough_filp);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int fuse_passthrough_setup(struct fuse_conn *fc, struct fuse_file *ff,
|
||||
struct fuse_open_out *openarg)
|
||||
{
|
||||
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;
|
||||
|
||||
ff->passthrough = *passthrough;
|
||||
kfree(passthrough);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void fuse_passthrough_release(struct fuse_passthrough *passthrough)
|
||||
{
|
||||
if (passthrough->filp) {
|
||||
fput(passthrough->filp);
|
||||
passthrough->filp = NULL;
|
||||
}
|
||||
if (passthrough->cred) {
|
||||
put_cred(passthrough->cred);
|
||||
passthrough->cred = NULL;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -782,7 +791,10 @@ struct fuse_notify_retrieve_in {
|
||||
};
|
||||
|
||||
/* Device ioctls: */
|
||||
#define FUSE_DEV_IOC_CLONE _IOR(229, 0, uint32_t)
|
||||
#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;
|
||||
|
||||
Reference in new issue
Block a user