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