summaryrefslogtreecommitdiffstats
path: root/fuse
diff options
context:
space:
mode:
Diffstat (limited to 'fuse')
-rw-r--r--fuse/Android.mk66
-rw-r--r--fuse/README12
-rw-r--r--fuse/buffer.c318
-rw-r--r--fuse/cuse_lowlevel.c371
-rw-r--r--fuse/fuse.c4886
-rw-r--r--fuse/fuse_i.h128
-rw-r--r--fuse/fuse_kern_chan.c95
-rw-r--r--fuse/fuse_loop.c46
-rw-r--r--fuse/fuse_loop_mt.c262
-rw-r--r--fuse/fuse_lowlevel.c2967
-rw-r--r--fuse/fuse_misc.h57
-rw-r--r--fuse/fuse_mt.c122
-rw-r--r--fuse/fuse_opt.c426
-rw-r--r--fuse/fuse_session.c233
-rw-r--r--fuse/fuse_signals.c72
-rw-r--r--fuse/fusexmp.c385
-rw-r--r--fuse/helper.c478
-rw-r--r--fuse/include/Makefile515
-rw-r--r--fuse/include/Makefile.am17
-rw-r--r--fuse/include/Makefile.in515
-rw-r--r--fuse/include/config.h87
-rw-r--r--fuse/include/config.h.in86
-rw-r--r--fuse/include/cuse_lowlevel.h87
-rw-r--r--fuse/include/fuse.h1059
-rw-r--r--fuse/include/fuse_common.h505
-rw-r--r--fuse/include/fuse_common_compat.h26
-rw-r--r--fuse/include/fuse_compat.h201
-rw-r--r--fuse/include/fuse_kernel.h691
-rw-r--r--fuse/include/fuse_lowlevel.h1837
-rw-r--r--fuse/include/fuse_lowlevel_compat.h155
-rw-r--r--fuse/include/fuse_opt.h270
-rw-r--r--fuse/include/old/fuse.h9
-rw-r--r--fuse/include/stamp-h11
-rw-r--r--fuse/include/sys/statvfs.h18
-rw-r--r--fuse/include/ulockmgr.h24
-rw-r--r--fuse/mount.c638
-rw-r--r--fuse/mount_bsd.c388
-rw-r--r--fuse/mount_util.c350
-rw-r--r--fuse/mount_util.h19
-rw-r--r--fuse/ulockmgr.c444
40 files changed, 18866 insertions, 0 deletions
diff --git a/fuse/Android.mk b/fuse/Android.mk
new file mode 100644
index 000000000..3bd16161f
--- /dev/null
+++ b/fuse/Android.mk
@@ -0,0 +1,66 @@
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ buffer.c \
+ cuse_lowlevel.c \
+ fuse.c \
+ fuse_kern_chan.c \
+ fuse_loop.c \
+ fuse_loop_mt.c \
+ fuse_lowlevel.c \
+ fuse_mt.c fuse_opt.c \
+ fuse_session.c \
+ fuse_signals.c \
+ helper.c \
+ mount.c \
+ mount_util.c \
+ ulockmgr.c
+
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/include
+
+LOCAL_SHARED_LIBRARIES := \
+ libutils libdl
+
+LOCAL_CFLAGS := \
+ -D_FILE_OFFSET_BITS=64 \
+ -DFUSE_USE_VERSION=26
+
+LOCAL_MODULE := libfusetwrp
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_STATIC_LIBRARY)
+#include $(BUILD_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ fusexmp.c
+
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/include
+
+LOCAL_CFLAGS := -D_FILE_OFFSET_BITS=64
+
+LOCAL_MODULE := fusexmp
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_STATIC_LIBRARIES := libfusetwrp
+
+include $(BUILD_EXECUTABLE)
diff --git a/fuse/README b/fuse/README
new file mode 100644
index 000000000..3d1f6f091
--- /dev/null
+++ b/fuse/README
@@ -0,0 +1,12 @@
+Libfuse for Android
+
+FUSE[1] is a framework to develop userspace file systems for linux. Since Android is based on linux, it won't be too difficult to port libfuse to Android.
+
+The main problem of building and running libfuse on Android is that the bionic c library lacks support for pthread_cancel(), which is necessary for libfuse multi-thread code. This stackoverflow entry[2] has suggested a solution, which uses SIGUSR1 as an alternative. It seems to work.
+
+Libfuse can be build with Android NDK[3]. If success, you will get libfuse.a and an example program fusexmp. To run the example, the android kernel should be built with FUSE kernel support.
+
+References:
+[1]. http://fuse.sourceforge.net
+[2]. http://stackoverflow.com/questions/4610086/pthread-cancel-alternatives-in-android-ndk
+[3]. http://developer.android.com/sdk/ndk/index.html
diff --git a/fuse/buffer.c b/fuse/buffer.c
new file mode 100644
index 000000000..053e396bc
--- /dev/null
+++ b/fuse/buffer.c
@@ -0,0 +1,318 @@
+/*
+ FUSE: Filesystem in Userspace
+ Copyright (C) 2010 Miklos Szeredi <miklos@szeredi.hu>
+
+ This program can be distributed under the terms of the GNU LGPLv2.
+ See the file COPYING.LIB
+*/
+
+#define _GNU_SOURCE
+
+#include "config.h"
+#include "fuse_i.h"
+#include "fuse_lowlevel.h"
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <assert.h>
+
+size_t fuse_buf_size(const struct fuse_bufvec *bufv)
+{
+ size_t i;
+ size_t size = 0;
+
+ for (i = 0; i < bufv->count; i++) {
+ if (bufv->buf[i].size == SIZE_MAX)
+ size = SIZE_MAX;
+ else
+ size += bufv->buf[i].size;
+ }
+
+ return size;
+}
+
+static size_t min_size(size_t s1, size_t s2)
+{
+ return s1 < s2 ? s1 : s2;
+}
+
+static ssize_t fuse_buf_write(const struct fuse_buf *dst, size_t dst_off,
+ const struct fuse_buf *src, size_t src_off,
+ size_t len)
+{
+ ssize_t res = 0;
+ size_t copied = 0;
+
+ while (len) {
+ if (dst->flags & FUSE_BUF_FD_SEEK) {
+ res = pwrite(dst->fd, src->mem + src_off, len,
+ dst->pos + dst_off);
+ } else {
+ res = write(dst->fd, src->mem + src_off, len);
+ }
+ if (res == -1) {
+ if (!copied)
+ return -errno;
+ break;
+ }
+ if (res == 0)
+ break;
+
+ copied += res;
+ if (!(dst->flags & FUSE_BUF_FD_RETRY))
+ break;
+
+ src_off += res;
+ dst_off += res;
+ len -= res;
+ }
+
+ return copied;
+}
+
+static ssize_t fuse_buf_read(const struct fuse_buf *dst, size_t dst_off,
+ const struct fuse_buf *src, size_t src_off,
+ size_t len)
+{
+ ssize_t res = 0;
+ size_t copied = 0;
+
+ while (len) {
+ if (src->flags & FUSE_BUF_FD_SEEK) {
+ res = pread(src->fd, dst->mem + dst_off, len,
+ src->pos + src_off);
+ } else {
+ res = read(src->fd, dst->mem + dst_off, len);
+ }
+ if (res == -1) {
+ if (!copied)
+ return -errno;
+ break;
+ }
+ if (res == 0)
+ break;
+
+ copied += res;
+ if (!(src->flags & FUSE_BUF_FD_RETRY))
+ break;
+
+ dst_off += res;
+ src_off += res;
+ len -= res;
+ }
+
+ return copied;
+}
+
+static ssize_t fuse_buf_fd_to_fd(const struct fuse_buf *dst, size_t dst_off,
+ const struct fuse_buf *src, size_t src_off,
+ size_t len)
+{
+ char buf[4096];
+ struct fuse_buf tmp = {
+ .size = sizeof(buf),
+ .flags = 0,
+ };
+ ssize_t res;
+ size_t copied = 0;
+
+ tmp.mem = buf;
+
+ while (len) {
+ size_t this_len = min_size(tmp.size, len);
+ size_t read_len;
+
+ res = fuse_buf_read(&tmp, 0, src, src_off, this_len);
+ if (res < 0) {
+ if (!copied)
+ return res;
+ break;
+ }
+ if (res == 0)
+ break;
+
+ read_len = res;
+ res = fuse_buf_write(dst, dst_off, &tmp, 0, read_len);
+ if (res < 0) {
+ if (!copied)
+ return res;
+ break;
+ }
+ if (res == 0)
+ break;
+
+ copied += res;
+
+ if (res < this_len)
+ break;
+
+ dst_off += res;
+ src_off += res;
+ len -= res;
+ }
+
+ return copied;
+}
+
+#ifdef HAVE_SPLICE
+static ssize_t fuse_buf_splice(const struct fuse_buf *dst, size_t dst_off,
+ const struct fuse_buf *src, size_t src_off,
+ size_t len, enum fuse_buf_copy_flags flags)
+{
+ int splice_flags = 0;
+ off64_t *srcpos = NULL;
+ off64_t *dstpos = NULL;
+ off64_t srcpos_val;
+ off64_t dstpos_val;
+ ssize_t res;
+ size_t copied = 0;
+
+ if (flags & FUSE_BUF_SPLICE_MOVE)
+ splice_flags |= SPLICE_F_MOVE;
+ if (flags & FUSE_BUF_SPLICE_NONBLOCK)
+ splice_flags |= SPLICE_F_NONBLOCK;
+
+ if (src->flags & FUSE_BUF_FD_SEEK) {
+ srcpos_val = src->pos + src_off;
+ srcpos = &srcpos_val;
+ }
+ if (dst->flags & FUSE_BUF_FD_SEEK) {
+ dstpos_val = dst->pos + dst_off;
+ dstpos = &dstpos_val;
+ }
+
+ while (len) {
+ res = splice(src->fd, srcpos, dst->fd, dstpos, len,
+ splice_flags);
+ if (res == -1) {
+ if (copied)
+ break;
+
+ if (errno != EINVAL || (flags & FUSE_BUF_FORCE_SPLICE))
+ return -errno;
+
+ /* Maybe splice is not supported for this combination */
+ return fuse_buf_fd_to_fd(dst, dst_off, src, src_off,
+ len);
+ }
+ if (res == 0)
+ break;
+
+ copied += res;
+ if (!(src->flags & FUSE_BUF_FD_RETRY) &&
+ !(dst->flags & FUSE_BUF_FD_RETRY)) {
+ break;
+ }
+
+ len -= res;
+ }
+
+ return copied;
+}
+#else
+static ssize_t fuse_buf_splice(const struct fuse_buf *dst, size_t dst_off,
+ const struct fuse_buf *src, size_t src_off,
+ size_t len, enum fuse_buf_copy_flags flags)
+{
+ (void) flags;
+
+ return fuse_buf_fd_to_fd(dst, dst_off, src, src_off, len);
+}
+#endif
+
+
+static ssize_t fuse_buf_copy_one(const struct fuse_buf *dst, size_t dst_off,
+ const struct fuse_buf *src, size_t src_off,
+ size_t len, enum fuse_buf_copy_flags flags)
+{
+ int src_is_fd = src->flags & FUSE_BUF_IS_FD;
+ int dst_is_fd = dst->flags & FUSE_BUF_IS_FD;
+
+ if (!src_is_fd && !dst_is_fd) {
+ void *dstmem = dst->mem + dst_off;
+ void *srcmem = src->mem + src_off;
+
+ if (dstmem != srcmem) {
+ if (dstmem + len <= srcmem || srcmem + len <= dstmem)
+ memcpy(dstmem, srcmem, len);
+ else
+ memmove(dstmem, srcmem, len);
+ }
+
+ return len;
+ } else if (!src_is_fd) {
+ return fuse_buf_write(dst, dst_off, src, src_off, len);
+ } else if (!dst_is_fd) {
+ return fuse_buf_read(dst, dst_off, src, src_off, len);
+ } else if (flags & FUSE_BUF_NO_SPLICE) {
+ return fuse_buf_fd_to_fd(dst, dst_off, src, src_off, len);
+ } else {
+ return fuse_buf_splice(dst, dst_off, src, src_off, len, flags);
+ }
+}
+
+static const struct fuse_buf *fuse_bufvec_current(struct fuse_bufvec *bufv)
+{
+ if (bufv->idx < bufv->count)
+ return &bufv->buf[bufv->idx];
+ else
+ return NULL;
+}
+
+static int fuse_bufvec_advance(struct fuse_bufvec *bufv, size_t len)
+{
+ const struct fuse_buf *buf = fuse_bufvec_current(bufv);
+
+ bufv->off += len;
+ assert(bufv->off <= buf->size);
+ if (bufv->off == buf->size) {
+ assert(bufv->idx < bufv->count);
+ bufv->idx++;
+ if (bufv->idx == bufv->count)
+ return 0;
+ bufv->off = 0;
+ }
+ return 1;
+}
+
+ssize_t fuse_buf_copy(struct fuse_bufvec *dstv, struct fuse_bufvec *srcv,
+ enum fuse_buf_copy_flags flags)
+{
+ size_t copied = 0;
+
+ if (dstv == srcv)
+ return fuse_buf_size(dstv);
+
+ for (;;) {
+ const struct fuse_buf *src = fuse_bufvec_current(srcv);
+ const struct fuse_buf *dst = fuse_bufvec_current(dstv);
+ size_t src_len;
+ size_t dst_len;
+ size_t len;
+ ssize_t res;
+
+ if (src == NULL || dst == NULL)
+ break;
+
+ src_len = src->size - srcv->off;
+ dst_len = dst->size - dstv->off;
+ len = min_size(src_len, dst_len);
+
+ res = fuse_buf_copy_one(dst, dstv->off, src, srcv->off, len, flags);
+ if (res < 0) {
+ if (!copied)
+ return res;
+ break;
+ }
+ copied += res;
+
+ if (!fuse_bufvec_advance(srcv, res) ||
+ !fuse_bufvec_advance(dstv, res))
+ break;
+
+ if (res < len)
+ break;
+ }
+
+ return copied;
+}
diff --git a/fuse/cuse_lowlevel.c b/fuse/cuse_lowlevel.c
new file mode 100644
index 000000000..be49ad473
--- /dev/null
+++ b/fuse/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,
+ off64_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, off64_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 fuse_send_reply_iov_nofree(req, 0, iov, 3);
+}
+
+void cuse_lowlevel_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, "cuse: unsupported protocol version: %u.%u\n",
+ arg->major, arg->minor);
+ fuse_reply_err(req, EPROTO);
+ return;
+ }
+
+ if (bufsize < FUSE_MIN_READ_BUFFER) {
+ fprintf(stderr, "cuse: 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);
+
+ fuse_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, "cuse: device not found, try 'modprobe cuse' first\n");
+ else
+ fprintf(stderr, "cuse: 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/fuse/fuse.c b/fuse/fuse.c
new file mode 100644
index 000000000..34b11d47a
--- /dev/null
+++ b/fuse/fuse.c
@@ -0,0 +1,4886 @@
+/*
+ FUSE: Filesystem in Userspace
+ Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
+
+ This program can be distributed under the terms of the GNU LGPLv2.
+ See the file COPYING.LIB
+*/
+
+
+/* For pthread_rwlock_t */
+#define _GNU_SOURCE
+
+#include "config.h"
+#include "fuse_i.h"
+#include "fuse_lowlevel.h"
+#include "fuse_opt.h"
+#include "fuse_misc.h"
+#include "fuse_common_compat.h"
+#include "fuse_compat.h"
+#include "fuse_kernel.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <time.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <errno.h>
+#include <signal.h>
+#include <dlfcn.h>
+#include <assert.h>
+#include <poll.h>
+#include <sys/param.h>
+#include <sys/uio.h>
+#include <sys/time.h>
+#include <sys/mman.h>
+
+#define FUSE_NODE_SLAB 1
+
+#ifndef MAP_ANONYMOUS
+#undef FUSE_NODE_SLAB
+#endif
+
+#define FUSE_DEFAULT_INTR_SIGNAL SIGUSR1
+
+#define FUSE_UNKNOWN_INO 0xffffffff
+#define OFFSET_MAX 0x7fffffffffffffffLL
+
+#define NODE_TABLE_MIN_SIZE 8192
+
+struct fuse_config {
+ unsigned int uid;
+ unsigned int gid;
+ unsigned int umask;
+ double entry_timeout;
+ double negative_timeout;
+ double attr_timeout;
+ double ac_attr_timeout;
+ int ac_attr_timeout_set;
+ int remember;
+ int nopath;
+ int debug;
+ int hard_remove;
+ int use_ino;
+ int readdir_ino;
+ int set_mode;
+ int set_uid;
+ int set_gid;
+ int direct_io;
+ int kernel_cache;
+ int auto_cache;
+ int intr;
+ int intr_signal;
+ int help;
+ char *modules;
+};
+
+struct fuse_fs {
+ struct fuse_operations op;
+ struct fuse_module *m;
+ void *user_data;
+ int compat;
+ int debug;
+};
+
+struct fusemod_so {
+ void *handle;
+ int ctr;
+};
+
+struct lock_queue_element {
+ struct lock_queue_element *next;
+ pthread_cond_t cond;
+ fuse_ino_t nodeid1;
+ const char *name1;
+ char **path1;
+ struct node **wnode1;
+ fuse_ino_t nodeid2;
+ const char *name2;
+ char **path2;
+ struct node **wnode2;
+ int err;
+ bool first_locked : 1;
+ bool second_locked : 1;
+ bool done : 1;
+};
+
+struct node_table {
+ struct node **array;
+ size_t use;
+ size_t size;
+ size_t split;
+};
+
+#define container_of(ptr, type, member) ({ \
+ const typeof( ((type *)0)->member ) *__mptr = (ptr); \
+ (type *)( (char *)__mptr - offsetof(type,member) );})
+
+#define list_entry(ptr, type, member) \
+ container_of(ptr, type, member)
+
+struct list_head {
+ struct list_head *next;
+ struct list_head *prev;
+};
+
+struct node_slab {
+ struct list_head list; /* must be the first member */
+ struct list_head freelist;
+ int used;
+};
+
+struct fuse {
+ struct fuse_session *se;
+ struct node_table name_table;
+ struct node_table id_table;
+ struct list_head lru_table;
+ fuse_ino_t ctr;
+ unsigned int generation;
+ unsigned int hidectr;
+ pthread_mutex_t lock;
+ struct fuse_config conf;
+ int intr_installed;
+ struct fuse_fs *fs;
+ int nullpath_ok;
+ int utime_omit_ok;
+ struct lock_queue_element *lockq;
+ int pagesize;
+ struct list_head partial_slabs;
+ struct list_head full_slabs;
+ pthread_t prune_thread;
+};
+
+struct lock {
+ int type;
+ off64_t start;
+ off64_t end;
+ pid_t pid;
+ uint64_t owner;
+ struct lock *next;
+};
+
+struct node {
+ struct node *name_next;
+ struct node *id_next;
+ fuse_ino_t nodeid;
+ unsigned int generation;
+ int refctr;
+ struct node *parent;
+ char *name;
+ uint64_t nlookup;
+ int open_count;
+ struct timespec stat_updated;
+ struct timespec mtime;
+ off64_t size;
+ struct lock *locks;
+ unsigned int is_hidden : 1;
+ unsigned int cache_valid : 1;
+ int treelock;
+ char inline_name[32];
+};
+
+#define TREELOCK_WRITE -1
+#define TREELOCK_WAIT_OFFSET INT_MIN
+
+struct node_lru {
+ struct node node;
+ struct list_head lru;
+ struct timespec forget_time;
+};
+
+struct fuse_dh {
+ pthread_mutex_t lock;
+ struct fuse *fuse;
+ fuse_req_t req;
+ char *contents;
+ int allocated;
+ unsigned len;
+ unsigned size;
+ unsigned needlen;
+ int filled;
+ uint64_t fh;
+ int error;
+ fuse_ino_t nodeid;
+};
+
+/* old dir handle */
+struct fuse_dirhandle {
+ fuse_fill_dir_t filler;
+ void *buf;
+};
+
+struct fuse_context_i {
+ struct fuse_context ctx;
+ fuse_req_t req;
+};
+
+static pthread_key_t fuse_context_key;
+static pthread_mutex_t fuse_context_lock = PTHREAD_MUTEX_INITIALIZER;
+static int fuse_context_ref;
+static struct fusemod_so *fuse_current_so;
+static struct fuse_module *fuse_modules;
+
+static int fuse_load_so_name(const char *soname)
+{
+ struct fusemod_so *so;
+
+ so = calloc(1, sizeof(struct fusemod_so));
+ if (!so) {
+ fprintf(stderr, "fuse: memory allocation failed\n");
+ return -1;
+ }
+
+ fuse_current_so = so;
+ so->handle = dlopen(soname, RTLD_NOW);
+ fuse_current_so = NULL;
+ if (!so->handle) {
+ fprintf(stderr, "fuse: %s\n", dlerror());
+ goto err;
+ }
+ if (!so->ctr) {
+ fprintf(stderr, "fuse: %s did not register any modules\n",
+ soname);
+ goto err;
+ }
+ return 0;
+
+err:
+ if (so->handle)
+ dlclose(so->handle);
+ free(so);
+ return -1;
+}
+
+static int fuse_load_so_module(const char *module)
+{
+ int res;
+ char *soname = malloc(strlen(module) + 64);
+ if (!soname) {
+ fprintf(stderr, "fuse: memory allocation failed\n");
+ return -1;
+ }
+ sprintf(soname, "libfusemod_%s.so", module);
+ res = fuse_load_so_name(soname);
+ free(soname);
+ return res;
+}
+
+static struct fuse_module *fuse_find_module(const char *module)
+{
+ struct fuse_module *m;
+ for (m = fuse_modules; m; m = m->next) {
+ if (strcmp(module, m->name) == 0) {
+ m->ctr++;
+ break;
+ }
+ }
+ return m;
+}
+
+static struct fuse_module *fuse_get_module(const char *module)
+{
+ struct fuse_module *m;
+
+ pthread_mutex_lock(&fuse_context_lock);
+ m = fuse_find_module(module);
+ if (!m) {
+ int err = fuse_load_so_module(module);
+ if (!err)
+ m = fuse_find_module(module);
+ }
+ pthread_mutex_unlock(&fuse_context_lock);
+ return m;
+}
+
+static void fuse_put_module(struct fuse_module *m)
+{
+ pthread_mutex_lock(&fuse_context_lock);
+ assert(m->ctr > 0);
+ m->ctr--;
+ if (!m->ctr && m->so) {
+ struct fusemod_so *so = m->so;
+ assert(so->ctr > 0);
+ so->ctr--;
+ if (!so->ctr) {
+ struct fuse_module **mp;
+ for (mp = &fuse_modules; *mp;) {
+ if ((*mp)->so == so)
+ *mp = (*mp)->next;
+ else
+ mp = &(*mp)->next;
+ }
+ dlclose(so->handle);
+ free(so);
+ }
+ }
+ pthread_mutex_unlock(&fuse_context_lock);
+}
+
+static void init_list_head(struct list_head *list)
+{
+ list->next = list;
+ list->prev = list;
+}
+
+static int list_empty(const struct list_head *head)
+{
+ return head->next == head;
+}
+
+static void list_add(struct list_head *new, struct list_head *prev,
+ struct list_head *next)
+{
+ next->prev = new;
+ new->next = next;
+ new->prev = prev;
+ prev->next = new;
+}
+
+static inline void list_add_head(struct list_head *new, struct list_head *head)
+{
+ list_add(new, head, head->next);
+}
+
+static inline void list_add_tail(struct list_head *new, struct list_head *head)
+{
+ list_add(new, head->prev, head);
+}
+
+static inline void list_del(struct list_head *entry)
+{
+ struct list_head *prev = entry->prev;
+ struct list_head *next = entry->next;
+
+ next->prev = prev;
+ prev->next = next;
+}
+
+static inline int lru_enabled(struct fuse *f)
+{
+ return f->conf.remember > 0;
+}
+
+static struct node_lru *node_lru(struct node *node)
+{
+ return (struct node_lru *) node;
+}
+
+static size_t get_node_size(struct fuse *f)
+{
+ if (lru_enabled(f))
+ return sizeof(struct node_lru);
+ else
+ return sizeof(struct node);
+}
+
+#ifdef FUSE_NODE_SLAB
+static struct node_slab *list_to_slab(struct list_head *head)
+{
+ return (struct node_slab *) head;
+}
+
+static struct node_slab *node_to_slab(struct fuse *f, struct node *node)
+{
+ return (struct node_slab *) (((uintptr_t) node) & ~((uintptr_t) f->pagesize - 1));
+}
+
+static int alloc_slab(struct fuse *f)
+{
+ void *mem;
+ struct node_slab *slab;
+ char *start;
+ size_t num;
+ size_t i;
+ size_t node_size = get_node_size(f);
+
+ mem = mmap(NULL, f->pagesize, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+
+ if (mem == MAP_FAILED)
+ return -1;
+
+ slab = mem;
+ init_list_head(&slab->freelist);
+ slab->used = 0;
+ num = (f->pagesize - sizeof(struct node_slab)) / node_size;
+
+ start = (char *) mem + f->pagesize - num * node_size;
+ for (i = 0; i < num; i++) {
+ struct list_head *n;
+
+ n = (struct list_head *) (start + i * node_size);
+ list_add_tail(n, &slab->freelist);
+ }
+ list_add_tail(&slab->list, &f->partial_slabs);
+
+ return 0;
+}
+
+static struct node *alloc_node(struct fuse *f)
+{
+ struct node_slab *slab;
+ struct list_head *node;
+
+ if (list_empty(&f->partial_slabs)) {
+ int res = alloc_slab(f);
+ if (res != 0)
+ return NULL;
+ }
+ slab = list_to_slab(f->partial_slabs.next);
+ slab->used++;
+ node = slab->freelist.next;
+ list_del(node);
+ if (list_empty(&slab->freelist)) {
+ list_del(&slab->list);
+ list_add_tail(&slab->list, &f->full_slabs);
+ }
+ memset(node, 0, sizeof(struct node));
+
+ return (struct node *) node;
+}
+
+static void free_slab(struct fuse *f, struct node_slab *slab)
+{
+ int res;
+
+ list_del(&slab->list);
+ res = munmap(slab, f->pagesize);
+ if (res == -1)
+ fprintf(stderr, "fuse warning: munmap(%p) failed\n", slab);
+}
+
+static void free_node_mem(struct fuse *f, struct node *node)
+{
+ struct node_slab *slab = node_to_slab(f, node);
+ struct list_head *n = (struct list_head *) node;
+
+ slab->used--;
+ if (slab->used) {
+ if (list_empty(&slab->freelist)) {
+ list_del(&slab->list);
+ list_add_tail(&slab->list, &f->partial_slabs);
+ }
+ list_add_head(n, &slab->freelist);
+ } else {
+ free_slab(f, slab);
+ }
+}
+#else
+static struct node *alloc_node(struct fuse *f)
+{
+ return (struct node *) calloc(1, get_node_size(f));
+}
+
+static void free_node_mem(struct fuse *f, struct node *node)
+{
+ (void) f;
+ free(node);
+}
+#endif
+
+static size_t id_hash(struct fuse *f, fuse_ino_t ino)
+{
+ uint64_t hash = ((uint32_t) ino * 2654435761U) % f->id_table.size;
+ uint64_t oldhash = hash % (f->id_table.size / 2);
+
+ if (oldhash >= f->id_table.split)
+ return oldhash;
+ else
+ return hash;
+}
+
+static struct node *get_node_nocheck(struct fuse *f, fuse_ino_t nodeid)
+{
+ size_t hash = id_hash(f, nodeid);
+ struct node *node;
+
+ for (node = f->id_table.array[hash]; node != NULL; node = node->id_next)
+ if (node->nodeid == nodeid)
+ return node;
+
+ return NULL;
+}
+
+static struct node *get_node(struct fuse *f, fuse_ino_t nodeid)
+{
+ struct node *node = get_node_nocheck(f, nodeid);
+ if (!node) {
+ fprintf(stderr, "fuse internal error: node %llu not found\n",
+ (unsigned long long) nodeid);
+ abort();
+ }
+ return node;
+}
+
+static void curr_time(struct timespec *now);
+static double diff_timespec(const struct timespec *t1,
+ const struct timespec *t2);
+
+static void remove_node_lru(struct node *node)
+{
+ struct node_lru *lnode = node_lru(node);
+ list_del(&lnode->lru);
+ init_list_head(&lnode->lru);
+}
+
+static void set_forget_time(struct fuse *f, struct node *node)
+{
+ struct node_lru *lnode = node_lru(node);
+
+ list_del(&lnode->lru);
+ list_add_tail(&lnode->lru, &f->lru_table);
+ curr_time(&lnode->forget_time);
+}
+
+static void free_node(struct fuse *f, struct node *node)
+{
+ if (node->name != node->inline_name)
+ free(node->name);
+ free_node_mem(f, node);
+}
+
+static void node_table_reduce(struct node_table *t)
+{
+ size_t newsize = t->size / 2;
+ void *newarray;
+
+ if (newsize < NODE_TABLE_MIN_SIZE)
+ return;
+
+ newarray = realloc(t->array, sizeof(struct node *) * newsize);
+ if (newarray != NULL)
+ t->array = newarray;
+
+ t->size = newsize;
+ t->split = t->size / 2;
+}
+
+static void remerge_id(struct fuse *f)
+{
+ struct node_table *t = &f->id_table;
+ int iter;
+
+ if (t->split == 0)
+ node_table_reduce(t);
+
+ for (iter = 8; t->split > 0 && iter; iter--) {
+ struct node **upper;
+
+ t->split--;
+ upper = &t->array[t->split + t->size / 2];
+ if (*upper) {
+ struct node **nodep;
+
+ for (nodep = &t->array[t->split]; *nodep;
+ nodep = &(*nodep)->id_next);
+
+ *nodep = *upper;
+ *upper = NULL;
+ break;
+ }
+ }
+}
+
+static void unhash_id(struct fuse *f, struct node *node)
+{
+ struct node **nodep = &f->id_table.array[id_hash(f, node->nodeid)];
+
+ for (; *nodep != NULL; nodep = &(*nodep)->id_next)
+ if (*nodep == node) {
+ *nodep = node->id_next;
+ f->id_table.use--;
+
+ if(f->id_table.use < f->id_table.size / 4)
+ remerge_id(f);
+ return;
+ }
+}
+
+static int node_table_resize(struct node_table *t)
+{
+ size_t newsize = t->size * 2;
+ void *newarray;
+
+ newarray = realloc(t->array, sizeof(struct node *) * newsize);
+ if (newarray == NULL)
+ return -1;
+
+ t->array = newarray;
+ memset(t->array + t->size, 0, t->size * sizeof(struct node *));
+ t->size = newsize;
+ t->split = 0;
+
+ return 0;
+}
+
+static void rehash_id(struct fuse *f)
+{
+ struct node_table *t = &f->id_table;
+ struct node **nodep;
+ struct node **next;
+ size_t hash;
+
+ if (t->split == t->size / 2)
+ return;
+
+ hash = t->split;
+ t->split++;
+ for (nodep = &t->array[hash]; *nodep != NULL; nodep = next) {
+ struct node *node = *nodep;
+ size_t newhash = id_hash(f, node->nodeid);
+
+ if (newhash != hash) {
+ next = nodep;
+ *nodep = node->id_next;
+ node->id_next = t->array[newhash];
+ t->array[newhash] = node;
+ } else {
+ next = &node->id_next;
+ }
+ }
+ if (t->split == t->size / 2)
+ node_table_resize(t);
+}
+
+static void hash_id(struct fuse *f, struct node *node)
+{
+ size_t hash = id_hash(f, node->nodeid);
+ node->id_next = f->id_table.array[hash];
+ f->id_table.array[hash] = node;
+ f->id_table.use++;
+
+ if (f->id_table.use >= f->id_table.size / 2)
+ rehash_id(f);
+}
+
+static size_t name_hash(struct fuse *f, fuse_ino_t parent,
+ const char *name)
+{
+ uint64_t hash = parent;
+ uint64_t oldhash;
+
+ for (; *name; name++)
+ hash = hash * 31 + (unsigned char) *name;
+
+ hash %= f->name_table.size;
+ oldhash = hash % (f->name_table.size / 2);
+ if (oldhash >= f->name_table.split)
+ return oldhash;
+ else
+ return hash;
+}
+
+static void unref_node(struct fuse *f, struct node *node);
+
+static void remerge_name(struct fuse *f)
+{
+ struct node_table *t = &f->name_table;
+ int iter;
+
+ if (t->split == 0)
+ node_table_reduce(t);
+
+ for (iter = 8; t->split > 0 && iter; iter--) {
+ struct node **upper;
+
+ t->split--;
+ upper = &t->array[t->split + t->size / 2];
+ if (*upper) {
+ struct node **nodep;
+
+ for (nodep = &t->array[t->split]; *nodep;
+ nodep = &(*nodep)->name_next);
+
+ *nodep = *upper;
+ *upper = NULL;
+ break;
+ }
+ }
+}
+
+static void unhash_name(struct fuse *f, struct node *node)
+{
+ if (node->name) {
+ size_t hash = name_hash(f, node->parent->nodeid, node->name);
+ struct node **nodep = &f->name_table.array[hash];
+
+ for (; *nodep != NULL; nodep = &(*nodep)->name_next)
+ if (*nodep == node) {
+ *nodep = node->name_next;
+ node->name_next = NULL;
+ unref_node(f, node->parent);
+ if (node->name != node->inline_name)
+ free(node->name);
+ node->name = NULL;
+ node->parent = NULL;
+ f->name_table.use--;
+
+ if (f->name_table.use < f->name_table.size / 4)
+ remerge_name(f);
+ return;
+ }
+ fprintf(stderr,
+ "fuse internal error: unable to unhash node: %llu\n",
+ (unsigned long long) node->nodeid);
+ abort();
+ }
+}
+
+static void rehash_name(struct fuse *f)
+{
+ struct node_table *t = &f->name_table;
+ struct node **nodep;
+ struct node **next;
+ size_t hash;
+
+ if (t->split == t->size / 2)
+ return;
+
+ hash = t->split;
+ t->split++;
+ for (nodep = &t->array[hash]; *nodep != NULL; nodep = next) {
+ struct node *node = *nodep;
+ size_t newhash = name_hash(f, node->parent->nodeid, node->name);
+
+ if (newhash != hash) {
+ next = nodep;
+ *nodep = node->name_next;
+ node->name_next = t->array[newhash];
+ t->array[newhash] = node;
+ } else {
+ next = &node->name_next;
+ }
+ }
+ if (t->split == t->size / 2)
+ node_table_resize(t);
+}
+
+static int hash_name(struct fuse *f, struct node *node, fuse_ino_t parentid,
+ const char *name)
+{
+ size_t hash = name_hash(f, parentid, name);
+ struct node *parent = get_node(f, parentid);
+ if (strlen(name) < sizeof(node->inline_name)) {
+ strcpy(node->inline_name, name);
+ node->name = node->inline_name;
+ } else {
+ node->name = strdup(name);
+ if (node->name == NULL)
+ return -1;
+ }
+
+ parent->refctr ++;
+ node->parent = parent;
+ node->name_next = f->name_table.array[hash];
+ f->name_table.array[hash] = node;
+ f->name_table.use++;
+
+ if (f->name_table.use >= f->name_table.size / 2)
+ rehash_name(f);
+
+ return 0;
+}
+
+static void delete_node(struct fuse *f, struct node *node)
+{
+ if (f->conf.debug)
+ fprintf(stderr, "DELETE: %llu\n",
+ (unsigned long long) node->nodeid);
+
+ assert(node->treelock == 0);
+ unhash_name(f, node);
+ if (lru_enabled(f))
+ remove_node_lru(node);
+ unhash_id(f, node);
+ free_node(f, node);
+}
+
+static void unref_node(struct fuse *f, struct node *node)
+{
+ assert(node->refctr > 0);
+ node->refctr --;
+ if (!node->refctr)
+ delete_node(f, node);
+}
+
+static fuse_ino_t next_id(struct fuse *f)
+{
+ do {
+ f->ctr = (f->ctr + 1) & 0xffffffff;
+ if (!f->ctr)
+ f->generation ++;
+ } while (f->ctr == 0 || f->ctr == FUSE_UNKNOWN_INO ||
+ get_node_nocheck(f, f->ctr) != NULL);
+ return f->ctr;
+}
+
+static struct node *lookup_node(struct fuse *f, fuse_ino_t parent,
+ const char *name)
+{
+ size_t hash = name_hash(f, parent, name);
+ struct node *node;
+
+ for (node = f->name_table.array[hash]; node != NULL; node = node->name_next)
+ if (node->parent->nodeid == parent &&
+ strcmp(node->name, name) == 0)
+ return node;
+
+ return NULL;
+}
+
+static void inc_nlookup(struct node *node)
+{
+ if (!node->nlookup)
+ node->refctr++;
+ node->nlookup++;
+}
+
+static struct node *find_node(struct fuse *f, fuse_ino_t parent,
+ const char *name)
+{
+ struct node *node;
+
+ pthread_mutex_lock(&f->lock);
+ if (!name)
+ node = get_node(f, parent);
+ else
+ node = lookup_node(f, parent, name);
+ if (node == NULL) {
+ node = alloc_node(f);
+ if (node == NULL)
+ goto out_err;
+
+ node->nodeid = next_id(f);
+ node->generation = f->generation;
+ if (f->conf.remember)
+ inc_nlookup(node);
+
+ if (hash_name(f, node, parent, name) == -1) {
+ free_node(f, node);
+ node = NULL;
+ goto out_err;
+ }
+ hash_id(f, node);
+ if (lru_enabled(f)) {
+ struct node_lru *lnode = node_lru(node);
+ init_list_head(&lnode->lru);
+ }
+ } else if (lru_enabled(f) && node->nlookup == 1) {
+ remove_node_lru(node);
+ }
+ inc_nlookup(node);
+out_err:
+ pthread_mutex_unlock(&f->lock);
+ return node;
+}
+
+static char *add_name(char **buf, unsigned *bufsize, char *s, const char *name)
+{
+ size_t len = strlen(name);
+
+ if (s - len <= *buf) {
+ unsigned pathlen = *bufsize - (s - *buf);
+ unsigned newbufsize = *bufsize;
+ char *newbuf;
+
+ while (newbufsize < pathlen + len + 1) {
+ if (newbufsize >= 0x80000000)
+ newbufsize = 0xffffffff;
+ else
+ newbufsize *= 2;
+ }
+
+ newbuf = realloc(*buf, newbufsize);
+ if (newbuf == NULL)
+ return NULL;
+
+ *buf = newbuf;
+ s = newbuf + newbufsize - pathlen;
+ memmove(s, newbuf + *bufsize - pathlen, pathlen);
+ *bufsize = newbufsize;
+ }
+ s -= len;
+ strncpy(s, name, len);
+ s--;
+ *s = '/';
+
+ return s;
+}
+
+static void unlock_path(struct fuse *f, fuse_ino_t nodeid, struct node *wnode,
+ struct node *end)
+{
+ struct node *node;
+
+ if (wnode) {
+ assert(wnode->treelock == TREELOCK_WRITE);
+ wnode->treelock = 0;
+ }
+
+ for (node = get_node(f, nodeid);
+ node != end && node->nodeid != FUSE_ROOT_ID; node = node->parent) {
+ assert(node->treelock != 0);
+ assert(node->treelock != TREELOCK_WAIT_OFFSET);
+ assert(node->treelock != TREELOCK_WRITE);
+ node->treelock--;
+ if (node->treelock == TREELOCK_WAIT_OFFSET)
+ node->treelock = 0;
+ }
+}
+
+static int try_get_path(struct fuse *f, fuse_ino_t nodeid, const char *name,
+ char **path, struct node **wnodep, bool need_lock)
+{
+ unsigned bufsize = 256;
+ char *buf;
+ char *s;
+ struct node *node;
+ struct node *wnode = NULL;
+ int err;
+
+ *path = NULL;
+
+ err = -ENOMEM;
+ buf = malloc(bufsize);
+ if (buf == NULL)
+ goto out_err;
+
+ s = buf + bufsize - 1;
+ *s = '\0';
+
+ if (name != NULL) {
+ s = add_name(&buf, &bufsize, s, name);
+ err = -ENOMEM;
+ if (s == NULL)
+ goto out_free;
+ }
+
+ if (wnodep) {
+ assert(need_lock);
+ wnode = lookup_node(f, nodeid, name);
+ if (wnode) {
+ if (wnode->treelock != 0) {
+ if (wnode->treelock > 0)
+ wnode->treelock += TREELOCK_WAIT_OFFSET;
+ err = -EAGAIN;
+ goto out_free;
+ }
+ wnode->treelock = TREELOCK_WRITE;
+ }
+ }
+
+ for (node = get_node(f, nodeid); node->nodeid != FUSE_ROOT_ID;
+ node = node->parent) {
+ err = -ENOENT;
+ if (node->name == NULL || node->parent == NULL)
+ goto out_unlock;
+
+ err = -ENOMEM;
+ s = add_name(&buf, &bufsize, s, node->name);
+ if (s == NULL)
+ goto out_unlock;
+
+ if (need_lock) {
+ err = -EAGAIN;
+ if (node->treelock < 0)
+ goto out_unlock;
+
+ node->treelock++;
+ }
+ }
+
+ if (s[0])
+ memmove(buf, s, bufsize - (s - buf));
+ else
+ strcpy(buf, "/");
+
+ *path = buf;
+ if (wnodep)
+ *wnodep = wnode;
+
+ return 0;
+
+ out_unlock:
+ if (need_lock)
+ unlock_path(f, nodeid, wnode, node);
+ out_free:
+ free(buf);
+
+ out_err:
+ return err;
+}
+
+static void queue_element_unlock(struct fuse *f, struct lock_queue_element *qe)
+{
+ struct node *wnode;
+
+ if (qe->first_locked) {
+ wnode = qe->wnode1 ? *qe->wnode1 : NULL;
+ unlock_path(f, qe->nodeid1, wnode, NULL);
+ }
+ if (qe->second_locked) {
+ wnode = qe->wnode2 ? *qe->wnode2 : NULL;
+ unlock_path(f, qe->nodeid2, wnode, NULL);
+ }
+}
+
+static void queue_element_wakeup(struct fuse *f, struct lock_queue_element *qe)
+{
+ int err;
+ bool first = (qe == f->lockq);
+
+ if (!qe->path1) {
+ /* Just waiting for it to be unlocked */
+ if (get_node(f, qe->nodeid1)->treelock == 0)
+ pthread_cond_signal(&qe->cond);
+
+ return;
+ }
+
+ if (!qe->first_locked) {
+ err = try_get_path(f, qe->nodeid1, qe->name1, qe->path1,
+ qe->wnode1, true);
+ if (!err)
+ qe->first_locked = true;
+ else if (err != -EAGAIN)
+ goto err_unlock;
+ }
+ if (!qe->second_locked && qe->path2) {
+ err = try_get_path(f, qe->nodeid2, qe->name2, qe->path2,
+ qe->wnode2, true);
+ if (!err)
+ qe->second_locked = true;
+ else if (err != -EAGAIN)
+ goto err_unlock;
+ }
+
+ if (qe->first_locked && (qe->second_locked || !qe->path2)) {
+ err = 0;
+ goto done;
+ }
+
+ /*
+ * Only let the first element be partially locked otherwise there could
+ * be a deadlock.
+ *
+ * But do allow the first element to be partially locked to prevent
+ * starvation.
+ */
+ if (!first)
+ queue_element_unlock(f, qe);
+
+ /* keep trying */
+ return;
+
+err_unlock:
+ queue_element_unlock(f, qe);
+done:
+ qe->err = err;
+ qe->done = true;
+ pthread_cond_signal(&qe->cond);
+}
+
+static void wake_up_queued(struct fuse *f)
+{
+ struct lock_queue_element *qe;
+
+ for (qe = f->lockq; qe != NULL; qe = qe->next)
+ queue_element_wakeup(f, qe);
+}
+
+static void debug_path(struct fuse *f, const char *msg, fuse_ino_t nodeid,
+ const char *name, bool wr)
+{
+ if (f->conf.debug) {
+ struct node *wnode = NULL;
+
+ if (wr)
+ wnode = lookup_node(f, nodeid, name);
+
+ if (wnode)
+ fprintf(stderr, "%s %li (w)\n", msg, wnode->nodeid);
+ else
+ fprintf(stderr, "%s %li\n", msg, nodeid);
+ }
+}
+
+static void queue_path(struct fuse *f, struct lock_queue_element *qe)
+{
+ struct lock_queue_element **qp;
+
+ qe->done = false;
+ qe->first_locked = false;
+ qe->second_locked = false;
+ pthread_cond_init(&qe->cond, NULL);
+ qe->next = NULL;
+ for (qp = &f->lockq; *qp != NULL; qp = &(*qp)->next);
+ *qp = qe;
+}
+
+static void dequeue_path(struct fuse *f, struct lock_queue_element *qe)
+{
+ struct lock_queue_element **qp;
+
+ pthread_cond_destroy(&qe->cond);
+ for (qp = &f->lockq; *qp != qe; qp = &(*qp)->next);
+ *qp = qe->next;
+}
+
+static int wait_path(struct fuse *f, struct lock_queue_element *qe)
+{
+ queue_path(f, qe);
+
+ do {
+ pthread_cond_wait(&qe->cond, &f->lock);
+ } while (!qe->done);
+
+ dequeue_path(f, qe);
+
+ return qe->err;
+}
+
+static int get_path_common(struct fuse *f, fuse_ino_t nodeid, const char *name,
+ char **path, struct node **wnode)
+{
+ int err;
+
+ pthread_mutex_lock(&f->lock);
+ err = try_get_path(f, nodeid, name, path, wnode, true);
+ if (err == -EAGAIN) {
+ struct lock_queue_element qe = {
+ .nodeid1 = nodeid,
+ .name1 = name,
+ .path1 = path,
+ .wnode1 = wnode,
+ };
+ debug_path(f, "QUEUE PATH", nodeid, name, !!wnode);
+ err = wait_path(f, &qe);
+ debug_path(f, "DEQUEUE PATH", nodeid, name, !!wnode);
+ }
+ pthread_mutex_unlock(&f->lock);
+
+ return err;
+}
+
+static int get_path(struct fuse *f, fuse_ino_t nodeid, char **path)
+{
+ return get_path_common(f, nodeid, NULL, path, NULL);
+}
+
+static int get_path_nullok(struct fuse *f, fuse_ino_t nodeid, char **path)
+{
+ int err = 0;
+
+ if (f->conf.nopath) {
+ *path = NULL;
+ } else {
+ err = get_path_common(f, nodeid, NULL, path, NULL);
+ if (err == -ENOENT && f->nullpath_ok)
+ err = 0;
+ }
+
+ return err;
+}
+
+static int get_path_name(struct fuse *f, fuse_ino_t nodeid, const char *name,
+ char **path)
+{
+ return get_path_common(f, nodeid, name, path, NULL);
+}
+
+static int get_path_wrlock(struct fuse *f, fuse_ino_t nodeid, const char *name,
+ char **path, struct node **wnode)
+{
+ return get_path_common(f, nodeid, name, path, wnode);
+}
+
+static int try_get_path2(struct fuse *f, fuse_ino_t nodeid1, const char *name1,
+ fuse_ino_t nodeid2, const char *name2,
+ char **path1, char **path2,
+ struct node **wnode1, struct node **wnode2)
+{
+ int err;
+
+ /* FIXME: locking two paths needs deadlock checking */
+ err = try_get_path(f, nodeid1, name1, path1, wnode1, true);
+ if (!err) {
+ err = try_get_path(f, nodeid2, name2, path2, wnode2, true);
+ if (err) {
+ struct node *wn1 = wnode1 ? *wnode1 : NULL;
+
+ unlock_path(f, nodeid1, wn1, NULL);
+ free(*path1);
+ }
+ }
+ return err;
+}
+
+static int get_path2(struct fuse *f, fuse_ino_t nodeid1, const char *name1,
+ fuse_ino_t nodeid2, const char *name2,
+ char **path1, char **path2,
+ struct node **wnode1, struct node **wnode2)
+{
+ int err;
+
+ pthread_mutex_lock(&f->lock);
+ err = try_get_path2(f, nodeid1, name1, nodeid2, name2,
+ path1, path2, wnode1, wnode2);
+ if (err == -EAGAIN) {
+ struct lock_queue_element qe = {
+ .nodeid1 = nodeid1,
+ .name1 = name1,
+ .path1 = path1,
+ .wnode1 = wnode1,
+ .nodeid2 = nodeid2,
+ .name2 = name2,
+ .path2 = path2,
+ .wnode2 = wnode2,
+ };
+
+ debug_path(f, "QUEUE PATH1", nodeid1, name1, !!wnode1);
+ debug_path(f, " PATH2", nodeid2, name2, !!wnode2);
+ err = wait_path(f, &qe);
+ debug_path(f, "DEQUEUE PATH1", nodeid1, name1, !!wnode1);
+ debug_path(f, " PATH2", nodeid2, name2, !!wnode2);
+ }
+ pthread_mutex_unlock(&f->lock);
+
+ return err;
+}
+
+static void free_path_wrlock(struct fuse *f, fuse_ino_t nodeid,
+ struct node *wnode, char *path)
+{
+ pthread_mutex_lock(&f->lock);
+ unlock_path(f, nodeid, wnode, NULL);
+ if (f->lockq)
+ wake_up_queued(f);
+ pthread_mutex_unlock(&f->lock);
+ free(path);
+}
+
+static void free_path(struct fuse *f, fuse_ino_t nodeid, char *path)
+{
+ if (path)
+ free_path_wrlock(f, nodeid, NULL, path);
+}
+
+static void free_path2(struct fuse *f, fuse_ino_t nodeid1, fuse_ino_t nodeid2,
+ struct node *wnode1, struct node *wnode2,
+ char *path1, char *path2)
+{
+ pthread_mutex_lock(&f->lock);
+ unlock_path(f, nodeid1, wnode1, NULL);
+ unlock_path(f, nodeid2, wnode2, NULL);
+ wake_up_queued(f);
+ pthread_mutex_unlock(&f->lock);
+ free(path1);
+ free(path2);
+}
+
+static void forget_node(struct fuse *f, fuse_ino_t nodeid, uint64_t nlookup)
+{
+ struct node *node;
+ if (nodeid == FUSE_ROOT_ID)
+ return;
+ pthread_mutex_lock(&f->lock);
+ node = get_node(f, nodeid);
+
+ /*
+ * Node may still be locked due to interrupt idiocy in open,
+ * create and opendir
+ */
+ while (node->nlookup == nlookup && node->treelock) {
+ struct lock_queue_element qe = {
+ .nodeid1 = nodeid,
+ };
+
+ debug_path(f, "QUEUE PATH (forget)", nodeid, NULL, false);
+ queue_path(f, &qe);
+
+ do {
+ pthread_cond_wait(&qe.cond, &f->lock);
+ } while (node->nlookup == nlookup && node->treelock);
+
+ dequeue_path(f, &qe);
+ debug_path(f, "DEQUEUE_PATH (forget)", nodeid, NULL, false);
+ }
+
+ assert(node->nlookup >= nlookup);
+ node->nlookup -= nlookup;
+ if (!node->nlookup) {
+ unref_node(f, node);
+ } else if (lru_enabled(f) && node->nlookup == 1) {
+ set_forget_time(f, node);
+ }
+ pthread_mutex_unlock(&f->lock);
+}
+
+static void unlink_node(struct fuse *f, struct node *node)
+{
+ if (f->conf.remember) {
+ 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;
+
+ pthread_mutex_lock(&f->lock);
+ node = lookup_node(f, dir, name);
+ if (node != NULL)
+ unlink_node(f, node);
+ pthread_mutex_unlock(&f->lock);
+}
+
+static int rename_node(struct fuse *f, fuse_ino_t olddir, const char *oldname,
+ fuse_ino_t newdir, const char *newname, int hide)
+{
+ struct node *node;
+ struct node *newnode;
+ int err = 0;
+
+ pthread_mutex_lock(&f->lock);
+ node = lookup_node(f, olddir, oldname);
+ newnode = lookup_node(f, newdir, newname);
+ if (node == NULL)
+ goto out;
+
+ if (newnode != NULL) {
+ if (hide) {
+ fprintf(stderr, "fuse: hidden file got created during hiding\n");
+ err = -EBUSY;
+ goto out;
+ }
+ unlink_node(f, newnode);
+ }
+
+ unhash_name(f, node);
+ if (hash_name(f, node, newdir, newname) == -1) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ if (hide)
+ node->is_hidden = 1;
+
+out:
+ pthread_mutex_unlock(&f->lock);
+ return err;
+}
+
+static void set_stat(struct fuse *f, fuse_ino_t nodeid, struct stat *stbuf)
+{
+ if (!f->conf.use_ino)
+ stbuf->st_ino = nodeid;
+ if (f->conf.set_mode)
+ stbuf->st_mode = (stbuf->st_mode & S_IFMT) |
+ (0777 & ~f->conf.umask);
+ if (f->conf.set_uid)
+ stbuf->st_uid = f->conf.uid;
+ if (f->conf.set_gid)
+ stbuf->st_gid = f->conf.gid;
+}
+
+static struct fuse *req_fuse(fuse_req_t req)
+{
+ return (struct fuse *) fuse_req_userdata(req);
+}
+
+static void fuse_intr_sighandler(int sig)
+{
+ (void) sig;
+ /* Nothing to do */
+}
+
+struct fuse_intr_data {
+ pthread_t id;
+ pthread_cond_t cond;
+ int finished;
+};
+
+static void fuse_interrupt(fuse_req_t req, void *d_)
+{
+ struct fuse_intr_data *d = d_;
+ struct fuse *f = req_fuse(req);
+
+ if (d->id == pthread_self())
+ return;
+
+ pthread_mutex_lock(&f->lock);
+ while (!d->finished) {
+ struct timeval now;
+ struct timespec timeout;
+
+ pthread_kill(d->id, f->conf.intr_signal);
+ gettimeofday(&now, NULL);
+ timeout.tv_sec = now.tv_sec + 1;
+ timeout.tv_nsec = now.tv_usec * 1000;
+ pthread_cond_timedwait(&d->cond, &f->lock, &timeout);
+ }
+ pthread_mutex_unlock(&f->lock);
+}
+
+static void fuse_do_finish_interrupt(struct fuse *f, fuse_req_t req,
+ struct fuse_intr_data *d)
+{
+ pthread_mutex_lock(&f->lock);
+ d->finished = 1;
+ pthread_cond_broadcast(&d->cond);
+ pthread_mutex_unlock(&f->lock);
+ fuse_req_interrupt_func(req, NULL, NULL);
+ pthread_cond_destroy(&d->cond);
+}
+
+static void fuse_do_prepare_interrupt(fuse_req_t req, struct fuse_intr_data *d)
+{
+ d->id = pthread_self();
+ pthread_cond_init(&d->cond, NULL);
+ d->finished = 0;
+ fuse_req_interrupt_func(req, fuse_interrupt, d);
+}
+
+static inline void fuse_finish_interrupt(struct fuse *f, fuse_req_t req,
+ struct fuse_intr_data *d)
+{
+ if (f->conf.intr)
+ fuse_do_finish_interrupt(f, req, d);
+}
+
+static inline void fuse_prepare_interrupt(struct fuse *f, fuse_req_t req,
+ struct fuse_intr_data *d)
+{
+ if (f->conf.intr)
+ fuse_do_prepare_interrupt(req, d);
+}
+
+#if !defined(__FreeBSD__) && !defined(__NetBSD__)
+
+static int fuse_compat_open(struct fuse_fs *fs, const char *path,
+ struct fuse_file_info *fi)
+{
+ int err;
+ if (!fs->compat || fs->compat >= 25)
+ err = fs->op.open(path, fi);
+ else if (fs->compat == 22) {
+ struct fuse_file_info_compat tmp;
+ memcpy(&tmp, fi, sizeof(tmp));
+ err = ((struct fuse_operations_compat22 *) &fs->op)->open(path,
+ &tmp);
+ memcpy(fi, &tmp, sizeof(tmp));
+ fi->fh = tmp.fh;
+ } else
+ err = ((struct fuse_operations_compat2 *) &fs->op)
+ ->open(path, fi->flags);
+ return err;
+}
+
+static int fuse_compat_release(struct fuse_fs *fs, const char *path,
+ struct fuse_file_info *fi)
+{
+ if (!fs->compat || fs->compat >= 22)
+ return fs->op.release(path, fi);
+ else
+ return ((struct fuse_operations_compat2 *) &fs->op)
+ ->release(path, fi->flags);
+}
+
+static int fuse_compat_opendir(struct fuse_fs *fs, const char *path,
+ struct fuse_file_info *fi)
+{
+ if (!fs->compat || fs->compat >= 25)
+ return fs->op.opendir(path, fi);
+ else {
+ int err;
+ struct fuse_file_info_compat tmp;
+ memcpy(&tmp, fi, sizeof(tmp));
+ err = ((struct fuse_operations_compat22 *) &fs->op)
+ ->opendir(path, &tmp);
+ memcpy(fi, &tmp, sizeof(tmp));
+ fi->fh = tmp.fh;
+ return err;
+ }
+}
+
+static void convert_statfs_compat(struct fuse_statfs_compat1 *compatbuf,
+ struct statvfs *stbuf)
+{
+ stbuf->f_bsize = compatbuf->block_size;
+ stbuf->f_blocks = compatbuf->blocks;
+ stbuf->f_bfree = compatbuf->blocks_free;
+ stbuf->f_bavail = compatbuf->blocks_free;
+ stbuf->f_files = compatbuf->files;
+ stbuf->f_ffree = compatbuf->files_free;
+ stbuf->f_namemax = compatbuf->namelen;
+}
+
+static void convert_statfs_old(struct statfs *oldbuf, struct statvfs *stbuf)
+{
+ stbuf->f_bsize = oldbuf->f_bsize;
+ stbuf->f_blocks = oldbuf->f_blocks;
+ stbuf->f_bfree = oldbuf->f_bfree;
+ stbuf->f_bavail = oldbuf->f_bavail;
+ stbuf->f_files = oldbuf->f_files;
+ stbuf->f_ffree = oldbuf->f_ffree;
+ stbuf->f_namemax = oldbuf->f_namelen;
+}
+
+static int fuse_compat_statfs(struct fuse_fs *fs, const char *path,
+ struct statvfs *buf)
+{
+ int err;
+
+ if (!fs->compat || fs->compat >= 25) {
+ err = fs->op.statfs(fs->compat == 25 ? "/" : path, buf);
+ } else if (fs->compat > 11) {
+ struct statfs oldbuf;
+ err = ((struct fuse_operations_compat22 *) &fs->op)
+ ->statfs("/", &oldbuf);
+ if (!err)
+ convert_statfs_old(&oldbuf, buf);
+ } else {
+ struct fuse_statfs_compat1 compatbuf;
+ memset(&compatbuf, 0, sizeof(struct fuse_statfs_compat1));
+ err = ((struct fuse_operations_compat1 *) &fs->op)
+ ->statfs(&compatbuf);
+ if (!err)
+ convert_statfs_compat(&compatbuf, buf);
+ }
+ return err;
+}
+
+#else /* __FreeBSD__ || __NetBSD__ */
+
+static inline int fuse_compat_open(struct fuse_fs *fs, char *path,
+ struct fuse_file_info *fi)
+{
+ return fs->op.open(path, fi);
+}
+
+static inline int fuse_compat_release(struct fuse_fs *fs, const char *path,
+ struct fuse_file_info *fi)
+{
+ return fs->op.release(path, fi);
+}
+
+static inline int fuse_compat_opendir(struct fuse_fs *fs, const char *path,
+ struct fuse_file_info *fi)
+{
+ return fs->op.opendir(path, fi);
+}
+
+static inline int fuse_compat_statfs(struct fuse_fs *fs, const char *path,
+ struct statvfs *buf)
+{
+ return fs->op.statfs(fs->compat == 25 ? "/" : path, buf);
+}
+
+#endif /* __FreeBSD__ || __NetBSD__ */
+
+int fuse_fs_getattr(struct fuse_fs *fs, const char *path, struct stat *buf)
+{
+ fuse_get_context()->private_data = fs->user_data;
+ if (fs->op.getattr) {
+ if (fs->debug)
+ fprintf(stderr, "getattr %s\n", path);
+
+ return fs->op.getattr(path, buf);
+ } else {
+ return -ENOSYS;
+ }
+}
+
+int fuse_fs_fgetattr(struct fuse_fs *fs, const char *path, struct stat *buf,
+ struct fuse_file_info *fi)
+{
+ fuse_get_context()->private_data = fs->user_data;
+ if (fs->op.fgetattr) {
+ if (fs->debug)
+ fprintf(stderr, "fgetattr[%llu] %s\n",
+ (unsigned long long) fi->fh, path);
+
+ return fs->op.fgetattr(path, buf, fi);
+ } else if (path && fs->op.getattr) {
+ if (fs->debug)
+ fprintf(stderr, "getattr %s\n", path);
+
+ return fs->op.getattr(path, buf);
+ } else {
+ return -ENOSYS;
+ }
+}
+
+int fuse_fs_rename(struct fuse_fs *fs, const char *oldpath,
+ const char *newpath)
+{
+ fuse_get_context()->private_data = fs->user_data;
+ if (fs->op.rename) {
+ if (fs->debug)
+ fprintf(stderr, "rename %s %s\n", oldpath, newpath);
+
+ return fs->op.rename(oldpath, newpath);
+ } else {
+ return -ENOSYS;
+ }
+}
+
+int fuse_fs_unlink(struct fuse_fs *fs, const char *path)
+{
+ fuse_get_context()->private_data = fs->user_data;
+ if (fs->op.unlink) {
+ if (fs->debug)
+ fprintf(stderr, "unlink %s\n", path);
+
+ return fs->op.unlink(path);
+ } else {
+ return -ENOSYS;
+ }
+}
+
+int fuse_fs_rmdir(struct fuse_fs *fs, const char *path)
+{
+ fuse_get_context()->private_data = fs->user_data;
+ if (fs->op.rmdir) {
+ if (fs->debug)
+ fprintf(stderr, "rmdir %s\n", path);
+
+ return fs->op.rmdir(path);
+ } else {
+ return -ENOSYS;
+ }
+}
+
+int fuse_fs_symlink(struct fuse_fs *fs, const char *linkname, const char *path)
+{
+ fuse_get_context()->private_data = fs->user_data;
+ if (fs->op.symlink) {
+ if (fs->debug)
+ fprintf(stderr, "symlink %s %s\n", linkname, path);
+
+ return fs->op.symlink(linkname, path);
+ } else {
+ return -ENOSYS;
+ }
+}
+
+int fuse_fs_link(struct fuse_fs *fs, const char *oldpath, const char *newpath)
+{
+ fuse_get_context()->private_data = fs->user_data;
+ if (fs->op.link) {
+ if (fs->debug)
+ fprintf(stderr, "link %s %s\n", oldpath, newpath);
+
+ return fs->op.link(oldpath, newpath);
+ } else {
+ return -ENOSYS;
+ }
+}
+
+int fuse_fs_release(struct fuse_fs *fs, const char *path,
+ struct fuse_file_info *fi)
+{
+ fuse_get_context()->private_data = fs->user_data;
+ if (fs->op.release) {
+ if (fs->debug)
+ fprintf(stderr, "release%s[%llu] flags: 0x%x\n",
+ fi->flush ? "+flush" : "",
+ (unsigned long long) fi->fh, fi->flags);
+
+ return fuse_compat_release(fs, path, fi);
+ } else {
+ return 0;
+ }
+}
+
+int fuse_fs_opendir(struct fuse_fs *fs, const char *path,
+ struct fuse_file_info *fi)
+{
+ fuse_get_context()->private_data = fs->user_data;
+ if (fs->op.opendir) {
+ int err;
+
+ if (fs->debug)
+ fprintf(stderr, "opendir flags: 0x%x %s\n", fi->flags,
+ path);
+
+ err = fuse_compat_opendir(fs, path, fi);
+
+ if (fs->debug && !err)
+ fprintf(stderr, " opendir[%lli] flags: 0x%x %s\n",
+ (unsigned long long) fi->fh, fi->flags, path);
+
+ return err;
+ } else {
+ return 0;
+ }
+}
+
+int fuse_fs_open(struct fuse_fs *fs, const char *path,
+ struct fuse_file_info *fi)
+{
+ fuse_get_context()->private_data = fs->user_data;
+ if (fs->op.open) {
+ int err;
+
+ if (fs->debug)
+ fprintf(stderr, "open flags: 0x%x %s\n", fi->flags,
+ path);
+
+ err = fuse_compat_open(fs, path, fi);
+
+ if (fs->debug && !err)
+ fprintf(stderr, " open[%lli] flags: 0x%x %s\n",
+ (unsigned long long) fi->fh, fi->flags, path);
+
+ return err;
+ } else {
+ return 0;
+ }
+}
+
+static void fuse_free_buf(struct fuse_bufvec *buf)
+{
+ if (buf != NULL) {
+ size_t i;
+
+ for (i = 0; i < buf->count; i++)
+ free(buf->buf[i].mem);
+ free(buf);
+ }
+}
+
+int fuse_fs_read_buf(struct fuse_fs *fs, const char *path,
+ struct fuse_bufvec **bufp, size_t size, off64_t off,
+ struct fuse_file_info *fi)
+{
+ fuse_get_context()->private_data = fs->user_data;
+ if (fs->op.read || fs->op.read_buf) {
+ int res;
+
+ if (fs->debug)
+ fprintf(stderr,
+ "read[%llu] %zu bytes from %llu flags: 0x%x\n",
+ (unsigned long long) fi->fh,
+ size, (unsigned long long) off, fi->flags);
+
+ if (fs->op.read_buf) {
+ res = fs->op.read_buf(path, bufp, size, off, fi);
+ } else {
+ struct fuse_bufvec *buf;
+ void *mem;
+
+ buf = malloc(sizeof(struct fuse_bufvec));
+ if (buf == NULL)
+ return -ENOMEM;
+
+ mem = malloc(size);
+ if (mem == NULL) {
+ free(buf);
+ return -ENOMEM;
+ }
+ *buf = FUSE_BUFVEC_INIT(size);
+ buf->buf[0].mem = mem;
+ *bufp = buf;
+
+ res = fs->op.read(path, mem, size, off, fi);
+ if (res >= 0)
+ buf->buf[0].size = res;
+ }
+
+ if (fs->debug && res >= 0)
+ fprintf(stderr, " read[%llu] %zu bytes from %llu\n",
+ (unsigned long long) fi->fh,
+ fuse_buf_size(*bufp),
+ (unsigned long long) off);
+ if (res >= 0 && fuse_buf_size(*bufp) > (int) size)
+ fprintf(stderr, "fuse: read too many bytes\n");
+
+ if (res < 0)
+ return res;
+
+ return 0;
+ } else {
+ return -ENOSYS;
+ }
+}
+
+int fuse_fs_read(struct fuse_fs *fs, const char *path, char *mem, size_t size,
+ off64_t off, struct fuse_file_info *fi)
+{
+ int res;
+ struct fuse_bufvec *buf = NULL;
+
+ res = fuse_fs_read_buf(fs, path, &buf, size, off, fi);
+ if (res == 0) {
+ struct fuse_bufvec dst = FUSE_BUFVEC_INIT(size);
+
+ dst.buf[0].mem = mem;
+ res = fuse_buf_copy(&dst, buf, 0);
+ }
+ fuse_free_buf(buf);
+
+ return res;
+}
+
+int fuse_fs_write_buf(struct fuse_fs *fs, const char *path,
+ struct fuse_bufvec *buf, off64_t off,
+ struct fuse_file_info *fi)
+{
+ fuse_get_context()->private_data = fs->user_data;
+ if (fs->op.write_buf || fs->op.write) {
+ int res;
+ size_t size = fuse_buf_size(buf);
+
+ assert(buf->idx == 0 && buf->off == 0);
+ if (fs->debug)
+ fprintf(stderr,
+ "write%s[%llu] %zu bytes to %llu flags: 0x%x\n",
+ fi->writepage ? "page" : "",
+ (unsigned long long) fi->fh,
+ size,
+ (unsigned long long) off,
+ fi->flags);
+
+ if (fs->op.write_buf) {
+ res = fs->op.write_buf(path, buf, off, fi);
+ } else {
+ void *mem = NULL;
+ struct fuse_buf *flatbuf;
+ struct fuse_bufvec tmp = FUSE_BUFVEC_INIT(size);
+
+ if (buf->count == 1 &&
+ !(buf->buf[0].flags & FUSE_BUF_IS_FD)) {
+ flatbuf = &buf->buf[0];
+ } else {
+ res = -ENOMEM;
+ mem = malloc(size);
+ if (mem == NULL)
+ goto out;
+
+ tmp.buf[0].mem = mem;
+ res = fuse_buf_copy(&tmp, buf, 0);
+ if (res <= 0)
+ goto out_free;
+
+ tmp.buf[0].size = res;
+ flatbuf = &tmp.buf[0];
+ }
+
+ res = fs->op.write(path, flatbuf->mem, flatbuf->size,
+ off, fi);
+out_free:
+ free(mem);
+ }
+out:
+ if (fs->debug && res >= 0)
+ fprintf(stderr, " write%s[%llu] %u bytes to %llu\n",
+ fi->writepage ? "page" : "",
+ (unsigned long long) fi->fh, res,
+ (unsigned long long) off);
+ if (res > (int) size)
+ fprintf(stderr, "fuse: wrote too many bytes\n");
+
+ return res;
+ } else {
+ return -ENOSYS;
+ }
+}
+
+int fuse_fs_write(struct fuse_fs *fs, const char *path, const char *mem,
+ size_t size, off64_t off, struct fuse_file_info *fi)
+{
+ struct fuse_bufvec bufv = FUSE_BUFVEC_INIT(size);
+
+ bufv.buf[0].mem = (void *) mem;
+
+ return fuse_fs_write_buf(fs, path, &bufv, off, fi);
+}
+
+int fuse_fs_fsync(struct fuse_fs *fs, const char *path, int datasync,
+ struct fuse_file_info *fi)
+{
+ fuse_get_context()->private_data = fs->user_data;
+ if (fs->op.fsync) {
+ if (fs->debug)
+ fprintf(stderr, "fsync[%llu] datasync: %i\n",
+ (unsigned long long) fi->fh, datasync);
+
+ return fs->op.fsync(path, datasync, fi);
+ } else {
+ return -ENOSYS;
+ }
+}
+
+int fuse_fs_fsyncdir(struct fuse_fs *fs, const char *path, int datasync,
+ struct fuse_file_info *fi)
+{
+ fuse_get_context()->private_data = fs->user_data;
+ if (fs->op.fsyncdir) {
+ if (fs->debug)
+ fprintf(stderr, "fsyncdir[%llu] datasync: %i\n",
+ (unsigned long long) fi->fh, datasync);
+
+ return fs->op.fsyncdir(path, datasync, fi);
+ } else {
+ return -ENOSYS;
+ }
+}
+
+int fuse_fs_flush(struct fuse_fs *fs, const char *path,
+ struct fuse_file_info *fi)
+{
+ fuse_get_context()->private_data = fs->user_data;
+ if (fs->op.flush) {
+ if (fs->debug)
+ fprintf(stderr, "flush[%llu]\n",
+ (unsigned long long) fi->fh);
+
+ return fs->op.flush(path, fi);
+ } else {
+ return -ENOSYS;
+ }
+}
+
+int fuse_fs_statfs(struct fuse_fs *fs, const char *path, struct statvfs *buf)
+{
+ fuse_get_context()->private_data = fs->user_data;
+ if (fs->op.statfs) {
+ if (fs->debug)
+ fprintf(stderr, "statfs %s\n", path);
+
+ return fuse_compat_statfs(fs, path, buf);
+ } else {
+ buf->f_namemax = 255;
+ buf->f_bsize = 512;
+ return 0;
+ }
+}
+
+int fuse_fs_releasedir(struct fuse_fs *fs, const char *path,
+ struct fuse_file_info *fi)
+{
+ fuse_get_context()->private_data = fs->user_data;
+ if (fs->op.releasedir) {
+ if (fs->debug)
+ fprintf(stderr, "releasedir[%llu] flags: 0x%x\n",
+ (unsigned long long) fi->fh, fi->flags);
+
+ return fs->op.releasedir(path, fi);
+ } else {
+ return 0;
+ }
+}
+
+static int fill_dir_old(struct fuse_dirhandle *dh, const char *name, int type,
+ ino_t ino)
+{
+ int res;
+ struct stat stbuf;
+
+ memset(&stbuf, 0, sizeof(stbuf));
+ stbuf.st_mode = type << 12;
+ stbuf.st_ino = ino;
+
+ res = dh->filler(dh->buf, name, &stbuf, 0);
+ return res ? -ENOMEM : 0;
+}
+
+int fuse_fs_readdir(struct fuse_fs *fs, const char *path, void *buf,
+ fuse_fill_dir_t filler, off64_t off,
+ struct fuse_file_info *fi)
+{
+ fuse_get_context()->private_data = fs->user_data;
+ if (fs->op.readdir) {
+ if (fs->debug)
+ fprintf(stderr, "readdir[%llu] from %llu\n",
+ (unsigned long long) fi->fh,
+ (unsigned long long) off);
+
+ return fs->op.readdir(path, buf, filler, off, fi);
+ } else if (fs->op.getdir) {
+ struct fuse_dirhandle dh;
+
+ if (fs->debug)
+ fprintf(stderr, "getdir[%llu]\n",
+ (unsigned long long) fi->fh);
+
+ dh.filler = filler;
+ dh.buf = buf;
+ return fs->op.getdir(path, &dh, fill_dir_old);
+ } else {
+ return -ENOSYS;
+ }
+}
+
+int fuse_fs_create(struct fuse_fs *fs, const char *path, mode_t mode,
+ struct fuse_file_info *fi)
+{
+ fuse_get_context()->private_data = fs->user_data;
+ if (fs->op.create) {
+ int err;
+
+ if (fs->debug)
+ fprintf(stderr,
+ "create flags: 0x%x %s 0%o umask=0%03o\n",
+ fi->flags, path, mode,
+ fuse_get_context()->umask);
+
+ err = fs->op.create(path, mode, fi);
+
+ if (fs->debug && !err)
+ fprintf(stderr, " create[%llu] flags: 0x%x %s\n",
+ (unsigned long long) fi->fh, fi->flags, path);
+
+ return err;
+ } else {
+ return -ENOSYS;
+ }
+}
+
+int fuse_fs_lock(struct fuse_fs *fs, const char *path,
+ struct fuse_file_info *fi, int cmd, struct flock *lock)
+{
+ fuse_get_context()->private_data = fs->user_data;
+ if (fs->op.lock) {
+ if (fs->debug)
+ fprintf(stderr, "lock[%llu] %s %s start: %llu len: %llu pid: %llu\n",
+ (unsigned long long) fi->fh,
+ (cmd == F_GETLK ? "F_GETLK" :
+ (cmd == F_SETLK ? "F_SETLK" :
+ (cmd == F_SETLKW ? "F_SETLKW" : "???"))),
+ (lock->l_type == F_RDLCK ? "F_RDLCK" :
+ (lock->l_type == F_WRLCK ? "F_WRLCK" :
+ (lock->l_type == F_UNLCK ? "F_UNLCK" :
+ "???"))),
+ (unsigned long long) lock->l_start,
+ (unsigned long long) lock->l_len,
+ (unsigned long long) lock->l_pid);
+
+ return fs->op.lock(path, fi, cmd, lock);
+ } else {
+ return -ENOSYS;
+ }
+}
+
+int fuse_fs_flock(struct fuse_fs *fs, const char *path,
+ struct fuse_file_info *fi, int op)
+{
+ fuse_get_context()->private_data = fs->user_data;
+ if (fs->op.flock) {
+ if (fs->debug) {
+ int xop = op & ~LOCK_NB;
+
+ fprintf(stderr, "lock[%llu] %s%s\n",
+ (unsigned long long) fi->fh,
+ xop == LOCK_SH ? "LOCK_SH" :
+ (xop == LOCK_EX ? "LOCK_EX" :
+ (xop == LOCK_UN ? "LOCK_UN" : "???")),
+ (op & LOCK_NB) ? "|LOCK_NB" : "");
+ }
+ return fs->op.flock(path, fi, op);
+ } else {
+ return -ENOSYS;
+ }
+}
+
+int fuse_fs_chown(struct fuse_fs *fs, const char *path, uid_t uid, gid_t gid)
+{
+ fuse_get_context()->private_data = fs->user_data;
+ if (fs->op.chown) {
+ if (fs->debug)
+ fprintf(stderr, "chown %s %lu %lu\n", path,
+ (unsigned long) uid, (unsigned long) gid);
+
+ return fs->op.chown(path, uid, gid);
+ } else {
+ return -ENOSYS;
+ }
+}
+
+int fuse_fs_truncate(struct fuse_fs *fs, const char *path, off64_t size)
+{
+ fuse_get_context()->private_data = fs->user_data;
+ if (fs->op.truncate) {
+ if (fs->debug)
+ fprintf(stderr, "truncate %s %llu\n", path,
+ (unsigned long long) size);
+
+ return fs->op.truncate(path, size);
+ } else {
+ return -ENOSYS;
+ }
+}
+
+int fuse_fs_ftruncate(struct fuse_fs *fs, const char *path, off64_t size,
+ struct fuse_file_info *fi)
+{
+ fuse_get_context()->private_data = fs->user_data;
+ if (fs->op.ftruncate) {
+ if (fs->debug)
+ fprintf(stderr, "ftruncate[%llu] %llu\n",
+ (unsigned long long) fi->fh,
+ (unsigned long long) size);
+
+ return fs->op.ftruncate(path, size, fi);
+ } else if (path && fs->op.truncate) {
+ if (fs->debug)
+ fprintf(stderr, "truncate %s %llu\n", path,
+ (unsigned long long) size);
+
+ return fs->op.truncate(path, size);
+ } else {
+ return -ENOSYS;
+ }
+}
+
+int fuse_fs_utimens(struct fuse_fs *fs, const char *path,
+ const struct timespec tv[2])
+{
+ fuse_get_context()->private_data = fs->user_data;
+ if (fs->op.utimens) {
+ if (fs->debug)
+ fprintf(stderr, "utimens %s %li.%09lu %li.%09lu\n",
+ path, tv[0].tv_sec, tv[0].tv_nsec,
+ tv[1].tv_sec, tv[1].tv_nsec);
+
+ return fs->op.utimens(path, tv);
+ } else if(fs->op.utime) {
+ struct utimbuf buf;
+
+ if (fs->debug)
+ fprintf(stderr, "utime %s %li %li\n", path,
+ tv[0].tv_sec, tv[1].tv_sec);
+
+ buf.actime = tv[0].tv_sec;
+ buf.modtime = tv[1].tv_sec;
+ return fs->op.utime(path, &buf);
+ } else {
+ return -ENOSYS;
+ }
+}
+
+int fuse_fs_access(struct fuse_fs *fs, const char *path, int mask)
+{
+ fuse_get_context()->private_data = fs->user_data;
+ if (fs->op.access) {
+ if (fs->debug)
+ fprintf(stderr, "access %s 0%o\n", path, mask);
+
+ return fs->op.access(path, mask);
+ } else {
+ return -ENOSYS;
+ }
+}
+
+int fuse_fs_readlink(struct fuse_fs *fs, const char *path, char *buf,
+ size_t len)
+{
+ fuse_get_context()->private_data = fs->user_data;
+ if (fs->op.readlink) {
+ if (fs->debug)
+ fprintf(stderr, "readlink %s %lu\n", path,
+ (unsigned long) len);
+
+ return fs->op.readlink(path, buf, len);
+ } else {
+ return -ENOSYS;
+ }
+}
+
+int fuse_fs_mknod(struct fuse_fs *fs, const char *path, mode_t mode,
+ dev_t rdev)
+{
+ fuse_get_context()->private_data = fs->user_data;
+ if (fs->op.mknod) {
+ if (fs->debug)
+ fprintf(stderr, "mknod %s 0%o 0x%llx umask=0%03o\n",
+ path, mode, (unsigned long long) rdev,
+ fuse_get_context()->umask);
+
+ return fs->op.mknod(path, mode, rdev);
+ } else {
+ return -ENOSYS;
+ }
+}
+
+int fuse_fs_mkdir(struct fuse_fs *fs, const char *path, mode_t mode)
+{
+ fuse_get_context()->private_data = fs->user_data;
+ if (fs->op.mkdir) {
+ if (fs->debug)
+ fprintf(stderr, "mkdir %s 0%o umask=0%03o\n",
+ path, mode, fuse_get_context()->umask);
+
+ return fs->op.mkdir(path, mode);
+ } else {
+ return -ENOSYS;
+ }
+}
+
+int fuse_fs_setxattr(struct fuse_fs *fs, const char *path, const char *name,
+ const char *value, size_t size, int flags)
+{
+ fuse_get_context()->private_data = fs->user_data;
+ if (fs->op.setxattr) {
+ if (fs->debug)
+ fprintf(stderr, "setxattr %s %s %lu 0x%x\n",
+ path, name, (unsigned long) size, flags);
+
+ return fs->op.setxattr(path, name, value, size, flags);
+ } else {
+ return -ENOSYS;
+ }
+}
+
+int fuse_fs_getxattr(struct fuse_fs *fs, const char *path, const char *name,
+ char *value, size_t size)
+{
+ fuse_get_context()->private_data = fs->user_data;
+ if (fs->op.getxattr) {
+ if (fs->debug)
+ fprintf(stderr, "getxattr %s %s %lu\n",
+ path, name, (unsigned long) size);
+
+ return fs->op.getxattr(path, name, value, size);
+ } else {
+ return -ENOSYS;
+ }
+}
+
+int fuse_fs_listxattr(struct fuse_fs *fs, const char *path, char *list,
+ size_t size)
+{
+ fuse_get_context()->private_data = fs->user_data;
+ if (fs->op.listxattr) {
+ if (fs->debug)
+ fprintf(stderr, "listxattr %s %lu\n",
+ path, (unsigned long) size);
+
+ return fs->op.listxattr(path, list, size);
+ } else {
+ return -ENOSYS;
+ }
+}
+
+int fuse_fs_bmap(struct fuse_fs *fs, const char *path, size_t blocksize,
+ uint64_t *idx)
+{
+ fuse_get_context()->private_data = fs->user_data;
+ if (fs->op.bmap) {
+ if (fs->debug)
+ fprintf(stderr, "bmap %s blocksize: %lu index: %llu\n",
+ path, (unsigned long) blocksize,
+ (unsigned long long) *idx);
+
+ return fs->op.bmap(path, blocksize, idx);
+ } else {
+ return -ENOSYS;
+ }
+}
+
+int fuse_fs_removexattr(struct fuse_fs *fs, const char *path, const char *name)
+{
+ fuse_get_context()->private_data = fs->user_data;
+ if (fs->op.removexattr) {
+ if (fs->debug)
+ fprintf(stderr, "removexattr %s %s\n", path, name);
+
+ return fs->op.removexattr(path, name);
+ } else {
+ return -ENOSYS;
+ }
+}
+
+int fuse_fs_ioctl(struct fuse_fs *fs, const char *path, int cmd, void *arg,
+ struct fuse_file_info *fi, unsigned int flags, void *data)
+{
+ fuse_get_context()->private_data = fs->user_data;
+ if (fs->op.ioctl) {
+ if (fs->debug)
+ fprintf(stderr, "ioctl[%llu] 0x%x flags: 0x%x\n",
+ (unsigned long long) fi->fh, cmd, flags);
+
+ return fs->op.ioctl(path, cmd, arg, fi, flags, data);
+ } else
+ return -ENOSYS;
+}
+
+int fuse_fs_poll(struct fuse_fs *fs, const char *path,
+ struct fuse_file_info *fi, struct fuse_pollhandle *ph,
+ unsigned *reventsp)
+{
+ fuse_get_context()->private_data = fs->user_data;
+ if (fs->op.poll) {
+ int res;
+
+ if (fs->debug)
+ fprintf(stderr, "poll[%llu] ph: %p\n",
+ (unsigned long long) fi->fh, ph);
+
+ res = fs->op.poll(path, fi, ph, reventsp);
+
+ if (fs->debug && !res)
+ fprintf(stderr, " poll[%llu] revents: 0x%x\n",
+ (unsigned long long) fi->fh, *reventsp);
+
+ return res;
+ } else
+ return -ENOSYS;
+}
+
+int fuse_fs_fallocate(struct fuse_fs *fs, const char *path, int mode,
+ off64_t offset, off64_t length, struct fuse_file_info *fi)
+{
+ fuse_get_context()->private_data = fs->user_data;
+ if (fs->op.fallocate) {
+ if (fs->debug)
+ fprintf(stderr, "fallocate %s mode %x, offset: %llu, length: %llu\n",
+ path,
+ mode,
+ (unsigned long long) offset,
+ (unsigned long long) length);
+
+ return fs->op.fallocate(path, mode, offset, length, fi);
+ } else
+ return -ENOSYS;
+}
+
+static int is_open(struct fuse *f, fuse_ino_t dir, const char *name)
+{
+ struct node *node;
+ int isopen = 0;
+ pthread_mutex_lock(&f->lock);
+ node = lookup_node(f, dir, name);
+ if (node && node->open_count > 0)
+ isopen = 1;
+ pthread_mutex_unlock(&f->lock);
+ return isopen;
+}
+
+static char *hidden_name(struct fuse *f, fuse_ino_t dir, const char *oldname,
+ char *newname, size_t bufsize)
+{
+ struct stat buf;
+ struct node *node;
+ struct node *newnode;
+ char *newpath;
+ int res;
+ int failctr = 10;
+
+ do {
+ pthread_mutex_lock(&f->lock);
+ node = lookup_node(f, dir, oldname);
+ if (node == NULL) {
+ pthread_mutex_unlock(&f->lock);
+ return NULL;
+ }
+ do {
+ f->hidectr ++;
+ snprintf(newname, bufsize, ".fuse_hidden%08x%08x",
+ (unsigned int) node->nodeid, f->hidectr);
+ newnode = lookup_node(f, dir, newname);
+ } while(newnode);
+
+ res = try_get_path(f, dir, newname, &newpath, NULL, false);
+ pthread_mutex_unlock(&f->lock);
+ if (res)
+ break;
+
+ res = fuse_fs_getattr(f->fs, newpath, &buf);
+ if (res == -ENOENT)
+ break;
+ free(newpath);
+ newpath = NULL;
+ } while(res == 0 && --failctr);
+
+ return newpath;
+}
+
+static int hide_node(struct fuse *f, const char *oldpath,
+ fuse_ino_t dir, const char *oldname)
+{
+ char newname[64];
+ char *newpath;
+ int err = -EBUSY;
+
+ newpath = hidden_name(f, dir, oldname, newname, sizeof(newname));
+ if (newpath) {
+ err = fuse_fs_rename(f->fs, oldpath, newpath);
+ if (!err)
+ err = rename_node(f, dir, oldname, dir, newname, 1);
+ free(newpath);
+ }
+ return err;
+}
+
+static int mtime_eq(const struct stat *stbuf, const struct timespec *ts)
+{
+ return stbuf->st_mtime == ts->tv_sec &&
+ ST_MTIM_NSEC(stbuf) == ts->tv_nsec;
+}
+
+#ifndef CLOCK_MONOTONIC
+#define CLOCK_MONOTONIC CLOCK_REALTIME
+#endif
+
+static void curr_time(struct timespec *now)
+{
+ static clockid_t clockid = CLOCK_MONOTONIC;
+ int res = clock_gettime(clockid, now);
+ if (res == -1 && errno == EINVAL) {
+ clockid = CLOCK_REALTIME;
+ res = clock_gettime(clockid, now);
+ }
+ if (res == -1) {
+ perror("fuse: clock_gettime");
+ abort();
+ }
+}
+
+static void update_stat(struct node *node, const struct stat *stbuf)
+{
+ if (node->cache_valid && (!mtime_eq(stbuf, &node->mtime) ||
+ stbuf->st_size != node->size))
+ node->cache_valid = 0;
+ node->mtime.tv_sec = stbuf->st_mtime;
+ node->mtime.tv_nsec = ST_MTIM_NSEC(stbuf);
+ node->size = stbuf->st_size;
+ curr_time(&node->stat_updated);
+}
+
+static int lookup_path(struct fuse *f, fuse_ino_t nodeid,
+ const char *name, const char *path,
+ struct fuse_entry_param *e, struct fuse_file_info *fi)
+{
+ int res;
+
+ memset(e, 0, sizeof(struct fuse_entry_param));
+ if (fi)
+ res = fuse_fs_fgetattr(f->fs, path, &e->attr, fi);
+ else
+ res = fuse_fs_getattr(f->fs, path, &e->attr);
+ if (res == 0) {
+ struct node *node;
+
+ node = find_node(f, nodeid, name);
+ if (node == NULL)
+ res = -ENOMEM;
+ else {
+ e->ino = node->nodeid;
+ e->generation = node->generation;
+ e->entry_timeout = f->conf.entry_timeout;
+ e->attr_timeout = f->conf.attr_timeout;
+ if (f->conf.auto_cache) {
+ pthread_mutex_lock(&f->lock);
+ update_stat(node, &e->attr);
+ pthread_mutex_unlock(&f->lock);
+ }
+ set_stat(f, e->ino, &e->attr);
+ if (f->conf.debug)
+ fprintf(stderr, " NODEID: %lu\n",
+ (unsigned long) e->ino);
+ }
+ }
+ return res;
+}
+
+static struct fuse_context_i *fuse_get_context_internal(void)
+{
+ struct fuse_context_i *c;
+
+ c = (struct fuse_context_i *) pthread_getspecific(fuse_context_key);
+ if (c == NULL) {
+ c = (struct fuse_context_i *)
+ calloc(1, sizeof(struct fuse_context_i));
+ if (c == NULL) {
+ /* This is hard to deal with properly, so just
+ abort. If memory is so low that the
+ context cannot be allocated, there's not
+ much hope for the filesystem anyway */
+ fprintf(stderr, "fuse: failed to allocate thread specific data\n");
+ abort();
+ }
+ pthread_setspecific(fuse_context_key, c);
+ }
+ return c;
+}
+
+static void fuse_freecontext(void *data)
+{
+ free(data);
+}
+
+static int fuse_create_context_key(void)
+{
+ int err = 0;
+ pthread_mutex_lock(&fuse_context_lock);
+ if (!fuse_context_ref) {
+ err = pthread_key_create(&fuse_context_key, fuse_freecontext);
+ if (err) {
+ fprintf(stderr, "fuse: failed to create thread specific key: %s\n",
+ strerror(err));
+ pthread_mutex_unlock(&fuse_context_lock);
+ return -1;
+ }
+ }
+ fuse_context_ref++;
+ pthread_mutex_unlock(&fuse_context_lock);
+ return 0;
+}
+
+static void fuse_delete_context_key(void)
+{
+ pthread_mutex_lock(&fuse_context_lock);
+ fuse_context_ref--;
+ if (!fuse_context_ref) {
+ free(pthread_getspecific(fuse_context_key));
+ pthread_key_delete(fuse_context_key);
+ }
+ pthread_mutex_unlock(&fuse_context_lock);
+}
+
+static struct fuse *req_fuse_prepare(fuse_req_t req)
+{
+ struct fuse_context_i *c = fuse_get_context_internal();
+ const struct fuse_ctx *ctx = fuse_req_ctx(req);
+ c->req = req;
+ c->ctx.fuse = req_fuse(req);
+ c->ctx.uid = ctx->uid;
+ c->ctx.gid = ctx->gid;
+ c->ctx.pid = ctx->pid;
+ c->ctx.umask = ctx->umask;
+ return c->ctx.fuse;
+}
+
+static inline void reply_err(fuse_req_t req, int err)
+{
+ /* fuse_reply_err() uses non-negated errno values */
+ fuse_reply_err(req, -err);
+}
+
+static void reply_entry(fuse_req_t req, const struct fuse_entry_param *e,
+ int err)
+{
+ if (!err) {
+ struct fuse *f = req_fuse(req);
+ if (fuse_reply_entry(req, e) == -ENOENT) {
+ /* Skip forget for negative result */
+ if (e->ino != 0)
+ forget_node(f, e->ino, 1);
+ }
+ } else
+ reply_err(req, err);
+}
+
+void fuse_fs_init(struct fuse_fs *fs, struct fuse_conn_info *conn)
+{
+ fuse_get_context()->private_data = fs->user_data;
+ if (!fs->op.write_buf)
+ conn->want &= ~FUSE_CAP_SPLICE_READ;
+ if (!fs->op.lock)
+ conn->want &= ~FUSE_CAP_POSIX_LOCKS;
+ if (!fs->op.flock)
+ conn->want &= ~FUSE_CAP_FLOCK_LOCKS;
+ if (fs->op.init)
+ fs->user_data = fs->op.init(conn);
+}
+
+static void fuse_lib_init(void *data, struct fuse_conn_info *conn)
+{
+ struct fuse *f = (struct fuse *) data;
+ struct fuse_context_i *c = fuse_get_context_internal();
+
+ memset(c, 0, sizeof(*c));
+ c->ctx.fuse = f;
+ conn->want |= FUSE_CAP_EXPORT_SUPPORT;
+ fuse_fs_init(f->fs, conn);
+}
+
+void fuse_fs_destroy(struct fuse_fs *fs)
+{
+ fuse_get_context()->private_data = fs->user_data;
+ if (fs->op.destroy)
+ fs->op.destroy(fs->user_data);
+ if (fs->m)
+ fuse_put_module(fs->m);
+ free(fs);
+}
+
+static void fuse_lib_destroy(void *data)
+{
+ struct fuse *f = (struct fuse *) data;
+ struct fuse_context_i *c = fuse_get_context_internal();
+
+ memset(c, 0, sizeof(*c));
+ c->ctx.fuse = f;
+ fuse_fs_destroy(f->fs);
+ f->fs = NULL;
+}
+
+static void fuse_lib_lookup(fuse_req_t req, fuse_ino_t parent,
+ const char *name)
+{
+ struct fuse *f = req_fuse_prepare(req);
+ 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) {
+ struct fuse_intr_data d;
+ if (f->conf.debug)
+ fprintf(stderr, "LOOKUP %s\n", path);
+ fuse_prepare_interrupt(f, req, &d);
+ err = lookup_path(f, parent, name, path, &e, NULL);
+ if (err == -ENOENT && f->conf.negative_timeout != 0.0) {
+ e.ino = 0;
+ e.entry_timeout = f->conf.negative_timeout;
+ err = 0;
+ }
+ 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);
+}
+
+static void do_forget(struct fuse *f, fuse_ino_t ino, uint64_t nlookup)
+{
+ if (f->conf.debug)
+ fprintf(stderr, "FORGET %llu/%llu\n", (unsigned long long)ino,
+ (unsigned long long) nlookup);
+ forget_node(f, ino, nlookup);
+}
+
+static void fuse_lib_forget(fuse_req_t req, fuse_ino_t ino,
+ unsigned long nlookup)
+{
+ do_forget(req_fuse(req), ino, nlookup);
+ fuse_reply_none(req);
+}
+
+static void fuse_lib_forget_multi(fuse_req_t req, size_t count,
+ struct fuse_forget_data *forgets)
+{
+ struct fuse *f = req_fuse(req);
+ size_t i;
+
+ for (i = 0; i < count; i++)
+ do_forget(f, forgets[i].ino, forgets[i].nlookup);
+
+ fuse_reply_none(req);
+}
+
+
+static void fuse_lib_getattr(fuse_req_t req, fuse_ino_t ino,
+ struct fuse_file_info *fi)
+{
+ struct fuse *f = req_fuse_prepare(req);
+ struct stat buf;
+ char *path;
+ int err;
+
+ memset(&buf, 0, sizeof(buf));
+
+ if (fi != NULL && f->fs->op.fgetattr)
+ err = get_path_nullok(f, ino, &path);
+ else
+ err = get_path(f, ino, &path);
+ if (!err) {
+ struct fuse_intr_data d;
+ fuse_prepare_interrupt(f, req, &d);
+ if (fi)
+ err = fuse_fs_fgetattr(f->fs, path, &buf, fi);
+ else
+ err = fuse_fs_getattr(f->fs, path, &buf);
+ fuse_finish_interrupt(f, req, &d);
+ free_path(f, ino, path);
+ }
+ if (!err) {
+ struct node *node;
+
+ pthread_mutex_lock(&f->lock);
+ node = get_node(f, ino);
+ if (node->is_hidden && buf.st_nlink > 0)
+ buf.st_nlink--;
+ if (f->conf.auto_cache)
+ update_stat(node, &buf);
+ pthread_mutex_unlock(&f->lock);
+ set_stat(f, ino, &buf);
+ fuse_reply_attr(req, &buf, f->conf.attr_timeout);
+ } else
+ reply_err(req, err);
+}
+
+int fuse_fs_chmod(struct fuse_fs *fs, const char *path, mode_t mode)
+{
+ fuse_get_context()->private_data = fs->user_data;
+ if (fs->op.chmod)
+ return fs->op.chmod(path, mode);
+ else
+ return -ENOSYS;
+}
+
+static void fuse_lib_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr,
+ int valid, struct fuse_file_info *fi)
+{
+ struct fuse *f = req_fuse_prepare(req);
+ struct stat buf;
+ char *path;
+ int err;
+
+ if (valid == FUSE_SET_ATTR_SIZE && fi != NULL &&
+ f->fs->op.ftruncate && f->fs->op.fgetattr)
+ err = get_path_nullok(f, ino, &path);
+ else
+ err = get_path(f, ino, &path);
+ if (!err) {
+ struct fuse_intr_data d;
+ fuse_prepare_interrupt(f, req, &d);
+ err = 0;
+ if (!err && (valid & FUSE_SET_ATTR_MODE))
+ err = fuse_fs_chmod(f->fs, path, attr->st_mode);
+ if (!err && (valid & (FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID))) {
+ uid_t uid = (valid & FUSE_SET_ATTR_UID) ?
+ attr->st_uid : (uid_t) -1;
+ gid_t gid = (valid & FUSE_SET_ATTR_GID) ?
+ attr->st_gid : (gid_t) -1;
+ err = fuse_fs_chown(f->fs, path, uid, gid);
+ }
+ if (!err && (valid & FUSE_SET_ATTR_SIZE)) {
+ if (fi)
+ err = fuse_fs_ftruncate(f->fs, path,
+ attr->st_size, fi);
+ else
+ err = fuse_fs_truncate(f->fs, path,
+ attr->st_size);
+ }
+#ifdef HAVE_UTIMENSAT
+ if (!err && f->utime_omit_ok &&
+ (valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME))) {
+ struct timespec tv[2];
+
+ tv[0].tv_sec = 0;
+ tv[1].tv_sec = 0;
+ tv[0].tv_nsec = UTIME_OMIT;
+ tv[1].tv_nsec = UTIME_OMIT;
+
+ if (valid & FUSE_SET_ATTR_ATIME_NOW)
+ tv[0].tv_nsec = UTIME_NOW;
+ else if (valid & FUSE_SET_ATTR_ATIME)
+ tv[0] = attr->st_atim;
+
+ if (valid & FUSE_SET_ATTR_MTIME_NOW)
+ tv[1].tv_nsec = UTIME_NOW;
+ else if (valid & FUSE_SET_ATTR_MTIME)
+ tv[1] = attr->st_mtim;
+
+ err = fuse_fs_utimens(f->fs, path, tv);
+ } else
+#endif
+ if (!err &&
+ (valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) ==
+ (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) {
+ struct timespec tv[2];
+ tv[0].tv_sec = attr->st_atime;
+ tv[0].tv_nsec = ST_ATIM_NSEC(attr);
+ tv[1].tv_sec = attr->st_mtime;
+ tv[1].tv_nsec = ST_MTIM_NSEC(attr);
+ err = fuse_fs_utimens(f->fs, path, tv);
+ }
+ if (!err) {
+ if (fi)
+ err = fuse_fs_fgetattr(f->fs, path, &buf, fi);
+ else
+ err = fuse_fs_getattr(f->fs, path, &buf);
+ }
+ fuse_finish_interrupt(f, req, &d);
+ free_path(f, ino, path);
+ }
+ if (!err) {
+ if (f->conf.auto_cache) {
+ pthread_mutex_lock(&f->lock);
+ update_stat(get_node(f, ino), &buf);
+ pthread_mutex_unlock(&f->lock);
+ }
+ set_stat(f, ino, &buf);
+ fuse_reply_attr(req, &buf, f->conf.attr_timeout);
+ } else
+ reply_err(req, err);
+}
+
+static void fuse_lib_access(fuse_req_t req, fuse_ino_t ino, int mask)
+{
+ struct fuse *f = req_fuse_prepare(req);
+ char *path;
+ int err;
+
+ err = get_path(f, ino, &path);
+ if (!err) {
+ struct fuse_intr_data d;
+
+ fuse_prepare_interrupt(f, req, &d);
+ err = fuse_fs_access(f->fs, path, mask);
+ fuse_finish_interrupt(f, req, &d);
+ free_path(f, ino, path);
+ }
+ reply_err(req, err);
+}
+
+static void fuse_lib_readlink(fuse_req_t req, fuse_ino_t ino)
+{
+ struct fuse *f = req_fuse_prepare(req);
+ char linkname[PATH_MAX + 1];
+ char *path;
+ int err;
+
+ err = get_path(f, ino, &path);
+ if (!err) {
+ struct fuse_intr_data d;
+ fuse_prepare_interrupt(f, req, &d);
+ err = fuse_fs_readlink(f->fs, path, linkname, sizeof(linkname));
+ fuse_finish_interrupt(f, req, &d);
+ free_path(f, ino, path);
+ }
+ if (!err) {
+ linkname[PATH_MAX] = '\0';
+ fuse_reply_readlink(req, linkname);
+ } else
+ reply_err(req, err);
+}
+
+static void fuse_lib_mknod(fuse_req_t req, fuse_ino_t parent, const char *name,
+ mode_t mode, dev_t rdev)
+{
+ struct fuse *f = req_fuse_prepare(req);
+ struct fuse_entry_param e;
+ char *path;
+ int err;
+
+ err = get_path_name(f, parent, name, &path);
+ if (!err) {
+ struct fuse_intr_data d;
+
+ fuse_prepare_interrupt(f, req, &d);
+ err = -ENOSYS;
+ if (S_ISREG(mode)) {
+ struct fuse_file_info fi;
+
+ memset(&fi, 0, sizeof(fi));
+ fi.flags = O_CREAT | O_EXCL | O_WRONLY;
+ err = fuse_fs_create(f->fs, path, mode, &fi);
+ if (!err) {
+ err = lookup_path(f, parent, name, path, &e,
+ &fi);
+ fuse_fs_release(f->fs, path, &fi);
+ }
+ }
+ if (err == -ENOSYS) {
+ err = fuse_fs_mknod(f->fs, path, mode, rdev);
+ if (!err)
+ err = lookup_path(f, parent, name, path, &e,
+ NULL);
+ }
+ fuse_finish_interrupt(f, req, &d);
+ free_path(f, parent, path);
+ }
+ reply_entry(req, &e, err);
+}
+
+static void fuse_lib_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name,
+ mode_t mode)
+{
+ struct fuse *f = req_fuse_prepare(req);
+ struct fuse_entry_param e;
+ char *path;
+ int err;
+
+ err = get_path_name(f, parent, name, &path);
+ if (!err) {
+ struct fuse_intr_data d;
+
+ fuse_prepare_interrupt(f, req, &d);
+ err = fuse_fs_mkdir(f->fs, path, mode);
+ if (!err)
+ err = lookup_path(f, parent, name, path, &e, NULL);
+ fuse_finish_interrupt(f, req, &d);
+ free_path(f, parent, path);
+ }
+ reply_entry(req, &e, err);
+}
+
+static void fuse_lib_unlink(fuse_req_t req, fuse_ino_t parent,
+ const char *name)
+{
+ struct fuse *f = req_fuse_prepare(req);
+ struct node *wnode;
+ char *path;
+ int err;
+
+ err = get_path_wrlock(f, parent, name, &path, &wnode);
+ if (!err) {
+ struct fuse_intr_data d;
+
+ fuse_prepare_interrupt(f, req, &d);
+ if (!f->conf.hard_remove && is_open(f, parent, name)) {
+ err = hide_node(f, path, parent, name);
+ } else {
+ err = fuse_fs_unlink(f->fs, path);
+ if (!err)
+ remove_node(f, parent, name);
+ }
+ fuse_finish_interrupt(f, req, &d);
+ free_path_wrlock(f, parent, wnode, path);
+ }
+ reply_err(req, err);
+}
+
+static void fuse_lib_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name)
+{
+ struct fuse *f = req_fuse_prepare(req);
+ struct node *wnode;
+ char *path;
+ int err;
+
+ err = get_path_wrlock(f, parent, name, &path, &wnode);
+ if (!err) {
+ struct fuse_intr_data d;
+
+ fuse_prepare_interrupt(f, req, &d);
+ err = fuse_fs_rmdir(f->fs, path);
+ fuse_finish_interrupt(f, req, &d);
+ if (!err)
+ remove_node(f, parent, name);
+ free_path_wrlock(f, parent, wnode, path);
+ }
+ reply_err(req, err);
+}
+
+static void fuse_lib_symlink(fuse_req_t req, const char *linkname,
+ fuse_ino_t parent, const char *name)
+{
+ struct fuse *f = req_fuse_prepare(req);
+ struct fuse_entry_param e;
+ char *path;
+ int err;
+
+ err = get_path_name(f, parent, name, &path);
+ if (!err) {
+ struct fuse_intr_data d;
+
+ fuse_prepare_interrupt(f, req, &d);
+ err = fuse_fs_symlink(f->fs, linkname, path);
+ if (!err)
+ err = lookup_path(f, parent, name, path, &e, NULL);
+ fuse_finish_interrupt(f, req, &d);
+ free_path(f, parent, path);
+ }
+ reply_entry(req, &e, err);
+}
+
+static void fuse_lib_rename(fuse_req_t req, fuse_ino_t olddir,
+ const char *oldname, fuse_ino_t newdir,
+ const char *newname)
+{
+ struct fuse *f = req_fuse_prepare(req);
+ char *oldpath;
+ char *newpath;
+ struct node *wnode1;
+ struct node *wnode2;
+ int err;
+
+ err = get_path2(f, olddir, oldname, newdir, newname,
+ &oldpath, &newpath, &wnode1, &wnode2);
+ if (!err) {
+ struct fuse_intr_data d;
+ err = 0;
+ fuse_prepare_interrupt(f, req, &d);
+ if (!f->conf.hard_remove && is_open(f, newdir, newname))
+ err = hide_node(f, newpath, newdir, newname);
+ if (!err) {
+ err = fuse_fs_rename(f->fs, oldpath, newpath);
+ if (!err)
+ err = rename_node(f, olddir, oldname, newdir,
+ newname, 0);
+ }
+ fuse_finish_interrupt(f, req, &d);
+ free_path2(f, olddir, newdir, wnode1, wnode2, oldpath, newpath);
+ }
+ reply_err(req, err);
+}
+
+static void fuse_lib_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent,
+ const char *newname)
+{
+ struct fuse *f = req_fuse_prepare(req);
+ struct fuse_entry_param e;
+ char *oldpath;
+ char *newpath;
+ int err;
+
+ err = get_path2(f, ino, NULL, newparent, newname,
+ &oldpath, &newpath, NULL, NULL);
+ if (!err) {
+ struct fuse_intr_data d;
+
+ fuse_prepare_interrupt(f, req, &d);
+ err = fuse_fs_link(f->fs, oldpath, newpath);
+ if (!err)
+ err = lookup_path(f, newparent, newname, newpath,
+ &e, NULL);
+ fuse_finish_interrupt(f, req, &d);
+ free_path2(f, ino, newparent, NULL, NULL, oldpath, newpath);
+ }
+ reply_entry(req, &e, err);
+}
+
+static void fuse_do_release(struct fuse *f, fuse_ino_t ino, const char *path,
+ struct fuse_file_info *fi)
+{
+ struct node *node;
+ int unlink_hidden = 0;
+ const char *compatpath;
+
+ if (path != NULL || f->nullpath_ok || f->conf.nopath)
+ compatpath = path;
+ else
+ compatpath = "-";
+
+ fuse_fs_release(f->fs, compatpath, fi);
+
+ pthread_mutex_lock(&f->lock);
+ node = get_node(f, ino);
+ assert(node->open_count > 0);
+ --node->open_count;
+ if (node->is_hidden && !node->open_count) {
+ unlink_hidden = 1;
+ node->is_hidden = 0;
+ }
+ pthread_mutex_unlock(&f->lock);
+
+ if(unlink_hidden) {
+ if (path) {
+ fuse_fs_unlink(f->fs, path);
+ } else if (f->conf.nopath) {
+ char *unlinkpath;
+
+ if (get_path(f, ino, &unlinkpath) == 0)
+ fuse_fs_unlink(f->fs, unlinkpath);
+
+ free_path(f, ino, unlinkpath);
+ }
+ }
+}
+
+static void fuse_lib_create(fuse_req_t req, fuse_ino_t parent,
+ const char *name, mode_t mode,
+ struct fuse_file_info *fi)
+{
+ struct fuse *f = req_fuse_prepare(req);
+ struct fuse_intr_data d;
+ struct fuse_entry_param e;
+ char *path;
+ int err;
+
+ err = get_path_name(f, parent, name, &path);
+ if (!err) {
+ fuse_prepare_interrupt(f, req, &d);
+ err = fuse_fs_create(f->fs, path, mode, fi);
+ if (!err) {
+ err = lookup_path(f, parent, name, path, &e, fi);
+ if (err)
+ fuse_fs_release(f->fs, path, fi);
+ else if (!S_ISREG(e.attr.st_mode)) {
+ err = -EIO;
+ fuse_fs_release(f->fs, path, fi);
+ forget_node(f, e.ino, 1);
+ } else {
+ if (f->conf.direct_io)
+ fi->direct_io = 1;
+ if (f->conf.kernel_cache)
+ fi->keep_cache = 1;
+
+ }
+ }
+ fuse_finish_interrupt(f, req, &d);
+ }
+ if (!err) {
+ pthread_mutex_lock(&f->lock);
+ get_node(f, e.ino)->open_count++;
+ pthread_mutex_unlock(&f->lock);
+ if (fuse_reply_create(req, &e, fi) == -ENOENT) {
+ /* The open syscall was interrupted, so it
+ must be cancelled */
+ fuse_do_release(f, e.ino, path, fi);
+ forget_node(f, e.ino, 1);
+ }
+ } else {
+ reply_err(req, err);
+ }
+
+ free_path(f, parent, path);
+}
+
+static double diff_timespec(const struct timespec *t1,
+ const struct timespec *t2)
+{
+ return (t1->tv_sec - t2->tv_sec) +
+ ((double) t1->tv_nsec - (double) t2->tv_nsec) / 1000000000.0;
+}
+
+static void open_auto_cache(struct fuse *f, fuse_ino_t ino, const char *path,
+ struct fuse_file_info *fi)
+{
+ struct node *node;
+
+ pthread_mutex_lock(&f->lock);
+ node = get_node(f, ino);
+ if (node->cache_valid) {
+ struct timespec now;
+
+ curr_time(&now);
+ if (diff_timespec(&now, &node->stat_updated) >
+ f->conf.ac_attr_timeout) {
+ struct stat stbuf;
+ int err;
+ pthread_mutex_unlock(&f->lock);
+ err = fuse_fs_fgetattr(f->fs, path, &stbuf, fi);
+ pthread_mutex_lock(&f->lock);
+ if (!err)
+ update_stat(node, &stbuf);
+ else
+ node->cache_valid = 0;
+ }
+ }
+ if (node->cache_valid)
+ fi->keep_cache = 1;
+
+ node->cache_valid = 1;
+ pthread_mutex_unlock(&f->lock);
+}
+
+static void fuse_lib_open(fuse_req_t req, fuse_ino_t ino,
+ struct fuse_file_info *fi)
+{
+ struct fuse *f = req_fuse_prepare(req);
+ struct fuse_intr_data d;
+ char *path;
+ int err;
+
+ err = get_path(f, ino, &path);
+ if (!err) {
+ fuse_prepare_interrupt(f, req, &d);
+ err = fuse_fs_open(f->fs, path, fi);
+ if (!err) {
+ if (f->conf.direct_io)
+ fi->direct_io = 1;
+ if (f->conf.kernel_cache)
+ fi->keep_cache = 1;
+
+ if (f->conf.auto_cache)
+ open_auto_cache(f, ino, path, fi);
+ }
+ fuse_finish_interrupt(f, req, &d);
+ }
+ if (!err) {
+ pthread_mutex_lock(&f->lock);
+ get_node(f, ino)->open_count++;
+ pthread_mutex_unlock(&f->lock);
+ if (fuse_reply_open(req, fi) == -ENOENT) {
+ /* The open syscall was interrupted, so it
+ must be cancelled */
+ fuse_do_release(f, ino, path, fi);
+ }
+ } else
+ reply_err(req, err);
+
+ free_path(f, ino, path);
+}
+
+static void fuse_lib_read(fuse_req_t req, fuse_ino_t ino, size_t size,
+ off64_t off, struct fuse_file_info *fi)
+{
+ struct fuse *f = req_fuse_prepare(req);
+ struct fuse_bufvec *buf = NULL;
+ char *path;
+ int res;
+
+ res = get_path_nullok(f, ino, &path);
+ if (res == 0) {
+ struct fuse_intr_data d;
+
+ fuse_prepare_interrupt(f, req, &d);
+ res = fuse_fs_read_buf(f->fs, path, &buf, size, off, fi);
+ fuse_finish_interrupt(f, req, &d);
+ free_path(f, ino, path);
+ }
+
+ if (res == 0)
+ fuse_reply_data(req, buf, FUSE_BUF_SPLICE_MOVE);
+ else
+ reply_err(req, res);
+
+ fuse_free_buf(buf);
+}
+
+static void fuse_lib_write_buf(fuse_req_t req, fuse_ino_t ino,
+ struct fuse_bufvec *buf, off64_t off,
+ struct fuse_file_info *fi)
+{
+ struct fuse *f = req_fuse_prepare(req);
+ char *path;
+ int res;
+
+ res = get_path_nullok(f, ino, &path);
+ if (res == 0) {
+ struct fuse_intr_data d;
+
+ fuse_prepare_interrupt(f, req, &d);
+ res = fuse_fs_write_buf(f->fs, path, buf, off, fi);
+ fuse_finish_interrupt(f, req, &d);
+ free_path(f, ino, path);
+ }
+
+ if (res >= 0)
+ fuse_reply_write(req, res);
+ else
+ reply_err(req, res);
+}
+
+static void fuse_lib_fsync(fuse_req_t req, fuse_ino_t ino, int datasync,
+ struct fuse_file_info *fi)
+{
+ struct fuse *f = req_fuse_prepare(req);
+ char *path;
+ int err;
+
+ err = get_path_nullok(f, ino, &path);
+ if (!err) {
+ struct fuse_intr_data d;
+
+ fuse_prepare_interrupt(f, req, &d);
+ err = fuse_fs_fsync(f->fs, path, datasync, fi);
+ fuse_finish_interrupt(f, req, &d);
+ free_path(f, ino, path);
+ }
+ reply_err(req, err);
+}
+
+static struct fuse_dh *get_dirhandle(const struct fuse_file_info *llfi,
+ struct fuse_file_info *fi)
+{
+ struct fuse_dh *dh = (struct fuse_dh *) (uintptr_t) llfi->fh;
+ memset(fi, 0, sizeof(struct fuse_file_info));
+ fi->fh = dh->fh;
+ fi->fh_old = dh->fh;
+ return dh;
+}
+
+static void fuse_lib_opendir(fuse_req_t req, fuse_ino_t ino,
+ struct fuse_file_info *llfi)
+{
+ struct fuse *f = req_fuse_prepare(req);
+ struct fuse_intr_data d;
+ struct fuse_dh *dh;
+ struct fuse_file_info fi;
+ char *path;
+ int err;
+
+ dh = (struct fuse_dh *) malloc(sizeof(struct fuse_dh));
+ if (dh == NULL) {
+ reply_err(req, -ENOMEM);
+ return;
+ }
+ memset(dh, 0, sizeof(struct fuse_dh));
+ dh->fuse = f;
+ dh->contents = NULL;
+ dh->len = 0;
+ dh->filled = 0;
+ dh->nodeid = ino;
+ fuse_mutex_init(&dh->lock);
+
+ llfi->fh = (uintptr_t) dh;
+
+ memset(&fi, 0, sizeof(fi));
+ fi.flags = llfi->flags;
+
+ err = get_path(f, ino, &path);
+ if (!err) {
+ fuse_prepare_interrupt(f, req, &d);
+ err = fuse_fs_opendir(f->fs, path, &fi);
+ fuse_finish_interrupt(f, req, &d);
+ dh->fh = fi.fh;
+ }
+ if (!err) {
+ if (fuse_reply_open(req, llfi) == -ENOENT) {
+ /* The opendir syscall was interrupted, so it
+ must be cancelled */
+ fuse_fs_releasedir(f->fs, path, &fi);
+ pthread_mutex_destroy(&dh->lock);
+ free(dh);
+ }
+ } else {
+ reply_err(req, err);
+ pthread_mutex_destroy(&dh->lock);
+ free(dh);
+ }
+ free_path(f, ino, path);
+}
+
+static int extend_contents(struct fuse_dh *dh, unsigned minsize)
+{
+ if (minsize > dh->size) {
+ char *newptr;
+ unsigned newsize = dh->size;
+ if (!newsize)
+ newsize = 1024;
+ while (newsize < minsize) {
+ if (newsize >= 0x80000000)
+ newsize = 0xffffffff;
+ else
+ newsize *= 2;
+ }
+
+ newptr = (char *) realloc(dh->contents, newsize);
+ if (!newptr) {
+ dh->error = -ENOMEM;
+ return -1;
+ }
+ dh->contents = newptr;
+ dh->size = newsize;
+ }
+ return 0;
+}
+
+static int fill_dir(void *dh_, const char *name, const struct stat *statp,
+ off64_t off)
+{
+ struct fuse_dh *dh = (struct fuse_dh *) dh_;
+ struct stat stbuf;
+ size_t newlen;
+
+ if (statp)
+ stbuf = *statp;
+ else {
+ memset(&stbuf, 0, sizeof(stbuf));
+ stbuf.st_ino = FUSE_UNKNOWN_INO;
+ }
+
+ if (!dh->fuse->conf.use_ino) {
+ stbuf.st_ino = FUSE_UNKNOWN_INO;
+ if (dh->fuse->conf.readdir_ino) {
+ struct node *node;
+ pthread_mutex_lock(&dh->fuse->lock);
+ node = lookup_node(dh->fuse, dh->nodeid, name);
+ if (node)
+ stbuf.st_ino = (ino_t) node->nodeid;
+ pthread_mutex_unlock(&dh->fuse->lock);
+ }
+ }
+
+ if (off) {
+ if (extend_contents(dh, dh->needlen) == -1)
+ return 1;
+
+ dh->filled = 0;
+ newlen = dh->len +
+ fuse_add_direntry(dh->req, dh->contents + dh->len,
+ dh->needlen - dh->len, name,
+ &stbuf, off);
+ if (newlen > dh->needlen)
+ return 1;
+ } else {
+ newlen = dh->len +
+ fuse_add_direntry(dh->req, NULL, 0, name, NULL, 0);
+ if (extend_contents(dh, newlen) == -1)
+ return 1;
+
+ fuse_add_direntry(dh->req, dh->contents + dh->len,
+ dh->size - dh->len, name, &stbuf, newlen);
+ }
+ dh->len = newlen;
+ return 0;
+}
+
+static int readdir_fill(struct fuse *f, fuse_req_t req, fuse_ino_t ino,
+ size_t size, off64_t off, struct fuse_dh *dh,
+ struct fuse_file_info *fi)
+{
+ char *path;
+ int err;
+
+ if (f->fs->op.readdir)
+ err = get_path_nullok(f, ino, &path);
+ else
+ err = get_path(f, ino, &path);
+ if (!err) {
+ struct fuse_intr_data d;
+
+ dh->len = 0;
+ dh->error = 0;
+ dh->needlen = size;
+ dh->filled = 1;
+ dh->req = req;
+ fuse_prepare_interrupt(f, req, &d);
+ err = fuse_fs_readdir(f->fs, path, dh, fill_dir, off, fi);
+ fuse_finish_interrupt(f, req, &d);
+ dh->req = NULL;
+ if (!err)
+ err = dh->error;
+ if (err)
+ dh->filled = 0;
+ free_path(f, ino, path);
+ }
+ return err;
+}
+
+static void fuse_lib_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
+ off64_t off, struct fuse_file_info *llfi)
+{
+ struct fuse *f = req_fuse_prepare(req);
+ struct fuse_file_info fi;
+ struct fuse_dh *dh = get_dirhandle(llfi, &fi);
+
+ pthread_mutex_lock(&dh->lock);
+ /* According to SUS, directory contents need to be refreshed on
+ rewinddir() */
+ if (!off)
+ dh->filled = 0;
+
+ if (!dh->filled) {
+ int err = readdir_fill(f, req, ino, size, off, dh, &fi);
+ if (err) {
+ reply_err(req, err);
+ goto out;
+ }
+ }
+ if (dh->filled) {
+ if (off < dh->len) {
+ if (off + size > dh->len)
+ size = dh->len - off;
+ } else
+ size = 0;
+ } else {
+ size = dh->len;
+ off = 0;
+ }
+ fuse_reply_buf(req, dh->contents + off, size);
+out:
+ pthread_mutex_unlock(&dh->lock);
+}
+
+static void fuse_lib_releasedir(fuse_req_t req, fuse_ino_t ino,
+ struct fuse_file_info *llfi)
+{
+ struct fuse *f = req_fuse_prepare(req);
+ struct fuse_intr_data d;
+ struct fuse_file_info fi;
+ struct fuse_dh *dh = get_dirhandle(llfi, &fi);
+ char *path;
+ const char *compatpath;
+
+ get_path_nullok(f, ino, &path);
+ if (path != NULL || f->nullpath_ok || f->conf.nopath)
+ compatpath = path;
+ else
+ compatpath = "-";
+
+ fuse_prepare_interrupt(f, req, &d);
+ fuse_fs_releasedir(f->fs, compatpath, &fi);
+ fuse_finish_interrupt(f, req, &d);
+ free_path(f, ino, path);
+
+ pthread_mutex_lock(&dh->lock);
+ pthread_mutex_unlock(&dh->lock);
+ pthread_mutex_destroy(&dh->lock);
+ free(dh->contents);
+ free(dh);
+ reply_err(req, 0);
+}
+
+static void fuse_lib_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync,
+ struct fuse_file_info *llfi)
+{
+ struct fuse *f = req_fuse_prepare(req);
+ struct fuse_file_info fi;
+ char *path;
+ int err;
+
+ get_dirhandle(llfi, &fi);
+
+ err = get_path_nullok(f, ino, &path);
+ if (!err) {
+ struct fuse_intr_data d;
+ fuse_prepare_interrupt(f, req, &d);
+ err = fuse_fs_fsyncdir(f->fs, path, datasync, &fi);
+ fuse_finish_interrupt(f, req, &d);
+ free_path(f, ino, path);
+ }
+ reply_err(req, err);
+}
+
+static void fuse_lib_statfs(fuse_req_t req, fuse_ino_t ino)
+{
+ struct fuse *f = req_fuse_prepare(req);
+ struct statvfs buf;
+ char *path = NULL;
+ int err = 0;
+
+ memset(&buf, 0, sizeof(buf));
+ if (ino)
+ err = get_path(f, ino, &path);
+
+ if (!err) {
+ struct fuse_intr_data d;
+ fuse_prepare_interrupt(f, req, &d);
+ err = fuse_fs_statfs(f->fs, path ? path : "/", &buf);
+ fuse_finish_interrupt(f, req, &d);
+ free_path(f, ino, path);
+ }
+
+ if (!err)
+ fuse_reply_statfs(req, &buf);
+ else
+ reply_err(req, err);
+}
+
+static void fuse_lib_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
+ const char *value, size_t size, int flags)
+{
+ struct fuse *f = req_fuse_prepare(req);
+ char *path;
+ int err;
+
+ err = get_path(f, ino, &path);
+ if (!err) {
+ struct fuse_intr_data d;
+ fuse_prepare_interrupt(f, req, &d);
+ err = fuse_fs_setxattr(f->fs, path, name, value, size, flags);
+ fuse_finish_interrupt(f, req, &d);
+ free_path(f, ino, path);
+ }
+ reply_err(req, err);
+}
+
+static int common_getxattr(struct fuse *f, fuse_req_t req, fuse_ino_t ino,
+ const char *name, char *value, size_t size)
+{
+ int err;
+ char *path;
+
+ err = get_path(f, ino, &path);
+ if (!err) {
+ struct fuse_intr_data d;
+ fuse_prepare_interrupt(f, req, &d);
+ err = fuse_fs_getxattr(f->fs, path, name, value, size);
+ fuse_finish_interrupt(f, req, &d);
+ free_path(f, ino, path);
+ }
+ return err;
+}
+
+static void fuse_lib_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
+ size_t size)
+{
+ struct fuse *f = req_fuse_prepare(req);
+ int res;
+
+ if (size) {
+ char *value = (char *) malloc(size);
+ if (value == NULL) {
+ reply_err(req, -ENOMEM);
+ return;
+ }
+ res = common_getxattr(f, req, ino, name, value, size);
+ if (res > 0)
+ fuse_reply_buf(req, value, res);
+ else
+ reply_err(req, res);
+ free(value);
+ } else {
+ res = common_getxattr(f, req, ino, name, NULL, 0);
+ if (res >= 0)
+ fuse_reply_xattr(req, res);
+ else
+ reply_err(req, res);
+ }
+}
+
+static int common_listxattr(struct fuse *f, fuse_req_t req, fuse_ino_t ino,
+ char *list, size_t size)
+{
+ char *path;
+ int err;
+
+ err = get_path(f, ino, &path);
+ if (!err) {
+ struct fuse_intr_data d;
+ fuse_prepare_interrupt(f, req, &d);
+ err = fuse_fs_listxattr(f->fs, path, list, size);
+ fuse_finish_interrupt(f, req, &d);
+ free_path(f, ino, path);
+ }
+ return err;
+}
+
+static void fuse_lib_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size)
+{
+ struct fuse *f = req_fuse_prepare(req);
+ int res;
+
+ if (size) {
+ char *list = (char *) malloc(size);
+ if (list == NULL) {
+ reply_err(req, -ENOMEM);
+ return;
+ }
+ res = common_listxattr(f, req, ino, list, size);
+ if (res > 0)
+ fuse_reply_buf(req, list, res);
+ else
+ reply_err(req, res);
+ free(list);
+ } else {
+ res = common_listxattr(f, req, ino, NULL, 0);
+ if (res >= 0)
+ fuse_reply_xattr(req, res);
+ else
+ reply_err(req, res);
+ }
+}
+
+static void fuse_lib_removexattr(fuse_req_t req, fuse_ino_t ino,
+ const char *name)
+{
+ struct fuse *f = req_fuse_prepare(req);
+ char *path;
+ int err;
+
+ err = get_path(f, ino, &path);
+ if (!err) {
+ struct fuse_intr_data d;
+ fuse_prepare_interrupt(f, req, &d);
+ err = fuse_fs_removexattr(f->fs, path, name);
+ fuse_finish_interrupt(f, req, &d);
+ free_path(f, ino, path);
+ }
+ reply_err(req, err);
+}
+
+static struct lock *locks_conflict(struct node *node, const struct lock *lock)
+{
+ struct lock *l;
+
+ for (l = node->locks; l; l = l->next)
+ if (l->owner != lock->owner &&
+ lock->start <= l->end && l->start <= lock->end &&
+ (l->type == F_WRLCK || lock->type == F_WRLCK))
+ break;
+
+ return l;
+}
+
+static void delete_lock(struct lock **lockp)
+{
+ struct lock *l = *lockp;
+ *lockp = l->next;
+ free(l);
+}
+
+static void insert_lock(struct lock **pos, struct lock *lock)
+{
+ lock->next = *pos;
+ *pos = lock;
+}
+
+static int locks_insert(struct node *node, struct lock *lock)
+{
+ struct lock **lp;
+ struct lock *newl1 = NULL;
+ struct lock *newl2 = NULL;
+
+ if (lock->type != F_UNLCK || lock->start != 0 ||
+ lock->end != OFFSET_MAX) {
+ newl1 = malloc(sizeof(struct lock));
+ newl2 = malloc(sizeof(struct lock));
+
+ if (!newl1 || !newl2) {
+ free(newl1);
+ free(newl2);
+ return -ENOLCK;
+ }
+ }
+
+ for (lp = &node->locks; *lp;) {
+ struct lock *l = *lp;
+ if (l->owner != lock->owner)
+ goto skip;
+
+ if (lock->type == l->type) {
+ if (l->end < lock->start - 1)
+ goto skip;
+ if (lock->end < l->start - 1)
+ break;
+ if (l->start <= lock->start && lock->end <= l->end)
+ goto out;
+ if (l->start < lock->start)
+ lock->start = l->start;
+ if (lock->end < l->end)
+ lock->end = l->end;
+ goto delete;
+ } else {
+ if (l->end < lock->start)
+ goto skip;
+ if (lock->end < l->start)
+ break;
+ if (lock->start <= l->start && l->end <= lock->end)
+ goto delete;
+ if (l->end <= lock->end) {
+ l->end = lock->start - 1;
+ goto skip;
+ }
+ if (lock->start <= l->start) {
+ l->start = lock->end + 1;
+ break;
+ }
+ *newl2 = *l;
+ newl2->start = lock->end + 1;
+ l->end = lock->start - 1;
+ insert_lock(&l->next, newl2);
+ newl2 = NULL;
+ }
+ skip:
+ lp = &l->next;
+ continue;
+
+ delete:
+ delete_lock(lp);
+ }
+ if (lock->type != F_UNLCK) {
+ *newl1 = *lock;
+ insert_lock(lp, newl1);
+ newl1 = NULL;
+ }
+out:
+ free(newl1);
+ free(newl2);
+ return 0;
+}
+
+static void flock_to_lock(struct flock *flock, struct lock *lock)
+{
+ memset(lock, 0, sizeof(struct lock));
+ lock->type = flock->l_type;
+ lock->start = flock->l_start;
+ lock->end =
+ flock->l_len ? flock->l_start + flock->l_len - 1 : OFFSET_MAX;
+ lock->pid = flock->l_pid;
+}
+
+static void lock_to_flock(struct lock *lock, struct flock *flock)
+{
+ flock->l_type = lock->type;
+ flock->l_start = lock->start;
+ flock->l_len =
+ (lock->end == OFFSET_MAX) ? 0 : lock->end - lock->start + 1;
+ flock->l_pid = lock->pid;
+}
+
+static int fuse_flush_common(struct fuse *f, fuse_req_t req, fuse_ino_t ino,
+ const char *path, struct fuse_file_info *fi)
+{
+ struct fuse_intr_data d;
+ struct flock lock;
+ struct lock l;
+ int err;
+ int errlock;
+
+ fuse_prepare_interrupt(f, req, &d);
+ memset(&lock, 0, sizeof(lock));
+ lock.l_type = F_UNLCK;
+ lock.l_whence = SEEK_SET;
+ err = fuse_fs_flush(f->fs, path, fi);
+ errlock = fuse_fs_lock(f->fs, path, fi, F_SETLK, &lock);
+ fuse_finish_interrupt(f, req, &d);
+
+ if (errlock != -ENOSYS) {
+ flock_to_lock(&lock, &l);
+ l.owner = fi->lock_owner;
+ pthread_mutex_lock(&f->lock);
+ locks_insert(get_node(f, ino), &l);
+ pthread_mutex_unlock(&f->lock);
+
+ /* if op.lock() is defined FLUSH is needed regardless
+ of op.flush() */
+ if (err == -ENOSYS)
+ err = 0;
+ }
+ return err;
+}
+
+static void fuse_lib_release(fuse_req_t req, fuse_ino_t ino,
+ struct fuse_file_info *fi)
+{
+ struct fuse *f = req_fuse_prepare(req);
+ struct fuse_intr_data d;
+ char *path;
+ int err = 0;
+
+ get_path_nullok(f, ino, &path);
+ if (fi->flush) {
+ err = fuse_flush_common(f, req, ino, path, fi);
+ if (err == -ENOSYS)
+ err = 0;
+ }
+
+ fuse_prepare_interrupt(f, req, &d);
+ fuse_do_release(f, ino, path, fi);
+ fuse_finish_interrupt(f, req, &d);
+ free_path(f, ino, path);
+
+ reply_err(req, err);
+}
+
+static void fuse_lib_flush(fuse_req_t req, fuse_ino_t ino,
+ struct fuse_file_info *fi)
+{
+ struct fuse *f = req_fuse_prepare(req);
+ char *path;
+ int err;
+
+ get_path_nullok(f, ino, &path);
+ err = fuse_flush_common(f, req, ino, path, fi);
+ free_path(f, ino, path);
+
+ reply_err(req, err);
+}
+
+static int fuse_lock_common(fuse_req_t req, fuse_ino_t ino,
+ struct fuse_file_info *fi, struct flock *lock,
+ int cmd)
+{
+ struct fuse *f = req_fuse_prepare(req);
+ char *path;
+ int err;
+
+ err = get_path_nullok(f, ino, &path);
+ if (!err) {
+ struct fuse_intr_data d;
+ fuse_prepare_interrupt(f, req, &d);
+ err = fuse_fs_lock(f->fs, path, fi, cmd, lock);
+ fuse_finish_interrupt(f, req, &d);
+ free_path(f, ino, path);
+ }
+ return err;
+}
+
+static void fuse_lib_getlk(fuse_req_t req, fuse_ino_t ino,
+ struct fuse_file_info *fi, struct flock *lock)
+{
+ int err;
+ struct lock l;
+ struct lock *conflict;
+ struct fuse *f = req_fuse(req);
+
+ flock_to_lock(lock, &l);
+ l.owner = fi->lock_owner;
+ pthread_mutex_lock(&f->lock);
+ conflict = locks_conflict(get_node(f, ino), &l);
+ if (conflict)
+ lock_to_flock(conflict, lock);
+ pthread_mutex_unlock(&f->lock);
+ if (!conflict)
+ err = fuse_lock_common(req, ino, fi, lock, F_GETLK);
+ else
+ err = 0;
+
+ if (!err)
+ fuse_reply_lock(req, lock);
+ else
+ reply_err(req, err);
+}
+
+static void fuse_lib_setlk(fuse_req_t req, fuse_ino_t ino,
+ struct fuse_file_info *fi, struct flock *lock,
+ int sleep)
+{
+ int err = fuse_lock_common(req, ino, fi, lock,
+ sleep ? F_SETLKW : F_SETLK);
+ if (!err) {
+ struct fuse *f = req_fuse(req);
+ struct lock l;
+ flock_to_lock(lock, &l);
+ l.owner = fi->lock_owner;
+ pthread_mutex_lock(&f->lock);
+ locks_insert(get_node(f, ino), &l);
+ pthread_mutex_unlock(&f->lock);
+ }
+ reply_err(req, err);
+}
+
+static void fuse_lib_flock(fuse_req_t req, fuse_ino_t ino,
+ struct fuse_file_info *fi, int op)
+{
+ struct fuse *f = req_fuse_prepare(req);
+ char *path;
+ int err;
+
+ err = get_path_nullok(f, ino, &path);
+ if (err == 0) {
+ struct fuse_intr_data d;
+ fuse_prepare_interrupt(f, req, &d);
+ err = fuse_fs_flock(f->fs, path, fi, op);
+ fuse_finish_interrupt(f, req, &d);
+ free_path(f, ino, path);
+ }
+ reply_err(req, err);
+}
+
+static void fuse_lib_bmap(fuse_req_t req, fuse_ino_t ino, size_t blocksize,
+ uint64_t idx)
+{
+ struct fuse *f = req_fuse_prepare(req);
+ struct fuse_intr_data d;
+ char *path;
+ int err;
+
+ err = get_path(f, ino, &path);
+ if (!err) {
+ fuse_prepare_interrupt(f, req, &d);
+ err = fuse_fs_bmap(f->fs, path, blocksize, &idx);
+ fuse_finish_interrupt(f, req, &d);
+ free_path(f, ino, path);
+ }
+ if (!err)
+ fuse_reply_bmap(req, idx);
+ else
+ reply_err(req, err);
+}
+
+static void fuse_lib_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)
+{
+ struct fuse *f = req_fuse_prepare(req);
+ struct fuse_intr_data d;
+ char *path, *out_buf = NULL;
+ int err;
+
+ err = -EPERM;
+ if (flags & FUSE_IOCTL_UNRESTRICTED)
+ goto err;
+
+ if (out_bufsz) {
+ err = -ENOMEM;
+ out_buf = malloc(out_bufsz);
+ if (!out_buf)
+ goto err;
+ }
+
+ assert(!in_bufsz || !out_bufsz || in_bufsz == out_bufsz);
+ if (out_buf)
+ memcpy(out_buf, in_buf, in_bufsz);
+
+ err = get_path_nullok(f, ino, &path);
+ if (err)
+ goto err;
+
+ fuse_prepare_interrupt(f, req, &d);
+
+ err = fuse_fs_ioctl(f->fs, path, cmd, arg, fi, flags,
+ out_buf ?: (void *)in_buf);
+
+ fuse_finish_interrupt(f, req, &d);
+ free_path(f, ino, path);
+
+ fuse_reply_ioctl(req, err, out_buf, out_bufsz);
+ goto out;
+err:
+ reply_err(req, err);
+out:
+ free(out_buf);
+}
+
+static void fuse_lib_poll(fuse_req_t req, fuse_ino_t ino,
+ struct fuse_file_info *fi, struct fuse_pollhandle *ph)
+{
+ struct fuse *f = req_fuse_prepare(req);
+ struct fuse_intr_data d;
+ char *path;
+ int err;
+ unsigned revents = 0;
+
+ err = get_path_nullok(f, ino, &path);
+ if (!err) {
+ fuse_prepare_interrupt(f, req, &d);
+ err = fuse_fs_poll(f->fs, path, fi, ph, &revents);
+ fuse_finish_interrupt(f, req, &d);
+ free_path(f, ino, path);
+ }
+ if (!err)
+ fuse_reply_poll(req, revents);
+ else
+ reply_err(req, err);
+}
+
+static void fuse_lib_fallocate(fuse_req_t req, fuse_ino_t ino, int mode,
+ off64_t offset, off64_t length, struct fuse_file_info *fi)
+{
+ struct fuse *f = req_fuse_prepare(req);
+ struct fuse_intr_data d;
+ char *path;
+ int err;
+
+ err = get_path_nullok(f, ino, &path);
+ if (!err) {
+ fuse_prepare_interrupt(f, req, &d);
+ err = fuse_fs_fallocate(f->fs, path, mode, offset, length, fi);
+ fuse_finish_interrupt(f, req, &d);
+ free_path(f, ino, path);
+ }
+ reply_err(req, err);
+}
+
+static int clean_delay(struct fuse *f)
+{
+ /*
+ * This is calculating the delay between clean runs. To
+ * reduce the number of cleans we are doing them 10 times
+ * within the remember window.
+ */
+ int min_sleep = 60;
+ int max_sleep = 3600;
+ int sleep_time = f->conf.remember / 10;
+
+ if (sleep_time > max_sleep)
+ return max_sleep;
+ if (sleep_time < min_sleep)
+ return min_sleep;
+ return sleep_time;
+}
+
+int fuse_clean_cache(struct fuse *f)
+{
+ struct node_lru *lnode;
+ struct list_head *curr, *next;
+ struct node *node;
+ struct timespec now;
+
+ pthread_mutex_lock(&f->lock);
+
+ curr_time(&now);
+
+ for (curr = f->lru_table.next; curr != &f->lru_table; curr = next) {
+ double age;
+
+ next = curr->next;
+ lnode = list_entry(curr, struct node_lru, lru);
+ node = &lnode->node;
+
+ age = diff_timespec(&now, &lnode->forget_time);
+ if (age <= f->conf.remember)
+ break;
+
+ assert(node->nlookup == 1);
+
+ /* Don't forget active directories */
+ if (node->refctr > 1)
+ continue;
+
+ node->nlookup = 0;
+ unhash_name(f, node);
+ unref_node(f, node);
+ }
+ pthread_mutex_unlock(&f->lock);
+
+ return clean_delay(f);
+}
+
+static struct fuse_lowlevel_ops fuse_path_ops = {
+ .init = fuse_lib_init,
+ .destroy = fuse_lib_destroy,
+ .lookup = fuse_lib_lookup,
+ .forget = fuse_lib_forget,
+ .forget_multi = fuse_lib_forget_multi,
+ .getattr = fuse_lib_getattr,
+ .setattr = fuse_lib_setattr,
+ .access = fuse_lib_access,
+ .readlink = fuse_lib_readlink,
+ .mknod = fuse_lib_mknod,
+ .mkdir = fuse_lib_mkdir,
+ .unlink = fuse_lib_unlink,
+ .rmdir = fuse_lib_rmdir,
+ .symlink = fuse_lib_symlink,
+ .rename = fuse_lib_rename,
+ .link = fuse_lib_link,
+ .create = fuse_lib_create,
+ .open = fuse_lib_open,
+ .read = fuse_lib_read,
+ .write_buf = fuse_lib_write_buf,
+ .flush = fuse_lib_flush,
+ .release = fuse_lib_release,
+ .fsync = fuse_lib_fsync,
+ .opendir = fuse_lib_opendir,
+ .readdir = fuse_lib_readdir,
+ .releasedir = fuse_lib_releasedir,
+ .fsyncdir = fuse_lib_fsyncdir,
+ .statfs = fuse_lib_statfs,
+ .setxattr = fuse_lib_setxattr,
+ .getxattr = fuse_lib_getxattr,
+ .listxattr = fuse_lib_listxattr,
+ .removexattr = fuse_lib_removexattr,
+ .getlk = fuse_lib_getlk,
+ .setlk = fuse_lib_setlk,
+ .flock = fuse_lib_flock,
+ .bmap = fuse_lib_bmap,
+ .ioctl = fuse_lib_ioctl,
+ .poll = fuse_lib_poll,
+ .fallocate = fuse_lib_fallocate,
+};
+
+int fuse_notify_poll(struct fuse_pollhandle *ph)
+{
+ return fuse_lowlevel_notify_poll(ph);
+}
+
+static void free_cmd(struct fuse_cmd *cmd)
+{
+ free(cmd->buf);
+ free(cmd);
+}
+
+void fuse_process_cmd(struct fuse *f, struct fuse_cmd *cmd)
+{
+ fuse_session_process(f->se, cmd->buf, cmd->buflen, cmd->ch);
+ free_cmd(cmd);
+}
+
+int fuse_exited(struct fuse *f)
+{
+ return fuse_session_exited(f->se);
+}
+
+struct fuse_session *fuse_get_session(struct fuse *f)
+{
+ return f->se;
+}
+
+static struct fuse_cmd *fuse_alloc_cmd(size_t bufsize)
+{
+ struct fuse_cmd *cmd = (struct fuse_cmd *) malloc(sizeof(*cmd));
+ if (cmd == NULL) {
+ fprintf(stderr, "fuse: failed to allocate cmd\n");
+ return NULL;
+ }
+ cmd->buf = (char *) malloc(bufsize);
+ if (cmd->buf == NULL) {
+ fprintf(stderr, "fuse: failed to allocate read buffer\n");
+ free(cmd);
+ return NULL;
+ }
+ return cmd;
+}
+
+struct fuse_cmd *fuse_read_cmd(struct fuse *f)
+{
+ struct fuse_chan *ch = fuse_session_next_chan(f->se, NULL);
+ size_t bufsize = fuse_chan_bufsize(ch);
+ struct fuse_cmd *cmd = fuse_alloc_cmd(bufsize);
+ if (cmd != NULL) {
+ int res = fuse_chan_recv(&ch, cmd->buf, bufsize);
+ if (res <= 0) {
+ free_cmd(cmd);
+ if (res < 0 && res != -EINTR && res != -EAGAIN)
+ fuse_exit(f);
+ return NULL;
+ }
+ cmd->buflen = res;
+ cmd->ch = ch;
+ }
+ return cmd;
+}
+
+static int fuse_session_loop_remember(struct fuse *f)
+{
+ struct fuse_session *se = f->se;
+ int res = 0;
+ struct timespec now;
+ time_t next_clean;
+ struct fuse_chan *ch = fuse_session_next_chan(se, NULL);
+ size_t bufsize = fuse_chan_bufsize(ch);
+ char *buf = (char *) malloc(bufsize);
+ struct pollfd fds = {
+ .fd = fuse_chan_fd(ch),
+ .events = POLLIN
+ };
+
+ if (!buf) {
+ fprintf(stderr, "fuse: failed to allocate read buffer\n");
+ return -1;
+ }
+
+ curr_time(&now);
+ next_clean = now.tv_sec;
+ while (!fuse_session_exited(se)) {
+ struct fuse_chan *tmpch = ch;
+ struct fuse_buf fbuf = {
+ .mem = buf,
+ .size = bufsize,
+ };
+ unsigned timeout;
+
+ curr_time(&now);
+ if (now.tv_sec < next_clean)
+ timeout = next_clean - now.tv_sec;
+ else
+ timeout = 0;
+
+ res = poll(&fds, 1, timeout * 1000);
+ if (res == -1) {
+ if (errno == -EINTR)
+ continue;
+ else
+ break;
+ } else if (res > 0) {
+ res = fuse_session_receive_buf(se, &fbuf, &tmpch);
+
+ if (res == -EINTR)
+ continue;
+ if (res <= 0)
+ break;
+
+ fuse_session_process_buf(se, &fbuf, tmpch);
+ } else {
+ timeout = fuse_clean_cache(f);
+ curr_time(&now);
+ next_clean = now.tv_sec + timeout;
+ }
+ }
+
+ free(buf);
+ fuse_session_reset(se);
+ return res < 0 ? -1 : 0;
+}
+
+int fuse_loop(struct fuse *f)
+{
+ if (!f)
+ return -1;
+
+ if (lru_enabled(f))
+ return fuse_session_loop_remember(f);
+
+ return fuse_session_loop(f->se);
+}
+
+int fuse_invalidate(struct fuse *f, const char *path)
+{
+ (void) f;
+ (void) path;
+ return -EINVAL;
+}
+
+void fuse_exit(struct fuse *f)
+{
+ fuse_session_exit(f->se);
+}
+
+struct fuse_context *fuse_get_context(void)
+{
+ return &fuse_get_context_internal()->ctx;
+}
+
+/*
+ * The size of fuse_context got extended, so need to be careful about
+ * incompatibility (i.e. a new binary cannot work with an old
+ * library).
+ */
+struct fuse_context *fuse_get_context_compat22(void);
+struct fuse_context *fuse_get_context_compat22(void)
+{
+ return &fuse_get_context_internal()->ctx;
+}
+FUSE_SYMVER(".symver fuse_get_context_compat22,fuse_get_context@FUSE_2.2");
+
+int fuse_getgroups(int size, gid_t list[])
+{
+ fuse_req_t req = fuse_get_context_internal()->req;
+ return fuse_req_getgroups(req, size, list);
+}
+
+int fuse_interrupted(void)
+{
+ return fuse_req_interrupted(fuse_get_context_internal()->req);
+}
+
+void fuse_set_getcontext_func(struct fuse_context *(*func)(void))
+{
+ (void) func;
+ /* no-op */
+}
+
+enum {
+ KEY_HELP,
+};
+
+#define FUSE_LIB_OPT(t, p, v) { t, offsetof(struct fuse_config, p), v }
+
+static const struct fuse_opt fuse_lib_opts[] = {
+ FUSE_OPT_KEY("-h", KEY_HELP),
+ FUSE_OPT_KEY("--help", KEY_HELP),
+ FUSE_OPT_KEY("debug", FUSE_OPT_KEY_KEEP),
+ FUSE_OPT_KEY("-d", FUSE_OPT_KEY_KEEP),
+ FUSE_LIB_OPT("debug", debug, 1),
+ FUSE_LIB_OPT("-d", debug, 1),
+ FUSE_LIB_OPT("hard_remove", hard_remove, 1),
+ FUSE_LIB_OPT("use_ino", use_ino, 1),
+ FUSE_LIB_OPT("readdir_ino", readdir_ino, 1),
+ FUSE_LIB_OPT("direct_io", direct_io, 1),
+ FUSE_LIB_OPT("kernel_cache", kernel_cache, 1),
+ FUSE_LIB_OPT("auto_cache", auto_cache, 1),
+ FUSE_LIB_OPT("noauto_cache", auto_cache, 0),
+ FUSE_LIB_OPT("umask=", set_mode, 1),
+ FUSE_LIB_OPT("umask=%o", umask, 0),
+ FUSE_LIB_OPT("uid=", set_uid, 1),
+ FUSE_LIB_OPT("uid=%d", uid, 0),
+ FUSE_LIB_OPT("gid=", set_gid, 1),
+ FUSE_LIB_OPT("gid=%d", gid, 0),
+ FUSE_LIB_OPT("entry_timeout=%lf", entry_timeout, 0),
+ FUSE_LIB_OPT("attr_timeout=%lf", attr_timeout, 0),
+ 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", remember, -1),
+ FUSE_LIB_OPT("remember=%u", remember, 0),
+ FUSE_LIB_OPT("nopath", nopath, 1),
+ FUSE_LIB_OPT("intr", intr, 1),
+ FUSE_LIB_OPT("intr_signal=%d", intr_signal, 0),
+ FUSE_LIB_OPT("modules=%s", modules, 0),
+ FUSE_OPT_END
+};
+
+static void fuse_lib_help(void)
+{
+ fprintf(stderr,
+" -o hard_remove immediate removal (don't hide files)\n"
+" -o use_ino let filesystem set inode numbers\n"
+" -o readdir_ino try to fill in d_ino in readdir\n"
+" -o direct_io use direct I/O\n"
+" -o kernel_cache cache files in kernel\n"
+" -o [no]auto_cache enable caching based on modification times (off)\n"
+" -o umask=M set file permissions (octal)\n"
+" -o uid=N set file owner\n"
+" -o gid=N set file group\n"
+" -o entry_timeout=T cache timeout for names (1.0s)\n"
+" -o negative_timeout=T cache timeout for deleted names (0.0s)\n"
+" -o attr_timeout=T cache timeout for attributes (1.0s)\n"
+" -o ac_attr_timeout=T auto cache timeout for attributes (attr_timeout)\n"
+" -o noforget never forget cached inodes\n"
+" -o remember=T remember cached inodes for T seconds (0s)\n"
+" -o intr allow requests to be interrupted\n"
+" -o intr_signal=NUM signal to send on interrupt (%i)\n"
+" -o modules=M1[:M2...] names of modules to push onto filesystem stack\n"
+"\n", FUSE_DEFAULT_INTR_SIGNAL);
+}
+
+static void fuse_lib_help_modules(void)
+{
+ struct fuse_module *m;
+ fprintf(stderr, "\nModule options:\n");
+ pthread_mutex_lock(&fuse_context_lock);
+ for (m = fuse_modules; m; m = m->next) {
+ struct fuse_fs *fs = NULL;
+ struct fuse_fs *newfs;
+ struct fuse_args args = FUSE_ARGS_INIT(0, NULL);
+ if (fuse_opt_add_arg(&args, "") != -1 &&
+ fuse_opt_add_arg(&args, "-h") != -1) {
+ fprintf(stderr, "\n[%s]\n", m->name);
+ newfs = m->factory(&args, &fs);
+ assert(newfs == NULL);
+ }
+ fuse_opt_free_args(&args);
+ }
+ pthread_mutex_unlock(&fuse_context_lock);
+}
+
+static int fuse_lib_opt_proc(void *data, const char *arg, int key,
+ struct fuse_args *outargs)
+{
+ (void) arg; (void) outargs;
+
+ if (key == KEY_HELP) {
+ struct fuse_config *conf = (struct fuse_config *) data;
+ fuse_lib_help();
+ conf->help = 1;
+ }
+
+ return 1;
+}
+
+int fuse_is_lib_option(const char *opt)
+{
+ return fuse_lowlevel_is_lib_option(opt) ||
+ fuse_opt_match(fuse_lib_opts, opt);
+}
+
+static int fuse_init_intr_signal(int signum, int *installed)
+{
+ struct sigaction old_sa;
+
+ if (sigaction(signum, NULL, &old_sa) == -1) {
+ perror("fuse: cannot get old signal handler");
+ return -1;
+ }
+
+ if (old_sa.sa_handler == SIG_DFL) {
+ struct sigaction sa;
+
+ memset(&sa, 0, sizeof(struct sigaction));
+ sa.sa_handler = fuse_intr_sighandler;
+ sigemptyset(&sa.sa_mask);
+
+ if (sigaction(signum, &sa, NULL) == -1) {
+ perror("fuse: cannot set interrupt signal handler");
+ return -1;
+ }
+ *installed = 1;
+ }
+ return 0;
+}
+
+static void fuse_restore_intr_signal(int signum)
+{
+ struct sigaction sa;
+
+ memset(&sa, 0, sizeof(struct sigaction));
+ sa.sa_handler = SIG_DFL;
+ sigaction(signum, &sa, NULL);
+}
+
+
+static int fuse_push_module(struct fuse *f, const char *module,
+ struct fuse_args *args)
+{
+ struct fuse_fs *fs[2] = { f->fs, NULL };
+ struct fuse_fs *newfs;
+ struct fuse_module *m = fuse_get_module(module);
+
+ if (!m)
+ return -1;
+
+ newfs = m->factory(args, fs);
+ if (!newfs) {
+ fuse_put_module(m);
+ return -1;
+ }
+ newfs->m = m;
+ f->fs = newfs;
+ f->nullpath_ok = newfs->op.flag_nullpath_ok && f->nullpath_ok;
+ f->conf.nopath = newfs->op.flag_nopath && f->conf.nopath;
+ f->utime_omit_ok = newfs->op.flag_utime_omit_ok && f->utime_omit_ok;
+ return 0;
+}
+
+struct fuse_fs *fuse_fs_new(const struct fuse_operations *op, size_t op_size,
+ void *user_data)
+{
+ struct fuse_fs *fs;
+
+ if (sizeof(struct fuse_operations) < op_size) {
+ fprintf(stderr, "fuse: warning: library too old, some operations may not not work\n");
+ op_size = sizeof(struct fuse_operations);
+ }
+
+ fs = (struct fuse_fs *) calloc(1, sizeof(struct fuse_fs));
+ if (!fs) {
+ fprintf(stderr, "fuse: failed to allocate fuse_fs object\n");
+ return NULL;
+ }
+
+ fs->user_data = user_data;
+ if (op)
+ memcpy(&fs->op, op, op_size);
+ return fs;
+}
+
+static int node_table_init(struct node_table *t)
+{
+ t->size = NODE_TABLE_MIN_SIZE;
+ t->array = (struct node **) calloc(1, sizeof(struct node *) * t->size);
+ if (t->array == NULL) {
+ fprintf(stderr, "fuse: memory allocation failed\n");
+ return -1;
+ }
+ t->use = 0;
+ t->split = 0;
+
+ return 0;
+}
+
+static void *fuse_prune_nodes(void *fuse)
+{
+ struct fuse *f = fuse;
+ int sleep_time;
+
+ while(1) {
+ sleep_time = fuse_clean_cache(f);
+ sleep(sleep_time);
+ }
+ return NULL;
+}
+
+int fuse_start_cleanup_thread(struct fuse *f)
+{
+ if (lru_enabled(f))
+ return fuse_start_thread(&f->prune_thread, fuse_prune_nodes, f);
+
+ return 0;
+}
+
+void fuse_stop_cleanup_thread(struct fuse *f)
+{
+ if (lru_enabled(f)) {
+ pthread_mutex_lock(&f->lock);
+ pthread_cancel(f->prune_thread);
+ pthread_mutex_unlock(&f->lock);
+ pthread_join(f->prune_thread, NULL);
+ }
+}
+
+struct fuse *fuse_new_common(struct fuse_chan *ch, struct fuse_args *args,
+ const struct fuse_operations *op,
+ size_t op_size, void *user_data, int compat)
+{
+ struct fuse *f;
+ struct node *root;
+ struct fuse_fs *fs;
+ struct fuse_lowlevel_ops llop = fuse_path_ops;
+
+ if (fuse_create_context_key() == -1)
+ goto out;
+
+ f = (struct fuse *) calloc(1, sizeof(struct fuse));
+ if (f == NULL) {
+ fprintf(stderr, "fuse: failed to allocate fuse object\n");
+ goto out_delete_context_key;
+ }
+
+ fs = fuse_fs_new(op, op_size, user_data);
+ if (!fs)
+ goto out_free;
+
+ fs->compat = compat;
+ f->fs = fs;
+ f->nullpath_ok = fs->op.flag_nullpath_ok;
+ f->conf.nopath = fs->op.flag_nopath;
+ f->utime_omit_ok = fs->op.flag_utime_omit_ok;
+
+ /* Oh f**k, this is ugly! */
+ if (!fs->op.lock) {
+ llop.getlk = NULL;
+ llop.setlk = NULL;
+ }
+
+ f->conf.entry_timeout = 1.0;
+ f->conf.attr_timeout = 1.0;
+ f->conf.negative_timeout = 0.0;
+ f->conf.intr_signal = FUSE_DEFAULT_INTR_SIGNAL;
+
+ f->pagesize = getpagesize();
+ init_list_head(&f->partial_slabs);
+ init_list_head(&f->full_slabs);
+ init_list_head(&f->lru_table);
+
+ if (fuse_opt_parse(args, &f->conf, fuse_lib_opts,
+ fuse_lib_opt_proc) == -1)
+ goto out_free_fs;
+
+ if (f->conf.modules) {
+ char *module;
+ char *next;
+
+ for (module = f->conf.modules; module; module = next) {
+ char *p;
+ for (p = module; *p && *p != ':'; p++);
+ next = *p ? p + 1 : NULL;
+ *p = '\0';
+ if (module[0] &&
+ fuse_push_module(f, module, args) == -1)
+ goto out_free_fs;
+ }
+ }
+
+ if (!f->conf.ac_attr_timeout_set)
+ f->conf.ac_attr_timeout = f->conf.attr_timeout;
+
+#if defined(__FreeBSD__) || defined(__NetBSD__)
+ /*
+ * In FreeBSD, we always use these settings as inode numbers
+ * are needed to make getcwd(3) work.
+ */
+ f->conf.readdir_ino = 1;
+#endif
+
+ if (compat && compat <= 25) {
+ if (fuse_sync_compat_args(args) == -1)
+ goto out_free_fs;
+ }
+
+ f->se = fuse_lowlevel_new_common(args, &llop, sizeof(llop), f);
+ if (f->se == NULL) {
+ if (f->conf.help)
+ fuse_lib_help_modules();
+ goto out_free_fs;
+ }
+
+ fuse_session_add_chan(f->se, ch);
+
+ if (f->conf.debug) {
+ fprintf(stderr, "nullpath_ok: %i\n", f->nullpath_ok);
+ fprintf(stderr, "nopath: %i\n", f->conf.nopath);
+ fprintf(stderr, "utime_omit_ok: %i\n", f->utime_omit_ok);
+ }
+
+ /* Trace topmost layer by default */
+ f->fs->debug = f->conf.debug;
+ f->ctr = 0;
+ f->generation = 0;
+ if (node_table_init(&f->name_table) == -1)
+ goto out_free_session;
+
+ if (node_table_init(&f->id_table) == -1)
+ goto out_free_name_table;
+
+ fuse_mutex_init(&f->lock);
+
+ root = alloc_node(f);
+ if (root == NULL) {
+ fprintf(stderr, "fuse: memory allocation failed\n");
+ goto out_free_id_table;
+ }
+
+ strcpy(root->inline_name, "/");
+ root->name = root->inline_name;
+
+ if (f->conf.intr &&
+ fuse_init_intr_signal(f->conf.intr_signal,
+ &f->intr_installed) == -1)
+ goto out_free_root;
+
+ root->parent = NULL;
+ root->nodeid = FUSE_ROOT_ID;
+ inc_nlookup(root);
+ hash_id(f, root);
+
+ return f;
+
+out_free_root:
+ free(root);
+out_free_id_table:
+ free(f->id_table.array);
+out_free_name_table:
+ free(f->name_table.array);
+out_free_session:
+ fuse_session_destroy(f->se);
+out_free_fs:
+ /* Horrible compatibility hack to stop the destructor from being
+ called on the filesystem without init being called first */
+ fs->op.destroy = NULL;
+ fuse_fs_destroy(f->fs);
+ free(f->conf.modules);
+out_free:
+ free(f);
+out_delete_context_key:
+ fuse_delete_context_key();
+out:
+ return NULL;
+}
+
+struct fuse *fuse_new(struct fuse_chan *ch, struct fuse_args *args,
+ const struct fuse_operations *op, size_t op_size,
+ void *user_data)
+{
+ return fuse_new_common(ch, args, op, op_size, user_data, 0);
+}
+
+void fuse_destroy(struct fuse *f)
+{
+ size_t i;
+
+ if (f->conf.intr && f->intr_installed)
+ fuse_restore_intr_signal(f->conf.intr_signal);
+
+ if (f->fs) {
+ struct fuse_context_i *c = fuse_get_context_internal();
+
+ memset(c, 0, sizeof(*c));
+ c->ctx.fuse = f;
+
+ for (i = 0; i < f->id_table.size; i++) {
+ struct node *node;
+
+ for (node = f->id_table.array[i]; node != NULL;
+ node = node->id_next) {
+ if (node->is_hidden) {
+ char *path;
+ if (try_get_path(f, node->nodeid, NULL, &path, NULL, false) == 0) {
+ fuse_fs_unlink(f->fs, path);
+ free(path);
+ }
+ }
+ }
+ }
+ }
+ for (i = 0; i < f->id_table.size; i++) {
+ struct node *node;
+ struct node *next;
+
+ for (node = f->id_table.array[i]; node != NULL; node = next) {
+ next = node->id_next;
+ free_node(f, node);
+ f->id_table.use--;
+ }
+ }
+ assert(list_empty(&f->partial_slabs));
+ assert(list_empty(&f->full_slabs));
+
+ free(f->id_table.array);
+ free(f->name_table.array);
+ pthread_mutex_destroy(&f->lock);
+ fuse_session_destroy(f->se);
+ free(f->conf.modules);
+ free(f);
+ fuse_delete_context_key();
+}
+
+static struct fuse *fuse_new_common_compat25(int fd, struct fuse_args *args,
+ const struct fuse_operations *op,
+ size_t op_size, int compat)
+{
+ struct fuse *f = NULL;
+ struct fuse_chan *ch = fuse_kern_chan_new(fd);
+
+ if (ch)
+ f = fuse_new_common(ch, args, op, op_size, NULL, compat);
+
+ return f;
+}
+
+/* called with fuse_context_lock held or during initialization (before
+ main() has been called) */
+void fuse_register_module(struct fuse_module *mod)
+{
+ mod->ctr = 0;
+ mod->so = fuse_current_so;
+ if (mod->so)
+ mod->so->ctr++;
+ mod->next = fuse_modules;
+ fuse_modules = mod;
+}
+
+#if !defined(__FreeBSD__) && !defined(__NetBSD__)
+
+static struct fuse *fuse_new_common_compat(int fd, const char *opts,
+ const struct fuse_operations *op,
+ size_t op_size, int compat)
+{
+ struct fuse *f;
+ struct fuse_args args = FUSE_ARGS_INIT(0, NULL);
+
+ if (fuse_opt_add_arg(&args, "") == -1)
+ return NULL;
+ if (opts &&
+ (fuse_opt_add_arg(&args, "-o") == -1 ||
+ fuse_opt_add_arg(&args, opts) == -1)) {
+ fuse_opt_free_args(&args);
+ return NULL;
+ }
+ f = fuse_new_common_compat25(fd, &args, op, op_size, compat);
+ fuse_opt_free_args(&args);
+
+ return f;
+}
+
+struct fuse *fuse_new_compat22(int fd, const char *opts,
+ const struct fuse_operations_compat22 *op,
+ size_t op_size)
+{
+ return fuse_new_common_compat(fd, opts, (struct fuse_operations *) op,
+ op_size, 22);
+}
+
+struct fuse *fuse_new_compat2(int fd, const char *opts,
+ const struct fuse_operations_compat2 *op)
+{
+ return fuse_new_common_compat(fd, opts, (struct fuse_operations *) op,
+ sizeof(struct fuse_operations_compat2),
+ 21);
+}
+
+struct fuse *fuse_new_compat1(int fd, int flags,
+ const struct fuse_operations_compat1 *op)
+{
+ const char *opts = NULL;
+ if (flags & FUSE_DEBUG_COMPAT1)
+ opts = "debug";
+ return fuse_new_common_compat(fd, opts, (struct fuse_operations *) op,
+ sizeof(struct fuse_operations_compat1),
+ 11);
+}
+
+FUSE_SYMVER(".symver fuse_exited,__fuse_exited@");
+FUSE_SYMVER(".symver fuse_process_cmd,__fuse_process_cmd@");
+FUSE_SYMVER(".symver fuse_read_cmd,__fuse_read_cmd@");
+FUSE_SYMVER(".symver fuse_set_getcontext_func,__fuse_set_getcontext_func@");
+FUSE_SYMVER(".symver fuse_new_compat2,fuse_new@");
+FUSE_SYMVER(".symver fuse_new_compat22,fuse_new@FUSE_2.2");
+
+#endif /* __FreeBSD__ || __NetBSD__ */
+
+struct fuse *fuse_new_compat25(int fd, struct fuse_args *args,
+ const struct fuse_operations_compat25 *op,
+ size_t op_size)
+{
+ return fuse_new_common_compat25(fd, args, (struct fuse_operations *) op,
+ op_size, 25);
+}
+
+FUSE_SYMVER(".symver fuse_new_compat25,fuse_new@FUSE_2.5");
diff --git a/fuse/fuse_i.h b/fuse/fuse_i.h
new file mode 100644
index 000000000..78f14677a
--- /dev/null
+++ b/fuse/fuse_i.h
@@ -0,0 +1,128 @@
+/*
+ FUSE: Filesystem in Userspace
+ Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
+
+ This program can be distributed under the terms of the GNU LGPLv2.
+ See the file COPYING.LIB
+*/
+
+#include "fuse.h"
+#include "fuse_lowlevel.h"
+
+struct fuse_chan;
+struct fuse_ll;
+
+struct fuse_session {
+ struct fuse_session_ops op;
+
+ int (*receive_buf)(struct fuse_session *se, struct fuse_buf *buf,
+ struct fuse_chan **chp);
+
+ void (*process_buf)(void *data, const struct fuse_buf *buf,
+ struct fuse_chan *ch);
+
+ void *data;
+
+ volatile int exited;
+
+ struct fuse_chan *ch;
+};
+
+struct fuse_req {
+ struct fuse_ll *f;
+ uint64_t unique;
+ int ctr;
+ pthread_mutex_t lock;
+ struct fuse_ctx ctx;
+ struct fuse_chan *ch;
+ int interrupted;
+ unsigned int ioctl_64bit : 1;
+ union {
+ struct {
+ uint64_t unique;
+ } i;
+ struct {
+ fuse_interrupt_func_t func;
+ void *data;
+ } ni;
+ } u;
+ struct fuse_req *next;
+ struct fuse_req *prev;
+};
+
+struct fuse_notify_req {
+ uint64_t unique;
+ void (*reply)(struct fuse_notify_req *, fuse_req_t, fuse_ino_t,
+ const void *, const struct fuse_buf *);
+ struct fuse_notify_req *next;
+ struct fuse_notify_req *prev;
+};
+
+struct fuse_ll {
+ int debug;
+ int allow_root;
+ int atomic_o_trunc;
+ int no_remote_posix_lock;
+ int no_remote_flock;
+ int big_writes;
+ int splice_write;
+ int splice_move;
+ int splice_read;
+ int no_splice_write;
+ int no_splice_move;
+ int no_splice_read;
+ struct fuse_lowlevel_ops op;
+ int got_init;
+ struct cuse_data *cuse_data;
+ void *userdata;
+ uid_t owner;
+ struct fuse_conn_info conn;
+ struct fuse_req list;
+ struct fuse_req interrupts;
+ pthread_mutex_t lock;
+ int got_destroy;
+ pthread_key_t pipe_key;
+ int broken_splice_nonblock;
+ uint64_t notify_ctr;
+ struct fuse_notify_req notify_list;
+};
+
+struct fuse_cmd {
+ char *buf;
+ size_t buflen;
+ struct fuse_chan *ch;
+};
+
+struct fuse *fuse_new_common(struct fuse_chan *ch, struct fuse_args *args,
+ const struct fuse_operations *op,
+ size_t op_size, void *user_data, int compat);
+
+int fuse_sync_compat_args(struct fuse_args *args);
+
+struct fuse_chan *fuse_kern_chan_new(int fd);
+
+struct fuse_session *fuse_lowlevel_new_common(struct fuse_args *args,
+ const struct fuse_lowlevel_ops *op,
+ size_t op_size, void *userdata);
+
+void fuse_kern_unmount_compat22(const char *mountpoint);
+void fuse_kern_unmount(const char *mountpoint, int fd);
+int fuse_kern_mount(const char *mountpoint, struct fuse_args *args);
+
+int fuse_send_reply_iov_nofree(fuse_req_t req, int error, struct iovec *iov,
+ int count);
+void fuse_free_req(fuse_req_t req);
+
+
+struct fuse *fuse_setup_common(int argc, char *argv[],
+ const struct fuse_operations *op,
+ size_t op_size,
+ char **mountpoint,
+ int *multithreaded,
+ int *fd,
+ void *user_data,
+ int compat);
+
+void cuse_lowlevel_init(fuse_req_t req, fuse_ino_t nodeide, const void *inarg);
+
+int fuse_start_thread(pthread_t *thread_id, void *(*func)(void *), void *arg);
diff --git a/fuse/fuse_kern_chan.c b/fuse/fuse_kern_chan.c
new file mode 100644
index 000000000..5f77bbf56
--- /dev/null
+++ b/fuse/fuse_kern_chan.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 LGPLv2.
+ See the file COPYING.LIB
+*/
+
+#include "fuse_lowlevel.h"
+#include "fuse_kernel.h"
+#include "fuse_i.h"
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <assert.h>
+
+static int fuse_kern_chan_receive(struct fuse_chan **chp, char *buf,
+ size_t size)
+{
+ struct fuse_chan *ch = *chp;
+ int err;
+ ssize_t res;
+ struct fuse_session *se = fuse_chan_session(ch);
+ assert(se != NULL);
+
+restart:
+ res = read(fuse_chan_fd(ch), buf, size);
+ err = errno;
+
+ if (fuse_session_exited(se))
+ return 0;
+ if (res == -1) {
+ /* ENOENT means the operation was interrupted, it's safe
+ to restart */
+ if (err == ENOENT)
+ goto restart;
+
+ if (err == ENODEV) {
+ fuse_session_exit(se);
+ return 0;
+ }
+ /* Errors occurring during normal operation: EINTR (read
+ interrupted), EAGAIN (nonblocking I/O), ENODEV (filesystem
+ umounted) */
+ if (err != EINTR && err != EAGAIN)
+ perror("fuse: reading device");
+ return -err;
+ }
+ if ((size_t) res < sizeof(struct fuse_in_header)) {
+ fprintf(stderr, "short read on fuse device\n");
+ return -EIO;
+ }
+ return res;
+}
+
+static int fuse_kern_chan_send(struct fuse_chan *ch, const struct iovec iov[],
+ size_t count)
+{
+ if (iov) {
+ ssize_t res = writev(fuse_chan_fd(ch), iov, count);
+ int err = errno;
+
+ if (res == -1) {
+ struct fuse_session *se = fuse_chan_session(ch);
+
+ assert(se != NULL);
+
+ /* ENOENT means the operation was interrupted */
+ if (!fuse_session_exited(se) && err != ENOENT)
+ perror("fuse: writing device");
+ return -err;
+ }
+ }
+ return 0;
+}
+
+static void fuse_kern_chan_destroy(struct fuse_chan *ch)
+{
+ close(fuse_chan_fd(ch));
+}
+
+#define MIN_BUFSIZE 0x21000
+
+struct fuse_chan *fuse_kern_chan_new(int fd)
+{
+ struct fuse_chan_ops op = {
+ .receive = fuse_kern_chan_receive,
+ .send = fuse_kern_chan_send,
+ .destroy = fuse_kern_chan_destroy,
+ };
+ size_t bufsize = getpagesize() + 0x1000;
+ bufsize = bufsize < MIN_BUFSIZE ? MIN_BUFSIZE : bufsize;
+ return fuse_chan_new(&op, fd, bufsize, NULL);
+}
diff --git a/fuse/fuse_loop.c b/fuse/fuse_loop.c
new file mode 100644
index 000000000..b7b4ca4ee
--- /dev/null
+++ b/fuse/fuse_loop.c
@@ -0,0 +1,46 @@
+/*
+ FUSE: Filesystem in Userspace
+ Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
+
+ This program can be distributed under the terms of the GNU LGPLv2.
+ See the file COPYING.LIB
+*/
+
+#include "fuse_lowlevel.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+int fuse_session_loop(struct fuse_session *se)
+{
+ int res = 0;
+ struct fuse_chan *ch = fuse_session_next_chan(se, NULL);
+ size_t bufsize = fuse_chan_bufsize(ch);
+ char *buf = (char *) malloc(bufsize);
+ if (!buf) {
+ fprintf(stderr, "fuse: failed to allocate read buffer\n");
+ return -1;
+ }
+
+ while (!fuse_session_exited(se)) {
+ struct fuse_chan *tmpch = ch;
+ struct fuse_buf fbuf = {
+ .mem = buf,
+ .size = bufsize,
+ };
+
+ res = fuse_session_receive_buf(se, &fbuf, &tmpch);
+
+ if (res == -EINTR)
+ continue;
+ if (res <= 0)
+ break;
+
+ fuse_session_process_buf(se, &fbuf, tmpch);
+ }
+
+ free(buf);
+ fuse_session_reset(se);
+ return res < 0 ? -1 : 0;
+}
diff --git a/fuse/fuse_loop_mt.c b/fuse/fuse_loop_mt.c
new file mode 100644
index 000000000..7e400c2a4
--- /dev/null
+++ b/fuse/fuse_loop_mt.c
@@ -0,0 +1,262 @@
+/*
+ FUSE: Filesystem in Userspace
+ Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
+
+ This program can be distributed under the terms of the GNU LGPLv2.
+ See the file COPYING.LIB.
+*/
+
+#include "fuse_lowlevel.h"
+#include "fuse_misc.h"
+#include "fuse_kernel.h"
+#include "fuse_i.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <semaphore.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <pthread.h>
+
+/* Environment var controlling the thread stack size */
+#define ENVNAME_THREAD_STACK "FUSE_THREAD_STACK"
+
+struct fuse_worker {
+ struct fuse_worker *prev;
+ struct fuse_worker *next;
+ pthread_t thread_id;
+ size_t bufsize;
+ char *buf;
+ struct fuse_mt *mt;
+};
+
+struct fuse_mt {
+ pthread_mutex_t lock;
+ int numworker;
+ int numavail;
+ struct fuse_session *se;
+ struct fuse_chan *prevch;
+ struct fuse_worker main;
+ sem_t finish;
+ int exit;
+ int error;
+};
+
+static void list_add_worker(struct fuse_worker *w, struct fuse_worker *next)
+{
+ struct fuse_worker *prev = next->prev;
+ w->next = next;
+ w->prev = prev;
+ prev->next = w;
+ next->prev = w;
+}
+
+static void list_del_worker(struct fuse_worker *w)
+{
+ struct fuse_worker *prev = w->prev;
+ struct fuse_worker *next = w->next;
+ prev->next = next;
+ next->prev = prev;
+}
+
+#define PTHREAD_CANCEL_ENABLE 0
+#define PTHREAD_CANCEL_DISABLE 1
+
+static int fuse_loop_start_thread(struct fuse_mt *mt);
+
+static void *fuse_do_work(void *data)
+{
+ struct fuse_worker *w = (struct fuse_worker *) data;
+ struct fuse_mt *mt = w->mt;
+
+ while (!fuse_session_exited(mt->se)) {
+ int isforget = 0;
+ struct fuse_chan *ch = mt->prevch;
+ struct fuse_buf fbuf = {
+ .mem = w->buf,
+ .size = w->bufsize,
+ };
+ int res;
+
+ pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
+ res = fuse_session_receive_buf(mt->se, &fbuf, &ch);
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
+ if (res == -EINTR)
+ continue;
+ if (res <= 0) {
+ if (res < 0) {
+ fuse_session_exit(mt->se);
+ mt->error = -1;
+ }
+ break;
+ }
+
+ pthread_mutex_lock(&mt->lock);
+ if (mt->exit) {
+ pthread_mutex_unlock(&mt->lock);
+ return NULL;
+ }
+
+ /*
+ * This disgusting hack is needed so that zillions of threads
+ * are not created on a burst of FORGET messages
+ */
+ if (!(fbuf.flags & FUSE_BUF_IS_FD)) {
+ struct fuse_in_header *in = fbuf.mem;
+
+ if (in->opcode == FUSE_FORGET ||
+ in->opcode == FUSE_BATCH_FORGET)
+ isforget = 1;
+ }
+
+ if (!isforget)
+ mt->numavail--;
+ if (mt->numavail == 0)
+ fuse_loop_start_thread(mt);
+ pthread_mutex_unlock(&mt->lock);
+
+ fuse_session_process_buf(mt->se, &fbuf, ch);
+
+ pthread_mutex_lock(&mt->lock);
+ if (!isforget)
+ mt->numavail++;
+ if (mt->numavail > 10) {
+ if (mt->exit) {
+ pthread_mutex_unlock(&mt->lock);
+ return NULL;
+ }
+ list_del_worker(w);
+ mt->numavail--;
+ mt->numworker--;
+ pthread_mutex_unlock(&mt->lock);
+
+ pthread_detach(w->thread_id);
+ free(w->buf);
+ free(w);
+ return NULL;
+ }
+ pthread_mutex_unlock(&mt->lock);
+ }
+
+ sem_post(&mt->finish);
+
+ return NULL;
+}
+
+int fuse_start_thread(pthread_t *thread_id, void *(*func)(void *), void *arg)
+{
+ sigset_t oldset;
+ sigset_t newset;
+ int res;
+ pthread_attr_t attr;
+ char *stack_size;
+
+ /* Override default stack size */
+ pthread_attr_init(&attr);
+ stack_size = getenv(ENVNAME_THREAD_STACK);
+ if (stack_size && pthread_attr_setstacksize(&attr, atoi(stack_size)))
+ fprintf(stderr, "fuse: invalid stack size: %s\n", stack_size);
+
+ /* Disallow signal reception in worker threads */
+ sigemptyset(&newset);
+ sigaddset(&newset, SIGTERM);
+ sigaddset(&newset, SIGINT);
+ sigaddset(&newset, SIGHUP);
+ sigaddset(&newset, SIGQUIT);
+ pthread_sigmask(SIG_BLOCK, &newset, &oldset);
+ res = pthread_create(thread_id, &attr, func, arg);
+ pthread_sigmask(SIG_SETMASK, &oldset, NULL);
+ pthread_attr_destroy(&attr);
+ if (res != 0) {
+ fprintf(stderr, "fuse: error creating thread: %s\n",
+ strerror(res));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int fuse_loop_start_thread(struct fuse_mt *mt)
+{
+ int res;
+ struct fuse_worker *w = malloc(sizeof(struct fuse_worker));
+ if (!w) {
+ fprintf(stderr, "fuse: failed to allocate worker structure\n");
+ return -1;
+ }
+ memset(w, 0, sizeof(struct fuse_worker));
+ w->bufsize = fuse_chan_bufsize(mt->prevch);
+ w->buf = malloc(w->bufsize);
+ w->mt = mt;
+ if (!w->buf) {
+ fprintf(stderr, "fuse: failed to allocate read buffer\n");
+ free(w);
+ return -1;
+ }
+
+ res = fuse_start_thread(&w->thread_id, fuse_do_work, w);
+ if (res == -1) {
+ free(w->buf);
+ free(w);
+ return -1;
+ }
+ list_add_worker(w, &mt->main);
+ mt->numavail ++;
+ mt->numworker ++;
+
+ return 0;
+}
+
+static void fuse_join_worker(struct fuse_mt *mt, struct fuse_worker *w)
+{
+ pthread_join(w->thread_id, NULL);
+ pthread_mutex_lock(&mt->lock);
+ list_del_worker(w);
+ pthread_mutex_unlock(&mt->lock);
+ free(w->buf);
+ free(w);
+}
+
+int fuse_session_loop_mt(struct fuse_session *se)
+{
+ int err;
+ struct fuse_mt mt;
+ struct fuse_worker *w;
+
+ memset(&mt, 0, sizeof(struct fuse_mt));
+ mt.se = se;
+ mt.prevch = fuse_session_next_chan(se, NULL);
+ mt.error = 0;
+ mt.numworker = 0;
+ mt.numavail = 0;
+ mt.main.thread_id = pthread_self();
+ mt.main.prev = mt.main.next = &mt.main;
+ sem_init(&mt.finish, 0, 0);
+ fuse_mutex_init(&mt.lock);
+
+ pthread_mutex_lock(&mt.lock);
+ err = fuse_loop_start_thread(&mt);
+ pthread_mutex_unlock(&mt.lock);
+ if (!err) {
+ /* sem_wait() is interruptible */
+ while (!fuse_session_exited(se))
+ sem_wait(&mt.finish);
+
+ for (w = mt.main.next; w != &mt.main; w = w->next)
+ pthread_cancel(w->thread_id);
+ mt.exit = 1;
+
+ while (mt.main.next != &mt.main)
+ fuse_join_worker(&mt, mt.main.next);
+
+ err = mt.error;
+ }
+
+ pthread_mutex_destroy(&mt.lock);
+ sem_destroy(&mt.finish);
+ fuse_session_reset(se);
+ return err;
+}
diff --git a/fuse/fuse_lowlevel.c b/fuse/fuse_lowlevel.c
new file mode 100644
index 000000000..103f83154
--- /dev/null
+++ b/fuse/fuse_lowlevel.c
@@ -0,0 +1,2967 @@
+/*
+ FUSE: Filesystem in Userspace
+ Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
+
+ This program can be distributed under the terms of the GNU LGPLv2.
+ See the file COPYING.LIB
+*/
+
+#define _GNU_SOURCE
+
+#include "config.h"
+#include "fuse_i.h"
+#include "fuse_kernel.h"
+#include "fuse_opt.h"
+#include "fuse_misc.h"
+#include "fuse_common_compat.h"
+#include "fuse_lowlevel_compat.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <unistd.h>
+#include <limits.h>
+#include <errno.h>
+#include <assert.h>
+
+#ifndef F_LINUX_SPECIFIC_BASE
+#define F_LINUX_SPECIFIC_BASE 1024
+#endif
+#ifndef F_SETPIPE_SZ
+#define F_SETPIPE_SZ (F_LINUX_SPECIFIC_BASE + 7)
+#endif
+
+
+#define PARAM(inarg) (((char *)(inarg)) + sizeof(*(inarg)))
+#define OFFSET_MAX 0x7fffffffffffffffLL
+
+#define container_of(ptr, type, member) ({ \
+ const typeof( ((type *)0)->member ) *__mptr = (ptr); \
+ (type *)( (char *)__mptr - offsetof(type,member) );})
+
+struct fuse_pollhandle {
+ uint64_t kh;
+ struct fuse_chan *ch;
+ struct fuse_ll *f;
+};
+
+static size_t pagesize;
+
+static __attribute__((constructor)) void fuse_ll_init_pagesize(void)
+{
+ pagesize = getpagesize();
+}
+
+static void convert_stat(const struct stat *stbuf, struct fuse_attr *attr)
+{
+ attr->ino = stbuf->st_ino;
+ attr->mode = stbuf->st_mode;
+ attr->nlink = stbuf->st_nlink;
+ attr->uid = stbuf->st_uid;
+ attr->gid = stbuf->st_gid;
+ attr->rdev = stbuf->st_rdev;
+ attr->size = stbuf->st_size;
+ attr->blksize = stbuf->st_blksize;
+ attr->blocks = stbuf->st_blocks;
+ attr->atime = stbuf->st_atime;
+ attr->mtime = stbuf->st_mtime;
+ attr->ctime = stbuf->st_ctime;
+ attr->atimensec = ST_ATIM_NSEC(stbuf);
+ attr->mtimensec = ST_MTIM_NSEC(stbuf);
+ attr->ctimensec = ST_CTIM_NSEC(stbuf);
+}
+
+static void convert_attr(const struct fuse_setattr_in *attr, struct stat *stbuf)
+{
+ stbuf->st_mode = attr->mode;
+ stbuf->st_uid = attr->uid;
+ stbuf->st_gid = attr->gid;
+ stbuf->st_size = attr->size;
+ stbuf->st_atime = attr->atime;
+ stbuf->st_mtime = attr->mtime;
+ ST_ATIM_NSEC_SET(stbuf, attr->atimensec);
+ ST_MTIM_NSEC_SET(stbuf, attr->mtimensec);
+}
+
+static size_t iov_length(const struct iovec *iov, size_t count)
+{
+ size_t seg;
+ size_t ret = 0;
+
+ for (seg = 0; seg < count; seg++)
+ ret += iov[seg].iov_len;
+ return ret;
+}
+
+static void list_init_req(struct fuse_req *req)
+{
+ req->next = req;
+ req->prev = req;
+}
+
+static void list_del_req(struct fuse_req *req)
+{
+ struct fuse_req *prev = req->prev;
+ struct fuse_req *next = req->next;
+ prev->next = next;
+ next->prev = prev;
+}
+
+static void list_add_req(struct fuse_req *req, struct fuse_req *next)
+{
+ struct fuse_req *prev = next->prev;
+ req->next = next;
+ req->prev = prev;
+ prev->next = req;
+ next->prev = req;
+}
+
+static void destroy_req(fuse_req_t req)
+{
+ pthread_mutex_destroy(&req->lock);
+ free(req);
+}
+
+void fuse_free_req(fuse_req_t req)
+{
+ int ctr;
+ struct fuse_ll *f = req->f;
+
+ pthread_mutex_lock(&f->lock);
+ req->u.ni.func = NULL;
+ req->u.ni.data = NULL;
+ list_del_req(req);
+ ctr = --req->ctr;
+ pthread_mutex_unlock(&f->lock);
+ if (!ctr)
+ destroy_req(req);
+}
+
+static struct fuse_req *fuse_ll_alloc_req(struct fuse_ll *f)
+{
+ struct fuse_req *req;
+
+ req = (struct fuse_req *) calloc(1, sizeof(struct fuse_req));
+ if (req == NULL) {
+ fprintf(stderr, "fuse: failed to allocate request\n");
+ } else {
+ req->f = f;
+ req->ctr = 1;
+ list_init_req(req);
+ fuse_mutex_init(&req->lock);
+ }
+
+ return req;
+}
+
+
+static int fuse_send_msg(struct fuse_ll *f, struct fuse_chan *ch,
+ struct iovec *iov, int count)
+{
+ struct fuse_out_header *out = iov[0].iov_base;
+
+ out->len = iov_length(iov, count);
+ if (f->debug) {
+ if (out->unique == 0) {
+ fprintf(stderr, "NOTIFY: code=%d length=%u\n",
+ out->error, out->len);
+ } else if (out->error) {
+ fprintf(stderr,
+ " unique: %llu, error: %i (%s), outsize: %i\n",
+ (unsigned long long) out->unique, out->error,
+ strerror(-out->error), out->len);
+ } else {
+ fprintf(stderr,
+ " unique: %llu, success, outsize: %i\n",
+ (unsigned long long) out->unique, out->len);
+ }
+ }
+
+ return fuse_chan_send(ch, iov, count);
+}
+
+int fuse_send_reply_iov_nofree(fuse_req_t req, int error, struct iovec *iov,
+ int count)
+{
+ struct fuse_out_header out;
+
+ if (error <= -1000 || error > 0) {
+ fprintf(stderr, "fuse: bad error value: %i\n", error);
+ error = -ERANGE;
+ }
+
+ out.unique = req->unique;
+ out.error = error;
+
+ iov[0].iov_base = &out;
+ iov[0].iov_len = sizeof(struct fuse_out_header);
+
+ return fuse_send_msg(req->f, req->ch, iov, count);
+}
+
+static int send_reply_iov(fuse_req_t req, int error, struct iovec *iov,
+ int count)
+{
+ int res;
+
+ res = fuse_send_reply_iov_nofree(req, error, iov, count);
+ fuse_free_req(req);
+ return res;
+}
+
+static int send_reply(fuse_req_t req, int error, const void *arg,
+ size_t argsize)
+{
+ struct iovec iov[2];
+ int count = 1;
+ if (argsize) {
+ iov[1].iov_base = (void *) arg;
+ iov[1].iov_len = argsize;
+ count++;
+ }
+ return send_reply_iov(req, error, iov, count);
+}
+
+int fuse_reply_iov(fuse_req_t req, const struct iovec *iov, int count)
+{
+ int res;
+ struct iovec *padded_iov;
+
+ padded_iov = malloc((count + 1) * sizeof(struct iovec));
+ if (padded_iov == NULL)
+ return fuse_reply_err(req, ENOMEM);
+
+ memcpy(padded_iov + 1, iov, count * sizeof(struct iovec));
+ count++;
+
+ res = send_reply_iov(req, 0, padded_iov, count);
+ free(padded_iov);
+
+ return res;
+}
+
+size_t fuse_dirent_size(size_t namelen)
+{
+ return FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + namelen);
+}
+
+char *fuse_add_dirent(char *buf, const char *name, const struct stat *stbuf,
+ off64_t off)
+{
+ unsigned namelen = strlen(name);
+ unsigned entlen = FUSE_NAME_OFFSET + namelen;
+ unsigned entsize = fuse_dirent_size(namelen);
+ unsigned padlen = entsize - entlen;
+ struct fuse_dirent *dirent = (struct fuse_dirent *) buf;
+
+ dirent->ino = stbuf->st_ino;
+ dirent->off = off;
+ dirent->namelen = namelen;
+ dirent->type = (stbuf->st_mode & 0170000) >> 12;
+ strncpy(dirent->name, name, namelen);
+ if (padlen)
+ memset(buf + entlen, 0, padlen);
+
+ return buf + entsize;
+}
+
+size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize,
+ const char *name, const struct stat *stbuf, off64_t off)
+{
+ size_t entsize;
+
+ (void) req;
+ entsize = fuse_dirent_size(strlen(name));
+ if (entsize <= bufsize && buf)
+ fuse_add_dirent(buf, name, stbuf, off);
+ return entsize;
+}
+
+static void convert_statfs(const struct statvfs *stbuf,
+ struct fuse_kstatfs *kstatfs)
+{
+ kstatfs->bsize = stbuf->f_bsize;
+ kstatfs->frsize = stbuf->f_frsize;
+ kstatfs->blocks = stbuf->f_blocks;
+ kstatfs->bfree = stbuf->f_bfree;
+ kstatfs->bavail = stbuf->f_bavail;
+ kstatfs->files = stbuf->f_files;
+ kstatfs->ffree = stbuf->f_ffree;
+ kstatfs->namelen = stbuf->f_namemax;
+}
+
+static int send_reply_ok(fuse_req_t req, const void *arg, size_t argsize)
+{
+ return send_reply(req, 0, arg, argsize);
+}
+
+int fuse_reply_err(fuse_req_t req, int err)
+{
+ return send_reply(req, -err, NULL, 0);
+}
+
+void fuse_reply_none(fuse_req_t req)
+{
+ if (req->ch)
+ fuse_chan_send(req->ch, NULL, 0);
+ fuse_free_req(req);
+}
+
+static unsigned long calc_timeout_sec(double t)
+{
+ if (t > (double) ULONG_MAX)
+ return ULONG_MAX;
+ else if (t < 0.0)
+ return 0;
+ else
+ return (unsigned long) t;
+}
+
+static unsigned int calc_timeout_nsec(double t)
+{
+ double f = t - (double) calc_timeout_sec(t);
+ if (f < 0.0)
+ return 0;
+ else if (f >= 0.999999999)
+ return 999999999;
+ else
+ return (unsigned int) (f * 1.0e9);
+}
+
+static void fill_entry(struct fuse_entry_out *arg,
+ const struct fuse_entry_param *e)
+{
+ arg->nodeid = e->ino;
+ arg->generation = e->generation;
+ arg->entry_valid = calc_timeout_sec(e->entry_timeout);
+ arg->entry_valid_nsec = calc_timeout_nsec(e->entry_timeout);
+ arg->attr_valid = calc_timeout_sec(e->attr_timeout);
+ arg->attr_valid_nsec = calc_timeout_nsec(e->attr_timeout);
+ convert_stat(&e->attr, &arg->attr);
+}
+
+static void fill_open(struct fuse_open_out *arg,
+ const struct fuse_file_info *f)
+{
+ arg->fh = f->fh;
+ if (f->direct_io)
+ arg->open_flags |= FOPEN_DIRECT_IO;
+ if (f->keep_cache)
+ arg->open_flags |= FOPEN_KEEP_CACHE;
+ if (f->nonseekable)
+ arg->open_flags |= FOPEN_NONSEEKABLE;
+}
+
+int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
+{
+ struct fuse_entry_out arg;
+ size_t size = req->f->conn.proto_minor < 9 ?
+ FUSE_COMPAT_ENTRY_OUT_SIZE : sizeof(arg);
+
+ /* before ABI 7.4 e->ino == 0 was invalid, only ENOENT meant
+ negative entry */
+ if (!e->ino && req->f->conn.proto_minor < 4)
+ return fuse_reply_err(req, ENOENT);
+
+ memset(&arg, 0, sizeof(arg));
+ fill_entry(&arg, e);
+ return send_reply_ok(req, &arg, size);
+}
+
+int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e,
+ const struct fuse_file_info *f)
+{
+ char buf[sizeof(struct fuse_entry_out) + sizeof(struct fuse_open_out)];
+ size_t entrysize = req->f->conn.proto_minor < 9 ?
+ FUSE_COMPAT_ENTRY_OUT_SIZE : sizeof(struct fuse_entry_out);
+ struct fuse_entry_out *earg = (struct fuse_entry_out *) buf;
+ struct fuse_open_out *oarg = (struct fuse_open_out *) (buf + entrysize);
+
+ memset(buf, 0, sizeof(buf));
+ fill_entry(earg, e);
+ fill_open(oarg, f);
+ return send_reply_ok(req, buf,
+ entrysize + sizeof(struct fuse_open_out));
+}
+
+int fuse_reply_attr(fuse_req_t req, const struct stat *attr,
+ double attr_timeout)
+{
+ struct fuse_attr_out arg;
+ size_t size = req->f->conn.proto_minor < 9 ?
+ FUSE_COMPAT_ATTR_OUT_SIZE : sizeof(arg);
+
+ memset(&arg, 0, sizeof(arg));
+ arg.attr_valid = calc_timeout_sec(attr_timeout);
+ arg.attr_valid_nsec = calc_timeout_nsec(attr_timeout);
+ convert_stat(attr, &arg.attr);
+
+ return send_reply_ok(req, &arg, size);
+}
+
+int fuse_reply_readlink(fuse_req_t req, const char *linkname)
+{
+ return send_reply_ok(req, linkname, strlen(linkname));
+}
+
+int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *f)
+{
+ struct fuse_open_out arg;
+
+ memset(&arg, 0, sizeof(arg));
+ fill_open(&arg, f);
+ return send_reply_ok(req, &arg, sizeof(arg));
+}
+
+int fuse_reply_write(fuse_req_t req, size_t count)
+{
+ struct fuse_write_out arg;
+
+ memset(&arg, 0, sizeof(arg));
+ arg.size = count;
+
+ return send_reply_ok(req, &arg, sizeof(arg));
+}
+
+int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
+{
+ return send_reply_ok(req, buf, size);
+}
+
+static int fuse_send_data_iov_fallback(struct fuse_ll *f, struct fuse_chan *ch,
+ struct iovec *iov, int iov_count,
+ struct fuse_bufvec *buf,
+ size_t len)
+{
+ struct fuse_bufvec mem_buf = FUSE_BUFVEC_INIT(len);
+ void *mbuf;
+ int res;
+
+ /* Optimize common case */
+ if (buf->count == 1 && buf->idx == 0 && buf->off == 0 &&
+ !(buf->buf[0].flags & FUSE_BUF_IS_FD)) {
+ /* FIXME: also avoid memory copy if there are multiple buffers
+ but none of them contain an fd */
+
+ iov[iov_count].iov_base = buf->buf[0].mem;
+ iov[iov_count].iov_len = len;
+ iov_count++;
+ return fuse_send_msg(f, ch, iov, iov_count);
+ }
+
+ res = posix_memalign(&mbuf, pagesize, len);
+ if (res != 0)
+ return res;
+
+ mem_buf.buf[0].mem = mbuf;
+ res = fuse_buf_copy(&mem_buf, buf, 0);
+ if (res < 0) {
+ free(mbuf);
+ return -res;
+ }
+ len = res;
+
+ iov[iov_count].iov_base = mbuf;
+ iov[iov_count].iov_len = len;
+ iov_count++;
+ res = fuse_send_msg(f, ch, iov, iov_count);
+ free(mbuf);
+
+ return res;
+}
+
+struct fuse_ll_pipe {
+ size_t size;
+ int can_grow;
+ int pipe[2];
+};
+
+static void fuse_ll_pipe_free(struct fuse_ll_pipe *llp)
+{
+ close(llp->pipe[0]);
+ close(llp->pipe[1]);
+ free(llp);
+}
+
+#ifdef HAVE_SPLICE
+static struct fuse_ll_pipe *fuse_ll_get_pipe(struct fuse_ll *f)
+{
+ struct fuse_ll_pipe *llp = pthread_getspecific(f->pipe_key);
+ if (llp == NULL) {
+ int res;
+
+ llp = malloc(sizeof(struct fuse_ll_pipe));
+ if (llp == NULL)
+ return NULL;
+
+ res = pipe(llp->pipe);
+ if (res == -1) {
+ free(llp);
+ return NULL;
+ }
+
+ if (fcntl(llp->pipe[0], F_SETFL, O_NONBLOCK) == -1 ||
+ fcntl(llp->pipe[1], F_SETFL, O_NONBLOCK) == -1) {
+ close(llp->pipe[0]);
+ close(llp->pipe[1]);
+ free(llp);
+ return NULL;
+ }
+
+ /*
+ *the default size is 16 pages on linux
+ */
+ llp->size = pagesize * 16;
+ llp->can_grow = 1;
+
+ pthread_setspecific(f->pipe_key, llp);
+ }
+
+ return llp;
+}
+#endif
+
+static void fuse_ll_clear_pipe(struct fuse_ll *f)
+{
+ struct fuse_ll_pipe *llp = pthread_getspecific(f->pipe_key);
+ if (llp) {
+ pthread_setspecific(f->pipe_key, NULL);
+ fuse_ll_pipe_free(llp);
+ }
+}
+
+#if defined(HAVE_SPLICE) && defined(HAVE_VMSPLICE)
+static int read_back(int fd, char *buf, size_t len)
+{
+ int res;
+
+ res = read(fd, buf, len);
+ if (res == -1) {
+ fprintf(stderr, "fuse: internal error: failed to read back from pipe: %s\n", strerror(errno));
+ return -EIO;
+ }
+ if (res != len) {
+ fprintf(stderr, "fuse: internal error: short read back from pipe: %i from %zi\n", res, len);
+ return -EIO;
+ }
+ return 0;
+}
+
+static int fuse_send_data_iov(struct fuse_ll *f, struct fuse_chan *ch,
+ struct iovec *iov, int iov_count,
+ struct fuse_bufvec *buf, unsigned int flags)
+{
+ int res;
+ size_t len = fuse_buf_size(buf);
+ struct fuse_out_header *out = iov[0].iov_base;
+ struct fuse_ll_pipe *llp;
+ int splice_flags;
+ size_t pipesize;
+ size_t total_fd_size;
+ size_t idx;
+ size_t headerlen;
+ struct fuse_bufvec pipe_buf = FUSE_BUFVEC_INIT(len);
+
+ if (f->broken_splice_nonblock)
+ goto fallback;
+
+ if (flags & FUSE_BUF_NO_SPLICE)
+ goto fallback;
+
+ total_fd_size = 0;
+ for (idx = buf->idx; idx < buf->count; idx++) {
+ if (buf->buf[idx].flags & FUSE_BUF_IS_FD) {
+ total_fd_size = buf->buf[idx].size;
+ if (idx == buf->idx)
+ total_fd_size -= buf->off;
+ }
+ }
+ if (total_fd_size < 2 * pagesize)
+ goto fallback;
+
+ if (f->conn.proto_minor < 14 ||
+ !(f->conn.want & FUSE_CAP_SPLICE_WRITE))
+ goto fallback;
+
+ llp = fuse_ll_get_pipe(f);
+ if (llp == NULL)
+ goto fallback;
+
+
+ headerlen = iov_length(iov, iov_count);
+
+ out->len = headerlen + len;
+
+ /*
+ * Heuristic for the required pipe size, does not work if the
+ * source contains less than page size fragments
+ */
+ pipesize = pagesize * (iov_count + buf->count + 1) + out->len;
+
+ if (llp->size < pipesize) {
+ if (llp->can_grow) {
+ res = fcntl(llp->pipe[0], F_SETPIPE_SZ, pipesize);
+ if (res == -1) {
+ llp->can_grow = 0;
+ goto fallback;
+ }
+ llp->size = res;
+ }
+ if (llp->size < pipesize)
+ goto fallback;
+ }
+
+
+ res = vmsplice(llp->pipe[1], iov, iov_count, SPLICE_F_NONBLOCK);
+ if (res == -1)
+ goto fallback;
+
+ if (res != headerlen) {
+ res = -EIO;
+ fprintf(stderr, "fuse: short vmsplice to pipe: %u/%zu\n", res,
+ headerlen);
+ goto clear_pipe;
+ }
+
+ pipe_buf.buf[0].flags = FUSE_BUF_IS_FD;
+ pipe_buf.buf[0].fd = llp->pipe[1];
+
+ res = fuse_buf_copy(&pipe_buf, buf,
+ FUSE_BUF_FORCE_SPLICE | FUSE_BUF_SPLICE_NONBLOCK);
+ if (res < 0) {
+ if (res == -EAGAIN || res == -EINVAL) {
+ /*
+ * Should only get EAGAIN on kernels with
+ * broken SPLICE_F_NONBLOCK support (<=
+ * 2.6.35) where this error or a short read is
+ * returned even if the pipe itself is not
+ * full
+ *
+ * EINVAL might mean that splice can't handle
+ * this combination of input and output.
+ */
+ if (res == -EAGAIN)
+ f->broken_splice_nonblock = 1;
+
+ pthread_setspecific(f->pipe_key, NULL);
+ fuse_ll_pipe_free(llp);
+ goto fallback;
+ }
+ res = -res;
+ goto clear_pipe;
+ }
+
+ if (res != 0 && res < len) {
+ struct fuse_bufvec mem_buf = FUSE_BUFVEC_INIT(len);
+ void *mbuf;
+ size_t now_len = res;
+ /*
+ * For regular files a short count is either
+ * 1) due to EOF, or
+ * 2) because of broken SPLICE_F_NONBLOCK (see above)
+ *
+ * For other inputs it's possible that we overflowed
+ * the pipe because of small buffer fragments.
+ */
+
+ res = posix_memalign(&mbuf, pagesize, len);
+ if (res != 0)
+ goto clear_pipe;
+
+ mem_buf.buf[0].mem = mbuf;
+ mem_buf.off = now_len;
+ res = fuse_buf_copy(&mem_buf, buf, 0);
+ if (res > 0) {
+ char *tmpbuf;
+ size_t extra_len = res;
+ /*
+ * Trickiest case: got more data. Need to get
+ * back the data from the pipe and then fall
+ * back to regular write.
+ */
+ tmpbuf = malloc(headerlen);
+ if (tmpbuf == NULL) {
+ free(mbuf);
+ res = ENOMEM;
+ goto clear_pipe;
+ }
+ res = read_back(llp->pipe[0], tmpbuf, headerlen);
+ if (res != 0) {
+ free(mbuf);
+ goto clear_pipe;
+ }
+ free(tmpbuf);
+ res = read_back(llp->pipe[0], mbuf, now_len);
+ if (res != 0) {
+ free(mbuf);
+ goto clear_pipe;
+ }
+ len = now_len + extra_len;
+ iov[iov_count].iov_base = mbuf;
+ iov[iov_count].iov_len = len;
+ iov_count++;
+ res = fuse_send_msg(f, ch, iov, iov_count);
+ free(mbuf);
+ return res;
+ }
+ free(mbuf);
+ res = now_len;
+ }
+ len = res;
+ out->len = headerlen + len;
+
+ if (f->debug) {
+ fprintf(stderr,
+ " unique: %llu, success, outsize: %i (splice)\n",
+ (unsigned long long) out->unique, out->len);
+ }
+
+ splice_flags = 0;
+ if ((flags & FUSE_BUF_SPLICE_MOVE) &&
+ (f->conn.want & FUSE_CAP_SPLICE_MOVE))
+ splice_flags |= SPLICE_F_MOVE;
+
+ res = splice(llp->pipe[0], NULL,
+ fuse_chan_fd(ch), NULL, out->len, splice_flags);
+ if (res == -1) {
+ res = -errno;
+ perror("fuse: splice from pipe");
+ goto clear_pipe;
+ }
+ if (res != out->len) {
+ res = -EIO;
+ fprintf(stderr, "fuse: short splice from pipe: %u/%u\n",
+ res, out->len);
+ goto clear_pipe;
+ }
+ return 0;
+
+clear_pipe:
+ fuse_ll_clear_pipe(f);
+ return res;
+
+fallback:
+ return fuse_send_data_iov_fallback(f, ch, iov, iov_count, buf, len);
+}
+#else
+static int fuse_send_data_iov(struct fuse_ll *f, struct fuse_chan *ch,
+ struct iovec *iov, int iov_count,
+ struct fuse_bufvec *buf, unsigned int flags)
+{
+ size_t len = fuse_buf_size(buf);
+ (void) flags;
+
+ return fuse_send_data_iov_fallback(f, ch, iov, iov_count, buf, len);
+}
+#endif
+
+int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv,
+ enum fuse_buf_copy_flags flags)
+{
+ struct iovec iov[2];
+ struct fuse_out_header out;
+ int res;
+
+ iov[0].iov_base = &out;
+ iov[0].iov_len = sizeof(struct fuse_out_header);
+
+ out.unique = req->unique;
+ out.error = 0;
+
+ res = fuse_send_data_iov(req->f, req->ch, iov, 1, bufv, flags);
+ if (res <= 0) {
+ fuse_free_req(req);
+ return res;
+ } else {
+ return fuse_reply_err(req, res);
+ }
+}
+
+int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf)
+{
+ struct fuse_statfs_out arg;
+ size_t size = req->f->conn.proto_minor < 4 ?
+ FUSE_COMPAT_STATFS_SIZE : sizeof(arg);
+
+ memset(&arg, 0, sizeof(arg));
+ convert_statfs(stbuf, &arg.st);
+
+ return send_reply_ok(req, &arg, size);
+}
+
+int fuse_reply_xattr(fuse_req_t req, size_t count)
+{
+ struct fuse_getxattr_out arg;
+
+ memset(&arg, 0, sizeof(arg));
+ arg.size = count;
+
+ return send_reply_ok(req, &arg, sizeof(arg));
+}
+
+int fuse_reply_lock(fuse_req_t req, const struct flock *lock)
+{
+ struct fuse_lk_out arg;
+
+ memset(&arg, 0, sizeof(arg));
+ arg.lk.type = lock->l_type;
+ if (lock->l_type != F_UNLCK) {
+ arg.lk.start = lock->l_start;
+ if (lock->l_len == 0)
+ arg.lk.end = OFFSET_MAX;
+ else
+ arg.lk.end = lock->l_start + lock->l_len - 1;
+ }
+ arg.lk.pid = lock->l_pid;
+ return send_reply_ok(req, &arg, sizeof(arg));
+}
+
+int fuse_reply_bmap(fuse_req_t req, uint64_t idx)
+{
+ struct fuse_bmap_out arg;
+
+ memset(&arg, 0, sizeof(arg));
+ arg.block = idx;
+
+ return send_reply_ok(req, &arg, sizeof(arg));
+}
+
+static struct fuse_ioctl_iovec *fuse_ioctl_iovec_copy(const struct iovec *iov,
+ size_t count)
+{
+ struct fuse_ioctl_iovec *fiov;
+ size_t i;
+
+ fiov = malloc(sizeof(fiov[0]) * count);
+ if (!fiov)
+ return NULL;
+
+ for (i = 0; i < count; i++) {
+ fiov[i].base = (uintptr_t) iov[i].iov_base;
+ fiov[i].len = iov[i].iov_len;
+ }
+
+ return fiov;
+}
+
+int fuse_reply_ioctl_retry(fuse_req_t req,
+ const struct iovec *in_iov, size_t in_count,
+ const struct iovec *out_iov, size_t out_count)
+{
+ struct fuse_ioctl_out arg;
+ struct fuse_ioctl_iovec *in_fiov = NULL;
+ struct fuse_ioctl_iovec *out_fiov = NULL;
+ struct iovec iov[4];
+ size_t count = 1;
+ int res;
+
+ memset(&arg, 0, sizeof(arg));
+ arg.flags |= FUSE_IOCTL_RETRY;
+ arg.in_iovs = in_count;
+ arg.out_iovs = out_count;
+ iov[count].iov_base = &arg;
+ iov[count].iov_len = sizeof(arg);
+ count++;
+
+ if (req->f->conn.proto_minor < 16) {
+ if (in_count) {
+ iov[count].iov_base = (void *)in_iov;
+ iov[count].iov_len = sizeof(in_iov[0]) * in_count;
+ count++;
+ }
+
+ if (out_count) {
+ iov[count].iov_base = (void *)out_iov;
+ iov[count].iov_len = sizeof(out_iov[0]) * out_count;
+ count++;
+ }
+ } else {
+ /* Can't handle non-compat 64bit ioctls on 32bit */
+ if (sizeof(void *) == 4 && req->ioctl_64bit) {
+ res = fuse_reply_err(req, EINVAL);
+ goto out;
+ }
+
+ if (in_count) {
+ in_fiov = fuse_ioctl_iovec_copy(in_iov, in_count);
+ if (!in_fiov)
+ goto enomem;
+
+ iov[count].iov_base = (void *)in_fiov;
+ iov[count].iov_len = sizeof(in_fiov[0]) * in_count;
+ count++;
+ }
+ if (out_count) {
+ out_fiov = fuse_ioctl_iovec_copy(out_iov, out_count);
+ if (!out_fiov)
+ goto enomem;
+
+ iov[count].iov_base = (void *)out_fiov;
+ iov[count].iov_len = sizeof(out_fiov[0]) * out_count;
+ count++;
+ }
+ }
+
+ res = send_reply_iov(req, 0, iov, count);
+out:
+ free(in_fiov);
+ free(out_fiov);
+
+ return res;
+
+enomem:
+ res = fuse_reply_err(req, ENOMEM);
+ goto out;
+}
+
+int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t size)
+{
+ struct fuse_ioctl_out arg;
+ struct iovec iov[3];
+ size_t count = 1;
+
+ memset(&arg, 0, sizeof(arg));
+ arg.result = result;
+ iov[count].iov_base = &arg;
+ iov[count].iov_len = sizeof(arg);
+ count++;
+
+ if (size) {
+ iov[count].iov_base = (char *) buf;
+ iov[count].iov_len = size;
+ count++;
+ }
+
+ return send_reply_iov(req, 0, iov, count);
+}
+
+int fuse_reply_ioctl_iov(fuse_req_t req, int result, const struct iovec *iov,
+ int count)
+{
+ struct iovec *padded_iov;
+ struct fuse_ioctl_out arg;
+ int res;
+
+ padded_iov = malloc((count + 2) * sizeof(struct iovec));
+ if (padded_iov == NULL)
+ return fuse_reply_err(req, ENOMEM);
+
+ memset(&arg, 0, sizeof(arg));
+ arg.result = result;
+ padded_iov[1].iov_base = &arg;
+ padded_iov[1].iov_len = sizeof(arg);
+
+ memcpy(&padded_iov[2], iov, count * sizeof(struct iovec));
+
+ res = send_reply_iov(req, 0, padded_iov, count + 2);
+ free(padded_iov);
+
+ return res;
+}
+
+int fuse_reply_poll(fuse_req_t req, unsigned revents)
+{
+ struct fuse_poll_out arg;
+
+ memset(&arg, 0, sizeof(arg));
+ arg.revents = revents;
+
+ return send_reply_ok(req, &arg, sizeof(arg));
+}
+
+static void do_lookup(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+ char *name = (char *) inarg;
+
+ if (req->f->op.lookup)
+ req->f->op.lookup(req, nodeid, name);
+ else
+ fuse_reply_err(req, ENOSYS);
+}
+
+static void do_forget(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+ struct fuse_forget_in *arg = (struct fuse_forget_in *) inarg;
+
+ if (req->f->op.forget)
+ req->f->op.forget(req, nodeid, arg->nlookup);
+ else
+ fuse_reply_none(req);
+}
+
+static void do_batch_forget(fuse_req_t req, fuse_ino_t nodeid,
+ const void *inarg)
+{
+ struct fuse_batch_forget_in *arg = (void *) inarg;
+ struct fuse_forget_one *param = (void *) PARAM(arg);
+ unsigned int i;
+
+ (void) nodeid;
+
+ if (req->f->op.forget_multi) {
+ req->f->op.forget_multi(req, arg->count,
+ (struct fuse_forget_data *) param);
+ } else if (req->f->op.forget) {
+ for (i = 0; i < arg->count; i++) {
+ struct fuse_forget_one *forget = &param[i];
+ struct fuse_req *dummy_req;
+
+ dummy_req = fuse_ll_alloc_req(req->f);
+ if (dummy_req == NULL)
+ break;
+
+ dummy_req->unique = req->unique;
+ dummy_req->ctx = req->ctx;
+ dummy_req->ch = NULL;
+
+ req->f->op.forget(dummy_req, forget->nodeid,
+ forget->nlookup);
+ }
+ fuse_reply_none(req);
+ } else {
+ fuse_reply_none(req);
+ }
+}
+
+static void do_getattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+ struct fuse_file_info *fip = NULL;
+ struct fuse_file_info fi;
+
+ if (req->f->conn.proto_minor >= 9) {
+ struct fuse_getattr_in *arg = (struct fuse_getattr_in *) inarg;
+
+ if (arg->getattr_flags & FUSE_GETATTR_FH) {
+ memset(&fi, 0, sizeof(fi));
+ fi.fh = arg->fh;
+ fi.fh_old = fi.fh;
+ fip = &fi;
+ }
+ }
+
+ if (req->f->op.getattr)
+ req->f->op.getattr(req, nodeid, fip);
+ else
+ fuse_reply_err(req, ENOSYS);
+}
+
+static void do_setattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+ struct fuse_setattr_in *arg = (struct fuse_setattr_in *) inarg;
+
+ if (req->f->op.setattr) {
+ struct fuse_file_info *fi = NULL;
+ struct fuse_file_info fi_store;
+ struct stat stbuf;
+ memset(&stbuf, 0, sizeof(stbuf));
+ convert_attr(arg, &stbuf);
+ if (arg->valid & FATTR_FH) {
+ arg->valid &= ~FATTR_FH;
+ memset(&fi_store, 0, sizeof(fi_store));
+ fi = &fi_store;
+ fi->fh = arg->fh;
+ fi->fh_old = fi->fh;
+ }
+ arg->valid &=
+ FUSE_SET_ATTR_MODE |
+ FUSE_SET_ATTR_UID |
+ FUSE_SET_ATTR_GID |
+ FUSE_SET_ATTR_SIZE |
+ FUSE_SET_ATTR_ATIME |
+ FUSE_SET_ATTR_MTIME |
+ FUSE_SET_ATTR_ATIME_NOW |
+ FUSE_SET_ATTR_MTIME_NOW;
+
+ req->f->op.setattr(req, nodeid, &stbuf, arg->valid, fi);
+ } else
+ fuse_reply_err(req, ENOSYS);
+}
+
+static void do_access(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+ struct fuse_access_in *arg = (struct fuse_access_in *) inarg;
+
+ if (req->f->op.access)
+ req->f->op.access(req, nodeid, arg->mask);
+ else
+ fuse_reply_err(req, ENOSYS);
+}
+
+static void do_readlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+ (void) inarg;
+
+ if (req->f->op.readlink)
+ req->f->op.readlink(req, nodeid);
+ else
+ fuse_reply_err(req, ENOSYS);
+}
+
+static void do_mknod(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+ struct fuse_mknod_in *arg = (struct fuse_mknod_in *) inarg;
+ char *name = PARAM(arg);
+
+ if (req->f->conn.proto_minor >= 12)
+ req->ctx.umask = arg->umask;
+ else
+ name = (char *) inarg + FUSE_COMPAT_MKNOD_IN_SIZE;
+
+ if (req->f->op.mknod)
+ req->f->op.mknod(req, nodeid, name, arg->mode, arg->rdev);
+ else
+ fuse_reply_err(req, ENOSYS);
+}
+
+static void do_mkdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+ struct fuse_mkdir_in *arg = (struct fuse_mkdir_in *) inarg;
+
+ if (req->f->conn.proto_minor >= 12)
+ req->ctx.umask = arg->umask;
+
+ if (req->f->op.mkdir)
+ req->f->op.mkdir(req, nodeid, PARAM(arg), arg->mode);
+ else
+ fuse_reply_err(req, ENOSYS);
+}
+
+static void do_unlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+ char *name = (char *) inarg;
+
+ if (req->f->op.unlink)
+ req->f->op.unlink(req, nodeid, name);
+ else
+ fuse_reply_err(req, ENOSYS);
+}
+
+static void do_rmdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+ char *name = (char *) inarg;
+
+ if (req->f->op.rmdir)
+ req->f->op.rmdir(req, nodeid, name);
+ else
+ fuse_reply_err(req, ENOSYS);
+}
+
+static void do_symlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+ char *name = (char *) inarg;
+ char *linkname = ((char *) inarg) + strlen((char *) inarg) + 1;
+
+ if (req->f->op.symlink)
+ req->f->op.symlink(req, linkname, nodeid, name);
+ else
+ fuse_reply_err(req, ENOSYS);
+}
+
+static void do_rename(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+ struct fuse_rename_in *arg = (struct fuse_rename_in *) inarg;
+ char *oldname = PARAM(arg);
+ char *newname = oldname + strlen(oldname) + 1;
+
+ if (req->f->op.rename)
+ req->f->op.rename(req, nodeid, oldname, arg->newdir, newname);
+ else
+ fuse_reply_err(req, ENOSYS);
+}
+
+static void do_link(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+ struct fuse_link_in *arg = (struct fuse_link_in *) inarg;
+
+ if (req->f->op.link)
+ req->f->op.link(req, arg->oldnodeid, nodeid, PARAM(arg));
+ else
+ fuse_reply_err(req, ENOSYS);
+}
+
+static void do_create(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+ struct fuse_create_in *arg = (struct fuse_create_in *) inarg;
+
+ if (req->f->op.create) {
+ struct fuse_file_info fi;
+ char *name = PARAM(arg);
+
+ memset(&fi, 0, sizeof(fi));
+ fi.flags = arg->flags;
+
+ if (req->f->conn.proto_minor >= 12)
+ req->ctx.umask = arg->umask;
+ else
+ name = (char *) inarg + sizeof(struct fuse_open_in);
+
+ req->f->op.create(req, nodeid, name, arg->mode, &fi);
+ } else
+ fuse_reply_err(req, ENOSYS);
+}
+
+static void do_open(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+ struct fuse_open_in *arg = (struct fuse_open_in *) inarg;
+ struct fuse_file_info fi;
+
+ memset(&fi, 0, sizeof(fi));
+ fi.flags = arg->flags;
+
+ if (req->f->op.open)
+ req->f->op.open(req, nodeid, &fi);
+ else
+ fuse_reply_open(req, &fi);
+}
+
+static void do_read(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+ struct fuse_read_in *arg = (struct fuse_read_in *) inarg;
+
+ if (req->f->op.read) {
+ struct fuse_file_info fi;
+
+ memset(&fi, 0, sizeof(fi));
+ fi.fh = arg->fh;
+ fi.fh_old = fi.fh;
+ if (req->f->conn.proto_minor >= 9) {
+ fi.lock_owner = arg->lock_owner;
+ fi.flags = arg->flags;
+ }
+ req->f->op.read(req, nodeid, arg->size, arg->offset, &fi);
+ } else
+ fuse_reply_err(req, ENOSYS);
+}
+
+static void do_write(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+ struct fuse_write_in *arg = (struct fuse_write_in *) inarg;
+ struct fuse_file_info fi;
+ char *param;
+
+ memset(&fi, 0, sizeof(fi));
+ fi.fh = arg->fh;
+ fi.fh_old = fi.fh;
+ fi.writepage = arg->write_flags & 1;
+
+ if (req->f->conn.proto_minor < 9) {
+ param = ((char *) arg) + FUSE_COMPAT_WRITE_IN_SIZE;
+ } else {
+ fi.lock_owner = arg->lock_owner;
+ fi.flags = arg->flags;
+ param = PARAM(arg);
+ }
+
+ if (req->f->op.write)
+ req->f->op.write(req, nodeid, param, arg->size,
+ arg->offset, &fi);
+ else
+ fuse_reply_err(req, ENOSYS);
+}
+
+static void do_write_buf(fuse_req_t req, fuse_ino_t nodeid, const void *inarg,
+ const struct fuse_buf *ibuf)
+{
+ struct fuse_ll *f = req->f;
+ struct fuse_bufvec bufv = {
+ .buf[0] = *ibuf,
+ .count = 1,
+ };
+ struct fuse_write_in *arg = (struct fuse_write_in *) inarg;
+ struct fuse_file_info fi;
+
+ memset(&fi, 0, sizeof(fi));
+ fi.fh = arg->fh;
+ fi.fh_old = fi.fh;
+ fi.writepage = arg->write_flags & 1;
+
+ if (req->f->conn.proto_minor < 9) {
+ bufv.buf[0].mem = ((char *) arg) + FUSE_COMPAT_WRITE_IN_SIZE;
+ bufv.buf[0].size -= sizeof(struct fuse_in_header) +
+ FUSE_COMPAT_WRITE_IN_SIZE;
+ assert(!(bufv.buf[0].flags & FUSE_BUF_IS_FD));
+ } else {
+ fi.lock_owner = arg->lock_owner;
+ fi.flags = arg->flags;
+ if (!(bufv.buf[0].flags & FUSE_BUF_IS_FD))
+ bufv.buf[0].mem = PARAM(arg);
+
+ bufv.buf[0].size -= sizeof(struct fuse_in_header) +
+ sizeof(struct fuse_write_in);
+ }
+ if (bufv.buf[0].size < arg->size) {
+ fprintf(stderr, "fuse: do_write_buf: buffer size too small\n");
+ fuse_reply_err(req, EIO);
+ goto out;
+ }
+ bufv.buf[0].size = arg->size;
+
+ req->f->op.write_buf(req, nodeid, &bufv, arg->offset, &fi);
+
+out:
+ /* Need to reset the pipe if ->write_buf() didn't consume all data */
+ if ((ibuf->flags & FUSE_BUF_IS_FD) && bufv.idx < bufv.count)
+ fuse_ll_clear_pipe(f);
+}
+
+static void do_flush(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+ struct fuse_flush_in *arg = (struct fuse_flush_in *) inarg;
+ struct fuse_file_info fi;
+
+ memset(&fi, 0, sizeof(fi));
+ fi.fh = arg->fh;
+ fi.fh_old = fi.fh;
+ fi.flush = 1;
+ if (req->f->conn.proto_minor >= 7)
+ fi.lock_owner = arg->lock_owner;
+
+ if (req->f->op.flush)
+ req->f->op.flush(req, nodeid, &fi);
+ else
+ fuse_reply_err(req, ENOSYS);
+}
+
+static void do_release(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+ struct fuse_release_in *arg = (struct fuse_release_in *) inarg;
+ struct fuse_file_info fi;
+
+ memset(&fi, 0, sizeof(fi));
+ fi.flags = arg->flags;
+ fi.fh = arg->fh;
+ fi.fh_old = fi.fh;
+ if (req->f->conn.proto_minor >= 8) {
+ fi.flush = (arg->release_flags & FUSE_RELEASE_FLUSH) ? 1 : 0;
+ fi.lock_owner = arg->lock_owner;
+ }
+ if (arg->release_flags & FUSE_RELEASE_FLOCK_UNLOCK) {
+ fi.flock_release = 1;
+ fi.lock_owner = arg->lock_owner;
+ }
+
+ if (req->f->op.release)
+ req->f->op.release(req, nodeid, &fi);
+ else
+ fuse_reply_err(req, 0);
+}
+
+static void do_fsync(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+ struct fuse_fsync_in *arg = (struct fuse_fsync_in *) inarg;
+ struct fuse_file_info fi;
+
+ memset(&fi, 0, sizeof(fi));
+ fi.fh = arg->fh;
+ fi.fh_old = fi.fh;
+
+ if (req->f->op.fsync)
+ req->f->op.fsync(req, nodeid, arg->fsync_flags & 1, &fi);
+ else
+ fuse_reply_err(req, ENOSYS);
+}
+
+static void do_opendir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+ struct fuse_open_in *arg = (struct fuse_open_in *) inarg;
+ struct fuse_file_info fi;
+
+ memset(&fi, 0, sizeof(fi));
+ fi.flags = arg->flags;
+
+ if (req->f->op.opendir)
+ req->f->op.opendir(req, nodeid, &fi);
+ else
+ fuse_reply_open(req, &fi);
+}
+
+static void do_readdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+ struct fuse_read_in *arg = (struct fuse_read_in *) inarg;
+ struct fuse_file_info fi;
+
+ memset(&fi, 0, sizeof(fi));
+ fi.fh = arg->fh;
+ fi.fh_old = fi.fh;
+
+ if (req->f->op.readdir)
+ req->f->op.readdir(req, nodeid, arg->size, arg->offset, &fi);
+ else
+ fuse_reply_err(req, ENOSYS);
+}
+
+static void do_releasedir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+ struct fuse_release_in *arg = (struct fuse_release_in *) inarg;
+ struct fuse_file_info fi;
+
+ memset(&fi, 0, sizeof(fi));
+ fi.flags = arg->flags;
+ fi.fh = arg->fh;
+ fi.fh_old = fi.fh;
+
+ if (req->f->op.releasedir)
+ req->f->op.releasedir(req, nodeid, &fi);
+ else
+ fuse_reply_err(req, 0);
+}
+
+static void do_fsyncdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+ struct fuse_fsync_in *arg = (struct fuse_fsync_in *) inarg;
+ struct fuse_file_info fi;
+
+ memset(&fi, 0, sizeof(fi));
+ fi.fh = arg->fh;
+ fi.fh_old = fi.fh;
+
+ if (req->f->op.fsyncdir)
+ req->f->op.fsyncdir(req, nodeid, arg->fsync_flags & 1, &fi);
+ else
+ fuse_reply_err(req, ENOSYS);
+}
+
+static void do_statfs(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+ (void) nodeid;
+ (void) inarg;
+
+ if (req->f->op.statfs)
+ req->f->op.statfs(req, nodeid);
+ else {
+ struct statvfs buf = {
+ .f_namemax = 255,
+ .f_bsize = 512,
+ };
+ fuse_reply_statfs(req, &buf);
+ }
+}
+
+static void do_setxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+ struct fuse_setxattr_in *arg = (struct fuse_setxattr_in *) inarg;
+ char *name = PARAM(arg);
+ char *value = name + strlen(name) + 1;
+
+ if (req->f->op.setxattr)
+ req->f->op.setxattr(req, nodeid, name, value, arg->size,
+ arg->flags);
+ else
+ fuse_reply_err(req, ENOSYS);
+}
+
+static void do_getxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+ struct fuse_getxattr_in *arg = (struct fuse_getxattr_in *) inarg;
+
+ if (req->f->op.getxattr)
+ req->f->op.getxattr(req, nodeid, PARAM(arg), arg->size);
+ else
+ fuse_reply_err(req, ENOSYS);
+}
+
+static void do_listxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+ struct fuse_getxattr_in *arg = (struct fuse_getxattr_in *) inarg;
+
+ if (req->f->op.listxattr)
+ req->f->op.listxattr(req, nodeid, arg->size);
+ else
+ fuse_reply_err(req, ENOSYS);
+}
+
+static void do_removexattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+ char *name = (char *) inarg;
+
+ if (req->f->op.removexattr)
+ req->f->op.removexattr(req, nodeid, name);
+ else
+ fuse_reply_err(req, ENOSYS);
+}
+
+static void convert_fuse_file_lock(struct fuse_file_lock *fl,
+ struct flock *flock)
+{
+ memset(flock, 0, sizeof(struct flock));
+ flock->l_type = fl->type;
+ flock->l_whence = SEEK_SET;
+ flock->l_start = fl->start;
+ if (fl->end == OFFSET_MAX)
+ flock->l_len = 0;
+ else
+ flock->l_len = fl->end - fl->start + 1;
+ flock->l_pid = fl->pid;
+}
+
+static void do_getlk(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+ struct fuse_lk_in *arg = (struct fuse_lk_in *) inarg;
+ struct fuse_file_info fi;
+ struct flock flock;
+
+ memset(&fi, 0, sizeof(fi));
+ fi.fh = arg->fh;
+ fi.lock_owner = arg->owner;
+
+ convert_fuse_file_lock(&arg->lk, &flock);
+ if (req->f->op.getlk)
+ req->f->op.getlk(req, nodeid, &fi, &flock);
+ else
+ fuse_reply_err(req, ENOSYS);
+}
+
+static void do_setlk_common(fuse_req_t req, fuse_ino_t nodeid,
+ const void *inarg, int sleep)
+{
+ struct fuse_lk_in *arg = (struct fuse_lk_in *) inarg;
+ struct fuse_file_info fi;
+ struct flock flock;
+
+ memset(&fi, 0, sizeof(fi));
+ fi.fh = arg->fh;
+ fi.lock_owner = arg->owner;
+
+ if (arg->lk_flags & FUSE_LK_FLOCK) {
+ int op = 0;
+
+ switch (arg->lk.type) {
+ case F_RDLCK:
+ op = LOCK_SH;
+ break;
+ case F_WRLCK:
+ op = LOCK_EX;
+ break;
+ case F_UNLCK:
+ op = LOCK_UN;
+ break;
+ }
+ if (!sleep)
+ op |= LOCK_NB;
+
+ if (req->f->op.flock)
+ req->f->op.flock(req, nodeid, &fi, op);
+ else
+ fuse_reply_err(req, ENOSYS);
+ } else {
+ convert_fuse_file_lock(&arg->lk, &flock);
+ if (req->f->op.setlk)
+ req->f->op.setlk(req, nodeid, &fi, &flock, sleep);
+ else
+ fuse_reply_err(req, ENOSYS);
+ }
+}
+
+static void do_setlk(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+ do_setlk_common(req, nodeid, inarg, 0);
+}
+
+static void do_setlkw(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+ do_setlk_common(req, nodeid, inarg, 1);
+}
+
+static int find_interrupted(struct fuse_ll *f, struct fuse_req *req)
+{
+ struct fuse_req *curr;
+
+ for (curr = f->list.next; curr != &f->list; curr = curr->next) {
+ if (curr->unique == req->u.i.unique) {
+ fuse_interrupt_func_t func;
+ void *data;
+
+ curr->ctr++;
+ pthread_mutex_unlock(&f->lock);
+
+ /* Ugh, ugly locking */
+ pthread_mutex_lock(&curr->lock);
+ pthread_mutex_lock(&f->lock);
+ curr->interrupted = 1;
+ func = curr->u.ni.func;
+ data = curr->u.ni.data;
+ pthread_mutex_unlock(&f->lock);
+ if (func)
+ func(curr, data);
+ pthread_mutex_unlock(&curr->lock);
+
+ pthread_mutex_lock(&f->lock);
+ curr->ctr--;
+ if (!curr->ctr)
+ destroy_req(curr);
+
+ return 1;
+ }
+ }
+ for (curr = f->interrupts.next; curr != &f->interrupts;
+ curr = curr->next) {
+ if (curr->u.i.unique == req->u.i.unique)
+ return 1;
+ }
+ return 0;
+}
+
+static void do_interrupt(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+ struct fuse_interrupt_in *arg = (struct fuse_interrupt_in *) inarg;
+ struct fuse_ll *f = req->f;
+
+ (void) nodeid;
+ if (f->debug)
+ fprintf(stderr, "INTERRUPT: %llu\n",
+ (unsigned long long) arg->unique);
+
+ req->u.i.unique = arg->unique;
+
+ pthread_mutex_lock(&f->lock);
+ if (find_interrupted(f, req))
+ destroy_req(req);
+ else
+ list_add_req(req, &f->interrupts);
+ pthread_mutex_unlock(&f->lock);
+}
+
+static struct fuse_req *check_interrupt(struct fuse_ll *f, struct fuse_req *req)
+{
+ struct fuse_req *curr;
+
+ for (curr = f->interrupts.next; curr != &f->interrupts;
+ curr = curr->next) {
+ if (curr->u.i.unique == req->unique) {
+ req->interrupted = 1;
+ list_del_req(curr);
+ free(curr);
+ return NULL;
+ }
+ }
+ curr = f->interrupts.next;
+ if (curr != &f->interrupts) {
+ list_del_req(curr);
+ list_init_req(curr);
+ return curr;
+ } else
+ return NULL;
+}
+
+static void do_bmap(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+ struct fuse_bmap_in *arg = (struct fuse_bmap_in *) inarg;
+
+ if (req->f->op.bmap)
+ req->f->op.bmap(req, nodeid, arg->blocksize, arg->block);
+ else
+ fuse_reply_err(req, ENOSYS);
+}
+
+static void do_ioctl(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+ struct fuse_ioctl_in *arg = (struct fuse_ioctl_in *) inarg;
+ unsigned int flags = arg->flags;
+ void *in_buf = arg->in_size ? PARAM(arg) : NULL;
+ struct fuse_file_info fi;
+
+ if (flags & FUSE_IOCTL_DIR &&
+ !(req->f->conn.want & FUSE_CAP_IOCTL_DIR)) {
+ fuse_reply_err(req, ENOTTY);
+ return;
+ }
+
+ memset(&fi, 0, sizeof(fi));
+ fi.fh = arg->fh;
+ fi.fh_old = fi.fh;
+
+ if (sizeof(void *) == 4 && req->f->conn.proto_minor >= 16 &&
+ !(flags & FUSE_IOCTL_32BIT)) {
+ req->ioctl_64bit = 1;
+ }
+
+ if (req->f->op.ioctl)
+ req->f->op.ioctl(req, nodeid, arg->cmd,
+ (void *)(uintptr_t)arg->arg, &fi, flags,
+ in_buf, arg->in_size, arg->out_size);
+ else
+ fuse_reply_err(req, ENOSYS);
+}
+
+void fuse_pollhandle_destroy(struct fuse_pollhandle *ph)
+{
+ free(ph);
+}
+
+static void do_poll(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+ struct fuse_poll_in *arg = (struct fuse_poll_in *) inarg;
+ struct fuse_file_info fi;
+
+ memset(&fi, 0, sizeof(fi));
+ fi.fh = arg->fh;
+ fi.fh_old = fi.fh;
+
+ if (req->f->op.poll) {
+ struct fuse_pollhandle *ph = NULL;
+
+ if (arg->flags & FUSE_POLL_SCHEDULE_NOTIFY) {
+ ph = malloc(sizeof(struct fuse_pollhandle));
+ if (ph == NULL) {
+ fuse_reply_err(req, ENOMEM);
+ return;
+ }
+ ph->kh = arg->kh;
+ ph->ch = req->ch;
+ ph->f = req->f;
+ }
+
+ req->f->op.poll(req, nodeid, &fi, ph);
+ } else {
+ fuse_reply_err(req, ENOSYS);
+ }
+}
+
+static void do_fallocate(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+ struct fuse_fallocate_in *arg = (struct fuse_fallocate_in *) inarg;
+ struct fuse_file_info fi;
+
+ memset(&fi, 0, sizeof(fi));
+ fi.fh = arg->fh;
+
+ if (req->f->op.fallocate)
+ req->f->op.fallocate(req, nodeid, arg->mode, arg->offset, arg->length, &fi);
+ else
+ fuse_reply_err(req, ENOSYS);
+}
+
+static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+ struct fuse_init_in *arg = (struct fuse_init_in *) inarg;
+ struct fuse_init_out outarg;
+ struct fuse_ll *f = req->f;
+ size_t bufsize = fuse_chan_bufsize(req->ch);
+
+ (void) nodeid;
+ if (f->debug) {
+ fprintf(stderr, "INIT: %u.%u\n", arg->major, arg->minor);
+ if (arg->major == 7 && arg->minor >= 6) {
+ fprintf(stderr, "flags=0x%08x\n", arg->flags);
+ fprintf(stderr, "max_readahead=0x%08x\n",
+ arg->max_readahead);
+ }
+ }
+ f->conn.proto_major = arg->major;
+ f->conn.proto_minor = arg->minor;
+ f->conn.capable = 0;
+ f->conn.want = 0;
+
+ memset(&outarg, 0, sizeof(outarg));
+ outarg.major = FUSE_KERNEL_VERSION;
+ outarg.minor = FUSE_KERNEL_MINOR_VERSION;
+
+ if (arg->major < 7) {
+ fprintf(stderr, "fuse: unsupported protocol version: %u.%u\n",
+ arg->major, arg->minor);
+ fuse_reply_err(req, EPROTO);
+ return;
+ }
+
+ if (arg->major > 7) {
+ /* Wait for a second INIT request with a 7.X version */
+ send_reply_ok(req, &outarg, sizeof(outarg));
+ return;
+ }
+
+ if (arg->minor >= 6) {
+ if (f->conn.async_read)
+ f->conn.async_read = arg->flags & FUSE_ASYNC_READ;
+ if (arg->max_readahead < f->conn.max_readahead)
+ f->conn.max_readahead = arg->max_readahead;
+ if (arg->flags & FUSE_ASYNC_READ)
+ f->conn.capable |= FUSE_CAP_ASYNC_READ;
+ if (arg->flags & FUSE_POSIX_LOCKS)
+ f->conn.capable |= FUSE_CAP_POSIX_LOCKS;
+ if (arg->flags & FUSE_ATOMIC_O_TRUNC)
+ f->conn.capable |= FUSE_CAP_ATOMIC_O_TRUNC;
+ if (arg->flags & FUSE_EXPORT_SUPPORT)
+ f->conn.capable |= FUSE_CAP_EXPORT_SUPPORT;
+ if (arg->flags & FUSE_BIG_WRITES)
+ f->conn.capable |= FUSE_CAP_BIG_WRITES;
+ if (arg->flags & FUSE_DONT_MASK)
+ f->conn.capable |= FUSE_CAP_DONT_MASK;
+ if (arg->flags & FUSE_FLOCK_LOCKS)
+ f->conn.capable |= FUSE_CAP_FLOCK_LOCKS;
+ } else {
+ f->conn.async_read = 0;
+ f->conn.max_readahead = 0;
+ }
+
+ if (req->f->conn.proto_minor >= 14) {
+#ifdef HAVE_SPLICE
+#ifdef HAVE_VMSPLICE
+ f->conn.capable |= FUSE_CAP_SPLICE_WRITE | FUSE_CAP_SPLICE_MOVE;
+ if (f->splice_write)
+ f->conn.want |= FUSE_CAP_SPLICE_WRITE;
+ if (f->splice_move)
+ f->conn.want |= FUSE_CAP_SPLICE_MOVE;
+#endif
+ f->conn.capable |= FUSE_CAP_SPLICE_READ;
+ if (f->splice_read)
+ f->conn.want |= FUSE_CAP_SPLICE_READ;
+#endif
+ }
+ if (req->f->conn.proto_minor >= 18)
+ f->conn.capable |= FUSE_CAP_IOCTL_DIR;
+
+ if (f->atomic_o_trunc)
+ f->conn.want |= FUSE_CAP_ATOMIC_O_TRUNC;
+ if (f->op.getlk && f->op.setlk && !f->no_remote_posix_lock)
+ f->conn.want |= FUSE_CAP_POSIX_LOCKS;
+ if (f->op.flock && !f->no_remote_flock)
+ f->conn.want |= FUSE_CAP_FLOCK_LOCKS;
+ if (f->big_writes)
+ f->conn.want |= FUSE_CAP_BIG_WRITES;
+
+ 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);
+
+ if (f->no_splice_read)
+ f->conn.want &= ~FUSE_CAP_SPLICE_READ;
+ if (f->no_splice_write)
+ f->conn.want &= ~FUSE_CAP_SPLICE_WRITE;
+ if (f->no_splice_move)
+ f->conn.want &= ~FUSE_CAP_SPLICE_MOVE;
+
+ if (f->conn.async_read || (f->conn.want & FUSE_CAP_ASYNC_READ))
+ outarg.flags |= FUSE_ASYNC_READ;
+ if (f->conn.want & FUSE_CAP_POSIX_LOCKS)
+ outarg.flags |= FUSE_POSIX_LOCKS;
+ if (f->conn.want & FUSE_CAP_ATOMIC_O_TRUNC)
+ outarg.flags |= FUSE_ATOMIC_O_TRUNC;
+ if (f->conn.want & FUSE_CAP_EXPORT_SUPPORT)
+ outarg.flags |= FUSE_EXPORT_SUPPORT;
+ if (f->conn.want & FUSE_CAP_BIG_WRITES)
+ outarg.flags |= FUSE_BIG_WRITES;
+ if (f->conn.want & FUSE_CAP_DONT_MASK)
+ outarg.flags |= FUSE_DONT_MASK;
+ if (f->conn.want & FUSE_CAP_FLOCK_LOCKS)
+ outarg.flags |= FUSE_FLOCK_LOCKS;
+ outarg.max_readahead = f->conn.max_readahead;
+ outarg.max_write = f->conn.max_write;
+ if (f->conn.proto_minor >= 13) {
+ if (f->conn.max_background >= (1 << 16))
+ f->conn.max_background = (1 << 16) - 1;
+ if (f->conn.congestion_threshold > f->conn.max_background)
+ f->conn.congestion_threshold = f->conn.max_background;
+ if (!f->conn.congestion_threshold) {
+ f->conn.congestion_threshold =
+ f->conn.max_background * 3 / 4;
+ }
+
+ outarg.max_background = f->conn.max_background;
+ outarg.congestion_threshold = f->conn.congestion_threshold;
+ }
+
+ if (f->debug) {
+ fprintf(stderr, " INIT: %u.%u\n", outarg.major, outarg.minor);
+ fprintf(stderr, " flags=0x%08x\n", outarg.flags);
+ fprintf(stderr, " max_readahead=0x%08x\n",
+ outarg.max_readahead);
+ fprintf(stderr, " max_write=0x%08x\n", outarg.max_write);
+ fprintf(stderr, " max_background=%i\n",
+ outarg.max_background);
+ fprintf(stderr, " congestion_threshold=%i\n",
+ outarg.congestion_threshold);
+ }
+
+ send_reply_ok(req, &outarg, arg->minor < 5 ? 8 : sizeof(outarg));
+}
+
+static void do_destroy(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+ struct fuse_ll *f = req->f;
+
+ (void) nodeid;
+ (void) inarg;
+
+ f->got_destroy = 1;
+ if (f->op.destroy)
+ f->op.destroy(f->userdata);
+
+ send_reply_ok(req, NULL, 0);
+}
+
+static void list_del_nreq(struct fuse_notify_req *nreq)
+{
+ struct fuse_notify_req *prev = nreq->prev;
+ struct fuse_notify_req *next = nreq->next;
+ prev->next = next;
+ next->prev = prev;
+}
+
+static void list_add_nreq(struct fuse_notify_req *nreq,
+ struct fuse_notify_req *next)
+{
+ struct fuse_notify_req *prev = next->prev;
+ nreq->next = next;
+ nreq->prev = prev;
+ prev->next = nreq;
+ next->prev = nreq;
+}
+
+static void list_init_nreq(struct fuse_notify_req *nreq)
+{
+ nreq->next = nreq;
+ nreq->prev = nreq;
+}
+
+static void do_notify_reply(fuse_req_t req, fuse_ino_t nodeid,
+ const void *inarg, const struct fuse_buf *buf)
+{
+ struct fuse_ll *f = req->f;
+ struct fuse_notify_req *nreq;
+ struct fuse_notify_req *head;
+
+ pthread_mutex_lock(&f->lock);
+ head = &f->notify_list;
+ for (nreq = head->next; nreq != head; nreq = nreq->next) {
+ if (nreq->unique == req->unique) {
+ list_del_nreq(nreq);
+ break;
+ }
+ }
+ pthread_mutex_unlock(&f->lock);
+
+ if (nreq != head)
+ nreq->reply(nreq, req, nodeid, inarg, buf);
+}
+
+static int send_notify_iov(struct fuse_ll *f, struct fuse_chan *ch,
+ int notify_code, struct iovec *iov, int count)
+{
+ struct fuse_out_header out;
+
+ if (!f->got_init)
+ return -ENOTCONN;
+
+ out.unique = 0;
+ out.error = notify_code;
+ iov[0].iov_base = &out;
+ iov[0].iov_len = sizeof(struct fuse_out_header);
+
+ return fuse_send_msg(f, ch, iov, count);
+}
+
+int fuse_lowlevel_notify_poll(struct fuse_pollhandle *ph)
+{
+ if (ph != NULL) {
+ struct fuse_notify_poll_wakeup_out outarg;
+ struct iovec iov[2];
+
+ outarg.kh = ph->kh;
+
+ iov[1].iov_base = &outarg;
+ iov[1].iov_len = sizeof(outarg);
+
+ return send_notify_iov(ph->f, ph->ch, FUSE_NOTIFY_POLL, iov, 2);
+ } else {
+ return 0;
+ }
+}
+
+int fuse_lowlevel_notify_inval_inode(struct fuse_chan *ch, fuse_ino_t ino,
+ off64_t off, off64_t len)
+{
+ struct fuse_notify_inval_inode_out outarg;
+ struct fuse_ll *f;
+ struct iovec iov[2];
+
+ if (!ch)
+ return -EINVAL;
+
+ f = (struct fuse_ll *)fuse_session_data(fuse_chan_session(ch));
+ if (!f)
+ return -ENODEV;
+
+ outarg.ino = ino;
+ outarg.off = off;
+ outarg.len = len;
+
+ iov[1].iov_base = &outarg;
+ iov[1].iov_len = sizeof(outarg);
+
+ return send_notify_iov(f, ch, FUSE_NOTIFY_INVAL_INODE, iov, 2);
+}
+
+int fuse_lowlevel_notify_inval_entry(struct fuse_chan *ch, fuse_ino_t parent,
+ const char *name, size_t namelen)
+{
+ struct fuse_notify_inval_entry_out outarg;
+ struct fuse_ll *f;
+ struct iovec iov[3];
+
+ if (!ch)
+ return -EINVAL;
+
+ f = (struct fuse_ll *)fuse_session_data(fuse_chan_session(ch));
+ if (!f)
+ return -ENODEV;
+
+ outarg.parent = parent;
+ outarg.namelen = namelen;
+ outarg.padding = 0;
+
+ iov[1].iov_base = &outarg;
+ iov[1].iov_len = sizeof(outarg);
+ iov[2].iov_base = (void *)name;
+ iov[2].iov_len = namelen + 1;
+
+ return send_notify_iov(f, ch, FUSE_NOTIFY_INVAL_ENTRY, iov, 3);
+}
+
+int fuse_lowlevel_notify_delete(struct fuse_chan *ch,
+ fuse_ino_t parent, fuse_ino_t child,
+ const char *name, size_t namelen)
+{
+ struct fuse_notify_delete_out outarg;
+ struct fuse_ll *f;
+ struct iovec iov[3];
+
+ if (!ch)
+ return -EINVAL;
+
+ f = (struct fuse_ll *)fuse_session_data(fuse_chan_session(ch));
+ if (!f)
+ return -ENODEV;
+
+ if (f->conn.proto_minor < 18)
+ return -ENOSYS;
+
+ outarg.parent = parent;
+ outarg.child = child;
+ outarg.namelen = namelen;
+ outarg.padding = 0;
+
+ iov[1].iov_base = &outarg;
+ iov[1].iov_len = sizeof(outarg);
+ iov[2].iov_base = (void *)name;
+ iov[2].iov_len = namelen + 1;
+
+ return send_notify_iov(f, ch, FUSE_NOTIFY_DELETE, iov, 3);
+}
+
+int fuse_lowlevel_notify_store(struct fuse_chan *ch, fuse_ino_t ino,
+ off64_t offset, struct fuse_bufvec *bufv,
+ enum fuse_buf_copy_flags flags)
+{
+ struct fuse_out_header out;
+ struct fuse_notify_store_out outarg;
+ struct fuse_ll *f;
+ struct iovec iov[3];
+ size_t size = fuse_buf_size(bufv);
+ int res;
+
+ if (!ch)
+ return -EINVAL;
+
+ f = (struct fuse_ll *)fuse_session_data(fuse_chan_session(ch));
+ if (!f)
+ return -ENODEV;
+
+ if (f->conn.proto_minor < 15)
+ return -ENOSYS;
+
+ out.unique = 0;
+ out.error = FUSE_NOTIFY_STORE;
+
+ outarg.nodeid = ino;
+ outarg.offset = offset;
+ outarg.size = size;
+
+ iov[0].iov_base = &out;
+ iov[0].iov_len = sizeof(out);
+ iov[1].iov_base = &outarg;
+ iov[1].iov_len = sizeof(outarg);
+
+ res = fuse_send_data_iov(f, ch, iov, 2, bufv, flags);
+ if (res > 0)
+ res = -res;
+
+ return res;
+}
+
+struct fuse_retrieve_req {
+ struct fuse_notify_req nreq;
+ void *cookie;
+};
+
+static void fuse_ll_retrieve_reply(struct fuse_notify_req *nreq,
+ fuse_req_t req, fuse_ino_t ino,
+ const void *inarg,
+ const struct fuse_buf *ibuf)
+{
+ struct fuse_ll *f = req->f;
+ struct fuse_retrieve_req *rreq =
+ container_of(nreq, struct fuse_retrieve_req, nreq);
+ const struct fuse_notify_retrieve_in *arg = inarg;
+ struct fuse_bufvec bufv = {
+ .buf[0] = *ibuf,
+ .count = 1,
+ };
+
+ if (!(bufv.buf[0].flags & FUSE_BUF_IS_FD))
+ bufv.buf[0].mem = PARAM(arg);
+
+ bufv.buf[0].size -= sizeof(struct fuse_in_header) +
+ sizeof(struct fuse_notify_retrieve_in);
+
+ if (bufv.buf[0].size < arg->size) {
+ fprintf(stderr, "fuse: retrieve reply: buffer size too small\n");
+ fuse_reply_none(req);
+ goto out;
+ }
+ bufv.buf[0].size = arg->size;
+
+ if (req->f->op.retrieve_reply) {
+ req->f->op.retrieve_reply(req, rreq->cookie, ino,
+ arg->offset, &bufv);
+ } else {
+ fuse_reply_none(req);
+ }
+out:
+ free(rreq);
+ if ((ibuf->flags & FUSE_BUF_IS_FD) && bufv.idx < bufv.count)
+ fuse_ll_clear_pipe(f);
+}
+
+int fuse_lowlevel_notify_retrieve(struct fuse_chan *ch, fuse_ino_t ino,
+ size_t size, off64_t offset, void *cookie)
+{
+ struct fuse_notify_retrieve_out outarg;
+ struct fuse_ll *f;
+ struct iovec iov[2];
+ struct fuse_retrieve_req *rreq;
+ int err;
+
+ if (!ch)
+ return -EINVAL;
+
+ f = (struct fuse_ll *)fuse_session_data(fuse_chan_session(ch));
+ if (!f)
+ return -ENODEV;
+
+ if (f->conn.proto_minor < 15)
+ return -ENOSYS;
+
+ rreq = malloc(sizeof(*rreq));
+ if (rreq == NULL)
+ return -ENOMEM;
+
+ pthread_mutex_lock(&f->lock);
+ rreq->cookie = cookie;
+ rreq->nreq.unique = f->notify_ctr++;
+ rreq->nreq.reply = fuse_ll_retrieve_reply;
+ list_add_nreq(&rreq->nreq, &f->notify_list);
+ pthread_mutex_unlock(&f->lock);
+
+ outarg.notify_unique = rreq->nreq.unique;
+ outarg.nodeid = ino;
+ outarg.offset = offset;
+ outarg.size = size;
+
+ iov[1].iov_base = &outarg;
+ iov[1].iov_len = sizeof(outarg);
+
+ err = send_notify_iov(f, ch, FUSE_NOTIFY_RETRIEVE, iov, 2);
+ if (err) {
+ pthread_mutex_lock(&f->lock);
+ list_del_nreq(&rreq->nreq);
+ pthread_mutex_unlock(&f->lock);
+ free(rreq);
+ }
+
+ return err;
+}
+
+void *fuse_req_userdata(fuse_req_t req)
+{
+ return req->f->userdata;
+}
+
+const struct fuse_ctx *fuse_req_ctx(fuse_req_t req)
+{
+ return &req->ctx;
+}
+
+/*
+ * The size of fuse_ctx got extended, so need to be careful about
+ * incompatibility (i.e. a new binary cannot work with an old
+ * library).
+ */
+const struct fuse_ctx *fuse_req_ctx_compat24(fuse_req_t req);
+const struct fuse_ctx *fuse_req_ctx_compat24(fuse_req_t req)
+{
+ return fuse_req_ctx(req);
+}
+#ifndef __NetBSD__
+FUSE_SYMVER(".symver fuse_req_ctx_compat24,fuse_req_ctx@FUSE_2.4");
+#endif
+
+
+void fuse_req_interrupt_func(fuse_req_t req, fuse_interrupt_func_t func,
+ void *data)
+{
+ pthread_mutex_lock(&req->lock);
+ pthread_mutex_lock(&req->f->lock);
+ req->u.ni.func = func;
+ req->u.ni.data = data;
+ pthread_mutex_unlock(&req->f->lock);
+ if (req->interrupted && func)
+ func(req, data);
+ pthread_mutex_unlock(&req->lock);
+}
+
+int fuse_req_interrupted(fuse_req_t req)
+{
+ int interrupted;
+
+ pthread_mutex_lock(&req->f->lock);
+ interrupted = req->interrupted;
+ pthread_mutex_unlock(&req->f->lock);
+
+ return interrupted;
+}
+
+static struct {
+ void (*func)(fuse_req_t, fuse_ino_t, const void *);
+ const char *name;
+} fuse_ll_ops[] = {
+ [FUSE_LOOKUP] = { do_lookup, "LOOKUP" },
+ [FUSE_FORGET] = { do_forget, "FORGET" },
+ [FUSE_GETATTR] = { do_getattr, "GETATTR" },
+ [FUSE_SETATTR] = { do_setattr, "SETATTR" },
+ [FUSE_READLINK] = { do_readlink, "READLINK" },
+ [FUSE_SYMLINK] = { do_symlink, "SYMLINK" },
+ [FUSE_MKNOD] = { do_mknod, "MKNOD" },
+ [FUSE_MKDIR] = { do_mkdir, "MKDIR" },
+ [FUSE_UNLINK] = { do_unlink, "UNLINK" },
+ [FUSE_RMDIR] = { do_rmdir, "RMDIR" },
+ [FUSE_RENAME] = { do_rename, "RENAME" },
+ [FUSE_LINK] = { do_link, "LINK" },
+ [FUSE_OPEN] = { do_open, "OPEN" },
+ [FUSE_READ] = { do_read, "READ" },
+ [FUSE_WRITE] = { do_write, "WRITE" },
+ [FUSE_STATFS] = { do_statfs, "STATFS" },
+ [FUSE_RELEASE] = { do_release, "RELEASE" },
+ [FUSE_FSYNC] = { do_fsync, "FSYNC" },
+ [FUSE_SETXATTR] = { do_setxattr, "SETXATTR" },
+ [FUSE_GETXATTR] = { do_getxattr, "GETXATTR" },
+ [FUSE_LISTXATTR] = { do_listxattr, "LISTXATTR" },
+ [FUSE_REMOVEXATTR] = { do_removexattr, "REMOVEXATTR" },
+ [FUSE_FLUSH] = { do_flush, "FLUSH" },
+ [FUSE_INIT] = { do_init, "INIT" },
+ [FUSE_OPENDIR] = { do_opendir, "OPENDIR" },
+ [FUSE_READDIR] = { do_readdir, "READDIR" },
+ [FUSE_RELEASEDIR] = { do_releasedir, "RELEASEDIR" },
+ [FUSE_FSYNCDIR] = { do_fsyncdir, "FSYNCDIR" },
+ [FUSE_GETLK] = { do_getlk, "GETLK" },
+ [FUSE_SETLK] = { do_setlk, "SETLK" },
+ [FUSE_SETLKW] = { do_setlkw, "SETLKW" },
+ [FUSE_ACCESS] = { do_access, "ACCESS" },
+ [FUSE_CREATE] = { do_create, "CREATE" },
+ [FUSE_INTERRUPT] = { do_interrupt, "INTERRUPT" },
+ [FUSE_BMAP] = { do_bmap, "BMAP" },
+ [FUSE_IOCTL] = { do_ioctl, "IOCTL" },
+ [FUSE_POLL] = { do_poll, "POLL" },
+ [FUSE_FALLOCATE] = { do_fallocate, "FALLOCATE" },
+ [FUSE_DESTROY] = { do_destroy, "DESTROY" },
+ [FUSE_NOTIFY_REPLY] = { (void *) 1, "NOTIFY_REPLY" },
+ [FUSE_BATCH_FORGET] = { do_batch_forget, "BATCH_FORGET" },
+ [CUSE_INIT] = { cuse_lowlevel_init, "CUSE_INIT" },
+};
+
+#define FUSE_MAXOP (sizeof(fuse_ll_ops) / sizeof(fuse_ll_ops[0]))
+
+static const char *opname(enum fuse_opcode opcode)
+{
+ if (opcode >= FUSE_MAXOP || !fuse_ll_ops[opcode].name)
+ return "???";
+ else
+ return fuse_ll_ops[opcode].name;
+}
+
+static int fuse_ll_copy_from_pipe(struct fuse_bufvec *dst,
+ struct fuse_bufvec *src)
+{
+ int res = fuse_buf_copy(dst, src, 0);
+ if (res < 0) {
+ fprintf(stderr, "fuse: copy from pipe: %s\n", strerror(-res));
+ return res;
+ }
+ if (res < fuse_buf_size(dst)) {
+ fprintf(stderr, "fuse: copy from pipe: short read\n");
+ return -1;
+ }
+ return 0;
+}
+
+static void fuse_ll_process_buf(void *data, const struct fuse_buf *buf,
+ struct fuse_chan *ch)
+{
+ struct fuse_ll *f = (struct fuse_ll *) data;
+ const size_t write_header_size = sizeof(struct fuse_in_header) +
+ sizeof(struct fuse_write_in);
+ struct fuse_bufvec bufv = { .buf[0] = *buf, .count = 1 };
+ struct fuse_bufvec tmpbuf = FUSE_BUFVEC_INIT(write_header_size);
+ struct fuse_in_header *in;
+ const void *inarg;
+ struct fuse_req *req;
+ void *mbuf = NULL;
+ int err;
+ int res;
+
+ if (buf->flags & FUSE_BUF_IS_FD) {
+ if (buf->size < tmpbuf.buf[0].size)
+ tmpbuf.buf[0].size = buf->size;
+
+ mbuf = malloc(tmpbuf.buf[0].size);
+ if (mbuf == NULL) {
+ fprintf(stderr, "fuse: failed to allocate header\n");
+ goto clear_pipe;
+ }
+ tmpbuf.buf[0].mem = mbuf;
+
+ res = fuse_ll_copy_from_pipe(&tmpbuf, &bufv);
+ if (res < 0)
+ goto clear_pipe;
+
+ in = mbuf;
+ } else {
+ in = buf->mem;
+ }
+
+ if (f->debug) {
+ fprintf(stderr,
+ "unique: %llu, opcode: %s (%i), nodeid: %lu, insize: %zu, pid: %u\n",
+ (unsigned long long) in->unique,
+ opname((enum fuse_opcode) in->opcode), in->opcode,
+ (unsigned long) in->nodeid, buf->size, in->pid);
+ }
+
+ req = fuse_ll_alloc_req(f);
+ if (req == NULL) {
+ struct fuse_out_header out = {
+ .unique = in->unique,
+ .error = -ENOMEM,
+ };
+ struct iovec iov = {
+ .iov_base = &out,
+ .iov_len = sizeof(struct fuse_out_header),
+ };
+
+ fuse_send_msg(f, ch, &iov, 1);
+ goto clear_pipe;
+ }
+
+ req->unique = in->unique;
+ req->ctx.uid = in->uid;
+ req->ctx.gid = in->gid;
+ req->ctx.pid = in->pid;
+ req->ch = ch;
+
+ err = EIO;
+ if (!f->got_init) {
+ enum fuse_opcode expected;
+
+ expected = f->cuse_data ? CUSE_INIT : FUSE_INIT;
+ if (in->opcode != expected)
+ goto reply_err;
+ } else if (in->opcode == FUSE_INIT || in->opcode == CUSE_INIT)
+ goto reply_err;
+
+ err = EACCES;
+ if (f->allow_root && in->uid != f->owner && in->uid != 0 &&
+ in->opcode != FUSE_INIT && in->opcode != FUSE_READ &&
+ in->opcode != FUSE_WRITE && in->opcode != FUSE_FSYNC &&
+ in->opcode != FUSE_RELEASE && in->opcode != FUSE_READDIR &&
+ in->opcode != FUSE_FSYNCDIR && in->opcode != FUSE_RELEASEDIR &&
+ in->opcode != FUSE_NOTIFY_REPLY)
+ goto reply_err;
+
+ err = ENOSYS;
+ if (in->opcode >= FUSE_MAXOP || !fuse_ll_ops[in->opcode].func)
+ goto reply_err;
+ if (in->opcode != FUSE_INTERRUPT) {
+ struct fuse_req *intr;
+ pthread_mutex_lock(&f->lock);
+ intr = check_interrupt(f, req);
+ list_add_req(req, &f->list);
+ pthread_mutex_unlock(&f->lock);
+ if (intr)
+ fuse_reply_err(intr, EAGAIN);
+ }
+
+ if ((buf->flags & FUSE_BUF_IS_FD) && write_header_size < buf->size &&
+ (in->opcode != FUSE_WRITE || !f->op.write_buf) &&
+ in->opcode != FUSE_NOTIFY_REPLY) {
+ void *newmbuf;
+
+ err = ENOMEM;
+ newmbuf = realloc(mbuf, buf->size);
+ if (newmbuf == NULL)
+ goto reply_err;
+ mbuf = newmbuf;
+
+ tmpbuf = FUSE_BUFVEC_INIT(buf->size - write_header_size);
+ tmpbuf.buf[0].mem = mbuf + write_header_size;
+
+ res = fuse_ll_copy_from_pipe(&tmpbuf, &bufv);
+ err = -res;
+ if (res < 0)
+ goto reply_err;
+
+ in = mbuf;
+ }
+
+ inarg = (void *) &in[1];
+ if (in->opcode == FUSE_WRITE && f->op.write_buf)
+ do_write_buf(req, in->nodeid, inarg, buf);
+ else if (in->opcode == FUSE_NOTIFY_REPLY)
+ do_notify_reply(req, in->nodeid, inarg, buf);
+ else
+ fuse_ll_ops[in->opcode].func(req, in->nodeid, inarg);
+
+out_free:
+ free(mbuf);
+ return;
+
+reply_err:
+ fuse_reply_err(req, err);
+clear_pipe:
+ if (buf->flags & FUSE_BUF_IS_FD)
+ fuse_ll_clear_pipe(f);
+ goto out_free;
+}
+
+static void fuse_ll_process(void *data, const char *buf, size_t len,
+ struct fuse_chan *ch)
+{
+ struct fuse_buf fbuf = {
+ .mem = (void *) buf,
+ .size = len,
+ };
+
+ fuse_ll_process_buf(data, &fbuf, ch);
+}
+
+enum {
+ KEY_HELP,
+ KEY_VERSION,
+};
+
+static const struct fuse_opt fuse_ll_opts[] = {
+ { "debug", offsetof(struct fuse_ll, debug), 1 },
+ { "-d", offsetof(struct fuse_ll, debug), 1 },
+ { "allow_root", offsetof(struct fuse_ll, allow_root), 1 },
+ { "max_write=%u", offsetof(struct fuse_ll, conn.max_write), 0 },
+ { "max_readahead=%u", offsetof(struct fuse_ll, conn.max_readahead), 0 },
+ { "max_background=%u", offsetof(struct fuse_ll, conn.max_background), 0 },
+ { "congestion_threshold=%u",
+ offsetof(struct fuse_ll, conn.congestion_threshold), 0 },
+ { "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_posix_lock), 1},
+ { "no_remote_lock", offsetof(struct fuse_ll, no_remote_flock), 1},
+ { "no_remote_flock", offsetof(struct fuse_ll, no_remote_flock), 1},
+ { "no_remote_posix_lock", offsetof(struct fuse_ll, no_remote_posix_lock), 1},
+ { "big_writes", offsetof(struct fuse_ll, big_writes), 1},
+ { "splice_write", offsetof(struct fuse_ll, splice_write), 1},
+ { "no_splice_write", offsetof(struct fuse_ll, no_splice_write), 1},
+ { "splice_move", offsetof(struct fuse_ll, splice_move), 1},
+ { "no_splice_move", offsetof(struct fuse_ll, no_splice_move), 1},
+ { "splice_read", offsetof(struct fuse_ll, splice_read), 1},
+ { "no_splice_read", offsetof(struct fuse_ll, no_splice_read), 1},
+ FUSE_OPT_KEY("max_read=", FUSE_OPT_KEY_DISCARD),
+ FUSE_OPT_KEY("-h", KEY_HELP),
+ FUSE_OPT_KEY("--help", KEY_HELP),
+ FUSE_OPT_KEY("-V", KEY_VERSION),
+ FUSE_OPT_KEY("--version", KEY_VERSION),
+ FUSE_OPT_END
+};
+
+static void fuse_ll_version(void)
+{
+ fprintf(stderr, "using FUSE kernel interface version %i.%i\n",
+ FUSE_KERNEL_VERSION, FUSE_KERNEL_MINOR_VERSION);
+}
+
+static void fuse_ll_help(void)
+{
+ fprintf(stderr,
+" -o max_write=N set maximum size of write requests\n"
+" -o max_readahead=N set maximum readahead\n"
+" -o max_background=N set number of maximum background requests\n"
+" -o congestion_threshold=N set kernel's congestion threshold\n"
+" -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 no_remote_lock disable remote file locking\n"
+" -o no_remote_flock disable remote file locking (BSD)\n"
+" -o no_remote_posix_lock disable remove file locking (POSIX)\n"
+" -o [no_]splice_write use splice to write to the fuse device\n"
+" -o [no_]splice_move move data while splicing to the fuse device\n"
+" -o [no_]splice_read use splice to read from the fuse device\n"
+);
+}
+
+static int fuse_ll_opt_proc(void *data, const char *arg, int key,
+ struct fuse_args *outargs)
+{
+ (void) data; (void) outargs;
+
+ switch (key) {
+ case KEY_HELP:
+ fuse_ll_help();
+ break;
+
+ case KEY_VERSION:
+ fuse_ll_version();
+ break;
+
+ default:
+ fprintf(stderr, "fuse: unknown option `%s'\n", arg);
+ }
+
+ return -1;
+}
+
+int fuse_lowlevel_is_lib_option(const char *opt)
+{
+ return fuse_opt_match(fuse_ll_opts, opt);
+}
+
+static void fuse_ll_destroy(void *data)
+{
+ struct fuse_ll *f = (struct fuse_ll *) data;
+ struct fuse_ll_pipe *llp;
+
+ if (f->got_init && !f->got_destroy) {
+ if (f->op.destroy)
+ f->op.destroy(f->userdata);
+ }
+ llp = pthread_getspecific(f->pipe_key);
+ if (llp != NULL)
+ fuse_ll_pipe_free(llp);
+ pthread_key_delete(f->pipe_key);
+ pthread_mutex_destroy(&f->lock);
+ free(f->cuse_data);
+ free(f);
+}
+
+static void fuse_ll_pipe_destructor(void *data)
+{
+ struct fuse_ll_pipe *llp = data;
+ fuse_ll_pipe_free(llp);
+}
+
+#ifdef HAVE_SPLICE
+static int fuse_ll_receive_buf(struct fuse_session *se, struct fuse_buf *buf,
+ struct fuse_chan **chp)
+{
+ struct fuse_chan *ch = *chp;
+ struct fuse_ll *f = fuse_session_data(se);
+ size_t bufsize = buf->size;
+ struct fuse_ll_pipe *llp;
+ struct fuse_buf tmpbuf;
+ int err;
+ int res;
+
+ if (f->conn.proto_minor < 14 || !(f->conn.want & FUSE_CAP_SPLICE_READ))
+ goto fallback;
+
+ llp = fuse_ll_get_pipe(f);
+ if (llp == NULL)
+ goto fallback;
+
+ if (llp->size < bufsize) {
+ if (llp->can_grow) {
+ res = fcntl(llp->pipe[0], F_SETPIPE_SZ, bufsize);
+ if (res == -1) {
+ llp->can_grow = 0;
+ goto fallback;
+ }
+ llp->size = res;
+ }
+ if (llp->size < bufsize)
+ goto fallback;
+ }
+
+ res = splice(fuse_chan_fd(ch), NULL, llp->pipe[1], NULL, bufsize, 0);
+ err = errno;
+
+ if (fuse_session_exited(se))
+ return 0;
+
+ if (res == -1) {
+ if (err == ENODEV) {
+ fuse_session_exit(se);
+ return 0;
+ }
+ if (err != EINTR && err != EAGAIN)
+ perror("fuse: splice from device");
+ return -err;
+ }
+
+ if (res < sizeof(struct fuse_in_header)) {
+ fprintf(stderr, "short splice from fuse device\n");
+ return -EIO;
+ }
+
+ tmpbuf = (struct fuse_buf) {
+ .size = res,
+ .flags = FUSE_BUF_IS_FD,
+ .fd = llp->pipe[0],
+ };
+
+ /*
+ * Don't bother with zero copy for small requests.
+ * fuse_loop_mt() needs to check for FORGET so this more than
+ * just an optimization.
+ */
+ if (res < sizeof(struct fuse_in_header) +
+ sizeof(struct fuse_write_in) + pagesize) {
+ struct fuse_bufvec src = { .buf[0] = tmpbuf, .count = 1 };
+ struct fuse_bufvec dst = { .buf[0] = *buf, .count = 1 };
+
+ res = fuse_buf_copy(&dst, &src, 0);
+ if (res < 0) {
+ fprintf(stderr, "fuse: copy from pipe: %s\n",
+ strerror(-res));
+ fuse_ll_clear_pipe(f);
+ return res;
+ }
+ if (res < tmpbuf.size) {
+ fprintf(stderr, "fuse: copy from pipe: short read\n");
+ fuse_ll_clear_pipe(f);
+ return -EIO;
+ }
+ buf->size = tmpbuf.size;
+ return buf->size;
+ }
+
+ *buf = tmpbuf;
+
+ return res;
+
+fallback:
+ res = fuse_chan_recv(chp, buf->mem, bufsize);
+ if (res <= 0)
+ return res;
+
+ buf->size = res;
+
+ return res;
+}
+#else
+static int fuse_ll_receive_buf(struct fuse_session *se, struct fuse_buf *buf,
+ struct fuse_chan **chp)
+{
+ (void) se;
+
+ int res = fuse_chan_recv(chp, buf->mem, buf->size);
+ if (res <= 0)
+ return res;
+
+ buf->size = res;
+
+ return res;
+}
+#endif
+
+
+/*
+ * always call fuse_lowlevel_new_common() internally, to work around a
+ * misfeature in the FreeBSD runtime linker, which links the old
+ * version of a symbol to internal references.
+ */
+struct fuse_session *fuse_lowlevel_new_common(struct fuse_args *args,
+ const struct fuse_lowlevel_ops *op,
+ size_t op_size, void *userdata)
+{
+ int err;
+ struct fuse_ll *f;
+ struct fuse_session *se;
+ struct fuse_session_ops sop = {
+ .process = fuse_ll_process,
+ .destroy = fuse_ll_destroy,
+ };
+
+ if (sizeof(struct fuse_lowlevel_ops) < op_size) {
+ fprintf(stderr, "fuse: warning: library too old, some operations may not work\n");
+ op_size = sizeof(struct fuse_lowlevel_ops);
+ }
+
+ f = (struct fuse_ll *) calloc(1, sizeof(struct fuse_ll));
+ if (f == NULL) {
+ fprintf(stderr, "fuse: failed to allocate fuse object\n");
+ goto out;
+ }
+
+ f->conn.async_read = 1;
+ f->conn.max_write = UINT_MAX;
+ f->conn.max_readahead = UINT_MAX;
+ f->atomic_o_trunc = 0;
+ list_init_req(&f->list);
+ list_init_req(&f->interrupts);
+ list_init_nreq(&f->notify_list);
+ f->notify_ctr = 1;
+ fuse_mutex_init(&f->lock);
+
+ err = pthread_key_create(&f->pipe_key, fuse_ll_pipe_destructor);
+ if (err) {
+ fprintf(stderr, "fuse: failed to create thread specific key: %s\n",
+ strerror(err));
+ goto out_free;
+ }
+
+ if (fuse_opt_parse(args, f, fuse_ll_opts, fuse_ll_opt_proc) == -1)
+ goto out_key_destroy;
+
+ if (f->debug)
+ fprintf(stderr, "FUSE library version: %s\n", PACKAGE_VERSION);
+
+ memcpy(&f->op, op, op_size);
+ f->owner = getuid();
+ f->userdata = userdata;
+
+ se = fuse_session_new(&sop, f);
+ if (!se)
+ goto out_key_destroy;
+
+ se->receive_buf = fuse_ll_receive_buf;
+ se->process_buf = fuse_ll_process_buf;
+
+ return se;
+
+out_key_destroy:
+ pthread_key_delete(f->pipe_key);
+out_free:
+ pthread_mutex_destroy(&f->lock);
+ free(f);
+out:
+ return NULL;
+}
+
+
+struct fuse_session *fuse_lowlevel_new(struct fuse_args *args,
+ const struct fuse_lowlevel_ops *op,
+ size_t op_size, void *userdata)
+{
+ return fuse_lowlevel_new_common(args, op, op_size, userdata);
+}
+
+#ifdef linux
+int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[])
+{
+ char *buf;
+ size_t bufsize = 1024;
+ char path[128];
+ int ret;
+ int fd;
+ unsigned long pid = req->ctx.pid;
+ char *s;
+
+ sprintf(path, "/proc/%lu/task/%lu/status", pid, pid);
+
+retry:
+ buf = malloc(bufsize);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ ret = -EIO;
+ fd = open(path, O_RDONLY);
+ if (fd == -1)
+ goto out_free;
+
+ ret = read(fd, buf, bufsize);
+ close(fd);
+ if (ret == -1) {
+ ret = -EIO;
+ goto out_free;
+ }
+
+ if (ret == bufsize) {
+ free(buf);
+ bufsize *= 4;
+ goto retry;
+ }
+
+ ret = -EIO;
+ s = strstr(buf, "\nGroups:");
+ if (s == NULL)
+ goto out_free;
+
+ s += 8;
+ ret = 0;
+ while (1) {
+ char *end;
+ unsigned long val = strtoul(s, &end, 0);
+ if (end == s)
+ break;
+
+ s = end;
+ if (ret < size)
+ list[ret] = val;
+ ret++;
+ }
+
+out_free:
+ free(buf);
+ return ret;
+}
+#else /* linux */
+/*
+ * This is currently not implemented on other than Linux...
+ */
+int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[])
+{
+ return -ENOSYS;
+}
+#endif
+
+#if !defined(__FreeBSD__) && !defined(__NetBSD__)
+
+static void fill_open_compat(struct fuse_open_out *arg,
+ const struct fuse_file_info_compat *f)
+{
+ arg->fh = f->fh;
+ if (f->direct_io)
+ arg->open_flags |= FOPEN_DIRECT_IO;
+ if (f->keep_cache)
+ arg->open_flags |= FOPEN_KEEP_CACHE;
+}
+
+static void convert_statfs_compat(const struct statfs *compatbuf,
+ struct statvfs *buf)
+{
+ buf->f_bsize = compatbuf->f_bsize;
+ buf->f_blocks = compatbuf->f_blocks;
+ buf->f_bfree = compatbuf->f_bfree;
+ buf->f_bavail = compatbuf->f_bavail;
+ buf->f_files = compatbuf->f_files;
+ buf->f_ffree = compatbuf->f_ffree;
+ buf->f_namemax = compatbuf->f_namelen;
+}
+
+int fuse_reply_open_compat(fuse_req_t req,
+ const struct fuse_file_info_compat *f)
+{
+ struct fuse_open_out arg;
+
+ memset(&arg, 0, sizeof(arg));
+ fill_open_compat(&arg, f);
+ return send_reply_ok(req, &arg, sizeof(arg));
+}
+
+int fuse_reply_statfs_compat(fuse_req_t req, const struct statfs *stbuf)
+{
+ struct statvfs newbuf;
+
+ memset(&newbuf, 0, sizeof(newbuf));
+ convert_statfs_compat(stbuf, &newbuf);
+
+ return fuse_reply_statfs(req, &newbuf);
+}
+
+struct fuse_session *fuse_lowlevel_new_compat(const char *opts,
+ const struct fuse_lowlevel_ops_compat *op,
+ size_t op_size, void *userdata)
+{
+ struct fuse_session *se;
+ struct fuse_args args = FUSE_ARGS_INIT(0, NULL);
+
+ if (opts &&
+ (fuse_opt_add_arg(&args, "") == -1 ||
+ fuse_opt_add_arg(&args, "-o") == -1 ||
+ fuse_opt_add_arg(&args, opts) == -1)) {
+ fuse_opt_free_args(&args);
+ return NULL;
+ }
+ se = fuse_lowlevel_new(&args, (const struct fuse_lowlevel_ops *) op,
+ op_size, userdata);
+ fuse_opt_free_args(&args);
+
+ return se;
+}
+
+struct fuse_ll_compat_conf {
+ unsigned max_read;
+ int set_max_read;
+};
+
+static const struct fuse_opt fuse_ll_opts_compat[] = {
+ { "max_read=", offsetof(struct fuse_ll_compat_conf, set_max_read), 1 },
+ { "max_read=%u", offsetof(struct fuse_ll_compat_conf, max_read), 0 },
+ FUSE_OPT_KEY("max_read=", FUSE_OPT_KEY_KEEP),
+ FUSE_OPT_END
+};
+
+int fuse_sync_compat_args(struct fuse_args *args)
+{
+ struct fuse_ll_compat_conf conf;
+
+ memset(&conf, 0, sizeof(conf));
+ if (fuse_opt_parse(args, &conf, fuse_ll_opts_compat, NULL) == -1)
+ return -1;
+
+ if (fuse_opt_insert_arg(args, 1, "-osync_read"))
+ return -1;
+
+ if (conf.set_max_read) {
+ char tmpbuf[64];
+
+ sprintf(tmpbuf, "-omax_readahead=%u", conf.max_read);
+ if (fuse_opt_insert_arg(args, 1, tmpbuf) == -1)
+ return -1;
+ }
+ return 0;
+}
+
+FUSE_SYMVER(".symver fuse_reply_statfs_compat,fuse_reply_statfs@FUSE_2.4");
+FUSE_SYMVER(".symver fuse_reply_open_compat,fuse_reply_open@FUSE_2.4");
+FUSE_SYMVER(".symver fuse_lowlevel_new_compat,fuse_lowlevel_new@FUSE_2.4");
+
+#else /* __FreeBSD__ || __NetBSD__ */
+
+int fuse_sync_compat_args(struct fuse_args *args)
+{
+ (void) args;
+ return 0;
+}
+
+#endif /* __FreeBSD__ || __NetBSD__ */
+
+struct fuse_session *fuse_lowlevel_new_compat25(struct fuse_args *args,
+ const struct fuse_lowlevel_ops_compat25 *op,
+ size_t op_size, void *userdata)
+{
+ if (fuse_sync_compat_args(args) == -1)
+ return NULL;
+
+ return fuse_lowlevel_new_common(args,
+ (const struct fuse_lowlevel_ops *) op,
+ op_size, userdata);
+}
+
+FUSE_SYMVER(".symver fuse_lowlevel_new_compat25,fuse_lowlevel_new@FUSE_2.5");
diff --git a/fuse/fuse_misc.h b/fuse/fuse_misc.h
new file mode 100644
index 000000000..eedf0e0f7
--- /dev/null
+++ b/fuse/fuse_misc.h
@@ -0,0 +1,57 @@
+/*
+ FUSE: Filesystem in Userspace
+ Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
+
+ This program can be distributed under the terms of the GNU LGPLv2.
+ See the file COPYING.LIB
+*/
+
+#include "config.h"
+#include <pthread.h>
+
+/*
+ Versioned symbols cannot be used in some cases because it
+ - confuse the dynamic linker in uClibc
+ - not supported on MacOSX (in MachO binary format)
+*/
+#if (!defined(__UCLIBC__) && !defined(__APPLE__))
+#define FUSE_SYMVER(x) __asm__(x)
+#else
+#define FUSE_SYMVER(x)
+#endif
+
+#ifndef USE_UCLIBC
+#define fuse_mutex_init(mut) pthread_mutex_init(mut, NULL)
+#else
+/* Is this hack still needed? */
+static inline void fuse_mutex_init(pthread_mutex_t *mut)
+{
+ pthread_mutexattr_t attr;
+ pthread_mutexattr_init(&attr);
+ pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ADAPTIVE_NP);
+ pthread_mutex_init(mut, &attr);
+ pthread_mutexattr_destroy(&attr);
+}
+#endif
+
+#ifdef HAVE_STRUCT_STAT_ST_ATIM
+/* Linux */
+#define ST_ATIM_NSEC(stbuf) ((stbuf)->st_atim.tv_nsec)
+#define ST_CTIM_NSEC(stbuf) ((stbuf)->st_ctim.tv_nsec)
+#define ST_MTIM_NSEC(stbuf) ((stbuf)->st_mtim.tv_nsec)
+#define ST_ATIM_NSEC_SET(stbuf, val) (stbuf)->st_atim.tv_nsec = (val)
+#define ST_MTIM_NSEC_SET(stbuf, val) (stbuf)->st_mtim.tv_nsec = (val)
+#elif defined(HAVE_STRUCT_STAT_ST_ATIMESPEC)
+/* FreeBSD */
+#define ST_ATIM_NSEC(stbuf) ((stbuf)->st_atimespec.tv_nsec)
+#define ST_CTIM_NSEC(stbuf) ((stbuf)->st_ctimespec.tv_nsec)
+#define ST_MTIM_NSEC(stbuf) ((stbuf)->st_mtimespec.tv_nsec)
+#define ST_ATIM_NSEC_SET(stbuf, val) (stbuf)->st_atimespec.tv_nsec = (val)
+#define ST_MTIM_NSEC_SET(stbuf, val) (stbuf)->st_mtimespec.tv_nsec = (val)
+#else
+#define ST_ATIM_NSEC(stbuf) 0
+#define ST_CTIM_NSEC(stbuf) 0
+#define ST_MTIM_NSEC(stbuf) 0
+#define ST_ATIM_NSEC_SET(stbuf, val) do { } while (0)
+#define ST_MTIM_NSEC_SET(stbuf, val) do { } while (0)
+#endif
diff --git a/fuse/fuse_mt.c b/fuse/fuse_mt.c
new file mode 100644
index 000000000..f6dbe71b2
--- /dev/null
+++ b/fuse/fuse_mt.c
@@ -0,0 +1,122 @@
+/*
+ FUSE: Filesystem in Userspace
+ Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
+
+ This program can be distributed under the terms of the GNU LGPLv2.
+ See the file COPYING.LIB.
+*/
+
+#include "fuse_i.h"
+#include "fuse_misc.h"
+#include "fuse_lowlevel.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <assert.h>
+
+struct procdata {
+ struct fuse *f;
+ struct fuse_chan *prevch;
+ struct fuse_session *prevse;
+ fuse_processor_t proc;
+ void *data;
+};
+
+static void mt_session_proc(void *data, const char *buf, size_t len,
+ struct fuse_chan *ch)
+{
+ struct procdata *pd = (struct procdata *) data;
+ struct fuse_cmd *cmd = *(struct fuse_cmd **) buf;
+
+ (void) len;
+ (void) ch;
+ pd->proc(pd->f, cmd, pd->data);
+}
+
+static void mt_session_exit(void *data, int val)
+{
+ struct procdata *pd = (struct procdata *) data;
+ if (val)
+ fuse_session_exit(pd->prevse);
+ else
+ fuse_session_reset(pd->prevse);
+}
+
+static int mt_session_exited(void *data)
+{
+ struct procdata *pd = (struct procdata *) data;
+ return fuse_session_exited(pd->prevse);
+}
+
+static int mt_chan_receive(struct fuse_chan **chp, char *buf, size_t size)
+{
+ struct fuse_cmd *cmd;
+ struct procdata *pd = (struct procdata *) fuse_chan_data(*chp);
+
+ assert(size >= sizeof(cmd));
+
+ cmd = fuse_read_cmd(pd->f);
+ if (cmd == NULL)
+ return 0;
+
+ *(struct fuse_cmd **) buf = cmd;
+
+ return sizeof(cmd);
+}
+
+int fuse_loop_mt_proc(struct fuse *f, fuse_processor_t proc, void *data)
+{
+ int res;
+ struct procdata pd;
+ struct fuse_session *prevse = fuse_get_session(f);
+ struct fuse_session *se;
+ struct fuse_chan *prevch = fuse_session_next_chan(prevse, NULL);
+ struct fuse_chan *ch;
+ struct fuse_session_ops sop = {
+ .exit = mt_session_exit,
+ .exited = mt_session_exited,
+ .process = mt_session_proc,
+ };
+ struct fuse_chan_ops cop = {
+ .receive = mt_chan_receive,
+ };
+
+ pd.f = f;
+ pd.prevch = prevch;
+ pd.prevse = prevse;
+ pd.proc = proc;
+ pd.data = data;
+
+ se = fuse_session_new(&sop, &pd);
+ if (se == NULL)
+ return -1;
+
+ ch = fuse_chan_new(&cop, fuse_chan_fd(prevch),
+ sizeof(struct fuse_cmd *), &pd);
+ if (ch == NULL) {
+ fuse_session_destroy(se);
+ return -1;
+ }
+ fuse_session_add_chan(se, ch);
+ res = fuse_session_loop_mt(se);
+ fuse_session_destroy(se);
+ return res;
+}
+
+int fuse_loop_mt(struct fuse *f)
+{
+ if (f == NULL)
+ return -1;
+
+ int res = fuse_start_cleanup_thread(f);
+ if (res)
+ return -1;
+
+ res = fuse_session_loop_mt(fuse_get_session(f));
+ fuse_stop_cleanup_thread(f);
+ return res;
+}
+
+FUSE_SYMVER(".symver fuse_loop_mt_proc,__fuse_loop_mt@");
diff --git a/fuse/fuse_opt.c b/fuse/fuse_opt.c
new file mode 100644
index 000000000..a2118cedc
--- /dev/null
+++ b/fuse/fuse_opt.c
@@ -0,0 +1,426 @@
+/*
+ FUSE: Filesystem in Userspace
+ Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
+
+ This program can be distributed under the terms of the GNU LGPLv2.
+ See the file COPYING.LIB
+*/
+
+#include "fuse_opt.h"
+#include "fuse_misc.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+struct fuse_opt_context {
+ void *data;
+ const struct fuse_opt *opt;
+ fuse_opt_proc_t proc;
+ int argctr;
+ int argc;
+ char **argv;
+ struct fuse_args outargs;
+ char *opts;
+ int nonopt;
+};
+
+void fuse_opt_free_args(struct fuse_args *args)
+{
+ if (args) {
+ if (args->argv && args->allocated) {
+ int i;
+ for (i = 0; i < args->argc; i++)
+ free(args->argv[i]);
+ free(args->argv);
+ }
+ args->argc = 0;
+ args->argv = NULL;
+ args->allocated = 0;
+ }
+}
+
+static int alloc_failed(void)
+{
+ fprintf(stderr, "fuse: memory allocation failed\n");
+ return -1;
+}
+
+int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
+{
+ char **newargv;
+ char *newarg;
+
+ assert(!args->argv || args->allocated);
+
+ newarg = strdup(arg);
+ if (!newarg)
+ return alloc_failed();
+
+ newargv = realloc(args->argv, (args->argc + 2) * sizeof(char *));
+ if (!newargv) {
+ free(newarg);
+ return alloc_failed();
+ }
+
+ args->argv = newargv;
+ args->allocated = 1;
+ args->argv[args->argc++] = newarg;
+ args->argv[args->argc] = NULL;
+ return 0;
+}
+
+static int fuse_opt_insert_arg_common(struct fuse_args *args, int pos,
+ const char *arg)
+{
+ assert(pos <= args->argc);
+ if (fuse_opt_add_arg(args, arg) == -1)
+ return -1;
+
+ if (pos != args->argc - 1) {
+ char *newarg = args->argv[args->argc - 1];
+ memmove(&args->argv[pos + 1], &args->argv[pos],
+ sizeof(char *) * (args->argc - pos - 1));
+ args->argv[pos] = newarg;
+ }
+ return 0;
+}
+
+int fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg)
+{
+ return fuse_opt_insert_arg_common(args, pos, arg);
+}
+
+int fuse_opt_insert_arg_compat(struct fuse_args *args, int pos,
+ const char *arg);
+int fuse_opt_insert_arg_compat(struct fuse_args *args, int pos, const char *arg)
+{
+ return fuse_opt_insert_arg_common(args, pos, arg);
+}
+
+static int next_arg(struct fuse_opt_context *ctx, const char *opt)
+{
+ if (ctx->argctr + 1 >= ctx->argc) {
+ fprintf(stderr, "fuse: missing argument after `%s'\n", opt);
+ return -1;
+ }
+ ctx->argctr++;
+ return 0;
+}
+
+static int add_arg(struct fuse_opt_context *ctx, const char *arg)
+{
+ return fuse_opt_add_arg(&ctx->outargs, arg);
+}
+
+static int add_opt_common(char **opts, const char *opt, int esc)
+{
+ unsigned oldlen = *opts ? strlen(*opts) : 0;
+ char *d = realloc(*opts, oldlen + 1 + strlen(opt) * 2 + 1);
+
+ if (!d)
+ return alloc_failed();
+
+ *opts = d;
+ if (oldlen) {
+ d += oldlen;
+ *d++ = ',';
+ }
+
+ for (; *opt; opt++) {
+ if (esc && (*opt == ',' || *opt == '\\'))
+ *d++ = '\\';
+ *d++ = *opt;
+ }
+ *d = '\0';
+
+ return 0;
+}
+
+int fuse_opt_add_opt(char **opts, const char *opt)
+{
+ return add_opt_common(opts, opt, 0);
+}
+
+int fuse_opt_add_opt_escaped(char **opts, const char *opt)
+{
+ return add_opt_common(opts, opt, 1);
+}
+
+static int add_opt(struct fuse_opt_context *ctx, const char *opt)
+{
+ return add_opt_common(&ctx->opts, opt, 1);
+}
+
+static int call_proc(struct fuse_opt_context *ctx, const char *arg, int key,
+ int iso)
+{
+ if (key == FUSE_OPT_KEY_DISCARD)
+ return 0;
+
+ if (key != FUSE_OPT_KEY_KEEP && ctx->proc) {
+ int res = ctx->proc(ctx->data, arg, key, &ctx->outargs);
+ if (res == -1 || !res)
+ return res;
+ }
+ if (iso)
+ return add_opt(ctx, arg);
+ else
+ return add_arg(ctx, arg);
+}
+
+static int match_template(const char *t, const char *arg, unsigned *sepp)
+{
+ int arglen = strlen(arg);
+ const char *sep = strchr(t, '=');
+ sep = sep ? sep : strchr(t, ' ');
+ if (sep && (!sep[1] || sep[1] == '%')) {
+ int tlen = sep - t;
+ if (sep[0] == '=')
+ tlen ++;
+ if (arglen >= tlen && strncmp(arg, t, tlen) == 0) {
+ *sepp = sep - t;
+ return 1;
+ }
+ }
+ if (strcmp(t, arg) == 0) {
+ *sepp = 0;
+ return 1;
+ }
+ return 0;
+}
+
+static const struct fuse_opt *find_opt(const struct fuse_opt *opt,
+ const char *arg, unsigned *sepp)
+{
+ for (; opt && opt->templ; opt++)
+ if (match_template(opt->templ, arg, sepp))
+ return opt;
+ return NULL;
+}
+
+int fuse_opt_match(const struct fuse_opt *opts, const char *opt)
+{
+ unsigned dummy;
+ return find_opt(opts, opt, &dummy) ? 1 : 0;
+}
+
+static int process_opt_param(void *var, const char *format, const char *param,
+ const char *arg)
+{
+ assert(format[0] == '%');
+ if (format[1] == 's') {
+ char *copy = strdup(param);
+ if (!copy)
+ return alloc_failed();
+
+ *(char **) var = copy;
+ } else {
+ if (sscanf(param, format, var) != 1) {
+ fprintf(stderr, "fuse: invalid parameter in option `%s'\n", arg);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int process_opt(struct fuse_opt_context *ctx,
+ const struct fuse_opt *opt, unsigned sep,
+ const char *arg, int iso)
+{
+ if (opt->offset == -1U) {
+ if (call_proc(ctx, arg, opt->value, iso) == -1)
+ return -1;
+ } else {
+ void *var = ctx->data + opt->offset;
+ if (sep && opt->templ[sep + 1]) {
+ const char *param = arg + sep;
+ if (opt->templ[sep] == '=')
+ param ++;
+ if (process_opt_param(var, opt->templ + sep + 1,
+ param, arg) == -1)
+ return -1;
+ } else
+ *(int *)var = opt->value;
+ }
+ return 0;
+}
+
+static int process_opt_sep_arg(struct fuse_opt_context *ctx,
+ const struct fuse_opt *opt, unsigned sep,
+ const char *arg, int iso)
+{
+ int res;
+ char *newarg;
+ char *param;
+
+ if (next_arg(ctx, arg) == -1)
+ return -1;
+
+ param = ctx->argv[ctx->argctr];
+ newarg = malloc(sep + strlen(param) + 1);
+ if (!newarg)
+ return alloc_failed();
+
+ memcpy(newarg, arg, sep);
+ strcpy(newarg + sep, param);
+ res = process_opt(ctx, opt, sep, newarg, iso);
+ free(newarg);
+
+ return res;
+}
+
+static int process_gopt(struct fuse_opt_context *ctx, const char *arg, int iso)
+{
+ unsigned sep;
+ const struct fuse_opt *opt = find_opt(ctx->opt, arg, &sep);
+ if (opt) {
+ for (; opt; opt = find_opt(opt + 1, arg, &sep)) {
+ int res;
+ if (sep && opt->templ[sep] == ' ' && !arg[sep])
+ res = process_opt_sep_arg(ctx, opt, sep, arg,
+ iso);
+ else
+ res = process_opt(ctx, opt, sep, arg, iso);
+ if (res == -1)
+ return -1;
+ }
+ return 0;
+ } else
+ return call_proc(ctx, arg, FUSE_OPT_KEY_OPT, iso);
+}
+
+static int process_real_option_group(struct fuse_opt_context *ctx, char *opts)
+{
+ char *s = opts;
+ char *d = s;
+ int end = 0;
+
+ while (!end) {
+ if (*s == '\0')
+ end = 1;
+ if (*s == ',' || end) {
+ int res;
+
+ *d = '\0';
+ res = process_gopt(ctx, opts, 1);
+ if (res == -1)
+ return -1;
+ d = opts;
+ } else {
+ if (s[0] == '\\' && s[1] != '\0') {
+ s++;
+ if (s[0] >= '0' && s[0] <= '3' &&
+ s[1] >= '0' && s[1] <= '7' &&
+ s[2] >= '0' && s[2] <= '7') {
+ *d++ = (s[0] - '0') * 0100 +
+ (s[1] - '0') * 0010 +
+ (s[2] - '0');
+ s += 2;
+ } else {
+ *d++ = *s;
+ }
+ } else {
+ *d++ = *s;
+ }
+ }
+ s++;
+ }
+
+ return 0;
+}
+
+static int process_option_group(struct fuse_opt_context *ctx, const char *opts)
+{
+ int res;
+ char *copy = strdup(opts);
+
+ if (!copy) {
+ fprintf(stderr, "fuse: memory allocation failed\n");
+ return -1;
+ }
+ res = process_real_option_group(ctx, copy);
+ free(copy);
+ return res;
+}
+
+static int process_one(struct fuse_opt_context *ctx, const char *arg)
+{
+ if (ctx->nonopt || arg[0] != '-')
+ return call_proc(ctx, arg, FUSE_OPT_KEY_NONOPT, 0);
+ else if (arg[1] == 'o') {
+ if (arg[2])
+ return process_option_group(ctx, arg + 2);
+ else {
+ if (next_arg(ctx, arg) == -1)
+ return -1;
+
+ return process_option_group(ctx,
+ ctx->argv[ctx->argctr]);
+ }
+ } else if (arg[1] == '-' && !arg[2]) {
+ if (add_arg(ctx, arg) == -1)
+ return -1;
+ ctx->nonopt = ctx->outargs.argc;
+ return 0;
+ } else
+ return process_gopt(ctx, arg, 0);
+}
+
+static int opt_parse(struct fuse_opt_context *ctx)
+{
+ if (ctx->argc) {
+ if (add_arg(ctx, ctx->argv[0]) == -1)
+ return -1;
+ }
+
+ for (ctx->argctr = 1; ctx->argctr < ctx->argc; ctx->argctr++)
+ if (process_one(ctx, ctx->argv[ctx->argctr]) == -1)
+ return -1;
+
+ if (ctx->opts) {
+ if (fuse_opt_insert_arg(&ctx->outargs, 1, "-o") == -1 ||
+ fuse_opt_insert_arg(&ctx->outargs, 2, ctx->opts) == -1)
+ return -1;
+ }
+
+ /* If option separator ("--") is the last argument, remove it */
+ if (ctx->nonopt && ctx->nonopt == ctx->outargs.argc &&
+ strcmp(ctx->outargs.argv[ctx->outargs.argc - 1], "--") == 0) {
+ free(ctx->outargs.argv[ctx->outargs.argc - 1]);
+ ctx->outargs.argv[--ctx->outargs.argc] = NULL;
+ }
+
+ return 0;
+}
+
+int fuse_opt_parse(struct fuse_args *args, void *data,
+ const struct fuse_opt opts[], fuse_opt_proc_t proc)
+{
+ int res;
+ struct fuse_opt_context ctx = {
+ .data = data,
+ .opt = opts,
+ .proc = proc,
+ };
+
+ if (!args || !args->argv || !args->argc)
+ return 0;
+
+ ctx.argc = args->argc;
+ ctx.argv = args->argv;
+
+ res = opt_parse(&ctx);
+ if (res != -1) {
+ struct fuse_args tmp = *args;
+ *args = ctx.outargs;
+ ctx.outargs = tmp;
+ }
+ free(ctx.opts);
+ fuse_opt_free_args(&ctx.outargs);
+ return res;
+}
+
+/* This symbol version was mistakenly added to the version script */
+FUSE_SYMVER(".symver fuse_opt_insert_arg_compat,fuse_opt_insert_arg@FUSE_2.5");
diff --git a/fuse/fuse_session.c b/fuse/fuse_session.c
new file mode 100644
index 000000000..c55f25074
--- /dev/null
+++ b/fuse/fuse_session.c
@@ -0,0 +1,233 @@
+/*
+ FUSE: Filesystem in Userspace
+ Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
+
+ This program can be distributed under the terms of the GNU LGPLv2.
+ See the file COPYING.LIB
+*/
+
+#include "fuse_i.h"
+#include "fuse_misc.h"
+#include "fuse_common_compat.h"
+#include "fuse_lowlevel_compat.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+
+struct fuse_chan {
+ struct fuse_chan_ops op;
+
+ struct fuse_session *se;
+
+ int fd;
+
+ size_t bufsize;
+
+ void *data;
+
+ int compat;
+};
+
+struct fuse_session *fuse_session_new(struct fuse_session_ops *op, void *data)
+{
+ struct fuse_session *se = (struct fuse_session *) malloc(sizeof(*se));
+ if (se == NULL) {
+ fprintf(stderr, "fuse: failed to allocate session\n");
+ return NULL;
+ }
+
+ memset(se, 0, sizeof(*se));
+ se->op = *op;
+ se->data = data;
+
+ return se;
+}
+
+void fuse_session_add_chan(struct fuse_session *se, struct fuse_chan *ch)
+{
+ assert(se->ch == NULL);
+ assert(ch->se == NULL);
+ se->ch = ch;
+ ch->se = se;
+}
+
+void fuse_session_remove_chan(struct fuse_chan *ch)
+{
+ struct fuse_session *se = ch->se;
+ if (se) {
+ assert(se->ch == ch);
+ se->ch = NULL;
+ ch->se = NULL;
+ }
+}
+
+struct fuse_chan *fuse_session_next_chan(struct fuse_session *se,
+ struct fuse_chan *ch)
+{
+ assert(ch == NULL || ch == se->ch);
+ if (ch == NULL)
+ return se->ch;
+ else
+ return NULL;
+}
+
+void fuse_session_process(struct fuse_session *se, const char *buf, size_t len,
+ struct fuse_chan *ch)
+{
+ se->op.process(se->data, buf, len, ch);
+}
+
+void fuse_session_process_buf(struct fuse_session *se,
+ const struct fuse_buf *buf, struct fuse_chan *ch)
+{
+ if (se->process_buf) {
+ se->process_buf(se->data, buf, ch);
+ } else {
+ assert(!(buf->flags & FUSE_BUF_IS_FD));
+ fuse_session_process(se->data, buf->mem, buf->size, ch);
+ }
+}
+
+int fuse_session_receive_buf(struct fuse_session *se, struct fuse_buf *buf,
+ struct fuse_chan **chp)
+{
+ int res;
+
+ if (se->receive_buf) {
+ res = se->receive_buf(se, buf, chp);
+ } else {
+ res = fuse_chan_recv(chp, buf->mem, buf->size);
+ if (res > 0)
+ buf->size = res;
+ }
+
+ return res;
+}
+
+
+void fuse_session_destroy(struct fuse_session *se)
+{
+ if (se->op.destroy)
+ se->op.destroy(se->data);
+ if (se->ch != NULL)
+ fuse_chan_destroy(se->ch);
+ free(se);
+}
+
+void fuse_session_exit(struct fuse_session *se)
+{
+ if (se->op.exit)
+ se->op.exit(se->data, 1);
+ se->exited = 1;
+}
+
+void fuse_session_reset(struct fuse_session *se)
+{
+ if (se->op.exit)
+ se->op.exit(se->data, 0);
+ se->exited = 0;
+}
+
+int fuse_session_exited(struct fuse_session *se)
+{
+ if (se->op.exited)
+ return se->op.exited(se->data);
+ else
+ return se->exited;
+}
+
+void *fuse_session_data(struct fuse_session *se)
+{
+ return se->data;
+}
+
+static struct fuse_chan *fuse_chan_new_common(struct fuse_chan_ops *op, int fd,
+ size_t bufsize, void *data,
+ int compat)
+{
+ struct fuse_chan *ch = (struct fuse_chan *) malloc(sizeof(*ch));
+ if (ch == NULL) {
+ fprintf(stderr, "fuse: failed to allocate channel\n");
+ return NULL;
+ }
+
+ memset(ch, 0, sizeof(*ch));
+ ch->op = *op;
+ ch->fd = fd;
+ ch->bufsize = bufsize;
+ ch->data = data;
+ ch->compat = compat;
+
+ return ch;
+}
+
+struct fuse_chan *fuse_chan_new(struct fuse_chan_ops *op, int fd,
+ size_t bufsize, void *data)
+{
+ return fuse_chan_new_common(op, fd, bufsize, data, 0);
+}
+
+struct fuse_chan *fuse_chan_new_compat24(struct fuse_chan_ops_compat24 *op,
+ int fd, size_t bufsize, void *data)
+{
+ return fuse_chan_new_common((struct fuse_chan_ops *) op, fd, bufsize,
+ data, 24);
+}
+
+int fuse_chan_fd(struct fuse_chan *ch)
+{
+ return ch->fd;
+}
+
+size_t fuse_chan_bufsize(struct fuse_chan *ch)
+{
+ return ch->bufsize;
+}
+
+void *fuse_chan_data(struct fuse_chan *ch)
+{
+ return ch->data;
+}
+
+struct fuse_session *fuse_chan_session(struct fuse_chan *ch)
+{
+ return ch->se;
+}
+
+int fuse_chan_recv(struct fuse_chan **chp, char *buf, size_t size)
+{
+ struct fuse_chan *ch = *chp;
+ if (ch->compat)
+ return ((struct fuse_chan_ops_compat24 *) &ch->op)
+ ->receive(ch, buf, size);
+ else
+ return ch->op.receive(chp, buf, size);
+}
+
+int fuse_chan_receive(struct fuse_chan *ch, char *buf, size_t size)
+{
+ int res;
+
+ res = fuse_chan_recv(&ch, buf, size);
+ return res >= 0 ? res : (res != -EINTR && res != -EAGAIN) ? -1 : 0;
+}
+
+int fuse_chan_send(struct fuse_chan *ch, const struct iovec iov[], size_t count)
+{
+ return ch->op.send(ch, iov, count);
+}
+
+void fuse_chan_destroy(struct fuse_chan *ch)
+{
+ fuse_session_remove_chan(ch);
+ if (ch->op.destroy)
+ ch->op.destroy(ch);
+ free(ch);
+}
+
+#ifndef __FreeBSD__
+FUSE_SYMVER(".symver fuse_chan_new_compat24,fuse_chan_new@FUSE_2.4");
+#endif
diff --git a/fuse/fuse_signals.c b/fuse/fuse_signals.c
new file mode 100644
index 000000000..88ac39e18
--- /dev/null
+++ b/fuse/fuse_signals.c
@@ -0,0 +1,72 @@
+/*
+ FUSE: Filesystem in Userspace
+ Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
+
+ This program can be distributed under the terms of the GNU LGPLv2.
+ See the file COPYING.LIB
+*/
+
+#include "fuse_lowlevel.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+
+static struct fuse_session *fuse_instance;
+
+static void exit_handler(int sig)
+{
+ (void) sig;
+ if (fuse_instance)
+ fuse_session_exit(fuse_instance);
+}
+
+static int set_one_signal_handler(int sig, void (*handler)(int))
+{
+ struct sigaction sa;
+ struct sigaction old_sa;
+
+ memset(&sa, 0, sizeof(struct sigaction));
+ sa.sa_handler = handler;
+ sigemptyset(&(sa.sa_mask));
+ sa.sa_flags = 0;
+
+ if (sigaction(sig, NULL, &old_sa) == -1) {
+ perror("fuse: cannot get old signal handler");
+ return -1;
+ }
+
+ if (old_sa.sa_handler == SIG_DFL &&
+ sigaction(sig, &sa, NULL) == -1) {
+ perror("fuse: cannot set signal handler");
+ return -1;
+ }
+ return 0;
+}
+
+int fuse_set_signal_handlers(struct fuse_session *se)
+{
+ if (set_one_signal_handler(SIGHUP, exit_handler) == -1 ||
+ set_one_signal_handler(SIGINT, exit_handler) == -1 ||
+ set_one_signal_handler(SIGTERM, exit_handler) == -1 ||
+ set_one_signal_handler(SIGPIPE, SIG_IGN) == -1)
+ return -1;
+
+ fuse_instance = se;
+ return 0;
+}
+
+void fuse_remove_signal_handlers(struct fuse_session *se)
+{
+ if (fuse_instance != se)
+ fprintf(stderr,
+ "fuse: fuse_remove_signal_handlers: unknown session\n");
+ else
+ fuse_instance = NULL;
+
+ set_one_signal_handler(SIGHUP, SIG_DFL);
+ set_one_signal_handler(SIGINT, SIG_DFL);
+ set_one_signal_handler(SIGTERM, SIG_DFL);
+ set_one_signal_handler(SIGPIPE, SIG_DFL);
+}
+
diff --git a/fuse/fusexmp.c b/fuse/fusexmp.c
new file mode 100644
index 000000000..d4edbc9ae
--- /dev/null
+++ b/fuse/fusexmp.c
@@ -0,0 +1,385 @@
+/*
+ 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 `pkg-config fuse --cflags --libs` fusexmp.c -o fusexmp
+*/
+
+#define FUSE_USE_VERSION 26
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef linux
+/* For pread()/pwrite() */
+#define _XOPEN_SOURCE 500
+#endif
+
+#include <fuse.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.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,
+ off64_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, off64_t size)
+{
+ int res;
+
+ res = truncate(path, size);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_utimens(const char *path, const struct timespec ts[2])
+{
+ int res;
+ struct timeval tv[2];
+
+ tv[0].tv_sec = ts[0].tv_sec;
+ tv[0].tv_usec = ts[0].tv_nsec / 1000;
+ tv[1].tv_sec = ts[1].tv_sec;
+ tv[1].tv_usec = ts[1].tv_nsec / 1000;
+
+ res = utimes(path, tv);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+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, off64_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,
+ off64_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_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,
+ .utimens = xmp_utimens,
+ .open = xmp_open,
+ .read = xmp_read,
+ .write = xmp_write,
+ .statfs = xmp_statfs,
+ .release = xmp_release,
+ .fsync = xmp_fsync,
+#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/fuse/helper.c b/fuse/helper.c
new file mode 100644
index 000000000..ace19dd70
--- /dev/null
+++ b/fuse/helper.c
@@ -0,0 +1,478 @@
+/*
+ FUSE: Filesystem in Userspace
+ Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
+
+ This program can be distributed under the terms of the GNU LGPLv2.
+ See the file COPYING.LIB.
+*/
+
+#include "config.h"
+#include "fuse_i.h"
+#include "fuse_misc.h"
+#include "fuse_opt.h"
+#include "fuse_lowlevel.h"
+#include "fuse_common_compat.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <string.h>
+#include <limits.h>
+#include <errno.h>
+#include <sys/param.h>
+
+enum {
+ KEY_HELP,
+ KEY_HELP_NOHEADER,
+ KEY_VERSION,
+};
+
+struct helper_opts {
+ int singlethread;
+ int foreground;
+ int nodefault_subtype;
+ char *mountpoint;
+};
+
+#define FUSE_HELPER_OPT(t, p) { t, offsetof(struct helper_opts, p), 1 }
+
+static const struct fuse_opt fuse_helper_opts[] = {
+ FUSE_HELPER_OPT("-d", foreground),
+ FUSE_HELPER_OPT("debug", foreground),
+ FUSE_HELPER_OPT("-f", foreground),
+ FUSE_HELPER_OPT("-s", singlethread),
+ FUSE_HELPER_OPT("fsname=", nodefault_subtype),
+ FUSE_HELPER_OPT("subtype=", nodefault_subtype),
+
+ FUSE_OPT_KEY("-h", KEY_HELP),
+ FUSE_OPT_KEY("--help", KEY_HELP),
+ FUSE_OPT_KEY("-ho", KEY_HELP_NOHEADER),
+ FUSE_OPT_KEY("-V", KEY_VERSION),
+ FUSE_OPT_KEY("--version", KEY_VERSION),
+ FUSE_OPT_KEY("-d", FUSE_OPT_KEY_KEEP),
+ FUSE_OPT_KEY("debug", FUSE_OPT_KEY_KEEP),
+ FUSE_OPT_KEY("fsname=", FUSE_OPT_KEY_KEEP),
+ FUSE_OPT_KEY("subtype=", FUSE_OPT_KEY_KEEP),
+ FUSE_OPT_END
+};
+
+static void usage(const char *progname)
+{
+ fprintf(stderr,
+ "usage: %s mountpoint [options]\n\n", progname);
+ fprintf(stderr,
+ "general options:\n"
+ " -o opt,[opt...] mount options\n"
+ " -h --help print help\n"
+ " -V --version print version\n"
+ "\n");
+}
+
+static void helper_help(void)
+{
+ fprintf(stderr,
+ "FUSE options:\n"
+ " -d -o debug enable debug output (implies -f)\n"
+ " -f foreground operation\n"
+ " -s disable multi-threaded operation\n"
+ "\n"
+ );
+}
+
+static void helper_version(void)
+{
+ fprintf(stderr, "FUSE library version: %s\n", PACKAGE_VERSION);
+}
+
+static int fuse_helper_opt_proc(void *data, const char *arg, int key,
+ struct fuse_args *outargs)
+{
+ struct helper_opts *hopts = data;
+
+ switch (key) {
+ case KEY_HELP:
+ usage(outargs->argv[0]);
+ /* fall through */
+
+ case KEY_HELP_NOHEADER:
+ helper_help();
+ return fuse_opt_add_arg(outargs, "-h");
+
+ case KEY_VERSION:
+ helper_version();
+ return 1;
+
+ case FUSE_OPT_KEY_NONOPT:
+ if (!hopts->mountpoint) {
+ char mountpoint[PATH_MAX];
+ if (realpath(arg, mountpoint) == NULL) {
+ fprintf(stderr,
+ "fuse: bad mount point `%s': %s\n",
+ arg, strerror(errno));
+ return -1;
+ }
+ return fuse_opt_add_opt(&hopts->mountpoint, mountpoint);
+ } else {
+ fprintf(stderr, "fuse: invalid argument `%s'\n", arg);
+ return -1;
+ }
+
+ default:
+ return 1;
+ }
+}
+
+static int add_default_subtype(const char *progname, struct fuse_args *args)
+{
+ int res;
+ char *subtype_opt;
+ const char *basename = strrchr(progname, '/');
+ if (basename == NULL)
+ basename = progname;
+ else if (basename[1] != '\0')
+ basename++;
+
+ subtype_opt = (char *) malloc(strlen(basename) + 64);
+ if (subtype_opt == NULL) {
+ fprintf(stderr, "fuse: memory allocation failed\n");
+ return -1;
+ }
+ sprintf(subtype_opt, "-osubtype=%s", basename);
+ res = fuse_opt_add_arg(args, subtype_opt);
+ free(subtype_opt);
+ return res;
+}
+
+int fuse_parse_cmdline(struct fuse_args *args, char **mountpoint,
+ int *multithreaded, int *foreground)
+{
+ int res;
+ struct helper_opts hopts;
+
+ memset(&hopts, 0, sizeof(hopts));
+ res = fuse_opt_parse(args, &hopts, fuse_helper_opts,
+ fuse_helper_opt_proc);
+ if (res == -1)
+ return -1;
+
+ if (!hopts.nodefault_subtype) {
+ res = add_default_subtype(args->argv[0], args);
+ if (res == -1)
+ goto err;
+ }
+ if (mountpoint)
+ *mountpoint = hopts.mountpoint;
+ else
+ free(hopts.mountpoint);
+
+ if (multithreaded)
+ *multithreaded = !hopts.singlethread;
+ if (foreground)
+ *foreground = hopts.foreground;
+ return 0;
+
+err:
+ free(hopts.mountpoint);
+ return -1;
+}
+
+int fuse_daemonize(int foreground)
+{
+ if (!foreground) {
+ int nullfd;
+
+ /*
+ * demonize current process by forking it and killing the
+ * parent. This makes current process as a child of 'init'.
+ */
+ switch(fork()) {
+ case -1:
+ perror("fuse_daemonize: fork");
+ return -1;
+ case 0:
+ break;
+ default:
+ _exit(0);
+ }
+
+ if (setsid() == -1) {
+ perror("fuse_daemonize: setsid");
+ return -1;
+ }
+
+ (void) chdir("/");
+
+ nullfd = open("/dev/null", O_RDWR, 0);
+ if (nullfd != -1) {
+ (void) dup2(nullfd, 0);
+ (void) dup2(nullfd, 1);
+ (void) dup2(nullfd, 2);
+ if (nullfd > 2)
+ close(nullfd);
+ }
+ }
+ return 0;
+}
+
+static struct fuse_chan *fuse_mount_common(const char *mountpoint,
+ struct fuse_args *args)
+{
+ struct fuse_chan *ch;
+ int fd;
+
+ /*
+ * 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);
+
+ fd = fuse_mount_compat25(mountpoint, args);
+ if (fd == -1)
+ return NULL;
+
+ ch = fuse_kern_chan_new(fd);
+ if (!ch)
+ fuse_kern_unmount(mountpoint, fd);
+
+ return ch;
+}
+
+struct fuse_chan *fuse_mount(const char *mountpoint, struct fuse_args *args)
+{
+ return fuse_mount_common(mountpoint, args);
+}
+
+static void fuse_unmount_common(const char *mountpoint, struct fuse_chan *ch)
+{
+ int fd = ch ? fuse_chan_fd(ch) : -1;
+ fuse_kern_unmount(mountpoint, fd);
+ if (ch)
+ fuse_chan_destroy(ch);
+}
+
+void fuse_unmount(const char *mountpoint, struct fuse_chan *ch)
+{
+ fuse_unmount_common(mountpoint, ch);
+}
+
+struct fuse *fuse_setup_common(int argc, char *argv[],
+ const struct fuse_operations *op,
+ size_t op_size,
+ char **mountpoint,
+ int *multithreaded,
+ int *fd,
+ void *user_data,
+ int compat)
+{
+ struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
+ struct fuse_chan *ch;
+ struct fuse *fuse;
+ int foreground;
+ int res;
+
+ res = fuse_parse_cmdline(&args, mountpoint, multithreaded, &foreground);
+ if (res == -1)
+ return NULL;
+
+ ch = fuse_mount_common(*mountpoint, &args);
+ if (!ch) {
+ fuse_opt_free_args(&args);
+ goto err_free;
+ }
+
+ fuse = fuse_new_common(ch, &args, op, op_size, user_data, compat);
+ fuse_opt_free_args(&args);
+ if (fuse == NULL)
+ goto err_unmount;
+
+ res = fuse_daemonize(foreground);
+ if (res == -1)
+ goto err_unmount;
+
+ res = fuse_set_signal_handlers(fuse_get_session(fuse));
+ if (res == -1)
+ goto err_unmount;
+
+ if (fd)
+ *fd = fuse_chan_fd(ch);
+
+ return fuse;
+
+err_unmount:
+ fuse_unmount_common(*mountpoint, ch);
+ if (fuse)
+ fuse_destroy(fuse);
+err_free:
+ free(*mountpoint);
+ return NULL;
+}
+
+struct fuse *fuse_setup(int argc, char *argv[],
+ const struct fuse_operations *op, size_t op_size,
+ char **mountpoint, int *multithreaded, void *user_data)
+{
+ return fuse_setup_common(argc, argv, op, op_size, mountpoint,
+ multithreaded, NULL, user_data, 0);
+}
+
+static void fuse_teardown_common(struct fuse *fuse, char *mountpoint)
+{
+ struct fuse_session *se = fuse_get_session(fuse);
+ struct fuse_chan *ch = fuse_session_next_chan(se, NULL);
+ fuse_remove_signal_handlers(se);
+ fuse_unmount_common(mountpoint, ch);
+ fuse_destroy(fuse);
+ free(mountpoint);
+}
+
+void fuse_teardown(struct fuse *fuse, char *mountpoint)
+{
+ fuse_teardown_common(fuse, mountpoint);
+}
+
+static int fuse_main_common(int argc, char *argv[],
+ const struct fuse_operations *op, size_t op_size,
+ void *user_data, int compat)
+{
+ struct fuse *fuse;
+ char *mountpoint;
+ int multithreaded;
+ int res;
+
+ fuse = fuse_setup_common(argc, argv, op, op_size, &mountpoint,
+ &multithreaded, NULL, user_data, compat);
+ if (fuse == NULL)
+ return 1;
+
+ if (multithreaded)
+ res = fuse_loop_mt(fuse);
+ else
+ res = fuse_loop(fuse);
+
+ fuse_teardown_common(fuse, mountpoint);
+ if (res == -1)
+ return 1;
+
+ return 0;
+}
+
+int fuse_main_real(int argc, char *argv[], const struct fuse_operations *op,
+ size_t op_size, void *user_data)
+{
+ return fuse_main_common(argc, argv, op, op_size, user_data, 0);
+}
+
+#undef fuse_main
+int fuse_main(void);
+int fuse_main(void)
+{
+ fprintf(stderr, "fuse_main(): This function does not exist\n");
+ return -1;
+}
+
+int fuse_version(void)
+{
+ return FUSE_VERSION;
+}
+
+#include "fuse_compat.h"
+
+#if !defined(__FreeBSD__) && !defined(__NetBSD__)
+
+struct fuse *fuse_setup_compat22(int argc, char *argv[],
+ const struct fuse_operations_compat22 *op,
+ size_t op_size, char **mountpoint,
+ int *multithreaded, int *fd)
+{
+ return fuse_setup_common(argc, argv, (struct fuse_operations *) op,
+ op_size, mountpoint, multithreaded, fd, NULL,
+ 22);
+}
+
+struct fuse *fuse_setup_compat2(int argc, char *argv[],
+ const struct fuse_operations_compat2 *op,
+ char **mountpoint, int *multithreaded,
+ int *fd)
+{
+ return fuse_setup_common(argc, argv, (struct fuse_operations *) op,
+ sizeof(struct fuse_operations_compat2),
+ mountpoint, multithreaded, fd, NULL, 21);
+}
+
+int fuse_main_real_compat22(int argc, char *argv[],
+ const struct fuse_operations_compat22 *op,
+ size_t op_size)
+{
+ return fuse_main_common(argc, argv, (struct fuse_operations *) op,
+ op_size, NULL, 22);
+}
+
+void fuse_main_compat1(int argc, char *argv[],
+ const struct fuse_operations_compat1 *op)
+{
+ fuse_main_common(argc, argv, (struct fuse_operations *) op,
+ sizeof(struct fuse_operations_compat1), NULL, 11);
+}
+
+int fuse_main_compat2(int argc, char *argv[],
+ const struct fuse_operations_compat2 *op)
+{
+ return fuse_main_common(argc, argv, (struct fuse_operations *) op,
+ sizeof(struct fuse_operations_compat2), NULL,
+ 21);
+}
+
+int fuse_mount_compat1(const char *mountpoint, const char *args[])
+{
+ /* just ignore mount args for now */
+ (void) args;
+ return fuse_mount_compat22(mountpoint, NULL);
+}
+
+FUSE_SYMVER(".symver fuse_setup_compat2,__fuse_setup@");
+FUSE_SYMVER(".symver fuse_setup_compat22,fuse_setup@FUSE_2.2");
+FUSE_SYMVER(".symver fuse_teardown,__fuse_teardown@");
+FUSE_SYMVER(".symver fuse_main_compat2,fuse_main@");
+FUSE_SYMVER(".symver fuse_main_real_compat22,fuse_main_real@FUSE_2.2");
+
+#endif /* __FreeBSD__ || __NetBSD__ */
+
+
+struct fuse *fuse_setup_compat25(int argc, char *argv[],
+ const struct fuse_operations_compat25 *op,
+ size_t op_size, char **mountpoint,
+ int *multithreaded, int *fd)
+{
+ return fuse_setup_common(argc, argv, (struct fuse_operations *) op,
+ op_size, mountpoint, multithreaded, fd, NULL,
+ 25);
+}
+
+int fuse_main_real_compat25(int argc, char *argv[],
+ const struct fuse_operations_compat25 *op,
+ size_t op_size)
+{
+ return fuse_main_common(argc, argv, (struct fuse_operations *) op,
+ op_size, NULL, 25);
+}
+
+void fuse_teardown_compat22(struct fuse *fuse, int fd, char *mountpoint)
+{
+ (void) fd;
+ fuse_teardown_common(fuse, mountpoint);
+}
+
+int fuse_mount_compat25(const char *mountpoint, struct fuse_args *args)
+{
+ return fuse_kern_mount(mountpoint, args);
+}
+
+FUSE_SYMVER(".symver fuse_setup_compat25,fuse_setup@FUSE_2.5");
+FUSE_SYMVER(".symver fuse_teardown_compat22,fuse_teardown@FUSE_2.2");
+FUSE_SYMVER(".symver fuse_main_real_compat25,fuse_main_real@FUSE_2.5");
+FUSE_SYMVER(".symver fuse_mount_compat25,fuse_mount@FUSE_2.5");
diff --git a/fuse/include/Makefile b/fuse/include/Makefile
new file mode 100644
index 000000000..43fd00aa8
--- /dev/null
+++ b/fuse/include/Makefile
@@ -0,0 +1,515 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# include/Makefile. Generated from Makefile.in by configure.
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
+# Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+
+
+
+pkgdatadir = $(datadir)/fuse
+pkgincludedir = $(includedir)/fuse
+pkglibdir = $(libdir)/fuse
+pkglibexecdir = $(libexecdir)/fuse
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = i686-pc-linux-gnu
+host_triplet = i686-pc-linux-gnu
+target_triplet = i686-pc-linux-gnu
+subdir = include
+DIST_COMMON = $(fuseinclude_HEADERS) $(include_HEADERS) \
+ $(noinst_HEADERS) $(srcdir)/Makefile.am $(srcdir)/Makefile.in \
+ $(srcdir)/config.h.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \
+ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
+ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
+ $(top_srcdir)/configure.in
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+SOURCES =
+DIST_SOURCES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__installdirs = "$(DESTDIR)$(fuseincludedir)" \
+ "$(DESTDIR)$(includedir)"
+HEADERS = $(fuseinclude_HEADERS) $(include_HEADERS) $(noinst_HEADERS)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = ${SHELL} /root/fuse-2.8.5/missing --run aclocal-1.11
+AMTAR = ${SHELL} /root/fuse-2.8.5/missing --run tar
+AR = ar
+AUTOCONF = ${SHELL} /root/fuse-2.8.5/missing --run autoconf
+AUTOHEADER = ${SHELL} /root/fuse-2.8.5/missing --run autoheader
+AUTOMAKE = ${SHELL} /root/fuse-2.8.5/missing --run automake-1.11
+AWK = mawk
+CC = gcc
+CCDEPMODE = depmode=gcc3
+CFLAGS = -Wall -W -Wno-sign-compare -Wstrict-prototypes -Wmissing-declarations -Wwrite-strings -g -O2 -fno-strict-aliasing
+CPP = gcc -E
+CPPFLAGS =
+CYGPATH_W = echo
+DEFS = -DHAVE_CONFIG_H
+DEPDIR = .deps
+DSYMUTIL =
+DUMPBIN =
+ECHO_C =
+ECHO_N = -n
+ECHO_T =
+EGREP = /bin/grep -E
+EXEEXT =
+FGREP = /bin/grep -F
+GREP = /bin/grep
+INIT_D_PATH = /etc/init.d
+INSTALL = /usr/bin/install -c
+INSTALL_DATA = ${INSTALL} -m 644
+INSTALL_PROGRAM = ${INSTALL}
+INSTALL_SCRIPT = ${INSTALL}
+INSTALL_STRIP_PROGRAM = $(install_sh) -c -s
+LD = /usr/bin/ld
+LDFLAGS =
+LIBICONV =
+LIBOBJS =
+LIBS =
+LIBTOOL = $(SHELL) $(top_builddir)/libtool
+LIPO =
+LN_S = ln -s
+LTLIBICONV =
+LTLIBOBJS =
+MAKEINFO = ${SHELL} /root/fuse-2.8.5/missing --run makeinfo
+MKDIR_P = /bin/mkdir -p
+MOUNT_FUSE_PATH = /sbin
+NM = /usr/bin/nm -B
+NMEDIT =
+OBJDUMP = objdump
+OBJEXT = o
+OTOOL =
+OTOOL64 =
+PACKAGE = fuse
+PACKAGE_BUGREPORT =
+PACKAGE_NAME = fuse
+PACKAGE_STRING = fuse 2.8.5
+PACKAGE_TARNAME = fuse
+PACKAGE_VERSION = 2.8.5
+PATH_SEPARATOR = :
+RANLIB = ranlib
+SED = /bin/sed
+SET_MAKE =
+SHELL = /bin/bash
+STRIP = strip
+UDEV_RULES_PATH = /etc/udev/rules.d
+VERSION = 2.8.5
+abs_builddir = /root/fuse-2.8.5/include
+abs_srcdir = /root/fuse-2.8.5/include
+abs_top_builddir = /root/fuse-2.8.5
+abs_top_srcdir = /root/fuse-2.8.5
+ac_ct_CC = gcc
+ac_ct_DUMPBIN =
+am__include = include
+am__leading_dot = .
+am__quote =
+am__tar = ${AMTAR} chof - "$$tardir"
+am__untar = ${AMTAR} xf -
+bindir = ${exec_prefix}/bin
+build = i686-pc-linux-gnu
+build_alias =
+build_cpu = i686
+build_os = linux-gnu
+build_vendor = pc
+builddir = .
+datadir = ${datarootdir}
+datarootdir = ${prefix}/share
+docdir = ${datarootdir}/doc/${PACKAGE_TARNAME}
+dvidir = ${docdir}
+exec_prefix = ${prefix}
+host = i686-pc-linux-gnu
+host_alias =
+host_cpu = i686
+host_os = linux-gnu
+host_vendor = pc
+htmldir = ${docdir}
+includedir = ${prefix}/include
+infodir = ${datarootdir}/info
+install_sh = ${SHELL} /root/fuse-2.8.5/install-sh
+libdir = ${exec_prefix}/lib
+libexecdir = ${exec_prefix}/libexec
+libfuse_libs = -pthread -lrt -ldl
+localedir = ${datarootdir}/locale
+localstatedir = ${prefix}/var
+lt_ECHO = echo
+mandir = ${datarootdir}/man
+mkdir_p = /bin/mkdir -p
+oldincludedir = /usr/include
+pdfdir = ${docdir}
+pkgconfigdir = ${libdir}/pkgconfig
+prefix = /usr/local
+program_transform_name = s,x,x,
+psdir = ${docdir}
+sbindir = ${exec_prefix}/sbin
+sharedstatedir = ${prefix}/com
+srcdir = .
+subdirs2 = include lib util example
+sysconfdir = ${prefix}/etc
+target = i686-pc-linux-gnu
+target_alias =
+target_cpu = i686
+target_os = linux-gnu
+target_vendor = pc
+top_build_prefix = ../
+top_builddir = ..
+top_srcdir = ..
+fuseincludedir = $(includedir)/fuse
+fuseinclude_HEADERS = \
+ fuse.h \
+ fuse_compat.h \
+ fuse_common.h \
+ fuse_common_compat.h \
+ fuse_lowlevel.h \
+ fuse_lowlevel_compat.h \
+ fuse_opt.h \
+ cuse_lowlevel.h
+
+include_HEADERS = old/fuse.h ulockmgr.h
+noinst_HEADERS = fuse_kernel.h
+all: config.h
+ $(MAKE) $(AM_MAKEFLAGS) all-am
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu include/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu include/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+config.h: stamp-h1
+ @if test ! -f $@; then \
+ rm -f stamp-h1; \
+ $(MAKE) $(AM_MAKEFLAGS) stamp-h1; \
+ else :; fi
+
+stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status
+ @rm -f stamp-h1
+ cd $(top_builddir) && $(SHELL) ./config.status include/config.h
+$(srcdir)/config.h.in: $(am__configure_deps)
+ ($(am__cd) $(top_srcdir) && $(AUTOHEADER))
+ rm -f stamp-h1
+ touch $@
+
+distclean-hdr:
+ -rm -f config.h stamp-h1
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-fuseincludeHEADERS: $(fuseinclude_HEADERS)
+ @$(NORMAL_INSTALL)
+ test -z "$(fuseincludedir)" || $(MKDIR_P) "$(DESTDIR)$(fuseincludedir)"
+ @list='$(fuseinclude_HEADERS)'; test -n "$(fuseincludedir)" || list=; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(fuseincludedir)'"; \
+ $(INSTALL_HEADER) $$files "$(DESTDIR)$(fuseincludedir)" || exit $$?; \
+ done
+
+uninstall-fuseincludeHEADERS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(fuseinclude_HEADERS)'; test -n "$(fuseincludedir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ test -n "$$files" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(fuseincludedir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(fuseincludedir)" && rm -f $$files
+install-includeHEADERS: $(include_HEADERS)
+ @$(NORMAL_INSTALL)
+ test -z "$(includedir)" || $(MKDIR_P) "$(DESTDIR)$(includedir)"
+ @list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(includedir)'"; \
+ $(INSTALL_HEADER) $$files "$(DESTDIR)$(includedir)" || exit $$?; \
+ done
+
+uninstall-includeHEADERS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ test -n "$$files" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(includedir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(includedir)" && rm -f $$files
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ set x; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) config.h.in $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ list='$(SOURCES) $(HEADERS) config.h.in $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(HEADERS) config.h
+installdirs:
+ for dir in "$(DESTDIR)$(fuseincludedir)" "$(DESTDIR)$(includedir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-am
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic distclean-hdr distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-fuseincludeHEADERS install-includeHEADERS
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-fuseincludeHEADERS uninstall-includeHEADERS
+
+.MAKE: all install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+ clean-libtool ctags distclean distclean-generic distclean-hdr \
+ distclean-libtool distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-data \
+ install-data-am install-dvi install-dvi-am install-exec \
+ install-exec-am install-fuseincludeHEADERS install-html \
+ install-html-am install-includeHEADERS install-info \
+ install-info-am install-man install-pdf install-pdf-am \
+ install-ps install-ps-am install-strip installcheck \
+ installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am tags uninstall \
+ uninstall-am uninstall-fuseincludeHEADERS \
+ uninstall-includeHEADERS
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/fuse/include/Makefile.am b/fuse/include/Makefile.am
new file mode 100644
index 000000000..663e164cc
--- /dev/null
+++ b/fuse/include/Makefile.am
@@ -0,0 +1,17 @@
+## Process this file with automake to produce Makefile.in
+
+fuseincludedir=$(includedir)/fuse
+
+fuseinclude_HEADERS = \
+ fuse.h \
+ fuse_compat.h \
+ fuse_common.h \
+ fuse_common_compat.h \
+ fuse_lowlevel.h \
+ fuse_lowlevel_compat.h \
+ fuse_opt.h \
+ cuse_lowlevel.h
+
+include_HEADERS = old/fuse.h ulockmgr.h
+
+noinst_HEADERS = fuse_kernel.h
diff --git a/fuse/include/Makefile.in b/fuse/include/Makefile.in
new file mode 100644
index 000000000..1ae6d8589
--- /dev/null
+++ b/fuse/include/Makefile.in
@@ -0,0 +1,515 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
+# Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+target_triplet = @target@
+subdir = include
+DIST_COMMON = $(fuseinclude_HEADERS) $(include_HEADERS) \
+ $(noinst_HEADERS) $(srcdir)/Makefile.am $(srcdir)/Makefile.in \
+ $(srcdir)/config.h.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \
+ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
+ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
+ $(top_srcdir)/configure.in
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+SOURCES =
+DIST_SOURCES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__installdirs = "$(DESTDIR)$(fuseincludedir)" \
+ "$(DESTDIR)$(includedir)"
+HEADERS = $(fuseinclude_HEADERS) $(include_HEADERS) $(noinst_HEADERS)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GREP = @GREP@
+INIT_D_PATH = @INIT_D_PATH@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBICONV = @LIBICONV@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBICONV = @LTLIBICONV@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+MOUNT_FUSE_PATH = @MOUNT_FUSE_PATH@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+RANLIB = @RANLIB@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+UDEV_RULES_PATH = @UDEV_RULES_PATH@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+libfuse_libs = @libfuse_libs@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgconfigdir = @pkgconfigdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+subdirs2 = @subdirs2@
+sysconfdir = @sysconfdir@
+target = @target@
+target_alias = @target_alias@
+target_cpu = @target_cpu@
+target_os = @target_os@
+target_vendor = @target_vendor@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+fuseincludedir = $(includedir)/fuse
+fuseinclude_HEADERS = \
+ fuse.h \
+ fuse_compat.h \
+ fuse_common.h \
+ fuse_common_compat.h \
+ fuse_lowlevel.h \
+ fuse_lowlevel_compat.h \
+ fuse_opt.h \
+ cuse_lowlevel.h
+
+include_HEADERS = old/fuse.h ulockmgr.h
+noinst_HEADERS = fuse_kernel.h
+all: config.h
+ $(MAKE) $(AM_MAKEFLAGS) all-am
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu include/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu include/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+config.h: stamp-h1
+ @if test ! -f $@; then \
+ rm -f stamp-h1; \
+ $(MAKE) $(AM_MAKEFLAGS) stamp-h1; \
+ else :; fi
+
+stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status
+ @rm -f stamp-h1
+ cd $(top_builddir) && $(SHELL) ./config.status include/config.h
+$(srcdir)/config.h.in: $(am__configure_deps)
+ ($(am__cd) $(top_srcdir) && $(AUTOHEADER))
+ rm -f stamp-h1
+ touch $@
+
+distclean-hdr:
+ -rm -f config.h stamp-h1
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-fuseincludeHEADERS: $(fuseinclude_HEADERS)
+ @$(NORMAL_INSTALL)
+ test -z "$(fuseincludedir)" || $(MKDIR_P) "$(DESTDIR)$(fuseincludedir)"
+ @list='$(fuseinclude_HEADERS)'; test -n "$(fuseincludedir)" || list=; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(fuseincludedir)'"; \
+ $(INSTALL_HEADER) $$files "$(DESTDIR)$(fuseincludedir)" || exit $$?; \
+ done
+
+uninstall-fuseincludeHEADERS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(fuseinclude_HEADERS)'; test -n "$(fuseincludedir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ test -n "$$files" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(fuseincludedir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(fuseincludedir)" && rm -f $$files
+install-includeHEADERS: $(include_HEADERS)
+ @$(NORMAL_INSTALL)
+ test -z "$(includedir)" || $(MKDIR_P) "$(DESTDIR)$(includedir)"
+ @list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(includedir)'"; \
+ $(INSTALL_HEADER) $$files "$(DESTDIR)$(includedir)" || exit $$?; \
+ done
+
+uninstall-includeHEADERS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ test -n "$$files" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(includedir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(includedir)" && rm -f $$files
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ set x; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) config.h.in $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ list='$(SOURCES) $(HEADERS) config.h.in $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(HEADERS) config.h
+installdirs:
+ for dir in "$(DESTDIR)$(fuseincludedir)" "$(DESTDIR)$(includedir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-am
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic distclean-hdr distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-fuseincludeHEADERS install-includeHEADERS
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-fuseincludeHEADERS uninstall-includeHEADERS
+
+.MAKE: all install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+ clean-libtool ctags distclean distclean-generic distclean-hdr \
+ distclean-libtool distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-data \
+ install-data-am install-dvi install-dvi-am install-exec \
+ install-exec-am install-fuseincludeHEADERS install-html \
+ install-html-am install-includeHEADERS install-info \
+ install-info-am install-man install-pdf install-pdf-am \
+ install-ps install-ps-am install-strip installcheck \
+ installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am tags uninstall \
+ uninstall-am uninstall-fuseincludeHEADERS \
+ uninstall-includeHEADERS
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/fuse/include/config.h b/fuse/include/config.h
new file mode 100644
index 000000000..a0c603ab6
--- /dev/null
+++ b/fuse/include/config.h
@@ -0,0 +1,87 @@
+/* include/config.h. Generated from config.h.in by configure. */
+/* include/config.h.in. Generated from configure.in by autoheader. */
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#define HAVE_DLFCN_H 1
+
+/* Define to 1 if you have the `fdatasync' function. */
+#define HAVE_FDATASYNC 1
+
+/* Define to 1 if you have the `fork' function. */
+#define HAVE_FORK 1
+
+/* Define if you have the iconv() function and it works. */
+#define HAVE_ICONV 1
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define to 1 if you have the `setxattr' function. */
+#define HAVE_SETXATTR 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if `st_atim' is member of `struct stat'. */
+//#define HAVE_STRUCT_STAT_ST_ATIM 0
+
+/* Define to 1 if `st_atimespec' is member of `struct stat'. */
+/* #undef HAVE_STRUCT_STAT_ST_ATIMESPEC */
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define as const if the declaration of iconv() needs const. */
+#define ICONV_CONST
+
+/* Don't update /etc/mtab */
+/* #undef IGNORE_MTAB */
+
+/* Define to the sub-directory in which libtool stores uninstalled libraries.
+ */
+#define LT_OBJDIR ".libs/"
+
+/* Define to 1 if your C compiler doesn't accept -c and -o together. */
+/* #undef NO_MINUS_C_MINUS_O */
+
+/* Name of package */
+#define PACKAGE "fuse"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT ""
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "fuse"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "fuse 2.8.5"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "fuse"
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "2.8.5"
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Version number of package */
+#define VERSION "2.8.5"
diff --git a/fuse/include/config.h.in b/fuse/include/config.h.in
new file mode 100644
index 000000000..3e7c14ccb
--- /dev/null
+++ b/fuse/include/config.h.in
@@ -0,0 +1,86 @@
+/* include/config.h.in. Generated from configure.in by autoheader. */
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#undef HAVE_DLFCN_H
+
+/* Define to 1 if you have the `fdatasync' function. */
+#undef HAVE_FDATASYNC
+
+/* Define to 1 if you have the `fork' function. */
+#undef HAVE_FORK
+
+/* Define if you have the iconv() function and it works. */
+#undef HAVE_ICONV
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#undef HAVE_INTTYPES_H
+
+/* Define to 1 if you have the <memory.h> header file. */
+#undef HAVE_MEMORY_H
+
+/* Define to 1 if you have the `setxattr' function. */
+#undef HAVE_SETXATTR
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#undef HAVE_STDINT_H
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#undef HAVE_STDLIB_H
+
+/* Define to 1 if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define to 1 if you have the <string.h> header file. */
+#undef HAVE_STRING_H
+
+/* Define to 1 if `st_atim' is member of `struct stat'. */
+#undef HAVE_STRUCT_STAT_ST_ATIM
+
+/* Define to 1 if `st_atimespec' is member of `struct stat'. */
+#undef HAVE_STRUCT_STAT_ST_ATIMESPEC
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#undef HAVE_SYS_STAT_H
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#undef HAVE_SYS_TYPES_H
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* Define as const if the declaration of iconv() needs const. */
+#undef ICONV_CONST
+
+/* Don't update /etc/mtab */
+#undef IGNORE_MTAB
+
+/* Define to the sub-directory in which libtool stores uninstalled libraries.
+ */
+#undef LT_OBJDIR
+
+/* Define to 1 if your C compiler doesn't accept -c and -o together. */
+#undef NO_MINUS_C_MINUS_O
+
+/* Name of package */
+#undef PACKAGE
+
+/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#undef PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
+
+/* Define to 1 if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
+/* Version number of package */
+#undef VERSION
diff --git a/fuse/include/cuse_lowlevel.h b/fuse/include/cuse_lowlevel.h
new file mode 100644
index 000000000..d628eac4e
--- /dev/null
+++ b/fuse/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 <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, off64_t off,
+ struct fuse_file_info *fi);
+ void (*write) (fuse_req_t req, const char *buf, size_t size, off64_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/fuse/include/fuse.h b/fuse/include/fuse.h
new file mode 100644
index 000000000..cad816cc4
--- /dev/null
+++ b/fuse/include/fuse.h
@@ -0,0 +1,1059 @@
+/*
+ FUSE: Filesystem in Userspace
+ Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
+
+ This program can be distributed under the terms of the GNU LGPLv2.
+ See the file COPYING.LIB.
+*/
+
+#ifndef _FUSE_H_
+#define _FUSE_H_
+
+/** @file
+ *
+ * This file defines the library interface of FUSE
+ *
+ * IMPORTANT: you should define FUSE_USE_VERSION before including this
+ * header. To use the newest API define it to 26 (recommended for any
+ * new application), to use the old API define it to 21 (default) 22
+ * or 25, to use the even older 1.X API define it to 11.
+ */
+
+#ifndef FUSE_USE_VERSION
+#define FUSE_USE_VERSION 21
+#endif
+
+#include "fuse_common.h"
+
+#include <fcntl.h>
+#include <time.h>
+#include <utime.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <sys/uio.h>
+#include <pthread.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* ----------------------------------------------------------- *
+ * Basic FUSE API *
+ * ----------------------------------------------------------- */
+
+/** Handle for a FUSE filesystem */
+struct fuse;
+
+/** Structure containing a raw command */
+struct fuse_cmd;
+
+/** Function to add an entry in a readdir() operation
+ *
+ * @param buf the buffer passed to the readdir() operation
+ * @param name the file name of the directory entry
+ * @param stat file attributes, can be NULL
+ * @param off offset of the next entry or zero
+ * @return 1 if buffer is full, zero otherwise
+ */
+typedef int (*fuse_fill_dir_t) (void *buf, const char *name,
+ const struct stat *stbuf, off64_t off);
+
+/* Used by deprecated getdir() method */
+typedef struct fuse_dirhandle *fuse_dirh_t;
+typedef int (*fuse_dirfil_t) (fuse_dirh_t h, const char *name, int type,
+ ino_t ino);
+
+/**
+ * The file system operations:
+ *
+ * Most of these should work very similarly to the well known UNIX
+ * file system operations. A major exception is that instead of
+ * returning an error in 'errno', the operation should return the
+ * negated error value (-errno) directly.
+ *
+ * All methods are optional, but some are essential for a useful
+ * filesystem (e.g. getattr). Open, flush, release, fsync, opendir,
+ * releasedir, fsyncdir, access, create, ftruncate, fgetattr, lock,
+ * init and destroy are special purpose methods, without which a full
+ * featured filesystem can still be implemented.
+ *
+ * Almost all operations take a path which can be of any length.
+ *
+ * Changed in fuse 2.8.0 (regardless of API version)
+ * Previously, paths were limited to a length of PATH_MAX.
+ *
+ * See http://fuse.sourceforge.net/wiki/ for more information. There
+ * is also a snapshot of the relevant wiki pages in the doc/ folder.
+ */
+struct fuse_operations {
+ /** Get file attributes.
+ *
+ * Similar to stat(). The 'st_dev' and 'st_blksize' fields are
+ * ignored. The 'st_ino' field is ignored except if the 'use_ino'
+ * mount option is given.
+ */
+ int (*getattr) (const char *, struct stat *);
+
+ /** Read the target of a symbolic link
+ *
+ * The buffer should be filled with a null terminated string. The
+ * buffer size argument includes the space for the terminating
+ * null character. If the linkname is too long to fit in the
+ * buffer, it should be truncated. The return value should be 0
+ * for success.
+ */
+ int (*readlink) (const char *, char *, size_t);
+
+ /* Deprecated, use readdir() instead */
+ int (*getdir) (const char *, fuse_dirh_t, fuse_dirfil_t);
+
+ /** Create a file node
+ *
+ * This is called for creation of all non-directory, non-symlink
+ * nodes. If the filesystem defines a create() method, then for
+ * regular files that will be called instead.
+ */
+ int (*mknod) (const char *, mode_t, dev_t);
+
+ /** Create a directory
+ *
+ * Note that the mode argument may not have the type specification
+ * bits set, i.e. S_ISDIR(mode) can be false. To obtain the
+ * correct directory type bits use mode|S_IFDIR
+ * */
+ int (*mkdir) (const char *, mode_t);
+
+ /** Remove a file */
+ int (*unlink) (const char *);
+
+ /** Remove a directory */
+ int (*rmdir) (const char *);
+
+ /** Create a symbolic link */
+ int (*symlink) (const char *, const char *);
+
+ /** Rename a file */
+ int (*rename) (const char *, const char *);
+
+ /** Create a hard link to a file */
+ int (*link) (const char *, const char *);
+
+ /** Change the permission bits of a file */
+ int (*chmod) (const char *, mode_t);
+
+ /** Change the owner and group of a file */
+ int (*chown) (const char *, uid_t, gid_t);
+
+ /** Change the size of a file */
+ int (*truncate) (const char *, off64_t);
+
+ /** Change the access and/or modification times of a file
+ *
+ * Deprecated, use utimens() instead.
+ */
+ int (*utime) (const char *, struct utimbuf *);
+
+ /** File open operation
+ *
+ * No creation (O_CREAT, O_EXCL) and by default also no
+ * truncation (O_TRUNC) flags will be passed to open(). If an
+ * application specifies O_TRUNC, fuse first calls truncate()
+ * and then open(). Only if 'atomic_o_trunc' has been
+ * specified and kernel version is 2.6.24 or later, O_TRUNC is
+ * passed on to open.
+ *
+ * Unless the 'default_permissions' mount option is given,
+ * open should check if the operation is permitted for the
+ * given flags. Optionally open may also return an arbitrary
+ * filehandle in the fuse_file_info structure, which will be
+ * passed to all file operations.
+ *
+ * Changed in version 2.2
+ */
+ int (*open) (const char *, struct fuse_file_info *);
+
+ /** Read data from an open file
+ *
+ * Read should return exactly the number of bytes requested except
+ * on EOF or error, otherwise the rest of the data will be
+ * substituted with zeroes. An exception to this is when the
+ * 'direct_io' mount option is specified, in which case the return
+ * value of the read system call will reflect the return value of
+ * this operation.
+ *
+ * Changed in version 2.2
+ */
+ int (*read) (const char *, char *, size_t, off64_t,
+ struct fuse_file_info *);
+
+ /** Write data to an open file
+ *
+ * Write should return exactly the number of bytes requested
+ * except on error. An exception to this is when the 'direct_io'
+ * mount option is specified (see read operation).
+ *
+ * Changed in version 2.2
+ */
+ int (*write) (const char *, const char *, size_t, off64_t,
+ struct fuse_file_info *);
+
+ /** Get file system statistics
+ *
+ * The 'f_frsize', 'f_favail', 'f_fsid' and 'f_flag' fields are ignored
+ *
+ * Replaced 'struct statfs' parameter with 'struct statvfs' in
+ * version 2.5
+ */
+ int (*statfs) (const char *, struct statvfs *);
+
+ /** Possibly flush cached data
+ *
+ * BIG NOTE: This is not equivalent to fsync(). It's not a
+ * request to sync dirty data.
+ *
+ * Flush is called on each close() of a file descriptor. So if a
+ * filesystem wants to return write errors in close() and the file
+ * has cached dirty data, this is a good place to write back data
+ * and return any errors. Since many applications ignore close()
+ * errors this is not always useful.
+ *
+ * NOTE: The flush() method may be called more than once for each
+ * open(). This happens if more than one file descriptor refers
+ * to an opened file due to dup(), dup2() or fork() calls. It is
+ * not possible to determine if a flush is final, so each flush
+ * should be treated equally. Multiple write-flush sequences are
+ * relatively rare, so this shouldn't be a problem.
+ *
+ * Filesystems shouldn't assume that flush will always be called
+ * after some writes, or that if will be called at all.
+ *
+ * Changed in version 2.2
+ */
+ int (*flush) (const char *, struct fuse_file_info *);
+
+ /** Release an open file
+ *
+ * Release is called when there are no more references to an open
+ * file: all file descriptors are closed and all memory mappings
+ * are unmapped.
+ *
+ * For every open() call there will be exactly one release() call
+ * with the same flags and file descriptor. It is possible to
+ * have a file opened more than once, in which case only the last
+ * release will mean, that no more reads/writes will happen on the
+ * file. The return value of release is ignored.
+ *
+ * Changed in version 2.2
+ */
+ int (*release) (const char *, struct fuse_file_info *);
+
+ /** Synchronize file contents
+ *
+ * If the datasync parameter is non-zero, then only the user data
+ * should be flushed, not the meta data.
+ *
+ * Changed in version 2.2
+ */
+ int (*fsync) (const char *, int, struct fuse_file_info *);
+
+ /** Set extended attributes */
+ int (*setxattr) (const char *, const char *, const char *, size_t, int);
+
+ /** Get extended attributes */
+ int (*getxattr) (const char *, const char *, char *, size_t);
+
+ /** List extended attributes */
+ int (*listxattr) (const char *, char *, size_t);
+
+ /** Remove extended attributes */
+ int (*removexattr) (const char *, const char *);
+
+ /** Open directory
+ *
+ * Unless the 'default_permissions' mount option is given,
+ * this method should check if opendir is permitted for this
+ * directory. Optionally opendir may also return an arbitrary
+ * filehandle in the fuse_file_info structure, which will be
+ * passed to readdir, closedir and fsyncdir.
+ *
+ * Introduced in version 2.3
+ */
+ int (*opendir) (const char *, struct fuse_file_info *);
+
+ /** Read directory
+ *
+ * This supersedes the old getdir() interface. New applications
+ * should use this.
+ *
+ * The filesystem may choose between two modes of operation:
+ *
+ * 1) The readdir implementation ignores the offset parameter, and
+ * passes zero to the filler function's offset. The filler
+ * function will not return '1' (unless an error happens), so the
+ * whole directory is read in a single readdir operation. This
+ * works just like the old getdir() method.
+ *
+ * 2) The readdir implementation keeps track of the offsets of the
+ * directory entries. It uses the offset parameter and always
+ * passes non-zero offset to the filler function. When the buffer
+ * is full (or an error happens) the filler function will return
+ * '1'.
+ *
+ * Introduced in version 2.3
+ */
+ int (*readdir) (const char *, void *, fuse_fill_dir_t, off64_t,
+ struct fuse_file_info *);
+
+ /** Release directory
+ *
+ * Introduced in version 2.3
+ */
+ int (*releasedir) (const char *, struct fuse_file_info *);
+
+ /** Synchronize directory contents
+ *
+ * If the datasync parameter is non-zero, then only the user data
+ * should be flushed, not the meta data
+ *
+ * Introduced in version 2.3
+ */
+ int (*fsyncdir) (const char *, int, struct fuse_file_info *);
+
+ /**
+ * Initialize filesystem
+ *
+ * The return value will passed in the private_data field of
+ * fuse_context to all file operations and as a parameter to the
+ * destroy() method.
+ *
+ * Introduced in version 2.3
+ * Changed in version 2.6
+ */
+ void *(*init) (struct fuse_conn_info *conn);
+
+ /**
+ * Clean up filesystem
+ *
+ * Called on filesystem exit.
+ *
+ * Introduced in version 2.3
+ */
+ void (*destroy) (void *);
+
+ /**
+ * Check file access permissions
+ *
+ * This will be called for the access() system call. If the
+ * 'default_permissions' mount option is given, this method is not
+ * called.
+ *
+ * This method is not called under Linux kernel versions 2.4.x
+ *
+ * Introduced in version 2.5
+ */
+ int (*access) (const char *, int);
+
+ /**
+ * Create and open a file
+ *
+ * If the file does not exist, first create it with the specified
+ * mode, and then open it.
+ *
+ * If this method is not implemented or under Linux kernel
+ * versions earlier than 2.6.15, the mknod() and open() methods
+ * will be called instead.
+ *
+ * Introduced in version 2.5
+ */
+ int (*create) (const char *, mode_t, struct fuse_file_info *);
+
+ /**
+ * Change the size of an open file
+ *
+ * This method is called instead of the truncate() method if the
+ * truncation was invoked from an ftruncate() system call.
+ *
+ * If this method is not implemented or under Linux kernel
+ * versions earlier than 2.6.15, the truncate() method will be
+ * called instead.
+ *
+ * Introduced in version 2.5
+ */
+ int (*ftruncate) (const char *, off64_t, struct fuse_file_info *);
+
+ /**
+ * Get attributes from an open file
+ *
+ * This method is called instead of the getattr() method if the
+ * file information is available.
+ *
+ * Currently this is only called after the create() method if that
+ * is implemented (see above). Later it may be called for
+ * invocations of fstat() too.
+ *
+ * Introduced in version 2.5
+ */
+ int (*fgetattr) (const char *, struct stat *, struct fuse_file_info *);
+
+ /**
+ * Perform POSIX file locking operation
+ *
+ * The cmd argument will be either F_GETLK, F_SETLK or F_SETLKW.
+ *
+ * For the meaning of fields in 'struct flock' see the man page
+ * for fcntl(2). The l_whence field will always be set to
+ * SEEK_SET.
+ *
+ * For checking lock ownership, the 'fuse_file_info->owner'
+ * argument must be used.
+ *
+ * For F_GETLK operation, the library will first check currently
+ * held locks, and if a conflicting lock is found it will return
+ * information without calling this method. This ensures, that
+ * for local locks the l_pid field is correctly filled in. The
+ * results may not be accurate in case of race conditions and in
+ * the presence of hard links, but it's unlikely that an
+ * application would rely on accurate GETLK results in these
+ * cases. If a conflicting lock is not found, this method will be
+ * called, and the filesystem may fill out l_pid by a meaningful
+ * value, or it may leave this field zero.
+ *
+ * For F_SETLK and F_SETLKW the l_pid field will be set to the pid
+ * of the process performing the locking operation.
+ *
+ * Note: if this method is not implemented, the kernel will still
+ * allow file locking to work locally. Hence it is only
+ * interesting for network filesystems and similar.
+ *
+ * Introduced in version 2.6
+ */
+ int (*lock) (const char *, struct fuse_file_info *, int cmd,
+ struct flock *);
+
+ /**
+ * Change the access and modification times of a file with
+ * nanosecond resolution
+ *
+ * This supersedes the old utime() interface. New applications
+ * should use this.
+ *
+ * See the utimensat(2) man page for details.
+ *
+ * Introduced in version 2.6
+ */
+ int (*utimens) (const char *, const struct timespec tv[2]);
+
+ /**
+ * Map block index within file to block index within device
+ *
+ * Note: This makes sense only for block device backed filesystems
+ * mounted with the 'blkdev' option
+ *
+ * Introduced in version 2.6
+ */
+ int (*bmap) (const char *, size_t blocksize, uint64_t *idx);
+
+ /**
+ * Flag indicating that the filesystem can accept a NULL path
+ * as the first argument for the following operations:
+ *
+ * read, write, flush, release, fsync, readdir, releasedir,
+ * fsyncdir, ftruncate, fgetattr, lock, ioctl and poll
+ *
+ * If this flag is set these operations continue to work on
+ * unlinked files even if "-ohard_remove" option was specified.
+ */
+ unsigned int flag_nullpath_ok:1;
+
+ /**
+ * Flag indicating that the path need not be calculated for
+ * the following operations:
+ *
+ * read, write, flush, release, fsync, readdir, releasedir,
+ * fsyncdir, ftruncate, fgetattr, lock, ioctl and poll
+ *
+ * Closely related to flag_nullpath_ok, but if this flag is
+ * set then the path will not be calculaged even if the file
+ * wasn't unlinked. However the path can still be non-NULL if
+ * it needs to be calculated for some other reason.
+ */
+ unsigned int flag_nopath:1;
+
+ /**
+ * Flag indicating that the filesystem accepts special
+ * UTIME_NOW and UTIME_OMIT values in its utimens operation.
+ */
+ unsigned int flag_utime_omit_ok:1;
+
+ /**
+ * Reserved flags, don't set
+ */
+ unsigned int flag_reserved:29;
+
+ /**
+ * Ioctl
+ *
+ * flags will have FUSE_IOCTL_COMPAT set for 32bit ioctls in
+ * 64bit environment. The size and direction of data is
+ * determined by _IOC_*() decoding of cmd. For _IOC_NONE,
+ * data will be NULL, for _IOC_WRITE data is out area, for
+ * _IOC_READ in area and if both are set in/out area. In all
+ * non-NULL cases, the area is of _IOC_SIZE(cmd) bytes.
+ *
+ * Introduced in version 2.8
+ */
+ int (*ioctl) (const char *, int cmd, void *arg,
+ struct fuse_file_info *, unsigned int flags, void *data);
+
+ /**
+ * Poll for IO readiness events
+ *
+ * Note: If ph is non-NULL, the client should notify
+ * when IO readiness events occur by calling
+ * fuse_notify_poll() with the specified ph.
+ *
+ * Regardless of the number of times poll with a non-NULL ph
+ * is received, single notification is enough to clear all.
+ * Notifying more times incurs overhead but doesn't harm
+ * correctness.
+ *
+ * The callee is responsible for destroying ph with
+ * fuse_pollhandle_destroy() when no longer in use.
+ *
+ * Introduced in version 2.8
+ */
+ int (*poll) (const char *, struct fuse_file_info *,
+ struct fuse_pollhandle *ph, unsigned *reventsp);
+
+ /** Write contents of buffer to an open file
+ *
+ * Similar to the write() method, but data is supplied in a
+ * generic buffer. Use fuse_buf_copy() to transfer data to
+ * the destination.
+ *
+ * Introduced in version 2.9
+ */
+ int (*write_buf) (const char *, struct fuse_bufvec *buf, off64_t off,
+ struct fuse_file_info *);
+
+ /** Store data from an open file in a buffer
+ *
+ * Similar to the read() method, but data is stored and
+ * returned in a generic buffer.
+ *
+ * No actual copying of data has to take place, the source
+ * file descriptor may simply be stored in the buffer for
+ * later data transfer.
+ *
+ * The buffer must be allocated dynamically and stored at the
+ * location pointed to by bufp. If the buffer contains memory
+ * regions, they too must be allocated using malloc(). The
+ * allocated memory will be freed by the caller.
+ *
+ * Introduced in version 2.9
+ */
+ int (*read_buf) (const char *, struct fuse_bufvec **bufp,
+ size_t size, off64_t off, struct fuse_file_info *);
+ /**
+ * Perform BSD file locking operation
+ *
+ * The op argument will be either LOCK_SH, LOCK_EX or LOCK_UN
+ *
+ * Nonblocking requests will be indicated by ORing LOCK_NB to
+ * the above operations
+ *
+ * For more information see the flock(2) manual page.
+ *
+ * Additionally fi->owner will be set to a value unique to
+ * this open file. This same value will be supplied to
+ * ->release() when the file is released.
+ *
+ * Note: if this method is not implemented, the kernel will still
+ * allow file locking to work locally. Hence it is only
+ * interesting for network filesystems and similar.
+ *
+ * Introduced in version 2.9
+ */
+ int (*flock) (const char *, struct fuse_file_info *, int op);
+
+ /**
+ * Allocates space for an open file
+ *
+ * This function ensures that required space is allocated for specified
+ * file. If this function returns success then any subsequent write
+ * request to specified range is guaranteed not to fail because of lack
+ * of space on the file system media.
+ *
+ * Introduced in version 2.9.1
+ */
+ int (*fallocate) (const char *, int, off64_t, off64_t,
+ struct fuse_file_info *);
+};
+
+/** Extra context that may be needed by some filesystems
+ *
+ * The uid, gid and pid fields are not filled in case of a writepage
+ * operation.
+ */
+struct fuse_context {
+ /** Pointer to the fuse object */
+ struct fuse *fuse;
+
+ /** User ID of the calling process */
+ uid_t uid;
+
+ /** Group ID of the calling process */
+ gid_t gid;
+
+ /** Thread ID of the calling process */
+ pid_t pid;
+
+ /** Private filesystem data */
+ void *private_data;
+
+ /** Umask of the calling process (introduced in version 2.8) */
+ mode_t umask;
+};
+
+/**
+ * Main function of FUSE.
+ *
+ * This is for the lazy. This is all that has to be called from the
+ * main() function.
+ *
+ * This function does the following:
+ * - parses command line options (-d -s and -h)
+ * - passes relevant mount options to the fuse_mount()
+ * - installs signal handlers for INT, HUP, TERM and PIPE
+ * - registers an exit handler to unmount the filesystem on program exit
+ * - creates a fuse handle
+ * - registers the operations
+ * - calls either the single-threaded or the multi-threaded event loop
+ *
+ * Note: this is currently implemented as a macro.
+ *
+ * @param argc the argument counter passed to the main() function
+ * @param argv the argument vector passed to the main() function
+ * @param op the file system operation
+ * @param user_data user data supplied in the context during the init() method
+ * @return 0 on success, nonzero on failure
+ */
+/*
+ int fuse_main(int argc, char *argv[], const struct fuse_operations *op,
+ void *user_data);
+*/
+#define fuse_main(argc, argv, op, user_data) \
+ fuse_main_real(argc, argv, op, sizeof(*(op)), user_data)
+
+/* ----------------------------------------------------------- *
+ * More detailed API *
+ * ----------------------------------------------------------- */
+
+/**
+ * Create a new FUSE filesystem.
+ *
+ * @param ch the communication channel
+ * @param args argument vector
+ * @param op the filesystem operations
+ * @param op_size the size of the fuse_operations structure
+ * @param user_data user data supplied in the context during the init() method
+ * @return the created FUSE handle
+ */
+struct fuse *fuse_new(struct fuse_chan *ch, struct fuse_args *args,
+ const struct fuse_operations *op, size_t op_size,
+ void *user_data);
+
+/**
+ * Destroy the FUSE handle.
+ *
+ * The communication channel attached to the handle is also destroyed.
+ *
+ * NOTE: This function does not unmount the filesystem. If this is
+ * needed, call fuse_unmount() before calling this function.
+ *
+ * @param f the FUSE handle
+ */
+void fuse_destroy(struct fuse *f);
+
+/**
+ * FUSE event loop.
+ *
+ * Requests from the kernel are processed, and the appropriate
+ * operations are called.
+ *
+ * @param f the FUSE handle
+ * @return 0 if no error occurred, -1 otherwise
+ */
+int fuse_loop(struct fuse *f);
+
+/**
+ * Exit from event loop
+ *
+ * @param f the FUSE handle
+ */
+void fuse_exit(struct fuse *f);
+
+/**
+ * FUSE event loop with multiple threads
+ *
+ * Requests from the kernel are processed, and the appropriate
+ * operations are called. Request are processed in parallel by
+ * distributing them between multiple threads.
+ *
+ * Calling this function requires the pthreads library to be linked to
+ * the application.
+ *
+ * @param f the FUSE handle
+ * @return 0 if no error occurred, -1 otherwise
+ */
+int fuse_loop_mt(struct fuse *f);
+
+/**
+ * Get the current context
+ *
+ * The context is only valid for the duration of a filesystem
+ * operation, and thus must not be stored and used later.
+ *
+ * @return the context
+ */
+struct fuse_context *fuse_get_context(void);
+
+/**
+ * Get the current supplementary group IDs for the current request
+ *
+ * Similar to the getgroups(2) system call, except the return value is
+ * always the total number of group IDs, even if it is larger than the
+ * specified size.
+ *
+ * The current fuse kernel module in linux (as of 2.6.30) doesn't pass
+ * the group list to userspace, hence this function needs to parse
+ * "/proc/$TID/task/$TID/status" to get the group IDs.
+ *
+ * This feature may not be supported on all operating systems. In
+ * such a case this function will return -ENOSYS.
+ *
+ * @param size size of given array
+ * @param list array of group IDs to be filled in
+ * @return the total number of supplementary group IDs or -errno on failure
+ */
+int fuse_getgroups(int size, gid_t list[]);
+
+/**
+ * Check if the current request has already been interrupted
+ *
+ * @return 1 if the request has been interrupted, 0 otherwise
+ */
+int fuse_interrupted(void);
+
+/**
+ * Obsolete, doesn't do anything
+ *
+ * @return -EINVAL
+ */
+int fuse_invalidate(struct fuse *f, const char *path);
+
+/* Deprecated, don't use */
+int fuse_is_lib_option(const char *opt);
+
+/**
+ * The real main function
+ *
+ * Do not call this directly, use fuse_main()
+ */
+int fuse_main_real(int argc, char *argv[], const struct fuse_operations *op,
+ size_t op_size, void *user_data);
+
+/**
+ * Start the cleanup thread when using option "remember".
+ *
+ * This is done automatically by fuse_loop_mt()
+ * @param fuse struct fuse pointer for fuse instance
+ * @return 0 on success and -1 on error
+ */
+int fuse_start_cleanup_thread(struct fuse *fuse);
+
+/**
+ * Stop the cleanup thread when using option "remember".
+ *
+ * This is done automatically by fuse_loop_mt()
+ * @param fuse struct fuse pointer for fuse instance
+ */
+void fuse_stop_cleanup_thread(struct fuse *fuse);
+
+/**
+ * Iterate over cache removing stale entries
+ * use in conjunction with "-oremember"
+ *
+ * NOTE: This is already done for the standard sessions
+ *
+ * @param fuse struct fuse pointer for fuse instance
+ * @return the number of seconds until the next cleanup
+ */
+int fuse_clean_cache(struct fuse *fuse);
+
+/*
+ * Stacking API
+ */
+
+/**
+ * Fuse filesystem object
+ *
+ * This is opaque object represents a filesystem layer
+ */
+struct fuse_fs;
+
+/*
+ * These functions call the relevant filesystem operation, and return
+ * the result.
+ *
+ * If the operation is not defined, they return -ENOSYS, with the
+ * exception of fuse_fs_open, fuse_fs_release, fuse_fs_opendir,
+ * fuse_fs_releasedir and fuse_fs_statfs, which return 0.
+ */
+
+int fuse_fs_getattr(struct fuse_fs *fs, const char *path, struct stat *buf);
+int fuse_fs_fgetattr(struct fuse_fs *fs, const char *path, struct stat *buf,
+ struct fuse_file_info *fi);
+int fuse_fs_rename(struct fuse_fs *fs, const char *oldpath,
+ const char *newpath);
+int fuse_fs_unlink(struct fuse_fs *fs, const char *path);
+int fuse_fs_rmdir(struct fuse_fs *fs, const char *path);
+int fuse_fs_symlink(struct fuse_fs *fs, const char *linkname,
+ const char *path);
+int fuse_fs_link(struct fuse_fs *fs, const char *oldpath, const char *newpath);
+int fuse_fs_release(struct fuse_fs *fs, const char *path,
+ struct fuse_file_info *fi);
+int fuse_fs_open(struct fuse_fs *fs, const char *path,
+ struct fuse_file_info *fi);
+int fuse_fs_read(struct fuse_fs *fs, const char *path, char *buf, size_t size,
+ off64_t off, struct fuse_file_info *fi);
+int fuse_fs_read_buf(struct fuse_fs *fs, const char *path,
+ struct fuse_bufvec **bufp, size_t size, off64_t off,
+ struct fuse_file_info *fi);
+int fuse_fs_write(struct fuse_fs *fs, const char *path, const char *buf,
+ size_t size, off64_t off, struct fuse_file_info *fi);
+int fuse_fs_write_buf(struct fuse_fs *fs, const char *path,
+ struct fuse_bufvec *buf, off64_t off,
+ struct fuse_file_info *fi);
+int fuse_fs_fsync(struct fuse_fs *fs, const char *path, int datasync,
+ struct fuse_file_info *fi);
+int fuse_fs_flush(struct fuse_fs *fs, const char *path,
+ struct fuse_file_info *fi);
+int fuse_fs_statfs(struct fuse_fs *fs, const char *path, struct statvfs *buf);
+int fuse_fs_opendir(struct fuse_fs *fs, const char *path,
+ struct fuse_file_info *fi);
+int fuse_fs_readdir(struct fuse_fs *fs, const char *path, void *buf,
+ fuse_fill_dir_t filler, off64_t off,
+ struct fuse_file_info *fi);
+int fuse_fs_fsyncdir(struct fuse_fs *fs, const char *path, int datasync,
+ struct fuse_file_info *fi);
+int fuse_fs_releasedir(struct fuse_fs *fs, const char *path,
+ struct fuse_file_info *fi);
+int fuse_fs_create(struct fuse_fs *fs, const char *path, mode_t mode,
+ struct fuse_file_info *fi);
+int fuse_fs_lock(struct fuse_fs *fs, const char *path,
+ struct fuse_file_info *fi, int cmd, struct flock *lock);
+int fuse_fs_flock(struct fuse_fs *fs, const char *path,
+ struct fuse_file_info *fi, int op);
+int fuse_fs_chmod(struct fuse_fs *fs, const char *path, mode_t mode);
+int fuse_fs_chown(struct fuse_fs *fs, const char *path, uid_t uid, gid_t gid);
+int fuse_fs_truncate(struct fuse_fs *fs, const char *path, off64_t size);
+int fuse_fs_ftruncate(struct fuse_fs *fs, const char *path, off64_t size,
+ struct fuse_file_info *fi);
+int fuse_fs_utimens(struct fuse_fs *fs, const char *path,
+ const struct timespec tv[2]);
+int fuse_fs_access(struct fuse_fs *fs, const char *path, int mask);
+int fuse_fs_readlink(struct fuse_fs *fs, const char *path, char *buf,
+ size_t len);
+int fuse_fs_mknod(struct fuse_fs *fs, const char *path, mode_t mode,
+ dev_t rdev);
+int fuse_fs_mkdir(struct fuse_fs *fs, const char *path, mode_t mode);
+int fuse_fs_setxattr(struct fuse_fs *fs, const char *path, const char *name,
+ const char *value, size_t size, int flags);
+int fuse_fs_getxattr(struct fuse_fs *fs, const char *path, const char *name,
+ char *value, size_t size);
+int fuse_fs_listxattr(struct fuse_fs *fs, const char *path, char *list,
+ size_t size);
+int fuse_fs_removexattr(struct fuse_fs *fs, const char *path,
+ const char *name);
+int fuse_fs_bmap(struct fuse_fs *fs, const char *path, size_t blocksize,
+ uint64_t *idx);
+int fuse_fs_ioctl(struct fuse_fs *fs, const char *path, int cmd, void *arg,
+ struct fuse_file_info *fi, unsigned int flags, void *data);
+int fuse_fs_poll(struct fuse_fs *fs, const char *path,
+ struct fuse_file_info *fi, struct fuse_pollhandle *ph,
+ unsigned *reventsp);
+int fuse_fs_fallocate(struct fuse_fs *fs, const char *path, int mode,
+ off64_t offset, off64_t length, struct fuse_file_info *fi);
+void fuse_fs_init(struct fuse_fs *fs, struct fuse_conn_info *conn);
+void fuse_fs_destroy(struct fuse_fs *fs);
+
+int fuse_notify_poll(struct fuse_pollhandle *ph);
+
+/**
+ * Create a new fuse filesystem object
+ *
+ * This is usually called from the factory of a fuse module to create
+ * a new instance of a filesystem.
+ *
+ * @param op the filesystem operations
+ * @param op_size the size of the fuse_operations structure
+ * @param user_data user data supplied in the context during the init() method
+ * @return a new filesystem object
+ */
+struct fuse_fs *fuse_fs_new(const struct fuse_operations *op, size_t op_size,
+ void *user_data);
+
+/**
+ * Filesystem module
+ *
+ * Filesystem modules are registered with the FUSE_REGISTER_MODULE()
+ * macro.
+ *
+ * If the "-omodules=modname:..." option is present, filesystem
+ * objects are created and pushed onto the stack with the 'factory'
+ * function.
+ */
+struct fuse_module {
+ /**
+ * Name of filesystem
+ */
+ const char *name;
+
+ /**
+ * Factory for creating filesystem objects
+ *
+ * The function may use and remove options from 'args' that belong
+ * to this module.
+ *
+ * For now the 'fs' vector always contains exactly one filesystem.
+ * This is the filesystem which will be below the newly created
+ * filesystem in the stack.
+ *
+ * @param args the command line arguments
+ * @param fs NULL terminated filesystem object vector
+ * @return the new filesystem object
+ */
+ struct fuse_fs *(*factory)(struct fuse_args *args,
+ struct fuse_fs *fs[]);
+
+ struct fuse_module *next;
+ struct fusemod_so *so;
+ int ctr;
+};
+
+/**
+ * Register a filesystem module
+ *
+ * This function is used by FUSE_REGISTER_MODULE and there's usually
+ * no need to call it directly
+ */
+void fuse_register_module(struct fuse_module *mod);
+
+/**
+ * Register filesystem module
+ *
+ * For the parameters, see description of the fields in 'struct
+ * fuse_module'
+ */
+#define FUSE_REGISTER_MODULE(name_, factory_) \
+ static __attribute__((constructor)) void name_ ## _register(void) \
+ { \
+ static struct fuse_module mod = \
+ { #name_, factory_, NULL, NULL, 0 }; \
+ fuse_register_module(&mod); \
+ }
+
+
+/* ----------------------------------------------------------- *
+ * Advanced API for event handling, don't worry about this... *
+ * ----------------------------------------------------------- */
+
+/* NOTE: the following functions are deprecated, and will be removed
+ from the 3.0 API. Use the lowlevel session functions instead */
+
+/** Function type used to process commands */
+typedef void (*fuse_processor_t)(struct fuse *, struct fuse_cmd *, void *);
+
+/** This is the part of fuse_main() before the event loop */
+struct fuse *fuse_setup(int argc, char *argv[],
+ const struct fuse_operations *op, size_t op_size,
+ char **mountpoint, int *multithreaded,
+ void *user_data);
+
+/** This is the part of fuse_main() after the event loop */
+void fuse_teardown(struct fuse *fuse, char *mountpoint);
+
+/** Read a single command. If none are read, return NULL */
+struct fuse_cmd *fuse_read_cmd(struct fuse *f);
+
+/** Process a single command */
+void fuse_process_cmd(struct fuse *f, struct fuse_cmd *cmd);
+
+/** Multi threaded event loop, which calls the custom command
+ processor function */
+int fuse_loop_mt_proc(struct fuse *f, fuse_processor_t proc, void *data);
+
+/** Return the exited flag, which indicates if fuse_exit() has been
+ called */
+int fuse_exited(struct fuse *f);
+
+/** This function is obsolete and implemented as a no-op */
+void fuse_set_getcontext_func(struct fuse_context *(*func)(void));
+
+/** Get session from fuse object */
+struct fuse_session *fuse_get_session(struct fuse *f);
+
+/* ----------------------------------------------------------- *
+ * Compatibility stuff *
+ * ----------------------------------------------------------- */
+
+#if FUSE_USE_VERSION < 26
+# include "fuse_compat.h"
+# undef fuse_main
+# if FUSE_USE_VERSION == 25
+# define fuse_main(argc, argv, op) \
+ fuse_main_real_compat25(argc, argv, op, sizeof(*(op)))
+# define fuse_new fuse_new_compat25
+# define fuse_setup fuse_setup_compat25
+# define fuse_teardown fuse_teardown_compat22
+# define fuse_operations fuse_operations_compat25
+# elif FUSE_USE_VERSION == 22
+# define fuse_main(argc, argv, op) \
+ fuse_main_real_compat22(argc, argv, op, sizeof(*(op)))
+# define fuse_new fuse_new_compat22
+# define fuse_setup fuse_setup_compat22
+# define fuse_teardown fuse_teardown_compat22
+# define fuse_operations fuse_operations_compat22
+# define fuse_file_info fuse_file_info_compat
+# elif FUSE_USE_VERSION == 24
+# error Compatibility with high-level API version 24 not supported
+# else
+# define fuse_dirfil_t fuse_dirfil_t_compat
+# define __fuse_read_cmd fuse_read_cmd
+# define __fuse_process_cmd fuse_process_cmd
+# define __fuse_loop_mt fuse_loop_mt_proc
+# if FUSE_USE_VERSION == 21
+# define fuse_operations fuse_operations_compat2
+# define fuse_main fuse_main_compat2
+# define fuse_new fuse_new_compat2
+# define __fuse_setup fuse_setup_compat2
+# define __fuse_teardown fuse_teardown_compat22
+# define __fuse_exited fuse_exited
+# define __fuse_set_getcontext_func fuse_set_getcontext_func
+# else
+# define fuse_statfs fuse_statfs_compat1
+# define fuse_operations fuse_operations_compat1
+# define fuse_main fuse_main_compat1
+# define fuse_new fuse_new_compat1
+# define FUSE_DEBUG FUSE_DEBUG_COMPAT1
+# endif
+# endif
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FUSE_H_ */
diff --git a/fuse/include/fuse_common.h b/fuse/include/fuse_common.h
new file mode 100644
index 000000000..dab3a569a
--- /dev/null
+++ b/fuse/include/fuse_common.h
@@ -0,0 +1,505 @@
+/*
+ FUSE: Filesystem in Userspace
+ Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
+
+ This program can be distributed under the terms of the GNU LGPLv2.
+ See the file COPYING.LIB.
+*/
+
+/** @file */
+
+#if !defined(_FUSE_H_) && !defined(_FUSE_LOWLEVEL_H_)
+#error "Never include <fuse_common.h> directly; use <fuse.h> or <fuse_lowlevel.h> instead."
+#endif
+
+#ifndef _FUSE_COMMON_H_
+#define _FUSE_COMMON_H_
+
+#include "fuse_opt.h"
+#include <stdint.h>
+#include <sys/types.h>
+
+/** Major version of FUSE library interface */
+#define FUSE_MAJOR_VERSION 2
+
+/** Minor version of FUSE library interface */
+#define FUSE_MINOR_VERSION 9
+
+#define FUSE_MAKE_VERSION(maj, min) ((maj) * 10 + (min))
+#define FUSE_VERSION FUSE_MAKE_VERSION(FUSE_MAJOR_VERSION, FUSE_MINOR_VERSION)
+
+/* This interface uses 64 bit off64_t */
+#if _FILE_OFFSET_BITS != 64
+#error Please add -D_FILE_OFFSET_BITS=64 to your compile flags!
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Information about open files
+ *
+ * Changed in version 2.5
+ */
+struct fuse_file_info {
+ /** Open flags. Available in open() and release() */
+ int flags;
+
+ /** Old file handle, don't use */
+ unsigned long fh_old;
+
+ /** In case of a write operation indicates if this was caused by a
+ writepage */
+ int writepage;
+
+ /** Can be filled in by open, to use direct I/O on this file.
+ Introduced in version 2.4 */
+ unsigned int direct_io : 1;
+
+ /** Can be filled in by open, to indicate, that cached file data
+ need not be invalidated. Introduced in version 2.4 */
+ unsigned int keep_cache : 1;
+
+ /** Indicates a flush operation. Set in flush operation, also
+ maybe set in highlevel lock operation and lowlevel release
+ operation. Introduced in version 2.6 */
+ unsigned int flush : 1;
+
+ /** Can be filled in by open, to indicate that the file is not
+ seekable. Introduced in version 2.8 */
+ unsigned int nonseekable : 1;
+
+ /* Indicates that flock locks for this file should be
+ released. If set, lock_owner shall contain a valid value.
+ May only be set in ->release(). Introduced in version
+ 2.9 */
+ unsigned int flock_release : 1;
+
+ /** Padding. Do not use*/
+ unsigned int padding : 27;
+
+ /** File handle. May be filled in by filesystem in open().
+ Available in all other file operations */
+ uint64_t fh;
+
+ /** Lock owner id. Available in locking operations and flush */
+ uint64_t lock_owner;
+};
+
+/**
+ * Capability bits for 'fuse_conn_info.capable' and 'fuse_conn_info.want'
+ *
+ * FUSE_CAP_ASYNC_READ: filesystem supports asynchronous read requests
+ * FUSE_CAP_POSIX_LOCKS: filesystem supports "remote" locking
+ * FUSE_CAP_ATOMIC_O_TRUNC: filesystem handles the O_TRUNC open flag
+ * FUSE_CAP_EXPORT_SUPPORT: filesystem handles lookups of "." and ".."
+ * FUSE_CAP_BIG_WRITES: filesystem can handle write size larger than 4kB
+ * FUSE_CAP_DONT_MASK: don't apply umask to file mode on create operations
+ * FUSE_CAP_SPLICE_WRITE: ability to use splice() to write to the fuse device
+ * FUSE_CAP_SPLICE_MOVE: ability to move data to the fuse device with splice()
+ * FUSE_CAP_SPLICE_READ: ability to use splice() to read from the fuse device
+ * FUSE_CAP_IOCTL_DIR: ioctl support on directories
+ */
+#define FUSE_CAP_ASYNC_READ (1 << 0)
+#define FUSE_CAP_POSIX_LOCKS (1 << 1)
+#define FUSE_CAP_ATOMIC_O_TRUNC (1 << 3)
+#define FUSE_CAP_EXPORT_SUPPORT (1 << 4)
+#define FUSE_CAP_BIG_WRITES (1 << 5)
+#define FUSE_CAP_DONT_MASK (1 << 6)
+#define FUSE_CAP_SPLICE_WRITE (1 << 7)
+#define FUSE_CAP_SPLICE_MOVE (1 << 8)
+#define FUSE_CAP_SPLICE_READ (1 << 9)
+#define FUSE_CAP_FLOCK_LOCKS (1 << 10)
+#define FUSE_CAP_IOCTL_DIR (1 << 11)
+
+/**
+ * Ioctl flags
+ *
+ * FUSE_IOCTL_COMPAT: 32bit compat ioctl on 64bit machine
+ * FUSE_IOCTL_UNRESTRICTED: not restricted to well-formed ioctls, retry allowed
+ * FUSE_IOCTL_RETRY: retry with new iovecs
+ * FUSE_IOCTL_DIR: is a directory
+ *
+ * FUSE_IOCTL_MAX_IOV: maximum of in_iovecs + out_iovecs
+ */
+#define FUSE_IOCTL_COMPAT (1 << 0)
+#define FUSE_IOCTL_UNRESTRICTED (1 << 1)
+#define FUSE_IOCTL_RETRY (1 << 2)
+#define FUSE_IOCTL_DIR (1 << 4)
+
+#define FUSE_IOCTL_MAX_IOV 256
+
+/**
+ * Connection information, passed to the ->init() method
+ *
+ * Some of the elements are read-write, these can be changed to
+ * indicate the value requested by the filesystem. The requested
+ * value must usually be smaller than the indicated value.
+ */
+struct fuse_conn_info {
+ /**
+ * Major version of the protocol (read-only)
+ */
+ unsigned proto_major;
+
+ /**
+ * Minor version of the protocol (read-only)
+ */
+ unsigned proto_minor;
+
+ /**
+ * Is asynchronous read supported (read-write)
+ */
+ unsigned async_read;
+
+ /**
+ * Maximum size of the write buffer
+ */
+ unsigned max_write;
+
+ /**
+ * Maximum readahead
+ */
+ unsigned max_readahead;
+
+ /**
+ * Capability flags, that the kernel supports
+ */
+ unsigned capable;
+
+ /**
+ * Capability flags, that the filesystem wants to enable
+ */
+ unsigned want;
+
+ /**
+ * Maximum number of backgrounded requests
+ */
+ unsigned max_background;
+
+ /**
+ * Kernel congestion threshold parameter
+ */
+ unsigned congestion_threshold;
+
+ /**
+ * For future use.
+ */
+ unsigned reserved[23];
+};
+
+struct fuse_session;
+struct fuse_chan;
+struct fuse_pollhandle;
+
+/**
+ * Create a FUSE mountpoint
+ *
+ * Returns a control file descriptor suitable for passing to
+ * fuse_new()
+ *
+ * @param mountpoint the mount point path
+ * @param args argument vector
+ * @return the communication channel on success, NULL on failure
+ */
+struct fuse_chan *fuse_mount(const char *mountpoint, struct fuse_args *args);
+
+/**
+ * Umount a FUSE mountpoint
+ *
+ * @param mountpoint the mount point path
+ * @param ch the communication channel
+ */
+void fuse_unmount(const char *mountpoint, struct fuse_chan *ch);
+
+/**
+ * Parse common options
+ *
+ * The following options are parsed:
+ *
+ * '-f' foreground
+ * '-d' '-odebug' foreground, but keep the debug option
+ * '-s' single threaded
+ * '-h' '--help' help
+ * '-ho' help without header
+ * '-ofsname=..' file system name, if not present, then set to the program
+ * name
+ *
+ * All parameters may be NULL
+ *
+ * @param args argument vector
+ * @param mountpoint the returned mountpoint, should be freed after use
+ * @param multithreaded set to 1 unless the '-s' option is present
+ * @param foreground set to 1 if one of the relevant options is present
+ * @return 0 on success, -1 on failure
+ */
+int fuse_parse_cmdline(struct fuse_args *args, char **mountpoint,
+ int *multithreaded, int *foreground);
+
+/**
+ * Go into the background
+ *
+ * @param foreground if true, stay in the foreground
+ * @return 0 on success, -1 on failure
+ */
+int fuse_daemonize(int foreground);
+
+/**
+ * Get the version of the library
+ *
+ * @return the version
+ */
+int fuse_version(void);
+
+/**
+ * Destroy poll handle
+ *
+ * @param ph the poll handle
+ */
+void fuse_pollhandle_destroy(struct fuse_pollhandle *ph);
+
+/* ----------------------------------------------------------- *
+ * Data buffer *
+ * ----------------------------------------------------------- */
+
+/**
+ * Buffer flags
+ */
+enum fuse_buf_flags {
+ /**
+ * Buffer contains a file descriptor
+ *
+ * If this flag is set, the .fd field is valid, otherwise the
+ * .mem fields is valid.
+ */
+ FUSE_BUF_IS_FD = (1 << 1),
+
+ /**
+ * Seek on the file descriptor
+ *
+ * If this flag is set then the .pos field is valid and is
+ * used to seek to the given offset before performing
+ * operation on file descriptor.
+ */
+ FUSE_BUF_FD_SEEK = (1 << 2),
+
+ /**
+ * Retry operation on file descriptor
+ *
+ * If this flag is set then retry operation on file descriptor
+ * until .size bytes have been copied or an error or EOF is
+ * detected.
+ */
+ FUSE_BUF_FD_RETRY = (1 << 3),
+};
+
+/**
+ * Buffer copy flags
+ */
+enum fuse_buf_copy_flags {
+ /**
+ * Don't use splice(2)
+ *
+ * Always fall back to using read and write instead of
+ * splice(2) to copy data from one file descriptor to another.
+ *
+ * If this flag is not set, then only fall back if splice is
+ * unavailable.
+ */
+ FUSE_BUF_NO_SPLICE = (1 << 1),
+
+ /**
+ * Force splice
+ *
+ * Always use splice(2) to copy data from one file descriptor
+ * to another. If splice is not available, return -EINVAL.
+ */
+ FUSE_BUF_FORCE_SPLICE = (1 << 2),
+
+ /**
+ * Try to move data with splice.
+ *
+ * If splice is used, try to move pages from the source to the
+ * destination instead of copying. See documentation of
+ * SPLICE_F_MOVE in splice(2) man page.
+ */
+ FUSE_BUF_SPLICE_MOVE = (1 << 3),
+
+ /**
+ * Don't block on the pipe when copying data with splice
+ *
+ * Makes the operations on the pipe non-blocking (if the pipe
+ * is full or empty). See SPLICE_F_NONBLOCK in the splice(2)
+ * man page.
+ */
+ FUSE_BUF_SPLICE_NONBLOCK= (1 << 4),
+};
+
+/**
+ * Single data buffer
+ *
+ * Generic data buffer for I/O, extended attributes, etc... Data may
+ * be supplied as a memory pointer or as a file descriptor
+ */
+struct fuse_buf {
+ /**
+ * Size of data in bytes
+ */
+ size_t size;
+
+ /**
+ * Buffer flags
+ */
+ enum fuse_buf_flags flags;
+
+ /**
+ * Memory pointer
+ *
+ * Used unless FUSE_BUF_IS_FD flag is set.
+ */
+ void *mem;
+
+ /**
+ * File descriptor
+ *
+ * Used if FUSE_BUF_IS_FD flag is set.
+ */
+ int fd;
+
+ /**
+ * File position
+ *
+ * Used if FUSE_BUF_FD_SEEK flag is set.
+ */
+ off64_t pos;
+};
+
+/**
+ * Data buffer vector
+ *
+ * An array of data buffers, each containing a memory pointer or a
+ * file descriptor.
+ *
+ * Allocate dynamically to add more than one buffer.
+ */
+struct fuse_bufvec {
+ /**
+ * Number of buffers in the array
+ */
+ size_t count;
+
+ /**
+ * Index of current buffer within the array
+ */
+ size_t idx;
+
+ /**
+ * Current offset within the current buffer
+ */
+ size_t off;
+
+ /**
+ * Array of buffers
+ */
+ struct fuse_buf buf[1];
+};
+
+/* Initialize bufvec with a single buffer of given size */
+#define FUSE_BUFVEC_INIT(size__) \
+ ((struct fuse_bufvec) { \
+ /* .count= */ 1, \
+ /* .idx = */ 0, \
+ /* .off = */ 0, \
+ /* .buf = */ { /* [0] = */ { \
+ /* .size = */ (size__), \
+ /* .flags = */ (enum fuse_buf_flags) 0, \
+ /* .mem = */ NULL, \
+ /* .fd = */ -1, \
+ /* .pos = */ 0, \
+ } } \
+ } )
+
+/**
+ * Get total size of data in a fuse buffer vector
+ *
+ * @param bufv buffer vector
+ * @return size of data
+ */
+size_t fuse_buf_size(const struct fuse_bufvec *bufv);
+
+/**
+ * Copy data from one buffer vector to another
+ *
+ * @param dst destination buffer vector
+ * @param src source buffer vector
+ * @param flags flags controlling the copy
+ * @return actual number of bytes copied or -errno on error
+ */
+ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src,
+ enum fuse_buf_copy_flags flags);
+
+/* ----------------------------------------------------------- *
+ * Signal handling *
+ * ----------------------------------------------------------- */
+
+/**
+ * Exit session on HUP, TERM and INT signals and ignore PIPE signal
+ *
+ * Stores session in a global variable. May only be called once per
+ * process until fuse_remove_signal_handlers() is called.
+ *
+ * @param se the session to exit
+ * @return 0 on success, -1 on failure
+ */
+int fuse_set_signal_handlers(struct fuse_session *se);
+
+/**
+ * Restore default signal handlers
+ *
+ * Resets global session. After this fuse_set_signal_handlers() may
+ * be called again.
+ *
+ * @param se the same session as given in fuse_set_signal_handlers()
+ */
+void fuse_remove_signal_handlers(struct fuse_session *se);
+
+/* ----------------------------------------------------------- *
+ * Compatibility stuff *
+ * ----------------------------------------------------------- */
+
+#if FUSE_USE_VERSION < 26
+# ifdef __FreeBSD__
+# if FUSE_USE_VERSION < 25
+# error On FreeBSD API version 25 or greater must be used
+# endif
+# endif
+# include "fuse_common_compat.h"
+# undef FUSE_MINOR_VERSION
+# undef fuse_main
+# define fuse_unmount fuse_unmount_compat22
+# if FUSE_USE_VERSION == 25
+# define FUSE_MINOR_VERSION 5
+# define fuse_mount fuse_mount_compat25
+# elif FUSE_USE_VERSION == 24 || FUSE_USE_VERSION == 22
+# define FUSE_MINOR_VERSION 4
+# define fuse_mount fuse_mount_compat22
+# elif FUSE_USE_VERSION == 21
+# define FUSE_MINOR_VERSION 1
+# define fuse_mount fuse_mount_compat22
+# elif FUSE_USE_VERSION == 11
+# warning Compatibility with API version 11 is deprecated
+# undef FUSE_MAJOR_VERSION
+# define FUSE_MAJOR_VERSION 1
+# define FUSE_MINOR_VERSION 1
+# define fuse_mount fuse_mount_compat1
+# else
+# error Compatibility with API version other than 21, 22, 24, 25 and 11 not supported
+# endif
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FUSE_COMMON_H_ */
diff --git a/fuse/include/fuse_common_compat.h b/fuse/include/fuse_common_compat.h
new file mode 100644
index 000000000..34440ff71
--- /dev/null
+++ b/fuse/include/fuse_common_compat.h
@@ -0,0 +1,26 @@
+/*
+ FUSE: Filesystem in Userspace
+ Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
+
+ This program can be distributed under the terms of the GNU LGPLv2.
+ See the file COPYING.LIB.
+*/
+
+/* these definitions provide source compatibility to prior versions.
+ Do not include this file directly! */
+
+struct fuse_file_info_compat {
+ int flags;
+ unsigned long fh;
+ int writepage;
+ unsigned int direct_io : 1;
+ unsigned int keep_cache : 1;
+};
+
+int fuse_mount_compat25(const char *mountpoint, struct fuse_args *args);
+
+int fuse_mount_compat22(const char *mountpoint, const char *opts);
+
+int fuse_mount_compat1(const char *mountpoint, const char *args[]);
+
+void fuse_unmount_compat22(const char *mountpoint);
diff --git a/fuse/include/fuse_compat.h b/fuse/include/fuse_compat.h
new file mode 100644
index 000000000..d09323844
--- /dev/null
+++ b/fuse/include/fuse_compat.h
@@ -0,0 +1,201 @@
+/*
+ FUSE: Filesystem in Userspace
+ Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
+
+ This program can be distributed under the terms of the GNU LGPLv2.
+ See the file COPYING.LIB.
+*/
+
+/* these definitions provide source compatibility to prior versions.
+ Do not include this file directly! */
+
+struct fuse_operations_compat25 {
+ int (*getattr) (const char *, struct stat *);
+ int (*readlink) (const char *, char *, size_t);
+ int (*getdir) (const char *, fuse_dirh_t, fuse_dirfil_t);
+ int (*mknod) (const char *, mode_t, dev_t);
+ int (*mkdir) (const char *, mode_t);
+ int (*unlink) (const char *);
+ int (*rmdir) (const char *);
+ int (*symlink) (const char *, const char *);
+ int (*rename) (const char *, const char *);
+ int (*link) (const char *, const char *);
+ int (*chmod) (const char *, mode_t);
+ int (*chown) (const char *, uid_t, gid_t);
+ int (*truncate) (const char *, off64_t);
+ int (*utime) (const char *, struct utimbuf *);
+ int (*open) (const char *, struct fuse_file_info *);
+ int (*read) (const char *, char *, size_t, off64_t,
+ struct fuse_file_info *);
+ int (*write) (const char *, const char *, size_t, off64_t,
+ struct fuse_file_info *);
+ int (*statfs) (const char *, struct statvfs *);
+ int (*flush) (const char *, struct fuse_file_info *);
+ int (*release) (const char *, struct fuse_file_info *);
+ int (*fsync) (const char *, int, struct fuse_file_info *);
+ int (*setxattr) (const char *, const char *, const char *, size_t, int);
+ int (*getxattr) (const char *, const char *, char *, size_t);
+ int (*listxattr) (const char *, char *, size_t);
+ int (*removexattr) (const char *, const char *);
+ int (*opendir) (const char *, struct fuse_file_info *);
+ int (*readdir) (const char *, void *, fuse_fill_dir_t, off64_t,
+ struct fuse_file_info *);
+ int (*releasedir) (const char *, struct fuse_file_info *);
+ int (*fsyncdir) (const char *, int, struct fuse_file_info *);
+ void *(*init) (void);
+ void (*destroy) (void *);
+ int (*access) (const char *, int);
+ int (*create) (const char *, mode_t, struct fuse_file_info *);
+ int (*ftruncate) (const char *, off64_t, struct fuse_file_info *);
+ int (*fgetattr) (const char *, struct stat *, struct fuse_file_info *);
+};
+
+struct fuse *fuse_new_compat25(int fd, struct fuse_args *args,
+ const struct fuse_operations_compat25 *op,
+ size_t op_size);
+
+int fuse_main_real_compat25(int argc, char *argv[],
+ const struct fuse_operations_compat25 *op,
+ size_t op_size);
+
+struct fuse *fuse_setup_compat25(int argc, char *argv[],
+ const struct fuse_operations_compat25 *op,
+ size_t op_size, char **mountpoint,
+ int *multithreaded, int *fd);
+
+void fuse_teardown_compat22(struct fuse *fuse, int fd, char *mountpoint);
+
+#if !defined(__FreeBSD__) && !defined(__NetBSD__)
+#include <sys/statfs.h>
+
+struct fuse_operations_compat22 {
+ int (*getattr) (const char *, struct stat *);
+ int (*readlink) (const char *, char *, size_t);
+ int (*getdir) (const char *, fuse_dirh_t, fuse_dirfil_t);
+ int (*mknod) (const char *, mode_t, dev_t);
+ int (*mkdir) (const char *, mode_t);
+ int (*unlink) (const char *);
+ int (*rmdir) (const char *);
+ int (*symlink) (const char *, const char *);
+ int (*rename) (const char *, const char *);
+ int (*link) (const char *, const char *);
+ int (*chmod) (const char *, mode_t);
+ int (*chown) (const char *, uid_t, gid_t);
+ int (*truncate) (const char *, off64_t);
+ int (*utime) (const char *, struct utimbuf *);
+ int (*open) (const char *, struct fuse_file_info_compat *);
+ int (*read) (const char *, char *, size_t, off64_t,
+ struct fuse_file_info_compat *);
+ int (*write) (const char *, const char *, size_t, off64_t,
+ struct fuse_file_info_compat *);
+ int (*statfs) (const char *, struct statfs *);
+ int (*flush) (const char *, struct fuse_file_info_compat *);
+ int (*release) (const char *, struct fuse_file_info_compat *);
+ int (*fsync) (const char *, int, struct fuse_file_info_compat *);
+ int (*setxattr) (const char *, const char *, const char *, size_t, int);
+ int (*getxattr) (const char *, const char *, char *, size_t);
+ int (*listxattr) (const char *, char *, size_t);
+ int (*removexattr) (const char *, const char *);
+ int (*opendir) (const char *, struct fuse_file_info_compat *);
+ int (*readdir) (const char *, void *, fuse_fill_dir_t, off64_t,
+ struct fuse_file_info_compat *);
+ int (*releasedir) (const char *, struct fuse_file_info_compat *);
+ int (*fsyncdir) (const char *, int, struct fuse_file_info_compat *);
+ void *(*init) (void);
+ void (*destroy) (void *);
+};
+
+struct fuse *fuse_new_compat22(int fd, const char *opts,
+ const struct fuse_operations_compat22 *op,
+ size_t op_size);
+
+struct fuse *fuse_setup_compat22(int argc, char *argv[],
+ const struct fuse_operations_compat22 *op,
+ size_t op_size, char **mountpoint,
+ int *multithreaded, int *fd);
+
+int fuse_main_real_compat22(int argc, char *argv[],
+ const struct fuse_operations_compat22 *op,
+ size_t op_size);
+
+typedef int (*fuse_dirfil_t_compat) (fuse_dirh_t h, const char *name, int type);
+struct fuse_operations_compat2 {
+ int (*getattr) (const char *, struct stat *);
+ int (*readlink) (const char *, char *, size_t);
+ int (*getdir) (const char *, fuse_dirh_t, fuse_dirfil_t_compat);
+ int (*mknod) (const char *, mode_t, dev_t);
+ int (*mkdir) (const char *, mode_t);
+ int (*unlink) (const char *);
+ int (*rmdir) (const char *);
+ int (*symlink) (const char *, const char *);
+ int (*rename) (const char *, const char *);
+ int (*link) (const char *, const char *);
+ int (*chmod) (const char *, mode_t);
+ int (*chown) (const char *, uid_t, gid_t);
+ int (*truncate) (const char *, off64_t);
+ int (*utime) (const char *, struct utimbuf *);
+ int (*open) (const char *, int);
+ int (*read) (const char *, char *, size_t, off64_t);
+ int (*write) (const char *, const char *, size_t, off64_t);
+ int (*statfs) (const char *, struct statfs *);
+ int (*flush) (const char *);
+ int (*release) (const char *, int);
+ int (*fsync) (const char *, int);
+ int (*setxattr) (const char *, const char *, const char *,
+ size_t, int);
+ int (*getxattr) (const char *, const char *, char *, size_t);
+ int (*listxattr) (const char *, char *, size_t);
+ int (*removexattr) (const char *, const char *);
+};
+
+int fuse_main_compat2(int argc, char *argv[],
+ const struct fuse_operations_compat2 *op);
+
+struct fuse *fuse_new_compat2(int fd, const char *opts,
+ const struct fuse_operations_compat2 *op);
+
+struct fuse *fuse_setup_compat2(int argc, char *argv[],
+ const struct fuse_operations_compat2 *op,
+ char **mountpoint, int *multithreaded, int *fd);
+
+struct fuse_statfs_compat1 {
+ long block_size;
+ long blocks;
+ long blocks_free;
+ long files;
+ long files_free;
+ long namelen;
+};
+
+struct fuse_operations_compat1 {
+ int (*getattr) (const char *, struct stat *);
+ int (*readlink) (const char *, char *, size_t);
+ int (*getdir) (const char *, fuse_dirh_t, fuse_dirfil_t_compat);
+ int (*mknod) (const char *, mode_t, dev_t);
+ int (*mkdir) (const char *, mode_t);
+ int (*unlink) (const char *);
+ int (*rmdir) (const char *);
+ int (*symlink) (const char *, const char *);
+ int (*rename) (const char *, const char *);
+ int (*link) (const char *, const char *);
+ int (*chmod) (const char *, mode_t);
+ int (*chown) (const char *, uid_t, gid_t);
+ int (*truncate) (const char *, off64_t);
+ int (*utime) (const char *, struct utimbuf *);
+ int (*open) (const char *, int);
+ int (*read) (const char *, char *, size_t, off64_t);
+ int (*write) (const char *, const char *, size_t, off64_t);
+ int (*statfs) (struct fuse_statfs_compat1 *);
+ int (*release) (const char *, int);
+ int (*fsync) (const char *, int);
+};
+
+#define FUSE_DEBUG_COMPAT1 (1 << 1)
+
+struct fuse *fuse_new_compat1(int fd, int flags,
+ const struct fuse_operations_compat1 *op);
+
+void fuse_main_compat1(int argc, char *argv[],
+ const struct fuse_operations_compat1 *op);
+
+#endif /* __FreeBSD__ || __NetBSD__ */
diff --git a/fuse/include/fuse_kernel.h b/fuse/include/fuse_kernel.h
new file mode 100644
index 000000000..c632b58fb
--- /dev/null
+++ b/fuse/include/fuse_kernel.h
@@ -0,0 +1,691 @@
+/*
+ This file defines the kernel interface of FUSE
+ Copyright (C) 2001-2008 Miklos Szeredi <miklos@szeredi.hu>
+
+ This program can be distributed under the terms of the GNU GPL.
+ See the file COPYING.
+
+ This -- and only this -- header file may also be distributed under
+ the terms of the BSD Licence as follows:
+
+ Copyright (C) 2001-2007 Miklos Szeredi. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ SUCH DAMAGE.
+*/
+
+/*
+ * This file defines the kernel interface of FUSE
+ *
+ * Protocol changelog:
+ *
+ * 7.9:
+ * - new fuse_getattr_in input argument of GETATTR
+ * - add lk_flags in fuse_lk_in
+ * - add lock_owner field to fuse_setattr_in, fuse_read_in and fuse_write_in
+ * - add blksize field to fuse_attr
+ * - add file flags field to fuse_read_in and fuse_write_in
+ *
+ * 7.10
+ * - add nonseekable open flag
+ *
+ * 7.11
+ * - add IOCTL message
+ * - add unsolicited notification support
+ * - add POLL message and NOTIFY_POLL notification
+ *
+ * 7.12
+ * - add umask flag to input argument of open, mknod and mkdir
+ * - add notification messages for invalidation of inodes and
+ * directory entries
+ *
+ * 7.13
+ * - make max number of background requests and congestion threshold
+ * tunables
+ *
+ * 7.14
+ * - add splice support to fuse device
+ *
+ * 7.15
+ * - add store notify
+ * - add retrieve notify
+ *
+ * 7.16
+ * - add BATCH_FORGET request
+ * - FUSE_IOCTL_UNRESTRICTED shall now return with array of 'struct
+ * fuse_ioctl_iovec' instead of ambiguous 'struct iovec'
+ * - add FUSE_IOCTL_32BIT flag
+ *
+ * 7.17
+ * - add FUSE_FLOCK_LOCKS and FUSE_RELEASE_FLOCK_UNLOCK
+ *
+ * 7.18
+ * - add FUSE_IOCTL_DIR flag
+ * - add FUSE_NOTIFY_DELETE
+ *
+ * 7.19
+ * - add FUSE_FALLOCATE
+ */
+
+#ifndef _LINUX_FUSE_H
+#define _LINUX_FUSE_H
+
+#include <sys/types.h>
+#define __u64 uint64_t
+#define __s64 int64_t
+#define __u32 uint32_t
+#define __s32 int32_t
+#define __u16 uint16_t
+
+/*
+ * Version negotiation:
+ *
+ * Both the kernel and userspace send the version they support in the
+ * INIT request and reply respectively.
+ *
+ * If the major versions match then both shall use the smallest
+ * of the two minor versions for communication.
+ *
+ * If the kernel supports a larger major version, then userspace shall
+ * reply with the major version it supports, ignore the rest of the
+ * INIT message and expect a new INIT message from the kernel with a
+ * matching major version.
+ *
+ * If the library supports a larger major version, then it shall fall
+ * back to the major protocol version sent by the kernel for
+ * communication and reply with that major version (and an arbitrary
+ * supported minor version).
+ */
+
+/** Version number of this interface */
+#define FUSE_KERNEL_VERSION 7
+
+/** Minor version number of this interface */
+#define FUSE_KERNEL_MINOR_VERSION 19
+
+/** The node ID of the root inode */
+#define FUSE_ROOT_ID 1
+
+/* Make sure all structures are padded to 64bit boundary, so 32bit
+ userspace works under 64bit kernels */
+
+struct fuse_attr {
+ __u64 ino;
+ __u64 size;
+ __u64 blocks;
+ __u64 atime;
+ __u64 mtime;
+ __u64 ctime;
+ __u32 atimensec;
+ __u32 mtimensec;
+ __u32 ctimensec;
+ __u32 mode;
+ __u32 nlink;
+ __u32 uid;
+ __u32 gid;
+ __u32 rdev;
+ __u32 blksize;
+ __u32 padding;
+};
+
+struct fuse_kstatfs {
+ __u64 blocks;
+ __u64 bfree;
+ __u64 bavail;
+ __u64 files;
+ __u64 ffree;
+ __u32 bsize;
+ __u32 namelen;
+ __u32 frsize;
+ __u32 padding;
+ __u32 spare[6];
+};
+
+struct fuse_file_lock {
+ __u64 start;
+ __u64 end;
+ __u32 type;
+ __u32 pid; /* tgid */
+};
+
+/**
+ * Bitmasks for fuse_setattr_in.valid
+ */
+#define FATTR_MODE (1 << 0)
+#define FATTR_UID (1 << 1)
+#define FATTR_GID (1 << 2)
+#define FATTR_SIZE (1 << 3)
+#define FATTR_ATIME (1 << 4)
+#define FATTR_MTIME (1 << 5)
+#define FATTR_FH (1 << 6)
+#define FATTR_ATIME_NOW (1 << 7)
+#define FATTR_MTIME_NOW (1 << 8)
+#define FATTR_LOCKOWNER (1 << 9)
+
+/**
+ * Flags returned by the OPEN request
+ *
+ * FOPEN_DIRECT_IO: bypass page cache for this open file
+ * FOPEN_KEEP_CACHE: don't invalidate the data cache on open
+ * FOPEN_NONSEEKABLE: the file is not seekable
+ */
+#define FOPEN_DIRECT_IO (1 << 0)
+#define FOPEN_KEEP_CACHE (1 << 1)
+#define FOPEN_NONSEEKABLE (1 << 2)
+
+/**
+ * INIT request/reply flags
+ *
+ * FUSE_POSIX_LOCKS: remote locking for POSIX file locks
+ * FUSE_EXPORT_SUPPORT: filesystem handles lookups of "." and ".."
+ * FUSE_DONT_MASK: don't apply umask to file mode on create operations
+ * FUSE_FLOCK_LOCKS: remote locking for BSD style file locks
+ */
+#define FUSE_ASYNC_READ (1 << 0)
+#define FUSE_POSIX_LOCKS (1 << 1)
+#define FUSE_FILE_OPS (1 << 2)
+#define FUSE_ATOMIC_O_TRUNC (1 << 3)
+#define FUSE_EXPORT_SUPPORT (1 << 4)
+#define FUSE_BIG_WRITES (1 << 5)
+#define FUSE_DONT_MASK (1 << 6)
+#define FUSE_FLOCK_LOCKS (1 << 10)
+
+/**
+ * CUSE INIT request/reply flags
+ *
+ * CUSE_UNRESTRICTED_IOCTL: use unrestricted ioctl
+ */
+#define CUSE_UNRESTRICTED_IOCTL (1 << 0)
+
+/**
+ * Release flags
+ */
+#define FUSE_RELEASE_FLUSH (1 << 0)
+#define FUSE_RELEASE_FLOCK_UNLOCK (1 << 1)
+
+/**
+ * Getattr flags
+ */
+#define FUSE_GETATTR_FH (1 << 0)
+
+/**
+ * Lock flags
+ */
+#define FUSE_LK_FLOCK (1 << 0)
+
+/**
+ * WRITE flags
+ *
+ * FUSE_WRITE_CACHE: delayed write from page cache, file handle is guessed
+ * FUSE_WRITE_LOCKOWNER: lock_owner field is valid
+ */
+#define FUSE_WRITE_CACHE (1 << 0)
+#define FUSE_WRITE_LOCKOWNER (1 << 1)
+
+/**
+ * Read flags
+ */
+#define FUSE_READ_LOCKOWNER (1 << 1)
+
+/**
+ * Ioctl flags
+ *
+ * FUSE_IOCTL_COMPAT: 32bit compat ioctl on 64bit machine
+ * FUSE_IOCTL_UNRESTRICTED: not restricted to well-formed ioctls, retry allowed
+ * FUSE_IOCTL_RETRY: retry with new iovecs
+ * FUSE_IOCTL_32BIT: 32bit ioctl
+ * FUSE_IOCTL_DIR: is a directory
+ *
+ * FUSE_IOCTL_MAX_IOV: maximum of in_iovecs + out_iovecs
+ */
+#define FUSE_IOCTL_COMPAT (1 << 0)
+#define FUSE_IOCTL_UNRESTRICTED (1 << 1)
+#define FUSE_IOCTL_RETRY (1 << 2)
+#define FUSE_IOCTL_32BIT (1 << 3)
+#define FUSE_IOCTL_DIR (1 << 4)
+
+#define FUSE_IOCTL_MAX_IOV 256
+
+/**
+ * Poll flags
+ *
+ * FUSE_POLL_SCHEDULE_NOTIFY: request poll notify
+ */
+#define FUSE_POLL_SCHEDULE_NOTIFY (1 << 0)
+
+enum fuse_opcode {
+ FUSE_LOOKUP = 1,
+ FUSE_FORGET = 2, /* no reply */
+ FUSE_GETATTR = 3,
+ FUSE_SETATTR = 4,
+ FUSE_READLINK = 5,
+ FUSE_SYMLINK = 6,
+ FUSE_MKNOD = 8,
+ FUSE_MKDIR = 9,
+ FUSE_UNLINK = 10,
+ FUSE_RMDIR = 11,
+ FUSE_RENAME = 12,
+ FUSE_LINK = 13,
+ FUSE_OPEN = 14,
+ FUSE_READ = 15,
+ FUSE_WRITE = 16,
+ FUSE_STATFS = 17,
+ FUSE_RELEASE = 18,
+ FUSE_FSYNC = 20,
+ FUSE_SETXATTR = 21,
+ FUSE_GETXATTR = 22,
+ FUSE_LISTXATTR = 23,
+ FUSE_REMOVEXATTR = 24,
+ FUSE_FLUSH = 25,
+ FUSE_INIT = 26,
+ FUSE_OPENDIR = 27,
+ FUSE_READDIR = 28,
+ FUSE_RELEASEDIR = 29,
+ FUSE_FSYNCDIR = 30,
+ FUSE_GETLK = 31,
+ FUSE_SETLK = 32,
+ FUSE_SETLKW = 33,
+ FUSE_ACCESS = 34,
+ FUSE_CREATE = 35,
+ FUSE_INTERRUPT = 36,
+ FUSE_BMAP = 37,
+ FUSE_DESTROY = 38,
+ FUSE_IOCTL = 39,
+ FUSE_POLL = 40,
+ FUSE_NOTIFY_REPLY = 41,
+ FUSE_BATCH_FORGET = 42,
+ FUSE_FALLOCATE = 43,
+
+ /* CUSE specific operations */
+ CUSE_INIT = 4096,
+};
+
+enum fuse_notify_code {
+ FUSE_NOTIFY_POLL = 1,
+ FUSE_NOTIFY_INVAL_INODE = 2,
+ FUSE_NOTIFY_INVAL_ENTRY = 3,
+ FUSE_NOTIFY_STORE = 4,
+ FUSE_NOTIFY_RETRIEVE = 5,
+ FUSE_NOTIFY_DELETE = 6,
+ FUSE_NOTIFY_CODE_MAX,
+};
+
+/* The read buffer is required to be at least 8k, but may be much larger */
+#define FUSE_MIN_READ_BUFFER 8192
+
+#define FUSE_COMPAT_ENTRY_OUT_SIZE 120
+
+struct fuse_entry_out {
+ __u64 nodeid; /* Inode ID */
+ __u64 generation; /* Inode generation: nodeid:gen must
+ be unique for the fs's lifetime */
+ __u64 entry_valid; /* Cache timeout for the name */
+ __u64 attr_valid; /* Cache timeout for the attributes */
+ __u32 entry_valid_nsec;
+ __u32 attr_valid_nsec;
+ struct fuse_attr attr;
+};
+
+struct fuse_forget_in {
+ __u64 nlookup;
+};
+
+struct fuse_forget_one {
+ __u64 nodeid;
+ __u64 nlookup;
+};
+
+struct fuse_batch_forget_in {
+ __u32 count;
+ __u32 dummy;
+};
+
+struct fuse_getattr_in {
+ __u32 getattr_flags;
+ __u32 dummy;
+ __u64 fh;
+};
+
+#define FUSE_COMPAT_ATTR_OUT_SIZE 96
+
+struct fuse_attr_out {
+ __u64 attr_valid; /* Cache timeout for the attributes */
+ __u32 attr_valid_nsec;
+ __u32 dummy;
+ struct fuse_attr attr;
+};
+
+#define FUSE_COMPAT_MKNOD_IN_SIZE 8
+
+struct fuse_mknod_in {
+ __u32 mode;
+ __u32 rdev;
+ __u32 umask;
+ __u32 padding;
+};
+
+struct fuse_mkdir_in {
+ __u32 mode;
+ __u32 umask;
+};
+
+struct fuse_rename_in {
+ __u64 newdir;
+};
+
+struct fuse_link_in {
+ __u64 oldnodeid;
+};
+
+struct fuse_setattr_in {
+ __u32 valid;
+ __u32 padding;
+ __u64 fh;
+ __u64 size;
+ __u64 lock_owner;
+ __u64 atime;
+ __u64 mtime;
+ __u64 unused2;
+ __u32 atimensec;
+ __u32 mtimensec;
+ __u32 unused3;
+ __u32 mode;
+ __u32 unused4;
+ __u32 uid;
+ __u32 gid;
+ __u32 unused5;
+};
+
+struct fuse_open_in {
+ __u32 flags;
+ __u32 unused;
+};
+
+struct fuse_create_in {
+ __u32 flags;
+ __u32 mode;
+ __u32 umask;
+ __u32 padding;
+};
+
+struct fuse_open_out {
+ __u64 fh;
+ __u32 open_flags;
+ __u32 padding;
+};
+
+struct fuse_release_in {
+ __u64 fh;
+ __u32 flags;
+ __u32 release_flags;
+ __u64 lock_owner;
+};
+
+struct fuse_flush_in {
+ __u64 fh;
+ __u32 unused;
+ __u32 padding;
+ __u64 lock_owner;
+};
+
+struct fuse_read_in {
+ __u64 fh;
+ __u64 offset;
+ __u32 size;
+ __u32 read_flags;
+ __u64 lock_owner;
+ __u32 flags;
+ __u32 padding;
+};
+
+#define FUSE_COMPAT_WRITE_IN_SIZE 24
+
+struct fuse_write_in {
+ __u64 fh;
+ __u64 offset;
+ __u32 size;
+ __u32 write_flags;
+ __u64 lock_owner;
+ __u32 flags;
+ __u32 padding;
+};
+
+struct fuse_write_out {
+ __u32 size;
+ __u32 padding;
+};
+
+#define FUSE_COMPAT_STATFS_SIZE 48
+
+struct fuse_statfs_out {
+ struct fuse_kstatfs st;
+};
+
+struct fuse_fsync_in {
+ __u64 fh;
+ __u32 fsync_flags;
+ __u32 padding;
+};
+
+struct fuse_setxattr_in {
+ __u32 size;
+ __u32 flags;
+};
+
+struct fuse_getxattr_in {
+ __u32 size;
+ __u32 padding;
+};
+
+struct fuse_getxattr_out {
+ __u32 size;
+ __u32 padding;
+};
+
+struct fuse_lk_in {
+ __u64 fh;
+ __u64 owner;
+ struct fuse_file_lock lk;
+ __u32 lk_flags;
+ __u32 padding;
+};
+
+struct fuse_lk_out {
+ struct fuse_file_lock lk;
+};
+
+struct fuse_access_in {
+ __u32 mask;
+ __u32 padding;
+};
+
+struct fuse_init_in {
+ __u32 major;
+ __u32 minor;
+ __u32 max_readahead;
+ __u32 flags;
+};
+
+struct fuse_init_out {
+ __u32 major;
+ __u32 minor;
+ __u32 max_readahead;
+ __u32 flags;
+ __u16 max_background;
+ __u16 congestion_threshold;
+ __u32 max_write;
+};
+
+#define CUSE_INIT_INFO_MAX 4096
+
+struct cuse_init_in {
+ __u32 major;
+ __u32 minor;
+ __u32 unused;
+ __u32 flags;
+};
+
+struct cuse_init_out {
+ __u32 major;
+ __u32 minor;
+ __u32 unused;
+ __u32 flags;
+ __u32 max_read;
+ __u32 max_write;
+ __u32 dev_major; /* chardev major */
+ __u32 dev_minor; /* chardev minor */
+ __u32 spare[10];
+};
+
+struct fuse_interrupt_in {
+ __u64 unique;
+};
+
+struct fuse_bmap_in {
+ __u64 block;
+ __u32 blocksize;
+ __u32 padding;
+};
+
+struct fuse_bmap_out {
+ __u64 block;
+};
+
+struct fuse_ioctl_in {
+ __u64 fh;
+ __u32 flags;
+ __u32 cmd;
+ __u64 arg;
+ __u32 in_size;
+ __u32 out_size;
+};
+
+struct fuse_ioctl_iovec {
+ __u64 base;
+ __u64 len;
+};
+
+struct fuse_ioctl_out {
+ __s32 result;
+ __u32 flags;
+ __u32 in_iovs;
+ __u32 out_iovs;
+};
+
+struct fuse_poll_in {
+ __u64 fh;
+ __u64 kh;
+ __u32 flags;
+ __u32 padding;
+};
+
+struct fuse_poll_out {
+ __u32 revents;
+ __u32 padding;
+};
+
+struct fuse_notify_poll_wakeup_out {
+ __u64 kh;
+};
+
+struct fuse_fallocate_in {
+ __u64 fh;
+ __u64 offset;
+ __u64 length;
+ __u32 mode;
+ __u32 padding;
+};
+
+struct fuse_in_header {
+ __u32 len;
+ __u32 opcode;
+ __u64 unique;
+ __u64 nodeid;
+ __u32 uid;
+ __u32 gid;
+ __u32 pid;
+ __u32 padding;
+};
+
+struct fuse_out_header {
+ __u32 len;
+ __s32 error;
+ __u64 unique;
+};
+
+struct fuse_dirent {
+ __u64 ino;
+ __u64 off;
+ __u32 namelen;
+ __u32 type;
+ char name[];
+};
+
+#define FUSE_NAME_OFFSET offsetof(struct fuse_dirent, name)
+#define FUSE_DIRENT_ALIGN(x) (((x) + sizeof(__u64) - 1) & ~(sizeof(__u64) - 1))
+#define FUSE_DIRENT_SIZE(d) \
+ FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + (d)->namelen)
+
+struct fuse_notify_inval_inode_out {
+ __u64 ino;
+ __s64 off;
+ __s64 len;
+};
+
+struct fuse_notify_inval_entry_out {
+ __u64 parent;
+ __u32 namelen;
+ __u32 padding;
+};
+
+struct fuse_notify_delete_out {
+ __u64 parent;
+ __u64 child;
+ __u32 namelen;
+ __u32 padding;
+};
+
+struct fuse_notify_store_out {
+ __u64 nodeid;
+ __u64 offset;
+ __u32 size;
+ __u32 padding;
+};
+
+struct fuse_notify_retrieve_out {
+ __u64 notify_unique;
+ __u64 nodeid;
+ __u64 offset;
+ __u32 size;
+ __u32 padding;
+};
+
+/* Matches the size of fuse_write_in */
+struct fuse_notify_retrieve_in {
+ __u64 dummy1;
+ __u64 offset;
+ __u32 size;
+ __u32 dummy2;
+ __u64 dummy3;
+ __u64 dummy4;
+};
+
+#endif /* _LINUX_FUSE_H */
diff --git a/fuse/include/fuse_lowlevel.h b/fuse/include/fuse_lowlevel.h
new file mode 100644
index 000000000..36cf26ddf
--- /dev/null
+++ b/fuse/include/fuse_lowlevel.h
@@ -0,0 +1,1837 @@
+/*
+ FUSE: Filesystem in Userspace
+ Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
+
+ This program can be distributed under the terms of the GNU LGPLv2.
+ See the file COPYING.LIB.
+*/
+
+#ifndef _FUSE_LOWLEVEL_H_
+#define _FUSE_LOWLEVEL_H_
+
+/** @file
+ *
+ * Low level API
+ *
+ * IMPORTANT: you should define FUSE_USE_VERSION before including this
+ * header. To use the newest API define it to 26 (recommended for any
+ * new application), to use the old API define it to 24 (default) or
+ * 25
+ */
+
+#ifndef FUSE_USE_VERSION
+#define FUSE_USE_VERSION 24
+#endif
+
+#include "fuse_common.h"
+
+#include <utime.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <sys/uio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* ----------------------------------------------------------- *
+ * Miscellaneous definitions *
+ * ----------------------------------------------------------- */
+
+/** The node ID of the root inode */
+#define FUSE_ROOT_ID 1
+
+/** Inode number type */
+typedef unsigned long fuse_ino_t;
+
+/** Request pointer type */
+typedef struct fuse_req *fuse_req_t;
+
+/**
+ * Session
+ *
+ * This provides hooks for processing requests, and exiting
+ */
+struct fuse_session;
+
+/**
+ * Channel
+ *
+ * A communication channel, providing hooks for sending and receiving
+ * messages
+ */
+struct fuse_chan;
+
+/** Directory entry parameters supplied to fuse_reply_entry() */
+struct fuse_entry_param {
+ /** Unique inode number
+ *
+ * In lookup, zero means negative entry (from version 2.5)
+ * Returning ENOENT also means negative entry, but by setting zero
+ * ino the kernel may cache negative entries for entry_timeout
+ * seconds.
+ */
+ fuse_ino_t ino;
+
+ /** Generation number for this entry.
+ *
+ * If the file system will be exported over NFS, the
+ * ino/generation pairs need to be unique over the file
+ * system's lifetime (rather than just the mount time). So if
+ * the file system reuses an inode after it has been deleted,
+ * it must assign a new, previously unused generation number
+ * to the inode at the same time.
+ *
+ * The generation must be non-zero, otherwise FUSE will treat
+ * it as an error.
+ *
+ */
+ unsigned long generation;
+
+ /** Inode attributes.
+ *
+ * Even if attr_timeout == 0, attr must be correct. For example,
+ * for open(), FUSE uses attr.st_size from lookup() to determine
+ * how many bytes to request. If this value is not correct,
+ * incorrect data will be returned.
+ */
+ struct stat attr;
+
+ /** Validity timeout (in seconds) for the attributes */
+ double attr_timeout;
+
+ /** Validity timeout (in seconds) for the name */
+ double entry_timeout;
+};
+
+/** Additional context associated with requests */
+struct fuse_ctx {
+ /** User ID of the calling process */
+ uid_t uid;
+
+ /** Group ID of the calling process */
+ gid_t gid;
+
+ /** Thread ID of the calling process */
+ pid_t pid;
+
+ /** Umask of the calling process (introduced in version 2.8) */
+ mode_t umask;
+};
+
+struct fuse_forget_data {
+ uint64_t ino;
+ uint64_t nlookup;
+};
+
+/* 'to_set' flags in setattr */
+#define FUSE_SET_ATTR_MODE (1 << 0)
+#define FUSE_SET_ATTR_UID (1 << 1)
+#define FUSE_SET_ATTR_GID (1 << 2)
+#define FUSE_SET_ATTR_SIZE (1 << 3)
+#define FUSE_SET_ATTR_ATIME (1 << 4)
+#define FUSE_SET_ATTR_MTIME (1 << 5)
+#define FUSE_SET_ATTR_ATIME_NOW (1 << 7)
+#define FUSE_SET_ATTR_MTIME_NOW (1 << 8)
+
+/* ----------------------------------------------------------- *
+ * Request methods and replies *
+ * ----------------------------------------------------------- */
+
+/**
+ * Low level filesystem operations
+ *
+ * Most of the methods (with the exception of init and destroy)
+ * receive a request handle (fuse_req_t) as their first argument.
+ * This handle must be passed to one of the specified reply functions.
+ *
+ * This may be done inside the method invocation, or after the call
+ * has returned. The request handle is valid until one of the reply
+ * functions is called.
+ *
+ * Other pointer arguments (name, fuse_file_info, etc) are not valid
+ * after the call has returned, so if they are needed later, their
+ * contents have to be copied.
+ *
+ * The filesystem sometimes needs to handle a return value of -ENOENT
+ * from the reply function, which means, that the request was
+ * interrupted, and the reply discarded. For example if
+ * fuse_reply_open() return -ENOENT means, that the release method for
+ * this file will not be called.
+ */
+struct fuse_lowlevel_ops {
+ /**
+ * Initialize filesystem
+ *
+ * Called before any other filesystem method
+ *
+ * There's no reply to this function
+ *
+ * @param userdata the user data passed to fuse_lowlevel_new()
+ */
+ void (*init) (void *userdata, struct fuse_conn_info *conn);
+
+ /**
+ * Clean up filesystem
+ *
+ * Called on filesystem exit
+ *
+ * There's no reply to this function
+ *
+ * @param userdata the user data passed to fuse_lowlevel_new()
+ */
+ void (*destroy) (void *userdata);
+
+ /**
+ * Look up a directory entry by name and get its attributes.
+ *
+ * Valid replies:
+ * fuse_reply_entry
+ * fuse_reply_err
+ *
+ * @param req request handle
+ * @param parent inode number of the parent directory
+ * @param name the name to look up
+ */
+ void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name);
+
+ /**
+ * Forget about an inode
+ *
+ * This function is called when the kernel removes an inode
+ * from its internal caches.
+ *
+ * The inode's lookup count increases by one for every call to
+ * fuse_reply_entry and fuse_reply_create. The nlookup parameter
+ * indicates by how much the lookup count should be decreased.
+ *
+ * Inodes with a non-zero lookup count may receive request from
+ * the kernel even after calls to unlink, rmdir or (when
+ * overwriting an existing file) rename. Filesystems must handle
+ * such requests properly and it is recommended to defer removal
+ * of the inode until the lookup count reaches zero. Calls to
+ * unlink, remdir or rename will be followed closely by forget
+ * unless the file or directory is open, in which case the
+ * kernel issues forget only after the release or releasedir
+ * calls.
+ *
+ * Note that if a file system will be exported over NFS the
+ * inodes lifetime must extend even beyond forget. See the
+ * generation field in struct fuse_entry_param above.
+ *
+ * On unmount the lookup count for all inodes implicitly drops
+ * to zero. It is not guaranteed that the file system will
+ * receive corresponding forget messages for the affected
+ * inodes.
+ *
+ * Valid replies:
+ * fuse_reply_none
+ *
+ * @param req request handle
+ * @param ino the inode number
+ * @param nlookup the number of lookups to forget
+ */
+ void (*forget) (fuse_req_t req, fuse_ino_t ino, unsigned long nlookup);
+
+ /**
+ * Get file attributes
+ *
+ * Valid replies:
+ * fuse_reply_attr
+ * fuse_reply_err
+ *
+ * @param req request handle
+ * @param ino the inode number
+ * @param fi for future use, currently always NULL
+ */
+ void (*getattr) (fuse_req_t req, fuse_ino_t ino,
+ struct fuse_file_info *fi);
+
+ /**
+ * Set file attributes
+ *
+ * In the 'attr' argument only members indicated by the 'to_set'
+ * bitmask contain valid values. Other members contain undefined
+ * values.
+ *
+ * If the setattr was invoked from the ftruncate() system call
+ * under Linux kernel versions 2.6.15 or later, the fi->fh will
+ * contain the value set by the open method or will be undefined
+ * if the open method didn't set any value. Otherwise (not
+ * ftruncate call, or kernel version earlier than 2.6.15) the fi
+ * parameter will be NULL.
+ *
+ * Valid replies:
+ * fuse_reply_attr
+ * fuse_reply_err
+ *
+ * @param req request handle
+ * @param ino the inode number
+ * @param attr the attributes
+ * @param to_set bit mask of attributes which should be set
+ * @param fi file information, or NULL
+ *
+ * Changed in version 2.5:
+ * file information filled in for ftruncate
+ */
+ void (*setattr) (fuse_req_t req, fuse_ino_t ino, struct stat *attr,
+ int to_set, struct fuse_file_info *fi);
+
+ /**
+ * Read symbolic link
+ *
+ * Valid replies:
+ * fuse_reply_readlink
+ * fuse_reply_err
+ *
+ * @param req request handle
+ * @param ino the inode number
+ */
+ void (*readlink) (fuse_req_t req, fuse_ino_t ino);
+
+ /**
+ * Create file node
+ *
+ * Create a regular file, character device, block device, fifo or
+ * socket node.
+ *
+ * Valid replies:
+ * fuse_reply_entry
+ * fuse_reply_err
+ *
+ * @param req request handle
+ * @param parent inode number of the parent directory
+ * @param name to create
+ * @param mode file type and mode with which to create the new file
+ * @param rdev the device number (only valid if created file is a device)
+ */
+ void (*mknod) (fuse_req_t req, fuse_ino_t parent, const char *name,
+ mode_t mode, dev_t rdev);
+
+ /**
+ * Create a directory
+ *
+ * Valid replies:
+ * fuse_reply_entry
+ * fuse_reply_err
+ *
+ * @param req request handle
+ * @param parent inode number of the parent directory
+ * @param name to create
+ * @param mode with which to create the new file
+ */
+ void (*mkdir) (fuse_req_t req, fuse_ino_t parent, const char *name,
+ mode_t mode);
+
+ /**
+ * Remove a file
+ *
+ * If the file's inode's lookup count is non-zero, the file
+ * system is expected to postpone any removal of the inode
+ * until the lookup count reaches zero (see description of the
+ * forget function).
+ *
+ * Valid replies:
+ * fuse_reply_err
+ *
+ * @param req request handle
+ * @param parent inode number of the parent directory
+ * @param name to remove
+ */
+ void (*unlink) (fuse_req_t req, fuse_ino_t parent, const char *name);
+
+ /**
+ * Remove a directory
+ *
+ * If the directory's inode's lookup count is non-zero, the
+ * file system is expected to postpone any removal of the
+ * inode until the lookup count reaches zero (see description
+ * of the forget function).
+ *
+ * Valid replies:
+ * fuse_reply_err
+ *
+ * @param req request handle
+ * @param parent inode number of the parent directory
+ * @param name to remove
+ */
+ void (*rmdir) (fuse_req_t req, fuse_ino_t parent, const char *name);
+
+ /**
+ * Create a symbolic link
+ *
+ * Valid replies:
+ * fuse_reply_entry
+ * fuse_reply_err
+ *
+ * @param req request handle
+ * @param link the contents of the symbolic link
+ * @param parent inode number of the parent directory
+ * @param name to create
+ */
+ void (*symlink) (fuse_req_t req, const char *link, fuse_ino_t parent,
+ const char *name);
+
+ /** Rename a file
+ *
+ * If the target exists it should be atomically replaced. If
+ * the target's inode's lookup count is non-zero, the file
+ * system is expected to postpone any removal of the inode
+ * until the lookup count reaches zero (see description of the
+ * forget function).
+ *
+ * Valid replies:
+ * fuse_reply_err
+ *
+ * @param req request handle
+ * @param parent inode number of the old parent directory
+ * @param name old name
+ * @param newparent inode number of the new parent directory
+ * @param newname new name
+ */
+ void (*rename) (fuse_req_t req, fuse_ino_t parent, const char *name,
+ fuse_ino_t newparent, const char *newname);
+
+ /**
+ * Create a hard link
+ *
+ * Valid replies:
+ * fuse_reply_entry
+ * fuse_reply_err
+ *
+ * @param req request handle
+ * @param ino the old inode number
+ * @param newparent inode number of the new parent directory
+ * @param newname new name to create
+ */
+ void (*link) (fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent,
+ const char *newname);
+
+ /**
+ * Open a file
+ *
+ * Open flags (with the exception of O_CREAT, O_EXCL, O_NOCTTY and
+ * O_TRUNC) are available in fi->flags.
+ *
+ * Filesystem may store an arbitrary file handle (pointer, index,
+ * etc) in fi->fh, and use this in other all other file operations
+ * (read, write, flush, release, fsync).
+ *
+ * Filesystem may also implement stateless file I/O and not store
+ * anything in fi->fh.
+ *
+ * There are also some flags (direct_io, keep_cache) which the
+ * filesystem may set in fi, to change the way the file is opened.
+ * See fuse_file_info structure in <fuse_common.h> for more details.
+ *
+ * Valid replies:
+ * fuse_reply_open
+ * fuse_reply_err
+ *
+ * @param req request handle
+ * @param ino the inode number
+ * @param fi file information
+ */
+ void (*open) (fuse_req_t req, fuse_ino_t ino,
+ struct fuse_file_info *fi);
+
+ /**
+ * Read data
+ *
+ * Read should send exactly the number of bytes requested except
+ * on EOF or error, otherwise the rest of the data will be
+ * substituted with zeroes. An exception to this is when the file
+ * has been opened in 'direct_io' mode, in which case the return
+ * value of the read system call will reflect the return value of
+ * this operation.
+ *
+ * fi->fh will contain the value set by the open method, or will
+ * be undefined if the open method didn't set any value.
+ *
+ * Valid replies:
+ * fuse_reply_buf
+ * fuse_reply_iov
+ * fuse_reply_data
+ * fuse_reply_err
+ *
+ * @param req request handle
+ * @param ino the inode number
+ * @param size number of bytes to read
+ * @param off offset to read from
+ * @param fi file information
+ */
+ void (*read) (fuse_req_t req, fuse_ino_t ino, size_t size, off64_t off,
+ struct fuse_file_info *fi);
+
+ /**
+ * Write data
+ *
+ * Write should return exactly the number of bytes requested
+ * except on error. An exception to this is when the file has
+ * been opened in 'direct_io' mode, in which case the return value
+ * of the write system call will reflect the return value of this
+ * operation.
+ *
+ * fi->fh will contain the value set by the open method, or will
+ * be undefined if the open method didn't set any value.
+ *
+ * Valid replies:
+ * fuse_reply_write
+ * fuse_reply_err
+ *
+ * @param req request handle
+ * @param ino the inode number
+ * @param buf data to write
+ * @param size number of bytes to write
+ * @param off offset to write to
+ * @param fi file information
+ */
+ void (*write) (fuse_req_t req, fuse_ino_t ino, const char *buf,
+ size_t size, off64_t off, struct fuse_file_info *fi);
+
+ /**
+ * Flush method
+ *
+ * This is called on each close() of the opened file.
+ *
+ * Since file descriptors can be duplicated (dup, dup2, fork), for
+ * one open call there may be many flush calls.
+ *
+ * Filesystems shouldn't assume that flush will always be called
+ * after some writes, or that if will be called at all.
+ *
+ * fi->fh will contain the value set by the open method, or will
+ * be undefined if the open method didn't set any value.
+ *
+ * NOTE: the name of the method is misleading, since (unlike
+ * fsync) the filesystem is not forced to flush pending writes.
+ * One reason to flush data, is if the filesystem wants to return
+ * write errors.
+ *
+ * If the filesystem supports file locking operations (setlk,
+ * getlk) it should remove all locks belonging to 'fi->owner'.
+ *
+ * Valid replies:
+ * fuse_reply_err
+ *
+ * @param req request handle
+ * @param ino the inode number
+ * @param fi file information
+ */
+ void (*flush) (fuse_req_t req, fuse_ino_t ino,
+ struct fuse_file_info *fi);
+
+ /**
+ * Release an open file
+ *
+ * Release is called when there are no more references to an open
+ * file: all file descriptors are closed and all memory mappings
+ * are unmapped.
+ *
+ * For every open call there will be exactly one release call.
+ *
+ * The filesystem may reply with an error, but error values are
+ * not returned to close() or munmap() which triggered the
+ * release.
+ *
+ * fi->fh will contain the value set by the open method, or will
+ * be undefined if the open method didn't set any value.
+ * fi->flags will contain the same flags as for open.
+ *
+ * Valid replies:
+ * fuse_reply_err
+ *
+ * @param req request handle
+ * @param ino the inode number
+ * @param fi file information
+ */
+ void (*release) (fuse_req_t req, fuse_ino_t ino,
+ struct fuse_file_info *fi);
+
+ /**
+ * Synchronize file contents
+ *
+ * If the datasync parameter is non-zero, then only the user data
+ * should be flushed, not the meta data.
+ *
+ * Valid replies:
+ * fuse_reply_err
+ *
+ * @param req request handle
+ * @param ino the inode number
+ * @param datasync flag indicating if only data should be flushed
+ * @param fi file information
+ */
+ void (*fsync) (fuse_req_t req, fuse_ino_t ino, int datasync,
+ struct fuse_file_info *fi);
+
+ /**
+ * Open a directory
+ *
+ * Filesystem may store an arbitrary file handle (pointer, index,
+ * etc) in fi->fh, and use this in other all other directory
+ * stream operations (readdir, releasedir, fsyncdir).
+ *
+ * Filesystem may also implement stateless directory I/O and not
+ * store anything in fi->fh, though that makes it impossible to
+ * implement standard conforming directory stream operations in
+ * case the contents of the directory can change between opendir
+ * and releasedir.
+ *
+ * Valid replies:
+ * fuse_reply_open
+ * fuse_reply_err
+ *
+ * @param req request handle
+ * @param ino the inode number
+ * @param fi file information
+ */
+ void (*opendir) (fuse_req_t req, fuse_ino_t ino,
+ struct fuse_file_info *fi);
+
+ /**
+ * Read directory
+ *
+ * Send a buffer filled using fuse_add_direntry(), with size not
+ * exceeding the requested size. Send an empty buffer on end of
+ * stream.
+ *
+ * fi->fh will contain the value set by the opendir method, or
+ * will be undefined if the opendir method didn't set any value.
+ *
+ * Valid replies:
+ * fuse_reply_buf
+ * fuse_reply_data
+ * fuse_reply_err
+ *
+ * @param req request handle
+ * @param ino the inode number
+ * @param size maximum number of bytes to send
+ * @param off offset to continue reading the directory stream
+ * @param fi file information
+ */
+ void (*readdir) (fuse_req_t req, fuse_ino_t ino, size_t size, off64_t off,
+ struct fuse_file_info *fi);
+
+ /**
+ * Release an open directory
+ *
+ * For every opendir call there will be exactly one releasedir
+ * call.
+ *
+ * fi->fh will contain the value set by the opendir method, or
+ * will be undefined if the opendir method didn't set any value.
+ *
+ * Valid replies:
+ * fuse_reply_err
+ *
+ * @param req request handle
+ * @param ino the inode number
+ * @param fi file information
+ */
+ void (*releasedir) (fuse_req_t req, fuse_ino_t ino,
+ struct fuse_file_info *fi);
+
+ /**
+ * Synchronize directory contents
+ *
+ * If the datasync parameter is non-zero, then only the directory
+ * contents should be flushed, not the meta data.
+ *
+ * fi->fh will contain the value set by the opendir method, or
+ * will be undefined if the opendir method didn't set any value.
+ *
+ * Valid replies:
+ * fuse_reply_err
+ *
+ * @param req request handle
+ * @param ino the inode number
+ * @param datasync flag indicating if only data should be flushed
+ * @param fi file information
+ */
+ void (*fsyncdir) (fuse_req_t req, fuse_ino_t ino, int datasync,
+ struct fuse_file_info *fi);
+
+ /**
+ * Get file system statistics
+ *
+ * Valid replies:
+ * fuse_reply_statfs
+ * fuse_reply_err
+ *
+ * @param req request handle
+ * @param ino the inode number, zero means "undefined"
+ */
+ void (*statfs) (fuse_req_t req, fuse_ino_t ino);
+
+ /**
+ * Set an extended attribute
+ *
+ * Valid replies:
+ * fuse_reply_err
+ */
+ void (*setxattr) (fuse_req_t req, fuse_ino_t ino, const char *name,
+ const char *value, size_t size, int flags);
+
+ /**
+ * Get an extended attribute
+ *
+ * If size is zero, the size of the value should be sent with
+ * fuse_reply_xattr.
+ *
+ * If the size is non-zero, and the value fits in the buffer, the
+ * value should be sent with fuse_reply_buf.
+ *
+ * If the size is too small for the value, the ERANGE error should
+ * be sent.
+ *
+ * Valid replies:
+ * fuse_reply_buf
+ * fuse_reply_data
+ * fuse_reply_xattr
+ * fuse_reply_err
+ *
+ * @param req request handle
+ * @param ino the inode number
+ * @param name of the extended attribute
+ * @param size maximum size of the value to send
+ */
+ void (*getxattr) (fuse_req_t req, fuse_ino_t ino, const char *name,
+ size_t size);
+
+ /**
+ * List extended attribute names
+ *
+ * If size is zero, the total size of the attribute list should be
+ * sent with fuse_reply_xattr.
+ *
+ * If the size is non-zero, and the null character separated
+ * attribute list fits in the buffer, the list should be sent with
+ * fuse_reply_buf.
+ *
+ * If the size is too small for the list, the ERANGE error should
+ * be sent.
+ *
+ * Valid replies:
+ * fuse_reply_buf
+ * fuse_reply_data
+ * fuse_reply_xattr
+ * fuse_reply_err
+ *
+ * @param req request handle
+ * @param ino the inode number
+ * @param size maximum size of the list to send
+ */
+ void (*listxattr) (fuse_req_t req, fuse_ino_t ino, size_t size);
+
+ /**
+ * Remove an extended attribute
+ *
+ * Valid replies:
+ * fuse_reply_err
+ *
+ * @param req request handle
+ * @param ino the inode number
+ * @param name of the extended attribute
+ */
+ void (*removexattr) (fuse_req_t req, fuse_ino_t ino, const char *name);
+
+ /**
+ * Check file access permissions
+ *
+ * This will be called for the access() system call. If the
+ * 'default_permissions' mount option is given, this method is not
+ * called.
+ *
+ * This method is not called under Linux kernel versions 2.4.x
+ *
+ * Introduced in version 2.5
+ *
+ * Valid replies:
+ * fuse_reply_err
+ *
+ * @param req request handle
+ * @param ino the inode number
+ * @param mask requested access mode
+ */
+ void (*access) (fuse_req_t req, fuse_ino_t ino, int mask);
+
+ /**
+ * Create and open a file
+ *
+ * If the file does not exist, first create it with the specified
+ * mode, and then open it.
+ *
+ * Open flags (with the exception of O_NOCTTY) are available in
+ * fi->flags.
+ *
+ * Filesystem may store an arbitrary file handle (pointer, index,
+ * etc) in fi->fh, and use this in other all other file operations
+ * (read, write, flush, release, fsync).
+ *
+ * There are also some flags (direct_io, keep_cache) which the
+ * filesystem may set in fi, to change the way the file is opened.
+ * See fuse_file_info structure in <fuse_common.h> for more details.
+ *
+ * If this method is not implemented or under Linux kernel
+ * versions earlier than 2.6.15, the mknod() and open() methods
+ * will be called instead.
+ *
+ * Introduced in version 2.5
+ *
+ * Valid replies:
+ * fuse_reply_create
+ * fuse_reply_err
+ *
+ * @param req request handle
+ * @param parent inode number of the parent directory
+ * @param name to create
+ * @param mode file type and mode with which to create the new file
+ * @param fi file information
+ */
+ void (*create) (fuse_req_t req, fuse_ino_t parent, const char *name,
+ mode_t mode, struct fuse_file_info *fi);
+
+ /**
+ * Test for a POSIX file lock
+ *
+ * Introduced in version 2.6
+ *
+ * Valid replies:
+ * fuse_reply_lock
+ * fuse_reply_err
+ *
+ * @param req request handle
+ * @param ino the inode number
+ * @param fi file information
+ * @param lock the region/type to test
+ */
+ void (*getlk) (fuse_req_t req, fuse_ino_t ino,
+ struct fuse_file_info *fi, struct flock *lock);
+
+ /**
+ * Acquire, modify or release a POSIX file lock
+ *
+ * For POSIX threads (NPTL) there's a 1-1 relation between pid and
+ * owner, but otherwise this is not always the case. For checking
+ * lock ownership, 'fi->owner' must be used. The l_pid field in
+ * 'struct flock' should only be used to fill in this field in
+ * getlk().
+ *
+ * Note: if the locking methods are not implemented, the kernel
+ * will still allow file locking to work locally. Hence these are
+ * only interesting for network filesystems and similar.
+ *
+ * Introduced in version 2.6
+ *
+ * Valid replies:
+ * fuse_reply_err
+ *
+ * @param req request handle
+ * @param ino the inode number
+ * @param fi file information
+ * @param lock the region/type to set
+ * @param sleep locking operation may sleep
+ */
+ void (*setlk) (fuse_req_t req, fuse_ino_t ino,
+ struct fuse_file_info *fi,
+ struct flock *lock, int sleep);
+
+ /**
+ * Map block index within file to block index within device
+ *
+ * Note: This makes sense only for block device backed filesystems
+ * mounted with the 'blkdev' option
+ *
+ * Introduced in version 2.6
+ *
+ * Valid replies:
+ * fuse_reply_bmap
+ * fuse_reply_err
+ *
+ * @param req request handle
+ * @param ino the inode number
+ * @param blocksize unit of block index
+ * @param idx block index within file
+ */
+ void (*bmap) (fuse_req_t req, fuse_ino_t ino, size_t blocksize,
+ uint64_t idx);
+
+ /**
+ * Ioctl
+ *
+ * Note: For unrestricted ioctls (not allowed for FUSE
+ * servers), data in and out areas can be discovered by giving
+ * iovs and setting FUSE_IOCTL_RETRY in @flags. For
+ * restricted ioctls, kernel prepares in/out data area
+ * according to the information encoded in cmd.
+ *
+ * Introduced in version 2.8
+ *
+ * Valid replies:
+ * fuse_reply_ioctl_retry
+ * fuse_reply_ioctl
+ * fuse_reply_ioctl_iov
+ * fuse_reply_err
+ *
+ * @param req request handle
+ * @param ino the inode number
+ * @param cmd ioctl command
+ * @param arg ioctl argument
+ * @param fi file information
+ * @param flags for FUSE_IOCTL_* flags
+ * @param in_buf data fetched from the caller
+ * @param in_bufsz number of fetched bytes
+ * @param out_bufsz maximum size of output data
+ */
+ void (*ioctl) (fuse_req_t req, fuse_ino_t ino, int cmd, void *arg,
+ struct fuse_file_info *fi, unsigned flags,
+ const void *in_buf, size_t in_bufsz, size_t out_bufsz);
+
+ /**
+ * Poll for IO readiness
+ *
+ * Introduced in version 2.8
+ *
+ * Note: If ph is non-NULL, the client should notify
+ * when IO readiness events occur by calling
+ * fuse_lowelevel_notify_poll() with the specified ph.
+ *
+ * Regardless of the number of times poll with a non-NULL ph
+ * is received, single notification is enough to clear all.
+ * Notifying more times incurs overhead but doesn't harm
+ * correctness.
+ *
+ * The callee is responsible for destroying ph with
+ * fuse_pollhandle_destroy() when no longer in use.
+ *
+ * Valid replies:
+ * fuse_reply_poll
+ * fuse_reply_err
+ *
+ * @param req request handle
+ * @param ino the inode number
+ * @param fi file information
+ * @param ph poll handle to be used for notification
+ */
+ void (*poll) (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi,
+ struct fuse_pollhandle *ph);
+
+ /**
+ * Write data made available in a buffer
+ *
+ * This is a more generic version of the ->write() method. If
+ * FUSE_CAP_SPLICE_READ is set in fuse_conn_info.want and the
+ * kernel supports splicing from the fuse device, then the
+ * data will be made available in pipe for supporting zero
+ * copy data transfer.
+ *
+ * Introduced in version 2.9
+ *
+ * Valid replies:
+ * fuse_reply_write
+ * fuse_reply_err
+ *
+ * @param req request handle
+ * @param ino the inode number
+ * @param bufv buffer containing the data
+ * @param off offset to write to
+ * @param fi file information
+ */
+ void (*write_buf) (fuse_req_t req, fuse_ino_t ino,
+ struct fuse_bufvec *bufv, off64_t off,
+ struct fuse_file_info *fi);
+
+ /**
+ * Callback function for the retrieve request
+ *
+ * Introduced in version 2.9
+ *
+ * Valid replies:
+ * fuse_reply_none
+ *
+ * @param req request handle
+ * @param cookie user data supplied to fuse_lowlevel_notify_retrieve()
+ * @param ino the inode number supplied to fuse_lowlevel_notify_retrieve()
+ * @param offset the offset supplied to fuse_lowlevel_notify_retrieve()
+ * @param bufv the buffer containing the returned data
+ */
+ void (*retrieve_reply) (fuse_req_t req, void *cookie, fuse_ino_t ino,
+ off64_t offset, struct fuse_bufvec *bufv);
+
+ /**
+ * Forget about multiple inodes
+ *
+ * See description of the forget function for more
+ * information.
+ *
+ * Introduced in version 2.9
+ *
+ * Valid replies:
+ * fuse_reply_none
+ *
+ * @param req request handle
+ */
+ void (*forget_multi) (fuse_req_t req, size_t count,
+ struct fuse_forget_data *forgets);
+
+ /**
+ * Acquire, modify or release a BSD file lock
+ *
+ * Note: if the locking methods are not implemented, the kernel
+ * will still allow file locking to work locally. Hence these are
+ * only interesting for network filesystems and similar.
+ *
+ * Introduced in version 2.9
+ *
+ * Valid replies:
+ * fuse_reply_err
+ *
+ * @param req request handle
+ * @param ino the inode number
+ * @param fi file information
+ * @param op the locking operation, see flock(2)
+ */
+ void (*flock) (fuse_req_t req, fuse_ino_t ino,
+ struct fuse_file_info *fi, int op);
+
+ /**
+ * Allocate requested space. If this function returns success then
+ * subsequent writes to the specified range shall not fail due to the lack
+ * of free space on the file system storage media.
+ *
+ * Introduced in version 2.9
+ *
+ * Valid replies:
+ * fuse_reply_err
+ *
+ * @param req request handle
+ * @param ino the inode number
+ * @param offset starting point for allocated region
+ * @param length size of allocated region
+ * @param mode determines the operation to be performed on the given range,
+ * see fallocate(2)
+ */
+ void (*fallocate) (fuse_req_t req, fuse_ino_t ino, int mode,
+ off64_t offset, off64_t length, struct fuse_file_info *fi);
+};
+
+/**
+ * Reply with an error code or success
+ *
+ * Possible requests:
+ * all except forget
+ *
+ * unlink, rmdir, rename, flush, release, fsync, fsyncdir, setxattr,
+ * removexattr and setlk may send a zero code
+ *
+ * @param req request handle
+ * @param err the positive error value, or zero for success
+ * @return zero for success, -errno for failure to send reply
+ */
+int fuse_reply_err(fuse_req_t req, int err);
+
+/**
+ * Don't send reply
+ *
+ * Possible requests:
+ * forget
+ *
+ * @param req request handle
+ */
+void fuse_reply_none(fuse_req_t req);
+
+/**
+ * Reply with a directory entry
+ *
+ * Possible requests:
+ * lookup, mknod, mkdir, symlink, link
+ *
+ * Side effects:
+ * increments the lookup count on success
+ *
+ * @param req request handle
+ * @param e the entry parameters
+ * @return zero for success, -errno for failure to send reply
+ */
+int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e);
+
+/**
+ * Reply with a directory entry and open parameters
+ *
+ * currently the following members of 'fi' are used:
+ * fh, direct_io, keep_cache
+ *
+ * Possible requests:
+ * create
+ *
+ * Side effects:
+ * increments the lookup count on success
+ *
+ * @param req request handle
+ * @param e the entry parameters
+ * @param fi file information
+ * @return zero for success, -errno for failure to send reply
+ */
+int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e,
+ const struct fuse_file_info *fi);
+
+/**
+ * Reply with attributes
+ *
+ * Possible requests:
+ * getattr, setattr
+ *
+ * @param req request handle
+ * @param attr the attributes
+ * @param attr_timeout validity timeout (in seconds) for the attributes
+ * @return zero for success, -errno for failure to send reply
+ */
+int fuse_reply_attr(fuse_req_t req, const struct stat *attr,
+ double attr_timeout);
+
+/**
+ * Reply with the contents of a symbolic link
+ *
+ * Possible requests:
+ * readlink
+ *
+ * @param req request handle
+ * @param link symbolic link contents
+ * @return zero for success, -errno for failure to send reply
+ */
+int fuse_reply_readlink(fuse_req_t req, const char *link);
+
+/**
+ * Reply with open parameters
+ *
+ * currently the following members of 'fi' are used:
+ * fh, direct_io, keep_cache
+ *
+ * Possible requests:
+ * open, opendir
+ *
+ * @param req request handle
+ * @param fi file information
+ * @return zero for success, -errno for failure to send reply
+ */
+int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi);
+
+/**
+ * Reply with number of bytes written
+ *
+ * Possible requests:
+ * write
+ *
+ * @param req request handle
+ * @param count the number of bytes written
+ * @return zero for success, -errno for failure to send reply
+ */
+int fuse_reply_write(fuse_req_t req, size_t count);
+
+/**
+ * Reply with data
+ *
+ * Possible requests:
+ * read, readdir, getxattr, listxattr
+ *
+ * @param req request handle
+ * @param buf buffer containing data
+ * @param size the size of data in bytes
+ * @return zero for success, -errno for failure to send reply
+ */
+int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size);
+
+/**
+ * Reply with data copied/moved from buffer(s)
+ *
+ * Possible requests:
+ * read, readdir, getxattr, listxattr
+ *
+ * @param req request handle
+ * @param bufv buffer vector
+ * @param flags flags controlling the copy
+ * @return zero for success, -errno for failure to send reply
+ */
+int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv,
+ enum fuse_buf_copy_flags flags);
+
+/**
+ * Reply with data vector
+ *
+ * Possible requests:
+ * read, readdir, getxattr, listxattr
+ *
+ * @param req request handle
+ * @param iov the vector containing the data
+ * @param count the size of vector
+ * @return zero for success, -errno for failure to send reply
+ */
+int fuse_reply_iov(fuse_req_t req, const struct iovec *iov, int count);
+
+/**
+ * Reply with filesystem statistics
+ *
+ * Possible requests:
+ * statfs
+ *
+ * @param req request handle
+ * @param stbuf filesystem statistics
+ * @return zero for success, -errno for failure to send reply
+ */
+int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf);
+
+/**
+ * Reply with needed buffer size
+ *
+ * Possible requests:
+ * getxattr, listxattr
+ *
+ * @param req request handle
+ * @param count the buffer size needed in bytes
+ * @return zero for success, -errno for failure to send reply
+ */
+int fuse_reply_xattr(fuse_req_t req, size_t count);
+
+/**
+ * Reply with file lock information
+ *
+ * Possible requests:
+ * getlk
+ *
+ * @param req request handle
+ * @param lock the lock information
+ * @return zero for success, -errno for failure to send reply
+ */
+int fuse_reply_lock(fuse_req_t req, const struct flock *lock);
+
+/**
+ * Reply with block index
+ *
+ * Possible requests:
+ * bmap
+ *
+ * @param req request handle
+ * @param idx block index within device
+ * @return zero for success, -errno for failure to send reply
+ */
+int fuse_reply_bmap(fuse_req_t req, uint64_t idx);
+
+/* ----------------------------------------------------------- *
+ * Filling a buffer in readdir *
+ * ----------------------------------------------------------- */
+
+/**
+ * Add a directory entry to the buffer
+ *
+ * Buffer needs to be large enough to hold the entry. If it's not,
+ * then the entry is not filled in but the size of the entry is still
+ * returned. The caller can check this by comparing the bufsize
+ * parameter with the returned entry size. If the entry size is
+ * larger than the buffer size, the operation failed.
+ *
+ * From the 'stbuf' argument the st_ino field and bits 12-15 of the
+ * st_mode field are used. The other fields are ignored.
+ *
+ * Note: offsets do not necessarily represent physical offsets, and
+ * could be any marker, that enables the implementation to find a
+ * specific point in the directory stream.
+ *
+ * @param req request handle
+ * @param buf the point where the new entry will be added to the buffer
+ * @param bufsize remaining size of the buffer
+ * @param name the name of the entry
+ * @param stbuf the file attributes
+ * @param off the offset of the next entry
+ * @return the space needed for the entry
+ */
+size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize,
+ const char *name, const struct stat *stbuf,
+ off64_t off);
+
+/**
+ * Reply to ask for data fetch and output buffer preparation. ioctl
+ * will be retried with the specified input data fetched and output
+ * buffer prepared.
+ *
+ * Possible requests:
+ * ioctl
+ *
+ * @param req request handle
+ * @param in_iov iovec specifying data to fetch from the caller
+ * @param in_count number of entries in in_iov
+ * @param out_iov iovec specifying addresses to write output to
+ * @param out_count number of entries in out_iov
+ * @return zero for success, -errno for failure to send reply
+ */
+int fuse_reply_ioctl_retry(fuse_req_t req,
+ const struct iovec *in_iov, size_t in_count,
+ const struct iovec *out_iov, size_t out_count);
+
+/**
+ * Reply to finish ioctl
+ *
+ * Possible requests:
+ * ioctl
+ *
+ * @param req request handle
+ * @param result result to be passed to the caller
+ * @param buf buffer containing output data
+ * @param size length of output data
+ */
+int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t size);
+
+/**
+ * Reply to finish ioctl with iov buffer
+ *
+ * Possible requests:
+ * ioctl
+ *
+ * @param req request handle
+ * @param result result to be passed to the caller
+ * @param iov the vector containing the data
+ * @param count the size of vector
+ */
+int fuse_reply_ioctl_iov(fuse_req_t req, int result, const struct iovec *iov,
+ int count);
+
+/**
+ * Reply with poll result event mask
+ *
+ * @param req request handle
+ * @param revents poll result event mask
+ */
+int fuse_reply_poll(fuse_req_t req, unsigned revents);
+
+/* ----------------------------------------------------------- *
+ * Notification *
+ * ----------------------------------------------------------- */
+
+/**
+ * Notify IO readiness event
+ *
+ * For more information, please read comment for poll operation.
+ *
+ * @param ph poll handle to notify IO readiness event for
+ */
+int fuse_lowlevel_notify_poll(struct fuse_pollhandle *ph);
+
+/**
+ * Notify to invalidate cache for an inode
+ *
+ * @param ch the channel through which to send the invalidation
+ * @param ino the inode number
+ * @param off the offset in the inode where to start invalidating
+ * or negative to invalidate attributes only
+ * @param len the amount of cache to invalidate or 0 for all
+ * @return zero for success, -errno for failure
+ */
+int fuse_lowlevel_notify_inval_inode(struct fuse_chan *ch, fuse_ino_t ino,
+ off64_t off, off64_t len);
+
+/**
+ * Notify to invalidate parent attributes and the dentry matching
+ * parent/name
+ *
+ * @param ch the channel through which to send the invalidation
+ * @param parent inode number
+ * @param name file name
+ * @param namelen strlen() of file name
+ * @return zero for success, -errno for failure
+ */
+int fuse_lowlevel_notify_inval_entry(struct fuse_chan *ch, fuse_ino_t parent,
+ const char *name, size_t namelen);
+
+/**
+ * Notify to invalidate parent attributes and delete the dentry matching
+ * parent/name if the dentry's inode number matches child (otherwise it
+ * will invalidate the matching dentry).
+ *
+ * @param ch the channel through which to send the notification
+ * @param parent inode number
+ * @param child inode number
+ * @param name file name
+ * @param namelen strlen() of file name
+ * @return zero for success, -errno for failure
+ */
+int fuse_lowlevel_notify_delete(struct fuse_chan *ch,
+ fuse_ino_t parent, fuse_ino_t child,
+ const char *name, size_t namelen);
+
+/**
+ * Store data to the kernel buffers
+ *
+ * Synchronously store data in the kernel buffers belonging to the
+ * given inode. The stored data is marked up-to-date (no read will be
+ * performed against it, unless it's invalidated or evicted from the
+ * cache).
+ *
+ * If the stored data overflows the current file size, then the size
+ * is extended, similarly to a write(2) on the filesystem.
+ *
+ * If this function returns an error, then the store wasn't fully
+ * completed, but it may have been partially completed.
+ *
+ * @param ch the channel through which to send the invalidation
+ * @param ino the inode number
+ * @param offset the starting offset into the file to store to
+ * @param bufv buffer vector
+ * @param flags flags controlling the copy
+ * @return zero for success, -errno for failure
+ */
+int fuse_lowlevel_notify_store(struct fuse_chan *ch, fuse_ino_t ino,
+ off64_t offset, struct fuse_bufvec *bufv,
+ enum fuse_buf_copy_flags flags);
+/**
+ * Retrieve data from the kernel buffers
+ *
+ * Retrieve data in the kernel buffers belonging to the given inode.
+ * If successful then the retrieve_reply() method will be called with
+ * the returned data.
+ *
+ * Only present pages are returned in the retrieve reply. Retrieving
+ * stops when it finds a non-present page and only data prior to that is
+ * returned.
+ *
+ * If this function returns an error, then the retrieve will not be
+ * completed and no reply will be sent.
+ *
+ * This function doesn't change the dirty state of pages in the kernel
+ * buffer. For dirty pages the write() method will be called
+ * regardless of having been retrieved previously.
+ *
+ * @param ch the channel through which to send the invalidation
+ * @param ino the inode number
+ * @param size the number of bytes to retrieve
+ * @param offset the starting offset into the file to retrieve from
+ * @param cookie user data to supply to the reply callback
+ * @return zero for success, -errno for failure
+ */
+int fuse_lowlevel_notify_retrieve(struct fuse_chan *ch, fuse_ino_t ino,
+ size_t size, off64_t offset, void *cookie);
+
+
+/* ----------------------------------------------------------- *
+ * Utility functions *
+ * ----------------------------------------------------------- */
+
+/**
+ * Get the userdata from the request
+ *
+ * @param req request handle
+ * @return the user data passed to fuse_lowlevel_new()
+ */
+void *fuse_req_userdata(fuse_req_t req);
+
+/**
+ * Get the context from the request
+ *
+ * The pointer returned by this function will only be valid for the
+ * request's lifetime
+ *
+ * @param req request handle
+ * @return the context structure
+ */
+const struct fuse_ctx *fuse_req_ctx(fuse_req_t req);
+
+/**
+ * Get the current supplementary group IDs for the specified request
+ *
+ * Similar to the getgroups(2) system call, except the return value is
+ * always the total number of group IDs, even if it is larger than the
+ * specified size.
+ *
+ * The current fuse kernel module in linux (as of 2.6.30) doesn't pass
+ * the group list to userspace, hence this function needs to parse
+ * "/proc/$TID/task/$TID/status" to get the group IDs.
+ *
+ * This feature may not be supported on all operating systems. In
+ * such a case this function will return -ENOSYS.
+ *
+ * @param req request handle
+ * @param size size of given array
+ * @param list array of group IDs to be filled in
+ * @return the total number of supplementary group IDs or -errno on failure
+ */
+int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[]);
+
+/**
+ * Callback function for an interrupt
+ *
+ * @param req interrupted request
+ * @param data user data
+ */
+typedef void (*fuse_interrupt_func_t)(fuse_req_t req, void *data);
+
+/**
+ * Register/unregister callback for an interrupt
+ *
+ * If an interrupt has already happened, then the callback function is
+ * called from within this function, hence it's not possible for
+ * interrupts to be lost.
+ *
+ * @param req request handle
+ * @param func the callback function or NULL for unregister
+ * @param data user data passed to the callback function
+ */
+void fuse_req_interrupt_func(fuse_req_t req, fuse_interrupt_func_t func,
+ void *data);
+
+/**
+ * Check if a request has already been interrupted
+ *
+ * @param req request handle
+ * @return 1 if the request has been interrupted, 0 otherwise
+ */
+int fuse_req_interrupted(fuse_req_t req);
+
+/* ----------------------------------------------------------- *
+ * Filesystem setup *
+ * ----------------------------------------------------------- */
+
+/* Deprecated, don't use */
+int fuse_lowlevel_is_lib_option(const char *opt);
+
+/**
+ * Create a low level session
+ *
+ * @param args argument vector
+ * @param op the low level filesystem operations
+ * @param op_size sizeof(struct fuse_lowlevel_ops)
+ * @param userdata user data
+ * @return the created session object, or NULL on failure
+ */
+struct fuse_session *fuse_lowlevel_new(struct fuse_args *args,
+ const struct fuse_lowlevel_ops *op,
+ size_t op_size, void *userdata);
+
+/* ----------------------------------------------------------- *
+ * Session interface *
+ * ----------------------------------------------------------- */
+
+/**
+ * Session operations
+ *
+ * This is used in session creation
+ */
+struct fuse_session_ops {
+ /**
+ * Hook to process a request (mandatory)
+ *
+ * @param data user data passed to fuse_session_new()
+ * @param buf buffer containing the raw request
+ * @param len request length
+ * @param ch channel on which the request was received
+ */
+ void (*process) (void *data, const char *buf, size_t len,
+ struct fuse_chan *ch);
+
+ /**
+ * Hook for session exit and reset (optional)
+ *
+ * @param data user data passed to fuse_session_new()
+ * @param val exited status (1 - exited, 0 - not exited)
+ */
+ void (*exit) (void *data, int val);
+
+ /**
+ * Hook for querying the current exited status (optional)
+ *
+ * @param data user data passed to fuse_session_new()
+ * @return 1 if exited, 0 if not exited
+ */
+ int (*exited) (void *data);
+
+ /**
+ * Hook for cleaning up the channel on destroy (optional)
+ *
+ * @param data user data passed to fuse_session_new()
+ */
+ void (*destroy) (void *data);
+};
+
+/**
+ * Create a new session
+ *
+ * @param op session operations
+ * @param data user data
+ * @return new session object, or NULL on failure
+ */
+struct fuse_session *fuse_session_new(struct fuse_session_ops *op, void *data);
+
+/**
+ * Assign a channel to a session
+ *
+ * Note: currently only a single channel may be assigned. This may
+ * change in the future
+ *
+ * If a session is destroyed, the assigned channel is also destroyed
+ *
+ * @param se the session
+ * @param ch the channel
+ */
+void fuse_session_add_chan(struct fuse_session *se, struct fuse_chan *ch);
+
+/**
+ * Remove a channel from a session
+ *
+ * If the channel is not assigned to a session, then this is a no-op
+ *
+ * @param ch the channel to remove
+ */
+void fuse_session_remove_chan(struct fuse_chan *ch);
+
+/**
+ * Iterate over the channels assigned to a session
+ *
+ * The iterating function needs to start with a NULL channel, and
+ * after that needs to pass the previously returned channel to the
+ * function.
+ *
+ * @param se the session
+ * @param ch the previous channel, or NULL
+ * @return the next channel, or NULL if no more channels exist
+ */
+struct fuse_chan *fuse_session_next_chan(struct fuse_session *se,
+ struct fuse_chan *ch);
+
+/**
+ * Process a raw request
+ *
+ * @param se the session
+ * @param buf buffer containing the raw request
+ * @param len request length
+ * @param ch channel on which the request was received
+ */
+void fuse_session_process(struct fuse_session *se, const char *buf, size_t len,
+ struct fuse_chan *ch);
+
+/**
+ * Process a raw request supplied in a generic buffer
+ *
+ * This is a more generic version of fuse_session_process(). The
+ * fuse_buf may contain a memory buffer or a pipe file descriptor.
+ *
+ * @param se the session
+ * @param buf the fuse_buf containing the request
+ * @param ch channel on which the request was received
+ */
+void fuse_session_process_buf(struct fuse_session *se,
+ const struct fuse_buf *buf, struct fuse_chan *ch);
+
+/**
+ * Receive a raw request supplied in a generic buffer
+ *
+ * This is a more generic version of fuse_chan_recv(). The fuse_buf
+ * supplied to this function contains a suitably allocated memory
+ * buffer. This may be overwritten with a file descriptor buffer.
+ *
+ * @param se the session
+ * @param buf the fuse_buf to store the request in
+ * @param chp pointer to the channel
+ * @return the actual size of the raw request, or -errno on error
+ */
+int fuse_session_receive_buf(struct fuse_session *se, struct fuse_buf *buf,
+ struct fuse_chan **chp);
+
+/**
+ * Destroy a session
+ *
+ * @param se the session
+ */
+void fuse_session_destroy(struct fuse_session *se);
+
+/**
+ * Exit a session
+ *
+ * @param se the session
+ */
+void fuse_session_exit(struct fuse_session *se);
+
+/**
+ * Reset the exited status of a session
+ *
+ * @param se the session
+ */
+void fuse_session_reset(struct fuse_session *se);
+
+/**
+ * Query the exited status of a session
+ *
+ * @param se the session
+ * @return 1 if exited, 0 if not exited
+ */
+int fuse_session_exited(struct fuse_session *se);
+
+/**
+ * Get the user data provided to the session
+ *
+ * @param se the session
+ * @return the user data
+ */
+void *fuse_session_data(struct fuse_session *se);
+
+/**
+ * Enter a single threaded event loop
+ *
+ * @param se the session
+ * @return 0 on success, -1 on error
+ */
+int fuse_session_loop(struct fuse_session *se);
+
+/**
+ * Enter a multi-threaded event loop
+ *
+ * @param se the session
+ * @return 0 on success, -1 on error
+ */
+int fuse_session_loop_mt(struct fuse_session *se);
+
+/* ----------------------------------------------------------- *
+ * Channel interface *
+ * ----------------------------------------------------------- */
+
+/**
+ * Channel operations
+ *
+ * This is used in channel creation
+ */
+struct fuse_chan_ops {
+ /**
+ * Hook for receiving a raw request
+ *
+ * @param ch pointer to the channel
+ * @param buf the buffer to store the request in
+ * @param size the size of the buffer
+ * @return the actual size of the raw request, or -1 on error
+ */
+ int (*receive)(struct fuse_chan **chp, char *buf, size_t size);
+
+ /**
+ * Hook for sending a raw reply
+ *
+ * A return value of -ENOENT means, that the request was
+ * interrupted, and the reply was discarded
+ *
+ * @param ch the channel
+ * @param iov vector of blocks
+ * @param count the number of blocks in vector
+ * @return zero on success, -errno on failure
+ */
+ int (*send)(struct fuse_chan *ch, const struct iovec iov[],
+ size_t count);
+
+ /**
+ * Destroy the channel
+ *
+ * @param ch the channel
+ */
+ void (*destroy)(struct fuse_chan *ch);
+};
+
+/**
+ * Create a new channel
+ *
+ * @param op channel operations
+ * @param fd file descriptor of the channel
+ * @param bufsize the minimal receive buffer size
+ * @param data user data
+ * @return the new channel object, or NULL on failure
+ */
+struct fuse_chan *fuse_chan_new(struct fuse_chan_ops *op, int fd,
+ size_t bufsize, void *data);
+
+/**
+ * Query the file descriptor of the channel
+ *
+ * @param ch the channel
+ * @return the file descriptor passed to fuse_chan_new()
+ */
+int fuse_chan_fd(struct fuse_chan *ch);
+
+/**
+ * Query the minimal receive buffer size
+ *
+ * @param ch the channel
+ * @return the buffer size passed to fuse_chan_new()
+ */
+size_t fuse_chan_bufsize(struct fuse_chan *ch);
+
+/**
+ * Query the user data
+ *
+ * @param ch the channel
+ * @return the user data passed to fuse_chan_new()
+ */
+void *fuse_chan_data(struct fuse_chan *ch);
+
+/**
+ * Query the session to which this channel is assigned
+ *
+ * @param ch the channel
+ * @return the session, or NULL if the channel is not assigned
+ */
+struct fuse_session *fuse_chan_session(struct fuse_chan *ch);
+
+/**
+ * Receive a raw request
+ *
+ * A return value of -ENODEV means, that the filesystem was unmounted
+ *
+ * @param ch pointer to the channel
+ * @param buf the buffer to store the request in
+ * @param size the size of the buffer
+ * @return the actual size of the raw request, or -errno on error
+ */
+int fuse_chan_recv(struct fuse_chan **ch, char *buf, size_t size);
+
+/**
+ * Send a raw reply
+ *
+ * A return value of -ENOENT means, that the request was
+ * interrupted, and the reply was discarded
+ *
+ * @param ch the channel
+ * @param iov vector of blocks
+ * @param count the number of blocks in vector
+ * @return zero on success, -errno on failure
+ */
+int fuse_chan_send(struct fuse_chan *ch, const struct iovec iov[],
+ size_t count);
+
+/**
+ * Destroy a channel
+ *
+ * @param ch the channel
+ */
+void fuse_chan_destroy(struct fuse_chan *ch);
+
+/* ----------------------------------------------------------- *
+ * Compatibility stuff *
+ * ----------------------------------------------------------- */
+
+#if FUSE_USE_VERSION < 26
+# include "fuse_lowlevel_compat.h"
+# define fuse_chan_ops fuse_chan_ops_compat24
+# define fuse_chan_new fuse_chan_new_compat24
+# if FUSE_USE_VERSION == 25
+# define fuse_lowlevel_ops fuse_lowlevel_ops_compat25
+# define fuse_lowlevel_new fuse_lowlevel_new_compat25
+# elif FUSE_USE_VERSION == 24
+# define fuse_lowlevel_ops fuse_lowlevel_ops_compat
+# define fuse_lowlevel_new fuse_lowlevel_new_compat
+# define fuse_file_info fuse_file_info_compat
+# define fuse_reply_statfs fuse_reply_statfs_compat
+# define fuse_reply_open fuse_reply_open_compat
+# else
+# error Compatibility with low-level API version < 24 not supported
+# endif
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FUSE_LOWLEVEL_H_ */
diff --git a/fuse/include/fuse_lowlevel_compat.h b/fuse/include/fuse_lowlevel_compat.h
new file mode 100644
index 000000000..78b7c2bd0
--- /dev/null
+++ b/fuse/include/fuse_lowlevel_compat.h
@@ -0,0 +1,155 @@
+/*
+ FUSE: Filesystem in Userspace
+ Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
+
+ This program can be distributed under the terms of the GNU LGPLv2.
+ See the file COPYING.LIB.
+*/
+
+/* these definitions provide source compatibility to prior versions.
+ Do not include this file directly! */
+
+struct fuse_lowlevel_ops_compat25 {
+ void (*init) (void *userdata);
+ void (*destroy) (void *userdata);
+ void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name);
+ void (*forget) (fuse_req_t req, fuse_ino_t ino, unsigned long nlookup);
+ void (*getattr) (fuse_req_t req, fuse_ino_t ino,
+ struct fuse_file_info *fi);
+ void (*setattr) (fuse_req_t req, fuse_ino_t ino, struct stat *attr,
+ int to_set, struct fuse_file_info *fi);
+ void (*readlink) (fuse_req_t req, fuse_ino_t ino);
+ void (*mknod) (fuse_req_t req, fuse_ino_t parent, const char *name,
+ mode_t mode, dev_t rdev);
+ void (*mkdir) (fuse_req_t req, fuse_ino_t parent, const char *name,
+ mode_t mode);
+ void (*unlink) (fuse_req_t req, fuse_ino_t parent, const char *name);
+ void (*rmdir) (fuse_req_t req, fuse_ino_t parent, const char *name);
+ void (*symlink) (fuse_req_t req, const char *link, fuse_ino_t parent,
+ const char *name);
+ void (*rename) (fuse_req_t req, fuse_ino_t parent, const char *name,
+ fuse_ino_t newparent, const char *newname);
+ void (*link) (fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent,
+ const char *newname);
+ void (*open) (fuse_req_t req, fuse_ino_t ino,
+ struct fuse_file_info *fi);
+ void (*read) (fuse_req_t req, fuse_ino_t ino, size_t size, off64_t off,
+ struct fuse_file_info *fi);
+ void (*write) (fuse_req_t req, fuse_ino_t ino, const char *buf,
+ size_t size, off64_t off, struct fuse_file_info *fi);
+ void (*flush) (fuse_req_t req, fuse_ino_t ino,
+ struct fuse_file_info *fi);
+ void (*release) (fuse_req_t req, fuse_ino_t ino,
+ struct fuse_file_info *fi);
+ void (*fsync) (fuse_req_t req, fuse_ino_t ino, int datasync,
+ struct fuse_file_info *fi);
+ void (*opendir) (fuse_req_t req, fuse_ino_t ino,
+ struct fuse_file_info *fi);
+ void (*readdir) (fuse_req_t req, fuse_ino_t ino, size_t size, off64_t off,
+ struct fuse_file_info *fi);
+ void (*releasedir) (fuse_req_t req, fuse_ino_t ino,
+ struct fuse_file_info *fi);
+ void (*fsyncdir) (fuse_req_t req, fuse_ino_t ino, int datasync,
+ struct fuse_file_info *fi);
+ void (*statfs) (fuse_req_t req);
+ void (*setxattr) (fuse_req_t req, fuse_ino_t ino, const char *name,
+ const char *value, size_t size, int flags);
+ void (*getxattr) (fuse_req_t req, fuse_ino_t ino, const char *name,
+ size_t size);
+ void (*listxattr) (fuse_req_t req, fuse_ino_t ino, size_t size);
+ void (*removexattr) (fuse_req_t req, fuse_ino_t ino, const char *name);
+ void (*access) (fuse_req_t req, fuse_ino_t ino, int mask);
+ void (*create) (fuse_req_t req, fuse_ino_t parent, const char *name,
+ mode_t mode, struct fuse_file_info *fi);
+};
+
+struct fuse_session *fuse_lowlevel_new_compat25(struct fuse_args *args,
+ const struct fuse_lowlevel_ops_compat25 *op,
+ size_t op_size, void *userdata);
+
+size_t fuse_dirent_size(size_t namelen);
+
+char *fuse_add_dirent(char *buf, const char *name, const struct stat *stbuf,
+ off64_t off);
+
+#if !defined(__FreeBSD__) && !defined(__NetBSD__)
+
+#include <sys/statfs.h>
+
+struct fuse_lowlevel_ops_compat {
+ void (*init) (void *userdata);
+ void (*destroy) (void *userdata);
+ void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name);
+ void (*forget) (fuse_req_t req, fuse_ino_t ino, unsigned long nlookup);
+ void (*getattr) (fuse_req_t req, fuse_ino_t ino,
+ struct fuse_file_info_compat *fi);
+ void (*setattr) (fuse_req_t req, fuse_ino_t ino, struct stat *attr,
+ int to_set, struct fuse_file_info_compat *fi);
+ void (*readlink) (fuse_req_t req, fuse_ino_t ino);
+ void (*mknod) (fuse_req_t req, fuse_ino_t parent, const char *name,
+ mode_t mode, dev_t rdev);
+ void (*mkdir) (fuse_req_t req, fuse_ino_t parent, const char *name,
+ mode_t mode);
+ void (*unlink) (fuse_req_t req, fuse_ino_t parent, const char *name);
+ void (*rmdir) (fuse_req_t req, fuse_ino_t parent, const char *name);
+ void (*symlink) (fuse_req_t req, const char *link, fuse_ino_t parent,
+ const char *name);
+ void (*rename) (fuse_req_t req, fuse_ino_t parent, const char *name,
+ fuse_ino_t newparent, const char *newname);
+ void (*link) (fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent,
+ const char *newname);
+ void (*open) (fuse_req_t req, fuse_ino_t ino,
+ struct fuse_file_info_compat *fi);
+ void (*read) (fuse_req_t req, fuse_ino_t ino, size_t size, off64_t off,
+ struct fuse_file_info_compat *fi);
+ void (*write) (fuse_req_t req, fuse_ino_t ino, const char *buf,
+ size_t size, off64_t off, struct fuse_file_info_compat *fi);
+ void (*flush) (fuse_req_t req, fuse_ino_t ino,
+ struct fuse_file_info_compat *fi);
+ void (*release) (fuse_req_t req, fuse_ino_t ino,
+ struct fuse_file_info_compat *fi);
+ void (*fsync) (fuse_req_t req, fuse_ino_t ino, int datasync,
+ struct fuse_file_info_compat *fi);
+ void (*opendir) (fuse_req_t req, fuse_ino_t ino,
+ struct fuse_file_info_compat *fi);
+ void (*readdir) (fuse_req_t req, fuse_ino_t ino, size_t size, off64_t off,
+ struct fuse_file_info_compat *fi);
+ void (*releasedir) (fuse_req_t req, fuse_ino_t ino,
+ struct fuse_file_info_compat *fi);
+ void (*fsyncdir) (fuse_req_t req, fuse_ino_t ino, int datasync,
+ struct fuse_file_info_compat *fi);
+ void (*statfs) (fuse_req_t req);
+ void (*setxattr) (fuse_req_t req, fuse_ino_t ino, const char *name,
+ const char *value, size_t size, int flags);
+ void (*getxattr) (fuse_req_t req, fuse_ino_t ino, const char *name,
+ size_t size);
+ void (*listxattr) (fuse_req_t req, fuse_ino_t ino, size_t size);
+ void (*removexattr) (fuse_req_t req, fuse_ino_t ino, const char *name);
+ void (*access) (fuse_req_t req, fuse_ino_t ino, int mask);
+ void (*create) (fuse_req_t req, fuse_ino_t parent, const char *name,
+ mode_t mode, struct fuse_file_info_compat *fi);
+};
+
+int fuse_reply_statfs_compat(fuse_req_t req, const struct statfs *stbuf);
+
+int fuse_reply_open_compat(fuse_req_t req,
+ const struct fuse_file_info_compat *fi);
+
+struct fuse_session *fuse_lowlevel_new_compat(const char *opts,
+ const struct fuse_lowlevel_ops_compat *op,
+ size_t op_size, void *userdata);
+
+#endif /* __FreeBSD__ || __NetBSD__ */
+
+struct fuse_chan_ops_compat24 {
+ int (*receive)(struct fuse_chan *ch, char *buf, size_t size);
+ int (*send)(struct fuse_chan *ch, const struct iovec iov[],
+ size_t count);
+ void (*destroy)(struct fuse_chan *ch);
+};
+
+struct fuse_chan *fuse_chan_new_compat24(struct fuse_chan_ops_compat24 *op,
+ int fd, size_t bufsize, void *data);
+
+int fuse_chan_receive(struct fuse_chan *ch, char *buf, size_t size);
+struct fuse_chan *fuse_kern_chan_new(int fd);
diff --git a/fuse/include/fuse_opt.h b/fuse/include/fuse_opt.h
new file mode 100644
index 000000000..add0a3089
--- /dev/null
+++ b/fuse/include/fuse_opt.h
@@ -0,0 +1,270 @@
+/*
+ FUSE: Filesystem in Userspace
+ Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
+
+ This program can be distributed under the terms of the GNU LGPLv2.
+ See the file COPYING.LIB.
+*/
+
+#ifndef _FUSE_OPT_H_
+#define _FUSE_OPT_H_
+
+/** @file
+ *
+ * This file defines the option parsing interface of FUSE
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Option description
+ *
+ * This structure describes a single option, and action associated
+ * with it, in case it matches.
+ *
+ * More than one such match may occur, in which case the action for
+ * each match is executed.
+ *
+ * There are three possible actions in case of a match:
+ *
+ * i) An integer (int or unsigned) variable determined by 'offset' is
+ * set to 'value'
+ *
+ * ii) The processing function is called, with 'value' as the key
+ *
+ * iii) An integer (any) or string (char *) variable determined by
+ * 'offset' is set to the value of an option parameter
+ *
+ * 'offset' should normally be either set to
+ *
+ * - 'offsetof(struct foo, member)' actions i) and iii)
+ *
+ * - -1 action ii)
+ *
+ * The 'offsetof()' macro is defined in the <stddef.h> header.
+ *
+ * The template determines which options match, and also have an
+ * effect on the action. Normally the action is either i) or ii), but
+ * if a format is present in the template, then action iii) is
+ * performed.
+ *
+ * The types of templates are:
+ *
+ * 1) "-x", "-foo", "--foo", "--foo-bar", etc. These match only
+ * themselves. Invalid values are "--" and anything beginning
+ * with "-o"
+ *
+ * 2) "foo", "foo-bar", etc. These match "-ofoo", "-ofoo-bar" or
+ * the relevant option in a comma separated option list
+ *
+ * 3) "bar=", "--foo=", etc. These are variations of 1) and 2)
+ * which have a parameter
+ *
+ * 4) "bar=%s", "--foo=%lu", etc. Same matching as above but perform
+ * action iii).
+ *
+ * 5) "-x ", etc. Matches either "-xparam" or "-x param" as
+ * two separate arguments
+ *
+ * 6) "-x %s", etc. Combination of 4) and 5)
+ *
+ * If the format is "%s", memory is allocated for the string unlike
+ * with scanf().
+ */
+struct fuse_opt {
+ /** Matching template and optional parameter formatting */
+ const char *templ;
+
+ /**
+ * Offset of variable within 'data' parameter of fuse_opt_parse()
+ * or -1
+ */
+ unsigned long offset;
+
+ /**
+ * Value to set the variable to, or to be passed as 'key' to the
+ * processing function. Ignored if template has a format
+ */
+ int value;
+};
+
+/**
+ * Key option. In case of a match, the processing function will be
+ * called with the specified key.
+ */
+#define FUSE_OPT_KEY(templ, key) { templ, -1U, key }
+
+/**
+ * Last option. An array of 'struct fuse_opt' must end with a NULL
+ * template value
+ */
+#define FUSE_OPT_END { NULL, 0, 0 }
+
+/**
+ * Argument list
+ */
+struct fuse_args {
+ /** Argument count */
+ int argc;
+
+ /** Argument vector. NULL terminated */
+ char **argv;
+
+ /** Is 'argv' allocated? */
+ int allocated;
+};
+
+/**
+ * Initializer for 'struct fuse_args'
+ */
+#define FUSE_ARGS_INIT(argc, argv) { argc, argv, 0 }
+
+/**
+ * Key value passed to the processing function if an option did not
+ * match any template
+ */
+#define FUSE_OPT_KEY_OPT -1
+
+/**
+ * Key value passed to the processing function for all non-options
+ *
+ * Non-options are the arguments beginning with a character other than
+ * '-' or all arguments after the special '--' option
+ */
+#define FUSE_OPT_KEY_NONOPT -2
+
+/**
+ * Special key value for options to keep
+ *
+ * Argument is not passed to processing function, but behave as if the
+ * processing function returned 1
+ */
+#define FUSE_OPT_KEY_KEEP -3
+
+/**
+ * Special key value for options to discard
+ *
+ * Argument is not passed to processing function, but behave as if the
+ * processing function returned zero
+ */
+#define FUSE_OPT_KEY_DISCARD -4
+
+/**
+ * Processing function
+ *
+ * This function is called if
+ * - option did not match any 'struct fuse_opt'
+ * - argument is a non-option
+ * - option did match and offset was set to -1
+ *
+ * The 'arg' parameter will always contain the whole argument or
+ * option including the parameter if exists. A two-argument option
+ * ("-x foo") is always converted to single argument option of the
+ * form "-xfoo" before this function is called.
+ *
+ * Options of the form '-ofoo' are passed to this function without the
+ * '-o' prefix.
+ *
+ * The return value of this function determines whether this argument
+ * is to be inserted into the output argument vector, or discarded.
+ *
+ * @param data is the user data passed to the fuse_opt_parse() function
+ * @param arg is the whole argument or option
+ * @param key determines why the processing function was called
+ * @param outargs the current output argument list
+ * @return -1 on error, 0 if arg is to be discarded, 1 if arg should be kept
+ */
+typedef int (*fuse_opt_proc_t)(void *data, const char *arg, int key,
+ struct fuse_args *outargs);
+
+/**
+ * Option parsing function
+ *
+ * If 'args' was returned from a previous call to fuse_opt_parse() or
+ * it was constructed from
+ *
+ * A NULL 'args' is equivalent to an empty argument vector
+ *
+ * A NULL 'opts' is equivalent to an 'opts' array containing a single
+ * end marker
+ *
+ * A NULL 'proc' is equivalent to a processing function always
+ * returning '1'
+ *
+ * @param args is the input and output argument list
+ * @param data is the user data
+ * @param opts is the option description array
+ * @param proc is the processing function
+ * @return -1 on error, 0 on success
+ */
+int fuse_opt_parse(struct fuse_args *args, void *data,
+ const struct fuse_opt opts[], fuse_opt_proc_t proc);
+
+/**
+ * Add an option to a comma separated option list
+ *
+ * @param opts is a pointer to an option list, may point to a NULL value
+ * @param opt is the option to add
+ * @return -1 on allocation error, 0 on success
+ */
+int fuse_opt_add_opt(char **opts, const char *opt);
+
+/**
+ * Add an option, escaping commas, to a comma separated option list
+ *
+ * @param opts is a pointer to an option list, may point to a NULL value
+ * @param opt is the option to add
+ * @return -1 on allocation error, 0 on success
+ */
+int fuse_opt_add_opt_escaped(char **opts, const char *opt);
+
+/**
+ * Add an argument to a NULL terminated argument vector
+ *
+ * @param args is the structure containing the current argument list
+ * @param arg is the new argument to add
+ * @return -1 on allocation error, 0 on success
+ */
+int fuse_opt_add_arg(struct fuse_args *args, const char *arg);
+
+/**
+ * Add an argument at the specified position in a NULL terminated
+ * argument vector
+ *
+ * Adds the argument to the N-th position. This is useful for adding
+ * options at the beginning of the array which must not come after the
+ * special '--' option.
+ *
+ * @param args is the structure containing the current argument list
+ * @param pos is the position at which to add the argument
+ * @param arg is the new argument to add
+ * @return -1 on allocation error, 0 on success
+ */
+int fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg);
+
+/**
+ * Free the contents of argument list
+ *
+ * The structure itself is not freed
+ *
+ * @param args is the structure containing the argument list
+ */
+void fuse_opt_free_args(struct fuse_args *args);
+
+
+/**
+ * Check if an option matches
+ *
+ * @param opts is the option description array
+ * @param opt is the option to match
+ * @return 1 if a match is found, 0 if not
+ */
+int fuse_opt_match(const struct fuse_opt opts[], const char *opt);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FUSE_OPT_H_ */
diff --git a/fuse/include/old/fuse.h b/fuse/include/old/fuse.h
new file mode 100644
index 000000000..3db0945a9
--- /dev/null
+++ b/fuse/include/old/fuse.h
@@ -0,0 +1,9 @@
+/*
+ This header is for compatibility with older software using FUSE.
+
+ Please use 'pkg-config --cflags fuse' to set include path. The
+ correct usage is still '#include <fuse.h>', not '#include
+ <fuse/fuse.h>'.
+*/
+
+#include "fuse/fuse.h"
diff --git a/fuse/include/stamp-h1 b/fuse/include/stamp-h1
new file mode 100644
index 000000000..b330768e9
--- /dev/null
+++ b/fuse/include/stamp-h1
@@ -0,0 +1 @@
+timestamp for include/config.h
diff --git a/fuse/include/sys/statvfs.h b/fuse/include/sys/statvfs.h
new file mode 100644
index 000000000..6e3e39fd5
--- /dev/null
+++ b/fuse/include/sys/statvfs.h
@@ -0,0 +1,18 @@
+#ifndef __STATVFS_H
+#define __STATVFS_H
+
+struct statvfs {
+ unsigned long f_bsize; /* file system block size */
+ unsigned long f_frsize; /* fragment size */
+ fsblkcnt_t f_blocks; /* size of fs in f_frsize units */
+ fsblkcnt_t f_bfree; /* # free blocks */
+ fsblkcnt_t f_bavail; /* # free blocks for non-root */
+ fsfilcnt_t f_files; /* # inodes */
+ fsfilcnt_t f_ffree; /* # free inodes */
+ fsfilcnt_t f_favail; /* # free inodes for non-root */
+ unsigned long f_fsid; /* file system ID */
+ unsigned long f_flag; /* mount flags */
+ unsigned long f_namemax; /* maximum filename length */
+};
+
+#endif
diff --git a/fuse/include/ulockmgr.h b/fuse/include/ulockmgr.h
new file mode 100644
index 000000000..ad555799f
--- /dev/null
+++ b/fuse/include/ulockmgr.h
@@ -0,0 +1,24 @@
+/*
+ libulockmgr: Userspace Lock Manager Library
+ Copyright (C) 2006 Miklos Szeredi <miklos@szeredi.hu>
+
+ This program can be distributed under the terms of the GNU LGPLv2.
+ See the file COPYING.LIB.
+*/
+
+#include <stdint.h>
+#include <fcntl.h>
+#include <sys/types.h>
+
+/**
+ * Perform POSIX locking operation
+ *
+ * @param fd the file descriptor
+ * @param cmd the locking command (F_GETFL, F_SETLK or F_SETLKW)
+ * @param lock the lock parameters
+ * @param owner the lock owner ID cookie
+ * @param owner_len length of the lock owner ID cookie
+ * @return 0 on success -errno on error
+ */
+int ulockmgr_op(int fd, int cmd, struct flock *lock, const void *owner,
+ size_t owner_len);
diff --git a/fuse/mount.c b/fuse/mount.c
new file mode 100644
index 000000000..af7218fa4
--- /dev/null
+++ b/fuse/mount.c
@@ -0,0 +1,638 @@
+/*
+ FUSE: Filesystem in Userspace
+ Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
+
+ This program can be distributed under the terms of the GNU LGPLv2.
+ See the file COPYING.LIB.
+*/
+
+#include "config.h"
+#include "fuse_i.h"
+#include "fuse_misc.h"
+#include "fuse_opt.h"
+#include "fuse_common_compat.h"
+#include "mount_util.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stddef.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/poll.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/wait.h>
+#include <sys/mount.h>
+
+#ifdef __NetBSD__
+#include <perfuse.h>
+
+#define MS_RDONLY MNT_RDONLY
+#define MS_NOSUID MNT_NOSUID
+#define MS_NODEV MNT_NODEV
+#define MS_NOEXEC MNT_NOEXEC
+#define MS_SYNCHRONOUS MNT_SYNCHRONOUS
+#define MS_NOATIME MNT_NOATIME
+
+
+#define umount2(mnt, flags) unmount(mnt, (flags == 2) ? MNT_FORCE : 0)
+#endif
+
+#define FUSERMOUNT_PROG "fusermount"
+#define FUSE_COMMFD_ENV "_FUSE_COMMFD"
+
+#ifndef HAVE_FORK
+#define fork() vfork()
+#endif
+
+#ifndef MS_DIRSYNC
+#define MS_DIRSYNC 128
+#endif
+
+enum {
+ KEY_KERN_FLAG,
+ KEY_KERN_OPT,
+ KEY_FUSERMOUNT_OPT,
+ KEY_SUBTYPE_OPT,
+ KEY_MTAB_OPT,
+ KEY_ALLOW_ROOT,
+ KEY_RO,
+ KEY_HELP,
+ KEY_VERSION,
+};
+
+struct mount_opts {
+ int allow_other;
+ int allow_root;
+ int ishelp;
+ int flags;
+ int nonempty;
+ int auto_unmount;
+ int blkdev;
+ char *fsname;
+ char *subtype;
+ char *subtype_opt;
+ char *mtab_opts;
+ char *fusermount_opts;
+ char *kernel_opts;
+};
+
+#define FUSE_MOUNT_OPT(t, p) { t, offsetof(struct mount_opts, p), 1 }
+
+static const struct fuse_opt fuse_mount_opts[] = {
+ FUSE_MOUNT_OPT("allow_other", allow_other),
+ FUSE_MOUNT_OPT("allow_root", allow_root),
+ FUSE_MOUNT_OPT("nonempty", nonempty),
+ FUSE_MOUNT_OPT("blkdev", blkdev),
+ FUSE_MOUNT_OPT("auto_unmount", auto_unmount),
+ FUSE_MOUNT_OPT("fsname=%s", fsname),
+ FUSE_MOUNT_OPT("subtype=%s", subtype),
+ FUSE_OPT_KEY("allow_other", KEY_KERN_OPT),
+ FUSE_OPT_KEY("allow_root", KEY_ALLOW_ROOT),
+ FUSE_OPT_KEY("nonempty", KEY_FUSERMOUNT_OPT),
+ FUSE_OPT_KEY("auto_unmount", KEY_FUSERMOUNT_OPT),
+ FUSE_OPT_KEY("blkdev", KEY_FUSERMOUNT_OPT),
+ FUSE_OPT_KEY("fsname=", KEY_FUSERMOUNT_OPT),
+ FUSE_OPT_KEY("subtype=", KEY_SUBTYPE_OPT),
+ FUSE_OPT_KEY("large_read", KEY_KERN_OPT),
+ FUSE_OPT_KEY("blksize=", KEY_KERN_OPT),
+ FUSE_OPT_KEY("default_permissions", KEY_KERN_OPT),
+ FUSE_OPT_KEY("max_read=", KEY_KERN_OPT),
+ FUSE_OPT_KEY("max_read=", FUSE_OPT_KEY_KEEP),
+ FUSE_OPT_KEY("user=", KEY_MTAB_OPT),
+ FUSE_OPT_KEY("-r", KEY_RO),
+ FUSE_OPT_KEY("ro", KEY_KERN_FLAG),
+ FUSE_OPT_KEY("rw", KEY_KERN_FLAG),
+ FUSE_OPT_KEY("suid", KEY_KERN_FLAG),
+ FUSE_OPT_KEY("nosuid", KEY_KERN_FLAG),
+ FUSE_OPT_KEY("dev", KEY_KERN_FLAG),
+ FUSE_OPT_KEY("nodev", KEY_KERN_FLAG),
+ FUSE_OPT_KEY("exec", KEY_KERN_FLAG),
+ FUSE_OPT_KEY("noexec", KEY_KERN_FLAG),
+ FUSE_OPT_KEY("async", KEY_KERN_FLAG),
+ FUSE_OPT_KEY("sync", KEY_KERN_FLAG),
+ FUSE_OPT_KEY("dirsync", KEY_KERN_FLAG),
+ FUSE_OPT_KEY("atime", KEY_KERN_FLAG),
+ FUSE_OPT_KEY("noatime", KEY_KERN_FLAG),
+ FUSE_OPT_KEY("-h", KEY_HELP),
+ FUSE_OPT_KEY("--help", KEY_HELP),
+ FUSE_OPT_KEY("-V", KEY_VERSION),
+ FUSE_OPT_KEY("--version", KEY_VERSION),
+ FUSE_OPT_END
+};
+
+static void mount_help(void)
+{
+ fprintf(stderr,
+" -o allow_other allow access to other users\n"
+" -o allow_root allow access to root\n"
+" -o auto_unmount auto unmount on process termination\n"
+" -o nonempty allow mounts over non-empty file/dir\n"
+" -o default_permissions enable permission checking by kernel\n"
+" -o fsname=NAME set filesystem name\n"
+" -o subtype=NAME set filesystem type\n"
+" -o large_read issue large read requests (2.4 only)\n"
+" -o max_read=N set maximum size of read requests\n"
+"\n");
+}
+
+#define FUSERMOUNT_DIR "/usr/bin"
+static void exec_fusermount(const char *argv[])
+{
+ execv(FUSERMOUNT_DIR "/" FUSERMOUNT_PROG, (char **) argv);
+ execvp(FUSERMOUNT_PROG, (char **) argv);
+}
+
+static void mount_version(void)
+{
+ int pid = fork();
+ if (!pid) {
+ const char *argv[] = { FUSERMOUNT_PROG, "--version", NULL };
+ exec_fusermount(argv);
+ _exit(1);
+ } else if (pid != -1)
+ waitpid(pid, NULL, 0);
+}
+
+struct mount_flags {
+ const char *opt;
+ unsigned long flag;
+ int on;
+};
+
+static const struct mount_flags mount_flags[] = {
+ {"rw", MS_RDONLY, 0},
+ {"ro", MS_RDONLY, 1},
+ {"suid", MS_NOSUID, 0},
+ {"nosuid", MS_NOSUID, 1},
+ {"dev", MS_NODEV, 0},
+ {"nodev", MS_NODEV, 1},
+ {"exec", MS_NOEXEC, 0},
+ {"noexec", MS_NOEXEC, 1},
+ {"async", MS_SYNCHRONOUS, 0},
+ {"sync", MS_SYNCHRONOUS, 1},
+ {"atime", MS_NOATIME, 0},
+ {"noatime", MS_NOATIME, 1},
+#ifndef __NetBSD__
+ {"dirsync", MS_DIRSYNC, 1},
+#endif
+ {NULL, 0, 0}
+};
+
+static void set_mount_flag(const char *s, int *flags)
+{
+ int i;
+
+ for (i = 0; mount_flags[i].opt != NULL; i++) {
+ const char *opt = mount_flags[i].opt;
+ if (strcmp(opt, s) == 0) {
+ if (mount_flags[i].on)
+ *flags |= mount_flags[i].flag;
+ else
+ *flags &= ~mount_flags[i].flag;
+ return;
+ }
+ }
+ fprintf(stderr, "fuse: internal error, can't find mount flag\n");
+ abort();
+}
+
+static int fuse_mount_opt_proc(void *data, const char *arg, int key,
+ struct fuse_args *outargs)
+{
+ struct mount_opts *mo = data;
+
+ switch (key) {
+ case KEY_ALLOW_ROOT:
+ if (fuse_opt_add_opt(&mo->kernel_opts, "allow_other") == -1 ||
+ fuse_opt_add_arg(outargs, "-oallow_root") == -1)
+ return -1;
+ return 0;
+
+ case KEY_RO:
+ arg = "ro";
+ /* fall through */
+ case KEY_KERN_FLAG:
+ set_mount_flag(arg, &mo->flags);
+ return 0;
+
+ case KEY_KERN_OPT:
+ return fuse_opt_add_opt(&mo->kernel_opts, arg);
+
+ case KEY_FUSERMOUNT_OPT:
+ return fuse_opt_add_opt_escaped(&mo->fusermount_opts, arg);
+
+ case KEY_SUBTYPE_OPT:
+ return fuse_opt_add_opt(&mo->subtype_opt, arg);
+
+ case KEY_MTAB_OPT:
+ return fuse_opt_add_opt(&mo->mtab_opts, arg);
+
+ case KEY_HELP:
+ mount_help();
+ mo->ishelp = 1;
+ break;
+
+ case KEY_VERSION:
+ mount_version();
+ mo->ishelp = 1;
+ break;
+ }
+ return 1;
+}
+
+/* return value:
+ * >= 0 => fd
+ * -1 => error
+ */
+static int receive_fd(int fd)
+{
+ struct msghdr msg;
+ struct iovec iov;
+ char buf[1];
+ int rv;
+ size_t ccmsg[CMSG_SPACE(sizeof(int)) / sizeof(size_t)];
+ struct cmsghdr *cmsg;
+
+ iov.iov_base = buf;
+ iov.iov_len = 1;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_name = 0;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ /* old BSD implementations should use msg_accrights instead of
+ * msg_control; the interface is different. */
+ msg.msg_control = ccmsg;
+ msg.msg_controllen = sizeof(ccmsg);
+
+ while(((rv = recvmsg(fd, &msg, 0)) == -1) && errno == EINTR);
+ if (rv == -1) {
+ perror("recvmsg");
+ return -1;
+ }
+ if(!rv) {
+ /* EOF */
+ return -1;
+ }
+
+ cmsg = CMSG_FIRSTHDR(&msg);
+ if (!cmsg->cmsg_type == SCM_RIGHTS) {
+ fprintf(stderr, "got control message of unknown type %d\n",
+ cmsg->cmsg_type);
+ return -1;
+ }
+ return *(int*)CMSG_DATA(cmsg);
+}
+
+void fuse_kern_unmount(const char *mountpoint, int fd)
+{
+ int res;
+ int pid;
+
+ if (!mountpoint)
+ return;
+
+ if (fd != -1) {
+ struct pollfd pfd;
+
+ pfd.fd = fd;
+ pfd.events = 0;
+ res = poll(&pfd, 1, 0);
+ /* If file poll returns POLLERR on the device file descriptor,
+ then the filesystem is already unmounted */
+ if (res == 1 && (pfd.revents & POLLERR))
+ return;
+
+ /* Need to close file descriptor, otherwise synchronous umount
+ would recurse into filesystem, and deadlock */
+ close(fd);
+ }
+
+ if (geteuid() == 0) {
+ fuse_mnt_umount("fuse", mountpoint, mountpoint, 1);
+ return;
+ }
+
+ res = umount2(mountpoint, 2);
+ if (res == 0)
+ return;
+
+ pid = fork();
+ if(pid == -1)
+ return;
+
+ if(pid == 0) {
+ const char *argv[] = { FUSERMOUNT_PROG, "-u", "-q", "-z",
+ "--", mountpoint, NULL };
+
+ exec_fusermount(argv);
+ _exit(1);
+ }
+ waitpid(pid, NULL, 0);
+}
+
+void fuse_unmount_compat22(const char *mountpoint)
+{
+ fuse_kern_unmount(mountpoint, -1);
+}
+
+static int fuse_mount_fusermount(const char *mountpoint, struct mount_opts *mo,
+ const char *opts, int quiet)
+{
+ int fds[2], pid;
+ int res;
+ int rv;
+
+ if (!mountpoint) {
+ fprintf(stderr, "fuse: missing mountpoint parameter\n");
+ return -1;
+ }
+
+ res = socketpair(PF_UNIX, SOCK_STREAM, 0, fds);
+ if(res == -1) {
+ perror("fuse: socketpair() failed");
+ return -1;
+ }
+
+ pid = fork();
+ if(pid == -1) {
+ perror("fuse: fork() failed");
+ close(fds[0]);
+ close(fds[1]);
+ return -1;
+ }
+
+ if(pid == 0) {
+ char env[10];
+ const char *argv[32];
+ int a = 0;
+
+ if (quiet) {
+ int fd = open("/dev/null", O_RDONLY);
+ if (fd != -1) {
+ dup2(fd, 1);
+ dup2(fd, 2);
+ }
+ }
+
+ argv[a++] = FUSERMOUNT_PROG;
+ if (opts) {
+ argv[a++] = "-o";
+ argv[a++] = opts;
+ }
+ argv[a++] = "--";
+ argv[a++] = mountpoint;
+ argv[a++] = NULL;
+
+ close(fds[1]);
+ fcntl(fds[0], F_SETFD, 0);
+ snprintf(env, sizeof(env), "%i", fds[0]);
+ setenv(FUSE_COMMFD_ENV, env, 1);
+ exec_fusermount(argv);
+ perror("fuse: failed to exec fusermount");
+ _exit(1);
+ }
+
+ close(fds[0]);
+ rv = receive_fd(fds[1]);
+
+ if (!mo->auto_unmount) {
+ /* with auto_unmount option fusermount will not exit until
+ this socket is closed */
+ close(fds[1]);
+ waitpid(pid, NULL, 0); /* bury zombie */
+ }
+
+ return rv;
+}
+
+int fuse_mount_compat22(const char *mountpoint, const char *opts)
+{
+ struct mount_opts mo;
+ memset(&mo, 0, sizeof(mo));
+ mo.flags = MS_NOSUID | MS_NODEV;
+
+ return fuse_mount_fusermount(mountpoint, &mo, opts, 0);
+}
+
+static int fuse_mount_sys(const char *mnt, struct mount_opts *mo,
+ const char *mnt_opts)
+{
+ char tmp[128];
+ const char *devname = "/dev/fuse";
+ char *source = NULL;
+ char *type = NULL;
+ struct stat stbuf;
+ int fd;
+ int res;
+
+ if (!mnt) {
+ fprintf(stderr, "fuse: missing mountpoint parameter\n");
+ return -1;
+ }
+
+ res = stat(mnt, &stbuf);
+ if (res == -1) {
+ fprintf(stderr ,"fuse: failed to access mountpoint %s: %s\n",
+ mnt, strerror(errno));
+ return -1;
+ }
+
+ if (!mo->nonempty) {
+ res = fuse_mnt_check_empty("fuse", mnt, stbuf.st_mode,
+ stbuf.st_size);
+ if (res == -1)
+ return -1;
+ }
+
+ if (mo->auto_unmount) {
+ /* Tell the caller to fallback to fusermount because
+ auto-unmount does not work otherwise. */
+ return -2;
+ }
+
+ fd = open(devname, O_RDWR);
+ if (fd == -1) {
+ if (errno == ENODEV || errno == ENOENT)
+ fprintf(stderr, "fuse: device not found, try 'modprobe fuse' first\n");
+ else
+ fprintf(stderr, "fuse: failed to open %s: %s\n",
+ devname, strerror(errno));
+ return -1;
+ }
+
+ snprintf(tmp, sizeof(tmp), "fd=%i,rootmode=%o,user_id=%i,group_id=%i",
+ fd, stbuf.st_mode & S_IFMT, getuid(), getgid());
+
+ res = fuse_opt_add_opt(&mo->kernel_opts, tmp);
+ if (res == -1)
+ goto out_close;
+
+ source = malloc((mo->fsname ? strlen(mo->fsname) : 0) +
+ (mo->subtype ? strlen(mo->subtype) : 0) +
+ strlen(devname) + 32);
+
+ type = malloc((mo->subtype ? strlen(mo->subtype) : 0) + 32);
+ if (!type || !source) {
+ fprintf(stderr, "fuse: failed to allocate memory\n");
+ goto out_close;
+ }
+
+ strcpy(type, mo->blkdev ? "fuseblk" : "fuse");
+ if (mo->subtype) {
+ strcat(type, ".");
+ strcat(type, mo->subtype);
+ }
+ strcpy(source,
+ mo->fsname ? mo->fsname : (mo->subtype ? mo->subtype : devname));
+
+ res = mount(source, mnt, type, mo->flags, mo->kernel_opts);
+ if (res == -1 && errno == ENODEV && mo->subtype) {
+ /* Probably missing subtype support */
+ strcpy(type, mo->blkdev ? "fuseblk" : "fuse");
+ if (mo->fsname) {
+ if (!mo->blkdev)
+ sprintf(source, "%s#%s", mo->subtype,
+ mo->fsname);
+ } else {
+ strcpy(source, type);
+ }
+ res = mount(source, mnt, type, mo->flags, mo->kernel_opts);
+ }
+ if (res == -1) {
+ /*
+ * Maybe kernel doesn't support unprivileged mounts, in this
+ * case try falling back to fusermount
+ */
+ if (errno == EPERM) {
+ res = -2;
+ } else {
+ int errno_save = errno;
+ if (mo->blkdev && errno == ENODEV &&
+ !fuse_mnt_check_fuseblk())
+ fprintf(stderr,
+ "fuse: 'fuseblk' support missing\n");
+ else
+ fprintf(stderr, "fuse: mount failed: %s\n",
+ strerror(errno_save));
+ }
+
+ goto out_close;
+ }
+
+#ifndef __NetBSD__
+#ifndef IGNORE_MTAB
+ if (geteuid() == 0) {
+ char *newmnt = fuse_mnt_resolve_path("fuse", mnt);
+ res = -1;
+ if (!newmnt)
+ goto out_umount;
+
+ res = fuse_mnt_add_mount("fuse", source, newmnt, type,
+ mnt_opts);
+ free(newmnt);
+ if (res == -1)
+ goto out_umount;
+ }
+#endif /* IGNORE_MTAB */
+#endif /* __NetBSD__ */
+ free(type);
+ free(source);
+
+ return fd;
+
+out_umount:
+ umount2(mnt, 2); /* lazy umount */
+out_close:
+ free(type);
+ free(source);
+ close(fd);
+ return res;
+}
+
+static int get_mnt_flag_opts(char **mnt_optsp, int flags)
+{
+ int i;
+
+ if (!(flags & MS_RDONLY) && fuse_opt_add_opt(mnt_optsp, "rw") == -1)
+ return -1;
+
+ for (i = 0; mount_flags[i].opt != NULL; i++) {
+ if (mount_flags[i].on && (flags & mount_flags[i].flag) &&
+ fuse_opt_add_opt(mnt_optsp, mount_flags[i].opt) == -1)
+ return -1;
+ }
+ return 0;
+}
+
+int fuse_kern_mount(const char *mountpoint, struct fuse_args *args)
+{
+ struct mount_opts mo;
+ int res = -1;
+ char *mnt_opts = NULL;
+
+ memset(&mo, 0, sizeof(mo));
+ mo.flags = MS_NOSUID | MS_NODEV;
+
+ if (args &&
+ fuse_opt_parse(args, &mo, fuse_mount_opts, fuse_mount_opt_proc) == -1)
+ return -1;
+
+ if (mo.allow_other && mo.allow_root) {
+ fprintf(stderr, "fuse: 'allow_other' and 'allow_root' options are mutually exclusive\n");
+ goto out;
+ }
+ res = 0;
+ if (mo.ishelp)
+ goto out;
+
+ res = -1;
+ if (get_mnt_flag_opts(&mnt_opts, mo.flags) == -1)
+ goto out;
+ if (mo.kernel_opts && fuse_opt_add_opt(&mnt_opts, mo.kernel_opts) == -1)
+ goto out;
+ if (mo.mtab_opts && fuse_opt_add_opt(&mnt_opts, mo.mtab_opts) == -1)
+ goto out;
+
+ res = fuse_mount_sys(mountpoint, &mo, mnt_opts);
+ if (res == -2) {
+ if (mo.fusermount_opts &&
+ fuse_opt_add_opt(&mnt_opts, mo.fusermount_opts) == -1)
+ goto out;
+
+ if (mo.subtype) {
+ char *tmp_opts = NULL;
+
+ res = -1;
+ if (fuse_opt_add_opt(&tmp_opts, mnt_opts) == -1 ||
+ fuse_opt_add_opt(&tmp_opts, mo.subtype_opt) == -1) {
+ free(tmp_opts);
+ goto out;
+ }
+
+ res = fuse_mount_fusermount(mountpoint, &mo, tmp_opts, 1);
+ free(tmp_opts);
+ if (res == -1)
+ res = fuse_mount_fusermount(mountpoint, &mo,
+ mnt_opts, 0);
+ } else {
+ res = fuse_mount_fusermount(mountpoint, &mo, mnt_opts, 0);
+ }
+ }
+out:
+ free(mnt_opts);
+ free(mo.fsname);
+ free(mo.subtype);
+ free(mo.fusermount_opts);
+ free(mo.subtype_opt);
+ free(mo.kernel_opts);
+ free(mo.mtab_opts);
+ return res;
+}
+
+FUSE_SYMVER(".symver fuse_mount_compat22,fuse_mount@FUSE_2.2");
+FUSE_SYMVER(".symver fuse_unmount_compat22,fuse_unmount@FUSE_2.2");
diff --git a/fuse/mount_bsd.c b/fuse/mount_bsd.c
new file mode 100644
index 000000000..62443ac25
--- /dev/null
+++ b/fuse/mount_bsd.c
@@ -0,0 +1,388 @@
+/*
+ FUSE: Filesystem in Userspace
+ Copyright (C) 2005-2008 Csaba Henk <csaba.henk@creo.hu>
+
+ This program can be distributed under the terms of the GNU LGPLv2.
+ See the file COPYING.LIB.
+*/
+
+#include "fuse_i.h"
+#include "fuse_misc.h"
+#include "fuse_opt.h"
+
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/sysctl.h>
+#include <sys/user.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stddef.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <paths.h>
+#include <limits.h>
+
+#define FUSERMOUNT_PROG "mount_fusefs"
+#define FUSE_DEV_TRUNK "/dev/fuse"
+
+enum {
+ KEY_ALLOW_ROOT,
+ KEY_RO,
+ KEY_HELP,
+ KEY_VERSION,
+ KEY_KERN
+};
+
+struct mount_opts {
+ int allow_other;
+ int allow_root;
+ int ishelp;
+ char *kernel_opts;
+};
+
+#define FUSE_DUAL_OPT_KEY(templ, key) \
+ FUSE_OPT_KEY(templ, key), FUSE_OPT_KEY("no" templ, key)
+
+static const struct fuse_opt fuse_mount_opts[] = {
+ { "allow_other", offsetof(struct mount_opts, allow_other), 1 },
+ { "allow_root", offsetof(struct mount_opts, allow_root), 1 },
+ FUSE_OPT_KEY("allow_root", KEY_ALLOW_ROOT),
+ FUSE_OPT_KEY("-r", KEY_RO),
+ FUSE_OPT_KEY("-h", KEY_HELP),
+ FUSE_OPT_KEY("--help", KEY_HELP),
+ FUSE_OPT_KEY("-V", KEY_VERSION),
+ FUSE_OPT_KEY("--version", KEY_VERSION),
+ /* standard FreeBSD mount options */
+ FUSE_DUAL_OPT_KEY("dev", KEY_KERN),
+ FUSE_DUAL_OPT_KEY("async", KEY_KERN),
+ FUSE_DUAL_OPT_KEY("atime", KEY_KERN),
+ FUSE_DUAL_OPT_KEY("dev", KEY_KERN),
+ FUSE_DUAL_OPT_KEY("exec", KEY_KERN),
+ FUSE_DUAL_OPT_KEY("suid", KEY_KERN),
+ FUSE_DUAL_OPT_KEY("symfollow", KEY_KERN),
+ FUSE_DUAL_OPT_KEY("rdonly", KEY_KERN),
+ FUSE_DUAL_OPT_KEY("sync", KEY_KERN),
+ FUSE_DUAL_OPT_KEY("union", KEY_KERN),
+ FUSE_DUAL_OPT_KEY("userquota", KEY_KERN),
+ FUSE_DUAL_OPT_KEY("groupquota", KEY_KERN),
+ FUSE_DUAL_OPT_KEY("clusterr", KEY_KERN),
+ FUSE_DUAL_OPT_KEY("clusterw", KEY_KERN),
+ FUSE_DUAL_OPT_KEY("suiddir", KEY_KERN),
+ FUSE_DUAL_OPT_KEY("snapshot", KEY_KERN),
+ FUSE_DUAL_OPT_KEY("multilabel", KEY_KERN),
+ FUSE_DUAL_OPT_KEY("acls", KEY_KERN),
+ FUSE_DUAL_OPT_KEY("force", KEY_KERN),
+ FUSE_DUAL_OPT_KEY("update", KEY_KERN),
+ FUSE_DUAL_OPT_KEY("ro", KEY_KERN),
+ FUSE_DUAL_OPT_KEY("rw", KEY_KERN),
+ FUSE_DUAL_OPT_KEY("auto", KEY_KERN),
+ /* options supported under both Linux and FBSD */
+ FUSE_DUAL_OPT_KEY("allow_other", KEY_KERN),
+ FUSE_DUAL_OPT_KEY("default_permissions",KEY_KERN),
+ FUSE_OPT_KEY("max_read=", KEY_KERN),
+ FUSE_OPT_KEY("subtype=", KEY_KERN),
+ /* FBSD FUSE specific mount options */
+ FUSE_DUAL_OPT_KEY("private", KEY_KERN),
+ FUSE_DUAL_OPT_KEY("neglect_shares", KEY_KERN),
+ FUSE_DUAL_OPT_KEY("push_symlinks_in", KEY_KERN),
+ FUSE_OPT_KEY("nosync_unmount", KEY_KERN),
+ /* stock FBSD mountopt parsing routine lets anything be negated... */
+ /*
+ * Linux specific mount options, but let just the mount util
+ * handle them
+ */
+ FUSE_OPT_KEY("fsname=", KEY_KERN),
+ FUSE_OPT_KEY("nonempty", KEY_KERN),
+ FUSE_OPT_KEY("large_read", KEY_KERN),
+ FUSE_OPT_END
+};
+
+static void mount_help(void)
+{
+ fprintf(stderr,
+ " -o allow_root allow access to root\n"
+ );
+ system(FUSERMOUNT_PROG " --help");
+ fputc('\n', stderr);
+}
+
+static void mount_version(void)
+{
+ system(FUSERMOUNT_PROG " --version");
+}
+
+static int fuse_mount_opt_proc(void *data, const char *arg, int key,
+ struct fuse_args *outargs)
+{
+ struct mount_opts *mo = data;
+
+ switch (key) {
+ case KEY_ALLOW_ROOT:
+ if (fuse_opt_add_opt(&mo->kernel_opts, "allow_other") == -1 ||
+ fuse_opt_add_arg(outargs, "-oallow_root") == -1)
+ return -1;
+ return 0;
+
+ case KEY_RO:
+ arg = "ro";
+ /* fall through */
+
+ case KEY_KERN:
+ return fuse_opt_add_opt(&mo->kernel_opts, arg);
+
+ case KEY_HELP:
+ mount_help();
+ mo->ishelp = 1;
+ break;
+
+ case KEY_VERSION:
+ mount_version();
+ mo->ishelp = 1;
+ break;
+ }
+ return 1;
+}
+
+void fuse_unmount_compat22(const char *mountpoint)
+{
+ char dev[128];
+ char *ssc, *umount_cmd;
+ FILE *sf;
+ int rv;
+ char seekscript[] =
+ /* error message is annoying in help output */
+ "exec 2>/dev/null; "
+ "/usr/bin/fstat " FUSE_DEV_TRUNK "* | "
+ "/usr/bin/awk 'BEGIN{ getline; if (! ($3 == \"PID\" && $10 == \"NAME\")) exit 1; }; "
+ " { if ($3 == %d) print $10; }' | "
+ "/usr/bin/sort | "
+ "/usr/bin/uniq | "
+ "/usr/bin/awk '{ i += 1; if (i > 1){ exit 1; }; printf; }; END{ if (i == 0) exit 1; }'";
+
+ (void) mountpoint;
+
+ /*
+ * If we don't know the fd, we have to resort to the scripted
+ * solution -- iterating over the fd-s is unpractical, as we
+ * don't know how many of open files we have. (This could be
+ * looked up in procfs -- however, that's optional on FBSD; or
+ * read out from the kmem -- however, that's bound to
+ * privileges (in fact, that's what happens when we call the
+ * setgid kmem fstat(1) utility).
+ */
+ if (asprintf(&ssc, seekscript, getpid()) == -1)
+ return;
+
+ errno = 0;
+ sf = popen(ssc, "r");
+ free(ssc);
+ if (! sf)
+ return;
+
+ fgets(dev, sizeof(dev), sf);
+ rv = pclose(sf);
+ if (rv)
+ return;
+
+ if (asprintf(&umount_cmd, "/sbin/umount %s", dev) == -1)
+ return;
+ system(umount_cmd);
+ free(umount_cmd);
+}
+
+static void do_unmount(char *dev, int fd)
+{
+ char device_path[SPECNAMELEN + 12];
+ const char *argv[4];
+ const char umount_cmd[] = "/sbin/umount";
+ pid_t pid;
+
+ snprintf(device_path, SPECNAMELEN + 12, _PATH_DEV "%s", dev);
+
+ argv[0] = umount_cmd;
+ argv[1] = "-f";
+ argv[2] = device_path;
+ argv[3] = NULL;
+
+ pid = fork();
+
+ if (pid == -1)
+ return;
+
+ if (pid == 0) {
+ close(fd);
+ execvp(umount_cmd, (char **)argv);
+ exit(1);
+ }
+
+ waitpid(pid, NULL, 0);
+}
+
+void fuse_kern_unmount(const char *mountpoint, int fd)
+{
+ char *ep, dev[128];
+ struct stat sbuf;
+
+ (void)mountpoint;
+
+ if (fstat(fd, &sbuf) == -1)
+ return;
+
+ devname_r(sbuf.st_rdev, S_IFCHR, dev, 128);
+
+ if (strncmp(dev, "fuse", 4))
+ return;
+
+ strtol(dev + 4, &ep, 10);
+ if (*ep != '\0')
+ return;
+
+ do_unmount(dev, fd);
+}
+
+/* Check if kernel is doing init in background */
+static int init_backgrounded(void)
+{
+ unsigned ibg, len;
+
+ len = sizeof(ibg);
+
+ if (sysctlbyname("vfs.fuse.init_backgrounded", &ibg, &len, NULL, 0))
+ return 0;
+
+ return ibg;
+}
+
+
+static int fuse_mount_core(const char *mountpoint, const char *opts)
+{
+ const char *mountprog = FUSERMOUNT_PROG;
+ int fd;
+ char *fdnam, *dev;
+ pid_t pid, cpid;
+ int status;
+
+ fdnam = getenv("FUSE_DEV_FD");
+
+ if (fdnam) {
+ char *ep;
+
+ fd = strtol(fdnam, &ep, 10);
+
+ if (*ep != '\0') {
+ fprintf(stderr, "invalid value given in FUSE_DEV_FD\n");
+ return -1;
+ }
+
+ if (fd < 0)
+ return -1;
+
+ goto mount;
+ }
+
+ dev = getenv("FUSE_DEV_NAME");
+
+ if (! dev)
+ dev = (char *)FUSE_DEV_TRUNK;
+
+ if ((fd = open(dev, O_RDWR)) < 0) {
+ perror("fuse: failed to open fuse device");
+ return -1;
+ }
+
+mount:
+ if (getenv("FUSE_NO_MOUNT") || ! mountpoint)
+ goto out;
+
+ pid = fork();
+ cpid = pid;
+
+ if (pid == -1) {
+ perror("fuse: fork() failed");
+ close(fd);
+ return -1;
+ }
+
+ if (pid == 0) {
+ if (! init_backgrounded()) {
+ /*
+ * If init is not backgrounded, we have to
+ * call the mount util backgrounded, to avoid
+ * deadlock.
+ */
+
+ pid = fork();
+
+ if (pid == -1) {
+ perror("fuse: fork() failed");
+ close(fd);
+ exit(1);
+ }
+ }
+
+ if (pid == 0) {
+ const char *argv[32];
+ int a = 0;
+
+ if (! fdnam && asprintf(&fdnam, "%d", fd) == -1) {
+ perror("fuse: failed to assemble mount arguments");
+ exit(1);
+ }
+
+ argv[a++] = mountprog;
+ if (opts) {
+ argv[a++] = "-o";
+ argv[a++] = opts;
+ }
+ argv[a++] = fdnam;
+ argv[a++] = mountpoint;
+ argv[a++] = NULL;
+ execvp(mountprog, (char **) argv);
+ perror("fuse: failed to exec mount program");
+ exit(1);
+ }
+
+ exit(0);
+ }
+
+ if (waitpid(cpid, &status, 0) == -1 || WEXITSTATUS(status) != 0) {
+ perror("fuse: failed to mount file system");
+ close(fd);
+ return -1;
+ }
+
+out:
+ return fd;
+}
+
+int fuse_kern_mount(const char *mountpoint, struct fuse_args *args)
+{
+ struct mount_opts mo;
+ int res = -1;
+
+ memset(&mo, 0, sizeof(mo));
+ /* mount util should not try to spawn the daemon */
+ setenv("MOUNT_FUSEFS_SAFE", "1", 1);
+ /* to notify the mount util it's called from lib */
+ setenv("MOUNT_FUSEFS_CALL_BY_LIB", "1", 1);
+
+ if (args &&
+ fuse_opt_parse(args, &mo, fuse_mount_opts, fuse_mount_opt_proc) == -1)
+ return -1;
+
+ if (mo.allow_other && mo.allow_root) {
+ fprintf(stderr, "fuse: 'allow_other' and 'allow_root' options are mutually exclusive\n");
+ goto out;
+ }
+ if (mo.ishelp)
+ return 0;
+
+ res = fuse_mount_core(mountpoint, mo.kernel_opts);
+out:
+ free(mo.kernel_opts);
+ return res;
+}
+
+FUSE_SYMVER(".symver fuse_unmount_compat22,fuse_unmount@FUSE_2.2");
diff --git a/fuse/mount_util.c b/fuse/mount_util.c
new file mode 100644
index 000000000..bfd801fff
--- /dev/null
+++ b/fuse/mount_util.c
@@ -0,0 +1,350 @@
+/*
+ FUSE: Filesystem in Userspace
+ Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
+
+ This program can be distributed under the terms of the GNU LGPLv2.
+ See the file COPYING.LIB.
+*/
+
+#include "mount_util.h"
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <mntent.h>
+#include <paths.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/mount.h>
+#include <sys/param.h>
+
+#ifdef __NetBSD__
+#define umount2(mnt, flags) unmount(mnt, (flags == 2) ? MNT_FORCE : 0)
+#define mtab_needs_update(mnt) 0
+#else
+static int mtab_needs_update(const char *mnt)
+{
+ int res;
+ struct stat stbuf;
+
+ /* If mtab is within new mount, don't touch it */
+ if (strncmp(mnt, _PATH_MOUNTED, strlen(mnt)) == 0 &&
+ _PATH_MOUNTED[strlen(mnt)] == '/')
+ return 0;
+
+ /*
+ * Skip mtab update if /etc/mtab:
+ *
+ * - doesn't exist,
+ * - is a symlink,
+ * - is on a read-only filesystem.
+ */
+ res = lstat(_PATH_MOUNTED, &stbuf);
+ if (res == -1) {
+ if (errno == ENOENT)
+ return 0;
+ } else {
+ uid_t ruid;
+ int err;
+
+ if (S_ISLNK(stbuf.st_mode))
+ return 0;
+
+ ruid = getuid();
+ if (ruid != 0)
+ setreuid(0, -1);
+
+ res = access(_PATH_MOUNTED, W_OK);
+ err = (res == -1) ? errno : 0;
+ if (ruid != 0)
+ setreuid(ruid, -1);
+
+ if (err == EROFS)
+ return 0;
+ }
+
+ return 1;
+}
+#endif /* __NetBSD__ */
+
+static int add_mount(const char *progname, const char *fsname,
+ const char *mnt, const char *type, const char *opts)
+{
+ int res;
+ int status;
+ sigset_t blockmask;
+ sigset_t oldmask;
+
+ sigemptyset(&blockmask);
+ sigaddset(&blockmask, SIGCHLD);
+ res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
+ if (res == -1) {
+ fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
+ return -1;
+ }
+
+ res = fork();
+ if (res == -1) {
+ fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
+ goto out_restore;
+ }
+ if (res == 0) {
+ sigprocmask(SIG_SETMASK, &oldmask, NULL);
+ setuid(geteuid());
+ execl("/bin/mount", "/bin/mount", "--no-canonicalize", "-i",
+ "-f", "-t", type, "-o", opts, fsname, mnt, NULL);
+ fprintf(stderr, "%s: failed to execute /bin/mount: %s\n",
+ progname, strerror(errno));
+ exit(1);
+ }
+ res = waitpid(res, &status, 0);
+ if (res == -1)
+ fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
+
+ if (status != 0)
+ res = -1;
+
+ out_restore:
+ sigprocmask(SIG_SETMASK, &oldmask, NULL);
+
+ return res;
+}
+
+int fuse_mnt_add_mount(const char *progname, const char *fsname,
+ const char *mnt, const char *type, const char *opts)
+{
+ if (!mtab_needs_update(mnt))
+ return 0;
+
+ return add_mount(progname, fsname, mnt, type, opts);
+}
+
+static int exec_umount(const char *progname, const char *rel_mnt, int lazy)
+{
+ int res;
+ int status;
+ sigset_t blockmask;
+ sigset_t oldmask;
+
+ sigemptyset(&blockmask);
+ sigaddset(&blockmask, SIGCHLD);
+ res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
+ if (res == -1) {
+ fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
+ return -1;
+ }
+
+ res = fork();
+ if (res == -1) {
+ fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
+ goto out_restore;
+ }
+ if (res == 0) {
+ sigprocmask(SIG_SETMASK, &oldmask, NULL);
+ setuid(geteuid());
+ execl("/bin/umount", "/bin/umount", "-i", rel_mnt,
+ lazy ? "-l" : NULL, NULL);
+ fprintf(stderr, "%s: failed to execute /bin/umount: %s\n",
+ progname, strerror(errno));
+ exit(1);
+ }
+ res = waitpid(res, &status, 0);
+ if (res == -1)
+ fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
+
+ if (status != 0) {
+ res = -1;
+ }
+
+ out_restore:
+ sigprocmask(SIG_SETMASK, &oldmask, NULL);
+ return res;
+
+}
+
+int fuse_mnt_umount(const char *progname, const char *abs_mnt,
+ const char *rel_mnt, int lazy)
+{
+ int res;
+
+ if (!mtab_needs_update(abs_mnt)) {
+ res = umount2(rel_mnt, lazy ? 2 : 0);
+ if (res == -1)
+ fprintf(stderr, "%s: failed to unmount %s: %s\n",
+ progname, abs_mnt, strerror(errno));
+ return res;
+ }
+
+ return exec_umount(progname, rel_mnt, lazy);
+}
+
+static int remove_mount(const char *progname, const char *mnt)
+{
+ int res;
+ int status;
+ sigset_t blockmask;
+ sigset_t oldmask;
+
+ sigemptyset(&blockmask);
+ sigaddset(&blockmask, SIGCHLD);
+ res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
+ if (res == -1) {
+ fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
+ return -1;
+ }
+
+ res = fork();
+ if (res == -1) {
+ fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
+ goto out_restore;
+ }
+ if (res == 0) {
+ sigprocmask(SIG_SETMASK, &oldmask, NULL);
+ setuid(geteuid());
+ execl("/bin/umount", "/bin/umount", "--no-canonicalize", "-i",
+ "--fake", mnt, NULL);
+ fprintf(stderr, "%s: failed to execute /bin/umount: %s\n",
+ progname, strerror(errno));
+ exit(1);
+ }
+ res = waitpid(res, &status, 0);
+ if (res == -1)
+ fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
+
+ if (status != 0)
+ res = -1;
+
+ out_restore:
+ sigprocmask(SIG_SETMASK, &oldmask, NULL);
+ return res;
+}
+
+int fuse_mnt_remove_mount(const char *progname, const char *mnt)
+{
+ if (!mtab_needs_update(mnt))
+ return 0;
+
+ return remove_mount(progname, mnt);
+}
+
+char *fuse_mnt_resolve_path(const char *progname, const char *orig)
+{
+ char buf[PATH_MAX];
+ char *copy;
+ char *dst;
+ char *end;
+ char *lastcomp;
+ const char *toresolv;
+
+ if (!orig[0]) {
+ fprintf(stderr, "%s: invalid mountpoint '%s'\n", progname,
+ orig);
+ return NULL;
+ }
+
+ copy = strdup(orig);
+ if (copy == NULL) {
+ fprintf(stderr, "%s: failed to allocate memory\n", progname);
+ return NULL;
+ }
+
+ toresolv = copy;
+ lastcomp = NULL;
+ for (end = copy + strlen(copy) - 1; end > copy && *end == '/'; end --);
+ if (end[0] != '/') {
+ char *tmp;
+ end[1] = '\0';
+ tmp = strrchr(copy, '/');
+ if (tmp == NULL) {
+ lastcomp = copy;
+ toresolv = ".";
+ } else {
+ lastcomp = tmp + 1;
+ if (tmp == copy)
+ toresolv = "/";
+ }
+ if (strcmp(lastcomp, ".") == 0 || strcmp(lastcomp, "..") == 0) {
+ lastcomp = NULL;
+ toresolv = copy;
+ }
+ else if (tmp)
+ tmp[0] = '\0';
+ }
+ if (realpath(toresolv, buf) == NULL) {
+ fprintf(stderr, "%s: bad mount point %s: %s\n", progname, orig,
+ strerror(errno));
+ free(copy);
+ return NULL;
+ }
+ if (lastcomp == NULL)
+ dst = strdup(buf);
+ else {
+ dst = (char *) malloc(strlen(buf) + 1 + strlen(lastcomp) + 1);
+ if (dst) {
+ unsigned buflen = strlen(buf);
+ if (buflen && buf[buflen-1] == '/')
+ sprintf(dst, "%s%s", buf, lastcomp);
+ else
+ sprintf(dst, "%s/%s", buf, lastcomp);
+ }
+ }
+ free(copy);
+ if (dst == NULL)
+ fprintf(stderr, "%s: failed to allocate memory\n", progname);
+ return dst;
+}
+
+int fuse_mnt_check_empty(const char *progname, const char *mnt,
+ mode_t rootmode, off64_t rootsize)
+{
+ int isempty = 1;
+
+ if (S_ISDIR(rootmode)) {
+ struct dirent *ent;
+ DIR *dp = opendir(mnt);
+ if (dp == NULL) {
+ fprintf(stderr,
+ "%s: failed to open mountpoint for reading: %s\n",
+ progname, strerror(errno));
+ return -1;
+ }
+ while ((ent = readdir(dp)) != NULL) {
+ if (strcmp(ent->d_name, ".") != 0 &&
+ strcmp(ent->d_name, "..") != 0) {
+ isempty = 0;
+ break;
+ }
+ }
+ closedir(dp);
+ } else if (rootsize)
+ isempty = 0;
+
+ if (!isempty) {
+ fprintf(stderr, "%s: mountpoint is not empty\n", progname);
+ fprintf(stderr, "%s: if you are sure this is safe, use the 'nonempty' mount option\n", progname);
+ return -1;
+ }
+ return 0;
+}
+
+int fuse_mnt_check_fuseblk(void)
+{
+ char buf[256];
+ FILE *f = fopen("/proc/filesystems", "r");
+ if (!f)
+ return 1;
+
+ while (fgets(buf, sizeof(buf), f))
+ if (strstr(buf, "fuseblk\n")) {
+ fclose(f);
+ return 1;
+ }
+
+ fclose(f);
+ return 0;
+}
diff --git a/fuse/mount_util.h b/fuse/mount_util.h
new file mode 100644
index 000000000..f89c1154d
--- /dev/null
+++ b/fuse/mount_util.h
@@ -0,0 +1,19 @@
+/*
+ FUSE: Filesystem in Userspace
+ Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
+
+ This program can be distributed under the terms of the GNU LGPLv2.
+ See the file COPYING.LIB.
+*/
+
+#include <sys/types.h>
+
+int fuse_mnt_add_mount(const char *progname, const char *fsname,
+ const char *mnt, const char *type, const char *opts);
+int fuse_mnt_remove_mount(const char *progname, const char *mnt);
+int fuse_mnt_umount(const char *progname, const char *abs_mnt,
+ const char *rel_mnt, int lazy);
+char *fuse_mnt_resolve_path(const char *progname, const char *orig);
+int fuse_mnt_check_empty(const char *progname, const char *mnt,
+ mode_t rootmode, off64_t rootsize);
+int fuse_mnt_check_fuseblk(void);
diff --git a/fuse/ulockmgr.c b/fuse/ulockmgr.c
new file mode 100644
index 000000000..f0523ae70
--- /dev/null
+++ b/fuse/ulockmgr.c
@@ -0,0 +1,444 @@
+/*
+ libulockmgr: Userspace Lock Manager Library
+ Copyright (C) 2006 Miklos Szeredi <miklos@szeredi.hu>
+
+ This program can be distributed under the terms of the GNU LGPLv2.
+ See the file COPYING.LIB
+*/
+
+/* #define DEBUG 1 */
+
+#include "ulockmgr.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <errno.h>
+#include <assert.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+
+struct message {
+ unsigned intr : 1;
+ unsigned nofd : 1;
+ pthread_t thr;
+ int cmd;
+ int fd;
+ struct flock lock;
+ int error;
+};
+
+struct fd_store {
+ struct fd_store *next;
+ int fd;
+ int inuse;
+};
+
+struct owner {
+ struct owner *next;
+ struct owner *prev;
+ struct fd_store *fds;
+ void *id;
+ size_t id_len;
+ int cfd;
+};
+
+static pthread_mutex_t ulockmgr_lock;
+static int ulockmgr_cfd = -1;
+static struct owner owner_list = { .next = &owner_list, .prev = &owner_list };
+
+#define MAX_SEND_FDS 2
+
+static void list_del_owner(struct owner *owner)
+{
+ struct owner *prev = owner->prev;
+ struct owner *next = owner->next;
+ prev->next = next;
+ next->prev = prev;
+}
+
+static void list_add_owner(struct owner *owner, struct owner *next)
+{
+ struct owner *prev = next->prev;
+ owner->next = next;
+ owner->prev = prev;
+ prev->next = owner;
+ next->prev = owner;
+}
+
+/*
+ * There's a bug in the linux kernel (< 2.6.22) recv() implementation
+ * on AF_UNIX, SOCK_STREAM sockets, that could cause it to return
+ * zero, even if data was available. Retrying the recv will return
+ * the data in this case.
+ */
+static int do_recv(int sock, void *buf, size_t len, int flags)
+{
+ int res = recv(sock, buf, len, flags);
+ if (res == 0)
+ res = recv(sock, buf, len, flags);
+
+ return res;
+}
+
+static int ulockmgr_send_message(int sock, void *buf, size_t buflen,
+ int *fdp, int numfds)
+{
+ struct msghdr msg;
+ struct cmsghdr *p_cmsg;
+ struct iovec vec;
+ size_t cmsgbuf[CMSG_SPACE(sizeof(int) * MAX_SEND_FDS) / sizeof(size_t)];
+ int res;
+
+ assert(numfds <= MAX_SEND_FDS);
+ msg.msg_control = cmsgbuf;
+ msg.msg_controllen = sizeof(cmsgbuf);
+ p_cmsg = CMSG_FIRSTHDR(&msg);
+ p_cmsg->cmsg_level = SOL_SOCKET;
+ p_cmsg->cmsg_type = SCM_RIGHTS;
+ p_cmsg->cmsg_len = CMSG_LEN(sizeof(int) * numfds);
+ memcpy(CMSG_DATA(p_cmsg), fdp, sizeof(int) * numfds);
+ msg.msg_controllen = p_cmsg->cmsg_len;
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &vec;
+ msg.msg_iovlen = 1;
+ msg.msg_flags = 0;
+ vec.iov_base = buf;
+ vec.iov_len = buflen;
+ res = sendmsg(sock, &msg, MSG_NOSIGNAL);
+ if (res == -1) {
+ perror("libulockmgr: sendmsg");
+ return -1;
+ }
+ if ((size_t) res != buflen) {
+ fprintf(stderr, "libulockmgr: sendmsg short\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int ulockmgr_start_daemon(void)
+{
+ int sv[2];
+ int res;
+ char tmp[64];
+
+ res = socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
+ if (res == -1) {
+ perror("libulockmgr: socketpair");
+ return -1;
+ }
+ snprintf(tmp, sizeof(tmp), "exec ulockmgr_server %i", sv[0]);
+ res = system(tmp);
+ close(sv[0]);
+ if (res == -1 || !WIFEXITED(res) || WEXITSTATUS(res) != 0) {
+ close(sv[1]);
+ return -1;
+ }
+ ulockmgr_cfd = sv[1];
+ return 0;
+}
+
+static struct owner *ulockmgr_new_owner(const void *id, size_t id_len)
+{
+ int sv[2];
+ int res;
+ char c = 'm';
+ struct owner *o;
+
+ if (ulockmgr_cfd == -1 && ulockmgr_start_daemon() == -1)
+ return NULL;
+
+ o = calloc(1, sizeof(struct owner) + id_len);
+ if (!o) {
+ fprintf(stderr, "libulockmgr: failed to allocate memory\n");
+ return NULL;
+ }
+ o->id = o + 1;
+ o->id_len = id_len;
+ res = socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
+ if (res == -1) {
+ perror("libulockmgr: socketpair");
+ goto out_free;
+ }
+ res = ulockmgr_send_message(ulockmgr_cfd, &c, sizeof(c), &sv[0], 1);
+ close(sv[0]);
+ if (res == -1) {
+ close(ulockmgr_cfd);
+ ulockmgr_cfd = -1;
+ goto out_close;
+ }
+
+ o->cfd = sv[1];
+ memcpy(o->id, id, id_len);
+ list_add_owner(o, &owner_list);
+
+ return o;
+
+out_close:
+ close(sv[1]);
+out_free:
+ free(o);
+ return NULL;
+}
+
+static int ulockmgr_send_request(struct message *msg, const void *id,
+ size_t id_len)
+{
+ int sv[2];
+ int cfd;
+ struct owner *o;
+ struct fd_store *f = NULL;
+ struct fd_store *newf = NULL;
+ struct fd_store **fp;
+ int fd = msg->fd;
+ int cmd = msg->cmd;
+ int res;
+ int unlockall = (cmd == F_SETLK && msg->lock.l_type == F_UNLCK &&
+ msg->lock.l_start == 0 && msg->lock.l_len == 0);
+
+ for (o = owner_list.next; o != &owner_list; o = o->next)
+ if (o->id_len == id_len && memcmp(o->id, id, id_len) == 0)
+ break;
+
+ if (o == &owner_list)
+ o = NULL;
+
+ if (!o && cmd != F_GETLK && msg->lock.l_type != F_UNLCK)
+ o = ulockmgr_new_owner(id, id_len);
+
+ if (!o) {
+ if (cmd == F_GETLK) {
+ res = fcntl(msg->fd, F_GETLK, &msg->lock);
+ return (res == -1) ? -errno : 0;
+ } else if (msg->lock.l_type == F_UNLCK)
+ return 0;
+ else
+ return -ENOLCK;
+ }
+
+ if (unlockall)
+ msg->nofd = 1;
+ else {
+ for (fp = &o->fds; *fp; fp = &(*fp)->next) {
+ f = *fp;
+ if (f->fd == fd) {
+ msg->nofd = 1;
+ break;
+ }
+ }
+ }
+
+ if (!msg->nofd) {
+ newf = f = calloc(1, sizeof(struct fd_store));
+ if (!f) {
+ fprintf(stderr, "libulockmgr: failed to allocate memory\n");
+ return -ENOLCK;
+ }
+ }
+
+ res = socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
+ if (res == -1) {
+ perror("libulockmgr: socketpair");
+ free(newf);
+ return -ENOLCK;
+ }
+
+ cfd = sv[1];
+ sv[1] = msg->fd;
+ res = ulockmgr_send_message(o->cfd, msg, sizeof(struct message), sv,
+ msg->nofd ? 1 : 2);
+ close(sv[0]);
+ if (res == -1) {
+ free(newf);
+ close(cfd);
+ return -EIO;
+ }
+
+ if (newf) {
+ newf->fd = msg->fd;
+ newf->next = o->fds;
+ o->fds = newf;
+ }
+ if (f)
+ f->inuse++;
+
+ res = do_recv(cfd, msg, sizeof(struct message), MSG_WAITALL);
+ if (res == -1) {
+ perror("libulockmgr: recv");
+ msg->error = EIO;
+ } else if (res != sizeof(struct message)) {
+ fprintf(stderr, "libulockmgr: recv short\n");
+ msg->error = EIO;
+ } else if (cmd == F_SETLKW && msg->error == EAGAIN) {
+ pthread_mutex_unlock(&ulockmgr_lock);
+ while (1) {
+ sigset_t old;
+ sigset_t unblock;
+ int errno_save;
+
+ sigemptyset(&unblock);
+ sigaddset(&unblock, SIGUSR1);
+ pthread_sigmask(SIG_UNBLOCK, &unblock, &old);
+ res = do_recv(cfd, msg, sizeof(struct message),
+ MSG_WAITALL);
+ errno_save = errno;
+ pthread_sigmask(SIG_SETMASK, &old, NULL);
+ if (res == sizeof(struct message))
+ break;
+ else if (res >= 0) {
+ fprintf(stderr, "libulockmgr: recv short\n");
+ msg->error = EIO;
+ break;
+ } else if (errno_save != EINTR) {
+ errno = errno_save;
+ perror("libulockmgr: recv");
+ msg->error = EIO;
+ break;
+ }
+ msg->intr = 1;
+ res = send(o->cfd, msg, sizeof(struct message),
+ MSG_NOSIGNAL);
+ if (res == -1) {
+ perror("libulockmgr: send");
+ msg->error = EIO;
+ break;
+ }
+ if (res != sizeof(struct message)) {
+ fprintf(stderr, "libulockmgr: send short\n");
+ msg->error = EIO;
+ break;
+ }
+ }
+ pthread_mutex_lock(&ulockmgr_lock);
+
+ }
+ if (f)
+ f->inuse--;
+ close(cfd);
+ if (unlockall) {
+ for (fp = &o->fds; *fp;) {
+ f = *fp;
+ if (f->fd == fd && !f->inuse) {
+ *fp = f->next;
+ free(f);
+ } else
+ fp = &f->next;
+ }
+ if (!o->fds) {
+ list_del_owner(o);
+ close(o->cfd);
+ free(o);
+ }
+ /* Force OK on unlock-all, since it _will_ succeed once the
+ owner is deleted */
+ msg->error = 0;
+ }
+
+ return -msg->error;
+}
+
+#ifdef DEBUG
+static uint32_t owner_hash(const unsigned char *id, size_t id_len)
+{
+ uint32_t h = 0;
+ size_t i;
+ for (i = 0; i < id_len; i++)
+ h = ((h << 8) | (h >> 24)) ^ id[i];
+
+ return h;
+}
+#endif
+
+static int ulockmgr_canonicalize(int fd, struct flock *lock)
+{
+ off64_t offset;
+ if (lock->l_whence == SEEK_CUR) {
+ offset = lseek(fd, 0, SEEK_CUR);
+ if (offset == (off64_t) -1)
+ return -errno;
+ } else if (lock->l_whence == SEEK_END) {
+ struct stat stbuf;
+ int res = fstat(fd, &stbuf);
+ if (res == -1)
+ return -errno;
+
+ offset = stbuf.st_size;
+ } else
+ offset = 0;
+
+ lock->l_whence = SEEK_SET;
+ lock->l_start += offset;
+
+ if (lock->l_start < 0)
+ return -EINVAL;
+
+ if (lock->l_len < 0) {
+ lock->l_start += lock->l_len;
+ if (lock->l_start < 0)
+ return -EINVAL;
+ lock->l_len = -lock->l_len;
+ }
+ if (lock->l_len && lock->l_start + lock->l_len - 1 < 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+int ulockmgr_op(int fd, int cmd, struct flock *lock, const void *owner,
+ size_t owner_len)
+{
+ int err;
+ struct message msg;
+ sigset_t old;
+ sigset_t block;
+
+ if (cmd != F_GETLK && cmd != F_SETLK && cmd != F_SETLKW)
+ return -EINVAL;
+
+ if (lock->l_type != F_RDLCK && lock->l_type != F_WRLCK &&
+ lock->l_type != F_UNLCK)
+ return -EINVAL;
+
+ if (lock->l_whence != SEEK_SET && lock->l_whence != SEEK_CUR &&
+ lock->l_whence != SEEK_END)
+ return -EINVAL;
+
+#ifdef DEBUG
+ fprintf(stderr, "libulockmgr: %i %i %i %lli %lli own: 0x%08x\n",
+ cmd, lock->l_type, lock->l_whence, lock->l_start, lock->l_len,
+ owner_hash(owner, owner_len));
+#endif
+
+ /* Unlock should never block anyway */
+ if (cmd == F_SETLKW && lock->l_type == F_UNLCK)
+ cmd = F_SETLK;
+
+ memset(&msg, 0, sizeof(struct message));
+ msg.cmd = cmd;
+ msg.fd = fd;
+ msg.lock = *lock;
+ err = ulockmgr_canonicalize(fd, &msg.lock);
+ if (err)
+ return err;
+
+ sigemptyset(&block);
+ sigaddset(&block, SIGUSR1);
+ pthread_sigmask(SIG_BLOCK, &block, &old);
+ pthread_mutex_lock(&ulockmgr_lock);
+ err = ulockmgr_send_request(&msg, owner, owner_len);
+ pthread_mutex_unlock(&ulockmgr_lock);
+ pthread_sigmask(SIG_SETMASK, &old, NULL);
+ if (!err && cmd == F_GETLK) {
+ if (msg.lock.l_type == F_UNLCK)
+ lock->l_type = F_UNLCK;
+ else
+ *lock = msg.lock;
+ }
+
+ return err;
+}