xref: /freebsd/sbin/hastd/nv.c (revision c9697b738cfa6fe585bc00d90899fc0fd51cd209)
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 }
885