bectl(3)/libbe(3): Allow BE root to be specified

Add an undocumented -r option preceding the bectl subcommand to specify a BE
root to operate out of. This will remain undocumented for now, as some
caveats apply:

- BEs cannot be activated in the pool that doesn't contain the rootfs
- bectl create cannot work out of the box without the -e option right now,
  since it defaults to the rootfs and cross-pool cloning doesn't work like
  that (IIRC)

Plumb the BE root through to libbe(3) so that some things -can- be done to
it, e.g.

bectl -r tank/ROOT create -e default upgrade
bectl -r tank/ROOT mount upgrade /mnt

this aides in some upgrade setups where rootfs is not necessarily ZFS, and
also makes it easier/possible to regression-test bectl when combined with a
file-backed zpool.

MFC after:	3 days
Differential Revision:	https://reviews.freebsd.org/D18029
This commit is contained in:
kevans 2018-11-19 02:12:08 +00:00
parent 59c7844347
commit 259052139d
5 changed files with 50 additions and 20 deletions

View File

@ -74,7 +74,7 @@ be_locate_rootfs(libbe_handle_t *lbh)
* dataset, for example, zroot/ROOT.
*/
libbe_handle_t *
libbe_init(void)
libbe_init(const char *root)
{
libbe_handle_t *lbh;
char *poolname, *pos;
@ -89,16 +89,21 @@ libbe_init(void)
if ((lbh->lzh = libzfs_init()) == NULL)
goto err;
/* Grab rootfs, we'll work backwards from there */
/*
* Grab rootfs, we'll work backwards from there if an optional BE root
* has not been passed in.
*/
if (be_locate_rootfs(lbh) != 0)
goto err;
/* Strip off the final slash from the 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';
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));
if ((pos = strchr(lbh->root, '/')) == NULL)
goto err;

View File

@ -63,7 +63,7 @@ typedef enum be_error {
/* 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 */

View File

@ -42,7 +42,10 @@ const char *
be_active_name(libbe_handle_t *lbh)
{
return (strrchr(lbh->rootfs, '/') + sizeof(char));
if (*lbh->rootfs != '\0')
return (strrchr(lbh->rootfs, '/') + sizeof(char));
else
return (lbh->rootfs);
}
@ -63,7 +66,10 @@ const char *
be_nextboot_name(libbe_handle_t *lbh)
{
return (strrchr(lbh->bootfs, '/') + sizeof(char));
if (*lbh->bootfs != '\0')
return (strrchr(lbh->bootfs, '/') + sizeof(char));
else
return (lbh->bootfs);
}

View File

@ -28,7 +28,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd August 31, 2018
.Dd November 17, 2018
.Dt LIBBE 3
.Os
.Sh NAME
@ -39,7 +39,7 @@
.Sh SYNOPSIS
.In be.h
.Ft "libbe_handle_t *hdl" Ns
.Fn libbe_init void
.Fn libbe_init "const char *be_root"
.Pp
.Ft void
.Fn libbe_close "libbe_handle_t *hdl"
@ -157,13 +157,16 @@ errno otherwise as described in
.Pp
The
.Fn libbe_init
function initializes
function takes an optional BE root and initializes
.Nm ,
returning a
.Vt "libbe_handle_t *"
on success, or
.Dv NULL
on error.
If a BE root is supplied,
.Nm
will only operate out of that pool and BE root.
An error may occur if:
.Bl -column
.It /boot and / are not on the same filesystem and device,
@ -184,11 +187,15 @@ invalidating the handle in the process.
.Pp
The
.Fn be_active_name
function returns the name of the currently booted boot environment,
function returns the name of the currently booted boot environment.
This boot environment may not belong to the same BE root as the root libbe
is operating on!
.Pp
The
.Fn be_active_path
function returns the full path of the currently booted boot environment.
This boot environment may not belong to the same BE root as the root libbe
is operating on!
.Pp
The
.Fn be_nextboot_name

View File

@ -489,12 +489,25 @@ int
main(int argc, char *argv[])
{
const char *command;
char *root;
int command_index, rc;
root = NULL;
if (argc < 2)
return (usage(false));
command = argv[1];
if (strcmp(argv[1], "-r") == 0) {
if (argc < 4)
return (usage(false));
root = strdup(argv[2]);
command = argv[3];
argc -= 3;
argv += 3;
} else {
command = argv[1];
argc -= 1;
argv += 1;
}
/* Handle command aliases */
if (strcmp(command, "umount") == 0)
@ -512,13 +525,12 @@ main(int argc, char *argv[])
}
if ((be = libbe_init()) == NULL)
if ((be = libbe_init(root)) == NULL)
return (-1);
libbe_print_on_error(be, true);
/* XXX TODO: can be simplified if offset by 2 instead of one */
rc = command_map[command_index].fn(argc-1, argv+1);
rc = command_map[command_index].fn(argc, argv);
libbe_close(be);
return (rc);