summaryrefslogtreecommitdiffstats
path: root/sys/fs/fuse
diff options
context:
space:
mode:
authorattilio <attilio@FreeBSD.org>2012-10-13 23:54:26 +0000
committerattilio <attilio@FreeBSD.org>2012-10-13 23:54:26 +0000
commitaf2d834e29a460249f721eae2a5cae2e111f2c74 (patch)
tree6143d77c432c2cec9495468fa9cd122b02b13fb7 /sys/fs/fuse
parent1df941a7f390f4de081ce3749d4a55c796f7b3fc (diff)
downloadFreeBSD-src-af2d834e29a460249f721eae2a5cae2e111f2c74.zip
FreeBSD-src-af2d834e29a460249f721eae2a5cae2e111f2c74.tar.gz
Import a FreeBSD port of the FUSE Linux module.
This has been developed during 2 summer of code mandates and being revived by gnn recently. The functionality in this commit mirrors entirely content of fusefs-kmod port, which doesn't need to be installed anymore for -CURRENT setups. In order to get some sparse technical notes, please refer to: http://lists.freebsd.org/pipermail/freebsd-fs/2012-March/013876.html or to the project branch: svn://svn.freebsd.org/base/projects/fuse/ which also contains granular history of changes happened during port refinements. This commit does not came from the branch reintegration itself because it seems svn is not behaving properly for this functionaly at the moment. Partly Sponsored by: Google, Summer of Code program 2005, 2011 Originally submitted by: ilya, Csaba Henk <csaba-ml AT creo DOT hu > In collabouration with: pho Tested by: flo, gnn, Gustau Perez, Kevin Oberman <rkoberman AT gmail DOT com> MFC after: 2 months
Diffstat (limited to 'sys/fs/fuse')
-rw-r--r--sys/fs/fuse/fuse.h223
-rw-r--r--sys/fs/fuse/fuse_debug.h79
-rw-r--r--sys/fs/fuse/fuse_device.c447
-rw-r--r--sys/fs/fuse/fuse_file.c260
-rw-r--r--sys/fs/fuse/fuse_file.h153
-rw-r--r--sys/fs/fuse/fuse_internal.c696
-rw-r--r--sys/fs/fuse/fuse_internal.h393
-rw-r--r--sys/fs/fuse/fuse_io.c811
-rw-r--r--sys/fs/fuse/fuse_io.h67
-rw-r--r--sys/fs/fuse/fuse_ipc.c904
-rw-r--r--sys/fs/fuse/fuse_ipc.h423
-rw-r--r--sys/fs/fuse/fuse_kernel.h373
-rw-r--r--sys/fs/fuse/fuse_main.c162
-rw-r--r--sys/fs/fuse/fuse_node.c384
-rw-r--r--sys/fs/fuse/fuse_node.h142
-rw-r--r--sys/fs/fuse/fuse_param.h80
-rw-r--r--sys/fs/fuse/fuse_vfsops.c533
-rw-r--r--sys/fs/fuse/fuse_vnops.c2037
18 files changed, 8167 insertions, 0 deletions
diff --git a/sys/fs/fuse/fuse.h b/sys/fs/fuse/fuse.h
new file mode 100644
index 0000000..ef9d561
--- /dev/null
+++ b/sys/fs/fuse/fuse.h
@@ -0,0 +1,223 @@
+/*
+ * Copyright (c) 2007-2009 Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
+ * OWNER 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.
+ *
+ * Copyright (C) 2005 Csaba Henk.
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#include "fuse_kernel.h"
+
+#define FUSE_DEFAULT_DAEMON_TIMEOUT 60 /* s */
+#define FUSE_MIN_DAEMON_TIMEOUT 0 /* s */
+#define FUSE_MAX_DAEMON_TIMEOUT 600 /* s */
+
+#ifndef FUSE_FREEBSD_VERSION
+#define FUSE_FREEBSD_VERSION "0.4.4"
+#endif
+
+/* Mapping versions to features */
+
+#define FUSE_KERNELABI_GEQ(maj, min) \
+(FUSE_KERNEL_VERSION > (maj) || (FUSE_KERNEL_VERSION == (maj) && FUSE_KERNEL_MINOR_VERSION >= (min)))
+
+/*
+ * Appearance of new FUSE operations is not always in par with version
+ * numbering... At least, 7.3 is a sufficient condition for having
+ * FUSE_{ACCESS,CREATE}.
+ */
+#if FUSE_KERNELABI_GEQ(7, 3)
+#ifndef FUSE_HAS_ACCESS
+#define FUSE_HAS_ACCESS 1
+#endif
+#ifndef FUSE_HAS_CREATE
+#define FUSE_HAS_CREATE 1
+#endif
+#else /* FUSE_KERNELABI_GEQ(7, 3) */
+#ifndef FUSE_HAS_ACCESS
+#define FUSE_HAS_ACCESS 0
+#endif
+#ifndef FUSE_HAS_CREATE
+#define FUSE_HAS_CREATE 0
+#endif
+#endif
+
+#if FUSE_KERNELABI_GEQ(7, 7)
+#ifndef FUSE_HAS_GETLK
+#define FUSE_HAS_GETLK 1
+#endif
+#ifndef FUSE_HAS_SETLK
+#define FUSE_HAS_SETLK 1
+#endif
+#ifndef FUSE_HAS_SETLKW
+#define FUSE_HAS_SETLKW 1
+#endif
+#ifndef FUSE_HAS_INTERRUPT
+#define FUSE_HAS_INTERRUPT 1
+#endif
+#else /* FUSE_KERNELABI_GEQ(7, 7) */
+#ifndef FUSE_HAS_GETLK
+#define FUSE_HAS_GETLK 0
+#endif
+#ifndef FUSE_HAS_SETLK
+#define FUSE_HAS_SETLK 0
+#endif
+#ifndef FUSE_HAS_SETLKW
+#define FUSE_HAS_SETLKW 0
+#endif
+#ifndef FUSE_HAS_INTERRUPT
+#define FUSE_HAS_INTERRUPT 0
+#endif
+#endif
+
+#if FUSE_KERNELABI_GEQ(7, 8)
+#ifndef FUSE_HAS_FLUSH_RELEASE
+#define FUSE_HAS_FLUSH_RELEASE 1
+/*
+ * "DESTROY" came in the middle of the 7.8 era,
+ * so this is not completely exact...
+ */
+#ifndef FUSE_HAS_DESTROY
+#define FUSE_HAS_DESTROY 1
+#endif
+#endif
+#else /* FUSE_KERNELABI_GEQ(7, 8) */
+#ifndef FUSE_HAS_FLUSH_RELEASE
+#define FUSE_HAS_FLUSH_RELEASE 0
+#ifndef FUSE_HAS_DESTROY
+#define FUSE_HAS_DESTROY 0
+#endif
+#endif
+#endif
+
+/* misc */
+
+SYSCTL_DECL(_vfs_fuse);
+
+/* Fuse locking */
+
+extern struct mtx fuse_mtx;
+#define FUSE_LOCK() fuse_lck_mtx_lock(fuse_mtx)
+#define FUSE_UNLOCK() fuse_lck_mtx_unlock(fuse_mtx)
+
+#define RECTIFY_TDCR(td, cred) \
+do { \
+ if (! (td)) \
+ (td) = curthread; \
+ if (! (cred)) \
+ (cred) = (td)->td_ucred; \
+} while (0)
+
+/* Debug related stuff */
+
+#ifndef FUSE_DEBUG_DEVICE
+#define FUSE_DEBUG_DEVICE 0
+#endif
+
+#ifndef FUSE_DEBUG_FILE
+#define FUSE_DEBUG_FILE 0
+#endif
+
+#ifndef FUSE_DEBUG_INTERNAL
+#define FUSE_DEBUG_INTERNAL 0
+#endif
+
+#ifndef FUSE_DEBUG_IO
+#define FUSE_DEBUG_IO 0
+#endif
+
+#ifndef FUSE_DEBUG_IPC
+#define FUSE_DEBUG_IPC 0
+#endif
+
+#ifndef FUSE_DEBUG_LOCK
+#define FUSE_DEBUG_LOCK 0
+#endif
+
+#ifndef FUSE_DEBUG_VFSOPS
+#define FUSE_DEBUG_VFSOPS 0
+#endif
+
+#ifndef FUSE_DEBUG_VNOPS
+#define FUSE_DEBUG_VNOPS 0
+#endif
+
+#ifndef FUSE_TRACE
+#define FUSE_TRACE 0
+#endif
+
+#define DEBUGX(cond, fmt, ...) do { \
+ if (((cond))) { \
+ printf("%s: " fmt, __func__, ## __VA_ARGS__); \
+ } } while (0)
+
+#define fuse_lck_mtx_lock(mtx) do { \
+ DEBUGX(FUSE_DEBUG_LOCK, "0: lock(%s): %s@%d by %d\n", \
+ __STRING(mtx), __func__, __LINE__, curthread->td_proc->p_pid); \
+ mtx_lock(&(mtx)); \
+ DEBUGX(FUSE_DEBUG_LOCK, "1: lock(%s): %s@%d by %d\n", \
+ __STRING(mtx), __func__, __LINE__, curthread->td_proc->p_pid); \
+ } while (0)
+
+#define fuse_lck_mtx_unlock(mtx) do { \
+ DEBUGX(FUSE_DEBUG_LOCK, "0: unlock(%s): %s@%d by %d\n", \
+ __STRING(mtx), __func__, __LINE__, curthread->td_proc->p_pid); \
+ mtx_unlock(&(mtx)); \
+ DEBUGX(FUSE_DEBUG_LOCK, "1: unlock(%s): %s@%d by %d\n", \
+ __STRING(mtx), __func__, __LINE__, curthread->td_proc->p_pid); \
+ } while (0)
+
+void fuse_ipc_init(void);
+void fuse_ipc_destroy(void);
+
+int fuse_device_init(void);
+void fuse_device_destroy(void);
diff --git a/sys/fs/fuse/fuse_debug.h b/sys/fs/fuse/fuse_debug.h
new file mode 100644
index 0000000..3faf372
--- /dev/null
+++ b/sys/fs/fuse/fuse_debug.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2007-2009 Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
+ * OWNER 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.
+ *
+ * Copyright (C) 2005 Csaba Henk.
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+
+/* Debug related stuff */
+
+#ifndef FUSE_DEBUG_MODULE
+#error "FUSE_DEBUG_MODULE is not defined"
+#else
+#define FUSE_DEBUG_VAR __CONCAT(FUSE_DEBUG_,FUSE_DEBUG_MODULE)
+#endif
+
+#define DEBUG(fmt, ...) DEBUGX(FUSE_DEBUG_VAR >= 1, fmt, ## __VA_ARGS__)
+#define DEBUG2G(fmt, ...) DEBUGX(FUSE_DEBUG_VAR >= 2, fmt, ## __VA_ARGS__)
+
+#define debug_printf(fmt, ...) DEBUG(fmt, ## __VA_ARGS__)
+#define kdebug_printf(fmt, ...) DEBUG(fmt, ## __VA_ARGS__)
+
+#define fuse_trace_printf(fmt, ...) \
+ DEBUGX(FUSE_DEBUG_VAR && FUSE_TRACE, fmt, ## __VA_ARGS__)
+#define fuse_trace_printf_func() \
+ fuse_trace_printf("%s:%d\n", __FILE__, __LINE__)
+#define fuse_trace_printf_vfsop() fuse_trace_printf_func()
+#define fuse_trace_printf_vnop() fuse_trace_printf_func()
diff --git a/sys/fs/fuse/fuse_device.c b/sys/fs/fuse/fuse_device.c
new file mode 100644
index 0000000..0efc503
--- /dev/null
+++ b/sys/fs/fuse/fuse_device.c
@@ -0,0 +1,447 @@
+/*
+ * Copyright (c) 2007-2009 Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
+ * OWNER 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.
+ *
+ * Copyright (C) 2005 Csaba Henk.
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/module.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/conf.h>
+#include <sys/uio.h>
+#include <sys/malloc.h>
+#include <sys/queue.h>
+#include <sys/lock.h>
+#include <sys/sx.h>
+#include <sys/mutex.h>
+#include <sys/proc.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/fcntl.h>
+#include <sys/sysctl.h>
+#include <sys/poll.h>
+#include <sys/selinfo.h>
+
+#include "fuse.h"
+#include "fuse_ipc.h"
+
+#define FUSE_DEBUG_MODULE DEVICE
+#include "fuse_debug.h"
+
+static struct cdev *fuse_dev;
+
+static d_open_t fuse_device_open;
+static d_close_t fuse_device_close;
+static d_poll_t fuse_device_poll;
+static d_read_t fuse_device_read;
+static d_write_t fuse_device_write;
+
+static struct cdevsw fuse_device_cdevsw = {
+ .d_open = fuse_device_open,
+ .d_close = fuse_device_close,
+ .d_name = "fuse",
+ .d_poll = fuse_device_poll,
+ .d_read = fuse_device_read,
+ .d_write = fuse_device_write,
+ .d_version = D_VERSION,
+ .d_flags = D_NEEDMINOR,
+};
+
+/****************************
+ *
+ * >>> Fuse device op defs
+ *
+ ****************************/
+
+static void
+fdata_dtor(void *arg)
+{
+ struct fuse_data *fdata;
+
+ fdata = arg;
+ fdata_trydestroy(fdata);
+}
+
+/*
+ * Resources are set up on a per-open basis
+ */
+static int
+fuse_device_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
+{
+ struct fuse_data *fdata;
+ int error;
+
+ DEBUG("device %p\n", dev);
+
+ fdata = fdata_alloc(dev, td->td_ucred);
+ error = devfs_set_cdevpriv(fdata, fdata_dtor);
+ if (error != 0)
+ fdata_trydestroy(fdata);
+ else
+ DEBUG("%s: device opened by thread %d.\n", dev->si_name,
+ td->td_tid);
+ return (error);
+}
+
+static int
+fuse_device_close(struct cdev *dev, int fflag, int devtype, struct thread *td)
+{
+ struct fuse_data *data;
+ struct fuse_ticket *tick;
+ int error;
+
+ error = devfs_get_cdevpriv((void **)&data);
+ if (error != 0)
+ return (error);
+ if (!data)
+ panic("no fuse data upon fuse device close");
+ fdata_set_dead(data);
+
+ FUSE_LOCK();
+ fuse_lck_mtx_lock(data->aw_mtx);
+ /* wakup poll()ers */
+ selwakeuppri(&data->ks_rsel, PZERO + 1);
+ /* Don't let syscall handlers wait in vain */
+ while ((tick = fuse_aw_pop(data))) {
+ fuse_lck_mtx_lock(tick->tk_aw_mtx);
+ fticket_set_answered(tick);
+ tick->tk_aw_errno = ENOTCONN;
+ wakeup(tick);
+ fuse_lck_mtx_unlock(tick->tk_aw_mtx);
+ FUSE_ASSERT_AW_DONE(tick);
+ fuse_ticket_drop(tick);
+ }
+ fuse_lck_mtx_unlock(data->aw_mtx);
+ FUSE_UNLOCK();
+
+ DEBUG("%s: device closed by thread %d.\n", dev->si_name, td->td_tid);
+ return (0);
+}
+
+int
+fuse_device_poll(struct cdev *dev, int events, struct thread *td)
+{
+ struct fuse_data *data;
+ int error, revents = 0;
+
+ error = devfs_get_cdevpriv((void **)&data);
+ if (error != 0)
+ return (events &
+ (POLLHUP|POLLIN|POLLRDNORM|POLLOUT|POLLWRNORM));
+
+ if (events & (POLLIN | POLLRDNORM)) {
+ fuse_lck_mtx_lock(data->ms_mtx);
+ if (fdata_get_dead(data) || STAILQ_FIRST(&data->ms_head))
+ revents |= events & (POLLIN | POLLRDNORM);
+ else
+ selrecord(td, &data->ks_rsel);
+ fuse_lck_mtx_unlock(data->ms_mtx);
+ }
+ if (events & (POLLOUT | POLLWRNORM)) {
+ revents |= events & (POLLOUT | POLLWRNORM);
+ }
+ return (revents);
+}
+
+/*
+ * fuse_device_read hangs on the queue of VFS messages.
+ * When it's notified that there is a new one, it picks that and
+ * passes up to the daemon
+ */
+int
+fuse_device_read(struct cdev *dev, struct uio *uio, int ioflag)
+{
+ int err;
+ struct fuse_data *data;
+ struct fuse_ticket *tick;
+ void *buf[] = {NULL, NULL, NULL};
+ int buflen[3];
+ int i;
+
+ DEBUG("fuse device being read on thread %d\n", uio->uio_td->td_tid);
+
+ err = devfs_get_cdevpriv((void **)&data);
+ if (err != 0)
+ return (err);
+
+ fuse_lck_mtx_lock(data->ms_mtx);
+again:
+ if (fdata_get_dead(data)) {
+ DEBUG2G("we know early on that reader should be kicked so we don't wait for news\n");
+ fuse_lck_mtx_unlock(data->ms_mtx);
+ return (ENODEV);
+ }
+ if (!(tick = fuse_ms_pop(data))) {
+ /* check if we may block */
+ if (ioflag & O_NONBLOCK) {
+ /* get outa here soon */
+ fuse_lck_mtx_unlock(data->ms_mtx);
+ return (EAGAIN);
+ } else {
+ err = msleep(data, &data->ms_mtx, PCATCH, "fu_msg", 0);
+ if (err != 0) {
+ fuse_lck_mtx_unlock(data->ms_mtx);
+ return (fdata_get_dead(data) ? ENODEV : err);
+ }
+ tick = fuse_ms_pop(data);
+ }
+ }
+ if (!tick) {
+ /*
+ * We can get here if fuse daemon suddenly terminates,
+ * eg, by being hit by a SIGKILL
+ * -- and some other cases, too, tho not totally clear, when
+ * (cv_signal/wakeup_one signals the whole process ?)
+ */
+ DEBUG("no message on thread #%d\n", uio->uio_td->td_tid);
+ goto again;
+ }
+ fuse_lck_mtx_unlock(data->ms_mtx);
+
+ if (fdata_get_dead(data)) {
+ /*
+ * somebody somewhere -- eg., umount routine --
+ * wants this liaison finished off
+ */
+ DEBUG2G("reader is to be sacked\n");
+ if (tick) {
+ DEBUG2G("weird -- \"kick\" is set tho there is message\n");
+ FUSE_ASSERT_MS_DONE(tick);
+ fuse_ticket_drop(tick);
+ }
+ return (ENODEV); /* This should make the daemon get off
+ * of us */
+ }
+ DEBUG("message got on thread #%d\n", uio->uio_td->td_tid);
+
+ KASSERT(tick->tk_ms_bufdata || tick->tk_ms_bufsize == 0,
+ ("non-null buf pointer with positive size"));
+
+ switch (tick->tk_ms_type) {
+ case FT_M_FIOV:
+ buf[0] = tick->tk_ms_fiov.base;
+ buflen[0] = tick->tk_ms_fiov.len;
+ break;
+ case FT_M_BUF:
+ buf[0] = tick->tk_ms_fiov.base;
+ buflen[0] = tick->tk_ms_fiov.len;
+ buf[1] = tick->tk_ms_bufdata;
+ buflen[1] = tick->tk_ms_bufsize;
+ break;
+ default:
+ panic("unknown message type for fuse_ticket %p", tick);
+ }
+
+ for (i = 0; buf[i]; i++) {
+ /*
+ * Why not ban mercilessly stupid daemons who can't keep up
+ * with us? (There is no much use of a partial read here...)
+ */
+ /*
+ * XXX note that in such cases Linux FUSE throws EIO at the
+ * syscall invoker and stands back to the message queue. The
+ * rationale should be made clear (and possibly adopt that
+ * behaviour). Keeping the current scheme at least makes
+ * fallacy as loud as possible...
+ */
+ if (uio->uio_resid < buflen[i]) {
+ fdata_set_dead(data);
+ DEBUG2G("daemon is stupid, kick it off...\n");
+ err = ENODEV;
+ break;
+ }
+ err = uiomove(buf[i], buflen[i], uio);
+ if (err)
+ break;
+ }
+
+ FUSE_ASSERT_MS_DONE(tick);
+ fuse_ticket_drop(tick);
+
+ return (err);
+}
+
+static __inline int
+fuse_ohead_audit(struct fuse_out_header *ohead, struct uio *uio)
+{
+ DEBUG("Out header -- len: %i, error: %i, unique: %llu; iovecs: %d\n",
+ ohead->len, ohead->error, (unsigned long long)ohead->unique,
+ uio->uio_iovcnt);
+
+ if (uio->uio_resid + sizeof(struct fuse_out_header) != ohead->len) {
+ DEBUG("Format error: body size differs from size claimed by header\n");
+ return (EINVAL);
+ }
+ if (uio->uio_resid && ohead->error) {
+ DEBUG("Format error: non zero error but message had a body\n");
+ return (EINVAL);
+ }
+ /* Sanitize the linuxism of negative errnos */
+ ohead->error = -(ohead->error);
+
+ return (0);
+}
+
+/*
+ * fuse_device_write first reads the header sent by the daemon.
+ * If that's OK, looks up ticket/callback node by the unique id seen in header.
+ * If the callback node contains a handler function, the uio is passed over
+ * that.
+ */
+static int
+fuse_device_write(struct cdev *dev, struct uio *uio, int ioflag)
+{
+ struct fuse_out_header ohead;
+ int err = 0;
+ struct fuse_data *data;
+ struct fuse_ticket *tick, *x_tick;
+ int found = 0;
+
+ DEBUG("resid: %zd, iovcnt: %d, thread: %d\n",
+ uio->uio_resid, uio->uio_iovcnt, uio->uio_td->td_tid);
+
+ err = devfs_get_cdevpriv((void **)&data);
+ if (err != 0)
+ return (err);
+
+ if (uio->uio_resid < sizeof(struct fuse_out_header)) {
+ DEBUG("got less than a header!\n");
+ fdata_set_dead(data);
+ return (EINVAL);
+ }
+ if ((err = uiomove(&ohead, sizeof(struct fuse_out_header), uio)) != 0)
+ return (err);
+
+ /*
+ * We check header information (which is redundant) and compare it
+ * with what we see. If we see some inconsistency we discard the
+ * whole answer and proceed on as if it had never existed. In
+ * particular, no pretender will be woken up, regardless the
+ * "unique" value in the header.
+ */
+ if ((err = fuse_ohead_audit(&ohead, uio))) {
+ fdata_set_dead(data);
+ return (err);
+ }
+ /* Pass stuff over to callback if there is one installed */
+
+ /* Looking for ticket with the unique id of header */
+ fuse_lck_mtx_lock(data->aw_mtx);
+ TAILQ_FOREACH_SAFE(tick, &data->aw_head, tk_aw_link,
+ x_tick) {
+ DEBUG("bumped into callback #%llu\n",
+ (unsigned long long)tick->tk_unique);
+ if (tick->tk_unique == ohead.unique) {
+ found = 1;
+ fuse_aw_remove(tick);
+ break;
+ }
+ }
+ fuse_lck_mtx_unlock(data->aw_mtx);
+
+ if (found) {
+ if (tick->tk_aw_handler) {
+ /*
+ * We found a callback with proper handler. In this
+ * case the out header will be 0wnd by the callback,
+ * so the fun of freeing that is left for her.
+ * (Then, by all chance, she'll just get that's done
+ * via ticket_drop(), so no manual mucking
+ * around...)
+ */
+ DEBUG("pass ticket to a callback\n");
+ memcpy(&tick->tk_aw_ohead, &ohead, sizeof(ohead));
+ err = tick->tk_aw_handler(tick, uio);
+ } else {
+ /* pretender doesn't wanna do anything with answer */
+ DEBUG("stuff devalidated, so we drop it\n");
+ }
+
+ /*
+ * As aw_mtx was not held during the callback execution the
+ * ticket may have been inserted again. However, this is safe
+ * because fuse_ticket_drop() will deal with refcount anyway.
+ */
+ fuse_ticket_drop(tick);
+ } else {
+ /* no callback at all! */
+ DEBUG("erhm, no handler for this response\n");
+ err = EINVAL;
+ }
+
+ return (err);
+}
+
+int
+fuse_device_init(void)
+{
+
+ fuse_dev = make_dev(&fuse_device_cdevsw, 0, UID_ROOT, GID_OPERATOR,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, "fuse");
+ if (fuse_dev == NULL)
+ return (ENOMEM);
+ return (0);
+}
+
+void
+fuse_device_destroy(void)
+{
+
+ MPASS(fuse_dev != NULL);
+ destroy_dev(fuse_dev);
+}
diff --git a/sys/fs/fuse/fuse_file.c b/sys/fs/fuse/fuse_file.c
new file mode 100644
index 0000000..dda9149
--- /dev/null
+++ b/sys/fs/fuse/fuse_file.c
@@ -0,0 +1,260 @@
+/*
+ * Copyright (c) 2007-2009 Google Inc. and Amit Singh
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
+ * OWNER 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.
+ *
+ * Copyright (C) 2005 Csaba Henk.
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/module.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/conf.h>
+#include <sys/uio.h>
+#include <sys/malloc.h>
+#include <sys/queue.h>
+#include <sys/lock.h>
+#include <sys/sx.h>
+#include <sys/mutex.h>
+#include <sys/proc.h>
+#include <sys/mount.h>
+#include <sys/vnode.h>
+#include <sys/sysctl.h>
+
+#include "fuse.h"
+#include "fuse_file.h"
+#include "fuse_internal.h"
+#include "fuse_ipc.h"
+#include "fuse_node.h"
+
+#define FUSE_DEBUG_MODULE FILE
+#include "fuse_debug.h"
+
+static int fuse_fh_count = 0;
+
+SYSCTL_INT(_vfs_fuse, OID_AUTO, filehandle_count, CTLFLAG_RD,
+ &fuse_fh_count, 0, "");
+
+int
+fuse_filehandle_open(struct vnode *vp,
+ fufh_type_t fufh_type,
+ struct fuse_filehandle **fufhp,
+ struct thread *td,
+ struct ucred *cred)
+{
+ struct fuse_dispatcher fdi;
+ struct fuse_open_in *foi;
+ struct fuse_open_out *foo;
+
+ int err = 0;
+ int isdir = 0;
+ int oflags = 0;
+ int op = FUSE_OPEN;
+
+ fuse_trace_printf("fuse_filehandle_open(vp=%p, fufh_type=%d)\n",
+ vp, fufh_type);
+
+ if (fuse_filehandle_valid(vp, fufh_type)) {
+ panic("FUSE: filehandle_open called despite valid fufh (type=%d)",
+ fufh_type);
+ /* NOTREACHED */
+ }
+ /*
+ * Note that this means we are effectively FILTERING OUT open() flags.
+ */
+ oflags = fuse_filehandle_xlate_to_oflags(fufh_type);
+
+ if (vnode_isdir(vp)) {
+ isdir = 1;
+ op = FUSE_OPENDIR;
+ if (fufh_type != FUFH_RDONLY) {
+ printf("FUSE:non-rdonly fh requested for a directory?\n");
+ fufh_type = FUFH_RDONLY;
+ }
+ }
+ fdisp_init(&fdi, sizeof(*foi));
+ fdisp_make_vp(&fdi, op, vp, td, cred);
+
+ foi = fdi.indata;
+ foi->flags = oflags;
+
+ if ((err = fdisp_wait_answ(&fdi))) {
+ debug_printf("OUCH ... daemon didn't give fh (err = %d)\n", err);
+ if (err == ENOENT) {
+ fuse_internal_vnode_disappear(vp);
+ }
+ goto out;
+ }
+ foo = fdi.answ;
+
+ fuse_filehandle_init(vp, fufh_type, fufhp, foo->fh);
+ fuse_vnode_open(vp, foo->open_flags, td);
+
+out:
+ fdisp_destroy(&fdi);
+ return err;
+}
+
+int
+fuse_filehandle_close(struct vnode *vp,
+ fufh_type_t fufh_type,
+ struct thread *td,
+ struct ucred *cred)
+{
+ struct fuse_dispatcher fdi;
+ struct fuse_release_in *fri;
+ struct fuse_vnode_data *fvdat = VTOFUD(vp);
+ struct fuse_filehandle *fufh = NULL;
+
+ int err = 0;
+ int isdir = 0;
+ int op = FUSE_RELEASE;
+
+ fuse_trace_printf("fuse_filehandle_put(vp=%p, fufh_type=%d)\n",
+ vp, fufh_type);
+
+ fufh = &(fvdat->fufh[fufh_type]);
+ if (!FUFH_IS_VALID(fufh)) {
+ panic("FUSE: filehandle_put called on invalid fufh (type=%d)",
+ fufh_type);
+ /* NOTREACHED */
+ }
+ if (fuse_isdeadfs(vp)) {
+ goto out;
+ }
+ if (vnode_isdir(vp)) {
+ op = FUSE_RELEASEDIR;
+ isdir = 1;
+ }
+ fdisp_init(&fdi, sizeof(*fri));
+ fdisp_make_vp(&fdi, op, vp, td, cred);
+ fri = fdi.indata;
+ fri->fh = fufh->fh_id;
+ fri->flags = fuse_filehandle_xlate_to_oflags(fufh_type);
+
+ err = fdisp_wait_answ(&fdi);
+ fdisp_destroy(&fdi);
+
+out:
+ atomic_subtract_acq_int(&fuse_fh_count, 1);
+ fufh->fh_id = (uint64_t)-1;
+ fufh->fh_type = FUFH_INVALID;
+ fuse_invalidate_attr(vp);
+
+ return err;
+}
+
+int
+fuse_filehandle_valid(struct vnode *vp, fufh_type_t fufh_type)
+{
+ struct fuse_vnode_data *fvdat = VTOFUD(vp);
+ struct fuse_filehandle *fufh;
+
+ fufh = &(fvdat->fufh[fufh_type]);
+ return FUFH_IS_VALID(fufh);
+}
+
+int
+fuse_filehandle_get(struct vnode *vp, fufh_type_t fufh_type,
+ struct fuse_filehandle **fufhp)
+{
+ struct fuse_vnode_data *fvdat = VTOFUD(vp);
+ struct fuse_filehandle *fufh;
+
+ fufh = &(fvdat->fufh[fufh_type]);
+ if (!FUFH_IS_VALID(fufh))
+ return EBADF;
+ if (fufhp != NULL)
+ *fufhp = fufh;
+ return 0;
+}
+
+int
+fuse_filehandle_getrw(struct vnode *vp, fufh_type_t fufh_type,
+ struct fuse_filehandle **fufhp)
+{
+ struct fuse_vnode_data *fvdat = VTOFUD(vp);
+ struct fuse_filehandle *fufh;
+
+ fufh = &(fvdat->fufh[fufh_type]);
+ if (!FUFH_IS_VALID(fufh)) {
+ fufh_type = FUFH_RDWR;
+ }
+ return fuse_filehandle_get(vp, fufh_type, fufhp);
+}
+
+void
+fuse_filehandle_init(struct vnode *vp,
+ fufh_type_t fufh_type,
+ struct fuse_filehandle **fufhp,
+ uint64_t fh_id)
+{
+ struct fuse_vnode_data *fvdat = VTOFUD(vp);
+ struct fuse_filehandle *fufh;
+
+ DEBUG("id=%jd type=%d\n", (intmax_t)fh_id, fufh_type);
+ fufh = &(fvdat->fufh[fufh_type]);
+ MPASS(!FUFH_IS_VALID(fufh));
+ fufh->fh_id = fh_id;
+ fufh->fh_type = fufh_type;
+ if (!FUFH_IS_VALID(fufh)) {
+ panic("FUSE: init: invalid filehandle id (type=%d)", fufh_type);
+ }
+ if (fufhp != NULL)
+ *fufhp = fufh;
+
+ atomic_add_acq_int(&fuse_fh_count, 1);
+}
diff --git a/sys/fs/fuse/fuse_file.h b/sys/fs/fuse/fuse_file.h
new file mode 100644
index 0000000..7d605ee
--- /dev/null
+++ b/sys/fs/fuse/fuse_file.h
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2007-2009 Google Inc. and Amit Singh
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
+ * OWNER 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.
+ *
+ * Copyright (C) 2005 Csaba Henk.
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _FUSE_FILE_H_
+#define _FUSE_FILE_H_
+
+#include <sys/types.h>
+#include <sys/fcntl.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/vnode.h>
+
+typedef enum fufh_type {
+ FUFH_INVALID = -1,
+ FUFH_RDONLY = 0,
+ FUFH_WRONLY = 1,
+ FUFH_RDWR = 2,
+ FUFH_MAXTYPE = 3,
+} fufh_type_t;
+
+struct fuse_filehandle {
+ uint64_t fh_id;
+ fufh_type_t fh_type;
+};
+
+#define FUFH_IS_VALID(f) ((f)->fh_type != FUFH_INVALID)
+
+static __inline__
+fufh_type_t
+fuse_filehandle_xlate_from_mmap(int fflags)
+{
+ if (fflags & (PROT_READ | PROT_WRITE)) {
+ return FUFH_RDWR;
+ } else if (fflags & (PROT_WRITE)) {
+ return FUFH_WRONLY;
+ } else if ((fflags & PROT_READ) || (fflags & PROT_EXEC)) {
+ return FUFH_RDONLY;
+ } else {
+ return FUFH_INVALID;
+ }
+}
+
+static __inline__
+fufh_type_t
+fuse_filehandle_xlate_from_fflags(int fflags)
+{
+ if ((fflags & FREAD) && (fflags & FWRITE)) {
+ return FUFH_RDWR;
+ } else if (fflags & (FWRITE)) {
+ return FUFH_WRONLY;
+ } else if (fflags & (FREAD)) {
+ return FUFH_RDONLY;
+ } else {
+ panic("FUSE: What kind of a flag is this (%x)?", fflags);
+ }
+}
+
+static __inline__
+int
+fuse_filehandle_xlate_to_oflags(fufh_type_t type)
+{
+ int oflags = -1;
+
+ switch (type) {
+
+ case FUFH_RDONLY:
+ oflags = O_RDONLY;
+ break;
+
+ case FUFH_WRONLY:
+ oflags = O_WRONLY;
+ break;
+
+ case FUFH_RDWR:
+ oflags = O_RDWR;
+ break;
+
+ default:
+ break;
+ }
+
+ return oflags;
+}
+
+int fuse_filehandle_valid(struct vnode *vp, fufh_type_t fufh_type);
+int fuse_filehandle_get(struct vnode *vp, fufh_type_t fufh_type,
+ struct fuse_filehandle **fufhp);
+int fuse_filehandle_getrw(struct vnode *vp, fufh_type_t fufh_type,
+ struct fuse_filehandle **fufhp);
+
+void fuse_filehandle_init(struct vnode *vp, fufh_type_t fufh_type,
+ struct fuse_filehandle **fufhp, uint64_t fh_id);
+int fuse_filehandle_open(struct vnode *vp, fufh_type_t fufh_type,
+ struct fuse_filehandle **fufhp, struct thread *td,
+ struct ucred *cred);
+int fuse_filehandle_close(struct vnode *vp, fufh_type_t fufh_type,
+ struct thread *td, struct ucred *cred);
+
+#endif /* _FUSE_FILE_H_ */
diff --git a/sys/fs/fuse/fuse_internal.c b/sys/fs/fuse/fuse_internal.c
new file mode 100644
index 0000000..96496c7
--- /dev/null
+++ b/sys/fs/fuse/fuse_internal.c
@@ -0,0 +1,696 @@
+/*
+ * Copyright (c) 2007-2009 Google Inc. and Amit Singh
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
+ * OWNER 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.
+ *
+ * Copyright (C) 2005 Csaba Henk.
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/module.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/conf.h>
+#include <sys/uio.h>
+#include <sys/malloc.h>
+#include <sys/queue.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/sx.h>
+#include <sys/proc.h>
+#include <sys/mount.h>
+#include <sys/vnode.h>
+#include <sys/namei.h>
+#include <sys/stat.h>
+#include <sys/unistd.h>
+#include <sys/filedesc.h>
+#include <sys/file.h>
+#include <sys/fcntl.h>
+#include <sys/dirent.h>
+#include <sys/bio.h>
+#include <sys/buf.h>
+#include <sys/sysctl.h>
+#include <sys/priv.h>
+
+#include "fuse.h"
+#include "fuse_file.h"
+#include "fuse_internal.h"
+#include "fuse_ipc.h"
+#include "fuse_node.h"
+#include "fuse_file.h"
+#include "fuse_param.h"
+
+#define FUSE_DEBUG_MODULE INTERNAL
+#include "fuse_debug.h"
+
+#ifdef ZERO_PAD_INCOMPLETE_BUFS
+static int isbzero(void *buf, size_t len);
+
+#endif
+
+/* access */
+
+int
+fuse_internal_access(struct vnode *vp,
+ mode_t mode,
+ struct fuse_access_param *facp,
+ struct thread *td,
+ struct ucred *cred)
+{
+ int err = 0;
+ uint32_t mask = 0;
+ int dataflags;
+ int vtype;
+ struct mount *mp;
+ struct fuse_dispatcher fdi;
+ struct fuse_access_in *fai;
+ struct fuse_data *data;
+
+ /* NOT YET DONE */
+ /*
+ * If this vnop gives you trouble, just return 0 here for a lazy
+ * kludge.
+ */
+ /* return 0;*/
+
+ fuse_trace_printf_func();
+
+ mp = vnode_mount(vp);
+ vtype = vnode_vtype(vp);
+
+ data = fuse_get_mpdata(mp);
+ dataflags = data->dataflags;
+
+ if ((mode & VWRITE) && vfs_isrdonly(mp)) {
+ return EACCES;
+ }
+ /* Unless explicitly permitted, deny everyone except the fs owner. */
+ if (vnode_isvroot(vp) && !(facp->facc_flags & FACCESS_NOCHECKSPY)) {
+ if (!(dataflags & FSESS_DAEMON_CAN_SPY)) {
+ int denied = fuse_match_cred(data->daemoncred,
+ cred);
+
+ if (denied) {
+ return EPERM;
+ }
+ }
+ facp->facc_flags |= FACCESS_NOCHECKSPY;
+ }
+ if (!(facp->facc_flags & FACCESS_DO_ACCESS)) {
+ return 0;
+ }
+ if (((vtype == VREG) && (mode & VEXEC))) {
+#ifdef NEED_MOUNT_ARGUMENT_FOR_THIS
+ /* Let the kernel handle this through open / close heuristics.*/
+ return ENOTSUP;
+#else
+ /* Let the kernel handle this. */
+ return 0;
+#endif
+ }
+ if (!fsess_isimpl(mp, FUSE_ACCESS)) {
+ /* Let the kernel handle this. */
+ return 0;
+ }
+ if (dataflags & FSESS_DEFAULT_PERMISSIONS) {
+ /* Let the kernel handle this. */
+ return 0;
+ }
+ if ((mode & VADMIN) != 0) {
+ err = priv_check_cred(cred, PRIV_VFS_ADMIN, 0);
+ if (err) {
+ return err;
+ }
+ }
+ if ((mode & (VWRITE | VAPPEND | VADMIN)) != 0) {
+ mask |= W_OK;
+ }
+ if ((mode & VREAD) != 0) {
+ mask |= R_OK;
+ }
+ if ((mode & VEXEC) != 0) {
+ mask |= X_OK;
+ }
+ bzero(&fdi, sizeof(fdi));
+
+ fdisp_init(&fdi, sizeof(*fai));
+ fdisp_make_vp(&fdi, FUSE_ACCESS, vp, td, cred);
+
+ fai = fdi.indata;
+ fai->mask = F_OK;
+ fai->mask |= mask;
+
+ err = fdisp_wait_answ(&fdi);
+ fdisp_destroy(&fdi);
+
+ if (err == ENOSYS) {
+ fsess_set_notimpl(mp, FUSE_ACCESS);
+ err = 0;
+ }
+ return err;
+}
+
+/* fsync */
+
+int
+fuse_internal_fsync_callback(struct fuse_ticket *tick, struct uio *uio)
+{
+ fuse_trace_printf_func();
+
+ if (tick->tk_aw_ohead.error == ENOSYS) {
+ fsess_set_notimpl(tick->tk_data->mp, fticket_opcode(tick));
+ }
+ return 0;
+}
+
+int
+fuse_internal_fsync(struct vnode *vp,
+ struct thread *td,
+ struct ucred *cred,
+ struct fuse_filehandle *fufh)
+{
+ int op = FUSE_FSYNC;
+ struct fuse_fsync_in *ffsi;
+ struct fuse_dispatcher fdi;
+
+ fuse_trace_printf_func();
+
+ if (vnode_isdir(vp)) {
+ op = FUSE_FSYNCDIR;
+ }
+ fdisp_init(&fdi, sizeof(*ffsi));
+ fdisp_make_vp(&fdi, op, vp, td, cred);
+ ffsi = fdi.indata;
+ ffsi->fh = fufh->fh_id;
+
+ ffsi->fsync_flags = 1; /* datasync */
+
+ fuse_insert_callback(fdi.tick, fuse_internal_fsync_callback);
+ fuse_insert_message(fdi.tick);
+
+ fdisp_destroy(&fdi);
+
+ return 0;
+
+}
+
+/* readdir */
+
+int
+fuse_internal_readdir(struct vnode *vp,
+ struct uio *uio,
+ struct fuse_filehandle *fufh,
+ struct fuse_iov *cookediov)
+{
+ int err = 0;
+ struct fuse_dispatcher fdi;
+ struct fuse_read_in *fri;
+
+ if (uio_resid(uio) == 0) {
+ return 0;
+ }
+ fdisp_init(&fdi, 0);
+
+ /*
+ * Note that we DO NOT have a UIO_SYSSPACE here (so no need for p2p
+ * I/O).
+ */
+
+ while (uio_resid(uio) > 0) {
+
+ fdi.iosize = sizeof(*fri);
+ fdisp_make_vp(&fdi, FUSE_READDIR, vp, NULL, NULL);
+
+ fri = fdi.indata;
+ fri->fh = fufh->fh_id;
+ fri->offset = uio_offset(uio);
+ fri->size = min(uio_resid(uio), FUSE_DEFAULT_IOSIZE);
+ /* mp->max_read */
+
+ if ((err = fdisp_wait_answ(&fdi))) {
+ break;
+ }
+ if ((err = fuse_internal_readdir_processdata(uio, fri->size, fdi.answ,
+ fdi.iosize, cookediov))) {
+ break;
+ }
+ }
+
+ fdisp_destroy(&fdi);
+ return ((err == -1) ? 0 : err);
+}
+
+int
+fuse_internal_readdir_processdata(struct uio *uio,
+ size_t reqsize,
+ void *buf,
+ size_t bufsize,
+ void *param)
+{
+ int err = 0;
+ int cou = 0;
+ int bytesavail;
+ size_t freclen;
+
+ struct dirent *de;
+ struct fuse_dirent *fudge;
+ struct fuse_iov *cookediov = param;
+
+ if (bufsize < FUSE_NAME_OFFSET) {
+ return -1;
+ }
+ for (;;) {
+
+ if (bufsize < FUSE_NAME_OFFSET) {
+ err = -1;
+ break;
+ }
+ fudge = (struct fuse_dirent *)buf;
+ freclen = FUSE_DIRENT_SIZE(fudge);
+
+ cou++;
+
+ if (bufsize < freclen) {
+ err = ((cou == 1) ? -1 : 0);
+ break;
+ }
+#ifdef ZERO_PAD_INCOMPLETE_BUFS
+ if (isbzero(buf, FUSE_NAME_OFFSET)) {
+ err = -1;
+ break;
+ }
+#endif
+
+ if (!fudge->namelen || fudge->namelen > MAXNAMLEN) {
+ err = EINVAL;
+ break;
+ }
+ bytesavail = GENERIC_DIRSIZ((struct pseudo_dirent *)
+ &fudge->namelen);
+
+ if (bytesavail > uio_resid(uio)) {
+ err = -1;
+ break;
+ }
+ fiov_refresh(cookediov);
+ fiov_adjust(cookediov, bytesavail);
+
+ de = (struct dirent *)cookediov->base;
+ de->d_fileno = fudge->ino; /* XXX: truncation */
+ de->d_reclen = bytesavail;
+ de->d_type = fudge->type;
+ de->d_namlen = fudge->namelen;
+ memcpy((char *)cookediov->base + sizeof(struct dirent) -
+ MAXNAMLEN - 1,
+ (char *)buf + FUSE_NAME_OFFSET, fudge->namelen);
+ ((char *)cookediov->base)[bytesavail] = '\0';
+
+ err = uiomove(cookediov->base, cookediov->len, uio);
+ if (err) {
+ break;
+ }
+ buf = (char *)buf + freclen;
+ bufsize -= freclen;
+ uio_setoffset(uio, fudge->off);
+ }
+
+ return err;
+}
+
+/* remove */
+
+#ifdef XXXIP
+static int
+fuse_internal_remove_callback(struct vnode *vp, void *cargs)
+{
+ struct vattr *vap;
+ uint64_t target_nlink;
+
+ vap = VTOVA(vp);
+
+ target_nlink = *(uint64_t *)cargs;
+
+ /* somewhat lame "heuristics", but you got better ideas? */
+ if ((vap->va_nlink == target_nlink) && vnode_isreg(vp)) {
+ fuse_invalidate_attr(vp);
+ }
+ return 0;
+}
+
+#endif
+
+#define INVALIDATE_CACHED_VATTRS_UPON_UNLINK 1
+int
+fuse_internal_remove(struct vnode *dvp,
+ struct vnode *vp,
+ struct componentname *cnp,
+ enum fuse_opcode op)
+{
+ struct fuse_dispatcher fdi;
+
+ struct vattr *vap = VTOVA(vp);
+
+#if INVALIDATE_CACHED_VATTRS_UPON_UNLINK
+ int need_invalidate = 0;
+ uint64_t target_nlink = 0;
+
+#endif
+ int err = 0;
+
+ debug_printf("dvp=%p, cnp=%p, op=%d\n", vp, cnp, op);
+
+ fdisp_init(&fdi, cnp->cn_namelen + 1);
+ fdisp_make_vp(&fdi, op, dvp, cnp->cn_thread, cnp->cn_cred);
+
+ memcpy(fdi.indata, cnp->cn_nameptr, cnp->cn_namelen);
+ ((char *)fdi.indata)[cnp->cn_namelen] = '\0';
+
+#if INVALIDATE_CACHED_VATTRS_UPON_UNLINK
+ if (vap->va_nlink > 1) {
+ need_invalidate = 1;
+ target_nlink = vap->va_nlink;
+ }
+#endif
+
+ err = fdisp_wait_answ(&fdi);
+ fdisp_destroy(&fdi);
+
+ fuse_invalidate_attr(dvp);
+ fuse_invalidate_attr(vp);
+
+#ifdef XXXIP
+ /*
+ * XXX: INVALIDATE_CACHED_VATTRS_UPON_UNLINK
+ *
+ * Consider the case where vap->va_nlink > 1 for the entity being
+ * removed. In our world, other in-memory vnodes that share a link
+ * count each with this one may not know right way that this one just
+ * got deleted. We should let them know, say, through a vnode_iterate()
+ * here and a callback that does fuse_invalidate_attr(vp) on each
+ * relevant vnode.
+ */
+ if (need_invalidate && !err) {
+ vnode_iterate(vnode_mount(vp), 0, fuse_internal_remove_callback,
+ (void *)&target_nlink);
+ }
+#endif
+
+ return err;
+}
+
+/* rename */
+
+int
+fuse_internal_rename(struct vnode *fdvp,
+ struct componentname *fcnp,
+ struct vnode *tdvp,
+ struct componentname *tcnp)
+{
+ struct fuse_dispatcher fdi;
+ struct fuse_rename_in *fri;
+ int err = 0;
+
+ fdisp_init(&fdi, sizeof(*fri) + fcnp->cn_namelen + tcnp->cn_namelen + 2);
+ fdisp_make_vp(&fdi, FUSE_RENAME, fdvp, tcnp->cn_thread, tcnp->cn_cred);
+
+ fri = fdi.indata;
+ fri->newdir = VTOI(tdvp);
+ memcpy((char *)fdi.indata + sizeof(*fri), fcnp->cn_nameptr,
+ fcnp->cn_namelen);
+ ((char *)fdi.indata)[sizeof(*fri) + fcnp->cn_namelen] = '\0';
+ memcpy((char *)fdi.indata + sizeof(*fri) + fcnp->cn_namelen + 1,
+ tcnp->cn_nameptr, tcnp->cn_namelen);
+ ((char *)fdi.indata)[sizeof(*fri) + fcnp->cn_namelen +
+ tcnp->cn_namelen + 1] = '\0';
+
+ err = fdisp_wait_answ(&fdi);
+ fdisp_destroy(&fdi);
+
+ fuse_invalidate_attr(fdvp);
+ if (tdvp != fdvp) {
+ fuse_invalidate_attr(tdvp);
+ }
+ return err;
+}
+
+/* strategy */
+
+/* entity creation */
+
+void
+fuse_internal_newentry_makerequest(struct mount *mp,
+ uint64_t dnid,
+ struct componentname *cnp,
+ enum fuse_opcode op,
+ void *buf,
+ size_t bufsize,
+ struct fuse_dispatcher *fdip)
+{
+ debug_printf("fdip=%p\n", fdip);
+
+ fdip->iosize = bufsize + cnp->cn_namelen + 1;
+
+ fdisp_make(fdip, op, mp, dnid, cnp->cn_thread, cnp->cn_cred);
+ memcpy(fdip->indata, buf, bufsize);
+ memcpy((char *)fdip->indata + bufsize, cnp->cn_nameptr, cnp->cn_namelen);
+ ((char *)fdip->indata)[bufsize + cnp->cn_namelen] = '\0';
+}
+
+int
+fuse_internal_newentry_core(struct vnode *dvp,
+ struct vnode **vpp,
+ struct componentname *cnp,
+ enum vtype vtyp,
+ struct fuse_dispatcher *fdip)
+{
+ int err = 0;
+ struct fuse_entry_out *feo;
+ struct mount *mp = vnode_mount(dvp);
+
+ if ((err = fdisp_wait_answ(fdip))) {
+ return err;
+ }
+ feo = fdip->answ;
+
+ if ((err = fuse_internal_checkentry(feo, vtyp))) {
+ return err;
+ }
+ err = fuse_vnode_get(mp, feo->nodeid, dvp, vpp, cnp, vtyp);
+ if (err) {
+ fuse_internal_forget_send(mp, cnp->cn_thread, cnp->cn_cred,
+ feo->nodeid, 1);
+ return err;
+ }
+ cache_attrs(*vpp, feo);
+
+ return err;
+}
+
+int
+fuse_internal_newentry(struct vnode *dvp,
+ struct vnode **vpp,
+ struct componentname *cnp,
+ enum fuse_opcode op,
+ void *buf,
+ size_t bufsize,
+ enum vtype vtype)
+{
+ int err;
+ struct fuse_dispatcher fdi;
+ struct mount *mp = vnode_mount(dvp);
+
+ fdisp_init(&fdi, 0);
+ fuse_internal_newentry_makerequest(mp, VTOI(dvp), cnp, op, buf,
+ bufsize, &fdi);
+ err = fuse_internal_newentry_core(dvp, vpp, cnp, vtype, &fdi);
+ fdisp_destroy(&fdi);
+ fuse_invalidate_attr(dvp);
+
+ return err;
+}
+
+/* entity destruction */
+
+int
+fuse_internal_forget_callback(struct fuse_ticket *ftick, struct uio *uio)
+{
+ fuse_internal_forget_send(ftick->tk_data->mp, curthread, NULL,
+ ((struct fuse_in_header *)ftick->tk_ms_fiov.base)->nodeid, 1);
+
+ return 0;
+}
+
+void
+fuse_internal_forget_send(struct mount *mp,
+ struct thread *td,
+ struct ucred *cred,
+ uint64_t nodeid,
+ uint64_t nlookup)
+{
+
+ struct fuse_dispatcher fdi;
+ struct fuse_forget_in *ffi;
+
+ debug_printf("mp=%p, nodeid=%ju, nlookup=%ju\n",
+ mp, (uintmax_t)nodeid, (uintmax_t)nlookup);
+
+ /*
+ * KASSERT(nlookup > 0, ("zero-times forget for vp #%llu",
+ * (long long unsigned) nodeid));
+ */
+
+ fdisp_init(&fdi, sizeof(*ffi));
+ fdisp_make(&fdi, FUSE_FORGET, mp, nodeid, td, cred);
+
+ ffi = fdi.indata;
+ ffi->nlookup = nlookup;
+
+ fuse_insert_message(fdi.tick);
+ fdisp_destroy(&fdi);
+}
+
+void
+fuse_internal_vnode_disappear(struct vnode *vp)
+{
+ struct fuse_vnode_data *fvdat = VTOFUD(vp);
+
+ ASSERT_VOP_ELOCKED(vp, "fuse_internal_vnode_disappear");
+ fvdat->flag |= FN_REVOKED;
+ cache_purge(vp);
+}
+
+/* fuse start/stop */
+
+int
+fuse_internal_init_callback(struct fuse_ticket *tick, struct uio *uio)
+{
+ int err = 0;
+ struct fuse_data *data = tick->tk_data;
+ struct fuse_init_out *fiio;
+
+ if ((err = tick->tk_aw_ohead.error)) {
+ goto out;
+ }
+ if ((err = fticket_pull(tick, uio))) {
+ goto out;
+ }
+ fiio = fticket_resp(tick)->base;
+
+ /* XXX: Do we want to check anything further besides this? */
+ if (fiio->major < 7) {
+ debug_printf("userpace version too low\n");
+ err = EPROTONOSUPPORT;
+ goto out;
+ }
+ data->fuse_libabi_major = fiio->major;
+ data->fuse_libabi_minor = fiio->minor;
+
+ if (fuse_libabi_geq(data, 7, 5)) {
+ if (fticket_resp(tick)->len == sizeof(struct fuse_init_out)) {
+ data->max_write = fiio->max_write;
+ } else {
+ err = EINVAL;
+ }
+ } else {
+ /* Old fix values */
+ data->max_write = 4096;
+ }
+
+out:
+ if (err) {
+ fdata_set_dead(data);
+ }
+ FUSE_LOCK();
+ data->dataflags |= FSESS_INITED;
+ wakeup(&data->ticketer);
+ FUSE_UNLOCK();
+
+ return 0;
+}
+
+void
+fuse_internal_send_init(struct fuse_data *data, struct thread *td)
+{
+ struct fuse_init_in *fiii;
+ struct fuse_dispatcher fdi;
+
+ fdisp_init(&fdi, sizeof(*fiii));
+ fdisp_make(&fdi, FUSE_INIT, data->mp, 0, td, NULL);
+ fiii = fdi.indata;
+ fiii->major = FUSE_KERNEL_VERSION;
+ fiii->minor = FUSE_KERNEL_MINOR_VERSION;
+ fiii->max_readahead = FUSE_DEFAULT_IOSIZE * 16;
+ fiii->flags = 0;
+
+ fuse_insert_callback(fdi.tick, fuse_internal_init_callback);
+ fuse_insert_message(fdi.tick);
+ fdisp_destroy(&fdi);
+}
+
+#ifdef ZERO_PAD_INCOMPLETE_BUFS
+static int
+isbzero(void *buf, size_t len)
+{
+ int i;
+
+ for (i = 0; i < len; i++) {
+ if (((char *)buf)[i])
+ return (0);
+ }
+
+ return (1);
+}
+
+#endif
diff --git a/sys/fs/fuse/fuse_internal.h b/sys/fs/fuse/fuse_internal.h
new file mode 100644
index 0000000..9cf20e9
--- /dev/null
+++ b/sys/fs/fuse/fuse_internal.h
@@ -0,0 +1,393 @@
+/*
+ * Copyright (c) 2007-2009 Google Inc. and Amit Singh
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
+ * OWNER 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.
+ *
+ * Copyright (C) 2005 Csaba Henk.
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _FUSE_INTERNAL_H_
+#define _FUSE_INTERNAL_H_
+
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/stat.h>
+#include <sys/vnode.h>
+
+#include "fuse_ipc.h"
+#include "fuse_node.h"
+
+static __inline int
+vfs_isrdonly(struct mount *mp)
+{
+ return ((mp->mnt_flag & MNT_RDONLY) != 0 ? 1 : 0);
+}
+
+static __inline struct mount *
+vnode_mount(struct vnode *vp)
+{
+ return (vp->v_mount);
+}
+
+static __inline int
+vnode_mountedhere(struct vnode *vp)
+{
+ return (vp->v_mountedhere != NULL ? 1 : 0);
+}
+
+static __inline enum vtype
+vnode_vtype(struct vnode *vp)
+{
+ return (vp->v_type);
+}
+
+static __inline int
+vnode_isvroot(struct vnode *vp)
+{
+ return ((vp->v_vflag & VV_ROOT) != 0 ? 1 : 0);
+}
+
+static __inline int
+vnode_isreg(struct vnode *vp)
+{
+ return (vp->v_type == VREG ? 1 : 0);
+}
+
+static __inline int
+vnode_isdir(struct vnode *vp)
+{
+ return (vp->v_type == VDIR ? 1 : 0);
+}
+
+static __inline int
+vnode_islnk(struct vnode *vp)
+{
+ return (vp->v_type == VLNK ? 1 : 0);
+}
+
+static __inline ssize_t
+uio_resid(struct uio *uio)
+{
+ return (uio->uio_resid);
+}
+
+static __inline off_t
+uio_offset(struct uio *uio)
+{
+ return (uio->uio_offset);
+}
+
+static __inline void
+uio_setoffset(struct uio *uio, off_t offset)
+{
+ uio->uio_offset = offset;
+}
+
+static __inline void
+uio_setresid(struct uio *uio, ssize_t resid)
+{
+ uio->uio_resid = resid;
+}
+
+/* time */
+
+#define fuse_timespec_add(vvp, uvp) \
+ do { \
+ (vvp)->tv_sec += (uvp)->tv_sec; \
+ (vvp)->tv_nsec += (uvp)->tv_nsec; \
+ if ((vvp)->tv_nsec >= 1000000000) { \
+ (vvp)->tv_sec++; \
+ (vvp)->tv_nsec -= 1000000000; \
+ } \
+ } while (0)
+
+#define fuse_timespec_cmp(tvp, uvp, cmp) \
+ (((tvp)->tv_sec == (uvp)->tv_sec) ? \
+ ((tvp)->tv_nsec cmp (uvp)->tv_nsec) : \
+ ((tvp)->tv_sec cmp (uvp)->tv_sec))
+
+/* miscellaneous */
+
+static __inline__
+int
+fuse_isdeadfs(struct vnode *vp)
+{
+ struct fuse_data *data = fuse_get_mpdata(vnode_mount(vp));
+
+ return (data->dataflags & FSESS_DEAD);
+}
+
+static __inline__
+int
+fuse_iosize(struct vnode *vp)
+{
+ return vp->v_mount->mnt_stat.f_iosize;
+}
+
+/* access */
+
+#define FVP_ACCESS_NOOP 0x01
+
+#define FACCESS_VA_VALID 0x01
+#define FACCESS_DO_ACCESS 0x02
+#define FACCESS_STICKY 0x04
+#define FACCESS_CHOWN 0x08
+#define FACCESS_NOCHECKSPY 0x10
+#define FACCESS_SETGID 0x12
+
+#define FACCESS_XQUERIES FACCESS_STICKY | FACCESS_CHOWN | FACCESS_SETGID
+
+struct fuse_access_param {
+ uid_t xuid;
+ gid_t xgid;
+ uint32_t facc_flags;
+};
+
+static __inline int
+fuse_match_cred(struct ucred *basecred, struct ucred *usercred)
+{
+ if (basecred->cr_uid == usercred->cr_uid &&
+ basecred->cr_uid == usercred->cr_ruid &&
+ basecred->cr_uid == usercred->cr_svuid &&
+ basecred->cr_groups[0] == usercred->cr_groups[0] &&
+ basecred->cr_groups[0] == usercred->cr_rgid &&
+ basecred->cr_groups[0] == usercred->cr_svgid)
+ return 0;
+
+ return EPERM;
+}
+
+int
+fuse_internal_access(struct vnode *vp,
+ mode_t mode,
+ struct fuse_access_param *facp,
+ struct thread *td,
+ struct ucred *cred);
+
+/* attributes */
+
+static __inline
+void
+fuse_internal_attr_fat2vat(struct mount *mp,
+ struct fuse_attr *fat,
+ struct vattr *vap)
+{
+ DEBUGX(FUSE_DEBUG_INTERNAL,
+ "node #%ju, mode 0%o\n", (uintmax_t)fat->ino, fat->mode);
+
+ vattr_null(vap);
+
+ vap->va_fsid = mp->mnt_stat.f_fsid.val[0];
+ vap->va_fileid = fat->ino; /* XXX cast from 64 bits to 32 */
+ vap->va_mode = fat->mode & ~S_IFMT;
+ vap->va_nlink = fat->nlink;
+ vap->va_uid = fat->uid;
+ vap->va_gid = fat->gid;
+ vap->va_rdev = fat->rdev;
+ vap->va_size = fat->size;
+ vap->va_atime.tv_sec = fat->atime; /* XXX on some platforms cast from 64 bits to 32 */
+ vap->va_atime.tv_nsec = fat->atimensec;
+ vap->va_mtime.tv_sec = fat->mtime;
+ vap->va_mtime.tv_nsec = fat->mtimensec;
+ vap->va_ctime.tv_sec = fat->ctime;
+ vap->va_ctime.tv_nsec = fat->ctimensec;
+ vap->va_blocksize = PAGE_SIZE;
+ vap->va_type = IFTOVT(fat->mode);
+
+#if (S_BLKSIZE == 512)
+ /* Optimize this case */
+ vap->va_bytes = fat->blocks << 9;
+#else
+ vap->va_bytes = fat->blocks * S_BLKSIZE;
+#endif
+
+ vap->va_flags = 0;
+}
+
+
+#define cache_attrs(vp, fuse_out) do { \
+ struct timespec uptsp_ ## __func__; \
+ \
+ VTOFUD(vp)->cached_attrs_valid.tv_sec = (fuse_out)->attr_valid; \
+ VTOFUD(vp)->cached_attrs_valid.tv_nsec = (fuse_out)->attr_valid_nsec; \
+ nanouptime(&uptsp_ ## __func__); \
+ \
+ fuse_timespec_add(&VTOFUD(vp)->cached_attrs_valid, &uptsp_ ## __func__); \
+ \
+ fuse_internal_attr_fat2vat(vnode_mount(vp), &(fuse_out)->attr, VTOVA(vp)); \
+} while (0)
+
+/* fsync */
+
+int
+fuse_internal_fsync(struct vnode *vp,
+ struct thread *td,
+ struct ucred *cred,
+ struct fuse_filehandle *fufh);
+
+int
+fuse_internal_fsync_callback(struct fuse_ticket *tick, struct uio *uio);
+
+/* readdir */
+
+struct pseudo_dirent {
+ uint32_t d_namlen;
+};
+
+int
+fuse_internal_readdir(struct vnode *vp,
+ struct uio *uio,
+ struct fuse_filehandle *fufh,
+ struct fuse_iov *cookediov);
+
+int
+fuse_internal_readdir_processdata(struct uio *uio,
+ size_t reqsize,
+ void *buf,
+ size_t bufsize,
+ void *param);
+
+/* remove */
+
+int
+fuse_internal_remove(struct vnode *dvp,
+ struct vnode *vp,
+ struct componentname *cnp,
+ enum fuse_opcode op);
+
+/* rename */
+
+int
+fuse_internal_rename(struct vnode *fdvp,
+ struct componentname *fcnp,
+ struct vnode *tdvp,
+ struct componentname *tcnp);
+/* revoke */
+
+void
+fuse_internal_vnode_disappear(struct vnode *vp);
+
+/* strategy */
+
+/* entity creation */
+
+static __inline
+int
+fuse_internal_checkentry(struct fuse_entry_out *feo, enum vtype vtyp)
+{
+ DEBUGX(FUSE_DEBUG_INTERNAL,
+ "feo=%p, vtype=%d\n", feo, vtyp);
+
+ if (vtyp != IFTOVT(feo->attr.mode)) {
+ DEBUGX(FUSE_DEBUG_INTERNAL,
+ "EINVAL -- %x != %x\n", vtyp, IFTOVT(feo->attr.mode));
+ return EINVAL;
+ }
+
+ if (feo->nodeid == FUSE_NULL_ID) {
+ DEBUGX(FUSE_DEBUG_INTERNAL,
+ "EINVAL -- feo->nodeid is NULL\n");
+ return EINVAL;
+ }
+
+ if (feo->nodeid == FUSE_ROOT_ID) {
+ DEBUGX(FUSE_DEBUG_INTERNAL,
+ "EINVAL -- feo->nodeid is FUSE_ROOT_ID\n");
+ return EINVAL;
+ }
+
+ return 0;
+}
+
+int
+fuse_internal_newentry(struct vnode *dvp,
+ struct vnode **vpp,
+ struct componentname *cnp,
+ enum fuse_opcode op,
+ void *buf,
+ size_t bufsize,
+ enum vtype vtyp);
+
+void
+fuse_internal_newentry_makerequest(struct mount *mp,
+ uint64_t dnid,
+ struct componentname *cnp,
+ enum fuse_opcode op,
+ void *buf,
+ size_t bufsize,
+ struct fuse_dispatcher *fdip);
+
+int
+fuse_internal_newentry_core(struct vnode *dvp,
+ struct vnode **vpp,
+ struct componentname *cnp,
+ enum vtype vtyp,
+ struct fuse_dispatcher *fdip);
+
+/* entity destruction */
+
+int
+fuse_internal_forget_callback(struct fuse_ticket *tick, struct uio *uio);
+
+void
+fuse_internal_forget_send(struct mount *mp,
+ struct thread *td,
+ struct ucred *cred,
+ uint64_t nodeid,
+ uint64_t nlookup);
+
+/* fuse start/stop */
+
+int fuse_internal_init_callback(struct fuse_ticket *tick, struct uio *uio);
+void fuse_internal_send_init(struct fuse_data *data, struct thread *td);
+
+#endif /* _FUSE_INTERNAL_H_ */
diff --git a/sys/fs/fuse/fuse_io.c b/sys/fs/fuse/fuse_io.c
new file mode 100644
index 0000000..7735329
--- /dev/null
+++ b/sys/fs/fuse/fuse_io.c
@@ -0,0 +1,811 @@
+/*
+ * Copyright (c) 2007-2009 Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
+ * OWNER 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.
+ *
+ * Copyright (C) 2005 Csaba Henk.
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/module.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/conf.h>
+#include <sys/uio.h>
+#include <sys/malloc.h>
+#include <sys/queue.h>
+#include <sys/lock.h>
+#include <sys/sx.h>
+#include <sys/mutex.h>
+#include <sys/proc.h>
+#include <sys/mount.h>
+#include <sys/vnode.h>
+#include <sys/stat.h>
+#include <sys/unistd.h>
+#include <sys/filedesc.h>
+#include <sys/file.h>
+#include <sys/fcntl.h>
+#include <sys/bio.h>
+#include <sys/buf.h>
+#include <sys/sysctl.h>
+
+#include <vm/vm.h>
+#include <vm/vm_extern.h>
+#include <vm/pmap.h>
+#include <vm/vm_map.h>
+#include <vm/vm_page.h>
+#include <vm/vm_object.h>
+#include <vm/vm_pager.h>
+#include <vm/vnode_pager.h>
+#include <vm/vm_object.h>
+
+#include "fuse.h"
+#include "fuse_file.h"
+#include "fuse_node.h"
+#include "fuse_internal.h"
+#include "fuse_ipc.h"
+#include "fuse_io.h"
+
+#define FUSE_DEBUG_MODULE IO
+#include "fuse_debug.h"
+
+
+static int
+fuse_read_directbackend(struct vnode *vp, struct uio *uio,
+ struct ucred *cred, struct fuse_filehandle *fufh);
+static int
+fuse_read_biobackend(struct vnode *vp, struct uio *uio,
+ struct ucred *cred, struct fuse_filehandle *fufh);
+static int
+fuse_write_directbackend(struct vnode *vp, struct uio *uio,
+ struct ucred *cred, struct fuse_filehandle *fufh);
+static int
+fuse_write_biobackend(struct vnode *vp, struct uio *uio,
+ struct ucred *cred, struct fuse_filehandle *fufh);
+
+int
+fuse_io_dispatch(struct vnode *vp, struct uio *uio, int ioflag,
+ struct ucred *cred)
+{
+ struct fuse_filehandle *fufh;
+ int err, directio;
+
+ MPASS(vp->v_type == VREG);
+
+ err = fuse_filehandle_getrw(vp,
+ (uio->uio_rw == UIO_READ) ? FUFH_RDONLY : FUFH_WRONLY, &fufh);
+ if (err) {
+ printf("FUSE: io dispatch: filehandles are closed\n");
+ return err;
+ }
+ /*
+ * Ideally, when the daemon asks for direct io at open time, the
+ * standard file flag should be set according to this, so that would
+ * just change the default mode, which later on could be changed via
+ * fcntl(2).
+ * But this doesn't work, the O_DIRECT flag gets cleared at some point
+ * (don't know where). So to make any use of the Fuse direct_io option,
+ * we hardwire it into the file's private data (similarly to Linux,
+ * btw.).
+ */
+ directio = (ioflag & IO_DIRECT) || !fsess_opt_datacache(vnode_mount(vp));
+
+ switch (uio->uio_rw) {
+ case UIO_READ:
+ if (directio) {
+ DEBUG("direct read of vnode %ju via file handle %ju\n",
+ (uintmax_t)VTOILLU(vp), (uintmax_t)fufh->fh_id);
+ err = fuse_read_directbackend(vp, uio, cred, fufh);
+ } else {
+ DEBUG("buffered read of vnode %ju\n",
+ (uintmax_t)VTOILLU(vp));
+ err = fuse_read_biobackend(vp, uio, cred, fufh);
+ }
+ break;
+ case UIO_WRITE:
+ if (directio) {
+ DEBUG("direct write of vnode %ju via file handle %ju\n",
+ (uintmax_t)VTOILLU(vp), (uintmax_t)fufh->fh_id);
+ err = fuse_write_directbackend(vp, uio, cred, fufh);
+ fuse_invalidate_attr(vp);
+ } else {
+ DEBUG("buffered write of vnode %ju\n",
+ (uintmax_t)VTOILLU(vp));
+ err = fuse_write_biobackend(vp, uio, cred, fufh);
+ }
+ break;
+ default:
+ panic("uninterpreted mode passed to fuse_io_dispatch");
+ }
+
+ return (err);
+}
+
+static int
+fuse_read_biobackend(struct vnode *vp, struct uio *uio,
+ struct ucred *cred, struct fuse_filehandle *fufh)
+{
+ struct buf *bp;
+ daddr_t lbn;
+ int bcount;
+ int err = 0, n = 0, on = 0;
+ off_t filesize;
+
+ const int biosize = fuse_iosize(vp);
+
+ DEBUG("resid=%zx offset=%jx fsize=%jx\n",
+ uio->uio_resid, uio->uio_offset, VTOFUD(vp)->filesize);
+
+ if (uio->uio_resid == 0)
+ return (0);
+ if (uio->uio_offset < 0)
+ return (EINVAL);
+
+ bcount = MIN(MAXBSIZE, biosize);
+ filesize = VTOFUD(vp)->filesize;
+
+ do {
+ if (fuse_isdeadfs(vp)) {
+ err = ENXIO;
+ break;
+ }
+ lbn = uio->uio_offset / biosize;
+ on = uio->uio_offset & (biosize - 1);
+
+ DEBUG2G("biosize %d, lbn %d, on %d\n", biosize, (int)lbn, on);
+
+ /*
+ * Obtain the buffer cache block. Figure out the buffer size
+ * when we are at EOF. If we are modifying the size of the
+ * buffer based on an EOF condition we need to hold
+ * nfs_rslock() through obtaining the buffer to prevent
+ * a potential writer-appender from messing with n_size.
+ * Otherwise we may accidently truncate the buffer and
+ * lose dirty data.
+ *
+ * Note that bcount is *not* DEV_BSIZE aligned.
+ */
+ if ((off_t)lbn * biosize >= filesize) {
+ bcount = 0;
+ } else if ((off_t)(lbn + 1) * biosize > filesize) {
+ bcount = filesize - (off_t)lbn *biosize;
+ }
+ bp = getblk(vp, lbn, bcount, PCATCH, 0, 0);
+
+ if (!bp)
+ return (EINTR);
+
+ /*
+ * If B_CACHE is not set, we must issue the read. If this
+ * fails, we return an error.
+ */
+
+ if ((bp->b_flags & B_CACHE) == 0) {
+ bp->b_iocmd = BIO_READ;
+ vfs_busy_pages(bp, 0);
+ err = fuse_io_strategy(vp, bp);
+ if (err) {
+ brelse(bp);
+ return (err);
+ }
+ }
+ /*
+ * on is the offset into the current bp. Figure out how many
+ * bytes we can copy out of the bp. Note that bcount is
+ * NOT DEV_BSIZE aligned.
+ *
+ * Then figure out how many bytes we can copy into the uio.
+ */
+
+ n = 0;
+ if (on < bcount)
+ n = MIN((unsigned)(bcount - on), uio->uio_resid);
+ if (n > 0) {
+ DEBUG2G("feeding buffeater with %d bytes of buffer %p,"
+ " saying %d was asked for\n",
+ n, bp->b_data + on, n + (int)bp->b_resid);
+ err = uiomove(bp->b_data + on, n, uio);
+ }
+ brelse(bp);
+ DEBUG2G("end of turn, err %d, uio->uio_resid %zd, n %d\n",
+ err, uio->uio_resid, n);
+ } while (err == 0 && uio->uio_resid > 0 && n > 0);
+
+ return (err);
+}
+
+static int
+fuse_read_directbackend(struct vnode *vp, struct uio *uio,
+ struct ucred *cred, struct fuse_filehandle *fufh)
+{
+ struct fuse_dispatcher fdi;
+ struct fuse_read_in *fri;
+ int err = 0;
+
+ if (uio->uio_resid == 0)
+ return (0);
+
+ fdisp_init(&fdi, 0);
+
+ /*
+ * XXX In "normal" case we use an intermediate kernel buffer for
+ * transmitting data from daemon's context to ours. Eventually, we should
+ * get rid of this. Anyway, if the target uio lives in sysspace (we are
+ * called from pageops), and the input data doesn't need kernel-side
+ * processing (we are not called from readdir) we can already invoke
+ * an optimized, "peer-to-peer" I/O routine.
+ */
+ while (uio->uio_resid > 0) {
+ fdi.iosize = sizeof(*fri);
+ fdisp_make_vp(&fdi, FUSE_READ, vp, uio->uio_td, cred);
+ fri = fdi.indata;
+ fri->fh = fufh->fh_id;
+ fri->offset = uio->uio_offset;
+ fri->size = MIN(uio->uio_resid,
+ fuse_get_mpdata(vp->v_mount)->max_read);
+
+ DEBUG2G("fri->fh %ju, fri->offset %ju, fri->size %ju\n",
+ (uintmax_t)fri->fh, (uintmax_t)fri->offset,
+ (uintmax_t)fri->size);
+
+ if ((err = fdisp_wait_answ(&fdi)))
+ goto out;
+
+ DEBUG2G("complete: got iosize=%d, requested fri.size=%zd; "
+ "resid=%zd offset=%ju\n",
+ fri->size, fdi.iosize, uio->uio_resid,
+ (uintmax_t)uio->uio_offset);
+
+ if ((err = uiomove(fdi.answ, MIN(fri->size, fdi.iosize), uio)))
+ break;
+ if (fdi.iosize < fri->size)
+ break;
+ }
+
+out:
+ fdisp_destroy(&fdi);
+ return (err);
+}
+
+static int
+fuse_write_directbackend(struct vnode *vp, struct uio *uio,
+ struct ucred *cred, struct fuse_filehandle *fufh)
+{
+ struct fuse_vnode_data *fvdat = VTOFUD(vp);
+ struct fuse_write_in *fwi;
+ struct fuse_dispatcher fdi;
+ size_t chunksize;
+ int diff;
+ int err = 0;
+
+ if (!uio->uio_resid)
+ return (0);
+
+ fdisp_init(&fdi, 0);
+
+ while (uio->uio_resid > 0) {
+ chunksize = MIN(uio->uio_resid,
+ fuse_get_mpdata(vp->v_mount)->max_write);
+
+ fdi.iosize = sizeof(*fwi) + chunksize;
+ fdisp_make_vp(&fdi, FUSE_WRITE, vp, uio->uio_td, cred);
+
+ fwi = fdi.indata;
+ fwi->fh = fufh->fh_id;
+ fwi->offset = uio->uio_offset;
+ fwi->size = chunksize;
+
+ if ((err = uiomove((char *)fdi.indata + sizeof(*fwi),
+ chunksize, uio)))
+ break;
+
+ if ((err = fdisp_wait_answ(&fdi)))
+ break;
+
+ diff = chunksize - ((struct fuse_write_out *)fdi.answ)->size;
+ if (diff < 0) {
+ err = EINVAL;
+ break;
+ }
+ uio->uio_resid += diff;
+ uio->uio_offset -= diff;
+ if (uio->uio_offset > fvdat->filesize)
+ fuse_vnode_setsize(vp, cred, uio->uio_offset);
+ }
+
+ fdisp_destroy(&fdi);
+
+ return (err);
+}
+
+static int
+fuse_write_biobackend(struct vnode *vp, struct uio *uio,
+ struct ucred *cred, struct fuse_filehandle *fufh)
+{
+ struct fuse_vnode_data *fvdat = VTOFUD(vp);
+ struct buf *bp;
+ daddr_t lbn;
+ int bcount;
+ int n, on, err = 0;
+
+ const int biosize = fuse_iosize(vp);
+
+ KASSERT(uio->uio_rw == UIO_WRITE, ("ncl_write mode"));
+ DEBUG("resid=%zx offset=%jx fsize=%jx\n",
+ uio->uio_resid, uio->uio_offset, fvdat->filesize);
+ if (vp->v_type != VREG)
+ return (EIO);
+ if (uio->uio_offset < 0)
+ return (EINVAL);
+ if (uio->uio_resid == 0)
+ return (0);
+
+ /*
+ * Find all of this file's B_NEEDCOMMIT buffers. If our writes
+ * would exceed the local maximum per-file write commit size when
+ * combined with those, we must decide whether to flush,
+ * go synchronous, or return err. We don't bother checking
+ * IO_UNIT -- we just make all writes atomic anyway, as there's
+ * no point optimizing for something that really won't ever happen.
+ */
+ do {
+ if (fuse_isdeadfs(vp)) {
+ err = ENXIO;
+ break;
+ }
+ lbn = uio->uio_offset / biosize;
+ on = uio->uio_offset & (biosize - 1);
+ n = MIN((unsigned)(biosize - on), uio->uio_resid);
+
+ DEBUG2G("lbn %ju, on %d, n %d, uio offset %ju, uio resid %zd\n",
+ (uintmax_t)lbn, on, n,
+ (uintmax_t)uio->uio_offset, uio->uio_resid);
+
+again:
+ /*
+ * Handle direct append and file extension cases, calculate
+ * unaligned buffer size.
+ */
+ if (uio->uio_offset == fvdat->filesize && n) {
+ /*
+ * Get the buffer (in its pre-append state to maintain
+ * B_CACHE if it was previously set). Resize the
+ * nfsnode after we have locked the buffer to prevent
+ * readers from reading garbage.
+ */
+ bcount = on;
+ DEBUG("getting block from OS, bcount %d\n", bcount);
+ bp = getblk(vp, lbn, bcount, PCATCH, 0, 0);
+
+ if (bp != NULL) {
+ long save;
+
+ err = fuse_vnode_setsize(vp, cred,
+ uio->uio_offset + n);
+ if (err) {
+ brelse(bp);
+ break;
+ }
+ save = bp->b_flags & B_CACHE;
+ bcount += n;
+ allocbuf(bp, bcount);
+ bp->b_flags |= save;
+ }
+ } else {
+ /*
+ * Obtain the locked cache block first, and then
+ * adjust the file's size as appropriate.
+ */
+ bcount = on + n;
+ if ((off_t)lbn * biosize + bcount < fvdat->filesize) {
+ if ((off_t)(lbn + 1) * biosize < fvdat->filesize)
+ bcount = biosize;
+ else
+ bcount = fvdat->filesize -
+ (off_t)lbn *biosize;
+ }
+ DEBUG("getting block from OS, bcount %d\n", bcount);
+ bp = getblk(vp, lbn, bcount, PCATCH, 0, 0);
+ if (bp && uio->uio_offset + n > fvdat->filesize) {
+ err = fuse_vnode_setsize(vp, cred,
+ uio->uio_offset + n);
+ if (err) {
+ brelse(bp);
+ break;
+ }
+ }
+ }
+
+ if (!bp) {
+ err = EINTR;
+ break;
+ }
+ /*
+ * Issue a READ if B_CACHE is not set. In special-append
+ * mode, B_CACHE is based on the buffer prior to the write
+ * op and is typically set, avoiding the read. If a read
+ * is required in special append mode, the server will
+ * probably send us a short-read since we extended the file
+ * on our end, resulting in b_resid == 0 and, thusly,
+ * B_CACHE getting set.
+ *
+ * We can also avoid issuing the read if the write covers
+ * the entire buffer. We have to make sure the buffer state
+ * is reasonable in this case since we will not be initiating
+ * I/O. See the comments in kern/vfs_bio.c's getblk() for
+ * more information.
+ *
+ * B_CACHE may also be set due to the buffer being cached
+ * normally.
+ */
+
+ if (on == 0 && n == bcount) {
+ bp->b_flags |= B_CACHE;
+ bp->b_flags &= ~B_INVAL;
+ bp->b_ioflags &= ~BIO_ERROR;
+ }
+ if ((bp->b_flags & B_CACHE) == 0) {
+ bp->b_iocmd = BIO_READ;
+ vfs_busy_pages(bp, 0);
+ fuse_io_strategy(vp, bp);
+ if ((err = bp->b_error)) {
+ brelse(bp);
+ break;
+ }
+ }
+ if (bp->b_wcred == NOCRED)
+ bp->b_wcred = crhold(cred);
+
+ /*
+ * If dirtyend exceeds file size, chop it down. This should
+ * not normally occur but there is an append race where it
+ * might occur XXX, so we log it.
+ *
+ * If the chopping creates a reverse-indexed or degenerate
+ * situation with dirtyoff/end, we 0 both of them.
+ */
+
+ if (bp->b_dirtyend > bcount) {
+ DEBUG("FUSE append race @%lx:%d\n",
+ (long)bp->b_blkno * biosize,
+ bp->b_dirtyend - bcount);
+ bp->b_dirtyend = bcount;
+ }
+ if (bp->b_dirtyoff >= bp->b_dirtyend)
+ bp->b_dirtyoff = bp->b_dirtyend = 0;
+
+ /*
+ * If the new write will leave a contiguous dirty
+ * area, just update the b_dirtyoff and b_dirtyend,
+ * otherwise force a write rpc of the old dirty area.
+ *
+ * While it is possible to merge discontiguous writes due to
+ * our having a B_CACHE buffer ( and thus valid read data
+ * for the hole), we don't because it could lead to
+ * significant cache coherency problems with multiple clients,
+ * especially if locking is implemented later on.
+ *
+ * as an optimization we could theoretically maintain
+ * a linked list of discontinuous areas, but we would still
+ * have to commit them separately so there isn't much
+ * advantage to it except perhaps a bit of asynchronization.
+ */
+
+ if (bp->b_dirtyend > 0 &&
+ (on > bp->b_dirtyend || (on + n) < bp->b_dirtyoff)) {
+ /*
+ * Yes, we mean it. Write out everything to "storage"
+ * immediatly, without hesitation. (Apart from other
+ * reasons: the only way to know if a write is valid
+ * if its actually written out.)
+ */
+ bwrite(bp);
+ if (bp->b_error == EINTR) {
+ err = EINTR;
+ break;
+ }
+ goto again;
+ }
+ err = uiomove((char *)bp->b_data + on, n, uio);
+
+ /*
+ * Since this block is being modified, it must be written
+ * again and not just committed. Since write clustering does
+ * not work for the stage 1 data write, only the stage 2
+ * commit rpc, we have to clear B_CLUSTEROK as well.
+ */
+ bp->b_flags &= ~(B_NEEDCOMMIT | B_CLUSTEROK);
+
+ if (err) {
+ bp->b_ioflags |= BIO_ERROR;
+ bp->b_error = err;
+ brelse(bp);
+ break;
+ }
+ /*
+ * Only update dirtyoff/dirtyend if not a degenerate
+ * condition.
+ */
+ if (n) {
+ if (bp->b_dirtyend > 0) {
+ bp->b_dirtyoff = MIN(on, bp->b_dirtyoff);
+ bp->b_dirtyend = MAX((on + n), bp->b_dirtyend);
+ } else {
+ bp->b_dirtyoff = on;
+ bp->b_dirtyend = on + n;
+ }
+ vfs_bio_set_valid(bp, on, n);
+ }
+ err = bwrite(bp);
+ if (err)
+ break;
+ } while (uio->uio_resid > 0 && n > 0);
+
+ if (fuse_sync_resize && (fvdat->flag & FN_SIZECHANGE) != 0)
+ fuse_vnode_savesize(vp, cred);
+
+ return (err);
+}
+
+int
+fuse_io_strategy(struct vnode *vp, struct buf *bp)
+{
+ struct fuse_filehandle *fufh;
+ struct fuse_vnode_data *fvdat = VTOFUD(vp);
+ struct ucred *cred;
+ struct uio *uiop;
+ struct uio uio;
+ struct iovec io;
+ int error = 0;
+
+ const int biosize = fuse_iosize(vp);
+
+ MPASS(vp->v_type == VREG);
+ MPASS(bp->b_iocmd == BIO_READ || bp->b_iocmd == BIO_WRITE);
+ DEBUG("inode=%ju offset=%jd resid=%ld\n",
+ (uintmax_t)VTOI(vp), (intmax_t)(((off_t)bp->b_blkno) * biosize),
+ bp->b_bcount);
+
+ error = fuse_filehandle_getrw(vp,
+ (bp->b_iocmd == BIO_READ) ? FUFH_RDONLY : FUFH_WRONLY, &fufh);
+ if (error) {
+ printf("FUSE: strategy: filehandles are closed\n");
+ bp->b_ioflags |= BIO_ERROR;
+ bp->b_error = error;
+ return (error);
+ }
+ cred = bp->b_iocmd == BIO_READ ? bp->b_rcred : bp->b_wcred;
+
+ uiop = &uio;
+ uiop->uio_iov = &io;
+ uiop->uio_iovcnt = 1;
+ uiop->uio_segflg = UIO_SYSSPACE;
+ uiop->uio_td = curthread;
+
+ /*
+ * clear BIO_ERROR and B_INVAL state prior to initiating the I/O. We
+ * do this here so we do not have to do it in all the code that
+ * calls us.
+ */
+ bp->b_flags &= ~B_INVAL;
+ bp->b_ioflags &= ~BIO_ERROR;
+
+ KASSERT(!(bp->b_flags & B_DONE),
+ ("fuse_io_strategy: bp %p already marked done", bp));
+ if (bp->b_iocmd == BIO_READ) {
+ io.iov_len = uiop->uio_resid = bp->b_bcount;
+ io.iov_base = bp->b_data;
+ uiop->uio_rw = UIO_READ;
+
+ uiop->uio_offset = ((off_t)bp->b_blkno) * biosize;
+ error = fuse_read_directbackend(vp, uiop, cred, fufh);
+
+ if ((!error && uiop->uio_resid) ||
+ (fsess_opt_brokenio(vnode_mount(vp)) && error == EIO &&
+ uiop->uio_offset < fvdat->filesize && fvdat->filesize > 0 &&
+ uiop->uio_offset >= fvdat->cached_attrs.va_size)) {
+ /*
+ * If we had a short read with no error, we must have
+ * hit a file hole. We should zero-fill the remainder.
+ * This can also occur if the server hits the file EOF.
+ *
+ * Holes used to be able to occur due to pending
+ * writes, but that is not possible any longer.
+ */
+ int nread = bp->b_bcount - uiop->uio_resid;
+ int left = uiop->uio_resid;
+
+ if (error != 0) {
+ printf("FUSE: Fix broken io: offset %ju, "
+ " resid %zd, file size %ju/%ju\n",
+ (uintmax_t)uiop->uio_offset,
+ uiop->uio_resid, fvdat->filesize,
+ fvdat->cached_attrs.va_size);
+ error = 0;
+ }
+ if (left > 0)
+ bzero((char *)bp->b_data + nread, left);
+ uiop->uio_resid = 0;
+ }
+ if (error) {
+ bp->b_ioflags |= BIO_ERROR;
+ bp->b_error = error;
+ }
+ } else {
+ /*
+ * If we only need to commit, try to commit
+ */
+ if (bp->b_flags & B_NEEDCOMMIT) {
+ DEBUG("write: B_NEEDCOMMIT flags set\n");
+ }
+ /*
+ * Setup for actual write
+ */
+ if ((off_t)bp->b_blkno * biosize + bp->b_dirtyend >
+ fvdat->filesize)
+ bp->b_dirtyend = fvdat->filesize -
+ (off_t)bp->b_blkno * biosize;
+
+ if (bp->b_dirtyend > bp->b_dirtyoff) {
+ io.iov_len = uiop->uio_resid = bp->b_dirtyend
+ - bp->b_dirtyoff;
+ uiop->uio_offset = (off_t)bp->b_blkno * biosize
+ + bp->b_dirtyoff;
+ io.iov_base = (char *)bp->b_data + bp->b_dirtyoff;
+ uiop->uio_rw = UIO_WRITE;
+
+ error = fuse_write_directbackend(vp, uiop, cred, fufh);
+
+ if (error == EINTR || error == ETIMEDOUT
+ || (!error && (bp->b_flags & B_NEEDCOMMIT))) {
+
+ bp->b_flags &= ~(B_INVAL | B_NOCACHE);
+ if ((bp->b_flags & B_PAGING) == 0) {
+ bdirty(bp);
+ bp->b_flags &= ~B_DONE;
+ }
+ if ((error == EINTR || error == ETIMEDOUT) &&
+ (bp->b_flags & B_ASYNC) == 0)
+ bp->b_flags |= B_EINTR;
+ } else {
+ if (error) {
+ bp->b_ioflags |= BIO_ERROR;
+ bp->b_flags |= B_INVAL;
+ bp->b_error = error;
+ }
+ bp->b_dirtyoff = bp->b_dirtyend = 0;
+ }
+ } else {
+ bp->b_resid = 0;
+ bufdone(bp);
+ return (0);
+ }
+ }
+ bp->b_resid = uiop->uio_resid;
+ bufdone(bp);
+ return (error);
+}
+
+int
+fuse_io_flushbuf(struct vnode *vp, int waitfor, struct thread *td)
+{
+ struct vop_fsync_args a = {
+ .a_vp = vp,
+ .a_waitfor = waitfor,
+ .a_td = td,
+ };
+
+ return (vop_stdfsync(&a));
+}
+
+/*
+ * Flush and invalidate all dirty buffers. If another process is already
+ * doing the flush, just wait for completion.
+ */
+int
+fuse_io_invalbuf(struct vnode *vp, struct thread *td)
+{
+ struct fuse_vnode_data *fvdat = VTOFUD(vp);
+ int error = 0;
+
+ if (vp->v_iflag & VI_DOOMED)
+ return 0;
+
+ ASSERT_VOP_ELOCKED(vp, "fuse_io_invalbuf");
+
+ while (fvdat->flag & FN_FLUSHINPROG) {
+ struct proc *p = td->td_proc;
+
+ if (vp->v_mount->mnt_kern_flag & MNTK_UNMOUNTF)
+ return EIO;
+ fvdat->flag |= FN_FLUSHWANT;
+ tsleep(&fvdat->flag, PRIBIO + 2, "fusevinv", 2 * hz);
+ error = 0;
+ if (p != NULL) {
+ PROC_LOCK(p);
+ if (SIGNOTEMPTY(p->p_siglist) ||
+ SIGNOTEMPTY(td->td_siglist))
+ error = EINTR;
+ PROC_UNLOCK(p);
+ }
+ if (error == EINTR)
+ return EINTR;
+ }
+ fvdat->flag |= FN_FLUSHINPROG;
+
+ if (vp->v_bufobj.bo_object != NULL) {
+ VM_OBJECT_LOCK(vp->v_bufobj.bo_object);
+ vm_object_page_clean(vp->v_bufobj.bo_object, 0, 0, OBJPC_SYNC);
+ VM_OBJECT_UNLOCK(vp->v_bufobj.bo_object);
+ }
+ error = vinvalbuf(vp, V_SAVE, PCATCH, 0);
+ while (error) {
+ if (error == ERESTART || error == EINTR) {
+ fvdat->flag &= ~FN_FLUSHINPROG;
+ if (fvdat->flag & FN_FLUSHWANT) {
+ fvdat->flag &= ~FN_FLUSHWANT;
+ wakeup(&fvdat->flag);
+ }
+ return EINTR;
+ }
+ error = vinvalbuf(vp, V_SAVE, PCATCH, 0);
+ }
+ fvdat->flag &= ~FN_FLUSHINPROG;
+ if (fvdat->flag & FN_FLUSHWANT) {
+ fvdat->flag &= ~FN_FLUSHWANT;
+ wakeup(&fvdat->flag);
+ }
+ return (error);
+}
diff --git a/sys/fs/fuse/fuse_io.h b/sys/fs/fuse/fuse_io.h
new file mode 100644
index 0000000..b56e14a
--- /dev/null
+++ b/sys/fs/fuse/fuse_io.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2007-2009 Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
+ * OWNER 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.
+ *
+ * Copyright (C) 2005 Csaba Henk.
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _FUSE_IO_H_
+#define _FUSE_IO_H_
+
+int fuse_io_dispatch(struct vnode *vp, struct uio *uio, int ioflag,
+ struct ucred *cred);
+int fuse_io_strategy(struct vnode *vp, struct buf *bp);
+int fuse_io_flushbuf(struct vnode *vp, int waitfor, struct thread *td);
+int fuse_io_invalbuf(struct vnode *vp, struct thread *td);
+
+#endif /* _FUSE_IO_H_ */
diff --git a/sys/fs/fuse/fuse_ipc.c b/sys/fs/fuse/fuse_ipc.c
new file mode 100644
index 0000000..52d69b5
--- /dev/null
+++ b/sys/fs/fuse/fuse_ipc.c
@@ -0,0 +1,904 @@
+/*
+ * Copyright (c) 2007-2009 Google Inc. and Amit Singh
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
+ * OWNER 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.
+ *
+ * Copyright (C) 2005 Csaba Henk.
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/module.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/conf.h>
+#include <sys/uio.h>
+#include <sys/malloc.h>
+#include <sys/queue.h>
+#include <sys/lock.h>
+#include <sys/sx.h>
+#include <sys/mutex.h>
+#include <sys/proc.h>
+#include <sys/mount.h>
+#include <sys/vnode.h>
+#include <sys/signalvar.h>
+#include <sys/syscallsubr.h>
+#include <sys/sysctl.h>
+#include <vm/uma.h>
+
+#include "fuse.h"
+#include "fuse_node.h"
+#include "fuse_ipc.h"
+#include "fuse_internal.h"
+
+#define FUSE_DEBUG_MODULE IPC
+#include "fuse_debug.h"
+
+static struct fuse_ticket *fticket_alloc(struct fuse_data *data);
+static void fticket_refresh(struct fuse_ticket *ftick);
+static void fticket_destroy(struct fuse_ticket *ftick);
+static int fticket_wait_answer(struct fuse_ticket *ftick);
+static __inline__ int
+fticket_aw_pull_uio(struct fuse_ticket *ftick,
+ struct uio *uio);
+
+static int fuse_body_audit(struct fuse_ticket *ftick, size_t blen);
+static __inline__ void
+fuse_setup_ihead(struct fuse_in_header *ihead,
+ struct fuse_ticket *ftick,
+ uint64_t nid,
+ enum fuse_opcode op,
+ size_t blen,
+ pid_t pid,
+ struct ucred *cred);
+
+static fuse_handler_t fuse_standard_handler;
+
+SYSCTL_NODE(_vfs, OID_AUTO, fuse, CTLFLAG_RW, 0, "FUSE tunables");
+SYSCTL_STRING(_vfs_fuse, OID_AUTO, version, CTLFLAG_RD,
+ FUSE_FREEBSD_VERSION, 0, "fuse-freebsd version");
+static int fuse_ticket_count = 0;
+
+SYSCTL_INT(_vfs_fuse, OID_AUTO, ticket_count, CTLFLAG_RW,
+ &fuse_ticket_count, 0, "number of allocated tickets");
+static long fuse_iov_permanent_bufsize = 1 << 19;
+
+SYSCTL_LONG(_vfs_fuse, OID_AUTO, iov_permanent_bufsize, CTLFLAG_RW,
+ &fuse_iov_permanent_bufsize, 0,
+ "limit for permanently stored buffer size for fuse_iovs");
+static int fuse_iov_credit = 16;
+
+SYSCTL_INT(_vfs_fuse, OID_AUTO, iov_credit, CTLFLAG_RW,
+ &fuse_iov_credit, 0,
+ "how many times is an oversized fuse_iov tolerated");
+
+MALLOC_DEFINE(M_FUSEMSG, "fuse_msgbuf", "fuse message buffer");
+static uma_zone_t ticket_zone;
+
+static void
+fuse_block_sigs(sigset_t *oldset)
+{
+ sigset_t newset;
+
+ SIGFILLSET(newset);
+ SIGDELSET(newset, SIGKILL);
+ if (kern_sigprocmask(curthread, SIG_BLOCK, &newset, oldset, 0))
+ panic("%s: Invalid operation for kern_sigprocmask()",
+ __func__);
+}
+
+static void
+fuse_restore_sigs(sigset_t *oldset)
+{
+
+ if (kern_sigprocmask(curthread, SIG_SETMASK, oldset, NULL, 0))
+ panic("%s: Invalid operation for kern_sigprocmask()",
+ __func__);
+}
+
+void
+fiov_init(struct fuse_iov *fiov, size_t size)
+{
+ uint32_t msize = FU_AT_LEAST(size);
+
+ debug_printf("fiov=%p, size=%zd\n", fiov, size);
+
+ fiov->len = 0;
+
+ fiov->base = malloc(msize, M_FUSEMSG, M_WAITOK | M_ZERO);
+
+ fiov->allocated_size = msize;
+ fiov->credit = fuse_iov_credit;
+}
+
+void
+fiov_teardown(struct fuse_iov *fiov)
+{
+ debug_printf("fiov=%p\n", fiov);
+
+ MPASS(fiov->base != NULL);
+ free(fiov->base, M_FUSEMSG);
+}
+
+void
+fiov_adjust(struct fuse_iov *fiov, size_t size)
+{
+ debug_printf("fiov=%p, size=%zd\n", fiov, size);
+
+ if (fiov->allocated_size < size ||
+ (fuse_iov_permanent_bufsize >= 0 &&
+ fiov->allocated_size - size > fuse_iov_permanent_bufsize &&
+ --fiov->credit < 0)) {
+
+ fiov->base = realloc(fiov->base, FU_AT_LEAST(size), M_FUSEMSG,
+ M_WAITOK | M_ZERO);
+ if (!fiov->base) {
+ panic("FUSE: realloc failed");
+ }
+ fiov->allocated_size = FU_AT_LEAST(size);
+ fiov->credit = fuse_iov_credit;
+ }
+ fiov->len = size;
+}
+
+void
+fiov_refresh(struct fuse_iov *fiov)
+{
+ debug_printf("fiov=%p\n", fiov);
+
+ bzero(fiov->base, fiov->len);
+ fiov_adjust(fiov, 0);
+}
+
+static int
+fticket_ctor(void *mem, int size, void *arg, int flags)
+{
+ struct fuse_ticket *ftick = mem;
+ struct fuse_data *data = arg;
+
+ debug_printf("ftick=%p data=%p\n", ftick, data);
+
+ FUSE_ASSERT_MS_DONE(ftick);
+ FUSE_ASSERT_AW_DONE(ftick);
+
+ ftick->tk_data = data;
+
+ if (ftick->tk_unique != 0)
+ fticket_refresh(ftick);
+
+ /* May be truncated to 32 bits */
+ ftick->tk_unique = atomic_fetchadd_long(&data->ticketer, 1);
+ if (ftick->tk_unique == 0)
+ ftick->tk_unique = atomic_fetchadd_long(&data->ticketer, 1);
+
+ refcount_init(&ftick->tk_refcount, 1);
+ atomic_add_acq_int(&fuse_ticket_count, 1);
+
+ return 0;
+}
+
+static void
+fticket_dtor(void *mem, int size, void *arg)
+{
+ struct fuse_ticket *ftick = mem;
+
+ debug_printf("ftick=%p\n", ftick);
+
+ FUSE_ASSERT_MS_DONE(ftick);
+ FUSE_ASSERT_AW_DONE(ftick);
+
+ atomic_subtract_acq_int(&fuse_ticket_count, 1);
+}
+
+static int
+fticket_init(void *mem, int size, int flags)
+{
+ struct fuse_ticket *ftick = mem;
+
+ DEBUG("ftick=%p\n", ftick);
+
+ bzero(ftick, sizeof(struct fuse_ticket));
+
+ fiov_init(&ftick->tk_ms_fiov, sizeof(struct fuse_in_header));
+ ftick->tk_ms_type = FT_M_FIOV;
+
+ mtx_init(&ftick->tk_aw_mtx, "fuse answer delivery mutex", NULL, MTX_DEF);
+ fiov_init(&ftick->tk_aw_fiov, 0);
+ ftick->tk_aw_type = FT_A_FIOV;
+
+ return 0;
+}
+
+static void
+fticket_fini(void *mem, int size)
+{
+ struct fuse_ticket *ftick = mem;
+
+ DEBUG("ftick=%p\n", ftick);
+
+ fiov_teardown(&ftick->tk_ms_fiov);
+ fiov_teardown(&ftick->tk_aw_fiov);
+ mtx_destroy(&ftick->tk_aw_mtx);
+}
+
+static __inline struct fuse_ticket *
+fticket_alloc(struct fuse_data *data)
+{
+ return uma_zalloc_arg(ticket_zone, data, M_WAITOK);
+}
+
+static __inline void
+fticket_destroy(struct fuse_ticket *ftick)
+{
+ return uma_zfree(ticket_zone, ftick);
+}
+
+static __inline__
+void
+fticket_refresh(struct fuse_ticket *ftick)
+{
+ debug_printf("ftick=%p\n", ftick);
+
+ FUSE_ASSERT_MS_DONE(ftick);
+ FUSE_ASSERT_AW_DONE(ftick);
+
+ fiov_refresh(&ftick->tk_ms_fiov);
+ ftick->tk_ms_bufdata = NULL;
+ ftick->tk_ms_bufsize = 0;
+ ftick->tk_ms_type = FT_M_FIOV;
+
+ bzero(&ftick->tk_aw_ohead, sizeof(struct fuse_out_header));
+
+ fiov_refresh(&ftick->tk_aw_fiov);
+ ftick->tk_aw_errno = 0;
+ ftick->tk_aw_bufdata = NULL;
+ ftick->tk_aw_bufsize = 0;
+ ftick->tk_aw_type = FT_A_FIOV;
+
+ ftick->tk_flag = 0;
+}
+
+static int
+fticket_wait_answer(struct fuse_ticket *ftick)
+{
+ sigset_t tset;
+ int err = 0;
+ struct fuse_data *data;
+
+ debug_printf("ftick=%p\n", ftick);
+ fuse_lck_mtx_lock(ftick->tk_aw_mtx);
+
+ if (fticket_answered(ftick)) {
+ goto out;
+ }
+ data = ftick->tk_data;
+
+ if (fdata_get_dead(data)) {
+ err = ENOTCONN;
+ fticket_set_answered(ftick);
+ goto out;
+ }
+ fuse_block_sigs(&tset);
+ err = msleep(ftick, &ftick->tk_aw_mtx, PCATCH, "fu_ans",
+ data->daemon_timeout * hz);
+ fuse_restore_sigs(&tset);
+ if (err == EAGAIN) { /* same as EWOULDBLOCK */
+#ifdef XXXIP /* die conditionally */
+ if (!fdata_get_dead(data)) {
+ fdata_set_dead(data);
+ }
+#endif
+ err = ETIMEDOUT;
+ fticket_set_answered(ftick);
+ }
+out:
+ if (!(err || fticket_answered(ftick))) {
+ debug_printf("FUSE: requester was woken up but still no answer");
+ err = ENXIO;
+ }
+ fuse_lck_mtx_unlock(ftick->tk_aw_mtx);
+
+ return err;
+}
+
+static __inline__
+int
+fticket_aw_pull_uio(struct fuse_ticket *ftick, struct uio *uio)
+{
+ int err = 0;
+ size_t len = uio_resid(uio);
+
+ debug_printf("ftick=%p, uio=%p\n", ftick, uio);
+
+ if (len) {
+ switch (ftick->tk_aw_type) {
+ case FT_A_FIOV:
+ fiov_adjust(fticket_resp(ftick), len);
+ err = uiomove(fticket_resp(ftick)->base, len, uio);
+ if (err) {
+ debug_printf("FUSE: FT_A_FIOV: error is %d"
+ " (%p, %zd, %p)\n",
+ err, fticket_resp(ftick)->base,
+ len, uio);
+ }
+ break;
+
+ case FT_A_BUF:
+ ftick->tk_aw_bufsize = len;
+ err = uiomove(ftick->tk_aw_bufdata, len, uio);
+ if (err) {
+ debug_printf("FUSE: FT_A_BUF: error is %d"
+ " (%p, %zd, %p)\n",
+ err, ftick->tk_aw_bufdata, len, uio);
+ }
+ break;
+
+ default:
+ panic("FUSE: unknown answer type for ticket %p", ftick);
+ }
+ }
+ return err;
+}
+
+int
+fticket_pull(struct fuse_ticket *ftick, struct uio *uio)
+{
+ int err = 0;
+
+ debug_printf("ftick=%p, uio=%p\n", ftick, uio);
+
+ if (ftick->tk_aw_ohead.error) {
+ return 0;
+ }
+ err = fuse_body_audit(ftick, uio_resid(uio));
+ if (!err) {
+ err = fticket_aw_pull_uio(ftick, uio);
+ }
+ return err;
+}
+
+struct fuse_data *
+fdata_alloc(struct cdev *fdev, struct ucred *cred)
+{
+ struct fuse_data *data;
+
+ debug_printf("fdev=%p\n", fdev);
+
+ data = malloc(sizeof(struct fuse_data), M_FUSEMSG, M_WAITOK | M_ZERO);
+
+ data->fdev = fdev;
+ mtx_init(&data->ms_mtx, "fuse message list mutex", NULL, MTX_DEF);
+ STAILQ_INIT(&data->ms_head);
+ mtx_init(&data->aw_mtx, "fuse answer list mutex", NULL, MTX_DEF);
+ TAILQ_INIT(&data->aw_head);
+ data->daemoncred = crhold(cred);
+ data->daemon_timeout = FUSE_DEFAULT_DAEMON_TIMEOUT;
+ sx_init(&data->rename_lock, "fuse rename lock");
+ data->ref = 1;
+
+ return data;
+}
+
+void
+fdata_trydestroy(struct fuse_data *data)
+{
+ DEBUG("data=%p data.mp=%p data.fdev=%p data.flags=%04x\n",
+ data, data->mp, data->fdev, data->dataflags);
+
+ DEBUG("destroy: data=%p\n", data);
+ data->ref--;
+ MPASS(data->ref >= 0);
+ if (data->ref != 0)
+ return;
+
+ /* Driving off stage all that stuff thrown at device... */
+ mtx_destroy(&data->ms_mtx);
+ mtx_destroy(&data->aw_mtx);
+ sx_destroy(&data->rename_lock);
+
+ crfree(data->daemoncred);
+
+ free(data, M_FUSEMSG);
+}
+
+void
+fdata_set_dead(struct fuse_data *data)
+{
+ debug_printf("data=%p\n", data);
+
+ FUSE_LOCK();
+ if (fdata_get_dead(data)) {
+ FUSE_UNLOCK();
+ return;
+ }
+ fuse_lck_mtx_lock(data->ms_mtx);
+ data->dataflags |= FSESS_DEAD;
+ wakeup_one(data);
+ selwakeuppri(&data->ks_rsel, PZERO + 1);
+ wakeup(&data->ticketer);
+ fuse_lck_mtx_unlock(data->ms_mtx);
+ FUSE_UNLOCK();
+}
+
+struct fuse_ticket *
+fuse_ticket_fetch(struct fuse_data *data)
+{
+ int err = 0;
+ struct fuse_ticket *ftick;
+
+ debug_printf("data=%p\n", data);
+
+ ftick = fticket_alloc(data);
+
+ if (!(data->dataflags & FSESS_INITED)) {
+ /* Sleep until get answer for INIT messsage */
+ FUSE_LOCK();
+ if (!(data->dataflags & FSESS_INITED) && data->ticketer > 2) {
+ err = msleep(&data->ticketer, &fuse_mtx, PCATCH | PDROP,
+ "fu_ini", 0);
+ if (err)
+ fdata_set_dead(data);
+ } else
+ FUSE_UNLOCK();
+ }
+ return ftick;
+}
+
+int
+fuse_ticket_drop(struct fuse_ticket *ftick)
+{
+ int die;
+
+ die = refcount_release(&ftick->tk_refcount);
+ debug_printf("ftick=%p refcount=%d\n", ftick, ftick->tk_refcount);
+ if (die)
+ fticket_destroy(ftick);
+
+ return die;
+}
+
+void
+fuse_insert_callback(struct fuse_ticket *ftick, fuse_handler_t * handler)
+{
+ debug_printf("ftick=%p, handler=%p data=%p\n", ftick, ftick->tk_data,
+ handler);
+
+ if (fdata_get_dead(ftick->tk_data)) {
+ return;
+ }
+ ftick->tk_aw_handler = handler;
+
+ fuse_lck_mtx_lock(ftick->tk_data->aw_mtx);
+ fuse_aw_push(ftick);
+ fuse_lck_mtx_unlock(ftick->tk_data->aw_mtx);
+}
+
+void
+fuse_insert_message(struct fuse_ticket *ftick)
+{
+ debug_printf("ftick=%p\n", ftick);
+
+ if (ftick->tk_flag & FT_DIRTY) {
+ panic("FUSE: ticket reused without being refreshed");
+ }
+ ftick->tk_flag |= FT_DIRTY;
+
+ if (fdata_get_dead(ftick->tk_data)) {
+ return;
+ }
+ fuse_lck_mtx_lock(ftick->tk_data->ms_mtx);
+ fuse_ms_push(ftick);
+ wakeup_one(ftick->tk_data);
+ selwakeuppri(&ftick->tk_data->ks_rsel, PZERO + 1);
+ fuse_lck_mtx_unlock(ftick->tk_data->ms_mtx);
+}
+
+static int
+fuse_body_audit(struct fuse_ticket *ftick, size_t blen)
+{
+ int err = 0;
+ enum fuse_opcode opcode;
+
+ debug_printf("ftick=%p, blen = %zu\n", ftick, blen);
+
+ opcode = fticket_opcode(ftick);
+
+ switch (opcode) {
+ case FUSE_LOOKUP:
+ err = (blen == sizeof(struct fuse_entry_out)) ? 0 : EINVAL;
+ break;
+
+ case FUSE_FORGET:
+ panic("FUSE: a handler has been intalled for FUSE_FORGET");
+ break;
+
+ case FUSE_GETATTR:
+ err = (blen == sizeof(struct fuse_attr_out)) ? 0 : EINVAL;
+ break;
+
+ case FUSE_SETATTR:
+ err = (blen == sizeof(struct fuse_attr_out)) ? 0 : EINVAL;
+ break;
+
+ case FUSE_READLINK:
+ err = (PAGE_SIZE >= blen) ? 0 : EINVAL;
+ break;
+
+ case FUSE_SYMLINK:
+ err = (blen == sizeof(struct fuse_entry_out)) ? 0 : EINVAL;
+ break;
+
+ case FUSE_MKNOD:
+ err = (blen == sizeof(struct fuse_entry_out)) ? 0 : EINVAL;
+ break;
+
+ case FUSE_MKDIR:
+ err = (blen == sizeof(struct fuse_entry_out)) ? 0 : EINVAL;
+ break;
+
+ case FUSE_UNLINK:
+ err = (blen == 0) ? 0 : EINVAL;
+ break;
+
+ case FUSE_RMDIR:
+ err = (blen == 0) ? 0 : EINVAL;
+ break;
+
+ case FUSE_RENAME:
+ err = (blen == 0) ? 0 : EINVAL;
+ break;
+
+ case FUSE_LINK:
+ err = (blen == sizeof(struct fuse_entry_out)) ? 0 : EINVAL;
+ break;
+
+ case FUSE_OPEN:
+ err = (blen == sizeof(struct fuse_open_out)) ? 0 : EINVAL;
+ break;
+
+ case FUSE_READ:
+ err = (((struct fuse_read_in *)(
+ (char *)ftick->tk_ms_fiov.base +
+ sizeof(struct fuse_in_header)
+ ))->size >= blen) ? 0 : EINVAL;
+ break;
+
+ case FUSE_WRITE:
+ err = (blen == sizeof(struct fuse_write_out)) ? 0 : EINVAL;
+ break;
+
+ case FUSE_STATFS:
+ if (fuse_libabi_geq(ftick->tk_data, 7, 4)) {
+ err = (blen == sizeof(struct fuse_statfs_out)) ?
+ 0 : EINVAL;
+ } else {
+ err = (blen == FUSE_COMPAT_STATFS_SIZE) ? 0 : EINVAL;
+ }
+ break;
+
+ case FUSE_RELEASE:
+ err = (blen == 0) ? 0 : EINVAL;
+ break;
+
+ case FUSE_FSYNC:
+ err = (blen == 0) ? 0 : EINVAL;
+ break;
+
+ case FUSE_SETXATTR:
+ panic("FUSE_SETXATTR implementor has forgotten to define a"
+ " response body format check");
+ break;
+
+ case FUSE_GETXATTR:
+ panic("FUSE_GETXATTR implementor has forgotten to define a"
+ " response body format check");
+ break;
+
+ case FUSE_LISTXATTR:
+ panic("FUSE_LISTXATTR implementor has forgotten to define a"
+ " response body format check");
+ break;
+
+ case FUSE_REMOVEXATTR:
+ panic("FUSE_REMOVEXATTR implementor has forgotten to define a"
+ " response body format check");
+ break;
+
+ case FUSE_FLUSH:
+ err = (blen == 0) ? 0 : EINVAL;
+ break;
+
+ case FUSE_INIT:
+ if (blen == sizeof(struct fuse_init_out) || blen == 8) {
+ err = 0;
+ } else {
+ err = EINVAL;
+ }
+ break;
+
+ case FUSE_OPENDIR:
+ err = (blen == sizeof(struct fuse_open_out)) ? 0 : EINVAL;
+ break;
+
+ case FUSE_READDIR:
+ err = (((struct fuse_read_in *)(
+ (char *)ftick->tk_ms_fiov.base +
+ sizeof(struct fuse_in_header)
+ ))->size >= blen) ? 0 : EINVAL;
+ break;
+
+ case FUSE_RELEASEDIR:
+ err = (blen == 0) ? 0 : EINVAL;
+ break;
+
+ case FUSE_FSYNCDIR:
+ err = (blen == 0) ? 0 : EINVAL;
+ break;
+
+ case FUSE_GETLK:
+ panic("FUSE: no response body format check for FUSE_GETLK");
+ break;
+
+ case FUSE_SETLK:
+ panic("FUSE: no response body format check for FUSE_SETLK");
+ break;
+
+ case FUSE_SETLKW:
+ panic("FUSE: no response body format check for FUSE_SETLKW");
+ break;
+
+ case FUSE_ACCESS:
+ err = (blen == 0) ? 0 : EINVAL;
+ break;
+
+ case FUSE_CREATE:
+ err = (blen == sizeof(struct fuse_entry_out) +
+ sizeof(struct fuse_open_out)) ? 0 : EINVAL;
+ break;
+
+ case FUSE_DESTROY:
+ err = (blen == 0) ? 0 : EINVAL;
+ break;
+
+ default:
+ panic("FUSE: opcodes out of sync (%d)\n", opcode);
+ }
+
+ return err;
+}
+
+static void
+fuse_setup_ihead(struct fuse_in_header *ihead,
+ struct fuse_ticket *ftick,
+ uint64_t nid,
+ enum fuse_opcode op,
+ size_t blen,
+ pid_t pid,
+ struct ucred *cred)
+{
+ ihead->len = sizeof(*ihead) + blen;
+ ihead->unique = ftick->tk_unique;
+ ihead->nodeid = nid;
+ ihead->opcode = op;
+
+ debug_printf("ihead=%p, ftick=%p, nid=%ju, op=%d, blen=%zu\n",
+ ihead, ftick, (uintmax_t)nid, op, blen);
+
+ ihead->pid = pid;
+ ihead->uid = cred->cr_uid;
+ ihead->gid = cred->cr_rgid;
+}
+
+/*
+ * fuse_standard_handler just pulls indata and wakes up pretender.
+ * Doesn't try to interpret data, that's left for the pretender.
+ * Though might do a basic size verification before the pull-in takes place
+ */
+
+static int
+fuse_standard_handler(struct fuse_ticket *ftick, struct uio *uio)
+{
+ int err = 0;
+
+ debug_printf("ftick=%p, uio=%p\n", ftick, uio);
+
+ err = fticket_pull(ftick, uio);
+
+ fuse_lck_mtx_lock(ftick->tk_aw_mtx);
+
+ if (!fticket_answered(ftick)) {
+ fticket_set_answered(ftick);
+ ftick->tk_aw_errno = err;
+ wakeup(ftick);
+ }
+ fuse_lck_mtx_unlock(ftick->tk_aw_mtx);
+
+ return err;
+}
+
+void
+fdisp_make_pid(struct fuse_dispatcher *fdip,
+ enum fuse_opcode op,
+ struct mount *mp,
+ uint64_t nid,
+ pid_t pid,
+ struct ucred *cred)
+{
+ struct fuse_data *data = fuse_get_mpdata(mp);
+
+ debug_printf("fdip=%p, op=%d, mp=%p, nid=%ju\n",
+ fdip, op, mp, (uintmax_t)nid);
+
+ if (fdip->tick) {
+ fticket_refresh(fdip->tick);
+ } else {
+ fdip->tick = fuse_ticket_fetch(data);
+ }
+
+ FUSE_DIMALLOC(&fdip->tick->tk_ms_fiov, fdip->finh,
+ fdip->indata, fdip->iosize);
+
+ fuse_setup_ihead(fdip->finh, fdip->tick, nid, op, fdip->iosize, pid, cred);
+}
+
+void
+fdisp_make(struct fuse_dispatcher *fdip,
+ enum fuse_opcode op,
+ struct mount *mp,
+ uint64_t nid,
+ struct thread *td,
+ struct ucred *cred)
+{
+ RECTIFY_TDCR(td, cred);
+
+ return fdisp_make_pid(fdip, op, mp, nid, td->td_proc->p_pid, cred);
+}
+
+void
+fdisp_make_vp(struct fuse_dispatcher *fdip,
+ enum fuse_opcode op,
+ struct vnode *vp,
+ struct thread *td,
+ struct ucred *cred)
+{
+ debug_printf("fdip=%p, op=%d, vp=%p\n", fdip, op, vp);
+ RECTIFY_TDCR(td, cred);
+ return fdisp_make_pid(fdip, op, vnode_mount(vp), VTOI(vp),
+ td->td_proc->p_pid, cred);
+}
+
+int
+fdisp_wait_answ(struct fuse_dispatcher *fdip)
+{
+ int err = 0;
+
+ fdip->answ_stat = 0;
+ fuse_insert_callback(fdip->tick, fuse_standard_handler);
+ fuse_insert_message(fdip->tick);
+
+ if ((err = fticket_wait_answer(fdip->tick))) {
+ debug_printf("IPC: interrupted, err = %d\n", err);
+
+ fuse_lck_mtx_lock(fdip->tick->tk_aw_mtx);
+
+ if (fticket_answered(fdip->tick)) {
+ /*
+ * Just between noticing the interrupt and getting here,
+ * the standard handler has completed his job.
+ * So we drop the ticket and exit as usual.
+ */
+ debug_printf("IPC: already answered\n");
+ fuse_lck_mtx_unlock(fdip->tick->tk_aw_mtx);
+ goto out;
+ } else {
+ /*
+ * So we were faster than the standard handler.
+ * Then by setting the answered flag we get *him*
+ * to drop the ticket.
+ */
+ debug_printf("IPC: setting to answered\n");
+ fticket_set_answered(fdip->tick);
+ fuse_lck_mtx_unlock(fdip->tick->tk_aw_mtx);
+ return err;
+ }
+ }
+ debug_printf("IPC: not interrupted, err = %d\n", err);
+
+ if (fdip->tick->tk_aw_errno) {
+ debug_printf("IPC: explicit EIO-ing, tk_aw_errno = %d\n",
+ fdip->tick->tk_aw_errno);
+ err = EIO;
+ goto out;
+ }
+ if ((err = fdip->tick->tk_aw_ohead.error)) {
+ debug_printf("IPC: setting status to %d\n",
+ fdip->tick->tk_aw_ohead.error);
+ /*
+ * This means a "proper" fuse syscall error.
+ * We record this value so the caller will
+ * be able to know it's not a boring messaging
+ * failure, if she wishes so (and if not, she can
+ * just simply propagate the return value of this routine).
+ * [XXX Maybe a bitflag would do the job too,
+ * if other flags needed, this will be converted thusly.]
+ */
+ fdip->answ_stat = err;
+ goto out;
+ }
+ fdip->answ = fticket_resp(fdip->tick)->base;
+ fdip->iosize = fticket_resp(fdip->tick)->len;
+
+ debug_printf("IPC: all is well\n");
+
+ return 0;
+
+out:
+ debug_printf("IPC: dropping ticket, err = %d\n", err);
+
+ return err;
+}
+
+void
+fuse_ipc_init(void)
+{
+ ticket_zone = uma_zcreate("fuse_ticket", sizeof(struct fuse_ticket),
+ fticket_ctor, fticket_dtor, fticket_init, fticket_fini,
+ UMA_ALIGN_PTR, 0);
+}
+
+void
+fuse_ipc_destroy(void)
+{
+ uma_zdestroy(ticket_zone);
+}
diff --git a/sys/fs/fuse/fuse_ipc.h b/sys/fs/fuse/fuse_ipc.h
new file mode 100644
index 0000000..cd08969
--- /dev/null
+++ b/sys/fs/fuse/fuse_ipc.h
@@ -0,0 +1,423 @@
+/*
+ * Copyright (c) 2007-2009 Google Inc. and Amit Singh
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
+ * OWNER 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.
+ *
+ * Copyright (C) 2005 Csaba Henk.
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _FUSE_IPC_H_
+#define _FUSE_IPC_H_
+
+#include <sys/param.h>
+#include <sys/refcount.h>
+
+struct fuse_iov {
+ void *base;
+ size_t len;
+ size_t allocated_size;
+ int credit;
+};
+
+void fiov_init(struct fuse_iov *fiov, size_t size);
+void fiov_teardown(struct fuse_iov *fiov);
+void fiov_refresh(struct fuse_iov *fiov);
+void fiov_adjust(struct fuse_iov *fiov, size_t size);
+
+#define FUSE_DIMALLOC(fiov, spc1, spc2, amnt) \
+do { \
+ fiov_adjust(fiov, (sizeof(*(spc1)) + (amnt))); \
+ (spc1) = (fiov)->base; \
+ (spc2) = (char *)(fiov)->base + (sizeof(*(spc1))); \
+} while (0)
+
+#define FU_AT_LEAST(siz) max((siz), 160)
+
+#define FUSE_ASSERT_AW_DONE(ftick) \
+ KASSERT((ftick)->tk_aw_link.tqe_next == NULL && \
+ (ftick)->tk_aw_link.tqe_prev == NULL, \
+ ("FUSE: ticket still on answer delivery list %p", (ftick))) \
+
+#define FUSE_ASSERT_MS_DONE(ftick) \
+ KASSERT((ftick)->tk_ms_link.stqe_next == NULL, \
+ ("FUSE: ticket still on message list %p", (ftick)))
+
+struct fuse_ticket;
+struct fuse_data;
+
+typedef int fuse_handler_t(struct fuse_ticket *ftick, struct uio *uio);
+
+struct fuse_ticket {
+ /* fields giving the identity of the ticket */
+ uint64_t tk_unique;
+ struct fuse_data *tk_data;
+ int tk_flag;
+ u_int tk_refcount;
+
+ /* fields for initiating an upgoing message */
+ struct fuse_iov tk_ms_fiov;
+ void *tk_ms_bufdata;
+ size_t tk_ms_bufsize;
+ enum { FT_M_FIOV, FT_M_BUF } tk_ms_type;
+ STAILQ_ENTRY(fuse_ticket) tk_ms_link;
+
+ /* fields for handling answers coming from userspace */
+ struct fuse_iov tk_aw_fiov;
+ void *tk_aw_bufdata;
+ size_t tk_aw_bufsize;
+ enum { FT_A_FIOV, FT_A_BUF } tk_aw_type;
+
+ struct fuse_out_header tk_aw_ohead;
+ int tk_aw_errno;
+ struct mtx tk_aw_mtx;
+ fuse_handler_t *tk_aw_handler;
+ TAILQ_ENTRY(fuse_ticket) tk_aw_link;
+};
+
+#define FT_ANSW 0x01 /* request of ticket has already been answered */
+#define FT_DIRTY 0x04 /* ticket has been used */
+
+static __inline__
+struct fuse_iov *
+fticket_resp(struct fuse_ticket *ftick)
+{
+ return (&ftick->tk_aw_fiov);
+}
+
+static __inline__
+int
+fticket_answered(struct fuse_ticket *ftick)
+{
+ DEBUGX(FUSE_DEBUG_IPC, "-> ftick=%p\n", ftick);
+ mtx_assert(&ftick->tk_aw_mtx, MA_OWNED);
+ return (ftick->tk_flag & FT_ANSW);
+}
+
+static __inline__
+void
+fticket_set_answered(struct fuse_ticket *ftick)
+{
+ DEBUGX(FUSE_DEBUG_IPC, "-> ftick=%p\n", ftick);
+ mtx_assert(&ftick->tk_aw_mtx, MA_OWNED);
+ ftick->tk_flag |= FT_ANSW;
+}
+
+static __inline__
+enum fuse_opcode
+fticket_opcode(struct fuse_ticket *ftick)
+{
+ DEBUGX(FUSE_DEBUG_IPC, "-> ftick=%p\n", ftick);
+ return (((struct fuse_in_header *)(ftick->tk_ms_fiov.base))->opcode);
+}
+
+int fticket_pull(struct fuse_ticket *ftick, struct uio *uio);
+
+enum mountpri { FM_NOMOUNTED, FM_PRIMARY, FM_SECONDARY };
+
+/*
+ * The data representing a FUSE session.
+ */
+struct fuse_data {
+ struct cdev *fdev;
+ struct mount *mp;
+ struct vnode *vroot;
+ struct ucred *daemoncred;
+ int dataflags;
+ int ref;
+
+ struct mtx ms_mtx;
+ STAILQ_HEAD(, fuse_ticket) ms_head;
+
+ struct mtx aw_mtx;
+ TAILQ_HEAD(, fuse_ticket) aw_head;
+
+ u_long ticketer;
+
+ struct sx rename_lock;
+
+ uint32_t fuse_libabi_major;
+ uint32_t fuse_libabi_minor;
+
+ uint32_t max_write;
+ uint32_t max_read;
+ uint32_t subtype;
+ char volname[MAXPATHLEN];
+
+ struct selinfo ks_rsel;
+
+ int daemon_timeout;
+ uint64_t notimpl;
+};
+
+#define FSESS_DEAD 0x0001 /* session is to be closed */
+#define FSESS_UNUSED0 0x0002 /* unused */
+#define FSESS_INITED 0x0004 /* session has been inited */
+#define FSESS_DAEMON_CAN_SPY 0x0010 /* let non-owners access this fs */
+ /* (and being observed by the daemon) */
+#define FSESS_PUSH_SYMLINKS_IN 0x0020 /* prefix absolute symlinks with mp */
+#define FSESS_DEFAULT_PERMISSIONS 0x0040 /* kernel does permission checking */
+#define FSESS_NO_ATTRCACHE 0x0080 /* no attribute caching */
+#define FSESS_NO_READAHEAD 0x0100 /* no readaheads */
+#define FSESS_NO_DATACACHE 0x0200 /* disable buffer cache */
+#define FSESS_NO_NAMECACHE 0x0400 /* disable name cache */
+#define FSESS_NO_MMAP 0x0800 /* disable mmap */
+#define FSESS_BROKENIO 0x1000 /* fix broken io */
+
+extern int fuse_data_cache_enable;
+extern int fuse_data_cache_invalidate;
+extern int fuse_mmap_enable;
+extern int fuse_sync_resize;
+extern int fuse_fix_broken_io;
+
+static __inline__
+struct fuse_data *
+fuse_get_mpdata(struct mount *mp)
+{
+ return mp->mnt_data;
+}
+
+static __inline int
+fsess_isimpl(struct mount *mp, int opcode)
+{
+ struct fuse_data *data = fuse_get_mpdata(mp);
+
+ return (data->notimpl & (1ULL << opcode)) == 0;
+
+}
+static __inline void
+fsess_set_notimpl(struct mount *mp, int opcode)
+{
+ struct fuse_data *data = fuse_get_mpdata(mp);
+
+ data->notimpl |= (1ULL << opcode);
+}
+
+static __inline int
+fsess_opt_datacache(struct mount *mp)
+{
+ struct fuse_data *data = fuse_get_mpdata(mp);
+
+ return (fuse_data_cache_enable ||
+ (data->dataflags & FSESS_NO_DATACACHE) == 0);
+}
+
+static __inline int
+fsess_opt_mmap(struct mount *mp)
+{
+ struct fuse_data *data = fuse_get_mpdata(mp);
+
+ if (!(fuse_mmap_enable && fuse_data_cache_enable))
+ return 0;
+ return ((data->dataflags & (FSESS_NO_DATACACHE | FSESS_NO_MMAP)) == 0);
+}
+
+static __inline int
+fsess_opt_brokenio(struct mount *mp)
+{
+ struct fuse_data *data = fuse_get_mpdata(mp);
+
+ return (fuse_fix_broken_io || (data->dataflags & FSESS_BROKENIO));
+}
+
+static __inline__
+void
+fuse_ms_push(struct fuse_ticket *ftick)
+{
+ DEBUGX(FUSE_DEBUG_IPC, "ftick=%p refcount=%d\n",
+ ftick, ftick->tk_refcount + 1);
+ mtx_assert(&ftick->tk_data->ms_mtx, MA_OWNED);
+ refcount_acquire(&ftick->tk_refcount);
+ STAILQ_INSERT_TAIL(&ftick->tk_data->ms_head, ftick, tk_ms_link);
+}
+
+static __inline__
+struct fuse_ticket *
+fuse_ms_pop(struct fuse_data *data)
+{
+ struct fuse_ticket *ftick = NULL;
+
+ mtx_assert(&data->ms_mtx, MA_OWNED);
+
+ if ((ftick = STAILQ_FIRST(&data->ms_head))) {
+ STAILQ_REMOVE_HEAD(&data->ms_head, tk_ms_link);
+#ifdef INVARIANTS
+ ftick->tk_ms_link.stqe_next = NULL;
+#endif
+ }
+ DEBUGX(FUSE_DEBUG_IPC, "ftick=%p refcount=%d\n",
+ ftick, ftick ? ftick->tk_refcount : -1);
+
+ return ftick;
+}
+
+static __inline__
+void
+fuse_aw_push(struct fuse_ticket *ftick)
+{
+ DEBUGX(FUSE_DEBUG_IPC, "ftick=%p refcount=%d\n",
+ ftick, ftick->tk_refcount + 1);
+ mtx_assert(&ftick->tk_data->aw_mtx, MA_OWNED);
+ refcount_acquire(&ftick->tk_refcount);
+ TAILQ_INSERT_TAIL(&ftick->tk_data->aw_head, ftick, tk_aw_link);
+}
+
+static __inline__
+void
+fuse_aw_remove(struct fuse_ticket *ftick)
+{
+ DEBUGX(FUSE_DEBUG_IPC, "ftick=%p refcount=%d\n",
+ ftick, ftick->tk_refcount);
+ mtx_assert(&ftick->tk_data->aw_mtx, MA_OWNED);
+ TAILQ_REMOVE(&ftick->tk_data->aw_head, ftick, tk_aw_link);
+#ifdef INVARIANTS
+ ftick->tk_aw_link.tqe_next = NULL;
+ ftick->tk_aw_link.tqe_prev = NULL;
+#endif
+}
+
+static __inline__
+struct fuse_ticket *
+fuse_aw_pop(struct fuse_data *data)
+{
+ struct fuse_ticket *ftick = NULL;
+
+ mtx_assert(&data->aw_mtx, MA_OWNED);
+
+ if ((ftick = TAILQ_FIRST(&data->aw_head))) {
+ fuse_aw_remove(ftick);
+ }
+ DEBUGX(FUSE_DEBUG_IPC, "ftick=%p refcount=%d\n",
+ ftick, ftick ? ftick->tk_refcount : -1);
+
+ return ftick;
+}
+
+struct fuse_ticket *fuse_ticket_fetch(struct fuse_data *data);
+int fuse_ticket_drop(struct fuse_ticket *ftick);
+void fuse_insert_callback(struct fuse_ticket *ftick, fuse_handler_t *handler);
+void fuse_insert_message(struct fuse_ticket *ftick);
+
+static __inline__
+int
+fuse_libabi_geq(struct fuse_data *data, uint32_t abi_maj, uint32_t abi_min)
+{
+ return (data->fuse_libabi_major > abi_maj ||
+ (data->fuse_libabi_major == abi_maj && data->fuse_libabi_minor >= abi_min));
+}
+
+struct fuse_data *fdata_alloc(struct cdev *dev, struct ucred *cred);
+void fdata_trydestroy(struct fuse_data *data);
+void fdata_set_dead(struct fuse_data *data);
+
+static __inline__
+int
+fdata_get_dead(struct fuse_data *data)
+{
+ return (data->dataflags & FSESS_DEAD);
+}
+
+struct fuse_dispatcher {
+
+ struct fuse_ticket *tick;
+ struct fuse_in_header *finh;
+
+ void *indata;
+ size_t iosize;
+ uint64_t nodeid;
+ int answ_stat;
+ void *answ;
+};
+
+static __inline__
+void
+fdisp_init(struct fuse_dispatcher *fdisp, size_t iosize)
+{
+ DEBUGX(FUSE_DEBUG_IPC, "-> fdisp=%p, iosize=%zx\n", fdisp, iosize);
+ fdisp->iosize = iosize;
+ fdisp->tick = NULL;
+}
+
+static __inline__
+void
+fdisp_destroy(struct fuse_dispatcher *fdisp)
+{
+ DEBUGX(FUSE_DEBUG_IPC, "-> fdisp=%p, ftick=%p\n", fdisp, fdisp->tick);
+ fuse_ticket_drop(fdisp->tick);
+#ifdef INVARIANTS
+ fdisp->tick = NULL;
+#endif
+}
+
+void fdisp_make(struct fuse_dispatcher *fdip, enum fuse_opcode op,
+ struct mount *mp, uint64_t nid, struct thread *td,
+ struct ucred *cred);
+
+void fdisp_make_pid(struct fuse_dispatcher *fdip, enum fuse_opcode op,
+ struct mount *mp, uint64_t nid, pid_t pid,
+ struct ucred *cred);
+
+void fdisp_make_vp(struct fuse_dispatcher *fdip, enum fuse_opcode op,
+ struct vnode *vp, struct thread *td, struct ucred *cred);
+
+int fdisp_wait_answ(struct fuse_dispatcher *fdip);
+
+static __inline__
+int
+fdisp_simple_putget_vp(struct fuse_dispatcher *fdip, enum fuse_opcode op,
+ struct vnode *vp, struct thread *td, struct ucred *cred)
+{
+ DEBUGX(FUSE_DEBUG_IPC, "-> fdip=%p, opcode=%d, vp=%p\n", fdip, op, vp);
+ fdisp_make_vp(fdip, op, vp, td, cred);
+ return fdisp_wait_answ(fdip);
+}
+
+#endif /* _FUSE_IPC_H_ */
diff --git a/sys/fs/fuse/fuse_kernel.h b/sys/fs/fuse/fuse_kernel.h
new file mode 100644
index 0000000..07cd4a9
--- /dev/null
+++ b/sys/fs/fuse/fuse_kernel.h
@@ -0,0 +1,373 @@
+/*-
+ * This file defines the kernel interface of FUSE
+ * 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.
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef linux
+#include <sys/types.h>
+#define __u64 uint64_t
+#define __u32 uint32_t
+#define __s32 int32_t
+#else
+#include <asm/types.h>
+#include <linux/major.h>
+#endif
+
+/** Version number of this interface */
+#define FUSE_KERNEL_VERSION 7
+
+/** Minor version number of this interface */
+#define FUSE_KERNEL_MINOR_VERSION 8
+
+/** The node ID of the root inode */
+#define FUSE_ROOT_ID 1
+
+/** The major number of the fuse character device */
+#define FUSE_MAJOR MISC_MAJOR
+
+/** The minor number of the fuse character device */
+#define FUSE_MINOR 229
+
+/* 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;
+};
+
+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)
+
+/**
+ * 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
+ */
+#define FOPEN_DIRECT_IO (1 << 0)
+#define FOPEN_KEEP_CACHE (1 << 1)
+
+/**
+ * INIT request/reply flags
+ */
+#define FUSE_ASYNC_READ (1 << 0)
+#define FUSE_POSIX_LOCKS (1 << 1)
+
+/**
+ * Release flags
+ */
+#define FUSE_RELEASE_FLUSH (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,
+};
+
+/* The read buffer is required to be at least 8k, but may be much larger */
+#define FUSE_MIN_READ_BUFFER 8192
+
+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_attr_out {
+ __u64 attr_valid; /* Cache timeout for the attributes */
+ __u32 attr_valid_nsec;
+ __u32 dummy;
+ struct fuse_attr attr;
+};
+
+struct fuse_mkdir_in {
+ __u32 mode;
+ __u32 padding;
+};
+
+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 unused1;
+ __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 mode;
+};
+
+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 padding;
+};
+
+struct fuse_write_in {
+ __u64 fh;
+ __u64 offset;
+ __u32 size;
+ __u32 write_flags;
+};
+
+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;
+};
+
+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;
+ __u32 unused;
+ __u32 max_write;
+};
+
+struct fuse_interrupt_in {
+ __u64 unique;
+};
+
+struct fuse_bmap_in {
+ __u64 block;
+ __u32 blocksize;
+ __u32 padding;
+};
+
+struct fuse_bmap_out {
+ __u64 block;
+};
+
+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[0];
+};
+
+#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)
diff --git a/sys/fs/fuse/fuse_main.c b/sys/fs/fuse/fuse_main.c
new file mode 100644
index 0000000..bf73a45f
--- /dev/null
+++ b/sys/fs/fuse/fuse_main.c
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2007-2009 Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
+ * OWNER 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.
+ *
+ * Copyright (C) 2005 Csaba Henk.
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/module.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/conf.h>
+#include <sys/mutex.h>
+#include <sys/proc.h>
+#include <sys/mount.h>
+#include <sys/vnode.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/buf.h>
+#include <sys/sysctl.h>
+
+#include "fuse.h"
+
+static void fuse_bringdown(eventhandler_tag eh_tag);
+static int fuse_loader(struct module *m, int what, void *arg);
+
+struct mtx fuse_mtx;
+
+extern struct vfsops fuse_vfsops;
+extern struct cdevsw fuse_cdevsw;
+extern struct vop_vector fuse_vnops;
+extern int fuse_pbuf_freecnt;
+
+static struct vfsconf fuse_vfsconf = {
+ .vfc_version = VFS_VERSION,
+ .vfc_name = "fusefs",
+ .vfc_vfsops = &fuse_vfsops,
+ .vfc_typenum = -1,
+ .vfc_flags = VFCF_SYNTHETIC
+};
+
+SYSCTL_INT(_vfs_fuse, OID_AUTO, kernelabi_major, CTLFLAG_RD,
+ 0, FUSE_KERNEL_VERSION, "FUSE kernel abi major version");
+SYSCTL_INT(_vfs_fuse, OID_AUTO, kernelabi_minor, CTLFLAG_RD,
+ 0, FUSE_KERNEL_MINOR_VERSION, "FUSE kernel abi minor version");
+
+/******************************
+ *
+ * >>> Module management stuff
+ *
+ ******************************/
+
+static void
+fuse_bringdown(eventhandler_tag eh_tag)
+{
+
+ fuse_ipc_destroy();
+ fuse_device_destroy();
+ mtx_destroy(&fuse_mtx);
+}
+
+static int
+fuse_loader(struct module *m, int what, void *arg)
+{
+ static eventhandler_tag eh_tag = NULL;
+ int err = 0;
+
+ switch (what) {
+ case MOD_LOAD: /* kldload */
+ fuse_pbuf_freecnt = nswbuf / 2 + 1;
+ mtx_init(&fuse_mtx, "fuse_mtx", NULL, MTX_DEF);
+ err = fuse_device_init();
+ if (err) {
+ mtx_destroy(&fuse_mtx);
+ return (err);
+ }
+ fuse_ipc_init();
+
+ /* vfs_modevent ignores its first arg */
+ if ((err = vfs_modevent(NULL, what, &fuse_vfsconf)))
+ fuse_bringdown(eh_tag);
+ else
+ printf("fuse-freebsd: version %s, FUSE ABI %d.%d\n",
+ FUSE_FREEBSD_VERSION,
+ FUSE_KERNEL_VERSION, FUSE_KERNEL_MINOR_VERSION);
+
+ break;
+ case MOD_UNLOAD:
+ if ((err = vfs_modevent(NULL, what, &fuse_vfsconf)))
+ return (err);
+ fuse_bringdown(eh_tag);
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ return (err);
+}
+
+/* Registering the module */
+
+static moduledata_t fuse_moddata = {
+ "fuse",
+ fuse_loader,
+ &fuse_vfsconf
+};
+
+DECLARE_MODULE(fuse, fuse_moddata, SI_SUB_VFS, SI_ORDER_MIDDLE);
+MODULE_VERSION(fuse, 1);
diff --git a/sys/fs/fuse/fuse_node.c b/sys/fs/fuse/fuse_node.c
new file mode 100644
index 0000000..3838ab7
--- /dev/null
+++ b/sys/fs/fuse/fuse_node.c
@@ -0,0 +1,384 @@
+/*
+ * Copyright (c) 2007-2009 Google Inc. and Amit Singh
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
+ * OWNER 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.
+ *
+ * Copyright (C) 2005 Csaba Henk.
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/module.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/conf.h>
+#include <sys/uio.h>
+#include <sys/malloc.h>
+#include <sys/queue.h>
+#include <sys/lock.h>
+#include <sys/sx.h>
+#include <sys/mutex.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/namei.h>
+#include <sys/mount.h>
+#include <sys/sysctl.h>
+#include <sys/fcntl.h>
+#include <sys/fnv_hash.h>
+#include <sys/priv.h>
+#include <security/mac/mac_framework.h>
+#include <vm/vm.h>
+#include <vm/vm_extern.h>
+
+#include "fuse.h"
+#include "fuse_node.h"
+#include "fuse_internal.h"
+#include "fuse_io.h"
+#include "fuse_ipc.h"
+
+#define FUSE_DEBUG_MODULE VNOPS
+#include "fuse_debug.h"
+
+MALLOC_DEFINE(M_FUSEVN, "fuse_vnode", "fuse vnode private data");
+
+static int fuse_node_count = 0;
+
+SYSCTL_INT(_vfs_fuse, OID_AUTO, node_count, CTLFLAG_RD,
+ &fuse_node_count, 0, "");
+
+int fuse_data_cache_enable = 1;
+
+SYSCTL_INT(_vfs_fuse, OID_AUTO, data_cache_enable, CTLFLAG_RW,
+ &fuse_data_cache_enable, 0, "");
+
+int fuse_data_cache_invalidate = 0;
+
+SYSCTL_INT(_vfs_fuse, OID_AUTO, data_cache_invalidate, CTLFLAG_RW,
+ &fuse_data_cache_invalidate, 0, "");
+
+int fuse_mmap_enable = 1;
+
+SYSCTL_INT(_vfs_fuse, OID_AUTO, mmap_enable, CTLFLAG_RW,
+ &fuse_mmap_enable, 0, "");
+
+int fuse_refresh_size = 0;
+
+SYSCTL_INT(_vfs_fuse, OID_AUTO, refresh_size, CTLFLAG_RW,
+ &fuse_refresh_size, 0, "");
+
+int fuse_sync_resize = 1;
+
+SYSCTL_INT(_vfs_fuse, OID_AUTO, sync_resize, CTLFLAG_RW,
+ &fuse_sync_resize, 0, "");
+
+int fuse_fix_broken_io = 0;
+
+SYSCTL_INT(_vfs_fuse, OID_AUTO, fix_broken_io, CTLFLAG_RW,
+ &fuse_fix_broken_io, 0, "");
+
+static void
+fuse_vnode_init(struct vnode *vp, struct fuse_vnode_data *fvdat,
+ uint64_t nodeid, enum vtype vtyp)
+{
+ int i;
+
+ fvdat->nid = nodeid;
+ if (nodeid == FUSE_ROOT_ID) {
+ vp->v_vflag |= VV_ROOT;
+ }
+ vp->v_type = vtyp;
+ vp->v_data = fvdat;
+
+ for (i = 0; i < FUFH_MAXTYPE; i++)
+ fvdat->fufh[i].fh_type = FUFH_INVALID;
+
+ atomic_add_acq_int(&fuse_node_count, 1);
+}
+
+void
+fuse_vnode_destroy(struct vnode *vp)
+{
+ struct fuse_vnode_data *fvdat = vp->v_data;
+
+ vp->v_data = NULL;
+ free(fvdat, M_FUSEVN);
+
+ atomic_subtract_acq_int(&fuse_node_count, 1);
+}
+
+static int
+fuse_vnode_cmp(struct vnode *vp, void *nidp)
+{
+ return (VTOI(vp) != *((uint64_t *)nidp));
+}
+
+static uint32_t __inline
+fuse_vnode_hash(uint64_t id)
+{
+ return (fnv_32_buf(&id, sizeof(id), FNV1_32_INIT));
+}
+
+static int
+fuse_vnode_alloc(struct mount *mp,
+ struct thread *td,
+ uint64_t nodeid,
+ enum vtype vtyp,
+ struct vnode **vpp)
+{
+ struct fuse_vnode_data *fvdat;
+ struct vnode *vp2;
+ int err = 0;
+
+ DEBUG("been asked for vno #%ju\n", (uintmax_t)nodeid);
+
+ if (vtyp == VNON) {
+ return EINVAL;
+ }
+ *vpp = NULL;
+ err = vfs_hash_get(mp, fuse_vnode_hash(nodeid), LK_EXCLUSIVE, td, vpp,
+ fuse_vnode_cmp, &nodeid);
+ if (err)
+ return (err);
+
+ if (*vpp) {
+ MPASS((*vpp)->v_type == vtyp && (*vpp)->v_data != NULL);
+ DEBUG("vnode taken from hash\n");
+ return (0);
+ }
+ fvdat = malloc(sizeof(*fvdat), M_FUSEVN, M_WAITOK | M_ZERO);
+ err = getnewvnode("fuse", mp, &fuse_vnops, vpp);
+ if (err) {
+ free(fvdat, M_FUSEVN);
+ return (err);
+ }
+ lockmgr((*vpp)->v_vnlock, LK_EXCLUSIVE, NULL);
+ fuse_vnode_init(*vpp, fvdat, nodeid, vtyp);
+ err = insmntque(*vpp, mp);
+ ASSERT_VOP_ELOCKED(*vpp, "fuse_vnode_alloc");
+ if (err) {
+ free(fvdat, M_FUSEVN);
+ *vpp = NULL;
+ return (err);
+ }
+ err = vfs_hash_insert(*vpp, fuse_vnode_hash(nodeid), LK_EXCLUSIVE,
+ td, &vp2, fuse_vnode_cmp, &nodeid);
+ if (err)
+ return (err);
+ if (vp2 != NULL) {
+ *vpp = vp2;
+ return (0);
+ }
+
+ ASSERT_VOP_ELOCKED(*vpp, "fuse_vnode_alloc");
+
+ return (0);
+}
+
+int
+fuse_vnode_get(struct mount *mp,
+ uint64_t nodeid,
+ struct vnode *dvp,
+ struct vnode **vpp,
+ struct componentname *cnp,
+ enum vtype vtyp)
+{
+ struct thread *td = (cnp != NULL ? cnp->cn_thread : curthread);
+ int err = 0;
+
+ debug_printf("dvp=%p\n", dvp);
+
+ err = fuse_vnode_alloc(mp, td, nodeid, vtyp, vpp);
+ if (err) {
+ return err;
+ }
+ if (dvp != NULL) {
+ MPASS((cnp->cn_flags & ISDOTDOT) == 0);
+ MPASS(!(cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.'));
+ fuse_vnode_setparent(*vpp, dvp);
+ }
+ if (dvp != NULL && cnp != NULL && (cnp->cn_flags & MAKEENTRY) != 0) {
+ ASSERT_VOP_LOCKED(*vpp, "fuse_vnode_get");
+ ASSERT_VOP_LOCKED(dvp, "fuse_vnode_get");
+ cache_enter(dvp, *vpp, cnp);
+ }
+
+ /*
+ * In userland, libfuse uses cached lookups for dot and dotdot entries,
+ * thus it does not really bump the nlookup counter for forget.
+ * Follow the same semantic and avoid tu bump it in order to keep
+ * nlookup counters consistent.
+ */
+ if (cnp == NULL || ((cnp->cn_flags & ISDOTDOT) == 0 &&
+ (cnp->cn_namelen != 1 || cnp->cn_nameptr[0] != '.')))
+ VTOFUD(*vpp)->nlookup++;
+
+ return 0;
+}
+
+void
+fuse_vnode_open(struct vnode *vp, int32_t fuse_open_flags, struct thread *td)
+{
+ /*
+ * Funcation is called for every vnode open.
+ * Merge fuse_open_flags it may be 0
+ *
+ * XXXIP: Handle FOPEN_DIRECT_IO and FOPEN_KEEP_CACHE
+ */
+
+ if (vnode_vtype(vp) == VREG) {
+ /* XXXIP prevent getattr, by using cached node size */
+ vnode_create_vobject(vp, 0, td);
+ }
+}
+
+int
+fuse_isvalid_attr(struct vnode *vp)
+{
+ struct fuse_vnode_data *fvdat = VTOFUD(vp);
+ struct timespec uptsp;
+
+ nanouptime(&uptsp);
+ return fuse_timespec_cmp(&uptsp, &fvdat->cached_attrs_valid, <=);
+}
+
+int
+fuse_vnode_savesize(struct vnode *vp, struct ucred *cred)
+{
+ struct fuse_vnode_data *fvdat = VTOFUD(vp);
+ struct thread *td = curthread;
+ struct fuse_filehandle *fufh = NULL;
+ struct fuse_dispatcher fdi;
+ struct fuse_setattr_in *fsai;
+ int err = 0;
+
+ DEBUG("inode=%ju size=%ju\n", (uintmax_t)VTOI(vp),
+ (uintmax_t)fvdat->filesize);
+ ASSERT_VOP_ELOCKED(vp, "fuse_io_extend");
+
+ if (fuse_isdeadfs(vp)) {
+ return EBADF;
+ }
+ if (vnode_vtype(vp) == VDIR) {
+ return EISDIR;
+ }
+ if (vfs_isrdonly(vnode_mount(vp))) {
+ return EROFS;
+ }
+ if (cred == NULL) {
+ cred = td->td_ucred;
+ }
+ fdisp_init(&fdi, sizeof(*fsai));
+ fdisp_make_vp(&fdi, FUSE_SETATTR, vp, td, cred);
+ fsai = fdi.indata;
+ fsai->valid = 0;
+
+ /* Truncate to a new value. */
+ fsai->size = fvdat->filesize;
+ fsai->valid |= FATTR_SIZE;
+
+ fuse_filehandle_getrw(vp, FUFH_WRONLY, &fufh);
+ if (fufh) {
+ fsai->fh = fufh->fh_id;
+ fsai->valid |= FATTR_FH;
+ }
+ err = fdisp_wait_answ(&fdi);
+ fdisp_destroy(&fdi);
+ if (err == 0)
+ fvdat->flag &= ~FN_SIZECHANGE;
+
+ fuse_invalidate_attr(vp);
+
+ return err;
+}
+
+void
+fuse_vnode_refreshsize(struct vnode *vp, struct ucred *cred)
+{
+
+ struct fuse_vnode_data *fvdat = VTOFUD(vp);
+ struct vattr va;
+
+ if ((fvdat->flag & FN_SIZECHANGE) != 0 ||
+ (fuse_refresh_size == 0 && fvdat->filesize != 0) ||
+ fuse_isvalid_attr(vp))
+ return;
+
+ VOP_GETATTR(vp, &va, cred);
+ DEBUG("refreshed file size: %jd\n", (intmax_t)VTOFUD(vp)->filesize);
+}
+
+int
+fuse_vnode_setsize(struct vnode *vp, struct ucred *cred, off_t newsize)
+{
+ struct fuse_vnode_data *fvdat = VTOFUD(vp);
+ off_t oldsize;
+ int err = 0;
+
+ DEBUG("inode=%ju oldsize=%ju newsize=%ju\n",
+ (uintmax_t)VTOI(vp), (uintmax_t)fvdat->filesize,
+ (uintmax_t)newsize);
+ ASSERT_VOP_ELOCKED(vp, "fuse_vnode_setsize");
+
+ oldsize = fvdat->filesize;
+ fvdat->filesize = newsize;
+ fvdat->flag |= FN_SIZECHANGE;
+
+ if (newsize < oldsize) {
+ err = vtruncbuf(vp, cred, newsize, fuse_iosize(vp));
+ }
+ vnode_pager_setsize(vp, newsize);
+ fuse_invalidate_attr(vp);
+
+ return err;
+}
diff --git a/sys/fs/fuse/fuse_node.h b/sys/fs/fuse/fuse_node.h
new file mode 100644
index 0000000..45b15a4
--- /dev/null
+++ b/sys/fs/fuse/fuse_node.h
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2007-2009 Google Inc. and Amit Singh
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
+ * OWNER 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.
+ *
+ * Copyright (C) 2005 Csaba Henk.
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _FUSE_NODE_H_
+#define _FUSE_NODE_H_
+
+#include <sys/types.h>
+#include <sys/mutex.h>
+
+#include "fuse_file.h"
+
+#define FN_REVOKED 0x00000020
+#define FN_FLUSHINPROG 0x00000040
+#define FN_FLUSHWANT 0x00000080
+#define FN_SIZECHANGE 0x00000100
+
+struct fuse_vnode_data {
+ /** self **/
+ uint64_t nid;
+
+ /** parent **/
+ /* XXXIP very likely to be stale, it's not updated in rename() */
+ uint64_t parent_nid;
+
+ /** I/O **/
+ struct fuse_filehandle fufh[FUFH_MAXTYPE];
+
+ /** flags **/
+ uint32_t flag;
+
+ /** meta **/
+ struct timespec cached_attrs_valid;
+ struct vattr cached_attrs;
+ off_t filesize;
+ uint64_t nlookup;
+ enum vtype vtype;
+};
+
+#define VTOFUD(vp) \
+ ((struct fuse_vnode_data *)((vp)->v_data))
+#define VTOI(vp) (VTOFUD(vp)->nid)
+#define VTOVA(vp) (&(VTOFUD(vp)->cached_attrs))
+#define VTOILLU(vp) ((uint64_t)(VTOFUD(vp) ? VTOI(vp) : 0))
+
+#define FUSE_NULL_ID 0
+
+extern struct vop_vector fuse_vnops;
+
+static __inline__
+void
+fuse_invalidate_attr(struct vnode *vp)
+{
+ if (VTOFUD(vp)) {
+ bzero(&VTOFUD(vp)->cached_attrs_valid, sizeof(struct timespec));
+ }
+}
+
+static __inline void
+fuse_vnode_setparent(struct vnode *vp, struct vnode *dvp)
+{
+ if (dvp != NULL && vp->v_type == VDIR) {
+ MPASS(dvp->v_type == VDIR);
+ VTOFUD(vp)->parent_nid = VTOI(dvp);
+ }
+}
+
+int fuse_isvalid_attr(struct vnode *vp);
+
+void fuse_vnode_destroy(struct vnode *vp);
+
+int fuse_vnode_get(struct mount *mp,
+ uint64_t nodeid,
+ struct vnode *dvp,
+ struct vnode **vpp,
+ struct componentname *cnp,
+ enum vtype vtyp);
+
+void fuse_vnode_open(struct vnode *vp,
+ int32_t fuse_open_flags,
+ struct thread *td);
+
+void fuse_vnode_refreshsize(struct vnode *vp, struct ucred *cred);
+
+int fuse_vnode_savesize(struct vnode *vp, struct ucred *cred);
+
+int fuse_vnode_setsize(struct vnode *vp, struct ucred *cred, off_t newsize);
+
+#endif /* _FUSE_NODE_H_ */
diff --git a/sys/fs/fuse/fuse_param.h b/sys/fs/fuse/fuse_param.h
new file mode 100644
index 0000000..493c385
--- /dev/null
+++ b/sys/fs/fuse/fuse_param.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2007-2009 Google Inc. and Amit Singh
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
+ * OWNER 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _FUSE_PARAM_H_
+#define _FUSE_PARAM_H_
+
+/*
+ * This is the prefix ("fuse" by default) of the name of a FUSE device node
+ * in devfs. The suffix is the device number. "/dev/fuse0" is the first FUSE
+ * device by default. If you change the prefix from the default to something
+ * else, the user-space FUSE library will need to know about it too.
+ */
+#define FUSE_DEVICE_BASENAME "fuse"
+
+/*
+ * This is the number of /dev/fuse<n> nodes we will create. <n> goes from
+ * 0 to (FUSE_NDEVICES - 1).
+ */
+#define FUSE_NDEVICES 16
+
+/*
+ * This is the default block size of the virtual storage devices that are
+ * implicitly implemented by the FUSE kernel extension. This can be changed
+ * on a per-mount basis (there's one such virtual device for each mount).
+ */
+#define FUSE_DEFAULT_BLOCKSIZE 4096
+
+/*
+ * This is default I/O size used while accessing the virtual storage devices.
+ * This can be changed on a per-mount basis.
+ */
+#define FUSE_DEFAULT_IOSIZE 4096
+
+#ifdef KERNEL
+
+/*
+ * This is the soft upper limit on the number of "request tickets" FUSE's
+ * user-kernel IPC layer can have for a given mount. This can be modified
+ * through the fuse.* sysctl interface.
+ */
+#define FUSE_DEFAULT_MAX_FREE_TICKETS 1024
+
+#define FUSE_DEFAULT_IOV_PERMANENT_BUFSIZE (1L << 19)
+#define FUSE_DEFAULT_IOV_CREDIT 16
+
+#endif
+
+#define FUSE_LINK_MAX LINK_MAX
+
+#endif /* _FUSE_PARAM_H_ */
diff --git a/sys/fs/fuse/fuse_vfsops.c b/sys/fs/fuse/fuse_vfsops.c
new file mode 100644
index 0000000..b8244ec
--- /dev/null
+++ b/sys/fs/fuse/fuse_vfsops.c
@@ -0,0 +1,533 @@
+/*
+ * Copyright (c) 2007-2009 Google Inc. and Amit Singh
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
+ * OWNER 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.
+ *
+ * Copyright (C) 2005 Csaba Henk.
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/module.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/capability.h>
+#include <sys/conf.h>
+#include <sys/filedesc.h>
+#include <sys/uio.h>
+#include <sys/malloc.h>
+#include <sys/queue.h>
+#include <sys/lock.h>
+#include <sys/sx.h>
+#include <sys/mutex.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/namei.h>
+#include <sys/mount.h>
+#include <sys/sysctl.h>
+#include <sys/fcntl.h>
+
+#include "fuse.h"
+#include "fuse_param.h"
+#include "fuse_node.h"
+#include "fuse_ipc.h"
+#include "fuse_internal.h"
+
+#include <sys/priv.h>
+#include <security/mac/mac_framework.h>
+
+#define FUSE_DEBUG_MODULE VFSOPS
+#include "fuse_debug.h"
+
+/* This will do for privilege types for now */
+#ifndef PRIV_VFS_FUSE_ALLOWOTHER
+#define PRIV_VFS_FUSE_ALLOWOTHER PRIV_VFS_MOUNT_NONUSER
+#endif
+#ifndef PRIV_VFS_FUSE_MOUNT_NONUSER
+#define PRIV_VFS_FUSE_MOUNT_NONUSER PRIV_VFS_MOUNT_NONUSER
+#endif
+#ifndef PRIV_VFS_FUSE_SYNC_UNMOUNT
+#define PRIV_VFS_FUSE_SYNC_UNMOUNT PRIV_VFS_MOUNT_NONUSER
+#endif
+
+static vfs_mount_t fuse_vfsop_mount;
+static vfs_unmount_t fuse_vfsop_unmount;
+static vfs_root_t fuse_vfsop_root;
+static vfs_statfs_t fuse_vfsop_statfs;
+
+struct vfsops fuse_vfsops = {
+ .vfs_mount = fuse_vfsop_mount,
+ .vfs_unmount = fuse_vfsop_unmount,
+ .vfs_root = fuse_vfsop_root,
+ .vfs_statfs = fuse_vfsop_statfs,
+};
+
+SYSCTL_INT(_vfs_fuse, OID_AUTO, init_backgrounded, CTLFLAG_RD,
+ 0, 1, "indicate async handshake");
+static int fuse_enforce_dev_perms = 0;
+
+SYSCTL_LONG(_vfs_fuse, OID_AUTO, enforce_dev_perms, CTLFLAG_RW,
+ &fuse_enforce_dev_perms, 0,
+ "enforce fuse device permissions for secondary mounts");
+static unsigned sync_unmount = 1;
+
+SYSCTL_UINT(_vfs_fuse, OID_AUTO, sync_unmount, CTLFLAG_RW,
+ &sync_unmount, 0, "specify when to use synchronous unmount");
+
+MALLOC_DEFINE(M_FUSEVFS, "fuse_filesystem", "buffer for fuse vfs layer");
+
+static int
+fuse_getdevice(const char *fspec, struct thread *td, struct cdev **fdevp)
+{
+ struct nameidata nd, *ndp = &nd;
+ struct vnode *devvp;
+ struct cdev *fdev;
+ int err;
+
+ /*
+ * Not an update, or updating the name: look up the name
+ * and verify that it refers to a sensible disk device.
+ */
+
+ NDINIT(ndp, LOOKUP, FOLLOW, UIO_SYSSPACE, fspec, td);
+ if ((err = namei(ndp)) != 0)
+ return err;
+ NDFREE(ndp, NDF_ONLY_PNBUF);
+ devvp = ndp->ni_vp;
+
+ if (devvp->v_type != VCHR) {
+ vrele(devvp);
+ return ENXIO;
+ }
+ fdev = devvp->v_rdev;
+ dev_ref(fdev);
+
+ if (fuse_enforce_dev_perms) {
+ /*
+ * Check if mounter can open the fuse device.
+ *
+ * This has significance only if we are doing a secondary mount
+ * which doesn't involve actually opening fuse devices, but we
+ * still want to enforce the permissions of the device (in
+ * order to keep control over the circle of fuse users).
+ *
+ * (In case of primary mounts, we are either the superuser so
+ * we can do anything anyway, or we can mount only if the
+ * device is already opened by us, ie. we are permitted to open
+ * the device.)
+ */
+#if 0
+#ifdef MAC
+ err = mac_check_vnode_open(td->td_ucred, devvp, VREAD | VWRITE);
+ if (!err)
+#endif
+#endif /* 0 */
+ err = VOP_ACCESS(devvp, VREAD | VWRITE, td->td_ucred, td);
+ if (err) {
+ vrele(devvp);
+ dev_rel(fdev);
+ return err;
+ }
+ }
+ /*
+ * according to coda code, no extra lock is needed --
+ * although in sys/vnode.h this field is marked "v"
+ */
+ vrele(devvp);
+
+ if (!fdev->si_devsw ||
+ strcmp("fuse", fdev->si_devsw->d_name)) {
+ dev_rel(fdev);
+ return ENXIO;
+ }
+ *fdevp = fdev;
+
+ return 0;
+}
+
+#define FUSE_FLAGOPT(fnam, fval) do { \
+ vfs_flagopt(opts, #fnam, &mntopts, fval); \
+ vfs_flagopt(opts, "__" #fnam, &__mntopts, fval); \
+} while (0)
+
+static int
+fuse_vfsop_mount(struct mount *mp)
+{
+ int err;
+
+ uint64_t mntopts, __mntopts;
+ int max_read_set;
+ uint32_t max_read;
+ int daemon_timeout;
+ int fd;
+
+ size_t len;
+
+ struct cdev *fdev;
+ struct fuse_data *data;
+ struct thread *td;
+ struct file *fp, *fptmp;
+ char *fspec, *subtype;
+ struct vfsoptlist *opts;
+
+ subtype = NULL;
+ max_read_set = 0;
+ max_read = ~0;
+ err = 0;
+ mntopts = 0;
+ __mntopts = 0;
+ td = curthread;
+
+ fuse_trace_printf_vfsop();
+
+ if (mp->mnt_flag & MNT_UPDATE)
+ return EOPNOTSUPP;
+
+ mp->mnt_flag |= MNT_SYNCHRONOUS;
+ mp->mnt_data = NULL;
+ /* Get the new options passed to mount */
+ opts = mp->mnt_optnew;
+
+ if (!opts)
+ return EINVAL;
+
+ /* `fspath' contains the mount point (eg. /mnt/fuse/sshfs); REQUIRED */
+ if (!vfs_getopts(opts, "fspath", &err))
+ return err;
+
+ /* `from' contains the device name (eg. /dev/fuse0); REQUIRED */
+ fspec = vfs_getopts(opts, "from", &err);
+ if (!fspec)
+ return err;
+
+ /* `fd' contains the filedescriptor for this session; REQUIRED */
+ if (vfs_scanopt(opts, "fd", "%d", &fd) != 1)
+ return EINVAL;
+
+ err = fuse_getdevice(fspec, td, &fdev);
+ if (err != 0)
+ return err;
+
+ /*
+ * With the help of underscored options the mount program
+ * can inform us from the flags it sets by default
+ */
+ FUSE_FLAGOPT(allow_other, FSESS_DAEMON_CAN_SPY);
+ FUSE_FLAGOPT(push_symlinks_in, FSESS_PUSH_SYMLINKS_IN);
+ FUSE_FLAGOPT(default_permissions, FSESS_DEFAULT_PERMISSIONS);
+ FUSE_FLAGOPT(no_attrcache, FSESS_NO_ATTRCACHE);
+ FUSE_FLAGOPT(no_readahed, FSESS_NO_READAHEAD);
+ FUSE_FLAGOPT(no_datacache, FSESS_NO_DATACACHE);
+ FUSE_FLAGOPT(no_namecache, FSESS_NO_NAMECACHE);
+ FUSE_FLAGOPT(no_mmap, FSESS_NO_MMAP);
+ FUSE_FLAGOPT(brokenio, FSESS_BROKENIO);
+
+ if (vfs_scanopt(opts, "max_read=", "%u", &max_read) == 1)
+ max_read_set = 1;
+ if (vfs_scanopt(opts, "timeout=", "%u", &daemon_timeout) == 1) {
+ if (daemon_timeout < FUSE_MIN_DAEMON_TIMEOUT)
+ daemon_timeout = FUSE_MIN_DAEMON_TIMEOUT;
+ else if (daemon_timeout > FUSE_MAX_DAEMON_TIMEOUT)
+ daemon_timeout = FUSE_MAX_DAEMON_TIMEOUT;
+ } else {
+ daemon_timeout = FUSE_DEFAULT_DAEMON_TIMEOUT;
+ }
+ subtype = vfs_getopts(opts, "subtype=", &err);
+
+ DEBUG2G("mntopts 0x%jx\n", (uintmax_t)mntopts);
+
+ err = fget(td, fd, CAP_READ, &fp);
+ if (err != 0) {
+ DEBUG("invalid or not opened device: data=%p\n", data);
+ goto out;
+ }
+ fptmp = td->td_fpop;
+ td->td_fpop = fp;
+ err = devfs_get_cdevpriv((void **)&data);
+ td->td_fpop = fptmp;
+ fdrop(fp, td);
+ FUSE_LOCK();
+ if (err != 0 || data == NULL || data->mp != NULL) {
+ DEBUG("invalid or not opened device: data=%p data.mp=%p\n",
+ data, data != NULL ? data->mp : NULL);
+ err = ENXIO;
+ FUSE_UNLOCK();
+ goto out;
+ }
+ if (fdata_get_dead(data)) {
+ DEBUG("device is dead during mount: data=%p\n", data);
+ err = ENOTCONN;
+ FUSE_UNLOCK();
+ goto out;
+ }
+ /* Sanity + permission checks */
+ if (!data->daemoncred)
+ panic("fuse daemon found, but identity unknown");
+ if (mntopts & FSESS_DAEMON_CAN_SPY)
+ err = priv_check(td, PRIV_VFS_FUSE_ALLOWOTHER);
+ if (err == 0 && td->td_ucred->cr_uid != data->daemoncred->cr_uid)
+ /* are we allowed to do the first mount? */
+ err = priv_check(td, PRIV_VFS_FUSE_MOUNT_NONUSER);
+ if (err) {
+ FUSE_UNLOCK();
+ goto out;
+ }
+ /* We need this here as this slot is used by getnewvnode() */
+ mp->mnt_stat.f_iosize = PAGE_SIZE;
+ mp->mnt_data = data;
+ data->ref++;
+ data->mp = mp;
+ data->dataflags |= mntopts;
+ data->max_read = max_read;
+ data->daemon_timeout = daemon_timeout;
+#ifdef XXXIP
+ if (!priv_check(td, PRIV_VFS_FUSE_SYNC_UNMOUNT))
+ data->dataflags |= FSESS_CAN_SYNC_UNMOUNT;
+#endif
+ FUSE_UNLOCK();
+
+ vfs_getnewfsid(mp);
+ mp->mnt_flag |= MNT_LOCAL;
+ mp->mnt_kern_flag |= MNTK_MPSAFE;
+ if (subtype) {
+ strlcat(mp->mnt_stat.f_fstypename, ".", MFSNAMELEN);
+ strlcat(mp->mnt_stat.f_fstypename, subtype, MFSNAMELEN);
+ }
+ copystr(fspec, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &len);
+ bzero(mp->mnt_stat.f_mntfromname + len, MNAMELEN - len);
+ DEBUG2G("mp %p: %s\n", mp, mp->mnt_stat.f_mntfromname);
+
+ /* Now handshaking with daemon */
+ fuse_internal_send_init(data, td);
+
+out:
+ if (err) {
+ FUSE_LOCK();
+ if (data->mp == mp) {
+ /*
+ * Destroy device only if we acquired reference to
+ * it
+ */
+ DEBUG("mount failed, destroy device: data=%p mp=%p"
+ " err=%d\n",
+ data, mp, err);
+ data->mp = NULL;
+ fdata_trydestroy(data);
+ }
+ FUSE_UNLOCK();
+ dev_rel(fdev);
+ }
+ return err;
+}
+
+static int
+fuse_vfsop_unmount(struct mount *mp, int mntflags)
+{
+ int err = 0;
+ int flags = 0;
+
+ struct cdev *fdev;
+ struct fuse_data *data;
+ struct fuse_dispatcher fdi;
+ struct thread *td = curthread;
+
+ fuse_trace_printf_vfsop();
+
+ if (mntflags & MNT_FORCE) {
+ flags |= FORCECLOSE;
+ }
+ data = fuse_get_mpdata(mp);
+ if (!data) {
+ panic("no private data for mount point?");
+ }
+ /* There is 1 extra root vnode reference (mp->mnt_data). */
+ FUSE_LOCK();
+ if (data->vroot != NULL) {
+ struct vnode *vroot = data->vroot;
+
+ data->vroot = NULL;
+ FUSE_UNLOCK();
+ vrele(vroot);
+ } else
+ FUSE_UNLOCK();
+ err = vflush(mp, 0, flags, td);
+ if (err) {
+ debug_printf("vflush failed");
+ return err;
+ }
+ if (fdata_get_dead(data)) {
+ goto alreadydead;
+ }
+ fdisp_init(&fdi, 0);
+ fdisp_make(&fdi, FUSE_DESTROY, mp, 0, td, NULL);
+
+ err = fdisp_wait_answ(&fdi);
+ fdisp_destroy(&fdi);
+
+ fdata_set_dead(data);
+
+alreadydead:
+ FUSE_LOCK();
+ data->mp = NULL;
+ fdev = data->fdev;
+ fdata_trydestroy(data);
+ FUSE_UNLOCK();
+
+ MNT_ILOCK(mp);
+ mp->mnt_data = NULL;
+ mp->mnt_flag &= ~MNT_LOCAL;
+ MNT_IUNLOCK(mp);
+
+ dev_rel(fdev);
+
+ return 0;
+}
+
+static int
+fuse_vfsop_root(struct mount *mp, int lkflags, struct vnode **vpp)
+{
+ struct fuse_data *data = fuse_get_mpdata(mp);
+ int err = 0;
+
+ if (data->vroot != NULL) {
+ err = vget(data->vroot, lkflags, curthread);
+ if (err == 0)
+ *vpp = data->vroot;
+ } else {
+ err = fuse_vnode_get(mp, FUSE_ROOT_ID, NULL, vpp, NULL, VDIR);
+ if (err == 0) {
+ FUSE_LOCK();
+ MPASS(data->vroot == NULL || data->vroot == *vpp);
+ if (data->vroot == NULL) {
+ DEBUG("new root vnode\n");
+ data->vroot = *vpp;
+ FUSE_UNLOCK();
+ vref(*vpp);
+ } else if (data->vroot != *vpp) {
+ DEBUG("root vnode race\n");
+ FUSE_UNLOCK();
+ VOP_UNLOCK(*vpp, 0);
+ vrele(*vpp);
+ vrecycle(*vpp);
+ *vpp = data->vroot;
+ } else
+ FUSE_UNLOCK();
+ }
+ }
+ return err;
+}
+
+static int
+fuse_vfsop_statfs(struct mount *mp, struct statfs *sbp)
+{
+ struct fuse_dispatcher fdi;
+ int err = 0;
+
+ struct fuse_statfs_out *fsfo;
+ struct fuse_data *data;
+
+ DEBUG2G("mp %p: %s\n", mp, mp->mnt_stat.f_mntfromname);
+ data = fuse_get_mpdata(mp);
+
+ if (!(data->dataflags & FSESS_INITED))
+ goto fake;
+
+ fdisp_init(&fdi, 0);
+ fdisp_make(&fdi, FUSE_STATFS, mp, FUSE_ROOT_ID, NULL, NULL);
+ err = fdisp_wait_answ(&fdi);
+ if (err) {
+ fdisp_destroy(&fdi);
+ if (err == ENOTCONN) {
+ /*
+ * We want to seem a legitimate fs even if the daemon
+ * is stiff dead... (so that, eg., we can still do path
+ * based unmounting after the daemon dies).
+ */
+ goto fake;
+ }
+ return err;
+ }
+ fsfo = fdi.answ;
+
+ sbp->f_blocks = fsfo->st.blocks;
+ sbp->f_bfree = fsfo->st.bfree;
+ sbp->f_bavail = fsfo->st.bavail;
+ sbp->f_files = fsfo->st.files;
+ sbp->f_ffree = fsfo->st.ffree; /* cast from uint64_t to int64_t */
+ sbp->f_namemax = fsfo->st.namelen;
+ sbp->f_bsize = fsfo->st.frsize; /* cast from uint32_t to uint64_t */
+
+ DEBUG("fuse_statfs_out -- blocks: %llu, bfree: %llu, bavail: %llu, "
+ "fil es: %llu, ffree: %llu, bsize: %i, namelen: %i\n",
+ (unsigned long long)fsfo->st.blocks,
+ (unsigned long long)fsfo->st.bfree,
+ (unsigned long long)fsfo->st.bavail,
+ (unsigned long long)fsfo->st.files,
+ (unsigned long long)fsfo->st.ffree, fsfo->st.bsize,
+ fsfo->st.namelen);
+
+ fdisp_destroy(&fdi);
+ return 0;
+
+fake:
+ sbp->f_blocks = 0;
+ sbp->f_bfree = 0;
+ sbp->f_bavail = 0;
+ sbp->f_files = 0;
+ sbp->f_ffree = 0;
+ sbp->f_namemax = 0;
+ sbp->f_bsize = FUSE_DEFAULT_BLOCKSIZE;
+
+ return 0;
+}
diff --git a/sys/fs/fuse/fuse_vnops.c b/sys/fs/fuse/fuse_vnops.c
new file mode 100644
index 0000000..172210f
--- /dev/null
+++ b/sys/fs/fuse/fuse_vnops.c
@@ -0,0 +1,2037 @@
+/*
+ * Copyright (c) 2007-2009 Google Inc. and Amit Singh
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
+ * OWNER 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.
+ *
+ * Copyright (C) 2005 Csaba Henk.
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/module.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/conf.h>
+#include <sys/uio.h>
+#include <sys/malloc.h>
+#include <sys/queue.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/sx.h>
+#include <sys/proc.h>
+#include <sys/mount.h>
+#include <sys/vnode.h>
+#include <sys/namei.h>
+#include <sys/stat.h>
+#include <sys/unistd.h>
+#include <sys/filedesc.h>
+#include <sys/file.h>
+#include <sys/fcntl.h>
+#include <sys/dirent.h>
+#include <sys/bio.h>
+#include <sys/buf.h>
+#include <sys/sysctl.h>
+
+#include <vm/vm.h>
+#include <vm/vm_extern.h>
+#include <vm/pmap.h>
+#include <vm/vm_map.h>
+#include <vm/vm_page.h>
+#include <vm/vm_param.h>
+#include <vm/vm_object.h>
+#include <vm/vm_pager.h>
+#include <vm/vnode_pager.h>
+#include <vm/vm_object.h>
+
+#include "fuse.h"
+#include "fuse_file.h"
+#include "fuse_internal.h"
+#include "fuse_ipc.h"
+#include "fuse_node.h"
+#include "fuse_param.h"
+#include "fuse_io.h"
+
+#include <sys/priv.h>
+
+#define FUSE_DEBUG_MODULE VNOPS
+#include "fuse_debug.h"
+
+/* vnode ops */
+static vop_access_t fuse_vnop_access;
+static vop_close_t fuse_vnop_close;
+static vop_create_t fuse_vnop_create;
+static vop_fsync_t fuse_vnop_fsync;
+static vop_getattr_t fuse_vnop_getattr;
+static vop_inactive_t fuse_vnop_inactive;
+static vop_link_t fuse_vnop_link;
+static vop_lookup_t fuse_vnop_lookup;
+static vop_mkdir_t fuse_vnop_mkdir;
+static vop_mknod_t fuse_vnop_mknod;
+static vop_open_t fuse_vnop_open;
+static vop_read_t fuse_vnop_read;
+static vop_readdir_t fuse_vnop_readdir;
+static vop_readlink_t fuse_vnop_readlink;
+static vop_reclaim_t fuse_vnop_reclaim;
+static vop_remove_t fuse_vnop_remove;
+static vop_rename_t fuse_vnop_rename;
+static vop_rmdir_t fuse_vnop_rmdir;
+static vop_setattr_t fuse_vnop_setattr;
+static vop_strategy_t fuse_vnop_strategy;
+static vop_symlink_t fuse_vnop_symlink;
+static vop_write_t fuse_vnop_write;
+static vop_getpages_t fuse_vnop_getpages;
+static vop_putpages_t fuse_vnop_putpages;
+static vop_print_t fuse_vnop_print;
+
+struct vop_vector fuse_vnops = {
+ .vop_default = &default_vnodeops,
+ .vop_access = fuse_vnop_access,
+ .vop_close = fuse_vnop_close,
+ .vop_create = fuse_vnop_create,
+ .vop_fsync = fuse_vnop_fsync,
+ .vop_getattr = fuse_vnop_getattr,
+ .vop_inactive = fuse_vnop_inactive,
+ .vop_link = fuse_vnop_link,
+ .vop_lookup = fuse_vnop_lookup,
+ .vop_mkdir = fuse_vnop_mkdir,
+ .vop_mknod = fuse_vnop_mknod,
+ .vop_open = fuse_vnop_open,
+ .vop_pathconf = vop_stdpathconf,
+ .vop_read = fuse_vnop_read,
+ .vop_readdir = fuse_vnop_readdir,
+ .vop_readlink = fuse_vnop_readlink,
+ .vop_reclaim = fuse_vnop_reclaim,
+ .vop_remove = fuse_vnop_remove,
+ .vop_rename = fuse_vnop_rename,
+ .vop_rmdir = fuse_vnop_rmdir,
+ .vop_setattr = fuse_vnop_setattr,
+ .vop_strategy = fuse_vnop_strategy,
+ .vop_symlink = fuse_vnop_symlink,
+ .vop_write = fuse_vnop_write,
+ .vop_getpages = fuse_vnop_getpages,
+ .vop_putpages = fuse_vnop_putpages,
+ .vop_print = fuse_vnop_print,
+};
+
+static u_long fuse_lookup_cache_hits = 0;
+
+SYSCTL_ULONG(_vfs_fuse, OID_AUTO, lookup_cache_hits, CTLFLAG_RD,
+ &fuse_lookup_cache_hits, 0, "");
+
+static u_long fuse_lookup_cache_misses = 0;
+
+SYSCTL_ULONG(_vfs_fuse, OID_AUTO, lookup_cache_misses, CTLFLAG_RD,
+ &fuse_lookup_cache_misses, 0, "");
+
+int fuse_lookup_cache_enable = 1;
+
+SYSCTL_INT(_vfs_fuse, OID_AUTO, lookup_cache_enable, CTLFLAG_RW,
+ &fuse_lookup_cache_enable, 0, "");
+
+/*
+ * XXX: This feature is highly experimental and can bring to instabilities,
+ * needs revisiting before to be enabled by default.
+ */
+static int fuse_reclaim_revoked = 0;
+
+SYSCTL_INT(_vfs_fuse, OID_AUTO, reclaim_revoked, CTLFLAG_RW,
+ &fuse_reclaim_revoked, 0, "");
+
+int fuse_pbuf_freecnt = -1;
+
+#define fuse_vm_page_lock(m) vm_page_lock((m));
+#define fuse_vm_page_unlock(m) vm_page_unlock((m));
+#define fuse_vm_page_lock_queues() ((void)0)
+#define fuse_vm_page_unlock_queues() ((void)0)
+
+/*
+ struct vnop_access_args {
+ struct vnode *a_vp;
+#if VOP_ACCESS_TAKES_ACCMODE_T
+ accmode_t a_accmode;
+#else
+ int a_mode;
+#endif
+ struct ucred *a_cred;
+ struct thread *a_td;
+ };
+*/
+static int
+fuse_vnop_access(struct vop_access_args *ap)
+{
+ struct vnode *vp = ap->a_vp;
+ int accmode = ap->a_accmode;
+ struct ucred *cred = ap->a_cred;
+
+ struct fuse_access_param facp;
+ struct fuse_data *data = fuse_get_mpdata(vnode_mount(vp));
+
+ int err;
+
+ DEBUG2G("inode=%ju\n", (uintmax_t)VTOI(vp));
+
+ if (fuse_isdeadfs(vp)) {
+ if (vnode_isvroot(vp)) {
+ return 0;
+ }
+ return ENXIO;
+ }
+ if (!(data->dataflags & FSESS_INITED)) {
+ if (vnode_isvroot(vp)) {
+ if (priv_check_cred(cred, PRIV_VFS_ADMIN, 0) ||
+ (fuse_match_cred(data->daemoncred, cred) == 0)) {
+ return 0;
+ }
+ }
+ return EBADF;
+ }
+ if (vnode_islnk(vp)) {
+ return 0;
+ }
+ bzero(&facp, sizeof(facp));
+
+ err = fuse_internal_access(vp, accmode, &facp, ap->a_td, ap->a_cred);
+ DEBUG2G("err=%d accmode=0x%x\n", err, accmode);
+ return err;
+}
+
+/*
+ struct vnop_close_args {
+ struct vnode *a_vp;
+ int a_fflag;
+ struct ucred *a_cred;
+ struct thread *a_td;
+ };
+*/
+static int
+fuse_vnop_close(struct vop_close_args *ap)
+{
+ struct vnode *vp = ap->a_vp;
+ struct ucred *cred = ap->a_cred;
+ int fflag = ap->a_fflag;
+ fufh_type_t fufh_type;
+
+ fuse_trace_printf_vnop();
+
+ if (fuse_isdeadfs(vp)) {
+ return 0;
+ }
+ if (vnode_isdir(vp)) {
+ if (fuse_filehandle_valid(vp, FUFH_RDONLY)) {
+ fuse_filehandle_close(vp, FUFH_RDONLY, NULL, cred);
+ }
+ return 0;
+ }
+ if (fflag & IO_NDELAY) {
+ return 0;
+ }
+ fufh_type = fuse_filehandle_xlate_from_fflags(fflag);
+
+ if (!fuse_filehandle_valid(vp, fufh_type)) {
+ int i;
+
+ for (i = 0; i < FUFH_MAXTYPE; i++)
+ if (fuse_filehandle_valid(vp, i))
+ break;
+ if (i == FUFH_MAXTYPE)
+ panic("FUSE: fufh type %d found to be invalid in close"
+ " (fflag=0x%x)\n",
+ fufh_type, fflag);
+ }
+ if ((VTOFUD(vp)->flag & FN_SIZECHANGE) != 0) {
+ fuse_vnode_savesize(vp, cred);
+ }
+ return 0;
+}
+
+/*
+ struct vnop_create_args {
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ struct vattr *a_vap;
+ };
+*/
+static int
+fuse_vnop_create(struct vop_create_args *ap)
+{
+ struct vnode *dvp = ap->a_dvp;
+ struct vnode **vpp = ap->a_vpp;
+ struct componentname *cnp = ap->a_cnp;
+ struct vattr *vap = ap->a_vap;
+ struct thread *td = cnp->cn_thread;
+ struct ucred *cred = cnp->cn_cred;
+
+ struct fuse_open_in *foi;
+ struct fuse_entry_out *feo;
+ struct fuse_dispatcher fdi;
+ struct fuse_dispatcher *fdip = &fdi;
+
+ int err;
+
+ struct mount *mp = vnode_mount(dvp);
+ uint64_t parentnid = VTOFUD(dvp)->nid;
+ mode_t mode = MAKEIMODE(vap->va_type, vap->va_mode);
+ uint64_t x_fh_id;
+ uint32_t x_open_flags;
+
+ fuse_trace_printf_vnop();
+
+ if (fuse_isdeadfs(dvp)) {
+ return ENXIO;
+ }
+ bzero(&fdi, sizeof(fdi));
+
+ /* XXX: Will we ever want devices ? */
+ if ((vap->va_type != VREG)) {
+ MPASS(vap->va_type != VFIFO);
+ goto bringup;
+ }
+ debug_printf("parent nid = %ju, mode = %x\n", (uintmax_t)parentnid,
+ mode);
+
+ fdisp_init(fdip, sizeof(*foi) + cnp->cn_namelen + 1);
+ if (!fsess_isimpl(mp, FUSE_CREATE)) {
+ debug_printf("eh, daemon doesn't implement create?\n");
+ return (EINVAL);
+ }
+ fdisp_make(fdip, FUSE_CREATE, vnode_mount(dvp), parentnid, td, cred);
+
+ foi = fdip->indata;
+ foi->mode = mode;
+ foi->flags = O_CREAT | O_RDWR;
+
+ memcpy((char *)fdip->indata + sizeof(*foi), cnp->cn_nameptr,
+ cnp->cn_namelen);
+ ((char *)fdip->indata)[sizeof(*foi) + cnp->cn_namelen] = '\0';
+
+ err = fdisp_wait_answ(fdip);
+
+ if (err == ENOSYS) {
+ debug_printf("create: got ENOSYS from daemon\n");
+ fsess_set_notimpl(mp, FUSE_CREATE);
+ fdisp_destroy(fdip);
+ } else if (err) {
+ debug_printf("create: darn, got err=%d from daemon\n", err);
+ goto out;
+ }
+bringup:
+ feo = fdip->answ;
+
+ if ((err = fuse_internal_checkentry(feo, VREG))) {
+ goto out;
+ }
+ err = fuse_vnode_get(mp, feo->nodeid, dvp, vpp, cnp, VREG);
+ if (err) {
+ struct fuse_release_in *fri;
+ uint64_t nodeid = feo->nodeid;
+ uint64_t fh_id = ((struct fuse_open_out *)(feo + 1))->fh;
+
+ fdisp_init(fdip, sizeof(*fri));
+ fdisp_make(fdip, FUSE_RELEASE, mp, nodeid, td, cred);
+ fri = fdip->indata;
+ fri->fh = fh_id;
+ fri->flags = OFLAGS(mode);
+ fuse_insert_callback(fdip->tick, fuse_internal_forget_callback);
+ fuse_insert_message(fdip->tick);
+ return err;
+ }
+ ASSERT_VOP_ELOCKED(*vpp, "fuse_vnop_create");
+
+ fdip->answ = feo + 1;
+
+ x_fh_id = ((struct fuse_open_out *)(feo + 1))->fh;
+ x_open_flags = ((struct fuse_open_out *)(feo + 1))->open_flags;
+ fuse_filehandle_init(*vpp, FUFH_RDWR, NULL, x_fh_id);
+ fuse_vnode_open(*vpp, x_open_flags, td);
+ cache_purge_negative(dvp);
+
+out:
+ fdisp_destroy(fdip);
+ return err;
+}
+
+/*
+ * Our vnop_fsync roughly corresponds to the FUSE_FSYNC method. The Linux
+ * version of FUSE also has a FUSE_FLUSH method.
+ *
+ * On Linux, fsync() synchronizes a file's complete in-core state with that
+ * on disk. The call is not supposed to return until the system has completed
+ * that action or until an error is detected.
+ *
+ * Linux also has an fdatasync() call that is similar to fsync() but is not
+ * required to update the metadata such as access time and modification time.
+ */
+
+/*
+ struct vnop_fsync_args {
+ struct vnodeop_desc *a_desc;
+ struct vnode * a_vp;
+ struct ucred * a_cred;
+ int a_waitfor;
+ struct thread * a_td;
+ };
+*/
+static int
+fuse_vnop_fsync(struct vop_fsync_args *ap)
+{
+ struct vnode *vp = ap->a_vp;
+ struct thread *td = ap->a_td;
+
+ struct fuse_filehandle *fufh;
+ struct fuse_vnode_data *fvdat = VTOFUD(vp);
+
+ int type, err = 0;
+
+ fuse_trace_printf_vnop();
+
+ if (fuse_isdeadfs(vp)) {
+ return 0;
+ }
+ if ((err = vop_stdfsync(ap)))
+ return err;
+
+ if (!fsess_isimpl(vnode_mount(vp),
+ (vnode_vtype(vp) == VDIR ? FUSE_FSYNCDIR : FUSE_FSYNC))) {
+ goto out;
+ }
+ for (type = 0; type < FUFH_MAXTYPE; type++) {
+ fufh = &(fvdat->fufh[type]);
+ if (FUFH_IS_VALID(fufh)) {
+ fuse_internal_fsync(vp, td, NULL, fufh);
+ }
+ }
+
+out:
+ return 0;
+}
+
+/*
+ struct vnop_getattr_args {
+ struct vnode *a_vp;
+ struct vattr *a_vap;
+ struct ucred *a_cred;
+ struct thread *a_td;
+ };
+*/
+static int
+fuse_vnop_getattr(struct vop_getattr_args *ap)
+{
+ struct vnode *vp = ap->a_vp;
+ struct vattr *vap = ap->a_vap;
+ struct ucred *cred = ap->a_cred;
+ struct thread *td = curthread;
+ struct fuse_vnode_data *fvdat = VTOFUD(vp);
+
+ int err = 0;
+ int dataflags;
+ struct fuse_dispatcher fdi;
+
+ DEBUG2G("inode=%ju\n", (uintmax_t)VTOI(vp));
+
+ dataflags = fuse_get_mpdata(vnode_mount(vp))->dataflags;
+
+ /* Note that we are not bailing out on a dead file system just yet. */
+
+ /* look for cached attributes */
+ if (fuse_isvalid_attr(vp)) {
+ if (vap != VTOVA(vp)) {
+ memcpy(vap, VTOVA(vp), sizeof(*vap));
+ }
+ if ((fvdat->flag & FN_SIZECHANGE) != 0) {
+ vap->va_size = fvdat->filesize;
+ }
+ debug_printf("return cached: inode=%ju\n", (uintmax_t)VTOI(vp));
+ return 0;
+ }
+ if (!(dataflags & FSESS_INITED)) {
+ if (!vnode_isvroot(vp)) {
+ fdata_set_dead(fuse_get_mpdata(vnode_mount(vp)));
+ err = ENOTCONN;
+ debug_printf("fuse_getattr b: returning ENOTCONN\n");
+ return err;
+ } else {
+ goto fake;
+ }
+ }
+ fdisp_init(&fdi, 0);
+ if ((err = fdisp_simple_putget_vp(&fdi, FUSE_GETATTR, vp, td, cred))) {
+ if ((err == ENOTCONN) && vnode_isvroot(vp)) {
+ /* see comment at similar place in fuse_statfs() */
+ fdisp_destroy(&fdi);
+ goto fake;
+ }
+ if (err == ENOENT) {
+ fuse_internal_vnode_disappear(vp);
+ }
+ goto out;
+ }
+ cache_attrs(vp, (struct fuse_attr_out *)fdi.answ);
+ if (vap != VTOVA(vp)) {
+ memcpy(vap, VTOVA(vp), sizeof(*vap));
+ }
+ if ((fvdat->flag & FN_SIZECHANGE) != 0)
+ vap->va_size = fvdat->filesize;
+
+ if (vnode_isreg(vp) && (fvdat->flag & FN_SIZECHANGE) == 0) {
+ /*
+ * This is for those cases when the file size changed without us
+ * knowing, and we want to catch up.
+ */
+ off_t new_filesize = ((struct fuse_attr_out *)
+ fdi.answ)->attr.size;
+
+ if (fvdat->filesize != new_filesize) {
+ fuse_vnode_setsize(vp, cred, new_filesize);
+ }
+ }
+ KASSERT(vnode_vtype(vp) == vap->va_type, ("stale vnode"));
+ debug_printf("fuse_getattr e: returning 0\n");
+
+out:
+ fdisp_destroy(&fdi);
+ return err;
+
+fake:
+ bzero(vap, sizeof(*vap));
+ vap->va_type = vnode_vtype(vp);
+
+ return 0;
+}
+
+/*
+ struct vnop_inactive_args {
+ struct vnode *a_vp;
+ struct thread *a_td;
+ };
+*/
+static int
+fuse_vnop_inactive(struct vop_inactive_args *ap)
+{
+ struct vnode *vp = ap->a_vp;
+ struct thread *td = ap->a_td;
+
+ struct fuse_vnode_data *fvdat = VTOFUD(vp);
+ struct fuse_filehandle *fufh = NULL;
+
+ int type, need_flush = 1;
+
+ DEBUG("inode=%ju\n", (uintmax_t)VTOI(vp));
+
+ for (type = 0; type < FUFH_MAXTYPE; type++) {
+ fufh = &(fvdat->fufh[type]);
+ if (FUFH_IS_VALID(fufh)) {
+ if (need_flush && vp->v_type == VREG) {
+ if ((VTOFUD(vp)->flag & FN_SIZECHANGE) != 0) {
+ fuse_vnode_savesize(vp, NULL);
+ }
+ if (fuse_data_cache_invalidate ||
+ (fvdat->flag & FN_REVOKED) != 0)
+ fuse_io_invalbuf(vp, td);
+ else
+ fuse_io_flushbuf(vp, MNT_WAIT, td);
+ need_flush = 0;
+ }
+ fuse_filehandle_close(vp, type, td, NULL);
+ }
+ }
+
+ if ((fvdat->flag & FN_REVOKED) != 0 && fuse_reclaim_revoked) {
+ vrecycle(vp);
+ }
+ return 0;
+}
+
+/*
+ struct vnop_link_args {
+ struct vnode *a_tdvp;
+ struct vnode *a_vp;
+ struct componentname *a_cnp;
+ };
+*/
+static int
+fuse_vnop_link(struct vop_link_args *ap)
+{
+ struct vnode *vp = ap->a_vp;
+ struct vnode *tdvp = ap->a_tdvp;
+ struct componentname *cnp = ap->a_cnp;
+
+ struct vattr *vap = VTOVA(vp);
+
+ struct fuse_dispatcher fdi;
+ struct fuse_entry_out *feo;
+ struct fuse_link_in fli;
+
+ int err;
+
+ fuse_trace_printf_vnop();
+
+ if (fuse_isdeadfs(vp)) {
+ return ENXIO;
+ }
+ if (vnode_mount(tdvp) != vnode_mount(vp)) {
+ return EXDEV;
+ }
+ if (vap->va_nlink >= FUSE_LINK_MAX) {
+ return EMLINK;
+ }
+ fli.oldnodeid = VTOI(vp);
+
+ fdisp_init(&fdi, 0);
+ fuse_internal_newentry_makerequest(vnode_mount(tdvp), VTOI(tdvp), cnp,
+ FUSE_LINK, &fli, sizeof(fli), &fdi);
+ if ((err = fdisp_wait_answ(&fdi))) {
+ goto out;
+ }
+ feo = fdi.answ;
+
+ err = fuse_internal_checkentry(feo, vnode_vtype(vp));
+ fuse_invalidate_attr(tdvp);
+ fuse_invalidate_attr(vp);
+
+out:
+ fdisp_destroy(&fdi);
+ return err;
+}
+
+/*
+ struct vnop_lookup_args {
+ struct vnodeop_desc *a_desc;
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ };
+*/
+int
+fuse_vnop_lookup(struct vop_lookup_args *ap)
+{
+ struct vnode *dvp = ap->a_dvp;
+ struct vnode **vpp = ap->a_vpp;
+ struct componentname *cnp = ap->a_cnp;
+ struct thread *td = cnp->cn_thread;
+ struct ucred *cred = cnp->cn_cred;
+
+ int nameiop = cnp->cn_nameiop;
+ int flags = cnp->cn_flags;
+ int wantparent = flags & (LOCKPARENT | WANTPARENT);
+ int islastcn = flags & ISLASTCN;
+ struct mount *mp = vnode_mount(dvp);
+
+ int err = 0;
+ int lookup_err = 0;
+ struct vnode *vp = NULL;
+
+ struct fuse_dispatcher fdi;
+ enum fuse_opcode op;
+
+ uint64_t nid;
+ struct fuse_access_param facp;
+
+ DEBUG2G("parent_inode=%ju - %*s\n",
+ (uintmax_t)VTOI(dvp), (int)cnp->cn_namelen, cnp->cn_nameptr);
+
+ if (fuse_isdeadfs(dvp)) {
+ *vpp = NULL;
+ return ENXIO;
+ }
+ if (!vnode_isdir(dvp)) {
+ return ENOTDIR;
+ }
+ if (islastcn && vfs_isrdonly(mp) && (nameiop != LOOKUP)) {
+ return EROFS;
+ }
+ /*
+ * We do access check prior to doing anything else only in the case
+ * when we are at fs root (we'd like to say, "we are at the first
+ * component", but that's not exactly the same... nevermind).
+ * See further comments at further access checks.
+ */
+
+ bzero(&facp, sizeof(facp));
+ if (vnode_isvroot(dvp)) { /* early permission check hack */
+ if ((err = fuse_internal_access(dvp, VEXEC, &facp, td, cred))) {
+ return err;
+ }
+ }
+ if (flags & ISDOTDOT) {
+ nid = VTOFUD(dvp)->parent_nid;
+ if (nid == 0) {
+ return ENOENT;
+ }
+ fdisp_init(&fdi, 0);
+ op = FUSE_GETATTR;
+ goto calldaemon;
+ } else if (cnp->cn_namelen == 1 && *(cnp->cn_nameptr) == '.') {
+ nid = VTOI(dvp);
+ fdisp_init(&fdi, 0);
+ op = FUSE_GETATTR;
+ goto calldaemon;
+ } else if (fuse_lookup_cache_enable) {
+ err = cache_lookup(dvp, vpp, cnp, NULL, NULL);
+ switch (err) {
+
+ case -1: /* positive match */
+ atomic_add_acq_long(&fuse_lookup_cache_hits, 1);
+ return 0;
+
+ case 0: /* no match in cache */
+ atomic_add_acq_long(&fuse_lookup_cache_misses, 1);
+ break;
+
+ case ENOENT: /* negative match */
+ /* fall through */
+ default:
+ return err;
+ }
+ }
+ nid = VTOI(dvp);
+ fdisp_init(&fdi, cnp->cn_namelen + 1);
+ op = FUSE_LOOKUP;
+
+calldaemon:
+ fdisp_make(&fdi, op, mp, nid, td, cred);
+
+ if (op == FUSE_LOOKUP) {
+ memcpy(fdi.indata, cnp->cn_nameptr, cnp->cn_namelen);
+ ((char *)fdi.indata)[cnp->cn_namelen] = '\0';
+ }
+ lookup_err = fdisp_wait_answ(&fdi);
+
+ if ((op == FUSE_LOOKUP) && !lookup_err) { /* lookup call succeeded */
+ nid = ((struct fuse_entry_out *)fdi.answ)->nodeid;
+ if (!nid) {
+ /*
+ * zero nodeid is the same as "not found",
+ * but it's also cacheable (which we keep
+ * keep on doing not as of writing this)
+ */
+ lookup_err = ENOENT;
+ } else if (nid == FUSE_ROOT_ID) {
+ lookup_err = EINVAL;
+ }
+ }
+ if (lookup_err &&
+ (!fdi.answ_stat || lookup_err != ENOENT || op != FUSE_LOOKUP)) {
+ fdisp_destroy(&fdi);
+ return lookup_err;
+ }
+ /* lookup_err, if non-zero, must be ENOENT at this point */
+
+ if (lookup_err) {
+
+ if ((nameiop == CREATE || nameiop == RENAME) && islastcn
+ /* && directory dvp has not been removed */ ) {
+
+ if (vfs_isrdonly(mp)) {
+ err = EROFS;
+ goto out;
+ }
+#if 0 /* THINK_ABOUT_THIS */
+ if ((err = fuse_internal_access(dvp, VWRITE, cred, td, &facp))) {
+ goto out;
+ }
+#endif
+
+ /*
+ * Possibly record the position of a slot in the
+ * directory large enough for the new component name.
+ * This can be recorded in the vnode private data for
+ * dvp. Set the SAVENAME flag to hold onto the
+ * pathname for use later in VOP_CREATE or VOP_RENAME.
+ */
+ cnp->cn_flags |= SAVENAME;
+
+ err = EJUSTRETURN;
+ goto out;
+ }
+ /* Consider inserting name into cache. */
+
+ /*
+ * No we can't use negative caching, as the fs
+ * changes are out of our control.
+ * False positives' falseness turns out just as things
+ * go by, but false negatives' falseness doesn't.
+ * (and aiding the caching mechanism with extra control
+ * mechanisms comes quite close to beating the whole purpose
+ * caching...)
+ */
+#if 0
+ if ((cnp->cn_flags & MAKEENTRY) && nameiop != CREATE) {
+ DEBUG("inserting NULL into cache\n");
+ cache_enter(dvp, NULL, cnp);
+ }
+#endif
+ err = ENOENT;
+ goto out;
+
+ } else {
+
+ /* !lookup_err */
+
+ struct fuse_entry_out *feo = NULL;
+ struct fuse_attr *fattr = NULL;
+
+ if (op == FUSE_GETATTR) {
+ fattr = &((struct fuse_attr_out *)fdi.answ)->attr;
+ } else {
+ feo = (struct fuse_entry_out *)fdi.answ;
+ fattr = &(feo->attr);
+ }
+
+ /*
+ * If deleting, and at end of pathname, return parameters
+ * which can be used to remove file. If the wantparent flag
+ * isn't set, we return only the directory, otherwise we go on
+ * and lock the inode, being careful with ".".
+ */
+ if (nameiop == DELETE && islastcn) {
+ /*
+ * Check for write access on directory.
+ */
+ facp.xuid = fattr->uid;
+ facp.facc_flags |= FACCESS_STICKY;
+ err = fuse_internal_access(dvp, VWRITE, &facp, td, cred);
+ facp.facc_flags &= ~FACCESS_XQUERIES;
+
+ if (err) {
+ goto out;
+ }
+ if (nid == VTOI(dvp)) {
+ vref(dvp);
+ *vpp = dvp;
+ } else {
+ err = fuse_vnode_get(dvp->v_mount, nid, dvp,
+ &vp, cnp, IFTOVT(fattr->mode));
+ if (err)
+ goto out;
+ *vpp = vp;
+ }
+
+ /*
+ * Save the name for use in VOP_RMDIR and VOP_REMOVE
+ * later.
+ */
+ cnp->cn_flags |= SAVENAME;
+ goto out;
+
+ }
+ /*
+ * If rewriting (RENAME), return the inode and the
+ * information required to rewrite the present directory
+ * Must get inode of directory entry to verify it's a
+ * regular file, or empty directory.
+ */
+ if (nameiop == RENAME && wantparent && islastcn) {
+
+#if 0 /* THINK_ABOUT_THIS */
+ if ((err = fuse_internal_access(dvp, VWRITE, cred, td, &facp))) {
+ goto out;
+ }
+#endif
+
+ /*
+ * Check for "."
+ */
+ if (nid == VTOI(dvp)) {
+ err = EISDIR;
+ goto out;
+ }
+ err = fuse_vnode_get(vnode_mount(dvp),
+ nid,
+ dvp,
+ &vp,
+ cnp,
+ IFTOVT(fattr->mode));
+ if (err) {
+ goto out;
+ }
+ *vpp = vp;
+ /*
+ * Save the name for use in VOP_RENAME later.
+ */
+ cnp->cn_flags |= SAVENAME;
+
+ goto out;
+ }
+ if (flags & ISDOTDOT) {
+ struct mount *mp;
+ int ltype;
+
+ /*
+ * Expanded copy of vn_vget_ino() so that
+ * fuse_vnode_get() can be used.
+ */
+ mp = dvp->v_mount;
+ ltype = VOP_ISLOCKED(dvp);
+ err = vfs_busy(mp, MBF_NOWAIT);
+ if (err != 0) {
+ vfs_ref(mp);
+ VOP_UNLOCK(dvp, 0);
+ err = vfs_busy(mp, 0);
+ vn_lock(dvp, ltype | LK_RETRY);
+ vfs_rel(mp);
+ if (err)
+ goto out;
+ if ((dvp->v_iflag & VI_DOOMED) != 0) {
+ err = ENOENT;
+ vfs_unbusy(mp);
+ goto out;
+ }
+ }
+ VOP_UNLOCK(dvp, 0);
+ err = fuse_vnode_get(vnode_mount(dvp),
+ nid,
+ NULL,
+ &vp,
+ cnp,
+ IFTOVT(fattr->mode));
+ vfs_unbusy(mp);
+ vn_lock(dvp, ltype | LK_RETRY);
+ if ((dvp->v_iflag & VI_DOOMED) != 0) {
+ if (err == 0)
+ vput(vp);
+ err = ENOENT;
+ }
+ if (err)
+ goto out;
+ *vpp = vp;
+ } else if (nid == VTOI(dvp)) {
+ vref(dvp);
+ *vpp = dvp;
+ } else {
+ err = fuse_vnode_get(vnode_mount(dvp),
+ nid,
+ dvp,
+ &vp,
+ cnp,
+ IFTOVT(fattr->mode));
+ if (err) {
+ goto out;
+ }
+ fuse_vnode_setparent(vp, dvp);
+ *vpp = vp;
+ }
+
+ if (op == FUSE_GETATTR) {
+ cache_attrs(*vpp, (struct fuse_attr_out *)fdi.answ);
+ } else {
+ cache_attrs(*vpp, (struct fuse_entry_out *)fdi.answ);
+ }
+
+ /* Insert name into cache if appropriate. */
+
+ /*
+ * Nooo, caching is evil. With caching, we can't avoid stale
+ * information taking over the playground (cached info is not
+ * just positive/negative, it does have qualitative aspects,
+ * too). And a (VOP/FUSE)_GETATTR is always thrown anyway, when
+ * walking down along cached path components, and that's not
+ * any cheaper than FUSE_LOOKUP. This might change with
+ * implementing kernel side attr caching, but... In Linux,
+ * lookup results are not cached, and the daemon is bombarded
+ * with FUSE_LOOKUPS on and on. This shows that by design, the
+ * daemon is expected to handle frequent lookup queries
+ * efficiently, do its caching in userspace, and so on.
+ *
+ * So just leave the name cache alone.
+ */
+
+ /*
+ * Well, now I know, Linux caches lookups, but with a
+ * timeout... So it's the same thing as attribute caching:
+ * we can deal with it when implement timeouts.
+ */
+#if 0
+ if (cnp->cn_flags & MAKEENTRY) {
+ cache_enter(dvp, *vpp, cnp);
+ }
+#endif
+ }
+out:
+ if (!lookup_err) {
+
+ /* No lookup error; need to clean up. */
+
+ if (err) { /* Found inode; exit with no vnode. */
+ if (op == FUSE_LOOKUP) {
+ fuse_internal_forget_send(vnode_mount(dvp), td, cred,
+ nid, 1);
+ }
+ fdisp_destroy(&fdi);
+ return err;
+ } else {
+#ifndef NO_EARLY_PERM_CHECK_HACK
+ if (!islastcn) {
+ /*
+ * We have the attributes of the next item
+ * *now*, and it's a fact, and we do not
+ * have to do extra work for it (ie, beg the
+ * daemon), and it neither depends on such
+ * accidental things like attr caching. So
+ * the big idea: check credentials *now*,
+ * not at the beginning of the next call to
+ * lookup.
+ *
+ * The first item of the lookup chain (fs root)
+ * won't be checked then here, of course, as
+ * its never "the next". But go and see that
+ * the root is taken care about at the very
+ * beginning of this function.
+ *
+ * Now, given we want to do the access check
+ * this way, one might ask: so then why not
+ * do the access check just after fetching
+ * the inode and its attributes from the
+ * daemon? Why bother with producing the
+ * corresponding vnode at all if something
+ * is not OK? We know what's the deal as
+ * soon as we get those attrs... There is
+ * one bit of info though not given us by
+ * the daemon: whether his response is
+ * authorative or not... His response should
+ * be ignored if something is mounted over
+ * the dir in question. But that can be
+ * known only by having the vnode...
+ */
+ int tmpvtype = vnode_vtype(*vpp);
+
+ bzero(&facp, sizeof(facp));
+ /*the early perm check hack */
+ facp.facc_flags |= FACCESS_VA_VALID;
+
+ if ((tmpvtype != VDIR) && (tmpvtype != VLNK)) {
+ err = ENOTDIR;
+ }
+ if (!err && !vnode_mountedhere(*vpp)) {
+ err = fuse_internal_access(*vpp, VEXEC, &facp, td, cred);
+ }
+ if (err) {
+ if (tmpvtype == VLNK)
+ DEBUG("weird, permission error with a symlink?\n");
+ vput(*vpp);
+ *vpp = NULL;
+ }
+ }
+#endif
+ }
+ }
+ fdisp_destroy(&fdi);
+
+ return err;
+}
+
+/*
+ struct vnop_mkdir_args {
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ struct vattr *a_vap;
+ };
+*/
+static int
+fuse_vnop_mkdir(struct vop_mkdir_args *ap)
+{
+ struct vnode *dvp = ap->a_dvp;
+ struct vnode **vpp = ap->a_vpp;
+ struct componentname *cnp = ap->a_cnp;
+ struct vattr *vap = ap->a_vap;
+
+ int err = 0;
+
+ struct fuse_mkdir_in fmdi;
+
+ fuse_trace_printf_vnop();
+
+ if (fuse_isdeadfs(dvp)) {
+ return ENXIO;
+ }
+ fmdi.mode = MAKEIMODE(vap->va_type, vap->va_mode);
+
+ err = fuse_internal_newentry(dvp, vpp, cnp, FUSE_MKDIR, &fmdi,
+ sizeof(fmdi), VDIR);
+
+ if (err == 0) {
+ fuse_invalidate_attr(dvp);
+ }
+ return err;
+}
+
+/*
+ struct vnop_mknod_args {
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ struct vattr *a_vap;
+ };
+*/
+static int
+fuse_vnop_mknod(struct vop_mknod_args *ap)
+{
+
+ return (EINVAL);
+}
+
+
+/*
+ struct vnop_open_args {
+ struct vnode *a_vp;
+ int a_mode;
+ struct ucred *a_cred;
+ struct thread *a_td;
+ int a_fdidx; / struct file *a_fp;
+ };
+*/
+static int
+fuse_vnop_open(struct vop_open_args *ap)
+{
+ struct vnode *vp = ap->a_vp;
+ int mode = ap->a_mode;
+ struct thread *td = ap->a_td;
+ struct ucred *cred = ap->a_cred;
+
+ fufh_type_t fufh_type;
+ struct fuse_vnode_data *fvdat;
+
+ int error, isdir = 0;
+
+ DEBUG2G("inode=%ju mode=0x%x\n", (uintmax_t)VTOI(vp), mode);
+
+ if (fuse_isdeadfs(vp)) {
+ return ENXIO;
+ }
+ fvdat = VTOFUD(vp);
+
+ if (vnode_isdir(vp)) {
+ isdir = 1;
+ }
+ if (isdir) {
+ fufh_type = FUFH_RDONLY;
+ } else {
+ fufh_type = fuse_filehandle_xlate_from_fflags(mode);
+ }
+
+ if (fuse_filehandle_valid(vp, fufh_type)) {
+ fuse_vnode_open(vp, 0, td);
+ return 0;
+ }
+ error = fuse_filehandle_open(vp, fufh_type, NULL, td, cred);
+
+ return error;
+}
+
+/*
+ struct vnop_read_args {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ };
+*/
+static int
+fuse_vnop_read(struct vop_read_args *ap)
+{
+ struct vnode *vp = ap->a_vp;
+ struct uio *uio = ap->a_uio;
+ int ioflag = ap->a_ioflag;
+ struct ucred *cred = ap->a_cred;
+
+ DEBUG2G("inode=%ju offset=%jd resid=%zd\n",
+ (uintmax_t)VTOI(vp), uio->uio_offset, uio->uio_resid);
+
+ if (fuse_isdeadfs(vp)) {
+ return ENXIO;
+ }
+ return fuse_io_dispatch(vp, uio, ioflag, cred);
+}
+
+/*
+ struct vnop_readdir_args {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ struct ucred *a_cred;
+ int *a_eofflag;
+ int *ncookies;
+ u_long **a_cookies;
+ };
+*/
+static int
+fuse_vnop_readdir(struct vop_readdir_args *ap)
+{
+ struct vnode *vp = ap->a_vp;
+ struct uio *uio = ap->a_uio;
+ struct ucred *cred = ap->a_cred;
+
+ struct fuse_filehandle *fufh = NULL;
+ struct fuse_vnode_data *fvdat;
+ struct fuse_iov cookediov;
+
+ int err = 0;
+ int freefufh = 0;
+
+ DEBUG2G("inode=%ju\n", (uintmax_t)VTOI(vp));
+
+ if (fuse_isdeadfs(vp)) {
+ return ENXIO;
+ }
+ if ( /* XXXIP ((uio_iovcnt(uio) > 1)) || */
+ (uio_resid(uio) < sizeof(struct dirent))) {
+ return EINVAL;
+ }
+ fvdat = VTOFUD(vp);
+
+ if (!fuse_filehandle_valid(vp, FUFH_RDONLY)) {
+ DEBUG("calling readdir() before open()");
+ err = fuse_filehandle_open(vp, FUFH_RDONLY, &fufh, NULL, cred);
+ freefufh = 1;
+ } else {
+ err = fuse_filehandle_get(vp, FUFH_RDONLY, &fufh);
+ }
+ if (err) {
+ return (err);
+ }
+#define DIRCOOKEDSIZE FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + MAXNAMLEN + 1)
+ fiov_init(&cookediov, DIRCOOKEDSIZE);
+
+ err = fuse_internal_readdir(vp, uio, fufh, &cookediov);
+
+ fiov_teardown(&cookediov);
+ if (freefufh) {
+ fuse_filehandle_close(vp, FUFH_RDONLY, NULL, cred);
+ }
+ return err;
+}
+
+/*
+ struct vnop_readlink_args {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ struct ucred *a_cred;
+ };
+*/
+static int
+fuse_vnop_readlink(struct vop_readlink_args *ap)
+{
+ struct vnode *vp = ap->a_vp;
+ struct uio *uio = ap->a_uio;
+ struct ucred *cred = ap->a_cred;
+
+ struct fuse_dispatcher fdi;
+ int err;
+
+ DEBUG2G("inode=%ju\n", (uintmax_t)VTOI(vp));
+
+ if (fuse_isdeadfs(vp)) {
+ return ENXIO;
+ }
+ if (!vnode_islnk(vp)) {
+ return EINVAL;
+ }
+ fdisp_init(&fdi, 0);
+ err = fdisp_simple_putget_vp(&fdi, FUSE_READLINK, vp, curthread, cred);
+ if (err) {
+ goto out;
+ }
+ if (((char *)fdi.answ)[0] == '/' &&
+ fuse_get_mpdata(vnode_mount(vp))->dataflags & FSESS_PUSH_SYMLINKS_IN) {
+ char *mpth = vnode_mount(vp)->mnt_stat.f_mntonname;
+
+ err = uiomove(mpth, strlen(mpth), uio);
+ }
+ if (!err) {
+ err = uiomove(fdi.answ, fdi.iosize, uio);
+ }
+out:
+ fdisp_destroy(&fdi);
+ return err;
+}
+
+/*
+ struct vnop_reclaim_args {
+ struct vnode *a_vp;
+ struct thread *a_td;
+ };
+*/
+static int
+fuse_vnop_reclaim(struct vop_reclaim_args *ap)
+{
+ struct vnode *vp = ap->a_vp;
+ struct thread *td = ap->a_td;
+
+ struct fuse_vnode_data *fvdat = VTOFUD(vp);
+ struct fuse_filehandle *fufh = NULL;
+
+ int type;
+
+ if (!fvdat) {
+ panic("FUSE: no vnode data during recycling");
+ }
+ DEBUG("inode=%ju\n", (uintmax_t)VTOI(vp));
+
+ for (type = 0; type < FUFH_MAXTYPE; type++) {
+ fufh = &(fvdat->fufh[type]);
+ if (FUFH_IS_VALID(fufh)) {
+ printf("FUSE: vnode being reclaimed but fufh (type=%d) is valid",
+ type);
+ fuse_filehandle_close(vp, type, td, NULL);
+ }
+ }
+
+ if ((!fuse_isdeadfs(vp)) && (fvdat->nlookup)) {
+ fuse_internal_forget_send(vnode_mount(vp), td, NULL, VTOI(vp),
+ fvdat->nlookup);
+ }
+ fuse_vnode_setparent(vp, NULL);
+ cache_purge(vp);
+ vfs_hash_remove(vp);
+ vnode_destroy_vobject(vp);
+ fuse_vnode_destroy(vp);
+
+ return 0;
+}
+
+/*
+ struct vnop_remove_args {
+ struct vnode *a_dvp;
+ struct vnode *a_vp;
+ struct componentname *a_cnp;
+ };
+*/
+static int
+fuse_vnop_remove(struct vop_remove_args *ap)
+{
+ struct vnode *dvp = ap->a_dvp;
+ struct vnode *vp = ap->a_vp;
+ struct componentname *cnp = ap->a_cnp;
+
+ int err;
+
+ DEBUG2G("inode=%ju name=%*s\n",
+ (uintmax_t)VTOI(vp), (int)cnp->cn_namelen, cnp->cn_nameptr);
+
+ if (fuse_isdeadfs(vp)) {
+ return ENXIO;
+ }
+ if (vnode_isdir(vp)) {
+ return EPERM;
+ }
+ cache_purge(vp);
+
+ err = fuse_internal_remove(dvp, vp, cnp, FUSE_UNLINK);
+
+ if (err == 0) {
+ fuse_internal_vnode_disappear(vp);
+ fuse_invalidate_attr(dvp);
+ }
+ return err;
+}
+
+/*
+ struct vnop_rename_args {
+ struct vnode *a_fdvp;
+ struct vnode *a_fvp;
+ struct componentname *a_fcnp;
+ struct vnode *a_tdvp;
+ struct vnode *a_tvp;
+ struct componentname *a_tcnp;
+ };
+*/
+static int
+fuse_vnop_rename(struct vop_rename_args *ap)
+{
+ struct vnode *fdvp = ap->a_fdvp;
+ struct vnode *fvp = ap->a_fvp;
+ struct componentname *fcnp = ap->a_fcnp;
+ struct vnode *tdvp = ap->a_tdvp;
+ struct vnode *tvp = ap->a_tvp;
+ struct componentname *tcnp = ap->a_tcnp;
+ struct fuse_data *data;
+
+ int err = 0;
+
+ DEBUG2G("from: inode=%ju name=%*s -> to: inode=%ju name=%*s\n",
+ (uintmax_t)VTOI(fvp), (int)fcnp->cn_namelen, fcnp->cn_nameptr,
+ (uintmax_t)(tvp == NULL ? -1 : VTOI(tvp)),
+ (int)tcnp->cn_namelen, tcnp->cn_nameptr);
+
+ if (fuse_isdeadfs(fdvp)) {
+ return ENXIO;
+ }
+ if (fvp->v_mount != tdvp->v_mount ||
+ (tvp && fvp->v_mount != tvp->v_mount)) {
+ DEBUG("cross-device rename: %s -> %s\n",
+ fcnp->cn_nameptr, (tcnp != NULL ? tcnp->cn_nameptr : "(NULL)"));
+ err = EXDEV;
+ goto out;
+ }
+ cache_purge(fvp);
+
+ /*
+ * FUSE library is expected to check if target directory is not
+ * under the source directory in the file system tree.
+ * Linux performs this check at VFS level.
+ */
+ data = fuse_get_mpdata(vnode_mount(tdvp));
+ sx_xlock(&data->rename_lock);
+ err = fuse_internal_rename(fdvp, fcnp, tdvp, tcnp);
+ if (err == 0) {
+ fuse_invalidate_attr(fdvp);
+ if (tdvp != fdvp) {
+ fuse_vnode_setparent(fvp, tdvp);
+ fuse_invalidate_attr(tdvp);
+ }
+ if (tvp != NULL)
+ fuse_vnode_setparent(tvp, NULL);
+ }
+ sx_unlock(&data->rename_lock);
+
+ if (tvp != NULL && tvp != fvp) {
+ cache_purge(tvp);
+ }
+ if (vnode_isdir(fvp)) {
+ if ((tvp != NULL) && vnode_isdir(tvp)) {
+ cache_purge(tdvp);
+ }
+ cache_purge(fdvp);
+ }
+out:
+ if (tdvp == tvp) {
+ vrele(tdvp);
+ } else {
+ vput(tdvp);
+ }
+ if (tvp != NULL) {
+ vput(tvp);
+ }
+ vrele(fdvp);
+ vrele(fvp);
+
+ return err;
+}
+
+/*
+ struct vnop_rmdir_args {
+ struct vnode *a_dvp;
+ struct vnode *a_vp;
+ struct componentname *a_cnp;
+ } *ap;
+*/
+static int
+fuse_vnop_rmdir(struct vop_rmdir_args *ap)
+{
+ struct vnode *dvp = ap->a_dvp;
+ struct vnode *vp = ap->a_vp;
+
+ int err;
+
+ DEBUG2G("inode=%ju\n", (uintmax_t)VTOI(vp));
+
+ if (fuse_isdeadfs(vp)) {
+ return ENXIO;
+ }
+ if (VTOFUD(vp) == VTOFUD(dvp)) {
+ return EINVAL;
+ }
+ err = fuse_internal_remove(dvp, vp, ap->a_cnp, FUSE_RMDIR);
+
+ if (err == 0) {
+ fuse_internal_vnode_disappear(vp);
+ fuse_invalidate_attr(dvp);
+ }
+ return err;
+}
+
+/*
+ struct vnop_setattr_args {
+ struct vnode *a_vp;
+ struct vattr *a_vap;
+ struct ucred *a_cred;
+ struct thread *a_td;
+ };
+*/
+static int
+fuse_vnop_setattr(struct vop_setattr_args *ap)
+{
+ struct vnode *vp = ap->a_vp;
+ struct vattr *vap = ap->a_vap;
+ struct ucred *cred = ap->a_cred;
+ struct thread *td = curthread;
+
+ struct fuse_dispatcher fdi;
+ struct fuse_setattr_in *fsai;
+ struct fuse_access_param facp;
+
+ int err = 0;
+ enum vtype vtyp;
+ int sizechanged = 0;
+ uint64_t newsize = 0;
+
+ DEBUG2G("inode=%ju\n", (uintmax_t)VTOI(vp));
+
+ if (fuse_isdeadfs(vp)) {
+ return ENXIO;
+ }
+ fdisp_init(&fdi, sizeof(*fsai));
+ fdisp_make_vp(&fdi, FUSE_SETATTR, vp, td, cred);
+ fsai = fdi.indata;
+ fsai->valid = 0;
+
+ bzero(&facp, sizeof(facp));
+
+ facp.xuid = vap->va_uid;
+ facp.xgid = vap->va_gid;
+
+ if (vap->va_uid != (uid_t)VNOVAL) {
+ facp.facc_flags |= FACCESS_CHOWN;
+ fsai->uid = vap->va_uid;
+ fsai->valid |= FATTR_UID;
+ }
+ if (vap->va_gid != (gid_t)VNOVAL) {
+ facp.facc_flags |= FACCESS_CHOWN;
+ fsai->gid = vap->va_gid;
+ fsai->valid |= FATTR_GID;
+ }
+ if (vap->va_size != VNOVAL) {
+
+ struct fuse_filehandle *fufh = NULL;
+
+ /*Truncate to a new value. */
+ fsai->size = vap->va_size;
+ sizechanged = 1;
+ newsize = vap->va_size;
+ fsai->valid |= FATTR_SIZE;
+
+ fuse_filehandle_getrw(vp, FUFH_WRONLY, &fufh);
+ if (fufh) {
+ fsai->fh = fufh->fh_id;
+ fsai->valid |= FATTR_FH;
+ }
+ }
+ if (vap->va_atime.tv_sec != VNOVAL) {
+ fsai->atime = vap->va_atime.tv_sec;
+ fsai->atimensec = vap->va_atime.tv_nsec;
+ fsai->valid |= FATTR_ATIME;
+ }
+ if (vap->va_mtime.tv_sec != VNOVAL) {
+ fsai->mtime = vap->va_mtime.tv_sec;
+ fsai->mtimensec = vap->va_mtime.tv_nsec;
+ fsai->valid |= FATTR_MTIME;
+ }
+ if (vap->va_mode != (mode_t)VNOVAL) {
+ fsai->mode = vap->va_mode & ALLPERMS;
+ fsai->valid |= FATTR_MODE;
+ }
+ if (!fsai->valid) {
+ goto out;
+ }
+ vtyp = vnode_vtype(vp);
+
+ if (fsai->valid & FATTR_SIZE && vtyp == VDIR) {
+ err = EISDIR;
+ goto out;
+ }
+ if (vfs_isrdonly(vnode_mount(vp)) && (fsai->valid & ~FATTR_SIZE || vtyp == VREG)) {
+ err = EROFS;
+ goto out;
+ }
+ if (fsai->valid & ~FATTR_SIZE) {
+ /*err = fuse_internal_access(vp, VADMIN, context, &facp); */
+ /*XXX */
+ err = 0;
+ }
+ facp.facc_flags &= ~FACCESS_XQUERIES;
+
+ if (err && !(fsai->valid & ~(FATTR_ATIME | FATTR_MTIME)) &&
+ vap->va_vaflags & VA_UTIMES_NULL) {
+ err = fuse_internal_access(vp, VWRITE, &facp, td, cred);
+ }
+ if (err) {
+ fuse_invalidate_attr(vp);
+ goto out;
+ }
+ if ((err = fdisp_wait_answ(&fdi))) {
+ fuse_invalidate_attr(vp);
+ goto out;
+ }
+ vtyp = IFTOVT(((struct fuse_attr_out *)fdi.answ)->attr.mode);
+
+ if (vnode_vtype(vp) != vtyp) {
+ if (vnode_vtype(vp) == VNON && vtyp != VNON) {
+ debug_printf("FUSE: Dang! vnode_vtype is VNON and vtype isn't.\n");
+ } else {
+ /*
+ * STALE vnode, ditch
+ *
+ * The vnode has changed its type "behind our back". There's
+ * nothing really we can do, so let us just force an internal
+ * revocation and tell the caller to try again, if interested.
+ */
+ fuse_internal_vnode_disappear(vp);
+ err = EAGAIN;
+ }
+ }
+ if (!err && !sizechanged) {
+ cache_attrs(vp, (struct fuse_attr_out *)fdi.answ);
+ }
+out:
+ fdisp_destroy(&fdi);
+ if (!err && sizechanged) {
+ fuse_invalidate_attr(vp);
+ fuse_vnode_setsize(vp, cred, newsize);
+ VTOFUD(vp)->flag &= ~FN_SIZECHANGE;
+ }
+ return err;
+}
+
+/*
+ struct vnop_strategy_args {
+ struct vnode *a_vp;
+ struct buf *a_bp;
+ };
+*/
+static int
+fuse_vnop_strategy(struct vop_strategy_args *ap)
+{
+ struct vnode *vp = ap->a_vp;
+ struct buf *bp = ap->a_bp;
+
+ fuse_trace_printf_vnop();
+
+ if (!vp || fuse_isdeadfs(vp)) {
+ bp->b_ioflags |= BIO_ERROR;
+ bp->b_error = ENXIO;
+ bufdone(bp);
+ return ENXIO;
+ }
+ if (bp->b_iocmd == BIO_WRITE)
+ fuse_vnode_refreshsize(vp, NOCRED);
+
+ (void)fuse_io_strategy(vp, bp);
+
+ /*
+ * This is a dangerous function. If returns error, that might mean a
+ * panic. We prefer pretty much anything over being forced to panic
+ * by a malicious daemon (a demon?). So we just return 0 anyway. You
+ * should never mind this: this function has its own error
+ * propagation mechanism via the argument buffer, so
+ * not-that-melodramatic residents of the call chain still will be
+ * able to know what to do.
+ */
+ return 0;
+}
+
+
+/*
+ struct vnop_symlink_args {
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ struct vattr *a_vap;
+ char *a_target;
+ };
+*/
+static int
+fuse_vnop_symlink(struct vop_symlink_args *ap)
+{
+ struct vnode *dvp = ap->a_dvp;
+ struct vnode **vpp = ap->a_vpp;
+ struct componentname *cnp = ap->a_cnp;
+ char *target = ap->a_target;
+
+ struct fuse_dispatcher fdi;
+
+ int err;
+ size_t len;
+
+ DEBUG2G("inode=%ju name=%*s\n",
+ (uintmax_t)VTOI(dvp), (int)cnp->cn_namelen, cnp->cn_nameptr);
+
+ if (fuse_isdeadfs(dvp)) {
+ return ENXIO;
+ }
+ /*
+ * Unlike the other creator type calls, here we have to create a message
+ * where the name of the new entry comes first, and the data describing
+ * the entry comes second.
+ * Hence we can't rely on our handy fuse_internal_newentry() routine,
+ * but put together the message manually and just call the core part.
+ */
+
+ len = strlen(target) + 1;
+ fdisp_init(&fdi, len + cnp->cn_namelen + 1);
+ fdisp_make_vp(&fdi, FUSE_SYMLINK, dvp, curthread, NULL);
+
+ memcpy(fdi.indata, cnp->cn_nameptr, cnp->cn_namelen);
+ ((char *)fdi.indata)[cnp->cn_namelen] = '\0';
+ memcpy((char *)fdi.indata + cnp->cn_namelen + 1, target, len);
+
+ err = fuse_internal_newentry_core(dvp, vpp, cnp, VLNK, &fdi);
+ fdisp_destroy(&fdi);
+
+ if (err == 0) {
+ fuse_invalidate_attr(dvp);
+ }
+ return err;
+}
+
+/*
+ struct vnop_write_args {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ };
+*/
+static int
+fuse_vnop_write(struct vop_write_args *ap)
+{
+ struct vnode *vp = ap->a_vp;
+ struct uio *uio = ap->a_uio;
+ int ioflag = ap->a_ioflag;
+ struct ucred *cred = ap->a_cred;
+
+ fuse_trace_printf_vnop();
+
+ if (fuse_isdeadfs(vp)) {
+ return ENXIO;
+ }
+ fuse_vnode_refreshsize(vp, cred);
+
+ return fuse_io_dispatch(vp, uio, ioflag, cred);
+}
+
+/*
+ struct vnop_getpages_args {
+ struct vnode *a_vp;
+ vm_page_t *a_m;
+ int a_count;
+ int a_reqpage;
+ vm_ooffset_t a_offset;
+ };
+*/
+static int
+fuse_vnop_getpages(struct vop_getpages_args *ap)
+{
+ int i, error, nextoff, size, toff, count, npages;
+ struct uio uio;
+ struct iovec iov;
+ vm_offset_t kva;
+ struct buf *bp;
+ struct vnode *vp;
+ struct thread *td;
+ struct ucred *cred;
+ vm_page_t *pages;
+
+ DEBUG2G("heh\n");
+
+ vp = ap->a_vp;
+ KASSERT(vp->v_object, ("objectless vp passed to getpages"));
+ td = curthread; /* XXX */
+ cred = curthread->td_ucred; /* XXX */
+ pages = ap->a_m;
+ count = ap->a_count;
+
+ if (!fsess_opt_mmap(vnode_mount(vp))) {
+ DEBUG("called on non-cacheable vnode??\n");
+ return (VM_PAGER_ERROR);
+ }
+ npages = btoc(count);
+
+ /*
+ * If the requested page is partially valid, just return it and
+ * allow the pager to zero-out the blanks. Partially valid pages
+ * can only occur at the file EOF.
+ */
+
+ VM_OBJECT_LOCK(vp->v_object);
+ fuse_vm_page_lock_queues();
+ if (pages[ap->a_reqpage]->valid != 0) {
+ for (i = 0; i < npages; ++i) {
+ if (i != ap->a_reqpage) {
+ fuse_vm_page_lock(pages[i]);
+ vm_page_free(pages[i]);
+ fuse_vm_page_unlock(pages[i]);
+ }
+ }
+ fuse_vm_page_unlock_queues();
+ VM_OBJECT_UNLOCK(vp->v_object);
+ return 0;
+ }
+ fuse_vm_page_unlock_queues();
+ VM_OBJECT_UNLOCK(vp->v_object);
+
+ /*
+ * We use only the kva address for the buffer, but this is extremely
+ * convienient and fast.
+ */
+ bp = getpbuf(&fuse_pbuf_freecnt);
+
+ kva = (vm_offset_t)bp->b_data;
+ pmap_qenter(kva, pages, npages);
+ PCPU_INC(cnt.v_vnodein);
+ PCPU_ADD(cnt.v_vnodepgsin, npages);
+
+ iov.iov_base = (caddr_t)kva;
+ iov.iov_len = count;
+ uio.uio_iov = &iov;
+ uio.uio_iovcnt = 1;
+ uio.uio_offset = IDX_TO_OFF(pages[0]->pindex);
+ uio.uio_resid = count;
+ uio.uio_segflg = UIO_SYSSPACE;
+ uio.uio_rw = UIO_READ;
+ uio.uio_td = td;
+
+ error = fuse_io_dispatch(vp, &uio, IO_DIRECT, cred);
+ pmap_qremove(kva, npages);
+
+ relpbuf(bp, &fuse_pbuf_freecnt);
+
+ if (error && (uio.uio_resid == count)) {
+ DEBUG("error %d\n", error);
+ VM_OBJECT_LOCK(vp->v_object);
+ fuse_vm_page_lock_queues();
+ for (i = 0; i < npages; ++i) {
+ if (i != ap->a_reqpage) {
+ fuse_vm_page_lock(pages[i]);
+ vm_page_free(pages[i]);
+ fuse_vm_page_unlock(pages[i]);
+ }
+ }
+ fuse_vm_page_unlock_queues();
+ VM_OBJECT_UNLOCK(vp->v_object);
+ return VM_PAGER_ERROR;
+ }
+ /*
+ * Calculate the number of bytes read and validate only that number
+ * of bytes. Note that due to pending writes, size may be 0. This
+ * does not mean that the remaining data is invalid!
+ */
+
+ size = count - uio.uio_resid;
+ VM_OBJECT_LOCK(vp->v_object);
+ fuse_vm_page_lock_queues();
+ for (i = 0, toff = 0; i < npages; i++, toff = nextoff) {
+ vm_page_t m;
+
+ nextoff = toff + PAGE_SIZE;
+ m = pages[i];
+
+ if (nextoff <= size) {
+ /*
+ * Read operation filled an entire page
+ */
+ m->valid = VM_PAGE_BITS_ALL;
+ KASSERT(m->dirty == 0,
+ ("fuse_getpages: page %p is dirty", m));
+ } else if (size > toff) {
+ /*
+ * Read operation filled a partial page.
+ */
+ m->valid = 0;
+ vm_page_set_valid_range(m, 0, size - toff);
+ KASSERT(m->dirty == 0,
+ ("fuse_getpages: page %p is dirty", m));
+ } else {
+ /*
+ * Read operation was short. If no error occured
+ * we may have hit a zero-fill section. We simply
+ * leave valid set to 0.
+ */
+ ;
+ }
+ if (i != ap->a_reqpage) {
+ /*
+ * Whether or not to leave the page activated is up in
+ * the air, but we should put the page on a page queue
+ * somewhere (it already is in the object). Result:
+ * It appears that emperical results show that
+ * deactivating pages is best.
+ */
+
+ /*
+ * Just in case someone was asking for this page we
+ * now tell them that it is ok to use.
+ */
+ if (!error) {
+ if (m->oflags & VPO_WANTED) {
+ fuse_vm_page_lock(m);
+ vm_page_activate(m);
+ fuse_vm_page_unlock(m);
+ } else {
+ fuse_vm_page_lock(m);
+ vm_page_deactivate(m);
+ fuse_vm_page_unlock(m);
+ }
+ vm_page_wakeup(m);
+ } else {
+ fuse_vm_page_lock(m);
+ vm_page_free(m);
+ fuse_vm_page_unlock(m);
+ }
+ }
+ }
+ fuse_vm_page_unlock_queues();
+ VM_OBJECT_UNLOCK(vp->v_object);
+ return 0;
+}
+
+/*
+ struct vnop_putpages_args {
+ struct vnode *a_vp;
+ vm_page_t *a_m;
+ int a_count;
+ int a_sync;
+ int *a_rtvals;
+ vm_ooffset_t a_offset;
+ };
+*/
+static int
+fuse_vnop_putpages(struct vop_putpages_args *ap)
+{
+ struct uio uio;
+ struct iovec iov;
+ vm_offset_t kva;
+ struct buf *bp;
+ int i, error, npages, count;
+ off_t offset;
+ int *rtvals;
+ struct vnode *vp;
+ struct thread *td;
+ struct ucred *cred;
+ vm_page_t *pages;
+ vm_ooffset_t fsize;
+
+ DEBUG2G("heh\n");
+
+ vp = ap->a_vp;
+ KASSERT(vp->v_object, ("objectless vp passed to putpages"));
+ fsize = vp->v_object->un_pager.vnp.vnp_size;
+ td = curthread; /* XXX */
+ cred = curthread->td_ucred; /* XXX */
+ pages = ap->a_m;
+ count = ap->a_count;
+ rtvals = ap->a_rtvals;
+ npages = btoc(count);
+ offset = IDX_TO_OFF(pages[0]->pindex);
+
+ if (!fsess_opt_mmap(vnode_mount(vp))) {
+ DEBUG("called on non-cacheable vnode??\n");
+ }
+ for (i = 0; i < npages; i++)
+ rtvals[i] = VM_PAGER_AGAIN;
+
+ /*
+ * When putting pages, do not extend file past EOF.
+ */
+
+ if (offset + count > fsize) {
+ count = fsize - offset;
+ if (count < 0)
+ count = 0;
+ }
+ /*
+ * We use only the kva address for the buffer, but this is extremely
+ * convienient and fast.
+ */
+ bp = getpbuf(&fuse_pbuf_freecnt);
+
+ kva = (vm_offset_t)bp->b_data;
+ pmap_qenter(kva, pages, npages);
+ PCPU_INC(cnt.v_vnodeout);
+ PCPU_ADD(cnt.v_vnodepgsout, count);
+
+ iov.iov_base = (caddr_t)kva;
+ iov.iov_len = count;
+ uio.uio_iov = &iov;
+ uio.uio_iovcnt = 1;
+ uio.uio_offset = offset;
+ uio.uio_resid = count;
+ uio.uio_segflg = UIO_SYSSPACE;
+ uio.uio_rw = UIO_WRITE;
+ uio.uio_td = td;
+
+ error = fuse_io_dispatch(vp, &uio, IO_DIRECT, cred);
+
+ pmap_qremove(kva, npages);
+ relpbuf(bp, &fuse_pbuf_freecnt);
+
+ if (!error) {
+ int nwritten = round_page(count - uio.uio_resid) / PAGE_SIZE;
+
+ for (i = 0; i < nwritten; i++) {
+ rtvals[i] = VM_PAGER_OK;
+ VM_OBJECT_LOCK(pages[i]->object);
+ vm_page_undirty(pages[i]);
+ VM_OBJECT_UNLOCK(pages[i]->object);
+ }
+ }
+ return rtvals[0];
+}
+
+/*
+ struct vnop_print_args {
+ struct vnode *a_vp;
+ };
+*/
+static int
+fuse_vnop_print(struct vop_print_args *ap)
+{
+ struct fuse_vnode_data *fvdat = VTOFUD(ap->a_vp);
+
+ printf("nodeid: %ju, parent nodeid: %ju, nlookup: %ju, flag: %#x\n",
+ (uintmax_t)VTOILLU(ap->a_vp), (uintmax_t)fvdat->parent_nid,
+ (uintmax_t)fvdat->nlookup,
+ fvdat->flag);
+
+ return 0;
+}
OpenPOWER on IntegriCloud