From 35ce627e35d1e293b25cfa8bdec2fb82c7da4630 Mon Sep 17 00:00:00 2001 From: Nikolaus Rath Date: Sun, 9 Oct 2016 21:05:54 -0700 Subject: Renamed cuses example and added test program An earlier version of the fioclient.c example was intended to be used together with cusexmp.c. The former has since evolved into ioctl_client.c and no longer has the function necessary to test CUSE. Therefore, we've added a new cuse_client.c that is clearly associated with the cuse.c example file system. --- example/.gitignore | 3 +- example/Makefile.am | 5 +- example/cuse.c | 322 ++++++++++++++++++++++++++++++++++++++++++++++++++ example/cuse_client.c | 153 ++++++++++++++++++++++++ example/cusexmp.c | 309 ------------------------------------------------ 5 files changed, 480 insertions(+), 312 deletions(-) create mode 100644 example/cuse.c create mode 100755 example/cuse_client.c delete mode 100644 example/cusexmp.c (limited to 'example') diff --git a/example/.gitignore b/example/.gitignore index f7cd045..6dd5004 100644 --- a/example/.gitignore +++ b/example/.gitignore @@ -6,7 +6,8 @@ /ioctl_client /poll /poll_client -/cusexmp +/cuse +/cuse_client /passthrough_ll /notify_inval_inode /notify_store_retrieve diff --git a/example/Makefile.am b/example/Makefile.am index 6d93edc..6e267a0 100644 --- a/example/Makefile.am +++ b/example/Makefile.am @@ -4,8 +4,9 @@ AM_CPPFLAGS = -I$(top_srcdir)/include -D_REENTRANT noinst_HEADERS = ioctl.h noinst_PROGRAMS = passthrough passthrough_fh hello hello_ll \ ioctl ioctl_client poll poll_client \ - cusexmp passthrough_ll notify_inval_inode \ - notify_store_retrieve notify_inval_entry + passthrough_ll notify_inval_inode \ + notify_store_retrieve notify_inval_entry \ + cuse cuse_client LDADD = ../lib/libfuse3.la passthrough_fh_LDADD = ../lib/libfuse3.la @passthrough_fh_libs@ diff --git a/example/cuse.c b/example/cuse.c new file mode 100644 index 0000000..443d308 --- /dev/null +++ b/example/cuse.c @@ -0,0 +1,322 @@ +/* + CUSE example: Character device in Userspace + Copyright (C) 2008-2009 SUSE Linux Products GmbH + Copyright (C) 2008-2009 Tejun Heo + + This program can be distributed under the terms of the GNU GPL. + See the file COPYING. + +*/ + +/** @file + * @tableofcontents + * + * This example demonstrates how to implement a character device in + * userspace ("CUSE"). This is only allowed for root. The character + * device should appear in /dev under the specified name. It can be + * tested with the cuse_client.c program. + * + * Mount the file system with: + * + * cuse -f --name=mydevice + * + * You should now have a new /dev/mydevice character device. To "unmount" it, + * kill the "cuse" process. + * + * \section section_compile compiling this example + * + * gcc -Wall cuse.c `pkg-config fuse3 --cflags --libs` -o cuse + * + * \section section_source the complete source + * \include cuse.c + */ + + +#define FUSE_USE_VERSION 30 + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ioctl.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" +" -d -o debug enable debug output (implies -f)\n" +" -f foreground operation\n" +" -s disable multi-threaded operation\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, "%s", 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); +} diff --git a/example/cuse_client.c b/example/cuse_client.c new file mode 100755 index 0000000..3e47635 --- /dev/null +++ b/example/cuse_client.c @@ -0,0 +1,153 @@ +/* + FUSE fioclient: FUSE ioctl example client + Copyright (C) 2008 SUSE Linux Products GmbH + Copyright (C) 2008 Tejun Heo + + This program can be distributed under the terms of the GNU GPL. + See the file COPYING. +*/ + +/** @file + * @tableofcontents + * + * This program tests the cuse.c example file system. + * + * Example usage (assuming that /dev/foobar is a CUSE device provided + * by the cuse.c example file system): + * + * $ cuse_client /dev/foobar s + * 0 + * + * $ echo "hello" | cuse_client /dev/foobar w 6 + * Writing 6 bytes + * transferred 6 bytes (0 -> 6) + * + * $ cuse_client /dev/foobar s + * 6 + * + * $ cuse_client /dev/foobar r 10 + * hello + * transferred 6 bytes (6 -> 6) + * + * \section section_compile compiling this example + * + * gcc -Wall cuse_client.c -o cuse_client + * + * \section section_source the complete source + * \include cuse_client.c + */ + + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include "ioctl.h" + +const char *usage = +"Usage: cuse_client FIOC_FILE COMMAND\n" +"\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 param[2] = { }; + size_t size, prev_size = 0, new_size = 0; + char cmd; + int fd, i, rc; + + if (argc < 3) + goto usage; + + fd = open(argv[1], O_RDWR); + if (fd < 0) { + perror("open"); + return 1; + } + + cmd = tolower(argv[2][0]); + argc -= 3; + argv += 3; + + for (i = 0; i < argc; i++) { + char *endp; + param[i] = strtoul(argv[i], &endp, 0); + if (endp == argv[i] || *endp != '\0') + goto usage; + } + + 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; + } + + usage: + fprintf(stderr, "%s", usage); + return 1; +} diff --git a/example/cusexmp.c b/example/cusexmp.c deleted file mode 100644 index a9f3365..0000000 --- a/example/cusexmp.c +++ /dev/null @@ -1,309 +0,0 @@ -/* - CUSE example: Character device in Userspace - Copyright (C) 2008-2009 SUSE Linux Products GmbH - Copyright (C) 2008-2009 Tejun Heo - - This program can be distributed under the terms of the GNU GPL. - See the file COPYING. - -*/ - -/** @file - * @tableofcontents - * - * cusexmp.c - CUSE example: Character device in Userspace - * - * \section section_compile compiling this example - * - * gcc -Wall cusexmp.c `pkg-config fuse3 --cflags --libs` -o cusexmp - * - * \section section_source the complete source - * \include cusexmp.c - */ - - -#define FUSE_USE_VERSION 30 - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ioctl.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, "%s", 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); -} -- cgit v1.2.3