nv.c revision 204076
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 204076 2010-02-18 23:16:19Z 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		return;
711	}
712	/* Add the actual data. */
713	if (ebuf_add_tail(nv->nv_ebuf, value, vsize) < 0) {
714		assert(errno != 0);
715		if (nv->nv_error == 0)
716			nv->nv_error = errno;
717		return;
718	}
719	/* Align the data (if needed). */
720	vsize = roundup2(vsize, 8) - vsize;
721	if (vsize == 0)
722		return;
723	assert(vsize > 0 && vsize <= sizeof(align));
724	if (ebuf_add_tail(nv->nv_ebuf, align, vsize) < 0) {
725		assert(errno != 0);
726		if (nv->nv_error == 0)
727			nv->nv_error = errno;
728		return;
729	}
730}
731
732static void
733nv_addv(struct nv *nv, const unsigned char *value, size_t vsize, int type,
734    const char *namefmt, va_list nameap)
735{
736	char name[255];
737	size_t namesize;
738
739	namesize = vsnprintf(name, sizeof(name), namefmt, nameap);
740	assert(namesize > 0 && namesize < sizeof(name));
741
742	nv_add(nv, value, vsize, type, name);
743}
744
745static struct nvhdr *
746nv_find(struct nv *nv, int type, const char *namefmt, va_list nameap)
747{
748	char name[255];
749	struct nvhdr *nvh;
750	unsigned char *ptr;
751	size_t size, namesize;
752
753	if (nv == NULL) {
754		errno = ENOMEM;
755		return (NULL);
756	}
757
758	NV_CHECK(nv);
759
760	namesize = vsnprintf(name, sizeof(name), namefmt, nameap);
761	assert(namesize > 0 && namesize < sizeof(name));
762	namesize++;
763
764	ptr = ebuf_data(nv->nv_ebuf, &size);
765	while (size > 0) {
766		assert(size >= sizeof(*nvh) + 2);
767		nvh = (struct nvhdr *)ptr;
768		assert(size >= NVH_SIZE(nvh));
769		nv_swap(nvh, true);
770		if (strcmp(nvh->nvh_name, name) == 0) {
771			if ((nvh->nvh_type & NV_TYPE_MASK) != type) {
772				errno = EINVAL;
773				if (nv->nv_error == 0)
774					nv->nv_error = EINVAL;
775				return (NULL);
776			}
777			return (nvh);
778		}
779		ptr += NVH_SIZE(nvh);
780		size -= NVH_SIZE(nvh);
781	}
782	errno = ENOENT;
783	if (nv->nv_error == 0)
784		nv->nv_error = ENOENT;
785	return (NULL);
786}
787
788static void
789nv_swap(struct nvhdr *nvh, bool tohost)
790{
791	unsigned char *data, *end, *p;
792	size_t vsize;
793
794	data = NVH_DATA(nvh);
795	if (tohost) {
796		if ((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST)
797			return;
798		nvh->nvh_dsize = le32toh(nvh->nvh_dsize);
799		end = data + nvh->nvh_dsize;
800		nvh->nvh_type &= ~NV_ORDER_MASK;
801		nvh->nvh_type |= NV_ORDER_HOST;
802	} else {
803		if ((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_NETWORK)
804			return;
805		end = data + nvh->nvh_dsize;
806		nvh->nvh_dsize = htole32(nvh->nvh_dsize);
807		nvh->nvh_type &= ~NV_ORDER_MASK;
808		nvh->nvh_type |= NV_ORDER_NETWORK;
809	}
810
811	vsize = 0;
812
813	switch (nvh->nvh_type & NV_TYPE_MASK) {
814	case NV_TYPE_INT8:
815	case NV_TYPE_UINT8:
816	case NV_TYPE_INT8_ARRAY:
817	case NV_TYPE_UINT8_ARRAY:
818		break;
819	case NV_TYPE_INT16:
820	case NV_TYPE_UINT16:
821	case NV_TYPE_INT16_ARRAY:
822	case NV_TYPE_UINT16_ARRAY:
823		if (vsize == 0)
824			vsize = 2;
825		/* FALLTHOUGH */
826	case NV_TYPE_INT32:
827	case NV_TYPE_UINT32:
828	case NV_TYPE_INT32_ARRAY:
829	case NV_TYPE_UINT32_ARRAY:
830		if (vsize == 0)
831			vsize = 4;
832		/* FALLTHOUGH */
833	case NV_TYPE_INT64:
834	case NV_TYPE_UINT64:
835	case NV_TYPE_INT64_ARRAY:
836	case NV_TYPE_UINT64_ARRAY:
837		if (vsize == 0)
838			vsize = 8;
839		for (p = data; p < end; p += vsize) {
840			if (tohost) {
841				switch (vsize) {
842				case 2:
843					*(uint16_t *)(void *)p =
844					    le16toh(*(uint16_t *)(void *)p);
845					break;
846				case 4:
847					*(uint32_t *)(void *)p =
848					    le32toh(*(uint32_t *)(void *)p);
849					break;
850				case 8:
851					*(uint64_t *)(void *)p =
852					    le64toh(*(uint64_t *)(void *)p);
853					break;
854				default:
855					assert(!"invalid condition");
856				}
857			} else {
858				switch (vsize) {
859				case 2:
860					*(uint16_t *)(void *)p =
861					    htole16(*(uint16_t *)(void *)p);
862					break;
863				case 4:
864					*(uint32_t *)(void *)p =
865					    htole32(*(uint32_t *)(void *)p);
866					break;
867				case 8:
868					*(uint64_t *)(void *)p =
869					    htole64(*(uint64_t *)(void *)p);
870					break;
871				default:
872					assert(!"invalid condition");
873				}
874			}
875		}
876		break;
877	case NV_TYPE_STRING:
878		break;
879	default:
880		assert(!"unrecognized type");
881	}
882}
883