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