cleanup
diff --git a/ChangeLog b/ChangeLog
index 4bc79dc..e41d1a9 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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
diff --git a/example/hello_ll.c b/example/hello_ll.c
index 3a6ea09..f5a2baf 100644
--- a/example/hello_ll.c
+++ b/example/hello_ll.c
@@ -134,7 +134,7 @@
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 @@
int main(int argc, char *argv[])
{
const char *mountpoint;
- struct fuse_ll *f;
int err = -1;
int fd;
@@ -156,10 +155,17 @@
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);
}
diff --git a/include/Makefile.am b/include/Makefile.am
index ee8a83c..8c33be5 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -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
diff --git a/include/fuse.h b/include/fuse.h
index ca96084..ca3797f 100644
--- a/include/fuse.h
+++ b/include/fuse.h
@@ -34,6 +34,12 @@
/** 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 @@
/** 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 *
diff --git a/include/fuse_common.h b/include/fuse_common.h
index 648d6e4..2af3ce9 100644
--- a/include/fuse_common.h
+++ b/include/fuse_common.h
@@ -53,7 +53,4 @@
unsigned int keep_cache : 1;
};
-/** Structure containing a raw command */
-struct fuse_cmd;
-
#endif /* _FUSE_COMMON_H_ */
diff --git a/include/fuse_lowlevel.h b/include/fuse_lowlevel.h
index 8b18959..4c1af83 100644
--- a/include/fuse_lowlevel.h
+++ b/include/fuse_lowlevel.h
@@ -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 @@
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_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 @@
/* ------------------------------------------ */
-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);
/* ------------------------------------------ */
diff --git a/lib/Makefile.am b/lib/Makefile.am
index b99f9e2..b92a0ad 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -4,12 +4,14 @@
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
diff --git a/lib/fuse.c b/lib/fuse.c
index 68eeee0..7613d38 100644
--- a/lib/fuse.c
+++ b/lib/fuse.c
@@ -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 @@
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 @@
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 @@
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 @@
.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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
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 @@
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);
}
diff --git a/lib/fuse_kern_chan.c b/lib/fuse_kern_chan.c
new file mode 100644
index 0000000..577d2e5
--- /dev/null
+++ b/lib/fuse_kern_chan.c
@@ -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);
+}
diff --git a/lib/fuse_loop.c b/lib/fuse_loop.c
new file mode 100644
index 0000000..f9a05d8
--- /dev/null
+++ b/lib/fuse_loop.c
@@ -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;
+}
diff --git a/lib/fuse_loop_mt.c b/lib/fuse_loop_mt.c
new file mode 100644
index 0000000..ad386a2
--- /dev/null
+++ b/lib/fuse_loop_mt.c
@@ -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;
+}
diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c
index fce1d6d..bcd48c5 100644
--- a/lib/fuse_lowlevel.c
+++ b/lib/fuse_lowlevel.c
@@ -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 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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);
-}
-
diff --git a/lib/fuse_lowlevel_i.h b/lib/fuse_lowlevel_i.h
deleted file mode 100644
index eb612fc..0000000
--- a/lib/fuse_lowlevel_i.h
+++ /dev/null
@@ -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;
-};
diff --git a/lib/fuse_lowlevel_mt.c b/lib/fuse_lowlevel_mt.c
deleted file mode 100644
index ee3a0ac..0000000
--- a/lib/fuse_lowlevel_mt.c
+++ /dev/null
@@ -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;
-}
-
diff --git a/lib/fuse_mt.c b/lib/fuse_mt.c
index 8359994..c6a6a03 100644
--- a/lib/fuse_mt.c
+++ b/lib/fuse_mt.c
@@ -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 @@
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 @@
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;
diff --git a/lib/fuse_session.c b/lib/fuse_session.c
new file mode 100644
index 0000000..a467b2f
--- /dev/null
+++ b/lib/fuse_session.c
@@ -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);
+}
diff --git a/lib/fuse_versionscript b/lib/fuse_versionscript
index 283adae..d1b6983 100644
--- a/lib/fuse_versionscript
+++ b/lib/fuse_versionscript
@@ -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_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;