nv.c revision 217732
1204076Spjd/*-
2204076Spjd * Copyright (c) 2009-2010 The FreeBSD Foundation
3204076Spjd * All rights reserved.
4204076Spjd *
5204076Spjd * This software was developed by Pawel Jakub Dawidek under sponsorship from
6204076Spjd * the FreeBSD Foundation.
7204076Spjd *
8204076Spjd * Redistribution and use in source and binary forms, with or without
9204076Spjd * modification, are permitted provided that the following conditions
10204076Spjd * are met:
11204076Spjd * 1. Redistributions of source code must retain the above copyright
12204076Spjd *    notice, this list of conditions and the following disclaimer.
13204076Spjd * 2. Redistributions in binary form must reproduce the above copyright
14204076Spjd *    notice, this list of conditions and the following disclaimer in the
15204076Spjd *    documentation and/or other materials provided with the distribution.
16204076Spjd *
17204076Spjd * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
18204076Spjd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19204076Spjd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20204076Spjd * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
21204076Spjd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22204076Spjd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23204076Spjd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24204076Spjd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25204076Spjd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26204076Spjd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27204076Spjd * SUCH DAMAGE.
28204076Spjd */
29204076Spjd
30204076Spjd#include <sys/cdefs.h>
31204076Spjd__FBSDID("$FreeBSD: head/sbin/hastd/nv.c 217732 2011-01-22 22:38:18Z pjd $");
32204076Spjd
33204076Spjd#include <sys/param.h>
34204076Spjd#include <sys/endian.h>
35204076Spjd
36204076Spjd#include <assert.h>
37204076Spjd#include <bitstring.h>
38204076Spjd#include <errno.h>
39204076Spjd#include <stdarg.h>
40204076Spjd#include <stdbool.h>
41204076Spjd#include <stdint.h>
42204076Spjd#include <stdlib.h>
43204076Spjd#include <string.h>
44204076Spjd#include <unistd.h>
45204076Spjd
46204076Spjd#include <ebuf.h>
47204076Spjd#include <nv.h>
48204076Spjd
49214283Spjd#define	NV_TYPE_NONE		0
50214283Spjd
51214282Spjd#define	NV_TYPE_INT8		1
52214282Spjd#define	NV_TYPE_UINT8		2
53214282Spjd#define	NV_TYPE_INT16		3
54214282Spjd#define	NV_TYPE_UINT16		4
55214282Spjd#define	NV_TYPE_INT32		5
56214282Spjd#define	NV_TYPE_UINT32		6
57214282Spjd#define	NV_TYPE_INT64		7
58214282Spjd#define	NV_TYPE_UINT64		8
59214282Spjd#define	NV_TYPE_INT8_ARRAY	9
60214282Spjd#define	NV_TYPE_UINT8_ARRAY	10
61214282Spjd#define	NV_TYPE_INT16_ARRAY	11
62214282Spjd#define	NV_TYPE_UINT16_ARRAY	12
63214282Spjd#define	NV_TYPE_INT32_ARRAY	13
64214282Spjd#define	NV_TYPE_UINT32_ARRAY	14
65214282Spjd#define	NV_TYPE_INT64_ARRAY	15
66214282Spjd#define	NV_TYPE_UINT64_ARRAY	16
67214282Spjd#define	NV_TYPE_STRING		17
68214282Spjd
69214282Spjd#define	NV_TYPE_MASK		0x7f
70214282Spjd#define	NV_TYPE_FIRST		NV_TYPE_INT8
71214282Spjd#define	NV_TYPE_LAST		NV_TYPE_STRING
72214282Spjd
73214282Spjd#define	NV_ORDER_NETWORK	0x00
74214282Spjd#define	NV_ORDER_HOST		0x80
75214282Spjd
76214282Spjd#define	NV_ORDER_MASK		0x80
77214282Spjd
78204076Spjd#define	NV_MAGIC	0xaea1e
79204076Spjdstruct nv {
80204076Spjd	int	nv_magic;
81204076Spjd	int	nv_error;
82204076Spjd	struct ebuf *nv_ebuf;
83204076Spjd};
84204076Spjd
85204076Spjdstruct nvhdr {
86204076Spjd	uint8_t		nvh_type;
87204076Spjd	uint8_t		nvh_namesize;
88204076Spjd	uint32_t	nvh_dsize;
89204076Spjd	char		nvh_name[0];
90204076Spjd} __packed;
91204076Spjd#define	NVH_DATA(nvh)	((unsigned char *)nvh + NVH_HSIZE(nvh))
92204076Spjd#define	NVH_HSIZE(nvh)	\
93204076Spjd	(sizeof(struct nvhdr) + roundup2((nvh)->nvh_namesize, 8))
94204076Spjd#define	NVH_DSIZE(nvh)	\
95204076Spjd	(((nvh)->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST ?		\
96204076Spjd	(nvh)->nvh_dsize :						\
97204076Spjd	le32toh((nvh)->nvh_dsize))
98204076Spjd#define	NVH_SIZE(nvh)	(NVH_HSIZE(nvh) + roundup2(NVH_DSIZE(nvh), 8))
99204076Spjd
100204076Spjd#define	NV_CHECK(nv)	do {						\
101204076Spjd	assert((nv) != NULL);						\
102204076Spjd	assert((nv)->nv_magic == NV_MAGIC);				\
103204076Spjd} while (0)
104204076Spjd
105204076Spjdstatic void nv_add(struct nv *nv, const unsigned char *value, size_t vsize,
106204076Spjd    int type, const char *name);
107204076Spjdstatic void nv_addv(struct nv *nv, const unsigned char *value, size_t vsize,
108204076Spjd    int type, const char *namefmt, va_list nameap);
109204076Spjdstatic struct nvhdr *nv_find(struct nv *nv, int type, const char *namefmt,
110204076Spjd    va_list nameap);
111204076Spjdstatic void nv_swap(struct nvhdr *nvh, bool tohost);
112204076Spjd
113204076Spjd/*
114204076Spjd * Allocate and initialize new nv structure.
115204076Spjd * Return NULL in case of malloc(3) failure.
116204076Spjd */
117204076Spjdstruct nv *
118204076Spjdnv_alloc(void)
119204076Spjd{
120204076Spjd	struct nv *nv;
121204076Spjd
122204076Spjd	nv = malloc(sizeof(*nv));
123204076Spjd	if (nv == NULL)
124204076Spjd		return (NULL);
125204076Spjd	nv->nv_ebuf = ebuf_alloc(0);
126204076Spjd	if (nv->nv_ebuf == NULL) {
127204076Spjd		free(nv);
128204076Spjd		return (NULL);
129204076Spjd	}
130204076Spjd	nv->nv_error = 0;
131204076Spjd	nv->nv_magic = NV_MAGIC;
132204076Spjd	return (nv);
133204076Spjd}
134204076Spjd
135204076Spjd/*
136204076Spjd * Free the given nv structure.
137204076Spjd */
138204076Spjdvoid
139204076Spjdnv_free(struct nv *nv)
140204076Spjd{
141204076Spjd
142204076Spjd	if (nv == NULL)
143204076Spjd		return;
144204076Spjd
145204076Spjd	NV_CHECK(nv);
146204076Spjd
147204076Spjd	nv->nv_magic = 0;
148204076Spjd	ebuf_free(nv->nv_ebuf);
149204076Spjd	free(nv);
150204076Spjd}
151204076Spjd
152204076Spjd/*
153204076Spjd * Return error for the given nv structure.
154204076Spjd */
155204076Spjdint
156204076Spjdnv_error(const struct nv *nv)
157204076Spjd{
158204076Spjd
159204076Spjd	if (nv == NULL)
160204076Spjd		return (ENOMEM);
161204076Spjd
162204076Spjd	NV_CHECK(nv);
163204076Spjd
164204076Spjd	return (nv->nv_error);
165204076Spjd}
166204076Spjd
167204076Spjd/*
168204076Spjd * Set error for the given nv structure and return previous error.
169204076Spjd */
170204076Spjdint
171204076Spjdnv_set_error(struct nv *nv, int error)
172204076Spjd{
173204076Spjd	int preverr;
174204076Spjd
175204076Spjd	if (nv == NULL)
176204076Spjd		return (ENOMEM);
177204076Spjd
178204076Spjd	NV_CHECK(nv);
179204076Spjd
180204076Spjd	preverr = nv->nv_error;
181204076Spjd	nv->nv_error = error;
182204076Spjd	return (preverr);
183204076Spjd}
184204076Spjd
185204076Spjd/*
186204076Spjd * Validate correctness of the entire nv structure and all its elements.
187204076Spjd * If extrap is not NULL, store number of extra bytes at the end of the buffer.
188204076Spjd */
189204076Spjdint
190204076Spjdnv_validate(struct nv *nv, size_t *extrap)
191204076Spjd{
192204076Spjd	struct nvhdr *nvh;
193204076Spjd	unsigned char *data, *ptr;
194204076Spjd	size_t dsize, size, vsize;
195204076Spjd	int error;
196204076Spjd
197204076Spjd	if (nv == NULL) {
198204076Spjd		errno = ENOMEM;
199204076Spjd		return (-1);
200204076Spjd	}
201204076Spjd
202204076Spjd	NV_CHECK(nv);
203204076Spjd	assert(nv->nv_error == 0);
204204076Spjd
205204076Spjd	/* TODO: Check that names are unique? */
206204076Spjd
207204076Spjd	error = 0;
208204076Spjd	ptr = ebuf_data(nv->nv_ebuf, &size);
209204076Spjd	while (size > 0) {
210204076Spjd		/*
211204076Spjd		 * Zeros at the end of the buffer are acceptable.
212204076Spjd		 */
213204076Spjd		if (ptr[0] == '\0')
214204076Spjd			break;
215204076Spjd		/*
216204076Spjd		 * Minimum size at this point is size of nvhdr structure, one
217204076Spjd		 * character long name plus terminating '\0'.
218204076Spjd		 */
219204076Spjd		if (size < sizeof(*nvh) + 2) {
220204076Spjd			error = EINVAL;
221204076Spjd			break;
222204076Spjd		}
223204076Spjd		nvh = (struct nvhdr *)ptr;
224204076Spjd		if (size < NVH_HSIZE(nvh)) {
225204076Spjd			error = EINVAL;
226204076Spjd			break;
227204076Spjd		}
228204076Spjd		if (nvh->nvh_name[nvh->nvh_namesize - 1] != '\0') {
229204076Spjd			error = EINVAL;
230204076Spjd			break;
231204076Spjd		}
232204076Spjd		if (strlen(nvh->nvh_name) !=
233204076Spjd		    (size_t)(nvh->nvh_namesize - 1)) {
234204076Spjd			error = EINVAL;
235204076Spjd			break;
236204076Spjd		}
237204076Spjd		if ((nvh->nvh_type & NV_TYPE_MASK) < NV_TYPE_FIRST ||
238204076Spjd		    (nvh->nvh_type & NV_TYPE_MASK) > NV_TYPE_LAST) {
239204076Spjd			error = EINVAL;
240204076Spjd			break;
241204076Spjd		}
242204076Spjd		dsize = NVH_DSIZE(nvh);
243204076Spjd		if (dsize == 0) {
244204076Spjd			error = EINVAL;
245204076Spjd			break;
246204076Spjd		}
247204076Spjd		if (size < NVH_SIZE(nvh)) {
248204076Spjd			error = EINVAL;
249204076Spjd			break;
250204076Spjd		}
251204076Spjd		vsize = 0;
252204076Spjd		switch (nvh->nvh_type & NV_TYPE_MASK) {
253204076Spjd		case NV_TYPE_INT8:
254204076Spjd		case NV_TYPE_UINT8:
255204076Spjd			if (vsize == 0)
256204076Spjd				vsize = 1;
257204076Spjd			/* FALLTHOUGH */
258204076Spjd		case NV_TYPE_INT16:
259204076Spjd		case NV_TYPE_UINT16:
260204076Spjd			if (vsize == 0)
261204076Spjd				vsize = 2;
262204076Spjd			/* FALLTHOUGH */
263204076Spjd		case NV_TYPE_INT32:
264204076Spjd		case NV_TYPE_UINT32:
265204076Spjd			if (vsize == 0)
266204076Spjd				vsize = 4;
267204076Spjd			/* FALLTHOUGH */
268204076Spjd		case NV_TYPE_INT64:
269204076Spjd		case NV_TYPE_UINT64:
270204076Spjd			if (vsize == 0)
271204076Spjd				vsize = 8;
272204076Spjd			if (dsize != vsize) {
273204076Spjd				error = EINVAL;
274204076Spjd				break;
275204076Spjd			}
276204076Spjd			break;
277204076Spjd		case NV_TYPE_INT8_ARRAY:
278204076Spjd		case NV_TYPE_UINT8_ARRAY:
279204076Spjd			break;
280204076Spjd		case NV_TYPE_INT16_ARRAY:
281204076Spjd		case NV_TYPE_UINT16_ARRAY:
282204076Spjd			if (vsize == 0)
283204076Spjd				vsize = 2;
284204076Spjd			/* FALLTHOUGH */
285204076Spjd		case NV_TYPE_INT32_ARRAY:
286204076Spjd		case NV_TYPE_UINT32_ARRAY:
287204076Spjd			if (vsize == 0)
288204076Spjd				vsize = 4;
289204076Spjd			/* FALLTHOUGH */
290204076Spjd		case NV_TYPE_INT64_ARRAY:
291204076Spjd		case NV_TYPE_UINT64_ARRAY:
292204076Spjd			if (vsize == 0)
293204076Spjd				vsize = 8;
294204076Spjd			if ((dsize % vsize) != 0) {
295204076Spjd				error = EINVAL;
296204076Spjd				break;
297204076Spjd			}
298204076Spjd			break;
299204076Spjd		case NV_TYPE_STRING:
300204076Spjd			data = NVH_DATA(nvh);
301204076Spjd			if (data[dsize - 1] != '\0') {
302204076Spjd				error = EINVAL;
303204076Spjd				break;
304204076Spjd			}
305204076Spjd			if (strlen((char *)data) != dsize - 1) {
306204076Spjd				error = EINVAL;
307204076Spjd				break;
308204076Spjd			}
309204076Spjd			break;
310204076Spjd		default:
311204076Spjd			assert(!"invalid condition");
312204076Spjd		}
313204076Spjd		if (error != 0)
314204076Spjd			break;
315204076Spjd		ptr += NVH_SIZE(nvh);
316204076Spjd		size -= NVH_SIZE(nvh);
317204076Spjd	}
318204076Spjd	if (error != 0) {
319204076Spjd		errno = error;
320204076Spjd		if (nv->nv_error == 0)
321204076Spjd			nv->nv_error = error;
322204076Spjd		return (-1);
323204076Spjd	}
324204076Spjd	if (extrap != NULL)
325204076Spjd		*extrap = size;
326204076Spjd	return (0);
327204076Spjd}
328204076Spjd
329204076Spjd/*
330204076Spjd * Convert the given nv structure to network byte order and return ebuf
331204076Spjd * structure.
332204076Spjd */
333204076Spjdstruct ebuf *
334204076Spjdnv_hton(struct nv *nv)
335204076Spjd{
336204076Spjd	struct nvhdr *nvh;
337204076Spjd	unsigned char *ptr;
338204076Spjd	size_t size;
339204076Spjd
340204076Spjd	NV_CHECK(nv);
341204076Spjd	assert(nv->nv_error == 0);
342204076Spjd
343204076Spjd	ptr = ebuf_data(nv->nv_ebuf, &size);
344204076Spjd	while (size > 0) {
345204076Spjd		/*
346204076Spjd		 * Minimum size at this point is size of nvhdr structure,
347204076Spjd		 * one character long name plus terminating '\0'.
348204076Spjd		 */
349204076Spjd		assert(size >= sizeof(*nvh) + 2);
350204076Spjd		nvh = (struct nvhdr *)ptr;
351204076Spjd		assert(NVH_SIZE(nvh) <= size);
352204076Spjd		nv_swap(nvh, false);
353204076Spjd		ptr += NVH_SIZE(nvh);
354204076Spjd		size -= NVH_SIZE(nvh);
355204076Spjd	}
356204076Spjd
357204076Spjd	return (nv->nv_ebuf);
358204076Spjd}
359204076Spjd
360204076Spjd/*
361204076Spjd * Create nv structure based on ebuf received from the network.
362204076Spjd */
363204076Spjdstruct nv *
364204076Spjdnv_ntoh(struct ebuf *eb)
365204076Spjd{
366204076Spjd	struct nv *nv;
367204076Spjd	size_t extra;
368204076Spjd	int rerrno;
369204076Spjd
370204076Spjd	assert(eb != NULL);
371204076Spjd
372204076Spjd	nv = malloc(sizeof(*nv));
373204076Spjd	if (nv == NULL)
374204076Spjd		return (NULL);
375204076Spjd	nv->nv_error = 0;
376204076Spjd	nv->nv_ebuf = eb;
377204076Spjd	nv->nv_magic = NV_MAGIC;
378204076Spjd
379204076Spjd	if (nv_validate(nv, &extra) < 0) {
380204076Spjd		rerrno = errno;
381204076Spjd		nv->nv_magic = 0;
382204076Spjd		free(nv);
383204076Spjd		errno = rerrno;
384204076Spjd		return (NULL);
385204076Spjd	}
386204076Spjd	/*
387204076Spjd	 * Remove extra zeros at the end of the buffer.
388204076Spjd	 */
389204076Spjd	ebuf_del_tail(eb, extra);
390204076Spjd
391204076Spjd	return (nv);
392204076Spjd}
393204076Spjd
394204076Spjd#define	NV_DEFINE_ADD(type, TYPE)					\
395204076Spjdvoid									\
396204076Spjdnv_add_##type(struct nv *nv, type##_t value, const char *namefmt, ...)	\
397204076Spjd{									\
398204076Spjd	va_list nameap;							\
399204076Spjd									\
400204076Spjd	va_start(nameap, namefmt);					\
401204076Spjd	nv_addv(nv, (unsigned char *)&value, sizeof(value),		\
402204076Spjd	    NV_TYPE_##TYPE, namefmt, nameap);				\
403204076Spjd	va_end(nameap);							\
404204076Spjd}
405204076Spjd
406204076SpjdNV_DEFINE_ADD(int8, INT8)
407204076SpjdNV_DEFINE_ADD(uint8, UINT8)
408204076SpjdNV_DEFINE_ADD(int16, INT16)
409204076SpjdNV_DEFINE_ADD(uint16, UINT16)
410204076SpjdNV_DEFINE_ADD(int32, INT32)
411204076SpjdNV_DEFINE_ADD(uint32, UINT32)
412204076SpjdNV_DEFINE_ADD(int64, INT64)
413204076SpjdNV_DEFINE_ADD(uint64, UINT64)
414204076Spjd
415204076Spjd#undef	NV_DEFINE_ADD
416204076Spjd
417204076Spjd#define	NV_DEFINE_ADD_ARRAY(type, TYPE)					\
418204076Spjdvoid									\
419204076Spjdnv_add_##type##_array(struct nv *nv, const type##_t *value,		\
420204076Spjd    size_t nsize, const char *namefmt, ...)				\
421204076Spjd{									\
422204076Spjd	va_list nameap;							\
423204076Spjd									\
424204076Spjd	va_start(nameap, namefmt);					\
425204076Spjd	nv_addv(nv, (const unsigned char *)value,			\
426204076Spjd	    sizeof(value[0]) * nsize, NV_TYPE_##TYPE##_ARRAY, namefmt,	\
427204076Spjd	    nameap);							\
428204076Spjd	va_end(nameap);							\
429204076Spjd}
430204076Spjd
431204076SpjdNV_DEFINE_ADD_ARRAY(int8, INT8)
432204076SpjdNV_DEFINE_ADD_ARRAY(uint8, UINT8)
433204076SpjdNV_DEFINE_ADD_ARRAY(int16, INT16)
434204076SpjdNV_DEFINE_ADD_ARRAY(uint16, UINT16)
435204076SpjdNV_DEFINE_ADD_ARRAY(int32, INT32)
436204076SpjdNV_DEFINE_ADD_ARRAY(uint32, UINT32)
437204076SpjdNV_DEFINE_ADD_ARRAY(int64, INT64)
438204076SpjdNV_DEFINE_ADD_ARRAY(uint64, UINT64)
439204076Spjd
440204076Spjd#undef	NV_DEFINE_ADD_ARRAY
441204076Spjd
442204076Spjdvoid
443204076Spjdnv_add_string(struct nv *nv, const char *value, const char *namefmt, ...)
444204076Spjd{
445204076Spjd	va_list nameap;
446204076Spjd	size_t size;
447204076Spjd
448204076Spjd	size = strlen(value) + 1;
449204076Spjd
450204076Spjd	va_start(nameap, namefmt);
451204076Spjd	nv_addv(nv, (const unsigned char *)value, size, NV_TYPE_STRING,
452204076Spjd	    namefmt, nameap);
453204076Spjd	va_end(nameap);
454204076Spjd}
455204076Spjd
456204076Spjdvoid
457204076Spjdnv_add_stringf(struct nv *nv, const char *name, const char *valuefmt, ...)
458204076Spjd{
459204076Spjd	va_list valueap;
460204076Spjd
461204076Spjd	va_start(valueap, valuefmt);
462204076Spjd	nv_add_stringv(nv, name, valuefmt, valueap);
463204076Spjd	va_end(valueap);
464204076Spjd}
465204076Spjd
466204076Spjdvoid
467204076Spjdnv_add_stringv(struct nv *nv, const char *name, const char *valuefmt,
468204076Spjd    va_list valueap)
469204076Spjd{
470204076Spjd	char *value;
471204076Spjd	ssize_t size;
472204076Spjd
473204076Spjd	size = vasprintf(&value, valuefmt, valueap);
474204076Spjd	if (size < 0) {
475204076Spjd		if (nv->nv_error == 0)
476204076Spjd			nv->nv_error = ENOMEM;
477204076Spjd		return;
478204076Spjd	}
479204076Spjd	size++;
480204076Spjd	nv_add(nv, (const unsigned char *)value, size, NV_TYPE_STRING, name);
481204076Spjd	free(value);
482204076Spjd}
483204076Spjd
484204076Spjd#define	NV_DEFINE_GET(type, TYPE)					\
485204076Spjdtype##_t								\
486204076Spjdnv_get_##type(struct nv *nv, const char *namefmt, ...)			\
487204076Spjd{									\
488204076Spjd	struct nvhdr *nvh;						\
489204076Spjd	va_list nameap;							\
490204076Spjd	type##_t value;							\
491204076Spjd									\
492204076Spjd	va_start(nameap, namefmt);					\
493204076Spjd	nvh = nv_find(nv, NV_TYPE_##TYPE, namefmt, nameap);		\
494204076Spjd	va_end(nameap);							\
495204076Spjd	if (nvh == NULL)						\
496204076Spjd		return (0);						\
497204076Spjd	assert((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST);	\
498204076Spjd	assert(sizeof(value) == nvh->nvh_dsize);			\
499204076Spjd	bcopy(NVH_DATA(nvh), &value, sizeof(value));			\
500204076Spjd									\
501204076Spjd	return (value);							\
502204076Spjd}
503204076Spjd
504204076SpjdNV_DEFINE_GET(int8, INT8)
505204076SpjdNV_DEFINE_GET(uint8, UINT8)
506204076SpjdNV_DEFINE_GET(int16, INT16)
507204076SpjdNV_DEFINE_GET(uint16, UINT16)
508204076SpjdNV_DEFINE_GET(int32, INT32)
509204076SpjdNV_DEFINE_GET(uint32, UINT32)
510204076SpjdNV_DEFINE_GET(int64, INT64)
511204076SpjdNV_DEFINE_GET(uint64, UINT64)
512204076Spjd
513204076Spjd#undef	NV_DEFINE_GET
514204076Spjd
515204076Spjd#define	NV_DEFINE_GET_ARRAY(type, TYPE)					\
516204076Spjdconst type##_t *							\
517204076Spjdnv_get_##type##_array(struct nv *nv, size_t *sizep,			\
518204076Spjd    const char *namefmt, ...)						\
519204076Spjd{									\
520204076Spjd	struct nvhdr *nvh;						\
521204076Spjd	va_list nameap;							\
522204076Spjd									\
523204076Spjd	va_start(nameap, namefmt);					\
524204076Spjd	nvh = nv_find(nv, NV_TYPE_##TYPE##_ARRAY, namefmt, nameap);	\
525204076Spjd	va_end(nameap);							\
526204076Spjd	if (nvh == NULL)						\
527204076Spjd		return (NULL);						\
528204076Spjd	assert((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST);	\
529204076Spjd	assert((nvh->nvh_dsize % sizeof(type##_t)) == 0);		\
530204076Spjd	if (sizep != NULL)						\
531204076Spjd		*sizep = nvh->nvh_dsize / sizeof(type##_t);		\
532204076Spjd	return ((type##_t *)(void *)NVH_DATA(nvh));			\
533204076Spjd}
534204076Spjd
535204076SpjdNV_DEFINE_GET_ARRAY(int8, INT8)
536204076SpjdNV_DEFINE_GET_ARRAY(uint8, UINT8)
537204076SpjdNV_DEFINE_GET_ARRAY(int16, INT16)
538204076SpjdNV_DEFINE_GET_ARRAY(uint16, UINT16)
539204076SpjdNV_DEFINE_GET_ARRAY(int32, INT32)
540204076SpjdNV_DEFINE_GET_ARRAY(uint32, UINT32)
541204076SpjdNV_DEFINE_GET_ARRAY(int64, INT64)
542204076SpjdNV_DEFINE_GET_ARRAY(uint64, UINT64)
543204076Spjd
544204076Spjd#undef	NV_DEFINE_GET_ARRAY
545204076Spjd
546204076Spjdconst char *
547204076Spjdnv_get_string(struct nv *nv, const char *namefmt, ...)
548204076Spjd{
549204076Spjd	struct nvhdr *nvh;
550204076Spjd	va_list nameap;
551204076Spjd	char *str;
552204076Spjd
553204076Spjd	va_start(nameap, namefmt);
554204076Spjd	nvh = nv_find(nv, NV_TYPE_STRING, namefmt, nameap);
555204076Spjd	va_end(nameap);
556204076Spjd	if (nvh == NULL)
557204076Spjd		return (NULL);
558204076Spjd	assert((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST);
559204076Spjd	assert(nvh->nvh_dsize >= 1);
560204076Spjd	str = NVH_DATA(nvh);
561204076Spjd	assert(str[nvh->nvh_dsize - 1] == '\0');
562204076Spjd	assert(strlen(str) == nvh->nvh_dsize - 1);
563204076Spjd	return (str);
564204076Spjd}
565204076Spjd
566217732Spjdstatic bool
567217732Spjdnv_vexists(struct nv *nv, const char *namefmt, va_list nameap)
568214283Spjd{
569214283Spjd	struct nvhdr *nvh;
570214283Spjd	int snverror, serrno;
571214283Spjd
572214283Spjd	if (nv == NULL)
573214283Spjd		return (false);
574214283Spjd
575214283Spjd	serrno = errno;
576214283Spjd	snverror = nv->nv_error;
577214283Spjd
578214283Spjd	nvh = nv_find(nv, NV_TYPE_NONE, namefmt, nameap);
579214283Spjd
580214283Spjd	errno = serrno;
581214283Spjd	nv->nv_error = snverror;
582214283Spjd
583214283Spjd	return (nvh != NULL);
584214283Spjd}
585214283Spjd
586217732Spjdbool
587217732Spjdnv_exists(struct nv *nv, const char *namefmt, ...)
588217732Spjd{
589217732Spjd	va_list nameap;
590217732Spjd	bool ret;
591217732Spjd
592217732Spjd	va_start(nameap, namefmt);
593217732Spjd	ret = nv_vexists(nv, namefmt, nameap);
594217732Spjd	va_end(nameap);
595217732Spjd
596217732Spjd	return (ret);
597217732Spjd}
598217732Spjd
599217732Spjdvoid
600217732Spjdnv_assert(struct nv *nv, const char *namefmt, ...)
601217732Spjd{
602217732Spjd	va_list nameap;
603217732Spjd
604217732Spjd	va_start(nameap, namefmt);
605217732Spjd	assert(nv_vexists(nv, namefmt, nameap));
606217732Spjd	va_end(nameap);
607217732Spjd}
608217732Spjd
609204076Spjd/*
610204076Spjd * Dump content of the nv structure.
611204076Spjd */
612204076Spjdvoid
613204076Spjdnv_dump(struct nv *nv)
614204076Spjd{
615204076Spjd	struct nvhdr *nvh;
616204076Spjd	unsigned char *data, *ptr;
617204076Spjd	size_t dsize, size;
618204076Spjd	unsigned int ii;
619204076Spjd	bool swap;
620204076Spjd
621204076Spjd	if (nv_validate(nv, NULL) < 0) {
622204076Spjd		printf("error: %d\n", errno);
623204076Spjd		return;
624204076Spjd	}
625204076Spjd
626204076Spjd	NV_CHECK(nv);
627204076Spjd	assert(nv->nv_error == 0);
628204076Spjd
629204076Spjd	ptr = ebuf_data(nv->nv_ebuf, &size);
630204076Spjd	while (size > 0) {
631204076Spjd		assert(size >= sizeof(*nvh) + 2);
632204076Spjd		nvh = (struct nvhdr *)ptr;
633204076Spjd		assert(size >= NVH_SIZE(nvh));
634204076Spjd		swap = ((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_NETWORK);
635204076Spjd		dsize = NVH_DSIZE(nvh);
636204076Spjd		data = NVH_DATA(nvh);
637204076Spjd		printf("  %s", nvh->nvh_name);
638204076Spjd		switch (nvh->nvh_type & NV_TYPE_MASK) {
639204076Spjd		case NV_TYPE_INT8:
640204076Spjd			printf("(int8): %jd", (intmax_t)(*(int8_t *)data));
641204076Spjd			break;
642204076Spjd		case NV_TYPE_UINT8:
643204076Spjd			printf("(uint8): %ju", (uintmax_t)(*(uint8_t *)data));
644204076Spjd			break;
645204076Spjd		case NV_TYPE_INT16:
646204076Spjd			printf("(int16): %jd", swap ?
647204076Spjd			    (intmax_t)le16toh(*(int16_t *)(void *)data) :
648204076Spjd			    (intmax_t)*(int16_t *)(void *)data);
649204076Spjd			break;
650204076Spjd		case NV_TYPE_UINT16:
651204076Spjd			printf("(uint16): %ju", swap ?
652204076Spjd			    (uintmax_t)le16toh(*(uint16_t *)(void *)data) :
653204076Spjd			    (uintmax_t)*(uint16_t *)(void *)data);
654204076Spjd			break;
655204076Spjd		case NV_TYPE_INT32:
656204076Spjd			printf("(int32): %jd", swap ?
657204076Spjd			    (intmax_t)le32toh(*(int32_t *)(void *)data) :
658204076Spjd			    (intmax_t)*(int32_t *)(void *)data);
659204076Spjd			break;
660204076Spjd		case NV_TYPE_UINT32:
661204076Spjd			printf("(uint32): %ju", swap ?
662204076Spjd			    (uintmax_t)le32toh(*(uint32_t *)(void *)data) :
663204076Spjd			    (uintmax_t)*(uint32_t *)(void *)data);
664204076Spjd			break;
665204076Spjd		case NV_TYPE_INT64:
666204076Spjd			printf("(int64): %jd", swap ?
667204076Spjd			    (intmax_t)le64toh(*(int64_t *)(void *)data) :
668204076Spjd			    (intmax_t)*(int64_t *)(void *)data);
669204076Spjd			break;
670204076Spjd		case NV_TYPE_UINT64:
671204076Spjd			printf("(uint64): %ju", swap ?
672204076Spjd			    (uintmax_t)le64toh(*(uint64_t *)(void *)data) :
673204076Spjd			    (uintmax_t)*(uint64_t *)(void *)data);
674204076Spjd			break;
675204076Spjd		case NV_TYPE_INT8_ARRAY:
676204076Spjd			printf("(int8 array):");
677204076Spjd			for (ii = 0; ii < dsize; ii++)
678204076Spjd				printf(" %jd", (intmax_t)((int8_t *)data)[ii]);
679204076Spjd			break;
680204076Spjd		case NV_TYPE_UINT8_ARRAY:
681204076Spjd			printf("(uint8 array):");
682204076Spjd			for (ii = 0; ii < dsize; ii++)
683204076Spjd				printf(" %ju", (uintmax_t)((uint8_t *)data)[ii]);
684204076Spjd			break;
685204076Spjd		case NV_TYPE_INT16_ARRAY:
686204076Spjd			printf("(int16 array):");
687204076Spjd			for (ii = 0; ii < dsize / 2; ii++) {
688204076Spjd				printf(" %jd", swap ?
689204076Spjd				    (intmax_t)le16toh(((int16_t *)(void *)data)[ii]) :
690204076Spjd				    (intmax_t)((int16_t *)(void *)data)[ii]);
691204076Spjd			}
692204076Spjd			break;
693204076Spjd		case NV_TYPE_UINT16_ARRAY:
694204076Spjd			printf("(uint16 array):");
695204076Spjd			for (ii = 0; ii < dsize / 2; ii++) {
696204076Spjd				printf(" %ju", swap ?
697204076Spjd				    (uintmax_t)le16toh(((uint16_t *)(void *)data)[ii]) :
698204076Spjd				    (uintmax_t)((uint16_t *)(void *)data)[ii]);
699204076Spjd			}
700204076Spjd			break;
701204076Spjd		case NV_TYPE_INT32_ARRAY:
702204076Spjd			printf("(int32 array):");
703204076Spjd			for (ii = 0; ii < dsize / 4; ii++) {
704204076Spjd				printf(" %jd", swap ?
705204076Spjd				    (intmax_t)le32toh(((int32_t *)(void *)data)[ii]) :
706204076Spjd				    (intmax_t)((int32_t *)(void *)data)[ii]);
707204076Spjd			}
708204076Spjd			break;
709204076Spjd		case NV_TYPE_UINT32_ARRAY:
710204076Spjd			printf("(uint32 array):");
711204076Spjd			for (ii = 0; ii < dsize / 4; ii++) {
712204076Spjd				printf(" %ju", swap ?
713204076Spjd				    (uintmax_t)le32toh(((uint32_t *)(void *)data)[ii]) :
714204076Spjd				    (uintmax_t)((uint32_t *)(void *)data)[ii]);
715204076Spjd			}
716204076Spjd			break;
717204076Spjd		case NV_TYPE_INT64_ARRAY:
718204076Spjd			printf("(int64 array):");
719204076Spjd			for (ii = 0; ii < dsize / 8; ii++) {
720204076Spjd				printf(" %ju", swap ?
721204076Spjd				    (uintmax_t)le64toh(((uint64_t *)(void *)data)[ii]) :
722204076Spjd				    (uintmax_t)((uint64_t *)(void *)data)[ii]);
723204076Spjd			}
724204076Spjd			break;
725204076Spjd		case NV_TYPE_UINT64_ARRAY:
726204076Spjd			printf("(uint64 array):");
727204076Spjd			for (ii = 0; ii < dsize / 8; ii++) {
728204076Spjd				printf(" %ju", swap ?
729204076Spjd				    (uintmax_t)le64toh(((uint64_t *)(void *)data)[ii]) :
730204076Spjd				    (uintmax_t)((uint64_t *)(void *)data)[ii]);
731204076Spjd			}
732204076Spjd			break;
733204076Spjd		case NV_TYPE_STRING:
734204076Spjd			printf("(string): %s", (char *)data);
735204076Spjd			break;
736204076Spjd		default:
737204076Spjd			assert(!"invalid condition");
738204076Spjd		}
739204076Spjd		printf("\n");
740204076Spjd		ptr += NVH_SIZE(nvh);
741204076Spjd		size -= NVH_SIZE(nvh);
742204076Spjd	}
743204076Spjd}
744204076Spjd
745204076Spjd/*
746204076Spjd * Local routines below.
747204076Spjd */
748204076Spjd
749204076Spjdstatic void
750204076Spjdnv_add(struct nv *nv, const unsigned char *value, size_t vsize, int type,
751204076Spjd    const char *name)
752204076Spjd{
753204076Spjd	static unsigned char align[7];
754204076Spjd	struct nvhdr *nvh;
755204076Spjd	size_t namesize;
756204076Spjd
757204076Spjd	if (nv == NULL) {
758204076Spjd		errno = ENOMEM;
759204076Spjd		return;
760204076Spjd	}
761204076Spjd
762204076Spjd	NV_CHECK(nv);
763204076Spjd
764204076Spjd	namesize = strlen(name) + 1;
765204076Spjd
766204076Spjd	nvh = malloc(sizeof(*nvh) + roundup2(namesize, 8));
767204076Spjd	if (nvh == NULL) {
768204076Spjd		if (nv->nv_error == 0)
769204076Spjd			nv->nv_error = ENOMEM;
770204076Spjd		return;
771204076Spjd	}
772204076Spjd	nvh->nvh_type = NV_ORDER_HOST | type;
773204076Spjd	nvh->nvh_namesize = (uint8_t)namesize;
774204076Spjd	nvh->nvh_dsize = (uint32_t)vsize;
775204076Spjd	bcopy(name, nvh->nvh_name, namesize);
776204076Spjd
777204076Spjd	/* Add header first. */
778204076Spjd	if (ebuf_add_tail(nv->nv_ebuf, nvh, NVH_HSIZE(nvh)) < 0) {
779204076Spjd		assert(errno != 0);
780204076Spjd		if (nv->nv_error == 0)
781204076Spjd			nv->nv_error = errno;
782209180Spjd		free(nvh);
783204076Spjd		return;
784204076Spjd	}
785209180Spjd	free(nvh);
786204076Spjd	/* Add the actual data. */
787204076Spjd	if (ebuf_add_tail(nv->nv_ebuf, value, vsize) < 0) {
788204076Spjd		assert(errno != 0);
789204076Spjd		if (nv->nv_error == 0)
790204076Spjd			nv->nv_error = errno;
791204076Spjd		return;
792204076Spjd	}
793204076Spjd	/* Align the data (if needed). */
794204076Spjd	vsize = roundup2(vsize, 8) - vsize;
795204076Spjd	if (vsize == 0)
796204076Spjd		return;
797204076Spjd	assert(vsize > 0 && vsize <= sizeof(align));
798204076Spjd	if (ebuf_add_tail(nv->nv_ebuf, align, vsize) < 0) {
799204076Spjd		assert(errno != 0);
800204076Spjd		if (nv->nv_error == 0)
801204076Spjd			nv->nv_error = errno;
802204076Spjd		return;
803204076Spjd	}
804204076Spjd}
805204076Spjd
806204076Spjdstatic void
807204076Spjdnv_addv(struct nv *nv, const unsigned char *value, size_t vsize, int type,
808204076Spjd    const char *namefmt, va_list nameap)
809204076Spjd{
810204076Spjd	char name[255];
811204076Spjd	size_t namesize;
812204076Spjd
813204076Spjd	namesize = vsnprintf(name, sizeof(name), namefmt, nameap);
814204076Spjd	assert(namesize > 0 && namesize < sizeof(name));
815204076Spjd
816204076Spjd	nv_add(nv, value, vsize, type, name);
817204076Spjd}
818204076Spjd
819204076Spjdstatic struct nvhdr *
820204076Spjdnv_find(struct nv *nv, int type, const char *namefmt, va_list nameap)
821204076Spjd{
822204076Spjd	char name[255];
823204076Spjd	struct nvhdr *nvh;
824204076Spjd	unsigned char *ptr;
825204076Spjd	size_t size, namesize;
826204076Spjd
827204076Spjd	if (nv == NULL) {
828204076Spjd		errno = ENOMEM;
829204076Spjd		return (NULL);
830204076Spjd	}
831204076Spjd
832204076Spjd	NV_CHECK(nv);
833204076Spjd
834204076Spjd	namesize = vsnprintf(name, sizeof(name), namefmt, nameap);
835204076Spjd	assert(namesize > 0 && namesize < sizeof(name));
836204076Spjd	namesize++;
837204076Spjd
838204076Spjd	ptr = ebuf_data(nv->nv_ebuf, &size);
839204076Spjd	while (size > 0) {
840204076Spjd		assert(size >= sizeof(*nvh) + 2);
841204076Spjd		nvh = (struct nvhdr *)ptr;
842204076Spjd		assert(size >= NVH_SIZE(nvh));
843204076Spjd		nv_swap(nvh, true);
844204076Spjd		if (strcmp(nvh->nvh_name, name) == 0) {
845214283Spjd			if (type != NV_TYPE_NONE &&
846214283Spjd			    (nvh->nvh_type & NV_TYPE_MASK) != type) {
847204076Spjd				errno = EINVAL;
848204076Spjd				if (nv->nv_error == 0)
849204076Spjd					nv->nv_error = EINVAL;
850204076Spjd				return (NULL);
851204076Spjd			}
852204076Spjd			return (nvh);
853204076Spjd		}
854204076Spjd		ptr += NVH_SIZE(nvh);
855204076Spjd		size -= NVH_SIZE(nvh);
856204076Spjd	}
857204076Spjd	errno = ENOENT;
858204076Spjd	if (nv->nv_error == 0)
859204076Spjd		nv->nv_error = ENOENT;
860204076Spjd	return (NULL);
861204076Spjd}
862204076Spjd
863204076Spjdstatic void
864204076Spjdnv_swap(struct nvhdr *nvh, bool tohost)
865204076Spjd{
866204076Spjd	unsigned char *data, *end, *p;
867204076Spjd	size_t vsize;
868204076Spjd
869204076Spjd	data = NVH_DATA(nvh);
870204076Spjd	if (tohost) {
871204076Spjd		if ((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST)
872204076Spjd			return;
873204076Spjd		nvh->nvh_dsize = le32toh(nvh->nvh_dsize);
874204076Spjd		end = data + nvh->nvh_dsize;
875204076Spjd		nvh->nvh_type &= ~NV_ORDER_MASK;
876204076Spjd		nvh->nvh_type |= NV_ORDER_HOST;
877204076Spjd	} else {
878204076Spjd		if ((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_NETWORK)
879204076Spjd			return;
880204076Spjd		end = data + nvh->nvh_dsize;
881204076Spjd		nvh->nvh_dsize = htole32(nvh->nvh_dsize);
882204076Spjd		nvh->nvh_type &= ~NV_ORDER_MASK;
883204076Spjd		nvh->nvh_type |= NV_ORDER_NETWORK;
884204076Spjd	}
885204076Spjd
886204076Spjd	vsize = 0;
887204076Spjd
888204076Spjd	switch (nvh->nvh_type & NV_TYPE_MASK) {
889204076Spjd	case NV_TYPE_INT8:
890204076Spjd	case NV_TYPE_UINT8:
891204076Spjd	case NV_TYPE_INT8_ARRAY:
892204076Spjd	case NV_TYPE_UINT8_ARRAY:
893204076Spjd		break;
894204076Spjd	case NV_TYPE_INT16:
895204076Spjd	case NV_TYPE_UINT16:
896204076Spjd	case NV_TYPE_INT16_ARRAY:
897204076Spjd	case NV_TYPE_UINT16_ARRAY:
898204076Spjd		if (vsize == 0)
899204076Spjd			vsize = 2;
900204076Spjd		/* FALLTHOUGH */
901204076Spjd	case NV_TYPE_INT32:
902204076Spjd	case NV_TYPE_UINT32:
903204076Spjd	case NV_TYPE_INT32_ARRAY:
904204076Spjd	case NV_TYPE_UINT32_ARRAY:
905204076Spjd		if (vsize == 0)
906204076Spjd			vsize = 4;
907204076Spjd		/* FALLTHOUGH */
908204076Spjd	case NV_TYPE_INT64:
909204076Spjd	case NV_TYPE_UINT64:
910204076Spjd	case NV_TYPE_INT64_ARRAY:
911204076Spjd	case NV_TYPE_UINT64_ARRAY:
912204076Spjd		if (vsize == 0)
913204076Spjd			vsize = 8;
914204076Spjd		for (p = data; p < end; p += vsize) {
915204076Spjd			if (tohost) {
916204076Spjd				switch (vsize) {
917204076Spjd				case 2:
918204076Spjd					*(uint16_t *)(void *)p =
919204076Spjd					    le16toh(*(uint16_t *)(void *)p);
920204076Spjd					break;
921204076Spjd				case 4:
922204076Spjd					*(uint32_t *)(void *)p =
923204076Spjd					    le32toh(*(uint32_t *)(void *)p);
924204076Spjd					break;
925204076Spjd				case 8:
926204076Spjd					*(uint64_t *)(void *)p =
927204076Spjd					    le64toh(*(uint64_t *)(void *)p);
928204076Spjd					break;
929204076Spjd				default:
930204076Spjd					assert(!"invalid condition");
931204076Spjd				}
932204076Spjd			} else {
933204076Spjd				switch (vsize) {
934204076Spjd				case 2:
935204076Spjd					*(uint16_t *)(void *)p =
936204076Spjd					    htole16(*(uint16_t *)(void *)p);
937204076Spjd					break;
938204076Spjd				case 4:
939204076Spjd					*(uint32_t *)(void *)p =
940204076Spjd					    htole32(*(uint32_t *)(void *)p);
941204076Spjd					break;
942204076Spjd				case 8:
943204076Spjd					*(uint64_t *)(void *)p =
944204076Spjd					    htole64(*(uint64_t *)(void *)p);
945204076Spjd					break;
946204076Spjd				default:
947204076Spjd					assert(!"invalid condition");
948204076Spjd				}
949204076Spjd			}
950204076Spjd		}
951204076Spjd		break;
952204076Spjd	case NV_TYPE_STRING:
953204076Spjd		break;
954204076Spjd	default:
955204076Spjd		assert(!"unrecognized type");
956204076Spjd	}
957204076Spjd}
958