FreeBSD source tree
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1694 lines
38 KiB

/*-
* Copyright 2020 Toomas Soome <tsoome@me.com>
*
* 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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <stand.h>
#include <stdbool.h>
#include <sys/endian.h>
#include <sys/stdint.h>
#include <sys/param.h>
#include <zfsimpl.h>
#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");
}