1b179da01SKyle Evans /*- 2b179da01SKyle Evans * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 328f16a0fSKyle Evans * 428f16a0fSKyle Evans * Copyright (c) 2017 Kyle J. Kneitinger <kyle@kneit.in> 528f16a0fSKyle Evans * All rights reserved. 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> 30b6e7c421SKyle Evans __FBSDID("$FreeBSD$"); 31b6e7c421SKyle Evans 3251aecc89SKyle Evans #include <sys/param.h> 3351aecc89SKyle Evans #include <sys/mount.h> 3428f16a0fSKyle Evans #include <sys/stat.h> 3551aecc89SKyle Evans #include <sys/ucred.h> 3628f16a0fSKyle Evans 3728f16a0fSKyle Evans #include <ctype.h> 3828f16a0fSKyle Evans #include <libgen.h> 3928f16a0fSKyle Evans #include <libzfs_core.h> 4028f16a0fSKyle Evans #include <stdio.h> 4128f16a0fSKyle Evans #include <stdlib.h> 4228f16a0fSKyle Evans #include <time.h> 4328f16a0fSKyle Evans #include <unistd.h> 4428f16a0fSKyle Evans 4528f16a0fSKyle Evans #include "be.h" 4628f16a0fSKyle Evans #include "be_impl.h" 4728f16a0fSKyle Evans 48be7dd423SKyle Evans struct be_destroy_data { 49be7dd423SKyle Evans libbe_handle_t *lbh; 50be7dd423SKyle Evans char *snapname; 51be7dd423SKyle Evans }; 52be7dd423SKyle Evans 533d1a1f2cSKyle Evans #if SOON 54c65a2111SKyle Evans static int be_create_child_noent(libbe_handle_t *lbh, const char *active, 55c65a2111SKyle Evans const char *child_path); 56c65a2111SKyle Evans static int be_create_child_cloned(libbe_handle_t *lbh, const char *active); 573d1a1f2cSKyle Evans #endif 58c65a2111SKyle Evans 5990cf61e8SKyle Evans /* Arbitrary... should tune */ 6090cf61e8SKyle Evans #define BE_SNAP_SERIAL_MAX 1024 6190cf61e8SKyle Evans 6228f16a0fSKyle Evans /* 63ee16b7c9SKyle Evans * Iterator function for locating the rootfs amongst the children of the 64ee16b7c9SKyle Evans * zfs_be_root set by loader(8). data is expected to be a libbe_handle_t *. 65ee16b7c9SKyle Evans */ 66ee16b7c9SKyle Evans static int 6751aecc89SKyle Evans be_locate_rootfs(libbe_handle_t *lbh) 68ee16b7c9SKyle Evans { 694ab5187dSKyle Evans struct statfs sfs; 704ab5187dSKyle Evans struct extmnttab entry; 7151aecc89SKyle Evans zfs_handle_t *zfs; 72ee16b7c9SKyle Evans 734ab5187dSKyle Evans /* 744ab5187dSKyle Evans * Check first if root is ZFS; if not, we'll bail on rootfs capture. 754ab5187dSKyle Evans * Unfortunately needed because zfs_path_to_zhandle will emit to 764ab5187dSKyle Evans * stderr if / isn't actually a ZFS filesystem, which we'd like 774ab5187dSKyle Evans * to avoid. 784ab5187dSKyle Evans */ 794ab5187dSKyle Evans if (statfs("/", &sfs) == 0) { 804ab5187dSKyle Evans statfs2mnttab(&sfs, &entry); 814ab5187dSKyle Evans if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) 824ab5187dSKyle Evans return (1); 834ab5187dSKyle Evans } else 844ab5187dSKyle Evans return (1); 8551aecc89SKyle Evans zfs = zfs_path_to_zhandle(lbh->lzh, "/", ZFS_TYPE_FILESYSTEM); 8651aecc89SKyle Evans if (zfs == NULL) 87ee16b7c9SKyle Evans return (1); 88ee16b7c9SKyle Evans 8951aecc89SKyle Evans strlcpy(lbh->rootfs, zfs_get_name(zfs), sizeof(lbh->rootfs)); 9051aecc89SKyle Evans zfs_close(zfs); 91ee16b7c9SKyle Evans return (0); 92ee16b7c9SKyle Evans } 93ee16b7c9SKyle Evans 94ee16b7c9SKyle Evans /* 9528f16a0fSKyle Evans * Initializes the libbe context to operate in the root boot environment 9628f16a0fSKyle Evans * dataset, for example, zroot/ROOT. 9728f16a0fSKyle Evans */ 9828f16a0fSKyle Evans libbe_handle_t * 99cc624025SKyle Evans libbe_init(const char *root) 10028f16a0fSKyle Evans { 101fc13fc1cSKyle Evans char altroot[MAXPATHLEN]; 10228f16a0fSKyle Evans libbe_handle_t *lbh; 103c3a34c08SKyle Evans char *poolname, *pos; 104c3a34c08SKyle Evans int pnamelen; 10528f16a0fSKyle Evans 106c3a34c08SKyle Evans lbh = NULL; 107c3a34c08SKyle Evans poolname = pos = NULL; 10828f16a0fSKyle Evans 109c3a34c08SKyle Evans if ((lbh = calloc(1, sizeof(libbe_handle_t))) == NULL) 110c3a34c08SKyle Evans goto err; 11128f16a0fSKyle Evans 112c3a34c08SKyle Evans if ((lbh->lzh = libzfs_init()) == NULL) 113c3a34c08SKyle Evans goto err; 11428f16a0fSKyle Evans 115cc624025SKyle Evans /* 116cc624025SKyle Evans * Grab rootfs, we'll work backwards from there if an optional BE root 117cc624025SKyle Evans * has not been passed in. 118cc624025SKyle Evans */ 1194ab5187dSKyle Evans if (be_locate_rootfs(lbh) != 0) { 1204ab5187dSKyle Evans if (root == NULL) 121c3a34c08SKyle Evans goto err; 1224ab5187dSKyle Evans *lbh->rootfs = '\0'; 1234ab5187dSKyle Evans } 124cc624025SKyle Evans if (root == NULL) { 125cc624025SKyle Evans /* Strip off the final slash from rootfs to get the be root */ 12651aecc89SKyle Evans strlcpy(lbh->root, lbh->rootfs, sizeof(lbh->root)); 12751aecc89SKyle Evans pos = strrchr(lbh->root, '/'); 12851aecc89SKyle Evans if (pos == NULL) 12951aecc89SKyle Evans goto err; 13051aecc89SKyle Evans *pos = '\0'; 131cc624025SKyle Evans } else 132cc624025SKyle Evans strlcpy(lbh->root, root, sizeof(lbh->root)); 133c3a34c08SKyle Evans 134c3a34c08SKyle Evans if ((pos = strchr(lbh->root, '/')) == NULL) 135c3a34c08SKyle Evans goto err; 136c3a34c08SKyle Evans 137c3a34c08SKyle Evans pnamelen = pos - lbh->root; 138c3a34c08SKyle Evans poolname = malloc(pnamelen + 1); 139c3a34c08SKyle Evans if (poolname == NULL) 140c3a34c08SKyle Evans goto err; 141c3a34c08SKyle Evans 14255b0e92bSKyle Evans strlcpy(poolname, lbh->root, pnamelen + 1); 143c3a34c08SKyle Evans if ((lbh->active_phandle = zpool_open(lbh->lzh, poolname)) == NULL) 144c3a34c08SKyle Evans goto err; 145a8e44f4dSKyle Evans free(poolname); 146a8e44f4dSKyle Evans poolname = NULL; 147c3a34c08SKyle Evans 148c3a34c08SKyle Evans if (zpool_get_prop(lbh->active_phandle, ZPOOL_PROP_BOOTFS, lbh->bootfs, 14955b0e92bSKyle Evans sizeof(lbh->bootfs), NULL, true) != 0) 150c3a34c08SKyle Evans goto err; 151c3a34c08SKyle Evans 152fc13fc1cSKyle Evans if (zpool_get_prop(lbh->active_phandle, ZPOOL_PROP_ALTROOT, 153fc13fc1cSKyle Evans altroot, sizeof(altroot), NULL, true) == 0 && 154fc13fc1cSKyle Evans strcmp(altroot, "-") != 0) 155fc13fc1cSKyle Evans lbh->altroot_len = strlen(altroot); 156fc13fc1cSKyle Evans 15728f16a0fSKyle Evans return (lbh); 158c3a34c08SKyle Evans err: 159c3a34c08SKyle Evans if (lbh != NULL) { 160c3a34c08SKyle Evans if (lbh->active_phandle != NULL) 161c3a34c08SKyle Evans zpool_close(lbh->active_phandle); 162c3a34c08SKyle Evans if (lbh->lzh != NULL) 163c3a34c08SKyle Evans libzfs_fini(lbh->lzh); 164c3a34c08SKyle Evans free(lbh); 165c3a34c08SKyle Evans } 166c3a34c08SKyle Evans free(poolname); 167c3a34c08SKyle Evans return (NULL); 16828f16a0fSKyle Evans } 16928f16a0fSKyle Evans 17028f16a0fSKyle Evans 17128f16a0fSKyle Evans /* 17228f16a0fSKyle Evans * Free memory allocated by libbe_init() 17328f16a0fSKyle Evans */ 17428f16a0fSKyle Evans void 17528f16a0fSKyle Evans libbe_close(libbe_handle_t *lbh) 17628f16a0fSKyle Evans { 177bfe0869cSKyle Evans 178c3a34c08SKyle Evans if (lbh->active_phandle != NULL) 179c3a34c08SKyle Evans zpool_close(lbh->active_phandle); 18028f16a0fSKyle Evans libzfs_fini(lbh->lzh); 18128f16a0fSKyle Evans free(lbh); 18228f16a0fSKyle Evans } 18328f16a0fSKyle Evans 1849b1662e6SKyle Evans /* 1859b1662e6SKyle Evans * Proxy through to libzfs for the moment. 1869b1662e6SKyle Evans */ 1879b1662e6SKyle Evans void 1889b1662e6SKyle Evans be_nicenum(uint64_t num, char *buf, size_t buflen) 1899b1662e6SKyle Evans { 1909b1662e6SKyle Evans 1919b1662e6SKyle Evans zfs_nicenum(num, buf, buflen); 1929b1662e6SKyle Evans } 19328f16a0fSKyle Evans 194920abf4dSKyle Evans static int 195920abf4dSKyle Evans be_destroy_cb(zfs_handle_t *zfs_hdl, void *data) 196920abf4dSKyle Evans { 197be7dd423SKyle Evans char path[BE_MAXPATHLEN]; 198be7dd423SKyle Evans struct be_destroy_data *bdd; 199be7dd423SKyle Evans zfs_handle_t *snap; 200920abf4dSKyle Evans int err; 201920abf4dSKyle Evans 202be7dd423SKyle Evans bdd = (struct be_destroy_data *)data; 203be7dd423SKyle Evans if (bdd->snapname == NULL) { 204be7dd423SKyle Evans err = zfs_iter_children(zfs_hdl, be_destroy_cb, data); 205be7dd423SKyle Evans if (err != 0) 206920abf4dSKyle Evans return (err); 207be7dd423SKyle Evans return (zfs_destroy(zfs_hdl, false)); 208be7dd423SKyle Evans } 209be7dd423SKyle Evans /* If we're dealing with snapshots instead, delete that one alone */ 210be7dd423SKyle Evans err = zfs_iter_filesystems(zfs_hdl, be_destroy_cb, data); 211be7dd423SKyle Evans if (err != 0) 212920abf4dSKyle Evans return (err); 213be7dd423SKyle Evans /* 214be7dd423SKyle Evans * This part is intentionally glossing over any potential errors, 215be7dd423SKyle Evans * because there's a lot less potential for errors when we're cleaning 216be7dd423SKyle Evans * up snapshots rather than a full deep BE. The primary error case 217be7dd423SKyle Evans * here being if the snapshot doesn't exist in the first place, which 218be7dd423SKyle Evans * the caller will likely deem insignificant as long as it doesn't 219be7dd423SKyle Evans * exist after the call. Thus, such a missing snapshot shouldn't jam 220be7dd423SKyle Evans * up the destruction. 221be7dd423SKyle Evans */ 222be7dd423SKyle Evans snprintf(path, sizeof(path), "%s@%s", zfs_get_name(zfs_hdl), 223be7dd423SKyle Evans bdd->snapname); 224be7dd423SKyle Evans if (!zfs_dataset_exists(bdd->lbh->lzh, path, ZFS_TYPE_SNAPSHOT)) 225be7dd423SKyle Evans return (0); 226be7dd423SKyle Evans snap = zfs_open(bdd->lbh->lzh, path, ZFS_TYPE_SNAPSHOT); 227be7dd423SKyle Evans if (snap != NULL) 228be7dd423SKyle Evans zfs_destroy(snap, false); 229920abf4dSKyle Evans return (0); 230920abf4dSKyle Evans } 231920abf4dSKyle Evans 23228f16a0fSKyle Evans /* 23328f16a0fSKyle Evans * Destroy the boot environment or snapshot specified by the name 23428f16a0fSKyle Evans * parameter. Options are or'd together with the possible values: 23528f16a0fSKyle Evans * BE_DESTROY_FORCE : forces operation on mounted datasets 236be7dd423SKyle Evans * BE_DESTROY_ORIGIN: destroy the origin snapshot as well 23728f16a0fSKyle Evans */ 23828f16a0fSKyle Evans int 23973c3d608SKyle Evans be_destroy(libbe_handle_t *lbh, const char *name, int options) 24028f16a0fSKyle Evans { 241be7dd423SKyle Evans struct be_destroy_data bdd; 24213c62c50SKyle Evans char origin[BE_MAXPATHLEN], path[BE_MAXPATHLEN]; 24328f16a0fSKyle Evans zfs_handle_t *fs; 244be7dd423SKyle Evans char *snapdelim; 245bfe0869cSKyle Evans int err, force, mounted; 246be7dd423SKyle Evans size_t rootlen; 24728f16a0fSKyle Evans 248be7dd423SKyle Evans bdd.lbh = lbh; 249be7dd423SKyle Evans bdd.snapname = NULL; 250bfe0869cSKyle Evans force = options & BE_DESTROY_FORCE; 25113c62c50SKyle Evans *origin = '\0'; 25228f16a0fSKyle Evans 25328f16a0fSKyle Evans be_root_concat(lbh, name, path); 25428f16a0fSKyle Evans 255be7dd423SKyle Evans if ((snapdelim = strchr(path, '@')) == NULL) { 256bfe0869cSKyle Evans if (!zfs_dataset_exists(lbh->lzh, path, ZFS_TYPE_FILESYSTEM)) 25728f16a0fSKyle Evans return (set_error(lbh, BE_ERR_NOENT)); 25828f16a0fSKyle Evans 259f08dac4eSKyle Evans if (strcmp(path, lbh->rootfs) == 0 || 260f08dac4eSKyle Evans strcmp(path, lbh->bootfs) == 0) 26128f16a0fSKyle Evans return (set_error(lbh, BE_ERR_DESTROYACT)); 26228f16a0fSKyle Evans 263be7dd423SKyle Evans fs = zfs_open(lbh->lzh, path, ZFS_TYPE_FILESYSTEM); 26413c62c50SKyle Evans if (fs == NULL) 26513c62c50SKyle Evans return (set_error(lbh, BE_ERR_ZFSOPEN)); 266be7dd423SKyle Evans 26713c62c50SKyle Evans if ((options & BE_DESTROY_ORIGIN) != 0 && 26813c62c50SKyle Evans zfs_prop_get(fs, ZFS_PROP_ORIGIN, origin, sizeof(origin), 26913c62c50SKyle Evans NULL, NULL, 0, 1) != 0) 27013c62c50SKyle Evans return (set_error(lbh, BE_ERR_NOORIGIN)); 271e1ee6230SKyle Evans 272e1ee6230SKyle Evans /* Don't destroy a mounted dataset unless force is specified */ 273e1ee6230SKyle Evans if ((mounted = zfs_is_mounted(fs, NULL)) != 0) { 274e1ee6230SKyle Evans if (force) { 275e1ee6230SKyle Evans zfs_unmount(fs, NULL, 0); 276e1ee6230SKyle Evans } else { 277e1ee6230SKyle Evans free(bdd.snapname); 278e1ee6230SKyle Evans return (set_error(lbh, BE_ERR_DESTROYMNT)); 279e1ee6230SKyle Evans } 280e1ee6230SKyle Evans } 28128f16a0fSKyle Evans } else { 282bfe0869cSKyle Evans if (!zfs_dataset_exists(lbh->lzh, path, ZFS_TYPE_SNAPSHOT)) 28328f16a0fSKyle Evans return (set_error(lbh, BE_ERR_NOENT)); 28428f16a0fSKyle Evans 285be7dd423SKyle Evans bdd.snapname = strdup(snapdelim + 1); 286be7dd423SKyle Evans if (bdd.snapname == NULL) 287be7dd423SKyle Evans return (set_error(lbh, BE_ERR_NOMEM)); 288be7dd423SKyle Evans *snapdelim = '\0'; 289be7dd423SKyle Evans fs = zfs_open(lbh->lzh, path, ZFS_TYPE_DATASET); 290be7dd423SKyle Evans if (fs == NULL) { 291be7dd423SKyle Evans free(bdd.snapname); 29228f16a0fSKyle Evans return (set_error(lbh, BE_ERR_ZFSOPEN)); 29313c62c50SKyle Evans } 294be7dd423SKyle Evans } 29528f16a0fSKyle Evans 296be7dd423SKyle Evans err = be_destroy_cb(fs, &bdd); 297be7dd423SKyle Evans zfs_close(fs); 298be7dd423SKyle Evans free(bdd.snapname); 299be7dd423SKyle Evans if (err != 0) { 300920abf4dSKyle Evans /* Children are still present or the mount is referenced */ 301920abf4dSKyle Evans if (err == EBUSY) 302920abf4dSKyle Evans return (set_error(lbh, BE_ERR_DESTROYMNT)); 303920abf4dSKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 304920abf4dSKyle Evans } 30528f16a0fSKyle Evans 306be7dd423SKyle Evans if ((options & BE_DESTROY_ORIGIN) == 0) 307920abf4dSKyle Evans return (0); 30828f16a0fSKyle Evans 309be7dd423SKyle Evans /* The origin can't possibly be shorter than the BE root */ 310be7dd423SKyle Evans rootlen = strlen(lbh->root); 311be7dd423SKyle Evans if (*origin == '\0' || strlen(origin) <= rootlen + 1) 312be7dd423SKyle Evans return (set_error(lbh, BE_ERR_INVORIGIN)); 313be7dd423SKyle Evans 314be7dd423SKyle Evans /* 315be7dd423SKyle Evans * We'll be chopping off the BE root and running this back through 316be7dd423SKyle Evans * be_destroy, so that we properly handle the origin snapshot whether 317be7dd423SKyle Evans * it be that of a deep BE or not. 318be7dd423SKyle Evans */ 319be7dd423SKyle Evans if (strncmp(origin, lbh->root, rootlen) != 0 || origin[rootlen] != '/') 320be7dd423SKyle Evans return (0); 321be7dd423SKyle Evans 322be7dd423SKyle Evans return (be_destroy(lbh, origin + rootlen + 1, 323be7dd423SKyle Evans options & ~BE_DESTROY_ORIGIN)); 324be7dd423SKyle Evans } 32528f16a0fSKyle Evans 32690cf61e8SKyle Evans static void 32790cf61e8SKyle Evans be_setup_snapshot_name(libbe_handle_t *lbh, char *buf, size_t buflen) 32890cf61e8SKyle Evans { 32990cf61e8SKyle Evans time_t rawtime; 33090cf61e8SKyle Evans int len, serial; 33190cf61e8SKyle Evans 33290cf61e8SKyle Evans time(&rawtime); 33390cf61e8SKyle Evans len = strlen(buf); 33490cf61e8SKyle Evans len += strftime(buf + len, buflen - len, "@%F-%T", localtime(&rawtime)); 33590cf61e8SKyle Evans /* No room for serial... caller will do its best */ 33690cf61e8SKyle Evans if (buflen - len < 2) 33790cf61e8SKyle Evans return; 33890cf61e8SKyle Evans 33990cf61e8SKyle Evans for (serial = 0; serial < BE_SNAP_SERIAL_MAX; ++serial) { 34090cf61e8SKyle Evans snprintf(buf + len, buflen - len, "-%d", serial); 34190cf61e8SKyle Evans if (!zfs_dataset_exists(lbh->lzh, buf, ZFS_TYPE_SNAPSHOT)) 34290cf61e8SKyle Evans return; 34390cf61e8SKyle Evans } 34490cf61e8SKyle Evans } 34590cf61e8SKyle Evans 34628f16a0fSKyle Evans int 347b29bf2f8SKyle Evans be_snapshot(libbe_handle_t *lbh, const char *source, const char *snap_name, 34828f16a0fSKyle Evans bool recursive, char *result) 34928f16a0fSKyle Evans { 35028f16a0fSKyle Evans char buf[BE_MAXPATHLEN]; 35190cf61e8SKyle Evans int err; 35228f16a0fSKyle Evans 35328f16a0fSKyle Evans be_root_concat(lbh, source, buf); 35428f16a0fSKyle Evans 355162ec569SKyle Evans if ((err = be_exists(lbh, buf)) != 0) 356162ec569SKyle Evans return (set_error(lbh, err)); 35728f16a0fSKyle Evans 35828f16a0fSKyle Evans if (snap_name != NULL) { 359a8e44f4dSKyle Evans if (strlcat(buf, "@", sizeof(buf)) >= sizeof(buf)) 360a8e44f4dSKyle Evans return (set_error(lbh, BE_ERR_INVALIDNAME)); 361a8e44f4dSKyle Evans 362a8e44f4dSKyle Evans if (strlcat(buf, snap_name, sizeof(buf)) >= sizeof(buf)) 363a8e44f4dSKyle Evans return (set_error(lbh, BE_ERR_INVALIDNAME)); 364a8e44f4dSKyle Evans 365bfe0869cSKyle Evans if (result != NULL) 36628f16a0fSKyle Evans snprintf(result, BE_MAXPATHLEN, "%s@%s", source, 36728f16a0fSKyle Evans snap_name); 36828f16a0fSKyle Evans } else { 36990cf61e8SKyle Evans be_setup_snapshot_name(lbh, buf, sizeof(buf)); 37090cf61e8SKyle Evans 371a8e44f4dSKyle Evans if (result != NULL && strlcpy(result, strrchr(buf, '/') + 1, 372a8e44f4dSKyle Evans sizeof(buf)) >= sizeof(buf)) 373a8e44f4dSKyle Evans return (set_error(lbh, BE_ERR_INVALIDNAME)); 37428f16a0fSKyle Evans } 375b29bf2f8SKyle Evans if ((err = zfs_snapshot(lbh->lzh, buf, recursive, NULL)) != 0) { 37628f16a0fSKyle Evans switch (err) { 37728f16a0fSKyle Evans case EZFS_INVALIDNAME: 37828f16a0fSKyle Evans return (set_error(lbh, BE_ERR_INVALIDNAME)); 37928f16a0fSKyle Evans 38028f16a0fSKyle Evans default: 3812989df09SKyle Evans /* 3822989df09SKyle Evans * The other errors that zfs_ioc_snapshot might return 3832989df09SKyle Evans * shouldn't happen if we've set things up properly, so 3842989df09SKyle Evans * we'll gloss over them and call it UNKNOWN as it will 3852989df09SKyle Evans * require further triage. 3862989df09SKyle Evans */ 3872989df09SKyle Evans if (errno == ENOTSUP) 3882989df09SKyle Evans return (set_error(lbh, BE_ERR_NOPOOL)); 38928f16a0fSKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 39028f16a0fSKyle Evans } 39128f16a0fSKyle Evans } 39228f16a0fSKyle Evans 39328f16a0fSKyle Evans return (BE_ERR_SUCCESS); 39428f16a0fSKyle Evans } 39528f16a0fSKyle Evans 39628f16a0fSKyle Evans 39728f16a0fSKyle Evans /* 39828f16a0fSKyle Evans * Create the boot environment specified by the name parameter 39928f16a0fSKyle Evans */ 40028f16a0fSKyle Evans int 40173c3d608SKyle Evans be_create(libbe_handle_t *lbh, const char *name) 40228f16a0fSKyle Evans { 40328f16a0fSKyle Evans int err; 40428f16a0fSKyle Evans 405b29bf2f8SKyle Evans err = be_create_from_existing(lbh, name, be_active_path(lbh)); 40628f16a0fSKyle Evans 40728f16a0fSKyle Evans return (set_error(lbh, err)); 40828f16a0fSKyle Evans } 40928f16a0fSKyle Evans 41028f16a0fSKyle Evans static int 41128f16a0fSKyle Evans be_deep_clone_prop(int prop, void *cb) 41228f16a0fSKyle Evans { 41328f16a0fSKyle Evans int err; 414bfe0869cSKyle Evans struct libbe_dccb *dccb; 41528f16a0fSKyle Evans zprop_source_t src; 41628f16a0fSKyle Evans char pval[BE_MAXPATHLEN]; 41728f16a0fSKyle Evans char source[BE_MAXPATHLEN]; 418af43c24dSKyle Evans char *val; 41928f16a0fSKyle Evans 420bfe0869cSKyle Evans dccb = cb; 42128f16a0fSKyle Evans /* Skip some properties we don't want to touch */ 42273c3d608SKyle Evans if (prop == ZFS_PROP_CANMOUNT) 42328f16a0fSKyle Evans return (ZPROP_CONT); 42428f16a0fSKyle Evans 42528f16a0fSKyle Evans /* Don't copy readonly properties */ 426bfe0869cSKyle Evans if (zfs_prop_readonly(prop)) 42728f16a0fSKyle Evans return (ZPROP_CONT); 42828f16a0fSKyle Evans 42928f16a0fSKyle Evans if ((err = zfs_prop_get(dccb->zhp, prop, (char *)&pval, 430bfe0869cSKyle Evans sizeof(pval), &src, (char *)&source, sizeof(source), false))) 43128f16a0fSKyle Evans /* Just continue if we fail to read a property */ 43228f16a0fSKyle Evans return (ZPROP_CONT); 433bfe0869cSKyle Evans 434*be13d48cSKyle Evans /* 435*be13d48cSKyle Evans * Only copy locally defined or received properties. This continues 436*be13d48cSKyle Evans * to avoid temporary/default/local properties intentionally without 437*be13d48cSKyle Evans * breaking received datasets. 438*be13d48cSKyle Evans */ 439*be13d48cSKyle Evans if (src != ZPROP_SRC_LOCAL && src != ZPROP_SRC_RECEIVED) 44028f16a0fSKyle Evans return (ZPROP_CONT); 44128f16a0fSKyle Evans 442af43c24dSKyle Evans /* Augment mountpoint with altroot, if needed */ 443af43c24dSKyle Evans val = pval; 444fc13fc1cSKyle Evans if (prop == ZFS_PROP_MOUNTPOINT) 445fc13fc1cSKyle Evans val = be_mountpoint_augmented(dccb->lbh, val); 446fc13fc1cSKyle Evans 447af43c24dSKyle Evans nvlist_add_string(dccb->props, zfs_prop_to_name(prop), val); 44828f16a0fSKyle Evans 44928f16a0fSKyle Evans return (ZPROP_CONT); 45028f16a0fSKyle Evans } 45128f16a0fSKyle Evans 452fa30d9edSKyle Evans /* 453fa30d9edSKyle Evans * Return the corresponding boot environment path for a given 454fa30d9edSKyle Evans * dataset path, the constructed path is placed in 'result'. 455fa30d9edSKyle Evans * 456fa30d9edSKyle Evans * example: say our new boot environment name is 'bootenv' and 457fa30d9edSKyle Evans * the dataset path is 'zroot/ROOT/default/data/set'. 458fa30d9edSKyle Evans * 459fa30d9edSKyle Evans * result should produce: 'zroot/ROOT/bootenv/data/set' 460fa30d9edSKyle Evans */ 46128f16a0fSKyle Evans static int 462fa30d9edSKyle Evans be_get_path(struct libbe_deep_clone *ldc, const char *dspath, char *result, int result_size) 463fa30d9edSKyle Evans { 464fa30d9edSKyle Evans char *pos; 465fa30d9edSKyle Evans char *child_dataset; 466fa30d9edSKyle Evans 467fa30d9edSKyle Evans /* match the root path for the boot environments */ 468fa30d9edSKyle Evans pos = strstr(dspath, ldc->lbh->root); 469fa30d9edSKyle Evans 470fa30d9edSKyle Evans /* no match, different pools? */ 471fa30d9edSKyle Evans if (pos == NULL) 472fa30d9edSKyle Evans return (BE_ERR_BADPATH); 473fa30d9edSKyle Evans 474fa30d9edSKyle Evans /* root path of the new boot environment */ 475fa30d9edSKyle Evans snprintf(result, result_size, "%s/%s", ldc->lbh->root, ldc->bename); 476fa30d9edSKyle Evans 477fa30d9edSKyle Evans /* gets us to the parent dataset, the +1 consumes a trailing slash */ 478fa30d9edSKyle Evans pos += strlen(ldc->lbh->root) + 1; 479fa30d9edSKyle Evans 480fa30d9edSKyle Evans /* skip the parent dataset */ 481fa30d9edSKyle Evans if ((child_dataset = strchr(pos, '/')) != NULL) 482fa30d9edSKyle Evans strlcat(result, child_dataset, result_size); 483fa30d9edSKyle Evans 484fa30d9edSKyle Evans return (BE_ERR_SUCCESS); 485fa30d9edSKyle Evans } 486fa30d9edSKyle Evans 487fa30d9edSKyle Evans static int 488fa30d9edSKyle Evans be_clone_cb(zfs_handle_t *ds, void *data) 48928f16a0fSKyle Evans { 49028f16a0fSKyle Evans int err; 49128f16a0fSKyle Evans char be_path[BE_MAXPATHLEN]; 49228f16a0fSKyle Evans char snap_path[BE_MAXPATHLEN]; 49328f16a0fSKyle Evans const char *dspath; 49428f16a0fSKyle Evans zfs_handle_t *snap_hdl; 49528f16a0fSKyle Evans nvlist_t *props; 496fa30d9edSKyle Evans struct libbe_deep_clone *ldc; 49728f16a0fSKyle Evans struct libbe_dccb dccb; 49828f16a0fSKyle Evans 499fa30d9edSKyle Evans ldc = (struct libbe_deep_clone *)data; 50028f16a0fSKyle Evans dspath = zfs_get_name(ds); 501bfe0869cSKyle Evans 502fa30d9edSKyle Evans snprintf(snap_path, sizeof(snap_path), "%s@%s", dspath, ldc->snapname); 503bfe0869cSKyle Evans 504fa30d9edSKyle Evans /* construct the boot environment path from the dataset we're cloning */ 505fa30d9edSKyle Evans if (be_get_path(ldc, dspath, be_path, sizeof(be_path)) != BE_ERR_SUCCESS) 506fa30d9edSKyle Evans return (set_error(ldc->lbh, BE_ERR_UNKNOWN)); 50728f16a0fSKyle Evans 508fa30d9edSKyle Evans /* the dataset to be created (i.e. the boot environment) already exists */ 509fa30d9edSKyle Evans if (zfs_dataset_exists(ldc->lbh->lzh, be_path, ZFS_TYPE_DATASET)) 510fa30d9edSKyle Evans return (set_error(ldc->lbh, BE_ERR_EXISTS)); 511fa30d9edSKyle Evans 512fa30d9edSKyle Evans /* no snapshot found for this dataset, silently skip it */ 513fa30d9edSKyle Evans if (!zfs_dataset_exists(ldc->lbh->lzh, snap_path, ZFS_TYPE_SNAPSHOT)) 514fa30d9edSKyle Evans return (0); 51528f16a0fSKyle Evans 51628f16a0fSKyle Evans if ((snap_hdl = 517fa30d9edSKyle Evans zfs_open(ldc->lbh->lzh, snap_path, ZFS_TYPE_SNAPSHOT)) == NULL) 518fa30d9edSKyle Evans return (set_error(ldc->lbh, BE_ERR_ZFSOPEN)); 51928f16a0fSKyle Evans 52028f16a0fSKyle Evans nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP); 52128f16a0fSKyle Evans nvlist_add_string(props, "canmount", "noauto"); 52228f16a0fSKyle Evans 523fa30d9edSKyle Evans dccb.lbh = ldc->lbh; 52428f16a0fSKyle Evans dccb.zhp = ds; 52528f16a0fSKyle Evans dccb.props = props; 52628f16a0fSKyle Evans if (zprop_iter(be_deep_clone_prop, &dccb, B_FALSE, B_FALSE, 527bfe0869cSKyle Evans ZFS_TYPE_FILESYSTEM) == ZPROP_INVAL) 52828f16a0fSKyle Evans return (-1); 52928f16a0fSKyle Evans 530cc4deabcSKyle Evans if ((err = zfs_clone(snap_hdl, be_path, props)) != 0) 531fa30d9edSKyle Evans return (set_error(ldc->lbh, BE_ERR_ZFSCLONE)); 53228f16a0fSKyle Evans 53328f16a0fSKyle Evans nvlist_free(props); 53428f16a0fSKyle Evans zfs_close(snap_hdl); 53528f16a0fSKyle Evans 536fa30d9edSKyle Evans if (ldc->depth_limit == -1 || ldc->depth < ldc->depth_limit) { 537fa30d9edSKyle Evans ldc->depth++; 538fa30d9edSKyle Evans err = zfs_iter_filesystems(ds, be_clone_cb, ldc); 539fa30d9edSKyle Evans ldc->depth--; 540fa30d9edSKyle Evans } 541cc4deabcSKyle Evans 542fa30d9edSKyle Evans return (set_error(ldc->lbh, err)); 54328f16a0fSKyle Evans } 54428f16a0fSKyle Evans 54528f16a0fSKyle Evans /* 546fa30d9edSKyle Evans * Create a boot environment with a given name from a given snapshot. 547fa30d9edSKyle Evans * Snapshots can be in the format 'zroot/ROOT/default@snapshot' or 548fa30d9edSKyle Evans * 'default@snapshot'. In the latter case, 'default@snapshot' will be prepended 549fa30d9edSKyle Evans * with the root path that libbe was initailized with. 55028f16a0fSKyle Evans */ 551fa30d9edSKyle Evans static int 552fa30d9edSKyle Evans be_clone(libbe_handle_t *lbh, const char *bename, const char *snapshot, int depth) 55328f16a0fSKyle Evans { 55428f16a0fSKyle Evans int err; 55528f16a0fSKyle Evans char snap_path[BE_MAXPATHLEN]; 556b29bf2f8SKyle Evans char *parentname, *snapname; 55728f16a0fSKyle Evans zfs_handle_t *parent_hdl; 558fa30d9edSKyle Evans struct libbe_deep_clone ldc; 55928f16a0fSKyle Evans 560fa30d9edSKyle Evans /* ensure the boot environment name is valid */ 561fa30d9edSKyle Evans if ((err = be_validate_name(lbh, bename)) != 0) 56228f16a0fSKyle Evans return (set_error(lbh, err)); 563fa30d9edSKyle Evans 564fa30d9edSKyle Evans /* 565fa30d9edSKyle Evans * prepend the boot environment root path if we're 566fa30d9edSKyle Evans * given a partial snapshot name. 567fa30d9edSKyle Evans */ 568fa30d9edSKyle Evans if ((err = be_root_concat(lbh, snapshot, snap_path)) != 0) 56928f16a0fSKyle Evans return (set_error(lbh, err)); 570fa30d9edSKyle Evans 571fa30d9edSKyle Evans /* ensure the snapshot exists */ 572b29bf2f8SKyle Evans if ((err = be_validate_snap(lbh, snap_path)) != 0) 57328f16a0fSKyle Evans return (set_error(lbh, err)); 57428f16a0fSKyle Evans 575fa30d9edSKyle Evans /* get a copy of the snapshot path so we can disect it */ 576cc4deabcSKyle Evans if ((parentname = strdup(snap_path)) == NULL) 577cc4deabcSKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 578cc4deabcSKyle Evans 579fa30d9edSKyle Evans /* split dataset name from snapshot name */ 58028f16a0fSKyle Evans snapname = strchr(parentname, '@'); 58128f16a0fSKyle Evans if (snapname == NULL) { 582cc4deabcSKyle Evans free(parentname); 583cc4deabcSKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 58428f16a0fSKyle Evans } 58528f16a0fSKyle Evans *snapname = '\0'; 58628f16a0fSKyle Evans snapname++; 58728f16a0fSKyle Evans 588fa30d9edSKyle Evans /* set-up the boot environment */ 589fa30d9edSKyle Evans ldc.lbh = lbh; 590fa30d9edSKyle Evans ldc.bename = bename; 591fa30d9edSKyle Evans ldc.snapname = snapname; 592fa30d9edSKyle Evans ldc.depth = 0; 593fa30d9edSKyle Evans ldc.depth_limit = depth; 59428f16a0fSKyle Evans 595fa30d9edSKyle Evans /* the boot environment will be cloned from this dataset */ 59628f16a0fSKyle Evans parent_hdl = zfs_open(lbh->lzh, parentname, ZFS_TYPE_DATASET); 597fa30d9edSKyle Evans 598fa30d9edSKyle Evans /* create the boot environment */ 599fa30d9edSKyle Evans err = be_clone_cb(parent_hdl, &ldc); 60028f16a0fSKyle Evans 601cc4deabcSKyle Evans free(parentname); 60228f16a0fSKyle Evans return (set_error(lbh, err)); 60328f16a0fSKyle Evans } 60428f16a0fSKyle Evans 605fa30d9edSKyle Evans /* 606fa30d9edSKyle Evans * Create a boot environment from pre-existing snapshot, specifying a depth. 607fa30d9edSKyle Evans */ 608fa30d9edSKyle Evans int be_create_depth(libbe_handle_t *lbh, const char *bename, 609fa30d9edSKyle Evans const char *snap, int depth) 610fa30d9edSKyle Evans { 611fa30d9edSKyle Evans return (be_clone(lbh, bename, snap, depth)); 612fa30d9edSKyle Evans } 613fa30d9edSKyle Evans 614fa30d9edSKyle Evans /* 615fa30d9edSKyle Evans * Create the boot environment from pre-existing snapshot 616fa30d9edSKyle Evans */ 617fa30d9edSKyle Evans int 618fa30d9edSKyle Evans be_create_from_existing_snap(libbe_handle_t *lbh, const char *bename, 619fa30d9edSKyle Evans const char *snap) 620fa30d9edSKyle Evans { 621fa30d9edSKyle Evans return (be_clone(lbh, bename, snap, -1)); 622fa30d9edSKyle Evans } 623fa30d9edSKyle Evans 62428f16a0fSKyle Evans 62528f16a0fSKyle Evans /* 62628f16a0fSKyle Evans * Create a boot environment from an existing boot environment 62728f16a0fSKyle Evans */ 62828f16a0fSKyle Evans int 629fa30d9edSKyle Evans be_create_from_existing(libbe_handle_t *lbh, const char *bename, const char *old) 63028f16a0fSKyle Evans { 63128f16a0fSKyle Evans int err; 632fa30d9edSKyle Evans char snap[BE_MAXPATHLEN]; 63328f16a0fSKyle Evans 634fa30d9edSKyle Evans if ((err = be_snapshot(lbh, old, NULL, true, snap)) != 0) 63528f16a0fSKyle Evans return (set_error(lbh, err)); 63628f16a0fSKyle Evans 637fa30d9edSKyle Evans err = be_clone(lbh, bename, snap, -1); 63828f16a0fSKyle Evans 63928f16a0fSKyle Evans return (set_error(lbh, err)); 64028f16a0fSKyle Evans } 64128f16a0fSKyle Evans 64228f16a0fSKyle Evans 64328f16a0fSKyle Evans /* 64428f16a0fSKyle Evans * Verifies that a snapshot has a valid name, exists, and has a mountpoint of 64528f16a0fSKyle Evans * '/'. Returns BE_ERR_SUCCESS (0), upon success, or the relevant BE_ERR_* upon 64628f16a0fSKyle Evans * failure. Does not set the internal library error state. 64728f16a0fSKyle Evans */ 64828f16a0fSKyle Evans int 649b29bf2f8SKyle Evans be_validate_snap(libbe_handle_t *lbh, const char *snap_name) 65028f16a0fSKyle Evans { 65128f16a0fSKyle Evans 652bfe0869cSKyle Evans if (strlen(snap_name) >= BE_MAXPATHLEN) 65328f16a0fSKyle Evans return (BE_ERR_PATHLEN); 65428f16a0fSKyle Evans 655fcb47c42SKyle Evans if (!zfs_name_valid(snap_name, ZFS_TYPE_SNAPSHOT)) 656fcb47c42SKyle Evans return (BE_ERR_INVALIDNAME); 657fcb47c42SKyle Evans 65828f16a0fSKyle Evans if (!zfs_dataset_exists(lbh->lzh, snap_name, 659bfe0869cSKyle Evans ZFS_TYPE_SNAPSHOT)) 66028f16a0fSKyle Evans return (BE_ERR_NOENT); 66128f16a0fSKyle Evans 66251aecc89SKyle Evans return (BE_ERR_SUCCESS); 66328f16a0fSKyle Evans } 66428f16a0fSKyle Evans 66528f16a0fSKyle Evans 66628f16a0fSKyle Evans /* 66728f16a0fSKyle Evans * Idempotently appends the name argument to the root boot environment path 66828f16a0fSKyle Evans * and copies the resulting string into the result buffer (which is assumed 66928f16a0fSKyle Evans * to be at least BE_MAXPATHLEN characters long. Returns BE_ERR_SUCCESS upon 67028f16a0fSKyle Evans * success, BE_ERR_PATHLEN if the resulting path is longer than BE_MAXPATHLEN, 67128f16a0fSKyle Evans * or BE_ERR_INVALIDNAME if the name is a path that does not begin with 67228f16a0fSKyle Evans * zfs_be_root. Does not set internal library error state. 67328f16a0fSKyle Evans */ 67428f16a0fSKyle Evans int 675b29bf2f8SKyle Evans be_root_concat(libbe_handle_t *lbh, const char *name, char *result) 67628f16a0fSKyle Evans { 67728f16a0fSKyle Evans size_t name_len, root_len; 67828f16a0fSKyle Evans 67928f16a0fSKyle Evans name_len = strlen(name); 68028f16a0fSKyle Evans root_len = strlen(lbh->root); 68128f16a0fSKyle Evans 68228f16a0fSKyle Evans /* Act idempotently; return be name if it is already a full path */ 68328f16a0fSKyle Evans if (strrchr(name, '/') != NULL) { 684bfe0869cSKyle Evans if (strstr(name, lbh->root) != name) 68528f16a0fSKyle Evans return (BE_ERR_INVALIDNAME); 68628f16a0fSKyle Evans 687bfe0869cSKyle Evans if (name_len >= BE_MAXPATHLEN) 68828f16a0fSKyle Evans return (BE_ERR_PATHLEN); 68928f16a0fSKyle Evans 69055b0e92bSKyle Evans strlcpy(result, name, BE_MAXPATHLEN); 69128f16a0fSKyle Evans return (BE_ERR_SUCCESS); 69228f16a0fSKyle Evans } else if (name_len + root_len + 1 < BE_MAXPATHLEN) { 69328f16a0fSKyle Evans snprintf(result, BE_MAXPATHLEN, "%s/%s", lbh->root, 69428f16a0fSKyle Evans name); 69528f16a0fSKyle Evans return (BE_ERR_SUCCESS); 69628f16a0fSKyle Evans } 69728f16a0fSKyle Evans 69828f16a0fSKyle Evans return (BE_ERR_PATHLEN); 69928f16a0fSKyle Evans } 70028f16a0fSKyle Evans 70128f16a0fSKyle Evans 70228f16a0fSKyle Evans /* 70328f16a0fSKyle Evans * Verifies the validity of a boot environment name (A-Za-z0-9-_.). Returns 7045b7803a9SKyle Evans * BE_ERR_SUCCESS (0) if name is valid, otherwise returns BE_ERR_INVALIDNAME 7055b7803a9SKyle Evans * or BE_ERR_PATHLEN. 70628f16a0fSKyle Evans * Does not set internal library error state. 70728f16a0fSKyle Evans */ 70828f16a0fSKyle Evans int 7095b7803a9SKyle Evans be_validate_name(libbe_handle_t *lbh, const char *name) 71028f16a0fSKyle Evans { 71128f16a0fSKyle Evans 7125b7803a9SKyle Evans /* 7135b7803a9SKyle Evans * Impose the additional restriction that the entire dataset name must 7145b7803a9SKyle Evans * not exceed the maximum length of a dataset, i.e. MAXNAMELEN. 7155b7803a9SKyle Evans */ 7165b7803a9SKyle Evans if (strlen(lbh->root) + 1 + strlen(name) > MAXNAMELEN) 7175b7803a9SKyle Evans return (BE_ERR_PATHLEN); 718fcb47c42SKyle Evans 719fcb47c42SKyle Evans if (!zfs_name_valid(name, ZFS_TYPE_DATASET)) 720fcb47c42SKyle Evans return (BE_ERR_INVALIDNAME); 721fcb47c42SKyle Evans 72228f16a0fSKyle Evans return (BE_ERR_SUCCESS); 72328f16a0fSKyle Evans } 72428f16a0fSKyle Evans 72528f16a0fSKyle Evans 72628f16a0fSKyle Evans /* 72728f16a0fSKyle Evans * usage 72828f16a0fSKyle Evans */ 72928f16a0fSKyle Evans int 73073c3d608SKyle Evans be_rename(libbe_handle_t *lbh, const char *old, const char *new) 73128f16a0fSKyle Evans { 73228f16a0fSKyle Evans char full_old[BE_MAXPATHLEN]; 73328f16a0fSKyle Evans char full_new[BE_MAXPATHLEN]; 73428f16a0fSKyle Evans zfs_handle_t *zfs_hdl; 73528f16a0fSKyle Evans int err; 73628f16a0fSKyle Evans 7375b7803a9SKyle Evans /* 7385b7803a9SKyle Evans * be_validate_name is documented not to set error state, so we should 7395b7803a9SKyle Evans * do so here. 7405b7803a9SKyle Evans */ 7415b7803a9SKyle Evans if ((err = be_validate_name(lbh, new)) != 0) 7425b7803a9SKyle Evans return (set_error(lbh, err)); 743b29bf2f8SKyle Evans if ((err = be_root_concat(lbh, old, full_old)) != 0) 74428f16a0fSKyle Evans return (set_error(lbh, err)); 745b29bf2f8SKyle Evans if ((err = be_root_concat(lbh, new, full_new)) != 0) 74628f16a0fSKyle Evans return (set_error(lbh, err)); 74728f16a0fSKyle Evans 748bfe0869cSKyle Evans if (!zfs_dataset_exists(lbh->lzh, full_old, ZFS_TYPE_DATASET)) 7492989df09SKyle Evans return (set_error(lbh, BE_ERR_NOENT)); 75028f16a0fSKyle Evans 751bfe0869cSKyle Evans if (zfs_dataset_exists(lbh->lzh, full_new, ZFS_TYPE_DATASET)) 7522989df09SKyle Evans return (set_error(lbh, BE_ERR_EXISTS)); 75328f16a0fSKyle Evans 75428f16a0fSKyle Evans if ((zfs_hdl = zfs_open(lbh->lzh, full_old, 755bfe0869cSKyle Evans ZFS_TYPE_FILESYSTEM)) == NULL) 7562989df09SKyle Evans return (set_error(lbh, BE_ERR_ZFSOPEN)); 75728f16a0fSKyle Evans 758bfe0869cSKyle Evans /* recurse, nounmount, forceunmount */ 7598369ba42SKyle Evans struct renameflags flags = { 7608369ba42SKyle Evans .nounmount = 1, 7618369ba42SKyle Evans }; 76228f16a0fSKyle Evans 76328f16a0fSKyle Evans err = zfs_rename(zfs_hdl, NULL, full_new, flags); 76428f16a0fSKyle Evans 76528f16a0fSKyle Evans zfs_close(zfs_hdl); 7665b7803a9SKyle Evans if (err != 0) 7675b7803a9SKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 7685b7803a9SKyle Evans return (0); 76928f16a0fSKyle Evans } 77028f16a0fSKyle Evans 77128f16a0fSKyle Evans 77228f16a0fSKyle Evans int 77373c3d608SKyle Evans be_export(libbe_handle_t *lbh, const char *bootenv, int fd) 77428f16a0fSKyle Evans { 77528f16a0fSKyle Evans char snap_name[BE_MAXPATHLEN]; 77628f16a0fSKyle Evans char buf[BE_MAXPATHLEN]; 77728f16a0fSKyle Evans zfs_handle_t *zfs; 77828f16a0fSKyle Evans int err; 77928f16a0fSKyle Evans 780b29bf2f8SKyle Evans if ((err = be_snapshot(lbh, bootenv, NULL, true, snap_name)) != 0) 7816d4b1d24SKyle Evans /* Use the error set by be_snapshot */ 7826d4b1d24SKyle Evans return (err); 78328f16a0fSKyle Evans 78428f16a0fSKyle Evans be_root_concat(lbh, snap_name, buf); 78528f16a0fSKyle Evans 786bfe0869cSKyle Evans if ((zfs = zfs_open(lbh->lzh, buf, ZFS_TYPE_DATASET)) == NULL) 787506f5fdfSKyle Evans return (set_error(lbh, BE_ERR_ZFSOPEN)); 78828f16a0fSKyle Evans 78928f16a0fSKyle Evans err = zfs_send_one(zfs, NULL, fd, 0); 7906d4b1d24SKyle Evans zfs_close(zfs); 7916d4b1d24SKyle Evans 79228f16a0fSKyle Evans return (err); 79328f16a0fSKyle Evans } 79428f16a0fSKyle Evans 79528f16a0fSKyle Evans 79628f16a0fSKyle Evans int 79773c3d608SKyle Evans be_import(libbe_handle_t *lbh, const char *bootenv, int fd) 79828f16a0fSKyle Evans { 79928f16a0fSKyle Evans char buf[BE_MAXPATHLEN]; 80028f16a0fSKyle Evans nvlist_t *props; 80128f16a0fSKyle Evans zfs_handle_t *zfs; 80216ac0705SKyle Evans recvflags_t flags = { .nomount = 1 }; 80316ac0705SKyle Evans int err; 80428f16a0fSKyle Evans 80516ac0705SKyle Evans be_root_concat(lbh, bootenv, buf); 80628f16a0fSKyle Evans 80716ac0705SKyle Evans if ((err = zfs_receive(lbh->lzh, buf, NULL, &flags, fd, NULL)) != 0) { 808506f5fdfSKyle Evans switch (err) { 809506f5fdfSKyle Evans case EINVAL: 810506f5fdfSKyle Evans return (set_error(lbh, BE_ERR_NOORIGIN)); 811506f5fdfSKyle Evans case ENOENT: 812506f5fdfSKyle Evans return (set_error(lbh, BE_ERR_NOENT)); 813506f5fdfSKyle Evans case EIO: 814506f5fdfSKyle Evans return (set_error(lbh, BE_ERR_IO)); 815506f5fdfSKyle Evans default: 816506f5fdfSKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 817506f5fdfSKyle Evans } 81828f16a0fSKyle Evans } 81928f16a0fSKyle Evans 82016ac0705SKyle Evans if ((zfs = zfs_open(lbh->lzh, buf, ZFS_TYPE_FILESYSTEM)) == NULL) 821506f5fdfSKyle Evans return (set_error(lbh, BE_ERR_ZFSOPEN)); 82228f16a0fSKyle Evans 82328f16a0fSKyle Evans nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP); 82428f16a0fSKyle Evans nvlist_add_string(props, "canmount", "noauto"); 82528f16a0fSKyle Evans nvlist_add_string(props, "mountpoint", "/"); 82628f16a0fSKyle Evans 82716ac0705SKyle Evans err = zfs_prop_set_list(zfs, props); 82828f16a0fSKyle Evans nvlist_free(props); 82928f16a0fSKyle Evans 8301b057aacSKyle Evans zfs_close(zfs); 8311b057aacSKyle Evans 8321b057aacSKyle Evans if (err != 0) 8331b057aacSKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 8341b057aacSKyle Evans 83516ac0705SKyle Evans return (0); 83628f16a0fSKyle Evans } 83728f16a0fSKyle Evans 8383d1a1f2cSKyle Evans #if SOON 839c65a2111SKyle Evans static int 840c65a2111SKyle Evans be_create_child_noent(libbe_handle_t *lbh, const char *active, 841c65a2111SKyle Evans const char *child_path) 842c65a2111SKyle Evans { 843c65a2111SKyle Evans nvlist_t *props; 844c65a2111SKyle Evans zfs_handle_t *zfs; 845c65a2111SKyle Evans int err; 846c65a2111SKyle Evans 847c65a2111SKyle Evans nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP); 848c65a2111SKyle Evans nvlist_add_string(props, "canmount", "noauto"); 849c65a2111SKyle Evans nvlist_add_string(props, "mountpoint", child_path); 850c65a2111SKyle Evans 851c65a2111SKyle Evans /* Create */ 852c65a2111SKyle Evans if ((err = zfs_create(lbh->lzh, active, ZFS_TYPE_DATASET, 853c65a2111SKyle Evans props)) != 0) { 854c65a2111SKyle Evans switch (err) { 855c65a2111SKyle Evans case EZFS_EXISTS: 856c65a2111SKyle Evans return (set_error(lbh, BE_ERR_EXISTS)); 857c65a2111SKyle Evans case EZFS_NOENT: 858c65a2111SKyle Evans return (set_error(lbh, BE_ERR_NOENT)); 859c65a2111SKyle Evans case EZFS_BADTYPE: 860c65a2111SKyle Evans case EZFS_BADVERSION: 861c65a2111SKyle Evans return (set_error(lbh, BE_ERR_NOPOOL)); 862c65a2111SKyle Evans case EZFS_BADPROP: 863c65a2111SKyle Evans default: 864c65a2111SKyle Evans /* We set something up wrong, probably... */ 865c65a2111SKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 866c65a2111SKyle Evans } 867c65a2111SKyle Evans } 868c65a2111SKyle Evans nvlist_free(props); 869c65a2111SKyle Evans 870c65a2111SKyle Evans if ((zfs = zfs_open(lbh->lzh, active, ZFS_TYPE_DATASET)) == NULL) 871c65a2111SKyle Evans return (set_error(lbh, BE_ERR_ZFSOPEN)); 872c65a2111SKyle Evans 873c65a2111SKyle Evans /* Set props */ 874c65a2111SKyle Evans if ((err = zfs_prop_set(zfs, "canmount", "noauto")) != 0) { 875c65a2111SKyle Evans zfs_close(zfs); 876c65a2111SKyle Evans /* 877c65a2111SKyle Evans * Similar to other cases, this shouldn't fail unless we've 878c65a2111SKyle Evans * done something wrong. This is a new dataset that shouldn't 879c65a2111SKyle Evans * have been mounted anywhere between creation and now. 880c65a2111SKyle Evans */ 881c65a2111SKyle Evans if (err == EZFS_NOMEM) 882c65a2111SKyle Evans return (set_error(lbh, BE_ERR_NOMEM)); 883c65a2111SKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 884c65a2111SKyle Evans } 885c65a2111SKyle Evans zfs_close(zfs); 886c65a2111SKyle Evans return (BE_ERR_SUCCESS); 887c65a2111SKyle Evans } 888c65a2111SKyle Evans 889c65a2111SKyle Evans static int 890c65a2111SKyle Evans be_create_child_cloned(libbe_handle_t *lbh, const char *active) 891c65a2111SKyle Evans { 8923d1a1f2cSKyle Evans char buf[BE_MAXPATHLEN], tmp[BE_MAXPATHLEN];; 893c65a2111SKyle Evans zfs_handle_t *zfs; 894c65a2111SKyle Evans int err; 895c65a2111SKyle Evans 896c65a2111SKyle Evans /* XXX TODO ? */ 897c65a2111SKyle Evans 898c65a2111SKyle Evans /* 899c65a2111SKyle Evans * Establish if the existing path is a zfs dataset or just 900c65a2111SKyle Evans * the subdirectory of one 901c65a2111SKyle Evans */ 9023d1a1f2cSKyle Evans strlcpy(tmp, "tmp/be_snap.XXXXX", sizeof(tmp)); 9033d1a1f2cSKyle Evans if (mktemp(tmp) == NULL) 904c65a2111SKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 905c65a2111SKyle Evans 9063d1a1f2cSKyle Evans be_root_concat(lbh, tmp, buf); 9073d1a1f2cSKyle Evans printf("Here %s?\n", buf); 908c65a2111SKyle Evans if ((err = zfs_snapshot(lbh->lzh, buf, false, NULL)) != 0) { 909c65a2111SKyle Evans switch (err) { 910c65a2111SKyle Evans case EZFS_INVALIDNAME: 911c65a2111SKyle Evans return (set_error(lbh, BE_ERR_INVALIDNAME)); 912c65a2111SKyle Evans 913c65a2111SKyle Evans default: 914c65a2111SKyle Evans /* 915c65a2111SKyle Evans * The other errors that zfs_ioc_snapshot might return 916c65a2111SKyle Evans * shouldn't happen if we've set things up properly, so 917c65a2111SKyle Evans * we'll gloss over them and call it UNKNOWN as it will 918c65a2111SKyle Evans * require further triage. 919c65a2111SKyle Evans */ 920c65a2111SKyle Evans if (errno == ENOTSUP) 921c65a2111SKyle Evans return (set_error(lbh, BE_ERR_NOPOOL)); 922c65a2111SKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 923c65a2111SKyle Evans } 924c65a2111SKyle Evans } 925c65a2111SKyle Evans 926c65a2111SKyle Evans /* Clone */ 927c65a2111SKyle Evans if ((zfs = zfs_open(lbh->lzh, buf, ZFS_TYPE_SNAPSHOT)) == NULL) 928c65a2111SKyle Evans return (BE_ERR_ZFSOPEN); 929c65a2111SKyle Evans 930c65a2111SKyle Evans if ((err = zfs_clone(zfs, active, NULL)) != 0) 931c65a2111SKyle Evans /* XXX TODO correct error */ 932c65a2111SKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 933c65a2111SKyle Evans 934c65a2111SKyle Evans /* set props */ 935c65a2111SKyle Evans zfs_close(zfs); 936c65a2111SKyle Evans return (BE_ERR_SUCCESS); 937c65a2111SKyle Evans } 93828f16a0fSKyle Evans 93928f16a0fSKyle Evans int 94073c3d608SKyle Evans be_add_child(libbe_handle_t *lbh, const char *child_path, bool cp_if_exists) 94128f16a0fSKyle Evans { 94273c3d608SKyle Evans struct stat sb; 943c65a2111SKyle Evans char active[BE_MAXPATHLEN], buf[BE_MAXPATHLEN]; 94428f16a0fSKyle Evans nvlist_t *props; 94573c3d608SKyle Evans const char *s; 94628f16a0fSKyle Evans 94728f16a0fSKyle Evans /* Require absolute paths */ 948bfe0869cSKyle Evans if (*child_path != '/') 9496d4b1d24SKyle Evans return (set_error(lbh, BE_ERR_BADPATH)); 95028f16a0fSKyle Evans 9516d4b1d24SKyle Evans strlcpy(active, be_active_path(lbh), BE_MAXPATHLEN); 95228f16a0fSKyle Evans strcpy(buf, active); 95328f16a0fSKyle Evans 95428f16a0fSKyle Evans /* Create non-mountable parent dataset(s) */ 95573c3d608SKyle Evans s = child_path; 95628f16a0fSKyle Evans for (char *p; (p = strchr(s+1, '/')) != NULL; s = p) { 95728f16a0fSKyle Evans size_t len = p - s; 95828f16a0fSKyle Evans strncat(buf, s, len); 95928f16a0fSKyle Evans 96028f16a0fSKyle Evans nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP); 96128f16a0fSKyle Evans nvlist_add_string(props, "canmount", "off"); 96228f16a0fSKyle Evans nvlist_add_string(props, "mountpoint", "none"); 96328f16a0fSKyle Evans zfs_create(lbh->lzh, buf, ZFS_TYPE_DATASET, props); 96428f16a0fSKyle Evans nvlist_free(props); 96528f16a0fSKyle Evans } 96628f16a0fSKyle Evans 96728f16a0fSKyle Evans /* Path does not exist as a descendent of / yet */ 9686d4b1d24SKyle Evans if (strlcat(active, child_path, BE_MAXPATHLEN) >= BE_MAXPATHLEN) 9696d4b1d24SKyle Evans return (set_error(lbh, BE_ERR_PATHLEN)); 97028f16a0fSKyle Evans 97128f16a0fSKyle Evans if (stat(child_path, &sb) != 0) { 97228f16a0fSKyle Evans /* Verify that error is ENOENT */ 9736d4b1d24SKyle Evans if (errno != ENOENT) 9746d4b1d24SKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 975c65a2111SKyle Evans return (be_create_child_noent(lbh, active, child_path)); 976c65a2111SKyle Evans } else if (cp_if_exists) 97728f16a0fSKyle Evans /* Path is already a descendent of / and should be copied */ 978c65a2111SKyle Evans return (be_create_child_cloned(lbh, active)); 9796d4b1d24SKyle Evans return (set_error(lbh, BE_ERR_EXISTS)); 98028f16a0fSKyle Evans } 9813d1a1f2cSKyle Evans #endif /* SOON */ 98228f16a0fSKyle Evans 983d06f7103SKyle Evans static int 984d06f7103SKyle Evans be_set_nextboot(libbe_handle_t *lbh, nvlist_t *config, uint64_t pool_guid, 985d06f7103SKyle Evans const char *zfsdev) 986d06f7103SKyle Evans { 987d06f7103SKyle Evans nvlist_t **child; 988d06f7103SKyle Evans uint64_t vdev_guid; 989d06f7103SKyle Evans int c, children; 990d06f7103SKyle Evans 991d06f7103SKyle Evans if (nvlist_lookup_nvlist_array(config, ZPOOL_CONFIG_CHILDREN, &child, 992d06f7103SKyle Evans &children) == 0) { 993d06f7103SKyle Evans for (c = 0; c < children; ++c) 994d06f7103SKyle Evans if (be_set_nextboot(lbh, child[c], pool_guid, zfsdev) != 0) 995d06f7103SKyle Evans return (1); 996d06f7103SKyle Evans return (0); 997d06f7103SKyle Evans } 998d06f7103SKyle Evans 999d06f7103SKyle Evans if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_GUID, 1000d06f7103SKyle Evans &vdev_guid) != 0) { 1001d06f7103SKyle Evans return (1); 1002d06f7103SKyle Evans } 1003d06f7103SKyle Evans 1004d06f7103SKyle Evans if (zpool_nextboot(lbh->lzh, pool_guid, vdev_guid, zfsdev) != 0) { 1005d06f7103SKyle Evans perror("ZFS_IOC_NEXTBOOT failed"); 1006d06f7103SKyle Evans return (1); 1007d06f7103SKyle Evans } 1008d06f7103SKyle Evans 1009d06f7103SKyle Evans return (0); 1010d06f7103SKyle Evans } 1011d06f7103SKyle Evans 10128d4ce358SKyle Evans /* 10138d4ce358SKyle Evans * Deactivate old BE dataset; currently just sets canmount=noauto 10148d4ce358SKyle Evans */ 10158d4ce358SKyle Evans static int 10168d4ce358SKyle Evans be_deactivate(libbe_handle_t *lbh, const char *ds) 10178d4ce358SKyle Evans { 10188d4ce358SKyle Evans zfs_handle_t *zfs; 10198d4ce358SKyle Evans 10208d4ce358SKyle Evans if ((zfs = zfs_open(lbh->lzh, ds, ZFS_TYPE_DATASET)) == NULL) 10218d4ce358SKyle Evans return (1); 10228d4ce358SKyle Evans if (zfs_prop_set(zfs, "canmount", "noauto") != 0) 10238d4ce358SKyle Evans return (1); 10248d4ce358SKyle Evans zfs_close(zfs); 10258d4ce358SKyle Evans return (0); 10268d4ce358SKyle Evans } 102728f16a0fSKyle Evans 102828f16a0fSKyle Evans int 102973c3d608SKyle Evans be_activate(libbe_handle_t *lbh, const char *bootenv, bool temporary) 103028f16a0fSKyle Evans { 103128f16a0fSKyle Evans char be_path[BE_MAXPATHLEN]; 103228f16a0fSKyle Evans char buf[BE_MAXPATHLEN]; 10334635676dSKyle Evans nvlist_t *config, *dsprops, *vdevs; 10344635676dSKyle Evans char *origin; 10350cadc427SKyle Evans uint64_t pool_guid; 10360cadc427SKyle Evans zfs_handle_t *zhp; 103728f16a0fSKyle Evans int err; 103828f16a0fSKyle Evans 103928f16a0fSKyle Evans be_root_concat(lbh, bootenv, be_path); 104028f16a0fSKyle Evans 104128f16a0fSKyle Evans /* Note: be_exists fails if mountpoint is not / */ 1042162ec569SKyle Evans if ((err = be_exists(lbh, be_path)) != 0) 1043162ec569SKyle Evans return (set_error(lbh, err)); 104428f16a0fSKyle Evans 104528f16a0fSKyle Evans if (temporary) { 1046d06f7103SKyle Evans config = zpool_get_config(lbh->active_phandle, NULL); 1047c65a2111SKyle Evans if (config == NULL) 1048c65a2111SKyle Evans /* config should be fetchable... */ 1049c65a2111SKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 105028f16a0fSKyle Evans 1051d06f7103SKyle Evans if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, 1052d06f7103SKyle Evans &pool_guid) != 0) 1053c65a2111SKyle Evans /* Similarly, it shouldn't be possible */ 1054c65a2111SKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 1055d06f7103SKyle Evans 105628f16a0fSKyle Evans /* Expected format according to zfsbootcfg(8) man */ 1057a8e44f4dSKyle Evans snprintf(buf, sizeof(buf), "zfs:%s:", be_path); 105828f16a0fSKyle Evans 1059c65a2111SKyle Evans /* We have no config tree */ 1060c65a2111SKyle Evans if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, 1061c65a2111SKyle Evans &vdevs) != 0) 1062c65a2111SKyle Evans return (set_error(lbh, BE_ERR_NOPOOL)); 106328f16a0fSKyle Evans 1064d06f7103SKyle Evans return (be_set_nextboot(lbh, vdevs, pool_guid, buf)); 106528f16a0fSKyle Evans } else { 10668d4ce358SKyle Evans if (be_deactivate(lbh, lbh->bootfs) != 0) 10678d4ce358SKyle Evans return (-1); 10688d4ce358SKyle Evans 106928f16a0fSKyle Evans /* Obtain bootenv zpool */ 1070c3a34c08SKyle Evans err = zpool_set_prop(lbh->active_phandle, "bootfs", be_path); 10710cadc427SKyle Evans if (err) 10720cadc427SKyle Evans return (-1); 107328f16a0fSKyle Evans 10740cadc427SKyle Evans zhp = zfs_open(lbh->lzh, be_path, ZFS_TYPE_FILESYSTEM); 10750cadc427SKyle Evans if (zhp == NULL) 10760cadc427SKyle Evans return (-1); 107728f16a0fSKyle Evans 10784635676dSKyle Evans if (be_prop_list_alloc(&dsprops) != 0) 10794635676dSKyle Evans return (-1); 10804635676dSKyle Evans 10814635676dSKyle Evans if (be_get_dataset_props(lbh, be_path, dsprops) != 0) { 10824635676dSKyle Evans nvlist_free(dsprops); 10834635676dSKyle Evans return (-1); 10844635676dSKyle Evans } 10854635676dSKyle Evans 10864635676dSKyle Evans if (nvlist_lookup_string(dsprops, "origin", &origin) == 0) 10870cadc427SKyle Evans err = zfs_promote(zhp); 10884635676dSKyle Evans nvlist_free(dsprops); 10894635676dSKyle Evans 10900cadc427SKyle Evans zfs_close(zhp); 10910cadc427SKyle Evans 10920cadc427SKyle Evans if (err) 109328f16a0fSKyle Evans return (-1); 109428f16a0fSKyle Evans } 10950cadc427SKyle Evans 10960cadc427SKyle Evans return (BE_ERR_SUCCESS); 109728f16a0fSKyle Evans } 1098