Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F132568707
D25512.id74917.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
103 KB
Referenced Files
None
Subscribers
None
D25512.id74917.diff
View Options
Index: Makefile.inc1
===================================================================
--- Makefile.inc1
+++ Makefile.inc1
@@ -2734,7 +2734,8 @@
lib/libufs \
lib/libutil lib/libpjdlog ${_lib_libypclnt} lib/libz lib/msun \
${_secure_lib_libcrypto} ${_secure_lib_libssl} \
- ${_lib_libldns} ${_secure_lib_libssh}
+ ${_lib_libldns} ${_secure_lib_libssh} \
+ lib/libzfsbootenv
.if ${MK_DIALOG} != "no"
_prebuild_libs+= gnu/lib/libdialog
@@ -2805,7 +2806,8 @@
cddl/lib/libzfs__L: lib/libthr__L lib/libmd__L lib/libz__L cddl/lib/libumem__L
cddl/lib/libzfs__L: cddl/lib/libuutil__L cddl/lib/libavl__L lib/libgeom__L
-lib/libbe__L: cddl/lib/libzfs__L
+lib/libzfsbootenv__L: cddl/lib/libzfs__L
+lib/libbe__L: cddl/lib/libzfs__L lib/libzfsbootenv__L
.endif
_cddl_lib_libctf= cddl/lib/libctf
_cddl_lib= cddl/lib
Index: cddl/contrib/opensolaris/cmd/zinject/translate.c
===================================================================
--- cddl/contrib/opensolaris/cmd/zinject/translate.c
+++ cddl/contrib/opensolaris/cmd/zinject/translate.c
@@ -20,7 +20,7 @@
*/
/*
* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2012 by Delphix. All rights reserved.
+ * Copyright (c) 2012, 2020 by Delphix. All rights reserved.
*/
#include <libzfs.h>
@@ -484,7 +484,7 @@
record->zi_end = record->zi_start + VDEV_PAD_SIZE - 1;
break;
case TYPE_LABEL_PAD2:
- record->zi_start = offsetof(vdev_label_t, vl_pad2);
+ record->zi_start = offsetof(vdev_label_t, vl_be);
record->zi_end = record->zi_start + VDEV_PAD_SIZE - 1;
break;
}
Index: cddl/contrib/opensolaris/lib/libzfs/common/libzfs.h
===================================================================
--- cddl/contrib/opensolaris/lib/libzfs/common/libzfs.h
+++ cddl/contrib/opensolaris/lib/libzfs/common/libzfs.h
@@ -837,6 +837,8 @@
extern int zpool_read_label(int, nvlist_t **);
extern int zpool_read_all_labels(int, nvlist_t **);
extern int zpool_clear_label(int);
+extern int zpool_set_bootenv(zpool_handle_t *, const nvlist_t *);
+extern int zpool_get_bootenv(zpool_handle_t *, nvlist_t **);
/* is this zvol valid for use as a dump device? */
extern int zvol_check_dump_config(char *);
Index: cddl/contrib/opensolaris/lib/libzfs/common/libzfs_pool.c
===================================================================
--- cddl/contrib/opensolaris/lib/libzfs/common/libzfs_pool.c
+++ cddl/contrib/opensolaris/lib/libzfs/common/libzfs_pool.c
@@ -21,7 +21,7 @@
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2011, 2017 by Delphix. All rights reserved.
+ * Copyright (c) 2011, 2020 by Delphix. All rights reserved.
* Copyright (c) 2013, Joyent, Inc. All rights reserved.
* Copyright 2016 Nexenta Systems, Inc.
* Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>
@@ -395,7 +395,7 @@
* Assuming bootfs is a valid dataset name.
*/
static boolean_t
-bootfs_name_valid(const char *pool, char *bootfs)
+bootfs_name_valid(const char *pool, const char *bootfs)
{
int len = strlen(pool);
@@ -4231,6 +4231,38 @@
(void) snprintf(pathname, len, "%s:<0x%llx>", dsname, obj);
}
free(mntpnt);
+}
+
+int
+zpool_set_bootenv(zpool_handle_t *zhp, const nvlist_t *envmap)
+{
+ int error = lzc_set_bootenv(zhp->zpool_name, envmap);
+ if (error != 0) {
+ (void) zpool_standard_error_fmt(zhp->zpool_hdl, error,
+ dgettext(TEXT_DOMAIN,
+ "error setting bootenv in pool '%s'"), zhp->zpool_name);
+ }
+
+ return (error);
+}
+
+int
+zpool_get_bootenv(zpool_handle_t *zhp, nvlist_t **nvlp)
+{
+ nvlist_t *nvl;
+ int error;
+
+ nvl = NULL;
+ error = lzc_get_bootenv(zhp->zpool_name, &nvl);
+ if (error != 0) {
+ (void) zpool_standard_error_fmt(zhp->zpool_hdl, error,
+ dgettext(TEXT_DOMAIN,
+ "error getting bootenv in pool '%s'"), zhp->zpool_name);
+ } else {
+ *nvlp = nvl;
+ }
+
+ return (error);
}
#ifdef illumos
Index: cddl/contrib/opensolaris/lib/libzfs_core/common/libzfs_core.h
===================================================================
--- cddl/contrib/opensolaris/lib/libzfs_core/common/libzfs_core.h
+++ cddl/contrib/opensolaris/lib/libzfs_core/common/libzfs_core.h
@@ -20,7 +20,7 @@
*/
/*
- * Copyright (c) 2012, 2016 by Delphix. All rights reserved.
+ * Copyright (c) 2012, 2020 by Delphix. All rights reserved.
* Copyright (c) 2013 by Martin Matuska <mm@FreeBSD.org>. All rights reserved.
* Copyright 2017 RackTop Systems.
* Copyright (c) 2017 Datto Inc.
@@ -105,6 +105,8 @@
int lzc_pool_checkpoint(const char *);
int lzc_pool_checkpoint_discard(const char *);
+int lzc_set_bootenv(const char *, const nvlist_t *);
+int lzc_get_bootenv(const char *, nvlist_t **);
#ifdef __cplusplus
}
#endif
Index: cddl/contrib/opensolaris/lib/libzfs_core/common/libzfs_core.c
===================================================================
--- cddl/contrib/opensolaris/lib/libzfs_core/common/libzfs_core.c
+++ cddl/contrib/opensolaris/lib/libzfs_core/common/libzfs_core.c
@@ -20,7 +20,7 @@
*/
/*
- * Copyright (c) 2012, 2018 by Delphix. All rights reserved.
+ * Copyright (c) 2012, 2020 by Delphix. All rights reserved.
* Copyright (c) 2013 Steven Hartland. All rights reserved.
* Copyright (c) 2014 Integros [integros.com]
* Copyright 2017 RackTop Systems.
@@ -1209,4 +1209,22 @@
fnvlist_free(args);
return (error);
+}
+
+/*
+ * Set the bootenv contents for the given pool.
+ */
+int
+lzc_set_bootenv(const char *pool, const nvlist_t *env)
+{
+ return (lzc_ioctl(ZFS_IOC_SET_BOOTENV, pool, (nvlist_t *)env, NULL));
+}
+
+/*
+ * Get the contents of the bootenv of the given pool.
+ */
+int
+lzc_get_bootenv(const char *pool, nvlist_t **outnvl)
+{
+ return (lzc_ioctl(ZFS_IOC_GET_BOOTENV, pool, NULL, outnvl));
}
Index: lib/Makefile
===================================================================
--- lib/Makefile
+++ lib/Makefile
@@ -212,7 +212,7 @@
SUBDIR.${MK_USB}+= libusbhid libusb
SUBDIR.${MK_OFED}+= ofed
SUBDIR.${MK_VERIEXEC}+= libveriexec
-SUBDIR.${MK_ZFS}+= libbe
+SUBDIR.${MK_ZFS}+= libbe libzfsbootenv
.if !make(install)
SUBDIR_PARALLEL=
Index: lib/libbe/Makefile
===================================================================
--- lib/libbe/Makefile
+++ lib/libbe/Makefile
@@ -17,7 +17,9 @@
LIBADD+= zfs
LIBADD+= nvpair
+LIBADD+= zfsbootenv
+CFLAGS+= -I${SRCTOP}/lib/libzfsbootenv
CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzfs/common
CFLAGS+= -I${SRCTOP}/sys/cddl/compat/opensolaris
CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/include
Index: lib/libbe/be.h
===================================================================
--- lib/libbe/be.h
+++ lib/libbe/be.h
@@ -81,6 +81,7 @@
void be_prop_list_free(nvlist_t *be_list);
int be_activate(libbe_handle_t *, const char *, bool);
+int be_deactivate(libbe_handle_t *, const char *, bool);
bool be_is_auto_snapshot_name(libbe_handle_t *, const char *);
Index: lib/libbe/be.c
===================================================================
--- lib/libbe/be.c
+++ lib/libbe/be.c
@@ -43,6 +43,7 @@
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
+#include <libzfsbootenv.h>
#include "be.h"
#include "be_impl.h"
@@ -1219,43 +1220,20 @@
}
#endif /* SOON */
-static int
-be_set_nextboot(libbe_handle_t *lbh, nvlist_t *config, uint64_t pool_guid,
- const char *zfsdev)
-{
- nvlist_t **child;
- uint64_t vdev_guid;
- int c, children;
-
- if (nvlist_lookup_nvlist_array(config, ZPOOL_CONFIG_CHILDREN, &child,
- &children) == 0) {
- for (c = 0; c < children; ++c)
- if (be_set_nextboot(lbh, child[c], pool_guid, zfsdev) != 0)
- return (1);
- return (0);
- }
-
- if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_GUID,
- &vdev_guid) != 0) {
- return (1);
- }
-
- if (zpool_nextboot(lbh->lzh, pool_guid, vdev_guid, zfsdev) != 0) {
- perror("ZFS_IOC_NEXTBOOT failed");
- return (1);
- }
-
- return (0);
-}
-
/*
- * Deactivate old BE dataset; currently just sets canmount=noauto
+ * Deactivate old BE dataset; currently just sets canmount=noauto or
+ * resets boot once configuration.
*/
-static int
-be_deactivate(libbe_handle_t *lbh, const char *ds)
+int
+be_deactivate(libbe_handle_t *lbh, const char *ds, bool temporary)
{
zfs_handle_t *zfs;
+ if (temporary) {
+ return (lzbe_set_boot_device(
+ zpool_get_name(lbh->active_phandle), NULL));
+ }
+
if ((zfs = zfs_open(lbh->lzh, ds, ZFS_TYPE_DATASET)) == NULL)
return (1);
if (zfs_prop_set(zfs, "canmount", "noauto") != 0)
@@ -1268,10 +1246,8 @@
be_activate(libbe_handle_t *lbh, const char *bootenv, bool temporary)
{
char be_path[BE_MAXPATHLEN];
- char buf[BE_MAXPATHLEN];
- nvlist_t *config, *dsprops, *vdevs;
+ nvlist_t *dsprops;
char *origin;
- uint64_t pool_guid;
zfs_handle_t *zhp;
int err;
@@ -1282,27 +1258,10 @@
return (set_error(lbh, err));
if (temporary) {
- config = zpool_get_config(lbh->active_phandle, NULL);
- if (config == NULL)
- /* config should be fetchable... */
- return (set_error(lbh, BE_ERR_UNKNOWN));
-
- if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID,
- &pool_guid) != 0)
- /* Similarly, it shouldn't be possible */
- return (set_error(lbh, BE_ERR_UNKNOWN));
-
- /* Expected format according to zfsbootcfg(8) man */
- snprintf(buf, sizeof(buf), "zfs:%s:", be_path);
-
- /* We have no config tree */
- if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
- &vdevs) != 0)
- return (set_error(lbh, BE_ERR_NOPOOL));
-
- return (be_set_nextboot(lbh, vdevs, pool_guid, buf));
+ return (lzbe_set_boot_device(
+ zpool_get_name(lbh->active_phandle), be_path));
} else {
- if (be_deactivate(lbh, lbh->bootfs) != 0)
+ if (be_deactivate(lbh, lbh->bootfs, false) != 0)
return (-1);
/* Obtain bootenv zpool */
Index: lib/libbe/be_impl.h
===================================================================
--- lib/libbe/be_impl.h
+++ lib/libbe/be_impl.h
@@ -63,6 +63,7 @@
nvlist_t *list;
libbe_handle_t *lbh;
bool single_object; /* list will contain props directly */
+ char *bootonce;
} prop_data_t;
int prop_list_builder_cb(zfs_handle_t *, void *);
Index: lib/libbe/be_info.c
===================================================================
--- lib/libbe/be_info.c
+++ lib/libbe/be_info.c
@@ -30,6 +30,7 @@
__FBSDID("$FreeBSD$");
#include <sys/zfs_context.h>
+#include <libzfsbootenv.h>
#include "be.h"
#include "be_impl.h"
@@ -108,6 +109,7 @@
data.lbh = lbh;
data.list = dsnvl;
data.single_object = false;
+ data.bootonce = NULL;
return (be_proplist_update(&data));
}
@@ -121,6 +123,7 @@
data.lbh = lbh;
data.list = props;
data.single_object = true;
+ data.bootonce = NULL;
if ((snap_hdl = zfs_open(lbh->lzh, name,
ZFS_TYPE_FILESYSTEM | ZFS_TYPE_SNAPSHOT)) == NULL)
return (BE_ERR_ZFSOPEN);
@@ -140,6 +143,7 @@
data.lbh = lbh;
data.list = props;
data.single_object = false;
+ data.bootonce = NULL;
if ((ds_hdl = zfs_open(lbh->lzh, name,
ZFS_TYPE_FILESYSTEM)) == NULL)
return (BE_ERR_ZFSOPEN);
@@ -179,6 +183,10 @@
dataset = zfs_get_name(zfs_hdl);
nvlist_add_string(props, "dataset", dataset);
+ if (data->bootonce != NULL &&
+ strcmp(dataset, data->bootonce) == 0)
+ nvlist_add_boolean_value(props, "bootonce", true);
+
name = strrchr(dataset, '/') + 1;
nvlist_add_string(props, "name", name);
@@ -245,6 +253,9 @@
if ((root_hdl = zfs_open(data->lbh->lzh, data->lbh->root,
ZFS_TYPE_FILESYSTEM)) == NULL)
return (BE_ERR_ZFSOPEN);
+
+ (void) lzbe_get_boot_device(zpool_get_name(data->lbh->active_phandle),
+ &data->bootonce);
/* XXX TODO: some error checking here */
zfs_iter_filesystems(root_hdl, prop_list_builder_cb, data);
Index: lib/libbe/libbe.3
===================================================================
--- lib/libbe/libbe.3
+++ lib/libbe/libbe.3
@@ -27,7 +27,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd October 16, 2019
+.Dd July 22, 2020
.Dt LIBBE 3
.Os
.Sh NAME
@@ -78,7 +78,11 @@
.Pp
.Ft int
.Fn be_activate "libbe_handle_t *hdl" "const char *be_name" "bool temporary"
+.Pp
.Ft int
+.Fn be_deactivate "libbe_handle_t *hdl" "const char *be_name" "bool temporary"
+.Pp
+.Ft int
.Fn be_destroy "libbe_handle_t *hdl" "const char *be_name" "int options"
.Pp
.Ft void
@@ -270,8 +274,24 @@
.Fa temporary
flag is set, then it will be active for the next boot only, as done by
.Xr zfsbootcfg 8 .
-Next boot functionality is currently only available when booting in x86 BIOS
-mode.
+.Pp
+The
+.Fn be_deactivate
+function deactivates a boot environment.
+If the
+.Fa temporary
+flag is set, then it will cause removal of boot once configuration, set by
+.Fn be_activate
+function or by
+.Xr zfsbootcfg 8 .
+If the
+.Fa temporary
+flag is not set,
+.Fn be_deactivate
+function will set zfs
+.Dv canmount
+property to
+.Dv noauto .
.Pp
The
.Fn be_destroy
Index: lib/libzfsbootenv/Makefile
===================================================================
--- /dev/null
+++ lib/libzfsbootenv/Makefile
@@ -0,0 +1,30 @@
+#! $FreeBSD$
+
+.include <src.opts.mk>
+
+PACKAGE= runtime
+LIB= zfsbootenv
+SHLIB_MAJOR= 0
+WARNS?= 1
+
+LIBADD+= zfs
+LIBADD+= nvpair
+
+INCS= libzfsbootenv.h
+SRCS= lzbe_device.c lzbe_util.c lzbe_pair.c
+
+CFLAGS+= -I${.CURDIR}
+CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/include
+CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/lib/libumem
+CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzfs/common
+CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzfs_core/common
+CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzpool/common
+CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libnvpair
+CFLAGS+= -I${SRCTOP}/sys/cddl/compat/opensolaris
+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
+
+CFLAGS+= -DNEED_SOLARIS_BOOLEAN
+
+.include <bsd.lib.mk>
Index: lib/libzfsbootenv/libzfsbootenv.h
===================================================================
--- /dev/null
+++ lib/libzfsbootenv/libzfsbootenv.h
@@ -0,0 +1,47 @@
+/*-
+ * Copyright 2020 Toomas Soome <tsoome@me.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _LIBZFSBOOTENV_H
+#define _LIBZFSBOOTENV_H
+
+#include <stdio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int lzbe_set_pair(const char *, const char *, const char *,
+ const char *);
+extern int lzbe_set_boot_device(const char *, const char *);
+extern int lzbe_get_boot_device(const char *, char **);
+extern int lzbe_bootenv_print(const char *, FILE *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBZFSBOOTENV_H */
Index: lib/libzfsbootenv/lzbe_device.c
===================================================================
--- /dev/null
+++ lib/libzfsbootenv/lzbe_device.c
@@ -0,0 +1,154 @@
+/*-
+ * Copyright 2020 Toomas Soome <tsoome@me.com>
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <string.h>
+#include <libzfs.h>
+#include <libzfsbootenv.h>
+#include <sys/zfs_bootenv.h>
+#include <sys/vdev_impl.h>
+
+/*
+ * Store device name to zpool label bootenv area.
+ */
+int
+lzbe_set_boot_device(const char *pool, const char *device)
+{
+ libzfs_handle_t *hdl;
+ zpool_handle_t *zphdl;
+ nvlist_t *nv;
+ char *descriptor;
+ int rv = -1;
+
+ if (pool == NULL || *pool == '\0')
+ return (rv);
+
+ if ((hdl = libzfs_init()) == NULL) {
+ return (rv);
+ }
+
+ zphdl = zpool_open(hdl, pool);
+ if (zphdl == NULL) {
+ libzfs_fini(hdl);
+ return (rv);
+ }
+
+ rv = zpool_get_bootenv(zphdl, &nv);
+ if (rv != 0)
+ nv = fnvlist_alloc();
+
+ /* version is mandatory */
+ if (!nvlist_exists(nv, BOOTENV_VERSION))
+ fnvlist_add_uint64(nv, BOOTENV_VERSION, VB_NVLIST);
+
+ /*
+ * If device name is empty, remove boot device configuration.
+ */
+ if ((device == NULL || *device == '\0') &&
+ nvlist_exists(nv, OS_BOOTONCE)) {
+ fnvlist_remove(nv, OS_BOOTONCE);
+ } else {
+ /*
+ * Use device name directly if it does start with
+ * prefix "zfs:". Otherwise, add prefix and sufix.
+ */
+ if (strncmp(device, "zfs:", 4) == 0) {
+ fnvlist_add_string(nv, OS_BOOTONCE, device);
+ } else {
+ descriptor = NULL;
+ if (asprintf(&descriptor, "zfs:%s:", device) > 0)
+ fnvlist_add_string(nv, OS_BOOTONCE, descriptor);
+ else
+ rv = ENOMEM;
+ free(descriptor);
+ }
+ }
+
+ rv = zpool_set_bootenv(zphdl, nv);
+ if (rv != 0)
+ fprintf(stderr, "%s\n", libzfs_error_description(hdl));
+
+ fnvlist_free(nv);
+ zpool_close(zphdl);
+ libzfs_fini(hdl);
+ return (rv);
+}
+
+/*
+ * Return boot device name from bootenv, if set.
+ */
+int
+lzbe_get_boot_device(const char *pool, char **device)
+{
+ libzfs_handle_t *hdl;
+ zpool_handle_t *zphdl;
+ nvlist_t *nv;
+ char *val;
+ int rv = -1;
+
+ if (pool == NULL || *pool == '\0' || device == NULL)
+ return (rv);
+
+ if ((hdl = libzfs_init()) == NULL) {
+ return (rv);
+ }
+
+ zphdl = zpool_open(hdl, pool);
+ if (zphdl == NULL) {
+ libzfs_fini(hdl);
+ return (rv);
+ }
+
+ rv = zpool_get_bootenv(zphdl, &nv);
+ if (rv == 0) {
+ rv = nvlist_lookup_string(nv, OS_BOOTONCE, &val);
+ if (rv == 0) {
+ /*
+ * zfs device descriptor is in form of "zfs:dataset:",
+ * we only do need dataset name.
+ */
+ if (strncmp(val, "zfs:", 4) == 0)
+ val += 4;
+ val = strdup(val);
+ if (val != NULL) {
+ size_t len = strlen(val);
+
+ if (val[len - 1] == ':')
+ val[len - 1] = '\0';
+ *device = val;
+ } else {
+ rv = ENOMEM;
+ }
+ }
+ }
+
+ nvlist_free(nv);
+ zpool_close(zphdl);
+ libzfs_fini(hdl);
+ return (rv);
+}
Index: lib/libzfsbootenv/lzbe_pair.c
===================================================================
--- /dev/null
+++ lib/libzfsbootenv/lzbe_pair.c
@@ -0,0 +1,76 @@
+/*-
+ * Copyright 2020 Toomas Soome <tsoome@me.com>
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <string.h>
+#include <libzfs.h>
+#include <libzfsbootenv.h>
+
+/*
+ * Store pair defined by key, type and value.
+ */
+int
+lzbe_set_pair(const char *pool, const char *key, const char *type,
+ const char *value)
+{
+ libzfs_handle_t *hdl;
+ zpool_handle_t *zphdl;
+ nvlist_t *nv;
+ int rv = -1;
+
+ if (pool == NULL || *pool == '\0' || type == NULL)
+ return (rv);
+
+ if ((hdl = libzfs_init()) == NULL) {
+ return (rv);
+ }
+
+ zphdl = zpool_open(hdl, pool);
+ if (zphdl == NULL) {
+ libzfs_fini(hdl);
+ return (rv);
+ }
+
+ rv = zpool_get_bootenv(zphdl, &nv);
+ if (rv == 0) {
+ if (strcmp(type, "DATA_TYPE_STRING") == 0) {
+ if ((value == NULL || *value == '\0') &&
+ nvlist_exists(nv, key)) {
+ fnvlist_remove(nv, key);
+ } else {
+ fnvlist_add_string(nv, key, value);
+ }
+ }
+ }
+ rv = zpool_set_bootenv(zphdl, nv);
+
+ nvlist_free(nv);
+ zpool_close(zphdl);
+ libzfs_fini(hdl);
+ return (rv);
+}
Index: lib/libzfsbootenv/lzbe_util.c
===================================================================
--- /dev/null
+++ lib/libzfsbootenv/lzbe_util.c
@@ -0,0 +1,66 @@
+/*-
+ * Copyright 2020 Toomas Soome <tsoome@me.com>
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <string.h>
+#include <libzfs.h>
+#include <libzfsbootenv.h>
+
+/*
+ * Output bootenv information.
+ */
+int
+lzbe_bootenv_print(const char *pool, FILE *of)
+{
+ libzfs_handle_t *hdl;
+ zpool_handle_t *zphdl;
+ nvlist_t *nv;
+ int rv = -1;
+
+ if (pool == NULL || *pool == '\0' || of == NULL)
+ return (rv);
+
+ if ((hdl = libzfs_init()) == NULL) {
+ return (rv);
+ }
+
+ zphdl = zpool_open(hdl, pool);
+ if (zphdl == NULL) {
+ libzfs_fini(hdl);
+ return (rv);
+ }
+
+ rv = zpool_get_bootenv(zphdl, &nv);
+ if (rv == 0)
+ nvlist_print(of, nv);
+
+ nvlist_free(nv);
+ zpool_close(zphdl);
+ libzfs_fini(hdl);
+ return (rv);
+}
Index: rescue/rescue/Makefile
===================================================================
--- rescue/rescue/Makefile
+++ rescue/rescue/Makefile
@@ -129,7 +129,7 @@
CRUNCH_LIBS+= -l80211 -lalias -lcam -lncursesw -ldevstat -lipsec -llzma
.if ${MK_ZFS} != "no"
CRUNCH_LIBS+= -lavl -lzpool -lzfs_core -lzfs -lnvpair -lpthread -luutil -lumem
-CRUNCH_LIBS+= -lbe
+CRUNCH_LIBS+= -lbe -lzfsbootenv
.else
# liblzma needs pthread
CRUNCH_LIBS+= -lpthread
Index: sbin/bectl/Makefile
===================================================================
--- sbin/bectl/Makefile
+++ sbin/bectl/Makefile
@@ -11,7 +11,9 @@
LIBADD+= jail
LIBADD+= nvpair
LIBADD+= util
+LIBADD+= zfsbootenv
+CFLAGS+= -I${SRCTOP}/lib/libzfsbootenv
CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzfs/common
CFLAGS+= -I${SRCTOP}/sys/cddl/compat/opensolaris
CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common
Index: sbin/bectl/bectl.8
===================================================================
--- sbin/bectl/bectl.8
+++ sbin/bectl/bectl.8
@@ -17,7 +17,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd April 18, 2020
+.Dd July 18, 2020
.Dt BECTL 8
.Os
.Sh NAME
@@ -26,7 +26,7 @@
.Sh SYNOPSIS
.Nm
.Cm activate
-.Op Fl t
+.Op Fl t | Fl T
.Ar beName
.Nm
.Cm check
@@ -95,15 +95,22 @@
.Bl -tag -width activate
.It Xo
.Cm activate
-.Op Fl t
+.Op Fl t | Fl T
.Ar beName
.Xc
Activate the given
.Ar beName
as the default boot filesystem.
If the
-.Op Fl t
+.Fl t
flag is given, this takes effect only for the next boot.
+Flag
+.Fl T
+removes temporary boot once configuration.
+Without temporary configuration, the next boot will use zfs dataset specified
+in boot pool
+.Ar bootfs
+property.
.It Xo
.Cm check
.Xc
@@ -260,8 +267,10 @@
.Pq Em \&N ;
active on reboot
.Pq Em \&R ;
-or both
-.Pq Em \&NR .
+is used on next boot once
+.Pq Em \&T ;
+or combination of
+.Pq Em \&NRT .
.Pp
.Bl -tag -width indent
.It Fl a
Index: sbin/bectl/bectl.c
===================================================================
--- sbin/bectl/bectl.c
+++ sbin/bectl/bectl.c
@@ -72,6 +72,7 @@
"\tbectl add (path)*\n"
#endif
"\tbectl activate [-t] beName\n"
+ "\tbectl activate [-T]\n"
"\tbectl check\n"
"\tbectl create [-r] [-e {nonActiveBe | beName@snapshot}] beName\n"
"\tbectl create [-r] beName@snapshot\n"
@@ -139,14 +140,22 @@
bectl_cmd_activate(int argc, char *argv[])
{
int err, opt;
- bool temp;
+ bool temp, reset;
temp = false;
- while ((opt = getopt(argc, argv, "t")) != -1) {
+ reset = false;
+ while ((opt = getopt(argc, argv, "tT")) != -1) {
switch (opt) {
case 't':
+ if (reset)
+ return (usage(false));
temp = true;
break;
+ case 'T':
+ if (temp)
+ return (usage(false));
+ reset = true;
+ break;
default:
fprintf(stderr, "bectl activate: unknown option '-%c'\n",
optopt);
@@ -157,11 +166,18 @@
argc -= optind;
argv += optind;
- if (argc != 1) {
+ if (argc != 1 && (!reset || argc != 0)) {
fprintf(stderr, "bectl activate: wrong number of arguments\n");
return (usage(false));
}
+ if (reset) {
+ if ((err = be_deactivate(be, NULL, reset)) == 0)
+ printf("Temporary activation removed\n");
+ else
+ printf("Failed to remove temporary activation\n");
+ return (err);
+ }
/* activate logic goes here */
if ((err = be_activate(be, argv[0], temp)) != 0)
Index: sbin/bectl/bectl_list.c
===================================================================
--- sbin/bectl/bectl_list.c
+++ sbin/bectl/bectl_list.c
@@ -182,7 +182,7 @@
const char *oname;
char *dsname, *propstr;
int active_colsz;
- boolean_t active_now, active_reboot;
+ boolean_t active_now, active_reboot, bootonce;
dsname = NULL;
originprops = NULL;
@@ -228,6 +228,11 @@
if (nvlist_lookup_boolean_value(dsprops, "nextboot",
&active_reboot) == 0 && active_reboot) {
printf("R");
+ active_colsz--;
+ }
+ if (nvlist_lookup_boolean_value(dsprops, "bootonce",
+ &bootonce) == 0 && bootonce) {
+ printf("T");
active_colsz--;
}
if (active_colsz == pc->active_colsz_def) {
Index: sbin/zfsbootcfg/Makefile
===================================================================
--- sbin/zfsbootcfg/Makefile
+++ sbin/zfsbootcfg/Makefile
@@ -2,26 +2,10 @@
# $FreeBSD$
PROG= zfsbootcfg
-WARNS?= 1
MAN= zfsbootcfg.8
-LIBADD+=zfs
-LIBADD+=nvpair
-LIBADD+=umem
-LIBADD+=uutil
-LIBADD+=geom
+LIBADD+=zfsbootenv
-CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/include
-CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/lib/libumem
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzfs/common
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzfs_core/common
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzpool/common
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libnvpair
-CFLAGS+= -I${SRCTOP}/sys/cddl/compat/opensolaris
-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
-
-CFLAGS+= -DNEED_SOLARIS_BOOLEAN
+CFLAGS+=-I${SRCTOP}/lib/libzfsbootenv
.include <bsd.prog.mk>
Index: sbin/zfsbootcfg/zfsbootcfg.8
===================================================================
--- sbin/zfsbootcfg/zfsbootcfg.8
+++ sbin/zfsbootcfg/zfsbootcfg.8
@@ -24,7 +24,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd May 24, 2017
+.Dd July 22, 2020
.Dt ZFSBOOTCFG 8
.Os
.Sh NAME
@@ -33,39 +33,69 @@
.Sh SYNOPSIS
.Nm
.Ao Ar options Ac
+.Nm
+.Op Fl k Ar key
+.Op Fl p
+.Op Fl t Ar type
+.Op Fl v Ar value
+.Op Fl z Ar pool
+.Nm
.Sh DESCRIPTION
.Nm
is used to set
.Xr boot.config 5 Ns -style
options to be used by
-.Xr zfsboot 8
-or
+.Xr zfsboot 8 ,
.Xr gptzfsboot 8
+or
+.Xr loader 8
the next time the machine is booted.
Once
.Xr zfsboot 8
or
.Xr gptzfsboot 8
+or
+.Xr loader 8
reads the information, it is deleted.
If booting fails, the machine automatically reverts to the previous
boot configuration.
-The information is stored in a special reserved area of a ZFS pool.
-.Xr zfsboot 8
-or
-.Xr gptzfsboot 8
-read the boot option information from the first disk found in the first
-ZFS pool found.
+The information is stored in a special boot environment area of a ZFS pool.
+.Pp
+If used without arguments,
+.Nm
+will output the current boot configuration, if set.
+.Pp
+The following options are supported by
+.Nm :
+.Bl -tag -width indent
+.It Fl k Ar key
+Define key for
+.Ao key , value Ac
+pair.
+.It Fl p
+Print all information stored in ZFS pool bootenv area.
+.It Fl t Ar type
+Set type of
+.Ar value
+used in
+.Ao key , value Ac
+pair.
+Currently, the only supported type is
+.Ar DATA_TYPE_STRING .
+.It Fl v Ar value
+Define value for
+.Ao key , value Ac
+pair.
+.It Fl z Ar pool
+Operate on
+.Ar pool .
+.El
.Sh ENVIRONMENT
-.Bl -tag -width vfs.zfs.boot.primary_pool -compact
-.It Ev vfs.zfs.boot.primary_pool
+.Bl -tag -width vfs.root.mountfrom -compact
+.It Ev vfs.root.mountfrom
The
.Xr kenv 1
variable that identifies a pool for which the options are written.
-.It Ev vfs.zfs.boot.primary_vdev
-The
-.Xr kenv 1
-variable that identifies a disk within the pool where the options
-are written.
.El
.Sh EXAMPLES
Try to boot to a new
@@ -81,7 +111,9 @@
.Dl "zfsbootcfg """"
.Sh SEE ALSO
.Xr boot.config 5 ,
+.Xr bectl 8 ,
.Xr gptzfsboot 8 ,
+.Xr loader 8 ,
.Xr zfsboot 8
.Sh HISTORY
.Nm
@@ -90,23 +122,3 @@
.Sh AUTHORS
This manual page was written by
.An Andriy Gapon Aq Mt avg@FreeBSD.org .
-.Sh CAVEATS
-At the moment,
-.Nm
-uses the
-.Ev vfs.zfs.boot.primary_pool
-and
-.Ev vfs.zfs.boot.primary_vdev
-.Xr kenv 1
-variables to determine a ZFS pool and a disk in it where the options
-are to be stored.
-The variables are set by the ZFS boot chain, so there is an assumption
-that the same boot disk is going to be used for the next reboot.
-There is no
-.Nm
-option to specify a different pool or a different disk.
-.Pp
-.Nm
-should be extended to install new
-.Xr zfsboot 8
-blocks in a ZFS pool.
Index: sbin/zfsbootcfg/zfsbootcfg.c
===================================================================
--- sbin/zfsbootcfg/zfsbootcfg.c
+++ sbin/zfsbootcfg/zfsbootcfg.c
@@ -32,115 +32,105 @@
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
+#include <stdbool.h>
#include <string.h>
#include <kenv.h>
+#include <unistd.h>
-#include <libzfs.h>
+#include <libzfsbootenv.h>
-/* Keep in sync with zfsboot.c. */
-#define MAX_COMMAND_LEN 512
+#ifndef ZFS_MAXNAMELEN
+#define ZFS_MAXNAMELEN 256
+#endif
int
-install_bootonce(libzfs_handle_t *hdl, uint64_t pool_guid, nvlist_t *nv,
- const char * const data)
+main(int argc, char * const *argv)
{
- nvlist_t **child;
- uint_t children = 0;
- uint64_t guid;
+ char buf[ZFS_MAXNAMELEN], *name;
+ const char *key, *value, *type;
int rv;
+ bool print;
- (void) nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN, &child,
- &children);
-
- for (int c = 0; c < children; c++) {
- rv = install_bootonce(hdl, pool_guid, child[c], data);
+ name = NULL;
+ key = NULL;
+ type = NULL;
+ value = NULL;
+ print = false;
+ while ((rv = getopt(argc, argv, "k:pt:v:z:")) != -1) {
+ switch (rv) {
+ case 'k':
+ key = optarg;
+ break;
+ case 'p':
+ print = true;
+ break;
+ case 't':
+ type = optarg;
+ break;
+ case 'v':
+ value = optarg;
+ break;
+ case 'z':
+ name = optarg;
+ break;
+ }
}
- if (children > 0)
- return (rv);
+ argc -= optind;
+ argv += optind;
- if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID, &guid) != 0) {
- perror("can't get vdev guid");
- return (1);
- }
- if (zpool_nextboot(hdl, pool_guid, guid, data) != 0) {
- perror("ZFS_IOC_NEXTBOOT failed");
- return (1);
- }
- return (0);
-}
+ if (argc == 1)
+ value = argv[0];
-int main(int argc, const char * const *argv)
-{
- char buf[32], *name;
- libzfs_handle_t *hdl;
- zpool_handle_t *zphdl;
- uint64_t pool_guid;
- nvlist_t *nv, *config;
- int rv;
- int len;
-
- if (argc != 2) {
+ if (argc > 1) {
fprintf(stderr, "usage: zfsbootcfg <boot.config(5) options>\n");
return (1);
}
- len = strlen(argv[1]);
- if (len >= MAX_COMMAND_LEN) {
- fprintf(stderr, "options string is too long\n");
- return (1);
- }
+ if (name == NULL) {
+ rv = kenv(KENV_GET, "vfs.root.mountfrom", buf, sizeof(buf));
+ if (rv <= 0) {
+ perror("can't get vfs.root.mountfrom");
+ return (1);
+ }
- if (kenv(KENV_GET, "vfs.root.mountfrom", buf, sizeof(buf)) <= 0) {
- perror("can't get vfs.root.mountfrom");
- return (1);
+ if (strncmp(buf, "zfs:", 4) == 0) {
+ name = strchr(buf + 4, '/');
+ if (name != NULL)
+ *name = '\0';
+ name = buf + 4;
+ } else {
+ perror("not a zfs root");
+ return (1);
+ }
}
- if (strncmp(buf, "zfs:", 4) == 0) {
- name = strchr(buf + 4, '/');
- if (name != NULL)
- *name = '\0';
- name = buf + 4;
- } else {
- perror("not a zfs root");
- return (1);
- }
-
- if ((hdl = libzfs_init()) == NULL) {
- (void) fprintf(stderr, "internal error: failed to "
- "initialize ZFS library\n");
- return (1);
- }
+ rv = 0;
+ if (key != NULL || value != NULL) {
+ if (type == NULL)
+ type = "DATA_TYPE_STRING";
- zphdl = zpool_open(hdl, name);
- if (zphdl == NULL) {
- perror("can't open pool");
- libzfs_fini(hdl);
- return (1);
- }
+ if (key == NULL || strcmp(key, "command") == 0)
+ rv = lzbe_set_boot_device(name, value);
+ else
+ rv = lzbe_set_pair(name, key, type, value);
- pool_guid = zpool_get_prop_int(zphdl, ZPOOL_PROP_GUID, NULL);
+ if (rv == 0)
+ printf("zfs bootenv is successfully written\n");
+ else
+ printf("error: %d\n", rv);
+ } else if (!print) {
+ char *ptr;
- config = zpool_get_config(zphdl, NULL);
- if (config == NULL) {
- perror("can't get pool config");
- zpool_close(zphdl);
- libzfs_fini(hdl);
- return (1);
+ if (lzbe_get_boot_device(name, &ptr) == 0) {
+ printf("zfs:%s:\n", ptr);
+ free(ptr);
+ }
}
- if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, &nv) != 0) {
- perror("failed to get vdev tree");
- zpool_close(zphdl);
- libzfs_fini(hdl);
- return (1);
+ if (print) {
+ rv = lzbe_bootenv_print(name, stdout);
}
- rv = install_bootonce(hdl, pool_guid, nv, argv[1]);
-
- zpool_close(zphdl);
- libzfs_fini(hdl);
- if (rv == 0)
- printf("zfs next boot options are successfully written\n");
return (rv);
}
Index: share/mk/bsd.libnames.mk
===================================================================
--- share/mk/bsd.libnames.mk
+++ share/mk/bsd.libnames.mk
@@ -165,6 +165,7 @@
LIBZ?= ${LIBDESTDIR}${LIBDIR_BASE}/libz.a
LIBZFS?= ${LIBDESTDIR}${LIBDIR_BASE}/libzfs.a
LIBZFS_CORE?= ${LIBDESTDIR}${LIBDIR_BASE}/libzfs_core.a
+LIBZFSBOOTENV?= ${LIBDESTDIR}${LIBDIR_BASE}/libzfsbootenv.a
LIBZPOOL?= ${LIBDESTDIR}${LIBDIR_BASE}/libzpool.a
# enforce the 2 -lpthread and -lc to always be the last in that exact order
Index: share/mk/src.libnames.mk
===================================================================
--- share/mk/src.libnames.mk
+++ share/mk/src.libnames.mk
@@ -197,6 +197,7 @@
z \
zfs_core \
zfs \
+ zfsbootenv \
zpool \
.if ${MK_BLACKLIST} != "no"
@@ -376,9 +377,10 @@
_DP_ipf= kvm
_DP_zfs= md pthread umem util uutil m nvpair avl bsdxml geom nvpair z \
zfs_core
+_DP_zfsbootenv= zfs nvpair
_DP_zfs_core= nvpair
_DP_zpool= md pthread z nvpair avl umem
-_DP_be= zfs nvpair
+_DP_be= zfs nvpair zfsbootenv
# OFED support
.if ${MK_OFED} != "no"
@@ -587,6 +589,7 @@
LIBUUTILDIR= ${OBJTOP}/cddl/lib/libuutil
LIBZFSDIR= ${OBJTOP}/cddl/lib/libzfs
LIBZFS_COREDIR= ${OBJTOP}/cddl/lib/libzfs_core
+LIBZFSBOOTENVDIR= ${OBJTOP}/lib/libzfsbootenv
LIBZPOOLDIR= ${OBJTOP}/cddl/lib/libzpool
# OFED support
Index: stand/efi/boot1/Makefile
===================================================================
--- stand/efi/boot1/Makefile
+++ stand/efi/boot1/Makefile
@@ -37,6 +37,7 @@
CFLAGS.zfs_module.c+= -I${SYSDIR}/cddl/boot/zfs
CFLAGS.zfs_module.c+= -I${SYSDIR}/crypto/skein
CFLAGS.zfs_module.c+= -I${SYSDIR}/cddl/contrib/opensolaris/uts/common
+CFLAGS.zfs_module.c+= -I${SYSDIR}/cddl/contrib/opensolaris/uts/common/fs/zfs
CFLAGS.zfs_module.c+= -I${SYSDIR}/cddl/contrib/opensolaris/common/lz4
CFLAGS+= -DEFI_ZFS_BOOT
Index: stand/efi/boot1/zfs_module.c
===================================================================
--- stand/efi/boot1/zfs_module.c
+++ stand/efi/boot1/zfs_module.c
@@ -124,7 +124,7 @@
}
memcpy(tdev, dev, sizeof(*dev));
- if (vdev_probe(vdev_read, tdev, &spa) != 0) {
+ if (vdev_probe(vdev_read, NULL, tdev, &spa) != 0) {
free(tdev);
return (EFI_UNSUPPORTED);
}
Index: stand/efi/loader/Makefile
===================================================================
--- stand/efi/loader/Makefile
+++ stand/efi/loader/Makefile
@@ -27,6 +27,7 @@
CFLAGS+= -I${.CURDIR}/../loader
.if ${MK_LOADER_ZFS} != "no"
CFLAGS+= -I${ZFSSRC}
+CFLAGS+= -I${SYSDIR}/cddl/contrib/opensolaris/uts/common/fs/zfs
CFLAGS+= -DEFI_ZFS_BOOT
HAVE_ZFS= yes
.endif
Index: stand/efi/loader/main.c
===================================================================
--- stand/efi/loader/main.c
+++ stand/efi/loader/main.c
@@ -36,6 +36,7 @@
#include <sys/param.h>
#include <sys/reboot.h>
#include <sys/boot.h>
+#include <sys/zfs_bootenv.h>
#include <paths.h>
#include <stdint.h>
#include <string.h>
@@ -275,9 +276,11 @@
if (rv) {
buf = malloc(VDEV_PAD_SIZE);
if (buf != NULL) {
- if (zfs_nextboot(&currdev, buf, VDEV_PAD_SIZE) == 0) {
- printf("zfs nextboot: %s\n", buf);
+ if (zfs_get_bootonce(&currdev, OS_BOOTONCE, buf,
+ VDEV_PAD_SIZE) == 0) {
+ printf("zfs bootonce: %s\n", buf);
set_currdev(buf);
+ setenv("zfs-bootonce", buf, 1);
}
free(buf);
}
Index: stand/i386/gptzfsboot/Makefile
===================================================================
--- stand/i386/gptzfsboot/Makefile
+++ stand/i386/gptzfsboot/Makefile
@@ -33,6 +33,7 @@
-I${SYSDIR}/crypto/skein \
-I${SYSDIR}/cddl/boot/zfs \
-I${SYSDIR}/cddl/contrib/opensolaris/uts/common \
+ -I${SYSDIR}/cddl/contrib/opensolaris/uts/common/fs/zfs \
-I${SYSDIR}/cddl/contrib/opensolaris/common/lz4 \
-I${BOOTSRC}/i386/btx/lib \
-I${BOOTSRC}/i386/boot2 \
Index: stand/i386/loader/Makefile
===================================================================
--- stand/i386/loader/Makefile
+++ stand/i386/loader/Makefile
@@ -37,6 +37,10 @@
LIBFIREWIRE= ${BOOTOBJ}/i386/libfirewire/libfirewire.a
.endif
+.if ${MK_LOADER_ZFS} == "yes"
+CFLAGS.main.c+= -I${SYSDIR}/cddl/contrib/opensolaris/uts/common/fs/zfs
+.endif
+
.if exists(${.CURDIR}/help.i386)
HELP_FILES= ${.CURDIR}/help.i386
.endif
Index: stand/i386/loader/main.c
===================================================================
--- stand/i386/loader/main.c
+++ stand/i386/loader/main.c
@@ -40,6 +40,7 @@
#include <machine/psl.h>
#include <sys/disk.h>
#include <sys/reboot.h>
+#include <sys/zfs_bootenv.h>
#include <common/drv.h>
#include "bootstrap.h"
@@ -274,6 +275,7 @@
struct i386_devdesc new_currdev;
#ifdef LOADER_ZFS_SUPPORT
char buf[20];
+ char *bootonce;
#endif
int biosdev = -1;
@@ -321,6 +323,15 @@
new_currdev.d_kind.zfs.root_guid = 0;
}
new_currdev.dd.d_dev = &zfs_dev;
+
+ if ((bootonce = malloc(VDEV_PAD_SIZE)) != NULL) {
+ if (zfs_get_bootonce(&new_currdev, OS_BOOTONCE_USED,
+ bootonce, VDEV_PAD_SIZE) == 0) {
+ setenv("zfs-bootonce", bootonce, 1);
+ }
+ free(bootonce);
+ }
+
#endif
} else if ((initial_bootdev & B_MAGICMASK) != B_DEVMAGIC) {
/* The passed-in boot device is bad */
Index: stand/i386/zfsboot/Makefile
===================================================================
--- stand/i386/zfsboot/Makefile
+++ stand/i386/zfsboot/Makefile
@@ -32,6 +32,7 @@
-I${SYSDIR}/crypto/skein \
-I${SYSDIR}/cddl/boot/zfs \
-I${SYSDIR}/cddl/contrib/opensolaris/uts/common \
+ -I${SYSDIR}/cddl/contrib/opensolaris/uts/common/fs/zfs \
-I${SYSDIR}/cddl/contrib/opensolaris/common/lz4 \
-I${BOOTSRC}/i386/boot2 \
-Wall -Waggregate-return -Wbad-function-cast -Wno-cast-align \
Index: stand/i386/zfsboot/zfsboot.c
===================================================================
--- stand/i386/zfsboot/zfsboot.c
+++ stand/i386/zfsboot/zfsboot.c
@@ -26,6 +26,7 @@
#endif
#include <sys/reboot.h>
#include <sys/queue.h>
+#include <sys/zfs_bootenv.h>
#include <machine/bootinfo.h>
#include <machine/elf.h>
@@ -218,16 +219,26 @@
if (bdev != NULL && bdev->dd.d_dev->dv_type == DEVT_ZFS) {
/* set up proper device name string for ZFS */
strncpy(boot_devname, zfs_fmtdev(bdev), sizeof (boot_devname));
- if (zfs_nextboot(bdev, cmd, sizeof(cmd)) == 0) {
+ if (zfs_get_bootonce(bdev, OS_BOOTONCE, cmd,
+ sizeof(cmd)) == 0) {
+ nvlist_t *benv;
+
nextboot = 1;
memcpy(cmddup, cmd, sizeof(cmd));
if (parse_cmd()) {
if (!OPT_CHECK(RBX_QUIET))
- printf("failed to parse pad2 area\n");
+ printf("failed to parse bootonce "
+ "command\n");
exit(0);
}
if (!OPT_CHECK(RBX_QUIET))
- printf("zfs nextboot: %s\n", cmddup);
+ printf("zfs bootonce: %s\n", cmddup);
+
+ if (zfs_get_bootenv(bdev, &benv) == 0) {
+ nvlist_add_string(benv, OS_BOOTONCE_USED,
+ cmddup);
+ zfs_set_bootenv(bdev, benv);
+ }
/* Do not process this command twice */
*cmd = 0;
}
Index: stand/libsa/zfs/Makefile.inc
===================================================================
--- stand/libsa/zfs/Makefile.inc
+++ stand/libsa/zfs/Makefile.inc
@@ -10,8 +10,9 @@
CFLAGS+= -I${LDRSRC}
CFLAGS+= -I${SYSDIR}/cddl/boot/zfs
CFLAGS+= -I${SYSDIR}/cddl/contrib/opensolaris/uts/common
+CFLAGS+= -I${SYSDIR}/cddl/contrib/opensolaris/uts/common/fs/zfs
CFLAGS+= -I${SYSDIR}/crypto/skein
-CFLAGS.zfs.c+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/common/lz4
+CFLAGS.zfs.c+= -I${SYSDIR}/cddl/contrib/opensolaris/common/lz4
CFLAGS+= -Wformat -Wall
Index: stand/libsa/zfs/libzfs.h
===================================================================
--- stand/libsa/zfs/libzfs.h
+++ stand/libsa/zfs/libzfs.h
@@ -26,15 +26,15 @@
* $FreeBSD$
*/
+#ifndef _BOOT_LIBZFS_H_
+#define _BOOT_LIBZFS_H_
+
#include <zfsimpl.h>
#ifdef LOADER_GELI_SUPPORT
#include <crypto/intake.h>
#endif
-#ifndef _BOOT_LIBZFS_H_
-#define _BOOT_LIBZFS_H_
-
#define ZFS_MAXNAMELEN 256
/*
@@ -54,6 +54,7 @@
#define NV_UNIQUE_NAME_TYPE 0x2
#define NV_ALIGN4(x) (((x) + 3) & ~3)
+#define NV_ALIGN(x) (((x) + 7) & ~7)
/*
* nvlist header.
@@ -109,17 +110,22 @@
nvlist_t *nvlist_create(int);
void nvlist_destroy(nvlist_t *);
-nvlist_t *nvlist_import(const uint8_t *, char, char);
+nvlist_t *nvlist_import(const char *);
+int nvlist_export(nvlist_t *);
int nvlist_remove(nvlist_t *, const char *, data_type_t);
void nvlist_print(nvlist_t *, unsigned int);
int nvlist_find(const nvlist_t *, const char *, data_type_t,
int *, void *, int *);
int nvlist_next(nvlist_t *);
+int nvlist_add_string(nvlist_t *, const char *, const char *);
+int nvlist_add_uint64(nvlist_t *, const char *, uint64_t);
int zfs_parsedev(struct zfs_devdesc *dev, const char *devspec,
const char **path);
char *zfs_fmtdev(void *vdev);
-int zfs_nextboot(void *vdev, char *buf, size_t size);
+int zfs_get_bootonce(void *, const char *, char *, size_t);
+int zfs_get_bootenv(void *, nvlist_t **);
+int zfs_set_bootenv(void *, nvlist_t *);
int zfs_probe_dev(const char *devname, uint64_t *pool_guid);
int zfs_list(const char *name);
uint64_t ldi_get_size(void *);
@@ -127,6 +133,8 @@
int zfs_bootenv(const char *name);
int zfs_belist_add(const char *name, uint64_t __unused);
int zfs_set_env(void);
+
+nvlist_t *vdev_read_bootenv(vdev_t *);
extern struct devsw zfs_dev;
extern struct fs_ops zfs_fsops;
Index: stand/libsa/zfs/nvlist.c
===================================================================
--- stand/libsa/zfs/nvlist.c
+++ stand/libsa/zfs/nvlist.c
@@ -28,128 +28,234 @@
#include <stand.h>
#include <sys/endian.h>
+#include <sys/stdint.h>
#include <zfsimpl.h>
#include "libzfs.h"
+enum xdr_op {
+ XDR_OP_ENCODE = 1,
+ XDR_OP_DECODE = 2
+};
+
typedef struct xdr {
- int (*xdr_getint)(const struct xdr *, const void *, int *);
+ enum xdr_op xdr_op;
+ int (*xdr_getint)(const void *, int *);
+ int (*xdr_putint)(void *, int);
+ int (*xdr_getuint)(const void *, unsigned *);
+ int (*xdr_putuint)(void *, unsigned);
} xdr_t;
-static int xdr_int(const xdr_t *, const void *, int *);
-static int mem_int(const xdr_t *, const void *, int *);
-static void nvlist_decode_nvlist(const xdr_t *, nvlist_t *);
+static int nvlist_xdr_nvlist(const xdr_t *, nvlist_t *);
static int nvlist_size(const xdr_t *, const uint8_t *);
+static int xdr_int(const xdr_t *, void *, int *);
+static int xdr_u_int(const xdr_t *, void *, unsigned *);
-/*
- * transform data from network to host.
- */
-xdr_t ntoh = {
- .xdr_getint = xdr_int
-};
+/* Basic primitives for XDR translation operations, getint and putint. */
+static int
+_getint(const void *buf, int *ip)
+{
+ *ip = be32dec(buf);
+ return (sizeof(int));
+}
+static int
+_putint(void *buf, int i)
+{
+ int *ip = buf;
+
+ *ip = htobe32(i);
+ return (sizeof(int));
+}
+
+static int
+_getuint(const void *buf, unsigned *ip)
+{
+ *ip = be32dec(buf);
+ return (sizeof(unsigned));
+}
+
+static int
+_putuint(void *buf, unsigned i)
+{
+ unsigned *up = buf;
+
+ *up = htobe32(i);
+ return (sizeof(int));
+}
+
/*
- * transform data from host to host.
+ * read native data without translation.
*/
-xdr_t native = {
- .xdr_getint = mem_int
-};
+static int
+mem_int(const void *buf, int *i)
+{
+ *i = *(int *)buf;
+ return (sizeof(int));
+}
+static int
+mem_uint(const void *buf, unsigned *u)
+{
+ *u = *(int *)buf;
+ return (sizeof(int));
+}
+
/*
- * transform data from host to network.
+ * XDR data translations.
*/
-xdr_t hton = {
- .xdr_getint = xdr_int
-};
-
static int
-xdr_short(const xdr_t *xdr, const uint8_t *buf, short *ip)
+xdr_short(const xdr_t *xdr, uint8_t *buf, short *ip)
{
- int i, rv;
+ int i, rv = 0;
- rv = xdr->xdr_getint(xdr, buf, &i);
- *ip = i;
+ i = *ip;
+ rv = xdr_int(xdr, buf, &i);
+ if (xdr->xdr_op == XDR_OP_DECODE) {
+ *ip = i;
+ }
return (rv);
}
static int
-xdr_u_short(const xdr_t *xdr, const uint8_t *buf, unsigned short *ip)
+xdr_u_short(const xdr_t *xdr, uint8_t *buf, unsigned short *ip)
{
unsigned u;
int rv;
- rv = xdr->xdr_getint(xdr, buf, &u);
- *ip = u;
+ u = *ip;
+ rv = xdr_u_int(xdr, buf, &u);
+ if (xdr->xdr_op == XDR_OP_DECODE) {
+ *ip = u;
+ }
return (rv);
}
static int
-xdr_int(const xdr_t *xdr __unused, const void *buf, int *ip)
+xdr_int(const xdr_t *xdr, void *buf, int *ip)
{
- *ip = be32dec(buf);
- return (sizeof(int));
+ int rv = 0;
+ int *i = buf;
+
+ switch (xdr->xdr_op) {
+ case XDR_OP_ENCODE:
+ /* Encode value *ip, store to buf */
+ rv = xdr->xdr_putint(buf, *ip);
+ break;
+
+ case XDR_OP_DECODE:
+ /* Decode buf, return value to *ip */
+ rv = xdr->xdr_getint(buf, i);
+ *ip = *i;
+ break;
+ }
+ return (rv);
}
static int
-xdr_u_int(const xdr_t *xdr __unused, const void *buf, unsigned *ip)
+xdr_u_int(const xdr_t *xdr, void *buf, unsigned *ip)
{
- *ip = be32dec(buf);
- return (sizeof(unsigned));
+ int rv = 0;
+ unsigned *u = buf;
+
+ switch (xdr->xdr_op) {
+ case XDR_OP_ENCODE:
+ /* Encode value *ip, store to buf */
+ rv = xdr->xdr_putuint(buf, *ip);
+ break;
+
+ case XDR_OP_DECODE:
+ /* Decode buf, return value to *ip */
+ rv = xdr->xdr_getuint(buf, u);
+ *ip = *u;
+ break;
+ }
+ return (rv);
}
static int
xdr_string(const xdr_t *xdr, const void *buf, nv_string_t *s)
{
- int size;
+ int size = 0;
- size = xdr->xdr_getint(xdr, buf, &s->nv_size);
- size = NV_ALIGN4(size + s->nv_size);
+ switch (xdr->xdr_op) {
+ case XDR_OP_ENCODE:
+ size = s->nv_size;
+ size += xdr->xdr_putuint(&s->nv_size, s->nv_size);
+ size = NV_ALIGN4(size);
+ break;
+
+ case XDR_OP_DECODE:
+ size = xdr->xdr_getuint(buf, &s->nv_size);
+ size = NV_ALIGN4(size + s->nv_size);
+ break;
+ }
return (size);
}
static int
-xdr_int64(const xdr_t *xdr, const uint8_t *buf, int64_t *lp)
+xdr_int64(const xdr_t *xdr, uint8_t *buf, int64_t *lp)
{
- int hi, rv;
+ int hi, rv = 0;
unsigned lo;
- rv = xdr->xdr_getint(xdr, buf, &hi);
- rv += xdr->xdr_getint(xdr, buf + rv, &lo);
- *lp = (((int64_t)hi) << 32) | lo;
+ switch (xdr->xdr_op) {
+ case XDR_OP_ENCODE:
+ /* Encode value *lp, store to buf */
+ hi = *lp >> 32;
+ lo = *lp & UINT32_MAX;
+ rv = xdr->xdr_putint(buf, hi);
+ rv += xdr->xdr_putint(buf + rv, lo);
+ break;
+
+ case XDR_OP_DECODE:
+ /* Decode buf, return value to *ip */
+ rv = xdr->xdr_getint(buf, &hi);
+ rv += xdr->xdr_getuint(buf + rv, &lo);
+ *lp = (((int64_t)hi) << 32) | lo;
+ }
return (rv);
}
static int
-xdr_uint64(const xdr_t *xdr, const uint8_t *buf, uint64_t *lp)
+xdr_uint64(const xdr_t *xdr, uint8_t *buf, uint64_t *lp)
{
unsigned hi, lo;
- int rv;
+ int rv = 0;
- rv = xdr->xdr_getint(xdr, buf, &hi);
- rv += xdr->xdr_getint(xdr, buf + rv, &lo);
- *lp = (((int64_t)hi) << 32) | lo;
+ switch (xdr->xdr_op) {
+ case XDR_OP_ENCODE:
+ /* Encode value *ip, store to buf */
+ hi = *lp >> 32;
+ lo = *lp & UINT32_MAX;
+ rv = xdr->xdr_putint(buf, hi);
+ rv += xdr->xdr_putint(buf + rv, lo);
+ break;
+
+ case XDR_OP_DECODE:
+ /* Decode buf, return value to *ip */
+ rv = xdr->xdr_getuint(buf, &hi);
+ rv += xdr->xdr_getuint(buf + rv, &lo);
+ *lp = (((uint64_t)hi) << 32) | lo;
+ }
return (rv);
}
static int
-xdr_char(const xdr_t *xdr, const uint8_t *buf, char *cp)
+xdr_char(const xdr_t *xdr, uint8_t *buf, char *cp)
{
- int i, rv;
+ int i, rv = 0;
- rv = xdr->xdr_getint(xdr, buf, &i);
- *cp = i;
+ i = *cp;
+ rv = xdr_int(xdr, buf, &i);
+ if (xdr->xdr_op == XDR_OP_DECODE) {
+ *cp = i;
+ }
return (rv);
}
/*
- * read native data.
+ * nvlist management functions.
*/
-static int
-mem_int(const xdr_t *xdr, const void *buf, int *i)
-{
- *i = *(int *)buf;
- return (sizeof(int));
-}
-
void
nvlist_destroy(nvlist_t *nvl)
{
@@ -205,30 +311,42 @@
return (nvl);
}
-static void
-nvlist_nvp_decode(const xdr_t *xdr, nvlist_t *nvl, nvp_header_t *nvph)
+static int
+nvlist_xdr_nvp(const xdr_t *xdr, nvlist_t *nvl)
{
nv_string_t *nv_string;
nv_pair_data_t *nvp_data;
nvlist_t nvlist;
+ unsigned type, nelem;
+ xdr_t xdrmem = {
+ .xdr_op = XDR_OP_DECODE,
+ .xdr_getint = mem_int,
+ .xdr_getuint = mem_uint
+ };
nv_string = (nv_string_t *)nvl->nv_idx;
nvl->nv_idx += xdr_string(xdr, &nv_string->nv_size, nv_string);
nvp_data = (nv_pair_data_t *)nvl->nv_idx;
- nvl->nv_idx += xdr_u_int(xdr, &nvp_data->nv_type, &nvp_data->nv_type);
- nvl->nv_idx += xdr_u_int(xdr, &nvp_data->nv_nelem, &nvp_data->nv_nelem);
+ type = nvp_data->nv_type;
+ nelem = nvp_data->nv_nelem;
+ nvl->nv_idx += xdr_u_int(xdr, nvl->nv_idx, &type);
+ nvl->nv_idx += xdr_u_int(xdr, nvl->nv_idx, &nelem);
- switch (nvp_data->nv_type) {
+ switch (type) {
case DATA_TYPE_NVLIST:
case DATA_TYPE_NVLIST_ARRAY:
- bzero(&nvlist, sizeof (nvlist));
+ bzero(&nvlist, sizeof(nvlist));
nvlist.nv_data = &nvp_data->nv_data[0];
nvlist.nv_idx = nvlist.nv_data;
- for (int i = 0; i < nvp_data->nv_nelem; i++) {
- nvlist.nv_asize =
- nvlist_size(xdr, nvlist.nv_data);
- nvlist_decode_nvlist(xdr, &nvlist);
+ for (unsigned i = 0; i < nelem; i++) {
+ if (xdr->xdr_op == XDR_OP_ENCODE)
+ nvlist.nv_asize =
+ nvlist_size(&xdrmem, nvlist.nv_data);
+ else
+ nvlist.nv_asize =
+ nvlist_size(xdr, nvlist.nv_data);
+ nvlist_xdr_nvlist(xdr, &nvlist);
nvl->nv_idx = nvlist.nv_idx;
nvlist.nv_data = nvlist.nv_idx;
}
@@ -282,35 +400,86 @@
break;
}
+ return (0);
}
-static void
-nvlist_decode_nvlist(const xdr_t *xdr, nvlist_t *nvl)
+static int
+nvlist_xdr_nvlist(const xdr_t *xdr, nvlist_t *nvl)
{
nvp_header_t *nvph;
- nvs_data_t *nvs = (nvs_data_t *)nvl->nv_data;
+ nvs_data_t *nvs;
+ unsigned encoded_size, decoded_size;
+ int rv;
nvl->nv_idx = nvl->nv_data;
- nvl->nv_idx += xdr->xdr_getint(xdr, (const uint8_t *)&nvs->nvl_version,
- &nvs->nvl_version);
- nvl->nv_idx += xdr->xdr_getint(xdr, (const uint8_t *)&nvs->nvl_nvflag,
- &nvs->nvl_nvflag);
-
+ nvs = (nvs_data_t *)nvl->nv_data;
nvph = &nvs->nvl_pair;
- nvl->nv_idx += xdr->xdr_getint(xdr,
- (const uint8_t *)&nvph->encoded_size, &nvph->encoded_size);
- nvl->nv_idx += xdr->xdr_getint(xdr,
- (const uint8_t *)&nvph->decoded_size, &nvph->decoded_size);
- while (nvph->encoded_size && nvph->decoded_size) {
- nvlist_nvp_decode(xdr, nvl, nvph);
+ switch (xdr->xdr_op) {
+ case XDR_OP_ENCODE:
+ nvl->nv_idx += xdr->xdr_putuint(nvl->nv_idx,
+ nvs->nvl_version);
+ nvl->nv_idx += xdr->xdr_putuint(nvl->nv_idx,
+ nvs->nvl_nvflag);
- nvph = (nvp_header_t *)(nvl->nv_idx);
- nvl->nv_idx += xdr->xdr_getint(xdr, &nvph->encoded_size,
+ encoded_size = nvph->encoded_size;
+ decoded_size = nvph->decoded_size;
+
+ nvl->nv_idx += xdr->xdr_putuint(nvl->nv_idx,
+ encoded_size);
+ nvl->nv_idx += xdr->xdr_putuint(nvl->nv_idx,
+ decoded_size);
+ break;
+
+ case XDR_OP_DECODE:
+ nvl->nv_idx += xdr->xdr_getuint(nvl->nv_idx,
+ &nvs->nvl_version);
+ nvl->nv_idx += xdr->xdr_getuint(nvl->nv_idx,
+ &nvs->nvl_nvflag);
+
+ nvl->nv_idx += xdr->xdr_getuint(nvl->nv_idx,
&nvph->encoded_size);
- nvl->nv_idx += xdr->xdr_getint(xdr, &nvph->decoded_size,
+ nvl->nv_idx += xdr->xdr_getuint(nvl->nv_idx,
&nvph->decoded_size);
+
+ encoded_size = nvph->encoded_size;
+ decoded_size = nvph->decoded_size;
+ break;
+
+ default:
+ return (EINVAL);
}
+
+ rv = 0;
+ while (encoded_size && decoded_size) {
+ rv = nvlist_xdr_nvp(xdr, nvl);
+ if (rv != 0)
+ return (rv);
+
+ nvph = (nvp_header_t *)(nvl->nv_idx);
+ switch (xdr->xdr_op) {
+ case XDR_OP_ENCODE:
+ encoded_size = nvph->encoded_size;
+ decoded_size = nvph->decoded_size;
+
+ nvl->nv_idx += xdr->xdr_putuint(nvl->nv_idx,
+ encoded_size);
+ nvl->nv_idx += xdr->xdr_putuint(nvl->nv_idx,
+ decoded_size);
+ break;
+
+ case XDR_OP_DECODE:
+ nvl->nv_idx += xdr->xdr_getuint(&nvph->encoded_size,
+ &nvph->encoded_size);
+ nvl->nv_idx += xdr->xdr_getuint(&nvph->decoded_size,
+ &nvph->decoded_size);
+
+ encoded_size = nvph->encoded_size;
+ decoded_size = nvph->decoded_size;
+ break;
+ }
+ }
+ return (rv);
}
static int
@@ -323,45 +492,88 @@
p += 2 * sizeof(unsigned);
pair = p;
- p += xdr->xdr_getint(xdr, p, &encoded_size);
- p += xdr->xdr_getint(xdr, p, &decoded_size);
+ p += xdr->xdr_getuint(p, &encoded_size);
+ p += xdr->xdr_getuint(p, &decoded_size);
while (encoded_size && decoded_size) {
p = pair + encoded_size;
pair = p;
- p += xdr->xdr_getint(xdr, p, &encoded_size);
- p += xdr->xdr_getint(xdr, p, &decoded_size);
+ p += xdr->xdr_getuint(p, &encoded_size);
+ p += xdr->xdr_getuint(p, &decoded_size);
}
return (p - stream);
}
/*
+ * Export nvlist to byte stream format.
+ */
+int
+nvlist_export(nvlist_t *nvl)
+{
+ int rv;
+ xdr_t xdr = {
+ .xdr_op = XDR_OP_ENCODE,
+ .xdr_putint = _putint,
+ .xdr_putuint = _putuint
+ };
+
+ if (nvl->nv_header.nvh_encoding != NV_ENCODE_XDR)
+ return (ENOTSUP);
+
+ nvl->nv_idx = nvl->nv_data;
+ rv = nvlist_xdr_nvlist(&xdr, nvl);
+
+ return (rv);
+}
+
+/*
* Import nvlist from byte stream.
* Determine the stream size and allocate private copy.
* Then translate the data.
*/
nvlist_t *
-nvlist_import(const uint8_t *stream, char encoding, char endian)
+nvlist_import(const char *stream)
{
nvlist_t *nvl;
+ xdr_t xdr = {
+ .xdr_op = XDR_OP_DECODE,
+ .xdr_getint = _getint,
+ .xdr_getuint = _getuint
+ };
- if (encoding != NV_ENCODE_XDR)
+ /* Check the nvlist head. */
+ if (stream[0] != NV_ENCODE_XDR ||
+ (stream[1] != '\0' && stream[1] != '\1') ||
+ stream[2] != '\0' || stream[3] != '\0' ||
+ be32toh(*(uint32_t *)(stream + 4)) != NV_VERSION ||
+ be32toh(*(uint32_t *)(stream + 8)) != NV_UNIQUE_NAME)
return (NULL);
nvl = malloc(sizeof(*nvl));
if (nvl == NULL)
return (nvl);
- nvl->nv_asize = nvl->nv_size = nvlist_size(&ntoh, stream);
+ nvl->nv_header.nvh_encoding = stream[0];
+ nvl->nv_header.nvh_endian = stream[1];
+ nvl->nv_header.nvh_reserved1 = stream[2];
+ nvl->nv_header.nvh_reserved2 = stream[3];
+ nvl->nv_asize = nvl->nv_size = nvlist_size(&xdr,
+ (const uint8_t *)stream + 4);
nvl->nv_data = malloc(nvl->nv_asize);
if (nvl->nv_data == NULL) {
free(nvl);
return (NULL);
}
nvl->nv_idx = nvl->nv_data;
- bcopy(stream, nvl->nv_data, nvl->nv_asize);
+ bcopy(stream + 4, nvl->nv_data, nvl->nv_asize);
- nvlist_decode_nvlist(&ntoh, nvl);
- nvl->nv_idx = nvl->nv_data;
+ if (nvlist_xdr_nvlist(&xdr, nvl) == 0) {
+ nvl->nv_idx = nvl->nv_data;
+ } else {
+ free(nvl->nv_data);
+ free(nvl);
+ nvl = NULL;
+ }
+
return (nvl);
}
@@ -432,7 +644,6 @@
while (nvp->encoded_size != 0 && nvp->decoded_size != 0) {
nvp_name = (nv_string_t *)((uint8_t *)nvp + sizeof(*nvp));
-
nvp_data = (nv_pair_data_t *)
NV_ALIGN4((uintptr_t)&nvp_name->nv_data[0] +
nvp_name->nv_size);
@@ -443,7 +654,7 @@
*elementsp = nvp_data->nv_nelem;
switch (nvp_data->nv_type) {
case DATA_TYPE_UINT64:
- *(uint64_t *)valuep =
+ *(uint64_t *)valuep =
*(uint64_t *)nvp_data->nv_data;
return (0);
case DATA_TYPE_STRING:
@@ -476,7 +687,99 @@
return (ENOENT);
}
-/*
+int
+nvlist_add_uint64(nvlist_t *nvl, const char *name, uint64_t value)
+{
+ nvs_data_t *nvs;
+ nvp_header_t head, *hp;
+ uint8_t *ptr;
+ size_t namelen, valuelen;
+
+ nvs = (nvs_data_t *)nvl->nv_data;
+ if (nvs->nvl_nvflag & NV_UNIQUE_NAME)
+ (void) nvlist_remove(nvl, name, DATA_TYPE_UINT64);
+
+ namelen = strlen(name);
+ valuelen = sizeof(value);
+ head.encoded_size = 4 + 4 + 4 + NV_ALIGN4(namelen) + 4 + 4 +
+ 4 + NV_ALIGN(valuelen + 1);
+ head.decoded_size = NV_ALIGN(4 * 4 + namelen + 1) + valuelen;
+
+ if (nvl->nv_asize - nvl->nv_size < head.encoded_size + 8) {
+ ptr = realloc(nvl->nv_data, nvl->nv_asize + head.encoded_size);
+ if (ptr == NULL)
+ return (ENOMEM);
+ nvl->nv_data = ptr;
+ nvl->nv_asize += head.encoded_size;
+ }
+ nvl->nv_idx = nvl->nv_data + nvl->nv_size - sizeof(*hp);
+ bzero(nvl->nv_idx, head.encoded_size + 8);
+ hp = (nvp_header_t *)nvl->nv_idx;
+ *hp = head;
+ nvl->nv_idx += sizeof(*hp);
+ *(unsigned *)nvl->nv_idx = namelen;
+ nvl->nv_idx += sizeof(unsigned);
+ strlcpy((char *)nvl->nv_idx, name, namelen + 1);
+ nvl->nv_idx += NV_ALIGN4(namelen);
+ *(unsigned *)nvl->nv_idx = DATA_TYPE_UINT64;
+ nvl->nv_idx += sizeof(unsigned);
+ *(unsigned *)nvl->nv_idx = 1;
+ nvl->nv_idx += sizeof(unsigned);
+ *(uint64_t *)nvl->nv_idx = value;
+ nvl->nv_size += head.encoded_size;
+
+ return (0);
+}
+
+int
+nvlist_add_string(nvlist_t *nvl, const char *name, const char *value)
+{
+ nvs_data_t *nvs;
+ nvp_header_t head, *hp;
+ uint8_t *ptr;
+ size_t namelen, valuelen;
+
+ nvs = (nvs_data_t *)nvl->nv_data;
+ if (nvs->nvl_nvflag & NV_UNIQUE_NAME)
+ (void) nvlist_remove(nvl, name, DATA_TYPE_STRING);
+
+ namelen = strlen(name);
+ valuelen = strlen(value);
+ head.encoded_size = 4 + 4 + 4 + NV_ALIGN4(namelen) + 4 + 4 +
+ 4 + NV_ALIGN(valuelen + 1);
+ head.decoded_size = NV_ALIGN(4 * 4 + namelen + 1) +
+ NV_ALIGN(valuelen + 1);
+
+ if (nvl->nv_asize - nvl->nv_size < head.encoded_size + 8) {
+ ptr = realloc(nvl->nv_data, nvl->nv_asize + head.encoded_size);
+ if (ptr == NULL)
+ return (ENOMEM);
+ nvl->nv_data = ptr;
+ nvl->nv_asize += head.encoded_size;
+ }
+ nvl->nv_idx = nvl->nv_data + nvl->nv_size - sizeof(*hp);
+ bzero(nvl->nv_idx, head.encoded_size + 8);
+ hp = (nvp_header_t *)nvl->nv_idx;
+ *hp = head;
+ nvl->nv_idx += sizeof(*hp);
+ *(unsigned *)nvl->nv_idx = namelen;
+ nvl->nv_idx += sizeof(unsigned);
+ strlcpy((char *)nvl->nv_idx, name, namelen + 1);
+ nvl->nv_idx += NV_ALIGN4(namelen);
+ *(unsigned *)nvl->nv_idx = DATA_TYPE_STRING;
+ nvl->nv_idx += sizeof(unsigned);
+ *(unsigned *)nvl->nv_idx = 1;
+ nvl->nv_idx += sizeof(unsigned);
+ *(unsigned *)nvl->nv_idx = valuelen;
+ nvl->nv_idx += sizeof(unsigned);
+ strlcpy((char *)nvl->nv_idx, value, valuelen + 1);
+ nvl->nv_idx += NV_ALIGN4(valuelen);
+ nvl->nv_size += head.encoded_size;
+
+ return (0);
+}
+
+/*
* Return the next nvlist in an nvlist array.
*/
int
@@ -535,7 +838,12 @@
nv_string_t *nvp_name;
nv_pair_data_t *nvp_data;
nvlist_t nvlist;
- int i, j;
+ unsigned i, j;
+ xdr_t xdr = {
+ .xdr_op = XDR_OP_DECODE,
+ .xdr_getint = mem_int,
+ .xdr_getuint = mem_uint
+ };
data = (nvs_data_t *)nvl->nv_data;
nvp = &data->nvl_pair; /* first pair in nvlist */
@@ -545,7 +853,7 @@
NV_ALIGN4((uintptr_t)&nvp_name->nv_data[0] +
nvp_name->nv_size);
- for (int i = 0; i < indent; i++)
+ for (i = 0; i < indent; i++)
printf(" ");
printf("%s [%d] %.*s", typenames[nvp_data->nv_type],
@@ -563,7 +871,7 @@
case DATA_TYPE_STRING: {
nvp_name = (nv_string_t *)&nvp_data->nv_data[0];
printf(" = \"%.*s\"\n", nvp_name->nv_size,
- nvp_name->nv_data );
+ nvp_name->nv_data);
break;
}
@@ -588,7 +896,7 @@
nvp_name->nv_data);
}
nvlist.nv_data = (uint8_t *)data +
- nvlist_size(&native, nvlist.nv_data);
+ nvlist_size(&xdr, nvlist.nv_data);
}
break;
Index: stand/libsa/zfs/zfs.c
===================================================================
--- stand/libsa/zfs/zfs.c
+++ stand/libsa/zfs/zfs.c
@@ -483,8 +483,7 @@
}
static int
-vdev_write(vdev_t *vdev __unused, void *priv, off_t offset, void *buf,
- size_t bytes)
+vdev_write(vdev_t *vdev, off_t offset, void *buf, size_t bytes)
{
int fd, ret;
size_t head, tail, total_size, full_sec_size;
@@ -493,8 +492,8 @@
ssize_t res;
char *outbuf, *bouncebuf;
- fd = (uintptr_t)priv;
- outbuf = (char *) buf;
+ fd = (uintptr_t)vdev->v_priv;
+ outbuf = (char *)buf;
bouncebuf = NULL;
ret = ioctl(fd, DIOCGSECTORSIZE, &secsz);
@@ -529,14 +528,14 @@
/* Partial data for first sector */
if (head > 0) {
res = read(fd, bouncebuf, secsz);
- if (res != secsz) {
+ if ((unsigned)res != secsz) {
ret = EIO;
goto error;
}
memcpy(bouncebuf + head, outbuf, min(secsz - head, bytes));
(void) lseek(fd, -secsz, SEEK_CUR);
res = write(fd, bouncebuf, secsz);
- if (res != secsz) {
+ if ((unsigned)res != secsz) {
ret = EIO;
goto error;
}
@@ -552,20 +551,20 @@
if (full_sec_size > 0) {
if (bytes < full_sec_size) {
res = read(fd, bouncebuf, secsz);
- if (res != secsz) {
+ if ((unsigned)res != secsz) {
ret = EIO;
goto error;
}
memcpy(bouncebuf, outbuf, bytes);
(void) lseek(fd, -secsz, SEEK_CUR);
res = write(fd, bouncebuf, secsz);
- if (res != secsz) {
+ if ((unsigned)res != secsz) {
ret = EIO;
goto error;
}
} else {
res = write(fd, outbuf, full_sec_size);
- if (res != full_sec_size) {
+ if ((unsigned)res != full_sec_size) {
ret = EIO;
goto error;
}
@@ -576,14 +575,14 @@
/* Partial data write to last sector */
if (do_tail_write) {
res = read(fd, bouncebuf, secsz);
- if (res != secsz) {
+ if ((unsigned)res != secsz) {
ret = EIO;
goto error;
}
memcpy(bouncebuf, outbuf, secsz - tail);
(void) lseek(fd, -secsz, SEEK_CUR);
res = write(fd, bouncebuf, secsz);
- if (res != secsz) {
+ if ((unsigned)res != secsz) {
ret = EIO;
goto error;
}
@@ -595,102 +594,6 @@
return (ret);
}
-static void
-vdev_clear_pad2(vdev_t *vdev)
-{
- vdev_t *kid;
- vdev_boot_envblock_t *be;
- off_t off = offsetof(vdev_label_t, vl_be);
- zio_checksum_info_t *ci;
- zio_cksum_t cksum;
-
- STAILQ_FOREACH(kid, &vdev->v_children, v_childlink) {
- if (kid->v_state != VDEV_STATE_HEALTHY)
- continue;
- vdev_clear_pad2(kid);
- }
-
- if (!STAILQ_EMPTY(&vdev->v_children))
- return;
-
- be = calloc(1, sizeof (*be));
- if (be == NULL) {
- printf("failed to clear be area: out of memory\n");
- return;
- }
-
- ci = &zio_checksum_table[ZIO_CHECKSUM_LABEL];
- be->vbe_zbt.zec_magic = ZEC_MAGIC;
- zio_checksum_label_verifier(&be->vbe_zbt.zec_cksum, off);
- ci->ci_func[0](be, sizeof (*be), NULL, &cksum);
- be->vbe_zbt.zec_cksum = cksum;
-
- if (vdev_write(vdev, vdev->v_read_priv, off, be, VDEV_PAD_SIZE)) {
- printf("failed to clear be area of primary vdev: %d\n",
- errno);
- }
- free(be);
-}
-
-/*
- * Read the next boot command from pad2.
- * If any instance of pad2 is set to empty string, or the returned string
- * values are not the same, we consider next boot not to be set.
- */
-static char *
-vdev_read_pad2(vdev_t *vdev)
-{
- vdev_t *kid;
- char *tmp, *result = NULL;
- vdev_boot_envblock_t *be;
- off_t off = offsetof(vdev_label_t, vl_be);
-
- STAILQ_FOREACH(kid, &vdev->v_children, v_childlink) {
- if (kid->v_state != VDEV_STATE_HEALTHY)
- continue;
- tmp = vdev_read_pad2(kid);
- if (tmp == NULL)
- continue;
-
- /* The next boot is not set, we are done. */
- if (*tmp == '\0') {
- free(result);
- return (tmp);
- }
- if (result == NULL) {
- result = tmp;
- continue;
- }
- /* Are the next boot strings different? */
- if (strcmp(result, tmp) != 0) {
- free(tmp);
- *result = '\0';
- break;
- }
- free(tmp);
- }
- if (result != NULL)
- return (result);
-
- be = malloc(sizeof (*be));
- if (be == NULL)
- return (NULL);
-
- if (vdev_read(vdev, vdev->v_read_priv, off, be, sizeof (*be))) {
- return (NULL);
- }
-
- switch (be->vbe_version) {
- case VB_RAW:
- case VB_NVLIST:
- result = strdup(be->vbe_bootenv);
- default:
- /* Backward compatibility with initial nextboot feaure. */
- result = strdup((char *)be);
- }
- return (result);
-}
-
static int
zfs_dev_init(void)
{
@@ -743,7 +646,7 @@
int ret;
spa = NULL;
- ret = vdev_probe(vdev_read, (void *)(uintptr_t)fd, &spa);
+ ret = vdev_probe(vdev_read, vdev_write, (void *)(uintptr_t)fd, &spa);
if (ret == 0 && pool_guid != NULL)
*pool_guid = spa->spa_guid;
return (ret);
@@ -766,7 +669,7 @@
ppa = (struct zfs_probe_args *)arg;
strncpy(devname, ppa->devname, strlen(ppa->devname) - 1);
devname[strlen(ppa->devname) - 1] = '\0';
- sprintf(devname, "%s%s:", devname, partname);
+ snprintf(devname, sizeof(devname), "%s%s:", devname, partname);
pa.fd = open(devname, O_RDWR);
if (pa.fd == -1)
return (0);
@@ -789,58 +692,98 @@
return (0);
}
+/*
+ * Return bootenv nvlist from pool label.
+ */
int
-zfs_nextboot(void *vdev, char *buf, size_t size)
+zfs_get_bootenv(void *vdev, nvlist_t **benvp)
{
struct zfs_devdesc *dev = (struct zfs_devdesc *)vdev;
- spa_t *spa;
+ nvlist_t *benv = NULL;
vdev_t *vd;
- char *result = NULL;
+ spa_t *spa;
if (dev->dd.d_dev->dv_type != DEVT_ZFS)
- return (1);
+ return (ENOTSUP);
- if (dev->pool_guid == 0)
- spa = STAILQ_FIRST(&zfs_pools);
- else
- spa = spa_find_by_guid(dev->pool_guid);
+ if ((spa = spa_find_by_dev(dev)) == NULL)
+ return (ENXIO);
- if (spa == NULL) {
- printf("ZFS: can't find pool by guid\n");
- return (1);
- }
+ if (spa->spa_bootenv == NULL) {
+ STAILQ_FOREACH(vd, &spa->spa_root_vdev->v_children,
+ v_childlink) {
+ benv = vdev_read_bootenv(vd);
- STAILQ_FOREACH(vd, &spa->spa_root_vdev->v_children, v_childlink) {
- char *tmp = vdev_read_pad2(vd);
-
- /* Continue on error. */
- if (tmp == NULL)
- continue;
- /* Nextboot is not set. */
- if (*tmp == '\0') {
- free(result);
- free(tmp);
- return (1);
+ if (benv != NULL)
+ break;
}
- if (result == NULL) {
- result = tmp;
- continue;
- }
- free(tmp);
+ spa->spa_bootenv = benv;
+ } else {
+ benv = spa->spa_bootenv;
}
- if (result == NULL)
- return (1);
+ if (benv == NULL)
+ return (ENOENT);
+
+ *benvp = benv;
+ return (0);
+}
+
+/*
+ * Store nvlist to pool label bootenv area. Also updates cached pointer in spa.
+ */
+int
+zfs_set_bootenv(void *vdev, nvlist_t *benv)
+{
+ struct zfs_devdesc *dev = (struct zfs_devdesc *)vdev;
+ spa_t *spa;
+ vdev_t *vd;
+
+ if (dev->dd.d_dev->dv_type != DEVT_ZFS)
+ return (ENOTSUP);
+
+ if ((spa = spa_find_by_dev(dev)) == NULL)
+ return (ENXIO);
+
STAILQ_FOREACH(vd, &spa->spa_root_vdev->v_children, v_childlink) {
- vdev_clear_pad2(vd);
+ vdev_write_bootenv(vd, benv);
}
- strlcpy(buf, result, size);
- free(result);
+ spa->spa_bootenv = benv;
return (0);
}
+/*
+ * Get bootonce value by key. The bootonce <key, value> pair is removed
+ * from the bootenv nvlist and the remaining nvlist is committed back to disk.
+ */
int
+zfs_get_bootonce(void *vdev, const char *key, char *buf, size_t size)
+{
+ nvlist_t *benv;
+ char *result = NULL;
+ int result_size, rv;
+
+ if ((rv = zfs_get_bootenv(vdev, &benv)) != 0)
+ return (rv);
+
+ if ((rv = nvlist_find(benv, key, DATA_TYPE_STRING, NULL,
+ &result, &result_size)) == 0) {
+ if (result_size == 0) {
+ /* ignore empty string */
+ rv = ENOENT;
+ } else {
+ size = MIN((size_t)result_size + 1, size);
+ strlcpy(buf, result, size);
+ }
+ (void) nvlist_remove(benv, key, DATA_TYPE_STRING);
+ (void) zfs_set_bootenv(vdev, benv);
+ }
+
+ return (rv);
+}
+
+int
zfs_probe_dev(const char *devname, uint64_t *pool_guid)
{
struct disk_devdesc *dev;
@@ -936,12 +879,9 @@
dev = va_arg(args, struct zfs_devdesc *);
va_end(args);
- if (dev->pool_guid == 0)
- spa = STAILQ_FIRST(&zfs_pools);
- else
- spa = spa_find_by_guid(dev->pool_guid);
- if (!spa)
+ if ((spa = spa_find_by_dev(dev)) == NULL)
return (ENXIO);
+
mount = malloc(sizeof(*mount));
if (mount == NULL)
rv = ENOMEM;
@@ -1070,10 +1010,11 @@
}
if (rootname[0] == '\0')
- sprintf(buf, "%s:%s:", dev->dd.d_dev->dv_name, spa->spa_name);
+ snprintf(buf, sizeof(buf), "%s:%s:", dev->dd.d_dev->dv_name,
+ spa->spa_name);
else
- sprintf(buf, "%s:%s/%s:", dev->dd.d_dev->dv_name, spa->spa_name,
- rootname);
+ snprintf(buf, sizeof(buf), "%s:%s/%s:", dev->dd.d_dev->dv_name,
+ spa->spa_name, rootname);
return (buf);
}
Index: stand/libsa/zfs/zfsimpl.c
===================================================================
--- stand/libsa/zfs/zfsimpl.c
+++ stand/libsa/zfs/zfsimpl.c
@@ -31,10 +31,12 @@
* Stand-alone ZFS file reader.
*/
+#include <stdbool.h>
#include <sys/endian.h>
#include <sys/stat.h>
#include <sys/stdint.h>
#include <sys/list.h>
+#include <sys/zfs_bootenv.h>
#include <machine/_inttypes.h>
#include "zfsimpl.h"
@@ -219,8 +221,8 @@
size_t psize;
int rc;
- if (!vdev->v_phys_read)
- return (EIO);
+ if (vdev->v_phys_read == NULL)
+ return (ENOTSUP);
if (bp) {
psize = BP_GET_PSIZE(bp);
@@ -228,7 +230,7 @@
psize = size;
}
- rc = vdev->v_phys_read(vdev, vdev->v_read_priv, offset, buf, psize);
+ rc = vdev->v_phys_read(vdev, vdev->v_priv, offset, buf, psize);
if (rc == 0) {
if (bp != NULL)
rc = zio_checksum_verify(vdev->v_spa, bp, buf);
@@ -237,6 +239,15 @@
return (rc);
}
+static int
+vdev_write_phys(vdev_t *vdev, void *buf, off_t offset, size_t size)
+{
+ if (vdev->v_phys_write == NULL)
+ return (ENOTSUP);
+
+ return (vdev->v_phys_write(vdev, offset, buf, size));
+}
+
typedef struct remap_segment {
vdev_t *rs_vd;
uint64_t rs_offset;
@@ -1337,6 +1348,19 @@
}
static spa_t *
+spa_find_by_dev(struct zfs_devdesc *dev)
+{
+
+ if (dev->dd.d_dev->dv_type != DEVT_ZFS)
+ return (NULL);
+
+ if (dev->pool_guid == 0)
+ return (STAILQ_FIRST(&zfs_pools));
+
+ return (spa_find_by_guid(dev->pool_guid));
+}
+
+static spa_t *
spa_create(uint64_t guid, const char *name)
{
spa_t *spa;
@@ -1586,6 +1610,249 @@
return (vdev_read_phys(vd, &bp, buf, off, size));
}
+/*
+ * We do need to be sure we write to correct location.
+ * Our vdev label does consist of 4 fields:
+ * pad1 (8k), reserved.
+ * bootenv (8k), checksummed, previously reserved, may contian garbage.
+ * vdev_phys (112k), checksummed
+ * uberblock ring (128k), checksummed.
+ *
+ * Since bootenv area may contain garbage, we can not reliably read it, as
+ * we can get checksum errors.
+ * Next best thing is vdev_phys - it is just after bootenv. It still may
+ * be corrupted, but in such case we will miss this one write.
+ */
+static int
+vdev_label_write_validate(vdev_t *vd, int l, uint64_t offset)
+{
+ uint64_t off, o_phys;
+ void *buf;
+ size_t size = VDEV_PHYS_SIZE;
+ int rc;
+
+ o_phys = offsetof(vdev_label_t, vl_vdev_phys);
+ off = vdev_label_offset(vd->v_psize, l, o_phys);
+
+ /* off should be 8K from bootenv */
+ if (vdev_label_offset(vd->v_psize, l, offset) + VDEV_PAD_SIZE != off)
+ return (EINVAL);
+
+ buf = malloc(size);
+ if (buf == NULL)
+ return (ENOMEM);
+
+ /* Read vdev_phys */
+ rc = vdev_label_read(vd, l, buf, o_phys, size);
+ free(buf);
+ return (rc);
+}
+
+static int
+vdev_label_write(vdev_t *vd, int l, vdev_boot_envblock_t *be, uint64_t offset)
+{
+ zio_checksum_info_t *ci;
+ zio_cksum_t cksum;
+ off_t off;
+ size_t size = VDEV_PAD_SIZE;
+ int rc;
+
+ if (vd->v_phys_write == NULL)
+ return (ENOTSUP);
+
+ off = vdev_label_offset(vd->v_psize, l, offset);
+
+ rc = vdev_label_write_validate(vd, l, offset);
+ if (rc != 0) {
+ return (rc);
+ }
+
+ ci = &zio_checksum_table[ZIO_CHECKSUM_LABEL];
+ be->vbe_zbt.zec_magic = ZEC_MAGIC;
+ zio_checksum_label_verifier(&be->vbe_zbt.zec_cksum, off);
+ ci->ci_func[0](be, size, NULL, &cksum);
+ be->vbe_zbt.zec_cksum = cksum;
+
+ return (vdev_write_phys(vd, be, off, size));
+}
+
+static int
+vdev_write_bootenv_impl(vdev_t *vdev, vdev_boot_envblock_t *be)
+{
+ vdev_t *kid;
+ int rv = 0, rc;
+
+ STAILQ_FOREACH(kid, &vdev->v_children, v_childlink) {
+ if (kid->v_state != VDEV_STATE_HEALTHY)
+ continue;
+ rc = vdev_write_bootenv_impl(kid, be);
+ if (rv == 0)
+ rv = rc;
+ }
+
+ /*
+ * Non-leaf vdevs do not have v_phys_write.
+ */
+ if (vdev->v_phys_write == NULL)
+ return (rv);
+
+ for (int l = 0; l < VDEV_LABELS; l++) {
+ rc = vdev_label_write(vdev, l, be,
+ offsetof(vdev_label_t, vl_be));
+ if (rc != 0) {
+ printf("failed to write bootenv to %s label %d: %d\n",
+ vdev->v_name ? vdev->v_name : "unknown", l, rc);
+ rv = rc;
+ }
+ }
+ return (rv);
+}
+
+int
+vdev_write_bootenv(vdev_t *vdev, nvlist_t *nvl)
+{
+ vdev_boot_envblock_t *be;
+ nvlist_t nv, *nvp;
+ uint64_t version;
+ int rv;
+
+ if (nvl->nv_size > sizeof(be->vbe_bootenv))
+ return (E2BIG);
+
+ version = VB_RAW;
+ nvp = vdev_read_bootenv(vdev);
+ if (nvp != NULL) {
+ nvlist_find(nvp, BOOTENV_VERSION, DATA_TYPE_UINT64, NULL,
+ &version, NULL);
+ nvlist_destroy(nvp);
+ }
+
+ be = calloc(1, sizeof(*be));
+ if (be == NULL)
+ return (ENOMEM);
+
+ be->vbe_version = version;
+ switch (version) {
+ case VB_RAW:
+ /*
+ * If there is no envmap, we will just wipe bootenv.
+ */
+ nvlist_find(nvl, GRUB_ENVMAP, DATA_TYPE_STRING, NULL,
+ be->vbe_bootenv, NULL);
+ rv = 0;
+ break;
+
+ case VB_NVLIST:
+ nv.nv_header = nvl->nv_header;
+ nv.nv_asize = nvl->nv_asize;
+ nv.nv_size = nvl->nv_size;
+
+ *(nvs_header_t *)be->vbe_bootenv = nv.nv_header;
+ nv.nv_data = be->vbe_bootenv + sizeof(nvs_header_t);
+ bcopy(nvl->nv_data, nv.nv_data, nv.nv_size);
+ rv = nvlist_export(&nv);
+ break;
+
+ default:
+ rv = EINVAL;
+ break;
+ }
+
+ if (rv == 0) {
+ be->vbe_version = htobe64(be->vbe_version);
+ rv = vdev_write_bootenv_impl(vdev, be);
+ }
+ free(be);
+ return (rv);
+}
+
+/*
+ * Read the bootenv area from pool label, return the nvlist from it.
+ * We return from first successful read.
+ */
+nvlist_t *
+vdev_read_bootenv(vdev_t *vdev)
+{
+ vdev_t *kid;
+ nvlist_t *benv;
+ vdev_boot_envblock_t *be;
+ char *command;
+ bool ok;
+ int rv;
+
+ STAILQ_FOREACH(kid, &vdev->v_children, v_childlink) {
+ if (kid->v_state != VDEV_STATE_HEALTHY)
+ continue;
+
+ benv = vdev_read_bootenv(kid);
+ if (benv != NULL)
+ return (benv);
+ }
+
+ be = malloc(sizeof (*be));
+ if (be == NULL)
+ return (NULL);
+
+ rv = 0;
+ for (int l = 0; l < VDEV_LABELS; l++) {
+ rv = vdev_label_read(vdev, l, be,
+ offsetof(vdev_label_t, vl_be),
+ sizeof (*be));
+ if (rv == 0)
+ break;
+ }
+ if (rv != 0) {
+ free(be);
+ return (NULL);
+ }
+
+ be->vbe_version = be64toh(be->vbe_version);
+ switch (be->vbe_version) {
+ case VB_RAW:
+ /*
+ * we have textual data in vbe_bootenv, create nvlist
+ * with key "envmap".
+ */
+ benv = nvlist_create(NV_UNIQUE_NAME);
+ if (benv != NULL) {
+ nvlist_add_uint64(benv, BOOTENV_VERSION, VB_RAW);
+ be->vbe_bootenv[sizeof (be->vbe_bootenv) - 1] = '\0';
+ nvlist_add_string(benv, GRUB_ENVMAP, be->vbe_bootenv);
+ }
+ break;
+
+ case VB_NVLIST:
+ benv = nvlist_import(be->vbe_bootenv);
+ break;
+
+ default:
+ command = (char *)be;
+ ok = false;
+
+ /* Check for legacy zfsbootcfg command string */
+ for (int i = 0; command[i] != '\0'; i++) {
+ if (iscntrl(command[i])) {
+ ok = false;
+ break;
+ } else {
+ ok = true;
+ }
+ }
+ benv = nvlist_create(NV_UNIQUE_NAME);
+ if (benv != NULL) {
+ if (ok)
+ nvlist_add_string(benv, FREEBSD_BOOTONCE,
+ command);
+ else
+ nvlist_add_uint64(benv, BOOTENV_VERSION,
+ VB_NVLIST);
+ }
+ break;
+ }
+ free(be);
+ return (benv);
+}
+
static uint64_t
vdev_get_label_asize(nvlist_t *nvl)
{
@@ -1652,15 +1919,12 @@
return (NULL);
for (int l = 0; l < VDEV_LABELS; l++) {
- const unsigned char *nvlist;
-
if (vdev_label_read(vd, l, label,
offsetof(vdev_label_t, vl_vdev_phys),
sizeof (vdev_phys_t)))
continue;
- nvlist = (const unsigned char *) label->vp_nvlist;
- tmp = nvlist_import(nvlist + 4, nvlist[0], nvlist[1]);
+ tmp = nvlist_import(label->vp_nvlist);
if (tmp == NULL)
continue;
@@ -1725,7 +1989,8 @@
}
static int
-vdev_probe(vdev_phys_read_t *_read, void *read_priv, spa_t **spap)
+vdev_probe(vdev_phys_read_t *_read, vdev_phys_write_t *_write, void *priv,
+ spa_t **spap)
{
vdev_t vtmp;
spa_t *spa;
@@ -1743,8 +2008,9 @@
*/
memset(&vtmp, 0, sizeof(vtmp));
vtmp.v_phys_read = _read;
- vtmp.v_read_priv = read_priv;
- vtmp.v_psize = P2ALIGN(ldi_get_size(read_priv),
+ vtmp.v_phys_write = _write;
+ vtmp.v_priv = priv;
+ vtmp.v_psize = P2ALIGN(ldi_get_size(priv),
(uint64_t)sizeof (vdev_label_t));
/* Test for minimum device size. */
@@ -1858,7 +2124,8 @@
vdev = vdev_find(guid);
if (vdev != NULL) {
vdev->v_phys_read = _read;
- vdev->v_read_priv = read_priv;
+ vdev->v_phys_write = _write;
+ vdev->v_priv = priv;
vdev->v_psize = vtmp.v_psize;
/*
* If no other state is set, mark vdev healthy.
@@ -3128,7 +3395,7 @@
dnode_phys_t dir;
size_t size;
int rc;
- unsigned char *nv;
+ char *nv;
*value = NULL;
if ((rc = objset_get_dnode(spa, &spa->spa_mos, obj, &dir)) != 0)
@@ -3152,7 +3419,7 @@
nv = NULL;
return (rc);
}
- *value = nvlist_import(nv + 4, nv[0], nv[1]);
+ *value = nvlist_import(nv);
free(nv);
return (rc);
}
Index: stand/userboot/test/test.c
===================================================================
--- stand/userboot/test/test.c
+++ stand/userboot/test/test.c
@@ -261,6 +261,21 @@
}
int
+test_diskwrite(void *arg, int unit, uint64_t offset, void *src, size_t size,
+ size_t *resid_return)
+{
+ ssize_t n;
+
+ if (unit > disk_index || disk_fd[unit] == -1)
+ return (EIO);
+ n = pwrite(disk_fd[unit], src, size, offset);
+ if (n < 0)
+ return (errno);
+ *resid_return = size - n;
+ return (0);
+}
+
+int
test_diskioctl(void *arg, int unit, u_long cmd, void *data)
{
struct stat sb;
@@ -399,6 +414,7 @@
.stat = test_stat,
.diskread = test_diskread,
+ .diskwrite = test_diskwrite,
.diskioctl = test_diskioctl,
.copyin = test_copyin,
@@ -431,8 +447,9 @@
void (*func)(struct loader_callbacks *, void *, int, int) __dead2;
int opt;
const char *userboot_obj = "/boot/userboot.so";
+ int oflag = O_RDONLY;
- while ((opt = getopt(argc, argv, "b:d:h:")) != -1) {
+ while ((opt = getopt(argc, argv, "wb:d:h:")) != -1) {
switch (opt) {
case 'b':
userboot_obj = optarg;
@@ -442,13 +459,17 @@
disk_index++;
disk_fd = reallocarray(disk_fd, disk_index + 1,
sizeof (int));
- disk_fd[disk_index] = open(optarg, O_RDONLY);
+ disk_fd[disk_index] = open(optarg, oflag);
if (disk_fd[disk_index] < 0)
err(1, "Can't open disk image '%s'", optarg);
break;
case 'h':
host_base = optarg;
+ break;
+
+ case 'w':
+ oflag = O_RDWR;
break;
case '?':
Index: stand/userboot/userboot.h
===================================================================
--- stand/userboot/userboot.h
+++ stand/userboot/userboot.h
@@ -131,6 +131,12 @@
int (*diskread)(void *arg, int unit, uint64_t offset,
void *dst, size_t size, size_t *resid_return);
+ /*
+ * Write to a disk image at the given offset
+ */
+ int (*diskwrite)(void *arg, int unit, uint64_t offset,
+ void *src, size_t size, size_t *resid_return);
+
/*
* Guest virtual machine i/o
*/
Index: stand/userboot/userboot/main.c
===================================================================
--- stand/userboot/userboot/main.c
+++ stand/userboot/userboot/main.c
@@ -214,6 +214,16 @@
exit(0);
}
+static void
+set_currdev(const char *devname)
+{
+
+ env_setenv("currdev", EV_VOLATILE, devname,
+ userboot_setcurrdev, env_nounset);
+ env_setenv("loaddev", EV_VOLATILE, devname,
+ env_noset, env_nounset);
+}
+
/*
* Set the 'current device' by (if possible) recovering the boot device as
* supplied by the initial bootstrap.
@@ -225,6 +235,7 @@
struct devdesc *dd;
#if defined(USERBOOT_ZFS_SUPPORT)
struct zfs_devdesc zdev;
+ char *buf = NULL;
if (userboot_zfs_found) {
@@ -257,10 +268,22 @@
dd = &dev.dd;
}
- env_setenv("currdev", EV_VOLATILE, userboot_fmtdev(dd),
- userboot_setcurrdev, env_nounset);
- env_setenv("loaddev", EV_VOLATILE, userboot_fmtdev(dd),
- env_noset, env_nounset);
+ set_currdev(userboot_fmtdev(dd));
+
+#if defined(USERBOOT_ZFS_SUPPORT)
+ if (userboot_zfs_found) {
+ buf = malloc(VDEV_PAD_SIZE);
+ if (buf != NULL) {
+ if (zfs_get_bootonce(&zdev, "command", buf,
+ VDEV_PAD_SIZE) == 0) {
+ printf("zfs bootonce: %s\n", buf);
+ set_currdev(buf);
+ setenv("zfs-bootonce", buf, 1);
+ }
+ free(buf);
+ }
+ }
+#endif
}
#if defined(USERBOOT_ZFS_SUPPORT)
Index: stand/userboot/userboot/userboot_disk.c
===================================================================
--- stand/userboot/userboot/userboot_disk.c
+++ stand/userboot/userboot/userboot_disk.c
@@ -211,15 +211,21 @@
size_t resid;
int rc;
- rw &= F_MASK;
- if (rw == F_WRITE)
- return (EROFS);
- if (rw != F_READ)
- return (EINVAL);
if (rsize)
*rsize = 0;
off = dblk * ud_info[dev->dd.d_unit].sectorsize;
- rc = CALLBACK(diskread, dev->dd.d_unit, off, buf, size, &resid);
+ switch (rw & F_MASK) {
+ case F_READ:
+ rc = CALLBACK(diskread, dev->dd.d_unit, off, buf, size, &resid);
+ break;
+ case F_WRITE:
+ rc = CALLBACK(diskwrite, dev->dd.d_unit, off, buf, size,
+ &resid);
+ break;
+ default:
+ rc = EINVAL;
+ break;
+ }
if (rc)
return (rc);
if (rsize)
Index: sys/cddl/boot/zfs/zfsimpl.h
===================================================================
--- sys/cddl/boot/zfs/zfsimpl.h
+++ sys/cddl/boot/zfs/zfsimpl.h
@@ -527,20 +527,20 @@
} vdev_phys_t;
typedef enum vbe_vers {
- /* The bootenv file is stored as ascii text in the envblock */
- VB_RAW = 0,
+ /* The bootenv file is stored as ascii text in the envblock */
+ VB_RAW = 0,
- /*
- * The bootenv file is converted to an nvlist and then packed into the
- * envblock.
- */
- VB_NVLIST = 1
+ /*
+ * The bootenv file is converted to an nvlist and then packed into the
+ * envblock.
+ */
+ VB_NVLIST = 1
} vbe_vers_t;
typedef struct vdev_boot_envblock {
- uint64_t vbe_version;
- char vbe_bootenv[VDEV_PAD_SIZE - sizeof (uint64_t) -
- sizeof (zio_eck_t)];
+ uint64_t vbe_version;
+ char vbe_bootenv[VDEV_PAD_SIZE - sizeof (uint64_t) -
+ sizeof (zio_eck_t)];
zio_eck_t vbe_zbt;
} vdev_boot_envblock_t;
@@ -1662,10 +1662,9 @@
*/
struct vdev;
struct spa;
-typedef int vdev_phys_read_t(struct vdev *vdev, void *priv,
- off_t offset, void *buf, size_t bytes);
-typedef int vdev_read_t(struct vdev *vdev, const blkptr_t *bp,
- void *buf, off_t offset, size_t bytes);
+typedef int vdev_phys_read_t(struct vdev *, void *, off_t, void *, size_t);
+typedef int vdev_phys_write_t(struct vdev *, off_t, void *, size_t);
+typedef int vdev_read_t(struct vdev *, const blkptr_t *, void *, off_t, size_t);
typedef STAILQ_HEAD(vdev_list, vdev) vdev_list_t;
@@ -1793,8 +1792,9 @@
size_t v_nchildren; /* # children */
vdev_state_t v_state; /* current state */
vdev_phys_read_t *v_phys_read; /* read from raw leaf vdev */
+ vdev_phys_write_t *v_phys_write; /* write to raw leaf vdev */
vdev_read_t *v_read; /* read from vdev */
- void *v_read_priv; /* private data for read function */
+ void *v_priv; /* data for read/write function */
boolean_t v_islog;
struct spa *v_spa; /* link to spa */
/*
@@ -1820,6 +1820,7 @@
zio_cksum_salt_t spa_cksum_salt; /* secret salt for cksum */
void *spa_cksum_tmpls[ZIO_CHECKSUM_FUNCTIONS];
boolean_t spa_with_log; /* this pool has log */
+ void *spa_bootenv; /* bootenv from pool label */
} spa_t;
/* IO related arguments. */
Index: sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/vdev.h
===================================================================
--- sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/vdev.h
+++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/vdev.h
@@ -21,7 +21,7 @@
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2011, 2017 by Delphix. All rights reserved.
+ * Copyright (c) 2011, 2020 by Delphix. All rights reserved.
* Copyright (c) 2017, Intel Corporation.
*/
@@ -173,6 +173,8 @@
extern void vdev_uberblock_load(vdev_t *, struct uberblock *, nvlist_t **);
extern void vdev_label_write(zio_t *zio, vdev_t *vd, int l, abd_t *buf, uint64_t
offset, uint64_t size, zio_done_func_t *done, void *priv, int flags);
+extern int vdev_label_read_bootenv(vdev_t *, nvlist_t *);
+extern int vdev_label_write_bootenv(vdev_t *, nvlist_t *);
typedef enum {
VDEV_LABEL_CREATE, /* create/add a new device */
@@ -184,8 +186,6 @@
} vdev_labeltype_t;
extern int vdev_label_init(vdev_t *vd, uint64_t txg, vdev_labeltype_t reason);
-
-extern int vdev_label_write_pad2(vdev_t *vd, const char *buf, size_t size);
#ifdef __cplusplus
}
Index: sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/vdev_impl.h
===================================================================
--- sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/vdev_impl.h
+++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/vdev_impl.h
@@ -20,7 +20,7 @@
*/
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2011, 2018 by Delphix. All rights reserved.
+ * Copyright (c) 2011, 2020 by Delphix. All rights reserved.
* Copyright (c) 2017, Intel Corporation.
*/
@@ -392,7 +392,7 @@
#define VDEV_RAIDZ_MAXPARITY 3
#define VDEV_PAD_SIZE (8 << 10)
-/* 2 padding areas (vl_pad1 and vl_pad2) to skip */
+/* 2 padding areas (vl_pad1 and vl_be) to skip */
#define VDEV_SKIP_SIZE VDEV_PAD_SIZE * 2
#define VDEV_PHYS_SIZE (112 << 10)
#define VDEV_UBERBLOCK_RING (128 << 10)
@@ -419,9 +419,29 @@
zio_eck_t vp_zbt;
} vdev_phys_t;
+typedef enum vbe_vers {
+ /* The bootenv file is stored as ascii text in the envblock */
+ VB_RAW = 0,
+
+ /*
+ * The bootenv file is converted to an nvlist and then packed into the
+ * envblock.
+ */
+ VB_NVLIST = 1
+} vbe_vers_t;
+
+typedef struct vdev_boot_envblock {
+ uint64_t vbe_version;
+ char vbe_bootenv[VDEV_PAD_SIZE - sizeof (uint64_t) -
+ sizeof (zio_eck_t)];
+ zio_eck_t vbe_zbt;
+} vdev_boot_envblock_t;
+
+CTASSERT(sizeof (vdev_boot_envblock_t) == VDEV_PAD_SIZE);
+
typedef struct vdev_label {
char vl_pad1[VDEV_PAD_SIZE]; /* 8K */
- char vl_pad2[VDEV_PAD_SIZE]; /* 8K */
+ vdev_boot_envblock_t vl_be; /* 8K */
vdev_phys_t vl_vdev_phys; /* 112K */
char vl_uberblock[VDEV_UBERBLOCK_RING]; /* 128K */
} vdev_label_t; /* 256K total */
Index: sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_bootenv.h
===================================================================
--- /dev/null
+++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_bootenv.h
@@ -0,0 +1,49 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2020 Toomas Soome <tsoome@me.com>
+ */
+
+#ifndef _ZFS_BOOTENV_H
+#define _ZFS_BOOTENV_H
+
+/*
+ * Define macros for label bootenv nvlist pair keys.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define BOOTENV_VERSION "version"
+
+#define BE_ILLUMOS_VENDOR "illumos"
+#define BE_FREEBSD_VENDOR "freebsd"
+#define BE_GRUB_VENDOR "grub"
+
+#define BOOTENV_OS BE_FREEBSD_VENDOR
+
+#define GRUB_ENVMAP BE_GRUB_VENDOR ":" "envmap"
+
+#define FREEBSD_BOOTONCE BE_FREEBSD_VENDOR ":" "bootonce"
+#define FREEBSD_BOOTONCE_USED BE_FREEBSD_VENDOR ":" "bootonce-used"
+#define ILLUMOS_BOOTONCE BE_ILLUMOS_VENDOR ":" "bootonce"
+#define ILLUMOS_BOOTONCE_USED BE_ILLUMOS_VENDOR ":" "bootonce-used"
+
+#define OS_BOOTONCE BOOTENV_OS ":" "bootonce"
+#define OS_BOOTONCE_USED BOOTENV_OS ":" "bootonce-used"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ZFS_BOOTENV_H */
Index: sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev.c
===================================================================
--- sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev.c
+++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev.c
@@ -1566,7 +1566,7 @@
for (int l = 1; l < VDEV_LABELS; l++) {
zio_nowait(zio_read_phys(pio, vd,
vdev_label_offset(vd->vdev_psize, l,
- offsetof(vdev_label_t, vl_pad2)), VDEV_PAD_SIZE,
+ offsetof(vdev_label_t, vl_be)), VDEV_PAD_SIZE,
abd_alloc_for_io(VDEV_PAD_SIZE, B_TRUE),
ZIO_CHECKSUM_OFF, vdev_probe_done, vps,
ZIO_PRIORITY_SYNC_READ, vps->vps_flags, B_TRUE));
Index: sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_label.c
===================================================================
--- sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_label.c
+++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_label.c
@@ -21,7 +21,7 @@
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2012, 2018 by Delphix. All rights reserved.
+ * Copyright (c) 2012, 2020 by Delphix. All rights reserved.
* Copyright (c) 2017, Intel Corporation.
* Copyright 2019 Joyent, Inc.
*/
@@ -151,6 +151,7 @@
#include <sys/abd.h>
#include <sys/fs/zfs.h>
#include <sys/trim_map.h>
+#include <sys/byteorder.h>
static boolean_t vdev_trim_on_init = B_TRUE;
SYSCTL_DECL(_vfs_zfs_vdev);
@@ -781,7 +782,7 @@
nvlist_t *label;
vdev_phys_t *vp;
abd_t *vp_abd;
- abd_t *pad2;
+ abd_t *bootenv;
uberblock_t *ub;
abd_t *ub_abd;
zio_t *zio;
@@ -956,8 +957,8 @@
ub->ub_txg = 0;
/* Initialize the 2nd padding area. */
- pad2 = abd_alloc_for_io(VDEV_PAD_SIZE, B_TRUE);
- abd_zero(pad2, VDEV_PAD_SIZE);
+ bootenv = abd_alloc_for_io(VDEV_PAD_SIZE, B_TRUE);
+ abd_zero(bootenv, VDEV_PAD_SIZE);
/*
* Write everything in parallel.
@@ -976,8 +977,8 @@
* Zero out the 2nd padding area where it might have
* left over data from previous filesystem format.
*/
- vdev_label_write(zio, vd, l, pad2,
- offsetof(vdev_label_t, vl_pad2),
+ vdev_label_write(zio, vd, l, bootenv,
+ offsetof(vdev_label_t, vl_be),
VDEV_PAD_SIZE, NULL, NULL, flags);
vdev_label_write(zio, vd, l, ub_abd,
@@ -993,7 +994,7 @@
}
nvlist_free(label);
- abd_free(pad2);
+ abd_free(bootenv);
abd_free(ub_abd);
abd_free(vp_abd);
@@ -1016,41 +1017,202 @@
return (error);
}
+/*
+ * Done callback for vdev_label_read_bootenv_impl. If this is the first
+ * callback to finish, store our abd in the callback pointer. Otherwise, we
+ * just free our abd and return.
+ */
+static void
+vdev_label_read_bootenv_done(zio_t *zio)
+{
+ zio_t *rio = zio->io_private;
+ abd_t **cbp = rio->io_private;
+
+ ASSERT3U(zio->io_size, ==, VDEV_PAD_SIZE);
+
+ if (zio->io_error == 0) {
+ mutex_enter(&rio->io_lock);
+ if (*cbp == NULL) {
+ /* Will free this buffer in vdev_label_read_bootenv. */
+ *cbp = zio->io_abd;
+ } else {
+ abd_free(zio->io_abd);
+ }
+ mutex_exit(&rio->io_lock);
+ } else {
+ abd_free(zio->io_abd);
+ }
+}
+
+static void
+vdev_label_read_bootenv_impl(zio_t *zio, vdev_t *vd, int flags)
+{
+ for (int c = 0; c < vd->vdev_children; c++)
+ vdev_label_read_bootenv_impl(zio, vd->vdev_child[c], flags);
+
+ /*
+ * We just use the first label that has a correct checksum; the
+ * bootloader should have rewritten them all to be the same on boot,
+ * and any changes we made since boot have been the same across all
+ * labels.
+ */
+ if (vd->vdev_ops->vdev_op_leaf && vdev_readable(vd)) {
+ for (int l = 0; l < VDEV_LABELS; l++) {
+ vdev_label_read(zio, vd, l,
+ abd_alloc_linear(VDEV_PAD_SIZE, B_FALSE),
+ offsetof(vdev_label_t, vl_be), VDEV_PAD_SIZE,
+ vdev_label_read_bootenv_done, zio, flags);
+ }
+ }
+}
+
int
-vdev_label_write_pad2(vdev_t *vd, const char *buf, size_t size)
+vdev_label_read_bootenv(vdev_t *rvd, nvlist_t *bootenv)
{
- spa_t *spa = vd->vdev_spa;
+ nvlist_t *config;
+ spa_t *spa = rvd->vdev_spa;
+ abd_t *abd = NULL;
+ int flags = ZIO_FLAG_CONFIG_WRITER | ZIO_FLAG_CANFAIL |
+ ZIO_FLAG_SPECULATIVE | ZIO_FLAG_TRYHARD;
+
+ ASSERT(bootenv);
+ ASSERT(spa_config_held(spa, SCL_ALL, RW_WRITER) == SCL_ALL);
+
+ zio_t *zio = zio_root(spa, NULL, &abd, flags);
+ vdev_label_read_bootenv_impl(zio, rvd, flags);
+ int err = zio_wait(zio);
+
+ if (abd != NULL) {
+ char *buf;
+ vdev_boot_envblock_t *vbe = abd_to_buf(abd);
+
+ vbe->vbe_version = ntohll(vbe->vbe_version);
+ switch (vbe->vbe_version) {
+ case VB_RAW:
+ /*
+ * we have textual data in vbe_bootenv, create nvlist
+ * with key "envmap".
+ */
+ vbe->vbe_bootenv[sizeof (vbe->vbe_bootenv) - 1] = '\0';
+ fnvlist_add_string(bootenv, "envmap", vbe->vbe_bootenv);
+ break;
+
+ case VB_NVLIST:
+ err = nvlist_unpack(vbe->vbe_bootenv,
+ sizeof (vbe->vbe_bootenv), &config, 0);
+ if (err == 0) {
+ fnvlist_merge(bootenv, config);
+ nvlist_free(config);
+ break;
+ }
+ /* FALLTHROUGH */
+ default:
+ /* Check for FreeBSD zfs bootonce command string */
+ buf = abd_to_buf(abd);
+ fnvlist_add_string(bootenv, "command", buf);
+ }
+
+ /*
+ * abd was allocated in vdev_label_read_bootenv_impl()
+ */
+ abd_free(abd);
+ /*
+ * If we managed to read any successfully,
+ * return success.
+ */
+ return (0);
+ }
+ return (err);
+}
+
+int
+vdev_label_write_bootenv(vdev_t *vd, nvlist_t *env)
+{
zio_t *zio;
- abd_t *pad2;
+ spa_t *spa = vd->vdev_spa;
+ vdev_boot_envblock_t *bootenv;
int flags = ZIO_FLAG_CONFIG_WRITER | ZIO_FLAG_CANFAIL;
int error;
+ size_t nvsize;
+ char *nvbuf;
- if (size > VDEV_PAD_SIZE)
- return (EINVAL);
+ error = nvlist_size(env, &nvsize, NV_ENCODE_XDR);
+ if (error != 0)
+ return (SET_ERROR(error));
- if (!vd->vdev_ops->vdev_op_leaf)
- return (ENODEV);
- if (vdev_is_dead(vd))
- return (ENXIO);
+ if (nvsize >= sizeof (bootenv->vbe_bootenv)) {
+ return (SET_ERROR(E2BIG));
+ }
ASSERT(spa_config_held(spa, SCL_ALL, RW_WRITER) == SCL_ALL);
- pad2 = abd_alloc_for_io(VDEV_PAD_SIZE, B_TRUE);
- abd_zero(pad2, VDEV_PAD_SIZE);
- abd_copy_from_buf(pad2, buf, size);
+ error = ENXIO;
+ for (int c = 0; c < vd->vdev_children; c++) {
+ int child_err;
+ child_err = vdev_label_write_bootenv(vd->vdev_child[c], env);
+ /*
+ * As long as any of the disks managed to write all of their
+ * labels successfully, return success.
+ */
+ if (child_err == 0)
+ error = child_err;
+ }
+
+ if (!vd->vdev_ops->vdev_op_leaf || vdev_is_dead(vd) ||
+ !vdev_writeable(vd)) {
+ return (error);
+ }
+ ASSERT3U(sizeof (*bootenv), ==, VDEV_PAD_SIZE);
+ abd_t *abd = abd_alloc_for_io(VDEV_PAD_SIZE, B_TRUE);
+ abd_zero(abd, VDEV_PAD_SIZE);
+
+ bootenv = abd_borrow_buf_copy(abd, VDEV_PAD_SIZE);
+ nvbuf = bootenv->vbe_bootenv;
+ nvsize = sizeof (bootenv->vbe_bootenv);
+
+ bootenv->vbe_version = fnvlist_lookup_uint64(env, "version");
+ switch (bootenv->vbe_version) {
+ case VB_RAW:
+ if (nvlist_lookup_string(env, "envmap", &nvbuf) == 0) {
+ (void) strlcpy(bootenv->vbe_bootenv, nvbuf, nvsize);
+ }
+ error = 0;
+ break;
+
+ case VB_NVLIST:
+ error = nvlist_pack(env, &nvbuf, &nvsize, NV_ENCODE_XDR,
+ KM_SLEEP);
+ break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+
+ if (error == 0) {
+ bootenv->vbe_version = htonll(bootenv->vbe_version);
+ abd_return_buf_copy(abd, bootenv, VDEV_PAD_SIZE);
+ } else {
+ abd_free(abd);
+ return (SET_ERROR(error));
+ }
+
retry:
zio = zio_root(spa, NULL, NULL, flags);
- vdev_label_write(zio, vd, 0, pad2,
- offsetof(vdev_label_t, vl_pad2),
- VDEV_PAD_SIZE, NULL, NULL, flags);
+ for (int l = 0; l < VDEV_LABELS; l++) {
+ vdev_label_write(zio, vd, l, abd,
+ offsetof(vdev_label_t, vl_be),
+ VDEV_PAD_SIZE, NULL, NULL, flags);
+ }
+
error = zio_wait(zio);
if (error != 0 && !(flags & ZIO_FLAG_TRYHARD)) {
flags |= ZIO_FLAG_TRYHARD;
goto retry;
}
- abd_free(pad2);
+ abd_free(abd);
return (error);
}
Index: sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c
===================================================================
--- sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c
+++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c
@@ -3654,50 +3654,72 @@
return (error);
}
+/*
+ * This ioctl is used to set the bootenv configuration on the current
+ * pool. This configuration is stored in the second padding area of the label,
+ * and it is used by the bootloader(s) to store bootloader and/or system
+ * specific data.
+ * The data is stored as nvlist data stream, and is protected by
+ * an embedded checksum.
+ */
+/* ARGSUSED */
+static const zfs_ioc_key_t zfs_keys_set_bootenv[] = {
+ {"version", DATA_TYPE_UINT64, 0},
+ {"<keys>", DATA_TYPE_ANY, ZK_OPTIONAL | ZK_WILDCARDLIST},
+};
+
+static int
+zfs_ioc_set_bootenv(const char *name, nvlist_t *innvl, nvlist_t *outnvl)
+{
+ int error;
+ spa_t *spa;
+
+ if ((error = spa_open(name, &spa, FTAG)) != 0)
+ return (error);
+ spa_vdev_state_enter(spa, SCL_ALL);
+ error = vdev_label_write_bootenv(spa->spa_root_vdev, innvl);
+ (void) spa_vdev_state_exit(spa, NULL, 0);
+ spa_close(spa, FTAG);
+ return (error);
+}
+
+static const zfs_ioc_key_t zfs_keys_get_bootenv[] = {
+ /* no nvl keys */
+};
+
+ /* ARGSUSED */
+static int
+zfs_ioc_get_bootenv(const char *name, nvlist_t *innvl, nvlist_t *outnvl)
+{
+ spa_t *spa;
+ int error;
+
+ if ((error = spa_open(name, &spa, FTAG)) != 0)
+ return (error);
+ spa_vdev_state_enter(spa, SCL_ALL);
+ error = vdev_label_read_bootenv(spa->spa_root_vdev, outnvl);
+ (void) spa_vdev_state_exit(spa, NULL, 0);
+ spa_close(spa, FTAG);
+ return (error);
+}
+
#ifdef __FreeBSD__
static const zfs_ioc_key_t zfs_keys_nextboot[] = {
{"command", DATA_TYPE_STRING, 0},
};
static int
-zfs_ioc_nextboot(const char *unused, nvlist_t *innvl, nvlist_t *outnvl)
+zfs_ioc_nextboot(const char *name, nvlist_t *innvl, nvlist_t *outnvl)
{
- char name[MAXNAMELEN];
spa_t *spa;
- vdev_t *vd;
- char *command;
- uint64_t pool_guid;
- uint64_t vdev_guid;
int error;
- if (nvlist_lookup_uint64(innvl,
- ZPOOL_CONFIG_POOL_GUID, &pool_guid) != 0)
- return (EINVAL);
- if (nvlist_lookup_uint64(innvl,
- ZPOOL_CONFIG_GUID, &vdev_guid) != 0)
- return (EINVAL);
- command = fnvlist_lookup_string(innvl, "command");
-
- mutex_enter(&spa_namespace_lock);
- spa = spa_by_guid(pool_guid, vdev_guid);
- if (spa != NULL)
- strcpy(name, spa_name(spa));
- mutex_exit(&spa_namespace_lock);
- if (spa == NULL)
- return (ENOENT);
-
if ((error = spa_open(name, &spa, FTAG)) != 0)
return (error);
+
spa_vdev_state_enter(spa, SCL_ALL);
- vd = spa_lookup_by_guid(spa, vdev_guid, B_TRUE);
- if (vd == NULL) {
- (void) spa_vdev_state_exit(spa, NULL, ENXIO);
- spa_close(spa, FTAG);
- return (ENODEV);
- }
- error = vdev_label_write_pad2(vd, command, strlen(command));
+ error = vdev_label_write_bootenv(spa->spa_root_vdev, innvl);
(void) spa_vdev_state_exit(spa, NULL, 0);
- txg_wait_synced(spa->spa_dsl_pool, 0);
spa_close(spa, FTAG);
return (error);
}
@@ -6564,6 +6586,16 @@
zfs_ioctl_register("reopen", ZFS_IOC_POOL_REOPEN, zfs_ioc_pool_reopen,
zfs_secpolicy_config, POOL_NAME, POOL_CHECK_SUSPENDED, B_TRUE,
B_TRUE, zfs_keys_pool_reopen, ARRAY_SIZE(zfs_keys_pool_reopen));
+
+ zfs_ioctl_register("set_bootenv", ZFS_IOC_SET_BOOTENV,
+ zfs_ioc_set_bootenv, zfs_secpolicy_config, POOL_NAME,
+ POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_FALSE, B_TRUE,
+ zfs_keys_set_bootenv, ARRAY_SIZE(zfs_keys_set_bootenv));
+
+ zfs_ioctl_register("get_bootenv", ZFS_IOC_GET_BOOTENV,
+ zfs_ioc_get_bootenv, zfs_secpolicy_none, POOL_NAME,
+ POOL_CHECK_SUSPENDED, B_FALSE, B_TRUE,
+ zfs_keys_get_bootenv, ARRAY_SIZE(zfs_keys_get_bootenv));
/* IOCTLS that use the legacy function signature */
Index: sys/cddl/contrib/opensolaris/uts/common/sys/fs/zfs.h
===================================================================
--- sys/cddl/contrib/opensolaris/uts/common/sys/fs/zfs.h
+++ sys/cddl/contrib/opensolaris/uts/common/sys/fs/zfs.h
@@ -21,7 +21,7 @@
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2011, 2018 by Delphix. All rights reserved.
+ * Copyright (c) 2011, 2020 by Delphix. All rights reserved.
* Copyright 2011 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2012, Martin Matuska <mm@FreeBSD.org>. All rights reserved.
* Copyright (c) 2014 Integros [integros.com]
@@ -1057,6 +1057,8 @@
ZFS_IOC_POOL_DISCARD_CHECKPOINT,
ZFS_IOC_POOL_INITIALIZE,
ZFS_IOC_POOL_SYNC,
+ ZFS_IOC_SET_BOOTENV,
+ ZFS_IOC_GET_BOOTENV,
ZFS_IOC_LAST
} zfs_ioc_t;
Index: tools/tools/zfsboottest/zfsboottest.c
===================================================================
--- tools/tools/zfsboottest/zfsboottest.c
+++ tools/tools/zfsboottest/zfsboottest.c
@@ -147,7 +147,7 @@
warn("open(%s) failed", argv[i]);
continue;
}
- if (vdev_probe(vdev_read, &fd[i - 1], NULL) != 0) {
+ if (vdev_probe(vdev_read, NULL, &fd[i - 1], NULL) != 0) {
warnx("vdev_probe(%s) failed", argv[i]);
close(fd[i - 1]);
}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sun, Oct 19, 1:27 AM (6 h, 50 s)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
23903611
Default Alt Text
D25512.id74917.diff (103 KB)
Attached To
Mode
D25512: WIP bootnext: Next Generation
Attached
Detach File
Event Timeline
Log In to Comment