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