| /* |
| * This file and its contents are supplied under the terms of the |
| * Common Development and Distribution License ("CDDL"), version 1.0. |
| * You may only use this file in accordance with the terms of version |
| * 1.0 of the CDDL. |
| * |
| * A full copy of the text of the CDDL should have accompanied this |
| * source. A copy of the CDDL is also available via the Internet at |
| * http://www.illumos.org/license/CDDL. |
| */ |
| /* |
| * Copyright 2020 Toomas Soome <tsoome@me.com> |
| */ |
| |
| #include <sys/types.h> |
| #include <string.h> |
| #include <libzfs.h> |
| #include <libzfsbootenv.h> |
| #include <sys/zfs_bootenv.h> |
| #include <sys/vdev_impl.h> |
| |
| /* |
| * Get or create nvlist. If key is not NULL, get nvlist from bootenv, |
| * otherwise return bootenv. |
| */ |
| int |
| lzbe_nvlist_get(const char *pool, const char *key, void **ptr) |
| { |
| libzfs_handle_t *hdl; |
| zpool_handle_t *zphdl; |
| nvlist_t *nv; |
| int rv = -1; |
| |
| if (pool == NULL || *pool == '\0') |
| return (rv); |
| |
| if ((hdl = libzfs_init()) == NULL) { |
| return (rv); |
| } |
| |
| zphdl = zpool_open(hdl, pool); |
| if (zphdl == NULL) { |
| libzfs_fini(hdl); |
| return (rv); |
| } |
| |
| rv = zpool_get_bootenv(zphdl, &nv); |
| if (rv == 0) { |
| nvlist_t *nvl, *dup; |
| |
| if (key != NULL) { |
| rv = nvlist_lookup_nvlist(nv, key, &nvl); |
| if (rv == 0) { |
| rv = nvlist_dup(nvl, &dup, 0); |
| nvlist_free(nv); |
| if (rv == 0) |
| nv = dup; |
| else |
| nv = NULL; |
| } else { |
| nvlist_free(nv); |
| rv = nvlist_alloc(&nv, NV_UNIQUE_NAME, 0); |
| } |
| } |
| *ptr = nv; |
| } |
| |
| zpool_close(zphdl); |
| libzfs_fini(hdl); |
| return (rv); |
| } |
| |
| int |
| lzbe_nvlist_set(const char *pool, const char *key, void *ptr) |
| { |
| libzfs_handle_t *hdl; |
| zpool_handle_t *zphdl; |
| nvlist_t *nv; |
| uint64_t version; |
| int rv = -1; |
| |
| if (pool == NULL || *pool == '\0') |
| return (rv); |
| |
| if ((hdl = libzfs_init()) == NULL) { |
| return (rv); |
| } |
| |
| zphdl = zpool_open(hdl, pool); |
| if (zphdl == NULL) { |
| libzfs_fini(hdl); |
| return (rv); |
| } |
| |
| if (key != NULL) { |
| rv = zpool_get_bootenv(zphdl, &nv); |
| if (rv == 0) { |
| /* |
| * We got the nvlist, check for version. |
| * if version is missing or is not VB_NVLIST, |
| * create new list. |
| */ |
| rv = nvlist_lookup_uint64(nv, BOOTENV_VERSION, |
| &version); |
| if (rv != 0 || version != VB_NVLIST) { |
| /* Drop this nvlist */ |
| fnvlist_free(nv); |
| /* Create and prepare new nvlist */ |
| nv = fnvlist_alloc(); |
| fnvlist_add_uint64(nv, BOOTENV_VERSION, |
| VB_NVLIST); |
| } |
| rv = nvlist_add_nvlist(nv, key, ptr); |
| if (rv == 0) |
| rv = zpool_set_bootenv(zphdl, nv); |
| nvlist_free(nv); |
| } |
| } else { |
| rv = zpool_set_bootenv(zphdl, ptr); |
| } |
| |
| zpool_close(zphdl); |
| libzfs_fini(hdl); |
| return (rv); |
| } |
| |
| /* |
| * free nvlist we got via lzbe_nvlist_get() |
| */ |
| void |
| lzbe_nvlist_free(void *ptr) |
| { |
| nvlist_free(ptr); |
| } |
| |
| static const char *typenames[] = { |
| "DATA_TYPE_UNKNOWN", |
| "DATA_TYPE_BOOLEAN", |
| "DATA_TYPE_BYTE", |
| "DATA_TYPE_INT16", |
| "DATA_TYPE_UINT16", |
| "DATA_TYPE_INT32", |
| "DATA_TYPE_UINT32", |
| "DATA_TYPE_INT64", |
| "DATA_TYPE_UINT64", |
| "DATA_TYPE_STRING", |
| "DATA_TYPE_BYTE_ARRAY", |
| "DATA_TYPE_INT16_ARRAY", |
| "DATA_TYPE_UINT16_ARRAY", |
| "DATA_TYPE_INT32_ARRAY", |
| "DATA_TYPE_UINT32_ARRAY", |
| "DATA_TYPE_INT64_ARRAY", |
| "DATA_TYPE_UINT64_ARRAY", |
| "DATA_TYPE_STRING_ARRAY", |
| "DATA_TYPE_HRTIME", |
| "DATA_TYPE_NVLIST", |
| "DATA_TYPE_NVLIST_ARRAY", |
| "DATA_TYPE_BOOLEAN_VALUE", |
| "DATA_TYPE_INT8", |
| "DATA_TYPE_UINT8", |
| "DATA_TYPE_BOOLEAN_ARRAY", |
| "DATA_TYPE_INT8_ARRAY", |
| "DATA_TYPE_UINT8_ARRAY" |
| }; |
| |
| static int |
| nvpair_type_from_name(const char *name) |
| { |
| unsigned i; |
| |
| for (i = 0; i < ARRAY_SIZE(typenames); i++) { |
| if (strcmp(name, typenames[i]) == 0) |
| return (i); |
| } |
| return (0); |
| } |
| |
| /* |
| * Add pair defined by key, type and value into nvlist. |
| */ |
| int |
| lzbe_add_pair(void *ptr, const char *key, const char *type, void *value, |
| size_t size) |
| { |
| nvlist_t *nv = ptr; |
| data_type_t dt; |
| int rv = 0; |
| |
| if (ptr == NULL || key == NULL || value == NULL) |
| return (rv); |
| |
| if (type == NULL) |
| type = "DATA_TYPE_STRING"; |
| dt = nvpair_type_from_name(type); |
| if (dt == DATA_TYPE_UNKNOWN) |
| return (EINVAL); |
| |
| switch (dt) { |
| case DATA_TYPE_BYTE: |
| if (size != sizeof (uint8_t)) { |
| rv = EINVAL; |
| break; |
| } |
| rv = nvlist_add_byte(nv, key, *(uint8_t *)value); |
| break; |
| |
| case DATA_TYPE_INT16: |
| if (size != sizeof (int16_t)) { |
| rv = EINVAL; |
| break; |
| } |
| rv = nvlist_add_int16(nv, key, *(int16_t *)value); |
| break; |
| |
| case DATA_TYPE_UINT16: |
| if (size != sizeof (uint16_t)) { |
| rv = EINVAL; |
| break; |
| } |
| rv = nvlist_add_uint16(nv, key, *(uint16_t *)value); |
| break; |
| |
| case DATA_TYPE_INT32: |
| if (size != sizeof (int32_t)) { |
| rv = EINVAL; |
| break; |
| } |
| rv = nvlist_add_int32(nv, key, *(int32_t *)value); |
| break; |
| |
| case DATA_TYPE_UINT32: |
| if (size != sizeof (uint32_t)) { |
| rv = EINVAL; |
| break; |
| } |
| rv = nvlist_add_uint32(nv, key, *(uint32_t *)value); |
| break; |
| |
| case DATA_TYPE_INT64: |
| if (size != sizeof (int64_t)) { |
| rv = EINVAL; |
| break; |
| } |
| rv = nvlist_add_int64(nv, key, *(int64_t *)value); |
| break; |
| |
| case DATA_TYPE_UINT64: |
| if (size != sizeof (uint64_t)) { |
| rv = EINVAL; |
| break; |
| } |
| rv = nvlist_add_uint64(nv, key, *(uint64_t *)value); |
| break; |
| |
| case DATA_TYPE_STRING: |
| rv = nvlist_add_string(nv, key, value); |
| break; |
| |
| case DATA_TYPE_BYTE_ARRAY: |
| rv = nvlist_add_byte_array(nv, key, value, size); |
| break; |
| |
| case DATA_TYPE_INT16_ARRAY: |
| rv = nvlist_add_int16_array(nv, key, value, size); |
| break; |
| |
| case DATA_TYPE_UINT16_ARRAY: |
| rv = nvlist_add_uint16_array(nv, key, value, size); |
| break; |
| |
| case DATA_TYPE_INT32_ARRAY: |
| rv = nvlist_add_int32_array(nv, key, value, size); |
| break; |
| |
| case DATA_TYPE_UINT32_ARRAY: |
| rv = nvlist_add_uint32_array(nv, key, value, size); |
| break; |
| |
| case DATA_TYPE_INT64_ARRAY: |
| rv = nvlist_add_int64_array(nv, key, value, size); |
| break; |
| |
| case DATA_TYPE_UINT64_ARRAY: |
| rv = nvlist_add_uint64_array(nv, key, value, size); |
| break; |
| |
| case DATA_TYPE_STRING_ARRAY: |
| rv = nvlist_add_string_array(nv, key, value, size); |
| break; |
| |
| case DATA_TYPE_NVLIST: |
| rv = nvlist_add_nvlist(nv, key, (nvlist_t *)value); |
| break; |
| |
| case DATA_TYPE_NVLIST_ARRAY: |
| rv = nvlist_add_nvlist_array(nv, key, value, size); |
| break; |
| |
| case DATA_TYPE_BOOLEAN_VALUE: |
| if (size != sizeof (boolean_t)) { |
| rv = EINVAL; |
| break; |
| } |
| rv = nvlist_add_boolean_value(nv, key, *(boolean_t *)value); |
| break; |
| |
| case DATA_TYPE_INT8: |
| if (size != sizeof (int8_t)) { |
| rv = EINVAL; |
| break; |
| } |
| rv = nvlist_add_int8(nv, key, *(int8_t *)value); |
| break; |
| |
| case DATA_TYPE_UINT8: |
| if (size != sizeof (uint8_t)) { |
| rv = EINVAL; |
| break; |
| } |
| rv = nvlist_add_uint8(nv, key, *(uint8_t *)value); |
| break; |
| |
| case DATA_TYPE_BOOLEAN_ARRAY: |
| rv = nvlist_add_boolean_array(nv, key, value, size); |
| break; |
| |
| case DATA_TYPE_INT8_ARRAY: |
| rv = nvlist_add_int8_array(nv, key, value, size); |
| break; |
| |
| case DATA_TYPE_UINT8_ARRAY: |
| rv = nvlist_add_uint8_array(nv, key, value, size); |
| break; |
| |
| default: |
| return (ENOTSUP); |
| } |
| |
| return (rv); |
| } |
| |
| int |
| lzbe_remove_pair(void *ptr, const char *key) |
| { |
| |
| return (nvlist_remove_all(ptr, key)); |
| } |