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 37*485172f5SKyle Evans #include <sys/zfs_context.h> 38*485172f5SKyle Evans #include <sys/mntent.h> 39*485172f5SKyle Evans 4028f16a0fSKyle Evans #include <ctype.h> 4128f16a0fSKyle Evans #include <libgen.h> 4228f16a0fSKyle Evans #include <libzfs_core.h> 4328f16a0fSKyle Evans #include <stdio.h> 4428f16a0fSKyle Evans #include <stdlib.h> 4528f16a0fSKyle Evans #include <time.h> 4628f16a0fSKyle Evans #include <unistd.h> 4728f16a0fSKyle Evans 4828f16a0fSKyle Evans #include "be.h" 4928f16a0fSKyle Evans #include "be_impl.h" 5028f16a0fSKyle Evans 51be7dd423SKyle Evans struct be_destroy_data { 52be7dd423SKyle Evans libbe_handle_t *lbh; 53be7dd423SKyle Evans char *snapname; 54be7dd423SKyle Evans }; 55be7dd423SKyle Evans 563d1a1f2cSKyle Evans #if SOON 57c65a2111SKyle Evans static int be_create_child_noent(libbe_handle_t *lbh, const char *active, 58c65a2111SKyle Evans const char *child_path); 59c65a2111SKyle Evans static int be_create_child_cloned(libbe_handle_t *lbh, const char *active); 603d1a1f2cSKyle Evans #endif 61c65a2111SKyle Evans 6290cf61e8SKyle Evans /* Arbitrary... should tune */ 6390cf61e8SKyle Evans #define BE_SNAP_SERIAL_MAX 1024 6490cf61e8SKyle Evans 6528f16a0fSKyle Evans /* 66ee16b7c9SKyle Evans * Iterator function for locating the rootfs amongst the children of the 67ee16b7c9SKyle Evans * zfs_be_root set by loader(8). data is expected to be a libbe_handle_t *. 68ee16b7c9SKyle Evans */ 69ee16b7c9SKyle Evans static int 7051aecc89SKyle Evans be_locate_rootfs(libbe_handle_t *lbh) 71ee16b7c9SKyle Evans { 724ab5187dSKyle Evans struct statfs sfs; 73*485172f5SKyle Evans struct mnttab entry; 7451aecc89SKyle Evans zfs_handle_t *zfs; 75ee16b7c9SKyle Evans 764ab5187dSKyle Evans /* 774ab5187dSKyle Evans * Check first if root is ZFS; if not, we'll bail on rootfs capture. 784ab5187dSKyle Evans * Unfortunately needed because zfs_path_to_zhandle will emit to 794ab5187dSKyle Evans * stderr if / isn't actually a ZFS filesystem, which we'd like 804ab5187dSKyle Evans * to avoid. 814ab5187dSKyle Evans */ 824ab5187dSKyle Evans if (statfs("/", &sfs) == 0) { 834ab5187dSKyle Evans statfs2mnttab(&sfs, &entry); 844ab5187dSKyle Evans if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) 854ab5187dSKyle Evans return (1); 864ab5187dSKyle Evans } else 874ab5187dSKyle Evans return (1); 8851aecc89SKyle Evans zfs = zfs_path_to_zhandle(lbh->lzh, "/", ZFS_TYPE_FILESYSTEM); 8951aecc89SKyle Evans if (zfs == NULL) 90ee16b7c9SKyle Evans return (1); 91ee16b7c9SKyle Evans 9251aecc89SKyle Evans strlcpy(lbh->rootfs, zfs_get_name(zfs), sizeof(lbh->rootfs)); 9351aecc89SKyle Evans zfs_close(zfs); 94ee16b7c9SKyle Evans return (0); 95ee16b7c9SKyle Evans } 96ee16b7c9SKyle Evans 97ee16b7c9SKyle Evans /* 9828f16a0fSKyle Evans * Initializes the libbe context to operate in the root boot environment 9928f16a0fSKyle Evans * dataset, for example, zroot/ROOT. 10028f16a0fSKyle Evans */ 10128f16a0fSKyle Evans libbe_handle_t * 102cc624025SKyle Evans libbe_init(const char *root) 10328f16a0fSKyle Evans { 104fc13fc1cSKyle Evans char altroot[MAXPATHLEN]; 10528f16a0fSKyle Evans libbe_handle_t *lbh; 106c3a34c08SKyle Evans char *poolname, *pos; 107c3a34c08SKyle Evans int pnamelen; 10828f16a0fSKyle Evans 109c3a34c08SKyle Evans lbh = NULL; 110c3a34c08SKyle Evans poolname = pos = NULL; 11128f16a0fSKyle Evans 112c3a34c08SKyle Evans if ((lbh = calloc(1, sizeof(libbe_handle_t))) == NULL) 113c3a34c08SKyle Evans goto err; 11428f16a0fSKyle Evans 115c3a34c08SKyle Evans if ((lbh->lzh = libzfs_init()) == NULL) 116c3a34c08SKyle Evans goto err; 11728f16a0fSKyle Evans 118cc624025SKyle Evans /* 119cc624025SKyle Evans * Grab rootfs, we'll work backwards from there if an optional BE root 120cc624025SKyle Evans * has not been passed in. 121cc624025SKyle Evans */ 1224ab5187dSKyle Evans if (be_locate_rootfs(lbh) != 0) { 1234ab5187dSKyle Evans if (root == NULL) 124c3a34c08SKyle Evans goto err; 1254ab5187dSKyle Evans *lbh->rootfs = '\0'; 1264ab5187dSKyle Evans } 127cc624025SKyle Evans if (root == NULL) { 128cc624025SKyle Evans /* Strip off the final slash from rootfs to get the be root */ 12951aecc89SKyle Evans strlcpy(lbh->root, lbh->rootfs, sizeof(lbh->root)); 13051aecc89SKyle Evans pos = strrchr(lbh->root, '/'); 13151aecc89SKyle Evans if (pos == NULL) 13251aecc89SKyle Evans goto err; 13351aecc89SKyle Evans *pos = '\0'; 134cc624025SKyle Evans } else 135cc624025SKyle Evans strlcpy(lbh->root, root, sizeof(lbh->root)); 136c3a34c08SKyle Evans 137c3a34c08SKyle Evans if ((pos = strchr(lbh->root, '/')) == NULL) 138c3a34c08SKyle Evans goto err; 139c3a34c08SKyle Evans 140c3a34c08SKyle Evans pnamelen = pos - lbh->root; 141c3a34c08SKyle Evans poolname = malloc(pnamelen + 1); 142c3a34c08SKyle Evans if (poolname == NULL) 143c3a34c08SKyle Evans goto err; 144c3a34c08SKyle Evans 14555b0e92bSKyle Evans strlcpy(poolname, lbh->root, pnamelen + 1); 146c3a34c08SKyle Evans if ((lbh->active_phandle = zpool_open(lbh->lzh, poolname)) == NULL) 147c3a34c08SKyle Evans goto err; 148a8e44f4dSKyle Evans free(poolname); 149a8e44f4dSKyle Evans poolname = NULL; 150c3a34c08SKyle Evans 151c3a34c08SKyle Evans if (zpool_get_prop(lbh->active_phandle, ZPOOL_PROP_BOOTFS, lbh->bootfs, 15255b0e92bSKyle Evans sizeof(lbh->bootfs), NULL, true) != 0) 153c3a34c08SKyle Evans goto err; 154c3a34c08SKyle Evans 155fc13fc1cSKyle Evans if (zpool_get_prop(lbh->active_phandle, ZPOOL_PROP_ALTROOT, 156fc13fc1cSKyle Evans altroot, sizeof(altroot), NULL, true) == 0 && 157fc13fc1cSKyle Evans strcmp(altroot, "-") != 0) 158fc13fc1cSKyle Evans lbh->altroot_len = strlen(altroot); 159fc13fc1cSKyle Evans 16028f16a0fSKyle Evans return (lbh); 161c3a34c08SKyle Evans err: 162c3a34c08SKyle Evans if (lbh != NULL) { 163c3a34c08SKyle Evans if (lbh->active_phandle != NULL) 164c3a34c08SKyle Evans zpool_close(lbh->active_phandle); 165c3a34c08SKyle Evans if (lbh->lzh != NULL) 166c3a34c08SKyle Evans libzfs_fini(lbh->lzh); 167c3a34c08SKyle Evans free(lbh); 168c3a34c08SKyle Evans } 169c3a34c08SKyle Evans free(poolname); 170c3a34c08SKyle Evans return (NULL); 17128f16a0fSKyle Evans } 17228f16a0fSKyle Evans 17328f16a0fSKyle Evans 17428f16a0fSKyle Evans /* 17528f16a0fSKyle Evans * Free memory allocated by libbe_init() 17628f16a0fSKyle Evans */ 17728f16a0fSKyle Evans void 17828f16a0fSKyle Evans libbe_close(libbe_handle_t *lbh) 17928f16a0fSKyle Evans { 180bfe0869cSKyle Evans 181c3a34c08SKyle Evans if (lbh->active_phandle != NULL) 182c3a34c08SKyle Evans zpool_close(lbh->active_phandle); 18328f16a0fSKyle Evans libzfs_fini(lbh->lzh); 18428f16a0fSKyle Evans free(lbh); 18528f16a0fSKyle Evans } 18628f16a0fSKyle Evans 1879b1662e6SKyle Evans /* 1889b1662e6SKyle Evans * Proxy through to libzfs for the moment. 1899b1662e6SKyle Evans */ 1909b1662e6SKyle Evans void 1919b1662e6SKyle Evans be_nicenum(uint64_t num, char *buf, size_t buflen) 1929b1662e6SKyle Evans { 1939b1662e6SKyle Evans 1949b1662e6SKyle Evans zfs_nicenum(num, buf, buflen); 1959b1662e6SKyle Evans } 19628f16a0fSKyle Evans 197920abf4dSKyle Evans static int 198920abf4dSKyle Evans be_destroy_cb(zfs_handle_t *zfs_hdl, void *data) 199920abf4dSKyle Evans { 200be7dd423SKyle Evans char path[BE_MAXPATHLEN]; 201be7dd423SKyle Evans struct be_destroy_data *bdd; 202be7dd423SKyle Evans zfs_handle_t *snap; 203920abf4dSKyle Evans int err; 204920abf4dSKyle Evans 205be7dd423SKyle Evans bdd = (struct be_destroy_data *)data; 206be7dd423SKyle Evans if (bdd->snapname == NULL) { 207be7dd423SKyle Evans err = zfs_iter_children(zfs_hdl, be_destroy_cb, data); 208be7dd423SKyle Evans if (err != 0) 209920abf4dSKyle Evans return (err); 210be7dd423SKyle Evans return (zfs_destroy(zfs_hdl, false)); 211be7dd423SKyle Evans } 212be7dd423SKyle Evans /* If we're dealing with snapshots instead, delete that one alone */ 213be7dd423SKyle Evans err = zfs_iter_filesystems(zfs_hdl, be_destroy_cb, data); 214be7dd423SKyle Evans if (err != 0) 215920abf4dSKyle Evans return (err); 216be7dd423SKyle Evans /* 217be7dd423SKyle Evans * This part is intentionally glossing over any potential errors, 218be7dd423SKyle Evans * because there's a lot less potential for errors when we're cleaning 219be7dd423SKyle Evans * up snapshots rather than a full deep BE. The primary error case 220be7dd423SKyle Evans * here being if the snapshot doesn't exist in the first place, which 221be7dd423SKyle Evans * the caller will likely deem insignificant as long as it doesn't 222be7dd423SKyle Evans * exist after the call. Thus, such a missing snapshot shouldn't jam 223be7dd423SKyle Evans * up the destruction. 224be7dd423SKyle Evans */ 225be7dd423SKyle Evans snprintf(path, sizeof(path), "%s@%s", zfs_get_name(zfs_hdl), 226be7dd423SKyle Evans bdd->snapname); 227be7dd423SKyle Evans if (!zfs_dataset_exists(bdd->lbh->lzh, path, ZFS_TYPE_SNAPSHOT)) 228be7dd423SKyle Evans return (0); 229be7dd423SKyle Evans snap = zfs_open(bdd->lbh->lzh, path, ZFS_TYPE_SNAPSHOT); 230be7dd423SKyle Evans if (snap != NULL) 231be7dd423SKyle Evans zfs_destroy(snap, false); 232920abf4dSKyle Evans return (0); 233920abf4dSKyle Evans } 234920abf4dSKyle Evans 2351dc85563SKyle Evans #define BE_DESTROY_WANTORIGIN (BE_DESTROY_ORIGIN | BE_DESTROY_AUTOORIGIN) 23628f16a0fSKyle Evans /* 23728f16a0fSKyle Evans * Destroy the boot environment or snapshot specified by the name 23828f16a0fSKyle Evans * parameter. Options are or'd together with the possible values: 23928f16a0fSKyle Evans * BE_DESTROY_FORCE : forces operation on mounted datasets 240be7dd423SKyle Evans * BE_DESTROY_ORIGIN: destroy the origin snapshot as well 24128f16a0fSKyle Evans */ 24228f16a0fSKyle Evans int 24373c3d608SKyle Evans be_destroy(libbe_handle_t *lbh, const char *name, int options) 24428f16a0fSKyle Evans { 245be7dd423SKyle Evans struct be_destroy_data bdd; 24613c62c50SKyle Evans char origin[BE_MAXPATHLEN], path[BE_MAXPATHLEN]; 24728f16a0fSKyle Evans zfs_handle_t *fs; 248be7dd423SKyle Evans char *snapdelim; 249bfe0869cSKyle Evans int err, force, mounted; 250be7dd423SKyle Evans size_t rootlen; 25128f16a0fSKyle Evans 252be7dd423SKyle Evans bdd.lbh = lbh; 253be7dd423SKyle Evans bdd.snapname = NULL; 254bfe0869cSKyle Evans force = options & BE_DESTROY_FORCE; 25513c62c50SKyle Evans *origin = '\0'; 25628f16a0fSKyle Evans 25728f16a0fSKyle Evans be_root_concat(lbh, name, path); 25828f16a0fSKyle Evans 259be7dd423SKyle Evans if ((snapdelim = strchr(path, '@')) == NULL) { 260bfe0869cSKyle Evans if (!zfs_dataset_exists(lbh->lzh, path, ZFS_TYPE_FILESYSTEM)) 26128f16a0fSKyle Evans return (set_error(lbh, BE_ERR_NOENT)); 26228f16a0fSKyle Evans 263f08dac4eSKyle Evans if (strcmp(path, lbh->rootfs) == 0 || 264f08dac4eSKyle Evans strcmp(path, lbh->bootfs) == 0) 26528f16a0fSKyle Evans return (set_error(lbh, BE_ERR_DESTROYACT)); 26628f16a0fSKyle Evans 267be7dd423SKyle Evans fs = zfs_open(lbh->lzh, path, ZFS_TYPE_FILESYSTEM); 26813c62c50SKyle Evans if (fs == NULL) 26913c62c50SKyle Evans return (set_error(lbh, BE_ERR_ZFSOPEN)); 270be7dd423SKyle Evans 2711dc85563SKyle Evans if ((options & BE_DESTROY_WANTORIGIN) != 0 && 27213c62c50SKyle Evans zfs_prop_get(fs, ZFS_PROP_ORIGIN, origin, sizeof(origin), 2731dc85563SKyle Evans NULL, NULL, 0, 1) != 0 && 2741dc85563SKyle Evans (options & BE_DESTROY_ORIGIN) != 0) 27513c62c50SKyle Evans return (set_error(lbh, BE_ERR_NOORIGIN)); 276e1ee6230SKyle Evans 277455d8009SKyle Evans /* 278455d8009SKyle Evans * If the caller wants auto-origin destruction and the origin 279455d8009SKyle Evans * name matches one of our automatically created snapshot names 280455d8009SKyle Evans * (i.e. strftime("%F-%T") with a serial at the end), then 281455d8009SKyle Evans * we'll set the DESTROY_ORIGIN flag and nuke it 282455d8009SKyle Evans * be_is_auto_snapshot_name is exported from libbe(3) so that 283455d8009SKyle Evans * the caller can determine if it needs to warn about the origin 284455d8009SKyle Evans * not being destroyed or not. 285455d8009SKyle Evans */ 2861dc85563SKyle Evans if ((options & BE_DESTROY_AUTOORIGIN) != 0 && *origin != '\0' && 287455d8009SKyle Evans be_is_auto_snapshot_name(lbh, origin)) 288455d8009SKyle Evans options |= BE_DESTROY_ORIGIN; 289455d8009SKyle Evans 290e1ee6230SKyle Evans /* Don't destroy a mounted dataset unless force is specified */ 291e1ee6230SKyle Evans if ((mounted = zfs_is_mounted(fs, NULL)) != 0) { 292e1ee6230SKyle Evans if (force) { 293e1ee6230SKyle Evans zfs_unmount(fs, NULL, 0); 294e1ee6230SKyle Evans } else { 295e1ee6230SKyle Evans free(bdd.snapname); 296e1ee6230SKyle Evans return (set_error(lbh, BE_ERR_DESTROYMNT)); 297e1ee6230SKyle Evans } 298e1ee6230SKyle Evans } 29928f16a0fSKyle Evans } else { 300bfe0869cSKyle Evans if (!zfs_dataset_exists(lbh->lzh, path, ZFS_TYPE_SNAPSHOT)) 30128f16a0fSKyle Evans return (set_error(lbh, BE_ERR_NOENT)); 30228f16a0fSKyle Evans 303be7dd423SKyle Evans bdd.snapname = strdup(snapdelim + 1); 304be7dd423SKyle Evans if (bdd.snapname == NULL) 305be7dd423SKyle Evans return (set_error(lbh, BE_ERR_NOMEM)); 306be7dd423SKyle Evans *snapdelim = '\0'; 307be7dd423SKyle Evans fs = zfs_open(lbh->lzh, path, ZFS_TYPE_DATASET); 308be7dd423SKyle Evans if (fs == NULL) { 309be7dd423SKyle Evans free(bdd.snapname); 31028f16a0fSKyle Evans return (set_error(lbh, BE_ERR_ZFSOPEN)); 31113c62c50SKyle Evans } 312be7dd423SKyle Evans } 31328f16a0fSKyle Evans 314be7dd423SKyle Evans err = be_destroy_cb(fs, &bdd); 315be7dd423SKyle Evans zfs_close(fs); 316be7dd423SKyle Evans free(bdd.snapname); 317be7dd423SKyle Evans if (err != 0) { 318920abf4dSKyle Evans /* Children are still present or the mount is referenced */ 319920abf4dSKyle Evans if (err == EBUSY) 320920abf4dSKyle Evans return (set_error(lbh, BE_ERR_DESTROYMNT)); 321920abf4dSKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 322920abf4dSKyle Evans } 32328f16a0fSKyle Evans 324be7dd423SKyle Evans if ((options & BE_DESTROY_ORIGIN) == 0) 325920abf4dSKyle Evans return (0); 32628f16a0fSKyle Evans 327be7dd423SKyle Evans /* The origin can't possibly be shorter than the BE root */ 328be7dd423SKyle Evans rootlen = strlen(lbh->root); 329be7dd423SKyle Evans if (*origin == '\0' || strlen(origin) <= rootlen + 1) 330be7dd423SKyle Evans return (set_error(lbh, BE_ERR_INVORIGIN)); 331be7dd423SKyle Evans 332be7dd423SKyle Evans /* 333be7dd423SKyle Evans * We'll be chopping off the BE root and running this back through 334be7dd423SKyle Evans * be_destroy, so that we properly handle the origin snapshot whether 335be7dd423SKyle Evans * it be that of a deep BE or not. 336be7dd423SKyle Evans */ 337be7dd423SKyle Evans if (strncmp(origin, lbh->root, rootlen) != 0 || origin[rootlen] != '/') 338be7dd423SKyle Evans return (0); 339be7dd423SKyle Evans 340be7dd423SKyle Evans return (be_destroy(lbh, origin + rootlen + 1, 341be7dd423SKyle Evans options & ~BE_DESTROY_ORIGIN)); 342be7dd423SKyle Evans } 34328f16a0fSKyle Evans 34490cf61e8SKyle Evans static void 34590cf61e8SKyle Evans be_setup_snapshot_name(libbe_handle_t *lbh, char *buf, size_t buflen) 34690cf61e8SKyle Evans { 34790cf61e8SKyle Evans time_t rawtime; 34890cf61e8SKyle Evans int len, serial; 34990cf61e8SKyle Evans 35090cf61e8SKyle Evans time(&rawtime); 35190cf61e8SKyle Evans len = strlen(buf); 35290cf61e8SKyle Evans len += strftime(buf + len, buflen - len, "@%F-%T", localtime(&rawtime)); 35390cf61e8SKyle Evans /* No room for serial... caller will do its best */ 35490cf61e8SKyle Evans if (buflen - len < 2) 35590cf61e8SKyle Evans return; 35690cf61e8SKyle Evans 35790cf61e8SKyle Evans for (serial = 0; serial < BE_SNAP_SERIAL_MAX; ++serial) { 35890cf61e8SKyle Evans snprintf(buf + len, buflen - len, "-%d", serial); 35990cf61e8SKyle Evans if (!zfs_dataset_exists(lbh->lzh, buf, ZFS_TYPE_SNAPSHOT)) 36090cf61e8SKyle Evans return; 36190cf61e8SKyle Evans } 36290cf61e8SKyle Evans } 36390cf61e8SKyle Evans 364455d8009SKyle Evans bool 365455d8009SKyle Evans be_is_auto_snapshot_name(libbe_handle_t *lbh, const char *name) 366455d8009SKyle Evans { 367455d8009SKyle Evans const char *snap; 368455d8009SKyle Evans int day, hour, minute, month, second, serial, year; 369455d8009SKyle Evans 370455d8009SKyle Evans if ((snap = strchr(name, '@')) == NULL) 371455d8009SKyle Evans return (false); 372455d8009SKyle Evans ++snap; 373455d8009SKyle Evans /* We'll grab the individual components and do some light validation. */ 374455d8009SKyle Evans if (sscanf(snap, "%d-%d-%d-%d:%d:%d-%d", &year, &month, &day, &hour, 375455d8009SKyle Evans &minute, &second, &serial) != 7) 376455d8009SKyle Evans return (false); 377455d8009SKyle Evans return (year >= 1970) && (month >= 1 && month <= 12) && 378455d8009SKyle Evans (day >= 1 && day <= 31) && (hour >= 0 && hour <= 23) && 379455d8009SKyle Evans (minute >= 0 && minute <= 59) && (second >= 0 && second <= 60) && 380455d8009SKyle Evans serial >= 0; 381455d8009SKyle Evans } 382455d8009SKyle Evans 38328f16a0fSKyle Evans int 384b29bf2f8SKyle Evans be_snapshot(libbe_handle_t *lbh, const char *source, const char *snap_name, 38528f16a0fSKyle Evans bool recursive, char *result) 38628f16a0fSKyle Evans { 38728f16a0fSKyle Evans char buf[BE_MAXPATHLEN]; 38890cf61e8SKyle Evans int err; 38928f16a0fSKyle Evans 39028f16a0fSKyle Evans be_root_concat(lbh, source, buf); 39128f16a0fSKyle Evans 392162ec569SKyle Evans if ((err = be_exists(lbh, buf)) != 0) 393162ec569SKyle Evans return (set_error(lbh, err)); 39428f16a0fSKyle Evans 39528f16a0fSKyle Evans if (snap_name != NULL) { 396a8e44f4dSKyle Evans if (strlcat(buf, "@", sizeof(buf)) >= sizeof(buf)) 397a8e44f4dSKyle Evans return (set_error(lbh, BE_ERR_INVALIDNAME)); 398a8e44f4dSKyle Evans 399a8e44f4dSKyle Evans if (strlcat(buf, snap_name, sizeof(buf)) >= sizeof(buf)) 400a8e44f4dSKyle Evans return (set_error(lbh, BE_ERR_INVALIDNAME)); 401a8e44f4dSKyle Evans 402bfe0869cSKyle Evans if (result != NULL) 40328f16a0fSKyle Evans snprintf(result, BE_MAXPATHLEN, "%s@%s", source, 40428f16a0fSKyle Evans snap_name); 40528f16a0fSKyle Evans } else { 40690cf61e8SKyle Evans be_setup_snapshot_name(lbh, buf, sizeof(buf)); 40790cf61e8SKyle Evans 408a8e44f4dSKyle Evans if (result != NULL && strlcpy(result, strrchr(buf, '/') + 1, 409a8e44f4dSKyle Evans sizeof(buf)) >= sizeof(buf)) 410a8e44f4dSKyle Evans return (set_error(lbh, BE_ERR_INVALIDNAME)); 41128f16a0fSKyle Evans } 412b29bf2f8SKyle Evans if ((err = zfs_snapshot(lbh->lzh, buf, recursive, NULL)) != 0) { 41328f16a0fSKyle Evans switch (err) { 41428f16a0fSKyle Evans case EZFS_INVALIDNAME: 41528f16a0fSKyle Evans return (set_error(lbh, BE_ERR_INVALIDNAME)); 41628f16a0fSKyle Evans 41728f16a0fSKyle Evans default: 4182989df09SKyle Evans /* 4192989df09SKyle Evans * The other errors that zfs_ioc_snapshot might return 4202989df09SKyle Evans * shouldn't happen if we've set things up properly, so 4212989df09SKyle Evans * we'll gloss over them and call it UNKNOWN as it will 4222989df09SKyle Evans * require further triage. 4232989df09SKyle Evans */ 4242989df09SKyle Evans if (errno == ENOTSUP) 4252989df09SKyle Evans return (set_error(lbh, BE_ERR_NOPOOL)); 42628f16a0fSKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 42728f16a0fSKyle Evans } 42828f16a0fSKyle Evans } 42928f16a0fSKyle Evans 43028f16a0fSKyle Evans return (BE_ERR_SUCCESS); 43128f16a0fSKyle Evans } 43228f16a0fSKyle Evans 43328f16a0fSKyle Evans 43428f16a0fSKyle Evans /* 43528f16a0fSKyle Evans * Create the boot environment specified by the name parameter 43628f16a0fSKyle Evans */ 43728f16a0fSKyle Evans int 43873c3d608SKyle Evans be_create(libbe_handle_t *lbh, const char *name) 43928f16a0fSKyle Evans { 44028f16a0fSKyle Evans int err; 44128f16a0fSKyle Evans 442b29bf2f8SKyle Evans err = be_create_from_existing(lbh, name, be_active_path(lbh)); 44328f16a0fSKyle Evans 44428f16a0fSKyle Evans return (set_error(lbh, err)); 44528f16a0fSKyle Evans } 44628f16a0fSKyle Evans 44728f16a0fSKyle Evans static int 44828f16a0fSKyle Evans be_deep_clone_prop(int prop, void *cb) 44928f16a0fSKyle Evans { 45028f16a0fSKyle Evans int err; 451bfe0869cSKyle Evans struct libbe_dccb *dccb; 45228f16a0fSKyle Evans zprop_source_t src; 45328f16a0fSKyle Evans char pval[BE_MAXPATHLEN]; 45428f16a0fSKyle Evans char source[BE_MAXPATHLEN]; 455af43c24dSKyle Evans char *val; 45628f16a0fSKyle Evans 457bfe0869cSKyle Evans dccb = cb; 45828f16a0fSKyle Evans /* Skip some properties we don't want to touch */ 45973c3d608SKyle Evans if (prop == ZFS_PROP_CANMOUNT) 46028f16a0fSKyle Evans return (ZPROP_CONT); 46128f16a0fSKyle Evans 46228f16a0fSKyle Evans /* Don't copy readonly properties */ 463bfe0869cSKyle Evans if (zfs_prop_readonly(prop)) 46428f16a0fSKyle Evans return (ZPROP_CONT); 46528f16a0fSKyle Evans 46628f16a0fSKyle Evans if ((err = zfs_prop_get(dccb->zhp, prop, (char *)&pval, 467bfe0869cSKyle Evans sizeof(pval), &src, (char *)&source, sizeof(source), false))) 46828f16a0fSKyle Evans /* Just continue if we fail to read a property */ 46928f16a0fSKyle Evans return (ZPROP_CONT); 470bfe0869cSKyle Evans 471be13d48cSKyle Evans /* 472be13d48cSKyle Evans * Only copy locally defined or received properties. This continues 473be13d48cSKyle Evans * to avoid temporary/default/local properties intentionally without 474be13d48cSKyle Evans * breaking received datasets. 475be13d48cSKyle Evans */ 476be13d48cSKyle Evans if (src != ZPROP_SRC_LOCAL && src != ZPROP_SRC_RECEIVED) 47728f16a0fSKyle Evans return (ZPROP_CONT); 47828f16a0fSKyle Evans 479af43c24dSKyle Evans /* Augment mountpoint with altroot, if needed */ 480af43c24dSKyle Evans val = pval; 481fc13fc1cSKyle Evans if (prop == ZFS_PROP_MOUNTPOINT) 482fc13fc1cSKyle Evans val = be_mountpoint_augmented(dccb->lbh, val); 483fc13fc1cSKyle Evans 484af43c24dSKyle Evans nvlist_add_string(dccb->props, zfs_prop_to_name(prop), val); 48528f16a0fSKyle Evans 48628f16a0fSKyle Evans return (ZPROP_CONT); 48728f16a0fSKyle Evans } 48828f16a0fSKyle Evans 489fa30d9edSKyle Evans /* 490fa30d9edSKyle Evans * Return the corresponding boot environment path for a given 491fa30d9edSKyle Evans * dataset path, the constructed path is placed in 'result'. 492fa30d9edSKyle Evans * 493fa30d9edSKyle Evans * example: say our new boot environment name is 'bootenv' and 494fa30d9edSKyle Evans * the dataset path is 'zroot/ROOT/default/data/set'. 495fa30d9edSKyle Evans * 496fa30d9edSKyle Evans * result should produce: 'zroot/ROOT/bootenv/data/set' 497fa30d9edSKyle Evans */ 49828f16a0fSKyle Evans static int 499fa30d9edSKyle Evans be_get_path(struct libbe_deep_clone *ldc, const char *dspath, char *result, int result_size) 500fa30d9edSKyle Evans { 501fa30d9edSKyle Evans char *pos; 502fa30d9edSKyle Evans char *child_dataset; 503fa30d9edSKyle Evans 504fa30d9edSKyle Evans /* match the root path for the boot environments */ 505fa30d9edSKyle Evans pos = strstr(dspath, ldc->lbh->root); 506fa30d9edSKyle Evans 507fa30d9edSKyle Evans /* no match, different pools? */ 508fa30d9edSKyle Evans if (pos == NULL) 509fa30d9edSKyle Evans return (BE_ERR_BADPATH); 510fa30d9edSKyle Evans 511fa30d9edSKyle Evans /* root path of the new boot environment */ 512fa30d9edSKyle Evans snprintf(result, result_size, "%s/%s", ldc->lbh->root, ldc->bename); 513fa30d9edSKyle Evans 514fa30d9edSKyle Evans /* gets us to the parent dataset, the +1 consumes a trailing slash */ 515fa30d9edSKyle Evans pos += strlen(ldc->lbh->root) + 1; 516fa30d9edSKyle Evans 517fa30d9edSKyle Evans /* skip the parent dataset */ 518fa30d9edSKyle Evans if ((child_dataset = strchr(pos, '/')) != NULL) 519fa30d9edSKyle Evans strlcat(result, child_dataset, result_size); 520fa30d9edSKyle Evans 521fa30d9edSKyle Evans return (BE_ERR_SUCCESS); 522fa30d9edSKyle Evans } 523fa30d9edSKyle Evans 524fa30d9edSKyle Evans static int 525fa30d9edSKyle Evans be_clone_cb(zfs_handle_t *ds, void *data) 52628f16a0fSKyle Evans { 52728f16a0fSKyle Evans int err; 52828f16a0fSKyle Evans char be_path[BE_MAXPATHLEN]; 52928f16a0fSKyle Evans char snap_path[BE_MAXPATHLEN]; 53028f16a0fSKyle Evans const char *dspath; 53128f16a0fSKyle Evans zfs_handle_t *snap_hdl; 53228f16a0fSKyle Evans nvlist_t *props; 533fa30d9edSKyle Evans struct libbe_deep_clone *ldc; 53428f16a0fSKyle Evans struct libbe_dccb dccb; 53528f16a0fSKyle Evans 536fa30d9edSKyle Evans ldc = (struct libbe_deep_clone *)data; 53728f16a0fSKyle Evans dspath = zfs_get_name(ds); 538bfe0869cSKyle Evans 539fa30d9edSKyle Evans snprintf(snap_path, sizeof(snap_path), "%s@%s", dspath, ldc->snapname); 540bfe0869cSKyle Evans 541fa30d9edSKyle Evans /* construct the boot environment path from the dataset we're cloning */ 542fa30d9edSKyle Evans if (be_get_path(ldc, dspath, be_path, sizeof(be_path)) != BE_ERR_SUCCESS) 543fa30d9edSKyle Evans return (set_error(ldc->lbh, BE_ERR_UNKNOWN)); 54428f16a0fSKyle Evans 545fa30d9edSKyle Evans /* the dataset to be created (i.e. the boot environment) already exists */ 546fa30d9edSKyle Evans if (zfs_dataset_exists(ldc->lbh->lzh, be_path, ZFS_TYPE_DATASET)) 547fa30d9edSKyle Evans return (set_error(ldc->lbh, BE_ERR_EXISTS)); 548fa30d9edSKyle Evans 549fa30d9edSKyle Evans /* no snapshot found for this dataset, silently skip it */ 550fa30d9edSKyle Evans if (!zfs_dataset_exists(ldc->lbh->lzh, snap_path, ZFS_TYPE_SNAPSHOT)) 551fa30d9edSKyle Evans return (0); 55228f16a0fSKyle Evans 55328f16a0fSKyle Evans if ((snap_hdl = 554fa30d9edSKyle Evans zfs_open(ldc->lbh->lzh, snap_path, ZFS_TYPE_SNAPSHOT)) == NULL) 555fa30d9edSKyle Evans return (set_error(ldc->lbh, BE_ERR_ZFSOPEN)); 55628f16a0fSKyle Evans 55728f16a0fSKyle Evans nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP); 55828f16a0fSKyle Evans nvlist_add_string(props, "canmount", "noauto"); 55928f16a0fSKyle Evans 560fa30d9edSKyle Evans dccb.lbh = ldc->lbh; 56128f16a0fSKyle Evans dccb.zhp = ds; 56228f16a0fSKyle Evans dccb.props = props; 56328f16a0fSKyle Evans if (zprop_iter(be_deep_clone_prop, &dccb, B_FALSE, B_FALSE, 564bfe0869cSKyle Evans ZFS_TYPE_FILESYSTEM) == ZPROP_INVAL) 56528f16a0fSKyle Evans return (-1); 56628f16a0fSKyle Evans 567cc4deabcSKyle Evans if ((err = zfs_clone(snap_hdl, be_path, props)) != 0) 568fa30d9edSKyle Evans return (set_error(ldc->lbh, BE_ERR_ZFSCLONE)); 56928f16a0fSKyle Evans 57028f16a0fSKyle Evans nvlist_free(props); 57128f16a0fSKyle Evans zfs_close(snap_hdl); 57228f16a0fSKyle Evans 573fa30d9edSKyle Evans if (ldc->depth_limit == -1 || ldc->depth < ldc->depth_limit) { 574fa30d9edSKyle Evans ldc->depth++; 575fa30d9edSKyle Evans err = zfs_iter_filesystems(ds, be_clone_cb, ldc); 576fa30d9edSKyle Evans ldc->depth--; 577fa30d9edSKyle Evans } 578cc4deabcSKyle Evans 579fa30d9edSKyle Evans return (set_error(ldc->lbh, err)); 58028f16a0fSKyle Evans } 58128f16a0fSKyle Evans 58228f16a0fSKyle Evans /* 583fa30d9edSKyle Evans * Create a boot environment with a given name from a given snapshot. 584fa30d9edSKyle Evans * Snapshots can be in the format 'zroot/ROOT/default@snapshot' or 585fa30d9edSKyle Evans * 'default@snapshot'. In the latter case, 'default@snapshot' will be prepended 586fa30d9edSKyle Evans * with the root path that libbe was initailized with. 58728f16a0fSKyle Evans */ 588fa30d9edSKyle Evans static int 589fa30d9edSKyle Evans be_clone(libbe_handle_t *lbh, const char *bename, const char *snapshot, int depth) 59028f16a0fSKyle Evans { 59128f16a0fSKyle Evans int err; 59228f16a0fSKyle Evans char snap_path[BE_MAXPATHLEN]; 593b29bf2f8SKyle Evans char *parentname, *snapname; 59428f16a0fSKyle Evans zfs_handle_t *parent_hdl; 595fa30d9edSKyle Evans struct libbe_deep_clone ldc; 59628f16a0fSKyle Evans 597fa30d9edSKyle Evans /* ensure the boot environment name is valid */ 598fa30d9edSKyle Evans if ((err = be_validate_name(lbh, bename)) != 0) 59928f16a0fSKyle Evans return (set_error(lbh, err)); 600fa30d9edSKyle Evans 601fa30d9edSKyle Evans /* 602fa30d9edSKyle Evans * prepend the boot environment root path if we're 603fa30d9edSKyle Evans * given a partial snapshot name. 604fa30d9edSKyle Evans */ 605fa30d9edSKyle Evans if ((err = be_root_concat(lbh, snapshot, snap_path)) != 0) 60628f16a0fSKyle Evans return (set_error(lbh, err)); 607fa30d9edSKyle Evans 608fa30d9edSKyle Evans /* ensure the snapshot exists */ 609b29bf2f8SKyle Evans if ((err = be_validate_snap(lbh, snap_path)) != 0) 61028f16a0fSKyle Evans return (set_error(lbh, err)); 61128f16a0fSKyle Evans 612fa30d9edSKyle Evans /* get a copy of the snapshot path so we can disect it */ 613cc4deabcSKyle Evans if ((parentname = strdup(snap_path)) == NULL) 614cc4deabcSKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 615cc4deabcSKyle Evans 616fa30d9edSKyle Evans /* split dataset name from snapshot name */ 61728f16a0fSKyle Evans snapname = strchr(parentname, '@'); 61828f16a0fSKyle Evans if (snapname == NULL) { 619cc4deabcSKyle Evans free(parentname); 620cc4deabcSKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 62128f16a0fSKyle Evans } 62228f16a0fSKyle Evans *snapname = '\0'; 62328f16a0fSKyle Evans snapname++; 62428f16a0fSKyle Evans 625fa30d9edSKyle Evans /* set-up the boot environment */ 626fa30d9edSKyle Evans ldc.lbh = lbh; 627fa30d9edSKyle Evans ldc.bename = bename; 628fa30d9edSKyle Evans ldc.snapname = snapname; 629fa30d9edSKyle Evans ldc.depth = 0; 630fa30d9edSKyle Evans ldc.depth_limit = depth; 63128f16a0fSKyle Evans 632fa30d9edSKyle Evans /* the boot environment will be cloned from this dataset */ 63328f16a0fSKyle Evans parent_hdl = zfs_open(lbh->lzh, parentname, ZFS_TYPE_DATASET); 634fa30d9edSKyle Evans 635fa30d9edSKyle Evans /* create the boot environment */ 636fa30d9edSKyle Evans err = be_clone_cb(parent_hdl, &ldc); 63728f16a0fSKyle Evans 638cc4deabcSKyle Evans free(parentname); 63928f16a0fSKyle Evans return (set_error(lbh, err)); 64028f16a0fSKyle Evans } 64128f16a0fSKyle Evans 642fa30d9edSKyle Evans /* 643fa30d9edSKyle Evans * Create a boot environment from pre-existing snapshot, specifying a depth. 644fa30d9edSKyle Evans */ 645fa30d9edSKyle Evans int be_create_depth(libbe_handle_t *lbh, const char *bename, 646fa30d9edSKyle Evans const char *snap, int depth) 647fa30d9edSKyle Evans { 648fa30d9edSKyle Evans return (be_clone(lbh, bename, snap, depth)); 649fa30d9edSKyle Evans } 650fa30d9edSKyle Evans 651fa30d9edSKyle Evans /* 652fa30d9edSKyle Evans * Create the boot environment from pre-existing snapshot 653fa30d9edSKyle Evans */ 654fa30d9edSKyle Evans int 655fa30d9edSKyle Evans be_create_from_existing_snap(libbe_handle_t *lbh, const char *bename, 656fa30d9edSKyle Evans const char *snap) 657fa30d9edSKyle Evans { 658fa30d9edSKyle Evans return (be_clone(lbh, bename, snap, -1)); 659fa30d9edSKyle Evans } 660fa30d9edSKyle Evans 66128f16a0fSKyle Evans 66228f16a0fSKyle Evans /* 66328f16a0fSKyle Evans * Create a boot environment from an existing boot environment 66428f16a0fSKyle Evans */ 66528f16a0fSKyle Evans int 666fa30d9edSKyle Evans be_create_from_existing(libbe_handle_t *lbh, const char *bename, const char *old) 66728f16a0fSKyle Evans { 66828f16a0fSKyle Evans int err; 669fa30d9edSKyle Evans char snap[BE_MAXPATHLEN]; 67028f16a0fSKyle Evans 671fa30d9edSKyle Evans if ((err = be_snapshot(lbh, old, NULL, true, snap)) != 0) 67228f16a0fSKyle Evans return (set_error(lbh, err)); 67328f16a0fSKyle Evans 674fa30d9edSKyle Evans err = be_clone(lbh, bename, snap, -1); 67528f16a0fSKyle Evans 67628f16a0fSKyle Evans return (set_error(lbh, err)); 67728f16a0fSKyle Evans } 67828f16a0fSKyle Evans 67928f16a0fSKyle Evans 68028f16a0fSKyle Evans /* 68128f16a0fSKyle Evans * Verifies that a snapshot has a valid name, exists, and has a mountpoint of 68228f16a0fSKyle Evans * '/'. Returns BE_ERR_SUCCESS (0), upon success, or the relevant BE_ERR_* upon 68328f16a0fSKyle Evans * failure. Does not set the internal library error state. 68428f16a0fSKyle Evans */ 68528f16a0fSKyle Evans int 686b29bf2f8SKyle Evans be_validate_snap(libbe_handle_t *lbh, const char *snap_name) 68728f16a0fSKyle Evans { 68828f16a0fSKyle Evans 689bfe0869cSKyle Evans if (strlen(snap_name) >= BE_MAXPATHLEN) 69028f16a0fSKyle Evans return (BE_ERR_PATHLEN); 69128f16a0fSKyle Evans 692fcb47c42SKyle Evans if (!zfs_name_valid(snap_name, ZFS_TYPE_SNAPSHOT)) 693fcb47c42SKyle Evans return (BE_ERR_INVALIDNAME); 694fcb47c42SKyle Evans 69528f16a0fSKyle Evans if (!zfs_dataset_exists(lbh->lzh, snap_name, 696bfe0869cSKyle Evans ZFS_TYPE_SNAPSHOT)) 69728f16a0fSKyle Evans return (BE_ERR_NOENT); 69828f16a0fSKyle Evans 69951aecc89SKyle Evans return (BE_ERR_SUCCESS); 70028f16a0fSKyle Evans } 70128f16a0fSKyle Evans 70228f16a0fSKyle Evans 70328f16a0fSKyle Evans /* 70428f16a0fSKyle Evans * Idempotently appends the name argument to the root boot environment path 70528f16a0fSKyle Evans * and copies the resulting string into the result buffer (which is assumed 70628f16a0fSKyle Evans * to be at least BE_MAXPATHLEN characters long. Returns BE_ERR_SUCCESS upon 70728f16a0fSKyle Evans * success, BE_ERR_PATHLEN if the resulting path is longer than BE_MAXPATHLEN, 70828f16a0fSKyle Evans * or BE_ERR_INVALIDNAME if the name is a path that does not begin with 70928f16a0fSKyle Evans * zfs_be_root. Does not set internal library error state. 71028f16a0fSKyle Evans */ 71128f16a0fSKyle Evans int 712b29bf2f8SKyle Evans be_root_concat(libbe_handle_t *lbh, const char *name, char *result) 71328f16a0fSKyle Evans { 71428f16a0fSKyle Evans size_t name_len, root_len; 71528f16a0fSKyle Evans 71628f16a0fSKyle Evans name_len = strlen(name); 71728f16a0fSKyle Evans root_len = strlen(lbh->root); 71828f16a0fSKyle Evans 71928f16a0fSKyle Evans /* Act idempotently; return be name if it is already a full path */ 72028f16a0fSKyle Evans if (strrchr(name, '/') != NULL) { 721bfe0869cSKyle Evans if (strstr(name, lbh->root) != name) 72228f16a0fSKyle Evans return (BE_ERR_INVALIDNAME); 72328f16a0fSKyle Evans 724bfe0869cSKyle Evans if (name_len >= BE_MAXPATHLEN) 72528f16a0fSKyle Evans return (BE_ERR_PATHLEN); 72628f16a0fSKyle Evans 72755b0e92bSKyle Evans strlcpy(result, name, BE_MAXPATHLEN); 72828f16a0fSKyle Evans return (BE_ERR_SUCCESS); 72928f16a0fSKyle Evans } else if (name_len + root_len + 1 < BE_MAXPATHLEN) { 73028f16a0fSKyle Evans snprintf(result, BE_MAXPATHLEN, "%s/%s", lbh->root, 73128f16a0fSKyle Evans name); 73228f16a0fSKyle Evans return (BE_ERR_SUCCESS); 73328f16a0fSKyle Evans } 73428f16a0fSKyle Evans 73528f16a0fSKyle Evans return (BE_ERR_PATHLEN); 73628f16a0fSKyle Evans } 73728f16a0fSKyle Evans 73828f16a0fSKyle Evans 73928f16a0fSKyle Evans /* 74028f16a0fSKyle Evans * Verifies the validity of a boot environment name (A-Za-z0-9-_.). Returns 7415b7803a9SKyle Evans * BE_ERR_SUCCESS (0) if name is valid, otherwise returns BE_ERR_INVALIDNAME 7425b7803a9SKyle Evans * or BE_ERR_PATHLEN. 74328f16a0fSKyle Evans * Does not set internal library error state. 74428f16a0fSKyle Evans */ 74528f16a0fSKyle Evans int 7465b7803a9SKyle Evans be_validate_name(libbe_handle_t *lbh, const char *name) 74728f16a0fSKyle Evans { 74828f16a0fSKyle Evans 7495b7803a9SKyle Evans /* 7505b7803a9SKyle Evans * Impose the additional restriction that the entire dataset name must 7515b7803a9SKyle Evans * not exceed the maximum length of a dataset, i.e. MAXNAMELEN. 7525b7803a9SKyle Evans */ 7535b7803a9SKyle Evans if (strlen(lbh->root) + 1 + strlen(name) > MAXNAMELEN) 7545b7803a9SKyle Evans return (BE_ERR_PATHLEN); 755fcb47c42SKyle Evans 756fcb47c42SKyle Evans if (!zfs_name_valid(name, ZFS_TYPE_DATASET)) 757fcb47c42SKyle Evans return (BE_ERR_INVALIDNAME); 758fcb47c42SKyle Evans 75928f16a0fSKyle Evans return (BE_ERR_SUCCESS); 76028f16a0fSKyle Evans } 76128f16a0fSKyle Evans 76228f16a0fSKyle Evans 76328f16a0fSKyle Evans /* 76428f16a0fSKyle Evans * usage 76528f16a0fSKyle Evans */ 76628f16a0fSKyle Evans int 76773c3d608SKyle Evans be_rename(libbe_handle_t *lbh, const char *old, const char *new) 76828f16a0fSKyle Evans { 76928f16a0fSKyle Evans char full_old[BE_MAXPATHLEN]; 77028f16a0fSKyle Evans char full_new[BE_MAXPATHLEN]; 77128f16a0fSKyle Evans zfs_handle_t *zfs_hdl; 77228f16a0fSKyle Evans int err; 77328f16a0fSKyle Evans 7745b7803a9SKyle Evans /* 7755b7803a9SKyle Evans * be_validate_name is documented not to set error state, so we should 7765b7803a9SKyle Evans * do so here. 7775b7803a9SKyle Evans */ 7785b7803a9SKyle Evans if ((err = be_validate_name(lbh, new)) != 0) 7795b7803a9SKyle Evans return (set_error(lbh, err)); 780b29bf2f8SKyle Evans if ((err = be_root_concat(lbh, old, full_old)) != 0) 78128f16a0fSKyle Evans return (set_error(lbh, err)); 782b29bf2f8SKyle Evans if ((err = be_root_concat(lbh, new, full_new)) != 0) 78328f16a0fSKyle Evans return (set_error(lbh, err)); 78428f16a0fSKyle Evans 785bfe0869cSKyle Evans if (!zfs_dataset_exists(lbh->lzh, full_old, ZFS_TYPE_DATASET)) 7862989df09SKyle Evans return (set_error(lbh, BE_ERR_NOENT)); 78728f16a0fSKyle Evans 788bfe0869cSKyle Evans if (zfs_dataset_exists(lbh->lzh, full_new, ZFS_TYPE_DATASET)) 7892989df09SKyle Evans return (set_error(lbh, BE_ERR_EXISTS)); 79028f16a0fSKyle Evans 79128f16a0fSKyle Evans if ((zfs_hdl = zfs_open(lbh->lzh, full_old, 792bfe0869cSKyle Evans ZFS_TYPE_FILESYSTEM)) == NULL) 7932989df09SKyle Evans return (set_error(lbh, BE_ERR_ZFSOPEN)); 79428f16a0fSKyle Evans 795bfe0869cSKyle Evans /* recurse, nounmount, forceunmount */ 7968369ba42SKyle Evans struct renameflags flags = { 7978369ba42SKyle Evans .nounmount = 1, 7988369ba42SKyle Evans }; 79928f16a0fSKyle Evans 80028f16a0fSKyle Evans err = zfs_rename(zfs_hdl, NULL, full_new, flags); 80128f16a0fSKyle Evans 80228f16a0fSKyle Evans zfs_close(zfs_hdl); 8035b7803a9SKyle Evans if (err != 0) 8045b7803a9SKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 8055b7803a9SKyle Evans return (0); 80628f16a0fSKyle Evans } 80728f16a0fSKyle Evans 80828f16a0fSKyle Evans 80928f16a0fSKyle Evans int 81073c3d608SKyle Evans be_export(libbe_handle_t *lbh, const char *bootenv, int fd) 81128f16a0fSKyle Evans { 81228f16a0fSKyle Evans char snap_name[BE_MAXPATHLEN]; 81328f16a0fSKyle Evans char buf[BE_MAXPATHLEN]; 81428f16a0fSKyle Evans zfs_handle_t *zfs; 8158569a95eSAndriy Gapon sendflags_t flags = { 0 }; 81628f16a0fSKyle Evans int err; 81728f16a0fSKyle Evans 818b29bf2f8SKyle Evans if ((err = be_snapshot(lbh, bootenv, NULL, true, snap_name)) != 0) 8196d4b1d24SKyle Evans /* Use the error set by be_snapshot */ 8206d4b1d24SKyle Evans return (err); 82128f16a0fSKyle Evans 82228f16a0fSKyle Evans be_root_concat(lbh, snap_name, buf); 82328f16a0fSKyle Evans 824bfe0869cSKyle Evans if ((zfs = zfs_open(lbh->lzh, buf, ZFS_TYPE_DATASET)) == NULL) 825506f5fdfSKyle Evans return (set_error(lbh, BE_ERR_ZFSOPEN)); 82628f16a0fSKyle Evans 8278569a95eSAndriy Gapon err = zfs_send_one(zfs, NULL, fd, flags); 8286d4b1d24SKyle Evans zfs_close(zfs); 8296d4b1d24SKyle Evans 83028f16a0fSKyle Evans return (err); 83128f16a0fSKyle Evans } 83228f16a0fSKyle Evans 83328f16a0fSKyle Evans 83428f16a0fSKyle Evans int 83573c3d608SKyle Evans be_import(libbe_handle_t *lbh, const char *bootenv, int fd) 83628f16a0fSKyle Evans { 83728f16a0fSKyle Evans char buf[BE_MAXPATHLEN]; 83828f16a0fSKyle Evans nvlist_t *props; 83928f16a0fSKyle Evans zfs_handle_t *zfs; 84016ac0705SKyle Evans recvflags_t flags = { .nomount = 1 }; 84116ac0705SKyle Evans int err; 84228f16a0fSKyle Evans 84316ac0705SKyle Evans be_root_concat(lbh, bootenv, buf); 84428f16a0fSKyle Evans 84516ac0705SKyle Evans if ((err = zfs_receive(lbh->lzh, buf, NULL, &flags, fd, NULL)) != 0) { 846506f5fdfSKyle Evans switch (err) { 847506f5fdfSKyle Evans case EINVAL: 848506f5fdfSKyle Evans return (set_error(lbh, BE_ERR_NOORIGIN)); 849506f5fdfSKyle Evans case ENOENT: 850506f5fdfSKyle Evans return (set_error(lbh, BE_ERR_NOENT)); 851506f5fdfSKyle Evans case EIO: 852506f5fdfSKyle Evans return (set_error(lbh, BE_ERR_IO)); 853506f5fdfSKyle Evans default: 854506f5fdfSKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 855506f5fdfSKyle Evans } 85628f16a0fSKyle Evans } 85728f16a0fSKyle Evans 85816ac0705SKyle Evans if ((zfs = zfs_open(lbh->lzh, buf, ZFS_TYPE_FILESYSTEM)) == NULL) 859506f5fdfSKyle Evans return (set_error(lbh, BE_ERR_ZFSOPEN)); 86028f16a0fSKyle Evans 86128f16a0fSKyle Evans nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP); 86228f16a0fSKyle Evans nvlist_add_string(props, "canmount", "noauto"); 863ac34fe23SKyle Evans nvlist_add_string(props, "mountpoint", "none"); 86428f16a0fSKyle Evans 86516ac0705SKyle Evans err = zfs_prop_set_list(zfs, props); 86628f16a0fSKyle Evans nvlist_free(props); 86728f16a0fSKyle Evans 8681b057aacSKyle Evans zfs_close(zfs); 8691b057aacSKyle Evans 8701b057aacSKyle Evans if (err != 0) 8711b057aacSKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 8721b057aacSKyle Evans 87316ac0705SKyle Evans return (0); 87428f16a0fSKyle Evans } 87528f16a0fSKyle Evans 8763d1a1f2cSKyle Evans #if SOON 877c65a2111SKyle Evans static int 878c65a2111SKyle Evans be_create_child_noent(libbe_handle_t *lbh, const char *active, 879c65a2111SKyle Evans const char *child_path) 880c65a2111SKyle Evans { 881c65a2111SKyle Evans nvlist_t *props; 882c65a2111SKyle Evans zfs_handle_t *zfs; 883c65a2111SKyle Evans int err; 884c65a2111SKyle Evans 885c65a2111SKyle Evans nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP); 886c65a2111SKyle Evans nvlist_add_string(props, "canmount", "noauto"); 887c65a2111SKyle Evans nvlist_add_string(props, "mountpoint", child_path); 888c65a2111SKyle Evans 889c65a2111SKyle Evans /* Create */ 890c65a2111SKyle Evans if ((err = zfs_create(lbh->lzh, active, ZFS_TYPE_DATASET, 891c65a2111SKyle Evans props)) != 0) { 892c65a2111SKyle Evans switch (err) { 893c65a2111SKyle Evans case EZFS_EXISTS: 894c65a2111SKyle Evans return (set_error(lbh, BE_ERR_EXISTS)); 895c65a2111SKyle Evans case EZFS_NOENT: 896c65a2111SKyle Evans return (set_error(lbh, BE_ERR_NOENT)); 897c65a2111SKyle Evans case EZFS_BADTYPE: 898c65a2111SKyle Evans case EZFS_BADVERSION: 899c65a2111SKyle Evans return (set_error(lbh, BE_ERR_NOPOOL)); 900c65a2111SKyle Evans case EZFS_BADPROP: 901c65a2111SKyle Evans default: 902c65a2111SKyle Evans /* We set something up wrong, probably... */ 903c65a2111SKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 904c65a2111SKyle Evans } 905c65a2111SKyle Evans } 906c65a2111SKyle Evans nvlist_free(props); 907c65a2111SKyle Evans 908c65a2111SKyle Evans if ((zfs = zfs_open(lbh->lzh, active, ZFS_TYPE_DATASET)) == NULL) 909c65a2111SKyle Evans return (set_error(lbh, BE_ERR_ZFSOPEN)); 910c65a2111SKyle Evans 911c65a2111SKyle Evans /* Set props */ 912c65a2111SKyle Evans if ((err = zfs_prop_set(zfs, "canmount", "noauto")) != 0) { 913c65a2111SKyle Evans zfs_close(zfs); 914c65a2111SKyle Evans /* 915c65a2111SKyle Evans * Similar to other cases, this shouldn't fail unless we've 916c65a2111SKyle Evans * done something wrong. This is a new dataset that shouldn't 917c65a2111SKyle Evans * have been mounted anywhere between creation and now. 918c65a2111SKyle Evans */ 919c65a2111SKyle Evans if (err == EZFS_NOMEM) 920c65a2111SKyle Evans return (set_error(lbh, BE_ERR_NOMEM)); 921c65a2111SKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 922c65a2111SKyle Evans } 923c65a2111SKyle Evans zfs_close(zfs); 924c65a2111SKyle Evans return (BE_ERR_SUCCESS); 925c65a2111SKyle Evans } 926c65a2111SKyle Evans 927c65a2111SKyle Evans static int 928c65a2111SKyle Evans be_create_child_cloned(libbe_handle_t *lbh, const char *active) 929c65a2111SKyle Evans { 9303d1a1f2cSKyle Evans char buf[BE_MAXPATHLEN], tmp[BE_MAXPATHLEN];; 931c65a2111SKyle Evans zfs_handle_t *zfs; 932c65a2111SKyle Evans int err; 933c65a2111SKyle Evans 934c65a2111SKyle Evans /* XXX TODO ? */ 935c65a2111SKyle Evans 936c65a2111SKyle Evans /* 937c65a2111SKyle Evans * Establish if the existing path is a zfs dataset or just 938c65a2111SKyle Evans * the subdirectory of one 939c65a2111SKyle Evans */ 9403d1a1f2cSKyle Evans strlcpy(tmp, "tmp/be_snap.XXXXX", sizeof(tmp)); 9413d1a1f2cSKyle Evans if (mktemp(tmp) == NULL) 942c65a2111SKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 943c65a2111SKyle Evans 9443d1a1f2cSKyle Evans be_root_concat(lbh, tmp, buf); 9453d1a1f2cSKyle Evans printf("Here %s?\n", buf); 946c65a2111SKyle Evans if ((err = zfs_snapshot(lbh->lzh, buf, false, NULL)) != 0) { 947c65a2111SKyle Evans switch (err) { 948c65a2111SKyle Evans case EZFS_INVALIDNAME: 949c65a2111SKyle Evans return (set_error(lbh, BE_ERR_INVALIDNAME)); 950c65a2111SKyle Evans 951c65a2111SKyle Evans default: 952c65a2111SKyle Evans /* 953c65a2111SKyle Evans * The other errors that zfs_ioc_snapshot might return 954c65a2111SKyle Evans * shouldn't happen if we've set things up properly, so 955c65a2111SKyle Evans * we'll gloss over them and call it UNKNOWN as it will 956c65a2111SKyle Evans * require further triage. 957c65a2111SKyle Evans */ 958c65a2111SKyle Evans if (errno == ENOTSUP) 959c65a2111SKyle Evans return (set_error(lbh, BE_ERR_NOPOOL)); 960c65a2111SKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 961c65a2111SKyle Evans } 962c65a2111SKyle Evans } 963c65a2111SKyle Evans 964c65a2111SKyle Evans /* Clone */ 965c65a2111SKyle Evans if ((zfs = zfs_open(lbh->lzh, buf, ZFS_TYPE_SNAPSHOT)) == NULL) 966c65a2111SKyle Evans return (BE_ERR_ZFSOPEN); 967c65a2111SKyle Evans 968c65a2111SKyle Evans if ((err = zfs_clone(zfs, active, NULL)) != 0) 969c65a2111SKyle Evans /* XXX TODO correct error */ 970c65a2111SKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 971c65a2111SKyle Evans 972c65a2111SKyle Evans /* set props */ 973c65a2111SKyle Evans zfs_close(zfs); 974c65a2111SKyle Evans return (BE_ERR_SUCCESS); 975c65a2111SKyle Evans } 97628f16a0fSKyle Evans 97728f16a0fSKyle Evans int 97873c3d608SKyle Evans be_add_child(libbe_handle_t *lbh, const char *child_path, bool cp_if_exists) 97928f16a0fSKyle Evans { 98073c3d608SKyle Evans struct stat sb; 981c65a2111SKyle Evans char active[BE_MAXPATHLEN], buf[BE_MAXPATHLEN]; 98228f16a0fSKyle Evans nvlist_t *props; 98373c3d608SKyle Evans const char *s; 98428f16a0fSKyle Evans 98528f16a0fSKyle Evans /* Require absolute paths */ 986bfe0869cSKyle Evans if (*child_path != '/') 9876d4b1d24SKyle Evans return (set_error(lbh, BE_ERR_BADPATH)); 98828f16a0fSKyle Evans 9896d4b1d24SKyle Evans strlcpy(active, be_active_path(lbh), BE_MAXPATHLEN); 99028f16a0fSKyle Evans strcpy(buf, active); 99128f16a0fSKyle Evans 99228f16a0fSKyle Evans /* Create non-mountable parent dataset(s) */ 99373c3d608SKyle Evans s = child_path; 99428f16a0fSKyle Evans for (char *p; (p = strchr(s+1, '/')) != NULL; s = p) { 99528f16a0fSKyle Evans size_t len = p - s; 99628f16a0fSKyle Evans strncat(buf, s, len); 99728f16a0fSKyle Evans 99828f16a0fSKyle Evans nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP); 99928f16a0fSKyle Evans nvlist_add_string(props, "canmount", "off"); 100028f16a0fSKyle Evans nvlist_add_string(props, "mountpoint", "none"); 100128f16a0fSKyle Evans zfs_create(lbh->lzh, buf, ZFS_TYPE_DATASET, props); 100228f16a0fSKyle Evans nvlist_free(props); 100328f16a0fSKyle Evans } 100428f16a0fSKyle Evans 100528f16a0fSKyle Evans /* Path does not exist as a descendent of / yet */ 10066d4b1d24SKyle Evans if (strlcat(active, child_path, BE_MAXPATHLEN) >= BE_MAXPATHLEN) 10076d4b1d24SKyle Evans return (set_error(lbh, BE_ERR_PATHLEN)); 100828f16a0fSKyle Evans 100928f16a0fSKyle Evans if (stat(child_path, &sb) != 0) { 101028f16a0fSKyle Evans /* Verify that error is ENOENT */ 10116d4b1d24SKyle Evans if (errno != ENOENT) 10126d4b1d24SKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 1013c65a2111SKyle Evans return (be_create_child_noent(lbh, active, child_path)); 1014c65a2111SKyle Evans } else if (cp_if_exists) 101528f16a0fSKyle Evans /* Path is already a descendent of / and should be copied */ 1016c65a2111SKyle Evans return (be_create_child_cloned(lbh, active)); 10176d4b1d24SKyle Evans return (set_error(lbh, BE_ERR_EXISTS)); 101828f16a0fSKyle Evans } 10193d1a1f2cSKyle Evans #endif /* SOON */ 102028f16a0fSKyle Evans 1021d06f7103SKyle Evans static int 1022d06f7103SKyle Evans be_set_nextboot(libbe_handle_t *lbh, nvlist_t *config, uint64_t pool_guid, 1023d06f7103SKyle Evans const char *zfsdev) 1024d06f7103SKyle Evans { 1025d06f7103SKyle Evans nvlist_t **child; 1026d06f7103SKyle Evans uint64_t vdev_guid; 1027d06f7103SKyle Evans int c, children; 1028d06f7103SKyle Evans 1029d06f7103SKyle Evans if (nvlist_lookup_nvlist_array(config, ZPOOL_CONFIG_CHILDREN, &child, 1030d06f7103SKyle Evans &children) == 0) { 1031d06f7103SKyle Evans for (c = 0; c < children; ++c) 1032d06f7103SKyle Evans if (be_set_nextboot(lbh, child[c], pool_guid, zfsdev) != 0) 1033d06f7103SKyle Evans return (1); 1034d06f7103SKyle Evans return (0); 1035d06f7103SKyle Evans } 1036d06f7103SKyle Evans 1037d06f7103SKyle Evans if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_GUID, 1038d06f7103SKyle Evans &vdev_guid) != 0) { 1039d06f7103SKyle Evans return (1); 1040d06f7103SKyle Evans } 1041d06f7103SKyle Evans 1042d06f7103SKyle Evans if (zpool_nextboot(lbh->lzh, pool_guid, vdev_guid, zfsdev) != 0) { 1043d06f7103SKyle Evans perror("ZFS_IOC_NEXTBOOT failed"); 1044d06f7103SKyle Evans return (1); 1045d06f7103SKyle Evans } 1046d06f7103SKyle Evans 1047d06f7103SKyle Evans return (0); 1048d06f7103SKyle Evans } 1049d06f7103SKyle Evans 10508d4ce358SKyle Evans /* 10518d4ce358SKyle Evans * Deactivate old BE dataset; currently just sets canmount=noauto 10528d4ce358SKyle Evans */ 10538d4ce358SKyle Evans static int 10548d4ce358SKyle Evans be_deactivate(libbe_handle_t *lbh, const char *ds) 10558d4ce358SKyle Evans { 10568d4ce358SKyle Evans zfs_handle_t *zfs; 10578d4ce358SKyle Evans 10588d4ce358SKyle Evans if ((zfs = zfs_open(lbh->lzh, ds, ZFS_TYPE_DATASET)) == NULL) 10598d4ce358SKyle Evans return (1); 10608d4ce358SKyle Evans if (zfs_prop_set(zfs, "canmount", "noauto") != 0) 10618d4ce358SKyle Evans return (1); 10628d4ce358SKyle Evans zfs_close(zfs); 10638d4ce358SKyle Evans return (0); 10648d4ce358SKyle Evans } 106528f16a0fSKyle Evans 106628f16a0fSKyle Evans int 106773c3d608SKyle Evans be_activate(libbe_handle_t *lbh, const char *bootenv, bool temporary) 106828f16a0fSKyle Evans { 106928f16a0fSKyle Evans char be_path[BE_MAXPATHLEN]; 107028f16a0fSKyle Evans char buf[BE_MAXPATHLEN]; 10714635676dSKyle Evans nvlist_t *config, *dsprops, *vdevs; 10724635676dSKyle Evans char *origin; 10730cadc427SKyle Evans uint64_t pool_guid; 10740cadc427SKyle Evans zfs_handle_t *zhp; 107528f16a0fSKyle Evans int err; 107628f16a0fSKyle Evans 107728f16a0fSKyle Evans be_root_concat(lbh, bootenv, be_path); 107828f16a0fSKyle Evans 107928f16a0fSKyle Evans /* Note: be_exists fails if mountpoint is not / */ 1080162ec569SKyle Evans if ((err = be_exists(lbh, be_path)) != 0) 1081162ec569SKyle Evans return (set_error(lbh, err)); 108228f16a0fSKyle Evans 108328f16a0fSKyle Evans if (temporary) { 1084d06f7103SKyle Evans config = zpool_get_config(lbh->active_phandle, NULL); 1085c65a2111SKyle Evans if (config == NULL) 1086c65a2111SKyle Evans /* config should be fetchable... */ 1087c65a2111SKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 108828f16a0fSKyle Evans 1089d06f7103SKyle Evans if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, 1090d06f7103SKyle Evans &pool_guid) != 0) 1091c65a2111SKyle Evans /* Similarly, it shouldn't be possible */ 1092c65a2111SKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 1093d06f7103SKyle Evans 109428f16a0fSKyle Evans /* Expected format according to zfsbootcfg(8) man */ 1095a8e44f4dSKyle Evans snprintf(buf, sizeof(buf), "zfs:%s:", be_path); 109628f16a0fSKyle Evans 1097c65a2111SKyle Evans /* We have no config tree */ 1098c65a2111SKyle Evans if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, 1099c65a2111SKyle Evans &vdevs) != 0) 1100c65a2111SKyle Evans return (set_error(lbh, BE_ERR_NOPOOL)); 110128f16a0fSKyle Evans 1102d06f7103SKyle Evans return (be_set_nextboot(lbh, vdevs, pool_guid, buf)); 110328f16a0fSKyle Evans } else { 11048d4ce358SKyle Evans if (be_deactivate(lbh, lbh->bootfs) != 0) 11058d4ce358SKyle Evans return (-1); 11068d4ce358SKyle Evans 110728f16a0fSKyle Evans /* Obtain bootenv zpool */ 1108c3a34c08SKyle Evans err = zpool_set_prop(lbh->active_phandle, "bootfs", be_path); 11090cadc427SKyle Evans if (err) 11100cadc427SKyle Evans return (-1); 111128f16a0fSKyle Evans 11120cadc427SKyle Evans zhp = zfs_open(lbh->lzh, be_path, ZFS_TYPE_FILESYSTEM); 11130cadc427SKyle Evans if (zhp == NULL) 11140cadc427SKyle Evans return (-1); 111528f16a0fSKyle Evans 11164635676dSKyle Evans if (be_prop_list_alloc(&dsprops) != 0) 11174635676dSKyle Evans return (-1); 11184635676dSKyle Evans 11194635676dSKyle Evans if (be_get_dataset_props(lbh, be_path, dsprops) != 0) { 11204635676dSKyle Evans nvlist_free(dsprops); 11214635676dSKyle Evans return (-1); 11224635676dSKyle Evans } 11234635676dSKyle Evans 11244635676dSKyle Evans if (nvlist_lookup_string(dsprops, "origin", &origin) == 0) 11250cadc427SKyle Evans err = zfs_promote(zhp); 11264635676dSKyle Evans nvlist_free(dsprops); 11274635676dSKyle Evans 11280cadc427SKyle Evans zfs_close(zhp); 11290cadc427SKyle Evans 11300cadc427SKyle Evans if (err) 113128f16a0fSKyle Evans return (-1); 113228f16a0fSKyle Evans } 11330cadc427SKyle Evans 11340cadc427SKyle Evans return (BE_ERR_SUCCESS); 113528f16a0fSKyle Evans } 1136