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 zfs_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 rv = 0; 78 /* 79 * If device name is empty, remove boot device configuration. 80 */ 81 if ((device == NULL || *device == '\0')) { 82 if (nvlist_exists(nv, OS_BOOTONCE)) 83 fnvlist_remove(nv, OS_BOOTONCE); 84 } else { 85 /* 86 * Use device name directly if it does start with 87 * prefix "zfs:". Otherwise, add prefix and suffix. 88 */ 89 if (strncmp(device, "zfs:", 4) == 0) { 90 fnvlist_add_string(nv, OS_BOOTONCE, device); 91 } else { 92 if (asprintf(&descriptor, "zfs:%s:", device) > 0) { 93 fnvlist_add_string(nv, OS_BOOTONCE, descriptor); 94 free(descriptor); 95 } else 96 rv = ENOMEM; 97 } 98 } 99 if (rv == 0) 100 rv = zpool_set_bootenv(zphdl, nv); 101 if (rv != 0) 102 fprintf(stderr, "%s\n", libzfs_error_description(hdl)); 103 104 fnvlist_free(nv); 105 zpool_close(zphdl); 106 libzfs_fini(hdl); 107 return (rv); 108 } 109 110 /* 111 * Return boot device name from bootenv, if set. 112 */ 113 int 114 lzbe_get_boot_device(const char *pool, char **device) 115 { 116 libzfs_handle_t *hdl; 117 zpool_handle_t *zphdl; 118 nvlist_t *nv; 119 const char *val; 120 int rv = -1; 121 122 if (pool == NULL || *pool == '\0' || device == NULL) 123 return (rv); 124 125 if ((hdl = libzfs_init()) == NULL) 126 return (rv); 127 128 zphdl = zpool_open(hdl, pool); 129 if (zphdl == NULL) { 130 libzfs_fini(hdl); 131 return (rv); 132 } 133 134 rv = zpool_get_bootenv(zphdl, &nv); 135 if (rv == 0) { 136 rv = nvlist_lookup_string(nv, OS_BOOTONCE, &val); 137 if (rv == 0) { 138 /* 139 * zfs device descriptor is in form of "zfs:dataset:", 140 * we only do need dataset name. 141 */ 142 if (strncmp(val, "zfs:", 4) == 0) { 143 char *tmp = strdup(val + 4); 144 if (tmp != NULL) { 145 size_t len = strlen(tmp); 146 147 if (tmp[len - 1] == ':') 148 tmp[len - 1] = '\0'; 149 *device = tmp; 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