xref: /freebsd/lib/libbe/be.c (revision be13d48c667e524bf28106f62117919b318b65f7)
1b179da01SKyle Evans /*-
2b179da01SKyle Evans  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
328f16a0fSKyle Evans  *
428f16a0fSKyle Evans  * Copyright (c) 2017 Kyle J. Kneitinger <kyle@kneit.in>
528f16a0fSKyle Evans  * All rights reserved.
628f16a0fSKyle Evans  *
728f16a0fSKyle Evans  * Redistribution and use in source and binary forms, with or without
828f16a0fSKyle Evans  * modification, are permitted provided that the following conditions
928f16a0fSKyle Evans  * are met:
1028f16a0fSKyle Evans  * 1. Redistributions of source code must retain the above copyright
1128f16a0fSKyle Evans  *    notice, this list of conditions and the following disclaimer.
1228f16a0fSKyle Evans  * 2. Redistributions in binary form must reproduce the above copyright
1328f16a0fSKyle Evans  *    notice, this list of conditions and the following disclaimer in the
1428f16a0fSKyle Evans  *    documentation and/or other materials provided with the distribution.
1528f16a0fSKyle Evans  *
1628f16a0fSKyle Evans  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
1728f16a0fSKyle Evans  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1828f16a0fSKyle Evans  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1928f16a0fSKyle Evans  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2028f16a0fSKyle Evans  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2128f16a0fSKyle Evans  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2228f16a0fSKyle Evans  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2328f16a0fSKyle Evans  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2428f16a0fSKyle Evans  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2528f16a0fSKyle Evans  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2628f16a0fSKyle Evans  * SUCH DAMAGE.
2728f16a0fSKyle Evans  */
2828f16a0fSKyle Evans 
29b6e7c421SKyle Evans #include <sys/cdefs.h>
30b6e7c421SKyle Evans __FBSDID("$FreeBSD$");
31b6e7c421SKyle Evans 
3251aecc89SKyle Evans #include <sys/param.h>
3351aecc89SKyle Evans #include <sys/mount.h>
3428f16a0fSKyle Evans #include <sys/stat.h>
3551aecc89SKyle Evans #include <sys/ucred.h>
3628f16a0fSKyle Evans 
3728f16a0fSKyle Evans #include <ctype.h>
3828f16a0fSKyle Evans #include <libgen.h>
3928f16a0fSKyle Evans #include <libzfs_core.h>
4028f16a0fSKyle Evans #include <stdio.h>
4128f16a0fSKyle Evans #include <stdlib.h>
4228f16a0fSKyle Evans #include <time.h>
4328f16a0fSKyle Evans #include <unistd.h>
4428f16a0fSKyle Evans 
4528f16a0fSKyle Evans #include "be.h"
4628f16a0fSKyle Evans #include "be_impl.h"
4728f16a0fSKyle Evans 
48be7dd423SKyle Evans struct be_destroy_data {
49be7dd423SKyle Evans 	libbe_handle_t		*lbh;
50be7dd423SKyle Evans 	char			*snapname;
51be7dd423SKyle Evans };
52be7dd423SKyle Evans 
533d1a1f2cSKyle Evans #if SOON
54c65a2111SKyle Evans static int be_create_child_noent(libbe_handle_t *lbh, const char *active,
55c65a2111SKyle Evans     const char *child_path);
56c65a2111SKyle Evans static int be_create_child_cloned(libbe_handle_t *lbh, const char *active);
573d1a1f2cSKyle Evans #endif
58c65a2111SKyle Evans 
5990cf61e8SKyle Evans /* Arbitrary... should tune */
6090cf61e8SKyle Evans #define	BE_SNAP_SERIAL_MAX	1024
6190cf61e8SKyle Evans 
6228f16a0fSKyle Evans /*
63ee16b7c9SKyle Evans  * Iterator function for locating the rootfs amongst the children of the
64ee16b7c9SKyle Evans  * zfs_be_root set by loader(8).  data is expected to be a libbe_handle_t *.
65ee16b7c9SKyle Evans  */
66ee16b7c9SKyle Evans static int
6751aecc89SKyle Evans be_locate_rootfs(libbe_handle_t *lbh)
68ee16b7c9SKyle Evans {
694ab5187dSKyle Evans 	struct statfs sfs;
704ab5187dSKyle Evans 	struct extmnttab entry;
7151aecc89SKyle Evans 	zfs_handle_t *zfs;
72ee16b7c9SKyle Evans 
734ab5187dSKyle Evans 	/*
744ab5187dSKyle Evans 	 * Check first if root is ZFS; if not, we'll bail on rootfs capture.
754ab5187dSKyle Evans 	 * Unfortunately needed because zfs_path_to_zhandle will emit to
764ab5187dSKyle Evans 	 * stderr if / isn't actually a ZFS filesystem, which we'd like
774ab5187dSKyle Evans 	 * to avoid.
784ab5187dSKyle Evans 	 */
794ab5187dSKyle Evans 	if (statfs("/", &sfs) == 0) {
804ab5187dSKyle Evans 		statfs2mnttab(&sfs, &entry);
814ab5187dSKyle Evans 		if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0)
824ab5187dSKyle Evans 			return (1);
834ab5187dSKyle Evans 	} else
844ab5187dSKyle Evans 		return (1);
8551aecc89SKyle Evans 	zfs = zfs_path_to_zhandle(lbh->lzh, "/", ZFS_TYPE_FILESYSTEM);
8651aecc89SKyle Evans 	if (zfs == NULL)
87ee16b7c9SKyle Evans 		return (1);
88ee16b7c9SKyle Evans 
8951aecc89SKyle Evans 	strlcpy(lbh->rootfs, zfs_get_name(zfs), sizeof(lbh->rootfs));
9051aecc89SKyle Evans 	zfs_close(zfs);
91ee16b7c9SKyle Evans 	return (0);
92ee16b7c9SKyle Evans }
93ee16b7c9SKyle Evans 
94ee16b7c9SKyle Evans /*
9528f16a0fSKyle Evans  * Initializes the libbe context to operate in the root boot environment
9628f16a0fSKyle Evans  * dataset, for example, zroot/ROOT.
9728f16a0fSKyle Evans  */
9828f16a0fSKyle Evans libbe_handle_t *
99cc624025SKyle Evans libbe_init(const char *root)
10028f16a0fSKyle Evans {
101fc13fc1cSKyle Evans 	char altroot[MAXPATHLEN];
10228f16a0fSKyle Evans 	libbe_handle_t *lbh;
103c3a34c08SKyle Evans 	char *poolname, *pos;
104c3a34c08SKyle Evans 	int pnamelen;
10528f16a0fSKyle Evans 
106c3a34c08SKyle Evans 	lbh = NULL;
107c3a34c08SKyle Evans 	poolname = pos = NULL;
10828f16a0fSKyle Evans 
109c3a34c08SKyle Evans 	if ((lbh = calloc(1, sizeof(libbe_handle_t))) == NULL)
110c3a34c08SKyle Evans 		goto err;
11128f16a0fSKyle Evans 
112c3a34c08SKyle Evans 	if ((lbh->lzh = libzfs_init()) == NULL)
113c3a34c08SKyle Evans 		goto err;
11428f16a0fSKyle Evans 
115cc624025SKyle Evans 	/*
116cc624025SKyle Evans 	 * Grab rootfs, we'll work backwards from there if an optional BE root
117cc624025SKyle Evans 	 * has not been passed in.
118cc624025SKyle Evans 	 */
1194ab5187dSKyle Evans 	if (be_locate_rootfs(lbh) != 0) {
1204ab5187dSKyle Evans 		if (root == NULL)
121c3a34c08SKyle Evans 			goto err;
1224ab5187dSKyle Evans 		*lbh->rootfs = '\0';
1234ab5187dSKyle Evans 	}
124cc624025SKyle Evans 	if (root == NULL) {
125cc624025SKyle Evans 		/* Strip off the final slash from rootfs to get the be root */
12651aecc89SKyle Evans 		strlcpy(lbh->root, lbh->rootfs, sizeof(lbh->root));
12751aecc89SKyle Evans 		pos = strrchr(lbh->root, '/');
12851aecc89SKyle Evans 		if (pos == NULL)
12951aecc89SKyle Evans 			goto err;
13051aecc89SKyle Evans 		*pos = '\0';
131cc624025SKyle Evans 	} else
132cc624025SKyle Evans 		strlcpy(lbh->root, root, sizeof(lbh->root));
133c3a34c08SKyle Evans 
134c3a34c08SKyle Evans 	if ((pos = strchr(lbh->root, '/')) == NULL)
135c3a34c08SKyle Evans 		goto err;
136c3a34c08SKyle Evans 
137c3a34c08SKyle Evans 	pnamelen = pos - lbh->root;
138c3a34c08SKyle Evans 	poolname = malloc(pnamelen + 1);
139c3a34c08SKyle Evans 	if (poolname == NULL)
140c3a34c08SKyle Evans 		goto err;
141c3a34c08SKyle Evans 
14255b0e92bSKyle Evans 	strlcpy(poolname, lbh->root, pnamelen + 1);
143c3a34c08SKyle Evans 	if ((lbh->active_phandle = zpool_open(lbh->lzh, poolname)) == NULL)
144c3a34c08SKyle Evans 		goto err;
145a8e44f4dSKyle Evans 	free(poolname);
146a8e44f4dSKyle Evans 	poolname = NULL;
147c3a34c08SKyle Evans 
148c3a34c08SKyle Evans 	if (zpool_get_prop(lbh->active_phandle, ZPOOL_PROP_BOOTFS, lbh->bootfs,
14955b0e92bSKyle Evans 	    sizeof(lbh->bootfs), NULL, true) != 0)
150c3a34c08SKyle Evans 		goto err;
151c3a34c08SKyle Evans 
152fc13fc1cSKyle Evans 	if (zpool_get_prop(lbh->active_phandle, ZPOOL_PROP_ALTROOT,
153fc13fc1cSKyle Evans 	    altroot, sizeof(altroot), NULL, true) == 0 &&
154fc13fc1cSKyle Evans 	    strcmp(altroot, "-") != 0)
155fc13fc1cSKyle Evans 		lbh->altroot_len = strlen(altroot);
156fc13fc1cSKyle Evans 
15728f16a0fSKyle Evans 	return (lbh);
158c3a34c08SKyle Evans err:
159c3a34c08SKyle Evans 	if (lbh != NULL) {
160c3a34c08SKyle Evans 		if (lbh->active_phandle != NULL)
161c3a34c08SKyle Evans 			zpool_close(lbh->active_phandle);
162c3a34c08SKyle Evans 		if (lbh->lzh != NULL)
163c3a34c08SKyle Evans 			libzfs_fini(lbh->lzh);
164c3a34c08SKyle Evans 		free(lbh);
165c3a34c08SKyle Evans 	}
166c3a34c08SKyle Evans 	free(poolname);
167c3a34c08SKyle Evans 	return (NULL);
16828f16a0fSKyle Evans }
16928f16a0fSKyle Evans 
17028f16a0fSKyle Evans 
17128f16a0fSKyle Evans /*
17228f16a0fSKyle Evans  * Free memory allocated by libbe_init()
17328f16a0fSKyle Evans  */
17428f16a0fSKyle Evans void
17528f16a0fSKyle Evans libbe_close(libbe_handle_t *lbh)
17628f16a0fSKyle Evans {
177bfe0869cSKyle Evans 
178c3a34c08SKyle Evans 	if (lbh->active_phandle != NULL)
179c3a34c08SKyle Evans 		zpool_close(lbh->active_phandle);
18028f16a0fSKyle Evans 	libzfs_fini(lbh->lzh);
18128f16a0fSKyle Evans 	free(lbh);
18228f16a0fSKyle Evans }
18328f16a0fSKyle Evans 
1849b1662e6SKyle Evans /*
1859b1662e6SKyle Evans  * Proxy through to libzfs for the moment.
1869b1662e6SKyle Evans  */
1879b1662e6SKyle Evans void
1889b1662e6SKyle Evans be_nicenum(uint64_t num, char *buf, size_t buflen)
1899b1662e6SKyle Evans {
1909b1662e6SKyle Evans 
1919b1662e6SKyle Evans 	zfs_nicenum(num, buf, buflen);
1929b1662e6SKyle Evans }
19328f16a0fSKyle Evans 
194920abf4dSKyle Evans static int
195920abf4dSKyle Evans be_destroy_cb(zfs_handle_t *zfs_hdl, void *data)
196920abf4dSKyle Evans {
197be7dd423SKyle Evans 	char path[BE_MAXPATHLEN];
198be7dd423SKyle Evans 	struct be_destroy_data *bdd;
199be7dd423SKyle Evans 	zfs_handle_t *snap;
200920abf4dSKyle Evans 	int err;
201920abf4dSKyle Evans 
202be7dd423SKyle Evans 	bdd = (struct be_destroy_data *)data;
203be7dd423SKyle Evans 	if (bdd->snapname == NULL) {
204be7dd423SKyle Evans 		err = zfs_iter_children(zfs_hdl, be_destroy_cb, data);
205be7dd423SKyle Evans 		if (err != 0)
206920abf4dSKyle Evans 			return (err);
207be7dd423SKyle Evans 		return (zfs_destroy(zfs_hdl, false));
208be7dd423SKyle Evans 	}
209be7dd423SKyle Evans 	/* If we're dealing with snapshots instead, delete that one alone */
210be7dd423SKyle Evans 	err = zfs_iter_filesystems(zfs_hdl, be_destroy_cb, data);
211be7dd423SKyle Evans 	if (err != 0)
212920abf4dSKyle Evans 		return (err);
213be7dd423SKyle Evans 	/*
214be7dd423SKyle Evans 	 * This part is intentionally glossing over any potential errors,
215be7dd423SKyle Evans 	 * because there's a lot less potential for errors when we're cleaning
216be7dd423SKyle Evans 	 * up snapshots rather than a full deep BE.  The primary error case
217be7dd423SKyle Evans 	 * here being if the snapshot doesn't exist in the first place, which
218be7dd423SKyle Evans 	 * the caller will likely deem insignificant as long as it doesn't
219be7dd423SKyle Evans 	 * exist after the call.  Thus, such a missing snapshot shouldn't jam
220be7dd423SKyle Evans 	 * up the destruction.
221be7dd423SKyle Evans 	 */
222be7dd423SKyle Evans 	snprintf(path, sizeof(path), "%s@%s", zfs_get_name(zfs_hdl),
223be7dd423SKyle Evans 	    bdd->snapname);
224be7dd423SKyle Evans 	if (!zfs_dataset_exists(bdd->lbh->lzh, path, ZFS_TYPE_SNAPSHOT))
225be7dd423SKyle Evans 		return (0);
226be7dd423SKyle Evans 	snap = zfs_open(bdd->lbh->lzh, path, ZFS_TYPE_SNAPSHOT);
227be7dd423SKyle Evans 	if (snap != NULL)
228be7dd423SKyle Evans 		zfs_destroy(snap, false);
229920abf4dSKyle Evans 	return (0);
230920abf4dSKyle Evans }
231920abf4dSKyle Evans 
23228f16a0fSKyle Evans /*
23328f16a0fSKyle Evans  * Destroy the boot environment or snapshot specified by the name
23428f16a0fSKyle Evans  * parameter. Options are or'd together with the possible values:
23528f16a0fSKyle Evans  * BE_DESTROY_FORCE : forces operation on mounted datasets
236be7dd423SKyle Evans  * BE_DESTROY_ORIGIN: destroy the origin snapshot as well
23728f16a0fSKyle Evans  */
23828f16a0fSKyle Evans int
23973c3d608SKyle Evans be_destroy(libbe_handle_t *lbh, const char *name, int options)
24028f16a0fSKyle Evans {
241be7dd423SKyle Evans 	struct be_destroy_data bdd;
24213c62c50SKyle Evans 	char origin[BE_MAXPATHLEN], path[BE_MAXPATHLEN];
24328f16a0fSKyle Evans 	zfs_handle_t *fs;
244be7dd423SKyle Evans 	char *snapdelim;
245bfe0869cSKyle Evans 	int err, force, mounted;
246be7dd423SKyle Evans 	size_t rootlen;
24728f16a0fSKyle Evans 
248be7dd423SKyle Evans 	bdd.lbh = lbh;
249be7dd423SKyle Evans 	bdd.snapname = NULL;
250bfe0869cSKyle Evans 	force = options & BE_DESTROY_FORCE;
25113c62c50SKyle Evans 	*origin = '\0';
25228f16a0fSKyle Evans 
25328f16a0fSKyle Evans 	be_root_concat(lbh, name, path);
25428f16a0fSKyle Evans 
255be7dd423SKyle Evans 	if ((snapdelim = strchr(path, '@')) == NULL) {
256bfe0869cSKyle Evans 		if (!zfs_dataset_exists(lbh->lzh, path, ZFS_TYPE_FILESYSTEM))
25728f16a0fSKyle Evans 			return (set_error(lbh, BE_ERR_NOENT));
25828f16a0fSKyle Evans 
259f08dac4eSKyle Evans 		if (strcmp(path, lbh->rootfs) == 0 ||
260f08dac4eSKyle Evans 		    strcmp(path, lbh->bootfs) == 0)
26128f16a0fSKyle Evans 			return (set_error(lbh, BE_ERR_DESTROYACT));
26228f16a0fSKyle Evans 
263be7dd423SKyle Evans 		fs = zfs_open(lbh->lzh, path, ZFS_TYPE_FILESYSTEM);
26413c62c50SKyle Evans 		if (fs == NULL)
26513c62c50SKyle Evans 			return (set_error(lbh, BE_ERR_ZFSOPEN));
266be7dd423SKyle Evans 
26713c62c50SKyle Evans 		if ((options & BE_DESTROY_ORIGIN) != 0 &&
26813c62c50SKyle Evans 		    zfs_prop_get(fs, ZFS_PROP_ORIGIN, origin, sizeof(origin),
26913c62c50SKyle Evans 		    NULL, NULL, 0, 1) != 0)
27013c62c50SKyle Evans 			return (set_error(lbh, BE_ERR_NOORIGIN));
271e1ee6230SKyle Evans 
272e1ee6230SKyle Evans 		/* Don't destroy a mounted dataset unless force is specified */
273e1ee6230SKyle Evans 		if ((mounted = zfs_is_mounted(fs, NULL)) != 0) {
274e1ee6230SKyle Evans 			if (force) {
275e1ee6230SKyle Evans 				zfs_unmount(fs, NULL, 0);
276e1ee6230SKyle Evans 			} else {
277e1ee6230SKyle Evans 				free(bdd.snapname);
278e1ee6230SKyle Evans 				return (set_error(lbh, BE_ERR_DESTROYMNT));
279e1ee6230SKyle Evans 			}
280e1ee6230SKyle Evans 		}
28128f16a0fSKyle Evans 	} else {
282bfe0869cSKyle Evans 		if (!zfs_dataset_exists(lbh->lzh, path, ZFS_TYPE_SNAPSHOT))
28328f16a0fSKyle Evans 			return (set_error(lbh, BE_ERR_NOENT));
28428f16a0fSKyle Evans 
285be7dd423SKyle Evans 		bdd.snapname = strdup(snapdelim + 1);
286be7dd423SKyle Evans 		if (bdd.snapname == NULL)
287be7dd423SKyle Evans 			return (set_error(lbh, BE_ERR_NOMEM));
288be7dd423SKyle Evans 		*snapdelim = '\0';
289be7dd423SKyle Evans 		fs = zfs_open(lbh->lzh, path, ZFS_TYPE_DATASET);
290be7dd423SKyle Evans 		if (fs == NULL) {
291be7dd423SKyle Evans 			free(bdd.snapname);
29228f16a0fSKyle Evans 			return (set_error(lbh, BE_ERR_ZFSOPEN));
29313c62c50SKyle Evans 		}
294be7dd423SKyle Evans 	}
29528f16a0fSKyle Evans 
296be7dd423SKyle Evans 	err = be_destroy_cb(fs, &bdd);
297be7dd423SKyle Evans 	zfs_close(fs);
298be7dd423SKyle Evans 	free(bdd.snapname);
299be7dd423SKyle Evans 	if (err != 0) {
300920abf4dSKyle Evans 		/* Children are still present or the mount is referenced */
301920abf4dSKyle Evans 		if (err == EBUSY)
302920abf4dSKyle Evans 			return (set_error(lbh, BE_ERR_DESTROYMNT));
303920abf4dSKyle Evans 		return (set_error(lbh, BE_ERR_UNKNOWN));
304920abf4dSKyle Evans 	}
30528f16a0fSKyle Evans 
306be7dd423SKyle Evans 	if ((options & BE_DESTROY_ORIGIN) == 0)
307920abf4dSKyle Evans 		return (0);
30828f16a0fSKyle Evans 
309be7dd423SKyle Evans 	/* The origin can't possibly be shorter than the BE root */
310be7dd423SKyle Evans 	rootlen = strlen(lbh->root);
311be7dd423SKyle Evans 	if (*origin == '\0' || strlen(origin) <= rootlen + 1)
312be7dd423SKyle Evans 		return (set_error(lbh, BE_ERR_INVORIGIN));
313be7dd423SKyle Evans 
314be7dd423SKyle Evans 	/*
315be7dd423SKyle Evans 	 * We'll be chopping off the BE root and running this back through
316be7dd423SKyle Evans 	 * be_destroy, so that we properly handle the origin snapshot whether
317be7dd423SKyle Evans 	 * it be that of a deep BE or not.
318be7dd423SKyle Evans 	 */
319be7dd423SKyle Evans 	if (strncmp(origin, lbh->root, rootlen) != 0 || origin[rootlen] != '/')
320be7dd423SKyle Evans 		return (0);
321be7dd423SKyle Evans 
322be7dd423SKyle Evans 	return (be_destroy(lbh, origin + rootlen + 1,
323be7dd423SKyle Evans 	    options & ~BE_DESTROY_ORIGIN));
324be7dd423SKyle Evans }
32528f16a0fSKyle Evans 
32690cf61e8SKyle Evans static void
32790cf61e8SKyle Evans be_setup_snapshot_name(libbe_handle_t *lbh, char *buf, size_t buflen)
32890cf61e8SKyle Evans {
32990cf61e8SKyle Evans 	time_t rawtime;
33090cf61e8SKyle Evans 	int len, serial;
33190cf61e8SKyle Evans 
33290cf61e8SKyle Evans 	time(&rawtime);
33390cf61e8SKyle Evans 	len = strlen(buf);
33490cf61e8SKyle Evans 	len += strftime(buf + len, buflen - len, "@%F-%T", localtime(&rawtime));
33590cf61e8SKyle Evans 	/* No room for serial... caller will do its best */
33690cf61e8SKyle Evans 	if (buflen - len < 2)
33790cf61e8SKyle Evans 		return;
33890cf61e8SKyle Evans 
33990cf61e8SKyle Evans 	for (serial = 0; serial < BE_SNAP_SERIAL_MAX; ++serial) {
34090cf61e8SKyle Evans 		snprintf(buf + len, buflen - len, "-%d", serial);
34190cf61e8SKyle Evans 		if (!zfs_dataset_exists(lbh->lzh, buf, ZFS_TYPE_SNAPSHOT))
34290cf61e8SKyle Evans 			return;
34390cf61e8SKyle Evans 	}
34490cf61e8SKyle Evans }
34590cf61e8SKyle Evans 
34628f16a0fSKyle Evans int
347b29bf2f8SKyle Evans be_snapshot(libbe_handle_t *lbh, const char *source, const char *snap_name,
34828f16a0fSKyle Evans     bool recursive, char *result)
34928f16a0fSKyle Evans {
35028f16a0fSKyle Evans 	char buf[BE_MAXPATHLEN];
35190cf61e8SKyle Evans 	int err;
35228f16a0fSKyle Evans 
35328f16a0fSKyle Evans 	be_root_concat(lbh, source, buf);
35428f16a0fSKyle Evans 
355162ec569SKyle Evans 	if ((err = be_exists(lbh, buf)) != 0)
356162ec569SKyle Evans 		return (set_error(lbh, err));
35728f16a0fSKyle Evans 
35828f16a0fSKyle Evans 	if (snap_name != NULL) {
359a8e44f4dSKyle Evans 		if (strlcat(buf, "@", sizeof(buf)) >= sizeof(buf))
360a8e44f4dSKyle Evans 			return (set_error(lbh, BE_ERR_INVALIDNAME));
361a8e44f4dSKyle Evans 
362a8e44f4dSKyle Evans 		if (strlcat(buf, snap_name, sizeof(buf)) >= sizeof(buf))
363a8e44f4dSKyle Evans 			return (set_error(lbh, BE_ERR_INVALIDNAME));
364a8e44f4dSKyle Evans 
365bfe0869cSKyle Evans 		if (result != NULL)
36628f16a0fSKyle Evans 			snprintf(result, BE_MAXPATHLEN, "%s@%s", source,
36728f16a0fSKyle Evans 			    snap_name);
36828f16a0fSKyle Evans 	} else {
36990cf61e8SKyle Evans 		be_setup_snapshot_name(lbh, buf, sizeof(buf));
37090cf61e8SKyle Evans 
371a8e44f4dSKyle Evans 		if (result != NULL && strlcpy(result, strrchr(buf, '/') + 1,
372a8e44f4dSKyle Evans 		    sizeof(buf)) >= sizeof(buf))
373a8e44f4dSKyle Evans 			return (set_error(lbh, BE_ERR_INVALIDNAME));
37428f16a0fSKyle Evans 	}
375b29bf2f8SKyle Evans 	if ((err = zfs_snapshot(lbh->lzh, buf, recursive, NULL)) != 0) {
37628f16a0fSKyle Evans 		switch (err) {
37728f16a0fSKyle Evans 		case EZFS_INVALIDNAME:
37828f16a0fSKyle Evans 			return (set_error(lbh, BE_ERR_INVALIDNAME));
37928f16a0fSKyle Evans 
38028f16a0fSKyle Evans 		default:
3812989df09SKyle Evans 			/*
3822989df09SKyle Evans 			 * The other errors that zfs_ioc_snapshot might return
3832989df09SKyle Evans 			 * shouldn't happen if we've set things up properly, so
3842989df09SKyle Evans 			 * we'll gloss over them and call it UNKNOWN as it will
3852989df09SKyle Evans 			 * require further triage.
3862989df09SKyle Evans 			 */
3872989df09SKyle Evans 			if (errno == ENOTSUP)
3882989df09SKyle Evans 				return (set_error(lbh, BE_ERR_NOPOOL));
38928f16a0fSKyle Evans 			return (set_error(lbh, BE_ERR_UNKNOWN));
39028f16a0fSKyle Evans 		}
39128f16a0fSKyle Evans 	}
39228f16a0fSKyle Evans 
39328f16a0fSKyle Evans 	return (BE_ERR_SUCCESS);
39428f16a0fSKyle Evans }
39528f16a0fSKyle Evans 
39628f16a0fSKyle Evans 
39728f16a0fSKyle Evans /*
39828f16a0fSKyle Evans  * Create the boot environment specified by the name parameter
39928f16a0fSKyle Evans  */
40028f16a0fSKyle Evans int
40173c3d608SKyle Evans be_create(libbe_handle_t *lbh, const char *name)
40228f16a0fSKyle Evans {
40328f16a0fSKyle Evans 	int err;
40428f16a0fSKyle Evans 
405b29bf2f8SKyle Evans 	err = be_create_from_existing(lbh, name, be_active_path(lbh));
40628f16a0fSKyle Evans 
40728f16a0fSKyle Evans 	return (set_error(lbh, err));
40828f16a0fSKyle Evans }
40928f16a0fSKyle Evans 
41028f16a0fSKyle Evans static int
41128f16a0fSKyle Evans be_deep_clone_prop(int prop, void *cb)
41228f16a0fSKyle Evans {
41328f16a0fSKyle Evans 	int err;
414bfe0869cSKyle Evans         struct libbe_dccb *dccb;
41528f16a0fSKyle Evans 	zprop_source_t src;
41628f16a0fSKyle Evans 	char pval[BE_MAXPATHLEN];
41728f16a0fSKyle Evans 	char source[BE_MAXPATHLEN];
418af43c24dSKyle Evans 	char *val;
41928f16a0fSKyle Evans 
420bfe0869cSKyle Evans 	dccb = cb;
42128f16a0fSKyle Evans 	/* Skip some properties we don't want to touch */
42273c3d608SKyle Evans 	if (prop == ZFS_PROP_CANMOUNT)
42328f16a0fSKyle Evans 		return (ZPROP_CONT);
42428f16a0fSKyle Evans 
42528f16a0fSKyle Evans 	/* Don't copy readonly properties */
426bfe0869cSKyle Evans 	if (zfs_prop_readonly(prop))
42728f16a0fSKyle Evans 		return (ZPROP_CONT);
42828f16a0fSKyle Evans 
42928f16a0fSKyle Evans 	if ((err = zfs_prop_get(dccb->zhp, prop, (char *)&pval,
430bfe0869cSKyle Evans 	    sizeof(pval), &src, (char *)&source, sizeof(source), false)))
43128f16a0fSKyle Evans 		/* Just continue if we fail to read a property */
43228f16a0fSKyle Evans 		return (ZPROP_CONT);
433bfe0869cSKyle Evans 
434*be13d48cSKyle Evans 	/*
435*be13d48cSKyle Evans 	 * Only copy locally defined or received properties.  This continues
436*be13d48cSKyle Evans 	 * to avoid temporary/default/local properties intentionally without
437*be13d48cSKyle Evans 	 * breaking received datasets.
438*be13d48cSKyle Evans 	 */
439*be13d48cSKyle Evans 	if (src != ZPROP_SRC_LOCAL && src != ZPROP_SRC_RECEIVED)
44028f16a0fSKyle Evans 		return (ZPROP_CONT);
44128f16a0fSKyle Evans 
442af43c24dSKyle Evans 	/* Augment mountpoint with altroot, if needed */
443af43c24dSKyle Evans 	val = pval;
444fc13fc1cSKyle Evans 	if (prop == ZFS_PROP_MOUNTPOINT)
445fc13fc1cSKyle Evans 		val = be_mountpoint_augmented(dccb->lbh, val);
446fc13fc1cSKyle Evans 
447af43c24dSKyle Evans 	nvlist_add_string(dccb->props, zfs_prop_to_name(prop), val);
44828f16a0fSKyle Evans 
44928f16a0fSKyle Evans 	return (ZPROP_CONT);
45028f16a0fSKyle Evans }
45128f16a0fSKyle Evans 
452fa30d9edSKyle Evans /*
453fa30d9edSKyle Evans  * Return the corresponding boot environment path for a given
454fa30d9edSKyle Evans  * dataset path, the constructed path is placed in 'result'.
455fa30d9edSKyle Evans  *
456fa30d9edSKyle Evans  * example: say our new boot environment name is 'bootenv' and
457fa30d9edSKyle Evans  *          the dataset path is 'zroot/ROOT/default/data/set'.
458fa30d9edSKyle Evans  *
459fa30d9edSKyle Evans  * result should produce: 'zroot/ROOT/bootenv/data/set'
460fa30d9edSKyle Evans  */
46128f16a0fSKyle Evans static int
462fa30d9edSKyle Evans be_get_path(struct libbe_deep_clone *ldc, const char *dspath, char *result, int result_size)
463fa30d9edSKyle Evans {
464fa30d9edSKyle Evans 	char *pos;
465fa30d9edSKyle Evans 	char *child_dataset;
466fa30d9edSKyle Evans 
467fa30d9edSKyle Evans 	/* match the root path for the boot environments */
468fa30d9edSKyle Evans 	pos = strstr(dspath, ldc->lbh->root);
469fa30d9edSKyle Evans 
470fa30d9edSKyle Evans 	/* no match, different pools? */
471fa30d9edSKyle Evans 	if (pos == NULL)
472fa30d9edSKyle Evans 		return (BE_ERR_BADPATH);
473fa30d9edSKyle Evans 
474fa30d9edSKyle Evans 	/* root path of the new boot environment */
475fa30d9edSKyle Evans 	snprintf(result, result_size, "%s/%s", ldc->lbh->root, ldc->bename);
476fa30d9edSKyle Evans 
477fa30d9edSKyle Evans         /* gets us to the parent dataset, the +1 consumes a trailing slash */
478fa30d9edSKyle Evans 	pos += strlen(ldc->lbh->root) + 1;
479fa30d9edSKyle Evans 
480fa30d9edSKyle Evans 	/* skip the parent dataset */
481fa30d9edSKyle Evans 	if ((child_dataset = strchr(pos, '/')) != NULL)
482fa30d9edSKyle Evans 		strlcat(result, child_dataset, result_size);
483fa30d9edSKyle Evans 
484fa30d9edSKyle Evans 	return (BE_ERR_SUCCESS);
485fa30d9edSKyle Evans }
486fa30d9edSKyle Evans 
487fa30d9edSKyle Evans static int
488fa30d9edSKyle Evans be_clone_cb(zfs_handle_t *ds, void *data)
48928f16a0fSKyle Evans {
49028f16a0fSKyle Evans 	int err;
49128f16a0fSKyle Evans 	char be_path[BE_MAXPATHLEN];
49228f16a0fSKyle Evans 	char snap_path[BE_MAXPATHLEN];
49328f16a0fSKyle Evans 	const char *dspath;
49428f16a0fSKyle Evans 	zfs_handle_t *snap_hdl;
49528f16a0fSKyle Evans 	nvlist_t *props;
496fa30d9edSKyle Evans 	struct libbe_deep_clone *ldc;
49728f16a0fSKyle Evans 	struct libbe_dccb dccb;
49828f16a0fSKyle Evans 
499fa30d9edSKyle Evans 	ldc = (struct libbe_deep_clone *)data;
50028f16a0fSKyle Evans 	dspath = zfs_get_name(ds);
501bfe0869cSKyle Evans 
502fa30d9edSKyle Evans 	snprintf(snap_path, sizeof(snap_path), "%s@%s", dspath, ldc->snapname);
503bfe0869cSKyle Evans 
504fa30d9edSKyle Evans 	/* construct the boot environment path from the dataset we're cloning */
505fa30d9edSKyle Evans 	if (be_get_path(ldc, dspath, be_path, sizeof(be_path)) != BE_ERR_SUCCESS)
506fa30d9edSKyle Evans 		return (set_error(ldc->lbh, BE_ERR_UNKNOWN));
50728f16a0fSKyle Evans 
508fa30d9edSKyle Evans 	/* the dataset to be created (i.e. the boot environment) already exists */
509fa30d9edSKyle Evans 	if (zfs_dataset_exists(ldc->lbh->lzh, be_path, ZFS_TYPE_DATASET))
510fa30d9edSKyle Evans 		return (set_error(ldc->lbh, BE_ERR_EXISTS));
511fa30d9edSKyle Evans 
512fa30d9edSKyle Evans 	/* no snapshot found for this dataset, silently skip it */
513fa30d9edSKyle Evans 	if (!zfs_dataset_exists(ldc->lbh->lzh, snap_path, ZFS_TYPE_SNAPSHOT))
514fa30d9edSKyle Evans 		return (0);
51528f16a0fSKyle Evans 
51628f16a0fSKyle Evans 	if ((snap_hdl =
517fa30d9edSKyle Evans 	    zfs_open(ldc->lbh->lzh, snap_path, ZFS_TYPE_SNAPSHOT)) == NULL)
518fa30d9edSKyle Evans 		return (set_error(ldc->lbh, BE_ERR_ZFSOPEN));
51928f16a0fSKyle Evans 
52028f16a0fSKyle Evans 	nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP);
52128f16a0fSKyle Evans 	nvlist_add_string(props, "canmount", "noauto");
52228f16a0fSKyle Evans 
523fa30d9edSKyle Evans 	dccb.lbh = ldc->lbh;
52428f16a0fSKyle Evans 	dccb.zhp = ds;
52528f16a0fSKyle Evans 	dccb.props = props;
52628f16a0fSKyle Evans 	if (zprop_iter(be_deep_clone_prop, &dccb, B_FALSE, B_FALSE,
527bfe0869cSKyle Evans 	    ZFS_TYPE_FILESYSTEM) == ZPROP_INVAL)
52828f16a0fSKyle Evans 		return (-1);
52928f16a0fSKyle Evans 
530cc4deabcSKyle Evans 	if ((err = zfs_clone(snap_hdl, be_path, props)) != 0)
531fa30d9edSKyle Evans 		return (set_error(ldc->lbh, BE_ERR_ZFSCLONE));
53228f16a0fSKyle Evans 
53328f16a0fSKyle Evans 	nvlist_free(props);
53428f16a0fSKyle Evans 	zfs_close(snap_hdl);
53528f16a0fSKyle Evans 
536fa30d9edSKyle Evans 	if (ldc->depth_limit == -1 || ldc->depth < ldc->depth_limit) {
537fa30d9edSKyle Evans 		ldc->depth++;
538fa30d9edSKyle Evans 		err = zfs_iter_filesystems(ds, be_clone_cb, ldc);
539fa30d9edSKyle Evans 		ldc->depth--;
540fa30d9edSKyle Evans 	}
541cc4deabcSKyle Evans 
542fa30d9edSKyle Evans 	return (set_error(ldc->lbh, err));
54328f16a0fSKyle Evans }
54428f16a0fSKyle Evans 
54528f16a0fSKyle Evans /*
546fa30d9edSKyle Evans  * Create a boot environment with a given name from a given snapshot.
547fa30d9edSKyle Evans  * Snapshots can be in the format 'zroot/ROOT/default@snapshot' or
548fa30d9edSKyle Evans  * 'default@snapshot'. In the latter case, 'default@snapshot' will be prepended
549fa30d9edSKyle Evans  * with the root path that libbe was initailized with.
55028f16a0fSKyle Evans */
551fa30d9edSKyle Evans static int
552fa30d9edSKyle Evans be_clone(libbe_handle_t *lbh, const char *bename, const char *snapshot, int depth)
55328f16a0fSKyle Evans {
55428f16a0fSKyle Evans 	int err;
55528f16a0fSKyle Evans 	char snap_path[BE_MAXPATHLEN];
556b29bf2f8SKyle Evans 	char *parentname, *snapname;
55728f16a0fSKyle Evans 	zfs_handle_t *parent_hdl;
558fa30d9edSKyle Evans 	struct libbe_deep_clone ldc;
55928f16a0fSKyle Evans 
560fa30d9edSKyle Evans         /* ensure the boot environment name is valid */
561fa30d9edSKyle Evans 	if ((err = be_validate_name(lbh, bename)) != 0)
56228f16a0fSKyle Evans 		return (set_error(lbh, err));
563fa30d9edSKyle Evans 
564fa30d9edSKyle Evans 	/*
565fa30d9edSKyle Evans 	 * prepend the boot environment root path if we're
566fa30d9edSKyle Evans 	 * given a partial snapshot name.
567fa30d9edSKyle Evans 	 */
568fa30d9edSKyle Evans 	if ((err = be_root_concat(lbh, snapshot, snap_path)) != 0)
56928f16a0fSKyle Evans 		return (set_error(lbh, err));
570fa30d9edSKyle Evans 
571fa30d9edSKyle Evans 	/* ensure the snapshot exists */
572b29bf2f8SKyle Evans 	if ((err = be_validate_snap(lbh, snap_path)) != 0)
57328f16a0fSKyle Evans 		return (set_error(lbh, err));
57428f16a0fSKyle Evans 
575fa30d9edSKyle Evans         /* get a copy of the snapshot path so we can disect it */
576cc4deabcSKyle Evans 	if ((parentname = strdup(snap_path)) == NULL)
577cc4deabcSKyle Evans 		return (set_error(lbh, BE_ERR_UNKNOWN));
578cc4deabcSKyle Evans 
579fa30d9edSKyle Evans         /* split dataset name from snapshot name */
58028f16a0fSKyle Evans 	snapname = strchr(parentname, '@');
58128f16a0fSKyle Evans 	if (snapname == NULL) {
582cc4deabcSKyle Evans 		free(parentname);
583cc4deabcSKyle Evans 		return (set_error(lbh, BE_ERR_UNKNOWN));
58428f16a0fSKyle Evans 	}
58528f16a0fSKyle Evans 	*snapname = '\0';
58628f16a0fSKyle Evans 	snapname++;
58728f16a0fSKyle Evans 
588fa30d9edSKyle Evans         /* set-up the boot environment */
589fa30d9edSKyle Evans         ldc.lbh = lbh;
590fa30d9edSKyle Evans         ldc.bename = bename;
591fa30d9edSKyle Evans         ldc.snapname = snapname;
592fa30d9edSKyle Evans 	ldc.depth = 0;
593fa30d9edSKyle Evans 	ldc.depth_limit = depth;
59428f16a0fSKyle Evans 
595fa30d9edSKyle Evans         /* the boot environment will be cloned from this dataset */
59628f16a0fSKyle Evans 	parent_hdl = zfs_open(lbh->lzh, parentname, ZFS_TYPE_DATASET);
597fa30d9edSKyle Evans 
598fa30d9edSKyle Evans         /* create the boot environment */
599fa30d9edSKyle Evans 	err = be_clone_cb(parent_hdl, &ldc);
60028f16a0fSKyle Evans 
601cc4deabcSKyle Evans 	free(parentname);
60228f16a0fSKyle Evans 	return (set_error(lbh, err));
60328f16a0fSKyle Evans }
60428f16a0fSKyle Evans 
605fa30d9edSKyle Evans /*
606fa30d9edSKyle Evans  * Create a boot environment from pre-existing snapshot, specifying a depth.
607fa30d9edSKyle Evans  */
608fa30d9edSKyle Evans int be_create_depth(libbe_handle_t *lbh, const char *bename,
609fa30d9edSKyle Evans 		    const char *snap, int depth)
610fa30d9edSKyle Evans {
611fa30d9edSKyle Evans 	return (be_clone(lbh, bename, snap, depth));
612fa30d9edSKyle Evans }
613fa30d9edSKyle Evans 
614fa30d9edSKyle Evans /*
615fa30d9edSKyle Evans  * Create the boot environment from pre-existing snapshot
616fa30d9edSKyle Evans  */
617fa30d9edSKyle Evans int
618fa30d9edSKyle Evans be_create_from_existing_snap(libbe_handle_t *lbh, const char *bename,
619fa30d9edSKyle Evans     const char *snap)
620fa30d9edSKyle Evans {
621fa30d9edSKyle Evans 	return (be_clone(lbh, bename, snap, -1));
622fa30d9edSKyle Evans }
623fa30d9edSKyle Evans 
62428f16a0fSKyle Evans 
62528f16a0fSKyle Evans /*
62628f16a0fSKyle Evans  * Create a boot environment from an existing boot environment
62728f16a0fSKyle Evans  */
62828f16a0fSKyle Evans int
629fa30d9edSKyle Evans be_create_from_existing(libbe_handle_t *lbh, const char *bename, const char *old)
63028f16a0fSKyle Evans {
63128f16a0fSKyle Evans 	int err;
632fa30d9edSKyle Evans 	char snap[BE_MAXPATHLEN];
63328f16a0fSKyle Evans 
634fa30d9edSKyle Evans 	if ((err = be_snapshot(lbh, old, NULL, true, snap)) != 0)
63528f16a0fSKyle Evans 		return (set_error(lbh, err));
63628f16a0fSKyle Evans 
637fa30d9edSKyle Evans         err = be_clone(lbh, bename, snap, -1);
63828f16a0fSKyle Evans 
63928f16a0fSKyle Evans 	return (set_error(lbh, err));
64028f16a0fSKyle Evans }
64128f16a0fSKyle Evans 
64228f16a0fSKyle Evans 
64328f16a0fSKyle Evans /*
64428f16a0fSKyle Evans  * Verifies that a snapshot has a valid name, exists, and has a mountpoint of
64528f16a0fSKyle Evans  * '/'. Returns BE_ERR_SUCCESS (0), upon success, or the relevant BE_ERR_* upon
64628f16a0fSKyle Evans  * failure. Does not set the internal library error state.
64728f16a0fSKyle Evans  */
64828f16a0fSKyle Evans int
649b29bf2f8SKyle Evans be_validate_snap(libbe_handle_t *lbh, const char *snap_name)
65028f16a0fSKyle Evans {
65128f16a0fSKyle Evans 
652bfe0869cSKyle Evans 	if (strlen(snap_name) >= BE_MAXPATHLEN)
65328f16a0fSKyle Evans 		return (BE_ERR_PATHLEN);
65428f16a0fSKyle Evans 
655fcb47c42SKyle Evans 	if (!zfs_name_valid(snap_name, ZFS_TYPE_SNAPSHOT))
656fcb47c42SKyle Evans 		return (BE_ERR_INVALIDNAME);
657fcb47c42SKyle Evans 
65828f16a0fSKyle Evans 	if (!zfs_dataset_exists(lbh->lzh, snap_name,
659bfe0869cSKyle Evans 	    ZFS_TYPE_SNAPSHOT))
66028f16a0fSKyle Evans 		return (BE_ERR_NOENT);
66128f16a0fSKyle Evans 
66251aecc89SKyle Evans 	return (BE_ERR_SUCCESS);
66328f16a0fSKyle Evans }
66428f16a0fSKyle Evans 
66528f16a0fSKyle Evans 
66628f16a0fSKyle Evans /*
66728f16a0fSKyle Evans  * Idempotently appends the name argument to the root boot environment path
66828f16a0fSKyle Evans  * and copies the resulting string into the result buffer (which is assumed
66928f16a0fSKyle Evans  * to be at least BE_MAXPATHLEN characters long. Returns BE_ERR_SUCCESS upon
67028f16a0fSKyle Evans  * success, BE_ERR_PATHLEN if the resulting path is longer than BE_MAXPATHLEN,
67128f16a0fSKyle Evans  * or BE_ERR_INVALIDNAME if the name is a path that does not begin with
67228f16a0fSKyle Evans  * zfs_be_root. Does not set internal library error state.
67328f16a0fSKyle Evans  */
67428f16a0fSKyle Evans int
675b29bf2f8SKyle Evans be_root_concat(libbe_handle_t *lbh, const char *name, char *result)
67628f16a0fSKyle Evans {
67728f16a0fSKyle Evans 	size_t name_len, root_len;
67828f16a0fSKyle Evans 
67928f16a0fSKyle Evans 	name_len = strlen(name);
68028f16a0fSKyle Evans 	root_len = strlen(lbh->root);
68128f16a0fSKyle Evans 
68228f16a0fSKyle Evans 	/* Act idempotently; return be name if it is already a full path */
68328f16a0fSKyle Evans 	if (strrchr(name, '/') != NULL) {
684bfe0869cSKyle Evans 		if (strstr(name, lbh->root) != name)
68528f16a0fSKyle Evans 			return (BE_ERR_INVALIDNAME);
68628f16a0fSKyle Evans 
687bfe0869cSKyle Evans 		if (name_len >= BE_MAXPATHLEN)
68828f16a0fSKyle Evans 			return (BE_ERR_PATHLEN);
68928f16a0fSKyle Evans 
69055b0e92bSKyle Evans 		strlcpy(result, name, BE_MAXPATHLEN);
69128f16a0fSKyle Evans 		return (BE_ERR_SUCCESS);
69228f16a0fSKyle Evans 	} else if (name_len + root_len + 1 < BE_MAXPATHLEN) {
69328f16a0fSKyle Evans 		snprintf(result, BE_MAXPATHLEN, "%s/%s", lbh->root,
69428f16a0fSKyle Evans 		    name);
69528f16a0fSKyle Evans 		return (BE_ERR_SUCCESS);
69628f16a0fSKyle Evans 	}
69728f16a0fSKyle Evans 
69828f16a0fSKyle Evans 	return (BE_ERR_PATHLEN);
69928f16a0fSKyle Evans }
70028f16a0fSKyle Evans 
70128f16a0fSKyle Evans 
70228f16a0fSKyle Evans /*
70328f16a0fSKyle Evans  * Verifies the validity of a boot environment name (A-Za-z0-9-_.). Returns
7045b7803a9SKyle Evans  * BE_ERR_SUCCESS (0) if name is valid, otherwise returns BE_ERR_INVALIDNAME
7055b7803a9SKyle Evans  * or BE_ERR_PATHLEN.
70628f16a0fSKyle Evans  * Does not set internal library error state.
70728f16a0fSKyle Evans  */
70828f16a0fSKyle Evans int
7095b7803a9SKyle Evans be_validate_name(libbe_handle_t *lbh, const char *name)
71028f16a0fSKyle Evans {
71128f16a0fSKyle Evans 
7125b7803a9SKyle Evans 	/*
7135b7803a9SKyle Evans 	 * Impose the additional restriction that the entire dataset name must
7145b7803a9SKyle Evans 	 * not exceed the maximum length of a dataset, i.e. MAXNAMELEN.
7155b7803a9SKyle Evans 	 */
7165b7803a9SKyle Evans 	if (strlen(lbh->root) + 1 + strlen(name) > MAXNAMELEN)
7175b7803a9SKyle Evans 		return (BE_ERR_PATHLEN);
718fcb47c42SKyle Evans 
719fcb47c42SKyle Evans 	if (!zfs_name_valid(name, ZFS_TYPE_DATASET))
720fcb47c42SKyle Evans 		return (BE_ERR_INVALIDNAME);
721fcb47c42SKyle Evans 
72228f16a0fSKyle Evans 	return (BE_ERR_SUCCESS);
72328f16a0fSKyle Evans }
72428f16a0fSKyle Evans 
72528f16a0fSKyle Evans 
72628f16a0fSKyle Evans /*
72728f16a0fSKyle Evans  * usage
72828f16a0fSKyle Evans  */
72928f16a0fSKyle Evans int
73073c3d608SKyle Evans be_rename(libbe_handle_t *lbh, const char *old, const char *new)
73128f16a0fSKyle Evans {
73228f16a0fSKyle Evans 	char full_old[BE_MAXPATHLEN];
73328f16a0fSKyle Evans 	char full_new[BE_MAXPATHLEN];
73428f16a0fSKyle Evans 	zfs_handle_t *zfs_hdl;
73528f16a0fSKyle Evans 	int err;
73628f16a0fSKyle Evans 
7375b7803a9SKyle Evans 	/*
7385b7803a9SKyle Evans 	 * be_validate_name is documented not to set error state, so we should
7395b7803a9SKyle Evans 	 * do so here.
7405b7803a9SKyle Evans 	 */
7415b7803a9SKyle Evans 	if ((err = be_validate_name(lbh, new)) != 0)
7425b7803a9SKyle Evans 		return (set_error(lbh, err));
743b29bf2f8SKyle Evans 	if ((err = be_root_concat(lbh, old, full_old)) != 0)
74428f16a0fSKyle Evans 		return (set_error(lbh, err));
745b29bf2f8SKyle Evans 	if ((err = be_root_concat(lbh, new, full_new)) != 0)
74628f16a0fSKyle Evans 		return (set_error(lbh, err));
74728f16a0fSKyle Evans 
748bfe0869cSKyle Evans 	if (!zfs_dataset_exists(lbh->lzh, full_old, ZFS_TYPE_DATASET))
7492989df09SKyle Evans 		return (set_error(lbh, BE_ERR_NOENT));
75028f16a0fSKyle Evans 
751bfe0869cSKyle Evans 	if (zfs_dataset_exists(lbh->lzh, full_new, ZFS_TYPE_DATASET))
7522989df09SKyle Evans 		return (set_error(lbh, BE_ERR_EXISTS));
75328f16a0fSKyle Evans 
75428f16a0fSKyle Evans 	if ((zfs_hdl = zfs_open(lbh->lzh, full_old,
755bfe0869cSKyle Evans 	    ZFS_TYPE_FILESYSTEM)) == NULL)
7562989df09SKyle Evans 		return (set_error(lbh, BE_ERR_ZFSOPEN));
75728f16a0fSKyle Evans 
758bfe0869cSKyle Evans 	/* recurse, nounmount, forceunmount */
7598369ba42SKyle Evans 	struct renameflags flags = {
7608369ba42SKyle Evans 		.nounmount = 1,
7618369ba42SKyle Evans 	};
76228f16a0fSKyle Evans 
76328f16a0fSKyle Evans 	err = zfs_rename(zfs_hdl, NULL, full_new, flags);
76428f16a0fSKyle Evans 
76528f16a0fSKyle Evans 	zfs_close(zfs_hdl);
7665b7803a9SKyle Evans 	if (err != 0)
7675b7803a9SKyle Evans 		return (set_error(lbh, BE_ERR_UNKNOWN));
7685b7803a9SKyle Evans 	return (0);
76928f16a0fSKyle Evans }
77028f16a0fSKyle Evans 
77128f16a0fSKyle Evans 
77228f16a0fSKyle Evans int
77373c3d608SKyle Evans be_export(libbe_handle_t *lbh, const char *bootenv, int fd)
77428f16a0fSKyle Evans {
77528f16a0fSKyle Evans 	char snap_name[BE_MAXPATHLEN];
77628f16a0fSKyle Evans 	char buf[BE_MAXPATHLEN];
77728f16a0fSKyle Evans 	zfs_handle_t *zfs;
77828f16a0fSKyle Evans 	int err;
77928f16a0fSKyle Evans 
780b29bf2f8SKyle Evans 	if ((err = be_snapshot(lbh, bootenv, NULL, true, snap_name)) != 0)
7816d4b1d24SKyle Evans 		/* Use the error set by be_snapshot */
7826d4b1d24SKyle Evans 		return (err);
78328f16a0fSKyle Evans 
78428f16a0fSKyle Evans 	be_root_concat(lbh, snap_name, buf);
78528f16a0fSKyle Evans 
786bfe0869cSKyle Evans 	if ((zfs = zfs_open(lbh->lzh, buf, ZFS_TYPE_DATASET)) == NULL)
787506f5fdfSKyle Evans 		return (set_error(lbh, BE_ERR_ZFSOPEN));
78828f16a0fSKyle Evans 
78928f16a0fSKyle Evans 	err = zfs_send_one(zfs, NULL, fd, 0);
7906d4b1d24SKyle Evans 	zfs_close(zfs);
7916d4b1d24SKyle Evans 
79228f16a0fSKyle Evans 	return (err);
79328f16a0fSKyle Evans }
79428f16a0fSKyle Evans 
79528f16a0fSKyle Evans 
79628f16a0fSKyle Evans int
79773c3d608SKyle Evans be_import(libbe_handle_t *lbh, const char *bootenv, int fd)
79828f16a0fSKyle Evans {
79928f16a0fSKyle Evans 	char buf[BE_MAXPATHLEN];
80028f16a0fSKyle Evans 	nvlist_t *props;
80128f16a0fSKyle Evans 	zfs_handle_t *zfs;
80216ac0705SKyle Evans 	recvflags_t flags = { .nomount = 1 };
80316ac0705SKyle Evans 	int err;
80428f16a0fSKyle Evans 
80516ac0705SKyle Evans 	be_root_concat(lbh, bootenv, buf);
80628f16a0fSKyle Evans 
80716ac0705SKyle Evans 	if ((err = zfs_receive(lbh->lzh, buf, NULL, &flags, fd, NULL)) != 0) {
808506f5fdfSKyle Evans 		switch (err) {
809506f5fdfSKyle Evans 		case EINVAL:
810506f5fdfSKyle Evans 			return (set_error(lbh, BE_ERR_NOORIGIN));
811506f5fdfSKyle Evans 		case ENOENT:
812506f5fdfSKyle Evans 			return (set_error(lbh, BE_ERR_NOENT));
813506f5fdfSKyle Evans 		case EIO:
814506f5fdfSKyle Evans 			return (set_error(lbh, BE_ERR_IO));
815506f5fdfSKyle Evans 		default:
816506f5fdfSKyle Evans 			return (set_error(lbh, BE_ERR_UNKNOWN));
817506f5fdfSKyle Evans 		}
81828f16a0fSKyle Evans 	}
81928f16a0fSKyle Evans 
82016ac0705SKyle Evans 	if ((zfs = zfs_open(lbh->lzh, buf, ZFS_TYPE_FILESYSTEM)) == NULL)
821506f5fdfSKyle Evans 		return (set_error(lbh, BE_ERR_ZFSOPEN));
82228f16a0fSKyle Evans 
82328f16a0fSKyle Evans 	nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP);
82428f16a0fSKyle Evans 	nvlist_add_string(props, "canmount", "noauto");
82528f16a0fSKyle Evans 	nvlist_add_string(props, "mountpoint", "/");
82628f16a0fSKyle Evans 
82716ac0705SKyle Evans 	err = zfs_prop_set_list(zfs, props);
82828f16a0fSKyle Evans 	nvlist_free(props);
82928f16a0fSKyle Evans 
8301b057aacSKyle Evans 	zfs_close(zfs);
8311b057aacSKyle Evans 
8321b057aacSKyle Evans 	if (err != 0)
8331b057aacSKyle Evans 		return (set_error(lbh, BE_ERR_UNKNOWN));
8341b057aacSKyle Evans 
83516ac0705SKyle Evans 	return (0);
83628f16a0fSKyle Evans }
83728f16a0fSKyle Evans 
8383d1a1f2cSKyle Evans #if SOON
839c65a2111SKyle Evans static int
840c65a2111SKyle Evans be_create_child_noent(libbe_handle_t *lbh, const char *active,
841c65a2111SKyle Evans     const char *child_path)
842c65a2111SKyle Evans {
843c65a2111SKyle Evans 	nvlist_t *props;
844c65a2111SKyle Evans 	zfs_handle_t *zfs;
845c65a2111SKyle Evans 	int err;
846c65a2111SKyle Evans 
847c65a2111SKyle Evans 	nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP);
848c65a2111SKyle Evans 	nvlist_add_string(props, "canmount", "noauto");
849c65a2111SKyle Evans 	nvlist_add_string(props, "mountpoint", child_path);
850c65a2111SKyle Evans 
851c65a2111SKyle Evans 	/* Create */
852c65a2111SKyle Evans 	if ((err = zfs_create(lbh->lzh, active, ZFS_TYPE_DATASET,
853c65a2111SKyle Evans 	    props)) != 0) {
854c65a2111SKyle Evans 		switch (err) {
855c65a2111SKyle Evans 		case EZFS_EXISTS:
856c65a2111SKyle Evans 			return (set_error(lbh, BE_ERR_EXISTS));
857c65a2111SKyle Evans 		case EZFS_NOENT:
858c65a2111SKyle Evans 			return (set_error(lbh, BE_ERR_NOENT));
859c65a2111SKyle Evans 		case EZFS_BADTYPE:
860c65a2111SKyle Evans 		case EZFS_BADVERSION:
861c65a2111SKyle Evans 			return (set_error(lbh, BE_ERR_NOPOOL));
862c65a2111SKyle Evans 		case EZFS_BADPROP:
863c65a2111SKyle Evans 		default:
864c65a2111SKyle Evans 			/* We set something up wrong, probably... */
865c65a2111SKyle Evans 			return (set_error(lbh, BE_ERR_UNKNOWN));
866c65a2111SKyle Evans 		}
867c65a2111SKyle Evans 	}
868c65a2111SKyle Evans 	nvlist_free(props);
869c65a2111SKyle Evans 
870c65a2111SKyle Evans 	if ((zfs = zfs_open(lbh->lzh, active, ZFS_TYPE_DATASET)) == NULL)
871c65a2111SKyle Evans 		return (set_error(lbh, BE_ERR_ZFSOPEN));
872c65a2111SKyle Evans 
873c65a2111SKyle Evans 	/* Set props */
874c65a2111SKyle Evans 	if ((err = zfs_prop_set(zfs, "canmount", "noauto")) != 0) {
875c65a2111SKyle Evans 		zfs_close(zfs);
876c65a2111SKyle Evans 		/*
877c65a2111SKyle Evans 		 * Similar to other cases, this shouldn't fail unless we've
878c65a2111SKyle Evans 		 * done something wrong.  This is a new dataset that shouldn't
879c65a2111SKyle Evans 		 * have been mounted anywhere between creation and now.
880c65a2111SKyle Evans 		 */
881c65a2111SKyle Evans 		if (err == EZFS_NOMEM)
882c65a2111SKyle Evans 			return (set_error(lbh, BE_ERR_NOMEM));
883c65a2111SKyle Evans 		return (set_error(lbh, BE_ERR_UNKNOWN));
884c65a2111SKyle Evans 	}
885c65a2111SKyle Evans 	zfs_close(zfs);
886c65a2111SKyle Evans 	return (BE_ERR_SUCCESS);
887c65a2111SKyle Evans }
888c65a2111SKyle Evans 
889c65a2111SKyle Evans static int
890c65a2111SKyle Evans be_create_child_cloned(libbe_handle_t *lbh, const char *active)
891c65a2111SKyle Evans {
8923d1a1f2cSKyle Evans 	char buf[BE_MAXPATHLEN], tmp[BE_MAXPATHLEN];;
893c65a2111SKyle Evans 	zfs_handle_t *zfs;
894c65a2111SKyle Evans 	int err;
895c65a2111SKyle Evans 
896c65a2111SKyle Evans 	/* XXX TODO ? */
897c65a2111SKyle Evans 
898c65a2111SKyle Evans 	/*
899c65a2111SKyle Evans 	 * Establish if the existing path is a zfs dataset or just
900c65a2111SKyle Evans 	 * the subdirectory of one
901c65a2111SKyle Evans 	 */
9023d1a1f2cSKyle Evans 	strlcpy(tmp, "tmp/be_snap.XXXXX", sizeof(tmp));
9033d1a1f2cSKyle Evans 	if (mktemp(tmp) == NULL)
904c65a2111SKyle Evans 		return (set_error(lbh, BE_ERR_UNKNOWN));
905c65a2111SKyle Evans 
9063d1a1f2cSKyle Evans 	be_root_concat(lbh, tmp, buf);
9073d1a1f2cSKyle Evans 	printf("Here %s?\n", buf);
908c65a2111SKyle Evans 	if ((err = zfs_snapshot(lbh->lzh, buf, false, NULL)) != 0) {
909c65a2111SKyle Evans 		switch (err) {
910c65a2111SKyle Evans 		case EZFS_INVALIDNAME:
911c65a2111SKyle Evans 			return (set_error(lbh, BE_ERR_INVALIDNAME));
912c65a2111SKyle Evans 
913c65a2111SKyle Evans 		default:
914c65a2111SKyle Evans 			/*
915c65a2111SKyle Evans 			 * The other errors that zfs_ioc_snapshot might return
916c65a2111SKyle Evans 			 * shouldn't happen if we've set things up properly, so
917c65a2111SKyle Evans 			 * we'll gloss over them and call it UNKNOWN as it will
918c65a2111SKyle Evans 			 * require further triage.
919c65a2111SKyle Evans 			 */
920c65a2111SKyle Evans 			if (errno == ENOTSUP)
921c65a2111SKyle Evans 				return (set_error(lbh, BE_ERR_NOPOOL));
922c65a2111SKyle Evans 			return (set_error(lbh, BE_ERR_UNKNOWN));
923c65a2111SKyle Evans 		}
924c65a2111SKyle Evans 	}
925c65a2111SKyle Evans 
926c65a2111SKyle Evans 	/* Clone */
927c65a2111SKyle Evans 	if ((zfs = zfs_open(lbh->lzh, buf, ZFS_TYPE_SNAPSHOT)) == NULL)
928c65a2111SKyle Evans 		return (BE_ERR_ZFSOPEN);
929c65a2111SKyle Evans 
930c65a2111SKyle Evans 	if ((err = zfs_clone(zfs, active, NULL)) != 0)
931c65a2111SKyle Evans 		/* XXX TODO correct error */
932c65a2111SKyle Evans 		return (set_error(lbh, BE_ERR_UNKNOWN));
933c65a2111SKyle Evans 
934c65a2111SKyle Evans 	/* set props */
935c65a2111SKyle Evans 	zfs_close(zfs);
936c65a2111SKyle Evans 	return (BE_ERR_SUCCESS);
937c65a2111SKyle Evans }
93828f16a0fSKyle Evans 
93928f16a0fSKyle Evans int
94073c3d608SKyle Evans be_add_child(libbe_handle_t *lbh, const char *child_path, bool cp_if_exists)
94128f16a0fSKyle Evans {
94273c3d608SKyle Evans 	struct stat sb;
943c65a2111SKyle Evans 	char active[BE_MAXPATHLEN], buf[BE_MAXPATHLEN];
94428f16a0fSKyle Evans 	nvlist_t *props;
94573c3d608SKyle Evans 	const char *s;
94628f16a0fSKyle Evans 
94728f16a0fSKyle Evans 	/* Require absolute paths */
948bfe0869cSKyle Evans 	if (*child_path != '/')
9496d4b1d24SKyle Evans 		return (set_error(lbh, BE_ERR_BADPATH));
95028f16a0fSKyle Evans 
9516d4b1d24SKyle Evans 	strlcpy(active, be_active_path(lbh), BE_MAXPATHLEN);
95228f16a0fSKyle Evans 	strcpy(buf, active);
95328f16a0fSKyle Evans 
95428f16a0fSKyle Evans 	/* Create non-mountable parent dataset(s) */
95573c3d608SKyle Evans 	s = child_path;
95628f16a0fSKyle Evans 	for (char *p; (p = strchr(s+1, '/')) != NULL; s = p) {
95728f16a0fSKyle Evans 		size_t len = p - s;
95828f16a0fSKyle Evans 		strncat(buf, s, len);
95928f16a0fSKyle Evans 
96028f16a0fSKyle Evans 		nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP);
96128f16a0fSKyle Evans 		nvlist_add_string(props, "canmount", "off");
96228f16a0fSKyle Evans 		nvlist_add_string(props, "mountpoint", "none");
96328f16a0fSKyle Evans 		zfs_create(lbh->lzh, buf, ZFS_TYPE_DATASET, props);
96428f16a0fSKyle Evans 		nvlist_free(props);
96528f16a0fSKyle Evans 	}
96628f16a0fSKyle Evans 
96728f16a0fSKyle Evans 	/* Path does not exist as a descendent of / yet */
9686d4b1d24SKyle Evans 	if (strlcat(active, child_path, BE_MAXPATHLEN) >= BE_MAXPATHLEN)
9696d4b1d24SKyle Evans 		return (set_error(lbh, BE_ERR_PATHLEN));
97028f16a0fSKyle Evans 
97128f16a0fSKyle Evans 	if (stat(child_path, &sb) != 0) {
97228f16a0fSKyle Evans 		/* Verify that error is ENOENT */
9736d4b1d24SKyle Evans 		if (errno != ENOENT)
9746d4b1d24SKyle Evans 			return (set_error(lbh, BE_ERR_UNKNOWN));
975c65a2111SKyle Evans 		return (be_create_child_noent(lbh, active, child_path));
976c65a2111SKyle Evans 	} else if (cp_if_exists)
97728f16a0fSKyle Evans 		/* Path is already a descendent of / and should be copied */
978c65a2111SKyle Evans 		return (be_create_child_cloned(lbh, active));
9796d4b1d24SKyle Evans 	return (set_error(lbh, BE_ERR_EXISTS));
98028f16a0fSKyle Evans }
9813d1a1f2cSKyle Evans #endif	/* SOON */
98228f16a0fSKyle Evans 
983d06f7103SKyle Evans static int
984d06f7103SKyle Evans be_set_nextboot(libbe_handle_t *lbh, nvlist_t *config, uint64_t pool_guid,
985d06f7103SKyle Evans     const char *zfsdev)
986d06f7103SKyle Evans {
987d06f7103SKyle Evans 	nvlist_t **child;
988d06f7103SKyle Evans 	uint64_t vdev_guid;
989d06f7103SKyle Evans 	int c, children;
990d06f7103SKyle Evans 
991d06f7103SKyle Evans 	if (nvlist_lookup_nvlist_array(config, ZPOOL_CONFIG_CHILDREN, &child,
992d06f7103SKyle Evans 	    &children) == 0) {
993d06f7103SKyle Evans 		for (c = 0; c < children; ++c)
994d06f7103SKyle Evans 			if (be_set_nextboot(lbh, child[c], pool_guid, zfsdev) != 0)
995d06f7103SKyle Evans 				return (1);
996d06f7103SKyle Evans 		return (0);
997d06f7103SKyle Evans 	}
998d06f7103SKyle Evans 
999d06f7103SKyle Evans 	if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_GUID,
1000d06f7103SKyle Evans 	    &vdev_guid) != 0) {
1001d06f7103SKyle Evans 		return (1);
1002d06f7103SKyle Evans 	}
1003d06f7103SKyle Evans 
1004d06f7103SKyle Evans 	if (zpool_nextboot(lbh->lzh, pool_guid, vdev_guid, zfsdev) != 0) {
1005d06f7103SKyle Evans 		perror("ZFS_IOC_NEXTBOOT failed");
1006d06f7103SKyle Evans 		return (1);
1007d06f7103SKyle Evans 	}
1008d06f7103SKyle Evans 
1009d06f7103SKyle Evans 	return (0);
1010d06f7103SKyle Evans }
1011d06f7103SKyle Evans 
10128d4ce358SKyle Evans /*
10138d4ce358SKyle Evans  * Deactivate old BE dataset; currently just sets canmount=noauto
10148d4ce358SKyle Evans  */
10158d4ce358SKyle Evans static int
10168d4ce358SKyle Evans be_deactivate(libbe_handle_t *lbh, const char *ds)
10178d4ce358SKyle Evans {
10188d4ce358SKyle Evans 	zfs_handle_t *zfs;
10198d4ce358SKyle Evans 
10208d4ce358SKyle Evans 	if ((zfs = zfs_open(lbh->lzh, ds, ZFS_TYPE_DATASET)) == NULL)
10218d4ce358SKyle Evans 		return (1);
10228d4ce358SKyle Evans 	if (zfs_prop_set(zfs, "canmount", "noauto") != 0)
10238d4ce358SKyle Evans 		return (1);
10248d4ce358SKyle Evans 	zfs_close(zfs);
10258d4ce358SKyle Evans 	return (0);
10268d4ce358SKyle Evans }
102728f16a0fSKyle Evans 
102828f16a0fSKyle Evans int
102973c3d608SKyle Evans be_activate(libbe_handle_t *lbh, const char *bootenv, bool temporary)
103028f16a0fSKyle Evans {
103128f16a0fSKyle Evans 	char be_path[BE_MAXPATHLEN];
103228f16a0fSKyle Evans 	char buf[BE_MAXPATHLEN];
10334635676dSKyle Evans 	nvlist_t *config, *dsprops, *vdevs;
10344635676dSKyle Evans 	char *origin;
10350cadc427SKyle Evans 	uint64_t pool_guid;
10360cadc427SKyle Evans 	zfs_handle_t *zhp;
103728f16a0fSKyle Evans 	int err;
103828f16a0fSKyle Evans 
103928f16a0fSKyle Evans 	be_root_concat(lbh, bootenv, be_path);
104028f16a0fSKyle Evans 
104128f16a0fSKyle Evans 	/* Note: be_exists fails if mountpoint is not / */
1042162ec569SKyle Evans 	if ((err = be_exists(lbh, be_path)) != 0)
1043162ec569SKyle Evans 		return (set_error(lbh, err));
104428f16a0fSKyle Evans 
104528f16a0fSKyle Evans 	if (temporary) {
1046d06f7103SKyle Evans 		config = zpool_get_config(lbh->active_phandle, NULL);
1047c65a2111SKyle Evans 		if (config == NULL)
1048c65a2111SKyle Evans 			/* config should be fetchable... */
1049c65a2111SKyle Evans 			return (set_error(lbh, BE_ERR_UNKNOWN));
105028f16a0fSKyle Evans 
1051d06f7103SKyle Evans 		if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID,
1052d06f7103SKyle Evans 		    &pool_guid) != 0)
1053c65a2111SKyle Evans 			/* Similarly, it shouldn't be possible */
1054c65a2111SKyle Evans 			return (set_error(lbh, BE_ERR_UNKNOWN));
1055d06f7103SKyle Evans 
105628f16a0fSKyle Evans 		/* Expected format according to zfsbootcfg(8) man */
1057a8e44f4dSKyle Evans 		snprintf(buf, sizeof(buf), "zfs:%s:", be_path);
105828f16a0fSKyle Evans 
1059c65a2111SKyle Evans 		/* We have no config tree */
1060c65a2111SKyle Evans 		if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
1061c65a2111SKyle Evans 		    &vdevs) != 0)
1062c65a2111SKyle Evans 			return (set_error(lbh, BE_ERR_NOPOOL));
106328f16a0fSKyle Evans 
1064d06f7103SKyle Evans 		return (be_set_nextboot(lbh, vdevs, pool_guid, buf));
106528f16a0fSKyle Evans 	} else {
10668d4ce358SKyle Evans 		if (be_deactivate(lbh, lbh->bootfs) != 0)
10678d4ce358SKyle Evans 			return (-1);
10688d4ce358SKyle Evans 
106928f16a0fSKyle Evans 		/* Obtain bootenv zpool */
1070c3a34c08SKyle Evans 		err = zpool_set_prop(lbh->active_phandle, "bootfs", be_path);
10710cadc427SKyle Evans 		if (err)
10720cadc427SKyle Evans 			return (-1);
107328f16a0fSKyle Evans 
10740cadc427SKyle Evans 		zhp = zfs_open(lbh->lzh, be_path, ZFS_TYPE_FILESYSTEM);
10750cadc427SKyle Evans 		if (zhp == NULL)
10760cadc427SKyle Evans 			return (-1);
107728f16a0fSKyle Evans 
10784635676dSKyle Evans 		if (be_prop_list_alloc(&dsprops) != 0)
10794635676dSKyle Evans 			return (-1);
10804635676dSKyle Evans 
10814635676dSKyle Evans 		if (be_get_dataset_props(lbh, be_path, dsprops) != 0) {
10824635676dSKyle Evans 			nvlist_free(dsprops);
10834635676dSKyle Evans 			return (-1);
10844635676dSKyle Evans 		}
10854635676dSKyle Evans 
10864635676dSKyle Evans 		if (nvlist_lookup_string(dsprops, "origin", &origin) == 0)
10870cadc427SKyle Evans 			err = zfs_promote(zhp);
10884635676dSKyle Evans 		nvlist_free(dsprops);
10894635676dSKyle Evans 
10900cadc427SKyle Evans 		zfs_close(zhp);
10910cadc427SKyle Evans 
10920cadc427SKyle Evans 		if (err)
109328f16a0fSKyle Evans 			return (-1);
109428f16a0fSKyle Evans 	}
10950cadc427SKyle Evans 
10960cadc427SKyle Evans 	return (BE_ERR_SUCCESS);
109728f16a0fSKyle Evans }
1098