1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
|
// SPDX-License-Identifier: CDDL-1.0
/*
* 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);
}
zfs_fallthrough;
case lzbe_replace:
nv = fnvlist_alloc();
break;
default:
return (rv);
}
/* version is mandatory */
fnvlist_add_uint64(nv, BOOTENV_VERSION, VB_NVLIST);
rv = 0;
/*
* 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 suffix.
*/
if (strncmp(device, "zfs:", 4) == 0) {
fnvlist_add_string(nv, OS_BOOTONCE, device);
} else {
if (asprintf(&descriptor, "zfs:%s:", device) > 0) {
fnvlist_add_string(nv, OS_BOOTONCE, descriptor);
free(descriptor);
} else
rv = ENOMEM;
}
}
if (rv == 0)
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;
const 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) {
char *tmp = strdup(val + 4);
if (tmp != NULL) {
size_t len = strlen(tmp);
if (tmp[len - 1] == ':')
tmp[len - 1] = '\0';
*device = tmp;
} else {
rv = ENOMEM;
}
} else {
rv = EINVAL;
}
}
nvlist_free(nv);
}
zpool_close(zphdl);
libzfs_fini(hdl);
return (rv);
}
|