nv.c revision 217732
1204076Spjd/*- 2204076Spjd * Copyright (c) 2009-2010 The FreeBSD Foundation 3204076Spjd * All rights reserved. 4204076Spjd * 5204076Spjd * This software was developed by Pawel Jakub Dawidek under sponsorship from 6204076Spjd * the FreeBSD Foundation. 7204076Spjd * 8204076Spjd * Redistribution and use in source and binary forms, with or without 9204076Spjd * modification, are permitted provided that the following conditions 10204076Spjd * are met: 11204076Spjd * 1. Redistributions of source code must retain the above copyright 12204076Spjd * notice, this list of conditions and the following disclaimer. 13204076Spjd * 2. Redistributions in binary form must reproduce the above copyright 14204076Spjd * notice, this list of conditions and the following disclaimer in the 15204076Spjd * documentation and/or other materials provided with the distribution. 16204076Spjd * 17204076Spjd * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 18204076Spjd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19204076Spjd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20204076Spjd * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 21204076Spjd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22204076Spjd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23204076Spjd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24204076Spjd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25204076Spjd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26204076Spjd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27204076Spjd * SUCH DAMAGE. 28204076Spjd */ 29204076Spjd 30204076Spjd#include <sys/cdefs.h> 31204076Spjd__FBSDID("$FreeBSD: head/sbin/hastd/nv.c 217732 2011-01-22 22:38:18Z pjd $"); 32204076Spjd 33204076Spjd#include <sys/param.h> 34204076Spjd#include <sys/endian.h> 35204076Spjd 36204076Spjd#include <assert.h> 37204076Spjd#include <bitstring.h> 38204076Spjd#include <errno.h> 39204076Spjd#include <stdarg.h> 40204076Spjd#include <stdbool.h> 41204076Spjd#include <stdint.h> 42204076Spjd#include <stdlib.h> 43204076Spjd#include <string.h> 44204076Spjd#include <unistd.h> 45204076Spjd 46204076Spjd#include <ebuf.h> 47204076Spjd#include <nv.h> 48204076Spjd 49214283Spjd#define NV_TYPE_NONE 0 50214283Spjd 51214282Spjd#define NV_TYPE_INT8 1 52214282Spjd#define NV_TYPE_UINT8 2 53214282Spjd#define NV_TYPE_INT16 3 54214282Spjd#define NV_TYPE_UINT16 4 55214282Spjd#define NV_TYPE_INT32 5 56214282Spjd#define NV_TYPE_UINT32 6 57214282Spjd#define NV_TYPE_INT64 7 58214282Spjd#define NV_TYPE_UINT64 8 59214282Spjd#define NV_TYPE_INT8_ARRAY 9 60214282Spjd#define NV_TYPE_UINT8_ARRAY 10 61214282Spjd#define NV_TYPE_INT16_ARRAY 11 62214282Spjd#define NV_TYPE_UINT16_ARRAY 12 63214282Spjd#define NV_TYPE_INT32_ARRAY 13 64214282Spjd#define NV_TYPE_UINT32_ARRAY 14 65214282Spjd#define NV_TYPE_INT64_ARRAY 15 66214282Spjd#define NV_TYPE_UINT64_ARRAY 16 67214282Spjd#define NV_TYPE_STRING 17 68214282Spjd 69214282Spjd#define NV_TYPE_MASK 0x7f 70214282Spjd#define NV_TYPE_FIRST NV_TYPE_INT8 71214282Spjd#define NV_TYPE_LAST NV_TYPE_STRING 72214282Spjd 73214282Spjd#define NV_ORDER_NETWORK 0x00 74214282Spjd#define NV_ORDER_HOST 0x80 75214282Spjd 76214282Spjd#define NV_ORDER_MASK 0x80 77214282Spjd 78204076Spjd#define NV_MAGIC 0xaea1e 79204076Spjdstruct nv { 80204076Spjd int nv_magic; 81204076Spjd int nv_error; 82204076Spjd struct ebuf *nv_ebuf; 83204076Spjd}; 84204076Spjd 85204076Spjdstruct nvhdr { 86204076Spjd uint8_t nvh_type; 87204076Spjd uint8_t nvh_namesize; 88204076Spjd uint32_t nvh_dsize; 89204076Spjd char nvh_name[0]; 90204076Spjd} __packed; 91204076Spjd#define NVH_DATA(nvh) ((unsigned char *)nvh + NVH_HSIZE(nvh)) 92204076Spjd#define NVH_HSIZE(nvh) \ 93204076Spjd (sizeof(struct nvhdr) + roundup2((nvh)->nvh_namesize, 8)) 94204076Spjd#define NVH_DSIZE(nvh) \ 95204076Spjd (((nvh)->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST ? \ 96204076Spjd (nvh)->nvh_dsize : \ 97204076Spjd le32toh((nvh)->nvh_dsize)) 98204076Spjd#define NVH_SIZE(nvh) (NVH_HSIZE(nvh) + roundup2(NVH_DSIZE(nvh), 8)) 99204076Spjd 100204076Spjd#define NV_CHECK(nv) do { \ 101204076Spjd assert((nv) != NULL); \ 102204076Spjd assert((nv)->nv_magic == NV_MAGIC); \ 103204076Spjd} while (0) 104204076Spjd 105204076Spjdstatic void nv_add(struct nv *nv, const unsigned char *value, size_t vsize, 106204076Spjd int type, const char *name); 107204076Spjdstatic void nv_addv(struct nv *nv, const unsigned char *value, size_t vsize, 108204076Spjd int type, const char *namefmt, va_list nameap); 109204076Spjdstatic struct nvhdr *nv_find(struct nv *nv, int type, const char *namefmt, 110204076Spjd va_list nameap); 111204076Spjdstatic void nv_swap(struct nvhdr *nvh, bool tohost); 112204076Spjd 113204076Spjd/* 114204076Spjd * Allocate and initialize new nv structure. 115204076Spjd * Return NULL in case of malloc(3) failure. 116204076Spjd */ 117204076Spjdstruct nv * 118204076Spjdnv_alloc(void) 119204076Spjd{ 120204076Spjd struct nv *nv; 121204076Spjd 122204076Spjd nv = malloc(sizeof(*nv)); 123204076Spjd if (nv == NULL) 124204076Spjd return (NULL); 125204076Spjd nv->nv_ebuf = ebuf_alloc(0); 126204076Spjd if (nv->nv_ebuf == NULL) { 127204076Spjd free(nv); 128204076Spjd return (NULL); 129204076Spjd } 130204076Spjd nv->nv_error = 0; 131204076Spjd nv->nv_magic = NV_MAGIC; 132204076Spjd return (nv); 133204076Spjd} 134204076Spjd 135204076Spjd/* 136204076Spjd * Free the given nv structure. 137204076Spjd */ 138204076Spjdvoid 139204076Spjdnv_free(struct nv *nv) 140204076Spjd{ 141204076Spjd 142204076Spjd if (nv == NULL) 143204076Spjd return; 144204076Spjd 145204076Spjd NV_CHECK(nv); 146204076Spjd 147204076Spjd nv->nv_magic = 0; 148204076Spjd ebuf_free(nv->nv_ebuf); 149204076Spjd free(nv); 150204076Spjd} 151204076Spjd 152204076Spjd/* 153204076Spjd * Return error for the given nv structure. 154204076Spjd */ 155204076Spjdint 156204076Spjdnv_error(const struct nv *nv) 157204076Spjd{ 158204076Spjd 159204076Spjd if (nv == NULL) 160204076Spjd return (ENOMEM); 161204076Spjd 162204076Spjd NV_CHECK(nv); 163204076Spjd 164204076Spjd return (nv->nv_error); 165204076Spjd} 166204076Spjd 167204076Spjd/* 168204076Spjd * Set error for the given nv structure and return previous error. 169204076Spjd */ 170204076Spjdint 171204076Spjdnv_set_error(struct nv *nv, int error) 172204076Spjd{ 173204076Spjd int preverr; 174204076Spjd 175204076Spjd if (nv == NULL) 176204076Spjd return (ENOMEM); 177204076Spjd 178204076Spjd NV_CHECK(nv); 179204076Spjd 180204076Spjd preverr = nv->nv_error; 181204076Spjd nv->nv_error = error; 182204076Spjd return (preverr); 183204076Spjd} 184204076Spjd 185204076Spjd/* 186204076Spjd * Validate correctness of the entire nv structure and all its elements. 187204076Spjd * If extrap is not NULL, store number of extra bytes at the end of the buffer. 188204076Spjd */ 189204076Spjdint 190204076Spjdnv_validate(struct nv *nv, size_t *extrap) 191204076Spjd{ 192204076Spjd struct nvhdr *nvh; 193204076Spjd unsigned char *data, *ptr; 194204076Spjd size_t dsize, size, vsize; 195204076Spjd int error; 196204076Spjd 197204076Spjd if (nv == NULL) { 198204076Spjd errno = ENOMEM; 199204076Spjd return (-1); 200204076Spjd } 201204076Spjd 202204076Spjd NV_CHECK(nv); 203204076Spjd assert(nv->nv_error == 0); 204204076Spjd 205204076Spjd /* TODO: Check that names are unique? */ 206204076Spjd 207204076Spjd error = 0; 208204076Spjd ptr = ebuf_data(nv->nv_ebuf, &size); 209204076Spjd while (size > 0) { 210204076Spjd /* 211204076Spjd * Zeros at the end of the buffer are acceptable. 212204076Spjd */ 213204076Spjd if (ptr[0] == '\0') 214204076Spjd break; 215204076Spjd /* 216204076Spjd * Minimum size at this point is size of nvhdr structure, one 217204076Spjd * character long name plus terminating '\0'. 218204076Spjd */ 219204076Spjd if (size < sizeof(*nvh) + 2) { 220204076Spjd error = EINVAL; 221204076Spjd break; 222204076Spjd } 223204076Spjd nvh = (struct nvhdr *)ptr; 224204076Spjd if (size < NVH_HSIZE(nvh)) { 225204076Spjd error = EINVAL; 226204076Spjd break; 227204076Spjd } 228204076Spjd if (nvh->nvh_name[nvh->nvh_namesize - 1] != '\0') { 229204076Spjd error = EINVAL; 230204076Spjd break; 231204076Spjd } 232204076Spjd if (strlen(nvh->nvh_name) != 233204076Spjd (size_t)(nvh->nvh_namesize - 1)) { 234204076Spjd error = EINVAL; 235204076Spjd break; 236204076Spjd } 237204076Spjd if ((nvh->nvh_type & NV_TYPE_MASK) < NV_TYPE_FIRST || 238204076Spjd (nvh->nvh_type & NV_TYPE_MASK) > NV_TYPE_LAST) { 239204076Spjd error = EINVAL; 240204076Spjd break; 241204076Spjd } 242204076Spjd dsize = NVH_DSIZE(nvh); 243204076Spjd if (dsize == 0) { 244204076Spjd error = EINVAL; 245204076Spjd break; 246204076Spjd } 247204076Spjd if (size < NVH_SIZE(nvh)) { 248204076Spjd error = EINVAL; 249204076Spjd break; 250204076Spjd } 251204076Spjd vsize = 0; 252204076Spjd switch (nvh->nvh_type & NV_TYPE_MASK) { 253204076Spjd case NV_TYPE_INT8: 254204076Spjd case NV_TYPE_UINT8: 255204076Spjd if (vsize == 0) 256204076Spjd vsize = 1; 257204076Spjd /* FALLTHOUGH */ 258204076Spjd case NV_TYPE_INT16: 259204076Spjd case NV_TYPE_UINT16: 260204076Spjd if (vsize == 0) 261204076Spjd vsize = 2; 262204076Spjd /* FALLTHOUGH */ 263204076Spjd case NV_TYPE_INT32: 264204076Spjd case NV_TYPE_UINT32: 265204076Spjd if (vsize == 0) 266204076Spjd vsize = 4; 267204076Spjd /* FALLTHOUGH */ 268204076Spjd case NV_TYPE_INT64: 269204076Spjd case NV_TYPE_UINT64: 270204076Spjd if (vsize == 0) 271204076Spjd vsize = 8; 272204076Spjd if (dsize != vsize) { 273204076Spjd error = EINVAL; 274204076Spjd break; 275204076Spjd } 276204076Spjd break; 277204076Spjd case NV_TYPE_INT8_ARRAY: 278204076Spjd case NV_TYPE_UINT8_ARRAY: 279204076Spjd break; 280204076Spjd case NV_TYPE_INT16_ARRAY: 281204076Spjd case NV_TYPE_UINT16_ARRAY: 282204076Spjd if (vsize == 0) 283204076Spjd vsize = 2; 284204076Spjd /* FALLTHOUGH */ 285204076Spjd case NV_TYPE_INT32_ARRAY: 286204076Spjd case NV_TYPE_UINT32_ARRAY: 287204076Spjd if (vsize == 0) 288204076Spjd vsize = 4; 289204076Spjd /* FALLTHOUGH */ 290204076Spjd case NV_TYPE_INT64_ARRAY: 291204076Spjd case NV_TYPE_UINT64_ARRAY: 292204076Spjd if (vsize == 0) 293204076Spjd vsize = 8; 294204076Spjd if ((dsize % vsize) != 0) { 295204076Spjd error = EINVAL; 296204076Spjd break; 297204076Spjd } 298204076Spjd break; 299204076Spjd case NV_TYPE_STRING: 300204076Spjd data = NVH_DATA(nvh); 301204076Spjd if (data[dsize - 1] != '\0') { 302204076Spjd error = EINVAL; 303204076Spjd break; 304204076Spjd } 305204076Spjd if (strlen((char *)data) != dsize - 1) { 306204076Spjd error = EINVAL; 307204076Spjd break; 308204076Spjd } 309204076Spjd break; 310204076Spjd default: 311204076Spjd assert(!"invalid condition"); 312204076Spjd } 313204076Spjd if (error != 0) 314204076Spjd break; 315204076Spjd ptr += NVH_SIZE(nvh); 316204076Spjd size -= NVH_SIZE(nvh); 317204076Spjd } 318204076Spjd if (error != 0) { 319204076Spjd errno = error; 320204076Spjd if (nv->nv_error == 0) 321204076Spjd nv->nv_error = error; 322204076Spjd return (-1); 323204076Spjd } 324204076Spjd if (extrap != NULL) 325204076Spjd *extrap = size; 326204076Spjd return (0); 327204076Spjd} 328204076Spjd 329204076Spjd/* 330204076Spjd * Convert the given nv structure to network byte order and return ebuf 331204076Spjd * structure. 332204076Spjd */ 333204076Spjdstruct ebuf * 334204076Spjdnv_hton(struct nv *nv) 335204076Spjd{ 336204076Spjd struct nvhdr *nvh; 337204076Spjd unsigned char *ptr; 338204076Spjd size_t size; 339204076Spjd 340204076Spjd NV_CHECK(nv); 341204076Spjd assert(nv->nv_error == 0); 342204076Spjd 343204076Spjd ptr = ebuf_data(nv->nv_ebuf, &size); 344204076Spjd while (size > 0) { 345204076Spjd /* 346204076Spjd * Minimum size at this point is size of nvhdr structure, 347204076Spjd * one character long name plus terminating '\0'. 348204076Spjd */ 349204076Spjd assert(size >= sizeof(*nvh) + 2); 350204076Spjd nvh = (struct nvhdr *)ptr; 351204076Spjd assert(NVH_SIZE(nvh) <= size); 352204076Spjd nv_swap(nvh, false); 353204076Spjd ptr += NVH_SIZE(nvh); 354204076Spjd size -= NVH_SIZE(nvh); 355204076Spjd } 356204076Spjd 357204076Spjd return (nv->nv_ebuf); 358204076Spjd} 359204076Spjd 360204076Spjd/* 361204076Spjd * Create nv structure based on ebuf received from the network. 362204076Spjd */ 363204076Spjdstruct nv * 364204076Spjdnv_ntoh(struct ebuf *eb) 365204076Spjd{ 366204076Spjd struct nv *nv; 367204076Spjd size_t extra; 368204076Spjd int rerrno; 369204076Spjd 370204076Spjd assert(eb != NULL); 371204076Spjd 372204076Spjd nv = malloc(sizeof(*nv)); 373204076Spjd if (nv == NULL) 374204076Spjd return (NULL); 375204076Spjd nv->nv_error = 0; 376204076Spjd nv->nv_ebuf = eb; 377204076Spjd nv->nv_magic = NV_MAGIC; 378204076Spjd 379204076Spjd if (nv_validate(nv, &extra) < 0) { 380204076Spjd rerrno = errno; 381204076Spjd nv->nv_magic = 0; 382204076Spjd free(nv); 383204076Spjd errno = rerrno; 384204076Spjd return (NULL); 385204076Spjd } 386204076Spjd /* 387204076Spjd * Remove extra zeros at the end of the buffer. 388204076Spjd */ 389204076Spjd ebuf_del_tail(eb, extra); 390204076Spjd 391204076Spjd return (nv); 392204076Spjd} 393204076Spjd 394204076Spjd#define NV_DEFINE_ADD(type, TYPE) \ 395204076Spjdvoid \ 396204076Spjdnv_add_##type(struct nv *nv, type##_t value, const char *namefmt, ...) \ 397204076Spjd{ \ 398204076Spjd va_list nameap; \ 399204076Spjd \ 400204076Spjd va_start(nameap, namefmt); \ 401204076Spjd nv_addv(nv, (unsigned char *)&value, sizeof(value), \ 402204076Spjd NV_TYPE_##TYPE, namefmt, nameap); \ 403204076Spjd va_end(nameap); \ 404204076Spjd} 405204076Spjd 406204076SpjdNV_DEFINE_ADD(int8, INT8) 407204076SpjdNV_DEFINE_ADD(uint8, UINT8) 408204076SpjdNV_DEFINE_ADD(int16, INT16) 409204076SpjdNV_DEFINE_ADD(uint16, UINT16) 410204076SpjdNV_DEFINE_ADD(int32, INT32) 411204076SpjdNV_DEFINE_ADD(uint32, UINT32) 412204076SpjdNV_DEFINE_ADD(int64, INT64) 413204076SpjdNV_DEFINE_ADD(uint64, UINT64) 414204076Spjd 415204076Spjd#undef NV_DEFINE_ADD 416204076Spjd 417204076Spjd#define NV_DEFINE_ADD_ARRAY(type, TYPE) \ 418204076Spjdvoid \ 419204076Spjdnv_add_##type##_array(struct nv *nv, const type##_t *value, \ 420204076Spjd size_t nsize, const char *namefmt, ...) \ 421204076Spjd{ \ 422204076Spjd va_list nameap; \ 423204076Spjd \ 424204076Spjd va_start(nameap, namefmt); \ 425204076Spjd nv_addv(nv, (const unsigned char *)value, \ 426204076Spjd sizeof(value[0]) * nsize, NV_TYPE_##TYPE##_ARRAY, namefmt, \ 427204076Spjd nameap); \ 428204076Spjd va_end(nameap); \ 429204076Spjd} 430204076Spjd 431204076SpjdNV_DEFINE_ADD_ARRAY(int8, INT8) 432204076SpjdNV_DEFINE_ADD_ARRAY(uint8, UINT8) 433204076SpjdNV_DEFINE_ADD_ARRAY(int16, INT16) 434204076SpjdNV_DEFINE_ADD_ARRAY(uint16, UINT16) 435204076SpjdNV_DEFINE_ADD_ARRAY(int32, INT32) 436204076SpjdNV_DEFINE_ADD_ARRAY(uint32, UINT32) 437204076SpjdNV_DEFINE_ADD_ARRAY(int64, INT64) 438204076SpjdNV_DEFINE_ADD_ARRAY(uint64, UINT64) 439204076Spjd 440204076Spjd#undef NV_DEFINE_ADD_ARRAY 441204076Spjd 442204076Spjdvoid 443204076Spjdnv_add_string(struct nv *nv, const char *value, const char *namefmt, ...) 444204076Spjd{ 445204076Spjd va_list nameap; 446204076Spjd size_t size; 447204076Spjd 448204076Spjd size = strlen(value) + 1; 449204076Spjd 450204076Spjd va_start(nameap, namefmt); 451204076Spjd nv_addv(nv, (const unsigned char *)value, size, NV_TYPE_STRING, 452204076Spjd namefmt, nameap); 453204076Spjd va_end(nameap); 454204076Spjd} 455204076Spjd 456204076Spjdvoid 457204076Spjdnv_add_stringf(struct nv *nv, const char *name, const char *valuefmt, ...) 458204076Spjd{ 459204076Spjd va_list valueap; 460204076Spjd 461204076Spjd va_start(valueap, valuefmt); 462204076Spjd nv_add_stringv(nv, name, valuefmt, valueap); 463204076Spjd va_end(valueap); 464204076Spjd} 465204076Spjd 466204076Spjdvoid 467204076Spjdnv_add_stringv(struct nv *nv, const char *name, const char *valuefmt, 468204076Spjd va_list valueap) 469204076Spjd{ 470204076Spjd char *value; 471204076Spjd ssize_t size; 472204076Spjd 473204076Spjd size = vasprintf(&value, valuefmt, valueap); 474204076Spjd if (size < 0) { 475204076Spjd if (nv->nv_error == 0) 476204076Spjd nv->nv_error = ENOMEM; 477204076Spjd return; 478204076Spjd } 479204076Spjd size++; 480204076Spjd nv_add(nv, (const unsigned char *)value, size, NV_TYPE_STRING, name); 481204076Spjd free(value); 482204076Spjd} 483204076Spjd 484204076Spjd#define NV_DEFINE_GET(type, TYPE) \ 485204076Spjdtype##_t \ 486204076Spjdnv_get_##type(struct nv *nv, const char *namefmt, ...) \ 487204076Spjd{ \ 488204076Spjd struct nvhdr *nvh; \ 489204076Spjd va_list nameap; \ 490204076Spjd type##_t value; \ 491204076Spjd \ 492204076Spjd va_start(nameap, namefmt); \ 493204076Spjd nvh = nv_find(nv, NV_TYPE_##TYPE, namefmt, nameap); \ 494204076Spjd va_end(nameap); \ 495204076Spjd if (nvh == NULL) \ 496204076Spjd return (0); \ 497204076Spjd assert((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST); \ 498204076Spjd assert(sizeof(value) == nvh->nvh_dsize); \ 499204076Spjd bcopy(NVH_DATA(nvh), &value, sizeof(value)); \ 500204076Spjd \ 501204076Spjd return (value); \ 502204076Spjd} 503204076Spjd 504204076SpjdNV_DEFINE_GET(int8, INT8) 505204076SpjdNV_DEFINE_GET(uint8, UINT8) 506204076SpjdNV_DEFINE_GET(int16, INT16) 507204076SpjdNV_DEFINE_GET(uint16, UINT16) 508204076SpjdNV_DEFINE_GET(int32, INT32) 509204076SpjdNV_DEFINE_GET(uint32, UINT32) 510204076SpjdNV_DEFINE_GET(int64, INT64) 511204076SpjdNV_DEFINE_GET(uint64, UINT64) 512204076Spjd 513204076Spjd#undef NV_DEFINE_GET 514204076Spjd 515204076Spjd#define NV_DEFINE_GET_ARRAY(type, TYPE) \ 516204076Spjdconst type##_t * \ 517204076Spjdnv_get_##type##_array(struct nv *nv, size_t *sizep, \ 518204076Spjd const char *namefmt, ...) \ 519204076Spjd{ \ 520204076Spjd struct nvhdr *nvh; \ 521204076Spjd va_list nameap; \ 522204076Spjd \ 523204076Spjd va_start(nameap, namefmt); \ 524204076Spjd nvh = nv_find(nv, NV_TYPE_##TYPE##_ARRAY, namefmt, nameap); \ 525204076Spjd va_end(nameap); \ 526204076Spjd if (nvh == NULL) \ 527204076Spjd return (NULL); \ 528204076Spjd assert((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST); \ 529204076Spjd assert((nvh->nvh_dsize % sizeof(type##_t)) == 0); \ 530204076Spjd if (sizep != NULL) \ 531204076Spjd *sizep = nvh->nvh_dsize / sizeof(type##_t); \ 532204076Spjd return ((type##_t *)(void *)NVH_DATA(nvh)); \ 533204076Spjd} 534204076Spjd 535204076SpjdNV_DEFINE_GET_ARRAY(int8, INT8) 536204076SpjdNV_DEFINE_GET_ARRAY(uint8, UINT8) 537204076SpjdNV_DEFINE_GET_ARRAY(int16, INT16) 538204076SpjdNV_DEFINE_GET_ARRAY(uint16, UINT16) 539204076SpjdNV_DEFINE_GET_ARRAY(int32, INT32) 540204076SpjdNV_DEFINE_GET_ARRAY(uint32, UINT32) 541204076SpjdNV_DEFINE_GET_ARRAY(int64, INT64) 542204076SpjdNV_DEFINE_GET_ARRAY(uint64, UINT64) 543204076Spjd 544204076Spjd#undef NV_DEFINE_GET_ARRAY 545204076Spjd 546204076Spjdconst char * 547204076Spjdnv_get_string(struct nv *nv, const char *namefmt, ...) 548204076Spjd{ 549204076Spjd struct nvhdr *nvh; 550204076Spjd va_list nameap; 551204076Spjd char *str; 552204076Spjd 553204076Spjd va_start(nameap, namefmt); 554204076Spjd nvh = nv_find(nv, NV_TYPE_STRING, namefmt, nameap); 555204076Spjd va_end(nameap); 556204076Spjd if (nvh == NULL) 557204076Spjd return (NULL); 558204076Spjd assert((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST); 559204076Spjd assert(nvh->nvh_dsize >= 1); 560204076Spjd str = NVH_DATA(nvh); 561204076Spjd assert(str[nvh->nvh_dsize - 1] == '\0'); 562204076Spjd assert(strlen(str) == nvh->nvh_dsize - 1); 563204076Spjd return (str); 564204076Spjd} 565204076Spjd 566217732Spjdstatic bool 567217732Spjdnv_vexists(struct nv *nv, const char *namefmt, va_list nameap) 568214283Spjd{ 569214283Spjd struct nvhdr *nvh; 570214283Spjd int snverror, serrno; 571214283Spjd 572214283Spjd if (nv == NULL) 573214283Spjd return (false); 574214283Spjd 575214283Spjd serrno = errno; 576214283Spjd snverror = nv->nv_error; 577214283Spjd 578214283Spjd nvh = nv_find(nv, NV_TYPE_NONE, namefmt, nameap); 579214283Spjd 580214283Spjd errno = serrno; 581214283Spjd nv->nv_error = snverror; 582214283Spjd 583214283Spjd return (nvh != NULL); 584214283Spjd} 585214283Spjd 586217732Spjdbool 587217732Spjdnv_exists(struct nv *nv, const char *namefmt, ...) 588217732Spjd{ 589217732Spjd va_list nameap; 590217732Spjd bool ret; 591217732Spjd 592217732Spjd va_start(nameap, namefmt); 593217732Spjd ret = nv_vexists(nv, namefmt, nameap); 594217732Spjd va_end(nameap); 595217732Spjd 596217732Spjd return (ret); 597217732Spjd} 598217732Spjd 599217732Spjdvoid 600217732Spjdnv_assert(struct nv *nv, const char *namefmt, ...) 601217732Spjd{ 602217732Spjd va_list nameap; 603217732Spjd 604217732Spjd va_start(nameap, namefmt); 605217732Spjd assert(nv_vexists(nv, namefmt, nameap)); 606217732Spjd va_end(nameap); 607217732Spjd} 608217732Spjd 609204076Spjd/* 610204076Spjd * Dump content of the nv structure. 611204076Spjd */ 612204076Spjdvoid 613204076Spjdnv_dump(struct nv *nv) 614204076Spjd{ 615204076Spjd struct nvhdr *nvh; 616204076Spjd unsigned char *data, *ptr; 617204076Spjd size_t dsize, size; 618204076Spjd unsigned int ii; 619204076Spjd bool swap; 620204076Spjd 621204076Spjd if (nv_validate(nv, NULL) < 0) { 622204076Spjd printf("error: %d\n", errno); 623204076Spjd return; 624204076Spjd } 625204076Spjd 626204076Spjd NV_CHECK(nv); 627204076Spjd assert(nv->nv_error == 0); 628204076Spjd 629204076Spjd ptr = ebuf_data(nv->nv_ebuf, &size); 630204076Spjd while (size > 0) { 631204076Spjd assert(size >= sizeof(*nvh) + 2); 632204076Spjd nvh = (struct nvhdr *)ptr; 633204076Spjd assert(size >= NVH_SIZE(nvh)); 634204076Spjd swap = ((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_NETWORK); 635204076Spjd dsize = NVH_DSIZE(nvh); 636204076Spjd data = NVH_DATA(nvh); 637204076Spjd printf(" %s", nvh->nvh_name); 638204076Spjd switch (nvh->nvh_type & NV_TYPE_MASK) { 639204076Spjd case NV_TYPE_INT8: 640204076Spjd printf("(int8): %jd", (intmax_t)(*(int8_t *)data)); 641204076Spjd break; 642204076Spjd case NV_TYPE_UINT8: 643204076Spjd printf("(uint8): %ju", (uintmax_t)(*(uint8_t *)data)); 644204076Spjd break; 645204076Spjd case NV_TYPE_INT16: 646204076Spjd printf("(int16): %jd", swap ? 647204076Spjd (intmax_t)le16toh(*(int16_t *)(void *)data) : 648204076Spjd (intmax_t)*(int16_t *)(void *)data); 649204076Spjd break; 650204076Spjd case NV_TYPE_UINT16: 651204076Spjd printf("(uint16): %ju", swap ? 652204076Spjd (uintmax_t)le16toh(*(uint16_t *)(void *)data) : 653204076Spjd (uintmax_t)*(uint16_t *)(void *)data); 654204076Spjd break; 655204076Spjd case NV_TYPE_INT32: 656204076Spjd printf("(int32): %jd", swap ? 657204076Spjd (intmax_t)le32toh(*(int32_t *)(void *)data) : 658204076Spjd (intmax_t)*(int32_t *)(void *)data); 659204076Spjd break; 660204076Spjd case NV_TYPE_UINT32: 661204076Spjd printf("(uint32): %ju", swap ? 662204076Spjd (uintmax_t)le32toh(*(uint32_t *)(void *)data) : 663204076Spjd (uintmax_t)*(uint32_t *)(void *)data); 664204076Spjd break; 665204076Spjd case NV_TYPE_INT64: 666204076Spjd printf("(int64): %jd", swap ? 667204076Spjd (intmax_t)le64toh(*(int64_t *)(void *)data) : 668204076Spjd (intmax_t)*(int64_t *)(void *)data); 669204076Spjd break; 670204076Spjd case NV_TYPE_UINT64: 671204076Spjd printf("(uint64): %ju", swap ? 672204076Spjd (uintmax_t)le64toh(*(uint64_t *)(void *)data) : 673204076Spjd (uintmax_t)*(uint64_t *)(void *)data); 674204076Spjd break; 675204076Spjd case NV_TYPE_INT8_ARRAY: 676204076Spjd printf("(int8 array):"); 677204076Spjd for (ii = 0; ii < dsize; ii++) 678204076Spjd printf(" %jd", (intmax_t)((int8_t *)data)[ii]); 679204076Spjd break; 680204076Spjd case NV_TYPE_UINT8_ARRAY: 681204076Spjd printf("(uint8 array):"); 682204076Spjd for (ii = 0; ii < dsize; ii++) 683204076Spjd printf(" %ju", (uintmax_t)((uint8_t *)data)[ii]); 684204076Spjd break; 685204076Spjd case NV_TYPE_INT16_ARRAY: 686204076Spjd printf("(int16 array):"); 687204076Spjd for (ii = 0; ii < dsize / 2; ii++) { 688204076Spjd printf(" %jd", swap ? 689204076Spjd (intmax_t)le16toh(((int16_t *)(void *)data)[ii]) : 690204076Spjd (intmax_t)((int16_t *)(void *)data)[ii]); 691204076Spjd } 692204076Spjd break; 693204076Spjd case NV_TYPE_UINT16_ARRAY: 694204076Spjd printf("(uint16 array):"); 695204076Spjd for (ii = 0; ii < dsize / 2; ii++) { 696204076Spjd printf(" %ju", swap ? 697204076Spjd (uintmax_t)le16toh(((uint16_t *)(void *)data)[ii]) : 698204076Spjd (uintmax_t)((uint16_t *)(void *)data)[ii]); 699204076Spjd } 700204076Spjd break; 701204076Spjd case NV_TYPE_INT32_ARRAY: 702204076Spjd printf("(int32 array):"); 703204076Spjd for (ii = 0; ii < dsize / 4; ii++) { 704204076Spjd printf(" %jd", swap ? 705204076Spjd (intmax_t)le32toh(((int32_t *)(void *)data)[ii]) : 706204076Spjd (intmax_t)((int32_t *)(void *)data)[ii]); 707204076Spjd } 708204076Spjd break; 709204076Spjd case NV_TYPE_UINT32_ARRAY: 710204076Spjd printf("(uint32 array):"); 711204076Spjd for (ii = 0; ii < dsize / 4; ii++) { 712204076Spjd printf(" %ju", swap ? 713204076Spjd (uintmax_t)le32toh(((uint32_t *)(void *)data)[ii]) : 714204076Spjd (uintmax_t)((uint32_t *)(void *)data)[ii]); 715204076Spjd } 716204076Spjd break; 717204076Spjd case NV_TYPE_INT64_ARRAY: 718204076Spjd printf("(int64 array):"); 719204076Spjd for (ii = 0; ii < dsize / 8; ii++) { 720204076Spjd printf(" %ju", swap ? 721204076Spjd (uintmax_t)le64toh(((uint64_t *)(void *)data)[ii]) : 722204076Spjd (uintmax_t)((uint64_t *)(void *)data)[ii]); 723204076Spjd } 724204076Spjd break; 725204076Spjd case NV_TYPE_UINT64_ARRAY: 726204076Spjd printf("(uint64 array):"); 727204076Spjd for (ii = 0; ii < dsize / 8; ii++) { 728204076Spjd printf(" %ju", swap ? 729204076Spjd (uintmax_t)le64toh(((uint64_t *)(void *)data)[ii]) : 730204076Spjd (uintmax_t)((uint64_t *)(void *)data)[ii]); 731204076Spjd } 732204076Spjd break; 733204076Spjd case NV_TYPE_STRING: 734204076Spjd printf("(string): %s", (char *)data); 735204076Spjd break; 736204076Spjd default: 737204076Spjd assert(!"invalid condition"); 738204076Spjd } 739204076Spjd printf("\n"); 740204076Spjd ptr += NVH_SIZE(nvh); 741204076Spjd size -= NVH_SIZE(nvh); 742204076Spjd } 743204076Spjd} 744204076Spjd 745204076Spjd/* 746204076Spjd * Local routines below. 747204076Spjd */ 748204076Spjd 749204076Spjdstatic void 750204076Spjdnv_add(struct nv *nv, const unsigned char *value, size_t vsize, int type, 751204076Spjd const char *name) 752204076Spjd{ 753204076Spjd static unsigned char align[7]; 754204076Spjd struct nvhdr *nvh; 755204076Spjd size_t namesize; 756204076Spjd 757204076Spjd if (nv == NULL) { 758204076Spjd errno = ENOMEM; 759204076Spjd return; 760204076Spjd } 761204076Spjd 762204076Spjd NV_CHECK(nv); 763204076Spjd 764204076Spjd namesize = strlen(name) + 1; 765204076Spjd 766204076Spjd nvh = malloc(sizeof(*nvh) + roundup2(namesize, 8)); 767204076Spjd if (nvh == NULL) { 768204076Spjd if (nv->nv_error == 0) 769204076Spjd nv->nv_error = ENOMEM; 770204076Spjd return; 771204076Spjd } 772204076Spjd nvh->nvh_type = NV_ORDER_HOST | type; 773204076Spjd nvh->nvh_namesize = (uint8_t)namesize; 774204076Spjd nvh->nvh_dsize = (uint32_t)vsize; 775204076Spjd bcopy(name, nvh->nvh_name, namesize); 776204076Spjd 777204076Spjd /* Add header first. */ 778204076Spjd if (ebuf_add_tail(nv->nv_ebuf, nvh, NVH_HSIZE(nvh)) < 0) { 779204076Spjd assert(errno != 0); 780204076Spjd if (nv->nv_error == 0) 781204076Spjd nv->nv_error = errno; 782209180Spjd free(nvh); 783204076Spjd return; 784204076Spjd } 785209180Spjd free(nvh); 786204076Spjd /* Add the actual data. */ 787204076Spjd if (ebuf_add_tail(nv->nv_ebuf, value, vsize) < 0) { 788204076Spjd assert(errno != 0); 789204076Spjd if (nv->nv_error == 0) 790204076Spjd nv->nv_error = errno; 791204076Spjd return; 792204076Spjd } 793204076Spjd /* Align the data (if needed). */ 794204076Spjd vsize = roundup2(vsize, 8) - vsize; 795204076Spjd if (vsize == 0) 796204076Spjd return; 797204076Spjd assert(vsize > 0 && vsize <= sizeof(align)); 798204076Spjd if (ebuf_add_tail(nv->nv_ebuf, align, vsize) < 0) { 799204076Spjd assert(errno != 0); 800204076Spjd if (nv->nv_error == 0) 801204076Spjd nv->nv_error = errno; 802204076Spjd return; 803204076Spjd } 804204076Spjd} 805204076Spjd 806204076Spjdstatic void 807204076Spjdnv_addv(struct nv *nv, const unsigned char *value, size_t vsize, int type, 808204076Spjd const char *namefmt, va_list nameap) 809204076Spjd{ 810204076Spjd char name[255]; 811204076Spjd size_t namesize; 812204076Spjd 813204076Spjd namesize = vsnprintf(name, sizeof(name), namefmt, nameap); 814204076Spjd assert(namesize > 0 && namesize < sizeof(name)); 815204076Spjd 816204076Spjd nv_add(nv, value, vsize, type, name); 817204076Spjd} 818204076Spjd 819204076Spjdstatic struct nvhdr * 820204076Spjdnv_find(struct nv *nv, int type, const char *namefmt, va_list nameap) 821204076Spjd{ 822204076Spjd char name[255]; 823204076Spjd struct nvhdr *nvh; 824204076Spjd unsigned char *ptr; 825204076Spjd size_t size, namesize; 826204076Spjd 827204076Spjd if (nv == NULL) { 828204076Spjd errno = ENOMEM; 829204076Spjd return (NULL); 830204076Spjd } 831204076Spjd 832204076Spjd NV_CHECK(nv); 833204076Spjd 834204076Spjd namesize = vsnprintf(name, sizeof(name), namefmt, nameap); 835204076Spjd assert(namesize > 0 && namesize < sizeof(name)); 836204076Spjd namesize++; 837204076Spjd 838204076Spjd ptr = ebuf_data(nv->nv_ebuf, &size); 839204076Spjd while (size > 0) { 840204076Spjd assert(size >= sizeof(*nvh) + 2); 841204076Spjd nvh = (struct nvhdr *)ptr; 842204076Spjd assert(size >= NVH_SIZE(nvh)); 843204076Spjd nv_swap(nvh, true); 844204076Spjd if (strcmp(nvh->nvh_name, name) == 0) { 845214283Spjd if (type != NV_TYPE_NONE && 846214283Spjd (nvh->nvh_type & NV_TYPE_MASK) != type) { 847204076Spjd errno = EINVAL; 848204076Spjd if (nv->nv_error == 0) 849204076Spjd nv->nv_error = EINVAL; 850204076Spjd return (NULL); 851204076Spjd } 852204076Spjd return (nvh); 853204076Spjd } 854204076Spjd ptr += NVH_SIZE(nvh); 855204076Spjd size -= NVH_SIZE(nvh); 856204076Spjd } 857204076Spjd errno = ENOENT; 858204076Spjd if (nv->nv_error == 0) 859204076Spjd nv->nv_error = ENOENT; 860204076Spjd return (NULL); 861204076Spjd} 862204076Spjd 863204076Spjdstatic void 864204076Spjdnv_swap(struct nvhdr *nvh, bool tohost) 865204076Spjd{ 866204076Spjd unsigned char *data, *end, *p; 867204076Spjd size_t vsize; 868204076Spjd 869204076Spjd data = NVH_DATA(nvh); 870204076Spjd if (tohost) { 871204076Spjd if ((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST) 872204076Spjd return; 873204076Spjd nvh->nvh_dsize = le32toh(nvh->nvh_dsize); 874204076Spjd end = data + nvh->nvh_dsize; 875204076Spjd nvh->nvh_type &= ~NV_ORDER_MASK; 876204076Spjd nvh->nvh_type |= NV_ORDER_HOST; 877204076Spjd } else { 878204076Spjd if ((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_NETWORK) 879204076Spjd return; 880204076Spjd end = data + nvh->nvh_dsize; 881204076Spjd nvh->nvh_dsize = htole32(nvh->nvh_dsize); 882204076Spjd nvh->nvh_type &= ~NV_ORDER_MASK; 883204076Spjd nvh->nvh_type |= NV_ORDER_NETWORK; 884204076Spjd } 885204076Spjd 886204076Spjd vsize = 0; 887204076Spjd 888204076Spjd switch (nvh->nvh_type & NV_TYPE_MASK) { 889204076Spjd case NV_TYPE_INT8: 890204076Spjd case NV_TYPE_UINT8: 891204076Spjd case NV_TYPE_INT8_ARRAY: 892204076Spjd case NV_TYPE_UINT8_ARRAY: 893204076Spjd break; 894204076Spjd case NV_TYPE_INT16: 895204076Spjd case NV_TYPE_UINT16: 896204076Spjd case NV_TYPE_INT16_ARRAY: 897204076Spjd case NV_TYPE_UINT16_ARRAY: 898204076Spjd if (vsize == 0) 899204076Spjd vsize = 2; 900204076Spjd /* FALLTHOUGH */ 901204076Spjd case NV_TYPE_INT32: 902204076Spjd case NV_TYPE_UINT32: 903204076Spjd case NV_TYPE_INT32_ARRAY: 904204076Spjd case NV_TYPE_UINT32_ARRAY: 905204076Spjd if (vsize == 0) 906204076Spjd vsize = 4; 907204076Spjd /* FALLTHOUGH */ 908204076Spjd case NV_TYPE_INT64: 909204076Spjd case NV_TYPE_UINT64: 910204076Spjd case NV_TYPE_INT64_ARRAY: 911204076Spjd case NV_TYPE_UINT64_ARRAY: 912204076Spjd if (vsize == 0) 913204076Spjd vsize = 8; 914204076Spjd for (p = data; p < end; p += vsize) { 915204076Spjd if (tohost) { 916204076Spjd switch (vsize) { 917204076Spjd case 2: 918204076Spjd *(uint16_t *)(void *)p = 919204076Spjd le16toh(*(uint16_t *)(void *)p); 920204076Spjd break; 921204076Spjd case 4: 922204076Spjd *(uint32_t *)(void *)p = 923204076Spjd le32toh(*(uint32_t *)(void *)p); 924204076Spjd break; 925204076Spjd case 8: 926204076Spjd *(uint64_t *)(void *)p = 927204076Spjd le64toh(*(uint64_t *)(void *)p); 928204076Spjd break; 929204076Spjd default: 930204076Spjd assert(!"invalid condition"); 931204076Spjd } 932204076Spjd } else { 933204076Spjd switch (vsize) { 934204076Spjd case 2: 935204076Spjd *(uint16_t *)(void *)p = 936204076Spjd htole16(*(uint16_t *)(void *)p); 937204076Spjd break; 938204076Spjd case 4: 939204076Spjd *(uint32_t *)(void *)p = 940204076Spjd htole32(*(uint32_t *)(void *)p); 941204076Spjd break; 942204076Spjd case 8: 943204076Spjd *(uint64_t *)(void *)p = 944204076Spjd htole64(*(uint64_t *)(void *)p); 945204076Spjd break; 946204076Spjd default: 947204076Spjd assert(!"invalid condition"); 948204076Spjd } 949204076Spjd } 950204076Spjd } 951204076Spjd break; 952204076Spjd case NV_TYPE_STRING: 953204076Spjd break; 954204076Spjd default: 955204076Spjd assert(!"unrecognized type"); 956204076Spjd } 957204076Spjd} 958