loader: zfs should support bootonce an nextboot

bootonce feature is temporary, one time boot, activated by
"bectl activate -t BE", "bectl activate -T BE" will reset the bootonce flag.

By default, the bootonce setting is reset on attempt to boot and the next
boot will use previously active BE.

By setting zfs_bootonce_activate="YES" in rc.conf, the bootonce BE will
be set permanently active.

bootonce dataset name is recorded in boot pool labels, bootenv area.

in case of nextboot, the nextboot_enable boolean variable is recorded in
freebsd:nvstore nvlist, also stored in boot pool label bootenv area.
On boot, the loader will process /boot/nextboot.conf if nextboot_enable
is "YES", and will set nextboot_enable to "NO", preventing /boot/nextboot.conf
processing on next boot.

bootonce and nextboot features are usable in both UEFI and BIOS boot.

To use bootonce/nextboot features, the boot loader needs to be updated on disk;
if loader.efi is stored on ESP, then ESP needs to be updated and
for BIOS boot, stage2 (zfsboot or gptzfsboot) needs to be updated
(gpart or other tools).

At this time, only lua loader is updated.

Sponsored by:	Netflix, Klara Inc.
Differential Revision:	https://reviews.freebsd.org/D25512
This commit is contained in:
Toomas Soome 2020-09-21 09:01:10 +00:00
parent 7d54cc9165
commit e307eb94ae
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=365938
52 changed files with 3347 additions and 780 deletions

View File

@ -2835,7 +2835,7 @@ _prebuild_libs= ${_kerberos5_lib_libasn1} \
${_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_libtpool= cddl/lib/libtpool
_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/libuutil__L cddl/lib/libavl__L lib/libgeom__L
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

View File

@ -15,6 +15,7 @@ SUBDIR= drti \
libuutil \
${_libzfs_core} \
${_libzfs} \
${_libzfsbootenv} \
${_libzpool} \
${_libzutil}
@ -26,6 +27,7 @@ _libicp= libicp
_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_core= libnvpair
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=

View File

@ -0,0 +1,33 @@
# $FreeBSD$
.PATH: ${SRCTOP}/sys/contrib/openzfs/lib/libzfsbootenv
.PATH: ${SRCTOP}/sys/contrib/openzfs/include
PACKAGE= runtime
LIB= zfsbootenv
SHLIB_MAJOR= 1
LIBADD= zfs
LIBADD+= nvpair
INCS= libzfsbootenv.h
USER_C= \
lzbe_device.c \
lzbe_util.c \
lzbe_pair.c
SRCS= $(USER_C)
CSTD= c99
CFLAGS+= -DIN_BASE
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/include
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/os/freebsd
CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/include
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/module/icp/include
CFLAGS+= -include ${SRCTOP}/sys/contrib/openzfs/include/os/freebsd/spl/sys/ccompile.h
CFLAGS+= -DHAVE_ISSETUGID
CFLAGS+= -include ${SRCTOP}/sys/modules/zfs/zfs_config.h
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/include/os/freebsd/zfs
.include <bsd.lib.mk>

View File

@ -228,6 +228,7 @@ CFLAGS+= \
-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 \

View File

@ -13,7 +13,9 @@ INCS= be.h
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

View File

@ -46,6 +46,7 @@ __FBSDID("$FreeBSD$");
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <libzfsbootenv.h>
#include "be.h"
#include "be_impl.h"
@ -1221,43 +1222,20 @@ be_add_child(libbe_handle_t *lbh, const char *child_path, bool cp_if_exists)
}
#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 @@ int
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 @@ be_activate(libbe_handle_t *lbh, const char *bootenv, bool temporary)
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 */

View File

@ -81,6 +81,7 @@ int be_prop_list_alloc(nvlist_t **be_list);
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 *);

View File

@ -63,6 +63,7 @@ typedef struct prop_data {
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 *);

View File

@ -30,6 +30,7 @@
__FBSDID("$FreeBSD$");
#include <sys/zfs_context.h>
#include <libzfsbootenv.h>
#include "be.h"
#include "be_impl.h"
@ -108,6 +109,7 @@ be_get_bootenv_props(libbe_handle_t *lbh, nvlist_t *dsnvl)
data.lbh = lbh;
data.list = dsnvl;
data.single_object = false;
data.bootonce = NULL;
return (be_proplist_update(&data));
}
@ -121,6 +123,7 @@ be_get_dataset_props(libbe_handle_t *lbh, const char *name, nvlist_t *props)
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 @@ be_get_dataset_snapshots(libbe_handle_t *lbh, const char *name, nvlist_t *props)
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 @@ prop_list_builder_cb(zfs_handle_t *zfs_hdl, void *data_p)
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);
@ -246,6 +254,9 @@ be_proplist_update(prop_data_t *data)
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);

View File

@ -27,7 +27,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd October 16, 2019
.Dd July 22, 2020
.Dt LIBBE 3
.Os
.Sh NAME
@ -78,6 +78,10 @@
.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
@ -270,8 +274,24 @@ If the
.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

View File

@ -64,6 +64,7 @@ rc_conf_files="/etc/rc.conf /etc/rc.conf.local"
# 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

View File

@ -48,6 +48,21 @@ mount_subordinate()
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 @@ be_start()
[ $_mp = "/" ] || continue
if [ $_type = "zfs" ] ; then
mount_subordinate $_dev
if checkyesno zfs_bootonce_activate; then
activate_bootonce $_dev
fi
fi
break
done

View File

@ -129,7 +129,7 @@ CRUNCH_PROGS_usr.sbin+= zdb
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

View File

@ -12,6 +12,7 @@ LIBADD+= be \
nvpair \
spl \
util \
zfsbootenv
CFLAGS+= -DIN_BASE
CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/include

View File

@ -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 @@ The following commands are supported by
.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 @@ as the default boot filesystem.
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 @@ field indicates whether the boot environment is active now
.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

View File

@ -74,6 +74,7 @@ usage(bool explicit)
"\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 @@ static int
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 @@ bectl_cmd_activate(int argc, char *argv[])
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)

View File

@ -182,7 +182,7 @@ print_info(const char *name, nvlist_t *dsprops, struct printc *pc)
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;
@ -230,6 +230,11 @@ print_info(const char *name, nvlist_t *dsprops, struct printc *pc)
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) {
printf("-");
active_colsz--;

View File

@ -24,7 +24,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd April 9, 2016
.Dd September 19, 2020
.Dt NEXTBOOT 8
.Os
.Sh NAME
@ -136,8 +136,3 @@ It is also my first attempt to write in Forth.
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.

View File

@ -33,6 +33,7 @@ delete="NO"
kenv=
force="NO"
nextboot_file="/boot/nextboot.conf"
zfs=
add_kenv()
{
@ -106,26 +107,26 @@ if [ -n "${kernel}" -a ${force} = "NO" -a ! -d /boot/${kernel} ]; then
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}

View File

@ -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

View File

@ -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 @@ To clear the boot options:
.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 @@ appeared in
.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.

View File

@ -32,115 +32,257 @@ __FBSDID("$FreeBSD$");
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <kenv.h>
#include <unistd.h>
#include <libzfs.h>
#include <libzfsbootenv.h>
/* Keep in sync with zfsboot.c. */
#define MAX_COMMAND_LEN 512
#ifndef ZFS_MAXNAMELEN
#define ZFS_MAXNAMELEN 256
#endif
int
install_bootonce(libzfs_handle_t *hdl, uint64_t pool_guid, nvlist_t *nv,
const char * const data)
static int
add_pair(const char *name, const char *nvlist, const char *key,
const char *type, const char *value)
{
nvlist_t **child;
uint_t children = 0;
uint64_t guid;
void *data, *nv;
size_t size;
int rv;
char *end;
(void) nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN, &child,
&children);
for (int c = 0; c < children; c++) {
rv = install_bootonce(hdl, pool_guid, child[c], data);
}
if (children > 0)
rv = lzbe_nvlist_get(name, nvlist, &nv);
if (rv != 0)
return (rv);
if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID, &guid) != 0) {
perror("can't get vdev guid");
return (1);
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;
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;
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) {
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);
lzbe_nvlist_free(nv);
return (rv);
}
/*
* 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;
}
}
argc -= optind;
argv += optind;
if (argc == 1)
value = argv[0];
if (argc > 1) {
fprintf(stderr, "usage: zfsbootcfg <boot.config(5) options>\n");
return (1);
}
len = strlen(argv[1]);
if (len >= MAX_COMMAND_LEN) {
fprintf(stderr, "options string is too long\n");
return (1);
if (name == NULL) {
rv = kenv(KENV_GET, "vfs.root.mountfrom", buf, sizeof(buf));
if (rv <= 0) {
perror("can't get vfs.root.mountfrom");
return (1);
}
if (strncmp(buf, "zfs:", 4) == 0) {
name = strchr(buf + 4, '/');
if (name != NULL)
*name = '\0';
name = buf + 4;
} else {
perror("not a zfs root");
return (1);
}
}
if (kenv(KENV_GET, "vfs.root.mountfrom", buf, sizeof(buf)) <= 0) {
perror("can't get vfs.root.mountfrom");
return (1);
rv = 0;
if (key != NULL || value != NULL) {
if (type == NULL)
type = "DATA_TYPE_STRING";
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 (strncmp(buf, "zfs:", 4) == 0) {
name = strchr(buf + 4, '/');
if (name != NULL)
*name = '\0';
name = buf + 4;
} else {
perror("not a zfs root");
return (1);
}
if ((hdl = libzfs_init()) == NULL) {
(void) fprintf(stderr, "internal error: failed to "
"initialize ZFS library\n");
return (1);
if (print) {
rv = lzbe_bootenv_print(name, nvlist, stdout);
}
zphdl = zpool_open(hdl, name);
if (zphdl == NULL) {
perror("can't open pool");
libzfs_fini(hdl);
return (1);
}
pool_guid = zpool_get_prop_int(zphdl, ZPOOL_PROP_GUID, NULL);
config = zpool_get_config(zphdl, NULL);
if (config == NULL) {
perror("can't get pool config");
zpool_close(zphdl);
libzfs_fini(hdl);
return (1);
}
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);
}
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);
}

View File

@ -24,7 +24,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd July 11, 2020
.Dd Sep 21, 2020
.Dt RC.CONF 5
.Os
.Sh NAME
@ -4574,6 +4574,12 @@ If set to
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

View File

@ -169,6 +169,7 @@ LIBYPCLNT?= ${LIBDESTDIR}${LIBDIR_BASE}/libypclnt.a
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

View File

@ -202,6 +202,7 @@ _LIBRARIES= \
z \
zfs_core \
zfs \
zfsbootenv \
zpool \
zutil
@ -385,10 +386,11 @@ _DP_tpool= spl
_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 @@ LIBUMEMDIR= ${OBJTOP}/cddl/lib/libumem
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

View File

@ -344,6 +344,37 @@ void delay(int delay);
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

310
stand/common/nvstore.c Normal file
View File

@ -0,0 +1,310 @@
/*-
* Copyright 2020 Toomas Soome <tsoome@me.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Big Theory Statement.
*
* nvstore is abstraction layer to implement data read/write to different
* types of non-volatile storage.
*
* User interfaces:
* Provide mapping via environment: setenv/unsetenv/putenv. Access via
* environment functions/commands is available once nvstore has
* attached the backend and stored textual data is mapped to environment.
*
* Provide command "nvstore" to create new data instances.
*
* API: TBD.
* nvstore_init(): attach new backend and create the environment mapping.
* nvstore_fini: detach backend and unmap the related environment.
*
* The disk based storage, such as UFS file or ZFS bootenv label area, is
* only accessible after root file system is set. Root file system change
* will switch the back end storage.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <stdbool.h>
#include <sys/queue.h>
#include <bootstrap.h>
#include "stand.h"
typedef struct nvstore {
char *nvs_name;
void *nvs_data;
nvs_callbacks_t *nvs_cb;
STAILQ_ENTRY(nvstore) nvs_next;
} nvstore_t;
typedef STAILQ_HEAD(store_list, nvstore) nvstore_list_t;
nvstore_list_t stores = STAILQ_HEAD_INITIALIZER(stores);
void *
nvstore_get_store(const char *name)
{
nvstore_t *st;
st = NULL;
STAILQ_FOREACH(st, &stores, nvs_next) {
if (strcmp(name, st->nvs_name) == 0)
break;
}
return (st);
}
int
nvstore_init(const char *name, nvs_callbacks_t *cb, void *data)
{
nvstore_t *st;
st = nvstore_get_store(name);
if (st != NULL)
return (EEXIST);
if ((st = malloc(sizeof (*st))) == NULL)
return (ENOMEM);
if ((st->nvs_name = strdup(name)) == NULL) {
free(st);
return (ENOMEM);
}
st->nvs_data = data;
st->nvs_cb = cb;
STAILQ_INSERT_TAIL(&stores, st, nvs_next);
return (0);
}
int
nvstore_fini(const char *name)
{
nvstore_t *st;
st = nvstore_get_store(name);
if (st == NULL)
return (ENOENT);
STAILQ_REMOVE(&stores, st, nvstore, nvs_next);
free(st->nvs_name);
free(st->nvs_data);
free(st);
return (0);
}
int
nvstore_print(void *ptr)
{
nvstore_t *st = ptr;
return (st->nvs_cb->nvs_iterate(st->nvs_data, st->nvs_cb->nvs_print));
}
int
nvstore_get_var(void *ptr, const char *name, void **data)
{
nvstore_t *st = ptr;
return (st->nvs_cb->nvs_getter(st->nvs_data, name, data));
}
int
nvstore_set_var(void *ptr, int type, const char *name,
void *data, size_t size)
{
nvstore_t *st = ptr;
return (st->nvs_cb->nvs_setter(st->nvs_data, type, name, data, size));
}
int
nvstore_set_var_from_string(void *ptr, const char *type, const char *name,
const char *data)
{
nvstore_t *st = ptr;
return (st->nvs_cb->nvs_setter_str(st->nvs_data, type, name, data));
}
int
nvstore_unset_var(void *ptr, const char *name)
{
nvstore_t *st = ptr;
return (st->nvs_cb->nvs_unset(st->nvs_data, name));
}
COMMAND_SET(nvstore, "nvstore", "manage non-volatile data", command_nvstore);
static void
nvstore_usage(const char *me)
{
printf("Usage:\t%s -l\n", me);
printf("\t%s store -l\n", me);
printf("\t%s store [-t type] key value\n", me);
printf("\t%s store -g key\n", me);
printf("\t%s store -d key\n", me);
}
/*
* Usage: nvstore -l # list stores
* nvstore store -l # list data in store
* nvstore store [-t type] key value
* nvstore store -g key # get value
* nvstore store -d key # delete key
*/
static int
command_nvstore(int argc, char *argv[])
{
int c;
bool list, get, delete;
nvstore_t *st;
char *me, *name, *type;
me = argv[0];
optind = 1;
optreset = 1;
list = false;
while ((c = getopt(argc, argv, "l")) != -1) {
switch (c) {
case 'l':
list = true;
break;
case '?':
default:
return (CMD_ERROR);
}
}
argc -= optind;
argv += optind;
if (argc == 0) {
if (list) {
if (STAILQ_EMPTY(&stores)) {
printf("No configured nvstores\n");
return (CMD_OK);
}
printf("List of configured nvstores:\n");
STAILQ_FOREACH(st, &stores, nvs_next) {
printf("\t%s\n", st->nvs_name);
}
return (CMD_OK);
}
nvstore_usage(me);
return (CMD_ERROR);
}
if (argc == 0 || (argc != 0 && list)) {
nvstore_usage(me);
return (CMD_ERROR);
}
st = nvstore_get_store(argv[0]);
if (st == NULL) {
nvstore_usage(me);
return (CMD_ERROR);
}
optind = 1;
optreset = 1;
name = NULL;
type = NULL;
get = delete = false;
while ((c = getopt(argc, argv, "d:g:lt:")) != -1) {
switch (c) {
case 'd':
if (list || get) {
nvstore_usage(me);
return (CMD_ERROR);
}
name = optarg;
delete = true;
break;
case 'g':
if (delete || list) {
nvstore_usage(me);
return (CMD_ERROR);
}
name = optarg;
get = true;
break;
case 'l':
if (delete || get) {
nvstore_usage(me);
return (CMD_ERROR);
}
list = true;
break;
case 't':
type = optarg;
break;
case '?':
default:
return (CMD_ERROR);
}
}
argc -= optind;
argv += optind;
if (list) {
(void) nvstore_print(st);
return (CMD_OK);
}
if (delete && name != NULL) {
(void) nvstore_unset_var(st, name);
return (CMD_OK);
}
if (get && name != NULL) {
char *ptr = NULL;
if (nvstore_get_var(st, name, (void **)&ptr) == 0)
printf("%s = %s\n", name, ptr);
return (CMD_OK);
}
if (argc == 2) {
c = nvstore_set_var_from_string(st, type, argv[0], argv[1]);
if (c != 0) {
printf("error: %s\n", strerror(c));
return (CMD_ERROR);
}
return (CMD_OK);
}
nvstore_usage(me);
return (CMD_OK);
}

View File

@ -16,7 +16,6 @@ bootfile="kernel" # Kernel name (possibly absolute path)
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 ############################

View File

@ -36,7 +36,9 @@ SRCS+= zfs_module.c
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

View File

@ -124,7 +124,7 @@ probe(dev_info_t *dev)
}
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);
}

View File

@ -27,6 +27,8 @@ SRCS= autoload.c \
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

View File

@ -36,6 +36,7 @@ __FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/reboot.h>
#include <sys/boot.h>
#include <sys/zfs_bootenv.h>
#include <paths.h>
#include <stdint.h>
#include <string.h>
@ -275,11 +276,14 @@ probe_zfs_currdev(uint64_t guid)
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);

View File

@ -1057,8 +1057,16 @@ string current_file_name_ref \ used to print the file name
;
: 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 @@ string current_file_name_ref \ used to print the file name
process_conf_errors
['] rewrite_nextboot_file catch if 2drop then
then
s' "NO"' s" nextboot_enable" setenv
;
\ Module loading functions

View File

@ -32,7 +32,9 @@ CFLAGS+=-DBOOTPROG=\"gptzfsboot\" \
-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 \

View File

@ -37,6 +37,11 @@ CFLAGS+= -DLOADER_FIREWIRE_SUPPORT
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

View File

@ -40,6 +40,7 @@ __FBSDID("$FreeBSD$");
#include <machine/psl.h>
#include <sys/disk.h>
#include <sys/reboot.h>
#include <sys/zfs_bootenv.h>
#include <common/drv.h>
#include "bootstrap.h"
@ -274,6 +275,7 @@ extract_currdev(void)
struct i386_devdesc new_currdev;
#ifdef LOADER_ZFS_SUPPORT
char buf[20];
char *bootonce;
#endif
int biosdev = -1;
@ -321,6 +323,16 @@ extract_currdev(void)
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 */

View File

@ -31,7 +31,9 @@ CFLAGS+=-DBOOTPROG=\"zfsboot\" \
-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 \

View File

@ -26,6 +26,7 @@ __FBSDID("$FreeBSD$");
#endif
#include <sys/reboot.h>
#include <sys/queue.h>
#include <sys/zfs_bootenv.h>
#include <machine/bootinfo.h>
#include <machine/elf.h>
@ -218,16 +219,26 @@ main(void)
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;
}

View File

@ -12,6 +12,8 @@ CFLAGS+= -I${SYSDIR}/crypto/skein
# 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

View File

@ -26,15 +26,15 @@
* $FreeBSD$
*/
#ifndef _BOOT_LIBZFS_H_
#define _BOOT_LIBZFS_H_
#include <zfsimpl.h>
#ifdef LOADER_GELI_SUPPORT
#include <crypto/intake.h>
#endif
#ifndef _BOOT_LIBZFS_H_
#define _BOOT_LIBZFS_H_
#define ZFS_MAXNAMELEN 256
/*
@ -54,6 +54,7 @@ struct zfs_devdesc {
#define NV_UNIQUE_NAME_TYPE 0x2
#define NV_ALIGN4(x) (((x) + 3) & ~3)
#define NV_ALIGN(x) (((x) + 7) & ~7)
/*
* nvlist header.
@ -109,25 +110,62 @@ typedef struct {
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;

File diff suppressed because it is too large Load Diff

View File

@ -486,8 +486,7 @@ vdev_read(vdev_t *vdev, void *priv, off_t offset, void *buf, size_t bytes)
}
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 @@ vdev_write(vdev_t *vdev __unused, void *priv, off_t offset, void *buf,
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 @@ vdev_write(vdev_t *vdev __unused, void *priv, off_t offset, void *buf,
/* 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 @@ vdev_write(vdev_t *vdev __unused, void *priv, off_t offset, void *buf,
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 @@ vdev_write(vdev_t *vdev __unused, void *priv, off_t offset, void *buf,
/* 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 @@ vdev_write(vdev_t *vdev __unused, void *priv, off_t offset, void *buf,
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 @@ zfs_probe(int fd, uint64_t *pool_guid)
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 @@ zfs_probe_partition(void *arg, const char *partname,
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,57 +695,728 @@ zfs_probe_partition(void *arg, const char *partname,
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;
char *result = NULL;
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 == NULL) {
printf("ZFS: can't find pool by guid\n");
return (1);
}
if ((spa = spa_find_by_dev(dev)) == NULL)
return (ENXIO);
STAILQ_FOREACH(vd, &spa->spa_root_vdev->v_children, v_childlink) {
char *tmp = vdev_read_pad2(vd);
/* Continue on error. */
if (tmp == NULL)
continue;
/* Nextboot is not set. */
if (*tmp == '\0') {
free(result);
free(tmp);
return (1);
}
if (result == NULL) {
result = tmp;
continue;
}
free(tmp);
}
if (result == NULL)
return (1);
STAILQ_FOREACH(vd, &spa->spa_root_vdev->v_children, v_childlink) {
vdev_clear_pad2(vd);
vdev_write_bootenv(vd, benv);
}
strlcpy(buf, result, size);
free(result);
spa->spa_bootenv = benv;
return (0);
}
/*
* Get bootonce value by key. The bootonce <key, value> pair is removed
* from the bootenv nvlist and the remaining nvlist is committed back to disk.
*/
int
zfs_get_bootonce(void *vdev, const char *key, char *buf, size_t size)
{
nvlist_t *benv;
char *result = NULL;
int result_size, rv;
if ((rv = zfs_get_bootenv(vdev, &benv)) != 0)
return (rv);
if ((rv = nvlist_find(benv, key, DATA_TYPE_STRING, NULL,
&result, &result_size)) == 0) {
if (result_size == 0) {
/* ignore empty string */
rv = ENOENT;
} else {
size = MIN((size_t)result_size + 1, size);
strlcpy(buf, result, size);
}
(void) nvlist_remove(benv, key, DATA_TYPE_STRING);
(void) zfs_set_bootenv(vdev, benv);
}
return (rv);
}
/*
* 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 (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_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);
}
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;
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;
}
rv = nvlist_add_int8(nv, name, *(int8_t *)data);
break;
case DATA_TYPE_INT16:
if (size != sizeof (int16_t)) {
rv = EINVAL;
break;
}
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 (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;
}
}
}
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)
{
@ -939,12 +1513,9 @@ zfs_dev_open(struct open_file *f, ...)
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 @@ zfs_fmtdev(void *vdev)
}
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);
}

View File

@ -31,10 +31,12 @@ __FBSDID("$FreeBSD$");
* Stand-alone ZFS file reader.
*/
#include <stdbool.h>
#include <sys/endian.h>
#include <sys/stat.h>
#include <sys/stdint.h>
#include <sys/list.h>
#include <sys/zfs_bootenv.h>
#include <machine/_inttypes.h>
#include "zfsimpl.h"
@ -220,8 +222,8 @@ vdev_read_phys(vdev_t *vdev, const blkptr_t *bp, void *buf,
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 @@ vdev_read_phys(vdev_t *vdev, const blkptr_t *bp, void *buf,
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 @@ vdev_read_phys(vdev_t *vdev, const blkptr_t *bp, void *buf,
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 @@ static int
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 @@ vdev_from_nvlist(spa_t *spa, uint64_t top_guid, const nvlist_t *nvlist)
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 @@ vdev_from_nvlist(spa_t *spa, uint64_t top_guid, const nvlist_t *nvlist)
*/
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 @@ static int
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 @@ vdev_update_from_nvlist(uint64_t top_guid, const nvlist_t *nvlist)
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 @@ static int
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 @@ vdev_init_from_nvlist(spa_t *spa, const nvlist_t *nvlist)
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 @@ vdev_init_from_nvlist(spa_t *spa, const nvlist_t *nvlist)
* 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);
if (rc != 0)
break;
rc = nvlist_next(kids);
rc = vdev_update_from_nvlist(guid, kids[i]);
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.
@ -1337,6 +1345,19 @@ spa_find_by_name(const char *name)
return (NULL);
}
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)
{
@ -1589,6 +1610,254 @@ vdev_label_read(vdev_t *vd, int l, void *buf, uint64_t offset,
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 @@ vdev_get_label_asize(nvlist_t *nvl)
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 @@ vdev_get_label_asize(nvlist_t *nvl)
}
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 @@ vdev_label_read_config(vdev_t *vd, uint64_t txg)
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 @@ vdev_uberblock_load(vdev_t *vd, uberblock_t *ub)
}
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 @@ vdev_probe(vdev_phys_read_t *_read, void *read_priv, spa_t **spap)
*/
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_probe(vdev_phys_read_t *_read, void *read_priv, spa_t **spap)
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 @@ load_nvlist(spa_t *spa, uint64_t obj, nvlist_t **value)
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 @@ load_nvlist(spa_t *spa, uint64_t obj, nvlist_t **value)
nv = NULL;
return (rc);
}
*value = nvlist_import(nv + 4, nv[0], nv[1]);
*value = nvlist_import(nv, size);
free(nv);
return (rc);
}

View File

@ -6,7 +6,7 @@ CFLAGS+=-I${LDRSRC}
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

View File

@ -389,16 +389,24 @@ end
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 @@ local function checkNextboot()
io.write(nfile, "nextboot_enable=\"NO\" ")
io.close(nfile)
end
loader.setenv("nextboot_enable", "NO")
end
-- Module exports

View File

@ -260,6 +260,21 @@ test_diskread(void *arg, int unit, uint64_t offset, void *dst, size_t size,
return (0);
}
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)
{
@ -399,6 +414,7 @@ struct loader_callbacks cb = {
.stat = test_stat,
.diskread = test_diskread,
.diskwrite = test_diskwrite,
.diskioctl = test_diskioctl,
.copyin = test_copyin,
@ -431,8 +447,9 @@ main(int argc, char** argv)
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,7 +459,7 @@ main(int argc, char** argv)
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;
@ -451,6 +468,10 @@ main(int argc, char** argv)
host_base = optarg;
break;
case 'w':
oflag = O_RDWR;
break;
case '?':
usage();
}

View File

@ -131,6 +131,12 @@ struct loader_callbacks {
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
*/

View File

@ -32,8 +32,9 @@ SRCS+= vers.c
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

View File

@ -32,6 +32,7 @@ __FBSDID("$FreeBSD$");
#include <string.h>
#include <setjmp.h>
#include <sys/disk.h>
#include <sys/zfs_bootenv.h>
#include "bootstrap.h"
#include "disk.h"
@ -214,6 +215,16 @@ loader_main(struct loader_callbacks *cb, void *arg, int version, int ndisks)
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 @@ extract_currdev(void)
struct devdesc *dd;
#if defined(USERBOOT_ZFS_SUPPORT)
struct zfs_devdesc zdev;
char *buf = NULL;
if (userboot_zfs_found) {
@ -257,10 +269,23 @@ extract_currdev(void)
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)

View File

@ -211,15 +211,21 @@ userdisk_realstrategy(void *devdata, int rw, daddr_t dblk, size_t size,
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)

View File

@ -527,20 +527,20 @@ typedef struct vdev_phys {
} 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 @@ typedef struct znode_phys {
*/
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 @@ typedef struct vdev {
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 @@ typedef struct spa {
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. */

View File

@ -147,7 +147,7 @@ main(int argc, char** argv)
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]);
}