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