xref: /freebsd/lib/libbe/be.c (revision 3f4e6c966c306cb9f983f8d23b658728156ee30e)
1 /*
2  * be.c
3  *
4  * Copyright (c) 2017 Kyle J. Kneitinger <kyle@kneit.in>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <sys/stat.h>
30 #include <sys/types.h>
31 
32 #include <ctype.h>
33 #include <kenv.h>
34 #include <libgen.h>
35 #include <libzfs_core.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <time.h>
39 #include <unistd.h>
40 
41 #include "be.h"
42 #include "be_impl.h"
43 
44 /*
45  * Iterator function for locating the rootfs amongst the children of the
46  * zfs_be_root set by loader(8).  data is expected to be a libbe_handle_t *.
47  */
48 static int
49 be_locate_rootfs(zfs_handle_t *chkds, void *data)
50 {
51 	libbe_handle_t *lbh;
52 	char *mntpoint;
53 
54 	lbh = (libbe_handle_t *)data;
55 	if (lbh == NULL)
56 		return (1);
57 
58 	if (zfs_is_mounted(chkds, &mntpoint) && strcmp(mntpoint, "/") == 0) {
59 		strncpy(lbh->rootfs, zfs_get_name(chkds), BE_MAXPATHLEN);
60 		return (1);
61 	}
62 
63 	return (0);
64 }
65 
66 /*
67  * Initializes the libbe context to operate in the root boot environment
68  * dataset, for example, zroot/ROOT.
69  */
70 libbe_handle_t *
71 libbe_init(void)
72 {
73 	struct stat sb;
74 	dev_t root_dev, boot_dev;
75 	libbe_handle_t *lbh;
76 	zfs_handle_t *rootds;
77 	char *poolname, *pos;
78 	int pnamelen;
79 
80 	lbh = NULL;
81 	poolname = pos = NULL;
82 	pnamelen = 0;
83 	rootds = NULL;
84 
85 	/* Verify that /boot and / are mounted on the same filesystem */
86 	/* TODO: use errno here?? */
87 	if (stat("/", &sb) != 0)
88 		goto err;
89 
90 	root_dev = sb.st_dev;
91 
92 	if (stat("/boot", &sb) != 0)
93 		goto err;
94 
95 	boot_dev = sb.st_dev;
96 
97 	if (root_dev != boot_dev) {
98 		fprintf(stderr, "/ and /boot not on same device, quitting\n");
99 		goto err;
100 	}
101 
102 	if ((lbh = calloc(1, sizeof(libbe_handle_t))) == NULL)
103 		goto err;
104 
105 	if ((lbh->lzh = libzfs_init()) == NULL)
106 		goto err;
107 
108 	/* Obtain path to boot environment root */
109 	if ((kenv(KENV_GET, "zfs_be_root", lbh->root, BE_MAXPATHLEN)) == -1)
110 		goto err;
111 
112 	/* Remove leading 'zfs:' if present, otherwise use value as-is */
113 	if (strcmp(lbh->root, "zfs:") == 0)
114 		strncpy(lbh->root, strchr(lbh->root, ':') + sizeof(char),
115 		    BE_MAXPATHLEN);
116 
117 	if ((pos = strchr(lbh->root, '/')) == NULL)
118 		goto err;
119 
120 	pnamelen = pos - lbh->root;
121 	poolname = malloc(pnamelen + 1);
122 	if (poolname == NULL)
123 		goto err;
124 
125 	strncpy(poolname, lbh->root, pnamelen);
126 	poolname[pnamelen] = '\0';
127 	if ((lbh->active_phandle = zpool_open(lbh->lzh, poolname)) == NULL)
128 		goto err;
129 
130 	if (zpool_get_prop(lbh->active_phandle, ZPOOL_PROP_BOOTFS, lbh->bootfs,
131 	    BE_MAXPATHLEN, NULL, true) != 0)
132 		goto err;
133 
134 	/* Obtain path to boot environment rootfs (currently booted) */
135 	/* XXX Get dataset mounted at / by kenv/GUID from mountroot? */
136 	if ((rootds = zfs_open(lbh->lzh, lbh->root, ZFS_TYPE_DATASET)) == NULL)
137 		goto err;
138 
139 	zfs_iter_filesystems(rootds, be_locate_rootfs, lbh);
140 	zfs_close(rootds);
141 	rootds = NULL;
142 	if (*lbh->rootfs == '\0')
143 		goto err;
144 
145 	return (lbh);
146 err:
147 	if (lbh != NULL) {
148 		if (lbh->active_phandle != NULL)
149 			zpool_close(lbh->active_phandle);
150 		if (lbh->lzh != NULL)
151 			libzfs_fini(lbh->lzh);
152 		free(lbh);
153 	}
154 	if (rootds != NULL)
155 		zfs_close(rootds);
156 	free(poolname);
157 	return (NULL);
158 }
159 
160 
161 /*
162  * Free memory allocated by libbe_init()
163  */
164 void
165 libbe_close(libbe_handle_t *lbh)
166 {
167 
168 	if (lbh->active_phandle != NULL)
169 		zpool_close(lbh->active_phandle);
170 	libzfs_fini(lbh->lzh);
171 	free(lbh);
172 }
173 
174 /*
175  * Proxy through to libzfs for the moment.
176  */
177 void
178 be_nicenum(uint64_t num, char *buf, size_t buflen)
179 {
180 
181 	zfs_nicenum(num, buf, buflen);
182 }
183 
184 /*
185  * Destroy the boot environment or snapshot specified by the name
186  * parameter. Options are or'd together with the possible values:
187  * BE_DESTROY_FORCE : forces operation on mounted datasets
188  * TODO: Test destroying a non active but mounted be
189  */
190 int
191 be_destroy(libbe_handle_t *lbh, char *name, int options)
192 {
193 	zfs_handle_t *fs;
194 	char path[BE_MAXPATHLEN];
195 	char *p;
196 	int err, force, mounted;
197 
198 	p = path;
199 	force = options & BE_DESTROY_FORCE;
200 	err = BE_ERR_SUCCESS;
201 
202 	be_root_concat(lbh, name, path);
203 
204 	if (strchr(name, '@') == NULL) {
205 		if (!zfs_dataset_exists(lbh->lzh, path, ZFS_TYPE_FILESYSTEM))
206 			return (set_error(lbh, BE_ERR_NOENT));
207 
208 		if (strcmp(path, lbh->rootfs) == 0)
209 			return (set_error(lbh, BE_ERR_DESTROYACT));
210 
211 		fs = zfs_open(lbh->lzh, p, ZFS_TYPE_FILESYSTEM);
212 	} else {
213 
214 		if (!zfs_dataset_exists(lbh->lzh, path, ZFS_TYPE_SNAPSHOT))
215 			return (set_error(lbh, BE_ERR_NOENT));
216 
217 		fs = zfs_open(lbh->lzh, p, ZFS_TYPE_SNAPSHOT);
218 	}
219 
220 	if (fs == NULL)
221 		return (set_error(lbh, BE_ERR_ZFSOPEN));
222 
223 	/* Check if mounted, unmount if force is specified */
224 	if ((mounted = zfs_is_mounted(fs, NULL)) != 0) {
225 		if (force)
226 			zfs_unmount(fs, NULL, 0);
227 		else
228 			return (set_error(lbh, BE_ERR_DESTROYMNT));
229 	}
230 
231 
232 	/* XXX TODO: convert this to use zfs_iter_children first for deep BEs */
233 	/* XXX Note: errno 16 (device busy) occurs when chilren are present */
234 	if ((err = zfs_destroy(fs, false)) != 0)
235 		fprintf(stderr, "delete failed errno: %d\n", errno);
236 
237 	return (err);
238 }
239 
240 
241 int
242 be_snapshot(libbe_handle_t *lbh, const char *source, const char *snap_name,
243     bool recursive, char *result)
244 {
245 	char buf[BE_MAXPATHLEN];
246 	time_t rawtime;
247 	int len, err;
248 
249 	be_root_concat(lbh, source, buf);
250 
251 	if (!be_exists(lbh, buf))
252 		return (BE_ERR_NOENT);
253 
254 	if (snap_name != NULL) {
255 		strcat(buf, "@");
256 		strcat(buf, snap_name);
257 		if (result != NULL)
258 			snprintf(result, BE_MAXPATHLEN, "%s@%s", source,
259 			    snap_name);
260 	} else {
261 		time(&rawtime);
262 		len = strlen(buf);
263 		strftime(buf + len, BE_MAXPATHLEN - len,
264 		    "@%F-%T", localtime(&rawtime));
265 		if (result != NULL)
266 			strcpy(result, strrchr(buf, '/') + 1);
267 	}
268 
269 	if ((err = zfs_snapshot(lbh->lzh, buf, recursive, NULL)) != 0) {
270 		switch (err) {
271 		case EZFS_INVALIDNAME:
272 			return (set_error(lbh, BE_ERR_INVALIDNAME));
273 
274 		default:
275 			/* XXX TODO: elaborate return codes */
276 			return (set_error(lbh, BE_ERR_UNKNOWN));
277 		}
278 	}
279 
280 	return (BE_ERR_SUCCESS);
281 }
282 
283 
284 /*
285  * Create the boot environment specified by the name parameter
286  */
287 int
288 be_create(libbe_handle_t *lbh, char *name)
289 {
290 	int err;
291 
292 	err = be_create_from_existing(lbh, name, be_active_path(lbh));
293 
294 	return (set_error(lbh, err));
295 }
296 
297 
298 static int
299 be_deep_clone_prop(int prop, void *cb)
300 {
301 	int err;
302         struct libbe_dccb *dccb;
303 	zprop_source_t src;
304 	char pval[BE_MAXPATHLEN];
305 	char source[BE_MAXPATHLEN];
306 
307 	dccb = cb;
308 	/* Skip some properties we don't want to touch */
309 	switch (prop) {
310 		case ZFS_PROP_CANMOUNT:
311 			return (ZPROP_CONT);
312 			break;
313 	}
314 
315 	/* Don't copy readonly properties */
316 	if (zfs_prop_readonly(prop))
317 		return (ZPROP_CONT);
318 
319 	if ((err = zfs_prop_get(dccb->zhp, prop, (char *)&pval,
320 	    sizeof(pval), &src, (char *)&source, sizeof(source), false)))
321 		/* Just continue if we fail to read a property */
322 		return (ZPROP_CONT);
323 
324 	/* Only copy locally defined properties */
325 	if (src != ZPROP_SRC_LOCAL)
326 		return (ZPROP_CONT);
327 
328 	nvlist_add_string(dccb->props, zfs_prop_to_name(prop), (char *)pval);
329 
330 	return (ZPROP_CONT);
331 }
332 
333 static int
334 be_deep_clone(zfs_handle_t *ds, void *data)
335 {
336 	int err;
337 	char be_path[BE_MAXPATHLEN];
338 	char snap_path[BE_MAXPATHLEN];
339 	const char *dspath;
340 	char *dsname;
341 	zfs_handle_t *snap_hdl;
342 	nvlist_t *props;
343 	struct libbe_deep_clone *isdc, sdc;
344 	struct libbe_dccb dccb;
345 
346 	isdc = (struct libbe_deep_clone *)data;
347 	dspath = zfs_get_name(ds);
348 	if ((dsname = strrchr(dspath, '/')) == NULL)
349 		return (BE_ERR_UNKNOWN);
350 	dsname++;
351 
352 	if (isdc->bename == NULL)
353 		snprintf(be_path, sizeof(be_path), "%s/%s", isdc->be_root, dsname);
354 	else
355 		snprintf(be_path, sizeof(be_path), "%s/%s", isdc->be_root, isdc->bename);
356 
357 	snprintf(snap_path, sizeof(snap_path), "%s@%s", dspath, isdc->snapname);
358 
359 	if (zfs_dataset_exists(isdc->lbh->lzh, be_path, ZFS_TYPE_DATASET))
360 		return (set_error(isdc->lbh, BE_ERR_EXISTS));
361 
362 	if ((snap_hdl =
363 	    zfs_open(isdc->lbh->lzh, snap_path, ZFS_TYPE_SNAPSHOT)) == NULL)
364 		return (set_error(isdc->lbh, BE_ERR_ZFSOPEN));
365 
366 	nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP);
367 	nvlist_add_string(props, "canmount", "noauto");
368 
369 	dccb.zhp = ds;
370 	dccb.props = props;
371 	if (zprop_iter(be_deep_clone_prop, &dccb, B_FALSE, B_FALSE,
372 	    ZFS_TYPE_FILESYSTEM) == ZPROP_INVAL)
373 		return (-1);
374 
375 	if ((err = zfs_clone(snap_hdl, be_path, props)) != 0) {
376 		switch (err) {
377 		case EZFS_SUCCESS:
378 			err = BE_ERR_SUCCESS;
379 			break;
380 		default:
381 			err = BE_ERR_ZFSCLONE;
382 			break;
383 		}
384 	}
385 
386 	nvlist_free(props);
387 	zfs_close(snap_hdl);
388 
389 	sdc.lbh = isdc->lbh;
390 	sdc.bename = NULL;
391 	sdc.snapname = isdc->snapname;
392 	sdc.be_root = (char *)&be_path;
393 
394 	err = zfs_iter_filesystems(ds, be_deep_clone, &sdc);
395 
396 	return (err);
397 }
398 
399 /*
400  * Create the boot environment from pre-existing snapshot
401  */
402 int
403 be_create_from_existing_snap(libbe_handle_t *lbh, const char *name,
404     const char *snap)
405 {
406 	int err;
407 	char be_path[BE_MAXPATHLEN];
408 	char snap_path[BE_MAXPATHLEN];
409 	const char *bename;
410 	char *parentname, *snapname;
411 	zfs_handle_t *parent_hdl;
412 	struct libbe_deep_clone sdc;
413 
414 	if ((err = be_validate_name(lbh, name)) != 0)
415 		return (set_error(lbh, err));
416 	if ((err = be_root_concat(lbh, snap, snap_path)) != 0)
417 		return (set_error(lbh, err));
418 	if ((err = be_validate_snap(lbh, snap_path)) != 0)
419 		return (set_error(lbh, err));
420 
421 	if ((err = be_root_concat(lbh, name, be_path)) != 0)
422 		return (set_error(lbh, err));
423 
424 	if ((bename = strrchr(name, '/')) == NULL)
425 		bename = name;
426 	else
427 		bename++;
428 
429 	if ((parentname = strdup(snap_path)) == NULL) {
430 		err = BE_ERR_UNKNOWN;
431 		return (set_error(lbh, err));
432 	}
433 	snapname = strchr(parentname, '@');
434 	if (snapname == NULL) {
435 		err = BE_ERR_UNKNOWN;
436 		return (set_error(lbh, err));
437 	}
438 	*snapname = '\0';
439 	snapname++;
440 
441 	sdc.lbh = lbh;
442 	sdc.bename = bename;
443 	sdc.snapname = snapname;
444 	sdc.be_root = lbh->root;
445 
446 	parent_hdl = zfs_open(lbh->lzh, parentname, ZFS_TYPE_DATASET);
447 	err = be_deep_clone(parent_hdl, &sdc);
448 
449 	return (set_error(lbh, err));
450 }
451 
452 
453 /*
454  * Create a boot environment from an existing boot environment
455  */
456 int
457 be_create_from_existing(libbe_handle_t *lbh, const char *name, const char *old)
458 {
459 	int err;
460 	char buf[BE_MAXPATHLEN];
461 
462 	if ((err = be_snapshot(lbh, old, NULL, true, (char *)&buf)))
463 		return (set_error(lbh, err));
464 
465 	err = be_create_from_existing_snap(lbh, name, (char *)buf);
466 
467 	return (set_error(lbh, err));
468 }
469 
470 
471 /*
472  * Verifies that a snapshot has a valid name, exists, and has a mountpoint of
473  * '/'. Returns BE_ERR_SUCCESS (0), upon success, or the relevant BE_ERR_* upon
474  * failure. Does not set the internal library error state.
475  */
476 int
477 be_validate_snap(libbe_handle_t *lbh, const char *snap_name)
478 {
479 	zfs_handle_t *zfs_hdl;
480 	char buf[BE_MAXPATHLEN];
481 	char *delim_pos;
482 	int err = BE_ERR_SUCCESS;
483 
484 	if (strlen(snap_name) >= BE_MAXPATHLEN)
485 		return (BE_ERR_PATHLEN);
486 
487 	if (!zfs_dataset_exists(lbh->lzh, snap_name,
488 	    ZFS_TYPE_SNAPSHOT))
489 		return (BE_ERR_NOENT);
490 
491 	strncpy(buf, snap_name, BE_MAXPATHLEN);
492 
493 	/* Find the base filesystem of the snapshot */
494 	if ((delim_pos = strchr(buf, '@')) == NULL)
495 		return (BE_ERR_INVALIDNAME);
496 	*delim_pos = '\0';
497 
498 	if ((zfs_hdl =
499 	    zfs_open(lbh->lzh, buf, ZFS_TYPE_DATASET)) == NULL)
500 		return (BE_ERR_NOORIGIN);
501 
502 	if ((err = zfs_prop_get(zfs_hdl, ZFS_PROP_MOUNTPOINT, buf, BE_MAXPATHLEN,
503 	    NULL, NULL, 0, 1)) != 0)
504 		err = BE_ERR_INVORIGIN;
505 
506 	if ((err != 0) && (strncmp(buf, "/", BE_MAXPATHLEN) != 0))
507 		err = BE_ERR_INVORIGIN;
508 
509 	zfs_close(zfs_hdl);
510 
511 	return (err);
512 }
513 
514 
515 /*
516  * Idempotently appends the name argument to the root boot environment path
517  * and copies the resulting string into the result buffer (which is assumed
518  * to be at least BE_MAXPATHLEN characters long. Returns BE_ERR_SUCCESS upon
519  * success, BE_ERR_PATHLEN if the resulting path is longer than BE_MAXPATHLEN,
520  * or BE_ERR_INVALIDNAME if the name is a path that does not begin with
521  * zfs_be_root. Does not set internal library error state.
522  */
523 int
524 be_root_concat(libbe_handle_t *lbh, const char *name, char *result)
525 {
526 	size_t name_len, root_len;
527 
528 	name_len = strlen(name);
529 	root_len = strlen(lbh->root);
530 
531 	/* Act idempotently; return be name if it is already a full path */
532 	if (strrchr(name, '/') != NULL) {
533 		if (strstr(name, lbh->root) != name)
534 			return (BE_ERR_INVALIDNAME);
535 
536 		if (name_len >= BE_MAXPATHLEN)
537 			return (BE_ERR_PATHLEN);
538 
539 		strncpy(result, name, BE_MAXPATHLEN);
540 		return (BE_ERR_SUCCESS);
541 	} else if (name_len + root_len + 1 < BE_MAXPATHLEN) {
542 		snprintf(result, BE_MAXPATHLEN, "%s/%s", lbh->root,
543 		    name);
544 		return (BE_ERR_SUCCESS);
545 	}
546 
547 	return (BE_ERR_PATHLEN);
548 }
549 
550 
551 /*
552  * Verifies the validity of a boot environment name (A-Za-z0-9-_.). Returns
553  * BE_ERR_SUCCESS (0) if name is valid, otherwise returns BE_ERR_INVALIDNAME.
554  * Does not set internal library error state.
555  */
556 int
557 be_validate_name(libbe_handle_t *lbh __unused, const char *name)
558 {
559 	for (int i = 0; *name; i++) {
560 		char c = *(name++);
561 		if (isalnum(c) || (c == '-') || (c == '_') || (c == '.'))
562 			continue;
563 		return (BE_ERR_INVALIDNAME);
564 	}
565 
566 	return (BE_ERR_SUCCESS);
567 }
568 
569 
570 /*
571  * usage
572  */
573 int
574 be_rename(libbe_handle_t *lbh, char *old, char *new)
575 {
576 	char full_old[BE_MAXPATHLEN];
577 	char full_new[BE_MAXPATHLEN];
578 	zfs_handle_t *zfs_hdl;
579 	int err;
580 
581 	if ((err = be_root_concat(lbh, old, full_old)) != 0)
582 		return (set_error(lbh, err));
583 	if ((err = be_root_concat(lbh, new, full_new)) != 0)
584 		return (set_error(lbh, err));
585 
586 	if (be_validate_name(lbh, new) != 0)
587 		return (BE_ERR_UNKNOWN);
588 		/* XXX TODO set and return correct error */
589 
590 	/* Check if old is active BE */
591 	if (strcmp(full_new, be_active_path(lbh)) == 0)
592 		return (BE_ERR_UNKNOWN);
593 		/* XXX TODO set and return correct error */
594 
595 	if (!zfs_dataset_exists(lbh->lzh, full_old, ZFS_TYPE_DATASET))
596 		return (BE_ERR_UNKNOWN);
597 		/* XXX TODO set and return correct error */
598 
599 	if (zfs_dataset_exists(lbh->lzh, full_new, ZFS_TYPE_DATASET))
600 		return (BE_ERR_UNKNOWN);
601 		/* XXX TODO set and return correct error */
602 
603 	/* XXX TODO
604 	 * - What about mounted BEs?
605 	 * - if mounted error out unless a force flag is set?
606 	 */
607 	if ((zfs_hdl = zfs_open(lbh->lzh, full_old,
608 	    ZFS_TYPE_FILESYSTEM)) == NULL)
609 		return (BE_ERR_UNKNOWN);
610 		/* XXX TODO set and return correct error */
611 
612 
613 	/* recurse, nounmount, forceunmount */
614 	struct renameflags flags = { 0, 0, 0 };
615 
616 	/* XXX TODO: error log on this call */
617 	err = zfs_rename(zfs_hdl, NULL, full_new, flags);
618 
619 	zfs_close(zfs_hdl);
620 
621 	return (set_error(lbh, err));
622 }
623 
624 
625 int
626 be_export(libbe_handle_t *lbh, char *bootenv, int fd)
627 {
628 	char snap_name[BE_MAXPATHLEN];
629 	char buf[BE_MAXPATHLEN];
630 	zfs_handle_t *zfs;
631 	int err;
632 
633 	if ((err = be_snapshot(lbh, bootenv, NULL, true, snap_name)) != 0)
634 		/* XXX TODO error handle */
635 		return (-1);
636 
637 	be_root_concat(lbh, snap_name, buf);
638 
639 	if ((zfs = zfs_open(lbh->lzh, buf, ZFS_TYPE_DATASET)) == NULL)
640 		return (BE_ERR_ZFSOPEN);
641 
642 	err = zfs_send_one(zfs, NULL, fd, 0);
643 	return (err);
644 }
645 
646 
647 int
648 be_import(libbe_handle_t *lbh, char *bootenv, int fd)
649 {
650 	char buf[BE_MAXPATHLEN];
651 	time_t rawtime;
652 	nvlist_t *props;
653 	zfs_handle_t *zfs;
654 	int err, len;
655 
656 	/*
657 	 * XXX TODO: this is a very likely name for someone to already have
658 	 * used... we should avoid it.
659 	 */
660 	if ((err = be_root_concat(lbh, "libbe_import_temp", buf)) != 0)
661 		/* XXX TODO error handle */
662 		return (-1);
663 
664 	time(&rawtime);
665 	len = strlen(buf);
666 	strftime(buf + len, BE_MAXPATHLEN - len,
667 	    "@%F-%T", localtime(&rawtime));
668 
669 	/* lzc_receive(SNAPNAME, PROPS, ORIGIN, FORCE, fd)) { */
670 	if ((err = lzc_receive(buf, NULL, NULL, false, fd)) != 0) {
671 		/* TODO: go through libzfs_core's recv_impl and find returned
672 		 * errors and set appropriate BE_ERR
673 		 * edit: errors are not in libzfs_core, my assumption is
674 		 *  that they use libzfs errors
675 		 * note: 17 is err for dataset already existing
676 		 */
677 		return (err);
678 	}
679 
680 	if ((zfs = zfs_open(lbh->lzh, buf, ZFS_TYPE_SNAPSHOT)) == NULL)
681 		/* XXX TODO correct error */
682 		return (-1);
683 
684 	nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP);
685 	nvlist_add_string(props, "canmount", "noauto");
686 	nvlist_add_string(props, "mountpoint", "/");
687 
688 	be_root_concat(lbh, bootenv, buf);
689 
690 	err = zfs_clone(zfs, buf, props);
691 	zfs_close(zfs);
692 
693 	nvlist_free(props);
694 
695 	/* XXX TODO: recursively delete be_import_temp dataset */
696 	return (err);
697 }
698 
699 
700 int
701 be_add_child(libbe_handle_t *lbh, char *child_path, bool cp_if_exists)
702 {
703 	char active[BE_MAXPATHLEN];
704 	char buf[BE_MAXPATHLEN];
705 	nvlist_t *props;
706 	zfs_handle_t *zfs;
707 	struct stat sb;
708 	int err;
709 
710 	/* Require absolute paths */
711 	if (*child_path != '/')
712 		/* XXX TODO: create appropriate error */
713 		return (-1);
714 
715 	strncpy(active, be_active_path(lbh), BE_MAXPATHLEN);
716 	strcpy(buf, active);
717 
718 	/* Create non-mountable parent dataset(s) */
719 	char *s = child_path;
720 	for (char *p; (p = strchr(s+1, '/')) != NULL; s = p) {
721 		size_t len = p - s;
722 		strncat(buf, s, len);
723 
724 		nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP);
725 		nvlist_add_string(props, "canmount", "off");
726 		nvlist_add_string(props, "mountpoint", "none");
727 		zfs_create(lbh->lzh, buf, ZFS_TYPE_DATASET, props);
728 		nvlist_free(props);
729 	}
730 
731 
732 	/* Path does not exist as a descendent of / yet */
733 	int pos = strlen(active);
734 
735 	/* XXX TODO: Verify that resulting str is less than BE_MAXPATHLEN */
736 	strncpy(&active[pos], child_path, BE_MAXPATHLEN-pos);
737 
738 	if (stat(child_path, &sb) != 0) {
739 		/* Verify that error is ENOENT */
740 		if (errno != 2)
741 			/* XXX TODO: create appropriate error */
742 			return (-1);
743 
744 		nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP);
745 		nvlist_add_string(props, "canmount", "noauto");
746 		nvlist_add_string(props, "mountpoint", child_path);
747 
748 		/* Create */
749 		if ((err =
750 		    zfs_create(lbh->lzh, active, ZFS_TYPE_DATASET, props)) != 0)
751 			/* XXX TODO handle error */
752 			return (-1);
753 		nvlist_free(props);
754 
755 		if ((zfs =
756 		    zfs_open(lbh->lzh, active, ZFS_TYPE_DATASET)) == NULL)
757 			/* XXX TODO handle error */
758 			return (-1);
759 
760 		/* Set props */
761 		if ((err = zfs_prop_set(zfs, "canmount", "noauto")) != 0)
762 			/* TODO handle error */
763 			return (-1);
764 	} else if (cp_if_exists) {
765 		/* Path is already a descendent of / and should be copied */
766 
767 		/* XXX TODO ? */
768 
769 		/*
770 		 * Establish if the existing path is a zfs dataset or just
771 		 * the subdirectory of one
772 		 */
773 
774 		/* XXX TODO: use mktemp */
775 		long int snap_name = random();
776 
777 		snprintf(buf, BE_MAXPATHLEN, "%s@%ld", child_path, snap_name);
778 
779 		if ((err = zfs_snapshot(lbh->lzh, buf, false, NULL)) != 0)
780 			/* XXX TODO correct error */
781 			return (-1);
782 
783 		/* Clone */
784 		if ((zfs =
785 		    zfs_open(lbh->lzh, buf, ZFS_TYPE_SNAPSHOT)) == NULL)
786 			/* XXX TODO correct error */
787 			return (-1);
788 
789 		if ((err = zfs_clone(zfs, active, NULL)) != 0)
790 			/* XXX TODO correct error */
791 			return (-1);
792 
793 		/* set props */
794 	} else
795 		/* TODO: error code for exists, but not cp? */
796 		return (-1);
797 
798 	return (BE_ERR_SUCCESS);
799 }
800 
801 static int
802 be_set_nextboot(libbe_handle_t *lbh, nvlist_t *config, uint64_t pool_guid,
803     const char *zfsdev)
804 {
805 	nvlist_t **child;
806 	uint64_t vdev_guid;
807 	int c, children;
808 
809 	if (nvlist_lookup_nvlist_array(config, ZPOOL_CONFIG_CHILDREN, &child,
810 	    &children) == 0) {
811 		for (c = 0; c < children; ++c)
812 			if (be_set_nextboot(lbh, child[c], pool_guid, zfsdev) != 0)
813 				return (1);
814 		return (0);
815 	}
816 
817 	if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_GUID,
818 	    &vdev_guid) != 0) {
819 		return (1);
820 	}
821 
822 	if (zpool_nextboot(lbh->lzh, pool_guid, vdev_guid, zfsdev) != 0) {
823 		perror("ZFS_IOC_NEXTBOOT failed");
824 		return (1);
825 	}
826 
827 	return (0);
828 }
829 
830 
831 int
832 be_activate(libbe_handle_t *lbh, char *bootenv, bool temporary)
833 {
834 	char be_path[BE_MAXPATHLEN];
835 	char buf[BE_MAXPATHLEN];
836 	uint64_t pool_guid;
837 	nvlist_t *config, *vdevs;
838 	int err;
839 
840 	be_root_concat(lbh, bootenv, be_path);
841 
842 	/* Note: be_exists fails if mountpoint is not / */
843 	if (!be_exists(lbh, be_path))
844 		return (BE_ERR_NOENT);
845 
846 	if (temporary) {
847 		config = zpool_get_config(lbh->active_phandle, NULL);
848 		if (config == NULL) {
849 			printf("no config\n");
850 			return (1);
851 		}
852 
853 		if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID,
854 		    &pool_guid) != 0)
855 			return (1);
856 
857 		/* Expected format according to zfsbootcfg(8) man */
858 		strcpy(buf, "zfs:");
859 		strcat(buf, be_path);
860 		strcat(buf, ":");
861 
862 		if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, &vdevs) != 0)
863 			return (1);
864 
865 		return (be_set_nextboot(lbh, vdevs, pool_guid, buf));
866 	} else {
867 		/* Obtain bootenv zpool */
868 		err = zpool_set_prop(lbh->active_phandle, "bootfs", be_path);
869 
870 		switch (err) {
871 		case 0:
872 			return (BE_ERR_SUCCESS);
873 
874 		default:
875 			/* XXX TODO correct errors */
876 			return (-1);
877 		}
878 	}
879 }
880