diff --git a/sys/contrib/libnv/bsd_nvpair.c b/sys/contrib/libnv/bsd_nvpair.c index 0c76fefeebb6..9560ebc74f83 100644 --- a/sys/contrib/libnv/bsd_nvpair.c +++ b/sys/contrib/libnv/bsd_nvpair.c @@ -1,2137 +1,2137 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * 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$"); #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 __unused) { 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); } static int nvpair_append(nvpair_t *nvp, const void *value, size_t valsize, size_t datasize) { void *olddata, *data, *valp; size_t oldlen; oldlen = nvp->nvp_nitems * valsize; olddata = (void *)(uintptr_t)nvp->nvp_data; data = nv_realloc(olddata, oldlen + valsize); if (data == NULL) { ERRNO_SET(ENOMEM); return (-1); } valp = (unsigned char *)data + oldlen; memcpy(valp, value, valsize); nvp->nvp_data = (uint64_t)(uintptr_t)data; nvp->nvp_datasize += datasize; nvp->nvp_nitems++; return (0); } 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); nvlist_set_parent(nvlarray[i], NULL); } } void nvpair_remove(struct nvl_head *head, nvpair_t *nvp, const nvlist_t *nvl __unused) { 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 __unused, 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 __unused, 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 fail; memcpy(&nvphdr, ptr, sizeof(nvphdr)); ptr += sizeof(nvphdr); *leftp -= sizeof(nvphdr); #if NV_TYPE_FIRST > 0 if (nvphdr.nvph_type < NV_TYPE_FIRST) goto fail; #endif if (nvphdr.nvph_type > NV_TYPE_LAST && nvphdr.nvph_type != NV_TYPE_NVLIST_UP && nvphdr.nvph_type != NV_TYPE_NVLIST_ARRAY_NEXT) { goto fail; } #if BYTE_ORDER == BIG_ENDIAN if (!isbe) { nvphdr.nvph_namesize = le16toh(nvphdr.nvph_namesize); nvphdr.nvph_datasize = le64toh(nvphdr.nvph_datasize); nvphdr.nvph_nitems = le64toh(nvphdr.nvph_nitems); } #else if (isbe) { nvphdr.nvph_namesize = be16toh(nvphdr.nvph_namesize); nvphdr.nvph_datasize = be64toh(nvphdr.nvph_datasize); nvphdr.nvph_nitems = be64toh(nvphdr.nvph_nitems); } #endif if (nvphdr.nvph_namesize > NV_NAME_MAX) goto fail; if (*leftp < nvphdr.nvph_namesize) goto fail; if (nvphdr.nvph_namesize < 1) goto fail; if (strnlen((const char *)ptr, nvphdr.nvph_namesize) != (size_t)(nvphdr.nvph_namesize - 1)) { goto fail; } memcpy(nvp->nvp_name, ptr, nvphdr.nvph_namesize); ptr += nvphdr.nvph_namesize; *leftp -= nvphdr.nvph_namesize; if (*leftp < nvphdr.nvph_datasize) goto fail; nvp->nvp_type = nvphdr.nvph_type; nvp->nvp_data = 0; nvp->nvp_datasize = nvphdr.nvph_datasize; nvp->nvp_nitems = nvphdr.nvph_nitems; return (ptr); fail: 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); + value = nv_calloc(nvp->nvp_nitems, sizeof(*value)); 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)); + value = nv_calloc(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 fail; tmp = nv_realloc(nvp, sizeof(*nvp) + strlen(nvp->nvp_name) + 1); if (tmp == NULL) goto fail; 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); fail: 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); 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; 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); + data = nv_calloc(nitems, sizeof(value[0])); if (data == NULL) return (NULL); + size = sizeof(value[0]) * nitems; 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); + data = nv_calloc(nitems, sizeof(value[0])); if (data == NULL) return (NULL); + size = sizeof(value[0]) * nitems; 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); + data = nv_calloc(nitems, sizeof(value[0])); 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 *parent; int flags; nvls = NULL; if (value == NULL || nitems == 0) { ERRNO_SET(EINVAL); return (NULL); } - nvls = nv_malloc(sizeof(value[0]) * nitems); + nvls = nv_calloc(nitems, sizeof(value[0])); 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) { nvpair_t *nvp; nvp = nvpair_allocv(" ", NV_TYPE_NVLIST, (uint64_t)(uintptr_t)nvls[ii], 0, 0); if (nvp == NULL) { ERRNO_SAVE(); nvlist_destroy(nvls[ii]); ERRNO_RESTORE(); 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); parent = nvpair_allocv(name, NV_TYPE_NVLIST_ARRAY, (uint64_t)(uintptr_t)nvls, 0, nitems); if (parent == NULL) goto fail; for (ii = 0; ii < nitems; ii++) nvlist_set_parent(nvls[ii], parent); return (parent); fail: ERRNO_SAVE(); for (; ii > 0; ii--) nvlist_destroy(nvls[ii - 1]); nv_free(nvls); ERRNO_RESTORE(); return (NULL); } #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); + fds = nv_calloc(nitems, sizeof(value[0])); if (fds == NULL) return (NULL); for (ii = 0; ii < nitems; ii++) { if (value[ii] == -1) { fds[ii] = -1; } else { 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) { nvpair_t *parent; unsigned int ii; int flags; 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) { nvpair_t *nvp; 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); parent = nvpair_allocv(name, NV_TYPE_NVLIST_ARRAY, (uint64_t)(uintptr_t)value, 0, nitems); if (parent == NULL) goto fail; for (ii = 0; ii < nitems; ii++) nvlist_set_parent(value[ii], parent); return (parent); fail: 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(); return (NULL); } #ifndef _KERNEL nvpair_t * nvpair_move_descriptor_array(const char *name, int *value, size_t nitems) { nvpair_t *nvp; size_t i; 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); if (nvp == NULL) goto fail; return (nvp); fail: ERRNO_SAVE(); for (i = 0; i < nitems; i++) { if (fd_is_valid(value[i])) close(value[i]); } nv_free(value); ERRNO_RESTORE(); return (NULL); } #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 int nvpair_append_bool_array(nvpair_t *nvp, const bool value) { NVPAIR_ASSERT(nvp); PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_BOOL_ARRAY); return (nvpair_append(nvp, &value, sizeof(value), sizeof(value))); } int nvpair_append_number_array(nvpair_t *nvp, const uint64_t value) { NVPAIR_ASSERT(nvp); PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_NUMBER_ARRAY); return (nvpair_append(nvp, &value, sizeof(value), sizeof(value))); } int nvpair_append_string_array(nvpair_t *nvp, const char *value) { char *str; NVPAIR_ASSERT(nvp); PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_STRING_ARRAY); if (value == NULL) { ERRNO_SET(EINVAL); return (-1); } str = nv_strdup(value); if (str == NULL) { return (-1); } if (nvpair_append(nvp, &str, sizeof(str), strlen(str) + 1) == -1) { nv_free(str); return (-1); } return (0); } int nvpair_append_nvlist_array(nvpair_t *nvp, const nvlist_t *value) { nvpair_t *tmpnvp; nvlist_t *nvl, *prev; int flags; NVPAIR_ASSERT(nvp); PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_NVLIST_ARRAY); if (value == NULL || nvlist_error(value) != 0 || nvlist_get_pararr(value, NULL) != NULL) { ERRNO_SET(EINVAL); return (-1); } nvl = nvlist_clone(value); if (nvl == NULL) { return (-1); } flags = nvlist_flags(nvl) | NV_FLAG_IN_ARRAY; nvlist_set_flags(nvl, flags); tmpnvp = NULL; prev = NULL; if (nvp->nvp_nitems > 0) { nvlist_t **nvls = (void *)(uintptr_t)nvp->nvp_data; prev = nvls[nvp->nvp_nitems - 1]; PJDLOG_ASSERT(prev != NULL); tmpnvp = nvpair_allocv(" ", NV_TYPE_NVLIST, (uint64_t)(uintptr_t)nvl, 0, 0); if (tmpnvp == NULL) { goto fail; } } if (nvpair_append(nvp, &nvl, sizeof(nvl), 0) == -1) { goto fail; } if (tmpnvp) { NVPAIR_ASSERT(tmpnvp); nvlist_set_array_next(prev, tmpnvp); } nvlist_set_parent(nvl, nvp); return (0); fail: if (tmpnvp) { nvpair_free(tmpnvp); } nvlist_destroy(nvl); return (-1); } #ifndef _KERNEL int nvpair_append_descriptor_array(nvpair_t *nvp, const int value) { int fd; NVPAIR_ASSERT(nvp); PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_DESCRIPTOR_ARRAY); fd = fcntl(value, F_DUPFD_CLOEXEC, 0); if (fd == -1) { return (-1); } if (nvpair_append(nvp, &fd, sizeof(fd), sizeof(fd)) == -1) { close(fd); return (-1); } return (0); } #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]); nv_free((int *)(intptr_t)nvp->nvp_data); 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]); nv_free((char **)(intptr_t)nvp->nvp_data); 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 (""); } } diff --git a/sys/contrib/libnv/nvlist.c b/sys/contrib/libnv/nvlist.c index 57343f953e94..64078b10973e 100644 --- a/sys/contrib/libnv/nvlist.c +++ b/sys/contrib/libnv/nvlist.c @@ -1,2061 +1,2065 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * 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$"); #include #include #include #ifdef _KERNEL #include #include #include #include #include #include #else #include #include #include #include #include #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; size_t nvl_datasize; 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; nvl->nvl_datasize = sizeof(struct nvlist_header); 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; nv_free(nvl->nvl_array_next); } nvl->nvl_array_next = ele; } static void nvlist_update_size(nvlist_t *nvl, nvpair_t *new, ssize_t mul) { ssize_t size; size_t nitems; const nvlist_t *nvlistnew; const nvlist_t * const *nvlarray; nvlist_t *parent; unsigned int ii; NVLIST_ASSERT(nvl); NVPAIR_ASSERT(new); PJDLOG_ASSERT(mul == 1 || mul == -1); size = nvpair_header_size(); size += strlen(nvpair_name(new)) + 1; if (nvpair_type(new) == NV_TYPE_NVLIST) { nvlistnew = nvpair_get_nvlist(new); size += nvlistnew->nvl_datasize; size += nvpair_header_size() + 1; } else if (nvpair_type(new) == NV_TYPE_NVLIST_ARRAY) { nvlarray = nvpair_get_nvlist_array(new, &nitems); PJDLOG_ASSERT(nitems > 0); size += (nvpair_header_size() + 1) * nitems; for (ii = 0; ii < nitems; ii++) { PJDLOG_ASSERT(nvlarray[ii]->nvl_error == 0); size += nvlarray[ii]->nvl_datasize; } } else { size += nvpair_size(new); } size *= mul; nvl->nvl_datasize += size; parent = nvl; while ((parent = __DECONST(nvlist_t *, nvlist_get_parent(parent, NULL))) != NULL) { parent->nvl_datasize += size; } } nvpair_t * nvlist_get_array_next_nvpair(nvlist_t *nvl) { NVLIST_ASSERT(nvl); return (nvl->nvl_array_next); } 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); } return (nvlist_get_parent(nvl, cookiep)); } 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; } 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) { return (nvl->nvl_datasize); } #ifndef _KERNEL static int * nvlist_xdescriptors(const nvlist_t *nvl, int *descs) { void *cookie; nvpair_t *nvp; int type; NVLIST_ASSERT(nvl); PJDLOG_ASSERT(nvl->nvl_error == 0); cookie = NULL; do { while (nvlist_next(nvl, &type, &cookie) != NULL) { nvp = cookie; 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); cookie = 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]; cookie = NULL; break; } } } } while ((nvl = nvlist_get_pararr(nvl, &cookie)) != 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)); + fds = nv_calloc(nitems + 1, sizeof(fds[0])); 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 void *cookie; nvpair_t *nvp; size_t ndescs; int type; NVLIST_ASSERT(nvl); PJDLOG_ASSERT(nvl->nvl_error == 0); ndescs = 0; cookie = NULL; do { while (nvlist_next(nvl, &type, &cookie) != NULL) { nvp = cookie; switch (type) { case NV_TYPE_DESCRIPTOR: ndescs++; break; case NV_TYPE_NVLIST: nvl = nvpair_get_nvlist(nvp); cookie = 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]; cookie = 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, &cookie)) != 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_size > SIZE_MAX - sizeof(nvlhdrp)) { + ERRNO_SET(EINVAL); + return (false); + } 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 fail; memcpy(&nvlhdr, ptr, sizeof(nvlhdr)); if (!nvlist_check_header(&nvlhdr)) goto fail; if (nvlhdr.nvlh_size != *leftp - sizeof(nvlhdr)) goto fail; /* * nvlh_descriptors might be smaller than nfds in embedded nvlists. */ if (nvlhdr.nvlh_descriptors > nfds) goto fail; if ((nvlhdr.nvlh_flags & ~NV_FLAG_ALL_MASK) != 0) goto fail; 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); fail: 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 fail; ptr = nvlist_unpack_header(nvl, ptr, nfds, &isbe, &left); if (ptr == NULL) goto fail; if (nvl->nvl_flags != flags) { ERRNO_SET(EILSEQ); goto fail; } while (left > 0) { ptr = nvpair_unpack(isbe, ptr, &left, &nvp); if (ptr == NULL) goto fail; 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 fail; 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 fail; 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 fail; 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 fail; } 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 fail; PJDLOG_ASSERT(array != NULL); tmpnvl = array; do { nvlist_set_parent(array, nvp); array = __DECONST(nvlist_t *, nvlist_get_array_next(array)); } while (array != NULL); ptr = nvlist_unpack_header(tmpnvl, ptr, nfds, &isbe, &left); break; default: PJDLOG_ABORT("Invalid type (%d).", nvpair_type(nvp)); } if (ptr == NULL) goto fail; if (!nvlist_move_nvpair(nvl, nvp)) goto fail; if (tmpnvl != NULL) { nvl = tmpnvl; tmpnvl = NULL; } } return (retnvl); fail: 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; 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, offset; int *fds, soflags, sotype; socklen_t solen; solen = sizeof(sotype); if (getsockopt(sock, SOL_SOCKET, SO_TYPE, &sotype, &solen) != 0) return (NULL); soflags = sotype == SOCK_DGRAM ? MSG_PEEK : 0; if (buf_recv(sock, &nvlhdr, sizeof(nvlhdr), soflags) == -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); ret = NULL; fds = NULL; if (sotype == SOCK_DGRAM) offset = 0; else { memcpy(buf, &nvlhdr, sizeof(nvlhdr)); offset = sizeof(nvlhdr); } if (buf_recv(sock, buf + offset, size - offset, 0) == -1) goto out; if (nfds > 0) { - fds = nv_malloc(nfds * sizeof(fds[0])); + fds = nv_calloc(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 __unused, 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 __unused, 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); nvlist_update_size(nvl, newnvp, 1); } 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 #define NVLIST_APPEND_ARRAY(vtype, type, TYPE) \ void \ nvlist_append_##type##_array(nvlist_t *nvl, const char *name, vtype value)\ { \ nvpair_t *nvp; \ \ if (nvlist_error(nvl) != 0) { \ ERRNO_SET(nvlist_error(nvl)); \ return; \ } \ nvp = nvlist_find(nvl, NV_TYPE_##TYPE##_ARRAY, name); \ if (nvp == NULL) { \ nvlist_add_##type##_array(nvl, name, &value, 1); \ return; \ } \ nvlist_update_size(nvl, nvp, -1); \ if (nvpair_append_##type##_array(nvp, value) == -1) { \ nvl->nvl_error = ERRNO_OR_DEFAULT(ENOMEM); \ ERRNO_SET(nvl->nvl_error); \ } \ nvlist_update_size(nvl, nvp, 1); \ } NVLIST_APPEND_ARRAY(const bool, bool, BOOL) NVLIST_APPEND_ARRAY(const uint64_t, number, NUMBER) NVLIST_APPEND_ARRAY(const char *, string, STRING) NVLIST_APPEND_ARRAY(const nvlist_t *, nvlist, NVLIST) #ifndef _KERNEL NVLIST_APPEND_ARRAY(const int, descriptor, DESCRIPTOR) #endif #undef NVLIST_APPEND_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); nvlist_update_size(nvl, nvp, 1); 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); nvlist_update_size(nvl, nvp, -1); } 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); }