1b179da01SKyle Evans /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 328f16a0fSKyle Evans * 428f16a0fSKyle Evans * Copyright (c) 2017 Kyle J. Kneitinger <kyle@kneit.in> 5b179da01SKyle Evans * Copyright (c) 2018 Kyle Evans <kevans@FreeBSD.org> 628f16a0fSKyle Evans * 728f16a0fSKyle Evans * Redistribution and use in source and binary forms, with or without 828f16a0fSKyle Evans * modification, are permitted provided that the following conditions 928f16a0fSKyle Evans * are met: 1028f16a0fSKyle Evans * 1. Redistributions of source code must retain the above copyright 1128f16a0fSKyle Evans * notice, this list of conditions and the following disclaimer. 1228f16a0fSKyle Evans * 2. Redistributions in binary form must reproduce the above copyright 1328f16a0fSKyle Evans * notice, this list of conditions and the following disclaimer in the 1428f16a0fSKyle Evans * documentation and/or other materials provided with the distribution. 1528f16a0fSKyle Evans * 1628f16a0fSKyle Evans * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 1728f16a0fSKyle Evans * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1828f16a0fSKyle Evans * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1928f16a0fSKyle Evans * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2028f16a0fSKyle Evans * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2128f16a0fSKyle Evans * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2228f16a0fSKyle Evans * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2328f16a0fSKyle Evans * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2428f16a0fSKyle Evans * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2528f16a0fSKyle Evans * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2628f16a0fSKyle Evans * SUCH DAMAGE. 2728f16a0fSKyle Evans */ 2828f16a0fSKyle Evans 29b6e7c421SKyle Evans #include <sys/cdefs.h> 30485172f5SKyle Evans #include <sys/zfs_context.h> 31e307eb94SToomas Soome #include <libzfsbootenv.h> 32485172f5SKyle Evans 3328f16a0fSKyle Evans #include "be.h" 3428f16a0fSKyle Evans #include "be_impl.h" 3528f16a0fSKyle Evans 365468566eSKyle Evans static int snapshot_proplist_update(zfs_handle_t *hdl, prop_data_t *data); 3796c5db58SKyle Evans 3828f16a0fSKyle Evans /* 3928f16a0fSKyle Evans * Returns the name of the active boot environment 4028f16a0fSKyle Evans */ 4128f16a0fSKyle Evans const char * 4228f16a0fSKyle Evans be_active_name(libbe_handle_t *lbh) 4328f16a0fSKyle Evans { 44bfe0869cSKyle Evans 45cc624025SKyle Evans if (*lbh->rootfs != '\0') 46c3a34c08SKyle Evans return (strrchr(lbh->rootfs, '/') + sizeof(char)); 47cc624025SKyle Evans else 48cc624025SKyle Evans return (lbh->rootfs); 4928f16a0fSKyle Evans } 5028f16a0fSKyle Evans 5128f16a0fSKyle Evans 5228f16a0fSKyle Evans /* 5328f16a0fSKyle Evans * Returns full path of the active boot environment 5428f16a0fSKyle Evans */ 5528f16a0fSKyle Evans const char * 5628f16a0fSKyle Evans be_active_path(libbe_handle_t *lbh) 5728f16a0fSKyle Evans { 58bfe0869cSKyle Evans 59c3a34c08SKyle Evans return (lbh->rootfs); 6028f16a0fSKyle Evans } 6128f16a0fSKyle Evans 62ff8676ccSKyle Evans /* 63ff8676ccSKyle Evans * Returns the name of the next active boot environment 64ff8676ccSKyle Evans */ 65ff8676ccSKyle Evans const char * 66ff8676ccSKyle Evans be_nextboot_name(libbe_handle_t *lbh) 67ff8676ccSKyle Evans { 68ff8676ccSKyle Evans 69cc624025SKyle Evans if (*lbh->bootfs != '\0') 70ff8676ccSKyle Evans return (strrchr(lbh->bootfs, '/') + sizeof(char)); 71cc624025SKyle Evans else 72cc624025SKyle Evans return (lbh->bootfs); 73ff8676ccSKyle Evans } 74ff8676ccSKyle Evans 75ff8676ccSKyle Evans 76ff8676ccSKyle Evans /* 77ff8676ccSKyle Evans * Returns full path of the active boot environment 78ff8676ccSKyle Evans */ 79ff8676ccSKyle Evans const char * 80ff8676ccSKyle Evans be_nextboot_path(libbe_handle_t *lbh) 81ff8676ccSKyle Evans { 82ff8676ccSKyle Evans 83ff8676ccSKyle Evans return (lbh->bootfs); 84ff8676ccSKyle Evans } 85ff8676ccSKyle Evans 8628f16a0fSKyle Evans 8728f16a0fSKyle Evans /* 8828f16a0fSKyle Evans * Returns the path of the boot environment root dataset 8928f16a0fSKyle Evans */ 9028f16a0fSKyle Evans const char * 9128f16a0fSKyle Evans be_root_path(libbe_handle_t *lbh) 9228f16a0fSKyle Evans { 93bfe0869cSKyle Evans 9428f16a0fSKyle Evans return (lbh->root); 9528f16a0fSKyle Evans } 9628f16a0fSKyle Evans 9728f16a0fSKyle Evans 9828f16a0fSKyle Evans /* 993682d5e9SKyle Evans * Populates dsnvl with one nvlist per bootenv dataset describing the properties 1003682d5e9SKyle Evans * of that dataset that we've declared ourselves to care about. 10128f16a0fSKyle Evans */ 1023682d5e9SKyle Evans int 1033682d5e9SKyle Evans be_get_bootenv_props(libbe_handle_t *lbh, nvlist_t *dsnvl) 10428f16a0fSKyle Evans { 10528f16a0fSKyle Evans prop_data_t data; 10628f16a0fSKyle Evans 10728f16a0fSKyle Evans data.lbh = lbh; 1083682d5e9SKyle Evans data.list = dsnvl; 1094146029bSKyle Evans data.single_object = false; 110e307eb94SToomas Soome data.bootonce = NULL; 1115468566eSKyle Evans return (be_proplist_update(&data)); 11228f16a0fSKyle Evans } 11328f16a0fSKyle Evans 1144146029bSKyle Evans int 1159b1662e6SKyle Evans be_get_dataset_props(libbe_handle_t *lbh, const char *name, nvlist_t *props) 1164146029bSKyle Evans { 1174146029bSKyle Evans zfs_handle_t *snap_hdl; 1184146029bSKyle Evans prop_data_t data; 1194146029bSKyle Evans int ret; 1204146029bSKyle Evans 1214146029bSKyle Evans data.lbh = lbh; 1224146029bSKyle Evans data.list = props; 1234146029bSKyle Evans data.single_object = true; 124e307eb94SToomas Soome data.bootonce = NULL; 1254146029bSKyle Evans if ((snap_hdl = zfs_open(lbh->lzh, name, 1269b1662e6SKyle Evans ZFS_TYPE_FILESYSTEM | ZFS_TYPE_SNAPSHOT)) == NULL) 1274146029bSKyle Evans return (BE_ERR_ZFSOPEN); 1284146029bSKyle Evans 1294146029bSKyle Evans ret = prop_list_builder_cb(snap_hdl, &data); 1304146029bSKyle Evans zfs_close(snap_hdl); 1314146029bSKyle Evans return (ret); 1324146029bSKyle Evans } 13328f16a0fSKyle Evans 13496c5db58SKyle Evans int 13596c5db58SKyle Evans be_get_dataset_snapshots(libbe_handle_t *lbh, const char *name, nvlist_t *props) 13696c5db58SKyle Evans { 13796c5db58SKyle Evans zfs_handle_t *ds_hdl; 13896c5db58SKyle Evans prop_data_t data; 13996c5db58SKyle Evans int ret; 14096c5db58SKyle Evans 14196c5db58SKyle Evans data.lbh = lbh; 14296c5db58SKyle Evans data.list = props; 14396c5db58SKyle Evans data.single_object = false; 144e307eb94SToomas Soome data.bootonce = NULL; 14596c5db58SKyle Evans if ((ds_hdl = zfs_open(lbh->lzh, name, 14696c5db58SKyle Evans ZFS_TYPE_FILESYSTEM)) == NULL) 14796c5db58SKyle Evans return (BE_ERR_ZFSOPEN); 14896c5db58SKyle Evans 1495468566eSKyle Evans ret = snapshot_proplist_update(ds_hdl, &data); 15096c5db58SKyle Evans zfs_close(ds_hdl); 15196c5db58SKyle Evans return (ret); 15296c5db58SKyle Evans } 15396c5db58SKyle Evans 15428f16a0fSKyle Evans /* 15528f16a0fSKyle Evans * Internal callback function used by zfs_iter_filesystems. For each dataset in 15628f16a0fSKyle Evans * the bootenv root, populate an nvlist_t of its relevant properties. 15728f16a0fSKyle Evans */ 158843e39ceSKyle Evans int 15928f16a0fSKyle Evans prop_list_builder_cb(zfs_handle_t *zfs_hdl, void *data_p) 16028f16a0fSKyle Evans { 1614831c931SKyle Evans char buf[512], *mountpoint; 162bfe0869cSKyle Evans prop_data_t *data; 163bfe0869cSKyle Evans libbe_handle_t *lbh; 164bfe0869cSKyle Evans nvlist_t *props; 165bfe0869cSKyle Evans const char *dataset, *name; 166b29bf2f8SKyle Evans boolean_t mounted; 167bfe0869cSKyle Evans 16828f16a0fSKyle Evans /* 169bfe0869cSKyle Evans * XXX TODO: 17028f16a0fSKyle Evans * some system for defining constants for the nvlist keys 17128f16a0fSKyle Evans * error checking 17228f16a0fSKyle Evans */ 17328f16a0fSKyle Evans data = (prop_data_t *)data_p; 174bfe0869cSKyle Evans lbh = data->lbh; 17528f16a0fSKyle Evans 1764146029bSKyle Evans if (data->single_object) 1774146029bSKyle Evans props = data->list; 1784146029bSKyle Evans else 17928f16a0fSKyle Evans nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP); 18028f16a0fSKyle Evans 181bfe0869cSKyle Evans dataset = zfs_get_name(zfs_hdl); 18228f16a0fSKyle Evans nvlist_add_string(props, "dataset", dataset); 18328f16a0fSKyle Evans 184*5086b6ecSR. Christian McDonald if (data->lbh->bootonce != NULL && 185*5086b6ecSR. Christian McDonald strcmp(dataset, data->lbh->bootonce) == 0) 186e307eb94SToomas Soome nvlist_add_boolean_value(props, "bootonce", true); 187e307eb94SToomas Soome 188bfe0869cSKyle Evans name = strrchr(dataset, '/') + 1; 18928f16a0fSKyle Evans nvlist_add_string(props, "name", name); 19028f16a0fSKyle Evans 1914831c931SKyle Evans mounted = zfs_is_mounted(zfs_hdl, &mountpoint); 19228f16a0fSKyle Evans 1934831c931SKyle Evans if (mounted) 194709b553cSKyle Evans nvlist_add_string(props, "mounted", mountpoint); 195709b553cSKyle Evans 196709b553cSKyle Evans if (zfs_prop_get(zfs_hdl, ZFS_PROP_MOUNTPOINT, buf, 512, 197709b553cSKyle Evans NULL, NULL, 0, 1) == 0) 198709b553cSKyle Evans nvlist_add_string(props, "mountpoint", buf); 19928f16a0fSKyle Evans 20028f16a0fSKyle Evans if (zfs_prop_get(zfs_hdl, ZFS_PROP_ORIGIN, buf, 512, 201513044c1SKyle Evans NULL, NULL, 0, 1) == 0) 20228f16a0fSKyle Evans nvlist_add_string(props, "origin", buf); 20328f16a0fSKyle Evans 20428f16a0fSKyle Evans if (zfs_prop_get(zfs_hdl, ZFS_PROP_CREATION, buf, 512, 205513044c1SKyle Evans NULL, NULL, 0, 1) == 0) 20628f16a0fSKyle Evans nvlist_add_string(props, "creation", buf); 20728f16a0fSKyle Evans 20828f16a0fSKyle Evans nvlist_add_boolean_value(props, "active", 20928f16a0fSKyle Evans (strcmp(be_active_path(lbh), dataset) == 0)); 21028f16a0fSKyle Evans 21128f16a0fSKyle Evans if (zfs_prop_get(zfs_hdl, ZFS_PROP_USED, buf, 512, 212513044c1SKyle Evans NULL, NULL, 0, 1) == 0) 21328f16a0fSKyle Evans nvlist_add_string(props, "used", buf); 21428f16a0fSKyle Evans 21528f16a0fSKyle Evans if (zfs_prop_get(zfs_hdl, ZFS_PROP_USEDDS, buf, 512, 216513044c1SKyle Evans NULL, NULL, 0, 1) == 0) 21728f16a0fSKyle Evans nvlist_add_string(props, "usedds", buf); 21828f16a0fSKyle Evans 21928f16a0fSKyle Evans if (zfs_prop_get(zfs_hdl, ZFS_PROP_USEDSNAP, buf, 512, 220513044c1SKyle Evans NULL, NULL, 0, 1) == 0) 22128f16a0fSKyle Evans nvlist_add_string(props, "usedsnap", buf); 22228f16a0fSKyle Evans 22328f16a0fSKyle Evans if (zfs_prop_get(zfs_hdl, ZFS_PROP_USEDREFRESERV, buf, 512, 224513044c1SKyle Evans NULL, NULL, 0, 1) == 0) 22528f16a0fSKyle Evans nvlist_add_string(props, "usedrefreserv", buf); 22628f16a0fSKyle Evans 22728f16a0fSKyle Evans if (zfs_prop_get(zfs_hdl, ZFS_PROP_REFERENCED, buf, 512, 228513044c1SKyle Evans NULL, NULL, 0, 1) == 0) 22928f16a0fSKyle Evans nvlist_add_string(props, "referenced", buf); 23028f16a0fSKyle Evans 231ff8676ccSKyle Evans nvlist_add_boolean_value(props, "nextboot", 232ff8676ccSKyle Evans (strcmp(be_nextboot_path(lbh), dataset) == 0)); 23328f16a0fSKyle Evans 2344146029bSKyle Evans if (!data->single_object) 23528f16a0fSKyle Evans nvlist_add_nvlist(data->list, name, props); 23628f16a0fSKyle Evans 23728f16a0fSKyle Evans return (0); 23828f16a0fSKyle Evans } 23928f16a0fSKyle Evans 24028f16a0fSKyle Evans 24128f16a0fSKyle Evans /* 24228f16a0fSKyle Evans * Updates the properties of each bootenv in the libbe handle 243bfe0869cSKyle Evans * XXX TODO: ensure that this is always consistent (run after adds, deletes, 24428f16a0fSKyle Evans * renames,etc 24528f16a0fSKyle Evans */ 246843e39ceSKyle Evans int 2475468566eSKyle Evans be_proplist_update(prop_data_t *data) 24828f16a0fSKyle Evans { 24928f16a0fSKyle Evans zfs_handle_t *root_hdl; 25028f16a0fSKyle Evans 251bfe0869cSKyle Evans if ((root_hdl = zfs_open(data->lbh->lzh, data->lbh->root, 252bfe0869cSKyle Evans ZFS_TYPE_FILESYSTEM)) == NULL) 25328f16a0fSKyle Evans return (BE_ERR_ZFSOPEN); 25428f16a0fSKyle Evans 255bfe0869cSKyle Evans /* XXX TODO: some error checking here */ 256d411c1d6SMartin Matuska zfs_iter_filesystems(root_hdl, prop_list_builder_cb, data); 25728f16a0fSKyle Evans 25828f16a0fSKyle Evans zfs_close(root_hdl); 25928f16a0fSKyle Evans 26028f16a0fSKyle Evans return (0); 26128f16a0fSKyle Evans } 26228f16a0fSKyle Evans 26396c5db58SKyle Evans static int 2645468566eSKyle Evans snapshot_proplist_update(zfs_handle_t *hdl, prop_data_t *data) 26596c5db58SKyle Evans { 26696c5db58SKyle Evans 267d411c1d6SMartin Matuska return (zfs_iter_snapshots_sorted(hdl, prop_list_builder_cb, data, 268cefbdf3eSAlan Somers 0, 0)); 26996c5db58SKyle Evans } 27096c5db58SKyle Evans 27128f16a0fSKyle Evans 272734e362fSKyle Evans int 273734e362fSKyle Evans be_prop_list_alloc(nvlist_t **be_list) 274734e362fSKyle Evans { 275734e362fSKyle Evans 276734e362fSKyle Evans return (nvlist_alloc(be_list, NV_UNIQUE_NAME, KM_SLEEP)); 277734e362fSKyle Evans } 278734e362fSKyle Evans 27928f16a0fSKyle Evans /* 28028f16a0fSKyle Evans * frees property list and its children 28128f16a0fSKyle Evans */ 28228f16a0fSKyle Evans void 28328f16a0fSKyle Evans be_prop_list_free(nvlist_t *be_list) 28428f16a0fSKyle Evans { 28528f16a0fSKyle Evans nvlist_t *prop_list; 286bfe0869cSKyle Evans nvpair_t *be_pair; 28728f16a0fSKyle Evans 288bfe0869cSKyle Evans be_pair = nvlist_next_nvpair(be_list, NULL); 289bfe0869cSKyle Evans if (nvpair_value_nvlist(be_pair, &prop_list) == 0) 29028f16a0fSKyle Evans nvlist_free(prop_list); 29128f16a0fSKyle Evans 29228f16a0fSKyle Evans while ((be_pair = nvlist_next_nvpair(be_list, be_pair)) != NULL) { 293bfe0869cSKyle Evans if (nvpair_value_nvlist(be_pair, &prop_list) == 0) 29428f16a0fSKyle Evans nvlist_free(prop_list); 29528f16a0fSKyle Evans } 29628f16a0fSKyle Evans } 29728f16a0fSKyle Evans 29828f16a0fSKyle Evans 29928f16a0fSKyle Evans /* 30028f16a0fSKyle Evans * Usage 30128f16a0fSKyle Evans */ 302162ec569SKyle Evans int 3035773e924SKyle Evans be_exists(libbe_handle_t *lbh, const char *be) 30428f16a0fSKyle Evans { 30528f16a0fSKyle Evans char buf[BE_MAXPATHLEN]; 30628f16a0fSKyle Evans 30728f16a0fSKyle Evans be_root_concat(lbh, be, buf); 30828f16a0fSKyle Evans 309709b553cSKyle Evans if (!zfs_dataset_exists(lbh->lzh, buf, ZFS_TYPE_DATASET)) 310162ec569SKyle Evans return (BE_ERR_NOENT); 311709b553cSKyle Evans 31251aecc89SKyle Evans return (BE_ERR_SUCCESS); 31328f16a0fSKyle Evans } 314