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