/*- * Copyright 2020 Toomas Soome * * 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$"); #include #include #include #include #include #include #include "libzfs.h" enum xdr_op { XDR_OP_ENCODE = 1, XDR_OP_DECODE = 2 }; typedef struct xdr { enum xdr_op xdr_op; int (*xdr_getint)(struct xdr *, int *); int (*xdr_putint)(struct xdr *, int); int (*xdr_getuint)(struct xdr *, unsigned *); int (*xdr_putuint)(struct xdr *, unsigned); const uint8_t *xdr_buf; uint8_t *xdr_idx; size_t xdr_buf_size; } xdr_t; static int nvlist_xdr_nvlist(xdr_t *, nvlist_t *); static bool nvlist_size_xdr(xdr_t *, size_t *); static bool nvlist_size_native(xdr_t *, size_t *); static bool xdr_int(xdr_t *, int *); static bool xdr_u_int(xdr_t *, unsigned *); typedef bool (*xdrproc_t)(xdr_t *, void *); /* Basic primitives for XDR translation operations, getint and putint. */ static int _getint(struct xdr *xdr, int *ip) { *ip = be32dec(xdr->xdr_idx); return (sizeof(int)); } static int _putint(struct xdr *xdr, int i) { int *ip = (int *)xdr->xdr_idx; *ip = htobe32(i); return (sizeof(int)); } static int _getuint(struct xdr *xdr, unsigned *ip) { *ip = be32dec(xdr->xdr_idx); return (sizeof(unsigned)); } static int _putuint(struct xdr *xdr, unsigned i) { unsigned *up = (unsigned *)xdr->xdr_idx; *up = htobe32(i); return (sizeof(int)); } static int _getint_mem(struct xdr *xdr, int *ip) { *ip = *(int *)xdr->xdr_idx; return (sizeof(int)); } static int _putint_mem(struct xdr *xdr, int i) { int *ip = (int *)xdr->xdr_idx; *ip = i; return (sizeof(int)); } static int _getuint_mem(struct xdr *xdr, unsigned *ip) { *ip = *(unsigned *)xdr->xdr_idx; return (sizeof(unsigned)); } static int _putuint_mem(struct xdr *xdr, unsigned i) { unsigned *up = (unsigned *)xdr->xdr_idx; *up = i; return (sizeof(int)); } /* * XDR data translations. */ static bool xdr_short(xdr_t *xdr, short *ip) { int i; bool rv; i = *ip; if ((rv = xdr_int(xdr, &i))) { if (xdr->xdr_op == XDR_OP_DECODE) *ip = i; } return (rv); } static bool xdr_u_short(xdr_t *xdr, unsigned short *ip) { unsigned u; bool rv; u = *ip; if ((rv = xdr_u_int(xdr, &u))) { if (xdr->xdr_op == XDR_OP_DECODE) *ip = u; } return (rv); } /* * translate xdr->xdr_idx, increment it by size of int. */ static bool xdr_int(xdr_t *xdr, int *ip) { bool rv = false; int *i = (int *)xdr->xdr_idx; if (xdr->xdr_idx + sizeof(int) > xdr->xdr_buf + xdr->xdr_buf_size) return (rv); switch (xdr->xdr_op) { case XDR_OP_ENCODE: /* Encode value *ip, store to buf */ xdr->xdr_idx += xdr->xdr_putint(xdr, *ip); rv = true; break; case XDR_OP_DECODE: /* Decode buf, return value to *ip */ xdr->xdr_idx += xdr->xdr_getint(xdr, i); *ip = *i; rv = true; break; } return (rv); } /* * translate xdr->xdr_idx, increment it by size of unsigned int. */ static bool xdr_u_int(xdr_t *xdr, unsigned *ip) { bool rv = false; unsigned *u = (unsigned *)xdr->xdr_idx; if (xdr->xdr_idx + sizeof(unsigned) > xdr->xdr_buf + xdr->xdr_buf_size) return (rv); switch (xdr->xdr_op) { case XDR_OP_ENCODE: /* Encode value *ip, store to buf */ xdr->xdr_idx += xdr->xdr_putuint(xdr, *ip); rv = true; break; case XDR_OP_DECODE: /* Decode buf, return value to *ip */ xdr->xdr_idx += xdr->xdr_getuint(xdr, u); *ip = *u; rv = true; break; } return (rv); } static bool xdr_int64(xdr_t *xdr, int64_t *lp) { bool rv = false; if (xdr->xdr_idx + sizeof(int64_t) > xdr->xdr_buf + xdr->xdr_buf_size) return (rv); switch (xdr->xdr_op) { case XDR_OP_ENCODE: /* Encode value *lp, store to buf */ if (xdr->xdr_putint == _putint) *(int64_t *)xdr->xdr_idx = htobe64(*lp); else *(int64_t *)xdr->xdr_idx = *lp; xdr->xdr_idx += sizeof(int64_t); rv = true; break; case XDR_OP_DECODE: /* Decode buf, return value to *ip */ if (xdr->xdr_getint == _getint) *lp = be64toh(*(int64_t *)xdr->xdr_idx); else *lp = *(int64_t *)xdr->xdr_idx; xdr->xdr_idx += sizeof(int64_t); rv = true; } return (rv); } static bool xdr_uint64(xdr_t *xdr, uint64_t *lp) { bool rv = false; if (xdr->xdr_idx + sizeof(uint64_t) > xdr->xdr_buf + xdr->xdr_buf_size) return (rv); switch (xdr->xdr_op) { case XDR_OP_ENCODE: /* Encode value *ip, store to buf */ if (xdr->xdr_putint == _putint) *(uint64_t *)xdr->xdr_idx = htobe64(*lp); else *(uint64_t *)xdr->xdr_idx = *lp; xdr->xdr_idx += sizeof(uint64_t); rv = true; break; case XDR_OP_DECODE: /* Decode buf, return value to *ip */ if (xdr->xdr_getuint == _getuint) *lp = be64toh(*(uint64_t *)xdr->xdr_idx); else *lp = *(uint64_t *)xdr->xdr_idx; xdr->xdr_idx += sizeof(uint64_t); rv = true; } return (rv); } static bool xdr_char(xdr_t *xdr, char *cp) { int i; bool rv = false; i = *cp; if ((rv = xdr_int(xdr, &i))) { if (xdr->xdr_op == XDR_OP_DECODE) *cp = i; } return (rv); } static bool xdr_string(xdr_t *xdr, nv_string_t *s) { int size = 0; bool rv = false; switch (xdr->xdr_op) { case XDR_OP_ENCODE: size = s->nv_size; if (xdr->xdr_idx + sizeof(unsigned) + NV_ALIGN4(size) > xdr->xdr_buf + xdr->xdr_buf_size) break; xdr->xdr_idx += xdr->xdr_putuint(xdr, s->nv_size); xdr->xdr_idx += NV_ALIGN4(size); rv = true; break; case XDR_OP_DECODE: if (xdr->xdr_idx + sizeof(unsigned) > xdr->xdr_buf + xdr->xdr_buf_size) break; size = xdr->xdr_getuint(xdr, &s->nv_size); size = NV_ALIGN4(size + s->nv_size); if (xdr->xdr_idx + size > xdr->xdr_buf + xdr->xdr_buf_size) break; xdr->xdr_idx += size; rv = true; break; } return (rv); } static bool xdr_array(xdr_t *xdr, const unsigned nelem, const xdrproc_t elproc) { bool rv = true; unsigned c = nelem; if (!xdr_u_int(xdr, &c)) return (false); for (unsigned i = 0; i < nelem; i++) { if (!elproc(xdr, xdr->xdr_idx)) return (false); } return (rv); } /* * nvlist management functions. */ void nvlist_destroy(nvlist_t *nvl) { if (nvl != NULL) { /* Free data if it was allocated by us. */ if (nvl->nv_asize > 0) free(nvl->nv_data); } free(nvl); } char * nvstring_get(nv_string_t *nvs) { char *s; s = malloc(nvs->nv_size + 1); if (s != NULL) { bcopy(nvs->nv_data, s, nvs->nv_size); s[nvs->nv_size] = '\0'; } return (s); } /* * Create empty nvlist. * The nvlist is terminated by 2x zeros (8 bytes). */ nvlist_t * nvlist_create(int flag) { nvlist_t *nvl; nvs_data_t *nvs; nvl = calloc(1, sizeof(*nvl)); if (nvl == NULL) return (nvl); nvl->nv_header.nvh_encoding = NV_ENCODE_XDR; nvl->nv_header.nvh_endian = _BYTE_ORDER == _LITTLE_ENDIAN; nvl->nv_asize = nvl->nv_size = sizeof(*nvs); nvs = calloc(1, nvl->nv_asize); if (nvs == NULL) { free(nvl); return (NULL); } /* data in nvlist is byte stream */ nvl->nv_data = (uint8_t *)nvs; nvs->nvl_version = NV_VERSION; nvs->nvl_nvflag = flag; return (nvl); } static bool nvlist_xdr_nvp(xdr_t *xdr, nvlist_t *nvl) { nv_string_t *nv_string; nv_pair_data_t *nvp_data; nvlist_t nvlist; unsigned type, nelem; xdr_t nv_xdr; nv_string = (nv_string_t *)xdr->xdr_idx; if (!xdr_string(xdr, nv_string)) { return (false); } nvp_data = (nv_pair_data_t *)xdr->xdr_idx; type = nvp_data->nv_type; nelem = nvp_data->nv_nelem; if (!xdr_u_int(xdr, &type) || !xdr_u_int(xdr, &nelem)) return (false); switch (type) { case DATA_TYPE_NVLIST: case DATA_TYPE_NVLIST_ARRAY: bzero(&nvlist, sizeof(nvlist)); nvlist.nv_data = xdr->xdr_idx; nvlist.nv_idx = nvlist.nv_data; /* Set up xdr for this nvlist. */ nv_xdr = *xdr; nv_xdr.xdr_buf = nvlist.nv_data; nv_xdr.xdr_idx = nvlist.nv_data; nv_xdr.xdr_buf_size = nvl->nv_data + nvl->nv_size - nvlist.nv_data; for (unsigned i = 0; i < nelem; i++) { if (xdr->xdr_op == XDR_OP_ENCODE) { if (!nvlist_size_native(&nv_xdr, &nvlist.nv_size)) return (false); } else { if (!nvlist_size_xdr(&nv_xdr, &nvlist.nv_size)) return (false); } if (nvlist_xdr_nvlist(xdr, &nvlist) != 0) return (false); nvlist.nv_data = nv_xdr.xdr_idx; nvlist.nv_idx = nv_xdr.xdr_idx; nv_xdr.xdr_buf = nv_xdr.xdr_idx; nv_xdr.xdr_buf_size = nvl->nv_data + nvl->nv_size - nvlist.nv_data; } break; case DATA_TYPE_BOOLEAN: /* BOOLEAN does not take value space */ break; case DATA_TYPE_BYTE: case DATA_TYPE_INT8: case DATA_TYPE_UINT8: if (!xdr_char(xdr, (char *)&nvp_data->nv_data[0])) return (false); break; case DATA_TYPE_INT16: if (!xdr_short(xdr, (short *)&nvp_data->nv_data[0])) return (false); break; case DATA_TYPE_UINT16: if (!xdr_u_short(xdr, (unsigned short *)&nvp_data->nv_data[0])) return (false); break; case DATA_TYPE_BOOLEAN_VALUE: case DATA_TYPE_INT32: if (!xdr_int(xdr, (int *)&nvp_data->nv_data[0])) return (false); break; case DATA_TYPE_UINT32: if (!xdr_u_int(xdr, (unsigned *)&nvp_data->nv_data[0])) return (false); break; case DATA_TYPE_HRTIME: case DATA_TYPE_INT64: if (!xdr_int64(xdr, (int64_t *)&nvp_data->nv_data[0])) return (false); break; case DATA_TYPE_UINT64: if (!xdr_uint64(xdr, (uint64_t *)&nvp_data->nv_data[0])) return (false); break; case DATA_TYPE_BYTE_ARRAY: case DATA_TYPE_STRING: nv_string = (nv_string_t *)&nvp_data->nv_data[0]; if (!xdr_string(xdr, nv_string)) return (false); break; case DATA_TYPE_STRING_ARRAY: nv_string = (nv_string_t *)&nvp_data->nv_data[0]; for (unsigned i = 0; i < nelem; i++) { if (!xdr_string(xdr, nv_string)) return (false); nv_string = (nv_string_t *)xdr->xdr_idx; } break; case DATA_TYPE_INT8_ARRAY: case DATA_TYPE_UINT8_ARRAY: case DATA_TYPE_INT16_ARRAY: case DATA_TYPE_UINT16_ARRAY: case DATA_TYPE_BOOLEAN_ARRAY: case DATA_TYPE_INT32_ARRAY: case DATA_TYPE_UINT32_ARRAY: if (!xdr_array(xdr, nelem, (xdrproc_t)xdr_u_int)) return (false); break; case DATA_TYPE_INT64_ARRAY: case DATA_TYPE_UINT64_ARRAY: if (!xdr_array(xdr, nelem, (xdrproc_t)xdr_uint64)) return (false); break; } return (true); } static int nvlist_xdr_nvlist(xdr_t *xdr, nvlist_t *nvl) { nvp_header_t *nvph; nvs_data_t *nvs; unsigned encoded_size, decoded_size; int rv; nvs = (nvs_data_t *)xdr->xdr_idx; nvph = &nvs->nvl_pair; if (!xdr_u_int(xdr, &nvs->nvl_version)) return (EINVAL); if (!xdr_u_int(xdr, &nvs->nvl_nvflag)) return (EINVAL); encoded_size = nvph->encoded_size; decoded_size = nvph->decoded_size; if (xdr->xdr_op == XDR_OP_ENCODE) { if (!xdr_u_int(xdr, &nvph->encoded_size)) return (EINVAL); if (!xdr_u_int(xdr, &nvph->decoded_size)) return (EINVAL); } else { xdr->xdr_idx += 2 * sizeof(unsigned); } rv = 0; while (encoded_size && decoded_size) { if (!nvlist_xdr_nvp(xdr, nvl)) return (EINVAL); nvph = (nvp_header_t *)(xdr->xdr_idx); encoded_size = nvph->encoded_size; decoded_size = nvph->decoded_size; if (xdr->xdr_op == XDR_OP_ENCODE) { if (!xdr_u_int(xdr, &nvph->encoded_size)) return (EINVAL); if (!xdr_u_int(xdr, &nvph->decoded_size)) return (EINVAL); } else { xdr->xdr_idx += 2 * sizeof(unsigned); } } return (rv); } /* * Calculate nvlist size, translating encoded_size and decoded_size. */ static bool nvlist_size_xdr(xdr_t *xdr, size_t *size) { uint8_t *pair; unsigned encoded_size, decoded_size; xdr->xdr_idx += 2 * sizeof(unsigned); pair = xdr->xdr_idx; if (!xdr_u_int(xdr, &encoded_size) || !xdr_u_int(xdr, &decoded_size)) return (false); while (encoded_size && decoded_size) { xdr->xdr_idx = pair + encoded_size; pair = xdr->xdr_idx; if (!xdr_u_int(xdr, &encoded_size) || !xdr_u_int(xdr, &decoded_size)) return (false); } *size = xdr->xdr_idx - xdr->xdr_buf; return (true); } nvp_header_t * nvlist_next_nvpair(nvlist_t *nvl, nvp_header_t *nvh) { uint8_t *pair; unsigned encoded_size, decoded_size; xdr_t xdr; if (nvl == NULL) return (NULL); xdr.xdr_buf = nvl->nv_data; xdr.xdr_idx = nvl->nv_data; xdr.xdr_buf_size = nvl->nv_size; xdr.xdr_idx += 2 * sizeof(unsigned); /* Skip tp current pair */ if (nvh != NULL) { xdr.xdr_idx = (uint8_t *)nvh; } pair = xdr.xdr_idx; if (xdr.xdr_idx > xdr.xdr_buf + xdr.xdr_buf_size) return (NULL); encoded_size = *(unsigned *)xdr.xdr_idx; xdr.xdr_idx += sizeof(unsigned); if (xdr.xdr_idx > xdr.xdr_buf + xdr.xdr_buf_size) return (NULL); decoded_size = *(unsigned *)xdr.xdr_idx; xdr.xdr_idx += sizeof(unsigned); if (xdr.xdr_idx > xdr.xdr_buf + xdr.xdr_buf_size) return (NULL); while (encoded_size && decoded_size) { if (nvh == NULL) return ((nvp_header_t *)pair); xdr.xdr_idx = pair + encoded_size; nvh = (nvp_header_t *)xdr.xdr_idx; if (xdr.xdr_idx > xdr.xdr_buf + xdr.xdr_buf_size) return (NULL); encoded_size = *(unsigned *)xdr.xdr_idx; xdr.xdr_idx += sizeof(unsigned); if (xdr.xdr_idx > xdr.xdr_buf + xdr.xdr_buf_size) return (NULL); decoded_size = *(unsigned *)xdr.xdr_idx; xdr.xdr_idx += sizeof(unsigned); if (xdr.xdr_idx > xdr.xdr_buf + xdr.xdr_buf_size) return (NULL); if (encoded_size != 0 && decoded_size != 0) { return (nvh); } } return (NULL); } /* * Calculate nvlist size by walking in memory data. */ static bool nvlist_size_native(xdr_t *xdr, size_t *size) { uint8_t *pair; unsigned encoded_size, decoded_size; xdr->xdr_idx += 2 * sizeof(unsigned); pair = xdr->xdr_idx; if (xdr->xdr_idx > xdr->xdr_buf + xdr->xdr_buf_size) return (false); encoded_size = *(unsigned *)xdr->xdr_idx; xdr->xdr_idx += sizeof(unsigned); if (xdr->xdr_idx > xdr->xdr_buf + xdr->xdr_buf_size) return (false); decoded_size = *(unsigned *)xdr->xdr_idx; xdr->xdr_idx += sizeof(unsigned); while (encoded_size && decoded_size) { xdr->xdr_idx = pair + encoded_size; pair = xdr->xdr_idx; if (xdr->xdr_idx > xdr->xdr_buf + xdr->xdr_buf_size) return (false); encoded_size = *(unsigned *)xdr->xdr_idx; xdr->xdr_idx += sizeof(unsigned); if (xdr->xdr_idx > xdr->xdr_buf + xdr->xdr_buf_size) return (false); decoded_size = *(unsigned *)xdr->xdr_idx; xdr->xdr_idx += sizeof(unsigned); } *size = xdr->xdr_idx - xdr->xdr_buf; return (true); } /* * Export nvlist to byte stream format. */ int nvlist_export(nvlist_t *nvl) { int rv; xdr_t xdr = { .xdr_op = XDR_OP_ENCODE, .xdr_putint = _putint, .xdr_putuint = _putuint, .xdr_buf = nvl->nv_data, .xdr_idx = nvl->nv_data, .xdr_buf_size = nvl->nv_size }; if (nvl->nv_header.nvh_encoding != NV_ENCODE_XDR) return (ENOTSUP); nvl->nv_idx = nvl->nv_data; rv = nvlist_xdr_nvlist(&xdr, nvl); return (rv); } /* * Import nvlist from byte stream. * Determine the stream size and allocate private copy. * Then translate the data. */ nvlist_t * nvlist_import(const char *stream, size_t size) { nvlist_t *nvl; xdr_t xdr = { .xdr_op = XDR_OP_DECODE, .xdr_getint = _getint, .xdr_getuint = _getuint }; /* Check the nvlist head. */ if (stream[0] != NV_ENCODE_XDR || (stream[1] != '\0' && stream[1] != '\1') || stream[2] != '\0' || stream[3] != '\0' || be32toh(*(uint32_t *)(stream + 4)) != NV_VERSION || be32toh(*(uint32_t *)(stream + 8)) != NV_UNIQUE_NAME) return (NULL); nvl = malloc(sizeof(*nvl)); if (nvl == NULL) return (nvl); nvl->nv_header.nvh_encoding = stream[0]; nvl->nv_header.nvh_endian = stream[1]; nvl->nv_header.nvh_reserved1 = stream[2]; nvl->nv_header.nvh_reserved2 = stream[3]; xdr.xdr_buf = xdr.xdr_idx = (uint8_t *)stream + 4; xdr.xdr_buf_size = size - 4; if (!nvlist_size_xdr(&xdr, &nvl->nv_asize)) { free(nvl); return (NULL); } nvl->nv_size = nvl->nv_asize; nvl->nv_data = malloc(nvl->nv_asize); if (nvl->nv_data == NULL) { free(nvl); return (NULL); } nvl->nv_idx = nvl->nv_data; bcopy(stream + 4, nvl->nv_data, nvl->nv_asize); xdr.xdr_buf = xdr.xdr_idx = nvl->nv_data; xdr.xdr_buf_size = nvl->nv_asize; if (nvlist_xdr_nvlist(&xdr, nvl) != 0) { free(nvl->nv_data); free(nvl); nvl = NULL; } return (nvl); } /* * remove pair from this nvlist. */ int nvlist_remove(nvlist_t *nvl, const char *name, data_type_t type) { uint8_t *head, *tail; nvs_data_t *data; nvp_header_t *nvp; nv_string_t *nvp_name; nv_pair_data_t *nvp_data; size_t size; xdr_t xdr; if (nvl == NULL || nvl->nv_data == NULL || name == NULL) return (EINVAL); /* Make sure the nvlist size is set correct */ xdr.xdr_idx = nvl->nv_data; xdr.xdr_buf = xdr.xdr_idx; xdr.xdr_buf_size = nvl->nv_size; if (!nvlist_size_native(&xdr, &nvl->nv_size)) return (EINVAL); data = (nvs_data_t *)nvl->nv_data; nvp = &data->nvl_pair; /* first pair in nvlist */ head = (uint8_t *)nvp; while (nvp->encoded_size != 0 && nvp->decoded_size != 0) { nvp_name = (nv_string_t *)(nvp + 1); nvp_data = (nv_pair_data_t *)(&nvp_name->nv_data[0] + NV_ALIGN4(nvp_name->nv_size)); if (strlen(name) == nvp_name->nv_size && memcmp(nvp_name->nv_data, name, nvp_name->nv_size) == 0 && (nvp_data->nv_type == type || type == DATA_TYPE_UNKNOWN)) { /* * set tail to point to next nvpair and size * is the length of the tail. */ tail = head + nvp->encoded_size; size = nvl->nv_size - (tail - nvl->nv_data); /* adjust the size of the nvlist. */ nvl->nv_size -= nvp->encoded_size; bcopy(tail, head, size); return (0); } /* Not our pair, skip to next. */ head = head + nvp->encoded_size; nvp = (nvp_header_t *)head; } return (ENOENT); } static int clone_nvlist(const nvlist_t *nvl, const uint8_t *ptr, unsigned size, nvlist_t **nvlist) { nvlist_t *nv; nv = calloc(1, sizeof(*nv)); if (nv == NULL) return (ENOMEM); nv->nv_header = nvl->nv_header; nv->nv_asize = size; nv->nv_size = size; nv->nv_data = malloc(nv->nv_asize); if (nv->nv_data == NULL) { free(nv); return (ENOMEM); } bcopy(ptr, nv->nv_data, nv->nv_asize); *nvlist = nv; return (0); } /* * Return the next nvlist in an nvlist array. */ static uint8_t * nvlist_next(const uint8_t *ptr) { nvs_data_t *data; nvp_header_t *nvp; data = (nvs_data_t *)ptr; nvp = &data->nvl_pair; /* first pair in nvlist */ while (nvp->encoded_size != 0 && nvp->decoded_size != 0) { nvp = (nvp_header_t *)((uint8_t *)nvp + nvp->encoded_size); } return ((uint8_t *)nvp + sizeof(*nvp)); } /* * Note: nvlist and nvlist array must be freed by caller. */ int nvlist_find(const nvlist_t *nvl, const char *name, data_type_t type, int *elementsp, void *valuep, int *sizep) { nvs_data_t *data; nvp_header_t *nvp; nv_string_t *nvp_name; nv_pair_data_t *nvp_data; nvlist_t **nvlist, *nv; uint8_t *ptr; int rv; if (nvl == NULL || nvl->nv_data == NULL || name == NULL) return (EINVAL); data = (nvs_data_t *)nvl->nv_data; nvp = &data->nvl_pair; /* first pair in nvlist */ while (nvp->encoded_size != 0 && nvp->decoded_size != 0) { nvp_name = (nv_string_t *)((uint8_t *)nvp + sizeof(*nvp)); if (nvl->nv_data + nvl->nv_size < nvp_name->nv_data + nvp_name->nv_size) return (EIO); nvp_data = (nv_pair_data_t *) NV_ALIGN4((uintptr_t)&nvp_name->nv_data[0] + nvp_name->nv_size); if (strlen(name) == nvp_name->nv_size && memcmp(nvp_name->nv_data, name, nvp_name->nv_size) == 0 && (nvp_data->nv_type == type || type == DATA_TYPE_UNKNOWN)) { if (elementsp != NULL) *elementsp = nvp_data->nv_nelem; switch (nvp_data->nv_type) { case DATA_TYPE_UINT64: bcopy(nvp_data->nv_data, valuep, sizeof(uint64_t)); return (0); case DATA_TYPE_STRING: nvp_name = (nv_string_t *)nvp_data->nv_data; if (sizep != NULL) { *sizep = nvp_name->nv_size; } *(const uint8_t **)valuep = &nvp_name->nv_data[0]; return (0); case DATA_TYPE_NVLIST: ptr = &nvp_data->nv_data[0]; rv = clone_nvlist(nvl, ptr, nvlist_next(ptr) - ptr, &nv); if (rv == 0) { *(nvlist_t **)valuep = nv; } return (rv); case DATA_TYPE_NVLIST_ARRAY: nvlist = calloc(nvp_data->nv_nelem, sizeof(nvlist_t *)); if (nvlist == NULL) return (ENOMEM); ptr = &nvp_data->nv_data[0]; rv = 0; for (unsigned i = 0; i < nvp_data->nv_nelem; i++) { rv = clone_nvlist(nvl, ptr, nvlist_next(ptr) - ptr, &nvlist[i]); if (rv != 0) goto error; ptr = nvlist_next(ptr); } *(nvlist_t ***)valuep = nvlist; return (rv); } return (EIO); } /* Not our pair, skip to next. */ nvp = (nvp_header_t *)((uint8_t *)nvp + nvp->encoded_size); if (nvl->nv_data + nvl->nv_size < (uint8_t *)nvp) return (EIO); } return (ENOENT); error: for (unsigned i = 0; i < nvp_data->nv_nelem; i++) { free(nvlist[i]->nv_data); free(nvlist[i]); } free(nvlist); return (rv); } static int get_value_size(data_type_t type, const void *data, uint32_t nelem) { uint64_t value_sz = 0; switch (type) { case DATA_TYPE_BOOLEAN: value_sz = 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: /* Our smallest data unit is 32-bit */ value_sz = sizeof(uint32_t); break; case DATA_TYPE_HRTIME: case DATA_TYPE_INT64: value_sz = sizeof(int64_t); break; case DATA_TYPE_UINT64: value_sz = sizeof(uint64_t); break; case DATA_TYPE_STRING: if (data == NULL) value_sz = 0; else value_sz = strlen(data) + 1; break; case DATA_TYPE_BYTE_ARRAY: value_sz = nelem * sizeof(uint8_t); 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: 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; uint32_t i; for (i = 0; i < nelem; i++) { if (strs[i] == NULL) return (-1); value_sz += strlen(strs[i]) + 1; } } break; case DATA_TYPE_NVLIST: /* * The decoded size of nvlist is constant. */ value_sz = NV_ALIGN(6 * 4); /* sizeof nvlist_t */ break; case DATA_TYPE_NVLIST_ARRAY: value_sz = (uint64_t)nelem * sizeof(uint64_t) + (uint64_t)nelem * NV_ALIGN(6 * 4); /* sizeof nvlist_t */ break; default: return (-1); } return (value_sz > INT32_MAX ? -1 : (int)value_sz); } static int get_nvp_data_size(data_type_t type, const void *data, uint32_t nelem) { uint64_t value_sz = 0; xdr_t xdr; size_t size; switch (type) { case DATA_TYPE_BOOLEAN: value_sz = 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: /* Our smallest data unit is 32-bit */ value_sz = sizeof(uint32_t); break; case DATA_TYPE_HRTIME: case DATA_TYPE_INT64: case DATA_TYPE_UINT64: value_sz = sizeof(uint64_t); break; case DATA_TYPE_STRING: value_sz = 4 + NV_ALIGN4(strlen(data)); break; case DATA_TYPE_BYTE_ARRAY: value_sz = NV_ALIGN4(nelem); 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: value_sz = 4 + (uint64_t)nelem * sizeof(uint32_t); break; case DATA_TYPE_INT64_ARRAY: case DATA_TYPE_UINT64_ARRAY: value_sz = 4 + (uint64_t)nelem * sizeof(uint64_t); break; case DATA_TYPE_STRING_ARRAY: if (data != NULL) { char *const *strs = data; uint32_t i; for (i = 0; i < nelem; i++) { value_sz += 4 + NV_ALIGN4(strlen(strs[i])); } } break; case DATA_TYPE_NVLIST: xdr.xdr_idx = ((nvlist_t *)data)->nv_data; xdr.xdr_buf = xdr.xdr_idx; xdr.xdr_buf_size = ((nvlist_t *)data)->nv_size; if (!nvlist_size_native(&xdr, &size)) return (-1); value_sz = size; break; case DATA_TYPE_NVLIST_ARRAY: value_sz = 0; for (uint32_t i = 0; i < nelem; i++) { xdr.xdr_idx = ((nvlist_t **)data)[i]->nv_data; xdr.xdr_buf = xdr.xdr_idx; xdr.xdr_buf_size = ((nvlist_t **)data)[i]->nv_size; if (!nvlist_size_native(&xdr, &size)) return (-1); value_sz += size; } break; default: return (-1); } return (value_sz > INT32_MAX ? -1 : (int)value_sz); } #define NVPE_SIZE(name_len, data_len) \ (4 + 4 + 4 + NV_ALIGN4(name_len) + 4 + 4 + data_len) #define NVP_SIZE(name_len, data_len) \ (NV_ALIGN((4 * 4) + (name_len)) + NV_ALIGN(data_len)) static int nvlist_add_common(nvlist_t *nvl, const char *name, data_type_t type, uint32_t nelem, const void *data) { nvs_data_t *nvs; nvp_header_t head, *hp; uint8_t *ptr; size_t namelen; int decoded_size, encoded_size; xdr_t xdr = { .xdr_op = XDR_OP_ENCODE, .xdr_putint = _putint_mem, .xdr_putuint = _putuint_mem, .xdr_buf = nvl->nv_data, .xdr_idx = nvl->nv_data, .xdr_buf_size = nvl->nv_size }; nvs = (nvs_data_t *)nvl->nv_data; if (nvs->nvl_nvflag & NV_UNIQUE_NAME) (void) nvlist_remove(nvl, name, type); xdr.xdr_buf = nvl->nv_data; xdr.xdr_idx = nvl->nv_data; xdr.xdr_buf_size = nvl->nv_size; if (!nvlist_size_native(&xdr, &nvl->nv_size)) return (EINVAL); namelen = strlen(name); if ((decoded_size = get_value_size(type, data, nelem)) < 0) return (EINVAL); if ((encoded_size = get_nvp_data_size(type, data, nelem)) < 0) return (EINVAL); /* * The encoded size is calculated as: * encode_size (4) + decode_size (4) + * name string size (4 + NV_ALIGN4(namelen) + * data type (4) + nelem size (4) + datalen * * The decoded size is calculated as: * Note: namelen is with terminating 0. * NV_ALIGN(sizeof(nvpair_t) (4 * 4) + namelen + 1) + * NV_ALIGN(data_len) */ head.encoded_size = NVPE_SIZE(namelen, encoded_size); head.decoded_size = NVP_SIZE(namelen + 1, decoded_size); if (nvl->nv_asize - nvl->nv_size < head.encoded_size + 8) { ptr = realloc(nvl->nv_data, nvl->nv_asize + head.encoded_size); if (ptr == NULL) return (ENOMEM); nvl->nv_data = ptr; nvl->nv_asize += head.encoded_size; } nvl->nv_idx = nvl->nv_data + nvl->nv_size - sizeof(*hp); bzero(nvl->nv_idx, head.encoded_size + 8); hp = (nvp_header_t *)nvl->nv_idx; *hp = head; nvl->nv_idx += sizeof(*hp); xdr.xdr_buf = nvl->nv_data; xdr.xdr_idx = nvl->nv_idx; xdr.xdr_idx += xdr.xdr_putuint(&xdr, namelen); strlcpy((char *)xdr.xdr_idx, name, namelen + 1); xdr.xdr_idx += NV_ALIGN4(namelen); xdr.xdr_idx += xdr.xdr_putuint(&xdr, type); xdr.xdr_idx += xdr.xdr_putuint(&xdr, nelem); switch (type) { case DATA_TYPE_BOOLEAN: break; case DATA_TYPE_BYTE_ARRAY: xdr.xdr_idx += xdr.xdr_putuint(&xdr, encoded_size); bcopy(data, xdr.xdr_idx, nelem); xdr.xdr_idx += NV_ALIGN4(encoded_size); break; case DATA_TYPE_STRING: encoded_size = strlen(data); xdr.xdr_idx += xdr.xdr_putuint(&xdr, encoded_size); strlcpy((char *)xdr.xdr_idx, data, encoded_size + 1); xdr.xdr_idx += NV_ALIGN4(encoded_size); break; case DATA_TYPE_STRING_ARRAY: for (uint32_t i = 0; i < nelem; i++) { encoded_size = strlen(((char **)data)[i]); xdr.xdr_idx += xdr.xdr_putuint(&xdr, encoded_size); strlcpy((char *)xdr.xdr_idx, ((char **)data)[i], encoded_size + 1); xdr.xdr_idx += NV_ALIGN4(encoded_size); } break; case DATA_TYPE_BYTE: case DATA_TYPE_INT8: case DATA_TYPE_UINT8: xdr_char(&xdr, (char *)data); break; case DATA_TYPE_INT8_ARRAY: case DATA_TYPE_UINT8_ARRAY: xdr_array(&xdr, nelem, (xdrproc_t)xdr_char); break; case DATA_TYPE_INT16: xdr_short(&xdr, (short *)data); break; case DATA_TYPE_UINT16: xdr_u_short(&xdr, (unsigned short *)data); break; case DATA_TYPE_INT16_ARRAY: xdr_array(&xdr, nelem, (xdrproc_t)xdr_short); break; case DATA_TYPE_UINT16_ARRAY: xdr_array(&xdr, nelem, (xdrproc_t)xdr_u_short); break; case DATA_TYPE_BOOLEAN_VALUE: case DATA_TYPE_INT32: xdr_int(&xdr, (int *)data); break; case DATA_TYPE_UINT32: xdr_u_int(&xdr, (unsigned int *)data); break; case DATA_TYPE_BOOLEAN_ARRAY: case DATA_TYPE_INT32_ARRAY: xdr_array(&xdr, nelem, (xdrproc_t)xdr_int); break; case DATA_TYPE_UINT32_ARRAY: xdr_array(&xdr, nelem, (xdrproc_t)xdr_u_int); break; case DATA_TYPE_INT64: xdr_int64(&xdr, (int64_t *)data); break; case DATA_TYPE_UINT64: xdr_uint64(&xdr, (uint64_t *)data); break; case DATA_TYPE_INT64_ARRAY: xdr_array(&xdr, nelem, (xdrproc_t)xdr_int64); break; case DATA_TYPE_UINT64_ARRAY: xdr_array(&xdr, nelem, (xdrproc_t)xdr_uint64); break; case DATA_TYPE_NVLIST: bcopy(((nvlist_t *)data)->nv_data, xdr.xdr_idx, encoded_size); break; case DATA_TYPE_NVLIST_ARRAY: { size_t size; xdr_t xdr_nv; for (uint32_t i = 0; i < nelem; i++) { xdr_nv.xdr_idx = ((nvlist_t **)data)[i]->nv_data; xdr_nv.xdr_buf = xdr_nv.xdr_idx; xdr_nv.xdr_buf_size = ((nvlist_t **)data)[i]->nv_size; if (!nvlist_size_native(&xdr_nv, &size)) return (EINVAL); bcopy(((nvlist_t **)data)[i]->nv_data, xdr.xdr_idx, size); xdr.xdr_idx += size; } break; } default: bcopy(data, xdr.xdr_idx, encoded_size); } nvl->nv_size += head.encoded_size; return (0); } int nvlist_add_boolean_value(nvlist_t *nvl, const char *name, boolean_t value) { return (nvlist_add_common(nvl, name, DATA_TYPE_BOOLEAN_VALUE, 1, &value)); } int nvlist_add_byte(nvlist_t *nvl, const char *name, uint8_t value) { return (nvlist_add_common(nvl, name, DATA_TYPE_BYTE, 1, &value)); } int nvlist_add_int8(nvlist_t *nvl, const char *name, int8_t value) { return (nvlist_add_common(nvl, name, DATA_TYPE_INT8, 1, &value)); } int nvlist_add_uint8(nvlist_t *nvl, const char *name, uint8_t value) { return (nvlist_add_common(nvl, name, DATA_TYPE_UINT8, 1, &value)); } int nvlist_add_int16(nvlist_t *nvl, const char *name, int16_t value) { return (nvlist_add_common(nvl, name, DATA_TYPE_INT16, 1, &value)); } int nvlist_add_uint16(nvlist_t *nvl, const char *name, uint16_t value) { return (nvlist_add_common(nvl, name, DATA_TYPE_UINT16, 1, &value)); } int nvlist_add_int32(nvlist_t *nvl, const char *name, int32_t value) { return (nvlist_add_common(nvl, name, DATA_TYPE_INT32, 1, &value)); } int nvlist_add_uint32(nvlist_t *nvl, const char *name, uint32_t value) { return (nvlist_add_common(nvl, name, DATA_TYPE_UINT32, 1, &value)); } int nvlist_add_int64(nvlist_t *nvl, const char *name, int64_t value) { return (nvlist_add_common(nvl, name, DATA_TYPE_INT64, 1, &value)); } int nvlist_add_uint64(nvlist_t *nvl, const char *name, uint64_t value) { return (nvlist_add_common(nvl, name, DATA_TYPE_UINT64, 1, &value)); } int nvlist_add_string(nvlist_t *nvl, const char *name, const char *value) { return (nvlist_add_common(nvl, name, DATA_TYPE_STRING, 1, value)); } int nvlist_add_boolean_array(nvlist_t *nvl, const char *name, boolean_t *a, uint32_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, uint8_t *a, uint32_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, uint32_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, uint32_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, uint32_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, uint32_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, uint32_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, uint32_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, uint32_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, uint32_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, uint32_t n) { return (nvlist_add_common(nvl, name, DATA_TYPE_STRING_ARRAY, n, a)); } 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, uint32_t n) { return (nvlist_add_common(nvl, name, DATA_TYPE_NVLIST_ARRAY, n, a)); } static const char *typenames[] = { "DATA_TYPE_UNKNOWN", "DATA_TYPE_BOOLEAN", "DATA_TYPE_BYTE", "DATA_TYPE_INT16", "DATA_TYPE_UINT16", "DATA_TYPE_INT32", "DATA_TYPE_UINT32", "DATA_TYPE_INT64", "DATA_TYPE_UINT64", "DATA_TYPE_STRING", "DATA_TYPE_BYTE_ARRAY", "DATA_TYPE_INT16_ARRAY", "DATA_TYPE_UINT16_ARRAY", "DATA_TYPE_INT32_ARRAY", "DATA_TYPE_UINT32_ARRAY", "DATA_TYPE_INT64_ARRAY", "DATA_TYPE_UINT64_ARRAY", "DATA_TYPE_STRING_ARRAY", "DATA_TYPE_HRTIME", "DATA_TYPE_NVLIST", "DATA_TYPE_NVLIST_ARRAY", "DATA_TYPE_BOOLEAN_VALUE", "DATA_TYPE_INT8", "DATA_TYPE_UINT8", "DATA_TYPE_BOOLEAN_ARRAY", "DATA_TYPE_INT8_ARRAY", "DATA_TYPE_UINT8_ARRAY" }; int nvpair_type_from_name(const char *name) { unsigned i; for (i = 0; i < nitems(typenames); i++) { if (strcmp(name, typenames[i]) == 0) return (i); } return (0); } nvp_header_t * nvpair_find(nvlist_t *nv, const char *name) { nvp_header_t *nvh; nvh = NULL; while ((nvh = nvlist_next_nvpair(nv, nvh)) != NULL) { nv_string_t *nvp_name; nvp_name = (nv_string_t *)(nvh + 1); if (nvp_name->nv_size == strlen(name) && memcmp(nvp_name->nv_data, name, nvp_name->nv_size) == 0) break; } return (nvh); } void nvpair_print(nvp_header_t *nvp, unsigned int indent) { nv_string_t *nvp_name; nv_pair_data_t *nvp_data; nvlist_t nvlist; unsigned i, j; xdr_t xdr = { .xdr_op = XDR_OP_DECODE, .xdr_getint = _getint_mem, .xdr_getuint = _getuint_mem, .xdr_buf = (const uint8_t *)nvp, .xdr_idx = NULL, .xdr_buf_size = nvp->encoded_size }; nvp_name = (nv_string_t *)((uintptr_t)nvp + sizeof(*nvp)); nvp_data = (nv_pair_data_t *) NV_ALIGN4((uintptr_t)&nvp_name->nv_data[0] + nvp_name->nv_size); for (i = 0; i < indent; i++) printf(" "); printf("%s [%d] %.*s", typenames[nvp_data->nv_type], nvp_data->nv_nelem, nvp_name->nv_size, nvp_name->nv_data); xdr.xdr_idx = nvp_data->nv_data; switch (nvp_data->nv_type) { case DATA_TYPE_BYTE: case DATA_TYPE_INT8: case DATA_TYPE_UINT8: { char c; if (xdr_char(&xdr, &c)) printf(" = 0x%x\n", c); break; } case DATA_TYPE_INT16: case DATA_TYPE_UINT16: { unsigned short u; if (xdr_u_short(&xdr, &u)) printf(" = 0x%hx\n", u); break; } case DATA_TYPE_BOOLEAN_VALUE: case DATA_TYPE_INT32: case DATA_TYPE_UINT32: { unsigned u; if (xdr_u_int(&xdr, &u)) printf(" = 0x%x\n", u); break; } case DATA_TYPE_INT64: case DATA_TYPE_UINT64: { uint64_t u; if (xdr_uint64(&xdr, &u)) printf(" = 0x%jx\n", (uintmax_t)u); break; } case DATA_TYPE_INT64_ARRAY: case DATA_TYPE_UINT64_ARRAY: { uint64_t *u; if (xdr_array(&xdr, nvp_data->nv_nelem, (xdrproc_t)xdr_uint64)) { u = (uint64_t *)(nvp_data->nv_data + sizeof(unsigned)); for (i = 0; i < nvp_data->nv_nelem; i++) printf(" [%u] = 0x%jx", i, (uintmax_t)u[i]); printf("\n"); } break; } case DATA_TYPE_STRING: case DATA_TYPE_STRING_ARRAY: nvp_name = (nv_string_t *)&nvp_data->nv_data[0]; for (i = 0; i < nvp_data->nv_nelem; i++) { printf(" = \"%.*s\"\n", nvp_name->nv_size, nvp_name->nv_data); } break; case DATA_TYPE_NVLIST: printf("\n"); nvlist.nv_data = &nvp_data->nv_data[0]; nvlist_print(&nvlist, indent + 2); break; case DATA_TYPE_NVLIST_ARRAY: nvlist.nv_data = &nvp_data->nv_data[0]; for (j = 0; j < nvp_data->nv_nelem; j++) { size_t size; printf("[%d]\n", j); nvlist_print(&nvlist, indent + 2); if (j != nvp_data->nv_nelem - 1) { for (i = 0; i < indent; i++) printf(" "); printf("%s %.*s", typenames[nvp_data->nv_type], nvp_name->nv_size, nvp_name->nv_data); } xdr.xdr_idx = nvlist.nv_data; xdr.xdr_buf = xdr.xdr_idx; xdr.xdr_buf_size = nvp->encoded_size - (xdr.xdr_idx - (uint8_t *)nvp); if (!nvlist_size_native(&xdr, &size)) return; nvlist.nv_data += size; } break; default: printf("\n"); } } void nvlist_print(const nvlist_t *nvl, unsigned int indent) { nvs_data_t *data; nvp_header_t *nvp; data = (nvs_data_t *)nvl->nv_data; nvp = &data->nvl_pair; /* first pair in nvlist */ while (nvp->encoded_size != 0 && nvp->decoded_size != 0) { nvpair_print(nvp, indent); nvp = (nvp_header_t *)((uint8_t *)nvp + nvp->encoded_size); } printf("%*s\n", indent + 13, "End of nvlist"); }