git: 5086b6ec32d5 - main - libbe: handle destroying/renaming temporary/bootonce boot environments

From: R. Christian McDonald <rcm_at_FreeBSD.org>
Date: Mon, 29 Jan 2024 15:08:25 UTC
The branch main has been updated by rcm:

URL: https://cgit.FreeBSD.org/src/commit/?id=5086b6ec32d5c82c3d0894eb7bd74a9b81480644

commit 5086b6ec32d5c82c3d0894eb7bd74a9b81480644
Author:     R. Christian McDonald <rcm@FreeBSD.org>
AuthorDate: 2024-01-26 16:39:38 +0000
Commit:     R. Christian McDonald <rcm@FreeBSD.org>
CommitDate: 2024-01-29 15:07:49 +0000

    libbe: handle destroying/renaming temporary/bootonce boot environments
    
    When a temporary/bootonce boot environment is renamed, we need to also
    update the bootenv nvlist on-disk to reflect the new name. Additionally,
    when a temporary/bootonce boot environment is destroyed, we also need to
    clear out the on-disk state.
    
    Reviewed by:    kevans
    Approved by:    kp
    MFC after:      1 week
    Sponsored by:   Rubicon Communications, LLC ("Netgate")
    Differential Revision:  https://reviews.freebsd.org/D43591
---
 lib/libbe/be.c                 |  23 ++++++++--
 lib/libbe/be_impl.h            |   1 +
 lib/libbe/be_info.c            |   7 +--
 sbin/bectl/tests/bectl_test.sh | 102 ++++++++++++++++++++++++++++++++++++-----
 4 files changed, 113 insertions(+), 20 deletions(-)

diff --git a/lib/libbe/be.c b/lib/libbe/be.c
index c74ec9b0f3dc..e61cfc9d68ae 100644
--- a/lib/libbe/be.c
+++ b/lib/libbe/be.c
@@ -175,6 +175,9 @@ libbe_init(const char *root)
 	    strcmp(altroot, "-") != 0)
 		lbh->altroot_len = strlen(altroot);
 
+	(void) lzbe_get_boot_device(zpool_get_name(lbh->active_phandle),
+	    &lbh->bootonce);
+
 	return (lbh);
 err:
 	if (lbh != NULL) {
@@ -199,6 +202,8 @@ libbe_close(libbe_handle_t *lbh)
 	if (lbh->active_phandle != NULL)
 		zpool_close(lbh->active_phandle);
 	libzfs_fini(lbh->lzh);
+
+	free(lbh->bootonce);
 	free(lbh);
 }
 
@@ -443,6 +448,12 @@ be_destroy_internal(libbe_handle_t *lbh, const char *name, int options,
 				return (set_error(lbh, BE_ERR_DESTROYMNT));
 			}
 		}
+
+		/* Handle destroying bootonce */
+		if (lbh->bootonce != NULL &&
+		    strcmp(path, lbh->bootonce) == 0)
+			(void) lzbe_set_boot_device(
+			    zpool_get_name(lbh->active_phandle), lzbe_add, NULL);
 	} else {
 		/*
 		 * If we're initially destroying a snapshot, origin options do
@@ -1021,11 +1032,17 @@ be_rename(libbe_handle_t *lbh, const char *old, const char *new)
 		.nounmount = 1,
 	};
 	err = zfs_rename(zfs_hdl, full_new, flags);
+	if (err != 0)
+		goto error;
+
+	/* handle renaming bootonce */
+	if (lbh->bootonce != NULL &&
+	    strcmp(full_old, lbh->bootonce) == 0)
+		err = be_activate(lbh, new, true);
 
+error:
 	zfs_close(zfs_hdl);
-	if (err != 0)
-		return (set_error(lbh, BE_ERR_UNKNOWN));
-	return (0);
+	return (set_error(lbh, err));
 }
 
 
diff --git a/lib/libbe/be_impl.h b/lib/libbe/be_impl.h
index d5fd26c4f072..0327f8abaa0a 100644
--- a/lib/libbe/be_impl.h
+++ b/lib/libbe/be_impl.h
@@ -36,6 +36,7 @@ struct libbe_handle {
 	char root[BE_MAXPATHLEN];
 	char rootfs[BE_MAXPATHLEN];
 	char bootfs[BE_MAXPATHLEN];
+	char *bootonce;
 	size_t altroot_len;
 	zpool_handle_t *active_phandle;
 	libzfs_handle_t *lzh;
diff --git a/lib/libbe/be_info.c b/lib/libbe/be_info.c
index 88a9b17bf421..509b56cb8cf5 100644
--- a/lib/libbe/be_info.c
+++ b/lib/libbe/be_info.c
@@ -181,8 +181,8 @@ prop_list_builder_cb(zfs_handle_t *zfs_hdl, void *data_p)
 	dataset = zfs_get_name(zfs_hdl);
 	nvlist_add_string(props, "dataset", dataset);
 
-	if (data->bootonce != NULL &&
-	    strcmp(dataset, data->bootonce) == 0)
+	if (data->lbh->bootonce != NULL &&
+	    strcmp(dataset, data->lbh->bootonce) == 0)
 		nvlist_add_boolean_value(props, "bootonce", true);
 
 	name = strrchr(dataset, '/') + 1;
@@ -252,9 +252,6 @@ be_proplist_update(prop_data_t *data)
 	    ZFS_TYPE_FILESYSTEM)) == NULL)
 		return (BE_ERR_ZFSOPEN);
 
-	(void) lzbe_get_boot_device(zpool_get_name(data->lbh->active_phandle),
-	    &data->bootonce);
-
 	/* XXX TODO: some error checking here */
 	zfs_iter_filesystems(root_hdl, prop_list_builder_cb, data);
 
diff --git a/sbin/bectl/tests/bectl_test.sh b/sbin/bectl/tests/bectl_test.sh
index 0f167829cf46..00c71e11de09 100755
--- a/sbin/bectl/tests/bectl_test.sh
+++ b/sbin/bectl/tests/bectl_test.sh
@@ -93,7 +93,6 @@ bectl_cleanup()
 atf_test_case bectl_create cleanup
 bectl_create_head()
 {
-
 	atf_set "descr" "Check the various forms of bectl create"
 	atf_set "require.user" root
 }
@@ -157,7 +156,6 @@ bectl_create_cleanup()
 atf_test_case bectl_destroy cleanup
 bectl_destroy_head()
 {
-
 	atf_set "descr" "Check bectl destroy"
 	atf_set "require.user" root
 }
@@ -240,14 +238,12 @@ bectl_destroy_body()
 }
 bectl_destroy_cleanup()
 {
-
 	bectl_cleanup $(get_zpool_name)
 }
 
 atf_test_case bectl_export_import cleanup
 bectl_export_import_head()
 {
-
 	atf_set "descr" "Check bectl export and import"
 	atf_set "require.user" root
 }
@@ -278,14 +274,12 @@ bectl_export_import_body()
 }
 bectl_export_import_cleanup()
 {
-
 	bectl_cleanup $(get_zpool_name)
 }
 
 atf_test_case bectl_list cleanup
 bectl_list_head()
 {
-
 	atf_set "descr" "Check bectl list"
 	atf_set "require.user" root
 }
@@ -323,14 +317,12 @@ bectl_list_body()
 }
 bectl_list_cleanup()
 {
-
 	bectl_cleanup $(get_zpool_name)
 }
 
 atf_test_case bectl_mount cleanup
 bectl_mount_head()
 {
-
 	atf_set "descr" "Check bectl mount/unmount"
 	atf_set "require.user" root
 }
@@ -367,14 +359,12 @@ bectl_mount_body()
 }
 bectl_mount_cleanup()
 {
-
 	bectl_cleanup $(get_zpool_name)
 }
 
 atf_test_case bectl_rename cleanup
 bectl_rename_head()
 {
-
 	atf_set "descr" "Check bectl rename"
 	atf_set "require.user" root
 }
@@ -403,14 +393,12 @@ bectl_rename_body()
 }
 bectl_rename_cleanup()
 {
-
 	bectl_cleanup $(get_zpool_name)
 }
 
 atf_test_case bectl_jail cleanup
 bectl_jail_head()
 {
-
 	atf_set "descr" "Check bectl rename"
 	atf_set "require.user" root
 	atf_set "require.progs" jail
@@ -577,6 +565,94 @@ bectl_promotion_cleanup()
 	bectl_cleanup $(get_zpool_name)
 }
 
+atf_test_case bectl_destroy_bootonce cleanup
+bectl_destroy_bootonce_head()
+{
+	atf_set "descr" "Check bectl destroy (bootonce)"
+	atf_set "require.user" root
+}
+bectl_destroy_bootonce_body()
+{
+	if [ "$(atf_config_get ci false)" = "true" ] && \
+		[ "$(uname -p)" = "i386" ]; then
+		atf_skip "https://bugs.freebsd.org/249055"
+	fi
+
+	if [ "$(atf_config_get ci false)" = "true" ] && \
+		[ "$(uname -p)" = "armv7" ]; then
+		atf_skip "https://bugs.freebsd.org/249229"
+	fi
+
+	cwd=$(realpath .)
+	zpool=$(make_zpool_name)
+	disk=${cwd}/disk.img
+	mount=${cwd}/mnt
+	root=${mount}/root
+
+	be=default2
+
+	bectl_create_setup ${zpool} ${disk} ${mount}
+	atf_check -s exit:0 -o empty bectl -r ${zpool}/ROOT create -e default ${be}
+
+	# Create boot environment and bootonce activate it
+	atf_check -s exit:0 -o ignore bectl -r ${zpool}/ROOT activate -t ${be}
+	atf_check -s exit:0 -o inline:"zfs:${zpool}/ROOT/${be}:\n" zfsbootcfg -z ${zpool}
+
+	# Destroy it
+	atf_check -s exit:0 -o ignore bectl -r ${zpool}/ROOT destroy ${be}
+
+	# Should be empty
+	atf_check -s exit:0 -o empty zfsbootcfg -z ${zpool}
+}
+bectl_destroy_bootonce_cleanup()
+{
+	bectl_cleanup $(get_zpool_name)
+}
+
+atf_test_case bectl_rename_bootonce cleanup
+bectl_rename_bootonce_head()
+{
+	atf_set "descr" "Check bectl destroy (bootonce)"
+	atf_set "require.user" root
+}
+bectl_rename_bootonce_body()
+{
+	if [ "$(atf_config_get ci false)" = "true" ] && \
+		[ "$(uname -p)" = "i386" ]; then
+		atf_skip "https://bugs.freebsd.org/249055"
+	fi
+
+	if [ "$(atf_config_get ci false)" = "true" ] && \
+		[ "$(uname -p)" = "armv7" ]; then
+		atf_skip "https://bugs.freebsd.org/249229"
+	fi
+
+	cwd=$(realpath .)
+	zpool=$(make_zpool_name)
+	disk=${cwd}/disk.img
+	mount=${cwd}/mnt
+	root=${mount}/root
+
+	be=default2
+
+	bectl_create_setup ${zpool} ${disk} ${mount}
+	atf_check -s exit:0 -o empty bectl -r ${zpool}/ROOT create -e default ${be}
+
+	# Create boot environment and bootonce activate it
+	atf_check -s exit:0 -o ignore bectl -r ${zpool}/ROOT activate -t ${be}
+	atf_check -s exit:0 -o inline:"zfs:${zpool}/ROOT/${be}:\n" zfsbootcfg -z ${zpool}
+
+	# Rename it
+	atf_check -s exit:0 -o ignore bectl -r ${zpool}/ROOT rename ${be} ${be}_renamed
+
+	# Should be renamed
+	atf_check -s exit:0 -o inline:"zfs:${zpool}/ROOT/${be}_renamed:\n" zfsbootcfg -z ${zpool}
+}
+bectl_rename_bootonce_cleanup()
+{
+	bectl_cleanup $(get_zpool_name)
+}
+
 atf_init_test_cases()
 {
 	atf_add_test_case bectl_create
@@ -587,4 +663,6 @@ atf_init_test_cases()
 	atf_add_test_case bectl_rename
 	atf_add_test_case bectl_jail
 	atf_add_test_case bectl_promotion
+	atf_add_test_case bectl_destroy_bootonce
+	atf_add_test_case bectl_rename_bootonce
 }