diff options
author | sjg <sjg@FreeBSD.org> | 2014-08-19 06:50:54 +0000 |
---|---|---|
committer | sjg <sjg@FreeBSD.org> | 2014-08-19 06:50:54 +0000 |
commit | d7cd1d425cc1ea9451fa235e3af9b6625c3e0de2 (patch) | |
tree | b04f4bd7cd887f50e7d98af35f46b9834ff86c80 /lib/libcuse | |
parent | 3c8e37b1d04827f33c0c9a7594bd1b1ef7cdb3d3 (diff) | |
parent | 4fbde208c6460d576f64d6dc3cdc6cab085a4283 (diff) | |
download | FreeBSD-src-d7cd1d425cc1ea9451fa235e3af9b6625c3e0de2.zip FreeBSD-src-d7cd1d425cc1ea9451fa235e3af9b6625c3e0de2.tar.gz |
Merge head from 7/28
Diffstat (limited to 'lib/libcuse')
-rw-r--r-- | lib/libcuse/Makefile | 69 | ||||
-rw-r--r-- | lib/libcuse/Makefile.depend | 21 | ||||
-rw-r--r-- | lib/libcuse/cuse.3 | 388 | ||||
-rw-r--r-- | lib/libcuse/cuse.h | 97 | ||||
-rw-r--r-- | lib/libcuse/cuse_lib.c | 800 |
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); +} |