diff --git a/lib/libcuse/cuse.3 b/lib/libcuse/cuse.3 index aab8ebb4d4f4..0aff43ac2155 100644 --- a/lib/libcuse/cuse.3 +++ b/lib/libcuse/cuse.3 @@ -1,391 +1,391 @@ .\" $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 April 17, 2019 .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" +.Fn "cuse_vmalloc" "unsigned 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 . This function is NULL safe. 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_ERR_NO_DEVICE 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_FFLAG_COMPAT32 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 index d502c5bd5415..f82401b98bf3 100644 --- a/lib/libcuse/cuse.h +++ b/lib/libcuse/cuse.h @@ -1,97 +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 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); +void *cuse_vmalloc(unsigned); 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 index ec300add5903..2653ed3067d5 100644 --- a/lib/libcuse/cuse_lib.c +++ b/lib/libcuse/cuse_lib.c @@ -1,795 +1,795 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2010-2022 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 int f_cuse = -1; static pthread_mutex_t m_cuse; static TAILQ_HEAD(, cuse_dev) h_cuse __guarded_by(m_cuse); static TAILQ_HEAD(, cuse_dev_entered) h_cuse_entered __guarded_by(m_cuse); static struct cuse_vm_allocation a_cuse[CUSE_ALLOC_UNIT_MAX] __guarded_by(m_cuse); #define CUSE_LOCK() \ pthread_mutex_lock(&m_cuse) #define CUSE_UNLOCK() \ 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; unsigned long n; CUSE_LOCK(); for (n = remainder = 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)) { remainder = (ptr - ptr_min); break; } } CUSE_UNLOCK(); return ((n << CUSE_ALLOC_UNIT_SHIFT) + remainder); } void * -cuse_vmalloc(int size) +cuse_vmalloc(unsigned size) { struct cuse_alloc_info info; unsigned long pgsize; unsigned long x; unsigned long m; unsigned long n; void *ptr; int error; /* some sanity checks */ - if (f_cuse < 0 || size < 1 || (unsigned long)size > CUSE_ALLOC_BYTES_MAX) + if (f_cuse < 0 || size < 1 || size > CUSE_ALLOC_BYTES_MAX) return (NULL); memset(&info, 0, sizeof(info)); pgsize = getpagesize(); info.page_count = howmany(size, pgsize); /* compute how many units the allocation needs */ m = howmany(size, 1 << CUSE_ALLOC_UNIT_SHIFT); if (m == 0 || m > CUSE_ALLOC_UNIT_MAX) return (NULL); CUSE_LOCK(); for (n = 0; n <= CUSE_ALLOC_UNIT_MAX - m; ) { if (a_cuse[n].size != 0) { /* skip to next available unit, depending on allocation size */ n += howmany(a_cuse[n].size, 1 << CUSE_ALLOC_UNIT_SHIFT); continue; } /* check if there are "m" free units ahead */ for (x = 1; x != m; x++) { if (a_cuse[n + x].size != 0) break; } if (x != m) { /* skip to next available unit, if any */ n += x + 1; continue; } /* reserve this unit by setting the size to a non-zero value */ a_cuse[n].size = size; CUSE_UNLOCK(); info.alloc_nr = n; error = ioctl(f_cuse, CUSE_IOCTL_ALLOC_MEMORY, &info); if (error == 0) { ptr = mmap(NULL, info.page_count * pgsize, PROT_READ | PROT_WRITE, MAP_SHARED, f_cuse, n << CUSE_ALLOC_UNIT_SHIFT); if (ptr != MAP_FAILED) { CUSE_LOCK(); a_cuse[n].ptr = ptr; CUSE_UNLOCK(); return (ptr); /* success */ } (void) ioctl(f_cuse, CUSE_IOCTL_FREE_MEMORY, &info); } CUSE_LOCK(); a_cuse[n].size = 0; n++; } 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_vm_allocation temp; struct cuse_alloc_info info; int error; int n; if (f_cuse < 0 || ptr == NULL) return; CUSE_LOCK(); for (n = 0; n != CUSE_ALLOC_UNIT_MAX; n++) { if (a_cuse[n].ptr != ptr) continue; temp = a_cuse[n]; CUSE_UNLOCK(); munmap(temp.ptr, temp.size); memset(&info, 0, sizeof(info)); info.alloc_nr = n; error = ioctl(f_cuse, CUSE_IOCTL_FREE_MEMORY, &info); if (error != 0) { /* ignore any errors */ DPRINTF("Freeing memory failed: %d\n", errno); } 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 = (uintptr_t)src; info.peer_ptr = (uintptr_t)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 = (uintptr_t)dst; info.peer_ptr = (uintptr_t)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); }