2ae5841472
Since our xdr translation function are returning bool, so should xdrproc_t. Issue reported by gcc 10 build.
1695 lines
38 KiB
C
1695 lines
38 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 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");
|
|
}
|