xref: /freebsd/stand/libsa/zfs/nvlist.c (revision 9e5787d2284e187abb5b654d924394a65772e004)
1 /*-
2  * Copyright 2020 Toomas Soome <tsoome@me.com>
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  */
25 
26 #include <sys/cdefs.h>
27 __FBSDID("$FreeBSD$");
28 
29 #include <stand.h>
30 #include <sys/endian.h>
31 #include <zfsimpl.h>
32 #include "libzfs.h"
33 
34 typedef struct xdr {
35 	int (*xdr_getint)(const struct xdr *, const void *, int *);
36 } xdr_t;
37 
38 static int xdr_int(const xdr_t *, const void *, int *);
39 static int mem_int(const xdr_t *, const void *, int *);
40 static void nvlist_decode_nvlist(const xdr_t *, nvlist_t *);
41 static int nvlist_size(const xdr_t *, const uint8_t *);
42 
43 /*
44  * transform data from network to host.
45  */
46 xdr_t ntoh = {
47 	.xdr_getint = xdr_int
48 };
49 
50 /*
51  * transform data from host to host.
52  */
53 xdr_t native = {
54 	.xdr_getint = mem_int
55 };
56 
57 /*
58  * transform data from host to network.
59  */
60 xdr_t hton = {
61 	.xdr_getint = xdr_int
62 };
63 
64 static int
65 xdr_short(const xdr_t *xdr, const uint8_t *buf, short *ip)
66 {
67 	int i, rv;
68 
69 	rv = xdr->xdr_getint(xdr, buf, &i);
70 	*ip = i;
71 	return (rv);
72 }
73 
74 static int
75 xdr_u_short(const xdr_t *xdr, const uint8_t *buf, unsigned short *ip)
76 {
77 	unsigned u;
78 	int rv;
79 
80 	rv = xdr->xdr_getint(xdr, buf, &u);
81 	*ip = u;
82 	return (rv);
83 }
84 
85 static int
86 xdr_int(const xdr_t *xdr __unused, const void *buf, int *ip)
87 {
88 	*ip = be32dec(buf);
89 	return (sizeof(int));
90 }
91 
92 static int
93 xdr_u_int(const xdr_t *xdr __unused, const void *buf, unsigned *ip)
94 {
95 	*ip = be32dec(buf);
96 	return (sizeof(unsigned));
97 }
98 
99 static int
100 xdr_string(const xdr_t *xdr, const void *buf, nv_string_t *s)
101 {
102 	int size;
103 
104 	size = xdr->xdr_getint(xdr, buf, &s->nv_size);
105 	size = NV_ALIGN4(size + s->nv_size);
106 	return (size);
107 }
108 
109 static int
110 xdr_int64(const xdr_t *xdr, const uint8_t *buf, int64_t *lp)
111 {
112 	int hi, rv;
113 	unsigned lo;
114 
115 	rv = xdr->xdr_getint(xdr, buf, &hi);
116 	rv += xdr->xdr_getint(xdr, buf + rv, &lo);
117 	*lp = (((int64_t)hi) << 32) | lo;
118 	return (rv);
119 }
120 
121 static int
122 xdr_uint64(const xdr_t *xdr, const uint8_t *buf, uint64_t *lp)
123 {
124 	unsigned hi, lo;
125 	int rv;
126 
127 	rv = xdr->xdr_getint(xdr, buf, &hi);
128 	rv += xdr->xdr_getint(xdr, buf + rv, &lo);
129 	*lp = (((int64_t)hi) << 32) | lo;
130 	return (rv);
131 }
132 
133 static int
134 xdr_char(const xdr_t *xdr, const uint8_t *buf, char *cp)
135 {
136 	int i, rv;
137 
138 	rv = xdr->xdr_getint(xdr, buf, &i);
139 	*cp = i;
140 	return (rv);
141 }
142 
143 /*
144  * read native data.
145  */
146 static int
147 mem_int(const xdr_t *xdr, const void *buf, int *i)
148 {
149 	*i = *(int *)buf;
150 	return (sizeof(int));
151 }
152 
153 void
154 nvlist_destroy(nvlist_t *nvl)
155 {
156 	if (nvl != NULL) {
157 		/* Free data if it was allocated by us. */
158 		if (nvl->nv_asize > 0)
159 			free(nvl->nv_data);
160 	}
161 	free(nvl);
162 }
163 
164 char *
165 nvstring_get(nv_string_t *nvs)
166 {
167 	char *s;
168 
169 	s = malloc(nvs->nv_size + 1);
170 	if (s != NULL) {
171 		bcopy(nvs->nv_data, s, nvs->nv_size);
172 		s[nvs->nv_size] = '\0';
173 	}
174 	return (s);
175 }
176 
177 /*
178  * Create empty nvlist.
179  * The nvlist is terminated by 2x zeros (8 bytes).
180  */
181 nvlist_t *
182 nvlist_create(int flag)
183 {
184 	nvlist_t *nvl;
185 	nvs_data_t *nvs;
186 
187 	nvl = calloc(1, sizeof(*nvl));
188 	if (nvl == NULL)
189 		return (nvl);
190 
191 	nvl->nv_header.nvh_encoding = NV_ENCODE_XDR;
192 	nvl->nv_header.nvh_endian = _BYTE_ORDER == _LITTLE_ENDIAN;
193 
194 	nvl->nv_asize = nvl->nv_size = sizeof(*nvs);
195 	nvs = calloc(1, nvl->nv_asize);
196 	if (nvs == NULL) {
197 		free(nvl);
198 		return (NULL);
199 	}
200 	/* data in nvlist is byte stream */
201 	nvl->nv_data = (uint8_t *)nvs;
202 
203 	nvs->nvl_version = NV_VERSION;
204 	nvs->nvl_nvflag = flag;
205 	return (nvl);
206 }
207 
208 static void
209 nvlist_nvp_decode(const xdr_t *xdr, nvlist_t *nvl, nvp_header_t *nvph)
210 {
211 	nv_string_t *nv_string;
212 	nv_pair_data_t *nvp_data;
213 	nvlist_t nvlist;
214 
215 	nv_string = (nv_string_t *)nvl->nv_idx;
216 	nvl->nv_idx += xdr_string(xdr, &nv_string->nv_size, nv_string);
217 	nvp_data = (nv_pair_data_t *)nvl->nv_idx;
218 
219 	nvl->nv_idx += xdr_u_int(xdr, &nvp_data->nv_type, &nvp_data->nv_type);
220 	nvl->nv_idx += xdr_u_int(xdr, &nvp_data->nv_nelem, &nvp_data->nv_nelem);
221 
222 	switch (nvp_data->nv_type) {
223 	case DATA_TYPE_NVLIST:
224 	case DATA_TYPE_NVLIST_ARRAY:
225 		bzero(&nvlist, sizeof (nvlist));
226 		nvlist.nv_data = &nvp_data->nv_data[0];
227 		nvlist.nv_idx = nvlist.nv_data;
228 		for (int i = 0; i < nvp_data->nv_nelem; i++) {
229 			nvlist.nv_asize =
230 			    nvlist_size(xdr, nvlist.nv_data);
231 			nvlist_decode_nvlist(xdr, &nvlist);
232 			nvl->nv_idx = nvlist.nv_idx;
233 			nvlist.nv_data = nvlist.nv_idx;
234 		}
235 		break;
236 
237 	case DATA_TYPE_BOOLEAN:
238 		/* BOOLEAN does not take value space */
239 		break;
240 	case DATA_TYPE_BYTE:
241 	case DATA_TYPE_INT8:
242 	case DATA_TYPE_UINT8:
243 		nvl->nv_idx += xdr_char(xdr, &nvp_data->nv_data[0],
244 		    (char *)&nvp_data->nv_data[0]);
245 		break;
246 
247 	case DATA_TYPE_INT16:
248 		nvl->nv_idx += xdr_short(xdr, &nvp_data->nv_data[0],
249 		    (short *)&nvp_data->nv_data[0]);
250 		break;
251 
252 	case DATA_TYPE_UINT16:
253 		nvl->nv_idx += xdr_u_short(xdr, &nvp_data->nv_data[0],
254 		    (unsigned short *)&nvp_data->nv_data[0]);
255 		break;
256 
257 	case DATA_TYPE_BOOLEAN_VALUE:
258 	case DATA_TYPE_INT32:
259 		nvl->nv_idx += xdr_int(xdr, &nvp_data->nv_data[0],
260 		    (int *)&nvp_data->nv_data[0]);
261 		break;
262 
263 	case DATA_TYPE_UINT32:
264 		nvl->nv_idx += xdr_u_int(xdr, &nvp_data->nv_data[0],
265 		    (unsigned *)&nvp_data->nv_data[0]);
266 		break;
267 
268 	case DATA_TYPE_INT64:
269 		nvl->nv_idx += xdr_int64(xdr, &nvp_data->nv_data[0],
270 		    (int64_t *)&nvp_data->nv_data[0]);
271 		break;
272 
273 	case DATA_TYPE_UINT64:
274 		nvl->nv_idx += xdr_uint64(xdr, &nvp_data->nv_data[0],
275 		    (uint64_t *)&nvp_data->nv_data[0]);
276 		break;
277 
278 	case DATA_TYPE_STRING:
279 		nv_string = (nv_string_t *)&nvp_data->nv_data[0];
280 		nvl->nv_idx += xdr_string(xdr, &nvp_data->nv_data[0],
281 		    nv_string);
282 
283 		break;
284 	}
285 }
286 
287 static void
288 nvlist_decode_nvlist(const xdr_t *xdr, nvlist_t *nvl)
289 {
290 	nvp_header_t *nvph;
291 	nvs_data_t *nvs = (nvs_data_t *)nvl->nv_data;
292 
293 	nvl->nv_idx = nvl->nv_data;
294 	nvl->nv_idx += xdr->xdr_getint(xdr, (const uint8_t *)&nvs->nvl_version,
295 	    &nvs->nvl_version);
296 	nvl->nv_idx += xdr->xdr_getint(xdr, (const uint8_t *)&nvs->nvl_nvflag,
297 	    &nvs->nvl_nvflag);
298 
299 	nvph = &nvs->nvl_pair;
300 	nvl->nv_idx += xdr->xdr_getint(xdr,
301 	    (const uint8_t *)&nvph->encoded_size, &nvph->encoded_size);
302 	nvl->nv_idx += xdr->xdr_getint(xdr,
303 	    (const uint8_t *)&nvph->decoded_size, &nvph->decoded_size);
304 
305 	while (nvph->encoded_size && nvph->decoded_size) {
306 		nvlist_nvp_decode(xdr, nvl, nvph);
307 
308 		nvph = (nvp_header_t *)(nvl->nv_idx);
309 		nvl->nv_idx += xdr->xdr_getint(xdr, &nvph->encoded_size,
310 		    &nvph->encoded_size);
311 		nvl->nv_idx += xdr->xdr_getint(xdr, &nvph->decoded_size,
312 		    &nvph->decoded_size);
313 	}
314 }
315 
316 static int
317 nvlist_size(const xdr_t *xdr, const uint8_t *stream)
318 {
319 	const uint8_t *p, *pair;
320 	unsigned encoded_size, decoded_size;
321 
322 	p = stream;
323 	p += 2 * sizeof(unsigned);
324 
325 	pair = p;
326 	p += xdr->xdr_getint(xdr, p, &encoded_size);
327 	p += xdr->xdr_getint(xdr, p, &decoded_size);
328 	while (encoded_size && decoded_size) {
329 		p = pair + encoded_size;
330 		pair = p;
331 		p += xdr->xdr_getint(xdr, p, &encoded_size);
332 		p += xdr->xdr_getint(xdr, p, &decoded_size);
333 	}
334 	return (p - stream);
335 }
336 
337 /*
338  * Import nvlist from byte stream.
339  * Determine the stream size and allocate private copy.
340  * Then translate the data.
341  */
342 nvlist_t *
343 nvlist_import(const uint8_t *stream, char encoding, char endian)
344 {
345 	nvlist_t *nvl;
346 
347 	if (encoding != NV_ENCODE_XDR)
348 		return (NULL);
349 
350 	nvl = malloc(sizeof(*nvl));
351 	if (nvl == NULL)
352 		return (nvl);
353 
354 	nvl->nv_asize = nvl->nv_size = nvlist_size(&ntoh, stream);
355 	nvl->nv_data = malloc(nvl->nv_asize);
356 	if (nvl->nv_data == NULL) {
357 		free(nvl);
358 		return (NULL);
359 	}
360 	nvl->nv_idx = nvl->nv_data;
361 	bcopy(stream, nvl->nv_data, nvl->nv_asize);
362 
363 	nvlist_decode_nvlist(&ntoh, nvl);
364 	nvl->nv_idx = nvl->nv_data;
365 	return (nvl);
366 }
367 
368 /*
369  * remove pair from this nvlist.
370  */
371 int
372 nvlist_remove(nvlist_t *nvl, const char *name, data_type_t type)
373 {
374 	uint8_t *head, *tail;
375 	nvs_data_t *data;
376 	nvp_header_t *nvp;
377 	nv_string_t *nvp_name;
378 	nv_pair_data_t *nvp_data;
379 	size_t size;
380 
381 	if (nvl == NULL || nvl->nv_data == NULL || name == NULL)
382 		return (EINVAL);
383 
384 	head = nvl->nv_data;
385 	data = (nvs_data_t *)head;
386 	nvp = &data->nvl_pair;	/* first pair in nvlist */
387 	head = (uint8_t *)nvp;
388 
389 	while (nvp->encoded_size != 0 && nvp->decoded_size != 0) {
390 		nvp_name = (nv_string_t *)(head + sizeof(*nvp));
391 
392 		nvp_data = (nv_pair_data_t *)
393 		    NV_ALIGN4((uintptr_t)&nvp_name->nv_data[0] +
394 		    nvp_name->nv_size);
395 
396 		if (memcmp(nvp_name->nv_data, name, nvp_name->nv_size) == 0 &&
397 		    nvp_data->nv_type == type) {
398 			/*
399 			 * set tail to point to next nvpair and size
400 			 * is the length of the tail.
401 			 */
402 			tail = head + nvp->encoded_size;
403 			size = nvl->nv_data + nvl->nv_size - tail;
404 
405 			/* adjust the size of the nvlist. */
406 			nvl->nv_size -= nvp->encoded_size;
407 			bcopy(tail, head, size);
408 			return (0);
409 		}
410 		/* Not our pair, skip to next. */
411 		head = head + nvp->encoded_size;
412 		nvp = (nvp_header_t *)head;
413 	}
414 	return (ENOENT);
415 }
416 
417 int
418 nvlist_find(const nvlist_t *nvl, const char *name, data_type_t type,
419     int *elementsp, void *valuep, int *sizep)
420 {
421 	nvs_data_t *data;
422 	nvp_header_t *nvp;
423 	nv_string_t *nvp_name;
424 	nv_pair_data_t *nvp_data;
425 	nvlist_t *nvlist;
426 
427 	if (nvl == NULL || nvl->nv_data == NULL || name == NULL)
428 		return (EINVAL);
429 
430 	data = (nvs_data_t *)nvl->nv_data;
431 	nvp = &data->nvl_pair;	/* first pair in nvlist */
432 
433 	while (nvp->encoded_size != 0 && nvp->decoded_size != 0) {
434 		nvp_name = (nv_string_t *)((uint8_t *)nvp + sizeof(*nvp));
435 
436 		nvp_data = (nv_pair_data_t *)
437 		    NV_ALIGN4((uintptr_t)&nvp_name->nv_data[0] +
438 		    nvp_name->nv_size);
439 
440 		if (memcmp(nvp_name->nv_data, name, nvp_name->nv_size) == 0 &&
441 		    nvp_data->nv_type == type) {
442 			if (elementsp != NULL)
443 				*elementsp = nvp_data->nv_nelem;
444 			switch (nvp_data->nv_type) {
445 			case DATA_TYPE_UINT64:
446 				*(uint64_t *)valuep =
447 				    *(uint64_t *)nvp_data->nv_data;
448 				return (0);
449 			case DATA_TYPE_STRING:
450 				nvp_name = (nv_string_t *)nvp_data->nv_data;
451 				if (sizep != NULL) {
452 					*sizep = nvp_name->nv_size;
453 				}
454 				*(const uint8_t **)valuep =
455 				    &nvp_name->nv_data[0];
456 				return (0);
457 			case DATA_TYPE_NVLIST:
458 			case DATA_TYPE_NVLIST_ARRAY:
459 				nvlist = malloc(sizeof(*nvlist));
460 				if (nvlist != NULL) {
461 					nvlist->nv_header = nvl->nv_header;
462 					nvlist->nv_asize = 0;
463 					nvlist->nv_size = 0;
464 					nvlist->nv_idx = NULL;
465 					nvlist->nv_data = &nvp_data->nv_data[0];
466 					*(nvlist_t **)valuep = nvlist;
467 					return (0);
468 				}
469 				return (ENOMEM);
470 			}
471 			return (EIO);
472 		}
473 		/* Not our pair, skip to next. */
474 		nvp = (nvp_header_t *)((uint8_t *)nvp + nvp->encoded_size);
475 	}
476 	return (ENOENT);
477 }
478 
479 /*
480  * Return the next nvlist in an nvlist array.
481  */
482 int
483 nvlist_next(nvlist_t *nvl)
484 {
485 	nvs_data_t *data;
486 	nvp_header_t *nvp;
487 
488 	if (nvl == NULL || nvl->nv_data == NULL || nvl->nv_asize != 0)
489 		return (EINVAL);
490 
491 	data = (nvs_data_t *)nvl->nv_data;
492 	nvp = &data->nvl_pair;	/* first pair in nvlist */
493 
494 	while (nvp->encoded_size != 0 && nvp->decoded_size != 0) {
495 		nvp = (nvp_header_t *)((uint8_t *)nvp + nvp->encoded_size);
496 	}
497 	nvl->nv_data = (uint8_t *)nvp + sizeof(*nvp);
498 	return (0);
499 }
500 
501 void
502 nvlist_print(nvlist_t *nvl, unsigned int indent)
503 {
504 	static const char *typenames[] = {
505 		"DATA_TYPE_UNKNOWN",
506 		"DATA_TYPE_BOOLEAN",
507 		"DATA_TYPE_BYTE",
508 		"DATA_TYPE_INT16",
509 		"DATA_TYPE_UINT16",
510 		"DATA_TYPE_INT32",
511 		"DATA_TYPE_UINT32",
512 		"DATA_TYPE_INT64",
513 		"DATA_TYPE_UINT64",
514 		"DATA_TYPE_STRING",
515 		"DATA_TYPE_BYTE_ARRAY",
516 		"DATA_TYPE_INT16_ARRAY",
517 		"DATA_TYPE_UINT16_ARRAY",
518 		"DATA_TYPE_INT32_ARRAY",
519 		"DATA_TYPE_UINT32_ARRAY",
520 		"DATA_TYPE_INT64_ARRAY",
521 		"DATA_TYPE_UINT64_ARRAY",
522 		"DATA_TYPE_STRING_ARRAY",
523 		"DATA_TYPE_HRTIME",
524 		"DATA_TYPE_NVLIST",
525 		"DATA_TYPE_NVLIST_ARRAY",
526 		"DATA_TYPE_BOOLEAN_VALUE",
527 		"DATA_TYPE_INT8",
528 		"DATA_TYPE_UINT8",
529 		"DATA_TYPE_BOOLEAN_ARRAY",
530 		"DATA_TYPE_INT8_ARRAY",
531 		"DATA_TYPE_UINT8_ARRAY"
532 	};
533 	nvs_data_t *data;
534 	nvp_header_t *nvp;
535 	nv_string_t *nvp_name;
536 	nv_pair_data_t *nvp_data;
537 	nvlist_t nvlist;
538 	int i, j;
539 
540 	data = (nvs_data_t *)nvl->nv_data;
541 	nvp = &data->nvl_pair;  /* first pair in nvlist */
542 	while (nvp->encoded_size != 0 && nvp->decoded_size != 0) {
543 		nvp_name = (nv_string_t *)((uintptr_t)nvp + sizeof(*nvp));
544 		nvp_data = (nv_pair_data_t *)
545 		    NV_ALIGN4((uintptr_t)&nvp_name->nv_data[0] +
546 		    nvp_name->nv_size);
547 
548 		for (int i = 0; i < indent; i++)
549 			printf(" ");
550 
551 		printf("%s [%d] %.*s", typenames[nvp_data->nv_type],
552 		    nvp_data->nv_nelem, nvp_name->nv_size, nvp_name->nv_data);
553 
554 		switch (nvp_data->nv_type) {
555 		case DATA_TYPE_UINT64: {
556 			uint64_t val;
557 
558 			val = *(uint64_t *)nvp_data->nv_data;
559 			printf(" = 0x%jx\n", (uintmax_t)val);
560 			break;
561 		}
562 
563 		case DATA_TYPE_STRING: {
564 			nvp_name = (nv_string_t *)&nvp_data->nv_data[0];
565 			printf(" = \"%.*s\"\n", nvp_name->nv_size,
566 			    nvp_name->nv_data );
567 			break;
568 		}
569 
570 		case DATA_TYPE_NVLIST:
571 			printf("\n");
572 			nvlist.nv_data = &nvp_data->nv_data[0];
573 			nvlist_print(&nvlist, indent + 2);
574 			break;
575 
576 		case DATA_TYPE_NVLIST_ARRAY:
577 			nvlist.nv_data = &nvp_data->nv_data[0];
578 			for (j = 0; j < nvp_data->nv_nelem; j++) {
579 				data = (nvs_data_t *)nvlist.nv_data;
580 				printf("[%d]\n", j);
581 				nvlist_print(&nvlist, indent + 2);
582 				if (j != nvp_data->nv_nelem - 1) {
583 					for (i = 0; i < indent; i++)
584 						printf(" ");
585 					printf("%s %.*s",
586 					    typenames[nvp_data->nv_type],
587 					    nvp_name->nv_size,
588 					    nvp_name->nv_data);
589 				}
590 				nvlist.nv_data = (uint8_t *)data +
591 				    nvlist_size(&native, nvlist.nv_data);
592 			}
593 			break;
594 
595 		default:
596 			printf("\n");
597 		}
598 		nvp = (nvp_header_t *)((uint8_t *)nvp + nvp->encoded_size);
599 	}
600 	printf("%*s\n", indent + 13, "End of nvlist");
601 }
602