nv.c revision 214282
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: head/sbin/hastd/nv.c 214282 2010-10-24 17:22:34Z pjd $");
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
77struct nv {
78	int	nv_magic;
79	int	nv_error;
80	struct ebuf *nv_ebuf;
81};
82
83struct 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
103static void nv_add(struct nv *nv, const unsigned char *value, size_t vsize,
104    int type, const char *name);
105static void nv_addv(struct nv *nv, const unsigned char *value, size_t vsize,
106    int type, const char *namefmt, va_list nameap);
107static struct nvhdr *nv_find(struct nv *nv, int type, const char *namefmt,
108    va_list nameap);
109static 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 */
115struct nv *
116nv_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 */
136void
137nv_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 */
153int
154nv_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 */
168int
169nv_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 */
187int
188nv_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 */
331struct ebuf *
332nv_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 */
361struct nv *
362nv_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)					\
393void									\
394nv_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
404NV_DEFINE_ADD(int8, INT8)
405NV_DEFINE_ADD(uint8, UINT8)
406NV_DEFINE_ADD(int16, INT16)
407NV_DEFINE_ADD(uint16, UINT16)
408NV_DEFINE_ADD(int32, INT32)
409NV_DEFINE_ADD(uint32, UINT32)
410NV_DEFINE_ADD(int64, INT64)
411NV_DEFINE_ADD(uint64, UINT64)
412
413#undef	NV_DEFINE_ADD
414
415#define	NV_DEFINE_ADD_ARRAY(type, TYPE)					\
416void									\
417nv_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
429NV_DEFINE_ADD_ARRAY(int8, INT8)
430NV_DEFINE_ADD_ARRAY(uint8, UINT8)
431NV_DEFINE_ADD_ARRAY(int16, INT16)
432NV_DEFINE_ADD_ARRAY(uint16, UINT16)
433NV_DEFINE_ADD_ARRAY(int32, INT32)
434NV_DEFINE_ADD_ARRAY(uint32, UINT32)
435NV_DEFINE_ADD_ARRAY(int64, INT64)
436NV_DEFINE_ADD_ARRAY(uint64, UINT64)
437
438#undef	NV_DEFINE_ADD_ARRAY
439
440void
441nv_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
454void
455nv_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
464void
465nv_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)					\
483type##_t								\
484nv_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
502NV_DEFINE_GET(int8, INT8)
503NV_DEFINE_GET(uint8, UINT8)
504NV_DEFINE_GET(int16, INT16)
505NV_DEFINE_GET(uint16, UINT16)
506NV_DEFINE_GET(int32, INT32)
507NV_DEFINE_GET(uint32, UINT32)
508NV_DEFINE_GET(int64, INT64)
509NV_DEFINE_GET(uint64, UINT64)
510
511#undef	NV_DEFINE_GET
512
513#define	NV_DEFINE_GET_ARRAY(type, TYPE)					\
514const type##_t *							\
515nv_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
533NV_DEFINE_GET_ARRAY(int8, INT8)
534NV_DEFINE_GET_ARRAY(uint8, UINT8)
535NV_DEFINE_GET_ARRAY(int16, INT16)
536NV_DEFINE_GET_ARRAY(uint16, UINT16)
537NV_DEFINE_GET_ARRAY(int32, INT32)
538NV_DEFINE_GET_ARRAY(uint32, UINT32)
539NV_DEFINE_GET_ARRAY(int64, INT64)
540NV_DEFINE_GET_ARRAY(uint64, UINT64)
541
542#undef	NV_DEFINE_GET_ARRAY
543
544const char *
545nv_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 */
567void
568nv_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
704static void
705nv_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
761static void
762nv_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
774static struct nvhdr *
775nv_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
817static void
818nv_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