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 483d1a1f2cSKyle Evans #if SOON 49c65a2111SKyle Evans static int be_create_child_noent(libbe_handle_t *lbh, const char *active, 50c65a2111SKyle Evans const char *child_path); 51c65a2111SKyle Evans static int be_create_child_cloned(libbe_handle_t *lbh, const char *active); 523d1a1f2cSKyle Evans #endif 53c65a2111SKyle Evans 5428f16a0fSKyle Evans /* 55ee16b7c9SKyle Evans * Iterator function for locating the rootfs amongst the children of the 56ee16b7c9SKyle Evans * zfs_be_root set by loader(8). data is expected to be a libbe_handle_t *. 57ee16b7c9SKyle Evans */ 58ee16b7c9SKyle Evans static int 5951aecc89SKyle Evans be_locate_rootfs(libbe_handle_t *lbh) 60ee16b7c9SKyle Evans { 61*4ab5187dSKyle Evans struct statfs sfs; 62*4ab5187dSKyle Evans struct extmnttab entry; 6351aecc89SKyle Evans zfs_handle_t *zfs; 64ee16b7c9SKyle Evans 65*4ab5187dSKyle Evans /* 66*4ab5187dSKyle Evans * Check first if root is ZFS; if not, we'll bail on rootfs capture. 67*4ab5187dSKyle Evans * Unfortunately needed because zfs_path_to_zhandle will emit to 68*4ab5187dSKyle Evans * stderr if / isn't actually a ZFS filesystem, which we'd like 69*4ab5187dSKyle Evans * to avoid. 70*4ab5187dSKyle Evans */ 71*4ab5187dSKyle Evans if (statfs("/", &sfs) == 0) { 72*4ab5187dSKyle Evans statfs2mnttab(&sfs, &entry); 73*4ab5187dSKyle Evans if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) 74*4ab5187dSKyle Evans return (1); 75*4ab5187dSKyle Evans } else 76*4ab5187dSKyle Evans return (1); 7751aecc89SKyle Evans zfs = zfs_path_to_zhandle(lbh->lzh, "/", ZFS_TYPE_FILESYSTEM); 7851aecc89SKyle Evans if (zfs == NULL) 79ee16b7c9SKyle Evans return (1); 80ee16b7c9SKyle Evans 8151aecc89SKyle Evans strlcpy(lbh->rootfs, zfs_get_name(zfs), sizeof(lbh->rootfs)); 8251aecc89SKyle Evans zfs_close(zfs); 83ee16b7c9SKyle Evans return (0); 84ee16b7c9SKyle Evans } 85ee16b7c9SKyle Evans 86ee16b7c9SKyle Evans /* 8728f16a0fSKyle Evans * Initializes the libbe context to operate in the root boot environment 8828f16a0fSKyle Evans * dataset, for example, zroot/ROOT. 8928f16a0fSKyle Evans */ 9028f16a0fSKyle Evans libbe_handle_t * 91cc624025SKyle Evans libbe_init(const char *root) 9228f16a0fSKyle Evans { 9328f16a0fSKyle Evans libbe_handle_t *lbh; 94c3a34c08SKyle Evans char *poolname, *pos; 95c3a34c08SKyle Evans int pnamelen; 9628f16a0fSKyle Evans 97c3a34c08SKyle Evans lbh = NULL; 98c3a34c08SKyle Evans poolname = pos = NULL; 9928f16a0fSKyle Evans 100c3a34c08SKyle Evans if ((lbh = calloc(1, sizeof(libbe_handle_t))) == NULL) 101c3a34c08SKyle Evans goto err; 10228f16a0fSKyle Evans 103c3a34c08SKyle Evans if ((lbh->lzh = libzfs_init()) == NULL) 104c3a34c08SKyle Evans goto err; 10528f16a0fSKyle Evans 106cc624025SKyle Evans /* 107cc624025SKyle Evans * Grab rootfs, we'll work backwards from there if an optional BE root 108cc624025SKyle Evans * has not been passed in. 109cc624025SKyle Evans */ 110*4ab5187dSKyle Evans if (be_locate_rootfs(lbh) != 0) { 111*4ab5187dSKyle Evans if (root == NULL) 112c3a34c08SKyle Evans goto err; 113*4ab5187dSKyle Evans *lbh->rootfs = '\0'; 114*4ab5187dSKyle Evans } 115cc624025SKyle Evans if (root == NULL) { 116cc624025SKyle Evans /* Strip off the final slash from rootfs to get the be root */ 11751aecc89SKyle Evans strlcpy(lbh->root, lbh->rootfs, sizeof(lbh->root)); 11851aecc89SKyle Evans pos = strrchr(lbh->root, '/'); 11951aecc89SKyle Evans if (pos == NULL) 12051aecc89SKyle Evans goto err; 12151aecc89SKyle Evans *pos = '\0'; 122cc624025SKyle Evans } else 123cc624025SKyle Evans strlcpy(lbh->root, root, sizeof(lbh->root)); 124c3a34c08SKyle Evans 125c3a34c08SKyle Evans if ((pos = strchr(lbh->root, '/')) == NULL) 126c3a34c08SKyle Evans goto err; 127c3a34c08SKyle Evans 128c3a34c08SKyle Evans pnamelen = pos - lbh->root; 129c3a34c08SKyle Evans poolname = malloc(pnamelen + 1); 130c3a34c08SKyle Evans if (poolname == NULL) 131c3a34c08SKyle Evans goto err; 132c3a34c08SKyle Evans 13355b0e92bSKyle Evans strlcpy(poolname, lbh->root, pnamelen + 1); 134c3a34c08SKyle Evans if ((lbh->active_phandle = zpool_open(lbh->lzh, poolname)) == NULL) 135c3a34c08SKyle Evans goto err; 136a8e44f4dSKyle Evans free(poolname); 137a8e44f4dSKyle Evans poolname = NULL; 138c3a34c08SKyle Evans 139c3a34c08SKyle Evans if (zpool_get_prop(lbh->active_phandle, ZPOOL_PROP_BOOTFS, lbh->bootfs, 14055b0e92bSKyle Evans sizeof(lbh->bootfs), NULL, true) != 0) 141c3a34c08SKyle Evans goto err; 142c3a34c08SKyle Evans 14328f16a0fSKyle Evans return (lbh); 144c3a34c08SKyle Evans err: 145c3a34c08SKyle Evans if (lbh != NULL) { 146c3a34c08SKyle Evans if (lbh->active_phandle != NULL) 147c3a34c08SKyle Evans zpool_close(lbh->active_phandle); 148c3a34c08SKyle Evans if (lbh->lzh != NULL) 149c3a34c08SKyle Evans libzfs_fini(lbh->lzh); 150c3a34c08SKyle Evans free(lbh); 151c3a34c08SKyle Evans } 152c3a34c08SKyle Evans free(poolname); 153c3a34c08SKyle Evans return (NULL); 15428f16a0fSKyle Evans } 15528f16a0fSKyle Evans 15628f16a0fSKyle Evans 15728f16a0fSKyle Evans /* 15828f16a0fSKyle Evans * Free memory allocated by libbe_init() 15928f16a0fSKyle Evans */ 16028f16a0fSKyle Evans void 16128f16a0fSKyle Evans libbe_close(libbe_handle_t *lbh) 16228f16a0fSKyle Evans { 163bfe0869cSKyle Evans 164c3a34c08SKyle Evans if (lbh->active_phandle != NULL) 165c3a34c08SKyle Evans zpool_close(lbh->active_phandle); 16628f16a0fSKyle Evans libzfs_fini(lbh->lzh); 16728f16a0fSKyle Evans free(lbh); 16828f16a0fSKyle Evans } 16928f16a0fSKyle Evans 1709b1662e6SKyle Evans /* 1719b1662e6SKyle Evans * Proxy through to libzfs for the moment. 1729b1662e6SKyle Evans */ 1739b1662e6SKyle Evans void 1749b1662e6SKyle Evans be_nicenum(uint64_t num, char *buf, size_t buflen) 1759b1662e6SKyle Evans { 1769b1662e6SKyle Evans 1779b1662e6SKyle Evans zfs_nicenum(num, buf, buflen); 1789b1662e6SKyle Evans } 17928f16a0fSKyle Evans 180920abf4dSKyle Evans static int 181920abf4dSKyle Evans be_destroy_cb(zfs_handle_t *zfs_hdl, void *data) 182920abf4dSKyle Evans { 183920abf4dSKyle Evans int err; 184920abf4dSKyle Evans 185920abf4dSKyle Evans if ((err = zfs_iter_children(zfs_hdl, be_destroy_cb, data)) != 0) 186920abf4dSKyle Evans return (err); 187920abf4dSKyle Evans if ((err = zfs_destroy(zfs_hdl, false)) != 0) 188920abf4dSKyle Evans return (err); 189920abf4dSKyle Evans return (0); 190920abf4dSKyle Evans } 191920abf4dSKyle Evans 19228f16a0fSKyle Evans /* 19328f16a0fSKyle Evans * Destroy the boot environment or snapshot specified by the name 19428f16a0fSKyle Evans * parameter. Options are or'd together with the possible values: 19528f16a0fSKyle Evans * BE_DESTROY_FORCE : forces operation on mounted datasets 19628f16a0fSKyle Evans */ 19728f16a0fSKyle Evans int 19873c3d608SKyle Evans be_destroy(libbe_handle_t *lbh, const char *name, int options) 19928f16a0fSKyle Evans { 20028f16a0fSKyle Evans zfs_handle_t *fs; 20128f16a0fSKyle Evans char path[BE_MAXPATHLEN]; 202bfe0869cSKyle Evans char *p; 203bfe0869cSKyle Evans int err, force, mounted; 20428f16a0fSKyle Evans 205bfe0869cSKyle Evans p = path; 206bfe0869cSKyle Evans force = options & BE_DESTROY_FORCE; 20728f16a0fSKyle Evans 20828f16a0fSKyle Evans be_root_concat(lbh, name, path); 20928f16a0fSKyle Evans 21028f16a0fSKyle Evans if (strchr(name, '@') == NULL) { 211bfe0869cSKyle Evans if (!zfs_dataset_exists(lbh->lzh, path, ZFS_TYPE_FILESYSTEM)) 21228f16a0fSKyle Evans return (set_error(lbh, BE_ERR_NOENT)); 21328f16a0fSKyle Evans 214bfe0869cSKyle Evans if (strcmp(path, lbh->rootfs) == 0) 21528f16a0fSKyle Evans return (set_error(lbh, BE_ERR_DESTROYACT)); 21628f16a0fSKyle Evans 21728f16a0fSKyle Evans fs = zfs_open(lbh->lzh, p, ZFS_TYPE_FILESYSTEM); 21828f16a0fSKyle Evans } else { 21928f16a0fSKyle Evans 220bfe0869cSKyle Evans if (!zfs_dataset_exists(lbh->lzh, path, ZFS_TYPE_SNAPSHOT)) 22128f16a0fSKyle Evans return (set_error(lbh, BE_ERR_NOENT)); 22228f16a0fSKyle Evans 22328f16a0fSKyle Evans fs = zfs_open(lbh->lzh, p, ZFS_TYPE_SNAPSHOT); 22428f16a0fSKyle Evans } 22528f16a0fSKyle Evans 22628f16a0fSKyle Evans if (fs == NULL) 22728f16a0fSKyle Evans return (set_error(lbh, BE_ERR_ZFSOPEN)); 22828f16a0fSKyle Evans 22928f16a0fSKyle Evans /* Check if mounted, unmount if force is specified */ 230b29bf2f8SKyle Evans if ((mounted = zfs_is_mounted(fs, NULL)) != 0) { 231bfe0869cSKyle Evans if (force) 23228f16a0fSKyle Evans zfs_unmount(fs, NULL, 0); 233bfe0869cSKyle Evans else 23428f16a0fSKyle Evans return (set_error(lbh, BE_ERR_DESTROYMNT)); 23528f16a0fSKyle Evans } 23628f16a0fSKyle Evans 237920abf4dSKyle Evans if ((err = be_destroy_cb(fs, NULL)) != 0) { 238920abf4dSKyle Evans /* Children are still present or the mount is referenced */ 239920abf4dSKyle Evans if (err == EBUSY) 240920abf4dSKyle Evans return (set_error(lbh, BE_ERR_DESTROYMNT)); 241920abf4dSKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 242920abf4dSKyle Evans } 24328f16a0fSKyle Evans 244920abf4dSKyle Evans return (0); 24528f16a0fSKyle Evans } 24628f16a0fSKyle Evans 24728f16a0fSKyle Evans 24828f16a0fSKyle Evans int 249b29bf2f8SKyle Evans be_snapshot(libbe_handle_t *lbh, const char *source, const char *snap_name, 25028f16a0fSKyle Evans bool recursive, char *result) 25128f16a0fSKyle Evans { 25228f16a0fSKyle Evans char buf[BE_MAXPATHLEN]; 25328f16a0fSKyle Evans time_t rawtime; 25428f16a0fSKyle Evans int len, err; 25528f16a0fSKyle Evans 25628f16a0fSKyle Evans be_root_concat(lbh, source, buf); 25728f16a0fSKyle Evans 258162ec569SKyle Evans if ((err = be_exists(lbh, buf)) != 0) 259162ec569SKyle Evans return (set_error(lbh, err)); 26028f16a0fSKyle Evans 26128f16a0fSKyle Evans if (snap_name != NULL) { 262a8e44f4dSKyle Evans if (strlcat(buf, "@", sizeof(buf)) >= sizeof(buf)) 263a8e44f4dSKyle Evans return (set_error(lbh, BE_ERR_INVALIDNAME)); 264a8e44f4dSKyle Evans 265a8e44f4dSKyle Evans if (strlcat(buf, snap_name, sizeof(buf)) >= sizeof(buf)) 266a8e44f4dSKyle Evans return (set_error(lbh, BE_ERR_INVALIDNAME)); 267a8e44f4dSKyle Evans 268bfe0869cSKyle Evans if (result != NULL) 26928f16a0fSKyle Evans snprintf(result, BE_MAXPATHLEN, "%s@%s", source, 27028f16a0fSKyle Evans snap_name); 27128f16a0fSKyle Evans } else { 27228f16a0fSKyle Evans time(&rawtime); 27328f16a0fSKyle Evans len = strlen(buf); 27455b0e92bSKyle Evans strftime(buf + len, sizeof(buf) - len, 27528f16a0fSKyle Evans "@%F-%T", localtime(&rawtime)); 276a8e44f4dSKyle Evans if (result != NULL && strlcpy(result, strrchr(buf, '/') + 1, 277a8e44f4dSKyle Evans sizeof(buf)) >= sizeof(buf)) 278a8e44f4dSKyle Evans return (set_error(lbh, BE_ERR_INVALIDNAME)); 27928f16a0fSKyle Evans } 28028f16a0fSKyle Evans 281b29bf2f8SKyle Evans if ((err = zfs_snapshot(lbh->lzh, buf, recursive, NULL)) != 0) { 28228f16a0fSKyle Evans switch (err) { 28328f16a0fSKyle Evans case EZFS_INVALIDNAME: 28428f16a0fSKyle Evans return (set_error(lbh, BE_ERR_INVALIDNAME)); 28528f16a0fSKyle Evans 28628f16a0fSKyle Evans default: 2872989df09SKyle Evans /* 2882989df09SKyle Evans * The other errors that zfs_ioc_snapshot might return 2892989df09SKyle Evans * shouldn't happen if we've set things up properly, so 2902989df09SKyle Evans * we'll gloss over them and call it UNKNOWN as it will 2912989df09SKyle Evans * require further triage. 2922989df09SKyle Evans */ 2932989df09SKyle Evans if (errno == ENOTSUP) 2942989df09SKyle Evans return (set_error(lbh, BE_ERR_NOPOOL)); 29528f16a0fSKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 29628f16a0fSKyle Evans } 29728f16a0fSKyle Evans } 29828f16a0fSKyle Evans 29928f16a0fSKyle Evans return (BE_ERR_SUCCESS); 30028f16a0fSKyle Evans } 30128f16a0fSKyle Evans 30228f16a0fSKyle Evans 30328f16a0fSKyle Evans /* 30428f16a0fSKyle Evans * Create the boot environment specified by the name parameter 30528f16a0fSKyle Evans */ 30628f16a0fSKyle Evans int 30773c3d608SKyle Evans be_create(libbe_handle_t *lbh, const char *name) 30828f16a0fSKyle Evans { 30928f16a0fSKyle Evans int err; 31028f16a0fSKyle Evans 311b29bf2f8SKyle Evans err = be_create_from_existing(lbh, name, be_active_path(lbh)); 31228f16a0fSKyle Evans 31328f16a0fSKyle Evans return (set_error(lbh, err)); 31428f16a0fSKyle Evans } 31528f16a0fSKyle Evans 31628f16a0fSKyle Evans 31728f16a0fSKyle Evans static int 31828f16a0fSKyle Evans be_deep_clone_prop(int prop, void *cb) 31928f16a0fSKyle Evans { 32028f16a0fSKyle Evans int err; 321bfe0869cSKyle Evans struct libbe_dccb *dccb; 32228f16a0fSKyle Evans zprop_source_t src; 32328f16a0fSKyle Evans char pval[BE_MAXPATHLEN]; 32428f16a0fSKyle Evans char source[BE_MAXPATHLEN]; 325af43c24dSKyle Evans char *val; 32628f16a0fSKyle Evans 327bfe0869cSKyle Evans dccb = cb; 32828f16a0fSKyle Evans /* Skip some properties we don't want to touch */ 32973c3d608SKyle Evans if (prop == ZFS_PROP_CANMOUNT) 33028f16a0fSKyle Evans return (ZPROP_CONT); 33128f16a0fSKyle Evans 33228f16a0fSKyle Evans /* Don't copy readonly properties */ 333bfe0869cSKyle Evans if (zfs_prop_readonly(prop)) 33428f16a0fSKyle Evans return (ZPROP_CONT); 33528f16a0fSKyle Evans 33628f16a0fSKyle Evans if ((err = zfs_prop_get(dccb->zhp, prop, (char *)&pval, 337bfe0869cSKyle Evans sizeof(pval), &src, (char *)&source, sizeof(source), false))) 33828f16a0fSKyle Evans /* Just continue if we fail to read a property */ 33928f16a0fSKyle Evans return (ZPROP_CONT); 340bfe0869cSKyle Evans 34128f16a0fSKyle Evans /* Only copy locally defined properties */ 342bfe0869cSKyle Evans if (src != ZPROP_SRC_LOCAL) 34328f16a0fSKyle Evans return (ZPROP_CONT); 34428f16a0fSKyle Evans 345af43c24dSKyle Evans /* Augment mountpoint with altroot, if needed */ 346af43c24dSKyle Evans val = pval; 347af43c24dSKyle Evans if (prop == ZFS_PROP_MOUNTPOINT && *dccb->altroot != '\0') { 348af43c24dSKyle Evans if (pval[strlen(dccb->altroot)] == '\0') 349af43c24dSKyle Evans strlcpy(pval, "/", sizeof(pval)); 350af43c24dSKyle Evans else 351af43c24dSKyle Evans val = pval + strlen(dccb->altroot); 352af43c24dSKyle Evans } 353af43c24dSKyle Evans nvlist_add_string(dccb->props, zfs_prop_to_name(prop), val); 35428f16a0fSKyle Evans 35528f16a0fSKyle Evans return (ZPROP_CONT); 35628f16a0fSKyle Evans } 35728f16a0fSKyle Evans 35828f16a0fSKyle Evans static int 35928f16a0fSKyle Evans be_deep_clone(zfs_handle_t *ds, void *data) 36028f16a0fSKyle Evans { 36128f16a0fSKyle Evans int err; 36228f16a0fSKyle Evans char be_path[BE_MAXPATHLEN]; 36328f16a0fSKyle Evans char snap_path[BE_MAXPATHLEN]; 36428f16a0fSKyle Evans const char *dspath; 36528f16a0fSKyle Evans char *dsname; 36628f16a0fSKyle Evans zfs_handle_t *snap_hdl; 36728f16a0fSKyle Evans nvlist_t *props; 368bfe0869cSKyle Evans struct libbe_deep_clone *isdc, sdc; 36928f16a0fSKyle Evans struct libbe_dccb dccb; 37028f16a0fSKyle Evans 371bfe0869cSKyle Evans isdc = (struct libbe_deep_clone *)data; 37228f16a0fSKyle Evans dspath = zfs_get_name(ds); 373bfe0869cSKyle Evans if ((dsname = strrchr(dspath, '/')) == NULL) 37428f16a0fSKyle Evans return (BE_ERR_UNKNOWN); 37528f16a0fSKyle Evans dsname++; 376bfe0869cSKyle Evans 377bfe0869cSKyle Evans if (isdc->bename == NULL) 37828f16a0fSKyle Evans snprintf(be_path, sizeof(be_path), "%s/%s", isdc->be_root, dsname); 379bfe0869cSKyle Evans else 38028f16a0fSKyle Evans snprintf(be_path, sizeof(be_path), "%s/%s", isdc->be_root, isdc->bename); 381bfe0869cSKyle Evans 38228f16a0fSKyle Evans snprintf(snap_path, sizeof(snap_path), "%s@%s", dspath, isdc->snapname); 38328f16a0fSKyle Evans 384bfe0869cSKyle Evans if (zfs_dataset_exists(isdc->lbh->lzh, be_path, ZFS_TYPE_DATASET)) 38528f16a0fSKyle Evans return (set_error(isdc->lbh, BE_ERR_EXISTS)); 38628f16a0fSKyle Evans 38728f16a0fSKyle Evans if ((snap_hdl = 388bfe0869cSKyle Evans zfs_open(isdc->lbh->lzh, snap_path, ZFS_TYPE_SNAPSHOT)) == NULL) 38928f16a0fSKyle Evans return (set_error(isdc->lbh, BE_ERR_ZFSOPEN)); 39028f16a0fSKyle Evans 39128f16a0fSKyle Evans nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP); 39228f16a0fSKyle Evans nvlist_add_string(props, "canmount", "noauto"); 39328f16a0fSKyle Evans 39428f16a0fSKyle Evans dccb.zhp = ds; 39528f16a0fSKyle Evans dccb.props = props; 396af43c24dSKyle Evans if (zpool_get_prop(isdc->lbh->active_phandle, ZPOOL_PROP_ALTROOT, 397af43c24dSKyle Evans dccb.altroot, sizeof(dccb.altroot), NULL, true) != 0 || 398af43c24dSKyle Evans strcmp(dccb.altroot, "-") == 0) 399af43c24dSKyle Evans *dccb.altroot = '\0'; 40028f16a0fSKyle Evans if (zprop_iter(be_deep_clone_prop, &dccb, B_FALSE, B_FALSE, 401bfe0869cSKyle Evans ZFS_TYPE_FILESYSTEM) == ZPROP_INVAL) 40228f16a0fSKyle Evans return (-1); 40328f16a0fSKyle Evans 404cc4deabcSKyle Evans if ((err = zfs_clone(snap_hdl, be_path, props)) != 0) 40528f16a0fSKyle Evans err = BE_ERR_ZFSCLONE; 40628f16a0fSKyle Evans 40728f16a0fSKyle Evans nvlist_free(props); 40828f16a0fSKyle Evans zfs_close(snap_hdl); 40928f16a0fSKyle Evans 410cc4deabcSKyle Evans /* Failed to clone */ 411cc4deabcSKyle Evans if (err != BE_ERR_SUCCESS) 412cc4deabcSKyle Evans return (set_error(isdc->lbh, err)); 413cc4deabcSKyle Evans 41428f16a0fSKyle Evans sdc.lbh = isdc->lbh; 41528f16a0fSKyle Evans sdc.bename = NULL; 41628f16a0fSKyle Evans sdc.snapname = isdc->snapname; 41728f16a0fSKyle Evans sdc.be_root = (char *)&be_path; 41828f16a0fSKyle Evans 41928f16a0fSKyle Evans err = zfs_iter_filesystems(ds, be_deep_clone, &sdc); 42028f16a0fSKyle Evans 42128f16a0fSKyle Evans return (err); 42228f16a0fSKyle Evans } 42328f16a0fSKyle Evans 42428f16a0fSKyle Evans /* 42528f16a0fSKyle Evans * Create the boot environment from pre-existing snapshot 42628f16a0fSKyle Evans */ 42728f16a0fSKyle Evans int 428b29bf2f8SKyle Evans be_create_from_existing_snap(libbe_handle_t *lbh, const char *name, 429b29bf2f8SKyle Evans const char *snap) 43028f16a0fSKyle Evans { 43128f16a0fSKyle Evans int err; 43228f16a0fSKyle Evans char be_path[BE_MAXPATHLEN]; 43328f16a0fSKyle Evans char snap_path[BE_MAXPATHLEN]; 434b29bf2f8SKyle Evans const char *bename; 435b29bf2f8SKyle Evans char *parentname, *snapname; 43628f16a0fSKyle Evans zfs_handle_t *parent_hdl; 43728f16a0fSKyle Evans struct libbe_deep_clone sdc; 43828f16a0fSKyle Evans 439b29bf2f8SKyle Evans if ((err = be_validate_name(lbh, name)) != 0) 44028f16a0fSKyle Evans return (set_error(lbh, err)); 441b29bf2f8SKyle Evans if ((err = be_root_concat(lbh, snap, snap_path)) != 0) 44228f16a0fSKyle Evans return (set_error(lbh, err)); 443b29bf2f8SKyle Evans if ((err = be_validate_snap(lbh, snap_path)) != 0) 44428f16a0fSKyle Evans return (set_error(lbh, err)); 44528f16a0fSKyle Evans 446b29bf2f8SKyle Evans if ((err = be_root_concat(lbh, name, be_path)) != 0) 44728f16a0fSKyle Evans return (set_error(lbh, err)); 44828f16a0fSKyle Evans 449bfe0869cSKyle Evans if ((bename = strrchr(name, '/')) == NULL) 45028f16a0fSKyle Evans bename = name; 451bfe0869cSKyle Evans else 45228f16a0fSKyle Evans bename++; 453bfe0869cSKyle Evans 454cc4deabcSKyle Evans if ((parentname = strdup(snap_path)) == NULL) 455cc4deabcSKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 456cc4deabcSKyle Evans 45728f16a0fSKyle Evans snapname = strchr(parentname, '@'); 45828f16a0fSKyle Evans if (snapname == NULL) { 459cc4deabcSKyle Evans free(parentname); 460cc4deabcSKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 46128f16a0fSKyle Evans } 46228f16a0fSKyle Evans *snapname = '\0'; 46328f16a0fSKyle Evans snapname++; 46428f16a0fSKyle Evans 46528f16a0fSKyle Evans sdc.lbh = lbh; 46628f16a0fSKyle Evans sdc.bename = bename; 46728f16a0fSKyle Evans sdc.snapname = snapname; 46828f16a0fSKyle Evans sdc.be_root = lbh->root; 46928f16a0fSKyle Evans 47028f16a0fSKyle Evans parent_hdl = zfs_open(lbh->lzh, parentname, ZFS_TYPE_DATASET); 47128f16a0fSKyle Evans err = be_deep_clone(parent_hdl, &sdc); 47228f16a0fSKyle Evans 473cc4deabcSKyle Evans free(parentname); 47428f16a0fSKyle Evans return (set_error(lbh, err)); 47528f16a0fSKyle Evans } 47628f16a0fSKyle Evans 47728f16a0fSKyle Evans 47828f16a0fSKyle Evans /* 47928f16a0fSKyle Evans * Create a boot environment from an existing boot environment 48028f16a0fSKyle Evans */ 48128f16a0fSKyle Evans int 482b29bf2f8SKyle Evans be_create_from_existing(libbe_handle_t *lbh, const char *name, const char *old) 48328f16a0fSKyle Evans { 48428f16a0fSKyle Evans int err; 48528f16a0fSKyle Evans char buf[BE_MAXPATHLEN]; 48628f16a0fSKyle Evans 4875b7803a9SKyle Evans if ((err = be_snapshot(lbh, old, NULL, true, (char *)&buf)) != 0) 48828f16a0fSKyle Evans return (set_error(lbh, err)); 48928f16a0fSKyle Evans 49028f16a0fSKyle Evans err = be_create_from_existing_snap(lbh, name, (char *)buf); 49128f16a0fSKyle Evans 49228f16a0fSKyle Evans return (set_error(lbh, err)); 49328f16a0fSKyle Evans } 49428f16a0fSKyle Evans 49528f16a0fSKyle Evans 49628f16a0fSKyle Evans /* 49728f16a0fSKyle Evans * Verifies that a snapshot has a valid name, exists, and has a mountpoint of 49828f16a0fSKyle Evans * '/'. Returns BE_ERR_SUCCESS (0), upon success, or the relevant BE_ERR_* upon 49928f16a0fSKyle Evans * failure. Does not set the internal library error state. 50028f16a0fSKyle Evans */ 50128f16a0fSKyle Evans int 502b29bf2f8SKyle Evans be_validate_snap(libbe_handle_t *lbh, const char *snap_name) 50328f16a0fSKyle Evans { 50428f16a0fSKyle Evans 505bfe0869cSKyle Evans if (strlen(snap_name) >= BE_MAXPATHLEN) 50628f16a0fSKyle Evans return (BE_ERR_PATHLEN); 50728f16a0fSKyle Evans 50828f16a0fSKyle Evans if (!zfs_dataset_exists(lbh->lzh, snap_name, 509bfe0869cSKyle Evans ZFS_TYPE_SNAPSHOT)) 51028f16a0fSKyle Evans return (BE_ERR_NOENT); 51128f16a0fSKyle Evans 51251aecc89SKyle Evans return (BE_ERR_SUCCESS); 51328f16a0fSKyle Evans } 51428f16a0fSKyle Evans 51528f16a0fSKyle Evans 51628f16a0fSKyle Evans /* 51728f16a0fSKyle Evans * Idempotently appends the name argument to the root boot environment path 51828f16a0fSKyle Evans * and copies the resulting string into the result buffer (which is assumed 51928f16a0fSKyle Evans * to be at least BE_MAXPATHLEN characters long. Returns BE_ERR_SUCCESS upon 52028f16a0fSKyle Evans * success, BE_ERR_PATHLEN if the resulting path is longer than BE_MAXPATHLEN, 52128f16a0fSKyle Evans * or BE_ERR_INVALIDNAME if the name is a path that does not begin with 52228f16a0fSKyle Evans * zfs_be_root. Does not set internal library error state. 52328f16a0fSKyle Evans */ 52428f16a0fSKyle Evans int 525b29bf2f8SKyle Evans be_root_concat(libbe_handle_t *lbh, const char *name, char *result) 52628f16a0fSKyle Evans { 52728f16a0fSKyle Evans size_t name_len, root_len; 52828f16a0fSKyle Evans 52928f16a0fSKyle Evans name_len = strlen(name); 53028f16a0fSKyle Evans root_len = strlen(lbh->root); 53128f16a0fSKyle Evans 53228f16a0fSKyle Evans /* Act idempotently; return be name if it is already a full path */ 53328f16a0fSKyle Evans if (strrchr(name, '/') != NULL) { 534bfe0869cSKyle Evans if (strstr(name, lbh->root) != name) 53528f16a0fSKyle Evans return (BE_ERR_INVALIDNAME); 53628f16a0fSKyle Evans 537bfe0869cSKyle Evans if (name_len >= BE_MAXPATHLEN) 53828f16a0fSKyle Evans return (BE_ERR_PATHLEN); 53928f16a0fSKyle Evans 54055b0e92bSKyle Evans strlcpy(result, name, BE_MAXPATHLEN); 54128f16a0fSKyle Evans return (BE_ERR_SUCCESS); 54228f16a0fSKyle Evans } else if (name_len + root_len + 1 < BE_MAXPATHLEN) { 54328f16a0fSKyle Evans snprintf(result, BE_MAXPATHLEN, "%s/%s", lbh->root, 54428f16a0fSKyle Evans name); 54528f16a0fSKyle Evans return (BE_ERR_SUCCESS); 54628f16a0fSKyle Evans } 54728f16a0fSKyle Evans 54828f16a0fSKyle Evans return (BE_ERR_PATHLEN); 54928f16a0fSKyle Evans } 55028f16a0fSKyle Evans 55128f16a0fSKyle Evans 55228f16a0fSKyle Evans /* 55328f16a0fSKyle Evans * Verifies the validity of a boot environment name (A-Za-z0-9-_.). Returns 5545b7803a9SKyle Evans * BE_ERR_SUCCESS (0) if name is valid, otherwise returns BE_ERR_INVALIDNAME 5555b7803a9SKyle Evans * or BE_ERR_PATHLEN. 55628f16a0fSKyle Evans * Does not set internal library error state. 55728f16a0fSKyle Evans */ 55828f16a0fSKyle Evans int 5595b7803a9SKyle Evans be_validate_name(libbe_handle_t *lbh, const char *name) 56028f16a0fSKyle Evans { 56128f16a0fSKyle Evans for (int i = 0; *name; i++) { 56228f16a0fSKyle Evans char c = *(name++); 563bfe0869cSKyle Evans if (isalnum(c) || (c == '-') || (c == '_') || (c == '.')) 56428f16a0fSKyle Evans continue; 56528f16a0fSKyle Evans return (BE_ERR_INVALIDNAME); 56628f16a0fSKyle Evans } 56728f16a0fSKyle Evans 5685b7803a9SKyle Evans /* 5695b7803a9SKyle Evans * Impose the additional restriction that the entire dataset name must 5705b7803a9SKyle Evans * not exceed the maximum length of a dataset, i.e. MAXNAMELEN. 5715b7803a9SKyle Evans */ 5725b7803a9SKyle Evans if (strlen(lbh->root) + 1 + strlen(name) > MAXNAMELEN) 5735b7803a9SKyle Evans return (BE_ERR_PATHLEN); 57428f16a0fSKyle Evans return (BE_ERR_SUCCESS); 57528f16a0fSKyle Evans } 57628f16a0fSKyle Evans 57728f16a0fSKyle Evans 57828f16a0fSKyle Evans /* 57928f16a0fSKyle Evans * usage 58028f16a0fSKyle Evans */ 58128f16a0fSKyle Evans int 58273c3d608SKyle Evans be_rename(libbe_handle_t *lbh, const char *old, const char *new) 58328f16a0fSKyle Evans { 58428f16a0fSKyle Evans char full_old[BE_MAXPATHLEN]; 58528f16a0fSKyle Evans char full_new[BE_MAXPATHLEN]; 58628f16a0fSKyle Evans zfs_handle_t *zfs_hdl; 58728f16a0fSKyle Evans int err; 58828f16a0fSKyle Evans 5895b7803a9SKyle Evans /* 5905b7803a9SKyle Evans * be_validate_name is documented not to set error state, so we should 5915b7803a9SKyle Evans * do so here. 5925b7803a9SKyle Evans */ 5935b7803a9SKyle Evans if ((err = be_validate_name(lbh, new)) != 0) 5945b7803a9SKyle Evans return (set_error(lbh, err)); 595b29bf2f8SKyle Evans if ((err = be_root_concat(lbh, old, full_old)) != 0) 59628f16a0fSKyle Evans return (set_error(lbh, err)); 597b29bf2f8SKyle Evans if ((err = be_root_concat(lbh, new, full_new)) != 0) 59828f16a0fSKyle Evans return (set_error(lbh, err)); 59928f16a0fSKyle Evans 600bfe0869cSKyle Evans if (!zfs_dataset_exists(lbh->lzh, full_old, ZFS_TYPE_DATASET)) 6012989df09SKyle Evans return (set_error(lbh, BE_ERR_NOENT)); 60228f16a0fSKyle Evans 603bfe0869cSKyle Evans if (zfs_dataset_exists(lbh->lzh, full_new, ZFS_TYPE_DATASET)) 6042989df09SKyle Evans return (set_error(lbh, BE_ERR_EXISTS)); 60528f16a0fSKyle Evans 60628f16a0fSKyle Evans if ((zfs_hdl = zfs_open(lbh->lzh, full_old, 607bfe0869cSKyle Evans ZFS_TYPE_FILESYSTEM)) == NULL) 6082989df09SKyle Evans return (set_error(lbh, BE_ERR_ZFSOPEN)); 60928f16a0fSKyle Evans 610bfe0869cSKyle Evans /* recurse, nounmount, forceunmount */ 6118369ba42SKyle Evans struct renameflags flags = { 6128369ba42SKyle Evans .nounmount = 1, 6138369ba42SKyle Evans }; 61428f16a0fSKyle Evans 61528f16a0fSKyle Evans err = zfs_rename(zfs_hdl, NULL, full_new, flags); 61628f16a0fSKyle Evans 61728f16a0fSKyle Evans zfs_close(zfs_hdl); 6185b7803a9SKyle Evans if (err != 0) 6195b7803a9SKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 6205b7803a9SKyle Evans return (0); 62128f16a0fSKyle Evans } 62228f16a0fSKyle Evans 62328f16a0fSKyle Evans 62428f16a0fSKyle Evans int 62573c3d608SKyle Evans be_export(libbe_handle_t *lbh, const char *bootenv, int fd) 62628f16a0fSKyle Evans { 62728f16a0fSKyle Evans char snap_name[BE_MAXPATHLEN]; 62828f16a0fSKyle Evans char buf[BE_MAXPATHLEN]; 62928f16a0fSKyle Evans zfs_handle_t *zfs; 63028f16a0fSKyle Evans int err; 63128f16a0fSKyle Evans 632b29bf2f8SKyle Evans if ((err = be_snapshot(lbh, bootenv, NULL, true, snap_name)) != 0) 6336d4b1d24SKyle Evans /* Use the error set by be_snapshot */ 6346d4b1d24SKyle Evans return (err); 63528f16a0fSKyle Evans 63628f16a0fSKyle Evans be_root_concat(lbh, snap_name, buf); 63728f16a0fSKyle Evans 638bfe0869cSKyle Evans if ((zfs = zfs_open(lbh->lzh, buf, ZFS_TYPE_DATASET)) == NULL) 639506f5fdfSKyle Evans return (set_error(lbh, BE_ERR_ZFSOPEN)); 64028f16a0fSKyle Evans 64128f16a0fSKyle Evans err = zfs_send_one(zfs, NULL, fd, 0); 6426d4b1d24SKyle Evans zfs_close(zfs); 6436d4b1d24SKyle Evans 64428f16a0fSKyle Evans return (err); 64528f16a0fSKyle Evans } 64628f16a0fSKyle Evans 64728f16a0fSKyle Evans 64828f16a0fSKyle Evans int 64973c3d608SKyle Evans be_import(libbe_handle_t *lbh, const char *bootenv, int fd) 65028f16a0fSKyle Evans { 65128f16a0fSKyle Evans char buf[BE_MAXPATHLEN]; 65228f16a0fSKyle Evans time_t rawtime; 65328f16a0fSKyle Evans nvlist_t *props; 65428f16a0fSKyle Evans zfs_handle_t *zfs; 65528f16a0fSKyle Evans int err, len; 656506f5fdfSKyle Evans char nbuf[24]; 65728f16a0fSKyle Evans 658bfe0869cSKyle Evans /* 659506f5fdfSKyle Evans * We don't need this to be incredibly random, just unique enough that 660506f5fdfSKyle Evans * it won't conflict with an existing dataset name. Chopping time 661506f5fdfSKyle Evans * down to 32 bits is probably good enough for this. 662bfe0869cSKyle Evans */ 663506f5fdfSKyle Evans snprintf(nbuf, 24, "tmp%u", 664506f5fdfSKyle Evans (uint32_t)(time(NULL) & 0xFFFFFFFF)); 665506f5fdfSKyle Evans if ((err = be_root_concat(lbh, nbuf, buf)) != 0) 666506f5fdfSKyle Evans /* 667506f5fdfSKyle Evans * Technically this is our problem, but we try to use short 668506f5fdfSKyle Evans * enough names that we won't run into problems except in 669506f5fdfSKyle Evans * worst-case BE root approaching MAXPATHLEN. 670506f5fdfSKyle Evans */ 671506f5fdfSKyle Evans return (set_error(lbh, BE_ERR_PATHLEN)); 67228f16a0fSKyle Evans 67328f16a0fSKyle Evans time(&rawtime); 67428f16a0fSKyle Evans len = strlen(buf); 67555b0e92bSKyle Evans strftime(buf + len, sizeof(buf) - len, "@%F-%T", localtime(&rawtime)); 67628f16a0fSKyle Evans 677b29bf2f8SKyle Evans if ((err = lzc_receive(buf, NULL, NULL, false, fd)) != 0) { 678506f5fdfSKyle Evans switch (err) { 679506f5fdfSKyle Evans case EINVAL: 680506f5fdfSKyle Evans return (set_error(lbh, BE_ERR_NOORIGIN)); 681506f5fdfSKyle Evans case ENOENT: 682506f5fdfSKyle Evans return (set_error(lbh, BE_ERR_NOENT)); 683506f5fdfSKyle Evans case EIO: 684506f5fdfSKyle Evans return (set_error(lbh, BE_ERR_IO)); 685506f5fdfSKyle Evans default: 686506f5fdfSKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 687506f5fdfSKyle Evans } 68828f16a0fSKyle Evans } 68928f16a0fSKyle Evans 690bfe0869cSKyle Evans if ((zfs = zfs_open(lbh->lzh, buf, ZFS_TYPE_SNAPSHOT)) == NULL) 691506f5fdfSKyle Evans return (set_error(lbh, BE_ERR_ZFSOPEN)); 69228f16a0fSKyle Evans 69328f16a0fSKyle Evans nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP); 69428f16a0fSKyle Evans nvlist_add_string(props, "canmount", "noauto"); 69528f16a0fSKyle Evans nvlist_add_string(props, "mountpoint", "/"); 69628f16a0fSKyle Evans 69728f16a0fSKyle Evans be_root_concat(lbh, bootenv, buf); 69828f16a0fSKyle Evans 69928f16a0fSKyle Evans err = zfs_clone(zfs, buf, props); 70028f16a0fSKyle Evans zfs_close(zfs); 70128f16a0fSKyle Evans nvlist_free(props); 70228f16a0fSKyle Evans 7031b057aacSKyle Evans if (err != 0) 7041b057aacSKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 7051b057aacSKyle Evans 7061b057aacSKyle Evans /* 70700ece7c2SKyle Evans * Finally, we open up the dataset we just cloned the snapshot so that 7081b057aacSKyle Evans * we may promote it. This is necessary in order to clean up the ghost 70900ece7c2SKyle Evans * snapshot that doesn't need to be seen after the operation is 71000ece7c2SKyle Evans * complete. 7111b057aacSKyle Evans */ 7121b057aacSKyle Evans if ((zfs = zfs_open(lbh->lzh, buf, ZFS_TYPE_DATASET)) == NULL) 7131b057aacSKyle Evans return (set_error(lbh, BE_ERR_ZFSOPEN)); 7141b057aacSKyle Evans 7151b057aacSKyle Evans err = zfs_promote(zfs); 7161b057aacSKyle Evans zfs_close(zfs); 7171b057aacSKyle Evans 7181b057aacSKyle Evans if (err != 0) 7191b057aacSKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 7201b057aacSKyle Evans 7211b057aacSKyle Evans /* Clean up the temporary snapshot */ 7221b057aacSKyle Evans return (be_destroy(lbh, nbuf, 0)); 72328f16a0fSKyle Evans } 72428f16a0fSKyle Evans 7253d1a1f2cSKyle Evans #if SOON 726c65a2111SKyle Evans static int 727c65a2111SKyle Evans be_create_child_noent(libbe_handle_t *lbh, const char *active, 728c65a2111SKyle Evans const char *child_path) 729c65a2111SKyle Evans { 730c65a2111SKyle Evans nvlist_t *props; 731c65a2111SKyle Evans zfs_handle_t *zfs; 732c65a2111SKyle Evans int err; 733c65a2111SKyle Evans 734c65a2111SKyle Evans nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP); 735c65a2111SKyle Evans nvlist_add_string(props, "canmount", "noauto"); 736c65a2111SKyle Evans nvlist_add_string(props, "mountpoint", child_path); 737c65a2111SKyle Evans 738c65a2111SKyle Evans /* Create */ 739c65a2111SKyle Evans if ((err = zfs_create(lbh->lzh, active, ZFS_TYPE_DATASET, 740c65a2111SKyle Evans props)) != 0) { 741c65a2111SKyle Evans switch (err) { 742c65a2111SKyle Evans case EZFS_EXISTS: 743c65a2111SKyle Evans return (set_error(lbh, BE_ERR_EXISTS)); 744c65a2111SKyle Evans case EZFS_NOENT: 745c65a2111SKyle Evans return (set_error(lbh, BE_ERR_NOENT)); 746c65a2111SKyle Evans case EZFS_BADTYPE: 747c65a2111SKyle Evans case EZFS_BADVERSION: 748c65a2111SKyle Evans return (set_error(lbh, BE_ERR_NOPOOL)); 749c65a2111SKyle Evans case EZFS_BADPROP: 750c65a2111SKyle Evans default: 751c65a2111SKyle Evans /* We set something up wrong, probably... */ 752c65a2111SKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 753c65a2111SKyle Evans } 754c65a2111SKyle Evans } 755c65a2111SKyle Evans nvlist_free(props); 756c65a2111SKyle Evans 757c65a2111SKyle Evans if ((zfs = zfs_open(lbh->lzh, active, ZFS_TYPE_DATASET)) == NULL) 758c65a2111SKyle Evans return (set_error(lbh, BE_ERR_ZFSOPEN)); 759c65a2111SKyle Evans 760c65a2111SKyle Evans /* Set props */ 761c65a2111SKyle Evans if ((err = zfs_prop_set(zfs, "canmount", "noauto")) != 0) { 762c65a2111SKyle Evans zfs_close(zfs); 763c65a2111SKyle Evans /* 764c65a2111SKyle Evans * Similar to other cases, this shouldn't fail unless we've 765c65a2111SKyle Evans * done something wrong. This is a new dataset that shouldn't 766c65a2111SKyle Evans * have been mounted anywhere between creation and now. 767c65a2111SKyle Evans */ 768c65a2111SKyle Evans if (err == EZFS_NOMEM) 769c65a2111SKyle Evans return (set_error(lbh, BE_ERR_NOMEM)); 770c65a2111SKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 771c65a2111SKyle Evans } 772c65a2111SKyle Evans zfs_close(zfs); 773c65a2111SKyle Evans return (BE_ERR_SUCCESS); 774c65a2111SKyle Evans } 775c65a2111SKyle Evans 776c65a2111SKyle Evans static int 777c65a2111SKyle Evans be_create_child_cloned(libbe_handle_t *lbh, const char *active) 778c65a2111SKyle Evans { 7793d1a1f2cSKyle Evans char buf[BE_MAXPATHLEN], tmp[BE_MAXPATHLEN];; 780c65a2111SKyle Evans zfs_handle_t *zfs; 781c65a2111SKyle Evans int err; 782c65a2111SKyle Evans 783c65a2111SKyle Evans /* XXX TODO ? */ 784c65a2111SKyle Evans 785c65a2111SKyle Evans /* 786c65a2111SKyle Evans * Establish if the existing path is a zfs dataset or just 787c65a2111SKyle Evans * the subdirectory of one 788c65a2111SKyle Evans */ 7893d1a1f2cSKyle Evans strlcpy(tmp, "tmp/be_snap.XXXXX", sizeof(tmp)); 7903d1a1f2cSKyle Evans if (mktemp(tmp) == NULL) 791c65a2111SKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 792c65a2111SKyle Evans 7933d1a1f2cSKyle Evans be_root_concat(lbh, tmp, buf); 7943d1a1f2cSKyle Evans printf("Here %s?\n", buf); 795c65a2111SKyle Evans if ((err = zfs_snapshot(lbh->lzh, buf, false, NULL)) != 0) { 796c65a2111SKyle Evans switch (err) { 797c65a2111SKyle Evans case EZFS_INVALIDNAME: 798c65a2111SKyle Evans return (set_error(lbh, BE_ERR_INVALIDNAME)); 799c65a2111SKyle Evans 800c65a2111SKyle Evans default: 801c65a2111SKyle Evans /* 802c65a2111SKyle Evans * The other errors that zfs_ioc_snapshot might return 803c65a2111SKyle Evans * shouldn't happen if we've set things up properly, so 804c65a2111SKyle Evans * we'll gloss over them and call it UNKNOWN as it will 805c65a2111SKyle Evans * require further triage. 806c65a2111SKyle Evans */ 807c65a2111SKyle Evans if (errno == ENOTSUP) 808c65a2111SKyle Evans return (set_error(lbh, BE_ERR_NOPOOL)); 809c65a2111SKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 810c65a2111SKyle Evans } 811c65a2111SKyle Evans } 812c65a2111SKyle Evans 813c65a2111SKyle Evans /* Clone */ 814c65a2111SKyle Evans if ((zfs = zfs_open(lbh->lzh, buf, ZFS_TYPE_SNAPSHOT)) == NULL) 815c65a2111SKyle Evans return (BE_ERR_ZFSOPEN); 816c65a2111SKyle Evans 817c65a2111SKyle Evans if ((err = zfs_clone(zfs, active, NULL)) != 0) 818c65a2111SKyle Evans /* XXX TODO correct error */ 819c65a2111SKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 820c65a2111SKyle Evans 821c65a2111SKyle Evans /* set props */ 822c65a2111SKyle Evans zfs_close(zfs); 823c65a2111SKyle Evans return (BE_ERR_SUCCESS); 824c65a2111SKyle Evans } 82528f16a0fSKyle Evans 82628f16a0fSKyle Evans int 82773c3d608SKyle Evans be_add_child(libbe_handle_t *lbh, const char *child_path, bool cp_if_exists) 82828f16a0fSKyle Evans { 82973c3d608SKyle Evans struct stat sb; 830c65a2111SKyle Evans char active[BE_MAXPATHLEN], buf[BE_MAXPATHLEN]; 83128f16a0fSKyle Evans nvlist_t *props; 83273c3d608SKyle Evans const char *s; 83328f16a0fSKyle Evans 83428f16a0fSKyle Evans /* Require absolute paths */ 835bfe0869cSKyle Evans if (*child_path != '/') 8366d4b1d24SKyle Evans return (set_error(lbh, BE_ERR_BADPATH)); 83728f16a0fSKyle Evans 8386d4b1d24SKyle Evans strlcpy(active, be_active_path(lbh), BE_MAXPATHLEN); 83928f16a0fSKyle Evans strcpy(buf, active); 84028f16a0fSKyle Evans 84128f16a0fSKyle Evans /* Create non-mountable parent dataset(s) */ 84273c3d608SKyle Evans s = child_path; 84328f16a0fSKyle Evans for (char *p; (p = strchr(s+1, '/')) != NULL; s = p) { 84428f16a0fSKyle Evans size_t len = p - s; 84528f16a0fSKyle Evans strncat(buf, s, len); 84628f16a0fSKyle Evans 84728f16a0fSKyle Evans nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP); 84828f16a0fSKyle Evans nvlist_add_string(props, "canmount", "off"); 84928f16a0fSKyle Evans nvlist_add_string(props, "mountpoint", "none"); 85028f16a0fSKyle Evans zfs_create(lbh->lzh, buf, ZFS_TYPE_DATASET, props); 85128f16a0fSKyle Evans nvlist_free(props); 85228f16a0fSKyle Evans } 85328f16a0fSKyle Evans 85428f16a0fSKyle Evans /* Path does not exist as a descendent of / yet */ 8556d4b1d24SKyle Evans if (strlcat(active, child_path, BE_MAXPATHLEN) >= BE_MAXPATHLEN) 8566d4b1d24SKyle Evans return (set_error(lbh, BE_ERR_PATHLEN)); 85728f16a0fSKyle Evans 85828f16a0fSKyle Evans if (stat(child_path, &sb) != 0) { 85928f16a0fSKyle Evans /* Verify that error is ENOENT */ 8606d4b1d24SKyle Evans if (errno != ENOENT) 8616d4b1d24SKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 862c65a2111SKyle Evans return (be_create_child_noent(lbh, active, child_path)); 863c65a2111SKyle Evans } else if (cp_if_exists) 86428f16a0fSKyle Evans /* Path is already a descendent of / and should be copied */ 865c65a2111SKyle Evans return (be_create_child_cloned(lbh, active)); 8666d4b1d24SKyle Evans return (set_error(lbh, BE_ERR_EXISTS)); 86728f16a0fSKyle Evans } 8683d1a1f2cSKyle Evans #endif /* SOON */ 86928f16a0fSKyle Evans 870d06f7103SKyle Evans static int 871d06f7103SKyle Evans be_set_nextboot(libbe_handle_t *lbh, nvlist_t *config, uint64_t pool_guid, 872d06f7103SKyle Evans const char *zfsdev) 873d06f7103SKyle Evans { 874d06f7103SKyle Evans nvlist_t **child; 875d06f7103SKyle Evans uint64_t vdev_guid; 876d06f7103SKyle Evans int c, children; 877d06f7103SKyle Evans 878d06f7103SKyle Evans if (nvlist_lookup_nvlist_array(config, ZPOOL_CONFIG_CHILDREN, &child, 879d06f7103SKyle Evans &children) == 0) { 880d06f7103SKyle Evans for (c = 0; c < children; ++c) 881d06f7103SKyle Evans if (be_set_nextboot(lbh, child[c], pool_guid, zfsdev) != 0) 882d06f7103SKyle Evans return (1); 883d06f7103SKyle Evans return (0); 884d06f7103SKyle Evans } 885d06f7103SKyle Evans 886d06f7103SKyle Evans if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_GUID, 887d06f7103SKyle Evans &vdev_guid) != 0) { 888d06f7103SKyle Evans return (1); 889d06f7103SKyle Evans } 890d06f7103SKyle Evans 891d06f7103SKyle Evans if (zpool_nextboot(lbh->lzh, pool_guid, vdev_guid, zfsdev) != 0) { 892d06f7103SKyle Evans perror("ZFS_IOC_NEXTBOOT failed"); 893d06f7103SKyle Evans return (1); 894d06f7103SKyle Evans } 895d06f7103SKyle Evans 896d06f7103SKyle Evans return (0); 897d06f7103SKyle Evans } 898d06f7103SKyle Evans 8998d4ce358SKyle Evans /* 9008d4ce358SKyle Evans * Deactivate old BE dataset; currently just sets canmount=noauto 9018d4ce358SKyle Evans */ 9028d4ce358SKyle Evans static int 9038d4ce358SKyle Evans be_deactivate(libbe_handle_t *lbh, const char *ds) 9048d4ce358SKyle Evans { 9058d4ce358SKyle Evans zfs_handle_t *zfs; 9068d4ce358SKyle Evans 9078d4ce358SKyle Evans if ((zfs = zfs_open(lbh->lzh, ds, ZFS_TYPE_DATASET)) == NULL) 9088d4ce358SKyle Evans return (1); 9098d4ce358SKyle Evans if (zfs_prop_set(zfs, "canmount", "noauto") != 0) 9108d4ce358SKyle Evans return (1); 9118d4ce358SKyle Evans zfs_close(zfs); 9128d4ce358SKyle Evans return (0); 9138d4ce358SKyle Evans } 91428f16a0fSKyle Evans 91528f16a0fSKyle Evans int 91673c3d608SKyle Evans be_activate(libbe_handle_t *lbh, const char *bootenv, bool temporary) 91728f16a0fSKyle Evans { 91828f16a0fSKyle Evans char be_path[BE_MAXPATHLEN]; 91928f16a0fSKyle Evans char buf[BE_MAXPATHLEN]; 9204635676dSKyle Evans nvlist_t *config, *dsprops, *vdevs; 9214635676dSKyle Evans char *origin; 9220cadc427SKyle Evans uint64_t pool_guid; 9230cadc427SKyle Evans zfs_handle_t *zhp; 92428f16a0fSKyle Evans int err; 92528f16a0fSKyle Evans 92628f16a0fSKyle Evans be_root_concat(lbh, bootenv, be_path); 92728f16a0fSKyle Evans 92828f16a0fSKyle Evans /* Note: be_exists fails if mountpoint is not / */ 929162ec569SKyle Evans if ((err = be_exists(lbh, be_path)) != 0) 930162ec569SKyle Evans return (set_error(lbh, err)); 93128f16a0fSKyle Evans 93228f16a0fSKyle Evans if (temporary) { 933d06f7103SKyle Evans config = zpool_get_config(lbh->active_phandle, NULL); 934c65a2111SKyle Evans if (config == NULL) 935c65a2111SKyle Evans /* config should be fetchable... */ 936c65a2111SKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 93728f16a0fSKyle Evans 938d06f7103SKyle Evans if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, 939d06f7103SKyle Evans &pool_guid) != 0) 940c65a2111SKyle Evans /* Similarly, it shouldn't be possible */ 941c65a2111SKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 942d06f7103SKyle Evans 94328f16a0fSKyle Evans /* Expected format according to zfsbootcfg(8) man */ 944a8e44f4dSKyle Evans snprintf(buf, sizeof(buf), "zfs:%s:", be_path); 94528f16a0fSKyle Evans 946c65a2111SKyle Evans /* We have no config tree */ 947c65a2111SKyle Evans if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, 948c65a2111SKyle Evans &vdevs) != 0) 949c65a2111SKyle Evans return (set_error(lbh, BE_ERR_NOPOOL)); 95028f16a0fSKyle Evans 951d06f7103SKyle Evans return (be_set_nextboot(lbh, vdevs, pool_guid, buf)); 95228f16a0fSKyle Evans } else { 9538d4ce358SKyle Evans if (be_deactivate(lbh, lbh->bootfs) != 0) 9548d4ce358SKyle Evans return (-1); 9558d4ce358SKyle Evans 95628f16a0fSKyle Evans /* Obtain bootenv zpool */ 957c3a34c08SKyle Evans err = zpool_set_prop(lbh->active_phandle, "bootfs", be_path); 9580cadc427SKyle Evans if (err) 9590cadc427SKyle Evans return (-1); 96028f16a0fSKyle Evans 9610cadc427SKyle Evans zhp = zfs_open(lbh->lzh, be_path, ZFS_TYPE_FILESYSTEM); 9620cadc427SKyle Evans if (zhp == NULL) 9630cadc427SKyle Evans return (-1); 96428f16a0fSKyle Evans 9654635676dSKyle Evans if (be_prop_list_alloc(&dsprops) != 0) 9664635676dSKyle Evans return (-1); 9674635676dSKyle Evans 9684635676dSKyle Evans if (be_get_dataset_props(lbh, be_path, dsprops) != 0) { 9694635676dSKyle Evans nvlist_free(dsprops); 9704635676dSKyle Evans return (-1); 9714635676dSKyle Evans } 9724635676dSKyle Evans 9734635676dSKyle Evans if (nvlist_lookup_string(dsprops, "origin", &origin) == 0) 9740cadc427SKyle Evans err = zfs_promote(zhp); 9754635676dSKyle Evans nvlist_free(dsprops); 9764635676dSKyle Evans 9770cadc427SKyle Evans zfs_close(zhp); 9780cadc427SKyle Evans 9790cadc427SKyle Evans if (err) 98028f16a0fSKyle Evans return (-1); 98128f16a0fSKyle Evans } 9820cadc427SKyle Evans 9830cadc427SKyle Evans return (BE_ERR_SUCCESS); 98428f16a0fSKyle Evans } 985