]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - stand/libsa/zfs/nvlist.c
MFV r362565:
[FreeBSD/FreeBSD.git] / stand / libsa / zfs / nvlist.c
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 }