From: Nikolaus Rath Date: Mon, 10 Oct 2016 02:50:51 +0000 (-0700) Subject: Renamed ioctl and poll examples X-Git-Tag: fuse-3.0.0rc1~73 X-Git-Url: http://git.maquefel.me/?a=commitdiff_plain;h=bcad1a6f22662ff0d04a6ae417adb30550252d97;p=qemu-gpiodev%2Flibfuse.git Renamed ioctl and poll examples The new names should make it more obvious at first glance what each example demonstrates. --- diff --git a/example/.gitignore b/example/.gitignore index ea56c8d..fb1794a 100644 --- a/example/.gitignore +++ b/example/.gitignore @@ -2,10 +2,10 @@ /passthrough_fh /hello /hello_ll -/fioc -/fioclient -/fsel -/fselclient +/ioctl +/ioctl_client +/poll +/poll_client /cusexmp /passthrough_ll /timefs1 diff --git a/example/Makefile.am b/example/Makefile.am index 5d82f64..9d780a7 100644 --- a/example/Makefile.am +++ b/example/Makefile.am @@ -1,17 +1,18 @@ ## Process this file with automake to produce Makefile.in AM_CPPFLAGS = -I$(top_srcdir)/include -D_REENTRANT -noinst_HEADERS = fioc.h -noinst_PROGRAMS = passthrough passthrough_fh hello hello_ll fioc fioclient \ - fsel fselclient cusexmp passthrough_ll timefs1 timefs2 \ +noinst_HEADERS = ioctl.h +noinst_PROGRAMS = passthrough passthrough_fh hello hello_ll \ + ioctl ioctl_client poll poll_client \ + cusexmp passthrough_ll timefs1 timefs2 \ timefs3 LDADD = ../lib/libfuse3.la passthrough_fh_LDADD = ../lib/libfuse3.la @passthrough_fh_libs@ -fioclient_CPPFLAGS = -fioclient_LDFLAGS = -fioclient_LDADD = -fselclient_CPPFLAGS = -fselclient_LDFLAGS = -fselclient_LDADD = +ioctl_client_CPPFLAGS = +ioctl_client_LDFLAGS = +ioctl_client_LDADD = +poll_client_CPPFLAGS = +poll_client_LDFLAGS = +poll_client_LDADD = diff --git a/example/cusexmp.c b/example/cusexmp.c index 8d22075..a9f3365 100644 --- a/example/cusexmp.c +++ b/example/cusexmp.c @@ -35,7 +35,7 @@ #include #include -#include "fioc.h" +#include "ioctl.h" static void *cusexmp_buf; static size_t cusexmp_size; diff --git a/example/fioc.c b/example/fioc.c deleted file mode 100644 index 368f807..0000000 --- a/example/fioc.c +++ /dev/null @@ -1,228 +0,0 @@ -/* - FUSE fioc: FUSE ioctl example - 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 - * - * fioc.c - FUSE fioc: FUSE ioctl example - * - * \section section_compile compiling this example - * - * gcc -Wall fioc.c `pkg-config fuse3 --cflags --libs` -o fioc - * - * \section section_source the complete source - * \include fioc.c - */ - - -#define FUSE_USE_VERSION 30 - -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "fioc.h" - -#define FIOC_NAME "fioc" - -enum { - FIOC_NONE, - FIOC_ROOT, - FIOC_FILE, -}; - -static void *fioc_buf; -static size_t fioc_size; - -static int fioc_resize(size_t new_size) -{ - void *new_buf; - - if (new_size == fioc_size) - return 0; - - new_buf = realloc(fioc_buf, new_size); - if (!new_buf && new_size) - return -ENOMEM; - - if (new_size > fioc_size) - memset(new_buf + fioc_size, 0, new_size - fioc_size); - - fioc_buf = new_buf; - fioc_size = new_size; - - return 0; -} - -static int fioc_expand(size_t new_size) -{ - if (new_size > fioc_size) - return fioc_resize(new_size); - return 0; -} - -static int fioc_file_type(const char *path) -{ - if (strcmp(path, "/") == 0) - return FIOC_ROOT; - if (strcmp(path, "/" FIOC_NAME) == 0) - return FIOC_FILE; - return FIOC_NONE; -} - -static int fioc_getattr(const char *path, struct stat *stbuf) -{ - stbuf->st_uid = getuid(); - stbuf->st_gid = getgid(); - stbuf->st_atime = stbuf->st_mtime = time(NULL); - - switch (fioc_file_type(path)) { - case FIOC_ROOT: - stbuf->st_mode = S_IFDIR | 0755; - stbuf->st_nlink = 2; - break; - case FIOC_FILE: - stbuf->st_mode = S_IFREG | 0644; - stbuf->st_nlink = 1; - stbuf->st_size = fioc_size; - break; - case FIOC_NONE: - return -ENOENT; - } - - return 0; -} - -static int fioc_open(const char *path, struct fuse_file_info *fi) -{ - (void) fi; - - if (fioc_file_type(path) != FIOC_NONE) - return 0; - return -ENOENT; -} - -static int fioc_do_read(char *buf, size_t size, off_t offset) -{ - if (offset >= fioc_size) - return 0; - - if (size > fioc_size - offset) - size = fioc_size - offset; - - memcpy(buf, fioc_buf + offset, size); - - return size; -} - -static int fioc_read(const char *path, char *buf, size_t size, - off_t offset, struct fuse_file_info *fi) -{ - (void) fi; - - if (fioc_file_type(path) != FIOC_FILE) - return -EINVAL; - - return fioc_do_read(buf, size, offset); -} - -static int fioc_do_write(const char *buf, size_t size, off_t offset) -{ - if (fioc_expand(offset + size)) - return -ENOMEM; - - memcpy(fioc_buf + offset, buf, size); - - return size; -} - -static int fioc_write(const char *path, const char *buf, size_t size, - off_t offset, struct fuse_file_info *fi) -{ - (void) fi; - - if (fioc_file_type(path) != FIOC_FILE) - return -EINVAL; - - return fioc_do_write(buf, size, offset); -} - -static int fioc_truncate(const char *path, off_t size) -{ - if (fioc_file_type(path) != FIOC_FILE) - return -EINVAL; - - return fioc_resize(size); -} - -static int fioc_readdir(const char *path, void *buf, fuse_fill_dir_t filler, - off_t offset, struct fuse_file_info *fi, - enum fuse_readdir_flags flags) -{ - (void) fi; - (void) offset; - (void) flags; - - if (fioc_file_type(path) != FIOC_ROOT) - return -ENOENT; - - filler(buf, ".", NULL, 0, 0); - filler(buf, "..", NULL, 0, 0); - filler(buf, FIOC_NAME, NULL, 0, 0); - - return 0; -} - -static int fioc_ioctl(const char *path, int cmd, void *arg, - struct fuse_file_info *fi, unsigned int flags, void *data) -{ - (void) arg; - (void) fi; - (void) flags; - - if (fioc_file_type(path) != FIOC_FILE) - return -EINVAL; - - if (flags & FUSE_IOCTL_COMPAT) - return -ENOSYS; - - switch (cmd) { - case FIOC_GET_SIZE: - *(size_t *)data = fioc_size; - return 0; - - case FIOC_SET_SIZE: - fioc_resize(*(size_t *)data); - return 0; - } - - return -EINVAL; -} - -static struct fuse_operations fioc_oper = { - .getattr = fioc_getattr, - .readdir = fioc_readdir, - .truncate = fioc_truncate, - .open = fioc_open, - .read = fioc_read, - .write = fioc_write, - .ioctl = fioc_ioctl, -}; - -int main(int argc, char *argv[]) -{ - return fuse_main(argc, argv, &fioc_oper, NULL); -} diff --git a/example/fioc.h b/example/fioc.h deleted file mode 100755 index 42799aa..0000000 --- a/example/fioc.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - FUSE-ioctl: ioctl support for FUSE - 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 - * - * fioc.h - FUSE-ioctl: ioctl support for FUSE - * - * \include fioc.h - */ - - -#include -#include -#include - -enum { - 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 */ -}; diff --git a/example/fioclient.c b/example/fioclient.c deleted file mode 100644 index 704f24b..0000000 --- a/example/fioclient.c +++ /dev/null @@ -1,72 +0,0 @@ -/* - 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 - * - * fioclient.c - FUSE fioclient: FUSE ioctl example client - * - * \section section_compile compiling this example - * - * gcc -Wall fioclient.c -o fioclient - * - * \section section_source the complete source - * fioclient.c - * \include fioclient.c - */ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include "fioc.h" - -const char *usage = -"Usage: fioclient FIOC_FILE [size]\n" -"\n" -"Get size if is omitted, set size otherwise\n" -"\n"; - -int main(int argc, char **argv) -{ - size_t size; - int fd; - - if (argc < 2) { - fprintf(stderr, "%s", usage); - return 1; - } - - fd = open(argv[1], O_RDWR); - if (fd < 0) { - perror("open"); - return 1; - } - - if (argc == 2) { - if (ioctl(fd, FIOC_GET_SIZE, &size)) { - perror("ioctl"); - return 1; - } - printf("%zu\n", size); - } else { - size = strtoul(argv[2], NULL, 0); - if (ioctl(fd, FIOC_SET_SIZE, &size)) { - perror("ioctl"); - return 1; - } - } - return 0; -} diff --git a/example/fsel.c b/example/fsel.c deleted file mode 100644 index b496c9a..0000000 --- a/example/fsel.c +++ /dev/null @@ -1,295 +0,0 @@ -/* - FUSE fsel: FUSE select example - 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 - * - * fsel.c - FUSE fsel: FUSE select example - * - * \section section_compile compiling this example - * - * gcc -Wall fsel.c `pkg-config fuse3 --cflags --libs` -o fsel - * - * \section section_source the complete source - * \include fsel.c - */ - - -#define FUSE_USE_VERSION 30 - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * fsel_open_mask is used to limit the number of opens to 1 per file. - * This is to use file index (0-F) as fh as poll support requires - * unique fh per open file. Lifting this would require proper open - * file management. - */ -static unsigned fsel_open_mask; -static const char fsel_hex_map[] = "0123456789ABCDEF"; -static struct fuse *fsel_fuse; /* needed for poll notification */ - -#define FSEL_CNT_MAX 10 /* each file can store upto 10 chars */ -#define FSEL_FILES 16 - -static pthread_mutex_t fsel_mutex; /* protects notify_mask and cnt array */ -static unsigned fsel_poll_notify_mask; /* poll notification scheduled? */ -static struct fuse_pollhandle *fsel_poll_handle[FSEL_FILES]; /* poll notify handles */ -static unsigned fsel_cnt[FSEL_FILES]; /* nbytes stored in each file */ - -static int fsel_path_index(const char *path) -{ - char ch = path[1]; - - if (strlen(path) != 2 || path[0] != '/' || !isxdigit(ch) || islower(ch)) - return -1; - return ch <= '9' ? ch - '0' : ch - 'A' + 10; -} - -static int fsel_getattr(const char *path, struct stat *stbuf) -{ - int idx; - - memset(stbuf, 0, sizeof(struct stat)); - - if (strcmp(path, "/") == 0) { - stbuf->st_mode = S_IFDIR | 0555; - stbuf->st_nlink = 2; - return 0; - } - - idx = fsel_path_index(path); - if (idx < 0) - return -ENOENT; - - stbuf->st_mode = S_IFREG | 0444; - stbuf->st_nlink = 1; - stbuf->st_size = fsel_cnt[idx]; - return 0; -} - -static int fsel_readdir(const char *path, void *buf, fuse_fill_dir_t filler, - off_t offset, struct fuse_file_info *fi, - enum fuse_readdir_flags flags) -{ - char name[2] = { }; - int i; - - (void) offset; - (void) fi; - (void) flags; - - if (strcmp(path, "/") != 0) - return -ENOENT; - - for (i = 0; i < FSEL_FILES; i++) { - name[0] = fsel_hex_map[i]; - filler(buf, name, NULL, 0, 0); - } - - return 0; -} - -static int fsel_open(const char *path, struct fuse_file_info *fi) -{ - int idx = fsel_path_index(path); - - if (idx < 0) - return -ENOENT; - if ((fi->flags & 3) != O_RDONLY) - return -EACCES; - if (fsel_open_mask & (1 << idx)) - return -EBUSY; - fsel_open_mask |= (1 << idx); - - /* - * fsel files are nonseekable somewhat pipe-like files which - * gets filled up periodically by producer thread and consumed - * on read. Tell FUSE as such. - */ - fi->fh = idx; - fi->direct_io = 1; - fi->nonseekable = 1; - - return 0; -} - -static int fsel_release(const char *path, struct fuse_file_info *fi) -{ - int idx = fi->fh; - - (void) path; - - fsel_open_mask &= ~(1 << idx); - return 0; -} - -static int fsel_read(const char *path, char *buf, size_t size, off_t offset, - struct fuse_file_info *fi) -{ - int idx = fi->fh; - - (void) path; - (void) offset; - - pthread_mutex_lock(&fsel_mutex); - if (fsel_cnt[idx] < size) - size = fsel_cnt[idx]; - printf("READ %X transferred=%zu cnt=%u\n", idx, size, fsel_cnt[idx]); - fsel_cnt[idx] -= size; - pthread_mutex_unlock(&fsel_mutex); - - memset(buf, fsel_hex_map[idx], size); - return size; -} - -static int fsel_poll(const char *path, struct fuse_file_info *fi, - struct fuse_pollhandle *ph, unsigned *reventsp) -{ - static unsigned polled_zero; - int idx = fi->fh; - - (void) path; - - /* - * Poll notification requires pointer to struct fuse which - * can't be obtained when using fuse_main(). As notification - * happens only after poll is called, fill it here from - * fuse_context. - */ - if (!fsel_fuse) { - struct fuse_context *cxt = fuse_get_context(); - if (cxt) - fsel_fuse = cxt->fuse; - } - - pthread_mutex_lock(&fsel_mutex); - - if (ph != NULL) { - struct fuse_pollhandle *oldph = fsel_poll_handle[idx]; - - if (oldph) - fuse_pollhandle_destroy(oldph); - - fsel_poll_notify_mask |= (1 << idx); - fsel_poll_handle[idx] = ph; - } - - if (fsel_cnt[idx]) { - *reventsp |= POLLIN; - printf("POLL %X cnt=%u polled_zero=%u\n", - idx, fsel_cnt[idx], polled_zero); - polled_zero = 0; - } else - polled_zero++; - - pthread_mutex_unlock(&fsel_mutex); - return 0; -} - -static struct fuse_operations fsel_oper = { - .getattr = fsel_getattr, - .readdir = fsel_readdir, - .open = fsel_open, - .release = fsel_release, - .read = fsel_read, - .poll = fsel_poll, -}; - -static void *fsel_producer(void *data) -{ - const struct timespec interval = { 0, 250000000 }; - unsigned idx = 0, nr = 1; - - (void) data; - - while (1) { - int i, t; - - pthread_mutex_lock(&fsel_mutex); - - /* - * This is the main producer loop which is executed - * ever 500ms. On each iteration, it fills one byte - * to 1, 2 or 4 files and sends poll notification if - * requested. - */ - for (i = 0, t = idx; i < nr; - i++, t = (t + FSEL_FILES / nr) % FSEL_FILES) { - if (fsel_cnt[t] == FSEL_CNT_MAX) - continue; - - fsel_cnt[t]++; - if (fsel_fuse && (fsel_poll_notify_mask & (1 << t))) { - struct fuse_pollhandle *ph; - - printf("NOTIFY %X\n", t); - ph = fsel_poll_handle[t]; - fuse_notify_poll(ph); - fuse_pollhandle_destroy(ph); - fsel_poll_notify_mask &= ~(1 << t); - fsel_poll_handle[t] = NULL; - } - } - - idx = (idx + 1) % FSEL_FILES; - if (idx == 0) - nr = (nr * 2) % 7; /* cycle through 1, 2 and 4 */ - - pthread_mutex_unlock(&fsel_mutex); - - nanosleep(&interval, NULL); - } - - return NULL; -} - -int main(int argc, char *argv[]) -{ - pthread_t producer; - pthread_attr_t attr; - int ret; - - errno = pthread_mutex_init(&fsel_mutex, NULL); - if (errno) { - perror("pthread_mutex_init"); - return 1; - } - - errno = pthread_attr_init(&attr); - if (errno) { - perror("pthread_attr_init"); - return 1; - } - - errno = pthread_create(&producer, &attr, fsel_producer, NULL); - if (errno) { - perror("pthread_create"); - return 1; - } - - ret = fuse_main(argc, argv, &fsel_oper, NULL); - - pthread_cancel(producer); - pthread_join(producer, NULL); - - return ret; -} diff --git a/example/fselclient.c b/example/fselclient.c deleted file mode 100644 index 637cb07..0000000 --- a/example/fselclient.c +++ /dev/null @@ -1,86 +0,0 @@ -/* - FUSE fselclient: FUSE select 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 - * - * fselclient.c - FUSE fselclient: FUSE select example client - * - * \section section_compile compiling this example - * - * gcc -Wall fselclient.c -o fselclient - * - * \section section_source the complete source - * \include fselclient.c - */ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define FSEL_FILES 16 - -int main(void) -{ - static const char hex_map[FSEL_FILES] = "0123456789ABCDEF"; - int fds[FSEL_FILES]; - int i, nfds, tries; - - for (i = 0; i < FSEL_FILES; i++) { - char name[] = { hex_map[i], '\0' }; - fds[i] = open(name, O_RDONLY); - if (fds[i] < 0) { - perror("open"); - return 1; - } - } - nfds = fds[FSEL_FILES - 1] + 1; - - for(tries=0; tries < 16; tries++) { - static char buf[4096]; - fd_set rfds; - int rc; - - FD_ZERO(&rfds); - for (i = 0; i < FSEL_FILES; i++) - FD_SET(fds[i], &rfds); - - rc = select(nfds, &rfds, NULL, NULL, NULL); - - if (rc < 0) { - perror("select"); - return 1; - } - - for (i = 0; i < FSEL_FILES; i++) { - if (!FD_ISSET(fds[i], &rfds)) { - printf("_: "); - continue; - } - printf("%X:", i); - rc = read(fds[i], buf, sizeof(buf)); - if (rc < 0) { - perror("read"); - return 1; - } - printf("%02d ", rc); - } - printf("\n"); - } -} diff --git a/example/ioctl.c b/example/ioctl.c new file mode 100644 index 0000000..ee58f03 --- /dev/null +++ b/example/ioctl.c @@ -0,0 +1,228 @@ +/* + FUSE fioc: FUSE ioctl example + 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 example illustrates how to write a FUSE file system that can + * process (a restricted set of) ioctls. It can be tested with the + * ioctl_client.c program. + * + * \section section_compile compiling this example + * + * gcc -Wall ioctl.c `pkg-config fuse3 --cflags --libs` -o ioctl + * + * \section section_source the complete source + * \include ioctl.c + */ + +#define FUSE_USE_VERSION 30 + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "ioctl.h" + +#define FIOC_NAME "fioc" + +enum { + FIOC_NONE, + FIOC_ROOT, + FIOC_FILE, +}; + +static void *fioc_buf; +static size_t fioc_size; + +static int fioc_resize(size_t new_size) +{ + void *new_buf; + + if (new_size == fioc_size) + return 0; + + new_buf = realloc(fioc_buf, new_size); + if (!new_buf && new_size) + return -ENOMEM; + + if (new_size > fioc_size) + memset(new_buf + fioc_size, 0, new_size - fioc_size); + + fioc_buf = new_buf; + fioc_size = new_size; + + return 0; +} + +static int fioc_expand(size_t new_size) +{ + if (new_size > fioc_size) + return fioc_resize(new_size); + return 0; +} + +static int fioc_file_type(const char *path) +{ + if (strcmp(path, "/") == 0) + return FIOC_ROOT; + if (strcmp(path, "/" FIOC_NAME) == 0) + return FIOC_FILE; + return FIOC_NONE; +} + +static int fioc_getattr(const char *path, struct stat *stbuf) +{ + stbuf->st_uid = getuid(); + stbuf->st_gid = getgid(); + stbuf->st_atime = stbuf->st_mtime = time(NULL); + + switch (fioc_file_type(path)) { + case FIOC_ROOT: + stbuf->st_mode = S_IFDIR | 0755; + stbuf->st_nlink = 2; + break; + case FIOC_FILE: + stbuf->st_mode = S_IFREG | 0644; + stbuf->st_nlink = 1; + stbuf->st_size = fioc_size; + break; + case FIOC_NONE: + return -ENOENT; + } + + return 0; +} + +static int fioc_open(const char *path, struct fuse_file_info *fi) +{ + (void) fi; + + if (fioc_file_type(path) != FIOC_NONE) + return 0; + return -ENOENT; +} + +static int fioc_do_read(char *buf, size_t size, off_t offset) +{ + if (offset >= fioc_size) + return 0; + + if (size > fioc_size - offset) + size = fioc_size - offset; + + memcpy(buf, fioc_buf + offset, size); + + return size; +} + +static int fioc_read(const char *path, char *buf, size_t size, + off_t offset, struct fuse_file_info *fi) +{ + (void) fi; + + if (fioc_file_type(path) != FIOC_FILE) + return -EINVAL; + + return fioc_do_read(buf, size, offset); +} + +static int fioc_do_write(const char *buf, size_t size, off_t offset) +{ + if (fioc_expand(offset + size)) + return -ENOMEM; + + memcpy(fioc_buf + offset, buf, size); + + return size; +} + +static int fioc_write(const char *path, const char *buf, size_t size, + off_t offset, struct fuse_file_info *fi) +{ + (void) fi; + + if (fioc_file_type(path) != FIOC_FILE) + return -EINVAL; + + return fioc_do_write(buf, size, offset); +} + +static int fioc_truncate(const char *path, off_t size) +{ + if (fioc_file_type(path) != FIOC_FILE) + return -EINVAL; + + return fioc_resize(size); +} + +static int fioc_readdir(const char *path, void *buf, fuse_fill_dir_t filler, + off_t offset, struct fuse_file_info *fi, + enum fuse_readdir_flags flags) +{ + (void) fi; + (void) offset; + (void) flags; + + if (fioc_file_type(path) != FIOC_ROOT) + return -ENOENT; + + filler(buf, ".", NULL, 0, 0); + filler(buf, "..", NULL, 0, 0); + filler(buf, FIOC_NAME, NULL, 0, 0); + + return 0; +} + +static int fioc_ioctl(const char *path, int cmd, void *arg, + struct fuse_file_info *fi, unsigned int flags, void *data) +{ + (void) arg; + (void) fi; + (void) flags; + + if (fioc_file_type(path) != FIOC_FILE) + return -EINVAL; + + if (flags & FUSE_IOCTL_COMPAT) + return -ENOSYS; + + switch (cmd) { + case FIOC_GET_SIZE: + *(size_t *)data = fioc_size; + return 0; + + case FIOC_SET_SIZE: + fioc_resize(*(size_t *)data); + return 0; + } + + return -EINVAL; +} + +static struct fuse_operations fioc_oper = { + .getattr = fioc_getattr, + .readdir = fioc_readdir, + .truncate = fioc_truncate, + .open = fioc_open, + .read = fioc_read, + .write = fioc_write, + .ioctl = fioc_ioctl, +}; + +int main(int argc, char *argv[]) +{ + return fuse_main(argc, argv, &fioc_oper, NULL); +} diff --git a/example/ioctl.h b/example/ioctl.h new file mode 100644 index 0000000..ded2a15 --- /dev/null +++ b/example/ioctl.h @@ -0,0 +1,42 @@ +/* + FUSE-ioctl: ioctl support for FUSE + 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 + * + * Header file to share definitions between the ioctl.c example file + * system and the ioctl_client.c test program. + * + * \include ioctl.h + */ + + +#include +#include +#include + +enum { + 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 */ +}; diff --git a/example/ioctl_client.c b/example/ioctl_client.c new file mode 100644 index 0000000..83ede65 --- /dev/null +++ b/example/ioctl_client.c @@ -0,0 +1,73 @@ +/* + FUSE fioclient: FUSE ioctl example client + Copyright (C) 2008 SUSE Linux Products GmbH + Copyright (C) 2008 Tejun Heo + + This program tests the ioctl.c example file systsem. + + This program can be distributed under the terms of the GNU GPL. + See the file COPYING. +*/ + +/** @file + * @tableofcontents + * + * This program tests the ioctl.c example file systsem. + * + * \section section_compile compiling this example + * + * gcc -Wall ioctl_client.c -o ioctl_client + * + * \section section_source the complete source + * \include ioctl_client.c + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include "ioctl.h" + +const char *usage = +"Usage: fioclient FIOC_FILE [size]\n" +"\n" +"Get size if is omitted, set size otherwise\n" +"\n"; + +int main(int argc, char **argv) +{ + size_t size; + int fd; + + if (argc < 2) { + fprintf(stderr, "%s", usage); + return 1; + } + + fd = open(argv[1], O_RDWR); + if (fd < 0) { + perror("open"); + return 1; + } + + if (argc == 2) { + if (ioctl(fd, FIOC_GET_SIZE, &size)) { + perror("ioctl"); + return 1; + } + printf("%zu\n", size); + } else { + size = strtoul(argv[2], NULL, 0); + if (ioctl(fd, FIOC_SET_SIZE, &size)) { + perror("ioctl"); + return 1; + } + } + return 0; +} diff --git a/example/poll.c b/example/poll.c new file mode 100644 index 0000000..61c5365 --- /dev/null +++ b/example/poll.c @@ -0,0 +1,295 @@ +/* + FUSE fsel: FUSE select example + 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 example illustrates how to write a FUSE file system that + * supports polling for changes that don't come through the kernel. It + * can be tested with the poll_client.c program. + * + * \section section_compile compiling this example + * + * gcc -Wall poll.c `pkg-config fuse3 --cflags --libs` -o poll + * + * \section section_source the complete source + * \include poll.c + */ + +#define FUSE_USE_VERSION 30 + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * fsel_open_mask is used to limit the number of opens to 1 per file. + * This is to use file index (0-F) as fh as poll support requires + * unique fh per open file. Lifting this would require proper open + * file management. + */ +static unsigned fsel_open_mask; +static const char fsel_hex_map[] = "0123456789ABCDEF"; +static struct fuse *fsel_fuse; /* needed for poll notification */ + +#define FSEL_CNT_MAX 10 /* each file can store upto 10 chars */ +#define FSEL_FILES 16 + +static pthread_mutex_t fsel_mutex; /* protects notify_mask and cnt array */ +static unsigned fsel_poll_notify_mask; /* poll notification scheduled? */ +static struct fuse_pollhandle *fsel_poll_handle[FSEL_FILES]; /* poll notify handles */ +static unsigned fsel_cnt[FSEL_FILES]; /* nbytes stored in each file */ + +static int fsel_path_index(const char *path) +{ + char ch = path[1]; + + if (strlen(path) != 2 || path[0] != '/' || !isxdigit(ch) || islower(ch)) + return -1; + return ch <= '9' ? ch - '0' : ch - 'A' + 10; +} + +static int fsel_getattr(const char *path, struct stat *stbuf) +{ + int idx; + + memset(stbuf, 0, sizeof(struct stat)); + + if (strcmp(path, "/") == 0) { + stbuf->st_mode = S_IFDIR | 0555; + stbuf->st_nlink = 2; + return 0; + } + + idx = fsel_path_index(path); + if (idx < 0) + return -ENOENT; + + stbuf->st_mode = S_IFREG | 0444; + stbuf->st_nlink = 1; + stbuf->st_size = fsel_cnt[idx]; + return 0; +} + +static int fsel_readdir(const char *path, void *buf, fuse_fill_dir_t filler, + off_t offset, struct fuse_file_info *fi, + enum fuse_readdir_flags flags) +{ + char name[2] = { }; + int i; + + (void) offset; + (void) fi; + (void) flags; + + if (strcmp(path, "/") != 0) + return -ENOENT; + + for (i = 0; i < FSEL_FILES; i++) { + name[0] = fsel_hex_map[i]; + filler(buf, name, NULL, 0, 0); + } + + return 0; +} + +static int fsel_open(const char *path, struct fuse_file_info *fi) +{ + int idx = fsel_path_index(path); + + if (idx < 0) + return -ENOENT; + if ((fi->flags & 3) != O_RDONLY) + return -EACCES; + if (fsel_open_mask & (1 << idx)) + return -EBUSY; + fsel_open_mask |= (1 << idx); + + /* + * fsel files are nonseekable somewhat pipe-like files which + * gets filled up periodically by producer thread and consumed + * on read. Tell FUSE as such. + */ + fi->fh = idx; + fi->direct_io = 1; + fi->nonseekable = 1; + + return 0; +} + +static int fsel_release(const char *path, struct fuse_file_info *fi) +{ + int idx = fi->fh; + + (void) path; + + fsel_open_mask &= ~(1 << idx); + return 0; +} + +static int fsel_read(const char *path, char *buf, size_t size, off_t offset, + struct fuse_file_info *fi) +{ + int idx = fi->fh; + + (void) path; + (void) offset; + + pthread_mutex_lock(&fsel_mutex); + if (fsel_cnt[idx] < size) + size = fsel_cnt[idx]; + printf("READ %X transferred=%zu cnt=%u\n", idx, size, fsel_cnt[idx]); + fsel_cnt[idx] -= size; + pthread_mutex_unlock(&fsel_mutex); + + memset(buf, fsel_hex_map[idx], size); + return size; +} + +static int fsel_poll(const char *path, struct fuse_file_info *fi, + struct fuse_pollhandle *ph, unsigned *reventsp) +{ + static unsigned polled_zero; + int idx = fi->fh; + + (void) path; + + /* + * Poll notification requires pointer to struct fuse which + * can't be obtained when using fuse_main(). As notification + * happens only after poll is called, fill it here from + * fuse_context. + */ + if (!fsel_fuse) { + struct fuse_context *cxt = fuse_get_context(); + if (cxt) + fsel_fuse = cxt->fuse; + } + + pthread_mutex_lock(&fsel_mutex); + + if (ph != NULL) { + struct fuse_pollhandle *oldph = fsel_poll_handle[idx]; + + if (oldph) + fuse_pollhandle_destroy(oldph); + + fsel_poll_notify_mask |= (1 << idx); + fsel_poll_handle[idx] = ph; + } + + if (fsel_cnt[idx]) { + *reventsp |= POLLIN; + printf("POLL %X cnt=%u polled_zero=%u\n", + idx, fsel_cnt[idx], polled_zero); + polled_zero = 0; + } else + polled_zero++; + + pthread_mutex_unlock(&fsel_mutex); + return 0; +} + +static struct fuse_operations fsel_oper = { + .getattr = fsel_getattr, + .readdir = fsel_readdir, + .open = fsel_open, + .release = fsel_release, + .read = fsel_read, + .poll = fsel_poll, +}; + +static void *fsel_producer(void *data) +{ + const struct timespec interval = { 0, 250000000 }; + unsigned idx = 0, nr = 1; + + (void) data; + + while (1) { + int i, t; + + pthread_mutex_lock(&fsel_mutex); + + /* + * This is the main producer loop which is executed + * ever 500ms. On each iteration, it fills one byte + * to 1, 2 or 4 files and sends poll notification if + * requested. + */ + for (i = 0, t = idx; i < nr; + i++, t = (t + FSEL_FILES / nr) % FSEL_FILES) { + if (fsel_cnt[t] == FSEL_CNT_MAX) + continue; + + fsel_cnt[t]++; + if (fsel_fuse && (fsel_poll_notify_mask & (1 << t))) { + struct fuse_pollhandle *ph; + + printf("NOTIFY %X\n", t); + ph = fsel_poll_handle[t]; + fuse_notify_poll(ph); + fuse_pollhandle_destroy(ph); + fsel_poll_notify_mask &= ~(1 << t); + fsel_poll_handle[t] = NULL; + } + } + + idx = (idx + 1) % FSEL_FILES; + if (idx == 0) + nr = (nr * 2) % 7; /* cycle through 1, 2 and 4 */ + + pthread_mutex_unlock(&fsel_mutex); + + nanosleep(&interval, NULL); + } + + return NULL; +} + +int main(int argc, char *argv[]) +{ + pthread_t producer; + pthread_attr_t attr; + int ret; + + errno = pthread_mutex_init(&fsel_mutex, NULL); + if (errno) { + perror("pthread_mutex_init"); + return 1; + } + + errno = pthread_attr_init(&attr); + if (errno) { + perror("pthread_attr_init"); + return 1; + } + + errno = pthread_create(&producer, &attr, fsel_producer, NULL); + if (errno) { + perror("pthread_create"); + return 1; + } + + ret = fuse_main(argc, argv, &fsel_oper, NULL); + + pthread_cancel(producer); + pthread_join(producer, NULL); + + return ret; +} diff --git a/example/poll_client.c b/example/poll_client.c new file mode 100644 index 0000000..5c04fea --- /dev/null +++ b/example/poll_client.c @@ -0,0 +1,87 @@ +/* + FUSE fselclient: FUSE select 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 + * + * poll_client.c + * + * This program tests the poll.c example file systsem. + * + * \section section_compile compiling this example + * + * gcc -Wall poll_client.c -o poll_client + * + * \section section_source the complete source + * \include poll_client.c + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define FSEL_FILES 16 + +int main(void) +{ + static const char hex_map[FSEL_FILES] = "0123456789ABCDEF"; + int fds[FSEL_FILES]; + int i, nfds, tries; + + for (i = 0; i < FSEL_FILES; i++) { + char name[] = { hex_map[i], '\0' }; + fds[i] = open(name, O_RDONLY); + if (fds[i] < 0) { + perror("open"); + return 1; + } + } + nfds = fds[FSEL_FILES - 1] + 1; + + for(tries=0; tries < 16; tries++) { + static char buf[4096]; + fd_set rfds; + int rc; + + FD_ZERO(&rfds); + for (i = 0; i < FSEL_FILES; i++) + FD_SET(fds[i], &rfds); + + rc = select(nfds, &rfds, NULL, NULL, NULL); + + if (rc < 0) { + perror("select"); + return 1; + } + + for (i = 0; i < FSEL_FILES; i++) { + if (!FD_ISSET(fds[i], &rfds)) { + printf("_: "); + continue; + } + printf("%X:", i); + rc = read(fds[i], buf, sizeof(buf)); + if (rc < 0) { + perror("read"); + return 1; + } + printf("%02d ", rc); + } + printf("\n"); + } +} diff --git a/test/test_examples.py b/test/test_examples.py index 6deaff1..0d754d2 100755 --- a/test/test_examples.py +++ b/test/test_examples.py @@ -101,17 +101,17 @@ def test_passthrough(tmpdir, name, options): else: umount(mount_process, mnt_dir) -def test_fioc(tmpdir): +def test_ioctl(tmpdir): mnt_dir = str(tmpdir) testfile = pjoin(mnt_dir, 'fioc') cmdline = base_cmdline + \ - [pjoin(basename, 'example', 'fioc'), '-f', mnt_dir ] + [pjoin(basename, 'example', 'ioctl'), '-f', mnt_dir ] mount_process = subprocess.Popen(cmdline) try: wait_for_mount(mount_process, mnt_dir) cmdline = base_cmdline + \ - [ pjoin(basename, 'example', 'fioclient'), + [ pjoin(basename, 'example', 'ioctl_client'), testfile ] assert subprocess.check_output(cmdline) == b'0\n' with open(testfile, 'wb') as fh: @@ -126,15 +126,15 @@ def test_fioc(tmpdir): else: umount(mount_process, mnt_dir) -def test_fsel(tmpdir): +def test_poll(tmpdir): mnt_dir = str(tmpdir) - cmdline = base_cmdline + [pjoin(basename, 'example', 'fsel'), + cmdline = base_cmdline + [pjoin(basename, 'example', 'poll'), '-f', mnt_dir ] mount_process = subprocess.Popen(cmdline) try: wait_for_mount(mount_process, mnt_dir) cmdline = base_cmdline + \ - [ pjoin(basename, 'example', 'fselclient') ] + [ pjoin(basename, 'example', 'poll_client') ] subprocess.check_call(cmdline, cwd=mnt_dir) except: cleanup(mnt_dir)