12c48331dSMatt Macy /*
22c48331dSMatt Macy * This file and its contents are supplied under the terms of the
32c48331dSMatt Macy * Common Development and Distribution License ("CDDL"), version 1.0.
42c48331dSMatt Macy * You may only use this file in accordance with the terms of version
52c48331dSMatt Macy * 1.0 of the CDDL.
62c48331dSMatt Macy *
72c48331dSMatt Macy * A full copy of the text of the CDDL should have accompanied this
82c48331dSMatt Macy * source. A copy of the CDDL is also available via the Internet at
92c48331dSMatt Macy * http://www.illumos.org/license/CDDL.
102c48331dSMatt Macy */
112c48331dSMatt Macy /*
122c48331dSMatt Macy * Copyright 2020 Toomas Soome <tsoome@me.com>
132c48331dSMatt Macy */
142c48331dSMatt Macy
152c48331dSMatt Macy #include <sys/types.h>
162c48331dSMatt Macy #include <string.h>
172c48331dSMatt Macy #include <libzfs.h>
182c48331dSMatt Macy #include <libzfsbootenv.h>
192c48331dSMatt Macy #include <sys/zfs_bootenv.h>
202c48331dSMatt Macy #include <sys/vdev_impl.h>
212c48331dSMatt Macy
222c48331dSMatt Macy /*
232c48331dSMatt Macy * Store device name to zpool label bootenv area.
242c48331dSMatt Macy * This call will set bootenv version to VB_NVLIST, if bootenv currently
252c48331dSMatt Macy * does contain other version, then old data will be replaced.
262c48331dSMatt Macy */
272c48331dSMatt Macy int
lzbe_set_boot_device(const char * pool,lzbe_flags_t flag,const char * device)282c48331dSMatt Macy lzbe_set_boot_device(const char *pool, lzbe_flags_t flag, const char *device)
292c48331dSMatt Macy {
302c48331dSMatt Macy libzfs_handle_t *hdl;
312c48331dSMatt Macy zpool_handle_t *zphdl;
322c48331dSMatt Macy nvlist_t *nv;
332c48331dSMatt Macy char *descriptor;
342c48331dSMatt Macy uint64_t version;
352c48331dSMatt Macy int rv = -1;
362c48331dSMatt Macy
372c48331dSMatt Macy if (pool == NULL || *pool == '\0')
382c48331dSMatt Macy return (rv);
392c48331dSMatt Macy
402c48331dSMatt Macy if ((hdl = libzfs_init()) == NULL)
412c48331dSMatt Macy return (rv);
422c48331dSMatt Macy
432c48331dSMatt Macy zphdl = zpool_open(hdl, pool);
442c48331dSMatt Macy if (zphdl == NULL) {
452c48331dSMatt Macy libzfs_fini(hdl);
462c48331dSMatt Macy return (rv);
472c48331dSMatt Macy }
482c48331dSMatt Macy
492c48331dSMatt Macy switch (flag) {
502c48331dSMatt Macy case lzbe_add:
512c48331dSMatt Macy rv = zpool_get_bootenv(zphdl, &nv);
522c48331dSMatt Macy if (rv == 0) {
532c48331dSMatt Macy /*
542c48331dSMatt Macy * We got the nvlist, check for version.
552c48331dSMatt Macy * if version is missing or is not VB_NVLIST,
562c48331dSMatt Macy * create new list.
572c48331dSMatt Macy */
582c48331dSMatt Macy rv = nvlist_lookup_uint64(nv, BOOTENV_VERSION,
592c48331dSMatt Macy &version);
602c48331dSMatt Macy if (rv == 0 && version == VB_NVLIST)
612c48331dSMatt Macy break;
622c48331dSMatt Macy
632c48331dSMatt Macy /* Drop this nvlist */
642c48331dSMatt Macy fnvlist_free(nv);
652c48331dSMatt Macy }
66c03c5b1cSMartin Matuska zfs_fallthrough;
672c48331dSMatt Macy case lzbe_replace:
682c48331dSMatt Macy nv = fnvlist_alloc();
692c48331dSMatt Macy break;
702c48331dSMatt Macy default:
712c48331dSMatt Macy return (rv);
722c48331dSMatt Macy }
732c48331dSMatt Macy
742c48331dSMatt Macy /* version is mandatory */
752c48331dSMatt Macy fnvlist_add_uint64(nv, BOOTENV_VERSION, VB_NVLIST);
762c48331dSMatt Macy
77be181ee2SMartin Matuska rv = 0;
782c48331dSMatt Macy /*
792c48331dSMatt Macy * If device name is empty, remove boot device configuration.
802c48331dSMatt Macy */
812c48331dSMatt Macy if ((device == NULL || *device == '\0')) {
822c48331dSMatt Macy if (nvlist_exists(nv, OS_BOOTONCE))
832c48331dSMatt Macy fnvlist_remove(nv, OS_BOOTONCE);
842c48331dSMatt Macy } else {
852c48331dSMatt Macy /*
862c48331dSMatt Macy * Use device name directly if it does start with
8716038816SMartin Matuska * prefix "zfs:". Otherwise, add prefix and suffix.
882c48331dSMatt Macy */
892c48331dSMatt Macy if (strncmp(device, "zfs:", 4) == 0) {
902c48331dSMatt Macy fnvlist_add_string(nv, OS_BOOTONCE, device);
912c48331dSMatt Macy } else {
9216038816SMartin Matuska if (asprintf(&descriptor, "zfs:%s:", device) > 0) {
932c48331dSMatt Macy fnvlist_add_string(nv, OS_BOOTONCE, descriptor);
942c48331dSMatt Macy free(descriptor);
9516038816SMartin Matuska } else
9616038816SMartin Matuska rv = ENOMEM;
972c48331dSMatt Macy }
982c48331dSMatt Macy }
99be181ee2SMartin Matuska if (rv == 0)
1002c48331dSMatt Macy rv = zpool_set_bootenv(zphdl, nv);
1012c48331dSMatt Macy if (rv != 0)
1022c48331dSMatt Macy fprintf(stderr, "%s\n", libzfs_error_description(hdl));
1032c48331dSMatt Macy
1042c48331dSMatt Macy fnvlist_free(nv);
1052c48331dSMatt Macy zpool_close(zphdl);
1062c48331dSMatt Macy libzfs_fini(hdl);
1072c48331dSMatt Macy return (rv);
1082c48331dSMatt Macy }
1092c48331dSMatt Macy
1102c48331dSMatt Macy /*
1112c48331dSMatt Macy * Return boot device name from bootenv, if set.
1122c48331dSMatt Macy */
1132c48331dSMatt Macy int
lzbe_get_boot_device(const char * pool,char ** device)1142c48331dSMatt Macy lzbe_get_boot_device(const char *pool, char **device)
1152c48331dSMatt Macy {
1162c48331dSMatt Macy libzfs_handle_t *hdl;
1172c48331dSMatt Macy zpool_handle_t *zphdl;
1182c48331dSMatt Macy nvlist_t *nv;
119*2a58b312SMartin Matuska const char *val;
1202c48331dSMatt Macy int rv = -1;
1212c48331dSMatt Macy
1222c48331dSMatt Macy if (pool == NULL || *pool == '\0' || device == NULL)
1232c48331dSMatt Macy return (rv);
1242c48331dSMatt Macy
1252c48331dSMatt Macy if ((hdl = libzfs_init()) == NULL)
1262c48331dSMatt Macy return (rv);
1272c48331dSMatt Macy
1282c48331dSMatt Macy zphdl = zpool_open(hdl, pool);
1292c48331dSMatt Macy if (zphdl == NULL) {
1302c48331dSMatt Macy libzfs_fini(hdl);
1312c48331dSMatt Macy return (rv);
1322c48331dSMatt Macy }
1332c48331dSMatt Macy
1342c48331dSMatt Macy rv = zpool_get_bootenv(zphdl, &nv);
1352c48331dSMatt Macy if (rv == 0) {
1362c48331dSMatt Macy rv = nvlist_lookup_string(nv, OS_BOOTONCE, &val);
1372c48331dSMatt Macy if (rv == 0) {
1382c48331dSMatt Macy /*
1392c48331dSMatt Macy * zfs device descriptor is in form of "zfs:dataset:",
1402c48331dSMatt Macy * we only do need dataset name.
1412c48331dSMatt Macy */
1422c48331dSMatt Macy if (strncmp(val, "zfs:", 4) == 0) {
143*2a58b312SMartin Matuska char *tmp = strdup(val + 4);
144*2a58b312SMartin Matuska if (tmp != NULL) {
145*2a58b312SMartin Matuska size_t len = strlen(tmp);
1462c48331dSMatt Macy
147*2a58b312SMartin Matuska if (tmp[len - 1] == ':')
148*2a58b312SMartin Matuska tmp[len - 1] = '\0';
149*2a58b312SMartin Matuska *device = tmp;
1502c48331dSMatt Macy } else {
1512c48331dSMatt Macy rv = ENOMEM;
1522c48331dSMatt Macy }
1532c48331dSMatt Macy } else {
1542c48331dSMatt Macy rv = EINVAL;
1552c48331dSMatt Macy }
1562c48331dSMatt Macy }
1572c48331dSMatt Macy nvlist_free(nv);
1582c48331dSMatt Macy }
1592c48331dSMatt Macy
1602c48331dSMatt Macy zpool_close(zphdl);
1612c48331dSMatt Macy libzfs_fini(hdl);
1622c48331dSMatt Macy return (rv);
1632c48331dSMatt Macy }
164