zfs/lib/libzfsbootenv/lzbe_device.c

165 lines
3.5 KiB
C

/*
* 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);
}