nv.c revision 225736
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: stable/9/sbin/hastd/nv.c 217732 2011-01-22 22:38:18Z 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_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
79struct nv {
80	int	nv_magic;
81	int	nv_error;
82	struct ebuf *nv_ebuf;
83};
84
85struct 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
105static void nv_add(struct nv *nv, const unsigned char *value, size_t vsize,
106    int type, const char *name);
107static void nv_addv(struct nv *nv, const unsigned char *value, size_t vsize,
108    int type, const char *namefmt, va_list nameap);
109static struct nvhdr *nv_find(struct nv *nv, int type, const char *namefmt,
110    va_list nameap);
111static 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 */
117struct nv *
118nv_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 */
138void
139nv_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 */
155int
156nv_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 */
170int
171nv_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 */
189int
190nv_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 */
333struct ebuf *
334nv_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 */
363struct nv *
364nv_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)					\
395void									\
396nv_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
406NV_DEFINE_ADD(int8, INT8)
407NV_DEFINE_ADD(uint8, UINT8)
408NV_DEFINE_ADD(int16, INT16)
409NV_DEFINE_ADD(uint16, UINT16)
410NV_DEFINE_ADD(int32, INT32)
411NV_DEFINE_ADD(uint32, UINT32)
412NV_DEFINE_ADD(int64, INT64)
413NV_DEFINE_ADD(uint64, UINT64)
414
415#undef	NV_DEFINE_ADD
416
417#define	NV_DEFINE_ADD_ARRAY(type, TYPE)					\
418void									\
419nv_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
431NV_DEFINE_ADD_ARRAY(int8, INT8)
432NV_DEFINE_ADD_ARRAY(uint8, UINT8)
433NV_DEFINE_ADD_ARRAY(int16, INT16)
434NV_DEFINE_ADD_ARRAY(uint16, UINT16)
435NV_DEFINE_ADD_ARRAY(int32, INT32)
436NV_DEFINE_ADD_ARRAY(uint32, UINT32)
437NV_DEFINE_ADD_ARRAY(int64, INT64)
438NV_DEFINE_ADD_ARRAY(uint64, UINT64)
439
440#undef	NV_DEFINE_ADD_ARRAY
441
442void
443nv_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
456void
457nv_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
466void
467nv_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)					\
485type##_t								\
486nv_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
504NV_DEFINE_GET(int8, INT8)
505NV_DEFINE_GET(uint8, UINT8)
506NV_DEFINE_GET(int16, INT16)
507NV_DEFINE_GET(uint16, UINT16)
508NV_DEFINE_GET(int32, INT32)
509NV_DEFINE_GET(uint32, UINT32)
510NV_DEFINE_GET(int64, INT64)
511NV_DEFINE_GET(uint64, UINT64)
512
513#undef	NV_DEFINE_GET
514
515#define	NV_DEFINE_GET_ARRAY(type, TYPE)					\
516const type##_t *							\
517nv_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
535NV_DEFINE_GET_ARRAY(int8, INT8)
536NV_DEFINE_GET_ARRAY(uint8, UINT8)
537NV_DEFINE_GET_ARRAY(int16, INT16)
538NV_DEFINE_GET_ARRAY(uint16, UINT16)
539NV_DEFINE_GET_ARRAY(int32, INT32)
540NV_DEFINE_GET_ARRAY(uint32, UINT32)
541NV_DEFINE_GET_ARRAY(int64, INT64)
542NV_DEFINE_GET_ARRAY(uint64, UINT64)
543
544#undef	NV_DEFINE_GET_ARRAY
545
546const char *
547nv_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
566static bool
567nv_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
586bool
587nv_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
599void
600nv_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 */
612void
613nv_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
749static void
750nv_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
806static void
807nv_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
819static struct nvhdr *
820nv_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
863static void
864nv_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