1 // SPDX-License-Identifier: CDDL-1.0 2 /* 3 * This file and its contents are supplied under the terms of the 4 * Common Development and Distribution License ("CDDL"), version 1.0. 5 * You may only use this file in accordance with the terms of version 6 * 1.0 of the CDDL. 7 * 8 * A full copy of the text of the CDDL should have accompanied this 9 * source. A copy of the CDDL is also available via the Internet at 10 * http://www.illumos.org/license/CDDL. 11 */ 12 /* 13 * Copyright 2020 Toomas Soome <tsoome@me.com> 14 */ 15 16 #include <sys/types.h> 17 #include <string.h> 18 #include <libzfs.h> 19 #include <libzfsbootenv.h> 20 #include <sys/zfs_bootenv.h> 21 #include <sys/vdev_impl.h> 22 23 /* 24 * Store device name to zpool label bootenv area. 25 * This call will set bootenv version to VB_NVLIST, if bootenv currently 26 * does contain other version, then old data will be replaced. 27 */ 28 int 29 lzbe_set_boot_device(const char *pool, lzbe_flags_t flag, const char *device) 30 { 31 libzfs_handle_t *hdl; 32 zpool_handle_t *zphdl; 33 nvlist_t *nv; 34 char *descriptor; 35 uint64_t version; 36 int rv = -1; 37 38 if (pool == NULL || *pool == '\0') 39 return (rv); 40 41 if ((hdl = libzfs_init()) == NULL) 42 return (rv); 43 44 zphdl = zpool_open(hdl, pool); 45 if (zphdl == NULL) { 46 libzfs_fini(hdl); 47 return (rv); 48 } 49 50 switch (flag) { 51 case lzbe_add: 52 rv = zpool_get_bootenv(zphdl, &nv); 53 if (rv == 0) { 54 /* 55 * We got the nvlist, check for version. 56 * if version is missing or is not VB_NVLIST, 57 * create new list. 58 */ 59 rv = nvlist_lookup_uint64(nv, BOOTENV_VERSION, 60 &version); 61 if (rv == 0 && version == VB_NVLIST) 62 break; 63 64 /* Drop this nvlist */ 65 fnvlist_free(nv); 66 } 67 zfs_fallthrough; 68 case lzbe_replace: 69 nv = fnvlist_alloc(); 70 break; 71 default: 72 return (rv); 73 } 74 75 /* version is mandatory */ 76 fnvlist_add_uint64(nv, BOOTENV_VERSION, VB_NVLIST); 77 78 rv = 0; 79 /* 80 * If device name is empty, remove boot device configuration. 81 */ 82 if ((device == NULL || *device == '\0')) { 83 if (nvlist_exists(nv, OS_BOOTONCE)) 84 fnvlist_remove(nv, OS_BOOTONCE); 85 } else { 86 /* 87 * Use device name directly if it does start with 88 * prefix "zfs:". Otherwise, add prefix and suffix. 89 */ 90 if (strncmp(device, "zfs:", 4) == 0) { 91 fnvlist_add_string(nv, OS_BOOTONCE, device); 92 } else { 93 if (asprintf(&descriptor, "zfs:%s:", device) > 0) { 94 fnvlist_add_string(nv, OS_BOOTONCE, descriptor); 95 free(descriptor); 96 } else 97 rv = ENOMEM; 98 } 99 } 100 if (rv == 0) 101 rv = zpool_set_bootenv(zphdl, nv); 102 if (rv != 0) 103 fprintf(stderr, "%s\n", libzfs_error_description(hdl)); 104 105 fnvlist_free(nv); 106 zpool_close(zphdl); 107 libzfs_fini(hdl); 108 return (rv); 109 } 110 111 /* 112 * Return boot device name from bootenv, if set. 113 */ 114 int 115 lzbe_get_boot_device(const char *pool, char **device) 116 { 117 libzfs_handle_t *hdl; 118 zpool_handle_t *zphdl; 119 nvlist_t *nv; 120 const char *val; 121 int rv = -1; 122 123 if (pool == NULL || *pool == '\0' || device == NULL) 124 return (rv); 125 126 if ((hdl = libzfs_init()) == NULL) 127 return (rv); 128 129 zphdl = zpool_open(hdl, pool); 130 if (zphdl == NULL) { 131 libzfs_fini(hdl); 132 return (rv); 133 } 134 135 rv = zpool_get_bootenv(zphdl, &nv); 136 if (rv == 0) { 137 rv = nvlist_lookup_string(nv, OS_BOOTONCE, &val); 138 if (rv == 0) { 139 /* 140 * zfs device descriptor is in form of "zfs:dataset:", 141 * we only do need dataset name. 142 */ 143 if (strncmp(val, "zfs:", 4) == 0) { 144 char *tmp = strdup(val + 4); 145 if (tmp != NULL) { 146 size_t len = strlen(tmp); 147 148 if (tmp[len - 1] == ':') 149 tmp[len - 1] = '\0'; 150 *device = tmp; 151 } else { 152 rv = ENOMEM; 153 } 154 } else { 155 rv = EINVAL; 156 } 157 } 158 nvlist_free(nv); 159 } 160 161 zpool_close(zphdl); 162 libzfs_fini(hdl); 163 return (rv); 164 } 165