Import libbe(3)/be(1) from socsvn/soc2017/kneitinger/libbe-head

This commit is contained in:
Kyle Evans 2018-07-24 13:17:40 +00:00
parent 2011986f09
commit 28f16a0f19
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/projects/bectl/; revision=336668
13 changed files with 2684 additions and 0 deletions

53
lib/libbe/Makefile Normal file
View File

@ -0,0 +1,53 @@
# $FreeBSD$
#
# Copyright (c) 2010 Hans Petter Selasky. All rights reserved.
#
# 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.
#
PACKAGE= lib${LIB}
LIB= be
SHLIB_MAJOR= 1
SHLIB_MINOR= 0
SRCS= be.c be_access.c be_error.c be_info.c
INCS= be.h
MAN= libbe.3
WARNS?= 1
LIBADD+= zfs
LIBADD+= nvpair
CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzfs/common
CFLAGS+= -I${SRCTOP}/sys/cddl/compat/opensolaris
CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/include
CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/lib/libumem
CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzpool/common
CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/common/zfs
CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common/fs/zfs
CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common
CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/head
LDFLAGS+= -v
CFLAGS+= -DNEED_SOLARIS_BOOLEAN
.include <bsd.lib.mk>

876
lib/libbe/be.c Normal file
View File

@ -0,0 +1,876 @@
/*
* be.c
*
* Copyright (c) 2017 Kyle J. Kneitinger <kyle@kneit.in>
* All rights reserved.
*
* 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 REGENTS 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 REGENTS 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.
*/
#include <sys/stat.h>
#include <sys/types.h>
#include <ctype.h>
#include <kenv.h>
#include <libgen.h>
#include <libzfs_core.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include "be.h"
#include "be_impl.h"
/*
* Initializes the libbe context to operate in the root boot environment
* dataset, for example, zroot/ROOT.
*/
libbe_handle_t *
libbe_init(void)
{
char buf[BE_MAXPATHLEN];
struct stat sb;
dev_t root_dev, boot_dev;
libbe_handle_t *lbh;
char *pos;
// TODO: use errno here??
/* Verify that /boot and / are mounted on the same filesystem */
if (stat("/", &sb) != 0) {
return (NULL);
}
root_dev = sb.st_dev;
if (stat("/boot", &sb) != 0) {
return (NULL);
}
boot_dev = sb.st_dev;
if (root_dev != boot_dev) {
fprintf(stderr, "/ and /boot not on same device, quitting\n");
return (NULL);
}
if ((lbh = calloc(1, sizeof(libbe_handle_t))) == NULL) {
return (NULL);
}
if ((lbh->lzh = libzfs_init()) == NULL) {
free(lbh);
return (NULL);
}
/* Obtain path to active boot environment */
if ((kenv(KENV_GET, "zfs_be_active", lbh->active,
BE_MAXPATHLEN)) == -1) {
libzfs_fini(lbh->lzh);
free(lbh);
return (NULL);
}
/* Remove leading 'zfs:' if present, otherwise use value as-is */
if ((pos = strrchr(lbh->active, ':')) != NULL) {
strncpy(lbh->active, pos + sizeof(char), BE_MAXPATHLEN);
}
/* Obtain path to boot environment root */
if ((kenv(KENV_GET, "zfs_be_root", lbh->root, BE_MAXPATHLEN)) == -1) {
libzfs_fini(lbh->lzh);
free(lbh);
return (NULL);
}
/* Remove leading 'zfs:' if present, otherwise use value as-is */
if ((pos = strrchr(lbh->root, ':')) != NULL) {
strncpy(lbh->root, pos + sizeof(char), BE_MAXPATHLEN);
}
return (lbh);
}
/*
* Free memory allocated by libbe_init()
*/
void
libbe_close(libbe_handle_t *lbh)
{
libzfs_fini(lbh->lzh);
free(lbh);
}
/*
* 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
* TODO: Test destroying a non active but mounted be
*/
int
be_destroy(libbe_handle_t *lbh, char *name, int options)
{
zfs_handle_t *fs;
char path[BE_MAXPATHLEN];
char *p = path;
int mounted;
int force = options & BE_DESTROY_FORCE;
int err = BE_ERR_SUCCESS;
be_root_concat(lbh, name, path);
printf("path: %s\n", path);
if (strchr(name, '@') == NULL) {
if (!zfs_dataset_exists(lbh->lzh, path, ZFS_TYPE_FILESYSTEM)) {
return (set_error(lbh, BE_ERR_NOENT));
}
if (strcmp(path, lbh->active) == 0) {
return (set_error(lbh, BE_ERR_DESTROYACT));
}
fs = zfs_open(lbh->lzh, p, ZFS_TYPE_FILESYSTEM);
} 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);
}
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)) {
if (force) {
zfs_unmount(fs, NULL, 0);
} else {
return (set_error(lbh, BE_ERR_DESTROYMNT));
}
}
// TODO: convert this to use zfs_iter_children first for deep bes
// XXX note: errno 16 (device busy) occurs when chilren are present
if ((err = zfs_destroy(fs, false)) != 0) {
fprintf(stderr, "delete failed errno: %d\n", errno);
}
return (err);
}
int
be_snapshot(libbe_handle_t *lbh, char *source, char *snap_name,
bool recursive, char *result)
{
char buf[BE_MAXPATHLEN];
time_t rawtime;
int len, err;
be_root_concat(lbh, source, buf);
if (!be_exists(lbh, buf)) {
return (BE_ERR_NOENT);
}
if (snap_name != NULL) {
strcat(buf, "@");
strcat(buf, snap_name);
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);
}
}
if (err = zfs_snapshot(lbh->lzh, buf, recursive, NULL) != 0) {
switch (err) {
case EZFS_INVALIDNAME:
return (set_error(lbh, BE_ERR_INVALIDNAME));
default:
// TODO: elaborate return codes
return (set_error(lbh, BE_ERR_UNKNOWN));
}
}
return (BE_ERR_SUCCESS);
}
/*
* Create the boot environment specified by the name parameter
*/
int
be_create(libbe_handle_t *lbh, char *name)
{
int err;
err = be_create_from_existing(lbh, name, (char *)be_active_path(lbh));
return (set_error(lbh, err));
}
static int
be_deep_clone_prop(int prop, void *cb)
{
int err;
struct libbe_dccb *dccb = cb;
zprop_source_t src;
char pval[BE_MAXPATHLEN];
char source[BE_MAXPATHLEN];
/* Skip some properties we don't want to touch */
switch (prop) {
case ZFS_PROP_CANMOUNT:
return (ZPROP_CONT);
break;
}
/* Don't copy readonly properties */
if (zfs_prop_readonly(prop)) {
return (ZPROP_CONT);
}
if ((err = zfs_prop_get(dccb->zhp, prop, (char *)&pval,
sizeof(pval), &src, (char *)&source, sizeof(source), false))) {
/* Just continue if we fail to read a property */
return (ZPROP_CONT);
}
/* Only copy locally defined properties */
if (src != ZPROP_SRC_LOCAL) {
return (ZPROP_CONT);
}
nvlist_add_string(dccb->props, zfs_prop_to_name(prop), (char *)pval);
return (ZPROP_CONT);
}
static int
be_deep_clone(zfs_handle_t *ds, void *data)
{
int err;
char be_path[BE_MAXPATHLEN];
char snap_path[BE_MAXPATHLEN];
char mp[BE_MAXPATHLEN];
const char *dspath;
char *dsname;
zfs_handle_t *snap_hdl;
nvlist_t *props;
struct libbe_deep_clone sdc;
struct libbe_deep_clone *isdc = (struct libbe_deep_clone *)data;
struct libbe_dccb dccb;
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, isdc->snapname);
if (zfs_dataset_exists(isdc->lbh->lzh, be_path, ZFS_TYPE_DATASET)) {
return (set_error(isdc->lbh, BE_ERR_EXISTS));
}
if ((snap_hdl =
zfs_open(isdc->lbh->lzh, snap_path, ZFS_TYPE_SNAPSHOT)) == NULL) {
return (set_error(isdc->lbh, BE_ERR_ZFSOPEN));
}
nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP);
nvlist_add_string(props, "canmount", "noauto");
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)) {
switch (err) {
case EZFS_SUCCESS:
err = BE_ERR_SUCCESS;
break;
default:
err = BE_ERR_ZFSCLONE;
break;
}
}
nvlist_free(props);
zfs_close(snap_hdl);
sdc.lbh = isdc->lbh;
sdc.bename = NULL;
sdc.snapname = isdc->snapname;
sdc.be_root = (char *)&be_path;
err = zfs_iter_filesystems(ds, be_deep_clone, &sdc);
return (err);
}
/*
* Create the boot environment from pre-existing snapshot
*/
int
be_create_from_existing_snap(libbe_handle_t *lbh, char *name, char *snap)
{
int err;
char be_path[BE_MAXPATHLEN];
char snap_path[BE_MAXPATHLEN];
char *parentname, *bename, *snapname;
zfs_handle_t *parent_hdl;
struct libbe_deep_clone sdc;
if (err = be_validate_name(lbh, name)) {
return (set_error(lbh, err));
}
if (err = be_root_concat(lbh, snap, snap_path)) {
return (set_error(lbh, err));
}
if (err = be_validate_snap(lbh, snap_path)) {
return (set_error(lbh, err));
}
if (err = be_root_concat(lbh, name, be_path)) {
return (set_error(lbh, err));
}
if ((bename = strrchr(name, '/')) == NULL) {
bename = name;
} else {
bename++;
}
if ((parentname = strdup(snap_path)) == NULL) {
err = BE_ERR_UNKNOWN;
return (set_error(lbh, err));
}
snapname = strchr(parentname, '@');
if (snapname == NULL) {
err = BE_ERR_UNKNOWN;
return (set_error(lbh, err));
}
*snapname = '\0';
snapname++;
sdc.lbh = lbh;
sdc.bename = bename;
sdc.snapname = snapname;
sdc.be_root = lbh->root;
parent_hdl = zfs_open(lbh->lzh, parentname, ZFS_TYPE_DATASET);
err = be_deep_clone(parent_hdl, &sdc);
return (set_error(lbh, err));
}
/*
* Create a boot environment from an existing boot environment
*/
int
be_create_from_existing(libbe_handle_t *lbh, char *name, char *old)
{
int err;
char buf[BE_MAXPATHLEN];
if ((err = be_snapshot(lbh, old, NULL, true, (char *)&buf))) {
return (set_error(lbh, err));
}
err = be_create_from_existing_snap(lbh, name, (char *)buf);
return (set_error(lbh, err));
}
/*
* Verifies that a snapshot has a valid name, exists, and has a mountpoint of
* '/'. Returns BE_ERR_SUCCESS (0), upon success, or the relevant BE_ERR_* upon
* failure. Does not set the internal library error state.
*/
int
be_validate_snap(libbe_handle_t *lbh, char *snap_name)
{
zfs_handle_t *zfs_hdl;
char buf[BE_MAXPATHLEN];
char *delim_pos;
char *mountpoint;
int err = BE_ERR_SUCCESS;
if (strlen(snap_name) >= BE_MAXPATHLEN) {
return (BE_ERR_PATHLEN);
}
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)) {
err = BE_ERR_INVORIGIN;
}
if ((err != 0) && (strncmp(buf, "/", BE_MAXPATHLEN) != 0)) {
err = BE_ERR_INVORIGIN;
}
zfs_close(zfs_hdl);
return (err);
}
/*
* Idempotently appends the name argument to the root boot environment path
* and copies the resulting string into the result buffer (which is assumed
* to be at least BE_MAXPATHLEN characters long. Returns BE_ERR_SUCCESS upon
* success, BE_ERR_PATHLEN if the resulting path is longer than BE_MAXPATHLEN,
* or BE_ERR_INVALIDNAME if the name is a path that does not begin with
* zfs_be_root. Does not set internal library error state.
*/
int
be_root_concat(libbe_handle_t *lbh, char *name, char *result)
{
size_t name_len, root_len;
name_len = strlen(name);
root_len = strlen(lbh->root);
/* Act idempotently; return be name if it is already a full path */
if (strrchr(name, '/') != NULL) {
if (strstr(name, lbh->root) != name) {
return (BE_ERR_INVALIDNAME);
}
if (name_len >= BE_MAXPATHLEN) {
return (BE_ERR_PATHLEN);
}
strncpy(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,
name);
return (BE_ERR_SUCCESS);
}
return (BE_ERR_PATHLEN);
}
/*
* 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.
* Does not set internal library error state.
*/
int
be_validate_name(libbe_handle_t *lbh, char *name)
{
for (int i = 0; *name; i++) {
char c = *(name++);
if (isalnum(c) || (c == '-') || (c == '_') ||
(c == '.')) {
continue;
}
return (BE_ERR_INVALIDNAME);
}
return (BE_ERR_SUCCESS);
}
/*
* usage
*/
int
be_rename(libbe_handle_t *lbh, char *old, char *new)
{
char full_old[BE_MAXPATHLEN];
char full_new[BE_MAXPATHLEN];
zfs_handle_t *zfs_hdl;
int err;
if (err = be_root_concat(lbh, old, full_old)) {
return (set_error(lbh, err));
}
if (err = be_root_concat(lbh, new, full_new)) {
return (set_error(lbh, err));
}
if (be_validate_name(lbh, new)) {
return (BE_ERR_UNKNOWN);
// TODO set and return correct error
}
// check if old is active be
if (strcmp(full_new, be_active_path(lbh)) == 0) {
return (BE_ERR_UNKNOWN);
// TODO set and return correct error
}
if (!zfs_dataset_exists(lbh->lzh, full_old, ZFS_TYPE_DATASET)) {
return (BE_ERR_UNKNOWN);
// TODO set and return correct error
}
if (zfs_dataset_exists(lbh->lzh, full_new, ZFS_TYPE_DATASET)) {
return (BE_ERR_UNKNOWN);
// TODO set and return correct error
}
// TODO: what about mounted bes?
// - if mounted error out unless a force flag is set?
if ((zfs_hdl = zfs_open(lbh->lzh, full_old,
ZFS_TYPE_FILESYSTEM)) == NULL) {
return (BE_ERR_UNKNOWN);
// TODO set and return correct error
}
// recurse, nounmount, forceunmount
struct renameflags flags = { 0, 0, 0 };
// TODO: error log on this call
err = zfs_rename(zfs_hdl, NULL, full_new, flags);
zfs_close(zfs_hdl);
return (set_error(lbh, err));
}
int
be_export(libbe_handle_t *lbh, char *bootenv, int fd)
{
char snap_name[BE_MAXPATHLEN];
char buf[BE_MAXPATHLEN];
zfs_handle_t *zfs;
int err;
if (err = be_snapshot(lbh, bootenv, NULL, true, snap_name)) {
// TODO error handle
return (-1);
}
be_root_concat(lbh, snap_name, buf);
if ((zfs = zfs_open(lbh->lzh, buf, ZFS_TYPE_DATASET)) == NULL) {
return (BE_ERR_ZFSOPEN);
}
err = zfs_send_one(zfs, NULL, fd, 0);
return (err);
}
int
be_import(libbe_handle_t *lbh, char *bootenv, int fd)
{
char buf[BE_MAXPATHLEN];
time_t rawtime;
nvlist_t *props;
zfs_handle_t *zfs;
int err, len;
// TODO: this is a very likely name for someone to already have used
if (err = be_root_concat(lbh, "be_import_temp", buf)) {
// TODO error handle
return (-1);
}
time(&rawtime);
len = strlen(buf);
strftime(buf + len, BE_MAXPATHLEN - len,
"@%F-%T", localtime(&rawtime));
// lzc_receive(SNAPNAME, PROPS, ORIGIN, FORCE, fd)) {
if (err = lzc_receive(buf, NULL, NULL, false, fd)) {
/* TODO: go through libzfs_core's recv_impl and find returned
* errors and set appropriate BE_ERR
* edit: errors are not in libzfs_core, my assumption is
* that they use libzfs errors
* note: 17 is err for dataset already existing */
return (err);
}
if ((zfs = zfs_open(lbh->lzh, buf, ZFS_TYPE_SNAPSHOT)) == NULL) {
// TODO correct error
return (-1);
}
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_clone(zfs, buf, props);
zfs_close(zfs);
nvlist_free(props);
// TODO: recursively delete be_import_temp dataset
return (err);
}
int
be_add_child(libbe_handle_t *lbh, char *child_path, bool cp_if_exists)
{
char active[BE_MAXPATHLEN];
char buf[BE_MAXPATHLEN];
nvlist_t *props;
zfs_handle_t *zfs;
struct stat sb;
int err;
/* Require absolute paths */
if (*child_path != '/') {
/* TODO: create appropriate error */
return (-1);
}
strncpy(active, be_active_path(lbh), BE_MAXPATHLEN);
strcpy(buf, active);
/* Create non-mountable parent dataset(s) */
char *s = child_path;
for (char *p; (p = strchr(s+1, '/')) != NULL; s = p) {
size_t len = p - s;
strncat(buf, s, len);
nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP);
nvlist_add_string(props, "canmount", "off");
nvlist_add_string(props, "mountpoint", "none");
zfs_create(lbh->lzh, buf, ZFS_TYPE_DATASET, props);
nvlist_free(props);
}
/* Path does not exist as a descendent of / yet */
int pos = strlen(active);
/* TODO: Verify that resulting str is less than BE_MAXPATHLEN */
strncpy(&active[pos], child_path, BE_MAXPATHLEN-pos);
if (stat(child_path, &sb) != 0) {
/* Verify that error is ENOENT */
if (errno != 2) {
/* TODO: create appropriate error */
return (-1);
}
nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP);
nvlist_add_string(props, "canmount", "noauto");
nvlist_add_string(props, "mountpoint", child_path);
// create
if (err =
zfs_create(lbh->lzh, active, ZFS_TYPE_DATASET, props)) {
/* TODO handle error */
return (-1);
}
nvlist_free(props);
if ((zfs =
zfs_open(lbh->lzh, active, ZFS_TYPE_DATASET)) == NULL) {
/* TODO handle error */
return (-1);
}
// set props
if (err = zfs_prop_set(zfs, "canmount", "noauto")) {
/* TODO handle error */
return (-1);
}
} else if (cp_if_exists) {
/* Path is already a descendent of / and should be copied */
// TODO
/*
* Establish if the existing path is a zfs dataset or just
* the subdirectory of one
*/
// TODO: use mktemp
long int snap_name = random();
snprintf(buf, BE_MAXPATHLEN, "%s@%ld", child_path, snap_name);
if (err = zfs_snapshot(lbh->lzh, buf, false, NULL)) {
// TODO correct error
return (-1);
}
// clone
if ((zfs =
zfs_open(lbh->lzh, buf, ZFS_TYPE_SNAPSHOT)) == NULL) {
// TODO correct error
return (-1);
}
if (err = zfs_clone(zfs, active, NULL)) {
// TODO correct error
return (-1);
}
// set props
} else {
/* TODO: error code for exists, but not cp? */
return (-1);
}
return (BE_ERR_SUCCESS);
}
int
be_activate(libbe_handle_t *lbh, char *bootenv, bool temporary)
{
char be_path[BE_MAXPATHLEN];
char buf[BE_MAXPATHLEN];
zpool_handle_t *zph;
uint64_t pool_guid;
uint64_t vdev_guid;
int zfs_fd;
int len;
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 (temporary) {
// TODO: give proper attribution to author(s) of zfsbootcfg
// for this snippet
if (kenv(KENV_GET, "vfs.zfs.boot.primary_pool", buf,
sizeof(buf)) <= 0) {
return (1);
}
pool_guid = strtoumax(buf, NULL, 10);
if (pool_guid == 0) {
return (1);
}
if (kenv(KENV_GET, "vfs.zfs.boot.primary_vdev", buf,
sizeof(buf)) <= 0) {
return (1);
}
vdev_guid = strtoumax(buf, NULL, 10);
if (vdev_guid == 0) {
return (1);
}
/* Expected format according to zfsbootcfg(8) man */
strcpy(buf, "zfs:");
strcat(buf, be_path);
strcat(buf, ":");
if (zpool_nextboot(lbh->lzh, pool_guid, vdev_guid, buf) != 0) {
perror("ZFS_IOC_NEXTBOOT failed");
return (1);
}
return (BE_ERR_SUCCESS);
} else {
/* Obtain bootenv zpool */
strncpy(buf, be_path, BE_MAXPATHLEN);
*(strchr(buf, '/')) = '\0';
if ((zph = zpool_open(lbh->lzh, buf)) == NULL) {
// TODO: create error for this
return (-1);
}
printf("asdf\n");
err = zpool_set_prop(zph, "bootfs", be_path);
zpool_close(zph);
switch (err) {
case 0:
return (BE_ERR_SUCCESS);
default:
// TODO correct errors
return (-1);
}
}
}

113
lib/libbe/be.h Normal file
View File

@ -0,0 +1,113 @@
/*
* be.h
*
* Copyright (c) 2017 Kyle J. Kneitinger <kyle@kneit.in>
* All rights reserved.
*
* 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 REGENTS 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 REGENTS 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.
*/
#ifndef _LIBBE_H
#define _LIBBE_H
#include <stdbool.h>
#define BE_MAXPATHLEN 512
typedef struct libbe_handle libbe_handle_t;
typedef enum be_error {
BE_ERR_SUCCESS = 0, /* No error */
BE_ERR_INVALIDNAME, /* invalid boot env name */
BE_ERR_EXISTS, /* boot env name already taken */
BE_ERR_NOENT, /* boot env doesn't exist */
BE_ERR_PERMS, /* insufficient permissions */
BE_ERR_DESTROYACT, /* cannot destroy active boot env */
BE_ERR_DESTROYMNT, /* destroying a mounted be requires force */
BE_ERR_PATHLEN, /* provided name exceeds maximum length limit */
BE_ERR_INVORIGIN, /* snapshot origin's 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 */
BE_ERR_ZFSOPEN, /* calling zfs_open() failed */
BE_ERR_ZFSCLONE, /* error when calling zfs_clone to create be */
BE_ERR_UNKNOWN, /* unknown error */
} be_error_t;
/* Library handling functions: be.c */
libbe_handle_t *libbe_init(void);
void libbe_close(libbe_handle_t *);
/* Bootenv information functions: be_info.c */
const char *be_active_name(libbe_handle_t *);
const char *be_active_path(libbe_handle_t *);
const char *be_root_path(libbe_handle_t *);
/* nvlist_t *be_get_bootenv_props(libbe_handle_t *); */
int be_activate(libbe_handle_t *, char *, bool);
/* Bootenv creation functions */
int be_create(libbe_handle_t *, char *);
int be_create_from_existing(libbe_handle_t *, char *, char *);
int be_create_from_existing_snap(libbe_handle_t *, char *, char *);
int be_snapshot(libbe_handle_t *, char *, char *, bool, char *);
/* Bootenv manipulation functions */
int be_rename(libbe_handle_t *, char *, char *);
/* Bootenv removal functions */
typedef enum {
BE_DESTROY_FORCE = 1 << 0,
} be_destroy_opt_t;
int be_destroy(libbe_handle_t *, char *, int);
/* Bootenv mounting functions: be_access.c */
typedef enum {
BE_MNT_FORCE = 1 << 0,
BE_MNT_DEEP = 1 << 1,
} be_mount_opt_t;
int be_mount(libbe_handle_t *, char *, char *, int, char *);
int be_unmount(libbe_handle_t *, char *, int);
/* Error related functions: be_error.c */
int libbe_errno(libbe_handle_t *);
const char *libbe_error_description(libbe_handle_t *);
void libbe_print_on_error(libbe_handle_t *, bool);
/* Utility Functions */
int be_root_concat(libbe_handle_t *, char *, char *);
int be_validate_name(libbe_handle_t *, char *);
int be_validate_snap(libbe_handle_t *, char *);
bool be_exists(libbe_handle_t *, char *);
int be_export(libbe_handle_t *, char *, int fd);
int be_import(libbe_handle_t *, char *, int fd);
int be_add_child(libbe_handle_t *, char *, bool);
#endif /* _LIBBE_H */

132
lib/libbe/be_access.c Normal file
View File

@ -0,0 +1,132 @@
/*
* be_access.c
*
* Copyright (c) 2017 Kyle J. Kneitinger <kyle@kneit.in>
* All rights reserved.
*
* 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 REGENTS 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 REGENTS 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.
*/
#include "be.h"
#include "be_impl.h"
/*
* usage
*/
int
be_mount(libbe_handle_t *lbh, char *bootenv, char *mountpoint, int flags,
char *result_loc)
{
char be[BE_MAXPATHLEN];
char mnt_temp[BE_MAXPATHLEN];
zfs_handle_t *zfs_hdl;
char *path;
int mntflags;
int err;
if (err = be_root_concat(lbh, bootenv, be)) {
return (set_error(lbh, err));
}
if (!be_exists(lbh, bootenv)) {
return (set_error(lbh, BE_ERR_NOENT));
}
if (is_mounted(lbh->lzh, be, &path)) {
return (set_error(lbh, BE_ERR_MOUNTED));
}
mntflags = (flags & BE_MNT_FORCE) ? MNT_FORCE : 0;
/* Create mountpoint if it is not specified */
if (mountpoint == NULL) {
strcpy(mnt_temp, "/tmp/be_mount.XXXX");
if (mkdtemp(mnt_temp) == NULL) {
// TODO: create error for this
return (set_error(lbh, BE_ERR_UNKNOWN));
}
}
char opt = '\0';
if (err = zmount(be, (mountpoint == NULL) ? mnt_temp : mountpoint,
mntflags, MNTTYPE_ZFS, NULL, 0, &opt, 1)) {
// TODO: zmount returns the nmount error, look into what kind of
// errors we can report from that
return (set_error(lbh, BE_ERR_UNKNOWN));
}
if (result_loc != NULL) {
strcpy(result_loc, mountpoint == NULL ? mnt_temp : mountpoint);
}
return (BE_ERR_SUCCESS);
}
/*
* usage
*/
int
be_unmount(libbe_handle_t *lbh, char *bootenv, int flags)
{
int err, mntflags;
char be[BE_MAXPATHLEN];
struct statfs *mntbuf;
int mntsize;
char *mntpath;
if (err = be_root_concat(lbh, bootenv, be)) {
return (set_error(lbh, err));
}
if ((mntsize = getmntinfo(&mntbuf, MNT_NOWAIT)) == 0) {
// TODO correct error
return (set_error(lbh, BE_ERR_NOMOUNT));
}
mntpath = NULL;
for (int i = 0; i < mntsize; ++i) {
/* 0x000000de is the type number of zfs */
if (mntbuf[i].f_type != 0x000000de) {
continue;
}
if (strcmp(mntbuf[i].f_mntfromname, be) == 0) {
mntpath = mntbuf[i].f_mntonname;
break;
}
}
if (mntpath == NULL) {
return (set_error(lbh, BE_ERR_NOMOUNT));
}
mntflags = (flags & BE_MNT_FORCE) ? MNT_FORCE : 0;
if (err = unmount(mntpath, mntflags)) {
// TODO correct error
return (set_error(lbh, BE_ERR_NOMOUNT));
}
return (set_error(lbh, BE_ERR_SUCCESS));
}

115
lib/libbe/be_error.c Normal file
View File

@ -0,0 +1,115 @@
/*
* be_error.c
*
* Copyright (c) 2017 Kyle J. Kneitinger <kyle@kneit.in>
* All rights reserved.
*
* 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 REGENTS 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 REGENTS 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.
*/
#include "be.h"
#include "be_impl.h"
/*
* Usage
*/
int
libbe_errno(libbe_handle_t *lbh)
{
return (lbh->error);
}
const char *
libbe_error_description(libbe_handle_t *lbh)
{
switch (lbh->error) {
case BE_ERR_INVALIDNAME:
return ("invalid boot environment name");
case BE_ERR_EXISTS:
return ("boot environment name already taken");
case BE_ERR_NOENT:
return ("specified boot environment does not exist");
case BE_ERR_PERMS:
return ("insufficient permissions");
case BE_ERR_DESTROYACT:
return ("cannot destroy active boot environment");
case BE_ERR_DESTROYMNT:
return ("cannot destroy mounted boot env unless forced");
case BE_ERR_PATHLEN:
return ("provided path name exceeds maximum length limit");
case BE_ERR_INVORIGIN:
return ("snapshot origin's mountpoint is not \"/\"");
case BE_ERR_NOORIGIN:
return ("could not open snapshot's origin");
case BE_ERR_MOUNTED:
return ("boot environment is already mounted");
case BE_ERR_NOMOUNT:
return ("boot environment is not mounted");
case BE_ERR_ZFSOPEN:
return ("calling zfs_open() failed");
case BE_ERR_ZFSCLONE:
return ("error when calling zfs_clone() to create boot env");
case BE_ERR_UNKNOWN:
return ("unknown error");
default:
assert(lbh->error == BE_ERR_SUCCESS);
return ("no error");
}
}
void
libbe_print_on_error(libbe_handle_t *lbh, bool val)
{
lbh->print_on_err = val;
libzfs_print_on_error(lbh->lzh, val);
}
int
set_error(libbe_handle_t *lbh, be_error_t err)
{
// TODO: should the old error be overwritten or no?
lbh->error = err;
if (lbh->print_on_err && (err != BE_ERR_SUCCESS)) {
fprintf(stderr, "%s\n", libbe_error_description(lbh));
}
return (err);
}

59
lib/libbe/be_impl.h Normal file
View File

@ -0,0 +1,59 @@
/*
* be_impl.h
*
* Copyright (c) 2017 Kyle J. Kneitinger <kyle@kneit.in>
* All rights reserved.
*
* 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 REGENTS 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 REGENTS 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.
*/
#ifndef _LIBBE_IMPL_H
#define _LIBBE_IMPL_H
#include <libzfs.h>
#include "be.h"
struct libbe_handle {
libzfs_handle_t *lzh;
char root[BE_MAXPATHLEN];
char active[BE_MAXPATHLEN];
be_error_t error;
bool print_on_err;
};
struct libbe_deep_clone {
libbe_handle_t *lbh;
char *bename;
char *snapname;
char *be_root;
};
struct libbe_dccb {
zfs_handle_t *zhp;
nvlist_t *props;
};
int set_error(libbe_handle_t *, be_error_t);
#endif /* _LIBBE_IMPL_H */

245
lib/libbe/be_info.c Normal file
View File

@ -0,0 +1,245 @@
/*
* be_info.c
*
* Copyright (c) 2017 Kyle J. Kneitinger <kyle@kneit.in>
* All rights reserved.
*
* 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 REGENTS 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 REGENTS 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.
*/
#include "be.h"
#include "be_impl.h"
typedef struct prop_data {
nvlist_t *list;
libbe_handle_t *lbh;
} prop_data_t;
static int prop_list_builder_cb(zfs_handle_t *, void *);
static int prop_list_builder(prop_data_t *);
/*
* Returns the name of the active boot environment
*/
const char *
be_active_name(libbe_handle_t *lbh)
{
return (strrchr(lbh->active, '/') + sizeof(char));
}
/*
* Returns full path of the active boot environment
*/
const char *
be_active_path(libbe_handle_t *lbh)
{
return (lbh->active);
}
/*
* Returns the path of the boot environment root dataset
*/
const char *
be_root_path(libbe_handle_t *lbh)
{
return (lbh->root);
}
/*
* Returns an nvlist of the bootenv's properties
* TODO: the nvlist should be passed as a param and ints should return status
*/
nvlist_t *
be_get_bootenv_props(libbe_handle_t *lbh)
{
prop_data_t data;
data.lbh = lbh;
prop_list_builder(&data);
return (data.list);
}
/*
* Internal callback function used by zfs_iter_filesystems. For each dataset in
* the bootenv root, populate an nvlist_t of its relevant properties.
* TODO: should any other properties be included?
*/
static int
prop_list_builder_cb(zfs_handle_t *zfs_hdl, void *data_p)
{
/*
* TODO:
* some system for defining constants for the nvlist keys
* error checking
*/
char buf[512];
prop_data_t *data;
boolean_t mounted, active, nextboot;
data = (prop_data_t *)data_p;
nvlist_t *props;
libbe_handle_t *lbh = data->lbh;
nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP);
const char *dataset = zfs_get_name(zfs_hdl);
nvlist_add_string(props, "dataset", dataset);
const char *name = strrchr(dataset, '/') + 1;
nvlist_add_string(props, "name", name);
mounted = zfs_prop_get_int(zfs_hdl, ZFS_PROP_MOUNTED);
nvlist_add_boolean_value(props, "mounted", mounted);
// TODO: NOT CORRECT! Must use is_mounted
if (mounted) {
zfs_prop_get(zfs_hdl, ZFS_PROP_MOUNTPOINT, buf, 512,
NULL, NULL, 0, 1);
nvlist_add_string(props, "mountpoint", buf);
}
if (zfs_prop_get(zfs_hdl, ZFS_PROP_ORIGIN, buf, 512,
NULL, NULL, 0, 1)) {
nvlist_add_string(props, "origin", buf);
}
if (zfs_prop_get(zfs_hdl, ZFS_PROP_CREATION, buf, 512,
NULL, NULL, 0, 1)) {
nvlist_add_string(props, "creation", buf);
}
nvlist_add_boolean_value(props, "active",
(strcmp(be_active_path(lbh), dataset) == 0));
if (zfs_prop_get(zfs_hdl, ZFS_PROP_USED, buf, 512,
NULL, NULL, 0, 1)) {
nvlist_add_string(props, "used", buf);
}
if (zfs_prop_get(zfs_hdl, ZFS_PROP_USEDDS, buf, 512,
NULL, NULL, 0, 1)) {
nvlist_add_string(props, "usedds", buf);
}
if (zfs_prop_get(zfs_hdl, ZFS_PROP_USEDSNAP, buf, 512,
NULL, NULL, 0, 1)) {
nvlist_add_string(props, "usedsnap", buf);
}
if (zfs_prop_get(zfs_hdl, ZFS_PROP_USEDREFRESERV, buf, 512,
NULL, NULL, 0, 1)) {
nvlist_add_string(props, "usedrefreserv", buf);
}
if (zfs_prop_get(zfs_hdl, ZFS_PROP_REFERENCED, buf, 512,
NULL, NULL, 0, 1)) {
nvlist_add_string(props, "referenced", buf);
}
/* TODO figure out how to read nextboot (set in libzfs_pool.c) */
nvlist_add_nvlist(data->list, name, props);
return (0);
}
/*
* Updates the properties of each bootenv in the libbe handle
* TODO: rename to be_proplist_update
* TODO: ensure that this is always consistent (run after adds, deletes,
* renames,etc
*/
static int
prop_list_builder(prop_data_t *data)
{
zfs_handle_t *root_hdl;
if (nvlist_alloc(&(data->list), NV_UNIQUE_NAME, KM_SLEEP) != 0) {
/* TODO: actually handle error */
return (1);
}
if ((root_hdl =
zfs_open(data->lbh->lzh, data->lbh->root,
ZFS_TYPE_FILESYSTEM)) == NULL) {
return (BE_ERR_ZFSOPEN);
}
// TODO: some error checking here
zfs_iter_filesystems(root_hdl, prop_list_builder_cb, data);
zfs_close(root_hdl);
return (0);
}
/*
* frees property list and its children
*/
void
be_prop_list_free(nvlist_t *be_list)
{
nvlist_t *prop_list;
nvpair_t *be_pair = nvlist_next_nvpair(be_list, NULL);
if (nvpair_value_nvlist(be_pair, &prop_list) == 0) {
nvlist_free(prop_list);
}
while ((be_pair = nvlist_next_nvpair(be_list, be_pair)) != NULL) {
if (nvpair_value_nvlist(be_pair, &prop_list) == 0) {
nvlist_free(prop_list);
}
}
}
/*
* Usage
*/
bool
be_exists(libbe_handle_t *lbh, char *be)
{
char buf[BE_MAXPATHLEN];
be_root_concat(lbh, be, buf);
// TODO: check mountpoint prop and see if its /, AND that result with below
// expression
return (zfs_dataset_exists(lbh->lzh, buf, ZFS_TYPE_DATASET));
}

192
lib/libbe/libbe.3 Normal file
View File

@ -0,0 +1,192 @@
.\"
.\" Copyright (c) 2017 Kyle Kneitinger
.\" All rights reserved.
.\"
.\" 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.
.\"
.\" This manual page is based on the mp(3X) manual page from Sun Release
.\" 4.1, dated 7 September 1989. It's an old, crufty, and relatively ugly
.\" manual page, but it does document what appears to be the "traditional"
.\" libmp interface.
.\"
.\" $FreeBSD$
.\"
.Dd August 28, 2017
.Dt LIBBE 3
.Os
.Sh NAME
.Nm libbe
.Nd library for creating, destroying and modifying ZFS boot environments.
.Sh SYNOPSIS
.In be.h
.Pp
Function prototypes are given in the main body of the text.
.Pp
Applications using this interface must be linked with
.Fl l Ns Ar be
.Sh DESCRIPTION
.Pp
.Nm
interfaces with libzfs to provide a set of functions for various operations
regarding ZFS boot environments including "deep" boot environments in which
a boot environments has child datasets.
.Pp
A context structure is passed to each function, allowing for a small amount
of state to be retained, such as errors from previous operations.
.\" TODO: describe break on err functionality
.Pp
.Ft "libbe_handle_t *" Ns
.Fn libbe_init "void" ;
.Pp
.Ft void
.Fn libbe_close "libbe_handle_t *" ;
.Pp
.Ft "const char *" Ns
.Fn be_active_name "libbe_handle_t *" ;
.Pp
.Ft "const char *" Ns
.Fn be_active_path "libbe_handle_t *" ;
.Pp
.Ft "const char *" Ns
.Fn be_root_path "libbe_handle_t *" ;
.Pp
.Ft "nvlist_t *" Ns
.Fn libbe_handle_t "libbe_handle_t " ;
.Pp
.Ft int
.Fn be_create "libbe_handle_t *, char *" ;
.Pp
.Ft int
.Fn be_create_from_existing "libbe_handle_t *, char *, char *" ;
.Pp
.Ft int
.Fn be_rename "libbe_handle_t *, char *, char *" ;
.Pp
.\" TODO: Write up of destroy options
.\" typedef enum {
.\" BE_DESTROY_FORCE = 1 << 0,
.\" } be_destroy_opt_t;
.Ft int
.Fn be_destroy "libbe_handle_t *, char *, int" ;
.Pp
.\" TODO: Write up of mount options
.\" typedef enum {
.\" BE_MNT_FORCE = 1 << 0,
.\" BE_MNT_DEEP = 1 << 1,
.\" } be_mount_opt_t;
.Ft int
.Fn be_mount "libbe_handle_t *, char *, char *, int" ;
.Pp
.Ft int
.Fn be_unmount "libbe_handle_t *, char *, int" ;
.Pp
.Ft int
.Fn libbe_errno "libbe_handle_t *" ;
.Pp
.Ft "const char *" Ns
.Fn libbe_error_description "libbe_handle_t *" ;
.Pp
.Ft void
.Fn libbe_print_on_error "libbe_handle_t *, bool" ;
.Pp
.Ft int
.Fn be_root_concat "libbe_handle_t *, char *, char *" ;
.Pp
.Ft int
.Fn be_validate_name "libbe_handle_t *, char *" ;
.Pp
.Ft int
.Fn be_validate_snap "libbe_handle_t *, char *" ;
.Pp
.Ft bool
.Fn be_exists "libbe_handle_t *, char *" ;
.Pp
.Ft int
.Fn be_export "libbe_handle_t *, char *, int fd" ;
.Pp
.Ft int
.Fn be_import "libbe_handle_t *, char *, int fd" ;
.Pp
.Ft int
.Fn be_add_child "libbe_handle_t *, char *, bool" ;
.Pp
.\" .Ft void
.\" .Fn mp_mfree "MINT *mp" ;
.\" .Bd -ragged -offset indent
.\" .Fn mp_itom
.\" returns an
.\" .Vt MINT
.\" with the value of
.\" .Fa n .
.\" .Fn mp_xtom
.\" returns an
.\" .Vt MINT
.\" with the value of
.\" .Fa s ,
.\" which is treated to be in hexadecimal.
.\" The return values from
.\" .Fn mp_itom
.\" and
.\" .Fn mp_xtom
.\" must be released with
.\" .Fn mp_mfree
.\" when they are no longer needed.
.\" .Fn mp_mtox
.\" returns a null-terminated hexadecimal string having the value of
.\" .Fa mp ;
.\" its return value must be released with
.\" .Fn free
.\" .Pq Xr free 3
.\" when it is no longer needed.
.\" .Ed
.\" .Pp
.Sh DIAGNOSTICS
Upon error, one of the following values will be returned.
.\" TODO: make each entry on its own line.
.Bd -ragged -offset indent
BE_ERR_SUCCESS,
BE_ERR_INVALIDNAME,
BE_ERR_EXISTS,
BE_ERR_NOENT,
BE_ERR_PERMS,
BE_ERR_DESTROYACT,
BE_ERR_DESTROYMNT,
BE_ERR_PATHLEN,
BE_ERR_INVORIGIN,
BE_ERR_NOORIGIN,
BE_ERR_MOUNTED,
BE_ERR_NOMOUNT,
BE_ERR_ZFSOPEN,
BE_ERR_ZFSCLONE,
BE_ERR_UNKNOWN
.Ed
.Sh SEE ALSO
.Xr be 1 ,
.Sh HISTORY
.Nm
and it's corresponding command,
.Xr be 3 ,
were written as a 2017 Google Summer of Code project with Allan Jude serving
as a mentor.
.\" TODO: update when implementation complete.
.\" .Sh BUGS

View File

@ -86,6 +86,7 @@ SUBDIR.${MK_PF}+= pfctl
SUBDIR.${MK_PF}+= pflogd
SUBDIR.${MK_QUOTAS}+= quotacheck
SUBDIR.${MK_ROUTED}+= routed
SUBDIR.${MK_ZFS}+= be
SUBDIR.${MK_ZFS}+= zfsbootcfg
SUBDIR.${MK_TESTS}+= tests

10
sbin/be/Makefile Normal file
View File

@ -0,0 +1,10 @@
# @(#)Makefile 8.1 (Berkeley) 6/6/93
PROG= be
WARNS?= 1
MAN= be.1
LIBADD+= be
LIBADD+= nvpair
.include <bsd.prog.mk>

220
sbin/be/be.1 Normal file
View File

@ -0,0 +1,220 @@
.\"
.\" be - Utility to manage Boot Environments on the ZFS filesystem
.\"
.\" 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.
.\"
.\"
.\" @(#)be.1
.\" $FreeBSD$
.\"
.Dd June 15, 2017
.Dt BE 1
.Os FreeBSD
.Sh NAME
.Nm be
.Nd Utility to manage Boot Environments on ZFS
.Sh SYNOPSIS
.Nm
activate
.Op Fl t
.Ao Ar beName Ac
.Nm
create
.Op Fl r
.Op Fl e Ar nonActiveBe | Fl e Ar beName@snapshot
.Ao Ar beName Ac
.Nm
create
.Op Fl r
.Ao Ar beName@snapshot Ac
.Nm
destroy
.Op Fl F
.Ao Ar beName | beName@snapshot Ac
.Nm
jail
.Ao Ar jailID | jailName Ac
.Ao Ar bootenv Ac
.Nm
list
.Op Fl a
.Op Fl D
.Op Fl H
.Op Fl s
.Nm
mount
.Ao Ar beName Ac
.Op mountpoint
.Nm
rename
.Ao Ar origBeName Ac
.Ao Ar newBeName Ac
.Nm
{ ujail | unjail }
.Ao Ar jailID | jailName Ac
.Ao Ar bootenv Ac
.Nm
{ umount | unmount }
.Op Fl f
.Ao Ar beName Ac
.Sh DESCRIPTION
The
.Nm
command is used to setup and interact with ZFS boot environments, which are bootable clones of datasets.
.Pp
.Em Boot Environments
allows the system to be upgraded, while preserving the old system environment in a separate ZFS dataset.
.Pp
.Sh COMMANDS
The following commands are supported by
.Nm :
.Bl -tag -width activate
.It Ic activate
.Op Fl t
.Ar <beName>
.Pp
Activate the given
.Ar beName
as the default boot filesystem. If the
.Op Fl t
flag is given, this takes effect only for the next boot.
.Pp
.It Ic create
.Op Fl r
.Op Fl e Ar nonActiveBe | Fl e Ar beName@snapshot
.Ao Ar beName Ac
.Pp
Creates a new boot environment named
.Ar beName .
If the -e param is specified, the new environment will be cloned from the given
.Ar nonActiveBe | Ar beName@snapshot .
If the
.Op Fl r
flag is given, a recursive boot environment will be made.
.Pp
.It Ic create
.Op Fl r
.Ao Ar beName@snapshot Ac
.Pp
Creates a snapshot of the existing boot environment named
.Ar beName .
If the
.Op Fl r
flag is given, a recursive boot environment will be made.
.Pp
.It Ic destroy
.Op Fl F
.Ao Ar beName | beName@snapshot Ac
.Pp
Destroys the given
.Ar beName
boot environment or
.Ar beName@snapshot
snapshot.
Specifying
.Fl F
will automatically unmount without confirmation.
.Pp
.It Ic jail
.Ao Ar jailID | jailName Ac
.Ao Ar bootenv Ac
.Pp
Creates a jail of the given boot environment.
.Pp
.It Ic list
.Op Fl a
.Op Fl D
.Op Fl H
.Op Fl s
.Pp
Displays all boot environments.
The Active field indicates whether the boot environment is active now (N); active on reboot (R); or both (NR).
.Pp
If
.Fl a
is used, display all datasets.
If
.Fl D
is used, display the full space usage for each boot environment, assuming all other boot environments were destroyed.
The
.Fl H
option is used for scripting. It does not print headers and separate fields by a single tab instead of arbitrary white space.
If
.Fl s
is used, display all snapshots as well.
.Pp
.It Ic mount
.Ao Ar beName Ac
.Op mountpoint
.Pp
Temporarily mount the boot environment.
Mount at the specified
.Ar mountpoint
if provided.
.Pp
.It Ic rename Ao Ar origBeName Ac Ao Ar newBeName Ac
.Pp
Renames the given nonactive
.Ar origBeName
to the given
.Ar newBeName
.Pp
.It Ic unmount
.Op Fl f
.Ao Ar beName Ac
.Pp
Unmount the given boot environment, if it is mounted.
Specifying
.Fl f
will force the unmount if busy.
.Pp
.It Ic unjail
.Ao Ar beName Ac
.Pp
Destroys the jail created from the given boot environment.
.Pp
.El
.Sh EXAMPLES
.Bl -bullet
.It
To fill in with jail upgrade example when behavior is firm.
.Pp
.Sh SEE ALSO
.Xr jail 8 ,
.Xr zfs 8 ,
.Xr zpool 8
.Sh HISTORY
.Nm
is based on
.Xr beadm 1
and was implemented as a project for the 2017 Summer of Code, along with
.Xr libbe 3 .
.Sh AUTHORS
.Bl -bullet
.It
Kyle Kneitinger (kneitinger)
.Ar kyle@kneit.in
.Pp
Creator of
.Nm .
.It
Slawomir Wojciech Wojtczak (vermaden)
.Ar vermaden@interia.pl
.Pp
Creator and maintainer of
.Xr beadm 1 .
.It
Bryan Drewery (bdrewery)
.Ar bryan@shatow.net
.Pp
Wrote the original
.Xr beadm 1
manual page that this one is derived from.
.El

664
sbin/be/be.c Normal file
View File

@ -0,0 +1,664 @@
/*
* be.c
*
* Copyright (c) 2017 Kyle J. Kneitinger <kyle@kneit.in>
* All rights reserved.
*
* 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 REGENTS 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 REGENTS 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.
*/
#include <sys/param.h>
#include <sys/jail.h>
#include <sys/mount.h>
#include <errno.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sysexits.h>
#include <unistd.h>
#include <sys/nv.h>
#include <be.h>
static int be_cmd_activate(int argc, char *argv[]);
static int be_cmd_create(int argc, char *argv[]);
static int be_cmd_destroy(int argc, char *argv[]);
static int be_cmd_export(int argc, char *argv[]);
static int be_cmd_import(int argc, char *argv[]);
static int be_cmd_add(int argc, char *argv[]);
static int be_cmd_jail(int argc, char *argv[]);
static int be_cmd_list(int argc, char *argv[]);
static int be_cmd_mount(int argc, char *argv[]);
static int be_cmd_rename(int argc, char *argv[]);
static int be_cmd_unjail(int argc, char *argv[]);
static int be_cmd_unmount(int argc, char *argv[]);
libbe_handle_t *be;
static int
usage(bool explicit)
{
FILE *fp = explicit ? stdout : stderr;
fprintf(fp,
"usage:\tbe ( -h | -? | subcommand [args...] )\n"
"\tbe activate [-t] beName\n"
"\tbe create [-e nonActiveBe | -e beName@snapshot] beName\n"
"\tbe create beName@snapshot\n"
"\tbe destroy [-F] beName | beName@snapshot⟩\n"
"\tbe export sourceBe\n"
"\tbe import targetBe\n"
"\tbe add (path)*\n"
"\tbe jail bootenv\n"
"\tbe list [-a] [-D] [-H] [-s]\n"
"\tbe mount beName [mountpoint]\n"
"\tbe rename origBeName newBeName\n"
"\tbe { ujail | unjail } ⟨jailID | jailName⟩ bootenv\n"
"\tbe { umount | unmount } [-f] beName\n");
return (explicit ? 0 : EX_USAGE);
}
/*
* Represents a relationship between the command name and the parser action
* that handles it.
*/
struct command_map_entry {
const char *command;
int (*fn)(int argc, char *argv[]);
};
static struct command_map_entry command_map[] =
{
{ "activate", be_cmd_activate },
{ "create", be_cmd_create },
{ "destroy", be_cmd_destroy },
{ "export", be_cmd_export },
{ "import", be_cmd_import },
{ "add", be_cmd_add },
{ "jail", be_cmd_jail },
{ "list", be_cmd_list },
{ "mount", be_cmd_mount },
{ "rename", be_cmd_rename },
{ "unjail", be_cmd_unjail },
{ "unmount", be_cmd_unmount },
};
static int
get_cmd_index(char *cmd, int *index)
{
int map_size = sizeof(command_map) / sizeof(struct command_map_entry);
for (int i = 0; i < map_size; ++i) {
if (strcmp(cmd, command_map[i].command) == 0) {
*index = i;
return (0);
}
}
return (1);
}
static int
be_cmd_activate(int argc, char *argv[])
{
int err, opt;
bool temp;
char *bootenv;
temp = false;
while ((opt = getopt(argc, argv, "t")) != -1) {
switch (opt) {
case 't':
temp = true;
break;
default:
fprintf(stderr, "be activate: unknown option '-%c'\n",
optopt);
return (usage(false));
}
}
argc -= optind;
argv += optind;
if (argc != 1) {
fprintf(stderr, "be activate: wrong number of arguments\n");
return (usage(false));
}
/* activate logic goes here */
if ((err = be_activate(be, argv[0], temp)) != 0) {
// TODO: more specific error msg based on err
printf("did not successfully activate boot environment %s\n",
argv[0]);
} else {
printf("successfully activated boot environment %s\n", argv[0]);
}
if (temp) {
printf("for next boot\n");
}
return (err);
}
// TODO: when only one arg is given, and it contains an "@" the this should
// create that snapshot
static int
be_cmd_create(int argc, char *argv[])
{
int err, opt;
char *snapname;
char *bootenv;
char *source;
snapname = NULL;
while ((opt = getopt(argc, argv, "e:")) != -1) {
switch (opt) {
case 'e':
snapname = optarg;
break;
default:
fprintf(stderr, "be create: unknown option '-%c'\n",
optopt);
return (usage(false));
}
}
argc -= optind;
argv += optind;
if (argc != 1) {
fprintf(stderr, "be create: wrong number of arguments\n");
return (usage(false));
}
bootenv = *argv;
if (snapname != NULL) {
if (strchr(snapname, '@') != NULL) {
err = be_create_from_existing_snap(be, bootenv,
snapname);
} else {
err = be_create_from_existing(be, bootenv, snapname);
}
} else {
if ((snapname = strchr(bootenv, '@')) != NULL) {
*(snapname++) = '\0';
if ((err = be_snapshot(be, (char *)be_active_path(be),
snapname, true, NULL)) != BE_ERR_SUCCESS) {
fprintf(stderr, "failed to create snapshot\n");
}
asprintf(&source, "%s@%s", be_active_path(be), snapname);
err = be_create_from_existing_snap(be, bootenv,
source);
return (err);
} else {
err = be_create(be, bootenv);
}
}
switch (err) {
case BE_ERR_SUCCESS:
break;
default:
if (snapname == NULL) {
fprintf(stderr,
"failed to create bootenv %s\n", bootenv);
} else {
fprintf(stderr,
"failed to create bootenv %s from snapshot %s\n",
bootenv, snapname);
}
}
return (err);
}
static int
be_cmd_export(int argc, char *argv[])
{
int opt;
char *bootenv;
if (argc == 1) {
fprintf(stderr, "be export: missing boot environment name\n");
return (usage(false));
}
if (argc > 2) {
fprintf(stderr, "be export: extra arguments provided\n");
return (usage(false));
}
bootenv = argv[1];
if (isatty(STDOUT_FILENO)) {
fprintf(stderr, "be export: must redirect output\n");
return (EX_USAGE);
}
be_export(be, bootenv, STDOUT_FILENO);
return (0);
}
static int
be_cmd_import(int argc, char *argv[])
{
char *bootenv;
int err;
if (argc == 1) {
fprintf(stderr, "be import: missing boot environment name\n");
return (usage(false));
}
if (argc > 2) {
fprintf(stderr, "be import: extra arguments provided\n");
return (usage(false));
}
bootenv = argv[1];
if (isatty(STDIN_FILENO)) {
fprintf(stderr, "be import: input can not be from terminal\n");
return (EX_USAGE);
}
err = be_import(be, bootenv, STDIN_FILENO);
return (err);
}
static int
be_cmd_add(int argc, char *argv[])
{
char *bootenv;
if (argc < 2) {
fprintf(stderr, "be add: must provide at least one path\n");
return (usage(false));
}
for (int i = 1; i < argc; ++i) {
printf("arg %d: %s\n", i, argv[i]);
// TODO catch err
be_add_child(be, argv[i], true);
}
return (0);
}
static int
be_cmd_destroy(int argc, char *argv[])
{
int opt, err;
bool force;
char *target;
force = false;
while ((opt = getopt(argc, argv, "F")) != -1) {
switch (opt) {
case 'F':
force = true;
break;
default:
fprintf(stderr, "be destroy: unknown option '-%c'\n",
optopt);
return (usage(false));
}
}
argc -= optind;
argv += optind;
if (argc != 1) {
fprintf(stderr, "be destroy: wrong number of arguments\n");
return (usage(false));
}
target = argv[0];
err = be_destroy(be, target, force);
return (err);
}
static int
be_cmd_jail(int argc, char *argv[])
{
char *bootenv;
char mnt_loc[BE_MAXPATHLEN];
char buf[BE_MAXPATHLEN*2];
int err, jid;
//struct jail be_jail = { 0 };
if (argc == 1) {
fprintf(stderr, "be jail: missing boot environment name\n");
return (usage(false));
}
if (argc > 2) {
fprintf(stderr, "be jail: too many arguments\n");
return (usage(false));
}
bootenv = argv[1];
// TODO: if its already mounted, perhaps there should be a flag to
// indicate its okay to proceed??
if ((err = be_mount(be, bootenv, NULL, 0, mnt_loc)) != BE_ERR_SUCCESS) {
fprintf(stderr, "could not mount bootenv\n");
}
// NOTE: this is not quite functional:
// see https://github.com/vermaden/beadm/blob/master/HOWTO.htm on
// neccesary modifications to correctly boot the jail
//snprintf(buf, BE_MAXPATHLEN*2, "jail %s %s %s /bin/sh /etc/rc", mnt_loc, bootenv, "192.168.1.123");
snprintf(buf, BE_MAXPATHLEN*2, "jail %s %s %s /bin/sh", mnt_loc,
bootenv, "192.168.1.123");
system(buf);
unmount(mnt_loc, 0);
/*
* be_jail.version = JAIL_API_VERSION;
* be_jail.path = "/tmp/be_mount.hCCk";
* be_jail.jailname = "sdfs";
*
* if ((jid = jail(&be_jail)) != -1) {
* printf("jail %d created at %s\n", jid, mnt_loc);
* err = 0;
* } else {
* fprintf(stderr, "unable to create jail. error: %d\n", errno);
* err = errno;
* }
*/
return (0);
}
static int
be_cmd_list(int argc, char *argv[])
{
int opt;
bool show_all_datasets, show_space, hide_headers, show_snaps;
char *bootenv;
nvlist_t *props;
show_all_datasets = show_space = hide_headers = show_snaps = false;
while ((opt = getopt(argc, argv, "aDHs")) != -1) {
switch (opt) {
case 'a':
show_all_datasets = true;
break;
case 'D':
show_space = true;
break;
case 'H':
hide_headers = true;
break;
case 's':
show_space = true;
break;
default:
fprintf(stderr, "be list: unknown option '-%c'\n",
optopt);
return (usage(false));
}
}
argc -= optind;
if (argc != 0) {
fprintf(stderr, "be list: extra argument provided\n");
return (usage(false));
}
//props = be_get_bootenv_props(be);
return (0);
}
static int
be_cmd_mount(int argc, char *argv[])
{
int err;
char result_loc[BE_MAXPATHLEN];
char *bootenv;
char *mountpoint;
if (argc < 2) {
fprintf(stderr, "be mount: missing argument(s)\n");
return (usage(false));
}
if (argc > 3) {
fprintf(stderr, "be mount: too many arguments\n");
return (usage(false));
}
bootenv = argv[1];
mountpoint = ((argc == 3) ? argv[2] : NULL);
err = be_mount(be, bootenv, mountpoint, 0, result_loc);
switch (err) {
case BE_ERR_SUCCESS:
printf("successfully mounted %s at %s\n", bootenv, result_loc);
break;
default:
fprintf(stderr,
(argc == 3) ? "failed to mount bootenv %s at %s\n" :
"failed to mount bootenv %s at temporary path %s\n",
bootenv, mountpoint);
}
return (err);
}
static int
be_cmd_rename(int argc, char *argv[])
{
char *src;
char *dest;
int err;
if (argc < 3) {
fprintf(stderr, "be rename: missing argument\n");
return (usage(false));
}
if (argc > 3) {
fprintf(stderr, "be rename: too many arguments\n");
return (usage(false));
}
src = argv[1];
dest = argv[2];
err = be_rename(be, src, dest);
switch (err) {
case BE_ERR_SUCCESS:
break;
default:
fprintf(stderr, "failed to rename bootenv %s to %s\n",
src, dest);
}
return (0);
}
static int
be_cmd_unjail(int argc, char *argv[])
{
int opt;
char *cmd, *target;
bool force;
/* Store alias used */
cmd = argv[0];
force = false;
while ((opt = getopt(argc, argv, "f")) != -1) {
switch (opt) {
case 'f':
force = true;
break;
default:
fprintf(stderr, "be %s: unknown option '-%c'\n",
cmd, optopt);
return (usage(false));
}
}
argc -= optind;
argv += optind;
if (argc != 1) {
fprintf(stderr, "be %s: wrong number of arguments\n", cmd);
return (usage(false));
}
target = argv[0];
/* unjail logic goes here */
return (0);
}
static int
be_cmd_unmount(int argc, char *argv[])
{
int err, flags, opt;
char *cmd, *bootenv;
/* Store alias used */
cmd = argv[0];
flags = 0;
while ((opt = getopt(argc, argv, "f")) != -1) {
switch (opt) {
case 'f':
flags |= BE_MNT_FORCE;
break;
default:
fprintf(stderr, "be %s: unknown option '-%c'\n",
cmd, optopt);
return (usage(false));
}
}
argc -= optind;
argv += optind;
if (argc != 1) {
fprintf(stderr, "be %s: wrong number of arguments\n", cmd);
return (usage(false));
}
bootenv = argv[0];
err = be_unmount(be, bootenv, flags);
switch (err) {
case BE_ERR_SUCCESS:
break;
default:
fprintf(stderr, "failed to unmount bootenv %s\n", bootenv);
}
return (err);
}
int
main(int argc, char *argv[])
{
char *command;
int command_index, rc;
if (argc < 2) {
fprintf(stderr, "missing command\n");
return (usage(false));
}
command = argv[1];
/* Handle command aliases */
if (strcmp(command, "umount") == 0) {
command = "unmount";
}
if (strcmp(command, "ujail") == 0) {
command = "unjail";
}
if ((strcmp(command, "-?") == 0) || (strcmp(command, "-h") == 0)) {
return (usage(true));
}
if (get_cmd_index(command, &command_index)) {
fprintf(stderr, "unknown command: %s\n", command);
return (usage(false));
}
if ((be = libbe_init()) == NULL) {
return (-1);
}
libbe_print_on_error(be, true);
/* TODO: can be simplified if offset by 2 instead of one */
rc = command_map[command_index].fn(argc-1, argv+1);
libbe_close(be);
return (rc);
}

View File

@ -62,6 +62,7 @@ _LIBRARIES= \
asn1 \
auditd \
avl \
be \
begemot \
bluetooth \
bsdxml \
@ -335,6 +336,7 @@ _DP_zfs= md pthread umem util uutil m nvpair avl bsdxml geom nvpair z \
zfs_core
_DP_zfs_core= nvpair
_DP_zpool= md pthread z nvpair avl umem
_DP_be= zfs nvpair
# OFED support
.if ${MK_OFED} != "no"
@ -472,6 +474,8 @@ LIBBSNMPTOOLS?= ${LIBBSNMPTOOLSDIR}/libbsnmptools.a
LIBAMUDIR= ${OBJTOP}/usr.sbin/amd/libamu
LIBAMU?= ${LIBAMUDIR}/libamu.a
LIBBE?= ${DESTDIR}${LIBDIR}/libbe.a
LIBPMCSTATDIR= ${OBJTOP}/lib/libpmcstat
LIBPMCSTAT?= ${LIBPMCSTATDIR}/libpmcstat.a