summaryrefslogtreecommitdiff
path: root/draft/fuse-examples
diff options
context:
space:
mode:
Diffstat (limited to 'draft/fuse-examples')
-rw-r--r--draft/fuse-examples/cusexmp.c294
-rw-r--r--draft/fuse-examples/fioc.c211
-rw-r--r--draft/fuse-examples/fioc.h32
-rw-r--r--draft/fuse-examples/fioclient.c122
-rw-r--r--draft/fuse-examples/fsel.c278
-rw-r--r--draft/fuse-examples/fselclient.c72
-rw-r--r--draft/fuse-examples/fusexmp.c412
-rw-r--r--draft/fuse-examples/fusexmp_fh.c567
-rw-r--r--draft/fuse-examples/hello.c96
-rw-r--r--draft/fuse-examples/hello_ll.c181
-rw-r--r--draft/fuse-examples/null.c95
11 files changed, 2360 insertions, 0 deletions
diff --git a/draft/fuse-examples/cusexmp.c b/draft/fuse-examples/cusexmp.c
new file mode 100644
index 0000000..01fcdf7
--- /dev/null
+++ b/draft/fuse-examples/cusexmp.c
@@ -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@kernel.org>
+
+ This program can be distributed under the terms of the GNU GPL.
+ See the file COPYING.
+
+ gcc -Wall cusexmp.c `pkg-config fuse --cflags --libs` -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, "%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, &param, 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/draft/fuse-examples/fioc.c b/draft/fuse-examples/fioc.c
new file mode 100644
index 0000000..bee40b9
--- /dev/null
+++ b/draft/fuse-examples/fioc.c
@@ -0,0 +1,211 @@
+/*
+ FUSE fioc: FUSE ioctl example
+ Copyright (C) 2008 SUSE Linux Products GmbH
+ Copyright (C) 2008 Tejun Heo <teheo@suse.de>
+
+ This program can be distributed under the terms of the GNU GPL.
+ See the file COPYING.
+
+ gcc -Wall fioc.c `pkg-config fuse --cflags --libs` -o fioc
+*/
+
+#define FUSE_USE_VERSION 26
+
+#include <fuse.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+#include <errno.h>
+
+#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)
+{
+ (void) fi;
+ (void) offset;
+
+ if (fioc_file_type(path) != FIOC_ROOT)
+ return -ENOENT;
+
+ filler(buf, ".", NULL, 0);
+ filler(buf, "..", NULL, 0);
+ filler(buf, FIOC_NAME, NULL, 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/draft/fuse-examples/fioc.h b/draft/fuse-examples/fioc.h
new file mode 100644
index 0000000..ec1a39d
--- /dev/null
+++ b/draft/fuse-examples/fioc.h
@@ -0,0 +1,32 @@
+/*
+ FUSE-ioctl: ioctl support for FUSE
+ Copyright (C) 2008 SUSE Linux Products GmbH
+ Copyright (C) 2008 Tejun Heo <teheo@suse.de>
+
+ This program can be distributed under the terms of the GNU GPL.
+ See the file COPYING.
+*/
+
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/ioctl.h>
+
+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/draft/fuse-examples/fioclient.c b/draft/fuse-examples/fioclient.c
new file mode 100644
index 0000000..5f05525
--- /dev/null
+++ b/draft/fuse-examples/fioclient.c
@@ -0,0 +1,122 @@
+/*
+ FUSE fioclient: FUSE ioctl example client
+ Copyright (C) 2008 SUSE Linux Products GmbH
+ Copyright (C) 2008 Tejun Heo <teheo@suse.de>
+
+ This program can be distributed under the terms of the GNU GPL.
+ See the file COPYING.
+
+ gcc -Wall fioclient.c -o fioclient
+*/
+
+#include <sys/types.h>
+#include <sys/fcntl.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+#include "fioc.h"
+
+const char *usage =
+"Usage: fioclient 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/draft/fuse-examples/fsel.c b/draft/fuse-examples/fsel.c
new file mode 100644
index 0000000..9cf0221
--- /dev/null
+++ b/draft/fuse-examples/fsel.c
@@ -0,0 +1,278 @@
+/*
+ FUSE fsel: FUSE select example
+ Copyright (C) 2008 SUSE Linux Products GmbH
+ Copyright (C) 2008 Tejun Heo <teheo@suse.de>
+
+ This program can be distributed under the terms of the GNU GPL.
+ See the file COPYING.
+
+ gcc -Wall fsel.c `pkg-config fuse --cflags --libs` -o fsel
+*/
+
+#define FUSE_USE_VERSION 29
+
+#include <fuse.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <time.h>
+#include <pthread.h>
+#include <poll.h>
+
+/*
+ * 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)
+{
+ char name[2] = { };
+ int i;
+
+ (void) offset;
+ (void) fi;
+
+ if (strcmp(path, "/") != 0)
+ return -ENOENT;
+
+ for (i = 0; i < FSEL_FILES; i++) {
+ name[0] = fsel_hex_map[i];
+ filler(buf, name, NULL, 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/draft/fuse-examples/fselclient.c b/draft/fuse-examples/fselclient.c
new file mode 100644
index 0000000..7c4b837
--- /dev/null
+++ b/draft/fuse-examples/fselclient.c
@@ -0,0 +1,72 @@
+/*
+ FUSE fselclient: FUSE select example client
+ Copyright (C) 2008 SUSE Linux Products GmbH
+ Copyright (C) 2008 Tejun Heo <teheo@suse.de>
+
+ This program can be distributed under the terms of the GNU GPL.
+ See the file COPYING.
+
+ gcc -Wall fselclient.c -o fselclient
+*/
+
+#include <sys/select.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#define FSEL_FILES 16
+
+int main(void)
+{
+ static const char hex_map[FSEL_FILES] = "0123456789ABCDEF";
+ int fds[FSEL_FILES];
+ int i, nfds;
+
+ 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;
+
+ while (1) {
+ 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/draft/fuse-examples/fusexmp.c b/draft/fuse-examples/fusexmp.c
new file mode 100644
index 0000000..dca8a46
--- /dev/null
+++ b/draft/fuse-examples/fusexmp.c
@@ -0,0 +1,412 @@
+/*
+ FUSE: Filesystem in Userspace
+ Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
+ Copyright (C) 2011 Sebastian Pipping <sebastian@pipping.org>
+
+ This program can be distributed under the terms of the GNU GPL.
+ See the file COPYING.
+
+ gcc -Wall fusexmp.c `pkg-config fuse --cflags --libs` -o fusexmp
+*/
+
+#define FUSE_USE_VERSION 26
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef linux
+/* For pread()/pwrite()/utimensat() */
+#define _XOPEN_SOURCE 700
+#endif
+
+#include <fuse.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <errno.h>
+#include <sys/time.h>
+#ifdef HAVE_SETXATTR
+#include <sys/xattr.h>
+#endif
+
+static int xmp_getattr(const char *path, struct stat *stbuf)
+{
+ int res;
+
+ res = lstat(path, stbuf);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_access(const char *path, int mask)
+{
+ int res;
+
+ res = access(path, mask);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_readlink(const char *path, char *buf, size_t size)
+{
+ int res;
+
+ res = readlink(path, buf, size - 1);
+ if (res == -1)
+ return -errno;
+
+ buf[res] = '\0';
+ return 0;
+}
+
+
+static int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
+ off_t offset, struct fuse_file_info *fi)
+{
+ DIR *dp;
+ struct dirent *de;
+
+ (void) offset;
+ (void) fi;
+
+ dp = opendir(path);
+ if (dp == NULL)
+ return -errno;
+
+ while ((de = readdir(dp)) != NULL) {
+ struct stat st;
+ memset(&st, 0, sizeof(st));
+ st.st_ino = de->d_ino;
+ st.st_mode = de->d_type << 12;
+ if (filler(buf, de->d_name, &st, 0))
+ break;
+ }
+
+ closedir(dp);
+ return 0;
+}
+
+static int xmp_mknod(const char *path, mode_t mode, dev_t rdev)
+{
+ int res;
+
+ /* On Linux this could just be 'mknod(path, mode, rdev)' but this
+ is more portable */
+ if (S_ISREG(mode)) {
+ res = open(path, O_CREAT | O_EXCL | O_WRONLY, mode);
+ if (res >= 0)
+ res = close(res);
+ } else if (S_ISFIFO(mode))
+ res = mkfifo(path, mode);
+ else
+ res = mknod(path, mode, rdev);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_mkdir(const char *path, mode_t mode)
+{
+ int res;
+
+ res = mkdir(path, mode);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_unlink(const char *path)
+{
+ int res;
+
+ res = unlink(path);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_rmdir(const char *path)
+{
+ int res;
+
+ res = rmdir(path);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_symlink(const char *from, const char *to)
+{
+ int res;
+
+ res = symlink(from, to);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_rename(const char *from, const char *to)
+{
+ int res;
+
+ res = rename(from, to);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_link(const char *from, const char *to)
+{
+ int res;
+
+ res = link(from, to);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_chmod(const char *path, mode_t mode)
+{
+ int res;
+
+ res = chmod(path, mode);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_chown(const char *path, uid_t uid, gid_t gid)
+{
+ int res;
+
+ res = lchown(path, uid, gid);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_truncate(const char *path, off_t size)
+{
+ int res;
+
+ res = truncate(path, size);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+#ifdef HAVE_UTIMENSAT
+static int xmp_utimens(const char *path, const struct timespec ts[2])
+{
+ int res;
+
+ /* don't use utime/utimes since they follow symlinks */
+ res = utimensat(0, path, ts, AT_SYMLINK_NOFOLLOW);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+#endif
+
+static int xmp_open(const char *path, struct fuse_file_info *fi)
+{
+ int res;
+
+ res = open(path, fi->flags);
+ if (res == -1)
+ return -errno;
+
+ close(res);
+ return 0;
+}
+
+static int xmp_read(const char *path, char *buf, size_t size, off_t offset,
+ struct fuse_file_info *fi)
+{
+ int fd;
+ int res;
+
+ (void) fi;
+ fd = open(path, O_RDONLY);
+ if (fd == -1)
+ return -errno;
+
+ res = pread(fd, buf, size, offset);
+ if (res == -1)
+ res = -errno;
+
+ close(fd);
+ return res;
+}
+
+static int xmp_write(const char *path, const char *buf, size_t size,
+ off_t offset, struct fuse_file_info *fi)
+{
+ int fd;
+ int res;
+
+ (void) fi;
+ fd = open(path, O_WRONLY);
+ if (fd == -1)
+ return -errno;
+
+ res = pwrite(fd, buf, size, offset);
+ if (res == -1)
+ res = -errno;
+
+ close(fd);
+ return res;
+}
+
+static int xmp_statfs(const char *path, struct statvfs *stbuf)
+{
+ int res;
+
+ res = statvfs(path, stbuf);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_release(const char *path, struct fuse_file_info *fi)
+{
+ /* Just a stub. This method is optional and can safely be left
+ unimplemented */
+
+ (void) path;
+ (void) fi;
+ return 0;
+}
+
+static int xmp_fsync(const char *path, int isdatasync,
+ struct fuse_file_info *fi)
+{
+ /* Just a stub. This method is optional and can safely be left
+ unimplemented */
+
+ (void) path;
+ (void) isdatasync;
+ (void) fi;
+ return 0;
+}
+
+#ifdef HAVE_POSIX_FALLOCATE
+static int xmp_fallocate(const char *path, int mode,
+ off_t offset, off_t length, struct fuse_file_info *fi)
+{
+ int fd;
+ int res;
+
+ (void) fi;
+
+ if (mode)
+ return -EOPNOTSUPP;
+
+ fd = open(path, O_WRONLY);
+ if (fd == -1)
+ return -errno;
+
+ res = -posix_fallocate(fd, offset, length);
+
+ close(fd);
+ return res;
+}
+#endif
+
+#ifdef HAVE_SETXATTR
+/* xattr operations are optional and can safely be left unimplemented */
+static int xmp_setxattr(const char *path, const char *name, const char *value,
+ size_t size, int flags)
+{
+ int res = lsetxattr(path, name, value, size, flags);
+ if (res == -1)
+ return -errno;
+ return 0;
+}
+
+static int xmp_getxattr(const char *path, const char *name, char *value,
+ size_t size)
+{
+ int res = lgetxattr(path, name, value, size);
+ if (res == -1)
+ return -errno;
+ return res;
+}
+
+static int xmp_listxattr(const char *path, char *list, size_t size)
+{
+ int res = llistxattr(path, list, size);
+ if (res == -1)
+ return -errno;
+ return res;
+}
+
+static int xmp_removexattr(const char *path, const char *name)
+{
+ int res = lremovexattr(path, name);
+ if (res == -1)
+ return -errno;
+ return 0;
+}
+#endif /* HAVE_SETXATTR */
+
+static struct fuse_operations xmp_oper = {
+ .getattr = xmp_getattr,
+ .access = xmp_access,
+ .readlink = xmp_readlink,
+ .readdir = xmp_readdir,
+ .mknod = xmp_mknod,
+ .mkdir = xmp_mkdir,
+ .symlink = xmp_symlink,
+ .unlink = xmp_unlink,
+ .rmdir = xmp_rmdir,
+ .rename = xmp_rename,
+ .link = xmp_link,
+ .chmod = xmp_chmod,
+ .chown = xmp_chown,
+ .truncate = xmp_truncate,
+#ifdef HAVE_UTIMENSAT
+ .utimens = xmp_utimens,
+#endif
+ .open = xmp_open,
+ .read = xmp_read,
+ .write = xmp_write,
+ .statfs = xmp_statfs,
+ .release = xmp_release,
+ .fsync = xmp_fsync,
+#ifdef HAVE_POSIX_FALLOCATE
+ .fallocate = xmp_fallocate,
+#endif
+#ifdef HAVE_SETXATTR
+ .setxattr = xmp_setxattr,
+ .getxattr = xmp_getxattr,
+ .listxattr = xmp_listxattr,
+ .removexattr = xmp_removexattr,
+#endif
+};
+
+int main(int argc, char *argv[])
+{
+ umask(0);
+ return fuse_main(argc, argv, &xmp_oper, NULL);
+}
diff --git a/draft/fuse-examples/fusexmp_fh.c b/draft/fuse-examples/fusexmp_fh.c
new file mode 100644
index 0000000..1ba9dbc
--- /dev/null
+++ b/draft/fuse-examples/fusexmp_fh.c
@@ -0,0 +1,567 @@
+/*
+ FUSE: Filesystem in Userspace
+ Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
+ Copyright (C) 2011 Sebastian Pipping <sebastian@pipping.org>
+
+ This program can be distributed under the terms of the GNU GPL.
+ See the file COPYING.
+
+ gcc -Wall fusexmp_fh.c `pkg-config fuse --cflags --libs` -lulockmgr -o fusexmp_fh
+*/
+
+#define FUSE_USE_VERSION 26
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+
+#include <fuse.h>
+#include <ulockmgr.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <errno.h>
+#include <sys/time.h>
+#ifdef HAVE_SETXATTR
+#include <sys/xattr.h>
+#endif
+#include <sys/file.h> /* flock(2) */
+
+static int xmp_getattr(const char *path, struct stat *stbuf)
+{
+ int res;
+
+ res = lstat(path, stbuf);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_fgetattr(const char *path, struct stat *stbuf,
+ struct fuse_file_info *fi)
+{
+ int res;
+
+ (void) path;
+
+ res = fstat(fi->fh, stbuf);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_access(const char *path, int mask)
+{
+ int res;
+
+ res = access(path, mask);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_readlink(const char *path, char *buf, size_t size)
+{
+ int res;
+
+ res = readlink(path, buf, size - 1);
+ if (res == -1)
+ return -errno;
+
+ buf[res] = '\0';
+ return 0;
+}
+
+struct xmp_dirp {
+ DIR *dp;
+ struct dirent *entry;
+ off_t offset;
+};
+
+static int xmp_opendir(const char *path, struct fuse_file_info *fi)
+{
+ int res;
+ struct xmp_dirp *d = malloc(sizeof(struct xmp_dirp));
+ if (d == NULL)
+ return -ENOMEM;
+
+ d->dp = opendir(path);
+ if (d->dp == NULL) {
+ res = -errno;
+ free(d);
+ return res;
+ }
+ d->offset = 0;
+ d->entry = NULL;
+
+ fi->fh = (unsigned long) d;
+ return 0;
+}
+
+static inline struct xmp_dirp *get_dirp(struct fuse_file_info *fi)
+{
+ return (struct xmp_dirp *) (uintptr_t) fi->fh;
+}
+
+static int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
+ off_t offset, struct fuse_file_info *fi)
+{
+ struct xmp_dirp *d = get_dirp(fi);
+
+ (void) path;
+ if (offset != d->offset) {
+ seekdir(d->dp, offset);
+ d->entry = NULL;
+ d->offset = offset;
+ }
+ while (1) {
+ struct stat st;
+ off_t nextoff;
+
+ if (!d->entry) {
+ d->entry = readdir(d->dp);
+ if (!d->entry)
+ break;
+ }
+
+ memset(&st, 0, sizeof(st));
+ st.st_ino = d->entry->d_ino;
+ st.st_mode = d->entry->d_type << 12;
+ nextoff = telldir(d->dp);
+ if (filler(buf, d->entry->d_name, &st, nextoff))
+ break;
+
+ d->entry = NULL;
+ d->offset = nextoff;
+ }
+
+ return 0;
+}
+
+static int xmp_releasedir(const char *path, struct fuse_file_info *fi)
+{
+ struct xmp_dirp *d = get_dirp(fi);
+ (void) path;
+ closedir(d->dp);
+ free(d);
+ return 0;
+}
+
+static int xmp_mknod(const char *path, mode_t mode, dev_t rdev)
+{
+ int res;
+
+ if (S_ISFIFO(mode))
+ res = mkfifo(path, mode);
+ else
+ res = mknod(path, mode, rdev);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_mkdir(const char *path, mode_t mode)
+{
+ int res;
+
+ res = mkdir(path, mode);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_unlink(const char *path)
+{
+ int res;
+
+ res = unlink(path);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_rmdir(const char *path)
+{
+ int res;
+
+ res = rmdir(path);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_symlink(const char *from, const char *to)
+{
+ int res;
+
+ res = symlink(from, to);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_rename(const char *from, const char *to)
+{
+ int res;
+
+ res = rename(from, to);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_link(const char *from, const char *to)
+{
+ int res;
+
+ res = link(from, to);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_chmod(const char *path, mode_t mode)
+{
+ int res;
+
+ res = chmod(path, mode);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_chown(const char *path, uid_t uid, gid_t gid)
+{
+ int res;
+
+ res = lchown(path, uid, gid);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_truncate(const char *path, off_t size)
+{
+ int res;
+
+ res = truncate(path, size);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_ftruncate(const char *path, off_t size,
+ struct fuse_file_info *fi)
+{
+ int res;
+
+ (void) path;
+
+ res = ftruncate(fi->fh, size);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+#ifdef HAVE_UTIMENSAT
+static int xmp_utimens(const char *path, const struct timespec ts[2])
+{
+ int res;
+
+ /* don't use utime/utimes since they follow symlinks */
+ res = utimensat(0, path, ts, AT_SYMLINK_NOFOLLOW);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+#endif
+
+static int xmp_create(const char *path, mode_t mode, struct fuse_file_info *fi)
+{
+ int fd;
+
+ fd = open(path, fi->flags, mode);
+ if (fd == -1)
+ return -errno;
+
+ fi->fh = fd;
+ return 0;
+}
+
+static int xmp_open(const char *path, struct fuse_file_info *fi)
+{
+ int fd;
+
+ fd = open(path, fi->flags);
+ if (fd == -1)
+ return -errno;
+
+ fi->fh = fd;
+ return 0;
+}
+
+static int xmp_read(const char *path, char *buf, size_t size, off_t offset,
+ struct fuse_file_info *fi)
+{
+ int res;
+
+ (void) path;
+ res = pread(fi->fh, buf, size, offset);
+ if (res == -1)
+ res = -errno;
+
+ return res;
+}
+
+static int xmp_read_buf(const char *path, struct fuse_bufvec **bufp,
+ size_t size, off_t offset, struct fuse_file_info *fi)
+{
+ struct fuse_bufvec *src;
+
+ (void) path;
+
+ src = malloc(sizeof(struct fuse_bufvec));
+ if (src == NULL)
+ return -ENOMEM;
+
+ *src = FUSE_BUFVEC_INIT(size);
+
+ src->buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK;
+ src->buf[0].fd = fi->fh;
+ src->buf[0].pos = offset;
+
+ *bufp = src;
+
+ return 0;
+}
+
+static int xmp_write(const char *path, const char *buf, size_t size,
+ off_t offset, struct fuse_file_info *fi)
+{
+ int res;
+
+ (void) path;
+ res = pwrite(fi->fh, buf, size, offset);
+ if (res == -1)
+ res = -errno;
+
+ return res;
+}
+
+static int xmp_write_buf(const char *path, struct fuse_bufvec *buf,
+ off_t offset, struct fuse_file_info *fi)
+{
+ struct fuse_bufvec dst = FUSE_BUFVEC_INIT(fuse_buf_size(buf));
+
+ (void) path;
+
+ dst.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK;
+ dst.buf[0].fd = fi->fh;
+ dst.buf[0].pos = offset;
+
+ return fuse_buf_copy(&dst, buf, FUSE_BUF_SPLICE_NONBLOCK);
+}
+
+static int xmp_statfs(const char *path, struct statvfs *stbuf)
+{
+ int res;
+
+ res = statvfs(path, stbuf);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_flush(const char *path, struct fuse_file_info *fi)
+{
+ int res;
+
+ (void) path;
+ /* This is called from every close on an open file, so call the
+ close on the underlying filesystem. But since flush may be
+ called multiple times for an open file, this must not really
+ close the file. This is important if used on a network
+ filesystem like NFS which flush the data/metadata on close() */
+ res = close(dup(fi->fh));
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_release(const char *path, struct fuse_file_info *fi)
+{
+ (void) path;
+ close(fi->fh);
+
+ return 0;
+}
+
+static int xmp_fsync(const char *path, int isdatasync,
+ struct fuse_file_info *fi)
+{
+ int res;
+ (void) path;
+
+#ifndef HAVE_FDATASYNC
+ (void) isdatasync;
+#else
+ if (isdatasync)
+ res = fdatasync(fi->fh);
+ else
+#endif
+ res = fsync(fi->fh);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+#ifdef HAVE_POSIX_FALLOCATE
+static int xmp_fallocate(const char *path, int mode,
+ off_t offset, off_t length, struct fuse_file_info *fi)
+{
+ (void) path;
+
+ if (mode)
+ return -EOPNOTSUPP;
+
+ return -posix_fallocate(fi->fh, offset, length);
+}
+#endif
+
+#ifdef HAVE_SETXATTR
+/* xattr operations are optional and can safely be left unimplemented */
+static int xmp_setxattr(const char *path, const char *name, const char *value,
+ size_t size, int flags)
+{
+ int res = lsetxattr(path, name, value, size, flags);
+ if (res == -1)
+ return -errno;
+ return 0;
+}
+
+static int xmp_getxattr(const char *path, const char *name, char *value,
+ size_t size)
+{
+ int res = lgetxattr(path, name, value, size);
+ if (res == -1)
+ return -errno;
+ return res;
+}
+
+static int xmp_listxattr(const char *path, char *list, size_t size)
+{
+ int res = llistxattr(path, list, size);
+ if (res == -1)
+ return -errno;
+ return res;
+}
+
+static int xmp_removexattr(const char *path, const char *name)
+{
+ int res = lremovexattr(path, name);
+ if (res == -1)
+ return -errno;
+ return 0;
+}
+#endif /* HAVE_SETXATTR */
+
+static int xmp_lock(const char *path, struct fuse_file_info *fi, int cmd,
+ struct flock *lock)
+{
+ (void) path;
+
+ return ulockmgr_op(fi->fh, cmd, lock, &fi->lock_owner,
+ sizeof(fi->lock_owner));
+}
+
+static int xmp_flock(const char *path, struct fuse_file_info *fi, int op)
+{
+ int res;
+ (void) path;
+
+ res = flock(fi->fh, op);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static struct fuse_operations xmp_oper = {
+ .getattr = xmp_getattr,
+ .fgetattr = xmp_fgetattr,
+ .access = xmp_access,
+ .readlink = xmp_readlink,
+ .opendir = xmp_opendir,
+ .readdir = xmp_readdir,
+ .releasedir = xmp_releasedir,
+ .mknod = xmp_mknod,
+ .mkdir = xmp_mkdir,
+ .symlink = xmp_symlink,
+ .unlink = xmp_unlink,
+ .rmdir = xmp_rmdir,
+ .rename = xmp_rename,
+ .link = xmp_link,
+ .chmod = xmp_chmod,
+ .chown = xmp_chown,
+ .truncate = xmp_truncate,
+ .ftruncate = xmp_ftruncate,
+#ifdef HAVE_UTIMENSAT
+ .utimens = xmp_utimens,
+#endif
+ .create = xmp_create,
+ .open = xmp_open,
+ .read = xmp_read,
+ .read_buf = xmp_read_buf,
+ .write = xmp_write,
+ .write_buf = xmp_write_buf,
+ .statfs = xmp_statfs,
+ .flush = xmp_flush,
+ .release = xmp_release,
+ .fsync = xmp_fsync,
+#ifdef HAVE_POSIX_FALLOCATE
+ .fallocate = xmp_fallocate,
+#endif
+#ifdef HAVE_SETXATTR
+ .setxattr = xmp_setxattr,
+ .getxattr = xmp_getxattr,
+ .listxattr = xmp_listxattr,
+ .removexattr = xmp_removexattr,
+#endif
+ .lock = xmp_lock,
+ .flock = xmp_flock,
+
+ .flag_nullpath_ok = 1,
+#if HAVE_UTIMENSAT
+ .flag_utime_omit_ok = 1,
+#endif
+};
+
+int main(int argc, char *argv[])
+{
+ umask(0);
+ return fuse_main(argc, argv, &xmp_oper, NULL);
+}
diff --git a/draft/fuse-examples/hello.c b/draft/fuse-examples/hello.c
new file mode 100644
index 0000000..bcb6b4c
--- /dev/null
+++ b/draft/fuse-examples/hello.c
@@ -0,0 +1,96 @@
+/*
+ FUSE: Filesystem in Userspace
+ Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
+
+ This program can be distributed under the terms of the GNU GPL.
+ See the file COPYING.
+
+ gcc -Wall hello.c `pkg-config fuse --cflags --libs` -o hello
+*/
+
+#define FUSE_USE_VERSION 26
+
+#include <fuse.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+
+static const char *hello_str = "Hello World!\n";
+static const char *hello_path = "/hello";
+
+static int hello_getattr(const char *path, struct stat *stbuf)
+{
+ int res = 0;
+
+ memset(stbuf, 0, sizeof(struct stat));
+ if (strcmp(path, "/") == 0) {
+ stbuf->st_mode = S_IFDIR | 0755;
+ stbuf->st_nlink = 2;
+ } else if (strcmp(path, hello_path) == 0) {
+ stbuf->st_mode = S_IFREG | 0444;
+ stbuf->st_nlink = 1;
+ stbuf->st_size = strlen(hello_str);
+ } else
+ res = -ENOENT;
+
+ return res;
+}
+
+static int hello_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
+ off_t offset, struct fuse_file_info *fi)
+{
+ (void) offset;
+ (void) fi;
+
+ if (strcmp(path, "/") != 0)
+ return -ENOENT;
+
+ filler(buf, ".", NULL, 0);
+ filler(buf, "..", NULL, 0);
+ filler(buf, hello_path + 1, NULL, 0);
+
+ return 0;
+}
+
+static int hello_open(const char *path, struct fuse_file_info *fi)
+{
+ if (strcmp(path, hello_path) != 0)
+ return -ENOENT;
+
+ if ((fi->flags & 3) != O_RDONLY)
+ return -EACCES;
+
+ return 0;
+}
+
+static int hello_read(const char *path, char *buf, size_t size, off_t offset,
+ struct fuse_file_info *fi)
+{
+ size_t len;
+ (void) fi;
+ if(strcmp(path, hello_path) != 0)
+ return -ENOENT;
+
+ len = strlen(hello_str);
+ if (offset < len) {
+ if (offset + size > len)
+ size = len - offset;
+ memcpy(buf, hello_str + offset, size);
+ } else
+ size = 0;
+
+ return size;
+}
+
+static struct fuse_operations hello_oper = {
+ .getattr = hello_getattr,
+ .readdir = hello_readdir,
+ .open = hello_open,
+ .read = hello_read,
+};
+
+int main(int argc, char *argv[])
+{
+ return fuse_main(argc, argv, &hello_oper, NULL);
+}
diff --git a/draft/fuse-examples/hello_ll.c b/draft/fuse-examples/hello_ll.c
new file mode 100644
index 0000000..1405441
--- /dev/null
+++ b/draft/fuse-examples/hello_ll.c
@@ -0,0 +1,181 @@
+/*
+ FUSE: Filesystem in Userspace
+ Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
+
+ This program can be distributed under the terms of the GNU GPL.
+ See the file COPYING.
+
+ gcc -Wall hello_ll.c `pkg-config fuse --cflags --libs` -o hello_ll
+*/
+
+#define FUSE_USE_VERSION 26
+
+#include <fuse_lowlevel.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <assert.h>
+
+static const char *hello_str = "Hello World!\n";
+static const char *hello_name = "hello";
+
+static int hello_stat(fuse_ino_t ino, struct stat *stbuf)
+{
+ stbuf->st_ino = ino;
+ switch (ino) {
+ case 1:
+ stbuf->st_mode = S_IFDIR | 0755;
+ stbuf->st_nlink = 2;
+ break;
+
+ case 2:
+ stbuf->st_mode = S_IFREG | 0444;
+ stbuf->st_nlink = 1;
+ stbuf->st_size = strlen(hello_str);
+ break;
+
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static void hello_ll_getattr(fuse_req_t req, fuse_ino_t ino,
+ struct fuse_file_info *fi)
+{
+ struct stat stbuf;
+
+ (void) fi;
+
+ memset(&stbuf, 0, sizeof(stbuf));
+ if (hello_stat(ino, &stbuf) == -1)
+ fuse_reply_err(req, ENOENT);
+ else
+ fuse_reply_attr(req, &stbuf, 1.0);
+}
+
+static void hello_ll_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
+{
+ struct fuse_entry_param e;
+
+ if (parent != 1 || strcmp(name, hello_name) != 0)
+ fuse_reply_err(req, ENOENT);
+ else {
+ memset(&e, 0, sizeof(e));
+ e.ino = 2;
+ e.attr_timeout = 1.0;
+ e.entry_timeout = 1.0;
+ hello_stat(e.ino, &e.attr);
+
+ fuse_reply_entry(req, &e);
+ }
+}
+
+struct dirbuf {
+ char *p;
+ size_t size;
+};
+
+static void dirbuf_add(fuse_req_t req, struct dirbuf *b, const char *name,
+ fuse_ino_t ino)
+{
+ struct stat stbuf;
+ size_t oldsize = b->size;
+ b->size += fuse_add_direntry(req, NULL, 0, name, NULL, 0);
+ b->p = (char *) realloc(b->p, b->size);
+ memset(&stbuf, 0, sizeof(stbuf));
+ stbuf.st_ino = ino;
+ fuse_add_direntry(req, b->p + oldsize, b->size - oldsize, name, &stbuf,
+ b->size);
+}
+
+#define min(x, y) ((x) < (y) ? (x) : (y))
+
+static int reply_buf_limited(fuse_req_t req, const char *buf, size_t bufsize,
+ off_t off, size_t maxsize)
+{
+ if (off < bufsize)
+ return fuse_reply_buf(req, buf + off,
+ min(bufsize - off, maxsize));
+ else
+ return fuse_reply_buf(req, NULL, 0);
+}
+
+static void hello_ll_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
+ off_t off, struct fuse_file_info *fi)
+{
+ (void) fi;
+
+ if (ino != 1)
+ fuse_reply_err(req, ENOTDIR);
+ else {
+ struct dirbuf b;
+
+ memset(&b, 0, sizeof(b));
+ dirbuf_add(req, &b, ".", 1);
+ dirbuf_add(req, &b, "..", 1);
+ dirbuf_add(req, &b, hello_name, 2);
+ reply_buf_limited(req, b.p, b.size, off, size);
+ free(b.p);
+ }
+}
+
+static void hello_ll_open(fuse_req_t req, fuse_ino_t ino,
+ struct fuse_file_info *fi)
+{
+ if (ino != 2)
+ fuse_reply_err(req, EISDIR);
+ else if ((fi->flags & 3) != O_RDONLY)
+ fuse_reply_err(req, EACCES);
+ else
+ fuse_reply_open(req, fi);
+}
+
+static void hello_ll_read(fuse_req_t req, fuse_ino_t ino, size_t size,
+ off_t off, struct fuse_file_info *fi)
+{
+ (void) fi;
+
+ assert(ino == 2);
+ reply_buf_limited(req, hello_str, strlen(hello_str), off, size);
+}
+
+static struct fuse_lowlevel_ops hello_ll_oper = {
+ .lookup = hello_ll_lookup,
+ .getattr = hello_ll_getattr,
+ .readdir = hello_ll_readdir,
+ .open = hello_ll_open,
+ .read = hello_ll_read,
+};
+
+int main(int argc, char *argv[])
+{
+ struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
+ struct fuse_chan *ch;
+ char *mountpoint;
+ int err = -1;
+
+ if (fuse_parse_cmdline(&args, &mountpoint, NULL, NULL) != -1 &&
+ (ch = fuse_mount(mountpoint, &args)) != NULL) {
+ struct fuse_session *se;
+
+ se = fuse_lowlevel_new(&args, &hello_ll_oper,
+ sizeof(hello_ll_oper), NULL);
+ if (se != NULL) {
+ if (fuse_set_signal_handlers(se) != -1) {
+ fuse_session_add_chan(se, ch);
+ err = fuse_session_loop(se);
+ fuse_remove_signal_handlers(se);
+ fuse_session_remove_chan(ch);
+ }
+ fuse_session_destroy(se);
+ }
+ fuse_unmount(mountpoint, ch);
+ }
+ fuse_opt_free_args(&args);
+
+ return err ? 1 : 0;
+}
diff --git a/draft/fuse-examples/null.c b/draft/fuse-examples/null.c
new file mode 100644
index 0000000..b72cf4d
--- /dev/null
+++ b/draft/fuse-examples/null.c
@@ -0,0 +1,95 @@
+/*
+ FUSE: Filesystem in Userspace
+ Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
+
+ This program can be distributed under the terms of the GNU GPL.
+ See the file COPYING.
+
+ gcc -Wall null.c `pkg-config fuse --cflags --libs` -o null
+*/
+
+#define FUSE_USE_VERSION 26
+
+#include <fuse.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+#include <errno.h>
+
+static int null_getattr(const char *path, struct stat *stbuf)
+{
+ if(strcmp(path, "/") != 0)
+ return -ENOENT;
+
+ stbuf->st_mode = S_IFREG | 0644;
+ stbuf->st_nlink = 1;
+ stbuf->st_uid = getuid();
+ stbuf->st_gid = getgid();
+ stbuf->st_size = (1ULL << 32); /* 4G */
+ stbuf->st_blocks = 0;
+ stbuf->st_atime = stbuf->st_mtime = stbuf->st_ctime = time(NULL);
+
+ return 0;
+}
+
+static int null_truncate(const char *path, off_t size)
+{
+ (void) size;
+
+ if(strcmp(path, "/") != 0)
+ return -ENOENT;
+
+ return 0;
+}
+
+static int null_open(const char *path, struct fuse_file_info *fi)
+{
+ (void) fi;
+
+ if(strcmp(path, "/") != 0)
+ return -ENOENT;
+
+ return 0;
+}
+
+static int null_read(const char *path, char *buf, size_t size,
+ off_t offset, struct fuse_file_info *fi)
+{
+ (void) buf;
+ (void) offset;
+ (void) fi;
+
+ if(strcmp(path, "/") != 0)
+ return -ENOENT;
+
+ if (offset >= (1ULL << 32))
+ return 0;
+
+ return size;
+}
+
+static int null_write(const char *path, const char *buf, size_t size,
+ off_t offset, struct fuse_file_info *fi)
+{
+ (void) buf;
+ (void) offset;
+ (void) fi;
+
+ if(strcmp(path, "/") != 0)
+ return -ENOENT;
+
+ return size;
+}
+
+static struct fuse_operations null_oper = {
+ .getattr = null_getattr,
+ .truncate = null_truncate,
+ .open = null_open,
+ .read = null_read,
+ .write = null_write,
+};
+
+int main(int argc, char *argv[])
+{
+ return fuse_main(argc, argv, &null_oper, NULL);
+}