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 <[email protected]>
  * Copyright (c) 2018 Kyle Evans <[email protected]>
+ * Copyright (c) 2019 Wes Maag <[email protected]>
  * 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 ***


_______________________________________________
[email protected] mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "[email protected]"

Reply via email to