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