This commit is contained in:
Miklos Szeredi
2005-08-14 23:00:27 +00:00
parent 45c5db5475
commit a148242fb8
17 changed files with 768 additions and 447 deletions
+5
View File
@@ -1,3 +1,8 @@
2005-08-15 Miklos Szeredi <miklos@szeredi.hu>
* lib: cleaned up (or messed up, depending on your POV) the low
level library API. Hopefully this is close to the final form.
2005-08-05 Miklos Szeredi <miklos@szeredi.hu>
* fusermount: don't allow empty mountpoint argument, which defeats
+12 -6
View File
@@ -134,7 +134,7 @@ static void hello_ll_read(fuse_req_t req, fuse_ino_t ino, size_t size,
reply_buf_limited(req, hello_str, strlen(hello_str), off, size);
}
static struct fuse_ll_operations hello_ll_oper = {
static struct fuse_lowlevel_ops hello_ll_oper = {
.lookup = hello_ll_lookup,
.getattr = hello_ll_getattr,
.readdir = hello_ll_readdir,
@@ -145,7 +145,6 @@ static struct fuse_ll_operations hello_ll_oper = {
int main(int argc, char *argv[])
{
const char *mountpoint;
struct fuse_ll *f;
int err = -1;
int fd;
@@ -156,10 +155,17 @@ int main(int argc, char *argv[])
mountpoint = argv[1];
fd = fuse_mount(mountpoint, NULL);
if (fd != -1) {
f = fuse_ll_new(fd, "debug", &hello_ll_oper, sizeof(hello_ll_oper), NULL);
if (f != NULL) {
err = fuse_ll_loop(f);
fuse_ll_destroy(f);
struct fuse_session *se;
se = fuse_lowlevel_new("debug", &hello_ll_oper, sizeof(hello_ll_oper),
NULL);
if (se != NULL) {
struct fuse_chan *ch = fuse_kern_chan_new(fd);
if (ch != NULL) {
fuse_session_add_chan(se, ch);
err = fuse_session_loop(se);
}
fuse_session_destroy(se);
}
close(fd);
}
+8 -1
View File
@@ -1,6 +1,13 @@
## Process this file with automake to produce Makefile.in
fuseincludedir=$(includedir)/fuse
fuseinclude_HEADERS = fuse.h fuse_compat.h fuse_common.h fuse_lowlevel.h
fuseinclude_HEADERS = \
fuse.h \
fuse_compat.h \
fuse_common.h \
fuse_lowlevel.h
include_HEADERS = old/fuse.h
noinst_HEADERS = fuse_kernel.h
+8 -2
View File
@@ -34,6 +34,12 @@ extern "C" {
/** Handle for a FUSE filesystem */
struct fuse;
/** Structure containing a raw command */
struct fuse_cmd;
/** The lowlevel FUSE session */
struct fuse_session;
/** Function to add an entry in a readdir() operation
*
* @param buf the buffer passed to the readdir() operation
@@ -504,8 +510,8 @@ int fuse_exited(struct fuse *f);
/** Set function which can be used to get the current context */
void fuse_set_getcontext_func(struct fuse_context *(*func)(void));
/** Returns the lowlevel FUSE object */
struct fuse_ll *fuse_get_lowlevel(struct fuse *f);
/** Returns the lowlevel FUSE session */
struct fuse_session *fuse_get_session(struct fuse *f);
/* ----------------------------------------------------------- *
* Compatibility stuff *
-3
View File
@@ -53,7 +53,4 @@ struct fuse_file_info {
unsigned int keep_cache : 1;
};
/** Structure containing a raw command */
struct fuse_cmd;
#endif /* _FUSE_COMMON_H_ */
+63 -16
View File
@@ -19,6 +19,7 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/statfs.h>
#include <sys/uio.h>
#ifdef __cplusplus
extern "C" {
@@ -29,7 +30,9 @@ extern "C" {
typedef unsigned long fuse_ino_t;
typedef struct fuse_req *fuse_req_t;
struct fuse_ll;
struct fuse_session;
struct fuse_chan;
struct fuse_entry_param {
fuse_ino_t ino;
@@ -69,8 +72,8 @@ struct fuse_ctx {
/* ------------------------------------------ */
struct fuse_ll_operations {
void* (*init) (void *);
struct fuse_lowlevel_ops {
void (*init) (void *);
void (*destroy)(void *);
void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name);
@@ -174,29 +177,73 @@ const struct fuse_ctx *fuse_req_ctx(fuse_req_t req);
/* ------------------------------------------ */
typedef void (*fuse_ll_processor_t)(struct fuse_ll *, struct fuse_cmd *, void *);
int fuse_lowlevel_is_lib_option(const char *opt);
struct fuse_ll *fuse_ll_new(int fd, const char *opts,
const struct fuse_ll_operations *op,
size_t op_size, void *userdata);
struct fuse_session *fuse_lowlevel_new(const char *opts,
const struct fuse_lowlevel_ops *op,
size_t op_size, void *userdata);
void fuse_ll_destroy(struct fuse_ll *f);
struct fuse_chan *fuse_kern_chan_new(int fd);
int fuse_ll_is_lib_option(const char *opt);
/* ------------------------------------------ */
int fuse_ll_loop(struct fuse_ll *f);
struct fuse_session_ops {
void (*process) (void *data, const char *buf, size_t len,
struct fuse_chan *ch);
void (*destroy) (void *data);
};
void fuse_ll_exit(struct fuse_ll *f);
struct fuse_session *fuse_session_new(struct fuse_session_ops *op, void *data);
int fuse_ll_exited(struct fuse_ll* f);
void fuse_session_add_chan(struct fuse_session *se, struct fuse_chan *ch);
struct fuse_cmd *fuse_ll_read_cmd(struct fuse_ll *f);
struct fuse_chan *fuse_session_next_chan(struct fuse_session *se,
struct fuse_chan *ch);
void fuse_ll_process_cmd(struct fuse_ll *f, struct fuse_cmd *cmd);
void fuse_session_process(struct fuse_session *se, const char *buf, size_t len,
struct fuse_chan *ch);
int fuse_ll_loop_mt(struct fuse_ll *f);
void fuse_session_destroy(struct fuse_session *se);
int fuse_ll_loop_mt_proc(struct fuse_ll *f, fuse_ll_processor_t proc, void *data);
void fuse_session_exit(struct fuse_session *se);
void fuse_session_reset(struct fuse_session *se);
int fuse_session_exited(struct fuse_session *se);
int fuse_session_loop(struct fuse_session *se);
int fuse_session_loop_mt(struct fuse_session *se);
/* ------------------------------------------ */
struct fuse_chan_ops {
int (*receive)(struct fuse_chan *ch, char *buf, size_t size);
int (*send)(struct fuse_chan *ch, const struct iovec iov[],
size_t count);
void (*destroy)(struct fuse_chan *ch);
};
struct fuse_chan *fuse_chan_new(struct fuse_chan_ops *op, int fd,
size_t bufsize, void *data);
int fuse_chan_fd(struct fuse_chan *ch);
size_t fuse_chan_bufsize(struct fuse_chan *ch);
void *fuse_chan_data(struct fuse_chan *ch);
struct fuse_session *fuse_chan_session(struct fuse_chan *ch);
int fuse_chan_receive(struct fuse_chan *ch, char *buf, size_t size);
int fuse_chan_send(struct fuse_chan *ch, const struct iovec iov[],
size_t count);
void fuse_chan_destroy(struct fuse_chan *ch);
/* ------------------------------------------ */
+6 -4
View File
@@ -4,12 +4,14 @@ lib_LTLIBRARIES = libfuse.la
libfuse_la_SOURCES = \
fuse.c \
fuse_mt.c \
fuse_kern_chan.c \
fuse_loop.c \
fuse_loop_mt.c \
fuse_lowlevel.c \
fuse_lowlevel_mt.c \
fuse_mt.c \
fuse_session.c \
helper.c \
mount.c \
fuse_lowlevel_i.h
mount.c
libfuse_la_LDFLAGS = -lpthread -version-number 2:4:0 \
-Wl,--version-script,fuse_versionscript
+67 -21
View File
@@ -63,7 +63,7 @@
#define FUSE_MAX_PATH 4096
struct fuse {
struct fuse_ll *fll;
struct fuse_session *se;
int flags;
struct fuse_operations op;
int compat;
@@ -110,6 +110,11 @@ struct fuse_dirhandle {
fuse_ino_t nodeid;
};
struct fuse_cmd {
char *buf;
size_t buflen;
};
static struct fuse_context *(*fuse_getcontext)(void) = NULL;
#ifndef USE_UCLIBC
@@ -561,14 +566,12 @@ static void reply_entry(fuse_req_t req, const struct fuse_entry_param *e,
reply_err(req, err);
}
static void *fuse_data_init(void *data)
static void fuse_data_init(void *data)
{
struct fuse *f = (struct fuse *) data;
if (f->op.init)
f->user_data = f->op.init();
return f;
}
static void fuse_data_destroy(void *data)
@@ -1599,7 +1602,7 @@ static void fuse_removexattr(fuse_req_t req, fuse_ino_t ino, const char *name)
reply_err(req, err);
}
static struct fuse_ll_operations fuse_path_ops = {
static struct fuse_lowlevel_ops fuse_path_ops = {
.init = fuse_data_init,
.destroy = fuse_data_destroy,
.lookup = fuse_lookup,
@@ -1632,25 +1635,64 @@ static struct fuse_ll_operations fuse_path_ops = {
.removexattr = fuse_removexattr,
};
static void free_cmd(struct fuse_cmd *cmd)
{
free(cmd->buf);
free(cmd);
}
void fuse_process_cmd(struct fuse *f, struct fuse_cmd *cmd)
{
fuse_ll_process_cmd(f->fll, cmd);
struct fuse_chan *ch = fuse_session_next_chan(f->se, NULL);
fuse_session_process(f->se, cmd->buf, cmd->buflen, ch);
}
int fuse_exited(struct fuse *f)
{
return fuse_ll_exited(f->fll);
return fuse_session_exited(f->se);
}
struct fuse_session *fuse_get_session(struct fuse *f)
{
return f->se;
}
static struct fuse_cmd *fuse_alloc_cmd(size_t bufsize)
{
struct fuse_cmd *cmd = (struct fuse_cmd *) malloc(sizeof(*cmd));
if (cmd == NULL) {
fprintf(stderr, "fuse: failed to allocate cmd\n");
return NULL;
}
cmd->buf = (char *) malloc(bufsize);
if (cmd->buf == NULL) {
fprintf(stderr, "fuse: failed to allocate read buffer\n");
free(cmd);
return NULL;
}
return cmd;
}
struct fuse_cmd *fuse_read_cmd(struct fuse *f)
{
return fuse_ll_read_cmd(f->fll);
struct fuse_chan *ch = fuse_session_next_chan(f->se, NULL);
size_t bufsize = fuse_chan_bufsize(ch);
struct fuse_cmd *cmd = fuse_alloc_cmd(bufsize);
if (cmd != NULL) {
int res = fuse_chan_receive(ch, cmd->buf, bufsize);
if (res <= 0) {
free_cmd(cmd);
return NULL;
}
cmd->buflen = res;
}
return cmd;
}
int fuse_loop(struct fuse *f)
{
if (f)
return fuse_ll_loop(f->fll);
return fuse_session_loop(f->se);
else
return -1;
}
@@ -1664,7 +1706,7 @@ int fuse_invalidate(struct fuse *f, const char *path)
void fuse_exit(struct fuse *f)
{
fuse_ll_exit(f->fll);
fuse_session_exit(f->se);
}
struct fuse_context *fuse_get_context()
@@ -1681,11 +1723,6 @@ void fuse_set_getcontext_func(struct fuse_context *(*func)(void))
fuse_getcontext = func;
}
struct fuse_ll *fuse_get_lowlevel(struct fuse *f)
{
return f->fll;
}
static int begins_with(const char *s, const char *beg)
{
if (strncmp(s, beg, strlen(beg)) == 0)
@@ -1696,7 +1733,7 @@ static int begins_with(const char *s, const char *beg)
int fuse_is_lib_option(const char *opt)
{
if (fuse_ll_is_lib_option(opt) ||
if (fuse_lowlevel_is_lib_option(opt) ||
strcmp(opt, "debug") == 0 ||
strcmp(opt, "hard_remove") == 0 ||
strcmp(opt, "use_ino") == 0 ||
@@ -1728,7 +1765,7 @@ static int parse_lib_opts(struct fuse *f, const char *opts, char **llopts)
}
while((opt = strsep(&s, ","))) {
if (fuse_ll_is_lib_option(opt)) {
if (fuse_lowlevel_is_lib_option(opt)) {
size_t optlen = strlen(opt);
if (strcmp(opt, "debug") == 0)
f->flags |= FUSE_DEBUG;
@@ -1772,6 +1809,7 @@ struct fuse *fuse_new_common(int fd, const char *opts,
const struct fuse_operations *op,
size_t op_size, int compat)
{
struct fuse_chan *ch;
struct fuse *f;
struct node *root;
char *llopts = NULL;
@@ -1793,10 +1831,16 @@ struct fuse *fuse_new_common(int fd, const char *opts,
if (parse_lib_opts(f, opts, &llopts) == -1)
goto out_free;
f->fll = fuse_ll_new(fd, llopts, &fuse_path_ops, sizeof(fuse_path_ops), f);
f->se = fuse_lowlevel_new(llopts, &fuse_path_ops, sizeof(fuse_path_ops), f);
free(llopts);
if (f->fll == NULL)
if (f->se == NULL)
goto out_free;
ch = fuse_kern_chan_new(fd);
if (ch == NULL)
goto out_free_session;
fuse_session_add_chan(f->se, ch);
f->ctr = 0;
f->generation = 0;
@@ -1806,7 +1850,7 @@ struct fuse *fuse_new_common(int fd, const char *opts,
calloc(1, sizeof(struct node *) * f->name_table_size);
if (f->name_table == NULL) {
fprintf(stderr, "fuse: memory allocation failed\n");
goto out_free;
goto out_free_session;
}
f->id_table_size = 14057;
@@ -1848,6 +1892,8 @@ struct fuse *fuse_new_common(int fd, const char *opts,
free(f->id_table);
out_free_name_table:
free(f->name_table);
out_free_session:
fuse_session_destroy(f->se);
out_free:
free(f);
out:
@@ -1905,7 +1951,7 @@ void fuse_destroy(struct fuse *f)
free(f->id_table);
free(f->name_table);
pthread_mutex_destroy(&f->lock);
fuse_ll_destroy(f->fll);
fuse_session_destroy(f->se);
free(f);
}
+73
View File
@@ -0,0 +1,73 @@
/*
FUSE: Filesystem in Userspace
Copyright (C) 2001-2005 Miklos Szeredi <miklos@szeredi.hu>
This program can be distributed under the terms of the GNU LGPL.
See the file COPYING.LIB
*/
#include "fuse_lowlevel.h"
#include "fuse_kernel.h"
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <assert.h>
static int fuse_kern_chan_receive(struct fuse_chan *ch, char *buf, size_t size)
{
ssize_t res = read(fuse_chan_fd(ch), buf, size);
struct fuse_session *se = fuse_chan_session(ch);
assert(se != NULL);
if (fuse_session_exited(se))
return 0;
if (res == -1) {
/* EINTR means, the read() was interrupted, ENOENT means the
operation was interrupted */
if (errno == EINTR || errno == ENOENT)
return 0;
/* ENODEV means we got unmounted, so we silenty return failure */
if (errno != ENODEV)
perror("fuse: reading device");
return -1;
}
if ((size_t) res < sizeof(struct fuse_in_header)) {
fprintf(stderr, "short read on fuse device\n");
return -1;
}
return res;
}
static int fuse_kern_chan_send(struct fuse_chan *ch, const struct iovec iov[],
size_t count)
{
ssize_t res = writev(fuse_chan_fd(ch), iov, count);
if (res == -1) {
struct fuse_session *se = fuse_chan_session(ch);
assert(se != NULL);
/* ENOENT means the operation was interrupted */
if (!fuse_session_exited(se) && errno != ENOENT)
perror("fuse: writing device");
return -errno;
}
return 0;
}
static void fuse_kern_chan_destroy(struct fuse_chan *ch)
{
close(fuse_chan_fd(ch));
}
struct fuse_chan *fuse_kern_chan_new(int fd)
{
struct fuse_chan_ops op = {
.receive = fuse_kern_chan_receive,
.send = fuse_kern_chan_send,
.destroy = fuse_kern_chan_destroy,
};
return fuse_chan_new(&op, fd, FUSE_MAX_IN, NULL);
}
+38
View File
@@ -0,0 +1,38 @@
/*
FUSE: Filesystem in Userspace
Copyright (C) 2001-2005 Miklos Szeredi <miklos@szeredi.hu>
This program can be distributed under the terms of the GNU LGPL.
See the file COPYING.LIB
*/
#include "fuse_lowlevel.h"
#include <stdio.h>
#include <stdlib.h>
int fuse_session_loop(struct fuse_session *se)
{
int res = 0;
struct fuse_chan *ch = fuse_session_next_chan(se, NULL);
size_t bufsize = fuse_chan_bufsize(ch);
char *buf = (char *) malloc(bufsize);
if (!buf) {
fprintf(stderr, "fuse: failed to allocate read buffer\n");
return -1;
}
while (!fuse_session_exited(se)) {
res = fuse_chan_receive(ch, buf, bufsize);
if (!res)
continue;
if (res == -1)
break;
fuse_session_process(se, buf, res, ch);
res = 0;
}
free(buf);
fuse_session_reset(se);
return res;
}
+161
View File
@@ -0,0 +1,161 @@
/*
FUSE: Filesystem in Userspace
Copyright (C) 2001-2005 Miklos Szeredi <miklos@szeredi.hu>
This program can be distributed under the terms of the GNU LGPL.
See the file COPYING.LIB.
*/
#include "fuse_lowlevel.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <sys/time.h>
#define FUSE_MAX_WORKERS 10
struct fuse_worker {
pthread_mutex_t lock;
int numworker;
int numavail;
struct fuse_session *se;
struct fuse_chan *ch;
struct fuse_chan *prevch;
pthread_t threads[FUSE_MAX_WORKERS];
int error;
};
#ifndef USE_UCLIBC
#define mutex_init(mut) pthread_mutex_init(mut, NULL)
#else
static void mutex_init(pthread_mutex_t mut)
{
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ADAPTIVE_NP);
pthread_mutex_init(mut, &attr);
pthread_mutexattr_destroy(&attr);
}
#endif
static int fuse_loop_mt_send(struct fuse_chan *ch, const struct iovec iov[],
size_t count)
{
struct fuse_worker *w = (struct fuse_worker *) fuse_chan_data(ch);
pthread_mutex_lock(&w->lock);
w->numavail ++;
pthread_mutex_unlock(&w->lock);
return fuse_chan_send(w->prevch, iov, count);
}
static int start_thread(struct fuse_worker *w, pthread_t *thread_id);
static void *do_work(void *data)
{
struct fuse_worker *w = (struct fuse_worker *) data;
int is_mainthread = (w->numworker == 1);
size_t bufsize = fuse_chan_bufsize(w->prevch);
char *buf = (char *) malloc(bufsize);
if (!buf) {
fprintf(stderr, "fuse: failed to allocate read buffer\n");
fuse_session_exit(w->se);
w->error = -1;
return NULL;
}
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
while (!fuse_session_exited(w->se)) {
int res = fuse_chan_receive(w->prevch, buf, bufsize);
if (!res)
continue;
if (res == -1) {
fuse_session_exit(w->se);
w->error = -1;
}
pthread_mutex_lock(&w->lock);
w->numavail--;
if (w->numavail == 0 && w->numworker < FUSE_MAX_WORKERS) {
if (w->numworker < FUSE_MAX_WORKERS) {
/* FIXME: threads should be stored in a list instead
of an array */
int start_res;
pthread_t *thread_id = &w->threads[w->numworker];
w->numavail ++;
w->numworker ++;
start_res = start_thread(w, thread_id);
if (start_res == -1)
w->numavail --;
}
}
pthread_mutex_unlock(&w->lock);
fuse_session_process(w->se, buf, res, w->ch);
}
/* Wait for cancellation */
if (!is_mainthread)
pause();
return NULL;
}
static int start_thread(struct fuse_worker *w, pthread_t *thread_id)
{
int res = pthread_create(thread_id, NULL, do_work, w);
if (res != 0) {
fprintf(stderr, "fuse: error creating thread: %s\n", strerror(res));
return -1;
}
pthread_detach(*thread_id);
return 0;
}
int fuse_session_loop_mt(struct fuse_session *se)
{
int i;
int err;
struct fuse_worker *w;
struct fuse_chan_ops cop = {
.send = fuse_loop_mt_send,
};
w = (struct fuse_worker *) malloc(sizeof(struct fuse_worker));
if (w == NULL) {
fprintf(stderr, "fuse: failed to allocate worker structure\n");
return -1;
}
memset(w, 0, sizeof(struct fuse_worker));
w->se = se;
w->prevch = fuse_session_next_chan(se, NULL);
w->ch = fuse_chan_new(&cop, -1, 0, w);
if (w->ch == NULL) {
free(w);
return -1;
}
w->error = 0;
w->numworker = 1;
w->numavail = 1;
mutex_init(&w->lock);
do_work(w);
pthread_mutex_lock(&w->lock);
for (i = 1; i < w->numworker; i++)
pthread_cancel(w->threads[i]);
pthread_mutex_unlock(&w->lock);
pthread_mutex_destroy(&w->lock);
err = w->error;
fuse_chan_destroy(w->ch);
free(w);
fuse_session_reset(se);
return err;
}
+92 -222
View File
@@ -7,7 +7,7 @@
*/
#include <config.h>
#include "fuse_lowlevel_i.h"
#include "fuse_lowlevel.h"
#include "fuse_kernel.h"
#include <stdio.h>
@@ -16,37 +16,28 @@
#include <unistd.h>
#include <limits.h>
#include <errno.h>
#include <assert.h>
#include <stdint.h>
#include <sys/param.h>
#include <sys/uio.h>
#define PARAM(inarg) (((char *)(inarg)) + sizeof(*(inarg)))
struct fuse_cmd {
char *buf;
size_t buflen;
struct fuse_ll {
unsigned int debug : 1;
unsigned int allow_root : 1;
struct fuse_lowlevel_ops op;
int got_init;
void *userdata;
int major;
int minor;
uid_t owner;
};
struct fuse_req {
struct fuse_ll *f;
uint64_t unique;
struct fuse_ctx ctx;
struct fuse_chan *ch;
};
#ifndef USE_UCLIBC
#define mutex_init(mut) pthread_mutex_init(mut, NULL)
#else
static void mutex_init(pthread_mutex_t *mut)
{
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ADAPTIVE_NP);
pthread_mutex_init(mut, &attr);
pthread_mutexattr_destroy(&attr);
}
#endif
static const char *opname(enum fuse_opcode opcode)
{
switch (opcode) {
@@ -86,20 +77,6 @@ static const char *opname(enum fuse_opcode opcode)
}
}
static inline void fuse_dec_avail(struct fuse_ll *f)
{
pthread_mutex_lock(&f->worker_lock);
f->numavail --;
pthread_mutex_unlock(&f->worker_lock);
}
static inline void fuse_inc_avail(struct fuse_ll *f)
{
pthread_mutex_lock(&f->worker_lock);
f->numavail ++;
pthread_mutex_unlock(&f->worker_lock);
}
static void convert_stat(const struct stat *stbuf, struct fuse_attr *attr)
{
attr->ino = stbuf->st_ino;
@@ -166,48 +143,25 @@ static size_t iov_length(const struct iovec *iov, size_t count)
return ret;
}
static int send_reply_raw(struct fuse_ll *f, const struct iovec iov[],
size_t count)
static void free_req(fuse_req_t req)
{
int res;
unsigned outsize = iov_length(iov, count);
struct fuse_out_header *out = (struct fuse_out_header *) iov[0].iov_base;
out->len = outsize;
if (f->debug) {
printf(" unique: %llu, error: %i (%s), outsize: %i\n",
out->unique, out->error, strerror(-out->error), outsize);
fflush(stdout);
}
/* This needs to be done before the reply, otherwise the scheduler
could play tricks with us, and only let the counter be
increased long after the operation is done */
fuse_inc_avail(f);
res = writev(f->fd, iov, count);
if (res == -1) {
/* ENOENT means the operation was interrupted */
if (!fuse_ll_exited(f) && errno != ENOENT)
perror("fuse: writing device");
return -errno;
}
return 0;
free(req);
}
static int send_reply(struct fuse_ll *f, uint64_t unique, int error,
const void *arg, size_t argsize)
static int send_reply(fuse_req_t req, int error, const void *arg,
size_t argsize)
{
struct fuse_out_header out;
struct iovec iov[2];
size_t count;
int res;
if (error <= -1000 || error > 0) {
fprintf(stderr, "fuse: bad error value: %i\n", error);
error = -ERANGE;
}
out.unique = unique;
out.unique = req->unique;
out.error = error;
count = 1;
iov[0].iov_base = &out;
@@ -217,7 +171,17 @@ static int send_reply(struct fuse_ll *f, uint64_t unique, int error,
iov[1].iov_base = (void *) arg;
iov[1].iov_len = argsize;
}
return send_reply_raw(f, iov, count);
out.len = iov_length(iov, count);
if (req->f->debug) {
printf(" unique: %llu, error: %i (%s), outsize: %i\n",
out.unique, out.error, strerror(-out.error), out.len);
fflush(stdout);
}
res = fuse_chan_send(req->ch, iov, count);
free_req(req);
return res;
}
size_t fuse_dirent_size(size_t namelen)
@@ -257,23 +221,14 @@ static void convert_statfs(const struct statfs *stbuf,
kstatfs->namelen = stbuf->f_namelen;
}
static void free_req(fuse_req_t req)
static int send_reply_ok(fuse_req_t req, const void *arg, size_t argsize)
{
free(req);
}
static int send_reply_req(fuse_req_t req, const void *arg, size_t argsize)
{
int res = send_reply(req->f, req->unique, 0, arg, argsize);
free_req(req);
return res;
return send_reply(req, 0, arg, argsize);
}
int fuse_reply_err(fuse_req_t req, int err)
{
int res = send_reply(req->f, req->unique, -err, NULL, 0);
free_req(req);
return res;
return send_reply(req, -err, NULL, 0);
}
int fuse_reply_none(fuse_req_t req)
@@ -316,7 +271,7 @@ int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
arg.attr_valid_nsec = calc_timeout_nsec(e->attr_timeout);
convert_stat(&e->attr, &arg.attr);
return send_reply_req(req, &arg, sizeof(arg));
return send_reply_ok(req, &arg, sizeof(arg));
}
int fuse_reply_attr(fuse_req_t req, const struct stat *attr,
@@ -329,12 +284,12 @@ int fuse_reply_attr(fuse_req_t req, const struct stat *attr,
arg.attr_valid_nsec = calc_timeout_nsec(attr_timeout);
convert_stat(attr, &arg.attr);
return send_reply_req(req, &arg, sizeof(arg));
return send_reply_ok(req, &arg, sizeof(arg));
}
int fuse_reply_readlink(fuse_req_t req, const char *linkname)
{
return send_reply_req(req, linkname, strlen(linkname));
return send_reply_ok(req, linkname, strlen(linkname));
}
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *f)
@@ -348,7 +303,7 @@ int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *f)
if (f->keep_cache)
arg.open_flags |= FOPEN_KEEP_CACHE;
return send_reply_req(req, &arg, sizeof(arg));
return send_reply_ok(req, &arg, sizeof(arg));
}
int fuse_reply_write(fuse_req_t req, size_t count)
@@ -358,12 +313,12 @@ int fuse_reply_write(fuse_req_t req, size_t count)
memset(&arg, 0, sizeof(arg));
arg.size = count;
return send_reply_req(req, &arg, sizeof(arg));
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_req(req, buf, size);
return send_reply_ok(req, buf, size);
}
int fuse_reply_statfs(fuse_req_t req, const struct statfs *stbuf)
@@ -373,7 +328,7 @@ int fuse_reply_statfs(fuse_req_t req, const struct statfs *stbuf)
memset(&arg, 0, sizeof(arg));
convert_statfs(stbuf, &arg.st);
return send_reply_req(req, &arg, sizeof(arg));
return send_reply_ok(req, &arg, sizeof(arg));
}
int fuse_reply_xattr(fuse_req_t req, size_t count)
@@ -383,7 +338,7 @@ int fuse_reply_xattr(fuse_req_t req, size_t count)
memset(&arg, 0, sizeof(arg));
arg.size = count;
return send_reply_req(req, &arg, sizeof(arg));
return send_reply_ok(req, &arg, sizeof(arg));
}
int fuse_reply_getlk(fuse_req_t req, const struct fuse_lock_param *lk)
@@ -393,7 +348,7 @@ int fuse_reply_getlk(fuse_req_t req, const struct fuse_lock_param *lk)
memset(&arg, 0, sizeof(arg));
convert_lock_param(lk, &arg.lk);
return send_reply_req(req, &arg, sizeof(arg));
return send_reply_ok(req, &arg, sizeof(arg));
}
static void do_lookup(fuse_req_t req, fuse_ino_t nodeid, char *name)
@@ -727,10 +682,10 @@ static void do_setlk(fuse_req_t req, fuse_ino_t nodeid, int issleep,
fuse_reply_err(req, ENOSYS);
}
static void do_init(struct fuse_ll *f, uint64_t unique,
struct fuse_init_in_out *arg)
static void do_init(fuse_req_t req, struct fuse_init_in_out *arg)
{
struct fuse_init_in_out outarg;
struct fuse_ll *f = req->f;
if (f->debug) {
printf("INIT: %u.%u\n", arg->major, arg->minor);
@@ -738,7 +693,7 @@ static void do_init(struct fuse_ll *f, uint64_t unique,
}
f->got_init = 1;
if (f->op.init)
f->userdata = f->op.init(f->userdata);
f->op.init(f->userdata);
f->major = FUSE_KERNEL_VERSION;
f->minor = FUSE_KERNEL_MINOR_VERSION;
@@ -752,7 +707,7 @@ static void do_init(struct fuse_ll *f, uint64_t unique,
fflush(stdout);
}
send_reply(f, unique, 0, &outarg, sizeof(outarg));
send_reply_ok(req, &outarg, sizeof(outarg));
}
void *fuse_req_userdata(fuse_req_t req)
@@ -765,48 +720,25 @@ const struct fuse_ctx *fuse_req_ctx(fuse_req_t req)
return &req->ctx;
}
static void free_cmd(struct fuse_cmd *cmd)
static void fuse_ll_process(void *data, const char *buf, size_t len,
struct fuse_chan *ch)
{
free(cmd->buf);
free(cmd);
}
void fuse_ll_process_cmd(struct fuse_ll *f, struct fuse_cmd *cmd)
{
struct fuse_in_header *in = (struct fuse_in_header *) cmd->buf;
void *inarg = cmd->buf + sizeof(struct fuse_in_header);
struct fuse_ll *f = (struct fuse_ll *) data;
struct fuse_in_header *in = (struct fuse_in_header *) buf;
const void *inarg = buf + sizeof(struct fuse_in_header);
struct fuse_req *req;
fuse_dec_avail(f);
if (f->debug) {
printf("unique: %llu, opcode: %s (%i), nodeid: %lu, insize: %i\n",
in->unique, opname((enum fuse_opcode) in->opcode), in->opcode,
(unsigned long) in->nodeid, cmd->buflen);
(unsigned long) in->nodeid, len);
fflush(stdout);
}
if (!f->got_init) {
if (in->opcode != FUSE_INIT)
send_reply(f, in->unique, -EPROTO, NULL, 0);
else
do_init(f, in->unique, (struct fuse_init_in_out *) inarg);
goto out;
}
if (f->allow_root && in->uid != f->owner && in->uid != 0 &&
in->opcode != FUSE_INIT && in->opcode != FUSE_READ &&
in->opcode != FUSE_WRITE && in->opcode != FUSE_FSYNC &&
in->opcode != FUSE_RELEASE && in->opcode != FUSE_READDIR &&
in->opcode != FUSE_FSYNCDIR && in->opcode != FUSE_RELEASEDIR) {
send_reply(f, in->unique, -EACCES, NULL, 0);
goto out;
}
req = (struct fuse_req *) malloc(sizeof(struct fuse_req));
if (req == NULL) {
fprintf(stderr, "fuse: failed to allocate request\n");
goto out;
return;
}
req->f = f;
@@ -814,8 +746,21 @@ void fuse_ll_process_cmd(struct fuse_ll *f, struct fuse_cmd *cmd)
req->ctx.uid = in->uid;
req->ctx.gid = in->gid;
req->ctx.pid = in->pid;
req->ch = ch;
switch (in->opcode) {
if (!f->got_init && in->opcode != FUSE_INIT)
fuse_reply_err(req, EPROTO);
else if (f->allow_root && in->uid != f->owner && in->uid != 0 &&
in->opcode != FUSE_INIT && in->opcode != FUSE_READ &&
in->opcode != FUSE_WRITE && in->opcode != FUSE_FSYNC &&
in->opcode != FUSE_RELEASE && in->opcode != FUSE_READDIR &&
in->opcode != FUSE_FSYNCDIR && in->opcode != FUSE_RELEASEDIR) {
fuse_reply_err(req, EACCES);
} else switch (in->opcode) {
case FUSE_INIT:
do_init(req, (struct fuse_init_in_out *) inarg);
break;
case FUSE_LOOKUP:
do_lookup(req, in->nodeid, (char *) inarg);
break;
@@ -944,92 +889,9 @@ void fuse_ll_process_cmd(struct fuse_ll *f, struct fuse_cmd *cmd)
default:
fuse_reply_err(req, ENOSYS);
}
out:
free_cmd(cmd);
}
void fuse_ll_exit(struct fuse_ll *f)
{
f->exited = 1;
}
int fuse_ll_exited(struct fuse_ll* f)
{
return f->exited;
}
struct fuse_cmd *fuse_ll_read_cmd(struct fuse_ll *f)
{
ssize_t res;
struct fuse_cmd *cmd;
struct fuse_in_header *in;
void *inarg;
cmd = (struct fuse_cmd *) malloc(sizeof(struct fuse_cmd));
if (cmd == NULL) {
fprintf(stderr, "fuse: failed to allocate cmd in read\n");
return NULL;
}
cmd->buf = (char *) malloc(FUSE_MAX_IN);
if (cmd->buf == NULL) {
fprintf(stderr, "fuse: failed to allocate read buffer\n");
free(cmd);
return NULL;
}
in = (struct fuse_in_header *) cmd->buf;
inarg = cmd->buf + sizeof(struct fuse_in_header);
res = read(f->fd, cmd->buf, FUSE_MAX_IN);
if (res == -1) {
free_cmd(cmd);
if (fuse_ll_exited(f) || errno == EINTR || errno == ENOENT)
return NULL;
/* ENODEV means we got unmounted, so we silenty return failure */
if (errno != ENODEV) {
/* BAD... This will happen again */
perror("fuse: reading device");
}
fuse_ll_exit(f);
return NULL;
}
if ((size_t) res < sizeof(struct fuse_in_header)) {
free_cmd(cmd);
/* Cannot happen */
fprintf(stderr, "short read on fuse device\n");
fuse_ll_exit(f);
return NULL;
}
cmd->buflen = res;
return cmd;
}
int fuse_ll_loop(struct fuse_ll *f)
{
if (f == NULL)
return -1;
while (1) {
struct fuse_cmd *cmd;
if (fuse_ll_exited(f))
break;
cmd = fuse_ll_read_cmd(f);
if (cmd == NULL)
continue;
fuse_ll_process_cmd(f, cmd);
}
f->exited = 0;
return 0;
}
int fuse_ll_is_lib_option(const char *opt)
int fuse_lowlevel_is_lib_option(const char *opt)
{
if (strcmp(opt, "debug") == 0 ||
strcmp(opt, "allow_root") == 0)
@@ -1063,15 +925,31 @@ static int parse_ll_opts(struct fuse_ll *f, const char *opts)
return 0;
}
struct fuse_ll *fuse_ll_new(int fd, const char *opts,
const struct fuse_ll_operations *op,
size_t op_size, void *userdata)
static void fuse_ll_destroy(void *data)
{
struct fuse_ll *f = (struct fuse_ll *) data;
if (f->op.destroy)
f->op.destroy(f->userdata);
free(f);
}
struct fuse_session *fuse_lowlevel_new(const char *opts,
const struct fuse_lowlevel_ops *op,
size_t op_size, void *userdata)
{
struct fuse_ll *f;
struct fuse_session *se;
struct fuse_session_ops sop = {
.process = fuse_ll_process,
.destroy = fuse_ll_destroy,
};
if (sizeof(struct fuse_ll_operations) < op_size) {
if (sizeof(struct fuse_lowlevel_ops) < op_size) {
fprintf(stderr, "fuse: warning: library too old, some operations may not not work\n");
op_size = sizeof(struct fuse_ll_operations);
op_size = sizeof(struct fuse_lowlevel_ops);
}
f = (struct fuse_ll *) calloc(1, sizeof(struct fuse_ll));
@@ -1083,14 +961,15 @@ struct fuse_ll *fuse_ll_new(int fd, const char *opts,
if (parse_ll_opts(f, opts) == -1)
goto out_free;
f->fd = fd;
memcpy(&f->op, op, op_size);
f->exited = 0;
f->owner = getuid();
f->userdata = userdata;
mutex_init(&f->worker_lock);
return f;
se = fuse_session_new(&sop, f);
if (!se)
goto out_free;
return se;
out_free:
free(f);
@@ -1098,12 +977,3 @@ struct fuse_ll *fuse_ll_new(int fd, const char *opts,
return NULL;
}
void fuse_ll_destroy(struct fuse_ll *f)
{
if (f->op.destroy)
f->op.destroy(f->userdata);
pthread_mutex_destroy(&f->worker_lock);
free(f);
}
-26
View File
@@ -1,26 +0,0 @@
/*
FUSE: Filesystem in Userspace
Copyright (C) 2001-2005 Miklos Szeredi <miklos@szeredi.hu>
This program can be distributed under the terms of the GNU LGPL.
See the file COPYING.LIB.
*/
#include "fuse_lowlevel.h"
#include <pthread.h>
struct fuse_ll {
unsigned int debug : 1;
unsigned int allow_root : 1;
int fd;
struct fuse_ll_operations op;
volatile int exited;
int got_init;
void *userdata;
int major;
int minor;
uid_t owner;
pthread_mutex_t worker_lock;
int numworker;
int numavail;
};
-126
View File
@@ -1,126 +0,0 @@
/*
FUSE: Filesystem in Userspace
Copyright (C) 2001-2005 Miklos Szeredi <miklos@szeredi.hu>
This program can be distributed under the terms of the GNU LGPL.
See the file COPYING.LIB.
*/
#include "fuse_lowlevel_i.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <sys/time.h>
#define FUSE_MAX_WORKERS 10
struct fuse_worker {
struct fuse_ll *f;
pthread_t threads[FUSE_MAX_WORKERS];
void *data;
fuse_ll_processor_t proc;
};
static int start_thread(struct fuse_worker *w, pthread_t *thread_id);
static void *do_work(void *data)
{
struct fuse_worker *w = (struct fuse_worker *) data;
struct fuse_ll *f = w->f;
int is_mainthread = (f->numworker == 1);
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
while (1) {
struct fuse_cmd *cmd;
if (fuse_ll_exited(f))
break;
cmd = fuse_ll_read_cmd(w->f);
if (cmd == NULL)
continue;
if (f->numavail == 0 && f->numworker < FUSE_MAX_WORKERS) {
pthread_mutex_lock(&f->worker_lock);
if (f->numworker < FUSE_MAX_WORKERS) {
/* FIXME: threads should be stored in a list instead
of an array */
int res;
pthread_t *thread_id = &w->threads[f->numworker];
f->numavail ++;
f->numworker ++;
pthread_mutex_unlock(&f->worker_lock);
res = start_thread(w, thread_id);
if (res == -1) {
pthread_mutex_lock(&f->worker_lock);
f->numavail --;
pthread_mutex_unlock(&f->worker_lock);
}
} else
pthread_mutex_unlock(&f->worker_lock);
}
w->proc(w->f, cmd, w->data);
}
/* Wait for cancellation */
if (!is_mainthread)
pause();
return NULL;
}
static int start_thread(struct fuse_worker *w, pthread_t *thread_id)
{
int res = pthread_create(thread_id, NULL, do_work, w);
if (res != 0) {
fprintf(stderr, "fuse: error creating thread: %s\n", strerror(res));
return -1;
}
pthread_detach(*thread_id);
return 0;
}
int fuse_ll_loop_mt_proc(struct fuse_ll *f, fuse_ll_processor_t proc, void *data)
{
struct fuse_worker *w;
int i;
w = (struct fuse_worker *) malloc(sizeof(struct fuse_worker));
if (w == NULL) {
fprintf(stderr, "fuse: failed to allocate worker structure\n");
return -1;
}
memset(w, 0, sizeof(struct fuse_worker));
w->f = f;
w->data = data;
w->proc = proc;
f->numworker = 1;
do_work(w);
pthread_mutex_lock(&f->worker_lock);
for (i = 1; i < f->numworker; i++)
pthread_cancel(w->threads[i]);
pthread_mutex_unlock(&f->worker_lock);
free(w);
f->exited = 0;
return 0;
}
int fuse_ll_loop_mt(struct fuse_ll *f)
{
if (f)
return fuse_ll_loop_mt_proc(f, (fuse_ll_processor_t) fuse_ll_process_cmd, NULL);
else
return -1;
}
+51 -6
View File
@@ -13,7 +13,7 @@
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <assert.h>
static pthread_key_t context_key;
static pthread_mutex_t context_lock = PTHREAD_MUTEX_INITIALIZER;
@@ -75,28 +75,73 @@ struct procdata {
void *data;
};
static void mt_generic_proc(struct fuse_ll *f, struct fuse_cmd *cmd, void *data)
static void mt_session_proc(void *data, const char *buf, size_t len,
struct fuse_chan *ch)
{
struct procdata *pd = (struct procdata *) data;
(void) f;
struct fuse_cmd *cmd = *(struct fuse_cmd **) buf;
(void) len;
(void) ch;
pd->proc(pd->f, cmd, pd->data);
}
static int mt_chan_receive(struct fuse_chan *ch, char *buf, size_t size)
{
struct fuse_cmd *cmd;
struct procdata *pd = (struct procdata *) fuse_chan_data(ch);
assert(size >= sizeof(cmd));
cmd = fuse_read_cmd(pd->f);
if (cmd == NULL)
return -1;
*(struct fuse_cmd **) buf = cmd;
return 0;
}
int fuse_loop_mt_proc(struct fuse *f, fuse_processor_t proc, void *data)
{
int res;
struct procdata pd;
struct fuse_session *prevse = fuse_get_session(f);
struct fuse_session *se;
struct fuse_chan *prevch = fuse_session_next_chan(prevse, NULL);
struct fuse_chan *ch;
struct fuse_session_ops sop = {
.process = mt_session_proc,
};
struct fuse_chan_ops cop = {
.receive = mt_chan_receive,
};
pd.f = f;
pd.proc = proc;
pd.data = data;
if (mt_create_context_key() != 0)
se = fuse_session_new(&sop, &pd);
if (se == NULL)
return -1;
res = fuse_ll_loop_mt_proc(fuse_get_lowlevel(f), mt_generic_proc, &pd);
ch = fuse_chan_new(&cop, fuse_chan_fd(prevch), sizeof(struct fuse_cmd *),
&pd);
if (ch == NULL) {
fuse_session_destroy(se);
return -1;
}
fuse_session_add_chan(se, ch);
if (mt_create_context_key() != 0) {
fuse_session_destroy(se);
return -1;
}
res = fuse_session_loop_mt(se);
mt_delete_context_key();
fuse_session_destroy(se);
return res;
}
@@ -107,7 +152,7 @@ int fuse_loop_mt(struct fuse *f)
if (mt_create_context_key() != 0)
return -1;
res = fuse_ll_loop_mt(fuse_get_lowlevel(f));
res = fuse_session_loop_mt(fuse_get_session(f));
mt_delete_context_key();
return res;
+154
View File
@@ -0,0 +1,154 @@
/*
FUSE: Filesystem in Userspace
Copyright (C) 2001-2005 Miklos Szeredi <miklos@szeredi.hu>
This program can be distributed under the terms of the GNU LGPL.
See the file COPYING.LIB
*/
#include "fuse_lowlevel.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
struct fuse_session {
struct fuse_session_ops op;
void *data;
volatile int exited;
struct fuse_chan *ch;
};
struct fuse_chan {
struct fuse_chan_ops op;
struct fuse_session *se;
int fd;
size_t bufsize;
void *data;
};
struct fuse_session *fuse_session_new(struct fuse_session_ops *op, void *data)
{
struct fuse_session *se = (struct fuse_session *) malloc(sizeof(*se));
if (se == NULL) {
fprintf(stderr, "fuse: failed to allocate session\n");
return NULL;
}
memset(se, 0, sizeof(*se));
se->op = *op;
se->data = data;
return se;
}
void fuse_session_add_chan(struct fuse_session *se, struct fuse_chan *ch)
{
assert(se->ch == NULL);
assert(ch->se == NULL);
se->ch = ch;
ch->se = se;
}
struct fuse_chan *fuse_session_next_chan(struct fuse_session *se,
struct fuse_chan *ch)
{
assert(ch == NULL || ch == se->ch);
if (ch == NULL)
return se->ch;
else
return NULL;
}
void fuse_session_process(struct fuse_session *se, const char *buf, size_t len,
struct fuse_chan *ch)
{
se->op.process(se->data, buf, len, ch);
}
void fuse_session_destroy(struct fuse_session *se)
{
if (se->op.destroy)
se->op.destroy(se->data);
if (se->ch != NULL)
fuse_chan_destroy(se->ch);
free(se);
}
void fuse_session_exit(struct fuse_session *se)
{
se->exited = 1;
}
void fuse_session_reset(struct fuse_session *se)
{
se->exited = 0;
}
int fuse_session_exited(struct fuse_session *se)
{
return se->exited;
}
struct fuse_chan *fuse_chan_new(struct fuse_chan_ops *op, int fd,
size_t bufsize, void *data)
{
struct fuse_chan *ch = (struct fuse_chan *) malloc(sizeof(*ch));
if (ch == NULL) {
fprintf(stderr, "fuse: failed to allocate channel\n");
return NULL;
}
memset(ch, 0, sizeof(*ch));
ch->op = *op;
ch->fd = fd;
ch->bufsize = bufsize;
ch->data = data;
return ch;
}
int fuse_chan_fd(struct fuse_chan *ch)
{
return ch->fd;
}
size_t fuse_chan_bufsize(struct fuse_chan *ch)
{
return ch->bufsize;
}
void *fuse_chan_data(struct fuse_chan *ch)
{
return ch->data;
}
struct fuse_session *fuse_chan_session(struct fuse_chan *ch)
{
return ch->se;
}
int fuse_chan_receive(struct fuse_chan *ch, char *buf, size_t size)
{
return ch->op.receive(ch, buf, size);
}
int fuse_chan_send(struct fuse_chan *ch, const struct iovec iov[], size_t count)
{
return ch->op.send(ch, iov, count);
}
void fuse_chan_destroy(struct fuse_chan *ch)
{
if (ch->op.destroy)
ch->op.destroy(ch);
free(ch);
}
+30 -14
View File
@@ -1,24 +1,28 @@
FUSE_2.2 {
global:
fuse_add_dirent;
fuse_chan_bufsize;
fuse_chan_data;
fuse_chan_destroy;
fuse_chan_fd;
fuse_chan_new;
fuse_chan_receive;
fuse_chan_send;
fuse_chan_session;
fuse_destroy;
fuse_dirent_size;
fuse_exit;
fuse_exited;
fuse_get_context;
fuse_get_session;
fuse_invalidate;
fuse_is_lib_option;
fuse_ll_new;
fuse_ll_destroy;
fuse_ll_is_lib_option;
fuse_ll_loop;
fuse_ll_loop_mt;
fuse_ll_exited;
fuse_ll_read_cmd;
fuse_ll_process_cmd;
fuse_kern_chan_new;
fuse_loop;
fuse_loop_mt;
fuse_loop_mt_proc;
fuse_lowlevel_is_lib_option;
fuse_lowlevel_new;
fuse_main;
fuse_main_compat1;
fuse_main_compat2;
@@ -30,17 +34,29 @@ FUSE_2.2 {
fuse_new_compat2;
fuse_process_cmd;
fuse_read_cmd;
fuse_reply_err;
fuse_reply_none;
fuse_reply_entry;
fuse_reply_attr;
fuse_reply_readlink;
fuse_reply_open;
fuse_reply_write;
fuse_reply_buf;
fuse_reply_entry;
fuse_reply_err;
fuse_reply_getlk;
fuse_reply_none;
fuse_reply_open;
fuse_reply_readlink;
fuse_reply_statfs;
fuse_reply_write;
fuse_reply_xattr;
fuse_req_ctx;
fuse_req_userdata;
fuse_session_add_chan;
fuse_session_destroy;
fuse_session_exit;
fuse_session_exited;
fuse_session_loop;
fuse_session_loop_mt;
fuse_session_new;
fuse_session_next_chan;
fuse_session_process;
fuse_session_reset;
fuse_set_getcontext_func;
fuse_setup;
fuse_setup_compat2;