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 <nivedita@alum.mit.edu>
Reviewed-by: Allan Jude <allan@klarasystems.com>
Reviewed-by: Paul Dagnelie <pcd@delphix.com>
Reviewed-by: Igor Kozhukhov <igor@dilos.org>
Signed-off-by: Toomas Soome <tsoome@me.com>
Closes #10774
This commit is contained in:
Toomas Soome
2020-09-16 01:42:27 +03:00
committed by GitHub
parent 37325e4749
commit 1db9e6e4e4
29 changed files with 886 additions and 68 deletions
+1
View File
@@ -0,0 +1 @@
/libzfsbootenv.pc
+32
View File
@@ -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
+12
View File
@@ -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
+164
View File
@@ -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);
}
+329
View File
@@ -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 <tsoome@me.com>
*/
#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));
}
+39
View File
@@ -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 <tsoome@me.com>
*/
#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);
}