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
Sat Apr 20 04:16:54 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-stable
mailing list