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 29*b6e7c421SKyle Evans #include <sys/cdefs.h> 30*b6e7c421SKyle Evans __FBSDID("$FreeBSD$"); 31*b6e7c421SKyle 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 4728f16a0fSKyle Evans /* 48ee16b7c9SKyle Evans * Iterator function for locating the rootfs amongst the children of the 49ee16b7c9SKyle Evans * zfs_be_root set by loader(8). data is expected to be a libbe_handle_t *. 50ee16b7c9SKyle Evans */ 51ee16b7c9SKyle Evans static int 52ee16b7c9SKyle Evans be_locate_rootfs(zfs_handle_t *chkds, void *data) 53ee16b7c9SKyle Evans { 54ee16b7c9SKyle Evans libbe_handle_t *lbh; 55ee16b7c9SKyle Evans char *mntpoint; 56ee16b7c9SKyle Evans 57ee16b7c9SKyle Evans lbh = (libbe_handle_t *)data; 58ee16b7c9SKyle Evans if (lbh == NULL) 59ee16b7c9SKyle Evans return (1); 60ee16b7c9SKyle Evans 61ee16b7c9SKyle Evans if (zfs_is_mounted(chkds, &mntpoint) && strcmp(mntpoint, "/") == 0) { 62ee16b7c9SKyle Evans strncpy(lbh->rootfs, zfs_get_name(chkds), BE_MAXPATHLEN); 63ee16b7c9SKyle Evans return (1); 64ee16b7c9SKyle Evans } 65ee16b7c9SKyle Evans 66ee16b7c9SKyle Evans return (0); 67ee16b7c9SKyle Evans } 68ee16b7c9SKyle Evans 69ee16b7c9SKyle Evans /* 7028f16a0fSKyle Evans * Initializes the libbe context to operate in the root boot environment 7128f16a0fSKyle Evans * dataset, for example, zroot/ROOT. 7228f16a0fSKyle Evans */ 7328f16a0fSKyle Evans libbe_handle_t * 7428f16a0fSKyle Evans libbe_init(void) 7528f16a0fSKyle Evans { 7628f16a0fSKyle Evans struct stat sb; 7728f16a0fSKyle Evans dev_t root_dev, boot_dev; 7828f16a0fSKyle Evans libbe_handle_t *lbh; 79ee16b7c9SKyle Evans zfs_handle_t *rootds; 80c3a34c08SKyle Evans char *poolname, *pos; 81c3a34c08SKyle Evans int pnamelen; 8228f16a0fSKyle Evans 83c3a34c08SKyle Evans lbh = NULL; 84c3a34c08SKyle Evans poolname = pos = NULL; 85c3a34c08SKyle Evans pnamelen = 0; 86ee16b7c9SKyle Evans rootds = NULL; 87ee16b7c9SKyle Evans 8828f16a0fSKyle Evans /* Verify that /boot and / are mounted on the same filesystem */ 89c3a34c08SKyle Evans /* TODO: use errno here?? */ 90c3a34c08SKyle Evans if (stat("/", &sb) != 0) 91c3a34c08SKyle Evans goto err; 9228f16a0fSKyle Evans 9328f16a0fSKyle Evans root_dev = sb.st_dev; 9428f16a0fSKyle Evans 95c3a34c08SKyle Evans if (stat("/boot", &sb) != 0) 96c3a34c08SKyle Evans goto err; 9728f16a0fSKyle Evans 9828f16a0fSKyle Evans boot_dev = sb.st_dev; 9928f16a0fSKyle Evans 10028f16a0fSKyle Evans if (root_dev != boot_dev) { 10128f16a0fSKyle Evans fprintf(stderr, "/ and /boot not on same device, quitting\n"); 102c3a34c08SKyle Evans goto err; 10328f16a0fSKyle Evans } 10428f16a0fSKyle Evans 105c3a34c08SKyle Evans if ((lbh = calloc(1, sizeof(libbe_handle_t))) == NULL) 106c3a34c08SKyle Evans goto err; 10728f16a0fSKyle Evans 108c3a34c08SKyle Evans if ((lbh->lzh = libzfs_init()) == NULL) 109c3a34c08SKyle Evans goto err; 11028f16a0fSKyle Evans 11128f16a0fSKyle Evans /* Obtain path to boot environment root */ 112c3a34c08SKyle Evans if ((kenv(KENV_GET, "zfs_be_root", lbh->root, BE_MAXPATHLEN)) == -1) 113c3a34c08SKyle Evans goto err; 11428f16a0fSKyle Evans 11528f16a0fSKyle Evans /* Remove leading 'zfs:' if present, otherwise use value as-is */ 116c3a34c08SKyle Evans if (strcmp(lbh->root, "zfs:") == 0) 117c3a34c08SKyle Evans strncpy(lbh->root, strchr(lbh->root, ':') + sizeof(char), 118c3a34c08SKyle Evans BE_MAXPATHLEN); 119c3a34c08SKyle Evans 120c3a34c08SKyle Evans if ((pos = strchr(lbh->root, '/')) == NULL) 121c3a34c08SKyle Evans goto err; 122c3a34c08SKyle Evans 123c3a34c08SKyle Evans pnamelen = pos - lbh->root; 124c3a34c08SKyle Evans poolname = malloc(pnamelen + 1); 125c3a34c08SKyle Evans if (poolname == NULL) 126c3a34c08SKyle Evans goto err; 127c3a34c08SKyle Evans 128c3a34c08SKyle Evans strncpy(poolname, lbh->root, pnamelen); 129c3a34c08SKyle Evans poolname[pnamelen] = '\0'; 130c3a34c08SKyle Evans if ((lbh->active_phandle = zpool_open(lbh->lzh, poolname)) == NULL) 131c3a34c08SKyle Evans goto err; 132c3a34c08SKyle Evans 133c3a34c08SKyle Evans if (zpool_get_prop(lbh->active_phandle, ZPOOL_PROP_BOOTFS, lbh->bootfs, 134c3a34c08SKyle Evans BE_MAXPATHLEN, NULL, true) != 0) 135c3a34c08SKyle Evans goto err; 136c3a34c08SKyle Evans 137c3a34c08SKyle Evans /* Obtain path to boot environment rootfs (currently booted) */ 138c3a34c08SKyle Evans /* XXX Get dataset mounted at / by kenv/GUID from mountroot? */ 139ee16b7c9SKyle Evans if ((rootds = zfs_open(lbh->lzh, lbh->root, ZFS_TYPE_DATASET)) == NULL) 140c3a34c08SKyle Evans goto err; 141c3a34c08SKyle Evans 142ee16b7c9SKyle Evans zfs_iter_filesystems(rootds, be_locate_rootfs, lbh); 143ee16b7c9SKyle Evans zfs_close(rootds); 144ff8676ccSKyle Evans rootds = NULL; 145b29bf2f8SKyle Evans if (*lbh->rootfs == '\0') 146ff8676ccSKyle Evans goto err; 14728f16a0fSKyle Evans 14828f16a0fSKyle Evans return (lbh); 149c3a34c08SKyle Evans err: 150c3a34c08SKyle Evans if (lbh != NULL) { 151c3a34c08SKyle Evans if (lbh->active_phandle != NULL) 152c3a34c08SKyle Evans zpool_close(lbh->active_phandle); 153c3a34c08SKyle Evans if (lbh->lzh != NULL) 154c3a34c08SKyle Evans libzfs_fini(lbh->lzh); 155c3a34c08SKyle Evans free(lbh); 156c3a34c08SKyle Evans } 157ee16b7c9SKyle Evans if (rootds != NULL) 158ee16b7c9SKyle Evans zfs_close(rootds); 159c3a34c08SKyle Evans free(poolname); 160c3a34c08SKyle Evans return (NULL); 16128f16a0fSKyle Evans } 16228f16a0fSKyle Evans 16328f16a0fSKyle Evans 16428f16a0fSKyle Evans /* 16528f16a0fSKyle Evans * Free memory allocated by libbe_init() 16628f16a0fSKyle Evans */ 16728f16a0fSKyle Evans void 16828f16a0fSKyle Evans libbe_close(libbe_handle_t *lbh) 16928f16a0fSKyle Evans { 170bfe0869cSKyle Evans 171c3a34c08SKyle Evans if (lbh->active_phandle != NULL) 172c3a34c08SKyle Evans zpool_close(lbh->active_phandle); 17328f16a0fSKyle Evans libzfs_fini(lbh->lzh); 17428f16a0fSKyle Evans free(lbh); 17528f16a0fSKyle Evans } 17628f16a0fSKyle Evans 1779b1662e6SKyle Evans /* 1789b1662e6SKyle Evans * Proxy through to libzfs for the moment. 1799b1662e6SKyle Evans */ 1809b1662e6SKyle Evans void 1819b1662e6SKyle Evans be_nicenum(uint64_t num, char *buf, size_t buflen) 1829b1662e6SKyle Evans { 1839b1662e6SKyle Evans 1849b1662e6SKyle Evans zfs_nicenum(num, buf, buflen); 1859b1662e6SKyle Evans } 18628f16a0fSKyle Evans 187920abf4dSKyle Evans static int 188920abf4dSKyle Evans be_destroy_cb(zfs_handle_t *zfs_hdl, void *data) 189920abf4dSKyle Evans { 190920abf4dSKyle Evans int err; 191920abf4dSKyle Evans 192920abf4dSKyle Evans if ((err = zfs_iter_children(zfs_hdl, be_destroy_cb, data)) != 0) 193920abf4dSKyle Evans return (err); 194920abf4dSKyle Evans if ((err = zfs_destroy(zfs_hdl, false)) != 0) 195920abf4dSKyle Evans return (err); 196920abf4dSKyle Evans return (0); 197920abf4dSKyle Evans } 198920abf4dSKyle Evans 19928f16a0fSKyle Evans /* 20028f16a0fSKyle Evans * Destroy the boot environment or snapshot specified by the name 20128f16a0fSKyle Evans * parameter. Options are or'd together with the possible values: 20228f16a0fSKyle Evans * BE_DESTROY_FORCE : forces operation on mounted datasets 20328f16a0fSKyle Evans */ 20428f16a0fSKyle Evans int 20528f16a0fSKyle Evans be_destroy(libbe_handle_t *lbh, char *name, int options) 20628f16a0fSKyle Evans { 20728f16a0fSKyle Evans zfs_handle_t *fs; 20828f16a0fSKyle Evans char path[BE_MAXPATHLEN]; 209bfe0869cSKyle Evans char *p; 210bfe0869cSKyle Evans int err, force, mounted; 21128f16a0fSKyle Evans 212bfe0869cSKyle Evans p = path; 213bfe0869cSKyle Evans force = options & BE_DESTROY_FORCE; 214bfe0869cSKyle Evans err = BE_ERR_SUCCESS; 21528f16a0fSKyle Evans 21628f16a0fSKyle Evans be_root_concat(lbh, name, path); 21728f16a0fSKyle Evans 21828f16a0fSKyle Evans if (strchr(name, '@') == NULL) { 219bfe0869cSKyle Evans if (!zfs_dataset_exists(lbh->lzh, path, ZFS_TYPE_FILESYSTEM)) 22028f16a0fSKyle Evans return (set_error(lbh, BE_ERR_NOENT)); 22128f16a0fSKyle Evans 222bfe0869cSKyle Evans if (strcmp(path, lbh->rootfs) == 0) 22328f16a0fSKyle Evans return (set_error(lbh, BE_ERR_DESTROYACT)); 22428f16a0fSKyle Evans 22528f16a0fSKyle Evans fs = zfs_open(lbh->lzh, p, ZFS_TYPE_FILESYSTEM); 22628f16a0fSKyle Evans } else { 22728f16a0fSKyle Evans 228bfe0869cSKyle Evans if (!zfs_dataset_exists(lbh->lzh, path, ZFS_TYPE_SNAPSHOT)) 22928f16a0fSKyle Evans return (set_error(lbh, BE_ERR_NOENT)); 23028f16a0fSKyle Evans 23128f16a0fSKyle Evans fs = zfs_open(lbh->lzh, p, ZFS_TYPE_SNAPSHOT); 23228f16a0fSKyle Evans } 23328f16a0fSKyle Evans 23428f16a0fSKyle Evans if (fs == NULL) 23528f16a0fSKyle Evans return (set_error(lbh, BE_ERR_ZFSOPEN)); 23628f16a0fSKyle Evans 23728f16a0fSKyle Evans /* Check if mounted, unmount if force is specified */ 238b29bf2f8SKyle Evans if ((mounted = zfs_is_mounted(fs, NULL)) != 0) { 239bfe0869cSKyle Evans if (force) 24028f16a0fSKyle Evans zfs_unmount(fs, NULL, 0); 241bfe0869cSKyle Evans else 24228f16a0fSKyle Evans return (set_error(lbh, BE_ERR_DESTROYMNT)); 24328f16a0fSKyle Evans } 24428f16a0fSKyle Evans 245920abf4dSKyle Evans if ((err = be_destroy_cb(fs, NULL)) != 0) { 246920abf4dSKyle Evans /* Children are still present or the mount is referenced */ 247920abf4dSKyle Evans if (err == EBUSY) 248920abf4dSKyle Evans return (set_error(lbh, BE_ERR_DESTROYMNT)); 249920abf4dSKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 250920abf4dSKyle Evans } 25128f16a0fSKyle Evans 252920abf4dSKyle Evans return (0); 25328f16a0fSKyle Evans } 25428f16a0fSKyle Evans 25528f16a0fSKyle Evans 25628f16a0fSKyle Evans int 257b29bf2f8SKyle Evans be_snapshot(libbe_handle_t *lbh, const char *source, const char *snap_name, 25828f16a0fSKyle Evans bool recursive, char *result) 25928f16a0fSKyle Evans { 26028f16a0fSKyle Evans char buf[BE_MAXPATHLEN]; 26128f16a0fSKyle Evans time_t rawtime; 26228f16a0fSKyle Evans int len, err; 26328f16a0fSKyle Evans 26428f16a0fSKyle Evans be_root_concat(lbh, source, buf); 26528f16a0fSKyle Evans 266bfe0869cSKyle Evans if (!be_exists(lbh, buf)) 26728f16a0fSKyle Evans return (BE_ERR_NOENT); 26828f16a0fSKyle Evans 26928f16a0fSKyle Evans if (snap_name != NULL) { 27028f16a0fSKyle Evans strcat(buf, "@"); 27128f16a0fSKyle Evans strcat(buf, snap_name); 272bfe0869cSKyle Evans if (result != NULL) 27328f16a0fSKyle Evans snprintf(result, BE_MAXPATHLEN, "%s@%s", source, 27428f16a0fSKyle Evans snap_name); 27528f16a0fSKyle Evans } else { 27628f16a0fSKyle Evans time(&rawtime); 27728f16a0fSKyle Evans len = strlen(buf); 27828f16a0fSKyle Evans strftime(buf + len, BE_MAXPATHLEN - len, 27928f16a0fSKyle Evans "@%F-%T", localtime(&rawtime)); 280bfe0869cSKyle Evans if (result != NULL) 28128f16a0fSKyle Evans strcpy(result, strrchr(buf, '/') + 1); 28228f16a0fSKyle Evans } 28328f16a0fSKyle Evans 284b29bf2f8SKyle Evans if ((err = zfs_snapshot(lbh->lzh, buf, recursive, NULL)) != 0) { 28528f16a0fSKyle Evans switch (err) { 28628f16a0fSKyle Evans case EZFS_INVALIDNAME: 28728f16a0fSKyle Evans return (set_error(lbh, BE_ERR_INVALIDNAME)); 28828f16a0fSKyle Evans 28928f16a0fSKyle Evans default: 290bfe0869cSKyle Evans /* XXX TODO: elaborate return codes */ 29128f16a0fSKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 29228f16a0fSKyle Evans } 29328f16a0fSKyle Evans } 29428f16a0fSKyle Evans 29528f16a0fSKyle Evans return (BE_ERR_SUCCESS); 29628f16a0fSKyle Evans } 29728f16a0fSKyle Evans 29828f16a0fSKyle Evans 29928f16a0fSKyle Evans /* 30028f16a0fSKyle Evans * Create the boot environment specified by the name parameter 30128f16a0fSKyle Evans */ 30228f16a0fSKyle Evans int 30328f16a0fSKyle Evans be_create(libbe_handle_t *lbh, char *name) 30428f16a0fSKyle Evans { 30528f16a0fSKyle Evans int err; 30628f16a0fSKyle Evans 307b29bf2f8SKyle Evans err = be_create_from_existing(lbh, name, be_active_path(lbh)); 30828f16a0fSKyle Evans 30928f16a0fSKyle Evans return (set_error(lbh, err)); 31028f16a0fSKyle Evans } 31128f16a0fSKyle Evans 31228f16a0fSKyle Evans 31328f16a0fSKyle Evans static int 31428f16a0fSKyle Evans be_deep_clone_prop(int prop, void *cb) 31528f16a0fSKyle Evans { 31628f16a0fSKyle Evans int err; 317bfe0869cSKyle Evans struct libbe_dccb *dccb; 31828f16a0fSKyle Evans zprop_source_t src; 31928f16a0fSKyle Evans char pval[BE_MAXPATHLEN]; 32028f16a0fSKyle Evans char source[BE_MAXPATHLEN]; 32128f16a0fSKyle Evans 322bfe0869cSKyle Evans dccb = cb; 32328f16a0fSKyle Evans /* Skip some properties we don't want to touch */ 32428f16a0fSKyle Evans switch (prop) { 32528f16a0fSKyle Evans case ZFS_PROP_CANMOUNT: 32628f16a0fSKyle Evans return (ZPROP_CONT); 32728f16a0fSKyle Evans break; 32828f16a0fSKyle Evans } 32928f16a0fSKyle Evans 33028f16a0fSKyle Evans /* Don't copy readonly properties */ 331bfe0869cSKyle Evans if (zfs_prop_readonly(prop)) 33228f16a0fSKyle Evans return (ZPROP_CONT); 33328f16a0fSKyle Evans 33428f16a0fSKyle Evans if ((err = zfs_prop_get(dccb->zhp, prop, (char *)&pval, 335bfe0869cSKyle Evans sizeof(pval), &src, (char *)&source, sizeof(source), false))) 33628f16a0fSKyle Evans /* Just continue if we fail to read a property */ 33728f16a0fSKyle Evans return (ZPROP_CONT); 338bfe0869cSKyle Evans 33928f16a0fSKyle Evans /* Only copy locally defined properties */ 340bfe0869cSKyle Evans if (src != ZPROP_SRC_LOCAL) 34128f16a0fSKyle Evans return (ZPROP_CONT); 34228f16a0fSKyle Evans 34328f16a0fSKyle Evans nvlist_add_string(dccb->props, zfs_prop_to_name(prop), (char *)pval); 34428f16a0fSKyle Evans 34528f16a0fSKyle Evans return (ZPROP_CONT); 34628f16a0fSKyle Evans } 34728f16a0fSKyle Evans 34828f16a0fSKyle Evans static int 34928f16a0fSKyle Evans be_deep_clone(zfs_handle_t *ds, void *data) 35028f16a0fSKyle Evans { 35128f16a0fSKyle Evans int err; 35228f16a0fSKyle Evans char be_path[BE_MAXPATHLEN]; 35328f16a0fSKyle Evans char snap_path[BE_MAXPATHLEN]; 35428f16a0fSKyle Evans const char *dspath; 35528f16a0fSKyle Evans char *dsname; 35628f16a0fSKyle Evans zfs_handle_t *snap_hdl; 35728f16a0fSKyle Evans nvlist_t *props; 358bfe0869cSKyle Evans struct libbe_deep_clone *isdc, sdc; 35928f16a0fSKyle Evans struct libbe_dccb dccb; 36028f16a0fSKyle Evans 361bfe0869cSKyle Evans isdc = (struct libbe_deep_clone *)data; 36228f16a0fSKyle Evans dspath = zfs_get_name(ds); 363bfe0869cSKyle Evans if ((dsname = strrchr(dspath, '/')) == NULL) 36428f16a0fSKyle Evans return (BE_ERR_UNKNOWN); 36528f16a0fSKyle Evans dsname++; 366bfe0869cSKyle Evans 367bfe0869cSKyle Evans if (isdc->bename == NULL) 36828f16a0fSKyle Evans snprintf(be_path, sizeof(be_path), "%s/%s", isdc->be_root, dsname); 369bfe0869cSKyle Evans else 37028f16a0fSKyle Evans snprintf(be_path, sizeof(be_path), "%s/%s", isdc->be_root, isdc->bename); 371bfe0869cSKyle Evans 37228f16a0fSKyle Evans snprintf(snap_path, sizeof(snap_path), "%s@%s", dspath, isdc->snapname); 37328f16a0fSKyle Evans 374bfe0869cSKyle Evans if (zfs_dataset_exists(isdc->lbh->lzh, be_path, ZFS_TYPE_DATASET)) 37528f16a0fSKyle Evans return (set_error(isdc->lbh, BE_ERR_EXISTS)); 37628f16a0fSKyle Evans 37728f16a0fSKyle Evans if ((snap_hdl = 378bfe0869cSKyle Evans zfs_open(isdc->lbh->lzh, snap_path, ZFS_TYPE_SNAPSHOT)) == NULL) 37928f16a0fSKyle Evans return (set_error(isdc->lbh, BE_ERR_ZFSOPEN)); 38028f16a0fSKyle Evans 38128f16a0fSKyle Evans nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP); 38228f16a0fSKyle Evans nvlist_add_string(props, "canmount", "noauto"); 38328f16a0fSKyle Evans 38428f16a0fSKyle Evans dccb.zhp = ds; 38528f16a0fSKyle Evans dccb.props = props; 38628f16a0fSKyle Evans if (zprop_iter(be_deep_clone_prop, &dccb, B_FALSE, B_FALSE, 387bfe0869cSKyle Evans ZFS_TYPE_FILESYSTEM) == ZPROP_INVAL) 38828f16a0fSKyle Evans return (-1); 38928f16a0fSKyle Evans 390b29bf2f8SKyle Evans if ((err = zfs_clone(snap_hdl, be_path, props)) != 0) { 39128f16a0fSKyle Evans switch (err) { 39228f16a0fSKyle Evans case EZFS_SUCCESS: 39328f16a0fSKyle Evans err = BE_ERR_SUCCESS; 39428f16a0fSKyle Evans break; 39528f16a0fSKyle Evans default: 39628f16a0fSKyle Evans err = BE_ERR_ZFSCLONE; 39728f16a0fSKyle Evans break; 39828f16a0fSKyle Evans } 39928f16a0fSKyle Evans } 40028f16a0fSKyle Evans 40128f16a0fSKyle Evans nvlist_free(props); 40228f16a0fSKyle Evans zfs_close(snap_hdl); 40328f16a0fSKyle Evans 40428f16a0fSKyle Evans sdc.lbh = isdc->lbh; 40528f16a0fSKyle Evans sdc.bename = NULL; 40628f16a0fSKyle Evans sdc.snapname = isdc->snapname; 40728f16a0fSKyle Evans sdc.be_root = (char *)&be_path; 40828f16a0fSKyle Evans 40928f16a0fSKyle Evans err = zfs_iter_filesystems(ds, be_deep_clone, &sdc); 41028f16a0fSKyle Evans 41128f16a0fSKyle Evans return (err); 41228f16a0fSKyle Evans } 41328f16a0fSKyle Evans 41428f16a0fSKyle Evans /* 41528f16a0fSKyle Evans * Create the boot environment from pre-existing snapshot 41628f16a0fSKyle Evans */ 41728f16a0fSKyle Evans int 418b29bf2f8SKyle Evans be_create_from_existing_snap(libbe_handle_t *lbh, const char *name, 419b29bf2f8SKyle Evans const char *snap) 42028f16a0fSKyle Evans { 42128f16a0fSKyle Evans int err; 42228f16a0fSKyle Evans char be_path[BE_MAXPATHLEN]; 42328f16a0fSKyle Evans char snap_path[BE_MAXPATHLEN]; 424b29bf2f8SKyle Evans const char *bename; 425b29bf2f8SKyle Evans char *parentname, *snapname; 42628f16a0fSKyle Evans zfs_handle_t *parent_hdl; 42728f16a0fSKyle Evans struct libbe_deep_clone sdc; 42828f16a0fSKyle Evans 429b29bf2f8SKyle Evans if ((err = be_validate_name(lbh, name)) != 0) 43028f16a0fSKyle Evans return (set_error(lbh, err)); 431b29bf2f8SKyle Evans if ((err = be_root_concat(lbh, snap, snap_path)) != 0) 43228f16a0fSKyle Evans return (set_error(lbh, err)); 433b29bf2f8SKyle Evans if ((err = be_validate_snap(lbh, snap_path)) != 0) 43428f16a0fSKyle Evans return (set_error(lbh, err)); 43528f16a0fSKyle Evans 436b29bf2f8SKyle Evans if ((err = be_root_concat(lbh, name, be_path)) != 0) 43728f16a0fSKyle Evans return (set_error(lbh, err)); 43828f16a0fSKyle Evans 439bfe0869cSKyle Evans if ((bename = strrchr(name, '/')) == NULL) 44028f16a0fSKyle Evans bename = name; 441bfe0869cSKyle Evans else 44228f16a0fSKyle Evans bename++; 443bfe0869cSKyle Evans 44428f16a0fSKyle Evans if ((parentname = strdup(snap_path)) == NULL) { 44528f16a0fSKyle Evans err = BE_ERR_UNKNOWN; 44628f16a0fSKyle Evans return (set_error(lbh, err)); 44728f16a0fSKyle Evans } 44828f16a0fSKyle Evans snapname = strchr(parentname, '@'); 44928f16a0fSKyle Evans if (snapname == NULL) { 45028f16a0fSKyle Evans err = BE_ERR_UNKNOWN; 45128f16a0fSKyle Evans return (set_error(lbh, err)); 45228f16a0fSKyle Evans } 45328f16a0fSKyle Evans *snapname = '\0'; 45428f16a0fSKyle Evans snapname++; 45528f16a0fSKyle Evans 45628f16a0fSKyle Evans sdc.lbh = lbh; 45728f16a0fSKyle Evans sdc.bename = bename; 45828f16a0fSKyle Evans sdc.snapname = snapname; 45928f16a0fSKyle Evans sdc.be_root = lbh->root; 46028f16a0fSKyle Evans 46128f16a0fSKyle Evans parent_hdl = zfs_open(lbh->lzh, parentname, ZFS_TYPE_DATASET); 46228f16a0fSKyle Evans err = be_deep_clone(parent_hdl, &sdc); 46328f16a0fSKyle Evans 46428f16a0fSKyle Evans return (set_error(lbh, err)); 46528f16a0fSKyle Evans } 46628f16a0fSKyle Evans 46728f16a0fSKyle Evans 46828f16a0fSKyle Evans /* 46928f16a0fSKyle Evans * Create a boot environment from an existing boot environment 47028f16a0fSKyle Evans */ 47128f16a0fSKyle Evans int 472b29bf2f8SKyle Evans be_create_from_existing(libbe_handle_t *lbh, const char *name, const char *old) 47328f16a0fSKyle Evans { 47428f16a0fSKyle Evans int err; 47528f16a0fSKyle Evans char buf[BE_MAXPATHLEN]; 47628f16a0fSKyle Evans 477bfe0869cSKyle Evans if ((err = be_snapshot(lbh, old, NULL, true, (char *)&buf))) 47828f16a0fSKyle Evans return (set_error(lbh, err)); 47928f16a0fSKyle Evans 48028f16a0fSKyle Evans err = be_create_from_existing_snap(lbh, name, (char *)buf); 48128f16a0fSKyle Evans 48228f16a0fSKyle Evans return (set_error(lbh, err)); 48328f16a0fSKyle Evans } 48428f16a0fSKyle Evans 48528f16a0fSKyle Evans 48628f16a0fSKyle Evans /* 48728f16a0fSKyle Evans * Verifies that a snapshot has a valid name, exists, and has a mountpoint of 48828f16a0fSKyle Evans * '/'. Returns BE_ERR_SUCCESS (0), upon success, or the relevant BE_ERR_* upon 48928f16a0fSKyle Evans * failure. Does not set the internal library error state. 49028f16a0fSKyle Evans */ 49128f16a0fSKyle Evans int 492b29bf2f8SKyle Evans be_validate_snap(libbe_handle_t *lbh, const char *snap_name) 49328f16a0fSKyle Evans { 49428f16a0fSKyle Evans zfs_handle_t *zfs_hdl; 49528f16a0fSKyle Evans char buf[BE_MAXPATHLEN]; 49628f16a0fSKyle Evans char *delim_pos; 49728f16a0fSKyle Evans int err = BE_ERR_SUCCESS; 49828f16a0fSKyle Evans 499bfe0869cSKyle Evans if (strlen(snap_name) >= BE_MAXPATHLEN) 50028f16a0fSKyle Evans return (BE_ERR_PATHLEN); 50128f16a0fSKyle Evans 50228f16a0fSKyle Evans if (!zfs_dataset_exists(lbh->lzh, snap_name, 503bfe0869cSKyle Evans ZFS_TYPE_SNAPSHOT)) 50428f16a0fSKyle Evans return (BE_ERR_NOENT); 50528f16a0fSKyle Evans 50628f16a0fSKyle Evans strncpy(buf, snap_name, BE_MAXPATHLEN); 50728f16a0fSKyle Evans 50828f16a0fSKyle Evans /* Find the base filesystem of the snapshot */ 509bfe0869cSKyle Evans if ((delim_pos = strchr(buf, '@')) == NULL) 51028f16a0fSKyle Evans return (BE_ERR_INVALIDNAME); 51128f16a0fSKyle Evans *delim_pos = '\0'; 51228f16a0fSKyle Evans 51328f16a0fSKyle Evans if ((zfs_hdl = 514bfe0869cSKyle Evans zfs_open(lbh->lzh, buf, ZFS_TYPE_DATASET)) == NULL) 51528f16a0fSKyle Evans return (BE_ERR_NOORIGIN); 51628f16a0fSKyle Evans 517b29bf2f8SKyle Evans if ((err = zfs_prop_get(zfs_hdl, ZFS_PROP_MOUNTPOINT, buf, BE_MAXPATHLEN, 518b29bf2f8SKyle Evans NULL, NULL, 0, 1)) != 0) 51928f16a0fSKyle Evans err = BE_ERR_INVORIGIN; 52028f16a0fSKyle Evans 521bfe0869cSKyle Evans if ((err != 0) && (strncmp(buf, "/", BE_MAXPATHLEN) != 0)) 52228f16a0fSKyle Evans err = BE_ERR_INVORIGIN; 52328f16a0fSKyle Evans 52428f16a0fSKyle Evans zfs_close(zfs_hdl); 52528f16a0fSKyle Evans 52628f16a0fSKyle Evans return (err); 52728f16a0fSKyle Evans } 52828f16a0fSKyle Evans 52928f16a0fSKyle Evans 53028f16a0fSKyle Evans /* 53128f16a0fSKyle Evans * Idempotently appends the name argument to the root boot environment path 53228f16a0fSKyle Evans * and copies the resulting string into the result buffer (which is assumed 53328f16a0fSKyle Evans * to be at least BE_MAXPATHLEN characters long. Returns BE_ERR_SUCCESS upon 53428f16a0fSKyle Evans * success, BE_ERR_PATHLEN if the resulting path is longer than BE_MAXPATHLEN, 53528f16a0fSKyle Evans * or BE_ERR_INVALIDNAME if the name is a path that does not begin with 53628f16a0fSKyle Evans * zfs_be_root. Does not set internal library error state. 53728f16a0fSKyle Evans */ 53828f16a0fSKyle Evans int 539b29bf2f8SKyle Evans be_root_concat(libbe_handle_t *lbh, const char *name, char *result) 54028f16a0fSKyle Evans { 54128f16a0fSKyle Evans size_t name_len, root_len; 54228f16a0fSKyle Evans 54328f16a0fSKyle Evans name_len = strlen(name); 54428f16a0fSKyle Evans root_len = strlen(lbh->root); 54528f16a0fSKyle Evans 54628f16a0fSKyle Evans /* Act idempotently; return be name if it is already a full path */ 54728f16a0fSKyle Evans if (strrchr(name, '/') != NULL) { 548bfe0869cSKyle Evans if (strstr(name, lbh->root) != name) 54928f16a0fSKyle Evans return (BE_ERR_INVALIDNAME); 55028f16a0fSKyle Evans 551bfe0869cSKyle Evans if (name_len >= BE_MAXPATHLEN) 55228f16a0fSKyle Evans return (BE_ERR_PATHLEN); 55328f16a0fSKyle Evans 55428f16a0fSKyle Evans strncpy(result, name, BE_MAXPATHLEN); 55528f16a0fSKyle Evans return (BE_ERR_SUCCESS); 55628f16a0fSKyle Evans } else if (name_len + root_len + 1 < BE_MAXPATHLEN) { 55728f16a0fSKyle Evans snprintf(result, BE_MAXPATHLEN, "%s/%s", lbh->root, 55828f16a0fSKyle Evans name); 55928f16a0fSKyle Evans return (BE_ERR_SUCCESS); 56028f16a0fSKyle Evans } 56128f16a0fSKyle Evans 56228f16a0fSKyle Evans return (BE_ERR_PATHLEN); 56328f16a0fSKyle Evans } 56428f16a0fSKyle Evans 56528f16a0fSKyle Evans 56628f16a0fSKyle Evans /* 56728f16a0fSKyle Evans * Verifies the validity of a boot environment name (A-Za-z0-9-_.). Returns 56828f16a0fSKyle Evans * BE_ERR_SUCCESS (0) if name is valid, otherwise returns BE_ERR_INVALIDNAME. 56928f16a0fSKyle Evans * Does not set internal library error state. 57028f16a0fSKyle Evans */ 57128f16a0fSKyle Evans int 572b29bf2f8SKyle Evans be_validate_name(libbe_handle_t *lbh __unused, const char *name) 57328f16a0fSKyle Evans { 57428f16a0fSKyle Evans for (int i = 0; *name; i++) { 57528f16a0fSKyle Evans char c = *(name++); 576bfe0869cSKyle Evans if (isalnum(c) || (c == '-') || (c == '_') || (c == '.')) 57728f16a0fSKyle Evans continue; 57828f16a0fSKyle Evans return (BE_ERR_INVALIDNAME); 57928f16a0fSKyle Evans } 58028f16a0fSKyle Evans 58128f16a0fSKyle Evans return (BE_ERR_SUCCESS); 58228f16a0fSKyle Evans } 58328f16a0fSKyle Evans 58428f16a0fSKyle Evans 58528f16a0fSKyle Evans /* 58628f16a0fSKyle Evans * usage 58728f16a0fSKyle Evans */ 58828f16a0fSKyle Evans int 58928f16a0fSKyle Evans be_rename(libbe_handle_t *lbh, char *old, char *new) 59028f16a0fSKyle Evans { 59128f16a0fSKyle Evans char full_old[BE_MAXPATHLEN]; 59228f16a0fSKyle Evans char full_new[BE_MAXPATHLEN]; 59328f16a0fSKyle Evans zfs_handle_t *zfs_hdl; 59428f16a0fSKyle Evans int err; 59528f16a0fSKyle Evans 596b29bf2f8SKyle Evans if ((err = be_root_concat(lbh, old, full_old)) != 0) 59728f16a0fSKyle Evans return (set_error(lbh, err)); 598b29bf2f8SKyle Evans if ((err = be_root_concat(lbh, new, full_new)) != 0) 59928f16a0fSKyle Evans return (set_error(lbh, err)); 60028f16a0fSKyle Evans 601b29bf2f8SKyle Evans if (be_validate_name(lbh, new) != 0) 60228f16a0fSKyle Evans return (BE_ERR_UNKNOWN); 603bfe0869cSKyle Evans /* XXX TODO set and return correct error */ 60428f16a0fSKyle Evans 605bfe0869cSKyle Evans /* Check if old is active BE */ 606bfe0869cSKyle Evans if (strcmp(full_new, be_active_path(lbh)) == 0) 60728f16a0fSKyle Evans return (BE_ERR_UNKNOWN); 608bfe0869cSKyle Evans /* XXX TODO set and return correct error */ 60928f16a0fSKyle Evans 610bfe0869cSKyle Evans if (!zfs_dataset_exists(lbh->lzh, full_old, ZFS_TYPE_DATASET)) 61128f16a0fSKyle Evans return (BE_ERR_UNKNOWN); 612bfe0869cSKyle Evans /* XXX TODO set and return correct error */ 61328f16a0fSKyle Evans 614bfe0869cSKyle Evans if (zfs_dataset_exists(lbh->lzh, full_new, ZFS_TYPE_DATASET)) 61528f16a0fSKyle Evans return (BE_ERR_UNKNOWN); 616bfe0869cSKyle Evans /* XXX TODO set and return correct error */ 61728f16a0fSKyle Evans 618bfe0869cSKyle Evans /* XXX TODO 619bfe0869cSKyle Evans * - What about mounted BEs? 620bfe0869cSKyle Evans * - if mounted error out unless a force flag is set? 621bfe0869cSKyle Evans */ 62228f16a0fSKyle Evans if ((zfs_hdl = zfs_open(lbh->lzh, full_old, 623bfe0869cSKyle Evans ZFS_TYPE_FILESYSTEM)) == NULL) 62428f16a0fSKyle Evans return (BE_ERR_UNKNOWN); 625bfe0869cSKyle Evans /* XXX TODO set and return correct error */ 62628f16a0fSKyle Evans 62728f16a0fSKyle Evans 628bfe0869cSKyle Evans /* recurse, nounmount, forceunmount */ 62928f16a0fSKyle Evans struct renameflags flags = { 0, 0, 0 }; 63028f16a0fSKyle Evans 631bfe0869cSKyle Evans /* XXX TODO: error log on this call */ 63228f16a0fSKyle Evans err = zfs_rename(zfs_hdl, NULL, full_new, flags); 63328f16a0fSKyle Evans 63428f16a0fSKyle Evans zfs_close(zfs_hdl); 63528f16a0fSKyle Evans 63628f16a0fSKyle Evans return (set_error(lbh, err)); 63728f16a0fSKyle Evans } 63828f16a0fSKyle Evans 63928f16a0fSKyle Evans 64028f16a0fSKyle Evans int 64128f16a0fSKyle Evans be_export(libbe_handle_t *lbh, char *bootenv, int fd) 64228f16a0fSKyle Evans { 64328f16a0fSKyle Evans char snap_name[BE_MAXPATHLEN]; 64428f16a0fSKyle Evans char buf[BE_MAXPATHLEN]; 64528f16a0fSKyle Evans zfs_handle_t *zfs; 64628f16a0fSKyle Evans int err; 64728f16a0fSKyle Evans 648b29bf2f8SKyle Evans if ((err = be_snapshot(lbh, bootenv, NULL, true, snap_name)) != 0) 649bfe0869cSKyle Evans /* XXX TODO error handle */ 65028f16a0fSKyle Evans return (-1); 65128f16a0fSKyle Evans 65228f16a0fSKyle Evans be_root_concat(lbh, snap_name, buf); 65328f16a0fSKyle Evans 654bfe0869cSKyle Evans if ((zfs = zfs_open(lbh->lzh, buf, ZFS_TYPE_DATASET)) == NULL) 65528f16a0fSKyle Evans return (BE_ERR_ZFSOPEN); 65628f16a0fSKyle Evans 65728f16a0fSKyle Evans err = zfs_send_one(zfs, NULL, fd, 0); 65828f16a0fSKyle Evans return (err); 65928f16a0fSKyle Evans } 66028f16a0fSKyle Evans 66128f16a0fSKyle Evans 66228f16a0fSKyle Evans int 66328f16a0fSKyle Evans be_import(libbe_handle_t *lbh, char *bootenv, int fd) 66428f16a0fSKyle Evans { 66528f16a0fSKyle Evans char buf[BE_MAXPATHLEN]; 66628f16a0fSKyle Evans time_t rawtime; 66728f16a0fSKyle Evans nvlist_t *props; 66828f16a0fSKyle Evans zfs_handle_t *zfs; 66928f16a0fSKyle Evans int err, len; 67028f16a0fSKyle Evans 671bfe0869cSKyle Evans /* 672bfe0869cSKyle Evans * XXX TODO: this is a very likely name for someone to already have 673bfe0869cSKyle Evans * used... we should avoid it. 674bfe0869cSKyle Evans */ 675d06f7103SKyle Evans if ((err = be_root_concat(lbh, "libbe_import_temp", buf)) != 0) 676bfe0869cSKyle Evans /* XXX TODO error handle */ 67728f16a0fSKyle Evans return (-1); 67828f16a0fSKyle Evans 67928f16a0fSKyle Evans time(&rawtime); 68028f16a0fSKyle Evans len = strlen(buf); 68128f16a0fSKyle Evans strftime(buf + len, BE_MAXPATHLEN - len, 68228f16a0fSKyle Evans "@%F-%T", localtime(&rawtime)); 68328f16a0fSKyle Evans 684bfe0869cSKyle Evans /* lzc_receive(SNAPNAME, PROPS, ORIGIN, FORCE, fd)) { */ 685b29bf2f8SKyle Evans if ((err = lzc_receive(buf, NULL, NULL, false, fd)) != 0) { 68628f16a0fSKyle Evans /* TODO: go through libzfs_core's recv_impl and find returned 68728f16a0fSKyle Evans * errors and set appropriate BE_ERR 68828f16a0fSKyle Evans * edit: errors are not in libzfs_core, my assumption is 68928f16a0fSKyle Evans * that they use libzfs errors 690bfe0869cSKyle Evans * note: 17 is err for dataset already existing 691bfe0869cSKyle Evans */ 69228f16a0fSKyle Evans return (err); 69328f16a0fSKyle Evans } 69428f16a0fSKyle Evans 695bfe0869cSKyle Evans if ((zfs = zfs_open(lbh->lzh, buf, ZFS_TYPE_SNAPSHOT)) == NULL) 696bfe0869cSKyle Evans /* XXX TODO correct error */ 69728f16a0fSKyle Evans return (-1); 69828f16a0fSKyle Evans 69928f16a0fSKyle Evans nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP); 70028f16a0fSKyle Evans nvlist_add_string(props, "canmount", "noauto"); 70128f16a0fSKyle Evans nvlist_add_string(props, "mountpoint", "/"); 70228f16a0fSKyle Evans 70328f16a0fSKyle Evans be_root_concat(lbh, bootenv, buf); 70428f16a0fSKyle Evans 70528f16a0fSKyle Evans err = zfs_clone(zfs, buf, props); 70628f16a0fSKyle Evans zfs_close(zfs); 70728f16a0fSKyle Evans 70828f16a0fSKyle Evans nvlist_free(props); 70928f16a0fSKyle Evans 710bfe0869cSKyle Evans /* XXX TODO: recursively delete be_import_temp dataset */ 71128f16a0fSKyle Evans return (err); 71228f16a0fSKyle Evans } 71328f16a0fSKyle Evans 71428f16a0fSKyle Evans 71528f16a0fSKyle Evans int 71628f16a0fSKyle Evans be_add_child(libbe_handle_t *lbh, char *child_path, bool cp_if_exists) 71728f16a0fSKyle Evans { 71828f16a0fSKyle Evans char active[BE_MAXPATHLEN]; 71928f16a0fSKyle Evans char buf[BE_MAXPATHLEN]; 72028f16a0fSKyle Evans nvlist_t *props; 72128f16a0fSKyle Evans zfs_handle_t *zfs; 72228f16a0fSKyle Evans struct stat sb; 72328f16a0fSKyle Evans int err; 72428f16a0fSKyle Evans 72528f16a0fSKyle Evans /* Require absolute paths */ 726bfe0869cSKyle Evans if (*child_path != '/') 727bfe0869cSKyle Evans /* XXX TODO: create appropriate error */ 72828f16a0fSKyle Evans return (-1); 72928f16a0fSKyle Evans 73028f16a0fSKyle Evans strncpy(active, be_active_path(lbh), BE_MAXPATHLEN); 73128f16a0fSKyle Evans strcpy(buf, active); 73228f16a0fSKyle Evans 73328f16a0fSKyle Evans /* Create non-mountable parent dataset(s) */ 73428f16a0fSKyle Evans char *s = child_path; 73528f16a0fSKyle Evans for (char *p; (p = strchr(s+1, '/')) != NULL; s = p) { 73628f16a0fSKyle Evans size_t len = p - s; 73728f16a0fSKyle Evans strncat(buf, s, len); 73828f16a0fSKyle Evans 73928f16a0fSKyle Evans nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP); 74028f16a0fSKyle Evans nvlist_add_string(props, "canmount", "off"); 74128f16a0fSKyle Evans nvlist_add_string(props, "mountpoint", "none"); 74228f16a0fSKyle Evans zfs_create(lbh->lzh, buf, ZFS_TYPE_DATASET, props); 74328f16a0fSKyle Evans nvlist_free(props); 74428f16a0fSKyle Evans } 74528f16a0fSKyle Evans 74628f16a0fSKyle Evans 74728f16a0fSKyle Evans /* Path does not exist as a descendent of / yet */ 74828f16a0fSKyle Evans int pos = strlen(active); 74928f16a0fSKyle Evans 750bfe0869cSKyle Evans /* XXX TODO: Verify that resulting str is less than BE_MAXPATHLEN */ 75128f16a0fSKyle Evans strncpy(&active[pos], child_path, BE_MAXPATHLEN-pos); 75228f16a0fSKyle Evans 75328f16a0fSKyle Evans if (stat(child_path, &sb) != 0) { 75428f16a0fSKyle Evans /* Verify that error is ENOENT */ 755bfe0869cSKyle Evans if (errno != 2) 756bfe0869cSKyle Evans /* XXX TODO: create appropriate error */ 75728f16a0fSKyle Evans return (-1); 75828f16a0fSKyle Evans 75928f16a0fSKyle Evans nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP); 76028f16a0fSKyle Evans nvlist_add_string(props, "canmount", "noauto"); 76128f16a0fSKyle Evans nvlist_add_string(props, "mountpoint", child_path); 76228f16a0fSKyle Evans 763bfe0869cSKyle Evans /* Create */ 764b29bf2f8SKyle Evans if ((err = 765b29bf2f8SKyle Evans zfs_create(lbh->lzh, active, ZFS_TYPE_DATASET, props)) != 0) 766bfe0869cSKyle Evans /* XXX TODO handle error */ 76728f16a0fSKyle Evans return (-1); 76828f16a0fSKyle Evans nvlist_free(props); 76928f16a0fSKyle Evans 77028f16a0fSKyle Evans if ((zfs = 771bfe0869cSKyle Evans zfs_open(lbh->lzh, active, ZFS_TYPE_DATASET)) == NULL) 772bfe0869cSKyle Evans /* XXX TODO handle error */ 77328f16a0fSKyle Evans return (-1); 77428f16a0fSKyle Evans 775bfe0869cSKyle Evans /* Set props */ 776b29bf2f8SKyle Evans if ((err = zfs_prop_set(zfs, "canmount", "noauto")) != 0) 77728f16a0fSKyle Evans /* TODO handle error */ 77828f16a0fSKyle Evans return (-1); 77928f16a0fSKyle Evans } else if (cp_if_exists) { 78028f16a0fSKyle Evans /* Path is already a descendent of / and should be copied */ 78128f16a0fSKyle Evans 782bfe0869cSKyle Evans /* XXX TODO ? */ 78328f16a0fSKyle Evans 78428f16a0fSKyle Evans /* 78528f16a0fSKyle Evans * Establish if the existing path is a zfs dataset or just 78628f16a0fSKyle Evans * the subdirectory of one 78728f16a0fSKyle Evans */ 78828f16a0fSKyle Evans 789bfe0869cSKyle Evans /* XXX TODO: use mktemp */ 79028f16a0fSKyle Evans long int snap_name = random(); 79128f16a0fSKyle Evans 79228f16a0fSKyle Evans snprintf(buf, BE_MAXPATHLEN, "%s@%ld", child_path, snap_name); 79328f16a0fSKyle Evans 794b29bf2f8SKyle Evans if ((err = zfs_snapshot(lbh->lzh, buf, false, NULL)) != 0) 795bfe0869cSKyle Evans /* XXX TODO correct error */ 79628f16a0fSKyle Evans return (-1); 79728f16a0fSKyle Evans 798bfe0869cSKyle Evans /* Clone */ 79928f16a0fSKyle Evans if ((zfs = 800bfe0869cSKyle Evans zfs_open(lbh->lzh, buf, ZFS_TYPE_SNAPSHOT)) == NULL) 801bfe0869cSKyle Evans /* XXX TODO correct error */ 80228f16a0fSKyle Evans return (-1); 80328f16a0fSKyle Evans 804b29bf2f8SKyle Evans if ((err = zfs_clone(zfs, active, NULL)) != 0) 805bfe0869cSKyle Evans /* XXX TODO correct error */ 80628f16a0fSKyle Evans return (-1); 80728f16a0fSKyle Evans 808bfe0869cSKyle Evans /* set props */ 809bfe0869cSKyle Evans } else 81028f16a0fSKyle Evans /* TODO: error code for exists, but not cp? */ 81128f16a0fSKyle Evans return (-1); 81228f16a0fSKyle Evans 81328f16a0fSKyle Evans return (BE_ERR_SUCCESS); 81428f16a0fSKyle Evans } 81528f16a0fSKyle Evans 816d06f7103SKyle Evans static int 817d06f7103SKyle Evans be_set_nextboot(libbe_handle_t *lbh, nvlist_t *config, uint64_t pool_guid, 818d06f7103SKyle Evans const char *zfsdev) 819d06f7103SKyle Evans { 820d06f7103SKyle Evans nvlist_t **child; 821d06f7103SKyle Evans uint64_t vdev_guid; 822d06f7103SKyle Evans int c, children; 823d06f7103SKyle Evans 824d06f7103SKyle Evans if (nvlist_lookup_nvlist_array(config, ZPOOL_CONFIG_CHILDREN, &child, 825d06f7103SKyle Evans &children) == 0) { 826d06f7103SKyle Evans for (c = 0; c < children; ++c) 827d06f7103SKyle Evans if (be_set_nextboot(lbh, child[c], pool_guid, zfsdev) != 0) 828d06f7103SKyle Evans return (1); 829d06f7103SKyle Evans return (0); 830d06f7103SKyle Evans } 831d06f7103SKyle Evans 832d06f7103SKyle Evans if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_GUID, 833d06f7103SKyle Evans &vdev_guid) != 0) { 834d06f7103SKyle Evans return (1); 835d06f7103SKyle Evans } 836d06f7103SKyle Evans 837d06f7103SKyle Evans if (zpool_nextboot(lbh->lzh, pool_guid, vdev_guid, zfsdev) != 0) { 838d06f7103SKyle Evans perror("ZFS_IOC_NEXTBOOT failed"); 839d06f7103SKyle Evans return (1); 840d06f7103SKyle Evans } 841d06f7103SKyle Evans 842d06f7103SKyle Evans return (0); 843d06f7103SKyle Evans } 844d06f7103SKyle Evans 84528f16a0fSKyle Evans 84628f16a0fSKyle Evans int 84728f16a0fSKyle Evans be_activate(libbe_handle_t *lbh, char *bootenv, bool temporary) 84828f16a0fSKyle Evans { 84928f16a0fSKyle Evans char be_path[BE_MAXPATHLEN]; 85028f16a0fSKyle Evans char buf[BE_MAXPATHLEN]; 85128f16a0fSKyle Evans uint64_t pool_guid; 852d06f7103SKyle Evans nvlist_t *config, *vdevs; 85328f16a0fSKyle Evans int err; 85428f16a0fSKyle Evans 85528f16a0fSKyle Evans be_root_concat(lbh, bootenv, be_path); 85628f16a0fSKyle Evans 85728f16a0fSKyle Evans /* Note: be_exists fails if mountpoint is not / */ 858bfe0869cSKyle Evans if (!be_exists(lbh, be_path)) 85928f16a0fSKyle Evans return (BE_ERR_NOENT); 86028f16a0fSKyle Evans 86128f16a0fSKyle Evans if (temporary) { 862d06f7103SKyle Evans config = zpool_get_config(lbh->active_phandle, NULL); 863d06f7103SKyle Evans if (config == NULL) { 864d06f7103SKyle Evans printf("no config\n"); 86528f16a0fSKyle Evans return (1); 86628f16a0fSKyle Evans } 86728f16a0fSKyle Evans 868d06f7103SKyle Evans if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, 869d06f7103SKyle Evans &pool_guid) != 0) 870d06f7103SKyle Evans return (1); 871d06f7103SKyle Evans 87228f16a0fSKyle Evans /* Expected format according to zfsbootcfg(8) man */ 87328f16a0fSKyle Evans strcpy(buf, "zfs:"); 87428f16a0fSKyle Evans strcat(buf, be_path); 87528f16a0fSKyle Evans strcat(buf, ":"); 87628f16a0fSKyle Evans 877d06f7103SKyle Evans if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, &vdevs) != 0) 87828f16a0fSKyle Evans return (1); 87928f16a0fSKyle Evans 880d06f7103SKyle Evans return (be_set_nextboot(lbh, vdevs, pool_guid, buf)); 88128f16a0fSKyle Evans } else { 88228f16a0fSKyle Evans /* Obtain bootenv zpool */ 883c3a34c08SKyle Evans err = zpool_set_prop(lbh->active_phandle, "bootfs", be_path); 88428f16a0fSKyle Evans 88528f16a0fSKyle Evans switch (err) { 88628f16a0fSKyle Evans case 0: 88728f16a0fSKyle Evans return (BE_ERR_SUCCESS); 88828f16a0fSKyle Evans 88928f16a0fSKyle Evans default: 890bfe0869cSKyle Evans /* XXX TODO correct errors */ 89128f16a0fSKyle Evans return (-1); 89228f16a0fSKyle Evans } 89328f16a0fSKyle Evans } 89428f16a0fSKyle Evans } 895