xref: /freebsd/lib/libbe/be.c (revision 485172f537e195e3d5ca9467192f73c1893ab537)
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 
37*485172f5SKyle Evans #include <sys/zfs_context.h>
38*485172f5SKyle Evans #include <sys/mntent.h>
39*485172f5SKyle Evans 
4028f16a0fSKyle Evans #include <ctype.h>
4128f16a0fSKyle Evans #include <libgen.h>
4228f16a0fSKyle Evans #include <libzfs_core.h>
4328f16a0fSKyle Evans #include <stdio.h>
4428f16a0fSKyle Evans #include <stdlib.h>
4528f16a0fSKyle Evans #include <time.h>
4628f16a0fSKyle Evans #include <unistd.h>
4728f16a0fSKyle Evans 
4828f16a0fSKyle Evans #include "be.h"
4928f16a0fSKyle Evans #include "be_impl.h"
5028f16a0fSKyle Evans 
51be7dd423SKyle Evans struct be_destroy_data {
52be7dd423SKyle Evans 	libbe_handle_t		*lbh;
53be7dd423SKyle Evans 	char			*snapname;
54be7dd423SKyle Evans };
55be7dd423SKyle Evans 
563d1a1f2cSKyle Evans #if SOON
57c65a2111SKyle Evans static int be_create_child_noent(libbe_handle_t *lbh, const char *active,
58c65a2111SKyle Evans     const char *child_path);
59c65a2111SKyle Evans static int be_create_child_cloned(libbe_handle_t *lbh, const char *active);
603d1a1f2cSKyle Evans #endif
61c65a2111SKyle Evans 
6290cf61e8SKyle Evans /* Arbitrary... should tune */
6390cf61e8SKyle Evans #define	BE_SNAP_SERIAL_MAX	1024
6490cf61e8SKyle Evans 
6528f16a0fSKyle Evans /*
66ee16b7c9SKyle Evans  * Iterator function for locating the rootfs amongst the children of the
67ee16b7c9SKyle Evans  * zfs_be_root set by loader(8).  data is expected to be a libbe_handle_t *.
68ee16b7c9SKyle Evans  */
69ee16b7c9SKyle Evans static int
7051aecc89SKyle Evans be_locate_rootfs(libbe_handle_t *lbh)
71ee16b7c9SKyle Evans {
724ab5187dSKyle Evans 	struct statfs sfs;
73*485172f5SKyle Evans 	struct mnttab entry;
7451aecc89SKyle Evans 	zfs_handle_t *zfs;
75ee16b7c9SKyle Evans 
764ab5187dSKyle Evans 	/*
774ab5187dSKyle Evans 	 * Check first if root is ZFS; if not, we'll bail on rootfs capture.
784ab5187dSKyle Evans 	 * Unfortunately needed because zfs_path_to_zhandle will emit to
794ab5187dSKyle Evans 	 * stderr if / isn't actually a ZFS filesystem, which we'd like
804ab5187dSKyle Evans 	 * to avoid.
814ab5187dSKyle Evans 	 */
824ab5187dSKyle Evans 	if (statfs("/", &sfs) == 0) {
834ab5187dSKyle Evans 		statfs2mnttab(&sfs, &entry);
844ab5187dSKyle Evans 		if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0)
854ab5187dSKyle Evans 			return (1);
864ab5187dSKyle Evans 	} else
874ab5187dSKyle Evans 		return (1);
8851aecc89SKyle Evans 	zfs = zfs_path_to_zhandle(lbh->lzh, "/", ZFS_TYPE_FILESYSTEM);
8951aecc89SKyle Evans 	if (zfs == NULL)
90ee16b7c9SKyle Evans 		return (1);
91ee16b7c9SKyle Evans 
9251aecc89SKyle Evans 	strlcpy(lbh->rootfs, zfs_get_name(zfs), sizeof(lbh->rootfs));
9351aecc89SKyle Evans 	zfs_close(zfs);
94ee16b7c9SKyle Evans 	return (0);
95ee16b7c9SKyle Evans }
96ee16b7c9SKyle Evans 
97ee16b7c9SKyle Evans /*
9828f16a0fSKyle Evans  * Initializes the libbe context to operate in the root boot environment
9928f16a0fSKyle Evans  * dataset, for example, zroot/ROOT.
10028f16a0fSKyle Evans  */
10128f16a0fSKyle Evans libbe_handle_t *
102cc624025SKyle Evans libbe_init(const char *root)
10328f16a0fSKyle Evans {
104fc13fc1cSKyle Evans 	char altroot[MAXPATHLEN];
10528f16a0fSKyle Evans 	libbe_handle_t *lbh;
106c3a34c08SKyle Evans 	char *poolname, *pos;
107c3a34c08SKyle Evans 	int pnamelen;
10828f16a0fSKyle Evans 
109c3a34c08SKyle Evans 	lbh = NULL;
110c3a34c08SKyle Evans 	poolname = pos = NULL;
11128f16a0fSKyle Evans 
112c3a34c08SKyle Evans 	if ((lbh = calloc(1, sizeof(libbe_handle_t))) == NULL)
113c3a34c08SKyle Evans 		goto err;
11428f16a0fSKyle Evans 
115c3a34c08SKyle Evans 	if ((lbh->lzh = libzfs_init()) == NULL)
116c3a34c08SKyle Evans 		goto err;
11728f16a0fSKyle Evans 
118cc624025SKyle Evans 	/*
119cc624025SKyle Evans 	 * Grab rootfs, we'll work backwards from there if an optional BE root
120cc624025SKyle Evans 	 * has not been passed in.
121cc624025SKyle Evans 	 */
1224ab5187dSKyle Evans 	if (be_locate_rootfs(lbh) != 0) {
1234ab5187dSKyle Evans 		if (root == NULL)
124c3a34c08SKyle Evans 			goto err;
1254ab5187dSKyle Evans 		*lbh->rootfs = '\0';
1264ab5187dSKyle Evans 	}
127cc624025SKyle Evans 	if (root == NULL) {
128cc624025SKyle Evans 		/* Strip off the final slash from rootfs to get the be root */
12951aecc89SKyle Evans 		strlcpy(lbh->root, lbh->rootfs, sizeof(lbh->root));
13051aecc89SKyle Evans 		pos = strrchr(lbh->root, '/');
13151aecc89SKyle Evans 		if (pos == NULL)
13251aecc89SKyle Evans 			goto err;
13351aecc89SKyle Evans 		*pos = '\0';
134cc624025SKyle Evans 	} else
135cc624025SKyle Evans 		strlcpy(lbh->root, root, sizeof(lbh->root));
136c3a34c08SKyle Evans 
137c3a34c08SKyle Evans 	if ((pos = strchr(lbh->root, '/')) == NULL)
138c3a34c08SKyle Evans 		goto err;
139c3a34c08SKyle Evans 
140c3a34c08SKyle Evans 	pnamelen = pos - lbh->root;
141c3a34c08SKyle Evans 	poolname = malloc(pnamelen + 1);
142c3a34c08SKyle Evans 	if (poolname == NULL)
143c3a34c08SKyle Evans 		goto err;
144c3a34c08SKyle Evans 
14555b0e92bSKyle Evans 	strlcpy(poolname, lbh->root, pnamelen + 1);
146c3a34c08SKyle Evans 	if ((lbh->active_phandle = zpool_open(lbh->lzh, poolname)) == NULL)
147c3a34c08SKyle Evans 		goto err;
148a8e44f4dSKyle Evans 	free(poolname);
149a8e44f4dSKyle Evans 	poolname = NULL;
150c3a34c08SKyle Evans 
151c3a34c08SKyle Evans 	if (zpool_get_prop(lbh->active_phandle, ZPOOL_PROP_BOOTFS, lbh->bootfs,
15255b0e92bSKyle Evans 	    sizeof(lbh->bootfs), NULL, true) != 0)
153c3a34c08SKyle Evans 		goto err;
154c3a34c08SKyle Evans 
155fc13fc1cSKyle Evans 	if (zpool_get_prop(lbh->active_phandle, ZPOOL_PROP_ALTROOT,
156fc13fc1cSKyle Evans 	    altroot, sizeof(altroot), NULL, true) == 0 &&
157fc13fc1cSKyle Evans 	    strcmp(altroot, "-") != 0)
158fc13fc1cSKyle Evans 		lbh->altroot_len = strlen(altroot);
159fc13fc1cSKyle Evans 
16028f16a0fSKyle Evans 	return (lbh);
161c3a34c08SKyle Evans err:
162c3a34c08SKyle Evans 	if (lbh != NULL) {
163c3a34c08SKyle Evans 		if (lbh->active_phandle != NULL)
164c3a34c08SKyle Evans 			zpool_close(lbh->active_phandle);
165c3a34c08SKyle Evans 		if (lbh->lzh != NULL)
166c3a34c08SKyle Evans 			libzfs_fini(lbh->lzh);
167c3a34c08SKyle Evans 		free(lbh);
168c3a34c08SKyle Evans 	}
169c3a34c08SKyle Evans 	free(poolname);
170c3a34c08SKyle Evans 	return (NULL);
17128f16a0fSKyle Evans }
17228f16a0fSKyle Evans 
17328f16a0fSKyle Evans 
17428f16a0fSKyle Evans /*
17528f16a0fSKyle Evans  * Free memory allocated by libbe_init()
17628f16a0fSKyle Evans  */
17728f16a0fSKyle Evans void
17828f16a0fSKyle Evans libbe_close(libbe_handle_t *lbh)
17928f16a0fSKyle Evans {
180bfe0869cSKyle Evans 
181c3a34c08SKyle Evans 	if (lbh->active_phandle != NULL)
182c3a34c08SKyle Evans 		zpool_close(lbh->active_phandle);
18328f16a0fSKyle Evans 	libzfs_fini(lbh->lzh);
18428f16a0fSKyle Evans 	free(lbh);
18528f16a0fSKyle Evans }
18628f16a0fSKyle Evans 
1879b1662e6SKyle Evans /*
1889b1662e6SKyle Evans  * Proxy through to libzfs for the moment.
1899b1662e6SKyle Evans  */
1909b1662e6SKyle Evans void
1919b1662e6SKyle Evans be_nicenum(uint64_t num, char *buf, size_t buflen)
1929b1662e6SKyle Evans {
1939b1662e6SKyle Evans 
1949b1662e6SKyle Evans 	zfs_nicenum(num, buf, buflen);
1959b1662e6SKyle Evans }
19628f16a0fSKyle Evans 
197920abf4dSKyle Evans static int
198920abf4dSKyle Evans be_destroy_cb(zfs_handle_t *zfs_hdl, void *data)
199920abf4dSKyle Evans {
200be7dd423SKyle Evans 	char path[BE_MAXPATHLEN];
201be7dd423SKyle Evans 	struct be_destroy_data *bdd;
202be7dd423SKyle Evans 	zfs_handle_t *snap;
203920abf4dSKyle Evans 	int err;
204920abf4dSKyle Evans 
205be7dd423SKyle Evans 	bdd = (struct be_destroy_data *)data;
206be7dd423SKyle Evans 	if (bdd->snapname == NULL) {
207be7dd423SKyle Evans 		err = zfs_iter_children(zfs_hdl, be_destroy_cb, data);
208be7dd423SKyle Evans 		if (err != 0)
209920abf4dSKyle Evans 			return (err);
210be7dd423SKyle Evans 		return (zfs_destroy(zfs_hdl, false));
211be7dd423SKyle Evans 	}
212be7dd423SKyle Evans 	/* If we're dealing with snapshots instead, delete that one alone */
213be7dd423SKyle Evans 	err = zfs_iter_filesystems(zfs_hdl, be_destroy_cb, data);
214be7dd423SKyle Evans 	if (err != 0)
215920abf4dSKyle Evans 		return (err);
216be7dd423SKyle Evans 	/*
217be7dd423SKyle Evans 	 * This part is intentionally glossing over any potential errors,
218be7dd423SKyle Evans 	 * because there's a lot less potential for errors when we're cleaning
219be7dd423SKyle Evans 	 * up snapshots rather than a full deep BE.  The primary error case
220be7dd423SKyle Evans 	 * here being if the snapshot doesn't exist in the first place, which
221be7dd423SKyle Evans 	 * the caller will likely deem insignificant as long as it doesn't
222be7dd423SKyle Evans 	 * exist after the call.  Thus, such a missing snapshot shouldn't jam
223be7dd423SKyle Evans 	 * up the destruction.
224be7dd423SKyle Evans 	 */
225be7dd423SKyle Evans 	snprintf(path, sizeof(path), "%s@%s", zfs_get_name(zfs_hdl),
226be7dd423SKyle Evans 	    bdd->snapname);
227be7dd423SKyle Evans 	if (!zfs_dataset_exists(bdd->lbh->lzh, path, ZFS_TYPE_SNAPSHOT))
228be7dd423SKyle Evans 		return (0);
229be7dd423SKyle Evans 	snap = zfs_open(bdd->lbh->lzh, path, ZFS_TYPE_SNAPSHOT);
230be7dd423SKyle Evans 	if (snap != NULL)
231be7dd423SKyle Evans 		zfs_destroy(snap, false);
232920abf4dSKyle Evans 	return (0);
233920abf4dSKyle Evans }
234920abf4dSKyle Evans 
2351dc85563SKyle Evans #define	BE_DESTROY_WANTORIGIN	(BE_DESTROY_ORIGIN | BE_DESTROY_AUTOORIGIN)
23628f16a0fSKyle Evans /*
23728f16a0fSKyle Evans  * Destroy the boot environment or snapshot specified by the name
23828f16a0fSKyle Evans  * parameter. Options are or'd together with the possible values:
23928f16a0fSKyle Evans  * BE_DESTROY_FORCE : forces operation on mounted datasets
240be7dd423SKyle Evans  * BE_DESTROY_ORIGIN: destroy the origin snapshot as well
24128f16a0fSKyle Evans  */
24228f16a0fSKyle Evans int
24373c3d608SKyle Evans be_destroy(libbe_handle_t *lbh, const char *name, int options)
24428f16a0fSKyle Evans {
245be7dd423SKyle Evans 	struct be_destroy_data bdd;
24613c62c50SKyle Evans 	char origin[BE_MAXPATHLEN], path[BE_MAXPATHLEN];
24728f16a0fSKyle Evans 	zfs_handle_t *fs;
248be7dd423SKyle Evans 	char *snapdelim;
249bfe0869cSKyle Evans 	int err, force, mounted;
250be7dd423SKyle Evans 	size_t rootlen;
25128f16a0fSKyle Evans 
252be7dd423SKyle Evans 	bdd.lbh = lbh;
253be7dd423SKyle Evans 	bdd.snapname = NULL;
254bfe0869cSKyle Evans 	force = options & BE_DESTROY_FORCE;
25513c62c50SKyle Evans 	*origin = '\0';
25628f16a0fSKyle Evans 
25728f16a0fSKyle Evans 	be_root_concat(lbh, name, path);
25828f16a0fSKyle Evans 
259be7dd423SKyle Evans 	if ((snapdelim = strchr(path, '@')) == NULL) {
260bfe0869cSKyle Evans 		if (!zfs_dataset_exists(lbh->lzh, path, ZFS_TYPE_FILESYSTEM))
26128f16a0fSKyle Evans 			return (set_error(lbh, BE_ERR_NOENT));
26228f16a0fSKyle Evans 
263f08dac4eSKyle Evans 		if (strcmp(path, lbh->rootfs) == 0 ||
264f08dac4eSKyle Evans 		    strcmp(path, lbh->bootfs) == 0)
26528f16a0fSKyle Evans 			return (set_error(lbh, BE_ERR_DESTROYACT));
26628f16a0fSKyle Evans 
267be7dd423SKyle Evans 		fs = zfs_open(lbh->lzh, path, ZFS_TYPE_FILESYSTEM);
26813c62c50SKyle Evans 		if (fs == NULL)
26913c62c50SKyle Evans 			return (set_error(lbh, BE_ERR_ZFSOPEN));
270be7dd423SKyle Evans 
2711dc85563SKyle Evans 		if ((options & BE_DESTROY_WANTORIGIN) != 0 &&
27213c62c50SKyle Evans 		    zfs_prop_get(fs, ZFS_PROP_ORIGIN, origin, sizeof(origin),
2731dc85563SKyle Evans 		    NULL, NULL, 0, 1) != 0 &&
2741dc85563SKyle Evans 		    (options & BE_DESTROY_ORIGIN) != 0)
27513c62c50SKyle Evans 			return (set_error(lbh, BE_ERR_NOORIGIN));
276e1ee6230SKyle Evans 
277455d8009SKyle Evans 		/*
278455d8009SKyle Evans 		 * If the caller wants auto-origin destruction and the origin
279455d8009SKyle Evans 		 * name matches one of our automatically created snapshot names
280455d8009SKyle Evans 		 * (i.e. strftime("%F-%T") with a serial at the end), then
281455d8009SKyle Evans 		 * we'll set the DESTROY_ORIGIN flag and nuke it
282455d8009SKyle Evans 		 * be_is_auto_snapshot_name is exported from libbe(3) so that
283455d8009SKyle Evans 		 * the caller can determine if it needs to warn about the origin
284455d8009SKyle Evans 		 * not being destroyed or not.
285455d8009SKyle Evans 		 */
2861dc85563SKyle Evans 		if ((options & BE_DESTROY_AUTOORIGIN) != 0 && *origin != '\0' &&
287455d8009SKyle Evans 		    be_is_auto_snapshot_name(lbh, origin))
288455d8009SKyle Evans 			options |= BE_DESTROY_ORIGIN;
289455d8009SKyle Evans 
290e1ee6230SKyle Evans 		/* Don't destroy a mounted dataset unless force is specified */
291e1ee6230SKyle Evans 		if ((mounted = zfs_is_mounted(fs, NULL)) != 0) {
292e1ee6230SKyle Evans 			if (force) {
293e1ee6230SKyle Evans 				zfs_unmount(fs, NULL, 0);
294e1ee6230SKyle Evans 			} else {
295e1ee6230SKyle Evans 				free(bdd.snapname);
296e1ee6230SKyle Evans 				return (set_error(lbh, BE_ERR_DESTROYMNT));
297e1ee6230SKyle Evans 			}
298e1ee6230SKyle Evans 		}
29928f16a0fSKyle Evans 	} else {
300bfe0869cSKyle Evans 		if (!zfs_dataset_exists(lbh->lzh, path, ZFS_TYPE_SNAPSHOT))
30128f16a0fSKyle Evans 			return (set_error(lbh, BE_ERR_NOENT));
30228f16a0fSKyle Evans 
303be7dd423SKyle Evans 		bdd.snapname = strdup(snapdelim + 1);
304be7dd423SKyle Evans 		if (bdd.snapname == NULL)
305be7dd423SKyle Evans 			return (set_error(lbh, BE_ERR_NOMEM));
306be7dd423SKyle Evans 		*snapdelim = '\0';
307be7dd423SKyle Evans 		fs = zfs_open(lbh->lzh, path, ZFS_TYPE_DATASET);
308be7dd423SKyle Evans 		if (fs == NULL) {
309be7dd423SKyle Evans 			free(bdd.snapname);
31028f16a0fSKyle Evans 			return (set_error(lbh, BE_ERR_ZFSOPEN));
31113c62c50SKyle Evans 		}
312be7dd423SKyle Evans 	}
31328f16a0fSKyle Evans 
314be7dd423SKyle Evans 	err = be_destroy_cb(fs, &bdd);
315be7dd423SKyle Evans 	zfs_close(fs);
316be7dd423SKyle Evans 	free(bdd.snapname);
317be7dd423SKyle Evans 	if (err != 0) {
318920abf4dSKyle Evans 		/* Children are still present or the mount is referenced */
319920abf4dSKyle Evans 		if (err == EBUSY)
320920abf4dSKyle Evans 			return (set_error(lbh, BE_ERR_DESTROYMNT));
321920abf4dSKyle Evans 		return (set_error(lbh, BE_ERR_UNKNOWN));
322920abf4dSKyle Evans 	}
32328f16a0fSKyle Evans 
324be7dd423SKyle Evans 	if ((options & BE_DESTROY_ORIGIN) == 0)
325920abf4dSKyle Evans 		return (0);
32628f16a0fSKyle Evans 
327be7dd423SKyle Evans 	/* The origin can't possibly be shorter than the BE root */
328be7dd423SKyle Evans 	rootlen = strlen(lbh->root);
329be7dd423SKyle Evans 	if (*origin == '\0' || strlen(origin) <= rootlen + 1)
330be7dd423SKyle Evans 		return (set_error(lbh, BE_ERR_INVORIGIN));
331be7dd423SKyle Evans 
332be7dd423SKyle Evans 	/*
333be7dd423SKyle Evans 	 * We'll be chopping off the BE root and running this back through
334be7dd423SKyle Evans 	 * be_destroy, so that we properly handle the origin snapshot whether
335be7dd423SKyle Evans 	 * it be that of a deep BE or not.
336be7dd423SKyle Evans 	 */
337be7dd423SKyle Evans 	if (strncmp(origin, lbh->root, rootlen) != 0 || origin[rootlen] != '/')
338be7dd423SKyle Evans 		return (0);
339be7dd423SKyle Evans 
340be7dd423SKyle Evans 	return (be_destroy(lbh, origin + rootlen + 1,
341be7dd423SKyle Evans 	    options & ~BE_DESTROY_ORIGIN));
342be7dd423SKyle Evans }
34328f16a0fSKyle Evans 
34490cf61e8SKyle Evans static void
34590cf61e8SKyle Evans be_setup_snapshot_name(libbe_handle_t *lbh, char *buf, size_t buflen)
34690cf61e8SKyle Evans {
34790cf61e8SKyle Evans 	time_t rawtime;
34890cf61e8SKyle Evans 	int len, serial;
34990cf61e8SKyle Evans 
35090cf61e8SKyle Evans 	time(&rawtime);
35190cf61e8SKyle Evans 	len = strlen(buf);
35290cf61e8SKyle Evans 	len += strftime(buf + len, buflen - len, "@%F-%T", localtime(&rawtime));
35390cf61e8SKyle Evans 	/* No room for serial... caller will do its best */
35490cf61e8SKyle Evans 	if (buflen - len < 2)
35590cf61e8SKyle Evans 		return;
35690cf61e8SKyle Evans 
35790cf61e8SKyle Evans 	for (serial = 0; serial < BE_SNAP_SERIAL_MAX; ++serial) {
35890cf61e8SKyle Evans 		snprintf(buf + len, buflen - len, "-%d", serial);
35990cf61e8SKyle Evans 		if (!zfs_dataset_exists(lbh->lzh, buf, ZFS_TYPE_SNAPSHOT))
36090cf61e8SKyle Evans 			return;
36190cf61e8SKyle Evans 	}
36290cf61e8SKyle Evans }
36390cf61e8SKyle Evans 
364455d8009SKyle Evans bool
365455d8009SKyle Evans be_is_auto_snapshot_name(libbe_handle_t *lbh, const char *name)
366455d8009SKyle Evans {
367455d8009SKyle Evans 	const char *snap;
368455d8009SKyle Evans 	int day, hour, minute, month, second, serial, year;
369455d8009SKyle Evans 
370455d8009SKyle Evans 	if ((snap = strchr(name, '@')) == NULL)
371455d8009SKyle Evans 		return (false);
372455d8009SKyle Evans 	++snap;
373455d8009SKyle Evans 	/* We'll grab the individual components and do some light validation. */
374455d8009SKyle Evans 	if (sscanf(snap, "%d-%d-%d-%d:%d:%d-%d", &year, &month, &day, &hour,
375455d8009SKyle Evans 	    &minute, &second, &serial) != 7)
376455d8009SKyle Evans 		return (false);
377455d8009SKyle Evans 	return (year >= 1970) && (month >= 1 && month <= 12) &&
378455d8009SKyle Evans 	    (day >= 1 && day <= 31) && (hour >= 0 && hour <= 23) &&
379455d8009SKyle Evans 	    (minute >= 0 && minute <= 59) && (second >= 0 && second <= 60) &&
380455d8009SKyle Evans 	    serial >= 0;
381455d8009SKyle Evans }
382455d8009SKyle Evans 
38328f16a0fSKyle Evans int
384b29bf2f8SKyle Evans be_snapshot(libbe_handle_t *lbh, const char *source, const char *snap_name,
38528f16a0fSKyle Evans     bool recursive, char *result)
38628f16a0fSKyle Evans {
38728f16a0fSKyle Evans 	char buf[BE_MAXPATHLEN];
38890cf61e8SKyle Evans 	int err;
38928f16a0fSKyle Evans 
39028f16a0fSKyle Evans 	be_root_concat(lbh, source, buf);
39128f16a0fSKyle Evans 
392162ec569SKyle Evans 	if ((err = be_exists(lbh, buf)) != 0)
393162ec569SKyle Evans 		return (set_error(lbh, err));
39428f16a0fSKyle Evans 
39528f16a0fSKyle Evans 	if (snap_name != NULL) {
396a8e44f4dSKyle Evans 		if (strlcat(buf, "@", sizeof(buf)) >= sizeof(buf))
397a8e44f4dSKyle Evans 			return (set_error(lbh, BE_ERR_INVALIDNAME));
398a8e44f4dSKyle Evans 
399a8e44f4dSKyle Evans 		if (strlcat(buf, snap_name, sizeof(buf)) >= sizeof(buf))
400a8e44f4dSKyle Evans 			return (set_error(lbh, BE_ERR_INVALIDNAME));
401a8e44f4dSKyle Evans 
402bfe0869cSKyle Evans 		if (result != NULL)
40328f16a0fSKyle Evans 			snprintf(result, BE_MAXPATHLEN, "%s@%s", source,
40428f16a0fSKyle Evans 			    snap_name);
40528f16a0fSKyle Evans 	} else {
40690cf61e8SKyle Evans 		be_setup_snapshot_name(lbh, buf, sizeof(buf));
40790cf61e8SKyle Evans 
408a8e44f4dSKyle Evans 		if (result != NULL && strlcpy(result, strrchr(buf, '/') + 1,
409a8e44f4dSKyle Evans 		    sizeof(buf)) >= sizeof(buf))
410a8e44f4dSKyle Evans 			return (set_error(lbh, BE_ERR_INVALIDNAME));
41128f16a0fSKyle Evans 	}
412b29bf2f8SKyle Evans 	if ((err = zfs_snapshot(lbh->lzh, buf, recursive, NULL)) != 0) {
41328f16a0fSKyle Evans 		switch (err) {
41428f16a0fSKyle Evans 		case EZFS_INVALIDNAME:
41528f16a0fSKyle Evans 			return (set_error(lbh, BE_ERR_INVALIDNAME));
41628f16a0fSKyle Evans 
41728f16a0fSKyle Evans 		default:
4182989df09SKyle Evans 			/*
4192989df09SKyle Evans 			 * The other errors that zfs_ioc_snapshot might return
4202989df09SKyle Evans 			 * shouldn't happen if we've set things up properly, so
4212989df09SKyle Evans 			 * we'll gloss over them and call it UNKNOWN as it will
4222989df09SKyle Evans 			 * require further triage.
4232989df09SKyle Evans 			 */
4242989df09SKyle Evans 			if (errno == ENOTSUP)
4252989df09SKyle Evans 				return (set_error(lbh, BE_ERR_NOPOOL));
42628f16a0fSKyle Evans 			return (set_error(lbh, BE_ERR_UNKNOWN));
42728f16a0fSKyle Evans 		}
42828f16a0fSKyle Evans 	}
42928f16a0fSKyle Evans 
43028f16a0fSKyle Evans 	return (BE_ERR_SUCCESS);
43128f16a0fSKyle Evans }
43228f16a0fSKyle Evans 
43328f16a0fSKyle Evans 
43428f16a0fSKyle Evans /*
43528f16a0fSKyle Evans  * Create the boot environment specified by the name parameter
43628f16a0fSKyle Evans  */
43728f16a0fSKyle Evans int
43873c3d608SKyle Evans be_create(libbe_handle_t *lbh, const char *name)
43928f16a0fSKyle Evans {
44028f16a0fSKyle Evans 	int err;
44128f16a0fSKyle Evans 
442b29bf2f8SKyle Evans 	err = be_create_from_existing(lbh, name, be_active_path(lbh));
44328f16a0fSKyle Evans 
44428f16a0fSKyle Evans 	return (set_error(lbh, err));
44528f16a0fSKyle Evans }
44628f16a0fSKyle Evans 
44728f16a0fSKyle Evans static int
44828f16a0fSKyle Evans be_deep_clone_prop(int prop, void *cb)
44928f16a0fSKyle Evans {
45028f16a0fSKyle Evans 	int err;
451bfe0869cSKyle Evans         struct libbe_dccb *dccb;
45228f16a0fSKyle Evans 	zprop_source_t src;
45328f16a0fSKyle Evans 	char pval[BE_MAXPATHLEN];
45428f16a0fSKyle Evans 	char source[BE_MAXPATHLEN];
455af43c24dSKyle Evans 	char *val;
45628f16a0fSKyle Evans 
457bfe0869cSKyle Evans 	dccb = cb;
45828f16a0fSKyle Evans 	/* Skip some properties we don't want to touch */
45973c3d608SKyle Evans 	if (prop == ZFS_PROP_CANMOUNT)
46028f16a0fSKyle Evans 		return (ZPROP_CONT);
46128f16a0fSKyle Evans 
46228f16a0fSKyle Evans 	/* Don't copy readonly properties */
463bfe0869cSKyle Evans 	if (zfs_prop_readonly(prop))
46428f16a0fSKyle Evans 		return (ZPROP_CONT);
46528f16a0fSKyle Evans 
46628f16a0fSKyle Evans 	if ((err = zfs_prop_get(dccb->zhp, prop, (char *)&pval,
467bfe0869cSKyle Evans 	    sizeof(pval), &src, (char *)&source, sizeof(source), false)))
46828f16a0fSKyle Evans 		/* Just continue if we fail to read a property */
46928f16a0fSKyle Evans 		return (ZPROP_CONT);
470bfe0869cSKyle Evans 
471be13d48cSKyle Evans 	/*
472be13d48cSKyle Evans 	 * Only copy locally defined or received properties.  This continues
473be13d48cSKyle Evans 	 * to avoid temporary/default/local properties intentionally without
474be13d48cSKyle Evans 	 * breaking received datasets.
475be13d48cSKyle Evans 	 */
476be13d48cSKyle Evans 	if (src != ZPROP_SRC_LOCAL && src != ZPROP_SRC_RECEIVED)
47728f16a0fSKyle Evans 		return (ZPROP_CONT);
47828f16a0fSKyle Evans 
479af43c24dSKyle Evans 	/* Augment mountpoint with altroot, if needed */
480af43c24dSKyle Evans 	val = pval;
481fc13fc1cSKyle Evans 	if (prop == ZFS_PROP_MOUNTPOINT)
482fc13fc1cSKyle Evans 		val = be_mountpoint_augmented(dccb->lbh, val);
483fc13fc1cSKyle Evans 
484af43c24dSKyle Evans 	nvlist_add_string(dccb->props, zfs_prop_to_name(prop), val);
48528f16a0fSKyle Evans 
48628f16a0fSKyle Evans 	return (ZPROP_CONT);
48728f16a0fSKyle Evans }
48828f16a0fSKyle Evans 
489fa30d9edSKyle Evans /*
490fa30d9edSKyle Evans  * Return the corresponding boot environment path for a given
491fa30d9edSKyle Evans  * dataset path, the constructed path is placed in 'result'.
492fa30d9edSKyle Evans  *
493fa30d9edSKyle Evans  * example: say our new boot environment name is 'bootenv' and
494fa30d9edSKyle Evans  *          the dataset path is 'zroot/ROOT/default/data/set'.
495fa30d9edSKyle Evans  *
496fa30d9edSKyle Evans  * result should produce: 'zroot/ROOT/bootenv/data/set'
497fa30d9edSKyle Evans  */
49828f16a0fSKyle Evans static int
499fa30d9edSKyle Evans be_get_path(struct libbe_deep_clone *ldc, const char *dspath, char *result, int result_size)
500fa30d9edSKyle Evans {
501fa30d9edSKyle Evans 	char *pos;
502fa30d9edSKyle Evans 	char *child_dataset;
503fa30d9edSKyle Evans 
504fa30d9edSKyle Evans 	/* match the root path for the boot environments */
505fa30d9edSKyle Evans 	pos = strstr(dspath, ldc->lbh->root);
506fa30d9edSKyle Evans 
507fa30d9edSKyle Evans 	/* no match, different pools? */
508fa30d9edSKyle Evans 	if (pos == NULL)
509fa30d9edSKyle Evans 		return (BE_ERR_BADPATH);
510fa30d9edSKyle Evans 
511fa30d9edSKyle Evans 	/* root path of the new boot environment */
512fa30d9edSKyle Evans 	snprintf(result, result_size, "%s/%s", ldc->lbh->root, ldc->bename);
513fa30d9edSKyle Evans 
514fa30d9edSKyle Evans         /* gets us to the parent dataset, the +1 consumes a trailing slash */
515fa30d9edSKyle Evans 	pos += strlen(ldc->lbh->root) + 1;
516fa30d9edSKyle Evans 
517fa30d9edSKyle Evans 	/* skip the parent dataset */
518fa30d9edSKyle Evans 	if ((child_dataset = strchr(pos, '/')) != NULL)
519fa30d9edSKyle Evans 		strlcat(result, child_dataset, result_size);
520fa30d9edSKyle Evans 
521fa30d9edSKyle Evans 	return (BE_ERR_SUCCESS);
522fa30d9edSKyle Evans }
523fa30d9edSKyle Evans 
524fa30d9edSKyle Evans static int
525fa30d9edSKyle Evans be_clone_cb(zfs_handle_t *ds, void *data)
52628f16a0fSKyle Evans {
52728f16a0fSKyle Evans 	int err;
52828f16a0fSKyle Evans 	char be_path[BE_MAXPATHLEN];
52928f16a0fSKyle Evans 	char snap_path[BE_MAXPATHLEN];
53028f16a0fSKyle Evans 	const char *dspath;
53128f16a0fSKyle Evans 	zfs_handle_t *snap_hdl;
53228f16a0fSKyle Evans 	nvlist_t *props;
533fa30d9edSKyle Evans 	struct libbe_deep_clone *ldc;
53428f16a0fSKyle Evans 	struct libbe_dccb dccb;
53528f16a0fSKyle Evans 
536fa30d9edSKyle Evans 	ldc = (struct libbe_deep_clone *)data;
53728f16a0fSKyle Evans 	dspath = zfs_get_name(ds);
538bfe0869cSKyle Evans 
539fa30d9edSKyle Evans 	snprintf(snap_path, sizeof(snap_path), "%s@%s", dspath, ldc->snapname);
540bfe0869cSKyle Evans 
541fa30d9edSKyle Evans 	/* construct the boot environment path from the dataset we're cloning */
542fa30d9edSKyle Evans 	if (be_get_path(ldc, dspath, be_path, sizeof(be_path)) != BE_ERR_SUCCESS)
543fa30d9edSKyle Evans 		return (set_error(ldc->lbh, BE_ERR_UNKNOWN));
54428f16a0fSKyle Evans 
545fa30d9edSKyle Evans 	/* the dataset to be created (i.e. the boot environment) already exists */
546fa30d9edSKyle Evans 	if (zfs_dataset_exists(ldc->lbh->lzh, be_path, ZFS_TYPE_DATASET))
547fa30d9edSKyle Evans 		return (set_error(ldc->lbh, BE_ERR_EXISTS));
548fa30d9edSKyle Evans 
549fa30d9edSKyle Evans 	/* no snapshot found for this dataset, silently skip it */
550fa30d9edSKyle Evans 	if (!zfs_dataset_exists(ldc->lbh->lzh, snap_path, ZFS_TYPE_SNAPSHOT))
551fa30d9edSKyle Evans 		return (0);
55228f16a0fSKyle Evans 
55328f16a0fSKyle Evans 	if ((snap_hdl =
554fa30d9edSKyle Evans 	    zfs_open(ldc->lbh->lzh, snap_path, ZFS_TYPE_SNAPSHOT)) == NULL)
555fa30d9edSKyle Evans 		return (set_error(ldc->lbh, BE_ERR_ZFSOPEN));
55628f16a0fSKyle Evans 
55728f16a0fSKyle Evans 	nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP);
55828f16a0fSKyle Evans 	nvlist_add_string(props, "canmount", "noauto");
55928f16a0fSKyle Evans 
560fa30d9edSKyle Evans 	dccb.lbh = ldc->lbh;
56128f16a0fSKyle Evans 	dccb.zhp = ds;
56228f16a0fSKyle Evans 	dccb.props = props;
56328f16a0fSKyle Evans 	if (zprop_iter(be_deep_clone_prop, &dccb, B_FALSE, B_FALSE,
564bfe0869cSKyle Evans 	    ZFS_TYPE_FILESYSTEM) == ZPROP_INVAL)
56528f16a0fSKyle Evans 		return (-1);
56628f16a0fSKyle Evans 
567cc4deabcSKyle Evans 	if ((err = zfs_clone(snap_hdl, be_path, props)) != 0)
568fa30d9edSKyle Evans 		return (set_error(ldc->lbh, BE_ERR_ZFSCLONE));
56928f16a0fSKyle Evans 
57028f16a0fSKyle Evans 	nvlist_free(props);
57128f16a0fSKyle Evans 	zfs_close(snap_hdl);
57228f16a0fSKyle Evans 
573fa30d9edSKyle Evans 	if (ldc->depth_limit == -1 || ldc->depth < ldc->depth_limit) {
574fa30d9edSKyle Evans 		ldc->depth++;
575fa30d9edSKyle Evans 		err = zfs_iter_filesystems(ds, be_clone_cb, ldc);
576fa30d9edSKyle Evans 		ldc->depth--;
577fa30d9edSKyle Evans 	}
578cc4deabcSKyle Evans 
579fa30d9edSKyle Evans 	return (set_error(ldc->lbh, err));
58028f16a0fSKyle Evans }
58128f16a0fSKyle Evans 
58228f16a0fSKyle Evans /*
583fa30d9edSKyle Evans  * Create a boot environment with a given name from a given snapshot.
584fa30d9edSKyle Evans  * Snapshots can be in the format 'zroot/ROOT/default@snapshot' or
585fa30d9edSKyle Evans  * 'default@snapshot'. In the latter case, 'default@snapshot' will be prepended
586fa30d9edSKyle Evans  * with the root path that libbe was initailized with.
58728f16a0fSKyle Evans */
588fa30d9edSKyle Evans static int
589fa30d9edSKyle Evans be_clone(libbe_handle_t *lbh, const char *bename, const char *snapshot, int depth)
59028f16a0fSKyle Evans {
59128f16a0fSKyle Evans 	int err;
59228f16a0fSKyle Evans 	char snap_path[BE_MAXPATHLEN];
593b29bf2f8SKyle Evans 	char *parentname, *snapname;
59428f16a0fSKyle Evans 	zfs_handle_t *parent_hdl;
595fa30d9edSKyle Evans 	struct libbe_deep_clone ldc;
59628f16a0fSKyle Evans 
597fa30d9edSKyle Evans         /* ensure the boot environment name is valid */
598fa30d9edSKyle Evans 	if ((err = be_validate_name(lbh, bename)) != 0)
59928f16a0fSKyle Evans 		return (set_error(lbh, err));
600fa30d9edSKyle Evans 
601fa30d9edSKyle Evans 	/*
602fa30d9edSKyle Evans 	 * prepend the boot environment root path if we're
603fa30d9edSKyle Evans 	 * given a partial snapshot name.
604fa30d9edSKyle Evans 	 */
605fa30d9edSKyle Evans 	if ((err = be_root_concat(lbh, snapshot, snap_path)) != 0)
60628f16a0fSKyle Evans 		return (set_error(lbh, err));
607fa30d9edSKyle Evans 
608fa30d9edSKyle Evans 	/* ensure the snapshot exists */
609b29bf2f8SKyle Evans 	if ((err = be_validate_snap(lbh, snap_path)) != 0)
61028f16a0fSKyle Evans 		return (set_error(lbh, err));
61128f16a0fSKyle Evans 
612fa30d9edSKyle Evans         /* get a copy of the snapshot path so we can disect it */
613cc4deabcSKyle Evans 	if ((parentname = strdup(snap_path)) == NULL)
614cc4deabcSKyle Evans 		return (set_error(lbh, BE_ERR_UNKNOWN));
615cc4deabcSKyle Evans 
616fa30d9edSKyle Evans         /* split dataset name from snapshot name */
61728f16a0fSKyle Evans 	snapname = strchr(parentname, '@');
61828f16a0fSKyle Evans 	if (snapname == NULL) {
619cc4deabcSKyle Evans 		free(parentname);
620cc4deabcSKyle Evans 		return (set_error(lbh, BE_ERR_UNKNOWN));
62128f16a0fSKyle Evans 	}
62228f16a0fSKyle Evans 	*snapname = '\0';
62328f16a0fSKyle Evans 	snapname++;
62428f16a0fSKyle Evans 
625fa30d9edSKyle Evans         /* set-up the boot environment */
626fa30d9edSKyle Evans         ldc.lbh = lbh;
627fa30d9edSKyle Evans         ldc.bename = bename;
628fa30d9edSKyle Evans         ldc.snapname = snapname;
629fa30d9edSKyle Evans 	ldc.depth = 0;
630fa30d9edSKyle Evans 	ldc.depth_limit = depth;
63128f16a0fSKyle Evans 
632fa30d9edSKyle Evans         /* the boot environment will be cloned from this dataset */
63328f16a0fSKyle Evans 	parent_hdl = zfs_open(lbh->lzh, parentname, ZFS_TYPE_DATASET);
634fa30d9edSKyle Evans 
635fa30d9edSKyle Evans         /* create the boot environment */
636fa30d9edSKyle Evans 	err = be_clone_cb(parent_hdl, &ldc);
63728f16a0fSKyle Evans 
638cc4deabcSKyle Evans 	free(parentname);
63928f16a0fSKyle Evans 	return (set_error(lbh, err));
64028f16a0fSKyle Evans }
64128f16a0fSKyle Evans 
642fa30d9edSKyle Evans /*
643fa30d9edSKyle Evans  * Create a boot environment from pre-existing snapshot, specifying a depth.
644fa30d9edSKyle Evans  */
645fa30d9edSKyle Evans int be_create_depth(libbe_handle_t *lbh, const char *bename,
646fa30d9edSKyle Evans 		    const char *snap, int depth)
647fa30d9edSKyle Evans {
648fa30d9edSKyle Evans 	return (be_clone(lbh, bename, snap, depth));
649fa30d9edSKyle Evans }
650fa30d9edSKyle Evans 
651fa30d9edSKyle Evans /*
652fa30d9edSKyle Evans  * Create the boot environment from pre-existing snapshot
653fa30d9edSKyle Evans  */
654fa30d9edSKyle Evans int
655fa30d9edSKyle Evans be_create_from_existing_snap(libbe_handle_t *lbh, const char *bename,
656fa30d9edSKyle Evans     const char *snap)
657fa30d9edSKyle Evans {
658fa30d9edSKyle Evans 	return (be_clone(lbh, bename, snap, -1));
659fa30d9edSKyle Evans }
660fa30d9edSKyle Evans 
66128f16a0fSKyle Evans 
66228f16a0fSKyle Evans /*
66328f16a0fSKyle Evans  * Create a boot environment from an existing boot environment
66428f16a0fSKyle Evans  */
66528f16a0fSKyle Evans int
666fa30d9edSKyle Evans be_create_from_existing(libbe_handle_t *lbh, const char *bename, const char *old)
66728f16a0fSKyle Evans {
66828f16a0fSKyle Evans 	int err;
669fa30d9edSKyle Evans 	char snap[BE_MAXPATHLEN];
67028f16a0fSKyle Evans 
671fa30d9edSKyle Evans 	if ((err = be_snapshot(lbh, old, NULL, true, snap)) != 0)
67228f16a0fSKyle Evans 		return (set_error(lbh, err));
67328f16a0fSKyle Evans 
674fa30d9edSKyle Evans         err = be_clone(lbh, bename, snap, -1);
67528f16a0fSKyle Evans 
67628f16a0fSKyle Evans 	return (set_error(lbh, err));
67728f16a0fSKyle Evans }
67828f16a0fSKyle Evans 
67928f16a0fSKyle Evans 
68028f16a0fSKyle Evans /*
68128f16a0fSKyle Evans  * Verifies that a snapshot has a valid name, exists, and has a mountpoint of
68228f16a0fSKyle Evans  * '/'. Returns BE_ERR_SUCCESS (0), upon success, or the relevant BE_ERR_* upon
68328f16a0fSKyle Evans  * failure. Does not set the internal library error state.
68428f16a0fSKyle Evans  */
68528f16a0fSKyle Evans int
686b29bf2f8SKyle Evans be_validate_snap(libbe_handle_t *lbh, const char *snap_name)
68728f16a0fSKyle Evans {
68828f16a0fSKyle Evans 
689bfe0869cSKyle Evans 	if (strlen(snap_name) >= BE_MAXPATHLEN)
69028f16a0fSKyle Evans 		return (BE_ERR_PATHLEN);
69128f16a0fSKyle Evans 
692fcb47c42SKyle Evans 	if (!zfs_name_valid(snap_name, ZFS_TYPE_SNAPSHOT))
693fcb47c42SKyle Evans 		return (BE_ERR_INVALIDNAME);
694fcb47c42SKyle Evans 
69528f16a0fSKyle Evans 	if (!zfs_dataset_exists(lbh->lzh, snap_name,
696bfe0869cSKyle Evans 	    ZFS_TYPE_SNAPSHOT))
69728f16a0fSKyle Evans 		return (BE_ERR_NOENT);
69828f16a0fSKyle Evans 
69951aecc89SKyle Evans 	return (BE_ERR_SUCCESS);
70028f16a0fSKyle Evans }
70128f16a0fSKyle Evans 
70228f16a0fSKyle Evans 
70328f16a0fSKyle Evans /*
70428f16a0fSKyle Evans  * Idempotently appends the name argument to the root boot environment path
70528f16a0fSKyle Evans  * and copies the resulting string into the result buffer (which is assumed
70628f16a0fSKyle Evans  * to be at least BE_MAXPATHLEN characters long. Returns BE_ERR_SUCCESS upon
70728f16a0fSKyle Evans  * success, BE_ERR_PATHLEN if the resulting path is longer than BE_MAXPATHLEN,
70828f16a0fSKyle Evans  * or BE_ERR_INVALIDNAME if the name is a path that does not begin with
70928f16a0fSKyle Evans  * zfs_be_root. Does not set internal library error state.
71028f16a0fSKyle Evans  */
71128f16a0fSKyle Evans int
712b29bf2f8SKyle Evans be_root_concat(libbe_handle_t *lbh, const char *name, char *result)
71328f16a0fSKyle Evans {
71428f16a0fSKyle Evans 	size_t name_len, root_len;
71528f16a0fSKyle Evans 
71628f16a0fSKyle Evans 	name_len = strlen(name);
71728f16a0fSKyle Evans 	root_len = strlen(lbh->root);
71828f16a0fSKyle Evans 
71928f16a0fSKyle Evans 	/* Act idempotently; return be name if it is already a full path */
72028f16a0fSKyle Evans 	if (strrchr(name, '/') != NULL) {
721bfe0869cSKyle Evans 		if (strstr(name, lbh->root) != name)
72228f16a0fSKyle Evans 			return (BE_ERR_INVALIDNAME);
72328f16a0fSKyle Evans 
724bfe0869cSKyle Evans 		if (name_len >= BE_MAXPATHLEN)
72528f16a0fSKyle Evans 			return (BE_ERR_PATHLEN);
72628f16a0fSKyle Evans 
72755b0e92bSKyle Evans 		strlcpy(result, name, BE_MAXPATHLEN);
72828f16a0fSKyle Evans 		return (BE_ERR_SUCCESS);
72928f16a0fSKyle Evans 	} else if (name_len + root_len + 1 < BE_MAXPATHLEN) {
73028f16a0fSKyle Evans 		snprintf(result, BE_MAXPATHLEN, "%s/%s", lbh->root,
73128f16a0fSKyle Evans 		    name);
73228f16a0fSKyle Evans 		return (BE_ERR_SUCCESS);
73328f16a0fSKyle Evans 	}
73428f16a0fSKyle Evans 
73528f16a0fSKyle Evans 	return (BE_ERR_PATHLEN);
73628f16a0fSKyle Evans }
73728f16a0fSKyle Evans 
73828f16a0fSKyle Evans 
73928f16a0fSKyle Evans /*
74028f16a0fSKyle Evans  * Verifies the validity of a boot environment name (A-Za-z0-9-_.). Returns
7415b7803a9SKyle Evans  * BE_ERR_SUCCESS (0) if name is valid, otherwise returns BE_ERR_INVALIDNAME
7425b7803a9SKyle Evans  * or BE_ERR_PATHLEN.
74328f16a0fSKyle Evans  * Does not set internal library error state.
74428f16a0fSKyle Evans  */
74528f16a0fSKyle Evans int
7465b7803a9SKyle Evans be_validate_name(libbe_handle_t *lbh, const char *name)
74728f16a0fSKyle Evans {
74828f16a0fSKyle Evans 
7495b7803a9SKyle Evans 	/*
7505b7803a9SKyle Evans 	 * Impose the additional restriction that the entire dataset name must
7515b7803a9SKyle Evans 	 * not exceed the maximum length of a dataset, i.e. MAXNAMELEN.
7525b7803a9SKyle Evans 	 */
7535b7803a9SKyle Evans 	if (strlen(lbh->root) + 1 + strlen(name) > MAXNAMELEN)
7545b7803a9SKyle Evans 		return (BE_ERR_PATHLEN);
755fcb47c42SKyle Evans 
756fcb47c42SKyle Evans 	if (!zfs_name_valid(name, ZFS_TYPE_DATASET))
757fcb47c42SKyle Evans 		return (BE_ERR_INVALIDNAME);
758fcb47c42SKyle Evans 
75928f16a0fSKyle Evans 	return (BE_ERR_SUCCESS);
76028f16a0fSKyle Evans }
76128f16a0fSKyle Evans 
76228f16a0fSKyle Evans 
76328f16a0fSKyle Evans /*
76428f16a0fSKyle Evans  * usage
76528f16a0fSKyle Evans  */
76628f16a0fSKyle Evans int
76773c3d608SKyle Evans be_rename(libbe_handle_t *lbh, const char *old, const char *new)
76828f16a0fSKyle Evans {
76928f16a0fSKyle Evans 	char full_old[BE_MAXPATHLEN];
77028f16a0fSKyle Evans 	char full_new[BE_MAXPATHLEN];
77128f16a0fSKyle Evans 	zfs_handle_t *zfs_hdl;
77228f16a0fSKyle Evans 	int err;
77328f16a0fSKyle Evans 
7745b7803a9SKyle Evans 	/*
7755b7803a9SKyle Evans 	 * be_validate_name is documented not to set error state, so we should
7765b7803a9SKyle Evans 	 * do so here.
7775b7803a9SKyle Evans 	 */
7785b7803a9SKyle Evans 	if ((err = be_validate_name(lbh, new)) != 0)
7795b7803a9SKyle Evans 		return (set_error(lbh, err));
780b29bf2f8SKyle Evans 	if ((err = be_root_concat(lbh, old, full_old)) != 0)
78128f16a0fSKyle Evans 		return (set_error(lbh, err));
782b29bf2f8SKyle Evans 	if ((err = be_root_concat(lbh, new, full_new)) != 0)
78328f16a0fSKyle Evans 		return (set_error(lbh, err));
78428f16a0fSKyle Evans 
785bfe0869cSKyle Evans 	if (!zfs_dataset_exists(lbh->lzh, full_old, ZFS_TYPE_DATASET))
7862989df09SKyle Evans 		return (set_error(lbh, BE_ERR_NOENT));
78728f16a0fSKyle Evans 
788bfe0869cSKyle Evans 	if (zfs_dataset_exists(lbh->lzh, full_new, ZFS_TYPE_DATASET))
7892989df09SKyle Evans 		return (set_error(lbh, BE_ERR_EXISTS));
79028f16a0fSKyle Evans 
79128f16a0fSKyle Evans 	if ((zfs_hdl = zfs_open(lbh->lzh, full_old,
792bfe0869cSKyle Evans 	    ZFS_TYPE_FILESYSTEM)) == NULL)
7932989df09SKyle Evans 		return (set_error(lbh, BE_ERR_ZFSOPEN));
79428f16a0fSKyle Evans 
795bfe0869cSKyle Evans 	/* recurse, nounmount, forceunmount */
7968369ba42SKyle Evans 	struct renameflags flags = {
7978369ba42SKyle Evans 		.nounmount = 1,
7988369ba42SKyle Evans 	};
79928f16a0fSKyle Evans 
80028f16a0fSKyle Evans 	err = zfs_rename(zfs_hdl, NULL, full_new, flags);
80128f16a0fSKyle Evans 
80228f16a0fSKyle Evans 	zfs_close(zfs_hdl);
8035b7803a9SKyle Evans 	if (err != 0)
8045b7803a9SKyle Evans 		return (set_error(lbh, BE_ERR_UNKNOWN));
8055b7803a9SKyle Evans 	return (0);
80628f16a0fSKyle Evans }
80728f16a0fSKyle Evans 
80828f16a0fSKyle Evans 
80928f16a0fSKyle Evans int
81073c3d608SKyle Evans be_export(libbe_handle_t *lbh, const char *bootenv, int fd)
81128f16a0fSKyle Evans {
81228f16a0fSKyle Evans 	char snap_name[BE_MAXPATHLEN];
81328f16a0fSKyle Evans 	char buf[BE_MAXPATHLEN];
81428f16a0fSKyle Evans 	zfs_handle_t *zfs;
8158569a95eSAndriy Gapon 	sendflags_t flags = { 0 };
81628f16a0fSKyle Evans 	int err;
81728f16a0fSKyle Evans 
818b29bf2f8SKyle Evans 	if ((err = be_snapshot(lbh, bootenv, NULL, true, snap_name)) != 0)
8196d4b1d24SKyle Evans 		/* Use the error set by be_snapshot */
8206d4b1d24SKyle Evans 		return (err);
82128f16a0fSKyle Evans 
82228f16a0fSKyle Evans 	be_root_concat(lbh, snap_name, buf);
82328f16a0fSKyle Evans 
824bfe0869cSKyle Evans 	if ((zfs = zfs_open(lbh->lzh, buf, ZFS_TYPE_DATASET)) == NULL)
825506f5fdfSKyle Evans 		return (set_error(lbh, BE_ERR_ZFSOPEN));
82628f16a0fSKyle Evans 
8278569a95eSAndriy Gapon 	err = zfs_send_one(zfs, NULL, fd, flags);
8286d4b1d24SKyle Evans 	zfs_close(zfs);
8296d4b1d24SKyle Evans 
83028f16a0fSKyle Evans 	return (err);
83128f16a0fSKyle Evans }
83228f16a0fSKyle Evans 
83328f16a0fSKyle Evans 
83428f16a0fSKyle Evans int
83573c3d608SKyle Evans be_import(libbe_handle_t *lbh, const char *bootenv, int fd)
83628f16a0fSKyle Evans {
83728f16a0fSKyle Evans 	char buf[BE_MAXPATHLEN];
83828f16a0fSKyle Evans 	nvlist_t *props;
83928f16a0fSKyle Evans 	zfs_handle_t *zfs;
84016ac0705SKyle Evans 	recvflags_t flags = { .nomount = 1 };
84116ac0705SKyle Evans 	int err;
84228f16a0fSKyle Evans 
84316ac0705SKyle Evans 	be_root_concat(lbh, bootenv, buf);
84428f16a0fSKyle Evans 
84516ac0705SKyle Evans 	if ((err = zfs_receive(lbh->lzh, buf, NULL, &flags, fd, NULL)) != 0) {
846506f5fdfSKyle Evans 		switch (err) {
847506f5fdfSKyle Evans 		case EINVAL:
848506f5fdfSKyle Evans 			return (set_error(lbh, BE_ERR_NOORIGIN));
849506f5fdfSKyle Evans 		case ENOENT:
850506f5fdfSKyle Evans 			return (set_error(lbh, BE_ERR_NOENT));
851506f5fdfSKyle Evans 		case EIO:
852506f5fdfSKyle Evans 			return (set_error(lbh, BE_ERR_IO));
853506f5fdfSKyle Evans 		default:
854506f5fdfSKyle Evans 			return (set_error(lbh, BE_ERR_UNKNOWN));
855506f5fdfSKyle Evans 		}
85628f16a0fSKyle Evans 	}
85728f16a0fSKyle Evans 
85816ac0705SKyle Evans 	if ((zfs = zfs_open(lbh->lzh, buf, ZFS_TYPE_FILESYSTEM)) == NULL)
859506f5fdfSKyle Evans 		return (set_error(lbh, BE_ERR_ZFSOPEN));
86028f16a0fSKyle Evans 
86128f16a0fSKyle Evans 	nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP);
86228f16a0fSKyle Evans 	nvlist_add_string(props, "canmount", "noauto");
863ac34fe23SKyle Evans 	nvlist_add_string(props, "mountpoint", "none");
86428f16a0fSKyle Evans 
86516ac0705SKyle Evans 	err = zfs_prop_set_list(zfs, props);
86628f16a0fSKyle Evans 	nvlist_free(props);
86728f16a0fSKyle Evans 
8681b057aacSKyle Evans 	zfs_close(zfs);
8691b057aacSKyle Evans 
8701b057aacSKyle Evans 	if (err != 0)
8711b057aacSKyle Evans 		return (set_error(lbh, BE_ERR_UNKNOWN));
8721b057aacSKyle Evans 
87316ac0705SKyle Evans 	return (0);
87428f16a0fSKyle Evans }
87528f16a0fSKyle Evans 
8763d1a1f2cSKyle Evans #if SOON
877c65a2111SKyle Evans static int
878c65a2111SKyle Evans be_create_child_noent(libbe_handle_t *lbh, const char *active,
879c65a2111SKyle Evans     const char *child_path)
880c65a2111SKyle Evans {
881c65a2111SKyle Evans 	nvlist_t *props;
882c65a2111SKyle Evans 	zfs_handle_t *zfs;
883c65a2111SKyle Evans 	int err;
884c65a2111SKyle Evans 
885c65a2111SKyle Evans 	nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP);
886c65a2111SKyle Evans 	nvlist_add_string(props, "canmount", "noauto");
887c65a2111SKyle Evans 	nvlist_add_string(props, "mountpoint", child_path);
888c65a2111SKyle Evans 
889c65a2111SKyle Evans 	/* Create */
890c65a2111SKyle Evans 	if ((err = zfs_create(lbh->lzh, active, ZFS_TYPE_DATASET,
891c65a2111SKyle Evans 	    props)) != 0) {
892c65a2111SKyle Evans 		switch (err) {
893c65a2111SKyle Evans 		case EZFS_EXISTS:
894c65a2111SKyle Evans 			return (set_error(lbh, BE_ERR_EXISTS));
895c65a2111SKyle Evans 		case EZFS_NOENT:
896c65a2111SKyle Evans 			return (set_error(lbh, BE_ERR_NOENT));
897c65a2111SKyle Evans 		case EZFS_BADTYPE:
898c65a2111SKyle Evans 		case EZFS_BADVERSION:
899c65a2111SKyle Evans 			return (set_error(lbh, BE_ERR_NOPOOL));
900c65a2111SKyle Evans 		case EZFS_BADPROP:
901c65a2111SKyle Evans 		default:
902c65a2111SKyle Evans 			/* We set something up wrong, probably... */
903c65a2111SKyle Evans 			return (set_error(lbh, BE_ERR_UNKNOWN));
904c65a2111SKyle Evans 		}
905c65a2111SKyle Evans 	}
906c65a2111SKyle Evans 	nvlist_free(props);
907c65a2111SKyle Evans 
908c65a2111SKyle Evans 	if ((zfs = zfs_open(lbh->lzh, active, ZFS_TYPE_DATASET)) == NULL)
909c65a2111SKyle Evans 		return (set_error(lbh, BE_ERR_ZFSOPEN));
910c65a2111SKyle Evans 
911c65a2111SKyle Evans 	/* Set props */
912c65a2111SKyle Evans 	if ((err = zfs_prop_set(zfs, "canmount", "noauto")) != 0) {
913c65a2111SKyle Evans 		zfs_close(zfs);
914c65a2111SKyle Evans 		/*
915c65a2111SKyle Evans 		 * Similar to other cases, this shouldn't fail unless we've
916c65a2111SKyle Evans 		 * done something wrong.  This is a new dataset that shouldn't
917c65a2111SKyle Evans 		 * have been mounted anywhere between creation and now.
918c65a2111SKyle Evans 		 */
919c65a2111SKyle Evans 		if (err == EZFS_NOMEM)
920c65a2111SKyle Evans 			return (set_error(lbh, BE_ERR_NOMEM));
921c65a2111SKyle Evans 		return (set_error(lbh, BE_ERR_UNKNOWN));
922c65a2111SKyle Evans 	}
923c65a2111SKyle Evans 	zfs_close(zfs);
924c65a2111SKyle Evans 	return (BE_ERR_SUCCESS);
925c65a2111SKyle Evans }
926c65a2111SKyle Evans 
927c65a2111SKyle Evans static int
928c65a2111SKyle Evans be_create_child_cloned(libbe_handle_t *lbh, const char *active)
929c65a2111SKyle Evans {
9303d1a1f2cSKyle Evans 	char buf[BE_MAXPATHLEN], tmp[BE_MAXPATHLEN];;
931c65a2111SKyle Evans 	zfs_handle_t *zfs;
932c65a2111SKyle Evans 	int err;
933c65a2111SKyle Evans 
934c65a2111SKyle Evans 	/* XXX TODO ? */
935c65a2111SKyle Evans 
936c65a2111SKyle Evans 	/*
937c65a2111SKyle Evans 	 * Establish if the existing path is a zfs dataset or just
938c65a2111SKyle Evans 	 * the subdirectory of one
939c65a2111SKyle Evans 	 */
9403d1a1f2cSKyle Evans 	strlcpy(tmp, "tmp/be_snap.XXXXX", sizeof(tmp));
9413d1a1f2cSKyle Evans 	if (mktemp(tmp) == NULL)
942c65a2111SKyle Evans 		return (set_error(lbh, BE_ERR_UNKNOWN));
943c65a2111SKyle Evans 
9443d1a1f2cSKyle Evans 	be_root_concat(lbh, tmp, buf);
9453d1a1f2cSKyle Evans 	printf("Here %s?\n", buf);
946c65a2111SKyle Evans 	if ((err = zfs_snapshot(lbh->lzh, buf, false, NULL)) != 0) {
947c65a2111SKyle Evans 		switch (err) {
948c65a2111SKyle Evans 		case EZFS_INVALIDNAME:
949c65a2111SKyle Evans 			return (set_error(lbh, BE_ERR_INVALIDNAME));
950c65a2111SKyle Evans 
951c65a2111SKyle Evans 		default:
952c65a2111SKyle Evans 			/*
953c65a2111SKyle Evans 			 * The other errors that zfs_ioc_snapshot might return
954c65a2111SKyle Evans 			 * shouldn't happen if we've set things up properly, so
955c65a2111SKyle Evans 			 * we'll gloss over them and call it UNKNOWN as it will
956c65a2111SKyle Evans 			 * require further triage.
957c65a2111SKyle Evans 			 */
958c65a2111SKyle Evans 			if (errno == ENOTSUP)
959c65a2111SKyle Evans 				return (set_error(lbh, BE_ERR_NOPOOL));
960c65a2111SKyle Evans 			return (set_error(lbh, BE_ERR_UNKNOWN));
961c65a2111SKyle Evans 		}
962c65a2111SKyle Evans 	}
963c65a2111SKyle Evans 
964c65a2111SKyle Evans 	/* Clone */
965c65a2111SKyle Evans 	if ((zfs = zfs_open(lbh->lzh, buf, ZFS_TYPE_SNAPSHOT)) == NULL)
966c65a2111SKyle Evans 		return (BE_ERR_ZFSOPEN);
967c65a2111SKyle Evans 
968c65a2111SKyle Evans 	if ((err = zfs_clone(zfs, active, NULL)) != 0)
969c65a2111SKyle Evans 		/* XXX TODO correct error */
970c65a2111SKyle Evans 		return (set_error(lbh, BE_ERR_UNKNOWN));
971c65a2111SKyle Evans 
972c65a2111SKyle Evans 	/* set props */
973c65a2111SKyle Evans 	zfs_close(zfs);
974c65a2111SKyle Evans 	return (BE_ERR_SUCCESS);
975c65a2111SKyle Evans }
97628f16a0fSKyle Evans 
97728f16a0fSKyle Evans int
97873c3d608SKyle Evans be_add_child(libbe_handle_t *lbh, const char *child_path, bool cp_if_exists)
97928f16a0fSKyle Evans {
98073c3d608SKyle Evans 	struct stat sb;
981c65a2111SKyle Evans 	char active[BE_MAXPATHLEN], buf[BE_MAXPATHLEN];
98228f16a0fSKyle Evans 	nvlist_t *props;
98373c3d608SKyle Evans 	const char *s;
98428f16a0fSKyle Evans 
98528f16a0fSKyle Evans 	/* Require absolute paths */
986bfe0869cSKyle Evans 	if (*child_path != '/')
9876d4b1d24SKyle Evans 		return (set_error(lbh, BE_ERR_BADPATH));
98828f16a0fSKyle Evans 
9896d4b1d24SKyle Evans 	strlcpy(active, be_active_path(lbh), BE_MAXPATHLEN);
99028f16a0fSKyle Evans 	strcpy(buf, active);
99128f16a0fSKyle Evans 
99228f16a0fSKyle Evans 	/* Create non-mountable parent dataset(s) */
99373c3d608SKyle Evans 	s = child_path;
99428f16a0fSKyle Evans 	for (char *p; (p = strchr(s+1, '/')) != NULL; s = p) {
99528f16a0fSKyle Evans 		size_t len = p - s;
99628f16a0fSKyle Evans 		strncat(buf, s, len);
99728f16a0fSKyle Evans 
99828f16a0fSKyle Evans 		nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP);
99928f16a0fSKyle Evans 		nvlist_add_string(props, "canmount", "off");
100028f16a0fSKyle Evans 		nvlist_add_string(props, "mountpoint", "none");
100128f16a0fSKyle Evans 		zfs_create(lbh->lzh, buf, ZFS_TYPE_DATASET, props);
100228f16a0fSKyle Evans 		nvlist_free(props);
100328f16a0fSKyle Evans 	}
100428f16a0fSKyle Evans 
100528f16a0fSKyle Evans 	/* Path does not exist as a descendent of / yet */
10066d4b1d24SKyle Evans 	if (strlcat(active, child_path, BE_MAXPATHLEN) >= BE_MAXPATHLEN)
10076d4b1d24SKyle Evans 		return (set_error(lbh, BE_ERR_PATHLEN));
100828f16a0fSKyle Evans 
100928f16a0fSKyle Evans 	if (stat(child_path, &sb) != 0) {
101028f16a0fSKyle Evans 		/* Verify that error is ENOENT */
10116d4b1d24SKyle Evans 		if (errno != ENOENT)
10126d4b1d24SKyle Evans 			return (set_error(lbh, BE_ERR_UNKNOWN));
1013c65a2111SKyle Evans 		return (be_create_child_noent(lbh, active, child_path));
1014c65a2111SKyle Evans 	} else if (cp_if_exists)
101528f16a0fSKyle Evans 		/* Path is already a descendent of / and should be copied */
1016c65a2111SKyle Evans 		return (be_create_child_cloned(lbh, active));
10176d4b1d24SKyle Evans 	return (set_error(lbh, BE_ERR_EXISTS));
101828f16a0fSKyle Evans }
10193d1a1f2cSKyle Evans #endif	/* SOON */
102028f16a0fSKyle Evans 
1021d06f7103SKyle Evans static int
1022d06f7103SKyle Evans be_set_nextboot(libbe_handle_t *lbh, nvlist_t *config, uint64_t pool_guid,
1023d06f7103SKyle Evans     const char *zfsdev)
1024d06f7103SKyle Evans {
1025d06f7103SKyle Evans 	nvlist_t **child;
1026d06f7103SKyle Evans 	uint64_t vdev_guid;
1027d06f7103SKyle Evans 	int c, children;
1028d06f7103SKyle Evans 
1029d06f7103SKyle Evans 	if (nvlist_lookup_nvlist_array(config, ZPOOL_CONFIG_CHILDREN, &child,
1030d06f7103SKyle Evans 	    &children) == 0) {
1031d06f7103SKyle Evans 		for (c = 0; c < children; ++c)
1032d06f7103SKyle Evans 			if (be_set_nextboot(lbh, child[c], pool_guid, zfsdev) != 0)
1033d06f7103SKyle Evans 				return (1);
1034d06f7103SKyle Evans 		return (0);
1035d06f7103SKyle Evans 	}
1036d06f7103SKyle Evans 
1037d06f7103SKyle Evans 	if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_GUID,
1038d06f7103SKyle Evans 	    &vdev_guid) != 0) {
1039d06f7103SKyle Evans 		return (1);
1040d06f7103SKyle Evans 	}
1041d06f7103SKyle Evans 
1042d06f7103SKyle Evans 	if (zpool_nextboot(lbh->lzh, pool_guid, vdev_guid, zfsdev) != 0) {
1043d06f7103SKyle Evans 		perror("ZFS_IOC_NEXTBOOT failed");
1044d06f7103SKyle Evans 		return (1);
1045d06f7103SKyle Evans 	}
1046d06f7103SKyle Evans 
1047d06f7103SKyle Evans 	return (0);
1048d06f7103SKyle Evans }
1049d06f7103SKyle Evans 
10508d4ce358SKyle Evans /*
10518d4ce358SKyle Evans  * Deactivate old BE dataset; currently just sets canmount=noauto
10528d4ce358SKyle Evans  */
10538d4ce358SKyle Evans static int
10548d4ce358SKyle Evans be_deactivate(libbe_handle_t *lbh, const char *ds)
10558d4ce358SKyle Evans {
10568d4ce358SKyle Evans 	zfs_handle_t *zfs;
10578d4ce358SKyle Evans 
10588d4ce358SKyle Evans 	if ((zfs = zfs_open(lbh->lzh, ds, ZFS_TYPE_DATASET)) == NULL)
10598d4ce358SKyle Evans 		return (1);
10608d4ce358SKyle Evans 	if (zfs_prop_set(zfs, "canmount", "noauto") != 0)
10618d4ce358SKyle Evans 		return (1);
10628d4ce358SKyle Evans 	zfs_close(zfs);
10638d4ce358SKyle Evans 	return (0);
10648d4ce358SKyle Evans }
106528f16a0fSKyle Evans 
106628f16a0fSKyle Evans int
106773c3d608SKyle Evans be_activate(libbe_handle_t *lbh, const char *bootenv, bool temporary)
106828f16a0fSKyle Evans {
106928f16a0fSKyle Evans 	char be_path[BE_MAXPATHLEN];
107028f16a0fSKyle Evans 	char buf[BE_MAXPATHLEN];
10714635676dSKyle Evans 	nvlist_t *config, *dsprops, *vdevs;
10724635676dSKyle Evans 	char *origin;
10730cadc427SKyle Evans 	uint64_t pool_guid;
10740cadc427SKyle Evans 	zfs_handle_t *zhp;
107528f16a0fSKyle Evans 	int err;
107628f16a0fSKyle Evans 
107728f16a0fSKyle Evans 	be_root_concat(lbh, bootenv, be_path);
107828f16a0fSKyle Evans 
107928f16a0fSKyle Evans 	/* Note: be_exists fails if mountpoint is not / */
1080162ec569SKyle Evans 	if ((err = be_exists(lbh, be_path)) != 0)
1081162ec569SKyle Evans 		return (set_error(lbh, err));
108228f16a0fSKyle Evans 
108328f16a0fSKyle Evans 	if (temporary) {
1084d06f7103SKyle Evans 		config = zpool_get_config(lbh->active_phandle, NULL);
1085c65a2111SKyle Evans 		if (config == NULL)
1086c65a2111SKyle Evans 			/* config should be fetchable... */
1087c65a2111SKyle Evans 			return (set_error(lbh, BE_ERR_UNKNOWN));
108828f16a0fSKyle Evans 
1089d06f7103SKyle Evans 		if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID,
1090d06f7103SKyle Evans 		    &pool_guid) != 0)
1091c65a2111SKyle Evans 			/* Similarly, it shouldn't be possible */
1092c65a2111SKyle Evans 			return (set_error(lbh, BE_ERR_UNKNOWN));
1093d06f7103SKyle Evans 
109428f16a0fSKyle Evans 		/* Expected format according to zfsbootcfg(8) man */
1095a8e44f4dSKyle Evans 		snprintf(buf, sizeof(buf), "zfs:%s:", be_path);
109628f16a0fSKyle Evans 
1097c65a2111SKyle Evans 		/* We have no config tree */
1098c65a2111SKyle Evans 		if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
1099c65a2111SKyle Evans 		    &vdevs) != 0)
1100c65a2111SKyle Evans 			return (set_error(lbh, BE_ERR_NOPOOL));
110128f16a0fSKyle Evans 
1102d06f7103SKyle Evans 		return (be_set_nextboot(lbh, vdevs, pool_guid, buf));
110328f16a0fSKyle Evans 	} else {
11048d4ce358SKyle Evans 		if (be_deactivate(lbh, lbh->bootfs) != 0)
11058d4ce358SKyle Evans 			return (-1);
11068d4ce358SKyle Evans 
110728f16a0fSKyle Evans 		/* Obtain bootenv zpool */
1108c3a34c08SKyle Evans 		err = zpool_set_prop(lbh->active_phandle, "bootfs", be_path);
11090cadc427SKyle Evans 		if (err)
11100cadc427SKyle Evans 			return (-1);
111128f16a0fSKyle Evans 
11120cadc427SKyle Evans 		zhp = zfs_open(lbh->lzh, be_path, ZFS_TYPE_FILESYSTEM);
11130cadc427SKyle Evans 		if (zhp == NULL)
11140cadc427SKyle Evans 			return (-1);
111528f16a0fSKyle Evans 
11164635676dSKyle Evans 		if (be_prop_list_alloc(&dsprops) != 0)
11174635676dSKyle Evans 			return (-1);
11184635676dSKyle Evans 
11194635676dSKyle Evans 		if (be_get_dataset_props(lbh, be_path, dsprops) != 0) {
11204635676dSKyle Evans 			nvlist_free(dsprops);
11214635676dSKyle Evans 			return (-1);
11224635676dSKyle Evans 		}
11234635676dSKyle Evans 
11244635676dSKyle Evans 		if (nvlist_lookup_string(dsprops, "origin", &origin) == 0)
11250cadc427SKyle Evans 			err = zfs_promote(zhp);
11264635676dSKyle Evans 		nvlist_free(dsprops);
11274635676dSKyle Evans 
11280cadc427SKyle Evans 		zfs_close(zhp);
11290cadc427SKyle Evans 
11300cadc427SKyle Evans 		if (err)
113128f16a0fSKyle Evans 			return (-1);
113228f16a0fSKyle Evans 	}
11330cadc427SKyle Evans 
11340cadc427SKyle Evans 	return (BE_ERR_SUCCESS);
113528f16a0fSKyle Evans }
1136