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 3228f16a0fSKyle Evans #include <sys/stat.h> 3328f16a0fSKyle Evans #include <sys/types.h> 3428f16a0fSKyle Evans 3528f16a0fSKyle Evans #include <ctype.h> 3628f16a0fSKyle Evans #include <kenv.h> 3728f16a0fSKyle Evans #include <libgen.h> 3828f16a0fSKyle Evans #include <libzfs_core.h> 3928f16a0fSKyle Evans #include <stdio.h> 4028f16a0fSKyle Evans #include <stdlib.h> 4128f16a0fSKyle Evans #include <time.h> 4228f16a0fSKyle Evans #include <unistd.h> 4328f16a0fSKyle Evans 4428f16a0fSKyle Evans #include "be.h" 4528f16a0fSKyle Evans #include "be_impl.h" 4628f16a0fSKyle Evans 473d1a1f2cSKyle Evans #if SOON 48c65a2111SKyle Evans static int be_create_child_noent(libbe_handle_t *lbh, const char *active, 49c65a2111SKyle Evans const char *child_path); 50c65a2111SKyle Evans static int be_create_child_cloned(libbe_handle_t *lbh, const char *active); 513d1a1f2cSKyle Evans #endif 52c65a2111SKyle Evans 5328f16a0fSKyle Evans /* 54ee16b7c9SKyle Evans * Iterator function for locating the rootfs amongst the children of the 55ee16b7c9SKyle Evans * zfs_be_root set by loader(8). data is expected to be a libbe_handle_t *. 56ee16b7c9SKyle Evans */ 57ee16b7c9SKyle Evans static int 58ee16b7c9SKyle Evans be_locate_rootfs(zfs_handle_t *chkds, void *data) 59ee16b7c9SKyle Evans { 60ee16b7c9SKyle Evans libbe_handle_t *lbh; 61ee16b7c9SKyle Evans char *mntpoint; 62ee16b7c9SKyle Evans 63ee16b7c9SKyle Evans lbh = (libbe_handle_t *)data; 64ee16b7c9SKyle Evans if (lbh == NULL) 65ee16b7c9SKyle Evans return (1); 66ee16b7c9SKyle Evans 67cc4deabcSKyle Evans mntpoint = NULL; 68ee16b7c9SKyle Evans if (zfs_is_mounted(chkds, &mntpoint) && strcmp(mntpoint, "/") == 0) { 6955b0e92bSKyle Evans strlcpy(lbh->rootfs, zfs_get_name(chkds), sizeof(lbh->rootfs)); 70cc4deabcSKyle Evans free(mntpoint); 71ee16b7c9SKyle Evans return (1); 72cc4deabcSKyle Evans } else if(mntpoint != NULL) 73cc4deabcSKyle Evans free(mntpoint); 74ee16b7c9SKyle Evans 75ee16b7c9SKyle Evans return (0); 76ee16b7c9SKyle Evans } 77ee16b7c9SKyle Evans 78ee16b7c9SKyle Evans /* 7928f16a0fSKyle Evans * Initializes the libbe context to operate in the root boot environment 8028f16a0fSKyle Evans * dataset, for example, zroot/ROOT. 8128f16a0fSKyle Evans */ 8228f16a0fSKyle Evans libbe_handle_t * 8328f16a0fSKyle Evans libbe_init(void) 8428f16a0fSKyle Evans { 8528f16a0fSKyle Evans struct stat sb; 8628f16a0fSKyle Evans dev_t root_dev, boot_dev; 8728f16a0fSKyle Evans libbe_handle_t *lbh; 88ee16b7c9SKyle Evans zfs_handle_t *rootds; 89c3a34c08SKyle Evans char *poolname, *pos; 90c3a34c08SKyle Evans int pnamelen; 9128f16a0fSKyle Evans 92c3a34c08SKyle Evans lbh = NULL; 93c3a34c08SKyle Evans poolname = pos = NULL; 94ee16b7c9SKyle Evans rootds = NULL; 95ee16b7c9SKyle Evans 9628f16a0fSKyle Evans /* Verify that /boot and / are mounted on the same filesystem */ 97c3a34c08SKyle Evans /* TODO: use errno here?? */ 98c3a34c08SKyle Evans if (stat("/", &sb) != 0) 99c3a34c08SKyle Evans goto err; 10028f16a0fSKyle Evans 10128f16a0fSKyle Evans root_dev = sb.st_dev; 10228f16a0fSKyle Evans 103c3a34c08SKyle Evans if (stat("/boot", &sb) != 0) 104c3a34c08SKyle Evans goto err; 10528f16a0fSKyle Evans 10628f16a0fSKyle Evans boot_dev = sb.st_dev; 10728f16a0fSKyle Evans 10828f16a0fSKyle Evans if (root_dev != boot_dev) { 10928f16a0fSKyle Evans fprintf(stderr, "/ and /boot not on same device, quitting\n"); 110c3a34c08SKyle Evans goto err; 11128f16a0fSKyle Evans } 11228f16a0fSKyle Evans 113c3a34c08SKyle Evans if ((lbh = calloc(1, sizeof(libbe_handle_t))) == NULL) 114c3a34c08SKyle Evans goto err; 11528f16a0fSKyle Evans 116c3a34c08SKyle Evans if ((lbh->lzh = libzfs_init()) == NULL) 117c3a34c08SKyle Evans goto err; 11828f16a0fSKyle Evans 11928f16a0fSKyle Evans /* Obtain path to boot environment root */ 12055b0e92bSKyle Evans if ((kenv(KENV_GET, "zfs_be_root", lbh->root, 12155b0e92bSKyle Evans sizeof(lbh->root))) == -1) 122c3a34c08SKyle Evans goto err; 12328f16a0fSKyle Evans 12428f16a0fSKyle Evans /* Remove leading 'zfs:' if present, otherwise use value as-is */ 125c3a34c08SKyle Evans if (strcmp(lbh->root, "zfs:") == 0) 126cc4deabcSKyle Evans strlcpy(lbh->root, strchr(lbh->root, ':') + sizeof(char), 12755b0e92bSKyle Evans sizeof(lbh->root)); 128c3a34c08SKyle Evans 129c3a34c08SKyle Evans if ((pos = strchr(lbh->root, '/')) == NULL) 130c3a34c08SKyle Evans goto err; 131c3a34c08SKyle Evans 132c3a34c08SKyle Evans pnamelen = pos - lbh->root; 133c3a34c08SKyle Evans poolname = malloc(pnamelen + 1); 134c3a34c08SKyle Evans if (poolname == NULL) 135c3a34c08SKyle Evans goto err; 136c3a34c08SKyle Evans 13755b0e92bSKyle Evans strlcpy(poolname, lbh->root, pnamelen + 1); 138c3a34c08SKyle Evans if ((lbh->active_phandle = zpool_open(lbh->lzh, poolname)) == NULL) 139c3a34c08SKyle Evans goto err; 140a8e44f4dSKyle Evans free(poolname); 141a8e44f4dSKyle Evans poolname = NULL; 142c3a34c08SKyle Evans 143c3a34c08SKyle Evans if (zpool_get_prop(lbh->active_phandle, ZPOOL_PROP_BOOTFS, lbh->bootfs, 14455b0e92bSKyle Evans sizeof(lbh->bootfs), NULL, true) != 0) 145c3a34c08SKyle Evans goto err; 146c3a34c08SKyle Evans 147c3a34c08SKyle Evans /* Obtain path to boot environment rootfs (currently booted) */ 148c3a34c08SKyle Evans /* XXX Get dataset mounted at / by kenv/GUID from mountroot? */ 149ee16b7c9SKyle Evans if ((rootds = zfs_open(lbh->lzh, lbh->root, ZFS_TYPE_DATASET)) == NULL) 150c3a34c08SKyle Evans goto err; 151c3a34c08SKyle Evans 152ee16b7c9SKyle Evans zfs_iter_filesystems(rootds, be_locate_rootfs, lbh); 153ee16b7c9SKyle Evans zfs_close(rootds); 154ff8676ccSKyle Evans rootds = NULL; 155b29bf2f8SKyle Evans if (*lbh->rootfs == '\0') 156ff8676ccSKyle Evans goto err; 15728f16a0fSKyle Evans 15828f16a0fSKyle Evans return (lbh); 159c3a34c08SKyle Evans err: 160c3a34c08SKyle Evans if (lbh != NULL) { 161c3a34c08SKyle Evans if (lbh->active_phandle != NULL) 162c3a34c08SKyle Evans zpool_close(lbh->active_phandle); 163c3a34c08SKyle Evans if (lbh->lzh != NULL) 164c3a34c08SKyle Evans libzfs_fini(lbh->lzh); 165c3a34c08SKyle Evans free(lbh); 166c3a34c08SKyle Evans } 167c3a34c08SKyle Evans free(poolname); 168c3a34c08SKyle Evans return (NULL); 16928f16a0fSKyle Evans } 17028f16a0fSKyle Evans 17128f16a0fSKyle Evans 17228f16a0fSKyle Evans /* 17328f16a0fSKyle Evans * Free memory allocated by libbe_init() 17428f16a0fSKyle Evans */ 17528f16a0fSKyle Evans void 17628f16a0fSKyle Evans libbe_close(libbe_handle_t *lbh) 17728f16a0fSKyle Evans { 178bfe0869cSKyle Evans 179c3a34c08SKyle Evans if (lbh->active_phandle != NULL) 180c3a34c08SKyle Evans zpool_close(lbh->active_phandle); 18128f16a0fSKyle Evans libzfs_fini(lbh->lzh); 18228f16a0fSKyle Evans free(lbh); 18328f16a0fSKyle Evans } 18428f16a0fSKyle Evans 1859b1662e6SKyle Evans /* 1869b1662e6SKyle Evans * Proxy through to libzfs for the moment. 1879b1662e6SKyle Evans */ 1889b1662e6SKyle Evans void 1899b1662e6SKyle Evans be_nicenum(uint64_t num, char *buf, size_t buflen) 1909b1662e6SKyle Evans { 1919b1662e6SKyle Evans 1929b1662e6SKyle Evans zfs_nicenum(num, buf, buflen); 1939b1662e6SKyle Evans } 19428f16a0fSKyle Evans 195920abf4dSKyle Evans static int 196920abf4dSKyle Evans be_destroy_cb(zfs_handle_t *zfs_hdl, void *data) 197920abf4dSKyle Evans { 198920abf4dSKyle Evans int err; 199920abf4dSKyle Evans 200920abf4dSKyle Evans if ((err = zfs_iter_children(zfs_hdl, be_destroy_cb, data)) != 0) 201920abf4dSKyle Evans return (err); 202920abf4dSKyle Evans if ((err = zfs_destroy(zfs_hdl, false)) != 0) 203920abf4dSKyle Evans return (err); 204920abf4dSKyle Evans return (0); 205920abf4dSKyle Evans } 206920abf4dSKyle Evans 20728f16a0fSKyle Evans /* 20828f16a0fSKyle Evans * Destroy the boot environment or snapshot specified by the name 20928f16a0fSKyle Evans * parameter. Options are or'd together with the possible values: 21028f16a0fSKyle Evans * BE_DESTROY_FORCE : forces operation on mounted datasets 21128f16a0fSKyle Evans */ 21228f16a0fSKyle Evans int 21373c3d608SKyle Evans be_destroy(libbe_handle_t *lbh, const char *name, int options) 21428f16a0fSKyle Evans { 21528f16a0fSKyle Evans zfs_handle_t *fs; 21628f16a0fSKyle Evans char path[BE_MAXPATHLEN]; 217bfe0869cSKyle Evans char *p; 218bfe0869cSKyle Evans int err, force, mounted; 21928f16a0fSKyle Evans 220bfe0869cSKyle Evans p = path; 221bfe0869cSKyle Evans force = options & BE_DESTROY_FORCE; 22228f16a0fSKyle Evans 22328f16a0fSKyle Evans be_root_concat(lbh, name, path); 22428f16a0fSKyle Evans 22528f16a0fSKyle Evans if (strchr(name, '@') == NULL) { 226bfe0869cSKyle Evans if (!zfs_dataset_exists(lbh->lzh, path, ZFS_TYPE_FILESYSTEM)) 22728f16a0fSKyle Evans return (set_error(lbh, BE_ERR_NOENT)); 22828f16a0fSKyle Evans 229bfe0869cSKyle Evans if (strcmp(path, lbh->rootfs) == 0) 23028f16a0fSKyle Evans return (set_error(lbh, BE_ERR_DESTROYACT)); 23128f16a0fSKyle Evans 23228f16a0fSKyle Evans fs = zfs_open(lbh->lzh, p, ZFS_TYPE_FILESYSTEM); 23328f16a0fSKyle Evans } else { 23428f16a0fSKyle Evans 235bfe0869cSKyle Evans if (!zfs_dataset_exists(lbh->lzh, path, ZFS_TYPE_SNAPSHOT)) 23628f16a0fSKyle Evans return (set_error(lbh, BE_ERR_NOENT)); 23728f16a0fSKyle Evans 23828f16a0fSKyle Evans fs = zfs_open(lbh->lzh, p, ZFS_TYPE_SNAPSHOT); 23928f16a0fSKyle Evans } 24028f16a0fSKyle Evans 24128f16a0fSKyle Evans if (fs == NULL) 24228f16a0fSKyle Evans return (set_error(lbh, BE_ERR_ZFSOPEN)); 24328f16a0fSKyle Evans 24428f16a0fSKyle Evans /* Check if mounted, unmount if force is specified */ 245b29bf2f8SKyle Evans if ((mounted = zfs_is_mounted(fs, NULL)) != 0) { 246bfe0869cSKyle Evans if (force) 24728f16a0fSKyle Evans zfs_unmount(fs, NULL, 0); 248bfe0869cSKyle Evans else 24928f16a0fSKyle Evans return (set_error(lbh, BE_ERR_DESTROYMNT)); 25028f16a0fSKyle Evans } 25128f16a0fSKyle Evans 252920abf4dSKyle Evans if ((err = be_destroy_cb(fs, NULL)) != 0) { 253920abf4dSKyle Evans /* Children are still present or the mount is referenced */ 254920abf4dSKyle Evans if (err == EBUSY) 255920abf4dSKyle Evans return (set_error(lbh, BE_ERR_DESTROYMNT)); 256920abf4dSKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 257920abf4dSKyle Evans } 25828f16a0fSKyle Evans 259920abf4dSKyle Evans return (0); 26028f16a0fSKyle Evans } 26128f16a0fSKyle Evans 26228f16a0fSKyle Evans 26328f16a0fSKyle Evans int 264b29bf2f8SKyle Evans be_snapshot(libbe_handle_t *lbh, const char *source, const char *snap_name, 26528f16a0fSKyle Evans bool recursive, char *result) 26628f16a0fSKyle Evans { 26728f16a0fSKyle Evans char buf[BE_MAXPATHLEN]; 26828f16a0fSKyle Evans time_t rawtime; 26928f16a0fSKyle Evans int len, err; 27028f16a0fSKyle Evans 27128f16a0fSKyle Evans be_root_concat(lbh, source, buf); 27228f16a0fSKyle Evans 273bfe0869cSKyle Evans if (!be_exists(lbh, buf)) 27428f16a0fSKyle Evans return (BE_ERR_NOENT); 27528f16a0fSKyle Evans 27628f16a0fSKyle Evans if (snap_name != NULL) { 277a8e44f4dSKyle Evans if (strlcat(buf, "@", sizeof(buf)) >= sizeof(buf)) 278a8e44f4dSKyle Evans return (set_error(lbh, BE_ERR_INVALIDNAME)); 279a8e44f4dSKyle Evans 280a8e44f4dSKyle Evans if (strlcat(buf, snap_name, sizeof(buf)) >= sizeof(buf)) 281a8e44f4dSKyle Evans return (set_error(lbh, BE_ERR_INVALIDNAME)); 282a8e44f4dSKyle Evans 283bfe0869cSKyle Evans if (result != NULL) 28428f16a0fSKyle Evans snprintf(result, BE_MAXPATHLEN, "%s@%s", source, 28528f16a0fSKyle Evans snap_name); 28628f16a0fSKyle Evans } else { 28728f16a0fSKyle Evans time(&rawtime); 28828f16a0fSKyle Evans len = strlen(buf); 28955b0e92bSKyle Evans strftime(buf + len, sizeof(buf) - len, 29028f16a0fSKyle Evans "@%F-%T", localtime(&rawtime)); 291a8e44f4dSKyle Evans if (result != NULL && strlcpy(result, strrchr(buf, '/') + 1, 292a8e44f4dSKyle Evans sizeof(buf)) >= sizeof(buf)) 293a8e44f4dSKyle Evans return (set_error(lbh, BE_ERR_INVALIDNAME)); 29428f16a0fSKyle Evans } 29528f16a0fSKyle Evans 296b29bf2f8SKyle Evans if ((err = zfs_snapshot(lbh->lzh, buf, recursive, NULL)) != 0) { 29728f16a0fSKyle Evans switch (err) { 29828f16a0fSKyle Evans case EZFS_INVALIDNAME: 29928f16a0fSKyle Evans return (set_error(lbh, BE_ERR_INVALIDNAME)); 30028f16a0fSKyle Evans 30128f16a0fSKyle Evans default: 3022989df09SKyle Evans /* 3032989df09SKyle Evans * The other errors that zfs_ioc_snapshot might return 3042989df09SKyle Evans * shouldn't happen if we've set things up properly, so 3052989df09SKyle Evans * we'll gloss over them and call it UNKNOWN as it will 3062989df09SKyle Evans * require further triage. 3072989df09SKyle Evans */ 3082989df09SKyle Evans if (errno == ENOTSUP) 3092989df09SKyle Evans return (set_error(lbh, BE_ERR_NOPOOL)); 31028f16a0fSKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 31128f16a0fSKyle Evans } 31228f16a0fSKyle Evans } 31328f16a0fSKyle Evans 31428f16a0fSKyle Evans return (BE_ERR_SUCCESS); 31528f16a0fSKyle Evans } 31628f16a0fSKyle Evans 31728f16a0fSKyle Evans 31828f16a0fSKyle Evans /* 31928f16a0fSKyle Evans * Create the boot environment specified by the name parameter 32028f16a0fSKyle Evans */ 32128f16a0fSKyle Evans int 32273c3d608SKyle Evans be_create(libbe_handle_t *lbh, const char *name) 32328f16a0fSKyle Evans { 32428f16a0fSKyle Evans int err; 32528f16a0fSKyle Evans 326b29bf2f8SKyle Evans err = be_create_from_existing(lbh, name, be_active_path(lbh)); 32728f16a0fSKyle Evans 32828f16a0fSKyle Evans return (set_error(lbh, err)); 32928f16a0fSKyle Evans } 33028f16a0fSKyle Evans 33128f16a0fSKyle Evans 33228f16a0fSKyle Evans static int 33328f16a0fSKyle Evans be_deep_clone_prop(int prop, void *cb) 33428f16a0fSKyle Evans { 33528f16a0fSKyle Evans int err; 336bfe0869cSKyle Evans struct libbe_dccb *dccb; 33728f16a0fSKyle Evans zprop_source_t src; 33828f16a0fSKyle Evans char pval[BE_MAXPATHLEN]; 33928f16a0fSKyle Evans char source[BE_MAXPATHLEN]; 34028f16a0fSKyle Evans 341bfe0869cSKyle Evans dccb = cb; 34228f16a0fSKyle Evans /* Skip some properties we don't want to touch */ 34373c3d608SKyle Evans if (prop == ZFS_PROP_CANMOUNT) 34428f16a0fSKyle Evans return (ZPROP_CONT); 34528f16a0fSKyle Evans 34628f16a0fSKyle Evans /* Don't copy readonly properties */ 347bfe0869cSKyle Evans if (zfs_prop_readonly(prop)) 34828f16a0fSKyle Evans return (ZPROP_CONT); 34928f16a0fSKyle Evans 35028f16a0fSKyle Evans if ((err = zfs_prop_get(dccb->zhp, prop, (char *)&pval, 351bfe0869cSKyle Evans sizeof(pval), &src, (char *)&source, sizeof(source), false))) 35228f16a0fSKyle Evans /* Just continue if we fail to read a property */ 35328f16a0fSKyle Evans return (ZPROP_CONT); 354bfe0869cSKyle Evans 35528f16a0fSKyle Evans /* Only copy locally defined properties */ 356bfe0869cSKyle Evans if (src != ZPROP_SRC_LOCAL) 35728f16a0fSKyle Evans return (ZPROP_CONT); 35828f16a0fSKyle Evans 35928f16a0fSKyle Evans nvlist_add_string(dccb->props, zfs_prop_to_name(prop), (char *)pval); 36028f16a0fSKyle Evans 36128f16a0fSKyle Evans return (ZPROP_CONT); 36228f16a0fSKyle Evans } 36328f16a0fSKyle Evans 36428f16a0fSKyle Evans static int 36528f16a0fSKyle Evans be_deep_clone(zfs_handle_t *ds, void *data) 36628f16a0fSKyle Evans { 36728f16a0fSKyle Evans int err; 36828f16a0fSKyle Evans char be_path[BE_MAXPATHLEN]; 36928f16a0fSKyle Evans char snap_path[BE_MAXPATHLEN]; 37028f16a0fSKyle Evans const char *dspath; 37128f16a0fSKyle Evans char *dsname; 37228f16a0fSKyle Evans zfs_handle_t *snap_hdl; 37328f16a0fSKyle Evans nvlist_t *props; 374bfe0869cSKyle Evans struct libbe_deep_clone *isdc, sdc; 37528f16a0fSKyle Evans struct libbe_dccb dccb; 37628f16a0fSKyle Evans 377bfe0869cSKyle Evans isdc = (struct libbe_deep_clone *)data; 37828f16a0fSKyle Evans dspath = zfs_get_name(ds); 379bfe0869cSKyle Evans if ((dsname = strrchr(dspath, '/')) == NULL) 38028f16a0fSKyle Evans return (BE_ERR_UNKNOWN); 38128f16a0fSKyle Evans dsname++; 382bfe0869cSKyle Evans 383bfe0869cSKyle Evans if (isdc->bename == NULL) 38428f16a0fSKyle Evans snprintf(be_path, sizeof(be_path), "%s/%s", isdc->be_root, dsname); 385bfe0869cSKyle Evans else 38628f16a0fSKyle Evans snprintf(be_path, sizeof(be_path), "%s/%s", isdc->be_root, isdc->bename); 387bfe0869cSKyle Evans 38828f16a0fSKyle Evans snprintf(snap_path, sizeof(snap_path), "%s@%s", dspath, isdc->snapname); 38928f16a0fSKyle Evans 390bfe0869cSKyle Evans if (zfs_dataset_exists(isdc->lbh->lzh, be_path, ZFS_TYPE_DATASET)) 39128f16a0fSKyle Evans return (set_error(isdc->lbh, BE_ERR_EXISTS)); 39228f16a0fSKyle Evans 39328f16a0fSKyle Evans if ((snap_hdl = 394bfe0869cSKyle Evans zfs_open(isdc->lbh->lzh, snap_path, ZFS_TYPE_SNAPSHOT)) == NULL) 39528f16a0fSKyle Evans return (set_error(isdc->lbh, BE_ERR_ZFSOPEN)); 39628f16a0fSKyle Evans 39728f16a0fSKyle Evans nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP); 39828f16a0fSKyle Evans nvlist_add_string(props, "canmount", "noauto"); 39928f16a0fSKyle Evans 40028f16a0fSKyle Evans dccb.zhp = ds; 40128f16a0fSKyle Evans dccb.props = props; 40228f16a0fSKyle Evans if (zprop_iter(be_deep_clone_prop, &dccb, B_FALSE, B_FALSE, 403bfe0869cSKyle Evans ZFS_TYPE_FILESYSTEM) == ZPROP_INVAL) 40428f16a0fSKyle Evans return (-1); 40528f16a0fSKyle Evans 406cc4deabcSKyle Evans if ((err = zfs_clone(snap_hdl, be_path, props)) != 0) 40728f16a0fSKyle Evans err = BE_ERR_ZFSCLONE; 40828f16a0fSKyle Evans 40928f16a0fSKyle Evans nvlist_free(props); 41028f16a0fSKyle Evans zfs_close(snap_hdl); 41128f16a0fSKyle Evans 412cc4deabcSKyle Evans /* Failed to clone */ 413cc4deabcSKyle Evans if (err != BE_ERR_SUCCESS) 414cc4deabcSKyle Evans return (set_error(isdc->lbh, err)); 415cc4deabcSKyle Evans 41628f16a0fSKyle Evans sdc.lbh = isdc->lbh; 41728f16a0fSKyle Evans sdc.bename = NULL; 41828f16a0fSKyle Evans sdc.snapname = isdc->snapname; 41928f16a0fSKyle Evans sdc.be_root = (char *)&be_path; 42028f16a0fSKyle Evans 42128f16a0fSKyle Evans err = zfs_iter_filesystems(ds, be_deep_clone, &sdc); 42228f16a0fSKyle Evans 42328f16a0fSKyle Evans return (err); 42428f16a0fSKyle Evans } 42528f16a0fSKyle Evans 42628f16a0fSKyle Evans /* 42728f16a0fSKyle Evans * Create the boot environment from pre-existing snapshot 42828f16a0fSKyle Evans */ 42928f16a0fSKyle Evans int 430b29bf2f8SKyle Evans be_create_from_existing_snap(libbe_handle_t *lbh, const char *name, 431b29bf2f8SKyle Evans const char *snap) 43228f16a0fSKyle Evans { 43328f16a0fSKyle Evans int err; 43428f16a0fSKyle Evans char be_path[BE_MAXPATHLEN]; 43528f16a0fSKyle Evans char snap_path[BE_MAXPATHLEN]; 436b29bf2f8SKyle Evans const char *bename; 437b29bf2f8SKyle Evans char *parentname, *snapname; 43828f16a0fSKyle Evans zfs_handle_t *parent_hdl; 43928f16a0fSKyle Evans struct libbe_deep_clone sdc; 44028f16a0fSKyle Evans 441b29bf2f8SKyle Evans if ((err = be_validate_name(lbh, name)) != 0) 44228f16a0fSKyle Evans return (set_error(lbh, err)); 443b29bf2f8SKyle Evans if ((err = be_root_concat(lbh, snap, snap_path)) != 0) 44428f16a0fSKyle Evans return (set_error(lbh, err)); 445b29bf2f8SKyle Evans if ((err = be_validate_snap(lbh, snap_path)) != 0) 44628f16a0fSKyle Evans return (set_error(lbh, err)); 44728f16a0fSKyle Evans 448b29bf2f8SKyle Evans if ((err = be_root_concat(lbh, name, be_path)) != 0) 44928f16a0fSKyle Evans return (set_error(lbh, err)); 45028f16a0fSKyle Evans 451bfe0869cSKyle Evans if ((bename = strrchr(name, '/')) == NULL) 45228f16a0fSKyle Evans bename = name; 453bfe0869cSKyle Evans else 45428f16a0fSKyle Evans bename++; 455bfe0869cSKyle Evans 456cc4deabcSKyle Evans if ((parentname = strdup(snap_path)) == NULL) 457cc4deabcSKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 458cc4deabcSKyle Evans 45928f16a0fSKyle Evans snapname = strchr(parentname, '@'); 46028f16a0fSKyle Evans if (snapname == NULL) { 461cc4deabcSKyle Evans free(parentname); 462cc4deabcSKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 46328f16a0fSKyle Evans } 46428f16a0fSKyle Evans *snapname = '\0'; 46528f16a0fSKyle Evans snapname++; 46628f16a0fSKyle Evans 46728f16a0fSKyle Evans sdc.lbh = lbh; 46828f16a0fSKyle Evans sdc.bename = bename; 46928f16a0fSKyle Evans sdc.snapname = snapname; 47028f16a0fSKyle Evans sdc.be_root = lbh->root; 47128f16a0fSKyle Evans 47228f16a0fSKyle Evans parent_hdl = zfs_open(lbh->lzh, parentname, ZFS_TYPE_DATASET); 47328f16a0fSKyle Evans err = be_deep_clone(parent_hdl, &sdc); 47428f16a0fSKyle Evans 475cc4deabcSKyle Evans free(parentname); 47628f16a0fSKyle Evans return (set_error(lbh, err)); 47728f16a0fSKyle Evans } 47828f16a0fSKyle Evans 47928f16a0fSKyle Evans 48028f16a0fSKyle Evans /* 48128f16a0fSKyle Evans * Create a boot environment from an existing boot environment 48228f16a0fSKyle Evans */ 48328f16a0fSKyle Evans int 484b29bf2f8SKyle Evans be_create_from_existing(libbe_handle_t *lbh, const char *name, const char *old) 48528f16a0fSKyle Evans { 48628f16a0fSKyle Evans int err; 48728f16a0fSKyle Evans char buf[BE_MAXPATHLEN]; 48828f16a0fSKyle Evans 489*5b7803a9SKyle Evans if ((err = be_snapshot(lbh, old, NULL, true, (char *)&buf)) != 0) 49028f16a0fSKyle Evans return (set_error(lbh, err)); 49128f16a0fSKyle Evans 49228f16a0fSKyle Evans err = be_create_from_existing_snap(lbh, name, (char *)buf); 49328f16a0fSKyle Evans 49428f16a0fSKyle Evans return (set_error(lbh, err)); 49528f16a0fSKyle Evans } 49628f16a0fSKyle Evans 49728f16a0fSKyle Evans 49828f16a0fSKyle Evans /* 49928f16a0fSKyle Evans * Verifies that a snapshot has a valid name, exists, and has a mountpoint of 50028f16a0fSKyle Evans * '/'. Returns BE_ERR_SUCCESS (0), upon success, or the relevant BE_ERR_* upon 50128f16a0fSKyle Evans * failure. Does not set the internal library error state. 50228f16a0fSKyle Evans */ 50328f16a0fSKyle Evans int 504b29bf2f8SKyle Evans be_validate_snap(libbe_handle_t *lbh, const char *snap_name) 50528f16a0fSKyle Evans { 50628f16a0fSKyle Evans zfs_handle_t *zfs_hdl; 50755b0e92bSKyle Evans char buf[BE_MAXPATHLEN]; 50828f16a0fSKyle Evans char *delim_pos; 50928f16a0fSKyle Evans int err = BE_ERR_SUCCESS; 51028f16a0fSKyle Evans 511bfe0869cSKyle Evans if (strlen(snap_name) >= BE_MAXPATHLEN) 51228f16a0fSKyle Evans return (BE_ERR_PATHLEN); 51328f16a0fSKyle Evans 51428f16a0fSKyle Evans if (!zfs_dataset_exists(lbh->lzh, snap_name, 515bfe0869cSKyle Evans ZFS_TYPE_SNAPSHOT)) 51628f16a0fSKyle Evans return (BE_ERR_NOENT); 51728f16a0fSKyle Evans 51855b0e92bSKyle Evans strlcpy(buf, snap_name, sizeof(buf)); 51928f16a0fSKyle Evans 52028f16a0fSKyle Evans /* Find the base filesystem of the snapshot */ 521bfe0869cSKyle Evans if ((delim_pos = strchr(buf, '@')) == NULL) 52228f16a0fSKyle Evans return (BE_ERR_INVALIDNAME); 52328f16a0fSKyle Evans *delim_pos = '\0'; 52428f16a0fSKyle Evans 52528f16a0fSKyle Evans if ((zfs_hdl = 526bfe0869cSKyle Evans zfs_open(lbh->lzh, buf, ZFS_TYPE_DATASET)) == NULL) 52728f16a0fSKyle Evans return (BE_ERR_NOORIGIN); 52828f16a0fSKyle Evans 52955b0e92bSKyle Evans if ((err = zfs_prop_get(zfs_hdl, ZFS_PROP_MOUNTPOINT, buf, 53055b0e92bSKyle Evans sizeof(buf), NULL, NULL, 0, 1)) != 0) 53128f16a0fSKyle Evans err = BE_ERR_INVORIGIN; 53228f16a0fSKyle Evans 53355b0e92bSKyle Evans if ((err != 0) && (strncmp(buf, "/", sizeof(buf)) != 0)) 53428f16a0fSKyle Evans err = BE_ERR_INVORIGIN; 53528f16a0fSKyle Evans 53628f16a0fSKyle Evans zfs_close(zfs_hdl); 53728f16a0fSKyle Evans 53828f16a0fSKyle Evans return (err); 53928f16a0fSKyle Evans } 54028f16a0fSKyle Evans 54128f16a0fSKyle Evans 54228f16a0fSKyle Evans /* 54328f16a0fSKyle Evans * Idempotently appends the name argument to the root boot environment path 54428f16a0fSKyle Evans * and copies the resulting string into the result buffer (which is assumed 54528f16a0fSKyle Evans * to be at least BE_MAXPATHLEN characters long. Returns BE_ERR_SUCCESS upon 54628f16a0fSKyle Evans * success, BE_ERR_PATHLEN if the resulting path is longer than BE_MAXPATHLEN, 54728f16a0fSKyle Evans * or BE_ERR_INVALIDNAME if the name is a path that does not begin with 54828f16a0fSKyle Evans * zfs_be_root. Does not set internal library error state. 54928f16a0fSKyle Evans */ 55028f16a0fSKyle Evans int 551b29bf2f8SKyle Evans be_root_concat(libbe_handle_t *lbh, const char *name, char *result) 55228f16a0fSKyle Evans { 55328f16a0fSKyle Evans size_t name_len, root_len; 55428f16a0fSKyle Evans 55528f16a0fSKyle Evans name_len = strlen(name); 55628f16a0fSKyle Evans root_len = strlen(lbh->root); 55728f16a0fSKyle Evans 55828f16a0fSKyle Evans /* Act idempotently; return be name if it is already a full path */ 55928f16a0fSKyle Evans if (strrchr(name, '/') != NULL) { 560bfe0869cSKyle Evans if (strstr(name, lbh->root) != name) 56128f16a0fSKyle Evans return (BE_ERR_INVALIDNAME); 56228f16a0fSKyle Evans 563bfe0869cSKyle Evans if (name_len >= BE_MAXPATHLEN) 56428f16a0fSKyle Evans return (BE_ERR_PATHLEN); 56528f16a0fSKyle Evans 56655b0e92bSKyle Evans strlcpy(result, name, BE_MAXPATHLEN); 56728f16a0fSKyle Evans return (BE_ERR_SUCCESS); 56828f16a0fSKyle Evans } else if (name_len + root_len + 1 < BE_MAXPATHLEN) { 56928f16a0fSKyle Evans snprintf(result, BE_MAXPATHLEN, "%s/%s", lbh->root, 57028f16a0fSKyle Evans name); 57128f16a0fSKyle Evans return (BE_ERR_SUCCESS); 57228f16a0fSKyle Evans } 57328f16a0fSKyle Evans 57428f16a0fSKyle Evans return (BE_ERR_PATHLEN); 57528f16a0fSKyle Evans } 57628f16a0fSKyle Evans 57728f16a0fSKyle Evans 57828f16a0fSKyle Evans /* 57928f16a0fSKyle Evans * Verifies the validity of a boot environment name (A-Za-z0-9-_.). Returns 580*5b7803a9SKyle Evans * BE_ERR_SUCCESS (0) if name is valid, otherwise returns BE_ERR_INVALIDNAME 581*5b7803a9SKyle Evans * or BE_ERR_PATHLEN. 58228f16a0fSKyle Evans * Does not set internal library error state. 58328f16a0fSKyle Evans */ 58428f16a0fSKyle Evans int 585*5b7803a9SKyle Evans be_validate_name(libbe_handle_t *lbh, const char *name) 58628f16a0fSKyle Evans { 58728f16a0fSKyle Evans for (int i = 0; *name; i++) { 58828f16a0fSKyle Evans char c = *(name++); 589bfe0869cSKyle Evans if (isalnum(c) || (c == '-') || (c == '_') || (c == '.')) 59028f16a0fSKyle Evans continue; 59128f16a0fSKyle Evans return (BE_ERR_INVALIDNAME); 59228f16a0fSKyle Evans } 59328f16a0fSKyle Evans 594*5b7803a9SKyle Evans /* 595*5b7803a9SKyle Evans * Impose the additional restriction that the entire dataset name must 596*5b7803a9SKyle Evans * not exceed the maximum length of a dataset, i.e. MAXNAMELEN. 597*5b7803a9SKyle Evans */ 598*5b7803a9SKyle Evans if (strlen(lbh->root) + 1 + strlen(name) > MAXNAMELEN) 599*5b7803a9SKyle Evans return (BE_ERR_PATHLEN); 60028f16a0fSKyle Evans return (BE_ERR_SUCCESS); 60128f16a0fSKyle Evans } 60228f16a0fSKyle Evans 60328f16a0fSKyle Evans 60428f16a0fSKyle Evans /* 60528f16a0fSKyle Evans * usage 60628f16a0fSKyle Evans */ 60728f16a0fSKyle Evans int 60873c3d608SKyle Evans be_rename(libbe_handle_t *lbh, const char *old, const char *new) 60928f16a0fSKyle Evans { 61028f16a0fSKyle Evans char full_old[BE_MAXPATHLEN]; 61128f16a0fSKyle Evans char full_new[BE_MAXPATHLEN]; 61228f16a0fSKyle Evans zfs_handle_t *zfs_hdl; 61328f16a0fSKyle Evans int err; 61428f16a0fSKyle Evans 615*5b7803a9SKyle Evans /* 616*5b7803a9SKyle Evans * be_validate_name is documented not to set error state, so we should 617*5b7803a9SKyle Evans * do so here. 618*5b7803a9SKyle Evans */ 619*5b7803a9SKyle Evans if ((err = be_validate_name(lbh, new)) != 0) 620*5b7803a9SKyle Evans return (set_error(lbh, err)); 621b29bf2f8SKyle Evans if ((err = be_root_concat(lbh, old, full_old)) != 0) 62228f16a0fSKyle Evans return (set_error(lbh, err)); 623b29bf2f8SKyle Evans if ((err = be_root_concat(lbh, new, full_new)) != 0) 62428f16a0fSKyle Evans return (set_error(lbh, err)); 62528f16a0fSKyle Evans 626bfe0869cSKyle Evans /* Check if old is active BE */ 6272989df09SKyle Evans if (strcmp(full_old, be_active_path(lbh)) == 0) 6282989df09SKyle Evans return (set_error(lbh, BE_ERR_MOUNTED)); 62928f16a0fSKyle Evans 630bfe0869cSKyle Evans if (!zfs_dataset_exists(lbh->lzh, full_old, ZFS_TYPE_DATASET)) 6312989df09SKyle Evans return (set_error(lbh, BE_ERR_NOENT)); 63228f16a0fSKyle Evans 633bfe0869cSKyle Evans if (zfs_dataset_exists(lbh->lzh, full_new, ZFS_TYPE_DATASET)) 6342989df09SKyle Evans return (set_error(lbh, BE_ERR_EXISTS)); 63528f16a0fSKyle Evans 63628f16a0fSKyle Evans if ((zfs_hdl = zfs_open(lbh->lzh, full_old, 637bfe0869cSKyle Evans ZFS_TYPE_FILESYSTEM)) == NULL) 6382989df09SKyle Evans return (set_error(lbh, BE_ERR_ZFSOPEN)); 63928f16a0fSKyle Evans 6406d4b1d24SKyle Evans /* XXX TODO: Allow a force flag */ 6416d4b1d24SKyle Evans if (zfs_is_mounted(zfs_hdl, NULL)) { 6426d4b1d24SKyle Evans zfs_close(zfs_hdl); 6436d4b1d24SKyle Evans return (set_error(lbh, BE_ERR_MOUNTED)); 6446d4b1d24SKyle Evans } 6456d4b1d24SKyle Evans 646bfe0869cSKyle Evans /* recurse, nounmount, forceunmount */ 64728f16a0fSKyle Evans struct renameflags flags = { 0, 0, 0 }; 64828f16a0fSKyle Evans 64928f16a0fSKyle Evans err = zfs_rename(zfs_hdl, NULL, full_new, flags); 65028f16a0fSKyle Evans 65128f16a0fSKyle Evans zfs_close(zfs_hdl); 652*5b7803a9SKyle Evans if (err != 0) 653*5b7803a9SKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 654*5b7803a9SKyle Evans return (0); 65528f16a0fSKyle Evans } 65628f16a0fSKyle Evans 65728f16a0fSKyle Evans 65828f16a0fSKyle Evans int 65973c3d608SKyle Evans be_export(libbe_handle_t *lbh, const char *bootenv, int fd) 66028f16a0fSKyle Evans { 66128f16a0fSKyle Evans char snap_name[BE_MAXPATHLEN]; 66228f16a0fSKyle Evans char buf[BE_MAXPATHLEN]; 66328f16a0fSKyle Evans zfs_handle_t *zfs; 66428f16a0fSKyle Evans int err; 66528f16a0fSKyle Evans 666b29bf2f8SKyle Evans if ((err = be_snapshot(lbh, bootenv, NULL, true, snap_name)) != 0) 6676d4b1d24SKyle Evans /* Use the error set by be_snapshot */ 6686d4b1d24SKyle Evans return (err); 66928f16a0fSKyle Evans 67028f16a0fSKyle Evans be_root_concat(lbh, snap_name, buf); 67128f16a0fSKyle Evans 672bfe0869cSKyle Evans if ((zfs = zfs_open(lbh->lzh, buf, ZFS_TYPE_DATASET)) == NULL) 673506f5fdfSKyle Evans return (set_error(lbh, BE_ERR_ZFSOPEN)); 67428f16a0fSKyle Evans 67528f16a0fSKyle Evans err = zfs_send_one(zfs, NULL, fd, 0); 6766d4b1d24SKyle Evans zfs_close(zfs); 6776d4b1d24SKyle Evans 67828f16a0fSKyle Evans return (err); 67928f16a0fSKyle Evans } 68028f16a0fSKyle Evans 68128f16a0fSKyle Evans 68228f16a0fSKyle Evans int 68373c3d608SKyle Evans be_import(libbe_handle_t *lbh, const char *bootenv, int fd) 68428f16a0fSKyle Evans { 68528f16a0fSKyle Evans char buf[BE_MAXPATHLEN]; 68628f16a0fSKyle Evans time_t rawtime; 68728f16a0fSKyle Evans nvlist_t *props; 68828f16a0fSKyle Evans zfs_handle_t *zfs; 68928f16a0fSKyle Evans int err, len; 690506f5fdfSKyle Evans char nbuf[24]; 69128f16a0fSKyle Evans 692bfe0869cSKyle Evans /* 693506f5fdfSKyle Evans * We don't need this to be incredibly random, just unique enough that 694506f5fdfSKyle Evans * it won't conflict with an existing dataset name. Chopping time 695506f5fdfSKyle Evans * down to 32 bits is probably good enough for this. 696bfe0869cSKyle Evans */ 697506f5fdfSKyle Evans snprintf(nbuf, 24, "tmp%u", 698506f5fdfSKyle Evans (uint32_t)(time(NULL) & 0xFFFFFFFF)); 699506f5fdfSKyle Evans if ((err = be_root_concat(lbh, nbuf, buf)) != 0) 700506f5fdfSKyle Evans /* 701506f5fdfSKyle Evans * Technically this is our problem, but we try to use short 702506f5fdfSKyle Evans * enough names that we won't run into problems except in 703506f5fdfSKyle Evans * worst-case BE root approaching MAXPATHLEN. 704506f5fdfSKyle Evans */ 705506f5fdfSKyle Evans return (set_error(lbh, BE_ERR_PATHLEN)); 70628f16a0fSKyle Evans 70728f16a0fSKyle Evans time(&rawtime); 70828f16a0fSKyle Evans len = strlen(buf); 70955b0e92bSKyle Evans strftime(buf + len, sizeof(buf) - len, "@%F-%T", localtime(&rawtime)); 71028f16a0fSKyle Evans 711b29bf2f8SKyle Evans if ((err = lzc_receive(buf, NULL, NULL, false, fd)) != 0) { 712506f5fdfSKyle Evans switch (err) { 713506f5fdfSKyle Evans case EINVAL: 714506f5fdfSKyle Evans return (set_error(lbh, BE_ERR_NOORIGIN)); 715506f5fdfSKyle Evans case ENOENT: 716506f5fdfSKyle Evans return (set_error(lbh, BE_ERR_NOENT)); 717506f5fdfSKyle Evans case EIO: 718506f5fdfSKyle Evans return (set_error(lbh, BE_ERR_IO)); 719506f5fdfSKyle Evans default: 720506f5fdfSKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 721506f5fdfSKyle Evans } 72228f16a0fSKyle Evans } 72328f16a0fSKyle Evans 724bfe0869cSKyle Evans if ((zfs = zfs_open(lbh->lzh, buf, ZFS_TYPE_SNAPSHOT)) == NULL) 725506f5fdfSKyle Evans return (set_error(lbh, BE_ERR_ZFSOPEN)); 72628f16a0fSKyle Evans 72728f16a0fSKyle Evans nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP); 72828f16a0fSKyle Evans nvlist_add_string(props, "canmount", "noauto"); 72928f16a0fSKyle Evans nvlist_add_string(props, "mountpoint", "/"); 73028f16a0fSKyle Evans 73128f16a0fSKyle Evans be_root_concat(lbh, bootenv, buf); 73228f16a0fSKyle Evans 73328f16a0fSKyle Evans err = zfs_clone(zfs, buf, props); 73428f16a0fSKyle Evans zfs_close(zfs); 73528f16a0fSKyle Evans nvlist_free(props); 73628f16a0fSKyle Evans 7371b057aacSKyle Evans if (err != 0) 7381b057aacSKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 7391b057aacSKyle Evans 7401b057aacSKyle Evans /* 74100ece7c2SKyle Evans * Finally, we open up the dataset we just cloned the snapshot so that 7421b057aacSKyle Evans * we may promote it. This is necessary in order to clean up the ghost 74300ece7c2SKyle Evans * snapshot that doesn't need to be seen after the operation is 74400ece7c2SKyle Evans * complete. 7451b057aacSKyle Evans */ 7461b057aacSKyle Evans if ((zfs = zfs_open(lbh->lzh, buf, ZFS_TYPE_DATASET)) == NULL) 7471b057aacSKyle Evans return (set_error(lbh, BE_ERR_ZFSOPEN)); 7481b057aacSKyle Evans 7491b057aacSKyle Evans err = zfs_promote(zfs); 7501b057aacSKyle Evans zfs_close(zfs); 7511b057aacSKyle Evans 7521b057aacSKyle Evans if (err != 0) 7531b057aacSKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 7541b057aacSKyle Evans 7551b057aacSKyle Evans /* Clean up the temporary snapshot */ 7561b057aacSKyle Evans return (be_destroy(lbh, nbuf, 0)); 75728f16a0fSKyle Evans } 75828f16a0fSKyle Evans 7593d1a1f2cSKyle Evans #if SOON 760c65a2111SKyle Evans static int 761c65a2111SKyle Evans be_create_child_noent(libbe_handle_t *lbh, const char *active, 762c65a2111SKyle Evans const char *child_path) 763c65a2111SKyle Evans { 764c65a2111SKyle Evans nvlist_t *props; 765c65a2111SKyle Evans zfs_handle_t *zfs; 766c65a2111SKyle Evans int err; 767c65a2111SKyle Evans 768c65a2111SKyle Evans nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP); 769c65a2111SKyle Evans nvlist_add_string(props, "canmount", "noauto"); 770c65a2111SKyle Evans nvlist_add_string(props, "mountpoint", child_path); 771c65a2111SKyle Evans 772c65a2111SKyle Evans /* Create */ 773c65a2111SKyle Evans if ((err = zfs_create(lbh->lzh, active, ZFS_TYPE_DATASET, 774c65a2111SKyle Evans props)) != 0) { 775c65a2111SKyle Evans switch (err) { 776c65a2111SKyle Evans case EZFS_EXISTS: 777c65a2111SKyle Evans return (set_error(lbh, BE_ERR_EXISTS)); 778c65a2111SKyle Evans case EZFS_NOENT: 779c65a2111SKyle Evans return (set_error(lbh, BE_ERR_NOENT)); 780c65a2111SKyle Evans case EZFS_BADTYPE: 781c65a2111SKyle Evans case EZFS_BADVERSION: 782c65a2111SKyle Evans return (set_error(lbh, BE_ERR_NOPOOL)); 783c65a2111SKyle Evans case EZFS_BADPROP: 784c65a2111SKyle Evans default: 785c65a2111SKyle Evans /* We set something up wrong, probably... */ 786c65a2111SKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 787c65a2111SKyle Evans } 788c65a2111SKyle Evans } 789c65a2111SKyle Evans nvlist_free(props); 790c65a2111SKyle Evans 791c65a2111SKyle Evans if ((zfs = zfs_open(lbh->lzh, active, ZFS_TYPE_DATASET)) == NULL) 792c65a2111SKyle Evans return (set_error(lbh, BE_ERR_ZFSOPEN)); 793c65a2111SKyle Evans 794c65a2111SKyle Evans /* Set props */ 795c65a2111SKyle Evans if ((err = zfs_prop_set(zfs, "canmount", "noauto")) != 0) { 796c65a2111SKyle Evans zfs_close(zfs); 797c65a2111SKyle Evans /* 798c65a2111SKyle Evans * Similar to other cases, this shouldn't fail unless we've 799c65a2111SKyle Evans * done something wrong. This is a new dataset that shouldn't 800c65a2111SKyle Evans * have been mounted anywhere between creation and now. 801c65a2111SKyle Evans */ 802c65a2111SKyle Evans if (err == EZFS_NOMEM) 803c65a2111SKyle Evans return (set_error(lbh, BE_ERR_NOMEM)); 804c65a2111SKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 805c65a2111SKyle Evans } 806c65a2111SKyle Evans zfs_close(zfs); 807c65a2111SKyle Evans return (BE_ERR_SUCCESS); 808c65a2111SKyle Evans } 809c65a2111SKyle Evans 810c65a2111SKyle Evans static int 811c65a2111SKyle Evans be_create_child_cloned(libbe_handle_t *lbh, const char *active) 812c65a2111SKyle Evans { 8133d1a1f2cSKyle Evans char buf[BE_MAXPATHLEN], tmp[BE_MAXPATHLEN];; 814c65a2111SKyle Evans zfs_handle_t *zfs; 815c65a2111SKyle Evans int err; 816c65a2111SKyle Evans 817c65a2111SKyle Evans /* XXX TODO ? */ 818c65a2111SKyle Evans 819c65a2111SKyle Evans /* 820c65a2111SKyle Evans * Establish if the existing path is a zfs dataset or just 821c65a2111SKyle Evans * the subdirectory of one 822c65a2111SKyle Evans */ 8233d1a1f2cSKyle Evans strlcpy(tmp, "tmp/be_snap.XXXXX", sizeof(tmp)); 8243d1a1f2cSKyle Evans if (mktemp(tmp) == NULL) 825c65a2111SKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 826c65a2111SKyle Evans 8273d1a1f2cSKyle Evans be_root_concat(lbh, tmp, buf); 8283d1a1f2cSKyle Evans printf("Here %s?\n", buf); 829c65a2111SKyle Evans if ((err = zfs_snapshot(lbh->lzh, buf, false, NULL)) != 0) { 830c65a2111SKyle Evans switch (err) { 831c65a2111SKyle Evans case EZFS_INVALIDNAME: 832c65a2111SKyle Evans return (set_error(lbh, BE_ERR_INVALIDNAME)); 833c65a2111SKyle Evans 834c65a2111SKyle Evans default: 835c65a2111SKyle Evans /* 836c65a2111SKyle Evans * The other errors that zfs_ioc_snapshot might return 837c65a2111SKyle Evans * shouldn't happen if we've set things up properly, so 838c65a2111SKyle Evans * we'll gloss over them and call it UNKNOWN as it will 839c65a2111SKyle Evans * require further triage. 840c65a2111SKyle Evans */ 841c65a2111SKyle Evans if (errno == ENOTSUP) 842c65a2111SKyle Evans return (set_error(lbh, BE_ERR_NOPOOL)); 843c65a2111SKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 844c65a2111SKyle Evans } 845c65a2111SKyle Evans } 846c65a2111SKyle Evans 847c65a2111SKyle Evans /* Clone */ 848c65a2111SKyle Evans if ((zfs = zfs_open(lbh->lzh, buf, ZFS_TYPE_SNAPSHOT)) == NULL) 849c65a2111SKyle Evans return (BE_ERR_ZFSOPEN); 850c65a2111SKyle Evans 851c65a2111SKyle Evans if ((err = zfs_clone(zfs, active, NULL)) != 0) 852c65a2111SKyle Evans /* XXX TODO correct error */ 853c65a2111SKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 854c65a2111SKyle Evans 855c65a2111SKyle Evans /* set props */ 856c65a2111SKyle Evans zfs_close(zfs); 857c65a2111SKyle Evans return (BE_ERR_SUCCESS); 858c65a2111SKyle Evans } 85928f16a0fSKyle Evans 86028f16a0fSKyle Evans int 86173c3d608SKyle Evans be_add_child(libbe_handle_t *lbh, const char *child_path, bool cp_if_exists) 86228f16a0fSKyle Evans { 86373c3d608SKyle Evans struct stat sb; 864c65a2111SKyle Evans char active[BE_MAXPATHLEN], buf[BE_MAXPATHLEN]; 86528f16a0fSKyle Evans nvlist_t *props; 86673c3d608SKyle Evans const char *s; 86728f16a0fSKyle Evans 86828f16a0fSKyle Evans /* Require absolute paths */ 869bfe0869cSKyle Evans if (*child_path != '/') 8706d4b1d24SKyle Evans return (set_error(lbh, BE_ERR_BADPATH)); 87128f16a0fSKyle Evans 8726d4b1d24SKyle Evans strlcpy(active, be_active_path(lbh), BE_MAXPATHLEN); 87328f16a0fSKyle Evans strcpy(buf, active); 87428f16a0fSKyle Evans 87528f16a0fSKyle Evans /* Create non-mountable parent dataset(s) */ 87673c3d608SKyle Evans s = child_path; 87728f16a0fSKyle Evans for (char *p; (p = strchr(s+1, '/')) != NULL; s = p) { 87828f16a0fSKyle Evans size_t len = p - s; 87928f16a0fSKyle Evans strncat(buf, s, len); 88028f16a0fSKyle Evans 88128f16a0fSKyle Evans nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP); 88228f16a0fSKyle Evans nvlist_add_string(props, "canmount", "off"); 88328f16a0fSKyle Evans nvlist_add_string(props, "mountpoint", "none"); 88428f16a0fSKyle Evans zfs_create(lbh->lzh, buf, ZFS_TYPE_DATASET, props); 88528f16a0fSKyle Evans nvlist_free(props); 88628f16a0fSKyle Evans } 88728f16a0fSKyle Evans 88828f16a0fSKyle Evans /* Path does not exist as a descendent of / yet */ 8896d4b1d24SKyle Evans if (strlcat(active, child_path, BE_MAXPATHLEN) >= BE_MAXPATHLEN) 8906d4b1d24SKyle Evans return (set_error(lbh, BE_ERR_PATHLEN)); 89128f16a0fSKyle Evans 89228f16a0fSKyle Evans if (stat(child_path, &sb) != 0) { 89328f16a0fSKyle Evans /* Verify that error is ENOENT */ 8946d4b1d24SKyle Evans if (errno != ENOENT) 8956d4b1d24SKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 896c65a2111SKyle Evans return (be_create_child_noent(lbh, active, child_path)); 897c65a2111SKyle Evans } else if (cp_if_exists) 89828f16a0fSKyle Evans /* Path is already a descendent of / and should be copied */ 899c65a2111SKyle Evans return (be_create_child_cloned(lbh, active)); 9006d4b1d24SKyle Evans return (set_error(lbh, BE_ERR_EXISTS)); 90128f16a0fSKyle Evans } 9023d1a1f2cSKyle Evans #endif /* SOON */ 90328f16a0fSKyle Evans 904d06f7103SKyle Evans static int 905d06f7103SKyle Evans be_set_nextboot(libbe_handle_t *lbh, nvlist_t *config, uint64_t pool_guid, 906d06f7103SKyle Evans const char *zfsdev) 907d06f7103SKyle Evans { 908d06f7103SKyle Evans nvlist_t **child; 909d06f7103SKyle Evans uint64_t vdev_guid; 910d06f7103SKyle Evans int c, children; 911d06f7103SKyle Evans 912d06f7103SKyle Evans if (nvlist_lookup_nvlist_array(config, ZPOOL_CONFIG_CHILDREN, &child, 913d06f7103SKyle Evans &children) == 0) { 914d06f7103SKyle Evans for (c = 0; c < children; ++c) 915d06f7103SKyle Evans if (be_set_nextboot(lbh, child[c], pool_guid, zfsdev) != 0) 916d06f7103SKyle Evans return (1); 917d06f7103SKyle Evans return (0); 918d06f7103SKyle Evans } 919d06f7103SKyle Evans 920d06f7103SKyle Evans if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_GUID, 921d06f7103SKyle Evans &vdev_guid) != 0) { 922d06f7103SKyle Evans return (1); 923d06f7103SKyle Evans } 924d06f7103SKyle Evans 925d06f7103SKyle Evans if (zpool_nextboot(lbh->lzh, pool_guid, vdev_guid, zfsdev) != 0) { 926d06f7103SKyle Evans perror("ZFS_IOC_NEXTBOOT failed"); 927d06f7103SKyle Evans return (1); 928d06f7103SKyle Evans } 929d06f7103SKyle Evans 930d06f7103SKyle Evans return (0); 931d06f7103SKyle Evans } 932d06f7103SKyle Evans 93328f16a0fSKyle Evans 93428f16a0fSKyle Evans int 93573c3d608SKyle Evans be_activate(libbe_handle_t *lbh, const char *bootenv, bool temporary) 93628f16a0fSKyle Evans { 93728f16a0fSKyle Evans char be_path[BE_MAXPATHLEN]; 93828f16a0fSKyle Evans char buf[BE_MAXPATHLEN]; 93928f16a0fSKyle Evans uint64_t pool_guid; 940d06f7103SKyle Evans nvlist_t *config, *vdevs; 94128f16a0fSKyle Evans int err; 94228f16a0fSKyle Evans 94328f16a0fSKyle Evans be_root_concat(lbh, bootenv, be_path); 94428f16a0fSKyle Evans 94528f16a0fSKyle Evans /* Note: be_exists fails if mountpoint is not / */ 946bfe0869cSKyle Evans if (!be_exists(lbh, be_path)) 94728f16a0fSKyle Evans return (BE_ERR_NOENT); 94828f16a0fSKyle Evans 94928f16a0fSKyle Evans if (temporary) { 950d06f7103SKyle Evans config = zpool_get_config(lbh->active_phandle, NULL); 951c65a2111SKyle Evans if (config == NULL) 952c65a2111SKyle Evans /* config should be fetchable... */ 953c65a2111SKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 95428f16a0fSKyle Evans 955d06f7103SKyle Evans if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, 956d06f7103SKyle Evans &pool_guid) != 0) 957c65a2111SKyle Evans /* Similarly, it shouldn't be possible */ 958c65a2111SKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 959d06f7103SKyle Evans 96028f16a0fSKyle Evans /* Expected format according to zfsbootcfg(8) man */ 961a8e44f4dSKyle Evans snprintf(buf, sizeof(buf), "zfs:%s:", be_path); 96228f16a0fSKyle Evans 963c65a2111SKyle Evans /* We have no config tree */ 964c65a2111SKyle Evans if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, 965c65a2111SKyle Evans &vdevs) != 0) 966c65a2111SKyle Evans return (set_error(lbh, BE_ERR_NOPOOL)); 96728f16a0fSKyle Evans 968d06f7103SKyle Evans return (be_set_nextboot(lbh, vdevs, pool_guid, buf)); 96928f16a0fSKyle Evans } else { 97028f16a0fSKyle Evans /* Obtain bootenv zpool */ 971c3a34c08SKyle Evans err = zpool_set_prop(lbh->active_phandle, "bootfs", be_path); 97228f16a0fSKyle Evans 97328f16a0fSKyle Evans switch (err) { 97428f16a0fSKyle Evans case 0: 97528f16a0fSKyle Evans return (BE_ERR_SUCCESS); 97628f16a0fSKyle Evans 97728f16a0fSKyle Evans default: 978bfe0869cSKyle Evans /* XXX TODO correct errors */ 97928f16a0fSKyle Evans return (-1); 98028f16a0fSKyle Evans } 98128f16a0fSKyle Evans } 98228f16a0fSKyle Evans } 983