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:
a17671 authored and Gilberto Gambugge committed 2023-06-01 00:51:08 -03:00
1 parent dc9d750ffe
commit e90e7499c7
9 files changed
+458 -16

No files matched your search

+1
View File
@@ -6,3 +6,4 @@ obj-$(CONFIG_FUSE_FS) += fuse.o
obj-$(CONFIG_CUSE) += cuse.o
fuse-objs := dev.o dir.o file.o inode.o control.o xattr.o acl.o
fuse-objs += passthrough.o
+2
View File
@@ -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
View File
@@ -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 = {
+1
View File
@@ -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
View File
@@ -136,7 +136,7 @@ int fuse_do_open(struct fuse_conn *fc, u64 nodeid, struct file *file,
if (!err) {
ff->fh = outarg.fh;
ff->open_flags = outarg.open_flags;
fuse_passthrough_setup(fc, ff, &outarg);
} else if (err != -ENOSYS || isdir) {
fuse_file_free(ff);
return err;
@@ -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;
+33
View File
@@ -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
View File
@@ -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);
}
+323
View File
@@ -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;
}
}
+14 -2
View File
@@ -274,6 +274,7 @@ struct fuse_file_lock {
#define FUSE_HANDLE_KILLPRIV (1 << 19)
#define FUSE_POSIX_ACL (1 << 20)
#define FUSE_ABORT_ERROR (1 << 21)
#define FUSE_PASSTHROUGH (1 << 31)
/**
* CUSE INIT request/reply flags
@@ -506,7 +507,7 @@ struct fuse_create_in {
struct fuse_open_out {
uint64_t fh;
uint32_t open_flags;
uint32_t padding;
uint32_t passthrough_fh;
};
struct fuse_release_in {
@@ -707,6 +708,14 @@ struct fuse_in_header {
uint32_t padding;
};
/* fuse_passthrough_out for passthrough V1 */
struct fuse_passthrough_out {
uint32_t fd;
/* For future implementation */
uint32_t len;
void *vec;
};
struct fuse_out_header {
uint32_t len;
int32_t error;
@@ -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;