From: Miklos S. <mi...@sz...> - 2009-04-28 15:52:29
|
Here's the patch for libfuse that contains the updated CUSE_INIT message. Thanks, Miklos --- From: Tejun Heo <tj...@ke...> Subject: [PATCH 5/6] implement CUSE - Character device in Userspace - support Date: Tue, 14 Apr 2009 11:09:55 +0900 Implement libfuse side of CUSE support. CUSE uses subset of FUSE operations as dir operations don't make sense for CUSE where one instance implements single character device. CUSE support comes with its own cuse_lowevel_ops and related initialization and helper functions. Except for initialization, it usage is basically identical to FUSE. This patch also adds example/cusexmp.c which can create a character device with name and device number specified on command line. The created device itself is pretty boring. It's a bit bucket supporting read, write and access via ioctl. Signed-off-by: Tejun Heo <tj...@ke...> --- example/Makefile.am | 2 example/cusexmp.c | 294 ++++++++++++++++++++++++++++++++++++++ example/fioc.h | 19 ++ example/fioclient.c | 98 +++++++++--- include/Makefile.am | 3 include/cuse_lowlevel.h | 87 +++++++++++ include/fuse_kernel.h | 31 ++++ lib/Makefile.am | 1 lib/cuse_lowlevel.c | 371 ++++++++++++++++++++++++++++++++++++++++++++++++ lib/fuse_i.h | 6 lib/fuse_lowlevel.c | 26 ++- lib/fuse_versionscript | 4 12 files changed, 910 insertions(+), 32 deletions(-) Index: fuse/example/Makefile.am =================================================================== --- fuse.orig/example/Makefile.am 2009-04-28 14:53:28.000000000 +0200 +++ fuse/example/Makefile.am 2009-04-28 14:53:34.000000000 +0200 @@ -3,7 +3,7 @@ AM_CPPFLAGS = -I$(top_srcdir)/include -D_FILE_OFFSET_BITS=64 -D_REENTRANT noinst_HEADERS = fioc.h noinst_PROGRAMS = fusexmp fusexmp_fh null hello hello_ll fioc fioclient \ - fsel fselclient + fsel fselclient cusexmp LDADD = ../lib/libfuse.la @libfuse_libs@ fusexmp_fh_LDADD = ../lib/libfuse.la ../lib/libulockmgr.la @libfuse_libs@ Index: fuse/example/cusexmp.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ fuse/example/cusexmp.c 2009-04-28 14:53:34.000000000 +0200 @@ -0,0 +1,294 @@ +/* + CUSE example: Character device in Userspace + Copyright (C) 2008-2009 SUSE Linux Products GmbH + Copyright (C) 2008-2009 Tejun Heo <tj...@ke...> + + This program can be distributed under the terms of the GNU GPL. + See the file COPYING. + + gcc -Wall `pkg-config fuse --cflags --libs` cusexmp.c -o cusexmp +*/ + +#define FUSE_USE_VERSION 29 + +#include <cuse_lowlevel.h> +#include <fuse_opt.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> + +#include "fioc.h" + +static void *cusexmp_buf; +static size_t cusexmp_size; + +static const char *usage = +"usage: cusexmp [options]\n" +"\n" +"options:\n" +" --help|-h print this help message\n" +" --maj=MAJ|-M MAJ device major number\n" +" --min=MIN|-m MIN device minor number\n" +" --name=NAME|-n NAME device name (mandatory)\n" +"\n"; + +static int cusexmp_resize(size_t new_size) +{ + void *new_buf; + + if (new_size == cusexmp_size) + return 0; + + new_buf = realloc(cusexmp_buf, new_size); + if (!new_buf && new_size) + return -ENOMEM; + + if (new_size > cusexmp_size) + memset(new_buf + cusexmp_size, 0, new_size - cusexmp_size); + + cusexmp_buf = new_buf; + cusexmp_size = new_size; + + return 0; +} + +static int cusexmp_expand(size_t new_size) +{ + if (new_size > cusexmp_size) + return cusexmp_resize(new_size); + return 0; +} + +static void cusexmp_open(fuse_req_t req, struct fuse_file_info *fi) +{ + fuse_reply_open(req, fi); +} + +static void cusexmp_read(fuse_req_t req, size_t size, off_t off, + struct fuse_file_info *fi) +{ + (void)fi; + + if (off >= cusexmp_size) + off = cusexmp_size; + if (size > cusexmp_size - off) + size = cusexmp_size - off; + + fuse_reply_buf(req, cusexmp_buf + off, size); +} + +static void cusexmp_write(fuse_req_t req, const char *buf, size_t size, + off_t off, struct fuse_file_info *fi) +{ + (void)fi; + + if (cusexmp_expand(off + size)) { + fuse_reply_err(req, ENOMEM); + return; + } + + memcpy(cusexmp_buf + off, buf, size); + fuse_reply_write(req, size); +} + +static void fioc_do_rw(fuse_req_t req, void *addr, const void *in_buf, + size_t in_bufsz, size_t out_bufsz, int is_read) +{ + const struct fioc_rw_arg *arg; + struct iovec in_iov[2], out_iov[3], iov[3]; + size_t cur_size; + + /* read in arg */ + in_iov[0].iov_base = addr; + in_iov[0].iov_len = sizeof(*arg); + if (!in_bufsz) { + fuse_reply_ioctl_retry(req, in_iov, 1, NULL, 0); + return; + } + arg = in_buf; + in_buf += sizeof(*arg); + in_bufsz -= sizeof(*arg); + + /* prepare size outputs */ + out_iov[0].iov_base = + addr + (unsigned long)&(((struct fioc_rw_arg *)0)->prev_size); + out_iov[0].iov_len = sizeof(arg->prev_size); + + out_iov[1].iov_base = + addr + (unsigned long)&(((struct fioc_rw_arg *)0)->new_size); + out_iov[1].iov_len = sizeof(arg->new_size); + + /* prepare client buf */ + if (is_read) { + out_iov[2].iov_base = arg->buf; + out_iov[2].iov_len = arg->size; + if (!out_bufsz) { + fuse_reply_ioctl_retry(req, in_iov, 1, out_iov, 3); + return; + } + } else { + in_iov[1].iov_base = arg->buf; + in_iov[1].iov_len = arg->size; + if (arg->size && !in_bufsz) { + fuse_reply_ioctl_retry(req, in_iov, 2, out_iov, 2); + return; + } + } + + /* we're all set */ + cur_size = cusexmp_size; + iov[0].iov_base = &cur_size; + iov[0].iov_len = sizeof(cur_size); + + iov[1].iov_base = &cusexmp_size; + iov[1].iov_len = sizeof(cusexmp_size); + + if (is_read) { + size_t off = arg->offset; + size_t size = arg->size; + + if (off >= cusexmp_size) + off = cusexmp_size; + if (size > cusexmp_size - off) + size = cusexmp_size - off; + + iov[2].iov_base = cusexmp_buf + off; + iov[2].iov_len = size; + fuse_reply_ioctl_iov(req, size, iov, 3); + } else { + if (cusexmp_expand(arg->offset + in_bufsz)) { + fuse_reply_err(req, ENOMEM); + return; + } + + memcpy(cusexmp_buf + arg->offset, in_buf, in_bufsz); + fuse_reply_ioctl_iov(req, in_bufsz, iov, 2); + } +} + +static void cusexmp_ioctl(fuse_req_t req, int cmd, void *arg, + struct fuse_file_info *fi, unsigned flags, + const void *in_buf, size_t in_bufsz, size_t out_bufsz) +{ + int is_read = 0; + + (void)fi; + + if (flags & FUSE_IOCTL_COMPAT) { + fuse_reply_err(req, ENOSYS); + return; + } + + switch (cmd) { + case FIOC_GET_SIZE: + if (!out_bufsz) { + struct iovec iov = { arg, sizeof(size_t) }; + + fuse_reply_ioctl_retry(req, NULL, 0, &iov, 1); + } else + fuse_reply_ioctl(req, 0, &cusexmp_size, + sizeof(cusexmp_size)); + break; + + case FIOC_SET_SIZE: + if (!in_bufsz) { + struct iovec iov = { arg, sizeof(size_t) }; + + fuse_reply_ioctl_retry(req, &iov, 1, NULL, 0); + } else { + cusexmp_resize(*(size_t *)in_buf); + fuse_reply_ioctl(req, 0, NULL, 0); + } + break; + + case FIOC_READ: + is_read = 1; + case FIOC_WRITE: + fioc_do_rw(req, arg, in_buf, in_bufsz, out_bufsz, is_read); + break; + + default: + fuse_reply_err(req, EINVAL); + } +} + +struct cusexmp_param { + unsigned major; + unsigned minor; + char *dev_name; + int is_help; +}; + +#define CUSEXMP_OPT(t, p) { t, offsetof(struct cusexmp_param, p), 1 } + +static const struct fuse_opt cusexmp_opts[] = { + CUSEXMP_OPT("-M %u", major), + CUSEXMP_OPT("--maj=%u", major), + CUSEXMP_OPT("-m %u", minor), + CUSEXMP_OPT("--min=%u", minor), + CUSEXMP_OPT("-n %s", dev_name), + CUSEXMP_OPT("--name=%s", dev_name), + FUSE_OPT_KEY("-h", 0), + FUSE_OPT_KEY("--help", 0), + FUSE_OPT_END +}; + +static int cusexmp_process_arg(void *data, const char *arg, int key, + struct fuse_args *outargs) +{ + struct cusexmp_param *param = data; + + (void)outargs; + (void)arg; + + switch (key) { + case 0: + param->is_help = 1; + fprintf(stderr, usage); + return fuse_opt_add_arg(outargs, "-ho"); + default: + return 1; + } +} + +static const struct cuse_lowlevel_ops cusexmp_clop = { + .open = cusexmp_open, + .read = cusexmp_read, + .write = cusexmp_write, + .ioctl = cusexmp_ioctl, +}; + +int main(int argc, char **argv) +{ + struct fuse_args args = FUSE_ARGS_INIT(argc, argv); + struct cusexmp_param param = { 0, 0, NULL, 0 }; + char dev_name[128] = "DEVNAME="; + const char *dev_info_argv[] = { dev_name }; + struct cuse_info ci; + + if (fuse_opt_parse(&args, ¶m, cusexmp_opts, cusexmp_process_arg)) { + printf("failed to parse option\n"); + return 1; + } + + if (!param.is_help) { + if (!param.dev_name) { + fprintf(stderr, "Error: device name missing\n"); + return 1; + } + strncat(dev_name, param.dev_name, sizeof(dev_name) - 9); + } + + memset(&ci, 0, sizeof(ci)); + ci.dev_major = param.major; + ci.dev_minor = param.minor; + ci.dev_info_argc = 1; + ci.dev_info_argv = dev_info_argv; + ci.flags = CUSE_UNRESTRICTED_IOCTL; + + return cuse_lowlevel_main(args.argc, args.argv, &ci, &cusexmp_clop, + NULL); +} Index: fuse/example/fioc.h =================================================================== --- fuse.orig/example/fioc.h 2009-04-28 14:53:28.000000000 +0200 +++ fuse/example/fioc.h 2009-04-28 14:53:34.000000000 +0200 @@ -12,6 +12,21 @@ #include <sys/ioctl.h> enum { - FIOC_GET_SIZE = _IOR('E', 0, size_t), - FIOC_SET_SIZE = _IOW('E', 1, size_t), + FIOC_GET_SIZE = _IOR('E', 0, size_t), + FIOC_SET_SIZE = _IOW('E', 1, size_t), + + /* + * The following two ioctls don't follow usual encoding rules + * and transfer variable amount of data. + */ + FIOC_READ = _IO('E', 2), + FIOC_WRITE = _IO('E', 3), +}; + +struct fioc_rw_arg { + off_t offset; + void *buf; + size_t size; + size_t prev_size; /* out param for previous total size */ + size_t new_size; /* out param for new total size */ }; Index: fuse/example/fioclient.c =================================================================== --- fuse.orig/example/fioclient.c 2009-04-28 14:53:28.000000000 +0200 +++ fuse/example/fioclient.c 2009-04-28 14:53:34.000000000 +0200 @@ -20,19 +20,56 @@ #include "fioc.h" const char *usage = -"Usage: fioclient FIOC_FILE [SIZE]\n" +"Usage: fioclient FIOC_FILE COMMAND\n" "\n" -" get size if SIZE is omitted, set size otherwise\n" +"COMMANDS\n" +" s [SIZE] : get size if SIZE is omitted, set size otherwise\n" +" r SIZE [OFF] : read SIZE bytes @ OFF (dfl 0) and output to stdout\n" +" w SIZE [OFF] : write SIZE bytes @ OFF (dfl 0) from stdin\n" "\n"; +static int do_rw(int fd, int is_read, size_t size, off_t offset, + size_t *prev_size, size_t *new_size) +{ + struct fioc_rw_arg arg = { .offset = offset }; + ssize_t ret; + + arg.buf = calloc(1, size); + if (!arg.buf) { + fprintf(stderr, "failed to allocated %zu bytes\n", size); + return -1; + } + + if (is_read) { + arg.size = size; + ret = ioctl(fd, FIOC_READ, &arg); + if (ret >= 0) + fwrite(arg.buf, 1, ret, stdout); + } else { + arg.size = fread(arg.buf, 1, size, stdin); + fprintf(stderr, "Writing %zu bytes\n", arg.size); + ret = ioctl(fd, FIOC_WRITE, &arg); + } + + if (ret >= 0) { + *prev_size = arg.prev_size; + *new_size = arg.new_size; + } else + perror("ioctl"); + + free(arg.buf); + return ret; +} + int main(int argc, char **argv) { - size_t size; - int fd; + size_t param[2] = { }; + size_t size, prev_size = 0, new_size = 0; + char cmd; + int fd, i, rc; - if (argc < 2) { + if (argc < 3) goto usage; - } fd = open(argv[1], O_RDWR); if (fd < 0) { @@ -40,27 +77,46 @@ int main(int argc, char **argv) return 1; } - if (argc == 2) { - if (ioctl(fd, FIOC_GET_SIZE, &size)) { - perror("ioctl"); - return 1; - } - printf("%zu\n", size); - } else { - char *endp; + cmd = tolower(argv[2][0]); + argc -= 3; + argv += 3; - size = strtoul(argv[2], &endp, 0); - if (endp == argv[2] || *endp != '\0') + for (i = 0; i < argc; i++) { + char *endp; + param[i] = strtoul(argv[i], &endp, 0); + if (endp == argv[i] || *endp != '\0') goto usage; + } - if (ioctl(fd, FIOC_SET_SIZE, &size)) { - perror("ioctl"); - return 1; + switch (cmd) { + case 's': + if (!argc) { + if (ioctl(fd, FIOC_GET_SIZE, &size)) { + perror("ioctl"); + return 1; + } + printf("%zu\n", size); + } else { + size = param[0]; + if (ioctl(fd, FIOC_SET_SIZE, &size)) { + perror("ioctl"); + return 1; + } } + return 0; + + case 'r': + case 'w': + rc = do_rw(fd, cmd == 'r', param[0], param[1], + &prev_size, &new_size); + if (rc < 0) + return 1; + fprintf(stderr, "transferred %d bytes (%zu -> %zu)\n", + rc, prev_size, new_size); + return 0; } - return 0; -usage: + usage: fprintf(stderr, usage); return 1; } Index: fuse/include/Makefile.am =================================================================== --- fuse.orig/include/Makefile.am 2009-04-28 14:53:28.000000000 +0200 +++ fuse/include/Makefile.am 2009-04-28 14:53:34.000000000 +0200 @@ -9,7 +9,8 @@ fuseinclude_HEADERS = \ fuse_common_compat.h \ fuse_lowlevel.h \ fuse_lowlevel_compat.h \ - fuse_opt.h + fuse_opt.h \ + cuse_lowlevel.h include_HEADERS = old/fuse.h ulockmgr.h Index: fuse/include/cuse_lowlevel.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ fuse/include/cuse_lowlevel.h 2009-04-28 14:53:34.000000000 +0200 @@ -0,0 +1,87 @@ +/* + CUSE: Character device in Userspace + Copyright (C) 2008-2009 SUSE Linux Products GmbH + Copyright (C) 2008-2009 Tejun Heo <tj...@ke...> + + This program can be distributed under the terms of the GNU LGPLv2. + See the file COPYING.LIB. + + Read example/cusexmp.c for usages. +*/ + +#ifndef _CUSE_LOWLEVEL_H_ +#define _CUSE_LOWLEVEL_H_ + +#ifndef FUSE_USE_VERSION +#define FUSE_USE_VERSION 29 +#endif + +#include "fuse_lowlevel.h" + +#include <fcntl.h> +#include <sys/types.h> +#include <sys/uio.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define CUSE_UNRESTRICTED_IOCTL (1 << 0) /* use unrestricted ioctl */ + +struct fuse_session; + +struct cuse_info { + unsigned dev_major; + unsigned dev_minor; + unsigned dev_info_argc; + const char **dev_info_argv; + unsigned flags; +}; + +/* + * Most ops behave almost identically to the matching fuse_lowlevel + * ops except that they don't take @ino. + * + * init_done : called after initialization is complete + * read/write : always direct IO, simultaneous operations allowed + * ioctl : might be in unrestricted mode depending on ci->flags + */ +struct cuse_lowlevel_ops { + void (*init) (void *userdata, struct fuse_conn_info *conn); + void (*init_done) (void *userdata); + void (*destroy) (void *userdata); + void (*open) (fuse_req_t req, struct fuse_file_info *fi); + void (*read) (fuse_req_t req, size_t size, off_t off, + struct fuse_file_info *fi); + void (*write) (fuse_req_t req, const char *buf, size_t size, off_t off, + struct fuse_file_info *fi); + void (*flush) (fuse_req_t req, struct fuse_file_info *fi); + void (*release) (fuse_req_t req, struct fuse_file_info *fi); + void (*fsync) (fuse_req_t req, int datasync, struct fuse_file_info *fi); + void (*ioctl) (fuse_req_t req, int cmd, void *arg, + struct fuse_file_info *fi, unsigned int flags, + const void *in_buf, size_t in_bufsz, size_t out_bufsz); + void (*poll) (fuse_req_t req, struct fuse_file_info *fi, + struct fuse_pollhandle *ph); +}; + +struct fuse_session *cuse_lowlevel_new(struct fuse_args *args, + const struct cuse_info *ci, + const struct cuse_lowlevel_ops *clop, + void *userdata); + +struct fuse_session *cuse_lowlevel_setup(int argc, char *argv[], + const struct cuse_info *ci, + const struct cuse_lowlevel_ops *clop, + int *multithreaded, void *userdata); + +void cuse_lowlevel_teardown(struct fuse_session *se); + +int cuse_lowlevel_main(int argc, char *argv[], const struct cuse_info *ci, + const struct cuse_lowlevel_ops *clop, void *userdata); + +#ifdef __cplusplus +} +#endif + +#endif /* _CUSE_LOWLEVEL_H_ */ Index: fuse/include/fuse_kernel.h =================================================================== --- fuse.orig/include/fuse_kernel.h 2009-04-28 14:53:28.000000000 +0200 +++ fuse/include/fuse_kernel.h 2009-04-28 14:53:34.000000000 +0200 @@ -150,6 +150,13 @@ struct fuse_file_lock { #define FUSE_BIG_WRITES (1 << 5) /** + * CUSE INIT request/reply flags + * + * CUSE_UNRESTRICTED_IOCTL: use unrestricted ioctl + */ +#define CUSE_UNRESTRICTED_IOCTL (1 << 0) + +/** * Release flags */ #define FUSE_RELEASE_FLUSH (1 << 0) @@ -239,6 +246,9 @@ enum fuse_opcode { FUSE_DESTROY = 38, FUSE_IOCTL = 39, FUSE_POLL = 40, + + /* CUSE specific operations */ + CUSE_INIT = 4096, }; enum fuse_notify_code { @@ -430,6 +440,27 @@ struct fuse_init_out { __u32 max_write; }; +#define CUSE_INIT_INFO_MAX 4096 + +struct cuse_init_in { + __u32 major; + __u32 minor; + __u32 unused; + __u32 flags; +}; + +struct cuse_init_out { + __u32 major; + __u32 minor; + __u32 unused; + __u32 flags; + __u32 max_read; + __u32 max_write; + __u32 dev_major; /* chardev major */ + __u32 dev_minor; /* chardev minor */ + __u32 spare[10]; +}; + struct fuse_interrupt_in { __u64 unique; }; Index: fuse/lib/Makefile.am =================================================================== --- fuse.orig/lib/Makefile.am 2009-04-28 14:53:28.000000000 +0200 +++ fuse/lib/Makefile.am 2009-04-28 14:53:34.000000000 +0200 @@ -29,6 +29,7 @@ libfuse_la_SOURCES = \ fuse_opt.c \ fuse_session.c \ fuse_signals.c \ + cuse_lowlevel.c \ helper.c \ modules/subdir.c \ $(iconv_source) \ Index: fuse/lib/cuse_lowlevel.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ fuse/lib/cuse_lowlevel.c 2009-04-28 15:14:34.000000000 +0200 @@ -0,0 +1,371 @@ +/* + CUSE: Character device in Userspace + Copyright (C) 2008 SUSE Linux Products GmbH + Copyright (C) 2008 Tejun Heo <te...@su...> + + This program can be distributed under the terms of the GNU LGPLv2. + See the file COPYING.LIB. +*/ + +#include "cuse_lowlevel.h" +#include "fuse_kernel.h" +#include "fuse_i.h" +#include "fuse_opt.h" +#include "fuse_misc.h" + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stddef.h> +#include <errno.h> +#include <unistd.h> + +struct cuse_data { + struct cuse_lowlevel_ops clop; + unsigned max_read; + unsigned dev_major; + unsigned dev_minor; + unsigned flags; + unsigned dev_info_len; + char dev_info[]; +}; + +static struct cuse_lowlevel_ops *req_clop(fuse_req_t req) +{ + return &req->f->cuse_data->clop; +} + +static void cuse_fll_open(fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi) +{ + (void)ino; + req_clop(req)->open(req, fi); +} + +static void cuse_fll_read(fuse_req_t req, fuse_ino_t ino, size_t size, + off_t off, struct fuse_file_info *fi) +{ + (void)ino; + req_clop(req)->read(req, size, off, fi); +} + +static void cuse_fll_write(fuse_req_t req, fuse_ino_t ino, const char *buf, + size_t size, off_t off, struct fuse_file_info *fi) +{ + (void)ino; + req_clop(req)->write(req, buf, size, off, fi); +} + +static void cuse_fll_flush(fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi) +{ + (void)ino; + req_clop(req)->flush(req, fi); +} + +static void cuse_fll_release(fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi) +{ + (void)ino; + req_clop(req)->release(req, fi); +} + +static void cuse_fll_fsync(fuse_req_t req, fuse_ino_t ino, int datasync, + struct fuse_file_info *fi) +{ + (void)ino; + req_clop(req)->fsync(req, datasync, fi); +} + +static void cuse_fll_ioctl(fuse_req_t req, fuse_ino_t ino, int cmd, void *arg, + struct fuse_file_info *fi, unsigned int flags, + const void *in_buf, size_t in_bufsz, size_t out_bufsz) +{ + (void)ino; + req_clop(req)->ioctl(req, cmd, arg, fi, flags, in_buf, in_bufsz, + out_bufsz); +} + +static void cuse_fll_poll(fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi, struct fuse_pollhandle *ph) +{ + (void)ino; + req_clop(req)->poll(req, fi, ph); +} + +static size_t cuse_pack_info(int argc, const char **argv, char *buf) +{ + size_t size = 0; + int i; + + for (i = 0; i < argc; i++) { + size_t len; + + len = strlen(argv[i]) + 1; + size += len; + if (buf) { + memcpy(buf, argv[i], len); + buf += len; + } + } + + return size; +} + +static struct cuse_data *cuse_prep_data(const struct cuse_info *ci, + const struct cuse_lowlevel_ops *clop) +{ + struct cuse_data *cd; + size_t dev_info_len; + + dev_info_len = cuse_pack_info(ci->dev_info_argc, ci->dev_info_argv, + NULL); + + if (dev_info_len > CUSE_INIT_INFO_MAX) { + fprintf(stderr, "cuse: dev_info (%zu) too large, limit=%u\n", + dev_info_len, CUSE_INIT_INFO_MAX); + return NULL; + } + + cd = calloc(1, sizeof(*cd) + dev_info_len); + if (!cd) { + fprintf(stderr, "cuse: failed to allocate cuse_data\n"); + return NULL; + } + + memcpy(&cd->clop, clop, sizeof(cd->clop)); + cd->max_read = 131072; + cd->dev_major = ci->dev_major; + cd->dev_minor = ci->dev_minor; + cd->dev_info_len = dev_info_len; + cd->flags = ci->flags; + cuse_pack_info(ci->dev_info_argc, ci->dev_info_argv, cd->dev_info); + + return cd; +} + +struct fuse_session *cuse_lowlevel_new(struct fuse_args *args, + const struct cuse_info *ci, + const struct cuse_lowlevel_ops *clop, + void *userdata) +{ + struct fuse_lowlevel_ops lop; + struct cuse_data *cd; + struct fuse_session *se; + struct fuse_ll *ll; + + cd = cuse_prep_data(ci, clop); + if (!cd) + return NULL; + + memset(&lop, 0, sizeof(lop)); + lop.init = clop->init; + lop.destroy = clop->destroy; + lop.open = clop->open ? cuse_fll_open : NULL; + lop.read = clop->read ? cuse_fll_read : NULL; + lop.write = clop->write ? cuse_fll_write : NULL; + lop.flush = clop->flush ? cuse_fll_flush : NULL; + lop.release = clop->release ? cuse_fll_release : NULL; + lop.fsync = clop->fsync ? cuse_fll_fsync : NULL; + lop.ioctl = clop->ioctl ? cuse_fll_ioctl : NULL; + lop.poll = clop->poll ? cuse_fll_poll : NULL; + + se = fuse_lowlevel_new_common(args, &lop, sizeof(lop), userdata); + if (!se) { + free(cd); + return NULL; + } + ll = se->data; + ll->cuse_data = cd; + + return se; +} + +static int cuse_reply_init(fuse_req_t req, struct cuse_init_out *arg, + char *dev_info, unsigned dev_info_len) +{ + struct iovec iov[3]; + + iov[1].iov_base = arg; + iov[1].iov_len = sizeof(struct cuse_init_out); + iov[2].iov_base = dev_info; + iov[2].iov_len = dev_info_len; + + return send_reply_iov_nofree(req, 0, iov, 3); +} + +void do_cuse_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + struct fuse_init_in *arg = (struct fuse_init_in *) inarg; + struct cuse_init_out outarg; + struct fuse_ll *f = req->f; + struct cuse_data *cd = f->cuse_data; + size_t bufsize = fuse_chan_bufsize(req->ch); + struct cuse_lowlevel_ops *clop = req_clop(req); + + (void) nodeid; + if (f->debug) { + fprintf(stderr, "CUSE_INIT: %u.%u\n", arg->major, arg->minor); + fprintf(stderr, "flags=0x%08x\n", arg->flags); + } + f->conn.proto_major = arg->major; + f->conn.proto_minor = arg->minor; + f->conn.capable = 0; + f->conn.want = 0; + + if (arg->major < 7) { + fprintf(stderr, "fuse: unsupported protocol version: %u.%u\n", + arg->major, arg->minor); + fuse_reply_err(req, EPROTO); + return; + } + + if (bufsize < FUSE_MIN_READ_BUFFER) { + fprintf(stderr, "fuse: warning: buffer size too small: %zu\n", + bufsize); + bufsize = FUSE_MIN_READ_BUFFER; + } + + bufsize -= 4096; + 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); + + memset(&outarg, 0, sizeof(outarg)); + outarg.major = FUSE_KERNEL_VERSION; + outarg.minor = FUSE_KERNEL_MINOR_VERSION; + outarg.flags = cd->flags; + outarg.max_read = cd->max_read; + outarg.max_write = f->conn.max_write; + outarg.dev_major = cd->dev_major; + outarg.dev_minor = cd->dev_minor; + + if (f->debug) { + fprintf(stderr, " CUSE_INIT: %u.%u\n", + outarg.major, outarg.minor); + fprintf(stderr, " flags=0x%08x\n", outarg.flags); + fprintf(stderr, " max_read=0x%08x\n", outarg.max_read); + fprintf(stderr, " max_write=0x%08x\n", outarg.max_write); + fprintf(stderr, " dev_major=%u\n", outarg.dev_major); + fprintf(stderr, " dev_minor=%u\n", outarg.dev_minor); + fprintf(stderr, " dev_info: %.*s\n", cd->dev_info_len, + cd->dev_info); + } + + cuse_reply_init(req, &outarg, cd->dev_info, cd->dev_info_len); + + if (clop->init_done) + clop->init_done(f->userdata); + + free_req(req); +} + +struct fuse_session *cuse_lowlevel_setup(int argc, char *argv[], + const struct cuse_info *ci, + const struct cuse_lowlevel_ops *clop, + int *multithreaded, void *userdata) +{ + const char *devname = "/dev/cuse"; + static const struct fuse_opt kill_subtype_opts[] = { + FUSE_OPT_KEY("subtype=", FUSE_OPT_KEY_DISCARD), + FUSE_OPT_END + }; + struct fuse_args args = FUSE_ARGS_INIT(argc, argv); + struct fuse_session *se; + struct fuse_chan *ch; + int fd; + int foreground; + int res; + + res = fuse_parse_cmdline(&args, NULL, multithreaded, &foreground); + if (res == -1) + goto err_args; + + res = fuse_opt_parse(&args, NULL, kill_subtype_opts, NULL); + if (res == -1) + goto err_args; + + /* + * Make sure file descriptors 0, 1 and 2 are open, otherwise chaos + * would ensue. + */ + do { + fd = open("/dev/null", O_RDWR); + if (fd > 2) + close(fd); + } while (fd >= 0 && fd <= 2); + + se = cuse_lowlevel_new(&args, ci, clop, userdata); + fuse_opt_free_args(&args); + if (se == NULL) + goto err_args; + + fd = open(devname, O_RDWR); + if (fd == -1) { + if (errno == ENODEV || errno == ENOENT) + fprintf(stderr, "fuse: device not found, try 'modprobe cuse' first\n"); + else + fprintf(stderr, "fuse: failed to open %s: %s\n", + devname, strerror(errno)); + goto err_se; + } + + ch = fuse_kern_chan_new(fd); + if (!ch) { + close(fd); + goto err_se; + } + + fuse_session_add_chan(se, ch); + + res = fuse_set_signal_handlers(se); + if (res == -1) + goto err_se; + + res = fuse_daemonize(foreground); + if (res == -1) + goto err_sig; + + return se; + +err_sig: + fuse_remove_signal_handlers(se); +err_se: + fuse_session_destroy(se); +err_args: + fuse_opt_free_args(&args); + return NULL; +} + +void cuse_lowlevel_teardown(struct fuse_session *se) +{ + fuse_remove_signal_handlers(se); + fuse_session_destroy(se); +} + +int cuse_lowlevel_main(int argc, char *argv[], const struct cuse_info *ci, + const struct cuse_lowlevel_ops *clop, void *userdata) +{ + struct fuse_session *se; + int multithreaded; + int res; + + se = cuse_lowlevel_setup(argc, argv, ci, clop, &multithreaded, + userdata); + if (se == NULL) + return 1; + + if (multithreaded) + res = fuse_session_loop_mt(se); + else + res = fuse_session_loop(se); + + cuse_lowlevel_teardown(se); + if (res == -1) + return 1; + + return 0; +} Index: fuse/lib/fuse_i.h =================================================================== --- fuse.orig/lib/fuse_i.h 2009-04-28 14:53:28.000000000 +0200 +++ fuse/lib/fuse_i.h 2009-04-28 15:14:40.000000000 +0200 @@ -51,6 +51,7 @@ struct fuse_ll { int big_writes; struct fuse_lowlevel_ops op; int got_init; + struct cuse_data *cuse_data; void *userdata; uid_t owner; struct fuse_conn_info conn; @@ -83,6 +84,9 @@ void fuse_kern_unmount(const char *mount int fuse_kern_mount(const char *mountpoint, struct fuse_args *args); int send_reply_iov(fuse_req_t req, int error, struct iovec *iov, int count); +int send_reply_iov_nofree(fuse_req_t req, int error, struct iovec *iov, int count); +void free_req(fuse_req_t req); + struct fuse *fuse_setup_common(int argc, char *argv[], const struct fuse_operations *op, @@ -92,3 +96,5 @@ struct fuse *fuse_setup_common(int argc, int *fd, void *user_data, int compat); + +void do_cuse_init(fuse_req_t req, fuse_ino_t nodeide, const void *inarg); Index: fuse/lib/fuse_lowlevel.c =================================================================== --- fuse.orig/lib/fuse_lowlevel.c 2009-04-28 14:53:28.000000000 +0200 +++ fuse/lib/fuse_lowlevel.c 2009-04-28 15:12:19.000000000 +0200 @@ -100,7 +100,7 @@ static void destroy_req(fuse_req_t req) free(req); } -static void free_req(fuse_req_t req) +void free_req(fuse_req_t req) { int ctr; struct fuse_ll *f = req->f; @@ -115,10 +115,10 @@ static void free_req(fuse_req_t req) destroy_req(req); } -int send_reply_iov(fuse_req_t req, int error, struct iovec *iov, int count) +int send_reply_iov_nofree(fuse_req_t req, int error, struct iovec *iov, + int count) { struct fuse_out_header out; - int res; if (error <= -1000 || error > 0) { fprintf(stderr, "fuse: bad error value: %i\n", error); @@ -143,9 +143,16 @@ int send_reply_iov(fuse_req_t req, int e (unsigned long long) out.unique, out.len); } } - res = fuse_chan_send(req->ch, iov, count); - free_req(req); + return fuse_chan_send(req->ch, iov, count); +} + +int send_reply_iov(fuse_req_t req, int error, struct iovec *iov, int count) +{ + int res; + + res = send_reply_iov_nofree(req, error, iov, count); + free_req(req); return res; } @@ -1339,6 +1346,7 @@ static struct { [FUSE_IOCTL] = { do_ioctl, "IOCTL" }, [FUSE_POLL] = { do_poll, "POLL" }, [FUSE_DESTROY] = { do_destroy, "DESTROY" }, + [CUSE_INIT] = { do_cuse_init, "CUSE_INIT" }, }; #define FUSE_MAXOP (sizeof(fuse_ll_ops) / sizeof(fuse_ll_ops[0])) @@ -1385,9 +1393,12 @@ static void fuse_ll_process(void *data, err = EIO; if (!f->got_init) { - if (in->opcode != FUSE_INIT) + enum fuse_opcode expected; + + expected = f->cuse_data ? CUSE_INIT : FUSE_INIT; + if (in->opcode != expected) goto reply_err; - } else if (in->opcode == FUSE_INIT) + } else if (in->opcode == FUSE_INIT || in->opcode == CUSE_INIT) goto reply_err; err = EACCES; @@ -1495,6 +1506,7 @@ static void fuse_ll_destroy(void *data) } pthread_mutex_destroy(&f->lock); + free(f->cuse_data); free(f); } Index: fuse/lib/fuse_versionscript =================================================================== --- fuse.orig/lib/fuse_versionscript 2009-04-28 14:53:28.000000000 +0200 +++ fuse/lib/fuse_versionscript 2009-04-28 14:53:34.000000000 +0200 @@ -155,6 +155,10 @@ FUSE_2.7 { FUSE_2.8 { global: + cuse_lowlevel_new; + cuse_lowlevel_main; + cuse_lowlevel_setup; + cuse_lowlevel_teardown; fuse_fs_ioctl; fuse_fs_poll; fuse_lowlevel_notify_poll; |