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> 36*8f5c6c31SKyle Evans #include <sys/queue.h> 37485172f5SKyle Evans #include <sys/zfs_context.h> 38485172f5SKyle Evans #include <sys/mntent.h> 39485172f5SKyle 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 51*8f5c6c31SKyle Evans struct promote_entry { 52*8f5c6c31SKyle Evans char name[BE_MAXPATHLEN]; 53*8f5c6c31SKyle Evans SLIST_ENTRY(promote_entry) link; 54*8f5c6c31SKyle Evans }; 55*8f5c6c31SKyle Evans 56be7dd423SKyle Evans struct be_destroy_data { 57be7dd423SKyle Evans libbe_handle_t *lbh; 58*8f5c6c31SKyle Evans char target_name[BE_MAXPATHLEN]; 59be7dd423SKyle Evans char *snapname; 60*8f5c6c31SKyle Evans SLIST_HEAD(, promote_entry) promotelist; 61be7dd423SKyle Evans }; 62be7dd423SKyle Evans 633d1a1f2cSKyle Evans #if SOON 64c65a2111SKyle Evans static int be_create_child_noent(libbe_handle_t *lbh, const char *active, 65c65a2111SKyle Evans const char *child_path); 66c65a2111SKyle Evans static int be_create_child_cloned(libbe_handle_t *lbh, const char *active); 673d1a1f2cSKyle Evans #endif 68c65a2111SKyle Evans 6990cf61e8SKyle Evans /* Arbitrary... should tune */ 7090cf61e8SKyle Evans #define BE_SNAP_SERIAL_MAX 1024 7190cf61e8SKyle Evans 7228f16a0fSKyle Evans /* 73ee16b7c9SKyle Evans * Iterator function for locating the rootfs amongst the children of the 74ee16b7c9SKyle Evans * zfs_be_root set by loader(8). data is expected to be a libbe_handle_t *. 75ee16b7c9SKyle Evans */ 76ee16b7c9SKyle Evans static int 7751aecc89SKyle Evans be_locate_rootfs(libbe_handle_t *lbh) 78ee16b7c9SKyle Evans { 794ab5187dSKyle Evans struct statfs sfs; 80485172f5SKyle Evans struct mnttab entry; 8151aecc89SKyle Evans zfs_handle_t *zfs; 82ee16b7c9SKyle Evans 834ab5187dSKyle Evans /* 844ab5187dSKyle Evans * Check first if root is ZFS; if not, we'll bail on rootfs capture. 854ab5187dSKyle Evans * Unfortunately needed because zfs_path_to_zhandle will emit to 864ab5187dSKyle Evans * stderr if / isn't actually a ZFS filesystem, which we'd like 874ab5187dSKyle Evans * to avoid. 884ab5187dSKyle Evans */ 894ab5187dSKyle Evans if (statfs("/", &sfs) == 0) { 904ab5187dSKyle Evans statfs2mnttab(&sfs, &entry); 914ab5187dSKyle Evans if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) 924ab5187dSKyle Evans return (1); 934ab5187dSKyle Evans } else 944ab5187dSKyle Evans return (1); 9551aecc89SKyle Evans zfs = zfs_path_to_zhandle(lbh->lzh, "/", ZFS_TYPE_FILESYSTEM); 9651aecc89SKyle Evans if (zfs == NULL) 97ee16b7c9SKyle Evans return (1); 98ee16b7c9SKyle Evans 9951aecc89SKyle Evans strlcpy(lbh->rootfs, zfs_get_name(zfs), sizeof(lbh->rootfs)); 10051aecc89SKyle Evans zfs_close(zfs); 101ee16b7c9SKyle Evans return (0); 102ee16b7c9SKyle Evans } 103ee16b7c9SKyle Evans 104ee16b7c9SKyle Evans /* 10528f16a0fSKyle Evans * Initializes the libbe context to operate in the root boot environment 10628f16a0fSKyle Evans * dataset, for example, zroot/ROOT. 10728f16a0fSKyle Evans */ 10828f16a0fSKyle Evans libbe_handle_t * 109cc624025SKyle Evans libbe_init(const char *root) 11028f16a0fSKyle Evans { 111fc13fc1cSKyle Evans char altroot[MAXPATHLEN]; 11228f16a0fSKyle Evans libbe_handle_t *lbh; 113c3a34c08SKyle Evans char *poolname, *pos; 114c3a34c08SKyle Evans int pnamelen; 11528f16a0fSKyle Evans 116c3a34c08SKyle Evans lbh = NULL; 117c3a34c08SKyle Evans poolname = pos = NULL; 11828f16a0fSKyle Evans 119c3a34c08SKyle Evans if ((lbh = calloc(1, sizeof(libbe_handle_t))) == NULL) 120c3a34c08SKyle Evans goto err; 12128f16a0fSKyle Evans 122c3a34c08SKyle Evans if ((lbh->lzh = libzfs_init()) == NULL) 123c3a34c08SKyle Evans goto err; 12428f16a0fSKyle Evans 125cc624025SKyle Evans /* 126cc624025SKyle Evans * Grab rootfs, we'll work backwards from there if an optional BE root 127cc624025SKyle Evans * has not been passed in. 128cc624025SKyle Evans */ 1294ab5187dSKyle Evans if (be_locate_rootfs(lbh) != 0) { 1304ab5187dSKyle Evans if (root == NULL) 131c3a34c08SKyle Evans goto err; 1324ab5187dSKyle Evans *lbh->rootfs = '\0'; 1334ab5187dSKyle Evans } 134cc624025SKyle Evans if (root == NULL) { 135cc624025SKyle Evans /* Strip off the final slash from rootfs to get the be root */ 13651aecc89SKyle Evans strlcpy(lbh->root, lbh->rootfs, sizeof(lbh->root)); 13751aecc89SKyle Evans pos = strrchr(lbh->root, '/'); 13851aecc89SKyle Evans if (pos == NULL) 13951aecc89SKyle Evans goto err; 14051aecc89SKyle Evans *pos = '\0'; 141cc624025SKyle Evans } else 142cc624025SKyle Evans strlcpy(lbh->root, root, sizeof(lbh->root)); 143c3a34c08SKyle Evans 144c3a34c08SKyle Evans if ((pos = strchr(lbh->root, '/')) == NULL) 145c3a34c08SKyle Evans goto err; 146c3a34c08SKyle Evans 147c3a34c08SKyle Evans pnamelen = pos - lbh->root; 148c3a34c08SKyle Evans poolname = malloc(pnamelen + 1); 149c3a34c08SKyle Evans if (poolname == NULL) 150c3a34c08SKyle Evans goto err; 151c3a34c08SKyle Evans 15255b0e92bSKyle Evans strlcpy(poolname, lbh->root, pnamelen + 1); 153c3a34c08SKyle Evans if ((lbh->active_phandle = zpool_open(lbh->lzh, poolname)) == NULL) 154c3a34c08SKyle Evans goto err; 155a8e44f4dSKyle Evans free(poolname); 156a8e44f4dSKyle Evans poolname = NULL; 157c3a34c08SKyle Evans 158c3a34c08SKyle Evans if (zpool_get_prop(lbh->active_phandle, ZPOOL_PROP_BOOTFS, lbh->bootfs, 15955b0e92bSKyle Evans sizeof(lbh->bootfs), NULL, true) != 0) 160c3a34c08SKyle Evans goto err; 161c3a34c08SKyle Evans 162fc13fc1cSKyle Evans if (zpool_get_prop(lbh->active_phandle, ZPOOL_PROP_ALTROOT, 163fc13fc1cSKyle Evans altroot, sizeof(altroot), NULL, true) == 0 && 164fc13fc1cSKyle Evans strcmp(altroot, "-") != 0) 165fc13fc1cSKyle Evans lbh->altroot_len = strlen(altroot); 166fc13fc1cSKyle Evans 16728f16a0fSKyle Evans return (lbh); 168c3a34c08SKyle Evans err: 169c3a34c08SKyle Evans if (lbh != NULL) { 170c3a34c08SKyle Evans if (lbh->active_phandle != NULL) 171c3a34c08SKyle Evans zpool_close(lbh->active_phandle); 172c3a34c08SKyle Evans if (lbh->lzh != NULL) 173c3a34c08SKyle Evans libzfs_fini(lbh->lzh); 174c3a34c08SKyle Evans free(lbh); 175c3a34c08SKyle Evans } 176c3a34c08SKyle Evans free(poolname); 177c3a34c08SKyle Evans return (NULL); 17828f16a0fSKyle Evans } 17928f16a0fSKyle Evans 18028f16a0fSKyle Evans 18128f16a0fSKyle Evans /* 18228f16a0fSKyle Evans * Free memory allocated by libbe_init() 18328f16a0fSKyle Evans */ 18428f16a0fSKyle Evans void 18528f16a0fSKyle Evans libbe_close(libbe_handle_t *lbh) 18628f16a0fSKyle Evans { 187bfe0869cSKyle Evans 188c3a34c08SKyle Evans if (lbh->active_phandle != NULL) 189c3a34c08SKyle Evans zpool_close(lbh->active_phandle); 19028f16a0fSKyle Evans libzfs_fini(lbh->lzh); 19128f16a0fSKyle Evans free(lbh); 19228f16a0fSKyle Evans } 19328f16a0fSKyle Evans 1949b1662e6SKyle Evans /* 1959b1662e6SKyle Evans * Proxy through to libzfs for the moment. 1969b1662e6SKyle Evans */ 1979b1662e6SKyle Evans void 1989b1662e6SKyle Evans be_nicenum(uint64_t num, char *buf, size_t buflen) 1999b1662e6SKyle Evans { 2009b1662e6SKyle Evans 2019b1662e6SKyle Evans zfs_nicenum(num, buf, buflen); 2029b1662e6SKyle Evans } 20328f16a0fSKyle Evans 204*8f5c6c31SKyle Evans static bool 205*8f5c6c31SKyle Evans be_should_promote_clones(zfs_handle_t *zfs_hdl, struct be_destroy_data *bdd) 206*8f5c6c31SKyle Evans { 207*8f5c6c31SKyle Evans char *atpos; 208*8f5c6c31SKyle Evans 209*8f5c6c31SKyle Evans if (zfs_get_type(zfs_hdl) != ZFS_TYPE_SNAPSHOT) 210*8f5c6c31SKyle Evans return (false); 211*8f5c6c31SKyle Evans 212*8f5c6c31SKyle Evans /* 213*8f5c6c31SKyle Evans * If we're deleting a snapshot, we need to make sure we only promote 214*8f5c6c31SKyle Evans * clones that are derived from one of the snapshots we're deleting, 215*8f5c6c31SKyle Evans * rather than that of a snapshot we're not touching. This keeps stuff 216*8f5c6c31SKyle Evans * in a consistent state, making sure that we don't error out unless 217*8f5c6c31SKyle Evans * we really need to. 218*8f5c6c31SKyle Evans */ 219*8f5c6c31SKyle Evans if (bdd->snapname == NULL) 220*8f5c6c31SKyle Evans return (true); 221*8f5c6c31SKyle Evans 222*8f5c6c31SKyle Evans atpos = strchr(zfs_get_name(zfs_hdl), '@'); 223*8f5c6c31SKyle Evans return (strcmp(atpos + 1, bdd->snapname) == 0); 224*8f5c6c31SKyle Evans } 225*8f5c6c31SKyle Evans 226*8f5c6c31SKyle Evans /* 227*8f5c6c31SKyle Evans * This is executed from be_promote_dependent_clones via zfs_iter_dependents, 228*8f5c6c31SKyle Evans * It checks if the dependent type is a snapshot then attempts to find any 229*8f5c6c31SKyle Evans * clones associated with it. Any clones not related to the destroy target are 230*8f5c6c31SKyle Evans * added to the promote list. 231*8f5c6c31SKyle Evans */ 232*8f5c6c31SKyle Evans static int 233*8f5c6c31SKyle Evans be_dependent_clone_cb(zfs_handle_t *zfs_hdl, void *data) 234*8f5c6c31SKyle Evans { 235*8f5c6c31SKyle Evans int err; 236*8f5c6c31SKyle Evans bool found; 237*8f5c6c31SKyle Evans char *name; 238*8f5c6c31SKyle Evans struct nvlist *nvl; 239*8f5c6c31SKyle Evans struct nvpair *nvp; 240*8f5c6c31SKyle Evans struct be_destroy_data *bdd; 241*8f5c6c31SKyle Evans struct promote_entry *entry, *newentry; 242*8f5c6c31SKyle Evans 243*8f5c6c31SKyle Evans nvp = NULL; 244*8f5c6c31SKyle Evans err = 0; 245*8f5c6c31SKyle Evans bdd = (struct be_destroy_data *)data; 246*8f5c6c31SKyle Evans 247*8f5c6c31SKyle Evans if (be_should_promote_clones(zfs_hdl, bdd) && 248*8f5c6c31SKyle Evans (nvl = zfs_get_clones_nvl(zfs_hdl)) != NULL) { 249*8f5c6c31SKyle Evans while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { 250*8f5c6c31SKyle Evans name = nvpair_name(nvp); 251*8f5c6c31SKyle Evans 252*8f5c6c31SKyle Evans /* 253*8f5c6c31SKyle Evans * Skip if the clone is equal to, or a child of, the 254*8f5c6c31SKyle Evans * destroy target. 255*8f5c6c31SKyle Evans */ 256*8f5c6c31SKyle Evans if (strncmp(name, bdd->target_name, 257*8f5c6c31SKyle Evans strlen(bdd->target_name)) == 0 || 258*8f5c6c31SKyle Evans strstr(name, bdd->target_name) == name) { 259*8f5c6c31SKyle Evans continue; 260*8f5c6c31SKyle Evans } 261*8f5c6c31SKyle Evans 262*8f5c6c31SKyle Evans found = false; 263*8f5c6c31SKyle Evans SLIST_FOREACH(entry, &bdd->promotelist, link) { 264*8f5c6c31SKyle Evans if (strcmp(entry->name, name) == 0) { 265*8f5c6c31SKyle Evans found = true; 266*8f5c6c31SKyle Evans break; 267*8f5c6c31SKyle Evans } 268*8f5c6c31SKyle Evans } 269*8f5c6c31SKyle Evans 270*8f5c6c31SKyle Evans if (found) 271*8f5c6c31SKyle Evans continue; 272*8f5c6c31SKyle Evans 273*8f5c6c31SKyle Evans newentry = malloc(sizeof(struct promote_entry)); 274*8f5c6c31SKyle Evans if (newentry == NULL) { 275*8f5c6c31SKyle Evans err = ENOMEM; 276*8f5c6c31SKyle Evans break; 277*8f5c6c31SKyle Evans } 278*8f5c6c31SKyle Evans 279*8f5c6c31SKyle Evans #define BE_COPY_NAME(entry, src) \ 280*8f5c6c31SKyle Evans strlcpy((entry)->name, (src), sizeof((entry)->name)) 281*8f5c6c31SKyle Evans if (BE_COPY_NAME(newentry, name) >= 282*8f5c6c31SKyle Evans sizeof(newentry->name)) { 283*8f5c6c31SKyle Evans /* Shouldn't happen. */ 284*8f5c6c31SKyle Evans free(newentry); 285*8f5c6c31SKyle Evans err = ENAMETOOLONG; 286*8f5c6c31SKyle Evans break; 287*8f5c6c31SKyle Evans } 288*8f5c6c31SKyle Evans #undef BE_COPY_NAME 289*8f5c6c31SKyle Evans 290*8f5c6c31SKyle Evans /* 291*8f5c6c31SKyle Evans * We're building up a SLIST here to make sure both that 292*8f5c6c31SKyle Evans * we get the order right and so that we don't 293*8f5c6c31SKyle Evans * inadvertently observe the wrong state by promoting 294*8f5c6c31SKyle Evans * datasets while we're still walking the tree. The 295*8f5c6c31SKyle Evans * latter can lead to situations where we promote a BE 296*8f5c6c31SKyle Evans * then effectively demote it again. 297*8f5c6c31SKyle Evans */ 298*8f5c6c31SKyle Evans SLIST_INSERT_HEAD(&bdd->promotelist, newentry, link); 299*8f5c6c31SKyle Evans } 300*8f5c6c31SKyle Evans nvlist_free(nvl); 301*8f5c6c31SKyle Evans } 302*8f5c6c31SKyle Evans zfs_close(zfs_hdl); 303*8f5c6c31SKyle Evans return (err); 304*8f5c6c31SKyle Evans } 305*8f5c6c31SKyle Evans 306*8f5c6c31SKyle Evans /* 307*8f5c6c31SKyle Evans * This is called before a destroy, so that any datasets(environments) that are 308*8f5c6c31SKyle Evans * dependent on this one get promoted before destroying the target. 309*8f5c6c31SKyle Evans */ 310*8f5c6c31SKyle Evans static int 311*8f5c6c31SKyle Evans be_promote_dependent_clones(zfs_handle_t *zfs_hdl, struct be_destroy_data *bdd) 312*8f5c6c31SKyle Evans { 313*8f5c6c31SKyle Evans int err; 314*8f5c6c31SKyle Evans zfs_handle_t *clone; 315*8f5c6c31SKyle Evans struct promote_entry *entry; 316*8f5c6c31SKyle Evans 317*8f5c6c31SKyle Evans snprintf(bdd->target_name, BE_MAXPATHLEN, "%s/", zfs_get_name(zfs_hdl)); 318*8f5c6c31SKyle Evans err = zfs_iter_dependents(zfs_hdl, true, be_dependent_clone_cb, bdd); 319*8f5c6c31SKyle Evans 320*8f5c6c31SKyle Evans /* 321*8f5c6c31SKyle Evans * Drain the list and walk away from it if we're only deleting a 322*8f5c6c31SKyle Evans * snapshot. 323*8f5c6c31SKyle Evans */ 324*8f5c6c31SKyle Evans if (bdd->snapname != NULL && !SLIST_EMPTY(&bdd->promotelist)) 325*8f5c6c31SKyle Evans err = BE_ERR_HASCLONES; 326*8f5c6c31SKyle Evans while (!SLIST_EMPTY(&bdd->promotelist)) { 327*8f5c6c31SKyle Evans entry = SLIST_FIRST(&bdd->promotelist); 328*8f5c6c31SKyle Evans SLIST_REMOVE_HEAD(&bdd->promotelist, link); 329*8f5c6c31SKyle Evans 330*8f5c6c31SKyle Evans #define ZFS_GRAB_CLONE() \ 331*8f5c6c31SKyle Evans zfs_open(bdd->lbh->lzh, entry->name, ZFS_TYPE_FILESYSTEM) 332*8f5c6c31SKyle Evans /* 333*8f5c6c31SKyle Evans * Just skip this part on error, we still want to clean up the 334*8f5c6c31SKyle Evans * promotion list after the first error. We'll then preserve it 335*8f5c6c31SKyle Evans * all the way back. 336*8f5c6c31SKyle Evans */ 337*8f5c6c31SKyle Evans if (err == 0 && (clone = ZFS_GRAB_CLONE()) != NULL) { 338*8f5c6c31SKyle Evans err = zfs_promote(clone); 339*8f5c6c31SKyle Evans if (err != 0) 340*8f5c6c31SKyle Evans err = BE_ERR_DESTROYMNT; 341*8f5c6c31SKyle Evans zfs_close(clone); 342*8f5c6c31SKyle Evans } 343*8f5c6c31SKyle Evans #undef ZFS_GRAB_CLONE 344*8f5c6c31SKyle Evans free(entry); 345*8f5c6c31SKyle Evans } 346*8f5c6c31SKyle Evans 347*8f5c6c31SKyle Evans return (err); 348*8f5c6c31SKyle Evans } 349*8f5c6c31SKyle Evans 350920abf4dSKyle Evans static int 351920abf4dSKyle Evans be_destroy_cb(zfs_handle_t *zfs_hdl, void *data) 352920abf4dSKyle Evans { 353be7dd423SKyle Evans char path[BE_MAXPATHLEN]; 354be7dd423SKyle Evans struct be_destroy_data *bdd; 355be7dd423SKyle Evans zfs_handle_t *snap; 356920abf4dSKyle Evans int err; 357920abf4dSKyle Evans 358be7dd423SKyle Evans bdd = (struct be_destroy_data *)data; 359be7dd423SKyle Evans if (bdd->snapname == NULL) { 360be7dd423SKyle Evans err = zfs_iter_children(zfs_hdl, be_destroy_cb, data); 361be7dd423SKyle Evans if (err != 0) 362920abf4dSKyle Evans return (err); 363be7dd423SKyle Evans return (zfs_destroy(zfs_hdl, false)); 364be7dd423SKyle Evans } 365be7dd423SKyle Evans /* If we're dealing with snapshots instead, delete that one alone */ 366be7dd423SKyle Evans err = zfs_iter_filesystems(zfs_hdl, be_destroy_cb, data); 367be7dd423SKyle Evans if (err != 0) 368920abf4dSKyle Evans return (err); 369be7dd423SKyle Evans /* 370be7dd423SKyle Evans * This part is intentionally glossing over any potential errors, 371be7dd423SKyle Evans * because there's a lot less potential for errors when we're cleaning 372be7dd423SKyle Evans * up snapshots rather than a full deep BE. The primary error case 373be7dd423SKyle Evans * here being if the snapshot doesn't exist in the first place, which 374be7dd423SKyle Evans * the caller will likely deem insignificant as long as it doesn't 375be7dd423SKyle Evans * exist after the call. Thus, such a missing snapshot shouldn't jam 376be7dd423SKyle Evans * up the destruction. 377be7dd423SKyle Evans */ 378be7dd423SKyle Evans snprintf(path, sizeof(path), "%s@%s", zfs_get_name(zfs_hdl), 379be7dd423SKyle Evans bdd->snapname); 380be7dd423SKyle Evans if (!zfs_dataset_exists(bdd->lbh->lzh, path, ZFS_TYPE_SNAPSHOT)) 381be7dd423SKyle Evans return (0); 382be7dd423SKyle Evans snap = zfs_open(bdd->lbh->lzh, path, ZFS_TYPE_SNAPSHOT); 383be7dd423SKyle Evans if (snap != NULL) 384be7dd423SKyle Evans zfs_destroy(snap, false); 385920abf4dSKyle Evans return (0); 386920abf4dSKyle Evans } 387920abf4dSKyle Evans 3881dc85563SKyle Evans #define BE_DESTROY_WANTORIGIN (BE_DESTROY_ORIGIN | BE_DESTROY_AUTOORIGIN) 38928f16a0fSKyle Evans /* 39028f16a0fSKyle Evans * Destroy the boot environment or snapshot specified by the name 39128f16a0fSKyle Evans * parameter. Options are or'd together with the possible values: 39228f16a0fSKyle Evans * BE_DESTROY_FORCE : forces operation on mounted datasets 393be7dd423SKyle Evans * BE_DESTROY_ORIGIN: destroy the origin snapshot as well 39428f16a0fSKyle Evans */ 395*8f5c6c31SKyle Evans static int 396*8f5c6c31SKyle Evans be_destroy_internal(libbe_handle_t *lbh, const char *name, int options, 397*8f5c6c31SKyle Evans bool odestroyer) 39828f16a0fSKyle Evans { 399be7dd423SKyle Evans struct be_destroy_data bdd; 40013c62c50SKyle Evans char origin[BE_MAXPATHLEN], path[BE_MAXPATHLEN]; 40128f16a0fSKyle Evans zfs_handle_t *fs; 402be7dd423SKyle Evans char *snapdelim; 403bfe0869cSKyle Evans int err, force, mounted; 404be7dd423SKyle Evans size_t rootlen; 40528f16a0fSKyle Evans 406be7dd423SKyle Evans bdd.lbh = lbh; 407be7dd423SKyle Evans bdd.snapname = NULL; 408*8f5c6c31SKyle Evans SLIST_INIT(&bdd.promotelist); 409bfe0869cSKyle Evans force = options & BE_DESTROY_FORCE; 41013c62c50SKyle Evans *origin = '\0'; 41128f16a0fSKyle Evans 41228f16a0fSKyle Evans be_root_concat(lbh, name, path); 41328f16a0fSKyle Evans 414be7dd423SKyle Evans if ((snapdelim = strchr(path, '@')) == NULL) { 415bfe0869cSKyle Evans if (!zfs_dataset_exists(lbh->lzh, path, ZFS_TYPE_FILESYSTEM)) 41628f16a0fSKyle Evans return (set_error(lbh, BE_ERR_NOENT)); 41728f16a0fSKyle Evans 418f08dac4eSKyle Evans if (strcmp(path, lbh->rootfs) == 0 || 419f08dac4eSKyle Evans strcmp(path, lbh->bootfs) == 0) 42028f16a0fSKyle Evans return (set_error(lbh, BE_ERR_DESTROYACT)); 42128f16a0fSKyle Evans 422be7dd423SKyle Evans fs = zfs_open(lbh->lzh, path, ZFS_TYPE_FILESYSTEM); 42313c62c50SKyle Evans if (fs == NULL) 42413c62c50SKyle Evans return (set_error(lbh, BE_ERR_ZFSOPEN)); 425be7dd423SKyle Evans 426*8f5c6c31SKyle Evans /* Don't destroy a mounted dataset unless force is specified */ 427*8f5c6c31SKyle Evans if ((mounted = zfs_is_mounted(fs, NULL)) != 0) { 428*8f5c6c31SKyle Evans if (force) { 429*8f5c6c31SKyle Evans zfs_unmount(fs, NULL, 0); 430*8f5c6c31SKyle Evans } else { 431*8f5c6c31SKyle Evans free(bdd.snapname); 432*8f5c6c31SKyle Evans return (set_error(lbh, BE_ERR_DESTROYMNT)); 433*8f5c6c31SKyle Evans } 434*8f5c6c31SKyle Evans } 435*8f5c6c31SKyle Evans } else { 436*8f5c6c31SKyle Evans /* 437*8f5c6c31SKyle Evans * If we're initially destroying a snapshot, origin options do 438*8f5c6c31SKyle Evans * not make sense. If we're destroying the origin snapshot of 439*8f5c6c31SKyle Evans * a BE, we want to maintain the options in case we need to 440*8f5c6c31SKyle Evans * fake success after failing to promote. 441*8f5c6c31SKyle Evans */ 442*8f5c6c31SKyle Evans if (!odestroyer) 443*8f5c6c31SKyle Evans options &= ~BE_DESTROY_WANTORIGIN; 444*8f5c6c31SKyle Evans if (!zfs_dataset_exists(lbh->lzh, path, ZFS_TYPE_SNAPSHOT)) 445*8f5c6c31SKyle Evans return (set_error(lbh, BE_ERR_NOENT)); 446*8f5c6c31SKyle Evans 447*8f5c6c31SKyle Evans bdd.snapname = strdup(snapdelim + 1); 448*8f5c6c31SKyle Evans if (bdd.snapname == NULL) 449*8f5c6c31SKyle Evans return (set_error(lbh, BE_ERR_NOMEM)); 450*8f5c6c31SKyle Evans *snapdelim = '\0'; 451*8f5c6c31SKyle Evans fs = zfs_open(lbh->lzh, path, ZFS_TYPE_DATASET); 452*8f5c6c31SKyle Evans if (fs == NULL) { 453*8f5c6c31SKyle Evans free(bdd.snapname); 454*8f5c6c31SKyle Evans return (set_error(lbh, BE_ERR_ZFSOPEN)); 455*8f5c6c31SKyle Evans } 456*8f5c6c31SKyle Evans } 457*8f5c6c31SKyle Evans 458*8f5c6c31SKyle Evans /* 459*8f5c6c31SKyle Evans * Whether we're destroying a BE or a single snapshot, we need to walk 460*8f5c6c31SKyle Evans * the tree of what we're going to destroy and promote everything in our 461*8f5c6c31SKyle Evans * path so that we can make it happen. 462*8f5c6c31SKyle Evans */ 463*8f5c6c31SKyle Evans if ((err = be_promote_dependent_clones(fs, &bdd)) != 0) { 464*8f5c6c31SKyle Evans free(bdd.snapname); 465*8f5c6c31SKyle Evans 466*8f5c6c31SKyle Evans /* 467*8f5c6c31SKyle Evans * If we're just destroying the origin of some other dataset 468*8f5c6c31SKyle Evans * we were invoked to destroy, then we just ignore 469*8f5c6c31SKyle Evans * BE_ERR_HASCLONES and return success unless the caller wanted 470*8f5c6c31SKyle Evans * to force the issue. 471*8f5c6c31SKyle Evans */ 472*8f5c6c31SKyle Evans if (odestroyer && err == BE_ERR_HASCLONES && 473*8f5c6c31SKyle Evans (options & BE_DESTROY_AUTOORIGIN) != 0) 474*8f5c6c31SKyle Evans return (0); 475*8f5c6c31SKyle Evans return (set_error(lbh, err)); 476*8f5c6c31SKyle Evans } 477*8f5c6c31SKyle Evans 478*8f5c6c31SKyle Evans /* 479*8f5c6c31SKyle Evans * This was deferred until after we promote all of the derivatives so 480*8f5c6c31SKyle Evans * that we grab the new origin after everything's settled down. 481*8f5c6c31SKyle Evans */ 4821dc85563SKyle Evans if ((options & BE_DESTROY_WANTORIGIN) != 0 && 48313c62c50SKyle Evans zfs_prop_get(fs, ZFS_PROP_ORIGIN, origin, sizeof(origin), 4841dc85563SKyle Evans NULL, NULL, 0, 1) != 0 && 4851dc85563SKyle Evans (options & BE_DESTROY_ORIGIN) != 0) 48613c62c50SKyle Evans return (set_error(lbh, BE_ERR_NOORIGIN)); 487e1ee6230SKyle Evans 488455d8009SKyle Evans /* 489455d8009SKyle Evans * If the caller wants auto-origin destruction and the origin 490455d8009SKyle Evans * name matches one of our automatically created snapshot names 491455d8009SKyle Evans * (i.e. strftime("%F-%T") with a serial at the end), then 492455d8009SKyle Evans * we'll set the DESTROY_ORIGIN flag and nuke it 493455d8009SKyle Evans * be_is_auto_snapshot_name is exported from libbe(3) so that 494455d8009SKyle Evans * the caller can determine if it needs to warn about the origin 495455d8009SKyle Evans * not being destroyed or not. 496455d8009SKyle Evans */ 4971dc85563SKyle Evans if ((options & BE_DESTROY_AUTOORIGIN) != 0 && *origin != '\0' && 498455d8009SKyle Evans be_is_auto_snapshot_name(lbh, origin)) 499455d8009SKyle Evans options |= BE_DESTROY_ORIGIN; 500455d8009SKyle Evans 501be7dd423SKyle Evans err = be_destroy_cb(fs, &bdd); 502be7dd423SKyle Evans zfs_close(fs); 503be7dd423SKyle Evans free(bdd.snapname); 504be7dd423SKyle Evans if (err != 0) { 505920abf4dSKyle Evans /* Children are still present or the mount is referenced */ 506920abf4dSKyle Evans if (err == EBUSY) 507920abf4dSKyle Evans return (set_error(lbh, BE_ERR_DESTROYMNT)); 508920abf4dSKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 509920abf4dSKyle Evans } 51028f16a0fSKyle Evans 511be7dd423SKyle Evans if ((options & BE_DESTROY_ORIGIN) == 0) 512920abf4dSKyle Evans return (0); 51328f16a0fSKyle Evans 514be7dd423SKyle Evans /* The origin can't possibly be shorter than the BE root */ 515be7dd423SKyle Evans rootlen = strlen(lbh->root); 516be7dd423SKyle Evans if (*origin == '\0' || strlen(origin) <= rootlen + 1) 517be7dd423SKyle Evans return (set_error(lbh, BE_ERR_INVORIGIN)); 518be7dd423SKyle Evans 519be7dd423SKyle Evans /* 520be7dd423SKyle Evans * We'll be chopping off the BE root and running this back through 521be7dd423SKyle Evans * be_destroy, so that we properly handle the origin snapshot whether 522be7dd423SKyle Evans * it be that of a deep BE or not. 523be7dd423SKyle Evans */ 524be7dd423SKyle Evans if (strncmp(origin, lbh->root, rootlen) != 0 || origin[rootlen] != '/') 525be7dd423SKyle Evans return (0); 526be7dd423SKyle Evans 527*8f5c6c31SKyle Evans return (be_destroy_internal(lbh, origin + rootlen + 1, 528*8f5c6c31SKyle Evans options & ~BE_DESTROY_ORIGIN, true)); 529*8f5c6c31SKyle Evans } 530*8f5c6c31SKyle Evans 531*8f5c6c31SKyle Evans int 532*8f5c6c31SKyle Evans be_destroy(libbe_handle_t *lbh, const char *name, int options) 533*8f5c6c31SKyle Evans { 534*8f5c6c31SKyle Evans 535*8f5c6c31SKyle Evans /* 536*8f5c6c31SKyle Evans * The consumer must not set both BE_DESTROY_AUTOORIGIN and 537*8f5c6c31SKyle Evans * BE_DESTROY_ORIGIN. Internally, we'll set the latter from the former. 538*8f5c6c31SKyle Evans * The latter should imply that we must succeed at destroying the 539*8f5c6c31SKyle Evans * origin, or complain otherwise. 540*8f5c6c31SKyle Evans */ 541*8f5c6c31SKyle Evans if ((options & BE_DESTROY_WANTORIGIN) == BE_DESTROY_WANTORIGIN) 542*8f5c6c31SKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 543*8f5c6c31SKyle Evans return (be_destroy_internal(lbh, name, options, false)); 544be7dd423SKyle Evans } 54528f16a0fSKyle Evans 54690cf61e8SKyle Evans static void 54790cf61e8SKyle Evans be_setup_snapshot_name(libbe_handle_t *lbh, char *buf, size_t buflen) 54890cf61e8SKyle Evans { 54990cf61e8SKyle Evans time_t rawtime; 55090cf61e8SKyle Evans int len, serial; 55190cf61e8SKyle Evans 55290cf61e8SKyle Evans time(&rawtime); 55390cf61e8SKyle Evans len = strlen(buf); 55490cf61e8SKyle Evans len += strftime(buf + len, buflen - len, "@%F-%T", localtime(&rawtime)); 55590cf61e8SKyle Evans /* No room for serial... caller will do its best */ 55690cf61e8SKyle Evans if (buflen - len < 2) 55790cf61e8SKyle Evans return; 55890cf61e8SKyle Evans 55990cf61e8SKyle Evans for (serial = 0; serial < BE_SNAP_SERIAL_MAX; ++serial) { 56090cf61e8SKyle Evans snprintf(buf + len, buflen - len, "-%d", serial); 56190cf61e8SKyle Evans if (!zfs_dataset_exists(lbh->lzh, buf, ZFS_TYPE_SNAPSHOT)) 56290cf61e8SKyle Evans return; 56390cf61e8SKyle Evans } 56490cf61e8SKyle Evans } 56590cf61e8SKyle Evans 566455d8009SKyle Evans bool 567455d8009SKyle Evans be_is_auto_snapshot_name(libbe_handle_t *lbh, const char *name) 568455d8009SKyle Evans { 569455d8009SKyle Evans const char *snap; 570455d8009SKyle Evans int day, hour, minute, month, second, serial, year; 571455d8009SKyle Evans 572455d8009SKyle Evans if ((snap = strchr(name, '@')) == NULL) 573455d8009SKyle Evans return (false); 574455d8009SKyle Evans ++snap; 575455d8009SKyle Evans /* We'll grab the individual components and do some light validation. */ 576455d8009SKyle Evans if (sscanf(snap, "%d-%d-%d-%d:%d:%d-%d", &year, &month, &day, &hour, 577455d8009SKyle Evans &minute, &second, &serial) != 7) 578455d8009SKyle Evans return (false); 579455d8009SKyle Evans return (year >= 1970) && (month >= 1 && month <= 12) && 580455d8009SKyle Evans (day >= 1 && day <= 31) && (hour >= 0 && hour <= 23) && 581455d8009SKyle Evans (minute >= 0 && minute <= 59) && (second >= 0 && second <= 60) && 582455d8009SKyle Evans serial >= 0; 583455d8009SKyle Evans } 584455d8009SKyle Evans 58528f16a0fSKyle Evans int 586b29bf2f8SKyle Evans be_snapshot(libbe_handle_t *lbh, const char *source, const char *snap_name, 58728f16a0fSKyle Evans bool recursive, char *result) 58828f16a0fSKyle Evans { 58928f16a0fSKyle Evans char buf[BE_MAXPATHLEN]; 59090cf61e8SKyle Evans int err; 59128f16a0fSKyle Evans 59228f16a0fSKyle Evans be_root_concat(lbh, source, buf); 59328f16a0fSKyle Evans 594162ec569SKyle Evans if ((err = be_exists(lbh, buf)) != 0) 595162ec569SKyle Evans return (set_error(lbh, err)); 59628f16a0fSKyle Evans 59728f16a0fSKyle Evans if (snap_name != NULL) { 598a8e44f4dSKyle Evans if (strlcat(buf, "@", sizeof(buf)) >= sizeof(buf)) 599a8e44f4dSKyle Evans return (set_error(lbh, BE_ERR_INVALIDNAME)); 600a8e44f4dSKyle Evans 601a8e44f4dSKyle Evans if (strlcat(buf, snap_name, sizeof(buf)) >= sizeof(buf)) 602a8e44f4dSKyle Evans return (set_error(lbh, BE_ERR_INVALIDNAME)); 603a8e44f4dSKyle Evans 604bfe0869cSKyle Evans if (result != NULL) 60528f16a0fSKyle Evans snprintf(result, BE_MAXPATHLEN, "%s@%s", source, 60628f16a0fSKyle Evans snap_name); 60728f16a0fSKyle Evans } else { 60890cf61e8SKyle Evans be_setup_snapshot_name(lbh, buf, sizeof(buf)); 60990cf61e8SKyle Evans 610a8e44f4dSKyle Evans if (result != NULL && strlcpy(result, strrchr(buf, '/') + 1, 611a8e44f4dSKyle Evans sizeof(buf)) >= sizeof(buf)) 612a8e44f4dSKyle Evans return (set_error(lbh, BE_ERR_INVALIDNAME)); 61328f16a0fSKyle Evans } 614b29bf2f8SKyle Evans if ((err = zfs_snapshot(lbh->lzh, buf, recursive, NULL)) != 0) { 61528f16a0fSKyle Evans switch (err) { 61628f16a0fSKyle Evans case EZFS_INVALIDNAME: 61728f16a0fSKyle Evans return (set_error(lbh, BE_ERR_INVALIDNAME)); 61828f16a0fSKyle Evans 61928f16a0fSKyle Evans default: 6202989df09SKyle Evans /* 6212989df09SKyle Evans * The other errors that zfs_ioc_snapshot might return 6222989df09SKyle Evans * shouldn't happen if we've set things up properly, so 6232989df09SKyle Evans * we'll gloss over them and call it UNKNOWN as it will 6242989df09SKyle Evans * require further triage. 6252989df09SKyle Evans */ 6262989df09SKyle Evans if (errno == ENOTSUP) 6272989df09SKyle Evans return (set_error(lbh, BE_ERR_NOPOOL)); 62828f16a0fSKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 62928f16a0fSKyle Evans } 63028f16a0fSKyle Evans } 63128f16a0fSKyle Evans 63228f16a0fSKyle Evans return (BE_ERR_SUCCESS); 63328f16a0fSKyle Evans } 63428f16a0fSKyle Evans 63528f16a0fSKyle Evans 63628f16a0fSKyle Evans /* 63728f16a0fSKyle Evans * Create the boot environment specified by the name parameter 63828f16a0fSKyle Evans */ 63928f16a0fSKyle Evans int 64073c3d608SKyle Evans be_create(libbe_handle_t *lbh, const char *name) 64128f16a0fSKyle Evans { 64228f16a0fSKyle Evans int err; 64328f16a0fSKyle Evans 644b29bf2f8SKyle Evans err = be_create_from_existing(lbh, name, be_active_path(lbh)); 64528f16a0fSKyle Evans 64628f16a0fSKyle Evans return (set_error(lbh, err)); 64728f16a0fSKyle Evans } 64828f16a0fSKyle Evans 64928f16a0fSKyle Evans static int 65028f16a0fSKyle Evans be_deep_clone_prop(int prop, void *cb) 65128f16a0fSKyle Evans { 65228f16a0fSKyle Evans int err; 653bfe0869cSKyle Evans struct libbe_dccb *dccb; 65428f16a0fSKyle Evans zprop_source_t src; 65528f16a0fSKyle Evans char pval[BE_MAXPATHLEN]; 65628f16a0fSKyle Evans char source[BE_MAXPATHLEN]; 657af43c24dSKyle Evans char *val; 65828f16a0fSKyle Evans 659bfe0869cSKyle Evans dccb = cb; 66028f16a0fSKyle Evans /* Skip some properties we don't want to touch */ 66173c3d608SKyle Evans if (prop == ZFS_PROP_CANMOUNT) 66228f16a0fSKyle Evans return (ZPROP_CONT); 66328f16a0fSKyle Evans 66428f16a0fSKyle Evans /* Don't copy readonly properties */ 665bfe0869cSKyle Evans if (zfs_prop_readonly(prop)) 66628f16a0fSKyle Evans return (ZPROP_CONT); 66728f16a0fSKyle Evans 66828f16a0fSKyle Evans if ((err = zfs_prop_get(dccb->zhp, prop, (char *)&pval, 669bfe0869cSKyle Evans sizeof(pval), &src, (char *)&source, sizeof(source), false))) 67028f16a0fSKyle Evans /* Just continue if we fail to read a property */ 67128f16a0fSKyle Evans return (ZPROP_CONT); 672bfe0869cSKyle Evans 673be13d48cSKyle Evans /* 674be13d48cSKyle Evans * Only copy locally defined or received properties. This continues 675be13d48cSKyle Evans * to avoid temporary/default/local properties intentionally without 676be13d48cSKyle Evans * breaking received datasets. 677be13d48cSKyle Evans */ 678be13d48cSKyle Evans if (src != ZPROP_SRC_LOCAL && src != ZPROP_SRC_RECEIVED) 67928f16a0fSKyle Evans return (ZPROP_CONT); 68028f16a0fSKyle Evans 681af43c24dSKyle Evans /* Augment mountpoint with altroot, if needed */ 682af43c24dSKyle Evans val = pval; 683fc13fc1cSKyle Evans if (prop == ZFS_PROP_MOUNTPOINT) 684fc13fc1cSKyle Evans val = be_mountpoint_augmented(dccb->lbh, val); 685fc13fc1cSKyle Evans 686af43c24dSKyle Evans nvlist_add_string(dccb->props, zfs_prop_to_name(prop), val); 68728f16a0fSKyle Evans 68828f16a0fSKyle Evans return (ZPROP_CONT); 68928f16a0fSKyle Evans } 69028f16a0fSKyle Evans 691fa30d9edSKyle Evans /* 692fa30d9edSKyle Evans * Return the corresponding boot environment path for a given 693fa30d9edSKyle Evans * dataset path, the constructed path is placed in 'result'. 694fa30d9edSKyle Evans * 695fa30d9edSKyle Evans * example: say our new boot environment name is 'bootenv' and 696fa30d9edSKyle Evans * the dataset path is 'zroot/ROOT/default/data/set'. 697fa30d9edSKyle Evans * 698fa30d9edSKyle Evans * result should produce: 'zroot/ROOT/bootenv/data/set' 699fa30d9edSKyle Evans */ 70028f16a0fSKyle Evans static int 701fa30d9edSKyle Evans be_get_path(struct libbe_deep_clone *ldc, const char *dspath, char *result, int result_size) 702fa30d9edSKyle Evans { 703fa30d9edSKyle Evans char *pos; 704fa30d9edSKyle Evans char *child_dataset; 705fa30d9edSKyle Evans 706fa30d9edSKyle Evans /* match the root path for the boot environments */ 707fa30d9edSKyle Evans pos = strstr(dspath, ldc->lbh->root); 708fa30d9edSKyle Evans 709fa30d9edSKyle Evans /* no match, different pools? */ 710fa30d9edSKyle Evans if (pos == NULL) 711fa30d9edSKyle Evans return (BE_ERR_BADPATH); 712fa30d9edSKyle Evans 713fa30d9edSKyle Evans /* root path of the new boot environment */ 714fa30d9edSKyle Evans snprintf(result, result_size, "%s/%s", ldc->lbh->root, ldc->bename); 715fa30d9edSKyle Evans 716fa30d9edSKyle Evans /* gets us to the parent dataset, the +1 consumes a trailing slash */ 717fa30d9edSKyle Evans pos += strlen(ldc->lbh->root) + 1; 718fa30d9edSKyle Evans 719fa30d9edSKyle Evans /* skip the parent dataset */ 720fa30d9edSKyle Evans if ((child_dataset = strchr(pos, '/')) != NULL) 721fa30d9edSKyle Evans strlcat(result, child_dataset, result_size); 722fa30d9edSKyle Evans 723fa30d9edSKyle Evans return (BE_ERR_SUCCESS); 724fa30d9edSKyle Evans } 725fa30d9edSKyle Evans 726fa30d9edSKyle Evans static int 727fa30d9edSKyle Evans be_clone_cb(zfs_handle_t *ds, void *data) 72828f16a0fSKyle Evans { 72928f16a0fSKyle Evans int err; 73028f16a0fSKyle Evans char be_path[BE_MAXPATHLEN]; 73128f16a0fSKyle Evans char snap_path[BE_MAXPATHLEN]; 73228f16a0fSKyle Evans const char *dspath; 73328f16a0fSKyle Evans zfs_handle_t *snap_hdl; 73428f16a0fSKyle Evans nvlist_t *props; 735fa30d9edSKyle Evans struct libbe_deep_clone *ldc; 73628f16a0fSKyle Evans struct libbe_dccb dccb; 73728f16a0fSKyle Evans 738fa30d9edSKyle Evans ldc = (struct libbe_deep_clone *)data; 73928f16a0fSKyle Evans dspath = zfs_get_name(ds); 740bfe0869cSKyle Evans 741fa30d9edSKyle Evans snprintf(snap_path, sizeof(snap_path), "%s@%s", dspath, ldc->snapname); 742bfe0869cSKyle Evans 743fa30d9edSKyle Evans /* construct the boot environment path from the dataset we're cloning */ 744fa30d9edSKyle Evans if (be_get_path(ldc, dspath, be_path, sizeof(be_path)) != BE_ERR_SUCCESS) 745fa30d9edSKyle Evans return (set_error(ldc->lbh, BE_ERR_UNKNOWN)); 74628f16a0fSKyle Evans 747fa30d9edSKyle Evans /* the dataset to be created (i.e. the boot environment) already exists */ 748fa30d9edSKyle Evans if (zfs_dataset_exists(ldc->lbh->lzh, be_path, ZFS_TYPE_DATASET)) 749fa30d9edSKyle Evans return (set_error(ldc->lbh, BE_ERR_EXISTS)); 750fa30d9edSKyle Evans 751fa30d9edSKyle Evans /* no snapshot found for this dataset, silently skip it */ 752fa30d9edSKyle Evans if (!zfs_dataset_exists(ldc->lbh->lzh, snap_path, ZFS_TYPE_SNAPSHOT)) 753fa30d9edSKyle Evans return (0); 75428f16a0fSKyle Evans 75528f16a0fSKyle Evans if ((snap_hdl = 756fa30d9edSKyle Evans zfs_open(ldc->lbh->lzh, snap_path, ZFS_TYPE_SNAPSHOT)) == NULL) 757fa30d9edSKyle Evans return (set_error(ldc->lbh, BE_ERR_ZFSOPEN)); 75828f16a0fSKyle Evans 75928f16a0fSKyle Evans nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP); 76028f16a0fSKyle Evans nvlist_add_string(props, "canmount", "noauto"); 76128f16a0fSKyle Evans 762fa30d9edSKyle Evans dccb.lbh = ldc->lbh; 76328f16a0fSKyle Evans dccb.zhp = ds; 76428f16a0fSKyle Evans dccb.props = props; 76528f16a0fSKyle Evans if (zprop_iter(be_deep_clone_prop, &dccb, B_FALSE, B_FALSE, 766bfe0869cSKyle Evans ZFS_TYPE_FILESYSTEM) == ZPROP_INVAL) 76728f16a0fSKyle Evans return (-1); 76828f16a0fSKyle Evans 769cc4deabcSKyle Evans if ((err = zfs_clone(snap_hdl, be_path, props)) != 0) 770fa30d9edSKyle Evans return (set_error(ldc->lbh, BE_ERR_ZFSCLONE)); 77128f16a0fSKyle Evans 77228f16a0fSKyle Evans nvlist_free(props); 77328f16a0fSKyle Evans zfs_close(snap_hdl); 77428f16a0fSKyle Evans 775fa30d9edSKyle Evans if (ldc->depth_limit == -1 || ldc->depth < ldc->depth_limit) { 776fa30d9edSKyle Evans ldc->depth++; 777fa30d9edSKyle Evans err = zfs_iter_filesystems(ds, be_clone_cb, ldc); 778fa30d9edSKyle Evans ldc->depth--; 779fa30d9edSKyle Evans } 780cc4deabcSKyle Evans 781fa30d9edSKyle Evans return (set_error(ldc->lbh, err)); 78228f16a0fSKyle Evans } 78328f16a0fSKyle Evans 78428f16a0fSKyle Evans /* 785fa30d9edSKyle Evans * Create a boot environment with a given name from a given snapshot. 786fa30d9edSKyle Evans * Snapshots can be in the format 'zroot/ROOT/default@snapshot' or 787fa30d9edSKyle Evans * 'default@snapshot'. In the latter case, 'default@snapshot' will be prepended 788fa30d9edSKyle Evans * with the root path that libbe was initailized with. 78928f16a0fSKyle Evans */ 790fa30d9edSKyle Evans static int 791fa30d9edSKyle Evans be_clone(libbe_handle_t *lbh, const char *bename, const char *snapshot, int depth) 79228f16a0fSKyle Evans { 79328f16a0fSKyle Evans int err; 79428f16a0fSKyle Evans char snap_path[BE_MAXPATHLEN]; 795b29bf2f8SKyle Evans char *parentname, *snapname; 79628f16a0fSKyle Evans zfs_handle_t *parent_hdl; 797fa30d9edSKyle Evans struct libbe_deep_clone ldc; 79828f16a0fSKyle Evans 799fa30d9edSKyle Evans /* ensure the boot environment name is valid */ 800fa30d9edSKyle Evans if ((err = be_validate_name(lbh, bename)) != 0) 80128f16a0fSKyle Evans return (set_error(lbh, err)); 802fa30d9edSKyle Evans 803fa30d9edSKyle Evans /* 804fa30d9edSKyle Evans * prepend the boot environment root path if we're 805fa30d9edSKyle Evans * given a partial snapshot name. 806fa30d9edSKyle Evans */ 807fa30d9edSKyle Evans if ((err = be_root_concat(lbh, snapshot, snap_path)) != 0) 80828f16a0fSKyle Evans return (set_error(lbh, err)); 809fa30d9edSKyle Evans 810fa30d9edSKyle Evans /* ensure the snapshot exists */ 811b29bf2f8SKyle Evans if ((err = be_validate_snap(lbh, snap_path)) != 0) 81228f16a0fSKyle Evans return (set_error(lbh, err)); 81328f16a0fSKyle Evans 814fa30d9edSKyle Evans /* get a copy of the snapshot path so we can disect it */ 815cc4deabcSKyle Evans if ((parentname = strdup(snap_path)) == NULL) 816cc4deabcSKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 817cc4deabcSKyle Evans 818fa30d9edSKyle Evans /* split dataset name from snapshot name */ 81928f16a0fSKyle Evans snapname = strchr(parentname, '@'); 82028f16a0fSKyle Evans if (snapname == NULL) { 821cc4deabcSKyle Evans free(parentname); 822cc4deabcSKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 82328f16a0fSKyle Evans } 82428f16a0fSKyle Evans *snapname = '\0'; 82528f16a0fSKyle Evans snapname++; 82628f16a0fSKyle Evans 827fa30d9edSKyle Evans /* set-up the boot environment */ 828fa30d9edSKyle Evans ldc.lbh = lbh; 829fa30d9edSKyle Evans ldc.bename = bename; 830fa30d9edSKyle Evans ldc.snapname = snapname; 831fa30d9edSKyle Evans ldc.depth = 0; 832fa30d9edSKyle Evans ldc.depth_limit = depth; 83328f16a0fSKyle Evans 834fa30d9edSKyle Evans /* the boot environment will be cloned from this dataset */ 83528f16a0fSKyle Evans parent_hdl = zfs_open(lbh->lzh, parentname, ZFS_TYPE_DATASET); 836fa30d9edSKyle Evans 837fa30d9edSKyle Evans /* create the boot environment */ 838fa30d9edSKyle Evans err = be_clone_cb(parent_hdl, &ldc); 83928f16a0fSKyle Evans 840cc4deabcSKyle Evans free(parentname); 84128f16a0fSKyle Evans return (set_error(lbh, err)); 84228f16a0fSKyle Evans } 84328f16a0fSKyle Evans 844fa30d9edSKyle Evans /* 845fa30d9edSKyle Evans * Create a boot environment from pre-existing snapshot, specifying a depth. 846fa30d9edSKyle Evans */ 847fa30d9edSKyle Evans int be_create_depth(libbe_handle_t *lbh, const char *bename, 848fa30d9edSKyle Evans const char *snap, int depth) 849fa30d9edSKyle Evans { 850fa30d9edSKyle Evans return (be_clone(lbh, bename, snap, depth)); 851fa30d9edSKyle Evans } 852fa30d9edSKyle Evans 853fa30d9edSKyle Evans /* 854fa30d9edSKyle Evans * Create the boot environment from pre-existing snapshot 855fa30d9edSKyle Evans */ 856fa30d9edSKyle Evans int 857fa30d9edSKyle Evans be_create_from_existing_snap(libbe_handle_t *lbh, const char *bename, 858fa30d9edSKyle Evans const char *snap) 859fa30d9edSKyle Evans { 860fa30d9edSKyle Evans return (be_clone(lbh, bename, snap, -1)); 861fa30d9edSKyle Evans } 862fa30d9edSKyle Evans 86328f16a0fSKyle Evans 86428f16a0fSKyle Evans /* 86528f16a0fSKyle Evans * Create a boot environment from an existing boot environment 86628f16a0fSKyle Evans */ 86728f16a0fSKyle Evans int 868fa30d9edSKyle Evans be_create_from_existing(libbe_handle_t *lbh, const char *bename, const char *old) 86928f16a0fSKyle Evans { 87028f16a0fSKyle Evans int err; 871fa30d9edSKyle Evans char snap[BE_MAXPATHLEN]; 87228f16a0fSKyle Evans 873fa30d9edSKyle Evans if ((err = be_snapshot(lbh, old, NULL, true, snap)) != 0) 87428f16a0fSKyle Evans return (set_error(lbh, err)); 87528f16a0fSKyle Evans 876fa30d9edSKyle Evans err = be_clone(lbh, bename, snap, -1); 87728f16a0fSKyle Evans 87828f16a0fSKyle Evans return (set_error(lbh, err)); 87928f16a0fSKyle Evans } 88028f16a0fSKyle Evans 88128f16a0fSKyle Evans 88228f16a0fSKyle Evans /* 88328f16a0fSKyle Evans * Verifies that a snapshot has a valid name, exists, and has a mountpoint of 88428f16a0fSKyle Evans * '/'. Returns BE_ERR_SUCCESS (0), upon success, or the relevant BE_ERR_* upon 88528f16a0fSKyle Evans * failure. Does not set the internal library error state. 88628f16a0fSKyle Evans */ 88728f16a0fSKyle Evans int 888b29bf2f8SKyle Evans be_validate_snap(libbe_handle_t *lbh, const char *snap_name) 88928f16a0fSKyle Evans { 89028f16a0fSKyle Evans 891bfe0869cSKyle Evans if (strlen(snap_name) >= BE_MAXPATHLEN) 89228f16a0fSKyle Evans return (BE_ERR_PATHLEN); 89328f16a0fSKyle Evans 894fcb47c42SKyle Evans if (!zfs_name_valid(snap_name, ZFS_TYPE_SNAPSHOT)) 895fcb47c42SKyle Evans return (BE_ERR_INVALIDNAME); 896fcb47c42SKyle Evans 89728f16a0fSKyle Evans if (!zfs_dataset_exists(lbh->lzh, snap_name, 898bfe0869cSKyle Evans ZFS_TYPE_SNAPSHOT)) 89928f16a0fSKyle Evans return (BE_ERR_NOENT); 90028f16a0fSKyle Evans 90151aecc89SKyle Evans return (BE_ERR_SUCCESS); 90228f16a0fSKyle Evans } 90328f16a0fSKyle Evans 90428f16a0fSKyle Evans 90528f16a0fSKyle Evans /* 90628f16a0fSKyle Evans * Idempotently appends the name argument to the root boot environment path 90728f16a0fSKyle Evans * and copies the resulting string into the result buffer (which is assumed 90828f16a0fSKyle Evans * to be at least BE_MAXPATHLEN characters long. Returns BE_ERR_SUCCESS upon 90928f16a0fSKyle Evans * success, BE_ERR_PATHLEN if the resulting path is longer than BE_MAXPATHLEN, 91028f16a0fSKyle Evans * or BE_ERR_INVALIDNAME if the name is a path that does not begin with 91128f16a0fSKyle Evans * zfs_be_root. Does not set internal library error state. 91228f16a0fSKyle Evans */ 91328f16a0fSKyle Evans int 914b29bf2f8SKyle Evans be_root_concat(libbe_handle_t *lbh, const char *name, char *result) 91528f16a0fSKyle Evans { 91628f16a0fSKyle Evans size_t name_len, root_len; 91728f16a0fSKyle Evans 91828f16a0fSKyle Evans name_len = strlen(name); 91928f16a0fSKyle Evans root_len = strlen(lbh->root); 92028f16a0fSKyle Evans 92128f16a0fSKyle Evans /* Act idempotently; return be name if it is already a full path */ 92228f16a0fSKyle Evans if (strrchr(name, '/') != NULL) { 923bfe0869cSKyle Evans if (strstr(name, lbh->root) != name) 92428f16a0fSKyle Evans return (BE_ERR_INVALIDNAME); 92528f16a0fSKyle Evans 926bfe0869cSKyle Evans if (name_len >= BE_MAXPATHLEN) 92728f16a0fSKyle Evans return (BE_ERR_PATHLEN); 92828f16a0fSKyle Evans 92955b0e92bSKyle Evans strlcpy(result, name, BE_MAXPATHLEN); 93028f16a0fSKyle Evans return (BE_ERR_SUCCESS); 93128f16a0fSKyle Evans } else if (name_len + root_len + 1 < BE_MAXPATHLEN) { 93228f16a0fSKyle Evans snprintf(result, BE_MAXPATHLEN, "%s/%s", lbh->root, 93328f16a0fSKyle Evans name); 93428f16a0fSKyle Evans return (BE_ERR_SUCCESS); 93528f16a0fSKyle Evans } 93628f16a0fSKyle Evans 93728f16a0fSKyle Evans return (BE_ERR_PATHLEN); 93828f16a0fSKyle Evans } 93928f16a0fSKyle Evans 94028f16a0fSKyle Evans 94128f16a0fSKyle Evans /* 94228f16a0fSKyle Evans * Verifies the validity of a boot environment name (A-Za-z0-9-_.). Returns 9435b7803a9SKyle Evans * BE_ERR_SUCCESS (0) if name is valid, otherwise returns BE_ERR_INVALIDNAME 9445b7803a9SKyle Evans * or BE_ERR_PATHLEN. 94528f16a0fSKyle Evans * Does not set internal library error state. 94628f16a0fSKyle Evans */ 94728f16a0fSKyle Evans int 9485b7803a9SKyle Evans be_validate_name(libbe_handle_t *lbh, const char *name) 94928f16a0fSKyle Evans { 95028f16a0fSKyle Evans 9515b7803a9SKyle Evans /* 9525b7803a9SKyle Evans * Impose the additional restriction that the entire dataset name must 9535b7803a9SKyle Evans * not exceed the maximum length of a dataset, i.e. MAXNAMELEN. 9545b7803a9SKyle Evans */ 9555b7803a9SKyle Evans if (strlen(lbh->root) + 1 + strlen(name) > MAXNAMELEN) 9565b7803a9SKyle Evans return (BE_ERR_PATHLEN); 957fcb47c42SKyle Evans 958fcb47c42SKyle Evans if (!zfs_name_valid(name, ZFS_TYPE_DATASET)) 959fcb47c42SKyle Evans return (BE_ERR_INVALIDNAME); 960fcb47c42SKyle Evans 96128f16a0fSKyle Evans return (BE_ERR_SUCCESS); 96228f16a0fSKyle Evans } 96328f16a0fSKyle Evans 96428f16a0fSKyle Evans 96528f16a0fSKyle Evans /* 96628f16a0fSKyle Evans * usage 96728f16a0fSKyle Evans */ 96828f16a0fSKyle Evans int 96973c3d608SKyle Evans be_rename(libbe_handle_t *lbh, const char *old, const char *new) 97028f16a0fSKyle Evans { 97128f16a0fSKyle Evans char full_old[BE_MAXPATHLEN]; 97228f16a0fSKyle Evans char full_new[BE_MAXPATHLEN]; 97328f16a0fSKyle Evans zfs_handle_t *zfs_hdl; 97428f16a0fSKyle Evans int err; 97528f16a0fSKyle Evans 9765b7803a9SKyle Evans /* 9775b7803a9SKyle Evans * be_validate_name is documented not to set error state, so we should 9785b7803a9SKyle Evans * do so here. 9795b7803a9SKyle Evans */ 9805b7803a9SKyle Evans if ((err = be_validate_name(lbh, new)) != 0) 9815b7803a9SKyle Evans return (set_error(lbh, err)); 982b29bf2f8SKyle Evans if ((err = be_root_concat(lbh, old, full_old)) != 0) 98328f16a0fSKyle Evans return (set_error(lbh, err)); 984b29bf2f8SKyle Evans if ((err = be_root_concat(lbh, new, full_new)) != 0) 98528f16a0fSKyle Evans return (set_error(lbh, err)); 98628f16a0fSKyle Evans 987bfe0869cSKyle Evans if (!zfs_dataset_exists(lbh->lzh, full_old, ZFS_TYPE_DATASET)) 9882989df09SKyle Evans return (set_error(lbh, BE_ERR_NOENT)); 98928f16a0fSKyle Evans 990bfe0869cSKyle Evans if (zfs_dataset_exists(lbh->lzh, full_new, ZFS_TYPE_DATASET)) 9912989df09SKyle Evans return (set_error(lbh, BE_ERR_EXISTS)); 99228f16a0fSKyle Evans 99328f16a0fSKyle Evans if ((zfs_hdl = zfs_open(lbh->lzh, full_old, 994bfe0869cSKyle Evans ZFS_TYPE_FILESYSTEM)) == NULL) 9952989df09SKyle Evans return (set_error(lbh, BE_ERR_ZFSOPEN)); 99628f16a0fSKyle Evans 997bfe0869cSKyle Evans /* recurse, nounmount, forceunmount */ 9988369ba42SKyle Evans struct renameflags flags = { 9998369ba42SKyle Evans .nounmount = 1, 10008369ba42SKyle Evans }; 100128f16a0fSKyle Evans 100228f16a0fSKyle Evans err = zfs_rename(zfs_hdl, NULL, full_new, flags); 100328f16a0fSKyle Evans 100428f16a0fSKyle Evans zfs_close(zfs_hdl); 10055b7803a9SKyle Evans if (err != 0) 10065b7803a9SKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 10075b7803a9SKyle Evans return (0); 100828f16a0fSKyle Evans } 100928f16a0fSKyle Evans 101028f16a0fSKyle Evans 101128f16a0fSKyle Evans int 101273c3d608SKyle Evans be_export(libbe_handle_t *lbh, const char *bootenv, int fd) 101328f16a0fSKyle Evans { 101428f16a0fSKyle Evans char snap_name[BE_MAXPATHLEN]; 101528f16a0fSKyle Evans char buf[BE_MAXPATHLEN]; 101628f16a0fSKyle Evans zfs_handle_t *zfs; 10178569a95eSAndriy Gapon sendflags_t flags = { 0 }; 101828f16a0fSKyle Evans int err; 101928f16a0fSKyle Evans 1020b29bf2f8SKyle Evans if ((err = be_snapshot(lbh, bootenv, NULL, true, snap_name)) != 0) 10216d4b1d24SKyle Evans /* Use the error set by be_snapshot */ 10226d4b1d24SKyle Evans return (err); 102328f16a0fSKyle Evans 102428f16a0fSKyle Evans be_root_concat(lbh, snap_name, buf); 102528f16a0fSKyle Evans 1026bfe0869cSKyle Evans if ((zfs = zfs_open(lbh->lzh, buf, ZFS_TYPE_DATASET)) == NULL) 1027506f5fdfSKyle Evans return (set_error(lbh, BE_ERR_ZFSOPEN)); 102828f16a0fSKyle Evans 10298569a95eSAndriy Gapon err = zfs_send_one(zfs, NULL, fd, flags); 10306d4b1d24SKyle Evans zfs_close(zfs); 10316d4b1d24SKyle Evans 103228f16a0fSKyle Evans return (err); 103328f16a0fSKyle Evans } 103428f16a0fSKyle Evans 103528f16a0fSKyle Evans 103628f16a0fSKyle Evans int 103773c3d608SKyle Evans be_import(libbe_handle_t *lbh, const char *bootenv, int fd) 103828f16a0fSKyle Evans { 103928f16a0fSKyle Evans char buf[BE_MAXPATHLEN]; 104028f16a0fSKyle Evans nvlist_t *props; 104128f16a0fSKyle Evans zfs_handle_t *zfs; 104216ac0705SKyle Evans recvflags_t flags = { .nomount = 1 }; 104316ac0705SKyle Evans int err; 104428f16a0fSKyle Evans 104516ac0705SKyle Evans be_root_concat(lbh, bootenv, buf); 104628f16a0fSKyle Evans 104716ac0705SKyle Evans if ((err = zfs_receive(lbh->lzh, buf, NULL, &flags, fd, NULL)) != 0) { 1048506f5fdfSKyle Evans switch (err) { 1049506f5fdfSKyle Evans case EINVAL: 1050506f5fdfSKyle Evans return (set_error(lbh, BE_ERR_NOORIGIN)); 1051506f5fdfSKyle Evans case ENOENT: 1052506f5fdfSKyle Evans return (set_error(lbh, BE_ERR_NOENT)); 1053506f5fdfSKyle Evans case EIO: 1054506f5fdfSKyle Evans return (set_error(lbh, BE_ERR_IO)); 1055506f5fdfSKyle Evans default: 1056506f5fdfSKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 1057506f5fdfSKyle Evans } 105828f16a0fSKyle Evans } 105928f16a0fSKyle Evans 106016ac0705SKyle Evans if ((zfs = zfs_open(lbh->lzh, buf, ZFS_TYPE_FILESYSTEM)) == NULL) 1061506f5fdfSKyle Evans return (set_error(lbh, BE_ERR_ZFSOPEN)); 106228f16a0fSKyle Evans 106328f16a0fSKyle Evans nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP); 106428f16a0fSKyle Evans nvlist_add_string(props, "canmount", "noauto"); 1065ac34fe23SKyle Evans nvlist_add_string(props, "mountpoint", "none"); 106628f16a0fSKyle Evans 106716ac0705SKyle Evans err = zfs_prop_set_list(zfs, props); 106828f16a0fSKyle Evans nvlist_free(props); 106928f16a0fSKyle Evans 10701b057aacSKyle Evans zfs_close(zfs); 10711b057aacSKyle Evans 10721b057aacSKyle Evans if (err != 0) 10731b057aacSKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 10741b057aacSKyle Evans 107516ac0705SKyle Evans return (0); 107628f16a0fSKyle Evans } 107728f16a0fSKyle Evans 10783d1a1f2cSKyle Evans #if SOON 1079c65a2111SKyle Evans static int 1080c65a2111SKyle Evans be_create_child_noent(libbe_handle_t *lbh, const char *active, 1081c65a2111SKyle Evans const char *child_path) 1082c65a2111SKyle Evans { 1083c65a2111SKyle Evans nvlist_t *props; 1084c65a2111SKyle Evans zfs_handle_t *zfs; 1085c65a2111SKyle Evans int err; 1086c65a2111SKyle Evans 1087c65a2111SKyle Evans nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP); 1088c65a2111SKyle Evans nvlist_add_string(props, "canmount", "noauto"); 1089c65a2111SKyle Evans nvlist_add_string(props, "mountpoint", child_path); 1090c65a2111SKyle Evans 1091c65a2111SKyle Evans /* Create */ 1092c65a2111SKyle Evans if ((err = zfs_create(lbh->lzh, active, ZFS_TYPE_DATASET, 1093c65a2111SKyle Evans props)) != 0) { 1094c65a2111SKyle Evans switch (err) { 1095c65a2111SKyle Evans case EZFS_EXISTS: 1096c65a2111SKyle Evans return (set_error(lbh, BE_ERR_EXISTS)); 1097c65a2111SKyle Evans case EZFS_NOENT: 1098c65a2111SKyle Evans return (set_error(lbh, BE_ERR_NOENT)); 1099c65a2111SKyle Evans case EZFS_BADTYPE: 1100c65a2111SKyle Evans case EZFS_BADVERSION: 1101c65a2111SKyle Evans return (set_error(lbh, BE_ERR_NOPOOL)); 1102c65a2111SKyle Evans case EZFS_BADPROP: 1103c65a2111SKyle Evans default: 1104c65a2111SKyle Evans /* We set something up wrong, probably... */ 1105c65a2111SKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 1106c65a2111SKyle Evans } 1107c65a2111SKyle Evans } 1108c65a2111SKyle Evans nvlist_free(props); 1109c65a2111SKyle Evans 1110c65a2111SKyle Evans if ((zfs = zfs_open(lbh->lzh, active, ZFS_TYPE_DATASET)) == NULL) 1111c65a2111SKyle Evans return (set_error(lbh, BE_ERR_ZFSOPEN)); 1112c65a2111SKyle Evans 1113c65a2111SKyle Evans /* Set props */ 1114c65a2111SKyle Evans if ((err = zfs_prop_set(zfs, "canmount", "noauto")) != 0) { 1115c65a2111SKyle Evans zfs_close(zfs); 1116c65a2111SKyle Evans /* 1117c65a2111SKyle Evans * Similar to other cases, this shouldn't fail unless we've 1118c65a2111SKyle Evans * done something wrong. This is a new dataset that shouldn't 1119c65a2111SKyle Evans * have been mounted anywhere between creation and now. 1120c65a2111SKyle Evans */ 1121c65a2111SKyle Evans if (err == EZFS_NOMEM) 1122c65a2111SKyle Evans return (set_error(lbh, BE_ERR_NOMEM)); 1123c65a2111SKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 1124c65a2111SKyle Evans } 1125c65a2111SKyle Evans zfs_close(zfs); 1126c65a2111SKyle Evans return (BE_ERR_SUCCESS); 1127c65a2111SKyle Evans } 1128c65a2111SKyle Evans 1129c65a2111SKyle Evans static int 1130c65a2111SKyle Evans be_create_child_cloned(libbe_handle_t *lbh, const char *active) 1131c65a2111SKyle Evans { 11323d1a1f2cSKyle Evans char buf[BE_MAXPATHLEN], tmp[BE_MAXPATHLEN];; 1133c65a2111SKyle Evans zfs_handle_t *zfs; 1134c65a2111SKyle Evans int err; 1135c65a2111SKyle Evans 1136c65a2111SKyle Evans /* XXX TODO ? */ 1137c65a2111SKyle Evans 1138c65a2111SKyle Evans /* 1139c65a2111SKyle Evans * Establish if the existing path is a zfs dataset or just 1140c65a2111SKyle Evans * the subdirectory of one 1141c65a2111SKyle Evans */ 11423d1a1f2cSKyle Evans strlcpy(tmp, "tmp/be_snap.XXXXX", sizeof(tmp)); 11433d1a1f2cSKyle Evans if (mktemp(tmp) == NULL) 1144c65a2111SKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 1145c65a2111SKyle Evans 11463d1a1f2cSKyle Evans be_root_concat(lbh, tmp, buf); 11473d1a1f2cSKyle Evans printf("Here %s?\n", buf); 1148c65a2111SKyle Evans if ((err = zfs_snapshot(lbh->lzh, buf, false, NULL)) != 0) { 1149c65a2111SKyle Evans switch (err) { 1150c65a2111SKyle Evans case EZFS_INVALIDNAME: 1151c65a2111SKyle Evans return (set_error(lbh, BE_ERR_INVALIDNAME)); 1152c65a2111SKyle Evans 1153c65a2111SKyle Evans default: 1154c65a2111SKyle Evans /* 1155c65a2111SKyle Evans * The other errors that zfs_ioc_snapshot might return 1156c65a2111SKyle Evans * shouldn't happen if we've set things up properly, so 1157c65a2111SKyle Evans * we'll gloss over them and call it UNKNOWN as it will 1158c65a2111SKyle Evans * require further triage. 1159c65a2111SKyle Evans */ 1160c65a2111SKyle Evans if (errno == ENOTSUP) 1161c65a2111SKyle Evans return (set_error(lbh, BE_ERR_NOPOOL)); 1162c65a2111SKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 1163c65a2111SKyle Evans } 1164c65a2111SKyle Evans } 1165c65a2111SKyle Evans 1166c65a2111SKyle Evans /* Clone */ 1167c65a2111SKyle Evans if ((zfs = zfs_open(lbh->lzh, buf, ZFS_TYPE_SNAPSHOT)) == NULL) 1168c65a2111SKyle Evans return (BE_ERR_ZFSOPEN); 1169c65a2111SKyle Evans 1170c65a2111SKyle Evans if ((err = zfs_clone(zfs, active, NULL)) != 0) 1171c65a2111SKyle Evans /* XXX TODO correct error */ 1172c65a2111SKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 1173c65a2111SKyle Evans 1174c65a2111SKyle Evans /* set props */ 1175c65a2111SKyle Evans zfs_close(zfs); 1176c65a2111SKyle Evans return (BE_ERR_SUCCESS); 1177c65a2111SKyle Evans } 117828f16a0fSKyle Evans 117928f16a0fSKyle Evans int 118073c3d608SKyle Evans be_add_child(libbe_handle_t *lbh, const char *child_path, bool cp_if_exists) 118128f16a0fSKyle Evans { 118273c3d608SKyle Evans struct stat sb; 1183c65a2111SKyle Evans char active[BE_MAXPATHLEN], buf[BE_MAXPATHLEN]; 118428f16a0fSKyle Evans nvlist_t *props; 118573c3d608SKyle Evans const char *s; 118628f16a0fSKyle Evans 118728f16a0fSKyle Evans /* Require absolute paths */ 1188bfe0869cSKyle Evans if (*child_path != '/') 11896d4b1d24SKyle Evans return (set_error(lbh, BE_ERR_BADPATH)); 119028f16a0fSKyle Evans 11916d4b1d24SKyle Evans strlcpy(active, be_active_path(lbh), BE_MAXPATHLEN); 119228f16a0fSKyle Evans strcpy(buf, active); 119328f16a0fSKyle Evans 119428f16a0fSKyle Evans /* Create non-mountable parent dataset(s) */ 119573c3d608SKyle Evans s = child_path; 119628f16a0fSKyle Evans for (char *p; (p = strchr(s+1, '/')) != NULL; s = p) { 119728f16a0fSKyle Evans size_t len = p - s; 119828f16a0fSKyle Evans strncat(buf, s, len); 119928f16a0fSKyle Evans 120028f16a0fSKyle Evans nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP); 120128f16a0fSKyle Evans nvlist_add_string(props, "canmount", "off"); 120228f16a0fSKyle Evans nvlist_add_string(props, "mountpoint", "none"); 120328f16a0fSKyle Evans zfs_create(lbh->lzh, buf, ZFS_TYPE_DATASET, props); 120428f16a0fSKyle Evans nvlist_free(props); 120528f16a0fSKyle Evans } 120628f16a0fSKyle Evans 120728f16a0fSKyle Evans /* Path does not exist as a descendent of / yet */ 12086d4b1d24SKyle Evans if (strlcat(active, child_path, BE_MAXPATHLEN) >= BE_MAXPATHLEN) 12096d4b1d24SKyle Evans return (set_error(lbh, BE_ERR_PATHLEN)); 121028f16a0fSKyle Evans 121128f16a0fSKyle Evans if (stat(child_path, &sb) != 0) { 121228f16a0fSKyle Evans /* Verify that error is ENOENT */ 12136d4b1d24SKyle Evans if (errno != ENOENT) 12146d4b1d24SKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 1215c65a2111SKyle Evans return (be_create_child_noent(lbh, active, child_path)); 1216c65a2111SKyle Evans } else if (cp_if_exists) 121728f16a0fSKyle Evans /* Path is already a descendent of / and should be copied */ 1218c65a2111SKyle Evans return (be_create_child_cloned(lbh, active)); 12196d4b1d24SKyle Evans return (set_error(lbh, BE_ERR_EXISTS)); 122028f16a0fSKyle Evans } 12213d1a1f2cSKyle Evans #endif /* SOON */ 122228f16a0fSKyle Evans 1223d06f7103SKyle Evans static int 1224d06f7103SKyle Evans be_set_nextboot(libbe_handle_t *lbh, nvlist_t *config, uint64_t pool_guid, 1225d06f7103SKyle Evans const char *zfsdev) 1226d06f7103SKyle Evans { 1227d06f7103SKyle Evans nvlist_t **child; 1228d06f7103SKyle Evans uint64_t vdev_guid; 1229d06f7103SKyle Evans int c, children; 1230d06f7103SKyle Evans 1231d06f7103SKyle Evans if (nvlist_lookup_nvlist_array(config, ZPOOL_CONFIG_CHILDREN, &child, 1232d06f7103SKyle Evans &children) == 0) { 1233d06f7103SKyle Evans for (c = 0; c < children; ++c) 1234d06f7103SKyle Evans if (be_set_nextboot(lbh, child[c], pool_guid, zfsdev) != 0) 1235d06f7103SKyle Evans return (1); 1236d06f7103SKyle Evans return (0); 1237d06f7103SKyle Evans } 1238d06f7103SKyle Evans 1239d06f7103SKyle Evans if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_GUID, 1240d06f7103SKyle Evans &vdev_guid) != 0) { 1241d06f7103SKyle Evans return (1); 1242d06f7103SKyle Evans } 1243d06f7103SKyle Evans 1244d06f7103SKyle Evans if (zpool_nextboot(lbh->lzh, pool_guid, vdev_guid, zfsdev) != 0) { 1245d06f7103SKyle Evans perror("ZFS_IOC_NEXTBOOT failed"); 1246d06f7103SKyle Evans return (1); 1247d06f7103SKyle Evans } 1248d06f7103SKyle Evans 1249d06f7103SKyle Evans return (0); 1250d06f7103SKyle Evans } 1251d06f7103SKyle Evans 12528d4ce358SKyle Evans /* 12538d4ce358SKyle Evans * Deactivate old BE dataset; currently just sets canmount=noauto 12548d4ce358SKyle Evans */ 12558d4ce358SKyle Evans static int 12568d4ce358SKyle Evans be_deactivate(libbe_handle_t *lbh, const char *ds) 12578d4ce358SKyle Evans { 12588d4ce358SKyle Evans zfs_handle_t *zfs; 12598d4ce358SKyle Evans 12608d4ce358SKyle Evans if ((zfs = zfs_open(lbh->lzh, ds, ZFS_TYPE_DATASET)) == NULL) 12618d4ce358SKyle Evans return (1); 12628d4ce358SKyle Evans if (zfs_prop_set(zfs, "canmount", "noauto") != 0) 12638d4ce358SKyle Evans return (1); 12648d4ce358SKyle Evans zfs_close(zfs); 12658d4ce358SKyle Evans return (0); 12668d4ce358SKyle Evans } 126728f16a0fSKyle Evans 126828f16a0fSKyle Evans int 126973c3d608SKyle Evans be_activate(libbe_handle_t *lbh, const char *bootenv, bool temporary) 127028f16a0fSKyle Evans { 127128f16a0fSKyle Evans char be_path[BE_MAXPATHLEN]; 127228f16a0fSKyle Evans char buf[BE_MAXPATHLEN]; 12734635676dSKyle Evans nvlist_t *config, *dsprops, *vdevs; 12744635676dSKyle Evans char *origin; 12750cadc427SKyle Evans uint64_t pool_guid; 12760cadc427SKyle Evans zfs_handle_t *zhp; 127728f16a0fSKyle Evans int err; 127828f16a0fSKyle Evans 127928f16a0fSKyle Evans be_root_concat(lbh, bootenv, be_path); 128028f16a0fSKyle Evans 128128f16a0fSKyle Evans /* Note: be_exists fails if mountpoint is not / */ 1282162ec569SKyle Evans if ((err = be_exists(lbh, be_path)) != 0) 1283162ec569SKyle Evans return (set_error(lbh, err)); 128428f16a0fSKyle Evans 128528f16a0fSKyle Evans if (temporary) { 1286d06f7103SKyle Evans config = zpool_get_config(lbh->active_phandle, NULL); 1287c65a2111SKyle Evans if (config == NULL) 1288c65a2111SKyle Evans /* config should be fetchable... */ 1289c65a2111SKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 129028f16a0fSKyle Evans 1291d06f7103SKyle Evans if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, 1292d06f7103SKyle Evans &pool_guid) != 0) 1293c65a2111SKyle Evans /* Similarly, it shouldn't be possible */ 1294c65a2111SKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN)); 1295d06f7103SKyle Evans 129628f16a0fSKyle Evans /* Expected format according to zfsbootcfg(8) man */ 1297a8e44f4dSKyle Evans snprintf(buf, sizeof(buf), "zfs:%s:", be_path); 129828f16a0fSKyle Evans 1299c65a2111SKyle Evans /* We have no config tree */ 1300c65a2111SKyle Evans if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, 1301c65a2111SKyle Evans &vdevs) != 0) 1302c65a2111SKyle Evans return (set_error(lbh, BE_ERR_NOPOOL)); 130328f16a0fSKyle Evans 1304d06f7103SKyle Evans return (be_set_nextboot(lbh, vdevs, pool_guid, buf)); 130528f16a0fSKyle Evans } else { 13068d4ce358SKyle Evans if (be_deactivate(lbh, lbh->bootfs) != 0) 13078d4ce358SKyle Evans return (-1); 13088d4ce358SKyle Evans 130928f16a0fSKyle Evans /* Obtain bootenv zpool */ 1310c3a34c08SKyle Evans err = zpool_set_prop(lbh->active_phandle, "bootfs", be_path); 13110cadc427SKyle Evans if (err) 13120cadc427SKyle Evans return (-1); 131328f16a0fSKyle Evans 13140cadc427SKyle Evans zhp = zfs_open(lbh->lzh, be_path, ZFS_TYPE_FILESYSTEM); 13150cadc427SKyle Evans if (zhp == NULL) 13160cadc427SKyle Evans return (-1); 131728f16a0fSKyle Evans 13184635676dSKyle Evans if (be_prop_list_alloc(&dsprops) != 0) 13194635676dSKyle Evans return (-1); 13204635676dSKyle Evans 13214635676dSKyle Evans if (be_get_dataset_props(lbh, be_path, dsprops) != 0) { 13224635676dSKyle Evans nvlist_free(dsprops); 13234635676dSKyle Evans return (-1); 13244635676dSKyle Evans } 13254635676dSKyle Evans 13264635676dSKyle Evans if (nvlist_lookup_string(dsprops, "origin", &origin) == 0) 13270cadc427SKyle Evans err = zfs_promote(zhp); 13284635676dSKyle Evans nvlist_free(dsprops); 13294635676dSKyle Evans 13300cadc427SKyle Evans zfs_close(zhp); 13310cadc427SKyle Evans 13320cadc427SKyle Evans if (err) 133328f16a0fSKyle Evans return (-1); 133428f16a0fSKyle Evans } 13350cadc427SKyle Evans 13360cadc427SKyle Evans return (BE_ERR_SUCCESS); 133728f16a0fSKyle Evans } 1338