1 /* 2 * Copyright (c) 2017 Kyle J. Kneitinger <kyle@kneit.in> 3 * Copyright (c) 2018 Kyle Evans <kevans@FreeBSD.org> 4 * 5 * SPDX-License-Identifier: BSD-2-Clause 6 */ 7 8 #include <sys/cdefs.h> 9 #include <sys/zfs_context.h> 10 #include <libzfsbootenv.h> 11 12 #include "be.h" 13 #include "be_impl.h" 14 15 static int snapshot_proplist_update(zfs_handle_t *hdl, prop_data_t *data); 16 17 /* 18 * Returns the name of the active boot environment 19 */ 20 const char * 21 be_active_name(libbe_handle_t *lbh) 22 { 23 24 if (*lbh->rootfs != '\0') 25 return (strrchr(lbh->rootfs, '/') + sizeof(char)); 26 else 27 return (lbh->rootfs); 28 } 29 30 31 /* 32 * Returns full path of the active boot environment 33 */ 34 const char * 35 be_active_path(libbe_handle_t *lbh) 36 { 37 38 return (lbh->rootfs); 39 } 40 41 /* 42 * Returns the name of the next active boot environment 43 */ 44 const char * 45 be_nextboot_name(libbe_handle_t *lbh) 46 { 47 48 if (*lbh->bootfs != '\0') 49 return (strrchr(lbh->bootfs, '/') + sizeof(char)); 50 else 51 return (lbh->bootfs); 52 } 53 54 55 /* 56 * Returns full path of the active boot environment 57 */ 58 const char * 59 be_nextboot_path(libbe_handle_t *lbh) 60 { 61 62 return (lbh->bootfs); 63 } 64 65 66 /* 67 * Returns the path of the boot environment root dataset 68 */ 69 const char * 70 be_root_path(libbe_handle_t *lbh) 71 { 72 73 return (lbh->root); 74 } 75 76 77 /* 78 * Populates dsnvl with one nvlist per bootenv dataset describing the properties 79 * of that dataset that we've declared ourselves to care about. 80 */ 81 int 82 be_get_bootenv_props(libbe_handle_t *lbh, nvlist_t *dsnvl) 83 { 84 prop_data_t data; 85 86 data.lbh = lbh; 87 data.list = dsnvl; 88 data.single_object = false; 89 data.bootonce = NULL; 90 return (be_proplist_update(&data)); 91 } 92 93 int 94 be_get_dataset_props(libbe_handle_t *lbh, const char *name, nvlist_t *props) 95 { 96 zfs_handle_t *snap_hdl; 97 prop_data_t data; 98 int ret; 99 100 data.lbh = lbh; 101 data.list = props; 102 data.single_object = true; 103 data.bootonce = NULL; 104 if ((snap_hdl = zfs_open(lbh->lzh, name, 105 ZFS_TYPE_FILESYSTEM | ZFS_TYPE_SNAPSHOT)) == NULL) 106 return (BE_ERR_ZFSOPEN); 107 108 ret = prop_list_builder_cb(snap_hdl, &data); 109 zfs_close(snap_hdl); 110 return (ret); 111 } 112 113 int 114 be_get_dataset_snapshots(libbe_handle_t *lbh, const char *name, nvlist_t *props) 115 { 116 zfs_handle_t *ds_hdl; 117 prop_data_t data; 118 int ret; 119 120 data.lbh = lbh; 121 data.list = props; 122 data.single_object = false; 123 data.bootonce = NULL; 124 if ((ds_hdl = zfs_open(lbh->lzh, name, 125 ZFS_TYPE_FILESYSTEM)) == NULL) 126 return (BE_ERR_ZFSOPEN); 127 128 ret = snapshot_proplist_update(ds_hdl, &data); 129 zfs_close(ds_hdl); 130 return (ret); 131 } 132 133 /* 134 * Internal callback function used by zfs_iter_filesystems. For each dataset in 135 * the bootenv root, populate an nvlist_t of its relevant properties. 136 */ 137 int 138 prop_list_builder_cb(zfs_handle_t *zfs_hdl, void *data_p) 139 { 140 char buf[512], *mountpoint; 141 prop_data_t *data; 142 libbe_handle_t *lbh; 143 nvlist_t *props; 144 const char *dataset, *name; 145 boolean_t mounted; 146 147 /* 148 * XXX TODO: 149 * some system for defining constants for the nvlist keys 150 * error checking 151 */ 152 data = (prop_data_t *)data_p; 153 lbh = data->lbh; 154 155 if (data->single_object) 156 props = data->list; 157 else 158 nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP); 159 160 dataset = zfs_get_name(zfs_hdl); 161 nvlist_add_string(props, "dataset", dataset); 162 163 if (data->lbh->bootonce != NULL && 164 strcmp(dataset, data->lbh->bootonce) == 0) 165 nvlist_add_boolean_value(props, "bootonce", true); 166 167 name = strrchr(dataset, '/') + 1; 168 nvlist_add_string(props, "name", name); 169 170 mounted = zfs_is_mounted(zfs_hdl, &mountpoint); 171 172 if (mounted) 173 nvlist_add_string(props, "mounted", mountpoint); 174 175 if (zfs_prop_get(zfs_hdl, ZFS_PROP_MOUNTPOINT, buf, 512, 176 NULL, NULL, 0, 1) == 0) 177 nvlist_add_string(props, "mountpoint", buf); 178 179 if (zfs_prop_get(zfs_hdl, ZFS_PROP_ORIGIN, buf, 512, 180 NULL, NULL, 0, 1) == 0) 181 nvlist_add_string(props, "origin", buf); 182 183 if (zfs_prop_get(zfs_hdl, ZFS_PROP_CREATION, buf, 512, 184 NULL, NULL, 0, 1) == 0) 185 nvlist_add_string(props, "creation", buf); 186 187 nvlist_add_boolean_value(props, "active", 188 (strcmp(be_active_path(lbh), dataset) == 0)); 189 190 if (zfs_prop_get(zfs_hdl, ZFS_PROP_USED, buf, 512, 191 NULL, NULL, 0, 1) == 0) 192 nvlist_add_string(props, "used", buf); 193 194 if (zfs_prop_get(zfs_hdl, ZFS_PROP_USEDDS, buf, 512, 195 NULL, NULL, 0, 1) == 0) 196 nvlist_add_string(props, "usedds", buf); 197 198 if (zfs_prop_get(zfs_hdl, ZFS_PROP_USEDSNAP, buf, 512, 199 NULL, NULL, 0, 1) == 0) 200 nvlist_add_string(props, "usedsnap", buf); 201 202 if (zfs_prop_get(zfs_hdl, ZFS_PROP_USEDREFRESERV, buf, 512, 203 NULL, NULL, 0, 1) == 0) 204 nvlist_add_string(props, "usedrefreserv", buf); 205 206 if (zfs_prop_get(zfs_hdl, ZFS_PROP_REFERENCED, buf, 512, 207 NULL, NULL, 0, 1) == 0) 208 nvlist_add_string(props, "referenced", buf); 209 210 nvlist_add_boolean_value(props, "nextboot", 211 (strcmp(be_nextboot_path(lbh), dataset) == 0)); 212 213 if (!data->single_object) 214 nvlist_add_nvlist(data->list, name, props); 215 216 return (0); 217 } 218 219 220 /* 221 * Updates the properties of each bootenv in the libbe handle 222 * XXX TODO: ensure that this is always consistent (run after adds, deletes, 223 * renames,etc 224 */ 225 int 226 be_proplist_update(prop_data_t *data) 227 { 228 zfs_handle_t *root_hdl; 229 230 if ((root_hdl = zfs_open(data->lbh->lzh, data->lbh->root, 231 ZFS_TYPE_FILESYSTEM)) == NULL) 232 return (BE_ERR_ZFSOPEN); 233 234 /* XXX TODO: some error checking here */ 235 zfs_iter_filesystems(root_hdl, prop_list_builder_cb, data); 236 237 zfs_close(root_hdl); 238 239 return (0); 240 } 241 242 static int 243 snapshot_proplist_update(zfs_handle_t *hdl, prop_data_t *data) 244 { 245 246 return (zfs_iter_snapshots_sorted(hdl, prop_list_builder_cb, data, 247 0, 0)); 248 } 249 250 251 int 252 be_prop_list_alloc(nvlist_t **be_list) 253 { 254 255 return (nvlist_alloc(be_list, NV_UNIQUE_NAME, KM_SLEEP)); 256 } 257 258 /* 259 * frees property list and its children 260 */ 261 void 262 be_prop_list_free(nvlist_t *be_list) 263 { 264 nvlist_t *prop_list; 265 nvpair_t *be_pair; 266 267 be_pair = nvlist_next_nvpair(be_list, NULL); 268 if (nvpair_value_nvlist(be_pair, &prop_list) == 0) 269 nvlist_free(prop_list); 270 271 while ((be_pair = nvlist_next_nvpair(be_list, be_pair)) != NULL) { 272 if (nvpair_value_nvlist(be_pair, &prop_list) == 0) 273 nvlist_free(prop_list); 274 } 275 } 276 277 278 /* 279 * Usage 280 */ 281 int 282 be_exists(libbe_handle_t *lbh, const char *be) 283 { 284 char buf[BE_MAXPATHLEN]; 285 286 be_root_concat(lbh, be, buf); 287 288 if (!zfs_dataset_exists(lbh->lzh, buf, ZFS_TYPE_DATASET)) 289 return (BE_ERR_NOENT); 290 291 return (BE_ERR_SUCCESS); 292 } 293