xref: /freebsd/sbin/hastd/nv.c (revision eed4e65fdbfaead56e9de7ab24608483f251e2ea)
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 static bool
567 nv_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 
586 bool
587 nv_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 
599 void
600 nv_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  */
612 void
613 nv_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 
749 static void
750 nv_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 
806 static void
807 nv_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 
819 static struct nvhdr *
820 nv_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 
863 static void
864 nv_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