diff options
Diffstat (limited to 'usr/src/lib/libzfsbootenv/common/lzbe_device.c')
-rw-r--r-- | usr/src/lib/libzfsbootenv/common/lzbe_device.c | 164 |
1 files changed, 164 insertions, 0 deletions
diff --git a/usr/src/lib/libzfsbootenv/common/lzbe_device.c b/usr/src/lib/libzfsbootenv/common/lzbe_device.c new file mode 100644 index 0000000000..670efd8b06 --- /dev/null +++ b/usr/src/lib/libzfsbootenv/common/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 <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> + +/* + * 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); +} |