Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F144309046
D18564.id54713.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
18 KB
Referenced Files
None
Subscribers
None
D18564.id54713.diff
View Options
Index: lib/libbe/Makefile
===================================================================
--- lib/libbe/Makefile
+++ lib/libbe/Makefile
@@ -1,8 +1,10 @@
# $FreeBSD$
+.include <src.opts.mk>
+
PACKAGE= lib${LIB}
LIB= be
-SHLIBDIR?= /lib
+SHLIBDIR= /lib
SHLIB_MAJOR= 1
SHLIB_MINOR= 0
@@ -28,4 +30,7 @@
CFLAGS+= -DNEED_SOLARIS_BOOLEAN
+HAS_TESTS= YES
+SUBDIR.${MK_TESTS}+= tests
+
.include <bsd.lib.mk>
Index: lib/libbe/be.h
===================================================================
--- lib/libbe/be.h
+++ lib/libbe/be.h
@@ -84,6 +84,7 @@
/* Bootenv creation functions */
int be_create(libbe_handle_t *, const char *);
+int be_create_shallow(libbe_handle_t *, const char *, const char *);
int be_create_from_existing(libbe_handle_t *, const char *, const char *);
int be_create_from_existing_snap(libbe_handle_t *, const char *, const char *);
int be_snapshot(libbe_handle_t *, const char *, const char *, bool, char *);
Index: lib/libbe/be.c
===================================================================
--- lib/libbe/be.c
+++ lib/libbe/be.c
@@ -352,7 +352,6 @@
sizeof(buf)) >= sizeof(buf))
return (set_error(lbh, BE_ERR_INVALIDNAME));
}
-
if ((err = zfs_snapshot(lbh->lzh, buf, recursive, NULL)) != 0) {
switch (err) {
case EZFS_INVALIDNAME:
@@ -426,43 +425,78 @@
return (ZPROP_CONT);
}
+/*
+ * Return the corresponding boot environment path for a given
+ * dataset path, the constructed path is placed in 'result'.
+ *
+ * example: say our new boot environment name is 'bootenv' and
+ * the dataset path is 'zroot/ROOT/default/data/set'.
+ *
+ * result should produce: 'zroot/ROOT/bootenv/data/set'
+ */
static int
-be_deep_clone(zfs_handle_t *ds, void *data)
+be_get_path(struct libbe_deep_clone *ldc, const char *dspath, char *result, int result_size)
+{
+ char *pos;
+ char *child_dataset;
+
+ /* match the root path for the boot environments */
+ pos = strstr(dspath, ldc->lbh->root);
+
+ /* no match, different pools? */
+ if (pos == NULL)
+ return (BE_ERR_BADPATH);
+
+ /* root path of the new boot environment */
+ snprintf(result, result_size, "%s/%s", ldc->lbh->root, ldc->bename);
+
+ /* gets us to the parent dataset, the +1 consumes a trailing slash */
+ pos += strlen(ldc->lbh->root) + 1;
+
+ /* skip the parent dataset */
+ if ((child_dataset = strchr(pos, '/')) != NULL)
+ strlcat(result, child_dataset, result_size);
+
+ return (BE_ERR_SUCCESS);
+}
+
+static int
+be_clone_cb(zfs_handle_t *ds, void *data)
{
int err;
char be_path[BE_MAXPATHLEN];
char snap_path[BE_MAXPATHLEN];
const char *dspath;
- char *dsname;
zfs_handle_t *snap_hdl;
nvlist_t *props;
- struct libbe_deep_clone *isdc, sdc;
+ struct libbe_deep_clone *ldc;
struct libbe_dccb dccb;
- isdc = (struct libbe_deep_clone *)data;
+ ldc = (struct libbe_deep_clone *)data;
dspath = zfs_get_name(ds);
- if ((dsname = strrchr(dspath, '/')) == NULL)
- return (BE_ERR_UNKNOWN);
- dsname++;
- if (isdc->bename == NULL)
- snprintf(be_path, sizeof(be_path), "%s/%s", isdc->be_root, dsname);
- else
- snprintf(be_path, sizeof(be_path), "%s/%s", isdc->be_root, isdc->bename);
+ snprintf(snap_path, sizeof(snap_path), "%s@%s", dspath, ldc->snapname);
- snprintf(snap_path, sizeof(snap_path), "%s@%s", dspath, isdc->snapname);
+ /* construct the boot environment path from the dataset we're cloning */
+ if (be_get_path(ldc, dspath, be_path, sizeof(be_path)) != BE_ERR_SUCCESS)
+ return (set_error(ldc->lbh, BE_ERR_UNKNOWN));
- if (zfs_dataset_exists(isdc->lbh->lzh, be_path, ZFS_TYPE_DATASET))
- return (set_error(isdc->lbh, BE_ERR_EXISTS));
+ /* the dataset to be created (i.e. the boot environment) already exists */
+ if (zfs_dataset_exists(ldc->lbh->lzh, be_path, ZFS_TYPE_DATASET))
+ return (set_error(ldc->lbh, BE_ERR_EXISTS));
+
+ /* no snapshot found for this dataset, silently skip it */
+ if (!zfs_dataset_exists(ldc->lbh->lzh, snap_path, ZFS_TYPE_SNAPSHOT))
+ return (0);
if ((snap_hdl =
- zfs_open(isdc->lbh->lzh, snap_path, ZFS_TYPE_SNAPSHOT)) == NULL)
- return (set_error(isdc->lbh, BE_ERR_ZFSOPEN));
+ zfs_open(ldc->lbh->lzh, snap_path, ZFS_TYPE_SNAPSHOT)) == NULL)
+ return (set_error(ldc->lbh, BE_ERR_ZFSOPEN));
nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP);
nvlist_add_string(props, "canmount", "noauto");
- dccb.lbh = isdc->lbh;
+ dccb.lbh = ldc->lbh;
dccb.zhp = ds;
dccb.props = props;
if (zprop_iter(be_deep_clone_prop, &dccb, B_FALSE, B_FALSE,
@@ -470,58 +504,53 @@
return (-1);
if ((err = zfs_clone(snap_hdl, be_path, props)) != 0)
- err = BE_ERR_ZFSCLONE;
+ return (set_error(ldc->lbh, 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;
- sdc.be_root = (char *)&be_path;
+ /* create recursive boot environment */
+ if (ldc->recursive)
+ err = zfs_iter_filesystems(ds, be_clone_cb, ldc);
- err = zfs_iter_filesystems(ds, be_deep_clone, &sdc);
-
- return (err);
+ return (set_error(ldc->lbh, err));
}
/*
- * Create the boot environment from pre-existing snapshot
- */
-int
-be_create_from_existing_snap(libbe_handle_t *lbh, const char *name,
- const char *snap)
+ * Create a boot environment with a given name from a given snapshot.
+ * Snapshots can be in the format 'zroot/ROOT/default@snapshot' or
+ * 'default@snapshot'. In the latter case, 'default@snapshot' will be prepended
+ * with the root path that libbe was initailized with.
+*/
+static int
+be_clone(libbe_handle_t *lbh, const char *bename, const char *snapshot, bool recursive)
{
int err;
- char be_path[BE_MAXPATHLEN];
char snap_path[BE_MAXPATHLEN];
- const char *bename;
char *parentname, *snapname;
zfs_handle_t *parent_hdl;
- struct libbe_deep_clone sdc;
+ struct libbe_deep_clone ldc;
- if ((err = be_validate_name(lbh, name)) != 0)
- return (set_error(lbh, err));
- if ((err = be_root_concat(lbh, snap, snap_path)) != 0)
- return (set_error(lbh, err));
- if ((err = be_validate_snap(lbh, snap_path)) != 0)
+ /* ensure the boot environment name is valid */
+ if ((err = be_validate_name(lbh, bename)) != 0)
return (set_error(lbh, err));
- if ((err = be_root_concat(lbh, name, be_path)) != 0)
+ /*
+ * prepend the boot environment root path if we're
+ * given a partial snapshot name.
+ */
+ if ((err = be_root_concat(lbh, snapshot, snap_path)) != 0)
return (set_error(lbh, err));
- if ((bename = strrchr(name, '/')) == NULL)
- bename = name;
- else
- bename++;
+ /* ensure the snapshot exists */
+ if ((err = be_validate_snap(lbh, snap_path)) != 0)
+ return (set_error(lbh, err));
+ /* get a copy of the snapshot path so we can disect it */
if ((parentname = strdup(snap_path)) == NULL)
return (set_error(lbh, BE_ERR_UNKNOWN));
+ /* split dataset name from snapshot name */
snapname = strchr(parentname, '@');
if (snapname == NULL) {
free(parentname);
@@ -530,32 +559,56 @@
*snapname = '\0';
snapname++;
- sdc.lbh = lbh;
- sdc.bename = bename;
- sdc.snapname = snapname;
- sdc.be_root = lbh->root;
+ /* set-up the boot environment */
+ ldc.lbh = lbh;
+ ldc.bename = bename;
+ ldc.snapname = snapname;
+ ldc.recursive = recursive;
+ /* the boot environment will be cloned from this dataset */
parent_hdl = zfs_open(lbh->lzh, parentname, ZFS_TYPE_DATASET);
- err = be_deep_clone(parent_hdl, &sdc);
+
+ /* create the boot environment */
+ err = be_clone_cb(parent_hdl, &ldc);
free(parentname);
return (set_error(lbh, err));
}
+/*
+ * Create a non-recursive boot environment from pre-existing snapshot
+ */
+int
+be_create_shallow(libbe_handle_t *lbh, const char *bename,
+ const char *snap)
+{
+ return (be_clone(lbh, bename, snap, false));
+}
+
+/*
+ * Create the boot environment from pre-existing snapshot
+ */
+int
+be_create_from_existing_snap(libbe_handle_t *lbh, const char *bename,
+ const char *snap)
+{
+ return (be_clone(lbh, bename, snap, true));
+}
+
/*
* Create a boot environment from an existing boot environment
*/
int
-be_create_from_existing(libbe_handle_t *lbh, const char *name, const char *old)
+be_create_from_existing(libbe_handle_t *lbh, const char *bename, const char *old)
{
int err;
- char buf[BE_MAXPATHLEN];
+ char snap[BE_MAXPATHLEN];
- if ((err = be_snapshot(lbh, old, NULL, true, (char *)&buf)) != 0)
+ if ((err = be_snapshot(lbh, old, NULL, true, snap)) != 0)
return (set_error(lbh, err));
- err = be_create_from_existing_snap(lbh, name, (char *)buf);
+ err = be_clone(lbh, bename, snap, true);
return (set_error(lbh, err));
}
Index: lib/libbe/be_impl.h
===================================================================
--- lib/libbe/be_impl.h
+++ lib/libbe/be_impl.h
@@ -50,7 +50,7 @@
libbe_handle_t *lbh;
const char *bename;
const char *snapname;
- const char *be_root;
+ bool recursive;
};
struct libbe_dccb {
Index: lib/libbe/libbe.3
===================================================================
--- lib/libbe/libbe.3
+++ lib/libbe/libbe.3
@@ -69,6 +69,9 @@
.Fn be_create_from_existing_snap "libbe_handle_t *hdl" "const char *be_name" "const char *snap"
.Pp
.Ft int
+.Fn be_create_shallow "libbe_handle_t *hdl" "const char *be_name" "const char *snap"
+.Pp
+.Ft int
.Fn be_rename "libbe_handle_t *hdl" "const char *be_old" "const char *be_new"
.Pp
.Ft int
@@ -213,21 +216,26 @@
The
.Fn be_create
function creates a boot environment with the given name.
-It will be created from a snapshot of the currently booted boot environment.
+The new boot environment will be created from a recursive snapshot of the currently
+booted boot environment.
.Pp
The
.Fn be_create_from_existing
function creates a boot environment with the given name from the name of an
-existing boot environment.
-A snapshot will be made of the base boot environment, and the new boot
-environment will be created from that.
+existing boot environment. A recursive snapshot will be made of the origin
+boot environment, and the new boot environment will be created from that.
.Pp
The
.Fn be_create_from_existing_snap
-function creates a boot environment with the given name from an existing
+function creates a recursive boot environment with the given name from an existing
snapshot.
.Pp
The
+.Fn be_create_shallow
+function creates a non-recursive boot environment with the given name from
+an existing snapshot.
+.Pp
+The
.Fn be_rename
function renames a boot environment without unmounting it, as if renamed with
the
Index: lib/libbe/tests/Makefile
===================================================================
--- /dev/null
+++ lib/libbe/tests/Makefile
@@ -0,0 +1,20 @@
+# $FreeBSD$
+
+PACKAGE= tests
+
+ATF_TESTS_SH+= be_create
+
+PROGS= target_prog
+SRCS_target_prog= target_prog.c
+BINDIR_target_prog= ${TESTSDIR}
+
+LIBADD+= zfs
+LIBADD+= nvpair
+LIBADD+= be
+
+CFLAGS+= -I${SRCTOP}/sys/cddl/compat/opensolaris
+CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common
+
+CFLAGS+= -DNEED_SOLARIS_BOOLEAN
+
+.include <bsd.test.mk>
Index: lib/libbe/tests/be_create.sh
===================================================================
--- /dev/null
+++ lib/libbe/tests/be_create.sh
@@ -0,0 +1,162 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+#
+# Copyright (c) 2019 Rob Wing
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD$
+
+# The code for the following tests was copied from the
+# bectl tests found in src/sbin/bectl/tests, modified as needed.
+
+ZPOOL_NAME_FILE=zpool_name
+get_zpool_name()
+{
+ cat $ZPOOL_NAME_FILE
+}
+make_zpool_name()
+{
+ mktemp -u libbe_test_XXXXXX > $ZPOOL_NAME_FILE
+ get_zpool_name
+}
+
+# Establishes a libbe zpool that can be used for some light testing; contains
+# a 'default' BE and not much else.
+libbe_create_setup()
+{
+ zpool=$1
+ disk=$2
+ mnt=$3
+
+ # Sanity check to make sure `make_zpool_name` succeeded
+ atf_check test -n "$zpool"
+
+ kldload -n -q zfs || atf_skip "ZFS module not loaded on the current system"
+ atf_check mkdir -p ${mnt}
+ atf_check truncate -s 1G ${disk}
+ atf_check zpool create -o altroot=${mnt} ${zpool} ${disk}
+ atf_check zfs create -o mountpoint=none ${zpool}/ROOT
+ atf_check zfs create -o mountpoint=/ -o canmount=noauto \
+ ${zpool}/ROOT/default
+ atf_check zfs create -o mountpoint=/usr -o canmount=noauto \
+ ${zpool}/ROOT/default/usr
+ atf_check zfs create -o mountpoint=/usr/obj -o canmount=noauto \
+ ${zpool}/ROOT/default/usr/obj
+}
+
+libbe_cleanup()
+{
+ zpool=$1
+ cwd=$(atf_get_srcdir)
+
+ if [ -z "$zpool" ]; then
+ echo "Skipping cleanup; zpool not set up"
+ elif zpool get health ${zpool} >/dev/null 2>&1; then
+ zpool destroy -f ${zpool}
+ fi
+
+ if [ -f "${cwd}/disk.img" ]; then
+ rm ${cwd}/disk.img
+ fi
+}
+
+atf_test_case libbe_create cleanup
+libbe_create_head()
+{
+ atf_set "descr" "check _be_create from libbe"
+ atf_set "require.user" root
+}
+libbe_create_body()
+{
+ cwd=$(atf_get_srcdir)
+ zpool=$(make_zpool_name)
+ disk=${cwd}/disk.img
+ mount=${cwd}/mnt
+ prog=${cwd}/./target_prog
+
+ # preliminary setup/checks
+ atf_require_prog $prog
+ libbe_create_setup ${zpool} ${disk} ${mount}
+
+ # a recursive and non-recursive snapshot to test against
+ atf_check zfs snapshot ${zpool}/ROOT/default@non-recursive
+ atf_check zfs snapshot -r ${zpool}/ROOT/default@recursive
+
+ # create a dataset after snapshots were taken
+ atf_check zfs create -o mountpoint=/usr/src -o canmount=noauto \
+ ${zpool}/ROOT/default/usr/src
+
+ # create a non-recursive boot environment named 'nonrecursive'
+ atf_check -o empty -s exit:0 $prog "${zpool}/ROOT" \
+ nonrecursive \
+ "${zpool}/ROOT/default@non-recursive" \
+ false
+ # the dataset should exist
+ atf_check -o not-empty -s exit:0 \
+ zfs list "${zpool}/ROOT/nonrecursive"
+ # the child dataset should not exist.
+ atf_check -e not-empty -s not-exit:0 \
+ zfs list "${zpool}/ROOT/nonrecursive/usr"
+
+ # create a recursive boot environment named 'recursive'.
+ atf_check -o empty -s exit:0 $prog "${zpool}/ROOT" \
+ recursive \
+ "${zpool}/ROOT/default@recursive" \
+ true
+ # the dataset should exist
+ atf_check -o not-empty -s exit:0 \
+ zfs list "${zpool}/ROOT/recursive"
+ # the child dataset should exist
+ atf_check -o not-empty -s exit:0 \
+ zfs list "${zpool}/ROOT/recursive/usr"
+ # the child dataset should exist
+ atf_check -o not-empty -s exit:0 \
+ zfs list "${zpool}/ROOT/recursive/usr/obj"
+ # the child dataset should not exist.
+ atf_check -e not-empty -s not-exit:0 \
+ zfs list "${zpool}/ROOT/recursive/usr/src"
+
+ # create a recursive boot environment named 'relative-snap'.
+ # This test is to ensure that a relative snapshot label can be used,
+ # (i.e. the format: 'bootenvironment@snapshot')
+ atf_check -o empty -s exit:0 $prog "${zpool}/ROOT" \
+ relative-snap \
+ default@recursive \
+ true
+ # the dataset should exist
+ atf_check -o not-empty -s exit:0 \
+ zfs list "${zpool}/ROOT/relative-snap"
+ # the child dataset should exist
+ atf_check -o not-empty -s exit:0 \
+ zfs list "${zpool}/ROOT/relative-snap/usr"
+}
+
+libbe_create_cleanup()
+{
+ libbe_cleanup $(get_zpool_name)
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case libbe_create
+}
Index: lib/libbe/tests/target_prog.c
===================================================================
--- lib/libbe/tests/target_prog.c
+++ lib/libbe/tests/target_prog.c
@@ -1,7 +1,7 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
- * Copyright (c) 2017 Kyle J. Kneitinger <kyle@kneit.in>
+ * Copyright (c) 2019 Rob Wing
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -24,53 +24,39 @@
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
- *
- * $FreeBSD$
*/
-#ifndef _LIBBE_IMPL_H
-#define _LIBBE_IMPL_H
-
-#include <libzfs.h>
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
-#include "be.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdbool.h>
-struct libbe_handle {
- char root[BE_MAXPATHLEN];
- char rootfs[BE_MAXPATHLEN];
- char bootfs[BE_MAXPATHLEN];
- size_t altroot_len;
- zpool_handle_t *active_phandle;
- libzfs_handle_t *lzh;
- be_error_t error;
- bool print_on_err;
-};
+#include <be.h>
-struct libbe_deep_clone {
- libbe_handle_t *lbh;
- const char *bename;
- const char *snapname;
- const char *be_root;
-};
+/*
+ * argv[1] = root boot environment (e.g. zroot/ROOT),
+ * argv[2] = name of boot environment to create
+ * argv[3] = snapshot to create boot environment from
+ * argv[4] = create boot environment recursively (true/false)
+ */
+int main(int argc, char *argv[]) {
-struct libbe_dccb {
- libbe_handle_t *lbh;
- zfs_handle_t *zhp;
- nvlist_t *props;
-};
+ libbe_handle_t *lbh;
-typedef struct prop_data {
- nvlist_t *list;
- libbe_handle_t *lbh;
- bool single_object; /* list will contain props directly */
-} prop_data_t;
+ if (argc != 5)
+ return -1;
-int prop_list_builder_cb(zfs_handle_t *, void *);
-int be_proplist_update(prop_data_t *);
+ if ((lbh = libbe_init(argv[1])) == NULL)
+ return -1;
-char *be_mountpoint_augmented(libbe_handle_t *lbh, char *mountpoint);
+ libbe_print_on_error(lbh, true);
-/* Clobbers any previous errors */
-int set_error(libbe_handle_t *, be_error_t);
+ /* create recursive boot environment */
+ if (strcmp(argv[4], "true") == 0)
+ return (be_create_from_existing_snap(lbh, argv[2], argv[3]));
-#endif /* _LIBBE_IMPL_H */
+ /* create non-recursive boot environment */
+ return (be_create_shallow(lbh, argv[2], argv[3]));
+}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sun, Feb 8, 6:44 PM (11 h, 45 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
28485661
Default Alt Text
D18564.id54713.diff (18 KB)
Attached To
Mode
D18564: allow creation of non-recursive boot environments (libbe)
Attached
Detach File
Event Timeline
Log In to Comment