freebsd-dev/stand/libsa/zfs/nvlist.c
Toomas Soome e307eb94ae loader: zfs should support bootonce an nextboot
bootonce feature is temporary, one time boot, activated by
"bectl activate -t BE", "bectl activate -T BE" will reset the bootonce flag.

By default, the bootonce setting is reset on attempt to boot and the next
boot will use previously active BE.

By setting zfs_bootonce_activate="YES" in rc.conf, the bootonce BE will
be set permanently active.

bootonce dataset name is recorded in boot pool labels, bootenv area.

in case of nextboot, the nextboot_enable boolean variable is recorded in
freebsd:nvstore nvlist, also stored in boot pool label bootenv area.
On boot, the loader will process /boot/nextboot.conf if nextboot_enable
is "YES", and will set nextboot_enable to "NO", preventing /boot/nextboot.conf
processing on next boot.

bootonce and nextboot features are usable in both UEFI and BIOS boot.

To use bootonce/nextboot features, the boot loader needs to be updated on disk;
if loader.efi is stored on ESP, then ESP needs to be updated and
for BIOS boot, stage2 (zfsboot or gptzfsboot) needs to be updated
(gpart or other tools).

At this time, only lua loader is updated.

Sponsored by:	Netflix, Klara Inc.
Differential Revision:	https://reviews.freebsd.org/D25512
2020-09-21 09:01:10 +00:00

1570 lines
36 KiB
C

/*-
* 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 int (*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));
}
/*
* 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)
{
int hi;
unsigned lo;
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 */
hi = *lp >> 32;
lo = *lp & UINT32_MAX;
xdr->xdr_idx += xdr->xdr_putint(xdr, hi);
xdr->xdr_idx += xdr->xdr_putint(xdr, lo);
rv = true;
break;
case XDR_OP_DECODE:
/* Decode buf, return value to *ip */
xdr->xdr_idx += xdr->xdr_getint(xdr, &hi);
xdr->xdr_idx += xdr->xdr_getuint(xdr, &lo);
*lp = (((int64_t)hi) << 32) | lo;
rv = true;
}
return (rv);
}
static bool
xdr_uint64(xdr_t *xdr, uint64_t *lp)
{
unsigned hi, lo;
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 */
hi = *lp >> 32;
lo = *lp & UINT32_MAX;
xdr->xdr_idx += xdr->xdr_putint(xdr, hi);
xdr->xdr_idx += xdr->xdr_putint(xdr, lo);
rv = true;
break;
case XDR_OP_DECODE:
/* Decode buf, return value to *ip */
xdr->xdr_idx += xdr->xdr_getuint(xdr, &hi);
xdr->xdr_idx += xdr->xdr_getuint(xdr, &lo);
*lp = (((uint64_t)hi) << 32) | lo;
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;
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;
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);
*(unsigned *)nvl->nv_idx = namelen;
nvl->nv_idx += sizeof (unsigned);
strlcpy((char *)nvl->nv_idx, name, namelen + 1);
nvl->nv_idx += NV_ALIGN4(namelen);
*(unsigned *)nvl->nv_idx = type;
nvl->nv_idx += sizeof (unsigned);
*(unsigned *)nvl->nv_idx = nelem;
nvl->nv_idx += sizeof (unsigned);
switch (type) {
case DATA_TYPE_BOOLEAN:
break;
case DATA_TYPE_BYTE_ARRAY:
*(unsigned *)nvl->nv_idx = encoded_size;
nvl->nv_idx += sizeof (unsigned);
bcopy(data, nvl->nv_idx, nelem);
nvl->nv_idx += encoded_size;
break;
case DATA_TYPE_STRING:
encoded_size = strlen(data);
*(unsigned *)nvl->nv_idx = encoded_size;
nvl->nv_idx += sizeof (unsigned);
strlcpy((char *)nvl->nv_idx, data, encoded_size + 1);
nvl->nv_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]);
*(unsigned *)nvl->nv_idx = encoded_size;
nvl->nv_idx += sizeof (unsigned);
strlcpy((char *)nvl->nv_idx, ((char **)data)[i],
encoded_size + 1);
nvl->nv_idx += NV_ALIGN4(encoded_size);
}
break;
case DATA_TYPE_BYTE:
case DATA_TYPE_INT8:
case DATA_TYPE_UINT8:
case DATA_TYPE_INT8_ARRAY:
case DATA_TYPE_UINT8_ARRAY:
for (uint32_t i = 0; i < nelem; i++) {
*(unsigned *)nvl->nv_idx = ((uint8_t *)data)[i];
nvl->nv_idx += sizeof (unsigned);
}
break;
case DATA_TYPE_INT16:
case DATA_TYPE_UINT16:
case DATA_TYPE_INT16_ARRAY:
case DATA_TYPE_UINT16_ARRAY:
for (uint32_t i = 0; i < nelem; i++) {
*(unsigned *)nvl->nv_idx = ((uint16_t *)data)[i];
nvl->nv_idx += sizeof (unsigned);
}
break;
case DATA_TYPE_NVLIST:
bcopy(((nvlist_t *)data)->nv_data, nvl->nv_idx, encoded_size);
break;
case DATA_TYPE_NVLIST_ARRAY: {
uint8_t *buf = nvl->nv_idx;
size_t size;
xdr_t xdr;
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 (EINVAL);
bcopy(((nvlist_t **)data)[i]->nv_data, buf, size);
buf += size;
}
break;
}
default:
bcopy(data, nvl->nv_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;
xdr_t xdr;
unsigned i, j, u;
uint64_t u64;
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);
switch (nvp_data->nv_type) {
case DATA_TYPE_BYTE:
case DATA_TYPE_INT8:
case DATA_TYPE_UINT8:
bcopy(nvp_data->nv_data, &u, sizeof (u));
printf(" = 0x%x\n", (unsigned char)u);
break;
case DATA_TYPE_INT16:
case DATA_TYPE_UINT16:
bcopy(nvp_data->nv_data, &u, sizeof (u));
printf(" = 0x%hx\n", (unsigned short)u);
break;
case DATA_TYPE_BOOLEAN_VALUE:
case DATA_TYPE_INT32:
case DATA_TYPE_UINT32:
bcopy(nvp_data->nv_data, &u, sizeof (u));
printf(" = 0x%x\n", u);
break;
case DATA_TYPE_INT64:
case DATA_TYPE_UINT64:
bcopy(nvp_data->nv_data, &u64, sizeof (u64));
printf(" = 0x%jx\n", (uintmax_t)u64);
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");
}