e307eb94ae
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
1570 lines
36 KiB
C
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");
|
|
}
|