Import libbe(3)/be(1) from socsvn/soc2017/kneitinger/libbe-head
This commit is contained in:
parent
7a13690726
commit
f741758a44
53
lib/libbe/Makefile
Normal file
53
lib/libbe/Makefile
Normal 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
876
lib/libbe/be.c
Normal 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
113
lib/libbe/be.h
Normal 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
132
lib/libbe/be_access.c
Normal 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
115
lib/libbe/be_error.c
Normal 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
59
lib/libbe/be_impl.h
Normal 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
245
lib/libbe/be_info.c
Normal 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
192
lib/libbe/libbe.3
Normal 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
|
||||
|
@ -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
10
sbin/be/Makefile
Normal 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
220
sbin/be/be.1
Normal 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
664
sbin/be/be.c
Normal 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);
|
||||
}
|
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user