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 * 628f16a0fSKyle Evans * Redistribution and use in source and binary forms, with or without 728f16a0fSKyle Evans * modification, are permitted provided that the following conditions 828f16a0fSKyle Evans * are met: 928f16a0fSKyle Evans * 1. Redistributions of source code must retain the above copyright 1028f16a0fSKyle Evans * notice, this list of conditions and the following disclaimer. 1128f16a0fSKyle Evans * 2. Redistributions in binary form must reproduce the above copyright 1228f16a0fSKyle Evans * notice, this list of conditions and the following disclaimer in the 1328f16a0fSKyle Evans * documentation and/or other materials provided with the distribution. 1428f16a0fSKyle Evans * 1528f16a0fSKyle Evans * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 1628f16a0fSKyle Evans * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1728f16a0fSKyle Evans * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1828f16a0fSKyle Evans * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 1928f16a0fSKyle Evans * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2028f16a0fSKyle Evans * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2128f16a0fSKyle Evans * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2228f16a0fSKyle Evans * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2328f16a0fSKyle Evans * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2428f16a0fSKyle Evans * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2528f16a0fSKyle Evans * SUCH DAMAGE. 2628f16a0fSKyle Evans */ 2728f16a0fSKyle Evans 28b6e7c421SKyle Evans #include <sys/cdefs.h> 29b6e7c421SKyle Evans __FBSDID("$FreeBSD$"); 30b6e7c421SKyle Evans 3151aecc89SKyle Evans #include <sys/param.h> 3251aecc89SKyle Evans #include <sys/mount.h> 3328f16a0fSKyle Evans #include <sys/stat.h> 3451aecc89SKyle Evans #include <sys/ucred.h> 358f5c6c31SKyle Evans #include <sys/queue.h> 36485172f5SKyle Evans #include <sys/zfs_context.h> 37485172f5SKyle Evans #include <sys/mntent.h> 389e5787d2SMatt Macy #include <sys/zfs_ioctl.h> 39485172f5SKyle Evans 409e5787d2SMatt Macy #include <libzutil.h> 4128f16a0fSKyle Evans #include <ctype.h> 4228f16a0fSKyle Evans #include <libgen.h> 4328f16a0fSKyle Evans #include <libzfs_core.h> 449e5787d2SMatt Macy #include <libzfs_impl.h> 4528f16a0fSKyle Evans #include <stdio.h> 4628f16a0fSKyle Evans #include <stdlib.h> 4728f16a0fSKyle Evans #include <time.h> 4828f16a0fSKyle Evans #include <unistd.h> 49e307eb94SToomas Soome #include <libzfsbootenv.h> 5028f16a0fSKyle Evans 5128f16a0fSKyle Evans #include "be.h" 5228f16a0fSKyle Evans #include "be_impl.h" 5328f16a0fSKyle Evans 548f5c6c31SKyle Evans struct promote_entry { 558f5c6c31SKyle Evans char name[BE_MAXPATHLEN]; 568f5c6c31SKyle Evans SLIST_ENTRY(promote_entry) link; 578f5c6c31SKyle Evans }; 588f5c6c31SKyle Evans 59be7dd423SKyle Evans struct be_destroy_data { 60be7dd423SKyle Evans libbe_handle_t *lbh; 618f5c6c31SKyle Evans char target_name[BE_MAXPATHLEN]; 62be7dd423SKyle Evans char *snapname; 638f5c6c31SKyle Evans SLIST_HEAD(, promote_entry) promotelist; 64be7dd423SKyle Evans }; 65be7dd423SKyle Evans 663d1a1f2cSKyle Evans #if SOON 67c65a2111SKyle Evans static int be_create_child_noent(libbe_handle_t *lbh, const char *active, 68c65a2111SKyle Evans const char *child_path); 69c65a2111SKyle Evans static int be_create_child_cloned(libbe_handle_t *lbh, const char *active); 703d1a1f2cSKyle Evans #endif 71c65a2111SKyle Evans 7290cf61e8SKyle Evans /* Arbitrary... should tune */ 7390cf61e8SKyle Evans #define BE_SNAP_SERIAL_MAX 1024 7490cf61e8SKyle Evans 7528f16a0fSKyle Evans /* 76ee16b7c9SKyle Evans * Iterator function for locating the rootfs amongst the children of the 77ee16b7c9SKyle Evans * zfs_be_root set by loader(8). data is expected to be a libbe_handle_t *. 78ee16b7c9SKyle Evans */ 79ee16b7c9SKyle Evans static int 8051aecc89SKyle Evans be_locate_rootfs(libbe_handle_t *lbh) 81ee16b7c9SKyle Evans { 824ab5187dSKyle Evans struct statfs sfs; 83485172f5SKyle Evans struct mnttab entry; 8451aecc89SKyle Evans zfs_handle_t *zfs; 85ee16b7c9SKyle Evans 864ab5187dSKyle Evans /* 874ab5187dSKyle Evans * Check first if root is ZFS; if not, we'll bail on rootfs capture. 884ab5187dSKyle Evans * Unfortunately needed because zfs_path_to_zhandle will emit to 894ab5187dSKyle Evans * stderr if / isn't actually a ZFS filesystem, which we'd like 904ab5187dSKyle Evans * to avoid. 914ab5187dSKyle Evans */ 924ab5187dSKyle Evans if (statfs("/", &sfs) == 0) { 934ab5187dSKyle Evans statfs2mnttab(&sfs, &entry); 944ab5187dSKyle Evans if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) 954ab5187dSKyle Evans return (1); 964ab5187dSKyle Evans } else 974ab5187dSKyle Evans return (1); 9851aecc89SKyle Evans zfs = zfs_path_to_zhandle(lbh->lzh, "/", ZFS_TYPE_FILESYSTEM); 9951aecc89SKyle Evans if (zfs == NULL) 100ee16b7c9SKyle Evans return (1); 101ee16b7c9SKyle Evans 10251aecc89SKyle Evans strlcpy(lbh->rootfs, zfs_get_name(zfs), sizeof(lbh->rootfs)); 10351aecc89SKyle Evans zfs_close(zfs); 104ee16b7c9SKyle Evans return (0); 105ee16b7c9SKyle Evans } 106ee16b7c9SKyle Evans 107ee16b7c9SKyle Evans /* 10828f16a0fSKyle Evans * Initializes the libbe context to operate in the root boot environment 10928f16a0fSKyle Evans * dataset, for example, zroot/ROOT. 11028f16a0fSKyle Evans */ 11128f16a0fSKyle Evans libbe_handle_t * 112cc624025SKyle Evans libbe_init(const char *root) 11328f16a0fSKyle Evans { 114fc13fc1cSKyle Evans char altroot[MAXPATHLEN]; 11528f16a0fSKyle Evans libbe_handle_t *lbh; 116c3a34c08SKyle Evans char *poolname, *pos; 117c3a34c08SKyle Evans int pnamelen; 11828f16a0fSKyle Evans 119c3a34c08SKyle Evans lbh = NULL; 120c3a34c08SKyle Evans poolname = pos = NULL; 12128f16a0fSKyle Evans 122c3a34c08SKyle Evans if ((lbh = calloc(1, sizeof(libbe_handle_t))) == NULL) 123c3a34c08SKyle Evans goto err; 12428f16a0fSKyle Evans 125c3a34c08SKyle Evans if ((lbh->lzh = libzfs_init()) == NULL) 126c3a34c08SKyle Evans goto err; 12728f16a0fSKyle Evans 128cc624025SKyle Evans /* 129cc624025SKyle Evans * Grab rootfs, we'll work backwards from there if an optional BE root 130cc624025SKyle Evans * has not been passed in. 131cc624025SKyle Evans */ 1324ab5187dSKyle Evans if (be_locate_rootfs(lbh) != 0) { 1334ab5187dSKyle Evans if (root == NULL) 134c3a34c08SKyle Evans goto err; 1354ab5187dSKyle Evans *lbh->rootfs = '\0'; 1364ab5187dSKyle Evans } 137cc624025SKyle Evans if (root == NULL) { 138cc624025SKyle Evans /* Strip off the final slash from rootfs to get the be root */ 13951aecc89SKyle Evans strlcpy(lbh->root, lbh->rootfs, sizeof(lbh->root)); 14051aecc89SKyle Evans pos = strrchr(lbh->root, '/'); 14151aecc89SKyle Evans if (pos == NULL) 14251aecc89SKyle Evans goto err; 14351aecc89SKyle Evans *pos = '\0'; 144cc624025SKyle Evans } else 145cc624025SKyle Evans strlcpy(lbh->root, root, sizeof(lbh->root)); 146c3a34c08SKyle Evans 147c3a34c08SKyle Evans if ((pos = strchr(lbh->root, '/')) == NULL) 148c3a34c08SKyle Evans goto err; 149c3a34c08SKyle Evans 150c3a34c08SKyle Evans pnamelen = pos - lbh->root; 151c3a34c08SKyle Evans poolname = malloc(pnamelen + 1); 152c3a34c08SKyle Evans if (poolname == NULL) 153c3a34c08SKyle Evans goto err; 154c3a34c08SKyle Evans 15555b0e92bSKyle Evans strlcpy(poolname, lbh->root, pnamelen + 1); 156c3a34c08SKyle Evans if ((lbh->active_phandle = zpool_open(lbh->lzh, poolname)) == NULL) 157c3a34c08SKyle Evans goto err; 158a8e44f4dSKyle Evans free(poolname); 159a8e44f4dSKyle Evans poolname = NULL; 160c3a34c08SKyle Evans 161c3a34c08SKyle Evans if (zpool_get_prop(lbh->active_phandle, ZPOOL_PROP_BOOTFS, lbh->bootfs, 16255b0e92bSKyle Evans sizeof(lbh->bootfs), NULL, true) != 0) 163c3a34c08SKyle Evans goto err; 164c3a34c08SKyle Evans 165fc13fc1cSKyle Evans if (zpool_get_prop(lbh->active_phandle, ZPOOL_PROP_ALTROOT, 166fc13fc1cSKyle Evans altroot, sizeof(altroot), NULL, true) == 0 && 167fc13fc1cSKyle Evans strcmp(altroot, "-") != 0) 168fc13fc1cSKyle Evans lbh->altroot_len = strlen(altroot); 169fc13fc1cSKyle Evans 17028f16a0fSKyle Evans return (lbh); 171c3a34c08SKyle Evans err: 172c3a34c08SKyle Evans if (lbh != NULL) { 173c3a34c08SKyle Evans if (lbh->active_phandle != NULL) 174c3a34c08SKyle Evans zpool_close(lbh->active_phandle); 175c3a34c08SKyle Evans if (lbh->lzh != NULL) 176c3a34c08SKyle Evans libzfs_fini(lbh->lzh); 177c3a34c08SKyle Evans free(lbh); 178c3a34c08SKyle Evans } 179c3a34c08SKyle Evans free(poolname); 180c3a34c08SKyle Evans return (NULL); 18128f16a0fSKyle Evans } 18228f16a0fSKyle Evans 18328f16a0fSKyle Evans 18428f16a0fSKyle Evans /* 18528f16a0fSKyle Evans * Free memory allocated by libbe_init() 18628f16a0fSKyle Evans */ 18728f16a0fSKyle Evans void 18828f16a0fSKyle Evans libbe_close(libbe_handle_t *lbh) 18928f16a0fSKyle Evans { 190bfe0869cSKyle Evans 191c3a34c08SKyle Evans if (lbh->active_phandle != NULL) 192c3a34c08SKyle Evans zpool_close(lbh->active_phandle); 19328f16a0fSKyle Evans libzfs_fini(lbh->lzh); 19428f16a0fSKyle Evans free(lbh); 19528f16a0fSKyle Evans } 19628f16a0fSKyle Evans 1979b1662e6SKyle Evans /* 1989b1662e6SKyle Evans * Proxy through to libzfs for the moment. 1999b1662e6SKyle Evans */ 2009b1662e6SKyle Evans void 2019b1662e6SKyle Evans be_nicenum(uint64_t num, char *buf, size_t buflen) 2029b1662e6SKyle Evans { 2039b1662e6SKyle Evans 2049b1662e6SKyle Evans zfs_nicenum(num, buf, buflen); 2059b1662e6SKyle Evans } 20628f16a0fSKyle Evans 2078f5c6c31SKyle Evans static bool 2088f5c6c31SKyle Evans be_should_promote_clones(zfs_handle_t *zfs_hdl, struct be_destroy_data *bdd) 2098f5c6c31SKyle Evans { 2108f5c6c31SKyle Evans char *atpos; 2118f5c6c31SKyle Evans 2128f5c6c31SKyle Evans if (zfs_get_type(zfs_hdl) != ZFS_TYPE_SNAPSHOT) 2138f5c6c31SKyle Evans return (false); 2148f5c6c31SKyle Evans 2158f5c6c31SKyle Evans /* 2168f5c6c31SKyle Evans * If we're deleting a snapshot, we need to make sure we only promote 2178f5c6c31SKyle Evans * clones that are derived from one of the snapshots we're deleting, 2188f5c6c31SKyle Evans * rather than that of a snapshot we're not touching. This keeps stuff 2198f5c6c31SKyle Evans * in a consistent state, making sure that we don't error out unless 2208f5c6c31SKyle Evans * we really need to. 2218f5c6c31SKyle Evans */ 2228f5c6c31SKyle Evans if (bdd->snapname == NULL) 2238f5c6c31SKyle Evans return (true); 2248f5c6c31SKyle Evans 2258f5c6c31SKyle Evans atpos = strchr(zfs_get_name(zfs_hdl), '@'); 2268f5c6c31SKyle Evans return (strcmp(atpos + 1, bdd->snapname) == 0); 2278f5c6c31SKyle Evans } 2288f5c6c31SKyle Evans 2298f5c6c31SKyle Evans /* 2308f5c6c31SKyle Evans * This is executed from be_promote_dependent_clones via zfs_iter_dependents, 2318f5c6c31SKyle Evans * It checks if the dependent type is a snapshot then attempts to find any 2328f5c6c31SKyle Evans * clones associated with it. Any clones not related to the destroy target are 2338f5c6c31SKyle Evans * added to the promote list. 2348f5c6c31SKyle Evans */ 2358f5c6c31SKyle Evans static int 2368f5c6c31SKyle Evans be_dependent_clone_cb(zfs_handle_t *zfs_hdl, void *data) 2378f5c6c31SKyle Evans { 2388f5c6c31SKyle Evans int err; 2398f5c6c31SKyle Evans bool found; 240*2a58b312SMartin Matuska const char *name; 2418f5c6c31SKyle Evans struct nvlist *nvl; 2428f5c6c31SKyle Evans struct nvpair *nvp; 2438f5c6c31SKyle Evans struct be_destroy_data *bdd; 2448f5c6c31SKyle Evans struct promote_entry *entry, *newentry; 2458f5c6c31SKyle Evans 2468f5c6c31SKyle Evans nvp = NULL; 2478f5c6c31SKyle Evans err = 0; 2488f5c6c31SKyle Evans bdd = (struct be_destroy_data *)data; 2498f5c6c31SKyle Evans 2508f5c6c31SKyle Evans if (be_should_promote_clones(zfs_hdl, bdd) && 2518f5c6c31SKyle Evans (nvl = zfs_get_clones_nvl(zfs_hdl)) != NULL) { 2528f5c6c31SKyle Evans while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { 2538f5c6c31SKyle Evans name = nvpair_name(nvp); 2548f5c6c31SKyle Evans 2558f5c6c31SKyle Evans /* 2568f5c6c31SKyle Evans * Skip if the clone is equal to, or a child of, the 2578f5c6c31SKyle Evans * destroy target. 2588f5c6c31SKyle Evans */ 2598f5c6c31SKyle Evans if (strncmp(name, bdd->target_name, 2608f5c6c31SKyle Evans strlen(bdd->target_name)) == 0 || 2618f5c6c31SKyle Evans strstr(name, bdd->target_name) == name) { 2628f5c6c31SKyle Evans continue; 2638f5c6c31SKyle Evans } 2648f5c6c31SKyle Evans 2658f5c6c31SKyle Evans found = false; 2668f5c6c31SKyle Evans SLIST_FOREACH(entry, &bdd->promotelist, link) { 2678f5c6c31SKyle Evans if (strcmp(entry->name, name) == 0) { 2688f5c6c31SKyle Evans found = true; 2698f5c6c31SKyle Evans break; 2708f5c6c31SKyle Evans } 2718f5c6c31SKyle Evans } 2728f5c6c31SKyle Evans 2738f5c6c31SKyle Evans if (found) 2748f5c6c31SKyle Evans continue; 2758f5c6c31SKyle Evans 2768f5c6c31SKyle Evans newentry = malloc(sizeof(struct promote_entry)); 2778f5c6c31SKyle Evans if (newentry == NULL) { 2788f5c6c31SKyle Evans err = ENOMEM; 2798f5c6c31SKyle Evans break; 2808f5c6c31SKyle Evans } 2818f5c6c31SKyle Evans 2828f5c6c31SKyle Evans #define BE_COPY_NAME(entry, src) \ 2838f5c6c31SKyle Evans strlcpy((entry)->name, (src), sizeof((entry)->name)) 2848f5c6c31SKyle Evans if (BE_COPY_NAME(newentry, name) >= 2858f5c6c31SKyle Evans sizeof(newentry->name)) { 2868f5c6c31SKyle Evans /* Shouldn't happen. */ 2878f5c6c31SKyle Evans free(newentry); 2888f5c6c31SKyle Evans err = ENAMETOOLONG; 2898f5c6c31SKyle Evans break; 2908f5c6c31SKyle Evans } 2918f5c6c31SKyle Evans #undef BE_COPY_NAME 2928f5c6c31SKyle Evans 2938f5c6c31SKyle Evans /* 2948f5c6c31SKyle Evans * We're building up a SLIST here to make sure both that 2958f5c6c31SKyle Evans * we get the order right and so that we don't 2968f5c6c31SKyle Evans * inadvertently observe the wrong state by promoting 2978f5c6c31SKyle Evans * datasets while we're still walking the tree. The 2988f5c6c31SKyle Evans * latter can lead to situations where we promote a BE 2998f5c6c31SKyle Evans * then effectively demote it again. 3008f5c6c31SKyle Evans */ 3018f5c6c31SKyle Evans SLIST_INSERT_HEAD(&bdd->promotelist, newentry, link); 3028f5c6c31SKyle Evans } 3038f5c6c31SKyle Evans nvlist_free(nvl); 3048f5c6c31SKyle Evans } 3058f5c6c31SKyle Evans zfs_close(zfs_hdl); 3068f5c6c31SKyle Evans return (err); 3078f5c6c31SKyle Evans } 3088f5c6c31SKyle Evans 3098f5c6c31SKyle Evans /* 3108f5c6c31SKyle Evans * This is called before a destroy, so that any datasets(environments) that are 3118f5c6c31SKyle Evans * dependent on this one get promoted before destroying the target. 3128f5c6c31SKyle Evans */ 3138f5c6c31SKyle Evans static int 3148f5c6c31SKyle Evans be_promote_dependent_clones(zfs_handle_t *zfs_hdl, struct be_destroy_data *bdd) 3158f5c6c31SKyle Evans { 3168f5c6c31SKyle Evans int err; 3178f5c6c31SKyle Evans zfs_handle_t *clone; 3188f5c6c31SKyle Evans struct promote_entry *entry; 3198f5c6c31SKyle Evans 3208f5c6c31SKyle Evans snprintf(bdd->target_name, BE_MAXPATHLEN, "%s/", zfs_get_name(zfs_hdl)); 32115f0b8c3SMartin Matuska err = zfs_iter_dependents(zfs_hdl, 0, true, be_dependent_clone_cb, bdd); 3228f5c6c31SKyle Evans 3238f5c6c31SKyle Evans /* 3248f5c6c31SKyle Evans * Drain the list and walk away from it if we're only deleting a 3258f5c6c31SKyle Evans * snapshot. 3268f5c6c31SKyle Evans */ 3278f5c6c31SKyle Evans if (bdd->snapname != NULL && !SLIST_EMPTY(&bdd->promotelist)) 3288f5c6c31SKyle Evans err = BE_ERR_HASCLONES; 3298f5c6c31SKyle Evans while (!SLIST_EMPTY(&bdd->promotelist)) { 3308f5c6c31SKyle Evans entry = SLIST_FIRST(&bdd->promotelist); 3318f5c6c31SKyle Evans SLIST_REMOVE_HEAD(&bdd->promotelist, link); 3328f5c6c31SKyle Evans 3338f5c6c31SKyle Evans #define ZFS_GRAB_CLONE() \ 3348f5c6c31SKyle Evans zfs_open(bdd->lbh->lzh, entry->name, ZFS_TYPE_FILESYSTEM) 3358f5c6c31SKyle Evans /* 3368f5c6c31SKyle Evans * Just skip this part on error, we still want to clean up the 3378f5c6c31SKyle Evans * promotion list after the first error. We'll then preserve it 3388f5c6c31SKyle Evans * all the way back. 3398f5c6c31SKyle Evans */ 3408f5c6c31SKyle Evans if (err == 0 && (clone = ZFS_GRAB_CLONE()) != NULL) { 3418f5c6c31SKyle Evans err = zfs_promote(clone); 3428f5c6c31SKyle Evans if (err != 0) 3438f5c6c31SKyle Evans err = BE_ERR_DESTROYMNT; 3448f5c6c31SKyle Evans zfs_close(clone); 3458f5c6c31SKyle Evans } 3468f5c6c31SKyle Evans #undef ZFS_GRAB_CLONE 3478f5c6c31SKyle Evans free(entry); 3488f5c6c31SKyle Evans } 3498f5c6c31SKyle Evans 3508f5c6c31SKyle Evans return (err); 3518f5c6c31SKyle Evans } 3528f5c6c31SKyle Evans 353920abf4dSKyle Evans static int 354920abf4dSKyle Evans be_destroy_cb(zfs_handle_t *zfs_hdl, void *data) 355920abf4dSKyle Evans { 356be7dd423SKyle Evans char path[BE_MAXPATHLEN]; 357be7dd423SKyle Evans struct be_destroy_data *bdd; 358be7dd423SKyle Evans zfs_handle_t *snap; 359920abf4dSKyle Evans int err; 360920abf4dSKyle Evans 361be7dd423SKyle Evans bdd = (struct be_destroy_data *)data; 362be7dd423SKyle Evans if (bdd->snapname == NULL) { 36315f0b8c3SMartin Matuska err = zfs_iter_children(zfs_hdl, 0, be_destroy_cb, data); 364be7dd423SKyle Evans if (err != 0) 365920abf4dSKyle Evans return (err); 366be7dd423SKyle Evans return (zfs_destroy(zfs_hdl, false)); 367be7dd423SKyle Evans } 368be7dd423SKyle Evans /* If we're dealing with snapshots instead, delete that one alone */ 36915f0b8c3SMartin Matuska err = zfs_iter_filesystems(zfs_hdl, 0, be_destroy_cb, data); 370be7dd423SKyle Evans if (err != 0) 371920abf4dSKyle Evans return (err); 372be7dd423SKyle Evans /* 373be7dd423SKyle Evans * This part is intentionally glossing over any potential errors, 374be7dd423SKyle Evans * because there's a lot less potential for errors when we're cleaning 375be7dd423SKyle Evans * up snapshots rather than a full deep BE. The primary error case 376be7dd423SKyle Evans * here being if the snapshot doesn't exist in the first place, which 377be7dd423SKyle Evans * the caller will likely deem insignificant as long as it doesn't 378be7dd423SKyle Evans * exist after the call. Thus, such a missing snapshot shouldn't jam 379be7dd423SKyle Evans * up the destruction. 380be7dd423SKyle Evans */ 381be7dd423SKyle Evans snprintf(path, sizeof(path), "%s@%s", zfs_get_name(zfs_hdl), 382be7dd423SKyle Evans bdd->snapname); 383be7dd423SKyle Evans if (!zfs_dataset_exists(bdd->lbh->lzh, path, ZFS_TYPE_SNAPSHOT)) 384be7dd423SKyle Evans return (0); 385be7dd423SKyle Evans snap = zfs_open(bdd->lbh->lzh, path, ZFS_TYPE_SNAPSHOT); 386be7dd423SKyle Evans if (snap != NULL) 387be7dd423SKyle Evans zfs_destroy(snap, false); 388920abf4dSKyle Evans return (0); 389920abf4dSKyle Evans } 390920abf4dSKyle Evans 3911dc85563SKyle Evans #define BE_DESTROY_WANTORIGIN (BE_DESTROY_ORIGIN | BE_DESTROY_AUTOORIGIN) 39228f16a0fSKyle Evans /* 39328f16a0fSKyle Evans * Destroy the boot environment or snapshot specified by the name 39428f16a0fSKyle Evans * parameter. Options are or'd together with the possible values: 39528f16a0fSKyle Evans * BE_DESTROY_FORCE : forces operation on mounted datasets 396be7dd423SKyle Evans * BE_DESTROY_ORIGIN: destroy the origin snapshot as well 39728f16a0fSKyle Evans */ 3988f5c6c31SKyle Evans static int 3998f5c6c31SKyle Evans be_destroy_internal(libbe_handle_t *lbh, const char *name, int options, 4008f5c6c31SKyle Evans bool odestroyer) 40128f16a0fSKyle Evans { 402be7dd423SKyle Evans struct be_destroy_data bdd; 40313c62c50SKyle Evans char origin[BE_MAXPATHLEN], path[BE_MAXPATHLEN]; 40428f16a0fSKyle Evans zfs_handle_t *fs; 405be7dd423SKyle Evans char *snapdelim; 406bfe0869cSKyle Evans int err, force, mounted; 407be7dd423SKyle Evans size_t rootlen; 40828f16a0fSKyle Evans 409be7dd423SKyle Evans bdd.lbh = lbh; 410be7dd423SKyle Evans bdd.snapname = NULL; 4118f5c6c31SKyle Evans SLIST_INIT(&bdd.promotelist); 412bfe0869cSKyle Evans force = options & BE_DESTROY_FORCE; 41313c62c50SKyle Evans *origin = '\0'; 41428f16a0fSKyle Evans 41528f16a0fSKyle Evans be_root_concat(lbh, name, path); 41628f16a0fSKyle Evans 417be7dd423SKyle Evans if ((snapdelim = strchr(path, '@')) == NULL) { 418bfe0869cSKyle Evans if (!zfs_dataset_exists(lbh->lzh, path, ZFS_TYPE_FILESYSTEM)) 41928f16a0fSKyle Evans return (set_error(lbh, BE_ERR_NOENT)); 42028f16a0fSKyle Evans 421f08dac4eSKyle Evans if (strcmp(path, lbh->rootfs) == 0 || 422f08dac4eSKyle Evans strcmp(path, lbh->bootfs) == 0) 42328f16a0fSKyle Evans return (set_error(lbh, BE_ERR_DESTROYACT)); 42428f16a0fSKyle Evans 425be7dd423SKyle Evans fs = zfs_open(lbh->lzh, path, ZFS_TYPE_FILESYSTEM); 42613c62c50SKyle Evans if (fs == NULL) 42713c62c50SKyle Evans return (set_error(lbh, BE_ERR_ZFSOPEN)); 428be7dd423SKyle Evans 4298f5c6c31SKyle Evans /* Don't destroy a mounted dataset unless force is specified */ 4308f5c6c31SKyle Evans if ((mounted = zfs_is_mounted(fs, NULL)) != 0) { 4318f5c6c31SKyle Evans if (force) { 4328f5c6c31SKyle Evans zfs_unmount(fs, NULL, 0); 4338f5c6c31SKyle Evans } else { 4348f5c6c31SKyle Evans free(bdd.snapname); 4358f5c6c31SKyle Evans return (set_error(lbh, BE_ERR_DESTROYMNT)); 4368f5c6c31SKyle Evans } 4378f5c6c31SKyle Evans } 4388f5c6c31SKyle Evans } else { 4398f5c6c31SKyle Evans /* 4408f5c6c31SKyle Evans * If we're initially destroying a snapshot, origin options do 4418f5c6c31SKyle Evans * not make sense. If we're destroying the origin snapshot of 4428f5c6c31SKyle Evans * a BE, we want to maintain the options in case we need to 4438f5c6c31SKyle Evans * fake success after failing to promote. 4448f5c6c31SKyle Evans */ 4458f5c6c31SKyle Evans if (!odestroyer) 4468f5c6c31SKyle Evans options &= ~BE_DESTROY_WANTORIGIN; 4478f5c6c31SKyle Evans if (!zfs_dataset_exists(lbh->lzh, path, ZFS_TYPE_SNAPSHOT)) 4488f5c6c31SKyle Evans return (set_error(lbh, BE_ERR_NOENT)); 4498f5c6c31SKyle Evans 4508f5c6c31SKyle Evans bdd.snapname = strdup(snapdelim + 1); 4518f5c6c31SKyle Evans if (bdd.snapname == NULL) 4528f5c6c31SKyle Evans return (set_error(lbh, BE_ERR_NOMEM)); 4538f5c6c31SKyle Evans *snapdelim = '\0'; 4548f5c6c31SKyle Evans fs = zfs_open(lbh->lzh, path, ZFS_TYPE_DATASET); 4558f5c6c31SKyle Evans if (fs == NULL) { 4568f5c6c31SKyle Evans free(bdd.snapname); 4578f5c6c31SKyle Evans return (set_error(lbh, BE_ERR_ZFSOPEN)); 4588f5c6c31SKyle Evans } 4598f5c6c31SKyle Evans } 4608f5c6c31SKyle Evans 4618f5c6c31SKyle Evans /* 4628f5c6c31SKyle Evans * Whether we're destroying a BE or a single snapshot, we need to walk 4638f5c6c31SKyle Evans * the tree of what we're going to destroy and promote everything in our 4648f5c6c31SKyle Evans * path so that we can make it happen. 4658f5c6c31SKyle Evans */ 4668f5c6c31SKyle Evans if ((err = be_promote_dependent_clones(fs, &bdd)) != 0) { 4678f5c6c31SKyle Evans free(bdd.snapname); 4688f5c6c31SKyle Evans 4698f5c6c31SKyle Evans /* 4708f5c6c31SKyle Evans * If we're just destroying the origin of some other dataset 4718f5c6c31SKyle Evans * we were invoked to destroy, then we just ignore 4728f5c6c31SKyle Evans * BE_ERR_HASCLONES and return success unless the caller wanted 4738f5c6c31SKyle Evans * to force the issue. 4748f5c6c31SKyle Evans */ 4758f5c6c31SKyle Evans if (odestroyer && err == BE_ERR_HASCLONES && 4768f5c6c31SKyle Evans (options & BE_DESTROY_AUTOORIGIN) != 0) 4778f5c6c31SKyle Evans return (0); 4788f5c6c31SKyle Evans return (set_error(lbh, err)); 4798f5c6c31SKyle Evans } 4808f5c6c31SKyle Evans 4818f5c6c31SKyle Evans /* 4828f5c6c31SKyle Evans * This was deferred until after we promote all of the derivatives so 4838f5c6c31SKyle Evans * that we grab the new origin after everything's settled down. 4848f5c6c31SKyle Evans */ 4851dc85563SKyle Evans if ((options & BE_DESTROY_WANTORIGIN) != 0 && 48613c62c50SKyle Evans zfs_prop_get(fs, ZFS_PROP_ORIGIN, origin, sizeof(origin), 4871dc85563SKyle Evans NULL, NULL, 0, 1) != 0 && 4881dc85563SKyle Evans (options & BE_DESTROY_ORIGIN) != 0) 48913c62c50SKyle Evans return (set_error(lbh, BE_ERR_NOORIGIN)); 490e1ee6230SKyle Evans 491455d8009SKyle Evans /* 492455d8009SKyle Evans * If the caller wants auto-origin destruction and the origin 493455d8009SKyle Evans * name matches one of our automatically created snapshot names 494455d8009SKyle Evans * (i.e. strftime("%F-%T") with a serial at the end), then 495455d8009SKyle Evans * we'll set the DESTROY_ORIGIN flag and nuke it 496455d8009SKyle Evans * be_is_auto_snapshot_name is exported from libbe(3) so that 497455d8009SKyle Evans * the caller can determine if it needs to warn about the origin 498455d8009SKyle Evans * not being destroyed or not. 499455d8009SKyle Evans */ 5001dc85563SKyle Evans if ((options & BE_DESTROY_AUTOORIGIN) != 0 && *origin != '\0' && 501455d8009SKyle Evans be_is_auto_snapshot_name(lbh, origin)) 502455d8009SKyle Evans options |= BE_DESTROY_ORIGIN; 503455d8009SKyle Evans 504be7dd423SKyle Evans err = be_destroy_cb(fs, &bdd); 505be7dd423SKyle Evans zfs_close(fs); 506be7dd423SKyle Evans free(bdd.snapname); 507be7dd423SKyle Evans if (err != 0) { 508920abf4dSKyle Evans /* Children are still present or the mount is referenced */ 509920abf4dSKyle Evans if (err == EBUSY) 510920abf4dSKyle Evans return (set_error(lbh, BE_ERR_DESTROYMNT)); 511920abf4dSKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 512920abf4dSKyle Evans } 51328f16a0fSKyle Evans 514be7dd423SKyle Evans if ((options & BE_DESTROY_ORIGIN) == 0) 515920abf4dSKyle Evans return (0); 51628f16a0fSKyle Evans 517be7dd423SKyle Evans /* The origin can't possibly be shorter than the BE root */ 518be7dd423SKyle Evans rootlen = strlen(lbh->root); 519be7dd423SKyle Evans if (*origin == '\0' || strlen(origin) <= rootlen + 1) 520be7dd423SKyle Evans return (set_error(lbh, BE_ERR_INVORIGIN)); 521be7dd423SKyle Evans 522be7dd423SKyle Evans /* 523be7dd423SKyle Evans * We'll be chopping off the BE root and running this back through 524be7dd423SKyle Evans * be_destroy, so that we properly handle the origin snapshot whether 525be7dd423SKyle Evans * it be that of a deep BE or not. 526be7dd423SKyle Evans */ 527be7dd423SKyle Evans if (strncmp(origin, lbh->root, rootlen) != 0 || origin[rootlen] != '/') 528be7dd423SKyle Evans return (0); 529be7dd423SKyle Evans 5308f5c6c31SKyle Evans return (be_destroy_internal(lbh, origin + rootlen + 1, 5318f5c6c31SKyle Evans options & ~BE_DESTROY_ORIGIN, true)); 5328f5c6c31SKyle Evans } 5338f5c6c31SKyle Evans 5348f5c6c31SKyle Evans int 5358f5c6c31SKyle Evans be_destroy(libbe_handle_t *lbh, const char *name, int options) 5368f5c6c31SKyle Evans { 5378f5c6c31SKyle Evans 5388f5c6c31SKyle Evans /* 5398f5c6c31SKyle Evans * The consumer must not set both BE_DESTROY_AUTOORIGIN and 5408f5c6c31SKyle Evans * BE_DESTROY_ORIGIN. Internally, we'll set the latter from the former. 5418f5c6c31SKyle Evans * The latter should imply that we must succeed at destroying the 5428f5c6c31SKyle Evans * origin, or complain otherwise. 5438f5c6c31SKyle Evans */ 5448f5c6c31SKyle Evans if ((options & BE_DESTROY_WANTORIGIN) == BE_DESTROY_WANTORIGIN) 5458f5c6c31SKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 5468f5c6c31SKyle Evans return (be_destroy_internal(lbh, name, options, false)); 547be7dd423SKyle Evans } 54828f16a0fSKyle Evans 54990cf61e8SKyle Evans static void 55090cf61e8SKyle Evans be_setup_snapshot_name(libbe_handle_t *lbh, char *buf, size_t buflen) 55190cf61e8SKyle Evans { 55290cf61e8SKyle Evans time_t rawtime; 55390cf61e8SKyle Evans int len, serial; 55490cf61e8SKyle Evans 55590cf61e8SKyle Evans time(&rawtime); 55690cf61e8SKyle Evans len = strlen(buf); 55790cf61e8SKyle Evans len += strftime(buf + len, buflen - len, "@%F-%T", localtime(&rawtime)); 55890cf61e8SKyle Evans /* No room for serial... caller will do its best */ 55990cf61e8SKyle Evans if (buflen - len < 2) 56090cf61e8SKyle Evans return; 56190cf61e8SKyle Evans 56290cf61e8SKyle Evans for (serial = 0; serial < BE_SNAP_SERIAL_MAX; ++serial) { 56390cf61e8SKyle Evans snprintf(buf + len, buflen - len, "-%d", serial); 56490cf61e8SKyle Evans if (!zfs_dataset_exists(lbh->lzh, buf, ZFS_TYPE_SNAPSHOT)) 56590cf61e8SKyle Evans return; 56690cf61e8SKyle Evans } 56790cf61e8SKyle Evans } 56890cf61e8SKyle Evans 569455d8009SKyle Evans bool 5709d6d8bf8SKyle Evans be_is_auto_snapshot_name(libbe_handle_t *lbh __unused, const char *name) 571455d8009SKyle Evans { 572455d8009SKyle Evans const char *snap; 573455d8009SKyle Evans int day, hour, minute, month, second, serial, year; 574455d8009SKyle Evans 575455d8009SKyle Evans if ((snap = strchr(name, '@')) == NULL) 576455d8009SKyle Evans return (false); 577455d8009SKyle Evans ++snap; 578455d8009SKyle Evans /* We'll grab the individual components and do some light validation. */ 579455d8009SKyle Evans if (sscanf(snap, "%d-%d-%d-%d:%d:%d-%d", &year, &month, &day, &hour, 580455d8009SKyle Evans &minute, &second, &serial) != 7) 581455d8009SKyle Evans return (false); 582455d8009SKyle Evans return (year >= 1970) && (month >= 1 && month <= 12) && 583455d8009SKyle Evans (day >= 1 && day <= 31) && (hour >= 0 && hour <= 23) && 584455d8009SKyle Evans (minute >= 0 && minute <= 59) && (second >= 0 && second <= 60) && 585455d8009SKyle Evans serial >= 0; 586455d8009SKyle Evans } 587455d8009SKyle Evans 58828f16a0fSKyle Evans int 589b29bf2f8SKyle Evans be_snapshot(libbe_handle_t *lbh, const char *source, const char *snap_name, 59028f16a0fSKyle Evans bool recursive, char *result) 59128f16a0fSKyle Evans { 59228f16a0fSKyle Evans char buf[BE_MAXPATHLEN]; 59390cf61e8SKyle Evans int err; 59428f16a0fSKyle Evans 59528f16a0fSKyle Evans be_root_concat(lbh, source, buf); 59628f16a0fSKyle Evans 597162ec569SKyle Evans if ((err = be_exists(lbh, buf)) != 0) 598162ec569SKyle Evans return (set_error(lbh, err)); 59928f16a0fSKyle Evans 60028f16a0fSKyle Evans if (snap_name != NULL) { 601a8e44f4dSKyle Evans if (strlcat(buf, "@", sizeof(buf)) >= sizeof(buf)) 602a8e44f4dSKyle Evans return (set_error(lbh, BE_ERR_INVALIDNAME)); 603a8e44f4dSKyle Evans 604a8e44f4dSKyle Evans if (strlcat(buf, snap_name, sizeof(buf)) >= sizeof(buf)) 605a8e44f4dSKyle Evans return (set_error(lbh, BE_ERR_INVALIDNAME)); 606a8e44f4dSKyle Evans 607bfe0869cSKyle Evans if (result != NULL) 60828f16a0fSKyle Evans snprintf(result, BE_MAXPATHLEN, "%s@%s", source, 60928f16a0fSKyle Evans snap_name); 61028f16a0fSKyle Evans } else { 61190cf61e8SKyle Evans be_setup_snapshot_name(lbh, buf, sizeof(buf)); 61290cf61e8SKyle Evans 613a8e44f4dSKyle Evans if (result != NULL && strlcpy(result, strrchr(buf, '/') + 1, 614a8e44f4dSKyle Evans sizeof(buf)) >= sizeof(buf)) 615a8e44f4dSKyle Evans return (set_error(lbh, BE_ERR_INVALIDNAME)); 61628f16a0fSKyle Evans } 617b29bf2f8SKyle Evans if ((err = zfs_snapshot(lbh->lzh, buf, recursive, NULL)) != 0) { 61828f16a0fSKyle Evans switch (err) { 61928f16a0fSKyle Evans case EZFS_INVALIDNAME: 62028f16a0fSKyle Evans return (set_error(lbh, BE_ERR_INVALIDNAME)); 62128f16a0fSKyle Evans 62228f16a0fSKyle Evans default: 6232989df09SKyle Evans /* 6242989df09SKyle Evans * The other errors that zfs_ioc_snapshot might return 6252989df09SKyle Evans * shouldn't happen if we've set things up properly, so 6262989df09SKyle Evans * we'll gloss over them and call it UNKNOWN as it will 6272989df09SKyle Evans * require further triage. 6282989df09SKyle Evans */ 6292989df09SKyle Evans if (errno == ENOTSUP) 6302989df09SKyle Evans return (set_error(lbh, BE_ERR_NOPOOL)); 63128f16a0fSKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 63228f16a0fSKyle Evans } 63328f16a0fSKyle Evans } 63428f16a0fSKyle Evans 63528f16a0fSKyle Evans return (BE_ERR_SUCCESS); 63628f16a0fSKyle Evans } 63728f16a0fSKyle Evans 63828f16a0fSKyle Evans 63928f16a0fSKyle Evans /* 64028f16a0fSKyle Evans * Create the boot environment specified by the name parameter 64128f16a0fSKyle Evans */ 64228f16a0fSKyle Evans int 64373c3d608SKyle Evans be_create(libbe_handle_t *lbh, const char *name) 64428f16a0fSKyle Evans { 64528f16a0fSKyle Evans int err; 64628f16a0fSKyle Evans 647b29bf2f8SKyle Evans err = be_create_from_existing(lbh, name, be_active_path(lbh)); 64828f16a0fSKyle Evans 64928f16a0fSKyle Evans return (set_error(lbh, err)); 65028f16a0fSKyle Evans } 65128f16a0fSKyle Evans 65228f16a0fSKyle Evans static int 65328f16a0fSKyle Evans be_deep_clone_prop(int prop, void *cb) 65428f16a0fSKyle Evans { 65528f16a0fSKyle Evans int err; 656bfe0869cSKyle Evans struct libbe_dccb *dccb; 65728f16a0fSKyle Evans zprop_source_t src; 65828f16a0fSKyle Evans char pval[BE_MAXPATHLEN]; 65928f16a0fSKyle Evans char source[BE_MAXPATHLEN]; 660af43c24dSKyle Evans char *val; 66128f16a0fSKyle Evans 662bfe0869cSKyle Evans dccb = cb; 66328f16a0fSKyle Evans /* Skip some properties we don't want to touch */ 66473c3d608SKyle Evans if (prop == ZFS_PROP_CANMOUNT) 66528f16a0fSKyle Evans return (ZPROP_CONT); 66628f16a0fSKyle Evans 66728f16a0fSKyle Evans /* Don't copy readonly properties */ 668bfe0869cSKyle Evans if (zfs_prop_readonly(prop)) 66928f16a0fSKyle Evans return (ZPROP_CONT); 67028f16a0fSKyle Evans 67128f16a0fSKyle Evans if ((err = zfs_prop_get(dccb->zhp, prop, (char *)&pval, 672bfe0869cSKyle Evans sizeof(pval), &src, (char *)&source, sizeof(source), false))) 67328f16a0fSKyle Evans /* Just continue if we fail to read a property */ 67428f16a0fSKyle Evans return (ZPROP_CONT); 675bfe0869cSKyle Evans 676be13d48cSKyle Evans /* 677be13d48cSKyle Evans * Only copy locally defined or received properties. This continues 678be13d48cSKyle Evans * to avoid temporary/default/local properties intentionally without 679be13d48cSKyle Evans * breaking received datasets. 680be13d48cSKyle Evans */ 681be13d48cSKyle Evans if (src != ZPROP_SRC_LOCAL && src != ZPROP_SRC_RECEIVED) 68228f16a0fSKyle Evans return (ZPROP_CONT); 68328f16a0fSKyle Evans 684af43c24dSKyle Evans /* Augment mountpoint with altroot, if needed */ 685af43c24dSKyle Evans val = pval; 686fc13fc1cSKyle Evans if (prop == ZFS_PROP_MOUNTPOINT) 687fc13fc1cSKyle Evans val = be_mountpoint_augmented(dccb->lbh, val); 688fc13fc1cSKyle Evans 689af43c24dSKyle Evans nvlist_add_string(dccb->props, zfs_prop_to_name(prop), val); 69028f16a0fSKyle Evans 69128f16a0fSKyle Evans return (ZPROP_CONT); 69228f16a0fSKyle Evans } 69328f16a0fSKyle Evans 694fa30d9edSKyle Evans /* 695fa30d9edSKyle Evans * Return the corresponding boot environment path for a given 696fa30d9edSKyle Evans * dataset path, the constructed path is placed in 'result'. 697fa30d9edSKyle Evans * 698fa30d9edSKyle Evans * example: say our new boot environment name is 'bootenv' and 699fa30d9edSKyle Evans * the dataset path is 'zroot/ROOT/default/data/set'. 700fa30d9edSKyle Evans * 701fa30d9edSKyle Evans * result should produce: 'zroot/ROOT/bootenv/data/set' 702fa30d9edSKyle Evans */ 70328f16a0fSKyle Evans static int 704fa30d9edSKyle Evans be_get_path(struct libbe_deep_clone *ldc, const char *dspath, char *result, int result_size) 705fa30d9edSKyle Evans { 706fa30d9edSKyle Evans char *pos; 707fa30d9edSKyle Evans char *child_dataset; 708fa30d9edSKyle Evans 709fa30d9edSKyle Evans /* match the root path for the boot environments */ 710fa30d9edSKyle Evans pos = strstr(dspath, ldc->lbh->root); 711fa30d9edSKyle Evans 712fa30d9edSKyle Evans /* no match, different pools? */ 713fa30d9edSKyle Evans if (pos == NULL) 714fa30d9edSKyle Evans return (BE_ERR_BADPATH); 715fa30d9edSKyle Evans 716fa30d9edSKyle Evans /* root path of the new boot environment */ 717fa30d9edSKyle Evans snprintf(result, result_size, "%s/%s", ldc->lbh->root, ldc->bename); 718fa30d9edSKyle Evans 719fa30d9edSKyle Evans /* gets us to the parent dataset, the +1 consumes a trailing slash */ 720fa30d9edSKyle Evans pos += strlen(ldc->lbh->root) + 1; 721fa30d9edSKyle Evans 722fa30d9edSKyle Evans /* skip the parent dataset */ 723fa30d9edSKyle Evans if ((child_dataset = strchr(pos, '/')) != NULL) 724fa30d9edSKyle Evans strlcat(result, child_dataset, result_size); 725fa30d9edSKyle Evans 726fa30d9edSKyle Evans return (BE_ERR_SUCCESS); 727fa30d9edSKyle Evans } 728fa30d9edSKyle Evans 729fa30d9edSKyle Evans static int 730fa30d9edSKyle Evans be_clone_cb(zfs_handle_t *ds, void *data) 73128f16a0fSKyle Evans { 73228f16a0fSKyle Evans int err; 73328f16a0fSKyle Evans char be_path[BE_MAXPATHLEN]; 73428f16a0fSKyle Evans char snap_path[BE_MAXPATHLEN]; 73528f16a0fSKyle Evans const char *dspath; 73628f16a0fSKyle Evans zfs_handle_t *snap_hdl; 73728f16a0fSKyle Evans nvlist_t *props; 738fa30d9edSKyle Evans struct libbe_deep_clone *ldc; 73928f16a0fSKyle Evans struct libbe_dccb dccb; 74028f16a0fSKyle Evans 741fa30d9edSKyle Evans ldc = (struct libbe_deep_clone *)data; 74228f16a0fSKyle Evans dspath = zfs_get_name(ds); 743bfe0869cSKyle Evans 744fa30d9edSKyle Evans snprintf(snap_path, sizeof(snap_path), "%s@%s", dspath, ldc->snapname); 745bfe0869cSKyle Evans 746fa30d9edSKyle Evans /* construct the boot environment path from the dataset we're cloning */ 747fa30d9edSKyle Evans if (be_get_path(ldc, dspath, be_path, sizeof(be_path)) != BE_ERR_SUCCESS) 7488e933d9cSJohn Grafton return (BE_ERR_UNKNOWN); 74928f16a0fSKyle Evans 750fa30d9edSKyle Evans /* the dataset to be created (i.e. the boot environment) already exists */ 751fa30d9edSKyle Evans if (zfs_dataset_exists(ldc->lbh->lzh, be_path, ZFS_TYPE_DATASET)) 7528e933d9cSJohn Grafton return (BE_ERR_EXISTS); 753fa30d9edSKyle Evans 754fa30d9edSKyle Evans /* no snapshot found for this dataset, silently skip it */ 755fa30d9edSKyle Evans if (!zfs_dataset_exists(ldc->lbh->lzh, snap_path, ZFS_TYPE_SNAPSHOT)) 756fa30d9edSKyle Evans return (0); 75728f16a0fSKyle Evans 75828f16a0fSKyle Evans if ((snap_hdl = 759fa30d9edSKyle Evans zfs_open(ldc->lbh->lzh, snap_path, ZFS_TYPE_SNAPSHOT)) == NULL) 7608e933d9cSJohn Grafton return (BE_ERR_ZFSOPEN); 76128f16a0fSKyle Evans 76228f16a0fSKyle Evans nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP); 76328f16a0fSKyle Evans nvlist_add_string(props, "canmount", "noauto"); 76428f16a0fSKyle Evans 765fa30d9edSKyle Evans dccb.lbh = ldc->lbh; 76628f16a0fSKyle Evans dccb.zhp = ds; 76728f16a0fSKyle Evans dccb.props = props; 76828f16a0fSKyle Evans if (zprop_iter(be_deep_clone_prop, &dccb, B_FALSE, B_FALSE, 769bfe0869cSKyle Evans ZFS_TYPE_FILESYSTEM) == ZPROP_INVAL) 77028f16a0fSKyle Evans return (-1); 77128f16a0fSKyle Evans 772cc4deabcSKyle Evans if ((err = zfs_clone(snap_hdl, be_path, props)) != 0) 7738e933d9cSJohn Grafton return (BE_ERR_ZFSCLONE); 77428f16a0fSKyle Evans 77528f16a0fSKyle Evans nvlist_free(props); 77628f16a0fSKyle Evans zfs_close(snap_hdl); 77728f16a0fSKyle Evans 778fa30d9edSKyle Evans if (ldc->depth_limit == -1 || ldc->depth < ldc->depth_limit) { 779fa30d9edSKyle Evans ldc->depth++; 78015f0b8c3SMartin Matuska err = zfs_iter_filesystems(ds, 0, be_clone_cb, ldc); 781fa30d9edSKyle Evans ldc->depth--; 782fa30d9edSKyle Evans } 783cc4deabcSKyle Evans 7848e933d9cSJohn Grafton return (err); 78528f16a0fSKyle Evans } 78628f16a0fSKyle Evans 78728f16a0fSKyle Evans /* 788fa30d9edSKyle Evans * Create a boot environment with a given name from a given snapshot. 789fa30d9edSKyle Evans * Snapshots can be in the format 'zroot/ROOT/default@snapshot' or 790fa30d9edSKyle Evans * 'default@snapshot'. In the latter case, 'default@snapshot' will be prepended 791fa30d9edSKyle Evans * with the root path that libbe was initailized with. 79228f16a0fSKyle Evans */ 793fa30d9edSKyle Evans static int 794fa30d9edSKyle Evans be_clone(libbe_handle_t *lbh, const char *bename, const char *snapshot, int depth) 79528f16a0fSKyle Evans { 79628f16a0fSKyle Evans int err; 79728f16a0fSKyle Evans char snap_path[BE_MAXPATHLEN]; 798b29bf2f8SKyle Evans char *parentname, *snapname; 79928f16a0fSKyle Evans zfs_handle_t *parent_hdl; 800fa30d9edSKyle Evans struct libbe_deep_clone ldc; 80128f16a0fSKyle Evans 802fa30d9edSKyle Evans /* ensure the boot environment name is valid */ 803fa30d9edSKyle Evans if ((err = be_validate_name(lbh, bename)) != 0) 80428f16a0fSKyle Evans return (set_error(lbh, err)); 805fa30d9edSKyle Evans 806fa30d9edSKyle Evans /* 807fa30d9edSKyle Evans * prepend the boot environment root path if we're 808fa30d9edSKyle Evans * given a partial snapshot name. 809fa30d9edSKyle Evans */ 810fa30d9edSKyle Evans if ((err = be_root_concat(lbh, snapshot, snap_path)) != 0) 81128f16a0fSKyle Evans return (set_error(lbh, err)); 812fa30d9edSKyle Evans 813fa30d9edSKyle Evans /* ensure the snapshot exists */ 814b29bf2f8SKyle Evans if ((err = be_validate_snap(lbh, snap_path)) != 0) 81528f16a0fSKyle Evans return (set_error(lbh, err)); 81628f16a0fSKyle Evans 817fa30d9edSKyle Evans /* get a copy of the snapshot path so we can disect it */ 818cc4deabcSKyle Evans if ((parentname = strdup(snap_path)) == NULL) 819cc4deabcSKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 820cc4deabcSKyle Evans 821fa30d9edSKyle Evans /* split dataset name from snapshot name */ 82228f16a0fSKyle Evans snapname = strchr(parentname, '@'); 82328f16a0fSKyle Evans if (snapname == NULL) { 824cc4deabcSKyle Evans free(parentname); 825cc4deabcSKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 82628f16a0fSKyle Evans } 82728f16a0fSKyle Evans *snapname = '\0'; 82828f16a0fSKyle Evans snapname++; 82928f16a0fSKyle Evans 830fa30d9edSKyle Evans /* set-up the boot environment */ 831fa30d9edSKyle Evans ldc.lbh = lbh; 832fa30d9edSKyle Evans ldc.bename = bename; 833fa30d9edSKyle Evans ldc.snapname = snapname; 834fa30d9edSKyle Evans ldc.depth = 0; 835fa30d9edSKyle Evans ldc.depth_limit = depth; 83628f16a0fSKyle Evans 837fa30d9edSKyle Evans /* the boot environment will be cloned from this dataset */ 83828f16a0fSKyle Evans parent_hdl = zfs_open(lbh->lzh, parentname, ZFS_TYPE_DATASET); 839fa30d9edSKyle Evans 840fa30d9edSKyle Evans /* create the boot environment */ 841fa30d9edSKyle Evans err = be_clone_cb(parent_hdl, &ldc); 84228f16a0fSKyle Evans 843cc4deabcSKyle Evans free(parentname); 84428f16a0fSKyle Evans return (set_error(lbh, err)); 84528f16a0fSKyle Evans } 84628f16a0fSKyle Evans 847fa30d9edSKyle Evans /* 848fa30d9edSKyle Evans * Create a boot environment from pre-existing snapshot, specifying a depth. 849fa30d9edSKyle Evans */ 850fa30d9edSKyle Evans int be_create_depth(libbe_handle_t *lbh, const char *bename, 851fa30d9edSKyle Evans const char *snap, int depth) 852fa30d9edSKyle Evans { 853fa30d9edSKyle Evans return (be_clone(lbh, bename, snap, depth)); 854fa30d9edSKyle Evans } 855fa30d9edSKyle Evans 856fa30d9edSKyle Evans /* 857fa30d9edSKyle Evans * Create the boot environment from pre-existing snapshot 858fa30d9edSKyle Evans */ 859fa30d9edSKyle Evans int 860fa30d9edSKyle Evans be_create_from_existing_snap(libbe_handle_t *lbh, const char *bename, 861fa30d9edSKyle Evans const char *snap) 862fa30d9edSKyle Evans { 863fa30d9edSKyle Evans return (be_clone(lbh, bename, snap, -1)); 864fa30d9edSKyle Evans } 865fa30d9edSKyle Evans 86628f16a0fSKyle Evans 86728f16a0fSKyle Evans /* 86828f16a0fSKyle Evans * Create a boot environment from an existing boot environment 86928f16a0fSKyle Evans */ 87028f16a0fSKyle Evans int 871fa30d9edSKyle Evans be_create_from_existing(libbe_handle_t *lbh, const char *bename, const char *old) 87228f16a0fSKyle Evans { 87328f16a0fSKyle Evans int err; 874fa30d9edSKyle Evans char snap[BE_MAXPATHLEN]; 87528f16a0fSKyle Evans 876fa30d9edSKyle Evans if ((err = be_snapshot(lbh, old, NULL, true, snap)) != 0) 87728f16a0fSKyle Evans return (set_error(lbh, err)); 87828f16a0fSKyle Evans 879fa30d9edSKyle Evans err = be_clone(lbh, bename, snap, -1); 88028f16a0fSKyle Evans 88128f16a0fSKyle Evans return (set_error(lbh, err)); 88228f16a0fSKyle Evans } 88328f16a0fSKyle Evans 88428f16a0fSKyle Evans 88528f16a0fSKyle Evans /* 88628f16a0fSKyle Evans * Verifies that a snapshot has a valid name, exists, and has a mountpoint of 88728f16a0fSKyle Evans * '/'. Returns BE_ERR_SUCCESS (0), upon success, or the relevant BE_ERR_* upon 88828f16a0fSKyle Evans * failure. Does not set the internal library error state. 88928f16a0fSKyle Evans */ 89028f16a0fSKyle Evans int 891b29bf2f8SKyle Evans be_validate_snap(libbe_handle_t *lbh, const char *snap_name) 89228f16a0fSKyle Evans { 89328f16a0fSKyle Evans 894bfe0869cSKyle Evans if (strlen(snap_name) >= BE_MAXPATHLEN) 89528f16a0fSKyle Evans return (BE_ERR_PATHLEN); 89628f16a0fSKyle Evans 897fcb47c42SKyle Evans if (!zfs_name_valid(snap_name, ZFS_TYPE_SNAPSHOT)) 898fcb47c42SKyle Evans return (BE_ERR_INVALIDNAME); 899fcb47c42SKyle Evans 90028f16a0fSKyle Evans if (!zfs_dataset_exists(lbh->lzh, snap_name, 901bfe0869cSKyle Evans ZFS_TYPE_SNAPSHOT)) 90228f16a0fSKyle Evans return (BE_ERR_NOENT); 90328f16a0fSKyle Evans 90451aecc89SKyle Evans return (BE_ERR_SUCCESS); 90528f16a0fSKyle Evans } 90628f16a0fSKyle Evans 90728f16a0fSKyle Evans 90828f16a0fSKyle Evans /* 90928f16a0fSKyle Evans * Idempotently appends the name argument to the root boot environment path 91028f16a0fSKyle Evans * and copies the resulting string into the result buffer (which is assumed 91128f16a0fSKyle Evans * to be at least BE_MAXPATHLEN characters long. Returns BE_ERR_SUCCESS upon 91228f16a0fSKyle Evans * success, BE_ERR_PATHLEN if the resulting path is longer than BE_MAXPATHLEN, 91328f16a0fSKyle Evans * or BE_ERR_INVALIDNAME if the name is a path that does not begin with 91428f16a0fSKyle Evans * zfs_be_root. Does not set internal library error state. 91528f16a0fSKyle Evans */ 91628f16a0fSKyle Evans int 917b29bf2f8SKyle Evans be_root_concat(libbe_handle_t *lbh, const char *name, char *result) 91828f16a0fSKyle Evans { 91928f16a0fSKyle Evans size_t name_len, root_len; 92028f16a0fSKyle Evans 92128f16a0fSKyle Evans name_len = strlen(name); 92228f16a0fSKyle Evans root_len = strlen(lbh->root); 92328f16a0fSKyle Evans 92428f16a0fSKyle Evans /* Act idempotently; return be name if it is already a full path */ 92528f16a0fSKyle Evans if (strrchr(name, '/') != NULL) { 926bfe0869cSKyle Evans if (strstr(name, lbh->root) != name) 92728f16a0fSKyle Evans return (BE_ERR_INVALIDNAME); 92828f16a0fSKyle Evans 929bfe0869cSKyle Evans if (name_len >= BE_MAXPATHLEN) 93028f16a0fSKyle Evans return (BE_ERR_PATHLEN); 93128f16a0fSKyle Evans 93255b0e92bSKyle Evans strlcpy(result, name, BE_MAXPATHLEN); 93328f16a0fSKyle Evans return (BE_ERR_SUCCESS); 93428f16a0fSKyle Evans } else if (name_len + root_len + 1 < BE_MAXPATHLEN) { 93528f16a0fSKyle Evans snprintf(result, BE_MAXPATHLEN, "%s/%s", lbh->root, 93628f16a0fSKyle Evans name); 93728f16a0fSKyle Evans return (BE_ERR_SUCCESS); 93828f16a0fSKyle Evans } 93928f16a0fSKyle Evans 94028f16a0fSKyle Evans return (BE_ERR_PATHLEN); 94128f16a0fSKyle Evans } 94228f16a0fSKyle Evans 94328f16a0fSKyle Evans 94428f16a0fSKyle Evans /* 94528f16a0fSKyle Evans * Verifies the validity of a boot environment name (A-Za-z0-9-_.). Returns 9465b7803a9SKyle Evans * BE_ERR_SUCCESS (0) if name is valid, otherwise returns BE_ERR_INVALIDNAME 9475b7803a9SKyle Evans * or BE_ERR_PATHLEN. 94828f16a0fSKyle Evans * Does not set internal library error state. 94928f16a0fSKyle Evans */ 95028f16a0fSKyle Evans int 9515b7803a9SKyle Evans be_validate_name(libbe_handle_t *lbh, const char *name) 95228f16a0fSKyle Evans { 95328f16a0fSKyle Evans 9545b7803a9SKyle Evans /* 9555b7803a9SKyle Evans * Impose the additional restriction that the entire dataset name must 9565b7803a9SKyle Evans * not exceed the maximum length of a dataset, i.e. MAXNAMELEN. 9575b7803a9SKyle Evans */ 9585b7803a9SKyle Evans if (strlen(lbh->root) + 1 + strlen(name) > MAXNAMELEN) 9595b7803a9SKyle Evans return (BE_ERR_PATHLEN); 960fcb47c42SKyle Evans 961fcb47c42SKyle Evans if (!zfs_name_valid(name, ZFS_TYPE_DATASET)) 962fcb47c42SKyle Evans return (BE_ERR_INVALIDNAME); 963fcb47c42SKyle Evans 964dadb9c70SKyle Evans /* 965dadb9c70SKyle Evans * ZFS allows spaces in boot environment names, but the kernel can't 966dadb9c70SKyle Evans * handle booting from such a dataset right now. vfs.root.mountfrom 967dadb9c70SKyle Evans * is defined to be a space-separated list, and there's no protocol for 968dadb9c70SKyle Evans * escaping whitespace in the path component of a dev:path spec. So 969dadb9c70SKyle Evans * while loader can handle this situation alright, it can't safely pass 970dadb9c70SKyle Evans * it on to mountroot. 971dadb9c70SKyle Evans */ 972dadb9c70SKyle Evans if (strchr(name, ' ') != NULL) 973dadb9c70SKyle Evans return (BE_ERR_INVALIDNAME); 974dadb9c70SKyle Evans 97528f16a0fSKyle Evans return (BE_ERR_SUCCESS); 97628f16a0fSKyle Evans } 97728f16a0fSKyle Evans 97828f16a0fSKyle Evans 97928f16a0fSKyle Evans /* 98028f16a0fSKyle Evans * usage 98128f16a0fSKyle Evans */ 98228f16a0fSKyle Evans int 98373c3d608SKyle Evans be_rename(libbe_handle_t *lbh, const char *old, const char *new) 98428f16a0fSKyle Evans { 98528f16a0fSKyle Evans char full_old[BE_MAXPATHLEN]; 98628f16a0fSKyle Evans char full_new[BE_MAXPATHLEN]; 98728f16a0fSKyle Evans zfs_handle_t *zfs_hdl; 98828f16a0fSKyle Evans int err; 98928f16a0fSKyle Evans 9905b7803a9SKyle Evans /* 9915b7803a9SKyle Evans * be_validate_name is documented not to set error state, so we should 9925b7803a9SKyle Evans * do so here. 9935b7803a9SKyle Evans */ 9945b7803a9SKyle Evans if ((err = be_validate_name(lbh, new)) != 0) 9955b7803a9SKyle Evans return (set_error(lbh, err)); 996b29bf2f8SKyle Evans if ((err = be_root_concat(lbh, old, full_old)) != 0) 99728f16a0fSKyle Evans return (set_error(lbh, err)); 998b29bf2f8SKyle Evans if ((err = be_root_concat(lbh, new, full_new)) != 0) 99928f16a0fSKyle Evans return (set_error(lbh, err)); 100028f16a0fSKyle Evans 1001bfe0869cSKyle Evans if (!zfs_dataset_exists(lbh->lzh, full_old, ZFS_TYPE_DATASET)) 10022989df09SKyle Evans return (set_error(lbh, BE_ERR_NOENT)); 100328f16a0fSKyle Evans 1004bfe0869cSKyle Evans if (zfs_dataset_exists(lbh->lzh, full_new, ZFS_TYPE_DATASET)) 10052989df09SKyle Evans return (set_error(lbh, BE_ERR_EXISTS)); 100628f16a0fSKyle Evans 100728f16a0fSKyle Evans if ((zfs_hdl = zfs_open(lbh->lzh, full_old, 1008bfe0869cSKyle Evans ZFS_TYPE_FILESYSTEM)) == NULL) 10092989df09SKyle Evans return (set_error(lbh, BE_ERR_ZFSOPEN)); 101028f16a0fSKyle Evans 1011eac7052fSMatt Macy /* recurse, nounmount, forceunmount */ 1012eac7052fSMatt Macy struct renameflags flags = { 1013eac7052fSMatt Macy .nounmount = 1, 1014eac7052fSMatt Macy }; 1015eac7052fSMatt Macy err = zfs_rename(zfs_hdl, full_new, flags); 101628f16a0fSKyle Evans 101728f16a0fSKyle Evans zfs_close(zfs_hdl); 10185b7803a9SKyle Evans if (err != 0) 10195b7803a9SKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 10205b7803a9SKyle Evans return (0); 102128f16a0fSKyle Evans } 102228f16a0fSKyle Evans 102328f16a0fSKyle Evans 102428f16a0fSKyle Evans int 102573c3d608SKyle Evans be_export(libbe_handle_t *lbh, const char *bootenv, int fd) 102628f16a0fSKyle Evans { 102728f16a0fSKyle Evans char snap_name[BE_MAXPATHLEN]; 102828f16a0fSKyle Evans char buf[BE_MAXPATHLEN]; 102928f16a0fSKyle Evans zfs_handle_t *zfs; 10308569a95eSAndriy Gapon sendflags_t flags = { 0 }; 103128f16a0fSKyle Evans int err; 103228f16a0fSKyle Evans 1033b29bf2f8SKyle Evans if ((err = be_snapshot(lbh, bootenv, NULL, true, snap_name)) != 0) 10346d4b1d24SKyle Evans /* Use the error set by be_snapshot */ 10356d4b1d24SKyle Evans return (err); 103628f16a0fSKyle Evans 103728f16a0fSKyle Evans be_root_concat(lbh, snap_name, buf); 103828f16a0fSKyle Evans 1039bfe0869cSKyle Evans if ((zfs = zfs_open(lbh->lzh, buf, ZFS_TYPE_DATASET)) == NULL) 1040506f5fdfSKyle Evans return (set_error(lbh, BE_ERR_ZFSOPEN)); 104128f16a0fSKyle Evans 10429e5787d2SMatt Macy err = zfs_send_one(zfs, NULL, fd, &flags, /* redactbook */ NULL); 10436d4b1d24SKyle Evans zfs_close(zfs); 10446d4b1d24SKyle Evans 104528f16a0fSKyle Evans return (err); 104628f16a0fSKyle Evans } 104728f16a0fSKyle Evans 104828f16a0fSKyle Evans 104928f16a0fSKyle Evans int 105073c3d608SKyle Evans be_import(libbe_handle_t *lbh, const char *bootenv, int fd) 105128f16a0fSKyle Evans { 105228f16a0fSKyle Evans char buf[BE_MAXPATHLEN]; 105328f16a0fSKyle Evans nvlist_t *props; 105428f16a0fSKyle Evans zfs_handle_t *zfs; 105516ac0705SKyle Evans recvflags_t flags = { .nomount = 1 }; 105616ac0705SKyle Evans int err; 105728f16a0fSKyle Evans 105816ac0705SKyle Evans be_root_concat(lbh, bootenv, buf); 105928f16a0fSKyle Evans 106016ac0705SKyle Evans if ((err = zfs_receive(lbh->lzh, buf, NULL, &flags, fd, NULL)) != 0) { 1061506f5fdfSKyle Evans switch (err) { 1062506f5fdfSKyle Evans case EINVAL: 1063506f5fdfSKyle Evans return (set_error(lbh, BE_ERR_NOORIGIN)); 1064506f5fdfSKyle Evans case ENOENT: 1065506f5fdfSKyle Evans return (set_error(lbh, BE_ERR_NOENT)); 1066506f5fdfSKyle Evans case EIO: 1067506f5fdfSKyle Evans return (set_error(lbh, BE_ERR_IO)); 1068506f5fdfSKyle Evans default: 1069506f5fdfSKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 1070506f5fdfSKyle Evans } 107128f16a0fSKyle Evans } 107228f16a0fSKyle Evans 107316ac0705SKyle Evans if ((zfs = zfs_open(lbh->lzh, buf, ZFS_TYPE_FILESYSTEM)) == NULL) 1074506f5fdfSKyle Evans return (set_error(lbh, BE_ERR_ZFSOPEN)); 107528f16a0fSKyle Evans 107628f16a0fSKyle Evans nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP); 107728f16a0fSKyle Evans nvlist_add_string(props, "canmount", "noauto"); 1078ac34fe23SKyle Evans nvlist_add_string(props, "mountpoint", "none"); 107928f16a0fSKyle Evans 108016ac0705SKyle Evans err = zfs_prop_set_list(zfs, props); 108128f16a0fSKyle Evans nvlist_free(props); 108228f16a0fSKyle Evans 10831b057aacSKyle Evans zfs_close(zfs); 10841b057aacSKyle Evans 10851b057aacSKyle Evans if (err != 0) 10861b057aacSKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 10871b057aacSKyle Evans 108816ac0705SKyle Evans return (0); 108928f16a0fSKyle Evans } 109028f16a0fSKyle Evans 10913d1a1f2cSKyle Evans #if SOON 1092c65a2111SKyle Evans static int 1093c65a2111SKyle Evans be_create_child_noent(libbe_handle_t *lbh, const char *active, 1094c65a2111SKyle Evans const char *child_path) 1095c65a2111SKyle Evans { 1096c65a2111SKyle Evans nvlist_t *props; 1097c65a2111SKyle Evans zfs_handle_t *zfs; 1098c65a2111SKyle Evans int err; 1099c65a2111SKyle Evans 1100c65a2111SKyle Evans nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP); 1101c65a2111SKyle Evans nvlist_add_string(props, "canmount", "noauto"); 1102c65a2111SKyle Evans nvlist_add_string(props, "mountpoint", child_path); 1103c65a2111SKyle Evans 1104c65a2111SKyle Evans /* Create */ 1105c65a2111SKyle Evans if ((err = zfs_create(lbh->lzh, active, ZFS_TYPE_DATASET, 1106c65a2111SKyle Evans props)) != 0) { 1107c65a2111SKyle Evans switch (err) { 1108c65a2111SKyle Evans case EZFS_EXISTS: 1109c65a2111SKyle Evans return (set_error(lbh, BE_ERR_EXISTS)); 1110c65a2111SKyle Evans case EZFS_NOENT: 1111c65a2111SKyle Evans return (set_error(lbh, BE_ERR_NOENT)); 1112c65a2111SKyle Evans case EZFS_BADTYPE: 1113c65a2111SKyle Evans case EZFS_BADVERSION: 1114c65a2111SKyle Evans return (set_error(lbh, BE_ERR_NOPOOL)); 1115c65a2111SKyle Evans case EZFS_BADPROP: 1116c65a2111SKyle Evans default: 1117c65a2111SKyle Evans /* We set something up wrong, probably... */ 1118c65a2111SKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 1119c65a2111SKyle Evans } 1120c65a2111SKyle Evans } 1121c65a2111SKyle Evans nvlist_free(props); 1122c65a2111SKyle Evans 1123c65a2111SKyle Evans if ((zfs = zfs_open(lbh->lzh, active, ZFS_TYPE_DATASET)) == NULL) 1124c65a2111SKyle Evans return (set_error(lbh, BE_ERR_ZFSOPEN)); 1125c65a2111SKyle Evans 1126c65a2111SKyle Evans /* Set props */ 1127c65a2111SKyle Evans if ((err = zfs_prop_set(zfs, "canmount", "noauto")) != 0) { 1128c65a2111SKyle Evans zfs_close(zfs); 1129c65a2111SKyle Evans /* 1130c65a2111SKyle Evans * Similar to other cases, this shouldn't fail unless we've 1131c65a2111SKyle Evans * done something wrong. This is a new dataset that shouldn't 1132c65a2111SKyle Evans * have been mounted anywhere between creation and now. 1133c65a2111SKyle Evans */ 1134c65a2111SKyle Evans if (err == EZFS_NOMEM) 1135c65a2111SKyle Evans return (set_error(lbh, BE_ERR_NOMEM)); 1136c65a2111SKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 1137c65a2111SKyle Evans } 1138c65a2111SKyle Evans zfs_close(zfs); 1139c65a2111SKyle Evans return (BE_ERR_SUCCESS); 1140c65a2111SKyle Evans } 1141c65a2111SKyle Evans 1142c65a2111SKyle Evans static int 1143c65a2111SKyle Evans be_create_child_cloned(libbe_handle_t *lbh, const char *active) 1144c65a2111SKyle Evans { 11453d1a1f2cSKyle Evans char buf[BE_MAXPATHLEN], tmp[BE_MAXPATHLEN];; 1146c65a2111SKyle Evans zfs_handle_t *zfs; 1147c65a2111SKyle Evans int err; 1148c65a2111SKyle Evans 1149c65a2111SKyle Evans /* XXX TODO ? */ 1150c65a2111SKyle Evans 1151c65a2111SKyle Evans /* 1152c65a2111SKyle Evans * Establish if the existing path is a zfs dataset or just 1153c65a2111SKyle Evans * the subdirectory of one 1154c65a2111SKyle Evans */ 11553d1a1f2cSKyle Evans strlcpy(tmp, "tmp/be_snap.XXXXX", sizeof(tmp)); 11563d1a1f2cSKyle Evans if (mktemp(tmp) == NULL) 1157c65a2111SKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 1158c65a2111SKyle Evans 11593d1a1f2cSKyle Evans be_root_concat(lbh, tmp, buf); 11603d1a1f2cSKyle Evans printf("Here %s?\n", buf); 1161c65a2111SKyle Evans if ((err = zfs_snapshot(lbh->lzh, buf, false, NULL)) != 0) { 1162c65a2111SKyle Evans switch (err) { 1163c65a2111SKyle Evans case EZFS_INVALIDNAME: 1164c65a2111SKyle Evans return (set_error(lbh, BE_ERR_INVALIDNAME)); 1165c65a2111SKyle Evans 1166c65a2111SKyle Evans default: 1167c65a2111SKyle Evans /* 1168c65a2111SKyle Evans * The other errors that zfs_ioc_snapshot might return 1169c65a2111SKyle Evans * shouldn't happen if we've set things up properly, so 1170c65a2111SKyle Evans * we'll gloss over them and call it UNKNOWN as it will 1171c65a2111SKyle Evans * require further triage. 1172c65a2111SKyle Evans */ 1173c65a2111SKyle Evans if (errno == ENOTSUP) 1174c65a2111SKyle Evans return (set_error(lbh, BE_ERR_NOPOOL)); 1175c65a2111SKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 1176c65a2111SKyle Evans } 1177c65a2111SKyle Evans } 1178c65a2111SKyle Evans 1179c65a2111SKyle Evans /* Clone */ 1180c65a2111SKyle Evans if ((zfs = zfs_open(lbh->lzh, buf, ZFS_TYPE_SNAPSHOT)) == NULL) 1181c65a2111SKyle Evans return (BE_ERR_ZFSOPEN); 1182c65a2111SKyle Evans 1183c65a2111SKyle Evans if ((err = zfs_clone(zfs, active, NULL)) != 0) 1184c65a2111SKyle Evans /* XXX TODO correct error */ 1185c65a2111SKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 1186c65a2111SKyle Evans 1187c65a2111SKyle Evans /* set props */ 1188c65a2111SKyle Evans zfs_close(zfs); 1189c65a2111SKyle Evans return (BE_ERR_SUCCESS); 1190c65a2111SKyle Evans } 119128f16a0fSKyle Evans 119228f16a0fSKyle Evans int 119373c3d608SKyle Evans be_add_child(libbe_handle_t *lbh, const char *child_path, bool cp_if_exists) 119428f16a0fSKyle Evans { 119573c3d608SKyle Evans struct stat sb; 1196c65a2111SKyle Evans char active[BE_MAXPATHLEN], buf[BE_MAXPATHLEN]; 119728f16a0fSKyle Evans nvlist_t *props; 119873c3d608SKyle Evans const char *s; 119928f16a0fSKyle Evans 120028f16a0fSKyle Evans /* Require absolute paths */ 1201bfe0869cSKyle Evans if (*child_path != '/') 12026d4b1d24SKyle Evans return (set_error(lbh, BE_ERR_BADPATH)); 120328f16a0fSKyle Evans 12046d4b1d24SKyle Evans strlcpy(active, be_active_path(lbh), BE_MAXPATHLEN); 120528f16a0fSKyle Evans strcpy(buf, active); 120628f16a0fSKyle Evans 120728f16a0fSKyle Evans /* Create non-mountable parent dataset(s) */ 120873c3d608SKyle Evans s = child_path; 120928f16a0fSKyle Evans for (char *p; (p = strchr(s+1, '/')) != NULL; s = p) { 121028f16a0fSKyle Evans size_t len = p - s; 121128f16a0fSKyle Evans strncat(buf, s, len); 121228f16a0fSKyle Evans 121328f16a0fSKyle Evans nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP); 121428f16a0fSKyle Evans nvlist_add_string(props, "canmount", "off"); 121528f16a0fSKyle Evans nvlist_add_string(props, "mountpoint", "none"); 121628f16a0fSKyle Evans zfs_create(lbh->lzh, buf, ZFS_TYPE_DATASET, props); 121728f16a0fSKyle Evans nvlist_free(props); 121828f16a0fSKyle Evans } 121928f16a0fSKyle Evans 122028f16a0fSKyle Evans /* Path does not exist as a descendent of / yet */ 12216d4b1d24SKyle Evans if (strlcat(active, child_path, BE_MAXPATHLEN) >= BE_MAXPATHLEN) 12226d4b1d24SKyle Evans return (set_error(lbh, BE_ERR_PATHLEN)); 122328f16a0fSKyle Evans 122428f16a0fSKyle Evans if (stat(child_path, &sb) != 0) { 122528f16a0fSKyle Evans /* Verify that error is ENOENT */ 12266d4b1d24SKyle Evans if (errno != ENOENT) 12276d4b1d24SKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 1228c65a2111SKyle Evans return (be_create_child_noent(lbh, active, child_path)); 1229c65a2111SKyle Evans } else if (cp_if_exists) 123028f16a0fSKyle Evans /* Path is already a descendent of / and should be copied */ 1231c65a2111SKyle Evans return (be_create_child_cloned(lbh, active)); 12326d4b1d24SKyle Evans return (set_error(lbh, BE_ERR_EXISTS)); 123328f16a0fSKyle Evans } 12343d1a1f2cSKyle Evans #endif /* SOON */ 123528f16a0fSKyle Evans 12368d4ce358SKyle Evans /* 1237e307eb94SToomas Soome * Deactivate old BE dataset; currently just sets canmount=noauto or 1238e307eb94SToomas Soome * resets boot once configuration. 12398d4ce358SKyle Evans */ 1240e307eb94SToomas Soome int 1241e307eb94SToomas Soome be_deactivate(libbe_handle_t *lbh, const char *ds, bool temporary) 12428d4ce358SKyle Evans { 12438d4ce358SKyle Evans zfs_handle_t *zfs; 12448d4ce358SKyle Evans 1245e307eb94SToomas Soome if (temporary) { 1246e307eb94SToomas Soome return (lzbe_set_boot_device( 1247e307eb94SToomas Soome zpool_get_name(lbh->active_phandle), lzbe_add, NULL)); 1248e307eb94SToomas Soome } 1249e307eb94SToomas Soome 12508d4ce358SKyle Evans if ((zfs = zfs_open(lbh->lzh, ds, ZFS_TYPE_DATASET)) == NULL) 12518d4ce358SKyle Evans return (1); 12528d4ce358SKyle Evans if (zfs_prop_set(zfs, "canmount", "noauto") != 0) 12538d4ce358SKyle Evans return (1); 12548d4ce358SKyle Evans zfs_close(zfs); 12558d4ce358SKyle Evans return (0); 12568d4ce358SKyle Evans } 125728f16a0fSKyle Evans 125828f16a0fSKyle Evans int 125973c3d608SKyle Evans be_activate(libbe_handle_t *lbh, const char *bootenv, bool temporary) 126028f16a0fSKyle Evans { 126128f16a0fSKyle Evans char be_path[BE_MAXPATHLEN]; 1262e307eb94SToomas Soome nvlist_t *dsprops; 1263*2a58b312SMartin Matuska const char *origin; 12640cadc427SKyle Evans zfs_handle_t *zhp; 126528f16a0fSKyle Evans int err; 126628f16a0fSKyle Evans 126728f16a0fSKyle Evans be_root_concat(lbh, bootenv, be_path); 126828f16a0fSKyle Evans 126928f16a0fSKyle Evans /* Note: be_exists fails if mountpoint is not / */ 1270162ec569SKyle Evans if ((err = be_exists(lbh, be_path)) != 0) 1271162ec569SKyle Evans return (set_error(lbh, err)); 127228f16a0fSKyle Evans 127328f16a0fSKyle Evans if (temporary) { 1274e307eb94SToomas Soome return (lzbe_set_boot_device( 1275e307eb94SToomas Soome zpool_get_name(lbh->active_phandle), lzbe_add, be_path)); 127628f16a0fSKyle Evans } else { 12777edc1bd9SGleb Smirnoff if (strncmp(lbh->bootfs, "-", 1) != 0 && 12787edc1bd9SGleb Smirnoff be_deactivate(lbh, lbh->bootfs, false) != 0) 12798d4ce358SKyle Evans return (-1); 12808d4ce358SKyle Evans 128128f16a0fSKyle Evans /* Obtain bootenv zpool */ 1282c3a34c08SKyle Evans err = zpool_set_prop(lbh->active_phandle, "bootfs", be_path); 12830cadc427SKyle Evans if (err) 12840cadc427SKyle Evans return (-1); 128528f16a0fSKyle Evans 12860cadc427SKyle Evans zhp = zfs_open(lbh->lzh, be_path, ZFS_TYPE_FILESYSTEM); 12870cadc427SKyle Evans if (zhp == NULL) 12880cadc427SKyle Evans return (-1); 128928f16a0fSKyle Evans 12904635676dSKyle Evans if (be_prop_list_alloc(&dsprops) != 0) 12914635676dSKyle Evans return (-1); 12924635676dSKyle Evans 12934635676dSKyle Evans if (be_get_dataset_props(lbh, be_path, dsprops) != 0) { 12944635676dSKyle Evans nvlist_free(dsprops); 12954635676dSKyle Evans return (-1); 12964635676dSKyle Evans } 12974635676dSKyle Evans 12984635676dSKyle Evans if (nvlist_lookup_string(dsprops, "origin", &origin) == 0) 12990cadc427SKyle Evans err = zfs_promote(zhp); 13004635676dSKyle Evans nvlist_free(dsprops); 13014635676dSKyle Evans 13020cadc427SKyle Evans zfs_close(zhp); 13030cadc427SKyle Evans 13040cadc427SKyle Evans if (err) 130528f16a0fSKyle Evans return (-1); 130628f16a0fSKyle Evans } 13070cadc427SKyle Evans 13080cadc427SKyle Evans return (BE_ERR_SUCCESS); 130928f16a0fSKyle Evans } 1310