diff options
author | Toomas Soome <[email protected]> | 2020-09-16 01:42:27 +0300 |
---|---|---|
committer | GitHub <[email protected]> | 2020-09-15 15:42:27 -0700 |
commit | 1db9e6e4e4c355a89e52f156f63843d01f84866e (patch) | |
tree | 1bfd632036c2caf3a2c6e0089635073ae6be5bde /lib | |
parent | 37325e47499ccdb4eeabfaa61d06a4075390b960 (diff) |
zfs label bootenv should store data as nvlist
nvlist does allow us to support different data types and systems.
To encapsulate user data to/from nvlist, the libzfsbootenv library is
provided.
Reviewed-by: Arvind Sankar <[email protected]>
Reviewed-by: Allan Jude <[email protected]>
Reviewed-by: Paul Dagnelie <[email protected]>
Reviewed-by: Igor Kozhukhov <[email protected]>
Signed-off-by: Toomas Soome <[email protected]>
Closes #10774
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Makefile.am | 4 | ||||
-rw-r--r-- | lib/libzfs/libzfs_pool.c | 24 | ||||
-rw-r--r-- | lib/libzfs_core/libzfs_core.c | 8 | ||||
-rw-r--r-- | lib/libzfsbootenv/.gitignore | 1 | ||||
-rw-r--r-- | lib/libzfsbootenv/Makefile.am | 32 | ||||
-rw-r--r-- | lib/libzfsbootenv/libzfsbootenv.pc.in | 12 | ||||
-rw-r--r-- | lib/libzfsbootenv/lzbe_device.c | 164 | ||||
-rw-r--r-- | lib/libzfsbootenv/lzbe_pair.c | 329 | ||||
-rw-r--r-- | lib/libzfsbootenv/lzbe_util.c | 39 | ||||
-rw-r--r-- | lib/libzpool/Makefile.am | 7 |
10 files changed, 598 insertions, 22 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am index 93ee30697..f049288a1 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -13,6 +13,6 @@ SUBDIRS += libnvpair # libzutil depends on libefi if present SUBDIRS += libzutil libunicode -# These four libraries, which are installed as the final build product, +# These five libraries, which are installed as the final build product, # incorporate the eight convenience libraries given above. -SUBDIRS += libuutil libzfs_core libzfs libzpool +SUBDIRS += libuutil libzfs_core libzfs libzpool libzfsbootenv diff --git a/lib/libzfs/libzfs_pool.c b/lib/libzfs/libzfs_pool.c index 2501965e4..00b0b6faf 100644 --- a/lib/libzfs/libzfs_pool.c +++ b/lib/libzfs/libzfs_pool.c @@ -4495,7 +4495,7 @@ zpool_wait_status(zpool_handle_t *zhp, zpool_wait_activity_t activity, } int -zpool_set_bootenv(zpool_handle_t *zhp, const char *envmap) +zpool_set_bootenv(zpool_handle_t *zhp, const nvlist_t *envmap) { int error = lzc_set_bootenv(zhp->zpool_name, envmap); if (error != 0) { @@ -4508,24 +4508,20 @@ zpool_set_bootenv(zpool_handle_t *zhp, const char *envmap) } int -zpool_get_bootenv(zpool_handle_t *zhp, char *outbuf, size_t size, off_t offset) +zpool_get_bootenv(zpool_handle_t *zhp, nvlist_t **nvlp) { - nvlist_t *nvl = NULL; - int error = lzc_get_bootenv(zhp->zpool_name, &nvl); + nvlist_t *nvl; + int error; + + nvl = NULL; + error = lzc_get_bootenv(zhp->zpool_name, &nvl); if (error != 0) { (void) zpool_standard_error_fmt(zhp->zpool_hdl, error, dgettext(TEXT_DOMAIN, "error getting bootenv in pool '%s'"), zhp->zpool_name); - return (-1); - } - char *envmap = fnvlist_lookup_string(nvl, "envmap"); - if (offset >= strlen(envmap)) { - fnvlist_free(nvl); - return (0); + } else { + *nvlp = nvl; } - strncpy(outbuf, envmap + offset, size); - int bytes = MIN(strlen(envmap + offset), size); - fnvlist_free(nvl); - return (bytes); + return (error); } diff --git a/lib/libzfs_core/libzfs_core.c b/lib/libzfs_core/libzfs_core.c index 50b1b9dec..a3ba3b284 100644 --- a/lib/libzfs_core/libzfs_core.c +++ b/lib/libzfs_core/libzfs_core.c @@ -1625,13 +1625,9 @@ lzc_wait_fs(const char *fs, zfs_wait_activity_t activity, boolean_t *waited) * Set the bootenv contents for the given pool. */ int -lzc_set_bootenv(const char *pool, const char *env) +lzc_set_bootenv(const char *pool, const nvlist_t *env) { - nvlist_t *args = fnvlist_alloc(); - fnvlist_add_string(args, "envmap", env); - int error = lzc_ioctl(ZFS_IOC_SET_BOOTENV, pool, args, NULL); - fnvlist_free(args); - return (error); + return (lzc_ioctl(ZFS_IOC_SET_BOOTENV, pool, (nvlist_t *)env, NULL)); } /* diff --git a/lib/libzfsbootenv/.gitignore b/lib/libzfsbootenv/.gitignore new file mode 100644 index 000000000..3fea5c642 --- /dev/null +++ b/lib/libzfsbootenv/.gitignore @@ -0,0 +1 @@ +/libzfsbootenv.pc diff --git a/lib/libzfsbootenv/Makefile.am b/lib/libzfsbootenv/Makefile.am new file mode 100644 index 000000000..6b9a8f013 --- /dev/null +++ b/lib/libzfsbootenv/Makefile.am @@ -0,0 +1,32 @@ +include $(top_srcdir)/config/Rules.am + +pkgconfig_DATA = libzfsbootenv.pc + +lib_LTLIBRARIES = libzfsbootenv.la + +if BUILD_FREEBSD +DEFAULT_INCLUDES += -I$(top_srcdir)/include/os/freebsd/zfs +endif +if BUILD_LINUX +DEFAULT_INCLUDES += -I$(top_srcdir)/include/os/linux/zfs +endif + +USER_C = \ + lzbe_device.c \ + lzbe_pair.c \ + lzbe_util.c + +dist_libzfsbootenv_la_SOURCES = \ + $(USER_C) + +libzfsbootenv_la_LIBADD = \ + $(abs_top_builddir)/lib/libzfs/libzfs.la \ + $(abs_top_builddir)/lib/libnvpair/libnvpair.la + +libzfsbootenv_la_LDFLAGS = + +if !ASAN_ENABLED +libzfsbootenv_la_LDFLAGS += -Wl,-z,defs +endif + +libzfsbootenv_la_LDFLAGS += -version-info 1:0:0 diff --git a/lib/libzfsbootenv/libzfsbootenv.pc.in b/lib/libzfsbootenv/libzfsbootenv.pc.in new file mode 100644 index 000000000..61bafa66e --- /dev/null +++ b/lib/libzfsbootenv/libzfsbootenv.pc.in @@ -0,0 +1,12 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libzfsbootenv +Description: LibZFSBootENV library +Version: @VERSION@ +URL: https://zfsonlinux.org +Requires: libzfs libnvpair +Cflags: -I${includedir} +Libs: -L${libdir} -lzfsbootenv diff --git a/lib/libzfsbootenv/lzbe_device.c b/lib/libzfsbootenv/lzbe_device.c new file mode 100644 index 000000000..670efd8b0 --- /dev/null +++ b/lib/libzfsbootenv/lzbe_device.c @@ -0,0 +1,164 @@ +/* + * 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 <[email protected]> + */ + +#include <sys/types.h> +#include <string.h> +#include <libzfs.h> +#include <libzfsbootenv.h> +#include <sys/zfs_bootenv.h> +#include <sys/vdev_impl.h> + +/* + * Store device name to zpool label bootenv area. + * This call will set bootenv version to VB_NVLIST, if bootenv currently + * does contain other version, then old data will be replaced. + */ +int +lzbe_set_boot_device(const char *pool, lzbe_flags_t flag, const char *device) +{ + libzfs_handle_t *hdl; + zpool_handle_t *zphdl; + nvlist_t *nv; + char *descriptor; + 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); + } + + switch (flag) { + case lzbe_add: + 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) + break; + + /* Drop this nvlist */ + fnvlist_free(nv); + } + /* FALLTHROUGH */ + case lzbe_replace: + nv = fnvlist_alloc(); + break; + default: + return (rv); + } + + /* version is mandatory */ + fnvlist_add_uint64(nv, BOOTENV_VERSION, VB_NVLIST); + + /* + * If device name is empty, remove boot device configuration. + */ + if ((device == NULL || *device == '\0')) { + if (nvlist_exists(nv, OS_BOOTONCE)) + fnvlist_remove(nv, OS_BOOTONCE); + } else { + /* + * Use device name directly if it does start with + * prefix "zfs:". Otherwise, add prefix and sufix. + */ + if (strncmp(device, "zfs:", 4) == 0) { + fnvlist_add_string(nv, OS_BOOTONCE, device); + } else { + descriptor = NULL; + if (asprintf(&descriptor, "zfs:%s:", device) > 0) + fnvlist_add_string(nv, OS_BOOTONCE, descriptor); + else + rv = ENOMEM; + free(descriptor); + } + } + + rv = zpool_set_bootenv(zphdl, nv); + if (rv != 0) + fprintf(stderr, "%s\n", libzfs_error_description(hdl)); + + fnvlist_free(nv); + zpool_close(zphdl); + libzfs_fini(hdl); + return (rv); +} + +/* + * Return boot device name from bootenv, if set. + */ +int +lzbe_get_boot_device(const char *pool, char **device) +{ + libzfs_handle_t *hdl; + zpool_handle_t *zphdl; + nvlist_t *nv; + char *val; + int rv = -1; + + if (pool == NULL || *pool == '\0' || device == NULL) + 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) { + rv = nvlist_lookup_string(nv, OS_BOOTONCE, &val); + if (rv == 0) { + /* + * zfs device descriptor is in form of "zfs:dataset:", + * we only do need dataset name. + */ + if (strncmp(val, "zfs:", 4) == 0) { + val += 4; + val = strdup(val); + if (val != NULL) { + size_t len = strlen(val); + + if (val[len - 1] == ':') + val[len - 1] = '\0'; + *device = val; + } else { + rv = ENOMEM; + } + } else { + rv = EINVAL; + } + } + nvlist_free(nv); + } + + zpool_close(zphdl); + libzfs_fini(hdl); + return (rv); +} diff --git a/lib/libzfsbootenv/lzbe_pair.c b/lib/libzfsbootenv/lzbe_pair.c new file mode 100644 index 000000000..e702a8585 --- /dev/null +++ b/lib/libzfsbootenv/lzbe_pair.c @@ -0,0 +1,329 @@ +/* + * 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 <[email protected]> + */ + +#include <sys/types.h> +#include <string.h> +#include <libzfs.h> +#include <libzfsbootenv.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; + 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) { + 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)); +} diff --git a/lib/libzfsbootenv/lzbe_util.c b/lib/libzfsbootenv/lzbe_util.c new file mode 100644 index 000000000..35e985495 --- /dev/null +++ b/lib/libzfsbootenv/lzbe_util.c @@ -0,0 +1,39 @@ +/* + * 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 <[email protected]> + */ + +#include <sys/types.h> +#include <string.h> +#include <libzfs.h> +#include <libzfsbootenv.h> + +/* + * Output bootenv information. + */ +int +lzbe_bootenv_print(const char *pool, const char *nvlist, FILE *of) +{ + nvlist_t *nv; + int rv = -1; + + if (pool == NULL || *pool == '\0' || of == NULL) + return (rv); + + rv = lzbe_nvlist_get(pool, nvlist, (void **)&nv); + if (rv == 0) { + nvlist_print(of, nv); + nvlist_free(nv); + } + + return (rv); +} diff --git a/lib/libzpool/Makefile.am b/lib/libzpool/Makefile.am index 9c1b81bf5..992c21cc1 100644 --- a/lib/libzpool/Makefile.am +++ b/lib/libzpool/Makefile.am @@ -7,6 +7,13 @@ VPATH = \ $(top_srcdir)/module/os/linux/zfs \ $(top_srcdir)/lib/libzpool +if BUILD_FREEBSD +DEFAULT_INCLUDES += -I$(top_srcdir)/include/os/freebsd/zfs +endif +if BUILD_LINUX +DEFAULT_INCLUDES += -I$(top_srcdir)/include/os/linux/zfs +endif + # Unconditionally enable debugging for libzpool AM_CPPFLAGS += -DDEBUG -UNDEBUG -DZFS_DEBUG |