1b179da01SKyle Evans /*-
24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
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
2851aecc89SKyle Evans #include <sys/param.h>
292f700ca9SKyle Evans #include <sys/module.h>
3051aecc89SKyle Evans #include <sys/mount.h>
3128f16a0fSKyle Evans #include <sys/stat.h>
3251aecc89SKyle Evans #include <sys/ucred.h>
338f5c6c31SKyle Evans #include <sys/queue.h>
34485172f5SKyle Evans #include <sys/zfs_context.h>
35485172f5SKyle Evans #include <sys/mntent.h>
369e5787d2SMatt Macy #include <sys/zfs_ioctl.h>
37485172f5SKyle Evans
389e5787d2SMatt Macy #include <libzutil.h>
3928f16a0fSKyle Evans #include <ctype.h>
4028f16a0fSKyle Evans #include <libgen.h>
4128f16a0fSKyle Evans #include <libzfs_core.h>
429e5787d2SMatt Macy #include <libzfs_impl.h>
4328f16a0fSKyle Evans #include <stdio.h>
4428f16a0fSKyle Evans #include <stdlib.h>
4528f16a0fSKyle Evans #include <time.h>
4628f16a0fSKyle Evans #include <unistd.h>
47e307eb94SToomas Soome #include <libzfsbootenv.h>
4828f16a0fSKyle Evans
4928f16a0fSKyle Evans #include "be.h"
5028f16a0fSKyle Evans #include "be_impl.h"
5128f16a0fSKyle Evans
528f5c6c31SKyle Evans struct promote_entry {
538f5c6c31SKyle Evans char name[BE_MAXPATHLEN];
548f5c6c31SKyle Evans SLIST_ENTRY(promote_entry) link;
558f5c6c31SKyle Evans };
568f5c6c31SKyle Evans
57be7dd423SKyle Evans struct be_destroy_data {
58be7dd423SKyle Evans libbe_handle_t *lbh;
598f5c6c31SKyle Evans char target_name[BE_MAXPATHLEN];
60be7dd423SKyle Evans char *snapname;
618f5c6c31SKyle Evans SLIST_HEAD(, promote_entry) promotelist;
62be7dd423SKyle Evans };
63be7dd423SKyle Evans
643d1a1f2cSKyle Evans #if SOON
65c65a2111SKyle Evans static int be_create_child_noent(libbe_handle_t *lbh, const char *active,
66c65a2111SKyle Evans const char *child_path);
67c65a2111SKyle Evans static int be_create_child_cloned(libbe_handle_t *lbh, const char *active);
683d1a1f2cSKyle Evans #endif
69c65a2111SKyle Evans
7090cf61e8SKyle Evans /* Arbitrary... should tune */
7190cf61e8SKyle Evans #define BE_SNAP_SERIAL_MAX 1024
7290cf61e8SKyle Evans
7328f16a0fSKyle Evans /*
74ee16b7c9SKyle Evans * Iterator function for locating the rootfs amongst the children of the
75ee16b7c9SKyle Evans * zfs_be_root set by loader(8). data is expected to be a libbe_handle_t *.
76ee16b7c9SKyle Evans */
77ee16b7c9SKyle Evans static int
be_locate_rootfs(libbe_handle_t * lbh)7851aecc89SKyle Evans be_locate_rootfs(libbe_handle_t *lbh)
79ee16b7c9SKyle Evans {
804ab5187dSKyle Evans struct statfs sfs;
81485172f5SKyle Evans struct mnttab entry;
8251aecc89SKyle Evans zfs_handle_t *zfs;
83ee16b7c9SKyle Evans
844ab5187dSKyle Evans /*
854ab5187dSKyle Evans * Check first if root is ZFS; if not, we'll bail on rootfs capture.
864ab5187dSKyle Evans * Unfortunately needed because zfs_path_to_zhandle will emit to
874ab5187dSKyle Evans * stderr if / isn't actually a ZFS filesystem, which we'd like
884ab5187dSKyle Evans * to avoid.
894ab5187dSKyle Evans */
904ab5187dSKyle Evans if (statfs("/", &sfs) == 0) {
914ab5187dSKyle Evans statfs2mnttab(&sfs, &entry);
924ab5187dSKyle Evans if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0)
934ab5187dSKyle Evans return (1);
944ab5187dSKyle Evans } else
954ab5187dSKyle Evans return (1);
9651aecc89SKyle Evans zfs = zfs_path_to_zhandle(lbh->lzh, "/", ZFS_TYPE_FILESYSTEM);
9751aecc89SKyle Evans if (zfs == NULL)
98ee16b7c9SKyle Evans return (1);
99ee16b7c9SKyle Evans
10051aecc89SKyle Evans strlcpy(lbh->rootfs, zfs_get_name(zfs), sizeof(lbh->rootfs));
10151aecc89SKyle Evans zfs_close(zfs);
102ee16b7c9SKyle Evans return (0);
103ee16b7c9SKyle Evans }
104ee16b7c9SKyle Evans
105ee16b7c9SKyle Evans /*
10628f16a0fSKyle Evans * Initializes the libbe context to operate in the root boot environment
10728f16a0fSKyle Evans * dataset, for example, zroot/ROOT.
10828f16a0fSKyle Evans */
10928f16a0fSKyle Evans libbe_handle_t *
libbe_init(const char * root)110cc624025SKyle Evans libbe_init(const char *root)
11128f16a0fSKyle Evans {
112fc13fc1cSKyle Evans char altroot[MAXPATHLEN];
11328f16a0fSKyle Evans libbe_handle_t *lbh;
114c3a34c08SKyle Evans char *poolname, *pos;
115c3a34c08SKyle Evans int pnamelen;
11628f16a0fSKyle Evans
117c3a34c08SKyle Evans lbh = NULL;
118c3a34c08SKyle Evans poolname = pos = NULL;
11928f16a0fSKyle Evans
1202f700ca9SKyle Evans /*
1212f700ca9SKyle Evans * If the zfs kmod's not loaded then the later libzfs_init() will load
1222f700ca9SKyle Evans * the module for us, but that's not desirable for a couple reasons. If
1232f700ca9SKyle Evans * the module's not loaded, there's no pool imported and we're going to
1242f700ca9SKyle Evans * fail anyways. We also don't really want libbe consumers to have that
1252f700ca9SKyle Evans * kind of side-effect (module loading) in the general case.
1262f700ca9SKyle Evans */
1272f700ca9SKyle Evans if (modfind("zfs") < 0)
1282f700ca9SKyle Evans goto err;
1292f700ca9SKyle Evans
130c3a34c08SKyle Evans if ((lbh = calloc(1, sizeof(libbe_handle_t))) == NULL)
131c3a34c08SKyle Evans goto err;
13228f16a0fSKyle Evans
133c3a34c08SKyle Evans if ((lbh->lzh = libzfs_init()) == NULL)
134c3a34c08SKyle Evans goto err;
13528f16a0fSKyle Evans
136cc624025SKyle Evans /*
137cc624025SKyle Evans * Grab rootfs, we'll work backwards from there if an optional BE root
138cc624025SKyle Evans * has not been passed in.
139cc624025SKyle Evans */
1404ab5187dSKyle Evans if (be_locate_rootfs(lbh) != 0) {
1414ab5187dSKyle Evans if (root == NULL)
142c3a34c08SKyle Evans goto err;
1434ab5187dSKyle Evans *lbh->rootfs = '\0';
1444ab5187dSKyle Evans }
145cc624025SKyle Evans if (root == NULL) {
146cc624025SKyle Evans /* Strip off the final slash from rootfs to get the be root */
14751aecc89SKyle Evans strlcpy(lbh->root, lbh->rootfs, sizeof(lbh->root));
14851aecc89SKyle Evans pos = strrchr(lbh->root, '/');
14951aecc89SKyle Evans if (pos == NULL)
15051aecc89SKyle Evans goto err;
15151aecc89SKyle Evans *pos = '\0';
152cc624025SKyle Evans } else
153cc624025SKyle Evans strlcpy(lbh->root, root, sizeof(lbh->root));
154c3a34c08SKyle Evans
155c3a34c08SKyle Evans if ((pos = strchr(lbh->root, '/')) == NULL)
156c3a34c08SKyle Evans goto err;
157c3a34c08SKyle Evans
158c3a34c08SKyle Evans pnamelen = pos - lbh->root;
159c3a34c08SKyle Evans poolname = malloc(pnamelen + 1);
160c3a34c08SKyle Evans if (poolname == NULL)
161c3a34c08SKyle Evans goto err;
162c3a34c08SKyle Evans
16355b0e92bSKyle Evans strlcpy(poolname, lbh->root, pnamelen + 1);
164c3a34c08SKyle Evans if ((lbh->active_phandle = zpool_open(lbh->lzh, poolname)) == NULL)
165c3a34c08SKyle Evans goto err;
166a8e44f4dSKyle Evans free(poolname);
167a8e44f4dSKyle Evans poolname = NULL;
168c3a34c08SKyle Evans
169c3a34c08SKyle Evans if (zpool_get_prop(lbh->active_phandle, ZPOOL_PROP_BOOTFS, lbh->bootfs,
17055b0e92bSKyle Evans sizeof(lbh->bootfs), NULL, true) != 0)
171c3a34c08SKyle Evans goto err;
172c3a34c08SKyle Evans
173fc13fc1cSKyle Evans if (zpool_get_prop(lbh->active_phandle, ZPOOL_PROP_ALTROOT,
174fc13fc1cSKyle Evans altroot, sizeof(altroot), NULL, true) == 0 &&
175fc13fc1cSKyle Evans strcmp(altroot, "-") != 0)
176fc13fc1cSKyle Evans lbh->altroot_len = strlen(altroot);
177fc13fc1cSKyle Evans
1785086b6ecSR. Christian McDonald (void) lzbe_get_boot_device(zpool_get_name(lbh->active_phandle),
1795086b6ecSR. Christian McDonald &lbh->bootonce);
1805086b6ecSR. Christian McDonald
18128f16a0fSKyle Evans return (lbh);
182c3a34c08SKyle Evans err:
183c3a34c08SKyle Evans if (lbh != NULL) {
184c3a34c08SKyle Evans if (lbh->active_phandle != NULL)
185c3a34c08SKyle Evans zpool_close(lbh->active_phandle);
186c3a34c08SKyle Evans if (lbh->lzh != NULL)
187c3a34c08SKyle Evans libzfs_fini(lbh->lzh);
188c3a34c08SKyle Evans free(lbh);
189c3a34c08SKyle Evans }
190c3a34c08SKyle Evans free(poolname);
191c3a34c08SKyle Evans return (NULL);
19228f16a0fSKyle Evans }
19328f16a0fSKyle Evans
19428f16a0fSKyle Evans
19528f16a0fSKyle Evans /*
19628f16a0fSKyle Evans * Free memory allocated by libbe_init()
19728f16a0fSKyle Evans */
19828f16a0fSKyle Evans void
libbe_close(libbe_handle_t * lbh)19928f16a0fSKyle Evans libbe_close(libbe_handle_t *lbh)
20028f16a0fSKyle Evans {
201bfe0869cSKyle Evans
202c3a34c08SKyle Evans if (lbh->active_phandle != NULL)
203c3a34c08SKyle Evans zpool_close(lbh->active_phandle);
20428f16a0fSKyle Evans libzfs_fini(lbh->lzh);
2055086b6ecSR. Christian McDonald
2065086b6ecSR. Christian McDonald free(lbh->bootonce);
20728f16a0fSKyle Evans free(lbh);
20828f16a0fSKyle Evans }
20928f16a0fSKyle Evans
2109b1662e6SKyle Evans /*
2119b1662e6SKyle Evans * Proxy through to libzfs for the moment.
2129b1662e6SKyle Evans */
2139b1662e6SKyle Evans void
be_nicenum(uint64_t num,char * buf,size_t buflen)2149b1662e6SKyle Evans be_nicenum(uint64_t num, char *buf, size_t buflen)
2159b1662e6SKyle Evans {
2169b1662e6SKyle Evans
2179b1662e6SKyle Evans zfs_nicenum(num, buf, buflen);
2189b1662e6SKyle Evans }
21928f16a0fSKyle Evans
2208f5c6c31SKyle Evans static bool
be_should_promote_clones(zfs_handle_t * zfs_hdl,struct be_destroy_data * bdd)2218f5c6c31SKyle Evans be_should_promote_clones(zfs_handle_t *zfs_hdl, struct be_destroy_data *bdd)
2228f5c6c31SKyle Evans {
2238f5c6c31SKyle Evans char *atpos;
2248f5c6c31SKyle Evans
2258f5c6c31SKyle Evans if (zfs_get_type(zfs_hdl) != ZFS_TYPE_SNAPSHOT)
2268f5c6c31SKyle Evans return (false);
2278f5c6c31SKyle Evans
2288f5c6c31SKyle Evans /*
2298f5c6c31SKyle Evans * If we're deleting a snapshot, we need to make sure we only promote
2308f5c6c31SKyle Evans * clones that are derived from one of the snapshots we're deleting,
2318f5c6c31SKyle Evans * rather than that of a snapshot we're not touching. This keeps stuff
2328f5c6c31SKyle Evans * in a consistent state, making sure that we don't error out unless
2338f5c6c31SKyle Evans * we really need to.
2348f5c6c31SKyle Evans */
2358f5c6c31SKyle Evans if (bdd->snapname == NULL)
2368f5c6c31SKyle Evans return (true);
2378f5c6c31SKyle Evans
2388f5c6c31SKyle Evans atpos = strchr(zfs_get_name(zfs_hdl), '@');
2398f5c6c31SKyle Evans return (strcmp(atpos + 1, bdd->snapname) == 0);
2408f5c6c31SKyle Evans }
2418f5c6c31SKyle Evans
2428f5c6c31SKyle Evans /*
2438f5c6c31SKyle Evans * This is executed from be_promote_dependent_clones via zfs_iter_dependents,
2448f5c6c31SKyle Evans * It checks if the dependent type is a snapshot then attempts to find any
2458f5c6c31SKyle Evans * clones associated with it. Any clones not related to the destroy target are
2468f5c6c31SKyle Evans * added to the promote list.
2478f5c6c31SKyle Evans */
2488f5c6c31SKyle Evans static int
be_dependent_clone_cb(zfs_handle_t * zfs_hdl,void * data)2498f5c6c31SKyle Evans be_dependent_clone_cb(zfs_handle_t *zfs_hdl, void *data)
2508f5c6c31SKyle Evans {
2518f5c6c31SKyle Evans int err;
2528f5c6c31SKyle Evans bool found;
2532a58b312SMartin Matuska const char *name;
2548f5c6c31SKyle Evans struct nvlist *nvl;
2558f5c6c31SKyle Evans struct nvpair *nvp;
2568f5c6c31SKyle Evans struct be_destroy_data *bdd;
2578f5c6c31SKyle Evans struct promote_entry *entry, *newentry;
2588f5c6c31SKyle Evans
2598f5c6c31SKyle Evans nvp = NULL;
2608f5c6c31SKyle Evans err = 0;
2618f5c6c31SKyle Evans bdd = (struct be_destroy_data *)data;
2628f5c6c31SKyle Evans
2638f5c6c31SKyle Evans if (be_should_promote_clones(zfs_hdl, bdd) &&
2648f5c6c31SKyle Evans (nvl = zfs_get_clones_nvl(zfs_hdl)) != NULL) {
2658f5c6c31SKyle Evans while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
2668f5c6c31SKyle Evans name = nvpair_name(nvp);
2678f5c6c31SKyle Evans
2688f5c6c31SKyle Evans /*
2698f5c6c31SKyle Evans * Skip if the clone is equal to, or a child of, the
2708f5c6c31SKyle Evans * destroy target.
2718f5c6c31SKyle Evans */
2728f5c6c31SKyle Evans if (strncmp(name, bdd->target_name,
2738f5c6c31SKyle Evans strlen(bdd->target_name)) == 0 ||
2748f5c6c31SKyle Evans strstr(name, bdd->target_name) == name) {
2758f5c6c31SKyle Evans continue;
2768f5c6c31SKyle Evans }
2778f5c6c31SKyle Evans
2788f5c6c31SKyle Evans found = false;
2798f5c6c31SKyle Evans SLIST_FOREACH(entry, &bdd->promotelist, link) {
2808f5c6c31SKyle Evans if (strcmp(entry->name, name) == 0) {
2818f5c6c31SKyle Evans found = true;
2828f5c6c31SKyle Evans break;
2838f5c6c31SKyle Evans }
2848f5c6c31SKyle Evans }
2858f5c6c31SKyle Evans
2868f5c6c31SKyle Evans if (found)
2878f5c6c31SKyle Evans continue;
2888f5c6c31SKyle Evans
2898f5c6c31SKyle Evans newentry = malloc(sizeof(struct promote_entry));
2908f5c6c31SKyle Evans if (newentry == NULL) {
2918f5c6c31SKyle Evans err = ENOMEM;
2928f5c6c31SKyle Evans break;
2938f5c6c31SKyle Evans }
2948f5c6c31SKyle Evans
2958f5c6c31SKyle Evans #define BE_COPY_NAME(entry, src) \
2968f5c6c31SKyle Evans strlcpy((entry)->name, (src), sizeof((entry)->name))
2978f5c6c31SKyle Evans if (BE_COPY_NAME(newentry, name) >=
2988f5c6c31SKyle Evans sizeof(newentry->name)) {
2998f5c6c31SKyle Evans /* Shouldn't happen. */
3008f5c6c31SKyle Evans free(newentry);
3018f5c6c31SKyle Evans err = ENAMETOOLONG;
3028f5c6c31SKyle Evans break;
3038f5c6c31SKyle Evans }
3048f5c6c31SKyle Evans #undef BE_COPY_NAME
3058f5c6c31SKyle Evans
3068f5c6c31SKyle Evans /*
3078f5c6c31SKyle Evans * We're building up a SLIST here to make sure both that
3088f5c6c31SKyle Evans * we get the order right and so that we don't
3098f5c6c31SKyle Evans * inadvertently observe the wrong state by promoting
3108f5c6c31SKyle Evans * datasets while we're still walking the tree. The
3118f5c6c31SKyle Evans * latter can lead to situations where we promote a BE
3128f5c6c31SKyle Evans * then effectively demote it again.
3138f5c6c31SKyle Evans */
3148f5c6c31SKyle Evans SLIST_INSERT_HEAD(&bdd->promotelist, newentry, link);
3158f5c6c31SKyle Evans }
3168f5c6c31SKyle Evans nvlist_free(nvl);
3178f5c6c31SKyle Evans }
3188f5c6c31SKyle Evans zfs_close(zfs_hdl);
3198f5c6c31SKyle Evans return (err);
3208f5c6c31SKyle Evans }
3218f5c6c31SKyle Evans
3228f5c6c31SKyle Evans /*
3238f5c6c31SKyle Evans * This is called before a destroy, so that any datasets(environments) that are
3248f5c6c31SKyle Evans * dependent on this one get promoted before destroying the target.
3258f5c6c31SKyle Evans */
3268f5c6c31SKyle Evans static int
be_promote_dependent_clones(zfs_handle_t * zfs_hdl,struct be_destroy_data * bdd)3278f5c6c31SKyle Evans be_promote_dependent_clones(zfs_handle_t *zfs_hdl, struct be_destroy_data *bdd)
3288f5c6c31SKyle Evans {
3298f5c6c31SKyle Evans int err;
3308f5c6c31SKyle Evans zfs_handle_t *clone;
3318f5c6c31SKyle Evans struct promote_entry *entry;
3328f5c6c31SKyle Evans
3338f5c6c31SKyle Evans snprintf(bdd->target_name, BE_MAXPATHLEN, "%s/", zfs_get_name(zfs_hdl));
334d411c1d6SMartin Matuska err = zfs_iter_dependents(zfs_hdl, true, be_dependent_clone_cb, bdd);
3358f5c6c31SKyle Evans
3368f5c6c31SKyle Evans /*
3378f5c6c31SKyle Evans * Drain the list and walk away from it if we're only deleting a
3388f5c6c31SKyle Evans * snapshot.
3398f5c6c31SKyle Evans */
3408f5c6c31SKyle Evans if (bdd->snapname != NULL && !SLIST_EMPTY(&bdd->promotelist))
3418f5c6c31SKyle Evans err = BE_ERR_HASCLONES;
3428f5c6c31SKyle Evans while (!SLIST_EMPTY(&bdd->promotelist)) {
3438f5c6c31SKyle Evans entry = SLIST_FIRST(&bdd->promotelist);
3448f5c6c31SKyle Evans SLIST_REMOVE_HEAD(&bdd->promotelist, link);
3458f5c6c31SKyle Evans
3468f5c6c31SKyle Evans #define ZFS_GRAB_CLONE() \
3478f5c6c31SKyle Evans zfs_open(bdd->lbh->lzh, entry->name, ZFS_TYPE_FILESYSTEM)
3488f5c6c31SKyle Evans /*
3498f5c6c31SKyle Evans * Just skip this part on error, we still want to clean up the
3508f5c6c31SKyle Evans * promotion list after the first error. We'll then preserve it
3518f5c6c31SKyle Evans * all the way back.
3528f5c6c31SKyle Evans */
3538f5c6c31SKyle Evans if (err == 0 && (clone = ZFS_GRAB_CLONE()) != NULL) {
3548f5c6c31SKyle Evans err = zfs_promote(clone);
3558f5c6c31SKyle Evans if (err != 0)
3568f5c6c31SKyle Evans err = BE_ERR_DESTROYMNT;
3578f5c6c31SKyle Evans zfs_close(clone);
3588f5c6c31SKyle Evans }
3598f5c6c31SKyle Evans #undef ZFS_GRAB_CLONE
3608f5c6c31SKyle Evans free(entry);
3618f5c6c31SKyle Evans }
3628f5c6c31SKyle Evans
3638f5c6c31SKyle Evans return (err);
3648f5c6c31SKyle Evans }
3658f5c6c31SKyle Evans
366920abf4dSKyle Evans static int
be_destroy_cb(zfs_handle_t * zfs_hdl,void * data)367920abf4dSKyle Evans be_destroy_cb(zfs_handle_t *zfs_hdl, void *data)
368920abf4dSKyle Evans {
369be7dd423SKyle Evans char path[BE_MAXPATHLEN];
370be7dd423SKyle Evans struct be_destroy_data *bdd;
371be7dd423SKyle Evans zfs_handle_t *snap;
372920abf4dSKyle Evans int err;
373920abf4dSKyle Evans
374be7dd423SKyle Evans bdd = (struct be_destroy_data *)data;
375be7dd423SKyle Evans if (bdd->snapname == NULL) {
376d411c1d6SMartin Matuska err = zfs_iter_children(zfs_hdl, be_destroy_cb, data);
377be7dd423SKyle Evans if (err != 0)
378920abf4dSKyle Evans return (err);
379be7dd423SKyle Evans return (zfs_destroy(zfs_hdl, false));
380be7dd423SKyle Evans }
381be7dd423SKyle Evans /* If we're dealing with snapshots instead, delete that one alone */
382d411c1d6SMartin Matuska err = zfs_iter_filesystems(zfs_hdl, be_destroy_cb, data);
383be7dd423SKyle Evans if (err != 0)
384920abf4dSKyle Evans return (err);
385be7dd423SKyle Evans /*
386be7dd423SKyle Evans * This part is intentionally glossing over any potential errors,
387be7dd423SKyle Evans * because there's a lot less potential for errors when we're cleaning
388be7dd423SKyle Evans * up snapshots rather than a full deep BE. The primary error case
389be7dd423SKyle Evans * here being if the snapshot doesn't exist in the first place, which
390be7dd423SKyle Evans * the caller will likely deem insignificant as long as it doesn't
391be7dd423SKyle Evans * exist after the call. Thus, such a missing snapshot shouldn't jam
392be7dd423SKyle Evans * up the destruction.
393be7dd423SKyle Evans */
394be7dd423SKyle Evans snprintf(path, sizeof(path), "%s@%s", zfs_get_name(zfs_hdl),
395be7dd423SKyle Evans bdd->snapname);
396be7dd423SKyle Evans if (!zfs_dataset_exists(bdd->lbh->lzh, path, ZFS_TYPE_SNAPSHOT))
397be7dd423SKyle Evans return (0);
398be7dd423SKyle Evans snap = zfs_open(bdd->lbh->lzh, path, ZFS_TYPE_SNAPSHOT);
399be7dd423SKyle Evans if (snap != NULL)
400be7dd423SKyle Evans zfs_destroy(snap, false);
401920abf4dSKyle Evans return (0);
402920abf4dSKyle Evans }
403920abf4dSKyle Evans
4041dc85563SKyle Evans #define BE_DESTROY_WANTORIGIN (BE_DESTROY_ORIGIN | BE_DESTROY_AUTOORIGIN)
40528f16a0fSKyle Evans /*
40628f16a0fSKyle Evans * Destroy the boot environment or snapshot specified by the name
40728f16a0fSKyle Evans * parameter. Options are or'd together with the possible values:
40828f16a0fSKyle Evans * BE_DESTROY_FORCE : forces operation on mounted datasets
409be7dd423SKyle Evans * BE_DESTROY_ORIGIN: destroy the origin snapshot as well
41028f16a0fSKyle Evans */
4118f5c6c31SKyle Evans static int
be_destroy_internal(libbe_handle_t * lbh,const char * name,int options,bool odestroyer)4128f5c6c31SKyle Evans be_destroy_internal(libbe_handle_t *lbh, const char *name, int options,
4138f5c6c31SKyle Evans bool odestroyer)
41428f16a0fSKyle Evans {
415be7dd423SKyle Evans struct be_destroy_data bdd;
41613c62c50SKyle Evans char origin[BE_MAXPATHLEN], path[BE_MAXPATHLEN];
41728f16a0fSKyle Evans zfs_handle_t *fs;
418be7dd423SKyle Evans char *snapdelim;
419bfe0869cSKyle Evans int err, force, mounted;
420be7dd423SKyle Evans size_t rootlen;
42128f16a0fSKyle Evans
422be7dd423SKyle Evans bdd.lbh = lbh;
423be7dd423SKyle Evans bdd.snapname = NULL;
4248f5c6c31SKyle Evans SLIST_INIT(&bdd.promotelist);
425bfe0869cSKyle Evans force = options & BE_DESTROY_FORCE;
42613c62c50SKyle Evans *origin = '\0';
42728f16a0fSKyle Evans
42828f16a0fSKyle Evans be_root_concat(lbh, name, path);
42928f16a0fSKyle Evans
430be7dd423SKyle Evans if ((snapdelim = strchr(path, '@')) == NULL) {
431bfe0869cSKyle Evans if (!zfs_dataset_exists(lbh->lzh, path, ZFS_TYPE_FILESYSTEM))
43228f16a0fSKyle Evans return (set_error(lbh, BE_ERR_NOENT));
43328f16a0fSKyle Evans
434f08dac4eSKyle Evans if (strcmp(path, lbh->rootfs) == 0 ||
435f08dac4eSKyle Evans strcmp(path, lbh->bootfs) == 0)
43628f16a0fSKyle Evans return (set_error(lbh, BE_ERR_DESTROYACT));
43728f16a0fSKyle Evans
438be7dd423SKyle Evans fs = zfs_open(lbh->lzh, path, ZFS_TYPE_FILESYSTEM);
43913c62c50SKyle Evans if (fs == NULL)
44013c62c50SKyle Evans return (set_error(lbh, BE_ERR_ZFSOPEN));
441be7dd423SKyle Evans
4428f5c6c31SKyle Evans /* Don't destroy a mounted dataset unless force is specified */
4438f5c6c31SKyle Evans if ((mounted = zfs_is_mounted(fs, NULL)) != 0) {
4448f5c6c31SKyle Evans if (force) {
4458f5c6c31SKyle Evans zfs_unmount(fs, NULL, 0);
4468f5c6c31SKyle Evans } else {
4478f5c6c31SKyle Evans free(bdd.snapname);
4488f5c6c31SKyle Evans return (set_error(lbh, BE_ERR_DESTROYMNT));
4498f5c6c31SKyle Evans }
4508f5c6c31SKyle Evans }
4515086b6ecSR. Christian McDonald
4525086b6ecSR. Christian McDonald /* Handle destroying bootonce */
4535086b6ecSR. Christian McDonald if (lbh->bootonce != NULL &&
4545086b6ecSR. Christian McDonald strcmp(path, lbh->bootonce) == 0)
4555086b6ecSR. Christian McDonald (void) lzbe_set_boot_device(
4565086b6ecSR. Christian McDonald zpool_get_name(lbh->active_phandle), lzbe_add, NULL);
4578f5c6c31SKyle Evans } else {
4588f5c6c31SKyle Evans /*
4598f5c6c31SKyle Evans * If we're initially destroying a snapshot, origin options do
4608f5c6c31SKyle Evans * not make sense. If we're destroying the origin snapshot of
4618f5c6c31SKyle Evans * a BE, we want to maintain the options in case we need to
4628f5c6c31SKyle Evans * fake success after failing to promote.
4638f5c6c31SKyle Evans */
4648f5c6c31SKyle Evans if (!odestroyer)
4658f5c6c31SKyle Evans options &= ~BE_DESTROY_WANTORIGIN;
4668f5c6c31SKyle Evans if (!zfs_dataset_exists(lbh->lzh, path, ZFS_TYPE_SNAPSHOT))
4678f5c6c31SKyle Evans return (set_error(lbh, BE_ERR_NOENT));
4688f5c6c31SKyle Evans
4698f5c6c31SKyle Evans bdd.snapname = strdup(snapdelim + 1);
4708f5c6c31SKyle Evans if (bdd.snapname == NULL)
4718f5c6c31SKyle Evans return (set_error(lbh, BE_ERR_NOMEM));
4728f5c6c31SKyle Evans *snapdelim = '\0';
4738f5c6c31SKyle Evans fs = zfs_open(lbh->lzh, path, ZFS_TYPE_DATASET);
4748f5c6c31SKyle Evans if (fs == NULL) {
4758f5c6c31SKyle Evans free(bdd.snapname);
4768f5c6c31SKyle Evans return (set_error(lbh, BE_ERR_ZFSOPEN));
4778f5c6c31SKyle Evans }
4788f5c6c31SKyle Evans }
4798f5c6c31SKyle Evans
4808f5c6c31SKyle Evans /*
4818f5c6c31SKyle Evans * Whether we're destroying a BE or a single snapshot, we need to walk
4828f5c6c31SKyle Evans * the tree of what we're going to destroy and promote everything in our
4838f5c6c31SKyle Evans * path so that we can make it happen.
4848f5c6c31SKyle Evans */
4858f5c6c31SKyle Evans if ((err = be_promote_dependent_clones(fs, &bdd)) != 0) {
4868f5c6c31SKyle Evans free(bdd.snapname);
4878f5c6c31SKyle Evans
4888f5c6c31SKyle Evans /*
4898f5c6c31SKyle Evans * If we're just destroying the origin of some other dataset
4908f5c6c31SKyle Evans * we were invoked to destroy, then we just ignore
4918f5c6c31SKyle Evans * BE_ERR_HASCLONES and return success unless the caller wanted
4928f5c6c31SKyle Evans * to force the issue.
4938f5c6c31SKyle Evans */
4948f5c6c31SKyle Evans if (odestroyer && err == BE_ERR_HASCLONES &&
4958f5c6c31SKyle Evans (options & BE_DESTROY_AUTOORIGIN) != 0)
4968f5c6c31SKyle Evans return (0);
4978f5c6c31SKyle Evans return (set_error(lbh, err));
4988f5c6c31SKyle Evans }
4998f5c6c31SKyle Evans
5008f5c6c31SKyle Evans /*
5018f5c6c31SKyle Evans * This was deferred until after we promote all of the derivatives so
5028f5c6c31SKyle Evans * that we grab the new origin after everything's settled down.
5038f5c6c31SKyle Evans */
5041dc85563SKyle Evans if ((options & BE_DESTROY_WANTORIGIN) != 0 &&
50513c62c50SKyle Evans zfs_prop_get(fs, ZFS_PROP_ORIGIN, origin, sizeof(origin),
5061dc85563SKyle Evans NULL, NULL, 0, 1) != 0 &&
5071dc85563SKyle Evans (options & BE_DESTROY_ORIGIN) != 0)
50813c62c50SKyle Evans return (set_error(lbh, BE_ERR_NOORIGIN));
509e1ee6230SKyle Evans
510455d8009SKyle Evans /*
511455d8009SKyle Evans * If the caller wants auto-origin destruction and the origin
512455d8009SKyle Evans * name matches one of our automatically created snapshot names
513455d8009SKyle Evans * (i.e. strftime("%F-%T") with a serial at the end), then
514455d8009SKyle Evans * we'll set the DESTROY_ORIGIN flag and nuke it
515455d8009SKyle Evans * be_is_auto_snapshot_name is exported from libbe(3) so that
516455d8009SKyle Evans * the caller can determine if it needs to warn about the origin
517455d8009SKyle Evans * not being destroyed or not.
518455d8009SKyle Evans */
5191dc85563SKyle Evans if ((options & BE_DESTROY_AUTOORIGIN) != 0 && *origin != '\0' &&
520455d8009SKyle Evans be_is_auto_snapshot_name(lbh, origin))
521455d8009SKyle Evans options |= BE_DESTROY_ORIGIN;
522455d8009SKyle Evans
523be7dd423SKyle Evans err = be_destroy_cb(fs, &bdd);
524be7dd423SKyle Evans zfs_close(fs);
525be7dd423SKyle Evans free(bdd.snapname);
526be7dd423SKyle Evans if (err != 0) {
527920abf4dSKyle Evans /* Children are still present or the mount is referenced */
528920abf4dSKyle Evans if (err == EBUSY)
529920abf4dSKyle Evans return (set_error(lbh, BE_ERR_DESTROYMNT));
530920abf4dSKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN));
531920abf4dSKyle Evans }
53228f16a0fSKyle Evans
533be7dd423SKyle Evans if ((options & BE_DESTROY_ORIGIN) == 0)
534920abf4dSKyle Evans return (0);
53528f16a0fSKyle Evans
536be7dd423SKyle Evans /* The origin can't possibly be shorter than the BE root */
537be7dd423SKyle Evans rootlen = strlen(lbh->root);
538be7dd423SKyle Evans if (*origin == '\0' || strlen(origin) <= rootlen + 1)
539be7dd423SKyle Evans return (set_error(lbh, BE_ERR_INVORIGIN));
540be7dd423SKyle Evans
541be7dd423SKyle Evans /*
542be7dd423SKyle Evans * We'll be chopping off the BE root and running this back through
543be7dd423SKyle Evans * be_destroy, so that we properly handle the origin snapshot whether
544be7dd423SKyle Evans * it be that of a deep BE or not.
545be7dd423SKyle Evans */
546be7dd423SKyle Evans if (strncmp(origin, lbh->root, rootlen) != 0 || origin[rootlen] != '/')
547be7dd423SKyle Evans return (0);
548be7dd423SKyle Evans
5498f5c6c31SKyle Evans return (be_destroy_internal(lbh, origin + rootlen + 1,
5508f5c6c31SKyle Evans options & ~BE_DESTROY_ORIGIN, true));
5518f5c6c31SKyle Evans }
5528f5c6c31SKyle Evans
5538f5c6c31SKyle Evans int
be_destroy(libbe_handle_t * lbh,const char * name,int options)5548f5c6c31SKyle Evans be_destroy(libbe_handle_t *lbh, const char *name, int options)
5558f5c6c31SKyle Evans {
5568f5c6c31SKyle Evans
5578f5c6c31SKyle Evans /*
5588f5c6c31SKyle Evans * The consumer must not set both BE_DESTROY_AUTOORIGIN and
5598f5c6c31SKyle Evans * BE_DESTROY_ORIGIN. Internally, we'll set the latter from the former.
5608f5c6c31SKyle Evans * The latter should imply that we must succeed at destroying the
5618f5c6c31SKyle Evans * origin, or complain otherwise.
5628f5c6c31SKyle Evans */
5638f5c6c31SKyle Evans if ((options & BE_DESTROY_WANTORIGIN) == BE_DESTROY_WANTORIGIN)
5648f5c6c31SKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN));
5658f5c6c31SKyle Evans return (be_destroy_internal(lbh, name, options, false));
566be7dd423SKyle Evans }
56728f16a0fSKyle Evans
56890cf61e8SKyle Evans static void
be_setup_snapshot_name(libbe_handle_t * lbh,char * buf,size_t buflen)56990cf61e8SKyle Evans be_setup_snapshot_name(libbe_handle_t *lbh, char *buf, size_t buflen)
57090cf61e8SKyle Evans {
57190cf61e8SKyle Evans time_t rawtime;
57290cf61e8SKyle Evans int len, serial;
57390cf61e8SKyle Evans
57490cf61e8SKyle Evans time(&rawtime);
57590cf61e8SKyle Evans len = strlen(buf);
57690cf61e8SKyle Evans len += strftime(buf + len, buflen - len, "@%F-%T", localtime(&rawtime));
57790cf61e8SKyle Evans /* No room for serial... caller will do its best */
57890cf61e8SKyle Evans if (buflen - len < 2)
57990cf61e8SKyle Evans return;
58090cf61e8SKyle Evans
58190cf61e8SKyle Evans for (serial = 0; serial < BE_SNAP_SERIAL_MAX; ++serial) {
58290cf61e8SKyle Evans snprintf(buf + len, buflen - len, "-%d", serial);
58390cf61e8SKyle Evans if (!zfs_dataset_exists(lbh->lzh, buf, ZFS_TYPE_SNAPSHOT))
58490cf61e8SKyle Evans return;
58590cf61e8SKyle Evans }
58690cf61e8SKyle Evans }
58790cf61e8SKyle Evans
588455d8009SKyle Evans bool
be_is_auto_snapshot_name(libbe_handle_t * lbh __unused,const char * name)5899d6d8bf8SKyle Evans be_is_auto_snapshot_name(libbe_handle_t *lbh __unused, const char *name)
590455d8009SKyle Evans {
591455d8009SKyle Evans const char *snap;
592455d8009SKyle Evans int day, hour, minute, month, second, serial, year;
593455d8009SKyle Evans
594455d8009SKyle Evans if ((snap = strchr(name, '@')) == NULL)
595455d8009SKyle Evans return (false);
596455d8009SKyle Evans ++snap;
597455d8009SKyle Evans /* We'll grab the individual components and do some light validation. */
598455d8009SKyle Evans if (sscanf(snap, "%d-%d-%d-%d:%d:%d-%d", &year, &month, &day, &hour,
599455d8009SKyle Evans &minute, &second, &serial) != 7)
600455d8009SKyle Evans return (false);
601455d8009SKyle Evans return (year >= 1970) && (month >= 1 && month <= 12) &&
602455d8009SKyle Evans (day >= 1 && day <= 31) && (hour >= 0 && hour <= 23) &&
603455d8009SKyle Evans (minute >= 0 && minute <= 59) && (second >= 0 && second <= 60) &&
604455d8009SKyle Evans serial >= 0;
605455d8009SKyle Evans }
606455d8009SKyle Evans
60728f16a0fSKyle Evans int
be_snapshot(libbe_handle_t * lbh,const char * source,const char * snap_name,bool recursive,char * result)608b29bf2f8SKyle Evans be_snapshot(libbe_handle_t *lbh, const char *source, const char *snap_name,
60928f16a0fSKyle Evans bool recursive, char *result)
61028f16a0fSKyle Evans {
61128f16a0fSKyle Evans char buf[BE_MAXPATHLEN];
61290cf61e8SKyle Evans int err;
61328f16a0fSKyle Evans
61428f16a0fSKyle Evans be_root_concat(lbh, source, buf);
61528f16a0fSKyle Evans
616162ec569SKyle Evans if ((err = be_exists(lbh, buf)) != 0)
617162ec569SKyle Evans return (set_error(lbh, err));
61828f16a0fSKyle Evans
61928f16a0fSKyle Evans if (snap_name != NULL) {
620a8e44f4dSKyle Evans if (strlcat(buf, "@", sizeof(buf)) >= sizeof(buf))
621a8e44f4dSKyle Evans return (set_error(lbh, BE_ERR_INVALIDNAME));
622a8e44f4dSKyle Evans
623a8e44f4dSKyle Evans if (strlcat(buf, snap_name, sizeof(buf)) >= sizeof(buf))
624a8e44f4dSKyle Evans return (set_error(lbh, BE_ERR_INVALIDNAME));
625a8e44f4dSKyle Evans
626bfe0869cSKyle Evans if (result != NULL)
62728f16a0fSKyle Evans snprintf(result, BE_MAXPATHLEN, "%s@%s", source,
62828f16a0fSKyle Evans snap_name);
62928f16a0fSKyle Evans } else {
63090cf61e8SKyle Evans be_setup_snapshot_name(lbh, buf, sizeof(buf));
63190cf61e8SKyle Evans
632a8e44f4dSKyle Evans if (result != NULL && strlcpy(result, strrchr(buf, '/') + 1,
633a8e44f4dSKyle Evans sizeof(buf)) >= sizeof(buf))
634a8e44f4dSKyle Evans return (set_error(lbh, BE_ERR_INVALIDNAME));
63528f16a0fSKyle Evans }
636b29bf2f8SKyle Evans if ((err = zfs_snapshot(lbh->lzh, buf, recursive, NULL)) != 0) {
63728f16a0fSKyle Evans switch (err) {
63828f16a0fSKyle Evans case EZFS_INVALIDNAME:
63928f16a0fSKyle Evans return (set_error(lbh, BE_ERR_INVALIDNAME));
64028f16a0fSKyle Evans
64128f16a0fSKyle Evans default:
6422989df09SKyle Evans /*
6432989df09SKyle Evans * The other errors that zfs_ioc_snapshot might return
6442989df09SKyle Evans * shouldn't happen if we've set things up properly, so
6452989df09SKyle Evans * we'll gloss over them and call it UNKNOWN as it will
6462989df09SKyle Evans * require further triage.
6472989df09SKyle Evans */
6482989df09SKyle Evans if (errno == ENOTSUP)
6492989df09SKyle Evans return (set_error(lbh, BE_ERR_NOPOOL));
65028f16a0fSKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN));
65128f16a0fSKyle Evans }
65228f16a0fSKyle Evans }
65328f16a0fSKyle Evans
65428f16a0fSKyle Evans return (BE_ERR_SUCCESS);
65528f16a0fSKyle Evans }
65628f16a0fSKyle Evans
65728f16a0fSKyle Evans
65828f16a0fSKyle Evans /*
65928f16a0fSKyle Evans * Create the boot environment specified by the name parameter
66028f16a0fSKyle Evans */
66128f16a0fSKyle Evans int
be_create(libbe_handle_t * lbh,const char * name)66273c3d608SKyle Evans be_create(libbe_handle_t *lbh, const char *name)
66328f16a0fSKyle Evans {
66428f16a0fSKyle Evans int err;
66528f16a0fSKyle Evans
666b29bf2f8SKyle Evans err = be_create_from_existing(lbh, name, be_active_path(lbh));
66728f16a0fSKyle Evans
66828f16a0fSKyle Evans return (set_error(lbh, err));
66928f16a0fSKyle Evans }
67028f16a0fSKyle Evans
67128f16a0fSKyle Evans static int
be_deep_clone_prop(int prop,void * cb)67228f16a0fSKyle Evans be_deep_clone_prop(int prop, void *cb)
67328f16a0fSKyle Evans {
67428f16a0fSKyle Evans int err;
675bfe0869cSKyle Evans struct libbe_dccb *dccb;
67628f16a0fSKyle Evans zprop_source_t src;
67728f16a0fSKyle Evans char pval[BE_MAXPATHLEN];
67828f16a0fSKyle Evans char source[BE_MAXPATHLEN];
679af43c24dSKyle Evans char *val;
68028f16a0fSKyle Evans
681bfe0869cSKyle Evans dccb = cb;
68228f16a0fSKyle Evans /* Skip some properties we don't want to touch */
68373c3d608SKyle Evans if (prop == ZFS_PROP_CANMOUNT)
68428f16a0fSKyle Evans return (ZPROP_CONT);
68528f16a0fSKyle Evans
68628f16a0fSKyle Evans /* Don't copy readonly properties */
687bfe0869cSKyle Evans if (zfs_prop_readonly(prop))
68828f16a0fSKyle Evans return (ZPROP_CONT);
68928f16a0fSKyle Evans
69028f16a0fSKyle Evans if ((err = zfs_prop_get(dccb->zhp, prop, (char *)&pval,
691bfe0869cSKyle Evans sizeof(pval), &src, (char *)&source, sizeof(source), false)))
69228f16a0fSKyle Evans /* Just continue if we fail to read a property */
69328f16a0fSKyle Evans return (ZPROP_CONT);
694bfe0869cSKyle Evans
695be13d48cSKyle Evans /*
696be13d48cSKyle Evans * Only copy locally defined or received properties. This continues
697be13d48cSKyle Evans * to avoid temporary/default/local properties intentionally without
698be13d48cSKyle Evans * breaking received datasets.
699be13d48cSKyle Evans */
700be13d48cSKyle Evans if (src != ZPROP_SRC_LOCAL && src != ZPROP_SRC_RECEIVED)
70128f16a0fSKyle Evans return (ZPROP_CONT);
70228f16a0fSKyle Evans
703af43c24dSKyle Evans /* Augment mountpoint with altroot, if needed */
704af43c24dSKyle Evans val = pval;
705fc13fc1cSKyle Evans if (prop == ZFS_PROP_MOUNTPOINT)
706fc13fc1cSKyle Evans val = be_mountpoint_augmented(dccb->lbh, val);
707fc13fc1cSKyle Evans
708af43c24dSKyle Evans nvlist_add_string(dccb->props, zfs_prop_to_name(prop), val);
70928f16a0fSKyle Evans
71028f16a0fSKyle Evans return (ZPROP_CONT);
71128f16a0fSKyle Evans }
71228f16a0fSKyle Evans
713fa30d9edSKyle Evans /*
714fa30d9edSKyle Evans * Return the corresponding boot environment path for a given
715fa30d9edSKyle Evans * dataset path, the constructed path is placed in 'result'.
716fa30d9edSKyle Evans *
717fa30d9edSKyle Evans * example: say our new boot environment name is 'bootenv' and
718fa30d9edSKyle Evans * the dataset path is 'zroot/ROOT/default/data/set'.
719fa30d9edSKyle Evans *
720fa30d9edSKyle Evans * result should produce: 'zroot/ROOT/bootenv/data/set'
721fa30d9edSKyle Evans */
72228f16a0fSKyle Evans static int
be_get_path(struct libbe_deep_clone * ldc,const char * dspath,char * result,int result_size)723fa30d9edSKyle Evans be_get_path(struct libbe_deep_clone *ldc, const char *dspath, char *result, int result_size)
724fa30d9edSKyle Evans {
725fa30d9edSKyle Evans char *pos;
726fa30d9edSKyle Evans char *child_dataset;
727fa30d9edSKyle Evans
728fa30d9edSKyle Evans /* match the root path for the boot environments */
729fa30d9edSKyle Evans pos = strstr(dspath, ldc->lbh->root);
730fa30d9edSKyle Evans
731fa30d9edSKyle Evans /* no match, different pools? */
732fa30d9edSKyle Evans if (pos == NULL)
733fa30d9edSKyle Evans return (BE_ERR_BADPATH);
734fa30d9edSKyle Evans
735fa30d9edSKyle Evans /* root path of the new boot environment */
736fa30d9edSKyle Evans snprintf(result, result_size, "%s/%s", ldc->lbh->root, ldc->bename);
737fa30d9edSKyle Evans
738fa30d9edSKyle Evans /* gets us to the parent dataset, the +1 consumes a trailing slash */
739fa30d9edSKyle Evans pos += strlen(ldc->lbh->root) + 1;
740fa30d9edSKyle Evans
741fa30d9edSKyle Evans /* skip the parent dataset */
742fa30d9edSKyle Evans if ((child_dataset = strchr(pos, '/')) != NULL)
743fa30d9edSKyle Evans strlcat(result, child_dataset, result_size);
744fa30d9edSKyle Evans
745fa30d9edSKyle Evans return (BE_ERR_SUCCESS);
746fa30d9edSKyle Evans }
747fa30d9edSKyle Evans
748fa30d9edSKyle Evans static int
be_clone_cb(zfs_handle_t * ds,void * data)749fa30d9edSKyle Evans be_clone_cb(zfs_handle_t *ds, void *data)
75028f16a0fSKyle Evans {
75128f16a0fSKyle Evans int err;
75228f16a0fSKyle Evans char be_path[BE_MAXPATHLEN];
75328f16a0fSKyle Evans char snap_path[BE_MAXPATHLEN];
75428f16a0fSKyle Evans const char *dspath;
75528f16a0fSKyle Evans zfs_handle_t *snap_hdl;
75628f16a0fSKyle Evans nvlist_t *props;
757fa30d9edSKyle Evans struct libbe_deep_clone *ldc;
75828f16a0fSKyle Evans struct libbe_dccb dccb;
75928f16a0fSKyle Evans
760fa30d9edSKyle Evans ldc = (struct libbe_deep_clone *)data;
76128f16a0fSKyle Evans dspath = zfs_get_name(ds);
762bfe0869cSKyle Evans
763fa30d9edSKyle Evans snprintf(snap_path, sizeof(snap_path), "%s@%s", dspath, ldc->snapname);
764bfe0869cSKyle Evans
765fa30d9edSKyle Evans /* construct the boot environment path from the dataset we're cloning */
766fa30d9edSKyle Evans if (be_get_path(ldc, dspath, be_path, sizeof(be_path)) != BE_ERR_SUCCESS)
7678e933d9cSJohn Grafton return (BE_ERR_UNKNOWN);
76828f16a0fSKyle Evans
769fa30d9edSKyle Evans /* the dataset to be created (i.e. the boot environment) already exists */
770fa30d9edSKyle Evans if (zfs_dataset_exists(ldc->lbh->lzh, be_path, ZFS_TYPE_DATASET))
7718e933d9cSJohn Grafton return (BE_ERR_EXISTS);
772fa30d9edSKyle Evans
773fa30d9edSKyle Evans /* no snapshot found for this dataset, silently skip it */
774fa30d9edSKyle Evans if (!zfs_dataset_exists(ldc->lbh->lzh, snap_path, ZFS_TYPE_SNAPSHOT))
775fa30d9edSKyle Evans return (0);
77628f16a0fSKyle Evans
77728f16a0fSKyle Evans if ((snap_hdl =
778fa30d9edSKyle Evans zfs_open(ldc->lbh->lzh, snap_path, ZFS_TYPE_SNAPSHOT)) == NULL)
7798e933d9cSJohn Grafton return (BE_ERR_ZFSOPEN);
78028f16a0fSKyle Evans
78128f16a0fSKyle Evans nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP);
78228f16a0fSKyle Evans nvlist_add_string(props, "canmount", "noauto");
78328f16a0fSKyle Evans
784fa30d9edSKyle Evans dccb.lbh = ldc->lbh;
78528f16a0fSKyle Evans dccb.zhp = ds;
78628f16a0fSKyle Evans dccb.props = props;
78728f16a0fSKyle Evans if (zprop_iter(be_deep_clone_prop, &dccb, B_FALSE, B_FALSE,
788bfe0869cSKyle Evans ZFS_TYPE_FILESYSTEM) == ZPROP_INVAL)
78928f16a0fSKyle Evans return (-1);
79028f16a0fSKyle Evans
791cc4deabcSKyle Evans if ((err = zfs_clone(snap_hdl, be_path, props)) != 0)
7928e933d9cSJohn Grafton return (BE_ERR_ZFSCLONE);
79328f16a0fSKyle Evans
79428f16a0fSKyle Evans nvlist_free(props);
79528f16a0fSKyle Evans zfs_close(snap_hdl);
79628f16a0fSKyle Evans
797fa30d9edSKyle Evans if (ldc->depth_limit == -1 || ldc->depth < ldc->depth_limit) {
798fa30d9edSKyle Evans ldc->depth++;
799d411c1d6SMartin Matuska err = zfs_iter_filesystems(ds, be_clone_cb, ldc);
800fa30d9edSKyle Evans ldc->depth--;
801fa30d9edSKyle Evans }
802cc4deabcSKyle Evans
8038e933d9cSJohn Grafton return (err);
80428f16a0fSKyle Evans }
80528f16a0fSKyle Evans
80628f16a0fSKyle Evans /*
807fa30d9edSKyle Evans * Create a boot environment with a given name from a given snapshot.
808fa30d9edSKyle Evans * Snapshots can be in the format 'zroot/ROOT/default@snapshot' or
809fa30d9edSKyle Evans * 'default@snapshot'. In the latter case, 'default@snapshot' will be prepended
810fa30d9edSKyle Evans * with the root path that libbe was initailized with.
81128f16a0fSKyle Evans */
812fa30d9edSKyle Evans static int
be_clone(libbe_handle_t * lbh,const char * bename,const char * snapshot,int depth)813fa30d9edSKyle Evans be_clone(libbe_handle_t *lbh, const char *bename, const char *snapshot, int depth)
81428f16a0fSKyle Evans {
81528f16a0fSKyle Evans int err;
81628f16a0fSKyle Evans char snap_path[BE_MAXPATHLEN];
817b29bf2f8SKyle Evans char *parentname, *snapname;
81828f16a0fSKyle Evans zfs_handle_t *parent_hdl;
819fa30d9edSKyle Evans struct libbe_deep_clone ldc;
82028f16a0fSKyle Evans
821fa30d9edSKyle Evans /* ensure the boot environment name is valid */
822fa30d9edSKyle Evans if ((err = be_validate_name(lbh, bename)) != 0)
82328f16a0fSKyle Evans return (set_error(lbh, err));
824fa30d9edSKyle Evans
825fa30d9edSKyle Evans /*
826fa30d9edSKyle Evans * prepend the boot environment root path if we're
827fa30d9edSKyle Evans * given a partial snapshot name.
828fa30d9edSKyle Evans */
829fa30d9edSKyle Evans if ((err = be_root_concat(lbh, snapshot, snap_path)) != 0)
83028f16a0fSKyle Evans return (set_error(lbh, err));
831fa30d9edSKyle Evans
832fa30d9edSKyle Evans /* ensure the snapshot exists */
833b29bf2f8SKyle Evans if ((err = be_validate_snap(lbh, snap_path)) != 0)
83428f16a0fSKyle Evans return (set_error(lbh, err));
83528f16a0fSKyle Evans
836fa30d9edSKyle Evans /* get a copy of the snapshot path so we can disect it */
837cc4deabcSKyle Evans if ((parentname = strdup(snap_path)) == NULL)
838cc4deabcSKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN));
839cc4deabcSKyle Evans
840fa30d9edSKyle Evans /* split dataset name from snapshot name */
84128f16a0fSKyle Evans snapname = strchr(parentname, '@');
84228f16a0fSKyle Evans if (snapname == NULL) {
843cc4deabcSKyle Evans free(parentname);
844cc4deabcSKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN));
84528f16a0fSKyle Evans }
84628f16a0fSKyle Evans *snapname = '\0';
84728f16a0fSKyle Evans snapname++;
84828f16a0fSKyle Evans
849fa30d9edSKyle Evans /* set-up the boot environment */
850fa30d9edSKyle Evans ldc.lbh = lbh;
851fa30d9edSKyle Evans ldc.bename = bename;
852fa30d9edSKyle Evans ldc.snapname = snapname;
853fa30d9edSKyle Evans ldc.depth = 0;
854fa30d9edSKyle Evans ldc.depth_limit = depth;
85528f16a0fSKyle Evans
856fa30d9edSKyle Evans /* the boot environment will be cloned from this dataset */
85728f16a0fSKyle Evans parent_hdl = zfs_open(lbh->lzh, parentname, ZFS_TYPE_DATASET);
858fa30d9edSKyle Evans
859fa30d9edSKyle Evans /* create the boot environment */
860fa30d9edSKyle Evans err = be_clone_cb(parent_hdl, &ldc);
86128f16a0fSKyle Evans
862cc4deabcSKyle Evans free(parentname);
86328f16a0fSKyle Evans return (set_error(lbh, err));
86428f16a0fSKyle Evans }
86528f16a0fSKyle Evans
866fa30d9edSKyle Evans /*
867fa30d9edSKyle Evans * Create a boot environment from pre-existing snapshot, specifying a depth.
868fa30d9edSKyle Evans */
be_create_depth(libbe_handle_t * lbh,const char * bename,const char * snap,int depth)869fa30d9edSKyle Evans int be_create_depth(libbe_handle_t *lbh, const char *bename,
870fa30d9edSKyle Evans const char *snap, int depth)
871fa30d9edSKyle Evans {
872fa30d9edSKyle Evans return (be_clone(lbh, bename, snap, depth));
873fa30d9edSKyle Evans }
874fa30d9edSKyle Evans
875fa30d9edSKyle Evans /*
876fa30d9edSKyle Evans * Create the boot environment from pre-existing snapshot
877fa30d9edSKyle Evans */
878fa30d9edSKyle Evans int
be_create_from_existing_snap(libbe_handle_t * lbh,const char * bename,const char * snap)879fa30d9edSKyle Evans be_create_from_existing_snap(libbe_handle_t *lbh, const char *bename,
880fa30d9edSKyle Evans const char *snap)
881fa30d9edSKyle Evans {
882fa30d9edSKyle Evans return (be_clone(lbh, bename, snap, -1));
883fa30d9edSKyle Evans }
884fa30d9edSKyle Evans
88528f16a0fSKyle Evans
88628f16a0fSKyle Evans /*
88728f16a0fSKyle Evans * Create a boot environment from an existing boot environment
88828f16a0fSKyle Evans */
88928f16a0fSKyle Evans int
be_create_from_existing(libbe_handle_t * lbh,const char * bename,const char * old)890fa30d9edSKyle Evans be_create_from_existing(libbe_handle_t *lbh, const char *bename, const char *old)
89128f16a0fSKyle Evans {
89228f16a0fSKyle Evans int err;
893fa30d9edSKyle Evans char snap[BE_MAXPATHLEN];
89428f16a0fSKyle Evans
895fa30d9edSKyle Evans if ((err = be_snapshot(lbh, old, NULL, true, snap)) != 0)
89628f16a0fSKyle Evans return (set_error(lbh, err));
89728f16a0fSKyle Evans
898fa30d9edSKyle Evans err = be_clone(lbh, bename, snap, -1);
89928f16a0fSKyle Evans
90028f16a0fSKyle Evans return (set_error(lbh, err));
90128f16a0fSKyle Evans }
90228f16a0fSKyle Evans
90328f16a0fSKyle Evans
90428f16a0fSKyle Evans /*
90528f16a0fSKyle Evans * Verifies that a snapshot has a valid name, exists, and has a mountpoint of
90628f16a0fSKyle Evans * '/'. Returns BE_ERR_SUCCESS (0), upon success, or the relevant BE_ERR_* upon
90728f16a0fSKyle Evans * failure. Does not set the internal library error state.
90828f16a0fSKyle Evans */
90928f16a0fSKyle Evans int
be_validate_snap(libbe_handle_t * lbh,const char * snap_name)910b29bf2f8SKyle Evans be_validate_snap(libbe_handle_t *lbh, const char *snap_name)
91128f16a0fSKyle Evans {
91228f16a0fSKyle Evans
913bfe0869cSKyle Evans if (strlen(snap_name) >= BE_MAXPATHLEN)
91428f16a0fSKyle Evans return (BE_ERR_PATHLEN);
91528f16a0fSKyle Evans
916fcb47c42SKyle Evans if (!zfs_name_valid(snap_name, ZFS_TYPE_SNAPSHOT))
917fcb47c42SKyle Evans return (BE_ERR_INVALIDNAME);
918fcb47c42SKyle Evans
91928f16a0fSKyle Evans if (!zfs_dataset_exists(lbh->lzh, snap_name,
920bfe0869cSKyle Evans ZFS_TYPE_SNAPSHOT))
92128f16a0fSKyle Evans return (BE_ERR_NOENT);
92228f16a0fSKyle Evans
92351aecc89SKyle Evans return (BE_ERR_SUCCESS);
92428f16a0fSKyle Evans }
92528f16a0fSKyle Evans
92628f16a0fSKyle Evans
92728f16a0fSKyle Evans /*
92828f16a0fSKyle Evans * Idempotently appends the name argument to the root boot environment path
92928f16a0fSKyle Evans * and copies the resulting string into the result buffer (which is assumed
93028f16a0fSKyle Evans * to be at least BE_MAXPATHLEN characters long. Returns BE_ERR_SUCCESS upon
93128f16a0fSKyle Evans * success, BE_ERR_PATHLEN if the resulting path is longer than BE_MAXPATHLEN,
93228f16a0fSKyle Evans * or BE_ERR_INVALIDNAME if the name is a path that does not begin with
93328f16a0fSKyle Evans * zfs_be_root. Does not set internal library error state.
93428f16a0fSKyle Evans */
93528f16a0fSKyle Evans int
be_root_concat(libbe_handle_t * lbh,const char * name,char * result)936b29bf2f8SKyle Evans be_root_concat(libbe_handle_t *lbh, const char *name, char *result)
93728f16a0fSKyle Evans {
93828f16a0fSKyle Evans size_t name_len, root_len;
93928f16a0fSKyle Evans
94028f16a0fSKyle Evans name_len = strlen(name);
94128f16a0fSKyle Evans root_len = strlen(lbh->root);
94228f16a0fSKyle Evans
94328f16a0fSKyle Evans /* Act idempotently; return be name if it is already a full path */
94428f16a0fSKyle Evans if (strrchr(name, '/') != NULL) {
945bfe0869cSKyle Evans if (strstr(name, lbh->root) != name)
94628f16a0fSKyle Evans return (BE_ERR_INVALIDNAME);
94728f16a0fSKyle Evans
948bfe0869cSKyle Evans if (name_len >= BE_MAXPATHLEN)
94928f16a0fSKyle Evans return (BE_ERR_PATHLEN);
95028f16a0fSKyle Evans
95155b0e92bSKyle Evans strlcpy(result, name, BE_MAXPATHLEN);
95228f16a0fSKyle Evans return (BE_ERR_SUCCESS);
95328f16a0fSKyle Evans } else if (name_len + root_len + 1 < BE_MAXPATHLEN) {
95428f16a0fSKyle Evans snprintf(result, BE_MAXPATHLEN, "%s/%s", lbh->root,
95528f16a0fSKyle Evans name);
95628f16a0fSKyle Evans return (BE_ERR_SUCCESS);
95728f16a0fSKyle Evans }
95828f16a0fSKyle Evans
95928f16a0fSKyle Evans return (BE_ERR_PATHLEN);
96028f16a0fSKyle Evans }
96128f16a0fSKyle Evans
96228f16a0fSKyle Evans
96328f16a0fSKyle Evans /*
96428f16a0fSKyle Evans * Verifies the validity of a boot environment name (A-Za-z0-9-_.). Returns
9655b7803a9SKyle Evans * BE_ERR_SUCCESS (0) if name is valid, otherwise returns BE_ERR_INVALIDNAME
9665b7803a9SKyle Evans * or BE_ERR_PATHLEN.
96728f16a0fSKyle Evans * Does not set internal library error state.
96828f16a0fSKyle Evans */
96928f16a0fSKyle Evans int
be_validate_name(libbe_handle_t * lbh,const char * name)9705b7803a9SKyle Evans be_validate_name(libbe_handle_t *lbh, const char *name)
97128f16a0fSKyle Evans {
97228f16a0fSKyle Evans
9735b7803a9SKyle Evans /*
9745b7803a9SKyle Evans * Impose the additional restriction that the entire dataset name must
9755b7803a9SKyle Evans * not exceed the maximum length of a dataset, i.e. MAXNAMELEN.
9765b7803a9SKyle Evans */
9775b7803a9SKyle Evans if (strlen(lbh->root) + 1 + strlen(name) > MAXNAMELEN)
9785b7803a9SKyle Evans return (BE_ERR_PATHLEN);
979fcb47c42SKyle Evans
980fcb47c42SKyle Evans if (!zfs_name_valid(name, ZFS_TYPE_DATASET))
981fcb47c42SKyle Evans return (BE_ERR_INVALIDNAME);
982fcb47c42SKyle Evans
983dadb9c70SKyle Evans /*
984dadb9c70SKyle Evans * ZFS allows spaces in boot environment names, but the kernel can't
985dadb9c70SKyle Evans * handle booting from such a dataset right now. vfs.root.mountfrom
986dadb9c70SKyle Evans * is defined to be a space-separated list, and there's no protocol for
987dadb9c70SKyle Evans * escaping whitespace in the path component of a dev:path spec. So
988dadb9c70SKyle Evans * while loader can handle this situation alright, it can't safely pass
989dadb9c70SKyle Evans * it on to mountroot.
990dadb9c70SKyle Evans */
991dadb9c70SKyle Evans if (strchr(name, ' ') != NULL)
992dadb9c70SKyle Evans return (BE_ERR_INVALIDNAME);
993dadb9c70SKyle Evans
99428f16a0fSKyle Evans return (BE_ERR_SUCCESS);
99528f16a0fSKyle Evans }
99628f16a0fSKyle Evans
99728f16a0fSKyle Evans
99828f16a0fSKyle Evans /*
99928f16a0fSKyle Evans * usage
100028f16a0fSKyle Evans */
100128f16a0fSKyle Evans int
be_rename(libbe_handle_t * lbh,const char * old,const char * new)100273c3d608SKyle Evans be_rename(libbe_handle_t *lbh, const char *old, const char *new)
100328f16a0fSKyle Evans {
100428f16a0fSKyle Evans char full_old[BE_MAXPATHLEN];
100528f16a0fSKyle Evans char full_new[BE_MAXPATHLEN];
100628f16a0fSKyle Evans zfs_handle_t *zfs_hdl;
100728f16a0fSKyle Evans int err;
100828f16a0fSKyle Evans
10095b7803a9SKyle Evans /*
10105b7803a9SKyle Evans * be_validate_name is documented not to set error state, so we should
10115b7803a9SKyle Evans * do so here.
10125b7803a9SKyle Evans */
10135b7803a9SKyle Evans if ((err = be_validate_name(lbh, new)) != 0)
10145b7803a9SKyle Evans return (set_error(lbh, err));
1015b29bf2f8SKyle Evans if ((err = be_root_concat(lbh, old, full_old)) != 0)
101628f16a0fSKyle Evans return (set_error(lbh, err));
1017b29bf2f8SKyle Evans if ((err = be_root_concat(lbh, new, full_new)) != 0)
101828f16a0fSKyle Evans return (set_error(lbh, err));
101928f16a0fSKyle Evans
1020bfe0869cSKyle Evans if (!zfs_dataset_exists(lbh->lzh, full_old, ZFS_TYPE_DATASET))
10212989df09SKyle Evans return (set_error(lbh, BE_ERR_NOENT));
102228f16a0fSKyle Evans
1023bfe0869cSKyle Evans if (zfs_dataset_exists(lbh->lzh, full_new, ZFS_TYPE_DATASET))
10242989df09SKyle Evans return (set_error(lbh, BE_ERR_EXISTS));
102528f16a0fSKyle Evans
102628f16a0fSKyle Evans if ((zfs_hdl = zfs_open(lbh->lzh, full_old,
1027bfe0869cSKyle Evans ZFS_TYPE_FILESYSTEM)) == NULL)
10282989df09SKyle Evans return (set_error(lbh, BE_ERR_ZFSOPEN));
102928f16a0fSKyle Evans
1030eac7052fSMatt Macy /* recurse, nounmount, forceunmount */
1031eac7052fSMatt Macy struct renameflags flags = {
1032eac7052fSMatt Macy .nounmount = 1,
1033eac7052fSMatt Macy };
1034eac7052fSMatt Macy err = zfs_rename(zfs_hdl, full_new, flags);
10355b7803a9SKyle Evans if (err != 0)
10365086b6ecSR. Christian McDonald goto error;
10375086b6ecSR. Christian McDonald
10385086b6ecSR. Christian McDonald /* handle renaming bootonce */
10395086b6ecSR. Christian McDonald if (lbh->bootonce != NULL &&
10405086b6ecSR. Christian McDonald strcmp(full_old, lbh->bootonce) == 0)
10415086b6ecSR. Christian McDonald err = be_activate(lbh, new, true);
10425086b6ecSR. Christian McDonald
10435086b6ecSR. Christian McDonald error:
10445086b6ecSR. Christian McDonald zfs_close(zfs_hdl);
10455086b6ecSR. Christian McDonald return (set_error(lbh, err));
104628f16a0fSKyle Evans }
104728f16a0fSKyle Evans
104828f16a0fSKyle Evans
104928f16a0fSKyle Evans int
be_export(libbe_handle_t * lbh,const char * bootenv,int fd)105073c3d608SKyle Evans be_export(libbe_handle_t *lbh, const char *bootenv, int fd)
105128f16a0fSKyle Evans {
105228f16a0fSKyle Evans char snap_name[BE_MAXPATHLEN];
105328f16a0fSKyle Evans char buf[BE_MAXPATHLEN];
105428f16a0fSKyle Evans zfs_handle_t *zfs;
10558569a95eSAndriy Gapon sendflags_t flags = { 0 };
105628f16a0fSKyle Evans int err;
105728f16a0fSKyle Evans
1058b29bf2f8SKyle Evans if ((err = be_snapshot(lbh, bootenv, NULL, true, snap_name)) != 0)
10596d4b1d24SKyle Evans /* Use the error set by be_snapshot */
10606d4b1d24SKyle Evans return (err);
106128f16a0fSKyle Evans
106228f16a0fSKyle Evans be_root_concat(lbh, snap_name, buf);
106328f16a0fSKyle Evans
1064bfe0869cSKyle Evans if ((zfs = zfs_open(lbh->lzh, buf, ZFS_TYPE_DATASET)) == NULL)
1065506f5fdfSKyle Evans return (set_error(lbh, BE_ERR_ZFSOPEN));
106628f16a0fSKyle Evans
10679e5787d2SMatt Macy err = zfs_send_one(zfs, NULL, fd, &flags, /* redactbook */ NULL);
10686d4b1d24SKyle Evans zfs_close(zfs);
10696d4b1d24SKyle Evans
107028f16a0fSKyle Evans return (err);
107128f16a0fSKyle Evans }
107228f16a0fSKyle Evans
107328f16a0fSKyle Evans
107428f16a0fSKyle Evans int
be_import(libbe_handle_t * lbh,const char * bootenv,int fd)107573c3d608SKyle Evans be_import(libbe_handle_t *lbh, const char *bootenv, int fd)
107628f16a0fSKyle Evans {
107728f16a0fSKyle Evans char buf[BE_MAXPATHLEN];
107828f16a0fSKyle Evans nvlist_t *props;
107928f16a0fSKyle Evans zfs_handle_t *zfs;
108016ac0705SKyle Evans recvflags_t flags = { .nomount = 1 };
108116ac0705SKyle Evans int err;
108228f16a0fSKyle Evans
108316ac0705SKyle Evans be_root_concat(lbh, bootenv, buf);
108428f16a0fSKyle Evans
108516ac0705SKyle Evans if ((err = zfs_receive(lbh->lzh, buf, NULL, &flags, fd, NULL)) != 0) {
1086506f5fdfSKyle Evans switch (err) {
1087506f5fdfSKyle Evans case EINVAL:
1088506f5fdfSKyle Evans return (set_error(lbh, BE_ERR_NOORIGIN));
1089506f5fdfSKyle Evans case ENOENT:
1090506f5fdfSKyle Evans return (set_error(lbh, BE_ERR_NOENT));
1091506f5fdfSKyle Evans case EIO:
1092506f5fdfSKyle Evans return (set_error(lbh, BE_ERR_IO));
1093506f5fdfSKyle Evans default:
1094506f5fdfSKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN));
1095506f5fdfSKyle Evans }
109628f16a0fSKyle Evans }
109728f16a0fSKyle Evans
109816ac0705SKyle Evans if ((zfs = zfs_open(lbh->lzh, buf, ZFS_TYPE_FILESYSTEM)) == NULL)
1099506f5fdfSKyle Evans return (set_error(lbh, BE_ERR_ZFSOPEN));
110028f16a0fSKyle Evans
110128f16a0fSKyle Evans nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP);
110228f16a0fSKyle Evans nvlist_add_string(props, "canmount", "noauto");
1103ac34fe23SKyle Evans nvlist_add_string(props, "mountpoint", "none");
110428f16a0fSKyle Evans
110516ac0705SKyle Evans err = zfs_prop_set_list(zfs, props);
110628f16a0fSKyle Evans nvlist_free(props);
110728f16a0fSKyle Evans
11081b057aacSKyle Evans zfs_close(zfs);
11091b057aacSKyle Evans
11101b057aacSKyle Evans if (err != 0)
11111b057aacSKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN));
11121b057aacSKyle Evans
111316ac0705SKyle Evans return (0);
111428f16a0fSKyle Evans }
111528f16a0fSKyle Evans
11163d1a1f2cSKyle Evans #if SOON
1117c65a2111SKyle Evans static int
be_create_child_noent(libbe_handle_t * lbh,const char * active,const char * child_path)1118c65a2111SKyle Evans be_create_child_noent(libbe_handle_t *lbh, const char *active,
1119c65a2111SKyle Evans const char *child_path)
1120c65a2111SKyle Evans {
1121c65a2111SKyle Evans nvlist_t *props;
1122c65a2111SKyle Evans zfs_handle_t *zfs;
1123c65a2111SKyle Evans int err;
1124c65a2111SKyle Evans
1125c65a2111SKyle Evans nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP);
1126c65a2111SKyle Evans nvlist_add_string(props, "canmount", "noauto");
1127c65a2111SKyle Evans nvlist_add_string(props, "mountpoint", child_path);
1128c65a2111SKyle Evans
1129c65a2111SKyle Evans /* Create */
1130c65a2111SKyle Evans if ((err = zfs_create(lbh->lzh, active, ZFS_TYPE_DATASET,
1131c65a2111SKyle Evans props)) != 0) {
1132c65a2111SKyle Evans switch (err) {
1133c65a2111SKyle Evans case EZFS_EXISTS:
1134c65a2111SKyle Evans return (set_error(lbh, BE_ERR_EXISTS));
1135c65a2111SKyle Evans case EZFS_NOENT:
1136c65a2111SKyle Evans return (set_error(lbh, BE_ERR_NOENT));
1137c65a2111SKyle Evans case EZFS_BADTYPE:
1138c65a2111SKyle Evans case EZFS_BADVERSION:
1139c65a2111SKyle Evans return (set_error(lbh, BE_ERR_NOPOOL));
1140c65a2111SKyle Evans case EZFS_BADPROP:
1141c65a2111SKyle Evans default:
1142c65a2111SKyle Evans /* We set something up wrong, probably... */
1143c65a2111SKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN));
1144c65a2111SKyle Evans }
1145c65a2111SKyle Evans }
1146c65a2111SKyle Evans nvlist_free(props);
1147c65a2111SKyle Evans
1148c65a2111SKyle Evans if ((zfs = zfs_open(lbh->lzh, active, ZFS_TYPE_DATASET)) == NULL)
1149c65a2111SKyle Evans return (set_error(lbh, BE_ERR_ZFSOPEN));
1150c65a2111SKyle Evans
1151c65a2111SKyle Evans /* Set props */
1152c65a2111SKyle Evans if ((err = zfs_prop_set(zfs, "canmount", "noauto")) != 0) {
1153c65a2111SKyle Evans zfs_close(zfs);
1154c65a2111SKyle Evans /*
1155c65a2111SKyle Evans * Similar to other cases, this shouldn't fail unless we've
1156c65a2111SKyle Evans * done something wrong. This is a new dataset that shouldn't
1157c65a2111SKyle Evans * have been mounted anywhere between creation and now.
1158c65a2111SKyle Evans */
1159c65a2111SKyle Evans if (err == EZFS_NOMEM)
1160c65a2111SKyle Evans return (set_error(lbh, BE_ERR_NOMEM));
1161c65a2111SKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN));
1162c65a2111SKyle Evans }
1163c65a2111SKyle Evans zfs_close(zfs);
1164c65a2111SKyle Evans return (BE_ERR_SUCCESS);
1165c65a2111SKyle Evans }
1166c65a2111SKyle Evans
1167c65a2111SKyle Evans static int
be_create_child_cloned(libbe_handle_t * lbh,const char * active)1168c65a2111SKyle Evans be_create_child_cloned(libbe_handle_t *lbh, const char *active)
1169c65a2111SKyle Evans {
1170*b139f2eeSrilysh char buf[BE_MAXPATHLEN], tmp[BE_MAXPATHLEN];
1171c65a2111SKyle Evans zfs_handle_t *zfs;
1172c65a2111SKyle Evans int err;
1173c65a2111SKyle Evans
1174c65a2111SKyle Evans /* XXX TODO ? */
1175c65a2111SKyle Evans
1176c65a2111SKyle Evans /*
1177c65a2111SKyle Evans * Establish if the existing path is a zfs dataset or just
1178c65a2111SKyle Evans * the subdirectory of one
1179c65a2111SKyle Evans */
11803d1a1f2cSKyle Evans strlcpy(tmp, "tmp/be_snap.XXXXX", sizeof(tmp));
11813d1a1f2cSKyle Evans if (mktemp(tmp) == NULL)
1182c65a2111SKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN));
1183c65a2111SKyle Evans
11843d1a1f2cSKyle Evans be_root_concat(lbh, tmp, buf);
11853d1a1f2cSKyle Evans printf("Here %s?\n", buf);
1186c65a2111SKyle Evans if ((err = zfs_snapshot(lbh->lzh, buf, false, NULL)) != 0) {
1187c65a2111SKyle Evans switch (err) {
1188c65a2111SKyle Evans case EZFS_INVALIDNAME:
1189c65a2111SKyle Evans return (set_error(lbh, BE_ERR_INVALIDNAME));
1190c65a2111SKyle Evans
1191c65a2111SKyle Evans default:
1192c65a2111SKyle Evans /*
1193c65a2111SKyle Evans * The other errors that zfs_ioc_snapshot might return
1194c65a2111SKyle Evans * shouldn't happen if we've set things up properly, so
1195c65a2111SKyle Evans * we'll gloss over them and call it UNKNOWN as it will
1196c65a2111SKyle Evans * require further triage.
1197c65a2111SKyle Evans */
1198c65a2111SKyle Evans if (errno == ENOTSUP)
1199c65a2111SKyle Evans return (set_error(lbh, BE_ERR_NOPOOL));
1200c65a2111SKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN));
1201c65a2111SKyle Evans }
1202c65a2111SKyle Evans }
1203c65a2111SKyle Evans
1204c65a2111SKyle Evans /* Clone */
1205c65a2111SKyle Evans if ((zfs = zfs_open(lbh->lzh, buf, ZFS_TYPE_SNAPSHOT)) == NULL)
1206c65a2111SKyle Evans return (BE_ERR_ZFSOPEN);
1207c65a2111SKyle Evans
1208c65a2111SKyle Evans if ((err = zfs_clone(zfs, active, NULL)) != 0)
1209c65a2111SKyle Evans /* XXX TODO correct error */
1210c65a2111SKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN));
1211c65a2111SKyle Evans
1212c65a2111SKyle Evans /* set props */
1213c65a2111SKyle Evans zfs_close(zfs);
1214c65a2111SKyle Evans return (BE_ERR_SUCCESS);
1215c65a2111SKyle Evans }
121628f16a0fSKyle Evans
121728f16a0fSKyle Evans int
be_add_child(libbe_handle_t * lbh,const char * child_path,bool cp_if_exists)121873c3d608SKyle Evans be_add_child(libbe_handle_t *lbh, const char *child_path, bool cp_if_exists)
121928f16a0fSKyle Evans {
122073c3d608SKyle Evans struct stat sb;
1221c65a2111SKyle Evans char active[BE_MAXPATHLEN], buf[BE_MAXPATHLEN];
122228f16a0fSKyle Evans nvlist_t *props;
122373c3d608SKyle Evans const char *s;
122428f16a0fSKyle Evans
122528f16a0fSKyle Evans /* Require absolute paths */
1226bfe0869cSKyle Evans if (*child_path != '/')
12276d4b1d24SKyle Evans return (set_error(lbh, BE_ERR_BADPATH));
122828f16a0fSKyle Evans
12296d4b1d24SKyle Evans strlcpy(active, be_active_path(lbh), BE_MAXPATHLEN);
123028f16a0fSKyle Evans strcpy(buf, active);
123128f16a0fSKyle Evans
123228f16a0fSKyle Evans /* Create non-mountable parent dataset(s) */
123373c3d608SKyle Evans s = child_path;
123428f16a0fSKyle Evans for (char *p; (p = strchr(s+1, '/')) != NULL; s = p) {
123528f16a0fSKyle Evans size_t len = p - s;
123628f16a0fSKyle Evans strncat(buf, s, len);
123728f16a0fSKyle Evans
123828f16a0fSKyle Evans nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP);
123928f16a0fSKyle Evans nvlist_add_string(props, "canmount", "off");
124028f16a0fSKyle Evans nvlist_add_string(props, "mountpoint", "none");
124128f16a0fSKyle Evans zfs_create(lbh->lzh, buf, ZFS_TYPE_DATASET, props);
124228f16a0fSKyle Evans nvlist_free(props);
124328f16a0fSKyle Evans }
124428f16a0fSKyle Evans
124528f16a0fSKyle Evans /* Path does not exist as a descendent of / yet */
12466d4b1d24SKyle Evans if (strlcat(active, child_path, BE_MAXPATHLEN) >= BE_MAXPATHLEN)
12476d4b1d24SKyle Evans return (set_error(lbh, BE_ERR_PATHLEN));
124828f16a0fSKyle Evans
124928f16a0fSKyle Evans if (stat(child_path, &sb) != 0) {
125028f16a0fSKyle Evans /* Verify that error is ENOENT */
12516d4b1d24SKyle Evans if (errno != ENOENT)
12526d4b1d24SKyle Evans return (set_error(lbh, BE_ERR_UNKNOWN));
1253c65a2111SKyle Evans return (be_create_child_noent(lbh, active, child_path));
1254c65a2111SKyle Evans } else if (cp_if_exists)
125528f16a0fSKyle Evans /* Path is already a descendent of / and should be copied */
1256c65a2111SKyle Evans return (be_create_child_cloned(lbh, active));
12576d4b1d24SKyle Evans return (set_error(lbh, BE_ERR_EXISTS));
125828f16a0fSKyle Evans }
12593d1a1f2cSKyle Evans #endif /* SOON */
126028f16a0fSKyle Evans
12618d4ce358SKyle Evans /*
1262e307eb94SToomas Soome * Deactivate old BE dataset; currently just sets canmount=noauto or
1263e307eb94SToomas Soome * resets boot once configuration.
12648d4ce358SKyle Evans */
1265e307eb94SToomas Soome int
be_deactivate(libbe_handle_t * lbh,const char * ds,bool temporary)1266e307eb94SToomas Soome be_deactivate(libbe_handle_t *lbh, const char *ds, bool temporary)
12678d4ce358SKyle Evans {
12688d4ce358SKyle Evans zfs_handle_t *zfs;
12698d4ce358SKyle Evans
1270e307eb94SToomas Soome if (temporary) {
1271e307eb94SToomas Soome return (lzbe_set_boot_device(
1272e307eb94SToomas Soome zpool_get_name(lbh->active_phandle), lzbe_add, NULL));
1273e307eb94SToomas Soome }
1274e307eb94SToomas Soome
12758d4ce358SKyle Evans if ((zfs = zfs_open(lbh->lzh, ds, ZFS_TYPE_DATASET)) == NULL)
12768d4ce358SKyle Evans return (1);
12778d4ce358SKyle Evans if (zfs_prop_set(zfs, "canmount", "noauto") != 0)
12788d4ce358SKyle Evans return (1);
12798d4ce358SKyle Evans zfs_close(zfs);
12808d4ce358SKyle Evans return (0);
12818d4ce358SKyle Evans }
128228f16a0fSKyle Evans
12834b426cf3SR. Christian McDonald static int
be_zfs_promote_cb(zfs_handle_t * zhp,void * data)12844b426cf3SR. Christian McDonald be_zfs_promote_cb(zfs_handle_t *zhp, void *data)
12854b426cf3SR. Christian McDonald {
12864b426cf3SR. Christian McDonald char origin[BE_MAXPATHLEN];
12874b426cf3SR. Christian McDonald bool *found_origin = (bool *)data;
12884b426cf3SR. Christian McDonald int err;
12894b426cf3SR. Christian McDonald
12904b426cf3SR. Christian McDonald if (zfs_prop_get(zhp, ZFS_PROP_ORIGIN, origin, sizeof(origin),
12914b426cf3SR. Christian McDonald NULL, NULL, 0, true) == 0) {
12924b426cf3SR. Christian McDonald *found_origin = true;
12934b426cf3SR. Christian McDonald err = zfs_promote(zhp);
12944b426cf3SR. Christian McDonald if (err)
12954b426cf3SR. Christian McDonald return (err);
12964b426cf3SR. Christian McDonald }
12974b426cf3SR. Christian McDonald
12984b426cf3SR. Christian McDonald return (zfs_iter_filesystems(zhp, be_zfs_promote_cb, data));
12994b426cf3SR. Christian McDonald }
13004b426cf3SR. Christian McDonald
13014b426cf3SR. Christian McDonald static int
be_zfs_promote(zfs_handle_t * zhp,bool * found_origin)13024b426cf3SR. Christian McDonald be_zfs_promote(zfs_handle_t *zhp, bool *found_origin)
13034b426cf3SR. Christian McDonald {
13044b426cf3SR. Christian McDonald *found_origin = false;
13054b426cf3SR. Christian McDonald return (be_zfs_promote_cb(zhp, (void *)found_origin));
13064b426cf3SR. Christian McDonald }
13074b426cf3SR. Christian McDonald
130828f16a0fSKyle Evans int
be_activate(libbe_handle_t * lbh,const char * bootenv,bool temporary)130973c3d608SKyle Evans be_activate(libbe_handle_t *lbh, const char *bootenv, bool temporary)
131028f16a0fSKyle Evans {
13114b426cf3SR. Christian McDonald char be_path[BE_MAXPATHLEN];
13120cadc427SKyle Evans zfs_handle_t *zhp;
131328f16a0fSKyle Evans int err;
13144b426cf3SR. Christian McDonald bool found_origin;
131528f16a0fSKyle Evans
131628f16a0fSKyle Evans be_root_concat(lbh, bootenv, be_path);
131728f16a0fSKyle Evans
131828f16a0fSKyle Evans /* Note: be_exists fails if mountpoint is not / */
1319162ec569SKyle Evans if ((err = be_exists(lbh, be_path)) != 0)
1320162ec569SKyle Evans return (set_error(lbh, err));
132128f16a0fSKyle Evans
132228f16a0fSKyle Evans if (temporary) {
1323e307eb94SToomas Soome return (lzbe_set_boot_device(
1324e307eb94SToomas Soome zpool_get_name(lbh->active_phandle), lzbe_add, be_path));
132528f16a0fSKyle Evans } else {
13267edc1bd9SGleb Smirnoff if (strncmp(lbh->bootfs, "-", 1) != 0 &&
13277edc1bd9SGleb Smirnoff be_deactivate(lbh, lbh->bootfs, false) != 0)
13288d4ce358SKyle Evans return (-1);
13298d4ce358SKyle Evans
133028f16a0fSKyle Evans /* Obtain bootenv zpool */
1331c3a34c08SKyle Evans err = zpool_set_prop(lbh->active_phandle, "bootfs", be_path);
13320cadc427SKyle Evans if (err)
13330cadc427SKyle Evans return (-1);
133428f16a0fSKyle Evans
13355d082601SKyle Evans for (;;) {
13360cadc427SKyle Evans zhp = zfs_open(lbh->lzh, be_path, ZFS_TYPE_FILESYSTEM);
13370cadc427SKyle Evans if (zhp == NULL)
13380cadc427SKyle Evans return (-1);
133928f16a0fSKyle Evans
13404b426cf3SR. Christian McDonald err = be_zfs_promote(zhp, &found_origin);
13414635676dSKyle Evans
13420cadc427SKyle Evans zfs_close(zhp);
13434b426cf3SR. Christian McDonald if (!found_origin)
13444b426cf3SR. Christian McDonald break;
13455d082601SKyle Evans if (err)
13464b426cf3SR. Christian McDonald return (err);
13475d082601SKyle Evans }
13485d082601SKyle Evans
13490cadc427SKyle Evans if (err)
135028f16a0fSKyle Evans return (-1);
135128f16a0fSKyle Evans }
13520cadc427SKyle Evans
13530cadc427SKyle Evans return (BE_ERR_SUCCESS);
135428f16a0fSKyle Evans }
1355