d5bb9ec5ff
Found by: Coverity Prevent CID: 7051 MFC after: 3 days
885 lines
20 KiB
C
885 lines
20 KiB
C
/*-
|
|
* Copyright (c) 2009-2010 The FreeBSD Foundation
|
|
* All rights reserved.
|
|
*
|
|
* This software was developed by Pawel Jakub Dawidek under sponsorship from
|
|
* the FreeBSD Foundation.
|
|
*
|
|
* 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 AUTHORS 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 AUTHORS 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 <sys/param.h>
|
|
#include <sys/endian.h>
|
|
|
|
#include <assert.h>
|
|
#include <bitstring.h>
|
|
#include <errno.h>
|
|
#include <stdarg.h>
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include <ebuf.h>
|
|
#include <nv.h>
|
|
|
|
#define NV_MAGIC 0xaea1e
|
|
struct nv {
|
|
int nv_magic;
|
|
int nv_error;
|
|
struct ebuf *nv_ebuf;
|
|
};
|
|
|
|
struct nvhdr {
|
|
uint8_t nvh_type;
|
|
uint8_t nvh_namesize;
|
|
uint32_t nvh_dsize;
|
|
char nvh_name[0];
|
|
} __packed;
|
|
#define NVH_DATA(nvh) ((unsigned char *)nvh + NVH_HSIZE(nvh))
|
|
#define NVH_HSIZE(nvh) \
|
|
(sizeof(struct nvhdr) + roundup2((nvh)->nvh_namesize, 8))
|
|
#define NVH_DSIZE(nvh) \
|
|
(((nvh)->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST ? \
|
|
(nvh)->nvh_dsize : \
|
|
le32toh((nvh)->nvh_dsize))
|
|
#define NVH_SIZE(nvh) (NVH_HSIZE(nvh) + roundup2(NVH_DSIZE(nvh), 8))
|
|
|
|
#define NV_CHECK(nv) do { \
|
|
assert((nv) != NULL); \
|
|
assert((nv)->nv_magic == NV_MAGIC); \
|
|
} while (0)
|
|
|
|
static void nv_add(struct nv *nv, const unsigned char *value, size_t vsize,
|
|
int type, const char *name);
|
|
static void nv_addv(struct nv *nv, const unsigned char *value, size_t vsize,
|
|
int type, const char *namefmt, va_list nameap);
|
|
static struct nvhdr *nv_find(struct nv *nv, int type, const char *namefmt,
|
|
va_list nameap);
|
|
static void nv_swap(struct nvhdr *nvh, bool tohost);
|
|
|
|
/*
|
|
* Allocate and initialize new nv structure.
|
|
* Return NULL in case of malloc(3) failure.
|
|
*/
|
|
struct nv *
|
|
nv_alloc(void)
|
|
{
|
|
struct nv *nv;
|
|
|
|
nv = malloc(sizeof(*nv));
|
|
if (nv == NULL)
|
|
return (NULL);
|
|
nv->nv_ebuf = ebuf_alloc(0);
|
|
if (nv->nv_ebuf == NULL) {
|
|
free(nv);
|
|
return (NULL);
|
|
}
|
|
nv->nv_error = 0;
|
|
nv->nv_magic = NV_MAGIC;
|
|
return (nv);
|
|
}
|
|
|
|
/*
|
|
* Free the given nv structure.
|
|
*/
|
|
void
|
|
nv_free(struct nv *nv)
|
|
{
|
|
|
|
if (nv == NULL)
|
|
return;
|
|
|
|
NV_CHECK(nv);
|
|
|
|
nv->nv_magic = 0;
|
|
ebuf_free(nv->nv_ebuf);
|
|
free(nv);
|
|
}
|
|
|
|
/*
|
|
* Return error for the given nv structure.
|
|
*/
|
|
int
|
|
nv_error(const struct nv *nv)
|
|
{
|
|
|
|
if (nv == NULL)
|
|
return (ENOMEM);
|
|
|
|
NV_CHECK(nv);
|
|
|
|
return (nv->nv_error);
|
|
}
|
|
|
|
/*
|
|
* Set error for the given nv structure and return previous error.
|
|
*/
|
|
int
|
|
nv_set_error(struct nv *nv, int error)
|
|
{
|
|
int preverr;
|
|
|
|
if (nv == NULL)
|
|
return (ENOMEM);
|
|
|
|
NV_CHECK(nv);
|
|
|
|
preverr = nv->nv_error;
|
|
nv->nv_error = error;
|
|
return (preverr);
|
|
}
|
|
|
|
/*
|
|
* Validate correctness of the entire nv structure and all its elements.
|
|
* If extrap is not NULL, store number of extra bytes at the end of the buffer.
|
|
*/
|
|
int
|
|
nv_validate(struct nv *nv, size_t *extrap)
|
|
{
|
|
struct nvhdr *nvh;
|
|
unsigned char *data, *ptr;
|
|
size_t dsize, size, vsize;
|
|
int error;
|
|
|
|
if (nv == NULL) {
|
|
errno = ENOMEM;
|
|
return (-1);
|
|
}
|
|
|
|
NV_CHECK(nv);
|
|
assert(nv->nv_error == 0);
|
|
|
|
/* TODO: Check that names are unique? */
|
|
|
|
error = 0;
|
|
ptr = ebuf_data(nv->nv_ebuf, &size);
|
|
while (size > 0) {
|
|
/*
|
|
* Zeros at the end of the buffer are acceptable.
|
|
*/
|
|
if (ptr[0] == '\0')
|
|
break;
|
|
/*
|
|
* Minimum size at this point is size of nvhdr structure, one
|
|
* character long name plus terminating '\0'.
|
|
*/
|
|
if (size < sizeof(*nvh) + 2) {
|
|
error = EINVAL;
|
|
break;
|
|
}
|
|
nvh = (struct nvhdr *)ptr;
|
|
if (size < NVH_HSIZE(nvh)) {
|
|
error = EINVAL;
|
|
break;
|
|
}
|
|
if (nvh->nvh_name[nvh->nvh_namesize - 1] != '\0') {
|
|
error = EINVAL;
|
|
break;
|
|
}
|
|
if (strlen(nvh->nvh_name) !=
|
|
(size_t)(nvh->nvh_namesize - 1)) {
|
|
error = EINVAL;
|
|
break;
|
|
}
|
|
if ((nvh->nvh_type & NV_TYPE_MASK) < NV_TYPE_FIRST ||
|
|
(nvh->nvh_type & NV_TYPE_MASK) > NV_TYPE_LAST) {
|
|
error = EINVAL;
|
|
break;
|
|
}
|
|
dsize = NVH_DSIZE(nvh);
|
|
if (dsize == 0) {
|
|
error = EINVAL;
|
|
break;
|
|
}
|
|
if (size < NVH_SIZE(nvh)) {
|
|
error = EINVAL;
|
|
break;
|
|
}
|
|
vsize = 0;
|
|
switch (nvh->nvh_type & NV_TYPE_MASK) {
|
|
case NV_TYPE_INT8:
|
|
case NV_TYPE_UINT8:
|
|
if (vsize == 0)
|
|
vsize = 1;
|
|
/* FALLTHOUGH */
|
|
case NV_TYPE_INT16:
|
|
case NV_TYPE_UINT16:
|
|
if (vsize == 0)
|
|
vsize = 2;
|
|
/* FALLTHOUGH */
|
|
case NV_TYPE_INT32:
|
|
case NV_TYPE_UINT32:
|
|
if (vsize == 0)
|
|
vsize = 4;
|
|
/* FALLTHOUGH */
|
|
case NV_TYPE_INT64:
|
|
case NV_TYPE_UINT64:
|
|
if (vsize == 0)
|
|
vsize = 8;
|
|
if (dsize != vsize) {
|
|
error = EINVAL;
|
|
break;
|
|
}
|
|
break;
|
|
case NV_TYPE_INT8_ARRAY:
|
|
case NV_TYPE_UINT8_ARRAY:
|
|
break;
|
|
case NV_TYPE_INT16_ARRAY:
|
|
case NV_TYPE_UINT16_ARRAY:
|
|
if (vsize == 0)
|
|
vsize = 2;
|
|
/* FALLTHOUGH */
|
|
case NV_TYPE_INT32_ARRAY:
|
|
case NV_TYPE_UINT32_ARRAY:
|
|
if (vsize == 0)
|
|
vsize = 4;
|
|
/* FALLTHOUGH */
|
|
case NV_TYPE_INT64_ARRAY:
|
|
case NV_TYPE_UINT64_ARRAY:
|
|
if (vsize == 0)
|
|
vsize = 8;
|
|
if ((dsize % vsize) != 0) {
|
|
error = EINVAL;
|
|
break;
|
|
}
|
|
break;
|
|
case NV_TYPE_STRING:
|
|
data = NVH_DATA(nvh);
|
|
if (data[dsize - 1] != '\0') {
|
|
error = EINVAL;
|
|
break;
|
|
}
|
|
if (strlen((char *)data) != dsize - 1) {
|
|
error = EINVAL;
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
assert(!"invalid condition");
|
|
}
|
|
if (error != 0)
|
|
break;
|
|
ptr += NVH_SIZE(nvh);
|
|
size -= NVH_SIZE(nvh);
|
|
}
|
|
if (error != 0) {
|
|
errno = error;
|
|
if (nv->nv_error == 0)
|
|
nv->nv_error = error;
|
|
return (-1);
|
|
}
|
|
if (extrap != NULL)
|
|
*extrap = size;
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Convert the given nv structure to network byte order and return ebuf
|
|
* structure.
|
|
*/
|
|
struct ebuf *
|
|
nv_hton(struct nv *nv)
|
|
{
|
|
struct nvhdr *nvh;
|
|
unsigned char *ptr;
|
|
size_t size;
|
|
|
|
NV_CHECK(nv);
|
|
assert(nv->nv_error == 0);
|
|
|
|
ptr = ebuf_data(nv->nv_ebuf, &size);
|
|
while (size > 0) {
|
|
/*
|
|
* Minimum size at this point is size of nvhdr structure,
|
|
* one character long name plus terminating '\0'.
|
|
*/
|
|
assert(size >= sizeof(*nvh) + 2);
|
|
nvh = (struct nvhdr *)ptr;
|
|
assert(NVH_SIZE(nvh) <= size);
|
|
nv_swap(nvh, false);
|
|
ptr += NVH_SIZE(nvh);
|
|
size -= NVH_SIZE(nvh);
|
|
}
|
|
|
|
return (nv->nv_ebuf);
|
|
}
|
|
|
|
/*
|
|
* Create nv structure based on ebuf received from the network.
|
|
*/
|
|
struct nv *
|
|
nv_ntoh(struct ebuf *eb)
|
|
{
|
|
struct nv *nv;
|
|
size_t extra;
|
|
int rerrno;
|
|
|
|
assert(eb != NULL);
|
|
|
|
nv = malloc(sizeof(*nv));
|
|
if (nv == NULL)
|
|
return (NULL);
|
|
nv->nv_error = 0;
|
|
nv->nv_ebuf = eb;
|
|
nv->nv_magic = NV_MAGIC;
|
|
|
|
if (nv_validate(nv, &extra) < 0) {
|
|
rerrno = errno;
|
|
nv->nv_magic = 0;
|
|
free(nv);
|
|
errno = rerrno;
|
|
return (NULL);
|
|
}
|
|
/*
|
|
* Remove extra zeros at the end of the buffer.
|
|
*/
|
|
ebuf_del_tail(eb, extra);
|
|
|
|
return (nv);
|
|
}
|
|
|
|
#define NV_DEFINE_ADD(type, TYPE) \
|
|
void \
|
|
nv_add_##type(struct nv *nv, type##_t value, const char *namefmt, ...) \
|
|
{ \
|
|
va_list nameap; \
|
|
\
|
|
va_start(nameap, namefmt); \
|
|
nv_addv(nv, (unsigned char *)&value, sizeof(value), \
|
|
NV_TYPE_##TYPE, namefmt, nameap); \
|
|
va_end(nameap); \
|
|
}
|
|
|
|
NV_DEFINE_ADD(int8, INT8)
|
|
NV_DEFINE_ADD(uint8, UINT8)
|
|
NV_DEFINE_ADD(int16, INT16)
|
|
NV_DEFINE_ADD(uint16, UINT16)
|
|
NV_DEFINE_ADD(int32, INT32)
|
|
NV_DEFINE_ADD(uint32, UINT32)
|
|
NV_DEFINE_ADD(int64, INT64)
|
|
NV_DEFINE_ADD(uint64, UINT64)
|
|
|
|
#undef NV_DEFINE_ADD
|
|
|
|
#define NV_DEFINE_ADD_ARRAY(type, TYPE) \
|
|
void \
|
|
nv_add_##type##_array(struct nv *nv, const type##_t *value, \
|
|
size_t nsize, const char *namefmt, ...) \
|
|
{ \
|
|
va_list nameap; \
|
|
\
|
|
va_start(nameap, namefmt); \
|
|
nv_addv(nv, (const unsigned char *)value, \
|
|
sizeof(value[0]) * nsize, NV_TYPE_##TYPE##_ARRAY, namefmt, \
|
|
nameap); \
|
|
va_end(nameap); \
|
|
}
|
|
|
|
NV_DEFINE_ADD_ARRAY(int8, INT8)
|
|
NV_DEFINE_ADD_ARRAY(uint8, UINT8)
|
|
NV_DEFINE_ADD_ARRAY(int16, INT16)
|
|
NV_DEFINE_ADD_ARRAY(uint16, UINT16)
|
|
NV_DEFINE_ADD_ARRAY(int32, INT32)
|
|
NV_DEFINE_ADD_ARRAY(uint32, UINT32)
|
|
NV_DEFINE_ADD_ARRAY(int64, INT64)
|
|
NV_DEFINE_ADD_ARRAY(uint64, UINT64)
|
|
|
|
#undef NV_DEFINE_ADD_ARRAY
|
|
|
|
void
|
|
nv_add_string(struct nv *nv, const char *value, const char *namefmt, ...)
|
|
{
|
|
va_list nameap;
|
|
size_t size;
|
|
|
|
size = strlen(value) + 1;
|
|
|
|
va_start(nameap, namefmt);
|
|
nv_addv(nv, (const unsigned char *)value, size, NV_TYPE_STRING,
|
|
namefmt, nameap);
|
|
va_end(nameap);
|
|
}
|
|
|
|
void
|
|
nv_add_stringf(struct nv *nv, const char *name, const char *valuefmt, ...)
|
|
{
|
|
va_list valueap;
|
|
|
|
va_start(valueap, valuefmt);
|
|
nv_add_stringv(nv, name, valuefmt, valueap);
|
|
va_end(valueap);
|
|
}
|
|
|
|
void
|
|
nv_add_stringv(struct nv *nv, const char *name, const char *valuefmt,
|
|
va_list valueap)
|
|
{
|
|
char *value;
|
|
ssize_t size;
|
|
|
|
size = vasprintf(&value, valuefmt, valueap);
|
|
if (size < 0) {
|
|
if (nv->nv_error == 0)
|
|
nv->nv_error = ENOMEM;
|
|
return;
|
|
}
|
|
size++;
|
|
nv_add(nv, (const unsigned char *)value, size, NV_TYPE_STRING, name);
|
|
free(value);
|
|
}
|
|
|
|
#define NV_DEFINE_GET(type, TYPE) \
|
|
type##_t \
|
|
nv_get_##type(struct nv *nv, const char *namefmt, ...) \
|
|
{ \
|
|
struct nvhdr *nvh; \
|
|
va_list nameap; \
|
|
type##_t value; \
|
|
\
|
|
va_start(nameap, namefmt); \
|
|
nvh = nv_find(nv, NV_TYPE_##TYPE, namefmt, nameap); \
|
|
va_end(nameap); \
|
|
if (nvh == NULL) \
|
|
return (0); \
|
|
assert((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST); \
|
|
assert(sizeof(value) == nvh->nvh_dsize); \
|
|
bcopy(NVH_DATA(nvh), &value, sizeof(value)); \
|
|
\
|
|
return (value); \
|
|
}
|
|
|
|
NV_DEFINE_GET(int8, INT8)
|
|
NV_DEFINE_GET(uint8, UINT8)
|
|
NV_DEFINE_GET(int16, INT16)
|
|
NV_DEFINE_GET(uint16, UINT16)
|
|
NV_DEFINE_GET(int32, INT32)
|
|
NV_DEFINE_GET(uint32, UINT32)
|
|
NV_DEFINE_GET(int64, INT64)
|
|
NV_DEFINE_GET(uint64, UINT64)
|
|
|
|
#undef NV_DEFINE_GET
|
|
|
|
#define NV_DEFINE_GET_ARRAY(type, TYPE) \
|
|
const type##_t * \
|
|
nv_get_##type##_array(struct nv *nv, size_t *sizep, \
|
|
const char *namefmt, ...) \
|
|
{ \
|
|
struct nvhdr *nvh; \
|
|
va_list nameap; \
|
|
\
|
|
va_start(nameap, namefmt); \
|
|
nvh = nv_find(nv, NV_TYPE_##TYPE##_ARRAY, namefmt, nameap); \
|
|
va_end(nameap); \
|
|
if (nvh == NULL) \
|
|
return (NULL); \
|
|
assert((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST); \
|
|
assert((nvh->nvh_dsize % sizeof(type##_t)) == 0); \
|
|
if (sizep != NULL) \
|
|
*sizep = nvh->nvh_dsize / sizeof(type##_t); \
|
|
return ((type##_t *)(void *)NVH_DATA(nvh)); \
|
|
}
|
|
|
|
NV_DEFINE_GET_ARRAY(int8, INT8)
|
|
NV_DEFINE_GET_ARRAY(uint8, UINT8)
|
|
NV_DEFINE_GET_ARRAY(int16, INT16)
|
|
NV_DEFINE_GET_ARRAY(uint16, UINT16)
|
|
NV_DEFINE_GET_ARRAY(int32, INT32)
|
|
NV_DEFINE_GET_ARRAY(uint32, UINT32)
|
|
NV_DEFINE_GET_ARRAY(int64, INT64)
|
|
NV_DEFINE_GET_ARRAY(uint64, UINT64)
|
|
|
|
#undef NV_DEFINE_GET_ARRAY
|
|
|
|
const char *
|
|
nv_get_string(struct nv *nv, const char *namefmt, ...)
|
|
{
|
|
struct nvhdr *nvh;
|
|
va_list nameap;
|
|
char *str;
|
|
|
|
va_start(nameap, namefmt);
|
|
nvh = nv_find(nv, NV_TYPE_STRING, namefmt, nameap);
|
|
va_end(nameap);
|
|
if (nvh == NULL)
|
|
return (NULL);
|
|
assert((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST);
|
|
assert(nvh->nvh_dsize >= 1);
|
|
str = NVH_DATA(nvh);
|
|
assert(str[nvh->nvh_dsize - 1] == '\0');
|
|
assert(strlen(str) == nvh->nvh_dsize - 1);
|
|
return (str);
|
|
}
|
|
|
|
/*
|
|
* Dump content of the nv structure.
|
|
*/
|
|
void
|
|
nv_dump(struct nv *nv)
|
|
{
|
|
struct nvhdr *nvh;
|
|
unsigned char *data, *ptr;
|
|
size_t dsize, size;
|
|
unsigned int ii;
|
|
bool swap;
|
|
|
|
if (nv_validate(nv, NULL) < 0) {
|
|
printf("error: %d\n", errno);
|
|
return;
|
|
}
|
|
|
|
NV_CHECK(nv);
|
|
assert(nv->nv_error == 0);
|
|
|
|
ptr = ebuf_data(nv->nv_ebuf, &size);
|
|
while (size > 0) {
|
|
assert(size >= sizeof(*nvh) + 2);
|
|
nvh = (struct nvhdr *)ptr;
|
|
assert(size >= NVH_SIZE(nvh));
|
|
swap = ((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_NETWORK);
|
|
dsize = NVH_DSIZE(nvh);
|
|
data = NVH_DATA(nvh);
|
|
printf(" %s", nvh->nvh_name);
|
|
switch (nvh->nvh_type & NV_TYPE_MASK) {
|
|
case NV_TYPE_INT8:
|
|
printf("(int8): %jd", (intmax_t)(*(int8_t *)data));
|
|
break;
|
|
case NV_TYPE_UINT8:
|
|
printf("(uint8): %ju", (uintmax_t)(*(uint8_t *)data));
|
|
break;
|
|
case NV_TYPE_INT16:
|
|
printf("(int16): %jd", swap ?
|
|
(intmax_t)le16toh(*(int16_t *)(void *)data) :
|
|
(intmax_t)*(int16_t *)(void *)data);
|
|
break;
|
|
case NV_TYPE_UINT16:
|
|
printf("(uint16): %ju", swap ?
|
|
(uintmax_t)le16toh(*(uint16_t *)(void *)data) :
|
|
(uintmax_t)*(uint16_t *)(void *)data);
|
|
break;
|
|
case NV_TYPE_INT32:
|
|
printf("(int32): %jd", swap ?
|
|
(intmax_t)le32toh(*(int32_t *)(void *)data) :
|
|
(intmax_t)*(int32_t *)(void *)data);
|
|
break;
|
|
case NV_TYPE_UINT32:
|
|
printf("(uint32): %ju", swap ?
|
|
(uintmax_t)le32toh(*(uint32_t *)(void *)data) :
|
|
(uintmax_t)*(uint32_t *)(void *)data);
|
|
break;
|
|
case NV_TYPE_INT64:
|
|
printf("(int64): %jd", swap ?
|
|
(intmax_t)le64toh(*(int64_t *)(void *)data) :
|
|
(intmax_t)*(int64_t *)(void *)data);
|
|
break;
|
|
case NV_TYPE_UINT64:
|
|
printf("(uint64): %ju", swap ?
|
|
(uintmax_t)le64toh(*(uint64_t *)(void *)data) :
|
|
(uintmax_t)*(uint64_t *)(void *)data);
|
|
break;
|
|
case NV_TYPE_INT8_ARRAY:
|
|
printf("(int8 array):");
|
|
for (ii = 0; ii < dsize; ii++)
|
|
printf(" %jd", (intmax_t)((int8_t *)data)[ii]);
|
|
break;
|
|
case NV_TYPE_UINT8_ARRAY:
|
|
printf("(uint8 array):");
|
|
for (ii = 0; ii < dsize; ii++)
|
|
printf(" %ju", (uintmax_t)((uint8_t *)data)[ii]);
|
|
break;
|
|
case NV_TYPE_INT16_ARRAY:
|
|
printf("(int16 array):");
|
|
for (ii = 0; ii < dsize / 2; ii++) {
|
|
printf(" %jd", swap ?
|
|
(intmax_t)le16toh(((int16_t *)(void *)data)[ii]) :
|
|
(intmax_t)((int16_t *)(void *)data)[ii]);
|
|
}
|
|
break;
|
|
case NV_TYPE_UINT16_ARRAY:
|
|
printf("(uint16 array):");
|
|
for (ii = 0; ii < dsize / 2; ii++) {
|
|
printf(" %ju", swap ?
|
|
(uintmax_t)le16toh(((uint16_t *)(void *)data)[ii]) :
|
|
(uintmax_t)((uint16_t *)(void *)data)[ii]);
|
|
}
|
|
break;
|
|
case NV_TYPE_INT32_ARRAY:
|
|
printf("(int32 array):");
|
|
for (ii = 0; ii < dsize / 4; ii++) {
|
|
printf(" %jd", swap ?
|
|
(intmax_t)le32toh(((int32_t *)(void *)data)[ii]) :
|
|
(intmax_t)((int32_t *)(void *)data)[ii]);
|
|
}
|
|
break;
|
|
case NV_TYPE_UINT32_ARRAY:
|
|
printf("(uint32 array):");
|
|
for (ii = 0; ii < dsize / 4; ii++) {
|
|
printf(" %ju", swap ?
|
|
(uintmax_t)le32toh(((uint32_t *)(void *)data)[ii]) :
|
|
(uintmax_t)((uint32_t *)(void *)data)[ii]);
|
|
}
|
|
break;
|
|
case NV_TYPE_INT64_ARRAY:
|
|
printf("(int64 array):");
|
|
for (ii = 0; ii < dsize / 8; ii++) {
|
|
printf(" %ju", swap ?
|
|
(uintmax_t)le64toh(((uint64_t *)(void *)data)[ii]) :
|
|
(uintmax_t)((uint64_t *)(void *)data)[ii]);
|
|
}
|
|
break;
|
|
case NV_TYPE_UINT64_ARRAY:
|
|
printf("(uint64 array):");
|
|
for (ii = 0; ii < dsize / 8; ii++) {
|
|
printf(" %ju", swap ?
|
|
(uintmax_t)le64toh(((uint64_t *)(void *)data)[ii]) :
|
|
(uintmax_t)((uint64_t *)(void *)data)[ii]);
|
|
}
|
|
break;
|
|
case NV_TYPE_STRING:
|
|
printf("(string): %s", (char *)data);
|
|
break;
|
|
default:
|
|
assert(!"invalid condition");
|
|
}
|
|
printf("\n");
|
|
ptr += NVH_SIZE(nvh);
|
|
size -= NVH_SIZE(nvh);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Local routines below.
|
|
*/
|
|
|
|
static void
|
|
nv_add(struct nv *nv, const unsigned char *value, size_t vsize, int type,
|
|
const char *name)
|
|
{
|
|
static unsigned char align[7];
|
|
struct nvhdr *nvh;
|
|
size_t namesize;
|
|
|
|
if (nv == NULL) {
|
|
errno = ENOMEM;
|
|
return;
|
|
}
|
|
|
|
NV_CHECK(nv);
|
|
|
|
namesize = strlen(name) + 1;
|
|
|
|
nvh = malloc(sizeof(*nvh) + roundup2(namesize, 8));
|
|
if (nvh == NULL) {
|
|
if (nv->nv_error == 0)
|
|
nv->nv_error = ENOMEM;
|
|
return;
|
|
}
|
|
nvh->nvh_type = NV_ORDER_HOST | type;
|
|
nvh->nvh_namesize = (uint8_t)namesize;
|
|
nvh->nvh_dsize = (uint32_t)vsize;
|
|
bcopy(name, nvh->nvh_name, namesize);
|
|
|
|
/* Add header first. */
|
|
if (ebuf_add_tail(nv->nv_ebuf, nvh, NVH_HSIZE(nvh)) < 0) {
|
|
assert(errno != 0);
|
|
if (nv->nv_error == 0)
|
|
nv->nv_error = errno;
|
|
free(nvh);
|
|
return;
|
|
}
|
|
free(nvh);
|
|
/* Add the actual data. */
|
|
if (ebuf_add_tail(nv->nv_ebuf, value, vsize) < 0) {
|
|
assert(errno != 0);
|
|
if (nv->nv_error == 0)
|
|
nv->nv_error = errno;
|
|
return;
|
|
}
|
|
/* Align the data (if needed). */
|
|
vsize = roundup2(vsize, 8) - vsize;
|
|
if (vsize == 0)
|
|
return;
|
|
assert(vsize > 0 && vsize <= sizeof(align));
|
|
if (ebuf_add_tail(nv->nv_ebuf, align, vsize) < 0) {
|
|
assert(errno != 0);
|
|
if (nv->nv_error == 0)
|
|
nv->nv_error = errno;
|
|
return;
|
|
}
|
|
}
|
|
|
|
static void
|
|
nv_addv(struct nv *nv, const unsigned char *value, size_t vsize, int type,
|
|
const char *namefmt, va_list nameap)
|
|
{
|
|
char name[255];
|
|
size_t namesize;
|
|
|
|
namesize = vsnprintf(name, sizeof(name), namefmt, nameap);
|
|
assert(namesize > 0 && namesize < sizeof(name));
|
|
|
|
nv_add(nv, value, vsize, type, name);
|
|
}
|
|
|
|
static struct nvhdr *
|
|
nv_find(struct nv *nv, int type, const char *namefmt, va_list nameap)
|
|
{
|
|
char name[255];
|
|
struct nvhdr *nvh;
|
|
unsigned char *ptr;
|
|
size_t size, namesize;
|
|
|
|
if (nv == NULL) {
|
|
errno = ENOMEM;
|
|
return (NULL);
|
|
}
|
|
|
|
NV_CHECK(nv);
|
|
|
|
namesize = vsnprintf(name, sizeof(name), namefmt, nameap);
|
|
assert(namesize > 0 && namesize < sizeof(name));
|
|
namesize++;
|
|
|
|
ptr = ebuf_data(nv->nv_ebuf, &size);
|
|
while (size > 0) {
|
|
assert(size >= sizeof(*nvh) + 2);
|
|
nvh = (struct nvhdr *)ptr;
|
|
assert(size >= NVH_SIZE(nvh));
|
|
nv_swap(nvh, true);
|
|
if (strcmp(nvh->nvh_name, name) == 0) {
|
|
if ((nvh->nvh_type & NV_TYPE_MASK) != type) {
|
|
errno = EINVAL;
|
|
if (nv->nv_error == 0)
|
|
nv->nv_error = EINVAL;
|
|
return (NULL);
|
|
}
|
|
return (nvh);
|
|
}
|
|
ptr += NVH_SIZE(nvh);
|
|
size -= NVH_SIZE(nvh);
|
|
}
|
|
errno = ENOENT;
|
|
if (nv->nv_error == 0)
|
|
nv->nv_error = ENOENT;
|
|
return (NULL);
|
|
}
|
|
|
|
static void
|
|
nv_swap(struct nvhdr *nvh, bool tohost)
|
|
{
|
|
unsigned char *data, *end, *p;
|
|
size_t vsize;
|
|
|
|
data = NVH_DATA(nvh);
|
|
if (tohost) {
|
|
if ((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST)
|
|
return;
|
|
nvh->nvh_dsize = le32toh(nvh->nvh_dsize);
|
|
end = data + nvh->nvh_dsize;
|
|
nvh->nvh_type &= ~NV_ORDER_MASK;
|
|
nvh->nvh_type |= NV_ORDER_HOST;
|
|
} else {
|
|
if ((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_NETWORK)
|
|
return;
|
|
end = data + nvh->nvh_dsize;
|
|
nvh->nvh_dsize = htole32(nvh->nvh_dsize);
|
|
nvh->nvh_type &= ~NV_ORDER_MASK;
|
|
nvh->nvh_type |= NV_ORDER_NETWORK;
|
|
}
|
|
|
|
vsize = 0;
|
|
|
|
switch (nvh->nvh_type & NV_TYPE_MASK) {
|
|
case NV_TYPE_INT8:
|
|
case NV_TYPE_UINT8:
|
|
case NV_TYPE_INT8_ARRAY:
|
|
case NV_TYPE_UINT8_ARRAY:
|
|
break;
|
|
case NV_TYPE_INT16:
|
|
case NV_TYPE_UINT16:
|
|
case NV_TYPE_INT16_ARRAY:
|
|
case NV_TYPE_UINT16_ARRAY:
|
|
if (vsize == 0)
|
|
vsize = 2;
|
|
/* FALLTHOUGH */
|
|
case NV_TYPE_INT32:
|
|
case NV_TYPE_UINT32:
|
|
case NV_TYPE_INT32_ARRAY:
|
|
case NV_TYPE_UINT32_ARRAY:
|
|
if (vsize == 0)
|
|
vsize = 4;
|
|
/* FALLTHOUGH */
|
|
case NV_TYPE_INT64:
|
|
case NV_TYPE_UINT64:
|
|
case NV_TYPE_INT64_ARRAY:
|
|
case NV_TYPE_UINT64_ARRAY:
|
|
if (vsize == 0)
|
|
vsize = 8;
|
|
for (p = data; p < end; p += vsize) {
|
|
if (tohost) {
|
|
switch (vsize) {
|
|
case 2:
|
|
*(uint16_t *)(void *)p =
|
|
le16toh(*(uint16_t *)(void *)p);
|
|
break;
|
|
case 4:
|
|
*(uint32_t *)(void *)p =
|
|
le32toh(*(uint32_t *)(void *)p);
|
|
break;
|
|
case 8:
|
|
*(uint64_t *)(void *)p =
|
|
le64toh(*(uint64_t *)(void *)p);
|
|
break;
|
|
default:
|
|
assert(!"invalid condition");
|
|
}
|
|
} else {
|
|
switch (vsize) {
|
|
case 2:
|
|
*(uint16_t *)(void *)p =
|
|
htole16(*(uint16_t *)(void *)p);
|
|
break;
|
|
case 4:
|
|
*(uint32_t *)(void *)p =
|
|
htole32(*(uint32_t *)(void *)p);
|
|
break;
|
|
case 8:
|
|
*(uint64_t *)(void *)p =
|
|
htole64(*(uint64_t *)(void *)p);
|
|
break;
|
|
default:
|
|
assert(!"invalid condition");
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case NV_TYPE_STRING:
|
|
break;
|
|
default:
|
|
assert(!"unrecognized type");
|
|
}
|
|
}
|