xref: /freebsd/lib/libbe/be.c (revision b139f2ee9939b5a93afb904942f67123c3985dc7)
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