nv.c revision 217732
1/*- 2 * Copyright (c) 2009-2010 The FreeBSD Foundation 3 * All rights reserved. 4 * 5 * This software was developed by Pawel Jakub Dawidek under sponsorship from 6 * the FreeBSD Foundation. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30#include <sys/cdefs.h> 31__FBSDID("$FreeBSD: head/sbin/hastd/nv.c 217732 2011-01-22 22:38:18Z pjd $"); 32 33#include <sys/param.h> 34#include <sys/endian.h> 35 36#include <assert.h> 37#include <bitstring.h> 38#include <errno.h> 39#include <stdarg.h> 40#include <stdbool.h> 41#include <stdint.h> 42#include <stdlib.h> 43#include <string.h> 44#include <unistd.h> 45 46#include <ebuf.h> 47#include <nv.h> 48 49#define NV_TYPE_NONE 0 50 51#define NV_TYPE_INT8 1 52#define NV_TYPE_UINT8 2 53#define NV_TYPE_INT16 3 54#define NV_TYPE_UINT16 4 55#define NV_TYPE_INT32 5 56#define NV_TYPE_UINT32 6 57#define NV_TYPE_INT64 7 58#define NV_TYPE_UINT64 8 59#define NV_TYPE_INT8_ARRAY 9 60#define NV_TYPE_UINT8_ARRAY 10 61#define NV_TYPE_INT16_ARRAY 11 62#define NV_TYPE_UINT16_ARRAY 12 63#define NV_TYPE_INT32_ARRAY 13 64#define NV_TYPE_UINT32_ARRAY 14 65#define NV_TYPE_INT64_ARRAY 15 66#define NV_TYPE_UINT64_ARRAY 16 67#define NV_TYPE_STRING 17 68 69#define NV_TYPE_MASK 0x7f 70#define NV_TYPE_FIRST NV_TYPE_INT8 71#define NV_TYPE_LAST NV_TYPE_STRING 72 73#define NV_ORDER_NETWORK 0x00 74#define NV_ORDER_HOST 0x80 75 76#define NV_ORDER_MASK 0x80 77 78#define NV_MAGIC 0xaea1e 79struct nv { 80 int nv_magic; 81 int nv_error; 82 struct ebuf *nv_ebuf; 83}; 84 85struct nvhdr { 86 uint8_t nvh_type; 87 uint8_t nvh_namesize; 88 uint32_t nvh_dsize; 89 char nvh_name[0]; 90} __packed; 91#define NVH_DATA(nvh) ((unsigned char *)nvh + NVH_HSIZE(nvh)) 92#define NVH_HSIZE(nvh) \ 93 (sizeof(struct nvhdr) + roundup2((nvh)->nvh_namesize, 8)) 94#define NVH_DSIZE(nvh) \ 95 (((nvh)->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST ? \ 96 (nvh)->nvh_dsize : \ 97 le32toh((nvh)->nvh_dsize)) 98#define NVH_SIZE(nvh) (NVH_HSIZE(nvh) + roundup2(NVH_DSIZE(nvh), 8)) 99 100#define NV_CHECK(nv) do { \ 101 assert((nv) != NULL); \ 102 assert((nv)->nv_magic == NV_MAGIC); \ 103} while (0) 104 105static void nv_add(struct nv *nv, const unsigned char *value, size_t vsize, 106 int type, const char *name); 107static void nv_addv(struct nv *nv, const unsigned char *value, size_t vsize, 108 int type, const char *namefmt, va_list nameap); 109static struct nvhdr *nv_find(struct nv *nv, int type, const char *namefmt, 110 va_list nameap); 111static void nv_swap(struct nvhdr *nvh, bool tohost); 112 113/* 114 * Allocate and initialize new nv structure. 115 * Return NULL in case of malloc(3) failure. 116 */ 117struct nv * 118nv_alloc(void) 119{ 120 struct nv *nv; 121 122 nv = malloc(sizeof(*nv)); 123 if (nv == NULL) 124 return (NULL); 125 nv->nv_ebuf = ebuf_alloc(0); 126 if (nv->nv_ebuf == NULL) { 127 free(nv); 128 return (NULL); 129 } 130 nv->nv_error = 0; 131 nv->nv_magic = NV_MAGIC; 132 return (nv); 133} 134 135/* 136 * Free the given nv structure. 137 */ 138void 139nv_free(struct nv *nv) 140{ 141 142 if (nv == NULL) 143 return; 144 145 NV_CHECK(nv); 146 147 nv->nv_magic = 0; 148 ebuf_free(nv->nv_ebuf); 149 free(nv); 150} 151 152/* 153 * Return error for the given nv structure. 154 */ 155int 156nv_error(const struct nv *nv) 157{ 158 159 if (nv == NULL) 160 return (ENOMEM); 161 162 NV_CHECK(nv); 163 164 return (nv->nv_error); 165} 166 167/* 168 * Set error for the given nv structure and return previous error. 169 */ 170int 171nv_set_error(struct nv *nv, int error) 172{ 173 int preverr; 174 175 if (nv == NULL) 176 return (ENOMEM); 177 178 NV_CHECK(nv); 179 180 preverr = nv->nv_error; 181 nv->nv_error = error; 182 return (preverr); 183} 184 185/* 186 * Validate correctness of the entire nv structure and all its elements. 187 * If extrap is not NULL, store number of extra bytes at the end of the buffer. 188 */ 189int 190nv_validate(struct nv *nv, size_t *extrap) 191{ 192 struct nvhdr *nvh; 193 unsigned char *data, *ptr; 194 size_t dsize, size, vsize; 195 int error; 196 197 if (nv == NULL) { 198 errno = ENOMEM; 199 return (-1); 200 } 201 202 NV_CHECK(nv); 203 assert(nv->nv_error == 0); 204 205 /* TODO: Check that names are unique? */ 206 207 error = 0; 208 ptr = ebuf_data(nv->nv_ebuf, &size); 209 while (size > 0) { 210 /* 211 * Zeros at the end of the buffer are acceptable. 212 */ 213 if (ptr[0] == '\0') 214 break; 215 /* 216 * Minimum size at this point is size of nvhdr structure, one 217 * character long name plus terminating '\0'. 218 */ 219 if (size < sizeof(*nvh) + 2) { 220 error = EINVAL; 221 break; 222 } 223 nvh = (struct nvhdr *)ptr; 224 if (size < NVH_HSIZE(nvh)) { 225 error = EINVAL; 226 break; 227 } 228 if (nvh->nvh_name[nvh->nvh_namesize - 1] != '\0') { 229 error = EINVAL; 230 break; 231 } 232 if (strlen(nvh->nvh_name) != 233 (size_t)(nvh->nvh_namesize - 1)) { 234 error = EINVAL; 235 break; 236 } 237 if ((nvh->nvh_type & NV_TYPE_MASK) < NV_TYPE_FIRST || 238 (nvh->nvh_type & NV_TYPE_MASK) > NV_TYPE_LAST) { 239 error = EINVAL; 240 break; 241 } 242 dsize = NVH_DSIZE(nvh); 243 if (dsize == 0) { 244 error = EINVAL; 245 break; 246 } 247 if (size < NVH_SIZE(nvh)) { 248 error = EINVAL; 249 break; 250 } 251 vsize = 0; 252 switch (nvh->nvh_type & NV_TYPE_MASK) { 253 case NV_TYPE_INT8: 254 case NV_TYPE_UINT8: 255 if (vsize == 0) 256 vsize = 1; 257 /* FALLTHOUGH */ 258 case NV_TYPE_INT16: 259 case NV_TYPE_UINT16: 260 if (vsize == 0) 261 vsize = 2; 262 /* FALLTHOUGH */ 263 case NV_TYPE_INT32: 264 case NV_TYPE_UINT32: 265 if (vsize == 0) 266 vsize = 4; 267 /* FALLTHOUGH */ 268 case NV_TYPE_INT64: 269 case NV_TYPE_UINT64: 270 if (vsize == 0) 271 vsize = 8; 272 if (dsize != vsize) { 273 error = EINVAL; 274 break; 275 } 276 break; 277 case NV_TYPE_INT8_ARRAY: 278 case NV_TYPE_UINT8_ARRAY: 279 break; 280 case NV_TYPE_INT16_ARRAY: 281 case NV_TYPE_UINT16_ARRAY: 282 if (vsize == 0) 283 vsize = 2; 284 /* FALLTHOUGH */ 285 case NV_TYPE_INT32_ARRAY: 286 case NV_TYPE_UINT32_ARRAY: 287 if (vsize == 0) 288 vsize = 4; 289 /* FALLTHOUGH */ 290 case NV_TYPE_INT64_ARRAY: 291 case NV_TYPE_UINT64_ARRAY: 292 if (vsize == 0) 293 vsize = 8; 294 if ((dsize % vsize) != 0) { 295 error = EINVAL; 296 break; 297 } 298 break; 299 case NV_TYPE_STRING: 300 data = NVH_DATA(nvh); 301 if (data[dsize - 1] != '\0') { 302 error = EINVAL; 303 break; 304 } 305 if (strlen((char *)data) != dsize - 1) { 306 error = EINVAL; 307 break; 308 } 309 break; 310 default: 311 assert(!"invalid condition"); 312 } 313 if (error != 0) 314 break; 315 ptr += NVH_SIZE(nvh); 316 size -= NVH_SIZE(nvh); 317 } 318 if (error != 0) { 319 errno = error; 320 if (nv->nv_error == 0) 321 nv->nv_error = error; 322 return (-1); 323 } 324 if (extrap != NULL) 325 *extrap = size; 326 return (0); 327} 328 329/* 330 * Convert the given nv structure to network byte order and return ebuf 331 * structure. 332 */ 333struct ebuf * 334nv_hton(struct nv *nv) 335{ 336 struct nvhdr *nvh; 337 unsigned char *ptr; 338 size_t size; 339 340 NV_CHECK(nv); 341 assert(nv->nv_error == 0); 342 343 ptr = ebuf_data(nv->nv_ebuf, &size); 344 while (size > 0) { 345 /* 346 * Minimum size at this point is size of nvhdr structure, 347 * one character long name plus terminating '\0'. 348 */ 349 assert(size >= sizeof(*nvh) + 2); 350 nvh = (struct nvhdr *)ptr; 351 assert(NVH_SIZE(nvh) <= size); 352 nv_swap(nvh, false); 353 ptr += NVH_SIZE(nvh); 354 size -= NVH_SIZE(nvh); 355 } 356 357 return (nv->nv_ebuf); 358} 359 360/* 361 * Create nv structure based on ebuf received from the network. 362 */ 363struct nv * 364nv_ntoh(struct ebuf *eb) 365{ 366 struct nv *nv; 367 size_t extra; 368 int rerrno; 369 370 assert(eb != NULL); 371 372 nv = malloc(sizeof(*nv)); 373 if (nv == NULL) 374 return (NULL); 375 nv->nv_error = 0; 376 nv->nv_ebuf = eb; 377 nv->nv_magic = NV_MAGIC; 378 379 if (nv_validate(nv, &extra) < 0) { 380 rerrno = errno; 381 nv->nv_magic = 0; 382 free(nv); 383 errno = rerrno; 384 return (NULL); 385 } 386 /* 387 * Remove extra zeros at the end of the buffer. 388 */ 389 ebuf_del_tail(eb, extra); 390 391 return (nv); 392} 393 394#define NV_DEFINE_ADD(type, TYPE) \ 395void \ 396nv_add_##type(struct nv *nv, type##_t value, const char *namefmt, ...) \ 397{ \ 398 va_list nameap; \ 399 \ 400 va_start(nameap, namefmt); \ 401 nv_addv(nv, (unsigned char *)&value, sizeof(value), \ 402 NV_TYPE_##TYPE, namefmt, nameap); \ 403 va_end(nameap); \ 404} 405 406NV_DEFINE_ADD(int8, INT8) 407NV_DEFINE_ADD(uint8, UINT8) 408NV_DEFINE_ADD(int16, INT16) 409NV_DEFINE_ADD(uint16, UINT16) 410NV_DEFINE_ADD(int32, INT32) 411NV_DEFINE_ADD(uint32, UINT32) 412NV_DEFINE_ADD(int64, INT64) 413NV_DEFINE_ADD(uint64, UINT64) 414 415#undef NV_DEFINE_ADD 416 417#define NV_DEFINE_ADD_ARRAY(type, TYPE) \ 418void \ 419nv_add_##type##_array(struct nv *nv, const type##_t *value, \ 420 size_t nsize, const char *namefmt, ...) \ 421{ \ 422 va_list nameap; \ 423 \ 424 va_start(nameap, namefmt); \ 425 nv_addv(nv, (const unsigned char *)value, \ 426 sizeof(value[0]) * nsize, NV_TYPE_##TYPE##_ARRAY, namefmt, \ 427 nameap); \ 428 va_end(nameap); \ 429} 430 431NV_DEFINE_ADD_ARRAY(int8, INT8) 432NV_DEFINE_ADD_ARRAY(uint8, UINT8) 433NV_DEFINE_ADD_ARRAY(int16, INT16) 434NV_DEFINE_ADD_ARRAY(uint16, UINT16) 435NV_DEFINE_ADD_ARRAY(int32, INT32) 436NV_DEFINE_ADD_ARRAY(uint32, UINT32) 437NV_DEFINE_ADD_ARRAY(int64, INT64) 438NV_DEFINE_ADD_ARRAY(uint64, UINT64) 439 440#undef NV_DEFINE_ADD_ARRAY 441 442void 443nv_add_string(struct nv *nv, const char *value, const char *namefmt, ...) 444{ 445 va_list nameap; 446 size_t size; 447 448 size = strlen(value) + 1; 449 450 va_start(nameap, namefmt); 451 nv_addv(nv, (const unsigned char *)value, size, NV_TYPE_STRING, 452 namefmt, nameap); 453 va_end(nameap); 454} 455 456void 457nv_add_stringf(struct nv *nv, const char *name, const char *valuefmt, ...) 458{ 459 va_list valueap; 460 461 va_start(valueap, valuefmt); 462 nv_add_stringv(nv, name, valuefmt, valueap); 463 va_end(valueap); 464} 465 466void 467nv_add_stringv(struct nv *nv, const char *name, const char *valuefmt, 468 va_list valueap) 469{ 470 char *value; 471 ssize_t size; 472 473 size = vasprintf(&value, valuefmt, valueap); 474 if (size < 0) { 475 if (nv->nv_error == 0) 476 nv->nv_error = ENOMEM; 477 return; 478 } 479 size++; 480 nv_add(nv, (const unsigned char *)value, size, NV_TYPE_STRING, name); 481 free(value); 482} 483 484#define NV_DEFINE_GET(type, TYPE) \ 485type##_t \ 486nv_get_##type(struct nv *nv, const char *namefmt, ...) \ 487{ \ 488 struct nvhdr *nvh; \ 489 va_list nameap; \ 490 type##_t value; \ 491 \ 492 va_start(nameap, namefmt); \ 493 nvh = nv_find(nv, NV_TYPE_##TYPE, namefmt, nameap); \ 494 va_end(nameap); \ 495 if (nvh == NULL) \ 496 return (0); \ 497 assert((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST); \ 498 assert(sizeof(value) == nvh->nvh_dsize); \ 499 bcopy(NVH_DATA(nvh), &value, sizeof(value)); \ 500 \ 501 return (value); \ 502} 503 504NV_DEFINE_GET(int8, INT8) 505NV_DEFINE_GET(uint8, UINT8) 506NV_DEFINE_GET(int16, INT16) 507NV_DEFINE_GET(uint16, UINT16) 508NV_DEFINE_GET(int32, INT32) 509NV_DEFINE_GET(uint32, UINT32) 510NV_DEFINE_GET(int64, INT64) 511NV_DEFINE_GET(uint64, UINT64) 512 513#undef NV_DEFINE_GET 514 515#define NV_DEFINE_GET_ARRAY(type, TYPE) \ 516const type##_t * \ 517nv_get_##type##_array(struct nv *nv, size_t *sizep, \ 518 const char *namefmt, ...) \ 519{ \ 520 struct nvhdr *nvh; \ 521 va_list nameap; \ 522 \ 523 va_start(nameap, namefmt); \ 524 nvh = nv_find(nv, NV_TYPE_##TYPE##_ARRAY, namefmt, nameap); \ 525 va_end(nameap); \ 526 if (nvh == NULL) \ 527 return (NULL); \ 528 assert((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST); \ 529 assert((nvh->nvh_dsize % sizeof(type##_t)) == 0); \ 530 if (sizep != NULL) \ 531 *sizep = nvh->nvh_dsize / sizeof(type##_t); \ 532 return ((type##_t *)(void *)NVH_DATA(nvh)); \ 533} 534 535NV_DEFINE_GET_ARRAY(int8, INT8) 536NV_DEFINE_GET_ARRAY(uint8, UINT8) 537NV_DEFINE_GET_ARRAY(int16, INT16) 538NV_DEFINE_GET_ARRAY(uint16, UINT16) 539NV_DEFINE_GET_ARRAY(int32, INT32) 540NV_DEFINE_GET_ARRAY(uint32, UINT32) 541NV_DEFINE_GET_ARRAY(int64, INT64) 542NV_DEFINE_GET_ARRAY(uint64, UINT64) 543 544#undef NV_DEFINE_GET_ARRAY 545 546const char * 547nv_get_string(struct nv *nv, const char *namefmt, ...) 548{ 549 struct nvhdr *nvh; 550 va_list nameap; 551 char *str; 552 553 va_start(nameap, namefmt); 554 nvh = nv_find(nv, NV_TYPE_STRING, namefmt, nameap); 555 va_end(nameap); 556 if (nvh == NULL) 557 return (NULL); 558 assert((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST); 559 assert(nvh->nvh_dsize >= 1); 560 str = NVH_DATA(nvh); 561 assert(str[nvh->nvh_dsize - 1] == '\0'); 562 assert(strlen(str) == nvh->nvh_dsize - 1); 563 return (str); 564} 565 566static bool 567nv_vexists(struct nv *nv, const char *namefmt, va_list nameap) 568{ 569 struct nvhdr *nvh; 570 int snverror, serrno; 571 572 if (nv == NULL) 573 return (false); 574 575 serrno = errno; 576 snverror = nv->nv_error; 577 578 nvh = nv_find(nv, NV_TYPE_NONE, namefmt, nameap); 579 580 errno = serrno; 581 nv->nv_error = snverror; 582 583 return (nvh != NULL); 584} 585 586bool 587nv_exists(struct nv *nv, const char *namefmt, ...) 588{ 589 va_list nameap; 590 bool ret; 591 592 va_start(nameap, namefmt); 593 ret = nv_vexists(nv, namefmt, nameap); 594 va_end(nameap); 595 596 return (ret); 597} 598 599void 600nv_assert(struct nv *nv, const char *namefmt, ...) 601{ 602 va_list nameap; 603 604 va_start(nameap, namefmt); 605 assert(nv_vexists(nv, namefmt, nameap)); 606 va_end(nameap); 607} 608 609/* 610 * Dump content of the nv structure. 611 */ 612void 613nv_dump(struct nv *nv) 614{ 615 struct nvhdr *nvh; 616 unsigned char *data, *ptr; 617 size_t dsize, size; 618 unsigned int ii; 619 bool swap; 620 621 if (nv_validate(nv, NULL) < 0) { 622 printf("error: %d\n", errno); 623 return; 624 } 625 626 NV_CHECK(nv); 627 assert(nv->nv_error == 0); 628 629 ptr = ebuf_data(nv->nv_ebuf, &size); 630 while (size > 0) { 631 assert(size >= sizeof(*nvh) + 2); 632 nvh = (struct nvhdr *)ptr; 633 assert(size >= NVH_SIZE(nvh)); 634 swap = ((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_NETWORK); 635 dsize = NVH_DSIZE(nvh); 636 data = NVH_DATA(nvh); 637 printf(" %s", nvh->nvh_name); 638 switch (nvh->nvh_type & NV_TYPE_MASK) { 639 case NV_TYPE_INT8: 640 printf("(int8): %jd", (intmax_t)(*(int8_t *)data)); 641 break; 642 case NV_TYPE_UINT8: 643 printf("(uint8): %ju", (uintmax_t)(*(uint8_t *)data)); 644 break; 645 case NV_TYPE_INT16: 646 printf("(int16): %jd", swap ? 647 (intmax_t)le16toh(*(int16_t *)(void *)data) : 648 (intmax_t)*(int16_t *)(void *)data); 649 break; 650 case NV_TYPE_UINT16: 651 printf("(uint16): %ju", swap ? 652 (uintmax_t)le16toh(*(uint16_t *)(void *)data) : 653 (uintmax_t)*(uint16_t *)(void *)data); 654 break; 655 case NV_TYPE_INT32: 656 printf("(int32): %jd", swap ? 657 (intmax_t)le32toh(*(int32_t *)(void *)data) : 658 (intmax_t)*(int32_t *)(void *)data); 659 break; 660 case NV_TYPE_UINT32: 661 printf("(uint32): %ju", swap ? 662 (uintmax_t)le32toh(*(uint32_t *)(void *)data) : 663 (uintmax_t)*(uint32_t *)(void *)data); 664 break; 665 case NV_TYPE_INT64: 666 printf("(int64): %jd", swap ? 667 (intmax_t)le64toh(*(int64_t *)(void *)data) : 668 (intmax_t)*(int64_t *)(void *)data); 669 break; 670 case NV_TYPE_UINT64: 671 printf("(uint64): %ju", swap ? 672 (uintmax_t)le64toh(*(uint64_t *)(void *)data) : 673 (uintmax_t)*(uint64_t *)(void *)data); 674 break; 675 case NV_TYPE_INT8_ARRAY: 676 printf("(int8 array):"); 677 for (ii = 0; ii < dsize; ii++) 678 printf(" %jd", (intmax_t)((int8_t *)data)[ii]); 679 break; 680 case NV_TYPE_UINT8_ARRAY: 681 printf("(uint8 array):"); 682 for (ii = 0; ii < dsize; ii++) 683 printf(" %ju", (uintmax_t)((uint8_t *)data)[ii]); 684 break; 685 case NV_TYPE_INT16_ARRAY: 686 printf("(int16 array):"); 687 for (ii = 0; ii < dsize / 2; ii++) { 688 printf(" %jd", swap ? 689 (intmax_t)le16toh(((int16_t *)(void *)data)[ii]) : 690 (intmax_t)((int16_t *)(void *)data)[ii]); 691 } 692 break; 693 case NV_TYPE_UINT16_ARRAY: 694 printf("(uint16 array):"); 695 for (ii = 0; ii < dsize / 2; ii++) { 696 printf(" %ju", swap ? 697 (uintmax_t)le16toh(((uint16_t *)(void *)data)[ii]) : 698 (uintmax_t)((uint16_t *)(void *)data)[ii]); 699 } 700 break; 701 case NV_TYPE_INT32_ARRAY: 702 printf("(int32 array):"); 703 for (ii = 0; ii < dsize / 4; ii++) { 704 printf(" %jd", swap ? 705 (intmax_t)le32toh(((int32_t *)(void *)data)[ii]) : 706 (intmax_t)((int32_t *)(void *)data)[ii]); 707 } 708 break; 709 case NV_TYPE_UINT32_ARRAY: 710 printf("(uint32 array):"); 711 for (ii = 0; ii < dsize / 4; ii++) { 712 printf(" %ju", swap ? 713 (uintmax_t)le32toh(((uint32_t *)(void *)data)[ii]) : 714 (uintmax_t)((uint32_t *)(void *)data)[ii]); 715 } 716 break; 717 case NV_TYPE_INT64_ARRAY: 718 printf("(int64 array):"); 719 for (ii = 0; ii < dsize / 8; ii++) { 720 printf(" %ju", swap ? 721 (uintmax_t)le64toh(((uint64_t *)(void *)data)[ii]) : 722 (uintmax_t)((uint64_t *)(void *)data)[ii]); 723 } 724 break; 725 case NV_TYPE_UINT64_ARRAY: 726 printf("(uint64 array):"); 727 for (ii = 0; ii < dsize / 8; ii++) { 728 printf(" %ju", swap ? 729 (uintmax_t)le64toh(((uint64_t *)(void *)data)[ii]) : 730 (uintmax_t)((uint64_t *)(void *)data)[ii]); 731 } 732 break; 733 case NV_TYPE_STRING: 734 printf("(string): %s", (char *)data); 735 break; 736 default: 737 assert(!"invalid condition"); 738 } 739 printf("\n"); 740 ptr += NVH_SIZE(nvh); 741 size -= NVH_SIZE(nvh); 742 } 743} 744 745/* 746 * Local routines below. 747 */ 748 749static void 750nv_add(struct nv *nv, const unsigned char *value, size_t vsize, int type, 751 const char *name) 752{ 753 static unsigned char align[7]; 754 struct nvhdr *nvh; 755 size_t namesize; 756 757 if (nv == NULL) { 758 errno = ENOMEM; 759 return; 760 } 761 762 NV_CHECK(nv); 763 764 namesize = strlen(name) + 1; 765 766 nvh = malloc(sizeof(*nvh) + roundup2(namesize, 8)); 767 if (nvh == NULL) { 768 if (nv->nv_error == 0) 769 nv->nv_error = ENOMEM; 770 return; 771 } 772 nvh->nvh_type = NV_ORDER_HOST | type; 773 nvh->nvh_namesize = (uint8_t)namesize; 774 nvh->nvh_dsize = (uint32_t)vsize; 775 bcopy(name, nvh->nvh_name, namesize); 776 777 /* Add header first. */ 778 if (ebuf_add_tail(nv->nv_ebuf, nvh, NVH_HSIZE(nvh)) < 0) { 779 assert(errno != 0); 780 if (nv->nv_error == 0) 781 nv->nv_error = errno; 782 free(nvh); 783 return; 784 } 785 free(nvh); 786 /* Add the actual data. */ 787 if (ebuf_add_tail(nv->nv_ebuf, value, vsize) < 0) { 788 assert(errno != 0); 789 if (nv->nv_error == 0) 790 nv->nv_error = errno; 791 return; 792 } 793 /* Align the data (if needed). */ 794 vsize = roundup2(vsize, 8) - vsize; 795 if (vsize == 0) 796 return; 797 assert(vsize > 0 && vsize <= sizeof(align)); 798 if (ebuf_add_tail(nv->nv_ebuf, align, vsize) < 0) { 799 assert(errno != 0); 800 if (nv->nv_error == 0) 801 nv->nv_error = errno; 802 return; 803 } 804} 805 806static void 807nv_addv(struct nv *nv, const unsigned char *value, size_t vsize, int type, 808 const char *namefmt, va_list nameap) 809{ 810 char name[255]; 811 size_t namesize; 812 813 namesize = vsnprintf(name, sizeof(name), namefmt, nameap); 814 assert(namesize > 0 && namesize < sizeof(name)); 815 816 nv_add(nv, value, vsize, type, name); 817} 818 819static struct nvhdr * 820nv_find(struct nv *nv, int type, const char *namefmt, va_list nameap) 821{ 822 char name[255]; 823 struct nvhdr *nvh; 824 unsigned char *ptr; 825 size_t size, namesize; 826 827 if (nv == NULL) { 828 errno = ENOMEM; 829 return (NULL); 830 } 831 832 NV_CHECK(nv); 833 834 namesize = vsnprintf(name, sizeof(name), namefmt, nameap); 835 assert(namesize > 0 && namesize < sizeof(name)); 836 namesize++; 837 838 ptr = ebuf_data(nv->nv_ebuf, &size); 839 while (size > 0) { 840 assert(size >= sizeof(*nvh) + 2); 841 nvh = (struct nvhdr *)ptr; 842 assert(size >= NVH_SIZE(nvh)); 843 nv_swap(nvh, true); 844 if (strcmp(nvh->nvh_name, name) == 0) { 845 if (type != NV_TYPE_NONE && 846 (nvh->nvh_type & NV_TYPE_MASK) != type) { 847 errno = EINVAL; 848 if (nv->nv_error == 0) 849 nv->nv_error = EINVAL; 850 return (NULL); 851 } 852 return (nvh); 853 } 854 ptr += NVH_SIZE(nvh); 855 size -= NVH_SIZE(nvh); 856 } 857 errno = ENOENT; 858 if (nv->nv_error == 0) 859 nv->nv_error = ENOENT; 860 return (NULL); 861} 862 863static void 864nv_swap(struct nvhdr *nvh, bool tohost) 865{ 866 unsigned char *data, *end, *p; 867 size_t vsize; 868 869 data = NVH_DATA(nvh); 870 if (tohost) { 871 if ((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST) 872 return; 873 nvh->nvh_dsize = le32toh(nvh->nvh_dsize); 874 end = data + nvh->nvh_dsize; 875 nvh->nvh_type &= ~NV_ORDER_MASK; 876 nvh->nvh_type |= NV_ORDER_HOST; 877 } else { 878 if ((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_NETWORK) 879 return; 880 end = data + nvh->nvh_dsize; 881 nvh->nvh_dsize = htole32(nvh->nvh_dsize); 882 nvh->nvh_type &= ~NV_ORDER_MASK; 883 nvh->nvh_type |= NV_ORDER_NETWORK; 884 } 885 886 vsize = 0; 887 888 switch (nvh->nvh_type & NV_TYPE_MASK) { 889 case NV_TYPE_INT8: 890 case NV_TYPE_UINT8: 891 case NV_TYPE_INT8_ARRAY: 892 case NV_TYPE_UINT8_ARRAY: 893 break; 894 case NV_TYPE_INT16: 895 case NV_TYPE_UINT16: 896 case NV_TYPE_INT16_ARRAY: 897 case NV_TYPE_UINT16_ARRAY: 898 if (vsize == 0) 899 vsize = 2; 900 /* FALLTHOUGH */ 901 case NV_TYPE_INT32: 902 case NV_TYPE_UINT32: 903 case NV_TYPE_INT32_ARRAY: 904 case NV_TYPE_UINT32_ARRAY: 905 if (vsize == 0) 906 vsize = 4; 907 /* FALLTHOUGH */ 908 case NV_TYPE_INT64: 909 case NV_TYPE_UINT64: 910 case NV_TYPE_INT64_ARRAY: 911 case NV_TYPE_UINT64_ARRAY: 912 if (vsize == 0) 913 vsize = 8; 914 for (p = data; p < end; p += vsize) { 915 if (tohost) { 916 switch (vsize) { 917 case 2: 918 *(uint16_t *)(void *)p = 919 le16toh(*(uint16_t *)(void *)p); 920 break; 921 case 4: 922 *(uint32_t *)(void *)p = 923 le32toh(*(uint32_t *)(void *)p); 924 break; 925 case 8: 926 *(uint64_t *)(void *)p = 927 le64toh(*(uint64_t *)(void *)p); 928 break; 929 default: 930 assert(!"invalid condition"); 931 } 932 } else { 933 switch (vsize) { 934 case 2: 935 *(uint16_t *)(void *)p = 936 htole16(*(uint16_t *)(void *)p); 937 break; 938 case 4: 939 *(uint32_t *)(void *)p = 940 htole32(*(uint32_t *)(void *)p); 941 break; 942 case 8: 943 *(uint64_t *)(void *)p = 944 htole64(*(uint64_t *)(void *)p); 945 break; 946 default: 947 assert(!"invalid condition"); 948 } 949 } 950 } 951 break; 952 case NV_TYPE_STRING: 953 break; 954 default: 955 assert(!"unrecognized type"); 956 } 957} 958