Page MenuHomeFreeBSD

D25512.diff
No OneTemporary

D25512.diff

Index: head/Makefile.inc1
===================================================================
--- head/Makefile.inc1
+++ head/Makefile.inc1
@@ -2835,7 +2835,7 @@
${_cddl_lib_libtpool} \
${_cddl_lib_libzfs_core} ${_cddl_lib_libzfs} \
${_cddl_lib_libzutil} \
- ${_cddl_lib_libctf} \
+ ${_cddl_lib_libctf} ${_cddl_lib_libzfsbootenv} \
lib/libufs \
lib/libutil lib/libpjdlog ${_lib_libypclnt} lib/libz lib/msun \
${_secure_lib_libcrypto} ${_secure_lib_libssl} \
@@ -2915,6 +2915,7 @@
_cddl_lib_libzutil= cddl/lib/libzutil
_cddl_lib_libzfs_core= cddl/lib/libzfs_core
_cddl_lib_libzfs= cddl/lib/libzfs
+_cddl_lib_libzfsbootenv= cddl/lib/libzfsbootenv
cddl/lib/libtpool__L: cddl/lib/libspl__L
@@ -2928,7 +2929,8 @@
cddl/lib/libzfs__L: cddl/lib/libnvpair__L cddl/lib/libzutil__L
cddl/lib/libzfs__L: secure/lib/libcrypto__L
-lib/libbe__L: cddl/lib/libzfs__L
+cddl/lib/libzfsbootenv__L: cddl/lib/libzfs__L
+lib/libbe__L: cddl/lib/libzfs__L cddl/lib/libzfsbootenv__L
.endif
_cddl_lib_libctf= cddl/lib/libctf
_cddl_lib= cddl/lib
Index: head/cddl/lib/Makefile
===================================================================
--- head/cddl/lib/Makefile
+++ head/cddl/lib/Makefile
@@ -15,6 +15,7 @@
libuutil \
${_libzfs_core} \
${_libzfs} \
+ ${_libzfsbootenv} \
${_libzpool} \
${_libzutil}
@@ -26,6 +27,7 @@
_libicp_rescue= libicp_rescue
_libzfs= libzfs
_libzutil= libzutil
+_libzfsbootenv= libzfsbootenv
.if ${MK_LIBTHR} != "no"
_libzpool= libzpool
_libtpool= libtpool
@@ -40,6 +42,7 @@
SUBDIR_DEPEND_libzfs= libavl libnvpair libumem libuutil libzfs_core libzutil
SUBDIR_DEPEND_libzpool= libavl libnvpair libumem libicp
SUBDIR_DEPEND_libzutil= libavl libtpool
+SUBDIR_DEPEND_libzfsbootenv= libzfs libnvpair
SUBDIR_PARALLEL=
Index: head/cddl/lib/libzfsbootenv/Makefile
===================================================================
--- head/cddl/lib/libzfsbootenv/Makefile
+++ head/cddl/lib/libzfsbootenv/Makefile
@@ -0,0 +1,33 @@
+# $FreeBSD$
+
+.PATH: ${SRCTOP}/sys/contrib/openzfs/lib/libzfsbootenv
+.PATH: ${SRCTOP}/sys/contrib/openzfs/include
+
+PACKAGE= runtime
+LIB= zfsbootenv
+SHLIB_MAJOR= 1
+
+LIBADD= zfs
+LIBADD+= nvpair
+
+INCS= libzfsbootenv.h
+USER_C= \
+ lzbe_device.c \
+ lzbe_util.c \
+ lzbe_pair.c
+
+SRCS= $(USER_C)
+
+CSTD= c99
+CFLAGS+= -DIN_BASE
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/include
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/os/freebsd
+CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/include
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/module/icp/include
+CFLAGS+= -include ${SRCTOP}/sys/contrib/openzfs/include/os/freebsd/spl/sys/ccompile.h
+CFLAGS+= -DHAVE_ISSETUGID
+CFLAGS+= -include ${SRCTOP}/sys/modules/zfs/zfs_config.h
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/include/os/freebsd/zfs
+
+.include <bsd.lib.mk>
Index: head/cddl/lib/libzpool/Makefile
===================================================================
--- head/cddl/lib/libzpool/Makefile
+++ head/cddl/lib/libzpool/Makefile
@@ -228,6 +228,7 @@
-I${ZFSTOP}/lib/libspl/include \
-I${ZFSTOP}/lib/libspl/include/os/freebsd \
-I${SRCTOP}/sys \
+ -I${ZFSTOP}/include/os/freebsd/zfs \
-I${SRCTOP}/cddl/compat/opensolaris/include \
-I${ZFSTOP}/module/icp/include \
-include ${ZFSTOP}/include/os/freebsd/spl/sys/ccompile.h \
Index: head/lib/libbe/Makefile
===================================================================
--- head/lib/libbe/Makefile
+++ head/lib/libbe/Makefile
@@ -13,7 +13,9 @@
MAN= libbe.3
LIBADD+= zfs
-LIBADD+= nvpair spl
+LIBADD+= nvpair
+LIBADD+= spl
+LIBADD+= zfsbootenv
CFLAGS+= -DIN_BASE -DHAVE_RPC_TYPES
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/include
Index: head/lib/libbe/be.h
===================================================================
--- head/lib/libbe/be.h
+++ head/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: head/lib/libbe/be.c
===================================================================
--- head/lib/libbe/be.c
+++ head/lib/libbe/be.c
@@ -46,6 +46,7 @@
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
+#include <libzfsbootenv.h>
#include "be.h"
#include "be_impl.h"
@@ -1221,43 +1222,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), lzbe_add, NULL));
+ }
+
if ((zfs = zfs_open(lbh->lzh, ds, ZFS_TYPE_DATASET)) == NULL)
return (1);
if (zfs_prop_set(zfs, "canmount", "noauto") != 0)
@@ -1270,10 +1248,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;
@@ -1284,27 +1260,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), lzbe_add, be_path));
} else {
- if (be_deactivate(lbh, lbh->bootfs) != 0)
+ if (be_deactivate(lbh, lbh->bootfs, false) != 0)
return (-1);
/* Obtain bootenv zpool */
Index: head/lib/libbe/be_impl.h
===================================================================
--- head/lib/libbe/be_impl.h
+++ head/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: head/lib/libbe/be_info.c
===================================================================
--- head/lib/libbe/be_info.c
+++ head/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: head/lib/libbe/libbe.3
===================================================================
--- head/lib/libbe/libbe.3
+++ head/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: head/libexec/rc/rc.conf
===================================================================
--- head/libexec/rc/rc.conf
+++ head/libexec/rc/rc.conf
@@ -64,6 +64,7 @@
# ZFS support
zfs_enable="NO" # Set to YES to automatically mount ZFS file systems
+zfs_bootonce_activate="NO" # Set YES to make successful bootonce BE permanent
# ZFSD support
zfsd_enable="NO" # Set to YES to automatically start the ZFS fault
Index: head/libexec/rc/rc.d/zfsbe
===================================================================
--- head/libexec/rc/rc.d/zfsbe
+++ head/libexec/rc/rc.d/zfsbe
@@ -48,6 +48,21 @@
done
}
+activate_bootonce()
+{
+ local _dev
+ local _bootonce
+ local _be
+
+ _dev=$1
+ _be=${_dev##*/}
+
+ _bootonce="`kenv -q zfs-bootonce`"
+ if [ "$_bootonce" = "zfs:${_dev}:" ] ; then
+ bectl activate $_be
+ fi
+}
+
be_start()
{
if [ `$SYSCTL_N security.jail.jailed` -eq 1 ]; then
@@ -57,6 +72,9 @@
[ $_mp = "/" ] || continue
if [ $_type = "zfs" ] ; then
mount_subordinate $_dev
+ if checkyesno zfs_bootonce_activate; then
+ activate_bootonce $_dev
+ fi
fi
break
done
Index: head/rescue/rescue/Makefile
===================================================================
--- head/rescue/rescue/Makefile
+++ head/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 -lzutil -ltpool -lspl -licp_rescue
+CRUNCH_LIBS+= -lbe -lzfsbootenv -lzutil -ltpool -lspl -licp_rescue
.else
# liblzma needs pthread
CRUNCH_LIBS+= -lpthread
Index: head/sbin/bectl/Makefile
===================================================================
--- head/sbin/bectl/Makefile
+++ head/sbin/bectl/Makefile
@@ -12,6 +12,7 @@
nvpair \
spl \
util \
+ zfsbootenv
CFLAGS+= -DIN_BASE
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/include
Index: head/sbin/bectl/bectl.8
===================================================================
--- head/sbin/bectl/bectl.8
+++ head/sbin/bectl/bectl.8
@@ -17,7 +17,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd August 17, 2020
+.Dd August 25, 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,7 +95,7 @@
.Bl -tag -width activate
.It Xo
.Cm activate
-.Op Fl t
+.Op Fl t | Fl T
.Ar beName
.Xc
Activate the given
@@ -104,6 +104,13 @@
If the
.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: head/sbin/bectl/bectl.c
===================================================================
--- head/sbin/bectl/bectl.c
+++ head/sbin/bectl/bectl.c
@@ -74,6 +74,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"
@@ -141,14 +142,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);
@@ -159,11 +168,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: head/sbin/bectl/bectl_list.c
===================================================================
--- head/sbin/bectl/bectl_list.c
+++ head/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: head/sbin/reboot/nextboot.8
===================================================================
--- head/sbin/reboot/nextboot.8
+++ head/sbin/reboot/nextboot.8
@@ -24,7 +24,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd April 9, 2016
+.Dd September 19, 2020
.Dt NEXTBOOT 8
.Os
.Sh NAME
@@ -136,8 +136,3 @@
Finally, it does some evil things like writing to the file system before it
has been checked.
If it scrambles your file system, do not blame me.
-.Pp
-.Xr loader 8
-is only able to read ZFS, not write to it.
-.Pa nextboot.conf
-will NOT be reset in case of a kernel boot failure.
Index: head/sbin/reboot/nextboot.sh
===================================================================
--- head/sbin/reboot/nextboot.sh
+++ head/sbin/reboot/nextboot.sh
@@ -33,6 +33,7 @@
kenv=
force="NO"
nextboot_file="/boot/nextboot.conf"
+zfs=
add_kenv()
{
@@ -106,26 +107,26 @@
exit 1
fi
-df -Tn "/boot/" 2>/dev/null | while read _fs _type _other ; do
+zfs=$(df -Tn "/boot/" 2>/dev/null | while read _fs _type _other ; do
[ "zfs" = "${_type}" ] || continue
- cat 1>&2 <<-EOF
- WARNING: loader(8) has only R/O support for ZFS
- nextboot.conf will NOT be reset in case of kernel boot failure
- EOF
-done
+ echo "${_fs%/ROOT/*}"
+done)
set -e
nextboot_tmp=$(mktemp $(dirname ${nextboot_file})/nextboot.XXXXXX)
-if [ ${append} = "YES" -a -f ${nextboot_file} ]; then
- cp -f ${nextboot_file} ${nextboot_tmp}
-fi
-
+if [ -n ${zfs} ]; then
+ zfsbootcfg -z ${zfs} -n freebsd:nvstore -k nextboot_enable -v YES
+ cat >> ${nextboot_tmp} << EOF
+$kenv
+EOF
+else
cat >> ${nextboot_tmp} << EOF
nextboot_enable="YES"
$kenv
EOF
+fi
fsync ${nextboot_tmp}
Index: head/sbin/zfsbootcfg/Makefile
===================================================================
--- head/sbin/zfsbootcfg/Makefile
+++ head/sbin/zfsbootcfg/Makefile
@@ -2,14 +2,9 @@
# $FreeBSD$
PROG= zfsbootcfg
-WARNS?= 2
MAN= zfsbootcfg.8
-LIBADD+=zfs
-LIBADD+=nvpair
-LIBADD+=umem
-LIBADD+=uutil
-LIBADD+=geom
+LIBADD+=zfsbootenv
CFLAGS+= -DIN_BASE
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/include
Index: head/sbin/zfsbootcfg/zfsbootcfg.8
===================================================================
--- head/sbin/zfsbootcfg/zfsbootcfg.8
+++ head/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,88 @@
.Sh SYNOPSIS
.Nm
.Ao Ar options Ac
+.Nm
+.Op Fl n Ar name
+.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 n Ar name
+Update nvlist
+.Ar name .
+.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 supported types are:
+.Bl -tag -width indent -compact
+.It Ar DATA_TYPE_BYTE
+.It Ar DATA_TYPE_INT8
+.It Ar DATA_TYPE_UINT8
+.It Ar DATA_TYPE_INT16
+.It Ar DATA_TYPE_UINT16
+.It Ar DATA_TYPE_INT32
+.It Ar DATA_TYPE_UINT32
+.It Ar DATA_TYPE_INT64
+.It Ar DATA_TYPE_UINT64
+.It Ar DATA_TYPE_BOOLEAN_VALUE
+.It Ar DATA_TYPE_STRING
+.El
+.Pp
+If not specified, the default 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 +130,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 +141,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: head/sbin/zfsbootcfg/zfsbootcfg.c
===================================================================
--- head/sbin/zfsbootcfg/zfsbootcfg.c
+++ head/sbin/zfsbootcfg/zfsbootcfg.c
@@ -32,115 +32,257 @@
#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)
+static int
+add_pair(const char *name, const char *nvlist, const char *key,
+ const char *type, const char *value)
{
- nvlist_t **child;
- uint_t children = 0;
- uint64_t guid;
+ void *data, *nv;
+ size_t size;
int rv;
+ char *end;
- (void) nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN, &child,
- &children);
+ rv = lzbe_nvlist_get(name, nvlist, &nv);
+ if (rv != 0)
+ return (rv);
- for (int c = 0; c < children; c++) {
- rv = install_bootonce(hdl, pool_guid, child[c], data);
- }
+ data = NULL;
+ rv = EINVAL;
+ if (strcmp(type, "DATA_TYPE_STRING") == 0) {
+ data = __DECONST(void *, value);
+ size = strlen(data) + 1;
+ rv = lzbe_add_pair(nv, key, type, data, size);
+ } else if (strcmp(type, "DATA_TYPE_UINT64") == 0) {
+ uint64_t v;
- if (children > 0)
- return (rv);
+ v = strtoull(value, &end, 0);
+ if (errno != 0 || *end != '\0')
+ goto done;
+ size = sizeof (v);
+ rv = lzbe_add_pair(nv, key, type, &v, size);
+ } else if (strcmp(type, "DATA_TYPE_INT64") == 0) {
+ int64_t v;
- if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID, &guid) != 0) {
- perror("can't get vdev guid");
- return (1);
+ v = strtoll(value, &end, 0);
+ if (errno != 0 || *end != '\0')
+ goto done;
+ size = sizeof (v);
+ rv = lzbe_add_pair(nv, key, type, &v, size);
+ } else if (strcmp(type, "DATA_TYPE_UINT32") == 0) {
+ uint32_t v;
+
+ v = strtoul(value, &end, 0);
+ if (errno != 0 || *end != '\0')
+ goto done;
+ size = sizeof (v);
+ rv = lzbe_add_pair(nv, key, type, &v, size);
+ } else if (strcmp(type, "DATA_TYPE_INT32") == 0) {
+ int32_t v;
+
+ v = strtol(value, &end, 0);
+ if (errno != 0 || *end != '\0')
+ goto done;
+ size = sizeof (v);
+ rv = lzbe_add_pair(nv, key, type, &v, size);
+ } else if (strcmp(type, "DATA_TYPE_UINT16") == 0) {
+ uint16_t v;
+
+ v = strtoul(value, &end, 0);
+ if (errno != 0 || *end != '\0')
+ goto done;
+ size = sizeof (v);
+ rv = lzbe_add_pair(nv, key, type, &v, size);
+ } else if (strcmp(type, "DATA_TYPE_INT16") == 0) {
+ int16_t v;
+
+ v = strtol(value, &end, 0);
+ if (errno != 0 || *end != '\0')
+ goto done;
+ size = sizeof (v);
+ rv = lzbe_add_pair(nv, key, type, &v, size);
+ } else if (strcmp(type, "DATA_TYPE_UINT8") == 0) {
+ uint8_t v;
+
+ v = strtoul(value, &end, 0);
+ if (errno != 0 || *end != '\0')
+ goto done;
+ size = sizeof (v);
+ rv = lzbe_add_pair(nv, key, type, &v, size);
+ } else if (strcmp(type, "DATA_TYPE_INT8") == 0) {
+ int8_t v;
+
+ v = strtol(value, &end, 0);
+ if (errno != 0 || *end != '\0')
+ goto done;
+ size = sizeof (v);
+ rv = lzbe_add_pair(nv, key, type, &v, size);
+ } else if (strcmp(type, "DATA_TYPE_BYTE") == 0) {
+ uint8_t v;
+
+ v = strtoul(value, &end, 0);
+ if (errno != 0 || *end != '\0')
+ goto done;
+ size = sizeof (v);
+ rv = lzbe_add_pair(nv, key, type, &v, size);
+ } else if (strcmp(type, "DATA_TYPE_BOOLEAN_VALUE") == 0) {
+ int32_t v;
+
+ v = strtol(value, &end, 0);
+ if (errno != 0 || *end != '\0') {
+ if (strcasecmp(value, "YES") == 0)
+ v = 1;
+ else if (strcasecmp(value, "NO") == 0)
+ v = 0;
+ if (strcasecmp(value, "true") == 0)
+ v = 1;
+ else if (strcasecmp(value, "false") == 0)
+ v = 0;
+ else goto done;
+ }
+ size = sizeof (v);
+ rv = lzbe_add_pair(nv, key, type, &v, size);
}
- if (zpool_nextboot(hdl, pool_guid, guid, data) != 0) {
- perror("ZFS_IOC_NEXTBOOT failed");
- return (1);
- }
- return (0);
+
+ if (rv == 0)
+ rv = lzbe_nvlist_set(name, nvlist, nv);
+
+done:
+ lzbe_nvlist_free(nv);
+ return (rv);
}
-int main(int argc, const char * const *argv)
+static int
+delete_pair(const char *name, const char *nvlist, const char *key)
{
- char buf[32], *name;
- libzfs_handle_t *hdl;
- zpool_handle_t *zphdl;
- uint64_t pool_guid;
- nvlist_t *nv, *config;
+ void *nv;
int rv;
- int len;
- if (argc != 2) {
- fprintf(stderr, "usage: zfsbootcfg <boot.config(5) options>\n");
- return (1);
+ rv = lzbe_nvlist_get(name, nvlist, &nv);
+ if (rv == 0) {
+ rv = lzbe_remove_pair(nv, key);
}
+ if (rv == 0)
+ rv = lzbe_nvlist_set(name, nvlist, nv);
- len = strlen(argv[1]);
- if (len >= MAX_COMMAND_LEN) {
- fprintf(stderr, "options string is too long\n");
- return (1);
- }
+ lzbe_nvlist_free(nv);
+ return (rv);
+}
- if (kenv(KENV_GET, "vfs.root.mountfrom", buf, sizeof(buf)) <= 0) {
- perror("can't get vfs.root.mountfrom");
- return (1);
+/*
+ * Usage: zfsbootcfg [-z pool] [-d key] [-k key -t type -v value] [-p]
+ * zfsbootcfg [-z pool] -n nvlist [-d key] [-k key -t type -v value] [-p]
+ *
+ * if nvlist is set, we will update nvlist in bootenv.
+ * if nvlist is not set, we update pairs in bootenv.
+ */
+int
+main(int argc, char * const *argv)
+{
+ char buf[ZFS_MAXNAMELEN], *name;
+ const char *key, *value, *type, *nvlist;
+ int rv;
+ bool print, delete;
+
+ nvlist = NULL;
+ name = NULL;
+ key = NULL;
+ type = NULL;
+ value = NULL;
+ print = delete = false;
+ while ((rv = getopt(argc, argv, "d:k:n:pt:v:z:")) != -1) {
+ switch (rv) {
+ case 'd':
+ delete = true;
+ key = optarg;
+ break;
+ case 'k':
+ key = optarg;
+ break;
+ case 'n':
+ nvlist = optarg;
+ break;
+ case 'p':
+ print = true;
+ break;
+ case 't':
+ type = optarg;
+ break;
+ case 'v':
+ value = optarg;
+ break;
+ case 'z':
+ name = optarg;
+ break;
+ }
}
- if (strncmp(buf, "zfs:", 4) == 0) {
- name = strchr(buf + 4, '/');
- if (name != NULL)
- *name = '\0';
- name = buf + 4;
- } else {
- perror("not a zfs root");
+ argc -= optind;
+ argv += optind;
+
+ if (argc == 1)
+ value = argv[0];
+
+ if (argc > 1) {
+ fprintf(stderr, "usage: zfsbootcfg <boot.config(5) options>\n");
return (1);
}
-
- if ((hdl = libzfs_init()) == NULL) {
- (void) fprintf(stderr, "internal error: failed to "
- "initialize ZFS library\n");
- return (1);
- }
- zphdl = zpool_open(hdl, name);
- if (zphdl == NULL) {
- perror("can't open pool");
- libzfs_fini(hdl);
- 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 (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);
+ }
}
- pool_guid = zpool_get_prop_int(zphdl, ZPOOL_PROP_GUID, NULL);
+ rv = 0;
+ if (key != NULL || value != NULL) {
+ if (type == NULL)
+ type = "DATA_TYPE_STRING";
- config = zpool_get_config(zphdl, NULL);
- if (config == NULL) {
- perror("can't get pool config");
- zpool_close(zphdl);
- libzfs_fini(hdl);
- return (1);
+ if (delete)
+ rv = delete_pair(name, nvlist, key);
+ else if (key == NULL || strcmp(key, "command") == 0)
+ rv = lzbe_set_boot_device(name, lzbe_add, value);
+ else
+ rv = add_pair(name, nvlist, key, type, value);
+
+ if (rv == 0)
+ printf("zfs bootenv is successfully written\n");
+ else
+ printf("error: %d\n", rv);
+ } else if (!print) {
+ char *ptr;
+
+ 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, nvlist, 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: head/share/man/man5/rc.conf.5
===================================================================
--- head/share/man/man5/rc.conf.5
+++ head/share/man/man5/rc.conf.5
@@ -24,7 +24,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd July 11, 2020
+.Dd Sep 21, 2020
.Dt RC.CONF 5
.Os
.Sh NAME
@@ -4574,6 +4574,12 @@
removes empty
.Dq Li rc.conf.d
files.
+.It Va zfs_bootonce_activate
+.Pq Vt bool
+If set to
+.Dq Li YES ,
+and a boot environment marked bootonce is successfully booted,
+it will be made permanently active.
.El
.Sh FILES
.Bl -tag -width ".Pa /etc/defaults/rc.conf" -compact
Index: head/share/mk/bsd.libnames.mk
===================================================================
--- head/share/mk/bsd.libnames.mk
+++ head/share/mk/bsd.libnames.mk
@@ -169,6 +169,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
LIBZUTIL?= ${LIBDESTDIR}${LIBDIR_BASE}/libzutil.a
Index: head/share/mk/src.libnames.mk
===================================================================
--- head/share/mk/src.libnames.mk
+++ head/share/mk/src.libnames.mk
@@ -202,6 +202,7 @@
z \
zfs_core \
zfs \
+ zfsbootenv \
zpool \
zutil
@@ -385,10 +386,11 @@
_DP_uutil= avl spl
_DP_zfs= md pthread umem util uutil m avl bsdxml crypto geom nvpair \
z zfs_core zutil
+_DP_zfsbootenv= zfs nvpair
_DP_zfs_core= nvpair
_DP_zpool= md pthread z icp spl nvpair avl umem
_DP_zutil= avl tpool
-_DP_be= zfs spl nvpair
+_DP_be= zfs spl nvpair zfsbootenv
_DP_netmap=
_DP_ifconfig= m
@@ -600,6 +602,7 @@
LIBUUTILDIR= ${OBJTOP}/cddl/lib/libuutil
LIBZFSDIR= ${OBJTOP}/cddl/lib/libzfs
LIBZFS_COREDIR= ${OBJTOP}/cddl/lib/libzfs_core
+LIBZFSBOOTENVDIR= ${OBJTOP}/cddl/lib/libzfsbootenv
LIBZPOOLDIR= ${OBJTOP}/cddl/lib/libzpool
LIBZUTILDIR= ${OBJTOP}/cddl/lib/libzutil
LIBTPOOLDIR= ${OBJTOP}/cddl/lib/libtpool
Index: head/stand/common/bootstrap.h
===================================================================
--- head/stand/common/bootstrap.h
+++ head/stand/common/bootstrap.h
@@ -344,6 +344,37 @@
void dev_cleanup(void);
+/*
+ * nvstore API.
+ */
+typedef int (nvstore_getter_cb_t)(void *, const char *, void **);
+typedef int (nvstore_setter_cb_t)(void *, int, const char *,
+ const void *, size_t);
+typedef int (nvstore_setter_str_cb_t)(void *, const char *, const char *,
+ const char *);
+typedef int (nvstore_unset_cb_t)(void *, const char *);
+typedef int (nvstore_print_cb_t)(void *, void *);
+typedef int (nvstore_iterate_cb_t)(void *, int (*)(void *, void *));
+
+typedef struct nvs_callbacks {
+ nvstore_getter_cb_t *nvs_getter;
+ nvstore_setter_cb_t *nvs_setter;
+ nvstore_setter_str_cb_t *nvs_setter_str;
+ nvstore_unset_cb_t *nvs_unset;
+ nvstore_print_cb_t *nvs_print;
+ nvstore_iterate_cb_t *nvs_iterate;
+} nvs_callbacks_t;
+
+int nvstore_init(const char *, nvs_callbacks_t *, void *);
+int nvstore_fini(const char *);
+void *nvstore_get_store(const char *);
+int nvstore_print(void *);
+int nvstore_get_var(void *, const char *, void **);
+int nvstore_set_var(void *, int, const char *, void *, size_t);
+int nvstore_set_var_from_string(void *, const char *, const char *,
+ const char *);
+int nvstore_unset_var(void *, const char *);
+
#ifndef CTASSERT
#define CTASSERT(x) _Static_assert(x, "compile-time assertion failed")
#endif
Index: head/stand/common/nvstore.c
===================================================================
--- head/stand/common/nvstore.c
+++ head/stand/common/nvstore.c
@@ -0,0 +1,310 @@
+/*-
+ * 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.
+ */
+
+/*
+ * Big Theory Statement.
+ *
+ * nvstore is abstraction layer to implement data read/write to different
+ * types of non-volatile storage.
+ *
+ * User interfaces:
+ * Provide mapping via environment: setenv/unsetenv/putenv. Access via
+ * environment functions/commands is available once nvstore has
+ * attached the backend and stored textual data is mapped to environment.
+ *
+ * Provide command "nvstore" to create new data instances.
+ *
+ * API: TBD.
+ * nvstore_init(): attach new backend and create the environment mapping.
+ * nvstore_fini: detach backend and unmap the related environment.
+ *
+ * The disk based storage, such as UFS file or ZFS bootenv label area, is
+ * only accessible after root file system is set. Root file system change
+ * will switch the back end storage.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stdbool.h>
+#include <sys/queue.h>
+#include <bootstrap.h>
+#include "stand.h"
+
+typedef struct nvstore {
+ char *nvs_name;
+ void *nvs_data;
+ nvs_callbacks_t *nvs_cb;
+ STAILQ_ENTRY(nvstore) nvs_next;
+} nvstore_t;
+
+typedef STAILQ_HEAD(store_list, nvstore) nvstore_list_t;
+
+nvstore_list_t stores = STAILQ_HEAD_INITIALIZER(stores);
+
+void *
+nvstore_get_store(const char *name)
+{
+ nvstore_t *st;
+
+ st = NULL;
+
+ STAILQ_FOREACH(st, &stores, nvs_next) {
+ if (strcmp(name, st->nvs_name) == 0)
+ break;
+ }
+
+ return (st);
+}
+
+int
+nvstore_init(const char *name, nvs_callbacks_t *cb, void *data)
+{
+ nvstore_t *st;
+
+ st = nvstore_get_store(name);
+ if (st != NULL)
+ return (EEXIST);
+
+ if ((st = malloc(sizeof (*st))) == NULL)
+ return (ENOMEM);
+
+ if ((st->nvs_name = strdup(name)) == NULL) {
+ free(st);
+ return (ENOMEM);
+ }
+
+ st->nvs_data = data;
+ st->nvs_cb = cb;
+
+ STAILQ_INSERT_TAIL(&stores, st, nvs_next);
+ return (0);
+}
+
+int
+nvstore_fini(const char *name)
+{
+ nvstore_t *st;
+
+ st = nvstore_get_store(name);
+ if (st == NULL)
+ return (ENOENT);
+
+ STAILQ_REMOVE(&stores, st, nvstore, nvs_next);
+
+ free(st->nvs_name);
+ free(st->nvs_data);
+ free(st);
+ return (0);
+}
+
+int
+nvstore_print(void *ptr)
+{
+ nvstore_t *st = ptr;
+
+ return (st->nvs_cb->nvs_iterate(st->nvs_data, st->nvs_cb->nvs_print));
+}
+
+int
+nvstore_get_var(void *ptr, const char *name, void **data)
+{
+ nvstore_t *st = ptr;
+
+ return (st->nvs_cb->nvs_getter(st->nvs_data, name, data));
+}
+
+int
+nvstore_set_var(void *ptr, int type, const char *name,
+ void *data, size_t size)
+{
+ nvstore_t *st = ptr;
+
+ return (st->nvs_cb->nvs_setter(st->nvs_data, type, name, data, size));
+}
+
+int
+nvstore_set_var_from_string(void *ptr, const char *type, const char *name,
+ const char *data)
+{
+ nvstore_t *st = ptr;
+
+ return (st->nvs_cb->nvs_setter_str(st->nvs_data, type, name, data));
+}
+
+int
+nvstore_unset_var(void *ptr, const char *name)
+{
+ nvstore_t *st = ptr;
+
+ return (st->nvs_cb->nvs_unset(st->nvs_data, name));
+}
+
+COMMAND_SET(nvstore, "nvstore", "manage non-volatile data", command_nvstore);
+
+static void
+nvstore_usage(const char *me)
+{
+ printf("Usage:\t%s -l\n", me);
+ printf("\t%s store -l\n", me);
+ printf("\t%s store [-t type] key value\n", me);
+ printf("\t%s store -g key\n", me);
+ printf("\t%s store -d key\n", me);
+}
+
+/*
+ * Usage: nvstore -l # list stores
+ * nvstore store -l # list data in store
+ * nvstore store [-t type] key value
+ * nvstore store -g key # get value
+ * nvstore store -d key # delete key
+ */
+static int
+command_nvstore(int argc, char *argv[])
+{
+ int c;
+ bool list, get, delete;
+ nvstore_t *st;
+ char *me, *name, *type;
+
+ me = argv[0];
+ optind = 1;
+ optreset = 1;
+
+ list = false;
+ while ((c = getopt(argc, argv, "l")) != -1) {
+ switch (c) {
+ case 'l':
+ list = true;
+ break;
+ case '?':
+ default:
+ return (CMD_ERROR);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc == 0) {
+ if (list) {
+ if (STAILQ_EMPTY(&stores)) {
+ printf("No configured nvstores\n");
+ return (CMD_OK);
+ }
+ printf("List of configured nvstores:\n");
+ STAILQ_FOREACH(st, &stores, nvs_next) {
+ printf("\t%s\n", st->nvs_name);
+ }
+ return (CMD_OK);
+ }
+ nvstore_usage(me);
+ return (CMD_ERROR);
+ }
+
+ if (argc == 0 || (argc != 0 && list)) {
+ nvstore_usage(me);
+ return (CMD_ERROR);
+ }
+
+ st = nvstore_get_store(argv[0]);
+ if (st == NULL) {
+ nvstore_usage(me);
+ return (CMD_ERROR);
+ }
+
+ optind = 1;
+ optreset = 1;
+ name = NULL;
+ type = NULL;
+ get = delete = false;
+
+ while ((c = getopt(argc, argv, "d:g:lt:")) != -1) {
+ switch (c) {
+ case 'd':
+ if (list || get) {
+ nvstore_usage(me);
+ return (CMD_ERROR);
+ }
+ name = optarg;
+ delete = true;
+ break;
+ case 'g':
+ if (delete || list) {
+ nvstore_usage(me);
+ return (CMD_ERROR);
+ }
+ name = optarg;
+ get = true;
+ break;
+ case 'l':
+ if (delete || get) {
+ nvstore_usage(me);
+ return (CMD_ERROR);
+ }
+ list = true;
+ break;
+ case 't':
+ type = optarg;
+ break;
+ case '?':
+ default:
+ return (CMD_ERROR);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (list) {
+ (void) nvstore_print(st);
+ return (CMD_OK);
+ }
+
+ if (delete && name != NULL) {
+ (void) nvstore_unset_var(st, name);
+ return (CMD_OK);
+ }
+
+ if (get && name != NULL) {
+ char *ptr = NULL;
+
+ if (nvstore_get_var(st, name, (void **)&ptr) == 0)
+ printf("%s = %s\n", name, ptr);
+ return (CMD_OK);
+ }
+
+ if (argc == 2) {
+ c = nvstore_set_var_from_string(st, type, argv[0], argv[1]);
+ if (c != 0) {
+ printf("error: %s\n", strerror(c));
+ return (CMD_ERROR);
+ }
+ return (CMD_OK);
+ }
+
+ nvstore_usage(me);
+ return (CMD_OK);
+}
Index: head/stand/defaults/loader.conf
===================================================================
--- head/stand/defaults/loader.conf
+++ head/stand/defaults/loader.conf
@@ -16,7 +16,6 @@
kernel_options="" # Flags to be passed to the kernel
loader_conf_files="/boot/device.hints /boot/loader.conf /boot/loader.conf.local"
nextboot_conf="/boot/nextboot.conf"
-nextboot_enable="NO"
verbose_loading="NO" # Set to YES for verbose loader output
### Splash screen configuration ############################
Index: head/stand/efi/boot1/Makefile
===================================================================
--- head/stand/efi/boot1/Makefile
+++ head/stand/efi/boot1/Makefile
@@ -36,7 +36,9 @@
CFLAGS.zfs_module.c+= -I${ZFSSRC}
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}/contrib/openzfs/include
+CFLAGS.zfs_module.c+= -I${SYSDIR}/contrib/openzfs/include/os/freebsd/spl
+CFLAGS.zfs_module.c+= -I${SYSDIR}/contrib/openzfs/include/os/freebsd/zfs
CFLAGS.zfs_module.c+= -I${SYSDIR}/cddl/contrib/opensolaris/common/lz4
CFLAGS+= -DEFI_ZFS_BOOT
Index: head/stand/efi/boot1/zfs_module.c
===================================================================
--- head/stand/efi/boot1/zfs_module.c
+++ head/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: head/stand/efi/loader/Makefile
===================================================================
--- head/stand/efi/loader/Makefile
+++ head/stand/efi/loader/Makefile
@@ -27,6 +27,8 @@
CFLAGS+= -I${.CURDIR}/../loader
.if ${MK_LOADER_ZFS} != "no"
CFLAGS+= -I${ZFSSRC}
+CFLAGS+= -I${SYSDIR}/contrib/openzfs/include
+CFLAGS+= -I${SYSDIR}/contrib/openzfs/include/os/freebsd/zfs
CFLAGS+= -DEFI_ZFS_BOOT
HAVE_ZFS= yes
.endif
Index: head/stand/efi/loader/main.c
===================================================================
--- head/stand/efi/loader/main.c
+++ head/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,11 +276,14 @@
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);
+ (void) zfs_attach_nvstore(&currdev);
}
}
return (rv);
Index: head/stand/forth/support.4th
===================================================================
--- head/stand/forth/support.4th
+++ head/stand/forth/support.4th
@@ -1057,8 +1057,16 @@
;
: include_nextboot_file ( -- )
- get_nextboot_conf_file
- ['] peek_file catch if 2drop then
+ s" nextboot_enable" getenv dup -1 <> if
+ 2dup s' "YES"' compare >r
+ 2dup s' "yes"' compare >r
+ 2dup s" YES" compare >r
+ 2dup s" yes" compare r> r> r> and and and 0= to nextboot?
+ else
+ drop
+ get_nextboot_conf_file
+ ['] peek_file catch if 2drop then
+ then
nextboot? if
get_nextboot_conf_file
current_file_name_ref strref
@@ -1066,6 +1074,7 @@
process_conf_errors
['] rewrite_nextboot_file catch if 2drop then
then
+ s' "NO"' s" nextboot_enable" setenv
;
\ Module loading functions
Index: head/stand/i386/gptzfsboot/Makefile
===================================================================
--- head/stand/i386/gptzfsboot/Makefile
+++ head/stand/i386/gptzfsboot/Makefile
@@ -32,7 +32,9 @@
-I${ZFSSRC} \
-I${SYSDIR}/crypto/skein \
-I${SYSDIR}/cddl/boot/zfs \
- -I${SYSDIR}/cddl/contrib/opensolaris/uts/common \
+ -I${SYSDIR}/contrib/openzfs/include \
+ -I${SYSDIR}/contrib/openzfs/include/os/freebsd/spl \
+ -I${SYSDIR}/contrib/openzfs/include/os/freebsd/zfs \
-I${SYSDIR}/cddl/contrib/opensolaris/common/lz4 \
-I${BOOTSRC}/i386/btx/lib \
-I${BOOTSRC}/i386/boot2 \
Index: head/stand/i386/loader/Makefile
===================================================================
--- head/stand/i386/loader/Makefile
+++ head/stand/i386/loader/Makefile
@@ -37,6 +37,11 @@
LIBFIREWIRE= ${BOOTOBJ}/i386/libfirewire/libfirewire.a
.endif
+.if ${MK_LOADER_ZFS} == "yes"
+CFLAGS.main.c+= -I${SYSDIR}/contrib/openzfs/include
+CFLAGS.main.c+= -I${SYSDIR}/contrib/openzfs/include/os/freebsd/zfs
+.endif
+
.if exists(${.CURDIR}/help.i386)
HELP_FILES= ${.CURDIR}/help.i386
.endif
Index: head/stand/i386/loader/main.c
===================================================================
--- head/stand/i386/loader/main.c
+++ head/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,16 @@
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);
+ (void) zfs_attach_nvstore(&new_currdev);
+ }
+
#endif
} else if ((initial_bootdev & B_MAGICMASK) != B_DEVMAGIC) {
/* The passed-in boot device is bad */
Index: head/stand/i386/zfsboot/Makefile
===================================================================
--- head/stand/i386/zfsboot/Makefile
+++ head/stand/i386/zfsboot/Makefile
@@ -31,7 +31,9 @@
-I${ZFSSRC} \
-I${SYSDIR}/crypto/skein \
-I${SYSDIR}/cddl/boot/zfs \
- -I${SYSDIR}/cddl/contrib/opensolaris/uts/common \
+ -I${SYSDIR}/contrib/openzfs/include \
+ -I${SYSDIR}/contrib/openzfs/include/os/freebsd/spl \
+ -I${SYSDIR}/contrib/openzfs/include/os/freebsd/zfs \
-I${SYSDIR}/cddl/contrib/opensolaris/common/lz4 \
-I${BOOTSRC}/i386/boot2 \
-Wall -Waggregate-return -Wbad-function-cast -Wno-cast-align \
Index: head/stand/i386/zfsboot/zfsboot.c
===================================================================
--- head/stand/i386/zfsboot/zfsboot.c
+++ head/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: head/stand/libsa/zfs/Makefile.inc
===================================================================
--- head/stand/libsa/zfs/Makefile.inc
+++ head/stand/libsa/zfs/Makefile.inc
@@ -12,6 +12,8 @@
# Do not unroll skein loops, reduce code size
CFLAGS.skein_block.c+= -DSKEIN_LOOP=111
-CFLAGS.zfs.c+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/common/lz4
+CFLAGS+= -I${SYSDIR}/contrib/openzfs/include
+CFLAGS+= -I${SYSDIR}/contrib/openzfs/include/os/freebsd/zfs
+CFLAGS.zfs.c+= -I${SYSDIR}/cddl/contrib/opensolaris/common/lz4
CFLAGS+= -Wformat -Wall
Index: head/stand/libsa/zfs/libzfs.h
===================================================================
--- head/stand/libsa/zfs/libzfs.h
+++ head/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,24 +110,61 @@
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 *, size_t);
+int nvlist_export(nvlist_t *);
int nvlist_remove(nvlist_t *, const char *, data_type_t);
-void nvlist_print(nvlist_t *, unsigned int);
+int nvpair_type_from_name(const char *);
+nvp_header_t *nvpair_find(nvlist_t *, const char *);
+void nvpair_print(nvp_header_t *, unsigned int);
+void nvlist_print(const nvlist_t *, unsigned int);
+char *nvstring_get(nv_string_t *);
int nvlist_find(const nvlist_t *, const char *, data_type_t,
int *, void *, int *);
-int nvlist_next(nvlist_t *);
+nvp_header_t *nvlist_next_nvpair(nvlist_t *, nvp_header_t *);
+int nvlist_add_boolean_value(nvlist_t *, const char *, boolean_t);
+int nvlist_add_byte(nvlist_t *, const char *, uint8_t);
+int nvlist_add_int8(nvlist_t *, const char *, int8_t);
+int nvlist_add_uint8(nvlist_t *, const char *, uint8_t);
+int nvlist_add_int16(nvlist_t *, const char *, int16_t);
+int nvlist_add_uint16(nvlist_t *, const char *, uint16_t);
+int nvlist_add_int32(nvlist_t *, const char *, int32_t);
+int nvlist_add_uint32(nvlist_t *, const char *, uint32_t);
+int nvlist_add_int64(nvlist_t *, const char *, int64_t);
+int nvlist_add_uint64(nvlist_t *, const char *, uint64_t);
+int nvlist_add_string(nvlist_t *, const char *, const char *);
+int nvlist_add_boolean_array(nvlist_t *, const char *, boolean_t *, uint32_t);
+int nvlist_add_byte_array(nvlist_t *, const char *, uint8_t *, uint32_t);
+int nvlist_add_int8_array(nvlist_t *, const char *, int8_t *, uint32_t);
+int nvlist_add_uint8_array(nvlist_t *, const char *, uint8_t *, uint32_t);
+int nvlist_add_int16_array(nvlist_t *, const char *, int16_t *, uint32_t);
+int nvlist_add_uint16_array(nvlist_t *, const char *, uint16_t *, uint32_t);
+int nvlist_add_int32_array(nvlist_t *, const char *, int32_t *, uint32_t);
+int nvlist_add_uint32_array(nvlist_t *, const char *, uint32_t *, uint32_t);
+int nvlist_add_int64_array(nvlist_t *, const char *, int64_t *, uint32_t);
+int nvlist_add_uint64_array(nvlist_t *, const char *, uint64_t *, uint32_t);
+int nvlist_add_string_array(nvlist_t *, const char *, char * const *, uint32_t);
+int nvlist_add_nvlist(nvlist_t *, const char *, nvlist_t *);
+int nvlist_add_nvlist_array(nvlist_t *, const char *, nvlist_t **, uint32_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_probe_dev(const char *devname, uint64_t *pool_guid);
int zfs_list(const char *name);
+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_attach_nvstore(void *);
uint64_t ldi_get_size(void *);
void init_zfs_boot_options(const char *currdev);
+
int zfs_bootenv(const char *name);
+int zfs_attach_nvstore(void *);
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: head/stand/libsa/zfs/nvlist.c
===================================================================
--- head/stand/libsa/zfs/nvlist.c
+++ head/stand/libsa/zfs/nvlist.c
@@ -27,129 +27,279 @@
__FBSDID("$FreeBSD$");
#include <stand.h>
+#include <stdbool.h>
#include <sys/endian.h>
+#include <sys/stdint.h>
+#include <sys/param.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)(struct xdr *, int *);
+ int (*xdr_putint)(struct xdr *, int);
+ int (*xdr_getuint)(struct xdr *, unsigned *);
+ int (*xdr_putuint)(struct xdr *, unsigned);
+ const uint8_t *xdr_buf;
+ uint8_t *xdr_idx;
+ size_t xdr_buf_size;
} 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_size(const xdr_t *, const uint8_t *);
+static int nvlist_xdr_nvlist(xdr_t *, nvlist_t *);
+static bool nvlist_size_xdr(xdr_t *, size_t *);
+static bool nvlist_size_native(xdr_t *, size_t *);
+static bool xdr_int(xdr_t *, int *);
+static bool xdr_u_int(xdr_t *, unsigned *);
-/*
- * transform data from network to host.
- */
-xdr_t ntoh = {
- .xdr_getint = xdr_int
-};
+typedef int (*xdrproc_t)(xdr_t *, void *);
-/*
- * transform data from host to host.
- */
-xdr_t native = {
- .xdr_getint = mem_int
-};
+/* Basic primitives for XDR translation operations, getint and putint. */
+static int
+_getint(struct xdr *xdr, int *ip)
+{
+ *ip = be32dec(xdr->xdr_idx);
+ return (sizeof (int));
+}
-/*
- * transform data from host to network.
- */
-xdr_t hton = {
- .xdr_getint = xdr_int
-};
-
static int
-xdr_short(const xdr_t *xdr, const uint8_t *buf, short *ip)
+_putint(struct xdr *xdr, int i)
{
- int i, rv;
+ int *ip = (int *)xdr->xdr_idx;
- rv = xdr->xdr_getint(xdr, buf, &i);
- *ip = i;
- return (rv);
+ *ip = htobe32(i);
+ return (sizeof (int));
}
static int
-xdr_u_short(const xdr_t *xdr, const uint8_t *buf, unsigned short *ip)
+_getuint(struct xdr *xdr, unsigned *ip)
{
- unsigned u;
- int rv;
+ *ip = be32dec(xdr->xdr_idx);
+ return (sizeof (unsigned));
+}
- rv = xdr->xdr_getint(xdr, buf, &u);
- *ip = u;
+static int
+_putuint(struct xdr *xdr, unsigned i)
+{
+ unsigned *up = (unsigned *)xdr->xdr_idx;
+
+ *up = htobe32(i);
+ return (sizeof (int));
+}
+
+/*
+ * XDR data translations.
+ */
+static bool
+xdr_short(xdr_t *xdr, short *ip)
+{
+ int i;
+ bool rv;
+
+ i = *ip;
+ if ((rv = xdr_int(xdr, &i))) {
+ if (xdr->xdr_op == XDR_OP_DECODE)
+ *ip = i;
+ }
return (rv);
}
-static int
-xdr_int(const xdr_t *xdr __unused, const void *buf, int *ip)
+static bool
+xdr_u_short(xdr_t *xdr, unsigned short *ip)
{
- *ip = be32dec(buf);
- return (sizeof(int));
+ unsigned u;
+ bool rv;
+
+ u = *ip;
+ if ((rv = xdr_u_int(xdr, &u))) {
+ if (xdr->xdr_op == XDR_OP_DECODE)
+ *ip = u;
+ }
+ return (rv);
}
-static int
-xdr_u_int(const xdr_t *xdr __unused, const void *buf, unsigned *ip)
+/*
+ * translate xdr->xdr_idx, increment it by size of int.
+ */
+static bool
+xdr_int(xdr_t *xdr, int *ip)
{
- *ip = be32dec(buf);
- return (sizeof(unsigned));
+ bool rv = false;
+ int *i = (int *)xdr->xdr_idx;
+
+ if (xdr->xdr_idx + sizeof (int) > xdr->xdr_buf + xdr->xdr_buf_size)
+ return (rv);
+
+ switch (xdr->xdr_op) {
+ case XDR_OP_ENCODE:
+ /* Encode value *ip, store to buf */
+ xdr->xdr_idx += xdr->xdr_putint(xdr, *ip);
+ rv = true;
+ break;
+
+ case XDR_OP_DECODE:
+ /* Decode buf, return value to *ip */
+ xdr->xdr_idx += xdr->xdr_getint(xdr, i);
+ *ip = *i;
+ rv = true;
+ break;
+ }
+ return (rv);
}
-static int
-xdr_string(const xdr_t *xdr, const void *buf, nv_string_t *s)
+/*
+ * translate xdr->xdr_idx, increment it by size of unsigned int.
+ */
+static bool
+xdr_u_int(xdr_t *xdr, unsigned *ip)
{
- int size;
+ bool rv = false;
+ unsigned *u = (unsigned *)xdr->xdr_idx;
- size = xdr->xdr_getint(xdr, buf, &s->nv_size);
- size = NV_ALIGN4(size + s->nv_size);
- return (size);
+ if (xdr->xdr_idx + sizeof (unsigned) > xdr->xdr_buf + xdr->xdr_buf_size)
+ return (rv);
+
+ switch (xdr->xdr_op) {
+ case XDR_OP_ENCODE:
+ /* Encode value *ip, store to buf */
+ xdr->xdr_idx += xdr->xdr_putuint(xdr, *ip);
+ rv = true;
+ break;
+
+ case XDR_OP_DECODE:
+ /* Decode buf, return value to *ip */
+ xdr->xdr_idx += xdr->xdr_getuint(xdr, u);
+ *ip = *u;
+ rv = true;
+ break;
+ }
+ return (rv);
}
-static int
-xdr_int64(const xdr_t *xdr, const uint8_t *buf, int64_t *lp)
+static bool
+xdr_int64(xdr_t *xdr, int64_t *lp)
{
- int hi, rv;
+ int hi;
unsigned lo;
+ bool rv = false;
- rv = xdr->xdr_getint(xdr, buf, &hi);
- rv += xdr->xdr_getint(xdr, buf + rv, &lo);
- *lp = (((int64_t)hi) << 32) | lo;
+ if (xdr->xdr_idx + sizeof (int64_t) > xdr->xdr_buf + xdr->xdr_buf_size)
+ return (rv);
+
+ switch (xdr->xdr_op) {
+ case XDR_OP_ENCODE:
+ /* Encode value *lp, store to buf */
+ hi = *lp >> 32;
+ lo = *lp & UINT32_MAX;
+ xdr->xdr_idx += xdr->xdr_putint(xdr, hi);
+ xdr->xdr_idx += xdr->xdr_putint(xdr, lo);
+ rv = true;
+ break;
+
+ case XDR_OP_DECODE:
+ /* Decode buf, return value to *ip */
+ xdr->xdr_idx += xdr->xdr_getint(xdr, &hi);
+ xdr->xdr_idx += xdr->xdr_getuint(xdr, &lo);
+ *lp = (((int64_t)hi) << 32) | lo;
+ rv = true;
+ }
return (rv);
}
-static int
-xdr_uint64(const xdr_t *xdr, const uint8_t *buf, uint64_t *lp)
+static bool
+xdr_uint64(xdr_t *xdr, uint64_t *lp)
{
unsigned hi, lo;
- int rv;
+ bool rv = false;
- rv = xdr->xdr_getint(xdr, buf, &hi);
- rv += xdr->xdr_getint(xdr, buf + rv, &lo);
- *lp = (((int64_t)hi) << 32) | lo;
+ if (xdr->xdr_idx + sizeof (uint64_t) > xdr->xdr_buf + xdr->xdr_buf_size)
+ return (rv);
+
+ switch (xdr->xdr_op) {
+ case XDR_OP_ENCODE:
+ /* Encode value *ip, store to buf */
+ hi = *lp >> 32;
+ lo = *lp & UINT32_MAX;
+ xdr->xdr_idx += xdr->xdr_putint(xdr, hi);
+ xdr->xdr_idx += xdr->xdr_putint(xdr, lo);
+ rv = true;
+ break;
+
+ case XDR_OP_DECODE:
+ /* Decode buf, return value to *ip */
+ xdr->xdr_idx += xdr->xdr_getuint(xdr, &hi);
+ xdr->xdr_idx += xdr->xdr_getuint(xdr, &lo);
+ *lp = (((uint64_t)hi) << 32) | lo;
+ rv = true;
+ }
return (rv);
}
-static int
-xdr_char(const xdr_t *xdr, const uint8_t *buf, char *cp)
+static bool
+xdr_char(xdr_t *xdr, char *cp)
{
- int i, rv;
+ int i;
+ bool rv = false;
- rv = xdr->xdr_getint(xdr, buf, &i);
- *cp = i;
+ i = *cp;
+ if ((rv = xdr_int(xdr, &i))) {
+ if (xdr->xdr_op == XDR_OP_DECODE)
+ *cp = i;
+ }
return (rv);
}
-/*
- * read native data.
- */
-static int
-mem_int(const xdr_t *xdr, const void *buf, int *i)
+static bool
+xdr_string(xdr_t *xdr, nv_string_t *s)
{
- *i = *(int *)buf;
- return (sizeof(int));
+ int size = 0;
+ bool rv = false;
+
+ switch (xdr->xdr_op) {
+ case XDR_OP_ENCODE:
+ size = s->nv_size;
+ if (xdr->xdr_idx + sizeof (unsigned) + NV_ALIGN4(size) >
+ xdr->xdr_buf + xdr->xdr_buf_size)
+ break;
+ xdr->xdr_idx += xdr->xdr_putuint(xdr, s->nv_size);
+ xdr->xdr_idx += NV_ALIGN4(size);
+ rv = true;
+ break;
+
+ case XDR_OP_DECODE:
+ if (xdr->xdr_idx + sizeof (unsigned) >
+ xdr->xdr_buf + xdr->xdr_buf_size)
+ break;
+ size = xdr->xdr_getuint(xdr, &s->nv_size);
+ size = NV_ALIGN4(size + s->nv_size);
+ if (xdr->xdr_idx + size > xdr->xdr_buf + xdr->xdr_buf_size)
+ break;
+ xdr->xdr_idx += size;
+ rv = true;
+ break;
+ }
+ return (rv);
}
+static bool
+xdr_array(xdr_t *xdr, const unsigned nelem, const xdrproc_t elproc)
+{
+ bool rv = true;
+
+ for (unsigned i = 0; i < nelem; i++) {
+ if (!elproc(xdr, xdr->xdr_idx))
+ return (false);
+ }
+ return (rv);
+}
+
+/*
+ * nvlist management functions.
+ */
void
nvlist_destroy(nvlist_t *nvl)
{
@@ -184,14 +334,14 @@
nvlist_t *nvl;
nvs_data_t *nvs;
- nvl = calloc(1, sizeof(*nvl));
+ nvl = calloc(1, sizeof (*nvl));
if (nvl == NULL)
return (nvl);
nvl->nv_header.nvh_encoding = NV_ENCODE_XDR;
nvl->nv_header.nvh_endian = _BYTE_ORDER == _LITTLE_ENDIAN;
- nvl->nv_asize = nvl->nv_size = sizeof(*nvs);
+ nvl->nv_asize = nvl->nv_size = sizeof (*nvs);
nvs = calloc(1, nvl->nv_asize);
if (nvs == NULL) {
free(nvl);
@@ -205,32 +355,59 @@
return (nvl);
}
-static void
-nvlist_nvp_decode(const xdr_t *xdr, nvlist_t *nvl, nvp_header_t *nvph)
+static bool
+nvlist_xdr_nvp(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 nv_xdr;
- 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;
+ nv_string = (nv_string_t *)xdr->xdr_idx;
+ if (!xdr_string(xdr, nv_string)) {
+ return (false);
+ }
+ nvp_data = (nv_pair_data_t *)xdr->xdr_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;
+ if (!xdr_u_int(xdr, &type) || !xdr_u_int(xdr, &nelem))
+ return (false);
- switch (nvp_data->nv_type) {
+ switch (type) {
case DATA_TYPE_NVLIST:
case DATA_TYPE_NVLIST_ARRAY:
bzero(&nvlist, sizeof (nvlist));
- nvlist.nv_data = &nvp_data->nv_data[0];
+ nvlist.nv_data = xdr->xdr_idx;
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);
- nvl->nv_idx = nvlist.nv_idx;
- nvlist.nv_data = nvlist.nv_idx;
+
+ /* Set up xdr for this nvlist. */
+ nv_xdr = *xdr;
+ nv_xdr.xdr_buf = nvlist.nv_data;
+ nv_xdr.xdr_idx = nvlist.nv_data;
+ nv_xdr.xdr_buf_size =
+ nvl->nv_data + nvl->nv_size - nvlist.nv_data;
+
+ for (unsigned i = 0; i < nelem; i++) {
+ if (xdr->xdr_op == XDR_OP_ENCODE) {
+ if (!nvlist_size_native(&nv_xdr,
+ &nvlist.nv_size))
+ return (false);
+ } else {
+ if (!nvlist_size_xdr(&nv_xdr,
+ &nvlist.nv_size))
+ return (false);
+ }
+ if (nvlist_xdr_nvlist(xdr, &nvlist) != 0)
+ return (false);
+
+ nvlist.nv_data = nv_xdr.xdr_idx;
+ nvlist.nv_idx = nv_xdr.xdr_idx;
+
+ nv_xdr.xdr_buf = nv_xdr.xdr_idx;
+ nv_xdr.xdr_buf_size =
+ nvl->nv_data + nvl->nv_size - nvlist.nv_data;
}
break;
@@ -240,128 +417,334 @@
case DATA_TYPE_BYTE:
case DATA_TYPE_INT8:
case DATA_TYPE_UINT8:
- nvl->nv_idx += xdr_char(xdr, &nvp_data->nv_data[0],
- (char *)&nvp_data->nv_data[0]);
+ if (!xdr_char(xdr, (char *)&nvp_data->nv_data[0]))
+ return (false);
break;
case DATA_TYPE_INT16:
- nvl->nv_idx += xdr_short(xdr, &nvp_data->nv_data[0],
- (short *)&nvp_data->nv_data[0]);
+ if (!xdr_short(xdr, (short *)&nvp_data->nv_data[0]))
+ return (false);
break;
case DATA_TYPE_UINT16:
- nvl->nv_idx += xdr_u_short(xdr, &nvp_data->nv_data[0],
- (unsigned short *)&nvp_data->nv_data[0]);
+ if (!xdr_u_short(xdr, (unsigned short *)&nvp_data->nv_data[0]))
+ return (false);
break;
case DATA_TYPE_BOOLEAN_VALUE:
case DATA_TYPE_INT32:
- nvl->nv_idx += xdr_int(xdr, &nvp_data->nv_data[0],
- (int *)&nvp_data->nv_data[0]);
+ if (!xdr_int(xdr, (int *)&nvp_data->nv_data[0]))
+ return (false);
break;
case DATA_TYPE_UINT32:
- nvl->nv_idx += xdr_u_int(xdr, &nvp_data->nv_data[0],
- (unsigned *)&nvp_data->nv_data[0]);
+ if (!xdr_u_int(xdr, (unsigned *)&nvp_data->nv_data[0]))
+ return (false);
break;
+ case DATA_TYPE_HRTIME:
case DATA_TYPE_INT64:
- nvl->nv_idx += xdr_int64(xdr, &nvp_data->nv_data[0],
- (int64_t *)&nvp_data->nv_data[0]);
+ if (!xdr_int64(xdr, (int64_t *)&nvp_data->nv_data[0]))
+ return (false);
break;
case DATA_TYPE_UINT64:
- nvl->nv_idx += xdr_uint64(xdr, &nvp_data->nv_data[0],
- (uint64_t *)&nvp_data->nv_data[0]);
+ if (!xdr_uint64(xdr, (uint64_t *)&nvp_data->nv_data[0]))
+ return (false);
break;
+ case DATA_TYPE_BYTE_ARRAY:
case DATA_TYPE_STRING:
nv_string = (nv_string_t *)&nvp_data->nv_data[0];
- nvl->nv_idx += xdr_string(xdr, &nvp_data->nv_data[0],
- nv_string);
+ if (!xdr_string(xdr, nv_string))
+ return (false);
+ break;
+ case DATA_TYPE_STRING_ARRAY:
+ nv_string = (nv_string_t *)&nvp_data->nv_data[0];
+ for (unsigned i = 0; i < nelem; i++) {
+ if (!xdr_string(xdr, nv_string))
+ return (false);
+ nv_string = (nv_string_t *)xdr->xdr_idx;
+ }
break;
+
+ case DATA_TYPE_INT8_ARRAY:
+ case DATA_TYPE_UINT8_ARRAY:
+ case DATA_TYPE_INT16_ARRAY:
+ case DATA_TYPE_UINT16_ARRAY:
+ case DATA_TYPE_BOOLEAN_ARRAY:
+ case DATA_TYPE_INT32_ARRAY:
+ case DATA_TYPE_UINT32_ARRAY:
+ if (!xdr_array(xdr, nelem, (xdrproc_t)xdr_u_int))
+ return (false);
+ break;
+
+ case DATA_TYPE_INT64_ARRAY:
+ case DATA_TYPE_UINT64_ARRAY:
+ if (!xdr_array(xdr, nelem, (xdrproc_t)xdr_uint64))
+ return (false);
+ break;
}
+ return (true);
}
-static void
-nvlist_decode_nvlist(const xdr_t *xdr, nvlist_t *nvl)
+static int
+nvlist_xdr_nvlist(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 *)xdr->xdr_idx;
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);
+ if (!xdr_u_int(xdr, &nvs->nvl_version))
+ return (EINVAL);
+ if (!xdr_u_int(xdr, &nvs->nvl_nvflag))
+ return (EINVAL);
- nvph = (nvp_header_t *)(nvl->nv_idx);
- nvl->nv_idx += xdr->xdr_getint(xdr, &nvph->encoded_size,
- &nvph->encoded_size);
- nvl->nv_idx += xdr->xdr_getint(xdr, &nvph->decoded_size,
- &nvph->decoded_size);
+ encoded_size = nvph->encoded_size;
+ decoded_size = nvph->decoded_size;
+
+ if (xdr->xdr_op == XDR_OP_ENCODE) {
+ if (!xdr_u_int(xdr, &nvph->encoded_size))
+ return (EINVAL);
+ if (!xdr_u_int(xdr, &nvph->decoded_size))
+ return (EINVAL);
+ } else {
+ xdr->xdr_idx += 2 * sizeof (unsigned);
}
+
+ rv = 0;
+ while (encoded_size && decoded_size) {
+ if (!nvlist_xdr_nvp(xdr, nvl))
+ return (EINVAL);
+
+ nvph = (nvp_header_t *)(xdr->xdr_idx);
+ encoded_size = nvph->encoded_size;
+ decoded_size = nvph->decoded_size;
+ if (xdr->xdr_op == XDR_OP_ENCODE) {
+ if (!xdr_u_int(xdr, &nvph->encoded_size))
+ return (EINVAL);
+ if (!xdr_u_int(xdr, &nvph->decoded_size))
+ return (EINVAL);
+ } else {
+ xdr->xdr_idx += 2 * sizeof (unsigned);
+ }
+ }
+ return (rv);
}
-static int
-nvlist_size(const xdr_t *xdr, const uint8_t *stream)
+/*
+ * Calculate nvlist size, translating encoded_size and decoded_size.
+ */
+static bool
+nvlist_size_xdr(xdr_t *xdr, size_t *size)
{
- const uint8_t *p, *pair;
+ uint8_t *pair;
unsigned encoded_size, decoded_size;
- p = stream;
- p += 2 * sizeof(unsigned);
+ xdr->xdr_idx += 2 * sizeof (unsigned);
- pair = p;
- p += xdr->xdr_getint(xdr, p, &encoded_size);
- p += xdr->xdr_getint(xdr, p, &decoded_size);
+ pair = xdr->xdr_idx;
+ if (!xdr_u_int(xdr, &encoded_size) || !xdr_u_int(xdr, &decoded_size))
+ return (false);
+
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);
+ xdr->xdr_idx = pair + encoded_size;
+ pair = xdr->xdr_idx;
+ if (!xdr_u_int(xdr, &encoded_size) ||
+ !xdr_u_int(xdr, &decoded_size))
+ return (false);
}
- return (p - stream);
+ *size = xdr->xdr_idx - xdr->xdr_buf;
+
+ return (true);
}
+nvp_header_t *
+nvlist_next_nvpair(nvlist_t *nvl, nvp_header_t *nvh)
+{
+ uint8_t *pair;
+ unsigned encoded_size, decoded_size;
+ xdr_t xdr;
+
+ if (nvl == NULL)
+ return (NULL);
+
+ xdr.xdr_buf = nvl->nv_data;
+ xdr.xdr_idx = nvl->nv_data;
+ xdr.xdr_buf_size = nvl->nv_size;
+
+ xdr.xdr_idx += 2 * sizeof (unsigned);
+
+ /* Skip tp current pair */
+ if (nvh != NULL) {
+ xdr.xdr_idx = (uint8_t *)nvh;
+ }
+
+ pair = xdr.xdr_idx;
+ if (xdr.xdr_idx > xdr.xdr_buf + xdr.xdr_buf_size)
+ return (NULL);
+
+ encoded_size = *(unsigned *)xdr.xdr_idx;
+ xdr.xdr_idx += sizeof (unsigned);
+ if (xdr.xdr_idx > xdr.xdr_buf + xdr.xdr_buf_size)
+ return (NULL);
+
+ decoded_size = *(unsigned *)xdr.xdr_idx;
+ xdr.xdr_idx += sizeof (unsigned);
+ if (xdr.xdr_idx > xdr.xdr_buf + xdr.xdr_buf_size)
+ return (NULL);
+
+ while (encoded_size && decoded_size) {
+ if (nvh == NULL)
+ return ((nvp_header_t *)pair);
+
+ xdr.xdr_idx = pair + encoded_size;
+ nvh = (nvp_header_t *)xdr.xdr_idx;
+
+ if (xdr.xdr_idx > xdr.xdr_buf + xdr.xdr_buf_size)
+ return (NULL);
+
+ encoded_size = *(unsigned *)xdr.xdr_idx;
+ xdr.xdr_idx += sizeof (unsigned);
+ if (xdr.xdr_idx > xdr.xdr_buf + xdr.xdr_buf_size)
+ return (NULL);
+ decoded_size = *(unsigned *)xdr.xdr_idx;
+ xdr.xdr_idx += sizeof (unsigned);
+ if (xdr.xdr_idx > xdr.xdr_buf + xdr.xdr_buf_size)
+ return (NULL);
+
+ if (encoded_size != 0 && decoded_size != 0) {
+ return (nvh);
+ }
+ }
+ return (NULL);
+}
+
/*
+ * Calculate nvlist size by walking in memory data.
+ */
+static bool
+nvlist_size_native(xdr_t *xdr, size_t *size)
+{
+ uint8_t *pair;
+ unsigned encoded_size, decoded_size;
+
+ xdr->xdr_idx += 2 * sizeof (unsigned);
+
+ pair = xdr->xdr_idx;
+ if (xdr->xdr_idx > xdr->xdr_buf + xdr->xdr_buf_size)
+ return (false);
+
+ encoded_size = *(unsigned *)xdr->xdr_idx;
+ xdr->xdr_idx += sizeof (unsigned);
+ if (xdr->xdr_idx > xdr->xdr_buf + xdr->xdr_buf_size)
+ return (false);
+ decoded_size = *(unsigned *)xdr->xdr_idx;
+ xdr->xdr_idx += sizeof (unsigned);
+ while (encoded_size && decoded_size) {
+ xdr->xdr_idx = pair + encoded_size;
+ pair = xdr->xdr_idx;
+ if (xdr->xdr_idx > xdr->xdr_buf + xdr->xdr_buf_size)
+ return (false);
+ encoded_size = *(unsigned *)xdr->xdr_idx;
+ xdr->xdr_idx += sizeof (unsigned);
+ if (xdr->xdr_idx > xdr->xdr_buf + xdr->xdr_buf_size)
+ return (false);
+ decoded_size = *(unsigned *)xdr->xdr_idx;
+ xdr->xdr_idx += sizeof (unsigned);
+ }
+ *size = xdr->xdr_idx - xdr->xdr_buf;
+
+ return (true);
+}
+
+/*
+ * 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,
+ .xdr_buf = nvl->nv_data,
+ .xdr_idx = nvl->nv_data,
+ .xdr_buf_size = nvl->nv_size
+ };
+
+ 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, size_t size)
{
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));
+ 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];
+
+ xdr.xdr_buf = xdr.xdr_idx = (uint8_t *)stream + 4;
+ xdr.xdr_buf_size = size - 4;
+
+ if (!nvlist_size_xdr(&xdr, &nvl->nv_asize)) {
+ free(nvl);
+ return (NULL);
+ }
+ nvl->nv_size = nvl->nv_asize;
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;
+ xdr.xdr_buf = xdr.xdr_idx = nvl->nv_data;
+ xdr.xdr_buf_size = nvl->nv_asize;
+
+ if (nvlist_xdr_nvlist(&xdr, nvl) != 0) {
+ free(nvl->nv_data);
+ free(nvl);
+ nvl = NULL;
+ }
+
return (nvl);
}
@@ -377,30 +760,37 @@
nv_string_t *nvp_name;
nv_pair_data_t *nvp_data;
size_t size;
+ xdr_t xdr;
if (nvl == NULL || nvl->nv_data == NULL || name == NULL)
return (EINVAL);
- head = nvl->nv_data;
- data = (nvs_data_t *)head;
+ /* Make sure the nvlist size is set correct */
+ xdr.xdr_idx = nvl->nv_data;
+ xdr.xdr_buf = xdr.xdr_idx;
+ xdr.xdr_buf_size = nvl->nv_size;
+ if (!nvlist_size_native(&xdr, &nvl->nv_size))
+ return (EINVAL);
+
+ data = (nvs_data_t *)nvl->nv_data;
nvp = &data->nvl_pair; /* first pair in nvlist */
head = (uint8_t *)nvp;
while (nvp->encoded_size != 0 && nvp->decoded_size != 0) {
- nvp_name = (nv_string_t *)(head + sizeof(*nvp));
+ nvp_name = (nv_string_t *)(nvp + 1);
- nvp_data = (nv_pair_data_t *)
- NV_ALIGN4((uintptr_t)&nvp_name->nv_data[0] +
- nvp_name->nv_size);
+ nvp_data = (nv_pair_data_t *)(&nvp_name->nv_data[0] +
+ NV_ALIGN4(nvp_name->nv_size));
- if (memcmp(nvp_name->nv_data, name, nvp_name->nv_size) == 0 &&
- nvp_data->nv_type == type) {
+ if (strlen(name) == nvp_name->nv_size &&
+ memcmp(nvp_name->nv_data, name, nvp_name->nv_size) == 0 &&
+ (nvp_data->nv_type == type || type == DATA_TYPE_UNKNOWN)) {
/*
* set tail to point to next nvpair and size
* is the length of the tail.
*/
tail = head + nvp->encoded_size;
- size = nvl->nv_data + nvl->nv_size - tail;
+ size = nvl->nv_size - (tail - nvl->nv_data);
/* adjust the size of the nvlist. */
nvl->nv_size -= nvp->encoded_size;
@@ -414,6 +804,51 @@
return (ENOENT);
}
+static int
+clone_nvlist(const nvlist_t *nvl, const uint8_t *ptr, unsigned size,
+ nvlist_t **nvlist)
+{
+ nvlist_t *nv;
+
+ nv = calloc(1, sizeof (*nv));
+ if (nv == NULL)
+ return (ENOMEM);
+
+ nv->nv_header = nvl->nv_header;
+ nv->nv_asize = size;
+ nv->nv_size = size;
+ nv->nv_data = malloc(nv->nv_asize);
+ if (nv->nv_data == NULL) {
+ free(nv);
+ return (ENOMEM);
+ }
+
+ bcopy(ptr, nv->nv_data, nv->nv_asize);
+ *nvlist = nv;
+ return (0);
+}
+
+/*
+ * Return the next nvlist in an nvlist array.
+ */
+static uint8_t *
+nvlist_next(const uint8_t *ptr)
+{
+ nvs_data_t *data;
+ nvp_header_t *nvp;
+
+ data = (nvs_data_t *)ptr;
+ nvp = &data->nvl_pair; /* first pair in nvlist */
+
+ while (nvp->encoded_size != 0 && nvp->decoded_size != 0) {
+ nvp = (nvp_header_t *)((uint8_t *)nvp + nvp->encoded_size);
+ }
+ return ((uint8_t *)nvp + sizeof (*nvp));
+}
+
+/*
+ * Note: nvlist and nvlist array must be freed by caller.
+ */
int
nvlist_find(const nvlist_t *nvl, const char *name, data_type_t type,
int *elementsp, void *valuep, int *sizep)
@@ -422,7 +857,9 @@
nvp_header_t *nvp;
nv_string_t *nvp_name;
nv_pair_data_t *nvp_data;
- nvlist_t *nvlist;
+ nvlist_t **nvlist, *nv;
+ uint8_t *ptr;
+ int rv;
if (nvl == NULL || nvl->nv_data == NULL || name == NULL)
return (EINVAL);
@@ -431,20 +868,24 @@
nvp = &data->nvl_pair; /* first pair in nvlist */
while (nvp->encoded_size != 0 && nvp->decoded_size != 0) {
- nvp_name = (nv_string_t *)((uint8_t *)nvp + sizeof(*nvp));
+ nvp_name = (nv_string_t *)((uint8_t *)nvp + sizeof (*nvp));
+ if (nvl->nv_data + nvl->nv_size <
+ nvp_name->nv_data + nvp_name->nv_size)
+ return (EIO);
nvp_data = (nv_pair_data_t *)
NV_ALIGN4((uintptr_t)&nvp_name->nv_data[0] +
nvp_name->nv_size);
- if (memcmp(nvp_name->nv_data, name, nvp_name->nv_size) == 0 &&
- nvp_data->nv_type == type) {
+ if (strlen(name) == nvp_name->nv_size &&
+ memcmp(nvp_name->nv_data, name, nvp_name->nv_size) == 0 &&
+ (nvp_data->nv_type == type || type == DATA_TYPE_UNKNOWN)) {
if (elementsp != NULL)
*elementsp = nvp_data->nv_nelem;
switch (nvp_data->nv_type) {
case DATA_TYPE_UINT64:
- *(uint64_t *)valuep =
- *(uint64_t *)nvp_data->nv_data;
+ bcopy(nvp_data->nv_data, valuep,
+ sizeof (uint64_t));
return (0);
case DATA_TYPE_STRING:
nvp_name = (nv_string_t *)nvp_data->nv_data;
@@ -455,146 +896,673 @@
&nvp_name->nv_data[0];
return (0);
case DATA_TYPE_NVLIST:
+ ptr = &nvp_data->nv_data[0];
+ rv = clone_nvlist(nvl, ptr,
+ nvlist_next(ptr) - ptr, &nv);
+ if (rv == 0) {
+ *(nvlist_t **)valuep = nv;
+ }
+ return (rv);
+
case DATA_TYPE_NVLIST_ARRAY:
- nvlist = malloc(sizeof(*nvlist));
- if (nvlist != NULL) {
- nvlist->nv_header = nvl->nv_header;
- nvlist->nv_asize = 0;
- nvlist->nv_size = 0;
- nvlist->nv_idx = NULL;
- nvlist->nv_data = &nvp_data->nv_data[0];
- *(nvlist_t **)valuep = nvlist;
- return (0);
+ nvlist = calloc(nvp_data->nv_nelem,
+ sizeof (nvlist_t *));
+ if (nvlist == NULL)
+ return (ENOMEM);
+ ptr = &nvp_data->nv_data[0];
+ rv = 0;
+ for (unsigned i = 0; i < nvp_data->nv_nelem;
+ i++) {
+ rv = clone_nvlist(nvl, ptr,
+ nvlist_next(ptr) - ptr, &nvlist[i]);
+ if (rv != 0)
+ goto error;
+ ptr = nvlist_next(ptr);
}
- return (ENOMEM);
+ *(nvlist_t ***)valuep = nvlist;
+ return (rv);
}
return (EIO);
}
/* Not our pair, skip to next. */
nvp = (nvp_header_t *)((uint8_t *)nvp + nvp->encoded_size);
+ if (nvl->nv_data + nvl->nv_size < (uint8_t *)nvp)
+ return (EIO);
}
return (ENOENT);
+error:
+ for (unsigned i = 0; i < nvp_data->nv_nelem; i++) {
+ free(nvlist[i]->nv_data);
+ free(nvlist[i]);
+ }
+ free(nvlist);
+ return (rv);
}
-/*
- * Return the next nvlist in an nvlist array.
- */
-int
-nvlist_next(nvlist_t *nvl)
+static int
+get_value_size(data_type_t type, const void *data, uint32_t nelem)
{
- nvs_data_t *data;
- nvp_header_t *nvp;
+ uint64_t value_sz = 0;
- if (nvl == NULL || nvl->nv_data == NULL || nvl->nv_asize != 0)
+ switch (type) {
+ case DATA_TYPE_BOOLEAN:
+ value_sz = 0;
+ break;
+ case DATA_TYPE_BOOLEAN_VALUE:
+ case DATA_TYPE_BYTE:
+ case DATA_TYPE_INT8:
+ case DATA_TYPE_UINT8:
+ case DATA_TYPE_INT16:
+ case DATA_TYPE_UINT16:
+ case DATA_TYPE_INT32:
+ case DATA_TYPE_UINT32:
+ /* Our smallest data unit is 32-bit */
+ value_sz = sizeof (uint32_t);
+ break;
+ case DATA_TYPE_HRTIME:
+ case DATA_TYPE_INT64:
+ value_sz = sizeof (int64_t);
+ break;
+ case DATA_TYPE_UINT64:
+ value_sz = sizeof (uint64_t);
+ break;
+ case DATA_TYPE_STRING:
+ if (data == NULL)
+ value_sz = 0;
+ else
+ value_sz = strlen(data) + 1;
+ break;
+ case DATA_TYPE_BYTE_ARRAY:
+ value_sz = nelem * sizeof (uint8_t);
+ break;
+ case DATA_TYPE_BOOLEAN_ARRAY:
+ case DATA_TYPE_INT8_ARRAY:
+ case DATA_TYPE_UINT8_ARRAY:
+ case DATA_TYPE_INT16_ARRAY:
+ case DATA_TYPE_UINT16_ARRAY:
+ case DATA_TYPE_INT32_ARRAY:
+ case DATA_TYPE_UINT32_ARRAY:
+ value_sz = (uint64_t)nelem * sizeof (uint32_t);
+ break;
+ case DATA_TYPE_INT64_ARRAY:
+ value_sz = (uint64_t)nelem * sizeof (int64_t);
+ break;
+ case DATA_TYPE_UINT64_ARRAY:
+ value_sz = (uint64_t)nelem * sizeof (uint64_t);
+ break;
+ case DATA_TYPE_STRING_ARRAY:
+ value_sz = (uint64_t)nelem * sizeof (uint64_t);
+
+ if (data != NULL) {
+ char *const *strs = data;
+ uint32_t i;
+
+ for (i = 0; i < nelem; i++) {
+ if (strs[i] == NULL)
+ return (-1);
+ value_sz += strlen(strs[i]) + 1;
+ }
+ }
+ break;
+ case DATA_TYPE_NVLIST:
+ /*
+ * The decoded size of nvlist is constant.
+ */
+ value_sz = NV_ALIGN(6 * 4); /* sizeof nvlist_t */
+ break;
+ case DATA_TYPE_NVLIST_ARRAY:
+ value_sz = (uint64_t)nelem * sizeof (uint64_t) +
+ (uint64_t)nelem * NV_ALIGN(6 * 4); /* sizeof nvlist_t */
+ break;
+ default:
+ return (-1);
+ }
+
+ return (value_sz > INT32_MAX ? -1 : (int)value_sz);
+}
+
+static int
+get_nvp_data_size(data_type_t type, const void *data, uint32_t nelem)
+{
+ uint64_t value_sz = 0;
+ xdr_t xdr;
+ size_t size;
+
+ switch (type) {
+ case DATA_TYPE_BOOLEAN:
+ value_sz = 0;
+ break;
+ case DATA_TYPE_BOOLEAN_VALUE:
+ case DATA_TYPE_BYTE:
+ case DATA_TYPE_INT8:
+ case DATA_TYPE_UINT8:
+ case DATA_TYPE_INT16:
+ case DATA_TYPE_UINT16:
+ case DATA_TYPE_INT32:
+ case DATA_TYPE_UINT32:
+ /* Our smallest data unit is 32-bit */
+ value_sz = sizeof (uint32_t);
+ break;
+ case DATA_TYPE_HRTIME:
+ case DATA_TYPE_INT64:
+ case DATA_TYPE_UINT64:
+ value_sz = sizeof (uint64_t);
+ break;
+ case DATA_TYPE_STRING:
+ value_sz = 4 + NV_ALIGN4(strlen(data));
+ break;
+ case DATA_TYPE_BYTE_ARRAY:
+ value_sz = NV_ALIGN4(nelem);
+ break;
+ case DATA_TYPE_BOOLEAN_ARRAY:
+ case DATA_TYPE_INT8_ARRAY:
+ case DATA_TYPE_UINT8_ARRAY:
+ case DATA_TYPE_INT16_ARRAY:
+ case DATA_TYPE_UINT16_ARRAY:
+ case DATA_TYPE_INT32_ARRAY:
+ case DATA_TYPE_UINT32_ARRAY:
+ value_sz = 4 + (uint64_t)nelem * sizeof (uint32_t);
+ break;
+ case DATA_TYPE_INT64_ARRAY:
+ case DATA_TYPE_UINT64_ARRAY:
+ value_sz = 4 + (uint64_t)nelem * sizeof (uint64_t);
+ break;
+ case DATA_TYPE_STRING_ARRAY:
+ if (data != NULL) {
+ char *const *strs = data;
+ uint32_t i;
+
+ for (i = 0; i < nelem; i++) {
+ value_sz += 4 + NV_ALIGN4(strlen(strs[i]));
+ }
+ }
+ break;
+ case DATA_TYPE_NVLIST:
+ xdr.xdr_idx = ((nvlist_t *)data)->nv_data;
+ xdr.xdr_buf = xdr.xdr_idx;
+ xdr.xdr_buf_size = ((nvlist_t *)data)->nv_size;
+
+ if (!nvlist_size_native(&xdr, &size))
+ return (-1);
+
+ value_sz = size;
+ break;
+ case DATA_TYPE_NVLIST_ARRAY:
+ value_sz = 0;
+ for (uint32_t i = 0; i < nelem; i++) {
+ xdr.xdr_idx = ((nvlist_t **)data)[i]->nv_data;
+ xdr.xdr_buf = xdr.xdr_idx;
+ xdr.xdr_buf_size = ((nvlist_t **)data)[i]->nv_size;
+
+ if (!nvlist_size_native(&xdr, &size))
+ return (-1);
+ value_sz += size;
+ }
+ break;
+ default:
+ return (-1);
+ }
+
+ return (value_sz > INT32_MAX ? -1 : (int)value_sz);
+}
+
+#define NVPE_SIZE(name_len, data_len) \
+ (4 + 4 + 4 + NV_ALIGN4(name_len) + 4 + 4 + data_len)
+#define NVP_SIZE(name_len, data_len) \
+ (NV_ALIGN((4 * 4) + (name_len)) + NV_ALIGN(data_len))
+
+static int
+nvlist_add_common(nvlist_t *nvl, const char *name, data_type_t type,
+ uint32_t nelem, const void *data)
+{
+ nvs_data_t *nvs;
+ nvp_header_t head, *hp;
+ uint8_t *ptr;
+ size_t namelen;
+ int decoded_size, encoded_size;
+ xdr_t xdr;
+
+ nvs = (nvs_data_t *)nvl->nv_data;
+ if (nvs->nvl_nvflag & NV_UNIQUE_NAME)
+ (void) nvlist_remove(nvl, name, type);
+
+ xdr.xdr_buf = nvl->nv_data;
+ xdr.xdr_idx = nvl->nv_data;
+ xdr.xdr_buf_size = nvl->nv_size;
+ if (!nvlist_size_native(&xdr, &nvl->nv_size))
return (EINVAL);
- data = (nvs_data_t *)nvl->nv_data;
- nvp = &data->nvl_pair; /* first pair in nvlist */
+ namelen = strlen(name);
+ if ((decoded_size = get_value_size(type, data, nelem)) < 0)
+ return (EINVAL);
+ if ((encoded_size = get_nvp_data_size(type, data, nelem)) < 0)
+ return (EINVAL);
- while (nvp->encoded_size != 0 && nvp->decoded_size != 0) {
- nvp = (nvp_header_t *)((uint8_t *)nvp + nvp->encoded_size);
+ /*
+ * The encoded size is calculated as:
+ * encode_size (4) + decode_size (4) +
+ * name string size (4 + NV_ALIGN4(namelen) +
+ * data type (4) + nelem size (4) + datalen
+ *
+ * The decoded size is calculated as:
+ * Note: namelen is with terminating 0.
+ * NV_ALIGN(sizeof (nvpair_t) (4 * 4) + namelen + 1) +
+ * NV_ALIGN(data_len)
+ */
+
+ head.encoded_size = NVPE_SIZE(namelen, encoded_size);
+ head.decoded_size = NVP_SIZE(namelen + 1, decoded_size);
+
+ 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_data = (uint8_t *)nvp + sizeof(*nvp);
+ 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 = type;
+ nvl->nv_idx += sizeof (unsigned);
+ *(unsigned *)nvl->nv_idx = nelem;
+ nvl->nv_idx += sizeof (unsigned);
+
+ switch (type) {
+ case DATA_TYPE_BOOLEAN:
+ break;
+ case DATA_TYPE_BYTE_ARRAY:
+ *(unsigned *)nvl->nv_idx = encoded_size;
+ nvl->nv_idx += sizeof (unsigned);
+ bcopy(data, nvl->nv_idx, nelem);
+ nvl->nv_idx += encoded_size;
+ break;
+ case DATA_TYPE_STRING:
+ encoded_size = strlen(data);
+ *(unsigned *)nvl->nv_idx = encoded_size;
+ nvl->nv_idx += sizeof (unsigned);
+ strlcpy((char *)nvl->nv_idx, data, encoded_size + 1);
+ nvl->nv_idx += NV_ALIGN4(encoded_size);
+ break;
+ case DATA_TYPE_STRING_ARRAY:
+ for (uint32_t i = 0; i < nelem; i++) {
+ encoded_size = strlen(((char **)data)[i]);
+ *(unsigned *)nvl->nv_idx = encoded_size;
+ nvl->nv_idx += sizeof (unsigned);
+ strlcpy((char *)nvl->nv_idx, ((char **)data)[i],
+ encoded_size + 1);
+ nvl->nv_idx += NV_ALIGN4(encoded_size);
+ }
+ break;
+ case DATA_TYPE_BYTE:
+ case DATA_TYPE_INT8:
+ case DATA_TYPE_UINT8:
+ case DATA_TYPE_INT8_ARRAY:
+ case DATA_TYPE_UINT8_ARRAY:
+ for (uint32_t i = 0; i < nelem; i++) {
+ *(unsigned *)nvl->nv_idx = ((uint8_t *)data)[i];
+ nvl->nv_idx += sizeof (unsigned);
+ }
+ break;
+ case DATA_TYPE_INT16:
+ case DATA_TYPE_UINT16:
+ case DATA_TYPE_INT16_ARRAY:
+ case DATA_TYPE_UINT16_ARRAY:
+ for (uint32_t i = 0; i < nelem; i++) {
+ *(unsigned *)nvl->nv_idx = ((uint16_t *)data)[i];
+ nvl->nv_idx += sizeof (unsigned);
+ }
+ break;
+ case DATA_TYPE_NVLIST:
+ bcopy(((nvlist_t *)data)->nv_data, nvl->nv_idx, encoded_size);
+ break;
+ case DATA_TYPE_NVLIST_ARRAY: {
+ uint8_t *buf = nvl->nv_idx;
+ size_t size;
+ xdr_t xdr;
+
+ for (uint32_t i = 0; i < nelem; i++) {
+ xdr.xdr_idx = ((nvlist_t **)data)[i]->nv_data;
+ xdr.xdr_buf = xdr.xdr_idx;
+ xdr.xdr_buf_size = ((nvlist_t **)data)[i]->nv_size;
+
+ if (!nvlist_size_native(&xdr, &size))
+ return (EINVAL);
+
+ bcopy(((nvlist_t **)data)[i]->nv_data, buf, size);
+ buf += size;
+ }
+ break;
+ }
+ default:
+ bcopy(data, nvl->nv_idx, encoded_size);
+ }
+
+ nvl->nv_size += head.encoded_size;
+
return (0);
}
+int
+nvlist_add_boolean_value(nvlist_t *nvl, const char *name, boolean_t value)
+{
+ return (nvlist_add_common(nvl, name, DATA_TYPE_BOOLEAN_VALUE, 1,
+ &value));
+}
+
+int
+nvlist_add_byte(nvlist_t *nvl, const char *name, uint8_t value)
+{
+ return (nvlist_add_common(nvl, name, DATA_TYPE_BYTE, 1, &value));
+}
+
+int
+nvlist_add_int8(nvlist_t *nvl, const char *name, int8_t value)
+{
+ return (nvlist_add_common(nvl, name, DATA_TYPE_INT8, 1, &value));
+}
+
+int
+nvlist_add_uint8(nvlist_t *nvl, const char *name, uint8_t value)
+{
+ return (nvlist_add_common(nvl, name, DATA_TYPE_UINT8, 1, &value));
+}
+
+int
+nvlist_add_int16(nvlist_t *nvl, const char *name, int16_t value)
+{
+ return (nvlist_add_common(nvl, name, DATA_TYPE_INT16, 1, &value));
+}
+
+int
+nvlist_add_uint16(nvlist_t *nvl, const char *name, uint16_t value)
+{
+ return (nvlist_add_common(nvl, name, DATA_TYPE_UINT16, 1, &value));
+}
+
+int
+nvlist_add_int32(nvlist_t *nvl, const char *name, int32_t value)
+{
+ return (nvlist_add_common(nvl, name, DATA_TYPE_INT32, 1, &value));
+}
+
+int
+nvlist_add_uint32(nvlist_t *nvl, const char *name, uint32_t value)
+{
+ return (nvlist_add_common(nvl, name, DATA_TYPE_UINT32, 1, &value));
+}
+
+int
+nvlist_add_int64(nvlist_t *nvl, const char *name, int64_t value)
+{
+ return (nvlist_add_common(nvl, name, DATA_TYPE_INT64, 1, &value));
+}
+
+int
+nvlist_add_uint64(nvlist_t *nvl, const char *name, uint64_t value)
+{
+ return (nvlist_add_common(nvl, name, DATA_TYPE_UINT64, 1, &value));
+}
+
+int
+nvlist_add_string(nvlist_t *nvl, const char *name, const char *value)
+{
+ return (nvlist_add_common(nvl, name, DATA_TYPE_STRING, 1, value));
+}
+
+int
+nvlist_add_boolean_array(nvlist_t *nvl, const char *name,
+ boolean_t *a, uint32_t n)
+{
+ return (nvlist_add_common(nvl, name, DATA_TYPE_BOOLEAN_ARRAY, n, a));
+}
+
+int
+nvlist_add_byte_array(nvlist_t *nvl, const char *name, uint8_t *a, uint32_t n)
+{
+ return (nvlist_add_common(nvl, name, DATA_TYPE_BYTE_ARRAY, n, a));
+}
+
+int
+nvlist_add_int8_array(nvlist_t *nvl, const char *name, int8_t *a, uint32_t n)
+{
+ return (nvlist_add_common(nvl, name, DATA_TYPE_INT8_ARRAY, n, a));
+}
+
+int
+nvlist_add_uint8_array(nvlist_t *nvl, const char *name, uint8_t *a, uint32_t n)
+{
+ return (nvlist_add_common(nvl, name, DATA_TYPE_UINT8_ARRAY, n, a));
+}
+
+int
+nvlist_add_int16_array(nvlist_t *nvl, const char *name, int16_t *a, uint32_t n)
+{
+ return (nvlist_add_common(nvl, name, DATA_TYPE_INT16_ARRAY, n, a));
+}
+
+int
+nvlist_add_uint16_array(nvlist_t *nvl, const char *name, uint16_t *a,
+ uint32_t n)
+{
+ return (nvlist_add_common(nvl, name, DATA_TYPE_UINT16_ARRAY, n, a));
+}
+
+int
+nvlist_add_int32_array(nvlist_t *nvl, const char *name, int32_t *a, uint32_t n)
+{
+ return (nvlist_add_common(nvl, name, DATA_TYPE_INT32_ARRAY, n, a));
+}
+
+int
+nvlist_add_uint32_array(nvlist_t *nvl, const char *name, uint32_t *a,
+ uint32_t n)
+{
+ return (nvlist_add_common(nvl, name, DATA_TYPE_UINT32_ARRAY, n, a));
+}
+
+int
+nvlist_add_int64_array(nvlist_t *nvl, const char *name, int64_t *a, uint32_t n)
+{
+ return (nvlist_add_common(nvl, name, DATA_TYPE_INT64_ARRAY, n, a));
+}
+
+int
+nvlist_add_uint64_array(nvlist_t *nvl, const char *name, uint64_t *a,
+ uint32_t n)
+{
+ return (nvlist_add_common(nvl, name, DATA_TYPE_UINT64_ARRAY, n, a));
+}
+
+int
+nvlist_add_string_array(nvlist_t *nvl, const char *name,
+ char * const *a, uint32_t n)
+{
+ return (nvlist_add_common(nvl, name, DATA_TYPE_STRING_ARRAY, n, a));
+}
+
+int
+nvlist_add_nvlist(nvlist_t *nvl, const char *name, nvlist_t *val)
+{
+ return (nvlist_add_common(nvl, name, DATA_TYPE_NVLIST, 1, val));
+}
+
+int
+nvlist_add_nvlist_array(nvlist_t *nvl, const char *name, nvlist_t **a,
+ uint32_t n)
+{
+ return (nvlist_add_common(nvl, name, DATA_TYPE_NVLIST_ARRAY, n, a));
+}
+
+static const char *typenames[] = {
+ "DATA_TYPE_UNKNOWN",
+ "DATA_TYPE_BOOLEAN",
+ "DATA_TYPE_BYTE",
+ "DATA_TYPE_INT16",
+ "DATA_TYPE_UINT16",
+ "DATA_TYPE_INT32",
+ "DATA_TYPE_UINT32",
+ "DATA_TYPE_INT64",
+ "DATA_TYPE_UINT64",
+ "DATA_TYPE_STRING",
+ "DATA_TYPE_BYTE_ARRAY",
+ "DATA_TYPE_INT16_ARRAY",
+ "DATA_TYPE_UINT16_ARRAY",
+ "DATA_TYPE_INT32_ARRAY",
+ "DATA_TYPE_UINT32_ARRAY",
+ "DATA_TYPE_INT64_ARRAY",
+ "DATA_TYPE_UINT64_ARRAY",
+ "DATA_TYPE_STRING_ARRAY",
+ "DATA_TYPE_HRTIME",
+ "DATA_TYPE_NVLIST",
+ "DATA_TYPE_NVLIST_ARRAY",
+ "DATA_TYPE_BOOLEAN_VALUE",
+ "DATA_TYPE_INT8",
+ "DATA_TYPE_UINT8",
+ "DATA_TYPE_BOOLEAN_ARRAY",
+ "DATA_TYPE_INT8_ARRAY",
+ "DATA_TYPE_UINT8_ARRAY"
+};
+
+int
+nvpair_type_from_name(const char *name)
+{
+ unsigned i;
+
+ for (i = 0; i < nitems(typenames); i++) {
+ if (strcmp(name, typenames[i]) == 0)
+ return (i);
+ }
+ return (0);
+}
+
+nvp_header_t *
+nvpair_find(nvlist_t *nv, const char *name)
+{
+ nvp_header_t *nvh;
+
+ nvh = NULL;
+ while ((nvh = nvlist_next_nvpair(nv, nvh)) != NULL) {
+ nv_string_t *nvp_name;
+
+ nvp_name = (nv_string_t *)(nvh + 1);
+ if (nvp_name->nv_size == strlen(name) &&
+ memcmp(nvp_name->nv_data, name, nvp_name->nv_size) == 0)
+ break;
+ }
+ return (nvh);
+}
+
void
-nvlist_print(nvlist_t *nvl, unsigned int indent)
+nvpair_print(nvp_header_t *nvp, unsigned int indent)
{
- static const char *typenames[] = {
- "DATA_TYPE_UNKNOWN",
- "DATA_TYPE_BOOLEAN",
- "DATA_TYPE_BYTE",
- "DATA_TYPE_INT16",
- "DATA_TYPE_UINT16",
- "DATA_TYPE_INT32",
- "DATA_TYPE_UINT32",
- "DATA_TYPE_INT64",
- "DATA_TYPE_UINT64",
- "DATA_TYPE_STRING",
- "DATA_TYPE_BYTE_ARRAY",
- "DATA_TYPE_INT16_ARRAY",
- "DATA_TYPE_UINT16_ARRAY",
- "DATA_TYPE_INT32_ARRAY",
- "DATA_TYPE_UINT32_ARRAY",
- "DATA_TYPE_INT64_ARRAY",
- "DATA_TYPE_UINT64_ARRAY",
- "DATA_TYPE_STRING_ARRAY",
- "DATA_TYPE_HRTIME",
- "DATA_TYPE_NVLIST",
- "DATA_TYPE_NVLIST_ARRAY",
- "DATA_TYPE_BOOLEAN_VALUE",
- "DATA_TYPE_INT8",
- "DATA_TYPE_UINT8",
- "DATA_TYPE_BOOLEAN_ARRAY",
- "DATA_TYPE_INT8_ARRAY",
- "DATA_TYPE_UINT8_ARRAY"
- };
- nvs_data_t *data;
- nvp_header_t *nvp;
nv_string_t *nvp_name;
nv_pair_data_t *nvp_data;
nvlist_t nvlist;
- int i, j;
+ xdr_t xdr;
+ unsigned i, j, u;
+ uint64_t u64;
- data = (nvs_data_t *)nvl->nv_data;
- nvp = &data->nvl_pair; /* first pair in nvlist */
- while (nvp->encoded_size != 0 && nvp->decoded_size != 0) {
- nvp_name = (nv_string_t *)((uintptr_t)nvp + sizeof(*nvp));
- nvp_data = (nv_pair_data_t *)
- NV_ALIGN4((uintptr_t)&nvp_name->nv_data[0] +
- nvp_name->nv_size);
+ nvp_name = (nv_string_t *)((uintptr_t)nvp + sizeof (*nvp));
+ nvp_data = (nv_pair_data_t *)
+ NV_ALIGN4((uintptr_t)&nvp_name->nv_data[0] + nvp_name->nv_size);
- for (int i = 0; i < indent; i++)
- printf(" ");
+ for (i = 0; i < indent; i++)
+ printf(" ");
- printf("%s [%d] %.*s", typenames[nvp_data->nv_type],
- nvp_data->nv_nelem, nvp_name->nv_size, nvp_name->nv_data);
+ printf("%s [%d] %.*s", typenames[nvp_data->nv_type],
+ nvp_data->nv_nelem, nvp_name->nv_size, nvp_name->nv_data);
- switch (nvp_data->nv_type) {
- case DATA_TYPE_UINT64: {
- uint64_t val;
+ switch (nvp_data->nv_type) {
+ case DATA_TYPE_BYTE:
+ case DATA_TYPE_INT8:
+ case DATA_TYPE_UINT8:
+ bcopy(nvp_data->nv_data, &u, sizeof (u));
+ printf(" = 0x%x\n", (unsigned char)u);
+ break;
- val = *(uint64_t *)nvp_data->nv_data;
- printf(" = 0x%jx\n", (uintmax_t)val);
- break;
- }
+ case DATA_TYPE_INT16:
+ case DATA_TYPE_UINT16:
+ bcopy(nvp_data->nv_data, &u, sizeof (u));
+ printf(" = 0x%hx\n", (unsigned short)u);
+ break;
- case DATA_TYPE_STRING: {
- nvp_name = (nv_string_t *)&nvp_data->nv_data[0];
+ case DATA_TYPE_BOOLEAN_VALUE:
+ case DATA_TYPE_INT32:
+ case DATA_TYPE_UINT32:
+ bcopy(nvp_data->nv_data, &u, sizeof (u));
+ printf(" = 0x%x\n", u);
+ break;
+
+ case DATA_TYPE_INT64:
+ case DATA_TYPE_UINT64:
+ bcopy(nvp_data->nv_data, &u64, sizeof (u64));
+ printf(" = 0x%jx\n", (uintmax_t)u64);
+ break;
+
+ case DATA_TYPE_STRING:
+ case DATA_TYPE_STRING_ARRAY:
+ nvp_name = (nv_string_t *)&nvp_data->nv_data[0];
+ for (i = 0; i < nvp_data->nv_nelem; i++) {
printf(" = \"%.*s\"\n", nvp_name->nv_size,
- nvp_name->nv_data );
- break;
+ nvp_name->nv_data);
}
+ break;
- case DATA_TYPE_NVLIST:
- printf("\n");
- nvlist.nv_data = &nvp_data->nv_data[0];
- nvlist_print(&nvlist, indent + 2);
- break;
+ case DATA_TYPE_NVLIST:
+ printf("\n");
+ nvlist.nv_data = &nvp_data->nv_data[0];
+ nvlist_print(&nvlist, indent + 2);
+ break;
- case DATA_TYPE_NVLIST_ARRAY:
- nvlist.nv_data = &nvp_data->nv_data[0];
- for (j = 0; j < nvp_data->nv_nelem; j++) {
- data = (nvs_data_t *)nvlist.nv_data;
- printf("[%d]\n", j);
- nvlist_print(&nvlist, indent + 2);
- if (j != nvp_data->nv_nelem - 1) {
- for (i = 0; i < indent; i++)
- printf(" ");
- printf("%s %.*s",
- typenames[nvp_data->nv_type],
- nvp_name->nv_size,
- nvp_name->nv_data);
- }
- nvlist.nv_data = (uint8_t *)data +
- nvlist_size(&native, nvlist.nv_data);
+ case DATA_TYPE_NVLIST_ARRAY:
+ nvlist.nv_data = &nvp_data->nv_data[0];
+ for (j = 0; j < nvp_data->nv_nelem; j++) {
+ size_t size;
+
+ printf("[%d]\n", j);
+ nvlist_print(&nvlist, indent + 2);
+ if (j != nvp_data->nv_nelem - 1) {
+ for (i = 0; i < indent; i++)
+ printf(" ");
+ printf("%s %.*s",
+ typenames[nvp_data->nv_type],
+ nvp_name->nv_size,
+ nvp_name->nv_data);
}
- break;
+ xdr.xdr_idx = nvlist.nv_data;
+ xdr.xdr_buf = xdr.xdr_idx;
+ xdr.xdr_buf_size = nvp->encoded_size -
+ (xdr.xdr_idx - (uint8_t *)nvp);
- default:
- printf("\n");
+ if (!nvlist_size_native(&xdr, &size))
+ return;
+
+ nvlist.nv_data += size;
}
+ break;
+
+ default:
+ printf("\n");
+ }
+}
+
+void
+nvlist_print(const nvlist_t *nvl, unsigned int indent)
+{
+ nvs_data_t *data;
+ nvp_header_t *nvp;
+
+ data = (nvs_data_t *)nvl->nv_data;
+ nvp = &data->nvl_pair; /* first pair in nvlist */
+ while (nvp->encoded_size != 0 && nvp->decoded_size != 0) {
+ nvpair_print(nvp, indent);
nvp = (nvp_header_t *)((uint8_t *)nvp + nvp->encoded_size);
}
printf("%*s\n", indent + 13, "End of nvlist");
Index: head/stand/libsa/zfs/zfs.c
===================================================================
--- head/stand/libsa/zfs/zfs.c
+++ head/stand/libsa/zfs/zfs.c
@@ -486,8 +486,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;
@@ -496,8 +495,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);
@@ -532,14 +531,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;
}
@@ -555,20 +554,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;
}
@@ -579,14 +578,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;
}
@@ -598,102 +597,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)
{
@@ -746,7 +649,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);
@@ -769,7 +672,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);
@@ -792,58 +695,729 @@
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;
+ nvlist_t *benv = NULL;
+ vdev_t *vd;
spa_t *spa;
+
+ if (dev->dd.d_dev->dv_type != DEVT_ZFS)
+ return (ENOTSUP);
+
+ if ((spa = spa_find_by_dev(dev)) == NULL)
+ return (ENXIO);
+
+ if (spa->spa_bootenv == NULL) {
+ STAILQ_FOREACH(vd, &spa->spa_root_vdev->v_children,
+ v_childlink) {
+ benv = vdev_read_bootenv(vd);
+
+ if (benv != NULL)
+ break;
+ }
+ spa->spa_bootenv = benv;
+ } else {
+ benv = spa->spa_bootenv;
+ }
+
+ 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_write_bootenv(vd, benv);
+ }
+
+ 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);
+}
+
+/*
+ * nvstore backend.
+ */
+
+static int zfs_nvstore_setter(void *, int, const char *,
+ const void *, size_t);
+static int zfs_nvstore_setter_str(void *, const char *, const char *,
+ const char *);
+static int zfs_nvstore_unset_impl(void *, const char *, bool);
+static int zfs_nvstore_setenv(void *, void *);
+
+/*
+ * nvstore is only present for current rootfs pool.
+ */
+static int
+zfs_nvstore_sethook(struct env_var *ev, int flags __unused, const void *value)
+{
+ struct zfs_devdesc *dev;
+ int rv;
+
+ archsw.arch_getdev((void **)&dev, NULL, NULL);
+ if (dev == NULL)
+ return (ENXIO);
+
+ rv = zfs_nvstore_setter_str(dev, NULL, ev->ev_name, value);
+
+ free(dev);
+ return (rv);
+}
+
+/*
+ * nvstore is only present for current rootfs pool.
+ */
+static int
+zfs_nvstore_unsethook(struct env_var *ev)
+{
+ struct zfs_devdesc *dev;
+ int rv;
+
+ archsw.arch_getdev((void **)&dev, NULL, NULL);
+ if (dev == NULL)
+ return (ENXIO);
+
+ rv = zfs_nvstore_unset_impl(dev, ev->ev_name, false);
+
+ free(dev);
+ return (rv);
+}
+
+static int
+zfs_nvstore_getter(void *vdev, const char *name, void **data)
+{
+ struct zfs_devdesc *dev = (struct zfs_devdesc *)vdev;
+ spa_t *spa;
+ nvlist_t *nv;
+ char *str, **ptr;
+ int size;
+ int rv;
+
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)
+ return (ENXIO);
+
+ if (nvlist_find(spa->spa_bootenv, OS_NVSTORE, DATA_TYPE_NVLIST,
+ NULL, &nv, NULL) != 0)
+ return (ENOENT);
+
+ rv = nvlist_find(nv, name, DATA_TYPE_STRING, NULL, &str, &size);
+ if (rv == 0) {
+ ptr = (char **)data;
+ asprintf(ptr, "%.*s", size, str);
+ if (*data == NULL)
+ rv = ENOMEM;
}
+ nvlist_destroy(nv);
+ return (rv);
+}
- STAILQ_FOREACH(vd, &spa->spa_root_vdev->v_children, v_childlink) {
- char *tmp = vdev_read_pad2(vd);
+static int
+zfs_nvstore_setter(void *vdev, int type, const char *name,
+ const void *data, size_t size)
+{
+ struct zfs_devdesc *dev = (struct zfs_devdesc *)vdev;
+ spa_t *spa;
+ nvlist_t *nv;
+ int rv;
+ bool env_set = true;
- /* Continue on error. */
- if (tmp == NULL)
- continue;
- /* Nextboot is not set. */
- if (*tmp == '\0') {
- free(result);
- free(tmp);
- return (1);
+ if (dev->dd.d_dev->dv_type != DEVT_ZFS)
+ return (ENOTSUP);
+
+ if ((spa = spa_find_by_dev(dev)) == NULL)
+ return (ENXIO);
+
+ if (spa->spa_bootenv == NULL)
+ return (ENXIO);
+
+ if (nvlist_find(spa->spa_bootenv, OS_NVSTORE, DATA_TYPE_NVLIST,
+ NULL, &nv, NULL) != 0) {
+ nv = nvlist_create(NV_UNIQUE_NAME);
+ if (nv == NULL)
+ return (ENOMEM);
+ }
+
+ rv = 0;
+ switch (type) {
+ case DATA_TYPE_INT8:
+ if (size != sizeof (int8_t)) {
+ rv = EINVAL;
+ break;
}
- if (result == NULL) {
- result = tmp;
- continue;
+ rv = nvlist_add_int8(nv, name, *(int8_t *)data);
+ break;
+
+ case DATA_TYPE_INT16:
+ if (size != sizeof (int16_t)) {
+ rv = EINVAL;
+ break;
}
- free(tmp);
+ rv = nvlist_add_int16(nv, name, *(int16_t *)data);
+ break;
+
+ case DATA_TYPE_INT32:
+ if (size != sizeof (int32_t)) {
+ rv = EINVAL;
+ break;
+ }
+ rv = nvlist_add_int32(nv, name, *(int32_t *)data);
+ break;
+
+ case DATA_TYPE_INT64:
+ if (size != sizeof (int64_t)) {
+ rv = EINVAL;
+ break;
+ }
+ rv = nvlist_add_int64(nv, name, *(int64_t *)data);
+ break;
+
+ case DATA_TYPE_BYTE:
+ if (size != sizeof (uint8_t)) {
+ rv = EINVAL;
+ break;
+ }
+ rv = nvlist_add_byte(nv, name, *(int8_t *)data);
+ break;
+
+ case DATA_TYPE_UINT8:
+ if (size != sizeof (uint8_t)) {
+ rv = EINVAL;
+ break;
+ }
+ rv = nvlist_add_uint8(nv, name, *(int8_t *)data);
+ break;
+
+ case DATA_TYPE_UINT16:
+ if (size != sizeof (uint16_t)) {
+ rv = EINVAL;
+ break;
+ }
+ rv = nvlist_add_uint16(nv, name, *(uint16_t *)data);
+ break;
+
+ case DATA_TYPE_UINT32:
+ if (size != sizeof (uint32_t)) {
+ rv = EINVAL;
+ break;
+ }
+ rv = nvlist_add_uint32(nv, name, *(uint32_t *)data);
+ break;
+
+ case DATA_TYPE_UINT64:
+ if (size != sizeof (uint64_t)) {
+ rv = EINVAL;
+ break;
+ }
+ rv = nvlist_add_uint64(nv, name, *(uint64_t *)data);
+ break;
+
+ case DATA_TYPE_STRING:
+ rv = nvlist_add_string(nv, name, data);
+ break;
+
+ case DATA_TYPE_BOOLEAN_VALUE:
+ if (size != sizeof (boolean_t)) {
+ rv = EINVAL;
+ break;
+ }
+ rv = nvlist_add_boolean_value(nv, name, *(boolean_t *)data);
+ break;
+
+ default:
+ rv = EINVAL;
+ break;
}
- if (result == NULL)
- return (1);
- STAILQ_FOREACH(vd, &spa->spa_root_vdev->v_children, v_childlink) {
- vdev_clear_pad2(vd);
+ if (rv == 0) {
+ rv = nvlist_add_nvlist(spa->spa_bootenv, OS_NVSTORE, nv);
+ if (rv == 0) {
+ rv = zfs_set_bootenv(vdev, spa->spa_bootenv);
+ }
+ if (rv == 0) {
+ if (env_set) {
+ rv = zfs_nvstore_setenv(vdev,
+ nvpair_find(nv, name));
+ } else {
+ env_discard(env_getenv(name));
+ rv = 0;
+ }
+ }
}
- strlcpy(buf, result, size);
- free(result);
+ nvlist_destroy(nv);
+ return (rv);
+}
+
+static int
+get_int64(const char *data, int64_t *ip)
+{
+ char *end;
+ int64_t val;
+
+ errno = 0;
+ val = strtoll(data, &end, 0);
+ if (errno != 0 || *data == '\0' || *end != '\0')
+ return (EINVAL);
+
+ *ip = val;
return (0);
}
+static int
+get_uint64(const char *data, uint64_t *ip)
+{
+ char *end;
+ uint64_t val;
+
+ errno = 0;
+ val = strtoull(data, &end, 0);
+ if (errno != 0 || *data == '\0' || *end != '\0')
+ return (EINVAL);
+
+ *ip = val;
+ return (0);
+}
+
+/*
+ * Translate textual data to data type. If type is not set, and we are
+ * creating new pair, use DATA_TYPE_STRING.
+ */
+static int
+zfs_nvstore_setter_str(void *vdev, const char *type, const char *name,
+ const char *data)
+{
+ struct zfs_devdesc *dev = (struct zfs_devdesc *)vdev;
+ spa_t *spa;
+ nvlist_t *nv;
+ int rv;
+ data_type_t dt;
+ int64_t val;
+ uint64_t uval;
+
+ if (dev->dd.d_dev->dv_type != DEVT_ZFS)
+ return (ENOTSUP);
+
+ if ((spa = spa_find_by_dev(dev)) == NULL)
+ return (ENXIO);
+
+ if (spa->spa_bootenv == NULL)
+ return (ENXIO);
+
+ if (nvlist_find(spa->spa_bootenv, OS_NVSTORE, DATA_TYPE_NVLIST,
+ NULL, &nv, NULL) != 0) {
+ nv = NULL;
+ }
+
+ if (type == NULL) {
+ nvp_header_t *nvh;
+
+ /*
+ * if there is no existing pair, default to string.
+ * Otherwise, use type from existing pair.
+ */
+ nvh = nvpair_find(nv, name);
+ if (nvh == NULL) {
+ dt = DATA_TYPE_STRING;
+ } else {
+ nv_string_t *nvp_name;
+ nv_pair_data_t *nvp_data;
+
+ nvp_name = (nv_string_t *)(nvh + 1);
+ nvp_data = (nv_pair_data_t *)(&nvp_name->nv_data[0] +
+ NV_ALIGN4(nvp_name->nv_size));
+ dt = nvp_data->nv_type;
+ }
+ } else {
+ dt = nvpair_type_from_name(type);
+ }
+ nvlist_destroy(nv);
+
+ rv = 0;
+ switch (dt) {
+ case DATA_TYPE_INT8:
+ rv = get_int64(data, &val);
+ if (rv == 0) {
+ int8_t v = val;
+
+ rv = zfs_nvstore_setter(vdev, dt, name, &v, sizeof (v));
+ }
+ break;
+ case DATA_TYPE_INT16:
+ rv = get_int64(data, &val);
+ if (rv == 0) {
+ int16_t v = val;
+
+ rv = zfs_nvstore_setter(vdev, dt, name, &v, sizeof (v));
+ }
+ break;
+ case DATA_TYPE_INT32:
+ rv = get_int64(data, &val);
+ if (rv == 0) {
+ int32_t v = val;
+
+ rv = zfs_nvstore_setter(vdev, dt, name, &v, sizeof (v));
+ }
+ break;
+ case DATA_TYPE_INT64:
+ rv = get_int64(data, &val);
+ if (rv == 0) {
+ rv = zfs_nvstore_setter(vdev, dt, name, &val,
+ sizeof (val));
+ }
+ break;
+
+ case DATA_TYPE_BYTE:
+ rv = get_uint64(data, &uval);
+ if (rv == 0) {
+ uint8_t v = uval;
+
+ rv = zfs_nvstore_setter(vdev, dt, name, &v, sizeof (v));
+ }
+ break;
+
+ case DATA_TYPE_UINT8:
+ rv = get_uint64(data, &uval);
+ if (rv == 0) {
+ uint8_t v = uval;
+
+ rv = zfs_nvstore_setter(vdev, dt, name, &v, sizeof (v));
+ }
+ break;
+
+ case DATA_TYPE_UINT16:
+ rv = get_uint64(data, &uval);
+ if (rv == 0) {
+ uint16_t v = uval;
+
+ rv = zfs_nvstore_setter(vdev, dt, name, &v, sizeof (v));
+ }
+ break;
+
+ case DATA_TYPE_UINT32:
+ rv = get_uint64(data, &uval);
+ if (rv == 0) {
+ uint32_t v = uval;
+
+ rv = zfs_nvstore_setter(vdev, dt, name, &v, sizeof (v));
+ }
+ break;
+
+ case DATA_TYPE_UINT64:
+ rv = get_uint64(data, &uval);
+ if (rv == 0) {
+ rv = zfs_nvstore_setter(vdev, dt, name, &uval,
+ sizeof (uval));
+ }
+ break;
+
+ case DATA_TYPE_STRING:
+ rv = zfs_nvstore_setter(vdev, dt, name, data, strlen(data) + 1);
+ break;
+
+ case DATA_TYPE_BOOLEAN_VALUE:
+ rv = get_int64(data, &val);
+ if (rv == 0) {
+ boolean_t v = val;
+
+ rv = zfs_nvstore_setter(vdev, dt, name, &v, sizeof (v));
+ }
+
+ default:
+ rv = EINVAL;
+ }
+ return (rv);
+}
+
+static int
+zfs_nvstore_unset_impl(void *vdev, const char *name, bool unset_env)
+{
+ struct zfs_devdesc *dev = (struct zfs_devdesc *)vdev;
+ spa_t *spa;
+ nvlist_t *nv;
+ int rv;
+
+ if (dev->dd.d_dev->dv_type != DEVT_ZFS)
+ return (ENOTSUP);
+
+ if ((spa = spa_find_by_dev(dev)) == NULL)
+ return (ENXIO);
+
+ if (spa->spa_bootenv == NULL)
+ return (ENXIO);
+
+ if (nvlist_find(spa->spa_bootenv, OS_NVSTORE, DATA_TYPE_NVLIST,
+ NULL, &nv, NULL) != 0)
+ return (ENOENT);
+
+ rv = nvlist_remove(nv, name, DATA_TYPE_UNKNOWN);
+ if (rv == 0) {
+ if (nvlist_next_nvpair(nv, NULL) == NULL) {
+ rv = nvlist_remove(spa->spa_bootenv, OS_NVSTORE,
+ DATA_TYPE_NVLIST);
+ } else {
+ rv = nvlist_add_nvlist(spa->spa_bootenv,
+ OS_NVSTORE, nv);
+ }
+ if (rv == 0)
+ rv = zfs_set_bootenv(vdev, spa->spa_bootenv);
+ }
+
+ if (unset_env)
+ env_discard(env_getenv(name));
+ return (rv);
+}
+
+static int
+zfs_nvstore_unset(void *vdev, const char *name)
+{
+ return (zfs_nvstore_unset_impl(vdev, name, true));
+}
+
+static int
+zfs_nvstore_print(void *vdev __unused, void *ptr)
+{
+
+ nvpair_print(ptr, 0);
+ return (0);
+}
+
+/*
+ * Create environment variable from nvpair.
+ * set hook will update nvstore with new value, unset hook will remove
+ * variable from nvstore.
+ */
+static int
+zfs_nvstore_setenv(void *vdev __unused, void *ptr)
+{
+ nvp_header_t *nvh = ptr;
+ nv_string_t *nvp_name, *nvp_value;
+ nv_pair_data_t *nvp_data;
+ char *name, *value;
+ int rv = 0;
+
+ if (nvh == NULL)
+ return (ENOENT);
+
+ nvp_name = (nv_string_t *)(nvh + 1);
+ nvp_data = (nv_pair_data_t *)(&nvp_name->nv_data[0] +
+ NV_ALIGN4(nvp_name->nv_size));
+
+ if ((name = nvstring_get(nvp_name)) == NULL)
+ return (ENOMEM);
+
+ value = NULL;
+ switch (nvp_data->nv_type) {
+ case DATA_TYPE_BYTE:
+ case DATA_TYPE_UINT8:
+ (void) asprintf(&value, "%uc",
+ *(unsigned *)&nvp_data->nv_data[0]);
+ if (value == NULL)
+ rv = ENOMEM;
+ break;
+
+ case DATA_TYPE_INT8:
+ (void) asprintf(&value, "%c", *(int *)&nvp_data->nv_data[0]);
+ if (value == NULL)
+ rv = ENOMEM;
+ break;
+
+ case DATA_TYPE_INT16:
+ (void) asprintf(&value, "%hd", *(short *)&nvp_data->nv_data[0]);
+ if (value == NULL)
+ rv = ENOMEM;
+ break;
+
+ case DATA_TYPE_UINT16:
+ (void) asprintf(&value, "%hu",
+ *(unsigned short *)&nvp_data->nv_data[0]);
+ if (value == NULL)
+ rv = ENOMEM;
+ break;
+
+ case DATA_TYPE_BOOLEAN_VALUE:
+ case DATA_TYPE_INT32:
+ (void) asprintf(&value, "%d", *(int *)&nvp_data->nv_data[0]);
+ if (value == NULL)
+ rv = ENOMEM;
+ break;
+
+ case DATA_TYPE_UINT32:
+ (void) asprintf(&value, "%u",
+ *(unsigned *)&nvp_data->nv_data[0]);
+ if (value == NULL)
+ rv = ENOMEM;
+ break;
+
+ case DATA_TYPE_INT64:
+ (void) asprintf(&value, "%jd",
+ (intmax_t)*(int64_t *)&nvp_data->nv_data[0]);
+ if (value == NULL)
+ rv = ENOMEM;
+ break;
+
+ case DATA_TYPE_UINT64:
+ (void) asprintf(&value, "%ju",
+ (uintmax_t)*(uint64_t *)&nvp_data->nv_data[0]);
+ if (value == NULL)
+ rv = ENOMEM;
+ break;
+
+ case DATA_TYPE_STRING:
+ nvp_value = (nv_string_t *)&nvp_data->nv_data[0];
+ if ((value = nvstring_get(nvp_value)) == NULL) {
+ rv = ENOMEM;
+ break;
+ }
+ break;
+
+ default:
+ rv = EINVAL;
+ break;
+ }
+
+ if (value != NULL) {
+ rv = env_setenv(name, EV_VOLATILE | EV_NOHOOK, value,
+ zfs_nvstore_sethook, zfs_nvstore_unsethook);
+ free(value);
+ }
+ free(name);
+ return (rv);
+}
+
+static int
+zfs_nvstore_iterate(void *vdev, int (*cb)(void *, void *))
+{
+ struct zfs_devdesc *dev = (struct zfs_devdesc *)vdev;
+ spa_t *spa;
+ nvlist_t *nv;
+ nvp_header_t *nvh;
+ int rv;
+
+ if (dev->dd.d_dev->dv_type != DEVT_ZFS)
+ return (ENOTSUP);
+
+ if ((spa = spa_find_by_dev(dev)) == NULL)
+ return (ENXIO);
+
+ if (spa->spa_bootenv == NULL)
+ return (ENXIO);
+
+ if (nvlist_find(spa->spa_bootenv, OS_NVSTORE, DATA_TYPE_NVLIST,
+ NULL, &nv, NULL) != 0)
+ return (ENOENT);
+
+ rv = 0;
+ nvh = NULL;
+ while ((nvh = nvlist_next_nvpair(nv, nvh)) != NULL) {
+ rv = cb(vdev, nvh);
+ if (rv != 0)
+ break;
+ }
+ return (rv);
+}
+
+nvs_callbacks_t nvstore_zfs_cb = {
+ .nvs_getter = zfs_nvstore_getter,
+ .nvs_setter = zfs_nvstore_setter,
+ .nvs_setter_str = zfs_nvstore_setter_str,
+ .nvs_unset = zfs_nvstore_unset,
+ .nvs_print = zfs_nvstore_print,
+ .nvs_iterate = zfs_nvstore_iterate
+};
+
int
+zfs_attach_nvstore(void *vdev)
+{
+ struct zfs_devdesc *dev = vdev;
+ spa_t *spa;
+ uint64_t version;
+ int rv;
+
+ if (dev->dd.d_dev->dv_type != DEVT_ZFS)
+ return (ENOTSUP);
+
+ if ((spa = spa_find_by_dev(dev)) == NULL)
+ return (ENXIO);
+
+ rv = nvlist_find(spa->spa_bootenv, BOOTENV_VERSION, DATA_TYPE_UINT64,
+ NULL, &version, NULL);
+
+ if (rv != 0 || version != VB_NVLIST) {
+ return (ENXIO);
+ }
+
+ dev = malloc(sizeof (*dev));
+ if (dev == NULL)
+ return (ENOMEM);
+ memcpy(dev, vdev, sizeof (*dev));
+
+ rv = nvstore_init(spa->spa_name, &nvstore_zfs_cb, dev);
+ if (rv != 0)
+ free(dev);
+ else
+ rv = zfs_nvstore_iterate(dev, zfs_nvstore_setenv);
+ return (rv);
+}
+
+int
zfs_probe_dev(const char *devname, uint64_t *pool_guid)
{
struct disk_devdesc *dev;
@@ -939,12 +1513,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;
@@ -1073,10 +1644,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: head/stand/libsa/zfs/zfsimpl.c
===================================================================
--- head/stand/libsa/zfs/zfsimpl.c
+++ head/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"
@@ -220,8 +222,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);
@@ -229,7 +231,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);
@@ -238,6 +240,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;
@@ -1084,7 +1095,7 @@
vdev_from_nvlist(spa_t *spa, uint64_t top_guid, const nvlist_t *nvlist)
{
vdev_t *top_vdev, *vdev;
- nvlist_t *kids = NULL;
+ nvlist_t **kids = NULL;
int rc, nkids;
/* Get top vdev. */
@@ -1105,27 +1116,18 @@
for (int i = 0; i < nkids; i++) {
uint64_t guid;
- rc = nvlist_find(kids, ZPOOL_CONFIG_GUID,
+ rc = nvlist_find(kids[i], ZPOOL_CONFIG_GUID,
DATA_TYPE_UINT64, NULL, &guid, NULL);
- if (rc != 0) {
- nvlist_destroy(kids);
- return (rc);
- }
- rc = vdev_init(guid, kids, &vdev);
- if (rc != 0) {
- nvlist_destroy(kids);
- return (rc);
- }
+ if (rc != 0)
+ goto done;
+ rc = vdev_init(guid, kids[i], &vdev);
+ if (rc != 0)
+ goto done;
+
vdev->v_spa = spa;
vdev->v_top = top_vdev;
vdev_insert(top_vdev, vdev);
-
- rc = nvlist_next(kids);
- if (rc != 0) {
- nvlist_destroy(kids);
- return (rc);
- }
}
} else {
/*
@@ -1134,7 +1136,12 @@
*/
rc = 0;
}
- nvlist_destroy(kids);
+done:
+ if (kids != NULL) {
+ for (int i = 0; i < nkids; i++)
+ nvlist_destroy(kids[i]);
+ free(kids);
+ }
return (rc);
}
@@ -1210,7 +1217,7 @@
vdev_update_from_nvlist(uint64_t top_guid, const nvlist_t *nvlist)
{
vdev_t *vdev;
- nvlist_t *kids = NULL;
+ nvlist_t **kids = NULL;
int rc, nkids;
/* Update top vdev. */
@@ -1225,23 +1232,23 @@
for (int i = 0; i < nkids; i++) {
uint64_t guid;
- rc = nvlist_find(kids, ZPOOL_CONFIG_GUID,
+ rc = nvlist_find(kids[i], ZPOOL_CONFIG_GUID,
DATA_TYPE_UINT64, NULL, &guid, NULL);
if (rc != 0)
break;
vdev = vdev_find(guid);
if (vdev != NULL)
- vdev_set_initial_state(vdev, kids);
-
- rc = nvlist_next(kids);
- if (rc != 0)
- break;
+ vdev_set_initial_state(vdev, kids[i]);
}
} else {
rc = 0;
}
- nvlist_destroy(kids);
+ if (kids != NULL) {
+ for (int i = 0; i < nkids; i++)
+ nvlist_destroy(kids[i]);
+ free(kids);
+ }
return (rc);
}
@@ -1250,7 +1257,7 @@
vdev_init_from_nvlist(spa_t *spa, const nvlist_t *nvlist)
{
uint64_t pool_guid, vdev_children;
- nvlist_t *vdevs = NULL, *kids = NULL;
+ nvlist_t *vdevs = NULL, **kids = NULL;
int rc, nkids;
if (nvlist_find(nvlist, ZPOOL_CONFIG_POOL_GUID, DATA_TYPE_UINT64,
@@ -1285,7 +1292,7 @@
uint64_t guid;
vdev_t *vdev;
- rc = nvlist_find(kids, ZPOOL_CONFIG_GUID, DATA_TYPE_UINT64,
+ rc = nvlist_find(kids[i], ZPOOL_CONFIG_GUID, DATA_TYPE_UINT64,
NULL, &guid, NULL);
if (rc != 0)
break;
@@ -1294,16 +1301,17 @@
* Top level vdev is missing, create it.
*/
if (vdev == NULL)
- rc = vdev_from_nvlist(spa, guid, kids);
+ rc = vdev_from_nvlist(spa, guid, kids[i]);
else
- rc = vdev_update_from_nvlist(guid, kids);
+ rc = vdev_update_from_nvlist(guid, kids[i]);
if (rc != 0)
break;
- rc = nvlist_next(kids);
- if (rc != 0)
- break;
}
- nvlist_destroy(kids);
+ if (kids != NULL) {
+ for (int i = 0; i < nkids; i++)
+ nvlist_destroy(kids[i]);
+ free(kids);
+ }
/*
* Re-evaluate top-level vdev state.
@@ -1338,6 +1346,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;
@@ -1589,6 +1610,254 @@
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;
+
+ bcopy(&nv.nv_header, be->vbe_bootenv, sizeof(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) {
+ if (*be->vbe_bootenv == '\0') {
+ nvlist_add_uint64(benv, BOOTENV_VERSION,
+ VB_NVLIST);
+ break;
+ }
+ 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, sizeof(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)
{
@@ -1621,7 +1890,7 @@
goto done;
if (memcmp(type, VDEV_TYPE_RAIDZ, len) == 0) {
- nvlist_t *kids;
+ nvlist_t **kids;
int nkids;
if (nvlist_find(vdevs, ZPOOL_CONFIG_CHILDREN,
@@ -1631,7 +1900,9 @@
}
asize /= nkids;
- nvlist_destroy(kids);
+ for (int i = 0; i < nkids; i++)
+ nvlist_destroy(kids[i]);
+ free(kids);
}
asize += VDEV_LABEL_START_SIZE + VDEV_LABEL_END_SIZE;
@@ -1655,15 +1926,13 @@
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,
+ sizeof(label->vp_nvlist));
if (tmp == NULL)
continue;
@@ -1728,7 +1997,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;
@@ -1746,8 +2016,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. */
@@ -1861,7 +2132,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.
@@ -3132,7 +3404,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)
@@ -3156,7 +3428,7 @@
nv = NULL;
return (rc);
}
- *value = nvlist_import(nv + 4, nv[0], nv[1]);
+ *value = nvlist_import(nv, size);
free(nv);
return (rc);
}
Index: head/stand/loader.mk
===================================================================
--- head/stand/loader.mk
+++ head/stand/loader.mk
@@ -6,7 +6,7 @@
SRCS+= boot.c commands.c console.c devopen.c interp.c
SRCS+= interp_backslash.c interp_parse.c ls.c misc.c
-SRCS+= module.c
+SRCS+= module.c nvstore.c
.if ${MACHINE} == "i386" || ${MACHINE_CPUARCH} == "amd64"
SRCS+= load_elf32.c load_elf32_obj.c reloc_elf32.c
Index: head/stand/lua/config.lua
===================================================================
--- head/stand/lua/config.lua
+++ head/stand/lua/config.lua
@@ -389,16 +389,24 @@
local function checkNextboot()
local nextboot_file = loader.getenv("nextboot_conf")
+ local nextboot_enable = loader.getenv("nextboot_enable")
+
if nextboot_file == nil then
return
end
+ -- is nextboot_enable set in nvstore?
+ if nextboot_enable == "NO" then
+ return
+ end
+
local text = readFile(nextboot_file, true)
if text == nil then
return
end
- if text:match("^nextboot_enable=\"NO\"") ~= nil then
+ if nextboot_enable == nil and
+ text:match("^nextboot_enable=\"NO\"") ~= nil then
-- We're done; nextboot is not enabled
return
end
@@ -421,6 +429,7 @@
io.write(nfile, "nextboot_enable=\"NO\" ")
io.close(nfile)
end
+ loader.setenv("nextboot_enable", "NO")
end
-- Module exports
Index: head/stand/userboot/test/test.c
===================================================================
--- head/stand/userboot/test/test.c
+++ head/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: head/stand/userboot/userboot.h
===================================================================
--- head/stand/userboot/userboot.h
+++ head/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: head/stand/userboot/userboot/Makefile
===================================================================
--- head/stand/userboot/userboot/Makefile
+++ head/stand/userboot/userboot/Makefile
@@ -32,8 +32,9 @@
CFLAGS+= -Wall
CFLAGS+= -I${BOOTSRC}/userboot
-
-CFLAGS+= -I${SYSDIR}/cddl/contrib/opensolaris/uts/common
+CFLAGS.main.c+= -I${BOOTSRC}/libsa/zfs
+CFLAGS.main.c+= -I${SYSDIR}/contrib/openzfs/include
+CFLAGS.main.c+= -I${SYSDIR}/contrib/openzfs/include/os/freebsd/zfs
CWARNFLAGS.main.c += -Wno-implicit-function-declaration
LDFLAGS+= -nostdlib -Wl,-Bsymbolic
Index: head/stand/userboot/userboot/main.c
===================================================================
--- head/stand/userboot/userboot/main.c
+++ head/stand/userboot/userboot/main.c
@@ -32,6 +32,7 @@
#include <string.h>
#include <setjmp.h>
#include <sys/disk.h>
+#include <sys/zfs_bootenv.h>
#include "bootstrap.h"
#include "disk.h"
@@ -214,6 +215,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 +236,7 @@
struct devdesc *dd;
#if defined(USERBOOT_ZFS_SUPPORT)
struct zfs_devdesc zdev;
+ char *buf = NULL;
if (userboot_zfs_found) {
@@ -257,10 +269,23 @@
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, OS_BOOTONCE, buf,
+ VDEV_PAD_SIZE) == 0) {
+ printf("zfs bootonce: %s\n", buf);
+ set_currdev(buf);
+ setenv("zfs-bootonce", buf, 1);
+ }
+ free(buf);
+ (void) zfs_attach_nvstore(&zdev);
+ }
+ }
+#endif
}
#if defined(USERBOOT_ZFS_SUPPORT)
Index: head/stand/userboot/userboot/userboot_disk.c
===================================================================
--- head/stand/userboot/userboot/userboot_disk.c
+++ head/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: head/sys/cddl/boot/zfs/zfsimpl.h
===================================================================
--- head/sys/cddl/boot/zfs/zfsimpl.h
+++ head/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;
@@ -1663,10 +1663,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;
@@ -1794,8 +1793,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 */
/*
@@ -1822,10 +1822,11 @@
void *spa_cksum_tmpls[ZIO_CHECKSUM_FUNCTIONS];
boolean_t spa_with_log; /* this pool has log */
- struct uberblock spa_uberblock_master; /* best uberblock so far */
- objset_phys_t spa_mos_master; /* MOS for this pool */
- struct uberblock spa_uberblock_checkpoint; /* checkpoint uberblock */
- objset_phys_t spa_mos_checkpoint; /* Checkpoint MOS */
+ struct uberblock spa_uberblock_master; /* best uberblock so far */
+ objset_phys_t spa_mos_master; /* MOS for this pool */
+ struct uberblock spa_uberblock_checkpoint; /* checkpoint uberblock */
+ objset_phys_t spa_mos_checkpoint; /* Checkpoint MOS */
+ void *spa_bootenv; /* bootenv from pool label */
} spa_t;
/* IO related arguments. */
Index: head/tools/tools/zfsboottest/zfsboottest.c
===================================================================
--- head/tools/tools/zfsboottest/zfsboottest.c
+++ head/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

Mime Type
text/plain
Expires
Sun, Feb 16, 7:31 AM (15 h, 22 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
16670808
Default Alt Text
D25512.diff (137 KB)

Event Timeline