summaryrefslogtreecommitdiffstats
path: root/lib/libcuse
diff options
context:
space:
mode:
authorsjg <sjg@FreeBSD.org>2014-08-19 06:50:54 +0000
committersjg <sjg@FreeBSD.org>2014-08-19 06:50:54 +0000
commitd7cd1d425cc1ea9451fa235e3af9b6625c3e0de2 (patch)
treeb04f4bd7cd887f50e7d98af35f46b9834ff86c80 /lib/libcuse
parent3c8e37b1d04827f33c0c9a7594bd1b1ef7cdb3d3 (diff)
parent4fbde208c6460d576f64d6dc3cdc6cab085a4283 (diff)
downloadFreeBSD-src-d7cd1d425cc1ea9451fa235e3af9b6625c3e0de2.zip
FreeBSD-src-d7cd1d425cc1ea9451fa235e3af9b6625c3e0de2.tar.gz
Merge head from 7/28
Diffstat (limited to 'lib/libcuse')
-rw-r--r--lib/libcuse/Makefile69
-rw-r--r--lib/libcuse/Makefile.depend21
-rw-r--r--lib/libcuse/cuse.3388
-rw-r--r--lib/libcuse/cuse.h97
-rw-r--r--lib/libcuse/cuse_lib.c800
5 files changed, 1375 insertions, 0 deletions
diff --git a/lib/libcuse/Makefile b/lib/libcuse/Makefile
new file mode 100644
index 0000000..6269cbe
--- /dev/null
+++ b/lib/libcuse/Makefile
@@ -0,0 +1,69 @@
+# $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_alloc_unit_number_by_id.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_dev_set_priv0.3
+MLINKS+= cuse.3 cuse_dev_set_priv1.3
+MLINKS+= cuse.3 cuse_free_unit_number.3
+MLINKS+= cuse.3 cuse_free_unit_number_by_id.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_vmoffset.3
+MLINKS+= cuse.3 cuse_wait_and_process.3
+
+.include <bsd.lib.mk>
diff --git a/lib/libcuse/Makefile.depend b/lib/libcuse/Makefile.depend
new file mode 100644
index 0000000..37acbe0
--- /dev/null
+++ b/lib/libcuse/Makefile.depend
@@ -0,0 +1,21 @@
+# Autogenerated - do NOT edit!
+
+DEP_RELDIR := ${_PARSEDIR:S,${SRCTOP}/,,}
+
+DIRDEPS = \
+ gnu/lib/csu \
+ gnu/lib/libgcc \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/libc \
+ lib/libcompiler_rt \
+ lib/libthr \
+ usr.bin/xinstall.host \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/lib/libcuse/cuse.3 b/lib/libcuse/cuse.3
new file mode 100644
index 0000000..6d8d2a3
--- /dev/null
+++ b/lib/libcuse/cuse.3
@@ -0,0 +1,388 @@
+.\" $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 June 6, 2014
+.Dt CUSE 3
+.Os
+.Sh NAME
+.Nm libcuse
+.Nd "Userland character device library"
+.Sh LIBRARY
+.Lb libcuse
+.Sh SYNOPSIS
+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
+.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
+.Dv CUSE_ERR_XXX
+for known error codes.
+If the cuse kernel module is not loaded,
+.Dv 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
+.Dv 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
+.Dv CUSE_ERR_XXX
+for known error codes.
+.Pp
+.Ft "int"
+.Fn "cuse_alloc_unit_number_by_id" "int *" "int id"
+This function stores a unique 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
+.Fn CUSE_ID_XXX
+macros for more information.
+This function returns 0 on success or a negative value on failure.
+See
+.Dv 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
+.Dv 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
+.Dv 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
+.Xr mmap 2 .
+This function returns a valid data pointer on success or
+.Dv 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
+.Fn cuse_vmalloc .
+Else this function returns zero.
+.Pp
+.Ft "void"
+.Fn "cuse_vmfree" "void *"
+This function frees memory allocated by
+.Fn cuse_vmalloc .
+Note that the
+cuse library will internally not free the memory until the
+.Fn cuse_uninit
+function is called and that the number of unique
+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
+.Dv 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 parallel 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
+.Dv 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
+.Fn cuse_copy_out
+and
+.Fn 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"
+Returns the current local state.
+See
+.Fn cuse_set_local .
+.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
+.Fn cm_read ,
+.Fn cm_write ,
+and
+.Fn cm_ioctl
+callback functions.
+These functions return 0 on success or a negative value on failure.
+See
+.Dv 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
+.Dv 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
+.Dv CUSE_CMD_XXX
+value.
+The
+.Ar pcmd
+argument
+is allowed to be
+.Dv NULL .
+This function should only be used inside the
+cuse file operation callbacks.
+On success a valid cuse device pointer
+is returned.
+On failure
+.Dv NULL
+is returned.
+.Pp
+.Ft "void"
+.Fn "cuse_poll_wakeup" "void"
+This function will wake up any file pollers.
+.Sh LIBRARY LIMITATIONS
+Transfer lengths for
+.Fn read ,
+.Fn write ,
+.Fn cuse_copy_in ,
+and
+.Fn cuse_copy_out
+should not exceed what can fit into a 32-bit signed integer and is
+defined by the
+.Fn CUSE_LENGTH_MAX
+macro.
+Transfer lengths for ioctls should not exceed what is defined by the
+.Fn CUSE_BUFFER_MAX
+macro.
+.Sh LIBRARY CALLBACK METHODS
+In general fflags are defined by
+.Dv CUSE_FFLAG_XXX
+and errors are defined by
+.Dv 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 function returns a
+.Dv CUSE_ERR_XXX
+value.
+.Pp
+.Ft "int"
+.Fn "cuse_close_t" "struct cuse_dev *" "int fflags"
+This function returns a
+.Dv CUSE_ERR_XXX
+value.
+.Pp
+.Ft "int"
+.Fn "cuse_read_t" "struct cuse_dev *" "int fflags" "void *peer_ptr" "int len"
+This function returns a
+.Dv CUSE_ERR_XXX
+value in case of failure or the
+actually transferred length in case of success.
+.Fn cuse_copy_in
+and
+.Fn cuse_copy_out
+must be used to transfer data to and from the
+.Ar peer_ptr .
+.Pp
+.Ft "int"
+.Fn "cuse_write_t" "struct cuse_dev *" "int fflags" "const void *peer_ptr" "int len"
+This function returns a
+.Dv CUSE_ERR_XXX
+value in case of failure or the
+actually transferred length in case of success.
+.Fn cuse_copy_in
+and
+.Fn cuse_copy_out
+must be used to transfer data to and from the
+.Ar peer_ptr .
+.Pp
+.Ft "int"
+.Fn "cuse_ioctl_t" "struct cuse_dev *" "int fflags" "unsigned long cmd" "void *peer_data"
+This function returns a
+.Dv CUSE_ERR_XXX
+value in case of failure or zero
+in case of success.
+.Fn cuse_copy_in
+and
+.Fn cuse_copy_out
+must be used to
+transfer data to and from the
+.Ar peer_data .
+.Pp
+.Ft "int"
+.Fn "cuse_poll_t" "struct cuse_dev *" "int fflags" "int events"
+This function returns a mask of
+.Dv CUSE_POLL_XXX
+values in case of failure and success.
+The events argument is also a mask of
+.Dv CUSE_POLL_XXX
+values.
+.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 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..9d8352f
--- /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);
+}
OpenPOWER on IntegriCloud