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 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 #include #include +#include #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 +#include #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 #include #include +#include #include #include +#include -#include +#include -/* 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 \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 \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 + * + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#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 #include #include +#include #include #include #include @@ -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 #include #include +#include #include #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 #include +#include #include #include @@ -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 #ifdef LOADER_GELI_SUPPORT #include #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 +#include #include +#include +#include #include #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 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 #include #include #include #include +#include #include #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 #include #include +#include #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]); }