Index: Makefile.inc1 =================================================================== --- Makefile.inc1 +++ Makefile.inc1 @@ -1652,7 +1652,7 @@ lib/libelf lib/libexpat \ lib/libfigpar \ ${_lib_libgssapi} ${_lib_libipx} \ - lib/libkiconv lib/libkvm lib/liblzma lib/libmd \ + lib/libkiconv lib/libkvm lib/liblzma lib/libmd lib/libnv \ lib/ncurses/ncurses lib/ncurses/ncursesw \ lib/libopie lib/libpam ${_lib_libthr} \ ${_lib_libradius} lib/libsbuf lib/libtacplus \ Index: cddl/lib/libnvpair/Makefile =================================================================== --- cddl/lib/libnvpair/Makefile +++ cddl/lib/libnvpair/Makefile @@ -7,10 +7,10 @@ SRCS= libnvpair.c \ nvpair_alloc_system.c \ - nvpair_alloc_fixed.c \ - nvpair.c \ nvpair_json.c \ - fnvpair.c + opensolaris_fnvpair.c \ + opensolaris_nvpair.c \ + opensolaris_nvpair_alloc_fixed.c WARNS?= 0 CFLAGS+= -I${.CURDIR}/../../../cddl/compat/opensolaris/include Index: contrib/mdocml/lib.in =================================================================== --- contrib/mdocml/lib.in +++ contrib/mdocml/lib.in @@ -67,6 +67,7 @@ LINE("libmenu", "Curses Menu Library (libmenu, \\-lmenu)") LINE("libnetgraph", "Netgraph User Library (libnetgraph, \\-lnetgraph)") LINE("libnetpgp", "Netpgp signing, verification, encryption and decryption (libnetpgp, \\-lnetpgp)") +LINE("libnv", "Name/value pairs library (libnv, \\-lnv)") LINE("libossaudio", "OSS Audio Emulation Library (libossaudio, \\-lossaudio)") LINE("libpam", "Pluggable Authentication Module Library (libpam, \\-lpam)") LINE("libpcap", "Packet Capture Library (libpcap, \\-lpcap)") Index: etc/mtree/BSD.tests.dist =================================================================== --- etc/mtree/BSD.tests.dist +++ etc/mtree/BSD.tests.dist @@ -138,6 +138,8 @@ .. libmp .. + libnv + .. librt .. libthr Index: lib/Makefile =================================================================== --- lib/Makefile +++ lib/Makefile @@ -70,6 +70,7 @@ libnetbsd \ ${_libnetgraph} \ ${_libngatm} \ + libnv \ libopie \ libpam \ libpcap \ Index: lib/libnv/Makefile =================================================================== --- /dev/null +++ lib/libnv/Makefile @@ -0,0 +1,24 @@ +# $FreeBSD: head/lib/libnv/Makefile 285139 2015-07-04 16:33:37Z oshogbo $ + +SHLIBDIR?= /lib + +.include + +LIB= nv +SHLIB_MAJOR= 0 + +.PATH: ${.CURDIR}/../../sys/contrib/libnv ${.CURDIR}/../../sys/sys +CFLAGS+=-I${.CURDIR}/../../sys -I${.CURDIR} + +SRCS= dnvlist.c +SRCS+= msgio.c +SRCS+= nvlist.c +SRCS+= nvpair.c + +WARNS?= 6 + +.if ${MK_TESTS} != "no" +SUBDIR+= tests +.endif + +.include Index: lib/libnv/Makefile.depend =================================================================== --- /dev/null +++ lib/libnv/Makefile.depend @@ -0,0 +1,19 @@ +# $FreeBSD: head/lib/libnv/Makefile.depend 284345 2015-06-13 19:20:56Z sjg $ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + gnu/lib/csu \ + gnu/lib/libgcc \ + include \ + include/xlocale \ + lib/${CSU_DIR} \ + lib/libc \ + lib/libcompiler_rt \ + usr.bin/xinstall.host \ + + +.include + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif Index: lib/libnv/common_impl.h =================================================================== --- /dev/null +++ lib/libnv/common_impl.h @@ -0,0 +1,37 @@ +/*- + * Copyright (c) 2013 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Pawel Jakub Dawidek under sponsorship from + * the FreeBSD Foundation. + * + * 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 AUTHORS 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 AUTHORS 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: head/lib/libnv/common_impl.h 258065 2013-11-12 19:39:14Z pjd $ + */ + +#ifndef _COMMON_IMPL_H_ +#define _COMMON_IMPL_H_ + +#define fd_is_valid(fd) (fcntl((fd), F_GETFL) != -1 || errno != EBADF) + +#endif /* !_COMMON_IMPL_H_ */ Index: lib/libnv/msgio.h =================================================================== --- /dev/null +++ lib/libnv/msgio.h @@ -0,0 +1,50 @@ +/*- + * Copyright (c) 2013 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Pawel Jakub Dawidek under sponsorship from + * the FreeBSD Foundation. + * + * Copyright (c) 2013 Mariusz Zaborski + * + * 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 AUTHORS 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 AUTHORS 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: head/lib/libnv/msgio.h 259430 2013-12-15 22:58:09Z pjd $ + */ + +#ifndef _MSGIO_H_ +#define _MSGIO_H_ + +struct cmsgcred; +struct iovec; +struct msghdr; + +int cred_send(int sock); +int cred_recv(int sock, struct cmsgcred *cred); + +int fd_send(int sock, const int *fds, size_t nfds); +int fd_recv(int sock, int *fds, size_t nfds); + +int buf_send(int sock, void *buf, size_t size); +int buf_recv(int sock, void *buf, size_t size); + +#endif /* !_MSGIO_H_ */ Index: lib/libnv/msgio.c =================================================================== --- /dev/null +++ lib/libnv/msgio.c @@ -0,0 +1,469 @@ +/*- + * Copyright (c) 2013 The FreeBSD Foundation + * Copyright (c) 2013 Mariusz Zaborski + * All rights reserved. + * + * This software was developed by Pawel Jakub Dawidek under sponsorship from + * the FreeBSD Foundation. + * + * 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 AUTHORS 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 AUTHORS 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 +__FBSDID("$FreeBSD: head/lib/libnv/msgio.c 271578 2014-09-14 09:27:12Z pjd $"); + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_PJDLOG +#include +#endif + +#include "common_impl.h" +#include "msgio.h" + +#ifndef HAVE_PJDLOG +#include +#define PJDLOG_ASSERT(...) assert(__VA_ARGS__) +#define PJDLOG_RASSERT(expr, ...) assert(expr) +#define PJDLOG_ABORT(...) abort() +#endif + +#define PKG_MAX_SIZE (MCLBYTES / CMSG_SPACE(sizeof(int)) - 1) + +static int +msghdr_add_fd(struct cmsghdr *cmsg, int fd) +{ + + PJDLOG_ASSERT(fd >= 0); + + if (!fd_is_valid(fd)) { + errno = EBADF; + return (-1); + } + + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(fd)); + bcopy(&fd, CMSG_DATA(cmsg), sizeof(fd)); + + return (0); +} + +static int +msghdr_get_fd(struct cmsghdr *cmsg) +{ + int fd; + + if (cmsg == NULL || cmsg->cmsg_level != SOL_SOCKET || + cmsg->cmsg_type != SCM_RIGHTS || + cmsg->cmsg_len != CMSG_LEN(sizeof(fd))) { + errno = EINVAL; + return (-1); + } + + bcopy(CMSG_DATA(cmsg), &fd, sizeof(fd)); +#ifndef MSG_CMSG_CLOEXEC + /* + * If the MSG_CMSG_CLOEXEC flag is not available we cannot set the + * close-on-exec flag atomically, but we still want to set it for + * consistency. + */ + (void) fcntl(fd, F_SETFD, FD_CLOEXEC); +#endif + + return (fd); +} + +static void +fd_wait(int fd, bool doread) +{ + fd_set fds; + + PJDLOG_ASSERT(fd >= 0); + + FD_ZERO(&fds); + FD_SET(fd, &fds); + (void)select(fd + 1, doread ? &fds : NULL, doread ? NULL : &fds, + NULL, NULL); +} + +static int +msg_recv(int sock, struct msghdr *msg) +{ + int flags; + + PJDLOG_ASSERT(sock >= 0); + +#ifdef MSG_CMSG_CLOEXEC + flags = MSG_CMSG_CLOEXEC; +#else + flags = 0; +#endif + + for (;;) { + fd_wait(sock, true); + if (recvmsg(sock, msg, flags) == -1) { + if (errno == EINTR) + continue; + return (-1); + } + break; + } + + return (0); +} + +static int +msg_send(int sock, const struct msghdr *msg) +{ + + PJDLOG_ASSERT(sock >= 0); + + for (;;) { + fd_wait(sock, false); + if (sendmsg(sock, msg, 0) == -1) { + if (errno == EINTR) + continue; + return (-1); + } + break; + } + + return (0); +} + +int +cred_send(int sock) +{ + unsigned char credbuf[CMSG_SPACE(sizeof(struct cmsgcred))]; + struct msghdr msg; + struct cmsghdr *cmsg; + struct iovec iov; + uint8_t dummy; + + bzero(credbuf, sizeof(credbuf)); + bzero(&msg, sizeof(msg)); + bzero(&iov, sizeof(iov)); + + /* + * XXX: We send one byte along with the control message, because + * setting msg_iov to NULL only works if this is the first + * packet send over the socket. Once we send some data we + * won't be able to send credentials anymore. This is most + * likely a kernel bug. + */ + dummy = 0; + iov.iov_base = &dummy; + iov.iov_len = sizeof(dummy); + + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = credbuf; + msg.msg_controllen = sizeof(credbuf); + + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len = CMSG_LEN(sizeof(struct cmsgcred)); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_CREDS; + + if (msg_send(sock, &msg) == -1) + return (-1); + + return (0); +} + +int +cred_recv(int sock, struct cmsgcred *cred) +{ + unsigned char credbuf[CMSG_SPACE(sizeof(struct cmsgcred))]; + struct msghdr msg; + struct cmsghdr *cmsg; + struct iovec iov; + uint8_t dummy; + + bzero(credbuf, sizeof(credbuf)); + bzero(&msg, sizeof(msg)); + bzero(&iov, sizeof(iov)); + + iov.iov_base = &dummy; + iov.iov_len = sizeof(dummy); + + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = credbuf; + msg.msg_controllen = sizeof(credbuf); + + if (msg_recv(sock, &msg) == -1) + return (-1); + + cmsg = CMSG_FIRSTHDR(&msg); + if (cmsg == NULL || + cmsg->cmsg_len != CMSG_LEN(sizeof(struct cmsgcred)) || + cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_CREDS) { + errno = EINVAL; + return (-1); + } + bcopy(CMSG_DATA(cmsg), cred, sizeof(*cred)); + + return (0); +} + +static int +fd_package_send(int sock, const int *fds, size_t nfds) +{ + struct msghdr msg; + struct cmsghdr *cmsg; + struct iovec iov; + unsigned int i; + int serrno, ret; + uint8_t dummy; + + PJDLOG_ASSERT(sock >= 0); + PJDLOG_ASSERT(fds != NULL); + PJDLOG_ASSERT(nfds > 0); + + bzero(&msg, sizeof(msg)); + + /* + * XXX: Look into cred_send function for more details. + */ + dummy = 0; + iov.iov_base = &dummy; + iov.iov_len = sizeof(dummy); + + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_controllen = nfds * CMSG_SPACE(sizeof(int)); + msg.msg_control = calloc(1, msg.msg_controllen); + if (msg.msg_control == NULL) + return (-1); + + ret = -1; + + for (i = 0, cmsg = CMSG_FIRSTHDR(&msg); i < nfds && cmsg != NULL; + i++, cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if (msghdr_add_fd(cmsg, fds[i]) == -1) + goto end; + } + + if (msg_send(sock, &msg) == -1) + goto end; + + ret = 0; +end: + serrno = errno; + free(msg.msg_control); + errno = serrno; + return (ret); +} + +static int +fd_package_recv(int sock, int *fds, size_t nfds) +{ + struct msghdr msg; + struct cmsghdr *cmsg; + unsigned int i; + int serrno, ret; + struct iovec iov; + uint8_t dummy; + + PJDLOG_ASSERT(sock >= 0); + PJDLOG_ASSERT(nfds > 0); + PJDLOG_ASSERT(fds != NULL); + + i = 0; + bzero(&msg, sizeof(msg)); + bzero(&iov, sizeof(iov)); + + /* + * XXX: Look into cred_send function for more details. + */ + iov.iov_base = &dummy; + iov.iov_len = sizeof(dummy); + + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_controllen = nfds * CMSG_SPACE(sizeof(int)); + msg.msg_control = calloc(1, msg.msg_controllen); + if (msg.msg_control == NULL) + return (-1); + + ret = -1; + + if (msg_recv(sock, &msg) == -1) + goto end; + + for (i = 0, cmsg = CMSG_FIRSTHDR(&msg); i < nfds && cmsg != NULL; + i++, cmsg = CMSG_NXTHDR(&msg, cmsg)) { + fds[i] = msghdr_get_fd(cmsg); + if (fds[i] < 0) + break; + } + + if (cmsg != NULL || i < nfds) { + int fd; + + /* + * We need to close all received descriptors, even if we have + * different control message (eg. SCM_CREDS) in between. + */ + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; + cmsg = CMSG_NXTHDR(&msg, cmsg)) { + fd = msghdr_get_fd(cmsg); + if (fd >= 0) + close(fd); + } + errno = EINVAL; + goto end; + } + + ret = 0; +end: + serrno = errno; + free(msg.msg_control); + errno = serrno; + return (ret); +} + +int +fd_recv(int sock, int *fds, size_t nfds) +{ + unsigned int i, step, j; + int ret, serrno; + + if (nfds == 0 || fds == NULL) { + errno = EINVAL; + return (-1); + } + + ret = i = step = 0; + while (i < nfds) { + if (PKG_MAX_SIZE < nfds - i) + step = PKG_MAX_SIZE; + else + step = nfds - i; + ret = fd_package_recv(sock, fds + i, step); + if (ret != 0) { + /* Close all received descriptors. */ + serrno = errno; + for (j = 0; j < i; j++) + close(fds[j]); + errno = serrno; + break; + } + i += step; + } + + return (ret); +} + +int +fd_send(int sock, const int *fds, size_t nfds) +{ + unsigned int i, step; + int ret; + + if (nfds == 0 || fds == NULL) { + errno = EINVAL; + return (-1); + } + + ret = i = step = 0; + while (i < nfds) { + if (PKG_MAX_SIZE < nfds - i) + step = PKG_MAX_SIZE; + else + step = nfds - i; + ret = fd_package_send(sock, fds + i, step); + if (ret != 0) + break; + i += step; + } + + return (ret); +} + +int +buf_send(int sock, void *buf, size_t size) +{ + ssize_t done; + unsigned char *ptr; + + PJDLOG_ASSERT(sock >= 0); + PJDLOG_ASSERT(size > 0); + PJDLOG_ASSERT(buf != NULL); + + ptr = buf; + do { + fd_wait(sock, false); + done = send(sock, ptr, size, 0); + if (done == -1) { + if (errno == EINTR) + continue; + return (-1); + } else if (done == 0) { + errno = ENOTCONN; + return (-1); + } + size -= done; + ptr += done; + } while (size > 0); + + return (0); +} + +int +buf_recv(int sock, void *buf, size_t size) +{ + ssize_t done; + unsigned char *ptr; + + PJDLOG_ASSERT(sock >= 0); + PJDLOG_ASSERT(buf != NULL); + + ptr = buf; + while (size > 0) { + fd_wait(sock, true); + done = recv(sock, ptr, size, 0); + if (done == -1) { + if (errno == EINTR) + continue; + return (-1); + } else if (done == 0) { + errno = ENOTCONN; + return (-1); + } + size -= done; + ptr += done; + } + + return (0); +} Index: lib/libnv/tests/Makefile =================================================================== --- /dev/null +++ lib/libnv/tests/Makefile @@ -0,0 +1,22 @@ +# $FreeBSD: head/lib/libnv/tests/Makefile 289172 2015-10-12 08:16:03Z ngie $ + +TESTSDIR= ${TESTSBASE}/lib/libnv + +ATF_TESTS_CXX= \ + dnv_tests \ + nv_array_tests \ + nv_tests \ + +TAP_TESTS_C+= nvlist_add_test +TAP_TESTS_C+= nvlist_exists_test +TAP_TESTS_C+= nvlist_free_test +TAP_TESTS_C+= nvlist_get_test +TAP_TESTS_C+= nvlist_move_test +TAP_TESTS_C+= nvlist_send_recv_test + +DPADD+= ${LIBNV} +LDADD+= -lnv + +WARNS?= 3 + +.include Index: lib/libnv/tests/dnv_tests.cc =================================================================== --- /dev/null +++ lib/libnv/tests/dnv_tests.cc @@ -0,0 +1,568 @@ +/*- + * Copyright (c) 2014-2015 Sandvine Inc. All rights reserved. + * 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 +__FBSDID("$FreeBSD: head/lib/libnv/tests/dnv_tests.cc 285063 2015-07-02 21:58:10Z oshogbo $"); + +#include +#include + +#include + +ATF_TEST_CASE_WITHOUT_HEAD(dnvlist_get_bool__present); +ATF_TEST_CASE_BODY(dnvlist_get_bool__present) +{ + nvlist_t *nvl; + const char *key; + bool value; + + nvl = nvlist_create(0); + + key = "name"; + value = true; + nvlist_add_bool(nvl, key, value); + + ATF_REQUIRE_EQ(dnvlist_get_bool(nvl, key, false), value); + ATF_REQUIRE_EQ(dnvlist_get_bool(nvl, "name", false), value); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(dnvlist_get_bool__default_value); +ATF_TEST_CASE_BODY(dnvlist_get_bool__default_value) +{ + nvlist_t *nvl; + const char *key; + + key = "123"; + nvl = nvlist_create(0); + + ATF_REQUIRE_EQ(dnvlist_get_bool(nvl, key, false), false); + ATF_REQUIRE_EQ(dnvlist_get_bool(nvl, "123", true), true); + + nvlist_add_bool(nvl, key, true); + + ATF_REQUIRE_EQ(dnvlist_get_bool(nvl, "otherkey", true), true); + ATF_REQUIRE_EQ(dnvlist_get_bool(nvl, "12c", false), false); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(dnvlist_get_number__present); +ATF_TEST_CASE_BODY(dnvlist_get_number__present) +{ + nvlist_t *nvl; + const char *key; + uint64_t value; + + nvl = nvlist_create(0); + + key = "key"; + value = 48952; + nvlist_add_number(nvl, key, value); + + ATF_REQUIRE_EQ(dnvlist_get_number(nvl, key, 19), value); + ATF_REQUIRE_EQ(dnvlist_get_number(nvl, "key", 65), value); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(dnvlist_get_number__default_value); +ATF_TEST_CASE_BODY(dnvlist_get_number__default_value) +{ + nvlist_t *nvl; + const char *key; + + key = "123"; + nvl = nvlist_create(0); + + ATF_REQUIRE_EQ(dnvlist_get_number(nvl, key, 5), 5); + ATF_REQUIRE_EQ(dnvlist_get_number(nvl, "1234", 5), 5); + + nvlist_add_number(nvl, key, 24841); + + ATF_REQUIRE_EQ(dnvlist_get_number(nvl, "1234", 5641), 5641); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(dnvlist_get_string__present); +ATF_TEST_CASE_BODY(dnvlist_get_string__present) +{ + nvlist_t *nvl; + const char *key; + const char *value, *actual_value; + + nvl = nvlist_create(0); + + key = "string"; + value = "fjdojfdi"; + nvlist_add_string(nvl, key, value); + + ATF_REQUIRE_EQ(strcmp(dnvlist_get_string(nvl, key, "g"), value), 0); + + actual_value = dnvlist_get_string(nvl, key, "rs"); + ATF_REQUIRE_EQ(strcmp(actual_value, value), 0); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(dnvlist_get_string__default_value); +ATF_TEST_CASE_BODY(dnvlist_get_string__default_value) +{ + nvlist_t *nvl; + const char *key; + const char *actual_value; + + key = "123"; + nvl = nvlist_create(0); + + ATF_REQUIRE_EQ(strcmp(dnvlist_get_string(nvl, key, "bar"), "bar"), 0); + + actual_value = dnvlist_get_string(nvl, key, "d"); + ATF_REQUIRE_EQ(strcmp(actual_value, "d"), 0); + + nvlist_add_string(nvl, key, "cxhweh"); + + ATF_REQUIRE_EQ(strcmp(dnvlist_get_string(nvl, "hthth", "fd"), "fd"), 0); + actual_value = dnvlist_get_string(nvl, "5", "5"); + ATF_REQUIRE_EQ(strcmp("5", "5"), 0); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(dnvlist_get_nvlist__present); +ATF_TEST_CASE_BODY(dnvlist_get_nvlist__present) +{ + nvlist_t *nvl; + const char *key; + nvlist_t *value; + const nvlist_t *actual_value; + + nvl = nvlist_create(0); + + key = "nvlist"; + value = nvlist_create(0); + nvlist_move_nvlist(nvl, key, value); + + actual_value = dnvlist_get_nvlist(nvl, key, NULL); + ATF_REQUIRE(actual_value != NULL); + ATF_REQUIRE(nvlist_empty(actual_value)); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(dnvlist_get_nvlist__default_value); +ATF_TEST_CASE_BODY(dnvlist_get_nvlist__default_value) +{ + nvlist_t *nvl; + const char *key; + nvlist_t *dummy; + + key = "123"; + nvl = nvlist_create(0); + dummy = nvlist_create(0); + + ATF_REQUIRE_EQ(dnvlist_get_nvlist(nvl, key, dummy), dummy); + + nvlist_move_nvlist(nvl, key, nvlist_create(0)); + ATF_REQUIRE_EQ(dnvlist_get_nvlist(nvl, "456", dummy), dummy); + ATF_REQUIRE_EQ(dnvlist_get_nvlist(nvl, "gh", dummy), dummy); + + nvlist_destroy(nvl); +} + +static void +set_const_binary_value(const void *&value, size_t &size, const char *str) +{ + + value = str; + size = strlen(str) + 1; /* +1 to include '\0' */ +} + +ATF_TEST_CASE_WITHOUT_HEAD(dnvlist_get_binary__present); +ATF_TEST_CASE_BODY(dnvlist_get_binary__present) +{ + nvlist_t *nvl; + const char *k; + const void *value, *actual_value; + size_t value_size, actual_size; + + nvl = nvlist_create(0); + + k = "binary"; + set_const_binary_value(value, value_size, "fjdojfdi"); + nvlist_add_binary(nvl, k, value, value_size); + + actual_value = dnvlist_get_binary(nvl, k, &actual_size, "g", 1); + ATF_REQUIRE_EQ(value_size, actual_size); + ATF_REQUIRE_EQ(memcmp(actual_value, value, actual_size), 0); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(dnvlist_get_binary__default_value); +ATF_TEST_CASE_BODY(dnvlist_get_binary__default_value) +{ + nvlist_t *nvl; + const char *key; + const void *default_value, *actual_value; + size_t default_size, actual_size; + + key = "123"; + nvl = nvlist_create(0); + + set_const_binary_value(default_value, default_size, "bar"); + actual_value = dnvlist_get_binary(nvl, key, &actual_size, default_value, + default_size); + ATF_REQUIRE_EQ(default_size, actual_size); + ATF_REQUIRE_EQ(memcmp(actual_value, default_value, actual_size), 0); + + set_const_binary_value(default_value, default_size, "atf"); + actual_value = dnvlist_get_binary(nvl, key, &actual_size, default_value, + default_size); + ATF_REQUIRE_EQ(default_size, actual_size); + ATF_REQUIRE_EQ(memcmp(actual_value, default_value, actual_size), 0); + + nvlist_add_binary(nvl, key, "test", 4); + + set_const_binary_value(default_value, default_size, "bthrg"); + actual_value = dnvlist_get_binary(nvl, "k", &actual_size, default_value, + default_size); + ATF_REQUIRE_EQ(default_size, actual_size); + ATF_REQUIRE_EQ(memcmp(actual_value, default_value, actual_size), 0); + + set_const_binary_value(default_value, default_size, + "rrhgrythtyrtgbrhgrtdsvdfbtjlkul"); + actual_value = dnvlist_get_binary(nvl, "s", &actual_size, default_value, + default_size); + ATF_REQUIRE_EQ(default_size, actual_size); + ATF_REQUIRE_EQ(memcmp(actual_value, default_value, actual_size), 0); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(dnvlist_take_bool__present); +ATF_TEST_CASE_BODY(dnvlist_take_bool__present) +{ + nvlist_t *nvl; + const char *key; + bool value; + + nvl = nvlist_create(0); + + key = "name"; + value = true; + nvlist_add_bool(nvl, key, value); + + ATF_REQUIRE_EQ(dnvlist_take_bool(nvl, key, false), value); + ATF_REQUIRE(nvlist_empty(nvl)); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(dnvlist_take_bool__empty); +ATF_TEST_CASE_BODY(dnvlist_take_bool__empty) +{ + nvlist_t *nvl; + + nvl = nvlist_create(0); + + ATF_REQUIRE_EQ(dnvlist_take_bool(nvl, "123", false), false); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(dnvlist_take_bool__default_value); +ATF_TEST_CASE_BODY(dnvlist_take_bool__default_value) +{ + nvlist_t *nvl; + + nvl = nvlist_create(0); + nvlist_add_bool(nvl, "key", true); + + ATF_REQUIRE_EQ(dnvlist_take_bool(nvl, "otherkey", true), true); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(dnvlist_take_number__present); +ATF_TEST_CASE_BODY(dnvlist_take_number__present) +{ + nvlist_t *nvl; + const char *key; + uint64_t value; + + nvl = nvlist_create(0); + + key = "name"; + value = 194154; + nvlist_add_number(nvl, key, value); + + ATF_REQUIRE_EQ(dnvlist_take_number(nvl, key, 2), value); + ATF_REQUIRE(nvlist_empty(nvl)); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(dnvlist_take_number__empty); +ATF_TEST_CASE_BODY(dnvlist_take_number__empty) +{ + nvlist_t *nvl; + + nvl = nvlist_create(0); + + ATF_REQUIRE_EQ(dnvlist_take_number(nvl, "123", 126484), 126484); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(dnvlist_take_number__default_value); +ATF_TEST_CASE_BODY(dnvlist_take_number__default_value) +{ + nvlist_t *nvl; + + nvl = nvlist_create(0); + nvlist_add_number(nvl, "key", 12); + + ATF_REQUIRE_EQ(dnvlist_take_number(nvl, "otherkey", 13), 13); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(dnvlist_take_string__present); +ATF_TEST_CASE_BODY(dnvlist_take_string__present) +{ + nvlist_t *nvl; + const char *key; + const char *value; + char *default_val, *actual_val; + + nvl = nvlist_create(0); + + key = "name"; + value = "wrowm"; + default_val = strdup("default"); + nvlist_add_string(nvl, key, value); + + actual_val = dnvlist_take_string(nvl, key, default_val); + ATF_REQUIRE_EQ(strcmp(actual_val, value), 0); + ATF_REQUIRE(nvlist_empty(nvl)); + + free(actual_val); + free(default_val); + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(dnvlist_take_string__empty); +ATF_TEST_CASE_BODY(dnvlist_take_string__empty) +{ + nvlist_t *nvl; + char *default_val, *actual_val; + + nvl = nvlist_create(0); + default_val = strdup(""); + + actual_val = dnvlist_take_string(nvl, "123", default_val); + ATF_REQUIRE_EQ(strcmp(actual_val, default_val), 0); + + free(actual_val); + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(dnvlist_take_string__default_value); +ATF_TEST_CASE_BODY(dnvlist_take_string__default_value) +{ + nvlist_t *nvl; + char *default_val, *actual_val; + + nvl = nvlist_create(0); + nvlist_add_string(nvl, "key", "foobar"); + default_val = strdup("other"); + + actual_val = dnvlist_take_string(nvl, "otherkey", default_val); + ATF_REQUIRE_EQ(strcmp(actual_val, default_val), 0); + + free(actual_val); + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(dnvlist_take_nvlist__present); +ATF_TEST_CASE_BODY(dnvlist_take_nvlist__present) +{ + nvlist_t *nvl; + const char *key; + nvlist_t *value, *default_val, *actual_val; + + nvl = nvlist_create(0); + + key = "name"; + value = nvlist_create(0); + default_val = nvlist_create(0); + nvlist_move_nvlist(nvl, key, value); + + actual_val = dnvlist_take_nvlist(nvl, key, default_val); + ATF_REQUIRE_EQ(actual_val, value); + ATF_REQUIRE(nvlist_empty(nvl)); + + free(actual_val); + free(default_val); + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(dnvlist_take_nvlist__empty); +ATF_TEST_CASE_BODY(dnvlist_take_nvlist__empty) +{ + nvlist_t *nvl, *actual_val; + + nvl = nvlist_create(0); + + actual_val = dnvlist_take_nvlist(nvl, "123", NULL); + ATF_REQUIRE_EQ(actual_val, static_cast(NULL)); + + free(actual_val); + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(dnvlist_take_nvlist__default_value); +ATF_TEST_CASE_BODY(dnvlist_take_nvlist__default_value) +{ + nvlist_t *nvl; + nvlist_t *default_val, *actual_val; + + nvl = nvlist_create(0); + nvlist_move_nvlist(nvl, "key", nvlist_create(0)); + default_val = nvlist_create(0); + + actual_val = dnvlist_take_nvlist(nvl, "otherkey", default_val); + ATF_REQUIRE_EQ(actual_val, default_val); + + free(actual_val); + nvlist_destroy(nvl); +} + +static void +set_binary_value(void *&value, size_t &size, const char *str) +{ + + value = strdup(str); + size = strlen(str) + 1; /* +1 to include '\0' */ +} + +ATF_TEST_CASE_WITHOUT_HEAD(dnvlist_take_binary__present); +ATF_TEST_CASE_BODY(dnvlist_take_binary__present) +{ + nvlist_t *nvl; + const char *key; + void *value, *default_val, *actual_val; + size_t value_size, default_size, actual_size; + + nvl = nvlist_create(0); + + key = "name"; + set_binary_value(value, value_size, "fkdojvmo908"); + set_binary_value(default_val, default_size, "16546"); + nvlist_add_binary(nvl, key, value, value_size); + + actual_val = dnvlist_take_binary(nvl, key, &actual_size, default_val, + default_size); + ATF_REQUIRE_EQ(value_size, actual_size); + ATF_REQUIRE_EQ(memcmp(actual_val, value, value_size), 0); + ATF_REQUIRE(nvlist_empty(nvl)); + + free(actual_val); + free(default_val); + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(dnvlist_take_binary__empty); +ATF_TEST_CASE_BODY(dnvlist_take_binary__empty) +{ + nvlist_t *nvl; + void *default_val, *actual_val; + size_t default_size, actual_size; + + nvl = nvlist_create(0); + set_binary_value(default_val, default_size, "\xa8\x89\x49\xff\xe2\x08"); + + actual_val = dnvlist_take_binary(nvl, "123", &actual_size, default_val, + default_size); + ATF_REQUIRE_EQ(default_size, actual_size); + ATF_REQUIRE_EQ(memcmp(actual_val, default_val, actual_size), 0); + + free(actual_val); + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(dnvlist_take_binary__default_value); +ATF_TEST_CASE_BODY(dnvlist_take_binary__default_value) +{ + nvlist_t *nvl; + void *default_val, *actual_val; + size_t default_size, actual_size; + + nvl = nvlist_create(0); + nvlist_add_binary(nvl, "key", "foobar", 6); + set_binary_value(default_val, default_size, "vbhag"); + + actual_val = dnvlist_take_binary(nvl, "otherkey", &actual_size, + default_val, default_size); + ATF_REQUIRE_EQ(default_size, actual_size); + ATF_REQUIRE_EQ(memcmp(actual_val, default_val, default_size), 0); + + free(actual_val); + nvlist_destroy(nvl); +} + +ATF_INIT_TEST_CASES(tp) +{ + ATF_ADD_TEST_CASE(tp, dnvlist_get_bool__present); + ATF_ADD_TEST_CASE(tp, dnvlist_get_bool__default_value); + ATF_ADD_TEST_CASE(tp, dnvlist_get_number__present); + ATF_ADD_TEST_CASE(tp, dnvlist_get_number__default_value); + ATF_ADD_TEST_CASE(tp, dnvlist_get_string__present); + ATF_ADD_TEST_CASE(tp, dnvlist_get_string__default_value); + ATF_ADD_TEST_CASE(tp, dnvlist_get_nvlist__present); + ATF_ADD_TEST_CASE(tp, dnvlist_get_nvlist__default_value); + ATF_ADD_TEST_CASE(tp, dnvlist_get_binary__present); + ATF_ADD_TEST_CASE(tp, dnvlist_get_binary__default_value); + + ATF_ADD_TEST_CASE(tp, dnvlist_take_bool__present); + ATF_ADD_TEST_CASE(tp, dnvlist_take_bool__empty); + ATF_ADD_TEST_CASE(tp, dnvlist_take_bool__default_value); + ATF_ADD_TEST_CASE(tp, dnvlist_take_number__present); + ATF_ADD_TEST_CASE(tp, dnvlist_take_number__empty); + ATF_ADD_TEST_CASE(tp, dnvlist_take_number__default_value); + ATF_ADD_TEST_CASE(tp, dnvlist_take_string__present); + ATF_ADD_TEST_CASE(tp, dnvlist_take_string__empty); + ATF_ADD_TEST_CASE(tp, dnvlist_take_string__default_value); + ATF_ADD_TEST_CASE(tp, dnvlist_take_nvlist__present); + ATF_ADD_TEST_CASE(tp, dnvlist_take_nvlist__empty); + ATF_ADD_TEST_CASE(tp, dnvlist_take_nvlist__default_value); + ATF_ADD_TEST_CASE(tp, dnvlist_take_binary__present); + ATF_ADD_TEST_CASE(tp, dnvlist_take_binary__empty); + ATF_ADD_TEST_CASE(tp, dnvlist_take_binary__default_value); +} Index: lib/libnv/tests/nv_array_tests.cc =================================================================== --- /dev/null +++ lib/libnv/tests/nv_array_tests.cc @@ -0,0 +1,1191 @@ +/*- + * Copyright (c) 2015 Mariusz Zaborski + * 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 +__FBSDID("$FreeBSD: head/lib/libnv/tests/nv_array_tests.cc 286796 2015-08-15 06:34:49Z oshogbo $"); + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#define fd_is_valid(fd) (fcntl((fd), F_GETFL) != -1 || errno != EBADF) + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_bool_array__basic); +ATF_TEST_CASE_BODY(nvlist_bool_array__basic) +{ + bool testbool[16]; + const bool *const_result; + bool *result; + nvlist_t *nvl; + size_t nitems; + unsigned int i; + const char *key; + + key = "nvl/bool"; + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + ATF_REQUIRE(nvlist_empty(nvl)); + ATF_REQUIRE(!nvlist_exists_string_array(nvl, key)); + + for (i = 0; i < 16; i++) + testbool[i] = (i % 2 == 0); + + nvlist_add_bool_array(nvl, key, testbool, 16); + ATF_REQUIRE_EQ(nvlist_error(nvl), 0); + ATF_REQUIRE(!nvlist_empty(nvl)); + ATF_REQUIRE(nvlist_exists_bool_array(nvl, key)); + ATF_REQUIRE(nvlist_exists_bool_array(nvl, "nvl/bool")); + + const_result = nvlist_get_bool_array(nvl, key, &nitems); + ATF_REQUIRE_EQ(nitems, 16); + ATF_REQUIRE(const_result != NULL); + for (i = 0; i < nitems; i++) + ATF_REQUIRE_EQ(const_result[i], testbool[i]); + + result = nvlist_take_bool_array(nvl, key, &nitems); + ATF_REQUIRE_EQ(nitems, 16); + ATF_REQUIRE(const_result != NULL); + for (i = 0; i < nitems; i++) + ATF_REQUIRE_EQ(result[i], testbool[i]); + + ATF_REQUIRE(!nvlist_exists_bool_array(nvl, key)); + ATF_REQUIRE(nvlist_empty(nvl)); + ATF_REQUIRE_EQ(nvlist_error(nvl), 0); + + free(result); + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_string_array__basic); +ATF_TEST_CASE_BODY(nvlist_string_array__basic) +{ + const char * const *const_result; + char **result; + nvlist_t *nvl; + size_t nitems; + unsigned int i; + const char *key; + const char *string[8] = { "a", "b", "kot", "foo", + "tests", "nice test", "", "abcdef" }; + + key = "nvl/string"; + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + ATF_REQUIRE(nvlist_empty(nvl)); + ATF_REQUIRE(!nvlist_exists_string_array(nvl, key)); + + nvlist_add_string_array(nvl, key, string, 8); + ATF_REQUIRE_EQ(nvlist_error(nvl), 0); + ATF_REQUIRE(!nvlist_empty(nvl)); + ATF_REQUIRE(nvlist_exists_string_array(nvl, key)); + ATF_REQUIRE(nvlist_exists_string_array(nvl, "nvl/string")); + + const_result = nvlist_get_string_array(nvl, key, &nitems); + ATF_REQUIRE(!nvlist_empty(nvl)); + ATF_REQUIRE(const_result != NULL); + ATF_REQUIRE(nitems == 8); + for (i = 0; i < nitems; i++) { + if (string[i] != NULL) { + ATF_REQUIRE(strcmp(const_result[i], string[i]) == 0); + } else { + ATF_REQUIRE(const_result[i] == string[i]); + } + } + + result = nvlist_take_string_array(nvl, key, &nitems); + ATF_REQUIRE(result != NULL); + ATF_REQUIRE_EQ(nitems, 8); + for (i = 0; i < nitems; i++) { + if (string[i] != NULL) { + ATF_REQUIRE_EQ(strcmp(result[i], string[i]), 0); + } else { + ATF_REQUIRE_EQ(result[i], string[i]); + } + } + + ATF_REQUIRE(!nvlist_exists_string_array(nvl, key)); + ATF_REQUIRE(nvlist_empty(nvl)); + ATF_REQUIRE_EQ(nvlist_error(nvl), 0); + + for (i = 0; i < 8; i++) + free(result[i]); + free(result); + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_descriptor_array__basic); +ATF_TEST_CASE_BODY(nvlist_descriptor_array__basic) +{ + int fd[32], *result; + const int *const_result; + nvlist_t *nvl; + size_t nitems; + unsigned int i; + const char *key; + + for (i = 0; i < 32; i++) { + fd[i] = dup(STDERR_FILENO); + ATF_REQUIRE(fd_is_valid(fd[i])); + } + + key = "nvl/descriptor"; + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + ATF_REQUIRE(nvlist_empty(nvl)); + ATF_REQUIRE(!nvlist_exists_descriptor_array(nvl, key)); + + nvlist_add_descriptor_array(nvl, key, fd, 32); + ATF_REQUIRE_EQ(nvlist_error(nvl), 0); + ATF_REQUIRE(!nvlist_empty(nvl)); + ATF_REQUIRE(nvlist_exists_descriptor_array(nvl, key)); + ATF_REQUIRE(nvlist_exists_descriptor_array(nvl, "nvl/descriptor")); + + const_result = nvlist_get_descriptor_array(nvl, key, &nitems); + ATF_REQUIRE(!nvlist_empty(nvl)); + ATF_REQUIRE(const_result != NULL); + ATF_REQUIRE(nitems == 32); + for (i = 0; i < nitems; i++) { + ATF_REQUIRE(fd_is_valid(const_result[i])); + if (i > 0) + ATF_REQUIRE(const_result[i] != const_result[i - 1]); + } + + result = nvlist_take_descriptor_array(nvl, key, &nitems); + ATF_REQUIRE(result != NULL); + ATF_REQUIRE_EQ(nitems, 32); + for (i = 0; i < nitems; i++) { + ATF_REQUIRE(fd_is_valid(result[i])); + if (i > 0) + ATF_REQUIRE(const_result[i] != const_result[i - 1]); + } + + ATF_REQUIRE(!nvlist_exists_string_array(nvl, key)); + ATF_REQUIRE(nvlist_empty(nvl)); + ATF_REQUIRE_EQ(nvlist_error(nvl), 0); + + for (i = 0; i < nitems; i++) { + close(result[i]); + close(fd[i]); + } + free(result); + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_number_array__basic); +ATF_TEST_CASE_BODY(nvlist_number_array__basic) +{ + const uint64_t *const_result; + uint64_t *result; + nvlist_t *nvl; + size_t nitems; + unsigned int i; + const char *key; + const uint64_t number[8] = { 0, UINT_MAX, 7, 123, 90, + 100000, 8, 1 }; + + key = "nvl/number"; + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + ATF_REQUIRE(nvlist_empty(nvl)); + ATF_REQUIRE(!nvlist_exists_string_array(nvl, key)); + + nvlist_add_number_array(nvl, key, number, 8); + ATF_REQUIRE_EQ(nvlist_error(nvl), 0); + ATF_REQUIRE(!nvlist_empty(nvl)); + ATF_REQUIRE(nvlist_exists_number_array(nvl, key)); + ATF_REQUIRE(nvlist_exists_number_array(nvl, "nvl/number")); + + const_result = nvlist_get_number_array(nvl, key, &nitems); + ATF_REQUIRE(!nvlist_empty(nvl)); + ATF_REQUIRE(const_result != NULL); + ATF_REQUIRE(nitems == 8); + for (i = 0; i < nitems; i++) + ATF_REQUIRE_EQ(const_result[i], number[i]); + + result = nvlist_take_number_array(nvl, key, &nitems); + ATF_REQUIRE(result != NULL); + ATF_REQUIRE_EQ(nitems, 8); + for (i = 0; i < nitems; i++) + ATF_REQUIRE_EQ(result[i], number[i]); + + ATF_REQUIRE(!nvlist_exists_string_array(nvl, key)); + ATF_REQUIRE(nvlist_empty(nvl)); + ATF_REQUIRE_EQ(nvlist_error(nvl), 0); + + free(result); + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_nvlist_array__basic); +ATF_TEST_CASE_BODY(nvlist_nvlist_array__basic) +{ + nvlist_t *testnvl[8]; + const nvlist_t * const *const_result; + nvlist_t **result; + nvlist_t *nvl; + size_t nitems; + unsigned int i; + const char *somestr[8] = { "a", "b", "c", "d", "e", "f", "g", "h" }; + const char *key; + + for (i = 0; i < 8; i++) { + testnvl[i] = nvlist_create(0); + ATF_REQUIRE(testnvl[i] != NULL); + ATF_REQUIRE_EQ(nvlist_error(testnvl[i]), 0); + nvlist_add_string(testnvl[i], "nvl/string", somestr[i]); + ATF_REQUIRE_EQ(nvlist_error(testnvl[i]), 0); + ATF_REQUIRE(nvlist_exists_string(testnvl[i], "nvl/string")); + } + + key = "nvl/nvlist"; + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + ATF_REQUIRE(nvlist_empty(nvl)); + ATF_REQUIRE(!nvlist_exists_string_array(nvl, key)); + + nvlist_add_nvlist_array(nvl, key, (const nvlist_t * const *)testnvl, 8); + ATF_REQUIRE_EQ(nvlist_error(nvl), 0); + ATF_REQUIRE(!nvlist_empty(nvl)); + ATF_REQUIRE(nvlist_exists_nvlist_array(nvl, key)); + ATF_REQUIRE(nvlist_exists_nvlist_array(nvl, "nvl/nvlist")); + + const_result = nvlist_get_nvlist_array(nvl, key, &nitems); + ATF_REQUIRE(!nvlist_empty(nvl)); + ATF_REQUIRE(const_result != NULL); + ATF_REQUIRE(nitems == 8); + + for (i = 0; i < nitems; i++) { + ATF_REQUIRE_EQ(nvlist_error(const_result[i]), 0); + if (i < nitems - 1) { + ATF_REQUIRE(nvlist_get_array_next(const_result[i]) == + const_result[i + 1]); + } else { + ATF_REQUIRE(nvlist_get_array_next(const_result[i]) == + NULL); + } + ATF_REQUIRE(nvlist_get_parent(const_result[i], NULL) == nvl); + ATF_REQUIRE(nvlist_in_array(const_result[i])); + ATF_REQUIRE(nvlist_exists_string(const_result[i], + "nvl/string")); + ATF_REQUIRE(strcmp(nvlist_get_string(const_result[i], + "nvl/string"), somestr[i]) == 0); + } + + result = nvlist_take_nvlist_array(nvl, key, &nitems); + ATF_REQUIRE(result != NULL); + ATF_REQUIRE_EQ(nitems, 8); + for (i = 0; i < nitems; i++) { + ATF_REQUIRE_EQ(nvlist_error(result[i]), 0); + ATF_REQUIRE(nvlist_get_array_next(result[i]) == NULL); + ATF_REQUIRE(nvlist_get_array_next(const_result[i]) == NULL); + ATF_REQUIRE(!nvlist_in_array(const_result[i])); + } + + ATF_REQUIRE(!nvlist_exists_string_array(nvl, key)); + ATF_REQUIRE(nvlist_empty(nvl)); + ATF_REQUIRE_EQ(nvlist_error(nvl), 0); + + for (i = 0; i < 8; i++) { + nvlist_destroy(result[i]); + nvlist_destroy(testnvl[i]); + } + + free(result); + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_clone_array); +ATF_TEST_CASE_BODY(nvlist_clone_array) +{ + nvlist_t *testnvl[8]; + nvlist_t *src, *dst; + const nvlist_t *nvl; + bool testbool[16]; + int testfd[16]; + size_t i, nitems; + const char *string[8] = { "a", "b", "kot", "foo", + "tests", "nice test", "", "abcdef" }; + const char *somestr[8] = { "a", "b", "c", "d", "e", "f", "g", "h" }; + const uint64_t number[8] = { 0, UINT_MAX, 7, 123, 90, + 100000, 8, 1 }; + + for (i = 0; i < 16; i++) { + testbool[i] = (i % 2 == 0); + testfd[i] = dup(STDERR_FILENO); + ATF_REQUIRE(fd_is_valid(testfd[i])); + } + for (i = 0; i < 8; i++) { + testnvl[i] = nvlist_create(0); + ATF_REQUIRE(nvlist_error(testnvl[i]) == 0); + nvlist_add_string(testnvl[i], "nvl/nvl/teststr", somestr[i]); + ATF_REQUIRE(nvlist_error(testnvl[i]) == 0); + } + + src = nvlist_create(0); + ATF_REQUIRE(nvlist_error(src) == 0); + + ATF_REQUIRE(!nvlist_exists_bool_array(src, "nvl/bool")); + nvlist_add_bool_array(src, "nvl/bool", testbool, 16); + ATF_REQUIRE_EQ(nvlist_error(src), 0); + ATF_REQUIRE(nvlist_exists_bool_array(src, "nvl/bool")); + + ATF_REQUIRE(!nvlist_exists_string_array(src, "nvl/string")); + nvlist_add_string_array(src, "nvl/string", string, 8); + ATF_REQUIRE_EQ(nvlist_error(src), 0); + ATF_REQUIRE(nvlist_exists_string_array(src, "nvl/string")); + + ATF_REQUIRE(!nvlist_exists_descriptor_array(src, "nvl/fd")); + nvlist_add_descriptor_array(src, "nvl/fd", testfd, 16); + ATF_REQUIRE_EQ(nvlist_error(src), 0); + ATF_REQUIRE(nvlist_exists_descriptor_array(src, "nvl/fd")); + + ATF_REQUIRE(!nvlist_exists_number_array(src, "nvl/number")); + nvlist_add_number_array(src, "nvl/number", number, 8); + ATF_REQUIRE_EQ(nvlist_error(src), 0); + ATF_REQUIRE(nvlist_exists_number_array(src, "nvl/number")); + + ATF_REQUIRE(!nvlist_exists_nvlist_array(src, "nvl/array")); + nvlist_add_nvlist_array(src, "nvl/array", + (const nvlist_t * const *)testnvl, 8); + ATF_REQUIRE_EQ(nvlist_error(src), 0); + ATF_REQUIRE(nvlist_exists_nvlist_array(src, "nvl/array")); + + dst = nvlist_clone(src); + ATF_REQUIRE(dst != NULL); + + ATF_REQUIRE(nvlist_exists_bool_array(dst, "nvl/bool")); + (void) nvlist_get_bool_array(dst, "nvl/bool", &nitems); + ATF_REQUIRE_EQ(nitems, 16); + for (i = 0; i < nitems; i++) { + ATF_REQUIRE( + nvlist_get_bool_array(dst, "nvl/bool", &nitems)[i] == + nvlist_get_bool_array(src, "nvl/bool", &nitems)[i]); + } + + ATF_REQUIRE(nvlist_exists_string_array(dst, "nvl/string")); + (void) nvlist_get_string_array(dst, "nvl/string", &nitems); + ATF_REQUIRE_EQ(nitems, 8); + for (i = 0; i < nitems; i++) { + if (nvlist_get_string_array(dst, "nvl/string", + &nitems)[i] == NULL) { + ATF_REQUIRE(nvlist_get_string_array(dst, "nvl/string", + &nitems)[i] == nvlist_get_string_array(src, + "nvl/string", &nitems)[i]); + } else { + ATF_REQUIRE(strcmp(nvlist_get_string_array(dst, + "nvl/string", &nitems)[i], nvlist_get_string_array( + src, "nvl/string", &nitems)[i]) == 0); + } + } + + ATF_REQUIRE(nvlist_exists_descriptor_array(dst, "nvl/fd")); + (void) nvlist_get_descriptor_array(dst, "nvl/fd", &nitems); + ATF_REQUIRE_EQ(nitems, 16); + for (i = 0; i < nitems; i++) { + ATF_REQUIRE(fd_is_valid( + nvlist_get_descriptor_array(dst, "nvl/fd", &nitems)[i])); + } + ATF_REQUIRE(nvlist_exists_number_array(dst, "nvl/number")); + (void) nvlist_get_number_array(dst, "nvl/number", &nitems); + ATF_REQUIRE_EQ(nitems, 8); + + for (i = 0; i < nitems; i++) { + ATF_REQUIRE( + nvlist_get_number_array(dst, "nvl/number", &nitems)[i] == + nvlist_get_number_array(src, "nvl/number", &nitems)[i]); + } + + ATF_REQUIRE(nvlist_exists_nvlist_array(dst, "nvl/array")); + (void) nvlist_get_nvlist_array(dst, "nvl/array", &nitems); + ATF_REQUIRE_EQ(nitems, 8); + for (i = 0; i < nitems; i++) { + nvl = nvlist_get_nvlist_array(dst, "nvl/array", &nitems)[i]; + ATF_REQUIRE(nvlist_exists_string(nvl, "nvl/nvl/teststr")); + ATF_REQUIRE(strcmp(nvlist_get_string(nvl, "nvl/nvl/teststr"), + somestr[i]) == 0); + } + + for (i = 0; i < 16; i++) { + close(testfd[i]); + if (i < 8) { + nvlist_destroy(testnvl[i]); + } + } + nvlist_destroy(src); + nvlist_destroy(dst); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_bool_array__move); +ATF_TEST_CASE_BODY(nvlist_bool_array__move) +{ + bool *testbool; + const bool *const_result; + nvlist_t *nvl; + size_t nitems, count; + unsigned int i; + const char *key; + + key = "nvl/bool"; + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + ATF_REQUIRE(nvlist_empty(nvl)); + ATF_REQUIRE(!nvlist_exists_string_array(nvl, key)); + + count = 16; + testbool = (bool*)malloc(sizeof(*testbool) * count); + ATF_REQUIRE(testbool != NULL); + for (i = 0; i < count; i++) + testbool[i] = (i % 2 == 0); + + nvlist_move_bool_array(nvl, key, testbool, count); + ATF_REQUIRE_EQ(nvlist_error(nvl), 0); + ATF_REQUIRE(!nvlist_empty(nvl)); + ATF_REQUIRE(nvlist_exists_bool_array(nvl, key)); + + const_result = nvlist_get_bool_array(nvl, key, &nitems); + ATF_REQUIRE_EQ(nitems, count); + ATF_REQUIRE(const_result != NULL); + ATF_REQUIRE(const_result == testbool); + for (i = 0; i < nitems; i++) + ATF_REQUIRE_EQ(const_result[i], (i % 2 == 0)); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_string_array__move); +ATF_TEST_CASE_BODY(nvlist_string_array__move) +{ + char **teststr; + const char * const *const_result; + nvlist_t *nvl; + size_t nitems, count; + unsigned int i; + const char *key; + + key = "nvl/string"; + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + ATF_REQUIRE(nvlist_empty(nvl)); + ATF_REQUIRE(!nvlist_exists_string_array(nvl, key)); + + count = 26; + teststr = (char**)malloc(sizeof(*teststr) * count); + ATF_REQUIRE(teststr != NULL); + for (i = 0; i < count; i++) { + teststr[i] = (char*)malloc(sizeof(**teststr) * 2); + ATF_REQUIRE(teststr[i] != NULL); + teststr[i][0] = 'a' + i; + teststr[i][1] = '\0'; + } + + nvlist_move_string_array(nvl, key, teststr, count); + ATF_REQUIRE_EQ(nvlist_error(nvl), 0); + ATF_REQUIRE(!nvlist_empty(nvl)); + ATF_REQUIRE(nvlist_exists_string_array(nvl, key)); + + const_result = nvlist_get_string_array(nvl, key, &nitems); + ATF_REQUIRE_EQ(nitems, count); + ATF_REQUIRE(const_result != NULL); + ATF_REQUIRE((intptr_t)const_result == (intptr_t)teststr); + for (i = 0; i < nitems; i++) { + ATF_REQUIRE_EQ(const_result[i][0], (char)('a' + i)); + ATF_REQUIRE_EQ(const_result[i][1], '\0'); + } + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_nvlist_array__move); +ATF_TEST_CASE_BODY(nvlist_nvlist_array__move) +{ + nvlist **testnv; + const nvlist * const *const_result; + nvlist_t *nvl; + size_t nitems, count; + unsigned int i; + const char *key; + + key = "nvl/nvlist"; + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + ATF_REQUIRE(nvlist_empty(nvl)); + ATF_REQUIRE(!nvlist_exists_nvlist_array(nvl, key)); + + count = 26; + testnv = (nvlist**)malloc(sizeof(*testnv) * count); + ATF_REQUIRE(testnv != NULL); + for (i = 0; i < count; i++) { + testnv[i] = nvlist_create(0); + ATF_REQUIRE(testnv[i] != NULL); + } + + nvlist_move_nvlist_array(nvl, key, testnv, count); + ATF_REQUIRE_EQ(nvlist_error(nvl), 0); + ATF_REQUIRE(!nvlist_empty(nvl)); + ATF_REQUIRE(nvlist_exists_nvlist_array(nvl, key)); + + const_result = nvlist_get_nvlist_array(nvl, key, &nitems); + ATF_REQUIRE_EQ(nitems, count); + ATF_REQUIRE(const_result != NULL); + ATF_REQUIRE((intptr_t)const_result == (intptr_t)testnv); + for (i = 0; i < nitems; i++) { + ATF_REQUIRE_EQ(nvlist_error(const_result[i]), 0); + ATF_REQUIRE(nvlist_empty(const_result[i])); + if (i < nitems - 1) { + ATF_REQUIRE(nvlist_get_array_next(const_result[i]) == + const_result[i + 1]); + } else { + ATF_REQUIRE(nvlist_get_array_next(const_result[i]) == + NULL); + } + ATF_REQUIRE(nvlist_get_parent(const_result[i], NULL) == nvl); + ATF_REQUIRE(nvlist_in_array(const_result[i])); + } + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_number_array__move); +ATF_TEST_CASE_BODY(nvlist_number_array__move) +{ + uint64_t *testnumber; + const uint64_t *const_result; + nvlist_t *nvl; + size_t nitems, count; + unsigned int i; + const char *key; + + key = "nvl/number"; + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + ATF_REQUIRE(nvlist_empty(nvl)); + ATF_REQUIRE(!nvlist_exists_string_array(nvl, key)); + + count = 1000; + testnumber = (uint64_t*)malloc(sizeof(*testnumber) * count); + ATF_REQUIRE(testnumber != NULL); + for (i = 0; i < count; i++) + testnumber[i] = i; + + nvlist_move_number_array(nvl, key, testnumber, count); + ATF_REQUIRE_EQ(nvlist_error(nvl), 0); + ATF_REQUIRE(!nvlist_empty(nvl)); + ATF_REQUIRE(nvlist_exists_number_array(nvl, key)); + + const_result = nvlist_get_number_array(nvl, key, &nitems); + ATF_REQUIRE_EQ(nitems, count); + ATF_REQUIRE(const_result != NULL); + ATF_REQUIRE(const_result == testnumber); + for (i = 0; i < nitems; i++) + ATF_REQUIRE_EQ(const_result[i], i); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_descriptor_array__move); +ATF_TEST_CASE_BODY(nvlist_descriptor_array__move) +{ + int *testfd; + const int *const_result; + nvlist_t *nvl; + size_t nitems, count; + unsigned int i; + const char *key; + + key = "nvl/fd"; + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + ATF_REQUIRE(nvlist_empty(nvl)); + ATF_REQUIRE(!nvlist_exists_string_array(nvl, key)); + + count = 50; + testfd = (int*)malloc(sizeof(*testfd) * count); + ATF_REQUIRE(testfd != NULL); + for (i = 0; i < count; i++) { + testfd[i] = dup(STDERR_FILENO); + ATF_REQUIRE(fd_is_valid(testfd[i])); + } + + nvlist_move_descriptor_array(nvl, key, testfd, count); + ATF_REQUIRE_EQ(nvlist_error(nvl), 0); + ATF_REQUIRE(!nvlist_empty(nvl)); + ATF_REQUIRE(nvlist_exists_descriptor_array(nvl, key)); + + const_result = nvlist_get_descriptor_array(nvl, key, &nitems); + ATF_REQUIRE_EQ(nitems, count); + ATF_REQUIRE(const_result != NULL); + ATF_REQUIRE(const_result == testfd); + for (i = 0; i < nitems; i++) + ATF_REQUIRE(fd_is_valid(const_result[i])); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_arrays__error_null); +ATF_TEST_CASE_BODY(nvlist_arrays__error_null) +{ + nvlist_t *nvl; + + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + nvlist_add_number_array(nvl, "nvl/number", NULL, 0); + ATF_REQUIRE(nvlist_error(nvl) != 0); + nvlist_destroy(nvl); + + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + nvlist_move_number_array(nvl, "nvl/number", NULL, 0); + ATF_REQUIRE(nvlist_error(nvl) != 0); + nvlist_destroy(nvl); + + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + nvlist_add_descriptor_array(nvl, "nvl/fd", NULL, 0); + ATF_REQUIRE(nvlist_error(nvl) != 0); + nvlist_destroy(nvl); + + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + nvlist_move_descriptor_array(nvl, "nvl/fd", NULL, 0); + ATF_REQUIRE(nvlist_error(nvl) != 0); + nvlist_destroy(nvl); + + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + nvlist_add_string_array(nvl, "nvl/string", NULL, 0); + ATF_REQUIRE(nvlist_error(nvl) != 0); + nvlist_destroy(nvl); + + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + nvlist_move_string_array(nvl, "nvl/string", NULL, 0); + ATF_REQUIRE(nvlist_error(nvl) != 0); + nvlist_destroy(nvl); + + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + nvlist_add_nvlist_array(nvl, "nvl/nvlist", NULL, 0); + ATF_REQUIRE(nvlist_error(nvl) != 0); + nvlist_destroy(nvl); + + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + nvlist_move_nvlist_array(nvl, "nvl/nvlist", NULL, 0); + ATF_REQUIRE(nvlist_error(nvl) != 0); + nvlist_destroy(nvl); + + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + nvlist_add_bool_array(nvl, "nvl/bool", NULL, 0); + ATF_REQUIRE(nvlist_error(nvl) != 0); + nvlist_destroy(nvl); + + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + nvlist_move_bool_array(nvl, "nvl/bool", NULL, 0); + ATF_REQUIRE(nvlist_error(nvl) != 0); + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_arrays__bad_value); +ATF_TEST_CASE_BODY(nvlist_arrays__bad_value) +{ + nvlist_t *nvl, *nvladd[1], **nvlmove; + int fdadd[1], *fdmove; + + nvladd[0] = NULL; + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + nvlist_add_nvlist_array(nvl, "nvl/nvlist", nvladd, 1); + ATF_REQUIRE(nvlist_error(nvl) != 0); + nvlist_destroy(nvl); + + nvlmove = (nvlist_t**)malloc(sizeof(*nvlmove)); + ATF_REQUIRE(nvlmove != NULL); + nvlmove[0] = NULL; + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + nvlist_move_nvlist_array(nvl, "nvl/nvlist", nvlmove, 1); + ATF_REQUIRE(nvlist_error(nvl) != 0); + nvlist_destroy(nvl); + + fdadd[0] = -2; + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + nvlist_add_descriptor_array(nvl, "nvl/fd", fdadd, 1); + ATF_REQUIRE(nvlist_error(nvl) != 0); + nvlist_destroy(nvl); + + fdmove = (int*)malloc(sizeof(*fdmove)); + ATF_REQUIRE(fdmove != NULL); + fdmove[0] = -2; + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + nvlist_move_descriptor_array(nvl, "nvl/fd", fdmove, 1); + ATF_REQUIRE(nvlist_error(nvl) != 0); + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_nvlist_array__travel); +ATF_TEST_CASE_BODY(nvlist_nvlist_array__travel) +{ + nvlist_t *nvl, *test[5], *nasted; + const nvlist_t *travel; + void *cookie; + int index, i, type; + const char *name; + + for (i = 0; i < 5; i++) { + test[i] = nvlist_create(0); + ATF_REQUIRE(test[i] != NULL); + nvlist_add_number(test[i], "nvl/number", i); + ATF_REQUIRE(nvlist_error(test[i]) == 0); + } + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + nvlist_add_nvlist_array(nvl, "nvl/nvlist_array", test, 5); + ATF_REQUIRE(nvlist_error(nvl) == 0); + nasted = nvlist_create(0); + ATF_REQUIRE(nasted != NULL); + nvlist_add_nvlist_array(nasted, "nvl/nvl/nvlist_array", test, 5); + ATF_REQUIRE(nvlist_error(nasted) == 0); + nvlist_move_nvlist(nvl, "nvl/nvl", nasted); + ATF_REQUIRE(nvlist_error(nvl) == 0); + nvlist_add_string(nvl, "nvl/string", "END"); + ATF_REQUIRE(nvlist_error(nvl) == 0); + + cookie = NULL; + index = 0; + travel = nvl; + do { + while ((name = nvlist_next(travel, &type, &cookie)) != NULL) { + if (index == 0) { + ATF_REQUIRE(type == NV_TYPE_NVLIST_ARRAY); + } else if (index >= 1 && index <= 5) { + ATF_REQUIRE(type == NV_TYPE_NUMBER); + } else if (index == 6) { + ATF_REQUIRE(type == NV_TYPE_NVLIST); + } else if (index == 7) { + ATF_REQUIRE(type == NV_TYPE_NVLIST_ARRAY); + } else if (index >= 8 && index <= 12) { + ATF_REQUIRE(type == NV_TYPE_NUMBER); + } else if (index == 13) { + ATF_REQUIRE(type == NV_TYPE_STRING); + } + + if (type == NV_TYPE_NVLIST) { + travel = nvlist_get_nvlist(travel, name); + cookie = NULL; + } else if (type == NV_TYPE_NVLIST_ARRAY) { + travel = nvlist_get_nvlist_array(travel, name, + NULL)[0]; + cookie = NULL; + } + index ++; + } + } while ((travel = nvlist_get_pararr(travel, &cookie)) != NULL); + + for (i = 0; i < 5; i++) + nvlist_destroy(test[i]); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_nvlist_array__travel_alternative); +ATF_TEST_CASE_BODY(nvlist_nvlist_array__travel_alternative) +{ + nvlist_t *nvl, *test[5], *nasted; + const nvlist_t *travel, *tmp; + void *cookie; + int index, i, type; + const char *name; + + for (i = 0; i < 5; i++) { + test[i] = nvlist_create(0); + ATF_REQUIRE(test[i] != NULL); + nvlist_add_number(test[i], "nvl/number", i); + ATF_REQUIRE(nvlist_error(test[i]) == 0); + } + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + nvlist_add_nvlist_array(nvl, "nvl/nvlist_array", test, 5); + ATF_REQUIRE(nvlist_error(nvl) == 0); + nasted = nvlist_create(0); + ATF_REQUIRE(nasted != NULL); + nvlist_add_nvlist_array(nasted, "nvl/nvl/nvlist_array", test, 5); + ATF_REQUIRE(nvlist_error(nasted) == 0); + nvlist_move_nvlist(nvl, "nvl/nvl", nasted); + ATF_REQUIRE(nvlist_error(nvl) == 0); + nvlist_add_string(nvl, "nvl/string", "END"); + ATF_REQUIRE(nvlist_error(nvl) == 0); + + cookie = NULL; + index = 0; + tmp = travel = nvl; + do { + do { + travel = tmp; + while ((name = nvlist_next(travel, &type, &cookie)) != + NULL) { + if (index == 0) { + ATF_REQUIRE(type == + NV_TYPE_NVLIST_ARRAY); + } else if (index >= 1 && index <= 5) { + ATF_REQUIRE(type == NV_TYPE_NUMBER); + } else if (index == 6) { + ATF_REQUIRE(type == NV_TYPE_NVLIST); + } else if (index == 7) { + ATF_REQUIRE(type == + NV_TYPE_NVLIST_ARRAY); + } else if (index >= 8 && index <= 12) { + ATF_REQUIRE(type == NV_TYPE_NUMBER); + } else if (index == 13) { + ATF_REQUIRE(type == NV_TYPE_STRING); + } + + if (type == NV_TYPE_NVLIST) { + travel = nvlist_get_nvlist(travel, + name); + cookie = NULL; + } else if (type == NV_TYPE_NVLIST_ARRAY) { + travel = nvlist_get_nvlist_array(travel, + name, NULL)[0]; + cookie = NULL; + } + index ++; + } + cookie = NULL; + } while ((tmp = nvlist_get_array_next(travel)) != NULL); + } while ((tmp = nvlist_get_parent(travel, &cookie)) != NULL); + + for (i = 0; i < 5; i++) + nvlist_destroy(test[i]); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_bool_array__pack); +ATF_TEST_CASE_BODY(nvlist_bool_array__pack) +{ + nvlist_t *nvl, *unpacked; + const char *key; + size_t packed_size, count; + void *packed; + unsigned int i; + const bool *const_result; + bool testbool[16]; + + for (i = 0; i < 16; i++) + testbool[i] = (i % 2 == 0); + + key = "nvl/bool"; + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + ATF_REQUIRE(nvlist_empty(nvl)); + ATF_REQUIRE(!nvlist_exists_string_array(nvl, key)); + + nvlist_add_bool_array(nvl, key, testbool, 16); + ATF_REQUIRE_EQ(nvlist_error(nvl), 0); + ATF_REQUIRE(!nvlist_empty(nvl)); + ATF_REQUIRE(nvlist_exists_bool_array(nvl, key)); + + packed = nvlist_pack(nvl, &packed_size); + ATF_REQUIRE(packed != NULL); + + unpacked = nvlist_unpack(packed, packed_size, 0); + ATF_REQUIRE(unpacked != NULL); + ATF_REQUIRE_EQ(nvlist_error(unpacked), 0); + ATF_REQUIRE(nvlist_exists_bool_array(unpacked, key)); + + const_result = nvlist_get_bool_array(unpacked, key, &count); + ATF_REQUIRE_EQ(count, 16); + for (i = 0; i < count; i++) { + ATF_REQUIRE_EQ(testbool[i], const_result[i]); + } + + nvlist_destroy(nvl); + nvlist_destroy(unpacked); + free(packed); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_number_array__pack); +ATF_TEST_CASE_BODY(nvlist_number_array__pack) +{ + nvlist_t *nvl, *unpacked; + const char *key; + size_t packed_size, count; + void *packed; + unsigned int i; + const uint64_t *const_result; + const uint64_t number[8] = { 0, UINT_MAX, 7, 123, 90, + 100000, 8, 1 }; + + key = "nvl/number"; + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + ATF_REQUIRE(nvlist_empty(nvl)); + ATF_REQUIRE(!nvlist_exists_string_array(nvl, key)); + + nvlist_add_number_array(nvl, key, number, 8); + ATF_REQUIRE_EQ(nvlist_error(nvl), 0); + ATF_REQUIRE(!nvlist_empty(nvl)); + ATF_REQUIRE(nvlist_exists_number_array(nvl, key)); + + packed = nvlist_pack(nvl, &packed_size); + ATF_REQUIRE(packed != NULL); + + unpacked = nvlist_unpack(packed, packed_size, 0); + ATF_REQUIRE(unpacked != NULL); + ATF_REQUIRE_EQ(nvlist_error(unpacked), 0); + ATF_REQUIRE(nvlist_exists_number_array(unpacked, key)); + + const_result = nvlist_get_number_array(unpacked, key, &count); + ATF_REQUIRE_EQ(count, 8); + for (i = 0; i < count; i++) { + ATF_REQUIRE_EQ(number[i], const_result[i]); + } + + nvlist_destroy(nvl); + nvlist_destroy(unpacked); + free(packed); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_descriptor_array__pack); +ATF_TEST_CASE_BODY(nvlist_descriptor_array__pack) +{ + nvlist_t *nvl; + const char *key; + size_t nitems; + unsigned int i; + const int *const_result; + int desc[32], fd, socks[2]; + pid_t pid; + + key = "nvl/descriptor"; + + ATF_REQUIRE_EQ(socketpair(PF_UNIX, SOCK_STREAM, 0, socks), 0); + + pid = atf::utils::fork(); + ATF_REQUIRE(pid >= 0); + if (pid == 0) { + /* Child. */ + fd = socks[0]; + close(socks[1]); + for (i = 0; i < 32; i++) { + desc[i] = dup(STDERR_FILENO); + ATF_REQUIRE(fd_is_valid(desc[i])); + } + + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + ATF_REQUIRE(nvlist_empty(nvl)); + ATF_REQUIRE(!nvlist_exists_descriptor_array(nvl, key)); + + nvlist_add_descriptor_array(nvl, key, desc, 32); + ATF_REQUIRE_EQ(nvlist_error(nvl), 0); + ATF_REQUIRE(!nvlist_empty(nvl)); + ATF_REQUIRE(nvlist_exists_descriptor_array(nvl, key)); + + ATF_REQUIRE(nvlist_send(fd, nvl) >= 0); + + for (i = 0; i < nitems; i++) + close(desc[i]); + } else { + /* Parent */ + fd = socks[1]; + close(socks[0]); + + errno = 0; + nvl = nvlist_recv(fd, 0); + ATF_REQUIRE(nvl != NULL); + ATF_REQUIRE_EQ(nvlist_error(nvl), 0); + ATF_REQUIRE(nvlist_exists_descriptor_array(nvl, key)); + + const_result = nvlist_get_descriptor_array(nvl, key, &nitems); + ATF_REQUIRE(const_result != NULL); + ATF_REQUIRE_EQ(nitems, 32); + for (i = 0; i < nitems; i++) + ATF_REQUIRE(fd_is_valid(const_result[i])); + + atf::utils::wait(pid, 0, "", ""); + } + + nvlist_destroy(nvl); + close(fd); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_string_array__pack); +ATF_TEST_CASE_BODY(nvlist_string_array__pack) +{ + nvlist_t *nvl, *unpacked; + const char *key; + size_t packed_size, count; + void *packed; + unsigned int i; + const char * const *const_result; + const char *string[8] = { "a", "b", "kot", "foo", + "tests", "nice test", "", "abcdef" }; + + key = "nvl/string"; + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + ATF_REQUIRE(nvlist_empty(nvl)); + ATF_REQUIRE(!nvlist_exists_string_array(nvl, key)); + + nvlist_add_string_array(nvl, key, string, 8); + ATF_REQUIRE_EQ(nvlist_error(nvl), 0); + ATF_REQUIRE(!nvlist_empty(nvl)); + ATF_REQUIRE(nvlist_exists_string_array(nvl, key)); + + packed = nvlist_pack(nvl, &packed_size); + ATF_REQUIRE(packed != NULL); + + unpacked = nvlist_unpack(packed, packed_size, 0); + ATF_REQUIRE(unpacked != NULL); + ATF_REQUIRE_EQ(nvlist_error(unpacked), 0); + ATF_REQUIRE(nvlist_exists_string_array(unpacked, key)); + + const_result = nvlist_get_string_array(unpacked, key, &count); + ATF_REQUIRE_EQ(count, 8); + for (i = 0; i < count; i++) { + ATF_REQUIRE_EQ(strcmp(string[i], const_result[i]), 0); + } + + nvlist_destroy(nvl); + nvlist_destroy(unpacked); + free(packed); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_nvlist_array__pack); +ATF_TEST_CASE_BODY(nvlist_nvlist_array__pack) +{ + nvlist_t *testnvl[8], *unpacked; + const nvlist_t * const *const_result; + nvlist_t **result; + nvlist_t *nvl; + size_t nitems, packed_size; + unsigned int i; + void *packed; + const char *somestr[8] = { "a", "b", "c", "d", "e", "f", "g", "h" }; + const char *key; + + for (i = 0; i < 8; i++) { + testnvl[i] = nvlist_create(0); + ATF_REQUIRE(testnvl[i] != NULL); + ATF_REQUIRE_EQ(nvlist_error(testnvl[i]), 0); + nvlist_add_string(testnvl[i], "nvl/string", somestr[i]); + ATF_REQUIRE_EQ(nvlist_error(testnvl[i]), 0); + ATF_REQUIRE(nvlist_exists_string(testnvl[i], "nvl/string")); + } + + key = "nvl/nvlist"; + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + ATF_REQUIRE(nvlist_empty(nvl)); + ATF_REQUIRE(!nvlist_exists_string_array(nvl, key)); + + nvlist_add_nvlist_array(nvl, key, (const nvlist_t * const *)testnvl, 8); + ATF_REQUIRE_EQ(nvlist_error(nvl), 0); + ATF_REQUIRE(!nvlist_empty(nvl)); + ATF_REQUIRE(nvlist_exists_nvlist_array(nvl, key)); + ATF_REQUIRE(nvlist_exists_nvlist_array(nvl, "nvl/nvlist")); + packed = nvlist_pack(nvl, &packed_size); + ATF_REQUIRE(packed != NULL); + + unpacked = nvlist_unpack(packed, packed_size, 0); + ATF_REQUIRE(unpacked != NULL); + ATF_REQUIRE_EQ(nvlist_error(unpacked), 0); + ATF_REQUIRE(nvlist_exists_nvlist_array(unpacked, key)); + + const_result = nvlist_get_nvlist_array(unpacked, key, &nitems); + ATF_REQUIRE(const_result != NULL); + ATF_REQUIRE_EQ(nitems, 8); + for (i = 0; i < nitems; i++) { + ATF_REQUIRE_EQ(nvlist_error(const_result[i]), 0); + if (i < nitems - 1) { + ATF_REQUIRE(nvlist_get_array_next(const_result[i]) == + const_result[i + 1]); + } else { + ATF_REQUIRE(nvlist_get_array_next(const_result[i]) == + NULL); + } + ATF_REQUIRE(nvlist_get_parent(const_result[i], NULL) == unpacked); + ATF_REQUIRE(nvlist_in_array(const_result[i])); + ATF_REQUIRE(nvlist_exists_string(const_result[i], + "nvl/string")); + ATF_REQUIRE(strcmp(nvlist_get_string(const_result[i], + "nvl/string"), somestr[i]) == 0); + } + + for (i = 0; i < 8; i++) + nvlist_destroy(testnvl[i]); + free(result); + nvlist_destroy(nvl); + nvlist_destroy(unpacked); + free(packed); +} + +ATF_INIT_TEST_CASES(tp) +{ + + ATF_ADD_TEST_CASE(tp, nvlist_bool_array__basic); + ATF_ADD_TEST_CASE(tp, nvlist_string_array__basic); + ATF_ADD_TEST_CASE(tp, nvlist_descriptor_array__basic); + ATF_ADD_TEST_CASE(tp, nvlist_number_array__basic); + ATF_ADD_TEST_CASE(tp, nvlist_nvlist_array__basic) + + ATF_ADD_TEST_CASE(tp, nvlist_clone_array) + + ATF_ADD_TEST_CASE(tp, nvlist_bool_array__move); + ATF_ADD_TEST_CASE(tp, nvlist_string_array__move); + ATF_ADD_TEST_CASE(tp, nvlist_nvlist_array__move); + ATF_ADD_TEST_CASE(tp, nvlist_number_array__move); + ATF_ADD_TEST_CASE(tp, nvlist_descriptor_array__move); + + ATF_ADD_TEST_CASE(tp, nvlist_arrays__error_null); + + ATF_ADD_TEST_CASE(tp, nvlist_arrays__bad_value) + + ATF_ADD_TEST_CASE(tp, nvlist_nvlist_array__travel) + ATF_ADD_TEST_CASE(tp, nvlist_nvlist_array__travel_alternative) + + ATF_ADD_TEST_CASE(tp, nvlist_bool_array__pack) + ATF_ADD_TEST_CASE(tp, nvlist_number_array__pack) + ATF_ADD_TEST_CASE(tp, nvlist_descriptor_array__pack) + ATF_ADD_TEST_CASE(tp, nvlist_string_array__pack) + ATF_ADD_TEST_CASE(tp, nvlist_nvlist_array__pack) +} + Index: lib/libnv/tests/nv_tests.cc =================================================================== --- /dev/null +++ lib/libnv/tests/nv_tests.cc @@ -0,0 +1,1276 @@ +/*- + * Copyright (c) 2014-2015 Sandvine Inc. All rights reserved. + * 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 +__FBSDID("$FreeBSD: head/lib/libnv/tests/nv_tests.cc 285063 2015-07-02 21:58:10Z oshogbo $"); + +#include + +#include + +#include +#include +#include +#include +#include + +/* + * Test that a newly created nvlist has no errors, and is empty. + */ +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_create__is_empty); +ATF_TEST_CASE_BODY(nvlist_create__is_empty) +{ + nvlist_t *nvl; + int type; + void *it; + + nvl = nvlist_create(0); + + ATF_REQUIRE(nvl != NULL); + + ATF_REQUIRE_EQ(nvlist_error(nvl), 0); + ATF_REQUIRE(nvlist_empty(nvl)); + + it = NULL; + ATF_REQUIRE_EQ(nvlist_next(nvl, &type, &it), static_cast(NULL)); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_add_null__single_insert); +ATF_TEST_CASE_BODY(nvlist_add_null__single_insert) +{ + nvlist_t *nvl; + void *it; + const char *key; + int type; + + key = "key"; + nvl = nvlist_create(0); + + ATF_REQUIRE(nvl != NULL); + ATF_REQUIRE(!nvlist_exists(nvl, key)); + + nvlist_add_null(nvl, key); + + ATF_REQUIRE(!nvlist_empty(nvl)); + ATF_REQUIRE(nvlist_exists(nvl, key)); + ATF_REQUIRE(nvlist_exists_null(nvl, key)); + ATF_REQUIRE(nvlist_exists_null(nvl, "key")); + + /* Iterate over the nvlist; ensure that it has only our one key. */ + it = NULL; + ATF_REQUIRE_EQ(strcmp(nvlist_next(nvl, &type, &it), key), 0); + ATF_REQUIRE_EQ(type, NV_TYPE_NULL); + ATF_REQUIRE_EQ(nvlist_next(nvl, &type,&it), static_cast(NULL)); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_add_bool__single_insert); +ATF_TEST_CASE_BODY(nvlist_add_bool__single_insert) +{ + nvlist_t *nvl; + void *it; + const char *key; + int type; + + key = "name"; + nvl = nvlist_create(0); + + ATF_REQUIRE(nvl != NULL); + ATF_REQUIRE(!nvlist_exists(nvl, key)); + + nvlist_add_bool(nvl, key, true); + + ATF_REQUIRE(!nvlist_empty(nvl)); + ATF_REQUIRE(nvlist_exists(nvl, key)); + ATF_REQUIRE(nvlist_exists(nvl, "name")); + ATF_REQUIRE(nvlist_exists_bool(nvl, key)); + ATF_REQUIRE(nvlist_exists_bool(nvl, "name")); + ATF_REQUIRE_EQ(nvlist_get_bool(nvl, key), true); + + /* Iterate over the nvlist; ensure that it has only our one key. */ + it = NULL; + ATF_REQUIRE_EQ(strcmp(nvlist_next(nvl, &type, &it), key), 0); + ATF_REQUIRE_EQ(type, NV_TYPE_BOOL); + ATF_REQUIRE_EQ(nvlist_next(nvl, &type,&it), static_cast(NULL)); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_add_number__single_insert); +ATF_TEST_CASE_BODY(nvlist_add_number__single_insert) +{ + nvlist_t *nvl; + void *it; + const char *key; + uint64_t value; + int type; + + key = "foo123"; + value = 71965; + nvl = nvlist_create(0); + + ATF_REQUIRE(nvl != NULL); + ATF_REQUIRE(!nvlist_exists(nvl, key)); + + nvlist_add_number(nvl, key, value); + + ATF_REQUIRE(!nvlist_empty(nvl)); + ATF_REQUIRE(nvlist_exists(nvl, key)); + ATF_REQUIRE(nvlist_exists(nvl, "foo123")); + ATF_REQUIRE(nvlist_exists_number(nvl, key)); + ATF_REQUIRE_EQ(nvlist_get_number(nvl, key), value); + + /* Iterate over the nvlist; ensure that it has only our one key. */ + it = NULL; + ATF_REQUIRE_EQ(strcmp(nvlist_next(nvl, &type, &it), key), 0); + ATF_REQUIRE_EQ(type, NV_TYPE_NUMBER); + ATF_REQUIRE_EQ(nvlist_next(nvl, &type,&it), static_cast(NULL)); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_add_string__single_insert); +ATF_TEST_CASE_BODY(nvlist_add_string__single_insert) +{ + nvlist_t *nvl; + void *it; + const char *key; + const char *value; + int type; + + key = "test"; + value = "fgjdkgjdk"; + nvl = nvlist_create(0); + + ATF_REQUIRE(nvl != NULL); + ATF_REQUIRE(!nvlist_exists(nvl, key)); + + nvlist_add_string(nvl, key, value); + + ATF_REQUIRE(!nvlist_empty(nvl)); + ATF_REQUIRE(nvlist_exists(nvl, key)); + ATF_REQUIRE(nvlist_exists(nvl, "test")); + ATF_REQUIRE(nvlist_exists_string(nvl, key)); + ATF_REQUIRE(nvlist_exists_string(nvl, "test")); + ATF_REQUIRE_EQ(strcmp(nvlist_get_string(nvl, key), value), 0); + + /* nvlist_add_* is required to clone the value, so check for that. */ + ATF_REQUIRE(nvlist_get_string(nvl, key) != value); + + /* Iterate over the nvlist; ensure that it has only our one key. */ + it = NULL; + ATF_REQUIRE_EQ(strcmp(nvlist_next(nvl, &type, &it), key), 0); + ATF_REQUIRE_EQ(type, NV_TYPE_STRING); + ATF_REQUIRE_EQ(nvlist_next(nvl, &type,&it), static_cast(NULL)); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_add_nvlist__single_insert); +ATF_TEST_CASE_BODY(nvlist_add_nvlist__single_insert) +{ + nvlist_t *nvl; + void *it; + const char *key, *subkey; + nvlist_t *sublist; + const nvlist_t *value; + int type; + + key = "test"; + subkey = "subkey"; + sublist = nvlist_create(0); + nvl = nvlist_create(0); + + ATF_REQUIRE(nvl != NULL); + ATF_REQUIRE(!nvlist_exists(nvl, key)); + + nvlist_add_null(sublist, subkey); + nvlist_add_nvlist(nvl, key, sublist); + + ATF_REQUIRE(!nvlist_empty(nvl)); + ATF_REQUIRE(nvlist_exists(nvl, key)); + ATF_REQUIRE(nvlist_exists(nvl, "test")); + ATF_REQUIRE(nvlist_exists_nvlist(nvl, key)); + ATF_REQUIRE(nvlist_exists_nvlist(nvl, "test")); + + value = nvlist_get_nvlist(nvl, key); + ATF_REQUIRE(nvlist_exists_null(value, subkey)); + + /* nvlist_add_* is required to clone the value, so check for that. */ + ATF_REQUIRE(sublist != value); + + /* Iterate over the nvlist; ensure that it has only our one key. */ + it = NULL; + ATF_REQUIRE_EQ(strcmp(nvlist_next(nvl, &type, &it), key), 0); + ATF_REQUIRE_EQ(type, NV_TYPE_NVLIST); + ATF_REQUIRE_EQ(nvlist_next(nvl, &type,&it), static_cast(NULL)); + + nvlist_destroy(sublist); + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_add_nvlist__child_with_error); +ATF_TEST_CASE_BODY(nvlist_add_nvlist__child_with_error) +{ + nvlist_t *nvl, *parent; + + nvl = nvlist_create(0); + parent = nvlist_create(0); + + nvlist_set_error(nvl, EBADF); + nvlist_add_nvlist(parent, "test", nvl); + ATF_REQUIRE_EQ(nvlist_error(parent), EBADF); + + nvlist_destroy(nvl); + nvlist_destroy(parent); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_add_binary__single_insert); +ATF_TEST_CASE_BODY(nvlist_add_binary__single_insert) +{ + nvlist_t *nvl; + void *it; + const char *key; + void *value; + const void *ret_value; + size_t value_size, ret_size; + int type; + + key = "binary"; + value_size = 13; + value = malloc(value_size); + memset(value, 0xa5, value_size); + nvl = nvlist_create(0); + + ATF_REQUIRE(nvl != NULL); + ATF_REQUIRE(!nvlist_exists(nvl, key)); + + nvlist_add_binary(nvl, key, value, value_size); + + ATF_REQUIRE(!nvlist_empty(nvl)); + ATF_REQUIRE(nvlist_exists(nvl, key)); + ATF_REQUIRE(nvlist_exists(nvl, "binary")); + ATF_REQUIRE(nvlist_exists_binary(nvl, key)); + ATF_REQUIRE(nvlist_exists_binary(nvl, "binary")); + + ret_value = nvlist_get_binary(nvl, key, &ret_size); + ATF_REQUIRE_EQ(value_size, ret_size); + ATF_REQUIRE_EQ(memcmp(value, ret_value, ret_size), 0); + + /* nvlist_add_* is required to clone the value, so check for that. */ + ATF_REQUIRE(value != ret_value); + + /* Iterate over the nvlist; ensure that it has only our one key. */ + it = NULL; + ATF_REQUIRE_EQ(strcmp(nvlist_next(nvl, &type, &it), key), 0); + ATF_REQUIRE_EQ(type, NV_TYPE_BINARY); + ATF_REQUIRE_EQ(nvlist_next(nvl, &type,&it), static_cast(NULL)); + + nvlist_destroy(nvl); + free(value); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_clone__empty_nvlist); +ATF_TEST_CASE_BODY(nvlist_clone__empty_nvlist) +{ + nvlist_t *nvl, *clone; + + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + + clone = nvlist_clone(nvl); + ATF_REQUIRE(clone != NULL); + ATF_REQUIRE(clone != nvl); + ATF_REQUIRE(nvlist_empty(clone)); + + nvlist_destroy(clone); + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_clone__nonempty_nvlist); +ATF_TEST_CASE_BODY(nvlist_clone__nonempty_nvlist) +{ + nvlist_t *nvl, *clone; + const char *key; + void *it; + uint64_t value; + int type; + + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + + key = "testkey"; + value = 684874; + nvlist_add_number(nvl, key, value); + + clone = nvlist_clone(nvl); + ATF_REQUIRE(clone != NULL); + ATF_REQUIRE(clone != nvl); + ATF_REQUIRE(nvlist_exists_number(clone, key)); + ATF_REQUIRE_EQ(nvlist_get_number(clone, key), value); + + /* Iterate over the nvlist; ensure that it has only our one key. */ + it = NULL; + ATF_REQUIRE_EQ(strcmp(nvlist_next(clone, &type, &it), key), 0); + ATF_REQUIRE_EQ(type, NV_TYPE_NUMBER); + ATF_REQUIRE_EQ(nvlist_next(clone, &type, &it), static_cast(NULL)); + + nvlist_destroy(clone); + nvlist_destroy(nvl); +} + +static const char * const test_subnvlist_key = "nvlist"; + +static const char * const test_string_key = "string"; +static const char * const test_string_val = "59525"; + +static nvlist_t* +create_test_nvlist(void) +{ + nvlist_t *nvl, *sublist; + + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + + sublist = nvlist_create(0); + ATF_REQUIRE(sublist != NULL); + + nvlist_add_string(sublist, test_string_key, test_string_val); + nvlist_move_nvlist(nvl, test_subnvlist_key, sublist); + + return (nvl); +} + +static void +verify_test_nvlist(const nvlist_t *nvl) +{ + void *it; + const nvlist_t *value; + int type; + + ATF_REQUIRE(nvlist_exists_nvlist(nvl, test_subnvlist_key)); + + value = nvlist_get_nvlist(nvl, test_subnvlist_key); + + ATF_REQUIRE(nvlist_exists_string(value, test_string_key)); + ATF_REQUIRE_EQ(strcmp(nvlist_get_string(value, test_string_key), test_string_val), 0); + ATF_REQUIRE(nvlist_get_string(value, test_string_key) != test_string_val); + + /* Iterate over both nvlists; ensure that each has only the one key. */ + it = NULL; + ATF_REQUIRE_EQ(strcmp(nvlist_next(value, &type, &it), + test_string_key), 0); + ATF_REQUIRE_EQ(type, NV_TYPE_STRING); + ATF_REQUIRE_EQ(nvlist_next(value, &type, &it), static_cast(NULL)); + + it = NULL; + ATF_REQUIRE_EQ(strcmp(nvlist_next(nvl, &type, &it), + test_subnvlist_key), 0); + ATF_REQUIRE_EQ(type, NV_TYPE_NVLIST); + ATF_REQUIRE_EQ(nvlist_next(nvl, &type, &it), static_cast(NULL)); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_clone__nested_nvlist); +ATF_TEST_CASE_BODY(nvlist_clone__nested_nvlist) +{ + nvlist_t *nvl, *clone; + + nvl = create_test_nvlist(); + clone = nvlist_clone(nvl); + + ATF_REQUIRE(clone != NULL); + ATF_REQUIRE(clone != nvl); + verify_test_nvlist(clone); + + nvlist_destroy(clone); + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_clone__error_nvlist); +ATF_TEST_CASE_BODY(nvlist_clone__error_nvlist) +{ + nvlist_t *nvl, *clone; + + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + + nvlist_set_error(nvl, ENOMEM); + + clone = nvlist_clone(nvl); + ATF_REQUIRE(clone == NULL); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_pack__empty_nvlist); +ATF_TEST_CASE_BODY(nvlist_pack__empty_nvlist) +{ + nvlist_t *nvl, *unpacked; + void *packed; + size_t packed_size; + + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + + packed = nvlist_pack(nvl, &packed_size); + ATF_REQUIRE(packed != NULL); + + unpacked = nvlist_unpack(packed, packed_size, 0); + ATF_REQUIRE(unpacked != NULL); + ATF_REQUIRE(unpacked != nvl); + ATF_REQUIRE(nvlist_empty(unpacked)); + + nvlist_destroy(unpacked); + nvlist_destroy(nvl); + free(packed); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_unpack__flags_nvlist); +ATF_TEST_CASE_BODY(nvlist_unpack__flags_nvlist) +{ + nvlist_t *nvl, *unpacked; + void *packed; + size_t packed_size; + + nvl = nvlist_create(NV_FLAG_NO_UNIQUE); + ATF_REQUIRE(nvl != NULL); + + nvlist_add_bool(nvl, "name", true); + ATF_REQUIRE(!nvlist_empty(nvl)); + ATF_REQUIRE(nvlist_exists_bool(nvl, "name")); + + packed = nvlist_pack(nvl, &packed_size); + ATF_REQUIRE(packed != NULL); + + unpacked = nvlist_unpack(packed, packed_size, 0); + ATF_REQUIRE(unpacked == NULL); + + unpacked = nvlist_unpack(packed, packed_size, NV_FLAG_IGNORE_CASE); + ATF_REQUIRE(unpacked == NULL); + + unpacked = nvlist_unpack(packed, packed_size, NV_FLAG_NO_UNIQUE); + ATF_REQUIRE(unpacked != NULL); + ATF_REQUIRE(unpacked != nvl); + ATF_REQUIRE(!nvlist_empty(unpacked)); + ATF_REQUIRE(nvlist_exists_bool(unpacked, "name")); + + nvlist_destroy(unpacked); + nvlist_destroy(nvl); + free(packed); +} + +static void +verify_null(const nvlist_t *nvl, int type) +{ + + ATF_REQUIRE_EQ(type, NV_TYPE_NULL); +} + +static void +verify_number(const nvlist_t *nvl, const char *name, int type, uint64_t value) +{ + + ATF_REQUIRE_EQ(type, NV_TYPE_NUMBER); + ATF_REQUIRE_EQ(nvlist_get_number(nvl, name), value); +} + +static void +verify_string(const nvlist_t *nvl, const char *name, int type, + const char * value) +{ + + ATF_REQUIRE_EQ(type, NV_TYPE_STRING); + ATF_REQUIRE_EQ(strcmp(nvlist_get_string(nvl, name), value), 0); +} + +static void +verify_nvlist(const nvlist_t *nvl, const char *name, int type) +{ + + ATF_REQUIRE_EQ(type, NV_TYPE_NVLIST); + verify_test_nvlist(nvlist_get_nvlist(nvl, name)); +} + +static void +verify_binary(const nvlist_t *nvl, const char *name, int type, + const void * value, size_t size) +{ + const void *actual_value; + size_t actual_size; + + ATF_REQUIRE_EQ(type, NV_TYPE_BINARY); + actual_value = nvlist_get_binary(nvl, name, &actual_size); + ATF_REQUIRE_EQ(size, actual_size); + ATF_REQUIRE_EQ(memcmp(value, actual_value, size), 0); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_pack__multiple_values); +ATF_TEST_CASE_BODY(nvlist_pack__multiple_values) +{ + std::ostringstream msg; + std::set keys_seen; + nvlist_t *nvl, *unpacked, *nvvalue; + const char *nullkey, *numkey, *strkey, *nvkey, *binkey, *name; + int numvalue; + const char * strvalue; + void *binvalue, *packed, *it; + size_t binsize, packed_size; + int type; + + nvl = nvlist_create(0); + + nullkey = "null"; + nvlist_add_null(nvl, nullkey); + + numkey = "number"; + numvalue = 939853984; + nvlist_add_number(nvl, numkey, numvalue); + + strkey = "string"; + strvalue = "jfieutijf"; + nvlist_add_string(nvl, strkey, strvalue); + + nvkey = "nvlist"; + nvvalue = create_test_nvlist(); + nvlist_move_nvlist(nvl, nvkey, nvvalue); + + binkey = "binary"; + binsize = 4; + binvalue = malloc(binsize); + memset(binvalue, 'b', binsize); + nvlist_move_binary(nvl, binkey, binvalue, binsize); + + packed = nvlist_pack(nvl, &packed_size); + ATF_REQUIRE(packed != NULL); + + unpacked = nvlist_unpack(packed, packed_size, 0); + ATF_REQUIRE(unpacked != 0); + + it = NULL; + while ((name = nvlist_next(unpacked, &type, &it)) != NULL) { + /* Ensure that we see every key only once. */ + ATF_REQUIRE_EQ(keys_seen.count(name), 0); + + if (strcmp(name, nullkey) == 0) + verify_null(unpacked, type); + else if (strcmp(name, numkey) == 0) + verify_number(unpacked, name, type, numvalue); + else if (strcmp(name, strkey) == 0) + verify_string(unpacked, name, type, strvalue); + else if (strcmp(name, nvkey) == 0) + verify_nvlist(unpacked, name, type); + else if (strcmp(name, binkey) == 0) + verify_binary(unpacked, name, type, binvalue, binsize); + else { + msg << "Unexpected key :'" << name << "'"; + ATF_FAIL(msg.str().c_str()); + } + + keys_seen.insert(name); + } + + /* Ensure that we saw every key. */ + ATF_REQUIRE_EQ(keys_seen.size(), 5); + + nvlist_destroy(nvl); + nvlist_destroy(unpacked); + free(packed); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_pack__error_nvlist); +ATF_TEST_CASE_BODY(nvlist_pack__error_nvlist) +{ + nvlist_t *nvl; + void *packed; + size_t size; + + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + + nvlist_set_error(nvl, ENOMEM); + + packed = nvlist_pack(nvl, &size); + ATF_REQUIRE(packed == NULL); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_unpack__duplicate_key); +ATF_TEST_CASE_BODY(nvlist_unpack__duplicate_key) +{ + nvlist_t *nvl, *unpacked; + const char *key1, *key2; + void *packed, *keypos; + size_t size, keylen; + + nvl = nvlist_create(0); + + key1 = "key1"; + keylen = strlen(key1); + nvlist_add_number(nvl, key1, 5); + + key2 = "key2"; + ATF_REQUIRE_EQ(keylen, strlen(key2)); + nvlist_add_number(nvl, key2, 10); + + packed = nvlist_pack(nvl, &size); + + /* + * Mangle the packed nvlist by replacing key1 with key2, creating a + * packed nvlist with a duplicate key. + */ + keypos = memmem(packed, size, key1, keylen); + ATF_REQUIRE(keypos != NULL); + memcpy(keypos, key2, keylen); + + unpacked = nvlist_unpack(packed, size, 0); + ATF_REQUIRE(nvlist_error(unpacked) != 0); + + free(packed); + nvlist_destroy(nvl); + nvlist_destroy(unpacked); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_move_string__single_insert); +ATF_TEST_CASE_BODY(nvlist_move_string__single_insert) +{ + nvlist_t *nvl; + const char *key; + char *value; + + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + + key = "testkey"; + value = strdup("testval"); + ATF_REQUIRE(value != NULL); + + nvlist_move_string(nvl, key, value); + ATF_REQUIRE_EQ(nvlist_get_string(nvl, key), value); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_move_nvlist__null_child); +ATF_TEST_CASE_BODY(nvlist_move_nvlist__null_child) +{ + nvlist_t *parent; + + parent = nvlist_create(0); + + nvlist_move_nvlist(parent, "test", NULL); + + ATF_REQUIRE(nvlist_error(parent) != 0); + + nvlist_destroy(parent); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_move_nvlist__child_with_error); +ATF_TEST_CASE_BODY(nvlist_move_nvlist__child_with_error) +{ + nvlist_t *nvl, *parent; + + nvl = nvlist_create(0); + parent = nvlist_create(0); + + nvlist_set_error(nvl, EBADF); + nvlist_move_nvlist(parent, "test", nvl); + ATF_REQUIRE_EQ(nvlist_error(parent), EBADF); + + nvlist_destroy(parent); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_move_nvlist__single_insert); +ATF_TEST_CASE_BODY(nvlist_move_nvlist__single_insert) +{ + nvlist_t *nvl; + const char *key; + nvlist_t *value; + + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + + key = "testkey"; + value = nvlist_create(0); + ATF_REQUIRE(value != NULL); + + nvlist_move_nvlist(nvl, key, value); + ATF_REQUIRE_EQ(nvlist_get_nvlist(nvl, key), value); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_move_binary__single_insert); +ATF_TEST_CASE_BODY(nvlist_move_binary__single_insert) +{ + nvlist_t *nvl; + const char *key; + void *value; + size_t size, actual_size; + + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + + key = "testkey"; + size = 73; + value = malloc(size); + ATF_REQUIRE(value != NULL); + + nvlist_move_binary(nvl, key, value, size); + ATF_REQUIRE_EQ(nvlist_get_binary(nvl, key, &actual_size), value); + ATF_REQUIRE_EQ(size, actual_size); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_take_bool__single_remove); +ATF_TEST_CASE_BODY(nvlist_take_bool__single_remove) +{ + nvlist_t *nvl; + const char *testkey; + bool testval; + + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + + testkey = "boolkey"; + testval = false; + nvlist_add_bool(nvl, testkey, testval); + + ATF_REQUIRE_EQ(nvlist_take_bool(nvl, testkey), testval); + ATF_REQUIRE(nvlist_empty(nvl)); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_take_bool__other_keys_unchanged); +ATF_TEST_CASE_BODY(nvlist_take_bool__other_keys_unchanged) +{ + nvlist_t *nvl; + const char *testkey, *otherkey1, *otherkey2; + bool testval, otherval1; + nvlist_t *otherval2; + + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + + testkey = "boolkey"; + testval = true; + nvlist_add_bool(nvl, testkey, testval); + + otherkey1 = "key1"; + otherval1 = false; + nvlist_add_bool(nvl, otherkey1, otherval1); + + otherkey2 = "key2"; + otherval2 = create_test_nvlist(); + nvlist_move_nvlist(nvl, otherkey2, otherval2); + + ATF_REQUIRE_EQ(nvlist_take_bool(nvl, testkey), testval); + + ATF_REQUIRE(nvlist_exists_bool(nvl, otherkey1)); + ATF_REQUIRE_EQ(nvlist_get_bool(nvl, otherkey1), otherval1); + + ATF_REQUIRE(nvlist_exists_nvlist(nvl, otherkey2)); + verify_test_nvlist(nvlist_get_nvlist(nvl, otherkey2)); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_take_number__single_remove); +ATF_TEST_CASE_BODY(nvlist_take_number__single_remove) +{ + nvlist_t *nvl; + const char *testkey; + uint64_t testval; + + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + + testkey = "numkey"; + testval = std::numeric_limits::max(); + nvlist_add_number(nvl, testkey, testval); + + ATF_REQUIRE_EQ(nvlist_take_number(nvl, testkey), testval); + ATF_REQUIRE(nvlist_empty(nvl)); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_take_number__other_keys_unchanged); +ATF_TEST_CASE_BODY(nvlist_take_number__other_keys_unchanged) +{ + nvlist_t *nvl; + const char *testkey, *otherkey1, *otherkey2; + uint64_t testval, otherval1; + const char *otherval2; + + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + + otherkey1 = "key1"; + otherval1 = 5; + nvlist_add_number(nvl, otherkey1, otherval1); + + testkey = "numkey"; + testval = 1654; + nvlist_add_number(nvl, testkey, testval); + + otherkey2 = "key2"; + otherval2 = "string"; + nvlist_add_string(nvl, otherkey2, otherval2); + + ATF_REQUIRE_EQ(nvlist_take_number(nvl, testkey), testval); + + ATF_REQUIRE(nvlist_exists_number(nvl, otherkey1)); + ATF_REQUIRE_EQ(nvlist_get_number(nvl, otherkey1), otherval1); + + ATF_REQUIRE(nvlist_exists_string(nvl, otherkey2)); + ATF_REQUIRE_EQ(strcmp(nvlist_get_string(nvl, otherkey2), otherval2), 0); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_take_string__single_remove); +ATF_TEST_CASE_BODY(nvlist_take_string__single_remove) +{ + nvlist_t *nvl; + const char *testkey; + const char *testval; + + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + + testkey = "numkey"; + testval = "nvlist"; + nvlist_add_string(nvl, testkey, testval); + + ATF_REQUIRE_EQ(strcmp(nvlist_take_string(nvl, testkey), testval), 0); + ATF_REQUIRE(nvlist_empty(nvl)); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_take_string__other_keys_unchanged); +ATF_TEST_CASE_BODY(nvlist_take_string__other_keys_unchanged) +{ + nvlist_t *nvl; + const char *testkey, *otherkey1, *otherkey2; + const char *testval, *otherval1; + bool otherval2; + + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + + otherkey1 = "key1"; + otherval1 = "fjdifjdk"; + nvlist_add_string(nvl, otherkey1, otherval1); + + otherkey2 = "key2"; + otherval2 = true; + nvlist_add_bool(nvl, otherkey2, otherval2); + + testkey = "strkey"; + testval = "1654"; + nvlist_add_string(nvl, testkey, testval); + + ATF_REQUIRE_EQ(strcmp(nvlist_take_string(nvl, testkey), testval), 0); + + ATF_REQUIRE(nvlist_exists_string(nvl, otherkey1)); + ATF_REQUIRE_EQ(strcmp(nvlist_get_string(nvl, otherkey1), otherval1), 0); + + ATF_REQUIRE(nvlist_exists_bool(nvl, otherkey2)); + ATF_REQUIRE_EQ(nvlist_get_bool(nvl, otherkey2), otherval2); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_take_nvlist__single_remove); +ATF_TEST_CASE_BODY(nvlist_take_nvlist__single_remove) +{ + nvlist_t *nvl; + const char *testkey; + nvlist_t *testval; + + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + + testkey = "numkey"; + testval = create_test_nvlist(); + nvlist_move_nvlist(nvl, testkey, testval); + + verify_test_nvlist(nvlist_take_nvlist(nvl, testkey)); + ATF_REQUIRE(nvlist_empty(nvl)); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_take_nvlist__other_keys_unchanged); +ATF_TEST_CASE_BODY(nvlist_take_nvlist__other_keys_unchanged) +{ + nvlist_t *nvl; + const char *testkey, *otherkey1, *otherkey2; + nvlist_t *testval, *otherval1; + + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + + testkey = "strkey"; + testval = create_test_nvlist(); + nvlist_move_nvlist(nvl, testkey, testval); + + otherkey1 = "key1"; + otherval1 = nvlist_create(0); + nvlist_move_nvlist(nvl, otherkey1, otherval1); + + otherkey2 = "key2"; + nvlist_add_null(nvl, otherkey2); + + verify_test_nvlist(nvlist_take_nvlist(nvl, testkey)); + + ATF_REQUIRE(nvlist_exists_nvlist(nvl, otherkey1)); + ATF_REQUIRE(nvlist_empty(nvlist_get_nvlist(nvl, otherkey1))); + + ATF_REQUIRE(nvlist_exists_null(nvl, otherkey2)); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_take_binary__single_remove); +ATF_TEST_CASE_BODY(nvlist_take_binary__single_remove) +{ + nvlist_t *nvl; + const char *testkey; + void *testval; + const void *actual_val; + size_t testsize, actual_size; + + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + + testkey = "numkey"; + testsize = 457; + testval = malloc(testsize); + memset(testval, '5', testsize); + nvlist_move_binary(nvl, testkey, testval, testsize); + + actual_val = nvlist_take_binary(nvl, testkey, &actual_size); + ATF_REQUIRE_EQ(testsize, actual_size); + ATF_REQUIRE_EQ(memcmp(actual_val, testval, testsize), 0); + ATF_REQUIRE(nvlist_empty(nvl)); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_take_binary__other_keys_unchanged); +ATF_TEST_CASE_BODY(nvlist_take_binary__other_keys_unchanged) +{ + nvlist_t *nvl; + const char *testkey, *otherkey1, *otherkey2; + const void *actual_value; + char testval[] = "gjiertj"; + char otherval1[] = "fdreg"; + size_t testsize, othersize, actual_size; + bool otherval2; + + nvl = nvlist_create(0); + ATF_REQUIRE(nvl != NULL); + + otherkey1 = "key1"; + othersize = sizeof(otherval1); + nvlist_add_binary(nvl, otherkey1, otherval1, othersize); + + otherkey2 = "key2"; + otherval2 = true; + nvlist_add_bool(nvl, otherkey2, otherval2); + + testkey = "strkey"; + testsize = sizeof(testval); + nvlist_add_binary(nvl, testkey, testval, testsize); + + actual_value = nvlist_take_binary(nvl, testkey, &actual_size); + ATF_REQUIRE_EQ(testsize, actual_size); + ATF_REQUIRE_EQ(memcmp(actual_value, testval, testsize), 0); + + ATF_REQUIRE(nvlist_exists_binary(nvl, otherkey1)); + actual_value = nvlist_get_binary(nvl, otherkey1, &actual_size); + ATF_REQUIRE_EQ(othersize, actual_size); + ATF_REQUIRE_EQ(memcmp(actual_value, otherval1, othersize), 0); + + ATF_REQUIRE(nvlist_exists_bool(nvl, otherkey2)); + ATF_REQUIRE_EQ(nvlist_get_bool(nvl, otherkey2), otherval2); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_free__single_null); +ATF_TEST_CASE_BODY(nvlist_free__single_null) +{ + nvlist_t *nvl; + const char *key; + + nvl = nvlist_create(0); + key = "test"; + nvlist_add_null(nvl, key); + + nvlist_free(nvl, key); + ATF_REQUIRE(nvlist_empty(nvl)); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_free__single_bool); +ATF_TEST_CASE_BODY(nvlist_free__single_bool) +{ + nvlist_t *nvl; + const char *key; + + nvl = nvlist_create(0); + key = "test"; + nvlist_add_bool(nvl, key, true); + + nvlist_free(nvl, key); + ATF_REQUIRE(nvlist_empty(nvl)); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_free__single_number); +ATF_TEST_CASE_BODY(nvlist_free__single_number) +{ + nvlist_t *nvl; + const char *key; + + nvl = nvlist_create(0); + key = "test"; + nvlist_add_number(nvl, key, 584); + + nvlist_free(nvl, key); + ATF_REQUIRE(nvlist_empty(nvl)); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_free__single_string); +ATF_TEST_CASE_BODY(nvlist_free__single_string) +{ + nvlist_t *nvl; + const char *key; + + nvl = nvlist_create(0); + key = "test"; + nvlist_add_string(nvl, key, "gjkfkjd"); + + nvlist_free(nvl, key); + ATF_REQUIRE(nvlist_empty(nvl)); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_free__single_nvlist); +ATF_TEST_CASE_BODY(nvlist_free__single_nvlist) +{ + nvlist_t *nvl; + const char *key; + + nvl = nvlist_create(0); + key = "test"; + nvlist_add_nvlist(nvl, key, nvlist_create(0)); + + nvlist_free(nvl, key); + ATF_REQUIRE(nvlist_empty(nvl)); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_free__single_binary); +ATF_TEST_CASE_BODY(nvlist_free__single_binary) +{ + nvlist_t *nvl; + const char *key; + + nvl = nvlist_create(0); + key = "test"; + nvlist_add_binary(nvl, key, "jgjgfd", 6); + + nvlist_free(nvl, key); + ATF_REQUIRE(nvlist_empty(nvl)); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_free_null__single_null); +ATF_TEST_CASE_BODY(nvlist_free_null__single_null) +{ + nvlist_t *nvl; + const char *key; + + nvl = nvlist_create(0); + key = "test"; + nvlist_add_null(nvl, key); + + nvlist_free_null(nvl, key); + ATF_REQUIRE(nvlist_empty(nvl)); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_free_bool__single_bool); +ATF_TEST_CASE_BODY(nvlist_free_bool__single_bool) +{ + nvlist_t *nvl; + const char *key; + + nvl = nvlist_create(0); + key = "test"; + nvlist_add_bool(nvl, key, true); + + nvlist_free_bool(nvl, key); + ATF_REQUIRE(nvlist_empty(nvl)); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_free_number__single_number); +ATF_TEST_CASE_BODY(nvlist_free_number__single_number) +{ + nvlist_t *nvl; + const char *key; + + nvl = nvlist_create(0); + key = "test"; + nvlist_add_number(nvl, key, 584); + + nvlist_free_number(nvl, key); + ATF_REQUIRE(nvlist_empty(nvl)); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_free_string__single_string); +ATF_TEST_CASE_BODY(nvlist_free_string__single_string) +{ + nvlist_t *nvl; + const char *key; + + nvl = nvlist_create(0); + key = "test"; + nvlist_add_string(nvl, key, "gjkfkjd"); + + nvlist_free_string(nvl, key); + ATF_REQUIRE(nvlist_empty(nvl)); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_free_nvlist__single_nvlist); +ATF_TEST_CASE_BODY(nvlist_free_nvlist__single_nvlist) +{ + nvlist_t *nvl; + const char *key; + + nvl = nvlist_create(0); + key = "test"; + nvlist_add_nvlist(nvl, key, nvlist_create(0)); + + nvlist_free_nvlist(nvl, key); + ATF_REQUIRE(nvlist_empty(nvl)); + + nvlist_destroy(nvl); +} + +ATF_TEST_CASE_WITHOUT_HEAD(nvlist_free_binary__single_binary); +ATF_TEST_CASE_BODY(nvlist_free_binary__single_binary) +{ + nvlist_t *nvl; + const char *key; + + nvl = nvlist_create(0); + key = "test"; + nvlist_add_binary(nvl, key, "jgjgfd", 6); + + nvlist_free_binary(nvl, key); + ATF_REQUIRE(nvlist_empty(nvl)); + + nvlist_destroy(nvl); +} + +ATF_INIT_TEST_CASES(tp) +{ + ATF_ADD_TEST_CASE(tp, nvlist_create__is_empty); + ATF_ADD_TEST_CASE(tp, nvlist_add_null__single_insert); + ATF_ADD_TEST_CASE(tp, nvlist_add_bool__single_insert); + ATF_ADD_TEST_CASE(tp, nvlist_add_number__single_insert); + ATF_ADD_TEST_CASE(tp, nvlist_add_string__single_insert); + ATF_ADD_TEST_CASE(tp, nvlist_add_nvlist__single_insert); + ATF_ADD_TEST_CASE(tp, nvlist_add_nvlist__child_with_error); + ATF_ADD_TEST_CASE(tp, nvlist_add_binary__single_insert); + + ATF_ADD_TEST_CASE(tp, nvlist_clone__empty_nvlist); + ATF_ADD_TEST_CASE(tp, nvlist_clone__nonempty_nvlist); + ATF_ADD_TEST_CASE(tp, nvlist_clone__nested_nvlist); + ATF_ADD_TEST_CASE(tp, nvlist_clone__error_nvlist); + + ATF_ADD_TEST_CASE(tp, nvlist_pack__empty_nvlist); + ATF_ADD_TEST_CASE(tp, nvlist_pack__multiple_values); + ATF_ADD_TEST_CASE(tp, nvlist_pack__error_nvlist); + ATF_ADD_TEST_CASE(tp, nvlist_unpack__duplicate_key); + ATF_ADD_TEST_CASE(tp, nvlist_unpack__flags_nvlist); + + ATF_ADD_TEST_CASE(tp, nvlist_move_string__single_insert); + ATF_ADD_TEST_CASE(tp, nvlist_move_nvlist__single_insert); + ATF_ADD_TEST_CASE(tp, nvlist_move_nvlist__null_child); + ATF_ADD_TEST_CASE(tp, nvlist_move_nvlist__child_with_error); + ATF_ADD_TEST_CASE(tp, nvlist_move_binary__single_insert); + + ATF_ADD_TEST_CASE(tp, nvlist_take_bool__single_remove); + ATF_ADD_TEST_CASE(tp, nvlist_take_bool__other_keys_unchanged); + ATF_ADD_TEST_CASE(tp, nvlist_take_number__single_remove); + ATF_ADD_TEST_CASE(tp, nvlist_take_number__other_keys_unchanged); + ATF_ADD_TEST_CASE(tp, nvlist_take_string__single_remove); + ATF_ADD_TEST_CASE(tp, nvlist_take_string__other_keys_unchanged); + ATF_ADD_TEST_CASE(tp, nvlist_take_nvlist__single_remove); + ATF_ADD_TEST_CASE(tp, nvlist_take_nvlist__other_keys_unchanged); + ATF_ADD_TEST_CASE(tp, nvlist_take_binary__single_remove); + ATF_ADD_TEST_CASE(tp, nvlist_take_binary__other_keys_unchanged); + + ATF_ADD_TEST_CASE(tp, nvlist_free__single_null); + ATF_ADD_TEST_CASE(tp, nvlist_free__single_bool); + ATF_ADD_TEST_CASE(tp, nvlist_free__single_number); + ATF_ADD_TEST_CASE(tp, nvlist_free__single_string); + ATF_ADD_TEST_CASE(tp, nvlist_free__single_nvlist); + ATF_ADD_TEST_CASE(tp, nvlist_free__single_binary); + + ATF_ADD_TEST_CASE(tp, nvlist_free_null__single_null); + ATF_ADD_TEST_CASE(tp, nvlist_free_bool__single_bool); + ATF_ADD_TEST_CASE(tp, nvlist_free_number__single_number); + ATF_ADD_TEST_CASE(tp, nvlist_free_string__single_string); + ATF_ADD_TEST_CASE(tp, nvlist_free_nvlist__single_nvlist); + ATF_ADD_TEST_CASE(tp, nvlist_free_binary__single_binary); +} Index: lib/libnv/tests/nvlist_add_test.c =================================================================== --- /dev/null +++ lib/libnv/tests/nvlist_add_test.c @@ -0,0 +1,196 @@ +/*- + * Copyright (c) 2013 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Pawel Jakub Dawidek under sponsorship from + * the FreeBSD Foundation. + * + * 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 AUTHORS 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 AUTHORS 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: head/lib/libnv/tests/nvlist_add_test.c 285063 2015-07-02 21:58:10Z oshogbo $ + */ + +#include + +#include +#include +#include + +static int ntest = 1; + +#define CHECK(expr) do { \ + if ((expr)) \ + printf("ok # %d %s:%u\n", ntest, __FILE__, __LINE__); \ + else \ + printf("not ok # %d %s:%u\n", ntest, __FILE__, __LINE__);\ + ntest++; \ +} while (0) + +int +main(void) +{ + const nvlist_t *cnvl; + nvlist_t *nvl; + + printf("1..94\n"); + + nvl = nvlist_create(0); + + CHECK(!nvlist_exists_null(nvl, "nvlist/null")); + nvlist_add_null(nvl, "nvlist/null"); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists_null(nvl, "nvlist/null")); + + CHECK(!nvlist_exists_bool(nvl, "nvlist/bool/true")); + nvlist_add_bool(nvl, "nvlist/bool/true", true); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists_bool(nvl, "nvlist/bool/true")); + + CHECK(!nvlist_exists_bool(nvl, "nvlist/bool/false")); + nvlist_add_bool(nvl, "nvlist/bool/false", false); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists_bool(nvl, "nvlist/bool/false")); + + CHECK(!nvlist_exists_number(nvl, "nvlist/number/0")); + nvlist_add_number(nvl, "nvlist/number/0", 0); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists_number(nvl, "nvlist/number/0")); + + CHECK(!nvlist_exists_number(nvl, "nvlist/number/1")); + nvlist_add_number(nvl, "nvlist/number/1", 1); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists_number(nvl, "nvlist/number/1")); + + CHECK(!nvlist_exists_number(nvl, "nvlist/number/-1")); + nvlist_add_number(nvl, "nvlist/number/-1", -1); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists_number(nvl, "nvlist/number/-1")); + + CHECK(!nvlist_exists_number(nvl, "nvlist/number/UINT64_MAX")); + nvlist_add_number(nvl, "nvlist/number/UINT64_MAX", UINT64_MAX); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists_number(nvl, "nvlist/number/UINT64_MAX")); + + CHECK(!nvlist_exists_number(nvl, "nvlist/number/INT64_MIN")); + nvlist_add_number(nvl, "nvlist/number/INT64_MIN", INT64_MIN); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists_number(nvl, "nvlist/number/INT64_MIN")); + + CHECK(!nvlist_exists_number(nvl, "nvlist/number/INT64_MAX")); + nvlist_add_number(nvl, "nvlist/number/INT64_MAX", INT64_MAX); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists_number(nvl, "nvlist/number/INT64_MAX")); + + CHECK(!nvlist_exists_string(nvl, "nvlist/string/")); + nvlist_add_string(nvl, "nvlist/string/", ""); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists_string(nvl, "nvlist/string/")); + + CHECK(!nvlist_exists_string(nvl, "nvlist/string/x")); + nvlist_add_string(nvl, "nvlist/string/x", "x"); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists_string(nvl, "nvlist/string/x")); + + CHECK(!nvlist_exists_string(nvl, "nvlist/string/abcdefghijklmnopqrstuvwxyz")); + nvlist_add_string(nvl, "nvlist/string/abcdefghijklmnopqrstuvwxyz", "abcdefghijklmnopqrstuvwxyz"); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists_string(nvl, "nvlist/string/abcdefghijklmnopqrstuvwxyz")); + + CHECK(!nvlist_exists_string(nvl, "nvlist/stringf/")); + nvlist_add_stringf(nvl, "nvlist/stringf/", "%s", ""); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists_string(nvl, "nvlist/stringf/")); + + CHECK(!nvlist_exists_string(nvl, "nvlist/stringf/x")); + nvlist_add_stringf(nvl, "nvlist/stringf/x", "%s", "x"); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists_string(nvl, "nvlist/stringf/x")); + + CHECK(!nvlist_exists_string(nvl, "nvlist/stringf/666Xabc")); + nvlist_add_stringf(nvl, "nvlist/stringf/666Xabc", "%d%c%s", 666, 'X', "abc"); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists_string(nvl, "nvlist/stringf/666Xabc")); + + CHECK(!nvlist_exists_descriptor(nvl, "nvlist/descriptor/STDERR_FILENO")); + nvlist_add_descriptor(nvl, "nvlist/descriptor/STDERR_FILENO", STDERR_FILENO); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor/STDERR_FILENO")); + + CHECK(!nvlist_exists_binary(nvl, "nvlist/binary/x")); + nvlist_add_binary(nvl, "nvlist/binary/x", "x", 1); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists_binary(nvl, "nvlist/binary/x")); + + CHECK(!nvlist_exists_binary(nvl, "nvlist/binary/abcdefghijklmnopqrstuvwxyz")); + nvlist_add_binary(nvl, "nvlist/binary/abcdefghijklmnopqrstuvwxyz", "abcdefghijklmnopqrstuvwxyz", sizeof("abcdefghijklmnopqrstuvwxyz")); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists_binary(nvl, "nvlist/binary/abcdefghijklmnopqrstuvwxyz")); + + CHECK(!nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + nvlist_add_nvlist(nvl, "nvlist/nvlist", nvl); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + + CHECK(nvlist_exists_null(nvl, "nvlist/null")); + CHECK(nvlist_exists_bool(nvl, "nvlist/bool/true")); + CHECK(nvlist_exists_bool(nvl, "nvlist/bool/false")); + CHECK(nvlist_exists_number(nvl, "nvlist/number/0")); + CHECK(nvlist_exists_number(nvl, "nvlist/number/1")); + CHECK(nvlist_exists_number(nvl, "nvlist/number/-1")); + CHECK(nvlist_exists_number(nvl, "nvlist/number/UINT64_MAX")); + CHECK(nvlist_exists_number(nvl, "nvlist/number/INT64_MIN")); + CHECK(nvlist_exists_number(nvl, "nvlist/number/INT64_MAX")); + CHECK(nvlist_exists_string(nvl, "nvlist/string/")); + CHECK(nvlist_exists_string(nvl, "nvlist/string/x")); + CHECK(nvlist_exists_string(nvl, "nvlist/string/abcdefghijklmnopqrstuvwxyz")); + CHECK(nvlist_exists_string(nvl, "nvlist/stringf/")); + CHECK(nvlist_exists_string(nvl, "nvlist/stringf/x")); + CHECK(nvlist_exists_string(nvl, "nvlist/stringf/666Xabc")); + CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor/STDERR_FILENO")); + CHECK(nvlist_exists_binary(nvl, "nvlist/binary/x")); + CHECK(nvlist_exists_binary(nvl, "nvlist/binary/abcdefghijklmnopqrstuvwxyz")); + CHECK(nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + + cnvl = nvlist_get_nvlist(nvl, "nvlist/nvlist"); + CHECK(nvlist_exists_null(cnvl, "nvlist/null")); + CHECK(nvlist_exists_bool(cnvl, "nvlist/bool/true")); + CHECK(nvlist_exists_bool(cnvl, "nvlist/bool/false")); + CHECK(nvlist_exists_number(cnvl, "nvlist/number/0")); + CHECK(nvlist_exists_number(cnvl, "nvlist/number/1")); + CHECK(nvlist_exists_number(cnvl, "nvlist/number/-1")); + CHECK(nvlist_exists_number(cnvl, "nvlist/number/UINT64_MAX")); + CHECK(nvlist_exists_number(cnvl, "nvlist/number/INT64_MIN")); + CHECK(nvlist_exists_number(cnvl, "nvlist/number/INT64_MAX")); + CHECK(nvlist_exists_string(cnvl, "nvlist/string/")); + CHECK(nvlist_exists_string(cnvl, "nvlist/string/x")); + CHECK(nvlist_exists_string(cnvl, "nvlist/string/abcdefghijklmnopqrstuvwxyz")); + CHECK(nvlist_exists_string(cnvl, "nvlist/stringf/")); + CHECK(nvlist_exists_string(cnvl, "nvlist/stringf/x")); + CHECK(nvlist_exists_string(cnvl, "nvlist/stringf/666Xabc")); + CHECK(nvlist_exists_descriptor(cnvl, "nvlist/descriptor/STDERR_FILENO")); + CHECK(nvlist_exists_binary(cnvl, "nvlist/binary/x")); + CHECK(nvlist_exists_binary(cnvl, "nvlist/binary/abcdefghijklmnopqrstuvwxyz")); + + nvlist_destroy(nvl); + + return (0); +} Index: lib/libnv/tests/nvlist_exists_test.c =================================================================== --- /dev/null +++ lib/libnv/tests/nvlist_exists_test.c @@ -0,0 +1,321 @@ +/*- + * Copyright (c) 2013 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Pawel Jakub Dawidek under sponsorship from + * the FreeBSD Foundation. + * + * 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 AUTHORS 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 AUTHORS 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: head/lib/libnv/tests/nvlist_exists_test.c 285063 2015-07-02 21:58:10Z oshogbo $ + */ + +#include + +#include +#include + +static int ntest = 1; + +#define CHECK(expr) do { \ + if ((expr)) \ + printf("ok # %d %s:%u\n", ntest, __FILE__, __LINE__); \ + else \ + printf("not ok # %d %s:%u\n", ntest, __FILE__, __LINE__);\ + ntest++; \ +} while (0) + +int +main(void) +{ + nvlist_t *nvl; + + printf("1..232\n"); + + nvl = nvlist_create(0); + + CHECK(!nvlist_exists(nvl, "nvlist/null")); + CHECK(!nvlist_exists_null(nvl, "nvlist/null")); + CHECK(!nvlist_exists_bool(nvl, "nvlist/null")); + CHECK(!nvlist_exists_number(nvl, "nvlist/null")); + CHECK(!nvlist_exists_string(nvl, "nvlist/null")); + CHECK(!nvlist_exists_nvlist(nvl, "nvlist/null")); + CHECK(!nvlist_exists_descriptor(nvl, "nvlist/null")); + CHECK(!nvlist_exists_binary(nvl, "nvlist/null")); + nvlist_add_null(nvl, "nvlist/null"); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists(nvl, "nvlist/null")); + CHECK(nvlist_exists_null(nvl, "nvlist/null")); + CHECK(!nvlist_exists_bool(nvl, "nvlist/null")); + CHECK(!nvlist_exists_number(nvl, "nvlist/null")); + CHECK(!nvlist_exists_string(nvl, "nvlist/null")); + CHECK(!nvlist_exists_nvlist(nvl, "nvlist/null")); + CHECK(!nvlist_exists_descriptor(nvl, "nvlist/null")); + CHECK(!nvlist_exists_binary(nvl, "nvlist/null")); + + CHECK(!nvlist_exists(nvl, "nvlist/bool")); + CHECK(!nvlist_exists_null(nvl, "nvlist/bool")); + CHECK(!nvlist_exists_bool(nvl, "nvlist/bool")); + CHECK(!nvlist_exists_number(nvl, "nvlist/bool")); + CHECK(!nvlist_exists_string(nvl, "nvlist/bool")); + CHECK(!nvlist_exists_nvlist(nvl, "nvlist/bool")); + CHECK(!nvlist_exists_descriptor(nvl, "nvlist/bool")); + CHECK(!nvlist_exists_binary(nvl, "nvlist/bool")); + nvlist_add_bool(nvl, "nvlist/bool", true); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists(nvl, "nvlist/bool")); + CHECK(!nvlist_exists_null(nvl, "nvlist/bool")); + CHECK(nvlist_exists_bool(nvl, "nvlist/bool")); + CHECK(!nvlist_exists_number(nvl, "nvlist/bool")); + CHECK(!nvlist_exists_string(nvl, "nvlist/bool")); + CHECK(!nvlist_exists_nvlist(nvl, "nvlist/bool")); + CHECK(!nvlist_exists_descriptor(nvl, "nvlist/bool")); + CHECK(!nvlist_exists_binary(nvl, "nvlist/bool")); + + CHECK(!nvlist_exists(nvl, "nvlist/number")); + CHECK(!nvlist_exists_null(nvl, "nvlist/number")); + CHECK(!nvlist_exists_bool(nvl, "nvlist/number")); + CHECK(!nvlist_exists_number(nvl, "nvlist/number")); + CHECK(!nvlist_exists_string(nvl, "nvlist/number")); + CHECK(!nvlist_exists_nvlist(nvl, "nvlist/number")); + CHECK(!nvlist_exists_descriptor(nvl, "nvlist/number")); + CHECK(!nvlist_exists_binary(nvl, "nvlist/number")); + nvlist_add_number(nvl, "nvlist/number", 0); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists(nvl, "nvlist/number")); + CHECK(!nvlist_exists_null(nvl, "nvlist/number")); + CHECK(!nvlist_exists_bool(nvl, "nvlist/number")); + CHECK(nvlist_exists_number(nvl, "nvlist/number")); + CHECK(!nvlist_exists_string(nvl, "nvlist/number")); + CHECK(!nvlist_exists_nvlist(nvl, "nvlist/number")); + CHECK(!nvlist_exists_descriptor(nvl, "nvlist/number")); + CHECK(!nvlist_exists_binary(nvl, "nvlist/number")); + + CHECK(!nvlist_exists(nvl, "nvlist/string")); + CHECK(!nvlist_exists_null(nvl, "nvlist/string")); + CHECK(!nvlist_exists_bool(nvl, "nvlist/string")); + CHECK(!nvlist_exists_number(nvl, "nvlist/string")); + CHECK(!nvlist_exists_string(nvl, "nvlist/string")); + CHECK(!nvlist_exists_nvlist(nvl, "nvlist/string")); + CHECK(!nvlist_exists_descriptor(nvl, "nvlist/string")); + CHECK(!nvlist_exists_binary(nvl, "nvlist/string")); + nvlist_add_string(nvl, "nvlist/string", "test"); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists(nvl, "nvlist/string")); + CHECK(!nvlist_exists_null(nvl, "nvlist/string")); + CHECK(!nvlist_exists_bool(nvl, "nvlist/string")); + CHECK(!nvlist_exists_number(nvl, "nvlist/string")); + CHECK(nvlist_exists_string(nvl, "nvlist/string")); + CHECK(!nvlist_exists_nvlist(nvl, "nvlist/string")); + CHECK(!nvlist_exists_descriptor(nvl, "nvlist/string")); + CHECK(!nvlist_exists_binary(nvl, "nvlist/string")); + + CHECK(!nvlist_exists(nvl, "nvlist/nvlist")); + CHECK(!nvlist_exists_null(nvl, "nvlist/nvlist")); + CHECK(!nvlist_exists_bool(nvl, "nvlist/nvlist")); + CHECK(!nvlist_exists_number(nvl, "nvlist/nvlist")); + CHECK(!nvlist_exists_string(nvl, "nvlist/nvlist")); + CHECK(!nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + CHECK(!nvlist_exists_descriptor(nvl, "nvlist/nvlist")); + CHECK(!nvlist_exists_binary(nvl, "nvlist/nvlist")); + nvlist_add_nvlist(nvl, "nvlist/nvlist", nvl); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists(nvl, "nvlist/nvlist")); + CHECK(!nvlist_exists_null(nvl, "nvlist/nvlist")); + CHECK(!nvlist_exists_bool(nvl, "nvlist/nvlist")); + CHECK(!nvlist_exists_number(nvl, "nvlist/nvlist")); + CHECK(!nvlist_exists_string(nvl, "nvlist/nvlist")); + CHECK(nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + CHECK(!nvlist_exists_descriptor(nvl, "nvlist/nvlist")); + CHECK(!nvlist_exists_binary(nvl, "nvlist/nvlist")); + + CHECK(!nvlist_exists(nvl, "nvlist/descriptor")); + CHECK(!nvlist_exists_null(nvl, "nvlist/descriptor")); + CHECK(!nvlist_exists_bool(nvl, "nvlist/descriptor")); + CHECK(!nvlist_exists_number(nvl, "nvlist/descriptor")); + CHECK(!nvlist_exists_string(nvl, "nvlist/descriptor")); + CHECK(!nvlist_exists_nvlist(nvl, "nvlist/descriptor")); + CHECK(!nvlist_exists_descriptor(nvl, "nvlist/descriptor")); + CHECK(!nvlist_exists_binary(nvl, "nvlist/descriptor")); + nvlist_add_descriptor(nvl, "nvlist/descriptor", STDERR_FILENO); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists(nvl, "nvlist/descriptor")); + CHECK(!nvlist_exists_null(nvl, "nvlist/descriptor")); + CHECK(!nvlist_exists_bool(nvl, "nvlist/descriptor")); + CHECK(!nvlist_exists_number(nvl, "nvlist/descriptor")); + CHECK(!nvlist_exists_string(nvl, "nvlist/descriptor")); + CHECK(!nvlist_exists_nvlist(nvl, "nvlist/descriptor")); + CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor")); + CHECK(!nvlist_exists_binary(nvl, "nvlist/descriptor")); + + CHECK(!nvlist_exists(nvl, "nvlist/binary")); + CHECK(!nvlist_exists_null(nvl, "nvlist/binary")); + CHECK(!nvlist_exists_bool(nvl, "nvlist/binary")); + CHECK(!nvlist_exists_number(nvl, "nvlist/binary")); + CHECK(!nvlist_exists_string(nvl, "nvlist/binary")); + CHECK(!nvlist_exists_nvlist(nvl, "nvlist/binary")); + CHECK(!nvlist_exists_descriptor(nvl, "nvlist/binary")); + CHECK(!nvlist_exists_binary(nvl, "nvlist/binary")); + nvlist_add_binary(nvl, "nvlist/binary", "test", 4); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists(nvl, "nvlist/binary")); + CHECK(!nvlist_exists_null(nvl, "nvlist/binary")); + CHECK(!nvlist_exists_bool(nvl, "nvlist/binary")); + CHECK(!nvlist_exists_number(nvl, "nvlist/binary")); + CHECK(!nvlist_exists_string(nvl, "nvlist/binary")); + CHECK(!nvlist_exists_nvlist(nvl, "nvlist/binary")); + CHECK(!nvlist_exists_descriptor(nvl, "nvlist/binary")); + CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); + + CHECK(nvlist_exists(nvl, "nvlist/null")); + CHECK(nvlist_exists(nvl, "nvlist/bool")); + CHECK(nvlist_exists(nvl, "nvlist/number")); + CHECK(nvlist_exists(nvl, "nvlist/string")); + CHECK(nvlist_exists(nvl, "nvlist/nvlist")); + CHECK(nvlist_exists(nvl, "nvlist/descriptor")); + CHECK(nvlist_exists(nvl, "nvlist/binary")); + CHECK(nvlist_exists_null(nvl, "nvlist/null")); + CHECK(nvlist_exists_bool(nvl, "nvlist/bool")); + CHECK(nvlist_exists_number(nvl, "nvlist/number")); + CHECK(nvlist_exists_string(nvl, "nvlist/string")); + CHECK(nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor")); + CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); + + nvlist_free_null(nvl, "nvlist/null"); + CHECK(!nvlist_exists(nvl, "nvlist/null")); + CHECK(nvlist_exists(nvl, "nvlist/bool")); + CHECK(nvlist_exists(nvl, "nvlist/number")); + CHECK(nvlist_exists(nvl, "nvlist/string")); + CHECK(nvlist_exists(nvl, "nvlist/nvlist")); + CHECK(nvlist_exists(nvl, "nvlist/descriptor")); + CHECK(nvlist_exists(nvl, "nvlist/binary")); + CHECK(!nvlist_exists_null(nvl, "nvlist/null")); + CHECK(nvlist_exists_bool(nvl, "nvlist/bool")); + CHECK(nvlist_exists_number(nvl, "nvlist/number")); + CHECK(nvlist_exists_string(nvl, "nvlist/string")); + CHECK(nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor")); + CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); + + nvlist_free_bool(nvl, "nvlist/bool"); + CHECK(!nvlist_exists(nvl, "nvlist/null")); + CHECK(!nvlist_exists(nvl, "nvlist/bool")); + CHECK(nvlist_exists(nvl, "nvlist/number")); + CHECK(nvlist_exists(nvl, "nvlist/string")); + CHECK(nvlist_exists(nvl, "nvlist/nvlist")); + CHECK(nvlist_exists(nvl, "nvlist/descriptor")); + CHECK(nvlist_exists(nvl, "nvlist/binary")); + CHECK(!nvlist_exists_null(nvl, "nvlist/null")); + CHECK(!nvlist_exists_bool(nvl, "nvlist/bool")); + CHECK(nvlist_exists_number(nvl, "nvlist/number")); + CHECK(nvlist_exists_string(nvl, "nvlist/string")); + CHECK(nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor")); + CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); + + nvlist_free_number(nvl, "nvlist/number"); + CHECK(!nvlist_exists(nvl, "nvlist/null")); + CHECK(!nvlist_exists(nvl, "nvlist/bool")); + CHECK(!nvlist_exists(nvl, "nvlist/number")); + CHECK(nvlist_exists(nvl, "nvlist/string")); + CHECK(nvlist_exists(nvl, "nvlist/nvlist")); + CHECK(nvlist_exists(nvl, "nvlist/descriptor")); + CHECK(nvlist_exists(nvl, "nvlist/binary")); + CHECK(!nvlist_exists_null(nvl, "nvlist/null")); + CHECK(!nvlist_exists_bool(nvl, "nvlist/bool")); + CHECK(!nvlist_exists_number(nvl, "nvlist/number")); + CHECK(nvlist_exists_string(nvl, "nvlist/string")); + CHECK(nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor")); + CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); + + nvlist_free_string(nvl, "nvlist/string"); + CHECK(!nvlist_exists(nvl, "nvlist/null")); + CHECK(!nvlist_exists(nvl, "nvlist/bool")); + CHECK(!nvlist_exists(nvl, "nvlist/number")); + CHECK(!nvlist_exists(nvl, "nvlist/string")); + CHECK(nvlist_exists(nvl, "nvlist/nvlist")); + CHECK(nvlist_exists(nvl, "nvlist/descriptor")); + CHECK(nvlist_exists(nvl, "nvlist/binary")); + CHECK(!nvlist_exists_null(nvl, "nvlist/null")); + CHECK(!nvlist_exists_bool(nvl, "nvlist/bool")); + CHECK(!nvlist_exists_number(nvl, "nvlist/number")); + CHECK(!nvlist_exists_string(nvl, "nvlist/string")); + CHECK(nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor")); + CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); + + nvlist_free_nvlist(nvl, "nvlist/nvlist"); + CHECK(!nvlist_exists(nvl, "nvlist/null")); + CHECK(!nvlist_exists(nvl, "nvlist/bool")); + CHECK(!nvlist_exists(nvl, "nvlist/number")); + CHECK(!nvlist_exists(nvl, "nvlist/string")); + CHECK(!nvlist_exists(nvl, "nvlist/nvlist")); + CHECK(nvlist_exists(nvl, "nvlist/descriptor")); + CHECK(nvlist_exists(nvl, "nvlist/binary")); + CHECK(!nvlist_exists_null(nvl, "nvlist/null")); + CHECK(!nvlist_exists_bool(nvl, "nvlist/bool")); + CHECK(!nvlist_exists_number(nvl, "nvlist/number")); + CHECK(!nvlist_exists_string(nvl, "nvlist/string")); + CHECK(!nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor")); + CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); + + nvlist_free_descriptor(nvl, "nvlist/descriptor"); + CHECK(!nvlist_exists(nvl, "nvlist/null")); + CHECK(!nvlist_exists(nvl, "nvlist/bool")); + CHECK(!nvlist_exists(nvl, "nvlist/number")); + CHECK(!nvlist_exists(nvl, "nvlist/string")); + CHECK(!nvlist_exists(nvl, "nvlist/nvlist")); + CHECK(!nvlist_exists(nvl, "nvlist/descriptor")); + CHECK(nvlist_exists(nvl, "nvlist/binary")); + CHECK(!nvlist_exists_null(nvl, "nvlist/null")); + CHECK(!nvlist_exists_bool(nvl, "nvlist/bool")); + CHECK(!nvlist_exists_number(nvl, "nvlist/number")); + CHECK(!nvlist_exists_string(nvl, "nvlist/string")); + CHECK(!nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + CHECK(!nvlist_exists_descriptor(nvl, "nvlist/descriptor")); + CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); + + nvlist_free_binary(nvl, "nvlist/binary"); + CHECK(!nvlist_exists(nvl, "nvlist/null")); + CHECK(!nvlist_exists(nvl, "nvlist/bool")); + CHECK(!nvlist_exists(nvl, "nvlist/number")); + CHECK(!nvlist_exists(nvl, "nvlist/string")); + CHECK(!nvlist_exists(nvl, "nvlist/nvlist")); + CHECK(!nvlist_exists(nvl, "nvlist/descriptor")); + CHECK(!nvlist_exists(nvl, "nvlist/binary")); + CHECK(!nvlist_exists_null(nvl, "nvlist/null")); + CHECK(!nvlist_exists_bool(nvl, "nvlist/bool")); + CHECK(!nvlist_exists_number(nvl, "nvlist/number")); + CHECK(!nvlist_exists_string(nvl, "nvlist/string")); + CHECK(!nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + CHECK(!nvlist_exists_descriptor(nvl, "nvlist/descriptor")); + CHECK(!nvlist_exists_binary(nvl, "nvlist/binary")); + + CHECK(nvlist_empty(nvl)); + + nvlist_destroy(nvl); + + return (0); +} Index: lib/libnv/tests/nvlist_free_test.c =================================================================== --- /dev/null +++ lib/libnv/tests/nvlist_free_test.c @@ -0,0 +1,221 @@ +/*- + * Copyright (c) 2013 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Pawel Jakub Dawidek under sponsorship from + * the FreeBSD Foundation. + * + * 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 AUTHORS 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 AUTHORS 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: head/lib/libnv/tests/nvlist_free_test.c 285063 2015-07-02 21:58:10Z oshogbo $ + */ + +#include + +#include +#include + +static int ntest = 1; + +#define CHECK(expr) do { \ + if ((expr)) \ + printf("ok # %d %s:%u\n", ntest, __FILE__, __LINE__); \ + else \ + printf("not ok # %d %s:%u\n", ntest, __FILE__, __LINE__);\ + ntest++; \ +} while (0) + +int +main(void) +{ + nvlist_t *nvl; + + printf("1..114\n"); + + nvl = nvlist_create(0); + + nvlist_add_null(nvl, "nvlist/null"); + nvlist_add_bool(nvl, "nvlist/bool", true); + nvlist_add_number(nvl, "nvlist/number", 0); + nvlist_add_string(nvl, "nvlist/string", "test"); + nvlist_add_nvlist(nvl, "nvlist/nvlist", nvl); + nvlist_add_descriptor(nvl, "nvlist/descriptor", STDERR_FILENO); + nvlist_add_binary(nvl, "nvlist/binary", "test", 4); + + CHECK(nvlist_exists_null(nvl, "nvlist/null")); + CHECK(nvlist_exists_bool(nvl, "nvlist/bool")); + CHECK(nvlist_exists_number(nvl, "nvlist/number")); + CHECK(nvlist_exists_string(nvl, "nvlist/string")); + CHECK(nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor")); + CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); + + nvlist_free_null(nvl, "nvlist/null"); + CHECK(!nvlist_exists_null(nvl, "nvlist/null")); + CHECK(nvlist_exists_bool(nvl, "nvlist/bool")); + CHECK(nvlist_exists_number(nvl, "nvlist/number")); + CHECK(nvlist_exists_string(nvl, "nvlist/string")); + CHECK(nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor")); + CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); + + nvlist_free_bool(nvl, "nvlist/bool"); + CHECK(!nvlist_exists_null(nvl, "nvlist/null")); + CHECK(!nvlist_exists_bool(nvl, "nvlist/bool")); + CHECK(nvlist_exists_number(nvl, "nvlist/number")); + CHECK(nvlist_exists_string(nvl, "nvlist/string")); + CHECK(nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor")); + CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); + + nvlist_free_number(nvl, "nvlist/number"); + CHECK(!nvlist_exists_null(nvl, "nvlist/null")); + CHECK(!nvlist_exists_bool(nvl, "nvlist/bool")); + CHECK(!nvlist_exists_number(nvl, "nvlist/number")); + CHECK(nvlist_exists_string(nvl, "nvlist/string")); + CHECK(nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor")); + CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); + + nvlist_free_string(nvl, "nvlist/string"); + CHECK(!nvlist_exists_null(nvl, "nvlist/null")); + CHECK(!nvlist_exists_bool(nvl, "nvlist/bool")); + CHECK(!nvlist_exists_number(nvl, "nvlist/number")); + CHECK(!nvlist_exists_string(nvl, "nvlist/string")); + CHECK(nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor")); + CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); + + nvlist_free_nvlist(nvl, "nvlist/nvlist"); + CHECK(!nvlist_exists_null(nvl, "nvlist/null")); + CHECK(!nvlist_exists_bool(nvl, "nvlist/bool")); + CHECK(!nvlist_exists_number(nvl, "nvlist/number")); + CHECK(!nvlist_exists_string(nvl, "nvlist/string")); + CHECK(!nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor")); + CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); + + nvlist_free_descriptor(nvl, "nvlist/descriptor"); + CHECK(!nvlist_exists_null(nvl, "nvlist/null")); + CHECK(!nvlist_exists_bool(nvl, "nvlist/bool")); + CHECK(!nvlist_exists_number(nvl, "nvlist/number")); + CHECK(!nvlist_exists_string(nvl, "nvlist/string")); + CHECK(!nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + CHECK(!nvlist_exists_descriptor(nvl, "nvlist/descriptor")); + CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); + + nvlist_free_binary(nvl, "nvlist/binary"); + CHECK(!nvlist_exists_null(nvl, "nvlist/null")); + CHECK(!nvlist_exists_bool(nvl, "nvlist/bool")); + CHECK(!nvlist_exists_number(nvl, "nvlist/number")); + CHECK(!nvlist_exists_string(nvl, "nvlist/string")); + CHECK(!nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + CHECK(!nvlist_exists_descriptor(nvl, "nvlist/descriptor")); + CHECK(!nvlist_exists_binary(nvl, "nvlist/binary")); + + CHECK(nvlist_empty(nvl)); + + nvlist_add_null(nvl, "nvlist/null"); + nvlist_add_bool(nvl, "nvlist/bool", true); + nvlist_add_number(nvl, "nvlist/number", 0); + nvlist_add_string(nvl, "nvlist/string", "test"); + nvlist_add_nvlist(nvl, "nvlist/nvlist", nvl); + nvlist_add_descriptor(nvl, "nvlist/descriptor", STDERR_FILENO); + nvlist_add_binary(nvl, "nvlist/binary", "test", 4); + + CHECK(nvlist_exists_null(nvl, "nvlist/null")); + CHECK(nvlist_exists_bool(nvl, "nvlist/bool")); + CHECK(nvlist_exists_number(nvl, "nvlist/number")); + CHECK(nvlist_exists_string(nvl, "nvlist/string")); + CHECK(nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor")); + CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); + + nvlist_free(nvl, "nvlist/null"); + CHECK(!nvlist_exists_null(nvl, "nvlist/null")); + CHECK(nvlist_exists_bool(nvl, "nvlist/bool")); + CHECK(nvlist_exists_number(nvl, "nvlist/number")); + CHECK(nvlist_exists_string(nvl, "nvlist/string")); + CHECK(nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor")); + CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); + + nvlist_free(nvl, "nvlist/bool"); + CHECK(!nvlist_exists_null(nvl, "nvlist/null")); + CHECK(!nvlist_exists_bool(nvl, "nvlist/bool")); + CHECK(nvlist_exists_number(nvl, "nvlist/number")); + CHECK(nvlist_exists_string(nvl, "nvlist/string")); + CHECK(nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor")); + CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); + + nvlist_free(nvl, "nvlist/number"); + CHECK(!nvlist_exists_null(nvl, "nvlist/null")); + CHECK(!nvlist_exists_bool(nvl, "nvlist/bool")); + CHECK(!nvlist_exists_number(nvl, "nvlist/number")); + CHECK(nvlist_exists_string(nvl, "nvlist/string")); + CHECK(nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor")); + CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); + + nvlist_free(nvl, "nvlist/string"); + CHECK(!nvlist_exists_null(nvl, "nvlist/null")); + CHECK(!nvlist_exists_bool(nvl, "nvlist/bool")); + CHECK(!nvlist_exists_number(nvl, "nvlist/number")); + CHECK(!nvlist_exists_string(nvl, "nvlist/string")); + CHECK(nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor")); + CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); + + nvlist_free(nvl, "nvlist/nvlist"); + CHECK(!nvlist_exists_null(nvl, "nvlist/null")); + CHECK(!nvlist_exists_bool(nvl, "nvlist/bool")); + CHECK(!nvlist_exists_number(nvl, "nvlist/number")); + CHECK(!nvlist_exists_string(nvl, "nvlist/string")); + CHECK(!nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor")); + CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); + + nvlist_free(nvl, "nvlist/descriptor"); + CHECK(!nvlist_exists_null(nvl, "nvlist/null")); + CHECK(!nvlist_exists_bool(nvl, "nvlist/bool")); + CHECK(!nvlist_exists_number(nvl, "nvlist/number")); + CHECK(!nvlist_exists_string(nvl, "nvlist/string")); + CHECK(!nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + CHECK(!nvlist_exists_descriptor(nvl, "nvlist/descriptor")); + CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); + + nvlist_free(nvl, "nvlist/binary"); + CHECK(!nvlist_exists_null(nvl, "nvlist/null")); + CHECK(!nvlist_exists_bool(nvl, "nvlist/bool")); + CHECK(!nvlist_exists_number(nvl, "nvlist/number")); + CHECK(!nvlist_exists_string(nvl, "nvlist/string")); + CHECK(!nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + CHECK(!nvlist_exists_descriptor(nvl, "nvlist/descriptor")); + CHECK(!nvlist_exists_binary(nvl, "nvlist/binary")); + + CHECK(nvlist_empty(nvl)); + + nvlist_destroy(nvl); + + return (0); +} Index: lib/libnv/tests/nvlist_get_test.c =================================================================== --- /dev/null +++ lib/libnv/tests/nvlist_get_test.c @@ -0,0 +1,181 @@ +/*- + * Copyright (c) 2013 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Pawel Jakub Dawidek under sponsorship from + * the FreeBSD Foundation. + * + * 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 AUTHORS 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 AUTHORS 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: head/lib/libnv/tests/nvlist_get_test.c 285063 2015-07-02 21:58:10Z oshogbo $ + */ +#include + +#include +#include +#include +#include +#include + +static int ntest = 1; + +#define CHECK(expr) do { \ + if ((expr)) \ + printf("ok # %d %s:%u\n", ntest, __FILE__, __LINE__); \ + else \ + printf("not ok # %d %s:%u\n", ntest, __FILE__, __LINE__);\ + ntest++; \ +} while (0) + +#define fd_is_valid(fd) (fcntl((fd), F_GETFL) != -1 || errno != EBADF) + +int +main(void) +{ + const nvlist_t *cnvl; + nvlist_t *nvl; + size_t size; + + printf("1..83\n"); + + nvl = nvlist_create(0); + + CHECK(!nvlist_exists_bool(nvl, "nvlist/bool/true")); + nvlist_add_bool(nvl, "nvlist/bool/true", true); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_get_bool(nvl, "nvlist/bool/true") == true); + + CHECK(!nvlist_exists_bool(nvl, "nvlist/bool/false")); + nvlist_add_bool(nvl, "nvlist/bool/false", false); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_get_bool(nvl, "nvlist/bool/false") == false); + + CHECK(!nvlist_exists_number(nvl, "nvlist/number/0")); + nvlist_add_number(nvl, "nvlist/number/0", 0); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_get_number(nvl, "nvlist/number/0") == 0); + + CHECK(!nvlist_exists_number(nvl, "nvlist/number/1")); + nvlist_add_number(nvl, "nvlist/number/1", 1); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_get_number(nvl, "nvlist/number/1") == 1); + + CHECK(!nvlist_exists_number(nvl, "nvlist/number/-1")); + nvlist_add_number(nvl, "nvlist/number/-1", -1); + CHECK(nvlist_error(nvl) == 0); + CHECK((int)nvlist_get_number(nvl, "nvlist/number/-1") == -1); + + CHECK(!nvlist_exists_number(nvl, "nvlist/number/UINT64_MAX")); + nvlist_add_number(nvl, "nvlist/number/UINT64_MAX", UINT64_MAX); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_get_number(nvl, "nvlist/number/UINT64_MAX") == UINT64_MAX); + + CHECK(!nvlist_exists_number(nvl, "nvlist/number/INT64_MIN")); + nvlist_add_number(nvl, "nvlist/number/INT64_MIN", INT64_MIN); + CHECK(nvlist_error(nvl) == 0); + CHECK((int64_t)nvlist_get_number(nvl, "nvlist/number/INT64_MIN") == INT64_MIN); + + CHECK(!nvlist_exists_number(nvl, "nvlist/number/INT64_MAX")); + nvlist_add_number(nvl, "nvlist/number/INT64_MAX", INT64_MAX); + CHECK(nvlist_error(nvl) == 0); + CHECK((int64_t)nvlist_get_number(nvl, "nvlist/number/INT64_MAX") == INT64_MAX); + + CHECK(!nvlist_exists_string(nvl, "nvlist/string/")); + nvlist_add_string(nvl, "nvlist/string/", ""); + CHECK(nvlist_error(nvl) == 0); + CHECK(strcmp(nvlist_get_string(nvl, "nvlist/string/"), "") == 0); + + CHECK(!nvlist_exists_string(nvl, "nvlist/string/x")); + nvlist_add_string(nvl, "nvlist/string/x", "x"); + CHECK(nvlist_error(nvl) == 0); + CHECK(strcmp(nvlist_get_string(nvl, "nvlist/string/x"), "x") == 0); + + CHECK(!nvlist_exists_string(nvl, "nvlist/string/abcdefghijklmnopqrstuvwxyz")); + nvlist_add_string(nvl, "nvlist/string/abcdefghijklmnopqrstuvwxyz", "abcdefghijklmnopqrstuvwxyz"); + CHECK(nvlist_error(nvl) == 0); + CHECK(strcmp(nvlist_get_string(nvl, "nvlist/string/abcdefghijklmnopqrstuvwxyz"), "abcdefghijklmnopqrstuvwxyz") == 0); + + CHECK(!nvlist_exists_descriptor(nvl, "nvlist/descriptor/STDERR_FILENO")); + nvlist_add_descriptor(nvl, "nvlist/descriptor/STDERR_FILENO", STDERR_FILENO); + CHECK(nvlist_error(nvl) == 0); + CHECK(fd_is_valid(nvlist_get_descriptor(nvl, "nvlist/descriptor/STDERR_FILENO"))); + + CHECK(!nvlist_exists_binary(nvl, "nvlist/binary/x")); + nvlist_add_binary(nvl, "nvlist/binary/x", "x", 1); + CHECK(nvlist_error(nvl) == 0); + CHECK(memcmp(nvlist_get_binary(nvl, "nvlist/binary/x", NULL), "x", 1) == 0); + CHECK(memcmp(nvlist_get_binary(nvl, "nvlist/binary/x", &size), "x", 1) == 0); + CHECK(size == 1); + + CHECK(!nvlist_exists_binary(nvl, "nvlist/binary/abcdefghijklmnopqrstuvwxyz")); + nvlist_add_binary(nvl, "nvlist/binary/abcdefghijklmnopqrstuvwxyz", "abcdefghijklmnopqrstuvwxyz", sizeof("abcdefghijklmnopqrstuvwxyz")); + CHECK(nvlist_error(nvl) == 0); + CHECK(memcmp(nvlist_get_binary(nvl, "nvlist/binary/abcdefghijklmnopqrstuvwxyz", NULL), "abcdefghijklmnopqrstuvwxyz", sizeof("abcdefghijklmnopqrstuvwxyz")) == 0); + CHECK(memcmp(nvlist_get_binary(nvl, "nvlist/binary/abcdefghijklmnopqrstuvwxyz", &size), "abcdefghijklmnopqrstuvwxyz", sizeof("abcdefghijklmnopqrstuvwxyz")) == 0); + CHECK(size == sizeof("abcdefghijklmnopqrstuvwxyz")); + + CHECK(!nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + nvlist_add_nvlist(nvl, "nvlist/nvlist", nvl); + CHECK(nvlist_error(nvl) == 0); + cnvl = nvlist_get_nvlist(nvl, "nvlist/nvlist"); + CHECK(nvlist_get_bool(cnvl, "nvlist/bool/true") == true); + CHECK(nvlist_get_bool(cnvl, "nvlist/bool/false") == false); + CHECK(nvlist_get_number(cnvl, "nvlist/number/0") == 0); + CHECK(nvlist_get_number(cnvl, "nvlist/number/1") == 1); + CHECK((int)nvlist_get_number(cnvl, "nvlist/number/-1") == -1); + CHECK(nvlist_get_number(cnvl, "nvlist/number/UINT64_MAX") == UINT64_MAX); + CHECK((int64_t)nvlist_get_number(cnvl, "nvlist/number/INT64_MIN") == INT64_MIN); + CHECK((int64_t)nvlist_get_number(cnvl, "nvlist/number/INT64_MAX") == INT64_MAX); + CHECK(strcmp(nvlist_get_string(cnvl, "nvlist/string/"), "") == 0); + CHECK(strcmp(nvlist_get_string(cnvl, "nvlist/string/x"), "x") == 0); + CHECK(strcmp(nvlist_get_string(cnvl, "nvlist/string/abcdefghijklmnopqrstuvwxyz"), "abcdefghijklmnopqrstuvwxyz") == 0); + /* TODO */ + CHECK(memcmp(nvlist_get_binary(cnvl, "nvlist/binary/x", NULL), "x", 1) == 0); + CHECK(memcmp(nvlist_get_binary(cnvl, "nvlist/binary/x", &size), "x", 1) == 0); + CHECK(size == 1); + CHECK(memcmp(nvlist_get_binary(cnvl, "nvlist/binary/abcdefghijklmnopqrstuvwxyz", NULL), "abcdefghijklmnopqrstuvwxyz", sizeof("abcdefghijklmnopqrstuvwxyz")) == 0); + CHECK(memcmp(nvlist_get_binary(cnvl, "nvlist/binary/abcdefghijklmnopqrstuvwxyz", &size), "abcdefghijklmnopqrstuvwxyz", sizeof("abcdefghijklmnopqrstuvwxyz")) == 0); + CHECK(size == sizeof("abcdefghijklmnopqrstuvwxyz")); + + CHECK(nvlist_get_bool(nvl, "nvlist/bool/true") == true); + CHECK(nvlist_get_bool(nvl, "nvlist/bool/false") == false); + CHECK(nvlist_get_number(nvl, "nvlist/number/0") == 0); + CHECK(nvlist_get_number(nvl, "nvlist/number/1") == 1); + CHECK((int)nvlist_get_number(nvl, "nvlist/number/-1") == -1); + CHECK(nvlist_get_number(nvl, "nvlist/number/UINT64_MAX") == UINT64_MAX); + CHECK((int64_t)nvlist_get_number(nvl, "nvlist/number/INT64_MIN") == INT64_MIN); + CHECK((int64_t)nvlist_get_number(nvl, "nvlist/number/INT64_MAX") == INT64_MAX); + CHECK(strcmp(nvlist_get_string(nvl, "nvlist/string/"), "") == 0); + CHECK(strcmp(nvlist_get_string(nvl, "nvlist/string/x"), "x") == 0); + CHECK(strcmp(nvlist_get_string(nvl, "nvlist/string/abcdefghijklmnopqrstuvwxyz"), "abcdefghijklmnopqrstuvwxyz") == 0); + CHECK(fd_is_valid(nvlist_get_descriptor(nvl, "nvlist/descriptor/STDERR_FILENO"))); + CHECK(memcmp(nvlist_get_binary(nvl, "nvlist/binary/x", NULL), "x", 1) == 0); + CHECK(memcmp(nvlist_get_binary(nvl, "nvlist/binary/x", &size), "x", 1) == 0); + CHECK(size == 1); + CHECK(memcmp(nvlist_get_binary(nvl, "nvlist/binary/abcdefghijklmnopqrstuvwxyz", NULL), "abcdefghijklmnopqrstuvwxyz", sizeof("abcdefghijklmnopqrstuvwxyz")) == 0); + CHECK(memcmp(nvlist_get_binary(nvl, "nvlist/binary/abcdefghijklmnopqrstuvwxyz", &size), "abcdefghijklmnopqrstuvwxyz", sizeof("abcdefghijklmnopqrstuvwxyz")) == 0); + CHECK(size == sizeof("abcdefghijklmnopqrstuvwxyz")); + + nvlist_destroy(nvl); + + return (0); +} Index: lib/libnv/tests/nvlist_move_test.c =================================================================== --- /dev/null +++ lib/libnv/tests/nvlist_move_test.c @@ -0,0 +1,161 @@ +/*- + * Copyright (c) 2013 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Pawel Jakub Dawidek under sponsorship from + * the FreeBSD Foundation. + * + * 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 AUTHORS 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 AUTHORS 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: head/lib/libnv/tests/nvlist_move_test.c 285063 2015-07-02 21:58:10Z oshogbo $ + */ + +#include + +#include +#include +#include +#include +#include + +static int ntest = 1; + +#define CHECK(expr) do { \ + if ((expr)) \ + printf("ok # %d %s:%u\n", ntest, __FILE__, __LINE__); \ + else \ + printf("not ok # %d %s:%u\n", ntest, __FILE__, __LINE__);\ + ntest++; \ +} while (0) + +int +main(void) +{ + const nvlist_t *cnvl; + nvlist_t *nvl; + void *ptr; + size_t size; + int fd; + + printf("1..52\n"); + + nvl = nvlist_create(0); + + CHECK(!nvlist_exists_string(nvl, "nvlist/string/")); + ptr = strdup(""); + CHECK(ptr != NULL); + nvlist_move_string(nvl, "nvlist/string/", ptr); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists_string(nvl, "nvlist/string/")); + CHECK(ptr == nvlist_get_string(nvl, "nvlist/string/")); + + CHECK(!nvlist_exists_string(nvl, "nvlist/string/x")); + ptr = strdup("x"); + CHECK(ptr != NULL); + nvlist_move_string(nvl, "nvlist/string/x", ptr); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists_string(nvl, "nvlist/string/x")); + CHECK(ptr == nvlist_get_string(nvl, "nvlist/string/x")); + + CHECK(!nvlist_exists_string(nvl, + "nvlist/string/abcdefghijklmnopqrstuvwxyz")); + ptr = strdup("abcdefghijklmnopqrstuvwxyz"); + CHECK(ptr != NULL); + nvlist_move_string(nvl, "nvlist/string/abcdefghijklmnopqrstuvwxyz", + ptr); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists_string(nvl, + "nvlist/string/abcdefghijklmnopqrstuvwxyz")); + CHECK(ptr == + nvlist_get_string(nvl, "nvlist/string/abcdefghijklmnopqrstuvwxyz")); + + CHECK(!nvlist_exists_descriptor(nvl, + "nvlist/descriptor/STDERR_FILENO")); + fd = dup(STDERR_FILENO); + CHECK(fd >= 0); + nvlist_move_descriptor(nvl, "nvlist/descriptor/STDERR_FILENO", fd); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor/STDERR_FILENO")); + CHECK(fd == + nvlist_get_descriptor(nvl, "nvlist/descriptor/STDERR_FILENO")); + + CHECK(!nvlist_exists_binary(nvl, "nvlist/binary/x")); + ptr = malloc(1); + CHECK(ptr != NULL); + memcpy(ptr, "x", 1); + nvlist_move_binary(nvl, "nvlist/binary/x", ptr, 1); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists_binary(nvl, "nvlist/binary/x")); + CHECK(ptr == nvlist_get_binary(nvl, "nvlist/binary/x", NULL)); + CHECK(ptr == nvlist_get_binary(nvl, "nvlist/binary/x", &size)); + CHECK(size == 1); + + CHECK(!nvlist_exists_binary(nvl, + "nvlist/binary/abcdefghijklmnopqrstuvwxyz")); + ptr = malloc(sizeof("abcdefghijklmnopqrstuvwxyz")); + CHECK(ptr != NULL); + memcpy(ptr, "abcdefghijklmnopqrstuvwxyz", + sizeof("abcdefghijklmnopqrstuvwxyz")); + nvlist_move_binary(nvl, "nvlist/binary/abcdefghijklmnopqrstuvwxyz", + ptr, sizeof("abcdefghijklmnopqrstuvwxyz")); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists_binary(nvl, + "nvlist/binary/abcdefghijklmnopqrstuvwxyz")); + CHECK(ptr == nvlist_get_binary(nvl, + "nvlist/binary/abcdefghijklmnopqrstuvwxyz", NULL)); + CHECK(ptr == nvlist_get_binary(nvl, + "nvlist/binary/abcdefghijklmnopqrstuvwxyz", &size)); + CHECK(size == sizeof("abcdefghijklmnopqrstuvwxyz")); + + CHECK(!nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + ptr = nvlist_clone(nvl); + CHECK(ptr != NULL); + nvlist_move_nvlist(nvl, "nvlist/nvlist", ptr); + CHECK(nvlist_error(nvl) == 0); + CHECK(nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + CHECK(ptr == nvlist_get_nvlist(nvl, "nvlist/nvlist")); + + CHECK(nvlist_exists_string(nvl, "nvlist/string/")); + CHECK(nvlist_exists_string(nvl, "nvlist/string/x")); + CHECK(nvlist_exists_string(nvl, + "nvlist/string/abcdefghijklmnopqrstuvwxyz")); + CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor/STDERR_FILENO")); + CHECK(nvlist_exists_binary(nvl, "nvlist/binary/x")); + CHECK(nvlist_exists_binary(nvl, + "nvlist/binary/abcdefghijklmnopqrstuvwxyz")); + CHECK(nvlist_exists_nvlist(nvl, "nvlist/nvlist")); + + cnvl = nvlist_get_nvlist(nvl, "nvlist/nvlist"); + CHECK(nvlist_exists_string(cnvl, "nvlist/string/")); + CHECK(nvlist_exists_string(cnvl, "nvlist/string/x")); + CHECK(nvlist_exists_string(cnvl, + "nvlist/string/abcdefghijklmnopqrstuvwxyz")); + CHECK(nvlist_exists_descriptor(cnvl, + "nvlist/descriptor/STDERR_FILENO")); + CHECK(nvlist_exists_binary(cnvl, "nvlist/binary/x")); + CHECK(nvlist_exists_binary(cnvl, + "nvlist/binary/abcdefghijklmnopqrstuvwxyz")); + + nvlist_destroy(nvl); + + return (0); +} Index: lib/libnv/tests/nvlist_send_recv_test.c =================================================================== --- /dev/null +++ lib/libnv/tests/nvlist_send_recv_test.c @@ -0,0 +1,341 @@ +/*- + * Copyright (c) 2013 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Pawel Jakub Dawidek under sponsorship from + * the FreeBSD Foundation. + * + * 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 AUTHORS 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 AUTHORS 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: head/lib/libnv/tests/nvlist_send_recv_test.c 285063 2015-07-02 21:58:10Z oshogbo $ + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +static int ntest = 1; + +#define CHECK(expr) do { \ + if ((expr)) \ + printf("ok # %d %s:%u\n", ntest, __FILE__, __LINE__); \ + else \ + printf("not ok # %d %s:%u\n", ntest, __FILE__, __LINE__);\ + ntest++; \ +} while (0) + +#define fd_is_valid(fd) (fcntl((fd), F_GETFL) != -1 || errno != EBADF) + +static void +child(int sock) +{ + nvlist_t *nvl; + nvlist_t *empty; + + nvl = nvlist_create(0); + empty = nvlist_create(0); + + nvlist_add_bool(nvl, "nvlist/bool/true", true); + nvlist_add_bool(nvl, "nvlist/bool/false", false); + nvlist_add_number(nvl, "nvlist/number/0", 0); + nvlist_add_number(nvl, "nvlist/number/1", 1); + nvlist_add_number(nvl, "nvlist/number/-1", -1); + nvlist_add_number(nvl, "nvlist/number/UINT64_MAX", UINT64_MAX); + nvlist_add_number(nvl, "nvlist/number/INT64_MIN", INT64_MIN); + nvlist_add_number(nvl, "nvlist/number/INT64_MAX", INT64_MAX); + nvlist_add_string(nvl, "nvlist/string/", ""); + nvlist_add_string(nvl, "nvlist/string/x", "x"); + nvlist_add_string(nvl, "nvlist/string/abcdefghijklmnopqrstuvwxyz", "abcdefghijklmnopqrstuvwxyz"); + nvlist_add_descriptor(nvl, "nvlist/descriptor/STDERR_FILENO", STDERR_FILENO); + nvlist_add_binary(nvl, "nvlist/binary/x", "x", 1); + nvlist_add_binary(nvl, "nvlist/binary/abcdefghijklmnopqrstuvwxyz", "abcdefghijklmnopqrstuvwxyz", sizeof("abcdefghijklmnopqrstuvwxyz")); + nvlist_move_nvlist(nvl, "nvlist/nvlist/empty", empty); + nvlist_add_nvlist(nvl, "nvlist/nvlist", nvl); + + nvlist_send(sock, nvl); + + nvlist_destroy(nvl); +} + +static void +parent(int sock) +{ + nvlist_t *nvl; + const nvlist_t *cnvl, *empty; + const char *name, *cname; + void *cookie, *ccookie; + int type, ctype; + size_t size; + + nvl = nvlist_recv(sock, 0); + CHECK(nvlist_error(nvl) == 0); + if (nvlist_error(nvl) != 0) + err(1, "nvlist_recv() failed"); + + cookie = NULL; + + name = nvlist_next(nvl, &type, &cookie); + CHECK(name != NULL); + CHECK(type == NV_TYPE_BOOL); + CHECK(strcmp(name, "nvlist/bool/true") == 0); + CHECK(nvlist_get_bool(nvl, name) == true); + + name = nvlist_next(nvl, &type, &cookie); + CHECK(name != NULL); + CHECK(type == NV_TYPE_BOOL); + CHECK(strcmp(name, "nvlist/bool/false") == 0); + CHECK(nvlist_get_bool(nvl, name) == false); + + name = nvlist_next(nvl, &type, &cookie); + CHECK(name != NULL); + CHECK(type == NV_TYPE_NUMBER); + CHECK(strcmp(name, "nvlist/number/0") == 0); + CHECK(nvlist_get_number(nvl, name) == 0); + + name = nvlist_next(nvl, &type, &cookie); + CHECK(name != NULL); + CHECK(type == NV_TYPE_NUMBER); + CHECK(strcmp(name, "nvlist/number/1") == 0); + CHECK(nvlist_get_number(nvl, name) == 1); + + name = nvlist_next(nvl, &type, &cookie); + CHECK(name != NULL); + CHECK(type == NV_TYPE_NUMBER); + CHECK(strcmp(name, "nvlist/number/-1") == 0); + CHECK((int)nvlist_get_number(nvl, name) == -1); + + name = nvlist_next(nvl, &type, &cookie); + CHECK(name != NULL); + CHECK(type == NV_TYPE_NUMBER); + CHECK(strcmp(name, "nvlist/number/UINT64_MAX") == 0); + CHECK(nvlist_get_number(nvl, name) == UINT64_MAX); + + name = nvlist_next(nvl, &type, &cookie); + CHECK(name != NULL); + CHECK(type == NV_TYPE_NUMBER); + CHECK(strcmp(name, "nvlist/number/INT64_MIN") == 0); + CHECK((int64_t)nvlist_get_number(nvl, name) == INT64_MIN); + + name = nvlist_next(nvl, &type, &cookie); + CHECK(name != NULL); + CHECK(type == NV_TYPE_NUMBER); + CHECK(strcmp(name, "nvlist/number/INT64_MAX") == 0); + CHECK((int64_t)nvlist_get_number(nvl, name) == INT64_MAX); + + name = nvlist_next(nvl, &type, &cookie); + CHECK(name != NULL); + CHECK(type == NV_TYPE_STRING); + CHECK(strcmp(name, "nvlist/string/") == 0); + CHECK(strcmp(nvlist_get_string(nvl, name), "") == 0); + + name = nvlist_next(nvl, &type, &cookie); + CHECK(name != NULL); + CHECK(type == NV_TYPE_STRING); + CHECK(strcmp(name, "nvlist/string/x") == 0); + CHECK(strcmp(nvlist_get_string(nvl, name), "x") == 0); + + name = nvlist_next(nvl, &type, &cookie); + CHECK(name != NULL); + CHECK(type == NV_TYPE_STRING); + CHECK(strcmp(name, "nvlist/string/abcdefghijklmnopqrstuvwxyz") == 0); + CHECK(strcmp(nvlist_get_string(nvl, name), "abcdefghijklmnopqrstuvwxyz") == 0); + + name = nvlist_next(nvl, &type, &cookie); + CHECK(name != NULL); + CHECK(type == NV_TYPE_DESCRIPTOR); + CHECK(strcmp(name, "nvlist/descriptor/STDERR_FILENO") == 0); + CHECK(fd_is_valid(nvlist_get_descriptor(nvl, name))); + + name = nvlist_next(nvl, &type, &cookie); + CHECK(name != NULL); + CHECK(type == NV_TYPE_BINARY); + CHECK(strcmp(name, "nvlist/binary/x") == 0); + CHECK(memcmp(nvlist_get_binary(nvl, name, NULL), "x", 1) == 0); + CHECK(memcmp(nvlist_get_binary(nvl, name, &size), "x", 1) == 0); + CHECK(size == 1); + + name = nvlist_next(nvl, &type, &cookie); + CHECK(name != NULL); + CHECK(type == NV_TYPE_BINARY); + CHECK(strcmp(name, "nvlist/binary/abcdefghijklmnopqrstuvwxyz") == 0); + CHECK(memcmp(nvlist_get_binary(nvl, name, NULL), "abcdefghijklmnopqrstuvwxyz", sizeof("abcdefghijklmnopqrstuvwxyz")) == 0); + CHECK(memcmp(nvlist_get_binary(nvl, name, &size), "abcdefghijklmnopqrstuvwxyz", sizeof("abcdefghijklmnopqrstuvwxyz")) == 0); + CHECK(size == sizeof("abcdefghijklmnopqrstuvwxyz")); + + name = nvlist_next(nvl, &type, &cookie); + CHECK(name != NULL); + CHECK(type == NV_TYPE_NVLIST); + CHECK(strcmp(name, "nvlist/nvlist/empty") == 0); + cnvl = nvlist_get_nvlist(nvl, name); + CHECK(nvlist_empty(cnvl)); + + name = nvlist_next(nvl, &type, &cookie); + CHECK(name != NULL); + CHECK(type == NV_TYPE_NVLIST); + CHECK(strcmp(name, "nvlist/nvlist") == 0); + cnvl = nvlist_get_nvlist(nvl, name); + + ccookie = NULL; + + cname = nvlist_next(cnvl, &ctype, &ccookie); + CHECK(cname != NULL); + CHECK(ctype == NV_TYPE_BOOL); + CHECK(strcmp(cname, "nvlist/bool/true") == 0); + CHECK(nvlist_get_bool(cnvl, cname) == true); + + cname = nvlist_next(cnvl, &ctype, &ccookie); + CHECK(cname != NULL); + CHECK(ctype == NV_TYPE_BOOL); + CHECK(strcmp(cname, "nvlist/bool/false") == 0); + CHECK(nvlist_get_bool(cnvl, cname) == false); + + cname = nvlist_next(cnvl, &ctype, &ccookie); + CHECK(cname != NULL); + CHECK(ctype == NV_TYPE_NUMBER); + CHECK(strcmp(cname, "nvlist/number/0") == 0); + CHECK(nvlist_get_number(cnvl, cname) == 0); + + cname = nvlist_next(cnvl, &ctype, &ccookie); + CHECK(cname != NULL); + CHECK(ctype == NV_TYPE_NUMBER); + CHECK(strcmp(cname, "nvlist/number/1") == 0); + CHECK(nvlist_get_number(cnvl, cname) == 1); + + cname = nvlist_next(cnvl, &ctype, &ccookie); + CHECK(cname != NULL); + CHECK(ctype == NV_TYPE_NUMBER); + CHECK(strcmp(cname, "nvlist/number/-1") == 0); + CHECK((int)nvlist_get_number(cnvl, cname) == -1); + + cname = nvlist_next(cnvl, &ctype, &ccookie); + CHECK(cname != NULL); + CHECK(ctype == NV_TYPE_NUMBER); + CHECK(strcmp(cname, "nvlist/number/UINT64_MAX") == 0); + CHECK(nvlist_get_number(cnvl, cname) == UINT64_MAX); + + cname = nvlist_next(cnvl, &ctype, &ccookie); + CHECK(cname != NULL); + CHECK(ctype == NV_TYPE_NUMBER); + CHECK(strcmp(cname, "nvlist/number/INT64_MIN") == 0); + CHECK((int64_t)nvlist_get_number(cnvl, cname) == INT64_MIN); + + cname = nvlist_next(cnvl, &ctype, &ccookie); + CHECK(cname != NULL); + CHECK(ctype == NV_TYPE_NUMBER); + CHECK(strcmp(cname, "nvlist/number/INT64_MAX") == 0); + CHECK((int64_t)nvlist_get_number(cnvl, cname) == INT64_MAX); + + cname = nvlist_next(cnvl, &ctype, &ccookie); + CHECK(cname != NULL); + CHECK(ctype == NV_TYPE_STRING); + CHECK(strcmp(cname, "nvlist/string/") == 0); + CHECK(strcmp(nvlist_get_string(cnvl, cname), "") == 0); + + cname = nvlist_next(cnvl, &ctype, &ccookie); + CHECK(cname != NULL); + CHECK(ctype == NV_TYPE_STRING); + CHECK(strcmp(cname, "nvlist/string/x") == 0); + CHECK(strcmp(nvlist_get_string(cnvl, cname), "x") == 0); + + cname = nvlist_next(cnvl, &ctype, &ccookie); + CHECK(cname != NULL); + CHECK(ctype == NV_TYPE_STRING); + CHECK(strcmp(cname, "nvlist/string/abcdefghijklmnopqrstuvwxyz") == 0); + CHECK(strcmp(nvlist_get_string(cnvl, cname), "abcdefghijklmnopqrstuvwxyz") == 0); + + cname = nvlist_next(cnvl, &ctype, &ccookie); + CHECK(cname != NULL); + CHECK(ctype == NV_TYPE_DESCRIPTOR); + CHECK(strcmp(cname, "nvlist/descriptor/STDERR_FILENO") == 0); + CHECK(fd_is_valid(nvlist_get_descriptor(cnvl, cname))); + + cname = nvlist_next(cnvl, &ctype, &ccookie); + CHECK(cname != NULL); + CHECK(ctype == NV_TYPE_BINARY); + CHECK(strcmp(cname, "nvlist/binary/x") == 0); + CHECK(memcmp(nvlist_get_binary(cnvl, cname, NULL), "x", 1) == 0); + CHECK(memcmp(nvlist_get_binary(cnvl, cname, &size), "x", 1) == 0); + CHECK(size == 1); + + cname = nvlist_next(cnvl, &ctype, &ccookie); + CHECK(cname != NULL); + CHECK(ctype == NV_TYPE_BINARY); + CHECK(strcmp(cname, "nvlist/binary/abcdefghijklmnopqrstuvwxyz") == 0); + CHECK(memcmp(nvlist_get_binary(cnvl, cname, NULL), "abcdefghijklmnopqrstuvwxyz", sizeof("abcdefghijklmnopqrstuvwxyz")) == 0); + CHECK(memcmp(nvlist_get_binary(cnvl, cname, &size), "abcdefghijklmnopqrstuvwxyz", sizeof("abcdefghijklmnopqrstuvwxyz")) == 0); + CHECK(size == sizeof("abcdefghijklmnopqrstuvwxyz")); + + cname = nvlist_next(cnvl, &ctype, &ccookie); + CHECK(cname != NULL); + CHECK(ctype == NV_TYPE_NVLIST); + CHECK(strcmp(cname, "nvlist/nvlist/empty") == 0); + empty = nvlist_get_nvlist(cnvl, cname); + CHECK(nvlist_empty(empty)); + + cname = nvlist_next(cnvl, &ctype, &ccookie); + CHECK(cname == NULL); + + name = nvlist_next(nvl, &type, &cookie); + CHECK(name == NULL); +} + +int +main(void) +{ + int status, socks[2]; + pid_t pid; + + printf("1..134\n"); + fflush(stdout); + + if (socketpair(PF_UNIX, SOCK_STREAM, 0, socks) < 0) + err(1, "socketpair() failed"); + pid = fork(); + switch (pid) { + case -1: + /* Failure. */ + err(1, "unable to fork"); + case 0: + /* Child. */ + close(socks[0]); + child(socks[1]); + return (0); + default: + /* Parent. */ + close(socks[1]); + parent(socks[0]); + break; + } + + if (waitpid(pid, &status, 0) < 0) + err(1, "waitpid() failed"); + + return (0); +} Index: nvpair.h =================================================================== --- /dev/null +++ nvpair.h @@ -0,0 +1,230 @@ +/*- + * Copyright (c) 2014 Sandvine Inc. + * 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 AUTHORS 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 AUTHORS 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: head/sys/cddl/compat/opensolaris/sys/nvpair.h 286796 2015-08-15 06:34:49Z oshogbo $ + */ + +#ifndef _OPENSOLARIS_SYS_NVPAIR_H_ +#define _OPENSOLARIS_SYS_NVPAIR_H_ + +#ifdef _KERNEL + +/* + * Some of the symbols in the Illumos nvpair library conflict with symbols + * provided by nv(9), so we use this preprocessor hack to avoid the conflict. + * + * This list was generated by: + * cat nv.h nv_impl.h nvlist_* nvpair_impl.h | \ + * sed -nE 's/^[[:alnum:]_][[:alnum:]_ ]*[[:space:]]+[*]*([[:alnum:]_]+)\(.*$/#define \1 illumos_\1/p' | \ + * sort -u + */ +#define nvlist_add_binary illumos_nvlist_add_binary +#define nvlist_add_bool illumos_nvlist_add_bool +#define nvlist_add_bool_array illumos_nvlist_add_bool_array +#define nvlist_add_descriptor illumos_nvlist_add_descriptor +#define nvlist_add_descriptor_array illumos_nvlist_add_descriptor_array +#define nvlist_add_null illumos_nvlist_add_null +#define nvlist_add_number illumos_nvlist_add_number +#define nvlist_add_number_array illumos_nvlist_add_number_array +#define nvlist_add_nvlist illumos_nvlist_add_nvlist +#define nvlist_add_nvlist_array illumos_nvlist_add_nvlist_array +#define nvlist_add_nvpair illumos_nvlist_add_nvpair +#define nvlist_add_string illumos_nvlist_add_string +#define nvlist_add_string_array illumos_nvlist_add_string_array +#define nvlist_add_stringf illumos_nvlist_add_stringf +#define nvlist_add_stringv illumos_nvlist_add_stringv +#define nvlist_clone illumos_nvlist_clone +#define nvlist_create illumos_nvlist_create +#define nvlist_descriptors illumos_nvlist_descriptors +#define nvlist_destroy illumos_nvlist_destroy +#define nvlist_dump illumos_nvlist_dump +#define nvlist_empty illumos_nvlist_empty +#define nvlist_error illumos_nvlist_error +#define nvlist_exists illumos_nvlist_exists +#define nvlist_exists_binary illumos_nvlist_exists_binary +#define nvlist_exists_bool illumos_nvlist_exists_bool +#define nvlist_exists_bool_array illumos_nvlist_exists_bool_array +#define nvlist_exists_descriptor illumos_nvlist_exists_descriptor +#define nvlist_exists_descriptor_array illumos_nvlist_exists_descriptor_array +#define nvlist_exists_null illumos_nvlist_exists_null +#define nvlist_exists_number illumos_nvlist_exists_number +#define nvlist_exists_number_array illumos_nvlist_exists_number_array +#define nvlist_exists_nvlist illumos_nvlist_exists_nvlist +#define nvlist_exists_nvlist_array illumos_nvlist_exists_nvlist_array +#define nvlist_exists_string illumos_nvlist_exists_string +#define nvlist_exists_string_array illumos_nvlist_exists_string_array +#define nvlist_exists_type illumos_nvlist_exists_type +#define nvlist_fdump illumos_nvlist_fdump +#define nvlist_first_nvpair illumos_nvlist_first_nvpair +#define nvlist_flags illumos_nvlist_flags +#define nvlist_free illumos_nvlist_free +#define nvlist_free_binary illumos_nvlist_free_binary +#define nvlist_free_binary_array illumos_nvlist_free_binary_array +#define nvlist_free_bool illumos_nvlist_free_bool +#define nvlist_free_bool_array illumos_nvlist_free_bool_array +#define nvlist_free_descriptor illumos_nvlist_free_descriptor +#define nvlist_free_descriptor_array illumos_nvlist_free_descriptor_array +#define nvlist_free_null illumos_nvlist_free_null +#define nvlist_free_number illumos_nvlist_free_number +#define nvlist_free_number_array illumos_nvlist_free_number_array +#define nvlist_free_nvlist illumos_nvlist_free_nvlist +#define nvlist_free_nvlist_array illumos_nvlist_free_nvlist_array +#define nvlist_free_nvpair illumos_nvlist_free_nvpair +#define nvlist_free_string illumos_nvlist_free_string +#define nvlist_free_string_array illumos_nvlist_free_string_array +#define nvlist_free_type illumos_nvlist_free_type +#define nvlist_get_array_next illumos_nvlist_get_array_next +#define nvlist_get_binary illumos_nvlist_get_binary +#define nvlist_get_bool illumos_nvlist_get_bool +#define nvlist_get_bool_array illumos_nvlist_get_bool_array +#define nvlist_get_descriptor illumos_nvlist_get_descriptor +#define nvlist_get_descriptor_array illumos_nvlist_get_descriptor_array +#define nvlist_get_number illumos_nvlist_get_number +#define nvlist_get_number_array illumos_nvlist_get_number_array +#define nvlist_get_nvlist illumos_nvlist_get_nvlist +#define nvlist_get_nvpair illumos_nvlist_get_nvpair +#define nvlist_get_nvpair_parent illumos_nvlist_get_nvpair_parent +#define nvlist_get_pararr illumos_nvlist_get_pararr +#define nvlist_get_parent illumos_nvlist_get_parent +#define nvlist_get_string illumos_nvlist_get_string +#define nvlist_in_array illumos_nvlist_in_array +#define nvlist_move_binary illumos_nvlist_move_binary +#define nvlist_move_bool_array illumos_nvlist_move_bool_array +#define nvlist_move_descriptor illumos_nvlist_move_descriptor +#define nvlist_move_descriptor_array illumos_nvlist_move_descriptor_array +#define nvlist_move_number_array illumos_nvlist_move_number_array +#define nvlist_move_nvlist illumos_nvlist_move_nvlist +#define nvlist_move_nvlist_array illumos_nvlist_move_nvlist_array +#define nvlist_move_nvpair illumos_nvlist_move_nvpair +#define nvlist_move_string illumos_nvlist_move_string +#define nvlist_move_string_array illumos_nvlist_move_string_array +#define nvlist_ndescriptors illumos_nvlist_ndescriptors +#define nvlist_next illumos_nvlist_next +#define nvlist_next_nvpair illumos_nvlist_next_nvpair +#define nvlist_pack illumos_nvlist_pack +#define nvlist_prev_nvpair illumos_nvlist_prev_nvpair +#define nvlist_recv illumos_nvlist_recv +#define nvlist_remove_nvpair illumos_nvlist_remove_nvpair +#define nvlist_send illumos_nvlist_send +#define nvlist_set_array_next illumos_nvlist_set_array_next +#define nvlist_set_error illumos_nvlist_set_error +#define nvlist_set_flags illumos_nvlist_set_flags +#define nvlist_set_parent illumos_nvlist_set_parent +#define nvlist_size illumos_nvlist_size +#define nvlist_take_binary illumos_nvlist_take_binary +#define nvlist_take_bool illumos_nvlist_take_bool +#define nvlist_take_bool_array illumos_nvlist_take_bool_array +#define nvlist_take_descriptor illumos_nvlist_take_descriptor +#define nvlist_take_descriptor_array illumos_nvlist_take_descriptor_array +#define nvlist_take_number illumos_nvlist_take_number +#define nvlist_take_number_array illumos_nvlist_take_number_array +#define nvlist_take_nvlist illumos_nvlist_take_nvlist +#define nvlist_take_nvlist_array illumos_nvlist_take_nvlist_array +#define nvlist_take_nvpair illumos_nvlist_take_nvpair +#define nvlist_take_string illumos_nvlist_take_string +#define nvlist_take_string_array illumos_nvlist_take_string_array +#define nvlist_unpack illumos_nvlist_unpack +#define nvlist_unpack_header illumos_nvlist_unpack_header +#define nvlist_xfer illumos_nvlist_xfer +#define nvpair_assert illumos_nvpair_assert +#define nvpair_clone illumos_nvpair_clone +#define nvpair_create_binary illumos_nvpair_create_binary +#define nvpair_create_bool illumos_nvpair_create_bool +#define nvpair_create_bool_array illumos_nvpair_create_bool_array +#define nvpair_create_descriptor illumos_nvpair_create_descriptor +#define nvpair_create_descriptor_array illumos_nvpair_create_descriptor_array +#define nvpair_create_null illumos_nvpair_create_null +#define nvpair_create_number illumos_nvpair_create_number +#define nvpair_create_number_array illumos_nvpair_create_number_array +#define nvpair_create_nvlist illumos_nvpair_create_nvlist +#define nvpair_create_nvlist_array illumos_nvpair_create_nvlist_array +#define nvpair_create_string illumos_nvpair_create_string +#define nvpair_create_string_array illumos_nvpair_create_string_array +#define nvpair_create_stringf illumos_nvpair_create_stringf +#define nvpair_create_stringv illumos_nvpair_create_stringv +#define nvpair_free illumos_nvpair_free +#define nvpair_free_structure illumos_nvpair_free_structure +#define nvpair_get_binary illumos_nvpair_get_binary +#define nvpair_get_bool illumos_nvpair_get_bool +#define nvpair_get_bool_array illumos_nvpair_get_bool_array +#define nvpair_get_descriptor illumos_nvpair_get_descriptor +#define nvpair_get_descriptor_array illumos_nvpair_get_descriptor_array +#define nvpair_get_number illumos_nvpair_get_number +#define nvpair_get_number_array illumos_nvpair_get_number_array +#define nvpair_get_nvlist illumos_nvpair_get_nvlist +#define nvpair_get_string illumos_nvpair_get_string +#define nvpair_header_size illumos_nvpair_header_size +#define nvpair_init_datasize illumos_nvpair_init_datasize +#define nvpair_insert illumos_nvpair_insert +#define nvpair_move_binary illumos_nvpair_move_binary +#define nvpair_move_bool_array illumos_nvpair_move_bool_array +#define nvpair_move_descriptor illumos_nvpair_move_descriptor +#define nvpair_move_descriptor_array illumos_nvpair_move_descriptor_array +#define nvpair_move_number_array illumos_nvpair_move_number_array +#define nvpair_move_nvlist illumos_nvpair_move_nvlist +#define nvpair_move_nvlist_array illumos_nvpair_move_nvlist_array +#define nvpair_move_string illumos_nvpair_move_string +#define nvpair_move_string_array illumos_nvpair_move_string_array +#define nvpair_name illumos_nvpair_name +#define nvpair_next illumos_nvpair_next +#define nvpair_nvlist illumos_nvpair_nvlist +#define nvpair_pack_binary illumos_nvpair_pack_binary +#define nvpair_pack_bool illumos_nvpair_pack_bool +#define nvpair_pack_bool_array illumos_nvpair_pack_bool_array +#define nvpair_pack_descriptor illumos_nvpair_pack_descriptor +#define nvpair_pack_descriptor_array illumos_nvpair_pack_descriptor_array +#define nvpair_pack_header illumos_nvpair_pack_header +#define nvpair_pack_null illumos_nvpair_pack_null +#define nvpair_pack_number illumos_nvpair_pack_number +#define nvpair_pack_number_array illumos_nvpair_pack_number_array +#define nvpair_pack_nvlist_array_next illumos_nvpair_pack_nvlist_array_next +#define nvpair_pack_nvlist_up illumos_nvpair_pack_nvlist_up +#define nvpair_pack_string illumos_nvpair_pack_string +#define nvpair_pack_string_array illumos_nvpair_pack_string_array +#define nvpair_prev illumos_nvpair_prev +#define nvpair_remove illumos_nvpair_remove +#define nvpair_size illumos_nvpair_size +#define nvpair_type illumos_nvpair_type +#define nvpair_type_string illumos_nvpair_type_string +#define nvpair_unpack illumos_nvpair_unpack +#define nvpair_unpack_binary illumos_nvpair_unpack_binary +#define nvpair_unpack_bool illumos_nvpair_unpack_bool +#define nvpair_unpack_bool_array illumos_nvpair_unpack_bool_array +#define nvpair_unpack_descriptor illumos_nvpair_unpack_descriptor +#define nvpair_unpack_descriptor_array illumos_nvpair_unpack_descriptor_array +#define nvpair_unpack_header illumos_nvpair_unpack_header +#define nvpair_unpack_null illumos_nvpair_unpack_null +#define nvpair_unpack_number illumos_nvpair_unpack_number +#define nvpair_unpack_number_array illumos_nvpair_unpack_number_array +#define nvpair_unpack_nvlist illumos_nvpair_unpack_nvlist +#define nvpair_unpack_nvlist_array illumos_nvpair_unpack_nvlist_array +#define nvpair_unpack_string illumos_nvpair_unpack_string +#define nvpair_unpack_string_array illumos_nvpair_unpack_string_array + +#endif /* _KERNEL */ + +#include_next + +#endif Index: share/man/man9/Makefile =================================================================== --- share/man/man9/Makefile +++ share/man/man9/Makefile @@ -188,6 +188,7 @@ mutex.9 \ namei.9 \ netisr.9 \ + nv.9 \ osd.9 \ panic.9 \ pbuf.9 \ @@ -998,6 +999,68 @@ mutex.9 mtx_unlock_spin_flags.9 MLINKS+=namei.9 NDFREE.9 \ namei.9 NDINIT.9 +MLINKS+=nv.9 libnv.9 \ + nv.9 nvlist.9 \ + nv.9 nvlist_add_binary.9 \ + nv.9 nvlist_add_bool.9 \ + nv.9 nvlist_add_descriptor.9 \ + nv.9 nvlist_add_null.9 \ + nv.9 nvlist_add_number.9 \ + nv.9 nvlist_add_nvlist.9 \ + nv.9 nvlist_add_string.9 \ + nv.9 nvlist_add_stringf.9 \ + nv.9 nvlist_add_stringv.9 \ + nv.9 nvlist_clone.9 \ + nv.9 nvlist_create.9 \ + nv.9 nvlist_destroy.9 \ + nv.9 nvlist_dump.9 \ + nv.9 nvlist_empty.9 \ + nv.9 nvlist_error.9 \ + nv.9 nvlist_exists.9 \ + nv.9 nvlist_exists_binary.9 \ + nv.9 nvlist_exists_bool.9 \ + nv.9 nvlist_exists_descriptor.9 \ + nv.9 nvlist_exists_null.9 \ + nv.9 nvlist_exists_number.9 \ + nv.9 nvlist_exists_nvlist.9 \ + nv.9 nvlist_exists_string.9 \ + nv.9 nvlist_exists_type.9 \ + nv.9 nvlist_fdump.9 \ + nv.9 nvlist_flags.9 \ + nv.9 nvlist_free.9 \ + nv.9 nvlist_free_binary.9 \ + nv.9 nvlist_free_bool.9 \ + nv.9 nvlist_free_descriptor.9 \ + nv.9 nvlist_free_null.9 \ + nv.9 nvlist_free_number.9 \ + nv.9 nvlist_free_nvlist.9 \ + nv.9 nvlist_free_string.9 \ + nv.9 nvlist_free_type.9 \ + nv.9 nvlist_get_binary.9 \ + nv.9 nvlist_get_bool.9 \ + nv.9 nvlist_get_descriptor.9 \ + nv.9 nvlist_get_number.9 \ + nv.9 nvlist_get_nvlist.9 \ + nv.9 nvlist_get_parent.9 \ + nv.9 nvlist_get_string.9 \ + nv.9 nvlist_move_binary.9 \ + nv.9 nvlist_move_descriptor.9 \ + nv.9 nvlist_move_nvlist.9 \ + nv.9 nvlist_move_string.9 \ + nv.9 nvlist_next.9 \ + nv.9 nvlist_pack.9 \ + nv.9 nvlist_recv.9 \ + nv.9 nvlist_send.9 \ + nv.9 nvlist_set_error.9 \ + nv.9 nvlist_size.9 \ + nv.9 nvlist_take_binary.9 \ + nv.9 nvlist_take_bool.9 \ + nv.9 nvlist_take_descriptor.9 \ + nv.9 nvlist_take_number.9 \ + nv.9 nvlist_take_nvlist.9 \ + nv.9 nvlist_take_string.9 \ + nv.9 nvlist_unpack.9 \ + nv.9 nvlist_xfer.9 MLINKS+=panic.9 vpanic.9 MLINKS+=pbuf.9 getpbuf.9 \ pbuf.9 relpbuf.9 \ Index: share/man/man9/nv.9 =================================================================== --- /dev/null +++ share/man/man9/nv.9 @@ -0,0 +1,935 @@ +.\" +.\" Copyright (c) 2013 The FreeBSD Foundation +.\" Copyright (c) 2013-2015 Mariusz Zaborski +.\" All rights reserved. +.\" +.\" This documentation was written by Pawel Jakub Dawidek under sponsorship +.\" the FreeBSD Foundation. +.\" +.\" 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: head/share/man/man9/nv.9 287232 2015-08-28 00:12:59Z markj $ +.\" +.Dd August 15, 2015 +.Dt NV 9 +.Os +.Sh NAME +.Nm nvlist_create , +.Nm nvlist_destroy , +.Nm nvlist_error , +.Nm nvlist_set_error , +.Nm nvlist_empty , +.Nm nvlist_flags , +.Nm nvlist_exists , +.Nm nvlist_free , +.Nm nvlist_clone , +.Nm nvlist_dump , +.Nm nvlist_fdump , +.Nm nvlist_size , +.Nm nvlist_pack , +.Nm nvlist_unpack , +.Nm nvlist_send , +.Nm nvlist_recv , +.Nm nvlist_xfer , +.Nm nvlist_in_array , +.Nm nvlist_next , +.Nm nvlist_add , +.Nm nvlist_move , +.Nm nvlist_get , +.Nm nvlist_take +.Nd "library for name/value pairs" +.Sh LIBRARY +.Lb libnv +.Sh SYNOPSIS +.In sys/nv.h +.Ft "nvlist_t *" +.Fn nvlist_create "int flags" +.Ft void +.Fn nvlist_destroy "nvlist_t *nvl" +.Ft int +.Fn nvlist_error "const nvlist_t *nvl" +.Ft void +.Fn nvlist_set_error "nvlist_t *nvl" "int error" +.Ft bool +.Fn nvlist_empty "const nvlist_t *nvl" +.Ft int +.Fn nvlist_flags "const nvlist_t *nvl" +.Ft bool +.Fn nvlist_in_array "const nvlist_t *nvl" +.\" +.Ft "nvlist_t *" +.Fn nvlist_clone "const nvlist_t *nvl" +.\" +.Ft void +.Fn nvlist_dump "const nvlist_t *nvl" "int fd" +.Ft void +.Fn nvlist_fdump "const nvlist_t *nvl" "FILE *fp" +.\" +.Ft size_t +.Fn nvlist_size "const nvlist_t *nvl" +.Ft "void *" +.Fn nvlist_pack "const nvlist_t *nvl" "size_t *sizep" +.Ft "nvlist_t *" +.Fn nvlist_unpack "const void *buf" "size_t size" "int flags" +.\" +.Ft int +.Fn nvlist_send "int sock" "const nvlist_t *nvl" +.Ft "nvlist_t *" +.Fn nvlist_recv "int sock" "int flags" +.Ft "nvlist_t *" +.Fn nvlist_xfer "int sock" "nvlist_t *nvl" "int flags" +.\" +.Ft "const char *" +.Fn nvlist_next "const nvlist_t *nvl" "int *typep" "void **cookiep" +.\" +.Ft bool +.Fn nvlist_exists "const nvlist_t *nvl" "const char *name" +.Ft bool +.Fn nvlist_exists_type "const nvlist_t *nvl" "const char *name" "int type" +.Ft bool +.Fn nvlist_exists_null "const nvlist_t *nvl" "const char *name" +.Ft bool +.Fn nvlist_exists_bool "const nvlist_t *nvl" "const char *name" +.Ft bool +.Fn nvlist_exists_number "const nvlist_t *nvl" "const char *name" +.Ft bool +.Fn nvlist_exists_string "const nvlist_t *nvl" "const char *name" +.Ft bool +.Fn nvlist_exists_nvlist "const nvlist_t *nvl" "const char *name" +.Ft bool +.Fn nvlist_exists_descriptor "const nvlist_t *nvl" "const char *name" +.Ft bool +.Fn nvlist_exists_binary "const nvlist_t *nvl" "const char *name" +.Ft bool +.Fn nvlist_exists_bool_array "const nvlist_t *nvl" "const char *name" +.Ft bool +.Fn nvlist_exists_number_array "const nvlist_t *nvl" "const char *name" +.Ft bool +.Fn nvlist_exists_string_array "const nvlist_t *nvl" "const char *name" +.Ft bool +.Fn nvlist_exists_nvlist_array "const nvlist_t *nvl" "const char *name" +.Ft bool +.Fn nvlist_exists_descriptor_array "const nvlist_t *nvl" "const char *name" +.\" +.Ft void +.Fn nvlist_add_null "nvlist_t *nvl" "const char *name" +.Ft void +.Fn nvlist_add_bool "nvlist_t *nvl" "const char *name" "bool value" +.Ft void +.Fn nvlist_add_number "nvlist_t *nvl" "const char *name" "uint64_t value" +.Ft void +.Fn nvlist_add_string "nvlist_t *nvl" "const char *name" "const char *value" +.Ft void +.Fn nvlist_add_stringf "nvlist_t *nvl" "const char *name" "const char *valuefmt" "..." +.Ft void +.Fn nvlist_add_stringv "nvlist_t *nvl" "const char *name" "const char *valuefmt" "va_list valueap" +.Ft void +.Fn nvlist_add_nvlist "nvlist_t *nvl" "const char *name" "const nvlist_t *value" +.Ft void +.Fn nvlist_add_descriptor "nvlist_t *nvl" "const char *name" "int value" +.Ft void +.Fn nvlist_add_binary "nvlist_t *nvl" "const char *name" "const void *value" "size_t size" +.Ft void +.Fn nvlist_add_bool_array "nvlist_t *nvl" "const char *name" "const bool *value" "size_t nitems" +. +.Ft void +.Fn nvlist_add_number_array "nvlist_t *nvl" "const char *name" "const uint64_t *value" "size_t nitems" +. +.Ft void +.Fn nvlist_add_string_array "nvlist_t *nvl" "const char *name" "const char * const * value" "size_t nitems" +. +.Ft void +.Fn nvlist_add_nvlist_array "nvlist_t *nvl" "const char *name" "const nvlist_t * const * value" "size_t nitems" +. +.Ft void +.Fn nvlist_add_descriptor_array "nvlist_t *nvl" "const char *name" "const int *value" "size_t nitems" +.\" +.Ft void +.Fn nvlist_move_string "nvlist_t *nvl" "const char *name" "char *value" +.Ft void +.Fn nvlist_move_nvlist "nvlist_t *nvl" "const char *name" "nvlist_t *value" +.Ft void +.Fn nvlist_move_descriptor "nvlist_t *nvl" "const char *name" "int value" +.Ft void +.Fn nvlist_move_binary "nvlist_t *nvl" "const char *name" "void *value" "size_t size" +.Ft void +.Fn nvlist_move_bool_array "nvlist_t *nvl" "const char *name" "bool *value" "size_t nitems" +. +.Ft void +.Fn nvlist_move_number_array "nvlist_t *nvl" "const char *name" "uint64_t *value" "size_t nitems" +. +.Ft void +.Fn nvlist_move_string_array "nvlist_t *nvl" "const char *name" "char **value" "size_t nitems" +. +.Ft void +.Fn nvlist_move_nvlist_array "nvlist_t *nvl" "const char *name" "nvlist_t **value" "size_t nitems" +. +.Ft void +.Fn nvlist_move_descriptor_array "nvlist_t *nvl" "const char *name" "int *value" "size_t nitems" +.\" +.Ft bool +.Fn nvlist_get_bool "const nvlist_t *nvl" "const char *name" +.Ft uint64_t +.Fn nvlist_get_number "const nvlist_t *nvl" "const char *name" +.Ft "const char *" +.Fn nvlist_get_string "const nvlist_t *nvl" "const char *name" +.Ft "const nvlist_t *" +.Fn nvlist_get_nvlist "const nvlist_t *nvl" "const char *name" +.Ft int +.Fn nvlist_get_descriptor "const nvlist_t *nvl" "const char *name" +.Ft "const void *" +.Fn nvlist_get_binary "const nvlist_t *nvl" "const char *name" "size_t *sizep" +.Ft "const bool *" +.Fn nvlist_get_bool_array "const nvlist_t *nvl" "const char *name" "size_t *nitems" +.Ft "const uint64_t *" +.Fn nvlist_get_number "const nvlist_t *nvl" "const char *name" "size_t *nitems" +.Ft "const char * const *" +.Fn nvlist_get_string "const nvlist_t *nvl" "const char *name" "size_t *nitems" +.Ft "const nvlist_t * const *" +.Fn nvlist_get_nvlist "const nvlist_t *nvl" "const char *name" "size_t *nitems" +.Ft "const int *" +.Fn nvlist_get_descriptor_array "const nvlist_t *nvl" "const char *name" "size_t *nitems" +.Ft "const nvlist_t *" +.Fn nvlist_get_parent "const nvlist_t *nvl" "void **cookiep" +.Ft "const nvlist_t *" +.Fn nvlist_get_array_next "const nvlist_t *nvl" +.Ft "const nvlist_t *" +.Fn nvlist_get_pararr "const nvlist_t *nvl" "void **cookiep" +.\" +.Ft bool +.Fn nvlist_take_bool "nvlist_t *nvl" "const char *name" +.Ft uint64_t +.Fn nvlist_take_number "nvlist_t *nvl" "const char *name" +.Ft "char *" +.Fn nvlist_take_string "nvlist_t *nvl" "const char *name" +.Ft "nvlist_t *" +.Fn nvlist_take_nvlist "nvlist_t *nvl" "const char *name" +.Ft int +.Fn nvlist_take_descriptor "nvlist_t *nvl" "const char *name" +.Ft "void *" +.Fn nvlist_take_binary "nvlist_t *nvl" "const char *name" "size_t *sizep" +.Ft "bool *" +.Fn nvlist_take_bool_array "nvlist_t *nvl" "const char *name" "size_t *nitems" +.Ft "uint64_t **" +.Fn nvlist_take_number "nvlist_t *nvl" "const char *name" "size_t *nitems" +.Ft "char **" +.Fn nvlist_take_string "nvlist_t *nvl" "const char *name" "size_t *nitems" +.Ft "nvlist_t **" +.Fn nvlist_take_nvlist "nvlist_t *nvl" "const char *name" "size_t *nitems" +.Ft "int *" +.Fn nvlist_take_descriptor "nvlist_t *nvl" "const char *name" "size_t *nitems" +.\" +.Ft void +.Fn nvlist_free "nvlist_t *nvl" "const char *name" +.Ft void +.Fn nvlist_free_type "nvlist_t *nvl" "const char *name" "int type" +.\" +.Ft void +.Fn nvlist_free_null "nvlist_t *nvl" "const char *name" +.Ft void +.Fn nvlist_free_bool "nvlist_t *nvl" "const char *name" +.Ft void +.Fn nvlist_free_number "nvlist_t *nvl" "const char *name" +.Ft void +.Fn nvlist_free_string "nvlist_t *nvl" "const char *name" +.Ft void +.Fn nvlist_free_nvlist "nvlist_t *nvl" "const char *name" +.Ft void +.Fn nvlist_free_descriptor "nvlist_t *nvl" "const char *name" +.Ft void +.Fn nvlist_free_binary "nvlist_t *nvl" "const char *name" +.Ft void +.Fn nvlist_free_bool_array "nvlist_t *nvl" "const char *name" +.Ft void +.Fn nvlist_free_number_array "nvlist_t *nvl" "const char *name" +.Ft void +.Fn nvlist_free_string_array "nvlist_t *nvl" "const char *name" +.Ft void +.Fn nvlist_free_nvlist_array "nvlist_t *nvl" "const char *name" +.Ft void +.Fn nvlist_free_descriptor_array "nvlist_t *nvl" "const char *name" +.Sh DESCRIPTION +The +.Nm libnv +library allows to easily manage name value pairs as well as send and receive +them over sockets. +A group (list) of name value pairs is called an +.Nm nvlist . +The API supports the following data types: +.Bl -ohang -offset indent +.It Sy null ( NV_TYPE_NULL ) +There is no data associated with the name. +.It Sy bool ( NV_TYPE_BOOL ) +The value can be either +.Dv true +or +.Dv false . +.It Sy number ( NV_TYPE_NUMBER ) +The value is a number stored as +.Vt uint64_t . +.It Sy string ( NV_TYPE_STRING ) +The value is a C string. +.It Sy nvlist ( NV_TYPE_NVLIST ) +The value is a nested nvlist. +.It Sy descriptor ( NV_TYPE_DESCRIPTOR ) +The value is a file descriptor. +Note that file descriptors can be sent only over +.Xr unix 4 +domain sockets. +.It Sy binary ( NV_TYPE_BINARY ) +The value is a binary buffer. +.It Sy bool array ( NV_TYPE_BOOL_ARRAY ) +The value is an array of boolean values. +.It Sy number array ( NV_TYPE_NUMBER_ARRAY ) +The value is an array of numbers, each stored as +.Vt uint64_t . +.It Sy string array ( NV_TYPE_STRING_ARRAY ) +The value is an array of C strings. +.It Sy nvlist array ( NV_TYPE_NVLIST_ARRAY ) +The value is an array of nvlists. +When an nvlist is added to an array, it becomes part of the primary nvlist. +Traversing these arrays can be done using the +.Fn nvlist_get_array_next +and +.Fn nvlist_get_pararr +functions. +.It Sy descriptor array ( NV_TYPE_DESCRIPTOR_ARRAY ) +The value is an array of files descriptors. +.El +.Pp +The +.Fn nvlist_create +function allocates memory and initializes an nvlist. +.Pp +The following flag can be provided: +.Pp +.Bl -tag -width "NV_FLAG_IGNORE_CASE" -compact -offset indent +.It Dv NV_FLAG_IGNORE_CASE +Perform case-insensitive lookups of provided names. +.It Dv NV_FLAG_NO_UNIQUE +Names in the nvlist do not have to be unique. +.El +.Pp +The +.Fn nvlist_destroy +function destroys the given nvlist. +Function does nothing if +.Dv NULL +nvlist is provided. +Function never modifies the +.Va errno +global variable. +.Pp +The +.Fn nvlist_error +function returns any error value that the nvlist accumulated. +If the given nvlist is +.Dv NULL +the +.Er ENOMEM +error will be returned. +.Pp +The +.Fn nvlist_set_error +function sets an nvlist to be in the error state. +Subsequent calls to +.Fn nvlist_error +will return the given error value. +This function cannot be used to clear the error state from an nvlist. +This function does nothing if the nvlist is already in the error state. +.Pp +The +.Fn nvlist_empty +function returns +.Dv true +if the given nvlist is empty and +.Dv false +otherwise. +The nvlist must not be in error state. +.Pp +The +.Fn nvlist_flags +function returns flags used to create the nvlist with the +.Fn nvlist_create +function. +.Pp +The +.Fn nvlist_in_array +function returns +.Dv true +if +.Fa nvl +is part of an array that is a member of another nvlist. +.Pp +The +.Fn nvlist_clone +functions clones the given nvlist. +The clone shares no resources with its origin. +This also means that all file descriptors that are part of the nvlist will be +duplicated with the +.Xr dup 2 +system call before placing them in the clone. +.Pp +The +.Fn nvlist_dump +dumps nvlist content for debugging purposes to the given file descriptor +.Fa fd . +.Pp +The +.Fn nvlist_fdump +dumps nvlist content for debugging purposes to the given file stream +.Fa fp . +.Pp +The +.Fn nvlist_size +function returns the size of the given nvlist after converting it to binary +buffer with the +.Fn nvlist_pack +function. +.Pp +The +.Fn nvlist_pack +function converts the given nvlist to a binary buffer. +The function allocates memory for the buffer, which should be freed with the +.Xr free 3 +function. +If the +.Fa sizep +argument is not +.Dv NULL , +the size of the buffer will be stored there. +The function returns +.Dv NULL +in case of an error (allocation failure). +If the nvlist contains any file descriptors +.Dv NULL +will be returned. +The nvlist must not be in error state. +.Pp +The +.Fn nvlist_unpack +function converts the given buffer to the nvlist. +The +.Fa flags +argument defines what type of the top level nvlist is expected to be. +Flags are set up using the +.Fn nvlist_create +function. +If the nvlist flags do not match the flags passed to +.Fn nvlist_unpack , +the nvlist will not be returned. +Every nested nvlist list should be checked using +.Fn nvlist_flags +function. +The function returns +.Dv NULL +in case of an error. +.Pp +The +.Fn nvlist_send +function sends the given nvlist over the socket given by the +.Fa sock +argument. +Note that nvlist that contains file descriptors can only be send over +.Xr unix 4 +domain sockets. +.Pp +The +.Fn nvlist_recv +function receives nvlist over the socket given by the +.Fa sock +argument. +The +.Fa flags +argument defines what type of the top level nvlist is expected to be. +Flags are set up using the +.Fn nvlist_create +function. +If the nvlist flags do not match the flags passed to +.Fn nvlist_recv , +the nvlist will not be returned. +Every nested nvlist list should be checked using +.Fn nvlist_flags +function. +.Pp +The +.Fn nvlist_xfer +function sends the given nvlist over the socket given by the +.Fa sock +argument and receives nvlist over the same socket. +The +.Fa flags +argument defines what type of the top level nvlist is expected to be. +Flags are set up using the +.Fn nvlist_create +function. +If the nvlist flags do not match the flags passed to +.Fn nvlist_xfer , +the nvlist will not be returned. +Every nested nvlist list should be checked using +.Fn nvlist_flags +function. +The given nvlist is always destroyed. +.Pp +The +.Fn nvlist_next +function iterates over the given nvlist returning names and types of subsequent +elements. +The +.Fa cookiep +argument allows the function to figure out which element should be returned +next. +The +.Va *cookiep +should be set to +.Dv NULL +for the first call and should not be changed later. +Returning +.Dv NULL +means there are no more elements on the nvlist. +The +.Fa typep +argument can be NULL. +Elements may not be removed from the nvlist while traversing it. +The nvlist must not be in error state. +Note that +.Fn nvlist_next +will handle +.Va cookiep +being set to +.Dv NULL . +In this case first element is returned or +.Dv NULL +if nvlist is empty. +This behavior simplifies removing the first element from the list. +.Pp +The +.Fn nvlist_exists +function returns +.Dv true +if element of the given name exists (besides of its type) or +.Dv false +otherwise. +The nvlist must not be in error state. +.Pp +The +.Fn nvlist_exists_type +function returns +.Dv true +if element of the given name and the given type exists or +.Dv false +otherwise. +The nvlist must not be in error state. +.Pp +The +.Fn nvlist_exists_null , +.Fn nvlist_exists_bool , +.Fn nvlist_exists_number , +.Fn nvlist_exists_string , +.Fn nvlist_exists_nvlist , +.Fn nvlist_exists_descriptor , +.Fn nvlist_exists_binary , +.Fn nvlist_exists_bool_array , +.Fn nvlist_exists_number_array , +.Fn nvlist_exists_string_array , +.Fn nvlist_exists_nvlist_array , +.Fn nvlist_exists_descriptor_array +functions return +.Dv true +if element of the given name and the given type determined by the function name +exists or +.Dv false +otherwise. +The nvlist must not be in error state. +.Pp +The +.Fn nvlist_add_null , +.Fn nvlist_add_bool , +.Fn nvlist_add_number , +.Fn nvlist_add_string , +.Fn nvlist_add_stringf , +.Fn nvlist_add_stringv , +.Fn nvlist_add_nvlist , +.Fn nvlist_add_descriptor , +.Fn nvlist_add_binary , +.Fn nvlist_add_bool_array , +.Fn nvlist_add_number_array , +.Fn nvlist_add_string_array , +.Fn nvlist_add_nvlist_array , +.Fn nvlist_add_descriptor_array +functions add element to the given nvlist. +When adding string or binary buffor the functions will allocate memory +and copy the data over. +When adding nvlist, the nvlist will be cloned and clone will be added. +When adding descriptor, the descriptor will be duplicated using the +.Xr dup 2 +system call and the new descriptor will be added. +The array functions will fail if there are any +.Dv NULL +elements in the array, or if the array pointer is +.Dv NULL . +If an error occurs while adding new element, internal error is set which can be +examined using the +.Fn nvlist_error +function. +.Pp +The +.Fn nvlist_move_string , +.Fn nvlist_move_nvlist , +.Fn nvlist_move_descriptor , +.Fn nvlist_move_binary , +.Fn nvlist_move_bool_array , +.Fn nvlist_move_number_array , +.Fn nvlist_move_string_array , +.Fn nvlist_move_nvlist_array , +.Fn nvlist_move_descriptor_array +functions add new element to the given nvlist, but unlike +.Fn nvlist_add_ +functions they will consume the given resource. +In the case of strings, descriptors, or nvlists every elements must be +unique, or it could cause a double free. +The array functions will fail if there are any +.Dv NULL +elements, or if the array pointer is +.Dv NULL . +If an error occurs while adding new element, the resource is destroyed and +internal error is set which can be examined using the +.Fn nvlist_error +function. +.Pp +The +.Fn nvlist_get_bool , +.Fn nvlist_get_number , +.Fn nvlist_get_string , +.Fn nvlist_get_nvlist , +.Fn nvlist_get_descriptor , +.Fn nvlist_get_binary , +.Fn nvlist_get_bool_array , +.Fn nvlist_get_number_array , +.Fn nvlist_get_string_array , +.Fn nvlist_get_nvlist_array , +.Fn nvlist_get_descriptor_array +functions return the value that corresponds to the given key name. +In the case of strings, nvlists, descriptors, binary, or arrays, the returned +resource should not be modified - they still belong to the nvlist. +If an element of the given name does not exist, the program will be aborted. +To avoid this, the caller should check for the existence of the name before +trying to obtain the value, or use the +.Xr dnvlist 3 +extension, which can provide a default value in the case of a missing element. +The nvlist must not be in error state. +.Pp +The +.Fn nvlist_get_parent +function returns the parent nvlist of the nested nvlist. +.Pp +The +.Fn nvlist_get_array_next +function returns the next element from the array or +.Dv NULL +if the nvlist is not in array or it is the last element. +Note that +.Fn nvlist_get_array_next +only works if you added the nvlist array using the +.Fn nvlist_move_nvlist_array +or +.Fn nvlist_add_nvlist_array +functions. +.Pp +The +.Fn nvlist_get_pararr +function returns the next element in the array, or if not available +the parent of the nested nvlist. +.Pp +The +.Fn nvlist_take_bool , +.Fn nvlist_take_number , +.Fn nvlist_take_string , +.Fn nvlist_take_nvlist , +.Fn nvlist_take_descriptor , +.Fn nvlist_take_binary , +.Fn nvlist_take_bool_array , +.Fn nvlist_take_number_array , +.Fn nvlist_take_string_array , +.Fn nvlist_take_nvlist_array , +.Fn nvlist_take_descriptor_array +functions return value associated with the given name and remove the element +from the nvlist. +In case of string and binary values, the caller is responsible for free returned +memory using the +.Xr free 3 +function. +In case of nvlist, the caller is responsible for destroying returned nvlist +using the +.Fn nvlist_destroy +function. +In case of descriptor, the caller is responsible for closing returned descriptor +using the +.Fn close 2 +system call. +If an element of the given name does not exist, the program will be aborted. +To avoid that the caller should check for the existence of the given name +before trying to obtain the value, or use the +.Xr dnvlist 3 +extension, which can provide a default value in the case of a missing element. +In the case of an array of strings or binary values, the caller is responsible +for freeing every element of the array using the +.Xr free 3 +function. +In the case of an array of nvlists, the caller is responsible for destroying +every element of array using the +.Fn nvlist_destroy +function. +In the case of descriptors, the caller is responsible for closing every +element of array using the +.Fn close 2 +system call. +In every case involving an array, the caller must also free the pointer to +the array using the +.Xr free 3 +function. +The nvlist must not be in error state. +.Pp +The +.Fn nvlist_free +function removes element of the given name from the nvlist (besides of its type) +and frees all resources associated with it. +If element of the given name does not exist, the program will be aborted. +The nvlist must not be in error state. +.Pp +The +.Fn nvlist_free_type +function removes element of the given name and the given type from the nvlist +and frees all resources associated with it. +If element of the given name and the given type does not exist, the program +will be aborted. +The nvlist must not be in error state. +.Pp +The +.Fn nvlist_free_null , +.Fn nvlist_free_bool , +.Fn nvlist_free_number , +.Fn nvlist_free_string , +.Fn nvlist_free_nvlist , +.Fn nvlist_free_descriptor , +.Fn nvlist_free_binary , +.Fn nvlist_free_bool_array , +.Fn nvlist_free_number_array , +.Fn nvlist_free_string_array , +.Fn nvlist_free_nvlist_array , +.Fn nvlist_free_descriptor_array +functions remove element of the given name and the given type determined by the +function name from the nvlist and free all resources associated with it. +If element of the given name and the given type does not exist, the program +will be aborted. +The nvlist must not be in error state. +.Sh EXAMPLES +The following example demonstrates how to prepare an nvlist and send it over +.Xr unix 4 +domain socket. +.Bd -literal +nvlist_t *nvl; +int fd; + +fd = open("/tmp/foo", O_RDONLY); +if (fd < 0) + err(1, "open(\\"/tmp/foo\\") failed"); + +nvl = nvlist_create(0); +/* + * There is no need to check if nvlist_create() succeeded, + * as the nvlist_add_() functions can cope. + * If it failed, nvlist_send() will fail. + */ +nvlist_add_string(nvl, "filename", "/tmp/foo"); +nvlist_add_number(nvl, "flags", O_RDONLY); +/* + * We just want to send the descriptor, so we can give it + * for the nvlist to consume (that's why we use nvlist_move + * not nvlist_add). + */ +nvlist_move_descriptor(nvl, "fd", fd); +if (nvlist_send(sock, nvl) < 0) { + nvlist_destroy(nvl); + err(1, "nvlist_send() failed"); +} +nvlist_destroy(nvl); +.Ed +.Pp +Receiving nvlist and getting data: +.Bd -literal +nvlist_t *nvl; +const char *command; +char *filename; +int fd; + +nvl = nvlist_recv(sock, 0); +if (nvl == NULL) + err(1, "nvlist_recv() failed"); + +/* For command we take pointer to nvlist's buffer. */ +command = nvlist_get_string(nvl, "command"); +/* + * For filename we remove it from the nvlist and take + * ownership of the buffer. + */ +filename = nvlist_take_string(nvl, "filename"); +/* The same for the descriptor. */ +fd = nvlist_take_descriptor(nvl, "fd"); + +printf("command=%s filename=%s fd=%d\n", command, filename, fd); + +nvlist_destroy(nvl); +free(filename); +close(fd); +/* command was freed by nvlist_destroy() */ +.Ed +.Pp +Iterating over nvlist: +.Bd -literal +nvlist_t *nvl; +const char *name; +void *cookie; +int type; + +nvl = nvlist_recv(sock, 0); +if (nvl == NULL) + err(1, "nvlist_recv() failed"); + +cookie = NULL; +while ((name = nvlist_next(nvl, &type, &cookie)) != NULL) { + printf("%s=", name); + switch (type) { + case NV_TYPE_NUMBER: + printf("%ju", (uintmax_t)nvlist_get_number(nvl, name)); + break; + case NV_TYPE_STRING: + printf("%s", nvlist_get_string(nvl, name)); + break; + default: + printf("N/A"); + break; + } + printf("\\n"); +} +.Ed +.Pp +Iterating over every nested nvlist: +.Bd -literal +nvlist_t *nvl; +const char *name; +void *cookie; +int type; + +nvl = nvlist_recv(sock, 0); +if (nvl == NULL) + err(1, "nvlist_recv() failed"); + +cookie = NULL; +do { + while ((name = nvlist_next(nvl, &type, &cookie)) != NULL) { + if (type == NV_TYPE_NVLIST) { + nvl = nvlist_get_nvlist(nvl, name); + cookie = NULL; + } + } +} while ((nvl = nvlist_get_parent(nvl, &cookie)) != NULL); +.Ed +.Pp +Iterating over every nested nvlist and every nvlist element: +.Bd -literal +nvlist_t *nvl; +const nvlist_t * const *array; +const char *name; +void *cookie; +int type; + +nvl = nvlist_recv(sock, 0); +if (nvl == null) + err(1, "nvlist_recv() failed"); + +cookie = null; +do { + while ((name = nvlist_next(nvl, &type, &cookie)) != NULL) { + if (type == NV_TYPE_NVLIST) { + nvl = nvlist_get_nvlist(nvl, name); + cookie = NULL; + } else if (type == NV_TYPE_NVLIST_ARRAY) { + nvl = nvlist_get_nvlist_array(nvl, name, NULL)[0]; + cookie = NULL; + } + } +} while ((nvl = nvlist_get_pararr(nvl, &cookie)) != NULL); +.Ed +.Pp +Or alternatively: +.Bd -literal +nvlist_t *nvl, *tmp; +const nvlist_t * const *array; +const char *name; +void *cookie; +int type; + +nvl = nvlist_recv(sock, 0); +if (nvl == null) + err(1, "nvlist_recv() failed"); + +cooke = NULL; +tmp = nvl; +do { + do { + nvl = tmp; + while ((name = nvlist_next(nvl, &type, &cookie)) != NULL) { + if (type == NV_TYPE_NVLIST) { + nvl = nvlist_get_nvlist(nvl, + name); + cookie = NULL; + } else if (type == NV_TYPE_NVLIST_ARRAY) { + nvl = nvlist_get_nvlist_array(nvl, name, + NULL)[0]; + cookie = NULL; + } + } + cookie = NULL; + } while ((tmp = nvlist_get_array_next(nvl)) != NULL); +} while ((tmp = nvlist_get_parent(nvl, &cookie)) != NULL); +.Ed +.Sh SEE ALSO +.Xr close 2 , +.Xr dup 2 , +.Xr open 2 , +.Xr err 3 , +.Xr free 3 , +.Xr printf 3 , +.Xr unix 4 +.Sh HISTORY +The +.Nm libnv +library appeared in +.Fx 11.0 . +.Sh AUTHORS +.An -nosplit +The +.Nm libnv +library was implemented by +.An Pawel Jakub Dawidek Aq Mt pawel@dawidek.net +under sponsorship from the FreeBSD Foundation. Index: share/mk/bsd.libnames.mk =================================================================== --- share/mk/bsd.libnames.mk +++ share/mk/bsd.libnames.mk @@ -100,6 +100,7 @@ LIBNCURSESW?= ${DESTDIR}${LIBDIR}/libncursesw.a LIBNETGRAPH?= ${DESTDIR}${LIBDIR}/libnetgraph.a LIBNGATM?= ${DESTDIR}${LIBDIR}/libngatm.a +LIBNV?= ${DESTDIR}${LIBDIR}/libnv.a LIBNVPAIR?= ${DESTDIR}${LIBDIR}/libnvpair.a LIBOPIE?= ${DESTDIR}${LIBDIR}/libopie.a Index: sys/cddl/contrib/opensolaris/common/nvpair/fnvpair.c =================================================================== --- sys/cddl/contrib/opensolaris/common/nvpair/fnvpair.c +++ /dev/null @@ -1,512 +0,0 @@ - -/* - * CDDL HEADER START - * - * The contents of this file are subject to the terms of the - * Common Development and Distribution License (the "License"). - * You may not use this file except in compliance with the License. - * - * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE - * or http://www.opensolaris.org/os/licensing. - * See the License for the specific language governing permissions - * and limitations under the License. - * - * When distributing Covered Code, include this CDDL HEADER in each - * file and include the License file at usr/src/OPENSOLARIS.LICENSE. - * If applicable, add the following below this CDDL HEADER, with the - * fields enclosed by brackets "[]" replaced with your own identifying - * information: Portions Copyright [yyyy] [name of copyright owner] - * - * CDDL HEADER END - */ - -/* - * Copyright (c) 2012 by Delphix. All rights reserved. - */ - -#include -#ifndef _KERNEL -#include -#else -#include -#include -#include -#include -#endif - -/* - * "Force" nvlist wrapper. - * - * These functions wrap the nvlist_* functions with assertions that assume - * the operation is successful. This allows the caller's code to be much - * more readable, especially for the fnvlist_lookup_* and fnvpair_value_* - * functions, which can return the requested value (rather than filling in - * a pointer). - * - * These functions use NV_UNIQUE_NAME, encoding NV_ENCODE_NATIVE, and allocate - * with KM_SLEEP. - * - * More wrappers should be added as needed -- for example - * nvlist_lookup_*_array and nvpair_value_*_array. - */ - -nvlist_t * -fnvlist_alloc(void) -{ - nvlist_t *nvl; - VERIFY0(nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP)); - return (nvl); -} - -void -fnvlist_free(nvlist_t *nvl) -{ - nvlist_free(nvl); -} - -size_t -fnvlist_size(nvlist_t *nvl) -{ - size_t size; - VERIFY0(nvlist_size(nvl, &size, NV_ENCODE_NATIVE)); - return (size); -} - -/* - * Returns allocated buffer of size *sizep. Caller must free the buffer with - * fnvlist_pack_free(). - */ -char * -fnvlist_pack(nvlist_t *nvl, size_t *sizep) -{ - char *packed = 0; - VERIFY3U(nvlist_pack(nvl, &packed, sizep, NV_ENCODE_NATIVE, - KM_SLEEP), ==, 0); - return (packed); -} - -/*ARGSUSED*/ -void -fnvlist_pack_free(char *pack, size_t size) -{ -#ifdef _KERNEL - kmem_free(pack, size); -#else - free(pack); -#endif -} - -nvlist_t * -fnvlist_unpack(char *buf, size_t buflen) -{ - nvlist_t *rv; - VERIFY0(nvlist_unpack(buf, buflen, &rv, KM_SLEEP)); - return (rv); -} - -nvlist_t * -fnvlist_dup(nvlist_t *nvl) -{ - nvlist_t *rv; - VERIFY0(nvlist_dup(nvl, &rv, KM_SLEEP)); - return (rv); -} - -void -fnvlist_merge(nvlist_t *dst, nvlist_t *src) -{ - VERIFY0(nvlist_merge(dst, src, KM_SLEEP)); -} - -size_t -fnvlist_num_pairs(nvlist_t *nvl) -{ - size_t count = 0; - nvpair_t *pair; - - for (pair = nvlist_next_nvpair(nvl, 0); pair != NULL; - pair = nvlist_next_nvpair(nvl, pair)) - count++; - return (count); -} - -void -fnvlist_add_boolean(nvlist_t *nvl, const char *name) -{ - VERIFY0(nvlist_add_boolean(nvl, name)); -} - -void -fnvlist_add_boolean_value(nvlist_t *nvl, const char *name, boolean_t val) -{ - VERIFY0(nvlist_add_boolean_value(nvl, name, val)); -} - -void -fnvlist_add_byte(nvlist_t *nvl, const char *name, uchar_t val) -{ - VERIFY0(nvlist_add_byte(nvl, name, val)); -} - -void -fnvlist_add_int8(nvlist_t *nvl, const char *name, int8_t val) -{ - VERIFY0(nvlist_add_int8(nvl, name, val)); -} - -void -fnvlist_add_uint8(nvlist_t *nvl, const char *name, uint8_t val) -{ - VERIFY0(nvlist_add_uint8(nvl, name, val)); -} - -void -fnvlist_add_int16(nvlist_t *nvl, const char *name, int16_t val) -{ - VERIFY0(nvlist_add_int16(nvl, name, val)); -} - -void -fnvlist_add_uint16(nvlist_t *nvl, const char *name, uint16_t val) -{ - VERIFY0(nvlist_add_uint16(nvl, name, val)); -} - -void -fnvlist_add_int32(nvlist_t *nvl, const char *name, int32_t val) -{ - VERIFY0(nvlist_add_int32(nvl, name, val)); -} - -void -fnvlist_add_uint32(nvlist_t *nvl, const char *name, uint32_t val) -{ - VERIFY0(nvlist_add_uint32(nvl, name, val)); -} - -void -fnvlist_add_int64(nvlist_t *nvl, const char *name, int64_t val) -{ - VERIFY0(nvlist_add_int64(nvl, name, val)); -} - -void -fnvlist_add_uint64(nvlist_t *nvl, const char *name, uint64_t val) -{ - VERIFY0(nvlist_add_uint64(nvl, name, val)); -} - -void -fnvlist_add_string(nvlist_t *nvl, const char *name, const char *val) -{ - VERIFY0(nvlist_add_string(nvl, name, val)); -} - -void -fnvlist_add_nvlist(nvlist_t *nvl, const char *name, nvlist_t *val) -{ - VERIFY0(nvlist_add_nvlist(nvl, name, val)); -} - -void -fnvlist_add_nvpair(nvlist_t *nvl, nvpair_t *pair) -{ - VERIFY0(nvlist_add_nvpair(nvl, pair)); -} - -void -fnvlist_add_boolean_array(nvlist_t *nvl, const char *name, - boolean_t *val, uint_t n) -{ - VERIFY0(nvlist_add_boolean_array(nvl, name, val, n)); -} - -void -fnvlist_add_byte_array(nvlist_t *nvl, const char *name, uchar_t *val, uint_t n) -{ - VERIFY0(nvlist_add_byte_array(nvl, name, val, n)); -} - -void -fnvlist_add_int8_array(nvlist_t *nvl, const char *name, int8_t *val, uint_t n) -{ - VERIFY0(nvlist_add_int8_array(nvl, name, val, n)); -} - -void -fnvlist_add_uint8_array(nvlist_t *nvl, const char *name, uint8_t *val, uint_t n) -{ - VERIFY0(nvlist_add_uint8_array(nvl, name, val, n)); -} - -void -fnvlist_add_int16_array(nvlist_t *nvl, const char *name, int16_t *val, uint_t n) -{ - VERIFY0(nvlist_add_int16_array(nvl, name, val, n)); -} - -void -fnvlist_add_uint16_array(nvlist_t *nvl, const char *name, - uint16_t *val, uint_t n) -{ - VERIFY0(nvlist_add_uint16_array(nvl, name, val, n)); -} - -void -fnvlist_add_int32_array(nvlist_t *nvl, const char *name, int32_t *val, uint_t n) -{ - VERIFY0(nvlist_add_int32_array(nvl, name, val, n)); -} - -void -fnvlist_add_uint32_array(nvlist_t *nvl, const char *name, - uint32_t *val, uint_t n) -{ - VERIFY0(nvlist_add_uint32_array(nvl, name, val, n)); -} - -void -fnvlist_add_int64_array(nvlist_t *nvl, const char *name, int64_t *val, uint_t n) -{ - VERIFY0(nvlist_add_int64_array(nvl, name, val, n)); -} - -void -fnvlist_add_uint64_array(nvlist_t *nvl, const char *name, - uint64_t *val, uint_t n) -{ - VERIFY0(nvlist_add_uint64_array(nvl, name, val, n)); -} - -void -fnvlist_add_string_array(nvlist_t *nvl, const char *name, - char * const *val, uint_t n) -{ - VERIFY0(nvlist_add_string_array(nvl, name, val, n)); -} - -void -fnvlist_add_nvlist_array(nvlist_t *nvl, const char *name, - nvlist_t **val, uint_t n) -{ - VERIFY0(nvlist_add_nvlist_array(nvl, name, val, n)); -} - -void -fnvlist_remove(nvlist_t *nvl, const char *name) -{ - VERIFY0(nvlist_remove_all(nvl, name)); -} - -void -fnvlist_remove_nvpair(nvlist_t *nvl, nvpair_t *pair) -{ - VERIFY0(nvlist_remove_nvpair(nvl, pair)); -} - -nvpair_t * -fnvlist_lookup_nvpair(nvlist_t *nvl, const char *name) -{ - nvpair_t *rv; - VERIFY0(nvlist_lookup_nvpair(nvl, name, &rv)); - return (rv); -} - -/* returns B_TRUE if the entry exists */ -boolean_t -fnvlist_lookup_boolean(nvlist_t *nvl, const char *name) -{ - return (nvlist_lookup_boolean(nvl, name) == 0); -} - -boolean_t -fnvlist_lookup_boolean_value(nvlist_t *nvl, const char *name) -{ - boolean_t rv; - VERIFY0(nvlist_lookup_boolean_value(nvl, name, &rv)); - return (rv); -} - -uchar_t -fnvlist_lookup_byte(nvlist_t *nvl, const char *name) -{ - uchar_t rv; - VERIFY0(nvlist_lookup_byte(nvl, name, &rv)); - return (rv); -} - -int8_t -fnvlist_lookup_int8(nvlist_t *nvl, const char *name) -{ - int8_t rv; - VERIFY0(nvlist_lookup_int8(nvl, name, &rv)); - return (rv); -} - -int16_t -fnvlist_lookup_int16(nvlist_t *nvl, const char *name) -{ - int16_t rv; - VERIFY0(nvlist_lookup_int16(nvl, name, &rv)); - return (rv); -} - -int32_t -fnvlist_lookup_int32(nvlist_t *nvl, const char *name) -{ - int32_t rv; - VERIFY0(nvlist_lookup_int32(nvl, name, &rv)); - return (rv); -} - -int64_t -fnvlist_lookup_int64(nvlist_t *nvl, const char *name) -{ - int64_t rv; - VERIFY0(nvlist_lookup_int64(nvl, name, &rv)); - return (rv); -} - -uint8_t -fnvlist_lookup_uint8_t(nvlist_t *nvl, const char *name) -{ - uint8_t rv; - VERIFY0(nvlist_lookup_uint8(nvl, name, &rv)); - return (rv); -} - -uint16_t -fnvlist_lookup_uint16(nvlist_t *nvl, const char *name) -{ - uint16_t rv; - VERIFY0(nvlist_lookup_uint16(nvl, name, &rv)); - return (rv); -} - -uint32_t -fnvlist_lookup_uint32(nvlist_t *nvl, const char *name) -{ - uint32_t rv; - VERIFY0(nvlist_lookup_uint32(nvl, name, &rv)); - return (rv); -} - -uint64_t -fnvlist_lookup_uint64(nvlist_t *nvl, const char *name) -{ - uint64_t rv; - VERIFY0(nvlist_lookup_uint64(nvl, name, &rv)); - return (rv); -} - -char * -fnvlist_lookup_string(nvlist_t *nvl, const char *name) -{ - char *rv; - VERIFY0(nvlist_lookup_string(nvl, name, &rv)); - return (rv); -} - -nvlist_t * -fnvlist_lookup_nvlist(nvlist_t *nvl, const char *name) -{ - nvlist_t *rv; - VERIFY0(nvlist_lookup_nvlist(nvl, name, &rv)); - return (rv); -} - -boolean_t -fnvpair_value_boolean_value(nvpair_t *nvp) -{ - boolean_t rv; - VERIFY0(nvpair_value_boolean_value(nvp, &rv)); - return (rv); -} - -uchar_t -fnvpair_value_byte(nvpair_t *nvp) -{ - uchar_t rv; - VERIFY0(nvpair_value_byte(nvp, &rv)); - return (rv); -} - -int8_t -fnvpair_value_int8(nvpair_t *nvp) -{ - int8_t rv; - VERIFY0(nvpair_value_int8(nvp, &rv)); - return (rv); -} - -int16_t -fnvpair_value_int16(nvpair_t *nvp) -{ - int16_t rv; - VERIFY0(nvpair_value_int16(nvp, &rv)); - return (rv); -} - -int32_t -fnvpair_value_int32(nvpair_t *nvp) -{ - int32_t rv; - VERIFY0(nvpair_value_int32(nvp, &rv)); - return (rv); -} - -int64_t -fnvpair_value_int64(nvpair_t *nvp) -{ - int64_t rv; - VERIFY0(nvpair_value_int64(nvp, &rv)); - return (rv); -} - -uint8_t -fnvpair_value_uint8_t(nvpair_t *nvp) -{ - uint8_t rv; - VERIFY0(nvpair_value_uint8(nvp, &rv)); - return (rv); -} - -uint16_t -fnvpair_value_uint16(nvpair_t *nvp) -{ - uint16_t rv; - VERIFY0(nvpair_value_uint16(nvp, &rv)); - return (rv); -} - -uint32_t -fnvpair_value_uint32(nvpair_t *nvp) -{ - uint32_t rv; - VERIFY0(nvpair_value_uint32(nvp, &rv)); - return (rv); -} - -uint64_t -fnvpair_value_uint64(nvpair_t *nvp) -{ - uint64_t rv; - VERIFY0(nvpair_value_uint64(nvp, &rv)); - return (rv); -} - -char * -fnvpair_value_string(nvpair_t *nvp) -{ - char *rv; - VERIFY0(nvpair_value_string(nvp, &rv)); - return (rv); -} - -nvlist_t * -fnvpair_value_nvlist(nvpair_t *nvp) -{ - nvlist_t *rv; - VERIFY0(nvpair_value_nvlist(nvp, &rv)); - return (rv); -} Index: sys/cddl/contrib/opensolaris/common/nvpair/nvpair.c =================================================================== --- sys/cddl/contrib/opensolaris/common/nvpair/nvpair.c +++ /dev/null @@ -1,3306 +0,0 @@ -/* - * CDDL HEADER START - * - * The contents of this file are subject to the terms of the - * Common Development and Distribution License (the "License"). - * You may not use this file except in compliance with the License. - * - * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE - * or http://www.opensolaris.org/os/licensing. - * See the License for the specific language governing permissions - * and limitations under the License. - * - * When distributing Covered Code, include this CDDL HEADER in each - * file and include the License file at usr/src/OPENSOLARIS.LICENSE. - * If applicable, add the following below this CDDL HEADER, with the - * fields enclosed by brackets "[]" replaced with your own identifying - * information: Portions Copyright [yyyy] [name of copyright owner] - * - * CDDL HEADER END - */ - -/* - * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. - */ - -#include -#include -#include -#include -#include - -#if defined(_KERNEL) && !defined(_BOOT) -#include -#include -#else -#include -#include -#include -#include -#endif - -#ifndef offsetof -#define offsetof(s, m) ((size_t)(&(((s *)0)->m))) -#endif -#define skip_whitespace(p) while ((*(p) == ' ') || (*(p) == '\t')) p++ - -#if defined(__FreeBSD__) && !defined(_KERNEL) -/* - * libnvpair is the lowest commen denominator for ZFS related libraries, - * defining aok here makes it usable by all ZFS related libraries - */ -int aok; -#endif - -/* - * nvpair.c - Provides kernel & userland interfaces for manipulating - * name-value pairs. - * - * Overview Diagram - * - * +--------------+ - * | nvlist_t | - * |--------------| - * | nvl_version | - * | nvl_nvflag | - * | nvl_priv -+-+ - * | nvl_flag | | - * | nvl_pad | | - * +--------------+ | - * V - * +--------------+ last i_nvp in list - * | nvpriv_t | +---------------------> - * |--------------| | - * +--+- nvp_list | | +------------+ - * | | nvp_last -+--+ + nv_alloc_t | - * | | nvp_curr | |------------| - * | | nvp_nva -+----> | nva_ops | - * | | nvp_stat | | nva_arg | - * | +--------------+ +------------+ - * | - * +-------+ - * V - * +---------------------+ +-------------------+ - * | i_nvp_t | +-->| i_nvp_t | +--> - * |---------------------| | |-------------------| | - * | nvi_next -+--+ | nvi_next -+--+ - * | nvi_prev (NULL) | <----+ nvi_prev | - * | . . . . . . . . . . | | . . . . . . . . . | - * | nvp (nvpair_t) | | nvp (nvpair_t) | - * | - nvp_size | | - nvp_size | - * | - nvp_name_sz | | - nvp_name_sz | - * | - nvp_value_elem | | - nvp_value_elem | - * | - nvp_type | | - nvp_type | - * | - data ... | | - data ... | - * +---------------------+ +-------------------+ - * - * - * - * +---------------------+ +---------------------+ - * | i_nvp_t | +--> +-->| i_nvp_t (last) | - * |---------------------| | | |---------------------| - * | nvi_next -+--+ ... --+ | nvi_next (NULL) | - * <-+- nvi_prev |<-- ... <----+ nvi_prev | - * | . . . . . . . . . | | . . . . . . . . . | - * | nvp (nvpair_t) | | nvp (nvpair_t) | - * | - nvp_size | | - nvp_size | - * | - nvp_name_sz | | - nvp_name_sz | - * | - nvp_value_elem | | - nvp_value_elem | - * | - DATA_TYPE_NVLIST | | - nvp_type | - * | - data (embedded) | | - data ... | - * | nvlist name | +---------------------+ - * | +--------------+ | - * | | nvlist_t | | - * | |--------------| | - * | | nvl_version | | - * | | nvl_nvflag | | - * | | nvl_priv --+---+----> - * | | nvl_flag | | - * | | nvl_pad | | - * | +--------------+ | - * +---------------------+ - * - * - * N.B. nvpair_t may be aligned on 4 byte boundary, so +4 will - * allow value to be aligned on 8 byte boundary - * - * name_len is the length of the name string including the null terminator - * so it must be >= 1 - */ -#define NVP_SIZE_CALC(name_len, data_len) \ - (NV_ALIGN((sizeof (nvpair_t)) + name_len) + NV_ALIGN(data_len)) - -static int i_get_value_size(data_type_t type, const void *data, uint_t nelem); -static int nvlist_add_common(nvlist_t *nvl, const char *name, data_type_t type, - uint_t nelem, const void *data); - -#define NV_STAT_EMBEDDED 0x1 -#define EMBEDDED_NVL(nvp) ((nvlist_t *)(void *)NVP_VALUE(nvp)) -#define EMBEDDED_NVL_ARRAY(nvp) ((nvlist_t **)(void *)NVP_VALUE(nvp)) - -#define NVP_VALOFF(nvp) (NV_ALIGN(sizeof (nvpair_t) + (nvp)->nvp_name_sz)) -#define NVPAIR2I_NVP(nvp) \ - ((i_nvp_t *)((size_t)(nvp) - offsetof(i_nvp_t, nvi_nvp))) - - -int -nv_alloc_init(nv_alloc_t *nva, const nv_alloc_ops_t *nvo, /* args */ ...) -{ - va_list valist; - int err = 0; - - nva->nva_ops = nvo; - nva->nva_arg = NULL; - - va_start(valist, nvo); - if (nva->nva_ops->nv_ao_init != NULL) - err = nva->nva_ops->nv_ao_init(nva, valist); - va_end(valist); - - return (err); -} - -void -nv_alloc_reset(nv_alloc_t *nva) -{ - if (nva->nva_ops->nv_ao_reset != NULL) - nva->nva_ops->nv_ao_reset(nva); -} - -void -nv_alloc_fini(nv_alloc_t *nva) -{ - if (nva->nva_ops->nv_ao_fini != NULL) - nva->nva_ops->nv_ao_fini(nva); -} - -nv_alloc_t * -nvlist_lookup_nv_alloc(nvlist_t *nvl) -{ - nvpriv_t *priv; - - if (nvl == NULL || - (priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv) == NULL) - return (NULL); - - return (priv->nvp_nva); -} - -static void * -nv_mem_zalloc(nvpriv_t *nvp, size_t size) -{ - nv_alloc_t *nva = nvp->nvp_nva; - void *buf; - - if ((buf = nva->nva_ops->nv_ao_alloc(nva, size)) != NULL) - bzero(buf, size); - - return (buf); -} - -static void -nv_mem_free(nvpriv_t *nvp, void *buf, size_t size) -{ - nv_alloc_t *nva = nvp->nvp_nva; - - nva->nva_ops->nv_ao_free(nva, buf, size); -} - -static void -nv_priv_init(nvpriv_t *priv, nv_alloc_t *nva, uint32_t stat) -{ - bzero(priv, sizeof (nvpriv_t)); - - priv->nvp_nva = nva; - priv->nvp_stat = stat; -} - -static nvpriv_t * -nv_priv_alloc(nv_alloc_t *nva) -{ - nvpriv_t *priv; - - /* - * nv_mem_alloc() cannot called here because it needs the priv - * argument. - */ - if ((priv = nva->nva_ops->nv_ao_alloc(nva, sizeof (nvpriv_t))) == NULL) - return (NULL); - - nv_priv_init(priv, nva, 0); - - return (priv); -} - -/* - * Embedded lists need their own nvpriv_t's. We create a new - * nvpriv_t using the parameters and allocator from the parent - * list's nvpriv_t. - */ -static nvpriv_t * -nv_priv_alloc_embedded(nvpriv_t *priv) -{ - nvpriv_t *emb_priv; - - if ((emb_priv = nv_mem_zalloc(priv, sizeof (nvpriv_t))) == NULL) - return (NULL); - - nv_priv_init(emb_priv, priv->nvp_nva, NV_STAT_EMBEDDED); - - return (emb_priv); -} - -static void -nvlist_init(nvlist_t *nvl, uint32_t nvflag, nvpriv_t *priv) -{ - nvl->nvl_version = NV_VERSION; - nvl->nvl_nvflag = nvflag & (NV_UNIQUE_NAME|NV_UNIQUE_NAME_TYPE); - nvl->nvl_priv = (uint64_t)(uintptr_t)priv; - nvl->nvl_flag = 0; - nvl->nvl_pad = 0; -} - -uint_t -nvlist_nvflag(nvlist_t *nvl) -{ - return (nvl->nvl_nvflag); -} - -/* - * nvlist_alloc - Allocate nvlist. - */ -/*ARGSUSED1*/ -int -nvlist_alloc(nvlist_t **nvlp, uint_t nvflag, int kmflag) -{ -#if defined(_KERNEL) && !defined(_BOOT) - return (nvlist_xalloc(nvlp, nvflag, - (kmflag == KM_SLEEP ? nv_alloc_sleep : nv_alloc_nosleep))); -#else - return (nvlist_xalloc(nvlp, nvflag, nv_alloc_nosleep)); -#endif -} - -int -nvlist_xalloc(nvlist_t **nvlp, uint_t nvflag, nv_alloc_t *nva) -{ - nvpriv_t *priv; - - if (nvlp == NULL || nva == NULL) - return (EINVAL); - - if ((priv = nv_priv_alloc(nva)) == NULL) - return (ENOMEM); - - if ((*nvlp = nv_mem_zalloc(priv, - NV_ALIGN(sizeof (nvlist_t)))) == NULL) { - nv_mem_free(priv, priv, sizeof (nvpriv_t)); - return (ENOMEM); - } - - nvlist_init(*nvlp, nvflag, priv); - - return (0); -} - -/* - * nvp_buf_alloc - Allocate i_nvp_t for storing a new nv pair. - */ -static nvpair_t * -nvp_buf_alloc(nvlist_t *nvl, size_t len) -{ - nvpriv_t *priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv; - i_nvp_t *buf; - nvpair_t *nvp; - size_t nvsize; - - /* - * Allocate the buffer - */ - nvsize = len + offsetof(i_nvp_t, nvi_nvp); - - if ((buf = nv_mem_zalloc(priv, nvsize)) == NULL) - return (NULL); - - nvp = &buf->nvi_nvp; - nvp->nvp_size = len; - - return (nvp); -} - -/* - * nvp_buf_free - de-Allocate an i_nvp_t. - */ -static void -nvp_buf_free(nvlist_t *nvl, nvpair_t *nvp) -{ - nvpriv_t *priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv; - size_t nvsize = nvp->nvp_size + offsetof(i_nvp_t, nvi_nvp); - - nv_mem_free(priv, NVPAIR2I_NVP(nvp), nvsize); -} - -/* - * nvp_buf_link - link a new nv pair into the nvlist. - */ -static void -nvp_buf_link(nvlist_t *nvl, nvpair_t *nvp) -{ - nvpriv_t *priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv; - i_nvp_t *curr = NVPAIR2I_NVP(nvp); - - /* Put element at end of nvlist */ - if (priv->nvp_list == NULL) { - priv->nvp_list = priv->nvp_last = curr; - } else { - curr->nvi_prev = priv->nvp_last; - priv->nvp_last->nvi_next = curr; - priv->nvp_last = curr; - } -} - -/* - * nvp_buf_unlink - unlink an removed nvpair out of the nvlist. - */ -static void -nvp_buf_unlink(nvlist_t *nvl, nvpair_t *nvp) -{ - nvpriv_t *priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv; - i_nvp_t *curr = NVPAIR2I_NVP(nvp); - - /* - * protect nvlist_next_nvpair() against walking on freed memory. - */ - if (priv->nvp_curr == curr) - priv->nvp_curr = curr->nvi_next; - - if (curr == priv->nvp_list) - priv->nvp_list = curr->nvi_next; - else - curr->nvi_prev->nvi_next = curr->nvi_next; - - if (curr == priv->nvp_last) - priv->nvp_last = curr->nvi_prev; - else - curr->nvi_next->nvi_prev = curr->nvi_prev; -} - -/* - * take a nvpair type and number of elements and make sure the are valid - */ -static int -i_validate_type_nelem(data_type_t type, uint_t nelem) -{ - switch (type) { - case DATA_TYPE_BOOLEAN: - if (nelem != 0) - return (EINVAL); - break; - case DATA_TYPE_BOOLEAN_VALUE: - case DATA_TYPE_BYTE: - case DATA_TYPE_INT8: - case DATA_TYPE_UINT8: - case DATA_TYPE_INT16: - case DATA_TYPE_UINT16: - case DATA_TYPE_INT32: - case DATA_TYPE_UINT32: - case DATA_TYPE_INT64: - case DATA_TYPE_UINT64: - case DATA_TYPE_STRING: - case DATA_TYPE_HRTIME: - case DATA_TYPE_NVLIST: -#if !defined(_KERNEL) - case DATA_TYPE_DOUBLE: -#endif - if (nelem != 1) - return (EINVAL); - break; - case DATA_TYPE_BOOLEAN_ARRAY: - case DATA_TYPE_BYTE_ARRAY: - case DATA_TYPE_INT8_ARRAY: - case DATA_TYPE_UINT8_ARRAY: - case DATA_TYPE_INT16_ARRAY: - case DATA_TYPE_UINT16_ARRAY: - case DATA_TYPE_INT32_ARRAY: - case DATA_TYPE_UINT32_ARRAY: - case DATA_TYPE_INT64_ARRAY: - case DATA_TYPE_UINT64_ARRAY: - case DATA_TYPE_STRING_ARRAY: - case DATA_TYPE_NVLIST_ARRAY: - /* we allow arrays with 0 elements */ - break; - default: - return (EINVAL); - } - return (0); -} - -/* - * Verify nvp_name_sz and check the name string length. - */ -static int -i_validate_nvpair_name(nvpair_t *nvp) -{ - if ((nvp->nvp_name_sz <= 0) || - (nvp->nvp_size < NVP_SIZE_CALC(nvp->nvp_name_sz, 0))) - return (EFAULT); - - /* verify the name string, make sure its terminated */ - if (NVP_NAME(nvp)[nvp->nvp_name_sz - 1] != '\0') - return (EFAULT); - - return (strlen(NVP_NAME(nvp)) == nvp->nvp_name_sz - 1 ? 0 : EFAULT); -} - -static int -i_validate_nvpair_value(data_type_t type, uint_t nelem, const void *data) -{ - switch (type) { - case DATA_TYPE_BOOLEAN_VALUE: - if (*(boolean_t *)data != B_TRUE && - *(boolean_t *)data != B_FALSE) - return (EINVAL); - break; - case DATA_TYPE_BOOLEAN_ARRAY: { - int i; - - for (i = 0; i < nelem; i++) - if (((boolean_t *)data)[i] != B_TRUE && - ((boolean_t *)data)[i] != B_FALSE) - return (EINVAL); - break; - } - default: - break; - } - - return (0); -} - -/* - * This function takes a pointer to what should be a nvpair and it's size - * and then verifies that all the nvpair fields make sense and can be - * trusted. This function is used when decoding packed nvpairs. - */ -static int -i_validate_nvpair(nvpair_t *nvp) -{ - data_type_t type = NVP_TYPE(nvp); - int size1, size2; - - /* verify nvp_name_sz, check the name string length */ - if (i_validate_nvpair_name(nvp) != 0) - return (EFAULT); - - if (i_validate_nvpair_value(type, NVP_NELEM(nvp), NVP_VALUE(nvp)) != 0) - return (EFAULT); - - /* - * verify nvp_type, nvp_value_elem, and also possibly - * verify string values and get the value size. - */ - size2 = i_get_value_size(type, NVP_VALUE(nvp), NVP_NELEM(nvp)); - size1 = nvp->nvp_size - NVP_VALOFF(nvp); - if (size2 < 0 || size1 != NV_ALIGN(size2)) - return (EFAULT); - - return (0); -} - -static int -nvlist_copy_pairs(nvlist_t *snvl, nvlist_t *dnvl) -{ - nvpriv_t *priv; - i_nvp_t *curr; - - if ((priv = (nvpriv_t *)(uintptr_t)snvl->nvl_priv) == NULL) - return (EINVAL); - - for (curr = priv->nvp_list; curr != NULL; curr = curr->nvi_next) { - nvpair_t *nvp = &curr->nvi_nvp; - int err; - - if ((err = nvlist_add_common(dnvl, NVP_NAME(nvp), NVP_TYPE(nvp), - NVP_NELEM(nvp), NVP_VALUE(nvp))) != 0) - return (err); - } - - return (0); -} - -/* - * Frees all memory allocated for an nvpair (like embedded lists) with - * the exception of the nvpair buffer itself. - */ -static void -nvpair_free(nvpair_t *nvp) -{ - switch (NVP_TYPE(nvp)) { - case DATA_TYPE_NVLIST: - nvlist_free(EMBEDDED_NVL(nvp)); - break; - case DATA_TYPE_NVLIST_ARRAY: { - nvlist_t **nvlp = EMBEDDED_NVL_ARRAY(nvp); - int i; - - for (i = 0; i < NVP_NELEM(nvp); i++) - if (nvlp[i] != NULL) - nvlist_free(nvlp[i]); - break; - } - default: - break; - } -} - -/* - * nvlist_free - free an unpacked nvlist - */ -void -nvlist_free(nvlist_t *nvl) -{ - nvpriv_t *priv; - i_nvp_t *curr; - - if (nvl == NULL || - (priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv) == NULL) - return; - - /* - * Unpacked nvlist are linked through i_nvp_t - */ - curr = priv->nvp_list; - while (curr != NULL) { - nvpair_t *nvp = &curr->nvi_nvp; - curr = curr->nvi_next; - - nvpair_free(nvp); - nvp_buf_free(nvl, nvp); - } - - if (!(priv->nvp_stat & NV_STAT_EMBEDDED)) - nv_mem_free(priv, nvl, NV_ALIGN(sizeof (nvlist_t))); - else - nvl->nvl_priv = 0; - - nv_mem_free(priv, priv, sizeof (nvpriv_t)); -} - -static int -nvlist_contains_nvp(nvlist_t *nvl, nvpair_t *nvp) -{ - nvpriv_t *priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv; - i_nvp_t *curr; - - if (nvp == NULL) - return (0); - - for (curr = priv->nvp_list; curr != NULL; curr = curr->nvi_next) - if (&curr->nvi_nvp == nvp) - return (1); - - return (0); -} - -/* - * Make a copy of nvlist - */ -/*ARGSUSED1*/ -int -nvlist_dup(nvlist_t *nvl, nvlist_t **nvlp, int kmflag) -{ -#if defined(_KERNEL) && !defined(_BOOT) - return (nvlist_xdup(nvl, nvlp, - (kmflag == KM_SLEEP ? nv_alloc_sleep : nv_alloc_nosleep))); -#else - return (nvlist_xdup(nvl, nvlp, nv_alloc_nosleep)); -#endif -} - -int -nvlist_xdup(nvlist_t *nvl, nvlist_t **nvlp, nv_alloc_t *nva) -{ - int err; - nvlist_t *ret; - - if (nvl == NULL || nvlp == NULL) - return (EINVAL); - - if ((err = nvlist_xalloc(&ret, nvl->nvl_nvflag, nva)) != 0) - return (err); - - if ((err = nvlist_copy_pairs(nvl, ret)) != 0) - nvlist_free(ret); - else - *nvlp = ret; - - return (err); -} - -/* - * Remove all with matching name - */ -int -nvlist_remove_all(nvlist_t *nvl, const char *name) -{ - nvpriv_t *priv; - i_nvp_t *curr; - int error = ENOENT; - - if (nvl == NULL || name == NULL || - (priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv) == NULL) - return (EINVAL); - - curr = priv->nvp_list; - while (curr != NULL) { - nvpair_t *nvp = &curr->nvi_nvp; - - curr = curr->nvi_next; - if (strcmp(name, NVP_NAME(nvp)) != 0) - continue; - - nvp_buf_unlink(nvl, nvp); - nvpair_free(nvp); - nvp_buf_free(nvl, nvp); - - error = 0; - } - - return (error); -} - -/* - * Remove first one with matching name and type - */ -int -nvlist_remove(nvlist_t *nvl, const char *name, data_type_t type) -{ - nvpriv_t *priv; - i_nvp_t *curr; - - if (nvl == NULL || name == NULL || - (priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv) == NULL) - return (EINVAL); - - curr = priv->nvp_list; - while (curr != NULL) { - nvpair_t *nvp = &curr->nvi_nvp; - - if (strcmp(name, NVP_NAME(nvp)) == 0 && NVP_TYPE(nvp) == type) { - nvp_buf_unlink(nvl, nvp); - nvpair_free(nvp); - nvp_buf_free(nvl, nvp); - - return (0); - } - curr = curr->nvi_next; - } - - return (ENOENT); -} - -int -nvlist_remove_nvpair(nvlist_t *nvl, nvpair_t *nvp) -{ - if (nvl == NULL || nvp == NULL) - return (EINVAL); - - nvp_buf_unlink(nvl, nvp); - nvpair_free(nvp); - nvp_buf_free(nvl, nvp); - return (0); -} - -/* - * This function calculates the size of an nvpair value. - * - * The data argument controls the behavior in case of the data types - * DATA_TYPE_STRING and - * DATA_TYPE_STRING_ARRAY - * Is data == NULL then the size of the string(s) is excluded. - */ -static int -i_get_value_size(data_type_t type, const void *data, uint_t nelem) -{ - uint64_t value_sz; - - if (i_validate_type_nelem(type, nelem) != 0) - return (-1); - - /* Calculate required size for holding value */ - switch (type) { - case DATA_TYPE_BOOLEAN: - value_sz = 0; - break; - case DATA_TYPE_BOOLEAN_VALUE: - value_sz = sizeof (boolean_t); - break; - case DATA_TYPE_BYTE: - value_sz = sizeof (uchar_t); - break; - case DATA_TYPE_INT8: - value_sz = sizeof (int8_t); - break; - case DATA_TYPE_UINT8: - value_sz = sizeof (uint8_t); - break; - case DATA_TYPE_INT16: - value_sz = sizeof (int16_t); - break; - case DATA_TYPE_UINT16: - value_sz = sizeof (uint16_t); - break; - case DATA_TYPE_INT32: - value_sz = sizeof (int32_t); - break; - case DATA_TYPE_UINT32: - value_sz = sizeof (uint32_t); - break; - case DATA_TYPE_INT64: - value_sz = sizeof (int64_t); - break; - case DATA_TYPE_UINT64: - value_sz = sizeof (uint64_t); - break; -#if !defined(_KERNEL) - case DATA_TYPE_DOUBLE: - value_sz = sizeof (double); - break; -#endif - case DATA_TYPE_STRING: - if (data == NULL) - value_sz = 0; - else - value_sz = strlen(data) + 1; - break; - case DATA_TYPE_BOOLEAN_ARRAY: - value_sz = (uint64_t)nelem * sizeof (boolean_t); - break; - case DATA_TYPE_BYTE_ARRAY: - value_sz = (uint64_t)nelem * sizeof (uchar_t); - break; - case DATA_TYPE_INT8_ARRAY: - value_sz = (uint64_t)nelem * sizeof (int8_t); - break; - case DATA_TYPE_UINT8_ARRAY: - value_sz = (uint64_t)nelem * sizeof (uint8_t); - break; - case DATA_TYPE_INT16_ARRAY: - value_sz = (uint64_t)nelem * sizeof (int16_t); - break; - case DATA_TYPE_UINT16_ARRAY: - value_sz = (uint64_t)nelem * sizeof (uint16_t); - break; - case DATA_TYPE_INT32_ARRAY: - value_sz = (uint64_t)nelem * sizeof (int32_t); - break; - case DATA_TYPE_UINT32_ARRAY: - value_sz = (uint64_t)nelem * sizeof (uint32_t); - break; - case DATA_TYPE_INT64_ARRAY: - value_sz = (uint64_t)nelem * sizeof (int64_t); - break; - case DATA_TYPE_UINT64_ARRAY: - value_sz = (uint64_t)nelem * sizeof (uint64_t); - break; - case DATA_TYPE_STRING_ARRAY: - value_sz = (uint64_t)nelem * sizeof (uint64_t); - - if (data != NULL) { - char *const *strs = data; - uint_t i; - - /* no alignment requirement for strings */ - for (i = 0; i < nelem; i++) { - if (strs[i] == NULL) - return (-1); - value_sz += strlen(strs[i]) + 1; - } - } - break; - case DATA_TYPE_HRTIME: - value_sz = sizeof (hrtime_t); - break; - case DATA_TYPE_NVLIST: - value_sz = NV_ALIGN(sizeof (nvlist_t)); - break; - case DATA_TYPE_NVLIST_ARRAY: - value_sz = (uint64_t)nelem * sizeof (uint64_t) + - (uint64_t)nelem * NV_ALIGN(sizeof (nvlist_t)); - break; - default: - return (-1); - } - - return (value_sz > INT32_MAX ? -1 : (int)value_sz); -} - -static int -nvlist_copy_embedded(nvlist_t *nvl, nvlist_t *onvl, nvlist_t *emb_nvl) -{ - nvpriv_t *priv; - int err; - - if ((priv = nv_priv_alloc_embedded((nvpriv_t *)(uintptr_t) - nvl->nvl_priv)) == NULL) - return (ENOMEM); - - nvlist_init(emb_nvl, onvl->nvl_nvflag, priv); - - if ((err = nvlist_copy_pairs(onvl, emb_nvl)) != 0) { - nvlist_free(emb_nvl); - emb_nvl->nvl_priv = 0; - } - - return (err); -} - -/* - * nvlist_add_common - Add new pair to nvlist - */ -static int -nvlist_add_common(nvlist_t *nvl, const char *name, - data_type_t type, uint_t nelem, const void *data) -{ - nvpair_t *nvp; - uint_t i; - - int nvp_sz, name_sz, value_sz; - int err = 0; - - if (name == NULL || nvl == NULL || nvl->nvl_priv == 0) - return (EINVAL); - - if (nelem != 0 && data == NULL) - return (EINVAL); - - /* - * Verify type and nelem and get the value size. - * In case of data types DATA_TYPE_STRING and DATA_TYPE_STRING_ARRAY - * is the size of the string(s) included. - */ - if ((value_sz = i_get_value_size(type, data, nelem)) < 0) - return (EINVAL); - - if (i_validate_nvpair_value(type, nelem, data) != 0) - return (EINVAL); - - /* - * If we're adding an nvlist or nvlist array, ensure that we are not - * adding the input nvlist to itself, which would cause recursion, - * and ensure that no NULL nvlist pointers are present. - */ - switch (type) { - case DATA_TYPE_NVLIST: - if (data == nvl || data == NULL) - return (EINVAL); - break; - case DATA_TYPE_NVLIST_ARRAY: { - nvlist_t **onvlp = (nvlist_t **)data; - for (i = 0; i < nelem; i++) { - if (onvlp[i] == nvl || onvlp[i] == NULL) - return (EINVAL); - } - break; - } - default: - break; - } - - /* calculate sizes of the nvpair elements and the nvpair itself */ - name_sz = strlen(name) + 1; - - nvp_sz = NVP_SIZE_CALC(name_sz, value_sz); - - if ((nvp = nvp_buf_alloc(nvl, nvp_sz)) == NULL) - return (ENOMEM); - - ASSERT(nvp->nvp_size == nvp_sz); - nvp->nvp_name_sz = name_sz; - nvp->nvp_value_elem = nelem; - nvp->nvp_type = type; - bcopy(name, NVP_NAME(nvp), name_sz); - - switch (type) { - case DATA_TYPE_BOOLEAN: - break; - case DATA_TYPE_STRING_ARRAY: { - char *const *strs = data; - char *buf = NVP_VALUE(nvp); - char **cstrs = (void *)buf; - - /* skip pre-allocated space for pointer array */ - buf += nelem * sizeof (uint64_t); - for (i = 0; i < nelem; i++) { - int slen = strlen(strs[i]) + 1; - bcopy(strs[i], buf, slen); - cstrs[i] = buf; - buf += slen; - } - break; - } - case DATA_TYPE_NVLIST: { - nvlist_t *nnvl = EMBEDDED_NVL(nvp); - nvlist_t *onvl = (nvlist_t *)data; - - if ((err = nvlist_copy_embedded(nvl, onvl, nnvl)) != 0) { - nvp_buf_free(nvl, nvp); - return (err); - } - break; - } - case DATA_TYPE_NVLIST_ARRAY: { - nvlist_t **onvlp = (nvlist_t **)data; - nvlist_t **nvlp = EMBEDDED_NVL_ARRAY(nvp); - nvlist_t *embedded = (nvlist_t *) - ((uintptr_t)nvlp + nelem * sizeof (uint64_t)); - - for (i = 0; i < nelem; i++) { - if ((err = nvlist_copy_embedded(nvl, - onvlp[i], embedded)) != 0) { - /* - * Free any successfully created lists - */ - nvpair_free(nvp); - nvp_buf_free(nvl, nvp); - return (err); - } - - nvlp[i] = embedded++; - } - break; - } - default: - bcopy(data, NVP_VALUE(nvp), value_sz); - } - - /* if unique name, remove before add */ - if (nvl->nvl_nvflag & NV_UNIQUE_NAME) - (void) nvlist_remove_all(nvl, name); - else if (nvl->nvl_nvflag & NV_UNIQUE_NAME_TYPE) - (void) nvlist_remove(nvl, name, type); - - nvp_buf_link(nvl, nvp); - - return (0); -} - -int -nvlist_add_boolean(nvlist_t *nvl, const char *name) -{ - return (nvlist_add_common(nvl, name, DATA_TYPE_BOOLEAN, 0, NULL)); -} - -int -nvlist_add_boolean_value(nvlist_t *nvl, const char *name, boolean_t val) -{ - return (nvlist_add_common(nvl, name, DATA_TYPE_BOOLEAN_VALUE, 1, &val)); -} - -int -nvlist_add_byte(nvlist_t *nvl, const char *name, uchar_t val) -{ - return (nvlist_add_common(nvl, name, DATA_TYPE_BYTE, 1, &val)); -} - -int -nvlist_add_int8(nvlist_t *nvl, const char *name, int8_t val) -{ - return (nvlist_add_common(nvl, name, DATA_TYPE_INT8, 1, &val)); -} - -int -nvlist_add_uint8(nvlist_t *nvl, const char *name, uint8_t val) -{ - return (nvlist_add_common(nvl, name, DATA_TYPE_UINT8, 1, &val)); -} - -int -nvlist_add_int16(nvlist_t *nvl, const char *name, int16_t val) -{ - return (nvlist_add_common(nvl, name, DATA_TYPE_INT16, 1, &val)); -} - -int -nvlist_add_uint16(nvlist_t *nvl, const char *name, uint16_t val) -{ - return (nvlist_add_common(nvl, name, DATA_TYPE_UINT16, 1, &val)); -} - -int -nvlist_add_int32(nvlist_t *nvl, const char *name, int32_t val) -{ - return (nvlist_add_common(nvl, name, DATA_TYPE_INT32, 1, &val)); -} - -int -nvlist_add_uint32(nvlist_t *nvl, const char *name, uint32_t val) -{ - return (nvlist_add_common(nvl, name, DATA_TYPE_UINT32, 1, &val)); -} - -int -nvlist_add_int64(nvlist_t *nvl, const char *name, int64_t val) -{ - return (nvlist_add_common(nvl, name, DATA_TYPE_INT64, 1, &val)); -} - -int -nvlist_add_uint64(nvlist_t *nvl, const char *name, uint64_t val) -{ - return (nvlist_add_common(nvl, name, DATA_TYPE_UINT64, 1, &val)); -} - -#if !defined(_KERNEL) -int -nvlist_add_double(nvlist_t *nvl, const char *name, double val) -{ - return (nvlist_add_common(nvl, name, DATA_TYPE_DOUBLE, 1, &val)); -} -#endif - -int -nvlist_add_string(nvlist_t *nvl, const char *name, const char *val) -{ - return (nvlist_add_common(nvl, name, DATA_TYPE_STRING, 1, (void *)val)); -} - -int -nvlist_add_boolean_array(nvlist_t *nvl, const char *name, - boolean_t *a, uint_t n) -{ - return (nvlist_add_common(nvl, name, DATA_TYPE_BOOLEAN_ARRAY, n, a)); -} - -int -nvlist_add_byte_array(nvlist_t *nvl, const char *name, uchar_t *a, uint_t n) -{ - return (nvlist_add_common(nvl, name, DATA_TYPE_BYTE_ARRAY, n, a)); -} - -int -nvlist_add_int8_array(nvlist_t *nvl, const char *name, int8_t *a, uint_t n) -{ - return (nvlist_add_common(nvl, name, DATA_TYPE_INT8_ARRAY, n, a)); -} - -int -nvlist_add_uint8_array(nvlist_t *nvl, const char *name, uint8_t *a, uint_t n) -{ - return (nvlist_add_common(nvl, name, DATA_TYPE_UINT8_ARRAY, n, a)); -} - -int -nvlist_add_int16_array(nvlist_t *nvl, const char *name, int16_t *a, uint_t n) -{ - return (nvlist_add_common(nvl, name, DATA_TYPE_INT16_ARRAY, n, a)); -} - -int -nvlist_add_uint16_array(nvlist_t *nvl, const char *name, uint16_t *a, uint_t n) -{ - return (nvlist_add_common(nvl, name, DATA_TYPE_UINT16_ARRAY, n, a)); -} - -int -nvlist_add_int32_array(nvlist_t *nvl, const char *name, int32_t *a, uint_t n) -{ - return (nvlist_add_common(nvl, name, DATA_TYPE_INT32_ARRAY, n, a)); -} - -int -nvlist_add_uint32_array(nvlist_t *nvl, const char *name, uint32_t *a, uint_t n) -{ - return (nvlist_add_common(nvl, name, DATA_TYPE_UINT32_ARRAY, n, a)); -} - -int -nvlist_add_int64_array(nvlist_t *nvl, const char *name, int64_t *a, uint_t n) -{ - return (nvlist_add_common(nvl, name, DATA_TYPE_INT64_ARRAY, n, a)); -} - -int -nvlist_add_uint64_array(nvlist_t *nvl, const char *name, uint64_t *a, uint_t n) -{ - return (nvlist_add_common(nvl, name, DATA_TYPE_UINT64_ARRAY, n, a)); -} - -int -nvlist_add_string_array(nvlist_t *nvl, const char *name, - char *const *a, uint_t n) -{ - return (nvlist_add_common(nvl, name, DATA_TYPE_STRING_ARRAY, n, a)); -} - -int -nvlist_add_hrtime(nvlist_t *nvl, const char *name, hrtime_t val) -{ - return (nvlist_add_common(nvl, name, DATA_TYPE_HRTIME, 1, &val)); -} - -int -nvlist_add_nvlist(nvlist_t *nvl, const char *name, nvlist_t *val) -{ - return (nvlist_add_common(nvl, name, DATA_TYPE_NVLIST, 1, val)); -} - -int -nvlist_add_nvlist_array(nvlist_t *nvl, const char *name, nvlist_t **a, uint_t n) -{ - return (nvlist_add_common(nvl, name, DATA_TYPE_NVLIST_ARRAY, n, a)); -} - -/* reading name-value pairs */ -nvpair_t * -nvlist_next_nvpair(nvlist_t *nvl, nvpair_t *nvp) -{ - nvpriv_t *priv; - i_nvp_t *curr; - - if (nvl == NULL || - (priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv) == NULL) - return (NULL); - - curr = NVPAIR2I_NVP(nvp); - - /* - * Ensure that nvp is a valid nvpair on this nvlist. - * NB: nvp_curr is used only as a hint so that we don't always - * have to walk the list to determine if nvp is still on the list. - */ - if (nvp == NULL) - curr = priv->nvp_list; - else if (priv->nvp_curr == curr || nvlist_contains_nvp(nvl, nvp)) - curr = curr->nvi_next; - else - curr = NULL; - - priv->nvp_curr = curr; - - return (curr != NULL ? &curr->nvi_nvp : NULL); -} - -nvpair_t * -nvlist_prev_nvpair(nvlist_t *nvl, nvpair_t *nvp) -{ - nvpriv_t *priv; - i_nvp_t *curr; - - if (nvl == NULL || - (priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv) == NULL) - return (NULL); - - curr = NVPAIR2I_NVP(nvp); - - if (nvp == NULL) - curr = priv->nvp_last; - else if (priv->nvp_curr == curr || nvlist_contains_nvp(nvl, nvp)) - curr = curr->nvi_prev; - else - curr = NULL; - - priv->nvp_curr = curr; - - return (curr != NULL ? &curr->nvi_nvp : NULL); -} - -boolean_t -nvlist_empty(nvlist_t *nvl) -{ - nvpriv_t *priv; - - if (nvl == NULL || - (priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv) == NULL) - return (B_TRUE); - - return (priv->nvp_list == NULL); -} - -char * -nvpair_name(nvpair_t *nvp) -{ - return (NVP_NAME(nvp)); -} - -data_type_t -nvpair_type(nvpair_t *nvp) -{ - return (NVP_TYPE(nvp)); -} - -int -nvpair_type_is_array(nvpair_t *nvp) -{ - data_type_t type = NVP_TYPE(nvp); - - if ((type == DATA_TYPE_BYTE_ARRAY) || - (type == DATA_TYPE_INT8_ARRAY) || - (type == DATA_TYPE_UINT8_ARRAY) || - (type == DATA_TYPE_INT16_ARRAY) || - (type == DATA_TYPE_UINT16_ARRAY) || - (type == DATA_TYPE_INT32_ARRAY) || - (type == DATA_TYPE_UINT32_ARRAY) || - (type == DATA_TYPE_INT64_ARRAY) || - (type == DATA_TYPE_UINT64_ARRAY) || - (type == DATA_TYPE_BOOLEAN_ARRAY) || - (type == DATA_TYPE_STRING_ARRAY) || - (type == DATA_TYPE_NVLIST_ARRAY)) - return (1); - return (0); - -} - -static int -nvpair_value_common(nvpair_t *nvp, data_type_t type, uint_t *nelem, void *data) -{ - if (nvp == NULL || nvpair_type(nvp) != type) - return (EINVAL); - - /* - * For non-array types, we copy the data. - * For array types (including string), we set a pointer. - */ - switch (type) { - case DATA_TYPE_BOOLEAN: - if (nelem != NULL) - *nelem = 0; - break; - - case DATA_TYPE_BOOLEAN_VALUE: - case DATA_TYPE_BYTE: - case DATA_TYPE_INT8: - case DATA_TYPE_UINT8: - case DATA_TYPE_INT16: - case DATA_TYPE_UINT16: - case DATA_TYPE_INT32: - case DATA_TYPE_UINT32: - case DATA_TYPE_INT64: - case DATA_TYPE_UINT64: - case DATA_TYPE_HRTIME: -#if !defined(_KERNEL) - case DATA_TYPE_DOUBLE: -#endif - if (data == NULL) - return (EINVAL); - bcopy(NVP_VALUE(nvp), data, - (size_t)i_get_value_size(type, NULL, 1)); - if (nelem != NULL) - *nelem = 1; - break; - - case DATA_TYPE_NVLIST: - case DATA_TYPE_STRING: - if (data == NULL) - return (EINVAL); - *(void **)data = (void *)NVP_VALUE(nvp); - if (nelem != NULL) - *nelem = 1; - break; - - case DATA_TYPE_BOOLEAN_ARRAY: - case DATA_TYPE_BYTE_ARRAY: - case DATA_TYPE_INT8_ARRAY: - case DATA_TYPE_UINT8_ARRAY: - case DATA_TYPE_INT16_ARRAY: - case DATA_TYPE_UINT16_ARRAY: - case DATA_TYPE_INT32_ARRAY: - case DATA_TYPE_UINT32_ARRAY: - case DATA_TYPE_INT64_ARRAY: - case DATA_TYPE_UINT64_ARRAY: - case DATA_TYPE_STRING_ARRAY: - case DATA_TYPE_NVLIST_ARRAY: - if (nelem == NULL || data == NULL) - return (EINVAL); - if ((*nelem = NVP_NELEM(nvp)) != 0) - *(void **)data = (void *)NVP_VALUE(nvp); - else - *(void **)data = NULL; - break; - - default: - return (ENOTSUP); - } - - return (0); -} - -static int -nvlist_lookup_common(nvlist_t *nvl, const char *name, data_type_t type, - uint_t *nelem, void *data) -{ - nvpriv_t *priv; - nvpair_t *nvp; - i_nvp_t *curr; - - if (name == NULL || nvl == NULL || - (priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv) == NULL) - return (EINVAL); - - if (!(nvl->nvl_nvflag & (NV_UNIQUE_NAME | NV_UNIQUE_NAME_TYPE))) - return (ENOTSUP); - - for (curr = priv->nvp_list; curr != NULL; curr = curr->nvi_next) { - nvp = &curr->nvi_nvp; - - if (strcmp(name, NVP_NAME(nvp)) == 0 && NVP_TYPE(nvp) == type) - return (nvpair_value_common(nvp, type, nelem, data)); - } - - return (ENOENT); -} - -int -nvlist_lookup_boolean(nvlist_t *nvl, const char *name) -{ - return (nvlist_lookup_common(nvl, name, DATA_TYPE_BOOLEAN, NULL, NULL)); -} - -int -nvlist_lookup_boolean_value(nvlist_t *nvl, const char *name, boolean_t *val) -{ - return (nvlist_lookup_common(nvl, name, - DATA_TYPE_BOOLEAN_VALUE, NULL, val)); -} - -int -nvlist_lookup_byte(nvlist_t *nvl, const char *name, uchar_t *val) -{ - return (nvlist_lookup_common(nvl, name, DATA_TYPE_BYTE, NULL, val)); -} - -int -nvlist_lookup_int8(nvlist_t *nvl, const char *name, int8_t *val) -{ - return (nvlist_lookup_common(nvl, name, DATA_TYPE_INT8, NULL, val)); -} - -int -nvlist_lookup_uint8(nvlist_t *nvl, const char *name, uint8_t *val) -{ - return (nvlist_lookup_common(nvl, name, DATA_TYPE_UINT8, NULL, val)); -} - -int -nvlist_lookup_int16(nvlist_t *nvl, const char *name, int16_t *val) -{ - return (nvlist_lookup_common(nvl, name, DATA_TYPE_INT16, NULL, val)); -} - -int -nvlist_lookup_uint16(nvlist_t *nvl, const char *name, uint16_t *val) -{ - return (nvlist_lookup_common(nvl, name, DATA_TYPE_UINT16, NULL, val)); -} - -int -nvlist_lookup_int32(nvlist_t *nvl, const char *name, int32_t *val) -{ - return (nvlist_lookup_common(nvl, name, DATA_TYPE_INT32, NULL, val)); -} - -int -nvlist_lookup_uint32(nvlist_t *nvl, const char *name, uint32_t *val) -{ - return (nvlist_lookup_common(nvl, name, DATA_TYPE_UINT32, NULL, val)); -} - -int -nvlist_lookup_int64(nvlist_t *nvl, const char *name, int64_t *val) -{ - return (nvlist_lookup_common(nvl, name, DATA_TYPE_INT64, NULL, val)); -} - -int -nvlist_lookup_uint64(nvlist_t *nvl, const char *name, uint64_t *val) -{ - return (nvlist_lookup_common(nvl, name, DATA_TYPE_UINT64, NULL, val)); -} - -#if !defined(_KERNEL) -int -nvlist_lookup_double(nvlist_t *nvl, const char *name, double *val) -{ - return (nvlist_lookup_common(nvl, name, DATA_TYPE_DOUBLE, NULL, val)); -} -#endif - -int -nvlist_lookup_string(nvlist_t *nvl, const char *name, char **val) -{ - return (nvlist_lookup_common(nvl, name, DATA_TYPE_STRING, NULL, val)); -} - -int -nvlist_lookup_nvlist(nvlist_t *nvl, const char *name, nvlist_t **val) -{ - return (nvlist_lookup_common(nvl, name, DATA_TYPE_NVLIST, NULL, val)); -} - -int -nvlist_lookup_boolean_array(nvlist_t *nvl, const char *name, - boolean_t **a, uint_t *n) -{ - return (nvlist_lookup_common(nvl, name, - DATA_TYPE_BOOLEAN_ARRAY, n, a)); -} - -int -nvlist_lookup_byte_array(nvlist_t *nvl, const char *name, - uchar_t **a, uint_t *n) -{ - return (nvlist_lookup_common(nvl, name, DATA_TYPE_BYTE_ARRAY, n, a)); -} - -int -nvlist_lookup_int8_array(nvlist_t *nvl, const char *name, int8_t **a, uint_t *n) -{ - return (nvlist_lookup_common(nvl, name, DATA_TYPE_INT8_ARRAY, n, a)); -} - -int -nvlist_lookup_uint8_array(nvlist_t *nvl, const char *name, - uint8_t **a, uint_t *n) -{ - return (nvlist_lookup_common(nvl, name, DATA_TYPE_UINT8_ARRAY, n, a)); -} - -int -nvlist_lookup_int16_array(nvlist_t *nvl, const char *name, - int16_t **a, uint_t *n) -{ - return (nvlist_lookup_common(nvl, name, DATA_TYPE_INT16_ARRAY, n, a)); -} - -int -nvlist_lookup_uint16_array(nvlist_t *nvl, const char *name, - uint16_t **a, uint_t *n) -{ - return (nvlist_lookup_common(nvl, name, DATA_TYPE_UINT16_ARRAY, n, a)); -} - -int -nvlist_lookup_int32_array(nvlist_t *nvl, const char *name, - int32_t **a, uint_t *n) -{ - return (nvlist_lookup_common(nvl, name, DATA_TYPE_INT32_ARRAY, n, a)); -} - -int -nvlist_lookup_uint32_array(nvlist_t *nvl, const char *name, - uint32_t **a, uint_t *n) -{ - return (nvlist_lookup_common(nvl, name, DATA_TYPE_UINT32_ARRAY, n, a)); -} - -int -nvlist_lookup_int64_array(nvlist_t *nvl, const char *name, - int64_t **a, uint_t *n) -{ - return (nvlist_lookup_common(nvl, name, DATA_TYPE_INT64_ARRAY, n, a)); -} - -int -nvlist_lookup_uint64_array(nvlist_t *nvl, const char *name, - uint64_t **a, uint_t *n) -{ - return (nvlist_lookup_common(nvl, name, DATA_TYPE_UINT64_ARRAY, n, a)); -} - -int -nvlist_lookup_string_array(nvlist_t *nvl, const char *name, - char ***a, uint_t *n) -{ - return (nvlist_lookup_common(nvl, name, DATA_TYPE_STRING_ARRAY, n, a)); -} - -int -nvlist_lookup_nvlist_array(nvlist_t *nvl, const char *name, - nvlist_t ***a, uint_t *n) -{ - return (nvlist_lookup_common(nvl, name, DATA_TYPE_NVLIST_ARRAY, n, a)); -} - -int -nvlist_lookup_hrtime(nvlist_t *nvl, const char *name, hrtime_t *val) -{ - return (nvlist_lookup_common(nvl, name, DATA_TYPE_HRTIME, NULL, val)); -} - -int -nvlist_lookup_pairs(nvlist_t *nvl, int flag, ...) -{ - va_list ap; - char *name; - int noentok = (flag & NV_FLAG_NOENTOK ? 1 : 0); - int ret = 0; - - va_start(ap, flag); - while (ret == 0 && (name = va_arg(ap, char *)) != NULL) { - data_type_t type; - void *val; - uint_t *nelem; - - switch (type = va_arg(ap, data_type_t)) { - case DATA_TYPE_BOOLEAN: - ret = nvlist_lookup_common(nvl, name, type, NULL, NULL); - break; - - case DATA_TYPE_BOOLEAN_VALUE: - case DATA_TYPE_BYTE: - case DATA_TYPE_INT8: - case DATA_TYPE_UINT8: - case DATA_TYPE_INT16: - case DATA_TYPE_UINT16: - case DATA_TYPE_INT32: - case DATA_TYPE_UINT32: - case DATA_TYPE_INT64: - case DATA_TYPE_UINT64: - case DATA_TYPE_HRTIME: - case DATA_TYPE_STRING: - case DATA_TYPE_NVLIST: -#if !defined(_KERNEL) - case DATA_TYPE_DOUBLE: -#endif - val = va_arg(ap, void *); - ret = nvlist_lookup_common(nvl, name, type, NULL, val); - break; - - case DATA_TYPE_BYTE_ARRAY: - case DATA_TYPE_BOOLEAN_ARRAY: - case DATA_TYPE_INT8_ARRAY: - case DATA_TYPE_UINT8_ARRAY: - case DATA_TYPE_INT16_ARRAY: - case DATA_TYPE_UINT16_ARRAY: - case DATA_TYPE_INT32_ARRAY: - case DATA_TYPE_UINT32_ARRAY: - case DATA_TYPE_INT64_ARRAY: - case DATA_TYPE_UINT64_ARRAY: - case DATA_TYPE_STRING_ARRAY: - case DATA_TYPE_NVLIST_ARRAY: - val = va_arg(ap, void *); - nelem = va_arg(ap, uint_t *); - ret = nvlist_lookup_common(nvl, name, type, nelem, val); - break; - - default: - ret = EINVAL; - } - - if (ret == ENOENT && noentok) - ret = 0; - } - va_end(ap); - - return (ret); -} - -/* - * Find the 'name'ed nvpair in the nvlist 'nvl'. If 'name' found, the function - * returns zero and a pointer to the matching nvpair is returned in '*ret' - * (given 'ret' is non-NULL). If 'sep' is specified then 'name' will penitrate - * multiple levels of embedded nvlists, with 'sep' as the separator. As an - * example, if sep is '.', name might look like: "a" or "a.b" or "a.c[3]" or - * "a.d[3].e[1]". This matches the C syntax for array embed (for convience, - * code also supports "a.d[3]e[1]" syntax). - * - * If 'ip' is non-NULL and the last name component is an array, return the - * value of the "...[index]" array index in *ip. For an array reference that - * is not indexed, *ip will be returned as -1. If there is a syntax error in - * 'name', and 'ep' is non-NULL then *ep will be set to point to the location - * inside the 'name' string where the syntax error was detected. - */ -static int -nvlist_lookup_nvpair_ei_sep(nvlist_t *nvl, const char *name, const char sep, - nvpair_t **ret, int *ip, char **ep) -{ - nvpair_t *nvp; - const char *np; - char *sepp; - char *idxp, *idxep; - nvlist_t **nva; - long idx; - int n; - - if (ip) - *ip = -1; /* not indexed */ - if (ep) - *ep = NULL; - - if ((nvl == NULL) || (name == NULL)) - return (EINVAL); - - /* step through components of name */ - for (np = name; np && *np; np = sepp) { - /* ensure unique names */ - if (!(nvl->nvl_nvflag & NV_UNIQUE_NAME)) - return (ENOTSUP); - - /* skip white space */ - skip_whitespace(np); - if (*np == 0) - break; - - /* set 'sepp' to end of current component 'np' */ - if (sep) - sepp = strchr(np, sep); - else - sepp = NULL; - - /* find start of next "[ index ]..." */ - idxp = strchr(np, '['); - - /* if sepp comes first, set idxp to NULL */ - if (sepp && idxp && (sepp < idxp)) - idxp = NULL; - - /* - * At this point 'idxp' is set if there is an index - * expected for the current component. - */ - if (idxp) { - /* set 'n' to length of current 'np' name component */ - n = idxp++ - np; - - /* keep sepp up to date for *ep use as we advance */ - skip_whitespace(idxp); - sepp = idxp; - - /* determine the index value */ -#if defined(_KERNEL) && !defined(_BOOT) - if (ddi_strtol(idxp, &idxep, 0, &idx)) - goto fail; -#else - idx = strtol(idxp, &idxep, 0); -#endif - if (idxep == idxp) - goto fail; - - /* keep sepp up to date for *ep use as we advance */ - sepp = idxep; - - /* skip white space index value and check for ']' */ - skip_whitespace(sepp); - if (*sepp++ != ']') - goto fail; - - /* for embedded arrays, support C syntax: "a[1].b" */ - skip_whitespace(sepp); - if (sep && (*sepp == sep)) - sepp++; - } else if (sepp) { - n = sepp++ - np; - } else { - n = strlen(np); - } - - /* trim trailing whitespace by reducing length of 'np' */ - if (n == 0) - goto fail; - for (n--; (np[n] == ' ') || (np[n] == '\t'); n--) - ; - n++; - - /* skip whitespace, and set sepp to NULL if complete */ - if (sepp) { - skip_whitespace(sepp); - if (*sepp == 0) - sepp = NULL; - } - - /* - * At this point: - * o 'n' is the length of current 'np' component. - * o 'idxp' is set if there was an index, and value 'idx'. - * o 'sepp' is set to the beginning of the next component, - * and set to NULL if we have no more components. - * - * Search for nvpair with matching component name. - */ - for (nvp = nvlist_next_nvpair(nvl, NULL); nvp != NULL; - nvp = nvlist_next_nvpair(nvl, nvp)) { - - /* continue if no match on name */ - if (strncmp(np, nvpair_name(nvp), n) || - (strlen(nvpair_name(nvp)) != n)) - continue; - - /* if indexed, verify type is array oriented */ - if (idxp && !nvpair_type_is_array(nvp)) - goto fail; - - /* - * Full match found, return nvp and idx if this - * was the last component. - */ - if (sepp == NULL) { - if (ret) - *ret = nvp; - if (ip && idxp) - *ip = (int)idx; /* return index */ - return (0); /* found */ - } - - /* - * More components: current match must be - * of DATA_TYPE_NVLIST or DATA_TYPE_NVLIST_ARRAY - * to support going deeper. - */ - if (nvpair_type(nvp) == DATA_TYPE_NVLIST) { - nvl = EMBEDDED_NVL(nvp); - break; - } else if (nvpair_type(nvp) == DATA_TYPE_NVLIST_ARRAY) { - (void) nvpair_value_nvlist_array(nvp, - &nva, (uint_t *)&n); - if ((n < 0) || (idx >= n)) - goto fail; - nvl = nva[idx]; - break; - } - - /* type does not support more levels */ - goto fail; - } - if (nvp == NULL) - goto fail; /* 'name' not found */ - - /* search for match of next component in embedded 'nvl' list */ - } - -fail: if (ep && sepp) - *ep = sepp; - return (EINVAL); -} - -/* - * Return pointer to nvpair with specified 'name'. - */ -int -nvlist_lookup_nvpair(nvlist_t *nvl, const char *name, nvpair_t **ret) -{ - return (nvlist_lookup_nvpair_ei_sep(nvl, name, 0, ret, NULL, NULL)); -} - -/* - * Determine if named nvpair exists in nvlist (use embedded separator of '.' - * and return array index). See nvlist_lookup_nvpair_ei_sep for more detailed - * description. - */ -int nvlist_lookup_nvpair_embedded_index(nvlist_t *nvl, - const char *name, nvpair_t **ret, int *ip, char **ep) -{ - return (nvlist_lookup_nvpair_ei_sep(nvl, name, '.', ret, ip, ep)); -} - -boolean_t -nvlist_exists(nvlist_t *nvl, const char *name) -{ - nvpriv_t *priv; - nvpair_t *nvp; - i_nvp_t *curr; - - if (name == NULL || nvl == NULL || - (priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv) == NULL) - return (B_FALSE); - - for (curr = priv->nvp_list; curr != NULL; curr = curr->nvi_next) { - nvp = &curr->nvi_nvp; - - if (strcmp(name, NVP_NAME(nvp)) == 0) - return (B_TRUE); - } - - return (B_FALSE); -} - -int -nvpair_value_boolean_value(nvpair_t *nvp, boolean_t *val) -{ - return (nvpair_value_common(nvp, DATA_TYPE_BOOLEAN_VALUE, NULL, val)); -} - -int -nvpair_value_byte(nvpair_t *nvp, uchar_t *val) -{ - return (nvpair_value_common(nvp, DATA_TYPE_BYTE, NULL, val)); -} - -int -nvpair_value_int8(nvpair_t *nvp, int8_t *val) -{ - return (nvpair_value_common(nvp, DATA_TYPE_INT8, NULL, val)); -} - -int -nvpair_value_uint8(nvpair_t *nvp, uint8_t *val) -{ - return (nvpair_value_common(nvp, DATA_TYPE_UINT8, NULL, val)); -} - -int -nvpair_value_int16(nvpair_t *nvp, int16_t *val) -{ - return (nvpair_value_common(nvp, DATA_TYPE_INT16, NULL, val)); -} - -int -nvpair_value_uint16(nvpair_t *nvp, uint16_t *val) -{ - return (nvpair_value_common(nvp, DATA_TYPE_UINT16, NULL, val)); -} - -int -nvpair_value_int32(nvpair_t *nvp, int32_t *val) -{ - return (nvpair_value_common(nvp, DATA_TYPE_INT32, NULL, val)); -} - -int -nvpair_value_uint32(nvpair_t *nvp, uint32_t *val) -{ - return (nvpair_value_common(nvp, DATA_TYPE_UINT32, NULL, val)); -} - -int -nvpair_value_int64(nvpair_t *nvp, int64_t *val) -{ - return (nvpair_value_common(nvp, DATA_TYPE_INT64, NULL, val)); -} - -int -nvpair_value_uint64(nvpair_t *nvp, uint64_t *val) -{ - return (nvpair_value_common(nvp, DATA_TYPE_UINT64, NULL, val)); -} - -#if !defined(_KERNEL) -int -nvpair_value_double(nvpair_t *nvp, double *val) -{ - return (nvpair_value_common(nvp, DATA_TYPE_DOUBLE, NULL, val)); -} -#endif - -int -nvpair_value_string(nvpair_t *nvp, char **val) -{ - return (nvpair_value_common(nvp, DATA_TYPE_STRING, NULL, val)); -} - -int -nvpair_value_nvlist(nvpair_t *nvp, nvlist_t **val) -{ - return (nvpair_value_common(nvp, DATA_TYPE_NVLIST, NULL, val)); -} - -int -nvpair_value_boolean_array(nvpair_t *nvp, boolean_t **val, uint_t *nelem) -{ - return (nvpair_value_common(nvp, DATA_TYPE_BOOLEAN_ARRAY, nelem, val)); -} - -int -nvpair_value_byte_array(nvpair_t *nvp, uchar_t **val, uint_t *nelem) -{ - return (nvpair_value_common(nvp, DATA_TYPE_BYTE_ARRAY, nelem, val)); -} - -int -nvpair_value_int8_array(nvpair_t *nvp, int8_t **val, uint_t *nelem) -{ - return (nvpair_value_common(nvp, DATA_TYPE_INT8_ARRAY, nelem, val)); -} - -int -nvpair_value_uint8_array(nvpair_t *nvp, uint8_t **val, uint_t *nelem) -{ - return (nvpair_value_common(nvp, DATA_TYPE_UINT8_ARRAY, nelem, val)); -} - -int -nvpair_value_int16_array(nvpair_t *nvp, int16_t **val, uint_t *nelem) -{ - return (nvpair_value_common(nvp, DATA_TYPE_INT16_ARRAY, nelem, val)); -} - -int -nvpair_value_uint16_array(nvpair_t *nvp, uint16_t **val, uint_t *nelem) -{ - return (nvpair_value_common(nvp, DATA_TYPE_UINT16_ARRAY, nelem, val)); -} - -int -nvpair_value_int32_array(nvpair_t *nvp, int32_t **val, uint_t *nelem) -{ - return (nvpair_value_common(nvp, DATA_TYPE_INT32_ARRAY, nelem, val)); -} - -int -nvpair_value_uint32_array(nvpair_t *nvp, uint32_t **val, uint_t *nelem) -{ - return (nvpair_value_common(nvp, DATA_TYPE_UINT32_ARRAY, nelem, val)); -} - -int -nvpair_value_int64_array(nvpair_t *nvp, int64_t **val, uint_t *nelem) -{ - return (nvpair_value_common(nvp, DATA_TYPE_INT64_ARRAY, nelem, val)); -} - -int -nvpair_value_uint64_array(nvpair_t *nvp, uint64_t **val, uint_t *nelem) -{ - return (nvpair_value_common(nvp, DATA_TYPE_UINT64_ARRAY, nelem, val)); -} - -int -nvpair_value_string_array(nvpair_t *nvp, char ***val, uint_t *nelem) -{ - return (nvpair_value_common(nvp, DATA_TYPE_STRING_ARRAY, nelem, val)); -} - -int -nvpair_value_nvlist_array(nvpair_t *nvp, nvlist_t ***val, uint_t *nelem) -{ - return (nvpair_value_common(nvp, DATA_TYPE_NVLIST_ARRAY, nelem, val)); -} - -int -nvpair_value_hrtime(nvpair_t *nvp, hrtime_t *val) -{ - return (nvpair_value_common(nvp, DATA_TYPE_HRTIME, NULL, val)); -} - -/* - * Add specified pair to the list. - */ -int -nvlist_add_nvpair(nvlist_t *nvl, nvpair_t *nvp) -{ - if (nvl == NULL || nvp == NULL) - return (EINVAL); - - return (nvlist_add_common(nvl, NVP_NAME(nvp), NVP_TYPE(nvp), - NVP_NELEM(nvp), NVP_VALUE(nvp))); -} - -/* - * Merge the supplied nvlists and put the result in dst. - * The merged list will contain all names specified in both lists, - * the values are taken from nvl in the case of duplicates. - * Return 0 on success. - */ -/*ARGSUSED*/ -int -nvlist_merge(nvlist_t *dst, nvlist_t *nvl, int flag) -{ - if (nvl == NULL || dst == NULL) - return (EINVAL); - - if (dst != nvl) - return (nvlist_copy_pairs(nvl, dst)); - - return (0); -} - -/* - * Encoding related routines - */ -#define NVS_OP_ENCODE 0 -#define NVS_OP_DECODE 1 -#define NVS_OP_GETSIZE 2 - -typedef struct nvs_ops nvs_ops_t; - -typedef struct { - int nvs_op; - const nvs_ops_t *nvs_ops; - void *nvs_private; - nvpriv_t *nvs_priv; -} nvstream_t; - -/* - * nvs operations are: - * - nvs_nvlist - * encoding / decoding of a nvlist header (nvlist_t) - * calculates the size used for header and end detection - * - * - nvs_nvpair - * responsible for the first part of encoding / decoding of an nvpair - * calculates the decoded size of an nvpair - * - * - nvs_nvp_op - * second part of encoding / decoding of an nvpair - * - * - nvs_nvp_size - * calculates the encoding size of an nvpair - * - * - nvs_nvl_fini - * encodes the end detection mark (zeros). - */ -struct nvs_ops { - int (*nvs_nvlist)(nvstream_t *, nvlist_t *, size_t *); - int (*nvs_nvpair)(nvstream_t *, nvpair_t *, size_t *); - int (*nvs_nvp_op)(nvstream_t *, nvpair_t *); - int (*nvs_nvp_size)(nvstream_t *, nvpair_t *, size_t *); - int (*nvs_nvl_fini)(nvstream_t *); -}; - -typedef struct { - char nvh_encoding; /* nvs encoding method */ - char nvh_endian; /* nvs endian */ - char nvh_reserved1; /* reserved for future use */ - char nvh_reserved2; /* reserved for future use */ -} nvs_header_t; - -static int -nvs_encode_pairs(nvstream_t *nvs, nvlist_t *nvl) -{ - nvpriv_t *priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv; - i_nvp_t *curr; - - /* - * Walk nvpair in list and encode each nvpair - */ - for (curr = priv->nvp_list; curr != NULL; curr = curr->nvi_next) - if (nvs->nvs_ops->nvs_nvpair(nvs, &curr->nvi_nvp, NULL) != 0) - return (EFAULT); - - return (nvs->nvs_ops->nvs_nvl_fini(nvs)); -} - -static int -nvs_decode_pairs(nvstream_t *nvs, nvlist_t *nvl) -{ - nvpair_t *nvp; - size_t nvsize; - int err; - - /* - * Get decoded size of next pair in stream, alloc - * memory for nvpair_t, then decode the nvpair - */ - while ((err = nvs->nvs_ops->nvs_nvpair(nvs, NULL, &nvsize)) == 0) { - if (nvsize == 0) /* end of list */ - break; - - /* make sure len makes sense */ - if (nvsize < NVP_SIZE_CALC(1, 0)) - return (EFAULT); - - if ((nvp = nvp_buf_alloc(nvl, nvsize)) == NULL) - return (ENOMEM); - - if ((err = nvs->nvs_ops->nvs_nvp_op(nvs, nvp)) != 0) { - nvp_buf_free(nvl, nvp); - return (err); - } - - if (i_validate_nvpair(nvp) != 0) { - nvpair_free(nvp); - nvp_buf_free(nvl, nvp); - return (EFAULT); - } - - nvp_buf_link(nvl, nvp); - } - return (err); -} - -static int -nvs_getsize_pairs(nvstream_t *nvs, nvlist_t *nvl, size_t *buflen) -{ - nvpriv_t *priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv; - i_nvp_t *curr; - uint64_t nvsize = *buflen; - size_t size; - - /* - * Get encoded size of nvpairs in nvlist - */ - for (curr = priv->nvp_list; curr != NULL; curr = curr->nvi_next) { - if (nvs->nvs_ops->nvs_nvp_size(nvs, &curr->nvi_nvp, &size) != 0) - return (EINVAL); - - if ((nvsize += size) > INT32_MAX) - return (EINVAL); - } - - *buflen = nvsize; - return (0); -} - -static int -nvs_operation(nvstream_t *nvs, nvlist_t *nvl, size_t *buflen) -{ - int err; - - if (nvl->nvl_priv == 0) - return (EFAULT); - - /* - * Perform the operation, starting with header, then each nvpair - */ - if ((err = nvs->nvs_ops->nvs_nvlist(nvs, nvl, buflen)) != 0) - return (err); - - switch (nvs->nvs_op) { - case NVS_OP_ENCODE: - err = nvs_encode_pairs(nvs, nvl); - break; - - case NVS_OP_DECODE: - err = nvs_decode_pairs(nvs, nvl); - break; - - case NVS_OP_GETSIZE: - err = nvs_getsize_pairs(nvs, nvl, buflen); - break; - - default: - err = EINVAL; - } - - return (err); -} - -static int -nvs_embedded(nvstream_t *nvs, nvlist_t *embedded) -{ - switch (nvs->nvs_op) { - case NVS_OP_ENCODE: - return (nvs_operation(nvs, embedded, NULL)); - - case NVS_OP_DECODE: { - nvpriv_t *priv; - int err; - - if (embedded->nvl_version != NV_VERSION) - return (ENOTSUP); - - if ((priv = nv_priv_alloc_embedded(nvs->nvs_priv)) == NULL) - return (ENOMEM); - - nvlist_init(embedded, embedded->nvl_nvflag, priv); - - if ((err = nvs_operation(nvs, embedded, NULL)) != 0) - nvlist_free(embedded); - return (err); - } - default: - break; - } - - return (EINVAL); -} - -static int -nvs_embedded_nvl_array(nvstream_t *nvs, nvpair_t *nvp, size_t *size) -{ - size_t nelem = NVP_NELEM(nvp); - nvlist_t **nvlp = EMBEDDED_NVL_ARRAY(nvp); - int i; - - switch (nvs->nvs_op) { - case NVS_OP_ENCODE: - for (i = 0; i < nelem; i++) - if (nvs_embedded(nvs, nvlp[i]) != 0) - return (EFAULT); - break; - - case NVS_OP_DECODE: { - size_t len = nelem * sizeof (uint64_t); - nvlist_t *embedded = (nvlist_t *)((uintptr_t)nvlp + len); - - bzero(nvlp, len); /* don't trust packed data */ - for (i = 0; i < nelem; i++) { - if (nvs_embedded(nvs, embedded) != 0) { - nvpair_free(nvp); - return (EFAULT); - } - - nvlp[i] = embedded++; - } - break; - } - case NVS_OP_GETSIZE: { - uint64_t nvsize = 0; - - for (i = 0; i < nelem; i++) { - size_t nvp_sz = 0; - - if (nvs_operation(nvs, nvlp[i], &nvp_sz) != 0) - return (EINVAL); - - if ((nvsize += nvp_sz) > INT32_MAX) - return (EINVAL); - } - - *size = nvsize; - break; - } - default: - return (EINVAL); - } - - return (0); -} - -static int nvs_native(nvstream_t *, nvlist_t *, char *, size_t *); -static int nvs_xdr(nvstream_t *, nvlist_t *, char *, size_t *); - -/* - * Common routine for nvlist operations: - * encode, decode, getsize (encoded size). - */ -static int -nvlist_common(nvlist_t *nvl, char *buf, size_t *buflen, int encoding, - int nvs_op) -{ - int err = 0; - nvstream_t nvs; - int nvl_endian; -#if BYTE_ORDER == _LITTLE_ENDIAN - int host_endian = 1; -#else - int host_endian = 0; -#endif /* _LITTLE_ENDIAN */ - nvs_header_t *nvh = (void *)buf; - - if (buflen == NULL || nvl == NULL || - (nvs.nvs_priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv) == NULL) - return (EINVAL); - - nvs.nvs_op = nvs_op; - - /* - * For NVS_OP_ENCODE and NVS_OP_DECODE make sure an nvlist and - * a buffer is allocated. The first 4 bytes in the buffer are - * used for encoding method and host endian. - */ - switch (nvs_op) { - case NVS_OP_ENCODE: - if (buf == NULL || *buflen < sizeof (nvs_header_t)) - return (EINVAL); - - nvh->nvh_encoding = encoding; - nvh->nvh_endian = nvl_endian = host_endian; - nvh->nvh_reserved1 = 0; - nvh->nvh_reserved2 = 0; - break; - - case NVS_OP_DECODE: - if (buf == NULL || *buflen < sizeof (nvs_header_t)) - return (EINVAL); - - /* get method of encoding from first byte */ - encoding = nvh->nvh_encoding; - nvl_endian = nvh->nvh_endian; - break; - - case NVS_OP_GETSIZE: - nvl_endian = host_endian; - - /* - * add the size for encoding - */ - *buflen = sizeof (nvs_header_t); - break; - - default: - return (ENOTSUP); - } - - /* - * Create an nvstream with proper encoding method - */ - switch (encoding) { - case NV_ENCODE_NATIVE: - /* - * check endianness, in case we are unpacking - * from a file - */ - if (nvl_endian != host_endian) - return (ENOTSUP); - err = nvs_native(&nvs, nvl, buf, buflen); - break; - case NV_ENCODE_XDR: - err = nvs_xdr(&nvs, nvl, buf, buflen); - break; - default: - err = ENOTSUP; - break; - } - - return (err); -} - -int -nvlist_size(nvlist_t *nvl, size_t *size, int encoding) -{ - return (nvlist_common(nvl, NULL, size, encoding, NVS_OP_GETSIZE)); -} - -/* - * Pack nvlist into contiguous memory - */ -/*ARGSUSED1*/ -int -nvlist_pack(nvlist_t *nvl, char **bufp, size_t *buflen, int encoding, - int kmflag) -{ -#if defined(_KERNEL) && !defined(_BOOT) - return (nvlist_xpack(nvl, bufp, buflen, encoding, - (kmflag == KM_SLEEP ? nv_alloc_sleep : nv_alloc_nosleep))); -#else - return (nvlist_xpack(nvl, bufp, buflen, encoding, nv_alloc_nosleep)); -#endif -} - -int -nvlist_xpack(nvlist_t *nvl, char **bufp, size_t *buflen, int encoding, - nv_alloc_t *nva) -{ - nvpriv_t nvpriv; - size_t alloc_size; - char *buf; - int err; - - if (nva == NULL || nvl == NULL || bufp == NULL || buflen == NULL) - return (EINVAL); - - if (*bufp != NULL) - return (nvlist_common(nvl, *bufp, buflen, encoding, - NVS_OP_ENCODE)); - - /* - * Here is a difficult situation: - * 1. The nvlist has fixed allocator properties. - * All other nvlist routines (like nvlist_add_*, ...) use - * these properties. - * 2. When using nvlist_pack() the user can specify his own - * allocator properties (e.g. by using KM_NOSLEEP). - * - * We use the user specified properties (2). A clearer solution - * will be to remove the kmflag from nvlist_pack(), but we will - * not change the interface. - */ - nv_priv_init(&nvpriv, nva, 0); - - if (err = nvlist_size(nvl, &alloc_size, encoding)) - return (err); - - if ((buf = nv_mem_zalloc(&nvpriv, alloc_size)) == NULL) - return (ENOMEM); - - if ((err = nvlist_common(nvl, buf, &alloc_size, encoding, - NVS_OP_ENCODE)) != 0) { - nv_mem_free(&nvpriv, buf, alloc_size); - } else { - *buflen = alloc_size; - *bufp = buf; - } - - return (err); -} - -/* - * Unpack buf into an nvlist_t - */ -/*ARGSUSED1*/ -int -nvlist_unpack(char *buf, size_t buflen, nvlist_t **nvlp, int kmflag) -{ -#if defined(_KERNEL) && !defined(_BOOT) - return (nvlist_xunpack(buf, buflen, nvlp, - (kmflag == KM_SLEEP ? nv_alloc_sleep : nv_alloc_nosleep))); -#else - return (nvlist_xunpack(buf, buflen, nvlp, nv_alloc_nosleep)); -#endif -} - -int -nvlist_xunpack(char *buf, size_t buflen, nvlist_t **nvlp, nv_alloc_t *nva) -{ - nvlist_t *nvl; - int err; - - if (nvlp == NULL) - return (EINVAL); - - if ((err = nvlist_xalloc(&nvl, 0, nva)) != 0) - return (err); - - if ((err = nvlist_common(nvl, buf, &buflen, 0, NVS_OP_DECODE)) != 0) - nvlist_free(nvl); - else - *nvlp = nvl; - - return (err); -} - -/* - * Native encoding functions - */ -typedef struct { - /* - * This structure is used when decoding a packed nvpair in - * the native format. n_base points to a buffer containing the - * packed nvpair. n_end is a pointer to the end of the buffer. - * (n_end actually points to the first byte past the end of the - * buffer.) n_curr is a pointer that lies between n_base and n_end. - * It points to the current data that we are decoding. - * The amount of data left in the buffer is equal to n_end - n_curr. - * n_flag is used to recognize a packed embedded list. - */ - caddr_t n_base; - caddr_t n_end; - caddr_t n_curr; - uint_t n_flag; -} nvs_native_t; - -static int -nvs_native_create(nvstream_t *nvs, nvs_native_t *native, char *buf, - size_t buflen) -{ - switch (nvs->nvs_op) { - case NVS_OP_ENCODE: - case NVS_OP_DECODE: - nvs->nvs_private = native; - native->n_curr = native->n_base = buf; - native->n_end = buf + buflen; - native->n_flag = 0; - return (0); - - case NVS_OP_GETSIZE: - nvs->nvs_private = native; - native->n_curr = native->n_base = native->n_end = NULL; - native->n_flag = 0; - return (0); - default: - return (EINVAL); - } -} - -/*ARGSUSED*/ -static void -nvs_native_destroy(nvstream_t *nvs) -{ -} - -static int -native_cp(nvstream_t *nvs, void *buf, size_t size) -{ - nvs_native_t *native = (nvs_native_t *)nvs->nvs_private; - - if (native->n_curr + size > native->n_end) - return (EFAULT); - - /* - * The bcopy() below eliminates alignment requirement - * on the buffer (stream) and is preferred over direct access. - */ - switch (nvs->nvs_op) { - case NVS_OP_ENCODE: - bcopy(buf, native->n_curr, size); - break; - case NVS_OP_DECODE: - bcopy(native->n_curr, buf, size); - break; - default: - return (EINVAL); - } - - native->n_curr += size; - return (0); -} - -/* - * operate on nvlist_t header - */ -static int -nvs_native_nvlist(nvstream_t *nvs, nvlist_t *nvl, size_t *size) -{ - nvs_native_t *native = nvs->nvs_private; - - switch (nvs->nvs_op) { - case NVS_OP_ENCODE: - case NVS_OP_DECODE: - if (native->n_flag) - return (0); /* packed embedded list */ - - native->n_flag = 1; - - /* copy version and nvflag of the nvlist_t */ - if (native_cp(nvs, &nvl->nvl_version, sizeof (int32_t)) != 0 || - native_cp(nvs, &nvl->nvl_nvflag, sizeof (int32_t)) != 0) - return (EFAULT); - - return (0); - - case NVS_OP_GETSIZE: - /* - * if calculate for packed embedded list - * 4 for end of the embedded list - * else - * 2 * sizeof (int32_t) for nvl_version and nvl_nvflag - * and 4 for end of the entire list - */ - if (native->n_flag) { - *size += 4; - } else { - native->n_flag = 1; - *size += 2 * sizeof (int32_t) + 4; - } - - return (0); - - default: - return (EINVAL); - } -} - -static int -nvs_native_nvl_fini(nvstream_t *nvs) -{ - if (nvs->nvs_op == NVS_OP_ENCODE) { - nvs_native_t *native = (nvs_native_t *)nvs->nvs_private; - /* - * Add 4 zero bytes at end of nvlist. They are used - * for end detection by the decode routine. - */ - if (native->n_curr + sizeof (int) > native->n_end) - return (EFAULT); - - bzero(native->n_curr, sizeof (int)); - native->n_curr += sizeof (int); - } - - return (0); -} - -static int -nvpair_native_embedded(nvstream_t *nvs, nvpair_t *nvp) -{ - if (nvs->nvs_op == NVS_OP_ENCODE) { - nvs_native_t *native = (nvs_native_t *)nvs->nvs_private; - char *packed = (void *) - (native->n_curr - nvp->nvp_size + NVP_VALOFF(nvp)); - /* - * Null out the pointer that is meaningless in the packed - * structure. The address may not be aligned, so we have - * to use bzero. - */ - bzero(packed + offsetof(nvlist_t, nvl_priv), - sizeof(((nvlist_t *)NULL)->nvl_priv)); - } - - return (nvs_embedded(nvs, EMBEDDED_NVL(nvp))); -} - -static int -nvpair_native_embedded_array(nvstream_t *nvs, nvpair_t *nvp) -{ - if (nvs->nvs_op == NVS_OP_ENCODE) { - nvs_native_t *native = (nvs_native_t *)nvs->nvs_private; - char *value = native->n_curr - nvp->nvp_size + NVP_VALOFF(nvp); - size_t len = NVP_NELEM(nvp) * sizeof (uint64_t); - int i; - /* - * Null out pointers that are meaningless in the packed - * structure. The addresses may not be aligned, so we have - * to use bzero. - */ - bzero(value, len); - - value += len; - for (i = 0; i < NVP_NELEM(nvp); i++) { - /* - * Null out the pointer that is meaningless in the - * packed structure. The address may not be aligned, - * so we have to use bzero. - */ - bzero(value + offsetof(nvlist_t, nvl_priv), - sizeof(((nvlist_t *)NULL)->nvl_priv)); - value += sizeof(nvlist_t); - } - } - - return (nvs_embedded_nvl_array(nvs, nvp, NULL)); -} - -static void -nvpair_native_string_array(nvstream_t *nvs, nvpair_t *nvp) -{ - switch (nvs->nvs_op) { - case NVS_OP_ENCODE: { - nvs_native_t *native = (nvs_native_t *)nvs->nvs_private; - uint64_t *strp = (void *) - (native->n_curr - nvp->nvp_size + NVP_VALOFF(nvp)); - /* - * Null out pointers that are meaningless in the packed - * structure. The addresses may not be aligned, so we have - * to use bzero. - */ - bzero(strp, NVP_NELEM(nvp) * sizeof (uint64_t)); - break; - } - case NVS_OP_DECODE: { - char **strp = (void *)NVP_VALUE(nvp); - char *buf = ((char *)strp + NVP_NELEM(nvp) * sizeof (uint64_t)); - int i; - - for (i = 0; i < NVP_NELEM(nvp); i++) { - strp[i] = buf; - buf += strlen(buf) + 1; - } - break; - } - } -} - -static int -nvs_native_nvp_op(nvstream_t *nvs, nvpair_t *nvp) -{ - data_type_t type; - int value_sz; - int ret = 0; - - /* - * We do the initial bcopy of the data before we look at - * the nvpair type, because when we're decoding, we won't - * have the correct values for the pair until we do the bcopy. - */ - switch (nvs->nvs_op) { - case NVS_OP_ENCODE: - case NVS_OP_DECODE: - if (native_cp(nvs, nvp, nvp->nvp_size) != 0) - return (EFAULT); - break; - default: - return (EINVAL); - } - - /* verify nvp_name_sz, check the name string length */ - if (i_validate_nvpair_name(nvp) != 0) - return (EFAULT); - - type = NVP_TYPE(nvp); - - /* - * Verify type and nelem and get the value size. - * In case of data types DATA_TYPE_STRING and DATA_TYPE_STRING_ARRAY - * is the size of the string(s) excluded. - */ - if ((value_sz = i_get_value_size(type, NULL, NVP_NELEM(nvp))) < 0) - return (EFAULT); - - if (NVP_SIZE_CALC(nvp->nvp_name_sz, value_sz) > nvp->nvp_size) - return (EFAULT); - - switch (type) { - case DATA_TYPE_NVLIST: - ret = nvpair_native_embedded(nvs, nvp); - break; - case DATA_TYPE_NVLIST_ARRAY: - ret = nvpair_native_embedded_array(nvs, nvp); - break; - case DATA_TYPE_STRING_ARRAY: - nvpair_native_string_array(nvs, nvp); - break; - default: - break; - } - - return (ret); -} - -static int -nvs_native_nvp_size(nvstream_t *nvs, nvpair_t *nvp, size_t *size) -{ - uint64_t nvp_sz = nvp->nvp_size; - - switch (NVP_TYPE(nvp)) { - case DATA_TYPE_NVLIST: { - size_t nvsize = 0; - - if (nvs_operation(nvs, EMBEDDED_NVL(nvp), &nvsize) != 0) - return (EINVAL); - - nvp_sz += nvsize; - break; - } - case DATA_TYPE_NVLIST_ARRAY: { - size_t nvsize; - - if (nvs_embedded_nvl_array(nvs, nvp, &nvsize) != 0) - return (EINVAL); - - nvp_sz += nvsize; - break; - } - default: - break; - } - - if (nvp_sz > INT32_MAX) - return (EINVAL); - - *size = nvp_sz; - - return (0); -} - -static int -nvs_native_nvpair(nvstream_t *nvs, nvpair_t *nvp, size_t *size) -{ - switch (nvs->nvs_op) { - case NVS_OP_ENCODE: - return (nvs_native_nvp_op(nvs, nvp)); - - case NVS_OP_DECODE: { - nvs_native_t *native = (nvs_native_t *)nvs->nvs_private; - int32_t decode_len; - - /* try to read the size value from the stream */ - if (native->n_curr + sizeof (int32_t) > native->n_end) - return (EFAULT); - bcopy(native->n_curr, &decode_len, sizeof (int32_t)); - - /* sanity check the size value */ - if (decode_len < 0 || - decode_len > native->n_end - native->n_curr) - return (EFAULT); - - *size = decode_len; - - /* - * If at the end of the stream then move the cursor - * forward, otherwise nvpair_native_op() will read - * the entire nvpair at the same cursor position. - */ - if (*size == 0) - native->n_curr += sizeof (int32_t); - break; - } - - default: - return (EINVAL); - } - - return (0); -} - -static const nvs_ops_t nvs_native_ops = { - nvs_native_nvlist, - nvs_native_nvpair, - nvs_native_nvp_op, - nvs_native_nvp_size, - nvs_native_nvl_fini -}; - -static int -nvs_native(nvstream_t *nvs, nvlist_t *nvl, char *buf, size_t *buflen) -{ - nvs_native_t native; - int err; - - nvs->nvs_ops = &nvs_native_ops; - - if ((err = nvs_native_create(nvs, &native, buf + sizeof (nvs_header_t), - *buflen - sizeof (nvs_header_t))) != 0) - return (err); - - err = nvs_operation(nvs, nvl, buflen); - - nvs_native_destroy(nvs); - - return (err); -} - -/* - * XDR encoding functions - * - * An xdr packed nvlist is encoded as: - * - * - encoding methode and host endian (4 bytes) - * - nvl_version (4 bytes) - * - nvl_nvflag (4 bytes) - * - * - encoded nvpairs, the format of one xdr encoded nvpair is: - * - encoded size of the nvpair (4 bytes) - * - decoded size of the nvpair (4 bytes) - * - name string, (4 + sizeof(NV_ALIGN4(string)) - * a string is coded as size (4 bytes) and data - * - data type (4 bytes) - * - number of elements in the nvpair (4 bytes) - * - data - * - * - 2 zero's for end of the entire list (8 bytes) - */ -static int -nvs_xdr_create(nvstream_t *nvs, XDR *xdr, char *buf, size_t buflen) -{ - /* xdr data must be 4 byte aligned */ - if ((ulong_t)buf % 4 != 0) - return (EFAULT); - - switch (nvs->nvs_op) { - case NVS_OP_ENCODE: - xdrmem_create(xdr, buf, (uint_t)buflen, XDR_ENCODE); - nvs->nvs_private = xdr; - return (0); - case NVS_OP_DECODE: - xdrmem_create(xdr, buf, (uint_t)buflen, XDR_DECODE); - nvs->nvs_private = xdr; - return (0); - case NVS_OP_GETSIZE: - nvs->nvs_private = NULL; - return (0); - default: - return (EINVAL); - } -} - -static void -nvs_xdr_destroy(nvstream_t *nvs) -{ - switch (nvs->nvs_op) { - case NVS_OP_ENCODE: - case NVS_OP_DECODE: - xdr_destroy((XDR *)nvs->nvs_private); - break; - default: - break; - } -} - -static int -nvs_xdr_nvlist(nvstream_t *nvs, nvlist_t *nvl, size_t *size) -{ - switch (nvs->nvs_op) { - case NVS_OP_ENCODE: - case NVS_OP_DECODE: { - XDR *xdr = nvs->nvs_private; - - if (!xdr_int(xdr, &nvl->nvl_version) || - !xdr_u_int(xdr, &nvl->nvl_nvflag)) - return (EFAULT); - break; - } - case NVS_OP_GETSIZE: { - /* - * 2 * 4 for nvl_version + nvl_nvflag - * and 8 for end of the entire list - */ - *size += 2 * 4 + 8; - break; - } - default: - return (EINVAL); - } - return (0); -} - -static int -nvs_xdr_nvl_fini(nvstream_t *nvs) -{ - if (nvs->nvs_op == NVS_OP_ENCODE) { - XDR *xdr = nvs->nvs_private; - int zero = 0; - - if (!xdr_int(xdr, &zero) || !xdr_int(xdr, &zero)) - return (EFAULT); - } - - return (0); -} - -/* - * The format of xdr encoded nvpair is: - * encode_size, decode_size, name string, data type, nelem, data - */ -static int -nvs_xdr_nvp_op(nvstream_t *nvs, nvpair_t *nvp) -{ - data_type_t type; - char *buf; - char *buf_end = (char *)nvp + nvp->nvp_size; - int value_sz; - uint_t nelem, buflen; - bool_t ret = FALSE; - XDR *xdr = nvs->nvs_private; - - ASSERT(xdr != NULL && nvp != NULL); - - /* name string */ - if ((buf = NVP_NAME(nvp)) >= buf_end) - return (EFAULT); - buflen = buf_end - buf; - - if (!xdr_string(xdr, &buf, buflen - 1)) - return (EFAULT); - nvp->nvp_name_sz = strlen(buf) + 1; - - /* type and nelem */ - if (!xdr_int(xdr, (int *)&nvp->nvp_type) || - !xdr_int(xdr, &nvp->nvp_value_elem)) - return (EFAULT); - - type = NVP_TYPE(nvp); - nelem = nvp->nvp_value_elem; - - /* - * Verify type and nelem and get the value size. - * In case of data types DATA_TYPE_STRING and DATA_TYPE_STRING_ARRAY - * is the size of the string(s) excluded. - */ - if ((value_sz = i_get_value_size(type, NULL, nelem)) < 0) - return (EFAULT); - - /* if there is no data to extract then return */ - if (nelem == 0) - return (0); - - /* value */ - if ((buf = NVP_VALUE(nvp)) >= buf_end) - return (EFAULT); - buflen = buf_end - buf; - - if (buflen < value_sz) - return (EFAULT); - - switch (type) { - case DATA_TYPE_NVLIST: - if (nvs_embedded(nvs, (void *)buf) == 0) - return (0); - break; - - case DATA_TYPE_NVLIST_ARRAY: - if (nvs_embedded_nvl_array(nvs, nvp, NULL) == 0) - return (0); - break; - - case DATA_TYPE_BOOLEAN: - ret = TRUE; - break; - - case DATA_TYPE_BYTE: - case DATA_TYPE_INT8: - case DATA_TYPE_UINT8: - ret = xdr_char(xdr, buf); - break; - - case DATA_TYPE_INT16: - ret = xdr_short(xdr, (void *)buf); - break; - - case DATA_TYPE_UINT16: - ret = xdr_u_short(xdr, (void *)buf); - break; - - case DATA_TYPE_BOOLEAN_VALUE: - case DATA_TYPE_INT32: - ret = xdr_int(xdr, (void *)buf); - break; - - case DATA_TYPE_UINT32: - ret = xdr_u_int(xdr, (void *)buf); - break; - - case DATA_TYPE_INT64: - ret = xdr_longlong_t(xdr, (void *)buf); - break; - - case DATA_TYPE_UINT64: - ret = xdr_u_longlong_t(xdr, (void *)buf); - break; - - case DATA_TYPE_HRTIME: - /* - * NOTE: must expose the definition of hrtime_t here - */ - ret = xdr_longlong_t(xdr, (void *)buf); - break; -#if !defined(_KERNEL) - case DATA_TYPE_DOUBLE: - ret = xdr_double(xdr, (void *)buf); - break; -#endif - case DATA_TYPE_STRING: - ret = xdr_string(xdr, &buf, buflen - 1); - break; - - case DATA_TYPE_BYTE_ARRAY: - ret = xdr_opaque(xdr, buf, nelem); - break; - - case DATA_TYPE_INT8_ARRAY: - case DATA_TYPE_UINT8_ARRAY: - ret = xdr_array(xdr, &buf, &nelem, buflen, sizeof (int8_t), - (xdrproc_t)xdr_char); - break; - - case DATA_TYPE_INT16_ARRAY: - ret = xdr_array(xdr, &buf, &nelem, buflen / sizeof (int16_t), - sizeof (int16_t), (xdrproc_t)xdr_short); - break; - - case DATA_TYPE_UINT16_ARRAY: - ret = xdr_array(xdr, &buf, &nelem, buflen / sizeof (uint16_t), - sizeof (uint16_t), (xdrproc_t)xdr_u_short); - break; - - case DATA_TYPE_BOOLEAN_ARRAY: - case DATA_TYPE_INT32_ARRAY: - ret = xdr_array(xdr, &buf, &nelem, buflen / sizeof (int32_t), - sizeof (int32_t), (xdrproc_t)xdr_int); - break; - - case DATA_TYPE_UINT32_ARRAY: - ret = xdr_array(xdr, &buf, &nelem, buflen / sizeof (uint32_t), - sizeof (uint32_t), (xdrproc_t)xdr_u_int); - break; - - case DATA_TYPE_INT64_ARRAY: - ret = xdr_array(xdr, &buf, &nelem, buflen / sizeof (int64_t), - sizeof (int64_t), (xdrproc_t)xdr_longlong_t); - break; - - case DATA_TYPE_UINT64_ARRAY: - ret = xdr_array(xdr, &buf, &nelem, buflen / sizeof (uint64_t), - sizeof (uint64_t), (xdrproc_t)xdr_u_longlong_t); - break; - - case DATA_TYPE_STRING_ARRAY: { - size_t len = nelem * sizeof (uint64_t); - char **strp = (void *)buf; - int i; - - if (nvs->nvs_op == NVS_OP_DECODE) - bzero(buf, len); /* don't trust packed data */ - - for (i = 0; i < nelem; i++) { - if (buflen <= len) - return (EFAULT); - - buf += len; - buflen -= len; - - if (xdr_string(xdr, &buf, buflen - 1) != TRUE) - return (EFAULT); - - if (nvs->nvs_op == NVS_OP_DECODE) - strp[i] = buf; - len = strlen(buf) + 1; - } - ret = TRUE; - break; - } - default: - break; - } - - return (ret == TRUE ? 0 : EFAULT); -} - -static int -nvs_xdr_nvp_size(nvstream_t *nvs, nvpair_t *nvp, size_t *size) -{ - data_type_t type = NVP_TYPE(nvp); - /* - * encode_size + decode_size + name string size + data type + nelem - * where name string size = 4 + NV_ALIGN4(strlen(NVP_NAME(nvp))) - */ - uint64_t nvp_sz = 4 + 4 + 4 + NV_ALIGN4(strlen(NVP_NAME(nvp))) + 4 + 4; - - switch (type) { - case DATA_TYPE_BOOLEAN: - break; - - case DATA_TYPE_BOOLEAN_VALUE: - case DATA_TYPE_BYTE: - case DATA_TYPE_INT8: - case DATA_TYPE_UINT8: - case DATA_TYPE_INT16: - case DATA_TYPE_UINT16: - case DATA_TYPE_INT32: - case DATA_TYPE_UINT32: - nvp_sz += 4; /* 4 is the minimum xdr unit */ - break; - - case DATA_TYPE_INT64: - case DATA_TYPE_UINT64: - case DATA_TYPE_HRTIME: -#if !defined(_KERNEL) - case DATA_TYPE_DOUBLE: -#endif - nvp_sz += 8; - break; - - case DATA_TYPE_STRING: - nvp_sz += 4 + NV_ALIGN4(strlen((char *)NVP_VALUE(nvp))); - break; - - case DATA_TYPE_BYTE_ARRAY: - nvp_sz += NV_ALIGN4(NVP_NELEM(nvp)); - break; - - case DATA_TYPE_BOOLEAN_ARRAY: - case DATA_TYPE_INT8_ARRAY: - case DATA_TYPE_UINT8_ARRAY: - case DATA_TYPE_INT16_ARRAY: - case DATA_TYPE_UINT16_ARRAY: - case DATA_TYPE_INT32_ARRAY: - case DATA_TYPE_UINT32_ARRAY: - nvp_sz += 4 + 4 * (uint64_t)NVP_NELEM(nvp); - break; - - case DATA_TYPE_INT64_ARRAY: - case DATA_TYPE_UINT64_ARRAY: - nvp_sz += 4 + 8 * (uint64_t)NVP_NELEM(nvp); - break; - - case DATA_TYPE_STRING_ARRAY: { - int i; - char **strs = (void *)NVP_VALUE(nvp); - - for (i = 0; i < NVP_NELEM(nvp); i++) - nvp_sz += 4 + NV_ALIGN4(strlen(strs[i])); - - break; - } - - case DATA_TYPE_NVLIST: - case DATA_TYPE_NVLIST_ARRAY: { - size_t nvsize = 0; - int old_nvs_op = nvs->nvs_op; - int err; - - nvs->nvs_op = NVS_OP_GETSIZE; - if (type == DATA_TYPE_NVLIST) - err = nvs_operation(nvs, EMBEDDED_NVL(nvp), &nvsize); - else - err = nvs_embedded_nvl_array(nvs, nvp, &nvsize); - nvs->nvs_op = old_nvs_op; - - if (err != 0) - return (EINVAL); - - nvp_sz += nvsize; - break; - } - - default: - return (EINVAL); - } - - if (nvp_sz > INT32_MAX) - return (EINVAL); - - *size = nvp_sz; - - return (0); -} - - -/* - * The NVS_XDR_MAX_LEN macro takes a packed xdr buffer of size x and estimates - * the largest nvpair that could be encoded in the buffer. - * - * See comments above nvpair_xdr_op() for the format of xdr encoding. - * The size of a xdr packed nvpair without any data is 5 words. - * - * Using the size of the data directly as an estimate would be ok - * in all cases except one. If the data type is of DATA_TYPE_STRING_ARRAY - * then the actual nvpair has space for an array of pointers to index - * the strings. These pointers are not encoded into the packed xdr buffer. - * - * If the data is of type DATA_TYPE_STRING_ARRAY and all the strings are - * of length 0, then each string is endcoded in xdr format as a single word. - * Therefore when expanded to an nvpair there will be 2.25 word used for - * each string. (a int64_t allocated for pointer usage, and a single char - * for the null termination.) - * - * This is the calculation performed by the NVS_XDR_MAX_LEN macro. - */ -#define NVS_XDR_HDR_LEN ((size_t)(5 * 4)) -#define NVS_XDR_DATA_LEN(y) (((size_t)(y) <= NVS_XDR_HDR_LEN) ? \ - 0 : ((size_t)(y) - NVS_XDR_HDR_LEN)) -#define NVS_XDR_MAX_LEN(x) (NVP_SIZE_CALC(1, 0) + \ - (NVS_XDR_DATA_LEN(x) * 2) + \ - NV_ALIGN4((NVS_XDR_DATA_LEN(x) / 4))) - -static int -nvs_xdr_nvpair(nvstream_t *nvs, nvpair_t *nvp, size_t *size) -{ - XDR *xdr = nvs->nvs_private; - int32_t encode_len, decode_len; - - switch (nvs->nvs_op) { - case NVS_OP_ENCODE: { - size_t nvsize; - - if (nvs_xdr_nvp_size(nvs, nvp, &nvsize) != 0) - return (EFAULT); - - decode_len = nvp->nvp_size; - encode_len = nvsize; - if (!xdr_int(xdr, &encode_len) || !xdr_int(xdr, &decode_len)) - return (EFAULT); - - return (nvs_xdr_nvp_op(nvs, nvp)); - } - case NVS_OP_DECODE: { - struct xdr_bytesrec bytesrec; - - /* get the encode and decode size */ - if (!xdr_int(xdr, &encode_len) || !xdr_int(xdr, &decode_len)) - return (EFAULT); - *size = decode_len; - - /* are we at the end of the stream? */ - if (*size == 0) - return (0); - - /* sanity check the size parameter */ - if (!xdr_control(xdr, XDR_GET_BYTES_AVAIL, &bytesrec)) - return (EFAULT); - - if (*size > NVS_XDR_MAX_LEN(bytesrec.xc_num_avail)) - return (EFAULT); - break; - } - - default: - return (EINVAL); - } - return (0); -} - -static const struct nvs_ops nvs_xdr_ops = { - nvs_xdr_nvlist, - nvs_xdr_nvpair, - nvs_xdr_nvp_op, - nvs_xdr_nvp_size, - nvs_xdr_nvl_fini -}; - -static int -nvs_xdr(nvstream_t *nvs, nvlist_t *nvl, char *buf, size_t *buflen) -{ - XDR xdr; - int err; - - nvs->nvs_ops = &nvs_xdr_ops; - - if ((err = nvs_xdr_create(nvs, &xdr, buf + sizeof (nvs_header_t), - *buflen - sizeof (nvs_header_t))) != 0) - return (err); - - err = nvs_operation(nvs, nvl, buflen); - - nvs_xdr_destroy(nvs); - - return (err); -} Index: sys/cddl/contrib/opensolaris/common/nvpair/nvpair_alloc_fixed.c =================================================================== --- sys/cddl/contrib/opensolaris/common/nvpair/nvpair_alloc_fixed.c +++ /dev/null @@ -1,118 +0,0 @@ -/* - * CDDL HEADER START - * - * The contents of this file are subject to the terms of the - * Common Development and Distribution License (the "License"). - * You may not use this file except in compliance with the License. - * - * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE - * or http://www.opensolaris.org/os/licensing. - * See the License for the specific language governing permissions - * and limitations under the License. - * - * When distributing Covered Code, include this CDDL HEADER in each - * file and include the License file at usr/src/OPENSOLARIS.LICENSE. - * If applicable, add the following below this CDDL HEADER, with the - * fields enclosed by brackets "[]" replaced with your own identifying - * information: Portions Copyright [yyyy] [name of copyright owner] - * - * CDDL HEADER END - */ - -/* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. - */ - -#pragma ident "%Z%%M% %I% %E% SMI" - -#include -#include -#if defined(_KERNEL) && !defined(_BOOT) -#include -#else -#include -#include -#endif - -/* - * This allocator is very simple. - * - it uses a pre-allocated buffer for memory allocations. - * - it does _not_ free memory in the pre-allocated buffer. - * - * The reason for the selected implemention is simplicity. - * This allocator is designed for the usage in interrupt context when - * the caller may not wait for free memory. - */ - -/* pre-allocated buffer for memory allocations */ -typedef struct nvbuf { - uintptr_t nvb_buf; /* address of pre-allocated buffer */ - uintptr_t nvb_lim; /* limit address in the buffer */ - uintptr_t nvb_cur; /* current address in the buffer */ -} nvbuf_t; - -/* - * Initialize the pre-allocated buffer allocator. The caller needs to supply - * - * buf address of pre-allocated buffer - * bufsz size of pre-allocated buffer - * - * nv_fixed_init() calculates the remaining members of nvbuf_t. - */ -static int -nv_fixed_init(nv_alloc_t *nva, va_list valist) -{ - uintptr_t base = va_arg(valist, uintptr_t); - uintptr_t lim = base + va_arg(valist, size_t); - nvbuf_t *nvb = (nvbuf_t *)P2ROUNDUP(base, sizeof (uintptr_t)); - - if (base == 0 || (uintptr_t)&nvb[1] > lim) - return (EINVAL); - - nvb->nvb_buf = (uintptr_t)&nvb[0]; - nvb->nvb_cur = (uintptr_t)&nvb[1]; - nvb->nvb_lim = lim; - nva->nva_arg = nvb; - - return (0); -} - -static void * -nv_fixed_alloc(nv_alloc_t *nva, size_t size) -{ - nvbuf_t *nvb = nva->nva_arg; - uintptr_t new = nvb->nvb_cur; - - if (size == 0 || new + size > nvb->nvb_lim) - return (NULL); - - nvb->nvb_cur = P2ROUNDUP(new + size, sizeof (uintptr_t)); - - return ((void *)new); -} - -/*ARGSUSED*/ -static void -nv_fixed_free(nv_alloc_t *nva, void *buf, size_t size) -{ - /* don't free memory in the pre-allocated buffer */ -} - -static void -nv_fixed_reset(nv_alloc_t *nva) -{ - nvbuf_t *nvb = nva->nva_arg; - - nvb->nvb_cur = (uintptr_t)&nvb[1]; -} - -const nv_alloc_ops_t nv_fixed_ops_def = { - nv_fixed_init, /* nv_ao_init() */ - NULL, /* nv_ao_fini() */ - nv_fixed_alloc, /* nv_ao_alloc() */ - nv_fixed_free, /* nv_ao_free() */ - nv_fixed_reset /* nv_ao_reset() */ -}; - -const nv_alloc_ops_t *nv_fixed_ops = &nv_fixed_ops_def; Index: sys/cddl/contrib/opensolaris/common/nvpair/opensolaris_fnvpair.c =================================================================== --- /dev/null +++ sys/cddl/contrib/opensolaris/common/nvpair/opensolaris_fnvpair.c @@ -0,0 +1,512 @@ + +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright (c) 2012 by Delphix. All rights reserved. + */ + +#include +#ifndef _KERNEL +#include +#else +#include +#include +#include +#include +#endif + +/* + * "Force" nvlist wrapper. + * + * These functions wrap the nvlist_* functions with assertions that assume + * the operation is successful. This allows the caller's code to be much + * more readable, especially for the fnvlist_lookup_* and fnvpair_value_* + * functions, which can return the requested value (rather than filling in + * a pointer). + * + * These functions use NV_UNIQUE_NAME, encoding NV_ENCODE_NATIVE, and allocate + * with KM_SLEEP. + * + * More wrappers should be added as needed -- for example + * nvlist_lookup_*_array and nvpair_value_*_array. + */ + +nvlist_t * +fnvlist_alloc(void) +{ + nvlist_t *nvl; + VERIFY0(nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP)); + return (nvl); +} + +void +fnvlist_free(nvlist_t *nvl) +{ + nvlist_free(nvl); +} + +size_t +fnvlist_size(nvlist_t *nvl) +{ + size_t size; + VERIFY0(nvlist_size(nvl, &size, NV_ENCODE_NATIVE)); + return (size); +} + +/* + * Returns allocated buffer of size *sizep. Caller must free the buffer with + * fnvlist_pack_free(). + */ +char * +fnvlist_pack(nvlist_t *nvl, size_t *sizep) +{ + char *packed = 0; + VERIFY3U(nvlist_pack(nvl, &packed, sizep, NV_ENCODE_NATIVE, + KM_SLEEP), ==, 0); + return (packed); +} + +/*ARGSUSED*/ +void +fnvlist_pack_free(char *pack, size_t size) +{ +#ifdef _KERNEL + kmem_free(pack, size); +#else + free(pack); +#endif +} + +nvlist_t * +fnvlist_unpack(char *buf, size_t buflen) +{ + nvlist_t *rv; + VERIFY0(nvlist_unpack(buf, buflen, &rv, KM_SLEEP)); + return (rv); +} + +nvlist_t * +fnvlist_dup(nvlist_t *nvl) +{ + nvlist_t *rv; + VERIFY0(nvlist_dup(nvl, &rv, KM_SLEEP)); + return (rv); +} + +void +fnvlist_merge(nvlist_t *dst, nvlist_t *src) +{ + VERIFY0(nvlist_merge(dst, src, KM_SLEEP)); +} + +size_t +fnvlist_num_pairs(nvlist_t *nvl) +{ + size_t count = 0; + nvpair_t *pair; + + for (pair = nvlist_next_nvpair(nvl, 0); pair != NULL; + pair = nvlist_next_nvpair(nvl, pair)) + count++; + return (count); +} + +void +fnvlist_add_boolean(nvlist_t *nvl, const char *name) +{ + VERIFY0(nvlist_add_boolean(nvl, name)); +} + +void +fnvlist_add_boolean_value(nvlist_t *nvl, const char *name, boolean_t val) +{ + VERIFY0(nvlist_add_boolean_value(nvl, name, val)); +} + +void +fnvlist_add_byte(nvlist_t *nvl, const char *name, uchar_t val) +{ + VERIFY0(nvlist_add_byte(nvl, name, val)); +} + +void +fnvlist_add_int8(nvlist_t *nvl, const char *name, int8_t val) +{ + VERIFY0(nvlist_add_int8(nvl, name, val)); +} + +void +fnvlist_add_uint8(nvlist_t *nvl, const char *name, uint8_t val) +{ + VERIFY0(nvlist_add_uint8(nvl, name, val)); +} + +void +fnvlist_add_int16(nvlist_t *nvl, const char *name, int16_t val) +{ + VERIFY0(nvlist_add_int16(nvl, name, val)); +} + +void +fnvlist_add_uint16(nvlist_t *nvl, const char *name, uint16_t val) +{ + VERIFY0(nvlist_add_uint16(nvl, name, val)); +} + +void +fnvlist_add_int32(nvlist_t *nvl, const char *name, int32_t val) +{ + VERIFY0(nvlist_add_int32(nvl, name, val)); +} + +void +fnvlist_add_uint32(nvlist_t *nvl, const char *name, uint32_t val) +{ + VERIFY0(nvlist_add_uint32(nvl, name, val)); +} + +void +fnvlist_add_int64(nvlist_t *nvl, const char *name, int64_t val) +{ + VERIFY0(nvlist_add_int64(nvl, name, val)); +} + +void +fnvlist_add_uint64(nvlist_t *nvl, const char *name, uint64_t val) +{ + VERIFY0(nvlist_add_uint64(nvl, name, val)); +} + +void +fnvlist_add_string(nvlist_t *nvl, const char *name, const char *val) +{ + VERIFY0(nvlist_add_string(nvl, name, val)); +} + +void +fnvlist_add_nvlist(nvlist_t *nvl, const char *name, nvlist_t *val) +{ + VERIFY0(nvlist_add_nvlist(nvl, name, val)); +} + +void +fnvlist_add_nvpair(nvlist_t *nvl, nvpair_t *pair) +{ + VERIFY0(nvlist_add_nvpair(nvl, pair)); +} + +void +fnvlist_add_boolean_array(nvlist_t *nvl, const char *name, + boolean_t *val, uint_t n) +{ + VERIFY0(nvlist_add_boolean_array(nvl, name, val, n)); +} + +void +fnvlist_add_byte_array(nvlist_t *nvl, const char *name, uchar_t *val, uint_t n) +{ + VERIFY0(nvlist_add_byte_array(nvl, name, val, n)); +} + +void +fnvlist_add_int8_array(nvlist_t *nvl, const char *name, int8_t *val, uint_t n) +{ + VERIFY0(nvlist_add_int8_array(nvl, name, val, n)); +} + +void +fnvlist_add_uint8_array(nvlist_t *nvl, const char *name, uint8_t *val, uint_t n) +{ + VERIFY0(nvlist_add_uint8_array(nvl, name, val, n)); +} + +void +fnvlist_add_int16_array(nvlist_t *nvl, const char *name, int16_t *val, uint_t n) +{ + VERIFY0(nvlist_add_int16_array(nvl, name, val, n)); +} + +void +fnvlist_add_uint16_array(nvlist_t *nvl, const char *name, + uint16_t *val, uint_t n) +{ + VERIFY0(nvlist_add_uint16_array(nvl, name, val, n)); +} + +void +fnvlist_add_int32_array(nvlist_t *nvl, const char *name, int32_t *val, uint_t n) +{ + VERIFY0(nvlist_add_int32_array(nvl, name, val, n)); +} + +void +fnvlist_add_uint32_array(nvlist_t *nvl, const char *name, + uint32_t *val, uint_t n) +{ + VERIFY0(nvlist_add_uint32_array(nvl, name, val, n)); +} + +void +fnvlist_add_int64_array(nvlist_t *nvl, const char *name, int64_t *val, uint_t n) +{ + VERIFY0(nvlist_add_int64_array(nvl, name, val, n)); +} + +void +fnvlist_add_uint64_array(nvlist_t *nvl, const char *name, + uint64_t *val, uint_t n) +{ + VERIFY0(nvlist_add_uint64_array(nvl, name, val, n)); +} + +void +fnvlist_add_string_array(nvlist_t *nvl, const char *name, + char * const *val, uint_t n) +{ + VERIFY0(nvlist_add_string_array(nvl, name, val, n)); +} + +void +fnvlist_add_nvlist_array(nvlist_t *nvl, const char *name, + nvlist_t **val, uint_t n) +{ + VERIFY0(nvlist_add_nvlist_array(nvl, name, val, n)); +} + +void +fnvlist_remove(nvlist_t *nvl, const char *name) +{ + VERIFY0(nvlist_remove_all(nvl, name)); +} + +void +fnvlist_remove_nvpair(nvlist_t *nvl, nvpair_t *pair) +{ + VERIFY0(nvlist_remove_nvpair(nvl, pair)); +} + +nvpair_t * +fnvlist_lookup_nvpair(nvlist_t *nvl, const char *name) +{ + nvpair_t *rv; + VERIFY0(nvlist_lookup_nvpair(nvl, name, &rv)); + return (rv); +} + +/* returns B_TRUE if the entry exists */ +boolean_t +fnvlist_lookup_boolean(nvlist_t *nvl, const char *name) +{ + return (nvlist_lookup_boolean(nvl, name) == 0); +} + +boolean_t +fnvlist_lookup_boolean_value(nvlist_t *nvl, const char *name) +{ + boolean_t rv; + VERIFY0(nvlist_lookup_boolean_value(nvl, name, &rv)); + return (rv); +} + +uchar_t +fnvlist_lookup_byte(nvlist_t *nvl, const char *name) +{ + uchar_t rv; + VERIFY0(nvlist_lookup_byte(nvl, name, &rv)); + return (rv); +} + +int8_t +fnvlist_lookup_int8(nvlist_t *nvl, const char *name) +{ + int8_t rv; + VERIFY0(nvlist_lookup_int8(nvl, name, &rv)); + return (rv); +} + +int16_t +fnvlist_lookup_int16(nvlist_t *nvl, const char *name) +{ + int16_t rv; + VERIFY0(nvlist_lookup_int16(nvl, name, &rv)); + return (rv); +} + +int32_t +fnvlist_lookup_int32(nvlist_t *nvl, const char *name) +{ + int32_t rv; + VERIFY0(nvlist_lookup_int32(nvl, name, &rv)); + return (rv); +} + +int64_t +fnvlist_lookup_int64(nvlist_t *nvl, const char *name) +{ + int64_t rv; + VERIFY0(nvlist_lookup_int64(nvl, name, &rv)); + return (rv); +} + +uint8_t +fnvlist_lookup_uint8_t(nvlist_t *nvl, const char *name) +{ + uint8_t rv; + VERIFY0(nvlist_lookup_uint8(nvl, name, &rv)); + return (rv); +} + +uint16_t +fnvlist_lookup_uint16(nvlist_t *nvl, const char *name) +{ + uint16_t rv; + VERIFY0(nvlist_lookup_uint16(nvl, name, &rv)); + return (rv); +} + +uint32_t +fnvlist_lookup_uint32(nvlist_t *nvl, const char *name) +{ + uint32_t rv; + VERIFY0(nvlist_lookup_uint32(nvl, name, &rv)); + return (rv); +} + +uint64_t +fnvlist_lookup_uint64(nvlist_t *nvl, const char *name) +{ + uint64_t rv; + VERIFY0(nvlist_lookup_uint64(nvl, name, &rv)); + return (rv); +} + +char * +fnvlist_lookup_string(nvlist_t *nvl, const char *name) +{ + char *rv; + VERIFY0(nvlist_lookup_string(nvl, name, &rv)); + return (rv); +} + +nvlist_t * +fnvlist_lookup_nvlist(nvlist_t *nvl, const char *name) +{ + nvlist_t *rv; + VERIFY0(nvlist_lookup_nvlist(nvl, name, &rv)); + return (rv); +} + +boolean_t +fnvpair_value_boolean_value(nvpair_t *nvp) +{ + boolean_t rv; + VERIFY0(nvpair_value_boolean_value(nvp, &rv)); + return (rv); +} + +uchar_t +fnvpair_value_byte(nvpair_t *nvp) +{ + uchar_t rv; + VERIFY0(nvpair_value_byte(nvp, &rv)); + return (rv); +} + +int8_t +fnvpair_value_int8(nvpair_t *nvp) +{ + int8_t rv; + VERIFY0(nvpair_value_int8(nvp, &rv)); + return (rv); +} + +int16_t +fnvpair_value_int16(nvpair_t *nvp) +{ + int16_t rv; + VERIFY0(nvpair_value_int16(nvp, &rv)); + return (rv); +} + +int32_t +fnvpair_value_int32(nvpair_t *nvp) +{ + int32_t rv; + VERIFY0(nvpair_value_int32(nvp, &rv)); + return (rv); +} + +int64_t +fnvpair_value_int64(nvpair_t *nvp) +{ + int64_t rv; + VERIFY0(nvpair_value_int64(nvp, &rv)); + return (rv); +} + +uint8_t +fnvpair_value_uint8_t(nvpair_t *nvp) +{ + uint8_t rv; + VERIFY0(nvpair_value_uint8(nvp, &rv)); + return (rv); +} + +uint16_t +fnvpair_value_uint16(nvpair_t *nvp) +{ + uint16_t rv; + VERIFY0(nvpair_value_uint16(nvp, &rv)); + return (rv); +} + +uint32_t +fnvpair_value_uint32(nvpair_t *nvp) +{ + uint32_t rv; + VERIFY0(nvpair_value_uint32(nvp, &rv)); + return (rv); +} + +uint64_t +fnvpair_value_uint64(nvpair_t *nvp) +{ + uint64_t rv; + VERIFY0(nvpair_value_uint64(nvp, &rv)); + return (rv); +} + +char * +fnvpair_value_string(nvpair_t *nvp) +{ + char *rv; + VERIFY0(nvpair_value_string(nvp, &rv)); + return (rv); +} + +nvlist_t * +fnvpair_value_nvlist(nvpair_t *nvp) +{ + nvlist_t *rv; + VERIFY0(nvpair_value_nvlist(nvp, &rv)); + return (rv); +} Index: sys/cddl/contrib/opensolaris/common/nvpair/opensolaris_nvpair.c =================================================================== --- /dev/null +++ sys/cddl/contrib/opensolaris/common/nvpair/opensolaris_nvpair.c @@ -0,0 +1,3306 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. + */ + +#include +#include +#include +#include +#include + +#if defined(_KERNEL) && !defined(_BOOT) +#include +#include +#else +#include +#include +#include +#include +#endif + +#ifndef offsetof +#define offsetof(s, m) ((size_t)(&(((s *)0)->m))) +#endif +#define skip_whitespace(p) while ((*(p) == ' ') || (*(p) == '\t')) p++ + +#if defined(__FreeBSD__) && !defined(_KERNEL) +/* + * libnvpair is the lowest commen denominator for ZFS related libraries, + * defining aok here makes it usable by all ZFS related libraries + */ +int aok; +#endif + +/* + * nvpair.c - Provides kernel & userland interfaces for manipulating + * name-value pairs. + * + * Overview Diagram + * + * +--------------+ + * | nvlist_t | + * |--------------| + * | nvl_version | + * | nvl_nvflag | + * | nvl_priv -+-+ + * | nvl_flag | | + * | nvl_pad | | + * +--------------+ | + * V + * +--------------+ last i_nvp in list + * | nvpriv_t | +---------------------> + * |--------------| | + * +--+- nvp_list | | +------------+ + * | | nvp_last -+--+ + nv_alloc_t | + * | | nvp_curr | |------------| + * | | nvp_nva -+----> | nva_ops | + * | | nvp_stat | | nva_arg | + * | +--------------+ +------------+ + * | + * +-------+ + * V + * +---------------------+ +-------------------+ + * | i_nvp_t | +-->| i_nvp_t | +--> + * |---------------------| | |-------------------| | + * | nvi_next -+--+ | nvi_next -+--+ + * | nvi_prev (NULL) | <----+ nvi_prev | + * | . . . . . . . . . . | | . . . . . . . . . | + * | nvp (nvpair_t) | | nvp (nvpair_t) | + * | - nvp_size | | - nvp_size | + * | - nvp_name_sz | | - nvp_name_sz | + * | - nvp_value_elem | | - nvp_value_elem | + * | - nvp_type | | - nvp_type | + * | - data ... | | - data ... | + * +---------------------+ +-------------------+ + * + * + * + * +---------------------+ +---------------------+ + * | i_nvp_t | +--> +-->| i_nvp_t (last) | + * |---------------------| | | |---------------------| + * | nvi_next -+--+ ... --+ | nvi_next (NULL) | + * <-+- nvi_prev |<-- ... <----+ nvi_prev | + * | . . . . . . . . . | | . . . . . . . . . | + * | nvp (nvpair_t) | | nvp (nvpair_t) | + * | - nvp_size | | - nvp_size | + * | - nvp_name_sz | | - nvp_name_sz | + * | - nvp_value_elem | | - nvp_value_elem | + * | - DATA_TYPE_NVLIST | | - nvp_type | + * | - data (embedded) | | - data ... | + * | nvlist name | +---------------------+ + * | +--------------+ | + * | | nvlist_t | | + * | |--------------| | + * | | nvl_version | | + * | | nvl_nvflag | | + * | | nvl_priv --+---+----> + * | | nvl_flag | | + * | | nvl_pad | | + * | +--------------+ | + * +---------------------+ + * + * + * N.B. nvpair_t may be aligned on 4 byte boundary, so +4 will + * allow value to be aligned on 8 byte boundary + * + * name_len is the length of the name string including the null terminator + * so it must be >= 1 + */ +#define NVP_SIZE_CALC(name_len, data_len) \ + (NV_ALIGN((sizeof (nvpair_t)) + name_len) + NV_ALIGN(data_len)) + +static int i_get_value_size(data_type_t type, const void *data, uint_t nelem); +static int nvlist_add_common(nvlist_t *nvl, const char *name, data_type_t type, + uint_t nelem, const void *data); + +#define NV_STAT_EMBEDDED 0x1 +#define EMBEDDED_NVL(nvp) ((nvlist_t *)(void *)NVP_VALUE(nvp)) +#define EMBEDDED_NVL_ARRAY(nvp) ((nvlist_t **)(void *)NVP_VALUE(nvp)) + +#define NVP_VALOFF(nvp) (NV_ALIGN(sizeof (nvpair_t) + (nvp)->nvp_name_sz)) +#define NVPAIR2I_NVP(nvp) \ + ((i_nvp_t *)((size_t)(nvp) - offsetof(i_nvp_t, nvi_nvp))) + + +int +nv_alloc_init(nv_alloc_t *nva, const nv_alloc_ops_t *nvo, /* args */ ...) +{ + va_list valist; + int err = 0; + + nva->nva_ops = nvo; + nva->nva_arg = NULL; + + va_start(valist, nvo); + if (nva->nva_ops->nv_ao_init != NULL) + err = nva->nva_ops->nv_ao_init(nva, valist); + va_end(valist); + + return (err); +} + +void +nv_alloc_reset(nv_alloc_t *nva) +{ + if (nva->nva_ops->nv_ao_reset != NULL) + nva->nva_ops->nv_ao_reset(nva); +} + +void +nv_alloc_fini(nv_alloc_t *nva) +{ + if (nva->nva_ops->nv_ao_fini != NULL) + nva->nva_ops->nv_ao_fini(nva); +} + +nv_alloc_t * +nvlist_lookup_nv_alloc(nvlist_t *nvl) +{ + nvpriv_t *priv; + + if (nvl == NULL || + (priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv) == NULL) + return (NULL); + + return (priv->nvp_nva); +} + +static void * +nv_mem_zalloc(nvpriv_t *nvp, size_t size) +{ + nv_alloc_t *nva = nvp->nvp_nva; + void *buf; + + if ((buf = nva->nva_ops->nv_ao_alloc(nva, size)) != NULL) + bzero(buf, size); + + return (buf); +} + +static void +nv_mem_free(nvpriv_t *nvp, void *buf, size_t size) +{ + nv_alloc_t *nva = nvp->nvp_nva; + + nva->nva_ops->nv_ao_free(nva, buf, size); +} + +static void +nv_priv_init(nvpriv_t *priv, nv_alloc_t *nva, uint32_t stat) +{ + bzero(priv, sizeof (nvpriv_t)); + + priv->nvp_nva = nva; + priv->nvp_stat = stat; +} + +static nvpriv_t * +nv_priv_alloc(nv_alloc_t *nva) +{ + nvpriv_t *priv; + + /* + * nv_mem_alloc() cannot called here because it needs the priv + * argument. + */ + if ((priv = nva->nva_ops->nv_ao_alloc(nva, sizeof (nvpriv_t))) == NULL) + return (NULL); + + nv_priv_init(priv, nva, 0); + + return (priv); +} + +/* + * Embedded lists need their own nvpriv_t's. We create a new + * nvpriv_t using the parameters and allocator from the parent + * list's nvpriv_t. + */ +static nvpriv_t * +nv_priv_alloc_embedded(nvpriv_t *priv) +{ + nvpriv_t *emb_priv; + + if ((emb_priv = nv_mem_zalloc(priv, sizeof (nvpriv_t))) == NULL) + return (NULL); + + nv_priv_init(emb_priv, priv->nvp_nva, NV_STAT_EMBEDDED); + + return (emb_priv); +} + +static void +nvlist_init(nvlist_t *nvl, uint32_t nvflag, nvpriv_t *priv) +{ + nvl->nvl_version = NV_VERSION; + nvl->nvl_nvflag = nvflag & (NV_UNIQUE_NAME|NV_UNIQUE_NAME_TYPE); + nvl->nvl_priv = (uint64_t)(uintptr_t)priv; + nvl->nvl_flag = 0; + nvl->nvl_pad = 0; +} + +uint_t +nvlist_nvflag(nvlist_t *nvl) +{ + return (nvl->nvl_nvflag); +} + +/* + * nvlist_alloc - Allocate nvlist. + */ +/*ARGSUSED1*/ +int +nvlist_alloc(nvlist_t **nvlp, uint_t nvflag, int kmflag) +{ +#if defined(_KERNEL) && !defined(_BOOT) + return (nvlist_xalloc(nvlp, nvflag, + (kmflag == KM_SLEEP ? nv_alloc_sleep : nv_alloc_nosleep))); +#else + return (nvlist_xalloc(nvlp, nvflag, nv_alloc_nosleep)); +#endif +} + +int +nvlist_xalloc(nvlist_t **nvlp, uint_t nvflag, nv_alloc_t *nva) +{ + nvpriv_t *priv; + + if (nvlp == NULL || nva == NULL) + return (EINVAL); + + if ((priv = nv_priv_alloc(nva)) == NULL) + return (ENOMEM); + + if ((*nvlp = nv_mem_zalloc(priv, + NV_ALIGN(sizeof (nvlist_t)))) == NULL) { + nv_mem_free(priv, priv, sizeof (nvpriv_t)); + return (ENOMEM); + } + + nvlist_init(*nvlp, nvflag, priv); + + return (0); +} + +/* + * nvp_buf_alloc - Allocate i_nvp_t for storing a new nv pair. + */ +static nvpair_t * +nvp_buf_alloc(nvlist_t *nvl, size_t len) +{ + nvpriv_t *priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv; + i_nvp_t *buf; + nvpair_t *nvp; + size_t nvsize; + + /* + * Allocate the buffer + */ + nvsize = len + offsetof(i_nvp_t, nvi_nvp); + + if ((buf = nv_mem_zalloc(priv, nvsize)) == NULL) + return (NULL); + + nvp = &buf->nvi_nvp; + nvp->nvp_size = len; + + return (nvp); +} + +/* + * nvp_buf_free - de-Allocate an i_nvp_t. + */ +static void +nvp_buf_free(nvlist_t *nvl, nvpair_t *nvp) +{ + nvpriv_t *priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv; + size_t nvsize = nvp->nvp_size + offsetof(i_nvp_t, nvi_nvp); + + nv_mem_free(priv, NVPAIR2I_NVP(nvp), nvsize); +} + +/* + * nvp_buf_link - link a new nv pair into the nvlist. + */ +static void +nvp_buf_link(nvlist_t *nvl, nvpair_t *nvp) +{ + nvpriv_t *priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv; + i_nvp_t *curr = NVPAIR2I_NVP(nvp); + + /* Put element at end of nvlist */ + if (priv->nvp_list == NULL) { + priv->nvp_list = priv->nvp_last = curr; + } else { + curr->nvi_prev = priv->nvp_last; + priv->nvp_last->nvi_next = curr; + priv->nvp_last = curr; + } +} + +/* + * nvp_buf_unlink - unlink an removed nvpair out of the nvlist. + */ +static void +nvp_buf_unlink(nvlist_t *nvl, nvpair_t *nvp) +{ + nvpriv_t *priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv; + i_nvp_t *curr = NVPAIR2I_NVP(nvp); + + /* + * protect nvlist_next_nvpair() against walking on freed memory. + */ + if (priv->nvp_curr == curr) + priv->nvp_curr = curr->nvi_next; + + if (curr == priv->nvp_list) + priv->nvp_list = curr->nvi_next; + else + curr->nvi_prev->nvi_next = curr->nvi_next; + + if (curr == priv->nvp_last) + priv->nvp_last = curr->nvi_prev; + else + curr->nvi_next->nvi_prev = curr->nvi_prev; +} + +/* + * take a nvpair type and number of elements and make sure the are valid + */ +static int +i_validate_type_nelem(data_type_t type, uint_t nelem) +{ + switch (type) { + case DATA_TYPE_BOOLEAN: + if (nelem != 0) + return (EINVAL); + break; + case DATA_TYPE_BOOLEAN_VALUE: + case DATA_TYPE_BYTE: + case DATA_TYPE_INT8: + case DATA_TYPE_UINT8: + case DATA_TYPE_INT16: + case DATA_TYPE_UINT16: + case DATA_TYPE_INT32: + case DATA_TYPE_UINT32: + case DATA_TYPE_INT64: + case DATA_TYPE_UINT64: + case DATA_TYPE_STRING: + case DATA_TYPE_HRTIME: + case DATA_TYPE_NVLIST: +#if !defined(_KERNEL) + case DATA_TYPE_DOUBLE: +#endif + if (nelem != 1) + return (EINVAL); + break; + case DATA_TYPE_BOOLEAN_ARRAY: + case DATA_TYPE_BYTE_ARRAY: + case DATA_TYPE_INT8_ARRAY: + case DATA_TYPE_UINT8_ARRAY: + case DATA_TYPE_INT16_ARRAY: + case DATA_TYPE_UINT16_ARRAY: + case DATA_TYPE_INT32_ARRAY: + case DATA_TYPE_UINT32_ARRAY: + case DATA_TYPE_INT64_ARRAY: + case DATA_TYPE_UINT64_ARRAY: + case DATA_TYPE_STRING_ARRAY: + case DATA_TYPE_NVLIST_ARRAY: + /* we allow arrays with 0 elements */ + break; + default: + return (EINVAL); + } + return (0); +} + +/* + * Verify nvp_name_sz and check the name string length. + */ +static int +i_validate_nvpair_name(nvpair_t *nvp) +{ + if ((nvp->nvp_name_sz <= 0) || + (nvp->nvp_size < NVP_SIZE_CALC(nvp->nvp_name_sz, 0))) + return (EFAULT); + + /* verify the name string, make sure its terminated */ + if (NVP_NAME(nvp)[nvp->nvp_name_sz - 1] != '\0') + return (EFAULT); + + return (strlen(NVP_NAME(nvp)) == nvp->nvp_name_sz - 1 ? 0 : EFAULT); +} + +static int +i_validate_nvpair_value(data_type_t type, uint_t nelem, const void *data) +{ + switch (type) { + case DATA_TYPE_BOOLEAN_VALUE: + if (*(boolean_t *)data != B_TRUE && + *(boolean_t *)data != B_FALSE) + return (EINVAL); + break; + case DATA_TYPE_BOOLEAN_ARRAY: { + int i; + + for (i = 0; i < nelem; i++) + if (((boolean_t *)data)[i] != B_TRUE && + ((boolean_t *)data)[i] != B_FALSE) + return (EINVAL); + break; + } + default: + break; + } + + return (0); +} + +/* + * This function takes a pointer to what should be a nvpair and it's size + * and then verifies that all the nvpair fields make sense and can be + * trusted. This function is used when decoding packed nvpairs. + */ +static int +i_validate_nvpair(nvpair_t *nvp) +{ + data_type_t type = NVP_TYPE(nvp); + int size1, size2; + + /* verify nvp_name_sz, check the name string length */ + if (i_validate_nvpair_name(nvp) != 0) + return (EFAULT); + + if (i_validate_nvpair_value(type, NVP_NELEM(nvp), NVP_VALUE(nvp)) != 0) + return (EFAULT); + + /* + * verify nvp_type, nvp_value_elem, and also possibly + * verify string values and get the value size. + */ + size2 = i_get_value_size(type, NVP_VALUE(nvp), NVP_NELEM(nvp)); + size1 = nvp->nvp_size - NVP_VALOFF(nvp); + if (size2 < 0 || size1 != NV_ALIGN(size2)) + return (EFAULT); + + return (0); +} + +static int +nvlist_copy_pairs(nvlist_t *snvl, nvlist_t *dnvl) +{ + nvpriv_t *priv; + i_nvp_t *curr; + + if ((priv = (nvpriv_t *)(uintptr_t)snvl->nvl_priv) == NULL) + return (EINVAL); + + for (curr = priv->nvp_list; curr != NULL; curr = curr->nvi_next) { + nvpair_t *nvp = &curr->nvi_nvp; + int err; + + if ((err = nvlist_add_common(dnvl, NVP_NAME(nvp), NVP_TYPE(nvp), + NVP_NELEM(nvp), NVP_VALUE(nvp))) != 0) + return (err); + } + + return (0); +} + +/* + * Frees all memory allocated for an nvpair (like embedded lists) with + * the exception of the nvpair buffer itself. + */ +static void +nvpair_free(nvpair_t *nvp) +{ + switch (NVP_TYPE(nvp)) { + case DATA_TYPE_NVLIST: + nvlist_free(EMBEDDED_NVL(nvp)); + break; + case DATA_TYPE_NVLIST_ARRAY: { + nvlist_t **nvlp = EMBEDDED_NVL_ARRAY(nvp); + int i; + + for (i = 0; i < NVP_NELEM(nvp); i++) + if (nvlp[i] != NULL) + nvlist_free(nvlp[i]); + break; + } + default: + break; + } +} + +/* + * nvlist_free - free an unpacked nvlist + */ +void +nvlist_free(nvlist_t *nvl) +{ + nvpriv_t *priv; + i_nvp_t *curr; + + if (nvl == NULL || + (priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv) == NULL) + return; + + /* + * Unpacked nvlist are linked through i_nvp_t + */ + curr = priv->nvp_list; + while (curr != NULL) { + nvpair_t *nvp = &curr->nvi_nvp; + curr = curr->nvi_next; + + nvpair_free(nvp); + nvp_buf_free(nvl, nvp); + } + + if (!(priv->nvp_stat & NV_STAT_EMBEDDED)) + nv_mem_free(priv, nvl, NV_ALIGN(sizeof (nvlist_t))); + else + nvl->nvl_priv = 0; + + nv_mem_free(priv, priv, sizeof (nvpriv_t)); +} + +static int +nvlist_contains_nvp(nvlist_t *nvl, nvpair_t *nvp) +{ + nvpriv_t *priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv; + i_nvp_t *curr; + + if (nvp == NULL) + return (0); + + for (curr = priv->nvp_list; curr != NULL; curr = curr->nvi_next) + if (&curr->nvi_nvp == nvp) + return (1); + + return (0); +} + +/* + * Make a copy of nvlist + */ +/*ARGSUSED1*/ +int +nvlist_dup(nvlist_t *nvl, nvlist_t **nvlp, int kmflag) +{ +#if defined(_KERNEL) && !defined(_BOOT) + return (nvlist_xdup(nvl, nvlp, + (kmflag == KM_SLEEP ? nv_alloc_sleep : nv_alloc_nosleep))); +#else + return (nvlist_xdup(nvl, nvlp, nv_alloc_nosleep)); +#endif +} + +int +nvlist_xdup(nvlist_t *nvl, nvlist_t **nvlp, nv_alloc_t *nva) +{ + int err; + nvlist_t *ret; + + if (nvl == NULL || nvlp == NULL) + return (EINVAL); + + if ((err = nvlist_xalloc(&ret, nvl->nvl_nvflag, nva)) != 0) + return (err); + + if ((err = nvlist_copy_pairs(nvl, ret)) != 0) + nvlist_free(ret); + else + *nvlp = ret; + + return (err); +} + +/* + * Remove all with matching name + */ +int +nvlist_remove_all(nvlist_t *nvl, const char *name) +{ + nvpriv_t *priv; + i_nvp_t *curr; + int error = ENOENT; + + if (nvl == NULL || name == NULL || + (priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv) == NULL) + return (EINVAL); + + curr = priv->nvp_list; + while (curr != NULL) { + nvpair_t *nvp = &curr->nvi_nvp; + + curr = curr->nvi_next; + if (strcmp(name, NVP_NAME(nvp)) != 0) + continue; + + nvp_buf_unlink(nvl, nvp); + nvpair_free(nvp); + nvp_buf_free(nvl, nvp); + + error = 0; + } + + return (error); +} + +/* + * Remove first one with matching name and type + */ +int +nvlist_remove(nvlist_t *nvl, const char *name, data_type_t type) +{ + nvpriv_t *priv; + i_nvp_t *curr; + + if (nvl == NULL || name == NULL || + (priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv) == NULL) + return (EINVAL); + + curr = priv->nvp_list; + while (curr != NULL) { + nvpair_t *nvp = &curr->nvi_nvp; + + if (strcmp(name, NVP_NAME(nvp)) == 0 && NVP_TYPE(nvp) == type) { + nvp_buf_unlink(nvl, nvp); + nvpair_free(nvp); + nvp_buf_free(nvl, nvp); + + return (0); + } + curr = curr->nvi_next; + } + + return (ENOENT); +} + +int +nvlist_remove_nvpair(nvlist_t *nvl, nvpair_t *nvp) +{ + if (nvl == NULL || nvp == NULL) + return (EINVAL); + + nvp_buf_unlink(nvl, nvp); + nvpair_free(nvp); + nvp_buf_free(nvl, nvp); + return (0); +} + +/* + * This function calculates the size of an nvpair value. + * + * The data argument controls the behavior in case of the data types + * DATA_TYPE_STRING and + * DATA_TYPE_STRING_ARRAY + * Is data == NULL then the size of the string(s) is excluded. + */ +static int +i_get_value_size(data_type_t type, const void *data, uint_t nelem) +{ + uint64_t value_sz; + + if (i_validate_type_nelem(type, nelem) != 0) + return (-1); + + /* Calculate required size for holding value */ + switch (type) { + case DATA_TYPE_BOOLEAN: + value_sz = 0; + break; + case DATA_TYPE_BOOLEAN_VALUE: + value_sz = sizeof (boolean_t); + break; + case DATA_TYPE_BYTE: + value_sz = sizeof (uchar_t); + break; + case DATA_TYPE_INT8: + value_sz = sizeof (int8_t); + break; + case DATA_TYPE_UINT8: + value_sz = sizeof (uint8_t); + break; + case DATA_TYPE_INT16: + value_sz = sizeof (int16_t); + break; + case DATA_TYPE_UINT16: + value_sz = sizeof (uint16_t); + break; + case DATA_TYPE_INT32: + value_sz = sizeof (int32_t); + break; + case DATA_TYPE_UINT32: + value_sz = sizeof (uint32_t); + break; + case DATA_TYPE_INT64: + value_sz = sizeof (int64_t); + break; + case DATA_TYPE_UINT64: + value_sz = sizeof (uint64_t); + break; +#if !defined(_KERNEL) + case DATA_TYPE_DOUBLE: + value_sz = sizeof (double); + break; +#endif + case DATA_TYPE_STRING: + if (data == NULL) + value_sz = 0; + else + value_sz = strlen(data) + 1; + break; + case DATA_TYPE_BOOLEAN_ARRAY: + value_sz = (uint64_t)nelem * sizeof (boolean_t); + break; + case DATA_TYPE_BYTE_ARRAY: + value_sz = (uint64_t)nelem * sizeof (uchar_t); + break; + case DATA_TYPE_INT8_ARRAY: + value_sz = (uint64_t)nelem * sizeof (int8_t); + break; + case DATA_TYPE_UINT8_ARRAY: + value_sz = (uint64_t)nelem * sizeof (uint8_t); + break; + case DATA_TYPE_INT16_ARRAY: + value_sz = (uint64_t)nelem * sizeof (int16_t); + break; + case DATA_TYPE_UINT16_ARRAY: + value_sz = (uint64_t)nelem * sizeof (uint16_t); + break; + case DATA_TYPE_INT32_ARRAY: + value_sz = (uint64_t)nelem * sizeof (int32_t); + break; + case DATA_TYPE_UINT32_ARRAY: + value_sz = (uint64_t)nelem * sizeof (uint32_t); + break; + case DATA_TYPE_INT64_ARRAY: + value_sz = (uint64_t)nelem * sizeof (int64_t); + break; + case DATA_TYPE_UINT64_ARRAY: + value_sz = (uint64_t)nelem * sizeof (uint64_t); + break; + case DATA_TYPE_STRING_ARRAY: + value_sz = (uint64_t)nelem * sizeof (uint64_t); + + if (data != NULL) { + char *const *strs = data; + uint_t i; + + /* no alignment requirement for strings */ + for (i = 0; i < nelem; i++) { + if (strs[i] == NULL) + return (-1); + value_sz += strlen(strs[i]) + 1; + } + } + break; + case DATA_TYPE_HRTIME: + value_sz = sizeof (hrtime_t); + break; + case DATA_TYPE_NVLIST: + value_sz = NV_ALIGN(sizeof (nvlist_t)); + break; + case DATA_TYPE_NVLIST_ARRAY: + value_sz = (uint64_t)nelem * sizeof (uint64_t) + + (uint64_t)nelem * NV_ALIGN(sizeof (nvlist_t)); + break; + default: + return (-1); + } + + return (value_sz > INT32_MAX ? -1 : (int)value_sz); +} + +static int +nvlist_copy_embedded(nvlist_t *nvl, nvlist_t *onvl, nvlist_t *emb_nvl) +{ + nvpriv_t *priv; + int err; + + if ((priv = nv_priv_alloc_embedded((nvpriv_t *)(uintptr_t) + nvl->nvl_priv)) == NULL) + return (ENOMEM); + + nvlist_init(emb_nvl, onvl->nvl_nvflag, priv); + + if ((err = nvlist_copy_pairs(onvl, emb_nvl)) != 0) { + nvlist_free(emb_nvl); + emb_nvl->nvl_priv = 0; + } + + return (err); +} + +/* + * nvlist_add_common - Add new pair to nvlist + */ +static int +nvlist_add_common(nvlist_t *nvl, const char *name, + data_type_t type, uint_t nelem, const void *data) +{ + nvpair_t *nvp; + uint_t i; + + int nvp_sz, name_sz, value_sz; + int err = 0; + + if (name == NULL || nvl == NULL || nvl->nvl_priv == 0) + return (EINVAL); + + if (nelem != 0 && data == NULL) + return (EINVAL); + + /* + * Verify type and nelem and get the value size. + * In case of data types DATA_TYPE_STRING and DATA_TYPE_STRING_ARRAY + * is the size of the string(s) included. + */ + if ((value_sz = i_get_value_size(type, data, nelem)) < 0) + return (EINVAL); + + if (i_validate_nvpair_value(type, nelem, data) != 0) + return (EINVAL); + + /* + * If we're adding an nvlist or nvlist array, ensure that we are not + * adding the input nvlist to itself, which would cause recursion, + * and ensure that no NULL nvlist pointers are present. + */ + switch (type) { + case DATA_TYPE_NVLIST: + if (data == nvl || data == NULL) + return (EINVAL); + break; + case DATA_TYPE_NVLIST_ARRAY: { + nvlist_t **onvlp = (nvlist_t **)data; + for (i = 0; i < nelem; i++) { + if (onvlp[i] == nvl || onvlp[i] == NULL) + return (EINVAL); + } + break; + } + default: + break; + } + + /* calculate sizes of the nvpair elements and the nvpair itself */ + name_sz = strlen(name) + 1; + + nvp_sz = NVP_SIZE_CALC(name_sz, value_sz); + + if ((nvp = nvp_buf_alloc(nvl, nvp_sz)) == NULL) + return (ENOMEM); + + ASSERT(nvp->nvp_size == nvp_sz); + nvp->nvp_name_sz = name_sz; + nvp->nvp_value_elem = nelem; + nvp->nvp_type = type; + bcopy(name, NVP_NAME(nvp), name_sz); + + switch (type) { + case DATA_TYPE_BOOLEAN: + break; + case DATA_TYPE_STRING_ARRAY: { + char *const *strs = data; + char *buf = NVP_VALUE(nvp); + char **cstrs = (void *)buf; + + /* skip pre-allocated space for pointer array */ + buf += nelem * sizeof (uint64_t); + for (i = 0; i < nelem; i++) { + int slen = strlen(strs[i]) + 1; + bcopy(strs[i], buf, slen); + cstrs[i] = buf; + buf += slen; + } + break; + } + case DATA_TYPE_NVLIST: { + nvlist_t *nnvl = EMBEDDED_NVL(nvp); + nvlist_t *onvl = (nvlist_t *)data; + + if ((err = nvlist_copy_embedded(nvl, onvl, nnvl)) != 0) { + nvp_buf_free(nvl, nvp); + return (err); + } + break; + } + case DATA_TYPE_NVLIST_ARRAY: { + nvlist_t **onvlp = (nvlist_t **)data; + nvlist_t **nvlp = EMBEDDED_NVL_ARRAY(nvp); + nvlist_t *embedded = (nvlist_t *) + ((uintptr_t)nvlp + nelem * sizeof (uint64_t)); + + for (i = 0; i < nelem; i++) { + if ((err = nvlist_copy_embedded(nvl, + onvlp[i], embedded)) != 0) { + /* + * Free any successfully created lists + */ + nvpair_free(nvp); + nvp_buf_free(nvl, nvp); + return (err); + } + + nvlp[i] = embedded++; + } + break; + } + default: + bcopy(data, NVP_VALUE(nvp), value_sz); + } + + /* if unique name, remove before add */ + if (nvl->nvl_nvflag & NV_UNIQUE_NAME) + (void) nvlist_remove_all(nvl, name); + else if (nvl->nvl_nvflag & NV_UNIQUE_NAME_TYPE) + (void) nvlist_remove(nvl, name, type); + + nvp_buf_link(nvl, nvp); + + return (0); +} + +int +nvlist_add_boolean(nvlist_t *nvl, const char *name) +{ + return (nvlist_add_common(nvl, name, DATA_TYPE_BOOLEAN, 0, NULL)); +} + +int +nvlist_add_boolean_value(nvlist_t *nvl, const char *name, boolean_t val) +{ + return (nvlist_add_common(nvl, name, DATA_TYPE_BOOLEAN_VALUE, 1, &val)); +} + +int +nvlist_add_byte(nvlist_t *nvl, const char *name, uchar_t val) +{ + return (nvlist_add_common(nvl, name, DATA_TYPE_BYTE, 1, &val)); +} + +int +nvlist_add_int8(nvlist_t *nvl, const char *name, int8_t val) +{ + return (nvlist_add_common(nvl, name, DATA_TYPE_INT8, 1, &val)); +} + +int +nvlist_add_uint8(nvlist_t *nvl, const char *name, uint8_t val) +{ + return (nvlist_add_common(nvl, name, DATA_TYPE_UINT8, 1, &val)); +} + +int +nvlist_add_int16(nvlist_t *nvl, const char *name, int16_t val) +{ + return (nvlist_add_common(nvl, name, DATA_TYPE_INT16, 1, &val)); +} + +int +nvlist_add_uint16(nvlist_t *nvl, const char *name, uint16_t val) +{ + return (nvlist_add_common(nvl, name, DATA_TYPE_UINT16, 1, &val)); +} + +int +nvlist_add_int32(nvlist_t *nvl, const char *name, int32_t val) +{ + return (nvlist_add_common(nvl, name, DATA_TYPE_INT32, 1, &val)); +} + +int +nvlist_add_uint32(nvlist_t *nvl, const char *name, uint32_t val) +{ + return (nvlist_add_common(nvl, name, DATA_TYPE_UINT32, 1, &val)); +} + +int +nvlist_add_int64(nvlist_t *nvl, const char *name, int64_t val) +{ + return (nvlist_add_common(nvl, name, DATA_TYPE_INT64, 1, &val)); +} + +int +nvlist_add_uint64(nvlist_t *nvl, const char *name, uint64_t val) +{ + return (nvlist_add_common(nvl, name, DATA_TYPE_UINT64, 1, &val)); +} + +#if !defined(_KERNEL) +int +nvlist_add_double(nvlist_t *nvl, const char *name, double val) +{ + return (nvlist_add_common(nvl, name, DATA_TYPE_DOUBLE, 1, &val)); +} +#endif + +int +nvlist_add_string(nvlist_t *nvl, const char *name, const char *val) +{ + return (nvlist_add_common(nvl, name, DATA_TYPE_STRING, 1, (void *)val)); +} + +int +nvlist_add_boolean_array(nvlist_t *nvl, const char *name, + boolean_t *a, uint_t n) +{ + return (nvlist_add_common(nvl, name, DATA_TYPE_BOOLEAN_ARRAY, n, a)); +} + +int +nvlist_add_byte_array(nvlist_t *nvl, const char *name, uchar_t *a, uint_t n) +{ + return (nvlist_add_common(nvl, name, DATA_TYPE_BYTE_ARRAY, n, a)); +} + +int +nvlist_add_int8_array(nvlist_t *nvl, const char *name, int8_t *a, uint_t n) +{ + return (nvlist_add_common(nvl, name, DATA_TYPE_INT8_ARRAY, n, a)); +} + +int +nvlist_add_uint8_array(nvlist_t *nvl, const char *name, uint8_t *a, uint_t n) +{ + return (nvlist_add_common(nvl, name, DATA_TYPE_UINT8_ARRAY, n, a)); +} + +int +nvlist_add_int16_array(nvlist_t *nvl, const char *name, int16_t *a, uint_t n) +{ + return (nvlist_add_common(nvl, name, DATA_TYPE_INT16_ARRAY, n, a)); +} + +int +nvlist_add_uint16_array(nvlist_t *nvl, const char *name, uint16_t *a, uint_t n) +{ + return (nvlist_add_common(nvl, name, DATA_TYPE_UINT16_ARRAY, n, a)); +} + +int +nvlist_add_int32_array(nvlist_t *nvl, const char *name, int32_t *a, uint_t n) +{ + return (nvlist_add_common(nvl, name, DATA_TYPE_INT32_ARRAY, n, a)); +} + +int +nvlist_add_uint32_array(nvlist_t *nvl, const char *name, uint32_t *a, uint_t n) +{ + return (nvlist_add_common(nvl, name, DATA_TYPE_UINT32_ARRAY, n, a)); +} + +int +nvlist_add_int64_array(nvlist_t *nvl, const char *name, int64_t *a, uint_t n) +{ + return (nvlist_add_common(nvl, name, DATA_TYPE_INT64_ARRAY, n, a)); +} + +int +nvlist_add_uint64_array(nvlist_t *nvl, const char *name, uint64_t *a, uint_t n) +{ + return (nvlist_add_common(nvl, name, DATA_TYPE_UINT64_ARRAY, n, a)); +} + +int +nvlist_add_string_array(nvlist_t *nvl, const char *name, + char *const *a, uint_t n) +{ + return (nvlist_add_common(nvl, name, DATA_TYPE_STRING_ARRAY, n, a)); +} + +int +nvlist_add_hrtime(nvlist_t *nvl, const char *name, hrtime_t val) +{ + return (nvlist_add_common(nvl, name, DATA_TYPE_HRTIME, 1, &val)); +} + +int +nvlist_add_nvlist(nvlist_t *nvl, const char *name, nvlist_t *val) +{ + return (nvlist_add_common(nvl, name, DATA_TYPE_NVLIST, 1, val)); +} + +int +nvlist_add_nvlist_array(nvlist_t *nvl, const char *name, nvlist_t **a, uint_t n) +{ + return (nvlist_add_common(nvl, name, DATA_TYPE_NVLIST_ARRAY, n, a)); +} + +/* reading name-value pairs */ +nvpair_t * +nvlist_next_nvpair(nvlist_t *nvl, nvpair_t *nvp) +{ + nvpriv_t *priv; + i_nvp_t *curr; + + if (nvl == NULL || + (priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv) == NULL) + return (NULL); + + curr = NVPAIR2I_NVP(nvp); + + /* + * Ensure that nvp is a valid nvpair on this nvlist. + * NB: nvp_curr is used only as a hint so that we don't always + * have to walk the list to determine if nvp is still on the list. + */ + if (nvp == NULL) + curr = priv->nvp_list; + else if (priv->nvp_curr == curr || nvlist_contains_nvp(nvl, nvp)) + curr = curr->nvi_next; + else + curr = NULL; + + priv->nvp_curr = curr; + + return (curr != NULL ? &curr->nvi_nvp : NULL); +} + +nvpair_t * +nvlist_prev_nvpair(nvlist_t *nvl, nvpair_t *nvp) +{ + nvpriv_t *priv; + i_nvp_t *curr; + + if (nvl == NULL || + (priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv) == NULL) + return (NULL); + + curr = NVPAIR2I_NVP(nvp); + + if (nvp == NULL) + curr = priv->nvp_last; + else if (priv->nvp_curr == curr || nvlist_contains_nvp(nvl, nvp)) + curr = curr->nvi_prev; + else + curr = NULL; + + priv->nvp_curr = curr; + + return (curr != NULL ? &curr->nvi_nvp : NULL); +} + +boolean_t +nvlist_empty(nvlist_t *nvl) +{ + nvpriv_t *priv; + + if (nvl == NULL || + (priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv) == NULL) + return (B_TRUE); + + return (priv->nvp_list == NULL); +} + +char * +nvpair_name(nvpair_t *nvp) +{ + return (NVP_NAME(nvp)); +} + +data_type_t +nvpair_type(nvpair_t *nvp) +{ + return (NVP_TYPE(nvp)); +} + +int +nvpair_type_is_array(nvpair_t *nvp) +{ + data_type_t type = NVP_TYPE(nvp); + + if ((type == DATA_TYPE_BYTE_ARRAY) || + (type == DATA_TYPE_INT8_ARRAY) || + (type == DATA_TYPE_UINT8_ARRAY) || + (type == DATA_TYPE_INT16_ARRAY) || + (type == DATA_TYPE_UINT16_ARRAY) || + (type == DATA_TYPE_INT32_ARRAY) || + (type == DATA_TYPE_UINT32_ARRAY) || + (type == DATA_TYPE_INT64_ARRAY) || + (type == DATA_TYPE_UINT64_ARRAY) || + (type == DATA_TYPE_BOOLEAN_ARRAY) || + (type == DATA_TYPE_STRING_ARRAY) || + (type == DATA_TYPE_NVLIST_ARRAY)) + return (1); + return (0); + +} + +static int +nvpair_value_common(nvpair_t *nvp, data_type_t type, uint_t *nelem, void *data) +{ + if (nvp == NULL || nvpair_type(nvp) != type) + return (EINVAL); + + /* + * For non-array types, we copy the data. + * For array types (including string), we set a pointer. + */ + switch (type) { + case DATA_TYPE_BOOLEAN: + if (nelem != NULL) + *nelem = 0; + break; + + case DATA_TYPE_BOOLEAN_VALUE: + case DATA_TYPE_BYTE: + case DATA_TYPE_INT8: + case DATA_TYPE_UINT8: + case DATA_TYPE_INT16: + case DATA_TYPE_UINT16: + case DATA_TYPE_INT32: + case DATA_TYPE_UINT32: + case DATA_TYPE_INT64: + case DATA_TYPE_UINT64: + case DATA_TYPE_HRTIME: +#if !defined(_KERNEL) + case DATA_TYPE_DOUBLE: +#endif + if (data == NULL) + return (EINVAL); + bcopy(NVP_VALUE(nvp), data, + (size_t)i_get_value_size(type, NULL, 1)); + if (nelem != NULL) + *nelem = 1; + break; + + case DATA_TYPE_NVLIST: + case DATA_TYPE_STRING: + if (data == NULL) + return (EINVAL); + *(void **)data = (void *)NVP_VALUE(nvp); + if (nelem != NULL) + *nelem = 1; + break; + + case DATA_TYPE_BOOLEAN_ARRAY: + case DATA_TYPE_BYTE_ARRAY: + case DATA_TYPE_INT8_ARRAY: + case DATA_TYPE_UINT8_ARRAY: + case DATA_TYPE_INT16_ARRAY: + case DATA_TYPE_UINT16_ARRAY: + case DATA_TYPE_INT32_ARRAY: + case DATA_TYPE_UINT32_ARRAY: + case DATA_TYPE_INT64_ARRAY: + case DATA_TYPE_UINT64_ARRAY: + case DATA_TYPE_STRING_ARRAY: + case DATA_TYPE_NVLIST_ARRAY: + if (nelem == NULL || data == NULL) + return (EINVAL); + if ((*nelem = NVP_NELEM(nvp)) != 0) + *(void **)data = (void *)NVP_VALUE(nvp); + else + *(void **)data = NULL; + break; + + default: + return (ENOTSUP); + } + + return (0); +} + +static int +nvlist_lookup_common(nvlist_t *nvl, const char *name, data_type_t type, + uint_t *nelem, void *data) +{ + nvpriv_t *priv; + nvpair_t *nvp; + i_nvp_t *curr; + + if (name == NULL || nvl == NULL || + (priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv) == NULL) + return (EINVAL); + + if (!(nvl->nvl_nvflag & (NV_UNIQUE_NAME | NV_UNIQUE_NAME_TYPE))) + return (ENOTSUP); + + for (curr = priv->nvp_list; curr != NULL; curr = curr->nvi_next) { + nvp = &curr->nvi_nvp; + + if (strcmp(name, NVP_NAME(nvp)) == 0 && NVP_TYPE(nvp) == type) + return (nvpair_value_common(nvp, type, nelem, data)); + } + + return (ENOENT); +} + +int +nvlist_lookup_boolean(nvlist_t *nvl, const char *name) +{ + return (nvlist_lookup_common(nvl, name, DATA_TYPE_BOOLEAN, NULL, NULL)); +} + +int +nvlist_lookup_boolean_value(nvlist_t *nvl, const char *name, boolean_t *val) +{ + return (nvlist_lookup_common(nvl, name, + DATA_TYPE_BOOLEAN_VALUE, NULL, val)); +} + +int +nvlist_lookup_byte(nvlist_t *nvl, const char *name, uchar_t *val) +{ + return (nvlist_lookup_common(nvl, name, DATA_TYPE_BYTE, NULL, val)); +} + +int +nvlist_lookup_int8(nvlist_t *nvl, const char *name, int8_t *val) +{ + return (nvlist_lookup_common(nvl, name, DATA_TYPE_INT8, NULL, val)); +} + +int +nvlist_lookup_uint8(nvlist_t *nvl, const char *name, uint8_t *val) +{ + return (nvlist_lookup_common(nvl, name, DATA_TYPE_UINT8, NULL, val)); +} + +int +nvlist_lookup_int16(nvlist_t *nvl, const char *name, int16_t *val) +{ + return (nvlist_lookup_common(nvl, name, DATA_TYPE_INT16, NULL, val)); +} + +int +nvlist_lookup_uint16(nvlist_t *nvl, const char *name, uint16_t *val) +{ + return (nvlist_lookup_common(nvl, name, DATA_TYPE_UINT16, NULL, val)); +} + +int +nvlist_lookup_int32(nvlist_t *nvl, const char *name, int32_t *val) +{ + return (nvlist_lookup_common(nvl, name, DATA_TYPE_INT32, NULL, val)); +} + +int +nvlist_lookup_uint32(nvlist_t *nvl, const char *name, uint32_t *val) +{ + return (nvlist_lookup_common(nvl, name, DATA_TYPE_UINT32, NULL, val)); +} + +int +nvlist_lookup_int64(nvlist_t *nvl, const char *name, int64_t *val) +{ + return (nvlist_lookup_common(nvl, name, DATA_TYPE_INT64, NULL, val)); +} + +int +nvlist_lookup_uint64(nvlist_t *nvl, const char *name, uint64_t *val) +{ + return (nvlist_lookup_common(nvl, name, DATA_TYPE_UINT64, NULL, val)); +} + +#if !defined(_KERNEL) +int +nvlist_lookup_double(nvlist_t *nvl, const char *name, double *val) +{ + return (nvlist_lookup_common(nvl, name, DATA_TYPE_DOUBLE, NULL, val)); +} +#endif + +int +nvlist_lookup_string(nvlist_t *nvl, const char *name, char **val) +{ + return (nvlist_lookup_common(nvl, name, DATA_TYPE_STRING, NULL, val)); +} + +int +nvlist_lookup_nvlist(nvlist_t *nvl, const char *name, nvlist_t **val) +{ + return (nvlist_lookup_common(nvl, name, DATA_TYPE_NVLIST, NULL, val)); +} + +int +nvlist_lookup_boolean_array(nvlist_t *nvl, const char *name, + boolean_t **a, uint_t *n) +{ + return (nvlist_lookup_common(nvl, name, + DATA_TYPE_BOOLEAN_ARRAY, n, a)); +} + +int +nvlist_lookup_byte_array(nvlist_t *nvl, const char *name, + uchar_t **a, uint_t *n) +{ + return (nvlist_lookup_common(nvl, name, DATA_TYPE_BYTE_ARRAY, n, a)); +} + +int +nvlist_lookup_int8_array(nvlist_t *nvl, const char *name, int8_t **a, uint_t *n) +{ + return (nvlist_lookup_common(nvl, name, DATA_TYPE_INT8_ARRAY, n, a)); +} + +int +nvlist_lookup_uint8_array(nvlist_t *nvl, const char *name, + uint8_t **a, uint_t *n) +{ + return (nvlist_lookup_common(nvl, name, DATA_TYPE_UINT8_ARRAY, n, a)); +} + +int +nvlist_lookup_int16_array(nvlist_t *nvl, const char *name, + int16_t **a, uint_t *n) +{ + return (nvlist_lookup_common(nvl, name, DATA_TYPE_INT16_ARRAY, n, a)); +} + +int +nvlist_lookup_uint16_array(nvlist_t *nvl, const char *name, + uint16_t **a, uint_t *n) +{ + return (nvlist_lookup_common(nvl, name, DATA_TYPE_UINT16_ARRAY, n, a)); +} + +int +nvlist_lookup_int32_array(nvlist_t *nvl, const char *name, + int32_t **a, uint_t *n) +{ + return (nvlist_lookup_common(nvl, name, DATA_TYPE_INT32_ARRAY, n, a)); +} + +int +nvlist_lookup_uint32_array(nvlist_t *nvl, const char *name, + uint32_t **a, uint_t *n) +{ + return (nvlist_lookup_common(nvl, name, DATA_TYPE_UINT32_ARRAY, n, a)); +} + +int +nvlist_lookup_int64_array(nvlist_t *nvl, const char *name, + int64_t **a, uint_t *n) +{ + return (nvlist_lookup_common(nvl, name, DATA_TYPE_INT64_ARRAY, n, a)); +} + +int +nvlist_lookup_uint64_array(nvlist_t *nvl, const char *name, + uint64_t **a, uint_t *n) +{ + return (nvlist_lookup_common(nvl, name, DATA_TYPE_UINT64_ARRAY, n, a)); +} + +int +nvlist_lookup_string_array(nvlist_t *nvl, const char *name, + char ***a, uint_t *n) +{ + return (nvlist_lookup_common(nvl, name, DATA_TYPE_STRING_ARRAY, n, a)); +} + +int +nvlist_lookup_nvlist_array(nvlist_t *nvl, const char *name, + nvlist_t ***a, uint_t *n) +{ + return (nvlist_lookup_common(nvl, name, DATA_TYPE_NVLIST_ARRAY, n, a)); +} + +int +nvlist_lookup_hrtime(nvlist_t *nvl, const char *name, hrtime_t *val) +{ + return (nvlist_lookup_common(nvl, name, DATA_TYPE_HRTIME, NULL, val)); +} + +int +nvlist_lookup_pairs(nvlist_t *nvl, int flag, ...) +{ + va_list ap; + char *name; + int noentok = (flag & NV_FLAG_NOENTOK ? 1 : 0); + int ret = 0; + + va_start(ap, flag); + while (ret == 0 && (name = va_arg(ap, char *)) != NULL) { + data_type_t type; + void *val; + uint_t *nelem; + + switch (type = va_arg(ap, data_type_t)) { + case DATA_TYPE_BOOLEAN: + ret = nvlist_lookup_common(nvl, name, type, NULL, NULL); + break; + + case DATA_TYPE_BOOLEAN_VALUE: + case DATA_TYPE_BYTE: + case DATA_TYPE_INT8: + case DATA_TYPE_UINT8: + case DATA_TYPE_INT16: + case DATA_TYPE_UINT16: + case DATA_TYPE_INT32: + case DATA_TYPE_UINT32: + case DATA_TYPE_INT64: + case DATA_TYPE_UINT64: + case DATA_TYPE_HRTIME: + case DATA_TYPE_STRING: + case DATA_TYPE_NVLIST: +#if !defined(_KERNEL) + case DATA_TYPE_DOUBLE: +#endif + val = va_arg(ap, void *); + ret = nvlist_lookup_common(nvl, name, type, NULL, val); + break; + + case DATA_TYPE_BYTE_ARRAY: + case DATA_TYPE_BOOLEAN_ARRAY: + case DATA_TYPE_INT8_ARRAY: + case DATA_TYPE_UINT8_ARRAY: + case DATA_TYPE_INT16_ARRAY: + case DATA_TYPE_UINT16_ARRAY: + case DATA_TYPE_INT32_ARRAY: + case DATA_TYPE_UINT32_ARRAY: + case DATA_TYPE_INT64_ARRAY: + case DATA_TYPE_UINT64_ARRAY: + case DATA_TYPE_STRING_ARRAY: + case DATA_TYPE_NVLIST_ARRAY: + val = va_arg(ap, void *); + nelem = va_arg(ap, uint_t *); + ret = nvlist_lookup_common(nvl, name, type, nelem, val); + break; + + default: + ret = EINVAL; + } + + if (ret == ENOENT && noentok) + ret = 0; + } + va_end(ap); + + return (ret); +} + +/* + * Find the 'name'ed nvpair in the nvlist 'nvl'. If 'name' found, the function + * returns zero and a pointer to the matching nvpair is returned in '*ret' + * (given 'ret' is non-NULL). If 'sep' is specified then 'name' will penitrate + * multiple levels of embedded nvlists, with 'sep' as the separator. As an + * example, if sep is '.', name might look like: "a" or "a.b" or "a.c[3]" or + * "a.d[3].e[1]". This matches the C syntax for array embed (for convience, + * code also supports "a.d[3]e[1]" syntax). + * + * If 'ip' is non-NULL and the last name component is an array, return the + * value of the "...[index]" array index in *ip. For an array reference that + * is not indexed, *ip will be returned as -1. If there is a syntax error in + * 'name', and 'ep' is non-NULL then *ep will be set to point to the location + * inside the 'name' string where the syntax error was detected. + */ +static int +nvlist_lookup_nvpair_ei_sep(nvlist_t *nvl, const char *name, const char sep, + nvpair_t **ret, int *ip, char **ep) +{ + nvpair_t *nvp; + const char *np; + char *sepp; + char *idxp, *idxep; + nvlist_t **nva; + long idx; + int n; + + if (ip) + *ip = -1; /* not indexed */ + if (ep) + *ep = NULL; + + if ((nvl == NULL) || (name == NULL)) + return (EINVAL); + + /* step through components of name */ + for (np = name; np && *np; np = sepp) { + /* ensure unique names */ + if (!(nvl->nvl_nvflag & NV_UNIQUE_NAME)) + return (ENOTSUP); + + /* skip white space */ + skip_whitespace(np); + if (*np == 0) + break; + + /* set 'sepp' to end of current component 'np' */ + if (sep) + sepp = strchr(np, sep); + else + sepp = NULL; + + /* find start of next "[ index ]..." */ + idxp = strchr(np, '['); + + /* if sepp comes first, set idxp to NULL */ + if (sepp && idxp && (sepp < idxp)) + idxp = NULL; + + /* + * At this point 'idxp' is set if there is an index + * expected for the current component. + */ + if (idxp) { + /* set 'n' to length of current 'np' name component */ + n = idxp++ - np; + + /* keep sepp up to date for *ep use as we advance */ + skip_whitespace(idxp); + sepp = idxp; + + /* determine the index value */ +#if defined(_KERNEL) && !defined(_BOOT) + if (ddi_strtol(idxp, &idxep, 0, &idx)) + goto fail; +#else + idx = strtol(idxp, &idxep, 0); +#endif + if (idxep == idxp) + goto fail; + + /* keep sepp up to date for *ep use as we advance */ + sepp = idxep; + + /* skip white space index value and check for ']' */ + skip_whitespace(sepp); + if (*sepp++ != ']') + goto fail; + + /* for embedded arrays, support C syntax: "a[1].b" */ + skip_whitespace(sepp); + if (sep && (*sepp == sep)) + sepp++; + } else if (sepp) { + n = sepp++ - np; + } else { + n = strlen(np); + } + + /* trim trailing whitespace by reducing length of 'np' */ + if (n == 0) + goto fail; + for (n--; (np[n] == ' ') || (np[n] == '\t'); n--) + ; + n++; + + /* skip whitespace, and set sepp to NULL if complete */ + if (sepp) { + skip_whitespace(sepp); + if (*sepp == 0) + sepp = NULL; + } + + /* + * At this point: + * o 'n' is the length of current 'np' component. + * o 'idxp' is set if there was an index, and value 'idx'. + * o 'sepp' is set to the beginning of the next component, + * and set to NULL if we have no more components. + * + * Search for nvpair with matching component name. + */ + for (nvp = nvlist_next_nvpair(nvl, NULL); nvp != NULL; + nvp = nvlist_next_nvpair(nvl, nvp)) { + + /* continue if no match on name */ + if (strncmp(np, nvpair_name(nvp), n) || + (strlen(nvpair_name(nvp)) != n)) + continue; + + /* if indexed, verify type is array oriented */ + if (idxp && !nvpair_type_is_array(nvp)) + goto fail; + + /* + * Full match found, return nvp and idx if this + * was the last component. + */ + if (sepp == NULL) { + if (ret) + *ret = nvp; + if (ip && idxp) + *ip = (int)idx; /* return index */ + return (0); /* found */ + } + + /* + * More components: current match must be + * of DATA_TYPE_NVLIST or DATA_TYPE_NVLIST_ARRAY + * to support going deeper. + */ + if (nvpair_type(nvp) == DATA_TYPE_NVLIST) { + nvl = EMBEDDED_NVL(nvp); + break; + } else if (nvpair_type(nvp) == DATA_TYPE_NVLIST_ARRAY) { + (void) nvpair_value_nvlist_array(nvp, + &nva, (uint_t *)&n); + if ((n < 0) || (idx >= n)) + goto fail; + nvl = nva[idx]; + break; + } + + /* type does not support more levels */ + goto fail; + } + if (nvp == NULL) + goto fail; /* 'name' not found */ + + /* search for match of next component in embedded 'nvl' list */ + } + +fail: if (ep && sepp) + *ep = sepp; + return (EINVAL); +} + +/* + * Return pointer to nvpair with specified 'name'. + */ +int +nvlist_lookup_nvpair(nvlist_t *nvl, const char *name, nvpair_t **ret) +{ + return (nvlist_lookup_nvpair_ei_sep(nvl, name, 0, ret, NULL, NULL)); +} + +/* + * Determine if named nvpair exists in nvlist (use embedded separator of '.' + * and return array index). See nvlist_lookup_nvpair_ei_sep for more detailed + * description. + */ +int nvlist_lookup_nvpair_embedded_index(nvlist_t *nvl, + const char *name, nvpair_t **ret, int *ip, char **ep) +{ + return (nvlist_lookup_nvpair_ei_sep(nvl, name, '.', ret, ip, ep)); +} + +boolean_t +nvlist_exists(nvlist_t *nvl, const char *name) +{ + nvpriv_t *priv; + nvpair_t *nvp; + i_nvp_t *curr; + + if (name == NULL || nvl == NULL || + (priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv) == NULL) + return (B_FALSE); + + for (curr = priv->nvp_list; curr != NULL; curr = curr->nvi_next) { + nvp = &curr->nvi_nvp; + + if (strcmp(name, NVP_NAME(nvp)) == 0) + return (B_TRUE); + } + + return (B_FALSE); +} + +int +nvpair_value_boolean_value(nvpair_t *nvp, boolean_t *val) +{ + return (nvpair_value_common(nvp, DATA_TYPE_BOOLEAN_VALUE, NULL, val)); +} + +int +nvpair_value_byte(nvpair_t *nvp, uchar_t *val) +{ + return (nvpair_value_common(nvp, DATA_TYPE_BYTE, NULL, val)); +} + +int +nvpair_value_int8(nvpair_t *nvp, int8_t *val) +{ + return (nvpair_value_common(nvp, DATA_TYPE_INT8, NULL, val)); +} + +int +nvpair_value_uint8(nvpair_t *nvp, uint8_t *val) +{ + return (nvpair_value_common(nvp, DATA_TYPE_UINT8, NULL, val)); +} + +int +nvpair_value_int16(nvpair_t *nvp, int16_t *val) +{ + return (nvpair_value_common(nvp, DATA_TYPE_INT16, NULL, val)); +} + +int +nvpair_value_uint16(nvpair_t *nvp, uint16_t *val) +{ + return (nvpair_value_common(nvp, DATA_TYPE_UINT16, NULL, val)); +} + +int +nvpair_value_int32(nvpair_t *nvp, int32_t *val) +{ + return (nvpair_value_common(nvp, DATA_TYPE_INT32, NULL, val)); +} + +int +nvpair_value_uint32(nvpair_t *nvp, uint32_t *val) +{ + return (nvpair_value_common(nvp, DATA_TYPE_UINT32, NULL, val)); +} + +int +nvpair_value_int64(nvpair_t *nvp, int64_t *val) +{ + return (nvpair_value_common(nvp, DATA_TYPE_INT64, NULL, val)); +} + +int +nvpair_value_uint64(nvpair_t *nvp, uint64_t *val) +{ + return (nvpair_value_common(nvp, DATA_TYPE_UINT64, NULL, val)); +} + +#if !defined(_KERNEL) +int +nvpair_value_double(nvpair_t *nvp, double *val) +{ + return (nvpair_value_common(nvp, DATA_TYPE_DOUBLE, NULL, val)); +} +#endif + +int +nvpair_value_string(nvpair_t *nvp, char **val) +{ + return (nvpair_value_common(nvp, DATA_TYPE_STRING, NULL, val)); +} + +int +nvpair_value_nvlist(nvpair_t *nvp, nvlist_t **val) +{ + return (nvpair_value_common(nvp, DATA_TYPE_NVLIST, NULL, val)); +} + +int +nvpair_value_boolean_array(nvpair_t *nvp, boolean_t **val, uint_t *nelem) +{ + return (nvpair_value_common(nvp, DATA_TYPE_BOOLEAN_ARRAY, nelem, val)); +} + +int +nvpair_value_byte_array(nvpair_t *nvp, uchar_t **val, uint_t *nelem) +{ + return (nvpair_value_common(nvp, DATA_TYPE_BYTE_ARRAY, nelem, val)); +} + +int +nvpair_value_int8_array(nvpair_t *nvp, int8_t **val, uint_t *nelem) +{ + return (nvpair_value_common(nvp, DATA_TYPE_INT8_ARRAY, nelem, val)); +} + +int +nvpair_value_uint8_array(nvpair_t *nvp, uint8_t **val, uint_t *nelem) +{ + return (nvpair_value_common(nvp, DATA_TYPE_UINT8_ARRAY, nelem, val)); +} + +int +nvpair_value_int16_array(nvpair_t *nvp, int16_t **val, uint_t *nelem) +{ + return (nvpair_value_common(nvp, DATA_TYPE_INT16_ARRAY, nelem, val)); +} + +int +nvpair_value_uint16_array(nvpair_t *nvp, uint16_t **val, uint_t *nelem) +{ + return (nvpair_value_common(nvp, DATA_TYPE_UINT16_ARRAY, nelem, val)); +} + +int +nvpair_value_int32_array(nvpair_t *nvp, int32_t **val, uint_t *nelem) +{ + return (nvpair_value_common(nvp, DATA_TYPE_INT32_ARRAY, nelem, val)); +} + +int +nvpair_value_uint32_array(nvpair_t *nvp, uint32_t **val, uint_t *nelem) +{ + return (nvpair_value_common(nvp, DATA_TYPE_UINT32_ARRAY, nelem, val)); +} + +int +nvpair_value_int64_array(nvpair_t *nvp, int64_t **val, uint_t *nelem) +{ + return (nvpair_value_common(nvp, DATA_TYPE_INT64_ARRAY, nelem, val)); +} + +int +nvpair_value_uint64_array(nvpair_t *nvp, uint64_t **val, uint_t *nelem) +{ + return (nvpair_value_common(nvp, DATA_TYPE_UINT64_ARRAY, nelem, val)); +} + +int +nvpair_value_string_array(nvpair_t *nvp, char ***val, uint_t *nelem) +{ + return (nvpair_value_common(nvp, DATA_TYPE_STRING_ARRAY, nelem, val)); +} + +int +nvpair_value_nvlist_array(nvpair_t *nvp, nvlist_t ***val, uint_t *nelem) +{ + return (nvpair_value_common(nvp, DATA_TYPE_NVLIST_ARRAY, nelem, val)); +} + +int +nvpair_value_hrtime(nvpair_t *nvp, hrtime_t *val) +{ + return (nvpair_value_common(nvp, DATA_TYPE_HRTIME, NULL, val)); +} + +/* + * Add specified pair to the list. + */ +int +nvlist_add_nvpair(nvlist_t *nvl, nvpair_t *nvp) +{ + if (nvl == NULL || nvp == NULL) + return (EINVAL); + + return (nvlist_add_common(nvl, NVP_NAME(nvp), NVP_TYPE(nvp), + NVP_NELEM(nvp), NVP_VALUE(nvp))); +} + +/* + * Merge the supplied nvlists and put the result in dst. + * The merged list will contain all names specified in both lists, + * the values are taken from nvl in the case of duplicates. + * Return 0 on success. + */ +/*ARGSUSED*/ +int +nvlist_merge(nvlist_t *dst, nvlist_t *nvl, int flag) +{ + if (nvl == NULL || dst == NULL) + return (EINVAL); + + if (dst != nvl) + return (nvlist_copy_pairs(nvl, dst)); + + return (0); +} + +/* + * Encoding related routines + */ +#define NVS_OP_ENCODE 0 +#define NVS_OP_DECODE 1 +#define NVS_OP_GETSIZE 2 + +typedef struct nvs_ops nvs_ops_t; + +typedef struct { + int nvs_op; + const nvs_ops_t *nvs_ops; + void *nvs_private; + nvpriv_t *nvs_priv; +} nvstream_t; + +/* + * nvs operations are: + * - nvs_nvlist + * encoding / decoding of a nvlist header (nvlist_t) + * calculates the size used for header and end detection + * + * - nvs_nvpair + * responsible for the first part of encoding / decoding of an nvpair + * calculates the decoded size of an nvpair + * + * - nvs_nvp_op + * second part of encoding / decoding of an nvpair + * + * - nvs_nvp_size + * calculates the encoding size of an nvpair + * + * - nvs_nvl_fini + * encodes the end detection mark (zeros). + */ +struct nvs_ops { + int (*nvs_nvlist)(nvstream_t *, nvlist_t *, size_t *); + int (*nvs_nvpair)(nvstream_t *, nvpair_t *, size_t *); + int (*nvs_nvp_op)(nvstream_t *, nvpair_t *); + int (*nvs_nvp_size)(nvstream_t *, nvpair_t *, size_t *); + int (*nvs_nvl_fini)(nvstream_t *); +}; + +typedef struct { + char nvh_encoding; /* nvs encoding method */ + char nvh_endian; /* nvs endian */ + char nvh_reserved1; /* reserved for future use */ + char nvh_reserved2; /* reserved for future use */ +} nvs_header_t; + +static int +nvs_encode_pairs(nvstream_t *nvs, nvlist_t *nvl) +{ + nvpriv_t *priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv; + i_nvp_t *curr; + + /* + * Walk nvpair in list and encode each nvpair + */ + for (curr = priv->nvp_list; curr != NULL; curr = curr->nvi_next) + if (nvs->nvs_ops->nvs_nvpair(nvs, &curr->nvi_nvp, NULL) != 0) + return (EFAULT); + + return (nvs->nvs_ops->nvs_nvl_fini(nvs)); +} + +static int +nvs_decode_pairs(nvstream_t *nvs, nvlist_t *nvl) +{ + nvpair_t *nvp; + size_t nvsize; + int err; + + /* + * Get decoded size of next pair in stream, alloc + * memory for nvpair_t, then decode the nvpair + */ + while ((err = nvs->nvs_ops->nvs_nvpair(nvs, NULL, &nvsize)) == 0) { + if (nvsize == 0) /* end of list */ + break; + + /* make sure len makes sense */ + if (nvsize < NVP_SIZE_CALC(1, 0)) + return (EFAULT); + + if ((nvp = nvp_buf_alloc(nvl, nvsize)) == NULL) + return (ENOMEM); + + if ((err = nvs->nvs_ops->nvs_nvp_op(nvs, nvp)) != 0) { + nvp_buf_free(nvl, nvp); + return (err); + } + + if (i_validate_nvpair(nvp) != 0) { + nvpair_free(nvp); + nvp_buf_free(nvl, nvp); + return (EFAULT); + } + + nvp_buf_link(nvl, nvp); + } + return (err); +} + +static int +nvs_getsize_pairs(nvstream_t *nvs, nvlist_t *nvl, size_t *buflen) +{ + nvpriv_t *priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv; + i_nvp_t *curr; + uint64_t nvsize = *buflen; + size_t size; + + /* + * Get encoded size of nvpairs in nvlist + */ + for (curr = priv->nvp_list; curr != NULL; curr = curr->nvi_next) { + if (nvs->nvs_ops->nvs_nvp_size(nvs, &curr->nvi_nvp, &size) != 0) + return (EINVAL); + + if ((nvsize += size) > INT32_MAX) + return (EINVAL); + } + + *buflen = nvsize; + return (0); +} + +static int +nvs_operation(nvstream_t *nvs, nvlist_t *nvl, size_t *buflen) +{ + int err; + + if (nvl->nvl_priv == 0) + return (EFAULT); + + /* + * Perform the operation, starting with header, then each nvpair + */ + if ((err = nvs->nvs_ops->nvs_nvlist(nvs, nvl, buflen)) != 0) + return (err); + + switch (nvs->nvs_op) { + case NVS_OP_ENCODE: + err = nvs_encode_pairs(nvs, nvl); + break; + + case NVS_OP_DECODE: + err = nvs_decode_pairs(nvs, nvl); + break; + + case NVS_OP_GETSIZE: + err = nvs_getsize_pairs(nvs, nvl, buflen); + break; + + default: + err = EINVAL; + } + + return (err); +} + +static int +nvs_embedded(nvstream_t *nvs, nvlist_t *embedded) +{ + switch (nvs->nvs_op) { + case NVS_OP_ENCODE: + return (nvs_operation(nvs, embedded, NULL)); + + case NVS_OP_DECODE: { + nvpriv_t *priv; + int err; + + if (embedded->nvl_version != NV_VERSION) + return (ENOTSUP); + + if ((priv = nv_priv_alloc_embedded(nvs->nvs_priv)) == NULL) + return (ENOMEM); + + nvlist_init(embedded, embedded->nvl_nvflag, priv); + + if ((err = nvs_operation(nvs, embedded, NULL)) != 0) + nvlist_free(embedded); + return (err); + } + default: + break; + } + + return (EINVAL); +} + +static int +nvs_embedded_nvl_array(nvstream_t *nvs, nvpair_t *nvp, size_t *size) +{ + size_t nelem = NVP_NELEM(nvp); + nvlist_t **nvlp = EMBEDDED_NVL_ARRAY(nvp); + int i; + + switch (nvs->nvs_op) { + case NVS_OP_ENCODE: + for (i = 0; i < nelem; i++) + if (nvs_embedded(nvs, nvlp[i]) != 0) + return (EFAULT); + break; + + case NVS_OP_DECODE: { + size_t len = nelem * sizeof (uint64_t); + nvlist_t *embedded = (nvlist_t *)((uintptr_t)nvlp + len); + + bzero(nvlp, len); /* don't trust packed data */ + for (i = 0; i < nelem; i++) { + if (nvs_embedded(nvs, embedded) != 0) { + nvpair_free(nvp); + return (EFAULT); + } + + nvlp[i] = embedded++; + } + break; + } + case NVS_OP_GETSIZE: { + uint64_t nvsize = 0; + + for (i = 0; i < nelem; i++) { + size_t nvp_sz = 0; + + if (nvs_operation(nvs, nvlp[i], &nvp_sz) != 0) + return (EINVAL); + + if ((nvsize += nvp_sz) > INT32_MAX) + return (EINVAL); + } + + *size = nvsize; + break; + } + default: + return (EINVAL); + } + + return (0); +} + +static int nvs_native(nvstream_t *, nvlist_t *, char *, size_t *); +static int nvs_xdr(nvstream_t *, nvlist_t *, char *, size_t *); + +/* + * Common routine for nvlist operations: + * encode, decode, getsize (encoded size). + */ +static int +nvlist_common(nvlist_t *nvl, char *buf, size_t *buflen, int encoding, + int nvs_op) +{ + int err = 0; + nvstream_t nvs; + int nvl_endian; +#if BYTE_ORDER == _LITTLE_ENDIAN + int host_endian = 1; +#else + int host_endian = 0; +#endif /* _LITTLE_ENDIAN */ + nvs_header_t *nvh = (void *)buf; + + if (buflen == NULL || nvl == NULL || + (nvs.nvs_priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv) == NULL) + return (EINVAL); + + nvs.nvs_op = nvs_op; + + /* + * For NVS_OP_ENCODE and NVS_OP_DECODE make sure an nvlist and + * a buffer is allocated. The first 4 bytes in the buffer are + * used for encoding method and host endian. + */ + switch (nvs_op) { + case NVS_OP_ENCODE: + if (buf == NULL || *buflen < sizeof (nvs_header_t)) + return (EINVAL); + + nvh->nvh_encoding = encoding; + nvh->nvh_endian = nvl_endian = host_endian; + nvh->nvh_reserved1 = 0; + nvh->nvh_reserved2 = 0; + break; + + case NVS_OP_DECODE: + if (buf == NULL || *buflen < sizeof (nvs_header_t)) + return (EINVAL); + + /* get method of encoding from first byte */ + encoding = nvh->nvh_encoding; + nvl_endian = nvh->nvh_endian; + break; + + case NVS_OP_GETSIZE: + nvl_endian = host_endian; + + /* + * add the size for encoding + */ + *buflen = sizeof (nvs_header_t); + break; + + default: + return (ENOTSUP); + } + + /* + * Create an nvstream with proper encoding method + */ + switch (encoding) { + case NV_ENCODE_NATIVE: + /* + * check endianness, in case we are unpacking + * from a file + */ + if (nvl_endian != host_endian) + return (ENOTSUP); + err = nvs_native(&nvs, nvl, buf, buflen); + break; + case NV_ENCODE_XDR: + err = nvs_xdr(&nvs, nvl, buf, buflen); + break; + default: + err = ENOTSUP; + break; + } + + return (err); +} + +int +nvlist_size(nvlist_t *nvl, size_t *size, int encoding) +{ + return (nvlist_common(nvl, NULL, size, encoding, NVS_OP_GETSIZE)); +} + +/* + * Pack nvlist into contiguous memory + */ +/*ARGSUSED1*/ +int +nvlist_pack(nvlist_t *nvl, char **bufp, size_t *buflen, int encoding, + int kmflag) +{ +#if defined(_KERNEL) && !defined(_BOOT) + return (nvlist_xpack(nvl, bufp, buflen, encoding, + (kmflag == KM_SLEEP ? nv_alloc_sleep : nv_alloc_nosleep))); +#else + return (nvlist_xpack(nvl, bufp, buflen, encoding, nv_alloc_nosleep)); +#endif +} + +int +nvlist_xpack(nvlist_t *nvl, char **bufp, size_t *buflen, int encoding, + nv_alloc_t *nva) +{ + nvpriv_t nvpriv; + size_t alloc_size; + char *buf; + int err; + + if (nva == NULL || nvl == NULL || bufp == NULL || buflen == NULL) + return (EINVAL); + + if (*bufp != NULL) + return (nvlist_common(nvl, *bufp, buflen, encoding, + NVS_OP_ENCODE)); + + /* + * Here is a difficult situation: + * 1. The nvlist has fixed allocator properties. + * All other nvlist routines (like nvlist_add_*, ...) use + * these properties. + * 2. When using nvlist_pack() the user can specify his own + * allocator properties (e.g. by using KM_NOSLEEP). + * + * We use the user specified properties (2). A clearer solution + * will be to remove the kmflag from nvlist_pack(), but we will + * not change the interface. + */ + nv_priv_init(&nvpriv, nva, 0); + + if (err = nvlist_size(nvl, &alloc_size, encoding)) + return (err); + + if ((buf = nv_mem_zalloc(&nvpriv, alloc_size)) == NULL) + return (ENOMEM); + + if ((err = nvlist_common(nvl, buf, &alloc_size, encoding, + NVS_OP_ENCODE)) != 0) { + nv_mem_free(&nvpriv, buf, alloc_size); + } else { + *buflen = alloc_size; + *bufp = buf; + } + + return (err); +} + +/* + * Unpack buf into an nvlist_t + */ +/*ARGSUSED1*/ +int +nvlist_unpack(char *buf, size_t buflen, nvlist_t **nvlp, int kmflag) +{ +#if defined(_KERNEL) && !defined(_BOOT) + return (nvlist_xunpack(buf, buflen, nvlp, + (kmflag == KM_SLEEP ? nv_alloc_sleep : nv_alloc_nosleep))); +#else + return (nvlist_xunpack(buf, buflen, nvlp, nv_alloc_nosleep)); +#endif +} + +int +nvlist_xunpack(char *buf, size_t buflen, nvlist_t **nvlp, nv_alloc_t *nva) +{ + nvlist_t *nvl; + int err; + + if (nvlp == NULL) + return (EINVAL); + + if ((err = nvlist_xalloc(&nvl, 0, nva)) != 0) + return (err); + + if ((err = nvlist_common(nvl, buf, &buflen, 0, NVS_OP_DECODE)) != 0) + nvlist_free(nvl); + else + *nvlp = nvl; + + return (err); +} + +/* + * Native encoding functions + */ +typedef struct { + /* + * This structure is used when decoding a packed nvpair in + * the native format. n_base points to a buffer containing the + * packed nvpair. n_end is a pointer to the end of the buffer. + * (n_end actually points to the first byte past the end of the + * buffer.) n_curr is a pointer that lies between n_base and n_end. + * It points to the current data that we are decoding. + * The amount of data left in the buffer is equal to n_end - n_curr. + * n_flag is used to recognize a packed embedded list. + */ + caddr_t n_base; + caddr_t n_end; + caddr_t n_curr; + uint_t n_flag; +} nvs_native_t; + +static int +nvs_native_create(nvstream_t *nvs, nvs_native_t *native, char *buf, + size_t buflen) +{ + switch (nvs->nvs_op) { + case NVS_OP_ENCODE: + case NVS_OP_DECODE: + nvs->nvs_private = native; + native->n_curr = native->n_base = buf; + native->n_end = buf + buflen; + native->n_flag = 0; + return (0); + + case NVS_OP_GETSIZE: + nvs->nvs_private = native; + native->n_curr = native->n_base = native->n_end = NULL; + native->n_flag = 0; + return (0); + default: + return (EINVAL); + } +} + +/*ARGSUSED*/ +static void +nvs_native_destroy(nvstream_t *nvs) +{ +} + +static int +native_cp(nvstream_t *nvs, void *buf, size_t size) +{ + nvs_native_t *native = (nvs_native_t *)nvs->nvs_private; + + if (native->n_curr + size > native->n_end) + return (EFAULT); + + /* + * The bcopy() below eliminates alignment requirement + * on the buffer (stream) and is preferred over direct access. + */ + switch (nvs->nvs_op) { + case NVS_OP_ENCODE: + bcopy(buf, native->n_curr, size); + break; + case NVS_OP_DECODE: + bcopy(native->n_curr, buf, size); + break; + default: + return (EINVAL); + } + + native->n_curr += size; + return (0); +} + +/* + * operate on nvlist_t header + */ +static int +nvs_native_nvlist(nvstream_t *nvs, nvlist_t *nvl, size_t *size) +{ + nvs_native_t *native = nvs->nvs_private; + + switch (nvs->nvs_op) { + case NVS_OP_ENCODE: + case NVS_OP_DECODE: + if (native->n_flag) + return (0); /* packed embedded list */ + + native->n_flag = 1; + + /* copy version and nvflag of the nvlist_t */ + if (native_cp(nvs, &nvl->nvl_version, sizeof (int32_t)) != 0 || + native_cp(nvs, &nvl->nvl_nvflag, sizeof (int32_t)) != 0) + return (EFAULT); + + return (0); + + case NVS_OP_GETSIZE: + /* + * if calculate for packed embedded list + * 4 for end of the embedded list + * else + * 2 * sizeof (int32_t) for nvl_version and nvl_nvflag + * and 4 for end of the entire list + */ + if (native->n_flag) { + *size += 4; + } else { + native->n_flag = 1; + *size += 2 * sizeof (int32_t) + 4; + } + + return (0); + + default: + return (EINVAL); + } +} + +static int +nvs_native_nvl_fini(nvstream_t *nvs) +{ + if (nvs->nvs_op == NVS_OP_ENCODE) { + nvs_native_t *native = (nvs_native_t *)nvs->nvs_private; + /* + * Add 4 zero bytes at end of nvlist. They are used + * for end detection by the decode routine. + */ + if (native->n_curr + sizeof (int) > native->n_end) + return (EFAULT); + + bzero(native->n_curr, sizeof (int)); + native->n_curr += sizeof (int); + } + + return (0); +} + +static int +nvpair_native_embedded(nvstream_t *nvs, nvpair_t *nvp) +{ + if (nvs->nvs_op == NVS_OP_ENCODE) { + nvs_native_t *native = (nvs_native_t *)nvs->nvs_private; + char *packed = (void *) + (native->n_curr - nvp->nvp_size + NVP_VALOFF(nvp)); + /* + * Null out the pointer that is meaningless in the packed + * structure. The address may not be aligned, so we have + * to use bzero. + */ + bzero(packed + offsetof(nvlist_t, nvl_priv), + sizeof(((nvlist_t *)NULL)->nvl_priv)); + } + + return (nvs_embedded(nvs, EMBEDDED_NVL(nvp))); +} + +static int +nvpair_native_embedded_array(nvstream_t *nvs, nvpair_t *nvp) +{ + if (nvs->nvs_op == NVS_OP_ENCODE) { + nvs_native_t *native = (nvs_native_t *)nvs->nvs_private; + char *value = native->n_curr - nvp->nvp_size + NVP_VALOFF(nvp); + size_t len = NVP_NELEM(nvp) * sizeof (uint64_t); + int i; + /* + * Null out pointers that are meaningless in the packed + * structure. The addresses may not be aligned, so we have + * to use bzero. + */ + bzero(value, len); + + value += len; + for (i = 0; i < NVP_NELEM(nvp); i++) { + /* + * Null out the pointer that is meaningless in the + * packed structure. The address may not be aligned, + * so we have to use bzero. + */ + bzero(value + offsetof(nvlist_t, nvl_priv), + sizeof(((nvlist_t *)NULL)->nvl_priv)); + value += sizeof(nvlist_t); + } + } + + return (nvs_embedded_nvl_array(nvs, nvp, NULL)); +} + +static void +nvpair_native_string_array(nvstream_t *nvs, nvpair_t *nvp) +{ + switch (nvs->nvs_op) { + case NVS_OP_ENCODE: { + nvs_native_t *native = (nvs_native_t *)nvs->nvs_private; + uint64_t *strp = (void *) + (native->n_curr - nvp->nvp_size + NVP_VALOFF(nvp)); + /* + * Null out pointers that are meaningless in the packed + * structure. The addresses may not be aligned, so we have + * to use bzero. + */ + bzero(strp, NVP_NELEM(nvp) * sizeof (uint64_t)); + break; + } + case NVS_OP_DECODE: { + char **strp = (void *)NVP_VALUE(nvp); + char *buf = ((char *)strp + NVP_NELEM(nvp) * sizeof (uint64_t)); + int i; + + for (i = 0; i < NVP_NELEM(nvp); i++) { + strp[i] = buf; + buf += strlen(buf) + 1; + } + break; + } + } +} + +static int +nvs_native_nvp_op(nvstream_t *nvs, nvpair_t *nvp) +{ + data_type_t type; + int value_sz; + int ret = 0; + + /* + * We do the initial bcopy of the data before we look at + * the nvpair type, because when we're decoding, we won't + * have the correct values for the pair until we do the bcopy. + */ + switch (nvs->nvs_op) { + case NVS_OP_ENCODE: + case NVS_OP_DECODE: + if (native_cp(nvs, nvp, nvp->nvp_size) != 0) + return (EFAULT); + break; + default: + return (EINVAL); + } + + /* verify nvp_name_sz, check the name string length */ + if (i_validate_nvpair_name(nvp) != 0) + return (EFAULT); + + type = NVP_TYPE(nvp); + + /* + * Verify type and nelem and get the value size. + * In case of data types DATA_TYPE_STRING and DATA_TYPE_STRING_ARRAY + * is the size of the string(s) excluded. + */ + if ((value_sz = i_get_value_size(type, NULL, NVP_NELEM(nvp))) < 0) + return (EFAULT); + + if (NVP_SIZE_CALC(nvp->nvp_name_sz, value_sz) > nvp->nvp_size) + return (EFAULT); + + switch (type) { + case DATA_TYPE_NVLIST: + ret = nvpair_native_embedded(nvs, nvp); + break; + case DATA_TYPE_NVLIST_ARRAY: + ret = nvpair_native_embedded_array(nvs, nvp); + break; + case DATA_TYPE_STRING_ARRAY: + nvpair_native_string_array(nvs, nvp); + break; + default: + break; + } + + return (ret); +} + +static int +nvs_native_nvp_size(nvstream_t *nvs, nvpair_t *nvp, size_t *size) +{ + uint64_t nvp_sz = nvp->nvp_size; + + switch (NVP_TYPE(nvp)) { + case DATA_TYPE_NVLIST: { + size_t nvsize = 0; + + if (nvs_operation(nvs, EMBEDDED_NVL(nvp), &nvsize) != 0) + return (EINVAL); + + nvp_sz += nvsize; + break; + } + case DATA_TYPE_NVLIST_ARRAY: { + size_t nvsize; + + if (nvs_embedded_nvl_array(nvs, nvp, &nvsize) != 0) + return (EINVAL); + + nvp_sz += nvsize; + break; + } + default: + break; + } + + if (nvp_sz > INT32_MAX) + return (EINVAL); + + *size = nvp_sz; + + return (0); +} + +static int +nvs_native_nvpair(nvstream_t *nvs, nvpair_t *nvp, size_t *size) +{ + switch (nvs->nvs_op) { + case NVS_OP_ENCODE: + return (nvs_native_nvp_op(nvs, nvp)); + + case NVS_OP_DECODE: { + nvs_native_t *native = (nvs_native_t *)nvs->nvs_private; + int32_t decode_len; + + /* try to read the size value from the stream */ + if (native->n_curr + sizeof (int32_t) > native->n_end) + return (EFAULT); + bcopy(native->n_curr, &decode_len, sizeof (int32_t)); + + /* sanity check the size value */ + if (decode_len < 0 || + decode_len > native->n_end - native->n_curr) + return (EFAULT); + + *size = decode_len; + + /* + * If at the end of the stream then move the cursor + * forward, otherwise nvpair_native_op() will read + * the entire nvpair at the same cursor position. + */ + if (*size == 0) + native->n_curr += sizeof (int32_t); + break; + } + + default: + return (EINVAL); + } + + return (0); +} + +static const nvs_ops_t nvs_native_ops = { + nvs_native_nvlist, + nvs_native_nvpair, + nvs_native_nvp_op, + nvs_native_nvp_size, + nvs_native_nvl_fini +}; + +static int +nvs_native(nvstream_t *nvs, nvlist_t *nvl, char *buf, size_t *buflen) +{ + nvs_native_t native; + int err; + + nvs->nvs_ops = &nvs_native_ops; + + if ((err = nvs_native_create(nvs, &native, buf + sizeof (nvs_header_t), + *buflen - sizeof (nvs_header_t))) != 0) + return (err); + + err = nvs_operation(nvs, nvl, buflen); + + nvs_native_destroy(nvs); + + return (err); +} + +/* + * XDR encoding functions + * + * An xdr packed nvlist is encoded as: + * + * - encoding methode and host endian (4 bytes) + * - nvl_version (4 bytes) + * - nvl_nvflag (4 bytes) + * + * - encoded nvpairs, the format of one xdr encoded nvpair is: + * - encoded size of the nvpair (4 bytes) + * - decoded size of the nvpair (4 bytes) + * - name string, (4 + sizeof(NV_ALIGN4(string)) + * a string is coded as size (4 bytes) and data + * - data type (4 bytes) + * - number of elements in the nvpair (4 bytes) + * - data + * + * - 2 zero's for end of the entire list (8 bytes) + */ +static int +nvs_xdr_create(nvstream_t *nvs, XDR *xdr, char *buf, size_t buflen) +{ + /* xdr data must be 4 byte aligned */ + if ((ulong_t)buf % 4 != 0) + return (EFAULT); + + switch (nvs->nvs_op) { + case NVS_OP_ENCODE: + xdrmem_create(xdr, buf, (uint_t)buflen, XDR_ENCODE); + nvs->nvs_private = xdr; + return (0); + case NVS_OP_DECODE: + xdrmem_create(xdr, buf, (uint_t)buflen, XDR_DECODE); + nvs->nvs_private = xdr; + return (0); + case NVS_OP_GETSIZE: + nvs->nvs_private = NULL; + return (0); + default: + return (EINVAL); + } +} + +static void +nvs_xdr_destroy(nvstream_t *nvs) +{ + switch (nvs->nvs_op) { + case NVS_OP_ENCODE: + case NVS_OP_DECODE: + xdr_destroy((XDR *)nvs->nvs_private); + break; + default: + break; + } +} + +static int +nvs_xdr_nvlist(nvstream_t *nvs, nvlist_t *nvl, size_t *size) +{ + switch (nvs->nvs_op) { + case NVS_OP_ENCODE: + case NVS_OP_DECODE: { + XDR *xdr = nvs->nvs_private; + + if (!xdr_int(xdr, &nvl->nvl_version) || + !xdr_u_int(xdr, &nvl->nvl_nvflag)) + return (EFAULT); + break; + } + case NVS_OP_GETSIZE: { + /* + * 2 * 4 for nvl_version + nvl_nvflag + * and 8 for end of the entire list + */ + *size += 2 * 4 + 8; + break; + } + default: + return (EINVAL); + } + return (0); +} + +static int +nvs_xdr_nvl_fini(nvstream_t *nvs) +{ + if (nvs->nvs_op == NVS_OP_ENCODE) { + XDR *xdr = nvs->nvs_private; + int zero = 0; + + if (!xdr_int(xdr, &zero) || !xdr_int(xdr, &zero)) + return (EFAULT); + } + + return (0); +} + +/* + * The format of xdr encoded nvpair is: + * encode_size, decode_size, name string, data type, nelem, data + */ +static int +nvs_xdr_nvp_op(nvstream_t *nvs, nvpair_t *nvp) +{ + data_type_t type; + char *buf; + char *buf_end = (char *)nvp + nvp->nvp_size; + int value_sz; + uint_t nelem, buflen; + bool_t ret = FALSE; + XDR *xdr = nvs->nvs_private; + + ASSERT(xdr != NULL && nvp != NULL); + + /* name string */ + if ((buf = NVP_NAME(nvp)) >= buf_end) + return (EFAULT); + buflen = buf_end - buf; + + if (!xdr_string(xdr, &buf, buflen - 1)) + return (EFAULT); + nvp->nvp_name_sz = strlen(buf) + 1; + + /* type and nelem */ + if (!xdr_int(xdr, (int *)&nvp->nvp_type) || + !xdr_int(xdr, &nvp->nvp_value_elem)) + return (EFAULT); + + type = NVP_TYPE(nvp); + nelem = nvp->nvp_value_elem; + + /* + * Verify type and nelem and get the value size. + * In case of data types DATA_TYPE_STRING and DATA_TYPE_STRING_ARRAY + * is the size of the string(s) excluded. + */ + if ((value_sz = i_get_value_size(type, NULL, nelem)) < 0) + return (EFAULT); + + /* if there is no data to extract then return */ + if (nelem == 0) + return (0); + + /* value */ + if ((buf = NVP_VALUE(nvp)) >= buf_end) + return (EFAULT); + buflen = buf_end - buf; + + if (buflen < value_sz) + return (EFAULT); + + switch (type) { + case DATA_TYPE_NVLIST: + if (nvs_embedded(nvs, (void *)buf) == 0) + return (0); + break; + + case DATA_TYPE_NVLIST_ARRAY: + if (nvs_embedded_nvl_array(nvs, nvp, NULL) == 0) + return (0); + break; + + case DATA_TYPE_BOOLEAN: + ret = TRUE; + break; + + case DATA_TYPE_BYTE: + case DATA_TYPE_INT8: + case DATA_TYPE_UINT8: + ret = xdr_char(xdr, buf); + break; + + case DATA_TYPE_INT16: + ret = xdr_short(xdr, (void *)buf); + break; + + case DATA_TYPE_UINT16: + ret = xdr_u_short(xdr, (void *)buf); + break; + + case DATA_TYPE_BOOLEAN_VALUE: + case DATA_TYPE_INT32: + ret = xdr_int(xdr, (void *)buf); + break; + + case DATA_TYPE_UINT32: + ret = xdr_u_int(xdr, (void *)buf); + break; + + case DATA_TYPE_INT64: + ret = xdr_longlong_t(xdr, (void *)buf); + break; + + case DATA_TYPE_UINT64: + ret = xdr_u_longlong_t(xdr, (void *)buf); + break; + + case DATA_TYPE_HRTIME: + /* + * NOTE: must expose the definition of hrtime_t here + */ + ret = xdr_longlong_t(xdr, (void *)buf); + break; +#if !defined(_KERNEL) + case DATA_TYPE_DOUBLE: + ret = xdr_double(xdr, (void *)buf); + break; +#endif + case DATA_TYPE_STRING: + ret = xdr_string(xdr, &buf, buflen - 1); + break; + + case DATA_TYPE_BYTE_ARRAY: + ret = xdr_opaque(xdr, buf, nelem); + break; + + case DATA_TYPE_INT8_ARRAY: + case DATA_TYPE_UINT8_ARRAY: + ret = xdr_array(xdr, &buf, &nelem, buflen, sizeof (int8_t), + (xdrproc_t)xdr_char); + break; + + case DATA_TYPE_INT16_ARRAY: + ret = xdr_array(xdr, &buf, &nelem, buflen / sizeof (int16_t), + sizeof (int16_t), (xdrproc_t)xdr_short); + break; + + case DATA_TYPE_UINT16_ARRAY: + ret = xdr_array(xdr, &buf, &nelem, buflen / sizeof (uint16_t), + sizeof (uint16_t), (xdrproc_t)xdr_u_short); + break; + + case DATA_TYPE_BOOLEAN_ARRAY: + case DATA_TYPE_INT32_ARRAY: + ret = xdr_array(xdr, &buf, &nelem, buflen / sizeof (int32_t), + sizeof (int32_t), (xdrproc_t)xdr_int); + break; + + case DATA_TYPE_UINT32_ARRAY: + ret = xdr_array(xdr, &buf, &nelem, buflen / sizeof (uint32_t), + sizeof (uint32_t), (xdrproc_t)xdr_u_int); + break; + + case DATA_TYPE_INT64_ARRAY: + ret = xdr_array(xdr, &buf, &nelem, buflen / sizeof (int64_t), + sizeof (int64_t), (xdrproc_t)xdr_longlong_t); + break; + + case DATA_TYPE_UINT64_ARRAY: + ret = xdr_array(xdr, &buf, &nelem, buflen / sizeof (uint64_t), + sizeof (uint64_t), (xdrproc_t)xdr_u_longlong_t); + break; + + case DATA_TYPE_STRING_ARRAY: { + size_t len = nelem * sizeof (uint64_t); + char **strp = (void *)buf; + int i; + + if (nvs->nvs_op == NVS_OP_DECODE) + bzero(buf, len); /* don't trust packed data */ + + for (i = 0; i < nelem; i++) { + if (buflen <= len) + return (EFAULT); + + buf += len; + buflen -= len; + + if (xdr_string(xdr, &buf, buflen - 1) != TRUE) + return (EFAULT); + + if (nvs->nvs_op == NVS_OP_DECODE) + strp[i] = buf; + len = strlen(buf) + 1; + } + ret = TRUE; + break; + } + default: + break; + } + + return (ret == TRUE ? 0 : EFAULT); +} + +static int +nvs_xdr_nvp_size(nvstream_t *nvs, nvpair_t *nvp, size_t *size) +{ + data_type_t type = NVP_TYPE(nvp); + /* + * encode_size + decode_size + name string size + data type + nelem + * where name string size = 4 + NV_ALIGN4(strlen(NVP_NAME(nvp))) + */ + uint64_t nvp_sz = 4 + 4 + 4 + NV_ALIGN4(strlen(NVP_NAME(nvp))) + 4 + 4; + + switch (type) { + case DATA_TYPE_BOOLEAN: + break; + + case DATA_TYPE_BOOLEAN_VALUE: + case DATA_TYPE_BYTE: + case DATA_TYPE_INT8: + case DATA_TYPE_UINT8: + case DATA_TYPE_INT16: + case DATA_TYPE_UINT16: + case DATA_TYPE_INT32: + case DATA_TYPE_UINT32: + nvp_sz += 4; /* 4 is the minimum xdr unit */ + break; + + case DATA_TYPE_INT64: + case DATA_TYPE_UINT64: + case DATA_TYPE_HRTIME: +#if !defined(_KERNEL) + case DATA_TYPE_DOUBLE: +#endif + nvp_sz += 8; + break; + + case DATA_TYPE_STRING: + nvp_sz += 4 + NV_ALIGN4(strlen((char *)NVP_VALUE(nvp))); + break; + + case DATA_TYPE_BYTE_ARRAY: + nvp_sz += NV_ALIGN4(NVP_NELEM(nvp)); + break; + + case DATA_TYPE_BOOLEAN_ARRAY: + case DATA_TYPE_INT8_ARRAY: + case DATA_TYPE_UINT8_ARRAY: + case DATA_TYPE_INT16_ARRAY: + case DATA_TYPE_UINT16_ARRAY: + case DATA_TYPE_INT32_ARRAY: + case DATA_TYPE_UINT32_ARRAY: + nvp_sz += 4 + 4 * (uint64_t)NVP_NELEM(nvp); + break; + + case DATA_TYPE_INT64_ARRAY: + case DATA_TYPE_UINT64_ARRAY: + nvp_sz += 4 + 8 * (uint64_t)NVP_NELEM(nvp); + break; + + case DATA_TYPE_STRING_ARRAY: { + int i; + char **strs = (void *)NVP_VALUE(nvp); + + for (i = 0; i < NVP_NELEM(nvp); i++) + nvp_sz += 4 + NV_ALIGN4(strlen(strs[i])); + + break; + } + + case DATA_TYPE_NVLIST: + case DATA_TYPE_NVLIST_ARRAY: { + size_t nvsize = 0; + int old_nvs_op = nvs->nvs_op; + int err; + + nvs->nvs_op = NVS_OP_GETSIZE; + if (type == DATA_TYPE_NVLIST) + err = nvs_operation(nvs, EMBEDDED_NVL(nvp), &nvsize); + else + err = nvs_embedded_nvl_array(nvs, nvp, &nvsize); + nvs->nvs_op = old_nvs_op; + + if (err != 0) + return (EINVAL); + + nvp_sz += nvsize; + break; + } + + default: + return (EINVAL); + } + + if (nvp_sz > INT32_MAX) + return (EINVAL); + + *size = nvp_sz; + + return (0); +} + + +/* + * The NVS_XDR_MAX_LEN macro takes a packed xdr buffer of size x and estimates + * the largest nvpair that could be encoded in the buffer. + * + * See comments above nvpair_xdr_op() for the format of xdr encoding. + * The size of a xdr packed nvpair without any data is 5 words. + * + * Using the size of the data directly as an estimate would be ok + * in all cases except one. If the data type is of DATA_TYPE_STRING_ARRAY + * then the actual nvpair has space for an array of pointers to index + * the strings. These pointers are not encoded into the packed xdr buffer. + * + * If the data is of type DATA_TYPE_STRING_ARRAY and all the strings are + * of length 0, then each string is endcoded in xdr format as a single word. + * Therefore when expanded to an nvpair there will be 2.25 word used for + * each string. (a int64_t allocated for pointer usage, and a single char + * for the null termination.) + * + * This is the calculation performed by the NVS_XDR_MAX_LEN macro. + */ +#define NVS_XDR_HDR_LEN ((size_t)(5 * 4)) +#define NVS_XDR_DATA_LEN(y) (((size_t)(y) <= NVS_XDR_HDR_LEN) ? \ + 0 : ((size_t)(y) - NVS_XDR_HDR_LEN)) +#define NVS_XDR_MAX_LEN(x) (NVP_SIZE_CALC(1, 0) + \ + (NVS_XDR_DATA_LEN(x) * 2) + \ + NV_ALIGN4((NVS_XDR_DATA_LEN(x) / 4))) + +static int +nvs_xdr_nvpair(nvstream_t *nvs, nvpair_t *nvp, size_t *size) +{ + XDR *xdr = nvs->nvs_private; + int32_t encode_len, decode_len; + + switch (nvs->nvs_op) { + case NVS_OP_ENCODE: { + size_t nvsize; + + if (nvs_xdr_nvp_size(nvs, nvp, &nvsize) != 0) + return (EFAULT); + + decode_len = nvp->nvp_size; + encode_len = nvsize; + if (!xdr_int(xdr, &encode_len) || !xdr_int(xdr, &decode_len)) + return (EFAULT); + + return (nvs_xdr_nvp_op(nvs, nvp)); + } + case NVS_OP_DECODE: { + struct xdr_bytesrec bytesrec; + + /* get the encode and decode size */ + if (!xdr_int(xdr, &encode_len) || !xdr_int(xdr, &decode_len)) + return (EFAULT); + *size = decode_len; + + /* are we at the end of the stream? */ + if (*size == 0) + return (0); + + /* sanity check the size parameter */ + if (!xdr_control(xdr, XDR_GET_BYTES_AVAIL, &bytesrec)) + return (EFAULT); + + if (*size > NVS_XDR_MAX_LEN(bytesrec.xc_num_avail)) + return (EFAULT); + break; + } + + default: + return (EINVAL); + } + return (0); +} + +static const struct nvs_ops nvs_xdr_ops = { + nvs_xdr_nvlist, + nvs_xdr_nvpair, + nvs_xdr_nvp_op, + nvs_xdr_nvp_size, + nvs_xdr_nvl_fini +}; + +static int +nvs_xdr(nvstream_t *nvs, nvlist_t *nvl, char *buf, size_t *buflen) +{ + XDR xdr; + int err; + + nvs->nvs_ops = &nvs_xdr_ops; + + if ((err = nvs_xdr_create(nvs, &xdr, buf + sizeof (nvs_header_t), + *buflen - sizeof (nvs_header_t))) != 0) + return (err); + + err = nvs_operation(nvs, nvl, buflen); + + nvs_xdr_destroy(nvs); + + return (err); +} Index: sys/cddl/contrib/opensolaris/common/nvpair/opensolaris_nvpair_alloc_fixed.c =================================================================== --- /dev/null +++ sys/cddl/contrib/opensolaris/common/nvpair/opensolaris_nvpair_alloc_fixed.c @@ -0,0 +1,118 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#if defined(_KERNEL) && !defined(_BOOT) +#include +#else +#include +#include +#endif + +/* + * This allocator is very simple. + * - it uses a pre-allocated buffer for memory allocations. + * - it does _not_ free memory in the pre-allocated buffer. + * + * The reason for the selected implemention is simplicity. + * This allocator is designed for the usage in interrupt context when + * the caller may not wait for free memory. + */ + +/* pre-allocated buffer for memory allocations */ +typedef struct nvbuf { + uintptr_t nvb_buf; /* address of pre-allocated buffer */ + uintptr_t nvb_lim; /* limit address in the buffer */ + uintptr_t nvb_cur; /* current address in the buffer */ +} nvbuf_t; + +/* + * Initialize the pre-allocated buffer allocator. The caller needs to supply + * + * buf address of pre-allocated buffer + * bufsz size of pre-allocated buffer + * + * nv_fixed_init() calculates the remaining members of nvbuf_t. + */ +static int +nv_fixed_init(nv_alloc_t *nva, va_list valist) +{ + uintptr_t base = va_arg(valist, uintptr_t); + uintptr_t lim = base + va_arg(valist, size_t); + nvbuf_t *nvb = (nvbuf_t *)P2ROUNDUP(base, sizeof (uintptr_t)); + + if (base == 0 || (uintptr_t)&nvb[1] > lim) + return (EINVAL); + + nvb->nvb_buf = (uintptr_t)&nvb[0]; + nvb->nvb_cur = (uintptr_t)&nvb[1]; + nvb->nvb_lim = lim; + nva->nva_arg = nvb; + + return (0); +} + +static void * +nv_fixed_alloc(nv_alloc_t *nva, size_t size) +{ + nvbuf_t *nvb = nva->nva_arg; + uintptr_t new = nvb->nvb_cur; + + if (size == 0 || new + size > nvb->nvb_lim) + return (NULL); + + nvb->nvb_cur = P2ROUNDUP(new + size, sizeof (uintptr_t)); + + return ((void *)new); +} + +/*ARGSUSED*/ +static void +nv_fixed_free(nv_alloc_t *nva, void *buf, size_t size) +{ + /* don't free memory in the pre-allocated buffer */ +} + +static void +nv_fixed_reset(nv_alloc_t *nva) +{ + nvbuf_t *nvb = nva->nva_arg; + + nvb->nvb_cur = (uintptr_t)&nvb[1]; +} + +const nv_alloc_ops_t nv_fixed_ops_def = { + nv_fixed_init, /* nv_ao_init() */ + NULL, /* nv_ao_fini() */ + nv_fixed_alloc, /* nv_ao_alloc() */ + nv_fixed_free, /* nv_ao_free() */ + nv_fixed_reset /* nv_ao_reset() */ +}; + +const nv_alloc_ops_t *nv_fixed_ops = &nv_fixed_ops_def; Index: sys/conf/files =================================================================== --- sys/conf/files +++ sys/conf/files @@ -126,9 +126,9 @@ cddl/compat/opensolaris/kern/opensolaris_zone.c optional zfs compile-with "${ZFS_C}" cddl/contrib/opensolaris/common/acl/acl_common.c optional zfs compile-with "${ZFS_C}" cddl/contrib/opensolaris/common/avl/avl.c optional zfs compile-with "${ZFS_C}" -cddl/contrib/opensolaris/common/nvpair/fnvpair.c optional zfs compile-with "${ZFS_C}" -cddl/contrib/opensolaris/common/nvpair/nvpair.c optional zfs compile-with "${ZFS_C}" -cddl/contrib/opensolaris/common/nvpair/nvpair_alloc_fixed.c optional zfs compile-with "${ZFS_C}" +cddl/contrib/opensolaris/common/nvpair/opensolaris_fnvpair.c optional zfs compile-with "${ZFS_C}" +cddl/contrib/opensolaris/common/nvpair/opensolaris_nvpair.c optional zfs compile-with "${ZFS_C}" +cddl/contrib/opensolaris/common/nvpair/opensolaris_nvpair_alloc_fixed.c optional zfs compile-with "${ZFS_C}" cddl/contrib/opensolaris/common/unicode/u8_textprep.c optional zfs compile-with "${ZFS_C}" cddl/contrib/opensolaris/common/zfs/zfeature_common.c optional zfs compile-with "${ZFS_C}" cddl/contrib/opensolaris/common/zfs/zfs_comutil.c optional zfs compile-with "${ZFS_C}" @@ -476,6 +476,9 @@ contrib/libfdt/fdt_strerror.c optional fdt contrib/libfdt/fdt_sw.c optional fdt contrib/libfdt/fdt_wip.c optional fdt +contrib/libnv/dnvlist.c standard +contrib/libnv/nvlist.c standard +contrib/libnv/nvpair.c standard contrib/ngatm/netnatm/api/cc_conn.c optional ngatm_ccatm \ compile-with "${NORMAL_C_NOWERROR} -I$S/contrib/ngatm" contrib/ngatm/netnatm/api/cc_data.c optional ngatm_ccatm \ Index: sys/contrib/libnv/dnvlist.c =================================================================== --- /dev/null +++ sys/contrib/libnv/dnvlist.c @@ -0,0 +1,128 @@ +/*- + * Copyright (c) 2013 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Pawel Jakub Dawidek under sponsorship from + * the FreeBSD Foundation. + * + * 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 AUTHORS 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 AUTHORS 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 +__FBSDID("$FreeBSD: head/sys/contrib/libnv/dnvlist.c 285139 2015-07-04 16:33:37Z oshogbo $"); + +#ifdef _KERNEL + +#include +#include +#include +#include +#include + +#include + +#else +#include +#include +#include +#include +#endif + +#include +#include + +#include "nv_impl.h" + +#define DNVLIST_GET(ftype, type) \ +ftype \ +dnvlist_get_##type(const nvlist_t *nvl, const char *name, ftype defval) \ +{ \ + \ + if (nvlist_exists_##type(nvl, name)) \ + return (nvlist_get_##type(nvl, name)); \ + else \ + return (defval); \ +} + +DNVLIST_GET(bool, bool) +DNVLIST_GET(uint64_t, number) +DNVLIST_GET(const char *, string) +DNVLIST_GET(const nvlist_t *, nvlist) +#ifndef _KERNEL +DNVLIST_GET(int, descriptor) +#endif + +#undef DNVLIST_GET + +const void * +dnvlist_get_binary(const nvlist_t *nvl, const char *name, size_t *sizep, + const void *defval, size_t defsize) +{ + const void *value; + + if (nvlist_exists_binary(nvl, name)) + value = nvlist_get_binary(nvl, name, sizep); + else { + if (sizep != NULL) + *sizep = defsize; + value = defval; + } + return (value); +} + +#define DNVLIST_TAKE(ftype, type) \ +ftype \ +dnvlist_take_##type(nvlist_t *nvl, const char *name, ftype defval) \ +{ \ + \ + if (nvlist_exists_##type(nvl, name)) \ + return (nvlist_take_##type(nvl, name)); \ + else \ + return (defval); \ +} + +DNVLIST_TAKE(bool, bool) +DNVLIST_TAKE(uint64_t, number) +DNVLIST_TAKE(char *, string) +DNVLIST_TAKE(nvlist_t *, nvlist) +#ifndef _KERNEL +DNVLIST_TAKE(int, descriptor) +#endif + +#undef DNVLIST_TAKE + +void * +dnvlist_take_binary(nvlist_t *nvl, const char *name, size_t *sizep, + void *defval, size_t defsize) +{ + void *value; + + if (nvlist_exists_binary(nvl, name)) + value = nvlist_take_binary(nvl, name, sizep); + else { + if (sizep != NULL) + *sizep = defsize; + value = defval; + } + return (value); +} + Index: sys/contrib/libnv/nv_impl.h =================================================================== --- /dev/null +++ sys/contrib/libnv/nv_impl.h @@ -0,0 +1,158 @@ +/*- + * Copyright (c) 2013 The FreeBSD Foundation + * Copyright (c) 2013-2015 Mariusz Zaborski + * All rights reserved. + * + * This software was developed by Pawel Jakub Dawidek under sponsorship from + * the FreeBSD Foundation. + * + * 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 AUTHORS 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 AUTHORS 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: head/sys/contrib/libnv/nv_impl.h 286796 2015-08-15 06:34:49Z oshogbo $ + */ + +#ifndef _NV_IMPL_H_ +#define _NV_IMPL_H_ + +#ifndef _NVPAIR_T_DECLARED +#define _NVPAIR_T_DECLARED +struct nvpair; + +typedef struct nvpair nvpair_t; +#endif + +#define NV_TYPE_NVLIST_ARRAY_NEXT 254 +#define NV_TYPE_NVLIST_UP 255 + +#define NV_TYPE_FIRST NV_TYPE_NULL +#define NV_TYPE_LAST NV_TYPE_DESCRIPTOR_ARRAY + +#define NV_FLAG_BIG_ENDIAN 0x080 +#define NV_FLAG_IN_ARRAY 0x100 + +#ifdef _KERNEL +#define nv_malloc(size) malloc((size), M_NVLIST, M_WAITOK) +#define nv_calloc(n, size) malloc((n) * (size), M_NVLIST, \ + M_WAITOK | M_ZERO) +#define nv_realloc(buf, size) realloc((buf), (size), M_NVLIST, \ + M_WAITOK) +#define nv_free(buf) free((buf), M_NVLIST) +#define nv_strdup(buf) strdup((buf), M_NVLIST) +#define nv_vasprintf(ptr, ...) vasprintf(ptr, M_NVLIST, __VA_ARGS__) + +#define ERRNO_SET(var) do { } while (0) +#define ERRNO_SAVE() do { do { } while(0) +#define ERRNO_RESTORE() } while (0) + +#define ERRNO_OR_DEFAULT(default) (default) + +#else + +#define nv_malloc(size) malloc((size)) +#define nv_calloc(n, size) calloc((n), (size)) +#define nv_realloc(buf, size) realloc((buf), (size)) +#define nv_free(buf) free((buf)) +#define nv_strdup(buf) strdup((buf)) +#define nv_vasprintf(ptr, ...) vasprintf(ptr, __VA_ARGS__) + +#define ERRNO_SET(var) do { errno = (var); } while (0) +#define ERRNO_SAVE() do { \ + int _serrno; \ + \ + _serrno = errno + +#define ERRNO_RESTORE() errno = _serrno; \ + } while (0) + +#define ERRNO_OR_DEFAULT(default) (errno == 0 ? (default) : errno) + +#endif + +int *nvlist_descriptors(const nvlist_t *nvl, size_t *nitemsp); +size_t nvlist_ndescriptors(const nvlist_t *nvl); +void nvlist_set_flags(nvlist_t *nvl, int flags); + +nvpair_t *nvlist_first_nvpair(const nvlist_t *nvl); +nvpair_t *nvlist_next_nvpair(const nvlist_t *nvl, const nvpair_t *nvp); +nvpair_t *nvlist_prev_nvpair(const nvlist_t *nvl, const nvpair_t *nvp); + +void nvlist_add_nvpair(nvlist_t *nvl, const nvpair_t *nvp); + +bool nvlist_move_nvpair(nvlist_t *nvl, nvpair_t *nvp); + +void nvlist_set_parent(nvlist_t *nvl, nvpair_t *parent); +void nvlist_set_array_next(nvlist_t *nvl, nvpair_t *ele); + +const nvpair_t *nvlist_get_nvpair(const nvlist_t *nvl, const char *name); + +nvpair_t *nvlist_take_nvpair(nvlist_t *nvl, const char *name); + +/* Function removes the given nvpair from the nvlist. */ +void nvlist_remove_nvpair(nvlist_t *nvl, nvpair_t *nvp); + +void nvlist_free_nvpair(nvlist_t *nvl, nvpair_t *nvp); + +int nvpair_type(const nvpair_t *nvp); +const char *nvpair_name(const nvpair_t *nvp); + +nvpair_t *nvpair_clone(const nvpair_t *nvp); + +nvpair_t *nvpair_create_null(const char *name); +nvpair_t *nvpair_create_bool(const char *name, bool value); +nvpair_t *nvpair_create_number(const char *name, uint64_t value); +nvpair_t *nvpair_create_string(const char *name, const char *value); +nvpair_t *nvpair_create_stringf(const char *name, const char *valuefmt, ...) __printflike(2, 3); +nvpair_t *nvpair_create_stringv(const char *name, const char *valuefmt, va_list valueap) __printflike(2, 0); +nvpair_t *nvpair_create_nvlist(const char *name, const nvlist_t *value); +nvpair_t *nvpair_create_descriptor(const char *name, int value); +nvpair_t *nvpair_create_binary(const char *name, const void *value, size_t size); +nvpair_t *nvpair_create_bool_array(const char *name, const bool *value, size_t nitems); +nvpair_t *nvpair_create_number_array(const char *name, const uint64_t *value, size_t nitems); +nvpair_t *nvpair_create_string_array(const char *name, const char * const *value, size_t nitems); +nvpair_t *nvpair_create_nvlist_array(const char *name, const nvlist_t * const *value, size_t nitems); +nvpair_t *nvpair_create_descriptor_array(const char *name, const int *value, size_t nitems); + +nvpair_t *nvpair_move_string(const char *name, char *value); +nvpair_t *nvpair_move_nvlist(const char *name, nvlist_t *value); +nvpair_t *nvpair_move_descriptor(const char *name, int value); +nvpair_t *nvpair_move_binary(const char *name, void *value, size_t size); +nvpair_t *nvpair_move_bool_array(const char *name, bool *value, size_t nitems); +nvpair_t *nvpair_move_nvlist_array(const char *name, nvlist_t **value, size_t nitems); +nvpair_t *nvpair_move_descriptor_array(const char *name, int *value, size_t nitems); +nvpair_t *nvpair_move_number_array(const char *name, uint64_t *value, size_t nitems); +nvpair_t *nvpair_move_string_array(const char *name, char **value, size_t nitems); + +bool nvpair_get_bool(const nvpair_t *nvp); +uint64_t nvpair_get_number(const nvpair_t *nvp); +const char *nvpair_get_string(const nvpair_t *nvp); +const nvlist_t *nvpair_get_nvlist(const nvpair_t *nvp); +int nvpair_get_descriptor(const nvpair_t *nvp); +const void *nvpair_get_binary(const nvpair_t *nvp, size_t *sizep); +const bool *nvpair_get_bool_array(const nvpair_t *nvp, size_t *nitemsp); +const uint64_t *nvpair_get_number_array(const nvpair_t *nvp, size_t *nitemsp); +const char * const *nvpair_get_string_array(const nvpair_t *nvp, size_t *nitemsp); +const nvlist_t * const *nvpair_get_nvlist_array(const nvpair_t *nvp, size_t *nitemsp); +const int *nvpair_get_descriptor_array(const nvpair_t *nvp, size_t *nitemsp); + +void nvpair_free(nvpair_t *nvp); + +#endif /* !_NV_IMPL_H_ */ Index: sys/contrib/libnv/nvlist.c =================================================================== --- /dev/null +++ sys/contrib/libnv/nvlist.c @@ -0,0 +1,2024 @@ +/*- + * Copyright (c) 2009-2013 The FreeBSD Foundation + * Copyright (c) 2013-2015 Mariusz Zaborski + * All rights reserved. + * + * This software was developed by Pawel Jakub Dawidek under sponsorship from + * the FreeBSD Foundation. + * + * 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 AUTHORS 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 AUTHORS 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 +__FBSDID("$FreeBSD: head/sys/contrib/libnv/nvlist.c 286796 2015-08-15 06:34:49Z oshogbo $"); + +#include +#include +#include + +#ifdef _KERNEL + +#include +#include +#include +#include +#include + +#include + +#else +#include + +#include +#include +#include +#include +#define _WITH_DPRINTF +#include +#include +#include +#include + +#include "msgio.h" +#endif + +#ifdef HAVE_PJDLOG +#include +#endif + +#include + +#include "nv_impl.h" +#include "nvlist_impl.h" +#include "nvpair_impl.h" + +#ifndef HAVE_PJDLOG +#ifdef _KERNEL +#define PJDLOG_ASSERT(...) MPASS(__VA_ARGS__) +#define PJDLOG_RASSERT(expr, ...) KASSERT(expr, (__VA_ARGS__)) +#define PJDLOG_ABORT(...) panic(__VA_ARGS__) +#else +#include +#define PJDLOG_ASSERT(...) assert(__VA_ARGS__) +#define PJDLOG_RASSERT(expr, ...) assert(expr) +#define PJDLOG_ABORT(...) do { \ + fprintf(stderr, "%s:%u: ", __FILE__, __LINE__); \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, "\n"); \ + abort(); \ +} while (0) +#endif +#endif + +#define NV_FLAG_PRIVATE_MASK (NV_FLAG_BIG_ENDIAN | NV_FLAG_IN_ARRAY) +#define NV_FLAG_PUBLIC_MASK (NV_FLAG_IGNORE_CASE | NV_FLAG_NO_UNIQUE) +#define NV_FLAG_ALL_MASK (NV_FLAG_PRIVATE_MASK | NV_FLAG_PUBLIC_MASK) + +#define NVLIST_MAGIC 0x6e766c /* "nvl" */ +struct nvlist { + int nvl_magic; + int nvl_error; + int nvl_flags; + nvpair_t *nvl_parent; + nvpair_t *nvl_array_next; + struct nvl_head nvl_head; +}; + +#define NVLIST_ASSERT(nvl) do { \ + PJDLOG_ASSERT((nvl) != NULL); \ + PJDLOG_ASSERT((nvl)->nvl_magic == NVLIST_MAGIC); \ +} while (0) + +#ifdef _KERNEL +MALLOC_DEFINE(M_NVLIST, "nvlist", "kernel nvlist"); +#endif + +#define NVPAIR_ASSERT(nvp) nvpair_assert(nvp) + +#define NVLIST_HEADER_MAGIC 0x6c +#define NVLIST_HEADER_VERSION 0x00 +struct nvlist_header { + uint8_t nvlh_magic; + uint8_t nvlh_version; + uint8_t nvlh_flags; + uint64_t nvlh_descriptors; + uint64_t nvlh_size; +} __packed; + +nvlist_t * +nvlist_create(int flags) +{ + nvlist_t *nvl; + + PJDLOG_ASSERT((flags & ~(NV_FLAG_PUBLIC_MASK)) == 0); + + nvl = nv_malloc(sizeof(*nvl)); + if (nvl == NULL) + return (NULL); + nvl->nvl_error = 0; + nvl->nvl_flags = flags; + nvl->nvl_parent = NULL; + nvl->nvl_array_next = NULL; + TAILQ_INIT(&nvl->nvl_head); + nvl->nvl_magic = NVLIST_MAGIC; + + return (nvl); +} + +void +nvlist_destroy(nvlist_t *nvl) +{ + nvpair_t *nvp; + + if (nvl == NULL) + return; + + ERRNO_SAVE(); + + NVLIST_ASSERT(nvl); + + while ((nvp = nvlist_first_nvpair(nvl)) != NULL) { + nvlist_remove_nvpair(nvl, nvp); + nvpair_free(nvp); + } + if (nvl->nvl_array_next != NULL) + nvpair_free_structure(nvl->nvl_array_next); + nvl->nvl_array_next = NULL; + nvl->nvl_parent = NULL; + nvl->nvl_magic = 0; + nv_free(nvl); + + ERRNO_RESTORE(); +} + +void +nvlist_set_error(nvlist_t *nvl, int error) +{ + + PJDLOG_ASSERT(error != 0); + + /* + * Check for error != 0 so that we don't do the wrong thing if somebody + * tries to abuse this API when asserts are disabled. + */ + if (nvl != NULL && error != 0 && nvl->nvl_error == 0) + nvl->nvl_error = error; +} + +int +nvlist_error(const nvlist_t *nvl) +{ + + if (nvl == NULL) + return (ENOMEM); + + NVLIST_ASSERT(nvl); + + return (nvl->nvl_error); +} + +nvpair_t * +nvlist_get_nvpair_parent(const nvlist_t *nvl) +{ + + NVLIST_ASSERT(nvl); + + return (nvl->nvl_parent); +} + +const nvlist_t * +nvlist_get_parent(const nvlist_t *nvl, void **cookiep) +{ + nvpair_t *nvp; + + NVLIST_ASSERT(nvl); + + nvp = nvl->nvl_parent; + if (cookiep != NULL) + *cookiep = nvp; + if (nvp == NULL) + return (NULL); + + return (nvpair_nvlist(nvp)); +} + +void +nvlist_set_parent(nvlist_t *nvl, nvpair_t *parent) +{ + + NVLIST_ASSERT(nvl); + + nvl->nvl_parent = parent; +} + +void +nvlist_set_array_next(nvlist_t *nvl, nvpair_t *ele) +{ + + NVLIST_ASSERT(nvl); + + if (ele != NULL) + nvl->nvl_flags |= NV_FLAG_IN_ARRAY; + else + nvl->nvl_flags &= ~NV_FLAG_IN_ARRAY; + + nvl->nvl_array_next = ele; +} + +bool +nvlist_in_array(const nvlist_t *nvl) +{ + + NVLIST_ASSERT(nvl); + + return ((nvl->nvl_flags & NV_FLAG_IN_ARRAY) != 0); +} + +const nvlist_t * +nvlist_get_array_next(const nvlist_t *nvl) +{ + nvpair_t *nvp; + + NVLIST_ASSERT(nvl); + + nvp = nvl->nvl_array_next; + if (nvp == NULL) + return (NULL); + + return (nvpair_get_nvlist(nvp)); +} + +const nvlist_t * +nvlist_get_pararr(const nvlist_t *nvl, void **cookiep) +{ + const nvlist_t *ret; + + ret = nvlist_get_array_next(nvl); + if (ret != NULL) { + if (cookiep != NULL) + *cookiep = NULL; + return (ret); + } + + ret = nvlist_get_parent(nvl, cookiep); + return (ret); +} + +bool +nvlist_empty(const nvlist_t *nvl) +{ + + NVLIST_ASSERT(nvl); + PJDLOG_ASSERT(nvl->nvl_error == 0); + + return (nvlist_first_nvpair(nvl) == NULL); +} + +int +nvlist_flags(const nvlist_t *nvl) +{ + + NVLIST_ASSERT(nvl); + PJDLOG_ASSERT(nvl->nvl_error == 0); + + return (nvl->nvl_flags & NV_FLAG_PUBLIC_MASK); +} + +void +nvlist_set_flags(nvlist_t *nvl, int flags) +{ + + NVLIST_ASSERT(nvl); + PJDLOG_ASSERT(nvl->nvl_error == 0); + + nvl->nvl_flags = flags; +} + +static void +nvlist_report_missing(int type, const char *name) +{ + + PJDLOG_ABORT("Element '%s' of type %s doesn't exist.", + name, nvpair_type_string(type)); +} + +static nvpair_t * +nvlist_find(const nvlist_t *nvl, int type, const char *name) +{ + nvpair_t *nvp; + + NVLIST_ASSERT(nvl); + PJDLOG_ASSERT(nvl->nvl_error == 0); + PJDLOG_ASSERT(type == NV_TYPE_NONE || + (type >= NV_TYPE_FIRST && type <= NV_TYPE_LAST)); + + for (nvp = nvlist_first_nvpair(nvl); nvp != NULL; + nvp = nvlist_next_nvpair(nvl, nvp)) { + if (type != NV_TYPE_NONE && nvpair_type(nvp) != type) + continue; + if ((nvl->nvl_flags & NV_FLAG_IGNORE_CASE) != 0) { + if (strcasecmp(nvpair_name(nvp), name) != 0) + continue; + } else { + if (strcmp(nvpair_name(nvp), name) != 0) + continue; + } + break; + } + + if (nvp == NULL) + ERRNO_SET(ENOENT); + + return (nvp); +} + +bool +nvlist_exists_type(const nvlist_t *nvl, const char *name, int type) +{ + + NVLIST_ASSERT(nvl); + PJDLOG_ASSERT(nvl->nvl_error == 0); + PJDLOG_ASSERT(type == NV_TYPE_NONE || + (type >= NV_TYPE_FIRST && type <= NV_TYPE_LAST)); + + return (nvlist_find(nvl, type, name) != NULL); +} + +void +nvlist_free_type(nvlist_t *nvl, const char *name, int type) +{ + nvpair_t *nvp; + + NVLIST_ASSERT(nvl); + PJDLOG_ASSERT(nvl->nvl_error == 0); + PJDLOG_ASSERT(type == NV_TYPE_NONE || + (type >= NV_TYPE_FIRST && type <= NV_TYPE_LAST)); + + nvp = nvlist_find(nvl, type, name); + if (nvp != NULL) + nvlist_free_nvpair(nvl, nvp); + else + nvlist_report_missing(type, name); +} + +nvlist_t * +nvlist_clone(const nvlist_t *nvl) +{ + nvlist_t *newnvl; + nvpair_t *nvp, *newnvp; + + NVLIST_ASSERT(nvl); + + if (nvl->nvl_error != 0) { + ERRNO_SET(nvl->nvl_error); + return (NULL); + } + + newnvl = nvlist_create(nvl->nvl_flags & NV_FLAG_PUBLIC_MASK); + for (nvp = nvlist_first_nvpair(nvl); nvp != NULL; + nvp = nvlist_next_nvpair(nvl, nvp)) { + newnvp = nvpair_clone(nvp); + if (newnvp == NULL) + break; + (void)nvlist_move_nvpair(newnvl, newnvp); + } + if (nvp != NULL) { + nvlist_destroy(newnvl); + return (NULL); + } + return (newnvl); +} + +#ifndef _KERNEL +static bool +nvlist_dump_error_check(const nvlist_t *nvl, int fd, int level) +{ + + if (nvlist_error(nvl) != 0) { + dprintf(fd, "%*serror: %d\n", level * 4, "", + nvlist_error(nvl)); + return (true); + } + + return (false); +} + +/* + * Dump content of nvlist. + */ +void +nvlist_dump(const nvlist_t *nvl, int fd) +{ + const nvlist_t *tmpnvl; + nvpair_t *nvp, *tmpnvp; + void *cookie; + int level; + + level = 0; + if (nvlist_dump_error_check(nvl, fd, level)) + return; + + nvp = nvlist_first_nvpair(nvl); + while (nvp != NULL) { + dprintf(fd, "%*s%s (%s):", level * 4, "", nvpair_name(nvp), + nvpair_type_string(nvpair_type(nvp))); + switch (nvpair_type(nvp)) { + case NV_TYPE_NULL: + dprintf(fd, " null\n"); + break; + case NV_TYPE_BOOL: + dprintf(fd, " %s\n", nvpair_get_bool(nvp) ? + "TRUE" : "FALSE"); + break; + case NV_TYPE_NUMBER: + dprintf(fd, " %ju (%jd) (0x%jx)\n", + (uintmax_t)nvpair_get_number(nvp), + (intmax_t)nvpair_get_number(nvp), + (uintmax_t)nvpair_get_number(nvp)); + break; + case NV_TYPE_STRING: + dprintf(fd, " [%s]\n", nvpair_get_string(nvp)); + break; + case NV_TYPE_NVLIST: + dprintf(fd, "\n"); + tmpnvl = nvpair_get_nvlist(nvp); + if (nvlist_dump_error_check(tmpnvl, fd, level + 1)) + break; + tmpnvp = nvlist_first_nvpair(tmpnvl); + if (tmpnvp != NULL) { + nvl = tmpnvl; + nvp = tmpnvp; + level++; + continue; + } + break; + case NV_TYPE_DESCRIPTOR: + dprintf(fd, " %d\n", nvpair_get_descriptor(nvp)); + break; + case NV_TYPE_BINARY: + { + const unsigned char *binary; + unsigned int ii; + size_t size; + + binary = nvpair_get_binary(nvp, &size); + dprintf(fd, " %zu ", size); + for (ii = 0; ii < size; ii++) + dprintf(fd, "%02hhx", binary[ii]); + dprintf(fd, "\n"); + break; + } + case NV_TYPE_BOOL_ARRAY: + { + const bool *value; + unsigned int ii; + size_t nitems; + + value = nvpair_get_bool_array(nvp, &nitems); + dprintf(fd, " [ "); + for (ii = 0; ii < nitems; ii++) { + dprintf(fd, "%s", value[ii] ? "TRUE" : "FALSE"); + if (ii != nitems - 1) + dprintf(fd, ", "); + } + dprintf(fd, " ]\n"); + break; + } + case NV_TYPE_STRING_ARRAY: + { + const char * const *value; + unsigned int ii; + size_t nitems; + + value = nvpair_get_string_array(nvp, &nitems); + dprintf(fd, " [ "); + for (ii = 0; ii < nitems; ii++) { + if (value[ii] == NULL) + dprintf(fd, "NULL"); + else + dprintf(fd, "\"%s\"", value[ii]); + if (ii != nitems - 1) + dprintf(fd, ", "); + } + dprintf(fd, " ]\n"); + break; + } + case NV_TYPE_NUMBER_ARRAY: + { + const uint64_t *value; + unsigned int ii; + size_t nitems; + + value = nvpair_get_number_array(nvp, &nitems); + dprintf(fd, " [ "); + for (ii = 0; ii < nitems; ii++) { + dprintf(fd, "%ju (%jd) (0x%jx)", + value[ii], value[ii], value[ii]); + if (ii != nitems - 1) + dprintf(fd, ", "); + } + dprintf(fd, " ]\n"); + break; + } + case NV_TYPE_DESCRIPTOR_ARRAY: + { + const int *value; + unsigned int ii; + size_t nitems; + + value = nvpair_get_descriptor_array(nvp, &nitems); + dprintf(fd, " [ "); + for (ii = 0; ii < nitems; ii++) { + dprintf(fd, "%d", value[ii]); + if (ii != nitems - 1) + dprintf(fd, ", "); + } + dprintf(fd, " ]\n"); + break; + } + case NV_TYPE_NVLIST_ARRAY: + { + const nvlist_t * const *value; + unsigned int ii; + size_t nitems; + + value = nvpair_get_nvlist_array(nvp, &nitems); + dprintf(fd, " %zu\n", nitems); + tmpnvl = NULL; + tmpnvp = NULL; + for (ii = 0; ii < nitems; ii++) { + if (nvlist_dump_error_check(value[ii], fd, + level + 1)) { + break; + } + + if (tmpnvl == NULL) { + tmpnvp = nvlist_first_nvpair(value[ii]); + if (tmpnvp != NULL) { + tmpnvl = value[ii]; + } else { + dprintf(fd, "%*s,\n", + (level + 1) * 4, ""); + } + } + } + if (tmpnvp != NULL) { + nvl = tmpnvl; + nvp = tmpnvp; + level++; + continue; + } + break; + } + default: + PJDLOG_ABORT("Unknown type: %d.", nvpair_type(nvp)); + } + + while ((nvp = nvlist_next_nvpair(nvl, nvp)) == NULL) { + do { + cookie = NULL; + if (nvlist_in_array(nvl)) + dprintf(fd, "%*s,\n", level * 4, ""); + nvl = nvlist_get_pararr(nvl, &cookie); + if (nvl == NULL) + return; + if (nvlist_in_array(nvl) && cookie == NULL) { + nvp = nvlist_first_nvpair(nvl); + } else { + nvp = cookie; + level--; + } + } while (nvp == NULL); + if (nvlist_in_array(nvl) && cookie == NULL) + break; + } + } +} + +void +nvlist_fdump(const nvlist_t *nvl, FILE *fp) +{ + + fflush(fp); + nvlist_dump(nvl, fileno(fp)); +} +#endif + +/* + * The function obtains size of the nvlist after nvlist_pack(). + */ +size_t +nvlist_size(const nvlist_t *nvl) +{ + const nvlist_t *tmpnvl; + const nvlist_t * const *nvlarray; + const nvpair_t *nvp, *tmpnvp; + void *cookie; + size_t size, nitems; + unsigned int ii; + + NVLIST_ASSERT(nvl); + PJDLOG_ASSERT(nvl->nvl_error == 0); + + size = sizeof(struct nvlist_header); + nvp = nvlist_first_nvpair(nvl); + while (nvp != NULL) { + size += nvpair_header_size(); + size += strlen(nvpair_name(nvp)) + 1; + if (nvpair_type(nvp) == NV_TYPE_NVLIST) { + size += sizeof(struct nvlist_header); + size += nvpair_header_size() + 1; + tmpnvl = nvpair_get_nvlist(nvp); + PJDLOG_ASSERT(tmpnvl->nvl_error == 0); + tmpnvp = nvlist_first_nvpair(tmpnvl); + if (tmpnvp != NULL) { + nvl = tmpnvl; + nvp = tmpnvp; + continue; + } + } else if (nvpair_type(nvp) == NV_TYPE_NVLIST_ARRAY) { + nvlarray = nvpair_get_nvlist_array(nvp, &nitems); + PJDLOG_ASSERT(nitems > 0); + + size += (nvpair_header_size() + 1) * nitems; + size += sizeof(struct nvlist_header) * nitems; + + tmpnvl = NULL; + tmpnvp = NULL; + for (ii = 0; ii < nitems; ii++) { + PJDLOG_ASSERT(nvlarray[ii]->nvl_error == 0); + tmpnvp = nvlist_first_nvpair(nvlarray[ii]); + if (tmpnvp != NULL) { + tmpnvl = nvlarray[ii]; + break; + } + } + if (tmpnvp != NULL) { + nvp = tmpnvp; + nvl = tmpnvl; + continue; + } + + } else { + size += nvpair_size(nvp); + } + + while ((nvp = nvlist_next_nvpair(nvl, nvp)) == NULL) { + do { + cookie = NULL; + nvl = nvlist_get_pararr(nvl, &cookie); + if (nvl == NULL) + goto out; + if (nvlist_in_array(nvl) && cookie == NULL) { + nvp = nvlist_first_nvpair(nvl); + } else { + nvp = cookie; + } + } while (nvp == NULL); + if (nvlist_in_array(nvl) && cookie == NULL) + break; + } + } + +out: + return (size); +} + +#ifndef _KERNEL +static int * +nvlist_xdescriptors(const nvlist_t *nvl, int *descs) +{ + nvpair_t *nvp; + const char *name; + int type; + + NVLIST_ASSERT(nvl); + PJDLOG_ASSERT(nvl->nvl_error == 0); + + nvp = NULL; + do { + while ((name = nvlist_next(nvl, &type, (void**)&nvp)) != NULL) { + switch (type) { + case NV_TYPE_DESCRIPTOR: + *descs = nvpair_get_descriptor(nvp); + descs++; + break; + case NV_TYPE_DESCRIPTOR_ARRAY: + { + const int *value; + size_t nitems; + unsigned int ii; + + value = nvpair_get_descriptor_array(nvp, + &nitems); + for (ii = 0; ii < nitems; ii++) { + *descs = value[ii]; + descs++; + } + break; + } + case NV_TYPE_NVLIST: + nvl = nvpair_get_nvlist(nvp); + nvp = NULL; + break; + case NV_TYPE_NVLIST_ARRAY: + { + const nvlist_t * const *value; + size_t nitems; + + value = nvpair_get_nvlist_array(nvp, &nitems); + PJDLOG_ASSERT(value != NULL); + PJDLOG_ASSERT(nitems > 0); + + nvl = value[0]; + nvp = NULL; + break; + } + } + } + } while ((nvl = nvlist_get_pararr(nvl, (void**)&nvp)) != NULL); + + return (descs); +} +#endif + +#ifndef _KERNEL +int * +nvlist_descriptors(const nvlist_t *nvl, size_t *nitemsp) +{ + size_t nitems; + int *fds; + + nitems = nvlist_ndescriptors(nvl); + fds = nv_malloc(sizeof(fds[0]) * (nitems + 1)); + if (fds == NULL) + return (NULL); + if (nitems > 0) + nvlist_xdescriptors(nvl, fds); + fds[nitems] = -1; + if (nitemsp != NULL) + *nitemsp = nitems; + return (fds); +} +#endif + +size_t +nvlist_ndescriptors(const nvlist_t *nvl) +{ +#ifndef _KERNEL + nvpair_t *nvp; + const char *name; + size_t ndescs; + int type; + + NVLIST_ASSERT(nvl); + PJDLOG_ASSERT(nvl->nvl_error == 0); + + ndescs = 0; + nvp = NULL; + do { + while ((name = nvlist_next(nvl, &type, (void**)&nvp)) != NULL) { + switch (type) { + case NV_TYPE_DESCRIPTOR: + ndescs++; + break; + case NV_TYPE_NVLIST: + nvl = nvpair_get_nvlist(nvp); + nvp = NULL; + break; + case NV_TYPE_NVLIST_ARRAY: + { + const nvlist_t * const *value; + size_t nitems; + + value = nvpair_get_nvlist_array(nvp, &nitems); + PJDLOG_ASSERT(value != NULL); + PJDLOG_ASSERT(nitems > 0); + + nvl = value[0]; + nvp = NULL; + break; + } + case NV_TYPE_DESCRIPTOR_ARRAY: + { + size_t nitems; + + (void)nvpair_get_descriptor_array(nvp, + &nitems); + ndescs += nitems; + break; + } + } + } + } while ((nvl = nvlist_get_pararr(nvl, (void**)&nvp)) != NULL); + + return (ndescs); +#else + return (0); +#endif +} + +static unsigned char * +nvlist_pack_header(const nvlist_t *nvl, unsigned char *ptr, size_t *leftp) +{ + struct nvlist_header nvlhdr; + + NVLIST_ASSERT(nvl); + + nvlhdr.nvlh_magic = NVLIST_HEADER_MAGIC; + nvlhdr.nvlh_version = NVLIST_HEADER_VERSION; + nvlhdr.nvlh_flags = nvl->nvl_flags; +#if BYTE_ORDER == BIG_ENDIAN + nvlhdr.nvlh_flags |= NV_FLAG_BIG_ENDIAN; +#endif + nvlhdr.nvlh_descriptors = nvlist_ndescriptors(nvl); + nvlhdr.nvlh_size = *leftp - sizeof(nvlhdr); + PJDLOG_ASSERT(*leftp >= sizeof(nvlhdr)); + memcpy(ptr, &nvlhdr, sizeof(nvlhdr)); + ptr += sizeof(nvlhdr); + *leftp -= sizeof(nvlhdr); + + return (ptr); +} + +static void * +nvlist_xpack(const nvlist_t *nvl, int64_t *fdidxp, size_t *sizep) +{ + unsigned char *buf, *ptr; + size_t left, size; + const nvlist_t *tmpnvl; + nvpair_t *nvp, *tmpnvp; + void *cookie; + + NVLIST_ASSERT(nvl); + + if (nvl->nvl_error != 0) { + ERRNO_SET(nvl->nvl_error); + return (NULL); + } + + size = nvlist_size(nvl); + buf = nv_malloc(size); + if (buf == NULL) + return (NULL); + + ptr = buf; + left = size; + + ptr = nvlist_pack_header(nvl, ptr, &left); + + nvp = nvlist_first_nvpair(nvl); + while (nvp != NULL) { + NVPAIR_ASSERT(nvp); + + nvpair_init_datasize(nvp); + ptr = nvpair_pack_header(nvp, ptr, &left); + if (ptr == NULL) + goto fail; + switch (nvpair_type(nvp)) { + case NV_TYPE_NULL: + ptr = nvpair_pack_null(nvp, ptr, &left); + break; + case NV_TYPE_BOOL: + ptr = nvpair_pack_bool(nvp, ptr, &left); + break; + case NV_TYPE_NUMBER: + ptr = nvpair_pack_number(nvp, ptr, &left); + break; + case NV_TYPE_STRING: + ptr = nvpair_pack_string(nvp, ptr, &left); + break; + case NV_TYPE_NVLIST: + tmpnvl = nvpair_get_nvlist(nvp); + ptr = nvlist_pack_header(tmpnvl, ptr, &left); + if (ptr == NULL) + goto fail; + tmpnvp = nvlist_first_nvpair(tmpnvl); + if (tmpnvp != NULL) { + nvl = tmpnvl; + nvp = tmpnvp; + continue; + } + ptr = nvpair_pack_nvlist_up(ptr, &left); + break; +#ifndef _KERNEL + case NV_TYPE_DESCRIPTOR: + ptr = nvpair_pack_descriptor(nvp, ptr, fdidxp, &left); + break; + case NV_TYPE_DESCRIPTOR_ARRAY: + ptr = nvpair_pack_descriptor_array(nvp, ptr, fdidxp, + &left); + break; +#endif + case NV_TYPE_BINARY: + ptr = nvpair_pack_binary(nvp, ptr, &left); + break; + case NV_TYPE_BOOL_ARRAY: + ptr = nvpair_pack_bool_array(nvp, ptr, &left); + break; + case NV_TYPE_NUMBER_ARRAY: + ptr = nvpair_pack_number_array(nvp, ptr, &left); + break; + case NV_TYPE_STRING_ARRAY: + ptr = nvpair_pack_string_array(nvp, ptr, &left); + break; + case NV_TYPE_NVLIST_ARRAY: + { + const nvlist_t * const * value; + size_t nitems; + unsigned int ii; + + tmpnvl = NULL; + value = nvpair_get_nvlist_array(nvp, &nitems); + for (ii = 0; ii < nitems; ii++) { + ptr = nvlist_pack_header(value[ii], ptr, &left); + if (ptr == NULL) + goto out; + tmpnvp = nvlist_first_nvpair(value[ii]); + if (tmpnvp != NULL) { + tmpnvl = value[ii]; + break; + } + ptr = nvpair_pack_nvlist_array_next(ptr, &left); + if (ptr == NULL) + goto out; + } + if (tmpnvl != NULL) { + nvl = tmpnvl; + nvp = tmpnvp; + continue; + } + break; + } + default: + PJDLOG_ABORT("Invalid type (%d).", nvpair_type(nvp)); + } + if (ptr == NULL) + goto fail; + while ((nvp = nvlist_next_nvpair(nvl, nvp)) == NULL) { + do { + cookie = NULL; + if (nvlist_in_array(nvl)) { + ptr = nvpair_pack_nvlist_array_next(ptr, + &left); + if (ptr == NULL) + goto fail; + } + nvl = nvlist_get_pararr(nvl, &cookie); + if (nvl == NULL) + goto out; + if (nvlist_in_array(nvl) && cookie == NULL) { + nvp = nvlist_first_nvpair(nvl); + ptr = nvlist_pack_header(nvl, ptr, + &left); + if (ptr == NULL) + goto fail; + } else if (nvpair_type((nvpair_t *)cookie) != + NV_TYPE_NVLIST_ARRAY) { + ptr = nvpair_pack_nvlist_up(ptr, &left); + if (ptr == NULL) + goto fail; + nvp = cookie; + } else { + nvp = cookie; + } + } while (nvp == NULL); + if (nvlist_in_array(nvl) && cookie == NULL) + break; + } + } + +out: + if (sizep != NULL) + *sizep = size; + return (buf); +fail: + nv_free(buf); + return (NULL); +} + +void * +nvlist_pack(const nvlist_t *nvl, size_t *sizep) +{ + + NVLIST_ASSERT(nvl); + + if (nvl->nvl_error != 0) { + ERRNO_SET(nvl->nvl_error); + return (NULL); + } + + if (nvlist_ndescriptors(nvl) > 0) { + ERRNO_SET(EOPNOTSUPP); + return (NULL); + } + + return (nvlist_xpack(nvl, NULL, sizep)); +} + +static bool +nvlist_check_header(struct nvlist_header *nvlhdrp) +{ + + if (nvlhdrp->nvlh_magic != NVLIST_HEADER_MAGIC) { + ERRNO_SET(EINVAL); + return (false); + } + if ((nvlhdrp->nvlh_flags & ~NV_FLAG_ALL_MASK) != 0) { + ERRNO_SET(EINVAL); + return (false); + } +#if BYTE_ORDER == BIG_ENDIAN + if ((nvlhdrp->nvlh_flags & NV_FLAG_BIG_ENDIAN) == 0) { + nvlhdrp->nvlh_size = le64toh(nvlhdrp->nvlh_size); + nvlhdrp->nvlh_descriptors = le64toh(nvlhdrp->nvlh_descriptors); + } +#else + if ((nvlhdrp->nvlh_flags & NV_FLAG_BIG_ENDIAN) != 0) { + nvlhdrp->nvlh_size = be64toh(nvlhdrp->nvlh_size); + nvlhdrp->nvlh_descriptors = be64toh(nvlhdrp->nvlh_descriptors); + } +#endif + return (true); +} + +const unsigned char * +nvlist_unpack_header(nvlist_t *nvl, const unsigned char *ptr, size_t nfds, + bool *isbep, size_t *leftp) +{ + struct nvlist_header nvlhdr; + int inarrayf; + + if (*leftp < sizeof(nvlhdr)) + goto failed; + + memcpy(&nvlhdr, ptr, sizeof(nvlhdr)); + + if (!nvlist_check_header(&nvlhdr)) + goto failed; + + if (nvlhdr.nvlh_size != *leftp - sizeof(nvlhdr)) + goto failed; + + /* + * nvlh_descriptors might be smaller than nfds in embedded nvlists. + */ + if (nvlhdr.nvlh_descriptors > nfds) + goto failed; + + if ((nvlhdr.nvlh_flags & ~NV_FLAG_ALL_MASK) != 0) + goto failed; + + inarrayf = (nvl->nvl_flags & NV_FLAG_IN_ARRAY); + nvl->nvl_flags = (nvlhdr.nvlh_flags & NV_FLAG_PUBLIC_MASK) | inarrayf; + + ptr += sizeof(nvlhdr); + if (isbep != NULL) + *isbep = (((int)nvlhdr.nvlh_flags & NV_FLAG_BIG_ENDIAN) != 0); + *leftp -= sizeof(nvlhdr); + + return (ptr); +failed: + ERRNO_SET(EINVAL); + return (NULL); +} + +static nvlist_t * +nvlist_xunpack(const void *buf, size_t size, const int *fds, size_t nfds, + int flags) +{ + const unsigned char *ptr; + nvlist_t *nvl, *retnvl, *tmpnvl, *array; + nvpair_t *nvp; + size_t left; + bool isbe; + + PJDLOG_ASSERT((flags & ~(NV_FLAG_PUBLIC_MASK)) == 0); + + left = size; + ptr = buf; + + tmpnvl = array = NULL; + nvl = retnvl = nvlist_create(0); + if (nvl == NULL) + goto failed; + + ptr = nvlist_unpack_header(nvl, ptr, nfds, &isbe, &left); + if (ptr == NULL) + goto failed; + if (nvl->nvl_flags != flags) { + ERRNO_SET(EILSEQ); + goto failed; + } + + while (left > 0) { + ptr = nvpair_unpack(isbe, ptr, &left, &nvp); + if (ptr == NULL) + goto failed; + switch (nvpair_type(nvp)) { + case NV_TYPE_NULL: + ptr = nvpair_unpack_null(isbe, nvp, ptr, &left); + break; + case NV_TYPE_BOOL: + ptr = nvpair_unpack_bool(isbe, nvp, ptr, &left); + break; + case NV_TYPE_NUMBER: + ptr = nvpair_unpack_number(isbe, nvp, ptr, &left); + break; + case NV_TYPE_STRING: + ptr = nvpair_unpack_string(isbe, nvp, ptr, &left); + break; + case NV_TYPE_NVLIST: + ptr = nvpair_unpack_nvlist(isbe, nvp, ptr, &left, nfds, + &tmpnvl); + if (tmpnvl == NULL || ptr == NULL) + goto failed; + nvlist_set_parent(tmpnvl, nvp); + break; +#ifndef _KERNEL + case NV_TYPE_DESCRIPTOR: + ptr = nvpair_unpack_descriptor(isbe, nvp, ptr, &left, + fds, nfds); + break; + case NV_TYPE_DESCRIPTOR_ARRAY: + ptr = nvpair_unpack_descriptor_array(isbe, nvp, ptr, + &left, fds, nfds); + break; +#endif + case NV_TYPE_BINARY: + ptr = nvpair_unpack_binary(isbe, nvp, ptr, &left); + break; + case NV_TYPE_NVLIST_UP: + if (nvl->nvl_parent == NULL) + goto failed; + nvl = nvpair_nvlist(nvl->nvl_parent); + nvpair_free_structure(nvp); + continue; + case NV_TYPE_NVLIST_ARRAY_NEXT: + if (nvl->nvl_array_next == NULL) { + if (nvl->nvl_parent == NULL) + goto failed; + nvl = nvpair_nvlist(nvl->nvl_parent); + } else { + nvl = __DECONST(nvlist_t *, + nvlist_get_array_next(nvl)); + ptr = nvlist_unpack_header(nvl, ptr, nfds, + &isbe, &left); + if (ptr == NULL) + goto failed; + } + nvpair_free_structure(nvp); + continue; + case NV_TYPE_BOOL_ARRAY: + ptr = nvpair_unpack_bool_array(isbe, nvp, ptr, &left); + break; + case NV_TYPE_NUMBER_ARRAY: + ptr = nvpair_unpack_number_array(isbe, nvp, ptr, &left); + break; + case NV_TYPE_STRING_ARRAY: + ptr = nvpair_unpack_string_array(isbe, nvp, ptr, &left); + break; + case NV_TYPE_NVLIST_ARRAY: + ptr = nvpair_unpack_nvlist_array(isbe, nvp, ptr, &left, + &array); + if (ptr == NULL) + goto failed; + tmpnvl = array; + while (array != NULL) { + nvlist_set_parent(array, nvp); + array = __DECONST(nvlist_t *, + nvlist_get_array_next(array)); + } + ptr = nvlist_unpack_header(tmpnvl, ptr, nfds, &isbe, + &left); + break; + default: + PJDLOG_ABORT("Invalid type (%d).", nvpair_type(nvp)); + } + if (ptr == NULL) + goto failed; + if (!nvlist_move_nvpair(nvl, nvp)) + goto failed; + if (tmpnvl != NULL) { + nvl = tmpnvl; + tmpnvl = NULL; + } + } + + return (retnvl); +failed: + nvlist_destroy(retnvl); + return (NULL); +} + +nvlist_t * +nvlist_unpack(const void *buf, size_t size, int flags) +{ + + return (nvlist_xunpack(buf, size, NULL, 0, flags)); +} + +#ifndef _KERNEL +int +nvlist_send(int sock, const nvlist_t *nvl) +{ + size_t datasize, nfds; + int *fds; + void *data; + int64_t fdidx; + int ret; + + if (nvlist_error(nvl) != 0) { + ERRNO_SET(nvlist_error(nvl)); + return (-1); + } + + fds = nvlist_descriptors(nvl, &nfds); + if (fds == NULL) + return (-1); + + ret = -1; + data = NULL; + fdidx = 0; + + data = nvlist_xpack(nvl, &fdidx, &datasize); + if (data == NULL) + goto out; + + if (buf_send(sock, data, datasize) == -1) + goto out; + + if (nfds > 0) { + if (fd_send(sock, fds, nfds) == -1) + goto out; + } + + ret = 0; +out: + ERRNO_SAVE(); + nv_free(fds); + nv_free(data); + ERRNO_RESTORE(); + return (ret); +} + +nvlist_t * +nvlist_recv(int sock, int flags) +{ + struct nvlist_header nvlhdr; + nvlist_t *nvl, *ret; + unsigned char *buf; + size_t nfds, size, i; + int *fds; + + if (buf_recv(sock, &nvlhdr, sizeof(nvlhdr)) == -1) + return (NULL); + + if (!nvlist_check_header(&nvlhdr)) + return (NULL); + + nfds = (size_t)nvlhdr.nvlh_descriptors; + size = sizeof(nvlhdr) + (size_t)nvlhdr.nvlh_size; + + buf = nv_malloc(size); + if (buf == NULL) + return (NULL); + + memcpy(buf, &nvlhdr, sizeof(nvlhdr)); + + ret = NULL; + fds = NULL; + + if (buf_recv(sock, buf + sizeof(nvlhdr), size - sizeof(nvlhdr)) == -1) + goto out; + + if (nfds > 0) { + fds = nv_malloc(nfds * sizeof(fds[0])); + if (fds == NULL) + goto out; + if (fd_recv(sock, fds, nfds) == -1) + goto out; + } + + nvl = nvlist_xunpack(buf, size, fds, nfds, flags); + if (nvl == NULL) { + ERRNO_SAVE(); + for (i = 0; i < nfds; i++) + close(fds[i]); + ERRNO_RESTORE(); + goto out; + } + + ret = nvl; +out: + ERRNO_SAVE(); + nv_free(buf); + nv_free(fds); + ERRNO_RESTORE(); + + return (ret); +} + +nvlist_t * +nvlist_xfer(int sock, nvlist_t *nvl, int flags) +{ + + if (nvlist_send(sock, nvl) < 0) { + nvlist_destroy(nvl); + return (NULL); + } + nvlist_destroy(nvl); + return (nvlist_recv(sock, flags)); +} +#endif + +nvpair_t * +nvlist_first_nvpair(const nvlist_t *nvl) +{ + + NVLIST_ASSERT(nvl); + + return (TAILQ_FIRST(&nvl->nvl_head)); +} + +nvpair_t * +nvlist_next_nvpair(const nvlist_t *nvl, const nvpair_t *nvp) +{ + nvpair_t *retnvp; + + NVLIST_ASSERT(nvl); + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvpair_nvlist(nvp) == nvl); + + retnvp = nvpair_next(nvp); + PJDLOG_ASSERT(retnvp == NULL || nvpair_nvlist(retnvp) == nvl); + + return (retnvp); + +} + +nvpair_t * +nvlist_prev_nvpair(const nvlist_t *nvl, const nvpair_t *nvp) +{ + nvpair_t *retnvp; + + NVLIST_ASSERT(nvl); + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvpair_nvlist(nvp) == nvl); + + retnvp = nvpair_prev(nvp); + PJDLOG_ASSERT(nvpair_nvlist(retnvp) == nvl); + + return (retnvp); +} + +const char * +nvlist_next(const nvlist_t *nvl, int *typep, void **cookiep) +{ + nvpair_t *nvp; + + NVLIST_ASSERT(nvl); + + if (cookiep == NULL || *cookiep == NULL) + nvp = nvlist_first_nvpair(nvl); + else + nvp = nvlist_next_nvpair(nvl, *cookiep); + if (nvp == NULL) + return (NULL); + if (typep != NULL) + *typep = nvpair_type(nvp); + if (cookiep != NULL) + *cookiep = nvp; + return (nvpair_name(nvp)); +} + +bool +nvlist_exists(const nvlist_t *nvl, const char *name) +{ + + return (nvlist_find(nvl, NV_TYPE_NONE, name) != NULL); +} + +#define NVLIST_EXISTS(type, TYPE) \ +bool \ +nvlist_exists_##type(const nvlist_t *nvl, const char *name) \ +{ \ + \ + return (nvlist_find(nvl, NV_TYPE_##TYPE, name) != NULL); \ +} + +NVLIST_EXISTS(null, NULL) +NVLIST_EXISTS(bool, BOOL) +NVLIST_EXISTS(number, NUMBER) +NVLIST_EXISTS(string, STRING) +NVLIST_EXISTS(nvlist, NVLIST) +NVLIST_EXISTS(binary, BINARY) +NVLIST_EXISTS(bool_array, BOOL_ARRAY) +NVLIST_EXISTS(number_array, NUMBER_ARRAY) +NVLIST_EXISTS(string_array, STRING_ARRAY) +NVLIST_EXISTS(nvlist_array, NVLIST_ARRAY) +#ifndef _KERNEL +NVLIST_EXISTS(descriptor, DESCRIPTOR) +NVLIST_EXISTS(descriptor_array, DESCRIPTOR_ARRAY) +#endif + +#undef NVLIST_EXISTS + +void +nvlist_add_nvpair(nvlist_t *nvl, const nvpair_t *nvp) +{ + nvpair_t *newnvp; + + NVPAIR_ASSERT(nvp); + + if (nvlist_error(nvl) != 0) { + ERRNO_SET(nvlist_error(nvl)); + return; + } + if ((nvl->nvl_flags & NV_FLAG_NO_UNIQUE) == 0) { + if (nvlist_exists(nvl, nvpair_name(nvp))) { + nvl->nvl_error = EEXIST; + ERRNO_SET(nvlist_error(nvl)); + return; + } + } + + newnvp = nvpair_clone(nvp); + if (newnvp == NULL) { + nvl->nvl_error = ERRNO_OR_DEFAULT(ENOMEM); + ERRNO_SET(nvlist_error(nvl)); + return; + } + + nvpair_insert(&nvl->nvl_head, newnvp, nvl); +} + +void +nvlist_add_stringf(nvlist_t *nvl, const char *name, const char *valuefmt, ...) +{ + va_list valueap; + + va_start(valueap, valuefmt); + nvlist_add_stringv(nvl, name, valuefmt, valueap); + va_end(valueap); +} + +void +nvlist_add_stringv(nvlist_t *nvl, const char *name, const char *valuefmt, + va_list valueap) +{ + nvpair_t *nvp; + + if (nvlist_error(nvl) != 0) { + ERRNO_SET(nvlist_error(nvl)); + return; + } + + nvp = nvpair_create_stringv(name, valuefmt, valueap); + if (nvp == NULL) { + nvl->nvl_error = ERRNO_OR_DEFAULT(ENOMEM); + ERRNO_SET(nvl->nvl_error); + } else { + (void)nvlist_move_nvpair(nvl, nvp); + } +} + +void +nvlist_add_null(nvlist_t *nvl, const char *name) +{ + nvpair_t *nvp; + + if (nvlist_error(nvl) != 0) { + ERRNO_SET(nvlist_error(nvl)); + return; + } + + nvp = nvpair_create_null(name); + if (nvp == NULL) { + nvl->nvl_error = ERRNO_OR_DEFAULT(ENOMEM); + ERRNO_SET(nvl->nvl_error); + } else { + (void)nvlist_move_nvpair(nvl, nvp); + } +} + +void +nvlist_add_binary(nvlist_t *nvl, const char *name, const void *value, + size_t size) +{ + nvpair_t *nvp; + + if (nvlist_error(nvl) != 0) { + ERRNO_SET(nvlist_error(nvl)); + return; + } + + nvp = nvpair_create_binary(name, value, size); + if (nvp == NULL) { + nvl->nvl_error = ERRNO_OR_DEFAULT(ENOMEM); + ERRNO_SET(nvl->nvl_error); + } else { + (void)nvlist_move_nvpair(nvl, nvp); + } +} + + +#define NVLIST_ADD(vtype, type) \ +void \ +nvlist_add_##type(nvlist_t *nvl, const char *name, vtype value) \ +{ \ + nvpair_t *nvp; \ + \ + if (nvlist_error(nvl) != 0) { \ + ERRNO_SET(nvlist_error(nvl)); \ + return; \ + } \ + \ + nvp = nvpair_create_##type(name, value); \ + if (nvp == NULL) { \ + nvl->nvl_error = ERRNO_OR_DEFAULT(ENOMEM); \ + ERRNO_SET(nvl->nvl_error); \ + } else { \ + (void)nvlist_move_nvpair(nvl, nvp); \ + } \ +} + +NVLIST_ADD(bool, bool) +NVLIST_ADD(uint64_t, number) +NVLIST_ADD(const char *, string) +NVLIST_ADD(const nvlist_t *, nvlist) +#ifndef _KERNEL +NVLIST_ADD(int, descriptor); +#endif + +#undef NVLIST_ADD + +#define NVLIST_ADD_ARRAY(vtype, type) \ +void \ +nvlist_add_##type##_array(nvlist_t *nvl, const char *name, vtype value, \ + size_t nitems) \ +{ \ + nvpair_t *nvp; \ + \ + if (nvlist_error(nvl) != 0) { \ + ERRNO_SET(nvlist_error(nvl)); \ + return; \ + } \ + \ + nvp = nvpair_create_##type##_array(name, value, nitems); \ + if (nvp == NULL) { \ + nvl->nvl_error = ERRNO_OR_DEFAULT(ENOMEM); \ + ERRNO_SET(nvl->nvl_error); \ + } else { \ + (void)nvlist_move_nvpair(nvl, nvp); \ + } \ +} + +NVLIST_ADD_ARRAY(const bool *, bool) +NVLIST_ADD_ARRAY(const uint64_t *, number) +NVLIST_ADD_ARRAY(const char * const *, string) +NVLIST_ADD_ARRAY(const nvlist_t * const *, nvlist) +#ifndef _KERNEL +NVLIST_ADD_ARRAY(const int *, descriptor) +#endif + +#undef NVLIST_ADD_ARRAY + +bool +nvlist_move_nvpair(nvlist_t *nvl, nvpair_t *nvp) +{ + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvpair_nvlist(nvp) == NULL); + + if (nvlist_error(nvl) != 0) { + nvpair_free(nvp); + ERRNO_SET(nvlist_error(nvl)); + return (false); + } + if ((nvl->nvl_flags & NV_FLAG_NO_UNIQUE) == 0) { + if (nvlist_exists(nvl, nvpair_name(nvp))) { + nvpair_free(nvp); + nvl->nvl_error = EEXIST; + ERRNO_SET(nvl->nvl_error); + return (false); + } + } + + nvpair_insert(&nvl->nvl_head, nvp, nvl); + return (true); +} + +void +nvlist_move_string(nvlist_t *nvl, const char *name, char *value) +{ + nvpair_t *nvp; + + if (nvlist_error(nvl) != 0) { + nv_free(value); + ERRNO_SET(nvlist_error(nvl)); + return; + } + + nvp = nvpair_move_string(name, value); + if (nvp == NULL) { + nvl->nvl_error = ERRNO_OR_DEFAULT(ENOMEM); + ERRNO_SET(nvl->nvl_error); + } else { + (void)nvlist_move_nvpair(nvl, nvp); + } +} + +void +nvlist_move_nvlist(nvlist_t *nvl, const char *name, nvlist_t *value) +{ + nvpair_t *nvp; + + if (nvlist_error(nvl) != 0) { + if (value != NULL && nvlist_get_nvpair_parent(value) != NULL) + nvlist_destroy(value); + ERRNO_SET(nvlist_error(nvl)); + return; + } + + nvp = nvpair_move_nvlist(name, value); + if (nvp == NULL) { + nvl->nvl_error = ERRNO_OR_DEFAULT(ENOMEM); + ERRNO_SET(nvl->nvl_error); + } else { + (void)nvlist_move_nvpair(nvl, nvp); + } +} + +#ifndef _KERNEL +void +nvlist_move_descriptor(nvlist_t *nvl, const char *name, int value) +{ + nvpair_t *nvp; + + if (nvlist_error(nvl) != 0) { + close(value); + ERRNO_SET(nvlist_error(nvl)); + return; + } + + nvp = nvpair_move_descriptor(name, value); + if (nvp == NULL) { + nvl->nvl_error = ERRNO_OR_DEFAULT(ENOMEM); + ERRNO_SET(nvl->nvl_error); + } else { + (void)nvlist_move_nvpair(nvl, nvp); + } +} +#endif + +void +nvlist_move_binary(nvlist_t *nvl, const char *name, void *value, size_t size) +{ + nvpair_t *nvp; + + if (nvlist_error(nvl) != 0) { + nv_free(value); + ERRNO_SET(nvlist_error(nvl)); + return; + } + + nvp = nvpair_move_binary(name, value, size); + if (nvp == NULL) { + nvl->nvl_error = ERRNO_OR_DEFAULT(ENOMEM); + ERRNO_SET(nvl->nvl_error); + } else { + (void)nvlist_move_nvpair(nvl, nvp); + } +} + +void +nvlist_move_bool_array(nvlist_t *nvl, const char *name, bool *value, + size_t nitems) +{ + nvpair_t *nvp; + + if (nvlist_error(nvl) != 0) { + nv_free(value); + ERRNO_SET(nvlist_error(nvl)); + return; + } + + nvp = nvpair_move_bool_array(name, value, nitems); + if (nvp == NULL) { + nvl->nvl_error = ERRNO_OR_DEFAULT(ENOMEM); + ERRNO_SET(nvl->nvl_error); + } else { + (void)nvlist_move_nvpair(nvl, nvp); + } +} + +void +nvlist_move_string_array(nvlist_t *nvl, const char *name, char **value, + size_t nitems) +{ + nvpair_t *nvp; + size_t i; + + if (nvlist_error(nvl) != 0) { + if (value != NULL) { + for (i = 0; i < nitems; i++) + nv_free(value[i]); + nv_free(value); + } + ERRNO_SET(nvlist_error(nvl)); + return; + } + + nvp = nvpair_move_string_array(name, value, nitems); + if (nvp == NULL) { + nvl->nvl_error = ERRNO_OR_DEFAULT(ENOMEM); + ERRNO_SET(nvl->nvl_error); + } else { + (void)nvlist_move_nvpair(nvl, nvp); + } +} + +void +nvlist_move_nvlist_array(nvlist_t *nvl, const char *name, nvlist_t **value, + size_t nitems) +{ + nvpair_t *nvp; + size_t i; + + if (nvlist_error(nvl) != 0) { + if (value != NULL) { + for (i = 0; i < nitems; i++) { + if (nvlist_get_pararr(value[i], NULL) == NULL) + nvlist_destroy(value[i]); + } + } + nv_free(value); + ERRNO_SET(nvlist_error(nvl)); + return; + } + + nvp = nvpair_move_nvlist_array(name, value, nitems); + if (nvp == NULL) { + nvl->nvl_error = ERRNO_OR_DEFAULT(ENOMEM); + ERRNO_SET(nvl->nvl_error); + } else { + (void)nvlist_move_nvpair(nvl, nvp); + } +} + +void +nvlist_move_number_array(nvlist_t *nvl, const char *name, uint64_t *value, + size_t nitems) +{ + nvpair_t *nvp; + + if (nvlist_error(nvl) != 0) { + nv_free(value); + ERRNO_SET(nvlist_error(nvl)); + return; + } + + nvp = nvpair_move_number_array(name, value, nitems); + if (nvp == NULL) { + nvl->nvl_error = ERRNO_OR_DEFAULT(ENOMEM); + ERRNO_SET(nvl->nvl_error); + } else { + (void)nvlist_move_nvpair(nvl, nvp); + } +} + +#ifndef _KERNEL +void +nvlist_move_descriptor_array(nvlist_t *nvl, const char *name, int *value, + size_t nitems) +{ + nvpair_t *nvp; + size_t i; + + if (nvlist_error(nvl) != 0) { + if (value != 0) { + for (i = 0; i < nitems; i++) + close(value[i]); + nv_free(value); + } + + ERRNO_SET(nvlist_error(nvl)); + return; + } + + nvp = nvpair_move_descriptor_array(name, value, nitems); + if (nvp == NULL) { + nvl->nvl_error = ERRNO_OR_DEFAULT(ENOMEM); + ERRNO_SET(nvl->nvl_error); + } else { + (void)nvlist_move_nvpair(nvl, nvp); + } +} +#endif + +const nvpair_t * +nvlist_get_nvpair(const nvlist_t *nvl, const char *name) +{ + + return (nvlist_find(nvl, NV_TYPE_NONE, name)); +} + +#define NVLIST_GET(ftype, type, TYPE) \ +ftype \ +nvlist_get_##type(const nvlist_t *nvl, const char *name) \ +{ \ + const nvpair_t *nvp; \ + \ + nvp = nvlist_find(nvl, NV_TYPE_##TYPE, name); \ + if (nvp == NULL) \ + nvlist_report_missing(NV_TYPE_##TYPE, name); \ + return (nvpair_get_##type(nvp)); \ +} + +NVLIST_GET(bool, bool, BOOL) +NVLIST_GET(uint64_t, number, NUMBER) +NVLIST_GET(const char *, string, STRING) +NVLIST_GET(const nvlist_t *, nvlist, NVLIST) +#ifndef _KERNEL +NVLIST_GET(int, descriptor, DESCRIPTOR) +#endif + +#undef NVLIST_GET + +const void * +nvlist_get_binary(const nvlist_t *nvl, const char *name, size_t *sizep) +{ + nvpair_t *nvp; + + nvp = nvlist_find(nvl, NV_TYPE_BINARY, name); + if (nvp == NULL) + nvlist_report_missing(NV_TYPE_BINARY, name); + + return (nvpair_get_binary(nvp, sizep)); +} + +#define NVLIST_GET_ARRAY(ftype, type, TYPE) \ +ftype \ +nvlist_get_##type##_array(const nvlist_t *nvl, const char *name, \ + size_t *nitems) \ +{ \ + const nvpair_t *nvp; \ + \ + nvp = nvlist_find(nvl, NV_TYPE_##TYPE##_ARRAY, name); \ + if (nvp == NULL) \ + nvlist_report_missing(NV_TYPE_##TYPE##_ARRAY, name); \ + return (nvpair_get_##type##_array(nvp, nitems)); \ +} + +NVLIST_GET_ARRAY(const bool *, bool, BOOL) +NVLIST_GET_ARRAY(const uint64_t *, number, NUMBER) +NVLIST_GET_ARRAY(const char * const *, string, STRING) +NVLIST_GET_ARRAY(const nvlist_t * const *, nvlist, NVLIST) +#ifndef _KERNEL +NVLIST_GET_ARRAY(const int *, descriptor, DESCRIPTOR) +#endif + +#undef NVLIST_GET_ARRAY + +#define NVLIST_TAKE(ftype, type, TYPE) \ +ftype \ +nvlist_take_##type(nvlist_t *nvl, const char *name) \ +{ \ + nvpair_t *nvp; \ + ftype value; \ + \ + nvp = nvlist_find(nvl, NV_TYPE_##TYPE, name); \ + if (nvp == NULL) \ + nvlist_report_missing(NV_TYPE_##TYPE, name); \ + value = (ftype)(intptr_t)nvpair_get_##type(nvp); \ + nvlist_remove_nvpair(nvl, nvp); \ + nvpair_free_structure(nvp); \ + return (value); \ +} + +NVLIST_TAKE(bool, bool, BOOL) +NVLIST_TAKE(uint64_t, number, NUMBER) +NVLIST_TAKE(char *, string, STRING) +NVLIST_TAKE(nvlist_t *, nvlist, NVLIST) +#ifndef _KERNEL +NVLIST_TAKE(int, descriptor, DESCRIPTOR) +#endif + +#undef NVLIST_TAKE + +void * +nvlist_take_binary(nvlist_t *nvl, const char *name, size_t *sizep) +{ + nvpair_t *nvp; + void *value; + + nvp = nvlist_find(nvl, NV_TYPE_BINARY, name); + if (nvp == NULL) + nvlist_report_missing(NV_TYPE_BINARY, name); + + value = (void *)(intptr_t)nvpair_get_binary(nvp, sizep); + nvlist_remove_nvpair(nvl, nvp); + nvpair_free_structure(nvp); + return (value); +} + +#define NVLIST_TAKE_ARRAY(ftype, type, TYPE) \ +ftype \ +nvlist_take_##type##_array(nvlist_t *nvl, const char *name, \ + size_t *nitems) \ +{ \ + nvpair_t *nvp; \ + ftype value; \ + \ + nvp = nvlist_find(nvl, NV_TYPE_##TYPE##_ARRAY, name); \ + if (nvp == NULL) \ + nvlist_report_missing(NV_TYPE_##TYPE##_ARRAY, name); \ + value = (ftype)(intptr_t)nvpair_get_##type##_array(nvp, nitems);\ + nvlist_remove_nvpair(nvl, nvp); \ + nvpair_free_structure(nvp); \ + return (value); \ +} + +NVLIST_TAKE_ARRAY(bool *, bool, BOOL) +NVLIST_TAKE_ARRAY(uint64_t *, number, NUMBER) +NVLIST_TAKE_ARRAY(char **, string, STRING) +NVLIST_TAKE_ARRAY(nvlist_t **, nvlist, NVLIST) +#ifndef _KERNEL +NVLIST_TAKE_ARRAY(int *, descriptor, DESCRIPTOR) +#endif + +void +nvlist_remove_nvpair(nvlist_t *nvl, nvpair_t *nvp) +{ + + NVLIST_ASSERT(nvl); + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvpair_nvlist(nvp) == nvl); + + nvpair_remove(&nvl->nvl_head, nvp, nvl); +} + +void +nvlist_free(nvlist_t *nvl, const char *name) +{ + + nvlist_free_type(nvl, name, NV_TYPE_NONE); +} + +#define NVLIST_FREE(type, TYPE) \ +void \ +nvlist_free_##type(nvlist_t *nvl, const char *name) \ +{ \ + \ + nvlist_free_type(nvl, name, NV_TYPE_##TYPE); \ +} + +NVLIST_FREE(null, NULL) +NVLIST_FREE(bool, BOOL) +NVLIST_FREE(number, NUMBER) +NVLIST_FREE(string, STRING) +NVLIST_FREE(nvlist, NVLIST) +NVLIST_FREE(binary, BINARY) +NVLIST_FREE(bool_array, BOOL_ARRAY) +NVLIST_FREE(number_array, NUMBER_ARRAY) +NVLIST_FREE(string_array, STRING_ARRAY) +NVLIST_FREE(nvlist_array, NVLIST_ARRAY) +#ifndef _KERNEL +NVLIST_FREE(descriptor, DESCRIPTOR) +NVLIST_FREE(descriptor_array, DESCRIPTOR_ARRAY) +#endif + +#undef NVLIST_FREE + +void +nvlist_free_nvpair(nvlist_t *nvl, nvpair_t *nvp) +{ + + NVLIST_ASSERT(nvl); + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvpair_nvlist(nvp) == nvl); + + nvlist_remove_nvpair(nvl, nvp); + nvpair_free(nvp); +} + Index: sys/contrib/libnv/nvlist_impl.h =================================================================== --- /dev/null +++ sys/contrib/libnv/nvlist_impl.h @@ -0,0 +1,46 @@ +/*- + * Copyright (c) 2013 The FreeBSD Foundation + * Copyright (c) 2013-2015 Mariusz Zaborski + * All rights reserved. + * + * This software was developed by Pawel Jakub Dawidek under sponsorship from + * the FreeBSD Foundation. + * + * 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 AUTHORS 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 AUTHORS 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: head/sys/contrib/libnv/nvlist_impl.h 286796 2015-08-15 06:34:49Z oshogbo $ + */ + +#ifndef _NVLIST_IMPL_H_ +#define _NVLIST_IMPL_H_ + +#include + +#ifndef _KERNEL +#include +#endif + +nvpair_t *nvlist_get_nvpair_parent(const nvlist_t *nvl); +const unsigned char *nvlist_unpack_header(nvlist_t *nvl, + const unsigned char *ptr, size_t nfds, bool *isbep, size_t *leftp); + +#endif /* !_NVLIST_IMPL_H_ */ Index: sys/contrib/libnv/nvpair.c =================================================================== --- /dev/null +++ sys/contrib/libnv/nvpair.c @@ -0,0 +1,1997 @@ +/*- + * Copyright (c) 2009-2013 The FreeBSD Foundation + * Copyright (c) 2013-2015 Mariusz Zaborski + * All rights reserved. + * + * This software was developed by Pawel Jakub Dawidek under sponsorship from + * the FreeBSD Foundation. + * + * 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 AUTHORS 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 AUTHORS 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 +__FBSDID("$FreeBSD: head/sys/contrib/libnv/nvpair.c 286796 2015-08-15 06:34:49Z oshogbo $"); + +#include +#include +#include + +#ifdef _KERNEL + +#include +#include +#include +#include + +#include + +#else +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common_impl.h" +#endif + +#ifdef HAVE_PJDLOG +#include +#endif + +#include + +#include "nv_impl.h" +#include "nvlist_impl.h" +#include "nvpair_impl.h" + +#ifndef HAVE_PJDLOG +#ifdef _KERNEL +#define PJDLOG_ASSERT(...) MPASS(__VA_ARGS__) +#define PJDLOG_RASSERT(expr, ...) KASSERT(expr, (__VA_ARGS__)) +#define PJDLOG_ABORT(...) panic(__VA_ARGS__) +#else +#include +#define PJDLOG_ASSERT(...) assert(__VA_ARGS__) +#define PJDLOG_RASSERT(expr, ...) assert(expr) +#define PJDLOG_ABORT(...) abort() +#endif +#endif + +#define NVPAIR_MAGIC 0x6e7670 /* "nvp" */ +struct nvpair { + int nvp_magic; + char *nvp_name; + int nvp_type; + uint64_t nvp_data; + size_t nvp_datasize; + size_t nvp_nitems; /* Used only for array types. */ + nvlist_t *nvp_list; + TAILQ_ENTRY(nvpair) nvp_next; +}; + +#define NVPAIR_ASSERT(nvp) do { \ + PJDLOG_ASSERT((nvp) != NULL); \ + PJDLOG_ASSERT((nvp)->nvp_magic == NVPAIR_MAGIC); \ +} while (0) + +struct nvpair_header { + uint8_t nvph_type; + uint16_t nvph_namesize; + uint64_t nvph_datasize; + uint64_t nvph_nitems; +} __packed; + + +void +nvpair_assert(const nvpair_t *nvp) +{ + + NVPAIR_ASSERT(nvp); +} + +static nvpair_t * +nvpair_allocv(const char *name, int type, uint64_t data, size_t datasize, + size_t nitems) +{ + nvpair_t *nvp; + size_t namelen; + + PJDLOG_ASSERT(type >= NV_TYPE_FIRST && type <= NV_TYPE_LAST); + + namelen = strlen(name); + if (namelen >= NV_NAME_MAX) { + ERRNO_SET(ENAMETOOLONG); + return (NULL); + } + + nvp = nv_calloc(1, sizeof(*nvp) + namelen + 1); + if (nvp != NULL) { + nvp->nvp_name = (char *)(nvp + 1); + memcpy(nvp->nvp_name, name, namelen); + nvp->nvp_name[namelen] = '\0'; + nvp->nvp_type = type; + nvp->nvp_data = data; + nvp->nvp_datasize = datasize; + nvp->nvp_nitems = nitems; + nvp->nvp_magic = NVPAIR_MAGIC; + } + + return (nvp); +} + +nvlist_t * +nvpair_nvlist(const nvpair_t *nvp) +{ + + NVPAIR_ASSERT(nvp); + + return (nvp->nvp_list); +} + +nvpair_t * +nvpair_next(const nvpair_t *nvp) +{ + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_list != NULL); + + return (TAILQ_NEXT(nvp, nvp_next)); +} + +nvpair_t * +nvpair_prev(const nvpair_t *nvp) +{ + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_list != NULL); + + return (TAILQ_PREV(nvp, nvl_head, nvp_next)); +} + +void +nvpair_insert(struct nvl_head *head, nvpair_t *nvp, nvlist_t *nvl) +{ + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_list == NULL); + PJDLOG_ASSERT((nvlist_flags(nvl) & NV_FLAG_NO_UNIQUE) != 0 || + !nvlist_exists(nvl, nvpair_name(nvp))); + + TAILQ_INSERT_TAIL(head, nvp, nvp_next); + nvp->nvp_list = nvl; +} + +static void +nvpair_remove_nvlist(nvpair_t *nvp) +{ + nvlist_t *nvl; + + /* XXX: DECONST is bad, mkay? */ + nvl = __DECONST(nvlist_t *, nvpair_get_nvlist(nvp)); + PJDLOG_ASSERT(nvl != NULL); + nvlist_set_parent(nvl, NULL); +} + +static void +nvpair_remove_nvlist_array(nvpair_t *nvp) +{ + nvlist_t **nvlarray; + size_t count, i; + + /* XXX: DECONST is bad, mkay? */ + nvlarray = __DECONST(nvlist_t **, + nvpair_get_nvlist_array(nvp, &count)); + for (i = 0; i < count; i++) + nvlist_set_array_next(nvlarray[i], NULL); +} + +void +nvpair_remove(struct nvl_head *head, nvpair_t *nvp, const nvlist_t *nvl) +{ + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_list == nvl); + + if (nvpair_type(nvp) == NV_TYPE_NVLIST) + nvpair_remove_nvlist(nvp); + else if (nvpair_type(nvp) == NV_TYPE_NVLIST_ARRAY) + nvpair_remove_nvlist_array(nvp); + + TAILQ_REMOVE(head, nvp, nvp_next); + nvp->nvp_list = NULL; +} + +nvpair_t * +nvpair_clone(const nvpair_t *nvp) +{ + nvpair_t *newnvp; + const char *name; + const void *data; + size_t datasize; + + NVPAIR_ASSERT(nvp); + + name = nvpair_name(nvp); + + switch (nvpair_type(nvp)) { + case NV_TYPE_NULL: + newnvp = nvpair_create_null(name); + break; + case NV_TYPE_BOOL: + newnvp = nvpair_create_bool(name, nvpair_get_bool(nvp)); + break; + case NV_TYPE_NUMBER: + newnvp = nvpair_create_number(name, nvpair_get_number(nvp)); + break; + case NV_TYPE_STRING: + newnvp = nvpair_create_string(name, nvpair_get_string(nvp)); + break; + case NV_TYPE_NVLIST: + newnvp = nvpair_create_nvlist(name, nvpair_get_nvlist(nvp)); + break; + case NV_TYPE_BINARY: + data = nvpair_get_binary(nvp, &datasize); + newnvp = nvpair_create_binary(name, data, datasize); + break; + case NV_TYPE_BOOL_ARRAY: + data = nvpair_get_bool_array(nvp, &datasize); + newnvp = nvpair_create_bool_array(name, data, datasize); + break; + case NV_TYPE_NUMBER_ARRAY: + data = nvpair_get_number_array(nvp, &datasize); + newnvp = nvpair_create_number_array(name, data, datasize); + break; + case NV_TYPE_STRING_ARRAY: + data = nvpair_get_string_array(nvp, &datasize); + newnvp = nvpair_create_string_array(name, data, datasize); + break; + case NV_TYPE_NVLIST_ARRAY: + data = nvpair_get_nvlist_array(nvp, &datasize); + newnvp = nvpair_create_nvlist_array(name, data, datasize); + break; +#ifndef _KERNEL + case NV_TYPE_DESCRIPTOR: + newnvp = nvpair_create_descriptor(name, + nvpair_get_descriptor(nvp)); + break; + case NV_TYPE_DESCRIPTOR_ARRAY: + data = nvpair_get_descriptor_array(nvp, &datasize); + newnvp = nvpair_create_descriptor_array(name, data, datasize); + break; +#endif + default: + PJDLOG_ABORT("Unknown type: %d.", nvpair_type(nvp)); + } + + return (newnvp); +} + +size_t +nvpair_header_size(void) +{ + + return (sizeof(struct nvpair_header)); +} + +size_t +nvpair_size(const nvpair_t *nvp) +{ + + NVPAIR_ASSERT(nvp); + + return (nvp->nvp_datasize); +} + +unsigned char * +nvpair_pack_header(const nvpair_t *nvp, unsigned char *ptr, size_t *leftp) +{ + struct nvpair_header nvphdr; + size_t namesize; + + NVPAIR_ASSERT(nvp); + + nvphdr.nvph_type = nvp->nvp_type; + namesize = strlen(nvp->nvp_name) + 1; + PJDLOG_ASSERT(namesize > 0 && namesize <= UINT16_MAX); + nvphdr.nvph_namesize = namesize; + nvphdr.nvph_datasize = nvp->nvp_datasize; + nvphdr.nvph_nitems = nvp->nvp_nitems; + PJDLOG_ASSERT(*leftp >= sizeof(nvphdr)); + memcpy(ptr, &nvphdr, sizeof(nvphdr)); + ptr += sizeof(nvphdr); + *leftp -= sizeof(nvphdr); + + PJDLOG_ASSERT(*leftp >= namesize); + memcpy(ptr, nvp->nvp_name, namesize); + ptr += namesize; + *leftp -= namesize; + + return (ptr); +} + +unsigned char * +nvpair_pack_null(const nvpair_t *nvp, unsigned char *ptr, + size_t *leftp __unused) +{ + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_NULL); + + return (ptr); +} + +unsigned char * +nvpair_pack_bool(const nvpair_t *nvp, unsigned char *ptr, size_t *leftp) +{ + uint8_t value; + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_BOOL); + + value = (uint8_t)nvp->nvp_data; + + PJDLOG_ASSERT(*leftp >= sizeof(value)); + memcpy(ptr, &value, sizeof(value)); + ptr += sizeof(value); + *leftp -= sizeof(value); + + return (ptr); +} + +unsigned char * +nvpair_pack_number(const nvpair_t *nvp, unsigned char *ptr, size_t *leftp) +{ + uint64_t value; + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_NUMBER); + + value = (uint64_t)nvp->nvp_data; + + PJDLOG_ASSERT(*leftp >= sizeof(value)); + memcpy(ptr, &value, sizeof(value)); + ptr += sizeof(value); + *leftp -= sizeof(value); + + return (ptr); +} + +unsigned char * +nvpair_pack_string(const nvpair_t *nvp, unsigned char *ptr, size_t *leftp) +{ + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_STRING); + + PJDLOG_ASSERT(*leftp >= nvp->nvp_datasize); + memcpy(ptr, (const void *)(intptr_t)nvp->nvp_data, nvp->nvp_datasize); + ptr += nvp->nvp_datasize; + *leftp -= nvp->nvp_datasize; + + return (ptr); +} + +unsigned char * +nvpair_pack_nvlist_up(unsigned char *ptr, size_t *leftp) +{ + struct nvpair_header nvphdr; + size_t namesize; + const char *name = ""; + + namesize = 1; + nvphdr.nvph_type = NV_TYPE_NVLIST_UP; + nvphdr.nvph_namesize = namesize; + nvphdr.nvph_datasize = 0; + nvphdr.nvph_nitems = 0; + PJDLOG_ASSERT(*leftp >= sizeof(nvphdr)); + memcpy(ptr, &nvphdr, sizeof(nvphdr)); + ptr += sizeof(nvphdr); + *leftp -= sizeof(nvphdr); + + PJDLOG_ASSERT(*leftp >= namesize); + memcpy(ptr, name, namesize); + ptr += namesize; + *leftp -= namesize; + + return (ptr); +} + +unsigned char * +nvpair_pack_nvlist_array_next(unsigned char *ptr, size_t *leftp) +{ + struct nvpair_header nvphdr; + size_t namesize; + const char *name = ""; + + namesize = 1; + nvphdr.nvph_type = NV_TYPE_NVLIST_ARRAY_NEXT; + nvphdr.nvph_namesize = namesize; + nvphdr.nvph_datasize = 0; + nvphdr.nvph_nitems = 0; + PJDLOG_ASSERT(*leftp >= sizeof(nvphdr)); + memcpy(ptr, &nvphdr, sizeof(nvphdr)); + ptr += sizeof(nvphdr); + *leftp -= sizeof(nvphdr); + + PJDLOG_ASSERT(*leftp >= namesize); + memcpy(ptr, name, namesize); + ptr += namesize; + *leftp -= namesize; + + return (ptr); +} + +#ifndef _KERNEL +unsigned char * +nvpair_pack_descriptor(const nvpair_t *nvp, unsigned char *ptr, int64_t *fdidxp, + size_t *leftp) +{ + int64_t value; + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_DESCRIPTOR); + + value = (int64_t)nvp->nvp_data; + if (value != -1) { + /* + * If there is a real descriptor here, we change its number + * to position in the array of descriptors send via control + * message. + */ + PJDLOG_ASSERT(fdidxp != NULL); + + value = *fdidxp; + (*fdidxp)++; + } + + PJDLOG_ASSERT(*leftp >= sizeof(value)); + memcpy(ptr, &value, sizeof(value)); + ptr += sizeof(value); + *leftp -= sizeof(value); + + return (ptr); +} +#endif + +unsigned char * +nvpair_pack_binary(const nvpair_t *nvp, unsigned char *ptr, size_t *leftp) +{ + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_BINARY); + + PJDLOG_ASSERT(*leftp >= nvp->nvp_datasize); + memcpy(ptr, (const void *)(intptr_t)nvp->nvp_data, nvp->nvp_datasize); + ptr += nvp->nvp_datasize; + *leftp -= nvp->nvp_datasize; + + return (ptr); +} + +unsigned char * +nvpair_pack_bool_array(const nvpair_t *nvp, unsigned char *ptr, size_t *leftp) +{ + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_BOOL_ARRAY); + PJDLOG_ASSERT(*leftp >= nvp->nvp_datasize); + + memcpy(ptr, (const void *)(intptr_t)nvp->nvp_data, nvp->nvp_datasize); + ptr += nvp->nvp_datasize; + *leftp -= nvp->nvp_datasize; + + return (ptr); +} + +unsigned char * +nvpair_pack_number_array(const nvpair_t *nvp, unsigned char *ptr, size_t *leftp) +{ + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_NUMBER_ARRAY); + PJDLOG_ASSERT(*leftp >= nvp->nvp_datasize); + + memcpy(ptr, (const void *)(intptr_t)nvp->nvp_data, nvp->nvp_datasize); + ptr += nvp->nvp_datasize; + *leftp -= nvp->nvp_datasize; + + return (ptr); +} + +unsigned char * +nvpair_pack_string_array(const nvpair_t *nvp, unsigned char *ptr, size_t *leftp) +{ + unsigned int ii; + size_t size, len; + const char * const *array; + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_STRING_ARRAY); + PJDLOG_ASSERT(*leftp >= nvp->nvp_datasize); + + size = 0; + array = nvpair_get_string_array(nvp, NULL); + PJDLOG_ASSERT(array != NULL); + + for (ii = 0; ii < nvp->nvp_nitems; ii++) { + len = strlen(array[ii]) + 1; + PJDLOG_ASSERT(*leftp >= len); + + memcpy(ptr, (const void *)array[ii], len); + size += len; + ptr += len; + *leftp -= len; + } + + PJDLOG_ASSERT(size == nvp->nvp_datasize); + + return (ptr); +} + +#ifndef _KERNEL +unsigned char * +nvpair_pack_descriptor_array(const nvpair_t *nvp, unsigned char *ptr, + int64_t *fdidxp, size_t *leftp) +{ + int64_t value; + const int *array; + unsigned int ii; + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_DESCRIPTOR_ARRAY); + PJDLOG_ASSERT(*leftp >= nvp->nvp_datasize); + + array = nvpair_get_descriptor_array(nvp, NULL); + PJDLOG_ASSERT(array != NULL); + + for (ii = 0; ii < nvp->nvp_nitems; ii++) { + PJDLOG_ASSERT(*leftp >= sizeof(value)); + + value = array[ii]; + if (value != -1) { + /* + * If there is a real descriptor here, we change its + * number to position in the array of descriptors send + * via control message. + */ + PJDLOG_ASSERT(fdidxp != NULL); + + value = *fdidxp; + (*fdidxp)++; + } + memcpy(ptr, &value, sizeof(value)); + ptr += sizeof(value); + *leftp -= sizeof(value); + } + + return (ptr); +} +#endif + +void +nvpair_init_datasize(nvpair_t *nvp) +{ + + NVPAIR_ASSERT(nvp); + + if (nvp->nvp_type == NV_TYPE_NVLIST) { + if (nvp->nvp_data == 0) { + nvp->nvp_datasize = 0; + } else { + nvp->nvp_datasize = + nvlist_size((const nvlist_t *)(intptr_t)nvp->nvp_data); + } + } +} + +const unsigned char * +nvpair_unpack_header(bool isbe, nvpair_t *nvp, const unsigned char *ptr, + size_t *leftp) +{ + struct nvpair_header nvphdr; + + if (*leftp < sizeof(nvphdr)) + goto failed; + + memcpy(&nvphdr, ptr, sizeof(nvphdr)); + ptr += sizeof(nvphdr); + *leftp -= sizeof(nvphdr); + +#if NV_TYPE_FIRST > 0 + if (nvphdr.nvph_type < NV_TYPE_FIRST) + goto failed; +#endif + if (nvphdr.nvph_type > NV_TYPE_LAST && + nvphdr.nvph_type != NV_TYPE_NVLIST_UP && + nvphdr.nvph_type != NV_TYPE_NVLIST_ARRAY_NEXT) { + goto failed; + } + +#if BYTE_ORDER == BIG_ENDIAN + if (!isbe) { + nvphdr.nvph_namesize = le16toh(nvphdr.nvph_namesize); + nvphdr.nvph_datasize = le64toh(nvphdr.nvph_datasize); + } +#else + if (isbe) { + nvphdr.nvph_namesize = be16toh(nvphdr.nvph_namesize); + nvphdr.nvph_datasize = be64toh(nvphdr.nvph_datasize); + } +#endif + + if (nvphdr.nvph_namesize > NV_NAME_MAX) + goto failed; + if (*leftp < nvphdr.nvph_namesize) + goto failed; + if (nvphdr.nvph_namesize < 1) + goto failed; + if (strnlen((const char *)ptr, nvphdr.nvph_namesize) != + (size_t)(nvphdr.nvph_namesize - 1)) { + goto failed; + } + + memcpy(nvp->nvp_name, ptr, nvphdr.nvph_namesize); + ptr += nvphdr.nvph_namesize; + *leftp -= nvphdr.nvph_namesize; + + if (*leftp < nvphdr.nvph_datasize) + goto failed; + + nvp->nvp_type = nvphdr.nvph_type; + nvp->nvp_data = 0; + nvp->nvp_datasize = nvphdr.nvph_datasize; + nvp->nvp_nitems = nvphdr.nvph_nitems; + + return (ptr); +failed: + ERRNO_SET(EINVAL); + return (NULL); +} + +const unsigned char * +nvpair_unpack_null(bool isbe __unused, nvpair_t *nvp, const unsigned char *ptr, + size_t *leftp __unused) +{ + + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_NULL); + + if (nvp->nvp_datasize != 0) { + ERRNO_SET(EINVAL); + return (NULL); + } + + return (ptr); +} + +const unsigned char * +nvpair_unpack_bool(bool isbe __unused, nvpair_t *nvp, const unsigned char *ptr, + size_t *leftp) +{ + uint8_t value; + + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_BOOL); + + if (nvp->nvp_datasize != sizeof(value)) { + ERRNO_SET(EINVAL); + return (NULL); + } + if (*leftp < sizeof(value)) { + ERRNO_SET(EINVAL); + return (NULL); + } + + memcpy(&value, ptr, sizeof(value)); + ptr += sizeof(value); + *leftp -= sizeof(value); + + if (value != 0 && value != 1) { + ERRNO_SET(EINVAL); + return (NULL); + } + + nvp->nvp_data = (uint64_t)value; + + return (ptr); +} + +const unsigned char * +nvpair_unpack_number(bool isbe, nvpair_t *nvp, const unsigned char *ptr, + size_t *leftp) +{ + + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_NUMBER); + + if (nvp->nvp_datasize != sizeof(uint64_t)) { + ERRNO_SET(EINVAL); + return (NULL); + } + if (*leftp < sizeof(uint64_t)) { + ERRNO_SET(EINVAL); + return (NULL); + } + + if (isbe) + nvp->nvp_data = be64dec(ptr); + else + nvp->nvp_data = le64dec(ptr); + + ptr += sizeof(uint64_t); + *leftp -= sizeof(uint64_t); + + return (ptr); +} + +const unsigned char * +nvpair_unpack_string(bool isbe __unused, nvpair_t *nvp, + const unsigned char *ptr, size_t *leftp) +{ + + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_STRING); + + if (*leftp < nvp->nvp_datasize || nvp->nvp_datasize == 0) { + ERRNO_SET(EINVAL); + return (NULL); + } + + if (strnlen((const char *)ptr, nvp->nvp_datasize) != + nvp->nvp_datasize - 1) { + ERRNO_SET(EINVAL); + return (NULL); + } + + nvp->nvp_data = (uint64_t)(uintptr_t)nv_strdup((const char *)ptr); + if (nvp->nvp_data == 0) + return (NULL); + + ptr += nvp->nvp_datasize; + *leftp -= nvp->nvp_datasize; + + return (ptr); +} + +const unsigned char * +nvpair_unpack_nvlist(bool isbe __unused, nvpair_t *nvp, + const unsigned char *ptr, size_t *leftp, size_t nfds, nvlist_t **child) +{ + nvlist_t *value; + + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_NVLIST); + + if (*leftp < nvp->nvp_datasize || nvp->nvp_datasize == 0) { + ERRNO_SET(EINVAL); + return (NULL); + } + + value = nvlist_create(0); + if (value == NULL) + return (NULL); + + ptr = nvlist_unpack_header(value, ptr, nfds, NULL, leftp); + if (ptr == NULL) + return (NULL); + + nvp->nvp_data = (uint64_t)(uintptr_t)value; + *child = value; + + return (ptr); +} + +#ifndef _KERNEL +const unsigned char * +nvpair_unpack_descriptor(bool isbe, nvpair_t *nvp, const unsigned char *ptr, + size_t *leftp, const int *fds, size_t nfds) +{ + int64_t idx; + + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_DESCRIPTOR); + + if (nvp->nvp_datasize != sizeof(idx)) { + ERRNO_SET(EINVAL); + return (NULL); + } + if (*leftp < sizeof(idx)) { + ERRNO_SET(EINVAL); + return (NULL); + } + + if (isbe) + idx = be64dec(ptr); + else + idx = le64dec(ptr); + + if (idx < 0) { + ERRNO_SET(EINVAL); + return (NULL); + } + + if ((size_t)idx >= nfds) { + ERRNO_SET(EINVAL); + return (NULL); + } + + nvp->nvp_data = (uint64_t)fds[idx]; + + ptr += sizeof(idx); + *leftp -= sizeof(idx); + + return (ptr); +} +#endif + +const unsigned char * +nvpair_unpack_binary(bool isbe __unused, nvpair_t *nvp, + const unsigned char *ptr, size_t *leftp) +{ + void *value; + + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_BINARY); + + if (*leftp < nvp->nvp_datasize || nvp->nvp_datasize == 0) { + ERRNO_SET(EINVAL); + return (NULL); + } + + value = nv_malloc(nvp->nvp_datasize); + if (value == NULL) + return (NULL); + + memcpy(value, ptr, nvp->nvp_datasize); + ptr += nvp->nvp_datasize; + *leftp -= nvp->nvp_datasize; + + nvp->nvp_data = (uint64_t)(uintptr_t)value; + + return (ptr); +} + +const unsigned char * +nvpair_unpack_bool_array(bool isbe __unused, nvpair_t *nvp, + const unsigned char *ptr, size_t *leftp) +{ + uint8_t *value; + size_t size; + unsigned int i; + + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_BOOL_ARRAY); + + size = sizeof(*value) * nvp->nvp_nitems; + if (nvp->nvp_datasize != size || *leftp < size || + nvp->nvp_nitems == 0 || size < nvp->nvp_nitems) { + ERRNO_SET(EINVAL); + return (NULL); + } + + value = nv_malloc(size); + if (value == NULL) + return (NULL); + + for (i = 0; i < nvp->nvp_nitems; i++) { + value[i] = *(const uint8_t *)ptr; + + ptr += sizeof(*value); + *leftp -= sizeof(*value); + } + + nvp->nvp_data = (uint64_t)(uintptr_t)value; + + return (ptr); +} + +const unsigned char * +nvpair_unpack_number_array(bool isbe, nvpair_t *nvp, const unsigned char *ptr, + size_t *leftp) +{ + uint64_t *value; + size_t size; + unsigned int i; + + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_NUMBER_ARRAY); + + size = sizeof(*value) * nvp->nvp_nitems; + if (nvp->nvp_datasize != size || *leftp < size || + nvp->nvp_nitems == 0 || size < nvp->nvp_nitems) { + ERRNO_SET(EINVAL); + return (NULL); + } + + value = nv_malloc(size); + if (value == NULL) + return (NULL); + + for (i = 0; i < nvp->nvp_nitems; i++) { + if (isbe) + value[i] = be64dec(ptr); + else + value[i] = le64dec(ptr); + + ptr += sizeof(*value); + *leftp -= sizeof(*value); + } + + nvp->nvp_data = (uint64_t)(uintptr_t)value; + + return (ptr); +} + +const unsigned char * +nvpair_unpack_string_array(bool isbe __unused, nvpair_t *nvp, + const unsigned char *ptr, size_t *leftp) +{ + ssize_t size; + size_t len; + const char *tmp; + char **value; + unsigned int ii, j; + + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_STRING_ARRAY); + + if (*leftp < nvp->nvp_datasize || nvp->nvp_datasize == 0 || + nvp->nvp_nitems == 0) { + ERRNO_SET(EINVAL); + return (NULL); + } + + size = nvp->nvp_datasize; + tmp = (const char *)ptr; + for (ii = 0; ii < nvp->nvp_nitems; ii++) { + len = strnlen(tmp, size - 1) + 1; + size -= len; + if (size < 0) { + ERRNO_SET(EINVAL); + return (NULL); + } + tmp += len; + } + if (size != 0) { + ERRNO_SET(EINVAL); + return (NULL); + } + + value = nv_malloc(sizeof(*value) * nvp->nvp_nitems); + if (value == NULL) + return (NULL); + + for (ii = 0; ii < nvp->nvp_nitems; ii++) { + value[ii] = nv_strdup((const char *)ptr); + if (value[ii] == NULL) + goto out; + len = strlen(value[ii]) + 1; + ptr += len; + *leftp -= len; + } + nvp->nvp_data = (uint64_t)(uintptr_t)value; + + return (ptr); +out: + for (j = 0; j < ii; j++) + nv_free(value[j]); + nv_free(value); + return (NULL); +} + +#ifndef _KERNEL +const unsigned char * +nvpair_unpack_descriptor_array(bool isbe, nvpair_t *nvp, + const unsigned char *ptr, size_t *leftp, const int *fds, size_t nfds) +{ + int64_t idx; + size_t size; + unsigned int ii; + int *array; + + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_DESCRIPTOR_ARRAY); + + size = sizeof(idx) * nvp->nvp_nitems; + if (nvp->nvp_datasize != size || *leftp < size || + nvp->nvp_nitems == 0 || size < nvp->nvp_nitems) { + ERRNO_SET(EINVAL); + return (NULL); + } + + array = (int *)nv_malloc(size); + if (array == NULL) + return (NULL); + + for (ii = 0; ii < nvp->nvp_nitems; ii++) { + if (isbe) + idx = be64dec(ptr); + else + idx = le64dec(ptr); + + if (idx < 0) { + ERRNO_SET(EINVAL); + nv_free(array); + return (NULL); + } + + if ((size_t)idx >= nfds) { + ERRNO_SET(EINVAL); + nv_free(array); + return (NULL); + } + + array[ii] = (uint64_t)fds[idx]; + + ptr += sizeof(idx); + *leftp -= sizeof(idx); + } + + nvp->nvp_data = (uint64_t)(uintptr_t)array; + + return (ptr); +} +#endif + +const unsigned char * +nvpair_unpack_nvlist_array(bool isbe __unused, nvpair_t *nvp, + const unsigned char *ptr, size_t *leftp, nvlist_t **firstel) +{ + nvlist_t **value; + nvpair_t *tmpnvp; + unsigned int ii, j; + size_t sizeup; + + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_NVLIST_ARRAY); + + sizeup = sizeof(struct nvpair_header) * nvp->nvp_nitems; + if (nvp->nvp_nitems == 0 || sizeup < nvp->nvp_nitems || + sizeup > *leftp) { + ERRNO_SET(EINVAL); + return (NULL); + } + + value = nv_malloc(nvp->nvp_nitems * sizeof(*value)); + if (value == NULL) + return (NULL); + + for (ii = 0; ii < nvp->nvp_nitems; ii++) { + value[ii] = nvlist_create(0); + if (value[ii] == NULL) + goto fail; + if (ii > 0) { + tmpnvp = nvpair_allocv(" ", NV_TYPE_NVLIST, + (uint64_t)(uintptr_t)value[ii], 0, 0); + if (tmpnvp == NULL) + goto fail; + nvlist_set_array_next(value[ii - 1], tmpnvp); + } + } + nvlist_set_flags(value[nvp->nvp_nitems - 1], NV_FLAG_IN_ARRAY); + + nvp->nvp_data = (uint64_t)(uintptr_t)value; + *firstel = value[0]; + + return (ptr); +fail: + ERRNO_SAVE(); + for (j = 0; j < ii; j++) + nvlist_destroy(value[j]); + nv_free(value); + ERRNO_RESTORE(); + + return (NULL); +} + +const unsigned char * +nvpair_unpack(bool isbe, const unsigned char *ptr, size_t *leftp, + nvpair_t **nvpp) +{ + nvpair_t *nvp, *tmp; + + nvp = nv_calloc(1, sizeof(*nvp) + NV_NAME_MAX); + if (nvp == NULL) + return (NULL); + nvp->nvp_name = (char *)(nvp + 1); + + ptr = nvpair_unpack_header(isbe, nvp, ptr, leftp); + if (ptr == NULL) + goto failed; + tmp = nv_realloc(nvp, sizeof(*nvp) + strlen(nvp->nvp_name) + 1); + if (tmp == NULL) + goto failed; + nvp = tmp; + + /* Update nvp_name after realloc(). */ + nvp->nvp_name = (char *)(nvp + 1); + nvp->nvp_data = 0x00; + nvp->nvp_magic = NVPAIR_MAGIC; + *nvpp = nvp; + return (ptr); +failed: + nv_free(nvp); + return (NULL); +} + +int +nvpair_type(const nvpair_t *nvp) +{ + + NVPAIR_ASSERT(nvp); + + return (nvp->nvp_type); +} + +const char * +nvpair_name(const nvpair_t *nvp) +{ + + NVPAIR_ASSERT(nvp); + + return (nvp->nvp_name); +} + +nvpair_t * +nvpair_create_stringf(const char *name, const char *valuefmt, ...) +{ + va_list valueap; + nvpair_t *nvp; + + va_start(valueap, valuefmt); + nvp = nvpair_create_stringv(name, valuefmt, valueap); + va_end(valueap); + + return (nvp); +} + +nvpair_t * +nvpair_create_stringv(const char *name, const char *valuefmt, va_list valueap) +{ + nvpair_t *nvp; + char *str; + int len; + + len = nv_vasprintf(&str, valuefmt, valueap); + if (len < 0) + return (NULL); + nvp = nvpair_create_string(name, str); + if (nvp == NULL) + nv_free(str); + return (nvp); +} + +nvpair_t * +nvpair_create_null(const char *name) +{ + + return (nvpair_allocv(name, NV_TYPE_NULL, 0, 0, 0)); +} + +nvpair_t * +nvpair_create_bool(const char *name, bool value) +{ + + return (nvpair_allocv(name, NV_TYPE_BOOL, value ? 1 : 0, + sizeof(uint8_t), 0)); +} + +nvpair_t * +nvpair_create_number(const char *name, uint64_t value) +{ + + return (nvpair_allocv(name, NV_TYPE_NUMBER, value, sizeof(value), 0)); +} + +nvpair_t * +nvpair_create_string(const char *name, const char *value) +{ + nvpair_t *nvp; + size_t size; + char *data; + + if (value == NULL) { + ERRNO_SET(EINVAL); + return (NULL); + } + + data = nv_strdup(value); + if (data == NULL) + return (NULL); + size = strlen(value) + 1; + + nvp = nvpair_allocv(name, NV_TYPE_STRING, (uint64_t)(uintptr_t)data, + size, 0); + if (nvp == NULL) + nv_free(data); + + return (nvp); +} + +nvpair_t * +nvpair_create_nvlist(const char *name, const nvlist_t *value) +{ + nvlist_t *nvl; + nvpair_t *nvp; + + if (value == NULL) { + ERRNO_SET(EINVAL); + return (NULL); + } + + nvl = nvlist_clone(value); + if (nvl == NULL) + return (NULL); + + nvp = nvpair_allocv(name, NV_TYPE_NVLIST, (uint64_t)(uintptr_t)nvl, 0, + 0); + if (nvp == NULL) + nvlist_destroy(nvl); + else + nvlist_set_parent(nvl, nvp); + + return (nvp); +} + +#ifndef _KERNEL +nvpair_t * +nvpair_create_descriptor(const char *name, int value) +{ + nvpair_t *nvp; + + if (value < 0 || !fd_is_valid(value)) { + ERRNO_SET(EBADF); + return (NULL); + } + + value = fcntl(value, F_DUPFD_CLOEXEC, 0); + if (value < 0) + return (NULL); + + nvp = nvpair_allocv(name, NV_TYPE_DESCRIPTOR, (uint64_t)value, + sizeof(int64_t), 0); + if (nvp == NULL) { + ERRNO_SAVE(); + close(value); + ERRNO_RESTORE(); + } + + return (nvp); +} +#endif + +nvpair_t * +nvpair_create_binary(const char *name, const void *value, size_t size) +{ + nvpair_t *nvp; + void *data; + + if (value == NULL || size == 0) { + ERRNO_SET(EINVAL); + return (NULL); + } + + data = nv_malloc(size); + if (data == NULL) + return (NULL); + memcpy(data, value, size); + + nvp = nvpair_allocv(name, NV_TYPE_BINARY, (uint64_t)(uintptr_t)data, + size, 0); + if (nvp == NULL) + nv_free(data); + + return (nvp); +} + +nvpair_t * +nvpair_create_bool_array(const char *name, const bool *value, size_t nitems) +{ + nvpair_t *nvp; + size_t size; + void *data; + + if (value == NULL || nitems == 0) { + ERRNO_SET(EINVAL); + return (NULL); + } + + size = sizeof(value[0]) * nitems; + data = nv_malloc(size); + if (data == NULL) + return (NULL); + + memcpy(data, value, size); + nvp = nvpair_allocv(name, NV_TYPE_BOOL_ARRAY, (uint64_t)(uintptr_t)data, + size, nitems); + if (nvp == NULL) { + ERRNO_SAVE(); + nv_free(data); + ERRNO_RESTORE(); + } + + return (nvp); +} + +nvpair_t * +nvpair_create_number_array(const char *name, const uint64_t *value, + size_t nitems) +{ + nvpair_t *nvp; + size_t size; + void *data; + + if (value == NULL || nitems == 0) { + ERRNO_SET(EINVAL); + return (NULL); + } + + size = sizeof(value[0]) * nitems; + data = nv_malloc(size); + if (data == NULL) + return (NULL); + + memcpy(data, value, size); + nvp = nvpair_allocv(name, NV_TYPE_NUMBER_ARRAY, + (uint64_t)(uintptr_t)data, size, nitems); + if (nvp == NULL) { + ERRNO_SAVE(); + nv_free(data); + ERRNO_RESTORE(); + } + + return (nvp); +} + +nvpair_t * +nvpair_create_string_array(const char *name, const char * const *value, + size_t nitems) +{ + nvpair_t *nvp; + unsigned int ii; + size_t datasize, size; + char **data; + + if (value == NULL || nitems == 0) { + ERRNO_SET(EINVAL); + return (NULL); + } + + nvp = NULL; + datasize = 0; + data = nv_malloc(sizeof(value[0]) * nitems); + if (data == NULL) + return (NULL); + + for (ii = 0; ii < nitems; ii++) { + if (value[ii] == NULL) { + ERRNO_SET(EINVAL); + goto fail; + } + + size = strlen(value[ii]) + 1; + datasize += size; + data[ii] = nv_strdup(value[ii]); + if (data[ii] == NULL) + goto fail; + } + nvp = nvpair_allocv(name, NV_TYPE_STRING_ARRAY, + (uint64_t)(uintptr_t)data, datasize, nitems); + +fail: + if (nvp == NULL) { + ERRNO_SAVE(); + for (; ii > 0; ii--) + nv_free(data[ii - 1]); + nv_free(data); + ERRNO_RESTORE(); + } + + return (nvp); +} + +nvpair_t * +nvpair_create_nvlist_array(const char *name, const nvlist_t * const *value, + size_t nitems) +{ + unsigned int ii; + nvlist_t **nvls; + nvpair_t *nvp; + int flags; + + nvp = NULL; + nvls = NULL; + ii = 0; + + if (value == NULL || nitems == 0) { + ERRNO_SET(EINVAL); + return (NULL); + } + + nvls = nv_malloc(sizeof(value[0]) * nitems); + if (nvls == NULL) + return (NULL); + + for (ii = 0; ii < nitems; ii++) { + if (value[ii] == NULL) { + ERRNO_SET(EINVAL); + goto fail; + } + + nvls[ii] = nvlist_clone(value[ii]); + if (nvls[ii] == NULL) + goto fail; + + if (ii > 0) { + nvp = nvpair_allocv(" ", NV_TYPE_NVLIST, + (uint64_t)(uintptr_t)nvls[ii], 0, 0); + if (nvp == NULL) + goto fail; + nvlist_set_array_next(nvls[ii - 1], nvp); + } + } + flags = nvlist_flags(nvls[nitems - 1]) | NV_FLAG_IN_ARRAY; + nvlist_set_flags(nvls[nitems - 1], flags); + + nvp = nvpair_allocv(name, NV_TYPE_NVLIST_ARRAY, + (uint64_t)(uintptr_t)nvls, 0, nitems); + +fail: + if (nvp == NULL) { + ERRNO_SAVE(); + for (; ii > 0; ii--) + nvlist_destroy(nvls[ii - 1]); + + nv_free(nvls); + ERRNO_RESTORE(); + } else { + for (ii = 0; ii < nitems; ii++) + nvlist_set_parent(nvls[ii], nvp); + } + + return (nvp); +} + +#ifndef _KERNEL +nvpair_t * +nvpair_create_descriptor_array(const char *name, const int *value, + size_t nitems) +{ + unsigned int ii; + nvpair_t *nvp; + int *fds; + + if (value == NULL) { + ERRNO_SET(EINVAL); + return (NULL); + } + + nvp = NULL; + + fds = nv_malloc(sizeof(value[0]) * nitems); + if (fds == NULL) + return (NULL); + for (ii = 0; ii < nitems; ii++) { + if (value[ii] == -1) { + fds[ii] = -1; + } else { + if (!fd_is_valid(value[ii])) { + ERRNO_SET(EBADF); + goto fail; + } + + fds[ii] = fcntl(value[ii], F_DUPFD_CLOEXEC, 0); + if (fds[ii] == -1) + goto fail; + } + } + + nvp = nvpair_allocv(name, NV_TYPE_DESCRIPTOR_ARRAY, + (uint64_t)(uintptr_t)fds, sizeof(int64_t) * nitems, nitems); + +fail: + if (nvp == NULL) { + ERRNO_SAVE(); + for (; ii > 0; ii--) { + if (fds[ii - 1] != -1) + close(fds[ii - 1]); + } + nv_free(fds); + ERRNO_RESTORE(); + } + + return (nvp); +} +#endif + +nvpair_t * +nvpair_move_string(const char *name, char *value) +{ + nvpair_t *nvp; + + if (value == NULL) { + ERRNO_SET(EINVAL); + return (NULL); + } + + nvp = nvpair_allocv(name, NV_TYPE_STRING, (uint64_t)(uintptr_t)value, + strlen(value) + 1, 0); + if (nvp == NULL) { + ERRNO_SAVE(); + nv_free(value); + ERRNO_RESTORE(); + } + + return (nvp); +} + +nvpair_t * +nvpair_move_nvlist(const char *name, nvlist_t *value) +{ + nvpair_t *nvp; + + if (value == NULL || nvlist_get_nvpair_parent(value) != NULL) { + ERRNO_SET(EINVAL); + return (NULL); + } + + if (nvlist_error(value) != 0) { + ERRNO_SET(nvlist_error(value)); + nvlist_destroy(value); + return (NULL); + } + + nvp = nvpair_allocv(name, NV_TYPE_NVLIST, (uint64_t)(uintptr_t)value, + 0, 0); + if (nvp == NULL) + nvlist_destroy(value); + else + nvlist_set_parent(value, nvp); + + return (nvp); +} + +#ifndef _KERNEL +nvpair_t * +nvpair_move_descriptor(const char *name, int value) +{ + nvpair_t *nvp; + + if (value < 0 || !fd_is_valid(value)) { + ERRNO_SET(EBADF); + return (NULL); + } + + nvp = nvpair_allocv(name, NV_TYPE_DESCRIPTOR, (uint64_t)value, + sizeof(int64_t), 0); + if (nvp == NULL) { + ERRNO_SAVE(); + close(value); + ERRNO_RESTORE(); + } + + return (nvp); +} +#endif + +nvpair_t * +nvpair_move_binary(const char *name, void *value, size_t size) +{ + nvpair_t *nvp; + + if (value == NULL || size == 0) { + ERRNO_SET(EINVAL); + return (NULL); + } + + nvp = nvpair_allocv(name, NV_TYPE_BINARY, (uint64_t)(uintptr_t)value, + size, 0); + if (nvp == NULL) { + ERRNO_SAVE(); + nv_free(value); + ERRNO_RESTORE(); + } + + return (nvp); +} + +nvpair_t * +nvpair_move_bool_array(const char *name, bool *value, size_t nitems) +{ + nvpair_t *nvp; + + if (value == NULL || nitems == 0) { + ERRNO_SET(EINVAL); + return (NULL); + } + + nvp = nvpair_allocv(name, NV_TYPE_BOOL_ARRAY, + (uint64_t)(uintptr_t)value, sizeof(value[0]) * nitems, nitems); + if (nvp == NULL) { + ERRNO_SAVE(); + nv_free(value); + ERRNO_RESTORE(); + } + + return (nvp); +} + +nvpair_t * +nvpair_move_string_array(const char *name, char **value, size_t nitems) +{ + nvpair_t *nvp; + size_t i, size; + + if (value == NULL || nitems == 0) { + ERRNO_SET(EINVAL); + return (NULL); + } + + size = 0; + for (i = 0; i < nitems; i++) { + if (value[i] == NULL) { + ERRNO_SET(EINVAL); + return (NULL); + } + + size += strlen(value[i]) + 1; + } + + nvp = nvpair_allocv(name, NV_TYPE_STRING_ARRAY, + (uint64_t)(uintptr_t)value, size, nitems); + if (nvp == NULL) { + ERRNO_SAVE(); + for (i = 0; i < nitems; i++) + nv_free(value[i]); + nv_free(value); + ERRNO_RESTORE(); + } + + return (nvp); +} + +nvpair_t * +nvpair_move_number_array(const char *name, uint64_t *value, size_t nitems) +{ + nvpair_t *nvp; + + if (value == NULL || nitems == 0) { + ERRNO_SET(EINVAL); + return (NULL); + } + + nvp = nvpair_allocv(name, NV_TYPE_NUMBER_ARRAY, + (uint64_t)(uintptr_t)value, sizeof(value[0]) * nitems, nitems); + if (nvp == NULL) { + ERRNO_SAVE(); + nv_free(value); + ERRNO_RESTORE(); + } + + return (nvp); +} + +nvpair_t * +nvpair_move_nvlist_array(const char *name, nvlist_t **value, size_t nitems) +{ + unsigned int ii; + nvpair_t *nvp; + int flags; + + nvp = NULL; + if (value == NULL || nitems == 0) { + ERRNO_SET(EINVAL); + return (NULL); + } + + for (ii = 0; ii < nitems; ii++) { + if (value == NULL || nvlist_error(value[ii]) != 0 || + nvlist_get_pararr(value[ii], NULL) != NULL) { + ERRNO_SET(EINVAL); + goto fail; + } + if (ii > 0) { + nvp = nvpair_allocv(" ", NV_TYPE_NVLIST, + (uint64_t)(uintptr_t)value[ii], 0, 0); + if (nvp == NULL) + goto fail; + nvlist_set_array_next(value[ii - 1], nvp); + } + } + flags = nvlist_flags(value[nitems - 1]) | NV_FLAG_IN_ARRAY; + nvlist_set_flags(value[nitems - 1], flags); + + nvp = nvpair_allocv(name, NV_TYPE_NVLIST_ARRAY, + (uint64_t)(uintptr_t)value, 0, nitems); +fail: + if (nvp == NULL) { + ERRNO_SAVE(); + for (ii = 0; ii < nitems; ii++) { + if (value[ii] != NULL && + nvlist_get_pararr(value[ii], NULL) != NULL) { + nvlist_destroy(value[ii]); + } + nv_free(value); + } + ERRNO_RESTORE(); + } else { + for (ii = 0; ii < nitems; ii++) + nvlist_set_parent(value[ii], nvp); + } + + return (nvp); +} + +#ifndef _KERNEL +nvpair_t * +nvpair_move_descriptor_array(const char *name, int *value, size_t nitems) +{ + nvpair_t *nvp; + size_t i; + + nvp = NULL; + if (value == NULL || nitems == 0) { + ERRNO_SET(EINVAL); + return (NULL); + } + + for (i = 0; i < nitems; i++) { + if (value[i] != -1 && !fd_is_valid(value[i])) { + ERRNO_SET(EBADF); + goto fail; + } + } + + nvp = nvpair_allocv(name, NV_TYPE_DESCRIPTOR_ARRAY, + (uint64_t)(uintptr_t)value, sizeof(value[0]) * nitems, nitems); + +fail: + if (nvp == NULL) { + ERRNO_SAVE(); + for (i = 0; i < nitems; i++) { + if (fd_is_valid(value[i])) + close(value[i]); + } + nv_free(value); + ERRNO_RESTORE(); + } + + return (nvp); +} +#endif + +bool +nvpair_get_bool(const nvpair_t *nvp) +{ + + NVPAIR_ASSERT(nvp); + + return (nvp->nvp_data == 1); +} + +uint64_t +nvpair_get_number(const nvpair_t *nvp) +{ + + NVPAIR_ASSERT(nvp); + + return (nvp->nvp_data); +} + +const char * +nvpair_get_string(const nvpair_t *nvp) +{ + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_STRING); + + return ((const char *)(intptr_t)nvp->nvp_data); +} + +const nvlist_t * +nvpair_get_nvlist(const nvpair_t *nvp) +{ + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_NVLIST); + + return ((const nvlist_t *)(intptr_t)nvp->nvp_data); +} + +#ifndef _KERNEL +int +nvpair_get_descriptor(const nvpair_t *nvp) +{ + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_DESCRIPTOR); + + return ((int)nvp->nvp_data); +} +#endif + +const void * +nvpair_get_binary(const nvpair_t *nvp, size_t *sizep) +{ + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_BINARY); + + if (sizep != NULL) + *sizep = nvp->nvp_datasize; + + return ((const void *)(intptr_t)nvp->nvp_data); +} + +const bool * +nvpair_get_bool_array(const nvpair_t *nvp, size_t *nitems) +{ + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_BOOL_ARRAY); + + if (nitems != NULL) + *nitems = nvp->nvp_nitems; + + return ((const bool *)(intptr_t)nvp->nvp_data); +} + +const uint64_t * +nvpair_get_number_array(const nvpair_t *nvp, size_t *nitems) +{ + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_NUMBER_ARRAY); + + if (nitems != NULL) + *nitems = nvp->nvp_nitems; + + return ((const uint64_t *)(intptr_t)nvp->nvp_data); +} + +const char * const * +nvpair_get_string_array(const nvpair_t *nvp, size_t *nitems) +{ + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_STRING_ARRAY); + + if (nitems != NULL) + *nitems = nvp->nvp_nitems; + + return ((const char * const *)(intptr_t)nvp->nvp_data); +} + +const nvlist_t * const * +nvpair_get_nvlist_array(const nvpair_t *nvp, size_t *nitems) +{ + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_NVLIST_ARRAY); + + if (nitems != NULL) + *nitems = nvp->nvp_nitems; + + return ((const nvlist_t * const *)((intptr_t)nvp->nvp_data)); +} + +#ifndef _KERNEL +const int * +nvpair_get_descriptor_array(const nvpair_t *nvp, size_t *nitems) +{ + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_DESCRIPTOR_ARRAY); + + if (nitems != NULL) + *nitems = nvp->nvp_nitems; + + return ((const int *)(intptr_t)nvp->nvp_data); +} +#endif + +void +nvpair_free(nvpair_t *nvp) +{ + size_t i; + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_list == NULL); + + nvp->nvp_magic = 0; + switch (nvp->nvp_type) { +#ifndef _KERNEL + case NV_TYPE_DESCRIPTOR: + close((int)nvp->nvp_data); + break; + case NV_TYPE_DESCRIPTOR_ARRAY: + for (i = 0; i < nvp->nvp_nitems; i++) + close(((int *)(intptr_t)nvp->nvp_data)[i]); + break; +#endif + case NV_TYPE_NVLIST: + nvlist_destroy((nvlist_t *)(intptr_t)nvp->nvp_data); + break; + case NV_TYPE_STRING: + nv_free((char *)(intptr_t)nvp->nvp_data); + break; + case NV_TYPE_BINARY: + nv_free((void *)(intptr_t)nvp->nvp_data); + break; + case NV_TYPE_NVLIST_ARRAY: + for (i = 0; i < nvp->nvp_nitems; i++) { + nvlist_destroy( + ((nvlist_t **)(intptr_t)nvp->nvp_data)[i]); + } + nv_free(((nvlist_t **)(intptr_t)nvp->nvp_data)); + break; + case NV_TYPE_NUMBER_ARRAY: + nv_free((uint64_t *)(intptr_t)nvp->nvp_data); + break; + case NV_TYPE_BOOL_ARRAY: + nv_free((bool *)(intptr_t)nvp->nvp_data); + break; + case NV_TYPE_STRING_ARRAY: + for (i = 0; i < nvp->nvp_nitems; i++) + nv_free(((char **)(intptr_t)nvp->nvp_data)[i]); + break; + } + nv_free(nvp); +} + +void +nvpair_free_structure(nvpair_t *nvp) +{ + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_list == NULL); + + nvp->nvp_magic = 0; + nv_free(nvp); +} + +const char * +nvpair_type_string(int type) +{ + + switch (type) { + case NV_TYPE_NULL: + return ("NULL"); + case NV_TYPE_BOOL: + return ("BOOL"); + case NV_TYPE_NUMBER: + return ("NUMBER"); + case NV_TYPE_STRING: + return ("STRING"); + case NV_TYPE_NVLIST: + return ("NVLIST"); + case NV_TYPE_DESCRIPTOR: + return ("DESCRIPTOR"); + case NV_TYPE_BINARY: + return ("BINARY"); + case NV_TYPE_BOOL_ARRAY: + return ("BOOL ARRAY"); + case NV_TYPE_NUMBER_ARRAY: + return ("NUMBER ARRAY"); + case NV_TYPE_STRING_ARRAY: + return ("STRING ARRAY"); + case NV_TYPE_NVLIST_ARRAY: + return ("NVLIST ARRAY"); + case NV_TYPE_DESCRIPTOR_ARRAY: + return ("DESCRIPTOR ARRAY"); + default: + return (""); + } +} + Index: sys/contrib/libnv/nvpair_impl.h =================================================================== --- /dev/null +++ sys/contrib/libnv/nvpair_impl.h @@ -0,0 +1,113 @@ +/*- + * Copyright (c) 2009-2013 The FreeBSD Foundation + * Copyright (c) 2013-2015 Mariusz Zaborski + * All rights reserved. + * + * This software was developed by Pawel Jakub Dawidek under sponsorship from + * the FreeBSD Foundation. + * + * 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 AUTHORS 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 AUTHORS 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: head/sys/contrib/libnv/nvpair_impl.h 286796 2015-08-15 06:34:49Z oshogbo $ + */ + +#ifndef _NVPAIR_IMPL_H_ +#define _NVPAIR_IMPL_H_ + +#include +#include + +#ifndef _KERNEL +#include +#endif + +TAILQ_HEAD(nvl_head, nvpair); + +void nvpair_assert(const nvpair_t *nvp); +nvlist_t *nvpair_nvlist(const nvpair_t *nvp); +nvpair_t *nvpair_next(const nvpair_t *nvp); +nvpair_t *nvpair_prev(const nvpair_t *nvp); +void nvpair_insert(struct nvl_head *head, nvpair_t *nvp, nvlist_t *nvl); +void nvpair_remove(struct nvl_head *head, nvpair_t *nvp, const nvlist_t *nvl); +size_t nvpair_header_size(void); +size_t nvpair_size(const nvpair_t *nvp); +const unsigned char *nvpair_unpack(bool isbe, const unsigned char *ptr, + size_t *leftp, nvpair_t **nvpp); +void nvpair_free_structure(nvpair_t *nvp); +void nvpair_init_datasize(nvpair_t *nvp); +const char *nvpair_type_string(int type); + +/* Pack functions. */ +unsigned char *nvpair_pack_header(const nvpair_t *nvp, unsigned char *ptr, + size_t *leftp); +unsigned char *nvpair_pack_null(const nvpair_t *nvp, unsigned char *ptr, + size_t *leftp); +unsigned char *nvpair_pack_bool(const nvpair_t *nvp, unsigned char *ptr, + size_t *leftp); +unsigned char *nvpair_pack_number(const nvpair_t *nvp, unsigned char *ptr, + size_t *leftp); +unsigned char *nvpair_pack_string(const nvpair_t *nvp, unsigned char *ptr, + size_t *leftp); +unsigned char *nvpair_pack_descriptor(const nvpair_t *nvp, unsigned char *ptr, + int64_t *fdidxp, size_t *leftp); +unsigned char *nvpair_pack_binary(const nvpair_t *nvp, unsigned char *ptr, + size_t *leftp); +unsigned char *nvpair_pack_nvlist_up(unsigned char *ptr, size_t *leftp); +unsigned char *nvpair_pack_bool_array(const nvpair_t *nvp, unsigned char *ptr, + size_t *leftp); +unsigned char *nvpair_pack_number_array(const nvpair_t *nvp, unsigned char *ptr, + size_t *leftp); +unsigned char *nvpair_pack_string_array(const nvpair_t *nvp, unsigned char *ptr, + size_t *leftp); +unsigned char *nvpair_pack_descriptor_array(const nvpair_t *nvp, + unsigned char *ptr, int64_t *fdidxp, size_t *leftp); +unsigned char *nvpair_pack_nvlist_array_next(unsigned char *ptr, size_t *leftp); + +/* Unpack data functions. */ +const unsigned char *nvpair_unpack_header(bool isbe, nvpair_t *nvp, + const unsigned char *ptr, size_t *leftp); +const unsigned char *nvpair_unpack_null(bool isbe, nvpair_t *nvp, + const unsigned char *ptr, size_t *leftp); +const unsigned char *nvpair_unpack_bool(bool isbe, nvpair_t *nvp, + const unsigned char *ptr, size_t *leftp); +const unsigned char *nvpair_unpack_number(bool isbe, nvpair_t *nvp, + const unsigned char *ptr, size_t *leftp); +const unsigned char *nvpair_unpack_string(bool isbe, nvpair_t *nvp, + const unsigned char *ptr, size_t *leftp); +const unsigned char *nvpair_unpack_nvlist(bool isbe, nvpair_t *nvp, + const unsigned char *ptr, size_t *leftp, size_t nfds, nvlist_t **child); +const unsigned char *nvpair_unpack_descriptor(bool isbe, nvpair_t *nvp, + const unsigned char *ptr, size_t *leftp, const int *fds, size_t nfds); +const unsigned char *nvpair_unpack_binary(bool isbe, nvpair_t *nvp, + const unsigned char *ptr, size_t *leftp); +const unsigned char *nvpair_unpack_bool_array(bool isbe, nvpair_t *nvp, + const unsigned char *ptr, size_t *leftp); +const unsigned char *nvpair_unpack_number_array(bool isbe, nvpair_t *nvp, + const unsigned char *ptr, size_t *leftp); +const unsigned char *nvpair_unpack_string_array(bool isbe, nvpair_t *nvp, + const unsigned char *ptr, size_t *leftp); +const unsigned char *nvpair_unpack_descriptor_array(bool isbe, nvpair_t *nvp, + const unsigned char *ptr, size_t *leftp, const int *fds, size_t nfds); +const unsigned char *nvpair_unpack_nvlist_array(bool isbe, nvpair_t *nvp, + const unsigned char *ptr, size_t *leftp, nvlist_t **firstel); + +#endif /* !_NVPAIR_IMPL_H_ */ Index: sys/modules/zfs/Makefile =================================================================== --- sys/modules/zfs/Makefile +++ sys/modules/zfs/Makefile @@ -17,9 +17,9 @@ .PATH: ${SUNW}/common/avl SRCS+= avl.c .PATH: ${SUNW}/common/nvpair -SRCS+= nvpair.c -SRCS+= nvpair_alloc_fixed.c -SRCS+= fnvpair.c +SRCS+= opensolaris_nvpair.c +SRCS+= opensolaris_nvpair_alloc_fixed.c +SRCS+= opensolaris_fnvpair.c .PATH: ${.CURDIR}/../../cddl/contrib/opensolaris/common/unicode SRCS+= u8_textprep.c Index: sys/sys/dnv.h =================================================================== --- /dev/null +++ sys/sys/dnv.h @@ -0,0 +1,85 @@ +/*- + * Copyright (c) 2013 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Pawel Jakub Dawidek under sponsorship from + * the FreeBSD Foundation. + * + * 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 AUTHORS 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 AUTHORS 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: head/sys/sys/dnv.h 285130 2015-07-04 10:33:33Z oshogbo $ + */ + +#ifndef _DNV_H_ +#define _DNV_H_ + +#include + +#ifndef _KERNEL +#include +#include +#include +#include +#endif + +#ifndef _NVLIST_T_DECLARED +#define _NVLIST_T_DECLARED +struct nvlist; + +typedef struct nvlist nvlist_t; +#endif + +__BEGIN_DECLS + +/* + * The dnvlist_get functions returns value associated with the given name. + * If it returns a pointer, the pointer represents internal buffer and should + * not be freed by the caller. + * If no element of the given name and type exists, the function will return + * provided default value. + */ + +bool dnvlist_get_bool(const nvlist_t *nvl, const char *name, bool defval); +uint64_t dnvlist_get_number(const nvlist_t *nvl, const char *name, uint64_t defval); +const char *dnvlist_get_string(const nvlist_t *nvl, const char *name, const char *defval); +const nvlist_t *dnvlist_get_nvlist(const nvlist_t *nvl, const char *name, const nvlist_t *defval); +int dnvlist_get_descriptor(const nvlist_t *nvl, const char *name, int defval); +const void *dnvlist_get_binary(const nvlist_t *nvl, const char *name, size_t *sizep, const void *defval, size_t defsize); + +/* + * The dnvlist_take functions returns value associated with the given name and + * remove corresponding nvpair. + * If it returns a pointer, the caller has to free it. + * If no element of the given name and type exists, the function will return + * provided default value. + */ + +bool dnvlist_take_bool(nvlist_t *nvl, const char *name, bool defval); +uint64_t dnvlist_take_number(nvlist_t *nvl, const char *name, uint64_t defval); +char *dnvlist_take_string(nvlist_t *nvl, const char *name, char *defval); +nvlist_t *dnvlist_take_nvlist(nvlist_t *nvl, const char *name, nvlist_t *defval); +int dnvlist_take_descriptor(nvlist_t *nvl, const char *name, int defval); +void *dnvlist_take_binary(nvlist_t *nvl, const char *name, size_t *sizep, void *defval, size_t defsize); + +__END_DECLS + +#endif /* !_DNV_H_ */ Index: sys/sys/nv.h =================================================================== --- /dev/null +++ sys/sys/nv.h @@ -0,0 +1,246 @@ +/*- + * Copyright (c) 2009-2013 The FreeBSD Foundation + * Copyright (c) 2013-2015 Mariusz Zaborski + * All rights reserved. + * + * This software was developed by Pawel Jakub Dawidek under sponsorship from + * the FreeBSD Foundation. + * + * 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 AUTHORS 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 AUTHORS 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: head/sys/sys/nv.h 286796 2015-08-15 06:34:49Z oshogbo $ + */ + +#ifndef _NV_H_ +#define _NV_H_ + +#include + +#ifndef _KERNEL +#include +#include +#include +#include +#endif + +#ifndef _NVLIST_T_DECLARED +#define _NVLIST_T_DECLARED +struct nvlist; + +typedef struct nvlist nvlist_t; +#endif + +#define NV_NAME_MAX 2048 + +#define NV_TYPE_NONE 0 + +#define NV_TYPE_NULL 1 +#define NV_TYPE_BOOL 2 +#define NV_TYPE_NUMBER 3 +#define NV_TYPE_STRING 4 +#define NV_TYPE_NVLIST 5 +#define NV_TYPE_DESCRIPTOR 6 +#define NV_TYPE_BINARY 7 +#define NV_TYPE_BOOL_ARRAY 8 +#define NV_TYPE_NUMBER_ARRAY 9 +#define NV_TYPE_STRING_ARRAY 10 +#define NV_TYPE_NVLIST_ARRAY 11 +#define NV_TYPE_DESCRIPTOR_ARRAY 12 + +/* + * Perform case-insensitive lookups of provided names. + */ +#define NV_FLAG_IGNORE_CASE 0x01 +/* + * Names don't have to be unique. + */ +#define NV_FLAG_NO_UNIQUE 0x02 + +#if defined(_KERNEL) && defined(MALLOC_DECLARE) +MALLOC_DECLARE(M_NVLIST); +#endif + +__BEGIN_DECLS + +nvlist_t *nvlist_create(int flags); +void nvlist_destroy(nvlist_t *nvl); +int nvlist_error(const nvlist_t *nvl); +bool nvlist_empty(const nvlist_t *nvl); +int nvlist_flags(const nvlist_t *nvl); +void nvlist_set_error(nvlist_t *nvl, int error); + +nvlist_t *nvlist_clone(const nvlist_t *nvl); + +#ifndef _KERNEL +void nvlist_dump(const nvlist_t *nvl, int fd); +void nvlist_fdump(const nvlist_t *nvl, FILE *fp); +#endif + +size_t nvlist_size(const nvlist_t *nvl); +void *nvlist_pack(const nvlist_t *nvl, size_t *sizep); +nvlist_t *nvlist_unpack(const void *buf, size_t size, int flags); + +int nvlist_send(int sock, const nvlist_t *nvl); +nvlist_t *nvlist_recv(int sock, int flags); +nvlist_t *nvlist_xfer(int sock, nvlist_t *nvl, int flags); + +const char *nvlist_next(const nvlist_t *nvl, int *typep, void **cookiep); + +const nvlist_t *nvlist_get_parent(const nvlist_t *nvl, void **cookiep); + +const nvlist_t *nvlist_get_array_next(const nvlist_t *nvl); +bool nvlist_in_array(const nvlist_t *nvl); + +const nvlist_t *nvlist_get_pararr(const nvlist_t *nvl, void **cookiep); + +/* + * The nvlist_exists functions check if the given name (optionally of the given + * type) exists on nvlist. + */ + +bool nvlist_exists(const nvlist_t *nvl, const char *name); +bool nvlist_exists_type(const nvlist_t *nvl, const char *name, int type); + +bool nvlist_exists_null(const nvlist_t *nvl, const char *name); +bool nvlist_exists_bool(const nvlist_t *nvl, const char *name); +bool nvlist_exists_number(const nvlist_t *nvl, const char *name); +bool nvlist_exists_string(const nvlist_t *nvl, const char *name); +bool nvlist_exists_nvlist(const nvlist_t *nvl, const char *name); +bool nvlist_exists_binary(const nvlist_t *nvl, const char *name); +bool nvlist_exists_bool_array(const nvlist_t *nvl, const char *name); +bool nvlist_exists_number_array(const nvlist_t *nvl, const char *name); +bool nvlist_exists_string_array(const nvlist_t *nvl, const char *name); +bool nvlist_exists_nvlist_array(const nvlist_t *nvl, const char *name); +#ifndef _KERNEL +bool nvlist_exists_descriptor(const nvlist_t *nvl, const char *name); +bool nvlist_exists_descriptor_array(const nvlist_t *nvl, const char *name); +#endif + +/* + * The nvlist_add functions add the given name/value pair. + * If a pointer is provided, nvlist_add will internally allocate memory for the + * given data (in other words it won't consume provided buffer). + */ + +void nvlist_add_null(nvlist_t *nvl, const char *name); +void nvlist_add_bool(nvlist_t *nvl, const char *name, bool value); +void nvlist_add_number(nvlist_t *nvl, const char *name, uint64_t value); +void nvlist_add_string(nvlist_t *nvl, const char *name, const char *value); +void nvlist_add_stringf(nvlist_t *nvl, const char *name, const char *valuefmt, ...) __printflike(3, 4); +#ifdef _VA_LIST_DECLARED +void nvlist_add_stringv(nvlist_t *nvl, const char *name, const char *valuefmt, va_list valueap) __printflike(3, 0); +#endif +void nvlist_add_nvlist(nvlist_t *nvl, const char *name, const nvlist_t *value); +void nvlist_add_binary(nvlist_t *nvl, const char *name, const void *value, size_t size); +void nvlist_add_bool_array(nvlist_t *nvl, const char *name, const bool *value, size_t nitems); +void nvlist_add_number_array(nvlist_t *nvl, const char *name, const uint64_t *value, size_t nitems); +void nvlist_add_string_array(nvlist_t *nvl, const char *name, const char * const *value, size_t nitems); +void nvlist_add_nvlist_array(nvlist_t *nvl, const char *name, const nvlist_t * const *value, size_t nitems); +#ifndef _KERNEL +void nvlist_add_descriptor(nvlist_t *nvl, const char *name, int value); +void nvlist_add_descriptor_array(nvlist_t *nvl, const char *name, const int *value, size_t nitems); +#endif + +/* + * The nvlist_move functions add the given name/value pair. + * The functions consumes provided buffer. + */ + +void nvlist_move_string(nvlist_t *nvl, const char *name, char *value); +void nvlist_move_nvlist(nvlist_t *nvl, const char *name, nvlist_t *value); +void nvlist_move_binary(nvlist_t *nvl, const char *name, void *value, size_t size); +void nvlist_move_bool_array(nvlist_t *nvl, const char *name, bool *value, size_t nitems); +void nvlist_move_string_array(nvlist_t *nvl, const char *name, char **value, size_t nitems); +void nvlist_move_nvlist_array(nvlist_t *nvl, const char *name, nvlist_t **value, size_t nitems); +void nvlist_move_number_array(nvlist_t *nvl, const char *name, uint64_t *value, size_t nitems); +#ifndef _KERNEL +void nvlist_move_descriptor(nvlist_t *nvl, const char *name, int value); +void nvlist_move_descriptor_array(nvlist_t *nvl, const char *name, int *value, size_t nitems); +#endif + +/* + * The nvlist_get functions returns value associated with the given name. + * If it returns a pointer, the pointer represents internal buffer and should + * not be freed by the caller. + */ + +bool nvlist_get_bool(const nvlist_t *nvl, const char *name); +uint64_t nvlist_get_number(const nvlist_t *nvl, const char *name); +const char *nvlist_get_string(const nvlist_t *nvl, const char *name); +const nvlist_t *nvlist_get_nvlist(const nvlist_t *nvl, const char *name); +const void *nvlist_get_binary(const nvlist_t *nvl, const char *name, size_t *sizep); +const bool *nvlist_get_bool_array(const nvlist_t *nvl, const char *name, size_t *nitemsp); +const uint64_t *nvlist_get_number_array(const nvlist_t *nvl, const char *name, size_t *nitemsp); +const char * const *nvlist_get_string_array(const nvlist_t *nvl, const char *name, size_t *nitemsp); +const nvlist_t * const *nvlist_get_nvlist_array(const nvlist_t *nvl, const char *name, size_t *nitemsp); +#ifndef _KERNEL +int nvlist_get_descriptor(const nvlist_t *nvl, const char *name); +const int *nvlist_get_descriptor_array(const nvlist_t *nvl, const char *name, size_t *nitemsp); +#endif + +/* + * The nvlist_take functions returns value associated with the given name and + * remove the given entry from the nvlist. + * The caller is responsible for freeing received data. + */ + +bool nvlist_take_bool(nvlist_t *nvl, const char *name); +uint64_t nvlist_take_number(nvlist_t *nvl, const char *name); +char *nvlist_take_string(nvlist_t *nvl, const char *name); +nvlist_t *nvlist_take_nvlist(nvlist_t *nvl, const char *name); +void *nvlist_take_binary(nvlist_t *nvl, const char *name, size_t *sizep); +bool *nvlist_take_bool_array(nvlist_t *nvl, const char *name, size_t *nitemsp); +uint64_t *nvlist_take_number_array(nvlist_t *nvl, const char *name, size_t *nitemsp); +char **nvlist_take_string_array(nvlist_t *nvl, const char *name, size_t *nitemsp); +nvlist_t **nvlist_take_nvlist_array(nvlist_t *nvl, const char *name, size_t *nitemsp); +#ifndef _KERNEL +int nvlist_take_descriptor(nvlist_t *nvl, const char *name); +int *nvlist_take_descriptor_array(nvlist_t *nvl, const char *name, size_t *nitemsp); +#endif + +/* + * The nvlist_free functions removes the given name/value pair from the nvlist + * and frees memory associated with it. + */ + +void nvlist_free(nvlist_t *nvl, const char *name); +void nvlist_free_type(nvlist_t *nvl, const char *name, int type); + +void nvlist_free_null(nvlist_t *nvl, const char *name); +void nvlist_free_bool(nvlist_t *nvl, const char *name); +void nvlist_free_number(nvlist_t *nvl, const char *name); +void nvlist_free_string(nvlist_t *nvl, const char *name); +void nvlist_free_nvlist(nvlist_t *nvl, const char *name); +void nvlist_free_binary(nvlist_t *nvl, const char *name); +void nvlist_free_bool_array(nvlist_t *nvl, const char *name); +void nvlist_free_number_array(nvlist_t *nvl, const char *name); +void nvlist_free_string_array(nvlist_t *nvl, const char *name); +void nvlist_free_nvlist_array(nvlist_t *nvl, const char *name); +void nvlist_free_binary_array(nvlist_t *nvl, const char *name); +#ifndef _KERNEL +void nvlist_free_descriptor(nvlist_t *nvl, const char *name); +void nvlist_free_descriptor_array(nvlist_t *nvl, const char *name); +#endif + +__END_DECLS + +#endif /* !_NV_H_ */