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$"); 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 79 struct nv { 80 int nv_magic; 81 int nv_error; 82 struct ebuf *nv_ebuf; 83 }; 84 85 struct 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 105 static void nv_add(struct nv *nv, const unsigned char *value, size_t vsize, 106 int type, const char *name); 107 static void nv_addv(struct nv *nv, const unsigned char *value, size_t vsize, 108 int type, const char *namefmt, va_list nameap); 109 static struct nvhdr *nv_find(struct nv *nv, int type, const char *namefmt, 110 va_list nameap); 111 static 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 */ 117 struct nv * 118 nv_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 */ 138 void 139 nv_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 */ 155 int 156 nv_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 */ 170 int 171 nv_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 */ 189 int 190 nv_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 */ 333 struct ebuf * 334 nv_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 */ 363 struct nv * 364 nv_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) \ 395 void \ 396 nv_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 406 NV_DEFINE_ADD(int8, INT8) 407 NV_DEFINE_ADD(uint8, UINT8) 408 NV_DEFINE_ADD(int16, INT16) 409 NV_DEFINE_ADD(uint16, UINT16) 410 NV_DEFINE_ADD(int32, INT32) 411 NV_DEFINE_ADD(uint32, UINT32) 412 NV_DEFINE_ADD(int64, INT64) 413 NV_DEFINE_ADD(uint64, UINT64) 414 415 #undef NV_DEFINE_ADD 416 417 #define NV_DEFINE_ADD_ARRAY(type, TYPE) \ 418 void \ 419 nv_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 431 NV_DEFINE_ADD_ARRAY(int8, INT8) 432 NV_DEFINE_ADD_ARRAY(uint8, UINT8) 433 NV_DEFINE_ADD_ARRAY(int16, INT16) 434 NV_DEFINE_ADD_ARRAY(uint16, UINT16) 435 NV_DEFINE_ADD_ARRAY(int32, INT32) 436 NV_DEFINE_ADD_ARRAY(uint32, UINT32) 437 NV_DEFINE_ADD_ARRAY(int64, INT64) 438 NV_DEFINE_ADD_ARRAY(uint64, UINT64) 439 440 #undef NV_DEFINE_ADD_ARRAY 441 442 void 443 nv_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 456 void 457 nv_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 466 void 467 nv_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) \ 485 type##_t \ 486 nv_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 504 NV_DEFINE_GET(int8, INT8) 505 NV_DEFINE_GET(uint8, UINT8) 506 NV_DEFINE_GET(int16, INT16) 507 NV_DEFINE_GET(uint16, UINT16) 508 NV_DEFINE_GET(int32, INT32) 509 NV_DEFINE_GET(uint32, UINT32) 510 NV_DEFINE_GET(int64, INT64) 511 NV_DEFINE_GET(uint64, UINT64) 512 513 #undef NV_DEFINE_GET 514 515 #define NV_DEFINE_GET_ARRAY(type, TYPE) \ 516 const type##_t * \ 517 nv_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 535 NV_DEFINE_GET_ARRAY(int8, INT8) 536 NV_DEFINE_GET_ARRAY(uint8, UINT8) 537 NV_DEFINE_GET_ARRAY(int16, INT16) 538 NV_DEFINE_GET_ARRAY(uint16, UINT16) 539 NV_DEFINE_GET_ARRAY(int32, INT32) 540 NV_DEFINE_GET_ARRAY(uint32, UINT32) 541 NV_DEFINE_GET_ARRAY(int64, INT64) 542 NV_DEFINE_GET_ARRAY(uint64, UINT64) 543 544 #undef NV_DEFINE_GET_ARRAY 545 546 const char * 547 nv_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 566 bool 567 nv_exists(struct nv *nv, const char *namefmt, ...) 568 { 569 struct nvhdr *nvh; 570 va_list nameap; 571 int snverror, serrno; 572 573 if (nv == NULL) 574 return (false); 575 576 serrno = errno; 577 snverror = nv->nv_error; 578 579 va_start(nameap, namefmt); 580 nvh = nv_find(nv, NV_TYPE_NONE, namefmt, nameap); 581 va_end(nameap); 582 583 errno = serrno; 584 nv->nv_error = snverror; 585 586 return (nvh != NULL); 587 } 588 589 /* 590 * Dump content of the nv structure. 591 */ 592 void 593 nv_dump(struct nv *nv) 594 { 595 struct nvhdr *nvh; 596 unsigned char *data, *ptr; 597 size_t dsize, size; 598 unsigned int ii; 599 bool swap; 600 601 if (nv_validate(nv, NULL) < 0) { 602 printf("error: %d\n", errno); 603 return; 604 } 605 606 NV_CHECK(nv); 607 assert(nv->nv_error == 0); 608 609 ptr = ebuf_data(nv->nv_ebuf, &size); 610 while (size > 0) { 611 assert(size >= sizeof(*nvh) + 2); 612 nvh = (struct nvhdr *)ptr; 613 assert(size >= NVH_SIZE(nvh)); 614 swap = ((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_NETWORK); 615 dsize = NVH_DSIZE(nvh); 616 data = NVH_DATA(nvh); 617 printf(" %s", nvh->nvh_name); 618 switch (nvh->nvh_type & NV_TYPE_MASK) { 619 case NV_TYPE_INT8: 620 printf("(int8): %jd", (intmax_t)(*(int8_t *)data)); 621 break; 622 case NV_TYPE_UINT8: 623 printf("(uint8): %ju", (uintmax_t)(*(uint8_t *)data)); 624 break; 625 case NV_TYPE_INT16: 626 printf("(int16): %jd", swap ? 627 (intmax_t)le16toh(*(int16_t *)(void *)data) : 628 (intmax_t)*(int16_t *)(void *)data); 629 break; 630 case NV_TYPE_UINT16: 631 printf("(uint16): %ju", swap ? 632 (uintmax_t)le16toh(*(uint16_t *)(void *)data) : 633 (uintmax_t)*(uint16_t *)(void *)data); 634 break; 635 case NV_TYPE_INT32: 636 printf("(int32): %jd", swap ? 637 (intmax_t)le32toh(*(int32_t *)(void *)data) : 638 (intmax_t)*(int32_t *)(void *)data); 639 break; 640 case NV_TYPE_UINT32: 641 printf("(uint32): %ju", swap ? 642 (uintmax_t)le32toh(*(uint32_t *)(void *)data) : 643 (uintmax_t)*(uint32_t *)(void *)data); 644 break; 645 case NV_TYPE_INT64: 646 printf("(int64): %jd", swap ? 647 (intmax_t)le64toh(*(int64_t *)(void *)data) : 648 (intmax_t)*(int64_t *)(void *)data); 649 break; 650 case NV_TYPE_UINT64: 651 printf("(uint64): %ju", swap ? 652 (uintmax_t)le64toh(*(uint64_t *)(void *)data) : 653 (uintmax_t)*(uint64_t *)(void *)data); 654 break; 655 case NV_TYPE_INT8_ARRAY: 656 printf("(int8 array):"); 657 for (ii = 0; ii < dsize; ii++) 658 printf(" %jd", (intmax_t)((int8_t *)data)[ii]); 659 break; 660 case NV_TYPE_UINT8_ARRAY: 661 printf("(uint8 array):"); 662 for (ii = 0; ii < dsize; ii++) 663 printf(" %ju", (uintmax_t)((uint8_t *)data)[ii]); 664 break; 665 case NV_TYPE_INT16_ARRAY: 666 printf("(int16 array):"); 667 for (ii = 0; ii < dsize / 2; ii++) { 668 printf(" %jd", swap ? 669 (intmax_t)le16toh(((int16_t *)(void *)data)[ii]) : 670 (intmax_t)((int16_t *)(void *)data)[ii]); 671 } 672 break; 673 case NV_TYPE_UINT16_ARRAY: 674 printf("(uint16 array):"); 675 for (ii = 0; ii < dsize / 2; ii++) { 676 printf(" %ju", swap ? 677 (uintmax_t)le16toh(((uint16_t *)(void *)data)[ii]) : 678 (uintmax_t)((uint16_t *)(void *)data)[ii]); 679 } 680 break; 681 case NV_TYPE_INT32_ARRAY: 682 printf("(int32 array):"); 683 for (ii = 0; ii < dsize / 4; ii++) { 684 printf(" %jd", swap ? 685 (intmax_t)le32toh(((int32_t *)(void *)data)[ii]) : 686 (intmax_t)((int32_t *)(void *)data)[ii]); 687 } 688 break; 689 case NV_TYPE_UINT32_ARRAY: 690 printf("(uint32 array):"); 691 for (ii = 0; ii < dsize / 4; ii++) { 692 printf(" %ju", swap ? 693 (uintmax_t)le32toh(((uint32_t *)(void *)data)[ii]) : 694 (uintmax_t)((uint32_t *)(void *)data)[ii]); 695 } 696 break; 697 case NV_TYPE_INT64_ARRAY: 698 printf("(int64 array):"); 699 for (ii = 0; ii < dsize / 8; ii++) { 700 printf(" %ju", swap ? 701 (uintmax_t)le64toh(((uint64_t *)(void *)data)[ii]) : 702 (uintmax_t)((uint64_t *)(void *)data)[ii]); 703 } 704 break; 705 case NV_TYPE_UINT64_ARRAY: 706 printf("(uint64 array):"); 707 for (ii = 0; ii < dsize / 8; ii++) { 708 printf(" %ju", swap ? 709 (uintmax_t)le64toh(((uint64_t *)(void *)data)[ii]) : 710 (uintmax_t)((uint64_t *)(void *)data)[ii]); 711 } 712 break; 713 case NV_TYPE_STRING: 714 printf("(string): %s", (char *)data); 715 break; 716 default: 717 assert(!"invalid condition"); 718 } 719 printf("\n"); 720 ptr += NVH_SIZE(nvh); 721 size -= NVH_SIZE(nvh); 722 } 723 } 724 725 /* 726 * Local routines below. 727 */ 728 729 static void 730 nv_add(struct nv *nv, const unsigned char *value, size_t vsize, int type, 731 const char *name) 732 { 733 static unsigned char align[7]; 734 struct nvhdr *nvh; 735 size_t namesize; 736 737 if (nv == NULL) { 738 errno = ENOMEM; 739 return; 740 } 741 742 NV_CHECK(nv); 743 744 namesize = strlen(name) + 1; 745 746 nvh = malloc(sizeof(*nvh) + roundup2(namesize, 8)); 747 if (nvh == NULL) { 748 if (nv->nv_error == 0) 749 nv->nv_error = ENOMEM; 750 return; 751 } 752 nvh->nvh_type = NV_ORDER_HOST | type; 753 nvh->nvh_namesize = (uint8_t)namesize; 754 nvh->nvh_dsize = (uint32_t)vsize; 755 bcopy(name, nvh->nvh_name, namesize); 756 757 /* Add header first. */ 758 if (ebuf_add_tail(nv->nv_ebuf, nvh, NVH_HSIZE(nvh)) < 0) { 759 assert(errno != 0); 760 if (nv->nv_error == 0) 761 nv->nv_error = errno; 762 free(nvh); 763 return; 764 } 765 free(nvh); 766 /* Add the actual data. */ 767 if (ebuf_add_tail(nv->nv_ebuf, value, vsize) < 0) { 768 assert(errno != 0); 769 if (nv->nv_error == 0) 770 nv->nv_error = errno; 771 return; 772 } 773 /* Align the data (if needed). */ 774 vsize = roundup2(vsize, 8) - vsize; 775 if (vsize == 0) 776 return; 777 assert(vsize > 0 && vsize <= sizeof(align)); 778 if (ebuf_add_tail(nv->nv_ebuf, align, vsize) < 0) { 779 assert(errno != 0); 780 if (nv->nv_error == 0) 781 nv->nv_error = errno; 782 return; 783 } 784 } 785 786 static void 787 nv_addv(struct nv *nv, const unsigned char *value, size_t vsize, int type, 788 const char *namefmt, va_list nameap) 789 { 790 char name[255]; 791 size_t namesize; 792 793 namesize = vsnprintf(name, sizeof(name), namefmt, nameap); 794 assert(namesize > 0 && namesize < sizeof(name)); 795 796 nv_add(nv, value, vsize, type, name); 797 } 798 799 static struct nvhdr * 800 nv_find(struct nv *nv, int type, const char *namefmt, va_list nameap) 801 { 802 char name[255]; 803 struct nvhdr *nvh; 804 unsigned char *ptr; 805 size_t size, namesize; 806 807 if (nv == NULL) { 808 errno = ENOMEM; 809 return (NULL); 810 } 811 812 NV_CHECK(nv); 813 814 namesize = vsnprintf(name, sizeof(name), namefmt, nameap); 815 assert(namesize > 0 && namesize < sizeof(name)); 816 namesize++; 817 818 ptr = ebuf_data(nv->nv_ebuf, &size); 819 while (size > 0) { 820 assert(size >= sizeof(*nvh) + 2); 821 nvh = (struct nvhdr *)ptr; 822 assert(size >= NVH_SIZE(nvh)); 823 nv_swap(nvh, true); 824 if (strcmp(nvh->nvh_name, name) == 0) { 825 if (type != NV_TYPE_NONE && 826 (nvh->nvh_type & NV_TYPE_MASK) != type) { 827 errno = EINVAL; 828 if (nv->nv_error == 0) 829 nv->nv_error = EINVAL; 830 return (NULL); 831 } 832 return (nvh); 833 } 834 ptr += NVH_SIZE(nvh); 835 size -= NVH_SIZE(nvh); 836 } 837 errno = ENOENT; 838 if (nv->nv_error == 0) 839 nv->nv_error = ENOENT; 840 return (NULL); 841 } 842 843 static void 844 nv_swap(struct nvhdr *nvh, bool tohost) 845 { 846 unsigned char *data, *end, *p; 847 size_t vsize; 848 849 data = NVH_DATA(nvh); 850 if (tohost) { 851 if ((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST) 852 return; 853 nvh->nvh_dsize = le32toh(nvh->nvh_dsize); 854 end = data + nvh->nvh_dsize; 855 nvh->nvh_type &= ~NV_ORDER_MASK; 856 nvh->nvh_type |= NV_ORDER_HOST; 857 } else { 858 if ((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_NETWORK) 859 return; 860 end = data + nvh->nvh_dsize; 861 nvh->nvh_dsize = htole32(nvh->nvh_dsize); 862 nvh->nvh_type &= ~NV_ORDER_MASK; 863 nvh->nvh_type |= NV_ORDER_NETWORK; 864 } 865 866 vsize = 0; 867 868 switch (nvh->nvh_type & NV_TYPE_MASK) { 869 case NV_TYPE_INT8: 870 case NV_TYPE_UINT8: 871 case NV_TYPE_INT8_ARRAY: 872 case NV_TYPE_UINT8_ARRAY: 873 break; 874 case NV_TYPE_INT16: 875 case NV_TYPE_UINT16: 876 case NV_TYPE_INT16_ARRAY: 877 case NV_TYPE_UINT16_ARRAY: 878 if (vsize == 0) 879 vsize = 2; 880 /* FALLTHOUGH */ 881 case NV_TYPE_INT32: 882 case NV_TYPE_UINT32: 883 case NV_TYPE_INT32_ARRAY: 884 case NV_TYPE_UINT32_ARRAY: 885 if (vsize == 0) 886 vsize = 4; 887 /* FALLTHOUGH */ 888 case NV_TYPE_INT64: 889 case NV_TYPE_UINT64: 890 case NV_TYPE_INT64_ARRAY: 891 case NV_TYPE_UINT64_ARRAY: 892 if (vsize == 0) 893 vsize = 8; 894 for (p = data; p < end; p += vsize) { 895 if (tohost) { 896 switch (vsize) { 897 case 2: 898 *(uint16_t *)(void *)p = 899 le16toh(*(uint16_t *)(void *)p); 900 break; 901 case 4: 902 *(uint32_t *)(void *)p = 903 le32toh(*(uint32_t *)(void *)p); 904 break; 905 case 8: 906 *(uint64_t *)(void *)p = 907 le64toh(*(uint64_t *)(void *)p); 908 break; 909 default: 910 assert(!"invalid condition"); 911 } 912 } else { 913 switch (vsize) { 914 case 2: 915 *(uint16_t *)(void *)p = 916 htole16(*(uint16_t *)(void *)p); 917 break; 918 case 4: 919 *(uint32_t *)(void *)p = 920 htole32(*(uint32_t *)(void *)p); 921 break; 922 case 8: 923 *(uint64_t *)(void *)p = 924 htole64(*(uint64_t *)(void *)p); 925 break; 926 default: 927 assert(!"invalid condition"); 928 } 929 } 930 } 931 break; 932 case NV_TYPE_STRING: 933 break; 934 default: 935 assert(!"unrecognized type"); 936 } 937 } 938