aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorGravatar Miklos Szeredi <miklos@szeredi.hu>2009-06-18 11:19:14 +0000
committerGravatar Miklos Szeredi <miklos@szeredi.hu>2009-06-18 11:19:14 +0000
commit6ae8980d69849a257fe571e02cc6f8f1befb07cd (patch)
treec4ee7048ced48f475d19900118be923aa9c4dcdb /lib
parentae9bfde712697205ac8809edc431cb7c0bdd484f (diff)
CUSE patches from Tejun Heo (add new files)
Diffstat (limited to 'lib')
-rw-r--r--lib/cuse_lowlevel.c371
-rw-r--r--lib/fuse.c54
-rw-r--r--lib/fuse_i.h1
-rw-r--r--lib/fuse_lowlevel.c6
4 files changed, 427 insertions, 5 deletions
diff --git a/lib/cuse_lowlevel.c b/lib/cuse_lowlevel.c
new file mode 100644
index 0000000..128f87f
--- /dev/null
+++ b/lib/cuse_lowlevel.c
@@ -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;
+}
diff --git a/lib/fuse.c b/lib/fuse.c
index f7e78a8..9267ca6 100644
--- a/lib/fuse.c
+++ b/lib/fuse.c
@@ -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),
diff --git a/lib/fuse_i.h b/lib/fuse_i.h
index 9bc1d70..2d3790a 100644
--- a/lib/fuse_i.h
+++ b/lib/fuse_i.h
@@ -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;
diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c
index e3b5d5d..a60c5b8 100644
--- a/lib/fuse_lowlevel.c
+++ b/lib/fuse_lowlevel.c
@@ -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,