From 448f5f73dcc7efe69df16b6a875b0cf0c6f41ae3 Mon Sep 17 00:00:00 2001 From: Jilles Tjoelker Date: Sun, 11 May 2014 13:48:21 +0000 Subject: include: Remove checks for __BSD_VISIBLE where redundant with __XSI_VISIBLE or __POSIX_VISIBLE. Whenever sets __BSD_VISIBLE to non-zero, it also sets __POSIX_VISIBLE and __XSI_VISIBLE to the newest version supported. No functional change is intended. --- include/dirent.h | 2 +- include/grp.h | 6 +++--- include/setjmp.h | 4 ++-- include/signal.h | 2 +- include/stdio.h | 8 ++++---- include/string.h | 6 +++--- include/termios.h | 2 +- include/unistd.h | 6 +++--- include/wchar.h | 2 +- 9 files changed, 19 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/include/dirent.h b/include/dirent.h index f43210c7ddb67..c77d844e399b1 100644 --- a/include/dirent.h +++ b/include/dirent.h @@ -40,7 +40,7 @@ #include #include -#if __BSD_VISIBLE || __XSI_VISIBLE +#if __XSI_VISIBLE /* * XXX this is probably illegal in the __XSI_VISIBLE case, but brings us closer * to the specification. diff --git a/include/grp.h b/include/grp.h index e7f65ecad636a..b10fd0f8ad36e 100644 --- a/include/grp.h +++ b/include/grp.h @@ -61,7 +61,7 @@ struct group { }; __BEGIN_DECLS -#if __BSD_VISIBLE || __POSIX_VISIBLE >= 200112 || __XSI_VISIBLE +#if __POSIX_VISIBLE >= 200112 || __XSI_VISIBLE void endgrent(void); struct group *getgrent(void); #endif @@ -74,11 +74,11 @@ int pwcache_groupdb(int (*)(int), void (*)(void), struct group * (*)(const char *), struct group * (*)(gid_t)); #endif -#if __BSD_VISIBLE || __XSI_VISIBLE +#if __XSI_VISIBLE /* XXX IEEE Std 1003.1, 2003 specifies `void setgrent(void)' */ int setgrent(void); #endif -#if __BSD_VISIBLE || __POSIX_VISIBLE >= 200112 || __XSI_VISIBLE +#if __POSIX_VISIBLE >= 200112 || __XSI_VISIBLE int getgrgid_r(gid_t, struct group *, char *, size_t, struct group **); int getgrnam_r(const char *, struct group *, char *, size_t, diff --git a/include/setjmp.h b/include/setjmp.h index bf03f6fbb43ae..598e07a77bc4e 100644 --- a/include/setjmp.h +++ b/include/setjmp.h @@ -44,7 +44,7 @@ #include __BEGIN_DECLS -#if __BSD_VISIBLE || __XSI_VISIBLE >= 600 +#if __XSI_VISIBLE >= 600 void _longjmp(jmp_buf, int) __dead2; int _setjmp(jmp_buf) __returns_twice; #endif @@ -53,7 +53,7 @@ void longjmp(jmp_buf, int) __dead2; void longjmperror(void); #endif int setjmp(jmp_buf) __returns_twice; -#if __BSD_VISIBLE || __POSIX_VISIBLE || __XSI_VISIBLE +#if __POSIX_VISIBLE || __XSI_VISIBLE void siglongjmp(sigjmp_buf, int) __dead2; int sigsetjmp(sigjmp_buf, int) __returns_twice; #endif diff --git a/include/signal.h b/include/signal.h index e7447dfd053d1..dca19aaa86131 100644 --- a/include/signal.h +++ b/include/signal.h @@ -108,7 +108,7 @@ int xsi_sigpause(int); int siginterrupt(int, int); #endif -#if __POSIX_VISIBLE >= 200809 || __BSD_VISIBLE +#if __POSIX_VISIBLE >= 200809 void psignal(unsigned int, const char *); #endif diff --git a/include/stdio.h b/include/stdio.h index 19b1e20f66bec..cebc1241359dd 100644 --- a/include/stdio.h +++ b/include/stdio.h @@ -47,7 +47,7 @@ typedef __size_t size_t; #define _SIZE_T_DECLARED #endif -#if __BSD_VISIBLE || __POSIX_VISIBLE >= 200809 +#if __POSIX_VISIBLE >= 200809 #ifndef _OFF_T_DECLARED #define _OFF_T_DECLARED typedef __off_t off_t; @@ -58,7 +58,7 @@ typedef __ssize_t ssize_t; #endif #endif -#if __BSD_VISIBLE || __POSIX_VISIBLE >= 200112 || __XSI_VISIBLE +#if __POSIX_VISIBLE >= 200112 || __XSI_VISIBLE #ifndef _VA_LIST_DECLARED typedef __va_list va_list; #define _VA_LIST_DECLARED @@ -342,7 +342,7 @@ int putw(int, FILE *); char *tempnam(const char *, const char *); #endif -#if __BSD_VISIBLE || __POSIX_VISIBLE >= 200809 +#if __POSIX_VISIBLE >= 200809 FILE *fmemopen(void * __restrict, size_t, const char * __restrict); ssize_t getdelim(char ** __restrict, size_t * __restrict, int, FILE * __restrict); @@ -387,7 +387,7 @@ ssize_t getline(char ** __restrict, size_t * __restrict, FILE * __restrict); int (dprintf)(int, const char * __restrict, ...); #endif -#endif /* __BSD_VISIBLE || __POSIX_VISIBLE >= 200809 */ +#endif /* __POSIX_VISIBLE >= 200809 */ /* * Routines that are purely local. diff --git a/include/string.h b/include/string.h index ceffcf6aeaac1..af8b0d46c86cd 100644 --- a/include/string.h +++ b/include/string.h @@ -65,7 +65,7 @@ void *memmem(const void *, size_t, const void *, size_t) __pure; #endif void *memmove(void *, const void *, size_t); void *memset(void *, int, size_t); -#if __POSIX_VISIBLE >= 200809 || __BSD_VISIBLE +#if __POSIX_VISIBLE >= 200809 char *stpcpy(char * __restrict, const char * __restrict); char *stpncpy(char * __restrict, const char * __restrict, size_t); #endif @@ -99,7 +99,7 @@ void strmode(int, char *); char *strncat(char * __restrict, const char * __restrict, size_t); int strncmp(const char *, const char *, size_t) __pure; char *strncpy(char * __restrict, const char * __restrict, size_t); -#if __POSIX_VISIBLE >= 200809 || __BSD_VISIBLE +#if __POSIX_VISIBLE >= 200809 char *strndup(const char *, size_t) __malloc_like; size_t strnlen(const char *, size_t) __pure; #endif @@ -111,7 +111,7 @@ char *strrchr(const char *, int) __pure; #if __BSD_VISIBLE char *strsep(char **, const char *); #endif -#if __POSIX_VISIBLE >= 200809 || __BSD_VISIBLE +#if __POSIX_VISIBLE >= 200809 char *strsignal(int); #endif size_t strspn(const char *, const char *) __pure; diff --git a/include/termios.h b/include/termios.h index e41da9fea12ab..ed8e328d7fd98 100644 --- a/include/termios.h +++ b/include/termios.h @@ -81,7 +81,7 @@ int tcflow(int, int); int tcflush(int, int); int tcsendbreak(int, int); -#if __POSIX_VISIBLE >= 200112 || __BSD_VISIBLE +#if __POSIX_VISIBLE >= 200112 pid_t tcgetsid(int); #endif #if __BSD_VISIBLE diff --git a/include/unistd.h b/include/unistd.h index 5868ba2ed94fb..f1913b6934582 100644 --- a/include/unistd.h +++ b/include/unistd.h @@ -426,7 +426,7 @@ int truncate(const char *, off_t); #endif #endif /* __POSIX_VISIBLE >= 200809 || __XSI_VISIBLE */ -#if __POSIX_VISIBLE >= 200809 || __BSD_VISIBLE +#if __POSIX_VISIBLE >= 200809 int faccessat(int, const char *, int, int); int fchownat(int, const char *, uid_t, gid_t, int); int fexecve(int, char *const [], char *const []); @@ -434,14 +434,14 @@ int linkat(int, const char *, int, const char *, int); ssize_t readlinkat(int, const char * __restrict, char * __restrict, size_t); int symlinkat(const char *, int, const char *); int unlinkat(int, const char *, int); -#endif /* __POSIX_VISIBLE >= 200809 || __BSD_VISIBLE */ +#endif /* __POSIX_VISIBLE >= 200809 */ /* * symlink() was originally in POSIX.1a, which was withdrawn after * being overtaken by events (1003.1-2001). It was in XPG4.2, and of * course has been in BSD since 4.2. */ -#if __POSIX_VISIBLE >= 200112 || __XSI_VISIBLE >= 402 || __BSD_VISIBLE +#if __POSIX_VISIBLE >= 200112 || __XSI_VISIBLE >= 402 int symlink(const char * __restrict, const char * __restrict); #endif diff --git a/include/wchar.h b/include/wchar.h index 8233bdff10147..84a4a976e6bd9 100644 --- a/include/wchar.h +++ b/include/wchar.h @@ -204,7 +204,7 @@ int wcwidth(wchar_t); #define wcwidth(_c) __wcwidth(_c) #endif -#if __POSIX_VISIBLE >= 200809 || __BSD_VISIBLE +#if __POSIX_VISIBLE >= 200809 size_t mbsnrtowcs(wchar_t * __restrict, const char ** __restrict, size_t, size_t, mbstate_t * __restrict); FILE *open_wmemstream(wchar_t **, size_t *); -- cgit v1.3 From 76902266ae6bd08d20532316b9c031e1f34ee232 Mon Sep 17 00:00:00 2001 From: Jilles Tjoelker Date: Sun, 11 May 2014 16:34:17 +0000 Subject: include: Don't expose L_cuserid in strict C standard compliance mode. L_cuserid is supposed to be exposed only for old POSIX, or in the default (expose everything) environment. --- include/stdio.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/stdio.h b/include/stdio.h index cebc1241359dd..512e60e9ac1a7 100644 --- a/include/stdio.h +++ b/include/stdio.h @@ -290,7 +290,7 @@ int vsscanf(const char * __restrict, const char * __restrict, __va_list) /* * Functions defined in all versions of POSIX 1003.1. */ -#if __BSD_VISIBLE || __POSIX_VISIBLE <= 199506 +#if __BSD_VISIBLE || (__POSIX_VISIBLE && __POSIX_VISIBLE <= 199506) #define L_cuserid 17 /* size for cuserid(3); MAXLOGNAME, legacy */ #endif -- cgit v1.3 From fa0f6e62c69e1920680eceb514976ef5c5d8f4da Mon Sep 17 00:00:00 2001 From: Hans Petter Selasky Date: Fri, 23 May 2014 08:46:28 +0000 Subject: Initial import of character device in userspace support for FreeBSD. The CUSE library is a wrapper for the devfs kernel functionality which is exposed through /dev/cuse . In order to function the CUSE kernel code must either be enabled in the kernel configuration file or loaded separately as a module. Currently none of the committed items are connected to the default builds, except for installing the needed header files. The CUSE code will be connected to the default world and kernel builds in a follow-up commit. The CUSE module was written by Hans Petter Selasky, somewhat inspired by similar functionality found in FUSE. The CUSE library can be used for many purposes. Currently CUSE is used when running Linux kernel drivers in user-space, which need to create a character device node to communicate with its applications. CUSE has full support for almost all devfs functionality found in the kernel: - kevents - read - write - ioctl - poll - open - close - mmap - private per file handle data Requested by several people. Also see "multimedia/cuse4bsd-kmod" in ports. --- etc/mtree/BSD.include.dist | 2 + include/Makefile | 1 + lib/libcuse/Makefile | 64 ++ lib/libcuse/cuse.3 | 393 ++++++++++ lib/libcuse/cuse.h | 97 +++ lib/libcuse/cuse_lib.c | 800 +++++++++++++++++++ sys/conf/files | 1 + sys/fs/cuse/cuse.c | 1866 ++++++++++++++++++++++++++++++++++++++++++++ sys/fs/cuse/cuse_defs.h | 86 ++ sys/fs/cuse/cuse_ioctl.h | 88 +++ sys/modules/cuse/Makefile | 32 + 11 files changed, 3430 insertions(+) create mode 100644 lib/libcuse/Makefile create mode 100644 lib/libcuse/cuse.3 create mode 100644 lib/libcuse/cuse.h create mode 100644 lib/libcuse/cuse_lib.c create mode 100644 sys/fs/cuse/cuse.c create mode 100644 sys/fs/cuse/cuse_defs.h create mode 100644 sys/fs/cuse/cuse_ioctl.h create mode 100644 sys/modules/cuse/Makefile (limited to 'include') diff --git a/etc/mtree/BSD.include.dist b/etc/mtree/BSD.include.dist index 7436eace4b906..bdd628cf05324 100644 --- a/etc/mtree/BSD.include.dist +++ b/etc/mtree/BSD.include.dist @@ -162,6 +162,8 @@ .. .. fs + cuse + .. devfs .. fdescfs diff --git a/include/Makefile b/include/Makefile index 737575fb1aef7..4693c2f874388 100644 --- a/include/Makefile +++ b/include/Makefile @@ -45,6 +45,7 @@ LSUBDIRS= cam/ata cam/scsi \ dev/ic dev/iicbus ${_dev_ieee488} dev/io dev/lmc dev/mfi dev/nvme \ dev/ofw dev/pbio dev/pci ${_dev_powermac_nvram} dev/ppbus dev/smbus \ dev/speaker dev/usb dev/utopia dev/vkbd dev/wi \ + fs/cuse \ fs/devfs fs/fdescfs fs/msdosfs fs/nandfs fs/nfs fs/nullfs \ fs/procfs fs/smbfs fs/udf fs/unionfs \ geom/cache geom/concat geom/eli geom/gate geom/journal geom/label \ diff --git a/lib/libcuse/Makefile b/lib/libcuse/Makefile new file mode 100644 index 0000000000000..db28c22293a9e --- /dev/null +++ b/lib/libcuse/Makefile @@ -0,0 +1,64 @@ +# $FreeBSD$ +# +# Copyright (c) 2010 Hans Petter Selasky. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +LIB= cuse +SHLIB_MAJOR= 1 +SHLIB_MINOR= 0 +SRCS= cuse_lib.c +INCS= cuse.h +MAN= cuse.3 +PTHREAD_LIBS?= -lpthread +CFLAGS+= -D_GNU_SOURCE +.if defined(HAVE_DEBUG) +CFLAGS+= -g +CFLAGS+= -DHAVE_DEBUG +.endif +LDADD+= ${PTHREAD_LIBS} + +MLINKS= +MLINKS+= cuse.3 cuse_alloc_unit_number.3 +MLINKS+= cuse.3 cuse_copy_in.3 +MLINKS+= cuse.3 cuse_copy_out.3 +MLINKS+= cuse.3 cuse_dev_create.3 +MLINKS+= cuse.3 cuse_dev_destroy.3 +MLINKS+= cuse.3 cuse_dev_get_current.3 +MLINKS+= cuse.3 cuse_dev_get_per_file_handle.3 +MLINKS+= cuse.3 cuse_dev_get_priv0.3 +MLINKS+= cuse.3 cuse_dev_get_priv1.3 +MLINKS+= cuse.3 cuse_dev_set_per_file_handle.3 +MLINKS+= cuse.3 cuse_free_unit_number.3 +MLINKS+= cuse.3 cuse_got_peer_signal.3 +MLINKS+= cuse.3 cuse_init.3 +MLINKS+= cuse.3 cuse_poll_wakeup.3 +MLINKS+= cuse.3 cuse_set_local.3 +MLINKS+= cuse.3 cuse_get_local.3 +MLINKS+= cuse.3 cuse_uninit.3 +MLINKS+= cuse.3 cuse_vmalloc.3 +MLINKS+= cuse.3 cuse_is_vmalloc_addr.3 +MLINKS+= cuse.3 cuse_vmfree.3 +MLINKS+= cuse.3 cuse_wait_and_process.3 + +.include diff --git a/lib/libcuse/cuse.3 b/lib/libcuse/cuse.3 new file mode 100644 index 0000000000000..635af2c15fd3b --- /dev/null +++ b/lib/libcuse/cuse.3 @@ -0,0 +1,393 @@ +.\" $FreeBSD$ +.\" +.\" Copyright (c) 2010-2013 Hans Petter Selasky +.\" +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.Dd May 23, 2014 +.Dt CUSE 3 +.Os +.Sh NAME +.Nm libcuse +. +.Nd "Userland character device library" +. +. +.Sh LIBRARY +. +. +Userland character device library (libcuse -lcuse) +. +. +.Sh SYNOPSIS +. +.Pp +To load the required kernel module at boot time, place the following line in +.Xr loader.conf 5 : +.Bd -literal -offset indent +cuse_load="YES" +.Ed +. +.Pp +. +.In cuse.h +. +. +.Sh DESCRIPTION +The +.Nm +library contains functions to create a character device in userspace. The +.Nm +library is thread safe. +. +. +.Sh LIBRARY INITIALISATION / DEINITIALISATION +. +.Pp +. +.Ft "int" +.Fn "cuse_init" "void" +This function initialises +.Nm . +Must be called at the beginning of the program. +This function returns 0 on success or a negative value on failure. +See CUSE_ERR_XXX for known error codes. +If the cuse kernel module is not loaded, CUSE_ERR_NOT_LOADED is +returned. +. +.Pp +. +.Ft "int" +.Fn "cuse_uninit" "void" +Deinitialise +.Nm . +Can be called at the end of the application. +This function returns 0 on success or a negative value on failure. +See CUSE_ERR_XXX for known error codes. +. +. +.Sh UNIT MANAGEMENT +. +.Ft "int" +.Fn "cuse_alloc_unit_number" "int *" +This function stores a uniq system unit number at the pointed +integer loation. +This function returns 0 on success or a negative value on failure. +See CUSE_ERR_XXX for known error codes. +. +.Pp +. +.Ft "int" +.Fn "cuse_alloc_unit_number_by_id" "int *" "int id" +This function stores a uniq system unit number at the pointed +integer loation. +The returned unit number is uniq within the given ID. +Valid ID values are defined by the cuse include file. +See the CUSE_ID_XXX() macros for more information. +This function returns 0 on success or a negative value on failure. +See CUSE_ERR_XXX for known error codes. +. +.Pp +. +.Ft "int" +.Fn "cuse_free_unit_number" "int" +This function frees the given allocated system unit number. +This function returns 0 on success or a negative value on failure. +See CUSE_ERR_XXX for known error codes. +. +.Pp +. +.Ft "int" +.Fn "cuse_free_unit_number_by_id" "int unit" "int id" +This function frees the given allocated system unit number belonging +to the given ID. +If both the unit and id argument is -1, all allocated units will be freed. +This function returns 0 on success or a negative value on failure. +See CUSE_ERR_XXX for known error codes. +. +. +.Sh LIBRARY USAGE +. +. +.Ft "void *" +.Fn "cuse_vmalloc" "int size" +This function allocates +.Ar size +bytes of memory. Only memory allocated by this function can be memory +mapped by mmap(). This function returns a valid data pointer on success or +NULL on failure. +. +.Pp +. +.Ft "int" +.Fn "cuse_is_vmalloc_addr" "void *" +This function returns non-zero if the passed pointer points to a valid +and non-freed allocation, as returned by "cuse_vmalloc()". +Else this function returns zero. +. +.Pp +. +.Ft "void" +.Fn "cuse_vmfree" "void *" +This function frees memory allocated by cuse_vmalloc(). Note that the +cuse library will internally not free the memory until the +cuse_uninit() function is called and that the number of uniq +allocations is limited. +. +. +.Pp +. +.Ft "unsigned long" +.Fn "cuse_vmoffset" "void *" +This function returns the mmap offset that the client must use to +access the allocated memory. +. +.Pp +. +.Ft "struct cuse_dev *" +.Fn "cuse_dev_create" "const struct cuse_methods *mtod" "void *priv0" "void *priv1" "uid_t" "gid_t" "int permission" "const char *fmt" "..." +This function creates a new character device according to the given +parameters. This function returns a valid cuse_dev structure pointer +on success or NULL on failure. The device name can only contain a-z, +A-Z, 0-9, dot, / and underscore characters. +. +.Pp +. +.Ft "void" +.Fn "cuse_dev_destroy" "struct cuse_dev *" +This functions destroys a previously created character device. +. +.Pp +. +. +.Ft "void *" +.Fn "cuse_dev_get_priv0" "struct cuse_dev *" +, +.Ft "void *" +.Fn "cuse_dev_get_priv1" "struct cuse_dev *" +, +.Ft "void" +.Fn "cuse_dev_set_priv0" "struct cuse_dev *" "void *" +, +.Ft "void" +.Fn "cuse_dev_set_priv1" "struct cuse_dev *" "void *" +These functions are used to set and get the private data of the given +cuse device. +. +.Pp +. +.Ft "int" +.Fn "cuse_wait_and_process" "void" +This function will block and do event processing. If parallell I/O is +required multiple threads must be created looping on this +function. +This function returns 0 on success or a negative value on failure. +See CUSE_ERR_XXX for known error codes. +. +.Pp +. +.Ft "void *" +.Fn "cuse_dev_get_per_file_handle" "struct cuse_dev *" +, +.Ft "void" +.Fn "cuse_dev_set_per_file_handle" "struct cuse_dev *" "void *" +These functions are used to set and get the per-file-open specific handle +and should only be used inside the cuse file operation callbacks. +. +.Pp +. +.Ft "void" +.Fn "cuse_set_local" "int" +This function instructs cuse_copy_out() and cuse_copy_in() that the +user pointer is local, if the argument passed to it is non-zero. +Else the user pointer is assumed to be at the peer application. +This function should only be used inside the cuse file operation callbacks. +The value is reset to zero when the given file operation returns, and +does not affect any other file operation callbacks. +. +.Pp +. +.Ft "int" +.Fn "cuse_get_local" "void" +Return current local state. See "cuse_set_local" function. +. +.Pp +. +.Ft "int" +.Fn "cuse_copy_out" "const void *src" "void *peer_dst" "int len" +, +.Ft "int" +.Fn "cuse_copy_in" "const void *peer_src" "void *dst" "int len" +These functions are used to transfer data between the local +application and the peer application. These functions must be used +when operating on the data pointers passed to the cm_read(), +cm_write() and cm_ioctl() callback functions. +These functions return 0 on success or a negative value on failure. +See CUSE_ERR_XXX for known error codes. +. +.Pp +. +.Ft "int" +.Fn "cuse_got_peer_signal" "void" +This function is used to check if a signal has been delivered to the +peer application and should only be used inside the cuse file +operation callbacks. This function returns 0 if a signal has been +delivered to the caller. +Else it returns a negative value. +See CUSE_ERR_XXX for known error codes. +. +.Pp +. +.Ft "struct cuse_dev *" +.Fn "cuse_dev_get_current" "int *pcmd" +This function is used to get the current cuse device pointer and the +currently executing command, by CUSE_CMD_XXX value. The pcmd argument +is allowed to be NULL. This function should only be used inside the +cuse file operation callbacks. On success a valid cuse device pointer +is returned. On failure NULL is returned. +. +.Pp +. +.Ft "void" +.Fn "cuse_poll_wakeup" "void" +This function will wake up any file pollers. +. +.Pp +. +.Sh LIBRARY LIMITATIONS +. +. +Transfer lengths for read, write, cuse_copy_in and cuse_copy_out +should not exceed what can fit into a 32-bit signed integer and is +defined by the CUSE_LENGTH_MAX macro. +. +Transfer lengths for ioctls should not exceed what is defined by the +CUSE_BUFFER_MAX macro. +. +. +.Sh LIBRARY CALLBACK METHODS +. +In general fflags are defined by CUSE_FFLAG_XXX and errors are defined by CUSE_ERR_XXX. +. +.Bd -literal -offset indent +enum { + CUSE_ERR_NONE + CUSE_ERR_BUSY + CUSE_ERR_WOULDBLOCK + CUSE_ERR_INVALID + CUSE_ERR_NO_MEMORY + CUSE_ERR_FAULT + CUSE_ERR_SIGNAL + CUSE_ERR_OTHER + CUSE_ERR_NOT_LOADED + + CUSE_POLL_NONE + CUSE_POLL_READ + CUSE_POLL_WRITE + CUSE_POLL_ERROR + + CUSE_FFLAG_NONE + CUSE_FFLAG_READ + CUSE_FFLAG_WRITE + CUSE_FFLAG_NONBLOCK + + CUSE_CMD_NONE + CUSE_CMD_OPEN + CUSE_CMD_CLOSE + CUSE_CMD_READ + CUSE_CMD_WRITE + CUSE_CMD_IOCTL + CUSE_CMD_POLL + CUSE_CMD_SIGNAL + CUSE_CMD_SYNC + CUSE_CMD_MAX +}; +.Ed +. +.Pp +. +.Ft "int" +.Fn "cuse_open_t" "struct cuse_dev *" "int fflags" +This functions returns a CUSE_ERR_XXX value. +. +.Pp +. +.Ft "int" +.Fn "cuse_close_t" "struct cuse_dev *" "int fflags" +This functions returns a CUSE_ERR_XXX value. +. +.Pp +. +.Ft "int" +.Fn "cuse_read_t" "struct cuse_dev *" "int fflags" "void *peer_ptr" "int len" +This functions returns a CUSE_ERR_XXX value in case of failure or the +actually transferred length in case of success. cuse_copy_in() and +cuse_copy_out() must be used to transfer data to and from the +peer_ptr. +. +.Pp +. +.Ft "int" +.Fn "cuse_write_t" "struct cuse_dev *" "int fflags" "const void *peer_ptr" "int len" +This functions returns a CUSE_ERR_XXX value in case of failure or the +actually transferred length in case of success. cuse_copy_in() and +cuse_copy_out() must be used to transfer data to and from the +peer_ptr. +. +.Pp +. +.Ft "int" +.Fn "cuse_ioctl_t" "struct cuse_dev *" "int fflags" "unsigned long cmd" "void *peer_data" +This functions returns a CUSE_ERR_XXX value in case of failure or zero +in case of success. cuse_copy_in() and cuse_copy_out() must be used to +transfer data to and from the peer_data. +. +.Pp +. +.Ft "int" +.Fn "cuse_poll_t" "struct cuse_dev *" "int fflags" "int events" +This functions returns a mask of CUSE_POLL_XXX values in case of +failure and success. The events argument is also a mask of +CUSE_POLL_XXX values. +. +.Pp +. +.Bd -literal -offset indent +struct cuse_methods { + cuse_open_t *cm_open; + cuse_close_t *cm_close; + cuse_read_t *cm_read; + cuse_write_t *cm_write; + cuse_ioctl_t *cm_ioctl; + cuse_poll_t *cm_poll; +}; +.Ed +. +. +.Sh SEE ALSO +. +.Sh HISTORY +. +.Nm +was written by Hans Petter Selasky . diff --git a/lib/libcuse/cuse.h b/lib/libcuse/cuse.h new file mode 100644 index 0000000000000..d502c5bd54151 --- /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 + +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 0000000000000..f518e11d78f3b --- /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 +#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 TAILQ_HEAD(, cuse_dev) h_cuse; +static TAILQ_HEAD(, cuse_dev_entered) h_cuse_entered; +static int f_cuse = -1; +static pthread_mutex_t m_cuse; +static struct cuse_vm_allocation a_cuse[CUSE_ALLOC_UNIT_MAX]; + +static void +cuse_lock(void) +{ + pthread_mutex_lock(&m_cuse); +} + +static void +cuse_unlock(void) +{ + pthread_mutex_unlock(&m_cuse); +} + +int +cuse_init(void) +{ + pthread_mutexattr_t attr; + + f_cuse = open("/dev/cuse", O_RDWR); + if (f_cuse < 0) { + if (feature_present("cuse") == 0) + return (CUSE_ERR_NOT_LOADED); + else + return (CUSE_ERR_INVALID); + } + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&m_cuse, &attr); + + TAILQ_INIT(&h_cuse); + TAILQ_INIT(&h_cuse_entered); + + return (0); +} + +int +cuse_uninit(void) +{ + int f; + + if (f_cuse < 0) + return (CUSE_ERR_INVALID); + + f = f_cuse; + f_cuse = -1; + + close(f); + + pthread_mutex_destroy(&m_cuse); + + memset(a_cuse, 0, sizeof(a_cuse)); + + return (0); +} + +unsigned long +cuse_vmoffset(void *_ptr) +{ + uint8_t *ptr_min; + uint8_t *ptr_max; + uint8_t *ptr = _ptr; + unsigned long remainder; + int n; + + cuse_lock(); + for (n = 0; n != CUSE_ALLOC_UNIT_MAX; n++) { + if (a_cuse[n].ptr == NULL) + continue; + + ptr_min = a_cuse[n].ptr; + ptr_max = a_cuse[n].ptr + a_cuse[n].size - 1; + + if ((ptr >= ptr_min) && (ptr <= ptr_max)) { + + cuse_unlock(); + + remainder = (ptr - ptr_min); + + remainder -= remainder % PAGE_SIZE; + + return ((n * PAGE_SIZE * CUSE_ALLOC_PAGES_MAX) + remainder); + } + } + cuse_unlock(); + + return (0x80000000UL); /* failure */ +} + +void * +cuse_vmalloc(int size) +{ + struct cuse_alloc_info info; + void *ptr; + int error; + int n; + + if (f_cuse < 0) + return (NULL); + + memset(&info, 0, sizeof(info)); + + if (size < 1) + return (NULL); + + info.page_count = (size + PAGE_SIZE - 1) / PAGE_SIZE; + + cuse_lock(); + for (n = 0; n != CUSE_ALLOC_UNIT_MAX; n++) { + + if (a_cuse[n].ptr != NULL) + continue; + + a_cuse[n].ptr = ((uint8_t *)1); /* reserve */ + a_cuse[n].size = 0; + + cuse_unlock(); + + info.alloc_nr = n; + + error = ioctl(f_cuse, CUSE_IOCTL_ALLOC_MEMORY, &info); + + if (error) { + + cuse_lock(); + + a_cuse[n].ptr = NULL; + + if (errno == EBUSY) + continue; + else + break; + } + ptr = mmap(NULL, info.page_count * PAGE_SIZE, + PROT_READ | PROT_WRITE, + MAP_SHARED, f_cuse, CUSE_ALLOC_PAGES_MAX * + PAGE_SIZE * n); + + if (ptr == MAP_FAILED) { + + error = ioctl(f_cuse, CUSE_IOCTL_FREE_MEMORY, &info); + + if (error) { + /* ignore */ + } + cuse_lock(); + + a_cuse[n].ptr = NULL; + + break; + } + cuse_lock(); + a_cuse[n].ptr = ptr; + a_cuse[n].size = size; + cuse_unlock(); + + return (ptr); /* success */ + } + cuse_unlock(); + return (NULL); /* failure */ +} + +int +cuse_is_vmalloc_addr(void *ptr) +{ + int n; + + if (f_cuse < 0 || ptr == NULL) + return (0); /* false */ + + cuse_lock(); + for (n = 0; n != CUSE_ALLOC_UNIT_MAX; n++) { + if (a_cuse[n].ptr == ptr) + break; + } + cuse_unlock(); + + return (n != CUSE_ALLOC_UNIT_MAX); +} + +void +cuse_vmfree(void *ptr) +{ + struct cuse_alloc_info info; + int error; + int n; + + if (f_cuse < 0) + return; + + memset(&info, 0, sizeof(info)); + + cuse_lock(); + for (n = 0; n != CUSE_ALLOC_UNIT_MAX; n++) { + if (a_cuse[n].ptr != ptr) + continue; + + cuse_unlock(); + + info.alloc_nr = n; + + munmap(ptr, a_cuse[n].size); + + error = ioctl(f_cuse, CUSE_IOCTL_FREE_MEMORY, &info); + + if (error) { + /* ignore */ + } + cuse_lock(); + + a_cuse[n].ptr = NULL; + a_cuse[n].size = 0; + + break; + } + cuse_unlock(); +} + +int +cuse_alloc_unit_number_by_id(int *pnum, int id) +{ + int error; + + if (f_cuse < 0) + return (CUSE_ERR_INVALID); + + *pnum = (id & CUSE_ID_MASK); + + error = ioctl(f_cuse, CUSE_IOCTL_ALLOC_UNIT_BY_ID, pnum); + if (error) + return (CUSE_ERR_NO_MEMORY); + + return (0); + +} + +int +cuse_free_unit_number_by_id(int num, int id) +{ + int error; + + if (f_cuse < 0) + return (CUSE_ERR_INVALID); + + if (num != -1 || id != -1) + num = (id & CUSE_ID_MASK) | (num & 0xFF); + + error = ioctl(f_cuse, CUSE_IOCTL_FREE_UNIT_BY_ID, &num); + if (error) + return (CUSE_ERR_NO_MEMORY); + + return (0); +} + +int +cuse_alloc_unit_number(int *pnum) +{ + int error; + + if (f_cuse < 0) + return (CUSE_ERR_INVALID); + + error = ioctl(f_cuse, CUSE_IOCTL_ALLOC_UNIT, pnum); + if (error) + return (CUSE_ERR_NO_MEMORY); + + return (0); +} + +int +cuse_free_unit_number(int num) +{ + int error; + + if (f_cuse < 0) + return (CUSE_ERR_INVALID); + + error = ioctl(f_cuse, CUSE_IOCTL_FREE_UNIT, &num); + if (error) + return (CUSE_ERR_NO_MEMORY); + + return (0); +} + +struct cuse_dev * +cuse_dev_create(const struct cuse_methods *mtod, void *priv0, void *priv1, + uid_t _uid, gid_t _gid, int _perms, const char *_fmt,...) +{ + struct cuse_create_dev info; + struct cuse_dev *cdev; + va_list args; + int error; + + if (f_cuse < 0) + return (NULL); + + cdev = malloc(sizeof(*cdev)); + if (cdev == NULL) + return (NULL); + + memset(cdev, 0, sizeof(*cdev)); + + cdev->mtod = mtod; + cdev->priv0 = priv0; + cdev->priv1 = priv1; + + memset(&info, 0, sizeof(info)); + + info.dev = cdev; + info.user_id = _uid; + info.group_id = _gid; + info.permissions = _perms; + + va_start(args, _fmt); + vsnprintf(info.devname, sizeof(info.devname), _fmt, args); + va_end(args); + + error = ioctl(f_cuse, CUSE_IOCTL_CREATE_DEV, &info); + if (error) { + free(cdev); + return (NULL); + } + cuse_lock(); + TAILQ_INSERT_TAIL(&h_cuse, cdev, entry); + cuse_unlock(); + + return (cdev); +} + + +void +cuse_dev_destroy(struct cuse_dev *cdev) +{ + int error; + + if (f_cuse < 0) + return; + + cuse_lock(); + TAILQ_REMOVE(&h_cuse, cdev, entry); + cuse_unlock(); + + error = ioctl(f_cuse, CUSE_IOCTL_DESTROY_DEV, cdev); + if (error) + return; + + free(cdev); +} + +void * +cuse_dev_get_priv0(struct cuse_dev *cdev) +{ + return (cdev->priv0); +} + +void * +cuse_dev_get_priv1(struct cuse_dev *cdev) +{ + return (cdev->priv1); +} + +void +cuse_dev_set_priv0(struct cuse_dev *cdev, void *priv) +{ + cdev->priv0 = priv; +} + +void +cuse_dev_set_priv1(struct cuse_dev *cdev, void *priv) +{ + cdev->priv1 = priv; +} + +int +cuse_wait_and_process(void) +{ + pthread_t curr = pthread_self(); + struct cuse_dev_entered *pe; + struct cuse_dev_entered enter; + struct cuse_command info; + struct cuse_dev *cdev; + int error; + + if (f_cuse < 0) + return (CUSE_ERR_INVALID); + + error = ioctl(f_cuse, CUSE_IOCTL_GET_COMMAND, &info); + if (error) + return (CUSE_ERR_OTHER); + + cdev = info.dev; + + cuse_lock(); + enter.thread = curr; + enter.per_file_handle = (void *)info.per_file_handle; + enter.cmd = info.command; + enter.is_local = 0; + enter.got_signal = 0; + enter.cdev = cdev; + TAILQ_INSERT_TAIL(&h_cuse_entered, &enter, entry); + cuse_unlock(); + + DPRINTF("cuse: Command = %d = %s, flags = %d, arg = 0x%08x, ptr = 0x%08x\n", + (int)info.command, cuse_cmd_str(info.command), (int)info.fflags, + (int)info.argument, (int)info.data_pointer); + + switch (info.command) { + case CUSE_CMD_OPEN: + if (cdev->mtod->cm_open != NULL) + error = (cdev->mtod->cm_open) (cdev, (int)info.fflags); + else + error = 0; + break; + + case CUSE_CMD_CLOSE: + + /* wait for other threads to stop */ + + while (1) { + + error = 0; + + cuse_lock(); + TAILQ_FOREACH(pe, &h_cuse_entered, entry) { + if (pe->cdev != cdev) + continue; + if (pe->thread == curr) + continue; + if (pe->per_file_handle != + enter.per_file_handle) + continue; + pe->got_signal = 1; + pthread_kill(pe->thread, SIGHUP); + error = CUSE_ERR_BUSY; + } + cuse_unlock(); + + if (error == 0) + break; + else + usleep(10000); + } + + if (cdev->mtod->cm_close != NULL) + error = (cdev->mtod->cm_close) (cdev, (int)info.fflags); + else + error = 0; + break; + + case CUSE_CMD_READ: + if (cdev->mtod->cm_read != NULL) { + error = (cdev->mtod->cm_read) (cdev, (int)info.fflags, + (void *)info.data_pointer, (int)info.argument); + } else { + error = CUSE_ERR_INVALID; + } + break; + + case CUSE_CMD_WRITE: + if (cdev->mtod->cm_write != NULL) { + error = (cdev->mtod->cm_write) (cdev, (int)info.fflags, + (void *)info.data_pointer, (int)info.argument); + } else { + error = CUSE_ERR_INVALID; + } + break; + + case CUSE_CMD_IOCTL: + if (cdev->mtod->cm_ioctl != NULL) { + error = (cdev->mtod->cm_ioctl) (cdev, (int)info.fflags, + (unsigned int)info.argument, (void *)info.data_pointer); + } else { + error = CUSE_ERR_INVALID; + } + break; + + case CUSE_CMD_POLL: + if (cdev->mtod->cm_poll != NULL) { + error = (cdev->mtod->cm_poll) (cdev, (int)info.fflags, + (int)info.argument); + } else { + error = CUSE_POLL_ERROR; + } + break; + + case CUSE_CMD_SIGNAL: + cuse_lock(); + TAILQ_FOREACH(pe, &h_cuse_entered, entry) { + if (pe->cdev != cdev) + continue; + if (pe->thread == curr) + continue; + if (pe->per_file_handle != + enter.per_file_handle) + continue; + pe->got_signal = 1; + pthread_kill(pe->thread, SIGHUP); + } + cuse_unlock(); + break; + + default: + error = CUSE_ERR_INVALID; + break; + } + + DPRINTF("cuse: Command error = %d for %s\n", + error, cuse_cmd_str(info.command)); + + cuse_lock(); + TAILQ_REMOVE(&h_cuse_entered, &enter, entry); + cuse_unlock(); + + /* we ignore any sync command failures */ + ioctl(f_cuse, CUSE_IOCTL_SYNC_COMMAND, &error); + + return (0); +} + +static struct cuse_dev_entered * +cuse_dev_get_entered(void) +{ + struct cuse_dev_entered *pe; + pthread_t curr = pthread_self(); + + cuse_lock(); + TAILQ_FOREACH(pe, &h_cuse_entered, entry) { + if (pe->thread == curr) + break; + } + cuse_unlock(); + return (pe); +} + +void +cuse_dev_set_per_file_handle(struct cuse_dev *cdev, void *handle) +{ + struct cuse_dev_entered *pe; + + pe = cuse_dev_get_entered(); + if (pe == NULL || pe->cdev != cdev) + return; + + pe->per_file_handle = handle; + ioctl(f_cuse, CUSE_IOCTL_SET_PFH, &handle); +} + +void * +cuse_dev_get_per_file_handle(struct cuse_dev *cdev) +{ + struct cuse_dev_entered *pe; + + pe = cuse_dev_get_entered(); + if (pe == NULL || pe->cdev != cdev) + return (NULL); + + return (pe->per_file_handle); +} + +void +cuse_set_local(int val) +{ + struct cuse_dev_entered *pe; + + pe = cuse_dev_get_entered(); + if (pe == NULL) + return; + + pe->is_local = val; +} + +#ifdef HAVE_DEBUG +static const char * +cuse_cmd_str(int cmd) +{ + static const char *str[CUSE_CMD_MAX] = { + [CUSE_CMD_NONE] = "none", + [CUSE_CMD_OPEN] = "open", + [CUSE_CMD_CLOSE] = "close", + [CUSE_CMD_READ] = "read", + [CUSE_CMD_WRITE] = "write", + [CUSE_CMD_IOCTL] = "ioctl", + [CUSE_CMD_POLL] = "poll", + [CUSE_CMD_SIGNAL] = "signal", + [CUSE_CMD_SYNC] = "sync", + }; + + if ((cmd >= 0) && (cmd < CUSE_CMD_MAX) && + (str[cmd] != NULL)) + return (str[cmd]); + else + return ("unknown"); +} + +#endif + +int +cuse_get_local(void) +{ + struct cuse_dev_entered *pe; + + pe = cuse_dev_get_entered(); + if (pe == NULL) + return (0); + + return (pe->is_local); +} + +int +cuse_copy_out(const void *src, void *user_dst, int len) +{ + struct cuse_data_chunk info; + struct cuse_dev_entered *pe; + int error; + + if ((f_cuse < 0) || (len < 0)) + return (CUSE_ERR_INVALID); + + pe = cuse_dev_get_entered(); + if (pe == NULL) + return (CUSE_ERR_INVALID); + + DPRINTF("cuse: copy_out(%p,%p,%d), cmd = %d = %s\n", + src, user_dst, len, pe->cmd, cuse_cmd_str(pe->cmd)); + + if (pe->is_local) { + memcpy(user_dst, src, len); + } else { + info.local_ptr = (unsigned long)src; + info.peer_ptr = (unsigned long)user_dst; + info.length = len; + + error = ioctl(f_cuse, CUSE_IOCTL_WRITE_DATA, &info); + if (error) { + DPRINTF("cuse: copy_out() error = %d\n", errno); + return (CUSE_ERR_FAULT); + } + } + return (0); +} + +int +cuse_copy_in(const void *user_src, void *dst, int len) +{ + struct cuse_data_chunk info; + struct cuse_dev_entered *pe; + int error; + + if ((f_cuse < 0) || (len < 0)) + return (CUSE_ERR_INVALID); + + pe = cuse_dev_get_entered(); + if (pe == NULL) + return (CUSE_ERR_INVALID); + + DPRINTF("cuse: copy_in(%p,%p,%d), cmd = %d = %s\n", + user_src, dst, len, pe->cmd, cuse_cmd_str(pe->cmd)); + + if (pe->is_local) { + memcpy(dst, user_src, len); + } else { + info.local_ptr = (unsigned long)dst; + info.peer_ptr = (unsigned long)user_src; + info.length = len; + + error = ioctl(f_cuse, CUSE_IOCTL_READ_DATA, &info); + if (error) { + DPRINTF("cuse: copy_in() error = %d\n", errno); + return (CUSE_ERR_FAULT); + } + } + return (0); +} + +struct cuse_dev * +cuse_dev_get_current(int *pcmd) +{ + struct cuse_dev_entered *pe; + + pe = cuse_dev_get_entered(); + if (pe == NULL) { + if (pcmd != NULL) + *pcmd = 0; + return (NULL); + } + if (pcmd != NULL) + *pcmd = pe->cmd; + return (pe->cdev); +} + +int +cuse_got_peer_signal(void) +{ + struct cuse_dev_entered *pe; + + pe = cuse_dev_get_entered(); + if (pe == NULL) + return (CUSE_ERR_INVALID); + + if (pe->got_signal) + return (0); + + return (CUSE_ERR_OTHER); +} + +void +cuse_poll_wakeup(void) +{ + int error = 0; + + if (f_cuse < 0) + return; + + ioctl(f_cuse, CUSE_IOCTL_SELWAKEUP, &error); +} diff --git a/sys/conf/files b/sys/conf/files index c7d850c49583f..79452a9056f2c 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -2595,6 +2595,7 @@ fs/devfs/devfs_vnops.c standard fs/fdescfs/fdesc_vfsops.c optional fdescfs fs/fdescfs/fdesc_vnops.c optional fdescfs fs/fifofs/fifo_vnops.c standard +fs/cuse/cuse.c optional cuse fs/fuse/fuse_device.c optional fuse fs/fuse/fuse_file.c optional fuse fs/fuse/fuse_internal.c optional fuse diff --git a/sys/fs/cuse/cuse.c b/sys/fs/cuse/cuse.c new file mode 100644 index 0000000000000..4a23963b6cf6b --- /dev/null +++ b/sys/fs/cuse/cuse.c @@ -0,0 +1,1866 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2010-2013 Hans Petter Selasky. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "opt_compat.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include + +MODULE_VERSION(cuse, 1); + +#define NBUSY ((uint8_t *)1) + +#ifdef FEATURE +FEATURE(cuse, "Userspace character devices"); +#endif + +struct cuse_command; +struct cuse_server; +struct cuse_client; + +struct cuse_client_command { + TAILQ_ENTRY(cuse_client_command) entry; + struct cuse_command sub; + struct sx sx; + struct cv cv; + struct thread *entered; + struct cuse_client *client; + struct proc *proc_curr; + int proc_refs; + int got_signal; + int error; + int command; +}; + +struct cuse_memory { + struct cuse_server *owner; + uint8_t *virtaddr; + uint32_t page_count; + uint32_t is_allocated; +}; + +struct cuse_server_dev { + TAILQ_ENTRY(cuse_server_dev) entry; + struct cuse_server *server; + struct cdev *kern_dev; + struct cuse_dev *user_dev; +}; + +struct cuse_server { + TAILQ_ENTRY(cuse_server) entry; + TAILQ_HEAD(, cuse_client_command) head; + TAILQ_HEAD(, cuse_server_dev) hdev; + TAILQ_HEAD(, cuse_client) hcli; + struct cv cv; + struct selinfo selinfo; + int is_closing; + int refs; +}; + +struct cuse_client { + TAILQ_ENTRY(cuse_client) entry; + TAILQ_ENTRY(cuse_client) entry_ref; + struct cuse_client_command cmds[CUSE_CMD_MAX]; + struct cuse_server *server; + struct cuse_server_dev *server_dev; + + uint8_t ioctl_buffer[CUSE_BUFFER_MAX] __aligned(4); + + int fflags; /* file flags */ + int cflags; /* client flags */ +#define CUSE_CLI_IS_CLOSING 0x01 +#define CUSE_CLI_KNOTE_NEED_READ 0x02 +#define CUSE_CLI_KNOTE_NEED_WRITE 0x04 +#define CUSE_CLI_KNOTE_HAS_READ 0x08 +#define CUSE_CLI_KNOTE_HAS_WRITE 0x10 +}; + +#define CUSE_CLIENT_CLOSING(pcc) \ + ((pcc)->cflags & CUSE_CLI_IS_CLOSING) + +static MALLOC_DEFINE(M_CUSE, "cuse", "CUSE memory"); + +static TAILQ_HEAD(, cuse_server) cuse_server_head; +static struct mtx cuse_mtx; +static struct cdev *cuse_dev; +static struct cuse_server *cuse_alloc_unit[CUSE_DEVICES_MAX]; +static int cuse_alloc_unit_id[CUSE_DEVICES_MAX]; +static struct cuse_memory cuse_mem[CUSE_ALLOC_UNIT_MAX]; + +static void cuse_client_kqfilter_read_detach(struct knote *kn); +static void cuse_client_kqfilter_write_detach(struct knote *kn); +static int cuse_client_kqfilter_read_event(struct knote *kn, long hint); +static int cuse_client_kqfilter_write_event(struct knote *kn, long hint); + +static struct filterops cuse_client_kqfilter_read_ops = { + .f_isfd = 1, + .f_detach = cuse_client_kqfilter_read_detach, + .f_event = cuse_client_kqfilter_read_event, +}; + +static struct filterops cuse_client_kqfilter_write_ops = { + .f_isfd = 1, + .f_detach = cuse_client_kqfilter_write_detach, + .f_event = cuse_client_kqfilter_write_event, +}; + +static d_open_t cuse_client_open; +static d_close_t cuse_client_close; +static d_ioctl_t cuse_client_ioctl; +static d_read_t cuse_client_read; +static d_write_t cuse_client_write; +static d_poll_t cuse_client_poll; +static d_mmap_t cuse_client_mmap; +static d_kqfilter_t cuse_client_kqfilter; + +static struct cdevsw cuse_client_devsw = { + .d_version = D_VERSION, + .d_open = cuse_client_open, + .d_close = cuse_client_close, + .d_ioctl = cuse_client_ioctl, + .d_name = "cuse_client", + .d_flags = D_TRACKCLOSE, + .d_read = cuse_client_read, + .d_write = cuse_client_write, + .d_poll = cuse_client_poll, + .d_mmap = cuse_client_mmap, + .d_kqfilter = cuse_client_kqfilter, +}; + +static d_open_t cuse_server_open; +static d_close_t cuse_server_close; +static d_ioctl_t cuse_server_ioctl; +static d_read_t cuse_server_read; +static d_write_t cuse_server_write; +static d_poll_t cuse_server_poll; +static d_mmap_t cuse_server_mmap; + +static struct cdevsw cuse_server_devsw = { + .d_version = D_VERSION, + .d_open = cuse_server_open, + .d_close = cuse_server_close, + .d_ioctl = cuse_server_ioctl, + .d_name = "cuse_server", + .d_flags = D_TRACKCLOSE, + .d_read = cuse_server_read, + .d_write = cuse_server_write, + .d_poll = cuse_server_poll, + .d_mmap = cuse_server_mmap, +}; + +static void cuse_client_is_closing(struct cuse_client *); +static int cuse_free_unit_by_id_locked(struct cuse_server *, int); + +static void +cuse_lock(void) +{ + mtx_lock(&cuse_mtx); +} + +static void +cuse_unlock(void) +{ + mtx_unlock(&cuse_mtx); +} + +static void +cuse_cmd_lock(struct cuse_client_command *pccmd) +{ + sx_xlock(&pccmd->sx); +} + +static void +cuse_cmd_unlock(struct cuse_client_command *pccmd) +{ + sx_xunlock(&pccmd->sx); +} + +static void +cuse_kern_init(void *arg) +{ + TAILQ_INIT(&cuse_server_head); + + mtx_init(&cuse_mtx, "cuse-mtx", NULL, MTX_DEF); + + cuse_dev = make_dev(&cuse_server_devsw, 0, + UID_ROOT, GID_OPERATOR, 0600, "cuse"); + + printf("Cuse v%d.%d.%d @ /dev/cuse\n", + (CUSE_VERSION >> 16) & 0xFF, (CUSE_VERSION >> 8) & 0xFF, + (CUSE_VERSION >> 0) & 0xFF); +} + +SYSINIT(cuse_kern_init, SI_SUB_DEVFS, SI_ORDER_ANY, cuse_kern_init, 0); + +static void +cuse_kern_uninit(void *arg) +{ + void *ptr; + + while (1) { + + printf("Cuse: Please exit all /dev/cuse instances " + "and processes which have used this device.\n"); + + pause("DRAIN", 2 * hz); + + cuse_lock(); + ptr = TAILQ_FIRST(&cuse_server_head); + cuse_unlock(); + + if (ptr == NULL) + break; + } + + if (cuse_dev != NULL) + destroy_dev(cuse_dev); + + mtx_destroy(&cuse_mtx); +} + +SYSUNINIT(cuse_kern_uninit, SI_SUB_DEVFS, SI_ORDER_ANY, cuse_kern_uninit, 0); + +static int +cuse_server_get(struct cuse_server **ppcs) +{ + struct cuse_server *pcs; + int error; + + error = devfs_get_cdevpriv((void **)&pcs); + if (error != 0) { + *ppcs = NULL; + return (error); + } + /* check if closing */ + cuse_lock(); + if (pcs->is_closing) { + cuse_unlock(); + *ppcs = NULL; + return (EINVAL); + } + cuse_unlock(); + *ppcs = pcs; + return (0); +} + +static void +cuse_server_is_closing(struct cuse_server *pcs) +{ + struct cuse_client *pcc; + + if (pcs->is_closing) + return; + + pcs->is_closing = 1; + + TAILQ_FOREACH(pcc, &pcs->hcli, entry) { + cuse_client_is_closing(pcc); + } +} + +static struct cuse_client_command * +cuse_server_find_command(struct cuse_server *pcs, struct thread *td) +{ + struct cuse_client *pcc; + int n; + + if (pcs->is_closing) + goto done; + + TAILQ_FOREACH(pcc, &pcs->hcli, entry) { + if (CUSE_CLIENT_CLOSING(pcc)) + continue; + for (n = 0; n != CUSE_CMD_MAX; n++) { + if (pcc->cmds[n].entered == td) + return (&pcc->cmds[n]); + } + } +done: + return (NULL); +} + +static void +cuse_str_filter(char *ptr) +{ + int c; + + while (((c = *ptr) != 0)) { + + if ((c >= 'a') && (c <= 'z')) { + ptr++; + continue; + } + if ((c >= 'A') && (c <= 'Z')) { + ptr++; + continue; + } + if ((c >= '0') && (c <= '9')) { + ptr++; + continue; + } + if ((c == '.') || (c == '_') || (c == '/')) { + ptr++; + continue; + } + *ptr = '_'; + + ptr++; + } +} + +static int +cuse_convert_error(int error) +{ + ; /* indent fix */ + switch (error) { + case CUSE_ERR_NONE: + return (0); + case CUSE_ERR_BUSY: + return (EBUSY); + case CUSE_ERR_WOULDBLOCK: + return (EWOULDBLOCK); + case CUSE_ERR_INVALID: + return (EINVAL); + case CUSE_ERR_NO_MEMORY: + return (ENOMEM); + case CUSE_ERR_FAULT: + return (EFAULT); + case CUSE_ERR_SIGNAL: + return (EINTR); + default: + return (ENXIO); + } +} + +static void +cuse_server_free_memory(struct cuse_server *pcs) +{ + struct cuse_memory *mem; + uint32_t n; + + for (n = 0; n != CUSE_ALLOC_UNIT_MAX; n++) { + mem = &cuse_mem[n]; + + /* this memory is never freed */ + if (mem->owner == pcs) { + mem->owner = NULL; + mem->is_allocated = 0; + } + } +} + +static int +cuse_server_alloc_memory(struct cuse_server *pcs, + struct cuse_memory *mem, uint32_t page_count) +{ + void *ptr; + int error; + + cuse_lock(); + + if (mem->virtaddr == NBUSY) { + cuse_unlock(); + return (EBUSY); + } + if (mem->virtaddr != NULL) { + if (mem->is_allocated != 0) { + cuse_unlock(); + return (EBUSY); + } + if (mem->page_count == page_count) { + mem->is_allocated = 1; + mem->owner = pcs; + cuse_unlock(); + return (0); + } + cuse_unlock(); + return (EBUSY); + } + memset(mem, 0, sizeof(*mem)); + + mem->virtaddr = NBUSY; + + cuse_unlock(); + + ptr = malloc(page_count * PAGE_SIZE, M_CUSE, M_WAITOK | M_ZERO); + if (ptr == NULL) + error = ENOMEM; + else + error = 0; + + cuse_lock(); + + if (error) { + mem->virtaddr = NULL; + cuse_unlock(); + return (error); + } + mem->virtaddr = ptr; + mem->page_count = page_count; + mem->is_allocated = 1; + mem->owner = pcs; + cuse_unlock(); + + return (0); +} + +static int +cuse_client_get(struct cuse_client **ppcc) +{ + struct cuse_client *pcc; + int error; + + /* try to get private data */ + error = devfs_get_cdevpriv((void **)&pcc); + if (error != 0) { + *ppcc = NULL; + return (error); + } + /* check if closing */ + cuse_lock(); + if (CUSE_CLIENT_CLOSING(pcc) || pcc->server->is_closing) { + cuse_unlock(); + *ppcc = NULL; + return (EINVAL); + } + cuse_unlock(); + *ppcc = pcc; + return (0); +} + +static void +cuse_client_is_closing(struct cuse_client *pcc) +{ + struct cuse_client_command *pccmd; + uint32_t n; + + if (CUSE_CLIENT_CLOSING(pcc)) + return; + + pcc->cflags |= CUSE_CLI_IS_CLOSING; + pcc->server_dev = NULL; + + for (n = 0; n != CUSE_CMD_MAX; n++) { + + pccmd = &pcc->cmds[n]; + + if (pccmd->entry.tqe_prev != NULL) { + TAILQ_REMOVE(&pcc->server->head, pccmd, entry); + pccmd->entry.tqe_prev = NULL; + } + cv_broadcast(&pccmd->cv); + } +} + +static void +cuse_client_send_command_locked(struct cuse_client_command *pccmd, + unsigned long data_ptr, unsigned long arg, int fflags, int ioflag) +{ + unsigned long cuse_fflags = 0; + struct cuse_server *pcs; + + if (fflags & FREAD) + cuse_fflags |= CUSE_FFLAG_READ; + + if (fflags & FWRITE) + cuse_fflags |= CUSE_FFLAG_WRITE; + + if (ioflag & IO_NDELAY) + cuse_fflags |= CUSE_FFLAG_NONBLOCK; + + pccmd->sub.fflags = cuse_fflags; + pccmd->sub.data_pointer = data_ptr; + pccmd->sub.argument = arg; + + pcs = pccmd->client->server; + + if ((pccmd->entry.tqe_prev == NULL) && + (CUSE_CLIENT_CLOSING(pccmd->client) == 0) && + (pcs->is_closing == 0)) { + TAILQ_INSERT_TAIL(&pcs->head, pccmd, entry); + cv_signal(&pcs->cv); + } +} + +static void +cuse_client_got_signal(struct cuse_client_command *pccmd) +{ + struct cuse_server *pcs; + + pccmd->got_signal = 1; + + pccmd = &pccmd->client->cmds[CUSE_CMD_SIGNAL]; + + pcs = pccmd->client->server; + + if ((pccmd->entry.tqe_prev == NULL) && + (CUSE_CLIENT_CLOSING(pccmd->client) == 0) && + (pcs->is_closing == 0)) { + TAILQ_INSERT_TAIL(&pcs->head, pccmd, entry); + cv_signal(&pcs->cv); + } +} + +static int +cuse_client_receive_command_locked(struct cuse_client_command *pccmd, + uint8_t *arg_ptr, uint32_t arg_len) +{ + int error; + + error = 0; + + pccmd->proc_curr = curthread->td_proc; + + if (CUSE_CLIENT_CLOSING(pccmd->client) || + pccmd->client->server->is_closing) { + error = CUSE_ERR_OTHER; + goto done; + } + while (pccmd->command == CUSE_CMD_NONE) { + if (error != 0) { + cv_wait(&pccmd->cv, &cuse_mtx); + } else { + error = cv_wait_sig(&pccmd->cv, &cuse_mtx); + + if (error != 0) + cuse_client_got_signal(pccmd); + } + if (CUSE_CLIENT_CLOSING(pccmd->client) || + pccmd->client->server->is_closing) { + error = CUSE_ERR_OTHER; + goto done; + } + } + + error = pccmd->error; + pccmd->command = CUSE_CMD_NONE; + cv_signal(&pccmd->cv); + +done: + + /* wait until all process references are gone */ + + pccmd->proc_curr = NULL; + + while (pccmd->proc_refs != 0) + cv_wait(&pccmd->cv, &cuse_mtx); + + return (error); +} + +/*------------------------------------------------------------------------* + * CUSE SERVER PART + *------------------------------------------------------------------------*/ + +static void +cuse_server_free_dev(struct cuse_server_dev *pcsd) +{ + struct cuse_server *pcs; + struct cuse_client *pcc; + + /* get server pointer */ + pcs = pcsd->server; + + /* prevent creation of more devices */ + cuse_lock(); + if (pcsd->kern_dev != NULL) + pcsd->kern_dev->si_drv1 = NULL; + + TAILQ_FOREACH(pcc, &pcs->hcli, entry) { + if (pcc->server_dev == pcsd) + cuse_client_is_closing(pcc); + } + cuse_unlock(); + + /* destroy device, if any */ + if (pcsd->kern_dev != NULL) { + /* destroy device synchronously */ + destroy_dev(pcsd->kern_dev); + } + free(pcsd, M_CUSE); +} + +static void +cuse_server_free(void *arg) +{ + struct cuse_server *pcs = arg; + struct cuse_server_dev *pcsd; + + cuse_lock(); + pcs->refs--; + if (pcs->refs != 0) { + cuse_unlock(); + return; + } + cuse_server_is_closing(pcs); + + TAILQ_REMOVE(&cuse_server_head, pcs, entry); + + cuse_free_unit_by_id_locked(pcs, -1); + + while ((pcsd = TAILQ_FIRST(&pcs->hdev)) != NULL) { + TAILQ_REMOVE(&pcs->hdev, pcsd, entry); + cuse_unlock(); + cuse_server_free_dev(pcsd); + cuse_lock(); + } + + cuse_server_free_memory(pcs); + + knlist_clear(&pcs->selinfo.si_note, 1); + knlist_destroy(&pcs->selinfo.si_note); + + cuse_unlock(); + + seldrain(&pcs->selinfo); + + cv_destroy(&pcs->cv); + + free(pcs, M_CUSE); +} + +static int +cuse_server_open(struct cdev *dev, int fflags, int devtype, struct thread *td) +{ + struct cuse_server *pcs; + + pcs = malloc(sizeof(*pcs), M_CUSE, M_WAITOK | M_ZERO); + if (pcs == NULL) + return (ENOMEM); + + if (devfs_set_cdevpriv(pcs, &cuse_server_free)) { + printf("Cuse: Cannot set cdevpriv.\n"); + free(pcs, M_CUSE); + return (ENOMEM); + } + TAILQ_INIT(&pcs->head); + TAILQ_INIT(&pcs->hdev); + TAILQ_INIT(&pcs->hcli); + + cv_init(&pcs->cv, "cuse-server-cv"); + + knlist_init_mtx(&pcs->selinfo.si_note, &cuse_mtx); + + cuse_lock(); + pcs->refs++; + TAILQ_INSERT_TAIL(&cuse_server_head, pcs, entry); + cuse_unlock(); + + return (0); +} + +static int +cuse_server_close(struct cdev *dev, int fflag, int devtype, struct thread *td) +{ + struct cuse_server *pcs; + int error; + + error = cuse_server_get(&pcs); + if (error != 0) + goto done; + + cuse_lock(); + cuse_server_is_closing(pcs); + knlist_clear(&pcs->selinfo.si_note, 1); + cuse_unlock(); + +done: + return (0); +} + +static int +cuse_server_read(struct cdev *dev, struct uio *uio, int ioflag) +{ + return (ENXIO); +} + +static int +cuse_server_write(struct cdev *dev, struct uio *uio, int ioflag) +{ + return (ENXIO); +} + +static int +cuse_server_ioctl_copy_locked(struct cuse_client_command *pccmd, + struct cuse_data_chunk *pchk, int isread) +{ + struct proc *p_proc; + uint32_t offset; + int error; + + offset = pchk->peer_ptr - CUSE_BUF_MIN_PTR; + + if (pchk->length > CUSE_BUFFER_MAX) + return (EFAULT); + + if (offset >= CUSE_BUFFER_MAX) + return (EFAULT); + + if ((offset + pchk->length) > CUSE_BUFFER_MAX) + return (EFAULT); + + p_proc = pccmd->proc_curr; + if (p_proc == NULL) + return (ENXIO); + + if (pccmd->proc_refs < 0) + return (ENOMEM); + + pccmd->proc_refs++; + + cuse_unlock(); + + if (isread == 0) { + error = copyin( + (void *)pchk->local_ptr, + pccmd->client->ioctl_buffer + offset, + pchk->length); + } else { + error = copyout( + pccmd->client->ioctl_buffer + offset, + (void *)pchk->local_ptr, + pchk->length); + } + + cuse_lock(); + + pccmd->proc_refs--; + + if (pccmd->proc_curr == NULL) + cv_signal(&pccmd->cv); + + return (error); +} + +static int +cuse_proc2proc_copy(struct proc *proc_s, vm_offset_t data_s, + struct proc *proc_d, vm_offset_t data_d, size_t len) +{ + struct thread *td; + struct proc *proc_cur; + int error; + + td = curthread; + proc_cur = td->td_proc; + + if (proc_cur == proc_d) { + struct iovec iov = { + .iov_base = (caddr_t)data_d, + .iov_len = len, + }; + struct uio uio = { + .uio_iov = &iov, + .uio_iovcnt = 1, + .uio_offset = (off_t)data_s, + .uio_resid = len, + .uio_segflg = UIO_USERSPACE, + .uio_rw = UIO_READ, + .uio_td = td, + }; + + PROC_LOCK(proc_s); + _PHOLD(proc_s); + PROC_UNLOCK(proc_s); + + error = proc_rwmem(proc_s, &uio); + + PROC_LOCK(proc_s); + _PRELE(proc_s); + PROC_UNLOCK(proc_s); + + } else if (proc_cur == proc_s) { + struct iovec iov = { + .iov_base = (caddr_t)data_s, + .iov_len = len, + }; + struct uio uio = { + .uio_iov = &iov, + .uio_iovcnt = 1, + .uio_offset = (off_t)data_d, + .uio_resid = len, + .uio_segflg = UIO_USERSPACE, + .uio_rw = UIO_WRITE, + .uio_td = td, + }; + + PROC_LOCK(proc_d); + _PHOLD(proc_d); + PROC_UNLOCK(proc_d); + + error = proc_rwmem(proc_d, &uio); + + PROC_LOCK(proc_d); + _PRELE(proc_d); + PROC_UNLOCK(proc_d); + } else { + error = EINVAL; + } + return (error); +} + +static int +cuse_server_data_copy_locked(struct cuse_client_command *pccmd, + struct cuse_data_chunk *pchk, int isread) +{ + struct proc *p_proc; + int error; + + p_proc = pccmd->proc_curr; + if (p_proc == NULL) + return (ENXIO); + + if (pccmd->proc_refs < 0) + return (ENOMEM); + + pccmd->proc_refs++; + + cuse_unlock(); + + if (isread == 0) { + error = cuse_proc2proc_copy( + curthread->td_proc, pchk->local_ptr, + p_proc, pchk->peer_ptr, + pchk->length); + } else { + error = cuse_proc2proc_copy( + p_proc, pchk->peer_ptr, + curthread->td_proc, pchk->local_ptr, + pchk->length); + } + + cuse_lock(); + + pccmd->proc_refs--; + + if (pccmd->proc_curr == NULL) + cv_signal(&pccmd->cv); + + return (error); +} + +static int +cuse_alloc_unit_by_id_locked(struct cuse_server *pcs, int id) +{ + int n; + int x = 0; + int match; + + do { + for (match = n = 0; n != CUSE_DEVICES_MAX; n++) { + if (cuse_alloc_unit[n] != NULL) { + if ((cuse_alloc_unit_id[n] ^ id) & CUSE_ID_MASK) + continue; + if ((cuse_alloc_unit_id[n] & ~CUSE_ID_MASK) == x) { + x++; + match = 1; + } + } + } + } while (match); + + if (x < 256) { + for (n = 0; n != CUSE_DEVICES_MAX; n++) { + if (cuse_alloc_unit[n] == NULL) { + cuse_alloc_unit[n] = pcs; + cuse_alloc_unit_id[n] = id | x; + return (x); + } + } + } + return (-1); +} + +static void +cuse_server_wakeup_locked(struct cuse_server *pcs) +{ + selwakeup(&pcs->selinfo); + KNOTE_LOCKED(&pcs->selinfo.si_note, 0); +} + +static int +cuse_free_unit_by_id_locked(struct cuse_server *pcs, int id) +{ + int n; + int found = 0; + + for (n = 0; n != CUSE_DEVICES_MAX; n++) { + if (cuse_alloc_unit[n] == pcs) { + if (cuse_alloc_unit_id[n] == id || id == -1) { + cuse_alloc_unit[n] = NULL; + cuse_alloc_unit_id[n] = 0; + found = 1; + } + } + } + + return (found ? 0 : EINVAL); +} + +static int +cuse_server_ioctl(struct cdev *dev, unsigned long cmd, + caddr_t data, int fflag, struct thread *td) +{ + struct cuse_server *pcs; + int error; + + error = cuse_server_get(&pcs); + if (error != 0) + return (error); + + switch (cmd) { + struct cuse_client_command *pccmd; + struct cuse_client *pcc; + struct cuse_command *pcmd; + struct cuse_alloc_info *pai; + struct cuse_create_dev *pcd; + struct cuse_server_dev *pcsd; + struct cuse_data_chunk *pchk; + int n; + + case CUSE_IOCTL_GET_COMMAND: + pcmd = (void *)data; + + cuse_lock(); + + while ((pccmd = TAILQ_FIRST(&pcs->head)) == NULL) { + error = cv_wait_sig(&pcs->cv, &cuse_mtx); + + if (pcs->is_closing) + error = ENXIO; + + if (error) { + cuse_unlock(); + return (error); + } + } + + TAILQ_REMOVE(&pcs->head, pccmd, entry); + pccmd->entry.tqe_prev = NULL; + + pccmd->entered = curthread; + + *pcmd = pccmd->sub; + + cuse_unlock(); + + break; + + case CUSE_IOCTL_SYNC_COMMAND: + + cuse_lock(); + while ((pccmd = cuse_server_find_command(pcs, curthread)) != NULL) { + + /* send sync command */ + pccmd->entered = NULL; + pccmd->error = *(int *)data; + pccmd->command = CUSE_CMD_SYNC; + + /* signal peer, if any */ + cv_signal(&pccmd->cv); + } + cuse_unlock(); + + break; + + case CUSE_IOCTL_ALLOC_UNIT: + + cuse_lock(); + n = cuse_alloc_unit_by_id_locked(pcs, + CUSE_ID_DEFAULT(0)); + cuse_unlock(); + + if (n < 0) + error = ENOMEM; + else + *(int *)data = n; + break; + + case CUSE_IOCTL_ALLOC_UNIT_BY_ID: + + n = *(int *)data; + + n = (n & CUSE_ID_MASK); + + cuse_lock(); + n = cuse_alloc_unit_by_id_locked(pcs, n); + cuse_unlock(); + + if (n < 0) + error = ENOMEM; + else + *(int *)data = n; + break; + + case CUSE_IOCTL_FREE_UNIT: + + n = *(int *)data; + + n = CUSE_ID_DEFAULT(n); + + cuse_lock(); + error = cuse_free_unit_by_id_locked(pcs, n); + cuse_unlock(); + break; + + case CUSE_IOCTL_FREE_UNIT_BY_ID: + + n = *(int *)data; + + cuse_lock(); + error = cuse_free_unit_by_id_locked(pcs, n); + cuse_unlock(); + break; + + case CUSE_IOCTL_ALLOC_MEMORY: + + pai = (void *)data; + + if (pai->alloc_nr >= CUSE_ALLOC_UNIT_MAX) { + error = ENOMEM; + break; + } + if (pai->page_count > CUSE_ALLOC_PAGES_MAX) { + error = ENOMEM; + break; + } + error = cuse_server_alloc_memory(pcs, + &cuse_mem[pai->alloc_nr], pai->page_count); + break; + + case CUSE_IOCTL_FREE_MEMORY: + pai = (void *)data; + + if (pai->alloc_nr >= CUSE_ALLOC_UNIT_MAX) { + error = ENOMEM; + break; + } + /* we trust the character device driver in this case */ + + cuse_lock(); + if (cuse_mem[pai->alloc_nr].owner == pcs) { + cuse_mem[pai->alloc_nr].is_allocated = 0; + cuse_mem[pai->alloc_nr].owner = NULL; + } else { + error = EINVAL; + } + cuse_unlock(); + break; + + case CUSE_IOCTL_GET_SIG: + + cuse_lock(); + pccmd = cuse_server_find_command(pcs, curthread); + + if (pccmd != NULL) { + n = pccmd->got_signal; + pccmd->got_signal = 0; + } else { + n = 0; + } + cuse_unlock(); + + *(int *)data = n; + + break; + + case CUSE_IOCTL_SET_PFH: + + cuse_lock(); + pccmd = cuse_server_find_command(pcs, curthread); + + if (pccmd != NULL) { + pcc = pccmd->client; + for (n = 0; n != CUSE_CMD_MAX; n++) { + pcc->cmds[n].sub.per_file_handle = *(unsigned long *)data; + } + } else { + error = ENXIO; + } + cuse_unlock(); + break; + + case CUSE_IOCTL_CREATE_DEV: + + error = priv_check(curthread, PRIV_DRIVER); + if (error) + break; + + pcd = (void *)data; + + /* filter input */ + + pcd->devname[sizeof(pcd->devname) - 1] = 0; + + if (pcd->devname[0] == 0) { + error = EINVAL; + break; + } + cuse_str_filter(pcd->devname); + + pcd->permissions &= 0777; + + /* try to allocate a character device */ + + pcsd = malloc(sizeof(*pcsd), M_CUSE, M_WAITOK | M_ZERO); + + if (pcsd == NULL) { + error = ENOMEM; + break; + } + pcsd->server = pcs; + + pcsd->user_dev = pcd->dev; + + pcsd->kern_dev = make_dev_credf(MAKEDEV_CHECKNAME, + &cuse_client_devsw, 0, NULL, pcd->user_id, pcd->group_id, + pcd->permissions, "%s", pcd->devname); + + if (pcsd->kern_dev == NULL) { + free(pcsd, M_CUSE); + error = ENOMEM; + break; + } + pcsd->kern_dev->si_drv1 = pcsd; + + cuse_lock(); + TAILQ_INSERT_TAIL(&pcs->hdev, pcsd, entry); + cuse_unlock(); + + break; + + case CUSE_IOCTL_DESTROY_DEV: + + error = priv_check(curthread, PRIV_DRIVER); + if (error) + break; + + cuse_lock(); + + error = EINVAL; + + pcsd = TAILQ_FIRST(&pcs->hdev); + while (pcsd != NULL) { + if (pcsd->user_dev == *(struct cuse_dev **)data) { + TAILQ_REMOVE(&pcs->hdev, pcsd, entry); + cuse_unlock(); + cuse_server_free_dev(pcsd); + cuse_lock(); + error = 0; + pcsd = TAILQ_FIRST(&pcs->hdev); + } else { + pcsd = TAILQ_NEXT(pcsd, entry); + } + } + + cuse_unlock(); + break; + + case CUSE_IOCTL_WRITE_DATA: + case CUSE_IOCTL_READ_DATA: + + cuse_lock(); + pchk = (struct cuse_data_chunk *)data; + + pccmd = cuse_server_find_command(pcs, curthread); + + if (pccmd == NULL) { + error = ENXIO; /* invalid request */ + } else if (pchk->peer_ptr < CUSE_BUF_MIN_PTR) { + error = EFAULT; /* NULL pointer */ + } else if (pchk->peer_ptr < CUSE_BUF_MAX_PTR) { + error = cuse_server_ioctl_copy_locked(pccmd, + pchk, cmd == CUSE_IOCTL_READ_DATA); + } else { + error = cuse_server_data_copy_locked(pccmd, + pchk, cmd == CUSE_IOCTL_READ_DATA); + } + cuse_unlock(); + break; + + case CUSE_IOCTL_SELWAKEUP: + cuse_lock(); + /* + * We don't know which direction caused the event. + * Wakeup both! + */ + TAILQ_FOREACH(pcc, &pcs->hcli, entry) { + pcc->cflags |= (CUSE_CLI_KNOTE_NEED_READ | + CUSE_CLI_KNOTE_NEED_WRITE); + } + cuse_server_wakeup_locked(pcs); + cuse_unlock(); + break; + + default: + error = ENXIO; + break; + } + return (error); +} + +static int +cuse_server_poll(struct cdev *dev, int events, struct thread *td) +{ + return (events & (POLLHUP | POLLPRI | POLLIN | + POLLRDNORM | POLLOUT | POLLWRNORM)); +} + +static int +cuse_server_mmap(struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr, int nprot, vm_memattr_t *memattr) +{ + uint32_t page_nr = offset / PAGE_SIZE; + uint32_t alloc_nr = page_nr / CUSE_ALLOC_PAGES_MAX; + struct cuse_memory *mem; + struct cuse_server *pcs; + uint8_t *ptr; + int error; + + if (alloc_nr >= CUSE_ALLOC_UNIT_MAX) + return (ENOMEM); + + error = cuse_server_get(&pcs); + if (error != 0) + pcs = NULL; + + cuse_lock(); + mem = &cuse_mem[alloc_nr]; + + /* try to enforce slight ownership */ + if ((pcs != NULL) && (mem->owner != pcs)) { + cuse_unlock(); + return (EINVAL); + } + if (mem->virtaddr == NULL) { + cuse_unlock(); + return (ENOMEM); + } + if (mem->virtaddr == NBUSY) { + cuse_unlock(); + return (ENOMEM); + } + page_nr %= CUSE_ALLOC_PAGES_MAX; + + if (page_nr >= mem->page_count) { + cuse_unlock(); + return (ENXIO); + } + ptr = mem->virtaddr + (page_nr * PAGE_SIZE); + cuse_unlock(); + + *paddr = vtophys(ptr); + + return (0); +} + +/*------------------------------------------------------------------------* + * CUSE CLIENT PART + *------------------------------------------------------------------------*/ +static void +cuse_client_free(void *arg) +{ + struct cuse_client *pcc = arg; + struct cuse_client_command *pccmd; + struct cuse_server *pcs; + int n; + + cuse_lock(); + cuse_client_is_closing(pcc); + TAILQ_REMOVE(&pcc->server->hcli, pcc, entry); + cuse_unlock(); + + for (n = 0; n != CUSE_CMD_MAX; n++) { + + pccmd = &pcc->cmds[n]; + + sx_destroy(&pccmd->sx); + cv_destroy(&pccmd->cv); + } + + pcs = pcc->server; + + free(pcc, M_CUSE); + + /* drop reference on server */ + cuse_server_free(pcs); +} + +static int +cuse_client_open(struct cdev *dev, int fflags, int devtype, struct thread *td) +{ + struct cuse_client_command *pccmd; + struct cuse_server_dev *pcsd; + struct cuse_client *pcc; + struct cuse_server *pcs; + struct cuse_dev *pcd; + int error; + int n; + + cuse_lock(); + pcsd = dev->si_drv1; + if (pcsd != NULL) { + pcs = pcsd->server; + pcd = pcsd->user_dev; + pcs->refs++; + if (pcs->refs < 0) { + /* overflow */ + pcs->refs--; + pcsd = NULL; + } + } else { + pcs = NULL; + pcd = NULL; + } + cuse_unlock(); + + if (pcsd == NULL) + return (EINVAL); + + pcc = malloc(sizeof(*pcc), M_CUSE, M_WAITOK | M_ZERO); + if (pcc == NULL) { + /* drop reference on server */ + cuse_server_free(pcs); + return (ENOMEM); + } + if (devfs_set_cdevpriv(pcc, &cuse_client_free)) { + printf("Cuse: Cannot set cdevpriv.\n"); + /* drop reference on server */ + cuse_server_free(pcs); + free(pcc, M_CUSE); + return (ENOMEM); + } + pcc->fflags = fflags; + pcc->server_dev = pcsd; + pcc->server = pcs; + + for (n = 0; n != CUSE_CMD_MAX; n++) { + + pccmd = &pcc->cmds[n]; + + pccmd->sub.dev = pcd; + pccmd->sub.command = n; + pccmd->client = pcc; + + sx_init(&pccmd->sx, "cuse-client-sx"); + cv_init(&pccmd->cv, "cuse-client-cv"); + } + + cuse_lock(); + + /* cuse_client_free() assumes that the client is listed somewhere! */ + /* always enqueue */ + + TAILQ_INSERT_TAIL(&pcs->hcli, pcc, entry); + + /* check if server is closing */ + if ((pcs->is_closing != 0) || (dev->si_drv1 == NULL)) { + error = EINVAL; + } else { + error = 0; + } + cuse_unlock(); + + if (error) { + devfs_clear_cdevpriv(); /* XXX bugfix */ + return (error); + } + pccmd = &pcc->cmds[CUSE_CMD_OPEN]; + + cuse_cmd_lock(pccmd); + + cuse_lock(); + cuse_client_send_command_locked(pccmd, 0, 0, pcc->fflags, 0); + + error = cuse_client_receive_command_locked(pccmd, 0, 0); + cuse_unlock(); + + if (error < 0) { + error = cuse_convert_error(error); + } else { + error = 0; + } + + cuse_cmd_unlock(pccmd); + + if (error) + devfs_clear_cdevpriv(); /* XXX bugfix */ + + return (error); +} + +static int +cuse_client_close(struct cdev *dev, int fflag, int devtype, struct thread *td) +{ + struct cuse_client_command *pccmd; + struct cuse_client *pcc; + int error; + + error = cuse_client_get(&pcc); + if (error != 0) + return (0); + + pccmd = &pcc->cmds[CUSE_CMD_CLOSE]; + + cuse_cmd_lock(pccmd); + + cuse_lock(); + cuse_client_send_command_locked(pccmd, 0, 0, pcc->fflags, 0); + + error = cuse_client_receive_command_locked(pccmd, 0, 0); + cuse_unlock(); + + cuse_cmd_unlock(pccmd); + + cuse_lock(); + cuse_client_is_closing(pcc); + cuse_unlock(); + + return (0); +} + +static void +cuse_client_kqfilter_poll(struct cdev *dev, struct cuse_client *pcc) +{ + int temp; + + cuse_lock(); + temp = (pcc->cflags & (CUSE_CLI_KNOTE_HAS_READ | + CUSE_CLI_KNOTE_HAS_WRITE)); + pcc->cflags &= ~(CUSE_CLI_KNOTE_NEED_READ | + CUSE_CLI_KNOTE_NEED_WRITE); + cuse_unlock(); + + if (temp != 0) { + /* get the latest polling state from the server */ + temp = cuse_client_poll(dev, POLLIN | POLLOUT, NULL); + + cuse_lock(); + if (temp & (POLLIN | POLLOUT)) { + if (temp & POLLIN) + pcc->cflags |= CUSE_CLI_KNOTE_NEED_READ; + if (temp & POLLOUT) + pcc->cflags |= CUSE_CLI_KNOTE_NEED_WRITE; + + /* make sure the "knote" gets woken up */ + cuse_server_wakeup_locked(pcc->server); + } + cuse_unlock(); + } +} + +static int +cuse_client_read(struct cdev *dev, struct uio *uio, int ioflag) +{ + struct cuse_client_command *pccmd; + struct cuse_client *pcc; + int error; + int len; + + error = cuse_client_get(&pcc); + if (error != 0) + return (error); + + pccmd = &pcc->cmds[CUSE_CMD_READ]; + + if (uio->uio_segflg != UIO_USERSPACE) { + return (EINVAL); + } + uio->uio_segflg = UIO_NOCOPY; + + cuse_cmd_lock(pccmd); + + while (uio->uio_resid != 0) { + + if (uio->uio_iov->iov_len > CUSE_LENGTH_MAX) { + error = ENOMEM; + break; + } + + len = uio->uio_iov->iov_len; + + cuse_lock(); + cuse_client_send_command_locked(pccmd, + (unsigned long)uio->uio_iov->iov_base, + (unsigned long)(unsigned int)len, pcc->fflags, ioflag); + + error = cuse_client_receive_command_locked(pccmd, 0, 0); + cuse_unlock(); + + if (error < 0) { + error = cuse_convert_error(error); + break; + } else if (error == len) { + error = uiomove(NULL, error, uio); + if (error) + break; + } else { + error = uiomove(NULL, error, uio); + break; + } + } + cuse_cmd_unlock(pccmd); + + uio->uio_segflg = UIO_USERSPACE;/* restore segment flag */ + + if (error == EWOULDBLOCK) + cuse_client_kqfilter_poll(dev, pcc); + + return (error); +} + +static int +cuse_client_write(struct cdev *dev, struct uio *uio, int ioflag) +{ + struct cuse_client_command *pccmd; + struct cuse_client *pcc; + int error; + int len; + + error = cuse_client_get(&pcc); + if (error != 0) + return (error); + + pccmd = &pcc->cmds[CUSE_CMD_WRITE]; + + if (uio->uio_segflg != UIO_USERSPACE) { + return (EINVAL); + } + uio->uio_segflg = UIO_NOCOPY; + + cuse_cmd_lock(pccmd); + + while (uio->uio_resid != 0) { + + if (uio->uio_iov->iov_len > CUSE_LENGTH_MAX) { + error = ENOMEM; + break; + } + + len = uio->uio_iov->iov_len; + + cuse_lock(); + cuse_client_send_command_locked(pccmd, + (unsigned long)uio->uio_iov->iov_base, + (unsigned long)(unsigned int)len, pcc->fflags, ioflag); + + error = cuse_client_receive_command_locked(pccmd, 0, 0); + cuse_unlock(); + + if (error < 0) { + error = cuse_convert_error(error); + break; + } else if (error == len) { + error = uiomove(NULL, error, uio); + if (error) + break; + } else { + error = uiomove(NULL, error, uio); + break; + } + } + cuse_cmd_unlock(pccmd); + + uio->uio_segflg = UIO_USERSPACE;/* restore segment flag */ + + if (error == EWOULDBLOCK) + cuse_client_kqfilter_poll(dev, pcc); + + return (error); +} + +int +cuse_client_ioctl(struct cdev *dev, unsigned long cmd, + caddr_t data, int fflag, struct thread *td) +{ + struct cuse_client_command *pccmd; + struct cuse_client *pcc; + int error; + int len; + + error = cuse_client_get(&pcc); + if (error != 0) + return (error); + + len = IOCPARM_LEN(cmd); + if (len > CUSE_BUFFER_MAX) + return (ENOMEM); + + pccmd = &pcc->cmds[CUSE_CMD_IOCTL]; + + cuse_cmd_lock(pccmd); + + if (cmd & IOC_IN) + memcpy(pcc->ioctl_buffer, data, len); + + /* + * When the ioctl-length is zero drivers can pass information + * through the data pointer of the ioctl. Make sure this information + * is forwarded to the driver. + */ + + cuse_lock(); + cuse_client_send_command_locked(pccmd, + (len == 0) ? *(long *)data : CUSE_BUF_MIN_PTR, + (unsigned long)cmd, pcc->fflags, + (fflag & O_NONBLOCK) ? IO_NDELAY : 0); + + error = cuse_client_receive_command_locked(pccmd, data, len); + cuse_unlock(); + + if (error < 0) { + error = cuse_convert_error(error); + } else { + error = 0; + } + + if (cmd & IOC_OUT) + memcpy(data, pcc->ioctl_buffer, len); + + cuse_cmd_unlock(pccmd); + + if (error == EWOULDBLOCK) + cuse_client_kqfilter_poll(dev, pcc); + + return (error); +} + +static int +cuse_client_poll(struct cdev *dev, int events, struct thread *td) +{ + struct cuse_client_command *pccmd; + struct cuse_client *pcc; + unsigned long temp; + int error; + int revents; + + error = cuse_client_get(&pcc); + if (error != 0) + return (POLLNVAL); + + temp = 0; + + if (events & (POLLPRI | POLLIN | POLLRDNORM)) + temp |= CUSE_POLL_READ; + + if (events & (POLLOUT | POLLWRNORM)) + temp |= CUSE_POLL_WRITE; + + if (events & POLLHUP) + temp |= CUSE_POLL_ERROR; + + pccmd = &pcc->cmds[CUSE_CMD_POLL]; + + cuse_cmd_lock(pccmd); + + /* Need to selrecord() first to not loose any events. */ + if (temp != 0 && td != NULL) + selrecord(td, &pcc->server->selinfo); + + cuse_lock(); + cuse_client_send_command_locked(pccmd, + 0, temp, pcc->fflags, IO_NDELAY); + + error = cuse_client_receive_command_locked(pccmd, 0, 0); + cuse_unlock(); + + if (error < 0) { + revents = POLLNVAL; + } else { + revents = 0; + if (error & CUSE_POLL_READ) + revents |= (events & (POLLPRI | POLLIN | POLLRDNORM)); + if (error & CUSE_POLL_WRITE) + revents |= (events & (POLLOUT | POLLWRNORM)); + if (error & CUSE_POLL_ERROR) + revents |= (events & POLLHUP); + } + + cuse_cmd_unlock(pccmd); + + return (revents); +} + +static int +cuse_client_mmap(struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr, int nprot, vm_memattr_t *memattr) +{ + uint32_t page_nr = offset / PAGE_SIZE; + uint32_t alloc_nr = page_nr / CUSE_ALLOC_PAGES_MAX; + struct cuse_memory *mem; + struct cuse_server *pcs; + struct cuse_client *pcc; + uint8_t *ptr; + int error; + + if (alloc_nr >= CUSE_ALLOC_UNIT_MAX) + return (ENOMEM); + + error = cuse_client_get(&pcc); + if (error != 0) + pcs = NULL; + else + pcs = pcc->server; + + cuse_lock(); + mem = &cuse_mem[alloc_nr]; + + /* try to enforce slight ownership */ + if ((pcs != NULL) && (mem->owner != pcs)) { + cuse_unlock(); + return (EINVAL); + } + if (mem->virtaddr == NULL) { + cuse_unlock(); + return (ENOMEM); + } + if (mem->virtaddr == NBUSY) { + cuse_unlock(); + return (ENOMEM); + } + page_nr %= CUSE_ALLOC_PAGES_MAX; + + if (page_nr >= mem->page_count) { + cuse_unlock(); + return (ENXIO); + } + ptr = mem->virtaddr + (page_nr * PAGE_SIZE); + cuse_unlock(); + + *paddr = vtophys(ptr); + + return (0); +} + +static void +cuse_client_kqfilter_read_detach(struct knote *kn) +{ + struct cuse_client *pcc; + + cuse_lock(); + pcc = kn->kn_hook; + knlist_remove(&pcc->server->selinfo.si_note, kn, 1); + cuse_unlock(); +} + +static void +cuse_client_kqfilter_write_detach(struct knote *kn) +{ + struct cuse_client *pcc; + + cuse_lock(); + pcc = kn->kn_hook; + knlist_remove(&pcc->server->selinfo.si_note, kn, 1); + cuse_unlock(); +} + +static int +cuse_client_kqfilter_read_event(struct knote *kn, long hint) +{ + struct cuse_client *pcc; + + mtx_assert(&cuse_mtx, MA_OWNED); + + pcc = kn->kn_hook; + return ((pcc->cflags & CUSE_CLI_KNOTE_NEED_READ) ? 1 : 0); +} + +static int +cuse_client_kqfilter_write_event(struct knote *kn, long hint) +{ + struct cuse_client *pcc; + + mtx_assert(&cuse_mtx, MA_OWNED); + + pcc = kn->kn_hook; + return ((pcc->cflags & CUSE_CLI_KNOTE_NEED_WRITE) ? 1 : 0); +} + +static int +cuse_client_kqfilter(struct cdev *dev, struct knote *kn) +{ + struct cuse_client *pcc; + struct cuse_server *pcs; + int error; + + error = cuse_client_get(&pcc); + if (error != 0) + return (error); + + cuse_lock(); + pcs = pcc->server; + switch (kn->kn_filter) { + case EVFILT_READ: + pcc->cflags |= CUSE_CLI_KNOTE_HAS_READ; + kn->kn_hook = pcc; + kn->kn_fop = &cuse_client_kqfilter_read_ops; + knlist_add(&pcs->selinfo.si_note, kn, 1); + break; + case EVFILT_WRITE: + pcc->cflags |= CUSE_CLI_KNOTE_HAS_WRITE; + kn->kn_hook = pcc; + kn->kn_fop = &cuse_client_kqfilter_write_ops; + knlist_add(&pcs->selinfo.si_note, kn, 1); + break; + default: + error = EINVAL; + break; + } + cuse_unlock(); + + if (error == 0) + cuse_client_kqfilter_poll(dev, pcc); + return (error); +} diff --git a/sys/fs/cuse/cuse_defs.h b/sys/fs/cuse/cuse_defs.h new file mode 100644 index 0000000000000..0134bfee8fdc9 --- /dev/null +++ b/sys/fs/cuse/cuse_defs.h @@ -0,0 +1,86 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2010-2012 Hans Petter Selasky. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _CUSE_DEFS_H_ +#define _CUSE_DEFS_H_ + +#define CUSE_VERSION 0x000122 + +#define CUSE_ERR_NONE 0 +#define CUSE_ERR_BUSY -1 +#define CUSE_ERR_WOULDBLOCK -2 +#define CUSE_ERR_INVALID -3 +#define CUSE_ERR_NO_MEMORY -4 +#define CUSE_ERR_FAULT -5 +#define CUSE_ERR_SIGNAL -6 +#define CUSE_ERR_OTHER -7 +#define CUSE_ERR_NOT_LOADED -8 + +#define CUSE_POLL_NONE 0 +#define CUSE_POLL_READ 1 +#define CUSE_POLL_WRITE 2 +#define CUSE_POLL_ERROR 4 + +#define CUSE_FFLAG_NONE 0 +#define CUSE_FFLAG_READ 1 +#define CUSE_FFLAG_WRITE 2 +#define CUSE_FFLAG_NONBLOCK 4 + +#define CUSE_DBG_NONE 0 +#define CUSE_DBG_FULL 1 + +/* maximum data transfer length */ +#define CUSE_LENGTH_MAX 0x7FFFFFFFU + +enum { + CUSE_CMD_NONE, + CUSE_CMD_OPEN, + CUSE_CMD_CLOSE, + CUSE_CMD_READ, + CUSE_CMD_WRITE, + CUSE_CMD_IOCTL, + CUSE_CMD_POLL, + CUSE_CMD_SIGNAL, + CUSE_CMD_SYNC, + CUSE_CMD_MAX, +}; + +#define CUSE_MAKE_ID(a,b,c,u) ((((a) & 0x7F) << 24)| \ + (((b) & 0xFF) << 16)|(((c) & 0xFF) << 8)|((u) & 0xFF)) + +#define CUSE_ID_MASK 0x7FFFFF00U + +/* + * The following ID's are defined: + * =============================== + */ +#define CUSE_ID_DEFAULT(what) CUSE_MAKE_ID(0,0,what,0) +#define CUSE_ID_WEBCAMD(what) CUSE_MAKE_ID('W','C',what,0) /* Used by Webcamd. */ +#define CUSE_ID_SUNDTEK(what) CUSE_MAKE_ID('S','K',what,0) /* Used by Sundtek. */ +#define CUSE_ID_CX88(what) CUSE_MAKE_ID('C','X',what,0) /* Used by cx88 driver. */ +#define CUSE_ID_UHIDD(what) CUSE_MAKE_ID('U','D',what,0) /* Used by uhidd. */ + +#endif /* _CUSE_DEFS_H_ */ diff --git a/sys/fs/cuse/cuse_ioctl.h b/sys/fs/cuse/cuse_ioctl.h new file mode 100644 index 0000000000000..8e1867cd009d9 --- /dev/null +++ b/sys/fs/cuse/cuse_ioctl.h @@ -0,0 +1,88 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2014 Hans Petter Selasky. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _CUSE_IOCTL_H_ +#define _CUSE_IOCTL_H_ + +#include +#include + +#define CUSE_BUFFER_MAX PAGE_SIZE +#define CUSE_DEVICES_MAX 64 /* units */ +#define CUSE_BUF_MIN_PTR 0x10000UL +#define CUSE_BUF_MAX_PTR 0x20000UL +#define CUSE_ALLOC_UNIT_MAX 128 /* units */ +#define CUSE_ALLOC_PAGES_MAX (((16UL * 1024UL * 1024UL) + PAGE_SIZE - 1) / PAGE_SIZE) + +struct cuse_dev; + +struct cuse_data_chunk { + unsigned long local_ptr; + unsigned long peer_ptr; + unsigned long length; +}; + +struct cuse_alloc_info { + unsigned long page_count; + unsigned long alloc_nr; +}; + +struct cuse_command { + struct cuse_dev *dev; + unsigned long fflags; + unsigned long per_file_handle; + unsigned long data_pointer; + unsigned long argument; + unsigned long command; /* see CUSE_CMD_XXX */ +}; + +struct cuse_create_dev { + struct cuse_dev *dev; + uid_t user_id; + gid_t group_id; + int permissions; + char devname[80]; /* /dev/xxxxx */ +}; + +/* Definition of internal IOCTLs for /dev/cuse */ + +#define CUSE_IOCTL_GET_COMMAND _IOR('C', 0, struct cuse_command) +#define CUSE_IOCTL_WRITE_DATA _IOW('C', 1, struct cuse_data_chunk) +#define CUSE_IOCTL_READ_DATA _IOW('C', 2, struct cuse_data_chunk) +#define CUSE_IOCTL_SYNC_COMMAND _IOW('C', 3, int) +#define CUSE_IOCTL_GET_SIG _IOR('C', 4, int) +#define CUSE_IOCTL_ALLOC_MEMORY _IOW('C', 5, struct cuse_alloc_info) +#define CUSE_IOCTL_FREE_MEMORY _IOW('C', 6, struct cuse_alloc_info) +#define CUSE_IOCTL_SET_PFH _IOW('C', 7, unsigned long) +#define CUSE_IOCTL_CREATE_DEV _IOW('C', 8, struct cuse_create_dev) +#define CUSE_IOCTL_DESTROY_DEV _IOW('C', 9, struct cuse_dev *) +#define CUSE_IOCTL_ALLOC_UNIT _IOR('C',10, int) +#define CUSE_IOCTL_FREE_UNIT _IOW('C',11, int) +#define CUSE_IOCTL_SELWAKEUP _IOW('C',12, int) +#define CUSE_IOCTL_ALLOC_UNIT_BY_ID _IOWR('C',13, int) +#define CUSE_IOCTL_FREE_UNIT_BY_ID _IOWR('C',14, int) + +#endif /* _CUSE_IOCTL_H_ */ diff --git a/sys/modules/cuse/Makefile b/sys/modules/cuse/Makefile new file mode 100644 index 0000000000000..049eb7e0d8583 --- /dev/null +++ b/sys/modules/cuse/Makefile @@ -0,0 +1,32 @@ +# $FreeBSD$ +# +# Copyright (c) 2010 Hans Petter Selasky. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +.PATH: ${.CURDIR}/../../fs/cuse + +KMOD= cuse +SRCS= cuse.c device_if.h bus_if.h vnode_if.h opt_compat.h + +.include -- cgit v1.3 From 1bb07edbd41ba73e6bfc12bc9f85d92f7c4e312d Mon Sep 17 00:00:00 2001 From: "Pedro F. Giffuni" Date: Fri, 30 May 2014 01:09:07 +0000 Subject: Fix strcasecmp_l() and strncasecmp_l() POSIX 2008 compliance. POSIX.1-2008 specifies that those two functions should be declared by including , not (the latter only has strcoll_l() and strxfrm_l()): http://pubs.opengroup.org/onlinepubs/9699919799/functions/strcasecmp.html Obtained from: DragonFlyBSD Reviewed by: theraven MFC after: 2 weeks --- include/strings.h | 4 ++++ include/xlocale/Makefile | 2 +- include/xlocale/_string.h | 2 -- include/xlocale/_strings.h | 48 ++++++++++++++++++++++++++++++++++++++++++++ lib/libc/string/strcasecmp.3 | 4 ++-- 5 files changed, 55 insertions(+), 5 deletions(-) create mode 100644 include/xlocale/_strings.h (limited to 'include') diff --git a/include/strings.h b/include/strings.h index f2720786f097e..4fb9311657f37 100644 --- a/include/strings.h +++ b/include/strings.h @@ -59,6 +59,10 @@ char *rindex(const char *, int) __pure; /* LEGACY */ #endif int strcasecmp(const char *, const char *) __pure; int strncasecmp(const char *, const char *, size_t) __pure; + +#if __POSIX_VISIBLE >= 200809 || defined(_XLOCALE_H_) +#include +#endif __END_DECLS #endif /* _STRINGS_H_ */ diff --git a/include/xlocale/Makefile b/include/xlocale/Makefile index a35db507a1342..7c20ea96a789e 100644 --- a/include/xlocale/Makefile +++ b/include/xlocale/Makefile @@ -2,7 +2,7 @@ NO_OBJ= INCS= _ctype.h _inttypes.h _langinfo.h _locale.h _monetary.h _stdio.h\ - _stdlib.h _string.h _time.h _uchar.h _wchar.h + _stdlib.h _string.h _strings.h _time.h _uchar.h _wchar.h INCSDIR=${INCLUDEDIR}/xlocale .include diff --git a/include/xlocale/_string.h b/include/xlocale/_string.h index 262728066c275..a7c146efc928f 100644 --- a/include/xlocale/_string.h +++ b/include/xlocale/_string.h @@ -53,9 +53,7 @@ size_t strxfrm_l(char *, const char *, size_t, locale_t); #ifdef _XLOCALE_H_ #ifndef _XLOCALE_STRING2_H #define _XLOCALE_STRING2_H -int strcasecmp_l(const char *, const char *, locale_t); char *strcasestr_l(const char *, const char *, locale_t); -int strncasecmp_l(const char *, const char *, size_t, locale_t); #endif /* _XLOCALE_STRING2_H */ #endif /* _XLOCALE_H_ */ diff --git a/include/xlocale/_strings.h b/include/xlocale/_strings.h new file mode 100644 index 0000000000000..da1cff3e7077c --- /dev/null +++ b/include/xlocale/_strings.h @@ -0,0 +1,48 @@ +/*- + * Copyright (c) 2011, 2012 The FreeBSD Foundation + * 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. + * + * $FreeBSD$ + */ + +#ifndef _LOCALE_T_DEFINED +#define _LOCALE_T_DEFINED +typedef struct _xlocale *locale_t; +#endif + +/* + * This file is included from both strings.h and xlocale.h. We need to expose + * the declarations unconditionally if we are included from xlocale.h, but only + * if we are in POSIX2008 mode if included from string.h. + */ + +#ifndef _XLOCALE_STRINGS1_H +#define _XLOCALE_STRINGS1_H + +/* + * POSIX2008 functions + */ +int strcasecmp_l(const char *, const char *, locale_t); +int strncasecmp_l(const char *, const char *, size_t, locale_t); +#endif /* _XLOCALE_STRINGS1_H */ diff --git a/lib/libc/string/strcasecmp.3 b/lib/libc/string/strcasecmp.3 index 1c8504214f460..4a13cc41d9342 100644 --- a/lib/libc/string/strcasecmp.3 +++ b/lib/libc/string/strcasecmp.3 @@ -30,7 +30,7 @@ .\" @(#)strcasecmp.3 8.1 (Berkeley) 6/9/93 .\" $FreeBSD$ .\" -.Dd June 9, 1993 +.Dd May 29, 2014 .Dt STRCASECMP 3 .Os .Sh NAME @@ -45,7 +45,7 @@ .Fn strcasecmp "const char *s1" "const char *s2" .Ft int .Fn strncasecmp "const char *s1" "const char *s2" "size_t len" -.In string.h +.In strings.h .In xlocale.h .Ft int .Fn strcasecmp_l "const char *s1" "const char *s2" "locale_t loc" -- cgit v1.3 From dd987372782e403e026c2e837a6eef8f8bd979cd Mon Sep 17 00:00:00 2001 From: Tijl Coosemans Date: Fri, 13 Jun 2014 10:08:18 +0000 Subject: Don't install GSS-API headers when the GSSAPI option has been disabled. Some ports assume GSS-API is supported when they find the headers. PR: 189156 Submitted by: Garrett Cooper --- include/Makefile | 9 +++++++-- tools/build/mk/OptionalObsoleteFiles.inc | 3 +++ 2 files changed, 10 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/Makefile b/include/Makefile index 4693c2f874388..f046bc16fc41f 100644 --- a/include/Makefile +++ b/include/Makefile @@ -6,11 +6,11 @@ .include CLEANFILES= osreldate.h version vers.c -SUBDIR= arpa gssapi protocols rpcsvc rpc xlocale +SUBDIR= arpa protocols rpcsvc rpc xlocale INCS= a.out.h ar.h assert.h bitstring.h complex.h cpio.h _ctype.h ctype.h \ db.h \ dirent.h dlfcn.h elf.h elf-hints.h err.h fmtmsg.h fnmatch.h fstab.h \ - fts.h ftw.h getopt.h glob.h grp.h gssapi.h \ + fts.h ftw.h getopt.h glob.h grp.h \ ieeefp.h ifaddrs.h \ inttypes.h iso646.h kenv.h langinfo.h libgen.h limits.h link.h \ locale.h malloc.h malloc_np.h memory.h monetary.h mpool.h mqueue.h \ @@ -67,6 +67,11 @@ _dev_powermac_nvram= dev/powermac_nvram _dev_ieee488= dev/ieee488 .endif +.if ${MK_GSSAPI} != "no" +SUBDIR+= gssapi +INCS+= gssapi.h +.endif + .if ${MK_HESIOD} != "no" INCS+= hesiod.h .endif diff --git a/tools/build/mk/OptionalObsoleteFiles.inc b/tools/build/mk/OptionalObsoleteFiles.inc index 82e0b9b28d323..23e91a7851036 100644 --- a/tools/build/mk/OptionalObsoleteFiles.inc +++ b/tools/build/mk/OptionalObsoleteFiles.inc @@ -1833,6 +1833,9 @@ OLD_DIRS+=usr/share/tmac .endif .if ${MK_GSSAPI} == no +OLD_FILES+=usr/include/gssapi/gssapi.h +OLD_DIRS+=usr/include/gssapi +OLD_FILES+=usr/include/gssapi.h OLD_FILES+=usr/lib/libgssapi.a OLD_FILES+=usr/lib/libgssapi.so OLD_LIBS+=usr/lib/libgssapi.so.10 -- cgit v1.3 From 9f72c0322cff595213c2087d6d04928f72e23b0d Mon Sep 17 00:00:00 2001 From: John Baldwin Date: Fri, 11 Jul 2014 16:16:26 +0000 Subject: Fix some edge cases with rewinddir(): - In the unionfs case, opendir() and fdopendir() read the directory's full contents and cache it. This cache is not refreshed when rewinddir() is called, so rewinddir() will not notice updates to a directory. Fix this by splitting the code to fetch a directory's contents out of __opendir_common() into a new _filldir() function and call this from rewinddir() when operating on a unionfs directory. - If rewinddir() is called on a directory opened with fdopendir() before any directory entries are fetched, rewinddir() will not adjust the seek location of the backing file descriptor. If the file descriptor passed to fdopendir() had a non-zero offset, the rewinddir() will not rewind to the beginning. Fix this by always seeking back to 0 in rewinddir(). This means the dd_rewind hack can also be removed. While here, add missing locking to rewinddir(). CR: https://phabric.freebsd.org/D312 Reviewed by: jilles MFC after: 1 week --- include/dirent.h | 1 + lib/libc/gen/gen-private.h | 1 - lib/libc/gen/opendir.c | 365 +++++++++++++++++++++++++-------------------- lib/libc/gen/readdir.c | 4 +- lib/libc/gen/rewinddir.c | 18 ++- lib/libc/gen/telldir.h | 2 + 6 files changed, 226 insertions(+), 165 deletions(-) (limited to 'include') diff --git a/include/dirent.h b/include/dirent.h index c77d844e399b1..b894bb8453292 100644 --- a/include/dirent.h +++ b/include/dirent.h @@ -63,6 +63,7 @@ typedef struct _dirdesc DIR; #define DTF_NODUP 0x0002 /* don't return duplicate names */ #define DTF_REWIND 0x0004 /* rewind after reading union stack */ #define __DTF_READALL 0x0008 /* everything has been read */ +#define __DTF_SKIPREAD 0x0010 /* assume internal buffer is populated */ #else /* !__BSD_VISIBLE */ diff --git a/lib/libc/gen/gen-private.h b/lib/libc/gen/gen-private.h index e8854ad2d856f..d1fab5f314624 100644 --- a/lib/libc/gen/gen-private.h +++ b/lib/libc/gen/gen-private.h @@ -48,7 +48,6 @@ struct _dirdesc { char *dd_buf; /* data buffer */ int dd_len; /* size of data buffer */ long dd_seek; /* magic cookie returned by getdirentries */ - long dd_rewind; /* magic cookie for rewinding */ int dd_flags; /* flags for readdir */ struct pthread_mutex *dd_lock; /* lock */ struct _telldir *dd_td; /* telldir position recording */ diff --git a/lib/libc/gen/opendir.c b/lib/libc/gen/opendir.c index a9eb0af1ba4ba..54928e73d60ca 100644 --- a/lib/libc/gen/opendir.c +++ b/lib/libc/gen/opendir.c @@ -49,7 +49,7 @@ __FBSDID("$FreeBSD$"); #include "gen-private.h" #include "telldir.h" -static DIR * __opendir_common(int, int); +static DIR * __opendir_common(int, int, bool); /* * Open a directory. @@ -67,18 +67,10 @@ opendir(const char *name) DIR * fdopendir(int fd) { - struct stat statb; - /* Check that fd is associated with a directory. */ - if (_fstat(fd, &statb) != 0) - return (NULL); - if (!S_ISDIR(statb.st_mode)) { - errno = ENOTDIR; - return (NULL); - } if (_fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) return (NULL); - return (__opendir_common(fd, DTF_HIDEW|DTF_NODUP)); + return (__opendir_common(fd, DTF_HIDEW|DTF_NODUP, true)); } DIR * @@ -88,11 +80,13 @@ __opendir2(const char *name, int flags) DIR *dir; int saved_errno; + if ((flags & (__DTF_READALL | __DTF_SKIPREAD)) != 0) + return (NULL); if ((fd = _open(name, O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC)) == -1) return (NULL); - dir = __opendir_common(fd, flags); + dir = __opendir_common(fd, flags, false); if (dir == NULL) { saved_errno = errno; _close(fd); @@ -109,23 +103,196 @@ opendir_compar(const void *p1, const void *p2) (*(const struct dirent **)p2)->d_name)); } +/* + * For a directory at the top of a unionfs stack, the entire directory's + * contents are read and cached locally until the next call to rewinddir(). + * For the fdopendir() case, the initial seek position must be preserved. + * For rewinddir(), the full directory should always be re-read from the + * beginning. + * + * If an error occurs, the existing buffer and state of 'dirp' is left + * unchanged. + */ +bool +_filldir(DIR *dirp, bool use_current_pos) +{ + struct dirent **dpv; + char *buf, *ddptr, *ddeptr; + off_t pos; + int fd2, incr, len, n, saved_errno, space; + + len = 0; + space = 0; + buf = NULL; + ddptr = NULL; + + /* + * Use the system page size if that is a multiple of DIRBLKSIZ. + * Hopefully this can be a big win someday by allowing page + * trades to user space to be done by _getdirentries(). + */ + incr = getpagesize(); + if ((incr % DIRBLKSIZ) != 0) + incr = DIRBLKSIZ; + + /* + * The strategy here is to read all the directory + * entries into a buffer, sort the buffer, and + * remove duplicate entries by setting the inode + * number to zero. + * + * We reopen the directory because _getdirentries() + * on a MNT_UNION mount modifies the open directory, + * making it refer to the lower directory after the + * upper directory's entries are exhausted. + * This would otherwise break software that uses + * the directory descriptor for fchdir or *at + * functions, such as fts.c. + */ + if ((fd2 = _openat(dirp->dd_fd, ".", O_RDONLY | O_CLOEXEC)) == -1) + return (false); + + if (use_current_pos) { + pos = lseek(dirp->dd_fd, 0, SEEK_CUR); + if (pos == -1 || lseek(fd2, pos, SEEK_SET) == -1) { + saved_errno = errno; + _close(fd2); + errno = saved_errno; + return (false); + } + } + + do { + /* + * Always make at least DIRBLKSIZ bytes + * available to _getdirentries + */ + if (space < DIRBLKSIZ) { + space += incr; + len += incr; + buf = reallocf(buf, len); + if (buf == NULL) { + saved_errno = errno; + _close(fd2); + errno = saved_errno; + return (false); + } + ddptr = buf + (len - space); + } + + n = _getdirentries(fd2, ddptr, space, &dirp->dd_seek); + if (n > 0) { + ddptr += n; + space -= n; + } + if (n < 0) { + saved_errno = errno; + _close(fd2); + errno = saved_errno; + return (false); + } + } while (n > 0); + _close(fd2); + + ddeptr = ddptr; + + /* + * There is now a buffer full of (possibly) duplicate + * names. + */ + dirp->dd_buf = buf; + + /* + * Go round this loop twice... + * + * Scan through the buffer, counting entries. + * On the second pass, save pointers to each one. + * Then sort the pointers and remove duplicate names. + */ + for (dpv = 0;;) { + n = 0; + ddptr = buf; + while (ddptr < ddeptr) { + struct dirent *dp; + + dp = (struct dirent *) ddptr; + if ((long)dp & 03L) + break; + if ((dp->d_reclen <= 0) || + (dp->d_reclen > (ddeptr + 1 - ddptr))) + break; + ddptr += dp->d_reclen; + if (dp->d_fileno) { + if (dpv) + dpv[n] = dp; + n++; + } + } + + if (dpv) { + struct dirent *xp; + + /* + * This sort must be stable. + */ + mergesort(dpv, n, sizeof(*dpv), opendir_compar); + + dpv[n] = NULL; + xp = NULL; + + /* + * Scan through the buffer in sort order, + * zapping the inode number of any + * duplicate names. + */ + for (n = 0; dpv[n]; n++) { + struct dirent *dp = dpv[n]; + + if ((xp == NULL) || + strcmp(dp->d_name, xp->d_name)) { + xp = dp; + } else { + dp->d_fileno = 0; + } + if (dp->d_type == DT_WHT && + (dirp->dd_flags & DTF_HIDEW)) + dp->d_fileno = 0; + } + + free(dpv); + break; + } else { + dpv = malloc((n+1) * sizeof(struct dirent *)); + if (dpv == NULL) + break; + } + } + + dirp->dd_len = len; + dirp->dd_size = ddptr - dirp->dd_buf; + return (true); +} + + /* * Common routine for opendir(3), __opendir2(3) and fdopendir(3). */ static DIR * -__opendir_common(int fd, int flags) +__opendir_common(int fd, int flags, bool use_current_pos) { DIR *dirp; int incr; int saved_errno; int unionstack; - int fd2; - - fd2 = -1; if ((dirp = malloc(sizeof(DIR) + sizeof(struct _telldir))) == NULL) return (NULL); + dirp->dd_buf = NULL; + dirp->dd_fd = fd; + dirp->dd_flags = flags; + dirp->dd_loc = 0; + dirp->dd_lock = NULL; dirp->dd_td = (struct _telldir *)((char *)dirp + sizeof(DIR)); LIST_INIT(&dirp->dd_td->td_locq); dirp->dd_td->td_loccnt = 0; @@ -154,163 +321,39 @@ __opendir_common(int fd, int flags) } if (unionstack) { - int len = 0; - int space = 0; - char *buf = 0; - char *ddptr = 0; - char *ddeptr; - int n; - struct dirent **dpv; - - /* - * The strategy here is to read all the directory - * entries into a buffer, sort the buffer, and - * remove duplicate entries by setting the inode - * number to zero. - * - * We reopen the directory because _getdirentries() - * on a MNT_UNION mount modifies the open directory, - * making it refer to the lower directory after the - * upper directory's entries are exhausted. - * This would otherwise break software that uses - * the directory descriptor for fchdir or *at - * functions, such as fts.c. - */ - if ((fd2 = _openat(fd, ".", O_RDONLY | O_CLOEXEC)) == -1) { - saved_errno = errno; - free(buf); - free(dirp); - errno = saved_errno; - return (NULL); - } - - do { - /* - * Always make at least DIRBLKSIZ bytes - * available to _getdirentries - */ - if (space < DIRBLKSIZ) { - space += incr; - len += incr; - buf = reallocf(buf, len); - if (buf == NULL) - goto fail; - ddptr = buf + (len - space); - } - - n = _getdirentries(fd2, ddptr, space, &dirp->dd_seek); - if (n > 0) { - ddptr += n; - space -= n; - } - } while (n > 0); - - ddeptr = ddptr; - flags |= __DTF_READALL; - - _close(fd2); - fd2 = -1; - - /* - * There is now a buffer full of (possibly) duplicate - * names. - */ - dirp->dd_buf = buf; - - /* - * Go round this loop twice... - * - * Scan through the buffer, counting entries. - * On the second pass, save pointers to each one. - * Then sort the pointers and remove duplicate names. - */ - for (dpv = 0;;) { - n = 0; - ddptr = buf; - while (ddptr < ddeptr) { - struct dirent *dp; - - dp = (struct dirent *) ddptr; - if ((long)dp & 03L) - break; - if ((dp->d_reclen <= 0) || - (dp->d_reclen > (ddeptr + 1 - ddptr))) - break; - ddptr += dp->d_reclen; - if (dp->d_fileno) { - if (dpv) - dpv[n] = dp; - n++; - } - } - - if (dpv) { - struct dirent *xp; - - /* - * This sort must be stable. - */ - mergesort(dpv, n, sizeof(*dpv), - opendir_compar); - - dpv[n] = NULL; - xp = NULL; - - /* - * Scan through the buffer in sort order, - * zapping the inode number of any - * duplicate names. - */ - for (n = 0; dpv[n]; n++) { - struct dirent *dp = dpv[n]; - - if ((xp == NULL) || - strcmp(dp->d_name, xp->d_name)) { - xp = dp; - } else { - dp->d_fileno = 0; - } - if (dp->d_type == DT_WHT && - (flags & DTF_HIDEW)) - dp->d_fileno = 0; - } - - free(dpv); - break; - } else { - dpv = malloc((n+1) * sizeof(struct dirent *)); - if (dpv == NULL) - break; - } - } - - dirp->dd_len = len; - dirp->dd_size = ddptr - dirp->dd_buf; + if (!_filldir(dirp, use_current_pos)) + goto fail; + dirp->dd_flags |= __DTF_READALL; } else { dirp->dd_len = incr; - dirp->dd_size = 0; dirp->dd_buf = malloc(dirp->dd_len); if (dirp->dd_buf == NULL) goto fail; - dirp->dd_seek = 0; + if (use_current_pos) { + /* + * Read the first batch of directory entries + * to prime dd_seek. This also checks if the + * fd passed to fdopendir() is a directory. + */ + dirp->dd_size = _getdirentries(dirp->dd_fd, + dirp->dd_buf, dirp->dd_len, &dirp->dd_seek); + if (dirp->dd_size < 0) { + if (errno == EINVAL) + errno = ENOTDIR; + goto fail; + } + dirp->dd_flags |= __DTF_SKIPREAD; + } else { + dirp->dd_size = 0; + dirp->dd_seek = 0; + } } - dirp->dd_loc = 0; - dirp->dd_fd = fd; - dirp->dd_flags = flags; - dirp->dd_lock = NULL; - - /* - * Set up seek point for rewinddir. - */ - dirp->dd_rewind = telldir(dirp); - return (dirp); fail: saved_errno = errno; - if (fd2 != -1) - _close(fd2); + free(dirp->dd_buf); free(dirp); errno = saved_errno; return (NULL); diff --git a/lib/libc/gen/readdir.c b/lib/libc/gen/readdir.c index 324870b39ad1c..69f59d16087ac 100644 --- a/lib/libc/gen/readdir.c +++ b/lib/libc/gen/readdir.c @@ -61,12 +61,14 @@ _readdir_unlocked(dirp, skip) return (NULL); dirp->dd_loc = 0; } - if (dirp->dd_loc == 0 && !(dirp->dd_flags & __DTF_READALL)) { + if (dirp->dd_loc == 0 && + !(dirp->dd_flags & (__DTF_READALL | __DTF_SKIPREAD))) { dirp->dd_size = _getdirentries(dirp->dd_fd, dirp->dd_buf, dirp->dd_len, &dirp->dd_seek); if (dirp->dd_size <= 0) return (NULL); } + dirp->dd_flags &= ~__DTF_SKIPREAD; dp = (struct dirent *)(dirp->dd_buf + dirp->dd_loc); if ((long)dp & 03L) /* bogus pointer check */ return (NULL); diff --git a/lib/libc/gen/rewinddir.c b/lib/libc/gen/rewinddir.c index 0eb091a082469..e1168b3570e2e 100644 --- a/lib/libc/gen/rewinddir.c +++ b/lib/libc/gen/rewinddir.c @@ -33,9 +33,14 @@ static char sccsid[] = "@(#)rewinddir.c 8.1 (Berkeley) 6/8/93"; #include __FBSDID("$FreeBSD$"); +#include "namespace.h" #include #include +#include +#include +#include "un-namespace.h" +#include "libc_private.h" #include "gen-private.h" #include "telldir.h" @@ -44,6 +49,15 @@ rewinddir(dirp) DIR *dirp; { - _seekdir(dirp, dirp->dd_rewind); - dirp->dd_rewind = telldir(dirp); + if (__isthreaded) + _pthread_mutex_lock(&dirp->dd_lock); + if (dirp->dd_flags & __DTF_READALL) + _filldir(dirp, false); + else if (dirp->dd_seek != 0) { + (void) lseek(dirp->dd_fd, 0, SEEK_SET); + dirp->dd_seek = 0; + } + dirp->dd_loc = 0; + if (__isthreaded) + _pthread_mutex_unlock(&dirp->dd_lock); } diff --git a/lib/libc/gen/telldir.h b/lib/libc/gen/telldir.h index ef930d2b80b7c..04989bb7b5360 100644 --- a/lib/libc/gen/telldir.h +++ b/lib/libc/gen/telldir.h @@ -36,6 +36,7 @@ #define _TELLDIR_H_ #include +#include /* * One of these structures is malloced to describe the current directory @@ -59,6 +60,7 @@ struct _telldir { long td_loccnt; /* index of entry for sequential readdir's */ }; +bool _filldir(DIR *, bool); struct dirent *_readdir_unlocked(DIR *, int); void _reclaim_telldir(DIR *); void _seekdir(DIR *, long); -- cgit v1.3 From 7bd26d456798a75b02f0db0d62564a5672a9e1c0 Mon Sep 17 00:00:00 2001 From: "Pedro F. Giffuni" Date: Fri, 18 Jul 2014 02:49:41 +0000 Subject: Minor sorting to match the NetBSD header MFC after: 3 days Obtained from: NetBSD --- include/search.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/search.h b/include/search.h index 4e4606f12720c..d93d806d63af9 100644 --- a/include/search.h +++ b/include/search.h @@ -1,8 +1,8 @@ /*- - * Written by J.T. Conklin + * Written by J.T. Conklin * Public domain. * - * $NetBSD: search.h,v 1.12 1999/02/22 10:34:28 christos Exp $ + * $NetBSD: search.h,v 1.18 2005/07/06 15:47:15 drochner Exp $ * $FreeBSD$ */ @@ -49,12 +49,14 @@ __BEGIN_DECLS int hcreate(size_t); void hdestroy(void); ENTRY *hsearch(ENTRY, ACTION); -void insque(void *, void *); + void *lfind(const void *, const void *, size_t *, size_t, int (*)(const void *, const void *)); void *lsearch(const void *, void *, size_t *, size_t, int (*)(const void *, const void *)); +void insque(void *, void *); void remque(void *); + void *tdelete(const void * __restrict, void ** __restrict, int (*)(const void *, const void *)); void *tfind(const void *, void * const *, -- cgit v1.3 From 1d717f206a16611a52d73b75489b07291cd9989d Mon Sep 17 00:00:00 2001 From: "Pedro F. Giffuni" Date: Fri, 18 Jul 2014 16:21:15 +0000 Subject: Revert r268826: The current ordering of this header is a feature as it is more consistent with POSIX. Also adding gratuitous newlines is not elegant. Pointed out by: bde --- include/search.h | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/search.h b/include/search.h index d93d806d63af9..4e4606f12720c 100644 --- a/include/search.h +++ b/include/search.h @@ -1,8 +1,8 @@ /*- - * Written by J.T. Conklin + * Written by J.T. Conklin * Public domain. * - * $NetBSD: search.h,v 1.18 2005/07/06 15:47:15 drochner Exp $ + * $NetBSD: search.h,v 1.12 1999/02/22 10:34:28 christos Exp $ * $FreeBSD$ */ @@ -49,14 +49,12 @@ __BEGIN_DECLS int hcreate(size_t); void hdestroy(void); ENTRY *hsearch(ENTRY, ACTION); - +void insque(void *, void *); void *lfind(const void *, const void *, size_t *, size_t, int (*)(const void *, const void *)); void *lsearch(const void *, void *, size_t *, size_t, int (*)(const void *, const void *)); -void insque(void *, void *); void remque(void *); - void *tdelete(const void * __restrict, void ** __restrict, int (*)(const void *, const void *)); void *tfind(const void *, void * const *, -- cgit v1.3 From 9823a90c79bac3a172a02f7e76947fcbb57e0955 Mon Sep 17 00:00:00 2001 From: "Pedro F. Giffuni" Date: Mon, 21 Jul 2014 15:22:48 +0000 Subject: Add re-entrant versions of the hash functions based on the GNU api. While testing this I found a conformance issue in hdestroy() that will be fixed in a subsequent commit. Obtained from: NetBSD (hcreate.c, CVS Rev. 1.7) --- include/search.h | 20 ++++++++- lib/libc/stdlib/Makefile.inc | 1 + lib/libc/stdlib/Symbol.map | 3 ++ lib/libc/stdlib/hcreate.3 | 93 ++++++++++++++++++++++++++++++++++++------ lib/libc/stdlib/hcreate.c | 97 +++++++++++++++++++++++++++++++------------- 5 files changed, 171 insertions(+), 43 deletions(-) (limited to 'include') diff --git a/include/search.h b/include/search.h index 4e4606f12720c..068c82d3ca61b 100644 --- a/include/search.h +++ b/include/search.h @@ -1,8 +1,8 @@ /*- - * Written by J.T. Conklin + * Written by J.T. Conklin * Public domain. * - * $NetBSD: search.h,v 1.12 1999/02/22 10:34:28 christos Exp $ + * $NetBSD: search.h,v 1.16 2005/02/03 04:39:32 perry Exp $ * $FreeBSD$ */ @@ -45,6 +45,15 @@ struct que_elem { }; #endif +#if __BSD_VISIBLE +struct _ENTRY; +struct hsearch_data { + struct _ENTRY *table; + size_t size; + size_t filled; +}; +#endif + __BEGIN_DECLS int hcreate(size_t); void hdestroy(void); @@ -61,6 +70,13 @@ void *tfind(const void *, void * const *, int (*)(const void *, const void *)); void *tsearch(const void *, void **, int (*)(const void *, const void *)); void twalk(const void *, void (*)(const void *, VISIT, int)); + +#if __BSD_VISIBLE +int hcreate_r(size_t, struct hsearch_data *); +void hdestroy_r(struct hsearch_data *); +int hsearch_r(ENTRY, ACTION, ENTRY **, struct hsearch_data *); +#endif + __END_DECLS #endif /* !_SEARCH_H_ */ diff --git a/lib/libc/stdlib/Makefile.inc b/lib/libc/stdlib/Makefile.inc index 68dda94c5d40a..57205a756a974 100644 --- a/lib/libc/stdlib/Makefile.inc +++ b/lib/libc/stdlib/Makefile.inc @@ -35,6 +35,7 @@ MLINKS+=exit.3 _Exit.3 MLINKS+=getenv.3 putenv.3 getenv.3 setenv.3 getenv.3 unsetenv.3 MLINKS+=getopt_long.3 getopt_long_only.3 MLINKS+=hcreate.3 hdestroy.3 hcreate.3 hsearch.3 +MLINKS+=hcreate.3 hcreate_r.3 hcreate.3 hdestroy_r.3 hcreate.3 hsearch_r.3 MLINKS+=insque.3 remque.3 MLINKS+=lsearch.3 lfind.3 MLINKS+=ptsname.3 grantpt.3 ptsname.3 unlockpt.3 diff --git a/lib/libc/stdlib/Symbol.map b/lib/libc/stdlib/Symbol.map index d28a8e9ecbd52..64c0e169dd270 100644 --- a/lib/libc/stdlib/Symbol.map +++ b/lib/libc/stdlib/Symbol.map @@ -109,6 +109,9 @@ FBSD_1.4 { heapsort_b; mergesort_b; qsort_b; + hcreate_r; + hdestroy_r; + hsearch_r; }; FBSDprivate_1.0 { diff --git a/lib/libc/stdlib/hcreate.3 b/lib/libc/stdlib/hcreate.3 index 2466c9f428a63..2161f9262c8b9 100644 --- a/lib/libc/stdlib/hcreate.3 +++ b/lib/libc/stdlib/hcreate.3 @@ -28,11 +28,16 @@ .\" .\" $FreeBSD$ .\" -.Dd July 6, 2008 +.Dd July 21, 2014 .Dt HCREATE 3 .Os .Sh NAME -.Nm hcreate , hdestroy , hsearch +.Nm hcreate , +.Nm hcreate_r , +.Nm hdestroy , +.Nm hdestroy_r , +.Nm hsearch , +.Nm hsearch_r .Nd manage hash search table .Sh LIBRARY .Lb libc @@ -40,16 +45,25 @@ .In search.h .Ft int .Fn hcreate "size_t nel" +.Ft int +.Fn hcreate_r "size_t nel" "struct hsearch_data *table" +.Ft void +.Fn hdestroy "void" .Ft void -.Fn hdestroy void +.Fn hdestroy_r "struct hsearch_data *table" .Ft ENTRY * .Fn hsearch "ENTRY item" "ACTION action" +.Ft int +.Fn hsearch_r "ENTRY item" "ACTION action" "ENTRY ** itemp" "struct hsearch_data *table" .Sh DESCRIPTION The .Fn hcreate , +.Fn hcreate_r , .Fn hdestroy , +.Fn hdestroy_r +.Fn hsearch , and -.Fn hsearch +.Fn hsearch_r functions manage hash search tables. .Pp The @@ -90,7 +104,7 @@ argument is a structure of type .Vt ENTRY (defined in the .In search.h -header) containing two pointers: +header) that contains two pointers: .Fa item.key points to the comparison key (a .Vt "char *" ) , @@ -136,21 +150,50 @@ is and .Fn hdestroy is called. +.Pp +The +.Fn hcreate_r , +.Fn hdestroy_r , +and +.Fn hsearch_r +functions are re-entrant versions of the above functions that can +operate on a table supplied by the user. +The +.Fn hsearch_r +function returns +.Dv 0 +if the action is +.Dv ENTER +and the element cannot be created, +.Dv 1 +otherwise. +If the element exists or can be created, it will be placed in +.Fa itemp , +otherwise +.Fa itemp +will be set to +.Dv NULL . .Sh RETURN VALUES The .Fn hcreate -function returns 0 if the table creation failed and the global variable +and +.Fn hcreate_r +functions return 0 if the table creation failed and the global variable .Va errno is set to indicate the error; otherwise, a non-zero value is returned. .Pp The .Fn hdestroy -function does not return a value. +and +.Fn hdestroy_r +functions return no value. .Pp The .Fn hsearch -function returns a +and +.Fn hsearch_r +functions return a .Dv NULL pointer if either the .Fa action @@ -223,15 +266,31 @@ main(void) .Sh ERRORS The .Fn hcreate -and +.Fn hcreate_r , .Fn hsearch -functions may fail if: +and +.Fn hsearch_r +functions will fail if: .Bl -tag -width Er .It Bq Er ENOMEM -Insufficient storage space is available. +Insufficient memory is available. .It Bq Er EINVAL A table already exists. .El +.Pp +The +.Fn hsearch +and +.Fn hsearch_r +functions will also fail if the action is +.Dv SEARCH +and the element is not found: +.Bl -tag -width Er +.It Bq Er ESRCH +The +.Fa item +given is not found. +.El .Sh SEE ALSO .Xr bsearch 3 , .Xr lsearch 3 , @@ -254,5 +313,15 @@ and .Fn hsearch functions first appeared in .At V . +The +.Fn hcreate_r , +.Fn hdestroy_r +and +.Fn hsearch_r +functions are +.Tn GNU +extensions. .Sh BUGS -The interface permits the use of only one hash table at a time. +The original, +.Pf non- Tn GNU +interface permits the use of only one hash table at a time. diff --git a/lib/libc/stdlib/hcreate.c b/lib/libc/stdlib/hcreate.c index cfcd11583be3c..8bb4d3e07fb00 100644 --- a/lib/libc/stdlib/hcreate.c +++ b/lib/libc/stdlib/hcreate.c @@ -1,4 +1,4 @@ -/* $NetBSD: hcreate.c,v 1.6 2008/07/21 12:05:43 lukem Exp $ */ +/* $NetBSD: hcreate.c,v 1.7 2011/09/14 23:33:51 christos Exp $ */ /* * Copyright (c) 2001 Christopher G. Demetriou @@ -49,7 +49,7 @@ #include #if 0 #if defined(LIBC_SCCS) && !defined(lint) -__RCSID("$NetBSD: hcreate.c,v 1.6 2008/07/21 12:05:43 lukem Exp $"); +__RCSID("$NetBSD: hcreate.c,v 1.7 2011/09/14 23:33:51 christos Exp $"); #endif /* LIBC_SCCS and not lint */ #endif __FBSDID("$FreeBSD$"); @@ -84,20 +84,27 @@ SLIST_HEAD(internal_head, internal_entry); /* Default hash function, from db/hash/hash_func.c */ extern u_int32_t (*__default_hash)(const void *, size_t); -static struct internal_head *htable; -static size_t htablesize; +static struct hsearch_data htable; int hcreate(size_t nel) { - size_t idx; - unsigned int p2; /* Make sure this is not called when a table already exists. */ - if (htable != NULL) { + if (htable.table != NULL) { errno = EINVAL; return 0; } + return hcreate_r(nel, &htable); +} + +int +hcreate_r(size_t nel, struct hsearch_data *head) +{ + struct internal_head *table; + size_t idx; + unsigned int p2; + void *p; /* If nel is too small, make it min sized. */ if (nel < MIN_BUCKETS) @@ -115,71 +122,103 @@ hcreate(size_t nel) } /* Allocate the table. */ - htablesize = nel; - htable = malloc(htablesize * sizeof htable[0]); - if (htable == NULL) { + head->size = nel; + head->filled = 0; + p = malloc(nel * sizeof table[0]); + if (p == NULL) { errno = ENOMEM; return 0; } + head->table = p; + table = p; /* Initialize it. */ - for (idx = 0; idx < htablesize; idx++) - SLIST_INIT(&htable[idx]); + for (idx = 0; idx < nel; idx++) + SLIST_INIT(&table[idx]); return 1; } void hdestroy(void) +{ + hdestroy_r(&htable); +} + +void +hdestroy_r(struct hsearch_data *head) { struct internal_entry *ie; size_t idx; + void *p; + struct internal_head *table; - if (htable == NULL) + if (head == NULL) return; - for (idx = 0; idx < htablesize; idx++) { - while (!SLIST_EMPTY(&htable[idx])) { - ie = SLIST_FIRST(&htable[idx]); - SLIST_REMOVE_HEAD(&htable[idx], link); + p = head->table; + head->table = NULL; + table = p; + + for (idx = 0; idx < head->size; idx++) { + while (!SLIST_EMPTY(&table[idx])) { + ie = SLIST_FIRST(&table[idx]); + SLIST_REMOVE_HEAD(&table[idx], link); free(ie->ent.key); free(ie); } } - free(htable); - htable = NULL; + free(table); } ENTRY * hsearch(ENTRY item, ACTION action) { - struct internal_head *head; + ENTRY *ep; + (void)hsearch_r(item, action, &ep, &htable); + return ep; +} + +int +hsearch_r(ENTRY item, ACTION action, ENTRY **itemp, struct hsearch_data *head) +{ + struct internal_head *table, *chain; struct internal_entry *ie; uint32_t hashval; size_t len; + void *p; + + p = head->table; + table = p; len = strlen(item.key); hashval = (*__default_hash)(item.key, len); - head = &htable[hashval & (htablesize - 1)]; - ie = SLIST_FIRST(head); + chain = &table[hashval & (head->size - 1)]; + ie = SLIST_FIRST(chain); while (ie != NULL) { if (strcmp(ie->ent.key, item.key) == 0) break; ie = SLIST_NEXT(ie, link); } - if (ie != NULL) - return &ie->ent; - else if (action == FIND) - return NULL; + if (ie != NULL) { + *itemp = &ie->ent; + return 1; + } else if (action == FIND) { + *itemp = NULL; + errno = ESRCH; + return 1; + } ie = malloc(sizeof *ie); if (ie == NULL) - return NULL; + return 0; ie->ent.key = item.key; ie->ent.data = item.data; - SLIST_INSERT_HEAD(head, ie, link); - return &ie->ent; + SLIST_INSERT_HEAD(chain, ie, link); + *itemp = &ie->ent; + head->filled++; + return 1; } -- cgit v1.3