summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorhselasky <hselasky@FreeBSD.org>2014-05-23 08:46:28 +0000
committerhselasky <hselasky@FreeBSD.org>2014-05-23 08:46:28 +0000
commit672c28ec684b6ca658c660c9ef90db259d6ae6fb (patch)
treea19650860d3b0bdcee196924c8202be65b2b24b1
parenta5bffeb3e9966192d80e808b770166ce4cd98bbf (diff)
downloadFreeBSD-src-672c28ec684b6ca658c660c9ef90db259d6ae6fb.zip
FreeBSD-src-672c28ec684b6ca658c660c9ef90db259d6ae6fb.tar.gz
Initial import of character device in userspace support for FreeBSD.
The CUSE library is a wrapper for the devfs kernel functionality which is exposed through /dev/cuse . In order to function the CUSE kernel code must either be enabled in the kernel configuration file or loaded separately as a module. Currently none of the committed items are connected to the default builds, except for installing the needed header files. The CUSE code will be connected to the default world and kernel builds in a follow-up commit. The CUSE module was written by Hans Petter Selasky, somewhat inspired by similar functionality found in FUSE. The CUSE library can be used for many purposes. Currently CUSE is used when running Linux kernel drivers in user-space, which need to create a character device node to communicate with its applications. CUSE has full support for almost all devfs functionality found in the kernel: - kevents - read - write - ioctl - poll - open - close - mmap - private per file handle data Requested by several people. Also see "multimedia/cuse4bsd-kmod" in ports.
-rw-r--r--etc/mtree/BSD.include.dist2
-rw-r--r--include/Makefile1
-rw-r--r--lib/libcuse/Makefile64
-rw-r--r--lib/libcuse/cuse.3393
-rw-r--r--lib/libcuse/cuse.h97
-rw-r--r--lib/libcuse/cuse_lib.c800
-rw-r--r--sys/conf/files1
-rw-r--r--sys/fs/cuse/cuse.c1866
-rw-r--r--sys/fs/cuse/cuse_defs.h86
-rw-r--r--sys/fs/cuse/cuse_ioctl.h88
-rw-r--r--sys/modules/cuse/Makefile32
11 files changed, 3430 insertions, 0 deletions
diff --git a/etc/mtree/BSD.include.dist b/etc/mtree/BSD.include.dist
index 7436eac..bdd628c 100644
--- a/etc/mtree/BSD.include.dist
+++ b/etc/mtree/BSD.include.dist
@@ -162,6 +162,8 @@
..
..
fs
+ cuse
+ ..
devfs
..
fdescfs
diff --git a/include/Makefile b/include/Makefile
index 737575f..4693c2f 100644
--- a/include/Makefile
+++ b/include/Makefile
@@ -45,6 +45,7 @@ LSUBDIRS= cam/ata cam/scsi \
dev/ic dev/iicbus ${_dev_ieee488} dev/io dev/lmc dev/mfi dev/nvme \
dev/ofw dev/pbio dev/pci ${_dev_powermac_nvram} dev/ppbus dev/smbus \
dev/speaker dev/usb dev/utopia dev/vkbd dev/wi \
+ fs/cuse \
fs/devfs fs/fdescfs fs/msdosfs fs/nandfs fs/nfs fs/nullfs \
fs/procfs fs/smbfs fs/udf fs/unionfs \
geom/cache geom/concat geom/eli geom/gate geom/journal geom/label \
diff --git a/lib/libcuse/Makefile b/lib/libcuse/Makefile
new file mode 100644
index 0000000..db28c22
--- /dev/null
+++ b/lib/libcuse/Makefile
@@ -0,0 +1,64 @@
+# $FreeBSD$
+#
+# Copyright (c) 2010 Hans Petter Selasky. 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 THE 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 THE 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.
+#
+
+LIB= cuse
+SHLIB_MAJOR= 1
+SHLIB_MINOR= 0
+SRCS= cuse_lib.c
+INCS= cuse.h
+MAN= cuse.3
+PTHREAD_LIBS?= -lpthread
+CFLAGS+= -D_GNU_SOURCE
+.if defined(HAVE_DEBUG)
+CFLAGS+= -g
+CFLAGS+= -DHAVE_DEBUG
+.endif
+LDADD+= ${PTHREAD_LIBS}
+
+MLINKS=
+MLINKS+= cuse.3 cuse_alloc_unit_number.3
+MLINKS+= cuse.3 cuse_copy_in.3
+MLINKS+= cuse.3 cuse_copy_out.3
+MLINKS+= cuse.3 cuse_dev_create.3
+MLINKS+= cuse.3 cuse_dev_destroy.3
+MLINKS+= cuse.3 cuse_dev_get_current.3
+MLINKS+= cuse.3 cuse_dev_get_per_file_handle.3
+MLINKS+= cuse.3 cuse_dev_get_priv0.3
+MLINKS+= cuse.3 cuse_dev_get_priv1.3
+MLINKS+= cuse.3 cuse_dev_set_per_file_handle.3
+MLINKS+= cuse.3 cuse_free_unit_number.3
+MLINKS+= cuse.3 cuse_got_peer_signal.3
+MLINKS+= cuse.3 cuse_init.3
+MLINKS+= cuse.3 cuse_poll_wakeup.3
+MLINKS+= cuse.3 cuse_set_local.3
+MLINKS+= cuse.3 cuse_get_local.3
+MLINKS+= cuse.3 cuse_uninit.3
+MLINKS+= cuse.3 cuse_vmalloc.3
+MLINKS+= cuse.3 cuse_is_vmalloc_addr.3
+MLINKS+= cuse.3 cuse_vmfree.3
+MLINKS+= cuse.3 cuse_wait_and_process.3
+
+.include <bsd.lib.mk>
diff --git a/lib/libcuse/cuse.3 b/lib/libcuse/cuse.3
new file mode 100644
index 0000000..635af2c
--- /dev/null
+++ b/lib/libcuse/cuse.3
@@ -0,0 +1,393 @@
+.\" $FreeBSD$
+.\"
+.\" Copyright (c) 2010-2013 Hans Petter Selasky
+.\"
+.\" 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 THE 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 THE 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.
+.\"
+.Dd May 23, 2014
+.Dt CUSE 3
+.Os
+.Sh NAME
+.Nm libcuse
+.
+.Nd "Userland character device library"
+.
+.
+.Sh LIBRARY
+.
+.
+Userland character device library (libcuse -lcuse)
+.
+.
+.Sh SYNOPSIS
+.
+.Pp
+To load the required kernel module at boot time, place the following line in
+.Xr loader.conf 5 :
+.Bd -literal -offset indent
+cuse_load="YES"
+.Ed
+.
+.Pp
+.
+.In cuse.h
+.
+.
+.Sh DESCRIPTION
+The
+.Nm
+library contains functions to create a character device in userspace. The
+.Nm
+library is thread safe.
+.
+.
+.Sh LIBRARY INITIALISATION / DEINITIALISATION
+.
+.Pp
+.
+.Ft "int"
+.Fn "cuse_init" "void"
+This function initialises
+.Nm .
+Must be called at the beginning of the program.
+This function returns 0 on success or a negative value on failure.
+See CUSE_ERR_XXX for known error codes.
+If the cuse kernel module is not loaded, CUSE_ERR_NOT_LOADED is
+returned.
+.
+.Pp
+.
+.Ft "int"
+.Fn "cuse_uninit" "void"
+Deinitialise
+.Nm .
+Can be called at the end of the application.
+This function returns 0 on success or a negative value on failure.
+See CUSE_ERR_XXX for known error codes.
+.
+.
+.Sh UNIT MANAGEMENT
+.
+.Ft "int"
+.Fn "cuse_alloc_unit_number" "int *"
+This function stores a uniq system unit number at the pointed
+integer loation.
+This function returns 0 on success or a negative value on failure.
+See CUSE_ERR_XXX for known error codes.
+.
+.Pp
+.
+.Ft "int"
+.Fn "cuse_alloc_unit_number_by_id" "int *" "int id"
+This function stores a uniq system unit number at the pointed
+integer loation.
+The returned unit number is uniq within the given ID.
+Valid ID values are defined by the cuse include file.
+See the CUSE_ID_XXX() macros for more information.
+This function returns 0 on success or a negative value on failure.
+See CUSE_ERR_XXX for known error codes.
+.
+.Pp
+.
+.Ft "int"
+.Fn "cuse_free_unit_number" "int"
+This function frees the given allocated system unit number.
+This function returns 0 on success or a negative value on failure.
+See CUSE_ERR_XXX for known error codes.
+.
+.Pp
+.
+.Ft "int"
+.Fn "cuse_free_unit_number_by_id" "int unit" "int id"
+This function frees the given allocated system unit number belonging
+to the given ID.
+If both the unit and id argument is -1, all allocated units will be freed.
+This function returns 0 on success or a negative value on failure.
+See CUSE_ERR_XXX for known error codes.
+.
+.
+.Sh LIBRARY USAGE
+.
+.
+.Ft "void *"
+.Fn "cuse_vmalloc" "int size"
+This function allocates
+.Ar size
+bytes of memory. Only memory allocated by this function can be memory
+mapped by mmap(). This function returns a valid data pointer on success or
+NULL on failure.
+.
+.Pp
+.
+.Ft "int"
+.Fn "cuse_is_vmalloc_addr" "void *"
+This function returns non-zero if the passed pointer points to a valid
+and non-freed allocation, as returned by "cuse_vmalloc()".
+Else this function returns zero.
+.
+.Pp
+.
+.Ft "void"
+.Fn "cuse_vmfree" "void *"
+This function frees memory allocated by cuse_vmalloc(). Note that the
+cuse library will internally not free the memory until the
+cuse_uninit() function is called and that the number of uniq
+allocations is limited.
+.
+.
+.Pp
+.
+.Ft "unsigned long"
+.Fn "cuse_vmoffset" "void *"
+This function returns the mmap offset that the client must use to
+access the allocated memory.
+.
+.Pp
+.
+.Ft "struct cuse_dev *"
+.Fn "cuse_dev_create" "const struct cuse_methods *mtod" "void *priv0" "void *priv1" "uid_t" "gid_t" "int permission" "const char *fmt" "..."
+This function creates a new character device according to the given
+parameters. This function returns a valid cuse_dev structure pointer
+on success or NULL on failure. The device name can only contain a-z,
+A-Z, 0-9, dot, / and underscore characters.
+.
+.Pp
+.
+.Ft "void"
+.Fn "cuse_dev_destroy" "struct cuse_dev *"
+This functions destroys a previously created character device.
+.
+.Pp
+.
+.
+.Ft "void *"
+.Fn "cuse_dev_get_priv0" "struct cuse_dev *"
+,
+.Ft "void *"
+.Fn "cuse_dev_get_priv1" "struct cuse_dev *"
+,
+.Ft "void"
+.Fn "cuse_dev_set_priv0" "struct cuse_dev *" "void *"
+,
+.Ft "void"
+.Fn "cuse_dev_set_priv1" "struct cuse_dev *" "void *"
+These functions are used to set and get the private data of the given
+cuse device.
+.
+.Pp
+.
+.Ft "int"
+.Fn "cuse_wait_and_process" "void"
+This function will block and do event processing. If parallell I/O is
+required multiple threads must be created looping on this
+function.
+This function returns 0 on success or a negative value on failure.
+See CUSE_ERR_XXX for known error codes.
+.
+.Pp
+.
+.Ft "void *"
+.Fn "cuse_dev_get_per_file_handle" "struct cuse_dev *"
+,
+.Ft "void"
+.Fn "cuse_dev_set_per_file_handle" "struct cuse_dev *" "void *"
+These functions are used to set and get the per-file-open specific handle
+and should only be used inside the cuse file operation callbacks.
+.
+.Pp
+.
+.Ft "void"
+.Fn "cuse_set_local" "int"
+This function instructs cuse_copy_out() and cuse_copy_in() that the
+user pointer is local, if the argument passed to it is non-zero.
+Else the user pointer is assumed to be at the peer application.
+This function should only be used inside the cuse file operation callbacks.
+The value is reset to zero when the given file operation returns, and
+does not affect any other file operation callbacks.
+.
+.Pp
+.
+.Ft "int"
+.Fn "cuse_get_local" "void"
+Return current local state. See "cuse_set_local" function.
+.
+.Pp
+.
+.Ft "int"
+.Fn "cuse_copy_out" "const void *src" "void *peer_dst" "int len"
+,
+.Ft "int"
+.Fn "cuse_copy_in" "const void *peer_src" "void *dst" "int len"
+These functions are used to transfer data between the local
+application and the peer application. These functions must be used
+when operating on the data pointers passed to the cm_read(),
+cm_write() and cm_ioctl() callback functions.
+These functions return 0 on success or a negative value on failure.
+See CUSE_ERR_XXX for known error codes.
+.
+.Pp
+.
+.Ft "int"
+.Fn "cuse_got_peer_signal" "void"
+This function is used to check if a signal has been delivered to the
+peer application and should only be used inside the cuse file
+operation callbacks. This function returns 0 if a signal has been
+delivered to the caller.
+Else it returns a negative value.
+See CUSE_ERR_XXX for known error codes.
+.
+.Pp
+.
+.Ft "struct cuse_dev *"
+.Fn "cuse_dev_get_current" "int *pcmd"
+This function is used to get the current cuse device pointer and the
+currently executing command, by CUSE_CMD_XXX value. The pcmd argument
+is allowed to be NULL. This function should only be used inside the
+cuse file operation callbacks. On success a valid cuse device pointer
+is returned. On failure NULL is returned.
+.
+.Pp
+.
+.Ft "void"
+.Fn "cuse_poll_wakeup" "void"
+This function will wake up any file pollers.
+.
+.Pp
+.
+.Sh LIBRARY LIMITATIONS
+.
+.
+Transfer lengths for read, write, cuse_copy_in and cuse_copy_out
+should not exceed what can fit into a 32-bit signed integer and is
+defined by the CUSE_LENGTH_MAX macro.
+.
+Transfer lengths for ioctls should not exceed what is defined by the
+CUSE_BUFFER_MAX macro.
+.
+.
+.Sh LIBRARY CALLBACK METHODS
+.
+In general fflags are defined by CUSE_FFLAG_XXX and errors are defined by CUSE_ERR_XXX.
+.
+.Bd -literal -offset indent
+enum {
+ CUSE_ERR_NONE
+ CUSE_ERR_BUSY
+ CUSE_ERR_WOULDBLOCK
+ CUSE_ERR_INVALID
+ CUSE_ERR_NO_MEMORY
+ CUSE_ERR_FAULT
+ CUSE_ERR_SIGNAL
+ CUSE_ERR_OTHER
+ CUSE_ERR_NOT_LOADED
+
+ CUSE_POLL_NONE
+ CUSE_POLL_READ
+ CUSE_POLL_WRITE
+ CUSE_POLL_ERROR
+
+ CUSE_FFLAG_NONE
+ CUSE_FFLAG_READ
+ CUSE_FFLAG_WRITE
+ CUSE_FFLAG_NONBLOCK
+
+ CUSE_CMD_NONE
+ CUSE_CMD_OPEN
+ CUSE_CMD_CLOSE
+ CUSE_CMD_READ
+ CUSE_CMD_WRITE
+ CUSE_CMD_IOCTL
+ CUSE_CMD_POLL
+ CUSE_CMD_SIGNAL
+ CUSE_CMD_SYNC
+ CUSE_CMD_MAX
+};
+.Ed
+.
+.Pp
+.
+.Ft "int"
+.Fn "cuse_open_t" "struct cuse_dev *" "int fflags"
+This functions returns a CUSE_ERR_XXX value.
+.
+.Pp
+.
+.Ft "int"
+.Fn "cuse_close_t" "struct cuse_dev *" "int fflags"
+This functions returns a CUSE_ERR_XXX value.
+.
+.Pp
+.
+.Ft "int"
+.Fn "cuse_read_t" "struct cuse_dev *" "int fflags" "void *peer_ptr" "int len"
+This functions returns a CUSE_ERR_XXX value in case of failure or the
+actually transferred length in case of success. cuse_copy_in() and
+cuse_copy_out() must be used to transfer data to and from the
+peer_ptr.
+.
+.Pp
+.
+.Ft "int"
+.Fn "cuse_write_t" "struct cuse_dev *" "int fflags" "const void *peer_ptr" "int len"
+This functions returns a CUSE_ERR_XXX value in case of failure or the
+actually transferred length in case of success. cuse_copy_in() and
+cuse_copy_out() must be used to transfer data to and from the
+peer_ptr.
+.
+.Pp
+.
+.Ft "int"
+.Fn "cuse_ioctl_t" "struct cuse_dev *" "int fflags" "unsigned long cmd" "void *peer_data"
+This functions returns a CUSE_ERR_XXX value in case of failure or zero
+in case of success. cuse_copy_in() and cuse_copy_out() must be used to
+transfer data to and from the peer_data.
+.
+.Pp
+.
+.Ft "int"
+.Fn "cuse_poll_t" "struct cuse_dev *" "int fflags" "int events"
+This functions returns a mask of CUSE_POLL_XXX values in case of
+failure and success. The events argument is also a mask of
+CUSE_POLL_XXX values.
+.
+.Pp
+.
+.Bd -literal -offset indent
+struct cuse_methods {
+ cuse_open_t *cm_open;
+ cuse_close_t *cm_close;
+ cuse_read_t *cm_read;
+ cuse_write_t *cm_write;
+ cuse_ioctl_t *cm_ioctl;
+ cuse_poll_t *cm_poll;
+};
+.Ed
+.
+.
+.Sh SEE ALSO
+.
+.Sh HISTORY
+.
+.Nm
+was written by Hans Petter Selasky .
diff --git a/lib/libcuse/cuse.h b/lib/libcuse/cuse.h
new file mode 100644
index 0000000..d502c5b
--- /dev/null
+++ b/lib/libcuse/cuse.h
@@ -0,0 +1,97 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2014 Hans Petter Selasky. 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 THE 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 THE 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.
+ */
+
+#ifndef _CUSE_H_
+#define _CUSE_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <fs/cuse/cuse_defs.h>
+
+struct cuse_dev;
+
+typedef int (cuse_open_t)(struct cuse_dev *, int fflags);
+typedef int (cuse_close_t)(struct cuse_dev *, int fflags);
+typedef int (cuse_read_t)(struct cuse_dev *, int fflags, void *user_ptr, int len);
+typedef int (cuse_write_t)(struct cuse_dev *, int fflags, const void *user_ptr, int len);
+typedef int (cuse_ioctl_t)(struct cuse_dev *, int fflags, unsigned long cmd, void *user_data);
+typedef int (cuse_poll_t)(struct cuse_dev *, int fflags, int events);
+
+struct cuse_methods {
+ cuse_open_t *cm_open;
+ cuse_close_t *cm_close;
+ cuse_read_t *cm_read;
+ cuse_write_t *cm_write;
+ cuse_ioctl_t *cm_ioctl;
+ cuse_poll_t *cm_poll;
+};
+
+int cuse_init(void);
+int cuse_uninit(void);
+
+void *cuse_vmalloc(int);
+int cuse_is_vmalloc_addr(void *);
+void cuse_vmfree(void *);
+unsigned long cuse_vmoffset(void *ptr);
+
+int cuse_alloc_unit_number_by_id(int *, int);
+int cuse_free_unit_number_by_id(int, int);
+int cuse_alloc_unit_number(int *);
+int cuse_free_unit_number(int);
+
+struct cuse_dev *cuse_dev_create(const struct cuse_methods *, void *, void *, uid_t, gid_t, int, const char *,...);
+void cuse_dev_destroy(struct cuse_dev *);
+
+void *cuse_dev_get_priv0(struct cuse_dev *);
+void *cuse_dev_get_priv1(struct cuse_dev *);
+
+void cuse_dev_set_priv0(struct cuse_dev *, void *);
+void cuse_dev_set_priv1(struct cuse_dev *, void *);
+
+void cuse_set_local(int);
+int cuse_get_local(void);
+
+int cuse_wait_and_process(void);
+
+void cuse_dev_set_per_file_handle(struct cuse_dev *, void *);
+void *cuse_dev_get_per_file_handle(struct cuse_dev *);
+
+int cuse_copy_out(const void *src, void *user_dst, int len);
+int cuse_copy_in(const void *user_src, void *dst, int len);
+int cuse_got_peer_signal(void);
+void cuse_poll_wakeup(void);
+
+struct cuse_dev *cuse_dev_get_current(int *);
+
+extern int cuse_debug_level;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _CUSE_H_ */
diff --git a/lib/libcuse/cuse_lib.c b/lib/libcuse/cuse_lib.c
new file mode 100644
index 0000000..f518e11
--- /dev/null
+++ b/lib/libcuse/cuse_lib.c
@@ -0,0 +1,800 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2010-2012 Hans Petter Selasky. 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 THE 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 THE 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 <stdio.h>
+#include <stdint.h>
+#include <pthread.h>
+#include <signal.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/fcntl.h>
+#include <sys/mman.h>
+#include <sys/param.h>
+
+#include <fs/cuse/cuse_ioctl.h>
+
+#include "cuse.h"
+
+int cuse_debug_level;
+
+#ifdef HAVE_DEBUG
+static const char *cuse_cmd_str(int cmd);
+
+#define DPRINTF(...) do { \
+ if (cuse_debug_level != 0) \
+ printf(__VA_ARGS__); \
+} while (0)
+#else
+#define DPRINTF(...) do { } while (0)
+#endif
+
+struct cuse_vm_allocation {
+ uint8_t *ptr;
+ uint32_t size;
+};
+
+struct cuse_dev_entered {
+ TAILQ_ENTRY(cuse_dev_entered) entry;
+ pthread_t thread;
+ void *per_file_handle;
+ struct cuse_dev *cdev;
+ int cmd;
+ int is_local;
+ int got_signal;
+};
+
+struct cuse_dev {
+ TAILQ_ENTRY(cuse_dev) entry;
+ const struct cuse_methods *mtod;
+ void *priv0;
+ void *priv1;
+};
+
+static TAILQ_HEAD(, cuse_dev) h_cuse;
+static TAILQ_HEAD(, cuse_dev_entered) h_cuse_entered;
+static int f_cuse = -1;
+static pthread_mutex_t m_cuse;
+static struct cuse_vm_allocation a_cuse[CUSE_ALLOC_UNIT_MAX];
+
+static void
+cuse_lock(void)
+{
+ pthread_mutex_lock(&m_cuse);
+}
+
+static void
+cuse_unlock(void)
+{
+ pthread_mutex_unlock(&m_cuse);
+}
+
+int
+cuse_init(void)
+{
+ pthread_mutexattr_t attr;
+
+ f_cuse = open("/dev/cuse", O_RDWR);
+ if (f_cuse < 0) {
+ if (feature_present("cuse") == 0)
+ return (CUSE_ERR_NOT_LOADED);
+ else
+ return (CUSE_ERR_INVALID);
+ }
+ pthread_mutexattr_init(&attr);
+ pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
+ pthread_mutex_init(&m_cuse, &attr);
+
+ TAILQ_INIT(&h_cuse);
+ TAILQ_INIT(&h_cuse_entered);
+
+ return (0);
+}
+
+int
+cuse_uninit(void)
+{
+ int f;
+
+ if (f_cuse < 0)
+ return (CUSE_ERR_INVALID);
+
+ f = f_cuse;
+ f_cuse = -1;
+
+ close(f);
+
+ pthread_mutex_destroy(&m_cuse);
+
+ memset(a_cuse, 0, sizeof(a_cuse));
+
+ return (0);
+}
+
+unsigned long
+cuse_vmoffset(void *_ptr)
+{
+ uint8_t *ptr_min;
+ uint8_t *ptr_max;
+ uint8_t *ptr = _ptr;
+ unsigned long remainder;
+ int n;
+
+ cuse_lock();
+ for (n = 0; n != CUSE_ALLOC_UNIT_MAX; n++) {
+ if (a_cuse[n].ptr == NULL)
+ continue;
+
+ ptr_min = a_cuse[n].ptr;
+ ptr_max = a_cuse[n].ptr + a_cuse[n].size - 1;
+
+ if ((ptr >= ptr_min) && (ptr <= ptr_max)) {
+
+ cuse_unlock();
+
+ remainder = (ptr - ptr_min);
+
+ remainder -= remainder % PAGE_SIZE;
+
+ return ((n * PAGE_SIZE * CUSE_ALLOC_PAGES_MAX) + remainder);
+ }
+ }
+ cuse_unlock();
+
+ return (0x80000000UL); /* failure */
+}
+
+void *
+cuse_vmalloc(int size)
+{
+ struct cuse_alloc_info info;
+ void *ptr;
+ int error;
+ int n;
+
+ if (f_cuse < 0)
+ return (NULL);
+
+ memset(&info, 0, sizeof(info));
+
+ if (size < 1)
+ return (NULL);
+
+ info.page_count = (size + PAGE_SIZE - 1) / PAGE_SIZE;
+
+ cuse_lock();
+ for (n = 0; n != CUSE_ALLOC_UNIT_MAX; n++) {
+
+ if (a_cuse[n].ptr != NULL)
+ continue;
+
+ a_cuse[n].ptr = ((uint8_t *)1); /* reserve */
+ a_cuse[n].size = 0;
+
+ cuse_unlock();
+
+ info.alloc_nr = n;
+
+ error = ioctl(f_cuse, CUSE_IOCTL_ALLOC_MEMORY, &info);
+
+ if (error) {
+
+ cuse_lock();
+
+ a_cuse[n].ptr = NULL;
+
+ if (errno == EBUSY)
+ continue;
+ else
+ break;
+ }
+ ptr = mmap(NULL, info.page_count * PAGE_SIZE,
+ PROT_READ | PROT_WRITE,
+ MAP_SHARED, f_cuse, CUSE_ALLOC_PAGES_MAX *
+ PAGE_SIZE * n);
+
+ if (ptr == MAP_FAILED) {
+
+ error = ioctl(f_cuse, CUSE_IOCTL_FREE_MEMORY, &info);
+
+ if (error) {
+ /* ignore */
+ }
+ cuse_lock();
+
+ a_cuse[n].ptr = NULL;
+
+ break;
+ }
+ cuse_lock();
+ a_cuse[n].ptr = ptr;
+ a_cuse[n].size = size;
+ cuse_unlock();
+
+ return (ptr); /* success */
+ }
+ cuse_unlock();
+ return (NULL); /* failure */
+}
+
+int
+cuse_is_vmalloc_addr(void *ptr)
+{
+ int n;
+
+ if (f_cuse < 0 || ptr == NULL)
+ return (0); /* false */
+
+ cuse_lock();
+ for (n = 0; n != CUSE_ALLOC_UNIT_MAX; n++) {
+ if (a_cuse[n].ptr == ptr)
+ break;
+ }
+ cuse_unlock();
+
+ return (n != CUSE_ALLOC_UNIT_MAX);
+}
+
+void
+cuse_vmfree(void *ptr)
+{
+ struct cuse_alloc_info info;
+ int error;
+ int n;
+
+ if (f_cuse < 0)
+ return;
+
+ memset(&info, 0, sizeof(info));
+
+ cuse_lock();
+ for (n = 0; n != CUSE_ALLOC_UNIT_MAX; n++) {
+ if (a_cuse[n].ptr != ptr)
+ continue;
+
+ cuse_unlock();
+
+ info.alloc_nr = n;
+
+ munmap(ptr, a_cuse[n].size);
+
+ error = ioctl(f_cuse, CUSE_IOCTL_FREE_MEMORY, &info);
+
+ if (error) {
+ /* ignore */
+ }
+ cuse_lock();
+
+ a_cuse[n].ptr = NULL;
+ a_cuse[n].size = 0;
+
+ break;
+ }
+ cuse_unlock();
+}
+
+int
+cuse_alloc_unit_number_by_id(int *pnum, int id)
+{
+ int error;
+
+ if (f_cuse < 0)
+ return (CUSE_ERR_INVALID);
+
+ *pnum = (id & CUSE_ID_MASK);
+
+ error = ioctl(f_cuse, CUSE_IOCTL_ALLOC_UNIT_BY_ID, pnum);
+ if (error)
+ return (CUSE_ERR_NO_MEMORY);
+
+ return (0);
+
+}
+
+int
+cuse_free_unit_number_by_id(int num, int id)
+{
+ int error;
+
+ if (f_cuse < 0)
+ return (CUSE_ERR_INVALID);
+
+ if (num != -1 || id != -1)
+ num = (id & CUSE_ID_MASK) | (num & 0xFF);
+
+ error = ioctl(f_cuse, CUSE_IOCTL_FREE_UNIT_BY_ID, &num);
+ if (error)
+ return (CUSE_ERR_NO_MEMORY);
+
+ return (0);
+}
+
+int
+cuse_alloc_unit_number(int *pnum)
+{
+ int error;
+
+ if (f_cuse < 0)
+ return (CUSE_ERR_INVALID);
+
+ error = ioctl(f_cuse, CUSE_IOCTL_ALLOC_UNIT, pnum);
+ if (error)
+ return (CUSE_ERR_NO_MEMORY);
+
+ return (0);
+}
+
+int
+cuse_free_unit_number(int num)
+{
+ int error;
+
+ if (f_cuse < 0)
+ return (CUSE_ERR_INVALID);
+
+ error = ioctl(f_cuse, CUSE_IOCTL_FREE_UNIT, &num);
+ if (error)
+ return (CUSE_ERR_NO_MEMORY);
+
+ return (0);
+}
+
+struct cuse_dev *
+cuse_dev_create(const struct cuse_methods *mtod, void *priv0, void *priv1,
+ uid_t _uid, gid_t _gid, int _perms, const char *_fmt,...)
+{
+ struct cuse_create_dev info;
+ struct cuse_dev *cdev;
+ va_list args;
+ int error;
+
+ if (f_cuse < 0)
+ return (NULL);
+
+ cdev = malloc(sizeof(*cdev));
+ if (cdev == NULL)
+ return (NULL);
+
+ memset(cdev, 0, sizeof(*cdev));
+
+ cdev->mtod = mtod;
+ cdev->priv0 = priv0;
+ cdev->priv1 = priv1;
+
+ memset(&info, 0, sizeof(info));
+
+ info.dev = cdev;
+ info.user_id = _uid;
+ info.group_id = _gid;
+ info.permissions = _perms;
+
+ va_start(args, _fmt);
+ vsnprintf(info.devname, sizeof(info.devname), _fmt, args);
+ va_end(args);
+
+ error = ioctl(f_cuse, CUSE_IOCTL_CREATE_DEV, &info);
+ if (error) {
+ free(cdev);
+ return (NULL);
+ }
+ cuse_lock();
+ TAILQ_INSERT_TAIL(&h_cuse, cdev, entry);
+ cuse_unlock();
+
+ return (cdev);
+}
+
+
+void
+cuse_dev_destroy(struct cuse_dev *cdev)
+{
+ int error;
+
+ if (f_cuse < 0)
+ return;
+
+ cuse_lock();
+ TAILQ_REMOVE(&h_cuse, cdev, entry);
+ cuse_unlock();
+
+ error = ioctl(f_cuse, CUSE_IOCTL_DESTROY_DEV, cdev);
+ if (error)
+ return;
+
+ free(cdev);
+}
+
+void *
+cuse_dev_get_priv0(struct cuse_dev *cdev)
+{
+ return (cdev->priv0);
+}
+
+void *
+cuse_dev_get_priv1(struct cuse_dev *cdev)
+{
+ return (cdev->priv1);
+}
+
+void
+cuse_dev_set_priv0(struct cuse_dev *cdev, void *priv)
+{
+ cdev->priv0 = priv;
+}
+
+void
+cuse_dev_set_priv1(struct cuse_dev *cdev, void *priv)
+{
+ cdev->priv1 = priv;
+}
+
+int
+cuse_wait_and_process(void)
+{
+ pthread_t curr = pthread_self();
+ struct cuse_dev_entered *pe;
+ struct cuse_dev_entered enter;
+ struct cuse_command info;
+ struct cuse_dev *cdev;
+ int error;
+
+ if (f_cuse < 0)
+ return (CUSE_ERR_INVALID);
+
+ error = ioctl(f_cuse, CUSE_IOCTL_GET_COMMAND, &info);
+ if (error)
+ return (CUSE_ERR_OTHER);
+
+ cdev = info.dev;
+
+ cuse_lock();
+ enter.thread = curr;
+ enter.per_file_handle = (void *)info.per_file_handle;
+ enter.cmd = info.command;
+ enter.is_local = 0;
+ enter.got_signal = 0;
+ enter.cdev = cdev;
+ TAILQ_INSERT_TAIL(&h_cuse_entered, &enter, entry);
+ cuse_unlock();
+
+ DPRINTF("cuse: Command = %d = %s, flags = %d, arg = 0x%08x, ptr = 0x%08x\n",
+ (int)info.command, cuse_cmd_str(info.command), (int)info.fflags,
+ (int)info.argument, (int)info.data_pointer);
+
+ switch (info.command) {
+ case CUSE_CMD_OPEN:
+ if (cdev->mtod->cm_open != NULL)
+ error = (cdev->mtod->cm_open) (cdev, (int)info.fflags);
+ else
+ error = 0;
+ break;
+
+ case CUSE_CMD_CLOSE:
+
+ /* wait for other threads to stop */
+
+ while (1) {
+
+ error = 0;
+
+ cuse_lock();
+ TAILQ_FOREACH(pe, &h_cuse_entered, entry) {
+ if (pe->cdev != cdev)
+ continue;
+ if (pe->thread == curr)
+ continue;
+ if (pe->per_file_handle !=
+ enter.per_file_handle)
+ continue;
+ pe->got_signal = 1;
+ pthread_kill(pe->thread, SIGHUP);
+ error = CUSE_ERR_BUSY;
+ }
+ cuse_unlock();
+
+ if (error == 0)
+ break;
+ else
+ usleep(10000);
+ }
+
+ if (cdev->mtod->cm_close != NULL)
+ error = (cdev->mtod->cm_close) (cdev, (int)info.fflags);
+ else
+ error = 0;
+ break;
+
+ case CUSE_CMD_READ:
+ if (cdev->mtod->cm_read != NULL) {
+ error = (cdev->mtod->cm_read) (cdev, (int)info.fflags,
+ (void *)info.data_pointer, (int)info.argument);
+ } else {
+ error = CUSE_ERR_INVALID;
+ }
+ break;
+
+ case CUSE_CMD_WRITE:
+ if (cdev->mtod->cm_write != NULL) {
+ error = (cdev->mtod->cm_write) (cdev, (int)info.fflags,
+ (void *)info.data_pointer, (int)info.argument);
+ } else {
+ error = CUSE_ERR_INVALID;
+ }
+ break;
+
+ case CUSE_CMD_IOCTL:
+ if (cdev->mtod->cm_ioctl != NULL) {
+ error = (cdev->mtod->cm_ioctl) (cdev, (int)info.fflags,
+ (unsigned int)info.argument, (void *)info.data_pointer);
+ } else {
+ error = CUSE_ERR_INVALID;
+ }
+ break;
+
+ case CUSE_CMD_POLL:
+ if (cdev->mtod->cm_poll != NULL) {
+ error = (cdev->mtod->cm_poll) (cdev, (int)info.fflags,
+ (int)info.argument);
+ } else {
+ error = CUSE_POLL_ERROR;
+ }
+ break;
+
+ case CUSE_CMD_SIGNAL:
+ cuse_lock();
+ TAILQ_FOREACH(pe, &h_cuse_entered, entry) {
+ if (pe->cdev != cdev)
+ continue;
+ if (pe->thread == curr)
+ continue;
+ if (pe->per_file_handle !=
+ enter.per_file_handle)
+ continue;
+ pe->got_signal = 1;
+ pthread_kill(pe->thread, SIGHUP);
+ }
+ cuse_unlock();
+ break;
+
+ default:
+ error = CUSE_ERR_INVALID;
+ break;
+ }
+
+ DPRINTF("cuse: Command error = %d for %s\n",
+ error, cuse_cmd_str(info.command));
+
+ cuse_lock();
+ TAILQ_REMOVE(&h_cuse_entered, &enter, entry);
+ cuse_unlock();
+
+ /* we ignore any sync command failures */
+ ioctl(f_cuse, CUSE_IOCTL_SYNC_COMMAND, &error);
+
+ return (0);
+}
+
+static struct cuse_dev_entered *
+cuse_dev_get_entered(void)
+{
+ struct cuse_dev_entered *pe;
+ pthread_t curr = pthread_self();
+
+ cuse_lock();
+ TAILQ_FOREACH(pe, &h_cuse_entered, entry) {
+ if (pe->thread == curr)
+ break;
+ }
+ cuse_unlock();
+ return (pe);
+}
+
+void
+cuse_dev_set_per_file_handle(struct cuse_dev *cdev, void *handle)
+{
+ struct cuse_dev_entered *pe;
+
+ pe = cuse_dev_get_entered();
+ if (pe == NULL || pe->cdev != cdev)
+ return;
+
+ pe->per_file_handle = handle;
+ ioctl(f_cuse, CUSE_IOCTL_SET_PFH, &handle);
+}
+
+void *
+cuse_dev_get_per_file_handle(struct cuse_dev *cdev)
+{
+ struct cuse_dev_entered *pe;
+
+ pe = cuse_dev_get_entered();
+ if (pe == NULL || pe->cdev != cdev)
+ return (NULL);
+
+ return (pe->per_file_handle);
+}
+
+void
+cuse_set_local(int val)
+{
+ struct cuse_dev_entered *pe;
+
+ pe = cuse_dev_get_entered();
+ if (pe == NULL)
+ return;
+
+ pe->is_local = val;
+}
+
+#ifdef HAVE_DEBUG
+static const char *
+cuse_cmd_str(int cmd)
+{
+ static const char *str[CUSE_CMD_MAX] = {
+ [CUSE_CMD_NONE] = "none",
+ [CUSE_CMD_OPEN] = "open",
+ [CUSE_CMD_CLOSE] = "close",
+ [CUSE_CMD_READ] = "read",
+ [CUSE_CMD_WRITE] = "write",
+ [CUSE_CMD_IOCTL] = "ioctl",
+ [CUSE_CMD_POLL] = "poll",
+ [CUSE_CMD_SIGNAL] = "signal",
+ [CUSE_CMD_SYNC] = "sync",
+ };
+
+ if ((cmd >= 0) && (cmd < CUSE_CMD_MAX) &&
+ (str[cmd] != NULL))
+ return (str[cmd]);
+ else
+ return ("unknown");
+}
+
+#endif
+
+int
+cuse_get_local(void)
+{
+ struct cuse_dev_entered *pe;
+
+ pe = cuse_dev_get_entered();
+ if (pe == NULL)
+ return (0);
+
+ return (pe->is_local);
+}
+
+int
+cuse_copy_out(const void *src, void *user_dst, int len)
+{
+ struct cuse_data_chunk info;
+ struct cuse_dev_entered *pe;
+ int error;
+
+ if ((f_cuse < 0) || (len < 0))
+ return (CUSE_ERR_INVALID);
+
+ pe = cuse_dev_get_entered();
+ if (pe == NULL)
+ return (CUSE_ERR_INVALID);
+
+ DPRINTF("cuse: copy_out(%p,%p,%d), cmd = %d = %s\n",
+ src, user_dst, len, pe->cmd, cuse_cmd_str(pe->cmd));
+
+ if (pe->is_local) {
+ memcpy(user_dst, src, len);
+ } else {
+ info.local_ptr = (unsigned long)src;
+ info.peer_ptr = (unsigned long)user_dst;
+ info.length = len;
+
+ error = ioctl(f_cuse, CUSE_IOCTL_WRITE_DATA, &info);
+ if (error) {
+ DPRINTF("cuse: copy_out() error = %d\n", errno);
+ return (CUSE_ERR_FAULT);
+ }
+ }
+ return (0);
+}
+
+int
+cuse_copy_in(const void *user_src, void *dst, int len)
+{
+ struct cuse_data_chunk info;
+ struct cuse_dev_entered *pe;
+ int error;
+
+ if ((f_cuse < 0) || (len < 0))
+ return (CUSE_ERR_INVALID);
+
+ pe = cuse_dev_get_entered();
+ if (pe == NULL)
+ return (CUSE_ERR_INVALID);
+
+ DPRINTF("cuse: copy_in(%p,%p,%d), cmd = %d = %s\n",
+ user_src, dst, len, pe->cmd, cuse_cmd_str(pe->cmd));
+
+ if (pe->is_local) {
+ memcpy(dst, user_src, len);
+ } else {
+ info.local_ptr = (unsigned long)dst;
+ info.peer_ptr = (unsigned long)user_src;
+ info.length = len;
+
+ error = ioctl(f_cuse, CUSE_IOCTL_READ_DATA, &info);
+ if (error) {
+ DPRINTF("cuse: copy_in() error = %d\n", errno);
+ return (CUSE_ERR_FAULT);
+ }
+ }
+ return (0);
+}
+
+struct cuse_dev *
+cuse_dev_get_current(int *pcmd)
+{
+ struct cuse_dev_entered *pe;
+
+ pe = cuse_dev_get_entered();
+ if (pe == NULL) {
+ if (pcmd != NULL)
+ *pcmd = 0;
+ return (NULL);
+ }
+ if (pcmd != NULL)
+ *pcmd = pe->cmd;
+ return (pe->cdev);
+}
+
+int
+cuse_got_peer_signal(void)
+{
+ struct cuse_dev_entered *pe;
+
+ pe = cuse_dev_get_entered();
+ if (pe == NULL)
+ return (CUSE_ERR_INVALID);
+
+ if (pe->got_signal)
+ return (0);
+
+ return (CUSE_ERR_OTHER);
+}
+
+void
+cuse_poll_wakeup(void)
+{
+ int error = 0;
+
+ if (f_cuse < 0)
+ return;
+
+ ioctl(f_cuse, CUSE_IOCTL_SELWAKEUP, &error);
+}
diff --git a/sys/conf/files b/sys/conf/files
index c7d850c..79452a9 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -2595,6 +2595,7 @@ fs/devfs/devfs_vnops.c standard
fs/fdescfs/fdesc_vfsops.c optional fdescfs
fs/fdescfs/fdesc_vnops.c optional fdescfs
fs/fifofs/fifo_vnops.c standard
+fs/cuse/cuse.c optional cuse
fs/fuse/fuse_device.c optional fuse
fs/fuse/fuse_file.c optional fuse
fs/fuse/fuse_internal.c optional fuse
diff --git a/sys/fs/cuse/cuse.c b/sys/fs/cuse/cuse.c
new file mode 100644
index 0000000..4a23963
--- /dev/null
+++ b/sys/fs/cuse/cuse.c
@@ -0,0 +1,1866 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2010-2013 Hans Petter Selasky. 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 THE 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 THE 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 "opt_compat.h"
+
+#include <sys/stdint.h>
+#include <sys/stddef.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/linker_set.h>
+#include <sys/module.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/unistd.h>
+#include <sys/malloc.h>
+#include <sys/priv.h>
+#include <sys/uio.h>
+#include <sys/poll.h>
+#include <sys/sx.h>
+#include <sys/queue.h>
+#include <sys/fcntl.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/selinfo.h>
+#include <sys/ptrace.h>
+
+#include <machine/bus.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <fs/cuse/cuse_defs.h>
+#include <fs/cuse/cuse_ioctl.h>
+
+MODULE_VERSION(cuse, 1);
+
+#define NBUSY ((uint8_t *)1)
+
+#ifdef FEATURE
+FEATURE(cuse, "Userspace character devices");
+#endif
+
+struct cuse_command;
+struct cuse_server;
+struct cuse_client;
+
+struct cuse_client_command {
+ TAILQ_ENTRY(cuse_client_command) entry;
+ struct cuse_command sub;
+ struct sx sx;
+ struct cv cv;
+ struct thread *entered;
+ struct cuse_client *client;
+ struct proc *proc_curr;
+ int proc_refs;
+ int got_signal;
+ int error;
+ int command;
+};
+
+struct cuse_memory {
+ struct cuse_server *owner;
+ uint8_t *virtaddr;
+ uint32_t page_count;
+ uint32_t is_allocated;
+};
+
+struct cuse_server_dev {
+ TAILQ_ENTRY(cuse_server_dev) entry;
+ struct cuse_server *server;
+ struct cdev *kern_dev;
+ struct cuse_dev *user_dev;
+};
+
+struct cuse_server {
+ TAILQ_ENTRY(cuse_server) entry;
+ TAILQ_HEAD(, cuse_client_command) head;
+ TAILQ_HEAD(, cuse_server_dev) hdev;
+ TAILQ_HEAD(, cuse_client) hcli;
+ struct cv cv;
+ struct selinfo selinfo;
+ int is_closing;
+ int refs;
+};
+
+struct cuse_client {
+ TAILQ_ENTRY(cuse_client) entry;
+ TAILQ_ENTRY(cuse_client) entry_ref;
+ struct cuse_client_command cmds[CUSE_CMD_MAX];
+ struct cuse_server *server;
+ struct cuse_server_dev *server_dev;
+
+ uint8_t ioctl_buffer[CUSE_BUFFER_MAX] __aligned(4);
+
+ int fflags; /* file flags */
+ int cflags; /* client flags */
+#define CUSE_CLI_IS_CLOSING 0x01
+#define CUSE_CLI_KNOTE_NEED_READ 0x02
+#define CUSE_CLI_KNOTE_NEED_WRITE 0x04
+#define CUSE_CLI_KNOTE_HAS_READ 0x08
+#define CUSE_CLI_KNOTE_HAS_WRITE 0x10
+};
+
+#define CUSE_CLIENT_CLOSING(pcc) \
+ ((pcc)->cflags & CUSE_CLI_IS_CLOSING)
+
+static MALLOC_DEFINE(M_CUSE, "cuse", "CUSE memory");
+
+static TAILQ_HEAD(, cuse_server) cuse_server_head;
+static struct mtx cuse_mtx;
+static struct cdev *cuse_dev;
+static struct cuse_server *cuse_alloc_unit[CUSE_DEVICES_MAX];
+static int cuse_alloc_unit_id[CUSE_DEVICES_MAX];
+static struct cuse_memory cuse_mem[CUSE_ALLOC_UNIT_MAX];
+
+static void cuse_client_kqfilter_read_detach(struct knote *kn);
+static void cuse_client_kqfilter_write_detach(struct knote *kn);
+static int cuse_client_kqfilter_read_event(struct knote *kn, long hint);
+static int cuse_client_kqfilter_write_event(struct knote *kn, long hint);
+
+static struct filterops cuse_client_kqfilter_read_ops = {
+ .f_isfd = 1,
+ .f_detach = cuse_client_kqfilter_read_detach,
+ .f_event = cuse_client_kqfilter_read_event,
+};
+
+static struct filterops cuse_client_kqfilter_write_ops = {
+ .f_isfd = 1,
+ .f_detach = cuse_client_kqfilter_write_detach,
+ .f_event = cuse_client_kqfilter_write_event,
+};
+
+static d_open_t cuse_client_open;
+static d_close_t cuse_client_close;
+static d_ioctl_t cuse_client_ioctl;
+static d_read_t cuse_client_read;
+static d_write_t cuse_client_write;
+static d_poll_t cuse_client_poll;
+static d_mmap_t cuse_client_mmap;
+static d_kqfilter_t cuse_client_kqfilter;
+
+static struct cdevsw cuse_client_devsw = {
+ .d_version = D_VERSION,
+ .d_open = cuse_client_open,
+ .d_close = cuse_client_close,
+ .d_ioctl = cuse_client_ioctl,
+ .d_name = "cuse_client",
+ .d_flags = D_TRACKCLOSE,
+ .d_read = cuse_client_read,
+ .d_write = cuse_client_write,
+ .d_poll = cuse_client_poll,
+ .d_mmap = cuse_client_mmap,
+ .d_kqfilter = cuse_client_kqfilter,
+};
+
+static d_open_t cuse_server_open;
+static d_close_t cuse_server_close;
+static d_ioctl_t cuse_server_ioctl;
+static d_read_t cuse_server_read;
+static d_write_t cuse_server_write;
+static d_poll_t cuse_server_poll;
+static d_mmap_t cuse_server_mmap;
+
+static struct cdevsw cuse_server_devsw = {
+ .d_version = D_VERSION,
+ .d_open = cuse_server_open,
+ .d_close = cuse_server_close,
+ .d_ioctl = cuse_server_ioctl,
+ .d_name = "cuse_server",
+ .d_flags = D_TRACKCLOSE,
+ .d_read = cuse_server_read,
+ .d_write = cuse_server_write,
+ .d_poll = cuse_server_poll,
+ .d_mmap = cuse_server_mmap,
+};
+
+static void cuse_client_is_closing(struct cuse_client *);
+static int cuse_free_unit_by_id_locked(struct cuse_server *, int);
+
+static void
+cuse_lock(void)
+{
+ mtx_lock(&cuse_mtx);
+}
+
+static void
+cuse_unlock(void)
+{
+ mtx_unlock(&cuse_mtx);
+}
+
+static void
+cuse_cmd_lock(struct cuse_client_command *pccmd)
+{
+ sx_xlock(&pccmd->sx);
+}
+
+static void
+cuse_cmd_unlock(struct cuse_client_command *pccmd)
+{
+ sx_xunlock(&pccmd->sx);
+}
+
+static void
+cuse_kern_init(void *arg)
+{
+ TAILQ_INIT(&cuse_server_head);
+
+ mtx_init(&cuse_mtx, "cuse-mtx", NULL, MTX_DEF);
+
+ cuse_dev = make_dev(&cuse_server_devsw, 0,
+ UID_ROOT, GID_OPERATOR, 0600, "cuse");
+
+ printf("Cuse v%d.%d.%d @ /dev/cuse\n",
+ (CUSE_VERSION >> 16) & 0xFF, (CUSE_VERSION >> 8) & 0xFF,
+ (CUSE_VERSION >> 0) & 0xFF);
+}
+
+SYSINIT(cuse_kern_init, SI_SUB_DEVFS, SI_ORDER_ANY, cuse_kern_init, 0);
+
+static void
+cuse_kern_uninit(void *arg)
+{
+ void *ptr;
+
+ while (1) {
+
+ printf("Cuse: Please exit all /dev/cuse instances "
+ "and processes which have used this device.\n");
+
+ pause("DRAIN", 2 * hz);
+
+ cuse_lock();
+ ptr = TAILQ_FIRST(&cuse_server_head);
+ cuse_unlock();
+
+ if (ptr == NULL)
+ break;
+ }
+
+ if (cuse_dev != NULL)
+ destroy_dev(cuse_dev);
+
+ mtx_destroy(&cuse_mtx);
+}
+
+SYSUNINIT(cuse_kern_uninit, SI_SUB_DEVFS, SI_ORDER_ANY, cuse_kern_uninit, 0);
+
+static int
+cuse_server_get(struct cuse_server **ppcs)
+{
+ struct cuse_server *pcs;
+ int error;
+
+ error = devfs_get_cdevpriv((void **)&pcs);
+ if (error != 0) {
+ *ppcs = NULL;
+ return (error);
+ }
+ /* check if closing */
+ cuse_lock();
+ if (pcs->is_closing) {
+ cuse_unlock();
+ *ppcs = NULL;
+ return (EINVAL);
+ }
+ cuse_unlock();
+ *ppcs = pcs;
+ return (0);
+}
+
+static void
+cuse_server_is_closing(struct cuse_server *pcs)
+{
+ struct cuse_client *pcc;
+
+ if (pcs->is_closing)
+ return;
+
+ pcs->is_closing = 1;
+
+ TAILQ_FOREACH(pcc, &pcs->hcli, entry) {
+ cuse_client_is_closing(pcc);
+ }
+}
+
+static struct cuse_client_command *
+cuse_server_find_command(struct cuse_server *pcs, struct thread *td)
+{
+ struct cuse_client *pcc;
+ int n;
+
+ if (pcs->is_closing)
+ goto done;
+
+ TAILQ_FOREACH(pcc, &pcs->hcli, entry) {
+ if (CUSE_CLIENT_CLOSING(pcc))
+ continue;
+ for (n = 0; n != CUSE_CMD_MAX; n++) {
+ if (pcc->cmds[n].entered == td)
+ return (&pcc->cmds[n]);
+ }
+ }
+done:
+ return (NULL);
+}
+
+static void
+cuse_str_filter(char *ptr)
+{
+ int c;
+
+ while (((c = *ptr) != 0)) {
+
+ if ((c >= 'a') && (c <= 'z')) {
+ ptr++;
+ continue;
+ }
+ if ((c >= 'A') && (c <= 'Z')) {
+ ptr++;
+ continue;
+ }
+ if ((c >= '0') && (c <= '9')) {
+ ptr++;
+ continue;
+ }
+ if ((c == '.') || (c == '_') || (c == '/')) {
+ ptr++;
+ continue;
+ }
+ *ptr = '_';
+
+ ptr++;
+ }
+}
+
+static int
+cuse_convert_error(int error)
+{
+ ; /* indent fix */
+ switch (error) {
+ case CUSE_ERR_NONE:
+ return (0);
+ case CUSE_ERR_BUSY:
+ return (EBUSY);
+ case CUSE_ERR_WOULDBLOCK:
+ return (EWOULDBLOCK);
+ case CUSE_ERR_INVALID:
+ return (EINVAL);
+ case CUSE_ERR_NO_MEMORY:
+ return (ENOMEM);
+ case CUSE_ERR_FAULT:
+ return (EFAULT);
+ case CUSE_ERR_SIGNAL:
+ return (EINTR);
+ default:
+ return (ENXIO);
+ }
+}
+
+static void
+cuse_server_free_memory(struct cuse_server *pcs)
+{
+ struct cuse_memory *mem;
+ uint32_t n;
+
+ for (n = 0; n != CUSE_ALLOC_UNIT_MAX; n++) {
+ mem = &cuse_mem[n];
+
+ /* this memory is never freed */
+ if (mem->owner == pcs) {
+ mem->owner = NULL;
+ mem->is_allocated = 0;
+ }
+ }
+}
+
+static int
+cuse_server_alloc_memory(struct cuse_server *pcs,
+ struct cuse_memory *mem, uint32_t page_count)
+{
+ void *ptr;
+ int error;
+
+ cuse_lock();
+
+ if (mem->virtaddr == NBUSY) {
+ cuse_unlock();
+ return (EBUSY);
+ }
+ if (mem->virtaddr != NULL) {
+ if (mem->is_allocated != 0) {
+ cuse_unlock();
+ return (EBUSY);
+ }
+ if (mem->page_count == page_count) {
+ mem->is_allocated = 1;
+ mem->owner = pcs;
+ cuse_unlock();
+ return (0);
+ }
+ cuse_unlock();
+ return (EBUSY);
+ }
+ memset(mem, 0, sizeof(*mem));
+
+ mem->virtaddr = NBUSY;
+
+ cuse_unlock();
+
+ ptr = malloc(page_count * PAGE_SIZE, M_CUSE, M_WAITOK | M_ZERO);
+ if (ptr == NULL)
+ error = ENOMEM;
+ else
+ error = 0;
+
+ cuse_lock();
+
+ if (error) {
+ mem->virtaddr = NULL;
+ cuse_unlock();
+ return (error);
+ }
+ mem->virtaddr = ptr;
+ mem->page_count = page_count;
+ mem->is_allocated = 1;
+ mem->owner = pcs;
+ cuse_unlock();
+
+ return (0);
+}
+
+static int
+cuse_client_get(struct cuse_client **ppcc)
+{
+ struct cuse_client *pcc;
+ int error;
+
+ /* try to get private data */
+ error = devfs_get_cdevpriv((void **)&pcc);
+ if (error != 0) {
+ *ppcc = NULL;
+ return (error);
+ }
+ /* check if closing */
+ cuse_lock();
+ if (CUSE_CLIENT_CLOSING(pcc) || pcc->server->is_closing) {
+ cuse_unlock();
+ *ppcc = NULL;
+ return (EINVAL);
+ }
+ cuse_unlock();
+ *ppcc = pcc;
+ return (0);
+}
+
+static void
+cuse_client_is_closing(struct cuse_client *pcc)
+{
+ struct cuse_client_command *pccmd;
+ uint32_t n;
+
+ if (CUSE_CLIENT_CLOSING(pcc))
+ return;
+
+ pcc->cflags |= CUSE_CLI_IS_CLOSING;
+ pcc->server_dev = NULL;
+
+ for (n = 0; n != CUSE_CMD_MAX; n++) {
+
+ pccmd = &pcc->cmds[n];
+
+ if (pccmd->entry.tqe_prev != NULL) {
+ TAILQ_REMOVE(&pcc->server->head, pccmd, entry);
+ pccmd->entry.tqe_prev = NULL;
+ }
+ cv_broadcast(&pccmd->cv);
+ }
+}
+
+static void
+cuse_client_send_command_locked(struct cuse_client_command *pccmd,
+ unsigned long data_ptr, unsigned long arg, int fflags, int ioflag)
+{
+ unsigned long cuse_fflags = 0;
+ struct cuse_server *pcs;
+
+ if (fflags & FREAD)
+ cuse_fflags |= CUSE_FFLAG_READ;
+
+ if (fflags & FWRITE)
+ cuse_fflags |= CUSE_FFLAG_WRITE;
+
+ if (ioflag & IO_NDELAY)
+ cuse_fflags |= CUSE_FFLAG_NONBLOCK;
+
+ pccmd->sub.fflags = cuse_fflags;
+ pccmd->sub.data_pointer = data_ptr;
+ pccmd->sub.argument = arg;
+
+ pcs = pccmd->client->server;
+
+ if ((pccmd->entry.tqe_prev == NULL) &&
+ (CUSE_CLIENT_CLOSING(pccmd->client) == 0) &&
+ (pcs->is_closing == 0)) {
+ TAILQ_INSERT_TAIL(&pcs->head, pccmd, entry);
+ cv_signal(&pcs->cv);
+ }
+}
+
+static void
+cuse_client_got_signal(struct cuse_client_command *pccmd)
+{
+ struct cuse_server *pcs;
+
+ pccmd->got_signal = 1;
+
+ pccmd = &pccmd->client->cmds[CUSE_CMD_SIGNAL];
+
+ pcs = pccmd->client->server;
+
+ if ((pccmd->entry.tqe_prev == NULL) &&
+ (CUSE_CLIENT_CLOSING(pccmd->client) == 0) &&
+ (pcs->is_closing == 0)) {
+ TAILQ_INSERT_TAIL(&pcs->head, pccmd, entry);
+ cv_signal(&pcs->cv);
+ }
+}
+
+static int
+cuse_client_receive_command_locked(struct cuse_client_command *pccmd,
+ uint8_t *arg_ptr, uint32_t arg_len)
+{
+ int error;
+
+ error = 0;
+
+ pccmd->proc_curr = curthread->td_proc;
+
+ if (CUSE_CLIENT_CLOSING(pccmd->client) ||
+ pccmd->client->server->is_closing) {
+ error = CUSE_ERR_OTHER;
+ goto done;
+ }
+ while (pccmd->command == CUSE_CMD_NONE) {
+ if (error != 0) {
+ cv_wait(&pccmd->cv, &cuse_mtx);
+ } else {
+ error = cv_wait_sig(&pccmd->cv, &cuse_mtx);
+
+ if (error != 0)
+ cuse_client_got_signal(pccmd);
+ }
+ if (CUSE_CLIENT_CLOSING(pccmd->client) ||
+ pccmd->client->server->is_closing) {
+ error = CUSE_ERR_OTHER;
+ goto done;
+ }
+ }
+
+ error = pccmd->error;
+ pccmd->command = CUSE_CMD_NONE;
+ cv_signal(&pccmd->cv);
+
+done:
+
+ /* wait until all process references are gone */
+
+ pccmd->proc_curr = NULL;
+
+ while (pccmd->proc_refs != 0)
+ cv_wait(&pccmd->cv, &cuse_mtx);
+
+ return (error);
+}
+
+/*------------------------------------------------------------------------*
+ * CUSE SERVER PART
+ *------------------------------------------------------------------------*/
+
+static void
+cuse_server_free_dev(struct cuse_server_dev *pcsd)
+{
+ struct cuse_server *pcs;
+ struct cuse_client *pcc;
+
+ /* get server pointer */
+ pcs = pcsd->server;
+
+ /* prevent creation of more devices */
+ cuse_lock();
+ if (pcsd->kern_dev != NULL)
+ pcsd->kern_dev->si_drv1 = NULL;
+
+ TAILQ_FOREACH(pcc, &pcs->hcli, entry) {
+ if (pcc->server_dev == pcsd)
+ cuse_client_is_closing(pcc);
+ }
+ cuse_unlock();
+
+ /* destroy device, if any */
+ if (pcsd->kern_dev != NULL) {
+ /* destroy device synchronously */
+ destroy_dev(pcsd->kern_dev);
+ }
+ free(pcsd, M_CUSE);
+}
+
+static void
+cuse_server_free(void *arg)
+{
+ struct cuse_server *pcs = arg;
+ struct cuse_server_dev *pcsd;
+
+ cuse_lock();
+ pcs->refs--;
+ if (pcs->refs != 0) {
+ cuse_unlock();
+ return;
+ }
+ cuse_server_is_closing(pcs);
+
+ TAILQ_REMOVE(&cuse_server_head, pcs, entry);
+
+ cuse_free_unit_by_id_locked(pcs, -1);
+
+ while ((pcsd = TAILQ_FIRST(&pcs->hdev)) != NULL) {
+ TAILQ_REMOVE(&pcs->hdev, pcsd, entry);
+ cuse_unlock();
+ cuse_server_free_dev(pcsd);
+ cuse_lock();
+ }
+
+ cuse_server_free_memory(pcs);
+
+ knlist_clear(&pcs->selinfo.si_note, 1);
+ knlist_destroy(&pcs->selinfo.si_note);
+
+ cuse_unlock();
+
+ seldrain(&pcs->selinfo);
+
+ cv_destroy(&pcs->cv);
+
+ free(pcs, M_CUSE);
+}
+
+static int
+cuse_server_open(struct cdev *dev, int fflags, int devtype, struct thread *td)
+{
+ struct cuse_server *pcs;
+
+ pcs = malloc(sizeof(*pcs), M_CUSE, M_WAITOK | M_ZERO);
+ if (pcs == NULL)
+ return (ENOMEM);
+
+ if (devfs_set_cdevpriv(pcs, &cuse_server_free)) {
+ printf("Cuse: Cannot set cdevpriv.\n");
+ free(pcs, M_CUSE);
+ return (ENOMEM);
+ }
+ TAILQ_INIT(&pcs->head);
+ TAILQ_INIT(&pcs->hdev);
+ TAILQ_INIT(&pcs->hcli);
+
+ cv_init(&pcs->cv, "cuse-server-cv");
+
+ knlist_init_mtx(&pcs->selinfo.si_note, &cuse_mtx);
+
+ cuse_lock();
+ pcs->refs++;
+ TAILQ_INSERT_TAIL(&cuse_server_head, pcs, entry);
+ cuse_unlock();
+
+ return (0);
+}
+
+static int
+cuse_server_close(struct cdev *dev, int fflag, int devtype, struct thread *td)
+{
+ struct cuse_server *pcs;
+ int error;
+
+ error = cuse_server_get(&pcs);
+ if (error != 0)
+ goto done;
+
+ cuse_lock();
+ cuse_server_is_closing(pcs);
+ knlist_clear(&pcs->selinfo.si_note, 1);
+ cuse_unlock();
+
+done:
+ return (0);
+}
+
+static int
+cuse_server_read(struct cdev *dev, struct uio *uio, int ioflag)
+{
+ return (ENXIO);
+}
+
+static int
+cuse_server_write(struct cdev *dev, struct uio *uio, int ioflag)
+{
+ return (ENXIO);
+}
+
+static int
+cuse_server_ioctl_copy_locked(struct cuse_client_command *pccmd,
+ struct cuse_data_chunk *pchk, int isread)
+{
+ struct proc *p_proc;
+ uint32_t offset;
+ int error;
+
+ offset = pchk->peer_ptr - CUSE_BUF_MIN_PTR;
+
+ if (pchk->length > CUSE_BUFFER_MAX)
+ return (EFAULT);
+
+ if (offset >= CUSE_BUFFER_MAX)
+ return (EFAULT);
+
+ if ((offset + pchk->length) > CUSE_BUFFER_MAX)
+ return (EFAULT);
+
+ p_proc = pccmd->proc_curr;
+ if (p_proc == NULL)
+ return (ENXIO);
+
+ if (pccmd->proc_refs < 0)
+ return (ENOMEM);
+
+ pccmd->proc_refs++;
+
+ cuse_unlock();
+
+ if (isread == 0) {
+ error = copyin(
+ (void *)pchk->local_ptr,
+ pccmd->client->ioctl_buffer + offset,
+ pchk->length);
+ } else {
+ error = copyout(
+ pccmd->client->ioctl_buffer + offset,
+ (void *)pchk->local_ptr,
+ pchk->length);
+ }
+
+ cuse_lock();
+
+ pccmd->proc_refs--;
+
+ if (pccmd->proc_curr == NULL)
+ cv_signal(&pccmd->cv);
+
+ return (error);
+}
+
+static int
+cuse_proc2proc_copy(struct proc *proc_s, vm_offset_t data_s,
+ struct proc *proc_d, vm_offset_t data_d, size_t len)
+{
+ struct thread *td;
+ struct proc *proc_cur;
+ int error;
+
+ td = curthread;
+ proc_cur = td->td_proc;
+
+ if (proc_cur == proc_d) {
+ struct iovec iov = {
+ .iov_base = (caddr_t)data_d,
+ .iov_len = len,
+ };
+ struct uio uio = {
+ .uio_iov = &iov,
+ .uio_iovcnt = 1,
+ .uio_offset = (off_t)data_s,
+ .uio_resid = len,
+ .uio_segflg = UIO_USERSPACE,
+ .uio_rw = UIO_READ,
+ .uio_td = td,
+ };
+
+ PROC_LOCK(proc_s);
+ _PHOLD(proc_s);
+ PROC_UNLOCK(proc_s);
+
+ error = proc_rwmem(proc_s, &uio);
+
+ PROC_LOCK(proc_s);
+ _PRELE(proc_s);
+ PROC_UNLOCK(proc_s);
+
+ } else if (proc_cur == proc_s) {
+ struct iovec iov = {
+ .iov_base = (caddr_t)data_s,
+ .iov_len = len,
+ };
+ struct uio uio = {
+ .uio_iov = &iov,
+ .uio_iovcnt = 1,
+ .uio_offset = (off_t)data_d,
+ .uio_resid = len,
+ .uio_segflg = UIO_USERSPACE,
+ .uio_rw = UIO_WRITE,
+ .uio_td = td,
+ };
+
+ PROC_LOCK(proc_d);
+ _PHOLD(proc_d);
+ PROC_UNLOCK(proc_d);
+
+ error = proc_rwmem(proc_d, &uio);
+
+ PROC_LOCK(proc_d);
+ _PRELE(proc_d);
+ PROC_UNLOCK(proc_d);
+ } else {
+ error = EINVAL;
+ }
+ return (error);
+}
+
+static int
+cuse_server_data_copy_locked(struct cuse_client_command *pccmd,
+ struct cuse_data_chunk *pchk, int isread)
+{
+ struct proc *p_proc;
+ int error;
+
+ p_proc = pccmd->proc_curr;
+ if (p_proc == NULL)
+ return (ENXIO);
+
+ if (pccmd->proc_refs < 0)
+ return (ENOMEM);
+
+ pccmd->proc_refs++;
+
+ cuse_unlock();
+
+ if (isread == 0) {
+ error = cuse_proc2proc_copy(
+ curthread->td_proc, pchk->local_ptr,
+ p_proc, pchk->peer_ptr,
+ pchk->length);
+ } else {
+ error = cuse_proc2proc_copy(
+ p_proc, pchk->peer_ptr,
+ curthread->td_proc, pchk->local_ptr,
+ pchk->length);
+ }
+
+ cuse_lock();
+
+ pccmd->proc_refs--;
+
+ if (pccmd->proc_curr == NULL)
+ cv_signal(&pccmd->cv);
+
+ return (error);
+}
+
+static int
+cuse_alloc_unit_by_id_locked(struct cuse_server *pcs, int id)
+{
+ int n;
+ int x = 0;
+ int match;
+
+ do {
+ for (match = n = 0; n != CUSE_DEVICES_MAX; n++) {
+ if (cuse_alloc_unit[n] != NULL) {
+ if ((cuse_alloc_unit_id[n] ^ id) & CUSE_ID_MASK)
+ continue;
+ if ((cuse_alloc_unit_id[n] & ~CUSE_ID_MASK) == x) {
+ x++;
+ match = 1;
+ }
+ }
+ }
+ } while (match);
+
+ if (x < 256) {
+ for (n = 0; n != CUSE_DEVICES_MAX; n++) {
+ if (cuse_alloc_unit[n] == NULL) {
+ cuse_alloc_unit[n] = pcs;
+ cuse_alloc_unit_id[n] = id | x;
+ return (x);
+ }
+ }
+ }
+ return (-1);
+}
+
+static void
+cuse_server_wakeup_locked(struct cuse_server *pcs)
+{
+ selwakeup(&pcs->selinfo);
+ KNOTE_LOCKED(&pcs->selinfo.si_note, 0);
+}
+
+static int
+cuse_free_unit_by_id_locked(struct cuse_server *pcs, int id)
+{
+ int n;
+ int found = 0;
+
+ for (n = 0; n != CUSE_DEVICES_MAX; n++) {
+ if (cuse_alloc_unit[n] == pcs) {
+ if (cuse_alloc_unit_id[n] == id || id == -1) {
+ cuse_alloc_unit[n] = NULL;
+ cuse_alloc_unit_id[n] = 0;
+ found = 1;
+ }
+ }
+ }
+
+ return (found ? 0 : EINVAL);
+}
+
+static int
+cuse_server_ioctl(struct cdev *dev, unsigned long cmd,
+ caddr_t data, int fflag, struct thread *td)
+{
+ struct cuse_server *pcs;
+ int error;
+
+ error = cuse_server_get(&pcs);
+ if (error != 0)
+ return (error);
+
+ switch (cmd) {
+ struct cuse_client_command *pccmd;
+ struct cuse_client *pcc;
+ struct cuse_command *pcmd;
+ struct cuse_alloc_info *pai;
+ struct cuse_create_dev *pcd;
+ struct cuse_server_dev *pcsd;
+ struct cuse_data_chunk *pchk;
+ int n;
+
+ case CUSE_IOCTL_GET_COMMAND:
+ pcmd = (void *)data;
+
+ cuse_lock();
+
+ while ((pccmd = TAILQ_FIRST(&pcs->head)) == NULL) {
+ error = cv_wait_sig(&pcs->cv, &cuse_mtx);
+
+ if (pcs->is_closing)
+ error = ENXIO;
+
+ if (error) {
+ cuse_unlock();
+ return (error);
+ }
+ }
+
+ TAILQ_REMOVE(&pcs->head, pccmd, entry);
+ pccmd->entry.tqe_prev = NULL;
+
+ pccmd->entered = curthread;
+
+ *pcmd = pccmd->sub;
+
+ cuse_unlock();
+
+ break;
+
+ case CUSE_IOCTL_SYNC_COMMAND:
+
+ cuse_lock();
+ while ((pccmd = cuse_server_find_command(pcs, curthread)) != NULL) {
+
+ /* send sync command */
+ pccmd->entered = NULL;
+ pccmd->error = *(int *)data;
+ pccmd->command = CUSE_CMD_SYNC;
+
+ /* signal peer, if any */
+ cv_signal(&pccmd->cv);
+ }
+ cuse_unlock();
+
+ break;
+
+ case CUSE_IOCTL_ALLOC_UNIT:
+
+ cuse_lock();
+ n = cuse_alloc_unit_by_id_locked(pcs,
+ CUSE_ID_DEFAULT(0));
+ cuse_unlock();
+
+ if (n < 0)
+ error = ENOMEM;
+ else
+ *(int *)data = n;
+ break;
+
+ case CUSE_IOCTL_ALLOC_UNIT_BY_ID:
+
+ n = *(int *)data;
+
+ n = (n & CUSE_ID_MASK);
+
+ cuse_lock();
+ n = cuse_alloc_unit_by_id_locked(pcs, n);
+ cuse_unlock();
+
+ if (n < 0)
+ error = ENOMEM;
+ else
+ *(int *)data = n;
+ break;
+
+ case CUSE_IOCTL_FREE_UNIT:
+
+ n = *(int *)data;
+
+ n = CUSE_ID_DEFAULT(n);
+
+ cuse_lock();
+ error = cuse_free_unit_by_id_locked(pcs, n);
+ cuse_unlock();
+ break;
+
+ case CUSE_IOCTL_FREE_UNIT_BY_ID:
+
+ n = *(int *)data;
+
+ cuse_lock();
+ error = cuse_free_unit_by_id_locked(pcs, n);
+ cuse_unlock();
+ break;
+
+ case CUSE_IOCTL_ALLOC_MEMORY:
+
+ pai = (void *)data;
+
+ if (pai->alloc_nr >= CUSE_ALLOC_UNIT_MAX) {
+ error = ENOMEM;
+ break;
+ }
+ if (pai->page_count > CUSE_ALLOC_PAGES_MAX) {
+ error = ENOMEM;
+ break;
+ }
+ error = cuse_server_alloc_memory(pcs,
+ &cuse_mem[pai->alloc_nr], pai->page_count);
+ break;
+
+ case CUSE_IOCTL_FREE_MEMORY:
+ pai = (void *)data;
+
+ if (pai->alloc_nr >= CUSE_ALLOC_UNIT_MAX) {
+ error = ENOMEM;
+ break;
+ }
+ /* we trust the character device driver in this case */
+
+ cuse_lock();
+ if (cuse_mem[pai->alloc_nr].owner == pcs) {
+ cuse_mem[pai->alloc_nr].is_allocated = 0;
+ cuse_mem[pai->alloc_nr].owner = NULL;
+ } else {
+ error = EINVAL;
+ }
+ cuse_unlock();
+ break;
+
+ case CUSE_IOCTL_GET_SIG:
+
+ cuse_lock();
+ pccmd = cuse_server_find_command(pcs, curthread);
+
+ if (pccmd != NULL) {
+ n = pccmd->got_signal;
+ pccmd->got_signal = 0;
+ } else {
+ n = 0;
+ }
+ cuse_unlock();
+
+ *(int *)data = n;
+
+ break;
+
+ case CUSE_IOCTL_SET_PFH:
+
+ cuse_lock();
+ pccmd = cuse_server_find_command(pcs, curthread);
+
+ if (pccmd != NULL) {
+ pcc = pccmd->client;
+ for (n = 0; n != CUSE_CMD_MAX; n++) {
+ pcc->cmds[n].sub.per_file_handle = *(unsigned long *)data;
+ }
+ } else {
+ error = ENXIO;
+ }
+ cuse_unlock();
+ break;
+
+ case CUSE_IOCTL_CREATE_DEV:
+
+ error = priv_check(curthread, PRIV_DRIVER);
+ if (error)
+ break;
+
+ pcd = (void *)data;
+
+ /* filter input */
+
+ pcd->devname[sizeof(pcd->devname) - 1] = 0;
+
+ if (pcd->devname[0] == 0) {
+ error = EINVAL;
+ break;
+ }
+ cuse_str_filter(pcd->devname);
+
+ pcd->permissions &= 0777;
+
+ /* try to allocate a character device */
+
+ pcsd = malloc(sizeof(*pcsd), M_CUSE, M_WAITOK | M_ZERO);
+
+ if (pcsd == NULL) {
+ error = ENOMEM;
+ break;
+ }
+ pcsd->server = pcs;
+
+ pcsd->user_dev = pcd->dev;
+
+ pcsd->kern_dev = make_dev_credf(MAKEDEV_CHECKNAME,
+ &cuse_client_devsw, 0, NULL, pcd->user_id, pcd->group_id,
+ pcd->permissions, "%s", pcd->devname);
+
+ if (pcsd->kern_dev == NULL) {
+ free(pcsd, M_CUSE);
+ error = ENOMEM;
+ break;
+ }
+ pcsd->kern_dev->si_drv1 = pcsd;
+
+ cuse_lock();
+ TAILQ_INSERT_TAIL(&pcs->hdev, pcsd, entry);
+ cuse_unlock();
+
+ break;
+
+ case CUSE_IOCTL_DESTROY_DEV:
+
+ error = priv_check(curthread, PRIV_DRIVER);
+ if (error)
+ break;
+
+ cuse_lock();
+
+ error = EINVAL;
+
+ pcsd = TAILQ_FIRST(&pcs->hdev);
+ while (pcsd != NULL) {
+ if (pcsd->user_dev == *(struct cuse_dev **)data) {
+ TAILQ_REMOVE(&pcs->hdev, pcsd, entry);
+ cuse_unlock();
+ cuse_server_free_dev(pcsd);
+ cuse_lock();
+ error = 0;
+ pcsd = TAILQ_FIRST(&pcs->hdev);
+ } else {
+ pcsd = TAILQ_NEXT(pcsd, entry);
+ }
+ }
+
+ cuse_unlock();
+ break;
+
+ case CUSE_IOCTL_WRITE_DATA:
+ case CUSE_IOCTL_READ_DATA:
+
+ cuse_lock();
+ pchk = (struct cuse_data_chunk *)data;
+
+ pccmd = cuse_server_find_command(pcs, curthread);
+
+ if (pccmd == NULL) {
+ error = ENXIO; /* invalid request */
+ } else if (pchk->peer_ptr < CUSE_BUF_MIN_PTR) {
+ error = EFAULT; /* NULL pointer */
+ } else if (pchk->peer_ptr < CUSE_BUF_MAX_PTR) {
+ error = cuse_server_ioctl_copy_locked(pccmd,
+ pchk, cmd == CUSE_IOCTL_READ_DATA);
+ } else {
+ error = cuse_server_data_copy_locked(pccmd,
+ pchk, cmd == CUSE_IOCTL_READ_DATA);
+ }
+ cuse_unlock();
+ break;
+
+ case CUSE_IOCTL_SELWAKEUP:
+ cuse_lock();
+ /*
+ * We don't know which direction caused the event.
+ * Wakeup both!
+ */
+ TAILQ_FOREACH(pcc, &pcs->hcli, entry) {
+ pcc->cflags |= (CUSE_CLI_KNOTE_NEED_READ |
+ CUSE_CLI_KNOTE_NEED_WRITE);
+ }
+ cuse_server_wakeup_locked(pcs);
+ cuse_unlock();
+ break;
+
+ default:
+ error = ENXIO;
+ break;
+ }
+ return (error);
+}
+
+static int
+cuse_server_poll(struct cdev *dev, int events, struct thread *td)
+{
+ return (events & (POLLHUP | POLLPRI | POLLIN |
+ POLLRDNORM | POLLOUT | POLLWRNORM));
+}
+
+static int
+cuse_server_mmap(struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr, int nprot, vm_memattr_t *memattr)
+{
+ uint32_t page_nr = offset / PAGE_SIZE;
+ uint32_t alloc_nr = page_nr / CUSE_ALLOC_PAGES_MAX;
+ struct cuse_memory *mem;
+ struct cuse_server *pcs;
+ uint8_t *ptr;
+ int error;
+
+ if (alloc_nr >= CUSE_ALLOC_UNIT_MAX)
+ return (ENOMEM);
+
+ error = cuse_server_get(&pcs);
+ if (error != 0)
+ pcs = NULL;
+
+ cuse_lock();
+ mem = &cuse_mem[alloc_nr];
+
+ /* try to enforce slight ownership */
+ if ((pcs != NULL) && (mem->owner != pcs)) {
+ cuse_unlock();
+ return (EINVAL);
+ }
+ if (mem->virtaddr == NULL) {
+ cuse_unlock();
+ return (ENOMEM);
+ }
+ if (mem->virtaddr == NBUSY) {
+ cuse_unlock();
+ return (ENOMEM);
+ }
+ page_nr %= CUSE_ALLOC_PAGES_MAX;
+
+ if (page_nr >= mem->page_count) {
+ cuse_unlock();
+ return (ENXIO);
+ }
+ ptr = mem->virtaddr + (page_nr * PAGE_SIZE);
+ cuse_unlock();
+
+ *paddr = vtophys(ptr);
+
+ return (0);
+}
+
+/*------------------------------------------------------------------------*
+ * CUSE CLIENT PART
+ *------------------------------------------------------------------------*/
+static void
+cuse_client_free(void *arg)
+{
+ struct cuse_client *pcc = arg;
+ struct cuse_client_command *pccmd;
+ struct cuse_server *pcs;
+ int n;
+
+ cuse_lock();
+ cuse_client_is_closing(pcc);
+ TAILQ_REMOVE(&pcc->server->hcli, pcc, entry);
+ cuse_unlock();
+
+ for (n = 0; n != CUSE_CMD_MAX; n++) {
+
+ pccmd = &pcc->cmds[n];
+
+ sx_destroy(&pccmd->sx);
+ cv_destroy(&pccmd->cv);
+ }
+
+ pcs = pcc->server;
+
+ free(pcc, M_CUSE);
+
+ /* drop reference on server */
+ cuse_server_free(pcs);
+}
+
+static int
+cuse_client_open(struct cdev *dev, int fflags, int devtype, struct thread *td)
+{
+ struct cuse_client_command *pccmd;
+ struct cuse_server_dev *pcsd;
+ struct cuse_client *pcc;
+ struct cuse_server *pcs;
+ struct cuse_dev *pcd;
+ int error;
+ int n;
+
+ cuse_lock();
+ pcsd = dev->si_drv1;
+ if (pcsd != NULL) {
+ pcs = pcsd->server;
+ pcd = pcsd->user_dev;
+ pcs->refs++;
+ if (pcs->refs < 0) {
+ /* overflow */
+ pcs->refs--;
+ pcsd = NULL;
+ }
+ } else {
+ pcs = NULL;
+ pcd = NULL;
+ }
+ cuse_unlock();
+
+ if (pcsd == NULL)
+ return (EINVAL);
+
+ pcc = malloc(sizeof(*pcc), M_CUSE, M_WAITOK | M_ZERO);
+ if (pcc == NULL) {
+ /* drop reference on server */
+ cuse_server_free(pcs);
+ return (ENOMEM);
+ }
+ if (devfs_set_cdevpriv(pcc, &cuse_client_free)) {
+ printf("Cuse: Cannot set cdevpriv.\n");
+ /* drop reference on server */
+ cuse_server_free(pcs);
+ free(pcc, M_CUSE);
+ return (ENOMEM);
+ }
+ pcc->fflags = fflags;
+ pcc->server_dev = pcsd;
+ pcc->server = pcs;
+
+ for (n = 0; n != CUSE_CMD_MAX; n++) {
+
+ pccmd = &pcc->cmds[n];
+
+ pccmd->sub.dev = pcd;
+ pccmd->sub.command = n;
+ pccmd->client = pcc;
+
+ sx_init(&pccmd->sx, "cuse-client-sx");
+ cv_init(&pccmd->cv, "cuse-client-cv");
+ }
+
+ cuse_lock();
+
+ /* cuse_client_free() assumes that the client is listed somewhere! */
+ /* always enqueue */
+
+ TAILQ_INSERT_TAIL(&pcs->hcli, pcc, entry);
+
+ /* check if server is closing */
+ if ((pcs->is_closing != 0) || (dev->si_drv1 == NULL)) {
+ error = EINVAL;
+ } else {
+ error = 0;
+ }
+ cuse_unlock();
+
+ if (error) {
+ devfs_clear_cdevpriv(); /* XXX bugfix */
+ return (error);
+ }
+ pccmd = &pcc->cmds[CUSE_CMD_OPEN];
+
+ cuse_cmd_lock(pccmd);
+
+ cuse_lock();
+ cuse_client_send_command_locked(pccmd, 0, 0, pcc->fflags, 0);
+
+ error = cuse_client_receive_command_locked(pccmd, 0, 0);
+ cuse_unlock();
+
+ if (error < 0) {
+ error = cuse_convert_error(error);
+ } else {
+ error = 0;
+ }
+
+ cuse_cmd_unlock(pccmd);
+
+ if (error)
+ devfs_clear_cdevpriv(); /* XXX bugfix */
+
+ return (error);
+}
+
+static int
+cuse_client_close(struct cdev *dev, int fflag, int devtype, struct thread *td)
+{
+ struct cuse_client_command *pccmd;
+ struct cuse_client *pcc;
+ int error;
+
+ error = cuse_client_get(&pcc);
+ if (error != 0)
+ return (0);
+
+ pccmd = &pcc->cmds[CUSE_CMD_CLOSE];
+
+ cuse_cmd_lock(pccmd);
+
+ cuse_lock();
+ cuse_client_send_command_locked(pccmd, 0, 0, pcc->fflags, 0);
+
+ error = cuse_client_receive_command_locked(pccmd, 0, 0);
+ cuse_unlock();
+
+ cuse_cmd_unlock(pccmd);
+
+ cuse_lock();
+ cuse_client_is_closing(pcc);
+ cuse_unlock();
+
+ return (0);
+}
+
+static void
+cuse_client_kqfilter_poll(struct cdev *dev, struct cuse_client *pcc)
+{
+ int temp;
+
+ cuse_lock();
+ temp = (pcc->cflags & (CUSE_CLI_KNOTE_HAS_READ |
+ CUSE_CLI_KNOTE_HAS_WRITE));
+ pcc->cflags &= ~(CUSE_CLI_KNOTE_NEED_READ |
+ CUSE_CLI_KNOTE_NEED_WRITE);
+ cuse_unlock();
+
+ if (temp != 0) {
+ /* get the latest polling state from the server */
+ temp = cuse_client_poll(dev, POLLIN | POLLOUT, NULL);
+
+ cuse_lock();
+ if (temp & (POLLIN | POLLOUT)) {
+ if (temp & POLLIN)
+ pcc->cflags |= CUSE_CLI_KNOTE_NEED_READ;
+ if (temp & POLLOUT)
+ pcc->cflags |= CUSE_CLI_KNOTE_NEED_WRITE;
+
+ /* make sure the "knote" gets woken up */
+ cuse_server_wakeup_locked(pcc->server);
+ }
+ cuse_unlock();
+ }
+}
+
+static int
+cuse_client_read(struct cdev *dev, struct uio *uio, int ioflag)
+{
+ struct cuse_client_command *pccmd;
+ struct cuse_client *pcc;
+ int error;
+ int len;
+
+ error = cuse_client_get(&pcc);
+ if (error != 0)
+ return (error);
+
+ pccmd = &pcc->cmds[CUSE_CMD_READ];
+
+ if (uio->uio_segflg != UIO_USERSPACE) {
+ return (EINVAL);
+ }
+ uio->uio_segflg = UIO_NOCOPY;
+
+ cuse_cmd_lock(pccmd);
+
+ while (uio->uio_resid != 0) {
+
+ if (uio->uio_iov->iov_len > CUSE_LENGTH_MAX) {
+ error = ENOMEM;
+ break;
+ }
+
+ len = uio->uio_iov->iov_len;
+
+ cuse_lock();
+ cuse_client_send_command_locked(pccmd,
+ (unsigned long)uio->uio_iov->iov_base,
+ (unsigned long)(unsigned int)len, pcc->fflags, ioflag);
+
+ error = cuse_client_receive_command_locked(pccmd, 0, 0);
+ cuse_unlock();
+
+ if (error < 0) {
+ error = cuse_convert_error(error);
+ break;
+ } else if (error == len) {
+ error = uiomove(NULL, error, uio);
+ if (error)
+ break;
+ } else {
+ error = uiomove(NULL, error, uio);
+ break;
+ }
+ }
+ cuse_cmd_unlock(pccmd);
+
+ uio->uio_segflg = UIO_USERSPACE;/* restore segment flag */
+
+ if (error == EWOULDBLOCK)
+ cuse_client_kqfilter_poll(dev, pcc);
+
+ return (error);
+}
+
+static int
+cuse_client_write(struct cdev *dev, struct uio *uio, int ioflag)
+{
+ struct cuse_client_command *pccmd;
+ struct cuse_client *pcc;
+ int error;
+ int len;
+
+ error = cuse_client_get(&pcc);
+ if (error != 0)
+ return (error);
+
+ pccmd = &pcc->cmds[CUSE_CMD_WRITE];
+
+ if (uio->uio_segflg != UIO_USERSPACE) {
+ return (EINVAL);
+ }
+ uio->uio_segflg = UIO_NOCOPY;
+
+ cuse_cmd_lock(pccmd);
+
+ while (uio->uio_resid != 0) {
+
+ if (uio->uio_iov->iov_len > CUSE_LENGTH_MAX) {
+ error = ENOMEM;
+ break;
+ }
+
+ len = uio->uio_iov->iov_len;
+
+ cuse_lock();
+ cuse_client_send_command_locked(pccmd,
+ (unsigned long)uio->uio_iov->iov_base,
+ (unsigned long)(unsigned int)len, pcc->fflags, ioflag);
+
+ error = cuse_client_receive_command_locked(pccmd, 0, 0);
+ cuse_unlock();
+
+ if (error < 0) {
+ error = cuse_convert_error(error);
+ break;
+ } else if (error == len) {
+ error = uiomove(NULL, error, uio);
+ if (error)
+ break;
+ } else {
+ error = uiomove(NULL, error, uio);
+ break;
+ }
+ }
+ cuse_cmd_unlock(pccmd);
+
+ uio->uio_segflg = UIO_USERSPACE;/* restore segment flag */
+
+ if (error == EWOULDBLOCK)
+ cuse_client_kqfilter_poll(dev, pcc);
+
+ return (error);
+}
+
+int
+cuse_client_ioctl(struct cdev *dev, unsigned long cmd,
+ caddr_t data, int fflag, struct thread *td)
+{
+ struct cuse_client_command *pccmd;
+ struct cuse_client *pcc;
+ int error;
+ int len;
+
+ error = cuse_client_get(&pcc);
+ if (error != 0)
+ return (error);
+
+ len = IOCPARM_LEN(cmd);
+ if (len > CUSE_BUFFER_MAX)
+ return (ENOMEM);
+
+ pccmd = &pcc->cmds[CUSE_CMD_IOCTL];
+
+ cuse_cmd_lock(pccmd);
+
+ if (cmd & IOC_IN)
+ memcpy(pcc->ioctl_buffer, data, len);
+
+ /*
+ * When the ioctl-length is zero drivers can pass information
+ * through the data pointer of the ioctl. Make sure this information
+ * is forwarded to the driver.
+ */
+
+ cuse_lock();
+ cuse_client_send_command_locked(pccmd,
+ (len == 0) ? *(long *)data : CUSE_BUF_MIN_PTR,
+ (unsigned long)cmd, pcc->fflags,
+ (fflag & O_NONBLOCK) ? IO_NDELAY : 0);
+
+ error = cuse_client_receive_command_locked(pccmd, data, len);
+ cuse_unlock();
+
+ if (error < 0) {
+ error = cuse_convert_error(error);
+ } else {
+ error = 0;
+ }
+
+ if (cmd & IOC_OUT)
+ memcpy(data, pcc->ioctl_buffer, len);
+
+ cuse_cmd_unlock(pccmd);
+
+ if (error == EWOULDBLOCK)
+ cuse_client_kqfilter_poll(dev, pcc);
+
+ return (error);
+}
+
+static int
+cuse_client_poll(struct cdev *dev, int events, struct thread *td)
+{
+ struct cuse_client_command *pccmd;
+ struct cuse_client *pcc;
+ unsigned long temp;
+ int error;
+ int revents;
+
+ error = cuse_client_get(&pcc);
+ if (error != 0)
+ return (POLLNVAL);
+
+ temp = 0;
+
+ if (events & (POLLPRI | POLLIN | POLLRDNORM))
+ temp |= CUSE_POLL_READ;
+
+ if (events & (POLLOUT | POLLWRNORM))
+ temp |= CUSE_POLL_WRITE;
+
+ if (events & POLLHUP)
+ temp |= CUSE_POLL_ERROR;
+
+ pccmd = &pcc->cmds[CUSE_CMD_POLL];
+
+ cuse_cmd_lock(pccmd);
+
+ /* Need to selrecord() first to not loose any events. */
+ if (temp != 0 && td != NULL)
+ selrecord(td, &pcc->server->selinfo);
+
+ cuse_lock();
+ cuse_client_send_command_locked(pccmd,
+ 0, temp, pcc->fflags, IO_NDELAY);
+
+ error = cuse_client_receive_command_locked(pccmd, 0, 0);
+ cuse_unlock();
+
+ if (error < 0) {
+ revents = POLLNVAL;
+ } else {
+ revents = 0;
+ if (error & CUSE_POLL_READ)
+ revents |= (events & (POLLPRI | POLLIN | POLLRDNORM));
+ if (error & CUSE_POLL_WRITE)
+ revents |= (events & (POLLOUT | POLLWRNORM));
+ if (error & CUSE_POLL_ERROR)
+ revents |= (events & POLLHUP);
+ }
+
+ cuse_cmd_unlock(pccmd);
+
+ return (revents);
+}
+
+static int
+cuse_client_mmap(struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr, int nprot, vm_memattr_t *memattr)
+{
+ uint32_t page_nr = offset / PAGE_SIZE;
+ uint32_t alloc_nr = page_nr / CUSE_ALLOC_PAGES_MAX;
+ struct cuse_memory *mem;
+ struct cuse_server *pcs;
+ struct cuse_client *pcc;
+ uint8_t *ptr;
+ int error;
+
+ if (alloc_nr >= CUSE_ALLOC_UNIT_MAX)
+ return (ENOMEM);
+
+ error = cuse_client_get(&pcc);
+ if (error != 0)
+ pcs = NULL;
+ else
+ pcs = pcc->server;
+
+ cuse_lock();
+ mem = &cuse_mem[alloc_nr];
+
+ /* try to enforce slight ownership */
+ if ((pcs != NULL) && (mem->owner != pcs)) {
+ cuse_unlock();
+ return (EINVAL);
+ }
+ if (mem->virtaddr == NULL) {
+ cuse_unlock();
+ return (ENOMEM);
+ }
+ if (mem->virtaddr == NBUSY) {
+ cuse_unlock();
+ return (ENOMEM);
+ }
+ page_nr %= CUSE_ALLOC_PAGES_MAX;
+
+ if (page_nr >= mem->page_count) {
+ cuse_unlock();
+ return (ENXIO);
+ }
+ ptr = mem->virtaddr + (page_nr * PAGE_SIZE);
+ cuse_unlock();
+
+ *paddr = vtophys(ptr);
+
+ return (0);
+}
+
+static void
+cuse_client_kqfilter_read_detach(struct knote *kn)
+{
+ struct cuse_client *pcc;
+
+ cuse_lock();
+ pcc = kn->kn_hook;
+ knlist_remove(&pcc->server->selinfo.si_note, kn, 1);
+ cuse_unlock();
+}
+
+static void
+cuse_client_kqfilter_write_detach(struct knote *kn)
+{
+ struct cuse_client *pcc;
+
+ cuse_lock();
+ pcc = kn->kn_hook;
+ knlist_remove(&pcc->server->selinfo.si_note, kn, 1);
+ cuse_unlock();
+}
+
+static int
+cuse_client_kqfilter_read_event(struct knote *kn, long hint)
+{
+ struct cuse_client *pcc;
+
+ mtx_assert(&cuse_mtx, MA_OWNED);
+
+ pcc = kn->kn_hook;
+ return ((pcc->cflags & CUSE_CLI_KNOTE_NEED_READ) ? 1 : 0);
+}
+
+static int
+cuse_client_kqfilter_write_event(struct knote *kn, long hint)
+{
+ struct cuse_client *pcc;
+
+ mtx_assert(&cuse_mtx, MA_OWNED);
+
+ pcc = kn->kn_hook;
+ return ((pcc->cflags & CUSE_CLI_KNOTE_NEED_WRITE) ? 1 : 0);
+}
+
+static int
+cuse_client_kqfilter(struct cdev *dev, struct knote *kn)
+{
+ struct cuse_client *pcc;
+ struct cuse_server *pcs;
+ int error;
+
+ error = cuse_client_get(&pcc);
+ if (error != 0)
+ return (error);
+
+ cuse_lock();
+ pcs = pcc->server;
+ switch (kn->kn_filter) {
+ case EVFILT_READ:
+ pcc->cflags |= CUSE_CLI_KNOTE_HAS_READ;
+ kn->kn_hook = pcc;
+ kn->kn_fop = &cuse_client_kqfilter_read_ops;
+ knlist_add(&pcs->selinfo.si_note, kn, 1);
+ break;
+ case EVFILT_WRITE:
+ pcc->cflags |= CUSE_CLI_KNOTE_HAS_WRITE;
+ kn->kn_hook = pcc;
+ kn->kn_fop = &cuse_client_kqfilter_write_ops;
+ knlist_add(&pcs->selinfo.si_note, kn, 1);
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+ cuse_unlock();
+
+ if (error == 0)
+ cuse_client_kqfilter_poll(dev, pcc);
+ return (error);
+}
diff --git a/sys/fs/cuse/cuse_defs.h b/sys/fs/cuse/cuse_defs.h
new file mode 100644
index 0000000..0134bfe
--- /dev/null
+++ b/sys/fs/cuse/cuse_defs.h
@@ -0,0 +1,86 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2010-2012 Hans Petter Selasky. 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 THE 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 THE 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.
+ */
+
+#ifndef _CUSE_DEFS_H_
+#define _CUSE_DEFS_H_
+
+#define CUSE_VERSION 0x000122
+
+#define CUSE_ERR_NONE 0
+#define CUSE_ERR_BUSY -1
+#define CUSE_ERR_WOULDBLOCK -2
+#define CUSE_ERR_INVALID -3
+#define CUSE_ERR_NO_MEMORY -4
+#define CUSE_ERR_FAULT -5
+#define CUSE_ERR_SIGNAL -6
+#define CUSE_ERR_OTHER -7
+#define CUSE_ERR_NOT_LOADED -8
+
+#define CUSE_POLL_NONE 0
+#define CUSE_POLL_READ 1
+#define CUSE_POLL_WRITE 2
+#define CUSE_POLL_ERROR 4
+
+#define CUSE_FFLAG_NONE 0
+#define CUSE_FFLAG_READ 1
+#define CUSE_FFLAG_WRITE 2
+#define CUSE_FFLAG_NONBLOCK 4
+
+#define CUSE_DBG_NONE 0
+#define CUSE_DBG_FULL 1
+
+/* maximum data transfer length */
+#define CUSE_LENGTH_MAX 0x7FFFFFFFU
+
+enum {
+ CUSE_CMD_NONE,
+ CUSE_CMD_OPEN,
+ CUSE_CMD_CLOSE,
+ CUSE_CMD_READ,
+ CUSE_CMD_WRITE,
+ CUSE_CMD_IOCTL,
+ CUSE_CMD_POLL,
+ CUSE_CMD_SIGNAL,
+ CUSE_CMD_SYNC,
+ CUSE_CMD_MAX,
+};
+
+#define CUSE_MAKE_ID(a,b,c,u) ((((a) & 0x7F) << 24)| \
+ (((b) & 0xFF) << 16)|(((c) & 0xFF) << 8)|((u) & 0xFF))
+
+#define CUSE_ID_MASK 0x7FFFFF00U
+
+/*
+ * The following ID's are defined:
+ * ===============================
+ */
+#define CUSE_ID_DEFAULT(what) CUSE_MAKE_ID(0,0,what,0)
+#define CUSE_ID_WEBCAMD(what) CUSE_MAKE_ID('W','C',what,0) /* Used by Webcamd. */
+#define CUSE_ID_SUNDTEK(what) CUSE_MAKE_ID('S','K',what,0) /* Used by Sundtek. */
+#define CUSE_ID_CX88(what) CUSE_MAKE_ID('C','X',what,0) /* Used by cx88 driver. */
+#define CUSE_ID_UHIDD(what) CUSE_MAKE_ID('U','D',what,0) /* Used by uhidd. */
+
+#endif /* _CUSE_DEFS_H_ */
diff --git a/sys/fs/cuse/cuse_ioctl.h b/sys/fs/cuse/cuse_ioctl.h
new file mode 100644
index 0000000..8e1867c
--- /dev/null
+++ b/sys/fs/cuse/cuse_ioctl.h
@@ -0,0 +1,88 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2014 Hans Petter Selasky. 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 THE 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 THE 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.
+ */
+
+#ifndef _CUSE_IOCTL_H_
+#define _CUSE_IOCTL_H_
+
+#include <sys/ioccom.h>
+#include <sys/types.h>
+
+#define CUSE_BUFFER_MAX PAGE_SIZE
+#define CUSE_DEVICES_MAX 64 /* units */
+#define CUSE_BUF_MIN_PTR 0x10000UL
+#define CUSE_BUF_MAX_PTR 0x20000UL
+#define CUSE_ALLOC_UNIT_MAX 128 /* units */
+#define CUSE_ALLOC_PAGES_MAX (((16UL * 1024UL * 1024UL) + PAGE_SIZE - 1) / PAGE_SIZE)
+
+struct cuse_dev;
+
+struct cuse_data_chunk {
+ unsigned long local_ptr;
+ unsigned long peer_ptr;
+ unsigned long length;
+};
+
+struct cuse_alloc_info {
+ unsigned long page_count;
+ unsigned long alloc_nr;
+};
+
+struct cuse_command {
+ struct cuse_dev *dev;
+ unsigned long fflags;
+ unsigned long per_file_handle;
+ unsigned long data_pointer;
+ unsigned long argument;
+ unsigned long command; /* see CUSE_CMD_XXX */
+};
+
+struct cuse_create_dev {
+ struct cuse_dev *dev;
+ uid_t user_id;
+ gid_t group_id;
+ int permissions;
+ char devname[80]; /* /dev/xxxxx */
+};
+
+/* Definition of internal IOCTLs for /dev/cuse */
+
+#define CUSE_IOCTL_GET_COMMAND _IOR('C', 0, struct cuse_command)
+#define CUSE_IOCTL_WRITE_DATA _IOW('C', 1, struct cuse_data_chunk)
+#define CUSE_IOCTL_READ_DATA _IOW('C', 2, struct cuse_data_chunk)
+#define CUSE_IOCTL_SYNC_COMMAND _IOW('C', 3, int)
+#define CUSE_IOCTL_GET_SIG _IOR('C', 4, int)
+#define CUSE_IOCTL_ALLOC_MEMORY _IOW('C', 5, struct cuse_alloc_info)
+#define CUSE_IOCTL_FREE_MEMORY _IOW('C', 6, struct cuse_alloc_info)
+#define CUSE_IOCTL_SET_PFH _IOW('C', 7, unsigned long)
+#define CUSE_IOCTL_CREATE_DEV _IOW('C', 8, struct cuse_create_dev)
+#define CUSE_IOCTL_DESTROY_DEV _IOW('C', 9, struct cuse_dev *)
+#define CUSE_IOCTL_ALLOC_UNIT _IOR('C',10, int)
+#define CUSE_IOCTL_FREE_UNIT _IOW('C',11, int)
+#define CUSE_IOCTL_SELWAKEUP _IOW('C',12, int)
+#define CUSE_IOCTL_ALLOC_UNIT_BY_ID _IOWR('C',13, int)
+#define CUSE_IOCTL_FREE_UNIT_BY_ID _IOWR('C',14, int)
+
+#endif /* _CUSE_IOCTL_H_ */
diff --git a/sys/modules/cuse/Makefile b/sys/modules/cuse/Makefile
new file mode 100644
index 0000000..049eb7e
--- /dev/null
+++ b/sys/modules/cuse/Makefile
@@ -0,0 +1,32 @@
+# $FreeBSD$
+#
+# Copyright (c) 2010 Hans Petter Selasky. 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 THE 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 THE 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.
+#
+
+.PATH: ${.CURDIR}/../../fs/cuse
+
+KMOD= cuse
+SRCS= cuse.c device_if.h bus_if.h vnode_if.h opt_compat.h
+
+.include <bsd.kmod.mk>
OpenPOWER on IntegriCloud