nv.c revision 214283
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 214283 2010-10-24 17:24:08Z 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
566bool
567nv_exists(struct nv *nv, const char *namefmt, ...)
568{
569	struct nvhdr *nvh;
570	va_list nameap;
571	int snverror, serrno;
572
573	if (nv == NULL)
574		return (false);
575
576	serrno = errno;
577	snverror = nv->nv_error;
578
579	va_start(nameap, namefmt);
580	nvh = nv_find(nv, NV_TYPE_NONE, namefmt, nameap);
581	va_end(nameap);
582
583	errno = serrno;
584	nv->nv_error = snverror;
585
586	return (nvh != NULL);
587}
588
589/*
590 * Dump content of the nv structure.
591 */
592void
593nv_dump(struct nv *nv)
594{
595	struct nvhdr *nvh;
596	unsigned char *data, *ptr;
597	size_t dsize, size;
598	unsigned int ii;
599	bool swap;
600
601	if (nv_validate(nv, NULL) < 0) {
602		printf("error: %d\n", errno);
603		return;
604	}
605
606	NV_CHECK(nv);
607	assert(nv->nv_error == 0);
608
609	ptr = ebuf_data(nv->nv_ebuf, &size);
610	while (size > 0) {
611		assert(size >= sizeof(*nvh) + 2);
612		nvh = (struct nvhdr *)ptr;
613		assert(size >= NVH_SIZE(nvh));
614		swap = ((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_NETWORK);
615		dsize = NVH_DSIZE(nvh);
616		data = NVH_DATA(nvh);
617		printf("  %s", nvh->nvh_name);
618		switch (nvh->nvh_type & NV_TYPE_MASK) {
619		case NV_TYPE_INT8:
620			printf("(int8): %jd", (intmax_t)(*(int8_t *)data));
621			break;
622		case NV_TYPE_UINT8:
623			printf("(uint8): %ju", (uintmax_t)(*(uint8_t *)data));
624			break;
625		case NV_TYPE_INT16:
626			printf("(int16): %jd", swap ?
627			    (intmax_t)le16toh(*(int16_t *)(void *)data) :
628			    (intmax_t)*(int16_t *)(void *)data);
629			break;
630		case NV_TYPE_UINT16:
631			printf("(uint16): %ju", swap ?
632			    (uintmax_t)le16toh(*(uint16_t *)(void *)data) :
633			    (uintmax_t)*(uint16_t *)(void *)data);
634			break;
635		case NV_TYPE_INT32:
636			printf("(int32): %jd", swap ?
637			    (intmax_t)le32toh(*(int32_t *)(void *)data) :
638			    (intmax_t)*(int32_t *)(void *)data);
639			break;
640		case NV_TYPE_UINT32:
641			printf("(uint32): %ju", swap ?
642			    (uintmax_t)le32toh(*(uint32_t *)(void *)data) :
643			    (uintmax_t)*(uint32_t *)(void *)data);
644			break;
645		case NV_TYPE_INT64:
646			printf("(int64): %jd", swap ?
647			    (intmax_t)le64toh(*(int64_t *)(void *)data) :
648			    (intmax_t)*(int64_t *)(void *)data);
649			break;
650		case NV_TYPE_UINT64:
651			printf("(uint64): %ju", swap ?
652			    (uintmax_t)le64toh(*(uint64_t *)(void *)data) :
653			    (uintmax_t)*(uint64_t *)(void *)data);
654			break;
655		case NV_TYPE_INT8_ARRAY:
656			printf("(int8 array):");
657			for (ii = 0; ii < dsize; ii++)
658				printf(" %jd", (intmax_t)((int8_t *)data)[ii]);
659			break;
660		case NV_TYPE_UINT8_ARRAY:
661			printf("(uint8 array):");
662			for (ii = 0; ii < dsize; ii++)
663				printf(" %ju", (uintmax_t)((uint8_t *)data)[ii]);
664			break;
665		case NV_TYPE_INT16_ARRAY:
666			printf("(int16 array):");
667			for (ii = 0; ii < dsize / 2; ii++) {
668				printf(" %jd", swap ?
669				    (intmax_t)le16toh(((int16_t *)(void *)data)[ii]) :
670				    (intmax_t)((int16_t *)(void *)data)[ii]);
671			}
672			break;
673		case NV_TYPE_UINT16_ARRAY:
674			printf("(uint16 array):");
675			for (ii = 0; ii < dsize / 2; ii++) {
676				printf(" %ju", swap ?
677				    (uintmax_t)le16toh(((uint16_t *)(void *)data)[ii]) :
678				    (uintmax_t)((uint16_t *)(void *)data)[ii]);
679			}
680			break;
681		case NV_TYPE_INT32_ARRAY:
682			printf("(int32 array):");
683			for (ii = 0; ii < dsize / 4; ii++) {
684				printf(" %jd", swap ?
685				    (intmax_t)le32toh(((int32_t *)(void *)data)[ii]) :
686				    (intmax_t)((int32_t *)(void *)data)[ii]);
687			}
688			break;
689		case NV_TYPE_UINT32_ARRAY:
690			printf("(uint32 array):");
691			for (ii = 0; ii < dsize / 4; ii++) {
692				printf(" %ju", swap ?
693				    (uintmax_t)le32toh(((uint32_t *)(void *)data)[ii]) :
694				    (uintmax_t)((uint32_t *)(void *)data)[ii]);
695			}
696			break;
697		case NV_TYPE_INT64_ARRAY:
698			printf("(int64 array):");
699			for (ii = 0; ii < dsize / 8; ii++) {
700				printf(" %ju", swap ?
701				    (uintmax_t)le64toh(((uint64_t *)(void *)data)[ii]) :
702				    (uintmax_t)((uint64_t *)(void *)data)[ii]);
703			}
704			break;
705		case NV_TYPE_UINT64_ARRAY:
706			printf("(uint64 array):");
707			for (ii = 0; ii < dsize / 8; ii++) {
708				printf(" %ju", swap ?
709				    (uintmax_t)le64toh(((uint64_t *)(void *)data)[ii]) :
710				    (uintmax_t)((uint64_t *)(void *)data)[ii]);
711			}
712			break;
713		case NV_TYPE_STRING:
714			printf("(string): %s", (char *)data);
715			break;
716		default:
717			assert(!"invalid condition");
718		}
719		printf("\n");
720		ptr += NVH_SIZE(nvh);
721		size -= NVH_SIZE(nvh);
722	}
723}
724
725/*
726 * Local routines below.
727 */
728
729static void
730nv_add(struct nv *nv, const unsigned char *value, size_t vsize, int type,
731    const char *name)
732{
733	static unsigned char align[7];
734	struct nvhdr *nvh;
735	size_t namesize;
736
737	if (nv == NULL) {
738		errno = ENOMEM;
739		return;
740	}
741
742	NV_CHECK(nv);
743
744	namesize = strlen(name) + 1;
745
746	nvh = malloc(sizeof(*nvh) + roundup2(namesize, 8));
747	if (nvh == NULL) {
748		if (nv->nv_error == 0)
749			nv->nv_error = ENOMEM;
750		return;
751	}
752	nvh->nvh_type = NV_ORDER_HOST | type;
753	nvh->nvh_namesize = (uint8_t)namesize;
754	nvh->nvh_dsize = (uint32_t)vsize;
755	bcopy(name, nvh->nvh_name, namesize);
756
757	/* Add header first. */
758	if (ebuf_add_tail(nv->nv_ebuf, nvh, NVH_HSIZE(nvh)) < 0) {
759		assert(errno != 0);
760		if (nv->nv_error == 0)
761			nv->nv_error = errno;
762		free(nvh);
763		return;
764	}
765	free(nvh);
766	/* Add the actual data. */
767	if (ebuf_add_tail(nv->nv_ebuf, value, vsize) < 0) {
768		assert(errno != 0);
769		if (nv->nv_error == 0)
770			nv->nv_error = errno;
771		return;
772	}
773	/* Align the data (if needed). */
774	vsize = roundup2(vsize, 8) - vsize;
775	if (vsize == 0)
776		return;
777	assert(vsize > 0 && vsize <= sizeof(align));
778	if (ebuf_add_tail(nv->nv_ebuf, align, vsize) < 0) {
779		assert(errno != 0);
780		if (nv->nv_error == 0)
781			nv->nv_error = errno;
782		return;
783	}
784}
785
786static void
787nv_addv(struct nv *nv, const unsigned char *value, size_t vsize, int type,
788    const char *namefmt, va_list nameap)
789{
790	char name[255];
791	size_t namesize;
792
793	namesize = vsnprintf(name, sizeof(name), namefmt, nameap);
794	assert(namesize > 0 && namesize < sizeof(name));
795
796	nv_add(nv, value, vsize, type, name);
797}
798
799static struct nvhdr *
800nv_find(struct nv *nv, int type, const char *namefmt, va_list nameap)
801{
802	char name[255];
803	struct nvhdr *nvh;
804	unsigned char *ptr;
805	size_t size, namesize;
806
807	if (nv == NULL) {
808		errno = ENOMEM;
809		return (NULL);
810	}
811
812	NV_CHECK(nv);
813
814	namesize = vsnprintf(name, sizeof(name), namefmt, nameap);
815	assert(namesize > 0 && namesize < sizeof(name));
816	namesize++;
817
818	ptr = ebuf_data(nv->nv_ebuf, &size);
819	while (size > 0) {
820		assert(size >= sizeof(*nvh) + 2);
821		nvh = (struct nvhdr *)ptr;
822		assert(size >= NVH_SIZE(nvh));
823		nv_swap(nvh, true);
824		if (strcmp(nvh->nvh_name, name) == 0) {
825			if (type != NV_TYPE_NONE &&
826			    (nvh->nvh_type & NV_TYPE_MASK) != type) {
827				errno = EINVAL;
828				if (nv->nv_error == 0)
829					nv->nv_error = EINVAL;
830				return (NULL);
831			}
832			return (nvh);
833		}
834		ptr += NVH_SIZE(nvh);
835		size -= NVH_SIZE(nvh);
836	}
837	errno = ENOENT;
838	if (nv->nv_error == 0)
839		nv->nv_error = ENOENT;
840	return (NULL);
841}
842
843static void
844nv_swap(struct nvhdr *nvh, bool tohost)
845{
846	unsigned char *data, *end, *p;
847	size_t vsize;
848
849	data = NVH_DATA(nvh);
850	if (tohost) {
851		if ((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST)
852			return;
853		nvh->nvh_dsize = le32toh(nvh->nvh_dsize);
854		end = data + nvh->nvh_dsize;
855		nvh->nvh_type &= ~NV_ORDER_MASK;
856		nvh->nvh_type |= NV_ORDER_HOST;
857	} else {
858		if ((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_NETWORK)
859			return;
860		end = data + nvh->nvh_dsize;
861		nvh->nvh_dsize = htole32(nvh->nvh_dsize);
862		nvh->nvh_type &= ~NV_ORDER_MASK;
863		nvh->nvh_type |= NV_ORDER_NETWORK;
864	}
865
866	vsize = 0;
867
868	switch (nvh->nvh_type & NV_TYPE_MASK) {
869	case NV_TYPE_INT8:
870	case NV_TYPE_UINT8:
871	case NV_TYPE_INT8_ARRAY:
872	case NV_TYPE_UINT8_ARRAY:
873		break;
874	case NV_TYPE_INT16:
875	case NV_TYPE_UINT16:
876	case NV_TYPE_INT16_ARRAY:
877	case NV_TYPE_UINT16_ARRAY:
878		if (vsize == 0)
879			vsize = 2;
880		/* FALLTHOUGH */
881	case NV_TYPE_INT32:
882	case NV_TYPE_UINT32:
883	case NV_TYPE_INT32_ARRAY:
884	case NV_TYPE_UINT32_ARRAY:
885		if (vsize == 0)
886			vsize = 4;
887		/* FALLTHOUGH */
888	case NV_TYPE_INT64:
889	case NV_TYPE_UINT64:
890	case NV_TYPE_INT64_ARRAY:
891	case NV_TYPE_UINT64_ARRAY:
892		if (vsize == 0)
893			vsize = 8;
894		for (p = data; p < end; p += vsize) {
895			if (tohost) {
896				switch (vsize) {
897				case 2:
898					*(uint16_t *)(void *)p =
899					    le16toh(*(uint16_t *)(void *)p);
900					break;
901				case 4:
902					*(uint32_t *)(void *)p =
903					    le32toh(*(uint32_t *)(void *)p);
904					break;
905				case 8:
906					*(uint64_t *)(void *)p =
907					    le64toh(*(uint64_t *)(void *)p);
908					break;
909				default:
910					assert(!"invalid condition");
911				}
912			} else {
913				switch (vsize) {
914				case 2:
915					*(uint16_t *)(void *)p =
916					    htole16(*(uint16_t *)(void *)p);
917					break;
918				case 4:
919					*(uint32_t *)(void *)p =
920					    htole32(*(uint32_t *)(void *)p);
921					break;
922				case 8:
923					*(uint64_t *)(void *)p =
924					    htole64(*(uint64_t *)(void *)p);
925					break;
926				default:
927					assert(!"invalid condition");
928				}
929			}
930		}
931		break;
932	case NV_TYPE_STRING:
933		break;
934	default:
935		assert(!"unrecognized type");
936	}
937}
938