svn commit: r346429 - in stable/11: . contrib/mdocml etc/mtree lib lib/libbe rescue/rescue sbin sbin/bectl sbin/bectl/tests share/mk tools/build/mk

Kyle Evans kevans at FreeBSD.org
Tue Sep 3 14:08:04 UTC 2019


Author: kevans
Date: Sat Apr 20 04:16:51 2019
New Revision: 346429
URL: https://svnweb.freebsd.org/changeset/base/346429

Log:
  MFC bectl(8)/libbe(3): r337663-337664,337667,337697-337699,337800,337805,
  337915-337918,337921,337924,337947,337993-337995,338221-338222,338303,
  338417,339047,339972,339994,340334,340507-340508,340592-340594,
  340635-340636,340722-340723,340974,342466,342849,342903,342911,343335,
  343543,343977,343993-343994,344034,344067,344084,345302,345769,
  345845-345846,345848,346082
  
  There are simply too many small changes to enumerate; in summary:
  
  bectl(8)/libbe(3) has been introduced from current state in -CURRENT and
  added to the stable/11 rescue build. bectl(8) is a tool for managing ZFS
  boot environments, largely inspired by beadm. It includes features such as
  being able to jail a boot environment or easily mount it for modification.
  
  Relnotes:	probably

Added:
  stable/11/lib/libbe/
     - copied from r337663, head/lib/libbe/
  stable/11/lib/libbe/Makefile
     - copied, changed from r337995, head/lib/libbe/Makefile
  stable/11/sbin/bectl/
     - copied from r337663, head/sbin/bectl/
  stable/11/sbin/bectl/tests/
     - copied from r340594, head/sbin/bectl/tests/
Modified:
  stable/11/Makefile.inc1
  stable/11/contrib/mdocml/lib.in
  stable/11/etc/mtree/BSD.tests.dist
  stable/11/lib/Makefile
  stable/11/lib/libbe/be.c
  stable/11/lib/libbe/be.h
  stable/11/lib/libbe/be_access.c
  stable/11/lib/libbe/be_error.c
  stable/11/lib/libbe/be_impl.h
  stable/11/lib/libbe/be_info.c
  stable/11/lib/libbe/libbe.3
  stable/11/rescue/rescue/Makefile
  stable/11/sbin/Makefile
  stable/11/sbin/bectl/Makefile
  stable/11/sbin/bectl/bectl.8
  stable/11/sbin/bectl/bectl.c
  stable/11/sbin/bectl/bectl_jail.c
  stable/11/sbin/bectl/bectl_list.c
  stable/11/sbin/bectl/tests/bectl_test.sh
  stable/11/share/mk/bsd.libnames.mk
  stable/11/share/mk/src.libnames.mk
  stable/11/tools/build/mk/OptionalObsoleteFiles.inc
Directory Properties:
  stable/11/   (props changed)

Modified: stable/11/Makefile.inc1
==============================================================================
--- stable/11/Makefile.inc1	Sat Apr 20 03:21:47 2019	(r346428)
+++ stable/11/Makefile.inc1	Sat Apr 20 04:16:51 2019	(r346429)
@@ -2155,7 +2155,7 @@ _prebuild_libs=	${_kerberos5_lib_libasn1} \
 		${_cddl_lib_libumem} ${_cddl_lib_libnvpair} \
 		${_cddl_lib_libuutil} \
 		${_cddl_lib_libavl} \
-		${_cddl_lib_libzfs_core} \
+		${_cddl_lib_libzfs_core} ${_cddl_lib_libzfs} \
 		${_cddl_lib_libctf} \
 		lib/libutil lib/libpjdlog ${_lib_libypclnt} lib/libz lib/msun \
 		${_secure_lib_libcrypto} ${_lib_libldns} \
@@ -2224,7 +2224,15 @@ _cddl_lib_libavl= cddl/lib/libavl
 _cddl_lib_libuutil= cddl/lib/libuutil
 .if ${MK_ZFS} != "no"
 _cddl_lib_libzfs_core= cddl/lib/libzfs_core
+_cddl_lib_libzfs= cddl/lib/libzfs
+
 cddl/lib/libzfs_core__L: cddl/lib/libnvpair__L
+
+cddl/lib/libzfs__L: cddl/lib/libzfs_core__L lib/msun__L lib/libutil__L
+cddl/lib/libzfs__L: lib/libthr__L lib/libmd__L lib/libz__L cddl/lib/libumem__L
+cddl/lib/libzfs__L: cddl/lib/libuutil__L cddl/lib/libavl__L lib/libgeom__L
+
+lib/libbe__L: cddl/lib/libzfs__L
 .endif
 _cddl_lib_libctf= cddl/lib/libctf
 _cddl_lib= cddl/lib

Modified: stable/11/contrib/mdocml/lib.in
==============================================================================
--- stable/11/contrib/mdocml/lib.in	Sat Apr 20 03:21:47 2019	(r346428)
+++ stable/11/contrib/mdocml/lib.in	Sat Apr 20 04:16:51 2019	(r346429)
@@ -28,6 +28,7 @@ LINE("lib80211",	"802.11 Wireless Network Management L
 LINE("libarchive",	"Streaming Archive Library (libarchive, \\-larchive)")
 LINE("libarm",		"ARM Architecture Library (libarm, \\-larm)")
 LINE("libarm32",	"ARM32 Architecture Library (libarm32, \\-larm32)")
+LINE("libbe",		"Boot Environment Library (libbe, \\-lbe)")
 LINE("libbluetooth",	"Bluetooth Library (libbluetooth, \\-lbluetooth)")
 LINE("libbsm",		"Basic Security Module Library (libbsm, \\-lbsm)")
 LINE("libc",		"Standard C\\~Library (libc, \\-lc)")

Modified: stable/11/etc/mtree/BSD.tests.dist
==============================================================================
--- stable/11/etc/mtree/BSD.tests.dist	Sat Apr 20 03:21:47 2019	(r346428)
+++ stable/11/etc/mtree/BSD.tests.dist	Sat Apr 20 04:16:51 2019	(r346429)
@@ -380,6 +380,8 @@
         ..
     ..
     sbin
+        bectl
+        ..
         dhclient
         ..
         devd

Modified: stable/11/lib/Makefile
==============================================================================
--- stable/11/lib/Makefile	Sat Apr 20 03:21:47 2019	(r346428)
+++ stable/11/lib/Makefile	Sat Apr 20 04:16:51 2019	(r346429)
@@ -290,6 +290,7 @@ _libproc=	libproc
 _librtld_db=	librtld_db
 .endif
 SUBDIR.${MK_OFED}+=	ofed
+SUBDIR.${MK_ZFS}+=	libbe
 
 SUBDIR.${MK_OPENMP}+=	libomp
 

Copied and modified: stable/11/lib/libbe/Makefile (from r337995, head/lib/libbe/Makefile)
==============================================================================
--- head/lib/libbe/Makefile	Sat Aug 18 03:20:59 2018	(r337995, copy source)
+++ stable/11/lib/libbe/Makefile	Sat Apr 20 04:16:51 2019	(r346429)
@@ -2,6 +2,7 @@
 
 PACKAGE=	lib${LIB}
 LIB=		be
+SHLIBDIR?= /lib
 SHLIB_MAJOR=	1
 SHLIB_MINOR=	0
 

Modified: stable/11/lib/libbe/be.c
==============================================================================
--- head/lib/libbe/be.c	Sat Aug 11 23:50:09 2018	(r337663)
+++ stable/11/lib/libbe/be.c	Sat Apr 20 04:16:51 2019	(r346429)
@@ -29,11 +29,12 @@
 #include <sys/cdefs.h>
 __FBSDID("$FreeBSD$");
 
+#include <sys/param.h>
+#include <sys/mount.h>
 #include <sys/stat.h>
-#include <sys/types.h>
+#include <sys/ucred.h>
 
 #include <ctype.h>
-#include <kenv.h>
 #include <libgen.h>
 #include <libzfs_core.h>
 #include <stdio.h>
@@ -44,31 +45,49 @@ __FBSDID("$FreeBSD$");
 #include "be.h"
 #include "be_impl.h"
 
+struct be_destroy_data {
+	libbe_handle_t		*lbh;
+	char			*snapname;
+};
+
 #if SOON
 static int be_create_child_noent(libbe_handle_t *lbh, const char *active,
     const char *child_path);
 static int be_create_child_cloned(libbe_handle_t *lbh, const char *active);
 #endif
 
+/* Arbitrary... should tune */
+#define	BE_SNAP_SERIAL_MAX	1024
+
 /*
  * Iterator function for locating the rootfs amongst the children of the
  * zfs_be_root set by loader(8).  data is expected to be a libbe_handle_t *.
  */
 static int
-be_locate_rootfs(zfs_handle_t *chkds, void *data)
+be_locate_rootfs(libbe_handle_t *lbh)
 {
-	libbe_handle_t *lbh;
-	char *mntpoint;
+	struct statfs sfs;
+	struct extmnttab entry;
+	zfs_handle_t *zfs;
 
-	lbh = (libbe_handle_t *)data;
-	if (lbh == NULL)
+	/*
+	 * Check first if root is ZFS; if not, we'll bail on rootfs capture.
+	 * Unfortunately needed because zfs_path_to_zhandle will emit to
+	 * stderr if / isn't actually a ZFS filesystem, which we'd like
+	 * to avoid.
+	 */
+	if (statfs("/", &sfs) == 0) {
+		statfs2mnttab(&sfs, &entry);
+		if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0)
+			return (1);
+	} else
 		return (1);
-
-	if (zfs_is_mounted(chkds, &mntpoint) && strcmp(mntpoint, "/") == 0) {
-		strncpy(lbh->rootfs, zfs_get_name(chkds), BE_MAXPATHLEN);
+	zfs = zfs_path_to_zhandle(lbh->lzh, "/", ZFS_TYPE_FILESYSTEM);
+	if (zfs == NULL)
 		return (1);
-	}
 
+	strlcpy(lbh->rootfs, zfs_get_name(zfs), sizeof(lbh->rootfs));
+	zfs_close(zfs);
 	return (0);
 }
 
@@ -77,52 +96,41 @@ be_locate_rootfs(zfs_handle_t *chkds, void *data)
  * dataset, for example, zroot/ROOT.
  */
 libbe_handle_t *
-libbe_init(void)
+libbe_init(const char *root)
 {
-	struct stat sb;
-	dev_t root_dev, boot_dev;
+	char altroot[MAXPATHLEN];
 	libbe_handle_t *lbh;
-	zfs_handle_t *rootds;
 	char *poolname, *pos;
 	int pnamelen;
 
 	lbh = NULL;
 	poolname = pos = NULL;
-	pnamelen = 0;
-	rootds = NULL;
 
-	/* Verify that /boot and / are mounted on the same filesystem */
-	/* TODO: use errno here?? */
-	if (stat("/", &sb) != 0)
-		goto err;
-
-	root_dev = sb.st_dev;
-
-	if (stat("/boot", &sb) != 0)
-		goto err;
-
-	boot_dev = sb.st_dev;
-
-	if (root_dev != boot_dev) {
-		fprintf(stderr, "/ and /boot not on same device, quitting\n");
-		goto err;
-	}
-
 	if ((lbh = calloc(1, sizeof(libbe_handle_t))) == NULL)
 		goto err;
 
 	if ((lbh->lzh = libzfs_init()) == NULL)
 		goto err;
 
-	/* Obtain path to boot environment root */
-	if ((kenv(KENV_GET, "zfs_be_root", lbh->root, BE_MAXPATHLEN)) == -1)
-		goto err;
+	/*
+	 * Grab rootfs, we'll work backwards from there if an optional BE root
+	 * has not been passed in.
+	 */
+	if (be_locate_rootfs(lbh) != 0) {
+		if (root == NULL)
+			goto err;
+		*lbh->rootfs = '\0';
+	}
+	if (root == NULL) {
+		/* Strip off the final slash from rootfs to get the be root */
+		strlcpy(lbh->root, lbh->rootfs, sizeof(lbh->root));
+		pos = strrchr(lbh->root, '/');
+		if (pos == NULL)
+			goto err;
+		*pos = '\0';
+	} else
+		strlcpy(lbh->root, root, sizeof(lbh->root));
 
-	/* Remove leading 'zfs:' if present, otherwise use value as-is */
-	if (strcmp(lbh->root, "zfs:") == 0)
-		strncpy(lbh->root, strchr(lbh->root, ':') + sizeof(char),
-		    BE_MAXPATHLEN);
-
 	if ((pos = strchr(lbh->root, '/')) == NULL)
 		goto err;
 
@@ -131,26 +139,21 @@ libbe_init(void)
 	if (poolname == NULL)
 		goto err;
 
-	strncpy(poolname, lbh->root, pnamelen);
-	poolname[pnamelen] = '\0';
+	strlcpy(poolname, lbh->root, pnamelen + 1);
 	if ((lbh->active_phandle = zpool_open(lbh->lzh, poolname)) == NULL)
 		goto err;
+	free(poolname);
+	poolname = NULL;
 
 	if (zpool_get_prop(lbh->active_phandle, ZPOOL_PROP_BOOTFS, lbh->bootfs,
-	    BE_MAXPATHLEN, NULL, true) != 0)
+	    sizeof(lbh->bootfs), NULL, true) != 0)
 		goto err;
 
-	/* Obtain path to boot environment rootfs (currently booted) */
-	/* XXX Get dataset mounted at / by kenv/GUID from mountroot? */
-	if ((rootds = zfs_open(lbh->lzh, lbh->root, ZFS_TYPE_DATASET)) == NULL)
-		goto err;
+	if (zpool_get_prop(lbh->active_phandle, ZPOOL_PROP_ALTROOT,
+	    altroot, sizeof(altroot), NULL, true) == 0 &&
+	    strcmp(altroot, "-") != 0)
+		lbh->altroot_len = strlen(altroot);
 
-	zfs_iter_filesystems(rootds, be_locate_rootfs, lbh);
-	zfs_close(rootds);
-	rootds = NULL;
-	if (*lbh->rootfs == '\0')
-		goto err;
-
 	return (lbh);
 err:
 	if (lbh != NULL) {
@@ -160,8 +163,6 @@ err:
 			libzfs_fini(lbh->lzh);
 		free(lbh);
 	}
-	if (rootds != NULL)
-		zfs_close(rootds);
 	free(poolname);
 	return (NULL);
 }
@@ -193,12 +194,38 @@ be_nicenum(uint64_t num, char *buf, size_t buflen)
 static int
 be_destroy_cb(zfs_handle_t *zfs_hdl, void *data)
 {
+	char path[BE_MAXPATHLEN];
+	struct be_destroy_data *bdd;
+	zfs_handle_t *snap;
 	int err;
 
-	if ((err = zfs_iter_children(zfs_hdl, be_destroy_cb, data)) != 0)
+	bdd = (struct be_destroy_data *)data;
+	if (bdd->snapname == NULL) {
+		err = zfs_iter_children(zfs_hdl, be_destroy_cb, data);
+		if (err != 0)
+			return (err);
+		return (zfs_destroy(zfs_hdl, false));
+	}
+	/* If we're dealing with snapshots instead, delete that one alone */
+	err = zfs_iter_filesystems(zfs_hdl, be_destroy_cb, data);
+	if (err != 0)
 		return (err);
-	if ((err = zfs_destroy(zfs_hdl, false)) != 0)
-		return (err);
+	/*
+	 * This part is intentionally glossing over any potential errors,
+	 * because there's a lot less potential for errors when we're cleaning
+	 * up snapshots rather than a full deep BE.  The primary error case
+	 * here being if the snapshot doesn't exist in the first place, which
+	 * the caller will likely deem insignificant as long as it doesn't
+	 * exist after the call.  Thus, such a missing snapshot shouldn't jam
+	 * up the destruction.
+	 */
+	snprintf(path, sizeof(path), "%s@%s", zfs_get_name(zfs_hdl),
+	    bdd->snapname);
+	if (!zfs_dataset_exists(bdd->lbh->lzh, path, ZFS_TYPE_SNAPSHOT))
+		return (0);
+	snap = zfs_open(bdd->lbh->lzh, path, ZFS_TYPE_SNAPSHOT);
+	if (snap != NULL)
+		zfs_destroy(snap, false);
 	return (0);
 }
 
@@ -206,85 +233,144 @@ be_destroy_cb(zfs_handle_t *zfs_hdl, void *data)
  * Destroy the boot environment or snapshot specified by the name
  * parameter. Options are or'd together with the possible values:
  * BE_DESTROY_FORCE : forces operation on mounted datasets
+ * BE_DESTROY_ORIGIN: destroy the origin snapshot as well
  */
 int
 be_destroy(libbe_handle_t *lbh, const char *name, int options)
 {
+	struct be_destroy_data bdd;
+	char origin[BE_MAXPATHLEN], path[BE_MAXPATHLEN];
 	zfs_handle_t *fs;
-	char path[BE_MAXPATHLEN];
-	char *p;
+	char *snapdelim;
 	int err, force, mounted;
+	size_t rootlen;
 
-	p = path;
+	bdd.lbh = lbh;
+	bdd.snapname = NULL;
 	force = options & BE_DESTROY_FORCE;
-	err = BE_ERR_SUCCESS;
+	*origin = '\0';
 
 	be_root_concat(lbh, name, path);
 
-	if (strchr(name, '@') == NULL) {
+	if ((snapdelim = strchr(path, '@')) == NULL) {
 		if (!zfs_dataset_exists(lbh->lzh, path, ZFS_TYPE_FILESYSTEM))
 			return (set_error(lbh, BE_ERR_NOENT));
 
-		if (strcmp(path, lbh->rootfs) == 0)
+		if (strcmp(path, lbh->rootfs) == 0 ||
+		    strcmp(path, lbh->bootfs) == 0)
 			return (set_error(lbh, BE_ERR_DESTROYACT));
 
-		fs = zfs_open(lbh->lzh, p, ZFS_TYPE_FILESYSTEM);
-	} else {
+		fs = zfs_open(lbh->lzh, path, ZFS_TYPE_FILESYSTEM);
+		if (fs == NULL)
+			return (set_error(lbh, BE_ERR_ZFSOPEN));
 
+		if ((options & BE_DESTROY_ORIGIN) != 0 &&
+		    zfs_prop_get(fs, ZFS_PROP_ORIGIN, origin, sizeof(origin),
+		    NULL, NULL, 0, 1) != 0)
+			return (set_error(lbh, BE_ERR_NOORIGIN));
+
+		/* Don't destroy a mounted dataset unless force is specified */
+		if ((mounted = zfs_is_mounted(fs, NULL)) != 0) {
+			if (force) {
+				zfs_unmount(fs, NULL, 0);
+			} else {
+				free(bdd.snapname);
+				return (set_error(lbh, BE_ERR_DESTROYMNT));
+			}
+		}
+	} else {
 		if (!zfs_dataset_exists(lbh->lzh, path, ZFS_TYPE_SNAPSHOT))
 			return (set_error(lbh, BE_ERR_NOENT));
 
-		fs = zfs_open(lbh->lzh, p, ZFS_TYPE_SNAPSHOT);
+		bdd.snapname = strdup(snapdelim + 1);
+		if (bdd.snapname == NULL)
+			return (set_error(lbh, BE_ERR_NOMEM));
+		*snapdelim = '\0';
+		fs = zfs_open(lbh->lzh, path, ZFS_TYPE_DATASET);
+		if (fs == NULL) {
+			free(bdd.snapname);
+			return (set_error(lbh, BE_ERR_ZFSOPEN));
+		}
 	}
 
-	if (fs == NULL)
-		return (set_error(lbh, BE_ERR_ZFSOPEN));
-
-	/* Check if mounted, unmount if force is specified */
-	if ((mounted = zfs_is_mounted(fs, NULL)) != 0) {
-		if (force)
-			zfs_unmount(fs, NULL, 0);
-		else
-			return (set_error(lbh, BE_ERR_DESTROYMNT));
-	}
-
-	if ((err = be_destroy_cb(fs, NULL)) != 0) {
+	err = be_destroy_cb(fs, &bdd);
+	zfs_close(fs);
+	free(bdd.snapname);
+	if (err != 0) {
 		/* Children are still present or the mount is referenced */
 		if (err == EBUSY)
 			return (set_error(lbh, BE_ERR_DESTROYMNT));
 		return (set_error(lbh, BE_ERR_UNKNOWN));
 	}
 
-	return (0);
+	if ((options & BE_DESTROY_ORIGIN) == 0)
+		return (0);
+
+	/* The origin can't possibly be shorter than the BE root */
+	rootlen = strlen(lbh->root);
+	if (*origin == '\0' || strlen(origin) <= rootlen + 1)
+		return (set_error(lbh, BE_ERR_INVORIGIN));
+
+	/*
+	 * We'll be chopping off the BE root and running this back through
+	 * be_destroy, so that we properly handle the origin snapshot whether
+	 * it be that of a deep BE or not.
+	 */
+	if (strncmp(origin, lbh->root, rootlen) != 0 || origin[rootlen] != '/')
+		return (0);
+
+	return (be_destroy(lbh, origin + rootlen + 1,
+	    options & ~BE_DESTROY_ORIGIN));
 }
 
+static void
+be_setup_snapshot_name(libbe_handle_t *lbh, char *buf, size_t buflen)
+{
+	time_t rawtime;
+	int len, serial;
 
+	time(&rawtime);
+	len = strlen(buf);
+	len += strftime(buf + len, buflen - len, "@%F-%T", localtime(&rawtime));
+	/* No room for serial... caller will do its best */
+	if (buflen - len < 2)
+		return;
+
+	for (serial = 0; serial < BE_SNAP_SERIAL_MAX; ++serial) {
+		snprintf(buf + len, buflen - len, "-%d", serial);
+		if (!zfs_dataset_exists(lbh->lzh, buf, ZFS_TYPE_SNAPSHOT))
+			return;
+	}
+}
+
 int
 be_snapshot(libbe_handle_t *lbh, const char *source, const char *snap_name,
     bool recursive, char *result)
 {
 	char buf[BE_MAXPATHLEN];
-	time_t rawtime;
-	int len, err;
+	int err;
 
 	be_root_concat(lbh, source, buf);
 
-	if (!be_exists(lbh, buf))
-		return (BE_ERR_NOENT);
+	if ((err = be_exists(lbh, buf)) != 0)
+		return (set_error(lbh, err));
 
 	if (snap_name != NULL) {
-		strcat(buf, "@");
-		strcat(buf, snap_name);
+		if (strlcat(buf, "@", sizeof(buf)) >= sizeof(buf))
+			return (set_error(lbh, BE_ERR_INVALIDNAME));
+
+		if (strlcat(buf, snap_name, sizeof(buf)) >= sizeof(buf))
+			return (set_error(lbh, BE_ERR_INVALIDNAME));
+
 		if (result != NULL)
 			snprintf(result, BE_MAXPATHLEN, "%s@%s", source,
 			    snap_name);
 	} else {
-		time(&rawtime);
-		len = strlen(buf);
-		strftime(buf + len, BE_MAXPATHLEN - len,
-		    "@%F-%T", localtime(&rawtime));
-		if (result != NULL)
-			strcpy(result, strrchr(buf, '/') + 1);
+		be_setup_snapshot_name(lbh, buf, sizeof(buf));
+
+		if (result != NULL && strlcpy(result, strrchr(buf, '/') + 1,
+		    sizeof(buf)) >= sizeof(buf))
+			return (set_error(lbh, BE_ERR_INVALIDNAME));
 	}
 
 	if ((err = zfs_snapshot(lbh->lzh, buf, recursive, NULL)) != 0) {
@@ -322,7 +408,6 @@ be_create(libbe_handle_t *lbh, const char *name)
 	return (set_error(lbh, err));
 }
 
-
 static int
 be_deep_clone_prop(int prop, void *cb)
 {
@@ -331,6 +416,7 @@ be_deep_clone_prop(int prop, void *cb)
 	zprop_source_t src;
 	char pval[BE_MAXPATHLEN];
 	char source[BE_MAXPATHLEN];
+	char *val;
 
 	dccb = cb;
 	/* Skip some properties we don't want to touch */
@@ -350,8 +436,13 @@ be_deep_clone_prop(int prop, void *cb)
 	if (src != ZPROP_SRC_LOCAL)
 		return (ZPROP_CONT);
 
-	nvlist_add_string(dccb->props, zfs_prop_to_name(prop), (char *)pval);
+	/* Augment mountpoint with altroot, if needed */
+	val = pval;
+	if (prop == ZFS_PROP_MOUNTPOINT)
+		val = be_mountpoint_augmented(dccb->lbh, val);
 
+	nvlist_add_string(dccb->props, zfs_prop_to_name(prop), val);
+
 	return (ZPROP_CONT);
 }
 
@@ -391,26 +482,23 @@ be_deep_clone(zfs_handle_t *ds, void *data)
 	nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP);
 	nvlist_add_string(props, "canmount", "noauto");
 
+	dccb.lbh = isdc->lbh;
 	dccb.zhp = ds;
 	dccb.props = props;
 	if (zprop_iter(be_deep_clone_prop, &dccb, B_FALSE, B_FALSE,
 	    ZFS_TYPE_FILESYSTEM) == ZPROP_INVAL)
 		return (-1);
 
-	if ((err = zfs_clone(snap_hdl, be_path, props)) != 0) {
-		switch (err) {
-		case EZFS_SUCCESS:
-			err = BE_ERR_SUCCESS;
-			break;
-		default:
-			err = BE_ERR_ZFSCLONE;
-			break;
-		}
-	}
+	if ((err = zfs_clone(snap_hdl, be_path, props)) != 0)
+		err = BE_ERR_ZFSCLONE;
 
 	nvlist_free(props);
 	zfs_close(snap_hdl);
 
+	/* Failed to clone */
+	if (err != BE_ERR_SUCCESS)
+		return (set_error(isdc->lbh, err));
+
 	sdc.lbh = isdc->lbh;
 	sdc.bename = NULL;
 	sdc.snapname = isdc->snapname;
@@ -451,14 +539,13 @@ be_create_from_existing_snap(libbe_handle_t *lbh, cons
 	else
 		bename++;
 
-	if ((parentname = strdup(snap_path)) == NULL) {
-		err = BE_ERR_UNKNOWN;
-		return (set_error(lbh, err));
-	}
+	if ((parentname = strdup(snap_path)) == NULL)
+		return (set_error(lbh, BE_ERR_UNKNOWN));
+
 	snapname = strchr(parentname, '@');
 	if (snapname == NULL) {
-		err = BE_ERR_UNKNOWN;
-		return (set_error(lbh, err));
+		free(parentname);
+		return (set_error(lbh, BE_ERR_UNKNOWN));
 	}
 	*snapname = '\0';
 	snapname++;
@@ -471,6 +558,7 @@ be_create_from_existing_snap(libbe_handle_t *lbh, cons
 	parent_hdl = zfs_open(lbh->lzh, parentname, ZFS_TYPE_DATASET);
 	err = be_deep_clone(parent_hdl, &sdc);
 
+	free(parentname);
 	return (set_error(lbh, err));
 }
 
@@ -484,7 +572,7 @@ be_create_from_existing(libbe_handle_t *lbh, const cha
 	int err;
 	char buf[BE_MAXPATHLEN];
 
-	if ((err = be_snapshot(lbh, old, NULL, true, (char *)&buf)))
+	if ((err = be_snapshot(lbh, old, NULL, true, (char *)&buf)) != 0)
 		return (set_error(lbh, err));
 
 	err = be_create_from_existing_snap(lbh, name, (char *)buf);
@@ -501,39 +589,18 @@ be_create_from_existing(libbe_handle_t *lbh, const cha
 int
 be_validate_snap(libbe_handle_t *lbh, const char *snap_name)
 {
-	zfs_handle_t *zfs_hdl;
-	char buf[BE_MAXPATHLEN];
-	char *delim_pos;
-	int err = BE_ERR_SUCCESS;
 
 	if (strlen(snap_name) >= BE_MAXPATHLEN)
 		return (BE_ERR_PATHLEN);
 
+	if (!zfs_name_valid(snap_name, ZFS_TYPE_SNAPSHOT))
+		return (BE_ERR_INVALIDNAME);
+
 	if (!zfs_dataset_exists(lbh->lzh, snap_name,
 	    ZFS_TYPE_SNAPSHOT))
 		return (BE_ERR_NOENT);
 
-	strncpy(buf, snap_name, BE_MAXPATHLEN);
-
-	/* Find the base filesystem of the snapshot */
-	if ((delim_pos = strchr(buf, '@')) == NULL)
-		return (BE_ERR_INVALIDNAME);
-	*delim_pos = '\0';
-
-	if ((zfs_hdl =
-	    zfs_open(lbh->lzh, buf, ZFS_TYPE_DATASET)) == NULL)
-		return (BE_ERR_NOORIGIN);
-
-	if ((err = zfs_prop_get(zfs_hdl, ZFS_PROP_MOUNTPOINT, buf, BE_MAXPATHLEN,
-	    NULL, NULL, 0, 1)) != 0)
-		err = BE_ERR_INVORIGIN;
-
-	if ((err != 0) && (strncmp(buf, "/", BE_MAXPATHLEN) != 0))
-		err = BE_ERR_INVORIGIN;
-
-	zfs_close(zfs_hdl);
-
-	return (err);
+	return (BE_ERR_SUCCESS);
 }
 
 
@@ -561,7 +628,7 @@ be_root_concat(libbe_handle_t *lbh, const char *name, 
 		if (name_len >= BE_MAXPATHLEN)
 			return (BE_ERR_PATHLEN);
 
-		strncpy(result, name, BE_MAXPATHLEN);
+		strlcpy(result, name, BE_MAXPATHLEN);
 		return (BE_ERR_SUCCESS);
 	} else if (name_len + root_len + 1 < BE_MAXPATHLEN) {
 		snprintf(result, BE_MAXPATHLEN, "%s/%s", lbh->root,
@@ -575,18 +642,23 @@ be_root_concat(libbe_handle_t *lbh, const char *name, 
 
 /*
  * Verifies the validity of a boot environment name (A-Za-z0-9-_.). Returns
- * BE_ERR_SUCCESS (0) if name is valid, otherwise returns BE_ERR_INVALIDNAME.
+ * BE_ERR_SUCCESS (0) if name is valid, otherwise returns BE_ERR_INVALIDNAME
+ * or BE_ERR_PATHLEN.
  * Does not set internal library error state.
  */
 int
-be_validate_name(libbe_handle_t *lbh __unused, const char *name)
+be_validate_name(libbe_handle_t *lbh, const char *name)
 {
-	for (int i = 0; *name; i++) {
-		char c = *(name++);
-		if (isalnum(c) || (c == '-') || (c == '_') || (c == '.'))
-			continue;
+
+	/*
+	 * Impose the additional restriction that the entire dataset name must
+	 * not exceed the maximum length of a dataset, i.e. MAXNAMELEN.
+	 */
+	if (strlen(lbh->root) + 1 + strlen(name) > MAXNAMELEN)
+		return (BE_ERR_PATHLEN);
+
+	if (!zfs_name_valid(name, ZFS_TYPE_DATASET))
 		return (BE_ERR_INVALIDNAME);
-	}
 
 	return (BE_ERR_SUCCESS);
 }
@@ -603,18 +675,17 @@ be_rename(libbe_handle_t *lbh, const char *old, const 
 	zfs_handle_t *zfs_hdl;
 	int err;
 
+	/*
+	 * be_validate_name is documented not to set error state, so we should
+	 * do so here.
+	 */
+	if ((err = be_validate_name(lbh, new)) != 0)
+		return (set_error(lbh, err));
 	if ((err = be_root_concat(lbh, old, full_old)) != 0)
 		return (set_error(lbh, err));
 	if ((err = be_root_concat(lbh, new, full_new)) != 0)
 		return (set_error(lbh, err));
 
-	if ((err = be_validate_name(lbh, new)) != 0)
-		return (err);
-
-	/* Check if old is active BE */
-	if (strcmp(full_old, be_active_path(lbh)) == 0)
-		return (set_error(lbh, BE_ERR_MOUNTED));
-
 	if (!zfs_dataset_exists(lbh->lzh, full_old, ZFS_TYPE_DATASET))
 		return (set_error(lbh, BE_ERR_NOENT));
 
@@ -625,20 +696,17 @@ be_rename(libbe_handle_t *lbh, const char *old, const 
 	    ZFS_TYPE_FILESYSTEM)) == NULL)
 		return (set_error(lbh, BE_ERR_ZFSOPEN));
 
-	/* XXX TODO: Allow a force flag */
-	if (zfs_is_mounted(zfs_hdl, NULL)) {
-		zfs_close(zfs_hdl);
-		return (set_error(lbh, BE_ERR_MOUNTED));
-	}
-
 	/* recurse, nounmount, forceunmount */
-	struct renameflags flags = { 0, 0, 0 };
+	struct renameflags flags = {
+		.nounmount = 1,
+	};
 
 	err = zfs_rename(zfs_hdl, NULL, full_new, flags);
 
 	zfs_close(zfs_hdl);
-
-	return (set_error(lbh, err));
+	if (err != 0)
+		return (set_error(lbh, BE_ERR_UNKNOWN));
+	return (0);
 }
 
 
@@ -670,33 +738,14 @@ int
 be_import(libbe_handle_t *lbh, const char *bootenv, int fd)
 {
 	char buf[BE_MAXPATHLEN];
-	time_t rawtime;
 	nvlist_t *props;
 	zfs_handle_t *zfs;
-	int err, len;
-	char nbuf[24];
+	recvflags_t flags = { .nomount = 1 };
+	int err;
 
-	/*
-	 * We don't need this to be incredibly random, just unique enough that
-	 * it won't conflict with an existing dataset name.  Chopping time
-	 * down to 32 bits is probably good enough for this.
-	 */
-	snprintf(nbuf, 24, "tmp%u",
-	    (uint32_t)(time(NULL) & 0xFFFFFFFF));
-	if ((err = be_root_concat(lbh, nbuf, buf)) != 0)
-		/*
-		 * Technically this is our problem, but we try to use short
-		 * enough names that we won't run into problems except in
-		 * worst-case BE root approaching MAXPATHLEN.
-		 */
-		return (set_error(lbh, BE_ERR_PATHLEN));
+	be_root_concat(lbh, bootenv, buf);
 
-	time(&rawtime);
-	len = strlen(buf);
-	strftime(buf + len, BE_MAXPATHLEN - len,
-	    "@%F-%T", localtime(&rawtime));
-
-	if ((err = lzc_receive(buf, NULL, NULL, false, fd)) != 0) {
+	if ((err = zfs_receive(lbh->lzh, buf, NULL, &flags, fd, NULL)) != 0) {
 		switch (err) {
 		case EINVAL:
 			return (set_error(lbh, BE_ERR_NOORIGIN));
@@ -709,22 +758,22 @@ be_import(libbe_handle_t *lbh, const char *bootenv, in
 		}
 	}
 
-	if ((zfs = zfs_open(lbh->lzh, buf, ZFS_TYPE_SNAPSHOT)) == NULL)
+	if ((zfs = zfs_open(lbh->lzh, buf, ZFS_TYPE_FILESYSTEM)) == NULL)
 		return (set_error(lbh, BE_ERR_ZFSOPEN));
 
 	nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP);
 	nvlist_add_string(props, "canmount", "noauto");
 	nvlist_add_string(props, "mountpoint", "/");
 
-	be_root_concat(lbh, bootenv, buf);
+	err = zfs_prop_set_list(zfs, props);
+	nvlist_free(props);
 
-	err = zfs_clone(zfs, buf, props);
 	zfs_close(zfs);
 
-	nvlist_free(props);
+	if (err != 0)
+		return (set_error(lbh, BE_ERR_UNKNOWN));
 
-	/* XXX TODO: Figure out how to destroy the ghost... */
-	return (BE_ERR_SUCCESS);
+	return (0);
 }
 
 #if SOON
@@ -901,21 +950,38 @@ be_set_nextboot(libbe_handle_t *lbh, nvlist_t *config,
 	return (0);
 }
 
+/*
+ * Deactivate old BE dataset; currently just sets canmount=noauto
+ */
+static int
+be_deactivate(libbe_handle_t *lbh, const char *ds)
+{
+	zfs_handle_t *zfs;
 
+	if ((zfs = zfs_open(lbh->lzh, ds, ZFS_TYPE_DATASET)) == NULL)
+		return (1);
+	if (zfs_prop_set(zfs, "canmount", "noauto") != 0)
+		return (1);
+	zfs_close(zfs);
+	return (0);
+}
+
 int
 be_activate(libbe_handle_t *lbh, const char *bootenv, bool temporary)
 {
 	char be_path[BE_MAXPATHLEN];
 	char buf[BE_MAXPATHLEN];
+	nvlist_t *config, *dsprops, *vdevs;
+	char *origin;
 	uint64_t pool_guid;
-	nvlist_t *config, *vdevs;
+	zfs_handle_t *zhp;
 	int err;
 
 	be_root_concat(lbh, bootenv, be_path);
 
 	/* Note: be_exists fails if mountpoint is not / */
-	if (!be_exists(lbh, be_path))
-		return (BE_ERR_NOENT);
+	if ((err = be_exists(lbh, be_path)) != 0)
+		return (set_error(lbh, err));
 
 	if (temporary) {
 		config = zpool_get_config(lbh->active_phandle, NULL);
@@ -929,9 +995,7 @@ be_activate(libbe_handle_t *lbh, const char *bootenv, 
 			return (set_error(lbh, BE_ERR_UNKNOWN));
 
 		/* Expected format according to zfsbootcfg(8) man */
-		strcpy(buf, "zfs:");
-		strcat(buf, be_path);
-		strcat(buf, ":");
+		snprintf(buf, sizeof(buf), "zfs:%s:", be_path);
 
 		/* We have no config tree */
 		if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
@@ -940,16 +1004,35 @@ be_activate(libbe_handle_t *lbh, const char *bootenv, 
 
 		return (be_set_nextboot(lbh, vdevs, pool_guid, buf));
 	} else {
+		if (be_deactivate(lbh, lbh->bootfs) != 0)
+			return (-1);
+
 		/* Obtain bootenv zpool */
 		err = zpool_set_prop(lbh->active_phandle, "bootfs", be_path);
+		if (err)
+			return (-1);
 
-		switch (err) {
-		case 0:
-			return (BE_ERR_SUCCESS);
+		zhp = zfs_open(lbh->lzh, be_path, ZFS_TYPE_FILESYSTEM);
+		if (zhp == NULL)
+			return (-1);
 
-		default:
-			/* XXX TODO correct errors */
+		if (be_prop_list_alloc(&dsprops) != 0)
 			return (-1);
+
+		if (be_get_dataset_props(lbh, be_path, dsprops) != 0) {
+			nvlist_free(dsprops);
+			return (-1);
 		}
+
+		if (nvlist_lookup_string(dsprops, "origin", &origin) == 0)
+			err = zfs_promote(zhp);
+		nvlist_free(dsprops);
+
+		zfs_close(zhp);
+
+		if (err)
+			return (-1);
 	}
+
+	return (BE_ERR_SUCCESS);
 }

Modified: stable/11/lib/libbe/be.h
==============================================================================
--- head/lib/libbe/be.h	Sat Aug 11 23:50:09 2018	(r337663)
+++ stable/11/lib/libbe/be.h	Sat Apr 20 04:16:51 2019	(r346429)
@@ -49,7 +49,7 @@ typedef enum be_error {
 	BE_ERR_BADPATH,		/* path not suitable for operation */
 	BE_ERR_PATHBUSY,	/* requested path is busy */
 	BE_ERR_PATHLEN,         /* provided name exceeds maximum length limit */
-	BE_ERR_INVORIGIN,       /* snapshot origin's mountpoint is not '/' */
+	BE_ERR_BADMOUNT,        /* mountpoint is not '/' */
 	BE_ERR_NOORIGIN,        /* could not open snapshot's origin */
 	BE_ERR_MOUNTED,         /* boot environment is already mounted */
 	BE_ERR_NOMOUNT,         /* boot environment is not mounted */
@@ -59,11 +59,12 @@ typedef enum be_error {
 	BE_ERR_NOPOOL,		/* operation not supported on this pool */
 	BE_ERR_NOMEM,		/* insufficient memory */
 	BE_ERR_UNKNOWN,         /* unknown error */
+	BE_ERR_INVORIGIN,       /* invalid origin */
 } be_error_t;
 
 
 /* Library handling functions: be.c */
-libbe_handle_t *libbe_init(void);
+libbe_handle_t *libbe_init(const char *root);
 void libbe_close(libbe_handle_t *);
 
 /* Bootenv information functions: be_info.c */
@@ -93,7 +94,8 @@ int be_rename(libbe_handle_t *, const char *, const ch
 /* Bootenv removal functions */
 
 typedef enum {
-	BE_DESTROY_FORCE = 1 << 0,
+	BE_DESTROY_FORCE	= 1 << 0,
+	BE_DESTROY_ORIGIN	= 1 << 1,
 } be_destroy_opt_t;
 
 int be_destroy(libbe_handle_t *, const char *, int);
@@ -102,7 +104,7 @@ int be_destroy(libbe_handle_t *, const char *, int);
 
 typedef enum {
 	BE_MNT_FORCE		= 1 << 0,
-		BE_MNT_DEEP	= 1 << 1,
+	BE_MNT_DEEP		= 1 << 1,
 } be_mount_opt_t;
 
 int be_mount(libbe_handle_t *, char *, char *, int, char *);
@@ -118,7 +120,7 @@ void libbe_print_on_error(libbe_handle_t *, bool);
 int be_root_concat(libbe_handle_t *, const char *, char *);
 int be_validate_name(libbe_handle_t * __unused, const char *);
 int be_validate_snap(libbe_handle_t *, const char *);
-bool be_exists(libbe_handle_t *, char *);
+int be_exists(libbe_handle_t *, char *);
 
 int be_export(libbe_handle_t *, const char *, int fd);
 int be_import(libbe_handle_t *, const char *, int fd);

Modified: stable/11/lib/libbe/be_access.c
==============================================================================
--- head/lib/libbe/be_access.c	Sat Aug 11 23:50:09 2018	(r337663)
+++ stable/11/lib/libbe/be_access.c	Sat Apr 20 04:16:51 2019	(r346429)
@@ -3,6 +3,7 @@
  *
  * Copyright (c) 2017 Kyle J. Kneitinger <kyle at kneit.in>
  * Copyright (c) 2018 Kyle Evans <kevans at FreeBSD.org>
+ * Copyright (c) 2019 Wes Maag <wes at jwmaag.org>
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -38,6 +39,14 @@ struct be_mountcheck_info {
 	char *name;
 };
 
+struct be_mount_info {
+	libbe_handle_t *lbh;
+	const char *be;
+	const char *mountpoint;
+	int mntflags;
+	int deepmount;
+};
+
 static int
 be_mountcheck_cb(zfs_handle_t *zfs_hdl, void *data)
 {
@@ -51,23 +60,124 @@ be_mountcheck_cb(zfs_handle_t *zfs_hdl, void *data)
 		return (0);
 	if (strcmp(mountpoint, info->path) == 0) {
 		info->name = strdup(zfs_get_name(zfs_hdl));
+		free(mountpoint);
 		return (1);
 	}
+	free(mountpoint);
 	return (0);
 }
 
 /*
+ * Called from be_mount, uses the given zfs_handle and attempts to
+ * mount it at the passed mountpoint. If the deepmount flag is set, continue
+ * calling the function for each child dataset.
+ */
+static int
+be_mount_iter(zfs_handle_t *zfs_hdl, void *data)
+{
+	int err;
+	char *mountpoint;
+	char tmp[BE_MAXPATHLEN], zfs_mnt[BE_MAXPATHLEN];
+	struct be_mount_info *info;
+
+	info = (struct be_mount_info *)data;
+
+	if (zfs_is_mounted(zfs_hdl, &mountpoint)) {
+		free(mountpoint);
+		return (0);
+	}
+
+	if (zfs_prop_get_int(zfs_hdl, ZFS_PROP_CANMOUNT) == ZFS_CANMOUNT_OFF)
+		return (0);
+
+	if (zfs_prop_get(zfs_hdl, ZFS_PROP_MOUNTPOINT, zfs_mnt, BE_MAXPATHLEN,
+	    NULL, NULL, 0, 1))
+		return (1);
+
+	if (strcmp("none", zfs_mnt) != 0) {
+		char opt = '\0';
+

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***




More information about the svn-src-all mailing list