From: Miklos Szeredi Date: Thu, 18 Jun 2009 11:19:14 +0000 (+0000) Subject: CUSE patches from Tejun Heo (add new files) X-Git-Tag: fuse_2_8_0~10 X-Git-Url: http://git.maquefel.me/?a=commitdiff_plain;h=6ae8980d69849a257fe571e02cc6f8f1befb07cd;p=qemu-gpiodev%2Flibfuse.git CUSE patches from Tejun Heo (add new files) --- diff --git a/example/cusexmp.c b/example/cusexmp.c new file mode 100644 index 0000000..48ccf8c --- /dev/null +++ b/example/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 + + 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 +#include +#include +#include +#include +#include +#include +#include + +#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, ¶m, cusexmp_opts, cusexmp_process_arg)) { + printf("failed to parse option\n"); + return 1; + } + + if (!param.is_help) { + if (!param.dev_name) { + fprintf(stderr, "Error: device name missing\n"); + return 1; + } + strncat(dev_name, param.dev_name, sizeof(dev_name) - 9); + } + + memset(&ci, 0, sizeof(ci)); + ci.dev_major = param.major; + ci.dev_minor = param.minor; + ci.dev_info_argc = 1; + ci.dev_info_argv = dev_info_argv; + ci.flags = CUSE_UNRESTRICTED_IOCTL; + + return cuse_lowlevel_main(args.argc, args.argv, &ci, &cusexmp_clop, + NULL); +} diff --git a/include/cuse_lowlevel.h b/include/cuse_lowlevel.h new file mode 100644 index 0000000..e147fa2 --- /dev/null +++ b/include/cuse_lowlevel.h @@ -0,0 +1,87 @@ +/* + CUSE: Character device in Userspace + Copyright (C) 2008-2009 SUSE Linux Products GmbH + Copyright (C) 2008-2009 Tejun Heo + + This program can be distributed under the terms of the GNU 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 +#include +#include + +#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 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 + + 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 +#include +#include +#include +#include +#include + +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,