1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2009-2010 The FreeBSD Foundation
5 * All rights reserved.
6 *
7 * This software was developed by Pawel Jakub Dawidek under sponsorship from
8 * the FreeBSD Foundation.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#include <sys/cdefs.h>
33__FBSDID("$FreeBSD: stable/11/sbin/hastd/nv.c 330449 2018-03-05 07:26:05Z eadler $");
34
35#include <sys/param.h>
36#include <sys/endian.h>
37
38#include <bitstring.h>
39#include <errno.h>
40#include <stdarg.h>
41#include <stdbool.h>
42#include <stdint.h>
43#include <stdlib.h>
44#include <string.h>
45#include <unistd.h>
46
47#include <ebuf.h>
48#include <pjdlog.h>
49
50#include "nv.h"
51
52#ifndef	PJDLOG_ASSERT
53#include <assert.h>
54#define	PJDLOG_ASSERT(...)	assert(__VA_ARGS__)
55#endif
56#ifndef	PJDLOG_ABORT
57#define	PJDLOG_ABORT(...)	abort()
58#endif
59
60#define	NV_TYPE_NONE		0
61
62#define	NV_TYPE_INT8		1
63#define	NV_TYPE_UINT8		2
64#define	NV_TYPE_INT16		3
65#define	NV_TYPE_UINT16		4
66#define	NV_TYPE_INT32		5
67#define	NV_TYPE_UINT32		6
68#define	NV_TYPE_INT64		7
69#define	NV_TYPE_UINT64		8
70#define	NV_TYPE_INT8_ARRAY	9
71#define	NV_TYPE_UINT8_ARRAY	10
72#define	NV_TYPE_INT16_ARRAY	11
73#define	NV_TYPE_UINT16_ARRAY	12
74#define	NV_TYPE_INT32_ARRAY	13
75#define	NV_TYPE_UINT32_ARRAY	14
76#define	NV_TYPE_INT64_ARRAY	15
77#define	NV_TYPE_UINT64_ARRAY	16
78#define	NV_TYPE_STRING		17
79
80#define	NV_TYPE_MASK		0x7f
81#define	NV_TYPE_FIRST		NV_TYPE_INT8
82#define	NV_TYPE_LAST		NV_TYPE_STRING
83
84#define	NV_ORDER_NETWORK	0x00
85#define	NV_ORDER_HOST		0x80
86
87#define	NV_ORDER_MASK		0x80
88
89#define	NV_MAGIC	0xaea1e
90struct nv {
91	int	nv_magic;
92	int	nv_error;
93	struct ebuf *nv_ebuf;
94};
95
96struct nvhdr {
97	uint8_t		nvh_type;
98	uint8_t		nvh_namesize;
99	uint32_t	nvh_dsize;
100	char		nvh_name[0];
101} __packed;
102#define	NVH_DATA(nvh)	((unsigned char *)nvh + NVH_HSIZE(nvh))
103#define	NVH_HSIZE(nvh)	\
104	(sizeof(struct nvhdr) + roundup2((nvh)->nvh_namesize, 8))
105#define	NVH_DSIZE(nvh)	\
106	(((nvh)->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST ?		\
107	(nvh)->nvh_dsize :						\
108	le32toh((nvh)->nvh_dsize))
109#define	NVH_SIZE(nvh)	(NVH_HSIZE(nvh) + roundup2(NVH_DSIZE(nvh), 8))
110
111#define	NV_CHECK(nv)	do {						\
112	PJDLOG_ASSERT((nv) != NULL);					\
113	PJDLOG_ASSERT((nv)->nv_magic == NV_MAGIC);			\
114} while (0)
115
116static void nv_add(struct nv *nv, const unsigned char *value, size_t vsize,
117    int type, const char *name);
118static void nv_addv(struct nv *nv, const unsigned char *value, size_t vsize,
119    int type, const char *namefmt, va_list nameap);
120static struct nvhdr *nv_find(struct nv *nv, int type, const char *namefmt,
121    va_list nameap);
122static void nv_swap(struct nvhdr *nvh, bool tohost);
123
124/*
125 * Allocate and initialize new nv structure.
126 * Return NULL in case of malloc(3) failure.
127 */
128struct nv *
129nv_alloc(void)
130{
131	struct nv *nv;
132
133	nv = malloc(sizeof(*nv));
134	if (nv == NULL)
135		return (NULL);
136	nv->nv_ebuf = ebuf_alloc(0);
137	if (nv->nv_ebuf == NULL) {
138		free(nv);
139		return (NULL);
140	}
141	nv->nv_error = 0;
142	nv->nv_magic = NV_MAGIC;
143	return (nv);
144}
145
146/*
147 * Free the given nv structure.
148 */
149void
150nv_free(struct nv *nv)
151{
152
153	if (nv == NULL)
154		return;
155
156	NV_CHECK(nv);
157
158	nv->nv_magic = 0;
159	ebuf_free(nv->nv_ebuf);
160	free(nv);
161}
162
163/*
164 * Return error for the given nv structure.
165 */
166int
167nv_error(const struct nv *nv)
168{
169
170	if (nv == NULL)
171		return (ENOMEM);
172
173	NV_CHECK(nv);
174
175	return (nv->nv_error);
176}
177
178/*
179 * Set error for the given nv structure and return previous error.
180 */
181int
182nv_set_error(struct nv *nv, int error)
183{
184	int preverr;
185
186	if (nv == NULL)
187		return (ENOMEM);
188
189	NV_CHECK(nv);
190
191	preverr = nv->nv_error;
192	nv->nv_error = error;
193	return (preverr);
194}
195
196/*
197 * Validate correctness of the entire nv structure and all its elements.
198 * If extrap is not NULL, store number of extra bytes at the end of the buffer.
199 */
200int
201nv_validate(struct nv *nv, size_t *extrap)
202{
203	struct nvhdr *nvh;
204	unsigned char *data, *ptr;
205	size_t dsize, size, vsize;
206	int error;
207
208	if (nv == NULL) {
209		errno = ENOMEM;
210		return (-1);
211	}
212
213	NV_CHECK(nv);
214	PJDLOG_ASSERT(nv->nv_error == 0);
215
216	/* TODO: Check that names are unique? */
217
218	error = 0;
219	ptr = ebuf_data(nv->nv_ebuf, &size);
220	while (size > 0) {
221		/*
222		 * Zeros at the end of the buffer are acceptable.
223		 */
224		if (ptr[0] == '\0')
225			break;
226		/*
227		 * Minimum size at this point is size of nvhdr structure, one
228		 * character long name plus terminating '\0'.
229		 */
230		if (size < sizeof(*nvh) + 2) {
231			error = EINVAL;
232			break;
233		}
234		nvh = (struct nvhdr *)ptr;
235		if (size < NVH_HSIZE(nvh)) {
236			error = EINVAL;
237			break;
238		}
239		if (nvh->nvh_name[nvh->nvh_namesize - 1] != '\0') {
240			error = EINVAL;
241			break;
242		}
243		if (strlen(nvh->nvh_name) !=
244		    (size_t)(nvh->nvh_namesize - 1)) {
245			error = EINVAL;
246			break;
247		}
248		if ((nvh->nvh_type & NV_TYPE_MASK) < NV_TYPE_FIRST ||
249		    (nvh->nvh_type & NV_TYPE_MASK) > NV_TYPE_LAST) {
250			error = EINVAL;
251			break;
252		}
253		dsize = NVH_DSIZE(nvh);
254		if (dsize == 0) {
255			error = EINVAL;
256			break;
257		}
258		if (size < NVH_SIZE(nvh)) {
259			error = EINVAL;
260			break;
261		}
262		vsize = 0;
263		switch (nvh->nvh_type & NV_TYPE_MASK) {
264		case NV_TYPE_INT8:
265		case NV_TYPE_UINT8:
266			if (vsize == 0)
267				vsize = 1;
268			/* FALLTHROUGH */
269		case NV_TYPE_INT16:
270		case NV_TYPE_UINT16:
271			if (vsize == 0)
272				vsize = 2;
273			/* FALLTHROUGH */
274		case NV_TYPE_INT32:
275		case NV_TYPE_UINT32:
276			if (vsize == 0)
277				vsize = 4;
278			/* FALLTHROUGH */
279		case NV_TYPE_INT64:
280		case NV_TYPE_UINT64:
281			if (vsize == 0)
282				vsize = 8;
283			if (dsize != vsize) {
284				error = EINVAL;
285				break;
286			}
287			break;
288		case NV_TYPE_INT8_ARRAY:
289		case NV_TYPE_UINT8_ARRAY:
290			break;
291		case NV_TYPE_INT16_ARRAY:
292		case NV_TYPE_UINT16_ARRAY:
293			if (vsize == 0)
294				vsize = 2;
295			/* FALLTHROUGH */
296		case NV_TYPE_INT32_ARRAY:
297		case NV_TYPE_UINT32_ARRAY:
298			if (vsize == 0)
299				vsize = 4;
300			/* FALLTHROUGH */
301		case NV_TYPE_INT64_ARRAY:
302		case NV_TYPE_UINT64_ARRAY:
303			if (vsize == 0)
304				vsize = 8;
305			if ((dsize % vsize) != 0) {
306				error = EINVAL;
307				break;
308			}
309			break;
310		case NV_TYPE_STRING:
311			data = NVH_DATA(nvh);
312			if (data[dsize - 1] != '\0') {
313				error = EINVAL;
314				break;
315			}
316			if (strlen((char *)data) != dsize - 1) {
317				error = EINVAL;
318				break;
319			}
320			break;
321		default:
322			PJDLOG_ABORT("invalid condition");
323		}
324		if (error != 0)
325			break;
326		ptr += NVH_SIZE(nvh);
327		size -= NVH_SIZE(nvh);
328	}
329	if (error != 0) {
330		errno = error;
331		if (nv->nv_error == 0)
332			nv->nv_error = error;
333		return (-1);
334	}
335	if (extrap != NULL)
336		*extrap = size;
337	return (0);
338}
339
340/*
341 * Convert the given nv structure to network byte order and return ebuf
342 * structure.
343 */
344struct ebuf *
345nv_hton(struct nv *nv)
346{
347	struct nvhdr *nvh;
348	unsigned char *ptr;
349	size_t size;
350
351	NV_CHECK(nv);
352	PJDLOG_ASSERT(nv->nv_error == 0);
353
354	ptr = ebuf_data(nv->nv_ebuf, &size);
355	while (size > 0) {
356		/*
357		 * Minimum size at this point is size of nvhdr structure,
358		 * one character long name plus terminating '\0'.
359		 */
360		PJDLOG_ASSERT(size >= sizeof(*nvh) + 2);
361		nvh = (struct nvhdr *)ptr;
362		PJDLOG_ASSERT(NVH_SIZE(nvh) <= size);
363		nv_swap(nvh, false);
364		ptr += NVH_SIZE(nvh);
365		size -= NVH_SIZE(nvh);
366	}
367
368	return (nv->nv_ebuf);
369}
370
371/*
372 * Create nv structure based on ebuf received from the network.
373 */
374struct nv *
375nv_ntoh(struct ebuf *eb)
376{
377	struct nv *nv;
378	size_t extra;
379	int rerrno;
380
381	PJDLOG_ASSERT(eb != NULL);
382
383	nv = malloc(sizeof(*nv));
384	if (nv == NULL)
385		return (NULL);
386	nv->nv_error = 0;
387	nv->nv_ebuf = eb;
388	nv->nv_magic = NV_MAGIC;
389
390	if (nv_validate(nv, &extra) == -1) {
391		rerrno = errno;
392		nv->nv_magic = 0;
393		free(nv);
394		errno = rerrno;
395		return (NULL);
396	}
397	/*
398	 * Remove extra zeros at the end of the buffer.
399	 */
400	ebuf_del_tail(eb, extra);
401
402	return (nv);
403}
404
405#define	NV_DEFINE_ADD(type, TYPE)					\
406void									\
407nv_add_##type(struct nv *nv, type##_t value, const char *namefmt, ...)	\
408{									\
409	va_list nameap;							\
410									\
411	va_start(nameap, namefmt);					\
412	nv_addv(nv, (unsigned char *)&value, sizeof(value),		\
413	    NV_TYPE_##TYPE, namefmt, nameap);				\
414	va_end(nameap);							\
415}
416
417NV_DEFINE_ADD(int8, INT8)
418NV_DEFINE_ADD(uint8, UINT8)
419NV_DEFINE_ADD(int16, INT16)
420NV_DEFINE_ADD(uint16, UINT16)
421NV_DEFINE_ADD(int32, INT32)
422NV_DEFINE_ADD(uint32, UINT32)
423NV_DEFINE_ADD(int64, INT64)
424NV_DEFINE_ADD(uint64, UINT64)
425
426#undef	NV_DEFINE_ADD
427
428#define	NV_DEFINE_ADD_ARRAY(type, TYPE)					\
429void									\
430nv_add_##type##_array(struct nv *nv, const type##_t *value,		\
431    size_t nsize, const char *namefmt, ...)				\
432{									\
433	va_list nameap;							\
434									\
435	va_start(nameap, namefmt);					\
436	nv_addv(nv, (const unsigned char *)value,			\
437	    sizeof(value[0]) * nsize, NV_TYPE_##TYPE##_ARRAY, namefmt,	\
438	    nameap);							\
439	va_end(nameap);							\
440}
441
442NV_DEFINE_ADD_ARRAY(int8, INT8)
443NV_DEFINE_ADD_ARRAY(uint8, UINT8)
444NV_DEFINE_ADD_ARRAY(int16, INT16)
445NV_DEFINE_ADD_ARRAY(uint16, UINT16)
446NV_DEFINE_ADD_ARRAY(int32, INT32)
447NV_DEFINE_ADD_ARRAY(uint32, UINT32)
448NV_DEFINE_ADD_ARRAY(int64, INT64)
449NV_DEFINE_ADD_ARRAY(uint64, UINT64)
450
451#undef	NV_DEFINE_ADD_ARRAY
452
453void
454nv_add_string(struct nv *nv, const char *value, const char *namefmt, ...)
455{
456	va_list nameap;
457	size_t size;
458
459	size = strlen(value) + 1;
460
461	va_start(nameap, namefmt);
462	nv_addv(nv, (const unsigned char *)value, size, NV_TYPE_STRING,
463	    namefmt, nameap);
464	va_end(nameap);
465}
466
467void
468nv_add_stringf(struct nv *nv, const char *name, const char *valuefmt, ...)
469{
470	va_list valueap;
471
472	va_start(valueap, valuefmt);
473	nv_add_stringv(nv, name, valuefmt, valueap);
474	va_end(valueap);
475}
476
477void
478nv_add_stringv(struct nv *nv, const char *name, const char *valuefmt,
479    va_list valueap)
480{
481	char *value;
482	ssize_t size;
483
484	size = vasprintf(&value, valuefmt, valueap);
485	if (size == -1) {
486		if (nv->nv_error == 0)
487			nv->nv_error = ENOMEM;
488		return;
489	}
490	size++;
491	nv_add(nv, (const unsigned char *)value, size, NV_TYPE_STRING, name);
492	free(value);
493}
494
495#define	NV_DEFINE_GET(type, TYPE)					\
496type##_t								\
497nv_get_##type(struct nv *nv, const char *namefmt, ...)			\
498{									\
499	struct nvhdr *nvh;						\
500	va_list nameap;							\
501	type##_t value;							\
502									\
503	va_start(nameap, namefmt);					\
504	nvh = nv_find(nv, NV_TYPE_##TYPE, namefmt, nameap);		\
505	va_end(nameap);							\
506	if (nvh == NULL)						\
507		return (0);						\
508	PJDLOG_ASSERT((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST);\
509	PJDLOG_ASSERT(sizeof(value) == nvh->nvh_dsize);			\
510	bcopy(NVH_DATA(nvh), &value, sizeof(value));			\
511									\
512	return (value);							\
513}
514
515NV_DEFINE_GET(int8, INT8)
516NV_DEFINE_GET(uint8, UINT8)
517NV_DEFINE_GET(int16, INT16)
518NV_DEFINE_GET(uint16, UINT16)
519NV_DEFINE_GET(int32, INT32)
520NV_DEFINE_GET(uint32, UINT32)
521NV_DEFINE_GET(int64, INT64)
522NV_DEFINE_GET(uint64, UINT64)
523
524#undef	NV_DEFINE_GET
525
526#define	NV_DEFINE_GET_ARRAY(type, TYPE)					\
527const type##_t *							\
528nv_get_##type##_array(struct nv *nv, size_t *sizep,			\
529    const char *namefmt, ...)						\
530{									\
531	struct nvhdr *nvh;						\
532	va_list nameap;							\
533									\
534	va_start(nameap, namefmt);					\
535	nvh = nv_find(nv, NV_TYPE_##TYPE##_ARRAY, namefmt, nameap);	\
536	va_end(nameap);							\
537	if (nvh == NULL)						\
538		return (NULL);						\
539	PJDLOG_ASSERT((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST);\
540	PJDLOG_ASSERT((nvh->nvh_dsize % sizeof(type##_t)) == 0);	\
541	if (sizep != NULL)						\
542		*sizep = nvh->nvh_dsize / sizeof(type##_t);		\
543	return ((type##_t *)(void *)NVH_DATA(nvh));			\
544}
545
546NV_DEFINE_GET_ARRAY(int8, INT8)
547NV_DEFINE_GET_ARRAY(uint8, UINT8)
548NV_DEFINE_GET_ARRAY(int16, INT16)
549NV_DEFINE_GET_ARRAY(uint16, UINT16)
550NV_DEFINE_GET_ARRAY(int32, INT32)
551NV_DEFINE_GET_ARRAY(uint32, UINT32)
552NV_DEFINE_GET_ARRAY(int64, INT64)
553NV_DEFINE_GET_ARRAY(uint64, UINT64)
554
555#undef	NV_DEFINE_GET_ARRAY
556
557const char *
558nv_get_string(struct nv *nv, const char *namefmt, ...)
559{
560	struct nvhdr *nvh;
561	va_list nameap;
562	char *str;
563
564	va_start(nameap, namefmt);
565	nvh = nv_find(nv, NV_TYPE_STRING, namefmt, nameap);
566	va_end(nameap);
567	if (nvh == NULL)
568		return (NULL);
569	PJDLOG_ASSERT((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST);
570	PJDLOG_ASSERT(nvh->nvh_dsize >= 1);
571	str = (char *)NVH_DATA(nvh);
572	PJDLOG_ASSERT(str[nvh->nvh_dsize - 1] == '\0');
573	PJDLOG_ASSERT(strlen(str) == nvh->nvh_dsize - 1);
574	return (str);
575}
576
577static bool
578nv_vexists(struct nv *nv, const char *namefmt, va_list nameap)
579{
580	struct nvhdr *nvh;
581	int snverror, serrno;
582
583	if (nv == NULL)
584		return (false);
585
586	serrno = errno;
587	snverror = nv->nv_error;
588
589	nvh = nv_find(nv, NV_TYPE_NONE, namefmt, nameap);
590
591	errno = serrno;
592	nv->nv_error = snverror;
593
594	return (nvh != NULL);
595}
596
597bool
598nv_exists(struct nv *nv, const char *namefmt, ...)
599{
600	va_list nameap;
601	bool ret;
602
603	va_start(nameap, namefmt);
604	ret = nv_vexists(nv, namefmt, nameap);
605	va_end(nameap);
606
607	return (ret);
608}
609
610void
611nv_assert(struct nv *nv, const char *namefmt, ...)
612{
613	va_list nameap;
614
615	va_start(nameap, namefmt);
616	PJDLOG_ASSERT(nv_vexists(nv, namefmt, nameap));
617	va_end(nameap);
618}
619
620/*
621 * Dump content of the nv structure.
622 */
623void
624nv_dump(struct nv *nv)
625{
626	struct nvhdr *nvh;
627	unsigned char *data, *ptr;
628	size_t dsize, size;
629	unsigned int ii;
630	bool swap;
631
632	if (nv_validate(nv, NULL) == -1) {
633		printf("error: %d\n", errno);
634		return;
635	}
636
637	NV_CHECK(nv);
638	PJDLOG_ASSERT(nv->nv_error == 0);
639
640	ptr = ebuf_data(nv->nv_ebuf, &size);
641	while (size > 0) {
642		PJDLOG_ASSERT(size >= sizeof(*nvh) + 2);
643		nvh = (struct nvhdr *)ptr;
644		PJDLOG_ASSERT(size >= NVH_SIZE(nvh));
645		swap = ((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_NETWORK);
646		dsize = NVH_DSIZE(nvh);
647		data = NVH_DATA(nvh);
648		printf("  %s", nvh->nvh_name);
649		switch (nvh->nvh_type & NV_TYPE_MASK) {
650		case NV_TYPE_INT8:
651			printf("(int8): %jd", (intmax_t)(*(int8_t *)data));
652			break;
653		case NV_TYPE_UINT8:
654			printf("(uint8): %ju", (uintmax_t)(*(uint8_t *)data));
655			break;
656		case NV_TYPE_INT16:
657			printf("(int16): %jd", swap ?
658			    (intmax_t)le16toh(*(int16_t *)(void *)data) :
659			    (intmax_t)*(int16_t *)(void *)data);
660			break;
661		case NV_TYPE_UINT16:
662			printf("(uint16): %ju", swap ?
663			    (uintmax_t)le16toh(*(uint16_t *)(void *)data) :
664			    (uintmax_t)*(uint16_t *)(void *)data);
665			break;
666		case NV_TYPE_INT32:
667			printf("(int32): %jd", swap ?
668			    (intmax_t)le32toh(*(int32_t *)(void *)data) :
669			    (intmax_t)*(int32_t *)(void *)data);
670			break;
671		case NV_TYPE_UINT32:
672			printf("(uint32): %ju", swap ?
673			    (uintmax_t)le32toh(*(uint32_t *)(void *)data) :
674			    (uintmax_t)*(uint32_t *)(void *)data);
675			break;
676		case NV_TYPE_INT64:
677			printf("(int64): %jd", swap ?
678			    (intmax_t)le64toh(*(int64_t *)(void *)data) :
679			    (intmax_t)*(int64_t *)(void *)data);
680			break;
681		case NV_TYPE_UINT64:
682			printf("(uint64): %ju", swap ?
683			    (uintmax_t)le64toh(*(uint64_t *)(void *)data) :
684			    (uintmax_t)*(uint64_t *)(void *)data);
685			break;
686		case NV_TYPE_INT8_ARRAY:
687			printf("(int8 array):");
688			for (ii = 0; ii < dsize; ii++)
689				printf(" %jd", (intmax_t)((int8_t *)data)[ii]);
690			break;
691		case NV_TYPE_UINT8_ARRAY:
692			printf("(uint8 array):");
693			for (ii = 0; ii < dsize; ii++)
694				printf(" %ju", (uintmax_t)((uint8_t *)data)[ii]);
695			break;
696		case NV_TYPE_INT16_ARRAY:
697			printf("(int16 array):");
698			for (ii = 0; ii < dsize / 2; ii++) {
699				printf(" %jd", swap ?
700				    (intmax_t)le16toh(((int16_t *)(void *)data)[ii]) :
701				    (intmax_t)((int16_t *)(void *)data)[ii]);
702			}
703			break;
704		case NV_TYPE_UINT16_ARRAY:
705			printf("(uint16 array):");
706			for (ii = 0; ii < dsize / 2; ii++) {
707				printf(" %ju", swap ?
708				    (uintmax_t)le16toh(((uint16_t *)(void *)data)[ii]) :
709				    (uintmax_t)((uint16_t *)(void *)data)[ii]);
710			}
711			break;
712		case NV_TYPE_INT32_ARRAY:
713			printf("(int32 array):");
714			for (ii = 0; ii < dsize / 4; ii++) {
715				printf(" %jd", swap ?
716				    (intmax_t)le32toh(((int32_t *)(void *)data)[ii]) :
717				    (intmax_t)((int32_t *)(void *)data)[ii]);
718			}
719			break;
720		case NV_TYPE_UINT32_ARRAY:
721			printf("(uint32 array):");
722			for (ii = 0; ii < dsize / 4; ii++) {
723				printf(" %ju", swap ?
724				    (uintmax_t)le32toh(((uint32_t *)(void *)data)[ii]) :
725				    (uintmax_t)((uint32_t *)(void *)data)[ii]);
726			}
727			break;
728		case NV_TYPE_INT64_ARRAY:
729			printf("(int64 array):");
730			for (ii = 0; ii < dsize / 8; ii++) {
731				printf(" %ju", swap ?
732				    (uintmax_t)le64toh(((uint64_t *)(void *)data)[ii]) :
733				    (uintmax_t)((uint64_t *)(void *)data)[ii]);
734			}
735			break;
736		case NV_TYPE_UINT64_ARRAY:
737			printf("(uint64 array):");
738			for (ii = 0; ii < dsize / 8; ii++) {
739				printf(" %ju", swap ?
740				    (uintmax_t)le64toh(((uint64_t *)(void *)data)[ii]) :
741				    (uintmax_t)((uint64_t *)(void *)data)[ii]);
742			}
743			break;
744		case NV_TYPE_STRING:
745			printf("(string): %s", (char *)data);
746			break;
747		default:
748			PJDLOG_ABORT("invalid condition");
749		}
750		printf("\n");
751		ptr += NVH_SIZE(nvh);
752		size -= NVH_SIZE(nvh);
753	}
754}
755
756/*
757 * Local routines below.
758 */
759
760static void
761nv_add(struct nv *nv, const unsigned char *value, size_t vsize, int type,
762    const char *name)
763{
764	static unsigned char align[7];
765	struct nvhdr *nvh;
766	size_t namesize;
767
768	if (nv == NULL) {
769		errno = ENOMEM;
770		return;
771	}
772
773	NV_CHECK(nv);
774
775	namesize = strlen(name) + 1;
776
777	nvh = malloc(sizeof(*nvh) + roundup2(namesize, 8));
778	if (nvh == NULL) {
779		if (nv->nv_error == 0)
780			nv->nv_error = ENOMEM;
781		return;
782	}
783	nvh->nvh_type = NV_ORDER_HOST | type;
784	nvh->nvh_namesize = (uint8_t)namesize;
785	nvh->nvh_dsize = (uint32_t)vsize;
786	bcopy(name, nvh->nvh_name, namesize);
787
788	/* Add header first. */
789	if (ebuf_add_tail(nv->nv_ebuf, nvh, NVH_HSIZE(nvh)) == -1) {
790		PJDLOG_ASSERT(errno != 0);
791		if (nv->nv_error == 0)
792			nv->nv_error = errno;
793		free(nvh);
794		return;
795	}
796	free(nvh);
797	/* Add the actual data. */
798	if (ebuf_add_tail(nv->nv_ebuf, value, vsize) == -1) {
799		PJDLOG_ASSERT(errno != 0);
800		if (nv->nv_error == 0)
801			nv->nv_error = errno;
802		return;
803	}
804	/* Align the data (if needed). */
805	vsize = roundup2(vsize, 8) - vsize;
806	if (vsize == 0)
807		return;
808	PJDLOG_ASSERT(vsize > 0 && vsize <= sizeof(align));
809	if (ebuf_add_tail(nv->nv_ebuf, align, vsize) == -1) {
810		PJDLOG_ASSERT(errno != 0);
811		if (nv->nv_error == 0)
812			nv->nv_error = errno;
813		return;
814	}
815}
816
817static void
818nv_addv(struct nv *nv, const unsigned char *value, size_t vsize, int type,
819    const char *namefmt, va_list nameap)
820{
821	char name[255];
822	size_t namesize;
823
824	namesize = vsnprintf(name, sizeof(name), namefmt, nameap);
825	PJDLOG_ASSERT(namesize > 0 && namesize < sizeof(name));
826
827	nv_add(nv, value, vsize, type, name);
828}
829
830static struct nvhdr *
831nv_find(struct nv *nv, int type, const char *namefmt, va_list nameap)
832{
833	char name[255];
834	struct nvhdr *nvh;
835	unsigned char *ptr;
836	size_t size, namesize;
837
838	if (nv == NULL) {
839		errno = ENOMEM;
840		return (NULL);
841	}
842
843	NV_CHECK(nv);
844
845	namesize = vsnprintf(name, sizeof(name), namefmt, nameap);
846	PJDLOG_ASSERT(namesize > 0 && namesize < sizeof(name));
847	namesize++;
848
849	ptr = ebuf_data(nv->nv_ebuf, &size);
850	while (size > 0) {
851		PJDLOG_ASSERT(size >= sizeof(*nvh) + 2);
852		nvh = (struct nvhdr *)ptr;
853		PJDLOG_ASSERT(size >= NVH_SIZE(nvh));
854		nv_swap(nvh, true);
855		if (strcmp(nvh->nvh_name, name) == 0) {
856			if (type != NV_TYPE_NONE &&
857			    (nvh->nvh_type & NV_TYPE_MASK) != type) {
858				errno = EINVAL;
859				if (nv->nv_error == 0)
860					nv->nv_error = EINVAL;
861				return (NULL);
862			}
863			return (nvh);
864		}
865		ptr += NVH_SIZE(nvh);
866		size -= NVH_SIZE(nvh);
867	}
868	errno = ENOENT;
869	if (nv->nv_error == 0)
870		nv->nv_error = ENOENT;
871	return (NULL);
872}
873
874static void
875nv_swap(struct nvhdr *nvh, bool tohost)
876{
877	unsigned char *data, *end, *p;
878	size_t vsize;
879
880	data = NVH_DATA(nvh);
881	if (tohost) {
882		if ((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST)
883			return;
884		nvh->nvh_dsize = le32toh(nvh->nvh_dsize);
885		end = data + nvh->nvh_dsize;
886		nvh->nvh_type &= ~NV_ORDER_MASK;
887		nvh->nvh_type |= NV_ORDER_HOST;
888	} else {
889		if ((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_NETWORK)
890			return;
891		end = data + nvh->nvh_dsize;
892		nvh->nvh_dsize = htole32(nvh->nvh_dsize);
893		nvh->nvh_type &= ~NV_ORDER_MASK;
894		nvh->nvh_type |= NV_ORDER_NETWORK;
895	}
896
897	vsize = 0;
898
899	switch (nvh->nvh_type & NV_TYPE_MASK) {
900	case NV_TYPE_INT8:
901	case NV_TYPE_UINT8:
902	case NV_TYPE_INT8_ARRAY:
903	case NV_TYPE_UINT8_ARRAY:
904		break;
905	case NV_TYPE_INT16:
906	case NV_TYPE_UINT16:
907	case NV_TYPE_INT16_ARRAY:
908	case NV_TYPE_UINT16_ARRAY:
909		if (vsize == 0)
910			vsize = 2;
911		/* FALLTHROUGH */
912	case NV_TYPE_INT32:
913	case NV_TYPE_UINT32:
914	case NV_TYPE_INT32_ARRAY:
915	case NV_TYPE_UINT32_ARRAY:
916		if (vsize == 0)
917			vsize = 4;
918		/* FALLTHROUGH */
919	case NV_TYPE_INT64:
920	case NV_TYPE_UINT64:
921	case NV_TYPE_INT64_ARRAY:
922	case NV_TYPE_UINT64_ARRAY:
923		if (vsize == 0)
924			vsize = 8;
925		for (p = data; p < end; p += vsize) {
926			if (tohost) {
927				switch (vsize) {
928				case 2:
929					*(uint16_t *)(void *)p =
930					    le16toh(*(uint16_t *)(void *)p);
931					break;
932				case 4:
933					*(uint32_t *)(void *)p =
934					    le32toh(*(uint32_t *)(void *)p);
935					break;
936				case 8:
937					*(uint64_t *)(void *)p =
938					    le64toh(*(uint64_t *)(void *)p);
939					break;
940				default:
941					PJDLOG_ABORT("invalid condition");
942				}
943			} else {
944				switch (vsize) {
945				case 2:
946					*(uint16_t *)(void *)p =
947					    htole16(*(uint16_t *)(void *)p);
948					break;
949				case 4:
950					*(uint32_t *)(void *)p =
951					    htole32(*(uint32_t *)(void *)p);
952					break;
953				case 8:
954					*(uint64_t *)(void *)p =
955					    htole64(*(uint64_t *)(void *)p);
956					break;
957				default:
958					PJDLOG_ABORT("invalid condition");
959				}
960			}
961		}
962		break;
963	case NV_TYPE_STRING:
964		break;
965	default:
966		PJDLOG_ABORT("unrecognized type");
967	}
968}
969