nv.c revision 214282
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 214282 2010-10-24 17:22:34Z 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
49214282Spjd#define	NV_TYPE_INT8		1
50214282Spjd#define	NV_TYPE_UINT8		2
51214282Spjd#define	NV_TYPE_INT16		3
52214282Spjd#define	NV_TYPE_UINT16		4
53214282Spjd#define	NV_TYPE_INT32		5
54214282Spjd#define	NV_TYPE_UINT32		6
55214282Spjd#define	NV_TYPE_INT64		7
56214282Spjd#define	NV_TYPE_UINT64		8
57214282Spjd#define	NV_TYPE_INT8_ARRAY	9
58214282Spjd#define	NV_TYPE_UINT8_ARRAY	10
59214282Spjd#define	NV_TYPE_INT16_ARRAY	11
60214282Spjd#define	NV_TYPE_UINT16_ARRAY	12
61214282Spjd#define	NV_TYPE_INT32_ARRAY	13
62214282Spjd#define	NV_TYPE_UINT32_ARRAY	14
63214282Spjd#define	NV_TYPE_INT64_ARRAY	15
64214282Spjd#define	NV_TYPE_UINT64_ARRAY	16
65214282Spjd#define	NV_TYPE_STRING		17
66214282Spjd
67214282Spjd#define	NV_TYPE_MASK		0x7f
68214282Spjd#define	NV_TYPE_FIRST		NV_TYPE_INT8
69214282Spjd#define	NV_TYPE_LAST		NV_TYPE_STRING
70214282Spjd
71214282Spjd#define	NV_ORDER_NETWORK	0x00
72214282Spjd#define	NV_ORDER_HOST		0x80
73214282Spjd
74214282Spjd#define	NV_ORDER_MASK		0x80
75214282Spjd
76204076Spjd#define	NV_MAGIC	0xaea1e
77204076Spjdstruct nv {
78204076Spjd	int	nv_magic;
79204076Spjd	int	nv_error;
80204076Spjd	struct ebuf *nv_ebuf;
81204076Spjd};
82204076Spjd
83204076Spjdstruct nvhdr {
84204076Spjd	uint8_t		nvh_type;
85204076Spjd	uint8_t		nvh_namesize;
86204076Spjd	uint32_t	nvh_dsize;
87204076Spjd	char		nvh_name[0];
88204076Spjd} __packed;
89204076Spjd#define	NVH_DATA(nvh)	((unsigned char *)nvh + NVH_HSIZE(nvh))
90204076Spjd#define	NVH_HSIZE(nvh)	\
91204076Spjd	(sizeof(struct nvhdr) + roundup2((nvh)->nvh_namesize, 8))
92204076Spjd#define	NVH_DSIZE(nvh)	\
93204076Spjd	(((nvh)->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST ?		\
94204076Spjd	(nvh)->nvh_dsize :						\
95204076Spjd	le32toh((nvh)->nvh_dsize))
96204076Spjd#define	NVH_SIZE(nvh)	(NVH_HSIZE(nvh) + roundup2(NVH_DSIZE(nvh), 8))
97204076Spjd
98204076Spjd#define	NV_CHECK(nv)	do {						\
99204076Spjd	assert((nv) != NULL);						\
100204076Spjd	assert((nv)->nv_magic == NV_MAGIC);				\
101204076Spjd} while (0)
102204076Spjd
103204076Spjdstatic void nv_add(struct nv *nv, const unsigned char *value, size_t vsize,
104204076Spjd    int type, const char *name);
105204076Spjdstatic void nv_addv(struct nv *nv, const unsigned char *value, size_t vsize,
106204076Spjd    int type, const char *namefmt, va_list nameap);
107204076Spjdstatic struct nvhdr *nv_find(struct nv *nv, int type, const char *namefmt,
108204076Spjd    va_list nameap);
109204076Spjdstatic void nv_swap(struct nvhdr *nvh, bool tohost);
110204076Spjd
111204076Spjd/*
112204076Spjd * Allocate and initialize new nv structure.
113204076Spjd * Return NULL in case of malloc(3) failure.
114204076Spjd */
115204076Spjdstruct nv *
116204076Spjdnv_alloc(void)
117204076Spjd{
118204076Spjd	struct nv *nv;
119204076Spjd
120204076Spjd	nv = malloc(sizeof(*nv));
121204076Spjd	if (nv == NULL)
122204076Spjd		return (NULL);
123204076Spjd	nv->nv_ebuf = ebuf_alloc(0);
124204076Spjd	if (nv->nv_ebuf == NULL) {
125204076Spjd		free(nv);
126204076Spjd		return (NULL);
127204076Spjd	}
128204076Spjd	nv->nv_error = 0;
129204076Spjd	nv->nv_magic = NV_MAGIC;
130204076Spjd	return (nv);
131204076Spjd}
132204076Spjd
133204076Spjd/*
134204076Spjd * Free the given nv structure.
135204076Spjd */
136204076Spjdvoid
137204076Spjdnv_free(struct nv *nv)
138204076Spjd{
139204076Spjd
140204076Spjd	if (nv == NULL)
141204076Spjd		return;
142204076Spjd
143204076Spjd	NV_CHECK(nv);
144204076Spjd
145204076Spjd	nv->nv_magic = 0;
146204076Spjd	ebuf_free(nv->nv_ebuf);
147204076Spjd	free(nv);
148204076Spjd}
149204076Spjd
150204076Spjd/*
151204076Spjd * Return error for the given nv structure.
152204076Spjd */
153204076Spjdint
154204076Spjdnv_error(const struct nv *nv)
155204076Spjd{
156204076Spjd
157204076Spjd	if (nv == NULL)
158204076Spjd		return (ENOMEM);
159204076Spjd
160204076Spjd	NV_CHECK(nv);
161204076Spjd
162204076Spjd	return (nv->nv_error);
163204076Spjd}
164204076Spjd
165204076Spjd/*
166204076Spjd * Set error for the given nv structure and return previous error.
167204076Spjd */
168204076Spjdint
169204076Spjdnv_set_error(struct nv *nv, int error)
170204076Spjd{
171204076Spjd	int preverr;
172204076Spjd
173204076Spjd	if (nv == NULL)
174204076Spjd		return (ENOMEM);
175204076Spjd
176204076Spjd	NV_CHECK(nv);
177204076Spjd
178204076Spjd	preverr = nv->nv_error;
179204076Spjd	nv->nv_error = error;
180204076Spjd	return (preverr);
181204076Spjd}
182204076Spjd
183204076Spjd/*
184204076Spjd * Validate correctness of the entire nv structure and all its elements.
185204076Spjd * If extrap is not NULL, store number of extra bytes at the end of the buffer.
186204076Spjd */
187204076Spjdint
188204076Spjdnv_validate(struct nv *nv, size_t *extrap)
189204076Spjd{
190204076Spjd	struct nvhdr *nvh;
191204076Spjd	unsigned char *data, *ptr;
192204076Spjd	size_t dsize, size, vsize;
193204076Spjd	int error;
194204076Spjd
195204076Spjd	if (nv == NULL) {
196204076Spjd		errno = ENOMEM;
197204076Spjd		return (-1);
198204076Spjd	}
199204076Spjd
200204076Spjd	NV_CHECK(nv);
201204076Spjd	assert(nv->nv_error == 0);
202204076Spjd
203204076Spjd	/* TODO: Check that names are unique? */
204204076Spjd
205204076Spjd	error = 0;
206204076Spjd	ptr = ebuf_data(nv->nv_ebuf, &size);
207204076Spjd	while (size > 0) {
208204076Spjd		/*
209204076Spjd		 * Zeros at the end of the buffer are acceptable.
210204076Spjd		 */
211204076Spjd		if (ptr[0] == '\0')
212204076Spjd			break;
213204076Spjd		/*
214204076Spjd		 * Minimum size at this point is size of nvhdr structure, one
215204076Spjd		 * character long name plus terminating '\0'.
216204076Spjd		 */
217204076Spjd		if (size < sizeof(*nvh) + 2) {
218204076Spjd			error = EINVAL;
219204076Spjd			break;
220204076Spjd		}
221204076Spjd		nvh = (struct nvhdr *)ptr;
222204076Spjd		if (size < NVH_HSIZE(nvh)) {
223204076Spjd			error = EINVAL;
224204076Spjd			break;
225204076Spjd		}
226204076Spjd		if (nvh->nvh_name[nvh->nvh_namesize - 1] != '\0') {
227204076Spjd			error = EINVAL;
228204076Spjd			break;
229204076Spjd		}
230204076Spjd		if (strlen(nvh->nvh_name) !=
231204076Spjd		    (size_t)(nvh->nvh_namesize - 1)) {
232204076Spjd			error = EINVAL;
233204076Spjd			break;
234204076Spjd		}
235204076Spjd		if ((nvh->nvh_type & NV_TYPE_MASK) < NV_TYPE_FIRST ||
236204076Spjd		    (nvh->nvh_type & NV_TYPE_MASK) > NV_TYPE_LAST) {
237204076Spjd			error = EINVAL;
238204076Spjd			break;
239204076Spjd		}
240204076Spjd		dsize = NVH_DSIZE(nvh);
241204076Spjd		if (dsize == 0) {
242204076Spjd			error = EINVAL;
243204076Spjd			break;
244204076Spjd		}
245204076Spjd		if (size < NVH_SIZE(nvh)) {
246204076Spjd			error = EINVAL;
247204076Spjd			break;
248204076Spjd		}
249204076Spjd		vsize = 0;
250204076Spjd		switch (nvh->nvh_type & NV_TYPE_MASK) {
251204076Spjd		case NV_TYPE_INT8:
252204076Spjd		case NV_TYPE_UINT8:
253204076Spjd			if (vsize == 0)
254204076Spjd				vsize = 1;
255204076Spjd			/* FALLTHOUGH */
256204076Spjd		case NV_TYPE_INT16:
257204076Spjd		case NV_TYPE_UINT16:
258204076Spjd			if (vsize == 0)
259204076Spjd				vsize = 2;
260204076Spjd			/* FALLTHOUGH */
261204076Spjd		case NV_TYPE_INT32:
262204076Spjd		case NV_TYPE_UINT32:
263204076Spjd			if (vsize == 0)
264204076Spjd				vsize = 4;
265204076Spjd			/* FALLTHOUGH */
266204076Spjd		case NV_TYPE_INT64:
267204076Spjd		case NV_TYPE_UINT64:
268204076Spjd			if (vsize == 0)
269204076Spjd				vsize = 8;
270204076Spjd			if (dsize != vsize) {
271204076Spjd				error = EINVAL;
272204076Spjd				break;
273204076Spjd			}
274204076Spjd			break;
275204076Spjd		case NV_TYPE_INT8_ARRAY:
276204076Spjd		case NV_TYPE_UINT8_ARRAY:
277204076Spjd			break;
278204076Spjd		case NV_TYPE_INT16_ARRAY:
279204076Spjd		case NV_TYPE_UINT16_ARRAY:
280204076Spjd			if (vsize == 0)
281204076Spjd				vsize = 2;
282204076Spjd			/* FALLTHOUGH */
283204076Spjd		case NV_TYPE_INT32_ARRAY:
284204076Spjd		case NV_TYPE_UINT32_ARRAY:
285204076Spjd			if (vsize == 0)
286204076Spjd				vsize = 4;
287204076Spjd			/* FALLTHOUGH */
288204076Spjd		case NV_TYPE_INT64_ARRAY:
289204076Spjd		case NV_TYPE_UINT64_ARRAY:
290204076Spjd			if (vsize == 0)
291204076Spjd				vsize = 8;
292204076Spjd			if ((dsize % vsize) != 0) {
293204076Spjd				error = EINVAL;
294204076Spjd				break;
295204076Spjd			}
296204076Spjd			break;
297204076Spjd		case NV_TYPE_STRING:
298204076Spjd			data = NVH_DATA(nvh);
299204076Spjd			if (data[dsize - 1] != '\0') {
300204076Spjd				error = EINVAL;
301204076Spjd				break;
302204076Spjd			}
303204076Spjd			if (strlen((char *)data) != dsize - 1) {
304204076Spjd				error = EINVAL;
305204076Spjd				break;
306204076Spjd			}
307204076Spjd			break;
308204076Spjd		default:
309204076Spjd			assert(!"invalid condition");
310204076Spjd		}
311204076Spjd		if (error != 0)
312204076Spjd			break;
313204076Spjd		ptr += NVH_SIZE(nvh);
314204076Spjd		size -= NVH_SIZE(nvh);
315204076Spjd	}
316204076Spjd	if (error != 0) {
317204076Spjd		errno = error;
318204076Spjd		if (nv->nv_error == 0)
319204076Spjd			nv->nv_error = error;
320204076Spjd		return (-1);
321204076Spjd	}
322204076Spjd	if (extrap != NULL)
323204076Spjd		*extrap = size;
324204076Spjd	return (0);
325204076Spjd}
326204076Spjd
327204076Spjd/*
328204076Spjd * Convert the given nv structure to network byte order and return ebuf
329204076Spjd * structure.
330204076Spjd */
331204076Spjdstruct ebuf *
332204076Spjdnv_hton(struct nv *nv)
333204076Spjd{
334204076Spjd	struct nvhdr *nvh;
335204076Spjd	unsigned char *ptr;
336204076Spjd	size_t size;
337204076Spjd
338204076Spjd	NV_CHECK(nv);
339204076Spjd	assert(nv->nv_error == 0);
340204076Spjd
341204076Spjd	ptr = ebuf_data(nv->nv_ebuf, &size);
342204076Spjd	while (size > 0) {
343204076Spjd		/*
344204076Spjd		 * Minimum size at this point is size of nvhdr structure,
345204076Spjd		 * one character long name plus terminating '\0'.
346204076Spjd		 */
347204076Spjd		assert(size >= sizeof(*nvh) + 2);
348204076Spjd		nvh = (struct nvhdr *)ptr;
349204076Spjd		assert(NVH_SIZE(nvh) <= size);
350204076Spjd		nv_swap(nvh, false);
351204076Spjd		ptr += NVH_SIZE(nvh);
352204076Spjd		size -= NVH_SIZE(nvh);
353204076Spjd	}
354204076Spjd
355204076Spjd	return (nv->nv_ebuf);
356204076Spjd}
357204076Spjd
358204076Spjd/*
359204076Spjd * Create nv structure based on ebuf received from the network.
360204076Spjd */
361204076Spjdstruct nv *
362204076Spjdnv_ntoh(struct ebuf *eb)
363204076Spjd{
364204076Spjd	struct nv *nv;
365204076Spjd	size_t extra;
366204076Spjd	int rerrno;
367204076Spjd
368204076Spjd	assert(eb != NULL);
369204076Spjd
370204076Spjd	nv = malloc(sizeof(*nv));
371204076Spjd	if (nv == NULL)
372204076Spjd		return (NULL);
373204076Spjd	nv->nv_error = 0;
374204076Spjd	nv->nv_ebuf = eb;
375204076Spjd	nv->nv_magic = NV_MAGIC;
376204076Spjd
377204076Spjd	if (nv_validate(nv, &extra) < 0) {
378204076Spjd		rerrno = errno;
379204076Spjd		nv->nv_magic = 0;
380204076Spjd		free(nv);
381204076Spjd		errno = rerrno;
382204076Spjd		return (NULL);
383204076Spjd	}
384204076Spjd	/*
385204076Spjd	 * Remove extra zeros at the end of the buffer.
386204076Spjd	 */
387204076Spjd	ebuf_del_tail(eb, extra);
388204076Spjd
389204076Spjd	return (nv);
390204076Spjd}
391204076Spjd
392204076Spjd#define	NV_DEFINE_ADD(type, TYPE)					\
393204076Spjdvoid									\
394204076Spjdnv_add_##type(struct nv *nv, type##_t value, const char *namefmt, ...)	\
395204076Spjd{									\
396204076Spjd	va_list nameap;							\
397204076Spjd									\
398204076Spjd	va_start(nameap, namefmt);					\
399204076Spjd	nv_addv(nv, (unsigned char *)&value, sizeof(value),		\
400204076Spjd	    NV_TYPE_##TYPE, namefmt, nameap);				\
401204076Spjd	va_end(nameap);							\
402204076Spjd}
403204076Spjd
404204076SpjdNV_DEFINE_ADD(int8, INT8)
405204076SpjdNV_DEFINE_ADD(uint8, UINT8)
406204076SpjdNV_DEFINE_ADD(int16, INT16)
407204076SpjdNV_DEFINE_ADD(uint16, UINT16)
408204076SpjdNV_DEFINE_ADD(int32, INT32)
409204076SpjdNV_DEFINE_ADD(uint32, UINT32)
410204076SpjdNV_DEFINE_ADD(int64, INT64)
411204076SpjdNV_DEFINE_ADD(uint64, UINT64)
412204076Spjd
413204076Spjd#undef	NV_DEFINE_ADD
414204076Spjd
415204076Spjd#define	NV_DEFINE_ADD_ARRAY(type, TYPE)					\
416204076Spjdvoid									\
417204076Spjdnv_add_##type##_array(struct nv *nv, const type##_t *value,		\
418204076Spjd    size_t nsize, const char *namefmt, ...)				\
419204076Spjd{									\
420204076Spjd	va_list nameap;							\
421204076Spjd									\
422204076Spjd	va_start(nameap, namefmt);					\
423204076Spjd	nv_addv(nv, (const unsigned char *)value,			\
424204076Spjd	    sizeof(value[0]) * nsize, NV_TYPE_##TYPE##_ARRAY, namefmt,	\
425204076Spjd	    nameap);							\
426204076Spjd	va_end(nameap);							\
427204076Spjd}
428204076Spjd
429204076SpjdNV_DEFINE_ADD_ARRAY(int8, INT8)
430204076SpjdNV_DEFINE_ADD_ARRAY(uint8, UINT8)
431204076SpjdNV_DEFINE_ADD_ARRAY(int16, INT16)
432204076SpjdNV_DEFINE_ADD_ARRAY(uint16, UINT16)
433204076SpjdNV_DEFINE_ADD_ARRAY(int32, INT32)
434204076SpjdNV_DEFINE_ADD_ARRAY(uint32, UINT32)
435204076SpjdNV_DEFINE_ADD_ARRAY(int64, INT64)
436204076SpjdNV_DEFINE_ADD_ARRAY(uint64, UINT64)
437204076Spjd
438204076Spjd#undef	NV_DEFINE_ADD_ARRAY
439204076Spjd
440204076Spjdvoid
441204076Spjdnv_add_string(struct nv *nv, const char *value, const char *namefmt, ...)
442204076Spjd{
443204076Spjd	va_list nameap;
444204076Spjd	size_t size;
445204076Spjd
446204076Spjd	size = strlen(value) + 1;
447204076Spjd
448204076Spjd	va_start(nameap, namefmt);
449204076Spjd	nv_addv(nv, (const unsigned char *)value, size, NV_TYPE_STRING,
450204076Spjd	    namefmt, nameap);
451204076Spjd	va_end(nameap);
452204076Spjd}
453204076Spjd
454204076Spjdvoid
455204076Spjdnv_add_stringf(struct nv *nv, const char *name, const char *valuefmt, ...)
456204076Spjd{
457204076Spjd	va_list valueap;
458204076Spjd
459204076Spjd	va_start(valueap, valuefmt);
460204076Spjd	nv_add_stringv(nv, name, valuefmt, valueap);
461204076Spjd	va_end(valueap);
462204076Spjd}
463204076Spjd
464204076Spjdvoid
465204076Spjdnv_add_stringv(struct nv *nv, const char *name, const char *valuefmt,
466204076Spjd    va_list valueap)
467204076Spjd{
468204076Spjd	char *value;
469204076Spjd	ssize_t size;
470204076Spjd
471204076Spjd	size = vasprintf(&value, valuefmt, valueap);
472204076Spjd	if (size < 0) {
473204076Spjd		if (nv->nv_error == 0)
474204076Spjd			nv->nv_error = ENOMEM;
475204076Spjd		return;
476204076Spjd	}
477204076Spjd	size++;
478204076Spjd	nv_add(nv, (const unsigned char *)value, size, NV_TYPE_STRING, name);
479204076Spjd	free(value);
480204076Spjd}
481204076Spjd
482204076Spjd#define	NV_DEFINE_GET(type, TYPE)					\
483204076Spjdtype##_t								\
484204076Spjdnv_get_##type(struct nv *nv, const char *namefmt, ...)			\
485204076Spjd{									\
486204076Spjd	struct nvhdr *nvh;						\
487204076Spjd	va_list nameap;							\
488204076Spjd	type##_t value;							\
489204076Spjd									\
490204076Spjd	va_start(nameap, namefmt);					\
491204076Spjd	nvh = nv_find(nv, NV_TYPE_##TYPE, namefmt, nameap);		\
492204076Spjd	va_end(nameap);							\
493204076Spjd	if (nvh == NULL)						\
494204076Spjd		return (0);						\
495204076Spjd	assert((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST);	\
496204076Spjd	assert(sizeof(value) == nvh->nvh_dsize);			\
497204076Spjd	bcopy(NVH_DATA(nvh), &value, sizeof(value));			\
498204076Spjd									\
499204076Spjd	return (value);							\
500204076Spjd}
501204076Spjd
502204076SpjdNV_DEFINE_GET(int8, INT8)
503204076SpjdNV_DEFINE_GET(uint8, UINT8)
504204076SpjdNV_DEFINE_GET(int16, INT16)
505204076SpjdNV_DEFINE_GET(uint16, UINT16)
506204076SpjdNV_DEFINE_GET(int32, INT32)
507204076SpjdNV_DEFINE_GET(uint32, UINT32)
508204076SpjdNV_DEFINE_GET(int64, INT64)
509204076SpjdNV_DEFINE_GET(uint64, UINT64)
510204076Spjd
511204076Spjd#undef	NV_DEFINE_GET
512204076Spjd
513204076Spjd#define	NV_DEFINE_GET_ARRAY(type, TYPE)					\
514204076Spjdconst type##_t *							\
515204076Spjdnv_get_##type##_array(struct nv *nv, size_t *sizep,			\
516204076Spjd    const char *namefmt, ...)						\
517204076Spjd{									\
518204076Spjd	struct nvhdr *nvh;						\
519204076Spjd	va_list nameap;							\
520204076Spjd									\
521204076Spjd	va_start(nameap, namefmt);					\
522204076Spjd	nvh = nv_find(nv, NV_TYPE_##TYPE##_ARRAY, namefmt, nameap);	\
523204076Spjd	va_end(nameap);							\
524204076Spjd	if (nvh == NULL)						\
525204076Spjd		return (NULL);						\
526204076Spjd	assert((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST);	\
527204076Spjd	assert((nvh->nvh_dsize % sizeof(type##_t)) == 0);		\
528204076Spjd	if (sizep != NULL)						\
529204076Spjd		*sizep = nvh->nvh_dsize / sizeof(type##_t);		\
530204076Spjd	return ((type##_t *)(void *)NVH_DATA(nvh));			\
531204076Spjd}
532204076Spjd
533204076SpjdNV_DEFINE_GET_ARRAY(int8, INT8)
534204076SpjdNV_DEFINE_GET_ARRAY(uint8, UINT8)
535204076SpjdNV_DEFINE_GET_ARRAY(int16, INT16)
536204076SpjdNV_DEFINE_GET_ARRAY(uint16, UINT16)
537204076SpjdNV_DEFINE_GET_ARRAY(int32, INT32)
538204076SpjdNV_DEFINE_GET_ARRAY(uint32, UINT32)
539204076SpjdNV_DEFINE_GET_ARRAY(int64, INT64)
540204076SpjdNV_DEFINE_GET_ARRAY(uint64, UINT64)
541204076Spjd
542204076Spjd#undef	NV_DEFINE_GET_ARRAY
543204076Spjd
544204076Spjdconst char *
545204076Spjdnv_get_string(struct nv *nv, const char *namefmt, ...)
546204076Spjd{
547204076Spjd	struct nvhdr *nvh;
548204076Spjd	va_list nameap;
549204076Spjd	char *str;
550204076Spjd
551204076Spjd	va_start(nameap, namefmt);
552204076Spjd	nvh = nv_find(nv, NV_TYPE_STRING, namefmt, nameap);
553204076Spjd	va_end(nameap);
554204076Spjd	if (nvh == NULL)
555204076Spjd		return (NULL);
556204076Spjd	assert((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST);
557204076Spjd	assert(nvh->nvh_dsize >= 1);
558204076Spjd	str = NVH_DATA(nvh);
559204076Spjd	assert(str[nvh->nvh_dsize - 1] == '\0');
560204076Spjd	assert(strlen(str) == nvh->nvh_dsize - 1);
561204076Spjd	return (str);
562204076Spjd}
563204076Spjd
564204076Spjd/*
565204076Spjd * Dump content of the nv structure.
566204076Spjd */
567204076Spjdvoid
568204076Spjdnv_dump(struct nv *nv)
569204076Spjd{
570204076Spjd	struct nvhdr *nvh;
571204076Spjd	unsigned char *data, *ptr;
572204076Spjd	size_t dsize, size;
573204076Spjd	unsigned int ii;
574204076Spjd	bool swap;
575204076Spjd
576204076Spjd	if (nv_validate(nv, NULL) < 0) {
577204076Spjd		printf("error: %d\n", errno);
578204076Spjd		return;
579204076Spjd	}
580204076Spjd
581204076Spjd	NV_CHECK(nv);
582204076Spjd	assert(nv->nv_error == 0);
583204076Spjd
584204076Spjd	ptr = ebuf_data(nv->nv_ebuf, &size);
585204076Spjd	while (size > 0) {
586204076Spjd		assert(size >= sizeof(*nvh) + 2);
587204076Spjd		nvh = (struct nvhdr *)ptr;
588204076Spjd		assert(size >= NVH_SIZE(nvh));
589204076Spjd		swap = ((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_NETWORK);
590204076Spjd		dsize = NVH_DSIZE(nvh);
591204076Spjd		data = NVH_DATA(nvh);
592204076Spjd		printf("  %s", nvh->nvh_name);
593204076Spjd		switch (nvh->nvh_type & NV_TYPE_MASK) {
594204076Spjd		case NV_TYPE_INT8:
595204076Spjd			printf("(int8): %jd", (intmax_t)(*(int8_t *)data));
596204076Spjd			break;
597204076Spjd		case NV_TYPE_UINT8:
598204076Spjd			printf("(uint8): %ju", (uintmax_t)(*(uint8_t *)data));
599204076Spjd			break;
600204076Spjd		case NV_TYPE_INT16:
601204076Spjd			printf("(int16): %jd", swap ?
602204076Spjd			    (intmax_t)le16toh(*(int16_t *)(void *)data) :
603204076Spjd			    (intmax_t)*(int16_t *)(void *)data);
604204076Spjd			break;
605204076Spjd		case NV_TYPE_UINT16:
606204076Spjd			printf("(uint16): %ju", swap ?
607204076Spjd			    (uintmax_t)le16toh(*(uint16_t *)(void *)data) :
608204076Spjd			    (uintmax_t)*(uint16_t *)(void *)data);
609204076Spjd			break;
610204076Spjd		case NV_TYPE_INT32:
611204076Spjd			printf("(int32): %jd", swap ?
612204076Spjd			    (intmax_t)le32toh(*(int32_t *)(void *)data) :
613204076Spjd			    (intmax_t)*(int32_t *)(void *)data);
614204076Spjd			break;
615204076Spjd		case NV_TYPE_UINT32:
616204076Spjd			printf("(uint32): %ju", swap ?
617204076Spjd			    (uintmax_t)le32toh(*(uint32_t *)(void *)data) :
618204076Spjd			    (uintmax_t)*(uint32_t *)(void *)data);
619204076Spjd			break;
620204076Spjd		case NV_TYPE_INT64:
621204076Spjd			printf("(int64): %jd", swap ?
622204076Spjd			    (intmax_t)le64toh(*(int64_t *)(void *)data) :
623204076Spjd			    (intmax_t)*(int64_t *)(void *)data);
624204076Spjd			break;
625204076Spjd		case NV_TYPE_UINT64:
626204076Spjd			printf("(uint64): %ju", swap ?
627204076Spjd			    (uintmax_t)le64toh(*(uint64_t *)(void *)data) :
628204076Spjd			    (uintmax_t)*(uint64_t *)(void *)data);
629204076Spjd			break;
630204076Spjd		case NV_TYPE_INT8_ARRAY:
631204076Spjd			printf("(int8 array):");
632204076Spjd			for (ii = 0; ii < dsize; ii++)
633204076Spjd				printf(" %jd", (intmax_t)((int8_t *)data)[ii]);
634204076Spjd			break;
635204076Spjd		case NV_TYPE_UINT8_ARRAY:
636204076Spjd			printf("(uint8 array):");
637204076Spjd			for (ii = 0; ii < dsize; ii++)
638204076Spjd				printf(" %ju", (uintmax_t)((uint8_t *)data)[ii]);
639204076Spjd			break;
640204076Spjd		case NV_TYPE_INT16_ARRAY:
641204076Spjd			printf("(int16 array):");
642204076Spjd			for (ii = 0; ii < dsize / 2; ii++) {
643204076Spjd				printf(" %jd", swap ?
644204076Spjd				    (intmax_t)le16toh(((int16_t *)(void *)data)[ii]) :
645204076Spjd				    (intmax_t)((int16_t *)(void *)data)[ii]);
646204076Spjd			}
647204076Spjd			break;
648204076Spjd		case NV_TYPE_UINT16_ARRAY:
649204076Spjd			printf("(uint16 array):");
650204076Spjd			for (ii = 0; ii < dsize / 2; ii++) {
651204076Spjd				printf(" %ju", swap ?
652204076Spjd				    (uintmax_t)le16toh(((uint16_t *)(void *)data)[ii]) :
653204076Spjd				    (uintmax_t)((uint16_t *)(void *)data)[ii]);
654204076Spjd			}
655204076Spjd			break;
656204076Spjd		case NV_TYPE_INT32_ARRAY:
657204076Spjd			printf("(int32 array):");
658204076Spjd			for (ii = 0; ii < dsize / 4; ii++) {
659204076Spjd				printf(" %jd", swap ?
660204076Spjd				    (intmax_t)le32toh(((int32_t *)(void *)data)[ii]) :
661204076Spjd				    (intmax_t)((int32_t *)(void *)data)[ii]);
662204076Spjd			}
663204076Spjd			break;
664204076Spjd		case NV_TYPE_UINT32_ARRAY:
665204076Spjd			printf("(uint32 array):");
666204076Spjd			for (ii = 0; ii < dsize / 4; ii++) {
667204076Spjd				printf(" %ju", swap ?
668204076Spjd				    (uintmax_t)le32toh(((uint32_t *)(void *)data)[ii]) :
669204076Spjd				    (uintmax_t)((uint32_t *)(void *)data)[ii]);
670204076Spjd			}
671204076Spjd			break;
672204076Spjd		case NV_TYPE_INT64_ARRAY:
673204076Spjd			printf("(int64 array):");
674204076Spjd			for (ii = 0; ii < dsize / 8; ii++) {
675204076Spjd				printf(" %ju", swap ?
676204076Spjd				    (uintmax_t)le64toh(((uint64_t *)(void *)data)[ii]) :
677204076Spjd				    (uintmax_t)((uint64_t *)(void *)data)[ii]);
678204076Spjd			}
679204076Spjd			break;
680204076Spjd		case NV_TYPE_UINT64_ARRAY:
681204076Spjd			printf("(uint64 array):");
682204076Spjd			for (ii = 0; ii < dsize / 8; ii++) {
683204076Spjd				printf(" %ju", swap ?
684204076Spjd				    (uintmax_t)le64toh(((uint64_t *)(void *)data)[ii]) :
685204076Spjd				    (uintmax_t)((uint64_t *)(void *)data)[ii]);
686204076Spjd			}
687204076Spjd			break;
688204076Spjd		case NV_TYPE_STRING:
689204076Spjd			printf("(string): %s", (char *)data);
690204076Spjd			break;
691204076Spjd		default:
692204076Spjd			assert(!"invalid condition");
693204076Spjd		}
694204076Spjd		printf("\n");
695204076Spjd		ptr += NVH_SIZE(nvh);
696204076Spjd		size -= NVH_SIZE(nvh);
697204076Spjd	}
698204076Spjd}
699204076Spjd
700204076Spjd/*
701204076Spjd * Local routines below.
702204076Spjd */
703204076Spjd
704204076Spjdstatic void
705204076Spjdnv_add(struct nv *nv, const unsigned char *value, size_t vsize, int type,
706204076Spjd    const char *name)
707204076Spjd{
708204076Spjd	static unsigned char align[7];
709204076Spjd	struct nvhdr *nvh;
710204076Spjd	size_t namesize;
711204076Spjd
712204076Spjd	if (nv == NULL) {
713204076Spjd		errno = ENOMEM;
714204076Spjd		return;
715204076Spjd	}
716204076Spjd
717204076Spjd	NV_CHECK(nv);
718204076Spjd
719204076Spjd	namesize = strlen(name) + 1;
720204076Spjd
721204076Spjd	nvh = malloc(sizeof(*nvh) + roundup2(namesize, 8));
722204076Spjd	if (nvh == NULL) {
723204076Spjd		if (nv->nv_error == 0)
724204076Spjd			nv->nv_error = ENOMEM;
725204076Spjd		return;
726204076Spjd	}
727204076Spjd	nvh->nvh_type = NV_ORDER_HOST | type;
728204076Spjd	nvh->nvh_namesize = (uint8_t)namesize;
729204076Spjd	nvh->nvh_dsize = (uint32_t)vsize;
730204076Spjd	bcopy(name, nvh->nvh_name, namesize);
731204076Spjd
732204076Spjd	/* Add header first. */
733204076Spjd	if (ebuf_add_tail(nv->nv_ebuf, nvh, NVH_HSIZE(nvh)) < 0) {
734204076Spjd		assert(errno != 0);
735204076Spjd		if (nv->nv_error == 0)
736204076Spjd			nv->nv_error = errno;
737209180Spjd		free(nvh);
738204076Spjd		return;
739204076Spjd	}
740209180Spjd	free(nvh);
741204076Spjd	/* Add the actual data. */
742204076Spjd	if (ebuf_add_tail(nv->nv_ebuf, value, vsize) < 0) {
743204076Spjd		assert(errno != 0);
744204076Spjd		if (nv->nv_error == 0)
745204076Spjd			nv->nv_error = errno;
746204076Spjd		return;
747204076Spjd	}
748204076Spjd	/* Align the data (if needed). */
749204076Spjd	vsize = roundup2(vsize, 8) - vsize;
750204076Spjd	if (vsize == 0)
751204076Spjd		return;
752204076Spjd	assert(vsize > 0 && vsize <= sizeof(align));
753204076Spjd	if (ebuf_add_tail(nv->nv_ebuf, align, vsize) < 0) {
754204076Spjd		assert(errno != 0);
755204076Spjd		if (nv->nv_error == 0)
756204076Spjd			nv->nv_error = errno;
757204076Spjd		return;
758204076Spjd	}
759204076Spjd}
760204076Spjd
761204076Spjdstatic void
762204076Spjdnv_addv(struct nv *nv, const unsigned char *value, size_t vsize, int type,
763204076Spjd    const char *namefmt, va_list nameap)
764204076Spjd{
765204076Spjd	char name[255];
766204076Spjd	size_t namesize;
767204076Spjd
768204076Spjd	namesize = vsnprintf(name, sizeof(name), namefmt, nameap);
769204076Spjd	assert(namesize > 0 && namesize < sizeof(name));
770204076Spjd
771204076Spjd	nv_add(nv, value, vsize, type, name);
772204076Spjd}
773204076Spjd
774204076Spjdstatic struct nvhdr *
775204076Spjdnv_find(struct nv *nv, int type, const char *namefmt, va_list nameap)
776204076Spjd{
777204076Spjd	char name[255];
778204076Spjd	struct nvhdr *nvh;
779204076Spjd	unsigned char *ptr;
780204076Spjd	size_t size, namesize;
781204076Spjd
782204076Spjd	if (nv == NULL) {
783204076Spjd		errno = ENOMEM;
784204076Spjd		return (NULL);
785204076Spjd	}
786204076Spjd
787204076Spjd	NV_CHECK(nv);
788204076Spjd
789204076Spjd	namesize = vsnprintf(name, sizeof(name), namefmt, nameap);
790204076Spjd	assert(namesize > 0 && namesize < sizeof(name));
791204076Spjd	namesize++;
792204076Spjd
793204076Spjd	ptr = ebuf_data(nv->nv_ebuf, &size);
794204076Spjd	while (size > 0) {
795204076Spjd		assert(size >= sizeof(*nvh) + 2);
796204076Spjd		nvh = (struct nvhdr *)ptr;
797204076Spjd		assert(size >= NVH_SIZE(nvh));
798204076Spjd		nv_swap(nvh, true);
799204076Spjd		if (strcmp(nvh->nvh_name, name) == 0) {
800204076Spjd			if ((nvh->nvh_type & NV_TYPE_MASK) != type) {
801204076Spjd				errno = EINVAL;
802204076Spjd				if (nv->nv_error == 0)
803204076Spjd					nv->nv_error = EINVAL;
804204076Spjd				return (NULL);
805204076Spjd			}
806204076Spjd			return (nvh);
807204076Spjd		}
808204076Spjd		ptr += NVH_SIZE(nvh);
809204076Spjd		size -= NVH_SIZE(nvh);
810204076Spjd	}
811204076Spjd	errno = ENOENT;
812204076Spjd	if (nv->nv_error == 0)
813204076Spjd		nv->nv_error = ENOENT;
814204076Spjd	return (NULL);
815204076Spjd}
816204076Spjd
817204076Spjdstatic void
818204076Spjdnv_swap(struct nvhdr *nvh, bool tohost)
819204076Spjd{
820204076Spjd	unsigned char *data, *end, *p;
821204076Spjd	size_t vsize;
822204076Spjd
823204076Spjd	data = NVH_DATA(nvh);
824204076Spjd	if (tohost) {
825204076Spjd		if ((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST)
826204076Spjd			return;
827204076Spjd		nvh->nvh_dsize = le32toh(nvh->nvh_dsize);
828204076Spjd		end = data + nvh->nvh_dsize;
829204076Spjd		nvh->nvh_type &= ~NV_ORDER_MASK;
830204076Spjd		nvh->nvh_type |= NV_ORDER_HOST;
831204076Spjd	} else {
832204076Spjd		if ((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_NETWORK)
833204076Spjd			return;
834204076Spjd		end = data + nvh->nvh_dsize;
835204076Spjd		nvh->nvh_dsize = htole32(nvh->nvh_dsize);
836204076Spjd		nvh->nvh_type &= ~NV_ORDER_MASK;
837204076Spjd		nvh->nvh_type |= NV_ORDER_NETWORK;
838204076Spjd	}
839204076Spjd
840204076Spjd	vsize = 0;
841204076Spjd
842204076Spjd	switch (nvh->nvh_type & NV_TYPE_MASK) {
843204076Spjd	case NV_TYPE_INT8:
844204076Spjd	case NV_TYPE_UINT8:
845204076Spjd	case NV_TYPE_INT8_ARRAY:
846204076Spjd	case NV_TYPE_UINT8_ARRAY:
847204076Spjd		break;
848204076Spjd	case NV_TYPE_INT16:
849204076Spjd	case NV_TYPE_UINT16:
850204076Spjd	case NV_TYPE_INT16_ARRAY:
851204076Spjd	case NV_TYPE_UINT16_ARRAY:
852204076Spjd		if (vsize == 0)
853204076Spjd			vsize = 2;
854204076Spjd		/* FALLTHOUGH */
855204076Spjd	case NV_TYPE_INT32:
856204076Spjd	case NV_TYPE_UINT32:
857204076Spjd	case NV_TYPE_INT32_ARRAY:
858204076Spjd	case NV_TYPE_UINT32_ARRAY:
859204076Spjd		if (vsize == 0)
860204076Spjd			vsize = 4;
861204076Spjd		/* FALLTHOUGH */
862204076Spjd	case NV_TYPE_INT64:
863204076Spjd	case NV_TYPE_UINT64:
864204076Spjd	case NV_TYPE_INT64_ARRAY:
865204076Spjd	case NV_TYPE_UINT64_ARRAY:
866204076Spjd		if (vsize == 0)
867204076Spjd			vsize = 8;
868204076Spjd		for (p = data; p < end; p += vsize) {
869204076Spjd			if (tohost) {
870204076Spjd				switch (vsize) {
871204076Spjd				case 2:
872204076Spjd					*(uint16_t *)(void *)p =
873204076Spjd					    le16toh(*(uint16_t *)(void *)p);
874204076Spjd					break;
875204076Spjd				case 4:
876204076Spjd					*(uint32_t *)(void *)p =
877204076Spjd					    le32toh(*(uint32_t *)(void *)p);
878204076Spjd					break;
879204076Spjd				case 8:
880204076Spjd					*(uint64_t *)(void *)p =
881204076Spjd					    le64toh(*(uint64_t *)(void *)p);
882204076Spjd					break;
883204076Spjd				default:
884204076Spjd					assert(!"invalid condition");
885204076Spjd				}
886204076Spjd			} else {
887204076Spjd				switch (vsize) {
888204076Spjd				case 2:
889204076Spjd					*(uint16_t *)(void *)p =
890204076Spjd					    htole16(*(uint16_t *)(void *)p);
891204076Spjd					break;
892204076Spjd				case 4:
893204076Spjd					*(uint32_t *)(void *)p =
894204076Spjd					    htole32(*(uint32_t *)(void *)p);
895204076Spjd					break;
896204076Spjd				case 8:
897204076Spjd					*(uint64_t *)(void *)p =
898204076Spjd					    htole64(*(uint64_t *)(void *)p);
899204076Spjd					break;
900204076Spjd				default:
901204076Spjd					assert(!"invalid condition");
902204076Spjd				}
903204076Spjd			}
904204076Spjd		}
905204076Spjd		break;
906204076Spjd	case NV_TYPE_STRING:
907204076Spjd		break;
908204076Spjd	default:
909204076Spjd		assert(!"unrecognized type");
910204076Spjd	}
911204076Spjd}
912