CUSE patches from Tejun Heo (add new files)
authorMiklos Szeredi <miklos@szeredi.hu>
Thu, 18 Jun 2009 11:19:14 +0000 (11:19 +0000)
committerMiklos Szeredi <miklos@szeredi.hu>
Thu, 18 Jun 2009 11:19:14 +0000 (11:19 +0000)
example/cusexmp.c [new file with mode: 0644]
include/cuse_lowlevel.h [new file with mode: 0644]
lib/cuse_lowlevel.c [new file with mode: 0644]
lib/fuse.c
lib/fuse_i.h
lib/fuse_lowlevel.c

diff --git a/example/cusexmp.c b/example/cusexmp.c
new file mode 100644 (file)
index 0000000..48ccf8c
--- /dev/null
@@ -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 `pkg-config fuse --cflags --libs` cusexmp.c -o cusexmp
+*/
+
+#define FUSE_USE_VERSION 29
+
+#include <cuse_lowlevel.h>
+#include <fuse_opt.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "fioc.h"
+
+static void *cusexmp_buf;
+static size_t cusexmp_size;
+
+static const char *usage =
+"usage: cusexmp [options]\n"
+"\n"
+"options:\n"
+"    --help|-h             print this help message\n"
+"    --maj=MAJ|-M MAJ      device major number\n"
+"    --min=MIN|-m MIN      device minor number\n"
+"    --name=NAME|-n NAME   device name (mandatory)\n"
+"\n";
+
+static int cusexmp_resize(size_t new_size)
+{
+       void *new_buf;
+
+       if (new_size == cusexmp_size)
+               return 0;
+
+       new_buf = realloc(cusexmp_buf, new_size);
+       if (!new_buf && new_size)
+               return -ENOMEM;
+
+       if (new_size > cusexmp_size)
+               memset(new_buf + cusexmp_size, 0, new_size - cusexmp_size);
+
+       cusexmp_buf = new_buf;
+       cusexmp_size = new_size;
+
+       return 0;
+}
+
+static int cusexmp_expand(size_t new_size)
+{
+       if (new_size > cusexmp_size)
+               return cusexmp_resize(new_size);
+       return 0;
+}
+
+static void cusexmp_open(fuse_req_t req, struct fuse_file_info *fi)
+{
+       fuse_reply_open(req, fi);
+}
+
+static void cusexmp_read(fuse_req_t req, size_t size, off_t off,
+                        struct fuse_file_info *fi)
+{
+       (void)fi;
+
+       if (off >= cusexmp_size)
+               off = cusexmp_size;
+       if (size > cusexmp_size - off)
+               size = cusexmp_size - off;
+
+       fuse_reply_buf(req, cusexmp_buf + off, size);
+}
+
+static void cusexmp_write(fuse_req_t req, const char *buf, size_t size,
+                         off_t off, struct fuse_file_info *fi)
+{
+       (void)fi;
+
+       if (cusexmp_expand(off + size)) {
+               fuse_reply_err(req, ENOMEM);
+               return;
+       }
+
+       memcpy(cusexmp_buf + off, buf, size);
+       fuse_reply_write(req, size);
+}
+
+static void fioc_do_rw(fuse_req_t req, void *addr, const void *in_buf,
+                      size_t in_bufsz, size_t out_bufsz, int is_read)
+{
+       const struct fioc_rw_arg *arg;
+       struct iovec in_iov[2], out_iov[3], iov[3];
+       size_t cur_size;
+
+       /* read in arg */
+       in_iov[0].iov_base = addr;
+       in_iov[0].iov_len = sizeof(*arg);
+       if (!in_bufsz) {
+               fuse_reply_ioctl_retry(req, in_iov, 1, NULL, 0);
+               return;
+       }
+       arg = in_buf;
+       in_buf += sizeof(*arg);
+       in_bufsz -= sizeof(*arg);
+
+       /* prepare size outputs */
+       out_iov[0].iov_base =
+               addr + (unsigned long)&(((struct fioc_rw_arg *)0)->prev_size);
+       out_iov[0].iov_len = sizeof(arg->prev_size);
+
+       out_iov[1].iov_base =
+               addr + (unsigned long)&(((struct fioc_rw_arg *)0)->new_size);
+       out_iov[1].iov_len = sizeof(arg->new_size);
+
+       /* prepare client buf */
+       if (is_read) {
+               out_iov[2].iov_base = arg->buf;
+               out_iov[2].iov_len = arg->size;
+               if (!out_bufsz) {
+                       fuse_reply_ioctl_retry(req, in_iov, 1, out_iov, 3);
+                       return;
+               }
+       } else {
+               in_iov[1].iov_base = arg->buf;
+               in_iov[1].iov_len = arg->size;
+               if (arg->size && !in_bufsz) {
+                       fuse_reply_ioctl_retry(req, in_iov, 2, out_iov, 2);
+                       return;
+               }
+       }
+
+       /* we're all set */
+       cur_size = cusexmp_size;
+       iov[0].iov_base = &cur_size;
+       iov[0].iov_len = sizeof(cur_size);
+
+       iov[1].iov_base = &cusexmp_size;
+       iov[1].iov_len = sizeof(cusexmp_size);
+
+       if (is_read) {
+               size_t off = arg->offset;
+               size_t size = arg->size;
+
+               if (off >= cusexmp_size)
+                       off = cusexmp_size;
+               if (size > cusexmp_size - off)
+                       size = cusexmp_size - off;
+
+               iov[2].iov_base = cusexmp_buf + off;
+               iov[2].iov_len = size;
+               fuse_reply_ioctl_iov(req, size, iov, 3);
+       } else {
+               if (cusexmp_expand(arg->offset + in_bufsz)) {
+                       fuse_reply_err(req, ENOMEM);
+                       return;
+               }
+
+               memcpy(cusexmp_buf + arg->offset, in_buf, in_bufsz);
+               fuse_reply_ioctl_iov(req, in_bufsz, iov, 2);
+       }
+}
+
+static void cusexmp_ioctl(fuse_req_t req, int cmd, void *arg,
+                         struct fuse_file_info *fi, unsigned flags,
+                         const void *in_buf, size_t in_bufsz, size_t out_bufsz)
+{
+       int is_read = 0;
+
+       (void)fi;
+
+       if (flags & FUSE_IOCTL_COMPAT) {
+               fuse_reply_err(req, ENOSYS);
+               return;
+       }
+
+       switch (cmd) {
+       case FIOC_GET_SIZE:
+               if (!out_bufsz) {
+                       struct iovec iov = { arg, sizeof(size_t) };
+
+                       fuse_reply_ioctl_retry(req, NULL, 0, &iov, 1);
+               } else
+                       fuse_reply_ioctl(req, 0, &cusexmp_size,
+                                        sizeof(cusexmp_size));
+               break;
+
+       case FIOC_SET_SIZE:
+               if (!in_bufsz) {
+                       struct iovec iov = { arg, sizeof(size_t) };
+
+                       fuse_reply_ioctl_retry(req, &iov, 1, NULL, 0);
+               } else {
+                       cusexmp_resize(*(size_t *)in_buf);
+                       fuse_reply_ioctl(req, 0, NULL, 0);
+               }
+               break;
+
+       case FIOC_READ:
+               is_read = 1;
+       case FIOC_WRITE:
+               fioc_do_rw(req, arg, in_buf, in_bufsz, out_bufsz, is_read);
+               break;
+
+       default:
+               fuse_reply_err(req, EINVAL);
+       }
+}
+
+struct cusexmp_param {
+       unsigned                major;
+       unsigned                minor;
+       char                    *dev_name;
+       int                     is_help;
+};
+
+#define CUSEXMP_OPT(t, p) { t, offsetof(struct cusexmp_param, p), 1 }
+
+static const struct fuse_opt cusexmp_opts[] = {
+       CUSEXMP_OPT("-M %u",            major),
+       CUSEXMP_OPT("--maj=%u",         major),
+       CUSEXMP_OPT("-m %u",            minor),
+       CUSEXMP_OPT("--min=%u",         minor),
+       CUSEXMP_OPT("-n %s",            dev_name),
+       CUSEXMP_OPT("--name=%s",        dev_name),
+       FUSE_OPT_KEY("-h",              0),
+       FUSE_OPT_KEY("--help",          0),
+       FUSE_OPT_END
+};
+
+static int cusexmp_process_arg(void *data, const char *arg, int key,
+                              struct fuse_args *outargs)
+{
+       struct cusexmp_param *param = data;
+
+       (void)outargs;
+       (void)arg;
+
+       switch (key) {
+       case 0:
+               param->is_help = 1;
+               fprintf(stderr, usage);
+               return fuse_opt_add_arg(outargs, "-ho");
+       default:
+               return 1;
+       }
+}
+
+static const struct cuse_lowlevel_ops cusexmp_clop = {
+       .open           = cusexmp_open,
+       .read           = cusexmp_read,
+       .write          = cusexmp_write,
+       .ioctl          = cusexmp_ioctl,
+};
+
+int main(int argc, char **argv)
+{
+       struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
+       struct cusexmp_param param = { 0, 0, NULL, 0 };
+       char dev_name[128] = "DEVNAME=";
+       const char *dev_info_argv[] = { dev_name };
+       struct cuse_info ci;
+
+       if (fuse_opt_parse(&args, &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/include/cuse_lowlevel.h b/include/cuse_lowlevel.h
new file mode 100644 (file)
index 0000000..e147fa2
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+  CUSE: 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 LGPLv2.
+  See the file COPYING.LIB.
+
+  Read example/cusexmp.c for usages.
+*/
+
+#ifndef _CUSE_LOWLEVEL_H_
+#define _CUSE_LOWLEVEL_H_
+
+#ifndef FUSE_USE_VERSION
+#define FUSE_USE_VERSION 29
+#endif
+
+#include "fuse_lowlevel.h"
+
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define CUSE_UNRESTRICTED_IOCTL                (1 << 0) /* use unrestricted ioctl */
+
+struct fuse_session;
+
+struct cuse_info {
+       unsigned        dev_major;
+       unsigned        dev_minor;
+       unsigned        dev_info_argc;
+       const char      **dev_info_argv;
+       unsigned        flags;
+};
+
+/*
+ * Most ops behave almost identically to the matching fuse_lowlevel
+ * ops except that they don't take @ino.
+ *
+ * init_done   : called after initialization is complete
+ * read/write  : always direct IO, simultaneous operations allowed
+ * ioctl       : might be in unrestricted mode depending on ci->flags
+ */
+struct cuse_lowlevel_ops {
+       void (*init) (void *userdata, struct fuse_conn_info *conn);
+       void (*init_done) (void *userdata);
+       void (*destroy) (void *userdata);
+       void (*open) (fuse_req_t req, struct fuse_file_info *fi);
+       void (*read) (fuse_req_t req, size_t size, off_t off,
+                     struct fuse_file_info *fi);
+       void (*write) (fuse_req_t req, const char *buf, size_t size, off_t off,
+                      struct fuse_file_info *fi);
+       void (*flush) (fuse_req_t req, struct fuse_file_info *fi);
+       void (*release) (fuse_req_t req, struct fuse_file_info *fi);
+       void (*fsync) (fuse_req_t req, int datasync, struct fuse_file_info *fi);
+       void (*ioctl) (fuse_req_t req, int cmd, void *arg,
+                      struct fuse_file_info *fi, unsigned int flags,
+                      const void *in_buf, size_t in_bufsz, size_t out_bufsz);
+       void (*poll) (fuse_req_t req, struct fuse_file_info *fi,
+                     struct fuse_pollhandle *ph);
+};
+
+struct fuse_session *cuse_lowlevel_new(struct fuse_args *args,
+                                      const struct cuse_info *ci,
+                                      const struct cuse_lowlevel_ops *clop,
+                                      void *userdata);
+
+struct fuse_session *cuse_lowlevel_setup(int argc, char *argv[],
+                                        const struct cuse_info *ci,
+                                        const struct cuse_lowlevel_ops *clop,
+                                        int *multithreaded, void *userdata);
+
+void cuse_lowlevel_teardown(struct fuse_session *se);
+
+int cuse_lowlevel_main(int argc, char *argv[], const struct cuse_info *ci,
+                      const struct cuse_lowlevel_ops *clop, void *userdata);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _CUSE_LOWLEVEL_H_ */
diff --git a/lib/cuse_lowlevel.c b/lib/cuse_lowlevel.c
new file mode 100644 (file)
index 0000000..128f87f
--- /dev/null
@@ -0,0 +1,371 @@
+/*
+  CUSE: Character device in Userspace
+  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 LGPLv2.
+  See the file COPYING.LIB.
+*/
+
+#include "cuse_lowlevel.h"
+#include "fuse_kernel.h"
+#include "fuse_i.h"
+#include "fuse_opt.h"
+#include "fuse_misc.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <errno.h>
+#include <unistd.h>
+
+struct cuse_data {
+       struct cuse_lowlevel_ops        clop;
+       unsigned                        max_read;
+       unsigned                        dev_major;
+       unsigned                        dev_minor;
+       unsigned                        flags;
+       unsigned                        dev_info_len;
+       char                            dev_info[];
+};
+
+static struct cuse_lowlevel_ops *req_clop(fuse_req_t req)
+{
+       return &req->f->cuse_data->clop;
+}
+
+static void cuse_fll_open(fuse_req_t req, fuse_ino_t ino,
+                         struct fuse_file_info *fi)
+{
+       (void)ino;
+       req_clop(req)->open(req, fi);
+}
+
+static void cuse_fll_read(fuse_req_t req, fuse_ino_t ino, size_t size,
+                         off_t off, struct fuse_file_info *fi)
+{
+       (void)ino;
+       req_clop(req)->read(req, size, off, fi);
+}
+
+static void cuse_fll_write(fuse_req_t req, fuse_ino_t ino, const char *buf,
+                          size_t size, off_t off, struct fuse_file_info *fi)
+{
+       (void)ino;
+       req_clop(req)->write(req, buf, size, off, fi);
+}
+
+static void cuse_fll_flush(fuse_req_t req, fuse_ino_t ino,
+                          struct fuse_file_info *fi)
+{
+       (void)ino;
+       req_clop(req)->flush(req, fi);
+}
+
+static void cuse_fll_release(fuse_req_t req, fuse_ino_t ino,
+                            struct fuse_file_info *fi)
+{
+       (void)ino;
+       req_clop(req)->release(req, fi);
+}
+
+static void cuse_fll_fsync(fuse_req_t req, fuse_ino_t ino, int datasync,
+                          struct fuse_file_info *fi)
+{
+       (void)ino;
+       req_clop(req)->fsync(req, datasync, fi);
+}
+
+static void cuse_fll_ioctl(fuse_req_t req, fuse_ino_t ino, int cmd, void *arg,
+                      struct fuse_file_info *fi, unsigned int flags,
+                      const void *in_buf, size_t in_bufsz, size_t out_bufsz)
+{
+       (void)ino;
+       req_clop(req)->ioctl(req, cmd, arg, fi, flags, in_buf, in_bufsz,
+                            out_bufsz);
+}
+
+static void cuse_fll_poll(fuse_req_t req, fuse_ino_t ino,
+                         struct fuse_file_info *fi, struct fuse_pollhandle *ph)
+{
+       (void)ino;
+       req_clop(req)->poll(req, fi, ph);
+}
+
+static size_t cuse_pack_info(int argc, const char **argv, char *buf)
+{
+       size_t size = 0;
+       int i;
+
+       for (i = 0; i < argc; i++) {
+               size_t len;
+
+               len = strlen(argv[i]) + 1;
+               size += len;
+               if (buf) {
+                       memcpy(buf, argv[i], len);
+                       buf += len;
+               }
+       }
+
+       return size;
+}
+
+static struct cuse_data *cuse_prep_data(const struct cuse_info *ci,
+                                       const struct cuse_lowlevel_ops *clop)
+{
+       struct cuse_data *cd;
+       size_t dev_info_len;
+
+       dev_info_len = cuse_pack_info(ci->dev_info_argc, ci->dev_info_argv,
+                                     NULL);
+
+       if (dev_info_len > CUSE_INIT_INFO_MAX) {
+               fprintf(stderr, "cuse: dev_info (%zu) too large, limit=%u\n",
+                       dev_info_len, CUSE_INIT_INFO_MAX);
+               return NULL;
+       }
+
+       cd = calloc(1, sizeof(*cd) + dev_info_len);
+       if (!cd) {
+               fprintf(stderr, "cuse: failed to allocate cuse_data\n");
+               return NULL;
+       }
+
+       memcpy(&cd->clop, clop, sizeof(cd->clop));
+       cd->max_read = 131072;
+       cd->dev_major = ci->dev_major;
+       cd->dev_minor = ci->dev_minor;
+       cd->dev_info_len = dev_info_len;
+       cd->flags = ci->flags;
+       cuse_pack_info(ci->dev_info_argc, ci->dev_info_argv, cd->dev_info);
+
+       return cd;
+}
+
+struct fuse_session *cuse_lowlevel_new(struct fuse_args *args,
+                                      const struct cuse_info *ci,
+                                      const struct cuse_lowlevel_ops *clop,
+                                      void *userdata)
+{
+       struct fuse_lowlevel_ops lop;
+       struct cuse_data *cd;
+       struct fuse_session *se;
+       struct fuse_ll *ll;
+
+       cd = cuse_prep_data(ci, clop);
+       if (!cd)
+               return NULL;
+
+       memset(&lop, 0, sizeof(lop));
+       lop.init        = clop->init;
+       lop.destroy     = clop->destroy;
+       lop.open        = clop->open            ? cuse_fll_open         : NULL;
+       lop.read        = clop->read            ? cuse_fll_read         : NULL;
+       lop.write       = clop->write           ? cuse_fll_write        : NULL;
+       lop.flush       = clop->flush           ? cuse_fll_flush        : NULL;
+       lop.release     = clop->release         ? cuse_fll_release      : NULL;
+       lop.fsync       = clop->fsync           ? cuse_fll_fsync        : NULL;
+       lop.ioctl       = clop->ioctl           ? cuse_fll_ioctl        : NULL;
+       lop.poll        = clop->poll            ? cuse_fll_poll         : NULL;
+
+       se = fuse_lowlevel_new_common(args, &lop, sizeof(lop), userdata);
+       if (!se) {
+               free(cd);
+               return NULL;
+       }
+       ll = se->data;
+       ll->cuse_data = cd;
+
+       return se;
+}
+
+static int cuse_reply_init(fuse_req_t req, struct cuse_init_out *arg,
+                          char *dev_info, unsigned dev_info_len)
+{
+       struct iovec iov[3];
+
+       iov[1].iov_base = arg;
+       iov[1].iov_len = sizeof(struct cuse_init_out);
+       iov[2].iov_base = dev_info;
+       iov[2].iov_len = dev_info_len;
+
+       return send_reply_iov_nofree(req, 0, iov, 3);
+}
+
+void do_cuse_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+       struct fuse_init_in *arg = (struct fuse_init_in *) inarg;
+       struct cuse_init_out outarg;
+       struct fuse_ll *f = req->f;
+       struct cuse_data *cd = f->cuse_data;
+       size_t bufsize = fuse_chan_bufsize(req->ch);
+       struct cuse_lowlevel_ops *clop = req_clop(req);
+
+       (void) nodeid;
+       if (f->debug) {
+               fprintf(stderr, "CUSE_INIT: %u.%u\n", arg->major, arg->minor);
+               fprintf(stderr, "flags=0x%08x\n", arg->flags);
+       }
+       f->conn.proto_major = arg->major;
+       f->conn.proto_minor = arg->minor;
+       f->conn.capable = 0;
+       f->conn.want = 0;
+
+       if (arg->major < 7) {
+               fprintf(stderr, "fuse: unsupported protocol version: %u.%u\n",
+                       arg->major, arg->minor);
+               fuse_reply_err(req, EPROTO);
+               return;
+       }
+
+       if (bufsize < FUSE_MIN_READ_BUFFER) {
+               fprintf(stderr, "fuse: warning: buffer size too small: %zu\n",
+                       bufsize);
+               bufsize = FUSE_MIN_READ_BUFFER;
+       }
+
+       bufsize -= 4096;
+       if (bufsize < f->conn.max_write)
+               f->conn.max_write = bufsize;
+
+       f->got_init = 1;
+       if (f->op.init)
+               f->op.init(f->userdata, &f->conn);
+
+       memset(&outarg, 0, sizeof(outarg));
+       outarg.major = FUSE_KERNEL_VERSION;
+       outarg.minor = FUSE_KERNEL_MINOR_VERSION;
+       outarg.flags = cd->flags;
+       outarg.max_read = cd->max_read;
+       outarg.max_write = f->conn.max_write;
+       outarg.dev_major = cd->dev_major;
+       outarg.dev_minor = cd->dev_minor;
+
+       if (f->debug) {
+               fprintf(stderr, "   CUSE_INIT: %u.%u\n",
+                       outarg.major, outarg.minor);
+               fprintf(stderr, "   flags=0x%08x\n", outarg.flags);
+               fprintf(stderr, "   max_read=0x%08x\n", outarg.max_read);
+               fprintf(stderr, "   max_write=0x%08x\n", outarg.max_write);
+               fprintf(stderr, "   dev_major=%u\n", outarg.dev_major);
+               fprintf(stderr, "   dev_minor=%u\n", outarg.dev_minor);
+               fprintf(stderr, "   dev_info: %.*s\n", cd->dev_info_len,
+                       cd->dev_info);
+       }
+
+       cuse_reply_init(req, &outarg, cd->dev_info, cd->dev_info_len);
+
+       if (clop->init_done)
+               clop->init_done(f->userdata);
+
+       free_req(req);
+}
+
+struct fuse_session *cuse_lowlevel_setup(int argc, char *argv[],
+                                        const struct cuse_info *ci,
+                                        const struct cuse_lowlevel_ops *clop,
+                                        int *multithreaded, void *userdata)
+{
+       const char *devname = "/dev/cuse";
+       static const struct fuse_opt kill_subtype_opts[] = {
+               FUSE_OPT_KEY("subtype=",  FUSE_OPT_KEY_DISCARD),
+               FUSE_OPT_END
+       };
+       struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
+       struct fuse_session *se;
+       struct fuse_chan *ch;
+       int fd;
+       int foreground;
+       int res;
+
+       res = fuse_parse_cmdline(&args, NULL, multithreaded, &foreground);
+       if (res == -1)
+               goto err_args;
+
+       res = fuse_opt_parse(&args, NULL, kill_subtype_opts, NULL);
+       if (res == -1)
+               goto err_args;
+
+       /*
+        * Make sure file descriptors 0, 1 and 2 are open, otherwise chaos
+        * would ensue.
+        */
+       do {
+               fd = open("/dev/null", O_RDWR);
+               if (fd > 2)
+                       close(fd);
+       } while (fd >= 0 && fd <= 2);
+
+       se = cuse_lowlevel_new(&args, ci, clop, userdata);
+       fuse_opt_free_args(&args);
+       if (se == NULL)
+               goto err_args;
+
+       fd = open(devname, O_RDWR);
+       if (fd == -1) {
+               if (errno == ENODEV || errno == ENOENT)
+                       fprintf(stderr, "fuse: device not found, try 'modprobe cuse' first\n");
+               else
+                       fprintf(stderr, "fuse: failed to open %s: %s\n",
+                               devname, strerror(errno));
+               goto err_se;
+       }
+
+       ch = fuse_kern_chan_new(fd);
+       if (!ch) {
+               close(fd);
+               goto err_se;
+       }
+
+       fuse_session_add_chan(se, ch);
+
+       res = fuse_set_signal_handlers(se);
+       if (res == -1)
+               goto err_se;
+
+       res = fuse_daemonize(foreground);
+       if (res == -1)
+               goto err_sig;
+
+       return se;
+
+err_sig:
+       fuse_remove_signal_handlers(se);
+err_se:
+       fuse_session_destroy(se);
+err_args:
+       fuse_opt_free_args(&args);
+       return NULL;
+}
+
+void cuse_lowlevel_teardown(struct fuse_session *se)
+{
+       fuse_remove_signal_handlers(se);
+       fuse_session_destroy(se);
+}
+
+int cuse_lowlevel_main(int argc, char *argv[], const struct cuse_info *ci,
+                      const struct cuse_lowlevel_ops *clop, void *userdata)
+{
+       struct fuse_session *se;
+       int multithreaded;
+       int res;
+
+       se = cuse_lowlevel_setup(argc, argv, ci, clop, &multithreaded,
+                                userdata);
+       if (se == NULL)
+               return 1;
+
+       if (multithreaded)
+               res = fuse_session_loop_mt(se);
+       else
+               res = fuse_session_loop(se);
+
+       cuse_lowlevel_teardown(se);
+       if (res == -1)
+               return 1;
+
+       return 0;
+}
index f7e78a8ea3d5a1b9bab3b8c66a1f6a25a81dc059..9267ca68b1d5b5778d956b558430d701876e8432 100644 (file)
@@ -48,6 +48,7 @@ struct fuse_config {
        double attr_timeout;
        double ac_attr_timeout;
        int ac_attr_timeout_set;
+       int noforget;
        int debug;
        int hard_remove;
        int use_ino;
@@ -409,12 +410,17 @@ static struct node *find_node(struct fuse *f, fuse_ino_t parent,
        struct node *node;
 
        pthread_mutex_lock(&f->lock);
-       node = lookup_node(f, parent, name);
+       if (!name)
+               node = get_node(f, parent);
+       else
+               node = lookup_node(f, parent, name);
        if (node == NULL) {
                node = (struct node *) calloc(1, sizeof(struct node));
                if (node == NULL)
                        goto out_err;
 
+               if (f->conf.noforget)
+                       node->nlookup = 1;
                node->refctr = 1;
                node->nodeid = next_id(f);
                node->generation = f->generation;
@@ -807,6 +813,15 @@ static void forget_node(struct fuse *f, fuse_ino_t nodeid, uint64_t nlookup)
        pthread_mutex_unlock(&f->lock);
 }
 
+static void unlink_node(struct fuse *f, struct node *node)
+{
+       if (f->conf.noforget) {
+               assert(node->nlookup > 1);
+               node->nlookup--;
+       }
+       unhash_name(f, node);
+}
+
 static void remove_node(struct fuse *f, fuse_ino_t dir, const char *name)
 {
        struct node *node;
@@ -814,7 +829,7 @@ static void remove_node(struct fuse *f, fuse_ino_t dir, const char *name)
        pthread_mutex_lock(&f->lock);
        node = lookup_node(f, dir, name);
        if (node != NULL)
-               unhash_name(f, node);
+               unlink_node(f, node);
        pthread_mutex_unlock(&f->lock);
 }
 
@@ -837,7 +852,7 @@ static int rename_node(struct fuse *f, fuse_ino_t olddir, const char *oldname,
                        err = -EBUSY;
                        goto out;
                }
-               unhash_name(f, newnode);
+               unlink_node(f, newnode);
        }
 
        unhash_name(f, node);
@@ -1931,6 +1946,7 @@ static void fuse_lib_init(void *data, struct fuse_conn_info *conn)
 
        memset(c, 0, sizeof(*c));
        c->ctx.fuse = f;
+       conn->want |= FUSE_CAP_EXPORT_SUPPORT;
        fuse_fs_init(f->fs, conn);
 }
 
@@ -1962,6 +1978,32 @@ static void fuse_lib_lookup(fuse_req_t req, fuse_ino_t parent,
        struct fuse_entry_param e;
        char *path;
        int err;
+       struct node *dot = NULL;
+
+       if (name[0] == '.') {
+               int len = strlen(name);
+
+               if (len == 1 || (name[1] == '.' && len == 2)) {
+                       pthread_mutex_lock(&f->lock);
+                       if (len == 1) {
+                               if (f->conf.debug)
+                                       fprintf(stderr, "LOOKUP-DOT\n");
+                               dot = get_node_nocheck(f, parent);
+                               if (dot == NULL) {
+                                       pthread_mutex_unlock(&f->lock);
+                                       reply_entry(req, &e, -ESTALE);
+                                       return;
+                               }
+                               dot->refctr++;
+                       } else {
+                               if (f->conf.debug)
+                                       fprintf(stderr, "LOOKUP-DOTDOT\n");
+                               parent = get_node(f, parent)->parent->nodeid;
+                       }
+                       pthread_mutex_unlock(&f->lock);
+                       name = NULL;
+               }
+       }
 
        err = get_path_name(f, parent, name, &path);
        if (!err) {
@@ -1978,6 +2020,11 @@ static void fuse_lib_lookup(fuse_req_t req, fuse_ino_t parent,
                fuse_finish_interrupt(f, req, &d);
                free_path(f, parent, path);
        }
+       if (dot) {
+               pthread_mutex_lock(&f->lock);
+               unref_node(f, dot);
+               pthread_mutex_unlock(&f->lock);
+       }
        reply_entry(req, &e, err);
 }
 
@@ -3445,6 +3492,7 @@ static const struct fuse_opt fuse_lib_opts[] = {
        FUSE_LIB_OPT("ac_attr_timeout=%lf",   ac_attr_timeout, 0),
        FUSE_LIB_OPT("ac_attr_timeout=",      ac_attr_timeout_set, 1),
        FUSE_LIB_OPT("negative_timeout=%lf",  negative_timeout, 0),
+       FUSE_LIB_OPT("noforget",              noforget, 1),
        FUSE_LIB_OPT("intr",                  intr, 1),
        FUSE_LIB_OPT("intr_signal=%d",        intr_signal, 0),
        FUSE_LIB_OPT("modules=%s",            modules, 0),
index 9bc1d7065103a4fda65976fd1e5d4772c77b6996..2d3790a3bf219f91b70e316a581a46ce3b57e1e7 100644 (file)
@@ -47,6 +47,7 @@ struct fuse_ll {
        int debug;
        int allow_root;
        int atomic_o_trunc;
+       int no_remote_lock;
        int big_writes;
        struct fuse_lowlevel_ops op;
        int got_init;
index e3b5d5d35bf2f0f985b7b6372b75603a938859e9..a60c5b89a90a056e0d360b5fb39e6ae46dd96638 100644 (file)
@@ -1175,7 +1175,7 @@ static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
 
        if (f->atomic_o_trunc)
                f->conn.want |= FUSE_CAP_ATOMIC_O_TRUNC;
-       if (f->op.getlk && f->op.setlk)
+       if (f->op.getlk && f->op.setlk && !f->no_remote_lock)
                f->conn.want |= FUSE_CAP_POSIX_LOCKS;
        if (f->big_writes)
                f->conn.want |= FUSE_CAP_BIG_WRITES;
@@ -1442,6 +1442,7 @@ static struct fuse_opt fuse_ll_opts[] = {
        { "async_read", offsetof(struct fuse_ll, conn.async_read), 1 },
        { "sync_read", offsetof(struct fuse_ll, conn.async_read), 0 },
        { "atomic_o_trunc", offsetof(struct fuse_ll, atomic_o_trunc), 1},
+       { "no_remote_lock", offsetof(struct fuse_ll, no_remote_lock), 1},
        { "big_writes", offsetof(struct fuse_ll, big_writes), 1},
        FUSE_OPT_KEY("max_read=", FUSE_OPT_KEY_DISCARD),
        FUSE_OPT_KEY("-h", KEY_HELP),
@@ -1465,7 +1466,8 @@ static void fuse_ll_help(void)
 "    -o async_read          perform reads asynchronously (default)\n"
 "    -o sync_read           perform reads synchronously\n"
 "    -o atomic_o_trunc      enable atomic open+truncate support\n"
-"    -o big_writes          enable larger than 4kB writes\n");
+"    -o big_writes          enable larger than 4kB writes\n"
+"    -o no_remote_lock      disable remote file locking\n");
 }
 
 static int fuse_ll_opt_proc(void *data, const char *arg, int key,