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