mergerfs/libfuse/lib/fuse_lowlevel.c
Antonio SJ Musumeci 84592a9f13 Remove splicing features
After numerous tests it was found the splice features were at best
the same performance as standard IO and at worse actually slower.
To simplify the code all splice features are removed.
2023-02-26 01:42:58 -05:00

1947 lines
44 KiB
C

/*
FUSE: Filesystem in Userspace
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
This program can be distributed under the terms of the GNU LGPLv2.
See the file COPYING.LIB
*/
#define _GNU_SOURCE
#include "lfmp.h"
#include "config.h"
#include "debug.h"
#include "fuse_i.h"
#include "fuse_kernel.h"
#include "fuse_opt.h"
#include "fuse_misc.h"
#include "fuse_pollhandle.h"
#include "fuse_msgbuf.hpp"
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
#include <errno.h>
#include <assert.h>
#include <sys/file.h>
#ifndef F_LINUX_SPECIFIC_BASE
#define F_LINUX_SPECIFIC_BASE 1024
#endif
#ifndef F_SETPIPE_SZ
#define F_SETPIPE_SZ (F_LINUX_SPECIFIC_BASE + 7)
#endif
#define PARAM(inarg) (((char*)(inarg)) + sizeof(*(inarg)))
#define OFFSET_MAX 0x7fffffffffffffffLL
#define container_of(ptr, type, member) ({ \
const typeof( ((type*)0)->member ) *__mptr = (ptr); \
(type *)( (char*)__mptr - offsetof(type,member) );})
static size_t pagesize;
static lfmp_t g_FMP_fuse_req;
static
__attribute__((constructor))
void
fuse_ll_constructor(void)
{
pagesize = sysconf(_SC_PAGESIZE);
lfmp_init(&g_FMP_fuse_req,sizeof(struct fuse_req),1);
}
static
__attribute__((destructor))
void
fuse_ll_destructor(void)
{
lfmp_destroy(&g_FMP_fuse_req);
}
static
void
convert_stat(const struct stat *stbuf_,
struct fuse_attr *attr_)
{
attr_->ino = stbuf_->st_ino;
attr_->mode = stbuf_->st_mode;
attr_->nlink = stbuf_->st_nlink;
attr_->uid = stbuf_->st_uid;
attr_->gid = stbuf_->st_gid;
attr_->rdev = stbuf_->st_rdev;
attr_->size = stbuf_->st_size;
attr_->blksize = stbuf_->st_blksize;
attr_->blocks = stbuf_->st_blocks;
attr_->atime = stbuf_->st_atime;
attr_->mtime = stbuf_->st_mtime;
attr_->ctime = stbuf_->st_ctime;
attr_->atimensec = ST_ATIM_NSEC(stbuf_);
attr_->mtimensec = ST_MTIM_NSEC(stbuf_);
attr_->ctimensec = ST_CTIM_NSEC(stbuf_);
}
static
size_t
iov_length(const struct iovec *iov,
size_t count)
{
size_t seg;
size_t ret = 0;
for(seg = 0; seg < count; seg++)
ret += iov[seg].iov_len;
return ret;
}
static
void
destroy_req(fuse_req_t req)
{
lfmp_free(&g_FMP_fuse_req,req);
}
static
struct fuse_req*
fuse_ll_alloc_req(struct fuse_ll *f)
{
struct fuse_req *req;
req = (struct fuse_req*)lfmp_calloc(&g_FMP_fuse_req);
if(req == NULL)
{
fprintf(stderr, "fuse: failed to allocate request\n");
}
else
{
req->f = f;
}
return req;
}
static
int
fuse_send_msg(struct fuse_ll *f,
struct fuse_chan *ch,
struct iovec *iov,
int count)
{
int rv;
struct fuse_out_header *out = iov[0].iov_base;
out->len = iov_length(iov, count);
rv = writev(fuse_chan_fd(ch),iov,count);
if(rv == -1)
return -errno;
return 0;
}
int
fuse_send_reply_iov_nofree(fuse_req_t req,
int error,
struct iovec *iov,
int count)
{
struct fuse_out_header out;
if(error <= -1000 || error > 0)
{
fprintf(stderr, "fuse: bad error value: %i\n",error);
error = -ERANGE;
}
out.unique = req->unique;
out.error = error;
iov[0].iov_base = &out;
iov[0].iov_len = sizeof(struct fuse_out_header);
return fuse_send_msg(req->f, req->ch, iov, count);
}
static
int
send_reply_iov(fuse_req_t req,
int error,
struct iovec *iov,
int count)
{
int res;
res = fuse_send_reply_iov_nofree(req, error, iov, count);
destroy_req(req);
return res;
}
static
int
send_reply(fuse_req_t req,
int error,
const void *arg,
size_t argsize)
{
struct iovec iov[2];
int count = 1;
if(argsize)
{
iov[1].iov_base = (void *) arg;
iov[1].iov_len = argsize;
count++;
}
return send_reply_iov(req, error, iov, count);
}
static
void
convert_statfs(const struct statvfs *stbuf,
struct fuse_kstatfs *kstatfs)
{
kstatfs->bsize = stbuf->f_bsize;
kstatfs->frsize = stbuf->f_frsize;
kstatfs->blocks = stbuf->f_blocks;
kstatfs->bfree = stbuf->f_bfree;
kstatfs->bavail = stbuf->f_bavail;
kstatfs->files = stbuf->f_files;
kstatfs->ffree = stbuf->f_ffree;
kstatfs->namelen = stbuf->f_namemax;
}
static
int
send_reply_ok(fuse_req_t req,
const void *arg,
size_t argsize)
{
return send_reply(req, 0, arg, argsize);
}
int
fuse_reply_err(fuse_req_t req_,
int err_)
{
return send_reply(req_,-err_,NULL,0);
}
void
fuse_reply_none(fuse_req_t req)
{
destroy_req(req);
}
static
void
fill_entry(struct fuse_entry_out *arg,
const struct fuse_entry_param *e)
{
arg->nodeid = e->ino;
arg->generation = e->generation;
arg->entry_valid = e->timeout.entry;
arg->entry_valid_nsec = 0;
arg->attr_valid = e->timeout.attr;
arg->attr_valid_nsec = 0;
convert_stat(&e->attr,&arg->attr);
}
static
void
fill_open(struct fuse_open_out *arg,
const fuse_file_info_t *f)
{
arg->fh = f->fh;
if(f->direct_io)
arg->open_flags |= FOPEN_DIRECT_IO;
if(f->keep_cache)
arg->open_flags |= FOPEN_KEEP_CACHE;
if(f->nonseekable)
arg->open_flags |= FOPEN_NONSEEKABLE;
if(f->cache_readdir)
arg->open_flags |= FOPEN_CACHE_DIR;
}
int
fuse_reply_entry(fuse_req_t req,
const struct fuse_entry_param *e)
{
struct fuse_entry_out arg = {0};
size_t size = req->f->conn.proto_minor < 9 ?
FUSE_COMPAT_ENTRY_OUT_SIZE : sizeof(arg);
/* before ABI 7.4 e->ino == 0 was invalid, only ENOENT meant
negative entry */
if(!e->ino && req->f->conn.proto_minor < 4)
return fuse_reply_err(req, ENOENT);
fill_entry(&arg, e);
return send_reply_ok(req, &arg, size);
}
struct fuse_create_out
{
struct fuse_entry_out e;
struct fuse_open_out o;
};
int
fuse_reply_create(fuse_req_t req,
const struct fuse_entry_param *e,
const fuse_file_info_t *f)
{
struct fuse_create_out buf = {0};
size_t entrysize = req->f->conn.proto_minor < 9 ?
FUSE_COMPAT_ENTRY_OUT_SIZE : sizeof(struct fuse_entry_out);
struct fuse_entry_out *earg = (struct fuse_entry_out*)&buf.e;
struct fuse_open_out *oarg = (struct fuse_open_out*)(((char*)&buf)+entrysize);
fill_entry(earg, e);
fill_open(oarg, f);
return send_reply_ok(req, &buf, entrysize + sizeof(struct fuse_open_out));
}
int
fuse_reply_attr(fuse_req_t req,
const struct stat *attr,
const uint64_t timeout)
{
struct fuse_attr_out arg = {0};
size_t size = req->f->conn.proto_minor < 9 ?
FUSE_COMPAT_ATTR_OUT_SIZE : sizeof(arg);
arg.attr_valid = timeout;
arg.attr_valid_nsec = 0;
convert_stat(attr,&arg.attr);
return send_reply_ok(req,&arg,size);
}
int
fuse_reply_readlink(fuse_req_t req,
const char *linkname)
{
return send_reply_ok(req, linkname, strlen(linkname));
}
int
fuse_reply_open(fuse_req_t req,
const fuse_file_info_t *f)
{
struct fuse_open_out arg = {0};
fill_open(&arg, f);
return send_reply_ok(req, &arg, sizeof(arg));
}
int
fuse_reply_write(fuse_req_t req,
size_t count)
{
struct fuse_write_out arg = {0};
arg.size = count;
return send_reply_ok(req, &arg, sizeof(arg));
}
int
fuse_reply_buf(fuse_req_t req,
const char *buf,
size_t size)
{
return send_reply_ok(req, buf, size);
}
static
int
fuse_send_data_iov_fallback(struct fuse_ll *f,
struct fuse_chan *ch,
struct iovec *iov,
int iov_count,
struct fuse_bufvec *buf,
size_t len)
{
int res;
struct fuse_bufvec mem_buf = FUSE_BUFVEC_INIT(len);
/* Optimize common case */
if(buf->count == 1 && buf->idx == 0 && buf->off == 0 &&
!(buf->buf[0].flags & FUSE_BUF_IS_FD))
{
/* FIXME: also avoid memory copy if there are multiple buffers
but none of them contain an fd */
iov[iov_count].iov_base = buf->buf[0].mem;
iov[iov_count].iov_len = len;
iov_count++;
return fuse_send_msg(f, ch, iov, iov_count);
}
fuse_msgbuf_t *msgbuf;
msgbuf = msgbuf_alloc();
if(msgbuf == NULL)
return -ENOMEM;
mem_buf.buf[0].mem = msgbuf->mem;
res = fuse_buf_copy(&mem_buf, buf, 0);
if(res < 0)
{
msgbuf_free(msgbuf);
return -res;
}
len = res;
iov[iov_count].iov_base = msgbuf->mem;
iov[iov_count].iov_len = len;
iov_count++;
res = fuse_send_msg(f, ch, iov, iov_count);
msgbuf_free(msgbuf);
return res;
}
struct fuse_ll_pipe
{
size_t size;
int can_grow;
int pipe[2];
};
static
void
fuse_ll_pipe_free(struct fuse_ll_pipe *llp)
{
close(llp->pipe[0]);
close(llp->pipe[1]);
free(llp);
}
static
int
fuse_send_data_iov(struct fuse_ll *f,
struct fuse_chan *ch,
struct iovec *iov,
int iov_count,
struct fuse_bufvec *buf,
unsigned int flags)
{
size_t len = fuse_buf_size(buf);
(void) flags;
return fuse_send_data_iov_fallback(f, ch, iov, iov_count, buf, len);
}
int
fuse_reply_data(fuse_req_t req,
struct fuse_bufvec *bufv,
enum fuse_buf_copy_flags flags)
{
struct iovec iov[2];
struct fuse_out_header out;
int res;
iov[0].iov_base = &out;
iov[0].iov_len = sizeof(struct fuse_out_header);
out.unique = req->unique;
out.error = 0;
res = fuse_send_data_iov(req->f, req->ch, iov, 1, bufv, flags);
if(res <= 0)
{
destroy_req(req);
return res;
}
else
{
return fuse_reply_err(req, res);
}
}
int
fuse_reply_statfs(fuse_req_t req,
const struct statvfs *stbuf)
{
struct fuse_statfs_out arg = {0};
size_t size = req->f->conn.proto_minor < 4 ?
FUSE_COMPAT_STATFS_SIZE : sizeof(arg);
convert_statfs(stbuf, &arg.st);
return send_reply_ok(req, &arg, size);
}
int
fuse_reply_xattr(fuse_req_t req,
size_t count)
{
struct fuse_getxattr_out arg = {0};
arg.size = count;
return send_reply_ok(req, &arg, sizeof(arg));
}
int
fuse_reply_lock(fuse_req_t req,
const struct flock *lock)
{
struct fuse_lk_out arg = {0};
arg.lk.type = lock->l_type;
if(lock->l_type != F_UNLCK)
{
arg.lk.start = lock->l_start;
if(lock->l_len == 0)
arg.lk.end = OFFSET_MAX;
else
arg.lk.end = lock->l_start + lock->l_len - 1;
}
arg.lk.pid = lock->l_pid;
return send_reply_ok(req, &arg, sizeof(arg));
}
int
fuse_reply_bmap(fuse_req_t req,
uint64_t idx)
{
struct fuse_bmap_out arg = {0};
arg.block = idx;
return send_reply_ok(req, &arg, sizeof(arg));
}
static
struct fuse_ioctl_iovec*
fuse_ioctl_iovec_copy(const struct iovec *iov,
size_t count)
{
struct fuse_ioctl_iovec *fiov;
size_t i;
fiov = malloc(sizeof(fiov[0]) * count);
if(!fiov)
return NULL;
for (i = 0; i < count; i++)
{
fiov[i].base = (uintptr_t) iov[i].iov_base;
fiov[i].len = iov[i].iov_len;
}
return fiov;
}
int
fuse_reply_ioctl_retry(fuse_req_t req,
const struct iovec *in_iov,
size_t in_count,
const struct iovec *out_iov,
size_t out_count)
{
struct fuse_ioctl_out arg = {0};
struct fuse_ioctl_iovec *in_fiov = NULL;
struct fuse_ioctl_iovec *out_fiov = NULL;
struct iovec iov[4];
size_t count = 1;
int res;
arg.flags |= FUSE_IOCTL_RETRY;
arg.in_iovs = in_count;
arg.out_iovs = out_count;
iov[count].iov_base = &arg;
iov[count].iov_len = sizeof(arg);
count++;
if(req->f->conn.proto_minor < 16)
{
if(in_count)
{
iov[count].iov_base = (void *)in_iov;
iov[count].iov_len = sizeof(in_iov[0]) * in_count;
count++;
}
if(out_count)
{
iov[count].iov_base = (void *)out_iov;
iov[count].iov_len = sizeof(out_iov[0]) * out_count;
count++;
}
}
else
{
/* Can't handle non-compat 64bit ioctls on 32bit */
if((sizeof(void *) == 4) && (req->ioctl_64bit))
{
res = fuse_reply_err(req, EINVAL);
goto out;
}
if(in_count)
{
in_fiov = fuse_ioctl_iovec_copy(in_iov, in_count);
if(!in_fiov)
goto enomem;
iov[count].iov_base = (void *)in_fiov;
iov[count].iov_len = sizeof(in_fiov[0]) * in_count;
count++;
}
if(out_count)
{
out_fiov = fuse_ioctl_iovec_copy(out_iov, out_count);
if(!out_fiov)
goto enomem;
iov[count].iov_base = (void *)out_fiov;
iov[count].iov_len = sizeof(out_fiov[0]) * out_count;
count++;
}
}
res = send_reply_iov(req, 0, iov, count);
out:
free(in_fiov);
free(out_fiov);
return res;
enomem:
res = fuse_reply_err(req, ENOMEM);
goto out;
}
int
fuse_reply_ioctl(fuse_req_t req,
int result,
const void *buf,
uint32_t size)
{
int count;
struct iovec iov[3];
struct fuse_ioctl_out arg;
arg.result = result;
arg.flags = 0;
arg.in_iovs = 0;
arg.out_iovs = 0;
count = 1;
iov[count].iov_base = &arg;
iov[count].iov_len = sizeof(arg);
count++;
if(size)
{
iov[count].iov_base = (char*)buf;
iov[count].iov_len = size;
count++;
}
return send_reply_iov(req, 0, iov, count);
}
int
fuse_reply_ioctl_iov(fuse_req_t req,
int result,
const struct iovec *iov,
int count)
{
struct iovec *padded_iov;
struct fuse_ioctl_out arg = {0};
int res;
padded_iov = malloc((count + 2) * sizeof(struct iovec));
if(padded_iov == NULL)
return fuse_reply_err(req, ENOMEM);
arg.result = result;
padded_iov[1].iov_base = &arg;
padded_iov[1].iov_len = sizeof(arg);
memcpy(&padded_iov[2], iov, count * sizeof(struct iovec));
res = send_reply_iov(req, 0, padded_iov, count + 2);
free(padded_iov);
return res;
}
int
fuse_reply_poll(fuse_req_t req,
unsigned revents)
{
struct fuse_poll_out arg = {0};
arg.revents = revents;
return send_reply_ok(req, &arg, sizeof(arg));
}
static
void
do_lookup(fuse_req_t req,
struct fuse_in_header *hdr_)
{
req->f->op.lookup(req,hdr_);
}
static
void
do_forget(fuse_req_t req,
struct fuse_in_header *hdr_)
{
req->f->op.forget(req,hdr_);
}
static
void
do_batch_forget(fuse_req_t req,
struct fuse_in_header *hdr_)
{
req->f->op.forget_multi(req,hdr_);
}
static
void
do_getattr(fuse_req_t req,
struct fuse_in_header *hdr_)
{
req->f->op.getattr(req, hdr_);
}
static
void
do_setattr(fuse_req_t req_,
struct fuse_in_header *hdr_)
{
req_->f->op.setattr(req_,hdr_);
}
static
void
do_access(fuse_req_t req,
struct fuse_in_header *hdr_)
{
req->f->op.access(req,hdr_);
}
static
void
do_readlink(fuse_req_t req,
struct fuse_in_header *hdr_)
{
req->f->op.readlink(req,hdr_);
}
static
void
do_mknod(fuse_req_t req,
struct fuse_in_header *hdr_)
{
req->f->op.mknod(req,hdr_);
}
static
void
do_mkdir(fuse_req_t req,
struct fuse_in_header *hdr_)
{
req->f->op.mkdir(req,hdr_);
}
static
void
do_unlink(fuse_req_t req,
struct fuse_in_header *hdr_)
{
req->f->op.unlink(req,hdr_);
}
static
void
do_rmdir(fuse_req_t req,
struct fuse_in_header *hdr_)
{
req->f->op.rmdir(req,hdr_);
}
static
void
do_symlink(fuse_req_t req,
struct fuse_in_header *hdr_)
{
req->f->op.symlink(req,hdr_);
}
static
void
do_rename(fuse_req_t req,
struct fuse_in_header *hdr_)
{
req->f->op.rename(req,hdr_);
}
static
void
do_link(fuse_req_t req,
struct fuse_in_header *hdr_)
{
req->f->op.link(req,hdr_);
}
static
void
do_create(fuse_req_t req,
struct fuse_in_header *hdr_)
{
req->f->op.create(req,hdr_);
}
static
void
do_open(fuse_req_t req,
struct fuse_in_header *hdr_)
{
req->f->op.open(req,hdr_);
}
static
void
do_read(fuse_req_t req,
struct fuse_in_header *hdr_)
{
req->f->op.read(req,hdr_);
}
static
void
do_write(fuse_req_t req,
struct fuse_in_header *hdr_)
{
req->f->op.write(req,hdr_);
}
static
void
do_flush(fuse_req_t req,
struct fuse_in_header *hdr_)
{
req->f->op.flush(req,hdr_);
}
static
void
do_release(fuse_req_t req,
struct fuse_in_header *hdr_)
{
req->f->op.release(req,hdr_);
}
static
void
do_fsync(fuse_req_t req,
struct fuse_in_header *hdr_)
{
req->f->op.fsync(req,hdr_);
}
static
void
do_opendir(fuse_req_t req,
struct fuse_in_header *hdr_)
{
req->f->op.opendir(req,hdr_);
}
static
void
do_readdir(fuse_req_t req,
struct fuse_in_header *hdr_)
{
req->f->op.readdir(req,hdr_);
}
static
void
do_readdir_plus(fuse_req_t req_,
struct fuse_in_header *hdr_)
{
req_->f->op.readdir_plus(req_,hdr_);
}
static
void
do_releasedir(fuse_req_t req,
struct fuse_in_header *hdr_)
{
req->f->op.releasedir(req,hdr_);
}
static
void
do_fsyncdir(fuse_req_t req,
struct fuse_in_header *hdr_)
{
req->f->op.fsyncdir(req,hdr_);
}
static
void
do_statfs(fuse_req_t req,
struct fuse_in_header *hdr_)
{
req->f->op.statfs(req,hdr_);
}
static
void
do_setxattr(fuse_req_t req,
struct fuse_in_header *hdr_)
{
req->f->op.setxattr(req,hdr_);
}
static
void
do_getxattr(fuse_req_t req,
struct fuse_in_header *hdr_)
{
req->f->op.getxattr(req,hdr_);
}
static
void
do_listxattr(fuse_req_t req,
struct fuse_in_header *hdr_)
{
req->f->op.listxattr(req,hdr_);
}
static
void
do_removexattr(fuse_req_t req,
struct fuse_in_header *hdr_)
{
req->f->op.removexattr(req,hdr_);
}
static
void
convert_fuse_file_lock(struct fuse_file_lock *fl,
struct flock *flock)
{
memset(flock, 0, sizeof(struct flock));
flock->l_type = fl->type;
flock->l_whence = SEEK_SET;
flock->l_start = fl->start;
if(fl->end == OFFSET_MAX)
flock->l_len = 0;
else
flock->l_len = fl->end - fl->start + 1;
flock->l_pid = fl->pid;
}
static
void
do_getlk(fuse_req_t req,
struct fuse_in_header *hdr_)
{
req->f->op.getlk(req,hdr_);
}
static
void
do_setlk_common(fuse_req_t req,
uint64_t nodeid,
const void *inarg,
int sleep)
{
struct flock flock;
fuse_file_info_t fi = {0};
struct fuse_lk_in *arg = (struct fuse_lk_in*)inarg;
fi.fh = arg->fh;
fi.lock_owner = arg->owner;
if(arg->lk_flags & FUSE_LK_FLOCK)
{
int op = 0;
switch (arg->lk.type)
{
case F_RDLCK:
op = LOCK_SH;
break;
case F_WRLCK:
op = LOCK_EX;
break;
case F_UNLCK:
op = LOCK_UN;
break;
}
if(!sleep)
op |= LOCK_NB;
req->f->op.flock(req,nodeid,&fi,op);
}
else
{
convert_fuse_file_lock(&arg->lk, &flock);
req->f->op.setlk(req,nodeid,&fi,&flock,sleep);
}
}
static
void
do_setlk(fuse_req_t req,
struct fuse_in_header *hdr_)
{
do_setlk_common(req, hdr_->nodeid, &hdr_[1], 0);
}
static
void
do_setlkw(fuse_req_t req,
struct fuse_in_header *hdr_)
{
do_setlk_common(req, hdr_->nodeid, &hdr_[1], 1);
}
static
void
do_interrupt(fuse_req_t req,
struct fuse_in_header *hdr_)
{
destroy_req(req);
}
static
void
do_bmap(fuse_req_t req,
struct fuse_in_header *hdr_)
{
req->f->op.bmap(req,hdr_);
}
static
void
do_ioctl(fuse_req_t req,
struct fuse_in_header *hdr_)
{
req->f->op.ioctl(req, hdr_);
}
void
fuse_pollhandle_destroy(fuse_pollhandle_t *ph)
{
free(ph);
}
static
void
do_poll(fuse_req_t req,
struct fuse_in_header *hdr_)
{
req->f->op.poll(req,hdr_);
}
static
void
do_fallocate(fuse_req_t req,
struct fuse_in_header *hdr_)
{
req->f->op.fallocate(req,hdr_);
}
static
void
do_init(fuse_req_t req,
struct fuse_in_header *hdr_)
{
struct fuse_init_out outarg = {0};
struct fuse_init_in *arg = (struct fuse_init_in *) &hdr_[1];
struct fuse_ll *f = req->f;
size_t bufsize = fuse_chan_bufsize(req->ch);
if(f->debug)
debug_fuse_init_in(arg);
f->conn.proto_major = arg->major;
f->conn.proto_minor = arg->minor;
f->conn.capable = 0;
f->conn.want = 0;
outarg.major = FUSE_KERNEL_VERSION;
outarg.minor = FUSE_KERNEL_MINOR_VERSION;
outarg.max_pages = FUSE_DEFAULT_MAX_PAGES_PER_REQ;
if(arg->major < 7)
{
fprintf(stderr, "fuse: unsupported protocol version: %u.%u\n",
arg->major, arg->minor);
fuse_reply_err(req, EPROTO);
return;
}
if(arg->major > 7)
{
/* Wait for a second INIT request with a 7.X version */
send_reply_ok(req, &outarg, sizeof(outarg));
return;
}
if(arg->minor >= 6)
{
if(arg->max_readahead < f->conn.max_readahead)
f->conn.max_readahead = arg->max_readahead;
if(arg->flags & FUSE_ASYNC_READ)
f->conn.capable |= FUSE_CAP_ASYNC_READ;
if(arg->flags & FUSE_POSIX_LOCKS)
f->conn.capable |= FUSE_CAP_POSIX_LOCKS;
if(arg->flags & FUSE_ATOMIC_O_TRUNC)
f->conn.capable |= FUSE_CAP_ATOMIC_O_TRUNC;
if(arg->flags & FUSE_EXPORT_SUPPORT)
f->conn.capable |= FUSE_CAP_EXPORT_SUPPORT;
if(arg->flags & FUSE_BIG_WRITES)
f->conn.capable |= FUSE_CAP_BIG_WRITES;
if(arg->flags & FUSE_DONT_MASK)
f->conn.capable |= FUSE_CAP_DONT_MASK;
if(arg->flags & FUSE_FLOCK_LOCKS)
f->conn.capable |= FUSE_CAP_FLOCK_LOCKS;
if(arg->flags & FUSE_POSIX_ACL)
f->conn.capable |= FUSE_CAP_POSIX_ACL;
if(arg->flags & FUSE_CACHE_SYMLINKS)
f->conn.capable |= FUSE_CAP_CACHE_SYMLINKS;
if(arg->flags & FUSE_ASYNC_DIO)
f->conn.capable |= FUSE_CAP_ASYNC_DIO;
if(arg->flags & FUSE_PARALLEL_DIROPS)
f->conn.capable |= FUSE_CAP_PARALLEL_DIROPS;
if(arg->flags & FUSE_MAX_PAGES)
f->conn.capable |= FUSE_CAP_MAX_PAGES;
if(arg->flags & FUSE_WRITEBACK_CACHE)
f->conn.capable |= FUSE_CAP_WRITEBACK_CACHE;
if(arg->flags & FUSE_DO_READDIRPLUS)
f->conn.capable |= FUSE_CAP_READDIR_PLUS;
if(arg->flags & FUSE_READDIRPLUS_AUTO)
f->conn.capable |= FUSE_CAP_READDIR_PLUS_AUTO;
if(arg->flags & FUSE_SETXATTR_EXT)
f->conn.capable |= FUSE_CAP_SETXATTR_EXT;
}
else
{
f->conn.want &= ~FUSE_CAP_ASYNC_READ;
f->conn.max_readahead = 0;
}
if(req->f->conn.proto_minor >= 18)
f->conn.capable |= FUSE_CAP_IOCTL_DIR;
if(f->op.getlk && f->op.setlk && !f->no_remote_posix_lock)
f->conn.want |= FUSE_CAP_POSIX_LOCKS;
if(f->op.flock && !f->no_remote_flock)
f->conn.want |= FUSE_CAP_FLOCK_LOCKS;
if(bufsize < FUSE_MIN_READ_BUFFER)
{
fprintf(stderr, "fuse: warning: buffer size too small: %zu\n",
bufsize);
bufsize = FUSE_MIN_READ_BUFFER;
}
bufsize -= pagesize;
if(bufsize < f->conn.max_write)
f->conn.max_write = bufsize;
f->got_init = 1;
if(f->op.init)
f->op.init(f->userdata, &f->conn);
if((arg->flags & FUSE_MAX_PAGES) && (f->conn.want & FUSE_CAP_MAX_PAGES))
{
outarg.flags |= FUSE_MAX_PAGES;
outarg.max_pages = f->conn.max_pages;
msgbuf_set_bufsize(outarg.max_pages + 1);
}
if(f->conn.want & FUSE_CAP_ASYNC_READ)
outarg.flags |= FUSE_ASYNC_READ;
if(f->conn.want & FUSE_CAP_POSIX_LOCKS)
outarg.flags |= FUSE_POSIX_LOCKS;
if(f->conn.want & FUSE_CAP_ATOMIC_O_TRUNC)
outarg.flags |= FUSE_ATOMIC_O_TRUNC;
if(f->conn.want & FUSE_CAP_EXPORT_SUPPORT)
outarg.flags |= FUSE_EXPORT_SUPPORT;
if(f->conn.want & FUSE_CAP_BIG_WRITES)
outarg.flags |= FUSE_BIG_WRITES;
if(f->conn.want & FUSE_CAP_DONT_MASK)
outarg.flags |= FUSE_DONT_MASK;
if(f->conn.want & FUSE_CAP_FLOCK_LOCKS)
outarg.flags |= FUSE_FLOCK_LOCKS;
if(f->conn.want & FUSE_CAP_POSIX_ACL)
outarg.flags |= FUSE_POSIX_ACL;
if(f->conn.want & FUSE_CAP_CACHE_SYMLINKS)
outarg.flags |= FUSE_CACHE_SYMLINKS;
if(f->conn.want & FUSE_CAP_ASYNC_DIO)
outarg.flags |= FUSE_ASYNC_DIO;
if(f->conn.want & FUSE_CAP_PARALLEL_DIROPS)
outarg.flags |= FUSE_PARALLEL_DIROPS;
if(f->conn.want & FUSE_CAP_WRITEBACK_CACHE)
outarg.flags |= FUSE_WRITEBACK_CACHE;
if(f->conn.want & FUSE_CAP_READDIR_PLUS)
outarg.flags |= FUSE_DO_READDIRPLUS;
if(f->conn.want & FUSE_CAP_READDIR_PLUS_AUTO)
outarg.flags |= FUSE_READDIRPLUS_AUTO;
if(f->conn.want & FUSE_CAP_SETXATTR_EXT)
outarg.flags |= FUSE_SETXATTR_EXT;
outarg.max_readahead = f->conn.max_readahead;
outarg.max_write = f->conn.max_write;
if(f->conn.proto_minor >= 13)
{
if(f->conn.max_background >= (1 << 16))
f->conn.max_background = (1 << 16) - 1;
if(f->conn.congestion_threshold > f->conn.max_background)
f->conn.congestion_threshold = f->conn.max_background;
if(!f->conn.congestion_threshold)
{
f->conn.congestion_threshold = f->conn.max_background * 3 / 4;
}
outarg.max_background = f->conn.max_background;
outarg.congestion_threshold = f->conn.congestion_threshold;
}
size_t outargsize;
if(arg->minor < 5)
outargsize = FUSE_COMPAT_INIT_OUT_SIZE;
else if(arg->minor < 23)
outargsize = FUSE_COMPAT_22_INIT_OUT_SIZE;
else
outargsize = sizeof(outarg);
if(f->debug)
debug_fuse_init_out(req->unique,&outarg,outargsize);
send_reply_ok(req, &outarg, outargsize);
}
static
void
do_destroy(fuse_req_t req,
struct fuse_in_header *hdr_)
{
struct fuse_ll *f = req->f;
f->got_destroy = 1;
f->op.destroy(f->userdata);
send_reply_ok(req,NULL,0);
}
static
void
list_del_nreq(struct fuse_notify_req *nreq)
{
struct fuse_notify_req *prev = nreq->prev;
struct fuse_notify_req *next = nreq->next;
prev->next = next;
next->prev = prev;
}
static
void
list_add_nreq(struct fuse_notify_req *nreq,
struct fuse_notify_req *next)
{
struct fuse_notify_req *prev = next->prev;
nreq->next = next;
nreq->prev = prev;
prev->next = nreq;
next->prev = nreq;
}
static
void
list_init_nreq(struct fuse_notify_req *nreq)
{
nreq->next = nreq;
nreq->prev = nreq;
}
static
void
do_notify_reply(fuse_req_t req,
struct fuse_in_header *hdr_)
{
struct fuse_ll *f = req->f;
struct fuse_notify_req *nreq;
struct fuse_notify_req *head;
pthread_mutex_lock(&f->lock);
head = &f->notify_list;
for(nreq = head->next; nreq != head; nreq = nreq->next)
{
if(nreq->unique == req->unique)
{
list_del_nreq(nreq);
break;
}
}
pthread_mutex_unlock(&f->lock);
if(nreq != head)
nreq->reply(nreq, req, hdr_->nodeid, &hdr_[1]);
}
static
void
do_copy_file_range(fuse_req_t req_,
struct fuse_in_header *hdr_)
{
req_->f->op.copy_file_range(req_,hdr_);
}
static
int
send_notify_iov(struct fuse_ll *f,
struct fuse_chan *ch,
int notify_code,
struct iovec *iov,
int count)
{
struct fuse_out_header out;
if(!f->got_init)
return -ENOTCONN;
out.unique = 0;
out.error = notify_code;
iov[0].iov_base = &out;
iov[0].iov_len = sizeof(struct fuse_out_header);
return fuse_send_msg(f, ch, iov, count);
}
int
fuse_lowlevel_notify_poll(fuse_pollhandle_t *ph)
{
if(ph != NULL)
{
struct fuse_notify_poll_wakeup_out outarg;
struct iovec iov[2];
outarg.kh = ph->kh;
iov[1].iov_base = &outarg;
iov[1].iov_len = sizeof(outarg);
return send_notify_iov(ph->f, ph->ch, FUSE_NOTIFY_POLL, iov, 2);
}
else
{
return 0;
}
}
int
fuse_lowlevel_notify_inval_inode(struct fuse_chan *ch,
uint64_t ino,
off_t off,
off_t len)
{
struct fuse_notify_inval_inode_out outarg;
struct fuse_ll *f;
struct iovec iov[2];
if(!ch)
return -EINVAL;
f = (struct fuse_ll*)fuse_session_data(fuse_chan_session(ch));
if(!f)
return -ENODEV;
outarg.ino = ino;
outarg.off = off;
outarg.len = len;
iov[1].iov_base = &outarg;
iov[1].iov_len = sizeof(outarg);
return send_notify_iov(f, ch, FUSE_NOTIFY_INVAL_INODE, iov, 2);
}
int
fuse_lowlevel_notify_inval_entry(struct fuse_chan *ch,
uint64_t parent,
const char *name,
size_t namelen)
{
struct fuse_notify_inval_entry_out outarg;
struct fuse_ll *f;
struct iovec iov[3];
if(!ch)
return -EINVAL;
f = (struct fuse_ll*)fuse_session_data(fuse_chan_session(ch));
if(!f)
return -ENODEV;
outarg.parent = parent;
outarg.namelen = namelen;
outarg.padding = 0;
iov[1].iov_base = &outarg;
iov[1].iov_len = sizeof(outarg);
iov[2].iov_base = (void *)name;
iov[2].iov_len = namelen + 1;
return send_notify_iov(f, ch, FUSE_NOTIFY_INVAL_ENTRY, iov, 3);
}
int
fuse_lowlevel_notify_delete(struct fuse_chan *ch,
uint64_t parent,
uint64_t child,
const char *name,
size_t namelen)
{
struct fuse_notify_delete_out outarg;
struct fuse_ll *f;
struct iovec iov[3];
if(!ch)
return -EINVAL;
f = (struct fuse_ll*)fuse_session_data(fuse_chan_session(ch));
if(!f)
return -ENODEV;
if(f->conn.proto_minor < 18)
return -ENOSYS;
outarg.parent = parent;
outarg.child = child;
outarg.namelen = namelen;
outarg.padding = 0;
iov[1].iov_base = &outarg;
iov[1].iov_len = sizeof(outarg);
iov[2].iov_base = (void *)name;
iov[2].iov_len = namelen + 1;
return send_notify_iov(f, ch, FUSE_NOTIFY_DELETE, iov, 3);
}
int
fuse_lowlevel_notify_store(struct fuse_chan *ch,
uint64_t ino,
off_t offset,
struct fuse_bufvec *bufv,
enum fuse_buf_copy_flags flags)
{
struct fuse_out_header out;
struct fuse_notify_store_out outarg;
struct fuse_ll *f;
struct iovec iov[3];
size_t size = fuse_buf_size(bufv);
int res;
if(!ch)
return -EINVAL;
f = (struct fuse_ll*)fuse_session_data(fuse_chan_session(ch));
if(!f)
return -ENODEV;
if(f->conn.proto_minor < 15)
return -ENOSYS;
out.unique = 0;
out.error = FUSE_NOTIFY_STORE;
outarg.nodeid = ino;
outarg.offset = offset;
outarg.size = size;
outarg.padding = 0;
iov[0].iov_base = &out;
iov[0].iov_len = sizeof(out);
iov[1].iov_base = &outarg;
iov[1].iov_len = sizeof(outarg);
res = fuse_send_data_iov(f, ch, iov, 2, bufv, flags);
if(res > 0)
res = -res;
return res;
}
struct fuse_retrieve_req
{
struct fuse_notify_req nreq;
void *cookie;
};
static
void
fuse_ll_retrieve_reply(struct fuse_notify_req *nreq,
fuse_req_t req,
uint64_t ino,
const void *inarg)
{
struct fuse_retrieve_req *rreq =
container_of(nreq, struct fuse_retrieve_req, nreq);
fuse_reply_none(req);
free(rreq);
}
int
fuse_lowlevel_notify_retrieve(struct fuse_chan *ch,
uint64_t ino,
size_t size,
off_t offset,
void *cookie)
{
struct fuse_notify_retrieve_out outarg;
struct fuse_ll *f;
struct iovec iov[2];
struct fuse_retrieve_req *rreq;
int err;
if(!ch)
return -EINVAL;
f = (struct fuse_ll*)fuse_session_data(fuse_chan_session(ch));
if(!f)
return -ENODEV;
if(f->conn.proto_minor < 15)
return -ENOSYS;
rreq = malloc(sizeof(*rreq));
if(rreq == NULL)
return -ENOMEM;
pthread_mutex_lock(&f->lock);
rreq->cookie = cookie;
rreq->nreq.unique = f->notify_ctr++;
rreq->nreq.reply = fuse_ll_retrieve_reply;
list_add_nreq(&rreq->nreq, &f->notify_list);
pthread_mutex_unlock(&f->lock);
outarg.notify_unique = rreq->nreq.unique;
outarg.nodeid = ino;
outarg.offset = offset;
outarg.size = size;
iov[1].iov_base = &outarg;
iov[1].iov_len = sizeof(outarg);
err = send_notify_iov(f, ch, FUSE_NOTIFY_RETRIEVE, iov, 2);
if(err)
{
pthread_mutex_lock(&f->lock);
list_del_nreq(&rreq->nreq);
pthread_mutex_unlock(&f->lock);
free(rreq);
}
return err;
}
void *
fuse_req_userdata(fuse_req_t req)
{
return req->f->userdata;
}
const
struct fuse_ctx *
fuse_req_ctx(fuse_req_t req)
{
return &req->ctx;
}
static struct {
void (*func)(fuse_req_t, struct fuse_in_header *);
const char *name;
} fuse_ll_ops[] =
{
[FUSE_LOOKUP] = { do_lookup, "LOOKUP" },
[FUSE_FORGET] = { do_forget, "FORGET" },
[FUSE_GETATTR] = { do_getattr, "GETATTR" },
[FUSE_SETATTR] = { do_setattr, "SETATTR" },
[FUSE_READLINK] = { do_readlink, "READLINK" },
[FUSE_SYMLINK] = { do_symlink, "SYMLINK" },
[FUSE_MKNOD] = { do_mknod, "MKNOD" },
[FUSE_MKDIR] = { do_mkdir, "MKDIR" },
[FUSE_UNLINK] = { do_unlink, "UNLINK" },
[FUSE_RMDIR] = { do_rmdir, "RMDIR" },
[FUSE_RENAME] = { do_rename, "RENAME" },
[FUSE_LINK] = { do_link, "LINK" },
[FUSE_OPEN] = { do_open, "OPEN" },
[FUSE_READ] = { do_read, "READ" },
[FUSE_WRITE] = { do_write, "WRITE" },
[FUSE_STATFS] = { do_statfs, "STATFS" },
[FUSE_RELEASE] = { do_release, "RELEASE" },
[FUSE_FSYNC] = { do_fsync, "FSYNC" },
[FUSE_SETXATTR] = { do_setxattr, "SETXATTR" },
[FUSE_GETXATTR] = { do_getxattr, "GETXATTR" },
[FUSE_LISTXATTR] = { do_listxattr, "LISTXATTR" },
[FUSE_REMOVEXATTR] = { do_removexattr, "REMOVEXATTR" },
[FUSE_FLUSH] = { do_flush, "FLUSH" },
[FUSE_INIT] = { do_init, "INIT" },
[FUSE_OPENDIR] = { do_opendir, "OPENDIR" },
[FUSE_READDIR] = { do_readdir, "READDIR" },
[FUSE_READDIRPLUS] = { do_readdir_plus, "READDIR_PLUS" },
[FUSE_RELEASEDIR] = { do_releasedir, "RELEASEDIR" },
[FUSE_FSYNCDIR] = { do_fsyncdir, "FSYNCDIR" },
[FUSE_GETLK] = { do_getlk, "GETLK" },
[FUSE_SETLK] = { do_setlk, "SETLK" },
[FUSE_SETLKW] = { do_setlkw, "SETLKW" },
[FUSE_ACCESS] = { do_access, "ACCESS" },
[FUSE_CREATE] = { do_create, "CREATE" },
[FUSE_INTERRUPT] = { do_interrupt, "INTERRUPT" },
[FUSE_BMAP] = { do_bmap, "BMAP" },
[FUSE_IOCTL] = { do_ioctl, "IOCTL" },
[FUSE_POLL] = { do_poll, "POLL" },
[FUSE_FALLOCATE] = { do_fallocate, "FALLOCATE" },
[FUSE_DESTROY] = { do_destroy, "DESTROY" },
[FUSE_NOTIFY_REPLY] = { do_notify_reply, "NOTIFY_REPLY" },
[FUSE_BATCH_FORGET] = { do_batch_forget, "BATCH_FORGET" },
[FUSE_COPY_FILE_RANGE] = { do_copy_file_range, "COPY_FILE_RANGE" },
};
#define FUSE_MAXOP (sizeof(fuse_ll_ops) / sizeof(fuse_ll_ops[0]))
enum {
KEY_HELP,
KEY_VERSION,
};
static const struct fuse_opt fuse_ll_opts[] =
{
{ "debug", offsetof(struct fuse_ll, debug), 1 },
{ "-d", offsetof(struct fuse_ll, debug), 1 },
{ "max_readahead=%u", offsetof(struct fuse_ll, conn.max_readahead), 0 },
{ "max_background=%u", offsetof(struct fuse_ll, conn.max_background), 0 },
{ "congestion_threshold=%u",
offsetof(struct fuse_ll, conn.congestion_threshold), 0 },
{ "no_remote_lock", offsetof(struct fuse_ll, no_remote_posix_lock), 1},
{ "no_remote_lock", offsetof(struct fuse_ll, no_remote_flock), 1},
{ "no_remote_flock", offsetof(struct fuse_ll, no_remote_flock), 1},
{ "no_remote_posix_lock", offsetof(struct fuse_ll, no_remote_posix_lock), 1},
FUSE_OPT_KEY("max_read=", FUSE_OPT_KEY_DISCARD),
FUSE_OPT_KEY("-h", KEY_HELP),
FUSE_OPT_KEY("--help", KEY_HELP),
FUSE_OPT_KEY("-V", KEY_VERSION),
FUSE_OPT_KEY("--version", KEY_VERSION),
FUSE_OPT_END
};
static
void
fuse_ll_version(void)
{
fprintf(stderr, "using FUSE kernel interface version %i.%i\n",
FUSE_KERNEL_VERSION, FUSE_KERNEL_MINOR_VERSION);
}
static
void
fuse_ll_help(void)
{
fprintf(stderr,
" -o max_readahead=N set maximum readahead\n"
" -o max_background=N set number of maximum background requests\n"
" -o congestion_threshold=N set kernel's congestion threshold\n"
" -o no_remote_lock disable remote file locking\n"
" -o no_remote_flock disable remote file locking (BSD)\n"
" -o no_remote_posix_lock disable remove file locking (POSIX)\n"
);
}
static
int
fuse_ll_opt_proc(void *data,
const char *arg,
int key,
struct fuse_args *outargs)
{
(void) data; (void) outargs;
switch (key)
{
case KEY_HELP:
fuse_ll_help();
break;
case KEY_VERSION:
fuse_ll_version();
break;
default:
fprintf(stderr, "fuse: unknown option `%s'\n", arg);
}
return -1;
}
int
fuse_lowlevel_is_lib_option(const char *opt)
{
return fuse_opt_match(fuse_ll_opts, opt);
}
static
void
fuse_ll_destroy(void *data)
{
struct fuse_ll *f = (struct fuse_ll *)data;
struct fuse_ll_pipe *llp;
if(f->got_init && !f->got_destroy)
{
if(f->op.destroy)
f->op.destroy(f->userdata);
}
llp = pthread_getspecific(f->pipe_key);
if(llp != NULL)
fuse_ll_pipe_free(llp);
pthread_key_delete(f->pipe_key);
pthread_mutex_destroy(&f->lock);
free(f);
lfmp_clear(&g_FMP_fuse_req);
}
static
void
fuse_ll_pipe_destructor(void *data)
{
struct fuse_ll_pipe *llp = data;
fuse_ll_pipe_free(llp);
}
static
void
fuse_send_errno(struct fuse_ll *f_,
struct fuse_chan *ch_,
const int errno_,
const uint64_t unique_id_)
{
struct fuse_out_header out = {0};
struct iovec iov = {0};
out.unique = unique_id_;
out.error = -errno_;
iov.iov_base = &out;
iov.iov_len = sizeof(struct fuse_out_header);
fuse_send_msg(f_,ch_,&iov,1);
}
static
void
fuse_send_enomem(struct fuse_ll *f_,
struct fuse_chan *ch_,
const uint64_t unique_id_)
{
fuse_send_errno(f_,ch_,ENOMEM,unique_id_);
}
static
int
fuse_ll_buf_receive_read(struct fuse_session *se_,
fuse_msgbuf_t *msgbuf_)
{
int rv;
rv = read(fuse_chan_fd(se_->ch),msgbuf_->mem,msgbuf_->size);
if(rv == -1)
return -errno;
if(rv < sizeof(struct fuse_in_header))
{
fprintf(stderr, "short read from fuse device\n");
return -EIO;
}
return rv;
}
static
void
fuse_ll_buf_process_read(struct fuse_session *se_,
const fuse_msgbuf_t *msgbuf_)
{
int err;
struct fuse_req *req;
struct fuse_in_header *in;
in = (struct fuse_in_header*)msgbuf_->mem;
req = fuse_ll_alloc_req(se_->f);
if(req == NULL)
return fuse_send_enomem(se_->f,se_->ch,in->unique);
req->unique = in->unique;
req->ctx.uid = in->uid;
req->ctx.gid = in->gid;
req->ctx.pid = in->pid;
req->ch = se_->ch;
err = ENOSYS;
if(in->opcode >= FUSE_MAXOP)
goto reply_err;
if(fuse_ll_ops[in->opcode].func == NULL)
goto reply_err;
fuse_ll_ops[in->opcode].func(req, in);
return;
reply_err:
fuse_reply_err(req, err);
return;
}
static
void
fuse_ll_buf_process_read_init(struct fuse_session *se_,
const fuse_msgbuf_t *msgbuf_)
{
int err;
struct fuse_req *req;
struct fuse_in_header *in;
in = (struct fuse_in_header*)msgbuf_->mem;
req = fuse_ll_alloc_req(se_->f);
if(req == NULL)
return fuse_send_enomem(se_->f,se_->ch,in->unique);
req->unique = in->unique;
req->ctx.uid = in->uid;
req->ctx.gid = in->gid;
req->ctx.pid = in->pid;
req->ch = se_->ch;
err = EIO;
if(in->opcode != FUSE_INIT)
goto reply_err;
if(fuse_ll_ops[in->opcode].func == NULL)
goto reply_err;
se_->process_buf = fuse_ll_buf_process_read;
fuse_ll_ops[in->opcode].func(req, in);
return;
reply_err:
fuse_reply_err(req, err);
return;
}
/*
* always call fuse_lowlevel_new_common() internally, to work around a
* misfeature in the FreeBSD runtime linker, which links the old
* version of a symbol to internal references.
*/
struct fuse_session *
fuse_lowlevel_new_common(struct fuse_args *args,
const struct fuse_lowlevel_ops *op,
size_t op_size,
void *userdata)
{
int err;
struct fuse_ll *f;
struct fuse_session *se;
if(sizeof(struct fuse_lowlevel_ops) < op_size)
{
fprintf(stderr, "fuse: warning: library too old, some operations may not work\n");
op_size = sizeof(struct fuse_lowlevel_ops);
}
f = (struct fuse_ll *) calloc(1, sizeof(struct fuse_ll));
if(f == NULL)
{
fprintf(stderr, "fuse: failed to allocate fuse object\n");
goto out;
}
f->conn.max_write = UINT_MAX;
f->conn.max_readahead = UINT_MAX;
list_init_nreq(&f->notify_list);
f->notify_ctr = 1;
fuse_mutex_init(&f->lock);
err = pthread_key_create(&f->pipe_key, fuse_ll_pipe_destructor);
if(err)
{
fprintf(stderr, "fuse: failed to create thread specific key: %s\n",
strerror(err));
goto out_free;
}
if(fuse_opt_parse(args, f, fuse_ll_opts, fuse_ll_opt_proc) == -1)
goto out_key_destroy;
memcpy(&f->op, op, op_size);
f->owner = getuid();
f->userdata = userdata;
se = fuse_session_new(f,
fuse_ll_buf_receive_read,
fuse_ll_buf_process_read_init,
fuse_ll_destroy);
if(!se)
goto out_key_destroy;
return se;
out_key_destroy:
pthread_key_delete(f->pipe_key);
out_free:
pthread_mutex_destroy(&f->lock);
free(f);
out:
return NULL;
}
struct fuse_session*
fuse_lowlevel_new(struct fuse_args *args,
const struct fuse_lowlevel_ops *op,
size_t op_size,
void *userdata)
{
return fuse_lowlevel_new_common(args, op, op_size, userdata);
}